Loading services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +27 −2 Original line number Original line Diff line number Diff line Loading @@ -87,10 +87,26 @@ ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsec return ScheduleResult::Scheduled; return ScheduleResult::Scheduled; } } void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(nsecs_t workDuration, nsecs_t earliestVsync) { mWorkloadUpdateInfo = {.earliestVsync = earliestVsync, .duration = workDuration}; } bool VSyncDispatchTimerQueueEntry::hasPendingWorkloadUpdate() const { return mWorkloadUpdateInfo.has_value(); } void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) { void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) { if (!mArmedInfo) { if (!mArmedInfo && !mWorkloadUpdateInfo) { return; return; } } if (mWorkloadUpdateInfo) { mEarliestVsync = mWorkloadUpdateInfo->earliestVsync; mWorkDuration = mWorkloadUpdateInfo->duration; mWorkloadUpdateInfo.reset(); } auto const nextVsyncTime = auto const nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration)); tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration)); mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime}; mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime}; Loading Loading @@ -192,7 +208,7 @@ void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor( std::optional<std::string_view> nextWakeupName; std::optional<std::string_view> nextWakeupName; for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) { for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) { auto& callback = it->second; auto& callback = it->second; if (!callback->wakeupTime()) { if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) { continue; continue; } } Loading Loading @@ -291,6 +307,15 @@ ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t wo } } auto& callback = it->second; auto& callback = it->second; auto const now = mTimeKeeper->now(); auto const now = mTimeKeeper->now(); /* If the timer thread will run soon, we'll apply this work update via the callback * timer recalculation to avoid cancelling a callback that is about to fire. */ auto const rearmImminent = now > mIntendedWakeupTime; if (CC_UNLIKELY(rearmImminent)) { callback->addPendingWorkloadUpdate(workDuration, earliestVsync); return ScheduleResult::Scheduled; } result = callback->schedule(workDuration, earliestVsync, mTracker, now); result = callback->schedule(workDuration, earliestVsync, mTracker, now); if (result == ScheduleResult::CannotSchedule) { if (result == ScheduleResult::CannotSchedule) { return result; return result; Loading services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h +13 −0 Original line number Original line Diff line number Diff line Loading @@ -64,6 +64,13 @@ public: // This moves the state from armed->running. // This moves the state from armed->running. // Store the timestamp that this was intended for as the last called timestamp. // Store the timestamp that this was intended for as the last called timestamp. nsecs_t executing(); nsecs_t executing(); // Adds a pending upload of the earliestVSync and workDuration that will be applied on the next // call to update() void addPendingWorkloadUpdate(nsecs_t workDuration, nsecs_t earliestVsync); // Checks if there is a pending update to the workload, returning true if so. bool hasPendingWorkloadUpdate() const; // End: functions that are not threadsafe. // End: functions that are not threadsafe. // Invoke the callback with the two given timestamps, moving the state from running->disarmed. // Invoke the callback with the two given timestamps, moving the state from running->disarmed. Loading @@ -88,6 +95,12 @@ private: std::optional<ArmingInfo> mArmedInfo; std::optional<ArmingInfo> mArmedInfo; std::optional<nsecs_t> mLastDispatchTime; std::optional<nsecs_t> mLastDispatchTime; struct WorkloadUpdateInfo { nsecs_t duration; nsecs_t earliestVsync; }; std::optional<WorkloadUpdateInfo> mWorkloadUpdateInfo; mutable std::mutex mRunningMutex; mutable std::mutex mRunningMutex; std::condition_variable mCv; std::condition_variable mCv; bool mRunning GUARDED_BY(mRunningMutex) = false; bool mRunning GUARDED_BY(mRunningMutex) = false; Loading services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +57 −1 Original line number Original line Diff line number Diff line Loading @@ -89,15 +89,18 @@ public: void advanceBy(nsecs_t advancement) { void advanceBy(nsecs_t advancement) { mCurrentTime += advancement; mCurrentTime += advancement; if (mCurrentTime >= mNextCallbackTime && mCallback) { if (mCurrentTime >= (mNextCallbackTime + mLag) && mCallback) { mCallback(); mCallback(); } } }; }; void setLag(nsecs_t lag) { mLag = lag; } private: private: std::function<void()> mCallback; std::function<void()> mCallback; nsecs_t mNextCallbackTime = 0; nsecs_t mNextCallbackTime = 0; nsecs_t mCurrentTime = 0; nsecs_t mCurrentTime = 0; nsecs_t mLag = 0; }; }; class CountingCallback { class CountingCallback { Loading Loading @@ -658,6 +661,46 @@ TEST_F(VSyncDispatchTimerQueueTest, helperMoveAssign) { cb1.cancel(); cb1.cancel(); } } // b/154303580 TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent) { Sequence seq; EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq); EXPECT_CALL(mMockClock, alarmIn(_, 1200)).InSequence(seq); CountingCallback cb1(mDispatch); CountingCallback cb2(mDispatch); EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled); mMockClock.setLag(100); mMockClock.advanceBy(620); EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled); mMockClock.advanceBy(80); EXPECT_THAT(cb1.mCalls.size(), Eq(1)); EXPECT_THAT(cb2.mCalls.size(), Eq(0)); } // b/154303580. // If the same callback tries to reschedule itself after it's too late, timer opts to apply the // update later, as opposed to blocking the calling thread. TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminentSameCallback) { Sequence seq; EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq); EXPECT_CALL(mMockClock, alarmIn(_, 930)).InSequence(seq); CountingCallback cb(mDispatch); EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled); mMockClock.setLag(100); mMockClock.advanceBy(620); EXPECT_EQ(mDispatch.schedule(cb, 370, 2000), ScheduleResult::Scheduled); mMockClock.advanceBy(80); EXPECT_THAT(cb.mCalls.size(), Eq(1)); } class VSyncDispatchTimerQueueEntryTest : public testing::Test { class VSyncDispatchTimerQueueEntryTest : public testing::Test { protected: protected: nsecs_t const mPeriod = 1000; nsecs_t const mPeriod = 1000; Loading Loading @@ -817,6 +860,19 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) { EXPECT_THAT(entry.schedule(1200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); EXPECT_THAT(entry.schedule(1200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); } } TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) { static constexpr auto effectualOffset = 200; VSyncDispatchTimerQueueEntry entry( "test", [](auto, auto) {}, mVsyncMoveThreshold); EXPECT_FALSE(entry.hasPendingWorkloadUpdate()); entry.addPendingWorkloadUpdate(100, 400); entry.addPendingWorkloadUpdate(effectualOffset, 700); EXPECT_TRUE(entry.hasPendingWorkloadUpdate()); entry.update(mStubTracker, 0); EXPECT_FALSE(entry.hasPendingWorkloadUpdate()); EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset)); } } // namespace android::scheduler } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues // TODO(b/129481165): remove the #pragma below and fix conversion issues Loading Loading
services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +27 −2 Original line number Original line Diff line number Diff line Loading @@ -87,10 +87,26 @@ ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsec return ScheduleResult::Scheduled; return ScheduleResult::Scheduled; } } void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(nsecs_t workDuration, nsecs_t earliestVsync) { mWorkloadUpdateInfo = {.earliestVsync = earliestVsync, .duration = workDuration}; } bool VSyncDispatchTimerQueueEntry::hasPendingWorkloadUpdate() const { return mWorkloadUpdateInfo.has_value(); } void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) { void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) { if (!mArmedInfo) { if (!mArmedInfo && !mWorkloadUpdateInfo) { return; return; } } if (mWorkloadUpdateInfo) { mEarliestVsync = mWorkloadUpdateInfo->earliestVsync; mWorkDuration = mWorkloadUpdateInfo->duration; mWorkloadUpdateInfo.reset(); } auto const nextVsyncTime = auto const nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration)); tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration)); mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime}; mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime}; Loading Loading @@ -192,7 +208,7 @@ void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor( std::optional<std::string_view> nextWakeupName; std::optional<std::string_view> nextWakeupName; for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) { for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) { auto& callback = it->second; auto& callback = it->second; if (!callback->wakeupTime()) { if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) { continue; continue; } } Loading Loading @@ -291,6 +307,15 @@ ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t wo } } auto& callback = it->second; auto& callback = it->second; auto const now = mTimeKeeper->now(); auto const now = mTimeKeeper->now(); /* If the timer thread will run soon, we'll apply this work update via the callback * timer recalculation to avoid cancelling a callback that is about to fire. */ auto const rearmImminent = now > mIntendedWakeupTime; if (CC_UNLIKELY(rearmImminent)) { callback->addPendingWorkloadUpdate(workDuration, earliestVsync); return ScheduleResult::Scheduled; } result = callback->schedule(workDuration, earliestVsync, mTracker, now); result = callback->schedule(workDuration, earliestVsync, mTracker, now); if (result == ScheduleResult::CannotSchedule) { if (result == ScheduleResult::CannotSchedule) { return result; return result; Loading
services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h +13 −0 Original line number Original line Diff line number Diff line Loading @@ -64,6 +64,13 @@ public: // This moves the state from armed->running. // This moves the state from armed->running. // Store the timestamp that this was intended for as the last called timestamp. // Store the timestamp that this was intended for as the last called timestamp. nsecs_t executing(); nsecs_t executing(); // Adds a pending upload of the earliestVSync and workDuration that will be applied on the next // call to update() void addPendingWorkloadUpdate(nsecs_t workDuration, nsecs_t earliestVsync); // Checks if there is a pending update to the workload, returning true if so. bool hasPendingWorkloadUpdate() const; // End: functions that are not threadsafe. // End: functions that are not threadsafe. // Invoke the callback with the two given timestamps, moving the state from running->disarmed. // Invoke the callback with the two given timestamps, moving the state from running->disarmed. Loading @@ -88,6 +95,12 @@ private: std::optional<ArmingInfo> mArmedInfo; std::optional<ArmingInfo> mArmedInfo; std::optional<nsecs_t> mLastDispatchTime; std::optional<nsecs_t> mLastDispatchTime; struct WorkloadUpdateInfo { nsecs_t duration; nsecs_t earliestVsync; }; std::optional<WorkloadUpdateInfo> mWorkloadUpdateInfo; mutable std::mutex mRunningMutex; mutable std::mutex mRunningMutex; std::condition_variable mCv; std::condition_variable mCv; bool mRunning GUARDED_BY(mRunningMutex) = false; bool mRunning GUARDED_BY(mRunningMutex) = false; Loading
services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +57 −1 Original line number Original line Diff line number Diff line Loading @@ -89,15 +89,18 @@ public: void advanceBy(nsecs_t advancement) { void advanceBy(nsecs_t advancement) { mCurrentTime += advancement; mCurrentTime += advancement; if (mCurrentTime >= mNextCallbackTime && mCallback) { if (mCurrentTime >= (mNextCallbackTime + mLag) && mCallback) { mCallback(); mCallback(); } } }; }; void setLag(nsecs_t lag) { mLag = lag; } private: private: std::function<void()> mCallback; std::function<void()> mCallback; nsecs_t mNextCallbackTime = 0; nsecs_t mNextCallbackTime = 0; nsecs_t mCurrentTime = 0; nsecs_t mCurrentTime = 0; nsecs_t mLag = 0; }; }; class CountingCallback { class CountingCallback { Loading Loading @@ -658,6 +661,46 @@ TEST_F(VSyncDispatchTimerQueueTest, helperMoveAssign) { cb1.cancel(); cb1.cancel(); } } // b/154303580 TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent) { Sequence seq; EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq); EXPECT_CALL(mMockClock, alarmIn(_, 1200)).InSequence(seq); CountingCallback cb1(mDispatch); CountingCallback cb2(mDispatch); EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled); mMockClock.setLag(100); mMockClock.advanceBy(620); EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled); mMockClock.advanceBy(80); EXPECT_THAT(cb1.mCalls.size(), Eq(1)); EXPECT_THAT(cb2.mCalls.size(), Eq(0)); } // b/154303580. // If the same callback tries to reschedule itself after it's too late, timer opts to apply the // update later, as opposed to blocking the calling thread. TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminentSameCallback) { Sequence seq; EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq); EXPECT_CALL(mMockClock, alarmIn(_, 930)).InSequence(seq); CountingCallback cb(mDispatch); EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled); mMockClock.setLag(100); mMockClock.advanceBy(620); EXPECT_EQ(mDispatch.schedule(cb, 370, 2000), ScheduleResult::Scheduled); mMockClock.advanceBy(80); EXPECT_THAT(cb.mCalls.size(), Eq(1)); } class VSyncDispatchTimerQueueEntryTest : public testing::Test { class VSyncDispatchTimerQueueEntryTest : public testing::Test { protected: protected: nsecs_t const mPeriod = 1000; nsecs_t const mPeriod = 1000; Loading Loading @@ -817,6 +860,19 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) { EXPECT_THAT(entry.schedule(1200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); EXPECT_THAT(entry.schedule(1200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); } } TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) { static constexpr auto effectualOffset = 200; VSyncDispatchTimerQueueEntry entry( "test", [](auto, auto) {}, mVsyncMoveThreshold); EXPECT_FALSE(entry.hasPendingWorkloadUpdate()); entry.addPendingWorkloadUpdate(100, 400); entry.addPendingWorkloadUpdate(effectualOffset, 700); EXPECT_TRUE(entry.hasPendingWorkloadUpdate()); entry.update(mStubTracker, 0); EXPECT_FALSE(entry.hasPendingWorkloadUpdate()); EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset)); } } // namespace android::scheduler } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues // TODO(b/129481165): remove the #pragma below and fix conversion issues Loading