diff options
author | Lajos Molnar <lajos@google.com> | 2013-05-03 14:50:50 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-05-23 22:17:54 +0000 |
commit | c5d7b7d323bba8772a9005f7d300ad983a04733a (patch) | |
tree | 07479c25735c0bbaccd497db21a90a87c3926242 | |
parent | d837969640efbc97eda2034c7811dda807d4174f (diff) | |
download | frameworks_native-c5d7b7d323bba8772a9005f7d300ad983a04733a.zip frameworks_native-c5d7b7d323bba8772a9005f7d300ad983a04733a.tar.gz frameworks_native-c5d7b7d323bba8772a9005f7d300ad983a04733a.tar.bz2 |
BufferQueue: track buffer-queue by instance vs. by reference
Instead of representing the buffer-queue as a vector of buffer
indices, represent them as a vector of BufferItems (copies).
This allows modifying the buffer slots independent of the queued
buffers.
As part of this change, BufferSlot properties that are only
been relevant in the buffer-queue have been removed.
Also, invalid scalingMode in queueBuffer now returns an error.
ConsumerBase has also changed to allow reuse of the same
buffer slots by different buffers.
Change-Id: If2a698fa142b67c69ad41b8eaca6e127eb3ef75b
Signed-off-by: Lajos Molnar <lajos@google.com>
Related-to-bug: 7093648
-rw-r--r-- | include/gui/BufferQueue.h | 38 | ||||
-rw-r--r-- | include/gui/ConsumerBase.h | 19 | ||||
-rw-r--r-- | include/gui/GLConsumer.h | 10 | ||||
-rw-r--r-- | libs/gui/BufferItemConsumer.cpp | 4 | ||||
-rw-r--r-- | libs/gui/BufferQueue.cpp | 198 | ||||
-rw-r--r-- | libs/gui/ConsumerBase.cpp | 47 | ||||
-rw-r--r-- | libs/gui/CpuConsumer.cpp | 4 | ||||
-rw-r--r-- | libs/gui/GLConsumer.cpp | 28 | ||||
-rw-r--r-- | services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp | 7 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlingerConsumer.cpp | 2 |
10 files changed, 232 insertions, 125 deletions
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h index 34264bf..8475a71 100644 --- a/include/gui/BufferQueue.h +++ b/include/gui/BufferQueue.h @@ -240,7 +240,8 @@ public: mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), mTimestamp(0), mFrameNumber(0), - mBuf(INVALID_BUFFER_SLOT) { + mBuf(INVALID_BUFFER_SLOT), + mAcquireCalled(false) { mCrop.makeInvalid(); } // mGraphicBuffer points to the buffer allocated for this slot, or is NULL @@ -269,6 +270,9 @@ public: // mFence is a fence that will signal when the buffer is idle. sp<Fence> mFence; + + // Indicates whether this buffer has been seen by a consumer yet + bool mAcquireCalled; }; // The following public functions are the consumer-facing interface @@ -285,7 +289,7 @@ public: // releaseBuffer releases a buffer slot from the consumer back to the // BufferQueue. This may be done while the buffer's contents are still // being accessed. The fence will signal when the buffer is no longer - // in use. + // in use. frameNumber is used to indentify the exact buffer returned. // // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free // any references to the just-released buffer that it might have, as if it @@ -294,7 +298,8 @@ public: // // Note that the dependencies on EGL will be removed once we switch to using // the Android HW Sync HAL. - status_t releaseBuffer(int buf, EGLDisplay display, EGLSyncKHR fence, + status_t releaseBuffer(int buf, uint64_t frameNumber, + EGLDisplay display, EGLSyncKHR fence, const sp<Fence>& releaseFence); // consumerConnect connects a consumer to the BufferQueue. Only one @@ -410,20 +415,20 @@ private: // connected, mDequeueCondition must be broadcast. int getMaxBufferCountLocked() const; + // stillTracking returns true iff the buffer item is still being tracked + // in one of the slots. + bool stillTracking(const BufferItem *item) const; + struct BufferSlot { BufferSlot() : mEglDisplay(EGL_NO_DISPLAY), mBufferState(BufferSlot::FREE), mRequestBufferCalled(false), - mTransform(0), - mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), - mTimestamp(0), mFrameNumber(0), mEglFence(EGL_NO_SYNC_KHR), mAcquireCalled(false), mNeedsCleanupOnRelease(false) { - mCrop.makeInvalid(); } // mGraphicBuffer points to the buffer allocated for this slot or is NULL @@ -482,21 +487,6 @@ private: // needed but useful for debugging and catching producer bugs. bool mRequestBufferCalled; - // mCrop is the current crop rectangle for this buffer slot. - Rect mCrop; - - // mTransform is the current transform flags for this buffer slot. - // (example: NATIVE_WINDOW_TRANSFORM_ROT_90) - uint32_t mTransform; - - // mScalingMode is the current scaling mode for this buffer slot. - // (example: NATIVE_WINDOW_SCALING_MODE_FREEZE) - uint32_t mScalingMode; - - // mTimestamp is the current timestamp for this buffer slot. This gets - // to set by queueBuffer each time this slot is queued. - int64_t mTimestamp; - // mFrameNumber is the number of the queued frame for this slot. This // is used to dequeue buffers in LRU order (useful because buffers // may be released before their release fence is signaled). @@ -592,7 +582,7 @@ private: mutable Condition mDequeueCondition; // mQueue is a FIFO of queued buffers used in synchronous mode - typedef Vector<int> Fifo; + typedef Vector<BufferItem> Fifo; Fifo mQueue; // mAbandoned indicates that the BufferQueue will no longer be used to @@ -613,7 +603,7 @@ private: mutable Mutex mMutex; // mFrameCounter is the free running counter, incremented on every - // successful queueBuffer call. + // successful queueBuffer call, and buffer allocation. uint64_t mFrameCounter; // mBufferHasBeenQueued is true once a buffer has been queued. It is diff --git a/include/gui/ConsumerBase.h b/include/gui/ConsumerBase.h index 6250d8f..1d51bc9 100644 --- a/include/gui/ConsumerBase.h +++ b/include/gui/ConsumerBase.h @@ -160,17 +160,23 @@ protected: // Derived classes should override this method to perform any cleanup that // must take place when a buffer is released back to the BufferQueue. If // it is overridden the derived class's implementation must call - // ConsumerBase::releaseBufferLocked. - virtual status_t releaseBufferLocked(int buf, EGLDisplay display, - EGLSyncKHR eglFence); + // ConsumerBase::releaseBufferLocked.e + virtual status_t releaseBufferLocked(int slot, + const sp<GraphicBuffer> graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence); + + // returns true iff the slot still has the graphicBuffer in it. + bool stillTracking(int slot, const sp<GraphicBuffer> graphicBuffer); // addReleaseFence* adds the sync points associated with a fence to the set // of sync points that must be reached before the buffer in the given slot // may be used after the slot has been released. This should be called by // derived classes each time some asynchronous work is kicked off that // references the buffer. - status_t addReleaseFence(int slot, const sp<Fence>& fence); - status_t addReleaseFenceLocked(int slot, const sp<Fence>& fence); + status_t addReleaseFence(int slot, + const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence); + status_t addReleaseFenceLocked(int slot, + const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence); // Slot contains the information and object references that // ConsumerBase maintains about a BufferQueue buffer slot. @@ -184,6 +190,9 @@ protected: // overwritten. The buffer can be dequeued before the fence signals; // the producer is responsible for delaying writes until it signals. sp<Fence> mFence; + + // the frame number of the last acquired frame for this slot + uint64_t mFrameNumber; }; // mSlots stores the buffers that have been allocated by the BufferQueue diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h index 1e88927..031684e 100644 --- a/include/gui/GLConsumer.h +++ b/include/gui/GLConsumer.h @@ -241,11 +241,13 @@ protected: // releaseBufferLocked overrides the ConsumerBase method to update the // mEglSlots array in addition to the ConsumerBase. - virtual status_t releaseBufferLocked(int buf, EGLDisplay display, - EGLSyncKHR eglFence); + virtual status_t releaseBufferLocked(int slot, + const sp<GraphicBuffer> graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence); - status_t releaseBufferLocked(int buf, EGLSyncKHR eglFence) { - return releaseBufferLocked(buf, mEglDisplay, eglFence); + status_t releaseBufferLocked(int slot, + const sp<GraphicBuffer> graphicBuffer, EGLSyncKHR eglFence) { + return releaseBufferLocked(slot, graphicBuffer, mEglDisplay, eglFence); } static bool isExternalFormat(uint32_t format); diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp index 7db1b84..ba04bdf 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp @@ -82,9 +82,9 @@ status_t BufferItemConsumer::releaseBuffer(const BufferItem &item, Mutex::Autolock _l(mMutex); - err = addReleaseFenceLocked(item.mBuf, releaseFence); + err = addReleaseFenceLocked(item.mBuf, item.mGraphicBuffer, releaseFence); - err = releaseBufferLocked(item.mBuf, EGL_NO_DISPLAY, + err = releaseBufferLocked(item.mBuf, item.mGraphicBuffer, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); if (err != OK) { BI_LOGE("Failed to release buffer: %s (%d)", diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index 942151f..34dbd71 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -418,6 +418,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, return NO_INIT; } + mSlots[*outBuf].mFrameNumber = ~0; mSlots[*outBuf].mGraphicBuffer = graphicBuffer; } } @@ -435,7 +436,8 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, eglDestroySyncKHR(dpy, eglFence); } - ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf, + ST_LOGV("dequeueBuffer: returning slot=%d/%llu buf=%p flags=%#x", *outBuf, + mSlots[*outBuf].mFrameNumber, mSlots[*outBuf].mGraphicBuffer->handle, returnFlags); return returnFlags; @@ -491,15 +493,22 @@ status_t BufferQueue::queueBuffer(int buf, return BAD_VALUE; } - ST_LOGV("queueBuffer: slot=%d time=%#llx crop=[%d,%d,%d,%d] tr=%#x " - "scale=%s", - buf, timestamp, crop.left, crop.top, crop.right, crop.bottom, - transform, scalingModeName(scalingMode)); + switch (scalingMode) { + case NATIVE_WINDOW_SCALING_MODE_FREEZE: + case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: + case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: + case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP: + break; + default: + ST_LOGE("unknown scaling mode: %d", scalingMode); + return -EINVAL; + } sp<ConsumerListener> listener; { // scope for the lock Mutex::Autolock lock(mMutex); + if (mAbandoned) { ST_LOGE("queueBuffer: BufferQueue has been abandoned!"); return NO_INIT; @@ -519,6 +528,12 @@ status_t BufferQueue::queueBuffer(int buf, return -EINVAL; } + ST_LOGV("queueBuffer: slot=%d/%llu time=%#llx crop=[%d,%d,%d,%d] " + "tr=%#x scale=%s", + buf, mFrameCounter + 1, timestamp, + crop.left, crop.top, crop.right, crop.bottom, + transform, scalingModeName(scalingMode)); + const sp<GraphicBuffer>& graphicBuffer(mSlots[buf].mGraphicBuffer); Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight()); Rect croppedCrop; @@ -529,9 +544,25 @@ status_t BufferQueue::queueBuffer(int buf, return -EINVAL; } + mSlots[buf].mFence = fence; + mSlots[buf].mBufferState = BufferSlot::QUEUED; + mFrameCounter++; + mSlots[buf].mFrameNumber = mFrameCounter; + + BufferItem item; + item.mAcquireCalled = mSlots[buf].mAcquireCalled; + item.mGraphicBuffer = mSlots[buf].mGraphicBuffer; + item.mCrop = crop; + item.mTransform = transform; + item.mScalingMode = scalingMode; + item.mTimestamp = timestamp; + item.mFrameNumber = mFrameCounter; + item.mBuf = buf; + item.mFence = fence; + if (mSynchronousMode) { // In synchronous mode we queue all buffers in a FIFO. - mQueue.push_back(buf); + mQueue.push_back(item); // Synchronous mode always signals that an additional frame should // be consumed. @@ -539,7 +570,7 @@ status_t BufferQueue::queueBuffer(int buf, } else { // In asynchronous mode we only keep the most recent buffer. if (mQueue.empty()) { - mQueue.push_back(buf); + mQueue.push_back(item); // Asynchronous mode only signals that a frame should be // consumed if no previous frame was pending. If a frame were @@ -547,34 +578,15 @@ status_t BufferQueue::queueBuffer(int buf, listener = mConsumerListener; } else { Fifo::iterator front(mQueue.begin()); - // buffer currently queued is freed - mSlots[*front].mBufferState = BufferSlot::FREE; + // buffer slot currently queued is marked free if still tracked + if (stillTracking(front)) { + mSlots[front->mBuf].mBufferState = BufferSlot::FREE; + } // and we record the new buffer index in the queued list - *front = buf; + *front = item; } } - mSlots[buf].mTimestamp = timestamp; - mSlots[buf].mCrop = crop; - mSlots[buf].mTransform = transform; - mSlots[buf].mFence = fence; - - switch (scalingMode) { - case NATIVE_WINDOW_SCALING_MODE_FREEZE: - case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: - case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: - break; - default: - ST_LOGE("unknown scaling mode: %d (ignoring)", scalingMode); - scalingMode = mSlots[buf].mScalingMode; - break; - } - - mSlots[buf].mBufferState = BufferSlot::QUEUED; - mSlots[buf].mScalingMode = scalingMode; - mFrameCounter++; - mSlots[buf].mFrameNumber = mFrameCounter; - mBufferHasBeenQueued = true; mDequeueCondition.broadcast(); @@ -718,7 +730,14 @@ void BufferQueue::dump(String8& result, const char* prefix) const { int fifoSize = 0; Fifo::const_iterator i(mQueue.begin()); while (i != mQueue.end()) { - fifo.appendFormat("%02d ", *i++); + fifo.appendFormat("%02d:%p crop=[%d,%d,%d,%d], " + "xform=0x%02x, time=%#llx, scale=%s\n", + i->mBuf, i->mGraphicBuffer.get(), + i->mCrop.left, i->mCrop.top, i->mCrop.right, + i->mCrop.bottom, i->mTransform, i->mTimestamp, + scalingModeName(i->mScalingMode) + ); + i++; fifoSize++; } @@ -746,14 +765,10 @@ void BufferQueue::dump(String8& result, const char* prefix) const { for (int i=0 ; i<maxBufferCount ; i++) { const BufferSlot& slot(mSlots[i]); result.appendFormat( - "%s%s[%02d] " - "state=%-8s, crop=[%d,%d,%d,%d], " - "xform=0x%02x, time=%#llx, scale=%s", + "%s%s[%02d:%p] state=%-8s", prefix, (slot.mBufferState == BufferSlot::ACQUIRED)?">":" ", i, - stateName(slot.mBufferState), - slot.mCrop.left, slot.mCrop.top, slot.mCrop.right, - slot.mCrop.bottom, slot.mTransform, slot.mTimestamp, - scalingModeName(slot.mScalingMode) + slot.mGraphicBuffer.get(), + stateName(slot.mBufferState) ); const sp<GraphicBuffer>& buf(slot.mGraphicBuffer); @@ -820,27 +835,27 @@ status_t BufferQueue::acquireBuffer(BufferItem *buffer) { // deep, while in synchronous mode we use the oldest buffer. if (!mQueue.empty()) { Fifo::iterator front(mQueue.begin()); - int buf = *front; - + int buf = front->mBuf; + *buffer = *front; ATRACE_BUFFER_INDEX(buf); - if (mSlots[buf].mAcquireCalled) { + ST_LOGV("acquireBuffer: acquiring { slot=%d/%llu, buffer=%p }", + front->mBuf, front->mFrameNumber, + front->mGraphicBuffer->handle); + // if front buffer still being tracked update slot state + if (stillTracking(front)) { + mSlots[buf].mAcquireCalled = true; + mSlots[buf].mNeedsCleanupOnRelease = false; + mSlots[buf].mBufferState = BufferSlot::ACQUIRED; + mSlots[buf].mFence = Fence::NO_FENCE; + } + + // If the buffer has previously been acquired by the consumer, set + // mGraphicBuffer to NULL to avoid unnecessarily remapping this + // buffer on the consumer side. + if (buffer->mAcquireCalled) { buffer->mGraphicBuffer = NULL; - } else { - buffer->mGraphicBuffer = mSlots[buf].mGraphicBuffer; } - buffer->mCrop = mSlots[buf].mCrop; - buffer->mTransform = mSlots[buf].mTransform; - buffer->mScalingMode = mSlots[buf].mScalingMode; - buffer->mFrameNumber = mSlots[buf].mFrameNumber; - buffer->mTimestamp = mSlots[buf].mTimestamp; - buffer->mBuf = buf; - buffer->mFence = mSlots[buf].mFence; - - mSlots[buf].mAcquireCalled = true; - mSlots[buf].mNeedsCleanupOnRelease = false; - mSlots[buf].mBufferState = BufferSlot::ACQUIRED; - mSlots[buf].mFence = Fence::NO_FENCE; mQueue.erase(front); mDequeueCondition.broadcast(); @@ -853,7 +868,8 @@ status_t BufferQueue::acquireBuffer(BufferItem *buffer) { return NO_ERROR; } -status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display, +status_t BufferQueue::releaseBuffer( + int buf, uint64_t frameNumber, EGLDisplay display, EGLSyncKHR eglFence, const sp<Fence>& fence) { ATRACE_CALL(); ATRACE_BUFFER_INDEX(buf); @@ -864,12 +880,35 @@ status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display, return BAD_VALUE; } - mSlots[buf].mEglDisplay = display; - mSlots[buf].mEglFence = eglFence; - mSlots[buf].mFence = fence; + // Check if this buffer slot is on the queue + bool slotQueued = false; + Fifo::iterator front(mQueue.begin()); + while (front != mQueue.end() && !slotQueued) { + if (front->mBuf == buf) + slotQueued = true; + front++; + } + + // If the frame number has changed because buffer has been reallocated, + // we can ignore this releaseBuffer for the old buffer. + if (frameNumber != mSlots[buf].mFrameNumber) { + // This should only occur if new buffer is still in the queue + ALOGE_IF(!slotQueued, + "received old buffer(#%lld) after new buffer(#%lld) on same " + "slot #%d already acquired", frameNumber, + mSlots[buf].mFrameNumber, buf); + return STALE_BUFFER_SLOT; + } + // this should never happen + ALOGE_IF(slotQueued, + "received new buffer(#%lld) on slot #%d that has not yet been " + "acquired", frameNumber, buf); // The buffer can now only be released if its in the acquired state if (mSlots[buf].mBufferState == BufferSlot::ACQUIRED) { + mSlots[buf].mEglDisplay = display; + mSlots[buf].mEglFence = eglFence; + mSlots[buf].mFence = fence; mSlots[buf].mBufferState = BufferSlot::FREE; } else if (mSlots[buf].mNeedsCleanupOnRelease) { ST_LOGV("releasing a stale buf %d its state was %d", buf, mSlots[buf].mBufferState); @@ -934,6 +973,17 @@ status_t BufferQueue::getReleasedBuffers(uint32_t* slotMask) { mask |= 1 << i; } } + + // Remove buffers in flight (on the queue) from the mask where acquire has + // been called, as the consumer will not receive the buffer address, so + // it should not free these slots. + Fifo::iterator front(mQueue.begin()); + while (front != mQueue.end()) { + if (front->mAcquireCalled) + mask &= ~(1 << front->mBuf); + front++; + } + *slotMask = mask; ST_LOGV("getReleasedBuffers: returning mask %#x", mask); @@ -977,16 +1027,14 @@ status_t BufferQueue::setMaxAcquiredBufferCount(int maxAcquiredBuffers) { } void BufferQueue::freeAllBuffersExceptHeadLocked() { - int head = -1; - if (!mQueue.empty()) { - Fifo::iterator front(mQueue.begin()); - head = *front; - } + // only called if mQueue is not empty + Fifo::iterator front(mQueue.begin()); mBufferHasBeenQueued = false; for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - if (i != head) { + const BufferSlot &slot = mSlots[i]; + if (slot.mGraphicBuffer == NULL || + slot.mGraphicBuffer->handle != front->mGraphicBuffer->handle) freeBufferLocked(i); - } } } @@ -1052,6 +1100,22 @@ int BufferQueue::getMaxBufferCountLocked() const { return maxBufferCount; } +bool BufferQueue::stillTracking(const BufferItem *item) const { + const BufferSlot &slot = mSlots[item->mBuf]; + + ST_LOGV("stillTracking?: item: { slot=%d/%llu, buffer=%p }, " + "slot: { slot=%d/%llu, buffer=%p }", + item->mBuf, item->mFrameNumber, + (item->mGraphicBuffer.get() ? item->mGraphicBuffer->handle : 0), + item->mBuf, slot.mFrameNumber, + (slot.mGraphicBuffer.get() ? slot.mGraphicBuffer->handle : 0)); + + // 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); +} + BufferQueue::ProxyConsumerListener::ProxyConsumerListener( const wp<BufferQueue::ConsumerListener>& consumerListener): mConsumerListener(consumerListener) {} diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index 8d911c9..fd9d153 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -95,6 +95,7 @@ void ConsumerBase::freeBufferLocked(int slotIndex) { CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); mSlots[slotIndex].mGraphicBuffer = 0; mSlots[slotIndex].mFence = Fence::NO_FENCE; + mSlots[slotIndex].mFrameNumber = 0; } // Used for refactoring, should not be in final interface @@ -191,21 +192,31 @@ status_t ConsumerBase::acquireBufferLocked(BufferQueue::BufferItem *item) { mSlots[item->mBuf].mGraphicBuffer = item->mGraphicBuffer; } + mSlots[item->mBuf].mFrameNumber = item->mFrameNumber; mSlots[item->mBuf].mFence = item->mFence; - CB_LOGV("acquireBufferLocked: -> slot=%d", item->mBuf); + CB_LOGV("acquireBufferLocked: -> slot=%d/%llu", + item->mBuf, item->mFrameNumber); return OK; } -status_t ConsumerBase::addReleaseFence(int slot, const sp<Fence>& fence) { +status_t ConsumerBase::addReleaseFence(int slot, + const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence) { Mutex::Autolock lock(mMutex); - return addReleaseFenceLocked(slot, fence); + return addReleaseFenceLocked(slot, graphicBuffer, fence); } -status_t ConsumerBase::addReleaseFenceLocked(int slot, const sp<Fence>& fence) { +status_t ConsumerBase::addReleaseFenceLocked(int slot, + const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence) { CB_LOGV("addReleaseFenceLocked: slot=%d", slot); + // If consumer no longer tracks this graphicBuffer, we can safely + // drop this fence, as it will never be received by the producer. + if (!stillTracking(slot, graphicBuffer)) { + return OK; + } + if (!mSlots[slot].mFence.get()) { mSlots[slot].mFence = fence; } else { @@ -225,11 +236,20 @@ status_t ConsumerBase::addReleaseFenceLocked(int slot, const sp<Fence>& fence) { return OK; } -status_t ConsumerBase::releaseBufferLocked(int slot, EGLDisplay display, - EGLSyncKHR eglFence) { - CB_LOGV("releaseBufferLocked: slot=%d", slot); - status_t err = mBufferQueue->releaseBuffer(slot, display, eglFence, - mSlots[slot].mFence); +status_t ConsumerBase::releaseBufferLocked( + int slot, const sp<GraphicBuffer> graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) { + // If consumer no longer tracks this graphicBuffer (we received a new + // buffer on the same slot), the buffer producer is definitely no longer + // tracking it. + if (!stillTracking(slot, graphicBuffer)) { + return OK; + } + + CB_LOGV("releaseBufferLocked: slot=%d/%llu", + slot, mSlots[slot].mFrameNumber); + status_t err = mBufferQueue->releaseBuffer(slot, mSlots[slot].mFrameNumber, + display, eglFence, mSlots[slot].mFence); if (err == BufferQueue::STALE_BUFFER_SLOT) { freeBufferLocked(slot); } @@ -239,4 +259,13 @@ status_t ConsumerBase::releaseBufferLocked(int slot, EGLDisplay display, return err; } +bool ConsumerBase::stillTracking(int slot, + const sp<GraphicBuffer> graphicBuffer) { + if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) { + return false; + } + return (mSlots[slot].mGraphicBuffer != NULL && + mSlots[slot].mGraphicBuffer->handle == graphicBuffer->handle); +} + } // namespace android diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp index 0543649..0834361 100644 --- a/libs/gui/CpuConsumer.cpp +++ b/libs/gui/CpuConsumer.cpp @@ -189,7 +189,9 @@ status_t CpuConsumer::releaseAcquiredBufferLocked(int lockedIdx) { // disconnected after this buffer was acquired. if (CC_LIKELY(mAcquiredBuffers[lockedIdx].mGraphicBuffer == mSlots[buf].mGraphicBuffer)) { - releaseBufferLocked(buf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); + releaseBufferLocked( + buf, mAcquiredBuffers[lockedIdx].mGraphicBuffer, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); } AcquiredBuffer &ab = mAcquiredBuffers.editItemAt(lockedIdx); diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 344a93a..6d29edc 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -188,12 +188,16 @@ status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item) { return NO_ERROR; } -status_t GLConsumer::releaseBufferLocked(int buf, EGLDisplay display, - EGLSyncKHR eglFence) { - status_t err = ConsumerBase::releaseBufferLocked(buf, display, eglFence); - +status_t GLConsumer::releaseBufferLocked(int buf, + sp<GraphicBuffer> graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) { + // release the buffer if it hasn't already been discarded by the + // BufferQueue. This can happen, for example, when the producer of this + // buffer has reallocated the original buffer slot after this buffer + // was acquired. + status_t err = ConsumerBase::releaseBufferLocked( + buf, graphicBuffer, display, eglFence); mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; - return err; } @@ -237,7 +241,10 @@ status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item) if (err != NO_ERROR) { // Release the buffer we just acquired. It's not safe to // release the old buffer, so instead we just drop the new frame. - releaseBufferLocked(buf, mEglDisplay, EGL_NO_SYNC_KHR); + // As we are still under lock since acquireBuffer, it is safe to + // release by slot. + releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer, + mEglDisplay, EGL_NO_SYNC_KHR); return err; } @@ -248,7 +255,8 @@ status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item) // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - status_t status = releaseBufferLocked(mCurrentTexture, mEglDisplay, + status_t status = releaseBufferLocked( + mCurrentTexture, mCurrentTextureBuf, mEglDisplay, mEglSlots[mCurrentTexture].mEglFence); if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) { ST_LOGE("releaseAndUpdate: failed to release buffer: %s (%d)", @@ -334,7 +342,8 @@ status_t GLConsumer::checkAndUpdateEglStateLocked() { void GLConsumer::setReleaseFence(const sp<Fence>& fence) { if (fence->isValid() && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - status_t err = addReleaseFence(mCurrentTexture, fence); + status_t err = addReleaseFence(mCurrentTexture, + mCurrentTextureBuf, fence); if (err != OK) { ST_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err); @@ -503,7 +512,8 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { return UNKNOWN_ERROR; } sp<Fence> fence(new Fence(fenceFd)); - status_t err = addReleaseFenceLocked(mCurrentTexture, fence); + status_t err = addReleaseFenceLocked(mCurrentTexture, + mCurrentTextureBuf, fence); if (err != OK) { ST_LOGE("syncForReleaseLocked: error adding release fence: " "%s (%d)", strerror(-err), err); diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp index 8b454ce..10bca38 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp @@ -103,8 +103,8 @@ status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& if (mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT && item.mBuf != mCurrentBufferSlot) { // Release the previous buffer. - err = releaseBufferLocked(mCurrentBufferSlot, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); + err = releaseBufferLocked(mCurrentBufferSlot, mCurrentBuffer, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); if (err != NO_ERROR && err != BufferQueue::STALE_BUFFER_SLOT) { ALOGE("error releasing buffer: %s (%d)", strerror(-err), err); return err; @@ -144,7 +144,8 @@ void FramebufferSurface::onFrameCommitted() { sp<Fence> fence = mHwc.getAndResetReleaseFence(mDisplayType); if (fence->isValid() && mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT) { - status_t err = addReleaseFence(mCurrentBufferSlot, fence); + status_t err = addReleaseFence(mCurrentBufferSlot, + mCurrentBuffer, fence); ALOGE_IF(err, "setReleaseFenceFd: failed to add the fence: %s (%d)", strerror(-err), err); } diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp index 2869250..6912dc0 100644 --- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp +++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp @@ -69,7 +69,7 @@ status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter) // reject buffers which have the wrong size int buf = item.mBuf; if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) { - releaseBufferLocked(buf, EGL_NO_SYNC_KHR); + releaseBufferLocked(buf, item.mGraphicBuffer, EGL_NO_SYNC_KHR); return NO_ERROR; } |