Fix dcheck in message port code on Mandoline shutdown.
[chromium-blink-merge.git] / cc / scheduler / scheduler.cc
blob452bbb676b9cbdf92773f0f5568324df685de1f7
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"
7 #include <algorithm>
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"
20 namespace cc {
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()));
42 Scheduler::Scheduler(
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),
52 client_(client),
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),
67 weak_factory_(this) {
68 TRACE_EVENT1("cc", "Scheduler::Scheduler", "settings", settings_.AsValue());
69 DCHECK(client_);
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"),
101 "Scheduler::Now",
102 "now",
103 now);
104 return 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;
157 if (throttle) {
158 frame_source_->SetActiveSource(primary_frame_source());
159 } else {
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
267 // value.
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_,
273 true);
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_,
281 false);
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);
313 return true;
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();
323 return true;
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.
338 } else {
339 BeginImplFrameWithDeadline(adjusted_args);
341 return true;
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_,
353 interval);
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)
400 break;
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);
413 } else {
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",
427 "state",
428 AsValue());
429 if (!frame_source_->NeedsBeginFrames())
430 return;
432 if (begin_retro_frame_args_.empty() || !begin_retro_frame_task_.IsCancelled())
433 return;
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)
441 return;
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());
468 return;
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",
480 args.AsValue());
481 BeginImplFrame(args);
482 FinishImplFrame();
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:
523 // No deadline.
524 return;
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();
529 break;
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;
533 break;
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;
540 break;
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");
547 return;
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)
562 return;
564 if (state_machine_.begin_impl_frame_state() !=
565 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME)
566 return;
568 if (begin_impl_frame_deadline_mode_ ==
569 state_machine_.CurrentBeginImplFrameDeadlineMode() &&
570 BeginImplFrameDeadlinePending())
571 return;
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();
593 FinishImplFrame();
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",
611 "defer_commits",
612 defer_commits);
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_)
621 return;
623 base::AutoReset<bool> mark_inside(&inside_process_scheduled_actions_, true);
625 SchedulerStateMachine::Action action;
626 do {
627 action = state_machine_.NextAction();
628 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
629 "SchedulerStateMachine",
630 "state",
631 AsValue());
632 state_machine_.UpdateState(action);
633 base::AutoReset<SchedulerStateMachine::Action>
634 mark_inside_action(&inside_action_, action);
635 switch (action) {
636 case SchedulerStateMachine::ACTION_NONE:
637 break;
638 case SchedulerStateMachine::ACTION_ANIMATE:
639 client_->ScheduledActionAnimate();
640 break;
641 case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME:
642 compositor_timing_history_->WillBeginMainFrame();
643 client_->ScheduledActionSendBeginMainFrame();
644 break;
645 case SchedulerStateMachine::ACTION_COMMIT: {
646 // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is
647 // fixed.
648 tracked_objects::ScopedTracker tracking_profile4(
649 FROM_HERE_WITH_EXPLICIT_FUNCTION(
650 "461509 Scheduler::ProcessScheduledActions4"));
651 client_->ScheduledActionCommit();
652 break;
654 case SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE:
655 compositor_timing_history_->WillActivate();
656 client_->ScheduledActionActivateSyncTree();
657 compositor_timing_history_->DidActivate();
658 break;
659 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE: {
660 // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is
661 // fixed.
662 tracked_objects::ScopedTracker tracking_profile6(
663 FROM_HERE_WITH_EXPLICIT_FUNCTION(
664 "461509 Scheduler::ProcessScheduledActions6"));
665 DrawAndSwapIfPossible();
666 break;
668 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED:
669 DrawAndSwapForced();
670 break;
671 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT:
672 // No action is actually performed, but this allows the state machine to
673 // advance out of its waiting to draw state without actually drawing.
674 break;
675 case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
676 client_->ScheduledActionBeginOutputSurfaceCreation();
677 break;
678 case SchedulerStateMachine::ACTION_PREPARE_TILES:
679 client_->ScheduledActionPrepareTiles();
680 break;
681 case SchedulerStateMachine::ACTION_INVALIDATE_OUTPUT_SURFACE: {
682 client_->ScheduledActionInvalidateOutputSurface();
683 break;
686 } while (action != SchedulerStateMachine::ACTION_NONE);
688 ScheduleBeginImplFrameDeadlineIfNeeded();
689 SetupNextBeginFrameIfNeeded();
692 scoped_refptr<base::trace_event::ConvertableToTraceFormat> Scheduler::AsValue()
693 const {
694 scoped_refptr<base::trace_event::TracedValue> state =
695 new base::trace_event::TracedValue();
696 AsValueInto(state.get());
697 return state;
700 void Scheduler::AsValueInto(base::trace_event::TracedValue* state) const {
701 base::TimeTicks now = Now();
703 state->BeginDictionary("state_machine");
704 state_machine_.AsValueInto(state);
705 state->EndDictionary();
707 // Only trace frame sources when explicitly enabled - http://crbug.com/420607
708 bool frame_tracing_enabled = false;
709 TRACE_EVENT_CATEGORY_GROUP_ENABLED(
710 TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"),
711 &frame_tracing_enabled);
712 if (frame_tracing_enabled) {
713 state->BeginDictionary("frame_source_");
714 frame_source_->AsValueInto(state);
715 state->EndDictionary();
718 state->BeginDictionary("scheduler_state");
719 state->SetDouble("estimated_parent_draw_time_ms",
720 estimated_parent_draw_time_.InMillisecondsF());
721 state->SetInteger("begin_retro_frame_args",
722 static_cast<int>(begin_retro_frame_args_.size()));
723 state->SetBoolean("begin_retro_frame_task",
724 !begin_retro_frame_task_.IsCancelled());
725 state->SetBoolean("begin_impl_frame_deadline_task",
726 !begin_impl_frame_deadline_task_.IsCancelled());
727 state->SetString("inside_action",
728 SchedulerStateMachine::ActionToString(inside_action_));
730 state->BeginDictionary("begin_impl_frame_args");
731 begin_impl_frame_tracker_.AsValueInto(now, state);
732 state->EndDictionary();
734 state->SetString("begin_impl_frame_deadline_mode_",
735 SchedulerStateMachine::BeginImplFrameDeadlineModeToString(
736 begin_impl_frame_deadline_mode_));
737 state->EndDictionary();
739 state->BeginDictionary("compositor_timing_history");
740 compositor_timing_history_->AsValueInto(state);
741 state->EndDictionary();
744 void Scheduler::UpdateCompositorTimingHistoryRecordingEnabled() {
745 compositor_timing_history_->SetRecordingEnabled(
746 state_machine_.HasInitializedOutputSurface() &&
747 state_machine_.visible() && frame_source_->NeedsBeginFrames());
750 bool Scheduler::ShouldRecoverMainLatency(const BeginFrameArgs& args) const {
751 DCHECK(!settings_.using_synchronous_renderer_compositor);
753 if (!state_machine_.main_thread_missed_last_deadline())
754 return false;
756 // When prioritizing impl thread latency, we currently put the
757 // main thread in a high latency mode. Don't try to fight it.
758 if (state_machine_.impl_latency_takes_priority())
759 return false;
761 return CanCommitAndActivateBeforeDeadline(args);
764 bool Scheduler::ShouldRecoverImplLatency(const BeginFrameArgs& args) const {
765 DCHECK(!settings_.using_synchronous_renderer_compositor);
767 // Disable impl thread latency recovery when using the unthrottled
768 // begin frame source since we will always get a BeginFrame before
769 // the swap ack and our heuristics below will not work.
770 if (!throttle_frame_production_)
771 return false;
773 // If we are swap throttled at the BeginFrame, that means the impl thread is
774 // very likely in a high latency mode.
775 bool impl_thread_is_likely_high_latency = state_machine_.SwapThrottled();
776 if (!impl_thread_is_likely_high_latency)
777 return false;
779 // The deadline may be in the past if our draw time is too long.
780 bool can_draw_before_deadline = args.frame_time < args.deadline;
782 // When prioritizing impl thread latency, the deadline doesn't wait
783 // for the main thread.
784 if (state_machine_.impl_latency_takes_priority())
785 return can_draw_before_deadline;
787 // If we only have impl-side updates, the deadline doesn't wait for
788 // the main thread.
789 if (state_machine_.OnlyImplSideUpdatesExpected())
790 return can_draw_before_deadline;
792 // If we get here, we know the main thread is in a low-latency mode relative
793 // to the impl thread. In this case, only try to also recover impl thread
794 // latency if both the main and impl threads can run serially before the
795 // deadline.
796 return CanCommitAndActivateBeforeDeadline(args);
799 bool Scheduler::CanCommitAndActivateBeforeDeadline(
800 const BeginFrameArgs& args) const {
801 // Check if the main thread computation and commit can be finished before the
802 // impl thread's deadline.
803 base::TimeTicks estimated_draw_time =
804 args.frame_time +
805 compositor_timing_history_->BeginMainFrameToCommitDurationEstimate() +
806 compositor_timing_history_->CommitToReadyToActivateDurationEstimate() +
807 compositor_timing_history_->ActivateDurationEstimate();
809 return estimated_draw_time < args.deadline;
812 bool Scheduler::IsBeginMainFrameSentOrStarted() const {
813 return (state_machine_.begin_main_frame_state() ==
814 SchedulerStateMachine::BEGIN_MAIN_FRAME_STATE_SENT ||
815 state_machine_.begin_main_frame_state() ==
816 SchedulerStateMachine::BEGIN_MAIN_FRAME_STATE_STARTED);
819 } // namespace cc