Skip to content

Commit ae7eda1

Browse files
Ram IndaniAndroid (Google) Code Review
authored andcommitted
Merge "[SF] VSyncPredictor to use idlePeriod for outlier calculation" into main
2 parents 7fc2c30 + 952f898 commit ae7eda1

2 files changed

Lines changed: 85 additions & 6 deletions

File tree

services/surfaceflinger/Scheduler/VSyncPredictor.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,11 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
206206
// Normalizing to the oldest timestamp cuts down on error in calculating the intercept.
207207
const auto oldestTS = *std::min_element(mTimestamps.begin(), mTimestamps.end());
208208
auto it = mRateMap.find(idealPeriod());
209-
auto const currentPeriod = it->second.slope;
209+
// Calculated slope over the period of time can become outdated as the new timestamps are
210+
// stored. Using idealPeriod instead provides a rate which is valid at all the times.
211+
auto const currentPeriod = FlagManager::getInstance().vsync_predictor_recovery()
212+
? idealPeriod()
213+
: it->second.slope;
210214

211215
// The mean of the ordinals must be precise for the intercept calculation, so scale them up for
212216
// fixed-point arithmetic.

services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,53 @@ TEST_F(VSyncPredictorTest, againstOutliersDiscontinuous_500hzLowVariance) {
304304
EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
305305
}
306306

307+
TEST_F(VSyncPredictorTest, recoverAfterDriftedVSyncAreReplacedWithCorrectVSync) {
308+
SET_FLAG_FOR_TEST(flags::vsync_predictor_recovery, true);
309+
auto constexpr idealPeriodNs = 4166666;
310+
auto constexpr minFrameIntervalNs = 8333333;
311+
auto constexpr idealPeriod = Fps::fromPeriodNsecs(idealPeriodNs);
312+
auto constexpr minFrameRate = Fps::fromPeriodNsecs(minFrameIntervalNs);
313+
hal::VrrConfig vrrConfig{.minFrameIntervalNs = minFrameIntervalNs};
314+
ftl::NonNull<DisplayModePtr> mode =
315+
ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), idealPeriod, vrrConfig));
316+
VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), mode, kHistorySize,
317+
kMinimumSamplesForPrediction, kOutlierTolerancePercent};
318+
vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ true);
319+
// Curated list of VSyncs that causes the VSync drift.
320+
std::vector<nsecs_t> const simulatedVsyncs{74473665741, 74481774375, 74489911818, 74497993491,
321+
74506000833, 74510002150, 74513904390, 74517748707,
322+
74521550947, 74525383187, 74529165427, 74533067667,
323+
74536751484, 74540653724, 74544282649, 74548084889,
324+
74551917129, 74555699369, 74559601609, 74563601611,
325+
74567503851, 74571358168, 74575260408, 74578889333,
326+
74582691573, 74586523813, 74590306053, 74593589870,
327+
74597492110, 74601121035, 74604923275, 74608755515,
328+
74612537755, 74616166680, 74619795605, 74623424530,
329+
74627043455, 74630645695, 74634245935, 74637778175,
330+
74641291992, 74644794232, 74648275157, 74651575397,
331+
74654807637, 74658007877, 74661176117, 74664345357};
332+
for (auto const& timestamp : simulatedVsyncs) {
333+
vrrTracker.addVsyncTimestamp(timestamp);
334+
}
335+
auto slope = vrrTracker.getVSyncPredictionModel().slope;
336+
// Without using the idealPeriod for the calculation of the VSync predictor mode the
337+
// the slope would be 3343031
338+
EXPECT_THAT(slope, IsCloseTo(4347805, mMaxRoundingError));
339+
EXPECT_FALSE(vrrTracker.needsMoreSamples());
340+
341+
auto lastVsync = 74664345357;
342+
// Add valid VSyncs to replace the drifted VSyncs
343+
for (int i = 0; i <= kHistorySize; i++) {
344+
lastVsync += minFrameIntervalNs;
345+
EXPECT_TRUE(vrrTracker.addVsyncTimestamp(lastVsync));
346+
}
347+
EXPECT_FALSE(vrrTracker.needsMoreSamples());
348+
slope = vrrTracker.getVSyncPredictionModel().slope;
349+
// Corrected slop is closer to the idealPeriod
350+
// when valid vsync are inserted otherwise this would still be 3349673
351+
EXPECT_THAT(slope, IsCloseTo(idealPeriodNs, mMaxRoundingError));
352+
}
353+
307354
TEST_F(VSyncPredictorTest, handlesVsyncChange) {
308355
auto const fastPeriod = 100;
309356
auto const fastTimeBase = 100;
@@ -397,8 +444,8 @@ TEST_F(VSyncPredictorTest, idealModelPredictionsBeforeRegressionModelIsBuilt) {
397444
}
398445
}
399446

400-
// See b/145667109, and comment in prod code under test.
401-
TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) {
447+
TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept_withPredictorRecovery) {
448+
SET_FLAG_FOR_TEST(flags::vsync_predictor_recovery, true);
402449
std::vector<nsecs_t> const simulatedVsyncs{
403450
158929578733000,
404451
158929306806205, // oldest TS in ringbuffer
@@ -409,6 +456,34 @@ TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) {
409456
158929706370359,
410457
};
411458
auto const idealPeriod = 11111111;
459+
auto const expectedPeriod = 11079563;
460+
auto const expectedIntercept = 1335662;
461+
462+
tracker.setDisplayModePtr(displayMode(idealPeriod));
463+
for (auto const& timestamp : simulatedVsyncs) {
464+
tracker.addVsyncTimestamp(timestamp);
465+
}
466+
467+
auto [slope, intercept] = tracker.getVSyncPredictionModel();
468+
EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
469+
EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
470+
471+
// (timePoint - oldestTS) % expectedPeriod works out to be: 894272
472+
// (timePoint - oldestTS) / expectedPeriod works out to be: 38.08
473+
auto const timePoint = 158929728723871;
474+
auto const prediction = tracker.nextAnticipatedVSyncTimeFrom(timePoint);
475+
EXPECT_THAT(prediction, Ge(timePoint));
476+
}
477+
478+
// See b/145667109, and comment in prod code under test.
479+
TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) {
480+
SET_FLAG_FOR_TEST(flags::vsync_predictor_recovery, false);
481+
std::vector<nsecs_t> const simulatedVsyncs{
482+
158929578733000,
483+
158929306806205, // oldest TS in ringbuffer
484+
158929650879052, 158929661969209, 158929684198847, 158929695268171, 158929706370359,
485+
};
486+
auto const idealPeriod = 11111111;
412487
auto const expectedPeriod = 11113919;
413488
auto const expectedIntercept = -1195945;
414489

@@ -421,9 +496,9 @@ TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) {
421496
EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
422497
EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
423498

424-
// (timePoint - oldestTS) % expectedPeriod works out to be: 395334
425-
// (timePoint - oldestTS) / expectedPeriod works out to be: 38.96
426-
// so failure to account for the offset will floor the ordinal to 38, which was in the past.
499+
// (timePoint - oldestTS) % expectedPeriod works out to be: 10702663
500+
// (timePoint - oldestTS) / expectedPeriod works out to be: 37.96
501+
// so failure to account for the offset will floor the ordinal to 37, which was in the past.
427502
auto const timePoint = 158929728723871;
428503
auto const prediction = tracker.nextAnticipatedVSyncTimeFrom(timePoint);
429504
EXPECT_THAT(prediction, Ge(timePoint));

0 commit comments

Comments
 (0)