scheduler_state_machine.cc revision a93a17c8d99d686bd4a1511e5504e5e6cc9fcadf
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_state_machine.h" 6 7#include "base/logging.h" 8#include "base/stringprintf.h" 9 10namespace cc { 11 12SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) 13 : settings_(settings), 14 commit_state_(COMMIT_STATE_IDLE), 15 current_frame_number_(0), 16 last_frame_number_where_draw_was_called_(-1), 17 last_frame_number_where_tree_activation_attempted_(-1), 18 last_frame_number_where_check_for_completed_tile_uploads_called_(-1), 19 consecutive_failed_draws_(0), 20 maximum_number_of_failed_draws_before_draw_is_forced_(3), 21 needs_redraw_(false), 22 swap_used_incomplete_tile_(false), 23 needs_forced_redraw_(false), 24 needs_forced_redraw_after_next_commit_(false), 25 needs_commit_(false), 26 needs_forced_commit_(false), 27 expect_immediate_begin_frame_(false), 28 main_thread_needs_layer_textures_(false), 29 inside_vsync_(false), 30 visible_(false), 31 can_start_(false), 32 can_draw_(false), 33 has_pending_tree_(false), 34 draw_if_possible_failed_(false), 35 texture_state_(LAYER_TEXTURE_STATE_UNLOCKED), 36 output_surface_state_(OUTPUT_SURFACE_LOST), 37 did_create_and_initialize_first_output_surface_(false) {} 38 39std::string SchedulerStateMachine::ToString() { 40 std::string str; 41 base::StringAppendF(&str, 42 "settings_.impl_side_painting = %d; ", 43 settings_.impl_side_painting); 44 base::StringAppendF(&str, "commit_state_ = %d; ", commit_state_); 45 base::StringAppendF( 46 &str, "current_frame_number_ = %d; ", current_frame_number_); 47 base::StringAppendF(&str, 48 "last_frame_number_where_draw_was_called_ = %d; ", 49 last_frame_number_where_draw_was_called_); 50 base::StringAppendF( 51 &str, 52 "last_frame_number_where_tree_activation_attempted_ = %d; ", 53 last_frame_number_where_tree_activation_attempted_); 54 base::StringAppendF( 55 &str, 56 "last_frame_number_where_check_for_completed_tile_uploads_called_ = %d; ", 57 last_frame_number_where_check_for_completed_tile_uploads_called_); 58 base::StringAppendF( 59 &str, "consecutive_failed_draws_ = %d; ", consecutive_failed_draws_); 60 base::StringAppendF( 61 &str, 62 "maximum_number_of_failed_draws_before_draw_is_forced_ = %d; ", 63 maximum_number_of_failed_draws_before_draw_is_forced_); 64 base::StringAppendF(&str, "needs_redraw_ = %d; ", needs_redraw_); 65 base::StringAppendF( 66 &str, "swap_used_incomplete_tile_ = %d; ", swap_used_incomplete_tile_); 67 base::StringAppendF( 68 &str, "needs_forced_redraw_ = %d; ", needs_forced_redraw_); 69 base::StringAppendF(&str, 70 "needs_forced_redraw_after_next_commit_ = %d; ", 71 needs_forced_redraw_after_next_commit_); 72 base::StringAppendF(&str, "needs_commit_ = %d; ", needs_commit_); 73 base::StringAppendF( 74 &str, "needs_forced_commit_ = %d; ", needs_forced_commit_); 75 base::StringAppendF(&str, 76 "expect_immediate_begin_frame_ = %d; ", 77 expect_immediate_begin_frame_); 78 base::StringAppendF(&str, 79 "main_thread_needs_layer_textures_ = %d; ", 80 main_thread_needs_layer_textures_); 81 base::StringAppendF(&str, "inside_vsync_ = %d; ", inside_vsync_); 82 base::StringAppendF(&str, "visible_ = %d; ", visible_); 83 base::StringAppendF(&str, "can_start_ = %d; ", can_start_); 84 base::StringAppendF(&str, "can_draw_ = %d; ", can_draw_); 85 base::StringAppendF( 86 &str, "draw_if_possible_failed_ = %d; ", draw_if_possible_failed_); 87 base::StringAppendF(&str, "has_pending_tree_ = %d; ", has_pending_tree_); 88 base::StringAppendF(&str, "texture_state_ = %d; ", texture_state_); 89 base::StringAppendF( 90 &str, "output_surface_state_ = %d; ", output_surface_state_); 91 return str; 92} 93 94bool SchedulerStateMachine::HasDrawnThisFrame() const { 95 return current_frame_number_ == last_frame_number_where_draw_was_called_; 96} 97 98bool SchedulerStateMachine::HasAttemptedTreeActivationThisFrame() const { 99 return current_frame_number_ == 100 last_frame_number_where_tree_activation_attempted_; 101} 102 103bool SchedulerStateMachine::HasCheckedForCompletedTileUploadsThisFrame() const { 104 return current_frame_number_ == 105 last_frame_number_where_check_for_completed_tile_uploads_called_; 106} 107 108bool SchedulerStateMachine::DrawSuspendedUntilCommit() const { 109 if (!can_draw_) 110 return true; 111 if (!visible_) 112 return true; 113 if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD) 114 return true; 115 return false; 116} 117 118bool SchedulerStateMachine::ScheduledToDraw() const { 119 if (!needs_redraw_) 120 return false; 121 if (DrawSuspendedUntilCommit()) 122 return false; 123 return true; 124} 125 126bool SchedulerStateMachine::ShouldDraw() const { 127 if (needs_forced_redraw_) 128 return true; 129 130 if (!ScheduledToDraw()) 131 return false; 132 if (!inside_vsync_) 133 return false; 134 if (HasDrawnThisFrame()) 135 return false; 136 if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE) 137 return false; 138 return true; 139} 140 141bool SchedulerStateMachine::ShouldAttemptTreeActivation() const { 142 return has_pending_tree_ && inside_vsync_ && 143 !HasAttemptedTreeActivationThisFrame(); 144} 145 146bool SchedulerStateMachine::ShouldCheckForCompletedTileUploads() const { 147 if (!settings_.impl_side_painting) 148 return false; 149 if (HasCheckedForCompletedTileUploadsThisFrame()) 150 return false; 151 152 return ShouldAttemptTreeActivation() || ShouldDraw() || 153 swap_used_incomplete_tile_; 154} 155 156bool SchedulerStateMachine::ShouldAcquireLayerTexturesForMainThread() const { 157 if (!main_thread_needs_layer_textures_) 158 return false; 159 if (texture_state_ == LAYER_TEXTURE_STATE_UNLOCKED) 160 return true; 161 DCHECK_EQ(texture_state_, LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD); 162 // Transfer the lock from impl thread to main thread immediately if the 163 // impl thread is not even scheduled to draw. Guards against deadlocking. 164 if (!ScheduledToDraw()) 165 return true; 166 if (!VSyncCallbackNeeded()) 167 return true; 168 return false; 169} 170 171SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const { 172 if (ShouldAcquireLayerTexturesForMainThread()) 173 return ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD; 174 175 switch (commit_state_) { 176 case COMMIT_STATE_IDLE: 177 if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE && 178 needs_forced_redraw_) 179 return ACTION_DRAW_FORCED; 180 if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE && 181 needs_forced_commit_) 182 // TODO(enne): Should probably drop the active tree on force commit. 183 return has_pending_tree_ ? ACTION_NONE : ACTION_BEGIN_FRAME; 184 if (output_surface_state_ == OUTPUT_SURFACE_LOST && can_start_) 185 return ACTION_BEGIN_OUTPUT_SURFACE_CREATION; 186 if (output_surface_state_ == OUTPUT_SURFACE_CREATING) 187 return ACTION_NONE; 188 if (ShouldCheckForCompletedTileUploads()) 189 return ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS; 190 if (ShouldAttemptTreeActivation()) 191 return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED; 192 if (ShouldDraw()) { 193 return needs_forced_redraw_ ? ACTION_DRAW_FORCED 194 : ACTION_DRAW_IF_POSSIBLE; 195 } 196 if (needs_commit_ && 197 ((visible_ && output_surface_state_ == OUTPUT_SURFACE_ACTIVE) 198 || needs_forced_commit_)) 199 // TODO(enne): Should probably drop the active tree on force commit. 200 return has_pending_tree_ ? ACTION_NONE : ACTION_BEGIN_FRAME; 201 return ACTION_NONE; 202 203 case COMMIT_STATE_FRAME_IN_PROGRESS: 204 if (ShouldCheckForCompletedTileUploads()) 205 return ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS; 206 if (ShouldAttemptTreeActivation()) 207 return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED; 208 if (ShouldDraw()) { 209 return needs_forced_redraw_ ? ACTION_DRAW_FORCED 210 : ACTION_DRAW_IF_POSSIBLE; 211 } 212 return ACTION_NONE; 213 214 case COMMIT_STATE_READY_TO_COMMIT: 215 return ACTION_COMMIT; 216 217 case COMMIT_STATE_WAITING_FOR_FIRST_DRAW: { 218 if (ShouldCheckForCompletedTileUploads()) 219 return ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS; 220 if (ShouldAttemptTreeActivation()) 221 return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED; 222 if (ShouldDraw() || output_surface_state_ == OUTPUT_SURFACE_LOST) { 223 return needs_forced_redraw_ ? ACTION_DRAW_FORCED 224 : ACTION_DRAW_IF_POSSIBLE; 225 } 226 // COMMIT_STATE_WAITING_FOR_FIRST_DRAW wants to enforce a draw. If 227 // can_draw_ is false or textures are not available, proceed to the next 228 // step (similar as in COMMIT_STATE_IDLE). 229 bool can_commit = visible_ || needs_forced_commit_; 230 if (needs_commit_ && can_commit && DrawSuspendedUntilCommit()) 231 return has_pending_tree_ ? ACTION_NONE : ACTION_BEGIN_FRAME; 232 return ACTION_NONE; 233 } 234 235 case COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW: 236 if (ShouldCheckForCompletedTileUploads()) 237 return ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS; 238 if (ShouldAttemptTreeActivation()) 239 return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED; 240 if (needs_forced_redraw_) 241 return ACTION_DRAW_FORCED; 242 return ACTION_NONE; 243 } 244 NOTREACHED(); 245 return ACTION_NONE; 246} 247 248void SchedulerStateMachine::UpdateState(Action action) { 249 switch (action) { 250 case ACTION_NONE: 251 return; 252 253 case ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS: 254 last_frame_number_where_check_for_completed_tile_uploads_called_ = 255 current_frame_number_; 256 return; 257 258 case ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED: 259 last_frame_number_where_tree_activation_attempted_ = 260 current_frame_number_; 261 return; 262 263 case ACTION_BEGIN_FRAME: 264 DCHECK(!has_pending_tree_); 265 DCHECK(visible_ || needs_forced_commit_); 266 commit_state_ = COMMIT_STATE_FRAME_IN_PROGRESS; 267 needs_commit_ = false; 268 needs_forced_commit_ = false; 269 return; 270 271 case ACTION_COMMIT: 272 if (expect_immediate_begin_frame_) 273 commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW; 274 else 275 commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW; 276 // When impl-side painting, we draw on activation instead of on commit. 277 if (!settings_.impl_side_painting) 278 needs_redraw_ = true; 279 if (draw_if_possible_failed_) 280 last_frame_number_where_draw_was_called_ = -1; 281 282 if (needs_forced_redraw_after_next_commit_) { 283 needs_forced_redraw_after_next_commit_ = false; 284 needs_forced_redraw_ = true; 285 } 286 287 texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD; 288 return; 289 290 case ACTION_DRAW_FORCED: 291 case ACTION_DRAW_IF_POSSIBLE: 292 needs_redraw_ = false; 293 needs_forced_redraw_ = false; 294 draw_if_possible_failed_ = false; 295 swap_used_incomplete_tile_ = false; 296 if (inside_vsync_) 297 last_frame_number_where_draw_was_called_ = current_frame_number_; 298 if (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW) { 299 DCHECK(expect_immediate_begin_frame_); 300 commit_state_ = COMMIT_STATE_FRAME_IN_PROGRESS; 301 expect_immediate_begin_frame_ = false; 302 } else if (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_DRAW) { 303 commit_state_ = COMMIT_STATE_IDLE; 304 } 305 if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD) 306 texture_state_ = LAYER_TEXTURE_STATE_UNLOCKED; 307 return; 308 309 case ACTION_BEGIN_OUTPUT_SURFACE_CREATION: 310 DCHECK_EQ(commit_state_, COMMIT_STATE_IDLE); 311 DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_LOST); 312 output_surface_state_ = OUTPUT_SURFACE_CREATING; 313 return; 314 315 case ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD: 316 texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD; 317 main_thread_needs_layer_textures_ = false; 318 if (commit_state_ != COMMIT_STATE_FRAME_IN_PROGRESS) 319 needs_commit_ = true; 320 return; 321 } 322} 323 324void SchedulerStateMachine::SetMainThreadNeedsLayerTextures() { 325 DCHECK(!main_thread_needs_layer_textures_); 326 DCHECK_NE(texture_state_, LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD); 327 main_thread_needs_layer_textures_ = true; 328} 329 330bool SchedulerStateMachine::VSyncCallbackNeeded() const { 331 // If we have a pending tree, need to keep getting notifications until 332 // the tree is ready to be swapped. 333 if (has_pending_tree_) 334 return true; 335 336 // If we can't draw, don't tick until we are notified that we can draw again. 337 if (!can_draw_) 338 return false; 339 340 if (needs_forced_redraw_) 341 return true; 342 343 if (visible_ && swap_used_incomplete_tile_) 344 return true; 345 346 return needs_redraw_ && visible_ && 347 output_surface_state_ == OUTPUT_SURFACE_ACTIVE; 348} 349 350void SchedulerStateMachine::DidEnterVSync() { inside_vsync_ = true; } 351 352void SchedulerStateMachine::DidLeaveVSync() { 353 current_frame_number_++; 354 inside_vsync_ = false; 355} 356 357void SchedulerStateMachine::SetVisible(bool visible) { visible_ = visible; } 358 359void SchedulerStateMachine::SetNeedsRedraw() { needs_redraw_ = true; } 360 361void SchedulerStateMachine::DidSwapUseIncompleteTile() { 362 swap_used_incomplete_tile_ = true; 363} 364 365void SchedulerStateMachine::SetNeedsForcedRedraw() { 366 needs_forced_redraw_ = true; 367} 368 369void SchedulerStateMachine::DidDrawIfPossibleCompleted(bool success) { 370 draw_if_possible_failed_ = !success; 371 if (draw_if_possible_failed_) { 372 needs_redraw_ = true; 373 needs_commit_ = true; 374 consecutive_failed_draws_++; 375 if (settings_.timeout_and_draw_when_animation_checkerboards && 376 consecutive_failed_draws_ >= 377 maximum_number_of_failed_draws_before_draw_is_forced_) { 378 consecutive_failed_draws_ = 0; 379 // We need to force a draw, but it doesn't make sense to do this until 380 // we've committed and have new textures. 381 needs_forced_redraw_after_next_commit_ = true; 382 } 383 } else { 384 consecutive_failed_draws_ = 0; 385 } 386} 387 388void SchedulerStateMachine::SetNeedsCommit() { needs_commit_ = true; } 389 390void SchedulerStateMachine::SetNeedsForcedCommit() { 391 needs_forced_commit_ = true; 392 expect_immediate_begin_frame_ = true; 393} 394 395void SchedulerStateMachine::BeginFrameComplete() { 396 DCHECK(commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS || 397 (expect_immediate_begin_frame_ && commit_state_ != COMMIT_STATE_IDLE)) 398 << ToString(); 399 commit_state_ = COMMIT_STATE_READY_TO_COMMIT; 400} 401 402void SchedulerStateMachine::BeginFrameAborted() { 403 DCHECK_EQ(commit_state_, COMMIT_STATE_FRAME_IN_PROGRESS); 404 if (expect_immediate_begin_frame_) { 405 expect_immediate_begin_frame_ = false; 406 } else { 407 commit_state_ = COMMIT_STATE_IDLE; 408 SetNeedsCommit(); 409 } 410} 411 412void SchedulerStateMachine::DidLoseOutputSurface() { 413 if (output_surface_state_ == OUTPUT_SURFACE_LOST || 414 output_surface_state_ == OUTPUT_SURFACE_CREATING) 415 return; 416 output_surface_state_ = OUTPUT_SURFACE_LOST; 417} 418 419void SchedulerStateMachine::SetHasPendingTree(bool has_pending_tree) { 420 has_pending_tree_ = has_pending_tree; 421} 422 423void SchedulerStateMachine::SetCanDraw(bool can) { can_draw_ = can; } 424 425void SchedulerStateMachine::DidCreateAndInitializeOutputSurface() { 426 DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_CREATING); 427 output_surface_state_ = OUTPUT_SURFACE_ACTIVE; 428 429 if (did_create_and_initialize_first_output_surface_) { 430 // TODO(boliu): See if we can remove this when impl-side painting is always 431 // on. Does anything on the main thread need to update after recreate? 432 needs_commit_ = true; 433 // If anything has requested a redraw, we don't want to actually draw 434 // when the output surface is restored until things have a chance to 435 // sort themselves out with a commit. 436 needs_redraw_ = false; 437 } 438 did_create_and_initialize_first_output_surface_ = true; 439} 440 441bool SchedulerStateMachine::HasInitializedOutputSurface() const { 442 return output_surface_state_ == OUTPUT_SURFACE_ACTIVE; 443} 444 445void SchedulerStateMachine::SetMaximumNumberOfFailedDrawsBeforeDrawIsForced( 446 int num_draws) { 447 maximum_number_of_failed_draws_before_draw_is_forced_ = num_draws; 448} 449 450} // namespace cc 451