Allow BlastBufferQueue to acquire an additional buffer

We want to be able to send buffers from BlastBufferQueue to the server
as soon as possible. This allows us among other things, dropping the
oldest buffer when multiple buffers with timestamps are queued up.
To support this behavior with BlastBufferQueue, we need to acquire an
additional buffer and send it to SurfaceFlinger.

This requires changing the consumer owned by BlastBufferQueue to
acquire an additional buffer. This change is safe because we will only
acquire the buffer if its not droppable, maintaining the contract
with the producer.

If the buffer is not droppable, i.e. the producer is in sync mode, once
the buffer has been queued, the buffer is owned by the queue until it
is released or acquired by the consumer. By acquiring an additional
buffer, we transfer the ownership to the consumer earlier. The server
has more info to make decisions faster. The producer still has access
the same number of buffers and is unaffected.

If the producer is in async mode, then this buffer may be released by
the producer when trying to queue a buffer. So we check if the buffer is
droppable, and we do not acquire the extra buffer in this scenario.

Test: atest BlastBufferQueueTests android.media.cts.PresentationSyncTest
Bug: b/176507654, b/176967609
Change-Id: I494a9edcbea0b1c297ee75df2b840d8328e59eca
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 0a3d44d..f9b152c 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -248,7 +248,7 @@
         mPendingReleaseItem.item = std::move(mSubmitted.front());
         mSubmitted.pop();
 
