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