blob: d3d950974f6f84670aca4db65a94967b211ded2c [file] [log] [blame]
/*
* 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 <cutils/trace.h>
#include <utils/Log.h>
#include <utils/Trace.h>
#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<int>(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<TransactionState> TransactionHandler::flushTransactions() {
// 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);
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<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;
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::StalledTransactionInfo>
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