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
18namespace cc {
19
20Scheduler::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
27Scheduler::SyntheticBeginFrameSource::~SyntheticBeginFrameSource() {
28}
29
30void Scheduler::SyntheticBeginFrameSource::CommitVSyncParameters(
31    base::TimeTicks timebase,
32    base::TimeDelta interval) {
33  time_source_->SetTimebaseAndInterval(timebase, interval);
34}
35
36void 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
48bool Scheduler::SyntheticBeginFrameSource::IsActive() const {
49  return time_source_->Active();
50}
51
52void Scheduler::SyntheticBeginFrameSource::OnTimerTick() {
53  BeginFrameArgs begin_frame_args(
54      CreateSyntheticBeginFrameArgs(time_source_->LastTickTime()));
55  scheduler_->BeginFrame(begin_frame_args);
56}
57
58void Scheduler::SyntheticBeginFrameSource::AsValueInto(
59    base::debug::TracedValue* state) const {
60  time_source_->AsValueInto(state);
61}
62
63BeginFrameArgs
64Scheduler::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
71Scheduler::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
114Scheduler::~Scheduler() {
115  if (synthetic_begin_frame_source_) {
116    synthetic_begin_frame_source_->SetNeedsBeginFrame(false,
117                                                      &begin_retro_frame_args_);
118  }
119}
120
121void 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
135base::TimeTicks Scheduler::Now() const {
136  return gfx::FrameTime::Now();
137}
138
139void 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
149void Scheduler::SetEstimatedParentDrawTime(base::TimeDelta draw_time) {
150  DCHECK_GE(draw_time.ToInternalValue(), 0);
151  estimated_parent_draw_time_ = draw_time;
152}
153
154void Scheduler::SetCanStart() {
155  state_machine_.SetCanStart();
156  ProcessScheduledActions();
157}
158
159void Scheduler::SetVisible(bool visible) {
160  state_machine_.SetVisible(visible);
161  ProcessScheduledActions();
162}
163
164void Scheduler::SetCanDraw(bool can_draw) {
165  state_machine_.SetCanDraw(can_draw);
166  ProcessScheduledActions();
167}
168
169void Scheduler::NotifyReadyToActivate() {
170  state_machine_.NotifyReadyToActivate();
171  ProcessScheduledActions();
172}
173
174void Scheduler::SetNeedsCommit() {
175  state_machine_.SetNeedsCommit();
176  ProcessScheduledActions();
177}
178
179void Scheduler::SetNeedsRedraw() {
180  state_machine_.SetNeedsRedraw();
181  ProcessScheduledActions();
182}
183
184void Scheduler::SetNeedsAnimate() {
185  state_machine_.SetNeedsAnimate();
186  ProcessScheduledActions();
187}
188
189void Scheduler::SetNeedsManageTiles() {
190  DCHECK(!IsInsideAction(SchedulerStateMachine::ACTION_MANAGE_TILES));
191  state_machine_.SetNeedsManageTiles();
192  ProcessScheduledActions();
193}
194
195void Scheduler::SetMaxSwapsPending(int max) {
196  state_machine_.SetMaxSwapsPending(max);
197}
198
199void 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
209void Scheduler::SetSwapUsedIncompleteTile(bool used_incomplete_tile) {
210  state_machine_.SetSwapUsedIncompleteTile(used_incomplete_tile);
211  ProcessScheduledActions();
212}
213
214void Scheduler::DidSwapBuffersComplete() {
215  state_machine_.DidSwapBuffersComplete();
216  ProcessScheduledActions();
217}
218
219void Scheduler::SetImplLatencyTakesPriority(bool impl_latency_takes_priority) {
220  state_machine_.SetImplLatencyTakesPriority(impl_latency_takes_priority);
221  ProcessScheduledActions();
222}
223
224void Scheduler::NotifyReadyToCommit() {
225  TRACE_EVENT0("cc", "Scheduler::NotifyReadyToCommit");
226  state_machine_.NotifyReadyToCommit();
227  ProcessScheduledActions();
228}
229
230void Scheduler::BeginMainFrameAborted(bool did_handle) {
231  TRACE_EVENT0("cc", "Scheduler::BeginMainFrameAborted");
232  state_machine_.BeginMainFrameAborted(did_handle);
233  ProcessScheduledActions();
234}
235
236void Scheduler::DidManageTiles() {
237  state_machine_.DidManageTiles();
238}
239
240void 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
252void 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
260void Scheduler::NotifyBeginMainFrameStarted() {
261  TRACE_EVENT0("cc", "Scheduler::NotifyBeginMainFrameStarted");
262  state_machine_.NotifyBeginMainFrameStarted();
263}
264
265base::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
277base::TimeTicks Scheduler::LastBeginImplFrameTime() {
278  return begin_impl_frame_args_.frame_time;
279}
280
281void 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.
297void 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.
324void 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.
344void 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.
360void 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.
411void 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.
441void 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.
492void 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.
518void 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
547base::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
572void 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
594void 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
613void 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
621void Scheduler::PollToAdvanceCommitState() {
622  TRACE_EVENT0("cc", "Scheduler::PollToAdvanceCommitState");
623  advance_commit_state_task_.Cancel();
624  ProcessScheduledActions();
625}
626
627void Scheduler::DrawAndSwapIfPossible() {
628  DrawResult result = client_->ScheduledActionDrawAndSwapIfPossible();
629  state_machine_.DidDrawIfPossibleCompleted(result);
630}
631
632void 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
699bool Scheduler::WillDrawIfNeeded() const {
700  return !state_machine_.PendingDrawsShouldBeAborted();
701}
702
703scoped_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
711void 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
756bool 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
775bool 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