From a61e4fbf1b2276ae43d23d8889f214f40ca13e20 Mon Sep 17 00:00:00 2001 From: Vishnu Nair Date: Tue, 18 Oct 2022 18:29:37 +0000 Subject: SF: Move TransactionHandler to frontend Makes it easier to carve out a library in the future. Test: presubmit Bug: 238781169 Change-Id: Iaaa576b47b226114eba48606bffa9ed0dc77c71a --- services/surfaceflinger/Android.bp | 2 +- .../surfaceflinger/FrontEnd/TransactionHandler.cpp | 188 +++++++++++++++++++++ .../surfaceflinger/FrontEnd/TransactionHandler.h | 74 ++++++++ services/surfaceflinger/SurfaceFlinger.h | 2 +- services/surfaceflinger/TransactionHandler.cpp | 188 --------------------- services/surfaceflinger/TransactionHandler.h | 74 -------- .../tests/unittests/TransactionApplicationTest.cpp | 2 +- 7 files changed, 265 insertions(+), 265 deletions(-) create mode 100644 services/surfaceflinger/FrontEnd/TransactionHandler.cpp create mode 100644 services/surfaceflinger/FrontEnd/TransactionHandler.h delete mode 100644 services/surfaceflinger/TransactionHandler.cpp delete mode 100644 services/surfaceflinger/TransactionHandler.h diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 67a226e24c..c9c0143c7d 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -155,6 +155,7 @@ filegroup { "DisplayRenderArea.cpp", "Effects/Daltonizer.cpp", "EventLog/EventLog.cpp", + "FrontEnd/TransactionHandler.cpp", "FlagManager.cpp", "FpsReporter.cpp", "FrameTracer/FrameTracer.cpp", @@ -193,7 +194,6 @@ filegroup { "Tracing/TransactionTracing.cpp", "Tracing/TransactionProtoParser.cpp", "TransactionCallbackInvoker.cpp", - "TransactionHandler.cpp", "TunnelModeEnabledReporter.cpp", ], } diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp new file mode 100644 index 0000000000..6c6a487b67 --- /dev/null +++ b/services/surfaceflinger/FrontEnd/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 +#include + +#include "TransactionHandler.h" + +namespace android { + +void TransactionHandler::queueTransaction(TransactionState&& state) { + mLocklessTransactionQueue.push(std::move(state)); + mPendingTransactionCount.fetch_add(1); + ATRACE_INT("TransactionQueue", static_cast(mPendingTransactionCount.load())); +} + +std::vector 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 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(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& 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::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& 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 diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h new file mode 100644 index 0000000000..237b48d55f --- /dev/null +++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h @@ -0,0 +1,74 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace android { +class TransactionHandler { +public: + struct TransactionFlushState { + const TransactionState* transaction; + bool firstTransaction = true; + nsecs_t queueProcessTime = 0; + // Layer handles that have transactions with buffers that are ready to be applied. + ftl::SmallMap + bufferLayersReadyToPresent = {}; + ftl::SmallVector queuesWithUnsignaledBuffers; + }; + enum class TransactionReadiness { + NotReady, + NotReadyBarrier, + Ready, + ReadyUnsignaled, + ReadyUnsignaledSingle, + }; + using TransactionFilter = std::function; + + bool hasPendingTransactions(); + std::vector flushTransactions(); + void addTransactionReadyFilter(TransactionFilter&&); + void queueTransaction(TransactionState&&); + void onTransactionQueueStalled(const TransactionState&, sp&); + void removeFromStalledTransactions(uint64_t transactionId); + +private: + // For unit tests + friend class TestableSurfaceFlinger; + + int flushPendingTransactionQueues(std::vector&, TransactionFlushState&); + TransactionReadiness applyFilters(TransactionFlushState&); + std::unordered_map, std::queue, IListenerHash> + mPendingTransactionQueues; + LocklessQueue mLocklessTransactionQueue; + std::atomic mPendingTransactionCount = 0; + ftl::SmallVector mTransactionReadyFilters; + std::vector mStalledTransactions; +}; + +} // namespace android diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 0f8ac98854..d512841e9f 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -65,6 +65,7 @@ #include "DisplayIdGenerator.h" #include "Effects/Daltonizer.h" #include "FlagManager.h" +#include "FrontEnd/TransactionHandler.h" #include "LayerVector.h" #include "Scheduler/RefreshRateConfigs.h" #include "Scheduler/RefreshRateStats.h" @@ -75,7 +76,6 @@ #include "Tracing/LayerTracing.h" #include "Tracing/TransactionTracing.h" #include "TransactionCallbackInvoker.h" -#include "TransactionHandler.h" #include "TransactionState.h" #include diff --git a/services/surfaceflinger/TransactionHandler.cpp b/services/surfaceflinger/TransactionHandler.cpp deleted file mode 100644 index 6c6a487b67..0000000000 --- a/services/surfaceflinger/TransactionHandler.cpp +++ /dev/null @@ -1,188 +0,0 @@ -/* - * 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 -#include - -#include "TransactionHandler.h" - -namespace android { - -void TransactionHandler::queueTransaction(TransactionState&& state) { - mLocklessTransactionQueue.push(std::move(state)); - mPendingTransactionCount.fetch_add(1); - ATRACE_INT("TransactionQueue", static_cast(mPendingTransactionCount.load())); -} - -std::vector 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 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(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& 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::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& 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 diff --git a/services/surfaceflinger/TransactionHandler.h b/services/surfaceflinger/TransactionHandler.h deleted file mode 100644 index 237b48d55f..0000000000 --- a/services/surfaceflinger/TransactionHandler.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace android { -class TransactionHandler { -public: - struct TransactionFlushState { - const TransactionState* transaction; - bool firstTransaction = true; - nsecs_t queueProcessTime = 0; - // Layer handles that have transactions with buffers that are ready to be applied. - ftl::SmallMap - bufferLayersReadyToPresent = {}; - ftl::SmallVector queuesWithUnsignaledBuffers; - }; - enum class TransactionReadiness { - NotReady, - NotReadyBarrier, - Ready, - ReadyUnsignaled, - ReadyUnsignaledSingle, - }; - using TransactionFilter = std::function; - - bool hasPendingTransactions(); - std::vector flushTransactions(); - void addTransactionReadyFilter(TransactionFilter&&); - void queueTransaction(TransactionState&&); - void onTransactionQueueStalled(const TransactionState&, sp&); - void removeFromStalledTransactions(uint64_t transactionId); - -private: - // For unit tests - friend class TestableSurfaceFlinger; - - int flushPendingTransactionQueues(std::vector&, TransactionFlushState&); - TransactionReadiness applyFilters(TransactionFlushState&); - std::unordered_map, std::queue, IListenerHash> - mPendingTransactionQueues; - LocklessQueue mLocklessTransactionQueue; - std::atomic mPendingTransactionCount = 0; - ftl::SmallVector mTransactionReadyFilters; - std::vector mStalledTransactions; -}; - -} // namespace android diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp index db438b70ab..de84faa5d2 100644 --- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp @@ -30,8 +30,8 @@ #include #include +#include "FrontEnd/TransactionHandler.h" #include "TestableSurfaceFlinger.h" -#include "TransactionHandler.h" #include "mock/MockEventThread.h" #include "mock/MockVsyncController.h" -- cgit v1.2.3-59-g8ed1b