Added mergeWithNextTransaction to BlastBufferQueue

Resurrected abandoned cl Ife2a8c9dbcfac7a5c3b294f64bc8dce40231f652

This is to support SurfaceView synchronizing transactions with
ViewRootImpl. Currently, when SurfaceView gets a callback from
PositionUpdateListener, it asks ViewRootImpl whether it's
waiting on a sync transactions. If so, it will add the SV transactions
to the sync transactions. Otherwise, it will apply the transaction on
its own.

By adding the new function mergeWithNextTransaction, SV can call into
BlastBufferQueue and send its own transaction without having to know
whether VRI is in a blast sync. As long as blast is enabled, it can
always send its transaction to the BlastBufferQueue, which will apply it
when the VRI frame comes in.

Test: open bubbles with sv blast
Test: atest SurfaceViewSyncTest
Bug: 175594838
Change-Id: I1d1b6eb3bbcaa844f430dd3a2f7cfbe59e558909
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 3415d9d..ee5552f 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -151,6 +151,19 @@
     mPendingReleaseItem.releaseFence = nullptr;
 }
 
+BLASTBufferQueue::~BLASTBufferQueue() {
+    if (mPendingTransactions.empty()) {
+        return;
+    }
+    BQA_LOGE("Applying pending transactions on dtor %d",
+             static_cast<uint32_t>(mPendingTransactions.size()));
+    SurfaceComposerClient::Transaction t;
+    for (auto& [targetFrameNumber, transaction] : mPendingTransactions) {
+        t.merge(std::move(transaction));
+    }
+    t.apply();
+}
+
 void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height) {
     std::unique_lock _lock{mMutex};
     mSurfaceControl = surface;
@@ -312,6 +325,7 @@
     incStrong((void*)transactionCallbackThunk);
 
     mLastBufferScalingMode = bufferItem.mScalingMode;
+    mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
 
     t->setBuffer(mSurfaceControl, buffer);
     t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
@@ -341,14 +355,29 @@
         mAutoRefresh = bufferItem.mAutoRefresh;
     }
 
+    auto mergeTransaction =
+            [&t, currentFrameNumber = bufferItem.mFrameNumber](
+                    std::tuple<uint64_t, SurfaceComposerClient::Transaction> pendingTransaction) {
+                auto& [targetFrameNumber, transaction] = pendingTransaction;
+                if (currentFrameNumber < targetFrameNumber) {
+                    return false;
+                }
+                t->merge(std::move(transaction));
+                return true;
+            };
+
+    mPendingTransactions.erase(std::remove_if(mPendingTransactions.begin(),
+                                              mPendingTransactions.end(), mergeTransaction),
+                               mPendingTransactions.end());
+
     if (applyTransaction) {
         t->apply();
     }
 
     BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
-             " applyTransaction=%s mTimestamp=%" PRId64,
+             " applyTransaction=%s mTimestamp=%" PRId64 " mPendingTransactions.size=%d",
              mSize.width, mSize.height, bufferItem.mFrameNumber, toString(applyTransaction),
-             bufferItem.mTimestamp);
+             bufferItem.mTimestamp, static_cast<uint32_t>(mPendingTransactions.size()));
 }
 
 Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
@@ -487,6 +516,17 @@
     return new BBQSurface(mProducer, true, scHandle, this);
 }
 
+void BLASTBufferQueue::mergeWithNextTransaction(SurfaceComposerClient::Transaction* t,
+                                                uint64_t frameNumber) {
+    std::lock_guard _lock{mMutex};
+    if (mLastAcquiredFrameNumber >= frameNumber) {
+        // Apply the transaction since we have already acquired the desired frame.
+        t->apply();
+    } else {
+        mPendingTransactions.emplace_back(frameNumber, std::move(*t));
+    }
+}
+
 // Maintains a single worker thread per process that services a list of runnables.
 class AsyncWorker : public Singleton<AsyncWorker> {
 private:
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 98e0e5a..c4cdb65 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -82,6 +82,7 @@
     void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
             const std::vector<SurfaceControlStats>& stats);
     void setNextTransaction(SurfaceComposerClient::Transaction *t);
+    void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
     void setTransactionCompleteCallback(uint64_t frameNumber,
                                         std::function<void(int64_t)>&& transactionCompleteCallback);
 
@@ -91,7 +92,7 @@
     status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
     status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId);
 
-    virtual ~BLASTBufferQueue() = default;
+    virtual ~BLASTBufferQueue();
 
 private:
     friend class BLASTBufferQueueHelper;
@@ -141,6 +142,9 @@
     sp<BLASTBufferItemConsumer> mBufferItemConsumer;
 
     SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex);
+    std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
+            mPendingTransactions GUARDED_BY(mMutex);
+
     // If set to true, the next queue buffer will wait until the shadow queue has been processed by
     // the adapter.
     bool mFlushShadowQueue = false;
@@ -156,6 +160,9 @@
     // where the layer can change sizes and the buffer will scale to fit the new size.
     uint32_t mLastBufferScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
 
+    // Tracks the last acquired frame number
+    uint64_t mLastAcquiredFrameNumber GUARDED_BY(mMutex) = 0;
+
     std::function<void(int64_t)> mTransactionCompleteCallback GUARDED_BY(mMutex) = nullptr;
     uint64_t mTransactionCompleteFrameNumber GUARDED_BY(mMutex){0};
 };