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