| Index: cc/scheduler/scheduler_state_machine.cc
|
| diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc
|
| index 15b6597f7ae5116c4aeb9a04cdbb14b85489b52a..dcd1ac9f8c23b8f2c4b049a5b8b8a921f96d6ea6 100644
|
| --- a/cc/scheduler/scheduler_state_machine.cc
|
| +++ b/cc/scheduler/scheduler_state_machine.cc
|
| @@ -26,11 +26,10 @@
|
| 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),
|
| @@ -39,6 +38,7 @@
|
| 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,9 +53,7 @@
|
| children_need_begin_frames_(false),
|
| defer_commits_(false),
|
| last_commit_had_no_updates_(false),
|
| - wait_for_active_tree_ready_to_draw_(false),
|
| - did_request_swap_in_last_frame_(false),
|
| - did_perform_swap_in_last_draw_(false) {
|
| + wait_for_active_tree_ready_to_draw_(false) {
|
| }
|
|
|
| const char* SchedulerStateMachine::OutputSurfaceStateToString(
|
| @@ -87,24 +85,6 @@
|
| return "BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME";
|
| case BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE:
|
| return "BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE";
|
| - }
|
| - NOTREACHED();
|
| - 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 "???";
|
| @@ -167,8 +147,6 @@
|
| 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 "???";
|
| @@ -207,12 +185,11 @@
|
| 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_);
|
| @@ -244,12 +221,24 @@
|
| 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 {
|
| @@ -441,7 +430,7 @@
|
| // TODO(brianderson): Remove this restriction to improve throughput.
|
| bool just_swapped_in_deadline =
|
| begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE &&
|
| - did_perform_swap_in_last_draw_;
|
| + perform_swap_funnel_;
|
| if (pending_swaps_ >= max_pending_swaps_ && !just_swapped_in_deadline)
|
| return false;
|
|
|
| @@ -476,30 +465,12 @@
|
| return false;
|
|
|
| // Limiting to once per-frame is not enough, since we only want to
|
| - // prepare tiles _after_ draws.
|
| - if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE)
|
| - return false;
|
| -
|
| + // 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_)
|
| + 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 {
|
| @@ -521,8 +492,6 @@
|
| 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;
|
| @@ -538,11 +507,25 @@
|
| return;
|
|
|
| case ACTION_ANIMATE:
|
| - 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();
|
| return;
|
|
|
| case ACTION_SEND_BEGIN_MAIN_FRAME:
|
| - 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_;
|
| return;
|
|
|
| case ACTION_COMMIT: {
|
| @@ -565,42 +548,26 @@
|
| }
|
|
|
| case ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
|
| - 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_);
|
| 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;
|
|
|
| @@ -685,32 +652,12 @@
|
| 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_;
|
| }
|
| }
|
|
|
| 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() {
|
| @@ -721,7 +668,10 @@
|
| }
|
|
|
| bool SchedulerStateMachine::BeginFrameNeededForChildren() const {
|
| - return children_need_begin_frames_;
|
| + if (HasInitializedOutputSurface())
|
| + return children_need_begin_frames_;
|
| +
|
| + return false;
|
| }
|
|
|
| bool SchedulerStateMachine::BeginFrameNeeded() const {
|
| @@ -729,8 +679,44 @@
|
| // TODO(brianderson): Support output surface creation inside a BeginFrame.
|
| if (!HasInitializedOutputSurface())
|
| return false;
|
| - return (BeginFrameNeededToAnimateOrDraw() || BeginFrameNeededForChildren() ||
|
| - ProactiveBeginFrameWanted());
|
| +
|
| + 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;
|
| }
|
|
|
| void SchedulerStateMachine::SetChildrenNeedBeginFrames(
|
| @@ -793,7 +779,7 @@
|
| // SetNeedsBeginFrame requests, which may propagate to the BeginImplFrame
|
| // provider and get sampled at an inopportune time, delaying the next
|
| // BeginImplFrame.
|
| - if (did_request_swap_in_last_frame_)
|
| + if (request_swap_funnel_)
|
| return true;
|
|
|
| // If the last commit was aborted because of early out (no updates), we should
|
| @@ -805,49 +791,35 @@
|
| }
|
|
|
| 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 (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_) {
|
| + 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;
|
| @@ -869,6 +841,7 @@
|
| 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;
|
|
|
| @@ -934,13 +907,22 @@
|
| // 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_ || did_perform_swap_in_last_draw_) &&
|
| + return (active_tree_needs_first_draw_ || perform_swap_funnel_) &&
|
| !send_begin_main_frame_funnel_;
|
| }
|
|
|
| // If the active tree needs its first draw in any other state, we know the
|
| // main thread is in a high latency mode.
|
| 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; }
|
| @@ -972,8 +954,9 @@
|
| void SchedulerStateMachine::DidSwapBuffers() {
|
| pending_swaps_++;
|
| DCHECK_LE(pending_swaps_, max_pending_swaps_);
|
| -
|
| - did_perform_swap_in_last_draw_ = true;
|
| + DCHECK(!perform_swap_funnel_);
|
| +
|
| + perform_swap_funnel_ = true;
|
| last_frame_number_swap_performed_ = current_frame_number_;
|
| }
|
|
|
|
|