SF: Add TransactionHandler

- migrate transaction queueing and flushing into
  a new class and remove dependencies from
  other components.
- Add a filter interface for other components to
  participate in transactionready logic.

Test: presubmit
Bug: 238781169
Change-Id: Ia4da386cd72058126f6f765adafb9cb4d15b1d2b
diff --git a/services/surfaceflinger/TransactionHandler.cpp b/services/surfaceflinger/TransactionHandler.cpp
new file mode 100644
index 0000000..6c6a487
--- /dev/null
+++ b/services/surfaceflinger/TransactionHandler.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "TransactionHandler"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <cutils/trace.h>
+#include <utils/Log.h>
+
+#include "TransactionHandler.h"
+
+namespace android {
+
+void TransactionHandler::queueTransaction(TransactionState&& state) {
+    mLocklessTransactionQueue.push(std::move(state));
+    mPendingTransactionCount.fetch_add(1);
+    ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
+}
+
+std::vector<TransactionState> TransactionHandler::flushTransactions() {
+    while (!mLocklessTransactionQueue.isEmpty()) {
+        auto maybeTransaction = mLocklessTransactionQueue.pop();
+        if (!maybeTransaction.has_value()) {
+            break;
+        }
+        auto transaction = maybeTransaction.value();
+        mPendingTransactionQueues[transaction.applyToken].emplace(std::move(transaction));
+    }
+
+    // Collect transaction that are ready to be applied.
+    std::vector<TransactionState> transactions;
+    TransactionFlushState flushState;
+    flushState.queueProcessTime = systemTime();
+    // Transactions with a buffer pending on a barrier may be on a different applyToken
+    // than the transaction which satisfies our barrier. In fact this is the exact use case
+    // that the primitive is designed for. This means we may first process
+    // the barrier dependent transaction, determine it ineligible to complete
+    // and then satisfy in a later inner iteration of flushPendingTransactionQueues.
+    // The barrier dependent transaction was eligible to be presented in this frame
+    // but we would have prevented it without case. To fix this we continually
+    // loop through flushPendingTransactionQueues until we perform an iteration
+    // where the number of transactionsPendingBarrier doesn't change. This way
+    // we can continue to resolve dependency chains of barriers as far as possible.
+    int lastTransactionsPendingBarrier = 0;
+    int transactionsPendingBarrier = 0;
+    do {
+        lastTransactionsPendingBarrier = transactionsPendingBarrier;
+        // Collect transactions that are ready to be applied.
+        transactionsPendingBarrier = flushPendingTransactionQueues(transactions, flushState);
+    } while (lastTransactionsPendingBarrier != transactionsPendingBarrier);
+
+    mPendingTransactionCount.fetch_sub(transactions.size());
+    ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
+    return transactions;
+}
+
+TransactionHandler::TransactionReadiness TransactionHandler::applyFilters(
+        TransactionFlushState& flushState) {
+    auto ready = TransactionReadiness::Ready;
+    for (auto& filter : mTransactionReadyFilters) {
+        auto perFilterReady = filter(flushState);
+        switch (perFilterReady) {
+            case TransactionReadiness::NotReady:
+            case TransactionReadiness::NotReadyBarrier:
+                return perFilterReady;
+
+            case TransactionReadiness::ReadyUnsignaled:
+            case TransactionReadiness::ReadyUnsignaledSingle:
+                // If one of the filters allows latching an unsignaled buffer, latch this ready
+                // state.
+                ready = perFilterReady;
+                break;
+            case TransactionReadiness::Ready:
+                continue;
+        }
+    }
+    return ready;
+}
+
+int TransactionHandler::flushPendingTransactionQueues(std::vector<TransactionState>& transactions,
+                                                      TransactionFlushState& flushState) {
+    int transactionsPendingBarrier = 0;
+    auto it = mPendingTransactionQueues.begin();
+    while (it != mPendingTransactionQueues.end()) {
+        auto& queue = it->second;
+        IBinder* queueToken = it->first.get();
+
+        // if we have already flushed a transaction with an unsignaled buffer then stop queue
+        // processing
+        if (std::find(flushState.queuesWithUnsignaledBuffers.begin(),
+                      flushState.queuesWithUnsignaledBuffers.end(),
+                      queueToken) != flushState.queuesWithUnsignaledBuffers.end()) {
+            continue;
+        }
+
+        while (!queue.empty()) {
+            auto& transaction = queue.front();
+            flushState.transaction = &transaction;
+            auto ready = applyFilters(flushState);
+            if (ready == TransactionReadiness::NotReadyBarrier) {
+                transactionsPendingBarrier++;
+                break;
+            } else if (ready == TransactionReadiness::NotReady) {
+                break;
+            }
+
+            // Transaction is ready move it from the pending queue.
+            flushState.firstTransaction = false;
+            removeFromStalledTransactions(transaction.id);
+            transactions.emplace_back(std::move(transaction));
+            queue.pop();
+
+            // If the buffer is unsignaled, then we don't want to signal other transactions using
+            // the buffer as a barrier.
+            auto& readyToApplyTransaction = transactions.back();
+            if (ready == TransactionReadiness::Ready) {
+                readyToApplyTransaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
+                    const bool frameNumberChanged = state.bufferData->flags.test(
+                            BufferData::BufferDataChange::frameNumberChanged);
+                    if (frameNumberChanged) {
+                        flushState.bufferLayersReadyToPresent
+                                .emplace_or_replace(state.surface.get(),
+                                                    state.bufferData->frameNumber);
+                    } else {
+                        // Barrier function only used for BBQ which always includes a frame number.
+                        // This value only used for barrier logic.
+                        flushState.bufferLayersReadyToPresent
+                                .emplace_or_replace(state.surface.get(),
+                                                    std::numeric_limits<uint64_t>::max());
+                    }
+                });
+            } else if (ready == TransactionReadiness::ReadyUnsignaledSingle) {
+                // Track queues with a flushed unsingaled buffer.
+                flushState.queuesWithUnsignaledBuffers.emplace_back(queueToken);
+                break;
+            }
+        }
+
+        if (queue.empty()) {
+            it = mPendingTransactionQueues.erase(it);
+        } else {
+            it = std::next(it, 1);
+        }
+    }
+    return transactionsPendingBarrier;
+}
+
+void TransactionHandler::addTransactionReadyFilter(TransactionFilter&& filter) {
+    mTransactionReadyFilters.emplace_back(std::move(filter));
+}
+
+bool TransactionHandler::hasPendingTransactions() {
+    return !mPendingTransactionQueues.empty() || !mLocklessTransactionQueue.isEmpty();
+}
+
+void TransactionHandler::onTransactionQueueStalled(const TransactionState& transaction,
+                                                   sp<ITransactionCompletedListener>& listener) {
+    if (std::find(mStalledTransactions.begin(), mStalledTransactions.end(), transaction.id) !=
+        mStalledTransactions.end()) {
+        return;
+    }
+
+    mStalledTransactions.push_back(transaction.id);
+    listener->onTransactionQueueStalled();
+}
+
+void TransactionHandler::removeFromStalledTransactions(uint64_t id) {
+    auto it = std::find(mStalledTransactions.begin(), mStalledTransactions.end(), id);
+    if (it != mStalledTransactions.end()) {
+        mStalledTransactions.erase(it);
+    }
+}
+} // namespace android
\ No newline at end of file