summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbrianderson <brianderson@chromium.org>2015-07-09 19:29:07 -0700
committerCommit bot <commit-bot@chromium.org>2015-07-10 02:29:44 +0000
commit6d50e7a7df5b33048b9bcd641171b31642b58850 (patch)
treea4dbeceb66c69a002252132278162f430a0a7341
parent068d2ad77420efd2fe8146ff6c6d6c1b44ce2c08 (diff)
downloadchromium_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.cc74
-rw-r--r--cc/scheduler/scheduler.h7
-rw-r--r--cc/scheduler/scheduler_state_machine.cc34
-rw-r--r--cc/scheduler/scheduler_state_machine.h7
-rw-r--r--cc/scheduler/scheduler_unittest.cc586
-rw-r--r--cc/test/scheduler_test_common.cc8
-rw-r--r--cc/test/scheduler_test_common.h10
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() {