/* * 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 #ifdef MTK_SF_DEBUG_SUPPORT #include #endif #include #include #include #include "TransactionHandler.h" #ifdef MTK_SF_SCHEDULE_DELAY #include #endif namespace android::surfaceflinger::frontend { #ifdef MTK_SF_DEBUG_SUPPORT static nsecs_t sTransactionDetectThreshold = android::base::GetIntProperty("debug.sf.transaction_detect_threshold", 3000); #endif 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)); } #ifdef MTK_SF_SCHEDULE_DELAY ScheduleHelper::getInstance().resetLocklessCount(); #endif // 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); LOG_ALWAYS_FATAL_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; #ifdef MTK_SF_SCHEDULE_DELAY ScheduleHelper::getInstance().resetPendingTransactionsCount(); #endif auto it = mPendingTransactionQueues.begin(); while (it != mPendingTransactionQueues.end()) { auto& [applyToken, queue] = *it; #ifdef MTK_SF_DEBUG_SUPPORT if (applyToken && !applyToken->isBinderAlive()) { ALOGE("%s applyToken binder not alive", __FUNCTION__); while (!queue.empty()) { queue.pop(); } it = mPendingTransactionQueues.erase(it); continue; } #endif while (!queue.empty()) { auto& transaction = queue.front(); flushState.transaction = &transaction; auto ready = applyFilters(flushState); #ifdef MTK_SF_DEBUG_SUPPORT // check mPendingTransactionQueues const auto _now = systemTime(); if (ready == TransactionReadiness::NotReady || ready == TransactionReadiness::NotReadyBarrier) { const auto _delay = (_now - transaction.postTime) / 1000000; if (_delay > 60*1000 && ready == TransactionReadiness::NotReadyBarrier) { ALOGE("NotReadyB 60s detected %" PRId64 "(ms), raise too many layers.", _delay); base::SetProperty("debug.sf.toomanylayers", "1"); } if (_delay > sTransactionDetectThreshold && _delay % 1000 < 200) { // random show debug log to reduce log // enable log in transactionIsReadyToBeApplied flushState.bTransactionIsReadyToBeAppliedLog = true; if (ready == TransactionReadiness::NotReady) { ALOGD("NotReady transaction is pending for %" PRId64 "(ms)", _delay); } else { ALOGD("NotReadyB transaction is pending for %" PRId64 "(ms)", _delay); } applyFilters(flushState); // disable log in transactionIsReadyToBeApplied flushState.bTransactionIsReadyToBeAppliedLog = false; } } #endif #ifdef MTK_SF_SCHEDULE_DELAY if (ready == TransactionReadiness::NotReady || ready == TransactionReadiness::NotReadyBarrier) { ScheduleHelper::getInstance().increasePendingTransactionsCount(); if (ready == TransactionReadiness::NotReady) { ScheduleHelper::getInstance().checkDelayTimeForPendingTransaction( transaction.isAutoTimestamp, transaction.desiredPresentTime); } } #endif 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() { #ifdef MTK_SF_SCHEDULE_DELAY return ScheduleHelper::getInstance().hasPendingTransactions(); #else return !mPendingTransactionQueues.empty() || !mLocklessTransactionQueue.isEmpty(); #endif } void TransactionHandler::onTransactionQueueStalled(uint64_t transactionId, sp& listener, const std::string& reason) { if (std::find(mStalledTransactions.begin(), mStalledTransactions.end(), transactionId) != mStalledTransactions.end()) { return; } mStalledTransactions.push_back(transactionId); listener->onTransactionQueueStalled(String8(reason.c_str())); } void TransactionHandler::removeFromStalledTransactions(uint64_t id) { auto it = std::find(mStalledTransactions.begin(), mStalledTransactions.end(), id); if (it != mStalledTransactions.end()) { mStalledTransactions.erase(it); } } } // namespace android::surfaceflinger::frontend