diff options
| author | 2022-10-05 16:59:56 -0700 | |
|---|---|---|
| committer | 2022-10-06 03:28:52 +0000 | |
| commit | 59f6d2df6cbc895651eb2628c9f98505ed9de8bf (patch) | |
| tree | 4ccb96004846f9fb507f0e413db68e4eafdffde0 /services/surfaceflinger/TransactionHandler.cpp | |
| parent | 1523dad8dc7c2cd9a110e0839ada541f49920a75 (diff) | |
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
Diffstat (limited to 'services/surfaceflinger/TransactionHandler.cpp')
| -rw-r--r-- | services/surfaceflinger/TransactionHandler.cpp | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/services/surfaceflinger/TransactionHandler.cpp b/services/surfaceflinger/TransactionHandler.cpp new file mode 100644 index 0000000000..6c6a487b67 --- /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 |