Index: cc/scheduler/scheduler_state_machine.cc |
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc |
index ed9c15f43f9d13af92e585dbf9ed58c608e99ecc..6c2d879ae1b769cbbd6f914d230f5ec69c235dab 100644 |
--- a/cc/scheduler/scheduler_state_machine.cc |
+++ b/cc/scheduler/scheduler_state_machine.cc |
@@ -12,54 +12,64 @@ namespace cc { |
SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) |
: settings_(settings), |
+ output_surface_state_(OUTPUT_SURFACE_LOST), |
+ begin_frame_state_(BEGIN_FRAME_STATE_IDLE), |
commit_state_(COMMIT_STATE_IDLE), |
+ texture_state_(LAYER_TEXTURE_STATE_UNLOCKED), |
+ readback_state_(READBACK_STATE_IDLE), |
+ forced_redraw_state_(FORCED_REDRAW_STATE_IDLE), |
commit_count_(0), |
- 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), |
+ begin_frame_count_(0), |
+ draw_attempt_count_(0), |
+ last_begin_frame_count_draw_was_called_(-1), |
+ last_draw_attempt_count_draw_was_called_(-1), |
+ last_begin_frame_count_begin_frame_sent_to_main_thread_(-1), |
+ last_draw_attempt_count_tree_activation_attempted_(-1), |
+ last_draw_attempt_count_update_visible_tiles_was_called_(-1), |
consecutive_failed_draws_(0), |
maximum_number_of_failed_draws_before_draw_is_forced_(3), |
needs_redraw_(false), |
swap_used_incomplete_tile_(false), |
- needs_forced_redraw_(false), |
- needs_forced_redraw_after_next_commit_(false), |
- needs_redraw_after_next_commit_(false), |
needs_commit_(false), |
- needs_forced_commit_(false), |
- expect_immediate_begin_frame_for_main_thread_(false), |
main_thread_needs_layer_textures_(false), |
- inside_begin_frame_(false), |
+ active_tree_has_been_drawn_(false), |
+ active_tree_is_null_(true), |
visible_(false), |
can_start_(false), |
can_draw_(false), |
has_pending_tree_(false), |
+ pending_tree_is_ready_for_activation_(false), |
draw_if_possible_failed_(false), |
- texture_state_(LAYER_TEXTURE_STATE_UNLOCKED), |
- output_surface_state_(OUTPUT_SURFACE_LOST), |
did_create_and_initialize_first_output_surface_(false) {} |
std::string SchedulerStateMachine::ToString() { |
std::string str; |
- base::StringAppendF(&str, |
- "settings_.impl_side_painting = %d; ", |
+ base::StringAppendF(&str, "settings_.impl_side_painting = %d; ", |
settings_.impl_side_painting); |
+ base::StringAppendF(&str, "output_surface_state_ = %d; ", |
+ output_surface_state_); |
+ base::StringAppendF(&str, "begin_frame_state_ = %d; ", begin_frame_state_); |
base::StringAppendF(&str, "commit_state_ = %d; ", commit_state_); |
+ base::StringAppendF(&str, "texture_state_ = %d; ", texture_state_); |
+ base::StringAppendF(&str, "readback_state_ = %d; ", readback_state_); |
+ base::StringAppendF(&str, "forced_redraw_state_ = %d; ", |
+ forced_redraw_state_); |
base::StringAppendF(&str, "commit_count_ = %d; ", commit_count_); |
- base::StringAppendF( |
- &str, "current_frame_number_ = %d; ", current_frame_number_); |
+ base::StringAppendF(&str, "begin_frame_count_ = %d; ", begin_frame_count_); |
+ base::StringAppendF(&str, "draw_attempt_count_ = %d; ", draw_attempt_count_); |
+ base::StringAppendF(&str, "last_begin_frame_count_draw_was_called_ = %d; ", |
+ last_begin_frame_count_draw_was_called_); |
+ base::StringAppendF(&str, "last_draw_attempt_count_draw_was_called_ = %d; ", |
+ last_draw_attempt_count_draw_was_called_); |
base::StringAppendF(&str, |
- "last_frame_number_where_draw_was_called_ = %d; ", |
- last_frame_number_where_draw_was_called_); |
- base::StringAppendF( |
- &str, |
- "last_frame_number_where_tree_activation_attempted_ = %d; ", |
- last_frame_number_where_tree_activation_attempted_); |
- base::StringAppendF( |
- &str, |
- "last_frame_number_where_update_visible_tiles_was_called_ = %d; ", |
- last_frame_number_where_update_visible_tiles_was_called_); |
+ "last_begin_frame_count_begin_frame_sent_to_main_thread_ = %d; ", |
+ last_begin_frame_count_begin_frame_sent_to_main_thread_); |
+ base::StringAppendF(&str, |
+ "last_draw_attempt_count_tree_activation_attempted_ = %d; ", |
+ last_draw_attempt_count_tree_activation_attempted_); |
+ base::StringAppendF(&str, |
+ "last_draw_attempt_count_update_visible_tiles_was_called_ = %d; ", |
+ last_draw_attempt_count_update_visible_tiles_was_called_); |
base::StringAppendF( |
&str, "consecutive_failed_draws_ = %d; ", consecutive_failed_draws_); |
base::StringAppendF( |
@@ -69,22 +79,10 @@ std::string SchedulerStateMachine::ToString() { |
base::StringAppendF(&str, "needs_redraw_ = %d; ", needs_redraw_); |
base::StringAppendF( |
&str, "swap_used_incomplete_tile_ = %d; ", swap_used_incomplete_tile_); |
- base::StringAppendF( |
- &str, "needs_forced_redraw_ = %d; ", needs_forced_redraw_); |
- base::StringAppendF(&str, |
- "needs_forced_redraw_after_next_commit_ = %d; ", |
- needs_forced_redraw_after_next_commit_); |
base::StringAppendF(&str, "needs_commit_ = %d; ", needs_commit_); |
- base::StringAppendF( |
- &str, "needs_forced_commit_ = %d; ", needs_forced_commit_); |
- base::StringAppendF(&str, |
- "expect_immediate_begin_frame_for_main_thread_ = %d; ", |
- expect_immediate_begin_frame_for_main_thread_); |
base::StringAppendF(&str, |
"main_thread_needs_layer_textures_ = %d; ", |
main_thread_needs_layer_textures_); |
- base::StringAppendF(&str, "inside_begin_frame_ = %d; ", |
- inside_begin_frame_); |
base::StringAppendF(&str, "last_frame_time_ = %" PRId64 "; ", |
(last_begin_frame_args_.frame_time - base::TimeTicks()) |
.InMilliseconds()); |
@@ -98,36 +96,89 @@ std::string SchedulerStateMachine::ToString() { |
base::StringAppendF( |
&str, "draw_if_possible_failed_ = %d; ", draw_if_possible_failed_); |
base::StringAppendF(&str, "has_pending_tree_ = %d; ", has_pending_tree_); |
- base::StringAppendF(&str, "texture_state_ = %d; ", texture_state_); |
- base::StringAppendF( |
- &str, "output_surface_state_ = %d; ", output_surface_state_); |
+ base::StringAppendF(&str, |
+ "pending_tree_is_ready_for_activation_ = %d; ", |
+ pending_tree_is_ready_for_activation_); |
+ base::StringAppendF(&str, "active_tree_has_been_drawn_ = %d; ", |
+ active_tree_has_been_drawn_); |
+ base::StringAppendF(&str, "active_tree_is_null_ = %d; ", |
+ active_tree_is_null_); |
return str; |
} |
bool SchedulerStateMachine::HasDrawnThisFrame() const { |
- return current_frame_number_ == last_frame_number_where_draw_was_called_; |
+ return begin_frame_count_ == last_begin_frame_count_draw_was_called_; |
} |
-bool SchedulerStateMachine::HasAttemptedTreeActivationThisFrame() const { |
- return current_frame_number_ == |
- last_frame_number_where_tree_activation_attempted_; |
+bool SchedulerStateMachine::HasDrawnThisDrawAttempt() const { |
+ return draw_attempt_count_ == last_draw_attempt_count_draw_was_called_; |
} |
-bool SchedulerStateMachine::HasUpdatedVisibleTilesThisFrame() const { |
- return current_frame_number_ == |
- last_frame_number_where_update_visible_tiles_was_called_; |
+bool SchedulerStateMachine::HasSentBeginFrameToMainThreadThisFrame() const { |
+ return begin_frame_count_ == |
+ last_begin_frame_count_begin_frame_sent_to_main_thread_; |
} |
-void SchedulerStateMachine::SetPostCommitFlags() { |
- // This post-commit work is common to both completed and aborted commits. |
- if (needs_forced_redraw_after_next_commit_) { |
- needs_forced_redraw_after_next_commit_ = false; |
- needs_forced_redraw_ = true; |
- } |
- if (needs_redraw_after_next_commit_) { |
- needs_redraw_after_next_commit_ = false; |
- needs_redraw_ = true; |
+bool SchedulerStateMachine::HasUpdatedVisibleTilesThisDrawAttempt() const { |
+ return draw_attempt_count_ == |
+ last_draw_attempt_count_update_visible_tiles_was_called_; |
+} |
+ |
+bool SchedulerStateMachine::HasActivatedPendingTreeThisDrawAttempt() const { |
+ return draw_attempt_count_ == |
+ last_draw_attempt_count_tree_activation_attempted_; |
+} |
+ |
+void SchedulerStateMachine::HandleCommitInternal(bool commit_was_aborted) { |
+ commit_count_++; |
+ draw_attempt_count_++; |
+ |
+ // If we are impl-side-painting but the commit was aborted, then we behave |
+ // as if we are not impl-side-painting since there is no pending tree. |
+ if (settings_.impl_side_painting && !commit_was_aborted) { |
+ commit_state_ = COMMIT_STATE_WAITING_FOR_ACTIVATION; |
+ |
+ if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT) |
+ output_surface_state_ = OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION; |
+ |
+ if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_COMMIT) |
+ forced_redraw_state_ = FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION; |
+ else if (readback_state_ == READBACK_STATE_FORCED_COMMIT_PENDING) |
+ readback_state_ = READBACK_STATE_WAITING_FOR_ACTIVATION; |
+ else if (readback_state_ == READBACK_STATE_REPLACEMENT_COMMIT_PENDING) |
+ readback_state_ = READBACK_STATE_REPLACEMENT_COMMIT_ACTIVATING; |
+ |
+ } else { |
+ if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT) { |
+ output_surface_state_ = OUTPUT_SURFACE_ACTIVE; |
+ needs_redraw_ = true; |
+ } |
+ |
+ if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_COMMIT) |
+ forced_redraw_state_ = FORCED_REDRAW_STATE_WAITING_FOR_DRAW; |
+ else if (readback_state_ == READBACK_STATE_FORCED_COMMIT_PENDING) |
+ readback_state_ = READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK; |
+ else if (readback_state_ == READBACK_STATE_REPLACEMENT_COMMIT_PENDING) |
+ readback_state_ = READBACK_STATE_IDLE; |
+ |
+ if (draw_if_possible_failed_) { |
+ last_begin_frame_count_draw_was_called_ = -1; |
+ last_draw_attempt_count_draw_was_called_ = -1; |
+ } |
+ |
+ if (commit_was_aborted) { |
+ if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW || |
+ readback_state_ == READBACK_STATE_FORCED_COMMIT_PENDING) |
+ commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW; |
+ else |
+ commit_state_ = COMMIT_STATE_IDLE; |
+ } else { |
+ commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW; |
+ active_tree_has_been_drawn_ = false; |
+ needs_redraw_ = true; |
+ } |
} |
+ |
texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD; |
} |
@@ -150,12 +201,43 @@ bool SchedulerStateMachine::ScheduledToDraw() const { |
} |
bool SchedulerStateMachine::ShouldDraw() const { |
- if (needs_forced_redraw_) |
+ if (HasDrawnThisDrawAttempt()) |
+ return false; |
+ |
+ // After a readback, make sure not to draw again until we've replaced the |
+ // readback commit with a real one. |
+ if (readback_state_ == READBACK_STATE_REPLACEMENT_COMMIT_PENDING || |
+ readback_state_ == READBACK_STATE_REPLACEMENT_COMMIT_ACTIVATING) |
+ return false; |
+ |
+ // Draw outside of the BeginFrame deadline for readbacks. |
+ if (readback_state_ == READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK) { |
+ DCHECK_EQ(commit_state_, COMMIT_STATE_WAITING_FOR_FIRST_DRAW); |
return true; |
+ } |
- if (!ScheduledToDraw()) |
+ // If we've lost the output surface or draw is suspended, we want to be able |
+ // to abort a draw and make forward progress to unblock the main thread. |
+ if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE || |
+ DrawSuspendedUntilCommit()) { |
+ return !active_tree_has_been_drawn_ && |
+ (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_DRAW || |
+ commit_state_ == COMMIT_STATE_WAITING_FOR_ACTIVATION || |
+ (output_surface_state_ != OUTPUT_SURFACE_ACTIVE && |
+ !active_tree_is_null_)); |
+ } |
+ |
+ // Except for the cases above, do not draw outside of the BeginFrame deadline. |
+ if (begin_frame_state_ != BEGIN_FRAME_STATE_INSIDE_DEADLINE) |
return false; |
- if (!inside_begin_frame_) |
+ |
+ // Only handle forced redraws due to timeouts on the regular deadline. |
+ if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) { |
+ DCHECK_EQ(commit_state_, COMMIT_STATE_WAITING_FOR_FIRST_DRAW); |
+ return true; |
+ } |
+ |
+ if (!ScheduledToDraw()) |
return false; |
if (HasDrawnThisFrame()) |
return false; |
@@ -164,18 +246,45 @@ bool SchedulerStateMachine::ShouldDraw() const { |
return true; |
} |
-bool SchedulerStateMachine::ShouldAttemptTreeActivation() const { |
- return has_pending_tree_ && inside_begin_frame_ && |
- !HasAttemptedTreeActivationThisFrame(); |
+bool SchedulerStateMachine::ShouldActivatePendingTree() const { |
+ // Some quick early outs. |
+ if (!has_pending_tree_) |
+ return false; |
+ if (HasActivatedPendingTreeThisDrawAttempt()) |
+ return false; |
+ |
+ // If this is our first activation, we don't want to activate early. |
+ if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION) |
+ return pending_tree_is_ready_for_activation_; |
+ |
+ // In these cases, we want to activate even if the pending tree isn't ready |
enne (OOO)
2013/08/02 19:41:25
This looks really good. This is what I was trying
|
+ // because we aren't going to draw the pending tree or need to make |
+ // forward progress to unblock the main thread. |
+ if (!visible_ || |
+ output_surface_state_ == OUTPUT_SURFACE_LOST || |
+ output_surface_state_ == OUTPUT_SURFACE_CREATING || |
+ output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT) |
+ return true; |
+ |
+ // After this line, we only want to activate if we are ready to activate. |
+ if (!pending_tree_is_ready_for_activation_) |
+ return false; |
+ |
enne (OOO)
2013/08/02 19:41:25
This comment doesn't really add much, and I don't
brianderson
2013/08/02 23:04:31
Good catch! This needlessly delays activation. I p
|
+ // Do not activate in BEGIN_FRAME_STATE_IDLE |
+ if (begin_frame_state_ == BEGIN_FRAME_STATE_IDLE) |
+ return false; |
+ |
+ // We do not want to activate a second tree before drawing the first one. |
enne (OOO)
2013/08/02 19:41:25
How is that possible? Maybe I'm misreading other p
brianderson
2013/08/02 23:04:31
In this patch, the draw no longer has to occur bef
|
+ return active_tree_has_been_drawn_ || active_tree_is_null_; |
enne (OOO)
2013/08/02 19:41:25
There are other reasons that the active tree might
brianderson
2013/08/02 23:04:31
In cases where can_draw_ is false, ACTION_DRAW_AND
enne (OOO)
2013/08/05 19:37:43
In that case, shouldn't that logic to set active_t
brianderson
2013/08/05 21:56:19
As it is currently coded, ShouldDraw() will not re
|
} |
bool SchedulerStateMachine::ShouldUpdateVisibleTiles() const { |
if (!settings_.impl_side_painting) |
return false; |
- if (HasUpdatedVisibleTilesThisFrame()) |
+ if (HasUpdatedVisibleTilesThisDrawAttempt()) |
return false; |
- return ShouldAttemptTreeActivation() || ShouldDraw() || |
+ return ShouldActivatePendingTree() || ShouldDraw() || |
swap_used_incomplete_tile_; |
} |
@@ -194,89 +303,136 @@ bool SchedulerStateMachine::ShouldAcquireLayerTexturesForMainThread() const { |
return false; |
} |
+bool SchedulerStateMachine::ShouldSendBeginFrameToMainThread() const { |
+ if (HasSentBeginFrameToMainThreadThisFrame()) |
+ return false; |
+ |
+ // Only send BeginFrame to the main thread when idle. |
+ if (commit_state_ != COMMIT_STATE_IDLE) |
+ return false; |
+ |
+ // We can't accept a commit if we have a pending tree. |
+ if (has_pending_tree_) |
+ return false; |
+ |
+ // We want to handle readback commits ASAP. |
+ if (readback_state_ == READBACK_STATE_FORCED_COMMIT_REQUESTED) |
+ return !CommitPending(); |
+ |
+ // We want to start the first commit after we get a new output surface |
+ // immediately. |
+ if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT |
+ && needs_commit_) |
+ return true; |
+ |
+ // We usually want to slave the main thread's BeginFrame to the impl thread's |
+ // BeginFrame. However, if we aren't expecting a BeginFrame on the impl |
+ // thread, we should send a BeginFrame to the main thread anyway to make |
+ // progress. |
+ // TODO(brianderson): Also allow sending BeginFrame to main thread while idle |
+ // when the main thread isn't consuming user input. |
+ if (begin_frame_state_ == BEGIN_FRAME_STATE_IDLE && |
+ BeginFrameNeededByImplThread()) |
+ return false; |
+ |
+ // Do not send begin frame to main thread in the deadline until we have drawn. |
+ // We have a train to catch! |
+ if (begin_frame_state_ == BEGIN_FRAME_STATE_INSIDE_DEADLINE && |
+ !HasDrawnThisFrame()) |
+ return false; |
+ |
+ // If not impl-side-painting, do not send a BeginFrame to the main thread if |
+ // we haven't drawn the active tree. Otherwise, the commit overwrite the |
+ // active tree before we have a chance to draw it. |
+ if (!settings_.impl_side_painting && |
+ !active_tree_has_been_drawn_ && |
+ !active_tree_is_null_) |
+ return false; |
+ |
+ bool can_commit = needs_commit_ && |
+ (visible_ || |
+ forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_COMMIT); |
+ return can_commit && |
+ (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_COMMIT || |
+ HasInitializedOutputSurface()); |
+} |
+ |
SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const { |
if (ShouldAcquireLayerTexturesForMainThread()) |
return ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD; |
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; |
+ case COMMIT_STATE_IDLE: |
+ if (ShouldSendBeginFrameToMainThread()) |
+ return 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; |
+ if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE || |
+ DrawSuspendedUntilCommit()) |
+ return ACTION_DRAW_AND_SWAP_ABORT; |
+ else |
+ return ACTION_DRAW_AND_SWAP_IF_POSSIBLE; |
} |
- bool can_commit_this_frame = |
- visible_ && |
- current_frame_number_ > |
- last_frame_number_where_begin_frame_sent_to_main_thread_; |
- if (needs_commit_ && ((can_commit_this_frame && |
- 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; |
return ACTION_NONE; |
- } |
+ |
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; |
+ if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE || |
+ DrawSuspendedUntilCommit()) |
+ return ACTION_DRAW_AND_SWAP_ABORT; |
+ else |
+ return ACTION_DRAW_AND_SWAP_IF_POSSIBLE; |
} |
return ACTION_NONE; |
case COMMIT_STATE_READY_TO_COMMIT: |
return ACTION_COMMIT; |
- case COMMIT_STATE_WAITING_FOR_FIRST_DRAW: { |
+ case COMMIT_STATE_WAITING_FOR_ACTIVATION: { |
if (ShouldUpdateVisibleTiles()) |
return ACTION_UPDATE_VISIBLE_TILES; |
- if (ShouldAttemptTreeActivation()) |
- return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED; |
- if (ShouldDraw() || output_surface_state_ == OUTPUT_SURFACE_LOST) { |
- return needs_forced_redraw_ ? ACTION_DRAW_FORCED |
- : ACTION_DRAW_IF_POSSIBLE; |
+ if (ShouldActivatePendingTree()) |
+ return ACTION_ACTIVATE_PENDING_TREE; |
+ if (ShouldDraw()) { |
+ if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE || |
+ DrawSuspendedUntilCommit()) |
+ return ACTION_DRAW_AND_SWAP_ABORT; |
+ else |
+ return ACTION_DRAW_AND_SWAP_IF_POSSIBLE; |
} |
- // COMMIT_STATE_WAITING_FOR_FIRST_DRAW wants to enforce a draw. If |
- // can_draw_ is false or textures are not available, proceed to the next |
- // step (similar as in COMMIT_STATE_IDLE). |
- bool can_commit = |
- needs_forced_commit_ || |
- (visible_ && |
- current_frame_number_ > |
- last_frame_number_where_begin_frame_sent_to_main_thread_); |
- if (needs_commit_ && can_commit && DrawSuspendedUntilCommit()) |
- return has_pending_tree_ ? ACTION_NONE |
- : ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD; |
return ACTION_NONE; |
} |
- case COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW: |
+ case COMMIT_STATE_WAITING_FOR_FIRST_DRAW: { |
if (ShouldUpdateVisibleTiles()) |
return ACTION_UPDATE_VISIBLE_TILES; |
- if (ShouldAttemptTreeActivation()) |
- return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED; |
- if (needs_forced_redraw_) |
- return ACTION_DRAW_FORCED; |
+ if (ShouldActivatePendingTree()) |
+ return ACTION_ACTIVATE_PENDING_TREE; |
+ if (ShouldDraw()) { |
+ if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) |
+ return ACTION_DRAW_AND_SWAP_FORCED; |
+ else if (readback_state_ == |
+ READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK) |
+ return ACTION_DRAW_AND_READBACK; |
+ else if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE || |
+ DrawSuspendedUntilCommit()) |
+ return ACTION_DRAW_AND_SWAP_ABORT; |
+ else |
+ return ACTION_DRAW_AND_SWAP_IF_POSSIBLE; |
+ } |
return ACTION_NONE; |
+ } |
} |
NOTREACHED(); |
return ACTION_NONE; |
@@ -288,60 +444,56 @@ void SchedulerStateMachine::UpdateState(Action action) { |
return; |
case ACTION_UPDATE_VISIBLE_TILES: |
- last_frame_number_where_update_visible_tiles_was_called_ = |
- current_frame_number_; |
+ last_draw_attempt_count_update_visible_tiles_was_called_ = |
+ draw_attempt_count_; |
return; |
- case ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED: |
- last_frame_number_where_tree_activation_attempted_ = |
- current_frame_number_; |
+ case ACTION_ACTIVATE_PENDING_TREE: |
+ last_draw_attempt_count_tree_activation_attempted_ = |
+ draw_attempt_count_; |
return; |
case ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD: |
DCHECK(!has_pending_tree_); |
- if (!needs_forced_commit_) { |
- DCHECK(visible_); |
- DCHECK_GT(current_frame_number_, |
- last_frame_number_where_begin_frame_sent_to_main_thread_); |
- } |
+ DCHECK(visible_ || |
+ readback_state_ == READBACK_STATE_FORCED_COMMIT_REQUESTED || |
+ forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_COMMIT || |
+ output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT); |
commit_state_ = COMMIT_STATE_FRAME_IN_PROGRESS; |
needs_commit_ = false; |
- needs_forced_commit_ = false; |
- last_frame_number_where_begin_frame_sent_to_main_thread_ = |
- current_frame_number_; |
+ if (readback_state_ == READBACK_STATE_FORCED_COMMIT_REQUESTED) |
+ readback_state_ = READBACK_STATE_FORCED_COMMIT_PENDING; |
+ last_begin_frame_count_begin_frame_sent_to_main_thread_ = |
+ begin_frame_count_; |
return; |
case ACTION_COMMIT: |
- commit_count_++; |
- if (expect_immediate_begin_frame_for_main_thread_) |
- commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW; |
- else |
- commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW; |
- // When impl-side painting, we draw on activation instead of on commit. |
- if (!settings_.impl_side_painting) |
- needs_redraw_ = true; |
- if (draw_if_possible_failed_) |
- last_frame_number_where_draw_was_called_ = -1; |
- SetPostCommitFlags(); |
+ HandleCommitInternal(false); |
return; |
- case ACTION_DRAW_FORCED: |
- case ACTION_DRAW_IF_POSSIBLE: |
- needs_redraw_ = false; |
- needs_forced_redraw_ = false; |
- draw_if_possible_failed_ = false; |
- swap_used_incomplete_tile_ = false; |
- if (inside_begin_frame_) |
- last_frame_number_where_draw_was_called_ = current_frame_number_; |
- if (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW) { |
- DCHECK(expect_immediate_begin_frame_for_main_thread_); |
+ case ACTION_DRAW_AND_SWAP_IF_POSSIBLE: |
+ case ACTION_DRAW_AND_SWAP_FORCED: |
+ case ACTION_DRAW_AND_READBACK: |
+ case ACTION_DRAW_AND_SWAP_ABORT: |
+ if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) { |
+ commit_state_ = COMMIT_STATE_IDLE; |
+ forced_redraw_state_ = FORCED_REDRAW_STATE_IDLE; |
+ } else if (readback_state_ == |
+ READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK) { |
+ DCHECK(!has_pending_tree_); |
commit_state_ = COMMIT_STATE_FRAME_IN_PROGRESS; |
- expect_immediate_begin_frame_for_main_thread_ = false; |
+ readback_state_ = READBACK_STATE_REPLACEMENT_COMMIT_PENDING; |
} else if (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_DRAW) { |
commit_state_ = COMMIT_STATE_IDLE; |
} |
+ needs_redraw_ = false; |
+ draw_if_possible_failed_ = false; |
+ swap_used_incomplete_tile_ = false; |
+ last_begin_frame_count_draw_was_called_ = begin_frame_count_; |
+ last_draw_attempt_count_draw_was_called_ = draw_attempt_count_; |
if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD) |
texture_state_ = LAYER_TEXTURE_STATE_UNLOCKED; |
+ active_tree_has_been_drawn_ = true; |
return; |
case ACTION_BEGIN_OUTPUT_SURFACE_CREATION: |
@@ -357,49 +509,88 @@ void SchedulerStateMachine::UpdateState(Action action) { |
} |
} |
+void SchedulerStateMachine::AdvanceBeginFrameStateWhenNoActionsRemain() { |
+ switch (begin_frame_state_) { |
+ case BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME: |
+ begin_frame_state_ = BEGIN_FRAME_STATE_DEADLINE_PENDING; |
+ break; |
+ case BEGIN_FRAME_STATE_INSIDE_DEADLINE: |
+ begin_frame_state_ = BEGIN_FRAME_STATE_IDLE; |
+ break; |
+ case BEGIN_FRAME_STATE_IDLE: |
+ case BEGIN_FRAME_STATE_DEADLINE_PENDING: |
+ break; |
+ } |
+} |
+ |
void SchedulerStateMachine::SetMainThreadNeedsLayerTextures() { |
DCHECK(!main_thread_needs_layer_textures_); |
DCHECK_NE(texture_state_, LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD); |
main_thread_needs_layer_textures_ = true; |
} |
+bool SchedulerStateMachine::BeginFrameNeededByImplThread() const { |
+ return BeginFrameNeededToDrawByImplThread() || |
+ BeginFrameProactivelyNeededByImplThread(); |
+} |
+ |
bool SchedulerStateMachine::BeginFrameNeededToDrawByImplThread() const { |
// If we can't draw, don't tick until we are notified that we can draw again. |
if (!can_draw_) |
return false; |
- if (needs_forced_redraw_) |
+ if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) |
return true; |
if (visible_ && swap_used_incomplete_tile_) |
return true; |
- return needs_redraw_ && visible_ && |
- output_surface_state_ == OUTPUT_SURFACE_ACTIVE; |
+ return needs_redraw_ && visible_ && HasInitializedOutputSurface(); |
} |
-bool SchedulerStateMachine::ProactiveBeginFrameWantedByImplThread() const { |
- // Do not be proactive when invisible. |
- if (!visible_ || output_surface_state_ != OUTPUT_SURFACE_ACTIVE) |
+bool SchedulerStateMachine::BeginFrameProactivelyNeededByImplThread() const { |
+ if (settings_.using_synchronous_renderer_compositor) |
+ return false; |
+ |
+ if (!settings_.throttle_frame_production) |
+ return false; |
+ |
+ if (!visible_ || !HasInitializedOutputSurface()) |
return false; |
// We should proactively request a BeginFrame if a commit or a tree activation |
// is pending. |
- return (needs_commit_ || needs_forced_commit_ || |
- commit_state_ != COMMIT_STATE_IDLE || has_pending_tree_); |
+ return (needs_commit_ || |
+ commit_state_ != COMMIT_STATE_IDLE || |
+ has_pending_tree_); |
} |
-void SchedulerStateMachine::DidEnterBeginFrame(const BeginFrameArgs& args) { |
- current_frame_number_++; |
- inside_begin_frame_ = true; |
+void SchedulerStateMachine::OnBeginFrame(const BeginFrameArgs& args) { |
+ begin_frame_count_++; |
+ draw_attempt_count_++; |
last_begin_frame_args_ = args; |
+ begin_frame_state_ = BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME; |
+} |
+ |
+bool SchedulerStateMachine::ShouldTriggerBeginFrameDeadlineEarly() const { |
+ // TODO(brianderson): This should take into account multiple commit sources. |
+ return begin_frame_state_ == BEGIN_FRAME_STATE_DEADLINE_PENDING && |
+ !active_tree_has_been_drawn_; |
+} |
+ |
+bool SchedulerStateMachine::InsideBeginFrame() const { |
+ return begin_frame_state_ != BEGIN_FRAME_STATE_IDLE; |
} |
-void SchedulerStateMachine::DidLeaveBeginFrame() { |
- inside_begin_frame_ = false; |
+void SchedulerStateMachine::OnBeginFrameDeadline() { |
+ DCHECK_EQ(begin_frame_state_, BEGIN_FRAME_STATE_DEADLINE_PENDING); |
+ draw_attempt_count_++; |
+ begin_frame_state_ = BEGIN_FRAME_STATE_INSIDE_DEADLINE; |
} |
-void SchedulerStateMachine::SetVisible(bool visible) { visible_ = visible; } |
+void SchedulerStateMachine::SetVisible(bool visible) { |
+ visible_ = visible; |
+} |
void SchedulerStateMachine::SetNeedsRedraw() { needs_redraw_ = true; } |
@@ -407,10 +598,6 @@ void SchedulerStateMachine::DidSwapUseIncompleteTile() { |
swap_used_incomplete_tile_ = true; |
} |
-void SchedulerStateMachine::SetNeedsForcedRedraw() { |
- needs_forced_redraw_ = true; |
-} |
- |
void SchedulerStateMachine::DidDrawIfPossibleCompleted(bool success) { |
draw_if_possible_failed_ = !success; |
if (draw_if_possible_failed_) { |
@@ -423,7 +610,7 @@ void SchedulerStateMachine::DidDrawIfPossibleCompleted(bool success) { |
consecutive_failed_draws_ = 0; |
// We need to force a draw, but it doesn't make sense to do this until |
// we've committed and have new textures. |
- needs_forced_redraw_after_next_commit_ = true; |
+ forced_redraw_state_ = FORCED_REDRAW_STATE_WAITING_FOR_COMMIT; |
} |
} else { |
consecutive_failed_draws_ = 0; |
@@ -432,26 +619,27 @@ void SchedulerStateMachine::DidDrawIfPossibleCompleted(bool success) { |
void SchedulerStateMachine::SetNeedsCommit() { needs_commit_ = true; } |
-void SchedulerStateMachine::SetNeedsForcedCommit() { |
- needs_forced_commit_ = true; |
- expect_immediate_begin_frame_for_main_thread_ = true; |
+void SchedulerStateMachine::SetNeedsForcedCommitForReadback() { |
+ DCHECK_EQ(readback_state_, READBACK_STATE_IDLE); |
+ if (commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS) |
+ readback_state_ = READBACK_STATE_FORCED_COMMIT_PENDING; |
+ else |
+ readback_state_ = READBACK_STATE_FORCED_COMMIT_REQUESTED; |
} |
void SchedulerStateMachine::FinishCommit() { |
DCHECK(commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS || |
- (expect_immediate_begin_frame_for_main_thread_ && |
- commit_state_ != COMMIT_STATE_IDLE)) |
+ readback_state_ == READBACK_STATE_FORCED_COMMIT_PENDING) |
<< ToString(); |
commit_state_ = COMMIT_STATE_READY_TO_COMMIT; |
} |
void SchedulerStateMachine::BeginFrameAbortedByMainThread(bool did_handle) { |
DCHECK_EQ(commit_state_, COMMIT_STATE_FRAME_IN_PROGRESS); |
- if (expect_immediate_begin_frame_for_main_thread_) { |
- expect_immediate_begin_frame_for_main_thread_ = false; |
+ if (readback_state_ == READBACK_STATE_FORCED_COMMIT_REQUESTED) { |
+ readback_state_ = READBACK_STATE_IDLE; |
} else if (did_handle) { |
- commit_state_ = COMMIT_STATE_IDLE; |
- SetPostCommitFlags(); |
+ HandleCommitInternal(true); |
} else { |
commit_state_ = COMMIT_STATE_IDLE; |
SetNeedsCommit(); |
@@ -463,33 +651,67 @@ void SchedulerStateMachine::DidLoseOutputSurface() { |
output_surface_state_ == OUTPUT_SURFACE_CREATING) |
return; |
output_surface_state_ = OUTPUT_SURFACE_LOST; |
+ needs_redraw_ = false; |
+} |
+ |
+void SchedulerStateMachine::NotifyReadyToActivate() { |
+ if (has_pending_tree_) |
+ pending_tree_is_ready_for_activation_ = true; |
} |
-void SchedulerStateMachine::SetHasPendingTree(bool has_pending_tree) { |
+void SchedulerStateMachine::SetHasTrees(bool has_pending_tree, |
+ bool active_tree_is_null) { |
+ active_tree_is_null_ = active_tree_is_null; |
+ if (has_pending_tree_ && !has_pending_tree) { |
+ // There is a new active tree. |
+ draw_attempt_count_++; |
+ pending_tree_is_ready_for_activation_ = false; |
+ active_tree_has_been_drawn_ = false; |
+ needs_redraw_ = true; |
+ |
+ if (draw_if_possible_failed_) { |
+ last_begin_frame_count_draw_was_called_ = -1; |
+ last_draw_attempt_count_draw_was_called_ = -1; |
+ } |
+ |
+ if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION) |
+ output_surface_state_ = OUTPUT_SURFACE_ACTIVE; |
+ |
+ DCHECK_EQ(COMMIT_STATE_WAITING_FOR_ACTIVATION, commit_state_); |
+ if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION) { |
+ commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW; |
+ forced_redraw_state_ = FORCED_REDRAW_STATE_WAITING_FOR_DRAW; |
+ } else if (readback_state_ == READBACK_STATE_WAITING_FOR_ACTIVATION) { |
+ commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW; |
+ readback_state_ = READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK; |
+ } else { |
+ commit_state_ = COMMIT_STATE_IDLE; |
+ if (readback_state_ == READBACK_STATE_REPLACEMENT_COMMIT_ACTIVATING) |
+ readback_state_ = READBACK_STATE_IDLE; |
+ } |
+ } else if (!has_pending_tree_ && has_pending_tree) { |
+ // There is a new pending tree. |
+ pending_tree_is_ready_for_activation_ = false; |
+ } |
+ |
has_pending_tree_ = has_pending_tree; |
} |
-void SchedulerStateMachine::SetCanDraw(bool can) { can_draw_ = can; } |
+void SchedulerStateMachine::SetCanDraw(bool can_draw) { |
+ can_draw_ = can_draw; |
+} |
void SchedulerStateMachine::DidCreateAndInitializeOutputSurface() { |
DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_CREATING); |
- output_surface_state_ = OUTPUT_SURFACE_ACTIVE; |
- |
- if (did_create_and_initialize_first_output_surface_) { |
- // TODO(boliu): See if we can remove this when impl-side painting is always |
- // on. Does anything on the main thread need to update after recreate? |
+ output_surface_state_ = OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT; |
+ if (did_create_and_initialize_first_output_surface_) |
needs_commit_ = true; |
- // If anything has requested a redraw, we don't want to actually draw |
- // when the output surface is restored until things have a chance to |
- // sort themselves out with a commit. |
- needs_redraw_ = false; |
- } |
- needs_redraw_after_next_commit_ = true; |
did_create_and_initialize_first_output_surface_ = true; |
} |
bool SchedulerStateMachine::HasInitializedOutputSurface() const { |
- return output_surface_state_ == OUTPUT_SURFACE_ACTIVE; |
+ return output_surface_state_ != OUTPUT_SURFACE_LOST && |
+ output_surface_state_ != OUTPUT_SURFACE_CREATING; |
} |
void SchedulerStateMachine::SetMaximumNumberOfFailedDrawsBeforeDrawIsForced( |