blob: 29711afdf924a57176b6062c246ca44283165e84 [file] [log] [blame]
Dominik Laskowskib418dd72023-06-13 17:31:04 -04001/*
2 * Copyright 2023 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 <ftl/optional.h>
18#include <gtest/gtest.h>
19
Ady Abraham3db8a3c2023-11-20 17:53:47 -080020#include <common/test/FlagUtils.h>
Dominik Laskowskib418dd72023-06-13 17:31:04 -040021#include <scheduler/Fps.h>
22#include <scheduler/FrameTargeter.h>
23#include <scheduler/IVsyncSource.h>
24
Ady Abraham3db8a3c2023-11-20 17:53:47 -080025#include <com_android_graphics_surfaceflinger_flags.h>
26
Dominik Laskowskib418dd72023-06-13 17:31:04 -040027using namespace std::chrono_literals;
28
29namespace android::scheduler {
30namespace {
31
32struct VsyncSource final : IVsyncSource {
Ady Abraham3db8a3c2023-11-20 17:53:47 -080033 VsyncSource(Period period, Period minFramePeriod, TimePoint deadline)
34 : vsyncPeriod(period), framePeriod(minFramePeriod), vsyncDeadline(deadline) {}
Dominik Laskowskib418dd72023-06-13 17:31:04 -040035
36 const Period vsyncPeriod;
Ady Abraham3db8a3c2023-11-20 17:53:47 -080037 const Period framePeriod;
Dominik Laskowskib418dd72023-06-13 17:31:04 -040038 const TimePoint vsyncDeadline;
39
40 Period period() const override { return vsyncPeriod; }
Leon Scroggins IIIa0785012024-01-23 16:05:59 -050041 TimePoint vsyncDeadlineAfter(TimePoint, ftl::Optional<TimePoint> = {}) const override {
42 return vsyncDeadline;
43 }
Ady Abraham3db8a3c2023-11-20 17:53:47 -080044 Period minFramePeriod() const override { return framePeriod; }
Dominik Laskowskib418dd72023-06-13 17:31:04 -040045};
46
47} // namespace
48
Leon Scroggins III0bd0d4c2022-12-08 13:20:45 -050049class FrameTargeterTestBase : public testing::Test {
Dominik Laskowskib418dd72023-06-13 17:31:04 -040050public:
Leon Scroggins III0bd0d4c2022-12-08 13:20:45 -050051 FrameTargeterTestBase(FeatureFlags flags) : mTargeter(PhysicalDisplayId::fromPort(13), flags) {}
52
Dominik Laskowskib418dd72023-06-13 17:31:04 -040053 const auto& target() const { return mTargeter.target(); }
54
Leon Scroggins III0bd0d4c2022-12-08 13:20:45 -050055 bool wouldPresentEarly(Period minFramePeriod) const {
56 return target().wouldPresentEarly(minFramePeriod);
57 }
58
Dominik Laskowskib418dd72023-06-13 17:31:04 -040059 struct Frame {
Leon Scroggins III0bd0d4c2022-12-08 13:20:45 -050060 Frame(FrameTargeterTestBase* testPtr, VsyncId vsyncId, TimePoint& frameBeginTime,
Ady Abraham3db8a3c2023-11-20 17:53:47 -080061 Duration frameDuration, Fps refreshRate, Fps peakRefreshRate,
Dominik Laskowskib418dd72023-06-13 17:31:04 -040062 FrameTargeter::IsFencePendingFuncPtr isFencePendingFuncPtr = Frame::fenceSignaled,
63 const ftl::Optional<VsyncSource>& vsyncSourceOpt = std::nullopt)
Ady Abraham3db8a3c2023-11-20 17:53:47 -080064 : testPtr(testPtr),
65 frameBeginTime(frameBeginTime),
66 period(refreshRate.getPeriod()),
67 minFramePeriod(peakRefreshRate.getPeriod()) {
Dominik Laskowskib418dd72023-06-13 17:31:04 -040068 const FrameTargeter::BeginFrameArgs args{.frameBeginTime = frameBeginTime,
69 .vsyncId = vsyncId,
70 .expectedVsyncTime =
71 frameBeginTime + frameDuration,
Leon Scroggins III0bd0d4c2022-12-08 13:20:45 -050072 .sfWorkDuration = 10ms,
73 .hwcMinWorkDuration = kHwcMinWorkDuration};
Dominik Laskowskib418dd72023-06-13 17:31:04 -040074
75 testPtr->mTargeter.beginFrame(args,
76 vsyncSourceOpt
77 .or_else([&] {
78 return std::make_optional(
Ady Abraham3db8a3c2023-11-20 17:53:47 -080079 VsyncSource(period, period,
Dominik Laskowskib418dd72023-06-13 17:31:04 -040080 args.expectedVsyncTime));
81 })
82 .value(),
83 isFencePendingFuncPtr);
84 }
85
86 FenceTimePtr end(CompositionCoverage coverage = CompositionCoverage::Hwc) {
87 if (ended) return nullptr;
88 ended = true;
89
90 auto [fence, fenceTime] = testPtr->mFenceMap.makePendingFenceForTest();
91 testPtr->mTargeter.setPresentFence(std::move(fence), fenceTime);
92
93 testPtr->mTargeter.endFrame({.compositionCoverage = coverage});
94 return fenceTime;
95 }
96
97 ~Frame() {
98 end();
99 frameBeginTime += period;
100 }
101
102 static bool fencePending(const FenceTimePtr&, int) { return true; }
103 static bool fenceSignaled(const FenceTimePtr&, int) { return false; }
104
Leon Scroggins III0bd0d4c2022-12-08 13:20:45 -0500105 FrameTargeterTestBase* const testPtr;
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400106
107 TimePoint& frameBeginTime;
108 const Period period;
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800109 const Period minFramePeriod;
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400110
111 bool ended = false;
112 };
113
Leon Scroggins III0bd0d4c2022-12-08 13:20:45 -0500114 static constexpr Duration kHwcMinWorkDuration = std::chrono::nanoseconds(5ns);
115
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400116private:
117 FenceToFenceTimeMap mFenceMap;
118
Leon Scroggins III0bd0d4c2022-12-08 13:20:45 -0500119 FrameTargeter mTargeter;
120};
121
122class FrameTargeterTest : public FrameTargeterTestBase {
123public:
124 FrameTargeterTest() : FrameTargeterTestBase(Feature::kBackpressureGpuComposition) {}
125};
126
127class FrameTargeterWithExpectedPresentSupportTest : public FrameTargeterTestBase {
128public:
129 FrameTargeterWithExpectedPresentSupportTest()
130 : FrameTargeterTestBase(FeatureFlags(Feature::kBackpressureGpuComposition) |
131 Feature::kExpectedPresentTime) {}
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400132};
133
134TEST_F(FrameTargeterTest, targetsFrames) {
135 VsyncId vsyncId{42};
136 {
137 TimePoint frameBeginTime(989ms);
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800138 const Frame frame(this, vsyncId++, frameBeginTime, 10ms, 60_Hz, 60_Hz);
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400139
140 EXPECT_EQ(target().vsyncId(), VsyncId{42});
141 EXPECT_EQ(target().frameBeginTime(), TimePoint(989ms));
142 EXPECT_EQ(target().expectedPresentTime(), TimePoint(999ms));
143 EXPECT_EQ(target().expectedFrameDuration(), 10ms);
144 }
145 {
146 TimePoint frameBeginTime(1100ms);
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800147 const Frame frame(this, vsyncId++, frameBeginTime, 11ms, 60_Hz, 60_Hz);
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400148
149 EXPECT_EQ(target().vsyncId(), VsyncId{43});
150 EXPECT_EQ(target().frameBeginTime(), TimePoint(1100ms));
151 EXPECT_EQ(target().expectedPresentTime(), TimePoint(1111ms));
152 EXPECT_EQ(target().expectedFrameDuration(), 11ms);
153 }
154}
155
156TEST_F(FrameTargeterTest, inflatesExpectedPresentTime) {
157 // Negative such that `expectedVsyncTime` is in the past.
158 constexpr Duration kFrameDuration = -3ms;
159 TimePoint frameBeginTime(777ms);
160
161 constexpr Fps kRefreshRate = 120_Hz;
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800162 const VsyncSource vsyncSource(kRefreshRate.getPeriod(), kRefreshRate.getPeriod(),
163 frameBeginTime + 5ms);
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400164 const Frame frame(this, VsyncId{123}, frameBeginTime, kFrameDuration, kRefreshRate,
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800165 kRefreshRate, Frame::fenceSignaled, vsyncSource);
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400166
167 EXPECT_EQ(target().expectedPresentTime(), vsyncSource.vsyncDeadline + vsyncSource.vsyncPeriod);
168}
169
170TEST_F(FrameTargeterTest, recallsPastVsync) {
171 VsyncId vsyncId{111};
172 TimePoint frameBeginTime(1000ms);
173 constexpr Fps kRefreshRate = 60_Hz;
174 constexpr Period kPeriod = kRefreshRate.getPeriod();
175 constexpr Duration kFrameDuration = 13ms;
176
177 for (int n = 5; n-- > 0;) {
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800178 Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400179 const auto fence = frame.end();
180
181 EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - kPeriod);
182 EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), fence);
183 }
184}
185
186TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAhead) {
187 VsyncId vsyncId{222};
188 TimePoint frameBeginTime(2000ms);
189 constexpr Fps kRefreshRate = 120_Hz;
190 constexpr Period kPeriod = kRefreshRate.getPeriod();
191 constexpr Duration kFrameDuration = 10ms;
192
193 FenceTimePtr previousFence = FenceTime::NO_FENCE;
194
195 for (int n = 5; n-- > 0;) {
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800196 Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
197 const auto fence = frame.end();
198
199 EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod);
200 EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), previousFence);
201
202 previousFence = fence;
203 }
204}
205
206TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAheadVrr) {
207 SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::vrr_config, true);
208
209 VsyncId vsyncId{222};
210 TimePoint frameBeginTime(2000ms);
211 constexpr Fps kRefreshRate = 120_Hz;
212 constexpr Fps kPeakRefreshRate = 240_Hz;
213 constexpr Period kPeriod = kRefreshRate.getPeriod();
214 constexpr Duration kFrameDuration = 10ms;
215
216 FenceTimePtr previousFence = FenceTime::NO_FENCE;
217
218 for (int n = 5; n-- > 0;) {
219 Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate,
220 kPeakRefreshRate);
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400221 const auto fence = frame.end();
222
223 EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod);
224 EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), previousFence);
225
226 previousFence = fence;
227 }
228}
229
230TEST_F(FrameTargeterTest, doesNotDetectEarlyPresentIfNoFence) {
231 constexpr Period kPeriod = (60_Hz).getPeriod();
232 EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), FenceTime::NO_FENCE);
Leon Scroggins III0bd0d4c2022-12-08 13:20:45 -0500233 EXPECT_FALSE(wouldPresentEarly(kPeriod));
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400234}
235
236TEST_F(FrameTargeterTest, detectsEarlyPresent) {
237 VsyncId vsyncId{333};
238 TimePoint frameBeginTime(3000ms);
239 constexpr Fps kRefreshRate = 60_Hz;
240 constexpr Period kPeriod = kRefreshRate.getPeriod();
241
242 // The target is not early while past present fences are pending.
243 for (int n = 3; n-- > 0;) {
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800244 const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
Leon Scroggins III0bd0d4c2022-12-08 13:20:45 -0500245 EXPECT_FALSE(wouldPresentEarly(kPeriod));
246 EXPECT_FALSE(target().earliestPresentTime());
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400247 }
248
249 // The target is early if the past present fence was signaled.
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800250 Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400251 const auto fence = frame.end();
252 fence->signalForTest(frameBeginTime.ns());
253
Leon Scroggins III0bd0d4c2022-12-08 13:20:45 -0500254 Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
255
256 // `finalFrame` would present early, so it has an earliest present time.
257 EXPECT_TRUE(wouldPresentEarly(kPeriod));
258 ASSERT_NE(std::nullopt, target().earliestPresentTime());
259 EXPECT_EQ(*target().earliestPresentTime(),
260 target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
261}
262
263// Same as `detectsEarlyPresent`, above, but verifies that we do not set an earliest present time
264// when there is expected present time support.
265TEST_F(FrameTargeterWithExpectedPresentSupportTest, detectsEarlyPresent) {
266 VsyncId vsyncId{333};
267 TimePoint frameBeginTime(3000ms);
268 constexpr Fps kRefreshRate = 60_Hz;
269 constexpr Period kPeriod = kRefreshRate.getPeriod();
270
271 // The target is not early while past present fences are pending.
272 for (int n = 3; n-- > 0;) {
273 const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
274 EXPECT_FALSE(wouldPresentEarly(kPeriod));
275 EXPECT_FALSE(target().earliestPresentTime());
276 }
277
278 // The target is early if the past present fence was signaled.
279 Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
280 const auto fence = frame.end();
281 fence->signalForTest(frameBeginTime.ns());
282
283 Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
284
285 // `finalFrame` would present early, but we have expected present time support, so it has no
286 // earliest present time.
287 EXPECT_TRUE(wouldPresentEarly(kPeriod));
288 ASSERT_EQ(std::nullopt, target().earliestPresentTime());
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400289}
290
291TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) {
292 VsyncId vsyncId{444};
293 TimePoint frameBeginTime(4000ms);
294 constexpr Fps kRefreshRate = 120_Hz;
295 constexpr Period kPeriod = kRefreshRate.getPeriod();
296
297 // The target is not early while past present fences are pending.
298 for (int n = 3; n-- > 0;) {
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800299 const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
Leon Scroggins III0bd0d4c2022-12-08 13:20:45 -0500300 EXPECT_FALSE(wouldPresentEarly(kPeriod));
301 EXPECT_FALSE(target().earliestPresentTime());
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400302 }
303
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800304 Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400305 const auto fence = frame.end();
306 fence->signalForTest(frameBeginTime.ns());
307
308 // The target is two VSYNCs ahead, so the past present fence is still pending.
Leon Scroggins III0bd0d4c2022-12-08 13:20:45 -0500309 EXPECT_FALSE(wouldPresentEarly(kPeriod));
310 EXPECT_FALSE(target().earliestPresentTime());
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400311
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800312 { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); }
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400313
Leon Scroggins III0bd0d4c2022-12-08 13:20:45 -0500314 Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
315
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400316 // The target is early if the past present fence was signaled.
Leon Scroggins III0bd0d4c2022-12-08 13:20:45 -0500317 EXPECT_TRUE(wouldPresentEarly(kPeriod));
318 ASSERT_NE(std::nullopt, target().earliestPresentTime());
319 EXPECT_EQ(*target().earliestPresentTime(),
320 target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400321}
322
323TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) {
324 TimePoint frameBeginTime(5000ms);
325 constexpr Fps kRefreshRate = 144_Hz;
326 constexpr Period kPeriod = kRefreshRate.getPeriod();
327
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800328 const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate, kRefreshRate);
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400329
330 // The target is more than two VSYNCs ahead, but present fences are not tracked that far back.
Leon Scroggins III0bd0d4c2022-12-08 13:20:45 -0500331 EXPECT_TRUE(wouldPresentEarly(kPeriod));
332 EXPECT_TRUE(target().earliestPresentTime());
333 EXPECT_EQ(*target().earliestPresentTime(),
334 target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400335}
336
337TEST_F(FrameTargeterTest, detectsMissedFrames) {
338 VsyncId vsyncId{555};
339 TimePoint frameBeginTime(5000ms);
340 constexpr Fps kRefreshRate = 60_Hz;
341 constexpr Period kPeriod = kRefreshRate.getPeriod();
342
343 EXPECT_FALSE(target().isFramePending());
344 EXPECT_FALSE(target().didMissFrame());
345 EXPECT_FALSE(target().didMissHwcFrame());
346
347 {
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800348 const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400349 EXPECT_FALSE(target().isFramePending());
350
351 // The frame did not miss if the past present fence is invalid.
352 EXPECT_FALSE(target().didMissFrame());
353 EXPECT_FALSE(target().didMissHwcFrame());
354 }
355 {
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800356 Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate,
357 Frame::fencePending);
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400358 EXPECT_TRUE(target().isFramePending());
359
360 // The frame missed if the past present fence is pending.
361 EXPECT_TRUE(target().didMissFrame());
362 EXPECT_TRUE(target().didMissHwcFrame());
363
364 frame.end(CompositionCoverage::Gpu);
365 }
366 {
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800367 const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate,
368 Frame::fencePending);
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400369 EXPECT_TRUE(target().isFramePending());
370
371 // The GPU frame missed if the past present fence is pending.
372 EXPECT_TRUE(target().didMissFrame());
373 EXPECT_FALSE(target().didMissHwcFrame());
374 }
375 {
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800376 Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400377 EXPECT_FALSE(target().isFramePending());
378
379 const auto fence = frame.end();
380 const auto expectedPresentTime = target().expectedPresentTime();
381 fence->signalForTest(expectedPresentTime.ns() + kPeriod.ns() / 2 + 1);
382 }
383 {
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800384 Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400385 EXPECT_FALSE(target().isFramePending());
386
387 const auto fence = frame.end();
388 const auto expectedPresentTime = target().expectedPresentTime();
389 fence->signalForTest(expectedPresentTime.ns() + kPeriod.ns() / 2);
390
391 // The frame missed if the past present fence was signaled but not within slop.
392 EXPECT_TRUE(target().didMissFrame());
393 EXPECT_TRUE(target().didMissHwcFrame());
394 }
395 {
Ady Abraham3db8a3c2023-11-20 17:53:47 -0800396 Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
Dominik Laskowskib418dd72023-06-13 17:31:04 -0400397 EXPECT_FALSE(target().isFramePending());
398
399 // The frame did not miss if the past present fence was signaled within slop.
400 EXPECT_FALSE(target().didMissFrame());
401 EXPECT_FALSE(target().didMissHwcFrame());
402 }
403}
404
405} // namespace android::scheduler