diff options
Diffstat (limited to 'cc')
-rw-r--r-- | cc/scheduler/scheduler.cc | 18 | ||||
-rw-r--r-- | cc/scheduler/scheduler.h | 5 | ||||
-rw-r--r-- | cc/scheduler/scheduler_settings.cc | 3 | ||||
-rw-r--r-- | cc/scheduler/scheduler_settings.h | 1 | ||||
-rw-r--r-- | cc/scheduler/scheduler_state_machine.cc | 52 | ||||
-rw-r--r-- | cc/scheduler/scheduler_state_machine.h | 7 | ||||
-rw-r--r-- | cc/scheduler/scheduler_unittest.cc | 89 |
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 |