276 lines
11 KiB
C++
276 lines
11 KiB
C++
/*
|
|
* 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 <android-base/properties.h>
|
|
#endif
|
|
#include <cutils/trace.h>
|
|
#include <utils/Log.h>
|
|
#include <utils/Trace.h>
|
|
|
|
#include "TransactionHandler.h"
|
|
|
|
#ifdef MTK_SF_SCHEDULE_DELAY
|
|
#include <mediatek/ScheduleHelper.h>
|
|
#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<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));
|
|
}
|
|
|
|
#ifdef MTK_SF_SCHEDULE_DELAY
|
|
ScheduleHelper::getInstance().resetLocklessCount();
|
|
#endif
|
|
|
|
// 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);
|
|
|
|
applyUnsignaledBufferTransaction(transactions, flushState);
|
|
|
|
mPendingTransactionCount.fetch_sub(transactions.size());
|
|
ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
|
|
return transactions;
|
|
}
|
|
|
|
void TransactionHandler::applyUnsignaledBufferTransaction(
|
|
std::vector<TransactionState>& 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<TransactionState>& transactions,
|
|
TransactionFlushState& flushState,
|
|
std::queue<TransactionState>& 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<uint64_t>::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<TransactionState>& 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<ITransactionCompletedListener>& 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
|