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/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 10bca38..938459e 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -83,7 +83,7 @@
     Mutex::Autolock lock(mMutex);
 
     BufferQueue::BufferItem item;
-    status_t err = acquireBufferLocked(&item);
+    status_t err = acquireBufferLocked(&item, 0);
     if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
         outBuffer = mCurrentBuffer;
         return NO_ERROR;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index b08b8d1..31a11ce 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1051,11 +1051,6 @@
         const bool oldOpacity = isOpaque();
         sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer;
 
-        // signal another event if we have more frames pending
-        if (android_atomic_dec(&mQueuedFrames) > 1) {
-            mFlinger->signalLayerUpdate();
-        }
-
         struct Reject : public SurfaceFlingerConsumer::BufferRejecter {
             Layer::State& front;
             Layer::State& current;
@@ -1161,7 +1156,21 @@
 
         Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions);
 
-        if (mSurfaceFlingerConsumer->updateTexImage(&r) != NO_ERROR) {
+        status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r);
+        if (updateResult == BufferQueue::PRESENT_LATER) {
+            // Producer doesn't want buffer to be displayed yet.  Signal a
+            // layer update so we check again at the next opportunity.
+            mFlinger->signalLayerUpdate();
+            return outDirtyRegion;
+        }
+
+        // Decrement the queued-frames count.  Signal another event if we
+        // have more frames pending.
+        if (android_atomic_dec(&mQueuedFrames) > 1) {
+            mFlinger->signalLayerUpdate();
+        }
+
+        if (updateResult != NO_ERROR) {
             // something happened!
             recomputeVisibleRegions = true;
             return outDirtyRegion;
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index 7ac5c60..b181b60 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -50,12 +50,14 @@
     // 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, computeExpectedPresent());
     if (err != NO_ERROR) {
         if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
             // This variant of updateTexImage does not guarantee that the
             // texture is bound, so no need to call glBindTexture.
             err = NO_ERROR;
+        } else if (err == BufferQueue::PRESENT_LATER) {
+            // return the error, without logging
         } else {
             ALOGE("updateTexImage: acquire failed: %s (%d)",
                 strerror(-err), err);
@@ -99,6 +101,48 @@
     return bindTextureImageLocked();
 }
 
+// We need to determine the time when a buffer acquired now will be
+// displayed.  This can be calculated:
+//   time when previous buffer's actual-present fence was signaled
+//    + current display refresh rate * HWC latency
+//    + a little extra padding
+//
+// Buffer producers are expected to set their desired presentation time
+// based on choreographer time stamps, which (coming from vsync events)
+// will be slightly later then the actual-present timing.  If we get a
+// desired-present time that is unintentionally a hair after the next
+// vsync, we'll hold the frame when we really want to display it.  We
+// want to use an expected-presentation time that is slightly late to
+// avoid this sort of edge case.
+nsecs_t SurfaceFlingerConsumer::computeExpectedPresent()
+{
+    // Don't yet have an easy way to get actual buffer flip time for
+    // the specific display, so use the current time.  This is typically
+    // 1.3ms past the vsync event time.
+    const nsecs_t prevVsync = systemTime(CLOCK_MONOTONIC);
+
+    // Given a SurfaceFlinger reference, and information about what display
+    // we're destined for, we could query the HWC for the refresh rate.  This
+    // could change over time, e.g. we could switch to 24fps for a movie.
+    // For now, assume 60fps.
+    //const nsecs_t vsyncPeriod =
+    //        getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
+    const nsecs_t vsyncPeriod = 16700000;
+
+    // The HWC doesn't currently have a way to report additional latency.
+    // Assume that whatever we submit now will appear on the next flip,
+    // i.e. 1 frame of latency w.r.t. the previous flip.
+    const uint32_t hwcLatency = 1;
+
+    // A little extra padding to compensate for slack between actual vsync
+    // time and vsync event receipt.  Currently not needed since we're
+    // using "now" instead of a vsync time.
+    const nsecs_t extraPadding = 0;
+
+    // Total it up.
+    return prevVsync + hwcLatency * vsyncPeriod + extraPadding;
+}
+
 // ---------------------------------------------------------------------------
 }; // namespace android
 
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
index 22eec81..d774c33 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -51,6 +51,9 @@
 
     // See GLConsumer::bindTextureImageLocked().
     status_t bindTextureImage();
+
+private:
+    nsecs_t computeExpectedPresent();
 };
 
 // ----------------------------------------------------------------------------