Loading libs/gui/TransactionState.cpp +21 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <gui/TransactionState.h> #include <private/gui/ParcelUtils.h> #include <algorithm> #include <numeric> namespace android { Loading Loading @@ -86,6 +87,8 @@ status_t TransactionState::writeToParcel(Parcel* parcel) const { SAFE_PARCEL(d.write, *parcel); } SAFE_PARCEL(parcel->writeParcelableVector, mBarriers); return NO_ERROR; } Loading Loading @@ -142,6 +145,8 @@ status_t TransactionState::readFromParcel(const Parcel* parcel) { mDisplayStates.emplace_back(std::move(d)); } mBarriers.clear(); SAFE_PARCEL(parcel->readParcelableVector, &mBarriers); return NO_ERROR; } Loading Loading @@ -206,6 +211,21 @@ void TransactionState::merge(TransactionState&& other, mMergedTransactionIds.insert(mMergedTransactionIds.begin(), other.mId); mBarriers.insert(mBarriers.end(), std::make_move_iterator(other.mBarriers.begin()), std::make_move_iterator(other.mBarriers.end())); if (mBarriers.size() > MAX_BARRIERS_LENGTH) { int numToRemove = mBarriers.size() - MAX_BARRIERS_LENGTH; std::string droppedBarriers = std::accumulate(mBarriers.begin(), mBarriers.begin() + numToRemove, std::string(), [](std::string&& s, const gui::TransactionBarrier& barrier) -> std::string { s += barrier.toString() + ","; return s; }); ALOGE("Dropping %d transaction barriers: %s", numToRemove, droppedBarriers.c_str()); mBarriers.erase(mBarriers.begin(), mBarriers.begin() + numToRemove); } other.clear(); } Loading Loading @@ -235,6 +255,7 @@ void TransactionState::clear() { mEarlyWakeupInfos.clear(); mComposerStates.clear(); mDisplayStates.clear(); mBarriers.clear(); } layer_state_t* TransactionState::getLayerState(const sp<SurfaceControl>& sc) { Loading libs/gui/aidl/android/gui/TransactionBarrier.aidl 0 → 100644 +28 −0 Original line number Diff line number Diff line /** * Copyright (c) 2025, 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. */ package android.gui; /** @hide */ parcelable TransactionBarrier { String barrierToken; enum BarrierKind { KIND_INVALID, KIND_SIGNAL, KIND_WAIT, } BarrierKind kind; } libs/gui/include/gui/TransactionState.h +6 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #pragma once #include <android/gui/FrameTimelineInfo.h> #include <android/gui/TransactionBarrier.h> #include <binder/Parcelable.h> #include <gui/LayerState.h> Loading Loading @@ -93,8 +94,12 @@ public: std::vector<gui::EarlyWakeupInfo> mEarlyWakeupInfos = {}; std::vector<ComposerState> mComposerStates = {}; std::vector<DisplayState> mDisplayStates = {}; std::vector<gui::TransactionBarrier> mBarriers = {}; // Keep track of the last MAX_BARRIERS_LENGTH transaction barriers. // Ordered most recently merged to least recently merged. static constexpr size_t MAX_BARRIERS_LENGTH = 10u; private: // We keep track of the last MAX_MERGE_HISTORY_LENGTH merged transaction ids. // Ordered most recently merged to least recently merged. static constexpr size_t MAX_MERGE_HISTORY_LENGTH = 10u; Loading libs/gui/tests/TransactionState_test.cpp +57 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <gtest/gtest.h> #include <unordered_map> #include "android/gui/FocusRequest.h" #include "android/gui/TransactionBarrier.h" #include "binder/Binder.h" #include "binder/Parcel.h" #include "gtest/gtest.h" Loading @@ -29,6 +30,8 @@ namespace android { using ::testing::Eq; void sprintf(std::string& out, const char* format, ...) { va_list arg_list; va_start(arg_list, format); Loading Loading @@ -83,11 +86,16 @@ void Compare(const TransactionListenerCallbacks& s1, const TransactionListenerCa EXPECT_EQ(s1.mFlattenedListenerCallbacks, s2.mFlattenedListenerCallbacks); } void Compare(const gui::TransactionBarrier& b1, const gui::TransactionBarrier& b2) { EXPECT_EQ(b1.barrierToken, b2.barrierToken); EXPECT_EQ(b1.kind, b2.kind); } void CompareComplex(const TransactionState& s1, const TransactionState& s2) { EXPECT_EQ(s1.mMergedTransactionIds, s2.mMergedTransactionIds); EXPECT_EQ(s1.mFrameTimelineInfo, s2.mFrameTimelineInfo); EXPECT_EQ(s1.mUncacheBuffers, s2.mUncacheBuffers); EXPECT_EQ(s1.mCallbacks, s2.mCallbacks); Compare(s1.mCallbacks, s2.mCallbacks); EXPECT_EQ(s1.mInputWindowCommands, s2.mInputWindowCommands); EXPECT_EQ(s1.mDisplayStates.size(), s2.mDisplayStates.size()); for (size_t i = 0; i < s1.mDisplayStates.size(); ++i) { Loading @@ -97,6 +105,11 @@ void CompareComplex(const TransactionState& s1, const TransactionState& s2) { for (size_t i = 0; i < s1.mComposerStates.size(); ++i) { EXPECT_EQ(s1.mComposerStates.at(i), s2.mComposerStates.at(i)); } EXPECT_EQ(s1.mBarriers.size(), s2.mBarriers.size()); for (size_t i = 0; i < s1.mBarriers.size(); ++i) { Compare(s1.mBarriers.at(i), s2.mBarriers.at(i)); } } // In case EXPECT_EQ fails, this function is useful to pinpoint exactly which Loading Loading @@ -183,7 +196,6 @@ TransactionState createTransactionStateForTest() { for (size_t i = 0; i < kMaxComposerStates; i++) { state.mComposerStates.push_back(createComposerStateForTest(i)); } return state; return state; } Loading @@ -194,6 +206,22 @@ TransactionState createEmptyTransaction(uint64_t id) { return state; } gui::TransactionBarrier createTransactionBarrier(std::string_view token, gui::TransactionBarrier::BarrierKind kind) { gui::TransactionBarrier barrier; barrier.barrierToken = String16(token.data(), token.size()); barrier.kind = kind; return barrier; } gui::TransactionBarrier createSignalBarrier(std::string_view token) { return createTransactionBarrier(token, gui::TransactionBarrier::BarrierKind::KIND_SIGNAL); } gui::TransactionBarrier createWaitBarrier(std::string_view token) { return createTransactionBarrier(token, gui::TransactionBarrier::BarrierKind::KIND_WAIT); } TEST(SimpleTransactionStateTest, parcel) { TransactionState state = createSimpleTransactionStateForTest(); Parcel p; Loading @@ -206,6 +234,7 @@ TEST(SimpleTransactionStateTest, parcel) { TEST(TransactionStateTest, parcel) { TransactionState state = createTransactionStateForTest(); state.mBarriers.emplace_back(createSignalBarrier("sig")); Parcel p; state.writeToParcel(&p); p.setDataPosition(0); Loading Loading @@ -287,6 +316,7 @@ TEST(TransactionStateTest, merge) { static constexpr uint64_t kUpdateTransactionId = 200; TransactionState state = createTransactionStateForTest(); state.mBarriers.emplace_back(createSignalBarrier("sig")); TransactionState update; update.mId = kUpdateTransactionId; Loading @@ -303,6 +333,8 @@ TEST(TransactionStateTest, merge) { composerState.state.what = layer_state_t::eBufferChanged; update.mComposerStates.push_back(composerState); } update.mBarriers.emplace_back( createTransactionBarrier("wait", gui::TransactionBarrier::BarrierKind::KIND_WAIT)); int32_t overrwiteLayerId = -1; auto updateCopy = update; // Mutation. Loading @@ -322,11 +354,34 @@ TEST(TransactionStateTest, merge) { // desired present time is not merged. expectedMergedState.mDesiredPresentTime = state.mDesiredPresentTime; expectedMergedState.mBarriers.emplace_back(createSignalBarrier("sig")); expectedMergedState.mBarriers.emplace_back(createWaitBarrier("wait")); EXPECT_EQ(state.mComposerStates[0], expectedMergedState.mComposerStates[0]); EXPECT_EQ(state.mInputWindowCommands, expectedMergedState.mInputWindowCommands); EXPECT_EQ(state, expectedMergedState); }; TEST(TransactionStateTest, mergeMoreThanMaxBarriers) { TransactionState state = createTransactionStateForTest(); for (size_t i = 0; i < TransactionState::MAX_BARRIERS_LENGTH; ++i) { std::string token = std::format("sig{}", i); state.mBarriers.emplace_back(createSignalBarrier(token)); } TransactionState update; update.mId = 200; update.mBarriers.emplace_back(createWaitBarrier("wait")); state.merge(std::move(update), [](layer_state_t) {}); EXPECT_EQ(TransactionState::MAX_BARRIERS_LENGTH, state.mBarriers.size()); EXPECT_THAT(state.mBarriers.front().toString(), Eq("TransactionBarrier{barrierToken: sig1, kind: KIND_SIGNAL}")); EXPECT_THAT(state.mBarriers.back().toString(), Eq("TransactionBarrier{barrierToken: wait, kind: KIND_WAIT}")); } TEST(TransactionStateTest, clear) { TransactionState state = createTransactionStateForTest(); state.clear(); Loading Loading
libs/gui/TransactionState.cpp +21 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <gui/TransactionState.h> #include <private/gui/ParcelUtils.h> #include <algorithm> #include <numeric> namespace android { Loading Loading @@ -86,6 +87,8 @@ status_t TransactionState::writeToParcel(Parcel* parcel) const { SAFE_PARCEL(d.write, *parcel); } SAFE_PARCEL(parcel->writeParcelableVector, mBarriers); return NO_ERROR; } Loading Loading @@ -142,6 +145,8 @@ status_t TransactionState::readFromParcel(const Parcel* parcel) { mDisplayStates.emplace_back(std::move(d)); } mBarriers.clear(); SAFE_PARCEL(parcel->readParcelableVector, &mBarriers); return NO_ERROR; } Loading Loading @@ -206,6 +211,21 @@ void TransactionState::merge(TransactionState&& other, mMergedTransactionIds.insert(mMergedTransactionIds.begin(), other.mId); mBarriers.insert(mBarriers.end(), std::make_move_iterator(other.mBarriers.begin()), std::make_move_iterator(other.mBarriers.end())); if (mBarriers.size() > MAX_BARRIERS_LENGTH) { int numToRemove = mBarriers.size() - MAX_BARRIERS_LENGTH; std::string droppedBarriers = std::accumulate(mBarriers.begin(), mBarriers.begin() + numToRemove, std::string(), [](std::string&& s, const gui::TransactionBarrier& barrier) -> std::string { s += barrier.toString() + ","; return s; }); ALOGE("Dropping %d transaction barriers: %s", numToRemove, droppedBarriers.c_str()); mBarriers.erase(mBarriers.begin(), mBarriers.begin() + numToRemove); } other.clear(); } Loading Loading @@ -235,6 +255,7 @@ void TransactionState::clear() { mEarlyWakeupInfos.clear(); mComposerStates.clear(); mDisplayStates.clear(); mBarriers.clear(); } layer_state_t* TransactionState::getLayerState(const sp<SurfaceControl>& sc) { Loading
libs/gui/aidl/android/gui/TransactionBarrier.aidl 0 → 100644 +28 −0 Original line number Diff line number Diff line /** * Copyright (c) 2025, 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. */ package android.gui; /** @hide */ parcelable TransactionBarrier { String barrierToken; enum BarrierKind { KIND_INVALID, KIND_SIGNAL, KIND_WAIT, } BarrierKind kind; }
libs/gui/include/gui/TransactionState.h +6 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #pragma once #include <android/gui/FrameTimelineInfo.h> #include <android/gui/TransactionBarrier.h> #include <binder/Parcelable.h> #include <gui/LayerState.h> Loading Loading @@ -93,8 +94,12 @@ public: std::vector<gui::EarlyWakeupInfo> mEarlyWakeupInfos = {}; std::vector<ComposerState> mComposerStates = {}; std::vector<DisplayState> mDisplayStates = {}; std::vector<gui::TransactionBarrier> mBarriers = {}; // Keep track of the last MAX_BARRIERS_LENGTH transaction barriers. // Ordered most recently merged to least recently merged. static constexpr size_t MAX_BARRIERS_LENGTH = 10u; private: // We keep track of the last MAX_MERGE_HISTORY_LENGTH merged transaction ids. // Ordered most recently merged to least recently merged. static constexpr size_t MAX_MERGE_HISTORY_LENGTH = 10u; Loading
libs/gui/tests/TransactionState_test.cpp +57 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <gtest/gtest.h> #include <unordered_map> #include "android/gui/FocusRequest.h" #include "android/gui/TransactionBarrier.h" #include "binder/Binder.h" #include "binder/Parcel.h" #include "gtest/gtest.h" Loading @@ -29,6 +30,8 @@ namespace android { using ::testing::Eq; void sprintf(std::string& out, const char* format, ...) { va_list arg_list; va_start(arg_list, format); Loading Loading @@ -83,11 +86,16 @@ void Compare(const TransactionListenerCallbacks& s1, const TransactionListenerCa EXPECT_EQ(s1.mFlattenedListenerCallbacks, s2.mFlattenedListenerCallbacks); } void Compare(const gui::TransactionBarrier& b1, const gui::TransactionBarrier& b2) { EXPECT_EQ(b1.barrierToken, b2.barrierToken); EXPECT_EQ(b1.kind, b2.kind); } void CompareComplex(const TransactionState& s1, const TransactionState& s2) { EXPECT_EQ(s1.mMergedTransactionIds, s2.mMergedTransactionIds); EXPECT_EQ(s1.mFrameTimelineInfo, s2.mFrameTimelineInfo); EXPECT_EQ(s1.mUncacheBuffers, s2.mUncacheBuffers); EXPECT_EQ(s1.mCallbacks, s2.mCallbacks); Compare(s1.mCallbacks, s2.mCallbacks); EXPECT_EQ(s1.mInputWindowCommands, s2.mInputWindowCommands); EXPECT_EQ(s1.mDisplayStates.size(), s2.mDisplayStates.size()); for (size_t i = 0; i < s1.mDisplayStates.size(); ++i) { Loading @@ -97,6 +105,11 @@ void CompareComplex(const TransactionState& s1, const TransactionState& s2) { for (size_t i = 0; i < s1.mComposerStates.size(); ++i) { EXPECT_EQ(s1.mComposerStates.at(i), s2.mComposerStates.at(i)); } EXPECT_EQ(s1.mBarriers.size(), s2.mBarriers.size()); for (size_t i = 0; i < s1.mBarriers.size(); ++i) { Compare(s1.mBarriers.at(i), s2.mBarriers.at(i)); } } // In case EXPECT_EQ fails, this function is useful to pinpoint exactly which Loading Loading @@ -183,7 +196,6 @@ TransactionState createTransactionStateForTest() { for (size_t i = 0; i < kMaxComposerStates; i++) { state.mComposerStates.push_back(createComposerStateForTest(i)); } return state; return state; } Loading @@ -194,6 +206,22 @@ TransactionState createEmptyTransaction(uint64_t id) { return state; } gui::TransactionBarrier createTransactionBarrier(std::string_view token, gui::TransactionBarrier::BarrierKind kind) { gui::TransactionBarrier barrier; barrier.barrierToken = String16(token.data(), token.size()); barrier.kind = kind; return barrier; } gui::TransactionBarrier createSignalBarrier(std::string_view token) { return createTransactionBarrier(token, gui::TransactionBarrier::BarrierKind::KIND_SIGNAL); } gui::TransactionBarrier createWaitBarrier(std::string_view token) { return createTransactionBarrier(token, gui::TransactionBarrier::BarrierKind::KIND_WAIT); } TEST(SimpleTransactionStateTest, parcel) { TransactionState state = createSimpleTransactionStateForTest(); Parcel p; Loading @@ -206,6 +234,7 @@ TEST(SimpleTransactionStateTest, parcel) { TEST(TransactionStateTest, parcel) { TransactionState state = createTransactionStateForTest(); state.mBarriers.emplace_back(createSignalBarrier("sig")); Parcel p; state.writeToParcel(&p); p.setDataPosition(0); Loading Loading @@ -287,6 +316,7 @@ TEST(TransactionStateTest, merge) { static constexpr uint64_t kUpdateTransactionId = 200; TransactionState state = createTransactionStateForTest(); state.mBarriers.emplace_back(createSignalBarrier("sig")); TransactionState update; update.mId = kUpdateTransactionId; Loading @@ -303,6 +333,8 @@ TEST(TransactionStateTest, merge) { composerState.state.what = layer_state_t::eBufferChanged; update.mComposerStates.push_back(composerState); } update.mBarriers.emplace_back( createTransactionBarrier("wait", gui::TransactionBarrier::BarrierKind::KIND_WAIT)); int32_t overrwiteLayerId = -1; auto updateCopy = update; // Mutation. Loading @@ -322,11 +354,34 @@ TEST(TransactionStateTest, merge) { // desired present time is not merged. expectedMergedState.mDesiredPresentTime = state.mDesiredPresentTime; expectedMergedState.mBarriers.emplace_back(createSignalBarrier("sig")); expectedMergedState.mBarriers.emplace_back(createWaitBarrier("wait")); EXPECT_EQ(state.mComposerStates[0], expectedMergedState.mComposerStates[0]); EXPECT_EQ(state.mInputWindowCommands, expectedMergedState.mInputWindowCommands); EXPECT_EQ(state, expectedMergedState); }; TEST(TransactionStateTest, mergeMoreThanMaxBarriers) { TransactionState state = createTransactionStateForTest(); for (size_t i = 0; i < TransactionState::MAX_BARRIERS_LENGTH; ++i) { std::string token = std::format("sig{}", i); state.mBarriers.emplace_back(createSignalBarrier(token)); } TransactionState update; update.mId = 200; update.mBarriers.emplace_back(createWaitBarrier("wait")); state.merge(std::move(update), [](layer_state_t) {}); EXPECT_EQ(TransactionState::MAX_BARRIERS_LENGTH, state.mBarriers.size()); EXPECT_THAT(state.mBarriers.front().toString(), Eq("TransactionBarrier{barrierToken: sig1, kind: KIND_SIGNAL}")); EXPECT_THAT(state.mBarriers.back().toString(), Eq("TransactionBarrier{barrierToken: wait, kind: KIND_WAIT}")); } TEST(TransactionStateTest, clear) { TransactionState state = createTransactionStateForTest(); state.clear(); Loading