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/debug/trace_event_argument.h"
11 #include "base/logging.h"
12 #include "base/single_thread_task_runner.h"
13 #include "cc/debug/devtools_instrumentation.h"
14 #include "cc/debug/traced_value.h"
15 #include "cc/scheduler/delay_based_time_source.h"
16 #include "ui/gfx/frame_time.h"
20 Scheduler::SyntheticBeginFrameSource::SyntheticBeginFrameSource(
22 base::SingleThreadTaskRunner
* task_runner
)
23 : scheduler_(scheduler
) {
24 if (gfx::FrameTime::TimestampsAreHighRes()) {
25 time_source_
= DelayBasedTimeSourceHighRes::Create(
26 scheduler_
->VSyncInterval(), task_runner
);
28 time_source_
= DelayBasedTimeSource::Create(scheduler_
->VSyncInterval(),
31 time_source_
->SetClient(this);
34 Scheduler::SyntheticBeginFrameSource::~SyntheticBeginFrameSource() {
37 void Scheduler::SyntheticBeginFrameSource::CommitVSyncParameters(
38 base::TimeTicks timebase
,
39 base::TimeDelta interval
) {
40 time_source_
->SetTimebaseAndInterval(timebase
, interval
);
43 void Scheduler::SyntheticBeginFrameSource::SetNeedsBeginFrame(
44 bool needs_begin_frame
,
45 std::deque
<BeginFrameArgs
>* begin_retro_frame_args
) {
46 DCHECK(begin_retro_frame_args
);
47 base::TimeTicks missed_tick_time
=
48 time_source_
->SetActive(needs_begin_frame
);
49 if (!missed_tick_time
.is_null()) {
50 begin_retro_frame_args
->push_back(
51 CreateSyntheticBeginFrameArgs(missed_tick_time
));
55 bool Scheduler::SyntheticBeginFrameSource::IsActive() const {
56 return time_source_
->Active();
59 void Scheduler::SyntheticBeginFrameSource::OnTimerTick() {
60 BeginFrameArgs
begin_frame_args(
61 CreateSyntheticBeginFrameArgs(time_source_
->LastTickTime()));
62 scheduler_
->BeginFrame(begin_frame_args
);
65 void Scheduler::SyntheticBeginFrameSource::AsValueInto(
66 base::debug::TracedValue
* state
) const {
67 time_source_
->AsValueInto(state
);
71 Scheduler::SyntheticBeginFrameSource::CreateSyntheticBeginFrameArgs(
72 base::TimeTicks frame_time
) {
73 base::TimeTicks deadline
= time_source_
->NextTickTime();
74 return BeginFrameArgs::Create(
75 frame_time
, deadline
, scheduler_
->VSyncInterval());
79 SchedulerClient
* client
,
80 const SchedulerSettings
& scheduler_settings
,
81 int layer_tree_host_id
,
82 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
)
83 : settings_(scheduler_settings
),
85 layer_tree_host_id_(layer_tree_host_id
),
86 task_runner_(task_runner
),
87 vsync_interval_(BeginFrameArgs::DefaultInterval()),
88 last_set_needs_begin_frame_(false),
89 begin_unthrottled_frame_posted_(false),
90 begin_retro_frame_posted_(false),
91 state_machine_(scheduler_settings
),
92 inside_process_scheduled_actions_(false),
93 inside_action_(SchedulerStateMachine::ACTION_NONE
),
95 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
96 "Scheduler::Scheduler",
100 DCHECK(!state_machine_
.BeginFrameNeeded());
101 if (settings_
.main_frame_before_activation_enabled
) {
102 DCHECK(settings_
.main_frame_before_draw_enabled
);
105 begin_retro_frame_closure_
=
106 base::Bind(&Scheduler::BeginRetroFrame
, weak_factory_
.GetWeakPtr());
107 begin_unthrottled_frame_closure_
=
108 base::Bind(&Scheduler::BeginUnthrottledFrame
, weak_factory_
.GetWeakPtr());
109 begin_impl_frame_deadline_closure_
= base::Bind(
110 &Scheduler::OnBeginImplFrameDeadline
, weak_factory_
.GetWeakPtr());
111 poll_for_draw_triggers_closure_
= base::Bind(
112 &Scheduler::PollForAnticipatedDrawTriggers
, weak_factory_
.GetWeakPtr());
113 advance_commit_state_closure_
= base::Bind(
114 &Scheduler::PollToAdvanceCommitState
, weak_factory_
.GetWeakPtr());
116 if (!settings_
.begin_frame_scheduling_enabled
) {
117 SetupSyntheticBeginFrames();
121 Scheduler::~Scheduler() {
122 if (synthetic_begin_frame_source_
) {
123 synthetic_begin_frame_source_
->SetNeedsBeginFrame(false,
124 &begin_retro_frame_args_
);
128 void Scheduler::SetupSyntheticBeginFrames() {
129 DCHECK(!synthetic_begin_frame_source_
);
130 synthetic_begin_frame_source_
.reset(
131 new SyntheticBeginFrameSource(this, task_runner_
.get()));
134 void Scheduler::CommitVSyncParameters(base::TimeTicks timebase
,
135 base::TimeDelta interval
) {
136 // TODO(brianderson): We should not be receiving 0 intervals.
137 if (interval
== base::TimeDelta())
138 interval
= BeginFrameArgs::DefaultInterval();
139 vsync_interval_
= interval
;
140 if (!settings_
.begin_frame_scheduling_enabled
)
141 synthetic_begin_frame_source_
->CommitVSyncParameters(timebase
, interval
);
144 void Scheduler::SetEstimatedParentDrawTime(base::TimeDelta draw_time
) {
145 DCHECK_GE(draw_time
.ToInternalValue(), 0);
146 estimated_parent_draw_time_
= draw_time
;
149 void Scheduler::SetCanStart() {
150 state_machine_
.SetCanStart();
151 ProcessScheduledActions();
154 void Scheduler::SetVisible(bool visible
) {
155 state_machine_
.SetVisible(visible
);
156 ProcessScheduledActions();
159 void Scheduler::SetCanDraw(bool can_draw
) {
160 state_machine_
.SetCanDraw(can_draw
);
161 ProcessScheduledActions();
164 void Scheduler::NotifyReadyToActivate() {
165 state_machine_
.NotifyReadyToActivate();
166 ProcessScheduledActions();
169 void Scheduler::SetNeedsCommit() {
170 state_machine_
.SetNeedsCommit();
171 ProcessScheduledActions();
174 void Scheduler::SetNeedsRedraw() {
175 state_machine_
.SetNeedsRedraw();
176 ProcessScheduledActions();
179 void Scheduler::SetNeedsAnimate() {
180 state_machine_
.SetNeedsAnimate();
181 ProcessScheduledActions();
184 void Scheduler::SetNeedsManageTiles() {
185 DCHECK(!IsInsideAction(SchedulerStateMachine::ACTION_MANAGE_TILES
));
186 state_machine_
.SetNeedsManageTiles();
187 ProcessScheduledActions();
190 void Scheduler::SetMaxSwapsPending(int max
) {
191 state_machine_
.SetMaxSwapsPending(max
);
194 void Scheduler::DidSwapBuffers() {
195 state_machine_
.DidSwapBuffers();
197 // There is no need to call ProcessScheduledActions here because
198 // swapping should not trigger any new actions.
199 if (!inside_process_scheduled_actions_
) {
200 DCHECK_EQ(state_machine_
.NextAction(), SchedulerStateMachine::ACTION_NONE
);
204 void Scheduler::SetSwapUsedIncompleteTile(bool used_incomplete_tile
) {
205 state_machine_
.SetSwapUsedIncompleteTile(used_incomplete_tile
);
206 ProcessScheduledActions();
209 void Scheduler::DidSwapBuffersComplete() {
210 state_machine_
.DidSwapBuffersComplete();
211 ProcessScheduledActions();
214 void Scheduler::SetImplLatencyTakesPriority(bool impl_latency_takes_priority
) {
215 state_machine_
.SetImplLatencyTakesPriority(impl_latency_takes_priority
);
216 ProcessScheduledActions();
219 void Scheduler::NotifyReadyToCommit() {
220 TRACE_EVENT0("cc", "Scheduler::NotifyReadyToCommit");
221 state_machine_
.NotifyReadyToCommit();
222 ProcessScheduledActions();
225 void Scheduler::BeginMainFrameAborted(bool did_handle
) {
226 TRACE_EVENT0("cc", "Scheduler::BeginMainFrameAborted");
227 state_machine_
.BeginMainFrameAborted(did_handle
);
228 ProcessScheduledActions();
231 void Scheduler::DidManageTiles() {
232 state_machine_
.DidManageTiles();
235 void Scheduler::DidLoseOutputSurface() {
236 TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface");
237 state_machine_
.DidLoseOutputSurface();
238 last_set_needs_begin_frame_
= false;
239 if (!settings_
.begin_frame_scheduling_enabled
) {
240 synthetic_begin_frame_source_
->SetNeedsBeginFrame(false,
241 &begin_retro_frame_args_
);
243 begin_retro_frame_args_
.clear();
244 ProcessScheduledActions();
247 void Scheduler::DidCreateAndInitializeOutputSurface() {
248 TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface");
249 DCHECK(!last_set_needs_begin_frame_
);
250 DCHECK(begin_impl_frame_deadline_task_
.IsCancelled());
251 state_machine_
.DidCreateAndInitializeOutputSurface();
252 ProcessScheduledActions();
255 void Scheduler::NotifyBeginMainFrameStarted() {
256 TRACE_EVENT0("cc", "Scheduler::NotifyBeginMainFrameStarted");
257 state_machine_
.NotifyBeginMainFrameStarted();
260 base::TimeTicks
Scheduler::AnticipatedDrawTime() const {
261 if (!last_set_needs_begin_frame_
||
262 begin_impl_frame_args_
.interval
<= base::TimeDelta())
263 return base::TimeTicks();
265 base::TimeTicks now
= gfx::FrameTime::Now();
266 base::TimeTicks timebase
= std::max(begin_impl_frame_args_
.frame_time
,
267 begin_impl_frame_args_
.deadline
);
268 int64 intervals
= 1 + ((now
- timebase
) / begin_impl_frame_args_
.interval
);
269 return timebase
+ (begin_impl_frame_args_
.interval
* intervals
);
272 base::TimeTicks
Scheduler::LastBeginImplFrameTime() {
273 return begin_impl_frame_args_
.frame_time
;
276 void Scheduler::SetupNextBeginFrameIfNeeded() {
277 if (!task_runner_
.get())
280 bool needs_begin_frame
= state_machine_
.BeginFrameNeeded();
282 if (settings_
.throttle_frame_production
) {
283 SetupNextBeginFrameWhenVSyncThrottlingEnabled(needs_begin_frame
);
285 SetupNextBeginFrameWhenVSyncThrottlingDisabled(needs_begin_frame
);
287 SetupPollingMechanisms(needs_begin_frame
);
290 // When we are throttling frame production, we request BeginFrames
291 // from the OutputSurface.
292 void Scheduler::SetupNextBeginFrameWhenVSyncThrottlingEnabled(
293 bool needs_begin_frame
) {
294 bool at_end_of_deadline
=
295 state_machine_
.begin_impl_frame_state() ==
296 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE
;
298 bool should_call_set_needs_begin_frame
=
299 // Always request the BeginFrame immediately if it wasn't needed before.
300 (needs_begin_frame
&& !last_set_needs_begin_frame_
) ||
301 // Only stop requesting BeginFrames after a deadline.
302 (!needs_begin_frame
&& last_set_needs_begin_frame_
&& at_end_of_deadline
);
304 if (should_call_set_needs_begin_frame
) {
305 if (settings_
.begin_frame_scheduling_enabled
) {
306 client_
->SetNeedsBeginFrame(needs_begin_frame
);
308 synthetic_begin_frame_source_
->SetNeedsBeginFrame(
309 needs_begin_frame
, &begin_retro_frame_args_
);
311 last_set_needs_begin_frame_
= needs_begin_frame
;
314 PostBeginRetroFrameIfNeeded();
317 // When we aren't throttling frame production, we initiate a BeginFrame
318 // as soon as one is needed.
319 void Scheduler::SetupNextBeginFrameWhenVSyncThrottlingDisabled(
320 bool needs_begin_frame
) {
321 last_set_needs_begin_frame_
= needs_begin_frame
;
323 if (!needs_begin_frame
|| begin_unthrottled_frame_posted_
)
326 if (state_machine_
.begin_impl_frame_state() !=
327 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
&&
328 state_machine_
.begin_impl_frame_state() !=
329 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE
) {
333 begin_unthrottled_frame_posted_
= true;
334 task_runner_
->PostTask(FROM_HERE
, begin_unthrottled_frame_closure_
);
337 // BeginUnthrottledFrame is used when we aren't throttling frame production.
338 // This will usually be because VSync is disabled.
339 void Scheduler::BeginUnthrottledFrame() {
340 DCHECK(!settings_
.throttle_frame_production
);
341 DCHECK(begin_retro_frame_args_
.empty());
343 base::TimeTicks now
= gfx::FrameTime::Now();
344 base::TimeTicks deadline
= now
+ vsync_interval_
;
346 BeginFrameArgs begin_frame_args
=
347 BeginFrameArgs::Create(now
, deadline
, vsync_interval_
);
348 BeginImplFrame(begin_frame_args
);
350 begin_unthrottled_frame_posted_
= false;
353 // We may need to poll when we can't rely on BeginFrame to advance certain
354 // state or to avoid deadlock.
355 void Scheduler::SetupPollingMechanisms(bool needs_begin_frame
) {
356 bool needs_advance_commit_state_timer
= false;
357 // Setup PollForAnticipatedDrawTriggers if we need to monitor state but
358 // aren't expecting any more BeginFrames. This should only be needed by
359 // the synchronous compositor when BeginFrameNeeded is false.
360 if (state_machine_
.ShouldPollForAnticipatedDrawTriggers()) {
361 DCHECK(!state_machine_
.SupportsProactiveBeginFrame());
362 DCHECK(!needs_begin_frame
);
363 if (poll_for_draw_triggers_task_
.IsCancelled()) {
364 poll_for_draw_triggers_task_
.Reset(poll_for_draw_triggers_closure_
);
365 base::TimeDelta delay
= begin_impl_frame_args_
.IsValid()
366 ? begin_impl_frame_args_
.interval
367 : BeginFrameArgs::DefaultInterval();
368 task_runner_
->PostDelayedTask(
369 FROM_HERE
, poll_for_draw_triggers_task_
.callback(), delay
);
372 poll_for_draw_triggers_task_
.Cancel();
374 // At this point we'd prefer to advance through the commit flow by
375 // drawing a frame, however it's possible that the frame rate controller
376 // will not give us a BeginFrame until the commit completes. See
377 // crbug.com/317430 for an example of a swap ack being held on commit. Thus
378 // we set a repeating timer to poll on ProcessScheduledActions until we
379 // successfully reach BeginFrame. Synchronous compositor does not use
380 // frame rate controller or have the circular wait in the bug.
381 if (IsBeginMainFrameSentOrStarted() &&
382 !settings_
.using_synchronous_renderer_compositor
) {
383 needs_advance_commit_state_timer
= true;
387 if (needs_advance_commit_state_timer
) {
388 if (advance_commit_state_task_
.IsCancelled() &&
389 begin_impl_frame_args_
.IsValid()) {
390 // Since we'd rather get a BeginImplFrame by the normal mechanism, we
391 // set the interval to twice the interval from the previous frame.
392 advance_commit_state_task_
.Reset(advance_commit_state_closure_
);
393 task_runner_
->PostDelayedTask(FROM_HERE
,
394 advance_commit_state_task_
.callback(),
395 begin_impl_frame_args_
.interval
* 2);
398 advance_commit_state_task_
.Cancel();
402 // BeginFrame is the mechanism that tells us that now is a good time to start
403 // making a frame. Usually this means that user input for the frame is complete.
404 // If the scheduler is busy, we queue the BeginFrame to be handled later as
405 // a BeginRetroFrame.
406 void Scheduler::BeginFrame(const BeginFrameArgs
& args
) {
407 TRACE_EVENT1("cc", "Scheduler::BeginFrame", "args", args
.AsValue());
408 DCHECK(settings_
.throttle_frame_production
);
410 BeginFrameArgs
adjusted_args(args
);
411 adjusted_args
.deadline
-= EstimatedParentDrawTime();
413 bool should_defer_begin_frame
;
414 if (settings_
.using_synchronous_renderer_compositor
) {
415 should_defer_begin_frame
= false;
417 should_defer_begin_frame
=
418 !begin_retro_frame_args_
.empty() || begin_retro_frame_posted_
||
419 !last_set_needs_begin_frame_
||
420 (state_machine_
.begin_impl_frame_state() !=
421 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
);
424 if (should_defer_begin_frame
) {
425 begin_retro_frame_args_
.push_back(adjusted_args
);
426 TRACE_EVENT_INSTANT0(
427 "cc", "Scheduler::BeginFrame deferred", TRACE_EVENT_SCOPE_THREAD
);
431 BeginImplFrame(adjusted_args
);
434 // BeginRetroFrame is called for BeginFrames that we've deferred because
435 // the scheduler was in the middle of processing a previous BeginFrame.
436 void Scheduler::BeginRetroFrame() {
437 TRACE_EVENT0("cc", "Scheduler::BeginRetroFrame");
438 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
439 DCHECK(begin_retro_frame_posted_
);
440 begin_retro_frame_posted_
= false;
442 // If there aren't any retroactive BeginFrames, then we've lost the
443 // OutputSurface and should abort.
444 if (begin_retro_frame_args_
.empty())
447 // Discard expired BeginRetroFrames
448 // Today, we should always end up with at most one un-expired BeginRetroFrame
449 // because deadlines will not be greater than the next frame time. We don't
450 // DCHECK though because some systems don't always have monotonic timestamps.
451 // TODO(brianderson): In the future, long deadlines could result in us not
452 // draining the queue if we don't catch up. If we consistently can't catch
453 // up, our fallback should be to lower our frame rate.
454 base::TimeTicks now
= gfx::FrameTime::Now();
455 base::TimeDelta draw_duration_estimate
= client_
->DrawDurationEstimate();
456 while (!begin_retro_frame_args_
.empty() &&
457 now
> AdjustedBeginImplFrameDeadline(begin_retro_frame_args_
.front(),
458 draw_duration_estimate
)) {
460 "Scheduler::BeginRetroFrame discarding",
462 begin_retro_frame_args_
.front().frame_time
);
463 begin_retro_frame_args_
.pop_front();
466 if (begin_retro_frame_args_
.empty()) {
467 DCHECK(settings_
.throttle_frame_production
);
468 TRACE_EVENT_INSTANT0("cc",
469 "Scheduler::BeginRetroFrames all expired",
470 TRACE_EVENT_SCOPE_THREAD
);
472 BeginImplFrame(begin_retro_frame_args_
.front());
473 begin_retro_frame_args_
.pop_front();
477 // There could be a race between the posted BeginRetroFrame and a new
478 // BeginFrame arriving via the normal mechanism. Scheduler::BeginFrame
479 // will check if there is a pending BeginRetroFrame to ensure we handle
480 // BeginFrames in FIFO order.
481 void Scheduler::PostBeginRetroFrameIfNeeded() {
482 if (!last_set_needs_begin_frame_
)
485 if (begin_retro_frame_args_
.empty() || begin_retro_frame_posted_
)
488 // begin_retro_frame_args_ should always be empty for the
489 // synchronous compositor.
490 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
492 if (state_machine_
.begin_impl_frame_state() !=
493 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
)
496 begin_retro_frame_posted_
= true;
497 task_runner_
->PostTask(FROM_HERE
, begin_retro_frame_closure_
);
500 // BeginImplFrame starts a compositor frame that will wait up until a deadline
501 // for a BeginMainFrame+activation to complete before it times out and draws
502 // any asynchronous animation and scroll/pinch updates.
503 void Scheduler::BeginImplFrame(const BeginFrameArgs
& args
) {
504 TRACE_EVENT1("cc", "Scheduler::BeginImplFrame", "args", args
.AsValue());
505 DCHECK_EQ(state_machine_
.begin_impl_frame_state(),
506 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
);
507 DCHECK(state_machine_
.HasInitializedOutputSurface());
509 advance_commit_state_task_
.Cancel();
511 base::TimeDelta draw_duration_estimate
= client_
->DrawDurationEstimate();
512 begin_impl_frame_args_
= args
;
513 begin_impl_frame_args_
.deadline
-= draw_duration_estimate
;
515 if (!state_machine_
.impl_latency_takes_priority() &&
516 state_machine_
.MainThreadIsInHighLatencyMode() &&
517 CanCommitAndActivateBeforeDeadline()) {
518 state_machine_
.SetSkipNextBeginMainFrameToReduceLatency();
521 client_
->WillBeginImplFrame(begin_impl_frame_args_
);
522 state_machine_
.OnBeginImplFrame(begin_impl_frame_args_
);
523 devtools_instrumentation::DidBeginFrame(layer_tree_host_id_
);
525 ProcessScheduledActions();
527 state_machine_
.OnBeginImplFrameDeadlinePending();
528 ScheduleBeginImplFrameDeadline(
529 AdjustedBeginImplFrameDeadline(args
, draw_duration_estimate
));
532 base::TimeTicks
Scheduler::AdjustedBeginImplFrameDeadline(
533 const BeginFrameArgs
& args
,
534 base::TimeDelta draw_duration_estimate
) const {
535 if (settings_
.using_synchronous_renderer_compositor
) {
536 // The synchronous compositor needs to draw right away.
537 return base::TimeTicks();
538 } else if (state_machine_
.ShouldTriggerBeginImplFrameDeadlineEarly()) {
539 // We are ready to draw a new active tree immediately.
540 return base::TimeTicks();
541 } else if (state_machine_
.needs_redraw()) {
542 // We have an animation or fast input path on the impl thread that wants
543 // to draw, so don't wait too long for a new active tree.
544 return args
.deadline
- draw_duration_estimate
;
546 // The impl thread doesn't have anything it wants to draw and we are just
547 // waiting for a new active tree, so post the deadline for the next
548 // expected BeginImplFrame start. This allows us to draw immediately when
549 // there is a new active tree, instead of waiting for the next
551 // TODO(brianderson): Handle long deadlines (that are past the next frame's
552 // frame time) properly instead of using this hack.
553 return args
.frame_time
+ args
.interval
;
557 void Scheduler::ScheduleBeginImplFrameDeadline(base::TimeTicks deadline
) {
558 if (settings_
.using_synchronous_renderer_compositor
) {
559 // The synchronous renderer compositor has to make its GL calls
561 // TODO(brianderson): Have the OutputSurface initiate the deadline tasks
562 // so the sychronous renderer compositor can take advantage of splitting
563 // up the BeginImplFrame and deadline as well.
564 OnBeginImplFrameDeadline();
567 begin_impl_frame_deadline_task_
.Cancel();
568 begin_impl_frame_deadline_task_
.Reset(begin_impl_frame_deadline_closure_
);
570 base::TimeDelta delta
= deadline
- gfx::FrameTime::Now();
571 if (delta
<= base::TimeDelta())
572 delta
= base::TimeDelta();
573 task_runner_
->PostDelayedTask(
574 FROM_HERE
, begin_impl_frame_deadline_task_
.callback(), delta
);
577 void Scheduler::OnBeginImplFrameDeadline() {
578 TRACE_EVENT0("cc", "Scheduler::OnBeginImplFrameDeadline");
579 begin_impl_frame_deadline_task_
.Cancel();
581 // We split the deadline actions up into two phases so the state machine
582 // has a chance to trigger actions that should occur durring and after
583 // the deadline separately. For example:
584 // * Sending the BeginMainFrame will not occur after the deadline in
585 // order to wait for more user-input before starting the next commit.
586 // * Creating a new OuputSurface will not occur during the deadline in
587 // order to allow the state machine to "settle" first.
588 state_machine_
.OnBeginImplFrameDeadline();
589 ProcessScheduledActions();
590 state_machine_
.OnBeginImplFrameIdle();
591 ProcessScheduledActions();
593 client_
->DidBeginImplFrameDeadline();
596 void Scheduler::PollForAnticipatedDrawTriggers() {
597 TRACE_EVENT0("cc", "Scheduler::PollForAnticipatedDrawTriggers");
598 poll_for_draw_triggers_task_
.Cancel();
599 state_machine_
.DidEnterPollForAnticipatedDrawTriggers();
600 ProcessScheduledActions();
601 state_machine_
.DidLeavePollForAnticipatedDrawTriggers();
604 void Scheduler::PollToAdvanceCommitState() {
605 TRACE_EVENT0("cc", "Scheduler::PollToAdvanceCommitState");
606 advance_commit_state_task_
.Cancel();
607 ProcessScheduledActions();
610 void Scheduler::DrawAndSwapIfPossible() {
611 DrawResult result
= client_
->ScheduledActionDrawAndSwapIfPossible();
612 state_machine_
.DidDrawIfPossibleCompleted(result
);
615 void Scheduler::ProcessScheduledActions() {
616 // We do not allow ProcessScheduledActions to be recursive.
617 // The top-level call will iteratively execute the next action for us anyway.
618 if (inside_process_scheduled_actions_
)
621 base::AutoReset
<bool> mark_inside(&inside_process_scheduled_actions_
, true);
623 SchedulerStateMachine::Action action
;
625 action
= state_machine_
.NextAction();
626 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
627 "SchedulerStateMachine",
630 VLOG(2) << "Scheduler::ProcessScheduledActions: "
631 << SchedulerStateMachine::ActionToString(action
) << " "
632 << state_machine_
.GetStatesForDebugging();
633 state_machine_
.UpdateState(action
);
634 base::AutoReset
<SchedulerStateMachine::Action
>
635 mark_inside_action(&inside_action_
, action
);
637 case SchedulerStateMachine::ACTION_NONE
:
639 case SchedulerStateMachine::ACTION_ANIMATE
:
640 client_
->ScheduledActionAnimate();
642 case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME
:
643 client_
->ScheduledActionSendBeginMainFrame();
645 case SchedulerStateMachine::ACTION_COMMIT
:
646 client_
->ScheduledActionCommit();
648 case SchedulerStateMachine::ACTION_UPDATE_VISIBLE_TILES
:
649 client_
->ScheduledActionUpdateVisibleTiles();
651 case SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE
:
652 client_
->ScheduledActionActivateSyncTree();
654 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE
:
655 DrawAndSwapIfPossible();
657 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED
:
658 client_
->ScheduledActionDrawAndSwapForced();
660 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT
:
661 // No action is actually performed, but this allows the state machine to
662 // advance out of its waiting to draw state without actually drawing.
664 case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION
:
665 client_
->ScheduledActionBeginOutputSurfaceCreation();
667 case SchedulerStateMachine::ACTION_MANAGE_TILES
:
668 client_
->ScheduledActionManageTiles();
671 } while (action
!= SchedulerStateMachine::ACTION_NONE
);
673 SetupNextBeginFrameIfNeeded();
674 client_
->DidAnticipatedDrawTimeChange(AnticipatedDrawTime());
676 if (state_machine_
.ShouldTriggerBeginImplFrameDeadlineEarly()) {
677 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
678 ScheduleBeginImplFrameDeadline(base::TimeTicks());
682 bool Scheduler::WillDrawIfNeeded() const {
683 return !state_machine_
.PendingDrawsShouldBeAborted();
686 scoped_refptr
<base::debug::ConvertableToTraceFormat
> Scheduler::AsValue()
688 scoped_refptr
<base::debug::TracedValue
> state
=
689 new base::debug::TracedValue();
690 state
->BeginDictionary("state_machine");
691 state_machine_
.AsValueInto(state
.get());
692 state
->EndDictionary();
693 if (synthetic_begin_frame_source_
) {
694 state
->BeginDictionary("synthetic_begin_frame_source_");
695 synthetic_begin_frame_source_
->AsValueInto(state
.get());
696 state
->EndDictionary();
699 state
->BeginDictionary("scheduler_state");
701 "time_until_anticipated_draw_time_ms",
702 (AnticipatedDrawTime() - base::TimeTicks::Now()).InMillisecondsF());
703 state
->SetDouble("vsync_interval_ms", vsync_interval_
.InMillisecondsF());
704 state
->SetDouble("estimated_parent_draw_time_ms",
705 estimated_parent_draw_time_
.InMillisecondsF());
706 state
->SetBoolean("last_set_needs_begin_frame_", last_set_needs_begin_frame_
);
707 state
->SetBoolean("begin_unthrottled_frame_posted_",
708 begin_unthrottled_frame_posted_
);
709 state
->SetBoolean("begin_retro_frame_posted_", begin_retro_frame_posted_
);
710 state
->SetInteger("begin_retro_frame_args_", begin_retro_frame_args_
.size());
711 state
->SetBoolean("begin_impl_frame_deadline_task_",
712 !begin_impl_frame_deadline_task_
.IsCancelled());
713 state
->SetBoolean("poll_for_draw_triggers_task_",
714 !poll_for_draw_triggers_task_
.IsCancelled());
715 state
->SetBoolean("advance_commit_state_task_",
716 !advance_commit_state_task_
.IsCancelled());
717 state
->BeginDictionary("begin_impl_frame_args");
718 begin_impl_frame_args_
.AsValueInto(state
.get());
719 state
->EndDictionary();
721 state
->EndDictionary();
723 state
->BeginDictionary("client_state");
724 state
->SetDouble("draw_duration_estimate_ms",
725 client_
->DrawDurationEstimate().InMillisecondsF());
727 "begin_main_frame_to_commit_duration_estimate_ms",
728 client_
->BeginMainFrameToCommitDurationEstimate().InMillisecondsF());
730 "commit_to_activate_duration_estimate_ms",
731 client_
->CommitToActivateDurationEstimate().InMillisecondsF());
732 state
->EndDictionary();
736 bool Scheduler::CanCommitAndActivateBeforeDeadline() const {
737 // Check if the main thread computation and commit can be finished before the
738 // impl thread's deadline.
739 base::TimeTicks estimated_draw_time
=
740 begin_impl_frame_args_
.frame_time
+
741 client_
->BeginMainFrameToCommitDurationEstimate() +
742 client_
->CommitToActivateDurationEstimate();
745 TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
746 "CanCommitAndActivateBeforeDeadline",
747 "time_left_after_drawing_ms",
748 (begin_impl_frame_args_
.deadline
- estimated_draw_time
).InMillisecondsF(),
752 return estimated_draw_time
< begin_impl_frame_args_
.deadline
;
755 bool Scheduler::IsBeginMainFrameSentOrStarted() const {
756 return (state_machine_
.commit_state() ==
757 SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT
||
758 state_machine_
.commit_state() ==
759 SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED
);