Fix PTS handling for buffer replacement

This changes the way that SurfaceFlinger's shadow buffer management
works such that instead of tracking the size of the shadow queue in the
BufferQueue, SF tracks the last frame number it has seen, and passes
that into the acquireBuffer call. BufferQueueConsumer then ensures that
it never returns a buffer newer than that frame number, even if that
means that it must return PRESENT_LATER for an otherwise valid buffer.

Change-Id: I3fcb45f683ed660c3f18a8b85ae1f8a962ba6f0e
This commit is contained in:
Dan Stoza 2015-05-12 12:56:16 -07:00
parent 2a7dde5803
commit a4650a50a0
14 changed files with 98 additions and 99 deletions

View File

@ -47,7 +47,7 @@ public:
// returned. The presentation time is in nanoseconds, and the time base
// is CLOCK_MONOTONIC.
virtual status_t acquireBuffer(BufferItem* outBuffer,
nsecs_t expectedPresent);
nsecs_t expectedPresent, uint64_t maxFrameNumber = 0) override;
// See IGraphicBufferConsumer::detachBuffer
virtual status_t detachBuffer(int slot);
@ -148,9 +148,6 @@ public:
// Retrieve the sideband buffer stream, if any.
virtual sp<NativeHandle> getSidebandStream() const;
// See IGraphicBufferConsumer::setShadowQueueSize
virtual void setShadowQueueSize(size_t size);
// dump our state in a String
virtual void dump(String8& result, const char* prefix) const;

View File

@ -275,12 +275,6 @@ private:
// buffer as the number of frames that have elapsed since it was last queued
uint64_t mBufferAge;
// mConsumerHasShadowQueue determines if acquireBuffer should be more
// cautious about dropping buffers so that it always returns a buffer that
// is represented in the consumer's shadow queue.
bool mConsumerHasShadowQueue;
size_t mConsumerShadowQueueSize;
}; // class BufferQueueCore
} // namespace android

View File

@ -152,7 +152,8 @@ protected:
// initialization that must take place the first time a buffer is assigned
// to a slot. If it is overridden the derived class's implementation must
// call ConsumerBase::acquireBufferLocked.
virtual status_t acquireBufferLocked(BufferItem *item, nsecs_t presentWhen);
virtual status_t acquireBufferLocked(BufferItem *item, nsecs_t presentWhen,
uint64_t maxFrameNumber = 0);
// releaseBufferLocked relinquishes control over a buffer, returning that
// control to the BufferQueue.

View File

@ -241,7 +241,8 @@ protected:
// acquireBufferLocked overrides the ConsumerBase method to update the
// mEglSlots array in addition to the ConsumerBase behavior.
virtual status_t acquireBufferLocked(BufferItem *item, nsecs_t presentWhen);
virtual status_t acquireBufferLocked(BufferItem *item, nsecs_t presentWhen,
uint64_t maxFrameNumber = 0) override;
// releaseBufferLocked overrides the ConsumerBase method to update the
// mEglSlots array in addition to the ConsumerBase.

View File

@ -69,6 +69,12 @@ public:
// returned. The presentation time is in nanoseconds, and the time base
// is CLOCK_MONOTONIC.
//
// If maxFrameNumber is non-zero, it indicates that acquireBuffer should
// only return a buffer with a frame number less than or equal to
// maxFrameNumber. If no such frame is available (such as when a buffer has
// been replaced but the consumer has not received the onFrameReplaced
// callback), then PRESENT_LATER will be returned.
//
// Return of NO_ERROR means the operation completed as normal.
//
// Return of a positive value means the operation could not be completed
@ -78,7 +84,8 @@ public:
//
// Return of a negative value means an error has occurred:
// * INVALID_OPERATION - too many buffers have been acquired
virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen) = 0;
virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen,
uint64_t maxFrameNumber = 0) = 0;
// detachBuffer attempts to remove all ownership of the buffer in the given
// slot from the buffer queue. If this call succeeds, the slot will be
@ -248,11 +255,6 @@ public:
// Retrieve the sideband buffer stream, if any.
virtual sp<NativeHandle> getSidebandStream() const = 0;
// setShadowQueueSize notifies the BufferQueue that the consumer is
// shadowing its queue and allows it to limit the number of buffers it is
// permitted to drop during acquire so as to not get out of sync.
virtual void setShadowQueueSize(size_t size) = 0;
// dump state into a string
virtual void dump(String8& result, const char* prefix) const = 0;

View File

