[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / cc / scheduler / scheduler.cc
blob15e1fe0b3661b74517af8bae2f042ba33550b73b
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 namespace {
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()));
49 Scheduler::Scheduler(
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),
59 client_(client),
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),
74 weak_factory_(this) {
75 TRACE_EVENT1("cc", "Scheduler::Scheduler", "settings", settings_.AsValue());
76 DCHECK(client_);
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"),
108 "Scheduler::Now",
109 "now",
110 now);
111 return 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;
164 if (throttle) {
165 frame_source_->SetActiveSource(primary_frame_source());
166 } else {
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
274 // value.
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_,
280 true);
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_,
288 false);
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);
320 return true;
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();
330 return true;
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.
345 } else {
346 BeginImplFrameWithDeadline(adjusted_args);
348 return true;
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_,
360 interval);
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)
407 break;
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);
420 } else {
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",
434 "state",
435 AsValue());
436 if (!frame_source_->NeedsBeginFrames())
437 return;
439 if (begin_retro_frame_args_.empty() || !begin_retro_frame_task_.IsCancelled())
440 return;
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)
448 return;
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());
476 return;
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",
488 args.AsValue());
489 BeginImplFrame(args);
490 FinishImplFrame();
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:
531 // No deadline.
532 return;
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();
537 break;
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;
541 break;
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;
548 break;
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");
555 return;
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)
570 return;
572 if (state_machine_.begin_impl_frame_state() !=
573 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME)
574 return;
576 if (begin_impl_frame_deadline_mode_ ==
577 state_machine_.CurrentBeginImplFrameDeadlineMode() &&
578 BeginImplFrameDeadlinePending())
579 return;
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();
601 FinishImplFrame();
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",
619 "defer_commits",
620 defer_commits);
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_)
629 return;
631 base::AutoReset<bool> mark_inside(&inside_process_scheduled_actions_, true);
633 SchedulerStateMachine::Action action;
634 do {
635 action = state_machine_.NextAction();
636 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
637 "SchedulerStateMachine",
638 "state",
639 AsValue());
640 base::AutoReset<SchedulerStateMachine::Action>
641 mark_inside_action(&inside_action_, action);
642 switch (action) {
643 case SchedulerStateMachine::ACTION_NONE:
644 break;
645 case SchedulerStateMachine::ACTION_ANIMATE:
646 state_machine_.WillAnimate();
647 client_->ScheduledActionAnimate();
648 break;
649 case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME:
650 compositor_timing_history_->WillBeginMainFrame();
651 state_machine_.WillSendBeginMainFrame();
652 client_->ScheduledActionSendBeginMainFrame();
653 break;
654 case SchedulerStateMachine::ACTION_COMMIT: {
655 // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is
656 // fixed.
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();
663 break;
665 case SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE:
666 compositor_timing_history_->WillActivate();
667 state_machine_.WillActivate();
668 client_->ScheduledActionActivateSyncTree();
669 compositor_timing_history_->DidActivate();
670 break;
671 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE: {
672 // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is
673 // fixed.
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();
680 break;
682 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED: {
683 bool did_request_swap = true;
684 state_machine_.WillDraw(did_request_swap);
685 DrawAndSwapForced();
686 break;
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);
693 break;
695 case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
696 state_machine_.WillBeginOutputSurfaceCreation();
697 client_->ScheduledActionBeginOutputSurfaceCreation();
698 break;
699 case SchedulerStateMachine::ACTION_PREPARE_TILES:
700 state_machine_.WillPrepareTiles();
701 client_->ScheduledActionPrepareTiles();
702 break;
703 case SchedulerStateMachine::ACTION_INVALIDATE_OUTPUT_SURFACE: {
704 state_machine_.WillInvalidateOutputSurface();
705 client_->ScheduledActionInvalidateOutputSurface();
706 break;
709 } while (action != SchedulerStateMachine::ACTION_NONE);
711 ScheduleBeginImplFrameDeadlineIfNeeded();
712 SetupNextBeginFrameIfNeeded();
715 scoped_refptr<base::trace_event::ConvertableToTraceFormat> Scheduler::AsValue()
716 const {
717 scoped_refptr<base::trace_event::TracedValue> state =
718 new base::trace_event::TracedValue();
719 AsValueInto(state.get());
720 return state;
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())
777 return false;
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())
782 return false;
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_)
794 return false;
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)
800 return false;
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
811 // the main thread.
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
818 // deadline.
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 =
827 args.frame_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);
842 } // namespace cc