surfaceflinger: move SurfaceFlingerConsumer::mPendingRelease
Move it and related methods to the base class, BufferLayerConsumer.
Test: boots
Change-Id: Ibe4d180aefbeeb3662fa40ca044d9d6fdd3b5765
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 0303d94..110b7de 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -21,6 +21,7 @@
#include "BufferLayerConsumer.h"
+#include "DispSync.h"
#include "Layer.h"
#include <inttypes.h>
@@ -149,7 +150,56 @@
mContentsChangedListener = listener;
}
-status_t BufferLayerConsumer::updateTexImage() {
+// 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
+// need to take the offset between actual-present and reported-vsync
+// into account.
+//
+// If the system is configured without a DispSync phase offset for the app,
+// we also want to throw in a bit of padding to avoid edge cases where we
+// just barely miss. We want to do it here, not in every app. A major
+// source of trouble is the app's use of the display's ideal refresh time
+// (via Display.getRefreshRate()), which could be off of the actual refresh
+// by a few percent, with the error multiplied by the number of frames
+// between now and when the buffer should be displayed.
+//
+// If the refresh reported to the app has a phase offset, we shouldn't need
+// to tweak anything here.
+nsecs_t BufferLayerConsumer::computeExpectedPresent(const DispSync& dispSync) {
+ // The HWC doesn't currently have a way to report additional latency.
+ // Assume that whatever we submit now will appear right after the flip.
+ // For a smart panel this might be 1. This is expressed in frames,
+ // rather than time, because we expect to have a constant frame delay
+ // regardless of the refresh rate.
+ const uint32_t hwcLatency = 0;
+
+ // Ask DispSync when the next refresh will be (CLOCK_MONOTONIC).
+ const nsecs_t nextRefresh = dispSync.computeNextRefresh(hwcLatency);
+
+ // The DispSync time is already adjusted for the difference between
+ // vsync and reported-vsync (SurfaceFlinger::dispSyncPresentTimeOffset), so
+ // we don't need to factor that in here. Pad a little to avoid
+ // weird effects if apps might be requesting times right on the edge.
+ nsecs_t extraPadding = 0;
+ if (SurfaceFlinger::vsyncPhaseOffsetNs == 0) {
+ extraPadding = 1000000; // 1ms (6% of 60Hz)
+ }
+
+ return nextRefresh + extraPadding;
+}
+
+status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync,
+ bool* autoRefresh, bool* queuedBuffer,
+ uint64_t maxFrameNumber) {
ATRACE_CALL();
BLC_LOGV("updateTexImage");
Mutex::Autolock lock(mMutex);
@@ -170,29 +220,86 @@
// 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, 0);
+ err = acquireBufferLocked(&item, computeExpectedPresent(dispSync), maxFrameNumber);
if (err != NO_ERROR) {
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
- // We always bind the texture even if we don't update its contents.
- BLC_LOGV("updateTexImage: no buffers were available");
- glBindTexture(sTexTarget, mTexName);
err = NO_ERROR;
+ } else if (err == BufferQueue::PRESENT_LATER) {
+ // return the error, without logging
} else {
BLC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err);
}
return err;
}
+ if (autoRefresh) {
+ *autoRefresh = item.mAutoRefresh;
+ }
+
+ if (queuedBuffer) {
+ *queuedBuffer = item.mQueuedBuffer;
+ }
+
+ // We call the rejecter here, in case the caller has a reason to
+ // not accept this buffer. This is used by SurfaceFlinger to
+ // reject buffers which have the wrong size
+ int slot = item.mSlot;
+ if (rejecter && rejecter->reject(mSlots[slot].mGraphicBuffer, item)) {
+ releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer);
+ return BUFFER_REJECTED;
+ }
+
// Release the previous buffer.
- err = updateAndReleaseLocked(item);
+ err = updateAndReleaseLocked(item, &mPendingRelease);
if (err != NO_ERROR) {
- // We always bind the texture.
- glBindTexture(sTexTarget, mTexName);
return err;
}
- // Bind the new buffer to the GL texture, and wait until it's ready.
- return bindTextureImageLocked();
+ if (!SyncFeatures::getInstance().useNativeFenceSync()) {
+ // Bind the new buffer to the GL texture.
+ //
+ // Older devices require the "implicit" synchronization provided
+ // by glEGLImageTargetTexture2DOES, which this method calls. Newer
+ // devices will either call this in Layer::onDraw, or (if it's not
+ // a GL-composited layer) not at all.
+ err = bindTextureImageLocked();
+ }
+
+ return err;
+}
+
+void BufferLayerConsumer::setReleaseFence(const sp<Fence>& fence) {
+ if (!fence->isValid()) {
+ return;
+ }
+
+ auto slot = mPendingRelease.isPending ? mPendingRelease.currentTexture : mCurrentTexture;
+ if (slot == BufferQueue::INVALID_BUFFER_SLOT) {
+ return;
+ }
+
+ auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer
+ : mCurrentTextureImage->graphicBuffer();
+ auto err = addReleaseFence(slot, buffer, fence);
+ if (err != OK) {
+ BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err);
+ }
+}
+
+bool BufferLayerConsumer::releasePendingBuffer() {
+ if (!mPendingRelease.isPending) {
+ BLC_LOGV("Pending buffer already released");
+ return false;
+ }
+ BLC_LOGV("Releasing pending buffer");
+ Mutex::Autolock lock(mMutex);
+ status_t result =
+ releaseBufferLocked(mPendingRelease.currentTexture, mPendingRelease.graphicBuffer);
+ if (result < NO_ERROR) {
+ BLC_LOGE("releasePendingBuffer failed: %s (%d)", strerror(-result), result);
+ }
+ mPendingRelease = PendingRelease();
+ return true;
}
status_t BufferLayerConsumer::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
@@ -351,16 +458,6 @@
return NO_ERROR;
}
-void BufferLayerConsumer::setReleaseFence(const sp<Fence>& fence) {
- if (fence->isValid() && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
- status_t err =
- addReleaseFence(mCurrentTexture, mCurrentTextureImage->graphicBuffer(), fence);
- if (err != OK) {
- BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err);
- }
- }
-}
-
status_t BufferLayerConsumer::syncForReleaseLocked(EGLDisplay dpy) {
BLC_LOGV("syncForReleaseLocked");