Index: cc/scheduler/scheduler_state_machine.cc |
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc |
index c512987bd23f501abdaf1fceab783945f8dc1ae4..9a0caf506cb0948de57ac94f3e78cc221771074a 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), |
@@ -52,7 +52,9 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) |
continuous_painting_(false), |
children_need_begin_frames_(false), |
defer_commits_(false), |
- last_commit_had_no_updates_(false) { |
+ last_commit_had_no_updates_(false), |
+ did_request_swap_in_last_frame_(false), |
+ did_perform_swap_in_last_draw_(false) { |
} |
const char* SchedulerStateMachine::OutputSurfaceStateToString( |
@@ -89,6 +91,22 @@ 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"; |
+ } |
+ NOTREACHED(); |
+ return "???"; |
+} |
+ |
const char* SchedulerStateMachine::CommitStateToString(CommitState state) { |
switch (state) { |
case COMMIT_STATE_IDLE: |
@@ -146,6 +164,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 "???"; |
@@ -184,11 +204,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_); |
@@ -218,26 +239,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, |
@@ -427,7 +436,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; |
@@ -462,14 +471,29 @@ 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; |
+ |
+ return needs_redraw_ || needs_prepare_tiles_; |
brianderson
2015/04/01 00:23:38
Can you add a TODO explaining what would need to h
sunnyps
2015/04/01 19:53:01
Done.
|
+} |
+ |
SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const { |
if (ShouldActivatePendingTree()) |
return ACTION_ACTIVATE_SYNC_TREE; |
@@ -489,6 +513,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; |
@@ -504,25 +530,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: { |
@@ -545,26 +557,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; |
@@ -649,6 +677,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_; |
} |
} |
@@ -657,6 +686,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", |
@@ -665,10 +713,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 { |
@@ -676,44 +721,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( |
@@ -765,7 +774,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 |
@@ -777,35 +786,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 (ShouldTriggerBeginImplFrameDeadlineImmediately()) { |
+ if (settings_.using_synchronous_renderer_compositor) { |
+ // No deadline for synchronous compositor. |
+ return BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE; |
+ } else if (ShouldTriggerBeginImplFrameDeadlineImmediately()) { |
return BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE; |
} else if (needs_redraw_ && pending_swaps_ < max_pending_swaps_) { |
// We have an animation or fast input path on the impl thread that wants |
@@ -823,7 +846,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; |
@@ -889,7 +911,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_; |
} |
@@ -898,15 +920,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; } |
@@ -931,9 +944,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_; |
} |