diff options
Diffstat (limited to 'cc')
37 files changed, 692 insertions, 805 deletions
@@ -321,8 +321,6 @@ 'scheduler/texture_uploader.cc', 'scheduler/texture_uploader.h', 'scheduler/time_source.h', - 'scheduler/vsync_time_source.cc', - 'scheduler/vsync_time_source.h', 'trees/damage_tracker.cc', 'trees/damage_tracker.h', 'trees/layer_sorter.cc', diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp index 07cecc0..f214015 100644 --- a/cc/cc_tests.gyp +++ b/cc/cc_tests.gyp @@ -68,7 +68,6 @@ 'scheduler/scheduler_state_machine_unittest.cc', 'scheduler/scheduler_unittest.cc', 'scheduler/texture_uploader_unittest.cc', - 'scheduler/vsync_time_source_unittest.cc', 'test/fake_web_graphics_context_3d_unittest.cc', 'trees/damage_tracker_unittest.cc', 'trees/layer_sorter_unittest.cc', diff --git a/cc/output/output_surface.cc b/cc/output/output_surface.cc index 0889b96..6efa083 100644 --- a/cc/output/output_surface.cc +++ b/cc/output/output_surface.cc @@ -9,12 +9,14 @@ #include <vector> #include "base/bind.h" +#include "base/debug/trace_event.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "cc/output/compositor_frame.h" #include "cc/output/output_surface_client.h" +#include "cc/scheduler/delay_based_time_source.h" #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/khronos/GLES2/gl2ext.h" @@ -32,7 +34,7 @@ class OutputSurfaceCallbacks WebGraphicsSwapBuffersCompleteCallbackCHROMIUM, public WebKit::WebGraphicsContext3D::WebGraphicsContextLostCallback { public: - explicit OutputSurfaceCallbacks(OutputSurfaceClient* client) + explicit OutputSurfaceCallbacks(OutputSurface* client) : client_(client) { DCHECK(client_); } @@ -44,50 +46,155 @@ class OutputSurfaceCallbacks virtual void onContextLost() { client_->DidLoseOutputSurface(); } private: - OutputSurfaceClient* client_; + OutputSurface* client_; }; OutputSurface::OutputSurface( scoped_ptr<WebKit::WebGraphicsContext3D> context3d) - : client_(NULL), - context3d_(context3d.Pass()), + : context3d_(context3d.Pass()), has_gl_discard_backbuffer_(false), has_swap_buffers_complete_callback_(false), device_scale_factor_(-1), - weak_ptr_factory_(this) { + weak_ptr_factory_(this), + max_frames_pending_(0), + pending_swap_buffers_(0), + begin_frame_pending_(false), + client_(NULL) { } OutputSurface::OutputSurface( scoped_ptr<cc::SoftwareOutputDevice> software_device) - : client_(NULL), - software_device_(software_device.Pass()), + : software_device_(software_device.Pass()), has_gl_discard_backbuffer_(false), has_swap_buffers_complete_callback_(false), device_scale_factor_(-1), - weak_ptr_factory_(this) { + weak_ptr_factory_(this), + max_frames_pending_(0), + pending_swap_buffers_(0), + begin_frame_pending_(false), + client_(NULL) { } OutputSurface::OutputSurface( scoped_ptr<WebKit::WebGraphicsContext3D> context3d, scoped_ptr<cc::SoftwareOutputDevice> software_device) - : client_(NULL), - context3d_(context3d.Pass()), + : context3d_(context3d.Pass()), software_device_(software_device.Pass()), has_gl_discard_backbuffer_(false), has_swap_buffers_complete_callback_(false), device_scale_factor_(-1), - weak_ptr_factory_(this) { + weak_ptr_factory_(this), + max_frames_pending_(0), + pending_swap_buffers_(0), + begin_frame_pending_(false), + client_(NULL) { +} + +void OutputSurface::InitializeBeginFrameEmulation( + Thread* thread, + bool throttle_frame_production, + base::TimeDelta interval) { + if (throttle_frame_production){ + frame_rate_controller_.reset( + new FrameRateController( + DelayBasedTimeSource::Create(interval, thread))); + } else { + frame_rate_controller_.reset(new FrameRateController(thread)); + } + + frame_rate_controller_->SetClient(this); + frame_rate_controller_->SetMaxSwapsPending(max_frames_pending_); + + // The new frame rate controller will consume the swap acks of the old + // frame rate controller, so we set that expectation up here. + for (int i = 0; i < pending_swap_buffers_; i++) + frame_rate_controller_->DidSwapBuffers(); +} + +void OutputSurface::SetMaxFramesPending(int max_frames_pending) { + if (frame_rate_controller_) + frame_rate_controller_->SetMaxSwapsPending(max_frames_pending); + max_frames_pending_ = max_frames_pending; +} + +void OutputSurface::OnVSyncParametersChanged(base::TimeTicks timebase, + base::TimeDelta interval) { + TRACE_EVENT2("cc", "OutputSurface::OnVSyncParametersChanged", + "timebase", (timebase - base::TimeTicks()).InSecondsF(), + "interval", interval.InSecondsF()); + if (frame_rate_controller_) + frame_rate_controller_->SetTimebaseAndInterval(timebase, interval); +} + +void OutputSurface::FrameRateControllerTick(bool throttled) { + DCHECK(frame_rate_controller_); + if (!throttled) + BeginFrame(frame_rate_controller_->LastTickTime()); +} + +// Forwarded to OutputSurfaceClient +void OutputSurface::SetNeedsRedrawRect(gfx::Rect damage_rect) { + TRACE_EVENT0("cc", "OutputSurface::SetNeedsRedrawRect"); + client_->SetNeedsRedrawRect(damage_rect); +} + +void OutputSurface::SetNeedsBeginFrame(bool enable) { + TRACE_EVENT1("cc", "OutputSurface::SetNeedsBeginFrame", "enable", enable); + begin_frame_pending_ = false; + if (frame_rate_controller_) + frame_rate_controller_->SetActive(enable); +} + +void OutputSurface::BeginFrame(base::TimeTicks frame_time) { + if (begin_frame_pending_ || + (pending_swap_buffers_ >= max_frames_pending_ && max_frames_pending_ > 0)) + return; + TRACE_EVENT1("cc", "OutputSurface::BeginFrame", + "pending_swap_buffers_", pending_swap_buffers_); + begin_frame_pending_ = true; + client_->BeginFrame(frame_time); +} + +void OutputSurface::DidSwapBuffers() { + begin_frame_pending_ = false; + pending_swap_buffers_++; + TRACE_EVENT1("cc", "OutputSurface::DidSwapBuffers", + "pending_swap_buffers_", pending_swap_buffers_); + if (frame_rate_controller_) + frame_rate_controller_->DidSwapBuffers(); +} + +void OutputSurface::OnSwapBuffersComplete(const CompositorFrameAck* ack) { + pending_swap_buffers_--; + TRACE_EVENT1("cc", "OutputSurface::OnSwapBuffersComplete", + "pending_swap_buffers_", pending_swap_buffers_); + client_->OnSwapBuffersComplete(ack); + if (frame_rate_controller_) + frame_rate_controller_->DidSwapBuffersComplete(); +} + +void OutputSurface::DidLoseOutputSurface() { + TRACE_EVENT0("cc", "OutputSurface::DidLoseOutputSurface"); + begin_frame_pending_ = false; + pending_swap_buffers_ = 0; + client_->DidLoseOutputSurface(); +} + +void OutputSurface::SetExternalDrawConstraints(const gfx::Transform& transform, + gfx::Rect viewport) { + client_->SetExternalDrawConstraints(transform, viewport); } OutputSurface::~OutputSurface() { + if (frame_rate_controller_) + frame_rate_controller_->SetActive(false); } bool OutputSurface::ForcedDrawToSoftwareDevice() const { return false; } -bool OutputSurface::BindToClient( - cc::OutputSurfaceClient* client) { +bool OutputSurface::BindToClient(cc::OutputSurfaceClient* client) { DCHECK(client); client_ = client; bool success = true; @@ -143,7 +250,7 @@ void OutputSurface::SetContext3D( context3d_ = context3d.Pass(); - callbacks_.reset(new OutputSurfaceCallbacks(client_)); + callbacks_.reset(new OutputSurfaceCallbacks(this)); context3d_->setSwapBuffersCompleteCallbackCHROMIUM(callbacks_.get()); context3d_->setContextLostCallback(callbacks_.get()); } @@ -206,20 +313,16 @@ void OutputSurface::SwapBuffers(cc::CompositorFrame* frame) { if (!has_swap_buffers_complete_callback_) PostSwapBuffersComplete(); + + DidSwapBuffers(); } void OutputSurface::PostSwapBuffersComplete() { base::MessageLoop::current()->PostTask( FROM_HERE, - base::Bind(&OutputSurface::SwapBuffersComplete, - weak_ptr_factory_.GetWeakPtr())); -} - -void OutputSurface::SwapBuffersComplete() { - if (!client_) - return; - - client_->OnSwapBuffersComplete(NULL); + base::Bind(&OutputSurface::OnSwapBuffersComplete, + weak_ptr_factory_.GetWeakPtr(), + static_cast<CompositorFrameAck*>(NULL))); } } // namespace cc diff --git a/cc/output/output_surface.h b/cc/output/output_surface.h index d263d3a..8f782d7 100644 --- a/cc/output/output_surface.h +++ b/cc/output/output_surface.h @@ -12,6 +12,7 @@ #include "cc/base/cc_export.h" #include "cc/output/context_provider.h" #include "cc/output/software_output_device.h" +#include "cc/scheduler/frame_rate_controller.h" #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" namespace ui { struct LatencyInfo; } @@ -19,13 +20,16 @@ namespace ui { struct LatencyInfo; } namespace gfx { class Rect; class Size; +class Transform; } namespace cc { class CompositorFrame; +class CompositorFrameAck; class OutputSurfaceClient; class OutputSurfaceCallbacks; +class Thread; // Represents the output surface for a compositor. The compositor owns // and manages its destruction. Its lifetime is: @@ -34,7 +38,7 @@ class OutputSurfaceCallbacks; // From here on, it will only be used on the compositor thread. // 3. If the 3D context is lost, then the compositor will delete the output // surface (on the compositor thread) and go back to step 1. -class CC_EXPORT OutputSurface { +class CC_EXPORT OutputSurface : public FrameRateControllerClient { public: explicit OutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D> context3d); @@ -83,6 +87,13 @@ class CC_EXPORT OutputSurface { // thread. virtual bool BindToClient(OutputSurfaceClient* client); + void InitializeBeginFrameEmulation( + Thread* thread, + bool throttle_frame_production, + base::TimeDelta interval); + + void SetMaxFramesPending(int max_frames_pending); + virtual void EnsureBackbuffer(); virtual void DiscardBackbuffer(); @@ -103,7 +114,7 @@ class CC_EXPORT OutputSurface { // Requests a BeginFrame notification from the output surface. The // notification will be delivered by calling // OutputSurfaceClient::BeginFrame until the callback is disabled. - virtual void SetNeedsBeginFrame(bool enable) {} + virtual void SetNeedsBeginFrame(bool enable); protected: // Synchronously initialize context3d and enter hardware mode. @@ -116,7 +127,6 @@ class CC_EXPORT OutputSurface { void PostSwapBuffersComplete(); - OutputSurfaceClient* client_; struct cc::OutputSurface::Capabilities capabilities_; scoped_ptr<OutputSurfaceCallbacks> callbacks_; scoped_ptr<WebKit::WebGraphicsContext3D> context3d_; @@ -125,12 +135,34 @@ class CC_EXPORT OutputSurface { bool has_swap_buffers_complete_callback_; gfx::Size surface_size_; float device_scale_factor_; + base::WeakPtrFactory<OutputSurface> weak_ptr_factory_; + + // The FrameRateController is deprecated. + // Platforms should move to native BeginFrames instead. + void OnVSyncParametersChanged(base::TimeTicks timebase, + base::TimeDelta interval); + virtual void FrameRateControllerTick(bool throttled) OVERRIDE; + scoped_ptr<FrameRateController> frame_rate_controller_; + int max_frames_pending_; + int pending_swap_buffers_; + bool begin_frame_pending_; + + // Forwarded to OutputSurfaceClient but threaded through OutputSurface + // first so OutputSurface has a chance to update the FrameRateController + bool HasClient() { return !!client_; } + void SetNeedsRedrawRect(gfx::Rect damage_rect); + void BeginFrame(base::TimeTicks frame_time); + void DidSwapBuffers(); + void OnSwapBuffersComplete(const CompositorFrameAck* ack); + void DidLoseOutputSurface(); + void SetExternalDrawConstraints(const gfx::Transform& transform, + gfx::Rect viewport); private: - void SetContext3D(scoped_ptr<WebKit::WebGraphicsContext3D> context3d); - void SwapBuffersComplete(); + OutputSurfaceClient* client_; + friend class OutputSurfaceCallbacks; - base::WeakPtrFactory<OutputSurface> weak_ptr_factory_; + void SetContext3D(scoped_ptr<WebKit::WebGraphicsContext3D> context3d); DISALLOW_COPY_AND_ASSIGN(OutputSurface); }; diff --git a/cc/output/output_surface_client.h b/cc/output/output_surface_client.h index f5999c3..d727629 100644 --- a/cc/output/output_surface_client.h +++ b/cc/output/output_surface_client.h @@ -27,8 +27,6 @@ class CC_EXPORT OutputSurfaceClient { virtual bool DeferredInitialize( scoped_refptr<ContextProvider> offscreen_context_provider) = 0; virtual void SetNeedsRedrawRect(gfx::Rect damage_rect) = 0; - virtual void OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) = 0; virtual void BeginFrame(base::TimeTicks frame_time) = 0; virtual void OnSwapBuffersComplete(const CompositorFrameAck* ack) = 0; virtual void DidLoseOutputSurface() = 0; diff --git a/cc/output/output_surface_unittest.cc b/cc/output/output_surface_unittest.cc index ebc018f..7dcb1cc 100644 --- a/cc/output/output_surface_unittest.cc +++ b/cc/output/output_surface_unittest.cc @@ -5,6 +5,7 @@ #include "cc/output/output_surface.h" #include "cc/output/output_surface_client.h" #include "cc/output/software_output_device.h" +#include "cc/test/scheduler_test_common.h" #include "cc/test/test_web_graphics_context_3d.h" #include "gpu/GLES2/gl2extchromium.h" #include "testing/gtest/include/gtest/gtest.h" @@ -25,19 +26,43 @@ class TestOutputSurface : public OutputSurface { scoped_ptr<cc::SoftwareOutputDevice> software_device) : OutputSurface(context3d.Pass(), software_device.Pass()) {} - OutputSurfaceClient* client() { return client_; } - bool InitializeNewContext3D( scoped_ptr<WebKit::WebGraphicsContext3D> new_context3d) { return InitializeAndSetContext3D(new_context3d.Pass(), scoped_refptr<ContextProvider>()); } + + bool HasClientForTesting() { + return HasClient(); + } + + void OnVSyncParametersChangedForTesting(base::TimeTicks timebase, + base::TimeDelta interval) { + OnVSyncParametersChanged(timebase, interval); + } + + void BeginFrameForTesting(base::TimeTicks frame_time) { + BeginFrame(frame_time); + } + + void DidSwapBuffersForTesting() { + DidSwapBuffers(); + } + + int pending_swap_buffers() { + return pending_swap_buffers_; + } + + void OnSwapBuffersCompleteForTesting() { + OnSwapBuffersComplete(NULL); + } }; class FakeOutputSurfaceClient : public OutputSurfaceClient { public: FakeOutputSurfaceClient() - : deferred_initialize_result_(true), + : begin_frame_count_(0), + deferred_initialize_result_(true), deferred_initialize_called_(false), did_lose_output_surface_called_(false) {} @@ -47,9 +72,9 @@ class FakeOutputSurfaceClient : public OutputSurfaceClient { return deferred_initialize_result_; } virtual void SetNeedsRedrawRect(gfx::Rect damage_rect) OVERRIDE {} - virtual void OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) OVERRIDE {} - virtual void BeginFrame(base::TimeTicks frame_time) OVERRIDE {} + virtual void BeginFrame(base::TimeTicks frame_time) OVERRIDE { + begin_frame_count_++; + } virtual void OnSwapBuffersComplete(const CompositorFrameAck* ack) OVERRIDE {} virtual void DidLoseOutputSurface() OVERRIDE { did_lose_output_surface_called_ = true; @@ -57,6 +82,10 @@ class FakeOutputSurfaceClient : public OutputSurfaceClient { virtual void SetExternalDrawConstraints(const gfx::Transform& transform, gfx::Rect viewport) OVERRIDE {} + int begin_frame_count() { + return begin_frame_count_; + } + void set_deferred_initialize_result(bool result) { deferred_initialize_result_ = result; } @@ -70,6 +99,7 @@ class FakeOutputSurfaceClient : public OutputSurfaceClient { } private: + int begin_frame_count_; bool deferred_initialize_result_; bool deferred_initialize_called_; bool did_lose_output_surface_called_; @@ -81,11 +111,11 @@ TEST(OutputSurfaceTest, ClientPointerIndicatesBindToClientSuccess) { TestOutputSurface output_surface( context3d.PassAs<WebKit::WebGraphicsContext3D>()); - EXPECT_EQ(NULL, output_surface.client()); + EXPECT_FALSE(output_surface.HasClientForTesting()); FakeOutputSurfaceClient client; EXPECT_TRUE(output_surface.BindToClient(&client)); - EXPECT_EQ(&client, output_surface.client()); + EXPECT_TRUE(output_surface.HasClientForTesting()); EXPECT_FALSE(client.deferred_initialize_called()); // Verify DidLoseOutputSurface callback is hooked up correctly. @@ -104,11 +134,11 @@ TEST(OutputSurfaceTest, ClientPointerIndicatesBindToClientFailure) { TestOutputSurface output_surface( context3d.PassAs<WebKit::WebGraphicsContext3D>()); - EXPECT_EQ(NULL, output_surface.client()); + EXPECT_FALSE(output_surface.HasClientForTesting()); FakeOutputSurfaceClient client; EXPECT_FALSE(output_surface.BindToClient(&client)); - EXPECT_EQ(NULL, output_surface.client()); + EXPECT_FALSE(output_surface.HasClientForTesting()); } class InitializeNewContext3D : public ::testing::Test { @@ -121,13 +151,13 @@ class InitializeNewContext3D : public ::testing::Test { protected: void BindOutputSurface() { EXPECT_TRUE(output_surface_.BindToClient(&client_)); - EXPECT_EQ(&client_, output_surface_.client()); + EXPECT_TRUE(output_surface_.HasClientForTesting()); } void InitializeNewContextExpectFail() { EXPECT_FALSE(output_surface_.InitializeNewContext3D( context3d_.PassAs<WebKit::WebGraphicsContext3D>())); - EXPECT_EQ(&client_, output_surface_.client()); + EXPECT_TRUE(output_surface_.HasClientForTesting()); EXPECT_FALSE(output_surface_.context3d()); EXPECT_TRUE(output_surface_.software_device()); @@ -164,5 +194,110 @@ TEST_F(InitializeNewContext3D, ClientDeferredInitializeFails) { InitializeNewContextExpectFail(); } +TEST(OutputSurfaceTest, BeginFrameEmulation) { + scoped_ptr<TestWebGraphicsContext3D> context3d = + TestWebGraphicsContext3D::Create(); + + TestOutputSurface output_surface( + context3d.PassAs<WebKit::WebGraphicsContext3D>()); + EXPECT_FALSE(output_surface.HasClientForTesting()); + + FakeOutputSurfaceClient client; + EXPECT_TRUE(output_surface.BindToClient(&client)); + EXPECT_TRUE(output_surface.HasClientForTesting()); + EXPECT_FALSE(client.deferred_initialize_called()); + + // Initialize BeginFrame emulation + FakeThread impl_thread; + bool throttle_frame_production = true; + const base::TimeDelta display_refresh_interval = + base::TimeDelta::FromMicroseconds(16666); + + output_surface.InitializeBeginFrameEmulation( + &impl_thread, + throttle_frame_production, + display_refresh_interval); + + output_surface.SetMaxFramesPending(2); + + // We should start off with 0 BeginFrames + EXPECT_EQ(client.begin_frame_count(), 0); + EXPECT_EQ(output_surface.pending_swap_buffers(), 0); + + // We should not have a pending task until a BeginFrame has been requested. + EXPECT_FALSE(impl_thread.HasPendingTask()); + output_surface.SetNeedsBeginFrame(true); + EXPECT_TRUE(impl_thread.HasPendingTask()); + + // BeginFrame should be called on the first tick. + impl_thread.RunPendingTask(); + EXPECT_EQ(client.begin_frame_count(), 1); + EXPECT_EQ(output_surface.pending_swap_buffers(), 0); + + // BeginFrame should not be called when there is a pending BeginFrame. + impl_thread.RunPendingTask(); + EXPECT_EQ(client.begin_frame_count(), 1); + EXPECT_EQ(output_surface.pending_swap_buffers(), 0); + + // DidSwapBuffers should clear the pending BeginFrame. + output_surface.DidSwapBuffersForTesting(); + EXPECT_EQ(client.begin_frame_count(), 1); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + impl_thread.RunPendingTask(); + EXPECT_EQ(client.begin_frame_count(), 2); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + + // BeginFrame should be throttled by pending swap buffers. + output_surface.DidSwapBuffersForTesting(); + EXPECT_EQ(client.begin_frame_count(), 2); + EXPECT_EQ(output_surface.pending_swap_buffers(), 2); + impl_thread.RunPendingTask(); + EXPECT_EQ(client.begin_frame_count(), 2); + EXPECT_EQ(output_surface.pending_swap_buffers(), 2); + + // SwapAck should decrement pending swap buffers and unblock BeginFrame again. + output_surface.OnSwapBuffersCompleteForTesting(); + EXPECT_EQ(client.begin_frame_count(), 2); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + impl_thread.RunPendingTask(); + EXPECT_EQ(client.begin_frame_count(), 3); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + + // Calling SetNeedsBeginFrame again indicates a swap did not occur but + // the client still wants another BeginFrame. + output_surface.SetNeedsBeginFrame(true); + impl_thread.RunPendingTask(); + EXPECT_EQ(client.begin_frame_count(), 4); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + + // Disabling SetNeedsBeginFrame should prevent further BeginFrames. + output_surface.SetNeedsBeginFrame(false); + impl_thread.RunPendingTask(); + EXPECT_FALSE(impl_thread.HasPendingTask()); + EXPECT_EQ(client.begin_frame_count(), 4); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + + // Optimistically injected BeginFrames without a SetNeedsBeginFrame should be + // allowed. + output_surface.BeginFrameForTesting(base::TimeTicks::Now()); + EXPECT_EQ(client.begin_frame_count(), 5); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + + // Optimistically injected BeginFrames without a SetNeedsBeginFrame should + // still be throttled by pending begin frames however. + output_surface.BeginFrameForTesting(base::TimeTicks::Now()); + EXPECT_EQ(client.begin_frame_count(), 5); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + + // Optimistically injected BeginFrames without a SetNeedsBeginFrame should + // also be throttled by pending swap buffers. + output_surface.DidSwapBuffersForTesting(); + EXPECT_EQ(client.begin_frame_count(), 5); + EXPECT_EQ(output_surface.pending_swap_buffers(), 2); + output_surface.BeginFrameForTesting(base::TimeTicks::Now()); + EXPECT_EQ(client.begin_frame_count(), 5); + EXPECT_EQ(output_surface.pending_swap_buffers(), 2); +} + } // namespace } // namespace cc diff --git a/cc/scheduler/frame_rate_controller.cc b/cc/scheduler/frame_rate_controller.cc index b4f1272..90cf0d7 100644 --- a/cc/scheduler/frame_rate_controller.cc +++ b/cc/scheduler/frame_rate_controller.cc @@ -21,7 +21,9 @@ 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( @@ -34,7 +36,7 @@ class FrameRateControllerTimeSourceAdapter : public TimeSourceClient { FrameRateController::FrameRateController(scoped_refptr<TimeSource> timer) : client_(NULL), num_frames_pending_(0), - max_frames_pending_(0), + max_swaps_pending_(0), time_source_(timer), active_(false), is_time_source_throttling_(true), @@ -48,7 +50,7 @@ FrameRateController::FrameRateController(scoped_refptr<TimeSource> timer) FrameRateController::FrameRateController(Thread* thread) : client_(NULL), num_frames_pending_(0), - max_frames_pending_(0), + max_swaps_pending_(0), active_(false), is_time_source_throttling_(false), weak_factory_(this), @@ -75,9 +77,9 @@ void FrameRateController::SetActive(bool active) { } } -void FrameRateController::SetMaxFramesPending(int max_frames_pending) { - DCHECK_GE(max_frames_pending, 0); - max_frames_pending_ = max_frames_pending; +void FrameRateController::SetMaxSwapsPending(int max_swaps_pending) { + DCHECK_GE(max_swaps_pending, 0); + max_swaps_pending_ = max_swaps_pending; } void FrameRateController::SetTimebaseAndInterval(base::TimeTicks timebase, @@ -87,15 +89,17 @@ 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_frames_pending_ && num_frames_pending_ >= max_frames_pending_; + max_swaps_pending_ && num_frames_pending_ >= max_swaps_pending_; TRACE_COUNTER_ID1("cc", "ThrottledCompositor", thread_, throttled); - if (client_) - client_->BeginFrame(throttled); + if (client_) { + client_->FrameRateControllerTick(throttled); + } if (!is_time_source_throttling_ && !throttled) PostManualTick(); @@ -108,7 +112,9 @@ 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 070c26a..70964e2 100644 --- a/cc/scheduler/frame_rate_controller.h +++ b/cc/scheduler/frame_rate_controller.h @@ -15,18 +15,21 @@ namespace cc { class Thread; class TimeSource; +class FrameRateController; class CC_EXPORT FrameRateControllerClient { - public: - // Throttled is true when we have a maximum number of frames pending. - virtual void BeginFrame(bool throttled) = 0; - protected: virtual ~FrameRateControllerClient() {} + + public: + // Throttled is true when we have a maximum number of frames pending. + virtual void FrameRateControllerTick(bool throttled) = 0; }; 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 { @@ -41,6 +44,7 @@ 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. // @@ -51,9 +55,9 @@ class CC_EXPORT FrameRateController { void DidSwapBuffers(); void DidSwapBuffersComplete(); void DidAbortAllPendingFrames(); - void SetMaxFramesPending(int max_frames_pending); // 0 for unlimited. - int MaxFramesPending() const { return max_frames_pending_; } - int NumFramesPendingForTesting() const { return num_frames_pending_; } + void SetMaxSwapsPending(int max_swaps_pending); // 0 for unlimited. + int MaxSwapsPending() const { return max_swaps_pending_; } + int NumSwapsPendingForTesting() const { return num_frames_pending_; } // This returns null for unthrottled frame-rate. base::TimeTicks NextTickTime(); @@ -73,7 +77,7 @@ class CC_EXPORT FrameRateController { FrameRateControllerClient* client_; int num_frames_pending_; - int max_frames_pending_; + int max_swaps_pending_; scoped_refptr<TimeSource> time_source_; scoped_ptr<FrameRateControllerTimeSourceAdapter> time_source_client_adapter_; bool active_; @@ -83,6 +87,7 @@ 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 183f576..3fcf4a7 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 BeginFrame(bool throttled) OVERRIDE { + virtual void FrameRateControllerTick(bool throttled) OVERRIDE { began_frame_ = !throttled; } @@ -74,7 +74,7 @@ TEST(FrameRateControllerTest, TestFrameThrottling_TwoFramesInFlight) { controller.SetClient(&client); controller.SetActive(true); - controller.SetMaxFramesPending(2); + controller.SetMaxSwapsPending(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.SetMaxFramesPending(2); + controller.SetMaxSwapsPending(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 d308b0d..7110bff 100644 --- a/cc/scheduler/scheduler.cc +++ b/cc/scheduler/scheduler.cc @@ -7,23 +7,29 @@ #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), - frame_rate_controller_(frame_rate_controller.Pass()), + 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)), state_machine_(scheduler_settings), inside_process_scheduled_actions_(false) { DCHECK(client_); - frame_rate_controller_->SetClient(this); DCHECK(!state_machine_.BeginFrameNeededByImplThread()); } -Scheduler::~Scheduler() { frame_rate_controller_->SetActive(false); } +Scheduler::~Scheduler() { + client_->SetNeedsBeginFrameOnImplThread(false); +} void Scheduler::SetCanStart() { state_machine_.SetCanStart(); @@ -88,23 +94,6 @@ 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(); @@ -113,31 +102,69 @@ 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() { - return frame_rate_controller_->NextTickTime(); + 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); } base::TimeTicks Scheduler::LastBeginFrameOnImplThreadTime() { - return frame_rate_controller_->LastTickTime(); + 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); + } } -void Scheduler::BeginFrame(bool throttled) { - TRACE_EVENT1("cc", "Scheduler::BeginFrame", "throttled", throttled); - if (!throttled) - state_machine_.DidEnterBeginFrame(); +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); ProcessScheduledActions(); - if (!throttled) - state_machine_.DidLeaveBeginFrame(); + 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; } void Scheduler::ProcessScheduledActions() { @@ -151,9 +178,6 @@ 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; @@ -169,23 +193,12 @@ void Scheduler::ProcessScheduledActions() { case SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED: client_->ScheduledActionActivatePendingTreeIfNeeded(); break; - 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(); + case SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE: + DrawAndSwapIfPossible(); break; - } - case SchedulerStateMachine::ACTION_DRAW_FORCED: { - frame_rate_controller_->DidSwapBuffers(); - ScheduledActionDrawAndSwapResult result = - client_->ScheduledActionDrawAndSwapForced(); - if (!result.did_swap) - frame_rate_controller_->DidSwapBuffersComplete(); + case SchedulerStateMachine::ACTION_DRAW_FORCED: + DrawAndSwapForced(); break; - } case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION: client_->ScheduledActionBeginOutputSurfaceCreation(); break; @@ -196,10 +209,8 @@ void Scheduler::ProcessScheduledActions() { action = state_machine_.NextAction(); } - // Activate or deactivate the frame rate controller. - frame_rate_controller_->SetActive( - state_machine_.BeginFrameNeededByImplThread()); - client_->DidAnticipatedDrawTimeChange(frame_rate_controller_->NextTickTime()); + SetupNextBeginFrameIfNeeded(); + client_->DidAnticipatedDrawTimeChange(AnticipatedDrawTime()); } bool Scheduler::WillDrawIfNeeded() const { diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h index 6ecd889..d8151fa 100644 --- a/cc/scheduler/scheduler.h +++ b/cc/scheduler/scheduler.h @@ -11,7 +11,6 @@ #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" @@ -33,6 +32,7 @@ struct ScheduledActionDrawAndSwapResult { class SchedulerClient { public: + virtual void SetNeedsBeginFrameOnImplThread(bool enable) = 0; virtual void ScheduledActionSendBeginFrameToMainThread() = 0; virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapIfPossible() = 0; @@ -49,14 +49,12 @@ class SchedulerClient { virtual ~SchedulerClient() {} }; -class CC_EXPORT Scheduler : FrameRateControllerClient { +class CC_EXPORT Scheduler { 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, frame_rate_controller.Pass(), scheduler_settings)); + return make_scoped_ptr(new Scheduler(client, scheduler_settings)); } virtual ~Scheduler(); @@ -86,12 +84,6 @@ class CC_EXPORT Scheduler : FrameRateControllerClient { void FinishCommit(); void BeginFrameAbortedByMainThread(); - void SetMaxFramesPending(int max); - int MaxFramesPending() const; - int NumFramesPendingForTesting() const; - - void DidSwapBuffersComplete(); - void DidLoseOutputSurface(); void DidCreateAndInitializeOutputSurface(); bool HasInitializedOutputSurface() const { @@ -103,28 +95,32 @@ class CC_EXPORT Scheduler : FrameRateControllerClient { bool WillDrawIfNeeded() const; - void SetTimebaseAndInterval(base::TimeTicks timebase, - base::TimeDelta interval); - base::TimeTicks AnticipatedDrawTime(); base::TimeTicks LastBeginFrameOnImplThreadTime(); - // FrameRateControllerClient implementation - virtual void BeginFrame(bool throttled) OVERRIDE; + void BeginFrame(base::TimeTicks frame_time); 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_; - scoped_ptr<FrameRateController> frame_rate_controller_; + + 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_; + SchedulerStateMachine state_machine_; bool inside_process_scheduled_actions_; diff --git a/cc/scheduler/scheduler_settings.cc b/cc/scheduler/scheduler_settings.cc index 29c525b..4850a108 100644 --- a/cc/scheduler/scheduler_settings.cc +++ b/cc/scheduler/scheduler_settings.cc @@ -8,7 +8,8 @@ namespace cc { SchedulerSettings::SchedulerSettings() : impl_side_painting(false), - timeout_and_draw_when_animation_checkerboards(true) {} + timeout_and_draw_when_animation_checkerboards(true), + using_synchronous_renderer_compositor(false) {} SchedulerSettings::~SchedulerSettings() {} diff --git a/cc/scheduler/scheduler_settings.h b/cc/scheduler/scheduler_settings.h index ebcfc6a..c06e28f 100644 --- a/cc/scheduler/scheduler_settings.h +++ b/cc/scheduler/scheduler_settings.h @@ -16,6 +16,7 @@ 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 76f7bb1..fda6e6b 100644 --- a/cc/scheduler/scheduler_state_machine.cc +++ b/cc/scheduler/scheduler_state_machine.cc @@ -4,6 +4,7 @@ #include "cc/scheduler/scheduler_state_machine.h" +#include "base/format_macros.h" #include "base/logging.h" #include "base/strings/stringprintf.h" @@ -12,6 +13,7 @@ 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), @@ -42,6 +44,7 @@ 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, @@ -80,6 +83,8 @@ 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_); @@ -273,6 +278,7 @@ 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 @@ -332,6 +338,11 @@ 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_) @@ -355,6 +366,10 @@ 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 1be0749..59e940c 100644 --- a/cc/scheduler/scheduler_state_machine.h +++ b/cc/scheduler/scheduler_state_machine.h @@ -8,6 +8,7 @@ #include <string> #include "base/basictypes.h" +#include "base/time.h" #include "cc/base/cc_export.h" #include "cc/scheduler/scheduler_settings.h" @@ -78,7 +79,9 @@ 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); @@ -168,6 +171,7 @@ 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_; @@ -184,6 +188,7 @@ 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_; @@ -193,6 +198,7 @@ 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 001a229..c535212 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_FALSE(state.BeginFrameNeededByImplThread()); + EXPECT_TRUE(state.BeginFrameNeededByImplThread()); } } diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc index 68aecaf..bc7d575 100644 --- a/cc/scheduler/scheduler_unittest.cc +++ b/cc/scheduler/scheduler_unittest.cc @@ -30,7 +30,11 @@ namespace { class FakeSchedulerClient : public SchedulerClient { public: - FakeSchedulerClient() { Reset(); } + FakeSchedulerClient() + : needs_begin_frame_(false) { + Reset(); + } + void Reset() { actions_.clear(); states_.clear(); @@ -39,15 +43,12 @@ class FakeSchedulerClient : public SchedulerClient { num_draws_ = 0; } - Scheduler* CreateScheduler( - scoped_ptr<FrameRateController> frame_rate_controller, - const SchedulerSettings& settings) { - scheduler_ = - Scheduler::Create(this, frame_rate_controller.Pass(), settings); + Scheduler* CreateScheduler(const SchedulerSettings& settings) { + scheduler_ = Scheduler::Create(this, 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]; } @@ -68,6 +69,11 @@ 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()); @@ -111,6 +117,7 @@ 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_; @@ -121,11 +128,8 @@ class FakeSchedulerClient : public SchedulerClient { TEST(SchedulerTest, InitializeOutputSurfaceDoesNotBeginFrame) { FakeSchedulerClient client; - scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -138,11 +142,8 @@ TEST(SchedulerTest, InitializeOutputSurfaceDoesNotBeginFrame) { TEST(SchedulerTest, RequestCommit) { FakeSchedulerClient client; - scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -153,33 +154,29 @@ TEST(SchedulerTest, RequestCommit) { // SetNeedsCommit should begin the frame. scheduler->SetNeedsCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionSendBeginFrameToMainThread", client); - EXPECT_FALSE(time_source->Active()); + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + EXPECT_TRUE(client.needs_begin_frame()); client.Reset(); // FinishCommit should commit scheduler->FinishCommit(); EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); client.Reset(); - // Tick should draw. - time_source->Tick(); - EXPECT_SINGLE_ACTION("ScheduledActionDrawAndSwapIfPossible", client); - EXPECT_FALSE(time_source->Active()); + // 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()); 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( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -190,7 +187,8 @@ TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) { // SetNedsCommit should begin the frame. scheduler->SetNeedsCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionSendBeginFrameToMainThread", client); + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); client.Reset(); // Now SetNeedsCommit again. Calling here means we need a second frame. @@ -203,78 +201,85 @@ TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) { client.Reset(); // Tick should draw but then begin another frame. - time_source->Tick(); - EXPECT_FALSE(time_source->Active()); + scheduler->BeginFrame(base::TimeTicks::Now()); + EXPECT_TRUE(client.needs_begin_frame()); 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( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); - EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client); + client.Reset(); scheduler->DidCreateAndInitializeOutputSurface(); - scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); + EXPECT_TRUE(client.needs_begin_frame()); - time_source->Tick(); - EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 1); - EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(time_source->Active()); client.Reset(); + scheduler->BeginFrame(base::TimeTicks::Now()); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + EXPECT_FALSE(scheduler->RedrawPending()); + EXPECT_FALSE(client.needs_begin_frame()); + client.Reset(); scheduler->SetMainThreadNeedsLayerTextures(); EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread", client, 0, - 2); + 3); // A commit was started by SetMainThreadNeedsLayerTextures(). - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 2); - client.Reset(); + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 3); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 2, 3); + // We should request a BeginFrame in anticipation of a draw. + client.Reset(); scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); // No draw happens since the textures are acquired by the main thread. - time_source->Tick(); - EXPECT_EQ(0, client.num_actions_()); + client.Reset(); + scheduler->BeginFrame(base::TimeTicks::Now()); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); + // Commit will release the texture. + client.Reset(); scheduler->FinishCommit(); - EXPECT_ACTION("ScheduledActionCommit", client, 0, 1); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); - client.Reset(); + EXPECT_TRUE(client.needs_begin_frame()); // Now we can draw again after the commit happens. - time_source->Tick(); - EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 1); + client.Reset(); + scheduler->BeginFrame(base::TimeTicks::Now()); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(time_source->Active()); + EXPECT_FALSE(client.needs_begin_frame()); client.Reset(); } TEST(SchedulerTest, TextureAcquisitionCollision) { FakeSchedulerClient client; - scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -285,19 +290,22 @@ TEST(SchedulerTest, TextureAcquisitionCollision) { scheduler->SetNeedsCommit(); scheduler->SetMainThreadNeedsLayerTextures(); - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 3); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 3); EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread", client, - 1, - 2); + 2, + 3); client.Reset(); - // Compositor not scheduled to draw because textures are locked by main thread - EXPECT_FALSE(time_source->Active()); + // 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()); // Trigger the commit scheduler->FinishCommit(); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); client.Reset(); // Between commit and draw, texture acquisition for main thread delayed, @@ -307,7 +315,7 @@ TEST(SchedulerTest, TextureAcquisitionCollision) { client.Reset(); // Once compositor draw complete, the delayed texture acquisition fires. - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 3); EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread", client, @@ -319,11 +327,8 @@ TEST(SchedulerTest, TextureAcquisitionCollision) { TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) { FakeSchedulerClient client; - scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -379,11 +384,8 @@ 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( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -391,28 +393,25 @@ TEST(SchedulerTest, RequestRedrawInsideDraw) { scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); EXPECT_EQ(0, client.num_draws()); - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(1, client.num_draws()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(2, client.num_draws()); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(time_source->Active()); + EXPECT_FALSE(client.needs_begin_frame()); } // 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( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -422,33 +421,33 @@ TEST(SchedulerTest, RequestRedrawInsideFailedDraw) { scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); EXPECT_EQ(0, client.num_draws()); // Fail the draw. - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); 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(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); // Fail the draw again. - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(2, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); // Draw successfully. client.SetDrawWillHappen(true); - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(3, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); } class SchedulerClientThatsetNeedsCommitInsideDraw : public FakeSchedulerClient { @@ -477,11 +476,8 @@ 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( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -490,28 +486,26 @@ TEST(SchedulerTest, RequestCommitInsideDraw) { scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); EXPECT_EQ(0, client.num_draws()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); - time_source->Tick(); - EXPECT_FALSE(time_source->Active()); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(1, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); + EXPECT_TRUE(client.needs_begin_frame()); scheduler->FinishCommit(); - time_source->Tick(); - EXPECT_EQ(2, client.num_draws()); - EXPECT_FALSE(time_source->Active()); + scheduler->BeginFrame(base::TimeTicks::Now()); + EXPECT_EQ(2, client.num_draws());; 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( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -521,128 +515,77 @@ TEST(SchedulerTest, RequestCommitInsideFailedDraw) { scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); EXPECT_EQ(0, client.num_draws()); // Fail the draw. - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); 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(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); // Fail the draw again. - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(2, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); // Draw successfully. client.SetDrawWillHappen(true); - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(3, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); } 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( - controller.PassAs<FrameRateController>(), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(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(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); EXPECT_EQ(0, client.num_draws()); // Draw successfully, this starts a new frame. - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); 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(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); // Fail to draw, this should not start a frame. client.SetDrawWillHappen(false); - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); 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( - controller.PassAs<FrameRateController>(), - default_scheduler_settings); - - EXPECT_EQ(0, controller_ptr->NumFramesPending()); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); // 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 deleted file mode 100644 index 206e2376..0000000 --- a/cc/scheduler/vsync_time_source.cc +++ /dev/null @@ -1,76 +0,0 @@ -// 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 deleted file mode 100644 index 4cd0441..0000000 --- a/cc/scheduler/vsync_time_source.h +++ /dev/null @@ -1,78 +0,0 @@ -// 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 deleted file mode 100644 index 4834630..0000000 --- a/cc/scheduler/vsync_time_source_unittest.cc +++ /dev/null @@ -1,124 +0,0 @@ -// 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 diff --git a/cc/test/fake_layer_tree_host_impl_client.h b/cc/test/fake_layer_tree_host_impl_client.h index dad4014..58816cc 100644 --- a/cc/test/fake_layer_tree_host_impl_client.h +++ b/cc/test/fake_layer_tree_host_impl_client.h @@ -17,9 +17,6 @@ class FakeLayerTreeHostImplClient : public LayerTreeHostImplClient { scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE {} virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE {} virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE {} - virtual void OnVSyncParametersChanged( - base::TimeTicks, - base::TimeDelta) OVERRIDE {} virtual void BeginFrameOnImplThread(base::TimeTicks frame_time) OVERRIDE {} virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE {} diff --git a/cc/test/fake_output_surface.cc b/cc/test/fake_output_surface.cc index c3f3e1a..9ceadda 100644 --- a/cc/test/fake_output_surface.cc +++ b/cc/test/fake_output_surface.cc @@ -8,6 +8,7 @@ #include "base/message_loop.h" #include "cc/output/compositor_frame_ack.h" #include "cc/output/output_surface_client.h" +#include "testing/gtest/include/gtest/gtest.h" namespace cc { @@ -17,7 +18,8 @@ FakeOutputSurface::FakeOutputSurface( : OutputSurface(context3d.Pass()), num_sent_frames_(0), needs_begin_frame_(false), - forced_draw_to_software_device_(false) { + forced_draw_to_software_device_(false), + fake_weak_ptr_factory_(this) { if (delegated_rendering) { capabilities_.delegated_rendering = true; capabilities_.max_frames_pending = 1; @@ -28,7 +30,8 @@ FakeOutputSurface::FakeOutputSurface( scoped_ptr<SoftwareOutputDevice> software_device, bool delegated_rendering) : OutputSurface(software_device.Pass()), num_sent_frames_(0), - forced_draw_to_software_device_(false) { + forced_draw_to_software_device_(false), + fake_weak_ptr_factory_(this) { if (delegated_rendering) { capabilities_.delegated_rendering = true; capabilities_.max_frames_pending = 1; @@ -41,7 +44,8 @@ FakeOutputSurface::FakeOutputSurface( bool delegated_rendering) : OutputSurface(context3d.Pass(), software_device.Pass()), num_sent_frames_(0), - forced_draw_to_software_device_(false) { + forced_draw_to_software_device_(false), + fake_weak_ptr_factory_(this) { if (delegated_rendering) { capabilities_.delegated_rendering = true; capabilities_.max_frames_pending = 1; @@ -56,6 +60,7 @@ void FakeOutputSurface::SwapBuffers(CompositorFrame* frame) { frame->AssignTo(&last_sent_frame_); ++num_sent_frames_; PostSwapBuffersComplete(); + DidSwapBuffers(); } else { OutputSurface::SwapBuffers(frame); frame->AssignTo(&last_sent_frame_); @@ -65,12 +70,23 @@ void FakeOutputSurface::SwapBuffers(CompositorFrame* frame) { void FakeOutputSurface::SetNeedsBeginFrame(bool enable) { needs_begin_frame_ = enable; + OutputSurface::SetNeedsBeginFrame(enable); + + // If there is not BeginFrame emulation from the FrameRateController, + // then we just post a BeginFrame to emulate it as part of the test. + if (enable && !frame_rate_controller_) { + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, base::Bind(&FakeOutputSurface::OnBeginFrame, + fake_weak_ptr_factory_.GetWeakPtr()), + base::TimeDelta::FromMilliseconds(16)); + } } -void FakeOutputSurface::BeginFrame(base::TimeTicks frame_time) { - client_->BeginFrame(frame_time); +void FakeOutputSurface::OnBeginFrame() { + OutputSurface::BeginFrame(base::TimeTicks::Now()); } + bool FakeOutputSurface::ForcedDrawToSoftwareDevice() const { return forced_draw_to_software_device_; } diff --git a/cc/test/fake_output_surface.h b/cc/test/fake_output_surface.h index d54ffd3..7bc1a4f 100644 --- a/cc/test/fake_output_surface.h +++ b/cc/test/fake_output_surface.h @@ -74,7 +74,6 @@ class FakeOutputSurface : public OutputSurface { bool needs_begin_frame() const { return needs_begin_frame_; } - void BeginFrame(base::TimeTicks frame_time); void set_forced_draw_to_software_device(bool forced) { forced_draw_to_software_device_ = forced; @@ -95,10 +94,13 @@ class FakeOutputSurface : public OutputSurface { scoped_ptr<SoftwareOutputDevice> software_device, bool delegated_rendering); + void OnBeginFrame(); + CompositorFrame last_sent_frame_; size_t num_sent_frames_; bool needs_begin_frame_; bool forced_draw_to_software_device_; + base::WeakPtrFactory<FakeOutputSurface> fake_weak_ptr_factory_; }; static inline scoped_ptr<cc::OutputSurface> CreateFakeOutputSurface() { diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc index 0bb17f346..9f046d9e 100644 --- a/cc/test/layer_tree_test.cc +++ b/cc/test/layer_tree_test.cc @@ -590,9 +590,13 @@ void LayerTreeTest::RunTest(bool threaded, } scoped_ptr<OutputSurface> LayerTreeTest::CreateOutputSurface() { + scoped_ptr<FakeOutputSurface> output_surface; if (delegating_renderer_) - return FakeOutputSurface::CreateDelegating3d().PassAs<OutputSurface>(); - return FakeOutputSurface::Create3d().PassAs<OutputSurface>(); + output_surface = FakeOutputSurface::CreateDelegating3d(); + else + output_surface = FakeOutputSurface::Create3d(); + output_surface_ = output_surface.get(); + return output_surface.PassAs<OutputSurface>(); } scoped_refptr<cc::ContextProvider> LayerTreeTest:: diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h index 856caaa..2d67a44 100644 --- a/cc/test/layer_tree_test.h +++ b/cc/test/layer_tree_test.h @@ -24,6 +24,7 @@ class LayerImpl; class LayerTreeHost; class LayerTreeHostClient; class LayerTreeHostImpl; +class FakeOutputSurface; // Used by test stubs to notify the test when something interesting happens. class TestHooks : public WebKit::WebAnimationDelegate { @@ -160,6 +161,7 @@ class LayerTreeTest : public testing::Test, public TestHooks { LayerTreeSettings settings_; scoped_ptr<LayerTreeHostClientForTesting> client_; scoped_ptr<LayerTreeHost> layer_tree_host_; + FakeOutputSurface* output_surface_; bool beginning_; bool end_when_begin_returns_; diff --git a/cc/test/scheduler_test_common.cc b/cc/test/scheduler_test_common.cc index c1aa200..8ccf47e 100644 --- a/cc/test/scheduler_test_common.cc +++ b/cc/test/scheduler_test_common.cc @@ -35,16 +35,6 @@ void FakeThread::PostDelayedTask(base::Closure cb, base::TimeDelta delay) { bool FakeThread::BelongsToCurrentThread() const { return true; } -void FakeTimeSource::SetClient(TimeSourceClient* client) { client_ = client; } - -void FakeTimeSource::SetActive(bool b) { active_ = b; } - -bool FakeTimeSource::Active() const { return active_; } - -base::TimeTicks FakeTimeSource::LastTickTime() { return base::TimeTicks(); } - -base::TimeTicks FakeTimeSource::NextTickTime() { return base::TimeTicks(); } - base::TimeTicks FakeDelayBasedTimeSource::Now() const { return now_; } } // namespace cc diff --git a/cc/test/scheduler_test_common.h b/cc/test/scheduler_test_common.h index b3b2c2d..8834e62 100644 --- a/cc/test/scheduler_test_common.h +++ b/cc/test/scheduler_test_common.h @@ -62,36 +62,6 @@ class FakeThread : public cc::Thread { bool run_pending_task_on_overwrite_; }; -class FakeTimeSource : public cc::TimeSource { - public: - FakeTimeSource() : active_(false), client_(0) {} - - virtual void SetClient(cc::TimeSourceClient* client) OVERRIDE; - virtual void SetActive(bool b) OVERRIDE; - virtual bool Active() const OVERRIDE; - virtual void SetTimebaseAndInterval(base::TimeTicks timebase, - base::TimeDelta interval) OVERRIDE {} - virtual base::TimeTicks LastTickTime() OVERRIDE; - virtual base::TimeTicks NextTickTime() OVERRIDE; - - void Tick() { - ASSERT_TRUE(active_); - if (client_) - client_->OnTimerTick(); - } - - void SetNextTickTime(base::TimeTicks next_tick_time) { - next_tick_time_ = next_tick_time; - } - - protected: - virtual ~FakeTimeSource() {} - - bool active_; - base::TimeTicks next_tick_time_; - cc::TimeSourceClient* client_; -}; - class FakeDelayBasedTimeSource : public cc::DelayBasedTimeSource { public: static scoped_refptr<FakeDelayBasedTimeSource> Create( diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index a497421..2670bf8 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc @@ -1071,11 +1071,6 @@ void LayerTreeHostImpl::SetNeedsRedrawRect(gfx::Rect damage_rect) { client_->SetNeedsRedrawRectOnImplThread(damage_rect); } -void LayerTreeHostImpl::OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) { - client_->OnVSyncParametersChanged(timebase, interval); -} - void LayerTreeHostImpl::BeginFrame(base::TimeTicks frame_time) { client_->BeginFrameOnImplThread(frame_time); } @@ -1514,6 +1509,25 @@ bool LayerTreeHostImpl::DoInitializeRenderer( resource_provider_ = resource_provider.Pass(); } + // Setup BeginFrameEmulation if it's not supported natively + if (!settings_.begin_frame_scheduling_enabled) { + const base::TimeDelta display_refresh_interval = + base::TimeDelta::FromMicroseconds( + base::Time::kMicrosecondsPerSecond / + settings_.refresh_rate); + + output_surface->InitializeBeginFrameEmulation( + proxy_->ImplThread(), + settings_.throttle_frame_production, + display_refresh_interval); + } + + int max_frames_pending = + output_surface->capabilities().max_frames_pending; + if (max_frames_pending <= 0) + max_frames_pending = FrameRateController::DEFAULT_MAX_FRAMES_PENDING; + output_surface->SetMaxFramesPending(max_frames_pending); + output_surface_ = output_surface.Pass(); if (!visible_) diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h index 72c542d..e2a303e 100644 --- a/cc/trees/layer_tree_host_impl.h +++ b/cc/trees/layer_tree_host_impl.h @@ -54,8 +54,6 @@ class LayerTreeHostImplClient { scoped_refptr<ContextProvider> offscreen_context_provider) = 0; virtual void DidLoseOutputSurfaceOnImplThread() = 0; virtual void OnSwapBuffersCompleteOnImplThread() = 0; - virtual void OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) = 0; virtual void BeginFrameOnImplThread(base::TimeTicks frame_time) = 0; virtual void OnCanDrawStateChanged(bool can_draw) = 0; virtual void OnHasPendingTreeStateChanged(bool has_pending_tree) = 0; @@ -183,8 +181,6 @@ class CC_EXPORT LayerTreeHostImpl virtual float DeviceScaleFactor() const OVERRIDE; virtual const LayerTreeSettings& Settings() const OVERRIDE; public: - virtual void DidLoseOutputSurface() OVERRIDE; - virtual void OnSwapBuffersComplete(const CompositorFrameAck* ack) OVERRIDE; virtual void SetFullRootLayerDamage() OVERRIDE; virtual void SetManagedMemoryPolicy(const ManagedMemoryPolicy& policy) OVERRIDE; @@ -205,12 +201,12 @@ class CC_EXPORT LayerTreeHostImpl virtual bool DeferredInitialize( scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE; virtual void SetNeedsRedrawRect(gfx::Rect rect) OVERRIDE; - virtual void OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) OVERRIDE; virtual void BeginFrame(base::TimeTicks frame_time) OVERRIDE; virtual void SetExternalDrawConstraints(const gfx::Transform& transform, gfx::Rect viewport) OVERRIDE; + virtual void DidLoseOutputSurface() OVERRIDE; + virtual void OnSwapBuffersComplete(const CompositorFrameAck* ack) OVERRIDE; // Called from LayerTreeImpl. void OnCanDrawStateChangedForTree(); diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index 8ceb6bd..78e5a6d 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc @@ -95,8 +95,6 @@ class LayerTreeHostImplTest : public testing::Test, } virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE {} virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE {} - virtual void OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) OVERRIDE {} virtual void BeginFrameOnImplThread(base::TimeTicks frame_time) OVERRIDE {} virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE { diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc index b8a2e9c..e4d192e 100644 --- a/cc/trees/layer_tree_host_unittest.cc +++ b/cc/trees/layer_tree_host_unittest.cc @@ -2049,36 +2049,6 @@ class LayerTreeHostTestCapturePicture : public LayerTreeHostTest { MULTI_THREAD_TEST_F(LayerTreeHostTestCapturePicture); -class LayerTreeHostTestMaxPendingFrames : public LayerTreeHostTest { - public: - LayerTreeHostTestMaxPendingFrames() : LayerTreeHostTest() {} - - virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } - - virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { - DCHECK(host_impl->proxy()->HasImplThread()); - - const ThreadProxy* proxy = static_cast<ThreadProxy*>(host_impl->proxy()); - if (delegating_renderer()) { - EXPECT_EQ(1, proxy->MaxFramesPendingForTesting()); - } else { - EXPECT_EQ(FrameRateController::DEFAULT_MAX_FRAMES_PENDING, - proxy->MaxFramesPendingForTesting()); - } - EndTest(); - } - - virtual void AfterTest() OVERRIDE {} -}; - -TEST_F(LayerTreeHostTestMaxPendingFrames, DelegatingRenderer) { - RunTest(true, true, true); -} - -TEST_F(LayerTreeHostTestMaxPendingFrames, GLRenderer) { - RunTest(true, false, true); -} - class LayerTreeHostTestShutdownWithOnlySomeResourcesEvicted : public LayerTreeHostTest { public: @@ -2253,32 +2223,10 @@ class LayerTreeHostTestBeginFrameNotification : public LayerTreeHostTest { } virtual void BeginTest() OVERRIDE { + // This will trigger a SetNeedsBeginFrame which will trigger a BeginFrame. PostSetNeedsCommitToMainThread(); } - virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { - FakeOutputSurface* fake_output_surface = - reinterpret_cast<FakeOutputSurface*>(host_impl->output_surface()); - - // The BeginFrame notification is turned off now but will get - // enabled once we return, so post a task to trigger it. - ASSERT_FALSE(fake_output_surface->needs_begin_frame()); - PostBeginFrameOnImplThread(fake_output_surface); - } - - void PostBeginFrameOnImplThread(FakeOutputSurface* fake_output_surface) { - DCHECK(ImplThread()); - ImplThread()->PostTask( - base::Bind(&LayerTreeHostTestBeginFrameNotification::BeginFrame, - base::Unretained(this), - base::Unretained(fake_output_surface))); - } - - void BeginFrame(FakeOutputSurface* fake_output_surface) { - ASSERT_TRUE(fake_output_surface->needs_begin_frame()); - fake_output_surface->BeginFrame(frame_time_); - } - virtual bool PrepareToDrawOnThread( LayerTreeHostImpl* host_impl, LayerTreeHostImpl::FrameData* frame, @@ -2824,11 +2772,6 @@ class LayerTreeHostTestNumFramesPending : public LayerTreeHostTest { } } - virtual void SwapBuffersCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { - const ThreadProxy* proxy = static_cast<ThreadProxy*>(impl->proxy()); - EXPECT_EQ(0, proxy->NumFramesPendingForTesting()); - } - virtual void AfterTest() OVERRIDE {} protected: @@ -2895,6 +2838,9 @@ class LayerTreeHostTestDeferredInitialize : public LayerTreeHostTest { // Force redraw again. host_impl->SetNeedsRedrawRect(gfx::Rect(1, 1)); + + // If we didn't swap this begin frame, we need to request another one. + host_impl->SetNeedsBeginFrame(true); } virtual void AfterTest() OVERRIDE { diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc index c448002..29cc4f4 100644 --- a/cc/trees/layer_tree_host_unittest_animation.cc +++ b/cc/trees/layer_tree_host_unittest_animation.cc @@ -193,7 +193,7 @@ class LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws started_animating_ = true; } - virtual void DrawLayersOnThread(LayerTreeHostImpl* tree_impl) OVERRIDE { + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { if (started_animating_) EndTest(); } @@ -205,7 +205,7 @@ class LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws return false; } - virtual void AfterTest() OVERRIDE {} + virtual void AfterTest() OVERRIDE { } private: bool started_animating_; diff --git a/cc/trees/proxy.cc b/cc/trees/proxy.cc index 8d2d2b7..fd39ed0 100644 --- a/cc/trees/proxy.cc +++ b/cc/trees/proxy.cc @@ -78,4 +78,8 @@ Proxy::~Proxy() { DCHECK(IsMainThread()); } +std::string Proxy::SchedulerStateAsStringForTesting() { + return ""; +} + } // namespace cc diff --git a/cc/trees/proxy.h b/cc/trees/proxy.h index aaab780..4e6171c 100644 --- a/cc/trees/proxy.h +++ b/cc/trees/proxy.h @@ -5,6 +5,8 @@ #ifndef CC_TREES_PROXY_H_ #define CC_TREES_PROXY_H_ +#include <string> + #include "base/basictypes.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" @@ -98,6 +100,7 @@ class CC_EXPORT Proxy { // Testing hooks virtual bool CommitPendingForTesting() = 0; + virtual std::string SchedulerStateAsStringForTesting(); protected: explicit Proxy(scoped_ptr<Thread> impl_thread); diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h index 0c3946b..9d995ba 100644 --- a/cc/trees/single_thread_proxy.h +++ b/cc/trees/single_thread_proxy.h @@ -51,8 +51,6 @@ class SingleThreadProxy : public Proxy, LayerTreeHostImplClient { scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE; virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE; virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE {} - virtual void OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) OVERRIDE {} virtual void BeginFrameOnImplThread(base::TimeTicks frame_time) OVERRIDE {} virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE; diff --git a/cc/trees/thread_proxy.cc b/cc/trees/thread_proxy.cc index 53db2c2..020c255 100644 --- a/cc/trees/thread_proxy.cc +++ b/cc/trees/thread_proxy.cc @@ -4,6 +4,8 @@ #include "cc/trees/thread_proxy.h" +#include <string> + #include "base/auto_reset.h" #include "base/bind.h" #include "base/debug/trace_event.h" @@ -16,7 +18,6 @@ #include "cc/scheduler/delay_based_time_source.h" #include "cc/scheduler/frame_rate_controller.h" #include "cc/scheduler/scheduler.h" -#include "cc/scheduler/vsync_time_source.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_impl.h" @@ -32,6 +33,23 @@ const double kSmoothnessTakesPriorityExpirationDelay = 0.25; namespace cc { +struct ThreadProxy::ReadbackRequest { + CompletionEvent completion; + bool success; + void* pixels; + gfx::Rect rect; +}; + +struct ThreadProxy::CommitPendingRequest { + CompletionEvent completion; + bool commit_pending; +}; + +struct ThreadProxy::SchedulerStateRequest { + CompletionEvent completion; + std::string state; +}; + scoped_ptr<Proxy> ThreadProxy::Create(LayerTreeHost* layer_tree_host, scoped_ptr<Thread> impl_thread) { return make_scoped_ptr( @@ -64,7 +82,6 @@ ThreadProxy::ThreadProxy(LayerTreeHost* layer_tree_host, layer_tree_host->settings().begin_frame_scheduling_enabled), using_synchronous_renderer_compositor_( layer_tree_host->settings().using_synchronous_renderer_compositor), - vsync_client_(NULL), inside_draw_(false), defer_commits_(false), renew_tree_priority_on_impl_thread_pending_(false) { @@ -328,36 +345,21 @@ void ThreadProxy::CheckOutputSurfaceStatusOnImplThread() { void ThreadProxy::OnSwapBuffersCompleteOnImplThread() { DCHECK(IsImplThread()); TRACE_EVENT0("cc", "ThreadProxy::OnSwapBuffersCompleteOnImplThread"); - scheduler_on_impl_thread_->DidSwapBuffersComplete(); Proxy::MainThread()->PostTask( base::Bind(&ThreadProxy::DidCompleteSwapBuffers, main_thread_weak_ptr_)); } -void ThreadProxy::OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) { +void ThreadProxy::SetNeedsBeginFrameOnImplThread(bool enable) { DCHECK(IsImplThread()); - TRACE_EVENT2("cc", - "ThreadProxy::OnVSyncParametersChanged", - "timebase", - (timebase - base::TimeTicks()).InMilliseconds(), - "interval", - interval.InMilliseconds()); - scheduler_on_impl_thread_->SetTimebaseAndInterval(timebase, interval); + TRACE_EVENT1("cc", "ThreadProxy::SetNeedsBeginFrameOnImplThread", + "enable", enable); + layer_tree_host_impl_->SetNeedsBeginFrame(enable); } void ThreadProxy::BeginFrameOnImplThread(base::TimeTicks frame_time) { DCHECK(IsImplThread()); - TRACE_EVENT0("cc", "ThreadProxy::OnBeginFrameOnImplThread"); - if (vsync_client_) - vsync_client_->DidVSync(frame_time); -} - -void ThreadProxy::RequestVSyncNotification(VSyncClient* client) { - DCHECK(IsImplThread()); - TRACE_EVENT1( - "cc", "ThreadProxy::RequestVSyncNotification", "enable", !!client); - vsync_client_ = client; - layer_tree_host_impl_->SetNeedsBeginFrame(!!client); + TRACE_EVENT0("cc", "ThreadProxy::BeginFrameOnImplThread"); + scheduler_on_impl_thread_->BeginFrame(frame_time); } void ThreadProxy::OnCanDrawStateChanged(bool can_draw) { @@ -1110,35 +1112,14 @@ void ThreadProxy::InitializeImplOnImplThread(CompletionEvent* completion) { TRACE_EVENT0("cc", "ThreadProxy::InitializeImplOnImplThread"); DCHECK(IsImplThread()); layer_tree_host_impl_ = layer_tree_host_->CreateLayerTreeHostImpl(this); - const base::TimeDelta display_refresh_interval = - base::TimeDelta::FromMicroseconds( - base::Time::kMicrosecondsPerSecond / - layer_tree_host_->settings().refresh_rate); - scoped_ptr<FrameRateController> frame_rate_controller; - if (throttle_frame_production_) { - if (begin_frame_scheduling_enabled_) { - frame_rate_controller.reset( - new FrameRateController(VSyncTimeSource::Create( - this, - using_synchronous_renderer_compositor_ ? - VSyncTimeSource::DISABLE_SYNCHRONOUSLY : - VSyncTimeSource::DISABLE_ON_NEXT_TICK))); - } else { - frame_rate_controller.reset( - new FrameRateController(DelayBasedTimeSource::Create( - display_refresh_interval, Proxy::ImplThread()))); - } - } else { - frame_rate_controller.reset(new FrameRateController(Proxy::ImplThread())); - } const LayerTreeSettings& settings = layer_tree_host_->settings(); SchedulerSettings scheduler_settings; scheduler_settings.impl_side_painting = settings.impl_side_painting; scheduler_settings.timeout_and_draw_when_animation_checkerboards = settings.timeout_and_draw_when_animation_checkerboards; - scheduler_on_impl_thread_ = Scheduler::Create(this, - frame_rate_controller.Pass(), - scheduler_settings); + scheduler_settings.using_synchronous_renderer_compositor = + settings.using_synchronous_renderer_compositor; + scheduler_on_impl_thread_ = Scheduler::Create(this, scheduler_settings); scheduler_on_impl_thread_->SetVisible(layer_tree_host_impl_->visible()); impl_thread_weak_ptr_ = weak_factory_on_impl_thread_.GetWeakPtr(); @@ -1164,16 +1145,6 @@ void ThreadProxy::InitializeOutputSurfaceOnImplThread( if (*success) { *capabilities = layer_tree_host_impl_->GetRendererCapabilities(); - - OutputSurface* output_surface_ptr = layer_tree_host_impl_->output_surface(); - DCHECK(output_surface_ptr); - int max_frames_pending = - output_surface_ptr->capabilities().max_frames_pending; - if (max_frames_pending <= 0) - max_frames_pending = FrameRateController::DEFAULT_MAX_FRAMES_PENDING; - - scheduler_on_impl_thread_->SetMaxFramesPending(max_frames_pending); - scheduler_on_impl_thread_->DidCreateAndInitializeOutputSurface(); } @@ -1218,7 +1189,6 @@ void ThreadProxy::LayerTreeHostClosedOnImplThread(CompletionEvent* completion) { scheduler_on_impl_thread_.reset(); layer_tree_host_impl_.reset(); weak_factory_on_impl_thread_.InvalidateWeakPtrs(); - vsync_client_ = NULL; completion->Signal(); } @@ -1278,6 +1248,29 @@ void ThreadProxy::CommitPendingOnImplThreadForTesting( request->completion.Signal(); } +std::string ThreadProxy::SchedulerStateAsStringForTesting() { + if (IsImplThread()) + return scheduler_on_impl_thread_->StateAsStringForTesting(); + + SchedulerStateRequest scheduler_state_request; + { + DebugScopedSetMainThreadBlocked main_thread_blocked(this); + Proxy::ImplThread()->PostTask( + base::Bind(&ThreadProxy::SchedulerStateAsStringOnImplThreadForTesting, + impl_thread_weak_ptr_, + &scheduler_state_request)); + scheduler_state_request.completion.Wait(); + } + return scheduler_state_request.state; +} + +void ThreadProxy::SchedulerStateAsStringOnImplThreadForTesting( + SchedulerStateRequest* request) { + DCHECK(IsImplThread()); + request->state = scheduler_on_impl_thread_->StateAsStringForTesting(); + request->completion.Signal(); +} + skia::RefPtr<SkPicture> ThreadProxy::CapturePicture() { DCHECK(IsMainThread()); CompletionEvent completion; diff --git a/cc/trees/thread_proxy.h b/cc/trees/thread_proxy.h index 3cc905d..8013882 100644 --- a/cc/trees/thread_proxy.h +++ b/cc/trees/thread_proxy.h @@ -5,6 +5,8 @@ #ifndef CC_TREES_THREAD_PROXY_H_ #define CC_TREES_THREAD_PROXY_H_ +#include <string> + #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/time.h" @@ -12,7 +14,6 @@ #include "cc/base/completion_event.h" #include "cc/resources/resource_update_controller.h" #include "cc/scheduler/scheduler.h" -#include "cc/scheduler/vsync_time_source.h" #include "cc/trees/layer_tree_host_impl.h" #include "cc/trees/proxy.h" @@ -29,8 +30,7 @@ class Thread; class ThreadProxy : public Proxy, LayerTreeHostImplClient, SchedulerClient, - ResourceUpdateControllerClient, - VSyncProvider { + ResourceUpdateControllerClient { public: static scoped_ptr<Proxy> Create(LayerTreeHost* layer_tree_host, scoped_ptr<Thread> impl_thread); @@ -59,6 +59,7 @@ class ThreadProxy : public Proxy, virtual skia::RefPtr<SkPicture> CapturePicture() OVERRIDE; virtual scoped_ptr<base::Value> AsValue() const OVERRIDE; virtual bool CommitPendingForTesting() OVERRIDE; + virtual std::string SchedulerStateAsStringForTesting() OVERRIDE; // LayerTreeHostImplClient implementation virtual void DidTryInitializeRendererOnImplThread( @@ -66,10 +67,7 @@ class ThreadProxy : public Proxy, scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE; virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE; virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE; - virtual void OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) OVERRIDE; - virtual void BeginFrameOnImplThread(base::TimeTicks frame_time) - OVERRIDE; + virtual void BeginFrameOnImplThread(base::TimeTicks frame_time) OVERRIDE; virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE; virtual void OnHasPendingTreeStateChanged(bool has_pending_tree) OVERRIDE; virtual void SetNeedsRedrawOnImplThread() OVERRIDE; @@ -91,6 +89,7 @@ class ThreadProxy : public Proxy, virtual void DidActivatePendingTree() OVERRIDE; // SchedulerClient implementation + virtual void SetNeedsBeginFrameOnImplThread(bool enable) OVERRIDE; virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE; virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapIfPossible() OVERRIDE; @@ -106,17 +105,6 @@ class ThreadProxy : public Proxy, // ResourceUpdateControllerClient implementation virtual void ReadyToFinalizeTextureUpdates() OVERRIDE; - // VSyncProvider implementation - virtual void RequestVSyncNotification(VSyncClient* client) OVERRIDE; - - int MaxFramesPendingForTesting() const { - return scheduler_on_impl_thread_->MaxFramesPending(); - } - - int NumFramesPendingForTesting() const { - return scheduler_on_impl_thread_->NumFramesPendingForTesting(); - } - private: ThreadProxy(LayerTreeHost* layer_tree_host, scoped_ptr<Thread> impl_thread); @@ -143,16 +131,10 @@ class ThreadProxy : public Proxy, const RendererCapabilities& capabilities); // Called on impl thread. - struct ReadbackRequest { - CompletionEvent completion; - bool success; - void* pixels; - gfx::Rect rect; - }; - struct CommitPendingRequest { - CompletionEvent completion; - bool commit_pending; - }; + struct ReadbackRequest; + struct CommitPendingRequest; + struct SchedulerStateRequest; + void ForceCommitOnImplThread(CompletionEvent* completion); void StartCommitOnImplThread( CompletionEvent* completion, @@ -182,6 +164,8 @@ class ThreadProxy : public Proxy, void ForceSerializeOnSwapBuffersOnImplThread(CompletionEvent* completion); void CheckOutputSurfaceStatusOnImplThread(); void CommitPendingOnImplThreadForTesting(CommitPendingRequest* request); + void SchedulerStateAsStringOnImplThreadForTesting( + SchedulerStateRequest* request); void CapturePictureOnImplThread(CompletionEvent* completion, skia::RefPtr<SkPicture>* picture); void AsValueOnImplThread(CompletionEvent* completion, @@ -250,7 +234,6 @@ class ThreadProxy : public Proxy, bool throttle_frame_production_; bool begin_frame_scheduling_enabled_; bool using_synchronous_renderer_compositor_; - VSyncClient* vsync_client_; bool inside_draw_; |