Chromium Code Reviews| Index: cc/scheduler/scheduler_state_machine.cc |
| diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc |
| index 4bba0454ec91dda8acaa5556df22ad1926d8fbd8..6ba71ea024eb8b71234a5f51eff24486596aef96 100644 |
| --- a/cc/scheduler/scheduler_state_machine.cc |
| +++ b/cc/scheduler/scheduler_state_machine.cc |
| @@ -19,7 +19,6 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) |
| current_frame_number_(0), |
| last_frame_number_where_begin_frame_sent_to_main_thread_(-1), |
| last_frame_number_where_draw_was_called_(-1), |
| - last_frame_number_where_tree_activation_attempted_(-1), |
| last_frame_number_where_update_visible_tiles_was_called_(-1), |
| consecutive_failed_draws_(0), |
| maximum_number_of_failed_draws_before_draw_is_forced_(3), |
| @@ -36,6 +35,8 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) |
| can_start_(false), |
| can_draw_(false), |
| has_pending_tree_(false), |
| + pending_tree_is_ready_for_activation_(false), |
| + active_tree_has_been_drawn_(false), |
| draw_if_possible_failed_(false), |
| texture_state_(LAYER_TEXTURE_STATE_UNLOCKED), |
| did_create_and_initialize_first_output_surface_(false) {} |
| @@ -98,8 +99,8 @@ const char* SchedulerStateMachine::ActionToString(Action action) { |
| return "ACTION_COMMIT"; |
| case ACTION_UPDATE_VISIBLE_TILES: |
| return "ACTION_UPDATE_VISIBLE_TILES"; |
| - case ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED: |
| - return "ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED"; |
| + case ACTION_ACTIVATE_PENDING_TREE: |
| + return "ACTION_ACTIVATE_PENDING_TREE"; |
| case ACTION_DRAW_IF_POSSIBLE: |
| return "ACTION_DRAW_IF_POSSIBLE"; |
| case ACTION_DRAW_FORCED: |
| @@ -162,8 +163,6 @@ scoped_ptr<base::Value> SchedulerStateMachine::AsValue() const { |
| last_frame_number_where_begin_frame_sent_to_main_thread_); |
| minor_state->SetInteger("last_frame_number_where_draw_was_called", |
| last_frame_number_where_draw_was_called_); |
| - minor_state->SetInteger("last_frame_number_where_tree_activation_attempted", |
| - last_frame_number_where_tree_activation_attempted_); |
| minor_state->SetInteger( |
| "last_frame_number_where_update_visible_tiles_was_called", |
| last_frame_number_where_update_visible_tiles_was_called_); |
| @@ -189,6 +188,10 @@ scoped_ptr<base::Value> SchedulerStateMachine::AsValue() const { |
| minor_state->SetBoolean("can_start", can_start_); |
| minor_state->SetBoolean("can_draw", can_draw_); |
| minor_state->SetBoolean("has_pending_tree", has_pending_tree_); |
| + minor_state->SetBoolean("pending_tree_is_ready_for_activation_", |
| + pending_tree_is_ready_for_activation_); |
| + minor_state->SetBoolean("active_tree_has_been_drawn_", |
| + active_tree_has_been_drawn_); |
| minor_state->SetBoolean("draw_if_possible_failed", draw_if_possible_failed_); |
| minor_state->SetBoolean("did_create_and_initialize_first_output_surface", |
| did_create_and_initialize_first_output_surface_); |
| @@ -201,11 +204,6 @@ bool SchedulerStateMachine::HasDrawnThisFrame() const { |
| return current_frame_number_ == last_frame_number_where_draw_was_called_; |
| } |
| -bool SchedulerStateMachine::HasAttemptedTreeActivationThisFrame() const { |
| - return current_frame_number_ == |
| - last_frame_number_where_tree_activation_attempted_; |
| -} |
| - |
| bool SchedulerStateMachine::HasUpdatedVisibleTilesThisFrame() const { |
| return current_frame_number_ == |
| last_frame_number_where_update_visible_tiles_was_called_; |
| @@ -216,86 +214,59 @@ bool SchedulerStateMachine::HasSentBeginFrameToMainThreadThisFrame() const { |
| last_frame_number_where_begin_frame_sent_to_main_thread_; |
| } |
| -void SchedulerStateMachine::HandleCommitInternal(bool commit_was_aborted) { |
| - commit_count_++; |
| - |
| - // If we are impl-side-painting but the commit was aborted, then we behave |
| - // mostly as if we are not impl-side-painting since there is no pending tree. |
| - bool commit_results_in_pending_tree = |
| - settings_.impl_side_painting && !commit_was_aborted; |
| - |
| - // Update the commit state. |
| - if (expect_immediate_begin_frame_for_main_thread_) |
| - commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW; |
| - else if (!commit_was_aborted) |
| - commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW; |
| - else |
| - commit_state_ = COMMIT_STATE_IDLE; |
| - |
| - // Update the output surface state. |
| - DCHECK_NE(output_surface_state_, |
| - OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION); |
| - if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT) { |
| - if (commit_results_in_pending_tree) { |
| - output_surface_state_ = OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION; |
| - } else { |
| - output_surface_state_ = OUTPUT_SURFACE_ACTIVE; |
| - needs_redraw_ = true; |
| - } |
| - } |
| - |
| - // if we don't have to wait for activation, update needs_redraw now. |
| - if (!commit_results_in_pending_tree) { |
| - if (!commit_was_aborted) |
| - needs_redraw_ = true; |
| - if (expect_immediate_begin_frame_for_main_thread_) |
| - needs_redraw_ = true; |
| - } |
| - |
| - // This post-commit work is common to both completed and aborted commits. |
| - if (draw_if_possible_failed_) |
| - last_frame_number_where_draw_was_called_ = -1; |
| - |
| - if (needs_forced_redraw_after_next_commit_) { |
| - needs_forced_redraw_after_next_commit_ = false; |
| - needs_forced_redraw_ = true; |
| - } |
| - |
| - // If we are planing to draw with the new commit, lock the layer textures for |
| - // use on the impl thread. Otherwise, leave them unlocked. |
| - if (commit_results_in_pending_tree || needs_redraw_ || needs_forced_redraw_) |
| - texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD; |
| - else |
| - texture_state_ = LAYER_TEXTURE_STATE_UNLOCKED; |
| -} |
| - |
| bool SchedulerStateMachine::PendingDrawsShouldBeAborted() const { |
| // These are all the cases where, if we do not abort draws to make |
| // forward progress, we might deadlock with the main thread. |
| + // This should be a superset of PendingActivationsShouldBeForced(). |
| + if (PendingActivationsShouldBeForced()) |
| + return true; |
| + |
| + // Additional states where we should abort draws. |
| + // Note: We don't force activation in these cases because doing so would |
| + // result in checkerboarding on resize, becoming visible, etc. |
| if (!can_draw_) |
| return true; |
| if (!visible_) |
| return true; |
| - if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE) |
| + if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION) |
| return true; |
| + return false; |
| +} |
| + |
| +bool SchedulerStateMachine::PendingActivationsShouldBeForced() const { |
| + // These are all the cases where, if we do not force activations to make |
| + // forward progress, we might deadlock with the main thread. |
| + // This should be a subset PendingDrawsShouldBeAborted(). |
| if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD) |
| return true; |
| + if (output_surface_state_ == OUTPUT_SURFACE_LOST || |
| + output_surface_state_ == OUTPUT_SURFACE_CREATING || |
| + output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT) |
| + return true; |
| return false; |
| } |
| bool SchedulerStateMachine::ShouldDraw() const { |
| + // We should not draw or abort draws while we are waiting for the |
| + // first activation. Doing so will cause us to transition to |
| + // COMMIT_STATE_IDLE and start the 2nd commit before the 1st commit |
| + // has been drawn. It will also cause us to fail readbacks when we could |
| + // have succeeded by waiting a little longer. |
| + if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION) |
| + return false; |
| + |
| // Always handle forced draws ASAP. |
| if (needs_forced_redraw_) |
| return true; |
| + // When we are waiting for a forced draw, only draw when |
| + // needs_forced_redraw_ is true. |
| + if (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW) |
| + return needs_forced_redraw_; |
| + |
| // If we are going to abort draws, we should do so ASAP. |
| - if (PendingDrawsShouldBeAborted()) { |
| - // TODO(brianderson): Remove the !has_pending_tree_ condition once |
| - // the Scheduler controls activation. It's dangerous for us to rely on |
| - // an eventual activation if we've lost the output surface. |
| - return commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_DRAW && |
| - !has_pending_tree_; |
| - } |
| + if (PendingDrawsShouldBeAborted()) |
| + return commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_DRAW; |
| // After this line, we only want to draw once per frame. |
| if (HasDrawnThisFrame()) |
| @@ -308,9 +279,25 @@ bool SchedulerStateMachine::ShouldDraw() const { |
| return needs_redraw_; |
| } |
| -bool SchedulerStateMachine::ShouldAttemptTreeActivation() const { |
| - return has_pending_tree_ && inside_begin_frame_ && |
| - !HasAttemptedTreeActivationThisFrame(); |
| +bool SchedulerStateMachine::ShouldActivatePendingTree() const { |
| + // There is nothing to activate. |
| + if (!has_pending_tree_) |
| + return false; |
| + |
| + // We don't want to activate a second tree before drawing the first one. |
| + // Note: It is possible that there is no active tree to draw when |
| + // output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION, |
| + // so we don't block activation on draw in that case. |
| + if (!active_tree_has_been_drawn_ && |
| + output_surface_state_ != OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION) |
| + return false; |
| + |
| + // If we want to force activation, do so ASAP. |
| + if (PendingActivationsShouldBeForced()) |
| + return true; |
| + |
| + // At this point, only activate if we are ready to activate. |
| + return pending_tree_is_ready_for_activation_; |
| } |
| bool SchedulerStateMachine::ShouldUpdateVisibleTiles() const { |
| @@ -319,7 +306,7 @@ bool SchedulerStateMachine::ShouldUpdateVisibleTiles() const { |
| if (HasUpdatedVisibleTilesThisFrame()) |
| return false; |
| - return ShouldAttemptTreeActivation() || ShouldDraw() || |
| + return ShouldActivatePendingTree() || ShouldDraw() || |
| swap_used_incomplete_tile_; |
| } |
| @@ -374,22 +361,14 @@ SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const { |
| switch (commit_state_) { |
| case COMMIT_STATE_IDLE: { |
| - if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE && |
| - needs_forced_redraw_) |
| - return ACTION_DRAW_FORCED; |
| - if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE && |
| - needs_forced_commit_) |
| - // TODO(enne): Should probably drop the active tree on force commit. |
| - return has_pending_tree_ ? ACTION_NONE |
| - : ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD; |
| if (output_surface_state_ == OUTPUT_SURFACE_LOST && can_start_) |
| return ACTION_BEGIN_OUTPUT_SURFACE_CREATION; |
| if (output_surface_state_ == OUTPUT_SURFACE_CREATING) |
| return ACTION_NONE; |
| if (ShouldUpdateVisibleTiles()) |
| return ACTION_UPDATE_VISIBLE_TILES; |
| - if (ShouldAttemptTreeActivation()) |
| - return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED; |
| + if (ShouldActivatePendingTree()) |
| + return ACTION_ACTIVATE_PENDING_TREE; |
| if (ShouldDraw()) { |
| return needs_forced_redraw_ ? ACTION_DRAW_FORCED |
| : ACTION_DRAW_IF_POSSIBLE; |
| @@ -401,8 +380,8 @@ SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const { |
| case COMMIT_STATE_FRAME_IN_PROGRESS: |
| if (ShouldUpdateVisibleTiles()) |
| return ACTION_UPDATE_VISIBLE_TILES; |
| - if (ShouldAttemptTreeActivation()) |
| - return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED; |
| + if (ShouldActivatePendingTree()) |
| + return ACTION_ACTIVATE_PENDING_TREE; |
| if (ShouldDraw()) { |
| return needs_forced_redraw_ ? ACTION_DRAW_FORCED |
| : ACTION_DRAW_IF_POSSIBLE; |
| @@ -415,8 +394,8 @@ SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const { |
| case COMMIT_STATE_WAITING_FOR_FIRST_DRAW: { |
| if (ShouldUpdateVisibleTiles()) |
| return ACTION_UPDATE_VISIBLE_TILES; |
| - if (ShouldAttemptTreeActivation()) |
| - return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED; |
| + if (ShouldActivatePendingTree()) |
| + return ACTION_ACTIVATE_PENDING_TREE; |
| if (ShouldDraw()) { |
| if (needs_forced_redraw_) |
| return ACTION_DRAW_FORCED; |
| @@ -431,9 +410,9 @@ SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const { |
| case COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW: |
| if (ShouldUpdateVisibleTiles()) |
| return ACTION_UPDATE_VISIBLE_TILES; |
| - if (ShouldAttemptTreeActivation()) |
| - return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED; |
| - if (needs_forced_redraw_) |
| + if (ShouldActivatePendingTree()) |
| + return ACTION_ACTIVATE_PENDING_TREE; |
| + if (ShouldDraw()) |
| return ACTION_DRAW_FORCED; |
| return ACTION_NONE; |
| } |
| @@ -451,9 +430,8 @@ void SchedulerStateMachine::UpdateState(Action action) { |
| current_frame_number_; |
| return; |
| - case ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED: |
| - last_frame_number_where_tree_activation_attempted_ = |
| - current_frame_number_; |
| + case ACTION_ACTIVATE_PENDING_TREE: |
| + UpdateStateOnActivation(); |
| return; |
| case ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD: |
| @@ -473,7 +451,7 @@ void SchedulerStateMachine::UpdateState(Action action) { |
| case ACTION_COMMIT: { |
| bool commit_was_aborted = false; |
| - HandleCommitInternal(commit_was_aborted); |
| + UpdateStateOnCommit(commit_was_aborted); |
| return; |
| } |
| @@ -503,6 +481,70 @@ void SchedulerStateMachine::UpdateState(Action action) { |
| } |
| } |
| +void SchedulerStateMachine::UpdateStateOnCommit(bool commit_was_aborted) { |
| + commit_count_++; |
| + |
| + // If we are impl-side-painting but the commit was aborted, then we behave |
| + // mostly as if we are not impl-side-painting since there is no pending tree. |
| + has_pending_tree_ = |
| + settings_.impl_side_painting && !commit_was_aborted; |
| + |
| + // Update the commit state. |
| + if (expect_immediate_begin_frame_for_main_thread_) |
| + commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW; |
| + else if (!commit_was_aborted) |
| + commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW; |
| + else |
| + commit_state_ = COMMIT_STATE_IDLE; |
| + |
| + // Update the output surface state. |
| + DCHECK_NE(output_surface_state_, |
| + OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION); |
| + if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT) { |
| + if (has_pending_tree_) { |
| + output_surface_state_ = OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION; |
| + } else { |
| + output_surface_state_ = OUTPUT_SURFACE_ACTIVE; |
| + needs_redraw_ = true; |
| + } |
| + } |
| + |
| + // Update state if we have a new active tree to draw. |
| + if (!has_pending_tree_ && |
| + (!commit_was_aborted || expect_immediate_begin_frame_for_main_thread_)) { |
| + needs_redraw_ = true; |
| + active_tree_has_been_drawn_ = false; |
| + } |
| + |
| + // This post-commit work is common to both completed and aborted commits. |
| + pending_tree_is_ready_for_activation_ = false; |
| + |
| + if (draw_if_possible_failed_) |
| + last_frame_number_where_draw_was_called_ = -1; |
| + |
| + if (needs_forced_redraw_after_next_commit_) { |
| + needs_forced_redraw_after_next_commit_ = false; |
| + needs_forced_redraw_ = true; |
| + } |
| + |
| + // If we are planing to draw with the new commit, lock the layer textures for |
| + // use on the impl thread. Otherwise, leave them unlocked. |
| + if (has_pending_tree_ || needs_redraw_ || needs_forced_redraw_) |
| + texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD; |
| + else |
| + texture_state_ = LAYER_TEXTURE_STATE_UNLOCKED; |
| +} |
| + |
| +void SchedulerStateMachine::UpdateStateOnActivation() { |
| + if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION) |
| + output_surface_state_ = OUTPUT_SURFACE_ACTIVE; |
| + |
| + has_pending_tree_ = false; |
| + pending_tree_is_ready_for_activation_ = false; |
| + active_tree_has_been_drawn_ = false; |
| + needs_redraw_ = true; |
| +} |
| + |
| void SchedulerStateMachine::UpdateStateOnDraw(bool did_swap) { |
|
enne (OOO)
2013/08/26 21:55:13
All of these UpdateStateOnFoo functions make the c
|
| if (inside_begin_frame_) |
| last_frame_number_where_draw_was_called_ = current_frame_number_; |
| @@ -519,6 +561,7 @@ void SchedulerStateMachine::UpdateStateOnDraw(bool did_swap) { |
| needs_redraw_ = false; |
| needs_forced_redraw_ = false; |
| draw_if_possible_failed_ = false; |
| + active_tree_has_been_drawn_ = true; |
| if (did_swap) |
| swap_used_incomplete_tile_ = false; |
| @@ -531,14 +574,6 @@ void SchedulerStateMachine::SetMainThreadNeedsLayerTextures() { |
| } |
| bool SchedulerStateMachine::BeginFrameNeededToDrawByImplThread() const { |
| - // TODO(brianderson): Remove this hack once the Scheduler controls activation, |
| - // otherwise we can get stuck in OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION. |
| - // It lies that we actually need to draw, but we won't actually draw |
| - // if we can't. This will cause WebView to potentially have corruption |
| - // in the first few frames, but this workaround is being removed soon. |
| - if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION) |
| - return true; |
| - |
| // If we can't draw, don't tick until we are notified that we can draw again. |
| if (!can_draw_) |
| return false; |
| @@ -554,7 +589,7 @@ bool SchedulerStateMachine::BeginFrameNeededToDrawByImplThread() const { |
| bool SchedulerStateMachine::ProactiveBeginFrameWantedByImplThread() const { |
| // Do not be proactive when invisible. |
| - if (!visible_ || output_surface_state_ != OUTPUT_SURFACE_ACTIVE) |
| + if (!visible_ || !HasInitializedOutputSurface()) |
| return false; |
| // We should proactively request a BeginFrame if a commit or a tree activation |
| @@ -623,7 +658,7 @@ void SchedulerStateMachine::BeginFrameAbortedByMainThread(bool did_handle) { |
| DCHECK_EQ(commit_state_, COMMIT_STATE_FRAME_IN_PROGRESS); |
| if (did_handle) { |
| bool commit_was_aborted = true; |
| - HandleCommitInternal(commit_was_aborted); |
| + UpdateStateOnCommit(commit_was_aborted); |
| } else if (expect_immediate_begin_frame_for_main_thread_) { |
| expect_immediate_begin_frame_for_main_thread_ = false; |
| } else { |
| @@ -640,15 +675,9 @@ void SchedulerStateMachine::DidLoseOutputSurface() { |
| needs_redraw_ = false; |
| } |
| -void SchedulerStateMachine::SetHasPendingTree(bool has_pending_tree) { |
| - if (has_pending_tree_ && !has_pending_tree) { |
| - // There is a new active tree. |
| - if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION) |
| - output_surface_state_ = OUTPUT_SURFACE_ACTIVE; |
| - |
| - needs_redraw_ = true; |
| - } |
| - has_pending_tree_ = has_pending_tree; |
| +void SchedulerStateMachine::NotifyReadyToActivate() { |
| + if (has_pending_tree_) |
| + pending_tree_is_ready_for_activation_ = true; |
| } |
| void SchedulerStateMachine::SetCanDraw(bool can) { can_draw_ = can; } |