Add ability to process buffers into the same syncTransaction

In a normal sync case, we send the syncTransaction and then clear it as
soon as a buffer is acquired and placed in the transaction. However, in
some cases, we want to continue processing buffers into the sync
transaction until we get the signal to stop. This is for cases when we
don't have a clear signal which buffer wants to be synced, but know when
it's done waiting for the correct buffer.

This also adds a way to retrieve and clear the buffer from the
transaction to allow BBQ to release the buffer when it knows it's ready
to acquire a new buffer. This speeds up the process so BBQ doesn't need
to wait for the asynchronous callback to release the previous buffer.

This new machanism can be used to synchronize SurfaceViews because it
knows when something has been changed and to wait until the app has
notified that it's drawn. Once it's notified, we can stop processing
frames into the sync transaction and allow the transaction to be applied
with the main window.

Bug: 200284684
Test: BLASTBufferQueueTest
Change-Id: Ibdb524270de368033cf8835f0c305e8a5756ff6e
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index e9149f3..5570321 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -407,18 +407,9 @@
 
     // Release all buffers that are beyond the ones that we need to hold
     while (mPendingRelease.size() > numPendingBuffersToHold) {
-        const auto releaseBuffer = mPendingRelease.front();
+        const auto releasedBuffer = mPendingRelease.front();
         mPendingRelease.pop_front();
-        auto it = mSubmitted.find(releaseBuffer.callbackId);
-        if (it == mSubmitted.end()) {
-            BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s",
-                     releaseBuffer.callbackId.to_string().c_str());
-            return;
-        }
-        mNumAcquired--;
-        BQA_LOGV("released %s", releaseBuffer.callbackId.to_string().c_str());
-        mBufferItemConsumer->releaseBuffer(it->second, releaseBuffer.releaseFence);
-        mSubmitted.erase(it);
+        releaseBuffer(releasedBuffer.callbackId, releasedBuffer.releaseFence);
         // Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let
         // onFrameAvailable handle processing them since it will merge with the syncTransaction.
         if (!mWaitForTransactionCallback) {
@@ -432,6 +423,20 @@
     mCallbackCV.notify_all();
 }
 
+void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId,
+                                     const sp<Fence>& releaseFence) {
+    auto it = mSubmitted.find(callbackId);
+    if (it == mSubmitted.end()) {
+        BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s",
+                 callbackId.to_string().c_str());
+        return;
+    }
+    mNumAcquired--;
+    BQA_LOGV("released %s", callbackId.to_string().c_str());
+    mBufferItemConsumer->releaseBuffer(it->second, releaseFence);
+    mSubmitted.erase(it);
+}
+
 void BLASTBufferQueue::acquireNextBufferLocked(
         const std::optional<SurfaceComposerClient::Transaction*> transaction) {
     ATRACE_CALL();
@@ -589,34 +594,57 @@
     mBufferItemConsumer->releaseBuffer(bufferItem, bufferItem.mFence);
 }
 
+void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) {
+    if (mWaitForTransactionCallback && mNumFrameAvailable > 0) {
+        // We are waiting on a previous sync's transaction callback so allow another sync
+        // transaction to proceed.
+        //
+        // We need to first flush out the transactions that were in between the two syncs.
+        // We do this by merging them into mSyncTransaction so any buffer merging will get
+        // a release callback invoked. The release callback will be async so we need to wait
+        // on max acquired to make sure we have the capacity to acquire another buffer.
+        if (maxBuffersAcquired(false /* includeExtraAcquire */)) {
+            BQA_LOGD("waiting to flush shadow queue...");
+            mCallbackCV.wait(lock);
+        }
+        while (mNumFrameAvailable > 0) {
+            // flush out the shadow queue
+            acquireAndReleaseBuffer();
+        }
+    }
+
+    while (maxBuffersAcquired(false /* includeExtraAcquire */)) {
+        BQA_LOGD("waiting for free buffer.");
+        mCallbackCV.wait(lock);
+    }
+}
+
 void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
     ATRACE_CALL();
     std::unique_lock _lock{mMutex};
 
     const bool syncTransactionSet = mSyncTransaction != nullptr;
     BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet));
+
     if (syncTransactionSet) {
-        if (mWaitForTransactionCallback) {
-            // We are waiting on a previous sync's transaction callback so allow another sync
-            // transaction to proceed.
-            //
-            // We need to first flush out the transactions that were in between the two syncs.
-            // We do this by merging them into mSyncTransaction so any buffer merging will get
-            // a release callback invoked. The release callback will be async so we need to wait
-            // on max acquired to make sure we have the capacity to acquire another buffer.
-            if (maxBuffersAcquired(false /* includeExtraAcquire */)) {
-                BQA_LOGD("waiting to flush shadow queue...");
-                mCallbackCV.wait(_lock);
-            }
-            while (mNumFrameAvailable > 0) {
-                // flush out the shadow queue
-                acquireAndReleaseBuffer();
+        bool mayNeedToWaitForBuffer = true;
+        // If we are going to re-use the same mSyncTransaction, release the buffer that may already
+        // be set in the Transaction. This is to allow us a free slot early to continue processing
+        // a new buffer.
+        if (!mAcquireSingleBuffer) {
+            auto bufferData = mSyncTransaction->getAndClearBuffer(mSurfaceControl);
+            if (bufferData) {
+                BQA_LOGD("Releasing previous buffer when syncing: framenumber=%" PRIu64,
+                         bufferData->frameNumber);
+                releaseBuffer(bufferData->releaseCallbackId, bufferData->acquireFence);
+                // Because we just released a buffer, we know there's no need to wait for a free
+                // buffer.
+                mayNeedToWaitForBuffer = false;
             }
         }
 
-        while (maxBuffersAcquired(false /* includeExtraAcquire */)) {
-            BQA_LOGD("waiting for free buffer.");
-            mCallbackCV.wait(_lock);
+        if (mayNeedToWaitForBuffer) {
+            flushAndWaitForFreeBuffer(_lock);
         }
     }
 
@@ -629,8 +657,10 @@
              boolToString(syncTransactionSet));
 
     if (syncTransactionSet) {
-        acquireNextBufferLocked(std::move(mSyncTransaction));
-        mSyncTransaction = nullptr;
+        acquireNextBufferLocked(mSyncTransaction);
+        if (mAcquireSingleBuffer) {
+            mSyncTransaction = nullptr;
+        }
         mWaitForTransactionCallback = true;
     } else if (!mWaitForTransactionCallback) {
         acquireNextBufferLocked(std::nullopt);
@@ -652,9 +682,11 @@
     mDequeueTimestamps.erase(bufferId);
 };
 
-void BLASTBufferQueue::setSyncTransaction(SurfaceComposerClient::Transaction* t) {
+void BLASTBufferQueue::setSyncTransaction(SurfaceComposerClient::Transaction* t,
+                                          bool acquireSingleBuffer) {
     std::lock_guard _lock{mMutex};
     mSyncTransaction = t;
+    mAcquireSingleBuffer = mSyncTransaction ? acquireSingleBuffer : true;
 }
 
 bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) {