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