1 // Copyright 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/scheduler/scheduler.h"
8 #include "base/auto_reset.h"
9 #include "base/debug/trace_event.h"
10 #include "base/debug/trace_event_argument.h"
11 #include "base/logging.h"
12 #include "base/single_thread_task_runner.h"
13 #include "cc/debug/devtools_instrumentation.h"
14 #include "cc/debug/traced_value.h"
15 #include "cc/scheduler/delay_based_time_source.h"
16 #include "ui/gfx/frame_time.h"
20 Scheduler::SyntheticBeginFrameSource::SyntheticBeginFrameSource(
22 scoped_refptr
<DelayBasedTimeSource
> time_source
)
23 : scheduler_(scheduler
), time_source_(time_source
) {
24 time_source_
->SetClient(this);
27 Scheduler::SyntheticBeginFrameSource::~SyntheticBeginFrameSource() {
30 void Scheduler::SyntheticBeginFrameSource::CommitVSyncParameters(
31 base::TimeTicks timebase
,
32 base::TimeDelta interval
) {
33 time_source_
->SetTimebaseAndInterval(timebase
, interval
);
36 void Scheduler::SyntheticBeginFrameSource::SetNeedsBeginFrame(
37 bool needs_begin_frame
,
38 std::deque
<BeginFrameArgs
>* begin_retro_frame_args
) {
39 DCHECK(begin_retro_frame_args
);
40 base::TimeTicks missed_tick_time
=
41 time_source_
->SetActive(needs_begin_frame
);
42 if (!missed_tick_time
.is_null()) {
43 begin_retro_frame_args
->push_back(
44 CreateSyntheticBeginFrameArgs(missed_tick_time
));
48 bool Scheduler::SyntheticBeginFrameSource::IsActive() const {
49 return time_source_
->Active();
52 void Scheduler::SyntheticBeginFrameSource::OnTimerTick() {
53 BeginFrameArgs
begin_frame_args(
54 CreateSyntheticBeginFrameArgs(time_source_
->LastTickTime()));
55 scheduler_
->BeginFrame(begin_frame_args
);
58 void Scheduler::SyntheticBeginFrameSource::AsValueInto(
59 base::debug::TracedValue
* state
) const {
60 time_source_
->AsValueInto(state
);
64 Scheduler::SyntheticBeginFrameSource::CreateSyntheticBeginFrameArgs(
65 base::TimeTicks frame_time
) {
66 base::TimeTicks deadline
= time_source_
->NextTickTime();
67 return BeginFrameArgs::Create(
68 frame_time
, deadline
, scheduler_
->VSyncInterval());
72 SchedulerClient
* client
,
73 const SchedulerSettings
& scheduler_settings
,
74 int layer_tree_host_id
,
75 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
)
76 : settings_(scheduler_settings
),
78 layer_tree_host_id_(layer_tree_host_id
),
79 task_runner_(task_runner
),
80 vsync_interval_(BeginFrameArgs::DefaultInterval()),
81 last_set_needs_begin_frame_(false),
82 begin_unthrottled_frame_posted_(false),
83 begin_retro_frame_posted_(false),
84 state_machine_(scheduler_settings
),
85 inside_process_scheduled_actions_(false),
86 inside_action_(SchedulerStateMachine::ACTION_NONE
),
88 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
89 "Scheduler::Scheduler",
93 DCHECK(!state_machine_
.BeginFrameNeeded());
94 if (settings_
.main_frame_before_activation_enabled
) {
95 DCHECK(settings_
.main_frame_before_draw_enabled
);
98 begin_retro_frame_closure_
=
99 base::Bind(&Scheduler::BeginRetroFrame
, weak_factory_
.GetWeakPtr());
100 begin_unthrottled_frame_closure_
=
101 base::Bind(&Scheduler::BeginUnthrottledFrame
, weak_factory_
.GetWeakPtr());
102 begin_impl_frame_deadline_closure_
= base::Bind(
103 &Scheduler::OnBeginImplFrameDeadline
, weak_factory_
.GetWeakPtr());
104 poll_for_draw_triggers_closure_
= base::Bind(
105 &Scheduler::PollForAnticipatedDrawTriggers
, weak_factory_
.GetWeakPtr());
106 advance_commit_state_closure_
= base::Bind(
107 &Scheduler::PollToAdvanceCommitState
, weak_factory_
.GetWeakPtr());
109 if (!settings_
.begin_frame_scheduling_enabled
) {
110 SetupSyntheticBeginFrames();
114 Scheduler::~Scheduler() {
115 if (synthetic_begin_frame_source_
) {
116 synthetic_begin_frame_source_
->SetNeedsBeginFrame(false,
117 &begin_retro_frame_args_
);
121 void Scheduler::SetupSyntheticBeginFrames() {
122 scoped_refptr
<DelayBasedTimeSource
> time_source
;
123 if (gfx::FrameTime::TimestampsAreHighRes()) {
124 time_source
= DelayBasedTimeSourceHighRes::Create(VSyncInterval(),
128 DelayBasedTimeSource::Create(VSyncInterval(), task_runner_
.get());
130 DCHECK(!synthetic_begin_frame_source_
);
131 synthetic_begin_frame_source_
.reset(
132 new SyntheticBeginFrameSource(this, time_source
));
135 base::TimeTicks
Scheduler::Now() const {
136 return gfx::FrameTime::Now();
139 void Scheduler::CommitVSyncParameters(base::TimeTicks timebase
,
140 base::TimeDelta interval
) {
141 // TODO(brianderson): We should not be receiving 0 intervals.
142 if (interval
== base::TimeDelta())
143 interval
= BeginFrameArgs::DefaultInterval();
144 vsync_interval_
= interval
;
145 if (!settings_
.begin_frame_scheduling_enabled
)
146 synthetic_begin_frame_source_
->CommitVSyncParameters(timebase
, interval
);
149 void Scheduler::SetEstimatedParentDrawTime(base::TimeDelta draw_time
) {
150 DCHECK_GE(draw_time
.ToInternalValue(), 0);
151 estimated_parent_draw_time_
= draw_time
;
154 void Scheduler::SetCanStart() {
155 state_machine_
.SetCanStart();
156 ProcessScheduledActions();
159 void Scheduler::SetVisible(bool visible
) {
160 state_machine_
.SetVisible(visible
);
161 ProcessScheduledActions();
164 void Scheduler::SetCanDraw(bool can_draw
) {
165 state_machine_
.SetCanDraw(can_draw
);
166 ProcessScheduledActions();
169 void Scheduler::NotifyReadyToActivate() {
170 state_machine_
.NotifyReadyToActivate();
171 ProcessScheduledActions();
174 void Scheduler::SetNeedsCommit() {
175 state_machine_
.SetNeedsCommit();
176 ProcessScheduledActions();
179 void Scheduler::SetNeedsRedraw() {
180 state_machine_
.SetNeedsRedraw();
181 ProcessScheduledActions();
184 void Scheduler::SetNeedsAnimate() {
185 state_machine_
.SetNeedsAnimate();
186 ProcessScheduledActions();
189 void Scheduler::SetNeedsManageTiles() {
190 DCHECK(!IsInsideAction(SchedulerStateMachine::ACTION_MANAGE_TILES
));
191 state_machine_
.SetNeedsManageTiles();
192 ProcessScheduledActions();
195 void Scheduler::SetMaxSwapsPending(int max
) {
196 state_machine_
.SetMaxSwapsPending(max
);
199 void Scheduler::DidSwapBuffers() {
200 state_machine_
.DidSwapBuffers();
202 // There is no need to call ProcessScheduledActions here because
203 // swapping should not trigger any new actions.
204 if (!inside_process_scheduled_actions_
) {
205 DCHECK_EQ(state_machine_
.NextAction(), SchedulerStateMachine::ACTION_NONE
);
209 void Scheduler::SetSwapUsedIncompleteTile(bool used_incomplete_tile
) {
210 state_machine_
.SetSwapUsedIncompleteTile(used_incomplete_tile
);
211 ProcessScheduledActions();
214 void Scheduler::DidSwapBuffersComplete() {
215 state_machine_
.DidSwapBuffersComplete();
216 ProcessScheduledActions();
219 void Scheduler::SetImplLatencyTakesPriority(bool impl_latency_takes_priority
) {
220 state_machine_
.SetImplLatencyTakesPriority(impl_latency_takes_priority
);
221 ProcessScheduledActions();
224 void Scheduler::NotifyReadyToCommit() {
225 TRACE_EVENT0("cc", "Scheduler::NotifyReadyToCommit");
226 state_machine_
.NotifyReadyToCommit();
227 ProcessScheduledActions();
230 void Scheduler::BeginMainFrameAborted(bool did_handle
) {
231 TRACE_EVENT0("cc", "Scheduler::BeginMainFrameAborted");
232 state_machine_
.BeginMainFrameAborted(did_handle
);
233 ProcessScheduledActions();
236 void Scheduler::DidManageTiles() {
237 state_machine_
.DidManageTiles();
240 void Scheduler::DidLoseOutputSurface() {
241 TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface");
242 state_machine_
.DidLoseOutputSurface();
243 last_set_needs_begin_frame_
= false;
244 if (!settings_
.begin_frame_scheduling_enabled
) {
245 synthetic_begin_frame_source_
->SetNeedsBeginFrame(false,
246 &begin_retro_frame_args_
);
248 begin_retro_frame_args_
.clear();
249 ProcessScheduledActions();
252 void Scheduler::DidCreateAndInitializeOutputSurface() {
253 TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface");
254 DCHECK(!last_set_needs_begin_frame_
);
255 DCHECK(begin_impl_frame_deadline_task_
.IsCancelled());
256 state_machine_
.DidCreateAndInitializeOutputSurface();
257 ProcessScheduledActions();
260 void Scheduler::NotifyBeginMainFrameStarted() {
261 TRACE_EVENT0("cc", "Scheduler::NotifyBeginMainFrameStarted");
262 state_machine_
.NotifyBeginMainFrameStarted();
265 base::TimeTicks
Scheduler::AnticipatedDrawTime() const {
266 if (!last_set_needs_begin_frame_
||
267 begin_impl_frame_args_
.interval
<= base::TimeDelta())
268 return base::TimeTicks();
270 base::TimeTicks now
= Now();
271 base::TimeTicks timebase
= std::max(begin_impl_frame_args_
.frame_time
,
272 begin_impl_frame_args_
.deadline
);
273 int64 intervals
= 1 + ((now
- timebase
) / begin_impl_frame_args_
.interval
);
274 return timebase
+ (begin_impl_frame_args_
.interval
* intervals
);
277 base::TimeTicks
Scheduler::LastBeginImplFrameTime() {
278 return begin_impl_frame_args_
.frame_time
;
281 void Scheduler::SetupNextBeginFrameIfNeeded() {
282 if (!task_runner_
.get())
285 bool needs_begin_frame
= state_machine_
.BeginFrameNeeded();
287 if (settings_
.throttle_frame_production
) {
288 SetupNextBeginFrameWhenVSyncThrottlingEnabled(needs_begin_frame
);
290 SetupNextBeginFrameWhenVSyncThrottlingDisabled(needs_begin_frame
);
292 SetupPollingMechanisms(needs_begin_frame
);
295 // When we are throttling frame production, we request BeginFrames
296 // from the OutputSurface.
297 void Scheduler::SetupNextBeginFrameWhenVSyncThrottlingEnabled(
298 bool needs_begin_frame
) {
299 bool at_end_of_deadline
=
300 state_machine_
.begin_impl_frame_state() ==
301 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE
;
303 bool should_call_set_needs_begin_frame
=
304 // Always request the BeginFrame immediately if it wasn't needed before.
305 (needs_begin_frame
&& !last_set_needs_begin_frame_
) ||
306 // Only stop requesting BeginFrames after a deadline.
307 (!needs_begin_frame
&& last_set_needs_begin_frame_
&& at_end_of_deadline
);
309 if (should_call_set_needs_begin_frame
) {
310 if (settings_
.begin_frame_scheduling_enabled
) {
311 client_
->SetNeedsBeginFrame(needs_begin_frame
);
313 synthetic_begin_frame_source_
->SetNeedsBeginFrame(
314 needs_begin_frame
, &begin_retro_frame_args_
);
316 last_set_needs_begin_frame_
= needs_begin_frame
;
319 PostBeginRetroFrameIfNeeded();
322 // When we aren't throttling frame production, we initiate a BeginFrame
323 // as soon as one is needed.
324 void Scheduler::SetupNextBeginFrameWhenVSyncThrottlingDisabled(
325 bool needs_begin_frame
) {
326 last_set_needs_begin_frame_
= needs_begin_frame
;
328 if (!needs_begin_frame
|| begin_unthrottled_frame_posted_
)
331 if (state_machine_
.begin_impl_frame_state() !=
332 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
&&
333 state_machine_
.begin_impl_frame_state() !=
334 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE
) {
338 begin_unthrottled_frame_posted_
= true;
339 task_runner_
->PostTask(FROM_HERE
, begin_unthrottled_frame_closure_
);
342 // BeginUnthrottledFrame is used when we aren't throttling frame production.
343 // This will usually be because VSync is disabled.
344 void Scheduler::BeginUnthrottledFrame() {
345 DCHECK(!settings_
.throttle_frame_production
);
346 DCHECK(begin_retro_frame_args_
.empty());
348 base::TimeTicks now
= Now();
349 base::TimeTicks deadline
= now
+ vsync_interval_
;
351 BeginFrameArgs begin_frame_args
=
352 BeginFrameArgs::Create(now
, deadline
, vsync_interval_
);
353 BeginImplFrame(begin_frame_args
);
355 begin_unthrottled_frame_posted_
= false;
358 // We may need to poll when we can't rely on BeginFrame to advance certain
359 // state or to avoid deadlock.
360 void Scheduler::SetupPollingMechanisms(bool needs_begin_frame
) {
361 bool needs_advance_commit_state_timer
= false;
362 // Setup PollForAnticipatedDrawTriggers if we need to monitor state but
363 // aren't expecting any more BeginFrames. This should only be needed by
364 // the synchronous compositor when BeginFrameNeeded is false.
365 if (state_machine_
.ShouldPollForAnticipatedDrawTriggers()) {
366 DCHECK(!state_machine_
.SupportsProactiveBeginFrame());
367 DCHECK(!needs_begin_frame
);
368 if (poll_for_draw_triggers_task_
.IsCancelled()) {
369 poll_for_draw_triggers_task_
.Reset(poll_for_draw_triggers_closure_
);
370 base::TimeDelta delay
= begin_impl_frame_args_
.IsValid()
371 ? begin_impl_frame_args_
.interval
372 : BeginFrameArgs::DefaultInterval();
373 task_runner_
->PostDelayedTask(
374 FROM_HERE
, poll_for_draw_triggers_task_
.callback(), delay
);
377 poll_for_draw_triggers_task_
.Cancel();
379 // At this point we'd prefer to advance through the commit flow by
380 // drawing a frame, however it's possible that the frame rate controller
381 // will not give us a BeginFrame until the commit completes. See
382 // crbug.com/317430 for an example of a swap ack being held on commit. Thus
383 // we set a repeating timer to poll on ProcessScheduledActions until we
384 // successfully reach BeginFrame. Synchronous compositor does not use
385 // frame rate controller or have the circular wait in the bug.
386 if (IsBeginMainFrameSentOrStarted() &&
387 !settings_
.using_synchronous_renderer_compositor
) {
388 needs_advance_commit_state_timer
= true;
392 if (needs_advance_commit_state_timer
) {
393 if (advance_commit_state_task_
.IsCancelled() &&
394 begin_impl_frame_args_
.IsValid()) {
395 // Since we'd rather get a BeginImplFrame by the normal mechanism, we
396 // set the interval to twice the interval from the previous frame.
397 advance_commit_state_task_
.Reset(advance_commit_state_closure_
);
398 task_runner_
->PostDelayedTask(FROM_HERE
,
399 advance_commit_state_task_
.callback(),
400 begin_impl_frame_args_
.interval
* 2);
403 advance_commit_state_task_
.Cancel();
407 // BeginFrame is the mechanism that tells us that now is a good time to start
408 // making a frame. Usually this means that user input for the frame is complete.
409 // If the scheduler is busy, we queue the BeginFrame to be handled later as
410 // a BeginRetroFrame.
411 void Scheduler::BeginFrame(const BeginFrameArgs
& args
) {
412 TRACE_EVENT1("cc", "Scheduler::BeginFrame", "args", args
.AsValue());
413 DCHECK(settings_
.throttle_frame_production
);
415 BeginFrameArgs
adjusted_args(args
);
416 adjusted_args
.deadline
-= EstimatedParentDrawTime();
418 bool should_defer_begin_frame
;
419 if (settings_
.using_synchronous_renderer_compositor
) {
420 should_defer_begin_frame
= false;
422 should_defer_begin_frame
=
423 !begin_retro_frame_args_
.empty() || begin_retro_frame_posted_
||
424 !last_set_needs_begin_frame_
||
425 (state_machine_
.begin_impl_frame_state() !=
426 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
);
429 if (should_defer_begin_frame
) {
430 begin_retro_frame_args_
.push_back(adjusted_args
);
431 TRACE_EVENT_INSTANT0(
432 "cc", "Scheduler::BeginFrame deferred", TRACE_EVENT_SCOPE_THREAD
);
436 BeginImplFrame(adjusted_args
);
439 // BeginRetroFrame is called for BeginFrames that we've deferred because
440 // the scheduler was in the middle of processing a previous BeginFrame.
441 void Scheduler::BeginRetroFrame() {
442 TRACE_EVENT0("cc", "Scheduler::BeginRetroFrame");
443 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
444 DCHECK(begin_retro_frame_posted_
);
445 begin_retro_frame_posted_
= false;
447 // If there aren't any retroactive BeginFrames, then we've lost the
448 // OutputSurface and should abort.
449 if (begin_retro_frame_args_
.empty())
452 // Discard expired BeginRetroFrames
453 // Today, we should always end up with at most one un-expired BeginRetroFrame
454 // because deadlines will not be greater than the next frame time. We don't
455 // DCHECK though because some systems don't always have monotonic timestamps.
456 // TODO(brianderson): In the future, long deadlines could result in us not
457 // draining the queue if we don't catch up. If we consistently can't catch
458 // up, our fallback should be to lower our frame rate.
459 base::TimeTicks now
= Now();
460 base::TimeDelta draw_duration_estimate
= client_
->DrawDurationEstimate();
461 while (!begin_retro_frame_args_
.empty()) {
462 base::TimeTicks adjusted_deadline
= AdjustedBeginImplFrameDeadline(
463 begin_retro_frame_args_
.front(), draw_duration_estimate
);
464 if (now
<= adjusted_deadline
)
467 TRACE_EVENT_INSTANT2("cc",
468 "Scheduler::BeginRetroFrame discarding",
469 TRACE_EVENT_SCOPE_THREAD
,
471 (adjusted_deadline
- now
).InMicroseconds(),
473 begin_retro_frame_args_
.front().AsValue());
474 begin_retro_frame_args_
.pop_front();
477 if (begin_retro_frame_args_
.empty()) {
478 DCHECK(settings_
.throttle_frame_production
);
479 TRACE_EVENT_INSTANT0("cc",
480 "Scheduler::BeginRetroFrames all expired",
481 TRACE_EVENT_SCOPE_THREAD
);
483 BeginImplFrame(begin_retro_frame_args_
.front());
484 begin_retro_frame_args_
.pop_front();
488 // There could be a race between the posted BeginRetroFrame and a new
489 // BeginFrame arriving via the normal mechanism. Scheduler::BeginFrame
490 // will check if there is a pending BeginRetroFrame to ensure we handle
491 // BeginFrames in FIFO order.
492 void Scheduler::PostBeginRetroFrameIfNeeded() {
493 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
494 "Scheduler::PostBeginRetroFrameIfNeeded",
497 if (!last_set_needs_begin_frame_
)
500 if (begin_retro_frame_args_
.empty() || begin_retro_frame_posted_
)
503 // begin_retro_frame_args_ should always be empty for the
504 // synchronous compositor.
505 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
507 if (state_machine_
.begin_impl_frame_state() !=
508 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
)
511 begin_retro_frame_posted_
= true;
512 task_runner_
->PostTask(FROM_HERE
, begin_retro_frame_closure_
);
515 // BeginImplFrame starts a compositor frame that will wait up until a deadline
516 // for a BeginMainFrame+activation to complete before it times out and draws
517 // any asynchronous animation and scroll/pinch updates.
518 void Scheduler::BeginImplFrame(const BeginFrameArgs
& args
) {
519 TRACE_EVENT1("cc", "Scheduler::BeginImplFrame", "args", args
.AsValue());
520 DCHECK_EQ(state_machine_
.begin_impl_frame_state(),
521 SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE
);
522 DCHECK(state_machine_
.HasInitializedOutputSurface());
524 advance_commit_state_task_
.Cancel();
526 base::TimeDelta draw_duration_estimate
= client_
->DrawDurationEstimate();
527 begin_impl_frame_args_
= args
;
528 begin_impl_frame_args_
.deadline
-= draw_duration_estimate
;
530 if (!state_machine_
.impl_latency_takes_priority() &&
531 state_machine_
.MainThreadIsInHighLatencyMode() &&
532 CanCommitAndActivateBeforeDeadline()) {
533 state_machine_
.SetSkipNextBeginMainFrameToReduceLatency();
536 client_
->WillBeginImplFrame(begin_impl_frame_args_
);
537 state_machine_
.OnBeginImplFrame(begin_impl_frame_args_
);
538 devtools_instrumentation::DidBeginFrame(layer_tree_host_id_
);
540 ProcessScheduledActions();
542 state_machine_
.OnBeginImplFrameDeadlinePending();
543 ScheduleBeginImplFrameDeadline(
544 AdjustedBeginImplFrameDeadline(args
, draw_duration_estimate
));
547 base::TimeTicks
Scheduler::AdjustedBeginImplFrameDeadline(
548 const BeginFrameArgs
& args
,
549 base::TimeDelta draw_duration_estimate
) const {
550 if (settings_
.using_synchronous_renderer_compositor
) {
551 // The synchronous compositor needs to draw right away.
552 return base::TimeTicks();
553 } else if (state_machine_
.ShouldTriggerBeginImplFrameDeadlineEarly()) {
554 // We are ready to draw a new active tree immediately.
555 return base::TimeTicks();
556 } else if (state_machine_
.needs_redraw()) {
557 // We have an animation or fast input path on the impl thread that wants
558 // to draw, so don't wait too long for a new active tree.
559 return args
.deadline
- draw_duration_estimate
;
561 // The impl thread doesn't have anything it wants to draw and we are just
562 // waiting for a new active tree, so post the deadline for the next
563 // expected BeginImplFrame start. This allows us to draw immediately when
564 // there is a new active tree, instead of waiting for the next
566 // TODO(brianderson): Handle long deadlines (that are past the next frame's
567 // frame time) properly instead of using this hack.
568 return args
.frame_time
+ args
.interval
;
572 void Scheduler::ScheduleBeginImplFrameDeadline(base::TimeTicks deadline
) {
574 "cc", "Scheduler::ScheduleBeginImplFrameDeadline", "deadline", deadline
);
575 if (settings_
.using_synchronous_renderer_compositor
) {
576 // The synchronous renderer compositor has to make its GL calls
578 // TODO(brianderson): Have the OutputSurface initiate the deadline tasks
579 // so the sychronous renderer compositor can take advantage of splitting
580 // up the BeginImplFrame and deadline as well.
581 OnBeginImplFrameDeadline();
584 begin_impl_frame_deadline_task_
.Cancel();
585 begin_impl_frame_deadline_task_
.Reset(begin_impl_frame_deadline_closure_
);
587 base::TimeDelta delta
= deadline
- Now();
588 if (delta
<= base::TimeDelta())
589 delta
= base::TimeDelta();
590 task_runner_
->PostDelayedTask(
591 FROM_HERE
, begin_impl_frame_deadline_task_
.callback(), delta
);
594 void Scheduler::OnBeginImplFrameDeadline() {
595 TRACE_EVENT0("cc", "Scheduler::OnBeginImplFrameDeadline");
596 begin_impl_frame_deadline_task_
.Cancel();
598 // We split the deadline actions up into two phases so the state machine
599 // has a chance to trigger actions that should occur durring and after
600 // the deadline separately. For example:
601 // * Sending the BeginMainFrame will not occur after the deadline in
602 // order to wait for more user-input before starting the next commit.
603 // * Creating a new OuputSurface will not occur during the deadline in
604 // order to allow the state machine to "settle" first.
605 state_machine_
.OnBeginImplFrameDeadline();
606 ProcessScheduledActions();
607 state_machine_
.OnBeginImplFrameIdle();
608 ProcessScheduledActions();
610 client_
->DidBeginImplFrameDeadline();
613 void Scheduler::PollForAnticipatedDrawTriggers() {
614 TRACE_EVENT0("cc", "Scheduler::PollForAnticipatedDrawTriggers");
615 poll_for_draw_triggers_task_
.Cancel();
616 state_machine_
.DidEnterPollForAnticipatedDrawTriggers();
617 ProcessScheduledActions();
618 state_machine_
.DidLeavePollForAnticipatedDrawTriggers();
621 void Scheduler::PollToAdvanceCommitState() {
622 TRACE_EVENT0("cc", "Scheduler::PollToAdvanceCommitState");
623 advance_commit_state_task_
.Cancel();
624 ProcessScheduledActions();
627 void Scheduler::DrawAndSwapIfPossible() {
628 DrawResult result
= client_
->ScheduledActionDrawAndSwapIfPossible();
629 state_machine_
.DidDrawIfPossibleCompleted(result
);
632 void Scheduler::ProcessScheduledActions() {
633 // We do not allow ProcessScheduledActions to be recursive.
634 // The top-level call will iteratively execute the next action for us anyway.
635 if (inside_process_scheduled_actions_
)
638 base::AutoReset
<bool> mark_inside(&inside_process_scheduled_actions_
, true);
640 SchedulerStateMachine::Action action
;
642 action
= state_machine_
.NextAction();
643 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
644 "SchedulerStateMachine",
647 VLOG(2) << "Scheduler::ProcessScheduledActions: "
648 << SchedulerStateMachine::ActionToString(action
) << " "
649 << state_machine_
.GetStatesForDebugging();
650 state_machine_
.UpdateState(action
);
651 base::AutoReset
<SchedulerStateMachine::Action
>
652 mark_inside_action(&inside_action_
, action
);
654 case SchedulerStateMachine::ACTION_NONE
:
656 case SchedulerStateMachine::ACTION_ANIMATE
:
657 client_
->ScheduledActionAnimate();
659 case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME
:
660 client_
->ScheduledActionSendBeginMainFrame();
662 case SchedulerStateMachine::ACTION_COMMIT
:
663 client_
->ScheduledActionCommit();
665 case SchedulerStateMachine::ACTION_UPDATE_VISIBLE_TILES
:
666 client_
->ScheduledActionUpdateVisibleTiles();
668 case SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE
:
669 client_
->ScheduledActionActivateSyncTree();
671 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE
:
672 DrawAndSwapIfPossible();
674 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED
:
675 client_
->ScheduledActionDrawAndSwapForced();
677 case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT
:
678 // No action is actually performed, but this allows the state machine to
679 // advance out of its waiting to draw state without actually drawing.
681 case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION
:
682 client_
->ScheduledActionBeginOutputSurfaceCreation();
684 case SchedulerStateMachine::ACTION_MANAGE_TILES
:
685 client_
->ScheduledActionManageTiles();
688 } while (action
!= SchedulerStateMachine::ACTION_NONE
);
690 SetupNextBeginFrameIfNeeded();
691 client_
->DidAnticipatedDrawTimeChange(AnticipatedDrawTime());
693 if (state_machine_
.ShouldTriggerBeginImplFrameDeadlineEarly()) {
694 DCHECK(!settings_
.using_synchronous_renderer_compositor
);
695 ScheduleBeginImplFrameDeadline(base::TimeTicks());
699 bool Scheduler::WillDrawIfNeeded() const {
700 return !state_machine_
.PendingDrawsShouldBeAborted();
703 scoped_refptr
<base::debug::ConvertableToTraceFormat
> Scheduler::AsValue()
705 scoped_refptr
<base::debug::TracedValue
> state
=
706 new base::debug::TracedValue();
707 AsValueInto(state
.get());
711 void Scheduler::AsValueInto(base::debug::TracedValue
* state
) const {
712 state
->BeginDictionary("state_machine");
713 state_machine_
.AsValueInto(state
, Now());
714 state
->EndDictionary();
715 if (synthetic_begin_frame_source_
) {
716 state
->BeginDictionary("synthetic_begin_frame_source_");
717 synthetic_begin_frame_source_
->AsValueInto(state
);
718 state
->EndDictionary();
721 state
->BeginDictionary("scheduler_state");
722 state
->SetDouble("time_until_anticipated_draw_time_ms",
723 (AnticipatedDrawTime() - Now()).InMillisecondsF());
724 state
->SetDouble("vsync_interval_ms", vsync_interval_
.InMillisecondsF());
725 state
->SetDouble("estimated_parent_draw_time_ms",
726 estimated_parent_draw_time_
.InMillisecondsF());
727 state
->SetBoolean("last_set_needs_begin_frame_", last_set_needs_begin_frame_
);
728 state
->SetBoolean("begin_unthrottled_frame_posted_",
729 begin_unthrottled_frame_posted_
);
730 state
->SetBoolean("begin_retro_frame_posted_", begin_retro_frame_posted_
);
731 state
->SetInteger("begin_retro_frame_args_", begin_retro_frame_args_
.size());
732 state
->SetBoolean("begin_impl_frame_deadline_task_",
733 !begin_impl_frame_deadline_task_
.IsCancelled());
734 state
->SetBoolean("poll_for_draw_triggers_task_",
735 !poll_for_draw_triggers_task_
.IsCancelled());
736 state
->SetBoolean("advance_commit_state_task_",
737 !advance_commit_state_task_
.IsCancelled());
738 state
->BeginDictionary("begin_impl_frame_args");
739 begin_impl_frame_args_
.AsValueInto(state
);
740 state
->EndDictionary();
742 state
->EndDictionary();
744 state
->BeginDictionary("client_state");
745 state
->SetDouble("draw_duration_estimate_ms",
746 client_
->DrawDurationEstimate().InMillisecondsF());
748 "begin_main_frame_to_commit_duration_estimate_ms",
749 client_
->BeginMainFrameToCommitDurationEstimate().InMillisecondsF());
751 "commit_to_activate_duration_estimate_ms",
752 client_
->CommitToActivateDurationEstimate().InMillisecondsF());
753 state
->EndDictionary();
756 bool Scheduler::CanCommitAndActivateBeforeDeadline() const {
757 // Check if the main thread computation and commit can be finished before the
758 // impl thread's deadline.
759 base::TimeTicks estimated_draw_time
=
760 begin_impl_frame_args_
.frame_time
+
761 client_
->BeginMainFrameToCommitDurationEstimate() +
762 client_
->CommitToActivateDurationEstimate();
765 TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
766 "CanCommitAndActivateBeforeDeadline",
767 "time_left_after_drawing_ms",
768 (begin_impl_frame_args_
.deadline
- estimated_draw_time
).InMillisecondsF(),
772 return estimated_draw_time
< begin_impl_frame_args_
.deadline
;
775 bool Scheduler::IsBeginMainFrameSentOrStarted() const {
776 return (state_machine_
.commit_state() ==
777 SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT
||
778 state_machine_
.commit_state() ==
779 SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED
);