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