@ -36,7 +36,7 @@ BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
BufferQueueConsumer::~BufferQueueConsumer() {}
status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
nsecs_t expectedPresent) {
nsecs_t expectedPresent, uint64_t maxFrameNumber) {
ATRACE_CALL();
Mutex::Autolock lock(mCore->mMutex);
@ -89,17 +89,12 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
// the timestamps are being auto-generated by Surface. If the app isn't
// generating timestamps explicitly, it probably doesn't want frames to
// be discarded based on them.
//
// If the consumer is shadowing our queue, we also make sure that we
// don't drop so many buffers that the consumer hasn't received the
// onFrameAvailable callback for the buffer it acquires. That is, we
// want the buffer we return to be in the consumer's shadow queue.
size_t droppableBuffers = mCore->mConsumerShadowQueueSize > 1 ?
mCore->mConsumerShadowQueueSize - 1 : 0;
while (mCore->mQueue.size() > 1 && !mCore->mQueue[0].mIsAutoTimestamp) {
if (mCore->mConsumerHasShadowQueue && droppableBuffers == 0) {
BQ_LOGV("acquireBuffer: no droppable buffers in consumer's"
" shadow queue, continuing");
const BufferItem& bufferItem(mCore->mQueue[1]);
// If dropping entry[0] would leave us with a buffer that the
// consumer is not yet ready for, don't drop it.
if (maxFrameNumber && bufferItem.mFrameNumber > maxFrameNumber) {
break;
}
@ -112,7 +107,6 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
//
// We may want to add an additional criterion: don't drop the
// earlier buffer if entry[1]'s fence hasn't signaled yet.
const BufferItem& bufferItem(mCore->mQueue[1]);
nsecs_t desiredPresent = bufferItem.mTimestamp;
if (desiredPresent < expectedPresent - MAX_REASONABLE_NSEC ||
desiredPresent > expectedPresent) {
@ -137,18 +131,22 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
}
mCore->mQueue.erase(front);
front = mCore->mQueue.begin();
--droppableBuffers;
}
// See if the front buffer is due
// See if the front buffer is ready to be acquired
nsecs_t desiredPresent = front->mTimestamp;
if (desiredPresent > expectedPresent &&
desiredPresent < expectedPresent + MAX_REASONABLE_NSEC) {
bool bufferIsDue = desiredPresent <= expectedPresent ||
desiredPresent > expectedPresent + MAX_REASONABLE_NSEC;
bool consumerIsReady = maxFrameNumber > 0 ?
front->mFrameNumber <= maxFrameNumber : true;
if (!bufferIsDue || !consumerIsReady) {
BQ_LOGV("acquireBuffer: defer desire=%" PRId64 " expect=%" PRId64
" (%" PRId64 ") now=%" PRId64,
" (%" PRId64 ") now=%" PRId64 " frame=%" PRIu64
" consumer=%" PRIu64,
desiredPresent, expectedPresent,
desiredPresent - expectedPresent,
systemTime(CLOCK_MONOTONIC));
systemTime(CLOCK_MONOTONIC),
front->mFrameNumber, maxFrameNumber);
return PRESENT_LATER;
}
@ -553,14 +551,6 @@ sp<NativeHandle> BufferQueueConsumer::getSidebandStream() const {
return mCore->mSidebandStream;
}
void BufferQueueConsumer::setShadowQueueSize(size_t size) {
ATRACE_CALL();
BQ_LOGV("setShadowQueueSize: %zu", size);
Mutex::Autolock lock(mCore->mMutex);
mCore->mConsumerHasShadowQueue = true;
mCore->mConsumerShadowQueueSize = size;
}
void BufferQueueConsumer::dump(String8& result, const char* prefix) const {
mCore->dump(result, prefix);
}

View File

@ -71,9 +71,7 @@ BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
mIsAllocating(false),
mIsAllocatingCondition(),
mAllowAllocation(true),
mBufferAge(0),
mConsumerHasShadowQueue(false),
mConsumerShadowQueueSize(0)
mBufferAge(0)
{
if (allocator == NULL) {
sp<ISurfaceComposer> composer(ComposerService::getComposerService());

View File

@ -211,8 +211,8 @@ void ConsumerBase::dumpLocked(String8& result, const char* prefix) const {
}
status_t ConsumerBase::acquireBufferLocked(BufferItem *item,
nsecs_t presentWhen) {
status_t err = mConsumer->acquireBuffer(item, presentWhen);
nsecs_t presentWhen, uint64_t maxFrameNumber) {
status_t err = mConsumer->acquireBuffer(item, presentWhen, maxFrameNumber);
if (err != NO_ERROR) {
return err;
}

View File

@ -344,8 +344,9 @@ sp<GraphicBuffer> GLConsumer::getDebugTexImageBuffer() {
}
status_t GLConsumer::acquireBufferLocked(BufferItem *item,
nsecs_t presentWhen) {
status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen);
nsecs_t presentWhen, uint64_t maxFrameNumber) {
status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen,
maxFrameNumber);
if (err != NO_ERROR) {
return err;
}

View File

@ -52,7 +52,6 @@ enum {
SET_CONSUMER_USAGE_BITS,
SET_TRANSFORM_HINT,
GET_SIDEBAND_STREAM,
SET_SHADOW_QUEUE_SIZE,
DUMP,
};
@ -67,10 +66,12 @@ public:
virtual ~BpGraphicBufferConsumer();
virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen) {
virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen,
uint64_t maxFrameNumber) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
data.writeInt64(presentWhen);
data.writeUint64(maxFrameNumber);
status_t result = remote()->transact(ACQUIRE_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
@ -270,17 +271,6 @@ public:
return stream;
}
virtual void setShadowQueueSize(size_t size) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
data.writeInt64(static_cast<int64_t>(size));
status_t result = remote()->transact(SET_SHADOW_QUEUE_SIZE, data, &reply);
if (result != NO_ERROR) {
ALOGE("setShadowQueueSize failed (%d)", result);
return;
}
}
virtual void dump(String8& result, const char* prefix) const {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
@ -307,7 +297,8 @@ status_t BnGraphicBufferConsumer::onTransact(
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
BufferItem item;
int64_t presentWhen = data.readInt64();
status_t result = acquireBuffer(&item, presentWhen);
uint64_t maxFrameNumber = data.readUint64();
status_t result = acquireBuffer(&item, presentWhen, maxFrameNumber);
status_t err = reply->write(item);
if (err) return err;
reply->writeInt32(result);
@ -435,12 +426,6 @@ status_t BnGraphicBufferConsumer::onTransact(
}
return NO_ERROR;
}
case SET_SHADOW_QUEUE_SIZE: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
size_t size = static_cast<size_t>(data.readInt64());
setShadowQueueSize(size);
return NO_ERROR;
}
case DUMP: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
String8 result = data.readString8();

View File

@ -80,7 +80,11 @@ Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client,
mProtectedByApp(false),
mHasSurface(false),
mClientRef(client),
mPotentialCursor(false)
mPotentialCursor(false),
mQueueItemLock(),
mQueueItemCondition(),
mQueueItems(),
mLastFrameNumberReceived(0)
{
mCurrentCrop.makeInvalid();
mFlinger->getRenderEngine().genTextures(1, &mTextureName);
@ -127,10 +131,6 @@ void Layer::onFirstRef() {
mSurfaceFlingerConsumer->setContentsChangedListener(this);
mSurfaceFlingerConsumer->setName(mName);
// Set the shadow queue size to 0 to notify the BufferQueue that we are
// shadowing it
mSurfaceFlingerConsumer->setShadowQueueSize(0);
#ifdef TARGET_DISABLE_TRIPLE_BUFFERING
#warning "disabling triple buffering"
mSurfaceFlingerConsumer->setDefaultMaxBufferCount(2);
@ -167,9 +167,28 @@ void Layer::onFrameAvailable(const BufferItem& item) {
// Add this buffer from our internal queue tracker
{ // Autolock scope
Mutex::Autolock lock(mQueueItemLock);
// Reset the frame number tracker when we receive the first buffer after
// a frame number reset
if (item.mFrameNumber == 1) {
mLastFrameNumberReceived = 0;
}
// Ensure that callbacks are handled in order
while (item.mFrameNumber != mLastFrameNumberReceived + 1) {
status_t result = mQueueItemCondition.waitRelative(mQueueItemLock,
ms2ns(500));
if (result != NO_ERROR) {
ALOGE("[%s] Timed out waiting on callback", mName.string());
}
}
mQueueItems.push_back(item);
mSurfaceFlingerConsumer->setShadowQueueSize(mQueueItems.size());
android_atomic_inc(&mQueuedFrames);
// Wake up any pending callbacks
mLastFrameNumberReceived = item.mFrameNumber;
mQueueItemCondition.broadcast();
}
mFlinger->signalLayerUpdate();
@ -177,11 +196,25 @@ void Layer::onFrameAvailable(const BufferItem& item) {
void Layer::onFrameReplaced(const BufferItem& item) {
Mutex::Autolock lock(mQueueItemLock);
// Ensure that callbacks are handled in order
while (item.mFrameNumber != mLastFrameNumberReceived + 1) {
status_t result = mQueueItemCondition.waitRelative(mQueueItemLock,
ms2ns(500));
if (result != NO_ERROR) {
ALOGE("[%s] Timed out waiting on callback", mName.string());
}
}
if (mQueueItems.empty()) {
ALOGE("Can't replace a frame on an empty queue");
return;
}
mQueueItems.editItemAt(0) = item;
// Wake up any pending callbacks
mLastFrameNumberReceived = item.mFrameNumber;
mQueueItemCondition.broadcast();
}
void Layer::onSidebandStreamChanged() {
@ -1261,8 +1294,14 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions)
Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
getProducerStickyTransform() != 0);
uint64_t maxFrameNumber = 0;
{
Mutex::Autolock lock(mQueueItemLock);
maxFrameNumber = mLastFrameNumberReceived;
}
status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r,
mFlinger->mPrimaryDispSync);
mFlinger->mPrimaryDispSync, maxFrameNumber);
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.
@ -1272,12 +1311,7 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions)
// If the buffer has been rejected, remove it from the shadow queue
// and return early
Mutex::Autolock lock(mQueueItemLock);
// Update the BufferQueue with the new shadow queue size after
// dropping this item
mQueueItems.removeAt(0);
mSurfaceFlingerConsumer->setShadowQueueSize(mQueueItems.size());
android_atomic_dec(&mQueuedFrames);
return outDirtyRegion;
}
@ -1294,10 +1328,7 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions)
android_atomic_dec(&mQueuedFrames);
}
// Update the BufferQueue with our new shadow queue size, since we
// have removed at least one item
mQueueItems.removeAt(0);
mSurfaceFlingerConsumer->setShadowQueueSize(mQueueItems.size());
}

