SF: recover from sub-frame jank V2
Respect the VrrConfig::minFrameInterval and adjust the vsync timeline
when a frame miss causes a violation of the minFrameInterval
with the next frame scheduled frame(s).
Bug: 296635687
Test: presubmit
Test: adb root && adb shell service call SurfaceFlinger 1045 f 0.9
Change-Id: Ice2128e291ca4890c7be3b24b9938e6faa383a82
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index ca2f92a..6a56353 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -34,12 +34,36 @@
return std::chrono::duration_cast<std::chrono::nanoseconds>(tp).count();
}
-class FixedRateIdealStubTracker : public VSyncTracker {
+class StubTracker : public VSyncTracker {
public:
- FixedRateIdealStubTracker() : mPeriod{toNs(3ms)} {}
+ StubTracker(nsecs_t period) : mPeriod(period) {}
bool addVsyncTimestamp(nsecs_t) final { return true; }
+ nsecs_t currentPeriod() const final {
+ std::lock_guard lock(mMutex);
+ return mPeriod;
+ }
+
+ Period minFramePeriod() const final { return Period::fromNs(currentPeriod()); }
+ void resetModel() final {}
+ bool needsMoreSamples() const final { return false; }
+ bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
+ void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final {}
+ void setRenderRate(Fps) final {}
+ void onFrameBegin(TimePoint, TimePoint) final {}
+ void onFrameMissed(TimePoint) final {}
+ void dump(std::string&) const final {}
+
+protected:
+ std::mutex mutable mMutex;
+ nsecs_t mPeriod;
+};
+
+class FixedRateIdealStubTracker : public StubTracker {
+public:
+ FixedRateIdealStubTracker() : StubTracker{toNs(3ms)} {}
+
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
auto const floor = timePoint % mPeriod;
if (floor == 0) {
@@ -47,26 +71,11 @@
}
return timePoint - floor + mPeriod;
}
-
- nsecs_t currentPeriod() const final { return mPeriod; }
- Period minFramePeriod() const final { return Period::fromNs(currentPeriod()); }
-
- void resetModel() final {}
- bool needsMoreSamples() const final { return false; }
- bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
- void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final {}
- void setRenderRate(Fps) final {}
- void dump(std::string&) const final {}
-
-private:
- nsecs_t const mPeriod;
};
-class VRRStubTracker : public VSyncTracker {
+class VRRStubTracker : public StubTracker {
public:
- VRRStubTracker(nsecs_t period) : mPeriod{period} {}
-
- bool addVsyncTimestamp(nsecs_t) final { return true; }
+ VRRStubTracker(nsecs_t period) : StubTracker(period) {}
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final {
std::lock_guard lock(mMutex);
@@ -84,23 +93,7 @@
mBase = last_known;
}
- nsecs_t currentPeriod() const final {
- std::lock_guard lock(mMutex);
- return mPeriod;
- }
-
- Period minFramePeriod() const final { return Period::fromNs(currentPeriod()); }
-
- void resetModel() final {}
- bool needsMoreSamples() const final { return false; }
- bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
- void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final {}
- void setRenderRate(Fps) final {}
- void dump(std::string&) const final {}
-
private:
- std::mutex mutable mMutex;
- nsecs_t mPeriod;
nsecs_t mBase = 0;
};
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 16aa249..7a498c9 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -44,6 +44,7 @@
::aidl::android::hardware::graphics::composer3::VrrConfig::NotifyExpectedPresentConfig;
using android::mock::createDisplayMode;
+using android::mock::createDisplayModeBuilder;
using android::mock::createVrrDisplayMode;
namespace android::scheduler {
@@ -698,6 +699,38 @@
}
}
+TEST_F(VSyncPredictorTest, adjustsVrrTimeline) {
+ 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{kMode, kHistorySize, kMinimumSamplesForPrediction,
+ kOutlierTolerancePercent, mVsyncTrackerCallback};
+
+ vrrTracker.setRenderRate(minFrameRate);
+ vrrTracker.addVsyncTimestamp(0);
+ EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1300));
+
+ vrrTracker.onFrameBegin(TimePoint::fromNs(2000), TimePoint::fromNs(1500));
+ EXPECT_EQ(1500, vrrTracker.nextAnticipatedVSyncTimeFrom(1300));
+ EXPECT_EQ(2500, vrrTracker.nextAnticipatedVSyncTimeFrom(2300));
+
+ vrrTracker.onFrameMissed(TimePoint::fromNs(2500));
+ EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2300));
+ EXPECT_EQ(4000, vrrTracker.nextAnticipatedVSyncTimeFrom(3300));
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index 0858c28..e588bb9 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -36,6 +36,8 @@
MOCK_METHOD(bool, isVSyncInPhase, (nsecs_t, Fps), (const, override));
MOCK_METHOD(void, setDisplayModePtr, (ftl::NonNull<DisplayModePtr>), (override));
MOCK_METHOD(void, setRenderRate, (Fps), (override));
+ MOCK_METHOD(void, onFrameBegin, (TimePoint, TimePoint), (override));
+ MOCK_METHOD(void, onFrameMissed, (TimePoint), (override));
MOCK_METHOD(void, dump, (std::string&), (const, override));
};