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 #include "Scheduler/TimeKeeper.h"
18 #include "Scheduler/Timer.h"
19 #include "Scheduler/VSyncDispatchTimerQueue.h"
20 #include "Scheduler/VSyncTracker.h"
21 
22 #include <gmock/gmock.h>
23 #include <gtest/gtest.h>
24 #include <thread>
25 
26 using namespace testing;
27 using namespace std::literals;
28 
29 namespace android::scheduler {
30 
31 template <typename Rep, typename Per>
toNs(std::chrono::duration<Rep,Per> const & tp)32 constexpr nsecs_t toNs(std::chrono::duration<Rep, Per> const& tp) {
33     return std::chrono::duration_cast<std::chrono::nanoseconds>(tp).count();
34 }
35 
36 class FixedRateIdealStubTracker : public VSyncTracker {
37 public:
FixedRateIdealStubTracker()38     FixedRateIdealStubTracker() : mPeriod{toNs(3ms)} {}
39 
addVsyncTimestamp(nsecs_t)40     bool addVsyncTimestamp(nsecs_t) final { return true; }
41 
nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const42     nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
43         auto const floor = timePoint % mPeriod;
44         if (floor == 0) {
45             return timePoint;
46         }
47         return timePoint - floor + mPeriod;
48     }
49 
currentPeriod() const50     nsecs_t currentPeriod() const final { return mPeriod; }
51 
setPeriod(nsecs_t)52     void setPeriod(nsecs_t) final {}
resetModel()53     void resetModel() final {}
needsMoreSamples() const54     bool needsMoreSamples() const final { return false; }
isVSyncInPhase(nsecs_t,Fps) const55     bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
dump(std::string &) const56     void dump(std::string&) const final {}
57 
58 private:
59     nsecs_t const mPeriod;
60 };
61 
62 class VRRStubTracker : public VSyncTracker {
63 public:
VRRStubTracker(nsecs_t period)64     VRRStubTracker(nsecs_t period) : mPeriod{period} {}
65 
addVsyncTimestamp(nsecs_t)66     bool addVsyncTimestamp(nsecs_t) final { return true; }
67 
nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const68     nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final {
69         std::lock_guard lock(mMutex);
70         auto const normalized_to_base = time_point - mBase;
71         auto const floor = (normalized_to_base) % mPeriod;
72         if (floor == 0) {
73             return time_point;
74         }
75         return normalized_to_base - floor + mPeriod + mBase;
76     }
77 
set_interval(nsecs_t interval,nsecs_t last_known)78     void set_interval(nsecs_t interval, nsecs_t last_known) {
79         std::lock_guard lock(mMutex);
80         mPeriod = interval;
81         mBase = last_known;
82     }
83 
currentPeriod() const84     nsecs_t currentPeriod() const final {
85         std::lock_guard lock(mMutex);
86         return mPeriod;
87     }
88 
setPeriod(nsecs_t)89     void setPeriod(nsecs_t) final {}
resetModel()90     void resetModel() final {}
needsMoreSamples() const91     bool needsMoreSamples() const final { return false; }
isVSyncInPhase(nsecs_t,Fps) const92     bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
dump(std::string &) const93     void dump(std::string&) const final {}
94 
95 private:
96     std::mutex mutable mMutex;
97     nsecs_t mPeriod;
98     nsecs_t mBase = 0;
99 };
100 
101 struct VSyncDispatchRealtimeTest : testing::Test {
102     static nsecs_t constexpr mDispatchGroupThreshold = toNs(100us);
103     static nsecs_t constexpr mVsyncMoveThreshold = toNs(500us);
104     static size_t constexpr mIterations = 20;
105 };
106 
107 class RepeatingCallbackReceiver {
108 public:
RepeatingCallbackReceiver(VSyncDispatch & dispatch,nsecs_t workload,nsecs_t readyDuration)109     RepeatingCallbackReceiver(VSyncDispatch& dispatch, nsecs_t workload, nsecs_t readyDuration)
110           : mWorkload(workload),
111             mReadyDuration(readyDuration),
112             mCallback(
113                     dispatch, [&](auto time, auto, auto) { callback_called(time); }, "repeat0") {}
114 
repeatedly_schedule(size_t iterations,std::function<void (nsecs_t)> const & onEachFrame)115     void repeatedly_schedule(size_t iterations, std::function<void(nsecs_t)> const& onEachFrame) {
116         mCallbackTimes.reserve(iterations);
117         mCallback.schedule(
118                 {.workDuration = mWorkload,
119                  .readyDuration = mReadyDuration,
120                  .earliestVsync = systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload + mReadyDuration});
121 
122         for (auto i = 0u; i < iterations - 1; i++) {
123             std::unique_lock lock(mMutex);
124             mCv.wait(lock, [&] { return mCalled; });
125             mCalled = false;
126             auto last = mLastTarget;
127             lock.unlock();
128 
129             onEachFrame(last);
130 
131             mCallback.schedule({.workDuration = mWorkload,
132                                 .readyDuration = mReadyDuration,
133                                 .earliestVsync = last + mWorkload + mReadyDuration});
134         }
135 
136         // wait for the last callback.
137         std::unique_lock lock(mMutex);
138         mCv.wait(lock, [&] { return mCalled; });
139     }
140 
with_callback_times(std::function<void (std::vector<nsecs_t> const &)> const & fn) const141     void with_callback_times(std::function<void(std::vector<nsecs_t> const&)> const& fn) const {
142         fn(mCallbackTimes);
143     }
144 
145 private:
callback_called(nsecs_t time)146     void callback_called(nsecs_t time) {
147         std::lock_guard lock(mMutex);
148         mCallbackTimes.push_back(time);
149         mCalled = true;
150         mLastTarget = time;
151         mCv.notify_all();
152     }
153 
154     nsecs_t const mWorkload;
155     nsecs_t const mReadyDuration;
156     VSyncCallbackRegistration mCallback;
157 
158     std::mutex mMutex;
159     std::condition_variable mCv;
160     bool mCalled = false;
161     nsecs_t mLastTarget = 0;
162     std::vector<nsecs_t> mCallbackTimes;
163 };
164 
TEST_F(VSyncDispatchRealtimeTest,triple_alarm)165 TEST_F(VSyncDispatchRealtimeTest, triple_alarm) {
166     FixedRateIdealStubTracker tracker;
167     VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
168                                      mVsyncMoveThreshold);
169 
170     static size_t constexpr num_clients = 3;
171     std::array<RepeatingCallbackReceiver, num_clients>
172             cb_receiver{RepeatingCallbackReceiver(dispatch, toNs(1500us), toNs(2500us)),
173                         RepeatingCallbackReceiver(dispatch, toNs(0h), toNs(0h)),
174                         RepeatingCallbackReceiver(dispatch, toNs(1ms), toNs(3ms))};
175 
176     auto const on_each_frame = [](nsecs_t) {};
177     std::array<std::thread, num_clients> threads{
178             std::thread([&] { cb_receiver[0].repeatedly_schedule(mIterations, on_each_frame); }),
179             std::thread([&] { cb_receiver[1].repeatedly_schedule(mIterations, on_each_frame); }),
180             std::thread([&] { cb_receiver[2].repeatedly_schedule(mIterations, on_each_frame); }),
181     };
182 
183     for (auto it = threads.rbegin(); it != threads.rend(); it++) {
184         it->join();
185     }
186 
187     for (auto const& cbs : cb_receiver) {
188         cbs.with_callback_times([](auto times) { EXPECT_THAT(times.size(), Eq(mIterations)); });
189     }
190 }
191 
192 // starts at 333hz, slides down to 43hz
TEST_F(VSyncDispatchRealtimeTest,vascillating_vrr)193 TEST_F(VSyncDispatchRealtimeTest, vascillating_vrr) {
194     auto next_vsync_interval = toNs(3ms);
195     VRRStubTracker tracker(next_vsync_interval);
196     VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
197                                      mVsyncMoveThreshold);
198 
199     RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms), toNs(5ms));
200 
201     auto const on_each_frame = [&](nsecs_t last_known) {
202         tracker.set_interval(next_vsync_interval += toNs(1ms), last_known);
203     };
204 
205     std::thread eventThread([&] { cb_receiver.repeatedly_schedule(mIterations, on_each_frame); });
206     eventThread.join();
207 
208     cb_receiver.with_callback_times([](auto times) { EXPECT_THAT(times.size(), Eq(mIterations)); });
209 }
210 
211 // starts at 333hz, jumps to 200hz at frame 10
TEST_F(VSyncDispatchRealtimeTest,fixed_jump)212 TEST_F(VSyncDispatchRealtimeTest, fixed_jump) {
213     VRRStubTracker tracker(toNs(3ms));
214     VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
215                                      mVsyncMoveThreshold);
216 
217     RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms), toNs(5ms));
218 
219     auto jump_frame_counter = 0u;
220     auto constexpr jump_frame_at = 10u;
221     auto const on_each_frame = [&](nsecs_t last_known) {
222         if (jump_frame_counter++ == jump_frame_at) {
223             tracker.set_interval(toNs(5ms), last_known);
224         }
225     };
226     std::thread eventThread([&] { cb_receiver.repeatedly_schedule(mIterations, on_each_frame); });
227     eventThread.join();
228 
229     cb_receiver.with_callback_times([](auto times) { EXPECT_THAT(times.size(), Eq(mIterations)); });
230 }
231 } // namespace android::scheduler
232