From bf1349c867de1813a8002489fe2a5f5cc958ae17 Mon Sep 17 00:00:00 2001 From: Ady Abraham Date: Fri, 12 Jun 2020 14:26:18 -0700 Subject: SurfaceFlinger: add explicit eEarlyWakeup start and end The current handling of eEarlyWakeup is using 2 frames of timeout in SurfaceFlinger to know when we are no longer in early offset config. There are few cases where a transaction is dropped or delayed (usually caused by the offset change itself) which results in switching back and forth from early offset to non-early offset. This change adds two new flags, eExplicitEarlyWakeupStart and eExplicitEarlyWakeupEnd that will be used by WindowManager to indicate when to enter early offset and when to exit. With these explicit flags, the timings on transaction no longer matters and we consistently stay at early offset config for the desired duration. Bug: 158127834 Test: Quick switch between apps and verify that offset doesn't change Test: adb shell /data/nativetest64/libsurfaceflinger_unittest/libsurfaceflinger_unittest Change-Id: Ie10af30a2d8c0f4f21ac7ffed469a74e1bf8dbc1 --- libs/gui/SurfaceComposerClient.cpp | 31 +++ libs/gui/include/gui/ISurfaceComposer.h | 20 +- libs/gui/include/gui/SurfaceComposerClient.h | 12 +- services/surfaceflinger/Scheduler/Scheduler.h | 17 +- .../surfaceflinger/Scheduler/VSyncModulator.cpp | 46 +++- services/surfaceflinger/Scheduler/VSyncModulator.h | 11 +- services/surfaceflinger/SurfaceFlinger.cpp | 36 ++- services/surfaceflinger/tests/unittests/Android.bp | 1 + .../tests/unittests/VSyncModulatorTest.cpp | 301 +++++++++++++++++++++ 9 files changed, 442 insertions(+), 33 deletions(-) create mode 100644 services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 5922f3a876..83bc06997a 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -353,6 +353,8 @@ SurfaceComposerClient::Transaction::Transaction(const Transaction& other) 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 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel 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 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel mTransactionNestCount = transactionNestCount; mAnimation = animation; mEarlyWakeup = earlyWakeup; + mExplicitEarlyWakeupStart = explicitEarlyWakeupStart; + mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd; mContainsBuffer = containsBuffer; mDesiredPresentTime = desiredPresentTime; mDisplayStates = displayStates; @@ -470,6 +476,8 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const 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(mDisplayStates.size())); @@ -545,6 +553,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr mContainsBuffer |= other.mContainsBuffer; mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup; + mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart; + mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd; other.clear(); return *this; } @@ -559,6 +569,8 @@ void SurfaceComposerClient::Transaction::clear() { mTransactionNestCount = 0; mAnimation = false; mEarlyWakeup = false; + mExplicitEarlyWakeupStart = false; + mExplicitEarlyWakeupEnd = false; mDesiredPresentTime = -1; } @@ -682,9 +694,20 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { 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 applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands, @@ -731,6 +754,14 @@ void SurfaceComposerClient::Transaction::setEarlyWakeup() { mEarlyWakeup = true; } +void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupStart() { + mExplicitEarlyWakeupStart = true; +} + +void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupEnd() { + mExplicitEarlyWakeupEnd = true; +} + layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp& 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 b49fa1baf1..8d3160a815 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -81,12 +81,20 @@ public: // flags for setTransactionState() enum { eSynchronous = 0x01, - 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 + eAnimation = 0x02, + + // 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 e981a39edc..adcb8982a0 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -345,10 +345,12 @@ public: std::unordered_map, 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 @@ public: void setDisplaySize(const sp& token, uint32_t width, uint32_t height); void setAnimationTransaction(); void setEarlyWakeup(); + void setExplicitEarlyWakeupStart(); + void setExplicitEarlyWakeupEnd(); }; status_t clearLayerFrameStats(const sp& token) const; diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 9e24f90933..730ea8f0c8 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -55,13 +55,24 @@ public: 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 @@ public: 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 510dc2d6ac..2567c0430d 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::setPhaseOffsets(const OffsetsConfig& config) { } 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::setTransactionStart(Scheduler::TransactionStart transaction 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::onRefreshRateChangeCompleted() { 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 @@ VSyncModulator::Offsets VSyncModulator::getOffsets() const { 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::updateOffsets() { 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 d777ef99ae..ab678c98c5 100644 --- a/services/surfaceflinger/Scheduler/VSyncModulator.h +++ b/services/surfaceflinger/Scheduler/VSyncModulator.h @@ -38,6 +38,9 @@ private: // 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 @@ public: 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 @@ public: 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 @@ private: Offsets mOffsets GUARDED_BY(mMutex){mOffsetsConfig.late}; std::atomic mTransactionStart = - Scheduler::TransactionStart::NORMAL; + Scheduler::TransactionStart::Normal; std::atomic mRefreshRateChangePending = false; + std::atomic mExplicitEarlyWakeup = false; std::atomic mRemainingEarlyFrameCount = 0; std::atomic mRemainingRenderEngineUsageCount = 0; std::atomic mEarlyTxnStartTime = {}; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index fbfe448cdf..82d8f089ee 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -3235,7 +3235,7 @@ uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) { } 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, @@ -3452,15 +3452,36 @@ void SurfaceFlinger::applyTransactionState( mTraversalNeededMainThread = 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; @@ -3497,6 +3518,13 @@ void SurfaceFlinger::applyTransactionState( 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 7574ff1773..3c4a791a5e 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -62,6 +62,7 @@ cc_test { "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 0000000000..9c1ec07084 --- /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 +#include + +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 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 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(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 -- cgit v1.2.3-59-g8ed1b