diff options
author | 2016-01-08 12:15:22 -0800 | |
---|---|---|
committer | 2016-02-02 11:15:47 -0800 | |
commit | 23b4abe024ea88c45e0b94c80e1fb537a573b143 (patch) | |
tree | 410a141ea14a17a668076f28de3f5260cd43543d | |
parent | c2a3d7aa1636d1fda62d65d7bd557a26428f1380 (diff) |
BQ: Improved buffer/slot tracking
- Explicitly track active buffers and unused slots on top of the
already existing tracking for free slots and free buffers.
Change-Id: Ife2678678e96f0eb0b3fb21571058378134bd868
-rw-r--r-- | include/gui/BufferItem.h | 4 | ||||
-rw-r--r-- | include/gui/BufferQueueCore.h | 27 | ||||
-rw-r--r-- | include/gui/BufferQueueProducer.h | 9 | ||||
-rw-r--r-- | include/gui/BufferSlot.h | 22 | ||||
-rw-r--r-- | include/gui/IGraphicBufferConsumer.h | 8 | ||||
-rw-r--r-- | include/gui/IGraphicBufferProducer.h | 15 | ||||
-rw-r--r-- | libs/gui/BufferItem.cpp | 3 | ||||
-rw-r--r-- | libs/gui/BufferQueueConsumer.cpp | 96 | ||||
-rw-r--r-- | libs/gui/BufferQueueCore.cpp | 301 | ||||
-rw-r--r-- | libs/gui/BufferQueueProducer.cpp | 196 | ||||
-rw-r--r-- | libs/gui/Surface.cpp | 7 |
11 files changed, 429 insertions, 259 deletions
diff --git a/include/gui/BufferItem.h b/include/gui/BufferItem.h index 370f5d52f7..a515f39848 100644 --- a/include/gui/BufferItem.h +++ b/include/gui/BufferItem.h @@ -125,6 +125,10 @@ class BufferItem : public Flattenable<BufferItem> { // Indicates that this buffer was queued by the producer. When in single // buffer mode acquire() can return a BufferItem that wasn't in the queue. bool mQueuedBuffer; + + // Indicates that this BufferItem contains a stale buffer which has already + // been released by the BufferQueue. + bool mIsStale; }; } // namespace android diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h index fbd5114c1d..23cd0666f1 100644 --- a/include/gui/BufferQueueCore.h +++ b/include/gui/BufferQueueCore.h @@ -105,17 +105,23 @@ private: // connected, mDequeueCondition must be broadcast. int getMaxBufferCountLocked() const; - // freeBufferLocked frees the GraphicBuffer and sync resources for the + // This performs the same computation but uses the given arguments instead + // of the member variables for mMaxBufferCount, mAsyncMode, and + // mDequeueBufferCannotBlock. + int getMaxBufferCountLocked(bool asyncMode, + bool dequeueBufferCannotBlock, int maxBufferCount) const; + + // clearBufferSlotLocked frees the GraphicBuffer and sync resources for the // given slot. - void freeBufferLocked(int slot, bool validate = true); + void clearBufferSlotLocked(int slot); // freeAllBuffersLocked frees the GraphicBuffer and sync resources for // all slots, even if they're currently dequeued, queued, or acquired. void freeAllBuffersLocked(); - // stillTracking returns true iff the buffer item is still being tracked - // in one of the slots. - bool stillTracking(const BufferItem* item) const; + // If delta is positive, makes more slots available. If negative, takes + // away slots. Returns false if the request can't be met. + bool adjustAvailableSlotsLocked(int delta); // waitWhileAllocatingLocked blocks until mIsAllocating is false. void waitWhileAllocatingLocked() const; @@ -179,13 +185,20 @@ private: Fifo mQueue; // mFreeSlots contains all of the slots which are FREE and do not currently - // have a buffer attached + // have a buffer attached. std::set<int> mFreeSlots; // mFreeBuffers contains all of the slots which are FREE and currently have - // a buffer attached + // a buffer attached. std::list<int> mFreeBuffers; + // mUnusedSlots contains all slots that are currently unused. They should be + // free and not have a buffer attached. + std::list<int> mUnusedSlots; + + // mActiveBuffers contains all slots which have a non-FREE buffer attached. + std::set<int> mActiveBuffers; + // mDequeueCondition is a condition variable used for dequeueBuffer in // synchronous mode. mutable Condition mDequeueCondition; diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h index 645a07bafd..dc05e9856a 100644 --- a/include/gui/BufferQueueProducer.h +++ b/include/gui/BufferQueueProducer.h @@ -187,9 +187,9 @@ private: // BufferQueueCore::INVALID_BUFFER_SLOT otherwise int getFreeBufferLocked() const; - // Returns the next free slot if one less than or equal to maxBufferCount - // is available or BufferQueueCore::INVALID_BUFFER_SLOT otherwise - int getFreeSlotLocked(int maxBufferCount) const; + // Returns the next free slot if one is available or + // BufferQueueCore::INVALID_BUFFER_SLOT otherwise + int getFreeSlotLocked() const; // waitForFreeSlotThenRelock finds the oldest slot in the FREE state. It may // block if there are no available slots and we are not in non-blocking @@ -200,8 +200,7 @@ private: Dequeue, Attach, }; - status_t waitForFreeSlotThenRelock(FreeSlotCaller caller, int* found, - status_t* returnFlags) const; + status_t waitForFreeSlotThenRelock(FreeSlotCaller caller, int* found) const; sp<BufferQueueCore> mCore; diff --git a/include/gui/BufferSlot.h b/include/gui/BufferSlot.h index 17a654a89b..943fa821a4 100644 --- a/include/gui/BufferSlot.h +++ b/include/gui/BufferSlot.h @@ -174,14 +174,15 @@ struct BufferState { struct BufferSlot { BufferSlot() - : mEglDisplay(EGL_NO_DISPLAY), + : mGraphicBuffer(nullptr), + mEglDisplay(EGL_NO_DISPLAY), mBufferState(), mRequestBufferCalled(false), mFrameNumber(0), mEglFence(EGL_NO_SYNC_KHR), + mFence(Fence::NO_FENCE), mAcquireCalled(false), - mNeedsCleanupOnRelease(false), - mAttachedByConsumer(false) { + mNeedsReallocation(false) { } // mGraphicBuffer points to the buffer allocated for this slot or is NULL @@ -191,8 +192,6 @@ struct BufferSlot { // mEglDisplay is the EGLDisplay used to create EGLSyncKHR objects. EGLDisplay mEglDisplay; - static const char* bufferStateName(BufferState state); - // mBufferState is the current state of this buffer slot. BufferState mBufferState; @@ -227,15 +226,10 @@ struct BufferSlot { // Indicates whether this buffer has been seen by a consumer yet bool mAcquireCalled; - // Indicates whether this buffer needs to be cleaned up by the - // consumer. This is set when a buffer in ACQUIRED state is freed. - // It causes releaseBuffer to return STALE_BUFFER_SLOT. - bool mNeedsCleanupOnRelease; - - // Indicates whether the buffer was attached on the consumer side. - // If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when dequeued - // to prevent the producer from using a stale cached buffer. - bool mAttachedByConsumer; + // Indicates whether the buffer was re-allocated without notifying the + // producer. If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when + // dequeued to prevent the producer from using a stale cached buffer. + bool mNeedsReallocation; }; } // namespace android diff --git a/include/gui/IGraphicBufferConsumer.h b/include/gui/IGraphicBufferConsumer.h index d4c9ee535f..ebce7fb03b 100644 --- a/include/gui/IGraphicBufferConsumer.h +++ b/include/gui/IGraphicBufferConsumer.h @@ -199,7 +199,9 @@ public: // cannot be less than maxAcquiredBufferCount. // // Return of a value other than NO_ERROR means an error has occurred: - // * BAD_VALUE - bufferCount was out of range (see above). + // * BAD_VALUE - one of the below conditions occurred: + // * bufferCount was out of range (see above). + // * failure to adjust the number of available slots. // * INVALID_OPERATION - attempting to call this after a producer connected. virtual status_t setMaxBufferCount(int bufferCount) = 0; @@ -212,7 +214,9 @@ public: // to be exceeded. // // Return of a value other than NO_ERROR means an error has occurred: - // * BAD_VALUE - maxAcquiredBuffers was out of range (see above). + // * BAD_VALUE - one of the below conditions occurred: + // * maxAcquiredBuffers was out of range (see above). + // * failure to adjust the number of available slots. // * INVALID_OPERATION - attempting to call this after a producer connected. virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0; diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h index 8646981001..1f4c8ac8ae 100644 --- a/include/gui/IGraphicBufferProducer.h +++ b/include/gui/IGraphicBufferProducer.h @@ -101,8 +101,9 @@ public: // * NO_INIT - the buffer queue has been abandoned. // * BAD_VALUE - one of the below conditions occurred: // * bufferCount was out of range (see above) - // * client has one or more buffers dequeued + // * client has too many buffers dequeued // * this call would cause the maxBufferCount value to be exceeded + // * failure to adjust the number of available slots. virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) = 0; // Set the async flag if the producer intends to asynchronously queue @@ -115,8 +116,10 @@ public: // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the buffer queue has been abandoned. - // * BAD_VALUE - this call would cause the maxBufferCount value to be + // * BAD_VALUE - one of the following has occurred: + // * this call would cause the maxBufferCount value to be // exceeded + // * failure to adjust the number of available slots. virtual status_t setAsyncMode(bool async) = 0; // dequeueBuffer requests a new buffer slot for the client to use. Ownership @@ -436,6 +439,9 @@ public: // * the producer is already connected // * api was out of range (see above). // * output was NULL. + // * Failure to adjust the number of available slots. This can + // happen because of trying to allocate/deallocate the async + // buffer in response to the value of producerControlledByApp. // * DEAD_OBJECT - the token is hosted by an already-dead process // // Additional negative errors may be returned by the internals, they @@ -534,6 +540,11 @@ public: // timeout of -1. If set (to a value other than -1), this will disable // non-blocking mode and its corresponding spare buffer (which is used to // ensure a buffer is always available). + // + // Return of a value other than NO_ERROR means an error has occurred: + // * BAD_VALUE - Failure to adjust the number of available slots. This can + // happen because of trying to allocate/deallocate the async + // buffer. virtual status_t setDequeueTimeout(nsecs_t timeout) = 0; }; diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp index de8ff70846..036ef1ecf7 100644 --- a/libs/gui/BufferItem.cpp +++ b/libs/gui/BufferItem.cpp @@ -39,7 +39,8 @@ BufferItem::BufferItem() : mTransformToDisplayInverse(false), mSurfaceDamage(), mSingleBufferMode(false), - mQueuedBuffer(true) { + mQueuedBuffer(true), + mIsStale(false) { } BufferItem::~BufferItem() {} diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index 6f9f21f296..d182f6b788 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -49,7 +49,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, // buffer so that the consumer can successfully set up the newly acquired // buffer before releasing the old one. int numAcquiredBuffers = 0; - for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + for (int s : mCore->mActiveBuffers) { if (mSlots[s].mBufferState.isAcquired()) { ++numAcquiredBuffers; } @@ -133,7 +133,8 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, BQ_LOGV("acquireBuffer: drop desire=%" PRId64 " expect=%" PRId64 " size=%zu", desiredPresent, expectedPresent, mCore->mQueue.size()); - if (mCore->stillTracking(front)) { + + if (!front->mIsStale) { // Front buffer is still in mSlots, so mark the slot as free mSlots[front->mSlot].mBufferState.freeQueued(); @@ -144,13 +145,17 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, mSlots[front->mSlot].mBufferState.isFree()) { mSlots[front->mSlot].mBufferState.mShared = false; } - // Don't put the shared buffer on the free list. + + // Don't put the shared buffer on the free list if (!mSlots[front->mSlot].mBufferState.isShared()) { + mCore->mActiveBuffers.erase(front->mSlot); mCore->mFreeBuffers.push_back(front->mSlot); } + listener = mCore->mConnectedProducerListener; ++numDroppedBuffers; } + mCore->mQueue.erase(front); front = mCore->mQueue.begin(); } @@ -205,6 +210,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, outBuffer->mSurfaceDamage = Region::INVALID_REGION; outBuffer->mSingleBufferMode = true; outBuffer->mQueuedBuffer = false; + outBuffer->mIsStale = false; } else { slot = front->mSlot; *outBuffer = *front; @@ -216,10 +222,9 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }", slot, outBuffer->mFrameNumber, outBuffer->mGraphicBuffer->handle); - // If the front buffer is still being tracked, update its slot state - if (mCore->stillTracking(outBuffer)) { + + if (!outBuffer->mIsStale) { mSlots[slot].mAcquireCalled = true; - mSlots[slot].mNeedsCleanupOnRelease = false; // Don't decrease the queue count if the BufferItem wasn't // previously in the queue. This happens in single buffer mode when // the queue is empty and the BufferItem is created above. @@ -270,7 +275,7 @@ status_t BufferQueueConsumer::detachBuffer(int slot) { return NO_INIT; } - if (mCore->mSingleBufferMode) { + if (mCore->mSingleBufferMode || slot == mCore->mSingleBufferSlot) { BQ_LOGE("detachBuffer: detachBuffer not allowed in single buffer" "mode"); return BAD_VALUE; @@ -287,7 +292,9 @@ status_t BufferQueueConsumer::detachBuffer(int slot) { } mSlots[slot].mBufferState.detachConsumer(); - mCore->freeBufferLocked(slot); + mCore->mActiveBuffers.erase(slot); + mCore->mFreeSlots.insert(slot); + mCore->clearBufferSlotLocked(slot); mCore->mDequeueCondition.broadcast(); mCore->validateConsistencyLocked(); @@ -316,7 +323,7 @@ status_t BufferQueueConsumer::attachBuffer(int* outSlot, // Make sure we don't have too many acquired buffers int numAcquiredBuffers = 0; - for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + for (int s : mCore->mActiveBuffers) { if (mSlots[s].mBufferState.isAcquired()) { ++numAcquiredBuffers; } @@ -351,14 +358,14 @@ status_t BufferQueueConsumer::attachBuffer(int* outSlot, return NO_MEMORY; } + mCore->mActiveBuffers.insert(found); *outSlot = found; ATRACE_BUFFER_INDEX(*outSlot); BQ_LOGV("attachBuffer: returning slot %d", *outSlot); mSlots[*outSlot].mGraphicBuffer = buffer; mSlots[*outSlot].mBufferState.attachConsumer(); - mSlots[*outSlot].mAttachedByConsumer = true; - mSlots[*outSlot].mNeedsCleanupOnRelease = false; + mSlots[*outSlot].mNeedsReallocation = true; mSlots[*outSlot].mFence = Fence::NO_FENCE; mSlots[*outSlot].mFrameNumber = 0; @@ -411,39 +418,33 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, return STALE_BUFFER_SLOT; } - - if (mSlots[slot].mBufferState.isAcquired()) { - mSlots[slot].mEglDisplay = eglDisplay; - mSlots[slot].mEglFence = eglFence; - mSlots[slot].mFence = releaseFence; - mSlots[slot].mBufferState.release(); - - // After leaving single buffer mode, the shared buffer will - // still be around. Mark it as no longer shared if this - // operation causes it to be free. - if (!mCore->mSingleBufferMode && - mSlots[slot].mBufferState.isFree()) { - mSlots[slot].mBufferState.mShared = false; - } - // Don't put the shared buffer on the free list. - if (!mSlots[slot].mBufferState.isShared()) { - mCore->mFreeBuffers.push_back(slot); - } - - listener = mCore->mConnectedProducerListener; - BQ_LOGV("releaseBuffer: releasing slot %d", slot); - } else if (mSlots[slot].mNeedsCleanupOnRelease) { - BQ_LOGV("releaseBuffer: releasing a stale buffer slot %d " - "(state = %s)", slot, mSlots[slot].mBufferState.string()); - mSlots[slot].mNeedsCleanupOnRelease = false; - return STALE_BUFFER_SLOT; - } else { + if (!mSlots[slot].mBufferState.isAcquired()) { BQ_LOGE("releaseBuffer: attempted to release buffer slot %d " "but its state was %s", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; } + mSlots[slot].mEglDisplay = eglDisplay; + mSlots[slot].mEglFence = eglFence; + mSlots[slot].mFence = releaseFence; + mSlots[slot].mBufferState.release(); + + // After leaving single buffer mode, the shared buffer will + // still be around. Mark it as no longer shared if this + // operation causes it to be free. + if (!mCore->mSingleBufferMode && mSlots[slot].mBufferState.isFree()) { + mSlots[slot].mBufferState.mShared = false; + } + // Don't put the shared buffer on the free list. + if (!mSlots[slot].mBufferState.isShared()) { + mCore->mActiveBuffers.erase(slot); + mCore->mFreeBuffers.push_back(slot); + } + + listener = mCore->mConnectedProducerListener; + BQ_LOGV("releaseBuffer: releasing slot %d", slot); + mCore->mDequeueCondition.broadcast(); mCore->validateConsistencyLocked(); } // Autolock scope @@ -497,6 +498,7 @@ status_t BufferQueueConsumer::disconnect() { mCore->mConsumerListener = NULL; mCore->mQueue.clear(); mCore->freeAllBuffersLocked(); + mCore->mSingleBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; mCore->mDequeueCondition.broadcast(); return NO_ERROR; } @@ -579,6 +581,15 @@ status_t BufferQueueConsumer::setMaxBufferCount(int bufferCount) { return BAD_VALUE; } + int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, + mCore->mDequeueBufferCannotBlock, bufferCount) - + mCore->getMaxBufferCountLocked(); + if (!mCore->adjustAvailableSlotsLocked(delta)) { + BQ_LOGE("setMaxBufferCount: BufferQueue failed to adjust the number of " + "available slots. Delta = %d", delta); + return BAD_VALUE; + } + mCore->mMaxBufferCount = bufferCount; return NO_ERROR; } @@ -612,8 +623,17 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount( return BAD_VALUE; } + if (!mCore->adjustAvailableSlotsLocked( + maxAcquiredBuffers - mCore->mMaxAcquiredBufferCount)) { + BQ_LOGE("setMaxAcquiredBufferCount: BufferQueue failed to adjust the " + "number of available slots. Delta = %d", + maxAcquiredBuffers - mCore->mMaxAcquiredBufferCount); + return BAD_VALUE; + } + BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers); mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers; + mCore->validateConsistencyLocked(); return NO_ERROR; } diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp index c24ad19897..f02ff5f40a 100644 --- a/libs/gui/BufferQueueCore.cpp +++ b/libs/gui/BufferQueueCore.cpp @@ -52,6 +52,8 @@ BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) : mQueue(), mFreeSlots(), mFreeBuffers(), + mUnusedSlots(), + mActiveBuffers(), mDequeueCondition(), mDequeueBufferCannotBlock(false), mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888), @@ -82,8 +84,14 @@ BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) : BQ_LOGE("createGraphicBufferAlloc failed"); } } - for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { - mFreeSlots.insert(slot); + + int numStartingBuffers = getMaxBufferCountLocked(); + for (int s = 0; s < numStartingBuffers; s++) { + mFreeSlots.insert(s); + } + for (int s = numStartingBuffers; s < BufferQueueDefs::NUM_BUFFER_SLOTS; + s++) { + mUnusedSlots.push_front(s); } } @@ -113,32 +121,26 @@ void BufferQueueCore::dump(String8& result, const char* prefix) const { mDefaultHeight, mDefaultBufferFormat, mTransformHint, mQueue.size(), fifo.string()); - // Trim the free buffers so as to not spam the dump - int maxBufferCount = 0; - for (int s = BufferQueueDefs::NUM_BUFFER_SLOTS - 1; s >= 0; --s) { - const BufferSlot& slot(mSlots[s]); - if (!slot.mBufferState.isFree() || - slot.mGraphicBuffer != NULL) { - maxBufferCount = s + 1; - break; - } - } + for (int s : mActiveBuffers) { + const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer); + result.appendFormat("%s%s[%02d:%p] state=%-8s, %p [%4ux%4u:%4u,%3X]\n", + prefix, (mSlots[s].mBufferState.isAcquired()) ? ">" : " ", s, + buffer.get(), mSlots[s].mBufferState.string(), buffer->handle, + buffer->width, buffer->height, buffer->stride, buffer->format); - for (int s = 0; s < maxBufferCount; ++s) { - const BufferSlot& slot(mSlots[s]); - const sp<GraphicBuffer>& buffer(slot.mGraphicBuffer); - result.appendFormat("%s%s[%02d:%p] state=%-8s", prefix, - (slot.mBufferState.isAcquired()) ? ">" : " ", - s, buffer.get(), - slot.mBufferState.string()); - - if (buffer != NULL) { - result.appendFormat(", %p [%4ux%4u:%4u,%3X]", buffer->handle, - buffer->width, buffer->height, buffer->stride, - buffer->format); - } + } + for (int s : mFreeBuffers) { + const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer); + result.appendFormat("%s [%02d:%p] state=%-8s, %p [%4ux%4u:%4u,%3X]\n", + prefix, s, buffer.get(), mSlots[s].mBufferState.string(), + buffer->handle, buffer->width, buffer->height, buffer->stride, + buffer->format); + } - result.append("\n"); + for (int s : mFreeSlots) { + const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer); + result.appendFormat("%s [%02d:%p] state=%-8s\n", prefix, s, + buffer.get(), mSlots[s].mBufferState.string()); } } @@ -156,44 +158,33 @@ int BufferQueueCore::getMinMaxBufferCountLocked() const { return getMinUndequeuedBufferCountLocked() + 1; } +int BufferQueueCore::getMaxBufferCountLocked(bool asyncMode, + bool dequeueBufferCannotBlock, int maxBufferCount) const { + int maxCount = mMaxAcquiredBufferCount + mMaxDequeuedBufferCount + + ((asyncMode || dequeueBufferCannotBlock) ? 1 : 0); + maxCount = std::min(maxBufferCount, maxCount); + return maxCount; +} + int BufferQueueCore::getMaxBufferCountLocked() const { int maxBufferCount = mMaxAcquiredBufferCount + mMaxDequeuedBufferCount + - (mAsyncMode || mDequeueBufferCannotBlock ? 1 : 0); + ((mAsyncMode || mDequeueBufferCannotBlock) ? 1 : 0); // limit maxBufferCount by mMaxBufferCount always maxBufferCount = std::min(mMaxBufferCount, maxBufferCount); - // Any buffers that are dequeued by the producer or sitting in the queue - // waiting to be consumed need to have their slots preserved. Such buffers - // will temporarily keep the max buffer count up until the slots no longer - // need to be preserved. - for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { - BufferState state = mSlots[s].mBufferState; - if (state.isQueued() || state.isDequeued()) { - maxBufferCount = s + 1; - } - } - return maxBufferCount; } -void BufferQueueCore::freeBufferLocked(int slot, bool validate) { - BQ_LOGV("freeBufferLocked: slot %d", slot); - bool hadBuffer = mSlots[slot].mGraphicBuffer != NULL; +void BufferQueueCore::clearBufferSlotLocked(int slot) { + BQ_LOGV("clearBufferSlotLocked: slot %d", slot); + mSlots[slot].mGraphicBuffer.clear(); - if (mSlots[slot].mBufferState.isAcquired()) { - mSlots[slot].mNeedsCleanupOnRelease = true; - } - if (!mSlots[slot].mBufferState.isFree()) { - mFreeSlots.insert(slot); - } else if (hadBuffer) { - // If the slot was FREE, but we had a buffer, we need to move this slot - // from the free buffers list to the the free slots list - mFreeBuffers.remove(slot); - mFreeSlots.insert(slot); - } - mSlots[slot].mAcquireCalled = false; + mSlots[slot].mBufferState.reset(); + mSlots[slot].mRequestBufferCalled = false; mSlots[slot].mFrameNumber = 0; + mSlots[slot].mAcquireCalled = false; + mSlots[slot].mNeedsReallocation = true; // Destroy fence as BufferQueue now takes ownership if (mSlots[slot].mEglFence != EGL_NO_SYNC_KHR) { @@ -201,35 +192,63 @@ void BufferQueueCore::freeBufferLocked(int slot, bool validate) { mSlots[slot].mEglFence = EGL_NO_SYNC_KHR; } mSlots[slot].mFence = Fence::NO_FENCE; - if (validate) { - validateConsistencyLocked(); - } + mSlots[slot].mEglDisplay = EGL_NO_DISPLAY; } void BufferQueueCore::freeAllBuffersLocked() { - mBufferHasBeenQueued = false; - for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { - freeBufferLocked(s, false); - mSlots[s].mBufferState.reset(); + for (int s : mFreeSlots) { + clearBufferSlotLocked(s); } - mSingleBufferSlot = INVALID_BUFFER_SLOT; - validateConsistencyLocked(); -} -bool BufferQueueCore::stillTracking(const BufferItem* item) const { - const BufferSlot& slot = mSlots[item->mSlot]; + for (int s : mFreeBuffers) { + mFreeSlots.insert(s); + clearBufferSlotLocked(s); + } + mFreeBuffers.clear(); + + for (int s : mActiveBuffers) { + mFreeSlots.insert(s); + clearBufferSlotLocked(s); + } + mActiveBuffers.clear(); + + for (auto& b : mQueue) { + b.mIsStale = true; + } - BQ_LOGV("stillTracking: item { slot=%d/%" PRIu64 " buffer=%p } " - "slot { slot=%d/%" PRIu64 " buffer=%p }", - item->mSlot, item->mFrameNumber, - (item->mGraphicBuffer.get() ? item->mGraphicBuffer->handle : 0), - item->mSlot, slot.mFrameNumber, - (slot.mGraphicBuffer.get() ? slot.mGraphicBuffer->handle : 0)); + validateConsistencyLocked(); +} - // Compare item with its original buffer slot. We can check the slot as - // the buffer would not be moved to a different slot by the producer. - return (slot.mGraphicBuffer != NULL) && - (item->mGraphicBuffer->handle == slot.mGraphicBuffer->handle); +bool BufferQueueCore::adjustAvailableSlotsLocked(int delta) { + if (delta >= 0) { + while (delta > 0) { + if (mUnusedSlots.empty()) { + return false; + } + int slot = mUnusedSlots.back(); + mUnusedSlots.pop_back(); + mFreeSlots.insert(slot); + delta--; + } + } else { + while (delta < 0) { + if (!mFreeSlots.empty()) { + auto slot = mFreeSlots.begin(); + clearBufferSlotLocked(*slot); + mUnusedSlots.push_back(*slot); + mFreeSlots.erase(slot); + } else if (!mFreeBuffers.empty()) { + int slot = mFreeBuffers.back(); + clearBufferSlotLocked(slot); + mUnusedSlots.push_back(slot); + mFreeBuffers.pop_back(); + } else { + return false; + } + delta++; + } + } + return true; } void BufferQueueCore::waitWhileAllocatingLocked() const { @@ -241,47 +260,127 @@ void BufferQueueCore::waitWhileAllocatingLocked() const { void BufferQueueCore::validateConsistencyLocked() const { static const useconds_t PAUSE_TIME = 0; + int allocatedSlots = 0; for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { bool isInFreeSlots = mFreeSlots.count(slot) != 0; bool isInFreeBuffers = std::find(mFreeBuffers.cbegin(), mFreeBuffers.cend(), slot) != mFreeBuffers.cend(); - if (mSlots[slot].mBufferState.isFree() && - !mSlots[slot].mBufferState.isShared()) { + bool isInActiveBuffers = mActiveBuffers.count(slot) != 0; + bool isInUnusedSlots = + std::find(mUnusedSlots.cbegin(), mUnusedSlots.cend(), slot) != + mUnusedSlots.cend(); + + if (isInFreeSlots || isInFreeBuffers || isInActiveBuffers) { + allocatedSlots++; + } + + if (isInUnusedSlots) { + if (isInFreeSlots) { + BQ_LOGE("Slot %d is in mUnusedSlots and in mFreeSlots", slot); + usleep(PAUSE_TIME); + } + if (isInFreeBuffers) { + BQ_LOGE("Slot %d is in mUnusedSlots and in mFreeBuffers", slot); + usleep(PAUSE_TIME); + } + if (isInActiveBuffers) { + BQ_LOGE("Slot %d is in mUnusedSlots and in mActiveBuffers", + slot); + usleep(PAUSE_TIME); + } + if (!mSlots[slot].mBufferState.isFree()) { + BQ_LOGE("Slot %d is in mUnusedSlots but is not FREE", slot); + usleep(PAUSE_TIME); + } + if (mSlots[slot].mGraphicBuffer != NULL) { + BQ_LOGE("Slot %d is in mUnusedSluts but has an active buffer", + slot); + usleep(PAUSE_TIME); + } + } else if (isInFreeSlots) { + if (isInUnusedSlots) { + BQ_LOGE("Slot %d is in mFreeSlots and in mUnusedSlots", slot); + usleep(PAUSE_TIME); + } + if (isInFreeBuffers) { + BQ_LOGE("Slot %d is in mFreeSlots and in mFreeBuffers", slot); + usleep(PAUSE_TIME); + } + if (isInActiveBuffers) { + BQ_LOGE("Slot %d is in mFreeSlots and in mActiveBuffers", slot); + usleep(PAUSE_TIME); + } + if (!mSlots[slot].mBufferState.isFree()) { + BQ_LOGE("Slot %d is in mFreeSlots but is not FREE", slot); + usleep(PAUSE_TIME); + } + if (mSlots[slot].mGraphicBuffer != NULL) { + BQ_LOGE("Slot %d is in mFreeSlots but has a buffer", + slot); + usleep(PAUSE_TIME); + } + } else if (isInFreeBuffers) { + if (isInUnusedSlots) { + BQ_LOGE("Slot %d is in mFreeBuffers and in mUnusedSlots", slot); + usleep(PAUSE_TIME); + } + if (isInFreeSlots) { + BQ_LOGE("Slot %d is in mFreeBuffers and in mFreeSlots", slot); + usleep(PAUSE_TIME); + } + if (isInActiveBuffers) { + BQ_LOGE("Slot %d is in mFreeBuffers and in mActiveBuffers", + slot); + usleep(PAUSE_TIME); + } + if (!mSlots[slot].mBufferState.isFree()) { + BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE", slot); + usleep(PAUSE_TIME); + } if (mSlots[slot].mGraphicBuffer == NULL) { - if (!isInFreeSlots) { - BQ_LOGE("Slot %d is FREE but is not in mFreeSlots", slot); - usleep(PAUSE_TIME); - } - if (isInFreeBuffers) { - BQ_LOGE("Slot %d is in mFreeSlots " - "but is also in mFreeBuffers", slot); - usleep(PAUSE_TIME); - } - } else { - if (!isInFreeBuffers) { - BQ_LOGE("Slot %d is FREE but is not in mFreeBuffers", slot); - usleep(PAUSE_TIME); - } - if (isInFreeSlots) { - BQ_LOGE("Slot %d is in mFreeBuffers " - "but is also in mFreeSlots", slot); - usleep(PAUSE_TIME); - } + BQ_LOGE("Slot %d is in mFreeBuffers but has no buffer", slot); + usleep(PAUSE_TIME); + } + } else if (isInActiveBuffers) { + if (isInUnusedSlots) { + BQ_LOGE("Slot %d is in mActiveBuffers and in mUnusedSlots", + slot); + usleep(PAUSE_TIME); } - } else { if (isInFreeSlots) { - BQ_LOGE("Slot %d is in mFreeSlots but is not FREE (%s)", - slot, mSlots[slot].mBufferState.string()); + BQ_LOGE("Slot %d is in mActiveBuffers and in mFreeSlots", slot); usleep(PAUSE_TIME); } if (isInFreeBuffers) { - BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE (%s)", - slot, mSlots[slot].mBufferState.string()); + BQ_LOGE("Slot %d is in mActiveBuffers and in mFreeBuffers", + slot); + usleep(PAUSE_TIME); + } + if (mSlots[slot].mBufferState.isFree() && + !mSlots[slot].mBufferState.isShared()) { + BQ_LOGE("Slot %d is in mActiveBuffers but is FREE", slot); usleep(PAUSE_TIME); } + if (mSlots[slot].mGraphicBuffer == NULL && !mIsAllocating) { + BQ_LOGE("Slot %d is in mActiveBuffers but has no buffer", slot); + usleep(PAUSE_TIME); + } + } else { + BQ_LOGE("Slot %d isn't in any of mUnusedSlots, mFreeSlots, " + "mFreeBuffers, or mActiveBuffers", slot); + usleep(PAUSE_TIME); } } + + if (allocatedSlots != getMaxBufferCountLocked()) { + BQ_LOGE("Number of allocated slots is incorrect. Allocated = %d, " + "Should be %d (%zu free slots, %zu free buffers, " + "%zu activeBuffers, %zu unusedSlots)", allocatedSlots, + getMaxBufferCountLocked(), mFreeSlots.size(), + mFreeBuffers.size(), mActiveBuffers.size(), + mUnusedSlots.size()); + } } } // namespace android diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 5b1aaa0671..22a2d79f51 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -96,7 +96,7 @@ status_t BufferQueueProducer::setMaxDequeuedBufferCount( } // There must be no dequeued buffers when changing the buffer count. - for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + for (int s : mCore->mActiveBuffers) { if (mSlots[s].mBufferState.isDequeued()) { BQ_LOGE("setMaxDequeuedBufferCount: buffer owned by producer"); return BAD_VALUE; @@ -132,8 +132,15 @@ status_t BufferQueueProducer::setMaxDequeuedBufferCount( // buffers and will release all of its buffer references. We don't // clear the queue, however, so that currently queued buffers still // get displayed. - mCore->freeAllBuffersLocked(); + if (!mCore->adjustAvailableSlotsLocked( + maxDequeuedBuffers - mCore->mMaxDequeuedBufferCount)) { + BQ_LOGE("setMaxDequeuedBufferCount: BufferQueue failed to adjust " + "the number of available slots. Delta = %d", + maxDequeuedBuffers - mCore->mMaxDequeuedBufferCount); + return BAD_VALUE; + } mCore->mMaxDequeuedBufferCount = maxDequeuedBuffers; + mCore->validateConsistencyLocked(); mCore->mDequeueCondition.broadcast(); listener = mCore->mConsumerListener; } // Autolock scope @@ -172,7 +179,17 @@ status_t BufferQueueProducer::setAsyncMode(bool async) { return BAD_VALUE; } + int delta = mCore->getMaxBufferCountLocked(async, + mCore->mDequeueBufferCannotBlock, mCore->mMaxBufferCount) + - mCore->getMaxBufferCountLocked(); + + if (!mCore->adjustAvailableSlotsLocked(delta)) { + BQ_LOGE("setAsyncMode: BufferQueue failed to adjust the number of " + "available slots. Delta = %d", delta); + return BAD_VALUE; + } mCore->mAsyncMode = async; + mCore->validateConsistencyLocked(); mCore->mDequeueCondition.broadcast(); listener = mCore->mConsumerListener; } // Autolock scope @@ -188,25 +205,22 @@ int BufferQueueProducer::getFreeBufferLocked() const { if (mCore->mFreeBuffers.empty()) { return BufferQueueCore::INVALID_BUFFER_SLOT; } - auto slot = mCore->mFreeBuffers.front(); + int slot = mCore->mFreeBuffers.front(); mCore->mFreeBuffers.pop_front(); return slot; } -int BufferQueueProducer::getFreeSlotLocked(int maxBufferCount) const { +int BufferQueueProducer::getFreeSlotLocked() 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; + auto slot = mCore->mFreeSlots.begin(); + mCore->mFreeSlots.erase(slot); + return *slot; } status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller, - int* found, status_t* returnFlags) const { + int* found) const { auto callerString = (caller == FreeSlotCaller::Dequeue) ? "dequeueBuffer" : "attachBuffer"; bool tryAgain = true; @@ -216,20 +230,9 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller, return NO_INIT; } - const int maxBufferCount = mCore->getMaxBufferCountLocked(); - - // Free up any buffers that are in slots beyond the max buffer count - for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { - assert(mSlots[s].mBufferState.isFree()); - if (mSlots[s].mGraphicBuffer != NULL) { - mCore->freeBufferLocked(s); - *returnFlags |= RELEASE_ALL_BUFFERS; - } - } - int dequeuedCount = 0; int acquiredCount = 0; - for (int s = 0; s < maxBufferCount; ++s) { + for (int s : mCore->mActiveBuffers) { if (mSlots[s].mBufferState.isDequeued()) { ++dequeuedCount; } @@ -254,6 +257,7 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller, // our slots are empty but we have many buffers in the queue. This can // cause us to run out of memory if we outrun the consumer. Wait here if // it looks like we have too many buffers queued up. + const int maxBufferCount = mCore->getMaxBufferCountLocked(); bool tooManyBuffers = mCore->mQueue.size() > static_cast<size_t>(maxBufferCount); if (tooManyBuffers) { @@ -268,15 +272,15 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller, } else { if (caller == FreeSlotCaller::Dequeue) { // If we're calling this from dequeue, prefer free buffers - auto slot = getFreeBufferLocked(); + int slot = getFreeBufferLocked(); if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) { *found = slot; } else if (mCore->mAllowAllocation) { - *found = getFreeSlotLocked(maxBufferCount); + *found = getFreeSlotLocked(); } } else { // If we're calling this from attach, prefer free slots - auto slot = getFreeSlotLocked(maxBufferCount); + int slot = getFreeSlotLocked(); if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) { *found = slot; } else { @@ -369,7 +373,7 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, int found = BufferItem::INVALID_BUFFER_SLOT; while (found == BufferItem::INVALID_BUFFER_SLOT) { status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, - &found, &returnFlags); + &found); if (status != NO_ERROR) { return status; } @@ -388,24 +392,36 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, // requested attributes, we free it and attempt to get another one. if (!mCore->mAllowAllocation) { if (buffer->needsReallocation(width, height, format, usage)) { - if (mCore->mSingleBufferMode && - mCore->mSingleBufferSlot == found) { + if (mCore->mSingleBufferSlot == found) { BQ_LOGE("dequeueBuffer: cannot re-allocate a shared" "buffer"); return BAD_VALUE; } - - mCore->freeBufferLocked(found); + mCore->mFreeSlots.insert(found); + mCore->clearBufferSlotLocked(found); found = BufferItem::INVALID_BUFFER_SLOT; continue; } } } + const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer); + if (mCore->mSingleBufferSlot == found && + buffer->needsReallocation(width, height, format, usage)) { + BQ_LOGE("dequeueBuffer: cannot re-allocate a shared" + "buffer"); + + return BAD_VALUE; + } + + if (mCore->mSingleBufferSlot != found) { + mCore->mActiveBuffers.insert(found); + } *outSlot = found; ATRACE_BUFFER_INDEX(found); - attachedByConsumer = mSlots[found].mAttachedByConsumer; + attachedByConsumer = mSlots[found].mNeedsReallocation; + mSlots[found].mNeedsReallocation = false; mSlots[found].mBufferState.dequeue(); @@ -417,7 +433,6 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, mSlots[found].mBufferState.mShared = true; } - const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer); if ((buffer == NULL) || buffer->needsReallocation(width, height, format, usage)) { @@ -452,8 +467,6 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, *outFence = mSlots[found].mFence; mSlots[found].mEglFence = EGL_NO_SYNC_KHR; mSlots[found].mFence = Fence::NO_FENCE; - - mCore->validateConsistencyLocked(); } // Autolock scope if (returnFlags & BUFFER_NEEDS_REALLOCATION) { @@ -481,6 +494,8 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned"); return NO_INIT; } + + mCore->validateConsistencyLocked(); } // Autolock scope } @@ -527,9 +542,8 @@ status_t BufferQueueProducer::detachBuffer(int slot) { return NO_INIT; } - if (mCore->mSingleBufferMode) { - BQ_LOGE("detachBuffer: cannot detach a buffer in single buffer" - "mode"); + if (mCore->mSingleBufferMode || mCore->mSingleBufferSlot == slot) { + BQ_LOGE("detachBuffer: cannot detach a buffer in single buffer mode"); return BAD_VALUE; } @@ -548,7 +562,9 @@ status_t BufferQueueProducer::detachBuffer(int slot) { } mSlots[slot].mBufferState.detachProducer(); - mCore->freeBufferLocked(slot); + mCore->mActiveBuffers.erase(slot); + mCore->mFreeSlots.insert(slot); + mCore->clearBufferSlotLocked(slot); mCore->mDequeueCondition.broadcast(); mCore->validateConsistencyLocked(); @@ -593,12 +609,13 @@ status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer, int found = mCore->mFreeBuffers.front(); mCore->mFreeBuffers.remove(found); + mCore->mFreeSlots.insert(found); BQ_LOGV("detachNextBuffer detached slot %d", found); *outBuffer = mSlots[found].mGraphicBuffer; *outFence = mSlots[found].mFence; - mCore->freeBufferLocked(found); + mCore->clearBufferSlotLocked(found); mCore->validateConsistencyLocked(); return NO_ERROR; @@ -644,8 +661,7 @@ status_t BufferQueueProducer::attachBuffer(int* outSlot, status_t returnFlags = NO_ERROR; int found; - status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, &found, - &returnFlags); + status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, &found); if (status != NO_ERROR) { return status; } @@ -666,7 +682,8 @@ status_t BufferQueueProducer::attachBuffer(int* outSlot, mSlots[*outSlot].mEglFence = EGL_NO_SYNC_KHR; mSlots[*outSlot].mFence = Fence::NO_FENCE; mSlots[*outSlot].mRequestBufferCalled = true; - + mSlots[*outSlot].mAcquireCalled = false; + mCore->mActiveBuffers.insert(found); mCore->validateConsistencyLocked(); return returnFlags; @@ -722,11 +739,9 @@ status_t BufferQueueProducer::queueBuffer(int slot, return NO_INIT; } - const int maxBufferCount = mCore->getMaxBufferCountLocked(); - - if (slot < 0 || slot >= maxBufferCount) { + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)", - slot, maxBufferCount); + slot, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("queueBuffer: slot %d is not owned by the producer " @@ -807,9 +822,8 @@ status_t BufferQueueProducer::queueBuffer(int slot, // state to see if we need to replace it BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin()); if (front->mIsDroppable) { - // If the front queued buffer is still being tracked, we first - // mark it as freed - if (mCore->stillTracking(front)) { + + if (!front->mIsStale) { mSlots[front->mSlot].mBufferState.freeQueued(); // After leaving single buffer mode, the shared buffer will @@ -821,9 +835,11 @@ status_t BufferQueueProducer::queueBuffer(int slot, } // Don't put the shared buffer on the free list. if (!mSlots[front->mSlot].mBufferState.isShared()) { - mCore->mFreeBuffers.push_front(front->mSlot); + mCore->mActiveBuffers.erase(front->mSlot); + mCore->mFreeBuffers.push_back(front->mSlot); } } + // Overwrite the droppable buffer with the incoming one *front = item; frameReplacedListener = mCore->mConsumerListener; @@ -926,8 +942,10 @@ status_t BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) { // Don't put the shared buffer on the free list. if (!mSlots[slot].mBufferState.isShared()) { - mCore->mFreeBuffers.push_front(slot); + mCore->mActiveBuffers.erase(slot); + mCore->mFreeBuffers.push_back(slot); } + mSlots[slot].mFence = fence; mCore->mDequeueCondition.broadcast(); mCore->validateConsistencyLocked(); @@ -1020,6 +1038,17 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, return BAD_VALUE; } + int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, + mDequeueTimeout < 0 ? + mCore->mConsumerControlledByApp && producerControlledByApp : false, + mCore->mMaxBufferCount) - + mCore->getMaxBufferCountLocked(); + if (!mCore->adjustAvailableSlotsLocked(delta)) { + BQ_LOGE("connect: BufferQueue failed to adjust the number of available " + "slots. Delta = %d", delta); + return BAD_VALUE; + } + int status = NO_ERROR; switch (api) { case NATIVE_WINDOW_API_EGL: @@ -1056,8 +1085,9 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, mCore->mDequeueBufferCannotBlock = mCore->mConsumerControlledByApp && producerControlledByApp; } - mCore->mAllowAllocation = true; + mCore->mAllowAllocation = true; + mCore->validateConsistencyLocked(); return status; } @@ -1094,6 +1124,8 @@ status_t BufferQueueProducer::disconnect(int api) { token->unlinkToDeath( static_cast<IBinder::DeathRecipient*>(this)); } + mCore->mSingleBufferSlot = + BufferQueueCore::INVALID_BUFFER_SLOT; mCore->mConnectedProducerListener = NULL; mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API; mCore->mSidebandStream.clear(); @@ -1138,7 +1170,6 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, PixelFormat format, uint32_t usage) { ATRACE_CALL(); while (true) { - Vector<int> freeSlots; size_t newBufferCount = 0; uint32_t allocWidth = 0; uint32_t allocHeight = 0; @@ -1154,32 +1185,11 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, return; } - int currentBufferCount = 0; - for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { - if (mSlots[slot].mGraphicBuffer != NULL) { - ++currentBufferCount; - } else { - if (!mSlots[slot].mBufferState.isFree()) { - BQ_LOGE("allocateBuffers: slot %d without buffer is not FREE", - slot); - continue; - } - - freeSlots.push_back(slot); - } - } - - int maxBufferCount = mCore->getMaxBufferCountLocked(); - BQ_LOGV("allocateBuffers: allocating from %d buffers up to %d buffers", - currentBufferCount, maxBufferCount); - if (maxBufferCount <= currentBufferCount) - return; - newBufferCount = - static_cast<size_t>(maxBufferCount - currentBufferCount); - if (freeSlots.size() < newBufferCount) { - BQ_LOGE("allocateBuffers: ran out of free slots"); + newBufferCount = mCore->mFreeSlots.size(); + if (newBufferCount == 0) { return; } + allocWidth = width > 0 ? width : mCore->mDefaultWidth; allocHeight = height > 0 ? height : mCore->mDefaultHeight; allocFormat = format != 0 ? format : mCore->mDefaultBufferFormat; @@ -1221,24 +1231,23 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, } for (size_t i = 0; i < newBufferCount; ++i) { - int slot = freeSlots[i]; - if (!mSlots[slot].mBufferState.isFree()) { - // A consumer allocated the FREE slot with attachBuffer. Discard the buffer we - // allocated. - BQ_LOGV("allocateBuffers: slot %d was acquired while allocating. " - "Dropping allocated buffer.", slot); + if (mCore->mFreeSlots.empty()) { + BQ_LOGV("allocateBuffers: a slot was occupied while " + "allocating. Dropping allocated buffer."); continue; } - mCore->freeBufferLocked(slot); // Clean up the slot first - mSlots[slot].mGraphicBuffer = buffers[i]; - mSlots[slot].mFence = Fence::NO_FENCE; + auto slot = mCore->mFreeSlots.begin(); + mCore->clearBufferSlotLocked(*slot); // Clean up the slot first + mSlots[*slot].mGraphicBuffer = buffers[i]; + mSlots[*slot].mFence = Fence::NO_FENCE; // freeBufferLocked puts this slot on the free slots list. Since // we then attached a buffer, move the slot to free buffer list. mCore->mFreeSlots.erase(slot); - mCore->mFreeBuffers.push_front(slot); + mCore->mFreeBuffers.push_front(*slot); - BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", slot); + BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", + *slot); } mCore->mIsAllocating = false; @@ -1297,8 +1306,17 @@ status_t BufferQueueProducer::setDequeueTimeout(nsecs_t timeout) { BQ_LOGV("setDequeueTimeout: %" PRId64, timeout); Mutex::Autolock lock(mCore->mMutex); + int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, false, + mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked(); + if (!mCore->adjustAvailableSlotsLocked(delta)) { + BQ_LOGE("setDequeueTimeout: BufferQueue failed to adjust the number of " + "available slots. Delta = %d", delta); + return BAD_VALUE; + } + mDequeueTimeout = timeout; mCore->mDequeueBufferCannotBlock = false; + mCore->validateConsistencyLocked(); return NO_ERROR; } diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 9e90ad0f5c..6fc55c344e 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -759,6 +759,13 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer, *outFence = Fence::NO_FENCE; } + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + if (mSlots[i].buffer != NULL && + mSlots[i].buffer->handle == buffer->handle) { + mSlots[i].buffer = NULL; + } + } + return NO_ERROR; } |