From 9f3053de78630815d60cf48a2cf2348cc5867c45 Mon Sep 17 00:00:00 2001 From: Dan Stoza Date: Thu, 6 Mar 2014 15:14:33 -0800 Subject: [PATCH] BufferQueue: Allow detaching/reattaching buffers Adds detachBuffer and attachBuffer calls to both the producer and consumer sides of BufferQueue. Buffers may be detached while dequeued by the producer or acquired by the consumer, and when attached, enter the dequeued and acquired states, respectively. Bug: 13173343 Change-Id: Ic152692b0a94d99e0135b9bfa62747dab2a54220 --- include/gui/BufferQueue.h | 57 +++- include/gui/BufferQueueConsumer.h | 6 + include/gui/BufferQueueProducer.h | 14 + include/gui/BufferSlot.h | 8 +- include/gui/IGraphicBufferConsumer.h | 30 ++ include/gui/IGraphicBufferProducer.h | 57 +++- libs/gui/BufferQueue.cpp | 31 ++ libs/gui/BufferQueueConsumer.cpp | 91 +++++- libs/gui/BufferQueueProducer.cpp | 309 ++++++++++++------ libs/gui/IGraphicBufferConsumer.cpp | 44 +++ libs/gui/IGraphicBufferProducer.cpp | 44 +++ libs/gui/tests/BufferQueue_test.cpp | 225 +++++++++++-- .../DisplayHardware/VirtualDisplaySurface.cpp | 9 + .../DisplayHardware/VirtualDisplaySurface.h | 2 + 14 files changed, 771 insertions(+), 156 deletions(-) diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h index 7eaf84223..f230e559e 100644 --- a/include/gui/BufferQueue.h +++ b/include/gui/BufferQueue.h @@ -28,10 +28,43 @@ #include namespace android { -// ---------------------------------------------------------------------------- -class BufferQueue : public BnGraphicBufferProducer, - public BnGraphicBufferConsumer, +// BQProducer and BQConsumer are thin shim classes to allow methods with the +// same signature in both IGraphicBufferProducer and IGraphicBufferConsumer. +// This will stop being an issue when we deprecate creating BufferQueues +// directly (as opposed to using the *Producer and *Consumer interfaces). +class BQProducer : public BnGraphicBufferProducer { +public: + virtual status_t detachProducerBuffer(int slot) = 0; + virtual status_t attachProducerBuffer(int* slot, + const sp& buffer) = 0; + + virtual status_t detachBuffer(int slot) { + return detachProducerBuffer(slot); + } + + virtual status_t attachBuffer(int* slot, const sp& buffer) { + return attachProducerBuffer(slot, buffer); + } +}; + +class BQConsumer : public BnGraphicBufferConsumer { +public: + virtual status_t detachConsumerBuffer(int slot) = 0; + virtual status_t attachConsumerBuffer(int* slot, + const sp& buffer) = 0; + + virtual status_t detachBuffer(int slot) { + return detachConsumerBuffer(slot); + } + + virtual status_t attachBuffer(int* slot, const sp& buffer) { + return attachConsumerBuffer(slot, buffer); + } +}; + +class BufferQueue : public BQProducer, + public BQConsumer, private IBinder::DeathRecipient { public: // BufferQueue will keep track of at most this value of buffers. @@ -73,6 +106,10 @@ public: wp mConsumerListener; }; + static void createBufferQueue(sp* outProducer, + sp* outConsumer, + const sp& allocator = NULL); + // BufferQueue manages a pool of gralloc memory slots to be used by // producers and consumers. allocator is used to allocate all the // needed gralloc buffers. @@ -157,6 +194,13 @@ public: virtual status_t dequeueBuffer(int *buf, sp* fence, bool async, uint32_t width, uint32_t height, uint32_t format, uint32_t usage); + // See IGraphicBufferProducer::detachBuffer + virtual status_t detachProducerBuffer(int slot); + + // See IGraphicBufferProducer::attachBuffer + virtual status_t attachProducerBuffer(int* slot, + const sp& buffer); + // queueBuffer returns a filled buffer to the BufferQueue. // // Additional data is provided in the QueueBufferInput struct. Notably, @@ -223,6 +267,13 @@ public: // is CLOCK_MONOTONIC. virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen); + // See IGraphicBufferConsumer::detachBuffer + virtual status_t detachConsumerBuffer(int slot); + + // See IGraphicBufferConsumer::attachBuffer + virtual status_t attachConsumerBuffer(int* slot, + const sp& buffer); + // 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 diff --git a/include/gui/BufferQueueConsumer.h b/include/gui/BufferQueueConsumer.h index 20db98cd1..71105dfd3 100644 --- a/include/gui/BufferQueueConsumer.h +++ b/include/gui/BufferQueueConsumer.h @@ -49,6 +49,12 @@ public: virtual status_t acquireBuffer(BufferItem* outBuffer, nsecs_t expectedPresent); + // See IGraphicBufferConsumer::detachBuffer + virtual status_t detachBuffer(int slot); + + // See IGraphicBufferConsumer::attachBuffer + virtual status_t attachBuffer(int* slot, const sp& buffer); + // 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 diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h index 8912097f7..2eeb979cf 100644 --- a/include/gui/BufferQueueProducer.h +++ b/include/gui/BufferQueueProducer.h @@ -96,6 +96,12 @@ public: virtual status_t dequeueBuffer(int *outSlot, sp* outFence, bool async, uint32_t width, uint32_t height, uint32_t format, uint32_t usage); + // See IGraphicBufferProducer::detachBuffer + virtual status_t detachBuffer(int slot); + + // See IGraphicBufferProducer::attachBuffer + virtual status_t attachBuffer(int* outSlot, const sp& buffer); + // queueBuffer returns a filled buffer to the BufferQueue. // // Additional data is provided in the QueueBufferInput struct. Notably, @@ -151,6 +157,14 @@ private: // This is required by the IBinder::DeathRecipient interface virtual void binderDied(const wp& who); + // 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 + // mode (producer and consumer controlled by the application). If it blocks, + // it will release mCore->mMutex while blocked so that other operations on + // the BufferQueue may succeed. + status_t waitForFreeSlotThenRelock(const char* caller, bool async, + int* found, status_t* returnFlags) const; + sp mCore; // This references mCore->mSlots. Lock mCore->mMutex while accessing. diff --git a/include/gui/BufferSlot.h b/include/gui/BufferSlot.h index 2c4b43fa1..6085e116a 100644 --- a/include/gui/BufferSlot.h +++ b/include/gui/BufferSlot.h @@ -38,7 +38,8 @@ struct BufferSlot { mFrameNumber(0), mEglFence(EGL_NO_SYNC_KHR), mAcquireCalled(false), - mNeedsCleanupOnRelease(false) { + mNeedsCleanupOnRelease(false), + mAttachedByConsumer(false) { } // mGraphicBuffer points to the buffer allocated for this slot or is NULL @@ -129,6 +130,11 @@ struct BufferSlot { // 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; }; } // namespace android diff --git a/include/gui/IGraphicBufferConsumer.h b/include/gui/IGraphicBufferConsumer.h index 9a6645cf2..5e9785487 100644 --- a/include/gui/IGraphicBufferConsumer.h +++ b/include/gui/IGraphicBufferConsumer.h @@ -139,6 +139,36 @@ public: // * INVALID_OPERATION - too many buffers have been acquired virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen) = 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 + // freed, and there will be no way to obtain the buffer from this interface. + // The freed slot will remain unallocated until either it is selected to + // hold a freshly allocated buffer in dequeueBuffer or a buffer is attached + // to the slot. The buffer must have already been acquired. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * BAD_VALUE - the given slot number is invalid, either because it is + // out of the range [0, NUM_BUFFER_SLOTS) or because the slot + // it refers to is not currently acquired. + virtual status_t detachBuffer(int slot) = 0; + + // attachBuffer attempts to transfer ownership of a buffer to the buffer + // queue. If this call succeeds, it will be as if this buffer was acquired + // from the returned slot number. As such, this call will fail if attaching + // this buffer would cause too many buffers to be simultaneously acquired. + // + // If the buffer is successfully attached, its frameNumber is initialized + // to 0. This must be passed into the releaseBuffer call or else the buffer + // will be deallocated as stale. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * BAD_VALUE - outSlot or buffer were NULL + // * INVALID_OPERATION - cannot attach the buffer because it would cause too + // many buffers to be acquired. + // * NO_MEMORY - no free slots available + virtual status_t attachBuffer(int *outSlot, + const sp& buffer) = 0; + // 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 diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h index 70025308c..25a86a593 100644 --- a/include/gui/IGraphicBufferProducer.h +++ b/include/gui/IGraphicBufferProducer.h @@ -150,12 +150,14 @@ public: // // Return of a negative means an error has occurred: // * NO_INIT - the buffer queue has been abandoned. - // * BAD_VALUE - one of the below conditions occurred: - // * both in async mode and buffer count was less than the - // max numbers of buffers that can be allocated at once - // * attempting dequeue more than one buffer at a time - // without setting the buffer count with setBufferCount() - // * -EBUSY - attempting to dequeue too many buffers at a time + // * BAD_VALUE - both in async mode and buffer count was less than the + // max numbers of buffers that can be allocated at once. + // * INVALID_OPERATION - cannot attach the buffer because it would cause + // too many buffers to be dequeued, either because + // the producer already has a single buffer dequeued + // and did not set a buffer count, or because a + // buffer count was set and this call would cause + // it to be exceeded. // * WOULD_BLOCK - no buffer is currently available, and blocking is disabled // since both the producer/consumer are controlled by app // * NO_MEMORY - out of memory, cannot allocate the graphics buffer. @@ -165,6 +167,49 @@ public: virtual status_t dequeueBuffer(int* slot, sp* fence, bool async, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) = 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 + // freed, and there will be no way to obtain the buffer from this interface. + // The freed slot will remain unallocated until either it is selected to + // hold a freshly allocated buffer in dequeueBuffer or a buffer is attached + // to the slot. The buffer must have already been dequeued, and the caller + // must already possesses the sp (i.e., must have called + // requestBuffer). + // + // Return of a value other than NO_ERROR means an error has occurred: + // * NO_INIT - the buffer queue has been abandoned. + // * BAD_VALUE - the given slot number is invalid, either because it is + // out of the range [0, NUM_BUFFER_SLOTS), or because the slot + // it refers to is not currently dequeued and requested. + virtual status_t detachBuffer(int slot) = 0; + + // attachBuffer attempts to transfer ownership of a buffer to the buffer + // queue. If this call succeeds, it will be as if this buffer was dequeued + // from the returned slot number. As such, this call will fail if attaching + // this buffer would cause too many buffers to be simultaneously dequeued. + // + // If attachBuffer returns the RELEASE_ALL_BUFFERS flag, the caller is + // expected to release all of the mirrored slot->buffer mappings. + // + // A non-negative value with flags set (see above) will be returned upon + // success. + // + // Return of a negative value means an error has occurred: + // * NO_INIT - the buffer queue has been abandoned. + // * BAD_VALUE - outSlot or buffer were NULL or invalid combination of + // async mode and buffer count override. + // * INVALID_OPERATION - cannot attach the buffer because it would cause + // too many buffers to be dequeued, either because + // the producer already has a single buffer dequeued + // and did not set a buffer count, or because a + // buffer count was set and this call would cause + // it to be exceeded. + // * WOULD_BLOCK - no buffer slot is currently available, and blocking is + // disabled since both the producer/consumer are + // controlled by the app. + virtual status_t attachBuffer(int* outSlot, + const sp& buffer) = 0; + // queueBuffer indicates that the client has finished filling in the // contents of the buffer associated with slot and transfers ownership of // that slot back to the server. diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index af857fd3e..26e215b1d 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -43,6 +43,19 @@ void BufferQueue::ProxyConsumerListener::onBuffersReleased() { } } +void BufferQueue::createBufferQueue(sp* outProducer, + sp* outConsumer, + const sp& allocator) { + LOG_ALWAYS_FATAL_IF(outProducer == NULL, + "BufferQueue: outProducer must not be NULL"); + LOG_ALWAYS_FATAL_IF(outConsumer == NULL, + "BufferQueue: outConsumer must not be NULL"); + + sp core(new BufferQueueCore(allocator)); + *outProducer = new BufferQueueProducer(core); + *outConsumer = new BufferQueueConsumer(core); +} + BufferQueue::BufferQueue(const sp& allocator) : mProducer(), mConsumer() @@ -75,6 +88,15 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp* outFence, bool async return mProducer->dequeueBuffer(outBuf, outFence, async, w, h, format, usage); } +status_t BufferQueue::detachProducerBuffer(int slot) { + return mProducer->detachBuffer(slot); +} + +status_t BufferQueue::attachProducerBuffer(int* slot, + const sp& buffer) { + return mProducer->attachBuffer(slot, buffer); +} + status_t BufferQueue::queueBuffer(int buf, const QueueBufferInput& input, QueueBufferOutput* output) { return mProducer->queueBuffer(buf, input, output); @@ -97,6 +119,15 @@ status_t BufferQueue::acquireBuffer(BufferItem* buffer, nsecs_t presentWhen) { return mConsumer->acquireBuffer(buffer, presentWhen); } +status_t BufferQueue::detachConsumerBuffer(int slot) { + return mConsumer->detachBuffer(slot); +} + +status_t BufferQueue::attachConsumerBuffer(int* slot, + const sp& buffer) { + return mConsumer->attachBuffer(slot, buffer); +} + status_t BufferQueue::releaseBuffer( int buf, uint64_t frameNumber, EGLDisplay display, EGLSyncKHR eglFence, const sp& fence) { diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index dc7aa157f..66fdab32e 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -168,13 +168,96 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, return NO_ERROR; } +status_t BufferQueueConsumer::detachBuffer(int slot) { + ATRACE_CALL(); + ATRACE_BUFFER_INDEX(slot); + BQ_LOGV("detachBuffer(C): slot %d", slot); + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + BQ_LOGE("detachBuffer(C): BufferQueue has been abandoned"); + return NO_INIT; + } + + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + BQ_LOGE("detachBuffer(C): slot index %d out of range [0, %d)", + slot, BufferQueueDefs::NUM_BUFFER_SLOTS); + return BAD_VALUE; + } else if (mSlots[slot].mBufferState != BufferSlot::ACQUIRED) { + BQ_LOGE("detachBuffer(C): slot %d is not owned by the consumer " + "(state = %d)", slot, mSlots[slot].mBufferState); + return BAD_VALUE; + } + + mCore->freeBufferLocked(slot); + mCore->mDequeueCondition.broadcast(); + + return NO_ERROR; +} + +status_t BufferQueueConsumer::attachBuffer(int* outSlot, + const sp& buffer) { + ATRACE_CALL(); + + if (outSlot == NULL) { + BQ_LOGE("attachBuffer(P): outSlot must not be NULL"); + return BAD_VALUE; + } else if (buffer == NULL) { + BQ_LOGE("attachBuffer(P): cannot attach NULL buffer"); + return BAD_VALUE; + } + + Mutex::Autolock lock(mCore->mMutex); + + // Make sure we don't have too many acquired buffers and find a free slot + // to put the buffer into (the oldest if there are multiple). + int numAcquiredBuffers = 0; + int found = BufferQueueCore::INVALID_BUFFER_SLOT; + for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) { + ++numAcquiredBuffers; + } else if (mSlots[s].mBufferState == BufferSlot::FREE) { + if (found == BufferQueueCore::INVALID_BUFFER_SLOT || + mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) { + found = s; + } + } + } + + if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) { + BQ_LOGE("attachBuffer(P): max acquired buffer count reached: %d " + "(max %d)", numAcquiredBuffers, + mCore->mMaxAcquiredBufferCount); + return INVALID_OPERATION; + } + if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { + BQ_LOGE("attachBuffer(P): could not find free buffer slot"); + return NO_MEMORY; + } + + *outSlot = found; + ATRACE_BUFFER_INDEX(*outSlot); + BQ_LOGV("attachBuffer(C): returning slot %d", *outSlot); + + mSlots[*outSlot].mGraphicBuffer = buffer; + mSlots[*outSlot].mBufferState = BufferSlot::ACQUIRED; + mSlots[*outSlot].mAttachedByConsumer = true; + mSlots[*outSlot].mAcquireCalled = true; + mSlots[*outSlot].mNeedsCleanupOnRelease = false; + mSlots[*outSlot].mFence = Fence::NO_FENCE; + mSlots[*outSlot].mFrameNumber = 0; + + return NO_ERROR; +} + status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, const sp& releaseFence, EGLDisplay eglDisplay, EGLSyncKHR eglFence) { ATRACE_CALL(); ATRACE_BUFFER_INDEX(slot); - if (slot == BufferQueueCore::INVALID_BUFFER_SLOT || releaseFence == NULL) { + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS || + releaseFence == NULL) { return BAD_VALUE; } @@ -192,7 +275,7 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, if (current->mSlot == slot) { BQ_LOGE("releaseBuffer: buffer slot %d pending release is " "currently queued", slot); - return -EINVAL; + return BAD_VALUE; } ++current; } @@ -210,7 +293,7 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, } else { BQ_LOGV("releaseBuffer: attempted to release buffer slot %d " "but its state was %d", slot, mSlots[slot].mBufferState); - return -EINVAL; + return BAD_VALUE; } mCore->mDequeueCondition.broadcast(); @@ -252,7 +335,7 @@ status_t BufferQueueConsumer::disconnect() { if (mCore->mConsumerListener == NULL) { BQ_LOGE("disconnect(C): no consumer is connected"); - return -EINVAL; + return BAD_VALUE; } mCore->mIsAbandoned = true; diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 0e9de88a1..58e19f075 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -86,7 +86,7 @@ status_t BufferQueueProducer::setBufferCount(int bufferCount) { for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { if (mSlots[s].mBufferState == BufferSlot::DEQUEUED) { BQ_LOGE("setBufferCount: buffer owned by producer"); - return -EINVAL; + return BAD_VALUE; } } @@ -121,6 +121,110 @@ status_t BufferQueueProducer::setBufferCount(int bufferCount) { return NO_ERROR; } +status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller, + bool async, int* found, status_t* returnFlags) const { + bool tryAgain = true; + while (tryAgain) { + if (mCore->mIsAbandoned) { + BQ_LOGE("%s: BufferQueue has been abandoned", caller); + return NO_INIT; + } + + const int maxBufferCount = mCore->getMaxBufferCountLocked(async); + if (async && mCore->mOverrideMaxBufferCount) { + // FIXME: Some drivers are manually setting the buffer count + // (which they shouldn't), so we do this extra test here to + // handle that case. This is TEMPORARY until we get this fixed. + if (mCore->mOverrideMaxBufferCount < maxBufferCount) { + BQ_LOGE("%s: async mode is invalid with buffer count override", + caller); + return BAD_VALUE; + } + } + + // 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 == BufferSlot::FREE); + if (mSlots[s].mGraphicBuffer != NULL) { + mCore->freeBufferLocked(s); + *returnFlags |= RELEASE_ALL_BUFFERS; + } + } + + // Look for a free buffer to give to the client + *found = BufferQueueCore::INVALID_BUFFER_SLOT; + int dequeuedCount = 0; + int acquiredCount = 0; + for (int s = 0; s < maxBufferCount; ++s) { + switch (mSlots[s].mBufferState) { + case BufferSlot::DEQUEUED: + ++dequeuedCount; + break; + case BufferSlot::ACQUIRED: + ++acquiredCount; + break; + case BufferSlot::FREE: + // We return the oldest of the free buffers to avoid + // stalling the producer if possible, since the consumer + // may still have pending reads of in-flight buffers + if (*found == BufferQueueCore::INVALID_BUFFER_SLOT || + mSlots[s].mFrameNumber < mSlots[*found].mFrameNumber) { + *found = s; + } + break; + default: + break; + } + } + + // Producers are not allowed to dequeue more than one buffer if they + // did not set a buffer count + if (!mCore->mOverrideMaxBufferCount && dequeuedCount) { + BQ_LOGE("%s: can't dequeue multiple buffers without setting the " + "buffer count", caller); + return INVALID_OPERATION; + } + + // See whether a buffer has been queued since the last + // setBufferCount so we know whether to perform the min undequeued + // buffers check below + if (mCore->mBufferHasBeenQueued) { + // Make sure the producer is not trying to dequeue more buffers + // than allowed + const int newUndequeuedCount = + maxBufferCount - (dequeuedCount + 1); + const int minUndequeuedCount = + mCore->getMinUndequeuedBufferCountLocked(async); + if (newUndequeuedCount < minUndequeuedCount) { + BQ_LOGE("%s: min undequeued buffer count (%d) exceeded " + "(dequeued=%d undequeued=%d)", + caller, minUndequeuedCount, + dequeuedCount, newUndequeuedCount); + return INVALID_OPERATION; + } + } + + // If no buffer is found, wait for a buffer to be released or for + // the max buffer count to change + tryAgain = (*found == BufferQueueCore::INVALID_BUFFER_SLOT); + if (tryAgain) { + // Return an error if we're in non-blocking mode (producer and + // consumer are controlled by the application). + // However, the consumer is allowed to briefly acquire an extra + // buffer (which could cause us to have to wait here), which is + // okay, since it is only used to implement an atomic acquire + + // release (e.g., in GLConsumer::updateTexImage()) + if (mCore->mDequeueBufferCannotBlock && + (acquiredCount <= mCore->mMaxAcquiredBufferCount)) { + return WOULD_BLOCK; + } + mCore->mDequeueCondition.wait(mCore->mMutex); + } + } // while (tryAgain) + + return NO_ERROR; +} + status_t BufferQueueProducer::dequeueBuffer(int *outSlot, sp *outFence, bool async, uint32_t width, uint32_t height, uint32_t format, uint32_t usage) { @@ -141,6 +245,7 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, status_t returnFlags = NO_ERROR; EGLDisplay eglDisplay = EGL_NO_DISPLAY; EGLSyncKHR eglFence = EGL_NO_SYNC_KHR; + bool attachedByConsumer = false; { // Autolock scope Mutex::Autolock lock(mCore->mMutex); @@ -152,105 +257,12 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, // Enable the usage bits the consumer requested usage |= mCore->mConsumerUsageBits; - int found = -1; - bool tryAgain = true; - while (tryAgain) { - if (mCore->mIsAbandoned) { - BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned"); - return NO_INIT; - } - - const int maxBufferCount = mCore->getMaxBufferCountLocked(async); - if (async && mCore->mOverrideMaxBufferCount) { - // FIXME: Some drivers are manually setting the buffer count - // (which they shouldn't), so we do this extra test here to - // handle that case. This is TEMPORARY until we get this fixed. - if (mCore->mOverrideMaxBufferCount < maxBufferCount) { - BQ_LOGE("dequeueBuffer: async mode is invalid with " - "buffer count override"); - return BAD_VALUE; - } - } - - // 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 == BufferSlot::FREE); - if (mSlots[s].mGraphicBuffer != NULL) { - mCore->freeBufferLocked(s); - returnFlags |= RELEASE_ALL_BUFFERS; - } - } - - // Look for a free buffer to give to the client - found = BufferQueueCore::INVALID_BUFFER_SLOT; - int dequeuedCount = 0; - int acquiredCount = 0; - for (int s = 0; s < maxBufferCount; ++s) { - switch (mSlots[s].mBufferState) { - case BufferSlot::DEQUEUED: - ++dequeuedCount; - break; - case BufferSlot::ACQUIRED: - ++acquiredCount; - break; - case BufferSlot::FREE: - // We return the oldest of the free buffers to avoid - // stalling the producer if possible, since the consumer - // may still have pending reads of in-flight buffers - if (found == BufferQueueCore::INVALID_BUFFER_SLOT || - mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) { - found = s; - } - break; - default: - break; - } - } - - // Producers are not allowed to dequeue more than one buffer if they - // did not set a buffer count - if (!mCore->mOverrideMaxBufferCount && dequeuedCount) { - BQ_LOGE("dequeueBuffer: can't dequeue multiple buffers " - "without setting the buffer count"); - return -EINVAL; - } - - // See whether a buffer has been queued since the last - // setBufferCount so we know whether to perform the min undequeued - // buffers check below - if (mCore->mBufferHasBeenQueued) { - // Make sure the producer is not trying to dequeue more buffers - // than allowed - const int newUndequeuedCount = - maxBufferCount - (dequeuedCount + 1); - const int minUndequeuedCount = - mCore->getMinUndequeuedBufferCountLocked(async); - if (newUndequeuedCount < minUndequeuedCount) { - BQ_LOGE("dequeueBuffer: min undequeued buffer count (%d) " - "exceeded (dequeued=%d undequeued=%d)", - minUndequeuedCount, dequeuedCount, - newUndequeuedCount); - return -EBUSY; - } - } - - // If no buffer is found, wait for a buffer to be released or for - // the max buffer count to change - tryAgain = (found == BufferQueueCore::INVALID_BUFFER_SLOT); - if (tryAgain) { - // Return an error if we're in non-blocking mode (producer and - // consumer are controlled by the application). - // However, the consumer is allowed to briefly acquire an extra - // buffer (which could cause us to have to wait here), which is - // okay, since it is only used to implement an atomic acquire + - // release (e.g., in GLConsumer::updateTexImage()) - if (mCore->mDequeueBufferCannotBlock && - (acquiredCount <= mCore->mMaxAcquiredBufferCount)) { - return WOULD_BLOCK; - } - mCore->mDequeueCondition.wait(mCore->mMutex); - } - } // while (tryAgain) + int found; + status_t status = waitForFreeSlotThenRelock("dequeueBuffer", async, + &found, &returnFlags); + if (status != NO_ERROR) { + return status; + } // This should not happen if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { @@ -261,6 +273,8 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, *outSlot = found; ATRACE_BUFFER_INDEX(found); + attachedByConsumer = mSlots[found].mAttachedByConsumer; + const bool useDefaultSize = !width && !height; if (useDefaultSize) { width = mCore->mDefaultWidth; @@ -316,11 +330,15 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, return NO_INIT; } - mSlots[*outSlot].mFrameNumber = ~0; + mSlots[*outSlot].mFrameNumber = UINT32_MAX; mSlots[*outSlot].mGraphicBuffer = graphicBuffer; } // Autolock scope } + if (attachedByConsumer) { + returnFlags |= BUFFER_NEEDS_REALLOCATION; + } + if (eglFence != EGL_NO_SYNC_KHR) { EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, 0, 1000000000); @@ -343,6 +361,81 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, return returnFlags; } +status_t BufferQueueProducer::detachBuffer(int slot) { + ATRACE_CALL(); + ATRACE_BUFFER_INDEX(slot); + BQ_LOGV("detachBuffer(P): slot %d", slot); + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + BQ_LOGE("detachBuffer(P): BufferQueue has been abandoned"); + return NO_INIT; + } + + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + BQ_LOGE("detachBuffer(P): slot index %d out of range [0, %d)", + slot, BufferQueueDefs::NUM_BUFFER_SLOTS); + return BAD_VALUE; + } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { + BQ_LOGE("detachBuffer(P): slot %d is not owned by the producer " + "(state = %d)", slot, mSlots[slot].mBufferState); + return BAD_VALUE; + } else if (!mSlots[slot].mRequestBufferCalled) { + BQ_LOGE("detachBuffer(P): buffer in slot %d has not been requested", + slot); + return BAD_VALUE; + } + + mCore->freeBufferLocked(slot); + mCore->mDequeueCondition.broadcast(); + + return NO_ERROR; +} + +status_t BufferQueueProducer::attachBuffer(int* outSlot, + const sp& buffer) { + ATRACE_CALL(); + + if (outSlot == NULL) { + BQ_LOGE("attachBuffer(P): outSlot must not be NULL"); + return BAD_VALUE; + } else if (buffer == NULL) { + BQ_LOGE("attachBuffer(P): cannot attach NULL buffer"); + return BAD_VALUE; + } + + Mutex::Autolock lock(mCore->mMutex); + + status_t returnFlags = NO_ERROR; + int found; + // TODO: Should we provide an async flag to attachBuffer? It seems + // unlikely that buffers which we are attaching to a BufferQueue will + // be asynchronous (droppable), but it may not be impossible. + status_t status = waitForFreeSlotThenRelock("attachBuffer(P)", false, + &found, &returnFlags); + if (status != NO_ERROR) { + return status; + } + + // This should not happen + if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { + BQ_LOGE("attachBuffer(P): no available buffer slots"); + return -EBUSY; + } + + *outSlot = found; + ATRACE_BUFFER_INDEX(*outSlot); + BQ_LOGV("attachBuffer(P): returning slot %d flags=%#x", + *outSlot, returnFlags); + + mSlots[*outSlot].mGraphicBuffer = buffer; + mSlots[*outSlot].mBufferState = BufferSlot::DEQUEUED; + mSlots[*outSlot].mEglFence = EGL_NO_SYNC_KHR; + mSlots[*outSlot].mFence = Fence::NO_FENCE; + + return returnFlags; +} + status_t BufferQueueProducer::queueBuffer(int slot, const QueueBufferInput &input, QueueBufferOutput *output) { ATRACE_CALL(); @@ -371,7 +464,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, break; default: BQ_LOGE("queueBuffer: unknown scaling mode %d", scalingMode); - return -EINVAL; + return BAD_VALUE; } sp listener; @@ -398,15 +491,15 @@ status_t BufferQueueProducer::queueBuffer(int slot, if (slot < 0 || slot >= maxBufferCount) { BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)", slot, maxBufferCount); - return -EINVAL; + return BAD_VALUE; } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { BQ_LOGE("queueBuffer: slot %d is not owned by the producer " "(state = %d)", slot, mSlots[slot].mBufferState); - return -EINVAL; + return BAD_VALUE; } else if (!mSlots[slot].mRequestBufferCalled) { BQ_LOGE("queueBuffer: slot %d was queued without requesting " "a buffer", slot); - return -EINVAL; + return BAD_VALUE; } BQ_LOGV("queueBuffer: slot=%d/%llu time=%llu crop=[%d,%d,%d,%d] " @@ -422,7 +515,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, if (croppedRect != crop) { BQ_LOGE("queueBuffer: crop rect is not contained within the " "buffer in slot %d", slot); - return -EINVAL; + return BAD_VALUE; } mSlots[slot].mFence = fence; @@ -679,12 +772,12 @@ status_t BufferQueueProducer::disconnect(int api) { } else { BQ_LOGE("disconnect(P): connected to another API " "(cur=%d req=%d)", mCore->mConnectedApi, api); - status = -EINVAL; + status = BAD_VALUE; } break; default: BQ_LOGE("disconnect(P): unknown API %d", api); - status = -EINVAL; + status = BAD_VALUE; break; } } // Autolock scope diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp index 876c89568..8d6438008 100644 --- a/libs/gui/IGraphicBufferConsumer.cpp +++ b/libs/gui/IGraphicBufferConsumer.cpp @@ -183,6 +183,8 @@ status_t IGraphicBufferConsumer::BufferItem::unflatten( enum { ACQUIRE_BUFFER = IBinder::FIRST_CALL_TRANSACTION, + DETACH_BUFFER, + ATTACH_BUFFER, RELEASE_BUFFER, CONSUMER_CONNECT, CONSUMER_DISCONNECT, @@ -222,6 +224,31 @@ public: return reply.readInt32(); } + virtual status_t detachBuffer(int slot) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(slot); + status_t result = remote()->transact(DETACH_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.readInt32(); + return result; + } + + virtual status_t attachBuffer(int* slot, const sp& buffer) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); + data.write(*buffer.get()); + status_t result = remote()->transact(ATTACH_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + *slot = reply.readInt32(); + result = reply.readInt32(); + return result; + } + virtual status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display __attribute__((unused)), EGLSyncKHR fence __attribute__((unused)), const sp& releaseFence) { @@ -382,6 +409,23 @@ status_t BnGraphicBufferConsumer::onTransact( reply->writeInt32(result); return NO_ERROR; } break; + case DETACH_BUFFER: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + int slot = data.readInt32(); + int result = detachBuffer(slot); + reply->writeInt32(result); + return NO_ERROR; + } break; + case ATTACH_BUFFER: { + CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); + sp buffer = new GraphicBuffer(); + data.read(*buffer.get()); + int slot; + int result = attachBuffer(&slot, buffer); + reply->writeInt32(slot); + reply->writeInt32(result); + return NO_ERROR; + } break; case RELEASE_BUFFER: { CHECK_INTERFACE(IGraphicBufferConsumer, data, reply); int buf = data.readInt32(); diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index 0f461e59c..16d8c0583 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -34,6 +34,8 @@ enum { REQUEST_BUFFER = IBinder::FIRST_CALL_TRANSACTION, SET_BUFFER_COUNT, DEQUEUE_BUFFER, + DETACH_BUFFER, + ATTACH_BUFFER, QUEUE_BUFFER, CANCEL_BUFFER, QUERY, @@ -106,6 +108,31 @@ public: return result; } + virtual status_t detachBuffer(int slot) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32(slot); + status_t result = remote()->transact(DETACH_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.readInt32(); + return result; + } + + virtual status_t attachBuffer(int* slot, const sp& buffer) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.write(*buffer.get()); + status_t result = remote()->transact(ATTACH_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + *slot = reply.readInt32(); + result = reply.readInt32(); + return result; + } + virtual status_t queueBuffer(int buf, const QueueBufferInput& input, QueueBufferOutput* output) { Parcel data, reply; @@ -216,6 +243,23 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(result); return NO_ERROR; } break; + case DETACH_BUFFER: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + int slot = data.readInt32(); + int result = detachBuffer(slot); + reply->writeInt32(result); + return NO_ERROR; + } break; + case ATTACH_BUFFER: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + sp buffer = new GraphicBuffer(); + data.read(*buffer.get()); + int slot; + int result = attachBuffer(&slot, buffer); + reply->writeInt32(slot); + reply->writeInt32(result); + return NO_ERROR; + } break; case QUEUE_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int buf = data.readInt32(); diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index aafa6ea93..b859fcbde 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -24,27 +24,31 @@ #include +#include #include namespace android { class BufferQueueTest : public ::testing::Test { + +public: + static const String16 PRODUCER_NAME; + static const String16 CONSUMER_NAME; + protected: - - BufferQueueTest() {} - - virtual void SetUp() { + BufferQueueTest() { const ::testing::TestInfo* const testInfo = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name()); - mBQ = new BufferQueue(); + BufferQueue::createBufferQueue(&mProducer, &mConsumer); + sp serviceManager = defaultServiceManager(); + serviceManager->addService(PRODUCER_NAME, mProducer.get()); + serviceManager->addService(CONSUMER_NAME, mConsumer.get()); } - virtual void TearDown() { - mBQ.clear(); - + ~BufferQueueTest() { const ::testing::TestInfo* const testInfo = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGV("End test: %s.%s", testInfo->test_case_name(), @@ -52,14 +56,19 @@ protected: } void GetMinUndequeuedBufferCount(int* bufferCount) { - ASSERT_NE((void*)NULL, bufferCount); - ASSERT_EQ(OK, mBQ->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, bufferCount)); - ASSERT_LE(0, *bufferCount); // non-negative + ASSERT_TRUE(bufferCount != NULL); + ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + bufferCount)); + ASSERT_GE(*bufferCount, 0); } - sp mBQ; + sp mProducer; + sp mConsumer; }; +const String16 BufferQueueTest::PRODUCER_NAME = String16("BQTestProducer"); +const String16 BufferQueueTest::CONSUMER_NAME = String16("BQTestConsumer"); + struct DummyConsumer : public BnConsumerListener { virtual void onFrameAvailable() {} virtual void onBuffersReleased() {} @@ -67,10 +76,10 @@ struct DummyConsumer : public BnConsumerListener { TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) { sp dc(new DummyConsumer); - mBQ->consumerConnect(dc, false); + mConsumer->consumerConnect(dc, false); IGraphicBufferProducer::QueueBufferOutput qbo; - mBQ->connect(NULL, NATIVE_WINDOW_API_CPU, false, &qbo); - mBQ->setBufferCount(4); + mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &qbo); + mProducer->setBufferCount(4); int slot; sp fence; @@ -81,50 +90,198 @@ TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) { for (int i = 0; i < 2; i++) { ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mBQ->dequeueBuffer(&slot, &fence, false, 1, 1, 0, + mProducer->dequeueBuffer(&slot, &fence, false, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN)); - ASSERT_EQ(OK, mBQ->requestBuffer(slot, &buf)); - ASSERT_EQ(OK, mBQ->queueBuffer(slot, qbi, &qbo)); - ASSERT_EQ(OK, mBQ->acquireBuffer(&item, 0)); + ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf)); + ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); + ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); } ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, - mBQ->dequeueBuffer(&slot, &fence, false, 1, 1, 0, + mProducer->dequeueBuffer(&slot, &fence, false, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN)); - ASSERT_EQ(OK, mBQ->requestBuffer(slot, &buf)); - ASSERT_EQ(OK, mBQ->queueBuffer(slot, qbi, &qbo)); + ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buf)); + ASSERT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); // Acquire the third buffer, which should fail. - ASSERT_EQ(INVALID_OPERATION, mBQ->acquireBuffer(&item, 0)); + ASSERT_EQ(INVALID_OPERATION, mConsumer->acquireBuffer(&item, 0)); } TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) { sp dc(new DummyConsumer); - mBQ->consumerConnect(dc, false); + mConsumer->consumerConnect(dc, false); int minBufferCount; ASSERT_NO_FATAL_FAILURE(GetMinUndequeuedBufferCount(&minBufferCount)); - EXPECT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(minBufferCount - 1)); + EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount( + minBufferCount - 1)); - EXPECT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(0)); - EXPECT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(-3)); - EXPECT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount( + EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount(0)); + EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount(-3)); + EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount( BufferQueue::MAX_MAX_ACQUIRED_BUFFERS+1)); - EXPECT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(100)); + EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount(100)); } TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) { sp dc(new DummyConsumer); - mBQ->consumerConnect(dc, false); + mConsumer->consumerConnect(dc, false); int minBufferCount; ASSERT_NO_FATAL_FAILURE(GetMinUndequeuedBufferCount(&minBufferCount)); - EXPECT_EQ(OK, mBQ->setMaxAcquiredBufferCount(1)); - EXPECT_EQ(OK, mBQ->setMaxAcquiredBufferCount(2)); - EXPECT_EQ(OK, mBQ->setMaxAcquiredBufferCount(minBufferCount)); - EXPECT_EQ(OK, mBQ->setMaxAcquiredBufferCount( + EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1)); + EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(2)); + EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(minBufferCount)); + EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount( BufferQueue::MAX_MAX_ACQUIRED_BUFFERS)); } +TEST_F(BufferQueueTest, DetachAndReattachOnProducerSide) { + sp dc(new DummyConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); + IGraphicBufferProducer::QueueBufferOutput output; + ASSERT_EQ(OK, + mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output)); + + ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(-1)); // Index too low + ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer( + BufferQueueDefs::NUM_BUFFER_SLOTS)); // Index too high + ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(0)); // Not dequeued + + int slot; + sp fence; + sp buffer; + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, + mProducer->dequeueBuffer(&slot, &fence, false, 0, 0, 0, + GRALLOC_USAGE_SW_WRITE_OFTEN)); + ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(slot)); // Not requested + ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); + ASSERT_EQ(OK, mProducer->detachBuffer(slot)); + ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(slot)); // Not dequeued + + sp safeToClobberBuffer; + // Can no longer request buffer from this slot + ASSERT_EQ(BAD_VALUE, mProducer->requestBuffer(slot, &safeToClobberBuffer)); + + uint32_t* dataIn; + ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, + reinterpret_cast(&dataIn))); + *dataIn = 0x12345678; + ASSERT_EQ(OK, buffer->unlock()); + + int newSlot; + ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(NULL, safeToClobberBuffer)); + ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(&newSlot, NULL)); + + ASSERT_EQ(OK, mProducer->attachBuffer(&newSlot, buffer)); + IGraphicBufferProducer::QueueBufferInput input(0, false, Rect(0, 0, 1, 1), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, false, Fence::NO_FENCE); + ASSERT_EQ(OK, mProducer->queueBuffer(newSlot, input, &output)); + + IGraphicBufferConsumer::BufferItem item; + ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, static_cast(0))); + + uint32_t* dataOut; + ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, + reinterpret_cast(&dataOut))); + ASSERT_EQ(*dataOut, 0x12345678); +} + +TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) { + sp dc(new DummyConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); + IGraphicBufferProducer::QueueBufferOutput output; + ASSERT_EQ(OK, + mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output)); + + int slot; + sp fence; + sp buffer; + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, + mProducer->dequeueBuffer(&slot, &fence, false, 0, 0, 0, + GRALLOC_USAGE_SW_WRITE_OFTEN)); + ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); + IGraphicBufferProducer::QueueBufferInput input(0, false, Rect(0, 0, 1, 1), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, false, Fence::NO_FENCE); + ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); + + ASSERT_EQ(BAD_VALUE, mConsumer->detachBuffer(-1)); // Index too low + ASSERT_EQ(BAD_VALUE, mConsumer->detachBuffer( + BufferQueueDefs::NUM_BUFFER_SLOTS)); // Index too high + ASSERT_EQ(BAD_VALUE, mConsumer->detachBuffer(0)); // Not acquired + + IGraphicBufferConsumer::BufferItem item; + ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, static_cast(0))); + + ASSERT_EQ(OK, mConsumer->detachBuffer(item.mBuf)); + ASSERT_EQ(BAD_VALUE, mConsumer->detachBuffer(item.mBuf)); // Not acquired + + uint32_t* dataIn; + ASSERT_EQ(OK, item.mGraphicBuffer->lock( + GraphicBuffer::USAGE_SW_WRITE_OFTEN, + reinterpret_cast(&dataIn))); + *dataIn = 0x12345678; + ASSERT_EQ(OK, item.mGraphicBuffer->unlock()); + + int newSlot; + sp safeToClobberBuffer; + ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(NULL, safeToClobberBuffer)); + ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&newSlot, NULL)); + ASSERT_EQ(OK, mConsumer->attachBuffer(&newSlot, item.mGraphicBuffer)); + + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mBuf, 0, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, + mProducer->dequeueBuffer(&slot, &fence, false, 0, 0, 0, + GRALLOC_USAGE_SW_WRITE_OFTEN)); + ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); + + uint32_t* dataOut; + ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, + reinterpret_cast(&dataOut))); + ASSERT_EQ(*dataOut, 0x12345678); +} + +TEST_F(BufferQueueTest, MoveFromConsumerToProducer) { + sp dc(new DummyConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); + IGraphicBufferProducer::QueueBufferOutput output; + ASSERT_EQ(OK, + mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output)); + + int slot; + sp fence; + sp buffer; + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, + mProducer->dequeueBuffer(&slot, &fence, false, 0, 0, 0, + GRALLOC_USAGE_SW_WRITE_OFTEN)); + ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); + + uint32_t* dataIn; + ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, + reinterpret_cast(&dataIn))); + *dataIn = 0x12345678; + ASSERT_EQ(OK, buffer->unlock()); + + IGraphicBufferProducer::QueueBufferInput input(0, false, Rect(0, 0, 1, 1), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, false, Fence::NO_FENCE); + ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); + + IGraphicBufferConsumer::BufferItem item; + ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, static_cast(0))); + ASSERT_EQ(OK, mConsumer->detachBuffer(item.mBuf)); + + int newSlot; + ASSERT_EQ(OK, mProducer->attachBuffer(&newSlot, item.mGraphicBuffer)); + ASSERT_EQ(OK, mProducer->queueBuffer(newSlot, input, &output)); + ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, static_cast(0))); + + uint32_t* dataOut; + ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, + reinterpret_cast(&dataOut))); + ASSERT_EQ(*dataOut, 0x12345678); +} + } // namespace android diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index a1820abc6..de0d16d20 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -374,6 +374,15 @@ status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp* fence, bool return result; } +status_t VirtualDisplaySurface::detachBuffer(int slot) { + return mSource[SOURCE_SINK]->detachBuffer(slot); +} + +status_t VirtualDisplaySurface::attachBuffer(int* outSlot, + const sp& buffer) { + return mSource[SOURCE_SINK]->attachBuffer(outSlot, buffer); +} + status_t VirtualDisplaySurface::queueBuffer(int pslot, const QueueBufferInput& input, QueueBufferOutput* output) { VDS_LOGW_IF(mDbgState != DBG_STATE_GLES, diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h index 68999049d..cd9a5b0ca 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -98,6 +98,8 @@ private: virtual status_t setBufferCount(int bufferCount); virtual status_t dequeueBuffer(int* pslot, sp* fence, bool async, uint32_t w, uint32_t h, uint32_t format, uint32_t usage); + virtual status_t detachBuffer(int slot); + virtual status_t attachBuffer(int* slot, const sp& buffer); virtual status_t queueBuffer(int pslot, const QueueBufferInput& input, QueueBufferOutput* output); virtual void cancelBuffer(int pslot, const sp& fence);