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         mLastTimerCallback = mTimeKeeper->now();
247         for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
248             auto& callback = it->second;
249             auto const wakeupTime = callback->wakeupTime();
250             if (!wakeupTime) {
251                 continue;
252             }
253 
254             if (*wakeupTime < mIntendedWakeupTime + mTimerSlack) {
255                 callback->executing();
256                 invocations.emplace_back(
257                         Invocation{callback, *callback->lastExecutedVsyncTarget(), *wakeupTime});
258             }
259         }
260 
261         mIntendedWakeupTime = kInvalidTime;
262         rearmTimer(mTimeKeeper->now());
263     }
264 
265     for (auto const& invocation : invocations) {
266         invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp);
267     }
268 }
269 
registerCallback(Callback const & callbackFn,std::string callbackName)270 VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
271         Callback const& callbackFn, std::string callbackName) {
272     std::lock_guard<decltype(mMutex)> lk(mMutex);
273     return CallbackToken{
274             mCallbacks
275                     .emplace(++mCallbackToken,
276                              std::make_shared<VSyncDispatchTimerQueueEntry>(callbackName,
277                                                                             callbackFn,
278                                                                             mMinVsyncDistance))
279                     .first->first};
280 }
281 
unregisterCallback(CallbackToken token)282 void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
283     std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr;
284     {
285         std::lock_guard<decltype(mMutex)> lk(mMutex);
286         auto it = mCallbacks.find(token);
287         if (it != mCallbacks.end()) {
288             entry = it->second;
289             mCallbacks.erase(it);
290         }
291     }
292 
293     if (entry) {
294         entry->ensureNotRunning();
295     }
296 }
297 
schedule(CallbackToken token,nsecs_t workDuration,nsecs_t earliestVsync)298 ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t workDuration,
299                                                  nsecs_t earliestVsync) {
300     auto result = ScheduleResult::Error;
301     {
302         std::lock_guard<decltype(mMutex)> lk(mMutex);
303 
304         auto it = mCallbacks.find(token);
305         if (it == mCallbacks.end()) {
306             return result;
307         }
308         auto& callback = it->second;
309         auto const now = mTimeKeeper->now();
310 
311         /* If the timer thread will run soon, we'll apply this work update via the callback
312          * timer recalculation to avoid cancelling a callback that is about to fire. */
313         auto const rearmImminent = now > mIntendedWakeupTime;
314         if (CC_UNLIKELY(rearmImminent)) {
315             callback->addPendingWorkloadUpdate(workDuration, earliestVsync);
316             return ScheduleResult::Scheduled;
317         }
318 
319         result = callback->schedule(workDuration, earliestVsync, mTracker, now);
320         if (result == ScheduleResult::CannotSchedule) {
321             return result;
322         }
323 
324         if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
325             rearmTimerSkippingUpdateFor(now, it);
326         }
327     }
328 
329     return result;
330 }
331 
cancel(CallbackToken token)332 CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
333     std::lock_guard<decltype(mMutex)> lk(mMutex);
334 
335     auto it = mCallbacks.find(token);
336     if (it == mCallbacks.end()) {
337         return CancelResult::Error;
338     }
339     auto& callback = it->second;
340 
341     auto const wakeupTime = callback->wakeupTime();
342     if (wakeupTime) {
343         callback->disarm();
344 
345         if (*wakeupTime == mIntendedWakeupTime) {
346             mIntendedWakeupTime = kInvalidTime;
347             rearmTimer(mTimeKeeper->now());
348         }
349         return CancelResult::Cancelled;
350     }
351     return CancelResult::TooLate;
352 }
353 
dump(std::string & result) const354 void VSyncDispatchTimerQueue::dump(std::string& result) const {
355     std::lock_guard<decltype(mMutex)> lk(mMutex);
356     StringAppendF(&result, "\tTimer:\n");
357     mTimeKeeper->dump(result);
358     StringAppendF(&result, "\tmTimerSlack: %.2fms mMinVsyncDistance: %.2fms\n", mTimerSlack / 1e6f,
359                   mMinVsyncDistance / 1e6f);
360     StringAppendF(&result, "\tmIntendedWakeupTime: %.2fms from now\n",
361                   (mIntendedWakeupTime - mTimeKeeper->now()) / 1e6f);
362     StringAppendF(&result, "\tmLastTimerCallback: %.2fms ago mLastTimerSchedule: %.2fms ago\n",
363                   (mTimeKeeper->now() - mLastTimerCallback) / 1e6f,
364                   (mTimeKeeper->now() - mLastTimerSchedule) / 1e6f);
365     StringAppendF(&result, "\tCallbacks:\n");
366     for (const auto& [token, entry] : mCallbacks) {
367         entry->dump(result);
368     }
369 }
370 
VSyncCallbackRegistration(VSyncDispatch & dispatch,VSyncDispatch::Callback const & callbackFn,std::string const & callbackName)371 VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
372                                                      VSyncDispatch::Callback const& callbackFn,
373                                                      std::string const& callbackName)
374       : mDispatch(dispatch),
375         mToken(dispatch.registerCallback(callbackFn, callbackName)),
376         mValidToken(true) {}
377 
VSyncCallbackRegistration(VSyncCallbackRegistration && other)378 VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other)
379       : mDispatch(other.mDispatch),
380         mToken(std::move(other.mToken)),
381         mValidToken(std::move(other.mValidToken)) {
382     other.mValidToken = false;
383 }
384 
operator =(VSyncCallbackRegistration && other)385 VSyncCallbackRegistration& VSyncCallbackRegistration::operator=(VSyncCallbackRegistration&& other) {
386     mDispatch = std::move(other.mDispatch);
387     mToken = std::move(other.mToken);
388     mValidToken = std::move(other.mValidToken);
389     other.mValidToken = false;
390     return *this;
391 }
392 
~VSyncCallbackRegistration()393 VSyncCallbackRegistration::~VSyncCallbackRegistration() {
394     if (mValidToken) mDispatch.get().unregisterCallback(mToken);
395 }
396 
schedule(nsecs_t workDuration,nsecs_t earliestVsync)397 ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) {
398     if (!mValidToken) {
399         return ScheduleResult::Error;
400     }
401     return mDispatch.get().schedule(mToken, workDuration, earliestVsync);
402 }
403 
cancel()404 CancelResult VSyncCallbackRegistration::cancel() {
405     if (!mValidToken) {
406         return CancelResult::Error;
407     }
408     return mDispatch.get().cancel(mToken);
409 }
410 
411 } // namespace android::scheduler
412