| 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_;
|
| }
|
|
|
|
|