SF: fix vsync shift repeatedly when HWC miss a frame
Bug: 328085852
Change-Id: I17df8ae1fcf5e21662e582e795d00a3d800640f3
Test: presubmit
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 2f9dfea..45b2961 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -45,28 +45,6 @@
static auto constexpr kMaxPercent = 100u;
-namespace {
-nsecs_t getVsyncFixup(VSyncPredictor::Model model, Period minFramePeriod, nsecs_t vsyncTime,
- std::optional<nsecs_t> lastVsyncOpt) {
- const auto threshold = model.slope / 2;
-
- if (FlagManager::getInstance().vrr_config() && lastVsyncOpt) {
- const auto vsyncDiff = vsyncTime - *lastVsyncOpt;
- if (vsyncDiff >= threshold && vsyncDiff <= minFramePeriod.ns() - threshold) {
- const auto vsyncFixup = *lastVsyncOpt + minFramePeriod.ns() - vsyncTime;
- ATRACE_FORMAT_INSTANT("minFramePeriod violation. next in %.2f which is %.2f from prev. "
- "adjust by %.2f",
- static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f,
- static_cast<float>(vsyncTime - *lastVsyncOpt) / 1e6f,
- static_cast<float>(vsyncFixup) / 1e6f);
- return vsyncFixup;
- }
- }
-
- return 0;
-}
-} // namespace
-
VSyncPredictor::~VSyncPredictor() = default;
VSyncPredictor::VSyncPredictor(std::unique_ptr<Clock> clock, ftl::NonNull<DisplayModePtr> modePtr,
@@ -585,16 +563,33 @@
std::optional<nsecs_t> lastVsyncOpt) {
ATRACE_FORMAT("renderRate %s", mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA");
+ nsecs_t vsyncTime = snapToVsyncAlignedWithRenderRate(model, vsync);
const auto threshold = model.slope / 2;
const auto lastFrameMissed =
lastVsyncOpt && std::abs(*lastVsyncOpt - missedVsync.vsync.ns()) < threshold;
- nsecs_t vsyncTime = snapToVsyncAlignedWithRenderRate(model, vsync);
nsecs_t vsyncFixupTime = 0;
if (FlagManager::getInstance().vrr_config() && lastFrameMissed) {
+ // If the last frame missed is the last vsync, we already shifted the timeline. Depends on
+ // whether we skipped the frame (onFrameMissed) or not (onFrameBegin) we apply a different
+ // fixup. There is no need to to shift the vsync timeline again.
vsyncTime += missedVsync.fixup.ns();
ATRACE_FORMAT_INSTANT("lastFrameMissed");
} else {
- vsyncFixupTime = getVsyncFixup(model, minFramePeriod, vsyncTime, lastVsyncOpt);
+ if (FlagManager::getInstance().vrr_config() && lastVsyncOpt) {
+ // lastVsyncOpt is based on the old timeline before we shifted it. we should correct it
+ // first before trying to use it.
+ lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt);
+ const auto vsyncDiff = vsyncTime - *lastVsyncOpt;
+ if (vsyncDiff <= minFramePeriod.ns() - threshold) {
+ vsyncFixupTime = *lastVsyncOpt + minFramePeriod.ns() - vsyncTime;
+ ATRACE_FORMAT_INSTANT("minFramePeriod violation. next in %.2f which is %.2f from "
+ "prev. "
+ "adjust by %.2f",
+ static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f,
+ static_cast<float>(vsyncTime - *lastVsyncOpt) / 1e6f,
+ static_cast<float>(vsyncFixupTime) / 1e6f);
+ }
+ }
vsyncTime += vsyncFixupTime;
}
@@ -605,6 +600,8 @@
return std::nullopt;
}
+ // If we needed a fixup, it means that we changed the render rate and the chosen vsync would
+ // cross minFramePeriod. In that case we need to shift the entire vsync timeline.
if (vsyncFixupTime > 0) {
shiftVsyncSequence(Duration::fromNs(vsyncFixupTime));
}
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 049b092..e69410b 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -591,6 +591,7 @@
TimePoint::fromNs(2000)));
// Not crossing the min frame period
+ vrrTracker->onFrameBegin(TimePoint::fromNs(2000), TimePoint::fromNs(1500));
EXPECT_EQ(Fps::fromPeriodNsecs(1000),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
TimePoint::fromNs(2500)));
@@ -599,15 +600,15 @@
vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate);
scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate);
- // Set 2000 as vsync seq #0
- vrrTracker->nextAnticipatedVSyncTimeFrom(1700);
+ // Set 4000 as vsync seq #0
+ vrrTracker->nextAnticipatedVSyncTimeFrom(3700);
EXPECT_EQ(Fps::fromPeriodNsecs(2000),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
- TimePoint::fromNs(2000)));
+ TimePoint::fromNs(4000)));
EXPECT_EQ(Fps::fromPeriodNsecs(2000),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
- TimePoint::fromNs(4000)));
+ TimePoint::fromNs(6000)));
}
TEST_F(SchedulerTest, resyncAllToHardwareVsync) FTL_FAKE_GUARD(kMainThreadContext) {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 6b9ea56..1394c55 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -688,6 +688,50 @@
EXPECT_EQ(10500, vrrTracker.nextAnticipatedVSyncTimeFrom(9000, 7000));
}
+TEST_F(VSyncPredictorTest, adjustsVrrTimelineTwoClients) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto refreshRate = Fps::fromPeriodNsecs(500);
+ const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig;
+ vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+ const ftl::NonNull<DisplayModePtr> kMode =
+ ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), refreshRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID)
+ .setVrrConfig(std::move(vrrConfig))
+ .build());
+
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+
+ vrrTracker.setRenderRate(minFrameRate);
+ vrrTracker.addVsyncTimestamp(0);
+ EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2700));
+
+ EXPECT_EQ(4000, vrrTracker.nextAnticipatedVSyncTimeFrom(3000, 3000));
+
+ EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
+ EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2700));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(3000), TimePoint::fromNs(0));
+
+ EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+ EXPECT_EQ(4000, vrrTracker.nextAnticipatedVSyncTimeFrom(3700));
+ vrrTracker.onFrameMissed(TimePoint::fromNs(4000));
+
+ EXPECT_EQ(4500, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(4500), TimePoint::fromNs(4500));
+
+ EXPECT_EQ(7500, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));
+ EXPECT_EQ(5500, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(5500), TimePoint::fromNs(4500));
+
+ EXPECT_EQ(8500, vrrTracker.nextAnticipatedVSyncTimeFrom(7500, 7500));
+ EXPECT_EQ(6500, vrrTracker.nextAnticipatedVSyncTimeFrom(5500, 5500));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(6500), TimePoint::fromNs(5500));
+}
+
TEST_F(VSyncPredictorTest, renderRateIsPreservedForCommittedVsyncs) {
tracker.addVsyncTimestamp(1000);