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 c512987bd23f501abdaf1fceab783945f8dc1ae4..37f7bd6089ae4372a18d554cb93d002c7892c3d1 100644 |
| --- a/cc/scheduler/scheduler_state_machine.cc |
| +++ b/cc/scheduler/scheduler_state_machine.cc |
| @@ -26,10 +26,12 @@ 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 +40,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), |
| @@ -89,6 +90,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 +163,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 "???"; |
| @@ -189,6 +208,8 @@ void SchedulerStateMachine::AsValueInto( |
| 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,23 +242,6 @@ void SchedulerStateMachine::AsValueInto( |
| 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, |
| @@ -462,14 +466,30 @@ 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 or inside deadline. |
| + if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING && |
| + begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE) |
| + return false; |
| + |
| + return needs_redraw_ || needs_prepare_tiles_; |
|
brianderson
2015/03/25 23:10:37
Why needs_prepare_tiles_?
sunnyps
2015/03/27 00:52:48
The previous draw might have caused tile propertie
brianderson
2015/03/27 21:24:24
I think you can remove needs_prepare_tiles_ from h
sunnyps
2015/03/27 22:38:24
I moved the prepare_tiles_funnel_ decrement to OnB
|
| +} |
| + |
| SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const { |
| if (ShouldActivatePendingTree()) |
| return ACTION_ACTIVATE_SYNC_TREE; |
| @@ -489,6 +509,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 +526,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,23 +553,39 @@ 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: |
|
brianderson
2015/03/25 23:10:37
Thanks for making UpdateStateOn* method usage cons
|
| + 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. |
|
brianderson
2015/03/25 23:10:37
Comment can be one line now.
sunnyps
2015/03/27 00:52:48
Done.
|
| + 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_++; |
| @@ -657,6 +681,28 @@ 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_; |
| + // There is no guarantee that a draw will arrive from the synchronous |
| + // compositor. We clear this flag here so that the pipeline isn't blocked. |
| + active_tree_needs_first_draw_ = false; |
|
brianderson
2015/03/25 23:10:37
I'm worried that this will make us skip frames or
sunnyps
2015/03/27 00:52:48
Webview can't even tell if it's in this state. Sin
brianderson
2015/03/27 21:24:24
Is the problem that we are blocking the main threa
sunnyps
2015/03/27 22:38:24
The problem is that the main thread is blocked ind
sunnyps
2015/03/30 21:49:18
I removed this piece of code and found that I coul
|
| +} |
| + |
| void SchedulerStateMachine::SetSkipNextBeginMainFrameToReduceLatency() { |
| TRACE_EVENT_INSTANT0("cc", |
| "Scheduler: SkipNextBeginMainFrameToReduceLatency", |
| @@ -676,44 +722,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() || |
|
brianderson
2015/03/25 23:10:37
So much easier to understand!
|
| + ProactiveBeginFrameWanted()); |
| } |
| void SchedulerStateMachine::SetChildrenNeedBeginFrames( |
| @@ -777,35 +787,50 @@ 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; |
| + |
| + // Clear funnels for any actions we perform during the frame. |
| + animate_funnel_ = false; |
| + perform_swap_funnel_ = false; |
| + request_swap_funnel_ = false; |
| + send_begin_main_frame_funnel_ = false; |
| + invalidate_output_surface_funnel_ = false; |
| + |
| + 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; |
| + |
| + // Clear funnels for any actions we perform during the deadline. |
|
brianderson
2015/03/25 23:10:37
Why are these cleared in both the deadline and Beg
sunnyps
2015/03/27 00:52:48
For invalidate_output_surface_funnel_ the reason i
brianderson
2015/03/27 21:24:24
Can add a comment about that and also explain in w
sunnyps
2015/03/27 22:38:24
I changed this so that it's invalidated only once,
|
| + perform_swap_funnel_ = false; |
| + request_swap_funnel_ = false; |
| + invalidate_output_surface_funnel_ = false; |
| + |
| + // "Drain" the PrepareTiles funnel. |
|
brianderson
2015/03/25 23:10:37
Why is this moved to the deadline rather than the
sunnyps
2015/03/27 00:52:48
Because prepare tiles (when initiated by the sched
brianderson
2015/03/27 21:24:24
Was the intent not to have a functional change by
sunnyps
2015/03/27 22:38:24
Good catch. Done.
|
| + if (prepare_tiles_funnel_ > 0) |
| + prepare_tiles_funnel_--; |
| } |
| 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 +848,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; |
| @@ -898,15 +922,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; } |