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);