summaryrefslogtreecommitdiffstats
path: root/cc
diff options
context:
space:
mode:
Diffstat (limited to 'cc')
-rw-r--r--cc/scheduler/scheduler.cc18
-rw-r--r--cc/scheduler/scheduler.h5
-rw-r--r--cc/scheduler/scheduler_settings.cc3
-rw-r--r--cc/scheduler/scheduler_settings.h1
-rw-r--r--cc/scheduler/scheduler_state_machine.cc52
-rw-r--r--cc/scheduler/scheduler_state_machine.h7
-rw-r--r--cc/scheduler/scheduler_unittest.cc89
7 files changed, 173 insertions, 2 deletions
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index 4f2dbf6..4e06d80 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -188,6 +188,13 @@ void Scheduler::BeginImplFrame(const BeginFrameArgs& args) {
last_begin_impl_frame_args_ = args;
last_begin_impl_frame_args_.deadline -= client_->DrawDurationEstimate();
state_machine_.OnBeginImplFrame(last_begin_impl_frame_args_);
+
+ if (settings_.switch_to_low_latency_if_possible) {
+ state_machine_.SetSkipBeginMainFrameToReduceLatency(
+ state_machine_.MainThreadIsInHighLatencyMode() &&
+ CanCommitAndActivateBeforeDeadline());
+ }
+
ProcessScheduledActions();
if (!state_machine_.HasInitializedOutputSurface())
@@ -348,4 +355,15 @@ bool Scheduler::WillDrawIfNeeded() const {
return !state_machine_.PendingDrawsShouldBeAborted();
}
+bool Scheduler::CanCommitAndActivateBeforeDeadline() const {
+ // Check if the main thread computation and commit can be finished before the
+ // impl thread's deadline.
+ base::TimeTicks estimated_draw_time =
+ last_begin_impl_frame_args_.frame_time +
+ client_->BeginMainFrameToCommitDurationEstimate() +
+ client_->CommitToActivateDurationEstimate();
+
+ return estimated_draw_time < last_begin_impl_frame_args_.deadline;
+}
+
} // namespace cc
diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h
index e4a8bf3..8362a74 100644
--- a/cc/scheduler/scheduler.h
+++ b/cc/scheduler/scheduler.h
@@ -103,6 +103,9 @@ class CC_EXPORT Scheduler {
bool ManageTilesPending() const {
return state_machine_.ManageTilesPending();
}
+ bool MainThreadIsInHighLatencyMode() const {
+ return state_machine_.MainThreadIsInHighLatencyMode();
+ }
bool WillDrawIfNeeded() const;
@@ -134,6 +137,8 @@ class CC_EXPORT Scheduler {
void DrawAndReadback();
void ProcessScheduledActions();
+ bool CanCommitAndActivateBeforeDeadline() const;
+
const SchedulerSettings settings_;
SchedulerClient* client_;
diff --git a/cc/scheduler/scheduler_settings.cc b/cc/scheduler/scheduler_settings.cc
index 9b8032c..ad925d4 100644
--- a/cc/scheduler/scheduler_settings.cc
+++ b/cc/scheduler/scheduler_settings.cc
@@ -12,7 +12,8 @@ SchedulerSettings::SchedulerSettings()
timeout_and_draw_when_animation_checkerboards(true),
maximum_number_of_failed_draws_before_draw_is_forced_(3),
using_synchronous_renderer_compositor(false),
- throttle_frame_production(true) {}
+ throttle_frame_production(true),
+ switch_to_low_latency_if_possible(false) {}
SchedulerSettings::~SchedulerSettings() {}
diff --git a/cc/scheduler/scheduler_settings.h b/cc/scheduler/scheduler_settings.h
index 7857a70..b35cb7e 100644
--- a/cc/scheduler/scheduler_settings.h
+++ b/cc/scheduler/scheduler_settings.h
@@ -20,6 +20,7 @@ class CC_EXPORT SchedulerSettings {
int maximum_number_of_failed_draws_before_draw_is_forced_;
bool using_synchronous_renderer_compositor;
bool throttle_frame_production;
+ bool switch_to_low_latency_if_possible;
};
} // namespace cc
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc
index 4d9e5e9..e6c8275 100644
--- a/cc/scheduler/scheduler_state_machine.cc
+++ b/cc/scheduler/scheduler_state_machine.cc
@@ -42,7 +42,8 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings)
active_tree_needs_first_draw_(false),
draw_if_possible_failed_(false),
did_create_and_initialize_first_output_surface_(false),
- smoothness_takes_priority_(false) {}
+ smoothness_takes_priority_(false),
+ skip_begin_main_frame_to_reduce_latency_(false) {}
const char* SchedulerStateMachine::OutputSurfaceStateToString(
OutputSurfaceState state) {
@@ -260,6 +261,10 @@ scoped_ptr<base::Value> SchedulerStateMachine::AsValue() const {
did_create_and_initialize_first_output_surface_);
minor_state->SetBoolean("smoothness_takes_priority",
smoothness_takes_priority_);
+ minor_state->SetBoolean("main_thread_is_in_high_latency_mode",
+ MainThreadIsInHighLatencyMode());
+ minor_state->SetBoolean("skip_begin_main_frame_to_reduce_latency",
+ skip_begin_main_frame_to_reduce_latency_);
state->Set("minor_state", minor_state.release());
return state.PassAs<base::Value>();
@@ -486,6 +491,9 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const {
if (!HasInitializedOutputSurface())
return false;
+ if (skip_begin_main_frame_to_reduce_latency_)
+ return false;
+
return true;
}
@@ -763,6 +771,10 @@ void SchedulerStateMachine::SetMainThreadNeedsLayerTextures() {
main_thread_needs_layer_textures_ = true;
}
+void SchedulerStateMachine::SetSkipBeginMainFrameToReduceLatency(bool skip) {
+ skip_begin_main_frame_to_reduce_latency_ = skip;
+}
+
bool SchedulerStateMachine::BeginImplFrameNeeded() const {
// Proactive BeginImplFrames are bad for the synchronous compositor because we
// have to draw when we get the BeginImplFrame and could end up drawing many
@@ -926,6 +938,44 @@ bool SchedulerStateMachine::ShouldTriggerBeginImplFrameDeadlineEarly() const {
return false;
}
+bool SchedulerStateMachine::MainThreadIsInHighLatencyMode() const {
+ // If we just sent a BeginMainFrame and haven't hit the deadline yet, the main
+ // thread is in a low latency mode.
+ if (last_frame_number_begin_main_frame_sent_ == current_frame_number_ &&
+ (begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING ||
+ begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME))
+ return false;
+
+ // If there's a commit in progress it must either be from the previous frame
+ // or it started after the impl thread's deadline. In either case the main
+ // thread is in high latency mode.
+ if (commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS ||
+ commit_state_ == COMMIT_STATE_READY_TO_COMMIT)
+ return true;
+
+ // Similarly, if there's a pending tree the main thread is in high latency
+ // mode, because either
+ // it's from the previous frame
+ // or
+ // we're currently drawing the active tree and the pending tree will thus
+ // only be drawn in the next frame.
+ if (has_pending_tree_)
+ return true;
+
+ if (begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE) {
+ // Even if there's a new active tree to draw at the deadline or we've just
+ // drawn 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_ ||
+ last_frame_number_swap_performed_ == current_frame_number_) &&
+ last_frame_number_begin_main_frame_sent_ != current_frame_number_;
+ }
+
+ // If the active tree needs its first draw in any other state, we know the
+ // main thread is in a high latency mode.
+ return active_tree_needs_first_draw_;
+}
+
void SchedulerStateMachine::DidEnterPollForAnticipatedDrawTriggers() {
current_frame_number_++;
inside_poll_for_anticipated_draw_triggers_ = true;
diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h
index 50efada..1790353 100644
--- a/cc/scheduler/scheduler_state_machine.h
+++ b/cc/scheduler/scheduler_state_machine.h
@@ -146,6 +146,10 @@ class CC_EXPORT SchedulerStateMachine {
return begin_impl_frame_state_;
}
+ // If the main thread didn't manage to produce a new frame in time for the
+ // impl thread to draw, it is in a high latency mode.
+ bool MainThreadIsInHighLatencyMode() const;
+
// PollForAnticipatedDrawTriggers is used by the synchronous compositor to
// avoid requesting BeginImplFrames when we won't actually draw but still
// need to advance our state at vsync intervals.
@@ -209,6 +213,8 @@ class CC_EXPORT SchedulerStateMachine {
// Set that we can create the first OutputSurface and start the scheduler.
void SetCanStart() { can_start_ = true; }
+ void SetSkipBeginMainFrameToReduceLatency(bool skip);
+
// Indicates whether drawing would, at this time, make sense.
// CanDraw can be used to suppress flashes or checkerboarding
// when such behavior would be undesirable.
@@ -290,6 +296,7 @@ class CC_EXPORT SchedulerStateMachine {
bool draw_if_possible_failed_;
bool did_create_and_initialize_first_output_surface_;
bool smoothness_takes_priority_;
+ bool skip_begin_main_frame_to_reduce_latency_;
private:
DISALLOW_COPY_AND_ASSIGN(SchedulerStateMachine);
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index 03f3148..772ce7c 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -8,6 +8,7 @@
#include "base/logging.h"
#include "base/memory/scoped_vector.h"
+#include "base/time/time.h"
#include "cc/test/scheduler_test_common.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -1128,5 +1129,93 @@ TEST(SchedulerTest, ManageTilesOncePerFrame) {
EXPECT_FALSE(scheduler->ManageTilesPending());
}
+class SchedulerClientWithFixedEstimates : public FakeSchedulerClient {
+ public:
+ SchedulerClientWithFixedEstimates(
+ base::TimeDelta draw_duration,
+ base::TimeDelta begin_main_frame_to_commit_duration,
+ base::TimeDelta commit_to_activate_duration)
+ : draw_duration_(draw_duration),
+ begin_main_frame_to_commit_duration_(
+ begin_main_frame_to_commit_duration),
+ commit_to_activate_duration_(commit_to_activate_duration) {}
+
+ virtual base::TimeDelta DrawDurationEstimate() OVERRIDE {
+ return draw_duration_;
+ }
+ virtual base::TimeDelta BeginMainFrameToCommitDurationEstimate() OVERRIDE {
+ return begin_main_frame_to_commit_duration_;
+ }
+ virtual base::TimeDelta CommitToActivateDurationEstimate() OVERRIDE {
+ return commit_to_activate_duration_;
+ }
+
+ private:
+ base::TimeDelta draw_duration_;
+ base::TimeDelta begin_main_frame_to_commit_duration_;
+ base::TimeDelta commit_to_activate_duration_;
+};
+
+void MainFrameInHighLatencyMode(int64 begin_main_frame_to_commit_estimate_in_ms,
+ int64 commit_to_activate_estimate_in_ms,
+ bool should_send_begin_main_frame) {
+ // Set up client with specified estimates (draw duration is set to 1).
+ SchedulerClientWithFixedEstimates client(
+ base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromMilliseconds(
+ begin_main_frame_to_commit_estimate_in_ms),
+ base::TimeDelta::FromMilliseconds(commit_to_activate_estimate_in_ms));
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.deadline_scheduling_enabled = true;
+ scheduler_settings.switch_to_low_latency_if_possible = true;
+ Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
+ scheduler->SetCanStart();
+ scheduler->SetVisible(true);
+ scheduler->SetCanDraw(true);
+ InitializeOutputSurfaceAndFirstCommit(scheduler);
+
+ // Impl thread hits deadline before commit finishes.
+ client.Reset();
+ scheduler->SetNeedsCommit();
+ EXPECT_FALSE(scheduler->MainThreadIsInHighLatencyMode());
+ scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_FALSE(scheduler->MainThreadIsInHighLatencyMode());
+ scheduler->OnBeginImplFrameDeadline();
+ EXPECT_TRUE(scheduler->MainThreadIsInHighLatencyMode());
+ scheduler->FinishCommit();
+ EXPECT_TRUE(scheduler->MainThreadIsInHighLatencyMode());
+ EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginMainFrame"));
+
+ client.Reset();
+ scheduler->SetNeedsCommit();
+ EXPECT_TRUE(scheduler->MainThreadIsInHighLatencyMode());
+ scheduler->BeginImplFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_TRUE(scheduler->MainThreadIsInHighLatencyMode());
+ scheduler->OnBeginImplFrameDeadline();
+ EXPECT_EQ(scheduler->MainThreadIsInHighLatencyMode(),
+ should_send_begin_main_frame);
+ EXPECT_EQ(client.HasAction("ScheduledActionSendBeginMainFrame"),
+ should_send_begin_main_frame);
+}
+
+TEST(SchedulerTest,
+ SkipMainFrameIfHighLatencyAndCanCommitAndActivateBeforeDeadline) {
+ // Set up client so that estimates indicate that we can commit and activate
+ // before the deadline (~8ms by default).
+ MainFrameInHighLatencyMode(1, 1, false);
+}
+
+TEST(SchedulerTest, NotSkipMainFrameIfHighLatencyAndCanCommitTooLong) {
+ // Set up client so that estimates indicate that the commit cannot finish
+ // before the deadline (~8ms by default).
+ MainFrameInHighLatencyMode(10, 1, true);
+}
+
+TEST(SchedulerTest, NotSkipMainFrameIfHighLatencyAndCanActivateTooLong) {
+ // Set up client so that estimates indicate that the activate cannot finish
+ // before the deadline (~8ms by default).
+ MainFrameInHighLatencyMode(1, 10, true);
+}
+
} // namespace
} // namespace cc