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