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
This commit is contained in:
Dan Stoza 2014-03-06 15:14:33 -08:00
parent 544b09523b
commit 9f3053de78
14 changed files with 771 additions and 156 deletions

View File

@ -28,10 +28,43 @@
#include <binder/IBinder.h>
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<GraphicBuffer>& buffer) = 0;
virtual status_t detachBuffer(int slot) {
return detachProducerBuffer(slot);
}
virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& 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<GraphicBuffer>& buffer) = 0;
virtual status_t detachBuffer(int slot) {
return detachConsumerBuffer(slot);
}
virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& 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<ConsumerListener> mConsumerListener;
};
static void createBufferQueue(sp<BnGraphicBufferProducer>* outProducer,
sp<BnGraphicBufferConsumer>* outConsumer,
const sp<IGraphicBufferAlloc>& 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>* 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<GraphicBuffer>& 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<GraphicBuffer>& 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

View File

@ -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<GraphicBuffer>& 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

View File

@ -96,6 +96,12 @@ public:
virtual status_t dequeueBuffer(int *outSlot, sp<Fence>* 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<GraphicBuffer>& 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<IBinder>& 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<BufferQueueCore> mCore;
// This references mCore->mSlots. Lock mCore->mMutex while accessing.

View File

@ -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

View File

@ -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<GraphicBuffer>& 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

View File

@ -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>* 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<GraphicBuffer> (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<GraphicBuffer>& 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.

View File

@ -43,6 +43,19 @@ void BufferQueue::ProxyConsumerListener::onBuffersReleased() {
}
}
void BufferQueue::createBufferQueue(sp<BnGraphicBufferProducer>* outProducer,
sp<BnGraphicBufferConsumer>* outConsumer,
const sp<IGraphicBufferAlloc>& 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<BufferQueueCore> core(new BufferQueueCore(allocator));
*outProducer = new BufferQueueProducer(core);
*outConsumer = new BufferQueueConsumer(core);
}
BufferQueue::BufferQueue(const sp<IGraphicBufferAlloc>& allocator) :
mProducer(),
mConsumer()
@ -75,6 +88,15 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* 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<GraphicBuffer>& 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<GraphicBuffer>& buffer) {
return mConsumer->attachBuffer(slot, buffer);
}
status_t BufferQueue::releaseBuffer(
int buf, uint64_t frameNumber, EGLDisplay display,
EGLSyncKHR eglFence, const sp<Fence>& fence) {

View File

@ -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<android::GraphicBuffer>& 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<Fence>& 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;

View File

@ -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<android::Fence> *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<android::GraphicBuffer>& 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<IConsumerListener> 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

View File

@ -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<GraphicBuffer>& 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<Fence>& 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<GraphicBuffer> 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();

View File

@ -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<GraphicBuffer>& 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<GraphicBuffer> 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();

View File

@ -24,27 +24,31 @@
#include <ui/GraphicBuffer.h>
#include <binder/IServiceManager.h>
#include <gui/BufferQueue.h>
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<IServiceManager> 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<BufferQueue> mBQ;
sp<BnGraphicBufferProducer> mProducer;
sp<BnGraphicBufferConsumer> 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<DummyConsumer> 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> 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<DummyConsumer> 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<DummyConsumer> 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<DummyConsumer> 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> fence;
sp<GraphicBuffer> 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<GraphicBuffer> 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<void**>(&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<nsecs_t>(0)));
uint32_t* dataOut;
ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
reinterpret_cast<void**>(&dataOut)));
ASSERT_EQ(*dataOut, 0x12345678);
}
TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) {
sp<DummyConsumer> 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> fence;
sp<GraphicBuffer> 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<nsecs_t>(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<void**>(&dataIn)));
*dataIn = 0x12345678;
ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
int newSlot;
sp<GraphicBuffer> 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<void**>(&dataOut)));
ASSERT_EQ(*dataOut, 0x12345678);
}
TEST_F(BufferQueueTest, MoveFromConsumerToProducer) {
sp<DummyConsumer> 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> fence;
sp<GraphicBuffer> 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<void**>(&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<nsecs_t>(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<nsecs_t>(0)));
uint32_t* dataOut;
ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
reinterpret_cast<void**>(&dataOut)));
ASSERT_EQ(*dataOut, 0x12345678);
}
} // namespace android

View File

@ -374,6 +374,15 @@ status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, bool
return result;
}
status_t VirtualDisplaySurface::detachBuffer(int slot) {
return mSource[SOURCE_SINK]->detachBuffer(slot);
}
status_t VirtualDisplaySurface::attachBuffer(int* outSlot,
const sp<GraphicBuffer>& 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,

View File

@ -98,6 +98,8 @@ private:
virtual status_t setBufferCount(int bufferCount);
virtual status_t dequeueBuffer(int* pslot, sp<Fence>* 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<GraphicBuffer>& buffer);
virtual status_t queueBuffer(int pslot,
const QueueBufferInput& input, QueueBufferOutput* output);
virtual void cancelBuffer(int pslot, const sp<Fence>& fence);