Loading services/surfaceflinger/Scheduler/VSyncPredictor.cpp +12 −5 Original line number Diff line number Diff line Loading @@ -452,7 +452,7 @@ Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentT const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope; const auto threshold = currentPeriod / 2; const auto minFramePeriod = minFramePeriodLocked().ns(); const auto minFramePeriod = minFramePeriodLocked(); auto prev = lastConfirmedPresentTime.ns(); for (auto& current : mPastExpectedPresentTimes) { Loading @@ -463,10 +463,10 @@ Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentT 1e6f); } const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod; const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod.ns(); if (minPeriodViolation) { SFTRACE_NAME("minPeriodViolation"); current = TimePoint::fromNs(prev + minFramePeriod); current = TimePoint::fromNs(prev + minFramePeriod.ns()); prev = current.ns(); } else { break; Loading @@ -477,7 +477,7 @@ Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentT const auto phase = Duration(mPastExpectedPresentTimes.back() - expectedPresentTime); if (phase > 0ns) { for (auto& timeline : mTimelines) { timeline.shiftVsyncSequence(phase); timeline.shiftVsyncSequence(phase, minFramePeriod); } mPastExpectedPresentTimes.clear(); return phase; Loading Loading @@ -778,8 +778,15 @@ bool VSyncPredictor::VsyncTimeline::isVSyncInPhase(Model model, nsecs_t vsync, F return vsyncSequence.seq % divisor == 0; } void VSyncPredictor::VsyncTimeline::shiftVsyncSequence(Duration phase) { void VSyncPredictor::VsyncTimeline::shiftVsyncSequence(Duration phase, Period minFramePeriod) { if (mLastVsyncSequence) { const auto renderRate = mRenderRateOpt.value_or(Fps::fromPeriodNsecs(mIdealPeriod.ns())); const auto threshold = mIdealPeriod.ns() / 2; if (renderRate.getPeriodNsecs() - phase.ns() + threshold >= minFramePeriod.ns()) { SFTRACE_FORMAT_INSTANT("Not-Adjusting vsync by %.2f", static_cast<float>(phase.ns()) / 1e6f); return; } SFTRACE_FORMAT_INSTANT("adjusting vsync by %.2f", static_cast<float>(phase.ns()) / 1e6f); mLastVsyncSequence->vsyncTime += phase.ns(); } Loading services/surfaceflinger/Scheduler/VSyncPredictor.h +1 −1 Original line number Diff line number Diff line Loading @@ -104,7 +104,7 @@ private: void freeze(TimePoint lastVsync); std::optional<TimePoint> validUntil() const { return mValidUntil; } bool isVSyncInPhase(Model, nsecs_t vsync, Fps frameRate); void shiftVsyncSequence(Duration phase); void shiftVsyncSequence(Duration phase, Period minFramePeriod); void setRenderRate(std::optional<Fps> renderRateOpt) { mRenderRateOpt = renderRateOpt; } enum class VsyncOnTimeline { Loading services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +30 −0 Original line number Diff line number Diff line Loading @@ -1055,6 +1055,36 @@ TEST_F(VSyncPredictorTest, timelineNotAdjustedForEarlyPresent) { EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1400, 1000)); EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2000, 1000)); } TEST_F(VSyncPredictorTest, adjustsOnlyMinFrameViolatingVrrTimeline) { const auto refreshRate = Fps::fromPeriodNsecs(500); auto minFrameRate = Fps::fromPeriodNsecs(1000); hal::VrrConfig vrrConfig{.minFrameIntervalNs = static_cast<int32_t>(minFrameRate.getPeriodNsecs())}; ftl::NonNull<DisplayModePtr> mode = ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), refreshRate, vrrConfig)); VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), mode, kHistorySize, kMinimumSamplesForPrediction, kOutlierTolerancePercent}; vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false); vrrTracker.addVsyncTimestamp(0); EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700)); EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000)); auto lastConfirmedSignalTime = TimePoint::fromNs(1500); auto lastConfirmedExpectedPresentTime = TimePoint::fromNs(1000); vrrTracker.onFrameBegin(TimePoint::fromNs(2000), {lastConfirmedSignalTime, lastConfirmedExpectedPresentTime}); EXPECT_EQ(3500, vrrTracker.nextAnticipatedVSyncTimeFrom(3000, 1500)); minFrameRate = Fps::fromPeriodNsecs(2000); vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false); lastConfirmedSignalTime = TimePoint::fromNs(2500); lastConfirmedExpectedPresentTime = TimePoint::fromNs(2500); vrrTracker.onFrameBegin(TimePoint::fromNs(3000), {lastConfirmedSignalTime, lastConfirmedExpectedPresentTime}); // Enough time without adjusting vsync to present with new rate on time, no need of adjustment EXPECT_EQ(5500, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 3500)); } } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues Loading Loading
services/surfaceflinger/Scheduler/VSyncPredictor.cpp +12 −5 Original line number Diff line number Diff line Loading @@ -452,7 +452,7 @@ Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentT const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope; const auto threshold = currentPeriod / 2; const auto minFramePeriod = minFramePeriodLocked().ns(); const auto minFramePeriod = minFramePeriodLocked(); auto prev = lastConfirmedPresentTime.ns(); for (auto& current : mPastExpectedPresentTimes) { Loading @@ -463,10 +463,10 @@ Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentT 1e6f); } const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod; const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod.ns(); if (minPeriodViolation) { SFTRACE_NAME("minPeriodViolation"); current = TimePoint::fromNs(prev + minFramePeriod); current = TimePoint::fromNs(prev + minFramePeriod.ns()); prev = current.ns(); } else { break; Loading @@ -477,7 +477,7 @@ Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentT const auto phase = Duration(mPastExpectedPresentTimes.back() - expectedPresentTime); if (phase > 0ns) { for (auto& timeline : mTimelines) { timeline.shiftVsyncSequence(phase); timeline.shiftVsyncSequence(phase, minFramePeriod); } mPastExpectedPresentTimes.clear(); return phase; Loading Loading @@ -778,8 +778,15 @@ bool VSyncPredictor::VsyncTimeline::isVSyncInPhase(Model model, nsecs_t vsync, F return vsyncSequence.seq % divisor == 0; } void VSyncPredictor::VsyncTimeline::shiftVsyncSequence(Duration phase) { void VSyncPredictor::VsyncTimeline::shiftVsyncSequence(Duration phase, Period minFramePeriod) { if (mLastVsyncSequence) { const auto renderRate = mRenderRateOpt.value_or(Fps::fromPeriodNsecs(mIdealPeriod.ns())); const auto threshold = mIdealPeriod.ns() / 2; if (renderRate.getPeriodNsecs() - phase.ns() + threshold >= minFramePeriod.ns()) { SFTRACE_FORMAT_INSTANT("Not-Adjusting vsync by %.2f", static_cast<float>(phase.ns()) / 1e6f); return; } SFTRACE_FORMAT_INSTANT("adjusting vsync by %.2f", static_cast<float>(phase.ns()) / 1e6f); mLastVsyncSequence->vsyncTime += phase.ns(); } Loading
services/surfaceflinger/Scheduler/VSyncPredictor.h +1 −1 Original line number Diff line number Diff line Loading @@ -104,7 +104,7 @@ private: void freeze(TimePoint lastVsync); std::optional<TimePoint> validUntil() const { return mValidUntil; } bool isVSyncInPhase(Model, nsecs_t vsync, Fps frameRate); void shiftVsyncSequence(Duration phase); void shiftVsyncSequence(Duration phase, Period minFramePeriod); void setRenderRate(std::optional<Fps> renderRateOpt) { mRenderRateOpt = renderRateOpt; } enum class VsyncOnTimeline { Loading
services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +30 −0 Original line number Diff line number Diff line Loading @@ -1055,6 +1055,36 @@ TEST_F(VSyncPredictorTest, timelineNotAdjustedForEarlyPresent) { EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1400, 1000)); EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2000, 1000)); } TEST_F(VSyncPredictorTest, adjustsOnlyMinFrameViolatingVrrTimeline) { const auto refreshRate = Fps::fromPeriodNsecs(500); auto minFrameRate = Fps::fromPeriodNsecs(1000); hal::VrrConfig vrrConfig{.minFrameIntervalNs = static_cast<int32_t>(minFrameRate.getPeriodNsecs())}; ftl::NonNull<DisplayModePtr> mode = ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), refreshRate, vrrConfig)); VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), mode, kHistorySize, kMinimumSamplesForPrediction, kOutlierTolerancePercent}; vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false); vrrTracker.addVsyncTimestamp(0); EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700)); EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000)); auto lastConfirmedSignalTime = TimePoint::fromNs(1500); auto lastConfirmedExpectedPresentTime = TimePoint::fromNs(1000); vrrTracker.onFrameBegin(TimePoint::fromNs(2000), {lastConfirmedSignalTime, lastConfirmedExpectedPresentTime}); EXPECT_EQ(3500, vrrTracker.nextAnticipatedVSyncTimeFrom(3000, 1500)); minFrameRate = Fps::fromPeriodNsecs(2000); vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false); lastConfirmedSignalTime = TimePoint::fromNs(2500); lastConfirmedExpectedPresentTime = TimePoint::fromNs(2500); vrrTracker.onFrameBegin(TimePoint::fromNs(3000), {lastConfirmedSignalTime, lastConfirmedExpectedPresentTime}); // Enough time without adjusting vsync to present with new rate on time, no need of adjustment EXPECT_EQ(5500, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 3500)); } } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues Loading