diff options
author | skaslev@chromium.org <skaslev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-12 17:10:56 +0000 |
---|---|---|
committer | skaslev@chromium.org <skaslev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-12 17:10:56 +0000 |
commit | c7480aff12d1133ab4216fa6580e2edbef4d7078 (patch) | |
tree | 434dfe820371e751e9fb2e686ee0f72c19a61443 /cc | |
parent | 8426f3aaa2ba2fe446512c4dbcd7bcf3b4c7eb4a (diff) | |
download | chromium_src-c7480aff12d1133ab4216fa6580e2edbef4d7078.zip chromium_src-c7480aff12d1133ab4216fa6580e2edbef4d7078.tar.gz chromium_src-c7480aff12d1133ab4216fa6580e2edbef4d7078.tar.bz2 |
Revert 205750 "cc: Emulate BeginFrame in OutputSurfaces that don..."
> cc: Emulate BeginFrame in OutputSurfaces that don't support it natively
>
> This will allow us to avoid having two different code paths
> in the Scheduler. It also allows us to more easily remove the
> VSyncTimeSource and FrameRateController from the Scheduler.
>
> This patch instantiates the FrameRateController inside of
> OutputSurface for now, but the FrameRateController could be
> removed in future patches.
>
> BUG=245920
> BUG=243497
>
> Review URL: https://chromiumcodereview.appspot.com/15836005
TBR=brianderson@chromium.org
Review URL: https://codereview.chromium.org/16679010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@205838 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cc')
37 files changed, 805 insertions, 692 deletions
@@ -321,6 +321,8 @@ '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 f214015..07cecc0 100644 --- a/cc/cc_tests.gyp +++ b/cc/cc_tests.gyp @@ -68,6 +68,7 @@ '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 6efa083..0889b96 100644 --- a/cc/output/output_surface.cc +++ b/cc/output/output_surface.cc @@ -9,14 +9,12 @@ #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" @@ -34,7 +32,7 @@ class OutputSurfaceCallbacks WebGraphicsSwapBuffersCompleteCallbackCHROMIUM, public WebKit::WebGraphicsContext3D::WebGraphicsContextLostCallback { public: - explicit OutputSurfaceCallbacks(OutputSurface* client) + explicit OutputSurfaceCallbacks(OutputSurfaceClient* client) : client_(client) { DCHECK(client_); } @@ -46,155 +44,50 @@ class OutputSurfaceCallbacks virtual void onContextLost() { client_->DidLoseOutputSurface(); } private: - OutputSurface* client_; + OutputSurfaceClient* client_; }; OutputSurface::OutputSurface( scoped_ptr<WebKit::WebGraphicsContext3D> context3d) - : context3d_(context3d.Pass()), + : client_(NULL), + context3d_(context3d.Pass()), has_gl_discard_backbuffer_(false), has_swap_buffers_complete_callback_(false), device_scale_factor_(-1), - weak_ptr_factory_(this), - max_frames_pending_(0), - pending_swap_buffers_(0), - begin_frame_pending_(false), - client_(NULL) { + weak_ptr_factory_(this) { } OutputSurface::OutputSurface( scoped_ptr<cc::SoftwareOutputDevice> software_device) - : software_device_(software_device.Pass()), + : client_(NULL), + software_device_(software_device.Pass()), has_gl_discard_backbuffer_(false), has_swap_buffers_complete_callback_(false), device_scale_factor_(-1), - weak_ptr_factory_(this), - max_frames_pending_(0), - pending_swap_buffers_(0), - begin_frame_pending_(false), - client_(NULL) { + weak_ptr_factory_(this) { } OutputSurface::OutputSurface( scoped_ptr<WebKit::WebGraphicsContext3D> context3d, scoped_ptr<cc::SoftwareOutputDevice> software_device) - : context3d_(context3d.Pass()), + : client_(NULL), + 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), - 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); + weak_ptr_factory_(this) { } 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; @@ -250,7 +143,7 @@ void OutputSurface::SetContext3D( context3d_ = context3d.Pass(); - callbacks_.reset(new OutputSurfaceCallbacks(this)); + callbacks_.reset(new OutputSurfaceCallbacks(client_)); context3d_->setSwapBuffersCompleteCallbackCHROMIUM(callbacks_.get()); context3d_->setContextLostCallback(callbacks_.get()); } @@ -313,16 +206,20 @@ 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::OnSwapBuffersComplete, - weak_ptr_factory_.GetWeakPtr(), - static_cast<CompositorFrameAck*>(NULL))); + base::Bind(&OutputSurface::SwapBuffersComplete, + weak_ptr_factory_.GetWeakPtr())); +} + +void OutputSurface::SwapBuffersComplete() { + if (!client_) + return; + + client_->OnSwapBuffersComplete(NULL); } } // namespace cc diff --git a/cc/output/output_surface.h b/cc/output/output_surface.h index 8f782d7..d263d3a 100644 --- a/cc/output/output_surface.h +++ b/cc/output/output_surface.h @@ -12,7 +12,6 @@ #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; } @@ -20,16 +19,13 @@ 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: @@ -38,7 +34,7 @@ class Thread; // 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 : public FrameRateControllerClient { +class CC_EXPORT OutputSurface { public: explicit OutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D> context3d); @@ -87,13 +83,6 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient { // 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(); @@ -114,7 +103,7 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient { // 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. @@ -127,6 +116,7 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient { void PostSwapBuffersComplete(); + OutputSurfaceClient* client_; struct cc::OutputSurface::Capabilities capabilities_; scoped_ptr<OutputSurfaceCallbacks> callbacks_; scoped_ptr<WebKit::WebGraphicsContext3D> context3d_; @@ -135,34 +125,12 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient { 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: - OutputSurfaceClient* client_; - friend class OutputSurfaceCallbacks; - void SetContext3D(scoped_ptr<WebKit::WebGraphicsContext3D> context3d); + void SwapBuffersComplete(); + + base::WeakPtrFactory<OutputSurface> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(OutputSurface); }; diff --git a/cc/output/output_surface_client.h b/cc/output/output_surface_client.h index d727629..f5999c3 100644 --- a/cc/output/output_surface_client.h +++ b/cc/output/output_surface_client.h @@ -27,6 +27,8 @@ 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 7dcb1cc..ebc018f 100644 --- a/cc/output/output_surface_unittest.cc +++ b/cc/output/output_surface_unittest.cc @@ -5,7 +5,6 @@ #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" @@ -26,43 +25,19 @@ 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() - : begin_frame_count_(0), - deferred_initialize_result_(true), + : deferred_initialize_result_(true), deferred_initialize_called_(false), did_lose_output_surface_called_(false) {} @@ -72,9 +47,9 @@ class FakeOutputSurfaceClient : public OutputSurfaceClient { return deferred_initialize_result_; } virtual void SetNeedsRedrawRect(gfx::Rect damage_rect) OVERRIDE {} - virtual void BeginFrame(base::TimeTicks frame_time) OVERRIDE { - begin_frame_count_++; - } + virtual void OnVSyncParametersChanged(base::TimeTicks timebase, + base::TimeDelta interval) OVERRIDE {} + virtual void BeginFrame(base::TimeTicks frame_time) OVERRIDE {} virtual void OnSwapBuffersComplete(const CompositorFrameAck* ack) OVERRIDE {} virtual void DidLoseOutputSurface() OVERRIDE { did_lose_output_surface_called_ = true; @@ -82,10 +57,6 @@ 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; } @@ -99,7 +70,6 @@ class FakeOutputSurfaceClient : public OutputSurfaceClient { } private: - int begin_frame_count_; bool deferred_initialize_result_; bool deferred_initialize_called_; bool did_lose_output_surface_called_; @@ -111,11 +81,11 @@ TEST(OutputSurfaceTest, ClientPointerIndicatesBindToClientSuccess) { TestOutputSurface output_surface( context3d.PassAs<WebKit::WebGraphicsContext3D>()); - EXPECT_FALSE(output_surface.HasClientForTesting()); + EXPECT_EQ(NULL, output_surface.client()); FakeOutputSurfaceClient client; EXPECT_TRUE(output_surface.BindToClient(&client)); - EXPECT_TRUE(output_surface.HasClientForTesting()); + EXPECT_EQ(&client, output_surface.client()); EXPECT_FALSE(client.deferred_initialize_called()); // Verify DidLoseOutputSurface callback is hooked up correctly. @@ -134,11 +104,11 @@ TEST(OutputSurfaceTest, ClientPointerIndicatesBindToClientFailure) { TestOutputSurface output_surface( context3d.PassAs<WebKit::WebGraphicsContext3D>()); - EXPECT_FALSE(output_surface.HasClientForTesting()); + EXPECT_EQ(NULL, output_surface.client()); FakeOutputSurfaceClient client; EXPECT_FALSE(output_surface.BindToClient(&client)); - EXPECT_FALSE(output_surface.HasClientForTesting()); + EXPECT_EQ(NULL, output_surface.client()); } class InitializeNewContext3D : public ::testing::Test { @@ -151,13 +121,13 @@ class InitializeNewContext3D : public ::testing::Test { protected: void BindOutputSurface() { EXPECT_TRUE(output_surface_.BindToClient(&client_)); - EXPECT_TRUE(output_surface_.HasClientForTesting()); + EXPECT_EQ(&client_, output_surface_.client()); } void InitializeNewContextExpectFail() { EXPECT_FALSE(output_surface_.InitializeNewContext3D( context3d_.PassAs<WebKit::WebGraphicsContext3D>())); - EXPECT_TRUE(output_surface_.HasClientForTesting()); + EXPECT_EQ(&client_, output_surface_.client()); EXPECT_FALSE(output_surface_.context3d()); EXPECT_TRUE(output_surface_.software_device()); @@ -194,110 +164,5 @@ 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 90cf0d7..b4f1272 100644 --- a/cc/scheduler/frame_rate_controller.cc +++ b/cc/scheduler/frame_rate_controller.cc @@ -21,9 +21,7 @@ class FrameRateControllerTimeSourceAdapter : public TimeSourceClient { } virtual ~FrameRateControllerTimeSourceAdapter() {} - virtual void OnTimerTick() OVERRIDE { - frame_rate_controller_->OnTimerTick(); - } + virtual void OnTimerTick() OVERRIDE { frame_rate_controller_->OnTimerTick(); } private: explicit FrameRateControllerTimeSourceAdapter( @@ -36,7 +34,7 @@ class FrameRateControllerTimeSourceAdapter : public TimeSourceClient { FrameRateController::FrameRateController(scoped_refptr<TimeSource> timer) : client_(NULL), num_frames_pending_(0), - max_swaps_pending_(0), + max_frames_pending_(0), time_source_(timer), active_(false), is_time_source_throttling_(true), @@ -50,7 +48,7 @@ FrameRateController::FrameRateController(scoped_refptr<TimeSource> timer) FrameRateController::FrameRateController(Thread* thread) : client_(NULL), num_frames_pending_(0), - max_swaps_pending_(0), + max_frames_pending_(0), active_(false), is_time_source_throttling_(false), weak_factory_(this), @@ -77,9 +75,9 @@ void FrameRateController::SetActive(bool active) { } } -void FrameRateController::SetMaxSwapsPending(int max_swaps_pending) { - DCHECK_GE(max_swaps_pending, 0); - max_swaps_pending_ = max_swaps_pending; +void FrameRateController::SetMaxFramesPending(int max_frames_pending) { + DCHECK_GE(max_frames_pending, 0); + max_frames_pending_ = max_frames_pending; } void FrameRateController::SetTimebaseAndInterval(base::TimeTicks timebase, @@ -89,17 +87,15 @@ void FrameRateController::SetTimebaseAndInterval(base::TimeTicks timebase, } void FrameRateController::OnTimerTick() { - TRACE_EVENT0("cc", "FrameRateController::OnTimerTick"); DCHECK(active_); // Check if we have too many frames in flight. bool throttled = - max_swaps_pending_ && num_frames_pending_ >= max_swaps_pending_; + max_frames_pending_ && num_frames_pending_ >= max_frames_pending_; TRACE_COUNTER_ID1("cc", "ThrottledCompositor", thread_, throttled); - if (client_) { - client_->FrameRateControllerTick(throttled); - } + if (client_) + client_->BeginFrame(throttled); if (!is_time_source_throttling_ && !throttled) PostManualTick(); @@ -112,9 +108,7 @@ void FrameRateController::PostManualTick() { } } -void FrameRateController::ManualTick() { - OnTimerTick(); -} +void FrameRateController::ManualTick() { OnTimerTick(); } void FrameRateController::DidSwapBuffers() { num_frames_pending_++; diff --git a/cc/scheduler/frame_rate_controller.h b/cc/scheduler/frame_rate_controller.h index 70964e2..070c26a 100644 --- a/cc/scheduler/frame_rate_controller.h +++ b/cc/scheduler/frame_rate_controller.h @@ -15,21 +15,18 @@ namespace cc { class Thread; class TimeSource; -class FrameRateController; class CC_EXPORT FrameRateControllerClient { - protected: - virtual ~FrameRateControllerClient() {} - public: // Throttled is true when we have a maximum number of frames pending. - virtual void FrameRateControllerTick(bool throttled) = 0; + virtual void BeginFrame(bool throttled) = 0; + + protected: + virtual ~FrameRateControllerClient() {} }; class FrameRateControllerTimeSourceAdapter; -// The FrameRateController is used in cases where we self-tick (i.e. BeginFrame -// is not sent by a parent compositor. class CC_EXPORT FrameRateController { public: enum { @@ -44,7 +41,6 @@ class CC_EXPORT FrameRateController { void SetClient(FrameRateControllerClient* client) { client_ = client; } void SetActive(bool active); - bool IsActive() { return active_; } // Use the following methods to adjust target frame rate. // @@ -55,9 +51,9 @@ class CC_EXPORT FrameRateController { void DidSwapBuffers(); void DidSwapBuffersComplete(); void DidAbortAllPendingFrames(); - void SetMaxSwapsPending(int max_swaps_pending); // 0 for unlimited. - int MaxSwapsPending() const { return max_swaps_pending_; } - int NumSwapsPendingForTesting() const { return num_frames_pending_; } + void SetMaxFramesPending(int max_frames_pending); // 0 for unlimited. + int MaxFramesPending() const { return max_frames_pending_; } + int NumFramesPendingForTesting() const { return num_frames_pending_; } // This returns null for unthrottled frame-rate. base::TimeTicks NextTickTime(); @@ -77,7 +73,7 @@ class CC_EXPORT FrameRateController { FrameRateControllerClient* client_; int num_frames_pending_; - int max_swaps_pending_; + int max_frames_pending_; scoped_refptr<TimeSource> time_source_; scoped_ptr<FrameRateControllerTimeSourceAdapter> time_source_client_adapter_; bool active_; @@ -87,7 +83,6 @@ class CC_EXPORT FrameRateController { base::WeakPtrFactory<FrameRateController> weak_factory_; Thread* thread_; - private: DISALLOW_COPY_AND_ASSIGN(FrameRateController); }; diff --git a/cc/scheduler/frame_rate_controller_unittest.cc b/cc/scheduler/frame_rate_controller_unittest.cc index 3fcf4a7..183f576 100644 --- a/cc/scheduler/frame_rate_controller_unittest.cc +++ b/cc/scheduler/frame_rate_controller_unittest.cc @@ -17,7 +17,7 @@ class FakeFrameRateControllerClient : public cc::FrameRateControllerClient { void Reset() { began_frame_ = false; } bool BeganFrame() const { return began_frame_; } - virtual void FrameRateControllerTick(bool throttled) OVERRIDE { + virtual void BeginFrame(bool throttled) OVERRIDE { began_frame_ = !throttled; } @@ -74,7 +74,7 @@ TEST(FrameRateControllerTest, TestFrameThrottling_TwoFramesInFlight) { controller.SetClient(&client); controller.SetActive(true); - controller.SetMaxSwapsPending(2); + controller.SetMaxFramesPending(2); base::TimeTicks elapsed; // Muck around with time a bit @@ -132,7 +132,7 @@ TEST(FrameRateControllerTest, TestFrameThrottling_Unthrottled) { FrameRateController controller(&thread); controller.SetClient(&client); - controller.SetMaxSwapsPending(2); + controller.SetMaxFramesPending(2); // SetActive triggers 1st frame, make sure the BeginFrame callback // is called diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc index 7110bff..d308b0d 100644 --- a/cc/scheduler/scheduler.cc +++ b/cc/scheduler/scheduler.cc @@ -7,29 +7,23 @@ #include "base/auto_reset.h" #include "base/debug/trace_event.h" #include "base/logging.h" -#include "cc/base/thread.h" namespace cc { Scheduler::Scheduler(SchedulerClient* client, + scoped_ptr<FrameRateController> frame_rate_controller, const SchedulerSettings& scheduler_settings) : settings_(scheduler_settings), client_(client), - weak_factory_(this), - last_set_needs_begin_frame_(false), - has_pending_begin_frame_(false), - last_begin_frame_time_(base::TimeTicks()), - // TODO(brianderson): Pass with BeginFrame in the near future. - interval_(base::TimeDelta::FromMicroseconds(16666)), + frame_rate_controller_(frame_rate_controller.Pass()), state_machine_(scheduler_settings), inside_process_scheduled_actions_(false) { DCHECK(client_); + frame_rate_controller_->SetClient(this); DCHECK(!state_machine_.BeginFrameNeededByImplThread()); } -Scheduler::~Scheduler() { - client_->SetNeedsBeginFrameOnImplThread(false); -} +Scheduler::~Scheduler() { frame_rate_controller_->SetActive(false); } void Scheduler::SetCanStart() { state_machine_.SetCanStart(); @@ -94,6 +88,23 @@ void Scheduler::BeginFrameAbortedByMainThread() { ProcessScheduledActions(); } +void Scheduler::SetMaxFramesPending(int max_frames_pending) { + frame_rate_controller_->SetMaxFramesPending(max_frames_pending); +} + +int Scheduler::MaxFramesPending() const { + return frame_rate_controller_->MaxFramesPending(); +} + +int Scheduler::NumFramesPendingForTesting() const { + return frame_rate_controller_->NumFramesPendingForTesting(); +} + +void Scheduler::DidSwapBuffersComplete() { + TRACE_EVENT0("cc", "Scheduler::DidSwapBuffersComplete"); + frame_rate_controller_->DidSwapBuffersComplete(); +} + void Scheduler::DidLoseOutputSurface() { TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface"); state_machine_.DidLoseOutputSurface(); @@ -102,69 +113,31 @@ void Scheduler::DidLoseOutputSurface() { void Scheduler::DidCreateAndInitializeOutputSurface() { TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface"); + frame_rate_controller_->DidAbortAllPendingFrames(); state_machine_.DidCreateAndInitializeOutputSurface(); - has_pending_begin_frame_ = false; - last_set_needs_begin_frame_ = false; ProcessScheduledActions(); } +void Scheduler::SetTimebaseAndInterval(base::TimeTicks timebase, + base::TimeDelta interval) { + frame_rate_controller_->SetTimebaseAndInterval(timebase, interval); +} + base::TimeTicks Scheduler::AnticipatedDrawTime() { - TRACE_EVENT0("cc", "Scheduler::AnticipatedDrawTime"); - base::TimeTicks now = base::TimeTicks::Now(); - int64 intervals = ((now - last_begin_frame_time_) / interval_) + 1; - return last_begin_frame_time_ + (interval_ * intervals); + return frame_rate_controller_->NextTickTime(); } base::TimeTicks Scheduler::LastBeginFrameOnImplThreadTime() { - return last_begin_frame_time_; -} - -void Scheduler::SetupNextBeginFrameIfNeeded() { - // Determine if we need BeginFrame notifications. - // If we do, always request the BeginFrame immediately. - // If not, only disable on the next BeginFrame to avoid unnecessary toggles. - // The synchronous renderer compositor requires immediate disables though. - bool needs_begin_frame = state_machine_.BeginFrameNeededByImplThread(); - if ((needs_begin_frame || - state_machine_.inside_begin_frame() || - settings_.using_synchronous_renderer_compositor) && - (needs_begin_frame != last_set_needs_begin_frame_)) { - client_->SetNeedsBeginFrameOnImplThread(needs_begin_frame); - last_set_needs_begin_frame_ = needs_begin_frame; - } - - // Request another BeginFrame if we haven't drawn for now until we have - // deadlines implemented. - if (state_machine_.inside_begin_frame() && has_pending_begin_frame_) { - has_pending_begin_frame_ = false; - client_->SetNeedsBeginFrameOnImplThread(true); - } + return frame_rate_controller_->LastTickTime(); } -void Scheduler::BeginFrame(base::TimeTicks frame_time) { - TRACE_EVENT0("cc", "Scheduler::BeginFrame"); - DCHECK(!has_pending_begin_frame_); - has_pending_begin_frame_ = true; - last_begin_frame_time_ = frame_time; - state_machine_.DidEnterBeginFrame(); - state_machine_.SetFrameTime(frame_time); +void Scheduler::BeginFrame(bool throttled) { + TRACE_EVENT1("cc", "Scheduler::BeginFrame", "throttled", throttled); + if (!throttled) + state_machine_.DidEnterBeginFrame(); ProcessScheduledActions(); - state_machine_.DidLeaveBeginFrame(); -} - -void Scheduler::DrawAndSwapIfPossible() { - ScheduledActionDrawAndSwapResult result = - client_->ScheduledActionDrawAndSwapIfPossible(); - state_machine_.DidDrawIfPossibleCompleted(result.did_draw); - if (result.did_swap) - has_pending_begin_frame_ = false; -} - -void Scheduler::DrawAndSwapForced() { - ScheduledActionDrawAndSwapResult result = - client_->ScheduledActionDrawAndSwapForced(); - if (result.did_swap) - has_pending_begin_frame_ = false; + if (!throttled) + state_machine_.DidLeaveBeginFrame(); } void Scheduler::ProcessScheduledActions() { @@ -178,6 +151,9 @@ void Scheduler::ProcessScheduledActions() { SchedulerStateMachine::Action action = state_machine_.NextAction(); while (action != SchedulerStateMachine::ACTION_NONE) { state_machine_.UpdateState(action); + TRACE_EVENT1( + "cc", "Scheduler::ProcessScheduledActions()", "action", action); + switch (action) { case SchedulerStateMachine::ACTION_NONE: break; @@ -193,12 +169,23 @@ void Scheduler::ProcessScheduledActions() { case SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED: client_->ScheduledActionActivatePendingTreeIfNeeded(); break; - case SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE: - DrawAndSwapIfPossible(); + case SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE: { + frame_rate_controller_->DidSwapBuffers(); + ScheduledActionDrawAndSwapResult result = + client_->ScheduledActionDrawAndSwapIfPossible(); + state_machine_.DidDrawIfPossibleCompleted(result.did_draw); + if (!result.did_swap) + frame_rate_controller_->DidSwapBuffersComplete(); break; - case SchedulerStateMachine::ACTION_DRAW_FORCED: - DrawAndSwapForced(); + } + case SchedulerStateMachine::ACTION_DRAW_FORCED: { + frame_rate_controller_->DidSwapBuffers(); + ScheduledActionDrawAndSwapResult result = + client_->ScheduledActionDrawAndSwapForced(); + if (!result.did_swap) + frame_rate_controller_->DidSwapBuffersComplete(); break; + } case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION: client_->ScheduledActionBeginOutputSurfaceCreation(); break; @@ -209,8 +196,10 @@ void Scheduler::ProcessScheduledActions() { action = state_machine_.NextAction(); } - SetupNextBeginFrameIfNeeded(); - client_->DidAnticipatedDrawTimeChange(AnticipatedDrawTime()); + // Activate or deactivate the frame rate controller. + frame_rate_controller_->SetActive( + state_machine_.BeginFrameNeededByImplThread()); + client_->DidAnticipatedDrawTimeChange(frame_rate_controller_->NextTickTime()); } bool Scheduler::WillDrawIfNeeded() const { diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h index d8151fa..6ecd889 100644 --- a/cc/scheduler/scheduler.h +++ b/cc/scheduler/scheduler.h @@ -11,6 +11,7 @@ #include "base/memory/scoped_ptr.h" #include "base/time.h" #include "cc/base/cc_export.h" +#include "cc/scheduler/frame_rate_controller.h" #include "cc/scheduler/scheduler_settings.h" #include "cc/scheduler/scheduler_state_machine.h" #include "cc/trees/layer_tree_host.h" @@ -32,7 +33,6 @@ struct ScheduledActionDrawAndSwapResult { class SchedulerClient { public: - virtual void SetNeedsBeginFrameOnImplThread(bool enable) = 0; virtual void ScheduledActionSendBeginFrameToMainThread() = 0; virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapIfPossible() = 0; @@ -49,12 +49,14 @@ class SchedulerClient { virtual ~SchedulerClient() {} }; -class CC_EXPORT Scheduler { +class CC_EXPORT Scheduler : FrameRateControllerClient { public: static scoped_ptr<Scheduler> Create( SchedulerClient* client, + scoped_ptr<FrameRateController> frame_rate_controller, const SchedulerSettings& scheduler_settings) { - return make_scoped_ptr(new Scheduler(client, scheduler_settings)); + return make_scoped_ptr(new Scheduler( + client, frame_rate_controller.Pass(), scheduler_settings)); } virtual ~Scheduler(); @@ -84,6 +86,12 @@ class CC_EXPORT Scheduler { void FinishCommit(); void BeginFrameAbortedByMainThread(); + void SetMaxFramesPending(int max); + int MaxFramesPending() const; + int NumFramesPendingForTesting() const; + + void DidSwapBuffersComplete(); + void DidLoseOutputSurface(); void DidCreateAndInitializeOutputSurface(); bool HasInitializedOutputSurface() const { @@ -95,32 +103,28 @@ class CC_EXPORT Scheduler { bool WillDrawIfNeeded() const; + void SetTimebaseAndInterval(base::TimeTicks timebase, + base::TimeDelta interval); + base::TimeTicks AnticipatedDrawTime(); base::TimeTicks LastBeginFrameOnImplThreadTime(); - void BeginFrame(base::TimeTicks frame_time); + // FrameRateControllerClient implementation + virtual void BeginFrame(bool throttled) OVERRIDE; std::string StateAsStringForTesting() { return state_machine_.ToString(); } private: Scheduler(SchedulerClient* client, + scoped_ptr<FrameRateController> frame_rate_controller, const SchedulerSettings& scheduler_settings); - void SetupNextBeginFrameIfNeeded(); - void DrawAndSwapIfPossible(); - void DrawAndSwapForced(); void ProcessScheduledActions(); const SchedulerSettings settings_; SchedulerClient* client_; - - base::WeakPtrFactory<Scheduler> weak_factory_; - bool last_set_needs_begin_frame_; - bool has_pending_begin_frame_; - base::TimeTicks last_begin_frame_time_; - base::TimeDelta interval_; - + scoped_ptr<FrameRateController> frame_rate_controller_; SchedulerStateMachine state_machine_; bool inside_process_scheduled_actions_; diff --git a/cc/scheduler/scheduler_settings.cc b/cc/scheduler/scheduler_settings.cc index 4850a108..29c525b 100644 --- a/cc/scheduler/scheduler_settings.cc +++ b/cc/scheduler/scheduler_settings.cc @@ -8,8 +8,7 @@ namespace cc { SchedulerSettings::SchedulerSettings() : impl_side_painting(false), - timeout_and_draw_when_animation_checkerboards(true), - using_synchronous_renderer_compositor(false) {} + timeout_and_draw_when_animation_checkerboards(true) {} SchedulerSettings::~SchedulerSettings() {} diff --git a/cc/scheduler/scheduler_settings.h b/cc/scheduler/scheduler_settings.h index c06e28f..ebcfc6a 100644 --- a/cc/scheduler/scheduler_settings.h +++ b/cc/scheduler/scheduler_settings.h @@ -16,7 +16,6 @@ class CC_EXPORT SchedulerSettings { bool impl_side_painting; bool timeout_and_draw_when_animation_checkerboards; - bool using_synchronous_renderer_compositor; }; } // namespace cc diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc index fda6e6b..76f7bb1 100644 --- a/cc/scheduler/scheduler_state_machine.cc +++ b/cc/scheduler/scheduler_state_machine.cc @@ -4,7 +4,6 @@ #include "cc/scheduler/scheduler_state_machine.h" -#include "base/format_macros.h" #include "base/logging.h" #include "base/strings/stringprintf.h" @@ -13,7 +12,6 @@ namespace cc { SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) : settings_(settings), commit_state_(COMMIT_STATE_IDLE), - commit_count_(0), current_frame_number_(0), last_frame_number_where_draw_was_called_(-1), last_frame_number_where_tree_activation_attempted_(-1), @@ -44,7 +42,6 @@ std::string SchedulerStateMachine::ToString() { "settings_.impl_side_painting = %d; ", settings_.impl_side_painting); base::StringAppendF(&str, "commit_state_ = %d; ", commit_state_); - base::StringAppendF(&str, "commit_count_ = %d; ", commit_count_); base::StringAppendF( &str, "current_frame_number_ = %d; ", current_frame_number_); base::StringAppendF(&str, @@ -83,8 +80,6 @@ std::string SchedulerStateMachine::ToString() { main_thread_needs_layer_textures_); base::StringAppendF(&str, "inside_begin_frame_ = %d; ", inside_begin_frame_); - base::StringAppendF(&str, "last_frame_time_ = %"PRId64"; ", - (last_frame_time_ - base::TimeTicks()).InMilliseconds()); base::StringAppendF(&str, "visible_ = %d; ", visible_); base::StringAppendF(&str, "can_start_ = %d; ", can_start_); base::StringAppendF(&str, "can_draw_ = %d; ", can_draw_); @@ -278,7 +273,6 @@ void SchedulerStateMachine::UpdateState(Action action) { return; case ACTION_COMMIT: - commit_count_++; if (expect_immediate_begin_frame_for_main_thread_) commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW; else @@ -338,11 +332,6 @@ void SchedulerStateMachine::SetMainThreadNeedsLayerTextures() { } bool SchedulerStateMachine::BeginFrameNeededByImplThread() const { - // We should proactively request a BeginFrame if a commit is pending. - if (needs_commit_ || needs_forced_commit_ || - commit_state_ != COMMIT_STATE_IDLE) - return true; - // If we have a pending tree, need to keep getting notifications until // the tree is ready to be swapped. if (has_pending_tree_) @@ -366,10 +355,6 @@ void SchedulerStateMachine::DidEnterBeginFrame() { inside_begin_frame_ = true; } -void SchedulerStateMachine::SetFrameTime(base::TimeTicks frame_time) { - last_frame_time_ = frame_time; -} - void SchedulerStateMachine::DidLeaveBeginFrame() { current_frame_number_++; inside_begin_frame_ = false; diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h index 59e940c..1be0749 100644 --- a/cc/scheduler/scheduler_state_machine.h +++ b/cc/scheduler/scheduler_state_machine.h @@ -8,7 +8,6 @@ #include <string> #include "base/basictypes.h" -#include "base/time.h" #include "cc/base/cc_export.h" #include "cc/scheduler/scheduler_settings.h" @@ -79,9 +78,7 @@ class CC_EXPORT SchedulerStateMachine { // The scheduler will not draw more than once in a given BeginFrame // callback. void DidEnterBeginFrame(); - void SetFrameTime(base::TimeTicks frame_time); void DidLeaveBeginFrame(); - bool inside_begin_frame() const { return inside_begin_frame_; } // Indicates whether the LayerTreeHostImpl is visible. void SetVisible(bool visible); @@ -171,7 +168,6 @@ class CC_EXPORT SchedulerStateMachine { const SchedulerSettings settings_; CommitState commit_state_; - int commit_count_; int current_frame_number_; int last_frame_number_where_draw_was_called_; @@ -188,7 +184,6 @@ class CC_EXPORT SchedulerStateMachine { bool expect_immediate_begin_frame_for_main_thread_; bool main_thread_needs_layer_textures_; bool inside_begin_frame_; - base::TimeTicks last_frame_time_; bool visible_; bool can_start_; bool can_draw_; @@ -198,7 +193,6 @@ class CC_EXPORT SchedulerStateMachine { OutputSurfaceState output_surface_state_; bool did_create_and_initialize_first_output_surface_; - private: DISALLOW_COPY_AND_ASSIGN(SchedulerStateMachine); }; diff --git a/cc/scheduler/scheduler_state_machine_unittest.cc b/cc/scheduler/scheduler_state_machine_unittest.cc index c535212..001a229 100644 --- a/cc/scheduler/scheduler_state_machine_unittest.cc +++ b/cc/scheduler/scheduler_state_machine_unittest.cc @@ -98,7 +98,7 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); EXPECT_FALSE(state.NeedsCommit()); - EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + EXPECT_FALSE(state.BeginFrameNeededByImplThread()); } } diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc index bc7d575..68aecaf 100644 --- a/cc/scheduler/scheduler_unittest.cc +++ b/cc/scheduler/scheduler_unittest.cc @@ -30,11 +30,7 @@ namespace { class FakeSchedulerClient : public SchedulerClient { public: - FakeSchedulerClient() - : needs_begin_frame_(false) { - Reset(); - } - + FakeSchedulerClient() { Reset(); } void Reset() { actions_.clear(); states_.clear(); @@ -43,12 +39,15 @@ class FakeSchedulerClient : public SchedulerClient { num_draws_ = 0; } - Scheduler* CreateScheduler(const SchedulerSettings& settings) { - scheduler_ = Scheduler::Create(this, settings); + Scheduler* CreateScheduler( + scoped_ptr<FrameRateController> frame_rate_controller, + const SchedulerSettings& settings) { + scheduler_ = + Scheduler::Create(this, frame_rate_controller.Pass(), settings); return scheduler_.get(); } - bool needs_begin_frame() { return needs_begin_frame_; } + int num_draws() const { return num_draws_; } int num_actions_() const { return static_cast<int>(actions_.size()); } const char* Action(int i) const { return actions_[i]; } @@ -69,11 +68,6 @@ class FakeSchedulerClient : public SchedulerClient { } // Scheduler Implementation. - virtual void SetNeedsBeginFrameOnImplThread(bool enable) OVERRIDE { - actions_.push_back("SetNeedsBeginFrameOnImplThread"); - states_.push_back(scheduler_->StateAsStringForTesting()); - needs_begin_frame_ = enable; - } virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE { actions_.push_back("ScheduledActionSendBeginFrameToMainThread"); states_.push_back(scheduler_->StateAsStringForTesting()); @@ -117,7 +111,6 @@ class FakeSchedulerClient : public SchedulerClient { virtual void DidAnticipatedDrawTimeChange(base::TimeTicks) OVERRIDE {} protected: - bool needs_begin_frame_; bool draw_will_happen_; bool swap_will_happen_if_draw_happens_; int num_draws_; @@ -128,8 +121,11 @@ class FakeSchedulerClient : public SchedulerClient { TEST(SchedulerTest, InitializeOutputSurfaceDoesNotBeginFrame) { FakeSchedulerClient client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -142,8 +138,11 @@ TEST(SchedulerTest, InitializeOutputSurfaceDoesNotBeginFrame) { TEST(SchedulerTest, RequestCommit) { FakeSchedulerClient client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -154,29 +153,33 @@ TEST(SchedulerTest, RequestCommit) { // SetNeedsCommit should begin the frame. scheduler->SetNeedsCommit(); - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_SINGLE_ACTION("ScheduledActionSendBeginFrameToMainThread", client); + EXPECT_FALSE(time_source->Active()); client.Reset(); // FinishCommit should commit scheduler->FinishCommit(); EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); client.Reset(); - // BeginFrame should draw. - scheduler->BeginFrame(base::TimeTicks::Now()); - EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); - EXPECT_FALSE(client.needs_begin_frame()); + // Tick should draw. + time_source->Tick(); + EXPECT_SINGLE_ACTION("ScheduledActionDrawAndSwapIfPossible", client); + EXPECT_FALSE(time_source->Active()); client.Reset(); + + // Timer should be off. + EXPECT_FALSE(time_source->Active()); } TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) { FakeSchedulerClient client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -187,8 +190,7 @@ TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) { // SetNedsCommit should begin the frame. scheduler->SetNeedsCommit(); - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + EXPECT_SINGLE_ACTION("ScheduledActionSendBeginFrameToMainThread", client); client.Reset(); // Now SetNeedsCommit again. Calling here means we need a second frame. @@ -201,85 +203,78 @@ TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) { client.Reset(); // Tick should draw but then begin another frame. - scheduler->BeginFrame(base::TimeTicks::Now()); - EXPECT_TRUE(client.needs_begin_frame()); + time_source->Tick(); + EXPECT_FALSE(time_source->Active()); EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 2); client.Reset(); - - // Go back to quiescent state and verify we no longer request BeginFrames. - scheduler->FinishCommit(); - scheduler->BeginFrame(base::TimeTicks::Now()); - EXPECT_FALSE(client.needs_begin_frame()); } TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) { FakeSchedulerClient client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); - EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client); + EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client); client.Reset(); scheduler->DidCreateAndInitializeOutputSurface(); + scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); - client.Reset(); - scheduler->BeginFrame(base::TimeTicks::Now()); - EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + time_source->Tick(); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 1); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(client.needs_begin_frame()); - + EXPECT_FALSE(time_source->Active()); client.Reset(); + scheduler->SetMainThreadNeedsLayerTextures(); EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread", client, 0, - 3); + 2); // A commit was started by SetMainThreadNeedsLayerTextures(). - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 3); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 2, 3); - - // We should request a BeginFrame in anticipation of a draw. + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 2); client.Reset(); + scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); // No draw happens since the textures are acquired by the main thread. - client.Reset(); - scheduler->BeginFrame(base::TimeTicks::Now()); - EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); + time_source->Tick(); + EXPECT_EQ(0, client.num_actions_()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); - // Commit will release the texture. - client.Reset(); scheduler->FinishCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); + EXPECT_ACTION("ScheduledActionCommit", client, 0, 1); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); + client.Reset(); // Now we can draw again after the commit happens. - client.Reset(); - scheduler->BeginFrame(base::TimeTicks::Now()); - EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + time_source->Tick(); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 1); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(client.needs_begin_frame()); + EXPECT_FALSE(time_source->Active()); client.Reset(); } TEST(SchedulerTest, TextureAcquisitionCollision) { FakeSchedulerClient client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -290,22 +285,19 @@ TEST(SchedulerTest, TextureAcquisitionCollision) { scheduler->SetNeedsCommit(); scheduler->SetMainThreadNeedsLayerTextures(); - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 3); - EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 3); + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread", client, - 2, - 3); + 1, + 2); client.Reset(); - // Although the compositor cannot draw because textures are locked by main - // thread, we continue requesting SetNeedsBeginFrame in anticipation of the - // unlock. - EXPECT_TRUE(client.needs_begin_frame()); + // Compositor not scheduled to draw because textures are locked by main thread + EXPECT_FALSE(time_source->Active()); // Trigger the commit scheduler->FinishCommit(); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); client.Reset(); // Between commit and draw, texture acquisition for main thread delayed, @@ -315,7 +307,7 @@ TEST(SchedulerTest, TextureAcquisitionCollision) { client.Reset(); // Once compositor draw complete, the delayed texture acquisition fires. - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 3); EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread", client, @@ -327,8 +319,11 @@ TEST(SchedulerTest, TextureAcquisitionCollision) { TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) { FakeSchedulerClient client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -384,8 +379,11 @@ class SchedulerClientThatsetNeedsDrawInsideDraw : public FakeSchedulerClient { // 2. the scheduler drawing twice inside a single tick TEST(SchedulerTest, RequestRedrawInsideDraw) { SchedulerClientThatsetNeedsDrawInsideDraw client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -393,25 +391,28 @@ TEST(SchedulerTest, RequestRedrawInsideDraw) { scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); EXPECT_EQ(0, client.num_draws()); - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(1, client.num_draws()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(2, client.num_draws()); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(client.needs_begin_frame()); + EXPECT_FALSE(time_source->Active()); } // Test that requesting redraw inside a failed draw doesn't lose the request. TEST(SchedulerTest, RequestRedrawInsideFailedDraw) { SchedulerClientThatsetNeedsDrawInsideDraw client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -421,33 +422,33 @@ TEST(SchedulerTest, RequestRedrawInsideFailedDraw) { scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); EXPECT_EQ(0, client.num_draws()); // Fail the draw. - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(1, client.num_draws()); // We have a commit pending and the draw failed, and we didn't lose the redraw // request. EXPECT_TRUE(scheduler->CommitPending()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); // Fail the draw again. - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(2, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); // Draw successfully. client.SetDrawWillHappen(true); - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(3, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_FALSE(time_source->Active()); } class SchedulerClientThatsetNeedsCommitInsideDraw : public FakeSchedulerClient { @@ -476,8 +477,11 @@ class SchedulerClientThatsetNeedsCommitInsideDraw : public FakeSchedulerClient { // happen inside a ScheduledActionDrawAndSwap TEST(SchedulerTest, RequestCommitInsideDraw) { SchedulerClientThatsetNeedsCommitInsideDraw client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -486,26 +490,28 @@ TEST(SchedulerTest, RequestCommitInsideDraw) { scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); EXPECT_EQ(0, client.num_draws()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); + EXPECT_FALSE(time_source->Active()); EXPECT_EQ(1, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); - EXPECT_TRUE(client.needs_begin_frame()); scheduler->FinishCommit(); - scheduler->BeginFrame(base::TimeTicks::Now()); - EXPECT_EQ(2, client.num_draws());; + time_source->Tick(); + EXPECT_EQ(2, client.num_draws()); + EXPECT_FALSE(time_source->Active()); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(scheduler->CommitPending()); - EXPECT_FALSE(client.needs_begin_frame()); } // Tests that when a draw fails then the pending commit should not be dropped. TEST(SchedulerTest, RequestCommitInsideFailedDraw) { SchedulerClientThatsetNeedsDrawInsideDraw client; + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + make_scoped_ptr(new FrameRateController(time_source)), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -515,77 +521,128 @@ TEST(SchedulerTest, RequestCommitInsideFailedDraw) { scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); EXPECT_EQ(0, client.num_draws()); // Fail the draw. - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(1, client.num_draws()); // We have a commit pending and the draw failed, and we didn't lose the commit // request. EXPECT_TRUE(scheduler->CommitPending()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); // Fail the draw again. - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(2, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); // Draw successfully. client.SetDrawWillHappen(true); - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(3, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_FALSE(time_source->Active()); } TEST(SchedulerTest, NoSwapWhenDrawFails) { + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerClientThatsetNeedsCommitInsideDraw client; + scoped_ptr<FakeFrameRateController> controller( + new FakeFrameRateController(time_source)); + FakeFrameRateController* controller_ptr = controller.get(); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + controller.PassAs<FrameRateController>(), + default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); scheduler->DidCreateAndInitializeOutputSurface(); + EXPECT_EQ(0, controller_ptr->NumFramesPending()); + scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); EXPECT_EQ(0, client.num_draws()); // Draw successfully, this starts a new frame. - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(1, client.num_draws()); + EXPECT_EQ(1, controller_ptr->NumFramesPending()); + scheduler->DidSwapBuffersComplete(); + EXPECT_EQ(0, controller_ptr->NumFramesPending()); scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(client.needs_begin_frame()); + EXPECT_TRUE(time_source->Active()); // Fail to draw, this should not start a frame. client.SetDrawWillHappen(false); - scheduler->BeginFrame(base::TimeTicks::Now()); + time_source->Tick(); EXPECT_EQ(2, client.num_draws()); + EXPECT_EQ(0, controller_ptr->NumFramesPending()); } TEST(SchedulerTest, NoSwapWhenSwapFailsDuringForcedCommit) { + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); FakeSchedulerClient client; + scoped_ptr<FakeFrameRateController> controller( + new FakeFrameRateController(time_source)); + FakeFrameRateController* controller_ptr = controller.get(); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler( + controller.PassAs<FrameRateController>(), + default_scheduler_settings); + + EXPECT_EQ(0, controller_ptr->NumFramesPending()); // Tell the client that it will fail to swap. client.SetDrawWillHappen(true); client.SetSwapWillHappenIfDrawHappens(false); // Get the compositor to do a ScheduledActionDrawAndSwapForced. - scheduler->SetCanDraw(true); scheduler->SetNeedsRedraw(); scheduler->SetNeedsForcedRedraw(); EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapForced")); + + // We should not have told the frame rate controller that we began a frame. + EXPECT_EQ(0, controller_ptr->NumFramesPending()); +} + +TEST(SchedulerTest, RecreateOutputSurfaceClearsPendingDrawCount) { + scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); + FakeSchedulerClient client; + scoped_ptr<FakeFrameRateController> controller( + new FakeFrameRateController(time_source)); + FakeFrameRateController* controller_ptr = controller.get(); + SchedulerSettings default_scheduler_settings; + Scheduler* scheduler = client.CreateScheduler( + controller.PassAs<FrameRateController>(), + default_scheduler_settings); + + scheduler->SetCanStart(); + scheduler->SetVisible(true); + scheduler->SetCanDraw(true); + scheduler->DidCreateAndInitializeOutputSurface(); + + // Draw successfully, this starts a new frame. + scheduler->SetNeedsRedraw(); + time_source->Tick(); + EXPECT_EQ(1, controller_ptr->NumFramesPending()); + + scheduler->DidLoseOutputSurface(); + // Verifying that it's 1 so that we know that it's reset on recreate. + EXPECT_EQ(1, controller_ptr->NumFramesPending()); + + scheduler->DidCreateAndInitializeOutputSurface(); + EXPECT_EQ(0, controller_ptr->NumFramesPending()); } } // namespace diff --git a/cc/scheduler/vsync_time_source.cc b/cc/scheduler/vsync_time_source.cc new file mode 100644 index 0000000..206e2376 --- /dev/null +++ b/cc/scheduler/vsync_time_source.cc @@ -0,0 +1,76 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/scheduler/vsync_time_source.h" + +#include "base/logging.h" + +namespace cc { + +scoped_refptr<VSyncTimeSource> VSyncTimeSource::Create( + VSyncProvider* vsync_provider, NotificationDisableOption option) { + return make_scoped_refptr(new VSyncTimeSource(vsync_provider, option)); +} + +VSyncTimeSource::VSyncTimeSource( + VSyncProvider* vsync_provider, NotificationDisableOption option) + : active_(false), + notification_requested_(false), + vsync_provider_(vsync_provider), + client_(NULL), + disable_option_(option) {} + +VSyncTimeSource::~VSyncTimeSource() {} + +void VSyncTimeSource::SetClient(TimeSourceClient* client) { + client_ = client; +} + +void VSyncTimeSource::SetActive(bool active) { + if (active_ == active) + return; + active_ = active; + if (active_ && !notification_requested_) { + notification_requested_ = true; + vsync_provider_->RequestVSyncNotification(this); + } + if (!active_ && disable_option_ == DISABLE_SYNCHRONOUSLY) { + notification_requested_ = false; + vsync_provider_->RequestVSyncNotification(NULL); + } +} + +bool VSyncTimeSource::Active() const { + return active_; +} + +base::TimeTicks VSyncTimeSource::LastTickTime() { + return last_tick_time_; +} + +base::TimeTicks VSyncTimeSource::NextTickTime() { + return Active() ? last_tick_time_ + interval_ : base::TimeTicks(); +} + +void VSyncTimeSource::SetTimebaseAndInterval(base::TimeTicks, + base::TimeDelta interval) { + interval_ = interval; +} + +void VSyncTimeSource::DidVSync(base::TimeTicks frame_time) { + last_tick_time_ = frame_time; + if (disable_option_ == DISABLE_SYNCHRONOUSLY) { + DCHECK(active_); + } else if (!active_) { + if (notification_requested_) { + notification_requested_ = false; + vsync_provider_->RequestVSyncNotification(NULL); + } + return; + } + if (client_) + client_->OnTimerTick(); +} + +} // namespace cc diff --git a/cc/scheduler/vsync_time_source.h b/cc/scheduler/vsync_time_source.h new file mode 100644 index 0000000..4cd0441 --- /dev/null +++ b/cc/scheduler/vsync_time_source.h @@ -0,0 +1,78 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_SCHEDULER_VSYNC_TIME_SOURCE_H_ +#define CC_SCHEDULER_VSYNC_TIME_SOURCE_H_ + +#include "cc/base/cc_export.h" +#include "cc/scheduler/time_source.h" + +namespace cc { + +class CC_EXPORT VSyncClient { + public: + virtual void DidVSync(base::TimeTicks frame_time) = 0; + + protected: + virtual ~VSyncClient() {} +}; + +class VSyncProvider { + public: + // Request to be notified of future vsync events. The notifications will be + // delivered until they are disabled by calling this function with a null + // client. + virtual void RequestVSyncNotification(VSyncClient* client) = 0; + + protected: + virtual ~VSyncProvider() {} +}; + +// This timer implements a time source that is explicitly triggered by an +// external vsync signal. +class CC_EXPORT VSyncTimeSource : public TimeSource, public VSyncClient { + public: + enum NotificationDisableOption { + // The notification will be lazily disabled in the callback to ensure + // we get notified of the frame immediately following a quick on-off-on + // transition. + DISABLE_ON_NEXT_TICK, + DISABLE_SYNCHRONOUSLY + }; + + static scoped_refptr<VSyncTimeSource> Create( + VSyncProvider* vsync_provider, NotificationDisableOption option); + + // TimeSource implementation + virtual void SetClient(TimeSourceClient* client) OVERRIDE; + virtual void SetTimebaseAndInterval(base::TimeTicks timebase, + base::TimeDelta interval) OVERRIDE; + virtual void SetActive(bool active) OVERRIDE; + virtual bool Active() const OVERRIDE; + virtual base::TimeTicks LastTickTime() OVERRIDE; + virtual base::TimeTicks NextTickTime() OVERRIDE; + + // VSyncClient implementation + virtual void DidVSync(base::TimeTicks frame_time) OVERRIDE; + + protected: + explicit VSyncTimeSource(VSyncProvider* vsync_provider, + NotificationDisableOption option); + virtual ~VSyncTimeSource(); + + base::TimeTicks last_tick_time_; + base::TimeDelta interval_; + bool active_; + bool notification_requested_; + + VSyncProvider* vsync_provider_; + TimeSourceClient* client_; + NotificationDisableOption disable_option_; + + DISALLOW_COPY_AND_ASSIGN(VSyncTimeSource); +}; + +} // namespace cc + +#endif // CC_SCHEDULER_VSYNC_TIME_SOURCE_H_ diff --git a/cc/scheduler/vsync_time_source_unittest.cc b/cc/scheduler/vsync_time_source_unittest.cc new file mode 100644 index 0000000..4834630 --- /dev/null +++ b/cc/scheduler/vsync_time_source_unittest.cc @@ -0,0 +1,124 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/scheduler/vsync_time_source.h" + +#include "cc/test/scheduler_test_common.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace { + +class FakeVSyncProvider : public VSyncProvider { + public: + FakeVSyncProvider() : client_(NULL) {} + + // VSyncProvider implementation. + virtual void RequestVSyncNotification(VSyncClient* client) OVERRIDE { + client_ = client; + } + + bool IsVSyncNotificationEnabled() const { return client_ != NULL; } + + void Trigger(base::TimeTicks frame_time) { + if (client_) + client_->DidVSync(frame_time); + } + + private: + VSyncClient* client_; +}; + +class VSyncTimeSourceTest : public testing::Test { + public: + VSyncTimeSourceTest() + : timer_(VSyncTimeSource::Create( + &provider_, VSyncTimeSource::DISABLE_ON_NEXT_TICK)) { + timer_->SetClient(&client_); + } + + protected: + FakeTimeSourceClient client_; + FakeVSyncProvider provider_; + scoped_refptr<VSyncTimeSource> timer_; +}; + +TEST_F(VSyncTimeSourceTest, TaskPostedAndTickCalled) { + EXPECT_FALSE(provider_.IsVSyncNotificationEnabled()); + + timer_->SetActive(true); + EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); + + base::TimeTicks frame_time = base::TimeTicks::Now(); + provider_.Trigger(frame_time); + EXPECT_TRUE(client_.TickCalled()); + EXPECT_EQ(timer_->LastTickTime(), frame_time); +} + +TEST_F(VSyncTimeSourceTest, NotificationDisabledLazily) { + base::TimeTicks frame_time = base::TimeTicks::Now(); + + // Enable timer and trigger sync once. + timer_->SetActive(true); + EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); + provider_.Trigger(frame_time); + EXPECT_TRUE(client_.TickCalled()); + + // Disabling the timer should not disable vsync notification immediately. + client_.Reset(); + timer_->SetActive(false); + EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); + + // At the next vsync the notification is disabled, but the timer isn't ticked. + provider_.Trigger(frame_time); + EXPECT_FALSE(provider_.IsVSyncNotificationEnabled()); + EXPECT_FALSE(client_.TickCalled()); + + // The notification should not be disabled multiple times. + provider_.RequestVSyncNotification(timer_.get()); + provider_.Trigger(frame_time); + EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); + EXPECT_FALSE(client_.TickCalled()); +} + +TEST_F(VSyncTimeSourceTest, NotificationDisabledImmediatelyForSetting) { + timer_ = VSyncTimeSource::Create(&provider_, + VSyncTimeSource::DISABLE_SYNCHRONOUSLY); + timer_->SetClient(&client_); + base::TimeTicks frame_time = base::TimeTicks::Now(); + + // Enable timer and trigger sync once. + timer_->SetActive(true); + EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); + provider_.Trigger(frame_time); + EXPECT_TRUE(client_.TickCalled()); + + // Disable timer should disable vsync notification immediately. + timer_->SetActive(false); + EXPECT_FALSE(provider_.IsVSyncNotificationEnabled()); + + // Enable again and timer can be ticked again. + client_.Reset(); + timer_->SetActive(true); + EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); + provider_.Trigger(frame_time); + EXPECT_TRUE(client_.TickCalled()); +} + +TEST_F(VSyncTimeSourceTest, ValidNextTickTime) { + base::TimeTicks frame_time = base::TimeTicks::Now(); + base::TimeDelta interval = base::TimeDelta::FromSeconds(1); + + ASSERT_EQ(timer_->NextTickTime(), base::TimeTicks()); + + timer_->SetActive(true); + provider_.Trigger(frame_time); + ASSERT_EQ(timer_->NextTickTime(), frame_time); + + timer_->SetTimebaseAndInterval(frame_time, interval); + ASSERT_EQ(timer_->NextTickTime(), frame_time + interval); +} + +} // namespace +} // namespace cc diff --git a/cc/test/fake_layer_tree_host_impl_client.h b/cc/test/fake_layer_tree_host_impl_client.h index 58816cc..dad4014 100644 --- a/cc/test/fake_layer_tree_host_impl_client.h +++ b/cc/test/fake_layer_tree_host_impl_client.h @@ -17,6 +17,9 @@ 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 9ceadda..c3f3e1a 100644 --- a/cc/test/fake_output_surface.cc +++ b/cc/test/fake_output_surface.cc @@ -8,7 +8,6 @@ #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 { @@ -18,8 +17,7 @@ FakeOutputSurface::FakeOutputSurface( : OutputSurface(context3d.Pass()), num_sent_frames_(0), needs_begin_frame_(false), - forced_draw_to_software_device_(false), - fake_weak_ptr_factory_(this) { + forced_draw_to_software_device_(false) { if (delegated_rendering) { capabilities_.delegated_rendering = true; capabilities_.max_frames_pending = 1; @@ -30,8 +28,7 @@ FakeOutputSurface::FakeOutputSurface( scoped_ptr<SoftwareOutputDevice> software_device, bool delegated_rendering) : OutputSurface(software_device.Pass()), num_sent_frames_(0), - forced_draw_to_software_device_(false), - fake_weak_ptr_factory_(this) { + forced_draw_to_software_device_(false) { if (delegated_rendering) { capabilities_.delegated_rendering = true; capabilities_.max_frames_pending = 1; @@ -44,8 +41,7 @@ FakeOutputSurface::FakeOutputSurface( bool delegated_rendering) : OutputSurface(context3d.Pass(), software_device.Pass()), num_sent_frames_(0), - forced_draw_to_software_device_(false), - fake_weak_ptr_factory_(this) { + forced_draw_to_software_device_(false) { if (delegated_rendering) { capabilities_.delegated_rendering = true; capabilities_.max_frames_pending = 1; @@ -60,7 +56,6 @@ void FakeOutputSurface::SwapBuffers(CompositorFrame* frame) { frame->AssignTo(&last_sent_frame_); ++num_sent_frames_; PostSwapBuffersComplete(); - DidSwapBuffers(); } else { OutputSurface::SwapBuffers(frame); frame->AssignTo(&last_sent_frame_); @@ -70,23 +65,12 @@ 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::OnBeginFrame() { - OutputSurface::BeginFrame(base::TimeTicks::Now()); +void FakeOutputSurface::BeginFrame(base::TimeTicks frame_time) { + client_->BeginFrame(frame_time); } - 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 7bc1a4f..d54ffd3 100644 --- a/cc/test/fake_output_surface.h +++ b/cc/test/fake_output_surface.h @@ -74,6 +74,7 @@ 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; @@ -94,13 +95,10 @@ 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 b6f47ab..b8c5366 100644 --- a/cc/test/layer_tree_test.cc +++ b/cc/test/layer_tree_test.cc @@ -589,13 +589,9 @@ void LayerTreeTest::RunTest(bool threaded, } scoped_ptr<OutputSurface> LayerTreeTest::CreateOutputSurface() { - scoped_ptr<FakeOutputSurface> output_surface; if (delegating_renderer_) - output_surface = FakeOutputSurface::CreateDelegating3d(); - else - output_surface = FakeOutputSurface::Create3d(); - output_surface_ = output_surface.get(); - return output_surface.PassAs<OutputSurface>(); + return FakeOutputSurface::CreateDelegating3d().PassAs<OutputSurface>(); + return FakeOutputSurface::Create3d().PassAs<OutputSurface>(); } scoped_refptr<cc::ContextProvider> LayerTreeTest:: diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h index 2d67a44..856caaa 100644 --- a/cc/test/layer_tree_test.h +++ b/cc/test/layer_tree_test.h @@ -24,7 +24,6 @@ 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 { @@ -161,7 +160,6 @@ 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 8ccf47e..c1aa200 100644 --- a/cc/test/scheduler_test_common.cc +++ b/cc/test/scheduler_test_common.cc @@ -35,6 +35,16 @@ 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 8834e62..b3b2c2d 100644 --- a/cc/test/scheduler_test_common.h +++ b/cc/test/scheduler_test_common.h @@ -62,6 +62,36 @@ 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 2670bf8..a497421 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc @@ -1071,6 +1071,11 @@ 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); } @@ -1509,25 +1514,6 @@ 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 e2a303e..72c542d 100644 --- a/cc/trees/layer_tree_host_impl.h +++ b/cc/trees/layer_tree_host_impl.h @@ -54,6 +54,8 @@ 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; @@ -181,6 +183,8 @@ 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; @@ -201,12 +205,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 360e4e3..d643656 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc @@ -95,6 +95,8 @@ 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 e4d192e..b8a2e9c 100644 --- a/cc/trees/layer_tree_host_unittest.cc +++ b/cc/trees/layer_tree_host_unittest.cc @@ -2049,6 +2049,36 @@ 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: @@ -2223,10 +2253,32 @@ 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, @@ -2772,6 +2824,11 @@ 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: @@ -2838,9 +2895,6 @@ 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 29cc4f4..c448002 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* host_impl) OVERRIDE { + virtual void DrawLayersOnThread(LayerTreeHostImpl* tree_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 fd39ed0..8d2d2b7 100644 --- a/cc/trees/proxy.cc +++ b/cc/trees/proxy.cc @@ -78,8 +78,4 @@ Proxy::~Proxy() { DCHECK(IsMainThread()); } -std::string Proxy::SchedulerStateAsStringForTesting() { - return ""; -} - } // namespace cc diff --git a/cc/trees/proxy.h b/cc/trees/proxy.h index 4e6171c..aaab780 100644 --- a/cc/trees/proxy.h +++ b/cc/trees/proxy.h @@ -5,8 +5,6 @@ #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" @@ -100,7 +98,6 @@ 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 9d995ba..0c3946b 100644 --- a/cc/trees/single_thread_proxy.h +++ b/cc/trees/single_thread_proxy.h @@ -51,6 +51,8 @@ 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 020c255..53db2c2 100644 --- a/cc/trees/thread_proxy.cc +++ b/cc/trees/thread_proxy.cc @@ -4,8 +4,6 @@ #include "cc/trees/thread_proxy.h" -#include <string> - #include "base/auto_reset.h" #include "base/bind.h" #include "base/debug/trace_event.h" @@ -18,6 +16,7 @@ #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" @@ -33,23 +32,6 @@ 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( @@ -82,6 +64,7 @@ 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) { @@ -345,21 +328,36 @@ 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::SetNeedsBeginFrameOnImplThread(bool enable) { +void ThreadProxy::OnVSyncParametersChanged(base::TimeTicks timebase, + base::TimeDelta interval) { DCHECK(IsImplThread()); - TRACE_EVENT1("cc", "ThreadProxy::SetNeedsBeginFrameOnImplThread", - "enable", enable); - layer_tree_host_impl_->SetNeedsBeginFrame(enable); + TRACE_EVENT2("cc", + "ThreadProxy::OnVSyncParametersChanged", + "timebase", + (timebase - base::TimeTicks()).InMilliseconds(), + "interval", + interval.InMilliseconds()); + scheduler_on_impl_thread_->SetTimebaseAndInterval(timebase, interval); } void ThreadProxy::BeginFrameOnImplThread(base::TimeTicks frame_time) { DCHECK(IsImplThread()); - TRACE_EVENT0("cc", "ThreadProxy::BeginFrameOnImplThread"); - scheduler_on_impl_thread_->BeginFrame(frame_time); + 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); } void ThreadProxy::OnCanDrawStateChanged(bool can_draw) { @@ -1112,14 +1110,35 @@ 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_settings.using_synchronous_renderer_compositor = - settings.using_synchronous_renderer_compositor; - scheduler_on_impl_thread_ = Scheduler::Create(this, scheduler_settings); + scheduler_on_impl_thread_ = Scheduler::Create(this, + frame_rate_controller.Pass(), + scheduler_settings); scheduler_on_impl_thread_->SetVisible(layer_tree_host_impl_->visible()); impl_thread_weak_ptr_ = weak_factory_on_impl_thread_.GetWeakPtr(); @@ -1145,6 +1164,16 @@ 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(); } @@ -1189,6 +1218,7 @@ 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(); } @@ -1248,29 +1278,6 @@ 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 8013882..3cc905d 100644 --- a/cc/trees/thread_proxy.h +++ b/cc/trees/thread_proxy.h @@ -5,8 +5,6 @@ #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" @@ -14,6 +12,7 @@ #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" @@ -30,7 +29,8 @@ class Thread; class ThreadProxy : public Proxy, LayerTreeHostImplClient, SchedulerClient, - ResourceUpdateControllerClient { + ResourceUpdateControllerClient, + VSyncProvider { public: static scoped_ptr<Proxy> Create(LayerTreeHost* layer_tree_host, scoped_ptr<Thread> impl_thread); @@ -59,7 +59,6 @@ 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( @@ -67,7 +66,10 @@ class ThreadProxy : public Proxy, scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE; virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE; virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE; - virtual void BeginFrameOnImplThread(base::TimeTicks frame_time) 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; virtual void OnHasPendingTreeStateChanged(bool has_pending_tree) OVERRIDE; virtual void SetNeedsRedrawOnImplThread() OVERRIDE; @@ -89,7 +91,6 @@ class ThreadProxy : public Proxy, virtual void DidActivatePendingTree() OVERRIDE; // SchedulerClient implementation - virtual void SetNeedsBeginFrameOnImplThread(bool enable) OVERRIDE; virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE; virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapIfPossible() OVERRIDE; @@ -105,6 +106,17 @@ 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); @@ -131,10 +143,16 @@ class ThreadProxy : public Proxy, const RendererCapabilities& capabilities); // Called on impl thread. - struct ReadbackRequest; - struct CommitPendingRequest; - struct SchedulerStateRequest; - + struct ReadbackRequest { + CompletionEvent completion; + bool success; + void* pixels; + gfx::Rect rect; + }; + struct CommitPendingRequest { + CompletionEvent completion; + bool commit_pending; + }; void ForceCommitOnImplThread(CompletionEvent* completion); void StartCommitOnImplThread( CompletionEvent* completion, @@ -164,8 +182,6 @@ 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, @@ -234,6 +250,7 @@ class ThreadProxy : public Proxy, bool throttle_frame_production_; bool begin_frame_scheduling_enabled_; bool using_synchronous_renderer_compositor_; + VSyncClient* vsync_client_; bool inside_draw_; |