Merge "SurfaceFlinger: add explicit eEarlyWakeup start and end" into rvc-dev am: 72b3fa467d
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/native/+/11854660
Change-Id: I64fa19d9958b9aebe49b70dfecaf3c0539a75f57
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 5922f3a..83bc069 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -353,6 +353,8 @@
mTransactionNestCount(other.mTransactionNestCount),
mAnimation(other.mAnimation),
mEarlyWakeup(other.mEarlyWakeup),
+ mExplicitEarlyWakeupStart(other.mExplicitEarlyWakeupStart),
+ mExplicitEarlyWakeupEnd(other.mExplicitEarlyWakeupEnd),
mContainsBuffer(other.mContainsBuffer),
mDesiredPresentTime(other.mDesiredPresentTime) {
mDisplayStates = other.mDisplayStates;
@@ -375,6 +377,8 @@
const uint32_t transactionNestCount = parcel->readUint32();
const bool animation = parcel->readBool();
const bool earlyWakeup = parcel->readBool();
+ const bool explicitEarlyWakeupStart = parcel->readBool();
+ const bool explicitEarlyWakeupEnd = parcel->readBool();
const bool containsBuffer = parcel->readBool();
const int64_t desiredPresentTime = parcel->readInt64();
@@ -443,6 +447,8 @@
mTransactionNestCount = transactionNestCount;
mAnimation = animation;
mEarlyWakeup = earlyWakeup;
+ mExplicitEarlyWakeupStart = explicitEarlyWakeupStart;
+ mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd;
mContainsBuffer = containsBuffer;
mDesiredPresentTime = desiredPresentTime;
mDisplayStates = displayStates;
@@ -470,6 +476,8 @@
parcel->writeUint32(mTransactionNestCount);
parcel->writeBool(mAnimation);
parcel->writeBool(mEarlyWakeup);
+ parcel->writeBool(mExplicitEarlyWakeupStart);
+ parcel->writeBool(mExplicitEarlyWakeupEnd);
parcel->writeBool(mContainsBuffer);
parcel->writeInt64(mDesiredPresentTime);
parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
@@ -545,6 +553,8 @@
mContainsBuffer |= other.mContainsBuffer;
mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup;
+ mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart;
+ mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd;
other.clear();
return *this;
}
@@ -559,6 +569,8 @@
mTransactionNestCount = 0;
mAnimation = false;
mEarlyWakeup = false;
+ mExplicitEarlyWakeupStart = false;
+ mExplicitEarlyWakeupEnd = false;
mDesiredPresentTime = -1;
}
@@ -682,9 +694,20 @@
flags |= ISurfaceComposer::eEarlyWakeup;
}
+ // If both mExplicitEarlyWakeupStart and mExplicitEarlyWakeupEnd are set
+ // it is equivalent for none
+ if (mExplicitEarlyWakeupStart && !mExplicitEarlyWakeupEnd) {
+ flags |= ISurfaceComposer::eExplicitEarlyWakeupStart;
+ }
+ if (mExplicitEarlyWakeupEnd && !mExplicitEarlyWakeupStart) {
+ flags |= ISurfaceComposer::eExplicitEarlyWakeupEnd;
+ }
+
mForceSynchronous = false;
mAnimation = false;
mEarlyWakeup = false;
+ mExplicitEarlyWakeupStart = false;
+ mExplicitEarlyWakeupEnd = false;
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
@@ -731,6 +754,14 @@
mEarlyWakeup = true;
}
+void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupStart() {
+ mExplicitEarlyWakeupStart = true;
+}
+
+void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupEnd() {
+ mExplicitEarlyWakeupEnd = true;
+}
+
layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<IBinder>& handle) {
if (mComposerStates.count(handle) == 0) {
// we don't have it, add an initialized layer_state to our list
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index b49fa1b..8d3160a 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -81,12 +81,20 @@
// flags for setTransactionState()
enum {
eSynchronous = 0x01,
- eAnimation = 0x02,
+ eAnimation = 0x02,
- // Indicates that this transaction will likely result in a lot of layers being composed, and
- // thus, SurfaceFlinger should wake-up earlier to avoid missing frame deadlines. In this
- // case SurfaceFlinger will wake up at (sf vsync offset - debug.sf.early_phase_offset_ns)
- eEarlyWakeup = 0x04
+ // DEPRECATED - use eExplicitEarlyWakeup[Start|End]
+ eEarlyWakeup = 0x04,
+
+ // Explicit indication that this transaction and others to follow will likely result in a
+ // lot of layers being composed, and thus, SurfaceFlinger should wake-up earlier to avoid
+ // missing frame deadlines. In this case SurfaceFlinger will wake up at
+ // (sf vsync offset - debug.sf.early_phase_offset_ns). SurfaceFlinger will continue to be
+ // in the early configuration until it receives eExplicitEarlyWakeupEnd. These flags are
+ // expected to be used by WindowManager only and are guarded by
+ // android.permission.ACCESS_SURFACE_FLINGER
+ eExplicitEarlyWakeupStart = 0x08,
+ eExplicitEarlyWakeupEnd = 0x10,
};
enum VsyncSource {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index e981a39..adcb898 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -345,10 +345,12 @@
std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
mListenerCallbacks;
- uint32_t mForceSynchronous = 0;
- uint32_t mTransactionNestCount = 0;
- bool mAnimation = false;
- bool mEarlyWakeup = false;
+ uint32_t mForceSynchronous = 0;
+ uint32_t mTransactionNestCount = 0;
+ bool mAnimation = false;
+ bool mEarlyWakeup = false;
+ bool mExplicitEarlyWakeupStart = false;
+ bool mExplicitEarlyWakeupEnd = false;
// Indicates that the Transaction contains a buffer that should be cached
bool mContainsBuffer = false;
@@ -547,6 +549,8 @@
void setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height);
void setAnimationTransaction();
void setEarlyWakeup();
+ void setExplicitEarlyWakeupStart();
+ void setExplicitEarlyWakeupEnd();
};
status_t clearLayerFrameStats(const sp<IBinder>& token) const;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 9e24f909..730ea8f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -55,13 +55,24 @@
virtual void kernelTimerChanged(bool expired) = 0;
};
-class Scheduler {
+class IPhaseOffsetControl {
+public:
+ virtual ~IPhaseOffsetControl() = default;
+ virtual void setPhaseOffset(scheduler::ConnectionHandle, nsecs_t phaseOffset) = 0;
+};
+
+class Scheduler : public IPhaseOffsetControl {
public:
using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
using ConfigEvent = scheduler::RefreshRateConfigEvent;
// Indicates whether to start the transaction early, or at vsync time.
- enum class TransactionStart { EARLY, NORMAL };
+ enum class TransactionStart {
+ Early, // DEPRECATED. Start the transaction early. Times out on its own
+ EarlyStart, // Start the transaction early and keep this config until EarlyEnd
+ EarlyEnd, // End the early config started at EarlyStart
+ Normal // Start the transaction at the normal time
+ };
Scheduler(impl::EventControlThread::SetVSyncEnabledFunction,
const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
@@ -90,7 +101,7 @@
void onScreenReleased(ConnectionHandle);
// Modifies phase offset in the event thread.
- void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset);
+ void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset) override;
void getDisplayStatInfo(DisplayStatInfo* stats);
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
index 510dc2d..2567c04 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
@@ -31,11 +31,11 @@
namespace android::scheduler {
-VSyncModulator::VSyncModulator(Scheduler& scheduler,
+VSyncModulator::VSyncModulator(IPhaseOffsetControl& phaseOffsetControl,
Scheduler::ConnectionHandle appConnectionHandle,
Scheduler::ConnectionHandle sfConnectionHandle,
const OffsetsConfig& config)
- : mScheduler(scheduler),
+ : mPhaseOffsetControl(phaseOffsetControl),
mAppConnectionHandle(appConnectionHandle),
mSfConnectionHandle(sfConnectionHandle),
mOffsetsConfig(config) {
@@ -51,14 +51,35 @@
}
void VSyncModulator::setTransactionStart(Scheduler::TransactionStart transactionStart) {
- if (transactionStart == Scheduler::TransactionStart::EARLY) {
+ switch (transactionStart) {
+ case Scheduler::TransactionStart::EarlyStart:
+ ALOGW_IF(mExplicitEarlyWakeup, "Already in TransactionStart::EarlyStart");
+ mExplicitEarlyWakeup = true;
+ break;
+ case Scheduler::TransactionStart::EarlyEnd:
+ ALOGW_IF(!mExplicitEarlyWakeup, "Not in TransactionStart::EarlyStart");
+ mExplicitEarlyWakeup = false;
+ break;
+ case Scheduler::TransactionStart::Normal:
+ case Scheduler::TransactionStart::Early:
+ // Non explicit don't change the explicit early wakeup state
+ break;
+ }
+
+ if (mTraceDetailedInfo) {
+ ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup);
+ }
+
+ if (!mExplicitEarlyWakeup &&
+ (transactionStart == Scheduler::TransactionStart::Early ||
+ transactionStart == Scheduler::TransactionStart::EarlyEnd)) {
mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION;
mEarlyTxnStartTime = std::chrono::steady_clock::now();
}
// An early transaction stays an early transaction.
if (transactionStart == mTransactionStart ||
- mTransactionStart == Scheduler::TransactionStart::EARLY) {
+ mTransactionStart == Scheduler::TransactionStart::EarlyEnd) {
return;
}
mTransactionStart = transactionStart;
@@ -67,8 +88,8 @@
void VSyncModulator::onTransactionHandled() {
mTxnAppliedTime = std::chrono::steady_clock::now();
- if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return;
- mTransactionStart = Scheduler::TransactionStart::NORMAL;
+ if (mTransactionStart == Scheduler::TransactionStart::Normal) return;
+ mTransactionStart = Scheduler::TransactionStart::Normal;
updateOffsets();
}
@@ -91,11 +112,10 @@
void VSyncModulator::onRefreshed(bool usedRenderEngine) {
bool updateOffsetsNeeded = false;
- // Apply a 1ms margin to account for potential data races
+ // Apply a margin to account for potential data races
// This might make us stay in early offsets for one
// additional frame but it's better to be conservative here.
- static const constexpr std::chrono::nanoseconds kMargin = 1ms;
- if ((mEarlyTxnStartTime.load() + kMargin) < mTxnAppliedTime.load()) {
+ if ((mEarlyTxnStartTime.load() + MARGIN_FOR_TX_APPLY) < mTxnAppliedTime.load()) {
if (mRemainingEarlyFrameCount > 0) {
mRemainingEarlyFrameCount--;
updateOffsetsNeeded = true;
@@ -121,8 +141,8 @@
const VSyncModulator::Offsets& VSyncModulator::getNextOffsets() const {
// Early offsets are used if we're in the middle of a refresh rate
// change, or if we recently begin a transaction.
- if (mTransactionStart == Scheduler::TransactionStart::EARLY || mRemainingEarlyFrameCount > 0 ||
- mRefreshRateChangePending) {
+ if (mExplicitEarlyWakeup || mTransactionStart == Scheduler::TransactionStart::EarlyEnd ||
+ mRemainingEarlyFrameCount > 0 || mRefreshRateChangePending) {
return mOffsetsConfig.early;
} else if (mRemainingRenderEngineUsageCount > 0) {
return mOffsetsConfig.earlyGl;
@@ -139,8 +159,8 @@
void VSyncModulator::updateOffsetsLocked() {
const Offsets& offsets = getNextOffsets();
- mScheduler.setPhaseOffset(mSfConnectionHandle, offsets.sf);
- mScheduler.setPhaseOffset(mAppConnectionHandle, offsets.app);
+ mPhaseOffsetControl.setPhaseOffset(mSfConnectionHandle, offsets.sf);
+ mPhaseOffsetControl.setPhaseOffset(mAppConnectionHandle, offsets.app);
mOffsets = offsets;
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index d777ef9..ab678c9 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -38,6 +38,9 @@
// switch in and out of gl composition.
static constexpr int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
+ // Margin used to account for potential data races
+ static const constexpr std::chrono::nanoseconds MARGIN_FOR_TX_APPLY = 1ms;
+
public:
// Wrapper for a collection of surfaceflinger/app offsets for a particular
// configuration.
@@ -62,7 +65,7 @@
bool operator!=(const OffsetsConfig& other) const { return !(*this == other); }
};
- VSyncModulator(Scheduler&, ConnectionHandle appConnectionHandle,
+ VSyncModulator(IPhaseOffsetControl&, ConnectionHandle appConnectionHandle,
ConnectionHandle sfConnectionHandle, const OffsetsConfig&);
void setPhaseOffsets(const OffsetsConfig&) EXCLUDES(mMutex);
@@ -91,13 +94,14 @@
Offsets getOffsets() const EXCLUDES(mMutex);
private:
+ friend class VSyncModulatorTest;
// Returns the next offsets that we should be using
const Offsets& getNextOffsets() const REQUIRES(mMutex);
// Updates offsets and persists them into the scheduler framework.
void updateOffsets() EXCLUDES(mMutex);
void updateOffsetsLocked() REQUIRES(mMutex);
- Scheduler& mScheduler;
+ IPhaseOffsetControl& mPhaseOffsetControl;
const ConnectionHandle mAppConnectionHandle;
const ConnectionHandle mSfConnectionHandle;
@@ -107,8 +111,9 @@
Offsets mOffsets GUARDED_BY(mMutex){mOffsetsConfig.late};
std::atomic<Scheduler::TransactionStart> mTransactionStart =
- Scheduler::TransactionStart::NORMAL;
+ Scheduler::TransactionStart::Normal;
std::atomic<bool> mRefreshRateChangePending = false;
+ std::atomic<bool> mExplicitEarlyWakeup = false;
std::atomic<int> mRemainingEarlyFrameCount = 0;
std::atomic<int> mRemainingRenderEngineUsageCount = 0;
std::atomic<std::chrono::steady_clock::time_point> mEarlyTxnStartTime = {};
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 068049d..ef315e2 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3236,7 +3236,7 @@
}
uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) {
- return setTransactionFlags(flags, Scheduler::TransactionStart::NORMAL);
+ return setTransactionFlags(flags, Scheduler::TransactionStart::Normal);
}
uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags,
@@ -3453,15 +3453,36 @@
mForceTraversal = true;
}
+ const auto transactionStart = [](uint32_t flags) {
+ if (flags & eEarlyWakeup) {
+ return Scheduler::TransactionStart::Early;
+ }
+ if (flags & eExplicitEarlyWakeupEnd) {
+ return Scheduler::TransactionStart::EarlyEnd;
+ }
+ if (flags & eExplicitEarlyWakeupStart) {
+ return Scheduler::TransactionStart::EarlyStart;
+ }
+ return Scheduler::TransactionStart::Normal;
+ }(flags);
+
if (transactionFlags) {
if (mInterceptor->isEnabled()) {
mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags);
}
+ // TODO(b/159125966): Remove eEarlyWakeup completly as no client should use this flag
+ if (flags & eEarlyWakeup) {
+ ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
+ }
+
+ if (!privileged && (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
+ ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
+ flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
+ }
+
// this triggers the transaction
- const auto start = (flags & eEarlyWakeup) ? Scheduler::TransactionStart::EARLY
- : Scheduler::TransactionStart::NORMAL;
- setTransactionFlags(transactionFlags, start);
+ setTransactionFlags(transactionFlags, transactionStart);
if (flags & eAnimation) {
mAnimTransactionPending = true;
@@ -3498,6 +3519,13 @@
break;
}
}
+ } else {
+ // even if a transaction is not needed, we need to update VsyncModulator
+ // about explicit early indications
+ if (transactionStart == Scheduler::TransactionStart::EarlyStart ||
+ transactionStart == Scheduler::TransactionStart::EarlyEnd) {
+ mVSyncModulator->setTransactionStart(transactionStart);
+ }
}
}
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 7574ff1..3c4a791 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -62,6 +62,7 @@
"StrongTypingTest.cpp",
"VSyncDispatchTimerQueueTest.cpp",
"VSyncDispatchRealtimeTest.cpp",
+ "VSyncModulatorTest.cpp",
"VSyncPredictorTest.cpp",
"VSyncReactorTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
diff --git a/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp
new file mode 100644
index 0000000..9c1ec07
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include "Scheduler/VSyncModulator.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using namespace testing;
+
+namespace android::scheduler {
+
+class MockScheduler : public IPhaseOffsetControl {
+public:
+ void setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) {
+ mPhaseOffset[handle] = phaseOffset;
+ }
+
+ nsecs_t getOffset(ConnectionHandle handle) { return mPhaseOffset[handle]; }
+
+private:
+ std::unordered_map<ConnectionHandle, nsecs_t> mPhaseOffset;
+};
+
+class VSyncModulatorTest : public testing::Test {
+protected:
+ static constexpr auto MIN_EARLY_FRAME_COUNT_TRANSACTION =
+ VSyncModulator::MIN_EARLY_FRAME_COUNT_TRANSACTION;
+ // Add a 1ms slack to avoid strange timer race conditions.
+ static constexpr auto MARGIN_FOR_TX_APPLY = VSyncModulator::MARGIN_FOR_TX_APPLY + 1ms;
+
+ // Used to enumerate the different offsets we have
+ enum {
+ SF_LATE,
+ APP_LATE,
+ SF_EARLY,
+ APP_EARLY,
+ SF_EARLY_GL,
+ APP_EARLY_GL,
+ };
+
+ std::unique_ptr<VSyncModulator> mVSyncModulator;
+ MockScheduler mMockScheduler;
+ ConnectionHandle mAppConnection{1};
+ ConnectionHandle mSfConnection{2};
+ VSyncModulator::OffsetsConfig mOffsets = {{SF_EARLY, APP_EARLY},
+ {SF_EARLY_GL, APP_EARLY_GL},
+ {SF_LATE, APP_LATE}};
+
+ void SetUp() override {
+ mVSyncModulator = std::make_unique<VSyncModulator>(mMockScheduler, mAppConnection,
+ mSfConnection, mOffsets);
+ mVSyncModulator->setPhaseOffsets(mOffsets);
+
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+ };
+
+ void TearDown() override { mVSyncModulator.reset(); }
+};
+
+TEST_F(VSyncModulatorTest, Normal) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+ }
+}
+
+TEST_F(VSyncModulatorTest, EarlyEnd) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStart) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStartWithEarly) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Early);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStartWithMoreTransactions) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEnd) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEndWithMoreTransactions) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+ std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+ mVSyncModulator->onTransactionHandled();
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+ for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+ }
+
+ mVSyncModulator->onRefreshed(false);
+ EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+ EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+} // namespace android::scheduler