diff options
| author | 2020-06-18 04:51:35 +0000 | |
|---|---|---|
| committer | 2020-06-18 04:51:35 +0000 | |
| commit | 482fad245111826edc5ffd3d56e64db607033242 (patch) | |
| tree | fae899abbadfbf001a7287932242fa928d40359a | |
| parent | 5f0d4ca5676b6e32cb1c8a45b3f4025ad4006eaa (diff) | |
| parent | 72b3fa467da003ac78248b6868ed51d2401e0b47 (diff) | |
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: I3f4640ef2a637ca59d72c2655548420ea2baaf5e
| -rw-r--r-- | libs/gui/SurfaceComposerClient.cpp | 31 | ||||
| -rw-r--r-- | libs/gui/include/gui/ISurfaceComposer.h | 20 | ||||
| -rw-r--r-- | libs/gui/include/gui/SurfaceComposerClient.h | 12 | ||||
| -rw-r--r-- | services/surfaceflinger/Scheduler/Scheduler.h | 17 | ||||
| -rw-r--r-- | services/surfaceflinger/Scheduler/VSyncModulator.cpp | 46 | ||||
| -rw-r--r-- | services/surfaceflinger/Scheduler/VSyncModulator.h | 11 | ||||
| -rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 36 | ||||
| -rw-r--r-- | services/surfaceflinger/tests/unittests/Android.bp | 1 | ||||
| -rw-r--r-- | services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp | 301 |
9 files changed, 442 insertions, 33 deletions
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<uint32_t>(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<IBinder> 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<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 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<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 @@ public: 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 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<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 068049dfea..ef315e24c6 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -3236,7 +3236,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, @@ -3453,15 +3453,36 @@ void SurfaceFlinger::applyTransactionState( 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 @@ 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 <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 |