1 // Copyright 2011 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/scheduler/scheduler.h"
8 #include "base/auto_reset.h"
9 #include "base/debug/trace_event.h"
10 #include "base/logging.h"
11 #include "cc/debug/devtools_instrumentation.h"
12 #include "cc/debug/traced_value.h"
13 #include "ui/gfx/frame_time.h"
18 SchedulerClient
* client
,
19 const SchedulerSettings
& scheduler_settings
,
20 int layer_tree_host_id
,
21 const scoped_refptr
<base::SequencedTaskRunner
>& impl_task_runner
)
22 : settings_(scheduler_settings
),
24 layer_tree_host_id_(layer_tree_host_id
),
25 impl_task_runner_(impl_task_runner
),
26 last_set_needs_begin_frame_(false),
27 begin_retro_frame_posted_(false),
28 state_machine_(scheduler_settings
),
29 inside_process_scheduled_actions_(false),
30 inside_action_(SchedulerStateMachine::ACTION_NONE
),
33 DCHECK(!state_machine_
.BeginFrameNeeded());
34 if (settings_
.main_frame_before_activation_enabled
) {
35 DCHECK(settings_
.main_frame_before_draw_enabled
);
38 begin_retro_frame_closure_
=
39 base::Bind(&Scheduler::BeginRetroFrame
, weak_factory_
.GetWeakPtr());
40 begin_impl_frame_deadline_closure_
= base::Bind(
41 &Scheduler::OnBeginImplFrameDeadline
, weak_factory_
.GetWeakPtr());
42 poll_for_draw_triggers_closure_
= base::Bind(
43 &Scheduler::PollForAnticipatedDrawTriggers
, weak_factory_
.GetWeakPtr());
44 advance_commit_state_closure_
= base::Bind(
45 &Scheduler::PollToAdvanceCommitState
, weak_factory_
.GetWeakPtr());
48 Scheduler::~Scheduler() {}
50 void Scheduler::SetCanStart() {
51 state_machine_
.SetCanStart();
52 ProcessScheduledActions();
55 void Scheduler::SetVisible(bool visible
) {
56 state_machine_
.SetVisible(visible
);
57 ProcessScheduledActions();
60 void Scheduler::SetCanDraw(bool can_draw
) {
61 state_machine_
.SetCanDraw(can_draw
);
62 ProcessScheduledActions();
65 void Scheduler::NotifyReadyToActivate() {
66 state_machine_
.NotifyReadyToActivate();
67 ProcessScheduledActions();
70 void Scheduler::ActivatePendingTree() {
71 client_
->ScheduledActionActivatePendingTree();
74 void Scheduler::SetNeedsCommit() {
75 state_machine_
.SetNeedsCommit();
76 ProcessScheduledActions();
79 void Scheduler::SetNeedsForcedCommitForReadback() {
80 state_machine_
.SetNeedsForcedCommitForReadback();
81 ProcessScheduledActions();
84 void Scheduler::SetNeedsRedraw() {
85 state_machine_
.SetNeedsRedraw();
86 ProcessScheduledActions();
89 void Scheduler::SetNeedsManageTiles() {
90 DCHECK(!IsInsideAction(SchedulerStateMachine::ACTION_MANAGE_TILES
));
91 state_machine_
.SetNeedsManageTiles();
92 ProcessScheduledActions();
95 void Scheduler::SetSwapUsedIncompleteTile(bool used_incomplete_tile
) {
96 state_machine_
.SetSwapUsedIncompleteTile(used_incomplete_tile
);
97 ProcessScheduledActions();
100 void Scheduler::SetSmoothnessTakesPriority(bool smoothness_takes_priority
) {
101 state_machine_
.SetSmoothnessTakesPriority(smoothness_takes_priority
);
102 ProcessScheduledActions();
105 void Scheduler::NotifyReadyToCommit() {
106 TRACE_EVENT0("cc", "Scheduler::NotifyReadyToCommit");
107 state_machine_
.NotifyReadyToCommit();
108 ProcessScheduledActions();
111 void Scheduler::BeginMainFrameAborted(bool did_handle
) {
112 TRACE_EVENT0("cc", "Scheduler::BeginMainFrameAborted");
113 state_machine_
.BeginMainFrameAborted(did_handle
);
114 ProcessScheduledActions();
117 void Scheduler::DidManageTiles() {
118 state_machine_
.DidManageTiles();
121 void Scheduler::DidLoseOutputSurface() {
122 TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface");
123 state_machine_
.DidLoseOutputSurface();
124 last_set_needs_begin_frame_
= false;
125 begin_retro_frame_args_
.clear();
126 ProcessScheduledActions();
129 void Scheduler::DidCreateAndInitializeOutputSurface() {
130 TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface");
131 DCHECK(!last_set_needs_begin_frame_
);
132 DCHECK(begin_impl_frame_deadline_task_
.IsCancelled());
133 state_machine_
.DidCreateAndInitializeOutputSurface();
134 ProcessScheduledActions();
137 void Scheduler::NotifyBeginMainFrameStarted() {
138 TRACE_EVENT0("cc", "Scheduler::NotifyBeginMainFrameStarted");
139 state_machine_
.NotifyBeginMainFrameStarted();
142 base::TimeTicks
Scheduler::AnticipatedDrawTime() const {
143 if (!last_set_needs_begin_frame_
||
144 begin_impl_frame_args_
.interval
<= base::TimeDelta())
145 return base::TimeTicks();
147 base::TimeTicks now
= gfx::FrameTime::Now();
148 base::TimeTicks timebase
= std::max(begin_impl_frame_args_
.frame_time
,
149 begin_impl_frame_args_
.deadline
);
150 int64 intervals
= 1 + ((now
- timebase
) / begin_impl_frame_args_
.interval
);
151 return timebase
+ (begin_impl_frame_args_
.interval
* intervals
);
154 base::TimeTicks
Scheduler::LastBeginImplFrameTime() {
155 return begin_impl_frame_args_
.frame_time
;
158 void Scheduler::SetupNextBeginFrameIfNeeded() {
159 bool needs_begin_frame
= state_machine_
.BeginFrameNeeded();
161 bool at_end_of_deadline
=
162 state_machine_
.begin_impl_frame_state() ==
163 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE
;
165 bool should_call_set_needs_begin_frame
=
166 // Always request the BeginFrame immediately if it wasn't needed before.
167 (needs_begin_frame
&& !last_set_needs_begin_frame_
) ||
168 // We always need to explicitly request our next BeginFrame.
171 if (should_call_set_needs_begin_frame
) {
172 client_
->SetNeedsBeginFrame(needs_begin_frame
);
173 last_set_needs_begin_frame_
= needs_begin_frame
;
176 // Handle retroactive BeginFrames.
177 if (last_set_needs_begin_frame_
)
178 PostBeginRetroFrameIfNeeded();
180 bool needs_advance_commit_state_timer
= false;
181 // Setup PollForAnticipatedDrawTriggers if we need to monitor state but
182 // aren't expecting any more BeginFrames. This should only be needed by
183 // the synchronous compositor when BeginFrameNeeded is false.
184 if (state_machine_
.ShouldPollForAnticipatedDrawTriggers()) {
185 DCHECK(!state_machine_
.SupportsProactiveBeginFrame());
186 DCHECK(!needs_begin_frame
);
187 if (poll_for_draw_triggers_task_
.IsCancelled()) {
188 poll_for_draw_triggers_task_
.Reset(poll_for_draw_triggers_closure_
);
189 base::TimeDelta delay
= begin_impl_frame_args_
.IsValid()
190 ? begin_impl_frame_args_
.interval
191 : BeginFrameArgs::DefaultInterval();
192 impl_task_runner_
->PostDelayedTask(
193 FROM_HERE
, poll_for_draw_triggers_task_
.callback(), delay
);
196 poll_for_draw_triggers_task_
.Cancel();
198 // At this point we'd prefer to advance through the commit flow by
199 // drawing a frame, however it's possible that the frame rate controller
200 // will not give us a BeginFrame until the commit completes. See
201 // crbug.com/317430 for an example of a swap ack being held on commit. Thus
202 // we set a repeating timer to poll on ProcessScheduledActions until we
203 // successfully reach BeginFrame. Synchronous compositor does not use
204 // frame rate controller or have the circular wait in the bug.
205 if (IsBeginMainFrameSentOrStarted() &&
206 !settings_
.using_synchronous_renderer_compositor
) {
207 needs_advance_commit_state_timer
= true;
211 if (needs_advance_commit_state_timer
) {
212 if (advance_commit_state_task_
.IsCancelled() &&
213 begin_impl_frame_args_
.IsValid()) {
214 // Since we'd rather get a BeginImplFrame by the normal mechanism, we
215 // set the interval to twice the interval from the previous frame.
216 advance_commit_state_task_
.Reset(advance_commit_state_closure_
);
217 impl_task_runner_
->PostDelayedTask(FROM_HERE
,
218 advance_commit_state_task_
.callback(),
219 begin_impl_frame_args_
.interval
* 2);
222 advance_commit_state_task_
.Cancel();
226 // BeginFrame is the mechanism that tells us that now is a good time to start
227 // making a frame. Usually this means that user input for the frame is complete.
228 // If the scheduler is busy, we queue the BeginFrame to be handled later as
229 // a BeginRetroFrame.
230 void Scheduler::BeginFrame(const BeginFrameArgs
& args
) {
231 bool should_defer_begin_frame
;
232 if (settings_
.using_synchronous_renderer_compositor
) {
233 should_defer_begin_frame
= false;
235 should_defer_begin_frame
=
236 !begin_retro_frame_args_
.empty() || begin_retro_frame_posted_
||
237 !last_set_needs_begin_frame_
||
238 (state_machine_
.begin_impl_frame_state() !=
239 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
);
242 if (should_defer_begin_frame
) {
243 begin_retro_frame_args_
.push_back(args
);
244 TRACE_EVENT_INSTANT0(
245 "cc", "Scheduler::BeginFrame deferred", TRACE_EVENT_SCOPE_THREAD
);
249 BeginImplFrame(args
);
252 // BeginRetroFrame is called for BeginFrames that we've deferred because
253 // the scheduler was in the middle of processing a previous BeginFrame.
254 void Scheduler::BeginRetroFrame() {
255 TRACE_EVENT0("cc", "Scheduler::BeginRetroFrame");
256 DCHECK(begin_retro_frame_posted_
);
257 DCHECK(!begin_retro_frame_args_
.empty());
258 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
260 // Discard expired BeginRetroFrames
261 // Today, we should always end up with at most one un-expired BeginRetroFrame
262 // because deadlines will not be greater than the next frame time. We don't
263 // DCHECK though because some systems don't always have monotonic timestamps.
264 // TODO(brianderson): In the future, long deadlines could result in us not
265 // draining the queue if we don't catch up. If we consistently can't catch
266 // up, our fallback should be to lower our frame rate.
267 base::TimeTicks now
= gfx::FrameTime::Now();
268 base::TimeDelta draw_duration_estimate
= client_
->DrawDurationEstimate();
269 while (!begin_retro_frame_args_
.empty() &&
270 now
> AdjustedBeginImplFrameDeadline(begin_retro_frame_args_
.front(),
271 draw_duration_estimate
)) {
272 begin_retro_frame_args_
.pop_front();
275 if (begin_retro_frame_args_
.empty()) {
276 TRACE_EVENT_INSTANT0(
277 "cc", "Scheduler::BeginRetroFrames expired", TRACE_EVENT_SCOPE_THREAD
);
279 BeginImplFrame(begin_retro_frame_args_
.front());
280 begin_retro_frame_args_
.pop_front();
283 begin_retro_frame_posted_
= false;
286 // There could be a race between the posted BeginRetroFrame and a new
287 // BeginFrame arriving via the normal mechanism. Scheduler::BeginFrame
288 // will check if there is a pending BeginRetroFrame to ensure we handle
289 // BeginFrames in FIFO order.
290 void Scheduler::PostBeginRetroFrameIfNeeded() {
291 if (begin_retro_frame_args_
.empty() || begin_retro_frame_posted_
)
294 // begin_retro_frame_args_ should always be empty for the
295 // synchronous compositor.
296 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
298 if (state_machine_
.begin_impl_frame_state() !=
299 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
)
302 begin_retro_frame_posted_
= true;
303 impl_task_runner_
->PostTask(FROM_HERE
, begin_retro_frame_closure_
);
306 // BeginImplFrame starts a compositor frame that will wait up until a deadline
307 // for a BeginMainFrame+activation to complete before it times out and draws
308 // any asynchronous animation and scroll/pinch updates.
309 void Scheduler::BeginImplFrame(const BeginFrameArgs
& args
) {
310 TRACE_EVENT0("cc", "Scheduler::BeginImplFrame");
311 DCHECK(state_machine_
.begin_impl_frame_state() ==
312 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
);
313 DCHECK(state_machine_
.HasInitializedOutputSurface());
315 advance_commit_state_task_
.Cancel();
317 base::TimeDelta draw_duration_estimate
= client_
->DrawDurationEstimate();
318 begin_impl_frame_args_
= args
;
319 begin_impl_frame_args_
.deadline
-= draw_duration_estimate
;
321 if (!state_machine_
.smoothness_takes_priority() &&
322 state_machine_
.MainThreadIsInHighLatencyMode() &&
323 CanCommitAndActivateBeforeDeadline()) {
324 state_machine_
.SetSkipNextBeginMainFrameToReduceLatency();
327 client_
->WillBeginImplFrame(begin_impl_frame_args_
);
328 state_machine_
.OnBeginImplFrame(begin_impl_frame_args_
);
329 devtools_instrumentation::DidBeginFrame(layer_tree_host_id_
);
331 ProcessScheduledActions();
333 if (!state_machine_
.HasInitializedOutputSurface())
336 state_machine_
.OnBeginImplFrameDeadlinePending();
337 ScheduleBeginImplFrameDeadline(
338 AdjustedBeginImplFrameDeadline(args
, draw_duration_estimate
));
341 base::TimeTicks
Scheduler::AdjustedBeginImplFrameDeadline(
342 const BeginFrameArgs
& args
,
343 base::TimeDelta draw_duration_estimate
) const {
344 if (settings_
.using_synchronous_renderer_compositor
) {
345 // The synchronous compositor needs to draw right away.
346 return base::TimeTicks();
347 } else if (state_machine_
.ShouldTriggerBeginImplFrameDeadlineEarly()) {
348 // We are ready to draw a new active tree immediately.
349 return base::TimeTicks();
350 } else if (state_machine_
.needs_redraw()) {
351 // We have an animation or fast input path on the impl thread that wants
352 // to draw, so don't wait too long for a new active tree.
353 return args
.deadline
- draw_duration_estimate
;
355 // The impl thread doesn't have anything it wants to draw and we are just
356 // waiting for a new active tree, so post the deadline for the next
357 // expected BeginImplFrame start. This allows us to draw immediately when
358 // there is a new active tree, instead of waiting for the next
360 // TODO(brianderson): Handle long deadlines (that are past the next frame's
361 // frame time) properly instead of using this hack.
362 return args
.frame_time
+ args
.interval
;
366 void Scheduler::ScheduleBeginImplFrameDeadline(base::TimeTicks deadline
) {
367 if (settings_
.using_synchronous_renderer_compositor
) {
368 // The synchronous renderer compositor has to make its GL calls
370 // TODO(brianderson): Have the OutputSurface initiate the deadline tasks
371 // so the sychronous renderer compositor can take advantage of splitting
372 // up the BeginImplFrame and deadline as well.
373 OnBeginImplFrameDeadline();
376 begin_impl_frame_deadline_task_
.Cancel();
377 begin_impl_frame_deadline_task_
.Reset(begin_impl_frame_deadline_closure_
);
379 base::TimeDelta delta
= deadline
- gfx::FrameTime::Now();
380 if (delta
<= base::TimeDelta())
381 delta
= base::TimeDelta();
382 impl_task_runner_
->PostDelayedTask(
383 FROM_HERE
, begin_impl_frame_deadline_task_
.callback(), delta
);
386 void Scheduler::OnBeginImplFrameDeadline() {
387 TRACE_EVENT0("cc", "Scheduler::OnBeginImplFrameDeadline");
388 begin_impl_frame_deadline_task_
.Cancel();
390 // We split the deadline actions up into two phases so the state machine
391 // has a chance to trigger actions that should occur durring and after
392 // the deadline separately. For example:
393 // * Sending the BeginMainFrame will not occur after the deadline in
394 // order to wait for more user-input before starting the next commit.
395 // * Creating a new OuputSurface will not occur during the deadline in
396 // order to allow the state machine to "settle" first.
397 state_machine_
.OnBeginImplFrameDeadline();
398 ProcessScheduledActions();
399 state_machine_
.OnBeginImplFrameIdle();
400 ProcessScheduledActions();
402 client_
->DidBeginImplFrameDeadline();
405 void Scheduler::PollForAnticipatedDrawTriggers() {
406 TRACE_EVENT0("cc", "Scheduler::PollForAnticipatedDrawTriggers");
407 poll_for_draw_triggers_task_
.Cancel();
408 state_machine_
.DidEnterPollForAnticipatedDrawTriggers();
409 ProcessScheduledActions();
410 state_machine_
.DidLeavePollForAnticipatedDrawTriggers();
413 void Scheduler::PollToAdvanceCommitState() {
414 TRACE_EVENT0("cc", "Scheduler::PollToAdvanceCommitState");
415 advance_commit_state_task_
.Cancel();
416 ProcessScheduledActions();
419 bool Scheduler::IsBeginMainFrameSent() const {
420 return state_machine_
.commit_state() ==
421 SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT
;
424 void Scheduler::DrawAndSwapIfPossible() {
425 DrawSwapReadbackResult result
=
426 client_
->ScheduledActionDrawAndSwapIfPossible();
427 state_machine_
.DidDrawIfPossibleCompleted(result
.draw_result
);
430 void Scheduler::DrawAndSwapForced() {
431 client_
->ScheduledActionDrawAndSwapForced();
434 void Scheduler::DrawAndReadback() {
435 DrawSwapReadbackResult result
= client_
->ScheduledActionDrawAndReadback();
436 DCHECK(!result
.did_swap
);
439 void Scheduler::ProcessScheduledActions() {
440 // We do not allow ProcessScheduledActions to be recursive.
441 // The top-level call will iteratively execute the next action for us anyway.
442 if (inside_process_scheduled_actions_
)
445 base::AutoReset
<bool> mark_inside(&inside_process_scheduled_actions_
, true);
447 SchedulerStateMachine::Action action
;
449 state_machine_
.CheckInvariants();
450 action
= state_machine_
.NextAction();
451 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
452 "SchedulerStateMachine",
454 TracedValue::FromValue(StateAsValue().release()));
455 state_machine_
.UpdateState(action
);
456 base::AutoReset
<SchedulerStateMachine::Action
>
457 mark_inside_action(&inside_action_
, action
);
459 case SchedulerStateMachine::ACTION_NONE
:
461 case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME
:
462 client_
->ScheduledActionSendBeginMainFrame();
464 case SchedulerStateMachine::ACTION_COMMIT
:
465 client_
->ScheduledActionCommit();
467 case SchedulerStateMachine::ACTION_UPDATE_VISIBLE_TILES
:
468 client_
->ScheduledActionUpdateVisibleTiles();
470 case SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE
:
471 ActivatePendingTree();
473 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE
:
474 DrawAndSwapIfPossible();
476 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED
:
479 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT
:
480 // No action is actually performed, but this allows the state machine to
481 // advance out of its waiting to draw state without actually drawing.
483 case SchedulerStateMachine::ACTION_DRAW_AND_READBACK
:
486 case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION
:
487 client_
->ScheduledActionBeginOutputSurfaceCreation();
489 case SchedulerStateMachine::ACTION_MANAGE_TILES
:
490 client_
->ScheduledActionManageTiles();
493 } while (action
!= SchedulerStateMachine::ACTION_NONE
);
495 SetupNextBeginFrameIfNeeded();
496 client_
->DidAnticipatedDrawTimeChange(AnticipatedDrawTime());
498 if (state_machine_
.ShouldTriggerBeginImplFrameDeadlineEarly()) {
499 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
500 ScheduleBeginImplFrameDeadline(base::TimeTicks());
504 bool Scheduler::WillDrawIfNeeded() const {
505 return !state_machine_
.PendingDrawsShouldBeAborted();
508 scoped_ptr
<base::Value
> Scheduler::StateAsValue() const {
509 scoped_ptr
<base::DictionaryValue
> state(new base::DictionaryValue
);
510 state
->Set("state_machine", state_machine_
.AsValue().release());
512 scoped_ptr
<base::DictionaryValue
> scheduler_state(new base::DictionaryValue
);
513 scheduler_state
->SetDouble(
514 "time_until_anticipated_draw_time_ms",
515 (AnticipatedDrawTime() - base::TimeTicks::Now()).InMillisecondsF());
516 scheduler_state
->SetBoolean("last_set_needs_begin_frame_",
517 last_set_needs_begin_frame_
);
518 scheduler_state
->SetBoolean("begin_impl_frame_deadline_task_",
519 !begin_impl_frame_deadline_task_
.IsCancelled());
520 scheduler_state
->SetBoolean("poll_for_draw_triggers_task_",
521 !poll_for_draw_triggers_task_
.IsCancelled());
522 scheduler_state
->SetBoolean("advance_commit_state_task_",
523 !advance_commit_state_task_
.IsCancelled());
524 state
->Set("scheduler_state", scheduler_state
.release());
526 scoped_ptr
<base::DictionaryValue
> client_state(new base::DictionaryValue
);
527 client_state
->SetDouble("draw_duration_estimate_ms",
528 client_
->DrawDurationEstimate().InMillisecondsF());
529 client_state
->SetDouble(
530 "begin_main_frame_to_commit_duration_estimate_ms",
531 client_
->BeginMainFrameToCommitDurationEstimate().InMillisecondsF());
532 client_state
->SetDouble(
533 "commit_to_activate_duration_estimate_ms",
534 client_
->CommitToActivateDurationEstimate().InMillisecondsF());
535 state
->Set("client_state", client_state
.release());
536 return state
.PassAs
<base::Value
>();
539 bool Scheduler::CanCommitAndActivateBeforeDeadline() const {
540 // Check if the main thread computation and commit can be finished before the
541 // impl thread's deadline.
542 base::TimeTicks estimated_draw_time
=
543 begin_impl_frame_args_
.frame_time
+
544 client_
->BeginMainFrameToCommitDurationEstimate() +
545 client_
->CommitToActivateDurationEstimate();
548 TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
549 "CanCommitAndActivateBeforeDeadline",
550 "time_left_after_drawing_ms",
551 (begin_impl_frame_args_
.deadline
- estimated_draw_time
).InMillisecondsF(),
553 TracedValue::FromValue(StateAsValue().release()));
555 return estimated_draw_time
< begin_impl_frame_args_
.deadline
;
558 bool Scheduler::IsBeginMainFrameSentOrStarted() const {
559 return (state_machine_
.commit_state() ==
560 SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT
||
561 state_machine_
.commit_state() ==
562 SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED
);