xref: /aosp_15_r20/frameworks/native/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
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 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
18 
19 #include <vector>
20 
21 #include <android-base/stringprintf.h>
22 #include <common/trace.h>
23 #include <ftl/concat.h>
24 #include <log/log_main.h>
25 
26 #include <scheduler/TimeKeeper.h>
27 
28 #include <common/FlagManager.h>
29 #include "VSyncDispatchTimerQueue.h"
30 #include "VSyncTracker.h"
31 
32 #undef LOG_TAG
33 #define LOG_TAG "VSyncDispatch"
34 
35 namespace android::scheduler {
36 
37 using base::StringAppendF;
38 
39 namespace {
40 
getExpectedCallbackTime(nsecs_t nextVsyncTime,const VSyncDispatch::ScheduleTiming & timing)41 ScheduleResult getExpectedCallbackTime(nsecs_t nextVsyncTime,
42                                        const VSyncDispatch::ScheduleTiming& timing) {
43     return {TimePoint::fromNs(nextVsyncTime - timing.readyDuration - timing.workDuration),
44             TimePoint::fromNs(nextVsyncTime)};
45 }
46 
traceEntry(const VSyncDispatchTimerQueueEntry & entry,nsecs_t now)47 void traceEntry(const VSyncDispatchTimerQueueEntry& entry, nsecs_t now) {
48     if (!SFTRACE_ENABLED() || !entry.wakeupTime().has_value() || !entry.targetVsync().has_value()) {
49         return;
50     }
51 
52     ftl::Concat trace(ftl::truncated<5>(entry.name()), " alarm in ",
53                       ns2us(*entry.wakeupTime() - now), "us; VSYNC in ",
54                       ns2us(*entry.targetVsync() - now), "us");
55     SFTRACE_FORMAT_INSTANT(trace.c_str());
56 }
57 
58 } // namespace
59 
60 VSyncDispatch::~VSyncDispatch() = default;
61 VSyncTracker::~VSyncTracker() = default;
62 
VSyncDispatchTimerQueueEntry(std::string name,VSyncDispatch::Callback callback,nsecs_t minVsyncDistance)63 VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string name,
64                                                            VSyncDispatch::Callback callback,
65                                                            nsecs_t minVsyncDistance)
66       : mName(std::move(name)),
67         mCallback(std::move(callback)),
68         mMinVsyncDistance(minVsyncDistance) {}
69 
lastExecutedVsyncTarget() const70 std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
71     return mLastDispatchTime;
72 }
73 
name() const74 std::string_view VSyncDispatchTimerQueueEntry::name() const {
75     return mName;
76 }
77 
wakeupTime() const78 std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::wakeupTime() const {
79     if (!mArmedInfo) {
80         return {};
81     }
82     return {mArmedInfo->mActualWakeupTime};
83 }
84 
readyTime() const85 std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::readyTime() const {
86     if (!mArmedInfo) {
87         return {};
88     }
89     return {mArmedInfo->mActualReadyTime};
90 }
91 
targetVsync() const92 std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::targetVsync() const {
93     if (!mArmedInfo) {
94         return {};
95     }
96     return {mArmedInfo->mActualVsyncTime};
97 }
98 
schedule(VSyncDispatch::ScheduleTiming timing,VSyncTracker & tracker,nsecs_t now)99 ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
100                                                       VSyncTracker& tracker, nsecs_t now) {
101     SFTRACE_NAME("VSyncDispatchTimerQueueEntry::schedule");
102     auto nextVsyncTime =
103             tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync,
104                                                           now + timing.workDuration +
105                                                                   timing.readyDuration),
106                                                  timing.committedVsyncOpt.value_or(
107                                                          timing.lastVsync));
108     auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
109 
110     bool const wouldSkipAVsyncTarget =
111             mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
112     bool const wouldSkipAWakeup =
113             mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
114     SFTRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(),
115                            wouldSkipAVsyncTarget, wouldSkipAWakeup);
116     if (FlagManager::getInstance().dont_skip_on_early_ro()) {
117         if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
118             nextVsyncTime = mArmedInfo->mActualVsyncTime;
119         } else {
120             nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime);
121         }
122         nextWakeupTime = std::max(now, nextVsyncTime - timing.workDuration - timing.readyDuration);
123     } else {
124         if (wouldSkipAVsyncTarget && wouldSkipAWakeup) {
125             return getExpectedCallbackTime(nextVsyncTime, timing);
126         }
127         nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime);
128         nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
129     }
130 
131     auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
132     mScheduleTiming = timing;
133     mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
134     return ScheduleResult{TimePoint::fromNs(nextWakeupTime), TimePoint::fromNs(nextVsyncTime)};
135 }
136 
addPendingWorkloadUpdate(VSyncTracker & tracker,nsecs_t now,VSyncDispatch::ScheduleTiming timing)137 ScheduleResult VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(
138         VSyncTracker& tracker, nsecs_t now, VSyncDispatch::ScheduleTiming timing) {
139     mWorkloadUpdateInfo = timing;
140     const auto armedInfo = getArmedInfo(tracker, now, timing, mArmedInfo);
141     return {TimePoint::fromNs(armedInfo.mActualWakeupTime),
142             TimePoint::fromNs(armedInfo.mActualVsyncTime)};
143 }
144 
hasPendingWorkloadUpdate() const145 bool VSyncDispatchTimerQueueEntry::hasPendingWorkloadUpdate() const {
146     return mWorkloadUpdateInfo.has_value();
147 }
148 
adjustVsyncIfNeeded(VSyncTracker & tracker,nsecs_t nextVsyncTime) const149 nsecs_t VSyncDispatchTimerQueueEntry::adjustVsyncIfNeeded(VSyncTracker& tracker,
150                                                           nsecs_t nextVsyncTime) const {
151     bool const alreadyDispatchedForVsync = mLastDispatchTime &&
152             ((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime &&
153              (*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime);
154     const nsecs_t currentPeriod = tracker.currentPeriod();
155     bool const nextVsyncTooClose = mLastDispatchTime &&
156             (nextVsyncTime - *mLastDispatchTime + mMinVsyncDistance) <= currentPeriod;
157     if (alreadyDispatchedForVsync) {
158         SFTRACE_FORMAT_INSTANT("alreadyDispatchedForVsync");
159         return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance,
160                                                     *mLastDispatchTime);
161     }
162 
163     if (nextVsyncTooClose) {
164         SFTRACE_FORMAT_INSTANT("nextVsyncTooClose");
165         return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod,
166                                                     *mLastDispatchTime + currentPeriod);
167     }
168 
169     return nextVsyncTime;
170 }
171 
getArmedInfo(VSyncTracker & tracker,nsecs_t now,VSyncDispatch::ScheduleTiming timing,std::optional<ArmingInfo> armedInfo) const172 auto VSyncDispatchTimerQueueEntry::getArmedInfo(VSyncTracker& tracker, nsecs_t now,
173                                                 VSyncDispatch::ScheduleTiming timing,
174                                                 std::optional<ArmingInfo> armedInfo) const
175         -> ArmingInfo {
176     SFTRACE_NAME("VSyncDispatchTimerQueueEntry::getArmedInfo");
177     const auto earliestReadyBy = now + timing.workDuration + timing.readyDuration;
178     const auto earliestVsync = std::max(earliestReadyBy, timing.lastVsync);
179 
180     const auto nextVsyncTime =
181             adjustVsyncIfNeeded(tracker, /*nextVsyncTime*/
182                                 tracker.nextAnticipatedVSyncTimeFrom(earliestVsync,
183                                                                      timing.lastVsync));
184     const auto nextReadyTime = nextVsyncTime - timing.readyDuration;
185     const auto nextWakeupTime = nextReadyTime - timing.workDuration;
186 
187     if (FlagManager::getInstance().dont_skip_on_early_ro()) {
188         bool const wouldSkipAVsyncTarget =
189                 armedInfo && (nextVsyncTime > (armedInfo->mActualVsyncTime + mMinVsyncDistance));
190         bool const wouldSkipAWakeup =
191                 armedInfo && (nextWakeupTime > (armedInfo->mActualWakeupTime + mMinVsyncDistance));
192         SFTRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(),
193                                wouldSkipAVsyncTarget, wouldSkipAWakeup);
194         if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
195             return *armedInfo;
196         }
197     }
198 
199     return ArmingInfo{nextWakeupTime, nextVsyncTime, nextReadyTime};
200 }
201 
update(VSyncTracker & tracker,nsecs_t now)202 void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
203     SFTRACE_NAME("VSyncDispatchTimerQueueEntry::update");
204     if (!mArmedInfo && !mWorkloadUpdateInfo) {
205         return;
206     }
207 
208     if (mWorkloadUpdateInfo) {
209         const auto workDelta = mWorkloadUpdateInfo->workDuration - mScheduleTiming.workDuration;
210         const auto readyDelta = mWorkloadUpdateInfo->readyDuration - mScheduleTiming.readyDuration;
211         const auto lastVsyncDelta = mWorkloadUpdateInfo->lastVsync - mScheduleTiming.lastVsync;
212         const auto lastCommittedVsyncDelta =
213                 mWorkloadUpdateInfo->committedVsyncOpt.value_or(mWorkloadUpdateInfo->lastVsync) -
214                 mScheduleTiming.committedVsyncOpt.value_or(mScheduleTiming.lastVsync);
215         SFTRACE_FORMAT_INSTANT("Workload updated workDelta=%" PRId64 " readyDelta=%" PRId64
216                                " lastVsyncDelta=%" PRId64 " committedVsyncDelta=%" PRId64,
217                                workDelta, readyDelta, lastVsyncDelta, lastCommittedVsyncDelta);
218         mScheduleTiming = *mWorkloadUpdateInfo;
219         mWorkloadUpdateInfo.reset();
220     }
221 
222     mArmedInfo = getArmedInfo(tracker, now, mScheduleTiming, mArmedInfo);
223 }
224 
disarm()225 void VSyncDispatchTimerQueueEntry::disarm() {
226     mArmedInfo.reset();
227 }
228 
executing()229 nsecs_t VSyncDispatchTimerQueueEntry::executing() {
230     mLastDispatchTime = mArmedInfo->mActualVsyncTime;
231     disarm();
232     return *mLastDispatchTime;
233 }
234 
callback(nsecs_t vsyncTimestamp,nsecs_t wakeupTimestamp,nsecs_t deadlineTimestamp)235 void VSyncDispatchTimerQueueEntry::callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp,
236                                             nsecs_t deadlineTimestamp) {
237     {
238         std::lock_guard<std::mutex> lk(mRunningMutex);
239         mRunning = true;
240     }
241 
242     mCallback(vsyncTimestamp, wakeupTimestamp, deadlineTimestamp);
243 
244     std::lock_guard<std::mutex> lk(mRunningMutex);
245     mRunning = false;
246     mCv.notify_all();
247 }
248 
ensureNotRunning()249 void VSyncDispatchTimerQueueEntry::ensureNotRunning() {
250     std::unique_lock<std::mutex> lk(mRunningMutex);
251     mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; });
252 }
253 
dump(std::string & result) const254 void VSyncDispatchTimerQueueEntry::dump(std::string& result) const {
255     std::lock_guard<std::mutex> lk(mRunningMutex);
256     std::string armedInfo;
257     if (mArmedInfo) {
258         StringAppendF(&armedInfo,
259                       "[wake up in %.2fms deadline in %.2fms for vsync %.2fms from now]",
260                       (mArmedInfo->mActualWakeupTime - systemTime()) / 1e6f,
261                       (mArmedInfo->mActualReadyTime - systemTime()) / 1e6f,
262                       (mArmedInfo->mActualVsyncTime - systemTime()) / 1e6f);
263     }
264 
265     StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
266                   mRunning ? "(in callback function)" : "", armedInfo.c_str());
267     StringAppendF(&result,
268                   "\t\t\tworkDuration: %.2fms readyDuration: %.2fms "
269                   "lastVsync: %.2fms relative to now "
270                   "committedVsync: %.2fms relative to now\n",
271                   mScheduleTiming.workDuration / 1e6f, mScheduleTiming.readyDuration / 1e6f,
272                   (mScheduleTiming.lastVsync - systemTime()) / 1e6f,
273                   (mScheduleTiming.committedVsyncOpt.value_or(mScheduleTiming.lastVsync) -
274                    systemTime()) /
275                           1e6f);
276 
277     if (mLastDispatchTime) {
278         StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
279                       (systemTime() - *mLastDispatchTime) / 1e6f);
280     } else {
281         StringAppendF(&result, "\t\t\tmLastDispatchTime unknown\n");
282     }
283 }
284 
VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,VsyncSchedule::TrackerPtr tracker,nsecs_t timerSlack,nsecs_t minVsyncDistance)285 VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
286                                                  VsyncSchedule::TrackerPtr tracker,
287                                                  nsecs_t timerSlack, nsecs_t minVsyncDistance)
288       : mTimeKeeper(std::move(tk)),
289         mTracker(std::move(tracker)),
290         mTimerSlack(timerSlack),
291         mMinVsyncDistance(minVsyncDistance) {}
292 
~VSyncDispatchTimerQueue()293 VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
294     std::lock_guard lock(mMutex);
295     mRunning = false;
296     cancelTimer();
297     for (auto& [_, entry] : mCallbacks) {
298         ALOGE("Forgot to unregister a callback on VSyncDispatch!");
299         entry->ensureNotRunning();
300     }
301 }
302 
cancelTimer()303 void VSyncDispatchTimerQueue::cancelTimer() {
304     mIntendedWakeupTime = kInvalidTime;
305     mTimeKeeper->alarmCancel();
306 }
307 
setTimer(nsecs_t targetTime,nsecs_t)308 void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t /*now*/) {
309     mIntendedWakeupTime = targetTime;
310     mTimeKeeper->alarmAt(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
311                          mIntendedWakeupTime);
312     mLastTimerSchedule = mTimeKeeper->now();
313 }
314 
rearmTimer(nsecs_t now)315 void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) {
316     rearmTimerSkippingUpdateFor(now, mCallbacks.cend());
317 }
318 
rearmTimerSkippingUpdateFor(nsecs_t now,CallbackMap::const_iterator skipUpdateIt)319 void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
320         nsecs_t now, CallbackMap::const_iterator skipUpdateIt) {
321     SFTRACE_CALL();
322     std::optional<nsecs_t> min;
323     std::optional<nsecs_t> targetVsync;
324     std::optional<std::string_view> nextWakeupName;
325     for (auto it = mCallbacks.cbegin(); it != mCallbacks.cend(); ++it) {
326         auto& callback = it->second;
327         if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) {
328             continue;
329         }
330 
331         if (it != skipUpdateIt) {
332             callback->update(*mTracker, now);
333         }
334 
335         traceEntry(*callback, now);
336 
337         const auto wakeupTime = *callback->wakeupTime();
338         if (!min || *min > wakeupTime) {
339             nextWakeupName = callback->name();
340             min = wakeupTime;
341             targetVsync = callback->targetVsync();
342         }
343     }
344 
345     if (min && min < mIntendedWakeupTime) {
346         setTimer(*min, now);
347     } else {
348         SFTRACE_NAME("cancel timer");
349         cancelTimer();
350     }
351 }
352 
timerCallback()353 void VSyncDispatchTimerQueue::timerCallback() {
354     SFTRACE_CALL();
355     struct Invocation {
356         std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
357         nsecs_t vsyncTimestamp;
358         nsecs_t wakeupTimestamp;
359         nsecs_t deadlineTimestamp;
360     };
361     std::vector<Invocation> invocations;
362     {
363         std::lock_guard lock(mMutex);
364         if (!mRunning) {
365             ALOGD("TimerQueue is not running. Skipping callback.");
366             return;
367         }
368         auto const now = mTimeKeeper->now();
369         mLastTimerCallback = now;
370         for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
371             auto& callback = it->second;
372             auto const wakeupTime = callback->wakeupTime();
373             if (!wakeupTime) {
374                 continue;
375             }
376 
377             traceEntry(*callback, now);
378 
379             auto const readyTime = callback->readyTime();
380             auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0));
381             if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
382                 callback->executing();
383                 invocations.emplace_back(Invocation{callback, *callback->lastExecutedVsyncTarget(),
384                                                     *wakeupTime, *readyTime});
385             }
386         }
387 
388         mIntendedWakeupTime = kInvalidTime;
389         rearmTimer(mTimeKeeper->now());
390     }
391 
392     for (auto const& invocation : invocations) {
393         ftl::Concat trace(ftl::truncated<5>(invocation.callback->name()));
394         SFTRACE_FORMAT("%s: %s", __func__, trace.c_str());
395         invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp,
396                                       invocation.deadlineTimestamp);
397     }
398 }
399 
registerCallback(Callback callback,std::string callbackName)400 VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
401         Callback callback, std::string callbackName) {
402     std::lock_guard lock(mMutex);
403     return mCallbacks
404             .try_emplace(++mCallbackToken,
405                          std::make_shared<VSyncDispatchTimerQueueEntry>(std::move(callbackName),
406                                                                         std::move(callback),
407                                                                         mMinVsyncDistance))
408             .first->first;
409 }
410 
unregisterCallback(CallbackToken token)411 void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
412     std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr;
413     {
414         std::lock_guard lock(mMutex);
415         auto it = mCallbacks.find(token);
416         if (it != mCallbacks.end()) {
417             entry = it->second;
418             mCallbacks.erase(it->first);
419         }
420     }
421 
422     if (entry) {
423         entry->ensureNotRunning();
424     }
425 }
426 
schedule(CallbackToken token,ScheduleTiming scheduleTiming)427 std::optional<ScheduleResult> VSyncDispatchTimerQueue::schedule(CallbackToken token,
428                                                                 ScheduleTiming scheduleTiming) {
429     std::lock_guard lock(mMutex);
430     return scheduleLocked(token, scheduleTiming);
431 }
432 
scheduleLocked(CallbackToken token,ScheduleTiming scheduleTiming)433 std::optional<ScheduleResult> VSyncDispatchTimerQueue::scheduleLocked(
434         CallbackToken token, ScheduleTiming scheduleTiming) {
435     auto it = mCallbacks.find(token);
436     if (it == mCallbacks.end()) {
437         return {};
438     }
439     auto& callback = it->second;
440     auto const now = mTimeKeeper->now();
441 
442     /* If the timer thread will run soon, we'll apply this work update via the callback
443      * timer recalculation to avoid cancelling a callback that is about to fire. */
444     auto const rearmImminent = now > mIntendedWakeupTime;
445     if (CC_UNLIKELY(rearmImminent)) {
446         return callback->addPendingWorkloadUpdate(*mTracker, now, scheduleTiming);
447     }
448 
449     const auto result = callback->schedule(scheduleTiming, *mTracker, now);
450 
451     if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
452         rearmTimerSkippingUpdateFor(now, it);
453     }
454 
455     return result;
456 }
457 
update(CallbackToken token,ScheduleTiming scheduleTiming)458 std::optional<ScheduleResult> VSyncDispatchTimerQueue::update(CallbackToken token,
459                                                               ScheduleTiming scheduleTiming) {
460     std::lock_guard lock(mMutex);
461     const auto it = mCallbacks.find(token);
462     if (it == mCallbacks.end()) {
463         return {};
464     }
465 
466     auto& callback = it->second;
467     if (!callback->targetVsync().has_value()) {
468         return {};
469     }
470 
471     return scheduleLocked(token, scheduleTiming);
472 }
473 
cancel(CallbackToken token)474 CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
475     std::lock_guard lock(mMutex);
476 
477     auto it = mCallbacks.find(token);
478     if (it == mCallbacks.end()) {
479         return CancelResult::Error;
480     }
481     auto& callback = it->second;
482 
483     auto const wakeupTime = callback->wakeupTime();
484     if (wakeupTime) {
485         callback->disarm();
486 
487         if (*wakeupTime == mIntendedWakeupTime) {
488             mIntendedWakeupTime = kInvalidTime;
489             rearmTimer(mTimeKeeper->now());
490         }
491         return CancelResult::Cancelled;
492     }
493     return CancelResult::TooLate;
494 }
495 
dump(std::string & result) const496 void VSyncDispatchTimerQueue::dump(std::string& result) const {
497     std::lock_guard lock(mMutex);
498     StringAppendF(&result, "\tTimer:\n");
499     mTimeKeeper->dump(result);
500     StringAppendF(&result, "\tmTimerSlack: %.2fms mMinVsyncDistance: %.2fms\n", mTimerSlack / 1e6f,
501                   mMinVsyncDistance / 1e6f);
502     StringAppendF(&result, "\tmIntendedWakeupTime: %.2fms from now\n",
503                   (mIntendedWakeupTime - mTimeKeeper->now()) / 1e6f);
504     StringAppendF(&result, "\tmLastTimerCallback: %.2fms ago mLastTimerSchedule: %.2fms ago\n",
505                   (mTimeKeeper->now() - mLastTimerCallback) / 1e6f,
506                   (mTimeKeeper->now() - mLastTimerSchedule) / 1e6f);
507     StringAppendF(&result, "\tCallbacks:\n");
508     for (const auto& [token, entry] : mCallbacks) {
509         entry->dump(result);
510     }
511 }
512 
VSyncCallbackRegistration(std::shared_ptr<VSyncDispatch> dispatch,VSyncDispatch::Callback callback,std::string callbackName)513 VSyncCallbackRegistration::VSyncCallbackRegistration(std::shared_ptr<VSyncDispatch> dispatch,
514                                                      VSyncDispatch::Callback callback,
515                                                      std::string callbackName)
516       : mDispatch(std::move(dispatch)),
517         mToken(mDispatch->registerCallback(std::move(callback), std::move(callbackName))) {}
518 
VSyncCallbackRegistration(VSyncCallbackRegistration && other)519 VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other)
520       : mDispatch(std::move(other.mDispatch)), mToken(std::exchange(other.mToken, std::nullopt)) {}
521 
operator =(VSyncCallbackRegistration && other)522 VSyncCallbackRegistration& VSyncCallbackRegistration::operator=(VSyncCallbackRegistration&& other) {
523     if (this == &other) return *this;
524     if (mToken) {
525         mDispatch->unregisterCallback(*mToken);
526     }
527     mDispatch = std::move(other.mDispatch);
528     mToken = std::exchange(other.mToken, std::nullopt);
529     return *this;
530 }
531 
~VSyncCallbackRegistration()532 VSyncCallbackRegistration::~VSyncCallbackRegistration() {
533     if (mToken) mDispatch->unregisterCallback(*mToken);
534 }
535 
schedule(VSyncDispatch::ScheduleTiming scheduleTiming)536 std::optional<ScheduleResult> VSyncCallbackRegistration::schedule(
537         VSyncDispatch::ScheduleTiming scheduleTiming) {
538     if (!mToken) {
539         return std::nullopt;
540     }
541     return mDispatch->schedule(*mToken, scheduleTiming);
542 }
543 
update(VSyncDispatch::ScheduleTiming scheduleTiming)544 std::optional<ScheduleResult> VSyncCallbackRegistration::update(
545         VSyncDispatch::ScheduleTiming scheduleTiming) {
546     if (!mToken) {
547         return std::nullopt;
548     }
549     return mDispatch->update(*mToken, scheduleTiming);
550 }
551 
cancel()552 CancelResult VSyncCallbackRegistration::cancel() {
553     if (!mToken) {
554         return CancelResult::Error;
555     }
556     return mDispatch->cancel(*mToken);
557 }
558 
559 } // namespace android::scheduler
560