blob: 00504957a9e58dbc1e74fdb94605a272c8e05a85 [file] [log] [blame]
Kevin DuBois305bef12019-10-09 13:23:27 -07001/*
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#pragma once
18
19#include <android-base/thread_annotations.h>
20#include <utils/Timers.h>
21#include <functional>
22#include <memory>
23#include <mutex>
24#include <string>
25#include <string_view>
26#include <unordered_map>
27
28#include "StrongTyping.h"
29
30namespace android::scheduler {
31class TimeKeeper;
32class VSyncTracker;
33
34enum class ScheduleResult { Scheduled, ReScheduled, CannotSchedule, Error };
35enum class CancelResult { Cancelled, TooLate, Error };
36
37namespace impl {
38
39// VSyncDispatchEntry is a helper class representing internal state for each entry in VSyncDispatch
40// hoisted to public for unit testing.
41class VSyncDispatchEntry {
42public:
43 // This is the state of the entry. There are 3 states, armed, running, disarmed.
44 // Valid transition: disarmed -> armed ( when scheduled )
45 // Valid transition: armed -> running -> disarmed ( when timer is called)
46 // Valid transition: armed -> disarmed ( when cancelled )
47 VSyncDispatchEntry(std::string const& name, std::function<void(nsecs_t)> const& fn);
48 std::string_view name() const;
49
50 // Start: functions that are not threadsafe.
51 // Return the last vsync time this callback was invoked.
52 std::optional<nsecs_t> lastExecutedVsyncTarget() const;
53
54 // This moves the state from disarmed->armed and will calculate the wakeupTime.
55 nsecs_t schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
56 nsecs_t now);
57 // This will update armed entries with the latest vsync information. Entry remains armed.
58 void update(VSyncTracker& tracker, nsecs_t now);
59
60 // This will return empty if not armed, or the next calculated wakeup time if armed.
61 // It will not update the wakeupTime.
62 std::optional<nsecs_t> wakeupTime() const;
63
64 // This moves state from armed->disarmed.
65 void disarm();
66
67 // This moves the state from armed->running.
68 // Store the timestamp that this was intended for as the last called timestamp.
69 nsecs_t executing();
70 // End: functions that are not threadsafe.
71
72 // Invoke the callback with the timestamp, moving the state from running->disarmed.
73 void callback(nsecs_t timestamp);
74 // Block calling thread while the callback is executing.
75 void ensureNotRunning();
76
77private:
78 void arm(VSyncTracker& tracker, nsecs_t now);
79 std::string const mName;
80 std::function<void(nsecs_t)> const mCallback;
81
82 nsecs_t mWorkDuration;
83 nsecs_t mEarliestVsync;
84
85 struct ArmingInfo {
86 nsecs_t mActualWakeupTime;
87 nsecs_t mActualVsyncTime;
88 };
89 std::optional<ArmingInfo> mArmedInfo;
90 std::optional<nsecs_t> mLastDispatchTime;
91
92 std::mutex mRunningMutex;
93 std::condition_variable mCv;
94 bool mRunning GUARDED_BY(mRunningMutex) = false;
95};
96
97} // namespace impl
98
99/*
100 * VSyncDispatch is a class that will dispatch callbacks relative to system vsync events.
101 */
102class VSyncDispatch {
103public:
104 using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare>;
105
106 /* creates a VsyncDispatch.
107 * \param [in] a timekeeper object for dispatching events.
108 * \param [in] a tracker object that is monitoring expected vsync events.
109 * \param [in] a tunable in nanoseconds that indicates when events that fall close together
110 * should be dispatched in one timer wakeup.
111 */
112 explicit VSyncDispatch(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
113 nsecs_t timerSlack);
114 ~VSyncDispatch();
115
116 /*
117 * Registers a callback that will be called at designated points on the vsync timeline.
118 * The callback can be scheduled, rescheduled targeting vsync times, or cancelled.
119 * The token returned must be cleaned up via unregisterCallback.
120 *
121 * \param [in] callbackFn A function to schedule for callback. The resources needed to invoke
122 * callbackFn must have lifetimes encompassing the lifetime of the
123 * CallbackToken returned.
124 * \param [in] callbackName A human-readable, unique name to identify the callback.
125 * \return A token that can be used to schedule, reschedule, or cancel the
126 * invocation of callbackFn.
127 *
128 */
129 CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
130 std::string callbackName);
131
132 /*
133 * Unregisters a callback.
134 *
135 * \param [in] token The callback to unregister.
136 *
137 */
138 void unregisterCallback(CallbackToken token);
139
140 /*
141 * Schedules the registered callback to be dispatched.
142 *
143 * The callback will be dispatched at 'workDuration' nanoseconds before a vsync event.
144 *
145 * The caller designates the earliest vsync event that should be targeted by the earliestVsync
146 * parameter.
147 * The callback will be scheduled at (workDuration - predictedVsync), where predictedVsync
148 * is the first vsync event time where ( predictedVsync >= earliestVsync ).
149 *
150 * If (workDuration - earliestVsync) is in the past, or if a callback has already been
151 * dispatched for the predictedVsync, an error will be returned.
152 *
153 * It is valid to reschedule a callback to a different time.
154 *
155 * \param [in] token The callback to schedule.
156 * \param [in] workDuration The time before the actual vsync time to invoke the callback
157 * associated with token.
158 * \param [in] earliestVsync The targeted display time. This will be snapped to the closest
159 * predicted vsync time after earliestVsync.
160 * \return A ScheduleResult::Scheduled if callback was scheduled.
161 * A ScheduleResult::ReScheduled if callback was rescheduled.
162 * A ScheduleResult::CannotSchedule
163 * if (workDuration - earliestVsync) is in the past, or
164 * if a callback was dispatched for the predictedVsync already.
165 * A ScheduleResult::Error if there was another error.
166 */
167 ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync);
168
169 /* Cancels a scheduled callback, if possible.
170 *
171 * \param [in] token The callback to cancel.
172 * \return A CancelResult::TooLate if the callback was already dispatched.
173 * A CancelResult::Cancelled if the callback was successfully cancelled.
174 * A CancelResult::Error if there was an pre-condition violation.
175 */
176 CancelResult cancel(CallbackToken token);
177
178private:
179 VSyncDispatch(VSyncDispatch const&) = delete;
180 VSyncDispatch& operator=(VSyncDispatch const&) = delete;
181
182 using CallbackMap = std::unordered_map<size_t, std::shared_ptr<impl::VSyncDispatchEntry>>;
183
184 void timerCallback();
185 void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex);
186 void rearmTimer(nsecs_t now) REQUIRES(mMutex);
187 void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate)
188 REQUIRES(mMutex);
189 void cancelTimer() REQUIRES(mMutex);
190
191 static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
192 std::unique_ptr<TimeKeeper> const mTimeKeeper;
193 VSyncTracker& mTracker;
194 nsecs_t const mTimerSlack;
195
196 std::mutex mutable mMutex;
197 size_t mCallbackToken GUARDED_BY(mMutex) = 0;
198
199 CallbackMap mCallbacks GUARDED_BY(mMutex);
200 nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime;
201};
202
203/*
204 * Helper class to operate on registered callbacks. It is up to user of the class to ensure
205 * that VsyncDispatch lifetime exceeds the lifetime of VSyncCallbackRegistation.
206 */
207class VSyncCallbackRegistration {
208public:
209 VSyncCallbackRegistration(VSyncDispatch&, std::function<void(nsecs_t)> const& callbackFn,
210 std::string const& callbackName);
211 VSyncCallbackRegistration(VSyncCallbackRegistration&&);
212 VSyncCallbackRegistration& operator=(VSyncCallbackRegistration&&);
213 ~VSyncCallbackRegistration();
214
215 // See documentation for VSyncDispatch::schedule.
216 ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync);
217
218 // See documentation for VSyncDispatch::cancel.
219 CancelResult cancel();
220
221private:
222 VSyncCallbackRegistration(VSyncCallbackRegistration const&) = delete;
223 VSyncCallbackRegistration& operator=(VSyncCallbackRegistration const&) = delete;
224
225 std::reference_wrapper<VSyncDispatch> mDispatch;
226 VSyncDispatch::CallbackToken mToken;
227 bool mValidToken;
228};
229
230} // namespace android::scheduler