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