Skip to content

Commit cecfd02

Browse files
Treehugger RobotAndroid (Google) Code Review
authored andcommitted
Merge "Introduce TransactionState." into main
2 parents 6925c95 + 15bfd3d commit cecfd02

5 files changed

Lines changed: 643 additions & 0 deletions

File tree

libs/gui/Android.bp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ filegroup {
281281
"SurfaceControl.cpp",
282282
"SurfaceComposerClient.cpp",
283283
"SyncFeatures.cpp",
284+
"TransactionState.cpp",
284285
"VsyncEventData.cpp",
285286
"view/Surface.cpp",
286287
"WindowInfosListenerReporter.cpp",

libs/gui/TransactionState.cpp

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
/*
2+
* Copyright (C) 2025 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_TAG "TransactionState"
18+
#include <gui/LayerState.h>
19+
#include <gui/SurfaceComposerClient.h>
20+
#include <gui/TransactionState.h>
21+
#include <private/gui/ParcelUtils.h>
22+
#include <algorithm>
23+
24+
namespace android {
25+
26+
status_t TransactionState::writeToParcel(Parcel* parcel) const {
27+
SAFE_PARCEL(parcel->writeUint64, mId);
28+
SAFE_PARCEL(parcel->writeUint32, mFlags);
29+
SAFE_PARCEL(parcel->writeInt64, mDesiredPresentTime);
30+
SAFE_PARCEL(parcel->writeBool, mIsAutoTimestamp);
31+
SAFE_PARCEL(parcel->writeParcelable, mFrameTimelineInfo);
32+
SAFE_PARCEL(parcel->writeStrongBinder, mApplyToken);
33+
SAFE_PARCEL(parcel->writeBool, mMayContainBuffer);
34+
SAFE_PARCEL(parcel->writeBool, mLogCallPoints);
35+
36+
SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mDisplayStates.size()));
37+
for (auto const& displayState : mDisplayStates) {
38+
displayState.write(*parcel);
39+
}
40+
SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mComposerStates.size()));
41+
for (auto const& composerState : mComposerStates) {
42+
composerState.write(*parcel);
43+
}
44+
45+
mInputWindowCommands.write(*parcel);
46+
SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mUncacheBuffers.size()));
47+
for (const client_cache_t& uncacheBuffer : mUncacheBuffers) {
48+
SAFE_PARCEL(parcel->writeStrongBinder, uncacheBuffer.token.promote());
49+
SAFE_PARCEL(parcel->writeUint64, uncacheBuffer.id);
50+
}
51+
52+
SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mMergedTransactionIds.size()));
53+
for (auto mergedTransactionId : mMergedTransactionIds) {
54+
SAFE_PARCEL(parcel->writeUint64, mergedTransactionId);
55+
}
56+
57+
SAFE_PARCEL(parcel->writeBool, mHasListenerCallbacks);
58+
SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mListenerCallbacks.size()));
59+
for (const auto& [listener, callbackIds] : mListenerCallbacks) {
60+
SAFE_PARCEL(parcel->writeStrongBinder, listener);
61+
SAFE_PARCEL(parcel->writeParcelableVector, callbackIds);
62+
}
63+
64+
return NO_ERROR;
65+
}
66+
67+
status_t TransactionState::readFromParcel(const Parcel* parcel) {
68+
SAFE_PARCEL(parcel->readUint64, &mId);
69+
SAFE_PARCEL(parcel->readUint32, &mFlags);
70+
SAFE_PARCEL(parcel->readInt64, &mDesiredPresentTime);
71+
SAFE_PARCEL(parcel->readBool, &mIsAutoTimestamp);
72+
SAFE_PARCEL(parcel->readParcelable, &mFrameTimelineInfo);
73+
SAFE_PARCEL(parcel->readNullableStrongBinder, &mApplyToken);
74+
SAFE_PARCEL(parcel->readBool, &mMayContainBuffer);
75+
SAFE_PARCEL(parcel->readBool, &mLogCallPoints);
76+
77+
uint32_t count;
78+
SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
79+
mDisplayStates.clear();
80+
mDisplayStates.reserve(count);
81+
for (size_t i = 0; i < count; i++) {
82+
DisplayState displayState;
83+
if (displayState.read(*parcel) == BAD_VALUE) {
84+
return BAD_VALUE;
85+
}
86+
mDisplayStates.emplace_back(std::move(displayState));
87+
}
88+
89+
SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
90+
mComposerStates.clear();
91+
mComposerStates.reserve(count);
92+
for (size_t i = 0; i < count; i++) {
93+
ComposerState composerState;
94+
if (composerState.read(*parcel) == BAD_VALUE) {
95+
return BAD_VALUE;
96+
}
97+
mComposerStates.emplace_back(std::move(composerState));
98+
}
99+
100+
if (status_t status = mInputWindowCommands.read(*parcel) != NO_ERROR) {
101+
return status;
102+
}
103+
104+
SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
105+
mUncacheBuffers.clear();
106+
mUncacheBuffers.reserve(count);
107+
for (size_t i = 0; i < count; i++) {
108+
client_cache_t client_cache;
109+
sp<IBinder> tmpBinder;
110+
SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
111+
client_cache.token = tmpBinder;
112+
SAFE_PARCEL(parcel->readUint64, &client_cache.id);
113+
mUncacheBuffers.emplace_back(std::move(client_cache));
114+
}
115+
116+
SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
117+
mMergedTransactionIds.clear();
118+
mMergedTransactionIds.resize(count);
119+
for (size_t i = 0; i < count; i++) {
120+
SAFE_PARCEL(parcel->readUint64, &mMergedTransactionIds[i]);
121+
}
122+
123+
SAFE_PARCEL(parcel->readBool, &mHasListenerCallbacks);
124+
SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize());
125+
mListenerCallbacks.clear();
126+
mListenerCallbacks.reserve(count);
127+
for (uint32_t i = 0; i < count; i++) {
128+
sp<IBinder> tmpBinder;
129+
SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
130+
std::vector<CallbackId> callbackIds;
131+
SAFE_PARCEL(parcel->readParcelableVector, &callbackIds);
132+
mListenerCallbacks.emplace_back(tmpBinder, callbackIds);
133+
}
134+
135+
return NO_ERROR;
136+
}
137+
138+
void TransactionState::merge(TransactionState&& other,
139+
const std::function<void(layer_state_t&)>& onBufferOverwrite) {
140+
while (mMergedTransactionIds.size() + other.mMergedTransactionIds.size() >
141+
MAX_MERGE_HISTORY_LENGTH - 1 &&
142+
mMergedTransactionIds.size() > 0) {
143+
mMergedTransactionIds.pop_back();
144+
}
145+
if (other.mMergedTransactionIds.size() == MAX_MERGE_HISTORY_LENGTH) {
146+
mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
147+
other.mMergedTransactionIds.begin(),
148+
other.mMergedTransactionIds.end() - 1);
149+
} else if (other.mMergedTransactionIds.size() > 0u) {
150+
mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
151+
other.mMergedTransactionIds.begin(),
152+
other.mMergedTransactionIds.end());
153+
}
154+
mMergedTransactionIds.insert(mMergedTransactionIds.begin(), other.mId);
155+
156+
for (auto const& otherState : other.mComposerStates) {
157+
if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(),
158+
[&otherState](const auto& composerState) {
159+
return composerState.state.surface ==
160+
otherState.state.surface;
161+
});
162+
it != mComposerStates.end()) {
163+
if (otherState.state.what & layer_state_t::eBufferChanged) {
164+
onBufferOverwrite(it->state);
165+
}
166+
it->state.merge(otherState.state);
167+
} else {
168+
mComposerStates.push_back(otherState);
169+
}
170+
}
171+
172+
for (auto const& state : other.mDisplayStates) {
173+
if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(),
174+
[&state](const auto& displayState) {
175+
return displayState.token == state.token;
176+
});
177+
it != mDisplayStates.end()) {
178+
it->merge(state);
179+
} else {
180+
mDisplayStates.push_back(state);
181+
}
182+
}
183+
184+
for (const auto& cacheId : other.mUncacheBuffers) {
185+
mUncacheBuffers.push_back(cacheId);
186+
}
187+
188+
mInputWindowCommands.merge(other.mInputWindowCommands);
189+
// TODO(b/385156191) Consider merging desired present time.
190+
mFlags |= other.mFlags;
191+
mMayContainBuffer |= other.mMayContainBuffer;
192+
mLogCallPoints |= other.mLogCallPoints;
193+
194+
// mApplyToken is explicitly not merged. Token should be set before applying the transactions to
195+
// make synchronization decisions a bit simpler.
196+
mergeFrameTimelineInfo(other.mFrameTimelineInfo);
197+
other.clear();
198+
}
199+
200+
// copied from FrameTimelineInfo::merge()
201+
void TransactionState::mergeFrameTimelineInfo(const FrameTimelineInfo& other) {
202+
// When merging vsync Ids we take the oldest valid one
203+
if (mFrameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID &&
204+
other.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
205+
if (other.vsyncId > mFrameTimelineInfo.vsyncId) {
206+
mFrameTimelineInfo = other;
207+
}
208+
} else if (mFrameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
209+
mFrameTimelineInfo = other;
210+
}
211+
}
212+
213+
void TransactionState::clear() {
214+
mComposerStates.clear();
215+
mDisplayStates.clear();
216+
mListenerCallbacks.clear();
217+
mHasListenerCallbacks = false;
218+
mInputWindowCommands.clear();
219+
mUncacheBuffers.clear();
220+
mDesiredPresentTime = 0;
221+
mIsAutoTimestamp = true;
222+
mApplyToken = nullptr;
223+
mFrameTimelineInfo = {};
224+
mMergedTransactionIds.clear();
225+
mFlags = 0;
226+
mMayContainBuffer = false;
227+
mLogCallPoints = false;
228+
}
229+
230+
layer_state_t* TransactionState::getLayerState(const sp<SurfaceControl>& sc) {
231+
auto handle = sc->getLayerStateHandle();
232+
if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(),
233+
[&handle](const auto& composerState) {
234+
return composerState.state.surface == handle;
235+
});
236+
it != mComposerStates.end()) {
237+
return &it->state;
238+
}
239+
240+
// we don't have it, add an initialized layer_state to our list
241+
ComposerState s;
242+
s.state.surface = handle;
243+
s.state.layerId = sc->getLayerId();
244+
mComposerStates.push_back(s);
245+
246+
return &mComposerStates.back().state;
247+
}
248+
249+
DisplayState& TransactionState::getDisplayState(const sp<IBinder>& token) {
250+
if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(),
251+
[token](const auto& display) { return display.token == token; });
252+
it != mDisplayStates.end()) {
253+
return *it;
254+
}
255+
256+
// If display state doesn't exist, add a new one.
257+
DisplayState s;
258+
s.token = token;
259+
mDisplayStates.push_back(s);
260+
return mDisplayStates.back();
261+
}
262+
263+
}; // namespace android
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright (C) 2025 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+
#pragma once
18+
19+
#include <android/gui/FrameTimelineInfo.h>
20+
#include <binder/Parcelable.h>
21+
#include <gui/LayerState.h>
22+
23+
namespace android {
24+
25+
// Class to store all the transaction data and the parcelling logic
26+
class TransactionState {
27+
public:
28+
explicit TransactionState() = default;
29+
TransactionState(TransactionState const& other) = default;
30+
status_t writeToParcel(Parcel* parcel) const;
31+
status_t readFromParcel(const Parcel* parcel);
32+
layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
33+
DisplayState& getDisplayState(const sp<IBinder>& token);
34+
35+
// Returns the current id of the transaction.
36+
// The id is updated every time the transaction is applied.
37+
uint64_t getId() const { return mId; }
38+
std::vector<uint64_t> getMergedTransactionIds() const { return mMergedTransactionIds; }
39+
void enableDebugLogCallPoints() { mLogCallPoints = true; }
40+
void merge(TransactionState&& other,
41+
const std::function<void(layer_state_t&)>& onBufferOverwrite);
42+
43+
// copied from FrameTimelineInfo::merge()
44+
void mergeFrameTimelineInfo(const FrameTimelineInfo& other);
45+
void clear();
46+
bool operator==(const TransactionState& rhs) const = default;
47+
bool operator!=(const TransactionState& rhs) const = default;
48+
49+
uint64_t mId = 0;
50+
std::vector<uint64_t> mMergedTransactionIds;
51+
uint32_t mFlags = 0;
52+
// The vsync id provided by Choreographer.getVsyncId and the input event id
53+
gui::FrameTimelineInfo mFrameTimelineInfo;
54+
// mDesiredPresentTime is the time in nanoseconds that the client would like the transaction
55+
// to be presented. When it is not possible to present at exactly that time, it will be
56+
// presented after the time has passed.
57+
//
58+
// If the client didn't pass a desired presentation time, mDesiredPresentTime will be
59+
// populated to the time setBuffer was called, and mIsAutoTimestamp will be set to true.
60+
//
61+
// Desired present times that are more than 1 second in the future may be ignored.
62+
// When a desired present time has already passed, the transaction will be presented as soon
63+
// as possible.
64+
//
65+
// Transactions from the same process are presented in the same order that they are applied.
66+
// The desired present time does not affect this ordering.
67+
int64_t mDesiredPresentTime = 0;
68+
bool mIsAutoTimestamp = true;
69+
// If not null, transactions will be queued up using this token otherwise a common token
70+
// per process will be used.
71+
sp<IBinder> mApplyToken;
72+
// Indicates that the Transaction may contain buffers that should be cached. The reason this
73+
// is only a guess is that buffers can be removed before cache is called. This is only a
74+
// hint that at some point a buffer was added to this transaction before apply was called.
75+
bool mMayContainBuffer = false;
76+
// Prints debug logs when enabled.
77+
bool mLogCallPoints = false;
78+
79+
std::vector<DisplayState> mDisplayStates;
80+
std::vector<ComposerState> mComposerStates;
81+
InputWindowCommands mInputWindowCommands;
82+
std::vector<client_cache_t> mUncacheBuffers;
83+
// Note: mHasListenerCallbacks can be true even if mListenerCallbacks is
84+
// empty.
85+
bool mHasListenerCallbacks = false;
86+
std::vector<ListenerCallbacks> mListenerCallbacks;
87+
88+
private:
89+
// We keep track of the last MAX_MERGE_HISTORY_LENGTH merged transaction ids.
90+
// Ordered most recently merged to least recently merged.
91+
static constexpr size_t MAX_MERGE_HISTORY_LENGTH = 10u;
92+
};
93+
94+
}; // namespace android

libs/gui/tests/Android.bp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ cc_test {
9090
"testserver/TestServerClient.cpp",
9191
"testserver/TestServerHost.cpp",
9292
"TextureRenderer.cpp",
93+
"TransactionState_test.cpp",
9394
"VsyncEventData_test.cpp",
9495
"WindowInfo_test.cpp",
9596
],

0 commit comments

Comments
 (0)