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 {}
dump(std::string &) const54     void dump(std::string&) const final {}
55 
56 private:
57     nsecs_t const mPeriod;
58 };
59 
60 class VRRStubTracker : public VSyncTracker {
61 public:
VRRStubTracker(nsecs_t period)62     VRRStubTracker(nsecs_t period) : mPeriod{period} {}
63 
addVsyncTimestamp(nsecs_t)64     bool addVsyncTimestamp(nsecs_t) final { return true; }
65 
nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const66     nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final {
67         std::lock_guard<decltype(mMutex)> lk(mMutex);
68         auto const normalized_to_base = time_point - mBase;
69         auto const floor = (normalized_to_base) % mPeriod;
70         if (floor == 0) {
71             return time_point;
72         }
73         return normalized_to_base - floor + mPeriod + mBase;
74     }
75 
set_interval(nsecs_t interval,nsecs_t last_known)76     void set_interval(nsecs_t interval, nsecs_t last_known) {
77         std::lock_guard<decltype(mMutex)> lk(mMutex);
78         mPeriod = interval;
79         mBase = last_known;
80     }
81 
currentPeriod() const82     nsecs_t currentPeriod() const final {
83         std::lock_guard<decltype(mMutex)> lk(mMutex);
84         return mPeriod;
85     }
86 
setPeriod(nsecs_t)87     void setPeriod(nsecs_t) final {}
resetModel()88     void resetModel() final {}
dump(std::string &) const89     void dump(std::string&) const final {}
90 
91 private:
92     std::mutex mutable mMutex;
93     nsecs_t mPeriod;
94     nsecs_t mBase = 0;
95 };
96 
97 struct VSyncDispatchRealtimeTest : testing::Test {
98     static nsecs_t constexpr mDispatchGroupThreshold = toNs(100us);
99     static nsecs_t constexpr mVsyncMoveThreshold = toNs(500us);
100     static size_t constexpr mIterations = 20;
101 };
102 
103 class RepeatingCallbackReceiver {
104 public:
RepeatingCallbackReceiver(VSyncDispatch & dispatch,nsecs_t wl)105     RepeatingCallbackReceiver(VSyncDispatch& dispatch, nsecs_t wl)
106           : mWorkload(wl),
107             mCallback(
108                     dispatch, [&](auto time, auto) { callback_called(time); }, "repeat0") {}
109 
repeatedly_schedule(size_t iterations,std::function<void (nsecs_t)> const & onEachFrame)110     void repeatedly_schedule(size_t iterations, std::function<void(nsecs_t)> const& onEachFrame) {
111         mCallbackTimes.reserve(iterations);
112         mCallback.schedule(mWorkload, systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload);
113 
114         for (auto i = 0u; i < iterations - 1; i++) {
115             std::unique_lock<decltype(mMutex)> lk(mMutex);
116             mCv.wait(lk, [&] { return mCalled; });
117             mCalled = false;
118             auto last = mLastTarget;
119             lk.unlock();
120 
121             onEachFrame(last);
122 
123             mCallback.schedule(mWorkload, last + mWorkload);
124         }
125 
126         // wait for the last callback.
127         std::unique_lock<decltype(mMutex)> lk(mMutex);
128         mCv.wait(lk, [&] { return mCalled; });
129     }
130 
with_callback_times(std::function<void (std::vector<nsecs_t> const &)> const & fn) const131     void with_callback_times(std::function<void(std::vector<nsecs_t> const&)> const& fn) const {
132         fn(mCallbackTimes);
133     }
134 
135 private:
callback_called(nsecs_t time)136     void callback_called(nsecs_t time) {
137         std::lock_guard<decltype(mMutex)> lk(mMutex);
138         mCallbackTimes.push_back(time);
139         mCalled = true;
140         mLastTarget = time;
141         mCv.notify_all();
142     }
143 
144     nsecs_t const mWorkload;
145     VSyncCallbackRegistration mCallback;
146 
147     std::mutex mMutex;
148     std::condition_variable mCv;
149     bool mCalled = false;
150     nsecs_t mLastTarget = 0;
151     std::vector<nsecs_t> mCallbackTimes;
152 };
153 
TEST_F(VSyncDispatchRealtimeTest,triple_alarm)154 TEST_F(VSyncDispatchRealtimeTest, triple_alarm) {
155     FixedRateIdealStubTracker tracker;
156     VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
157                                      mVsyncMoveThreshold);
158 
159     static size_t constexpr num_clients = 3;
160     std::array<RepeatingCallbackReceiver, num_clients>
161             cb_receiver{RepeatingCallbackReceiver(dispatch, toNs(1500us)),
162                         RepeatingCallbackReceiver(dispatch, toNs(0h)),
163                         RepeatingCallbackReceiver(dispatch, toNs(1ms))};
164 
165     auto const on_each_frame = [](nsecs_t) {};
166     std::array<std::thread, num_clients> threads{
167             std::thread([&] { cb_receiver[0].repeatedly_schedule(mIterations, on_each_frame); }),
168             std::thread([&] { cb_receiver[1].repeatedly_schedule(mIterations, on_each_frame); }),
169             std::thread([&] { cb_receiver[2].repeatedly_schedule(mIterations, on_each_frame); }),
170     };
171 
172     for (auto it = threads.rbegin(); it != threads.rend(); it++) {
173         it->join();
174     }
175 
176     for (auto const& cbs : cb_receiver) {
177         cbs.with_callback_times([](auto times) { EXPECT_THAT(times.size(), Eq(mIterations)); });
178     }
179 }
180 
181 // starts at 333hz, slides down to 43hz
TEST_F(VSyncDispatchRealtimeTest,vascillating_vrr)182 TEST_F(VSyncDispatchRealtimeTest, vascillating_vrr) {
183     auto next_vsync_interval = toNs(3ms);
184     VRRStubTracker tracker(next_vsync_interval);
185     VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
186                                      mVsyncMoveThreshold);
187 
188     RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
189 
190     auto const on_each_frame = [&](nsecs_t last_known) {
191         tracker.set_interval(next_vsync_interval += toNs(1ms), last_known);
192     };
193 
194     std::thread eventThread([&] { cb_receiver.repeatedly_schedule(mIterations, on_each_frame); });
195     eventThread.join();
196 
197     cb_receiver.with_callback_times([](auto times) { EXPECT_THAT(times.size(), Eq(mIterations)); });
198 }
199 
200 // starts at 333hz, jumps to 200hz at frame 10
TEST_F(VSyncDispatchRealtimeTest,fixed_jump)201 TEST_F(VSyncDispatchRealtimeTest, fixed_jump) {
202     VRRStubTracker tracker(toNs(3ms));
203     VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
204                                      mVsyncMoveThreshold);
205 
206     RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
207 
208     auto jump_frame_counter = 0u;
209     auto constexpr jump_frame_at = 10u;
210     auto const on_each_frame = [&](nsecs_t last_known) {
211         if (jump_frame_counter++ == jump_frame_at) {
212             tracker.set_interval(toNs(5ms), last_known);
213         }
214     };
215     std::thread eventThread([&] { cb_receiver.repeatedly_schedule(mIterations, on_each_frame); });
216     eventThread.join();
217 
218     cb_receiver.with_callback_times([](auto times) { EXPECT_THAT(times.size(), Eq(mIterations)); });
219 }
220 } // namespace android::scheduler
221