/* * Copyright 2019 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. */ // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wextra" #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" #define LOG_NDEBUG 0 #include "Scheduler/TimeKeeper.h" #include "Scheduler/VSyncDispatch.h" #include "Scheduler/VSyncReactor.h" #include "Scheduler/VSyncTracker.h" #include #include #include #include #include using namespace testing; using namespace std::literals; namespace android::scheduler { class MockVSyncTracker : public VSyncTracker { public: MockVSyncTracker() { ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true)); } MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t)); MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t)); MOCK_CONST_METHOD0(currentPeriod, nsecs_t()); MOCK_METHOD1(setPeriod, void(nsecs_t)); MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; class MockClock : public Clock { public: MOCK_CONST_METHOD0(now, nsecs_t()); }; class ClockWrapper : public Clock { public: ClockWrapper(std::shared_ptr const& clock) : mClock(clock) {} nsecs_t now() const { return mClock->now(); } private: std::shared_ptr const mClock; }; class MockVSyncDispatch : public VSyncDispatch { public: MOCK_METHOD2(registerCallback, CallbackToken(std::function const&, std::string)); MOCK_METHOD1(unregisterCallback, void(CallbackToken)); MOCK_METHOD2(schedule, ScheduleResult(CallbackToken, ScheduleTiming)); MOCK_METHOD1(cancel, CancelResult(CallbackToken token)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; std::shared_ptr generateInvalidFence() { sp fence = new Fence(); return std::make_shared(fence); } std::shared_ptr generatePendingFence() { sp fence = new Fence(dup(fileno(tmpfile()))); return std::make_shared(fence); } void signalFenceWithTime(std::shared_ptr const& fence, nsecs_t time) { android::FenceTime::Snapshot snap(time); fence->applyTrustedSnapshot(snap); } std::shared_ptr generateSignalledFenceWithTime(nsecs_t time) { sp fence = new Fence(dup(fileno(tmpfile()))); std::shared_ptr ft = std::make_shared(fence); signalFenceWithTime(ft, time); return ft; } class VSyncReactorTest : public testing::Test { protected: VSyncReactorTest() : mMockTracker(std::make_shared>()), mMockClock(std::make_shared>()), mReactor(std::make_unique(mMockClock), *mMockTracker, kPendingLimit, false /* supportKernelIdleTimer */) { ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow)); ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period)); } std::shared_ptr mMockTracker; std::shared_ptr mMockClock; static constexpr size_t kPendingLimit = 3; static constexpr nsecs_t mDummyTime = 47; static constexpr nsecs_t mPhase = 3000; static constexpr nsecs_t mAnotherPhase = 5200; static constexpr nsecs_t period = 10000; static constexpr nsecs_t mFakeVSyncTime = 2093; static constexpr nsecs_t mFakeWakeupTime = 1892; static constexpr nsecs_t mFakeNow = 2214; static constexpr const char mName[] = "callbacky"; VSyncDispatch::CallbackToken const mFakeToken{2398}; nsecs_t lastCallbackTime = 0; // StubCallback outerCb; std::function innerCb; VSyncReactor mReactor; }; TEST_F(VSyncReactorTest, addingNullFenceCheck) { EXPECT_FALSE(mReactor.addPresentFence(nullptr)); } TEST_F(VSyncReactorTest, addingInvalidFenceSignalsNeedsMoreInfo) { EXPECT_TRUE(mReactor.addPresentFence(generateInvalidFence())); } TEST_F(VSyncReactorTest, addingSignalledFenceAddsToTracker) { EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime)); EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime))); } TEST_F(VSyncReactorTest, addingPendingFenceAddsSignalled) { nsecs_t anotherDummyTime = 2919019201; EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(0); auto pendingFence = generatePendingFence(); EXPECT_FALSE(mReactor.addPresentFence(pendingFence)); Mock::VerifyAndClearExpectations(mMockTracker.get()); signalFenceWithTime(pendingFence, mDummyTime); EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime)); EXPECT_CALL(*mMockTracker, addVsyncTimestamp(anotherDummyTime)); EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(anotherDummyTime))); } TEST_F(VSyncReactorTest, limitsPendingFences) { std::array, kPendingLimit * 2> fences; std::array fakeTimes; std::generate(fences.begin(), fences.end(), [] { return generatePendingFence(); }); std::generate(fakeTimes.begin(), fakeTimes.end(), [i = 10]() mutable { i++; return i * i; }); for (auto const& fence : fences) { mReactor.addPresentFence(fence); } for (auto i = fences.size() - kPendingLimit; i < fences.size(); i++) { EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimes[i])); } for (auto i = 0u; i < fences.size(); i++) { signalFenceWithTime(fences[i], fakeTimes[i]); } mReactor.addPresentFence(generatePendingFence()); } TEST_F(VSyncReactorTest, ignoresPresentFencesWhenToldTo) { static constexpr size_t aFewTimes = 8; EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime)).Times(1); mReactor.setIgnorePresentFences(true); for (auto i = 0; i < aFewTimes; i++) { mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)); } mReactor.setIgnorePresentFences(false); EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime))); } TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) { bool periodFlushed = true; EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2); mReactor.setIgnorePresentFences(true); nsecs_t const newPeriod = 5000; mReactor.startPeriodTransition(newPeriod); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); EXPECT_FALSE(mReactor.addHwVsyncTimestamp(newPeriod, std::nullopt, &periodFlushed)); EXPECT_TRUE(periodFlushed); EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); } TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) { nsecs_t const newPeriod = 5000; EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0); mReactor.startPeriodTransition(newPeriod); bool periodFlushed = true; EXPECT_TRUE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(20000, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); Mock::VerifyAndClearExpectations(mMockTracker.get()); EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1); EXPECT_FALSE(mReactor.addHwVsyncTimestamp(25000, std::nullopt, &periodFlushed)); EXPECT_TRUE(periodFlushed); } TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) { nsecs_t sampleTime = 0; nsecs_t const newPeriod = 5000; mReactor.startPeriodTransition(newPeriod); bool periodFlushed = true; EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); mReactor.startPeriodTransition(period); EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); } TEST_F(VSyncReactorTest, changingToAThirdPeriodWillWaitForLastPeriod) { nsecs_t sampleTime = 0; nsecs_t const secondPeriod = 5000; nsecs_t const thirdPeriod = 2000; mReactor.startPeriodTransition(secondPeriod); bool periodFlushed = true; EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); mReactor.startPeriodTransition(thirdPeriod); EXPECT_TRUE( mReactor.addHwVsyncTimestamp(sampleTime += secondPeriod, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); EXPECT_FALSE( mReactor.addHwVsyncTimestamp(sampleTime += thirdPeriod, std::nullopt, &periodFlushed)); EXPECT_TRUE(periodFlushed); } TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSync) { EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)) .WillOnce(Return(false)) .WillOnce(Return(true)) .WillOnce(Return(true)); EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); nsecs_t skewyPeriod = period >> 1; bool periodFlushed = false; nsecs_t sampleTime = 0; EXPECT_TRUE( mReactor.addHwVsyncTimestamp(sampleTime += skewyPeriod, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); } TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSyncPendingFence) { EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)) .Times(2) .WillOnce(Return(false)) .WillOnce(Return(true)); auto fence = generatePendingFence(); EXPECT_FALSE(mReactor.addPresentFence(fence)); signalFenceWithTime(fence, period >> 1); EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); } TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) { nsecs_t const newPeriod = 5000; mReactor.startPeriodTransition(newPeriod); EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); } TEST_F(VSyncReactorTest, setPeriodCalledFirstTwoEventsNewPeriod) { nsecs_t const newPeriod = 5000; EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0); mReactor.startPeriodTransition(newPeriod); bool periodFlushed = true; EXPECT_TRUE(mReactor.addHwVsyncTimestamp(5000, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); Mock::VerifyAndClearExpectations(mMockTracker.get()); EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1); EXPECT_FALSE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed)); EXPECT_TRUE(periodFlushed); } TEST_F(VSyncReactorTest, addResyncSampleTypical) { nsecs_t const fakeTimestamp = 3032; bool periodFlushed = false; EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp)); EXPECT_FALSE(mReactor.addHwVsyncTimestamp(fakeTimestamp, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); } TEST_F(VSyncReactorTest, addResyncSamplePeriodChanges) { bool periodFlushed = false; nsecs_t const newPeriod = 4000; mReactor.startPeriodTransition(newPeriod); auto time = 0; auto constexpr numTimestampSubmissions = 10; for (auto i = 0; i < numTimestampSubmissions; i++) { time += period; EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); } time += newPeriod; EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed)); EXPECT_TRUE(periodFlushed); for (auto i = 0; i < numTimestampSubmissions; i++) { time += newPeriod; EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); } } TEST_F(VSyncReactorTest, addPresentFenceWhileAwaitingPeriodConfirmationRequestsHwVsync) { auto time = 0; bool periodFlushed = false; nsecs_t const newPeriod = 4000; mReactor.startPeriodTransition(newPeriod); time += period; mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed); EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); time += newPeriod; mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed); EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); } TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTracker) { auto time = 0; bool periodFlushed = false; nsecs_t const newPeriod = 4000; mReactor.startPeriodTransition(newPeriod); static auto constexpr numSamplesWithNewPeriod = 4; Sequence seq; EXPECT_CALL(*mMockTracker, needsMoreSamples()) .Times(numSamplesWithNewPeriod - 2) .InSequence(seq) .WillRepeatedly(Return(true)); EXPECT_CALL(*mMockTracker, needsMoreSamples()) .Times(1) .InSequence(seq) .WillRepeatedly(Return(false)); EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(numSamplesWithNewPeriod); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed)); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed)); // confirmed period, but predictor wants numRequest samples. This one and prior are valid. EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed)); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed)); EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed)); } TEST_F(VSyncReactorTest, hwVsyncturnsOffOnConfirmationWhenTrackerDoesntRequest) { auto time = 0; bool periodFlushed = false; nsecs_t const newPeriod = 4000; mReactor.startPeriodTransition(newPeriod); Sequence seq; EXPECT_CALL(*mMockTracker, needsMoreSamples()) .Times(1) .InSequence(seq) .WillRepeatedly(Return(false)); EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed)); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed)); } TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) { auto time = 0; bool periodFlushed = false; nsecs_t const newPeriod1 = 4000; nsecs_t const newPeriod2 = 7000; mReactor.startPeriodTransition(newPeriod1); Sequence seq; EXPECT_CALL(*mMockTracker, needsMoreSamples()) .Times(4) .InSequence(seq) .WillRepeatedly(Return(true)); EXPECT_CALL(*mMockTracker, needsMoreSamples()) .Times(1) .InSequence(seq) .WillRepeatedly(Return(false)); EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(7); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed)); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed)); // confirmed period, but predictor wants numRequest samples. This one and prior are valid. EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed)); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed)); mReactor.startPeriodTransition(newPeriod2); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed)); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed)); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed)); EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed)); } TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) { bool periodFlushed = true; EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2); mReactor.setIgnorePresentFences(true); nsecs_t const newPeriod = 5000; mReactor.startPeriodTransition(newPeriod); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, 0, &periodFlushed)); EXPECT_FALSE(periodFlushed); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(newPeriod, 0, &periodFlushed)); EXPECT_FALSE(periodFlushed); EXPECT_FALSE(mReactor.addHwVsyncTimestamp(newPeriod, newPeriod, &periodFlushed)); EXPECT_TRUE(periodFlushed); EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); } TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) { // Create a reactor which supports the kernel idle timer auto idleReactor = VSyncReactor(std::make_unique(mMockClock), *mMockTracker, kPendingLimit, true /* supportKernelIdleTimer */); bool periodFlushed = true; EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(4); idleReactor.setIgnorePresentFences(true); // First, set the same period, which should only be confirmed when we receive two // matching callbacks idleReactor.startPeriodTransition(10000); EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 0, &periodFlushed)); EXPECT_FALSE(periodFlushed); // Correct period but incorrect timestamp delta EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 10000, &periodFlushed)); EXPECT_FALSE(periodFlushed); // Correct period and correct timestamp delta EXPECT_FALSE(idleReactor.addHwVsyncTimestamp(10000, 10000, &periodFlushed)); EXPECT_TRUE(periodFlushed); // Then, set a new period, which should be confirmed as soon as we receive a callback // reporting the new period nsecs_t const newPeriod = 5000; idleReactor.startPeriodTransition(newPeriod); // Incorrect timestamp delta and period EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(20000, 10000, &periodFlushed)); EXPECT_FALSE(periodFlushed); // Incorrect timestamp delta but correct period EXPECT_FALSE(idleReactor.addHwVsyncTimestamp(20000, 5000, &periodFlushed)); EXPECT_TRUE(periodFlushed); EXPECT_TRUE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0))); } } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wextra"