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