• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // TODO(b/129481165): remove the #pragma below and fix conversion issues
18 #pragma clang diagnostic push
19 #pragma clang diagnostic ignored "-Wextra"
20 
21 #undef LOG_TAG
22 #define LOG_TAG "VSyncPredictor"
23 
24 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
25 
26 #include <algorithm>
27 #include <chrono>
28 #include <sstream>
29 
30 #include <android-base/logging.h>
31 #include <android-base/stringprintf.h>
32 #include <common/FlagManager.h>
33 #include <cutils/compiler.h>
34 #include <cutils/properties.h>
35 #include <ftl/concat.h>
36 #include <gui/TraceUtils.h>
37 #include <utils/Log.h>
38 
39 #include "RefreshRateSelector.h"
40 #include "VSyncPredictor.h"
41 
42 namespace android::scheduler {
43 
44 using base::StringAppendF;
45 
46 static auto constexpr kMaxPercent = 100u;
47 
48 namespace {
numVsyncsPerFrame(const ftl::NonNull<DisplayModePtr> & displayModePtr)49 int numVsyncsPerFrame(const ftl::NonNull<DisplayModePtr>& displayModePtr) {
50     const auto idealPeakRefreshPeriod = displayModePtr->getPeakFps().getPeriodNsecs();
51     const auto idealRefreshPeriod = displayModePtr->getVsyncRate().getPeriodNsecs();
52     return static_cast<int>(std::round(static_cast<float>(idealPeakRefreshPeriod) /
53                                        static_cast<float>(idealRefreshPeriod)));
54 }
55 } // namespace
56 
57 VSyncPredictor::~VSyncPredictor() = default;
58 
VSyncPredictor(std::unique_ptr<Clock> clock,ftl::NonNull<DisplayModePtr> modePtr,size_t historySize,size_t minimumSamplesForPrediction,uint32_t outlierTolerancePercent)59 VSyncPredictor::VSyncPredictor(std::unique_ptr<Clock> clock, ftl::NonNull<DisplayModePtr> modePtr,
60                                size_t historySize, size_t minimumSamplesForPrediction,
61                                uint32_t outlierTolerancePercent)
62       : mClock(std::move(clock)),
63         mId(modePtr->getPhysicalDisplayId()),
64         mTraceOn(property_get_bool("debug.sf.vsp_trace", false)),
65         kHistorySize(historySize),
66         kMinimumSamplesForPrediction(minimumSamplesForPrediction),
67         kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)),
68         mDisplayModePtr(modePtr),
69         mNumVsyncsForFrame(numVsyncsPerFrame(mDisplayModePtr)) {
70     resetModel();
71 }
72 
traceInt64If(const char * name,int64_t value) const73 inline void VSyncPredictor::traceInt64If(const char* name, int64_t value) const {
74     if (CC_UNLIKELY(mTraceOn)) {
75         traceInt64(name, value);
76     }
77 }
78 
traceInt64(const char * name,int64_t value) const79 inline void VSyncPredictor::traceInt64(const char* name, int64_t value) const {
80     ATRACE_INT64(ftl::Concat(ftl::truncated<14>(name), " ", mId.value).c_str(), value);
81 }
82 
next(size_t i) const83 inline size_t VSyncPredictor::next(size_t i) const {
84     return (i + 1) % mTimestamps.size();
85 }
86 
idealPeriod() const87 nsecs_t VSyncPredictor::idealPeriod() const {
88     return mDisplayModePtr->getVsyncRate().getPeriodNsecs();
89 }
90 
validate(nsecs_t timestamp) const91 bool VSyncPredictor::validate(nsecs_t timestamp) const {
92     if (mLastTimestampIndex < 0 || mTimestamps.empty()) {
93         return true;
94     }
95 
96     const auto aValidTimestamp = mTimestamps[mLastTimestampIndex];
97     const auto percent =
98             (timestamp - aValidTimestamp) % idealPeriod() * kMaxPercent / idealPeriod();
99     if (percent >= kOutlierTolerancePercent &&
100         percent <= (kMaxPercent - kOutlierTolerancePercent)) {
101         ATRACE_FORMAT_INSTANT("timestamp is not aligned with model");
102         return false;
103     }
104 
105     const auto iter = std::min_element(mTimestamps.begin(), mTimestamps.end(),
106                                        [timestamp](nsecs_t a, nsecs_t b) {
107                                            return std::abs(timestamp - a) < std::abs(timestamp - b);
108                                        });
109     const auto distancePercent = std::abs(*iter - timestamp) * kMaxPercent / idealPeriod();
110     if (distancePercent < kOutlierTolerancePercent) {
111         // duplicate timestamp
112         ATRACE_FORMAT_INSTANT("duplicate timestamp");
113         return false;
114     }
115     return true;
116 }
117 
currentPeriod() const118 nsecs_t VSyncPredictor::currentPeriod() const {
119     std::lock_guard lock(mMutex);
120     return mRateMap.find(idealPeriod())->second.slope;
121 }
122 
minFramePeriod() const123 Period VSyncPredictor::minFramePeriod() const {
124     if (!FlagManager::getInstance().vrr_config()) {
125         return Period::fromNs(currentPeriod());
126     }
127 
128     std::lock_guard lock(mMutex);
129     return minFramePeriodLocked();
130 }
131 
minFramePeriodLocked() const132 Period VSyncPredictor::minFramePeriodLocked() const {
133     const auto slope = mRateMap.find(idealPeriod())->second.slope;
134     return Period::fromNs(slope * mNumVsyncsForFrame);
135 }
136 
addVsyncTimestamp(nsecs_t timestamp)137 bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
138     ATRACE_CALL();
139 
140     std::lock_guard lock(mMutex);
141 
142     if (!validate(timestamp)) {
143         // VSR could elect to ignore the incongruent timestamp or resetModel(). If ts is ignored,
144         // don't insert this ts into mTimestamps ringbuffer. If we are still
145         // in the learning phase we should just clear all timestamps and start
146         // over.
147         if (mTimestamps.size() < kMinimumSamplesForPrediction) {
148             // Add the timestamp to mTimestamps before clearing it so we could
149             // update mKnownTimestamp based on the new timestamp.
150             mTimestamps.push_back(timestamp);
151             clearTimestamps();
152         } else if (!mTimestamps.empty()) {
153             mKnownTimestamp =
154                     std::max(timestamp, *std::max_element(mTimestamps.begin(), mTimestamps.end()));
155         } else {
156             mKnownTimestamp = timestamp;
157         }
158         ATRACE_FORMAT_INSTANT("timestamp rejected. mKnownTimestamp was %.2fms ago",
159                               (mClock->now() - *mKnownTimestamp) / 1e6f);
160         return false;
161     }
162 
163     if (mTimestamps.size() != kHistorySize) {
164         mTimestamps.push_back(timestamp);
165         mLastTimestampIndex = next(mLastTimestampIndex);
166     } else {
167         mLastTimestampIndex = next(mLastTimestampIndex);
168         mTimestamps[mLastTimestampIndex] = timestamp;
169     }
170 
171     traceInt64If("VSP-ts", timestamp);
172 
173     const size_t numSamples = mTimestamps.size();
174     if (numSamples < kMinimumSamplesForPrediction) {
175         mRateMap[idealPeriod()] = {idealPeriod(), 0};
176         return true;
177     }
178 
179     // This is a 'simple linear regression' calculation of Y over X, with Y being the
180     // vsync timestamps, and X being the ordinal of vsync count.
181     // The calculated slope is the vsync period.
182     // Formula for reference:
183     // Sigma_i: means sum over all timestamps.
184     // mean(variable): statistical mean of variable.
185     // X: snapped ordinal of the timestamp
186     // Y: vsync timestamp
187     //
188     //         Sigma_i( (X_i - mean(X)) * (Y_i - mean(Y) )
189     // slope = -------------------------------------------
190     //         Sigma_i ( X_i - mean(X) ) ^ 2
191     //
192     // intercept = mean(Y) - slope * mean(X)
193     //
194     std::vector<nsecs_t> vsyncTS(numSamples);
195     std::vector<nsecs_t> ordinals(numSamples);
196 
197     // Normalizing to the oldest timestamp cuts down on error in calculating the intercept.
198     const auto oldestTS = *std::min_element(mTimestamps.begin(), mTimestamps.end());
199     auto it = mRateMap.find(idealPeriod());
200     auto const currentPeriod = it->second.slope;
201 
202     // The mean of the ordinals must be precise for the intercept calculation, so scale them up for
203     // fixed-point arithmetic.
204     constexpr int64_t kScalingFactor = 1000;
205 
206     nsecs_t meanTS = 0;
207     nsecs_t meanOrdinal = 0;
208 
209     for (size_t i = 0; i < numSamples; i++) {
210         const auto timestamp = mTimestamps[i] - oldestTS;
211         vsyncTS[i] = timestamp;
212         meanTS += timestamp;
213 
214         const auto ordinal = currentPeriod == 0
215                 ? 0
216                 : (vsyncTS[i] + currentPeriod / 2) / currentPeriod * kScalingFactor;
217         ordinals[i] = ordinal;
218         meanOrdinal += ordinal;
219     }
220 
221     meanTS /= numSamples;
222     meanOrdinal /= numSamples;
223 
224     for (size_t i = 0; i < numSamples; i++) {
225         vsyncTS[i] -= meanTS;
226         ordinals[i] -= meanOrdinal;
227     }
228 
229     nsecs_t top = 0;
230     nsecs_t bottom = 0;
231     for (size_t i = 0; i < numSamples; i++) {
232         top += vsyncTS[i] * ordinals[i];
233         bottom += ordinals[i] * ordinals[i];
234     }
235 
236     if (CC_UNLIKELY(bottom == 0)) {
237         it->second = {idealPeriod(), 0};
238         clearTimestamps();
239         return false;
240     }
241 
242     nsecs_t const anticipatedPeriod = top * kScalingFactor / bottom;
243     nsecs_t const intercept = meanTS - (anticipatedPeriod * meanOrdinal / kScalingFactor);
244 
245     auto const percent = std::abs(anticipatedPeriod - idealPeriod()) * kMaxPercent / idealPeriod();
246     if (percent >= kOutlierTolerancePercent) {
247         it->second = {idealPeriod(), 0};
248         clearTimestamps();
249         return false;
250     }
251 
252     traceInt64If("VSP-period", anticipatedPeriod);
253     traceInt64If("VSP-intercept", intercept);
254 
255     it->second = {anticipatedPeriod, intercept};
256 
257     ALOGV("model update ts %" PRIu64 ": %" PRId64 " slope: %" PRId64 " intercept: %" PRId64,
258           mId.value, timestamp, anticipatedPeriod, intercept);
259     return true;
260 }
261 
snapToVsync(nsecs_t timePoint) const262 nsecs_t VSyncPredictor::snapToVsync(nsecs_t timePoint) const {
263     auto const [slope, intercept] = getVSyncPredictionModelLocked();
264 
265     if (mTimestamps.empty()) {
266         traceInt64("VSP-mode", 1);
267         auto const knownTimestamp = mKnownTimestamp ? *mKnownTimestamp : timePoint;
268         auto const numPeriodsOut = ((timePoint - knownTimestamp) / idealPeriod()) + 1;
269         return knownTimestamp + numPeriodsOut * idealPeriod();
270     }
271 
272     auto const oldest = *std::min_element(mTimestamps.begin(), mTimestamps.end());
273 
274     // See b/145667109, the ordinal calculation must take into account the intercept.
275     auto const zeroPoint = oldest + intercept;
276     auto const ordinalRequest = (timePoint - zeroPoint + slope) / slope;
277     auto const prediction = (ordinalRequest * slope) + intercept + oldest;
278 
279     traceInt64("VSP-mode", 0);
280     traceInt64If("VSP-timePoint", timePoint);
281     traceInt64If("VSP-prediction", prediction);
282 
283     auto const printer = [&, slope = slope, intercept = intercept] {
284         std::stringstream str;
285         str << "prediction made from: " << timePoint << "prediction: " << prediction << " (+"
286             << prediction - timePoint << ") slope: " << slope << " intercept: " << intercept
287             << "oldestTS: " << oldest << " ordinal: " << ordinalRequest;
288         return str.str();
289     };
290 
291     ALOGV("%s", printer().c_str());
292     LOG_ALWAYS_FATAL_IF(prediction < timePoint, "VSyncPredictor: model miscalculation: %s",
293                         printer().c_str());
294 
295     return prediction;
296 }
297 
nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,std::optional<nsecs_t> lastVsyncOpt)298 nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,
299                                                      std::optional<nsecs_t> lastVsyncOpt) {
300     ATRACE_CALL();
301     std::lock_guard lock(mMutex);
302 
303     const auto now = TimePoint::fromNs(mClock->now());
304     purgeTimelines(now);
305 
306     if (lastVsyncOpt && *lastVsyncOpt > timePoint) {
307         timePoint = *lastVsyncOpt;
308     }
309 
310     const auto model = getVSyncPredictionModelLocked();
311     const auto threshold = model.slope / 2;
312     std::optional<Period> minFramePeriodOpt;
313 
314     if (mNumVsyncsForFrame > 1) {
315         minFramePeriodOpt = minFramePeriodLocked();
316     }
317 
318     std::optional<TimePoint> vsyncOpt;
319     for (auto& timeline : mTimelines) {
320         vsyncOpt = timeline.nextAnticipatedVSyncTimeFrom(model, minFramePeriodOpt,
321                                                          snapToVsync(timePoint), mMissedVsync,
322                                                          lastVsyncOpt ? snapToVsync(*lastVsyncOpt -
323                                                                                     threshold)
324                                                                       : lastVsyncOpt);
325         if (vsyncOpt) {
326             break;
327         }
328     }
329     LOG_ALWAYS_FATAL_IF(!vsyncOpt);
330 
331     if (*vsyncOpt > mLastCommittedVsync) {
332         mLastCommittedVsync = *vsyncOpt;
333         ATRACE_FORMAT_INSTANT("mLastCommittedVsync in %.2fms",
334                               float(mLastCommittedVsync.ns() - mClock->now()) / 1e6f);
335     }
336 
337     return vsyncOpt->ns();
338 }
339 
340 /*
341  * Returns whether a given vsync timestamp is in phase with a frame rate.
342  * If the frame rate is not a divisor of the refresh rate, it is always considered in phase.
343  * For example, if the vsync timestamps are (16.6,33.3,50.0,66.6):
344  * isVSyncInPhase(16.6, 30) = true
345  * isVSyncInPhase(33.3, 30) = false
346  * isVSyncInPhase(50.0, 30) = true
347  */
isVSyncInPhase(nsecs_t timePoint,Fps frameRate)348 bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) {
349     if (timePoint == 0) {
350         return true;
351     }
352 
353     std::lock_guard lock(mMutex);
354     const auto model = getVSyncPredictionModelLocked();
355     const nsecs_t period = model.slope;
356     const nsecs_t justBeforeTimePoint = timePoint - period / 2;
357     const auto now = TimePoint::fromNs(mClock->now());
358     const auto vsync = snapToVsync(justBeforeTimePoint);
359 
360     purgeTimelines(now);
361 
362     for (auto& timeline : mTimelines) {
363         if (timeline.validUntil() && timeline.validUntil()->ns() > vsync) {
364             return timeline.isVSyncInPhase(model, vsync, frameRate);
365         }
366     }
367 
368     // The last timeline should always be valid
369     return mTimelines.back().isVSyncInPhase(model, vsync, frameRate);
370 }
371 
setRenderRate(Fps renderRate,bool applyImmediately)372 void VSyncPredictor::setRenderRate(Fps renderRate, bool applyImmediately) {
373     ATRACE_FORMAT("%s %s", __func__, to_string(renderRate).c_str());
374     ALOGV("%s %s: RenderRate %s ", __func__, to_string(mId).c_str(), to_string(renderRate).c_str());
375     std::lock_guard lock(mMutex);
376     const auto prevRenderRate = mRenderRateOpt;
377     mRenderRateOpt = renderRate;
378     const auto renderPeriodDelta =
379             prevRenderRate ? prevRenderRate->getPeriodNsecs() - renderRate.getPeriodNsecs() : 0;
380     if (applyImmediately) {
381         ATRACE_FORMAT_INSTANT("applyImmediately");
382         while (mTimelines.size() > 1) {
383             mTimelines.pop_front();
384         }
385 
386         mTimelines.front().setRenderRate(renderRate);
387         return;
388     }
389 
390     const bool newRenderRateIsHigher = renderPeriodDelta > renderRate.getPeriodNsecs() &&
391             mLastCommittedVsync.ns() - mClock->now() > 2 * renderRate.getPeriodNsecs();
392     if (newRenderRateIsHigher) {
393         ATRACE_FORMAT_INSTANT("newRenderRateIsHigher");
394         mTimelines.clear();
395         mLastCommittedVsync = TimePoint::fromNs(0);
396 
397     } else {
398         mTimelines.back().freeze(
399                 TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2));
400     }
401     mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, renderRate);
402     purgeTimelines(TimePoint::fromNs(mClock->now()));
403 }
404 
setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr)405 void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) {
406     LOG_ALWAYS_FATAL_IF(mId != modePtr->getPhysicalDisplayId(),
407                         "mode does not belong to the display");
408     ATRACE_FORMAT("%s %s", __func__, to_string(*modePtr).c_str());
409     const auto timeout = modePtr->getVrrConfig()
410             ? modePtr->getVrrConfig()->notifyExpectedPresentConfig
411             : std::nullopt;
412     ALOGV("%s %s: DisplayMode %s notifyExpectedPresentTimeout %s", __func__, to_string(mId).c_str(),
413           to_string(*modePtr).c_str(),
414           timeout ? std::to_string(timeout->timeoutNs).c_str() : "N/A");
415     std::lock_guard lock(mMutex);
416 
417     mDisplayModePtr = modePtr;
418     mNumVsyncsForFrame = numVsyncsPerFrame(mDisplayModePtr);
419     traceInt64("VSP-setPeriod", modePtr->getVsyncRate().getPeriodNsecs());
420 
421     static constexpr size_t kSizeLimit = 30;
422     if (CC_UNLIKELY(mRateMap.size() == kSizeLimit)) {
423         mRateMap.erase(mRateMap.begin());
424     }
425 
426     if (mRateMap.find(idealPeriod()) == mRateMap.end()) {
427         mRateMap[idealPeriod()] = {idealPeriod(), 0};
428     }
429 
430     mTimelines.clear();
431     clearTimestamps();
432 }
433 
ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,TimePoint lastConfirmedPresentTime)434 Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,
435                                                       TimePoint lastConfirmedPresentTime) {
436     ATRACE_CALL();
437 
438     if (mNumVsyncsForFrame <= 1) {
439         return 0ns;
440     }
441 
442     const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
443     const auto threshold = currentPeriod / 2;
444     const auto minFramePeriod = minFramePeriodLocked().ns();
445 
446     auto prev = lastConfirmedPresentTime.ns();
447     for (auto& current : mPastExpectedPresentTimes) {
448         if (CC_UNLIKELY(mTraceOn)) {
449             ATRACE_FORMAT_INSTANT("current %.2f past last signaled fence",
450                                   static_cast<float>(current.ns() - lastConfirmedPresentTime.ns()) /
451                                           1e6f);
452         }
453 
454         const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod;
455         if (minPeriodViolation) {
456             ATRACE_NAME("minPeriodViolation");
457             current = TimePoint::fromNs(prev + minFramePeriod);
458             prev = current.ns();
459         } else {
460             break;
461         }
462     }
463 
464     if (!mPastExpectedPresentTimes.empty()) {
465         const auto phase = Duration(mPastExpectedPresentTimes.back() - expectedPresentTime);
466         if (phase > 0ns) {
467             for (auto& timeline : mTimelines) {
468                 timeline.shiftVsyncSequence(phase);
469             }
470             mPastExpectedPresentTimes.clear();
471             return phase;
472         }
473     }
474 
475     return 0ns;
476 }
477 
onFrameBegin(TimePoint expectedPresentTime,TimePoint lastConfirmedPresentTime)478 void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime,
479                                   TimePoint lastConfirmedPresentTime) {
480     ATRACE_NAME("VSyncPredictor::onFrameBegin");
481     std::lock_guard lock(mMutex);
482 
483     if (!mDisplayModePtr->getVrrConfig()) return;
484 
485     if (CC_UNLIKELY(mTraceOn)) {
486         ATRACE_FORMAT_INSTANT("vsync is %.2f past last signaled fence",
487                               static_cast<float>(expectedPresentTime.ns() -
488                                                  lastConfirmedPresentTime.ns()) /
489                                       1e6f);
490     }
491     const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
492     const auto threshold = currentPeriod / 2;
493     mPastExpectedPresentTimes.push_back(expectedPresentTime);
494 
495     while (!mPastExpectedPresentTimes.empty()) {
496         const auto front = mPastExpectedPresentTimes.front().ns();
497         const bool frontIsBeforeConfirmed = front < lastConfirmedPresentTime.ns() + threshold;
498         if (frontIsBeforeConfirmed) {
499             if (CC_UNLIKELY(mTraceOn)) {
500                 ATRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence",
501                                       static_cast<float>(lastConfirmedPresentTime.ns() - front) /
502                                               1e6f);
503             }
504             mPastExpectedPresentTimes.pop_front();
505         } else {
506             break;
507         }
508     }
509 
510     const auto phase = ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
511     if (phase > 0ns) {
512         mMissedVsync = {expectedPresentTime, minFramePeriodLocked()};
513     }
514 }
515 
onFrameMissed(TimePoint expectedPresentTime)516 void VSyncPredictor::onFrameMissed(TimePoint expectedPresentTime) {
517     ATRACE_NAME("VSyncPredictor::onFrameMissed");
518 
519     std::lock_guard lock(mMutex);
520     if (!mDisplayModePtr->getVrrConfig()) return;
521 
522     // We don't know when the frame is going to be presented, so we assume it missed one vsync
523     const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
524     const auto lastConfirmedPresentTime =
525             TimePoint::fromNs(expectedPresentTime.ns() + currentPeriod);
526 
527     const auto phase = ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
528     if (phase > 0ns) {
529         mMissedVsync = {expectedPresentTime, Duration::fromNs(0)};
530     }
531 }
532 
getVSyncPredictionModel() const533 VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
534     std::lock_guard lock(mMutex);
535     return VSyncPredictor::getVSyncPredictionModelLocked();
536 }
537 
getVSyncPredictionModelLocked() const538 VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const {
539     return mRateMap.find(idealPeriod())->second;
540 }
541 
clearTimestamps()542 void VSyncPredictor::clearTimestamps() {
543     ATRACE_CALL();
544 
545     if (!mTimestamps.empty()) {
546         auto const maxRb = *std::max_element(mTimestamps.begin(), mTimestamps.end());
547         if (mKnownTimestamp) {
548             mKnownTimestamp = std::max(*mKnownTimestamp, maxRb);
549         } else {
550             mKnownTimestamp = maxRb;
551         }
552 
553         mTimestamps.clear();
554         mLastTimestampIndex = 0;
555     }
556 
557     mIdealPeriod = Period::fromNs(idealPeriod());
558     if (mTimelines.empty()) {
559         mLastCommittedVsync = TimePoint::fromNs(0);
560         mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt);
561     } else {
562         while (mTimelines.size() > 1) {
563             mTimelines.pop_front();
564         }
565         mTimelines.front().setRenderRate(mRenderRateOpt);
566         // set mLastCommittedVsync to a valid vsync but don't commit too much in the future
567         const auto vsyncOpt = mTimelines.front().nextAnticipatedVSyncTimeFrom(
568             getVSyncPredictionModelLocked(),
569             /* minFramePeriodOpt */ std::nullopt,
570             snapToVsync(mClock->now()), MissedVsync{},
571             /* lastVsyncOpt */ std::nullopt);
572         mLastCommittedVsync = *vsyncOpt;
573     }
574 }
575 
needsMoreSamples() const576 bool VSyncPredictor::needsMoreSamples() const {
577     std::lock_guard lock(mMutex);
578     return mTimestamps.size() < kMinimumSamplesForPrediction;
579 }
580 
resetModel()581 void VSyncPredictor::resetModel() {
582     std::lock_guard lock(mMutex);
583     mRateMap[idealPeriod()] = {idealPeriod(), 0};
584     clearTimestamps();
585 }
586 
dump(std::string & result) const587 void VSyncPredictor::dump(std::string& result) const {
588     std::lock_guard lock(mMutex);
589     StringAppendF(&result, "\tmDisplayModePtr=%s\n", to_string(*mDisplayModePtr).c_str());
590     StringAppendF(&result, "\tRefresh Rate Map:\n");
591     for (const auto& [period, periodInterceptTuple] : mRateMap) {
592         StringAppendF(&result,
593                       "\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n",
594                       period / 1e6f, periodInterceptTuple.slope / 1e6f,
595                       periodInterceptTuple.intercept);
596     }
597     StringAppendF(&result, "\tmTimelines.size()=%zu\n", mTimelines.size());
598 }
599 
purgeTimelines(android::TimePoint now)600 void VSyncPredictor::purgeTimelines(android::TimePoint now) {
601     const auto kEnoughFramesToBreakPhase = 5;
602     if (mRenderRateOpt &&
603         mLastCommittedVsync.ns() + mRenderRateOpt->getPeriodNsecs() * kEnoughFramesToBreakPhase <
604                 mClock->now()) {
605         ATRACE_FORMAT_INSTANT("kEnoughFramesToBreakPhase");
606         mTimelines.clear();
607         mLastCommittedVsync = TimePoint::fromNs(0);
608         mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt);
609         return;
610     }
611 
612     while (mTimelines.size() > 1) {
613         const auto validUntilOpt = mTimelines.front().validUntil();
614         if (validUntilOpt && *validUntilOpt < now) {
615             mTimelines.pop_front();
616         } else {
617             break;
618         }
619     }
620     LOG_ALWAYS_FATAL_IF(mTimelines.empty());
621     LOG_ALWAYS_FATAL_IF(mTimelines.back().validUntil().has_value());
622 }
623 
makeVsyncSequence(TimePoint knownVsync)624 auto VSyncPredictor::VsyncTimeline::makeVsyncSequence(TimePoint knownVsync)
625         -> std::optional<VsyncSequence> {
626     if (knownVsync.ns() == 0) return std::nullopt;
627     return std::make_optional<VsyncSequence>({knownVsync.ns(), 0});
628 }
629 
VsyncTimeline(TimePoint knownVsync,Period idealPeriod,std::optional<Fps> renderRateOpt)630 VSyncPredictor::VsyncTimeline::VsyncTimeline(TimePoint knownVsync, Period idealPeriod,
631                                              std::optional<Fps> renderRateOpt)
632       : mIdealPeriod(idealPeriod),
633         mRenderRateOpt(renderRateOpt),
634         mLastVsyncSequence(makeVsyncSequence(knownVsync)) {}
635 
freeze(TimePoint lastVsync)636 void VSyncPredictor::VsyncTimeline::freeze(TimePoint lastVsync) {
637     LOG_ALWAYS_FATAL_IF(mValidUntil.has_value());
638     ATRACE_FORMAT_INSTANT("renderRate %s valid for %.2f",
639                           mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA",
640                           float(lastVsync.ns() - TimePoint::now().ns()) / 1e6f);
641     mValidUntil = lastVsync;
642 }
643 
nextAnticipatedVSyncTimeFrom(Model model,std::optional<Period> minFramePeriodOpt,nsecs_t vsync,MissedVsync missedVsync,std::optional<nsecs_t> lastVsyncOpt)644 std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTimeFrom(
645         Model model, std::optional<Period> minFramePeriodOpt, nsecs_t vsync,
646         MissedVsync missedVsync, std::optional<nsecs_t> lastVsyncOpt) {
647     ATRACE_FORMAT("renderRate %s", mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA");
648 
649     nsecs_t vsyncTime = snapToVsyncAlignedWithRenderRate(model, vsync);
650     const auto threshold = model.slope / 2;
651     const auto lastFrameMissed =
652             lastVsyncOpt && std::abs(*lastVsyncOpt - missedVsync.vsync.ns()) < threshold;
653     const auto mightBackpressure = minFramePeriodOpt && mRenderRateOpt &&
654             mRenderRateOpt->getPeriod() < 2 * (*minFramePeriodOpt);
655     if (FlagManager::getInstance().vrr_config()) {
656         if (lastFrameMissed) {
657             // If the last frame missed is the last vsync, we already shifted the timeline. Depends
658             // on whether we skipped the frame (onFrameMissed) or not (onFrameBegin) we apply a
659             // different fixup. There is no need to to shift the vsync timeline again.
660             vsyncTime += missedVsync.fixup.ns();
661             ATRACE_FORMAT_INSTANT("lastFrameMissed");
662         } else if (mightBackpressure && lastVsyncOpt) {
663             // lastVsyncOpt is based on the old timeline before we shifted it. we should correct it
664             // first before trying to use it.
665             lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt);
666             const auto vsyncDiff = vsyncTime - *lastVsyncOpt;
667             if (vsyncDiff <= minFramePeriodOpt->ns() - threshold) {
668                 // avoid a duplicate vsync
669                 ATRACE_FORMAT_INSTANT("skipping a vsync to avoid duplicate frame. next in %.2f "
670                                       "which "
671                                       "is %.2f "
672                                       "from "
673                                       "prev. "
674                                       "adjust by %.2f",
675                                       static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f,
676                                       static_cast<float>(vsyncDiff) / 1e6f,
677                                       static_cast<float>(mRenderRateOpt->getPeriodNsecs()) / 1e6f);
678                 vsyncTime += mRenderRateOpt->getPeriodNsecs();
679             }
680         }
681     }
682 
683     ATRACE_FORMAT_INSTANT("vsync in %.2fms", float(vsyncTime - TimePoint::now().ns()) / 1e6f);
684     if (mValidUntil && vsyncTime > mValidUntil->ns()) {
685         ATRACE_FORMAT_INSTANT("no longer valid for vsync in %.2f",
686                               static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f);
687         return std::nullopt;
688     }
689 
690     return TimePoint::fromNs(vsyncTime);
691 }
692 
getVsyncSequenceLocked(Model model,nsecs_t vsync)693 auto VSyncPredictor::VsyncTimeline::getVsyncSequenceLocked(Model model, nsecs_t vsync)
694         -> VsyncSequence {
695     if (!mLastVsyncSequence) return {vsync, 0};
696 
697     const auto [lastVsyncTime, lastVsyncSequence] = *mLastVsyncSequence;
698     const auto vsyncSequence = lastVsyncSequence +
699             static_cast<int64_t>(std::round((vsync - lastVsyncTime) /
700                                             static_cast<float>(model.slope)));
701     return {vsync, vsyncSequence};
702 }
703 
snapToVsyncAlignedWithRenderRate(Model model,nsecs_t vsync)704 nsecs_t VSyncPredictor::VsyncTimeline::snapToVsyncAlignedWithRenderRate(Model model,
705                                                                         nsecs_t vsync) {
706     // update the mLastVsyncSequence for reference point
707     mLastVsyncSequence = getVsyncSequenceLocked(model, vsync);
708 
709     const auto renderRatePhase = [&]() -> int {
710         if (!mRenderRateOpt) return 0;
711         const auto divisor =
712                 RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod.ns()),
713                                                          *mRenderRateOpt);
714         if (divisor <= 1) return 0;
715 
716         int mod = mLastVsyncSequence->seq % divisor;
717         if (mod == 0) return 0;
718 
719         // This is actually a bug fix, but guarded with vrr_config since we found it with this
720         // config
721         if (FlagManager::getInstance().vrr_config()) {
722             if (mod < 0) mod += divisor;
723         }
724 
725         return divisor - mod;
726     }();
727 
728     if (renderRatePhase == 0) {
729         return mLastVsyncSequence->vsyncTime;
730     }
731 
732     return mLastVsyncSequence->vsyncTime + model.slope * renderRatePhase;
733 }
734 
isVSyncInPhase(Model model,nsecs_t vsync,Fps frameRate)735 bool VSyncPredictor::VsyncTimeline::isVSyncInPhase(Model model, nsecs_t vsync, Fps frameRate) {
736     const auto getVsyncIn = [](TimePoint now, nsecs_t timePoint) -> float {
737         return ticks<std::milli, float>(TimePoint::fromNs(timePoint) - now);
738     };
739 
740     Fps displayFps = Fps::fromPeriodNsecs(mIdealPeriod.ns());
741     const auto divisor = RefreshRateSelector::getFrameRateDivisor(displayFps, frameRate);
742     const auto now = TimePoint::now();
743 
744     if (divisor <= 1) {
745         return true;
746     }
747     const auto vsyncSequence = getVsyncSequenceLocked(model, vsync);
748     ATRACE_FORMAT_INSTANT("vsync in: %.2f sequence: %" PRId64 " divisor: %zu",
749                           getVsyncIn(now, vsyncSequence.vsyncTime), vsyncSequence.seq, divisor);
750     return vsyncSequence.seq % divisor == 0;
751 }
752 
shiftVsyncSequence(Duration phase)753 void VSyncPredictor::VsyncTimeline::shiftVsyncSequence(Duration phase) {
754     if (mLastVsyncSequence) {
755         ATRACE_FORMAT_INSTANT("adjusting vsync by %.2f", static_cast<float>(phase.ns()) / 1e6f);
756         mLastVsyncSequence->vsyncTime += phase.ns();
757     }
758 }
759 
760 } // namespace android::scheduler
761 
762 // TODO(b/129481165): remove the #pragma below and fix conversion issues
763 #pragma clang diagnostic pop // ignored "-Wextra"
764