diff options
Diffstat (limited to 'cc/scheduler')
-rw-r--r-- | cc/scheduler/frame_rate_controller.cc | 26 | ||||
-rw-r--r-- | cc/scheduler/frame_rate_controller.h | 21 | ||||
-rw-r--r-- | cc/scheduler/frame_rate_controller_unittest.cc | 6 | ||||
-rw-r--r-- | cc/scheduler/scheduler.cc | 125 | ||||
-rw-r--r-- | cc/scheduler/scheduler.h | 32 | ||||
-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 | 15 | ||||
-rw-r--r-- | cc/scheduler/scheduler_state_machine.h | 6 | ||||
-rw-r--r-- | cc/scheduler/scheduler_state_machine_unittest.cc | 2 | ||||
-rw-r--r-- | cc/scheduler/scheduler_unittest.cc | 283 | ||||
-rw-r--r-- | cc/scheduler/vsync_time_source.cc | 76 | ||||
-rw-r--r-- | cc/scheduler/vsync_time_source.h | 78 | ||||
-rw-r--r-- | cc/scheduler/vsync_time_source_unittest.cc | 124 |
14 files changed, 546 insertions, 252 deletions
diff --git a/cc/scheduler/frame_rate_controller.cc b/cc/scheduler/frame_rate_controller.cc index 90cf0d7..b4f1272 100644 --- a/cc/scheduler/frame_rate_controller.cc +++ b/cc/scheduler/frame_rate_controller.cc @@ -21,9 +21,7 @@ class FrameRateControllerTimeSourceAdapter : public TimeSourceClient { } virtual ~FrameRateControllerTimeSourceAdapter() {} - virtual void OnTimerTick() OVERRIDE { - frame_rate_controller_->OnTimerTick(); - } + virtual void OnTimerTick() OVERRIDE { frame_rate_controller_->OnTimerTick(); } private: explicit FrameRateControllerTimeSourceAdapter( @@ -36,7 +34,7 @@ class FrameRateControllerTimeSourceAdapter : public TimeSourceClient { FrameRateController::FrameRateController(scoped_refptr<TimeSource> timer) : client_(NULL), num_frames_pending_(0), - max_swaps_pending_(0), + max_frames_pending_(0), time_source_(timer), active_(false), is_time_source_throttling_(true), @@ -50,7 +48,7 @@ FrameRateController::FrameRateController(scoped_refptr<TimeSource> timer) FrameRateController::FrameRateController(Thread* thread) : client_(NULL), num_frames_pending_(0), - max_swaps_pending_(0), + max_frames_pending_(0), active_(false), is_time_source_throttling_(false), weak_factory_(this), @@ -77,9 +75,9 @@ void FrameRateController::SetActive(bool active) { } } -void FrameRateController::SetMaxSwapsPending(int max_swaps_pending) { - DCHECK_GE(max_swaps_pending, 0); - max_swaps_pending_ = max_swaps_pending; +void FrameRateController::SetMaxFramesPending(int max_frames_pending) { + DCHECK_GE(max_frames_pending, 0); + max_frames_pending_ = max_frames_pending; } void FrameRateController::SetTimebaseAndInterval(base::TimeTicks timebase, @@ -89,17 +87,15 @@ void FrameRateController::SetTimebaseAndInterval(base::TimeTicks timebase, } void FrameRateController::OnTimerTick() { - TRACE_EVENT0("cc", "FrameRateController::OnTimerTick"); DCHECK(active_); // Check if we have too many frames in flight. bool throttled = - max_swaps_pending_ && num_frames_pending_ >= max_swaps_pending_; + max_frames_pending_ && num_frames_pending_ >= max_frames_pending_; TRACE_COUNTER_ID1("cc", "ThrottledCompositor", thread_, throttled); - if (client_) { - client_->FrameRateControllerTick(throttled); - } + if (client_) + client_->BeginFrame(throttled); if (!is_time_source_throttling_ && !throttled) PostManualTick(); @@ -112,9 +108,7 @@ void FrameRateController::PostManualTick() { } } -void FrameRateController::ManualTick() { - OnTimerTick(); -} +void FrameRateController::ManualTick() { OnTimerTick(); } void FrameRateController::DidSwapBuffers() { num_frames_pending_++; diff --git a/cc/scheduler/frame_rate_controller.h b/cc/scheduler/frame_rate_controller.h index 70964e2..070c26a 100644 --- a/cc/scheduler/frame_rate_controller.h +++ b/cc/scheduler/frame_rate_controller.h @@ -15,21 +15,18 @@ namespace cc { class Thread; class TimeSource; -class FrameRateController; class CC_EXPORT FrameRateControllerClient { - protected: - virtual ~FrameRateControllerClient() {} - public: // Throttled is true when we have a maximum number of frames pending. - virtual void FrameRateControllerTick(bool throttled) = 0; + virtual void BeginFrame(bool throttled) = 0; + + protected: + virtual ~FrameRateControllerClient() {} }; class FrameRateControllerTimeSourceAdapter; -// The FrameRateController is used in cases where we self-tick (i.e. BeginFrame -// is not sent by a parent compositor. class CC_EXPORT FrameRateController { public: enum { @@ -44,7 +41,6 @@ class CC_EXPORT FrameRateController { void SetClient(FrameRateControllerClient* client) { client_ = client; } void SetActive(bool active); - bool IsActive() { return active_; } // Use the following methods to adjust target frame rate. // @@ -55,9 +51,9 @@ class CC_EXPORT FrameRateController { void DidSwapBuffers(); void DidSwapBuffersComplete(); void DidAbortAllPendingFrames(); - void SetMaxSwapsPending(int max_swaps_pending); // 0 for unlimited. - int MaxSwapsPending() const { return max_swaps_pending_; } - int NumSwapsPendingForTesting() const { return num_frames_pending_; } + void SetMaxFramesPending(int max_frames_pending); // 0 for unlimited. + int MaxFramesPending() const { return max_frames_pending_; } + int NumFramesPendingForTesting() const { return num_frames_pending_; } // This returns null for unthrottled frame-rate. base::TimeTicks NextTickTime(); @@ -77,7 +73,7 @@ class CC_EXPORT FrameRateController { FrameRateControllerClient* client_; int num_frames_pending_; - int max_swaps_pending_; + int max_frames_pending_; scoped_refptr<TimeSource> time_source_; scoped_ptr<FrameRateControllerTimeSourceAdapter> time_source_client_adapter_; bool active_; @@ -87,7 +83,6 @@ class CC_EXPORT FrameRateController { base::WeakPtrFactory<FrameRateController> weak_factory_; Thread* thread_; - private: DISALLOW_COPY_AND_ASSIGN(FrameRateController); }; diff --git a/cc/scheduler/frame_rate_controller_unittest.cc b/cc/scheduler/frame_rate_controller_unittest.cc index 3fcf4a7..183f576 100644 --- a/cc/scheduler/frame_rate_controller_unittest.cc +++ b/cc/scheduler/frame_rate_controller_unittest.cc @@ -17,7 +17,7 @@ class FakeFrameRateControllerClient : public cc::FrameRateControllerClient { void Reset() { began_frame_ = false; } bool BeganFrame() const { return began_frame_; } - virtual void FrameRateControllerTick(bool throttled) OVERRIDE { + virtual void BeginFrame(bool throttled) OVERRIDE { began_frame_ = !throttled; } @@ -74,7 +74,7 @@ TEST(FrameRateControllerTest, TestFrameThrottling_TwoFramesInFlight) { controller.SetClient(&client); controller.SetActive(true); - controller.SetMaxSwapsPending(2); + controller.SetMaxFramesPending(2); base::TimeTicks elapsed; // Muck around with time a bit @@ -132,7 +132,7 @@ TEST(FrameRateControllerTest, TestFrameThrottling_Unthrottled) { FrameRateController controller(&thread); controller.SetClient(&client); - controller.SetMaxSwapsPending(2); + controller.SetMaxFramesPending(2); // SetActive triggers 1st frame, make sure the BeginFrame callback // is called diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc index 7110bff..d308b0d 100644 --- a/cc/scheduler/scheduler.cc +++ b/cc/scheduler/scheduler.cc @@ -7,29 +7,23 @@ #include "base/auto_reset.h" #include "base/debug/trace_event.h" #include "base/logging.h" -#include "cc/base/thread.h" namespace cc { Scheduler::Scheduler(SchedulerClient* client, + scoped_ptr<FrameRateController> frame_rate_controller, const SchedulerSettings& scheduler_settings) : settings_(scheduler_settings), client_(client), - weak_factory_(this), - last_set_needs_begin_frame_(false), - has_pending_begin_frame_(false), - last_begin_frame_time_(base::TimeTicks()), - // TODO(brianderson): Pass with BeginFrame in the near future. - interval_(base::TimeDelta::FromMicroseconds(16666)), + frame_rate_controller_(frame_rate_controller.Pass()), state_machine_(scheduler_settings), inside_process_scheduled_actions_(false) { DCHECK(client_); + frame_rate_controller_->SetClient(this); DCHECK(!state_machine_.BeginFrameNeededByImplThread()); } -Scheduler::~Scheduler() { - client_->SetNeedsBeginFrameOnImplThread(false); -} +Scheduler::~Scheduler() { frame_rate_controller_->SetActive(false); } void Scheduler::SetCanStart() { state_machine_.SetCanStart(); @@ -94,6 +88,23 @@ void Scheduler::BeginFrameAbortedByMainThread() { ProcessScheduledActions(); } +void Scheduler::SetMaxFramesPending(int max_frames_pending) { + frame_rate_controller_->SetMaxFramesPending(max_frames_pending); +} + +int Scheduler::MaxFramesPending() const { + return frame_rate_controller_->MaxFramesPending(); +} + +int Scheduler::NumFramesPendingForTesting() const { + return frame_rate_controller_->NumFramesPendingForTesting(); +} + +void Scheduler::DidSwapBuffersComplete() { + TRACE_EVENT0("cc", "Scheduler::DidSwapBuffersComplete"); + frame_rate_controller_->DidSwapBuffersComplete(); +} + void Scheduler::DidLoseOutputSurface() { TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface"); state_machine_.DidLoseOutputSurface(); @@ -102,69 +113,31 @@ void Scheduler::DidLoseOutputSurface() { void Scheduler::DidCreateAndInitializeOutputSurface() { TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface"); + frame_rate_controller_->DidAbortAllPendingFrames(); state_machine_.DidCreateAndInitializeOutputSurface(); - has_pending_begin_frame_ = false; - last_set_needs_begin_frame_ = false; ProcessScheduledActions(); } +void Scheduler::SetTimebaseAndInterval(base::TimeTicks timebase, + base::TimeDelta interval) { + frame_rate_controller_->SetTimebaseAndInterval(timebase, interval); +} + base::TimeTicks Scheduler::AnticipatedDrawTime() { - TRACE_EVENT0("cc", "Scheduler::AnticipatedDrawTime"); - base::TimeTicks now = base::TimeTicks::Now(); - int64 intervals = ((now - last_begin_frame_time_) / interval_) + 1; - return last_begin_frame_time_ + (interval_ * intervals); + return frame_rate_controller_->NextTickTime(); } base::TimeTicks Scheduler::LastBeginFrameOnImplThreadTime() { - return last_begin_frame_time_; -} - -void Scheduler::SetupNextBeginFrameIfNeeded() { - // Determine if we need BeginFrame notifications. - // If we do, always request the BeginFrame immediately. - // If not, only disable on the next BeginFrame to avoid unnecessary toggles. - // The synchronous renderer compositor requires immediate disables though. - bool needs_begin_frame = state_machine_.BeginFrameNeededByImplThread(); - if ((needs_begin_frame || - state_machine_.inside_begin_frame() || - settings_.using_synchronous_renderer_compositor) && - (needs_begin_frame != last_set_needs_begin_frame_)) { - client_->SetNeedsBeginFrameOnImplThread(needs_begin_frame); - last_set_needs_begin_frame_ = needs_begin_frame; - } - - // Request another BeginFrame if we haven't drawn for now until we have - // deadlines implemented. - if (state_machine_.inside_begin_frame() && has_pending_begin_frame_) { - has_pending_begin_frame_ = false; - client_->SetNeedsBeginFrameOnImplThread(true); - } + return frame_rate_controller_->LastTickTime(); } -void Scheduler::BeginFrame(base::TimeTicks frame_time) { - TRACE_EVENT0("cc", "Scheduler::BeginFrame"); - DCHECK(!has_pending_begin_frame_); - has_pending_begin_frame_ = true; - last_begin_frame_time_ = frame_time; - state_machine_.DidEnterBeginFrame(); - state_machine_.SetFrameTime(frame_time); +void Scheduler::BeginFrame(bool throttled) { + TRACE_EVENT1("cc", "Scheduler::BeginFrame", "throttled", throttled); + if (!throttled) + state_machine_.DidEnterBeginFrame(); ProcessScheduledActions(); - state_machine_.DidLeaveBeginFrame(); -} - -void Scheduler::DrawAndSwapIfPossible() { - ScheduledActionDrawAndSwapResult result = - client_->ScheduledActionDrawAndSwapIfPossible(); - state_machine_.DidDrawIfPossibleCompleted(result.did_draw); - if (result.did_swap) - has_pending_begin_frame_ = false; -} - -void Scheduler::DrawAndSwapForced() { - ScheduledActionDrawAndSwapResult result = - client_->ScheduledActionDrawAndSwapForced(); - if (result.did_swap) - has_pending_begin_frame_ = false; + if (!throttled) + state_machine_.DidLeaveBeginFrame(); } void Scheduler::ProcessScheduledActions() { @@ -178,6 +151,9 @@ void Scheduler::ProcessScheduledActions() { SchedulerStateMachine::Action action = state_machine_.NextAction(); while (action != SchedulerStateMachine::ACTION_NONE) { state_machine_.UpdateState(action); + TRACE_EVENT1( + "cc", "Scheduler::ProcessScheduledActions()", "action", action); + switch (action) { case SchedulerStateMachine::ACTION_NONE: break; @@ -193,12 +169,23 @@ void Scheduler::ProcessScheduledActions() { case SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED: client_->ScheduledActionActivatePendingTreeIfNeeded(); break; - case SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE: - DrawAndSwapIfPossible(); + case SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE: { + frame_rate_controller_->DidSwapBuffers(); + ScheduledActionDrawAndSwapResult result = + client_->ScheduledActionDrawAndSwapIfPossible(); + state_machine_.DidDrawIfPossibleCompleted(result.did_draw); + if (!result.did_swap) + frame_rate_controller_->DidSwapBuffersComplete(); break; - case SchedulerStateMachine::ACTION_DRAW_FORCED: - DrawAndSwapForced(); + } + case SchedulerStateMachine::ACTION_DRAW_FORCED: { + frame_rate_controller_->DidSwapBuffers(); + ScheduledActionDrawAndSwapResult result = + client_->ScheduledActionDrawAndSwapForced(); + if (!result.did_swap) + frame_rate_controller_->DidSwapBuffersComplete(); break; + } case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION: client_->ScheduledActionBeginOutputSurfaceCreation(); break; @@ -209,8 +196,10 @@ void Scheduler::ProcessScheduledActions() { action = state_machine_.NextAction(); } - SetupNextBeginFrameIfNeeded(); - client_->DidAnticipatedDrawTimeChange(AnticipatedDrawTime()); + // Activate or deactivate the frame rate controller. + frame_rate_controller_->SetActive( + state_machine_.BeginFrameNeededByImplThread()); + client_->DidAnticipatedDrawTimeChange(frame_rate_controller_->NextTickTime()); } bool Scheduler::WillDrawIfNeeded() const { diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h index d8151fa..6ecd889 100644 --- a/cc/scheduler/scheduler.h +++ b/cc/scheduler/scheduler.h @@ -11,6 +11,7 @@ #include "base/memory/scoped_ptr.h" #include "base/time.h" #include "cc/base/cc_export.h" +#include "cc/scheduler/frame_rate_controller.h" #include "cc/scheduler/scheduler_settings.h" #include "cc/scheduler/scheduler_state_machine.h" #include "cc/trees/layer_tree_host.h" @@ -32,7 +33,6 @@ struct ScheduledActionDrawAndSwapResult { class SchedulerClient { public: - virtual void SetNeedsBeginFrameOnImplThread(bool enable) = 0; virtual void ScheduledActionSendBeginFrameToMainThread() = 0; virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapIfPossible() = 0; @@ -49,12 +49,14 @@ class SchedulerClient { virtual ~SchedulerClient() {} }; -class CC_EXPORT Scheduler { +class CC_EXPORT Scheduler : FrameRateControllerClient { public: static scoped_ptr<Scheduler> Create( SchedulerClient* client, + scoped_ptr<FrameRateController> frame_rate_controller, const SchedulerSettings& scheduler_settings) { - return make_scoped_ptr(new Scheduler(client, scheduler_settings)); + return make_scoped_ptr(new Scheduler( + client, frame_rate_controller.Pass(), scheduler_settings)); } virtual ~Scheduler(); @@ -84,6 +86,12 @@ class CC_EXPORT Scheduler { void FinishCommit(); void BeginFrameAbortedByMainThread(); + void SetMaxFramesPending(int max); + int MaxFramesPending() const; + int NumFramesPendingForTesting() const; + + void DidSwapBuffersComplete(); + void DidLoseOutputSurface(); void DidCreateAndInitializeOutputSurface(); bool HasInitializedOutputSurface() const { @@ -95,32 +103,28 @@ class CC_EXPORT Scheduler { bool WillDrawIfNeeded() const; + void SetTimebaseAndInterval(base::TimeTicks timebase, + base::TimeDelta interval); + base::TimeTicks AnticipatedDrawTime(); base::TimeTicks LastBeginFrameOnImplThreadTime(); - void BeginFrame(base::TimeTicks frame_time); + // FrameRateControllerClient implementation + virtual void BeginFrame(bool throttled) OVERRIDE; std::string StateAsStringForTesting() { return state_machine_.ToString(); } private: Scheduler(SchedulerClient* client, + scoped_ptr<FrameRateController> frame_rate_controller, const SchedulerSettings& scheduler_settings); - void SetupNextBeginFrameIfNeeded(); - void DrawAndSwapIfPossible(); - void DrawAndSwapForced(); void ProcessScheduledActions(); const SchedulerSettings settings_; SchedulerClient* client_; - - base::WeakPtrFactory<Scheduler> weak_factory_; - bool last_set_needs_begin_frame_; - bool has_pending_begin_frame_; - base::TimeTicks last_begin_frame_time_; - base::TimeDelta interval_; - + scoped_ptr<FrameRateController> frame_rate_controller_; SchedulerStateMachine state_machine_; bool inside_process_scheduled_actions_; diff --git a/cc/scheduler/scheduler_settings.cc b/cc/scheduler/scheduler_settings.cc index 4850a108..29c525b 100644 --- a/cc/scheduler/scheduler_settings.cc +++ b/cc/scheduler/scheduler_settings.cc @@ -8,8 +8,7 @@ namespace cc { SchedulerSettings::SchedulerSettings() : impl_side_painting(false), - timeout_and_draw_when_animation_checkerboards(true), - using_synchronous_renderer_compositor(false) {} + timeout_and_draw_when_animation_checkerboards(true) {} SchedulerSettings::~SchedulerSettings() {} diff --git a/cc/scheduler/scheduler_settings.h b/cc/scheduler/scheduler_settings.h index c06e28f..ebcfc6a 100644 --- a/cc/scheduler/scheduler_settings.h +++ b/cc/scheduler/scheduler_settings.h @@ -16,7 +16,6 @@ class CC_EXPORT SchedulerSettings { bool impl_side_painting; bool timeout_and_draw_when_animation_checkerboards; - bool using_synchronous_renderer_compositor; }; } // namespace cc diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc index fda6e6b..76f7bb1 100644 --- a/cc/scheduler/scheduler_state_machine.cc +++ b/cc/scheduler/scheduler_state_machine.cc @@ -4,7 +4,6 @@ #include "cc/scheduler/scheduler_state_machine.h" -#include "base/format_macros.h" #include "base/logging.h" #include "base/strings/stringprintf.h" @@ -13,7 +12,6 @@ namespace cc { SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) : settings_(settings), commit_state_(COMMIT_STATE_IDLE), - commit_count_(0), current_frame_number_(0), last_frame_number_where_draw_was_called_(-1), last_frame_number_where_tree_activation_attempted_(-1), @@ -44,7 +42,6 @@ std::string SchedulerStateMachine::ToString() { "settings_.impl_side_painting = %d; ", settings_.impl_side_painting); base::StringAppendF(&str, "commit_state_ = %d; ", commit_state_); - base::StringAppendF(&str, "commit_count_ = %d; ", commit_count_); base::StringAppendF( &str, "current_frame_number_ = %d; ", current_frame_number_); base::StringAppendF(&str, @@ -83,8 +80,6 @@ std::string SchedulerStateMachine::ToString() { main_thread_needs_layer_textures_); base::StringAppendF(&str, "inside_begin_frame_ = %d; ", inside_begin_frame_); - base::StringAppendF(&str, "last_frame_time_ = %"PRId64"; ", - (last_frame_time_ - base::TimeTicks()).InMilliseconds()); base::StringAppendF(&str, "visible_ = %d; ", visible_); base::StringAppendF(&str, "can_start_ = %d; ", can_start_); base::StringAppendF(&str, "can_draw_ = %d; ", can_draw_); @@ -278,7 +273,6 @@ void SchedulerStateMachine::UpdateState(Action action) { return; case ACTION_COMMIT: - commit_count_++; if (expect_immediate_begin_frame_for_main_thread_) commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW; else @@ -338,11 +332,6 @@ void SchedulerStateMachine::SetMainThreadNeedsLayerTextures() { } bool SchedulerStateMachine::BeginFrameNeededByImplThread() const { - // We should proactively request a BeginFrame if a commit is pending. - if (needs_commit_ || needs_forced_commit_ || - commit_state_ != COMMIT_STATE_IDLE) - return true; - // If we have a pending tree, need to keep getting notifications until // the tree is ready to be swapped. if (has_pending_tree_) @@ -366,10 +355,6 @@ void SchedulerStateMachine::DidEnterBeginFrame() { inside_begin_frame_ = true; } -void SchedulerStateMachine::SetFrameTime(base::TimeTicks frame_time) { - last_frame_time_ = frame_time; -} - void SchedulerStateMachine::DidLeaveBeginFrame() { current_frame_number_++; inside_begin_frame_ = false; diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h index 59e940c..1be0749 100644 --- a/cc/scheduler/scheduler_state_machine.h +++ b/cc/scheduler/scheduler_state_machine.h @@ -8,7 +8,6 @@ #include <string> #include "base/basictypes.h" -#include "base/time.h" #include "cc/base/cc_export.h" #include "cc/scheduler/scheduler_settings.h" @@ -79,9 +78,7 @@ class CC_EXPORT SchedulerStateMachine { // The scheduler will not draw more than once in a given BeginFrame // callback. void DidEnterBeginFrame(); - void SetFrameTime(base::TimeTicks frame_time); void DidLeaveBeginFrame(); - bool inside_begin_frame() const { return inside_begin_frame_; } // Indicates whether the LayerTreeHostImpl is visible. void SetVisible(bool visible); @@ -171,7 +168,6 @@ class CC_EXPORT SchedulerStateMachine { const SchedulerSettings settings_; CommitState commit_state_; - int commit_count_; int current_frame_number_; int last_frame_number_where_draw_was_called_; @@ -188,7 +184,6 @@ class CC_EXPORT SchedulerStateMachine { bool expect_immediate_begin_frame_for_main_thread_; bool main_thread_needs_layer_textures_; bool inside_begin_frame_; - base::TimeTicks last_frame_time_; bool visible_; bool can_start_; bool can_draw_; @@ -198,7 +193,6 @@ class CC_EXPORT SchedulerStateMachine { OutputSurfaceState output_surface_state_; bool did_create_and_initialize_first_output_surface_; - private: DISALLOW_COPY_AND_ASSIGN(SchedulerStateMachine); }; diff --git a/cc/scheduler/scheduler_state_machine_unittest.cc b/cc/scheduler/scheduler_state_machine_unittest.cc index c535212..001a229 100644 --- a/cc/scheduler/scheduler_state_machine_unittest.cc +++ b/cc/scheduler/scheduler_state_machine_unittest.cc @@ -98,7 +98,7 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); EXPECT_FALSE(state.NeedsCommit()); - EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + EXPECT_FALSE(state.BeginFrameNeededByImplThread()); } } diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc index bc7d575..68aecaf 100644 --- a/cc/scheduler/scheduler_unittest.cc +++ b/cc/scheduler/scheduler_unittest.cc @@ -30,11 +30,7 @@ namespace { class FakeSchedulerClient : public SchedulerClient { public: - FakeSchedulerClient() - : needs_begin_frame_(false) { - Reset(); - } - + FakeSchedulerClient() { Reset(); } void Reset() { actions_.clear(); states_.clear(); @@ -43,12 +39,15 @@ class FakeSchedulerClient : public SchedulerClient { num_draws_ = 0; } - Scheduler* CreateScheduler(const SchedulerSettings& settings) { - scheduler_ = Scheduler::Create(this, settings); + Scheduler* CreateScheduler( + scoped_ptr<FrameRateController> frame_rate_controller, + const SchedulerSettings& settings) { + scheduler_ = + Scheduler::Create(this, frame_rate_controller.Pass(), settings); return scheduler_.get(); } - bool needs_begin_frame() { return needs_begin_frame_; } + int num_draws() const { return num_draws_; } int num_actions_() const { return static_cast<int>(actions_.size()); } const char* Action(int i) const { return actions_[i]; } @@ -69,11 +68,6 @@ class FakeSchedulerClient : public SchedulerClient { } // Scheduler Implementation. - virtual void SetNeedsBeginFrameOnImplThread(bool enable) OVERRIDE { - actions_.push_back("SetNeedsBeginFrameOnImplThread"); - states_.push_back(scheduler_->StateAsStringForTesting()); - needs_begin_frame_ = enable; - } virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE { actions_.push_back("ScheduledActionSendBeginFrameToMainThread"); states_.push_back(scheduler_->StateAsStringForTesting()); @@ -117,7 +111,6 @@ class FakeSchedulerClient : public SchedulerClient { virtual void DidAnticipatedDrawTimeChange(base::TimeTicks) OVERRIDE {} protected: - bool needs_begin_frame_; bool draw_will_happen_; bool swap_will_happen_if_draw_happens_; int num_draws_; @@ -128,8 +121,11 @@ class FakeSchedulerClient : public SchedulerClient { TEST(SchedulerTest, InitializeOutputSurfaceDoesNotBeginFrame) { FakeSchedulerClient client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -142,8 +138,11 @@ TEST(SchedulerTest, InitializeOutputSurfaceDoesNotBeginFrame) { TEST(SchedulerTest, RequestCommit) { FakeSchedulerClient client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -154,29 +153,33 @@ TEST(SchedulerTest, RequestCommit) { // SetNeedsCommit should begin the frame. scheduler->SetNeedsCommit(); - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_SINGLE_ACTION("ScheduledActionSendBeginFrameToMainThread", client); + EXPECT_FALSE(time_source->Active()); client.Reset(); // FinishCommit should commit scheduler->FinishCommit(); EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); client.Reset(); - // BeginFrame should draw. - scheduler->BeginFrame(base::TimeTicks::Now()); - EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); - EXPECT_FALSE(client.needs_begin_frame()); + // Tick should draw. + time_source->Tick(); + EXPECT_SINGLE_ACTION("ScheduledActionDrawAndSwapIfPossible", client); + EXPECT_FALSE(time_source->Active()); client.Reset(); + + // Timer should be off. + EXPECT_FALSE(time_source->Active()); } TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) { FakeSchedulerClient client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -187,8 +190,7 @@ TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) { // SetNedsCommit should begin the frame. scheduler->SetNeedsCommit(); - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + EXPECT_SINGLE_ACTION("ScheduledActionSendBeginFrameToMainThread", client); client.Reset(); // Now SetNeedsCommit again. Calling here means we need a second frame. @@ -201,85 +203,78 @@ TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) { client.Reset(); // Tick should draw but then begin another frame. - scheduler->BeginFrame(base::TimeTicks::Now()); - EXPECT_TRUE(client.needs_begin_frame()); + time_source->Tick(); + EXPECT_FALSE(time_source->Active()); EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 2); client.Reset(); - - // Go back to quiescent state and verify we no longer request BeginFrames. - scheduler->FinishCommit(); - scheduler->BeginFrame(base::TimeTicks::Now()); - EXPECT_FALSE(client.needs_begin_frame()); } TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) { FakeSchedulerClient client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); - EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client); + EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client); client.Reset(); scheduler->DidCreateAndInitializeOutputSurface(); + scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); - client.Reset(); - scheduler->BeginFrame(base::TimeTicks::Now()); - EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + time_source->Tick(); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 1); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(client.needs_begin_frame()); - + EXPECT_FALSE(time_source->Active()); client.Reset(); + scheduler->SetMainThreadNeedsLayerTextures(); EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread", client, 0, - 3); + 2); // A commit was started by SetMainThreadNeedsLayerTextures(). - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 3); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 2, 3); - - // We should request a BeginFrame in anticipation of a draw. + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 2); client.Reset(); + scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); // No draw happens since the textures are acquired by the main thread. - client.Reset(); - scheduler->BeginFrame(base::TimeTicks::Now()); - EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); + time_source->Tick(); + EXPECT_EQ(0, client.num_actions_()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); - // Commit will release the texture. - client.Reset(); scheduler->FinishCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); + EXPECT_ACTION("ScheduledActionCommit", client, 0, 1); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); + client.Reset(); // Now we can draw again after the commit happens. - client.Reset(); - scheduler->BeginFrame(base::TimeTicks::Now()); - EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + time_source->Tick(); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 1); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(client.needs_begin_frame()); + EXPECT_FALSE(time_source->Active()); client.Reset(); } TEST(SchedulerTest, TextureAcquisitionCollision) { FakeSchedulerClient client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -290,22 +285,19 @@ TEST(SchedulerTest, TextureAcquisitionCollision) { scheduler->SetNeedsCommit(); scheduler->SetMainThreadNeedsLayerTextures(); - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 3); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 3); + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread", client, - 2, - 3); + 1, + 2); client.Reset(); - // Although the compositor cannot draw because textures are locked by main - // thread, we continue requesting SetNeedsBeginFrame in anticipation of the - // unlock. - EXPECT_TRUE(client.needs_begin_frame()); + // Compositor not scheduled to draw because textures are locked by main thread + EXPECT_FALSE(time_source->Active()); // Trigger the commit scheduler->FinishCommit(); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); client.Reset(); // Between commit and draw, texture acquisition for main thread delayed, @@ -315,7 +307,7 @@ TEST(SchedulerTest, TextureAcquisitionCollision) { client.Reset(); // Once compositor draw complete, the delayed texture acquisition fires. - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 3); EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread", client, @@ -327,8 +319,11 @@ TEST(SchedulerTest, TextureAcquisitionCollision) { TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) { FakeSchedulerClient client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -384,8 +379,11 @@ class SchedulerClientThatsetNeedsDrawInsideDraw : public FakeSchedulerClient { // 2. the scheduler drawing twice inside a single tick TEST(SchedulerTest, RequestRedrawInsideDraw) { SchedulerClientThatsetNeedsDrawInsideDraw client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -393,25 +391,28 @@ TEST(SchedulerTest, RequestRedrawInsideDraw) { scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); EXPECT_EQ(0, client.num_draws()); - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(1, client.num_draws()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(2, client.num_draws()); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(client.needs_begin_frame()); + EXPECT_FALSE(time_source->Active()); } // Test that requesting redraw inside a failed draw doesn't lose the request. TEST(SchedulerTest, RequestRedrawInsideFailedDraw) { SchedulerClientThatsetNeedsDrawInsideDraw client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -421,33 +422,33 @@ TEST(SchedulerTest, RequestRedrawInsideFailedDraw) { scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); EXPECT_EQ(0, client.num_draws()); // Fail the draw. - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(1, client.num_draws()); // We have a commit pending and the draw failed, and we didn't lose the redraw // request. EXPECT_TRUE(scheduler->CommitPending()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); // Fail the draw again. - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(2, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); // Draw successfully. client.SetDrawWillHappen(true); - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(3, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_FALSE(time_source->Active()); } class SchedulerClientThatsetNeedsCommitInsideDraw : public FakeSchedulerClient { @@ -476,8 +477,11 @@ class SchedulerClientThatsetNeedsCommitInsideDraw : public FakeSchedulerClient { // happen inside a ScheduledActionDrawAndSwap TEST(SchedulerTest, RequestCommitInsideDraw) { SchedulerClientThatsetNeedsCommitInsideDraw client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -486,26 +490,28 @@ TEST(SchedulerTest, RequestCommitInsideDraw) { scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); EXPECT_EQ(0, client.num_draws()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); + EXPECT_FALSE(time_source->Active()); EXPECT_EQ(1, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); - EXPECT_TRUE(client.needs_begin_frame()); scheduler->FinishCommit(); - scheduler->BeginFrame(base::TimeTicks::Now()); - EXPECT_EQ(2, client.num_draws());; + time_source->Tick(); + EXPECT_EQ(2, client.num_draws()); + EXPECT_FALSE(time_source->Active()); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(scheduler->CommitPending()); - EXPECT_FALSE(client.needs_begin_frame()); } // Tests that when a draw fails then the pending commit should not be dropped. TEST(SchedulerTest, RequestCommitInsideFailedDraw) { SchedulerClientThatsetNeedsDrawInsideDraw client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -515,77 +521,128 @@ TEST(SchedulerTest, RequestCommitInsideFailedDraw) { scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); EXPECT_EQ(0, client.num_draws()); // Fail the draw. - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(1, client.num_draws()); // We have a commit pending and the draw failed, and we didn't lose the commit // request. EXPECT_TRUE(scheduler->CommitPending()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); // Fail the draw again. - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(2, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); // Draw successfully. client.SetDrawWillHappen(true); - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(3, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_FALSE(time_source->Active()); } TEST(SchedulerTest, NoSwapWhenDrawFails) { + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerClientThatsetNeedsCommitInsideDraw client; + scoped_ptr<FakeFrameRateController> controller( + new FakeFrameRateController(time_source)); + FakeFrameRateController* controller_ptr = controller.get(); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + controller.PassAs<FrameRateController>(), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); scheduler->DidCreateAndInitializeOutputSurface(); + EXPECT_EQ(0, controller_ptr->NumFramesPending()); + scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); EXPECT_EQ(0, client.num_draws()); // Draw successfully, this starts a new frame. - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(1, client.num_draws()); + EXPECT_EQ(1, controller_ptr->NumFramesPending()); + scheduler->DidSwapBuffersComplete(); + EXPECT_EQ(0, controller_ptr->NumFramesPending()); scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); // Fail to draw, this should not start a frame. client.SetDrawWillHappen(false); - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(2, client.num_draws()); + EXPECT_EQ(0, controller_ptr->NumFramesPending()); } TEST(SchedulerTest, NoSwapWhenSwapFailsDuringForcedCommit) { + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); FakeSchedulerClient client; + scoped_ptr<FakeFrameRateController> controller( + new FakeFrameRateController(time_source)); + FakeFrameRateController* controller_ptr = controller.get(); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + controller.PassAs<FrameRateController>(), + default_scheduler_settings); + + EXPECT_EQ(0, controller_ptr->NumFramesPending()); // Tell the client that it will fail to swap. client.SetDrawWillHappen(true); client.SetSwapWillHappenIfDrawHappens(false); // Get the compositor to do a ScheduledActionDrawAndSwapForced. - scheduler->SetCanDraw(true); scheduler->SetNeedsRedraw(); scheduler->SetNeedsForcedRedraw(); EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapForced")); + + // We should not have told the frame rate controller that we began a frame. + EXPECT_EQ(0, controller_ptr->NumFramesPending()); +} + +TEST(SchedulerTest, RecreateOutputSurfaceClearsPendingDrawCount) { + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); + FakeSchedulerClient client; + scoped_ptr<FakeFrameRateController> controller( + new FakeFrameRateController(time_source)); + FakeFrameRateController* controller_ptr = controller.get(); + SchedulerSettings default_scheduler_settings; + Scheduler* scheduler = client.CreateScheduler( + controller.PassAs<FrameRateController>(), + default_scheduler_settings); + + scheduler->SetCanStart(); + scheduler->SetVisible(true); + scheduler->SetCanDraw(true); + scheduler->DidCreateAndInitializeOutputSurface(); + + // Draw successfully, this starts a new frame. + scheduler->SetNeedsRedraw(); + time_source->Tick(); + EXPECT_EQ(1, controller_ptr->NumFramesPending()); + + scheduler->DidLoseOutputSurface(); + // Verifying that it's 1 so that we know that it's reset on recreate. + EXPECT_EQ(1, controller_ptr->NumFramesPending()); + + scheduler->DidCreateAndInitializeOutputSurface(); + EXPECT_EQ(0, controller_ptr->NumFramesPending()); } } // namespace diff --git a/cc/scheduler/vsync_time_source.cc b/cc/scheduler/vsync_time_source.cc new file mode 100644 index 0000000..206e2376 --- /dev/null +++ b/cc/scheduler/vsync_time_source.cc @@ -0,0 +1,76 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/scheduler/vsync_time_source.h" + +#include "base/logging.h" + +namespace cc { + +scoped_refptr<VSyncTimeSource> VSyncTimeSource::Create( + VSyncProvider* vsync_provider, NotificationDisableOption option) { + return make_scoped_refptr(new VSyncTimeSource(vsync_provider, option)); +} + +VSyncTimeSource::VSyncTimeSource( + VSyncProvider* vsync_provider, NotificationDisableOption option) + : active_(false), + notification_requested_(false), + vsync_provider_(vsync_provider), + client_(NULL), + disable_option_(option) {} + +VSyncTimeSource::~VSyncTimeSource() {} + +void VSyncTimeSource::SetClient(TimeSourceClient* client) { + client_ = client; +} + +void VSyncTimeSource::SetActive(bool active) { + if (active_ == active) + return; + active_ = active; + if (active_ && !notification_requested_) { + notification_requested_ = true; + vsync_provider_->RequestVSyncNotification(this); + } + if (!active_ && disable_option_ == DISABLE_SYNCHRONOUSLY) { + notification_requested_ = false; + vsync_provider_->RequestVSyncNotification(NULL); + } +} + +bool VSyncTimeSource::Active() const { + return active_; +} + +base::TimeTicks VSyncTimeSource::LastTickTime() { + return last_tick_time_; +} + +base::TimeTicks VSyncTimeSource::NextTickTime() { + return Active() ? last_tick_time_ + interval_ : base::TimeTicks(); +} + +void VSyncTimeSource::SetTimebaseAndInterval(base::TimeTicks, + base::TimeDelta interval) { + interval_ = interval; +} + +void VSyncTimeSource::DidVSync(base::TimeTicks frame_time) { + last_tick_time_ = frame_time; + if (disable_option_ == DISABLE_SYNCHRONOUSLY) { + DCHECK(active_); + } else if (!active_) { + if (notification_requested_) { + notification_requested_ = false; + vsync_provider_->RequestVSyncNotification(NULL); + } + return; + } + if (client_) + client_->OnTimerTick(); +} + +} // namespace cc diff --git a/cc/scheduler/vsync_time_source.h b/cc/scheduler/vsync_time_source.h new file mode 100644 index 0000000..4cd0441 --- /dev/null +++ b/cc/scheduler/vsync_time_source.h @@ -0,0 +1,78 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_SCHEDULER_VSYNC_TIME_SOURCE_H_ +#define CC_SCHEDULER_VSYNC_TIME_SOURCE_H_ + +#include "cc/base/cc_export.h" +#include "cc/scheduler/time_source.h" + +namespace cc { + +class CC_EXPORT VSyncClient { + public: + virtual void DidVSync(base::TimeTicks frame_time) = 0; + + protected: + virtual ~VSyncClient() {} +}; + +class VSyncProvider { + public: + // Request to be notified of future vsync events. The notifications will be + // delivered until they are disabled by calling this function with a null + // client. + virtual void RequestVSyncNotification(VSyncClient* client) = 0; + + protected: + virtual ~VSyncProvider() {} +}; + +// This timer implements a time source that is explicitly triggered by an +// external vsync signal. +class CC_EXPORT VSyncTimeSource : public TimeSource, public VSyncClient { + public: + enum NotificationDisableOption { + // The notification will be lazily disabled in the callback to ensure + // we get notified of the frame immediately following a quick on-off-on + // transition. + DISABLE_ON_NEXT_TICK, + DISABLE_SYNCHRONOUSLY + }; + + static scoped_refptr<VSyncTimeSource> Create( + VSyncProvider* vsync_provider, NotificationDisableOption option); + + // TimeSource implementation + virtual void SetClient(TimeSourceClient* client) OVERRIDE; + virtual void SetTimebaseAndInterval(base::TimeTicks timebase, + base::TimeDelta interval) OVERRIDE; + virtual void SetActive(bool active) OVERRIDE; + virtual bool Active() const OVERRIDE; + virtual base::TimeTicks LastTickTime() OVERRIDE; + virtual base::TimeTicks NextTickTime() OVERRIDE; + + // VSyncClient implementation + virtual void DidVSync(base::TimeTicks frame_time) OVERRIDE; + + protected: + explicit VSyncTimeSource(VSyncProvider* vsync_provider, + NotificationDisableOption option); + virtual ~VSyncTimeSource(); + + base::TimeTicks last_tick_time_; + base::TimeDelta interval_; + bool active_; + bool notification_requested_; + + VSyncProvider* vsync_provider_; + TimeSourceClient* client_; + NotificationDisableOption disable_option_; + + DISALLOW_COPY_AND_ASSIGN(VSyncTimeSource); +}; + +} // namespace cc + +#endif // CC_SCHEDULER_VSYNC_TIME_SOURCE_H_ diff --git a/cc/scheduler/vsync_time_source_unittest.cc b/cc/scheduler/vsync_time_source_unittest.cc new file mode 100644 index 0000000..4834630 --- /dev/null +++ b/cc/scheduler/vsync_time_source_unittest.cc @@ -0,0 +1,124 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/scheduler/vsync_time_source.h" + +#include "cc/test/scheduler_test_common.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace { + +class FakeVSyncProvider : public VSyncProvider { + public: + FakeVSyncProvider() : client_(NULL) {} + + // VSyncProvider implementation. + virtual void RequestVSyncNotification(VSyncClient* client) OVERRIDE { + client_ = client; + } + + bool IsVSyncNotificationEnabled() const { return client_ != NULL; } + + void Trigger(base::TimeTicks frame_time) { + if (client_) + client_->DidVSync(frame_time); + } + + private: + VSyncClient* client_; +}; + +class VSyncTimeSourceTest : public testing::Test { + public: + VSyncTimeSourceTest() + : timer_(VSyncTimeSource::Create( + &provider_, VSyncTimeSource::DISABLE_ON_NEXT_TICK)) { + timer_->SetClient(&client_); + } + + protected: + FakeTimeSourceClient client_; + FakeVSyncProvider provider_; + scoped_refptr<VSyncTimeSource> timer_; +}; + +TEST_F(VSyncTimeSourceTest, TaskPostedAndTickCalled) { + EXPECT_FALSE(provider_.IsVSyncNotificationEnabled()); + + timer_->SetActive(true); + EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); + + base::TimeTicks frame_time = base::TimeTicks::Now(); + provider_.Trigger(frame_time); + EXPECT_TRUE(client_.TickCalled()); + EXPECT_EQ(timer_->LastTickTime(), frame_time); +} + +TEST_F(VSyncTimeSourceTest, NotificationDisabledLazily) { + base::TimeTicks frame_time = base::TimeTicks::Now(); + + // Enable timer and trigger sync once. + timer_->SetActive(true); + EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); + provider_.Trigger(frame_time); + EXPECT_TRUE(client_.TickCalled()); + + // Disabling the timer should not disable vsync notification immediately. + client_.Reset(); + timer_->SetActive(false); + EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); + + // At the next vsync the notification is disabled, but the timer isn't ticked. + provider_.Trigger(frame_time); + EXPECT_FALSE(provider_.IsVSyncNotificationEnabled()); + EXPECT_FALSE(client_.TickCalled()); + + // The notification should not be disabled multiple times. + provider_.RequestVSyncNotification(timer_.get()); + provider_.Trigger(frame_time); + EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); + EXPECT_FALSE(client_.TickCalled()); +} + +TEST_F(VSyncTimeSourceTest, NotificationDisabledImmediatelyForSetting) { + timer_ = VSyncTimeSource::Create(&provider_, + VSyncTimeSource::DISABLE_SYNCHRONOUSLY); + timer_->SetClient(&client_); + base::TimeTicks frame_time = base::TimeTicks::Now(); + + // Enable timer and trigger sync once. + timer_->SetActive(true); + EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); + provider_.Trigger(frame_time); + EXPECT_TRUE(client_.TickCalled()); + + // Disable timer should disable vsync notification immediately. + timer_->SetActive(false); + EXPECT_FALSE(provider_.IsVSyncNotificationEnabled()); + + // Enable again and timer can be ticked again. + client_.Reset(); + timer_->SetActive(true); + EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); + provider_.Trigger(frame_time); + EXPECT_TRUE(client_.TickCalled()); +} + +TEST_F(VSyncTimeSourceTest, ValidNextTickTime) { + base::TimeTicks frame_time = base::TimeTicks::Now(); + base::TimeDelta interval = base::TimeDelta::FromSeconds(1); + + ASSERT_EQ(timer_->NextTickTime(), base::TimeTicks()); + + timer_->SetActive(true); + provider_.Trigger(frame_time); + ASSERT_EQ(timer_->NextTickTime(), frame_time); + + timer_->SetTimebaseAndInterval(frame_time, interval); + ASSERT_EQ(timer_->NextTickTime(), frame_time + interval); +} + +} // namespace +} // namespace cc |