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();
};
// ----------------------------------------------------------------------------