blob: 6c6a487b67af65ad91bf569c3f5e9011dcdf89ed [file] [log] [blame]
Vishnu Nair59f6d2d2022-10-05 16:59:56 -07001/*
2 * Copyright 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// #define LOG_NDEBUG 0
18#undef LOG_TAG
19#define LOG_TAG "TransactionHandler"
20#define ATRACE_TAG ATRACE_TAG_GRAPHICS
21
22#include <cutils/trace.h>
23#include <utils/Log.h>
24
25#include "TransactionHandler.h"
26
27namespace android {
28
29void TransactionHandler::queueTransaction(TransactionState&& state) {
30 mLocklessTransactionQueue.push(std::move(state));
31 mPendingTransactionCount.fetch_add(1);
32 ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
33}
34
35std::vector<TransactionState> TransactionHandler::flushTransactions() {
36 while (!mLocklessTransactionQueue.isEmpty()) {
37 auto maybeTransaction = mLocklessTransactionQueue.pop();
38 if (!maybeTransaction.has_value()) {
39 break;
40 }
41 auto transaction = maybeTransaction.value();
42 mPendingTransactionQueues[transaction.applyToken].emplace(std::move(transaction));
43 }
44
45 // Collect transaction that are ready to be applied.
46 std::vector<TransactionState> transactions;
47 TransactionFlushState flushState;
48 flushState.queueProcessTime = systemTime();
49 // Transactions with a buffer pending on a barrier may be on a different applyToken
50 // than the transaction which satisfies our barrier. In fact this is the exact use case
51 // that the primitive is designed for. This means we may first process
52 // the barrier dependent transaction, determine it ineligible to complete
53 // and then satisfy in a later inner iteration of flushPendingTransactionQueues.
54 // The barrier dependent transaction was eligible to be presented in this frame
55 // but we would have prevented it without case. To fix this we continually
56 // loop through flushPendingTransactionQueues until we perform an iteration
57 // where the number of transactionsPendingBarrier doesn't change. This way
58 // we can continue to resolve dependency chains of barriers as far as possible.
59 int lastTransactionsPendingBarrier = 0;
60 int transactionsPendingBarrier = 0;
61 do {
62 lastTransactionsPendingBarrier = transactionsPendingBarrier;
63 // Collect transactions that are ready to be applied.
64 transactionsPendingBarrier = flushPendingTransactionQueues(transactions, flushState);
65 } while (lastTransactionsPendingBarrier != transactionsPendingBarrier);
66
67 mPendingTransactionCount.fetch_sub(transactions.size());
68 ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
69 return transactions;
70}
71
72TransactionHandler::TransactionReadiness TransactionHandler::applyFilters(
73 TransactionFlushState& flushState) {
74 auto ready = TransactionReadiness::Ready;
75 for (auto& filter : mTransactionReadyFilters) {
76 auto perFilterReady = filter(flushState);
77 switch (perFilterReady) {
78 case TransactionReadiness::NotReady:
79 case TransactionReadiness::NotReadyBarrier:
80 return perFilterReady;
81
82 case TransactionReadiness::ReadyUnsignaled:
83 case TransactionReadiness::ReadyUnsignaledSingle:
84 // If one of the filters allows latching an unsignaled buffer, latch this ready
85 // state.
86 ready = perFilterReady;
87 break;
88 case TransactionReadiness::Ready:
89 continue;
90 }
91 }
92 return ready;
93}
94
95int TransactionHandler::flushPendingTransactionQueues(std::vector<TransactionState>& transactions,
96 TransactionFlushState& flushState) {
97 int transactionsPendingBarrier = 0;
98 auto it = mPendingTransactionQueues.begin();
99 while (it != mPendingTransactionQueues.end()) {
100 auto& queue = it->second;
101 IBinder* queueToken = it->first.get();
102
103 // if we have already flushed a transaction with an unsignaled buffer then stop queue
104 // processing
105 if (std::find(flushState.queuesWithUnsignaledBuffers.begin(),
106 flushState.queuesWithUnsignaledBuffers.end(),
107 queueToken) != flushState.queuesWithUnsignaledBuffers.end()) {
108 continue;
109 }
110
111 while (!queue.empty()) {
112 auto& transaction = queue.front();
113 flushState.transaction = &transaction;
114 auto ready = applyFilters(flushState);
115 if (ready == TransactionReadiness::NotReadyBarrier) {
116 transactionsPendingBarrier++;
117 break;
118 } else if (ready == TransactionReadiness::NotReady) {
119 break;
120 }
121
122 // Transaction is ready move it from the pending queue.
123 flushState.firstTransaction = false;
124 removeFromStalledTransactions(transaction.id);
125 transactions.emplace_back(std::move(transaction));
126 queue.pop();
127
128 // If the buffer is unsignaled, then we don't want to signal other transactions using
129 // the buffer as a barrier.
130 auto& readyToApplyTransaction = transactions.back();
131 if (ready == TransactionReadiness::Ready) {
132 readyToApplyTransaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
133 const bool frameNumberChanged = state.bufferData->flags.test(
134 BufferData::BufferDataChange::frameNumberChanged);
135 if (frameNumberChanged) {
136 flushState.bufferLayersReadyToPresent
137 .emplace_or_replace(state.surface.get(),
138 state.bufferData->frameNumber);
139 } else {
140 // Barrier function only used for BBQ which always includes a frame number.
141 // This value only used for barrier logic.
142 flushState.bufferLayersReadyToPresent
143 .emplace_or_replace(state.surface.get(),
144 std::numeric_limits<uint64_t>::max());
145 }
146 });
147 } else if (ready == TransactionReadiness::ReadyUnsignaledSingle) {
148 // Track queues with a flushed unsingaled buffer.
149 flushState.queuesWithUnsignaledBuffers.emplace_back(queueToken);
150 break;
151 }
152 }
153
154 if (queue.empty()) {
155 it = mPendingTransactionQueues.erase(it);
156 } else {
157 it = std::next(it, 1);
158 }
159 }
160 return transactionsPendingBarrier;
161}
162
163void TransactionHandler::addTransactionReadyFilter(TransactionFilter&& filter) {
164 mTransactionReadyFilters.emplace_back(std::move(filter));
165}
166
167bool TransactionHandler::hasPendingTransactions() {
168 return !mPendingTransactionQueues.empty() || !mLocklessTransactionQueue.isEmpty();
169}
170
171void TransactionHandler::onTransactionQueueStalled(const TransactionState& transaction,
172 sp<ITransactionCompletedListener>& listener) {
173 if (std::find(mStalledTransactions.begin(), mStalledTransactions.end(), transaction.id) !=
174 mStalledTransactions.end()) {
175 return;
176 }
177
178 mStalledTransactions.push_back(transaction.id);
179 listener->onTransactionQueueStalled();
180}
181
182void TransactionHandler::removeFromStalledTransactions(uint64_t id) {
183 auto it = std::find(mStalledTransactions.begin(), mStalledTransactions.end(), id);
184 if (it != mStalledTransactions.end()) {
185 mStalledTransactions.erase(it);
186 }
187}
188} // namespace android