@@ -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+
307354TEST_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