Pay attention to buffer timestamps

When acquiring a buffer, SurfaceFlinger now computes the expected
presentation time and passes it to the BufferQueue acquireBuffer()
method.  If it's not yet time to display the buffer, acquireBuffer()
returns PRESENT_LATER instead of a buffer.

The current implementation of the expected-present-time computation
uses approximations and guesswork.

Bug 7900302

Change-Id: If9345611c5983a11a811935aaf27d6388a5036f1
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index ba04bdf..8d86c59 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -47,14 +47,15 @@
     mBufferQueue->setConsumerName(name);
 }
 
-status_t BufferItemConsumer::acquireBuffer(BufferItem *item, bool waitForFence) {
+status_t BufferItemConsumer::acquireBuffer(BufferItem *item,
+        nsecs_t presentWhen, bool waitForFence) {
     status_t err;
 
     if (!item) return BAD_VALUE;
 
     Mutex::Autolock _l(mMutex);
 
-    err = acquireBufferLocked(item);
+    err = acquireBufferLocked(item, presentWhen);
     if (err != OK) {
         if (err != NO_BUFFER_AVAILABLE) {
             BI_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err);
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 0dab864..8d4b174 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -807,7 +807,7 @@
     }
 }
 
-status_t BufferQueue::acquireBuffer(BufferItem *buffer) {
+status_t BufferQueue::acquireBuffer(BufferItem *buffer, nsecs_t presentWhen) {
     ATRACE_CALL();
     Mutex::Autolock _l(mMutex);
 
@@ -830,38 +830,68 @@
     // check if queue is empty
     // In asynchronous mode the list is guaranteed to be one buffer
     // deep, while in synchronous mode we use the oldest buffer.
-    if (!mQueue.empty()) {
-        Fifo::iterator front(mQueue.begin());
-        int buf = front->mBuf;
-        *buffer = *front;
-        ATRACE_BUFFER_INDEX(buf);
-
-        ST_LOGV("acquireBuffer: acquiring { slot=%d/%llu, buffer=%p }",
-                front->mBuf, front->mFrameNumber,
-                front->mGraphicBuffer->handle);
-        // if front buffer still being tracked update slot state
-        if (stillTracking(front)) {
-            mSlots[buf].mAcquireCalled = true;
-            mSlots[buf].mNeedsCleanupOnRelease = false;
-            mSlots[buf].mBufferState = BufferSlot::ACQUIRED;
-            mSlots[buf].mFence = Fence::NO_FENCE;
-        }
-
-        // If the buffer has previously been acquired by the consumer, set
-        // mGraphicBuffer to NULL to avoid unnecessarily remapping this
-        // buffer on the consumer side.
-        if (buffer->mAcquireCalled) {
-            buffer->mGraphicBuffer = NULL;
-        }
-
-        mQueue.erase(front);
-        mDequeueCondition.broadcast();
-
-        ATRACE_INT(mConsumerName.string(), mQueue.size());
-    } else {
+    if (mQueue.empty()) {
         return NO_BUFFER_AVAILABLE;
     }
 
+    Fifo::iterator front(mQueue.begin());
+    int buf = front->mBuf;
+
+    // Compare the buffer's desired presentation time to the predicted
+    // actual display time.
+    //
+    // The "presentWhen" argument indicates when the buffer is expected
+    // to be presented on-screen.  If the buffer's desired-present time
+    // is earlier (less) than presentWhen, meaning it'll be displayed
+    // on time or possibly late, we acquire and return it.  If we don't want
+    // to display it until after the presentWhen time, we return PRESENT_LATER
+    // without acquiring it.
+    //
+    // To be safe, we don't refuse to acquire the buffer if presentWhen is
+    // more than one second in the future beyond the desired present time
+    // (i.e. we'd be holding the buffer for a really long time).
+    const int MAX_FUTURE_NSEC = 1000000000ULL;
+    nsecs_t desiredPresent = front->mTimestamp;
+    if (presentWhen != 0 && desiredPresent > presentWhen &&
+            desiredPresent - presentWhen < MAX_FUTURE_NSEC)
+    {
+        ALOGV("pts defer: des=%lld when=%lld (%lld) now=%lld",
+                desiredPresent, presentWhen, desiredPresent - presentWhen,
+                systemTime(CLOCK_MONOTONIC));
+        return PRESENT_LATER;
+    }
+    if (presentWhen != 0) {
+        ALOGV("pts accept: %p[%d] sig=%lld des=%lld when=%lld (%lld)",
+                mSlots, buf, mSlots[buf].mFence->getSignalTime(),
+                desiredPresent, presentWhen, desiredPresent - presentWhen);
+    }
+
+    *buffer = *front;
+    ATRACE_BUFFER_INDEX(buf);
+
+    ST_LOGV("acquireBuffer: acquiring { slot=%d/%llu, buffer=%p }",
+            front->mBuf, front->mFrameNumber,
+            front->mGraphicBuffer->handle);
+    // if front buffer still being tracked update slot state
+    if (stillTracking(front)) {
+        mSlots[buf].mAcquireCalled = true;
+        mSlots[buf].mNeedsCleanupOnRelease = false;
+        mSlots[buf].mBufferState = BufferSlot::ACQUIRED;
+        mSlots[buf].mFence = Fence::NO_FENCE;
+    }
+
+    // If the buffer has previously been acquired by the consumer, set
+    // mGraphicBuffer to NULL to avoid unnecessarily remapping this
+    // buffer on the consumer side.
+    if (buffer->mAcquireCalled) {
+        buffer->mGraphicBuffer = NULL;
+    }
+
+    mQueue.erase(front);
+    mDequeueCondition.broadcast();
+
+    ATRACE_INT(mConsumerName.string(), mQueue.size());
+
     return NO_ERROR;
 }
 
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index fd9d153..deb2646 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -182,8 +182,9 @@
     }
 }
 
-status_t ConsumerBase::acquireBufferLocked(BufferQueue::BufferItem *item) {
-    status_t err = mBufferQueue->acquireBuffer(item);
+status_t ConsumerBase::acquireBufferLocked(BufferQueue::BufferItem *item,
+        nsecs_t presentWhen) {
+    status_t err = mBufferQueue->acquireBuffer(item, presentWhen);
     if (err != NO_ERROR) {
         return err;
     }
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index 123b470..56bc7c6 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -79,7 +79,7 @@
 
     Mutex::Autolock _l(mMutex);
 
-    err = acquireBufferLocked(&b);
+    err = acquireBufferLocked(&b, 0);
     if (err != OK) {
         if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
             return BAD_VALUE;
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 6d29edc..d12083f 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -139,7 +139,7 @@
     // Acquire the next buffer.
     // In asynchronous mode the list is guaranteed to be one buffer
     // deep, while in synchronous mode we use the oldest buffer.
-    err = acquireBufferLocked(&item);
+    err = acquireBufferLocked(&item, 0);
     if (err != NO_ERROR) {
         if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
             // We always bind the texture even if we don't update its contents.
@@ -165,8 +165,9 @@
     return bindTextureImageLocked();
 }
 
-status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item) {
-    status_t err = ConsumerBase::acquireBufferLocked(item);
+status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item,
+        nsecs_t presentWhen) {
+    status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen);
     if (err != NO_ERROR) {
         return err;
     }
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 62d215b..9682987 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -80,7 +80,7 @@
                     GRALLOC_USAGE_SW_READ_OFTEN));
         ASSERT_EQ(OK, mBQ->requestBuffer(slot, &buf));
         ASSERT_EQ(OK, mBQ->queueBuffer(slot, qbi, &qbo));
-        ASSERT_EQ(OK, mBQ->acquireBuffer(&item));
+        ASSERT_EQ(OK, mBQ->acquireBuffer(&item, 0));
     }
 
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
@@ -90,7 +90,7 @@
     ASSERT_EQ(OK, mBQ->queueBuffer(slot, qbi, &qbo));
 
     // Acquire the third buffer, which should fail.
-    ASSERT_EQ(INVALID_OPERATION, mBQ->acquireBuffer(&item));
+    ASSERT_EQ(INVALID_OPERATION, mBQ->acquireBuffer(&item, 0));
 }
 
 TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) {