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"
23 // This is a fudge factor we subtract from the deadline to account
24 // for message latency and kernel scheduling variability.
25 const base::TimeDelta kDeadlineFudgeFactor
=
26 base::TimeDelta::FromMicroseconds(1000);
29 scoped_ptr
<Scheduler
> Scheduler::Create(
30 SchedulerClient
* client
,
31 const SchedulerSettings
& settings
,
32 int layer_tree_host_id
,
33 base::SingleThreadTaskRunner
* task_runner
,
34 BeginFrameSource
* external_frame_source
,
35 scoped_ptr
<CompositorTimingHistory
> compositor_timing_history
) {
36 scoped_ptr
<SyntheticBeginFrameSource
> synthetic_frame_source
;
37 if (!settings
.use_external_begin_frame_source
) {
38 synthetic_frame_source
= SyntheticBeginFrameSource::Create(
39 task_runner
, BeginFrameArgs::DefaultInterval());
41 scoped_ptr
<BackToBackBeginFrameSource
> unthrottled_frame_source
=
42 BackToBackBeginFrameSource::Create(task_runner
);
43 return make_scoped_ptr(new Scheduler(
44 client
, settings
, layer_tree_host_id
, task_runner
, external_frame_source
,
45 synthetic_frame_source
.Pass(), unthrottled_frame_source
.Pass(),
46 compositor_timing_history
.Pass()));
50 SchedulerClient
* client
,
51 const SchedulerSettings
& settings
,
52 int layer_tree_host_id
,
53 base::SingleThreadTaskRunner
* task_runner
,
54 BeginFrameSource
* external_frame_source
,
55 scoped_ptr
<SyntheticBeginFrameSource
> synthetic_frame_source
,
56 scoped_ptr
<BackToBackBeginFrameSource
> unthrottled_frame_source
,
57 scoped_ptr
<CompositorTimingHistory
> compositor_timing_history
)
58 : settings_(settings
),
60 layer_tree_host_id_(layer_tree_host_id
),
61 task_runner_(task_runner
),
62 external_frame_source_(external_frame_source
),
63 synthetic_frame_source_(synthetic_frame_source
.Pass()),
64 unthrottled_frame_source_(unthrottled_frame_source
.Pass()),
65 frame_source_(BeginFrameSourceMultiplexer::Create()),
66 throttle_frame_production_(false),
67 compositor_timing_history_(compositor_timing_history
.Pass()),
68 begin_impl_frame_deadline_mode_(
69 SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE
),
70 begin_impl_frame_tracker_(BEGINFRAMETRACKER_FROM_HERE
),
71 state_machine_(settings
),
72 inside_process_scheduled_actions_(false),
73 inside_action_(SchedulerStateMachine::ACTION_NONE
),
75 TRACE_EVENT1("cc", "Scheduler::Scheduler", "settings", settings_
.AsValue());
77 DCHECK(!state_machine_
.BeginFrameNeeded());
78 DCHECK_IMPLIES(settings_
.use_external_begin_frame_source
,
79 external_frame_source_
);
80 DCHECK_IMPLIES(!settings_
.use_external_begin_frame_source
,
81 synthetic_frame_source_
);
82 DCHECK(unthrottled_frame_source_
);
84 begin_retro_frame_closure_
=
85 base::Bind(&Scheduler::BeginRetroFrame
, weak_factory_
.GetWeakPtr());
86 begin_impl_frame_deadline_closure_
= base::Bind(
87 &Scheduler::OnBeginImplFrameDeadline
, weak_factory_
.GetWeakPtr());
89 frame_source_
->AddObserver(this);
90 frame_source_
->AddSource(primary_frame_source());
91 primary_frame_source()->SetClientReady();
93 frame_source_
->AddSource(unthrottled_frame_source_
.get());
94 unthrottled_frame_source_
->SetClientReady();
96 SetThrottleFrameProduction(settings_
.throttle_frame_production
);
99 Scheduler::~Scheduler() {
100 if (frame_source_
->NeedsBeginFrames())
101 frame_source_
->SetNeedsBeginFrames(false);
102 frame_source_
->SetActiveSource(nullptr);
105 base::TimeTicks
Scheduler::Now() const {
106 base::TimeTicks now
= base::TimeTicks::Now();
107 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.now"),
114 void Scheduler::CommitVSyncParameters(base::TimeTicks timebase
,
115 base::TimeDelta interval
) {
116 if (authoritative_vsync_interval_
!= base::TimeDelta()) {
117 interval
= authoritative_vsync_interval_
;
118 } else if (interval
== base::TimeDelta()) {
119 // TODO(brianderson): We should not be receiving 0 intervals.
120 interval
= BeginFrameArgs::DefaultInterval();
123 last_vsync_timebase_
= timebase
;
125 if (synthetic_frame_source_
)
126 synthetic_frame_source_
->OnUpdateVSyncParameters(timebase
, interval
);
129 void Scheduler::SetEstimatedParentDrawTime(base::TimeDelta draw_time
) {
130 DCHECK_GE(draw_time
.ToInternalValue(), 0);
131 estimated_parent_draw_time_
= draw_time
;
134 void Scheduler::SetCanStart() {
135 state_machine_
.SetCanStart();
136 ProcessScheduledActions();
139 void Scheduler::SetVisible(bool visible
) {
140 state_machine_
.SetVisible(visible
);
141 UpdateCompositorTimingHistoryRecordingEnabled();
142 ProcessScheduledActions();
145 void Scheduler::SetCanDraw(bool can_draw
) {
146 state_machine_
.SetCanDraw(can_draw
);
147 ProcessScheduledActions();
150 void Scheduler::NotifyReadyToActivate() {
151 compositor_timing_history_
->ReadyToActivate();
152 state_machine_
.NotifyReadyToActivate();
153 ProcessScheduledActions();
156 void Scheduler::NotifyReadyToDraw() {
157 // Future work might still needed for crbug.com/352894.
158 state_machine_
.NotifyReadyToDraw();
159 ProcessScheduledActions();
162 void Scheduler::SetThrottleFrameProduction(bool throttle
) {
163 throttle_frame_production_
= throttle
;
165 frame_source_
->SetActiveSource(primary_frame_source());
167 frame_source_
->SetActiveSource(unthrottled_frame_source_
.get());
169 ProcessScheduledActions();
172 void Scheduler::SetNeedsBeginMainFrame() {
173 state_machine_
.SetNeedsBeginMainFrame();
174 ProcessScheduledActions();
177 void Scheduler::SetNeedsRedraw() {
178 state_machine_
.SetNeedsRedraw();
179 ProcessScheduledActions();
182 void Scheduler::SetNeedsAnimate() {
183 state_machine_
.SetNeedsAnimate();
184 ProcessScheduledActions();
187 void Scheduler::SetNeedsPrepareTiles() {
188 DCHECK(!IsInsideAction(SchedulerStateMachine::ACTION_PREPARE_TILES
));
189 state_machine_
.SetNeedsPrepareTiles();
190 ProcessScheduledActions();
193 void Scheduler::SetMaxSwapsPending(int max
) {
194 state_machine_
.SetMaxSwapsPending(max
);
197 void Scheduler::DidSwapBuffers() {
198 state_machine_
.DidSwapBuffers();
200 // There is no need to call ProcessScheduledActions here because
201 // swapping should not trigger any new actions.
202 if (!inside_process_scheduled_actions_
) {
203 DCHECK_EQ(state_machine_
.NextAction(), SchedulerStateMachine::ACTION_NONE
);
207 void Scheduler::DidSwapBuffersComplete() {
208 DCHECK_GT(state_machine_
.pending_swaps(), 0) << AsValue()->ToString();
209 state_machine_
.DidSwapBuffersComplete();
210 ProcessScheduledActions();
213 void Scheduler::SetImplLatencyTakesPriority(bool impl_latency_takes_priority
) {
214 state_machine_
.SetImplLatencyTakesPriority(impl_latency_takes_priority
);
215 ProcessScheduledActions();
218 void Scheduler::NotifyReadyToCommit() {
219 TRACE_EVENT0("cc", "Scheduler::NotifyReadyToCommit");
220 state_machine_
.NotifyReadyToCommit();
221 ProcessScheduledActions();
224 void Scheduler::DidCommit() {
225 compositor_timing_history_
->DidCommit();
228 void Scheduler::BeginMainFrameAborted(CommitEarlyOutReason reason
) {
229 TRACE_EVENT1("cc", "Scheduler::BeginMainFrameAborted", "reason",
230 CommitEarlyOutReasonToString(reason
));
231 compositor_timing_history_
->BeginMainFrameAborted();
232 state_machine_
.BeginMainFrameAborted(reason
);
233 ProcessScheduledActions();
236 void Scheduler::WillPrepareTiles() {
237 compositor_timing_history_
->WillPrepareTiles();
240 void Scheduler::DidPrepareTiles() {
241 compositor_timing_history_
->DidPrepareTiles();
242 state_machine_
.DidPrepareTiles();
245 void Scheduler::DidLoseOutputSurface() {
246 TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface");
247 begin_retro_frame_args_
.clear();
248 begin_retro_frame_task_
.Cancel();
249 state_machine_
.DidLoseOutputSurface();
250 UpdateCompositorTimingHistoryRecordingEnabled();
251 ProcessScheduledActions();
254 void Scheduler::DidCreateAndInitializeOutputSurface() {
255 TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface");
256 DCHECK(!frame_source_
->NeedsBeginFrames());
257 DCHECK(begin_impl_frame_deadline_task_
.IsCancelled());
258 state_machine_
.DidCreateAndInitializeOutputSurface();
259 UpdateCompositorTimingHistoryRecordingEnabled();
260 ProcessScheduledActions();
263 void Scheduler::NotifyBeginMainFrameStarted() {
264 TRACE_EVENT0("cc", "Scheduler::NotifyBeginMainFrameStarted");
265 state_machine_
.NotifyBeginMainFrameStarted();
268 base::TimeTicks
Scheduler::LastBeginImplFrameTime() {
269 return begin_impl_frame_tracker_
.Current().frame_time
;
272 void Scheduler::SetupNextBeginFrameIfNeeded() {
273 // Never call SetNeedsBeginFrames if the frame source already has the right
275 if (frame_source_
->NeedsBeginFrames() != state_machine_
.BeginFrameNeeded()) {
276 if (state_machine_
.BeginFrameNeeded()) {
277 // Call SetNeedsBeginFrames(true) as soon as possible.
278 frame_source_
->SetNeedsBeginFrames(true);
279 devtools_instrumentation::NeedsBeginFrameChanged(layer_tree_host_id_
,
281 UpdateCompositorTimingHistoryRecordingEnabled();
282 } else if (state_machine_
.begin_impl_frame_state() ==
283 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
) {
284 // Call SetNeedsBeginFrames(false) in between frames only.
285 frame_source_
->SetNeedsBeginFrames(false);
286 client_
->SendBeginMainFrameNotExpectedSoon();
287 devtools_instrumentation::NeedsBeginFrameChanged(layer_tree_host_id_
,
289 UpdateCompositorTimingHistoryRecordingEnabled();
293 PostBeginRetroFrameIfNeeded();
296 // BeginFrame is the mechanism that tells us that now is a good time to start
297 // making a frame. Usually this means that user input for the frame is complete.
298 // If the scheduler is busy, we queue the BeginFrame to be handled later as
299 // a BeginRetroFrame.
300 bool Scheduler::OnBeginFrameDerivedImpl(const BeginFrameArgs
& args
) {
301 TRACE_EVENT1("cc,benchmark", "Scheduler::BeginFrame", "args", args
.AsValue());
303 // Trace this begin frame time through the Chrome stack
304 TRACE_EVENT_FLOW_BEGIN0(
305 TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), "BeginFrameArgs",
306 args
.frame_time
.ToInternalValue());
308 // TODO(brianderson): Adjust deadline in the DisplayScheduler.
309 BeginFrameArgs
adjusted_args(args
);
310 adjusted_args
.deadline
-= EstimatedParentDrawTime();
311 adjusted_args
.on_critical_path
= !ImplLatencyTakesPriority();
313 // Deliver BeginFrames to children.
314 // TODO(brianderson): Move this responsibility to the DisplayScheduler.
315 if (state_machine_
.children_need_begin_frames())
316 client_
->SendBeginFramesToChildren(adjusted_args
);
318 if (settings_
.using_synchronous_renderer_compositor
) {
319 BeginImplFrameSynchronous(adjusted_args
);
323 // We have just called SetNeedsBeginFrame(true) and the BeginFrameSource has
324 // sent us the last BeginFrame we have missed. As we might not be able to
325 // actually make rendering for this call, handle it like a "retro frame".
326 // TODO(brainderson): Add a test for this functionality ASAP!
327 if (adjusted_args
.type
== BeginFrameArgs::MISSED
) {
328 begin_retro_frame_args_
.push_back(adjusted_args
);
329 PostBeginRetroFrameIfNeeded();
333 bool should_defer_begin_frame
=
334 !begin_retro_frame_args_
.empty() ||
335 !begin_retro_frame_task_
.IsCancelled() ||
336 !frame_source_
->NeedsBeginFrames() ||
337 (state_machine_
.begin_impl_frame_state() !=
338 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
);
340 if (should_defer_begin_frame
) {
341 begin_retro_frame_args_
.push_back(adjusted_args
);
342 TRACE_EVENT_INSTANT0(
343 "cc", "Scheduler::BeginFrame deferred", TRACE_EVENT_SCOPE_THREAD
);
344 // Queuing the frame counts as "using it", so we need to return true.
346 BeginImplFrameWithDeadline(adjusted_args
);
351 void Scheduler::SetChildrenNeedBeginFrames(bool children_need_begin_frames
) {
352 state_machine_
.SetChildrenNeedBeginFrames(children_need_begin_frames
);
353 ProcessScheduledActions();
356 void Scheduler::SetAuthoritativeVSyncInterval(const base::TimeDelta
& interval
) {
357 authoritative_vsync_interval_
= interval
;
358 if (synthetic_frame_source_
) {
359 synthetic_frame_source_
->OnUpdateVSyncParameters(last_vsync_timebase_
,
364 void Scheduler::SetVideoNeedsBeginFrames(bool video_needs_begin_frames
) {
365 state_machine_
.SetVideoNeedsBeginFrames(video_needs_begin_frames
);
366 ProcessScheduledActions();
369 void Scheduler::OnDrawForOutputSurface() {
370 DCHECK(settings_
.using_synchronous_renderer_compositor
);
371 DCHECK_EQ(state_machine_
.begin_impl_frame_state(),
372 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
);
373 DCHECK(!BeginImplFrameDeadlinePending());
375 state_machine_
.OnBeginImplFrameDeadline();
376 ProcessScheduledActions();
378 state_machine_
.OnBeginImplFrameIdle();
379 ProcessScheduledActions();
382 // BeginRetroFrame is called for BeginFrames that we've deferred because
383 // the scheduler was in the middle of processing a previous BeginFrame.
384 void Scheduler::BeginRetroFrame() {
385 TRACE_EVENT0("cc,benchmark", "Scheduler::BeginRetroFrame");
386 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
387 DCHECK(!begin_retro_frame_args_
.empty());
388 DCHECK(!begin_retro_frame_task_
.IsCancelled());
389 DCHECK_EQ(state_machine_
.begin_impl_frame_state(),
390 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
);
392 begin_retro_frame_task_
.Cancel();
394 // Discard expired BeginRetroFrames
395 // Today, we should always end up with at most one un-expired BeginRetroFrame
396 // because deadlines will not be greater than the next frame time. We don't
397 // DCHECK though because some systems don't always have monotonic timestamps.
398 // TODO(brianderson): In the future, long deadlines could result in us not
399 // draining the queue if we don't catch up. If we consistently can't catch
400 // up, our fallback should be to lower our frame rate.
401 base::TimeTicks now
= Now();
403 while (!begin_retro_frame_args_
.empty()) {
404 const BeginFrameArgs
& args
= begin_retro_frame_args_
.front();
405 base::TimeTicks expiration_time
= args
.deadline
;
406 if (now
<= expiration_time
)
408 TRACE_EVENT_INSTANT2(
409 "cc", "Scheduler::BeginRetroFrame discarding", TRACE_EVENT_SCOPE_THREAD
,
410 "expiration_time - now", (expiration_time
- now
).InMillisecondsF(),
411 "BeginFrameArgs", begin_retro_frame_args_
.front().AsValue());
412 begin_retro_frame_args_
.pop_front();
413 frame_source_
->DidFinishFrame(begin_retro_frame_args_
.size());
416 if (begin_retro_frame_args_
.empty()) {
417 TRACE_EVENT_INSTANT0("cc",
418 "Scheduler::BeginRetroFrames all expired",
419 TRACE_EVENT_SCOPE_THREAD
);
421 BeginFrameArgs front
= begin_retro_frame_args_
.front();
422 begin_retro_frame_args_
.pop_front();
423 BeginImplFrameWithDeadline(front
);
427 // There could be a race between the posted BeginRetroFrame and a new
428 // BeginFrame arriving via the normal mechanism. Scheduler::BeginFrame
429 // will check if there is a pending BeginRetroFrame to ensure we handle
430 // BeginFrames in FIFO order.
431 void Scheduler::PostBeginRetroFrameIfNeeded() {
432 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
433 "Scheduler::PostBeginRetroFrameIfNeeded",
436 if (!frame_source_
->NeedsBeginFrames())
439 if (begin_retro_frame_args_
.empty() || !begin_retro_frame_task_
.IsCancelled())
442 // begin_retro_frame_args_ should always be empty for the
443 // synchronous compositor.
444 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
446 if (state_machine_
.begin_impl_frame_state() !=
447 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
)
450 begin_retro_frame_task_
.Reset(begin_retro_frame_closure_
);
452 task_runner_
->PostTask(FROM_HERE
, begin_retro_frame_task_
.callback());
455 void Scheduler::BeginImplFrameWithDeadline(const BeginFrameArgs
& args
) {
456 bool main_thread_is_in_high_latency_mode
=
457 state_machine_
.main_thread_missed_last_deadline();
458 TRACE_EVENT2("cc,benchmark", "Scheduler::BeginImplFrame", "args",
459 args
.AsValue(), "main_thread_missed_last_deadline",
460 main_thread_is_in_high_latency_mode
);
461 TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
462 "MainThreadLatency", main_thread_is_in_high_latency_mode
);
464 BeginFrameArgs adjusted_args
= args
;
465 adjusted_args
.deadline
-= compositor_timing_history_
->DrawDurationEstimate();
466 adjusted_args
.deadline
-= kDeadlineFudgeFactor
;
468 if (ShouldRecoverMainLatency(adjusted_args
)) {
469 TRACE_EVENT_INSTANT0("cc", "SkipBeginMainFrameToReduceLatency",
470 TRACE_EVENT_SCOPE_THREAD
);
471 state_machine_
.SetSkipNextBeginMainFrameToReduceLatency();
472 } else if (ShouldRecoverImplLatency(adjusted_args
)) {
473 TRACE_EVENT_INSTANT0("cc", "SkipBeginImplFrameToReduceLatency",
474 TRACE_EVENT_SCOPE_THREAD
);
475 frame_source_
->DidFinishFrame(begin_retro_frame_args_
.size());
479 BeginImplFrame(adjusted_args
);
481 // The deadline will be scheduled in ProcessScheduledActions.
482 state_machine_
.OnBeginImplFrameDeadlinePending();
483 ProcessScheduledActions();
486 void Scheduler::BeginImplFrameSynchronous(const BeginFrameArgs
& args
) {
487 TRACE_EVENT1("cc,benchmark", "Scheduler::BeginImplFrame", "args",
489 BeginImplFrame(args
);
493 void Scheduler::FinishImplFrame() {
494 state_machine_
.OnBeginImplFrameIdle();
495 ProcessScheduledActions();
497 client_
->DidFinishImplFrame();
498 frame_source_
->DidFinishFrame(begin_retro_frame_args_
.size());
499 begin_impl_frame_tracker_
.Finish();
502 // BeginImplFrame starts a compositor frame that will wait up until a deadline
503 // for a BeginMainFrame+activation to complete before it times out and draws
504 // any asynchronous animation and scroll/pinch updates.
505 void Scheduler::BeginImplFrame(const BeginFrameArgs
& args
) {
506 DCHECK_EQ(state_machine_
.begin_impl_frame_state(),
507 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
);
508 DCHECK(!BeginImplFrameDeadlinePending());
509 DCHECK(state_machine_
.HasInitializedOutputSurface());
511 begin_impl_frame_tracker_
.Start(args
);
512 state_machine_
.OnBeginImplFrame();
513 devtools_instrumentation::DidBeginFrame(layer_tree_host_id_
);
514 client_
->WillBeginImplFrame(begin_impl_frame_tracker_
.Current());
516 ProcessScheduledActions();
519 void Scheduler::ScheduleBeginImplFrameDeadline() {
520 // The synchronous compositor does not post a deadline task.
521 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
523 begin_impl_frame_deadline_task_
.Cancel();
524 begin_impl_frame_deadline_task_
.Reset(begin_impl_frame_deadline_closure_
);
526 begin_impl_frame_deadline_mode_
=
527 state_machine_
.CurrentBeginImplFrameDeadlineMode();
528 base::TimeTicks deadline
;
529 switch (begin_impl_frame_deadline_mode_
) {
530 case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE
:
533 case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE
:
534 // We are ready to draw a new active tree immediately.
535 // We don't use Now() here because it's somewhat expensive to call.
536 deadline
= base::TimeTicks();
538 case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_REGULAR
:
539 // We are animating on the impl thread but we can wait for some time.
540 deadline
= begin_impl_frame_tracker_
.Current().deadline
;
542 case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_LATE
:
543 // We are blocked for one reason or another and we should wait.
544 // TODO(brianderson): Handle long deadlines (that are past the next
545 // frame's frame time) properly instead of using this hack.
546 deadline
= begin_impl_frame_tracker_
.Current().frame_time
+
547 begin_impl_frame_tracker_
.Current().interval
;
549 case SchedulerStateMachine::
550 BEGIN_IMPL_FRAME_DEADLINE_MODE_BLOCKED_ON_READY_TO_DRAW
:
551 // We are blocked because we are waiting for ReadyToDraw signal. We would
552 // post deadline after we received ReadyToDraw singal.
553 TRACE_EVENT1("cc", "Scheduler::ScheduleBeginImplFrameDeadline",
554 "deadline_mode", "blocked_on_ready_to_draw");
558 TRACE_EVENT2("cc", "Scheduler::ScheduleBeginImplFrameDeadline", "mode",
559 SchedulerStateMachine::BeginImplFrameDeadlineModeToString(
560 begin_impl_frame_deadline_mode_
),
561 "deadline", deadline
);
563 base::TimeDelta delta
= std::max(deadline
- Now(), base::TimeDelta());
564 task_runner_
->PostDelayedTask(
565 FROM_HERE
, begin_impl_frame_deadline_task_
.callback(), delta
);
568 void Scheduler::ScheduleBeginImplFrameDeadlineIfNeeded() {
569 if (settings_
.using_synchronous_renderer_compositor
)
572 if (state_machine_
.begin_impl_frame_state() !=
573 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME
)
576 if (begin_impl_frame_deadline_mode_
==
577 state_machine_
.CurrentBeginImplFrameDeadlineMode() &&
578 BeginImplFrameDeadlinePending())
581 ScheduleBeginImplFrameDeadline();
584 void Scheduler::OnBeginImplFrameDeadline() {
585 TRACE_EVENT0("cc,benchmark", "Scheduler::OnBeginImplFrameDeadline");
586 begin_impl_frame_deadline_task_
.Cancel();
587 // We split the deadline actions up into two phases so the state machine
588 // has a chance to trigger actions that should occur durring and after
589 // the deadline separately. For example:
590 // * Sending the BeginMainFrame will not occur after the deadline in
591 // order to wait for more user-input before starting the next commit.
592 // * Creating a new OuputSurface will not occur during the deadline in
593 // order to allow the state machine to "settle" first.
595 // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is fixed.
596 tracked_objects::ScopedTracker
tracking_profile1(
597 FROM_HERE_WITH_EXPLICIT_FUNCTION(
598 "461509 Scheduler::OnBeginImplFrameDeadline1"));
599 state_machine_
.OnBeginImplFrameDeadline();
600 ProcessScheduledActions();
604 void Scheduler::DrawAndSwapIfPossible() {
605 compositor_timing_history_
->WillDraw();
606 DrawResult result
= client_
->ScheduledActionDrawAndSwapIfPossible();
607 state_machine_
.DidDrawIfPossibleCompleted(result
);
608 compositor_timing_history_
->DidDraw();
611 void Scheduler::DrawAndSwapForced() {
612 compositor_timing_history_
->WillDraw();
613 client_
->ScheduledActionDrawAndSwapForced();
614 compositor_timing_history_
->DidDraw();
617 void Scheduler::SetDeferCommits(bool defer_commits
) {
618 TRACE_EVENT1("cc", "Scheduler::SetDeferCommits",
621 state_machine_
.SetDeferCommits(defer_commits
);
622 ProcessScheduledActions();
625 void Scheduler::ProcessScheduledActions() {
626 // We do not allow ProcessScheduledActions to be recursive.
627 // The top-level call will iteratively execute the next action for us anyway.
628 if (inside_process_scheduled_actions_
)
631 base::AutoReset
<bool> mark_inside(&inside_process_scheduled_actions_
, true);
633 SchedulerStateMachine::Action action
;
635 action
= state_machine_
.NextAction();
636 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
637 "SchedulerStateMachine",
640 base::AutoReset
<SchedulerStateMachine::Action
>
641 mark_inside_action(&inside_action_
, action
);
643 case SchedulerStateMachine::ACTION_NONE
:
645 case SchedulerStateMachine::ACTION_ANIMATE
:
646 state_machine_
.WillAnimate();
647 client_
->ScheduledActionAnimate();
649 case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME
:
650 compositor_timing_history_
->WillBeginMainFrame();
651 state_machine_
.WillSendBeginMainFrame();
652 client_
->ScheduledActionSendBeginMainFrame();
654 case SchedulerStateMachine::ACTION_COMMIT
: {
655 // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is
657 tracked_objects::ScopedTracker
tracking_profile4(
658 FROM_HERE_WITH_EXPLICIT_FUNCTION(
659 "461509 Scheduler::ProcessScheduledActions4"));
660 bool commit_has_no_updates
= false;
661 state_machine_
.WillCommit(commit_has_no_updates
);
662 client_
->ScheduledActionCommit();
665 case SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE
:
666 compositor_timing_history_
->WillActivate();
667 state_machine_
.WillActivate();
668 client_
->ScheduledActionActivateSyncTree();
669 compositor_timing_history_
->DidActivate();
671 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE
: {
672 // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is
674 tracked_objects::ScopedTracker
tracking_profile6(
675 FROM_HERE_WITH_EXPLICIT_FUNCTION(
676 "461509 Scheduler::ProcessScheduledActions6"));
677 bool did_request_swap
= true;
678 state_machine_
.WillDraw(did_request_swap
);
679 DrawAndSwapIfPossible();
682 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED
: {
683 bool did_request_swap
= true;
684 state_machine_
.WillDraw(did_request_swap
);
688 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT
: {
689 // No action is actually performed, but this allows the state machine to
690 // advance out of its waiting to draw state without actually drawing.
691 bool did_request_swap
= false;
692 state_machine_
.WillDraw(did_request_swap
);
695 case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION
:
696 state_machine_
.WillBeginOutputSurfaceCreation();
697 client_
->ScheduledActionBeginOutputSurfaceCreation();
699 case SchedulerStateMachine::ACTION_PREPARE_TILES
:
700 state_machine_
.WillPrepareTiles();
701 client_
->ScheduledActionPrepareTiles();
703 case SchedulerStateMachine::ACTION_INVALIDATE_OUTPUT_SURFACE
: {
704 state_machine_
.WillInvalidateOutputSurface();
705 client_
->ScheduledActionInvalidateOutputSurface();
709 } while (action
!= SchedulerStateMachine::ACTION_NONE
);
711 ScheduleBeginImplFrameDeadlineIfNeeded();
712 SetupNextBeginFrameIfNeeded();
715 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
> Scheduler::AsValue()
717 scoped_refptr
<base::trace_event::TracedValue
> state
=
718 new base::trace_event::TracedValue();
719 AsValueInto(state
.get());
723 void Scheduler::AsValueInto(base::trace_event::TracedValue
* state
) const {
724 base::TimeTicks now
= Now();
726 state
->BeginDictionary("state_machine");
727 state_machine_
.AsValueInto(state
);
728 state
->EndDictionary();
730 // Only trace frame sources when explicitly enabled - http://crbug.com/420607
731 bool frame_tracing_enabled
= false;
732 TRACE_EVENT_CATEGORY_GROUP_ENABLED(
733 TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"),
734 &frame_tracing_enabled
);
735 if (frame_tracing_enabled
) {
736 state
->BeginDictionary("frame_source_");
737 frame_source_
->AsValueInto(state
);
738 state
->EndDictionary();
741 state
->BeginDictionary("scheduler_state");
742 state
->SetDouble("estimated_parent_draw_time_ms",
743 estimated_parent_draw_time_
.InMillisecondsF());
744 state
->SetInteger("begin_retro_frame_args",
745 static_cast<int>(begin_retro_frame_args_
.size()));
746 state
->SetBoolean("begin_retro_frame_task",
747 !begin_retro_frame_task_
.IsCancelled());
748 state
->SetBoolean("begin_impl_frame_deadline_task",
749 !begin_impl_frame_deadline_task_
.IsCancelled());
750 state
->SetString("inside_action",
751 SchedulerStateMachine::ActionToString(inside_action_
));
753 state
->BeginDictionary("begin_impl_frame_args");
754 begin_impl_frame_tracker_
.AsValueInto(now
, state
);
755 state
->EndDictionary();
757 state
->SetString("begin_impl_frame_deadline_mode_",
758 SchedulerStateMachine::BeginImplFrameDeadlineModeToString(
759 begin_impl_frame_deadline_mode_
));
760 state
->EndDictionary();
762 state
->BeginDictionary("compositor_timing_history");
763 compositor_timing_history_
->AsValueInto(state
);
764 state
->EndDictionary();
767 void Scheduler::UpdateCompositorTimingHistoryRecordingEnabled() {
768 compositor_timing_history_
->SetRecordingEnabled(
769 state_machine_
.HasInitializedOutputSurface() &&
770 state_machine_
.visible() && frame_source_
->NeedsBeginFrames());
773 bool Scheduler::ShouldRecoverMainLatency(const BeginFrameArgs
& args
) const {
774 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
776 if (!state_machine_
.main_thread_missed_last_deadline())
779 // When prioritizing impl thread latency, we currently put the
780 // main thread in a high latency mode. Don't try to fight it.
781 if (state_machine_
.impl_latency_takes_priority())
784 return CanCommitAndActivateBeforeDeadline(args
);
787 bool Scheduler::ShouldRecoverImplLatency(const BeginFrameArgs
& args
) const {
788 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
790 // Disable impl thread latency recovery when using the unthrottled
791 // begin frame source since we will always get a BeginFrame before
792 // the swap ack and our heuristics below will not work.
793 if (!throttle_frame_production_
)
796 // If we are swap throttled at the BeginFrame, that means the impl thread is
797 // very likely in a high latency mode.
798 bool impl_thread_is_likely_high_latency
= state_machine_
.SwapThrottled();
799 if (!impl_thread_is_likely_high_latency
)
802 // The deadline may be in the past if our draw time is too long.
803 bool can_draw_before_deadline
= args
.frame_time
< args
.deadline
;
805 // When prioritizing impl thread latency, the deadline doesn't wait
806 // for the main thread.
807 if (state_machine_
.impl_latency_takes_priority())
808 return can_draw_before_deadline
;
810 // If we only have impl-side updates, the deadline doesn't wait for
812 if (state_machine_
.OnlyImplSideUpdatesExpected())
813 return can_draw_before_deadline
;
815 // If we get here, we know the main thread is in a low-latency mode relative
816 // to the impl thread. In this case, only try to also recover impl thread
817 // latency if both the main and impl threads can run serially before the
819 return CanCommitAndActivateBeforeDeadline(args
);
822 bool Scheduler::CanCommitAndActivateBeforeDeadline(
823 const BeginFrameArgs
& args
) const {
824 // Check if the main thread computation and commit can be finished before the
825 // impl thread's deadline.
826 base::TimeTicks estimated_draw_time
=
828 compositor_timing_history_
->BeginMainFrameToCommitDurationEstimate() +
829 compositor_timing_history_
->CommitToReadyToActivateDurationEstimate() +
830 compositor_timing_history_
->ActivateDurationEstimate();
832 return estimated_draw_time
< args
.deadline
;
835 bool Scheduler::IsBeginMainFrameSentOrStarted() const {
836 return (state_machine_
.begin_main_frame_state() ==
837 SchedulerStateMachine::BEGIN_MAIN_FRAME_STATE_SENT
||
838 state_machine_
.begin_main_frame_state() ==
839 SchedulerStateMachine::BEGIN_MAIN_FRAME_STATE_STARTED
);