Home | History | Annotate | Download | only in scheduler
      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/logging.h"
     11 #include "base/single_thread_task_runner.h"
     12 #include "cc/debug/devtools_instrumentation.h"
     13 #include "cc/debug/traced_value.h"
     14 #include "cc/scheduler/delay_based_time_source.h"
     15 #include "ui/gfx/frame_time.h"
     16 
     17 namespace cc {
     18 
     19 Scheduler::SyntheticBeginFrameSource::SyntheticBeginFrameSource(
     20     Scheduler* scheduler,
     21     base::SingleThreadTaskRunner* task_runner)
     22     : scheduler_(scheduler) {
     23   if (gfx::FrameTime::TimestampsAreHighRes()) {
     24     time_source_ = DelayBasedTimeSourceHighRes::Create(
     25         scheduler_->VSyncInterval(), task_runner);
     26   } else {
     27     time_source_ = DelayBasedTimeSource::Create(scheduler_->VSyncInterval(),
     28                                                 task_runner);
     29   }
     30   time_source_->SetClient(this);
     31 }
     32 
     33 Scheduler::SyntheticBeginFrameSource::~SyntheticBeginFrameSource() {
     34 }
     35 
     36 void Scheduler::SyntheticBeginFrameSource::CommitVSyncParameters(
     37     base::TimeTicks timebase,
     38     base::TimeDelta interval) {
     39   time_source_->SetTimebaseAndInterval(timebase, interval);
     40 }
     41 
     42 void Scheduler::SyntheticBeginFrameSource::SetNeedsBeginFrame(
     43     bool needs_begin_frame,
     44     std::deque<BeginFrameArgs>* begin_retro_frame_args) {
     45   DCHECK(begin_retro_frame_args);
     46   base::TimeTicks missed_tick_time =
     47       time_source_->SetActive(needs_begin_frame);
     48   if (!missed_tick_time.is_null()) {
     49     begin_retro_frame_args->push_back(
     50         CreateSyntheticBeginFrameArgs(missed_tick_time));
     51   }
     52 }
     53 
     54 bool Scheduler::SyntheticBeginFrameSource::IsActive() const {
     55   return time_source_->Active();
     56 }
     57 
     58 void Scheduler::SyntheticBeginFrameSource::OnTimerTick() {
     59   BeginFrameArgs begin_frame_args(
     60       CreateSyntheticBeginFrameArgs(time_source_->LastTickTime()));
     61   scheduler_->BeginFrame(begin_frame_args);
     62 }
     63 
     64 scoped_ptr<base::Value> Scheduler::SyntheticBeginFrameSource::AsValue() const {
     65   return time_source_->AsValue();
     66 }
     67 
     68 BeginFrameArgs
     69 Scheduler::SyntheticBeginFrameSource::CreateSyntheticBeginFrameArgs(
     70     base::TimeTicks frame_time) {
     71   base::TimeTicks deadline = time_source_->NextTickTime();
     72   return BeginFrameArgs::Create(
     73       frame_time, deadline, scheduler_->VSyncInterval());
     74 }
     75 
     76 Scheduler::Scheduler(
     77     SchedulerClient* client,
     78     const SchedulerSettings& scheduler_settings,
     79     int layer_tree_host_id,
     80     const scoped_refptr<base::SingleThreadTaskRunner>& impl_task_runner)
     81     : settings_(scheduler_settings),
     82       client_(client),
     83       layer_tree_host_id_(layer_tree_host_id),
     84       impl_task_runner_(impl_task_runner),
     85       vsync_interval_(BeginFrameArgs::DefaultInterval()),
     86       last_set_needs_begin_frame_(false),
     87       begin_unthrottled_frame_posted_(false),
     88       begin_retro_frame_posted_(false),
     89       state_machine_(scheduler_settings),
     90       inside_process_scheduled_actions_(false),
     91       inside_action_(SchedulerStateMachine::ACTION_NONE),
     92       weak_factory_(this) {
     93   TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
     94                "Scheduler::Scheduler",
     95                "settings",
     96                ToTrace(settings_));
     97   DCHECK(client_);
     98   DCHECK(!state_machine_.BeginFrameNeeded());
     99   if (settings_.main_frame_before_activation_enabled) {
    100     DCHECK(settings_.main_frame_before_draw_enabled);
    101   }
    102 
    103   begin_retro_frame_closure_ =
    104       base::Bind(&Scheduler::BeginRetroFrame, weak_factory_.GetWeakPtr());
    105   begin_unthrottled_frame_closure_ =
    106       base::Bind(&Scheduler::BeginUnthrottledFrame, weak_factory_.GetWeakPtr());
    107   begin_impl_frame_deadline_closure_ = base::Bind(
    108       &Scheduler::OnBeginImplFrameDeadline, weak_factory_.GetWeakPtr());
    109   poll_for_draw_triggers_closure_ = base::Bind(
    110       &Scheduler::PollForAnticipatedDrawTriggers, weak_factory_.GetWeakPtr());
    111   advance_commit_state_closure_ = base::Bind(
    112       &Scheduler::PollToAdvanceCommitState, weak_factory_.GetWeakPtr());
    113 
    114   if (!settings_.begin_frame_scheduling_enabled) {
    115     SetupSyntheticBeginFrames();
    116   }
    117 }
    118 
    119 Scheduler::~Scheduler() {
    120   if (synthetic_begin_frame_source_) {
    121     synthetic_begin_frame_source_->SetNeedsBeginFrame(false,
    122                                                       &begin_retro_frame_args_);
    123   }
    124 }
    125 
    126 void Scheduler::SetupSyntheticBeginFrames() {
    127   DCHECK(!synthetic_begin_frame_source_);
    128   synthetic_begin_frame_source_.reset(
    129       new SyntheticBeginFrameSource(this, impl_task_runner_.get()));
    130 }
    131 
    132 void Scheduler::CommitVSyncParameters(base::TimeTicks timebase,
    133                                       base::TimeDelta interval) {
    134   // TODO(brianderson): We should not be receiving 0 intervals.
    135   if (interval == base::TimeDelta())
    136     interval = BeginFrameArgs::DefaultInterval();
    137   vsync_interval_ = interval;
    138   if (!settings_.begin_frame_scheduling_enabled)
    139     synthetic_begin_frame_source_->CommitVSyncParameters(timebase, interval);
    140 }
    141 
    142 void Scheduler::SetEstimatedParentDrawTime(base::TimeDelta draw_time) {
    143   DCHECK_GE(draw_time.ToInternalValue(), 0);
    144   estimated_parent_draw_time_ = draw_time;
    145 }
    146 
    147 void Scheduler::SetCanStart() {
    148   state_machine_.SetCanStart();
    149   ProcessScheduledActions();
    150 }
    151 
    152 void Scheduler::SetVisible(bool visible) {
    153   state_machine_.SetVisible(visible);
    154   ProcessScheduledActions();
    155 }
    156 
    157 void Scheduler::SetCanDraw(bool can_draw) {
    158   state_machine_.SetCanDraw(can_draw);
    159   ProcessScheduledActions();
    160 }
    161 
    162 void Scheduler::NotifyReadyToActivate() {
    163   state_machine_.NotifyReadyToActivate();
    164   ProcessScheduledActions();
    165 }
    166 
    167 void Scheduler::SetNeedsCommit() {
    168   state_machine_.SetNeedsCommit();
    169   ProcessScheduledActions();
    170 }
    171 
    172 void Scheduler::SetNeedsRedraw() {
    173   state_machine_.SetNeedsRedraw();
    174   ProcessScheduledActions();
    175 }
    176 
    177 void Scheduler::SetNeedsAnimate() {
    178   state_machine_.SetNeedsAnimate();
    179   ProcessScheduledActions();
    180 }
    181 
    182 void Scheduler::SetNeedsManageTiles() {
    183   DCHECK(!IsInsideAction(SchedulerStateMachine::ACTION_MANAGE_TILES));
    184   state_machine_.SetNeedsManageTiles();
    185   ProcessScheduledActions();
    186 }
    187 
    188 void Scheduler::SetMaxSwapsPending(int max) {
    189   state_machine_.SetMaxSwapsPending(max);
    190 }
    191 
    192 void Scheduler::DidSwapBuffers() {
    193   state_machine_.DidSwapBuffers();
    194 
    195   // There is no need to call ProcessScheduledActions here because
    196   // swapping should not trigger any new actions.
    197   if (!inside_process_scheduled_actions_) {
    198     DCHECK_EQ(state_machine_.NextAction(), SchedulerStateMachine::ACTION_NONE);
    199   }
    200 }
    201 
    202 void Scheduler::SetSwapUsedIncompleteTile(bool used_incomplete_tile) {
    203   state_machine_.SetSwapUsedIncompleteTile(used_incomplete_tile);
    204   ProcessScheduledActions();
    205 }
    206 
    207 void Scheduler::DidSwapBuffersComplete() {
    208   state_machine_.DidSwapBuffersComplete();
    209   ProcessScheduledActions();
    210 }
    211 
    212 void Scheduler::SetSmoothnessTakesPriority(bool smoothness_takes_priority) {
    213   state_machine_.SetSmoothnessTakesPriority(smoothness_takes_priority);
    214   ProcessScheduledActions();
    215 }
    216 
    217 void Scheduler::NotifyReadyToCommit() {
    218   TRACE_EVENT0("cc", "Scheduler::NotifyReadyToCommit");
    219   state_machine_.NotifyReadyToCommit();
    220   ProcessScheduledActions();
    221 }
    222 
    223 void Scheduler::BeginMainFrameAborted(bool did_handle) {
    224   TRACE_EVENT0("cc", "Scheduler::BeginMainFrameAborted");
    225   state_machine_.BeginMainFrameAborted(did_handle);
    226   ProcessScheduledActions();
    227 }
    228 
    229 void Scheduler::DidManageTiles() {
    230   state_machine_.DidManageTiles();
    231 }
    232 
    233 void Scheduler::DidLoseOutputSurface() {
    234   TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface");
    235   state_machine_.DidLoseOutputSurface();
    236   last_set_needs_begin_frame_ = false;
    237   if (!settings_.begin_frame_scheduling_enabled) {
    238     synthetic_begin_frame_source_->SetNeedsBeginFrame(false,
    239                                                       &begin_retro_frame_args_);
    240   }
    241   begin_retro_frame_args_.clear();
    242   ProcessScheduledActions();
    243 }
    244 
    245 void Scheduler::DidCreateAndInitializeOutputSurface() {
    246   TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface");
    247   DCHECK(!last_set_needs_begin_frame_);
    248   DCHECK(begin_impl_frame_deadline_task_.IsCancelled());
    249   state_machine_.DidCreateAndInitializeOutputSurface();
    250   ProcessScheduledActions();
    251 }
    252 
    253 void Scheduler::NotifyBeginMainFrameStarted() {
    254   TRACE_EVENT0("cc", "Scheduler::NotifyBeginMainFrameStarted");
    255   state_machine_.NotifyBeginMainFrameStarted();
    256 }
    257 
    258 base::TimeTicks Scheduler::AnticipatedDrawTime() const {
    259   if (!last_set_needs_begin_frame_ ||
    260       begin_impl_frame_args_.interval <= base::TimeDelta())
    261     return base::TimeTicks();
    262 
    263   base::TimeTicks now = gfx::FrameTime::Now();
    264   base::TimeTicks timebase = std::max(begin_impl_frame_args_.frame_time,
    265                                       begin_impl_frame_args_.deadline);
    266   int64 intervals = 1 + ((now - timebase) / begin_impl_frame_args_.interval);
    267   return timebase + (begin_impl_frame_args_.interval * intervals);
    268 }
    269 
    270 base::TimeTicks Scheduler::LastBeginImplFrameTime() {
    271   return begin_impl_frame_args_.frame_time;
    272 }
    273 
    274 void Scheduler::SetupNextBeginFrameIfNeeded() {
    275   bool needs_begin_frame = state_machine_.BeginFrameNeeded();
    276 
    277   if (settings_.throttle_frame_production) {
    278     SetupNextBeginFrameWhenVSyncThrottlingEnabled(needs_begin_frame);
    279   } else {
    280     SetupNextBeginFrameWhenVSyncThrottlingDisabled(needs_begin_frame);
    281   }
    282   SetupPollingMechanisms(needs_begin_frame);
    283 }
    284 
    285 // When we are throttling frame production, we request BeginFrames
    286 // from the OutputSurface.
    287 void Scheduler::SetupNextBeginFrameWhenVSyncThrottlingEnabled(
    288     bool needs_begin_frame) {
    289   bool at_end_of_deadline =
    290       state_machine_.begin_impl_frame_state() ==
    291           SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE;
    292 
    293   bool should_call_set_needs_begin_frame =
    294       // Always request the BeginFrame immediately if it wasn't needed before.
    295       (needs_begin_frame && !last_set_needs_begin_frame_) ||
    296       // Only stop requesting BeginFrames after a deadline.
    297       (!needs_begin_frame && last_set_needs_begin_frame_ && at_end_of_deadline);
    298 
    299   if (should_call_set_needs_begin_frame) {
    300     if (settings_.begin_frame_scheduling_enabled) {
    301       client_->SetNeedsBeginFrame(needs_begin_frame);
    302     } else {
    303       synthetic_begin_frame_source_->SetNeedsBeginFrame(
    304           needs_begin_frame, &begin_retro_frame_args_);
    305     }
    306     last_set_needs_begin_frame_ = needs_begin_frame;
    307   }
    308 
    309   PostBeginRetroFrameIfNeeded();
    310 }
    311 
    312 // When we aren't throttling frame production, we initiate a BeginFrame
    313 // as soon as one is needed.
    314 void Scheduler::SetupNextBeginFrameWhenVSyncThrottlingDisabled(
    315     bool needs_begin_frame) {
    316   last_set_needs_begin_frame_ = needs_begin_frame;
    317 
    318   if (!needs_begin_frame || begin_unthrottled_frame_posted_)
    319     return;
    320 
    321   if (state_machine_.begin_impl_frame_state() !=
    322           SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE &&
    323       state_machine_.begin_impl_frame_state() !=
    324           SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE) {
    325     return;
    326   }
    327 
    328   begin_unthrottled_frame_posted_ = true;
    329   impl_task_runner_->PostTask(FROM_HERE, begin_unthrottled_frame_closure_);
    330 }
    331 
    332 // BeginUnthrottledFrame is used when we aren't throttling frame production.
    333 // This will usually be because VSync is disabled.
    334 void Scheduler::BeginUnthrottledFrame() {
    335   DCHECK(!settings_.throttle_frame_production);
    336   DCHECK(begin_retro_frame_args_.empty());
    337 
    338   base::TimeTicks now = gfx::FrameTime::Now();
    339   base::TimeTicks deadline = now + vsync_interval_;
    340 
    341   BeginFrameArgs begin_frame_args =
    342       BeginFrameArgs::Create(now, deadline, vsync_interval_);
    343   BeginImplFrame(begin_frame_args);
    344 
    345   begin_unthrottled_frame_posted_ = false;
    346 }
    347 
    348 // We may need to poll when we can't rely on BeginFrame to advance certain
    349 // state or to avoid deadlock.
    350 void Scheduler::SetupPollingMechanisms(bool needs_begin_frame) {
    351   bool needs_advance_commit_state_timer = false;
    352   // Setup PollForAnticipatedDrawTriggers if we need to monitor state but
    353   // aren't expecting any more BeginFrames. This should only be needed by
    354   // the synchronous compositor when BeginFrameNeeded is false.
    355   if (state_machine_.ShouldPollForAnticipatedDrawTriggers()) {
    356     DCHECK(!state_machine_.SupportsProactiveBeginFrame());
    357     DCHECK(!needs_begin_frame);
    358     if (poll_for_draw_triggers_task_.IsCancelled()) {
    359       poll_for_draw_triggers_task_.Reset(poll_for_draw_triggers_closure_);
    360       base::TimeDelta delay = begin_impl_frame_args_.IsValid()
    361                                   ? begin_impl_frame_args_.interval
    362                                   : BeginFrameArgs::DefaultInterval();
    363       impl_task_runner_->PostDelayedTask(
    364           FROM_HERE, poll_for_draw_triggers_task_.callback(), delay);
    365     }
    366   } else {
    367     poll_for_draw_triggers_task_.Cancel();
    368 
    369     // At this point we'd prefer to advance through the commit flow by
    370     // drawing a frame, however it's possible that the frame rate controller
    371     // will not give us a BeginFrame until the commit completes.  See
    372     // crbug.com/317430 for an example of a swap ack being held on commit. Thus
    373     // we set a repeating timer to poll on ProcessScheduledActions until we
    374     // successfully reach BeginFrame. Synchronous compositor does not use
    375     // frame rate controller or have the circular wait in the bug.
    376     if (IsBeginMainFrameSentOrStarted() &&
    377         !settings_.using_synchronous_renderer_compositor) {
    378       needs_advance_commit_state_timer = true;
    379     }
    380   }
    381 
    382   if (needs_advance_commit_state_timer) {
    383     if (advance_commit_state_task_.IsCancelled() &&
    384         begin_impl_frame_args_.IsValid()) {
    385       // Since we'd rather get a BeginImplFrame by the normal mechanism, we
    386       // set the interval to twice the interval from the previous frame.
    387       advance_commit_state_task_.Reset(advance_commit_state_closure_);
    388       impl_task_runner_->PostDelayedTask(FROM_HERE,
    389                                          advance_commit_state_task_.callback(),
    390                                          begin_impl_frame_args_.interval * 2);
    391     }
    392   } else {
    393     advance_commit_state_task_.Cancel();
    394   }
    395 }
    396 
    397 // BeginFrame is the mechanism that tells us that now is a good time to start
    398 // making a frame. Usually this means that user input for the frame is complete.
    399 // If the scheduler is busy, we queue the BeginFrame to be handled later as
    400 // a BeginRetroFrame.
    401 void Scheduler::BeginFrame(const BeginFrameArgs& args) {
    402   TRACE_EVENT1("cc", "Scheduler::BeginFrame", "args", ToTrace(args));
    403   DCHECK(settings_.throttle_frame_production);
    404 
    405   BeginFrameArgs adjusted_args(args);
    406   adjusted_args.deadline -= EstimatedParentDrawTime();
    407 
    408   bool should_defer_begin_frame;
    409   if (settings_.using_synchronous_renderer_compositor) {
    410     should_defer_begin_frame = false;
    411   } else {
    412     should_defer_begin_frame =
    413         !begin_retro_frame_args_.empty() || begin_retro_frame_posted_ ||
    414         !last_set_needs_begin_frame_ ||
    415         (state_machine_.begin_impl_frame_state() !=
    416          SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE);
    417   }
    418 
    419   if (should_defer_begin_frame) {
    420     begin_retro_frame_args_.push_back(adjusted_args);
    421     TRACE_EVENT_INSTANT0(
    422         "cc", "Scheduler::BeginFrame deferred", TRACE_EVENT_SCOPE_THREAD);
    423     return;
    424   }
    425 
    426   BeginImplFrame(adjusted_args);
    427 }
    428 
    429 // BeginRetroFrame is called for BeginFrames that we've deferred because
    430 // the scheduler was in the middle of processing a previous BeginFrame.
    431 void Scheduler::BeginRetroFrame() {
    432   TRACE_EVENT0("cc", "Scheduler::BeginRetroFrame");
    433   DCHECK(!settings_.using_synchronous_renderer_compositor);
    434   DCHECK(begin_retro_frame_posted_);
    435   begin_retro_frame_posted_ = false;
    436 
    437   // If there aren't any retroactive BeginFrames, then we've lost the
    438   // OutputSurface and should abort.
    439   if (begin_retro_frame_args_.empty())
    440     return;
    441 
    442   // Discard expired BeginRetroFrames
    443   // Today, we should always end up with at most one un-expired BeginRetroFrame
    444   // because deadlines will not be greater than the next frame time. We don't
    445   // DCHECK though because some systems don't always have monotonic timestamps.
    446   // TODO(brianderson): In the future, long deadlines could result in us not
    447   // draining the queue if we don't catch up. If we consistently can't catch
    448   // up, our fallback should be to lower our frame rate.
    449   base::TimeTicks now = gfx::FrameTime::Now();
    450   base::TimeDelta draw_duration_estimate = client_->DrawDurationEstimate();
    451   while (!begin_retro_frame_args_.empty() &&
    452          now > AdjustedBeginImplFrameDeadline(begin_retro_frame_args_.front(),
    453                                               draw_duration_estimate)) {
    454     TRACE_EVENT1("cc",
    455                  "Scheduler::BeginRetroFrame discarding",
    456                  "frame_time",
    457                  begin_retro_frame_args_.front().frame_time);
    458     begin_retro_frame_args_.pop_front();
    459   }
    460 
    461   if (begin_retro_frame_args_.empty()) {
    462     DCHECK(settings_.throttle_frame_production);
    463     TRACE_EVENT_INSTANT0("cc",
    464                          "Scheduler::BeginRetroFrames all expired",
    465                          TRACE_EVENT_SCOPE_THREAD);
    466   } else {
    467     BeginImplFrame(begin_retro_frame_args_.front());
    468     begin_retro_frame_args_.pop_front();
    469   }
    470 }
    471 
    472 // There could be a race between the posted BeginRetroFrame and a new
    473 // BeginFrame arriving via the normal mechanism. Scheduler::BeginFrame
    474 // will check if there is a pending BeginRetroFrame to ensure we handle
    475 // BeginFrames in FIFO order.
    476 void Scheduler::PostBeginRetroFrameIfNeeded() {
    477   if (!last_set_needs_begin_frame_)
    478     return;
    479 
    480   if (begin_retro_frame_args_.empty() || begin_retro_frame_posted_)
    481     return;
    482 
    483   // begin_retro_frame_args_ should always be empty for the
    484   // synchronous compositor.
    485   DCHECK(!settings_.using_synchronous_renderer_compositor);
    486 
    487   if (state_machine_.begin_impl_frame_state() !=
    488       SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE)
    489     return;
    490 
    491   begin_retro_frame_posted_ = true;
    492   impl_task_runner_->PostTask(FROM_HERE, begin_retro_frame_closure_);
    493 }
    494 
    495 // BeginImplFrame starts a compositor frame that will wait up until a deadline
    496 // for a BeginMainFrame+activation to complete before it times out and draws
    497 // any asynchronous animation and scroll/pinch updates.
    498 void Scheduler::BeginImplFrame(const BeginFrameArgs& args) {
    499   TRACE_EVENT1("cc", "Scheduler::BeginImplFrame", "args", ToTrace(args));
    500   DCHECK(state_machine_.begin_impl_frame_state() ==
    501          SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE);
    502   DCHECK(state_machine_.HasInitializedOutputSurface());
    503 
    504   advance_commit_state_task_.Cancel();
    505 
    506   base::TimeDelta draw_duration_estimate = client_->DrawDurationEstimate();
    507   begin_impl_frame_args_ = args;
    508   begin_impl_frame_args_.deadline -= draw_duration_estimate;
    509 
    510   if (!state_machine_.smoothness_takes_priority() &&
    511       state_machine_.MainThreadIsInHighLatencyMode() &&
    512       CanCommitAndActivateBeforeDeadline()) {
    513     state_machine_.SetSkipNextBeginMainFrameToReduceLatency();
    514   }
    515 
    516   client_->WillBeginImplFrame(begin_impl_frame_args_);
    517   state_machine_.OnBeginImplFrame(begin_impl_frame_args_);
    518   devtools_instrumentation::DidBeginFrame(layer_tree_host_id_);
    519 
    520   ProcessScheduledActions();
    521 
    522   state_machine_.OnBeginImplFrameDeadlinePending();
    523   ScheduleBeginImplFrameDeadline(
    524       AdjustedBeginImplFrameDeadline(args, draw_duration_estimate));
    525 }
    526 
    527 base::TimeTicks Scheduler::AdjustedBeginImplFrameDeadline(
    528     const BeginFrameArgs& args,
    529     base::TimeDelta draw_duration_estimate) const {
    530   if (settings_.using_synchronous_renderer_compositor) {
    531     // The synchronous compositor needs to draw right away.
    532     return base::TimeTicks();
    533   } else if (state_machine_.ShouldTriggerBeginImplFrameDeadlineEarly()) {
    534     // We are ready to draw a new active tree immediately.
    535     return base::TimeTicks();
    536   } else if (state_machine_.needs_redraw()) {
    537     // We have an animation or fast input path on the impl thread that wants
    538     // to draw, so don't wait too long for a new active tree.
    539     return args.deadline - draw_duration_estimate;
    540   } else {
    541     // The impl thread doesn't have anything it wants to draw and we are just
    542     // waiting for a new active tree, so post the deadline for the next
    543     // expected BeginImplFrame start. This allows us to draw immediately when
    544     // there is a new active tree, instead of waiting for the next
    545     // BeginImplFrame.
    546     // TODO(brianderson): Handle long deadlines (that are past the next frame's
    547     // frame time) properly instead of using this hack.
    548     return args.frame_time + args.interval;
    549   }
    550 }
    551 
    552 void Scheduler::ScheduleBeginImplFrameDeadline(base::TimeTicks deadline) {
    553   if (settings_.using_synchronous_renderer_compositor) {
    554     // The synchronous renderer compositor has to make its GL calls
    555     // within this call.
    556     // TODO(brianderson): Have the OutputSurface initiate the deadline tasks
    557     // so the sychronous renderer compositor can take advantage of splitting
    558     // up the BeginImplFrame and deadline as well.
    559     OnBeginImplFrameDeadline();
    560     return;
    561   }
    562   begin_impl_frame_deadline_task_.Cancel();
    563   begin_impl_frame_deadline_task_.Reset(begin_impl_frame_deadline_closure_);
    564 
    565   base::TimeDelta delta = deadline - gfx::FrameTime::Now();
    566   if (delta <= base::TimeDelta())
    567     delta = base::TimeDelta();
    568   impl_task_runner_->PostDelayedTask(
    569       FROM_HERE, begin_impl_frame_deadline_task_.callback(), delta);
    570 }
    571 
    572 void Scheduler::OnBeginImplFrameDeadline() {
    573   TRACE_EVENT0("cc", "Scheduler::OnBeginImplFrameDeadline");
    574   begin_impl_frame_deadline_task_.Cancel();
    575 
    576   // We split the deadline actions up into two phases so the state machine
    577   // has a chance to trigger actions that should occur durring and after
    578   // the deadline separately. For example:
    579   // * Sending the BeginMainFrame will not occur after the deadline in
    580   //     order to wait for more user-input before starting the next commit.
    581   // * Creating a new OuputSurface will not occur during the deadline in
    582   //     order to allow the state machine to "settle" first.
    583   state_machine_.OnBeginImplFrameDeadline();
    584   ProcessScheduledActions();
    585   state_machine_.OnBeginImplFrameIdle();
    586   ProcessScheduledActions();
    587 
    588   client_->DidBeginImplFrameDeadline();
    589 }
    590 
    591 void Scheduler::PollForAnticipatedDrawTriggers() {
    592   TRACE_EVENT0("cc", "Scheduler::PollForAnticipatedDrawTriggers");
    593   poll_for_draw_triggers_task_.Cancel();
    594   state_machine_.DidEnterPollForAnticipatedDrawTriggers();
    595   ProcessScheduledActions();
    596   state_machine_.DidLeavePollForAnticipatedDrawTriggers();
    597 }
    598 
    599 void Scheduler::PollToAdvanceCommitState() {
    600   TRACE_EVENT0("cc", "Scheduler::PollToAdvanceCommitState");
    601   advance_commit_state_task_.Cancel();
    602   ProcessScheduledActions();
    603 }
    604 
    605 bool Scheduler::IsBeginMainFrameSent() const {
    606   return state_machine_.commit_state() ==
    607          SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT;
    608 }
    609 
    610 void Scheduler::DrawAndSwapIfPossible() {
    611   DrawResult result = client_->ScheduledActionDrawAndSwapIfPossible();
    612   state_machine_.DidDrawIfPossibleCompleted(result);
    613 }
    614 
    615 void Scheduler::ProcessScheduledActions() {
    616   // We do not allow ProcessScheduledActions to be recursive.
    617   // The top-level call will iteratively execute the next action for us anyway.
    618   if (inside_process_scheduled_actions_)
    619     return;
    620 
    621   base::AutoReset<bool> mark_inside(&inside_process_scheduled_actions_, true);
    622 
    623   SchedulerStateMachine::Action action;
    624   do {
    625     action = state_machine_.NextAction();
    626     TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
    627                  "SchedulerStateMachine",
    628                  "state",
    629                  ToTrace(this));
    630     state_machine_.UpdateState(action);
    631     base::AutoReset<SchedulerStateMachine::Action>
    632         mark_inside_action(&inside_action_, action);
    633     switch (action) {
    634       case SchedulerStateMachine::ACTION_NONE:
    635         break;
    636       case SchedulerStateMachine::ACTION_ANIMATE:
    637         client_->ScheduledActionAnimate();
    638         break;
    639       case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME:
    640         client_->ScheduledActionSendBeginMainFrame();
    641         break;
    642       case SchedulerStateMachine::ACTION_COMMIT:
    643         client_->ScheduledActionCommit();
    644         break;
    645       case SchedulerStateMachine::ACTION_UPDATE_VISIBLE_TILES:
    646         client_->ScheduledActionUpdateVisibleTiles();
    647         break;
    648       case SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE:
    649         client_->ScheduledActionActivatePendingTree();
    650         break;
    651       case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE:
    652         DrawAndSwapIfPossible();
    653         break;
    654       case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED:
    655         client_->ScheduledActionDrawAndSwapForced();
    656         break;
    657       case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT:
    658         // No action is actually performed, but this allows the state machine to
    659         // advance out of its waiting to draw state without actually drawing.
    660         break;
    661       case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
    662         client_->ScheduledActionBeginOutputSurfaceCreation();
    663         break;
    664       case SchedulerStateMachine::ACTION_MANAGE_TILES:
    665         client_->ScheduledActionManageTiles();
    666         break;
    667     }
    668   } while (action != SchedulerStateMachine::ACTION_NONE);
    669 
    670   SetupNextBeginFrameIfNeeded();
    671   client_->DidAnticipatedDrawTimeChange(AnticipatedDrawTime());
    672 
    673   if (state_machine_.ShouldTriggerBeginImplFrameDeadlineEarly()) {
    674     DCHECK(!settings_.using_synchronous_renderer_compositor);
    675     ScheduleBeginImplFrameDeadline(base::TimeTicks());
    676   }
    677 }
    678 
    679 bool Scheduler::WillDrawIfNeeded() const {
    680   return !state_machine_.PendingDrawsShouldBeAborted();
    681 }
    682 
    683 scoped_ptr<base::Value> Scheduler::AsValue() const {
    684   scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue);
    685   state->Set("state_machine", state_machine_.AsValue().release());
    686   if (synthetic_begin_frame_source_)
    687     state->Set("synthetic_begin_frame_source_",
    688                synthetic_begin_frame_source_->AsValue().release());
    689 
    690   scoped_ptr<base::DictionaryValue> scheduler_state(new base::DictionaryValue);
    691   scheduler_state->SetDouble(
    692       "time_until_anticipated_draw_time_ms",
    693       (AnticipatedDrawTime() - base::TimeTicks::Now()).InMillisecondsF());
    694   scheduler_state->SetDouble("vsync_interval_ms",
    695                              vsync_interval_.InMillisecondsF());
    696   scheduler_state->SetDouble("estimated_parent_draw_time_ms",
    697                              estimated_parent_draw_time_.InMillisecondsF());
    698   scheduler_state->SetBoolean("last_set_needs_begin_frame_",
    699                               last_set_needs_begin_frame_);
    700   scheduler_state->SetBoolean("begin_unthrottled_frame_posted_",
    701                               begin_unthrottled_frame_posted_);
    702   scheduler_state->SetBoolean("begin_retro_frame_posted_",
    703                               begin_retro_frame_posted_);
    704   scheduler_state->SetInteger("begin_retro_frame_args_",
    705                               begin_retro_frame_args_.size());
    706   scheduler_state->SetBoolean("begin_impl_frame_deadline_task_",
    707                               !begin_impl_frame_deadline_task_.IsCancelled());
    708   scheduler_state->SetBoolean("poll_for_draw_triggers_task_",
    709                               !poll_for_draw_triggers_task_.IsCancelled());
    710   scheduler_state->SetBoolean("advance_commit_state_task_",
    711                               !advance_commit_state_task_.IsCancelled());
    712   scheduler_state->Set("begin_impl_frame_args",
    713                        begin_impl_frame_args_.AsValue().release());
    714 
    715   state->Set("scheduler_state", scheduler_state.release());
    716 
    717   scoped_ptr<base::DictionaryValue> client_state(new base::DictionaryValue);
    718   client_state->SetDouble("draw_duration_estimate_ms",
    719                           client_->DrawDurationEstimate().InMillisecondsF());
    720   client_state->SetDouble(
    721       "begin_main_frame_to_commit_duration_estimate_ms",
    722       client_->BeginMainFrameToCommitDurationEstimate().InMillisecondsF());
    723   client_state->SetDouble(
    724       "commit_to_activate_duration_estimate_ms",
    725       client_->CommitToActivateDurationEstimate().InMillisecondsF());
    726   state->Set("client_state", client_state.release());
    727   return state.PassAs<base::Value>();
    728 }
    729 
    730 bool Scheduler::CanCommitAndActivateBeforeDeadline() const {
    731   // Check if the main thread computation and commit can be finished before the
    732   // impl thread's deadline.
    733   base::TimeTicks estimated_draw_time =
    734       begin_impl_frame_args_.frame_time +
    735       client_->BeginMainFrameToCommitDurationEstimate() +
    736       client_->CommitToActivateDurationEstimate();
    737 
    738   TRACE_EVENT2(
    739       TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
    740       "CanCommitAndActivateBeforeDeadline",
    741       "time_left_after_drawing_ms",
    742       (begin_impl_frame_args_.deadline - estimated_draw_time).InMillisecondsF(),
    743       "state",
    744       ToTrace(this));
    745 
    746   return estimated_draw_time < begin_impl_frame_args_.deadline;
    747 }
    748 
    749 bool Scheduler::IsBeginMainFrameSentOrStarted() const {
    750   return (state_machine_.commit_state() ==
    751               SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_SENT ||
    752           state_machine_.commit_state() ==
    753               SchedulerStateMachine::COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED);
    754 }
    755 
    756 }  // namespace cc
    757