View File

@ -416,7 +416,9 @@ private:
// Local copy of the queued contents of the incoming BufferQueue
mutable Mutex mQueueItemLock;
Condition mQueueItemCondition;
Vector<BufferItem> mQueueItems;
uint64_t mLastFrameNumberReceived;
};
// ---------------------------------------------------------------------------

View File

@ -32,7 +32,7 @@ namespace android {
// ---------------------------------------------------------------------------
status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter,
const DispSync& dispSync)
const DispSync& dispSync, uint64_t maxFrameNumber)
{
ATRACE_CALL();
ALOGV("updateTexImage");
@ -54,7 +54,8 @@ status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter,
// 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, computeExpectedPresent(dispSync));
err = acquireBufferLocked(&item, computeExpectedPresent(dispSync),
maxFrameNumber);
if (err != NO_ERROR) {
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
err = NO_ERROR;
@ -104,8 +105,9 @@ status_t SurfaceFlingerConsumer::bindTextureImage()
}
status_t SurfaceFlingerConsumer::acquireBufferLocked(BufferItem* item,
nsecs_t presentWhen) {
status_t result = GLConsumer::acquireBufferLocked(item, presentWhen);
nsecs_t presentWhen, uint64_t maxFrameNumber) {
status_t result = GLConsumer::acquireBufferLocked(item, presentWhen,
maxFrameNumber);
if (result == NO_ERROR) {
mTransformToDisplayInverse = item->mTransformToDisplayInverse;
mSurfaceDamage = item->mSurfaceDamage;
@ -125,10 +127,6 @@ sp<NativeHandle> SurfaceFlingerConsumer::getSidebandStream() const {
return mConsumer->getSidebandStream();
}
void SurfaceFlingerConsumer::setShadowQueueSize(size_t size) {
mConsumer->setShadowQueueSize(size);
}
// 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

View File

@ -49,13 +49,15 @@ public:
virtual ~BufferRejecter() { }
};
virtual status_t acquireBufferLocked(BufferItem *item, nsecs_t presentWhen);
virtual status_t acquireBufferLocked(BufferItem *item, nsecs_t presentWhen,
uint64_t maxFrameNumber = 0) override;
// This version of updateTexImage() takes a functor that may be used to
// reject the newly acquired buffer. Unlike the GLConsumer version,
// this does not guarantee that the buffer has been bound to the GL
// texture.
status_t updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync);
status_t updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync,
uint64_t maxFrameNumber = 0);
// See GLConsumer::bindTextureImageLocked().
status_t bindTextureImage();
@ -70,9 +72,6 @@ public:
sp<NativeHandle> getSidebandStream() const;
// See IGraphicBufferConsumer::setShadowQueueSize
void setShadowQueueSize(size_t size);
nsecs_t computeExpectedPresent(const DispSync& dispSync);
private: