• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "cc/scheduler/scheduler.h"
6 
7 #include <algorithm>
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"
17 
18 namespace cc {
19 
SyntheticBeginFrameSource(Scheduler * scheduler,scoped_refptr<DelayBasedTimeSource> time_source)20 Scheduler::SyntheticBeginFrameSource::SyntheticBeginFrameSource(
21     Scheduler* scheduler,
22     scoped_refptr<DelayBasedTimeSource> time_source)
23     : scheduler_(scheduler), time_source_(time_source) {
24   time_source_->SetClient(this);
25 }
26 
~SyntheticBeginFrameSource()27 Scheduler::SyntheticBeginFrameSource::~SyntheticBeginFrameSource() {
28 }
29 
CommitVSyncParameters(base::TimeTicks timebase,base::TimeDelta interval)30 void Scheduler::SyntheticBeginFrameSource::CommitVSyncParameters(
31     base::TimeTicks timebase,
32     base::TimeDelta interval) {
33   time_source_->SetTimebaseAndInterval(timebase, interval);
34 }
35 
SetNeedsBeginFrame(bool needs_begin_frame,std::deque<BeginFrameArgs> * begin_retro_frame_args)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));
45   }
46 }
47 
IsActive() const48 bool Scheduler::SyntheticBeginFrameSource::IsActive() const {
49   return time_source_->Active();
50 }
51 
OnTimerTick()52 void Scheduler::SyntheticBeginFrameSource::OnTimerTick() {
53   BeginFrameArgs begin_frame_args(
54       CreateSyntheticBeginFrameArgs(time_source_->LastTickTime()));
55   scheduler_->BeginFrame(begin_frame_args);
56 }
57 
AsValueInto(base::debug::TracedValue * state) const58 void Scheduler::SyntheticBeginFrameSource::AsValueInto(
59     base::debug::TracedValue* state) const {
60   time_source_->AsValueInto(state);
61 }
62 
63 BeginFrameArgs
CreateSyntheticBeginFrameArgs(base::TimeTicks frame_time)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());
69 }
70 
Scheduler(SchedulerClient * client,const SchedulerSettings & scheduler_settings,int layer_tree_host_id,const scoped_refptr<base::SingleThreadTaskRunner> & task_runner)71 Scheduler::Scheduler(
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),
77       client_(client),
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),
87       weak_factory_(this) {
88   TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
89                "Scheduler::Scheduler",
90                "settings",
91                settings_.AsValue());
92   DCHECK(client_);
93   DCHECK(!state_machine_.BeginFrameNeeded());
94   if (settings_.main_frame_before_activation_enabled) {
95     DCHECK(settings_.main_frame_before_draw_enabled);
96   }
97 
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());
108 
109   if (!settings_.begin_frame_scheduling_enabled) {
110     SetupSyntheticBeginFrames();
111   }
112 }
113 
~Scheduler()114 Scheduler::~Scheduler() {
115   if (synthetic_begin_frame_source_) {
116     synthetic_begin_frame_source_->SetNeedsBeginFrame(false,
117                                                       &begin_retro_frame_args_);
118   }
119 }
120 
SetupSyntheticBeginFrames()121 void Scheduler::SetupSyntheticBeginFrames() {
122   scoped_refptr<DelayBasedTimeSource> time_source;
123   if (gfx::FrameTime::TimestampsAreHighRes()) {
124     time_source = DelayBasedTimeSourceHighRes::Create(VSyncInterval(),
125                                                       task_runner_.get());
126   } else {
127     time_source =
128         DelayBasedTimeSource::Create(VSyncInterval(), task_runner_.get());
129   }
130   DCHECK(!synthetic_begin_frame_source_);
131   synthetic_begin_frame_source_.reset(
132       new SyntheticBeginFrameSource(this, time_source));
133 }
134 
Now() const135 base::TimeTicks Scheduler::Now() const {
136   return gfx::FrameTime::Now();
137 }
138 
CommitVSyncParameters(base::TimeTicks timebase,base::TimeDelta interval)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);
147 }
148 
SetEstimatedParentDrawTime(base::TimeDelta draw_time)149 void Scheduler::SetEstimatedParentDrawTime(base::TimeDelta draw_time) {
150   DCHECK_GE(draw_time.ToInternalValue(), 0);
151   estimated_parent_draw_time_ = draw_time;
152 }
153 
SetCanStart()154 void Scheduler::SetCanStart() {
155   state_machine_.SetCanStart();
156   ProcessScheduledActions();
157 }
158 
SetVisible(bool visible)159 void Scheduler::SetVisible(bool visible) {
160   state_machine_.SetVisible(visible);
161   ProcessScheduledActions();
162 }
163 
SetCanDraw(bool can_draw)164 void Scheduler::SetCanDraw(bool can_draw) {
165   state_machine_.SetCanDraw(can_draw);
166   ProcessScheduledActions();
167 }
168 
NotifyReadyToActivate()169 void Scheduler::NotifyReadyToActivate() {
170   state_machine_.NotifyReadyToActivate();
171   ProcessScheduledActions();
172 }
173 
SetNeedsCommit()174 void Scheduler::SetNeedsCommit() {
175   state_machine_.SetNeedsCommit();
176   ProcessScheduledActions();
177 }
178 
SetNeedsRedraw()179 void Scheduler::SetNeedsRedraw() {
180   state_machine_.SetNeedsRedraw();
181   ProcessScheduledActions();
182 }
183 
SetNeedsAnimate()184 void Scheduler::SetNeedsAnimate() {
185   state_machine_.SetNeedsAnimate();
186   ProcessScheduledActions();
187 }
188 
SetNeedsManageTiles()189 void Scheduler::SetNeedsManageTiles() {
190   DCHECK(!IsInsideAction(SchedulerStateMachine::ACTION_MANAGE_TILES));
191   state_machine_.SetNeedsManageTiles();
192   ProcessScheduledActions();
193 }
194 
SetMaxSwapsPending(int max)195 void Scheduler::SetMaxSwapsPending(int max) {
196   state_machine_.SetMaxSwapsPending(max);
197 }
198 
DidSwapBuffers()199 void Scheduler::DidSwapBuffers() {
200   state_machine_.DidSwapBuffers();
201 
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);
206   }
207 }
208 
SetSwapUsedIncompleteTile(bool used_incomplete_tile)209 void Scheduler::SetSwapUsedIncompleteTile(bool used_incomplete_tile) {
210   state_machine_.SetSwapUsedIncompleteTile(used_incomplete_tile);
211   ProcessScheduledActions();
212 }
213 
DidSwapBuffersComplete()214 void Scheduler::DidSwapBuffersComplete() {
215   state_machine_.DidSwapBuffersComplete();
216   ProcessScheduledActions();
217 }
218 
SetImplLatencyTakesPriority(bool impl_latency_takes_priority)219 void Scheduler::SetImplLatencyTakesPriority(bool impl_latency_takes_priority) {
220   state_machine_.SetImplLatencyTakesPriority(impl_latency_takes_priority);
221   ProcessScheduledActions();
222 }
223 
NotifyReadyToCommit()224 void Scheduler::NotifyReadyToCommit() {
225   TRACE_EVENT0("cc", "Scheduler::NotifyReadyToCommit");
226   state_machine_.NotifyReadyToCommit();
227   ProcessScheduledActions();
228 }
229 
BeginMainFrameAborted(bool did_handle)230 void Scheduler::BeginMainFrameAborted(bool did_handle) {
231   TRACE_EVENT0("cc", "Scheduler::BeginMainFrameAborted");
232   state_machine_.BeginMainFrameAborted(did_handle);
233   ProcessScheduledActions();
234 }
235 
DidManageTiles()236 void Scheduler::DidManageTiles() {
237   state_machine_.DidManageTiles();
238 }
239 
DidLoseOutputSurface()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_);
247   }
248   begin_retro_frame_args_.clear();
249   ProcessScheduledActions();
250 }
251 
DidCreateAndInitializeOutputSurface()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();
258 }
259 
NotifyBeginMainFrameStarted()260 void Scheduler::NotifyBeginMainFrameStarted() {
261   TRACE_EVENT0("cc", "Scheduler::NotifyBeginMainFrameStarted");
262   state_machine_.NotifyBeginMainFrameStarted();
263 }
264 
AnticipatedDrawTime() const265 base::TimeTicks Scheduler::AnticipatedDrawTime() const {
266   if (!last_set_needs_begin_frame_ ||
267       begin_impl_frame_args_.interval <= base::TimeDelta())
268     return base::TimeTicks();
269 
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);
275 }
276 
LastBeginImplFrameTime()277 base::TimeTicks Scheduler::LastBeginImplFrameTime() {
278   return begin_impl_frame_args_.frame_time;
279 }
280 
SetupNextBeginFrameIfNeeded()281 void Scheduler::SetupNextBeginFrameIfNeeded() {
282   if (!task_runner_.get())
283     return;
284 
285   bool needs_begin_frame = state_machine_.BeginFrameNeeded();
286 
287   if (settings_.throttle_frame_production) {
288     SetupNextBeginFrameWhenVSyncThrottlingEnabled(needs_begin_frame);
289   } else {
290     SetupNextBeginFrameWhenVSyncThrottlingDisabled(needs_begin_frame);
291   }
292   SetupPollingMechanisms(needs_begin_frame);
293 }
294 
295 // When we are throttling frame production, we request BeginFrames
296 // from the OutputSurface.
SetupNextBeginFrameWhenVSyncThrottlingEnabled(bool needs_begin_frame)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;
302 
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);
308 
309   if (should_call_set_needs_begin_frame) {
310     if (settings_.begin_frame_scheduling_enabled) {
311       client_->SetNeedsBeginFrame(needs_begin_frame);
312     } else {
313       synthetic_begin_frame_source_->SetNeedsBeginFrame(
314           needs_begin_frame, &begin_retro_frame_args_);
315     }
316     last_set_needs_begin_frame_ = needs_begin_frame;
317   }
318 
319   PostBeginRetroFrameIfNeeded();
320 }
321 
322 // When we aren't throttling frame production, we initiate a BeginFrame
323 // as soon as one is needed.
SetupNextBeginFrameWhenVSyncThrottlingDisabled(bool needs_begin_frame)324 void Scheduler::SetupNextBeginFrameWhenVSyncThrottlingDisabled(
325     bool needs_begin_frame) {
326   last_set_needs_begin_frame_ = needs_begin_frame;
327 
328   if (!needs_begin_frame || begin_unthrottled_frame_posted_)
329     return;
330 
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) {
335     return;
336   }
337 
338   begin_unthrottled_frame_posted_ = true;
339   task_runner_->PostTask(FROM_HERE, begin_unthrottled_frame_closure_);
340 }
341 
342 // BeginUnthrottledFrame is used when we aren't throttling frame production.
343 // This will usually be because VSync is disabled.
BeginUnthrottledFrame()344 void Scheduler::BeginUnthrottledFrame() {
345   DCHECK(!settings_.throttle_frame_production);
346   DCHECK(begin_retro_frame_args_.empty());
347 
348   base::TimeTicks now = Now();
349   base::TimeTicks deadline = now + vsync_interval_;
350 
351   BeginFrameArgs begin_frame_args =
352       BeginFrameArgs::Create(now, deadline, vsync_interval_);
353   BeginImplFrame(begin_frame_args);
354 
355   begin_unthrottled_frame_posted_ = false;
356 }
357 
358 // We may need to poll when we can't rely on BeginFrame to advance certain
359 // state or to avoid deadlock.
SetupPollingMechanisms(bool needs_begin_frame)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);
375     }
376   } else {
377     poll_for_draw_triggers_task_.Cancel();
378 
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;
389     }
390   }
391 
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);
401     }
402   } else {
403     advance_commit_state_task_.Cancel();
404   }
405 }
406 
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.
BeginFrame(const BeginFrameArgs & args)411 void Scheduler::BeginFrame(const BeginFrameArgs& args) {
412   TRACE_EVENT1("cc", "Scheduler::BeginFrame", "args", args.AsValue());
413   DCHECK(settings_.throttle_frame_production);
414 
415   BeginFrameArgs adjusted_args(args);
416   adjusted_args.deadline -= EstimatedParentDrawTime();
417 
418   bool should_defer_begin_frame;
419   if (settings_.using_synchronous_renderer_compositor) {
420     should_defer_begin_frame = false;
421   } else {
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);
427   }
428 
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);
433     return;
434   }
435 
436   BeginImplFrame(adjusted_args);
437 }
438 
439 // BeginRetroFrame is called for BeginFrames that we've deferred because
440 // the scheduler was in the middle of processing a previous BeginFrame.
BeginRetroFrame()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;
446 
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())
450     return;
451 
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)
465       break;
466 
467     TRACE_EVENT_INSTANT2("cc",
468                          "Scheduler::BeginRetroFrame discarding",
469                          TRACE_EVENT_SCOPE_THREAD,
470                          "deadline - now",
471                          (adjusted_deadline - now).InMicroseconds(),
472                          "BeginFrameArgs",
473                          begin_retro_frame_args_.front().AsValue());
474     begin_retro_frame_args_.pop_front();
475   }
476 
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);
482   } else {
483     BeginImplFrame(begin_retro_frame_args_.front());
484     begin_retro_frame_args_.pop_front();
485   }
486 }
487 
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.
PostBeginRetroFrameIfNeeded()492 void Scheduler::PostBeginRetroFrameIfNeeded() {
493   TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
494                "Scheduler::PostBeginRetroFrameIfNeeded",
495                "state",
496                AsValue());
497   if (!last_set_needs_begin_frame_)
498     return;
499 
500   if (begin_retro_frame_args_.empty() || begin_retro_frame_posted_)
501     return;
502 
503   // begin_retro_frame_args_ should always be empty for the
504   // synchronous compositor.
505   DCHECK(!settings_.using_synchronous_renderer_compositor);
506 
507   if (state_machine_.begin_impl_frame_state() !=
508       SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE)
509     return;
510 
511   begin_retro_frame_posted_ = true;
512   task_runner_->PostTask(FROM_HERE, begin_retro_frame_closure_);
513 }
514 
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.
BeginImplFrame(const BeginFrameArgs & args)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());
523 
524   advance_commit_state_task_.Cancel();
525 
526   base::TimeDelta draw_duration_estimate = client_->DrawDurationEstimate();
527   begin_impl_frame_args_ = args;
528   begin_impl_frame_args_.deadline -= draw_duration_estimate;
529 
530   if (!state_machine_.impl_latency_takes_priority() &&
531       state_machine_.MainThreadIsInHighLatencyMode() &&
532       CanCommitAndActivateBeforeDeadline()) {
533     state_machine_.SetSkipNextBeginMainFrameToReduceLatency();
534   }
535 
536   client_->WillBeginImplFrame(begin_impl_frame_args_);
537   state_machine_.OnBeginImplFrame(begin_impl_frame_args_);
538   devtools_instrumentation::DidBeginFrame(layer_tree_host_id_);
539 
540   ProcessScheduledActions();
541 
542   state_machine_.OnBeginImplFrameDeadlinePending();
543   ScheduleBeginImplFrameDeadline(
544       AdjustedBeginImplFrameDeadline(args, draw_duration_estimate));
545 }
546 
AdjustedBeginImplFrameDeadline(const BeginFrameArgs & args,base::TimeDelta draw_duration_estimate) const547 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;
560   } else {
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
565     // BeginImplFrame.
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;
569   }
570 }
571 
ScheduleBeginImplFrameDeadline(base::TimeTicks deadline)572 void Scheduler::ScheduleBeginImplFrameDeadline(base::TimeTicks deadline) {
573   TRACE_EVENT1(
574       "cc", "Scheduler::ScheduleBeginImplFrameDeadline", "deadline", deadline);
575   if (settings_.using_synchronous_renderer_compositor) {
576     // The synchronous renderer compositor has to make its GL calls
577     // within this call.
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();
582     return;
583   }
584   begin_impl_frame_deadline_task_.Cancel();
585   begin_impl_frame_deadline_task_.Reset(begin_impl_frame_deadline_closure_);
586 
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);
592 }
593 
OnBeginImplFrameDeadline()594 void Scheduler::OnBeginImplFrameDeadline() {
595   TRACE_EVENT0("cc", "Scheduler::OnBeginImplFrameDeadline");
596   begin_impl_frame_deadline_task_.Cancel();
597 
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();
609 
610   client_->DidBeginImplFrameDeadline();
611 }
612 
PollForAnticipatedDrawTriggers()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();
619 }
620 
PollToAdvanceCommitState()621 void Scheduler::PollToAdvanceCommitState() {
622   TRACE_EVENT0("cc", "Scheduler::PollToAdvanceCommitState");
623   advance_commit_state_task_.Cancel();
624   ProcessScheduledActions();
625 }
626 
DrawAndSwapIfPossible()627 void Scheduler::DrawAndSwapIfPossible() {
628   DrawResult result = client_->ScheduledActionDrawAndSwapIfPossible();
629   state_machine_.DidDrawIfPossibleCompleted(result);
630 }
631 
ProcessScheduledActions()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_)
636     return;
637 
638   base::AutoReset<bool> mark_inside(&inside_process_scheduled_actions_, true);
639 
640   SchedulerStateMachine::Action action;
641   do {
642     action = state_machine_.NextAction();
643     TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
644                  "SchedulerStateMachine",
645                  "state",
646                  AsValue());
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);
653     switch (action) {
654       case SchedulerStateMachine::ACTION_NONE:
655         break;
656       case SchedulerStateMachine::ACTION_ANIMATE:
657         client_->ScheduledActionAnimate();
658         break;
659       case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME:
660         client_->ScheduledActionSendBeginMainFrame();
661         break;
662       case SchedulerStateMachine::ACTION_COMMIT:
663         client_->ScheduledActionCommit();
664         break;
665       case SchedulerStateMachine::ACTION_UPDATE_VISIBLE_TILES:
666         client_->ScheduledActionUpdateVisibleTiles();
667         break;
668       case SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE:
669         client_->ScheduledActionActivateSyncTree();
670         break;
671       case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE:
672         DrawAndSwapIfPossible();
673         break;
674       case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED:
675         client_->ScheduledActionDrawAndSwapForced();
676         break;
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.
680         break;
681       case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
682         client_->ScheduledActionBeginOutputSurfaceCreation();
683         break;
684       case SchedulerStateMachine::ACTION_MANAGE_TILES:
685         client_->ScheduledActionManageTiles();
686         break;
687     }
688   } while (action != SchedulerStateMachine::ACTION_NONE);
689 
690   SetupNextBeginFrameIfNeeded();
691   client_->DidAnticipatedDrawTimeChange(AnticipatedDrawTime());
692 
693   if (state_machine_.ShouldTriggerBeginImplFrameDeadlineEarly()) {
694     DCHECK(!settings_.using_synchronous_renderer_compositor);
695     ScheduleBeginImplFrameDeadline(base::TimeTicks());
696   }
697 }
698 
WillDrawIfNeeded() const699 bool Scheduler::WillDrawIfNeeded() const {
700   return !state_machine_.PendingDrawsShouldBeAborted();
701 }
702 
AsValue() const703 scoped_refptr<base::debug::ConvertableToTraceFormat> Scheduler::AsValue()
704     const {
705   scoped_refptr<base::debug::TracedValue> state =
706       new base::debug::TracedValue();
707   AsValueInto(state.get());
708   return state;
709 }
710 
AsValueInto(base::debug::TracedValue * state) const711 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();
719   }
720 
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();
741 
742   state->EndDictionary();
743 
744   state->BeginDictionary("client_state");
745   state->SetDouble("draw_duration_estimate_ms",
746                    client_->DrawDurationEstimate().InMillisecondsF());
747   state->SetDouble(
748       "begin_main_frame_to_commit_duration_estimate_ms",
749       client_->BeginMainFrameToCommitDurationEstimate().InMillisecondsF());
750   state->SetDouble(
751       "commit_to_activate_duration_estimate_ms",
752       client_->CommitToActivateDurationEstimate().InMillisecondsF());
753   state->EndDictionary();
754 }
755 
CanCommitAndActivateBeforeDeadline() const756 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();
763 
764   TRACE_EVENT2(
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(),
769       "state",
770       AsValue());
771 
772   return estimated_draw_time < begin_impl_frame_args_.deadline;
773 }
774 
IsBeginMainFrameSentOrStarted() const775 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);
780 }
781 
782 }  // namespace cc
783