Merge "BufferQueue: Allow detaching/reattaching buffers"
This commit is contained in:
commit
c9ed7d3768
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user