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