blob: f613e43324f1b3881e3b7ef80dc8901a7d7fd685 [file] [log] [blame]
Ady Abraham50c202a2019-03-14 11:44:38 -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#undef LOG_TAG
18#define LOG_TAG "LibSurfaceFlingerUnittests"
19#define LOG_NDEBUG 0
20
21#include <inttypes.h>
22
23#include <gmock/gmock.h>
24#include <gtest/gtest.h>
25
26#include <log/log.h>
27
28#include "AsyncCallRecorder.h"
29#include "Scheduler/DispSyncSource.h"
Ady Abraham9c53ee72020-07-22 21:16:18 -070030#include "Scheduler/VSyncDispatch.h"
Ady Abraham50c202a2019-03-14 11:44:38 -070031
32namespace android {
33namespace {
34
35using namespace std::chrono_literals;
Ady Abraham9c53ee72020-07-22 21:16:18 -070036using namespace testing;
37
38class MockVSyncDispatch : public scheduler::VSyncDispatch {
39public:
Dominik Laskowski4e0d20d2021-12-06 11:31:02 -080040 MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override));
41 MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override));
42 MOCK_METHOD(scheduler::ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override));
43 MOCK_METHOD(scheduler::CancelResult, cancel, (CallbackToken), (override));
44 MOCK_METHOD(void, dump, (std::string&), (const, override));
Ady Abraham9c53ee72020-07-22 21:16:18 -070045
46 MockVSyncDispatch() {
47 ON_CALL(*this, registerCallback)
48 .WillByDefault(
49 [this](std::function<void(nsecs_t, nsecs_t, nsecs_t)> const& callback,
50 std::string) {
51 CallbackToken token(mNextToken);
52 mNextToken++;
53
54 mCallbacks.emplace(token, CallbackData(callback));
55 ALOGD("registerCallback: %zu", token.value());
56 return token;
57 });
58
59 ON_CALL(*this, unregisterCallback).WillByDefault([this](CallbackToken token) {
60 ALOGD("unregisterCallback: %zu", token.value());
61 mCallbacks.erase(token);
62 });
63
64 ON_CALL(*this, schedule).WillByDefault([this](CallbackToken token, ScheduleTiming timing) {
65 ALOGD("schedule: %zu", token.value());
66 if (mCallbacks.count(token) == 0) {
67 ALOGD("schedule: callback %zu not registered", token.value());
Ady Abrahamb5d3afa2021-05-07 11:22:23 -070068 return scheduler::ScheduleResult{};
Ady Abraham9c53ee72020-07-22 21:16:18 -070069 }
70
71 auto& callback = mCallbacks.at(token);
72 callback.scheduled = true;
73 callback.vsyncTime = timing.earliestVsync;
74 callback.targetWakeupTime =
75 timing.earliestVsync - timing.workDuration - timing.readyDuration;
76 ALOGD("schedule: callback %zu scheduled", token.value());
Ady Abrahamb5d3afa2021-05-07 11:22:23 -070077 return scheduler::ScheduleResult{callback.targetWakeupTime};
Ady Abraham9c53ee72020-07-22 21:16:18 -070078 });
79
80 ON_CALL(*this, cancel).WillByDefault([this](CallbackToken token) {
81 ALOGD("cancel: %zu", token.value());
82 if (mCallbacks.count(token) == 0) {
83 ALOGD("cancel: callback %zu is not registered", token.value());
84 return scheduler::CancelResult::Error;
85 }
86
87 auto& callback = mCallbacks.at(token);
88 callback.scheduled = false;
89 ALOGD("cancel: callback %zu cancelled", token.value());
90 return scheduler::CancelResult::Cancelled;
91 });
92 }
93
94 void triggerCallbacks() {
95 ALOGD("triggerCallbacks");
96 for (auto& [token, callback] : mCallbacks) {
97 if (callback.scheduled) {
98 ALOGD("triggerCallbacks: callback %zu", token.value());
99 callback.scheduled = false;
100 callback.func(callback.vsyncTime, callback.targetWakeupTime, callback.readyTime);
101 } else {
102 ALOGD("triggerCallbacks: callback %zu is not scheduled", token.value());
103 }
104 }
105 }
106
107private:
108 struct CallbackData {
109 explicit CallbackData(std::function<void(nsecs_t, nsecs_t, nsecs_t)> func)
110 : func(std::move(func)) {}
111
112 std::function<void(nsecs_t, nsecs_t, nsecs_t)> func;
113 bool scheduled = false;
114 nsecs_t vsyncTime = 0;
115 nsecs_t targetWakeupTime = 0;
116 nsecs_t readyTime = 0;
117 };
118
119 std::unordered_map<CallbackToken, CallbackData> mCallbacks;
120 size_t mNextToken;
121};
Ady Abraham50c202a2019-03-14 11:44:38 -0700122
123class DispSyncSourceTest : public testing::Test, private VSyncSource::Callback {
124protected:
125 DispSyncSourceTest();
126 ~DispSyncSourceTest() override;
127
128 void createDispSync();
129 void createDispSyncSource();
130
Ady Abraham9c53ee72020-07-22 21:16:18 -0700131 void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
132 nsecs_t deadlineTimestamp) override;
Ady Abraham50c202a2019-03-14 11:44:38 -0700133
Ady Abraham9c53ee72020-07-22 21:16:18 -0700134 std::unique_ptr<MockVSyncDispatch> mVSyncDispatch;
135 std::unique_ptr<scheduler::DispSyncSource> mDispSyncSource;
Ady Abraham50c202a2019-03-14 11:44:38 -0700136
Ady Abraham9c53ee72020-07-22 21:16:18 -0700137 AsyncCallRecorder<void (*)(nsecs_t, nsecs_t, nsecs_t)> mVSyncEventCallRecorder;
Ady Abraham50c202a2019-03-14 11:44:38 -0700138
Ady Abraham9c53ee72020-07-22 21:16:18 -0700139 static constexpr std::chrono::nanoseconds mWorkDuration = 20ms;
140 static constexpr std::chrono::nanoseconds mReadyDuration = 10ms;
Ady Abraham50c202a2019-03-14 11:44:38 -0700141 static constexpr int mIterations = 100;
Ady Abraham9c53ee72020-07-22 21:16:18 -0700142 const scheduler::VSyncDispatch::CallbackToken mFakeToken{2398};
143 const std::string mName = "DispSyncSourceTest";
Ady Abraham50c202a2019-03-14 11:44:38 -0700144};
145
146DispSyncSourceTest::DispSyncSourceTest() {
147 const ::testing::TestInfo* const test_info =
148 ::testing::UnitTest::GetInstance()->current_test_info();
149 ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
150}
151
152DispSyncSourceTest::~DispSyncSourceTest() {
153 const ::testing::TestInfo* const test_info =
154 ::testing::UnitTest::GetInstance()->current_test_info();
155 ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
156}
157
Ady Abraham9c53ee72020-07-22 21:16:18 -0700158void DispSyncSourceTest::onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
159 nsecs_t deadlineTimestamp) {
Ady Abraham50c202a2019-03-14 11:44:38 -0700160 ALOGD("onVSyncEvent: %" PRId64, when);
161
Ady Abraham9c53ee72020-07-22 21:16:18 -0700162 mVSyncEventCallRecorder.recordCall(when, expectedVSyncTimestamp, deadlineTimestamp);
Ady Abraham50c202a2019-03-14 11:44:38 -0700163}
164
165void DispSyncSourceTest::createDispSync() {
Ady Abraham9c53ee72020-07-22 21:16:18 -0700166 mVSyncDispatch = std::make_unique<MockVSyncDispatch>();
Ady Abraham50c202a2019-03-14 11:44:38 -0700167}
168
169void DispSyncSourceTest::createDispSyncSource() {
Ady Abraham9c53ee72020-07-22 21:16:18 -0700170 mDispSyncSource =
171 std::make_unique<scheduler::DispSyncSource>(*mVSyncDispatch, mWorkDuration,
172 mReadyDuration, true, mName.c_str());
Ady Abraham50c202a2019-03-14 11:44:38 -0700173 mDispSyncSource->setCallback(this);
174}
175
176/* ------------------------------------------------------------------------
177 * Test cases
178 */
179
180TEST_F(DispSyncSourceTest, createDispSync) {
181 createDispSync();
Ady Abraham9c53ee72020-07-22 21:16:18 -0700182 EXPECT_TRUE(mVSyncDispatch);
Ady Abraham50c202a2019-03-14 11:44:38 -0700183}
184
185TEST_F(DispSyncSourceTest, createDispSyncSource) {
Ady Abraham9c53ee72020-07-22 21:16:18 -0700186 createDispSync();
187
188 InSequence seq;
189 EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).WillOnce(Return(mFakeToken));
190 EXPECT_CALL(*mVSyncDispatch, cancel(mFakeToken))
191 .WillOnce(Return(scheduler::CancelResult::Cancelled));
192 EXPECT_CALL(*mVSyncDispatch, unregisterCallback(mFakeToken)).WillOnce(Return());
Ady Abraham50c202a2019-03-14 11:44:38 -0700193 createDispSyncSource();
Ady Abraham9c53ee72020-07-22 21:16:18 -0700194
Ady Abraham50c202a2019-03-14 11:44:38 -0700195 EXPECT_TRUE(mDispSyncSource);
196}
197
198TEST_F(DispSyncSourceTest, noCallbackAfterInit) {
Ady Abraham9c53ee72020-07-22 21:16:18 -0700199 createDispSync();
200
201 InSequence seq;
202 EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
203 EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
204 EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
Ady Abraham50c202a2019-03-14 11:44:38 -0700205 createDispSyncSource();
Ady Abraham9c53ee72020-07-22 21:16:18 -0700206
Ady Abraham50c202a2019-03-14 11:44:38 -0700207 EXPECT_TRUE(mDispSyncSource);
208
209 // DispSyncSource starts with Vsync disabled
Ady Abraham9c53ee72020-07-22 21:16:18 -0700210 mVSyncDispatch->triggerCallbacks();
Ady Abraham50c202a2019-03-14 11:44:38 -0700211 EXPECT_FALSE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value());
212}
213
214TEST_F(DispSyncSourceTest, waitForCallbacks) {
Ady Abraham9c53ee72020-07-22 21:16:18 -0700215 createDispSync();
216
217 InSequence seq;
218 EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
219 EXPECT_CALL(*mVSyncDispatch,
220 schedule(_, Truly([&](auto timings) {
221 return timings.workDuration == mWorkDuration.count() &&
222 timings.readyDuration == mReadyDuration.count();
223 })))
224 .Times(mIterations + 1);
225 EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
226 EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
Ady Abraham50c202a2019-03-14 11:44:38 -0700227 createDispSyncSource();
Ady Abraham9c53ee72020-07-22 21:16:18 -0700228
Ady Abraham50c202a2019-03-14 11:44:38 -0700229 EXPECT_TRUE(mDispSyncSource);
230
231 mDispSyncSource->setVSyncEnabled(true);
Ady Abraham50c202a2019-03-14 11:44:38 -0700232 for (int i = 0; i < mIterations; i++) {
Ady Abraham9c53ee72020-07-22 21:16:18 -0700233 mVSyncDispatch->triggerCallbacks();
234 const auto callbackData = mVSyncEventCallRecorder.waitForCall();
235 ASSERT_TRUE(callbackData.has_value());
236 const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value();
237 EXPECT_EQ(when, expectedVSyncTimestamp - mWorkDuration.count() - mReadyDuration.count());
Ady Abraham50c202a2019-03-14 11:44:38 -0700238 }
239}
240
Ady Abraham9c53ee72020-07-22 21:16:18 -0700241TEST_F(DispSyncSourceTest, waitForCallbacksWithDurationChange) {
242 createDispSync();
243
244 InSequence seq;
245 EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
246 EXPECT_CALL(*mVSyncDispatch,
247 schedule(_, Truly([&](auto timings) {
248 return timings.workDuration == mWorkDuration.count() &&
249 timings.readyDuration == mReadyDuration.count();
250 })))
251 .Times(1);
252
Ady Abraham50c202a2019-03-14 11:44:38 -0700253 createDispSyncSource();
Ady Abraham9c53ee72020-07-22 21:16:18 -0700254
Ady Abraham50c202a2019-03-14 11:44:38 -0700255 EXPECT_TRUE(mDispSyncSource);
256
257 mDispSyncSource->setVSyncEnabled(true);
Ady Abraham9c53ee72020-07-22 21:16:18 -0700258 EXPECT_CALL(*mVSyncDispatch,
259 schedule(_, Truly([&](auto timings) {
260 return timings.workDuration == mWorkDuration.count() &&
261 timings.readyDuration == mReadyDuration.count();
262 })))
263 .Times(mIterations);
Ady Abraham50c202a2019-03-14 11:44:38 -0700264 for (int i = 0; i < mIterations; i++) {
Ady Abraham9c53ee72020-07-22 21:16:18 -0700265 mVSyncDispatch->triggerCallbacks();
266 const auto callbackData = mVSyncEventCallRecorder.waitForCall();
267 ASSERT_TRUE(callbackData.has_value());
268 const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value();
269 EXPECT_EQ(when, expectedVSyncTimestamp - mWorkDuration.count() - mReadyDuration.count());
Ady Abraham50c202a2019-03-14 11:44:38 -0700270 }
271
Ady Abraham9c53ee72020-07-22 21:16:18 -0700272 const auto newDuration = mWorkDuration / 2;
273 EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) {
274 return timings.workDuration == newDuration.count() &&
275 timings.readyDuration == 0;
276 })))
277 .Times(1);
278 mDispSyncSource->setDuration(newDuration, 0ns);
Ady Abraham50c202a2019-03-14 11:44:38 -0700279
Ady Abraham9c53ee72020-07-22 21:16:18 -0700280 EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) {
281 return timings.workDuration == newDuration.count() &&
282 timings.readyDuration == 0;
283 })))
284 .Times(mIterations);
Ady Abraham50c202a2019-03-14 11:44:38 -0700285 for (int i = 0; i < mIterations; i++) {
Ady Abraham9c53ee72020-07-22 21:16:18 -0700286 mVSyncDispatch->triggerCallbacks();
287 const auto callbackData = mVSyncEventCallRecorder.waitForCall();
288 ASSERT_TRUE(callbackData.has_value());
289 const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value();
290 EXPECT_EQ(when, expectedVSyncTimestamp - newDuration.count());
Ady Abraham50c202a2019-03-14 11:44:38 -0700291 }
Ady Abraham9c53ee72020-07-22 21:16:18 -0700292
293 EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
294 EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
Ady Abraham50c202a2019-03-14 11:44:38 -0700295}
296
Ady Abraham50c202a2019-03-14 11:44:38 -0700297} // namespace
298} // namespace android