-        processNextBufferLocked(false);
+        processNextBufferLocked(false /* useNextTransaction */);
 
         currFrameNumber = mPendingReleaseItem.item.mFrameNumber;
         if (mTransactionCompleteCallback && mTransactionCompleteFrameNumber == currFrameNumber) {
@@ -269,9 +269,10 @@
     ATRACE_CALL();
     BQA_LOGV("processNextBufferLocked useNextTransaction=%s", toString(useNextTransaction));
 
-    // Wait to acquire a buffer if there are no frames available or we have acquired the max
-    // number of buffers.
-    if (mNumFrameAvailable == 0 || maxBuffersAcquired()) {
+    // If the next transaction is set, we want to guarantee the our acquire will not fail, so don't
+    // include the extra buffer when checking if we can acquire the next buffer.
+    const bool includeExtraAcquire = !useNextTransaction;
+    if (mNumFrameAvailable == 0 || maxBuffersAcquired(includeExtraAcquire)) {
         BQA_LOGV("processNextBufferLocked waiting for frame available or callback");
         mCallbackCV.notify_all();
         return;
@@ -295,7 +296,10 @@
 
     status_t status =
             mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false);
-    if (status != OK) {
+    if (status == BufferQueue::NO_BUFFER_AVAILABLE) {
+        BQA_LOGV("Failed to acquire a buffer, err=NO_BUFFER_AVAILABLE");
+        return;
+    } else if (status != OK) {
         BQA_LOGE("Failed to acquire a buffer, err=%s", statusToString(status).c_str());
         return;
     }
@@ -414,7 +418,7 @@
              item.mFrameNumber, toString(nextTransactionSet), toString(mFlushShadowQueue));
 
     if (nextTransactionSet || mFlushShadowQueue) {
-        while (mNumFrameAvailable > 0 || maxBuffersAcquired()) {
+        while (mNumFrameAvailable > 0 || maxBuffersAcquired(false /* includeExtraAcquire */)) {
             BQA_LOGV("waiting in onFrameAvailable...");
             mCallbackCV.wait(_lock);
         }
@@ -422,7 +426,7 @@
     mFlushShadowQueue = false;
     // add to shadow queue
     mNumFrameAvailable++;
-    processNextBufferLocked(true);
+    processNextBufferLocked(nextTransactionSet /* useNextTransaction */);
 }
 
 void BLASTBufferQueue::onFrameReplaced(const BufferItem& item) {
@@ -482,9 +486,12 @@
 // Check if we have acquired the maximum number of buffers.
 // As a special case, we wait for the first callback before acquiring the second buffer so we
 // can ensure the first buffer is presented if multiple buffers are queued in succession.
-bool BLASTBufferQueue::maxBuffersAcquired() const {
-    return mNumAcquired == MAX_ACQUIRED_BUFFERS + 1 ||
-            (!mInitialCallbackReceived && mNumAcquired == 1);
+// Consumer can acquire an additional buffer if that buffer is not droppable. Set
+// includeExtraAcquire is true to include this buffer to the count. Since this depends on the state
+// of the buffer, the next acquire may return with NO_BUFFER_AVAILABLE.
+bool BLASTBufferQueue::maxBuffersAcquired(bool includeExtraAcquire) const {
+    int maxAcquiredBuffers = MAX_ACQUIRED_BUFFERS + (includeExtraAcquire ? 2 : 1);
+    return mNumAcquired == maxAcquiredBuffers || (!mInitialCallbackReceived && mNumAcquired == 1);
 }
 
 class BBQSurface : public Surface {
@@ -654,7 +661,8 @@
     LOG_ALWAYS_FATAL_IF(producer == nullptr,
                         "BLASTBufferQueue: failed to create BBQBufferQueueProducer");
 
-    sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
+    sp<BufferQueueConsumer> consumer(new BufferQueueConsumer(core));
+    consumer->setAllowExtraAcquire(true);
     LOG_ALWAYS_FATAL_IF(consumer == nullptr,
                         "BLASTBufferQueue: failed to create BufferQueueConsumer");
 
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index da6143c..7f7a043 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -94,7 +94,10 @@
                 ++numAcquiredBuffers;
             }
         }
-        if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
+        const bool acquireNonDroppableBuffer = mCore->mAllowExtraAcquire &&
+                numAcquiredBuffers == mCore->mMaxAcquiredBufferCount + 1;
+        if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1 &&
+            !acquireNonDroppableBuffer) {
             BQ_LOGE("acquireBuffer: max acquired buffer count reached: %d (max %d)",
                     numAcquiredBuffers, mCore->mMaxAcquiredBufferCount);
             return INVALID_OPERATION;
@@ -254,6 +257,9 @@
             outBuffer->mIsStale = false;
             outBuffer->mAutoRefresh = mCore->mSharedBufferMode &&
                     mCore->mAutoRefresh;
+        } else if (acquireNonDroppableBuffer && front->mIsDroppable) {
+            BQ_LOGV("acquireBuffer: front buffer is not droppable");
+            return NO_BUFFER_AVAILABLE;
         } else {
             slot = front->mSlot;
             *outBuffer = *front;
@@ -824,4 +830,9 @@
     return NO_ERROR;
 }
 
+void BufferQueueConsumer::setAllowExtraAcquire(bool allow) {
+    std::lock_guard<std::mutex> lock(mCore->mMutex);
+    mCore->mAllowExtraAcquire = allow;
+}
+
 } // namespace android
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 411526e..7f69bc4 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -109,7 +109,7 @@
     Rect computeCrop(const BufferItem& item) REQUIRES(mMutex);
     // Return true if we need to reject the buffer based on the scaling mode and the buffer size.
     bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex);
-    bool maxBuffersAcquired() const REQUIRES(mMutex);
+    bool maxBuffersAcquired(bool includeExtraAcquire) const REQUIRES(mMutex);
 
     std::string mName;
     sp<SurfaceControl> mSurfaceControl;
diff --git a/libs/gui/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h
index 7db69ec..6aa801a 100644
--- a/libs/gui/include/gui/BufferQueueConsumer.h
+++ b/libs/gui/include/gui/BufferQueueConsumer.h
@@ -174,6 +174,10 @@
     // Value used to determine if present time is valid.
     constexpr static int MAX_REASONABLE_NSEC = 1'000'000'000ULL; // 1 second
 
+    // This allows the consumer to acquire an additional buffer if that buffer is not droppable and
+    // will eventually be released or acquired by the consumer.
+    void setAllowExtraAcquire(bool /* allow */);
+
 private:
     sp<BufferQueueCore> mCore;
 
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 557c28b..8d0828d 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -354,6 +354,9 @@
     // mTransformHintInUse is to cache the mTransformHint used by the producer.
     uint32_t mTransformHintInUse;
 
+    // This allows the consumer to acquire an additional buffer if that buffer is not droppable and
+    // will eventually be released or acquired by the consumer.
+    bool mAllowExtraAcquire = false;
 }; // class BufferQueueCore
 
 } // namespace android