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"
9 #include "base/auto_reset.h"
10 #include "base/logging.h"
11 #include "base/profiler/scoped_tracker.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/trace_event/trace_event.h"
14 #include "base/trace_event/trace_event_argument.h"
15 #include "cc/debug/devtools_instrumentation.h"
16 #include "cc/debug/traced_value.h"
17 #include "cc/scheduler/compositor_timing_history.h"
18 #include "cc/scheduler/delay_based_time_source.h"
22 scoped_ptr
<Scheduler
> Scheduler::Create(
23 SchedulerClient
* client
,
24 const SchedulerSettings
& settings
,
25 int layer_tree_host_id
,
26 base::SingleThreadTaskRunner
* task_runner
,
27 BeginFrameSource
* external_frame_source
,
28 scoped_ptr
<CompositorTimingHistory
> compositor_timing_history
) {
29 scoped_ptr
<SyntheticBeginFrameSource
> synthetic_frame_source
;
30 if (!settings
.use_external_begin_frame_source
) {
31 synthetic_frame_source
= SyntheticBeginFrameSource::Create(
32 task_runner
, BeginFrameArgs::DefaultInterval());
34 scoped_ptr
<BackToBackBeginFrameSource
> unthrottled_frame_source
=
35 BackToBackBeginFrameSource::Create(task_runner
);
36 return make_scoped_ptr(new Scheduler(
37 client
, settings
, layer_tree_host_id
, task_runner
, external_frame_source
,
38 synthetic_frame_source
.Pass(), unthrottled_frame_source
.Pass(),
39 compositor_timing_history
.Pass()));
43 SchedulerClient
* client
,
44 const SchedulerSettings
& settings
,
45 int layer_tree_host_id
,
46 base::SingleThreadTaskRunner
* task_runner
,
47 BeginFrameSource
* external_frame_source
,
48 scoped_ptr
<SyntheticBeginFrameSource
> synthetic_frame_source
,
49 scoped_ptr
<BackToBackBeginFrameSource
> unthrottled_frame_source
,
50 scoped_ptr
<CompositorTimingHistory
> compositor_timing_history
)
51 : settings_(settings
),
53 layer_tree_host_id_(layer_tree_host_id
),
54 task_runner_(task_runner
),
55 external_frame_source_(external_frame_source
),
56 synthetic_frame_source_(synthetic_frame_source
.Pass()),
57 unthrottled_frame_source_(unthrottled_frame_source
.Pass()),
58 frame_source_(BeginFrameSourceMultiplexer::Create()),
59 throttle_frame_production_(false),
60 compositor_timing_history_(compositor_timing_history
.Pass()),
61 begin_impl_frame_deadline_mode_(
62 SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE
),
63 begin_impl_frame_tracker_(BEGINFRAMETRACKER_FROM_HERE
),
64 state_machine_(settings
),
65 inside_process_scheduled_actions_(false),
66 inside_action_(SchedulerStateMachine::ACTION_NONE
),
68 TRACE_EVENT1("cc", "Scheduler::Scheduler", "settings", settings_
.AsValue());
70 DCHECK(!state_machine_
.BeginFrameNeeded());
71 DCHECK_IMPLIES(settings_
.use_external_begin_frame_source
,
72 external_frame_source_
);
73 DCHECK_IMPLIES(!settings_
.use_external_begin_frame_source
,
74 synthetic_frame_source_
);
75 DCHECK(unthrottled_frame_source_
);
77 begin_retro_frame_closure_
=
78 base::Bind(&Scheduler::BeginRetroFrame
, weak_factory_
.GetWeakPtr());
79 begin_impl_frame_deadline_closure_
= base::Bind(
80 &Scheduler::OnBeginImplFrameDeadline
, weak_factory_
.GetWeakPtr());
82 frame_source_
->AddObserver(this);
83 frame_source_
->AddSource(primary_frame_source());
84 primary_frame_source()->SetClientReady();
86 frame_source_
->AddSource(unthrottled_frame_source_
.get());
87 unthrottled_frame_source_
->SetClientReady();
89 SetThrottleFrameProduction(settings_
.throttle_frame_production
);
92 Scheduler::~Scheduler() {
93 if (frame_source_
->NeedsBeginFrames())
94 frame_source_
->SetNeedsBeginFrames(false);
95 frame_source_
->SetActiveSource(nullptr);
98 base::TimeTicks
Scheduler::Now() const {
99 base::TimeTicks now
= base::TimeTicks::Now();
100 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.now"),
107 void Scheduler::CommitVSyncParameters(base::TimeTicks timebase
,
108 base::TimeDelta interval
) {
109 if (authoritative_vsync_interval_
!= base::TimeDelta()) {
110 interval
= authoritative_vsync_interval_
;
111 } else if (interval
== base::TimeDelta()) {
112 // TODO(brianderson): We should not be receiving 0 intervals.
113 interval
= BeginFrameArgs::DefaultInterval();
116 last_vsync_timebase_
= timebase
;
118 if (synthetic_frame_source_
)
119 synthetic_frame_source_
->OnUpdateVSyncParameters(timebase
, interval
);
122 void Scheduler::SetEstimatedParentDrawTime(base::TimeDelta draw_time
) {
123 DCHECK_GE(draw_time
.ToInternalValue(), 0);
124 estimated_parent_draw_time_
= draw_time
;
127 void Scheduler::SetCanStart() {
128 state_machine_
.SetCanStart();
129 ProcessScheduledActions();
132 void Scheduler::SetVisible(bool visible
) {
133 state_machine_
.SetVisible(visible
);
134 UpdateCompositorTimingHistoryRecordingEnabled();
135 ProcessScheduledActions();
138 void Scheduler::SetCanDraw(bool can_draw
) {
139 state_machine_
.SetCanDraw(can_draw
);
140 ProcessScheduledActions();
143 void Scheduler::NotifyReadyToActivate() {
144 compositor_timing_history_
->ReadyToActivate();
145 state_machine_
.NotifyReadyToActivate();
146 ProcessScheduledActions();
149 void Scheduler::NotifyReadyToDraw() {
150 // Future work might still needed for crbug.com/352894.
151 state_machine_
.NotifyReadyToDraw();
152 ProcessScheduledActions();
155 void Scheduler::SetThrottleFrameProduction(bool throttle
) {
156 throttle_frame_production_
= throttle
;
158 frame_source_
->SetActiveSource(primary_frame_source());
160 frame_source_
->SetActiveSource(unthrottled_frame_source_
.get());
162 ProcessScheduledActions();
165 void Scheduler::SetNeedsBeginMainFrame() {
166 state_machine_
.SetNeedsBeginMainFrame();
167 ProcessScheduledActions();
170 void Scheduler::SetNeedsRedraw() {
171 state_machine_
.SetNeedsRedraw();
172 ProcessScheduledActions();
175 void Scheduler::SetNeedsAnimate() {
176 state_machine_
.SetNeedsAnimate();
177 ProcessScheduledActions();
180 void Scheduler::SetNeedsPrepareTiles() {
181 DCHECK(!IsInsideAction(SchedulerStateMachine::ACTION_PREPARE_TILES
));
182 state_machine_
.SetNeedsPrepareTiles();
183 ProcessScheduledActions();
186 void Scheduler::SetMaxSwapsPending(int max
) {
187 state_machine_
.SetMaxSwapsPending(max
);
190 void Scheduler::DidSwapBuffers() {
191 state_machine_
.DidSwapBuffers();
193 // There is no need to call ProcessScheduledActions here because
194 // swapping should not trigger any new actions.
195 if (!inside_process_scheduled_actions_
) {
196 DCHECK_EQ(state_machine_
.NextAction(), SchedulerStateMachine::ACTION_NONE
);
200 void Scheduler::DidSwapBuffersComplete() {
201 DCHECK_GT(state_machine_
.pending_swaps(), 0) << AsValue()->ToString();
202 state_machine_
.DidSwapBuffersComplete();
203 ProcessScheduledActions();
206 void Scheduler::SetImplLatencyTakesPriority(bool impl_latency_takes_priority
) {
207 state_machine_
.SetImplLatencyTakesPriority(impl_latency_takes_priority
);
208 ProcessScheduledActions();
211 void Scheduler::NotifyReadyToCommit() {
212 TRACE_EVENT0("cc", "Scheduler::NotifyReadyToCommit");
213 state_machine_
.NotifyReadyToCommit();
214 ProcessScheduledActions();
217 void Scheduler::DidCommit() {
218 compositor_timing_history_
->DidCommit();
221 void Scheduler::BeginMainFrameAborted(CommitEarlyOutReason reason
) {
222 TRACE_EVENT1("cc", "Scheduler::BeginMainFrameAborted", "reason",
223 CommitEarlyOutReasonToString(reason
));
224 compositor_timing_history_
->BeginMainFrameAborted();
225 state_machine_
.BeginMainFrameAborted(reason
);
226 ProcessScheduledActions();
229 void Scheduler::WillPrepareTiles() {
230 compositor_timing_history_
->WillPrepareTiles();
233 void Scheduler::DidPrepareTiles() {
234 compositor_timing_history_
->DidPrepareTiles();
235 state_machine_
.DidPrepareTiles();
238 void Scheduler::DidLoseOutputSurface() {
239 TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface");
240 begin_retro_frame_args_
.clear();
241 begin_retro_frame_task_
.Cancel();
242 state_machine_
.DidLoseOutputSurface();
243 UpdateCompositorTimingHistoryRecordingEnabled();
244 ProcessScheduledActions();
247 void Scheduler::DidCreateAndInitializeOutputSurface() {
248 TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface");
249 DCHECK(!frame_source_
->NeedsBeginFrames());
250 DCHECK(begin_impl_frame_deadline_task_
.IsCancelled());
251 state_machine_
.DidCreateAndInitializeOutputSurface();
252 UpdateCompositorTimingHistoryRecordingEnabled();
253 ProcessScheduledActions();
256 void Scheduler::NotifyBeginMainFrameStarted() {
257 TRACE_EVENT0("cc", "Scheduler::NotifyBeginMainFrameStarted");
258 state_machine_
.NotifyBeginMainFrameStarted();
261 base::TimeTicks
Scheduler::LastBeginImplFrameTime() {
262 return begin_impl_frame_tracker_
.Current().frame_time
;
265 void Scheduler::SetupNextBeginFrameIfNeeded() {
266 // Never call SetNeedsBeginFrames if the frame source already has the right
268 if (frame_source_
->NeedsBeginFrames() != state_machine_
.BeginFrameNeeded()) {
269 if (state_machine_
.BeginFrameNeeded()) {
270 // Call SetNeedsBeginFrames(true) as soon as possible.
271 frame_source_
->SetNeedsBeginFrames(true);
272 devtools_instrumentation::NeedsBeginFrameChanged(layer_tree_host_id_
,
274 UpdateCompositorTimingHistoryRecordingEnabled();
275 } else if (state_machine_
.begin_impl_frame_state() ==
276 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
) {
277 // Call SetNeedsBeginFrames(false) in between frames only.
278 frame_source_
->SetNeedsBeginFrames(false);
279 client_
->SendBeginMainFrameNotExpectedSoon();
280 devtools_instrumentation::NeedsBeginFrameChanged(layer_tree_host_id_
,
282 UpdateCompositorTimingHistoryRecordingEnabled();
286 PostBeginRetroFrameIfNeeded();
289 // BeginFrame is the mechanism that tells us that now is a good time to start
290 // making a frame. Usually this means that user input for the frame is complete.
291 // If the scheduler is busy, we queue the BeginFrame to be handled later as
292 // a BeginRetroFrame.
293 bool Scheduler::OnBeginFrameDerivedImpl(const BeginFrameArgs
& args
) {
294 TRACE_EVENT1("cc,benchmark", "Scheduler::BeginFrame", "args", args
.AsValue());
296 // Trace this begin frame time through the Chrome stack
297 TRACE_EVENT_FLOW_BEGIN0(
298 TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), "BeginFrameArgs",
299 args
.frame_time
.ToInternalValue());
301 // TODO(brianderson): Adjust deadline in the DisplayScheduler.
302 BeginFrameArgs
adjusted_args(args
);
303 adjusted_args
.deadline
-= EstimatedParentDrawTime();
304 adjusted_args
.on_critical_path
= !ImplLatencyTakesPriority();
306 // Deliver BeginFrames to children.
307 // TODO(brianderson): Move this responsibility to the DisplayScheduler.
308 if (state_machine_
.children_need_begin_frames())
309 client_
->SendBeginFramesToChildren(adjusted_args
);
311 if (settings_
.using_synchronous_renderer_compositor
) {
312 BeginImplFrameSynchronous(adjusted_args
);
316 // We have just called SetNeedsBeginFrame(true) and the BeginFrameSource has
317 // sent us the last BeginFrame we have missed. As we might not be able to
318 // actually make rendering for this call, handle it like a "retro frame".
319 // TODO(brainderson): Add a test for this functionality ASAP!
320 if (adjusted_args
.type
== BeginFrameArgs::MISSED
) {
321 begin_retro_frame_args_
.push_back(adjusted_args
);
322 PostBeginRetroFrameIfNeeded();
326 bool should_defer_begin_frame
=
327 !begin_retro_frame_args_
.empty() ||
328 !begin_retro_frame_task_
.IsCancelled() ||
329 !frame_source_
->NeedsBeginFrames() ||
330 (state_machine_
.begin_impl_frame_state() !=
331 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
);
333 if (should_defer_begin_frame
) {
334 begin_retro_frame_args_
.push_back(adjusted_args
);
335 TRACE_EVENT_INSTANT0(
336 "cc", "Scheduler::BeginFrame deferred", TRACE_EVENT_SCOPE_THREAD
);
337 // Queuing the frame counts as "using it", so we need to return true.
339 BeginImplFrameWithDeadline(adjusted_args
);
344 void Scheduler::SetChildrenNeedBeginFrames(bool children_need_begin_frames
) {
345 state_machine_
.SetChildrenNeedBeginFrames(children_need_begin_frames
);
346 ProcessScheduledActions();
349 void Scheduler::SetAuthoritativeVSyncInterval(const base::TimeDelta
& interval
) {
350 authoritative_vsync_interval_
= interval
;
351 if (synthetic_frame_source_
) {
352 synthetic_frame_source_
->OnUpdateVSyncParameters(last_vsync_timebase_
,
357 void Scheduler::SetVideoNeedsBeginFrames(bool video_needs_begin_frames
) {
358 state_machine_
.SetVideoNeedsBeginFrames(video_needs_begin_frames
);
359 ProcessScheduledActions();
362 void Scheduler::OnDrawForOutputSurface() {
363 DCHECK(settings_
.using_synchronous_renderer_compositor
);
364 DCHECK_EQ(state_machine_
.begin_impl_frame_state(),
365 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
);
366 DCHECK(!BeginImplFrameDeadlinePending());
368 state_machine_
.OnBeginImplFrameDeadline();
369 ProcessScheduledActions();
371 state_machine_
.OnBeginImplFrameIdle();
372 ProcessScheduledActions();
375 // BeginRetroFrame is called for BeginFrames that we've deferred because
376 // the scheduler was in the middle of processing a previous BeginFrame.
377 void Scheduler::BeginRetroFrame() {
378 TRACE_EVENT0("cc,benchmark", "Scheduler::BeginRetroFrame");
379 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
380 DCHECK(!begin_retro_frame_args_
.empty());
381 DCHECK(!begin_retro_frame_task_
.IsCancelled());
382 DCHECK_EQ(state_machine_
.begin_impl_frame_state(),
383 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
);
385 begin_retro_frame_task_
.Cancel();
387 // Discard expired BeginRetroFrames
388 // Today, we should always end up with at most one un-expired BeginRetroFrame
389 // because deadlines will not be greater than the next frame time. We don't
390 // DCHECK though because some systems don't always have monotonic timestamps.
391 // TODO(brianderson): In the future, long deadlines could result in us not
392 // draining the queue if we don't catch up. If we consistently can't catch
393 // up, our fallback should be to lower our frame rate.
394 base::TimeTicks now
= Now();
396 while (!begin_retro_frame_args_
.empty()) {
397 const BeginFrameArgs
& args
= begin_retro_frame_args_
.front();
398 base::TimeTicks expiration_time
= args
.deadline
;
399 if (now
<= expiration_time
)
401 TRACE_EVENT_INSTANT2(
402 "cc", "Scheduler::BeginRetroFrame discarding", TRACE_EVENT_SCOPE_THREAD
,
403 "expiration_time - now", (expiration_time
- now
).InMillisecondsF(),
404 "BeginFrameArgs", begin_retro_frame_args_
.front().AsValue());
405 begin_retro_frame_args_
.pop_front();
406 frame_source_
->DidFinishFrame(begin_retro_frame_args_
.size());
409 if (begin_retro_frame_args_
.empty()) {
410 TRACE_EVENT_INSTANT0("cc",
411 "Scheduler::BeginRetroFrames all expired",
412 TRACE_EVENT_SCOPE_THREAD
);
414 BeginFrameArgs front
= begin_retro_frame_args_
.front();
415 begin_retro_frame_args_
.pop_front();
416 BeginImplFrameWithDeadline(front
);
420 // There could be a race between the posted BeginRetroFrame and a new
421 // BeginFrame arriving via the normal mechanism. Scheduler::BeginFrame
422 // will check if there is a pending BeginRetroFrame to ensure we handle
423 // BeginFrames in FIFO order.
424 void Scheduler::PostBeginRetroFrameIfNeeded() {
425 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
426 "Scheduler::PostBeginRetroFrameIfNeeded",
429 if (!frame_source_
->NeedsBeginFrames())
432 if (begin_retro_frame_args_
.empty() || !begin_retro_frame_task_
.IsCancelled())
435 // begin_retro_frame_args_ should always be empty for the
436 // synchronous compositor.
437 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
439 if (state_machine_
.begin_impl_frame_state() !=
440 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
)
443 begin_retro_frame_task_
.Reset(begin_retro_frame_closure_
);
445 task_runner_
->PostTask(FROM_HERE
, begin_retro_frame_task_
.callback());
448 void Scheduler::BeginImplFrameWithDeadline(const BeginFrameArgs
& args
) {
449 bool main_thread_is_in_high_latency_mode
=
450 state_machine_
.main_thread_missed_last_deadline();
451 TRACE_EVENT2("cc,benchmark", "Scheduler::BeginImplFrame", "args",
452 args
.AsValue(), "main_thread_missed_last_deadline",
453 main_thread_is_in_high_latency_mode
);
454 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
455 "MainThreadLatency", main_thread_is_in_high_latency_mode
);
457 BeginFrameArgs adjusted_args
= args
;
458 adjusted_args
.deadline
-= compositor_timing_history_
->DrawDurationEstimate();
460 if (ShouldRecoverMainLatency(adjusted_args
)) {
461 TRACE_EVENT_INSTANT0("cc", "SkipBeginMainFrameToReduceLatency",
462 TRACE_EVENT_SCOPE_THREAD
);
463 state_machine_
.SetSkipNextBeginMainFrameToReduceLatency();
464 } else if (ShouldRecoverImplLatency(adjusted_args
)) {
465 TRACE_EVENT_INSTANT0("cc", "SkipBeginImplFrameToReduceLatency",
466 TRACE_EVENT_SCOPE_THREAD
);
467 frame_source_
->DidFinishFrame(begin_retro_frame_args_
.size());
471 BeginImplFrame(adjusted_args
);
473 // The deadline will be scheduled in ProcessScheduledActions.
474 state_machine_
.OnBeginImplFrameDeadlinePending();
475 ProcessScheduledActions();
478 void Scheduler::BeginImplFrameSynchronous(const BeginFrameArgs
& args
) {
479 TRACE_EVENT1("cc,benchmark", "Scheduler::BeginImplFrame", "args",
481 BeginImplFrame(args
);
485 void Scheduler::FinishImplFrame() {
486 state_machine_
.OnBeginImplFrameIdle();
487 ProcessScheduledActions();
489 client_
->DidFinishImplFrame();
490 frame_source_
->DidFinishFrame(begin_retro_frame_args_
.size());
491 begin_impl_frame_tracker_
.Finish();
494 // BeginImplFrame starts a compositor frame that will wait up until a deadline
495 // for a BeginMainFrame+activation to complete before it times out and draws
496 // any asynchronous animation and scroll/pinch updates.
497 void Scheduler::BeginImplFrame(const BeginFrameArgs
& args
) {
498 DCHECK_EQ(state_machine_
.begin_impl_frame_state(),
499 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
);
500 DCHECK(!BeginImplFrameDeadlinePending());
501 DCHECK(state_machine_
.HasInitializedOutputSurface());
503 begin_impl_frame_tracker_
.Start(args
);
504 state_machine_
.OnBeginImplFrame();
505 devtools_instrumentation::DidBeginFrame(layer_tree_host_id_
);
506 client_
->WillBeginImplFrame(begin_impl_frame_tracker_
.Current());
508 ProcessScheduledActions();
511 void Scheduler::ScheduleBeginImplFrameDeadline() {
512 // The synchronous compositor does not post a deadline task.
513 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
515 begin_impl_frame_deadline_task_
.Cancel();
516 begin_impl_frame_deadline_task_
.Reset(begin_impl_frame_deadline_closure_
);
518 begin_impl_frame_deadline_mode_
=
519 state_machine_
.CurrentBeginImplFrameDeadlineMode();
520 base::TimeTicks deadline
;
521 switch (begin_impl_frame_deadline_mode_
) {
522 case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE
:
525 case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE
:
526 // We are ready to draw a new active tree immediately.
527 // We don't use Now() here because it's somewhat expensive to call.
528 deadline
= base::TimeTicks();
530 case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_REGULAR
:
531 // We are animating on the impl thread but we can wait for some time.
532 deadline
= begin_impl_frame_tracker_
.Current().deadline
;
534 case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_LATE
:
535 // We are blocked for one reason or another and we should wait.
536 // TODO(brianderson): Handle long deadlines (that are past the next
537 // frame's frame time) properly instead of using this hack.
538 deadline
= begin_impl_frame_tracker_
.Current().frame_time
+
539 begin_impl_frame_tracker_
.Current().interval
;
541 case SchedulerStateMachine::
542 BEGIN_IMPL_FRAME_DEADLINE_MODE_BLOCKED_ON_READY_TO_DRAW
:
543 // We are blocked because we are waiting for ReadyToDraw signal. We would
544 // post deadline after we received ReadyToDraw singal.
545 TRACE_EVENT1("cc", "Scheduler::ScheduleBeginImplFrameDeadline",
546 "deadline_mode", "blocked_on_ready_to_draw");
550 TRACE_EVENT2("cc", "Scheduler::ScheduleBeginImplFrameDeadline", "mode",
551 SchedulerStateMachine::BeginImplFrameDeadlineModeToString(
552 begin_impl_frame_deadline_mode_
),
553 "deadline", deadline
);
555 base::TimeDelta delta
= std::max(deadline
- Now(), base::TimeDelta());
556 task_runner_
->PostDelayedTask(
557 FROM_HERE
, begin_impl_frame_deadline_task_
.callback(), delta
);
560 void Scheduler::ScheduleBeginImplFrameDeadlineIfNeeded() {
561 if (settings_
.using_synchronous_renderer_compositor
)
564 if (state_machine_
.begin_impl_frame_state() !=
565 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME
)
568 if (begin_impl_frame_deadline_mode_
==
569 state_machine_
.CurrentBeginImplFrameDeadlineMode() &&
570 BeginImplFrameDeadlinePending())
573 ScheduleBeginImplFrameDeadline();
576 void Scheduler::OnBeginImplFrameDeadline() {
577 TRACE_EVENT0("cc,benchmark", "Scheduler::OnBeginImplFrameDeadline");
578 begin_impl_frame_deadline_task_
.Cancel();
579 // We split the deadline actions up into two phases so the state machine
580 // has a chance to trigger actions that should occur durring and after
581 // the deadline separately. For example:
582 // * Sending the BeginMainFrame will not occur after the deadline in
583 // order to wait for more user-input before starting the next commit.
584 // * Creating a new OuputSurface will not occur during the deadline in
585 // order to allow the state machine to "settle" first.
587 // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is fixed.
588 tracked_objects::ScopedTracker
tracking_profile1(
589 FROM_HERE_WITH_EXPLICIT_FUNCTION(
590 "461509 Scheduler::OnBeginImplFrameDeadline1"));
591 state_machine_
.OnBeginImplFrameDeadline();
592 ProcessScheduledActions();
596 void Scheduler::DrawAndSwapIfPossible() {
597 compositor_timing_history_
->WillDraw();
598 DrawResult result
= client_
->ScheduledActionDrawAndSwapIfPossible();
599 state_machine_
.DidDrawIfPossibleCompleted(result
);
600 compositor_timing_history_
->DidDraw();
603 void Scheduler::DrawAndSwapForced() {
604 compositor_timing_history_
->WillDraw();
605 client_
->ScheduledActionDrawAndSwapForced();
606 compositor_timing_history_
->DidDraw();
609 void Scheduler::SetDeferCommits(bool defer_commits
) {
610 TRACE_EVENT1("cc", "Scheduler::SetDeferCommits",
613 state_machine_
.SetDeferCommits(defer_commits
);
614 ProcessScheduledActions();
617 void Scheduler::ProcessScheduledActions() {
618 // We do not allow ProcessScheduledActions to be recursive.
619 // The top-level call will iteratively execute the next action for us anyway.
620 if (inside_process_scheduled_actions_
)
623 base::AutoReset
<bool> mark_inside(&inside_process_scheduled_actions_
, true);
625 SchedulerStateMachine::Action action
;
627 action
= state_machine_
.NextAction();
628 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
629 "SchedulerStateMachine",
632 base::AutoReset
<SchedulerStateMachine::Action
>
633 mark_inside_action(&inside_action_
, action
);
635 case SchedulerStateMachine::ACTION_NONE
:
637 case SchedulerStateMachine::ACTION_ANIMATE
:
638 state_machine_
.WillAnimate();
639 client_
->ScheduledActionAnimate();
641 case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME
:
642 compositor_timing_history_
->WillBeginMainFrame();
643 state_machine_
.WillSendBeginMainFrame();
644 client_
->ScheduledActionSendBeginMainFrame();
646 case SchedulerStateMachine::ACTION_COMMIT
: {
647 // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is
649 tracked_objects::ScopedTracker
tracking_profile4(
650 FROM_HERE_WITH_EXPLICIT_FUNCTION(
651 "461509 Scheduler::ProcessScheduledActions4"));
652 bool commit_has_no_updates
= false;
653 state_machine_
.WillCommit(commit_has_no_updates
);
654 client_
->ScheduledActionCommit();
657 case SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE
:
658 compositor_timing_history_
->WillActivate();
659 state_machine_
.WillActivate();
660 client_
->ScheduledActionActivateSyncTree();
661 compositor_timing_history_
->DidActivate();
663 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE
: {
664 // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is
666 tracked_objects::ScopedTracker
tracking_profile6(
667 FROM_HERE_WITH_EXPLICIT_FUNCTION(
668 "461509 Scheduler::ProcessScheduledActions6"));
669 bool did_request_swap
= true;
670 state_machine_
.WillDraw(did_request_swap
);
671 DrawAndSwapIfPossible();
674 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED
: {
675 bool did_request_swap
= true;
676 state_machine_
.WillDraw(did_request_swap
);
680 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT
: {
681 // No action is actually performed, but this allows the state machine to
682 // advance out of its waiting to draw state without actually drawing.
683 bool did_request_swap
= false;
684 state_machine_
.WillDraw(did_request_swap
);
687 case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION
:
688 state_machine_
.WillBeginOutputSurfaceCreation();
689 client_
->ScheduledActionBeginOutputSurfaceCreation();
691 case SchedulerStateMachine::ACTION_PREPARE_TILES
:
692 state_machine_
.WillPrepareTiles();
693 client_
->ScheduledActionPrepareTiles();
695 case SchedulerStateMachine::ACTION_INVALIDATE_OUTPUT_SURFACE
: {
696 state_machine_
.WillInvalidateOutputSurface();
697 client_
->ScheduledActionInvalidateOutputSurface();
701 } while (action
!= SchedulerStateMachine::ACTION_NONE
);
703 ScheduleBeginImplFrameDeadlineIfNeeded();
704 SetupNextBeginFrameIfNeeded();
707 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
> Scheduler::AsValue()
709 scoped_refptr
<base::trace_event::TracedValue
> state
=
710 new base::trace_event::TracedValue();
711 AsValueInto(state
.get());
715 void Scheduler::AsValueInto(base::trace_event::TracedValue
* state
) const {
716 base::TimeTicks now
= Now();
718 state
->BeginDictionary("state_machine");
719 state_machine_
.AsValueInto(state
);
720 state
->EndDictionary();
722 // Only trace frame sources when explicitly enabled - http://crbug.com/420607
723 bool frame_tracing_enabled
= false;
724 TRACE_EVENT_CATEGORY_GROUP_ENABLED(
725 TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"),
726 &frame_tracing_enabled
);
727 if (frame_tracing_enabled
) {
728 state
->BeginDictionary("frame_source_");
729 frame_source_
->AsValueInto(state
);
730 state
->EndDictionary();
733 state
->BeginDictionary("scheduler_state");
734 state
->SetDouble("estimated_parent_draw_time_ms",
735 estimated_parent_draw_time_
.InMillisecondsF());
736 state
->SetInteger("begin_retro_frame_args",
737 static_cast<int>(begin_retro_frame_args_
.size()));
738 state
->SetBoolean("begin_retro_frame_task",
739 !begin_retro_frame_task_
.IsCancelled());
740 state
->SetBoolean("begin_impl_frame_deadline_task",
741 !begin_impl_frame_deadline_task_
.IsCancelled());
742 state
->SetString("inside_action",
743 SchedulerStateMachine::ActionToString(inside_action_
));
745 state
->BeginDictionary("begin_impl_frame_args");
746 begin_impl_frame_tracker_
.AsValueInto(now
, state
);
747 state
->EndDictionary();
749 state
->SetString("begin_impl_frame_deadline_mode_",
750 SchedulerStateMachine::BeginImplFrameDeadlineModeToString(
751 begin_impl_frame_deadline_mode_
));
752 state
->EndDictionary();
754 state
->BeginDictionary("compositor_timing_history");
755 compositor_timing_history_
->AsValueInto(state
);
756 state
->EndDictionary();
759 void Scheduler::UpdateCompositorTimingHistoryRecordingEnabled() {
760 compositor_timing_history_
->SetRecordingEnabled(
761 state_machine_
.HasInitializedOutputSurface() &&
762 state_machine_
.visible() && frame_source_
->NeedsBeginFrames());
765 bool Scheduler::ShouldRecoverMainLatency(const BeginFrameArgs
& args
) const {
766 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
768 if (!state_machine_
.main_thread_missed_last_deadline())
771 // When prioritizing impl thread latency, we currently put the
772 // main thread in a high latency mode. Don't try to fight it.
773 if (state_machine_
.impl_latency_takes_priority())
776 return CanCommitAndActivateBeforeDeadline(args
);
779 bool Scheduler::ShouldRecoverImplLatency(const BeginFrameArgs
& args
) const {
780 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
782 // Disable impl thread latency recovery when using the unthrottled
783 // begin frame source since we will always get a BeginFrame before
784 // the swap ack and our heuristics below will not work.
785 if (!throttle_frame_production_
)
788 // If we are swap throttled at the BeginFrame, that means the impl thread is
789 // very likely in a high latency mode.
790 bool impl_thread_is_likely_high_latency
= state_machine_
.SwapThrottled();
791 if (!impl_thread_is_likely_high_latency
)
794 // The deadline may be in the past if our draw time is too long.
795 bool can_draw_before_deadline
= args
.frame_time
< args
.deadline
;
797 // When prioritizing impl thread latency, the deadline doesn't wait
798 // for the main thread.
799 if (state_machine_
.impl_latency_takes_priority())
800 return can_draw_before_deadline
;
802 // If we only have impl-side updates, the deadline doesn't wait for
804 if (state_machine_
.OnlyImplSideUpdatesExpected())
805 return can_draw_before_deadline
;
807 // If we get here, we know the main thread is in a low-latency mode relative
808 // to the impl thread. In this case, only try to also recover impl thread
809 // latency if both the main and impl threads can run serially before the
811 return CanCommitAndActivateBeforeDeadline(args
);
814 bool Scheduler::CanCommitAndActivateBeforeDeadline(
815 const BeginFrameArgs
& args
) const {
816 // Check if the main thread computation and commit can be finished before the
817 // impl thread's deadline.
818 base::TimeTicks estimated_draw_time
=
820 compositor_timing_history_
->BeginMainFrameToCommitDurationEstimate() +
821 compositor_timing_history_
->CommitToReadyToActivateDurationEstimate() +
822 compositor_timing_history_
->ActivateDurationEstimate();
824 return estimated_draw_time
< args
.deadline
;
827 bool Scheduler::IsBeginMainFrameSentOrStarted() const {
828 return (state_machine_
.begin_main_frame_state() ==
829 SchedulerStateMachine::BEGIN_MAIN_FRAME_STATE_SENT
||
830 state_machine_
.begin_main_frame_state() ==
831 SchedulerStateMachine::BEGIN_MAIN_FRAME_STATE_STARTED
);