SF: Add TransactionHandler
- migrate transaction queueing and flushing into
a new class and remove dependencies from
other components.
- Add a filter interface for other components to
participate in transactionready logic.
Test: presubmit
Bug: 238781169
Change-Id: Ia4da386cd72058126f6f765adafb9cb4d15b1d2b
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index b911ae7..809c80b 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -194,6 +194,7 @@
"Tracing/TransactionTracing.cpp",
"Tracing/TransactionProtoParser.cpp",
"TransactionCallbackInvoker.cpp",
+ "TransactionHandler.cpp",
"TunnelModeEnabledReporter.cpp",
],
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 4ff86e5..418056c 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -881,7 +881,9 @@
bool mPendingHWCDestroy{false};
- bool backpressureEnabled() { return mDrawingState.flags & layer_state_t::eEnableBackpressure; }
+ bool backpressureEnabled() const {
+ return mDrawingState.flags & layer_state_t::eEnableBackpressure;
+ }
bool setStretchEffect(const StretchEffect& effect);
StretchEffect getStretchEffect() const;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3419721..002a637 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -129,14 +129,11 @@
#include "LayerVector.h"
#include "MutexUtils.h"
#include "NativeWindowSurface.h"
-#include "RefreshRateOverlay.h"
#include "RegionSamplingThread.h"
-#include "Scheduler/DispSyncSource.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VsyncConfiguration.h"
-#include "Scheduler/VsyncController.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlingerProperties.h"
#include "SurfaceInterceptor.h"
@@ -775,6 +772,7 @@
void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
+ addTransactionReadyFilters();
Mutex::Autolock lock(mStateLock);
// Get a RenderEngine for the given display / config (can't fail)
@@ -3691,122 +3689,117 @@
}
}
-int SurfaceFlinger::flushPendingTransactionQueues(
- std::vector<TransactionState>& transactions,
- std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
- bool tryApplyUnsignaled) {
- std::unordered_set<sp<IBinder>, SpHash<IBinder>> applyTokensWithUnsignaledTransactions;
- int transactionsPendingBarrier = 0;
- auto it = mPendingTransactionQueues.begin();
- while (it != mPendingTransactionQueues.end()) {
- auto& [applyToken, transactionQueue] = *it;
- while (!transactionQueue.empty()) {
- // if we are in LatchUnsignaledConfig::AutoSingleLayer
- // then we should have only one applyToken for processing.
- // so we can stop further transactions on this applyToken.
- if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer &&
- !applyTokensWithUnsignaledTransactions.empty()) {
- ATRACE_NAME("stopTransactionProcessing");
- break;
- }
-
- auto& transaction = transactionQueue.front();
- const auto ready =
- transactionIsReadyToBeApplied(transaction, transaction.frameTimelineInfo,
- transaction.isAutoTimestamp,
- TimePoint::fromNs(transaction.desiredPresentTime),
- transaction.originUid, transaction.states,
- bufferLayersReadyToPresent, transactions.size(),
- tryApplyUnsignaled);
- ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
- if (ready == TransactionReadiness::NotReady) {
- setTransactionFlags(eTransactionFlushNeeded);
- break;
- }
- if (ready == TransactionReadiness::NotReadyBarrier) {
- transactionsPendingBarrier++;
- setTransactionFlags(eTransactionFlushNeeded);
- break;
- }
- transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
- const bool frameNumberChanged = state.bufferData->flags.test(
- BufferData::BufferDataChange::frameNumberChanged);
- if (frameNumberChanged) {
- bufferLayersReadyToPresent[state.surface] = state.bufferData->frameNumber;
- } else {
- // Barrier function only used for BBQ which always includes a frame number
- bufferLayersReadyToPresent[state.surface] =
- std::numeric_limits<uint64_t>::max();
- }
- });
- const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled);
- if (appliedUnsignaled) {
- applyTokensWithUnsignaledTransactions.insert(transaction.applyToken);
- }
-
- transactions.emplace_back(std::move(transaction));
- transactionQueue.pop();
- mPendingTransactionCount--;
- ATRACE_INT("TransactionQueue", mPendingTransactionCount.load());
- }
-
- if (transactionQueue.empty()) {
- it = mPendingTransactionQueues.erase(it);
- } else {
- it = std::next(it, 1);
- }
+TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyTimelineCheck(
+ const TransactionHandler::TransactionFlushState& flushState) {
+ using TransactionReadiness = TransactionHandler::TransactionReadiness;
+ const auto& transaction = *flushState.transaction;
+ ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64,
+ transaction.frameTimelineInfo.vsyncId);
+ TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime);
+ // Do not present if the desiredPresentTime has not passed unless it is more than
+ // one second in the future. We ignore timestamps more than 1 second in the future
+ // for stability reasons.
+ if (!transaction.isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime &&
+ desiredPresentTime < mExpectedPresentTime + 1s) {
+ ATRACE_NAME("not current");
+ return TransactionReadiness::NotReady;
}
- return transactionsPendingBarrier;
+
+ if (!mScheduler->isVsyncValid(mExpectedPresentTime, transaction.originUid)) {
+ ATRACE_NAME("!isVsyncValid");
+ return TransactionReadiness::NotReady;
+ }
+
+ // If the client didn't specify desiredPresentTime, use the vsyncId to determine the
+ // expected present time of this transaction.
+ if (transaction.isAutoTimestamp &&
+ frameIsEarly(mExpectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) {
+ ATRACE_NAME("frameIsEarly");
+ return TransactionReadiness::NotReady;
+ }
+ return TransactionReadiness::Ready;
+}
+
+TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck(
+ const TransactionHandler::TransactionFlushState& flushState) {
+ using TransactionReadiness = TransactionHandler::TransactionReadiness;
+ auto ready = TransactionReadiness::Ready;
+ flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const layer_state_t& s) -> bool {
+ sp<Layer> layer = Layer::fromHandle(s.surface).promote();
+ const auto& transaction = *flushState.transaction;
+ // check for barrier frames
+ if (s.bufferData->hasBarrier &&
+ ((layer->getDrawingState().frameNumber) < s.bufferData->barrierFrameNumber)) {
+ const bool willApplyBarrierFrame =
+ flushState.bufferLayersReadyToPresent.contains(s.surface.get()) &&
+ (flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
+ s.bufferData->barrierFrameNumber);
+ if (!willApplyBarrierFrame) {
+ ATRACE_NAME("NotReadyBarrier");
+ ready = TransactionReadiness::NotReadyBarrier;
+ return false;
+ }
+ }
+
+ // If backpressure is enabled and we already have a buffer to commit, keep
+ // the transaction in the queue.
+ const bool hasPendingBuffer =
+ flushState.bufferLayersReadyToPresent.contains(s.surface.get());
+ if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) {
+ ATRACE_NAME("hasPendingBuffer");
+ ready = TransactionReadiness::NotReady;
+ return false;
+ }
+
+ // check fence status
+ const bool allowLatchUnsignaled = shouldLatchUnsignaled(layer, s, transaction.states.size(),
+ flushState.firstTransaction);
+ ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(),
+ allowLatchUnsignaled ? "true" : "false");
+
+ const bool acquireFenceChanged = s.bufferData &&
+ s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
+ s.bufferData->acquireFence;
+ const bool fenceSignaled =
+ (!acquireFenceChanged ||
+ s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled);
+ if (!fenceSignaled) {
+ if (!allowLatchUnsignaled) {
+ ready = TransactionReadiness::NotReady;
+ auto& listener = s.bufferData->releaseBufferListener;
+ if (listener &&
+ (flushState.queueProcessTime - transaction.postTime) >
+ std::chrono::nanoseconds(4s).count()) {
+ mTransactionHandler.onTransactionQueueStalled(transaction, listener);
+ }
+ return false;
+ }
+
+ ready = enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer
+ ? TransactionReadiness::ReadyUnsignaledSingle
+ : TransactionReadiness::ReadyUnsignaled;
+ }
+ return true;
+ });
+ ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
+ return ready;
+}
+
+void SurfaceFlinger::addTransactionReadyFilters() {
+ mTransactionHandler.addTransactionReadyFilter(
+ std::bind(&SurfaceFlinger::transactionReadyTimelineCheck, this, std::placeholders::_1));
+ mTransactionHandler.addTransactionReadyFilter(
+ std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this, std::placeholders::_1));
}
bool SurfaceFlinger::flushTransactionQueues(VsyncId vsyncId) {
// to prevent onHandleDestroyed from being called while the lock is held,
// we must keep a copy of the transactions (specifically the composer
// states) around outside the scope of the lock
- std::vector<TransactionState> transactions;
- // Layer handles that have transactions with buffers that are ready to be applied.
- std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>> bufferLayersReadyToPresent;
+ std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions();
{
Mutex::Autolock _l(mStateLock);
- {
- while (!mLocklessTransactionQueue.isEmpty()) {
- auto maybeTransaction = mLocklessTransactionQueue.pop();
- if (!maybeTransaction.has_value()) {
- break;
- }
- auto transaction = maybeTransaction.value();
- mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
- }
-
- // 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;
- transactionsPendingBarrier =
- flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
- /*tryApplyUnsignaled*/ false);
- } while (lastTransactionsPendingBarrier != transactionsPendingBarrier);
-
- // We collected all transactions that could apply without latching unsignaled buffers.
- // If we are allowing latch unsignaled of some form, now it's the time to go over the
- // transactions that were not applied and try to apply them unsignaled.
- if (enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
- flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
- /*tryApplyUnsignaled*/ true);
- }
-
- return applyTransactions(transactions, vsyncId);
- }
+ return applyTransactions(transactions, vsyncId);
}
}
@@ -3833,7 +3826,7 @@
}
bool SurfaceFlinger::transactionFlushNeeded() {
- return !mPendingTransactionQueues.empty() || !mLocklessTransactionQueue.isEmpty();
+ return mTransactionHandler.hasPendingTransactions();
}
bool SurfaceFlinger::frameIsEarly(TimePoint expectedPresentTime, VsyncId vsyncId) const {
@@ -3859,7 +3852,7 @@
}
bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t& state,
- size_t numStates, size_t totalTXapplied) const {
+ size_t numStates, bool firstTransaction) const {
if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) {
ALOGV("%s: false (LatchUnsignaledConfig::Disabled)", __func__);
return false;
@@ -3878,9 +3871,9 @@
}
if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer) {
- if (totalTXapplied > 0) {
- ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; totalTXapplied=%zu)",
- __func__, totalTXapplied);
+ if (!firstTransaction) {
+ ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; not first transaction)",
+ __func__);
return false;
}
@@ -3904,116 +3897,6 @@
return true;
}
-auto SurfaceFlinger::transactionIsReadyToBeApplied(
- TransactionState& transaction, const FrameTimelineInfo& info, bool isAutoTimestamp,
- TimePoint desiredPresentTime, uid_t originUid, const Vector<ComposerState>& states,
- const std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>&
- bufferLayersReadyToPresent,
- size_t totalTXapplied, bool tryApplyUnsignaled) const -> TransactionReadiness {
- ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64, info.vsyncId);
- // Do not present if the desiredPresentTime has not passed unless it is more than one second
- // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
- if (!isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime &&
- desiredPresentTime < mExpectedPresentTime + 1s) {
- ATRACE_NAME("not current");
- return TransactionReadiness::NotReady;
- }
-
- if (!mScheduler->isVsyncValid(mExpectedPresentTime, originUid)) {
- ATRACE_NAME("!isVsyncValid");
- return TransactionReadiness::NotReady;
- }
-
- // If the client didn't specify desiredPresentTime, use the vsyncId to determine the expected
- // present time of this transaction.
- if (isAutoTimestamp && frameIsEarly(mExpectedPresentTime, VsyncId{info.vsyncId})) {
- ATRACE_NAME("frameIsEarly");
- return TransactionReadiness::NotReady;
- }
-
- bool fenceUnsignaled = false;
- auto queueProcessTime = systemTime();
- for (const ComposerState& state : states) {
- const layer_state_t& s = state.state;
-
- sp<Layer> layer = nullptr;
- if (s.surface) {
- layer = fromHandle(s.surface).promote();
- } else if (s.hasBufferChanges()) {
- ALOGW("Transaction with buffer, but no Layer?");
- continue;
- }
- if (!layer) {
- continue;
- }
-
- if (s.hasBufferChanges() && s.bufferData->hasBarrier &&
- ((layer->getDrawingState().frameNumber) < s.bufferData->barrierFrameNumber)) {
- const bool willApplyBarrierFrame =
- (bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end()) &&
- (bufferLayersReadyToPresent.at(s.surface) >= s.bufferData->barrierFrameNumber);
- if (!willApplyBarrierFrame) {
- ATRACE_NAME("NotReadyBarrier");
- return TransactionReadiness::NotReadyBarrier;
- }
- }
-
- const bool allowLatchUnsignaled = tryApplyUnsignaled &&
- shouldLatchUnsignaled(layer, s, states.size(), totalTXapplied);
- ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(),
- allowLatchUnsignaled ? "true" : "false");
-
- const bool acquireFenceChanged = s.bufferData &&
- s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
- s.bufferData->acquireFence;
- fenceUnsignaled = fenceUnsignaled ||
- (acquireFenceChanged &&
- s.bufferData->acquireFence->getStatus() == Fence::Status::Unsignaled);
-
- if (fenceUnsignaled && !allowLatchUnsignaled) {
- if (!transaction.sentFenceTimeoutWarning &&
- queueProcessTime - transaction.postTime > std::chrono::nanoseconds(4s).count()) {
- transaction.sentFenceTimeoutWarning = true;
- auto listener = s.bufferData->releaseBufferListener;
- if (listener) {
- listener->onTransactionQueueStalled();
- }
- }
-
- ATRACE_NAME("fence unsignaled");
- return TransactionReadiness::NotReady;
- }
-
- if (s.hasBufferChanges()) {
- // If backpressure is enabled and we already have a buffer to commit, keep the
- // transaction in the queue.
- const bool hasPendingBuffer = bufferLayersReadyToPresent.find(s.surface) !=
- bufferLayersReadyToPresent.end();
- if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
- ATRACE_NAME("hasPendingBuffer");
- return TransactionReadiness::NotReady;
- }
- }
- }
- return fenceUnsignaled ? TransactionReadiness::ReadyUnsignaled : TransactionReadiness::Ready;
-}
-
-void SurfaceFlinger::queueTransaction(TransactionState& state) {
- mLocklessTransactionQueue.push(state);
- mPendingTransactionCount++;
- ATRACE_INT("TransactionQueue", mPendingTransactionCount.load());
-
- const auto schedule = [](uint32_t flags) {
- if (flags & eEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
- if (flags & eEarlyWakeupStart) return TransactionSchedule::EarlyStart;
- return TransactionSchedule::Late;
- }(state.flags);
-
- const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
-
- setTransactionFlags(eTransactionFlushNeeded, schedule, state.applyToken, frameHint);
-}
-
status_t SurfaceFlinger::setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
@@ -4064,7 +3947,16 @@
if (mTransactionTracing) {
mTransactionTracing->addQueuedTransaction(state);
}
- queueTransaction(state);
+
+ const auto schedule = [](uint32_t flags) {
+ if (flags & eEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
+ if (flags & eEarlyWakeupStart) return TransactionSchedule::EarlyStart;
+ return TransactionSchedule::Late;
+ }(state.flags);
+
+ const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
+ setTransactionFlags(eTransactionFlushNeeded, schedule, state.applyToken, frameHint);
+ mTransactionHandler.queueTransaction(std::move(state));
return NO_ERROR;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index baaf41e..f4fb8de 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -57,7 +57,6 @@
#include <scheduler/Time.h>
#include <ui/FenceResult.h>
-#include "ClientCache.h"
#include "Display/DisplayMap.h"
#include "Display/PhysicalDisplay.h"
#include "DisplayDevice.h"
@@ -66,19 +65,17 @@
#include "DisplayIdGenerator.h"
#include "Effects/Daltonizer.h"
#include "FlagManager.h"
-#include "FrameTracker.h"
#include "LayerVector.h"
-#include "LocklessQueue.h"
#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/RefreshRateStats.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VsyncModulator.h"
#include "SurfaceFlingerFactory.h"
#include "ThreadContext.h"
-#include "TracedOrdinal.h"
#include "Tracing/LayerTracing.h"
#include "Tracing/TransactionTracing.h"
#include "TransactionCallbackInvoker.h"
+#include "TransactionHandler.h"
#include "TransactionState.h"
#include <atomic>
@@ -341,6 +338,7 @@
friend class RegionSamplingThread;
friend class LayerRenderArea;
friend class LayerTracing;
+ friend class SurfaceComposerAIDL;
// For unit tests
friend class TestableSurfaceFlinger;
@@ -729,11 +727,13 @@
bool flushTransactionQueues(VsyncId) REQUIRES(kMainThreadContext);
// Returns true if there is at least one transaction that needs to be flushed
bool transactionFlushNeeded();
-
- int flushPendingTransactionQueues(
- std::vector<TransactionState>& transactions,
- std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
- bool tryApplyUnsignaled) REQUIRES(mStateLock) REQUIRES(kMainThreadContext);
+ void addTransactionReadyFilters();
+ TransactionHandler::TransactionReadiness transactionReadyTimelineCheck(
+ const TransactionHandler::TransactionFlushState& flushState)
+ REQUIRES(kMainThreadContext);
+ TransactionHandler::TransactionReadiness transactionReadyBufferCheck(
+ const TransactionHandler::TransactionFlushState& flushState)
+ REQUIRES(kMainThreadContext);
uint32_t setClientStateLocked(const FrameTimelineInfo&, ComposerState&,
int64_t desiredPresentTime, bool isAutoTimestamp,
@@ -751,23 +751,9 @@
void commitOffscreenLayers();
- enum class TransactionReadiness {
- NotReady,
- NotReadyBarrier,
- Ready,
- ReadyUnsignaled,
- };
- TransactionReadiness transactionIsReadyToBeApplied(
- TransactionState&, const FrameTimelineInfo&, bool isAutoTimestamp,
- TimePoint desiredPresentTime, uid_t originUid, const Vector<ComposerState>&,
- const std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>&
- bufferLayersReadyToPresent,
- size_t totalTXapplied, bool tryApplyUnsignaled) const REQUIRES(mStateLock)
- REQUIRES(kMainThreadContext);
-
static LatchUnsignaledConfig getLatchUnsignaledConfig();
bool shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t&, size_t numStates,
- size_t totalTXapplied) const;
+ bool firstTransaction) const;
bool applyTransactions(std::vector<TransactionState>& transactions, VsyncId)
REQUIRES(mStateLock);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
@@ -1094,7 +1080,6 @@
status_t CheckTransactCodeCredentials(uint32_t code);
// Add transaction to the Transaction Queue
- void queueTransaction(TransactionState& state);
/*
* Generic Layer Metadata
@@ -1256,10 +1241,6 @@
uint32_t mTexturePoolSize = 0;
std::vector<uint32_t> mTexturePool;
- std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
- mPendingTransactionQueues;
- LocklessQueue<TransactionState> mLocklessTransactionQueue;
- std::atomic<size_t> mPendingTransactionCount = 0;
std::atomic<size_t> mNumLayers = 0;
// to linkToDeath
@@ -1415,7 +1396,7 @@
bool early = false;
} mPowerHintSessionMode;
- friend class SurfaceComposerAIDL;
+ TransactionHandler mTransactionHandler;
};
class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index 872a901..2232bb9 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -15,6 +15,7 @@
*/
#pragma once
+#include <gui/fake/BufferData.h>
#include <layerproto/TransactionProto.h>
#include <utils/RefBase.h>
@@ -43,35 +44,6 @@
TracingLayerCreationArgs args;
};
-// Class which exposes buffer properties from BufferData without holding on to the actual buffer
-// handle.
-class BufferDataStub : public BufferData {
-public:
- BufferDataStub(uint64_t bufferId, uint32_t width, uint32_t height, int32_t pixelFormat,
- uint64_t outUsage)
- : mBufferId(bufferId),
- mWidth(width),
- mHeight(height),
- mPixelFormat(pixelFormat),
- mOutUsage(outUsage) {}
- bool hasBuffer() const override { return mBufferId != 0; }
- bool hasSameBuffer(const BufferData& other) const override {
- return getId() == other.getId() && frameNumber == other.frameNumber;
- }
- uint32_t getWidth() const override { return mWidth; }
- uint32_t getHeight() const override { return mHeight; }
- uint64_t getId() const override { return mBufferId; }
- PixelFormat getPixelFormat() const override { return mPixelFormat; }
- uint64_t getUsage() const override { return mOutUsage; }
-
-private:
- uint64_t mBufferId;
- uint32_t mWidth;
- uint32_t mHeight;
- int32_t mPixelFormat;
- uint64_t mOutUsage;
-};
-
class TransactionProtoParser {
public:
// Utility class to map handles to ids and buffers to buffer properties without pulling
@@ -87,7 +59,7 @@
virtual std::shared_ptr<BufferData> getGraphicData(uint64_t bufferId, uint32_t width,
uint32_t height, int32_t pixelFormat,
uint64_t usage) const {
- return std::make_shared<BufferDataStub>(bufferId, width, height, pixelFormat, usage);
+ return std::make_shared<fake::BufferData>(bufferId, width, height, pixelFormat, usage);
}
virtual void getGraphicBufferPropertiesFromCache(client_cache_t /* cachedBuffer */,
uint64_t* /* outBufferId */,
diff --git a/services/surfaceflinger/TransactionHandler.cpp b/services/surfaceflinger/TransactionHandler.cpp
new file mode 100644
index 0000000..6c6a487
--- /dev/null
+++ b/services/surfaceflinger/TransactionHandler.cpp
@@ -0,0 +1,188 @@
+/*
+ * 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
+
+#include <cutils/trace.h>
+#include <utils/Log.h>
+
+#include "TransactionHandler.h"
+
+namespace android {
+
+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));
+ }
+
+ // 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);
+
+ mPendingTransactionCount.fetch_sub(transactions.size());
+ ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
+ return transactions;
+}
+
+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::ReadyUnsignaled:
+ case TransactionReadiness::ReadyUnsignaledSingle:
+ // 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& queue = it->second;
+ IBinder* queueToken = it->first.get();
+
+ // if we have already flushed a transaction with an unsignaled buffer then stop queue
+ // processing
+ if (std::find(flushState.queuesWithUnsignaledBuffers.begin(),
+ flushState.queuesWithUnsignaledBuffers.end(),
+ queueToken) != flushState.queuesWithUnsignaledBuffers.end()) {
+ continue;
+ }
+
+ 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;
+ }
+
+ // Transaction is ready move it from the pending queue.
+ flushState.firstTransaction = false;
+ removeFromStalledTransactions(transaction.id);
+ transactions.emplace_back(std::move(transaction));
+ queue.pop();
+
+ // If the buffer is unsignaled, then we don't want to signal other transactions using
+ // the buffer as a barrier.
+ auto& readyToApplyTransaction = transactions.back();
+ if (ready == TransactionReadiness::Ready) {
+ 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());
+ }
+ });
+ } else if (ready == TransactionReadiness::ReadyUnsignaledSingle) {
+ // Track queues with a flushed unsingaled buffer.
+ flushState.queuesWithUnsignaledBuffers.emplace_back(queueToken);
+ break;
+ }
+ }
+
+ 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(const TransactionState& transaction,
+ sp<ITransactionCompletedListener>& listener) {
+ if (std::find(mStalledTransactions.begin(), mStalledTransactions.end(), transaction.id) !=
+ mStalledTransactions.end()) {
+ return;
+ }
+
+ mStalledTransactions.push_back(transaction.id);
+ listener->onTransactionQueueStalled();
+}
+
+void TransactionHandler::removeFromStalledTransactions(uint64_t id) {
+ auto it = std::find(mStalledTransactions.begin(), mStalledTransactions.end(), id);
+ if (it != mStalledTransactions.end()) {
+ mStalledTransactions.erase(it);
+ }
+}
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/TransactionHandler.h b/services/surfaceflinger/TransactionHandler.h
new file mode 100644
index 0000000..237b48d
--- /dev/null
+++ b/services/surfaceflinger/TransactionHandler.h
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <semaphore.h>
+#include <cstdint>
+#include <mutex>
+#include <queue>
+#include <thread>
+#include <vector>
+
+#include <LocklessQueue.h>
+#include <TransactionState.h>
+#include <android-base/thread_annotations.h>
+#include <ftl/small_map.h>
+#include <ftl/small_vector.h>
+
+namespace android {
+class TransactionHandler {
+public:
+ struct TransactionFlushState {
+ const TransactionState* transaction;
+ bool firstTransaction = true;
+ nsecs_t queueProcessTime = 0;
+ // Layer handles that have transactions with buffers that are ready to be applied.
+ ftl::SmallMap<IBinder* /* binder address */, uint64_t /* framenumber */, 15>
+ bufferLayersReadyToPresent = {};
+ ftl::SmallVector<IBinder* /* queueToken */, 15> queuesWithUnsignaledBuffers;
+ };
+ enum class TransactionReadiness {
+ NotReady,
+ NotReadyBarrier,
+ Ready,
+ ReadyUnsignaled,
+ ReadyUnsignaledSingle,
+ };
+ using TransactionFilter = std::function<TransactionReadiness(const TransactionFlushState&)>;
+
+ bool hasPendingTransactions();
+ std::vector<TransactionState> flushTransactions();
+ void addTransactionReadyFilter(TransactionFilter&&);
+ void queueTransaction(TransactionState&&);
+ void onTransactionQueueStalled(const TransactionState&, sp<ITransactionCompletedListener>&);
+ void removeFromStalledTransactions(uint64_t transactionId);
+
+private:
+ // For unit tests
+ friend class TestableSurfaceFlinger;
+
+ int flushPendingTransactionQueues(std::vector<TransactionState>&, TransactionFlushState&);
+ TransactionReadiness applyFilters(TransactionFlushState&);
+ std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
+ mPendingTransactionQueues;
+ LocklessQueue<TransactionState> mLocklessTransactionQueue;
+ std::atomic<size_t> mPendingTransactionCount = 0;
+ ftl::SmallVector<TransactionFilter, 2> mTransactionReadyFilters;
+ std::vector<uint64_t> mStalledTransactions;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 95eb503..3cbfe81 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -64,6 +64,15 @@
}
}
+ template <typename Visitor>
+ void traverseStatesWithBuffersWhileTrue(Visitor&& visitor) const {
+ for (const auto& [state] : states) {
+ if (state.hasBufferChanges() && state.hasValidBuffer() && state.surface) {
+ if (!visitor(state)) return;
+ }
+ }
+ }
+
// TODO(b/185535769): Remove FrameHint. Instead, reset the idle timer (of the relevant physical
// display) on the main thread if commit leads to composite. Then, RefreshRateOverlay should be
// able to setFrameRate once, rather than for each transaction.
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 370b23c..49dd80e 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -728,8 +728,10 @@
return mFlinger->setPowerModeInternal(display, mode);
}
- auto &getTransactionQueue() { return mFlinger->mLocklessTransactionQueue; }
- auto &getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
+ auto &getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; }
+ auto &getPendingTransactionQueue() {
+ return mFlinger->mTransactionHandler.mPendingTransactionQueues;
+ }
auto setTransactionState(
const FrameTimelineInfo &frameTimelineInfo, const Vector<ComposerState> &states,
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index e1a0eeb..7b319f5 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -417,8 +417,13 @@
return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
}
- auto& getTransactionQueue() { return mFlinger->mLocklessTransactionQueue; }
- auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
+ auto& getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; }
+ auto& getPendingTransactionQueue() {
+ return mFlinger->mTransactionHandler.mPendingTransactionQueues;
+ }
+ size_t getPendingTransactionCount() {
+ return mFlinger->mTransactionHandler.mPendingTransactionCount.load();
+ }
auto setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index b4030b3..db438b7 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
#undef LOG_TAG
#define LOG_TAG "CompositionTest"
@@ -22,12 +21,17 @@
#include <compositionengine/mock/DisplaySurface.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <gui/LayerState.h>
#include <gui/SurfaceComposerClient.h>
+#include <gui/fake/BufferData.h>
#include <log/log.h>
#include <ui/MockFence.h>
#include <utils/String8.h>
+#include <vector>
+#include <binder/Binder.h>
#include "TestableSurfaceFlinger.h"
+#include "TransactionHandler.h"
#include "mock/MockEventThread.h"
#include "mock/MockVsyncController.h"
@@ -78,6 +82,7 @@
mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController),
std::unique_ptr<mock::VSyncTracker>(mVSyncTracker),
std::move(eventThread), std::move(sfEventThread));
+ mFlinger.flinger()->addTransactionReadyFilters();
}
TestableSurfaceFlinger mFlinger;
@@ -314,7 +319,10 @@
ComposerState createComposerState(int layerId, sp<Fence> fence, uint64_t what) {
ComposerState state;
- state.state.bufferData = std::make_shared<BufferData>();
+ state.state.bufferData =
+ std::make_shared<fake::BufferData>(/* bufferId */ 123L, /* width */ 1,
+ /* height */ 2, /* pixelFormat */ 0,
+ /* outUsage */ 0);
state.state.bufferData->acquireFence = std::move(fence);
state.state.layerId = layerId;
state.state.surface =
@@ -361,7 +369,7 @@
}
mFlinger.flushTransactionQueues();
EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
- EXPECT_EQ(expectedTransactionsPending, mFlinger.getPendingTransactionQueue().size());
+ EXPECT_EQ(expectedTransactionsPending, mFlinger.getPendingTransactionCount());
}
};
@@ -413,7 +421,9 @@
{
createComposerState(kLayerId,
fence(Fence::Status::Unsignaled),
- layer_state_t::eCropChanged),
+ layer_state_t::eCropChanged |
+ layer_state_t::
+ eBufferChanged),
});
setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
}
@@ -536,41 +546,6 @@
kExpectedTransactionsPending);
}
-TEST_F(LatchUnsignaledAutoSingleLayerTest, UnsignaledNotAppliedWhenThereAreSignaled_SignaledFirst) {
- const sp<IBinder> kApplyToken1 =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
- const sp<IBinder> kApplyToken3 = sp<BBinder>::make();
- const auto kLayerId1 = 1;
- const auto kLayerId2 = 2;
- const auto kExpectedTransactionsPending = 1u;
-
- const auto signaledTransaction =
- createTransactionInfo(kApplyToken1,
- {
- createComposerState(kLayerId1,
- fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged),
- });
- const auto signaledTransaction2 =
- createTransactionInfo(kApplyToken2,
- {
- createComposerState(kLayerId1,
- fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged),
- });
- const auto unsignaledTransaction =
- createTransactionInfo(kApplyToken3,
- {
- createComposerState(kLayerId2,
- fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged),
- });
-
- setTransactionStates({signaledTransaction, signaledTransaction2, unsignaledTransaction},
- kExpectedTransactionsPending);
-}
-
TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsTransactionInTheQueueSameApplyToken) {
const sp<IBinder> kApplyToken =
IInterface::asBinder(TransactionCompletedListener::getIInstance());
@@ -798,7 +773,7 @@
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId1 = 1;
const auto kLayerId2 = 2;
- const auto kExpectedTransactionsPending = 1u;
+ const auto kExpectedTransactionsPending = 2u;
const auto unsignaledTransaction =
createTransactionInfo(kApplyToken,
@@ -1004,4 +979,16 @@
setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
}
+TEST(TransactionHandlerTest, QueueTransaction) {
+ TransactionHandler handler;
+ TransactionState transaction;
+ transaction.applyToken = sp<BBinder>::make();
+ transaction.id = 42;
+ handler.queueTransaction(std::move(transaction));
+ std::vector<TransactionState> transactionsReadyToBeApplied = handler.flushTransactions();
+
+ EXPECT_EQ(transactionsReadyToBeApplied.size(), 1u);
+ EXPECT_EQ(transactionsReadyToBeApplied.front().id, 42u);
+}
+
} // namespace android