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 "cc/debug/devtools_instrumentation.h"
     12 #include "cc/debug/traced_value.h"
     13 #include "ui/gfx/frame_time.h"
     14 
     15 namespace cc {
     16 
     17 Scheduler::Scheduler(SchedulerClient* client,
     18                      const SchedulerSettings& scheduler_settings,
     19                      int layer_tree_host_id)
     20     : settings_(scheduler_settings),
     21       client_(client),
     22       layer_tree_host_id_(layer_tree_host_id),
     23       last_set_needs_begin_impl_frame_(false),
     24       state_machine_(scheduler_settings),
     25       inside_process_scheduled_actions_(false),
     26       inside_action_(SchedulerStateMachine::ACTION_NONE),
     27       weak_factory_(this) {
     28   DCHECK(client_);
     29   DCHECK(!state_machine_.BeginImplFrameNeeded());
     30 }
     31 
     32 Scheduler::~Scheduler() {}
     33 
     34 void Scheduler::SetCanStart() {
     35   state_machine_.SetCanStart();
     36   ProcessScheduledActions();
     37 }
     38 
     39 void Scheduler::SetVisible(bool visible) {
     40   state_machine_.SetVisible(visible);
     41   ProcessScheduledActions();
     42 }
     43 
     44 void Scheduler::SetCanDraw(bool can_draw) {
     45   state_machine_.SetCanDraw(can_draw);
     46   ProcessScheduledActions();
     47 }
     48 
     49 void Scheduler::NotifyReadyToActivate() {
     50   state_machine_.NotifyReadyToActivate();
     51   ProcessScheduledActions();
     52 }
     53 
     54 void Scheduler::ActivatePendingTree() {
     55   client_->ScheduledActionActivatePendingTree();
     56 }
     57 
     58 void Scheduler::SetNeedsCommit() {
     59   state_machine_.SetNeedsCommit();
     60   ProcessScheduledActions();
     61 }
     62 
     63 void Scheduler::SetNeedsForcedCommitForReadback() {
     64   state_machine_.SetNeedsCommit();
     65   state_machine_.SetNeedsForcedCommitForReadback();
     66   ProcessScheduledActions();
     67 }
     68 
     69 void Scheduler::SetNeedsRedraw() {
     70   state_machine_.SetNeedsRedraw();
     71   ProcessScheduledActions();
     72 }
     73 
     74 void Scheduler::SetNeedsManageTiles() {
     75   DCHECK(!IsInsideAction(SchedulerStateMachine::ACTION_MANAGE_TILES));
     76   state_machine_.SetNeedsManageTiles();
     77   ProcessScheduledActions();
     78 }
     79 
     80 void Scheduler::SetSwapUsedIncompleteTile(bool used_incomplete_tile) {
     81   state_machine_.SetSwapUsedIncompleteTile(used_incomplete_tile);
     82   ProcessScheduledActions();
     83 }
     84 
     85 void Scheduler::SetSmoothnessTakesPriority(bool smoothness_takes_priority) {
     86   state_machine_.SetSmoothnessTakesPriority(smoothness_takes_priority);
     87   ProcessScheduledActions();
     88 }
     89 
     90 void Scheduler::SetMainThreadNeedsLayerTextures() {
     91   state_machine_.SetMainThreadNeedsLayerTextures();
     92   ProcessScheduledActions();
     93 }
     94 
     95 void Scheduler::FinishCommit() {
     96   TRACE_EVENT0("cc", "Scheduler::FinishCommit");
     97   state_machine_.FinishCommit();
     98   ProcessScheduledActions();
     99 }
    100 
    101 void Scheduler::BeginMainFrameAborted(bool did_handle) {
    102   TRACE_EVENT0("cc", "Scheduler::BeginMainFrameAborted");
    103   state_machine_.BeginMainFrameAborted(did_handle);
    104   ProcessScheduledActions();
    105 }
    106 
    107 void Scheduler::DidManageTiles() {
    108   state_machine_.DidManageTiles();
    109 }
    110 
    111 void Scheduler::DidLoseOutputSurface() {
    112   TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface");
    113   last_set_needs_begin_impl_frame_ = false;
    114   begin_impl_frame_deadline_closure_.Cancel();
    115   state_machine_.DidLoseOutputSurface();
    116   ProcessScheduledActions();
    117 }
    118 
    119 void Scheduler::DidCreateAndInitializeOutputSurface() {
    120   TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface");
    121   DCHECK(!last_set_needs_begin_impl_frame_);
    122   DCHECK(begin_impl_frame_deadline_closure_.IsCancelled());
    123   state_machine_.DidCreateAndInitializeOutputSurface();
    124   ProcessScheduledActions();
    125 }
    126 
    127 base::TimeTicks Scheduler::AnticipatedDrawTime() {
    128   TRACE_EVENT0("cc", "Scheduler::AnticipatedDrawTime");
    129 
    130   if (!last_set_needs_begin_impl_frame_ ||
    131       last_begin_impl_frame_args_.interval <= base::TimeDelta())
    132     return base::TimeTicks();
    133 
    134   base::TimeTicks now = gfx::FrameTime::Now();
    135   base::TimeTicks timebase = std::max(last_begin_impl_frame_args_.frame_time,
    136                                       last_begin_impl_frame_args_.deadline);
    137   int64 intervals =
    138       1 + ((now - timebase) / last_begin_impl_frame_args_.interval);
    139   return timebase + (last_begin_impl_frame_args_.interval * intervals);
    140 }
    141 
    142 base::TimeTicks Scheduler::LastBeginImplFrameTime() {
    143   return last_begin_impl_frame_args_.frame_time;
    144 }
    145 
    146 void Scheduler::SetupNextBeginImplFrameIfNeeded() {
    147   bool needs_begin_impl_frame =
    148       state_machine_.BeginImplFrameNeeded();
    149 
    150   bool at_end_of_deadline =
    151       state_machine_.begin_impl_frame_state() ==
    152           SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE;
    153 
    154   bool should_call_set_needs_begin_impl_frame =
    155       // Always request the BeginImplFrame immediately if it wasn't needed
    156       // before.
    157       (needs_begin_impl_frame && !last_set_needs_begin_impl_frame_) ||
    158       // We always need to explicitly request our next BeginImplFrame.
    159       at_end_of_deadline;
    160 
    161   if (should_call_set_needs_begin_impl_frame) {
    162     client_->SetNeedsBeginImplFrame(needs_begin_impl_frame);
    163     last_set_needs_begin_impl_frame_ = needs_begin_impl_frame;
    164   }
    165 
    166   bool needs_advance_commit_state_timer = false;
    167   // Setup PollForAnticipatedDrawTriggers if we need to monitor state but
    168   // aren't expecting any more BeginImplFrames. This should only be needed by
    169   // the synchronous compositor when BeginImplFrameNeeded is false.
    170   if (state_machine_.ShouldPollForAnticipatedDrawTriggers()) {
    171     DCHECK(!state_machine_.SupportsProactiveBeginImplFrame());
    172     DCHECK(!needs_begin_impl_frame);
    173     if (poll_for_draw_triggers_closure_.IsCancelled()) {
    174       poll_for_draw_triggers_closure_.Reset(
    175           base::Bind(&Scheduler::PollForAnticipatedDrawTriggers,
    176                      weak_factory_.GetWeakPtr()));
    177       base::MessageLoop::current()->PostDelayedTask(
    178           FROM_HERE,
    179           poll_for_draw_triggers_closure_.callback(),
    180           last_begin_impl_frame_args_.interval);
    181     }
    182   } else {
    183     poll_for_draw_triggers_closure_.Cancel();
    184 
    185     // At this point we'd prefer to advance through the commit flow by
    186     // drawing a frame, however it's possible that the frame rate controller
    187     // will not give us a BeginImplFrame until the commit completes.  See
    188     // crbug.com/317430 for an example of a swap ack being held on commit. Thus
    189     // we set a repeating timer to poll on ProcessScheduledActions until we
    190     // successfully reach BeginImplFrame.
    191     if (state_machine_.IsCommitStateWaiting())
    192       needs_advance_commit_state_timer = true;
    193   }
    194   if (needs_advance_commit_state_timer !=
    195       advance_commit_state_timer_.IsRunning()) {
    196     if (needs_advance_commit_state_timer &&
    197         last_begin_impl_frame_args_.IsValid()) {
    198     // Since we'd rather get a BeginImplFrame by the normally mechanism, we set
    199     // the interval to twice the interval from the previous frame.
    200       advance_commit_state_timer_.Start(
    201           FROM_HERE,
    202           last_begin_impl_frame_args_.interval * 2,
    203           base::Bind(&Scheduler::ProcessScheduledActions,
    204                      base::Unretained(this)));
    205     } else {
    206       advance_commit_state_timer_.Stop();
    207     }
    208   }
    209 }
    210 
    211 void Scheduler::BeginImplFrame(const BeginFrameArgs& args) {
    212   TRACE_EVENT0("cc", "Scheduler::BeginImplFrame");
    213   DCHECK(state_machine_.begin_impl_frame_state() ==
    214          SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE);
    215   DCHECK(state_machine_.HasInitializedOutputSurface());
    216   last_begin_impl_frame_args_ = args;
    217   last_begin_impl_frame_args_.deadline -= client_->DrawDurationEstimate();
    218   state_machine_.OnBeginImplFrame(last_begin_impl_frame_args_);
    219 
    220   if (settings_.switch_to_low_latency_if_possible) {
    221     state_machine_.SetSkipBeginMainFrameToReduceLatency(
    222         state_machine_.MainThreadIsInHighLatencyMode() &&
    223             CanCommitAndActivateBeforeDeadline());
    224   }
    225 
    226   ProcessScheduledActions();
    227 
    228   if (!state_machine_.HasInitializedOutputSurface())
    229     return;
    230 
    231   state_machine_.OnBeginImplFrameDeadlinePending();
    232   devtools_instrumentation::didBeginFrame(layer_tree_host_id_);
    233   if (settings_.using_synchronous_renderer_compositor) {
    234     // The synchronous renderer compositor has to make its GL calls
    235     // within this call to BeginImplFrame.
    236     // TODO(brianderson): Have the OutputSurface initiate the deadline tasks
    237     // so the sychronous renderer compositor can take advantage of splitting
    238     // up the BeginImplFrame and deadline as well.
    239     OnBeginImplFrameDeadline();
    240   } else if (!settings_.deadline_scheduling_enabled) {
    241     // We emulate the old non-deadline scheduler here by posting the
    242     // deadline task without any delay.
    243     PostBeginImplFrameDeadline(base::TimeTicks());
    244   } else if (state_machine_.ShouldTriggerBeginImplFrameDeadlineEarly()) {
    245     // We are ready to draw a new active tree immediately.
    246     PostBeginImplFrameDeadline(base::TimeTicks());
    247   } else if (state_machine_.needs_redraw()) {
    248     // We have an animation or fast input path on the impl thread that wants
    249     // to draw, so don't wait too long for a new active tree.
    250     PostBeginImplFrameDeadline(last_begin_impl_frame_args_.deadline);
    251   } else {
    252     // The impl thread doesn't have anything it wants to draw and we are just
    253     // waiting for a new active tree, so post the deadline for the next
    254     // expected BeginImplFrame start. This allows us to draw immediately when
    255     // there is a new active tree, instead of waiting for the next
    256     // BeginImplFrame.
    257     // TODO(brianderson): Handle long deadlines (that are past the next frame's
    258     // frame time) properly instead of using this hack.
    259     PostBeginImplFrameDeadline(last_begin_impl_frame_args_.frame_time +
    260                                last_begin_impl_frame_args_.interval);
    261   }
    262 }
    263 
    264 void Scheduler::PostBeginImplFrameDeadline(base::TimeTicks deadline) {
    265   begin_impl_frame_deadline_closure_.Cancel();
    266   begin_impl_frame_deadline_closure_.Reset(
    267       base::Bind(&Scheduler::OnBeginImplFrameDeadline,
    268                  weak_factory_.GetWeakPtr()));
    269   client_->PostBeginImplFrameDeadline(
    270       begin_impl_frame_deadline_closure_.callback(), deadline);
    271 }
    272 
    273 void Scheduler::OnBeginImplFrameDeadline() {
    274   TRACE_EVENT0("cc", "Scheduler::OnBeginImplFrameDeadline");
    275   DCHECK(state_machine_.HasInitializedOutputSurface());
    276   begin_impl_frame_deadline_closure_.Cancel();
    277   state_machine_.OnBeginImplFrameDeadline();
    278   ProcessScheduledActions();
    279 
    280   if (state_machine_.HasInitializedOutputSurface()) {
    281     // We only transition out of BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE when all
    282     // actions that occur back-to-back in response to entering
    283     // BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE have completed. This is important
    284     // because sending the BeginMainFrame will not occur if we transition to
    285     // BEGIN_IMPL_FRAME_STATE_IDLE too early.
    286     state_machine_.OnBeginImplFrameIdle();
    287   }
    288 
    289   client_->DidBeginImplFrameDeadline();
    290 }
    291 
    292 void Scheduler::PollForAnticipatedDrawTriggers() {
    293   TRACE_EVENT0("cc", "Scheduler::PollForAnticipatedDrawTriggers");
    294   poll_for_draw_triggers_closure_.Cancel();
    295   state_machine_.DidEnterPollForAnticipatedDrawTriggers();
    296   ProcessScheduledActions();
    297   state_machine_.DidLeavePollForAnticipatedDrawTriggers();
    298 }
    299 
    300 void Scheduler::DrawAndSwapIfPossible() {
    301   DrawSwapReadbackResult result =
    302       client_->ScheduledActionDrawAndSwapIfPossible();
    303   state_machine_.DidDrawIfPossibleCompleted(result.did_draw);
    304 }
    305 
    306 void Scheduler::DrawAndSwapForced() {
    307   client_->ScheduledActionDrawAndSwapForced();
    308 }
    309 
    310 void Scheduler::DrawAndReadback() {
    311   DrawSwapReadbackResult result = client_->ScheduledActionDrawAndReadback();
    312   DCHECK(!result.did_swap);
    313 }
    314 
    315 void Scheduler::ProcessScheduledActions() {
    316   // We do not allow ProcessScheduledActions to be recursive.
    317   // The top-level call will iteratively execute the next action for us anyway.
    318   if (inside_process_scheduled_actions_)
    319     return;
    320 
    321   base::AutoReset<bool> mark_inside(&inside_process_scheduled_actions_, true);
    322 
    323   SchedulerStateMachine::Action action;
    324   do {
    325     state_machine_.CheckInvariants();
    326     action = state_machine_.NextAction();
    327     TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
    328                  "SchedulerStateMachine",
    329                  "state",
    330                  TracedValue::FromValue(state_machine_.AsValue().release()));
    331     state_machine_.UpdateState(action);
    332     base::AutoReset<SchedulerStateMachine::Action>
    333         mark_inside_action(&inside_action_, action);
    334     switch (action) {
    335       case SchedulerStateMachine::ACTION_NONE:
    336         break;
    337       case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME:
    338         client_->ScheduledActionSendBeginMainFrame();
    339         break;
    340       case SchedulerStateMachine::ACTION_COMMIT:
    341         client_->ScheduledActionCommit();
    342         break;
    343       case SchedulerStateMachine::ACTION_UPDATE_VISIBLE_TILES:
    344         client_->ScheduledActionUpdateVisibleTiles();
    345         break;
    346       case SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE:
    347         ActivatePendingTree();
    348         break;
    349       case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE:
    350         DrawAndSwapIfPossible();
    351         break;
    352       case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED:
    353         DrawAndSwapForced();
    354         break;
    355       case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT:
    356         // No action is actually performed, but this allows the state machine to
    357         // advance out of its waiting to draw state without actually drawing.
    358         break;
    359       case SchedulerStateMachine::ACTION_DRAW_AND_READBACK:
    360         DrawAndReadback();
    361         break;
    362       case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
    363         client_->ScheduledActionBeginOutputSurfaceCreation();
    364         break;
    365       case SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD:
    366         client_->ScheduledActionAcquireLayerTexturesForMainThread();
    367         break;
    368       case SchedulerStateMachine::ACTION_MANAGE_TILES:
    369         client_->ScheduledActionManageTiles();
    370         break;
    371     }
    372   } while (action != SchedulerStateMachine::ACTION_NONE);
    373 
    374   SetupNextBeginImplFrameIfNeeded();
    375   client_->DidAnticipatedDrawTimeChange(AnticipatedDrawTime());
    376 
    377   if (state_machine_.ShouldTriggerBeginImplFrameDeadlineEarly())
    378     PostBeginImplFrameDeadline(base::TimeTicks());
    379 }
    380 
    381 bool Scheduler::WillDrawIfNeeded() const {
    382   return !state_machine_.PendingDrawsShouldBeAborted();
    383 }
    384 
    385 bool Scheduler::CanCommitAndActivateBeforeDeadline() const {
    386   // Check if the main thread computation and commit can be finished before the
    387   // impl thread's deadline.
    388   base::TimeTicks estimated_draw_time =
    389       last_begin_impl_frame_args_.frame_time +
    390       client_->BeginMainFrameToCommitDurationEstimate() +
    391       client_->CommitToActivateDurationEstimate();
    392 
    393   return estimated_draw_time < last_begin_impl_frame_args_.deadline;
    394 }
    395 
    396 }  // namespace cc
    397