Index: cc/scheduler/scheduler_state_machine.cc |
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc |
index dcd1ac9f8c23b8f2c4b049a5b8b8a921f96d6ea6..15b6597f7ae5116c4aeb9a04cdbb14b85489b52a 100644 |
--- a/cc/scheduler/scheduler_state_machine.cc |
+++ b/cc/scheduler/scheduler_state_machine.cc |
@@ -26,10 +26,11 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) |
last_frame_number_swap_performed_(-1), |
last_frame_number_swap_requested_(-1), |
last_frame_number_begin_main_frame_sent_(-1), |
+ last_frame_number_invalidate_output_surface_performed_(-1), |
animate_funnel_(false), |
- perform_swap_funnel_(false), |
request_swap_funnel_(false), |
send_begin_main_frame_funnel_(false), |
+ invalidate_output_surface_funnel_(false), |
prepare_tiles_funnel_(0), |
consecutive_checkerboard_animations_(0), |
max_pending_swaps_(1), |
@@ -38,7 +39,6 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) |
needs_animate_(false), |
needs_prepare_tiles_(false), |
needs_commit_(false), |
- inside_poll_for_anticipated_draw_triggers_(false), |
visible_(false), |
can_start_(false), |
can_draw_(false), |
@@ -53,7 +53,9 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) |
children_need_begin_frames_(false), |
defer_commits_(false), |
last_commit_had_no_updates_(false), |
- wait_for_active_tree_ready_to_draw_(false) { |
+ wait_for_active_tree_ready_to_draw_(false), |
+ did_request_swap_in_last_frame_(false), |
+ did_perform_swap_in_last_draw_(false) { |
} |
const char* SchedulerStateMachine::OutputSurfaceStateToString( |
@@ -90,6 +92,24 @@ const char* SchedulerStateMachine::BeginImplFrameStateToString( |
return "???"; |
} |
+const char* SchedulerStateMachine::BeginImplFrameDeadlineModeToString( |
+ BeginImplFrameDeadlineMode mode) { |
+ switch (mode) { |
+ case BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE: |
+ return "BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE"; |
+ case BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE: |
+ return "BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE"; |
+ case BEGIN_IMPL_FRAME_DEADLINE_MODE_REGULAR: |
+ return "BEGIN_IMPL_FRAME_DEADLINE_MODE_REGULAR"; |
+ case BEGIN_IMPL_FRAME_DEADLINE_MODE_LATE: |
+ return "BEGIN_IMPL_FRAME_DEADLINE_MODE_LATE"; |
+ case BEGIN_IMPL_FRAME_DEADLINE_MODE_BLOCKED_ON_READY_TO_DRAW: |
+ return "BEGIN_IMPL_FRAME_DEADLINE_MODE_BLOCKED_ON_READY_TO_DRAW"; |
+ } |
+ NOTREACHED(); |
+ return "???"; |
+} |
+ |
const char* SchedulerStateMachine::CommitStateToString(CommitState state) { |
switch (state) { |
case COMMIT_STATE_IDLE: |
@@ -147,6 +167,8 @@ const char* SchedulerStateMachine::ActionToString(Action action) { |
return "ACTION_BEGIN_OUTPUT_SURFACE_CREATION"; |
case ACTION_PREPARE_TILES: |
return "ACTION_PREPARE_TILES"; |
+ case ACTION_INVALIDATE_OUTPUT_SURFACE: |
+ return "ACTION_INVALIDATE_OUTPUT_SURFACE"; |
} |
NOTREACHED(); |
return "???"; |
@@ -185,11 +207,12 @@ void SchedulerStateMachine::AsValueInto( |
state->SetInteger("last_frame_number_begin_main_frame_sent", |
last_frame_number_begin_main_frame_sent_); |
state->SetBoolean("funnel: animate_funnel", animate_funnel_); |
- state->SetBoolean("funnel: perform_swap_funnel", perform_swap_funnel_); |
state->SetBoolean("funnel: request_swap_funnel", request_swap_funnel_); |
state->SetBoolean("funnel: send_begin_main_frame_funnel", |
send_begin_main_frame_funnel_); |
state->SetInteger("funnel: prepare_tiles_funnel", prepare_tiles_funnel_); |
+ state->SetBoolean("funnel: invalidate_output_surface_funnel", |
+ invalidate_output_surface_funnel_); |
state->SetInteger("consecutive_checkerboard_animations", |
consecutive_checkerboard_animations_); |
state->SetInteger("max_pending_swaps_", max_pending_swaps_); |
@@ -221,26 +244,14 @@ void SchedulerStateMachine::AsValueInto( |
state->SetBoolean("continuous_painting", continuous_painting_); |
state->SetBoolean("children_need_begin_frames", children_need_begin_frames_); |
state->SetBoolean("defer_commits", defer_commits_); |
+ state->SetBoolean("last_commit_had_no_updates", last_commit_had_no_updates_); |
+ state->SetBoolean("did_request_swap_in_last_frame", |
+ did_request_swap_in_last_frame_); |
+ state->SetBoolean("did_perform_swap_in_last_draw", |
+ did_perform_swap_in_last_draw_); |
state->EndDictionary(); |
} |
-void SchedulerStateMachine::AdvanceCurrentFrameNumber() { |
- current_frame_number_++; |
- |
- animate_funnel_ = false; |
- perform_swap_funnel_ = false; |
- request_swap_funnel_ = false; |
- send_begin_main_frame_funnel_ = false; |
- |
- // "Drain" the PrepareTiles funnel. |
- if (prepare_tiles_funnel_ > 0) |
- prepare_tiles_funnel_--; |
- |
- skip_begin_main_frame_to_reduce_latency_ = |
- skip_next_begin_main_frame_to_reduce_latency_; |
- skip_next_begin_main_frame_to_reduce_latency_ = false; |
-} |
- |
bool SchedulerStateMachine::PendingDrawsShouldBeAborted() const { |
// These are all the cases where we normally cannot or do not want to draw |
// but, if needs_redraw_ is true and we do not draw to make forward progress, |
@@ -430,7 +441,7 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const { |
// TODO(brianderson): Remove this restriction to improve throughput. |
bool just_swapped_in_deadline = |
begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE && |
- perform_swap_funnel_; |
+ did_perform_swap_in_last_draw_; |
if (pending_swaps_ >= max_pending_swaps_ && !just_swapped_in_deadline) |
return false; |
@@ -465,14 +476,32 @@ bool SchedulerStateMachine::ShouldPrepareTiles() const { |
return false; |
// Limiting to once per-frame is not enough, since we only want to |
- // prepare tiles _after_ draws. Polling for draw triggers and |
- // begin-frame are mutually exclusive, so we limit to these two cases. |
- if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE && |
- !inside_poll_for_anticipated_draw_triggers_) |
+ // prepare tiles _after_ draws. |
+ if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE) |
return false; |
+ |
return needs_prepare_tiles_; |
} |
+bool SchedulerStateMachine::ShouldInvalidateOutputSurface() const { |
+ // Do not invalidate too many times in a frame. |
+ if (invalidate_output_surface_funnel_) |
+ return false; |
+ |
+ // Only the synchronous compositor requires invalidations. |
+ if (!settings_.using_synchronous_renderer_compositor) |
+ return false; |
+ |
+ // Invalidations are only performed inside a BeginFrame. |
+ if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING) |
+ return false; |
+ |
+ // TODO(sunnyps): needs_prepare_tiles_ is needed here because PrepareTiles is |
+ // called only inside the deadline / draw phase. We could remove this if we |
+ // allowed PrepareTiles to happen in OnBeginImplFrame. |
+ return needs_redraw_ || needs_prepare_tiles_; |
+} |
+ |
SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const { |
if (ShouldActivatePendingTree()) |
return ACTION_ACTIVATE_SYNC_TREE; |
@@ -492,6 +521,8 @@ SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const { |
return ACTION_PREPARE_TILES; |
if (ShouldSendBeginMainFrame()) |
return ACTION_SEND_BEGIN_MAIN_FRAME; |
+ if (ShouldInvalidateOutputSurface()) |
+ return ACTION_INVALIDATE_OUTPUT_SURFACE; |
if (ShouldBeginOutputSurfaceCreation()) |
return ACTION_BEGIN_OUTPUT_SURFACE_CREATION; |
return ACTION_NONE; |
@@ -507,25 +538,11 @@ void SchedulerStateMachine::UpdateState(Action action) { |
return; |
case ACTION_ANIMATE: |
- DCHECK(!animate_funnel_); |
- last_frame_number_animate_performed_ = current_frame_number_; |
- animate_funnel_ = true; |
- needs_animate_ = false; |
- // TODO(skyostil): Instead of assuming this, require the client to tell |
- // us. |
- SetNeedsRedraw(); |
+ UpdateStateOnAnimate(); |
return; |
case ACTION_SEND_BEGIN_MAIN_FRAME: |
- DCHECK(!has_pending_tree_ || |
- settings_.main_frame_before_activation_enabled); |
- DCHECK(visible_); |
- DCHECK(!send_begin_main_frame_funnel_); |
- commit_state_ = COMMIT_STATE_BEGIN_MAIN_FRAME_SENT; |
- needs_commit_ = false; |
- send_begin_main_frame_funnel_ = true; |
- last_frame_number_begin_main_frame_sent_ = |
- current_frame_number_; |
+ UpdateStateOnSendBeginMainFrame(); |
return; |
case ACTION_COMMIT: { |
@@ -548,26 +565,42 @@ void SchedulerStateMachine::UpdateState(Action action) { |
} |
case ACTION_BEGIN_OUTPUT_SURFACE_CREATION: |
- DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_LOST); |
- output_surface_state_ = OUTPUT_SURFACE_CREATING; |
- |
- // The following DCHECKs make sure we are in the proper quiescent state. |
- // The pipeline should be flushed entirely before we start output |
- // surface creation to avoid complicated corner cases. |
- DCHECK_EQ(commit_state_, COMMIT_STATE_IDLE); |
- DCHECK(!has_pending_tree_); |
- DCHECK(!active_tree_needs_first_draw_); |
+ UpdateStateOnBeginOutputSurfaceCreation(); |
return; |
case ACTION_PREPARE_TILES: |
UpdateStateOnPrepareTiles(); |
return; |
+ |
+ case ACTION_INVALIDATE_OUTPUT_SURFACE: |
+ UpdateStateOnInvalidateOutputSurface(); |
+ return; |
} |
} |
+void SchedulerStateMachine::UpdateStateOnAnimate() { |
+ DCHECK(!animate_funnel_); |
+ last_frame_number_animate_performed_ = current_frame_number_; |
+ animate_funnel_ = true; |
+ needs_animate_ = false; |
+ // TODO(skyostil): Instead of assuming this, require the client to tell us. |
+ SetNeedsRedraw(); |
+} |
+ |
+void SchedulerStateMachine::UpdateStateOnSendBeginMainFrame() { |
+ DCHECK(!has_pending_tree_ || settings_.main_frame_before_activation_enabled); |
+ DCHECK(visible_); |
+ DCHECK(!send_begin_main_frame_funnel_); |
+ commit_state_ = COMMIT_STATE_BEGIN_MAIN_FRAME_SENT; |
+ needs_commit_ = false; |
+ send_begin_main_frame_funnel_ = true; |
+ last_frame_number_begin_main_frame_sent_ = current_frame_number_; |
+} |
+ |
void SchedulerStateMachine::UpdateStateOnCommit(bool commit_has_no_updates) { |
commit_count_++; |
+ // Animate after commit even if we've already animated. |
if (!commit_has_no_updates) |
animate_funnel_ = false; |
@@ -652,6 +685,7 @@ void SchedulerStateMachine::UpdateStateOnDraw(bool did_request_swap) { |
if (did_request_swap) { |
DCHECK(!request_swap_funnel_); |
request_swap_funnel_ = true; |
+ did_request_swap_in_last_frame_ = true; |
last_frame_number_swap_requested_ = current_frame_number_; |
} |
} |
@@ -660,6 +694,25 @@ void SchedulerStateMachine::UpdateStateOnPrepareTiles() { |
needs_prepare_tiles_ = false; |
} |
+void SchedulerStateMachine::UpdateStateOnBeginOutputSurfaceCreation() { |
+ DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_LOST); |
+ output_surface_state_ = OUTPUT_SURFACE_CREATING; |
+ |
+ // The following DCHECKs make sure we are in the proper quiescent state. |
+ // The pipeline should be flushed entirely before we start output |
+ // surface creation to avoid complicated corner cases. |
+ DCHECK_EQ(commit_state_, COMMIT_STATE_IDLE); |
+ DCHECK(!has_pending_tree_); |
+ DCHECK(!active_tree_needs_first_draw_); |
+} |
+ |
+void SchedulerStateMachine::UpdateStateOnInvalidateOutputSurface() { |
+ DCHECK(!invalidate_output_surface_funnel_); |
+ invalidate_output_surface_funnel_ = true; |
+ last_frame_number_invalidate_output_surface_performed_ = |
+ current_frame_number_; |
+} |
+ |
void SchedulerStateMachine::SetSkipNextBeginMainFrameToReduceLatency() { |
TRACE_EVENT_INSTANT0("cc", |
"Scheduler: SkipNextBeginMainFrameToReduceLatency", |
@@ -668,10 +721,7 @@ void SchedulerStateMachine::SetSkipNextBeginMainFrameToReduceLatency() { |
} |
bool SchedulerStateMachine::BeginFrameNeededForChildren() const { |
- if (HasInitializedOutputSurface()) |
- return children_need_begin_frames_; |
- |
- return false; |
+ return children_need_begin_frames_; |
} |
bool SchedulerStateMachine::BeginFrameNeeded() const { |
@@ -679,44 +729,8 @@ bool SchedulerStateMachine::BeginFrameNeeded() const { |
// TODO(brianderson): Support output surface creation inside a BeginFrame. |
if (!HasInitializedOutputSurface()) |
return false; |
- |
- if (SupportsProactiveBeginFrame()) { |
- return (BeginFrameNeededToAnimateOrDraw() || |
- BeginFrameNeededForChildren() || |
- ProactiveBeginFrameWanted()); |
- } |
- |
- // Proactive BeginFrames are bad for the synchronous compositor because we |
- // have to draw when we get the BeginFrame and could end up drawing many |
- // duplicate frames if our new frame isn't ready in time. |
- // To poll for state with the synchronous compositor without having to draw, |
- // we rely on ShouldPollForAnticipatedDrawTriggers instead. |
- // Synchronous compositor doesn't have a browser. |
- DCHECK(!children_need_begin_frames_); |
- return BeginFrameNeededToAnimateOrDraw(); |
-} |
- |
-bool SchedulerStateMachine::ShouldPollForAnticipatedDrawTriggers() const { |
- // ShouldPollForAnticipatedDrawTriggers is what we use in place of |
- // ProactiveBeginFrameWanted when we are using the synchronous |
- // compositor. |
- if (!SupportsProactiveBeginFrame()) { |
- return !BeginFrameNeededToAnimateOrDraw() && ProactiveBeginFrameWanted(); |
- } |
- |
- // Non synchronous compositors should rely on |
- // ProactiveBeginFrameWanted to poll for state instead. |
- return false; |
-} |
- |
-// Note: If SupportsProactiveBeginFrame is false, the scheduler should poll |
-// for changes in it's draw state so it can request a BeginFrame when it's |
-// actually ready. |
-bool SchedulerStateMachine::SupportsProactiveBeginFrame() const { |
- // It is undesirable to proactively request BeginFrames if we are |
- // using a synchronous compositor because we *must* draw for every |
- // BeginFrame, which could cause duplicate draws. |
- return !settings_.using_synchronous_renderer_compositor; |
+ return (BeginFrameNeededToAnimateOrDraw() || BeginFrameNeededForChildren() || |
+ ProactiveBeginFrameWanted()); |
} |
void SchedulerStateMachine::SetChildrenNeedBeginFrames( |
@@ -779,7 +793,7 @@ bool SchedulerStateMachine::ProactiveBeginFrameWanted() const { |
// SetNeedsBeginFrame requests, which may propagate to the BeginImplFrame |
// provider and get sampled at an inopportune time, delaying the next |
// BeginImplFrame. |
- if (request_swap_funnel_) |
+ if (did_request_swap_in_last_frame_) |
return true; |
// If the last commit was aborted because of early out (no updates), we should |
@@ -791,35 +805,49 @@ bool SchedulerStateMachine::ProactiveBeginFrameWanted() const { |
} |
void SchedulerStateMachine::OnBeginImplFrame() { |
- AdvanceCurrentFrameNumber(); |
- DCHECK_EQ(begin_impl_frame_state_, BEGIN_IMPL_FRAME_STATE_IDLE) |
- << AsValue()->ToString(); |
begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING; |
+ current_frame_number_++; |
+ |
last_commit_had_no_updates_ = false; |
+ did_request_swap_in_last_frame_ = false; |
+ |
+ // Clear funnels for any actions we perform during the frame. |
+ animate_funnel_ = false; |
+ send_begin_main_frame_funnel_ = false; |
+ invalidate_output_surface_funnel_ = false; |
+ |
+ // "Drain" the PrepareTiles funnel. |
+ if (prepare_tiles_funnel_ > 0) |
+ prepare_tiles_funnel_--; |
+ |
+ skip_begin_main_frame_to_reduce_latency_ = |
+ skip_next_begin_main_frame_to_reduce_latency_; |
+ skip_next_begin_main_frame_to_reduce_latency_ = false; |
} |
void SchedulerStateMachine::OnBeginImplFrameDeadlinePending() { |
- DCHECK_EQ(begin_impl_frame_state_, |
- BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING) |
- << AsValue()->ToString(); |
begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME; |
} |
void SchedulerStateMachine::OnBeginImplFrameDeadline() { |
- DCHECK_EQ(begin_impl_frame_state_, BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME) |
- << AsValue()->ToString(); |
begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE; |
+ |
+ did_perform_swap_in_last_draw_ = false; |
+ |
+ // Clear funnels for any actions we perform during the deadline. |
+ request_swap_funnel_ = false; |
} |
void SchedulerStateMachine::OnBeginImplFrameIdle() { |
- DCHECK_EQ(begin_impl_frame_state_, BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE) |
- << AsValue()->ToString(); |
begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_IDLE; |
} |
SchedulerStateMachine::BeginImplFrameDeadlineMode |
SchedulerStateMachine::CurrentBeginImplFrameDeadlineMode() const { |
- if (wait_for_active_tree_ready_to_draw_) { |
+ if (settings_.using_synchronous_renderer_compositor) { |
+ // No deadline for synchronous compositor. |
+ return BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE; |
+ } else if (wait_for_active_tree_ready_to_draw_) { |
// When we are waiting for ready to draw signal, we do not wait to post a |
// deadline yet. |
return BEGIN_IMPL_FRAME_DEADLINE_MODE_BLOCKED_ON_READY_TO_DRAW; |
@@ -841,7 +869,6 @@ SchedulerStateMachine::CurrentBeginImplFrameDeadlineMode() const { |
bool SchedulerStateMachine::ShouldTriggerBeginImplFrameDeadlineImmediately() |
const { |
// TODO(brianderson): This should take into account multiple commit sources. |
- |
if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME) |
return false; |
@@ -907,7 +934,7 @@ bool SchedulerStateMachine::MainThreadIsInHighLatencyMode() const { |
// Even if there's a new active tree to draw at the deadline or we've just |
// swapped it, it may have been triggered by a previous BeginImplFrame, in |
// which case the main thread is in a high latency mode. |
- return (active_tree_needs_first_draw_ || perform_swap_funnel_) && |
+ return (active_tree_needs_first_draw_ || did_perform_swap_in_last_draw_) && |
!send_begin_main_frame_funnel_; |
} |
@@ -916,15 +943,6 @@ bool SchedulerStateMachine::MainThreadIsInHighLatencyMode() const { |
return active_tree_needs_first_draw_; |
} |
-void SchedulerStateMachine::DidEnterPollForAnticipatedDrawTriggers() { |
- AdvanceCurrentFrameNumber(); |
- inside_poll_for_anticipated_draw_triggers_ = true; |
-} |
- |
-void SchedulerStateMachine::DidLeavePollForAnticipatedDrawTriggers() { |
- inside_poll_for_anticipated_draw_triggers_ = false; |
-} |
- |
void SchedulerStateMachine::SetVisible(bool visible) { visible_ = visible; } |
void SchedulerStateMachine::SetCanDraw(bool can_draw) { can_draw_ = can_draw; } |
@@ -954,9 +972,8 @@ void SchedulerStateMachine::SetMaxSwapsPending(int max) { |
void SchedulerStateMachine::DidSwapBuffers() { |
pending_swaps_++; |
DCHECK_LE(pending_swaps_, max_pending_swaps_); |
- DCHECK(!perform_swap_funnel_); |
- perform_swap_funnel_ = true; |
+ did_perform_swap_in_last_draw_ = true; |
last_frame_number_swap_performed_ = current_frame_number_; |
} |