BQ: add setMaxDequeuedBufferCount
Adds the new setMaxDequeuedBufferCount() function to
BufferQueueProducer. This will eventually replace setBufferCount.
Also add setAsyncMode.
Bug 13174928
Change-Id: Iea1adcd5d74a75f67d8e9dde06d521695628ad5a
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 851a396..70d80a7 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -65,6 +65,7 @@
mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
mDefaultMaxBufferCount(2),
mMaxAcquiredBufferCount(1),
+ mMaxDequeuedBufferCount(1),
mBufferHasBeenQueued(false),
mFrameCounter(0),
mTransformHint(0),
@@ -72,7 +73,8 @@
mIsAllocatingCondition(),
mAllowAllocation(true),
mBufferAge(0),
- mGenerationNumber(0)
+ mGenerationNumber(0),
+ mAsyncMode(false)
{
if (allocator == NULL) {
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
@@ -104,11 +106,12 @@
}
result.appendFormat("%s-BufferQueue mMaxAcquiredBufferCount=%d, "
- "mDequeueBufferCannotBlock=%d, default-size=[%dx%d], "
- "default-format=%d, transform-hint=%02x, FIFO(%zu)={%s}\n",
- prefix, mMaxAcquiredBufferCount, mDequeueBufferCannotBlock,
- mDefaultWidth, mDefaultHeight, mDefaultBufferFormat, mTransformHint,
- mQueue.size(), fifo.string());
+ "mMaxDequeuedBufferCount=%d, mDequeueBufferCannotBlock=%d, "
+ "default-size=[%dx%d], default-format=%d, transform-hint=%02x, "
+ "FIFO(%zu)={%s}\n",
+ prefix, mMaxAcquiredBufferCount, mMaxDequeuedBufferCount,
+ mDequeueBufferCannotBlock, mDefaultWidth, mDefaultHeight,
+ mDefaultBufferFormat, mTransformHint, mQueue.size(), fifo.string());
// Trim the free buffers so as to not spam the dump
int maxBufferCount = 0;
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index a440f6e..5ea4a10 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -118,6 +118,7 @@
// clear the queue, however, so that currently queued buffers still
// get displayed.
mCore->freeAllBuffersLocked();
+ mCore->mMaxDequeuedBufferCount = bufferCount - minBufferSlots + 1;
mCore->mOverrideMaxBufferCount = bufferCount;
mCore->mDequeueCondition.broadcast();
listener = mCore->mConsumerListener;
@@ -131,6 +132,102 @@
return NO_ERROR;
}
+status_t BufferQueueProducer::setMaxDequeuedBufferCount(
+ int maxDequeuedBuffers) {
+ ATRACE_CALL();
+ BQ_LOGV("setMaxDequeuedBufferCount: maxDequeuedBuffers = %d",
+ maxDequeuedBuffers);
+
+ sp<IConsumerListener> listener;
+ { // Autolock scope
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked();
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("setMaxDequeuedBufferCount: BufferQueue has been "
+ "abandoned");
+ return NO_INIT;
+ }
+
+ // There must be no dequeued buffers when changing the buffer count.
+ for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ if (mSlots[s].mBufferState == BufferSlot::DEQUEUED) {
+ BQ_LOGE("setMaxDequeuedBufferCount: buffer owned by producer");
+ return BAD_VALUE;
+ }
+ }
+
+ int bufferCount = mCore->getMinUndequeuedBufferCountLocked(
+ mCore->mAsyncMode);
+ bufferCount += maxDequeuedBuffers;
+
+ if (bufferCount > BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ BQ_LOGE("setMaxDequeuedBufferCount: bufferCount %d too large "
+ "(max %d)", bufferCount, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ }
+
+ const int minBufferSlots = mCore->getMinMaxBufferCountLocked(
+ mCore->mAsyncMode);
+ if (bufferCount < minBufferSlots) {
+ BQ_LOGE("setMaxDequeuedBufferCount: requested buffer count %d is "
+ "less than minimum %d", bufferCount, minBufferSlots);
+ return BAD_VALUE;
+ }
+
+ // Here we are guaranteed that the producer doesn't have any dequeued
+ // buffers and will release all of its buffer references. We don't
+ // clear the queue, however, so that currently queued buffers still
+ // get displayed.
+ mCore->freeAllBuffersLocked();
+ mCore->mMaxDequeuedBufferCount = maxDequeuedBuffers;
+ mCore->mOverrideMaxBufferCount = bufferCount;
+ mCore->mDequeueCondition.broadcast();
+ listener = mCore->mConsumerListener;
+ } // Autolock scope
+
+ // Call back without lock held
+ if (listener != NULL) {
+ listener->onBuffersReleased();
+ }
+
+ return NO_ERROR;
+}
+
+status_t BufferQueueProducer::setAsyncMode(bool async) {
+ ATRACE_CALL();
+ BQ_LOGV("setAsyncMode: async = %d", async);
+
+ sp<IConsumerListener> listener;
+ { // Autolock scope
+ Mutex::Autolock lock(mCore->mMutex);
+ mCore->waitWhileAllocatingLocked();
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("setAsyncMode: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ // There must be no dequeued buffers when changing the async mode.
+ for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ if (mSlots[s].mBufferState == BufferSlot::DEQUEUED) {
+ BQ_LOGE("setAsyncMode: buffer owned by producer");
+ return BAD_VALUE;
+ }
+ }
+
+ mCore->mAsyncMode = async;
+ mCore->mDequeueCondition.broadcast();
+ listener = mCore->mConsumerListener;
+ } // Autolock scope
+
+ // Call back without lock held
+ if (listener != NULL) {
+ listener->onBuffersReleased();
+ }
+ return NO_ERROR;
+}
+
status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller,
bool async, int* found, status_t* returnFlags) const {
bool tryAgain = true;
@@ -241,7 +338,7 @@
// 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 &&
+ if ((mCore->mDequeueBufferCannotBlock || mCore->mAsyncMode) &&
(acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
return WOULD_BLOCK;
}
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index d7a7885..4a6bada 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -49,6 +49,8 @@
ALLOW_ALLOCATION,
SET_GENERATION_NUMBER,
GET_CONSUMER_NAME,
+ SET_MAX_DEQUEUED_BUFFER_COUNT,
+ SET_ASYNC_MODE
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -95,6 +97,34 @@
return result;
}
+ virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) {
+ Parcel data, reply;
+ data.writeInterfaceToken(
+ IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32(maxDequeuedBuffers);
+ status_t result = remote()->transact(SET_MAX_DEQUEUED_BUFFER_COUNT,
+ data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readInt32();
+ return result;
+ }
+
+ virtual status_t setAsyncMode(bool async) {
+ Parcel data, reply;
+ data.writeInterfaceToken(
+ IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32(async);
+ status_t result = remote()->transact(SET_ASYNC_MODE,
+ data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readInt32();
+ return result;
+ }
+
virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, bool async,
uint32_t width, uint32_t height, PixelFormat format,
uint32_t usage) {
@@ -341,6 +371,20 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case SET_MAX_DEQUEUED_BUFFER_COUNT: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ int maxDequeuedBuffers = data.readInt32();
+ int result = setMaxDequeuedBufferCount(maxDequeuedBuffers);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
+ case SET_ASYNC_MODE: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ bool async = data.readInt32();
+ int result = setAsyncMode(async);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
case DEQUEUE_BUFFER: {
CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
bool async = static_cast<bool>(data.readInt32());
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 0a36c56..2a9273d 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -811,6 +811,39 @@
return err;
}
+int Surface::setMaxDequeuedBufferCount(int maxDequeuedBuffers) {
+ ATRACE_CALL();
+ ALOGV("Surface::setMaxDequeuedBufferCount");
+ Mutex::Autolock lock(mMutex);
+
+ status_t err = mGraphicBufferProducer->setMaxDequeuedBufferCount(
+ maxDequeuedBuffers);
+ ALOGE_IF(err, "IGraphicBufferProducer::setMaxDequeuedBufferCount(%d) "
+ "returned %s", maxDequeuedBuffers, strerror(-err));
+
+ if (err == NO_ERROR) {
+ freeAllBuffers();
+ }
+
+ return err;
+}
+
+int Surface::setAsyncMode(bool async) {
+ ATRACE_CALL();
+ ALOGV("Surface::setAsyncMode");
+ Mutex::Autolock lock(mMutex);
+
+ status_t err = mGraphicBufferProducer->setAsyncMode(async);
+ ALOGE_IF(err, "IGraphicBufferProducer::setAsyncMode(%d) returned %s",
+ async, strerror(-err));
+
+ if (err == NO_ERROR) {
+ freeAllBuffers();
+ }
+
+ return err;
+}
+
int Surface::setBuffersDimensions(uint32_t width, uint32_t height)
{
ATRACE_CALL();
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 1a54875..115e4db 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -145,7 +145,7 @@
IGraphicBufferProducer::QueueBufferOutput qbo;
mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
&qbo);
- mProducer->setBufferCount(4);
+ mProducer->setMaxDequeuedBufferCount(3);
int slot;
sp<Fence> fence;
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 4ef9a69..ced1555 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -569,4 +569,145 @@
}
+TEST_F(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) {
+ int minUndequeuedBuffers;
+ ASSERT_OK(mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &minUndequeuedBuffers));
+
+ const int minBuffers = 1;
+ const int maxBuffers = BufferQueue::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
+
+ ASSERT_OK(mProducer->setAsyncMode(false)) << "async mode: " << false;
+ ASSERT_OK(mProducer->setMaxDequeuedBufferCount(minBuffers))
+ << "bufferCount: " << minBuffers;
+
+ std::vector<DequeueBufferResult> dequeueList;
+
+ // Should now be able to dequeue up to minBuffers times
+ for (int i = 0; i < minBuffers; ++i) {
+ DequeueBufferResult result;
+
+ EXPECT_LE(OK,
+ dequeueBuffer(QUEUE_BUFFER_INPUT_ASYNC,
+ DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
+ TEST_PRODUCER_USAGE_BITS, &result))
+ << "iteration: " << i << ", slot: " << result.slot;
+
+ dequeueList.push_back(result);
+ }
+
+ // Cancel every buffer, so we can set buffer count again
+ for (auto& result : dequeueList) {
+ mProducer->cancelBuffer(result.slot, result.fence);
+ }
+
+ ASSERT_OK(mProducer->setMaxDequeuedBufferCount(maxBuffers));
+
+ // Should now be able to dequeue up to maxBuffers times
+ for (int i = 0; i < maxBuffers; ++i) {
+ int dequeuedSlot = -1;
+ sp<Fence> dequeuedFence;
+
+ EXPECT_LE(OK,
+ mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
+ QUEUE_BUFFER_INPUT_ASYNC,
+ DEFAULT_WIDTH, DEFAULT_HEIGHT,
+ DEFAULT_FORMAT,
+ TEST_PRODUCER_USAGE_BITS))
+ << "iteration: " << i << ", slot: " << dequeuedSlot;
+ }
+}
+
+TEST_F(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Fails) {
+ int minUndequeuedBuffers;
+ ASSERT_OK(mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &minUndequeuedBuffers));
+
+ const int minBuffers = 1;
+ const int maxBuffers = BufferQueue::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
+
+ ASSERT_OK(mProducer->setAsyncMode(false)) << "async mode: " << false;
+ // Buffer count was out of range
+ EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(0))
+ << "bufferCount: " << 0;
+ EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(maxBuffers + 1))
+ << "bufferCount: " << maxBuffers + 1;
+
+ // Prerequisite to fail out a valid setBufferCount call
+ {
+ int dequeuedSlot = -1;
+ sp<Fence> dequeuedFence;
+
+ ASSERT_LE(OK,
+ mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
+ QUEUE_BUFFER_INPUT_ASYNC,
+ DEFAULT_WIDTH, DEFAULT_HEIGHT,
+ DEFAULT_FORMAT,
+ TEST_PRODUCER_USAGE_BITS))
+ << "slot: " << dequeuedSlot;
+ }
+
+ // Client has one or more buffers dequeued
+ EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(minBuffers))
+ << "bufferCount: " << minBuffers;
+
+ // Abandon buffer queue
+ ASSERT_OK(mConsumer->consumerDisconnect());
+
+ // Fail because the buffer queue was abandoned
+ EXPECT_EQ(NO_INIT, mProducer->setMaxDequeuedBufferCount(minBuffers))
+ << "bufferCount: " << minBuffers;
+
+}
+
+TEST_F(IGraphicBufferProducerTest, SetAsyncMode_Succeeds) {
+ ASSERT_OK(mConsumer->setMaxAcquiredBufferCount(1)) << "maxAcquire: " << 1;
+ ASSERT_OK(mProducer->setAsyncMode(true)) << "async mode: " << true;
+ ASSERT_OK(mProducer->setMaxDequeuedBufferCount(1)) << "maxDequeue: " << 1;
+
+ int dequeuedSlot = -1;
+ sp<Fence> dequeuedFence;
+ IGraphicBufferProducer::QueueBufferInput input(QUEUE_BUFFER_INPUT_TIMESTAMP,
+ QUEUE_BUFFER_INPUT_IS_AUTO_TIMESTAMP, QUEUE_BUFFER_INPUT_DATASPACE,
+ QUEUE_BUFFER_INPUT_RECT, QUEUE_BUFFER_INPUT_SCALING_MODE,
+ QUEUE_BUFFER_INPUT_TRANSFORM, true, QUEUE_BUFFER_INPUT_FENCE);
+ IGraphicBufferProducer::QueueBufferOutput output;
+ sp<GraphicBuffer> dequeuedBuffer;
+
+ // Should now be able to queue/dequeue as many buffers as we want without
+ // blocking
+ for (int i = 0; i < 5; ++i) {
+ ASSERT_LE(OK, mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
+ true, DEFAULT_WIDTH, DEFAULT_HEIGHT,
+ DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)) << "slot : "
+ << dequeuedSlot;
+ ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer));
+ ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output));
+ }
+}
+
+TEST_F(IGraphicBufferProducerTest, SetAsyncMode_Fails) {
+ // Prerequisite to fail out a valid setBufferCount call
+ {
+ int dequeuedSlot = -1;
+ sp<Fence> dequeuedFence;
+
+ ASSERT_LE(OK, mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
+ QUEUE_BUFFER_INPUT_ASYNC, DEFAULT_WIDTH, DEFAULT_HEIGHT,
+ DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)) << "slot: "
+ << dequeuedSlot;
+ }
+
+ // Client has one or more buffers dequeued
+ EXPECT_EQ(BAD_VALUE, mProducer->setAsyncMode(false)) << "asyncMode: "
+ << false;
+
+ // Abandon buffer queue
+ ASSERT_OK(mConsumer->consumerDisconnect());
+
+ // Fail because the buffer queue was abandoned
+ EXPECT_EQ(NO_INIT, mProducer->setAsyncMode(false)) << "asyncMode: "
+ << false;
+}
+
} // namespace android