diff options
author | brianderson <brianderson@chromium.org> | 2015-07-09 19:29:07 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-07-10 02:29:44 +0000 |
commit | 6d50e7a7df5b33048b9bcd641171b31642b58850 (patch) | |
tree | a4dbeceb66c69a002252132278162f430a0a7341 | |
parent | 068d2ad77420efd2fe8146ff6c6d6c1b44ce2c08 (diff) | |
download | chromium_src-6d50e7a7df5b33048b9bcd641171b31642b58850.zip chromium_src-6d50e7a7df5b33048b9bcd641171b31642b58850.tar.gz chromium_src-6d50e7a7df5b33048b9bcd641171b31642b58850.tar.bz2 |
cc: Heuristic for Renderer latency recovery
If the Renderer impl thread can draw before the
deadline and we are getting swap acks from the
Browser into the next BeginFrame, there's
a very high probability that the Renderer is
in a high latency mode but could operate in a
low latency mode.
To recover latency in that case, we skip a
BeginImplFrame.
BUG=406158
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel
Review URL: https://codereview.chromium.org/1133673004
Cr-Commit-Position: refs/heads/master@{#338205}
-rw-r--r-- | cc/scheduler/scheduler.cc | 74 | ||||
-rw-r--r-- | cc/scheduler/scheduler.h | 7 | ||||
-rw-r--r-- | cc/scheduler/scheduler_state_machine.cc | 34 | ||||
-rw-r--r-- | cc/scheduler/scheduler_state_machine.h | 7 | ||||
-rw-r--r-- | cc/scheduler/scheduler_unittest.cc | 586 | ||||
-rw-r--r-- | cc/test/scheduler_test_common.cc | 8 | ||||
-rw-r--r-- | cc/test/scheduler_test_common.h | 10 |
7 files changed, 621 insertions, 105 deletions
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc index cd0d37e..2b668e8 100644 --- a/cc/scheduler/scheduler.cc +++ b/cc/scheduler/scheduler.cc @@ -460,10 +460,15 @@ void Scheduler::BeginImplFrameWithDeadline(const BeginFrameArgs& args) { BeginFrameArgs adjusted_args = args; adjusted_args.deadline -= compositor_timing_history_->DrawDurationEstimate(); - if (!state_machine_.impl_latency_takes_priority() && - main_thread_is_in_high_latency_mode && - CanCommitAndActivateBeforeDeadline()) { + if (ShouldRecoverMainLatency(adjusted_args)) { + TRACE_EVENT_INSTANT0("cc", "SkipBeginMainFrameToReduceLatency", + TRACE_EVENT_SCOPE_THREAD); state_machine_.SetSkipNextBeginMainFrameToReduceLatency(); + } else if (ShouldRecoverImplLatency(adjusted_args)) { + TRACE_EVENT_INSTANT0("cc", "SkipBeginImplFrameToReduceLatency", + TRACE_EVENT_SCOPE_THREAD); + frame_source_->DidFinishFrame(begin_retro_frame_args_.size()); + return; } BeginImplFrame(adjusted_args); @@ -515,7 +520,6 @@ void Scheduler::ScheduleBeginImplFrameDeadline() { begin_impl_frame_deadline_mode_ = state_machine_.CurrentBeginImplFrameDeadlineMode(); - base::TimeTicks deadline; switch (begin_impl_frame_deadline_mode_) { case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE: @@ -697,6 +701,8 @@ scoped_refptr<base::trace_event::ConvertableToTraceFormat> Scheduler::AsValue() } void Scheduler::AsValueInto(base::trace_event::TracedValue* state) const { + base::TimeTicks now = Now(); + state->BeginDictionary("state_machine"); state_machine_.AsValueInto(state); state->EndDictionary(); @@ -725,9 +731,14 @@ void Scheduler::AsValueInto(base::trace_event::TracedValue* state) const { !begin_impl_frame_deadline_task_.IsCancelled()); state->SetString("inside_action", SchedulerStateMachine::ActionToString(inside_action_)); + state->BeginDictionary("begin_impl_frame_args"); - begin_impl_frame_tracker_.AsValueInto(Now(), state); + begin_impl_frame_tracker_.AsValueInto(now, state); state->EndDictionary(); + + state->SetString("begin_impl_frame_deadline_mode_", + SchedulerStateMachine::BeginImplFrameDeadlineModeToString( + begin_impl_frame_deadline_mode_)); state->EndDictionary(); state->BeginDictionary("compositor_timing_history"); @@ -740,10 +751,51 @@ void Scheduler::UpdateCompositorTimingHistoryRecordingEnabled() { state_machine_.HasInitializedOutputSurface() && state_machine_.visible()); } -bool Scheduler::CanCommitAndActivateBeforeDeadline() const { - BeginFrameArgs args = - begin_impl_frame_tracker_.DangerousMethodCurrentOrLast(); +bool Scheduler::ShouldRecoverMainLatency(const BeginFrameArgs& args) const { + DCHECK(!settings_.using_synchronous_renderer_compositor); + + if (!state_machine_.MainThreadIsInHighLatencyMode()) + return false; + + // When prioritizing impl thread latency, we currently put the + // main thread in a high latency mode. Don't try to fight it. + if (state_machine_.impl_latency_takes_priority()) + return false; + + return CanCommitAndActivateBeforeDeadline(args); +} + +bool Scheduler::ShouldRecoverImplLatency(const BeginFrameArgs& args) const { + DCHECK(!settings_.using_synchronous_renderer_compositor); + + // If we are swap throttled at the BeginFrame, that means the impl thread is + // very likely in a high latency mode. + bool impl_thread_is_likely_high_latency = state_machine_.SwapThrottled(); + if (!impl_thread_is_likely_high_latency) + return false; + + // The deadline may be in the past if our draw time is too long. + bool can_draw_before_deadline = args.frame_time < args.deadline; + // When prioritizing impl thread latency, the deadline doesn't wait + // for the main thread. + if (state_machine_.impl_latency_takes_priority()) + return can_draw_before_deadline; + + // If we only have impl-side updates, the deadline doesn't wait for + // the main thread. + if (state_machine_.OnlyImplSideUpdatesExpected()) + return can_draw_before_deadline; + + // If we get here, we know the main thread is in a low-latency mode relative + // to the impl thread. In this case, only try to also recover impl thread + // latency if both the main and impl threads can run serially before the + // deadline. + return CanCommitAndActivateBeforeDeadline(args); +} + +bool Scheduler::CanCommitAndActivateBeforeDeadline( + const BeginFrameArgs& args) const { // Check if the main thread computation and commit can be finished before the // impl thread's deadline. base::TimeTicks estimated_draw_time = @@ -752,12 +804,6 @@ bool Scheduler::CanCommitAndActivateBeforeDeadline() const { compositor_timing_history_->CommitToReadyToActivateDurationEstimate() + compositor_timing_history_->ActivateDurationEstimate(); - TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"), - "CanCommitAndActivateBeforeDeadline", - "time_left_after_drawing_ms", - (args.deadline - estimated_draw_time).InMillisecondsF(), "state", - AsValue()); - return estimated_draw_time < args.deadline; } diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h index 967d31b..d6fc074 100644 --- a/cc/scheduler/scheduler.h +++ b/cc/scheduler/scheduler.h @@ -120,9 +120,6 @@ class CC_EXPORT Scheduler : public BeginFrameObserverBase { bool PrepareTilesPending() const { return state_machine_.PrepareTilesPending(); } - bool MainThreadIsInHighLatencyMode() const { - return state_machine_.MainThreadIsInHighLatencyMode(); - } bool BeginImplFrameDeadlinePending() const { return !begin_impl_frame_deadline_task_.IsCancelled(); } @@ -201,7 +198,9 @@ class CC_EXPORT Scheduler : public BeginFrameObserverBase { void DrawAndSwapForced(); void ProcessScheduledActions(); void UpdateCompositorTimingHistoryRecordingEnabled(); - bool CanCommitAndActivateBeforeDeadline() const; + bool ShouldRecoverMainLatency(const BeginFrameArgs& args) const; + bool ShouldRecoverImplLatency(const BeginFrameArgs& args) const; + bool CanCommitAndActivateBeforeDeadline(const BeginFrameArgs& args) const; void AdvanceCommitStateIfPossible(); bool IsBeginMainFrameSentOrStarted() const; void BeginRetroFrame(); diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc index 6742d0e..40e3918 100644 --- a/cc/scheduler/scheduler_state_machine.cc +++ b/cc/scheduler/scheduler_state_machine.cc @@ -48,7 +48,6 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) did_create_and_initialize_first_output_surface_(false), impl_latency_takes_priority_(false), skip_next_begin_main_frame_to_reduce_latency_(false), - skip_begin_main_frame_to_reduce_latency_(false), continuous_painting_(false), children_need_begin_frames_(false), defer_commits_(false), @@ -240,8 +239,6 @@ void SchedulerStateMachine::AsValueInto( impl_latency_takes_priority_); state->SetBoolean("main_thread_is_in_high_latency_mode", MainThreadIsInHighLatencyMode()); - state->SetBoolean("skip_begin_main_frame_to_reduce_latency", - skip_begin_main_frame_to_reduce_latency_); state->SetBoolean("skip_next_begin_main_frame_to_reduce_latency", skip_next_begin_main_frame_to_reduce_latency_); state->SetBoolean("continuous_painting", continuous_painting_); @@ -339,7 +336,7 @@ bool SchedulerStateMachine::ShouldDraw() const { return false; // Do not queue too many swaps. - if (pending_swaps_ >= max_pending_swaps_) + if (SwapThrottled()) return false; // Except for the cases above, do not draw outside of the BeginImplFrame @@ -413,8 +410,7 @@ bool SchedulerStateMachine::SendingBeginMainFrameMightCauseDeadlock() const { // and we have deadlock. // This returns true if there's too much backpressure to finish a commit // if we were to initiate a BeginMainFrame. - return has_pending_tree_ && active_tree_needs_first_draw_ && - pending_swaps_ >= max_pending_swaps_; + return has_pending_tree_ && active_tree_needs_first_draw_ && SwapThrottled(); } bool SchedulerStateMachine::ShouldSendBeginMainFrame() const { @@ -472,11 +468,11 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const { bool just_swapped_in_deadline = begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE && did_perform_swap_in_last_draw_; - if (pending_swaps_ >= max_pending_swaps_ && !just_swapped_in_deadline) + if (SwapThrottled() && !just_swapped_in_deadline) return false; } - if (skip_begin_main_frame_to_reduce_latency_) + if (skip_next_begin_main_frame_to_reduce_latency_) return false; return true; @@ -858,10 +854,6 @@ void SchedulerStateMachine::OnBeginImplFrame() { // "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() { @@ -885,6 +877,8 @@ void SchedulerStateMachine::OnBeginImplFrameDeadline() { void SchedulerStateMachine::OnBeginImplFrameIdle() { begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_IDLE; + + skip_next_begin_main_frame_to_reduce_latency_ = false; } SchedulerStateMachine::BeginImplFrameDeadlineMode @@ -898,7 +892,7 @@ SchedulerStateMachine::CurrentBeginImplFrameDeadlineMode() const { return BEGIN_IMPL_FRAME_DEADLINE_MODE_BLOCKED_ON_READY_TO_DRAW; } else if (ShouldTriggerBeginImplFrameDeadlineImmediately()) { return BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE; - } else if (needs_redraw_ && pending_swaps_ < max_pending_swaps_) { + } else if (needs_redraw_ && !SwapThrottled()) { // We have an animation or fast input path on the impl thread that wants // to draw, so don't wait too long for a new active tree. // If we are swap throttled we should wait until we are unblocked. @@ -913,7 +907,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; @@ -922,7 +915,7 @@ bool SchedulerStateMachine::ShouldTriggerBeginImplFrameDeadlineImmediately() return true; // SwapAck throttle the deadline since we wont draw and swap anyway. - if (pending_swaps_ >= max_pending_swaps_) + if (SwapThrottled()) return false; if (active_tree_needs_first_draw_) @@ -987,6 +980,10 @@ bool SchedulerStateMachine::MainThreadIsInHighLatencyMode() const { return active_tree_needs_first_draw_; } +bool SchedulerStateMachine::SwapThrottled() const { + return pending_swaps_ >= max_pending_swaps_; +} + void SchedulerStateMachine::SetVisible(bool visible) { visible_ = visible; // TODO(sunnyps): Change the funnel to a bool to avoid hacks like this. @@ -1005,6 +1002,13 @@ void SchedulerStateMachine::SetWaitForReadyToDraw() { wait_for_active_tree_ready_to_draw_ = true; } +bool SchedulerStateMachine::OnlyImplSideUpdatesExpected() const { + bool has_impl_updates = needs_redraw_ || needs_animate_; + bool main_updates_expected = + needs_commit_ || commit_state_ != COMMIT_STATE_IDLE || has_pending_tree_; + return has_impl_updates && !main_updates_expected; +} + void SchedulerStateMachine::SetNeedsPrepareTiles() { if (!needs_prepare_tiles_) { TRACE_EVENT0("cc", "SchedulerStateMachine::SetNeedsPrepareTiles"); diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h index fcb8494..cf1ab8c 100644 --- a/cc/scheduler/scheduler_state_machine.h +++ b/cc/scheduler/scheduler_state_machine.h @@ -145,6 +145,8 @@ class CC_EXPORT SchedulerStateMachine { // impl thread to draw, it is in a high latency mode. bool MainThreadIsInHighLatencyMode() const; + bool SwapThrottled() const; + // Indicates whether the LayerTreeHostImpl is visible. void SetVisible(bool visible); bool visible() const { return visible_; } @@ -157,6 +159,8 @@ class CC_EXPORT SchedulerStateMachine { void SetNeedsAnimate(); bool needs_animate() const { return needs_animate_; } + bool OnlyImplSideUpdatesExpected() const; + // Indicates that prepare-tiles is required. This guarantees another // PrepareTiles will occur shortly (even if no redraw is required). void SetNeedsPrepareTiles(); @@ -195,6 +199,7 @@ class CC_EXPORT SchedulerStateMachine { // updates from the main thread to the impl, or to push deltas from the impl // thread to main. void SetNeedsCommit(); + bool needs_commit() const { return needs_commit_; } // Call this only in response to receiving an ACTION_SEND_BEGIN_MAIN_FRAME // from NextAction. @@ -210,6 +215,7 @@ class CC_EXPORT SchedulerStateMachine { // Allow access of the can_start_ state in tests. bool CanStartForTesting() const { return can_start_; } + // Indicates production should be skipped to recover latency. void SetSkipNextBeginMainFrameToReduceLatency(); // Indicates whether drawing would, at this time, make sense. @@ -332,7 +338,6 @@ class CC_EXPORT SchedulerStateMachine { bool did_create_and_initialize_first_output_surface_; bool impl_latency_takes_priority_; bool skip_next_begin_main_frame_to_reduce_latency_; - bool skip_begin_main_frame_to_reduce_latency_; bool continuous_painting_; bool children_need_begin_frames_; bool defer_commits_; diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc index 1c37748..77e1f27 100644 --- a/cc/scheduler/scheduler_unittest.cc +++ b/cc/scheduler/scheduler_unittest.cc @@ -246,6 +246,12 @@ class SchedulerTest : public testing::Test { fake_compositor_timing_history.Pass()); DCHECK(scheduler_); client_->set_scheduler(scheduler_.get()); + + // Use large estimates by default to avoid latency recovery + // in most tests. + base::TimeDelta slow_duration = base::TimeDelta::FromSeconds(1); + fake_compositor_timing_history_->SetAllEstimatesTo(slow_duration); + return scheduler_.get(); } @@ -384,11 +390,9 @@ class SchedulerTest : public testing::Test { return fake_external_begin_frame_source_.get(); } - void MainFrameInHighLatencyMode( - int64 begin_main_frame_to_commit_estimate_in_ms, - int64 commit_to_activate_estimate_in_ms, - bool impl_latency_takes_priority, - bool should_send_begin_main_frame); + void CheckMainFrameSkippedAfterLateCommit(bool expect_send_begin_main_frame); + void ImplFrameSkippedAfterLateSwapAck(bool swap_ack_before_deadline); + void ImplFrameIsNotSkippedAfterLateSwapAck(); void BeginFramesNotFromClient(bool use_external_begin_frame_source, bool throttle_frame_production); void BeginFramesNotFromClient_SwapThrottled( @@ -1323,73 +1327,538 @@ TEST_F(SchedulerTest, WaitForReadyToDrawCancelledWhenLostOutputSurface) { EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3); } -void SchedulerTest::MainFrameInHighLatencyMode( - int64 begin_main_frame_to_commit_estimate_in_ms, - int64 commit_to_activate_estimate_in_ms, - bool impl_latency_takes_priority, - bool should_send_begin_main_frame) { +void SchedulerTest::CheckMainFrameSkippedAfterLateCommit( + bool expect_send_begin_main_frame) { + // Impl thread hits deadline before commit finishes. + scheduler_->SetNeedsCommit(); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_TRUE(scheduler_->MainThreadIsInHighLatencyMode()); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + scheduler_->NotifyReadyToActivate(); + EXPECT_ACTION("SetNeedsBeginFrames(true)", client_, 0, 5); + EXPECT_ACTION("WillBeginImplFrame", client_, 1, 5); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 2, 5); + EXPECT_ACTION("ScheduledActionCommit", client_, 3, 5); + EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 4, 5); + EXPECT_TRUE(scheduler_->MainThreadIsInHighLatencyMode()); + + client_->Reset(); + scheduler_->SetNeedsCommit(); + EXPECT_TRUE(scheduler_->MainThreadIsInHighLatencyMode()); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_TRUE(scheduler_->MainThreadIsInHighLatencyMode()); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_EQ(expect_send_begin_main_frame, + scheduler_->MainThreadIsInHighLatencyMode()); + EXPECT_EQ(expect_send_begin_main_frame, + client_->HasAction("ScheduledActionSendBeginMainFrame")); +} + +TEST_F(SchedulerTest, MainFrameSkippedAfterLateCommit) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + auto fast_duration = base::TimeDelta::FromMilliseconds(1); + fake_compositor_timing_history_->SetAllEstimatesTo(fast_duration); + + bool expect_send_begin_main_frame = false; + EXPECT_SCOPED( + CheckMainFrameSkippedAfterLateCommit(expect_send_begin_main_frame)); +} + +TEST_F(SchedulerTest, + MainFrameNotSkippedAfterLateCommitInPreferImplLatencyMode) { scheduler_settings_.use_external_begin_frame_source = true; SetUpScheduler(true); + scheduler_->SetImplLatencyTakesPriority(true); + + auto fast_duration = base::TimeDelta::FromMilliseconds(1); + fake_compositor_timing_history_->SetAllEstimatesTo(fast_duration); + bool expect_send_begin_main_frame = true; + EXPECT_SCOPED( + CheckMainFrameSkippedAfterLateCommit(expect_send_begin_main_frame)); +} + +TEST_F(SchedulerTest, + MainFrameNotSkippedAfterLateCommit_CommitEstimateTooLong) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + auto fast_duration = base::TimeDelta::FromMilliseconds(1); + fake_compositor_timing_history_->SetAllEstimatesTo(fast_duration); + auto slow_duration = base::TimeDelta::FromSeconds(1); fake_compositor_timing_history_->SetBeginMainFrameToCommitDurationEstimate( - base::TimeDelta::FromMilliseconds( - begin_main_frame_to_commit_estimate_in_ms)); + slow_duration); + + bool expect_send_begin_main_frame = true; + EXPECT_SCOPED( + CheckMainFrameSkippedAfterLateCommit(expect_send_begin_main_frame)); +} + +TEST_F(SchedulerTest, + MainFrameNotSkippedAfterLateCommit_ReadyToActivateEstimateTooLong) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + auto fast_duration = base::TimeDelta::FromMilliseconds(1); + fake_compositor_timing_history_->SetAllEstimatesTo(fast_duration); + auto slow_duration = base::TimeDelta::FromSeconds(1); fake_compositor_timing_history_->SetCommitToReadyToActivateDurationEstimate( - base::TimeDelta::FromMilliseconds(commit_to_activate_estimate_in_ms)); - fake_compositor_timing_history_->SetDrawDurationEstimate( - base::TimeDelta::FromMilliseconds(1)); + slow_duration); - scheduler_->SetImplLatencyTakesPriority(impl_latency_takes_priority); + bool expect_send_begin_main_frame = true; + EXPECT_SCOPED( + CheckMainFrameSkippedAfterLateCommit(expect_send_begin_main_frame)); +} - // Impl thread hits deadline before commit finishes. +TEST_F(SchedulerTest, + MainFrameNotSkippedAfterLateCommit_ActivateEstimateTooLong) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + auto fast_duration = base::TimeDelta::FromMilliseconds(1); + fake_compositor_timing_history_->SetAllEstimatesTo(fast_duration); + auto slow_duration = base::TimeDelta::FromSeconds(1); + fake_compositor_timing_history_->SetActivateDurationEstimate(slow_duration); + + bool expect_send_begin_main_frame = true; + EXPECT_SCOPED( + CheckMainFrameSkippedAfterLateCommit(expect_send_begin_main_frame)); +} + +TEST_F(SchedulerTest, MainFrameNotSkippedAfterLateCommit_DrawEstimateTooLong) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + auto fast_duration = base::TimeDelta::FromMilliseconds(1); + fake_compositor_timing_history_->SetAllEstimatesTo(fast_duration); + auto slow_duration = base::TimeDelta::FromSeconds(1); + fake_compositor_timing_history_->SetDrawDurationEstimate(slow_duration); + + bool expect_send_begin_main_frame = true; + EXPECT_SCOPED( + CheckMainFrameSkippedAfterLateCommit(expect_send_begin_main_frame)); +} + +void SchedulerTest::ImplFrameSkippedAfterLateSwapAck( + bool swap_ack_before_deadline) { + // To get into a high latency state, this test disables automatic swap acks. + scheduler_->SetMaxSwapsPending(1); + client_->SetAutomaticSwapAck(false); + + // Draw and swap for first BeginFrame + client_->Reset(); + scheduler_->SetNeedsCommit(); + scheduler_->SetNeedsRedraw(); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + SendNextBeginFrame(); + EXPECT_ACTION("SetNeedsBeginFrames(true)", client_, 0, 4); + EXPECT_ACTION("WillBeginImplFrame", client_, 1, 4); + EXPECT_ACTION("ScheduledActionAnimate", client_, 2, 4); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 3, 4); + + client_->Reset(); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + scheduler_->NotifyReadyToActivate(); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_ACTION("ScheduledActionCommit", client_, 0, 4); + EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 1, 4); + EXPECT_ACTION("ScheduledActionAnimate", client_, 2, 4); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 3, 4); + + // Verify we skip every other frame if the swap ack consistently + // comes back late. + for (int i = 0; i < 10; i++) { + // Not calling scheduler_->DidSwapBuffersComplete() until after next + // BeginImplFrame puts the impl thread in high latency mode. + client_->Reset(); + scheduler_->SetNeedsCommit(); + scheduler_->SetNeedsRedraw(); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + SendNextBeginFrame(); + // Verify that we skip the BeginImplFrame + EXPECT_NO_ACTION(client_); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + + // Verify that we do not perform any actions after we are no longer + // swap throttled. + client_->Reset(); + if (swap_ack_before_deadline) { + // It shouldn't matter if the swap ack comes back before the deadline... + scheduler_->DidSwapBuffersComplete(); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + } else { + // ... or after the deadline. + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + scheduler_->DidSwapBuffersComplete(); + } + EXPECT_NO_ACTION(client_); + + // Verify that we start the next BeginImplFrame and continue normally + // after having just skipped a BeginImplFrame. + client_->Reset(); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + SendNextBeginFrame(); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 3); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 2, 3); + + client_->Reset(); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + scheduler_->NotifyReadyToActivate(); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_ACTION("ScheduledActionCommit", client_, 0, 4); + EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 1, 4); + EXPECT_ACTION("ScheduledActionAnimate", client_, 2, 4); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 3, 4); + } +} + +TEST_F(SchedulerTest, + ImplFrameSkippedAfterLateSwapAck_FastEstimates_SwapAckThenDeadline) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + auto fast_duration = base::TimeDelta::FromMilliseconds(1); + fake_compositor_timing_history_->SetAllEstimatesTo(fast_duration); + + bool swap_ack_before_deadline = true; + EXPECT_SCOPED(ImplFrameSkippedAfterLateSwapAck(swap_ack_before_deadline)); +} + +TEST_F(SchedulerTest, + ImplFrameSkippedAfterLateSwapAck_FastEstimates_DeadlineThenSwapAck) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + auto fast_duration = base::TimeDelta::FromMilliseconds(1); + fake_compositor_timing_history_->SetAllEstimatesTo(fast_duration); + + bool swap_ack_before_deadline = false; + EXPECT_SCOPED(ImplFrameSkippedAfterLateSwapAck(swap_ack_before_deadline)); +} + +TEST_F(SchedulerTest, + ImplFrameSkippedAfterLateSwapAck_ImplLatencyTakesPriority) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + // Even if every estimate related to the main thread is slow, we should + // still expect to recover impl thread latency if the draw is fast and we + // are in impl latency takes priority. + scheduler_->SetImplLatencyTakesPriority(true); + auto slow_duration = base::TimeDelta::FromSeconds(1); + fake_compositor_timing_history_->SetAllEstimatesTo(slow_duration); + auto fast_duration = base::TimeDelta::FromMilliseconds(1); + fake_compositor_timing_history_->SetDrawDurationEstimate(fast_duration); + + bool swap_ack_before_deadline = false; + EXPECT_SCOPED(ImplFrameSkippedAfterLateSwapAck(swap_ack_before_deadline)); +} + +TEST_F(SchedulerTest, + ImplFrameSkippedAfterLateSwapAck_OnlyImplSideUpdatesExpected) { + // This tests that we recover impl thread latency when there are no commits. + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + // To get into a high latency state, this test disables automatic swap acks. + scheduler_->SetMaxSwapsPending(1); + client_->SetAutomaticSwapAck(false); + + // Even if every estimate related to the main thread is slow, we should + // still expect to recover impl thread latency if there are no commits from + // the main thread. + auto slow_duration = base::TimeDelta::FromSeconds(1); + fake_compositor_timing_history_->SetAllEstimatesTo(slow_duration); + auto fast_duration = base::TimeDelta::FromMilliseconds(1); + fake_compositor_timing_history_->SetDrawDurationEstimate(fast_duration); + + // Draw and swap for first BeginFrame + client_->Reset(); + scheduler_->SetNeedsRedraw(); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + SendNextBeginFrame(); + EXPECT_ACTION("SetNeedsBeginFrames(true)", client_, 0, 3); + EXPECT_ACTION("WillBeginImplFrame", client_, 1, 3); + EXPECT_ACTION("ScheduledActionAnimate", client_, 2, 3); + + client_->Reset(); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_SINGLE_ACTION("ScheduledActionDrawAndSwapIfPossible", client_); + + // Verify we skip every other frame if the swap ack consistently + // comes back late. + for (int i = 0; i < 10; i++) { + // Not calling scheduler_->DidSwapBuffersComplete() until after next + // BeginImplFrame puts the impl thread in high latency mode. + client_->Reset(); + scheduler_->SetNeedsRedraw(); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + SendNextBeginFrame(); + // Verify that we skip the BeginImplFrame + EXPECT_NO_ACTION(client_); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + + // Verify that we do not perform any actions after we are no longer + // swap throttled. + client_->Reset(); + scheduler_->DidSwapBuffersComplete(); + EXPECT_NO_ACTION(client_); + + // Verify that we start the next BeginImplFrame and continue normally + // after having just skipped a BeginImplFrame. + client_->Reset(); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + SendNextBeginFrame(); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + + client_->Reset(); + // Deadline should be immediate. + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + task_runner().RunUntilTime(now_src_->NowTicks()); + EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_SINGLE_ACTION("ScheduledActionDrawAndSwapIfPossible", client_); + } +} + +void SchedulerTest::ImplFrameIsNotSkippedAfterLateSwapAck() { + // To get into a high latency state, this test disables automatic swap acks. + scheduler_->SetMaxSwapsPending(1); + client_->SetAutomaticSwapAck(false); + + // Draw and swap for first BeginFrame + client_->Reset(); + scheduler_->SetNeedsCommit(); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + SendNextBeginFrame(); + EXPECT_ACTION("SetNeedsBeginFrames(true)", client_, 0, 3); + EXPECT_ACTION("WillBeginImplFrame", client_, 1, 3); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 2, 3); + + client_->Reset(); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + scheduler_->NotifyReadyToActivate(); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_ACTION("ScheduledActionCommit", client_, 0, 4); + EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 1, 4); + EXPECT_ACTION("ScheduledActionAnimate", client_, 2, 4); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 3, 4); + + // Verify impl thread consistently operates in high latency mode + // without skipping any frames. + for (int i = 0; i < 10; i++) { + // Not calling scheduler_->DidSwapBuffersComplete() until after next frame + // puts the impl thread in high latency mode. + client_->Reset(); + scheduler_->SetNeedsCommit(); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + SendNextBeginFrame(); + EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_); + EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending()); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + + client_->Reset(); + scheduler_->DidSwapBuffersComplete(); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + scheduler_->NotifyReadyToActivate(); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + + // Verify that we don't skip the actions of the BeginImplFrame + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 0, 5); + EXPECT_ACTION("ScheduledActionCommit", client_, 1, 5); + EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 2, 5); + EXPECT_ACTION("ScheduledActionAnimate", client_, 3, 5); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 4, 5); + } +} + +TEST_F(SchedulerTest, + ImplFrameIsNotSkippedAfterLateSwapAck_CommitEstimateTooLong) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + auto fast_duration = base::TimeDelta::FromMilliseconds(1); + fake_compositor_timing_history_->SetAllEstimatesTo(fast_duration); + auto slow_duration = base::TimeDelta::FromSeconds(1); + fake_compositor_timing_history_->SetBeginMainFrameToCommitDurationEstimate( + slow_duration); + EXPECT_SCOPED(ImplFrameIsNotSkippedAfterLateSwapAck()); +} + +TEST_F(SchedulerTest, + ImplFrameIsNotSkippedAfterLateSwapAck_ReadyToActivateEstimateTooLong) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + auto fast_duration = base::TimeDelta::FromMilliseconds(1); + fake_compositor_timing_history_->SetAllEstimatesTo(fast_duration); + auto slow_duration = base::TimeDelta::FromSeconds(1); + fake_compositor_timing_history_->SetCommitToReadyToActivateDurationEstimate( + slow_duration); + EXPECT_SCOPED(ImplFrameIsNotSkippedAfterLateSwapAck()); +} + +TEST_F(SchedulerTest, + ImplFrameIsNotSkippedAfterLateSwapAck_ActivateEstimateTooLong) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + auto fast_duration = base::TimeDelta::FromMilliseconds(1); + fake_compositor_timing_history_->SetAllEstimatesTo(fast_duration); + auto slow_duration = base::TimeDelta::FromSeconds(1); + fake_compositor_timing_history_->SetActivateDurationEstimate(slow_duration); + EXPECT_SCOPED(ImplFrameIsNotSkippedAfterLateSwapAck()); +} + +TEST_F(SchedulerTest, + ImplFrameIsNotSkippedAfterLateSwapAck_DrawEstimateTooLong) { + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + auto fast_duration = base::TimeDelta::FromMilliseconds(1); + fake_compositor_timing_history_->SetAllEstimatesTo(fast_duration); + auto slow_duration = base::TimeDelta::FromSeconds(1); + fake_compositor_timing_history_->SetDrawDurationEstimate(slow_duration); + EXPECT_SCOPED(ImplFrameIsNotSkippedAfterLateSwapAck()); +} + +TEST_F(SchedulerTest, + MainFrameThenImplFrameSkippedAfterLateCommitAndLateSwapAck) { + // Set up client with custom estimates. + // This test starts off with expensive estimates to prevent latency recovery + // initially, then lowers the estimates to enable it once both the main + // and impl threads are in a high latency mode. + scheduler_settings_.use_external_begin_frame_source = true; + SetUpScheduler(true); + + auto slow_duration = base::TimeDelta::FromSeconds(1); + fake_compositor_timing_history_->SetAllEstimatesTo(slow_duration); + + // To get into a high latency state, this test disables automatic swap acks. + scheduler_->SetMaxSwapsPending(1); + client_->SetAutomaticSwapAck(false); + + // Impl thread hits deadline before commit finishes to make + // MainThreadIsInHighLatencyMode true + client_->Reset(); scheduler_->SetNeedsCommit(); EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); EXPECT_SCOPED(AdvanceFrame()); EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); - task_runner().RunPendingTasks(); // Run posted deadline. + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); EXPECT_TRUE(scheduler_->MainThreadIsInHighLatencyMode()); scheduler_->NotifyBeginMainFrameStarted(); scheduler_->NotifyReadyToCommit(); scheduler_->NotifyReadyToActivate(); EXPECT_TRUE(scheduler_->MainThreadIsInHighLatencyMode()); - EXPECT_TRUE(client_->HasAction("ScheduledActionSendBeginMainFrame")); + EXPECT_ACTION("SetNeedsBeginFrames(true)", client_, 0, 5); + EXPECT_ACTION("WillBeginImplFrame", client_, 1, 5); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 2, 5); + EXPECT_ACTION("ScheduledActionCommit", client_, 3, 5); + EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 4, 5); + + // Draw and swap for first commit, start second commit. client_->Reset(); scheduler_->SetNeedsCommit(); EXPECT_TRUE(scheduler_->MainThreadIsInHighLatencyMode()); EXPECT_SCOPED(AdvanceFrame()); EXPECT_TRUE(scheduler_->MainThreadIsInHighLatencyMode()); - task_runner().RunPendingTasks(); // Run posted deadline. - EXPECT_EQ(scheduler_->MainThreadIsInHighLatencyMode(), - should_send_begin_main_frame); - EXPECT_EQ(client_->HasAction("ScheduledActionSendBeginMainFrame"), - should_send_begin_main_frame); -} + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + scheduler_->NotifyReadyToActivate(); -TEST_F(SchedulerTest, - SkipMainFrameIfHighLatencyAndCanCommitAndActivateBeforeDeadline) { - // Set up client so that estimates indicate that we can commit and activate - // before the deadline (~8ms by default). - EXPECT_SCOPED(MainFrameInHighLatencyMode(1, 1, false, false)); -} + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 6); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 6); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 2, 6); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 3, 6); + EXPECT_ACTION("ScheduledActionCommit", client_, 4, 6); + EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 5, 6); -TEST_F(SchedulerTest, NotSkipMainFrameIfHighLatencyAndCanCommitTooLong) { - // Set up client so that estimates indicate that the commit cannot finish - // before the deadline (~8ms by default). - EXPECT_SCOPED(MainFrameInHighLatencyMode(10, 1, false, true)); -} + // Don't call scheduler_->DidSwapBuffersComplete() until after next frame + // to put the impl thread in a high latency mode. + client_->Reset(); + scheduler_->SetNeedsCommit(); + EXPECT_TRUE(scheduler_->MainThreadIsInHighLatencyMode()); + EXPECT_TRUE(scheduler_->SwapThrottled()); + EXPECT_SCOPED(AdvanceFrame()); + EXPECT_TRUE(scheduler_->MainThreadIsInHighLatencyMode()); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); -TEST_F(SchedulerTest, NotSkipMainFrameIfHighLatencyAndCanActivateTooLong) { - // Set up client so that estimates indicate that the activate cannot finish - // before the deadline (~8ms by default). - EXPECT_SCOPED(MainFrameInHighLatencyMode(1, 10, false, true)); -} + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 2); + // Note: BeginMainFrame and swap are skipped here because of + // swap ack backpressure, not because of latency recovery. + EXPECT_FALSE(client_->HasAction("ScheduledActionSendBeginMainFrame")); + EXPECT_FALSE(client_->HasAction("ScheduledActionDrawAndSwapIfPossible")); + EXPECT_TRUE(scheduler_->MainThreadIsInHighLatencyMode()); + + // Lower estimates so that the scheduler will attempt latency recovery. + auto fast_duration = base::TimeDelta::FromMilliseconds(1); + fake_compositor_timing_history_->SetAllEstimatesTo(fast_duration); + + // Now that both threads are in a high latency mode, make sure we + // skip the BeginMainFrame, then the BeginImplFrame, but not both + // at the same time. + + // Verify we skip BeginMainFrame first. + client_->Reset(); + // Previous commit request is still outstanding. + EXPECT_TRUE(scheduler_->NeedsCommit()); + EXPECT_TRUE(scheduler_->SwapThrottled()); + SendNextBeginFrame(); + EXPECT_TRUE(scheduler_->MainThreadIsInHighLatencyMode()); + scheduler_->DidSwapBuffersComplete(); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3); + EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 3); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 2, 3); + + // Verify we skip the BeginImplFrame second. + client_->Reset(); + // Previous commit request is still outstanding. + EXPECT_TRUE(scheduler_->NeedsCommit()); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + SendNextBeginFrame(); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + scheduler_->DidSwapBuffersComplete(); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + + EXPECT_NO_ACTION(client_); + + // Then verify we operate in a low latency mode. + client_->Reset(); + // Previous commit request is still outstanding. + EXPECT_TRUE(scheduler_->NeedsCommit()); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + SendNextBeginFrame(); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + scheduler_->NotifyBeginMainFrameStarted(); + scheduler_->NotifyReadyToCommit(); + scheduler_->NotifyReadyToActivate(); + task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true)); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); + scheduler_->DidSwapBuffersComplete(); + EXPECT_FALSE(scheduler_->MainThreadIsInHighLatencyMode()); -TEST_F(SchedulerTest, NotSkipMainFrameInPreferImplLatencyMode) { - // Set up client so that estimates indicate that we can commit and activate - // before the deadline (~8ms by default), but also enable impl latency takes - // priority mode. - EXPECT_SCOPED(MainFrameInHighLatencyMode(1, 1, true, true)); + EXPECT_ACTION("WillBeginImplFrame", client_, 0, 6); + EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 6); + EXPECT_ACTION("ScheduledActionCommit", client_, 2, 6); + EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 3, 6); + EXPECT_ACTION("ScheduledActionAnimate", client_, 4, 6); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 5, 6); } TEST_F( @@ -1398,19 +1867,10 @@ TEST_F( // NPAPI plugins on Windows block the Browser UI thread on the Renderer main // thread. This prevents the scheduler from receiving any pending swap acks. - // Since we are simulating a long commit, set up a client with draw duration - // estimates that prevent skipping main frames to get to low latency mode. scheduler_settings_.use_external_begin_frame_source = true; scheduler_settings_.main_frame_while_swap_throttled_enabled = true; SetUpScheduler(true); - fake_compositor_timing_history_->SetBeginMainFrameToCommitDurationEstimate( - base::TimeDelta::FromMilliseconds(32)); - fake_compositor_timing_history_->SetCommitToReadyToActivateDurationEstimate( - base::TimeDelta::FromMilliseconds(32)); - fake_compositor_timing_history_->SetDrawDurationEstimate( - base::TimeDelta::FromMilliseconds(1)); - // Disables automatic swap acks so this test can force swap ack throttling // to simulate a blocked Browser ui thread. scheduler_->SetMaxSwapsPending(1); @@ -1477,20 +1937,11 @@ TEST_F(SchedulerTest, // swap trottled and we have a pending tree and active tree that // still needs to be drawn for the first time. - // Since we are simulating a long commit, set up a client with draw duration - // estimates that prevent skipping main frames to get to low latency mode. scheduler_settings_.use_external_begin_frame_source = true; scheduler_settings_.main_frame_while_swap_throttled_enabled = true; scheduler_settings_.main_frame_before_activation_enabled = true; SetUpScheduler(true); - fake_compositor_timing_history_->SetBeginMainFrameToCommitDurationEstimate( - base::TimeDelta::FromMilliseconds(32)); - fake_compositor_timing_history_->SetCommitToReadyToActivateDurationEstimate( - base::TimeDelta::FromMilliseconds(32)); - fake_compositor_timing_history_->SetDrawDurationEstimate( - base::TimeDelta::FromMilliseconds(1)); - // Disables automatic swap acks so this test can force swap ack throttling // to simulate a blocked Browser ui thread. scheduler_->SetMaxSwapsPending(1); @@ -1572,13 +2023,6 @@ TEST_F( scheduler_settings_.main_frame_before_activation_enabled = true; SetUpScheduler(true); - fake_compositor_timing_history_->SetBeginMainFrameToCommitDurationEstimate( - base::TimeDelta::FromMilliseconds(32)); - fake_compositor_timing_history_->SetCommitToReadyToActivateDurationEstimate( - base::TimeDelta::FromMilliseconds(32)); - fake_compositor_timing_history_->SetDrawDurationEstimate( - base::TimeDelta::FromMilliseconds(1)); - // Disables automatic swap acks so this test can force swap ack throttling // to simulate a blocked Browser ui thread. scheduler_->SetMaxSwapsPending(1); diff --git a/cc/test/scheduler_test_common.cc b/cc/test/scheduler_test_common.cc index 48e7eb5..7419b21 100644 --- a/cc/test/scheduler_test_common.cc +++ b/cc/test/scheduler_test_common.cc @@ -83,6 +83,14 @@ FakeCompositorTimingHistory::FakeCompositorTimingHistory( FakeCompositorTimingHistory::~FakeCompositorTimingHistory() { } +void FakeCompositorTimingHistory::SetAllEstimatesTo(base::TimeDelta duration) { + begin_main_frame_to_commit_duration_ = duration; + commit_to_ready_to_activate_duration_ = duration; + prepare_tiles_duration_ = duration; + activate_duration_ = duration; + draw_duration_ = duration; +} + void FakeCompositorTimingHistory::SetBeginMainFrameToCommitDurationEstimate( base::TimeDelta duration) { begin_main_frame_to_commit_duration_ = duration; diff --git a/cc/test/scheduler_test_common.h b/cc/test/scheduler_test_common.h index a887e8a..ef45ae9 100644 --- a/cc/test/scheduler_test_common.h +++ b/cc/test/scheduler_test_common.h @@ -166,6 +166,8 @@ class FakeCompositorTimingHistory : public CompositorTimingHistory { static scoped_ptr<FakeCompositorTimingHistory> Create(); ~FakeCompositorTimingHistory() override; + void SetAllEstimatesTo(base::TimeDelta duration); + void SetBeginMainFrameToCommitDurationEstimate(base::TimeDelta duration); void SetCommitToReadyToActivateDurationEstimate(base::TimeDelta duration); void SetPrepareTilesDurationEstimate(base::TimeDelta duration); @@ -211,11 +213,19 @@ class TestScheduler : public Scheduler { return begin_retro_frame_args_.empty(); } + bool SwapThrottled() const { return state_machine_.SwapThrottled(); } + bool CanStart() const { return state_machine_.CanStartForTesting(); } + bool NeedsCommit() const { return state_machine_.needs_commit(); } + BeginFrameSource& frame_source() { return *frame_source_; } bool FrameProductionThrottled() { return throttle_frame_production_; } + bool MainThreadIsInHighLatencyMode() const { + return state_machine_.MainThreadIsInHighLatencyMode(); + } + ~TestScheduler() override; base::TimeDelta BeginImplFrameInterval() { |