| Index: cc/scheduler/scheduler_state_machine.cc
|
| diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc
|
| index c2d0ba294b8ef2baa17389e3de7b92cfeadc078e..62b49cc5923ae2e533435fc204d622d897cac317 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),
|
| @@ -88,6 +89,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:
|
| @@ -145,6 +162,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 "???";
|
| @@ -188,6 +207,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_);
|
| @@ -220,23 +241,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,
|
| @@ -461,14 +465,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_;
|
| +}
|
| +
|
| SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const {
|
| if (ShouldActivatePendingTree())
|
| return ACTION_ACTIVATE_SYNC_TREE;
|
| @@ -488,6 +508,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;
|
| @@ -503,25 +525,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: {
|
| @@ -544,23 +552,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:
|
| + 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_++;
|
|
|
| @@ -655,6 +679,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;
|
| +}
|
| +
|
| void SchedulerStateMachine::SetSkipNextBeginMainFrameToReduceLatency() {
|
| TRACE_EVENT_INSTANT0("cc",
|
| "Scheduler: SkipNextBeginMainFrameToReduceLatency",
|
| @@ -674,44 +720,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(
|
| @@ -770,34 +780,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_++;
|
| +
|
| + // 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.
|
| + perform_swap_funnel_ = false;
|
| + request_swap_funnel_ = false;
|
| + invalidate_output_surface_funnel_ = false;
|
| +
|
| + // "Drain" the PrepareTiles funnel.
|
| + 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
|
| @@ -815,7 +840,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;
|
|
|
| @@ -890,15 +914,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; }
|
|
|