From 5ecfb68ffd63d352df0392dca6e95ef67a66c679 Mon Sep 17 00:00:00 2001 From: Dan Stoza Date: Mon, 4 Jan 2016 17:01:02 -0800 Subject: libgui: Fix attaching buffers without allocation This changes the way that BufferQueue selects slots in waitForFreeSlotThenRelock. This method is called from both dequeueBuffer and attachBuffer, but those two methods actually have different preferences: dequeueBuffer wants a slot with a buffer if possible (to avoid unnecessary allocations), but will settle for a slot without a buffer if no free buffers are available. attachBuffer wants a slot without a buffer if possible (to avoid clobbering an existing buffer), but will settle with clobbering a free buffer if no empty slots are available. These preferences are now respected, which has the side-effect of fixing a bug where it was not possible to attach a buffer if allocation is disabled (since the previous implementation assumed finding a slot without a buffer meant that the caller intended to allocate a buffer, which would accordingly be blocked since allocation is disabled). Bug: 26387372 Change-Id: Iefd550fd01925d8c51d6f062d5708d1f6d517edd --- libs/gui/BufferQueueProducer.cpp | 64 +++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 17 deletions(-) (limited to 'libs/gui/BufferQueueProducer.cpp') diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index c48f23728c..56f1a0905c 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -184,12 +184,35 @@ status_t BufferQueueProducer::setAsyncMode(bool async) { return NO_ERROR; } -status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller, +int BufferQueueProducer::getFreeBufferLocked() const { + if (mCore->mFreeBuffers.empty()) { + return BufferQueueCore::INVALID_BUFFER_SLOT; + } + auto slot = mCore->mFreeBuffers.front(); + mCore->mFreeBuffers.pop_front(); + return slot; +} + +int BufferQueueProducer::getFreeSlotLocked(int maxBufferCount) const { + if (mCore->mFreeSlots.empty()) { + return BufferQueueCore::INVALID_BUFFER_SLOT; + } + auto slot = *(mCore->mFreeSlots.begin()); + if (slot < maxBufferCount) { + mCore->mFreeSlots.erase(slot); + return slot; + } + return BufferQueueCore::INVALID_BUFFER_SLOT; +} + +status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller, int* found, status_t* returnFlags) const { + auto callerString = (caller == FreeSlotCaller::Dequeue) ? + "dequeueBuffer" : "attachBuffer"; bool tryAgain = true; while (tryAgain) { if (mCore->mIsAbandoned) { - BQ_LOGE("%s: BufferQueue has been abandoned", caller); + BQ_LOGE("%s: BufferQueue has been abandoned", callerString); return NO_INIT; } @@ -221,7 +244,7 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller, if (mCore->mBufferHasBeenQueued && dequeuedCount >= mCore->mMaxDequeuedBufferCount) { BQ_LOGE("%s: attempting to exceed the max dequeued buffer count " - "(%d)", caller, mCore->mMaxDequeuedBufferCount); + "(%d)", callerString, mCore->mMaxDequeuedBufferCount); return INVALID_OPERATION; } @@ -234,7 +257,7 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller, bool tooManyBuffers = mCore->mQueue.size() > static_cast(maxBufferCount); if (tooManyBuffers) { - BQ_LOGV("%s: queue size is %zu, waiting", caller, + BQ_LOGV("%s: queue size is %zu, waiting", callerString, mCore->mQueue.size()); } else { // If in single buffer mode and a shared buffer exists, always @@ -242,16 +265,23 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller, if (mCore->mSingleBufferMode && mCore->mSingleBufferSlot != BufferQueueCore::INVALID_BUFFER_SLOT) { *found = mCore->mSingleBufferSlot; - } else if (!mCore->mFreeBuffers.empty()) { - auto slot = mCore->mFreeBuffers.begin(); - *found = *slot; - mCore->mFreeBuffers.erase(slot); - } else if (mCore->mAllowAllocation && !mCore->mFreeSlots.empty()) { - auto slot = mCore->mFreeSlots.begin(); - // Only return free slots up to the max buffer count - if (*slot < maxBufferCount) { - *found = *slot; - mCore->mFreeSlots.erase(slot); + } else { + if (caller == FreeSlotCaller::Dequeue) { + // If we're calling this from dequeue, prefer free buffers + auto slot = getFreeBufferLocked(); + if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) { + *found = slot; + } else if (mCore->mAllowAllocation) { + *found = getFreeSlotLocked(maxBufferCount); + } + } else { + // If we're calling this from attach, prefer free slots + auto slot = getFreeSlotLocked(maxBufferCount); + if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) { + *found = slot; + } else { + *found = getFreeBufferLocked(); + } } } } @@ -338,8 +368,8 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, int found = BufferItem::INVALID_BUFFER_SLOT; while (found == BufferItem::INVALID_BUFFER_SLOT) { - status_t status = waitForFreeSlotThenRelock("dequeueBuffer", &found, - &returnFlags); + status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, + &found, &returnFlags); if (status != NO_ERROR) { return status; } @@ -614,7 +644,7 @@ status_t BufferQueueProducer::attachBuffer(int* outSlot, status_t returnFlags = NO_ERROR; int found; - status_t status = waitForFreeSlotThenRelock("attachBuffer", &found, + status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, &found, &returnFlags); if (status != NO_ERROR) { return status; -- cgit v1.2.3-59-g8ed1b