1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/surfaces/display_scheduler.h"
7 #include "base/logging.h"
8 #include "base/test/null_task_runner.h"
9 #include "base/test/simple_test_tick_clock.h"
10 #include "base/trace_event/trace_event.h"
11 #include "cc/output/begin_frame_args.h"
12 #include "cc/surfaces/display.h"
13 #include "cc/test/scheduler_test_common.h"
14 #include "testing/gtest/include/gtest/gtest.h"
19 class FakeDisplaySchedulerClient
: public DisplaySchedulerClient
{
21 FakeDisplaySchedulerClient() : draw_and_swap_count_(0) {}
23 ~FakeDisplaySchedulerClient() override
{}
25 bool DrawAndSwap() override
{
26 draw_and_swap_count_
++;
30 void Reset() { draw_and_swap_count_
= 0; }
32 int draw_and_swap_count() const { return draw_and_swap_count_
; }
35 int draw_and_swap_count_
;
38 class TestDisplayScheduler
: public DisplayScheduler
{
40 TestDisplayScheduler(DisplaySchedulerClient
* client
,
41 BeginFrameSource
* begin_frame_source
,
42 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
,
43 int max_pending_swaps
)
44 : DisplayScheduler(client
,
48 scheduler_begin_frame_deadline_count_(0) {}
50 base::TimeTicks
DesiredBeginFrameDeadlineTimeForTest() {
51 return DesiredBeginFrameDeadlineTime();
54 void BeginFrameDeadlineForTest() { OnBeginFrameDeadline(); }
56 void ScheduleBeginFrameDeadline() override
{
57 scheduler_begin_frame_deadline_count_
++;
58 DisplayScheduler::ScheduleBeginFrameDeadline();
61 int scheduler_begin_frame_deadline_count() {
62 return scheduler_begin_frame_deadline_count_
;
66 int scheduler_begin_frame_deadline_count_
;
69 class DisplaySchedulerTest
: public testing::Test
{
71 DisplaySchedulerTest() : now_src_(new base::SimpleTestTickClock()) {
72 const int max_pending_swaps
= 1;
73 now_src_
->Advance(base::TimeDelta::FromMicroseconds(10000));
74 null_task_runner_
= make_scoped_refptr(new base::NullTaskRunner
);
75 client_
= make_scoped_ptr(new FakeDisplaySchedulerClient
);
76 scheduler_
= make_scoped_ptr(
77 new TestDisplayScheduler(client_
.get(), &fake_begin_frame_source_
,
78 null_task_runner_
, max_pending_swaps
));
81 ~DisplaySchedulerTest() override
{}
83 void SetUp() override
{ scheduler_
->SetRootSurfaceResourcesLocked(false); }
85 void BeginFrameForTest() {
86 base::TimeTicks frame_time
= now_src_
->NowTicks();
87 base::TimeDelta interval
= BeginFrameArgs::DefaultInterval();
88 base::TimeTicks deadline
= frame_time
+ interval
;
89 fake_begin_frame_source_
.TestOnBeginFrame(
90 BeginFrameArgs::Create(BEGINFRAME_FROM_HERE
, frame_time
, deadline
,
91 interval
, BeginFrameArgs::NORMAL
));
95 base::SimpleTestTickClock
& now_src() { return *now_src_
; }
96 FakeDisplaySchedulerClient
& client() { return *client_
; }
97 DisplayScheduler
& scheduler() { return *scheduler_
; }
99 scoped_ptr
<base::SimpleTestTickClock
> now_src_
;
100 scoped_refptr
<base::NullTaskRunner
> null_task_runner_
;
102 FakeBeginFrameSource fake_begin_frame_source_
;
103 scoped_ptr
<FakeDisplaySchedulerClient
> client_
;
104 scoped_ptr
<TestDisplayScheduler
> scheduler_
;
107 TEST_F(DisplaySchedulerTest
, EntireDisplayDamagedDrawsImmediately
) {
108 SurfaceId
root_surface_id(1);
110 EXPECT_LT(now_src().NowTicks(),
111 scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
112 scheduler_
->EntireDisplayDamaged(root_surface_id
);
113 EXPECT_GE(now_src().NowTicks(),
114 scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
117 TEST_F(DisplaySchedulerTest
, SurfaceDamaged
) {
118 SurfaceId
root_surface_id(0);
122 // Set the root surface
123 scheduler_
->EntireDisplayDamaged(root_surface_id
);
125 // Get scheduler to detect surface 1 as active by drawing
126 // two frames in a row with damage from surface 1.
128 scheduler_
->SurfaceDamaged(sid1
);
129 scheduler_
->BeginFrameDeadlineForTest();
131 scheduler_
->SurfaceDamaged(sid1
);
132 scheduler_
->BeginFrameDeadlineForTest();
134 // Damage only from surface 2 (inactive) does not trigger deadline early.
136 scheduler_
->SurfaceDamaged(sid2
);
137 EXPECT_LT(now_src().NowTicks(),
138 scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
140 // Damage from surface 1 triggers deadline early.
141 scheduler_
->SurfaceDamaged(sid1
);
142 EXPECT_GE(now_src().NowTicks(),
143 scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
144 scheduler_
->BeginFrameDeadlineForTest();
146 // Make both surface 1 and 2 active.
148 scheduler_
->SurfaceDamaged(sid2
);
149 scheduler_
->SurfaceDamaged(sid1
);
150 scheduler_
->BeginFrameDeadlineForTest();
152 // Deadline doesn't trigger early until surface 1 and 2 are both damaged.
154 EXPECT_LT(now_src().NowTicks(),
155 scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
156 scheduler_
->SurfaceDamaged(sid1
);
157 EXPECT_LT(now_src().NowTicks(),
158 scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
159 scheduler_
->SurfaceDamaged(sid2
);
160 EXPECT_GE(now_src().NowTicks(),
161 scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
162 scheduler_
->BeginFrameDeadlineForTest();
164 // Make the system idle
166 scheduler_
->BeginFrameDeadlineForTest();
168 scheduler_
->BeginFrameDeadlineForTest();
170 // Deadline should trigger early if child surfaces are idle and
171 // we get damage on the root surface.
173 EXPECT_LT(now_src().NowTicks(),
174 scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
175 scheduler_
->SurfaceDamaged(root_surface_id
);
176 EXPECT_GE(now_src().NowTicks(),
177 scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
178 scheduler_
->BeginFrameDeadlineForTest();
181 TEST_F(DisplaySchedulerTest
, OutputSurfaceLost
) {
184 // DrawAndSwap normally.
186 EXPECT_LT(now_src().NowTicks(),
187 scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
188 EXPECT_EQ(0, client_
->draw_and_swap_count());
189 scheduler_
->SurfaceDamaged(sid1
);
190 scheduler_
->BeginFrameDeadlineForTest();
191 EXPECT_EQ(1, client_
->draw_and_swap_count());
193 // Deadline triggers immediately on OutputSurfaceLost.
195 EXPECT_LT(now_src().NowTicks(),
196 scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
197 scheduler_
->OutputSurfaceLost();
198 EXPECT_GE(now_src().NowTicks(),
199 scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
201 // Deadline does not DrawAndSwap after OutputSurfaceLost.
202 EXPECT_EQ(1, client_
->draw_and_swap_count());
203 scheduler_
->SurfaceDamaged(sid1
);
204 scheduler_
->BeginFrameDeadlineForTest();
205 EXPECT_EQ(1, client_
->draw_and_swap_count());
208 TEST_F(DisplaySchedulerTest
, RootSurfaceResourcesLocked
) {
210 base::TimeTicks late_deadline
;
212 // DrawAndSwap normally.
214 EXPECT_LT(now_src().NowTicks(),
215 scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
216 EXPECT_EQ(0, client_
->draw_and_swap_count());
217 scheduler_
->SurfaceDamaged(sid1
);
218 scheduler_
->BeginFrameDeadlineForTest();
219 EXPECT_EQ(1, client_
->draw_and_swap_count());
221 // Deadline triggers late while root resources are locked.
222 late_deadline
= now_src().NowTicks() + BeginFrameArgs::DefaultInterval();
224 scheduler_
->SurfaceDamaged(sid1
);
225 EXPECT_GT(late_deadline
, scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
226 scheduler_
->SetRootSurfaceResourcesLocked(true);
227 EXPECT_EQ(late_deadline
, scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
229 // Deadline does not DrawAndSwap while root resources are locked.
230 EXPECT_EQ(1, client_
->draw_and_swap_count());
231 scheduler_
->SurfaceDamaged(sid1
);
232 scheduler_
->BeginFrameDeadlineForTest();
233 EXPECT_EQ(1, client_
->draw_and_swap_count());
235 // Deadline triggers normally when root resources are unlocked.
236 late_deadline
= now_src().NowTicks() + BeginFrameArgs::DefaultInterval();
238 scheduler_
->SurfaceDamaged(sid1
);
239 EXPECT_EQ(late_deadline
, scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
240 scheduler_
->SetRootSurfaceResourcesLocked(false);
241 EXPECT_EQ(base::TimeTicks(),
242 scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
244 EXPECT_EQ(1, client_
->draw_and_swap_count());
245 scheduler_
->BeginFrameDeadlineForTest();
246 EXPECT_EQ(2, client_
->draw_and_swap_count());
249 TEST_F(DisplaySchedulerTest
, DidSwapBuffers
) {
252 base::TimeTicks expected_deadline
;
254 // Get scheduler to detect surface 1 and 2 as active.
256 scheduler_
->SurfaceDamaged(sid1
);
257 scheduler_
->SurfaceDamaged(sid2
);
258 scheduler_
->BeginFrameDeadlineForTest();
260 scheduler_
->SurfaceDamaged(sid1
);
261 scheduler_
->SurfaceDamaged(sid2
);
262 scheduler_
->BeginFrameDeadlineForTest();
264 // DrawAndSwap normally.
266 EXPECT_LT(now_src().NowTicks(),
267 scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
268 EXPECT_EQ(2, client_
->draw_and_swap_count());
269 scheduler_
->SurfaceDamaged(sid1
);
270 scheduler_
->SurfaceDamaged(sid2
);
271 scheduler_
->BeginFrameDeadlineForTest();
272 EXPECT_EQ(3, client_
->draw_and_swap_count());
273 scheduler_
->DidSwapBuffers();
275 // Deadline triggers normally when swap throttled.
277 fake_begin_frame_source_
.TestLastUsedBeginFrameArgs().deadline
-
278 BeginFrameArgs::DefaultEstimatedParentDrawTime();
280 // Damage surface 1, but not surface 2 so we avoid triggering deadline
281 // early because all surfaces are ready.
282 scheduler_
->SurfaceDamaged(sid1
);
283 EXPECT_EQ(expected_deadline
,
284 scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
286 // Don't draw and swap in deadline while swap throttled.
287 EXPECT_EQ(3, client_
->draw_and_swap_count());
288 scheduler_
->BeginFrameDeadlineForTest();
289 EXPECT_EQ(3, client_
->draw_and_swap_count());
291 // Deadline triggers normally once not swap throttled.
292 // Damage from previous BeginFrame should cary over, so don't damage again.
294 fake_begin_frame_source_
.TestLastUsedBeginFrameArgs().deadline
-
295 BeginFrameArgs::DefaultEstimatedParentDrawTime();
296 scheduler_
->DidSwapBuffersComplete();
298 EXPECT_EQ(expected_deadline
,
299 scheduler_
->DesiredBeginFrameDeadlineTimeForTest());
300 // Still waiting for surface 2. Once it updates, deadline should trigger
301 // immediately again.
302 scheduler_
->SurfaceDamaged(sid2
);
303 EXPECT_EQ(scheduler_
->DesiredBeginFrameDeadlineTimeForTest(),
305 // Draw and swap now that we aren't throttled.
306 EXPECT_EQ(3, client_
->draw_and_swap_count());
307 scheduler_
->BeginFrameDeadlineForTest();
308 EXPECT_EQ(4, client_
->draw_and_swap_count());
311 // This test verfies that we try to reschedule the deadline
312 // after any event that may change what deadline we want.
313 TEST_F(DisplaySchedulerTest
, ScheduleBeginFrameDeadline
) {
314 SurfaceId
root_surface_id(1);
317 EXPECT_EQ(count
++, scheduler_
->scheduler_begin_frame_deadline_count());
320 EXPECT_EQ(count
++, scheduler_
->scheduler_begin_frame_deadline_count());
322 scheduler_
->BeginFrameDeadlineForTest();
323 scheduler_
->DidSwapBuffers();
325 EXPECT_EQ(count
++, scheduler_
->scheduler_begin_frame_deadline_count());
327 scheduler_
->DidSwapBuffersComplete();
328 EXPECT_EQ(count
++, scheduler_
->scheduler_begin_frame_deadline_count());
330 scheduler_
->EntireDisplayDamaged(root_surface_id
);
331 EXPECT_EQ(count
++, scheduler_
->scheduler_begin_frame_deadline_count());
333 scheduler_
->SurfaceDamaged(sid1
);
334 EXPECT_EQ(count
++, scheduler_
->scheduler_begin_frame_deadline_count());
336 scheduler_
->SetRootSurfaceResourcesLocked(true);
337 EXPECT_EQ(count
++, scheduler_
->scheduler_begin_frame_deadline_count());
339 scheduler_
->OutputSurfaceLost();
340 EXPECT_EQ(count
++, scheduler_
->scheduler_begin_frame_deadline_count());