/* * 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 "SurfaceFlinger" #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include #include #include #include "FrontEnd/LayerLog.h" #include "TransactionHandler.h" namespace android::surfaceflinger::frontend { void TransactionHandler::queueTransaction(TransactionState&& state) { mLocklessTransactionQueue.push(std::move(state)); mPendingTransactionCount.fetch_add(1); ATRACE_INT("TransactionQueue", static_cast(mPendingTransactionCount.load())); } void TransactionHandler::collectTransactions() { while (!mLocklessTransactionQueue.isEmpty()) { auto maybeTransaction = mLocklessTransactionQueue.pop(); if (!maybeTransaction.has_value()) { break; } auto transaction = maybeTransaction.value(); mPendingTransactionQueues[transaction.applyToken].emplace(std::move(transaction)); } } std::vector TransactionHandler::flushTransactions() { // 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); applyUnsignaledBufferTransaction(transactions, flushState); mPendingTransactionCount.fetch_sub(transactions.size()); ATRACE_INT("TransactionQueue", static_cast(mPendingTransactionCount.load())); return transactions; } void TransactionHandler::applyUnsignaledBufferTransaction( std::vector& transactions, TransactionFlushState& flushState) { if (!flushState.queueWithUnsignaledBuffer) { return; } // only apply an unsignaled buffer transaction if it's the first one if (!transactions.empty()) { ATRACE_NAME("fence unsignaled"); return; } auto it = mPendingTransactionQueues.find(flushState.queueWithUnsignaledBuffer); LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mPendingTransactionQueues.end(), "Could not find queue with unsignaled buffer!"); auto& queue = it->second; popTransactionFromPending(transactions, flushState, queue); if (queue.empty()) { it = mPendingTransactionQueues.erase(it); } } void TransactionHandler::popTransactionFromPending(std::vector& transactions, TransactionFlushState& flushState, std::queue& queue) { auto& transaction = queue.front(); // Transaction is ready move it from the pending queue. flushState.firstTransaction = false; removeFromStalledTransactions(transaction.id); transactions.emplace_back(std::move(transaction)); queue.pop(); auto& readyToApplyTransaction = transactions.back(); 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()); } }); } 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::NotReadyUnsignaled: // 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& [applyToken, queue] = *it; 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; } else if (ready == TransactionReadiness::NotReadyUnsignaled) { // We maybe able to latch this transaction if it's the only transaction // ready to be applied. flushState.queueWithUnsignaledBuffer = applyToken; break; } // ready == TransactionReadiness::Ready popTransactionFromPending(transactions, flushState, queue); } 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(uint64_t transactionId, StalledTransactionInfo stalledTransactionInfo) { std::lock_guard lock{mStalledMutex}; mStalledTransactions.emplace(transactionId, std::move(stalledTransactionInfo)); } void TransactionHandler::removeFromStalledTransactions(uint64_t transactionId) { std::lock_guard lock{mStalledMutex}; mStalledTransactions.erase(transactionId); } std::optional TransactionHandler::getStalledTransactionInfo(pid_t pid) { std::lock_guard lock{mStalledMutex}; for (auto [_, stalledTransactionInfo] : mStalledTransactions) { if (pid == stalledTransactionInfo.pid) { return stalledTransactionInfo; } } return std::nullopt; } void TransactionHandler::onLayerDestroyed(uint32_t layerId) { std::lock_guard lock{mStalledMutex}; for (auto it = mStalledTransactions.begin(); it != mStalledTransactions.end();) { if (it->second.layerId == layerId) { it = mStalledTransactions.erase(it); } else { it++; } } } } // namespace android::surfaceflinger::frontend