diff options
author | brianderson@chromium.org <brianderson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-18 12:32:35 +0000 |
---|---|---|
committer | brianderson@chromium.org <brianderson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-18 12:32:35 +0000 |
commit | 049fc7a730faa0dc77df259cc63a7907bc87d922 (patch) | |
tree | f78d949c8bf641542c1225129ed49da901a333a5 | |
parent | f56c7874661fc602acb108ad923b9e7118eba058 (diff) | |
download | chromium_src-049fc7a730faa0dc77df259cc63a7907bc87d922.zip chromium_src-049fc7a730faa0dc77df259cc63a7907bc87d922.tar.gz chromium_src-049fc7a730faa0dc77df259cc63a7907bc87d922.tar.bz2 |
cc: Emulate BeginFrame in OutputSurfaces that don't support it natively
Relanding again. This time, the proactive SetNeedsBeginFrame is
broken out explicitly with logic to workaround crbug 249806 and
to avoid expensive redraws with the synchronous renderer.
This includes two small fixes for the original version of this
patch that broke software compositing and WebView.
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
BUG=249806
TBR=nduca@chromium.org,sievers@chromium.org,kbr@chromium.org
Review URL: https://chromiumcodereview.appspot.com/17353002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@206955 0039d316-1c4b-4281-b951-d872f2087c98
42 files changed, 783 insertions, 853 deletions
@@ -321,8 +321,6 @@ 'scheduler/texture_uploader.cc', 'scheduler/texture_uploader.h', 'scheduler/time_source.h', - 'scheduler/vsync_time_source.cc', - 'scheduler/vsync_time_source.h', 'trees/damage_tracker.cc', 'trees/damage_tracker.h', 'trees/layer_sorter.cc', diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp index d945513..9f4097c 100644 --- a/cc/cc_tests.gyp +++ b/cc/cc_tests.gyp @@ -68,7 +68,6 @@ 'scheduler/scheduler_state_machine_unittest.cc', 'scheduler/scheduler_unittest.cc', 'scheduler/texture_uploader_unittest.cc', - 'scheduler/vsync_time_source_unittest.cc', 'test/fake_web_graphics_context_3d_unittest.cc', 'trees/damage_tracker_unittest.cc', 'trees/layer_sorter_unittest.cc', diff --git a/cc/output/output_surface.cc b/cc/output/output_surface.cc index 1a956e4..610aebf 100644 --- a/cc/output/output_surface.cc +++ b/cc/output/output_surface.cc @@ -9,12 +9,14 @@ #include <vector> #include "base/bind.h" +#include "base/debug/trace_event.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "cc/output/compositor_frame.h" #include "cc/output/output_surface_client.h" +#include "cc/scheduler/delay_based_time_source.h" #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/khronos/GLES2/gl2ext.h" @@ -32,7 +34,7 @@ class OutputSurfaceCallbacks WebGraphicsSwapBuffersCompleteCallbackCHROMIUM, public WebKit::WebGraphicsContext3D::WebGraphicsContextLostCallback { public: - explicit OutputSurfaceCallbacks(OutputSurfaceClient* client) + explicit OutputSurfaceCallbacks(OutputSurface* client) : client_(client) { DCHECK(client_); } @@ -44,50 +46,156 @@ class OutputSurfaceCallbacks virtual void onContextLost() { client_->DidLoseOutputSurface(); } private: - OutputSurfaceClient* client_; + OutputSurface* client_; }; OutputSurface::OutputSurface( scoped_ptr<WebKit::WebGraphicsContext3D> context3d) - : client_(NULL), - context3d_(context3d.Pass()), + : context3d_(context3d.Pass()), has_gl_discard_backbuffer_(false), has_swap_buffers_complete_callback_(false), device_scale_factor_(-1), - weak_ptr_factory_(this) { + weak_ptr_factory_(this), + max_frames_pending_(0), + pending_swap_buffers_(0), + begin_frame_pending_(false), + client_(NULL) { } OutputSurface::OutputSurface( scoped_ptr<cc::SoftwareOutputDevice> software_device) - : client_(NULL), - software_device_(software_device.Pass()), + : software_device_(software_device.Pass()), has_gl_discard_backbuffer_(false), has_swap_buffers_complete_callback_(false), device_scale_factor_(-1), - weak_ptr_factory_(this) { + weak_ptr_factory_(this), + max_frames_pending_(0), + pending_swap_buffers_(0), + begin_frame_pending_(false), + client_(NULL) { } OutputSurface::OutputSurface( scoped_ptr<WebKit::WebGraphicsContext3D> context3d, scoped_ptr<cc::SoftwareOutputDevice> software_device) - : client_(NULL), - context3d_(context3d.Pass()), + : context3d_(context3d.Pass()), software_device_(software_device.Pass()), has_gl_discard_backbuffer_(false), has_swap_buffers_complete_callback_(false), device_scale_factor_(-1), - weak_ptr_factory_(this) { + weak_ptr_factory_(this), + max_frames_pending_(0), + pending_swap_buffers_(0), + begin_frame_pending_(false), + client_(NULL) { +} + +void OutputSurface::InitializeBeginFrameEmulation( + Thread* thread, + bool throttle_frame_production, + base::TimeDelta interval) { + if (throttle_frame_production){ + frame_rate_controller_.reset( + new FrameRateController( + DelayBasedTimeSource::Create(interval, thread))); + } else { + frame_rate_controller_.reset(new FrameRateController(thread)); + } + + frame_rate_controller_->SetClient(this); + frame_rate_controller_->SetMaxSwapsPending(max_frames_pending_); + + // The new frame rate controller will consume the swap acks of the old + // frame rate controller, so we set that expectation up here. + for (int i = 0; i < pending_swap_buffers_; i++) + frame_rate_controller_->DidSwapBuffers(); +} + +void OutputSurface::SetMaxFramesPending(int max_frames_pending) { + if (frame_rate_controller_) + frame_rate_controller_->SetMaxSwapsPending(max_frames_pending); + max_frames_pending_ = max_frames_pending; +} + +void OutputSurface::OnVSyncParametersChanged(base::TimeTicks timebase, + base::TimeDelta interval) { + TRACE_EVENT2("cc", "OutputSurface::OnVSyncParametersChanged", + "timebase", (timebase - base::TimeTicks()).InSecondsF(), + "interval", interval.InSecondsF()); + if (frame_rate_controller_) + frame_rate_controller_->SetTimebaseAndInterval(timebase, interval); +} + +void OutputSurface::FrameRateControllerTick(bool throttled) { + DCHECK(frame_rate_controller_); + if (!throttled) + BeginFrame(frame_rate_controller_->LastTickTime()); +} + +// Forwarded to OutputSurfaceClient +void OutputSurface::SetNeedsRedrawRect(gfx::Rect damage_rect) { + TRACE_EVENT0("cc", "OutputSurface::SetNeedsRedrawRect"); + client_->SetNeedsRedrawRect(damage_rect); +} + +void OutputSurface::SetNeedsBeginFrame(bool enable) { + TRACE_EVENT1("cc", "OutputSurface::SetNeedsBeginFrame", "enable", enable); + begin_frame_pending_ = false; + if (frame_rate_controller_) + frame_rate_controller_->SetActive(enable); +} + +void OutputSurface::BeginFrame(base::TimeTicks frame_time) { + TRACE_EVENT2("cc", "OutputSurface::BeginFrame", + "begin_frame_pending_", begin_frame_pending_, + "pending_swap_buffers_", pending_swap_buffers_); + if (begin_frame_pending_ || + (pending_swap_buffers_ >= max_frames_pending_ && max_frames_pending_ > 0)) + return; + begin_frame_pending_ = true; + client_->BeginFrame(frame_time); +} + +void OutputSurface::DidSwapBuffers() { + begin_frame_pending_ = false; + pending_swap_buffers_++; + TRACE_EVENT1("cc", "OutputSurface::DidSwapBuffers", + "pending_swap_buffers_", pending_swap_buffers_); + if (frame_rate_controller_) + frame_rate_controller_->DidSwapBuffers(); +} + +void OutputSurface::OnSwapBuffersComplete(const CompositorFrameAck* ack) { + pending_swap_buffers_--; + TRACE_EVENT1("cc", "OutputSurface::OnSwapBuffersComplete", + "pending_swap_buffers_", pending_swap_buffers_); + client_->OnSwapBuffersComplete(ack); + if (frame_rate_controller_) + frame_rate_controller_->DidSwapBuffersComplete(); +} + +void OutputSurface::DidLoseOutputSurface() { + TRACE_EVENT0("cc", "OutputSurface::DidLoseOutputSurface"); + begin_frame_pending_ = false; + pending_swap_buffers_ = 0; + client_->DidLoseOutputSurface(); +} + +void OutputSurface::SetExternalDrawConstraints(const gfx::Transform& transform, + gfx::Rect viewport) { + client_->SetExternalDrawConstraints(transform, viewport); } OutputSurface::~OutputSurface() { + if (frame_rate_controller_) + frame_rate_controller_->SetActive(false); } bool OutputSurface::ForcedDrawToSoftwareDevice() const { return false; } -bool OutputSurface::BindToClient( - cc::OutputSurfaceClient* client) { +bool OutputSurface::BindToClient(cc::OutputSurfaceClient* client) { DCHECK(client); client_ = client; bool success = true; @@ -143,7 +251,7 @@ void OutputSurface::SetContext3D( context3d_ = context3d.Pass(); - callbacks_.reset(new OutputSurfaceCallbacks(client_)); + callbacks_.reset(new OutputSurfaceCallbacks(this)); context3d_->setSwapBuffersCompleteCallbackCHROMIUM(callbacks_.get()); context3d_->setContextLostCallback(callbacks_.get()); } @@ -186,6 +294,7 @@ void OutputSurface::BindFramebuffer() { void OutputSurface::SwapBuffers(cc::CompositorFrame* frame) { if (frame->software_frame_data) { PostSwapBuffersComplete(); + DidSwapBuffers(); return; } @@ -207,20 +316,16 @@ void OutputSurface::SwapBuffers(cc::CompositorFrame* frame) { if (!has_swap_buffers_complete_callback_) PostSwapBuffersComplete(); + + DidSwapBuffers(); } void OutputSurface::PostSwapBuffersComplete() { base::MessageLoop::current()->PostTask( FROM_HERE, - base::Bind(&OutputSurface::SwapBuffersComplete, - weak_ptr_factory_.GetWeakPtr())); -} - -void OutputSurface::SwapBuffersComplete() { - if (!client_) - return; - - client_->OnSwapBuffersComplete(NULL); + base::Bind(&OutputSurface::OnSwapBuffersComplete, + weak_ptr_factory_.GetWeakPtr(), + static_cast<CompositorFrameAck*>(NULL))); } } // namespace cc diff --git a/cc/output/output_surface.h b/cc/output/output_surface.h index d263d3a..8f782d7 100644 --- a/cc/output/output_surface.h +++ b/cc/output/output_surface.h @@ -12,6 +12,7 @@ #include "cc/base/cc_export.h" #include "cc/output/context_provider.h" #include "cc/output/software_output_device.h" +#include "cc/scheduler/frame_rate_controller.h" #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" namespace ui { struct LatencyInfo; } @@ -19,13 +20,16 @@ namespace ui { struct LatencyInfo; } namespace gfx { class Rect; class Size; +class Transform; } namespace cc { class CompositorFrame; +class CompositorFrameAck; class OutputSurfaceClient; class OutputSurfaceCallbacks; +class Thread; // Represents the output surface for a compositor. The compositor owns // and manages its destruction. Its lifetime is: @@ -34,7 +38,7 @@ class OutputSurfaceCallbacks; // From here on, it will only be used on the compositor thread. // 3. If the 3D context is lost, then the compositor will delete the output // surface (on the compositor thread) and go back to step 1. -class CC_EXPORT OutputSurface { +class CC_EXPORT OutputSurface : public FrameRateControllerClient { public: explicit OutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D> context3d); @@ -83,6 +87,13 @@ class CC_EXPORT OutputSurface { // thread. virtual bool BindToClient(OutputSurfaceClient* client); + void InitializeBeginFrameEmulation( + Thread* thread, + bool throttle_frame_production, + base::TimeDelta interval); + + void SetMaxFramesPending(int max_frames_pending); + virtual void EnsureBackbuffer(); virtual void DiscardBackbuffer(); @@ -103,7 +114,7 @@ class CC_EXPORT OutputSurface { // Requests a BeginFrame notification from the output surface. The // notification will be delivered by calling // OutputSurfaceClient::BeginFrame until the callback is disabled. - virtual void SetNeedsBeginFrame(bool enable) {} + virtual void SetNeedsBeginFrame(bool enable); protected: // Synchronously initialize context3d and enter hardware mode. @@ -116,7 +127,6 @@ class CC_EXPORT OutputSurface { void PostSwapBuffersComplete(); - OutputSurfaceClient* client_; struct cc::OutputSurface::Capabilities capabilities_; scoped_ptr<OutputSurfaceCallbacks> callbacks_; scoped_ptr<WebKit::WebGraphicsContext3D> context3d_; @@ -125,12 +135,34 @@ class CC_EXPORT OutputSurface { bool has_swap_buffers_complete_callback_; gfx::Size surface_size_; float device_scale_factor_; + base::WeakPtrFactory<OutputSurface> weak_ptr_factory_; + + // The FrameRateController is deprecated. + // Platforms should move to native BeginFrames instead. + void OnVSyncParametersChanged(base::TimeTicks timebase, + base::TimeDelta interval); + virtual void FrameRateControllerTick(bool throttled) OVERRIDE; + scoped_ptr<FrameRateController> frame_rate_controller_; + int max_frames_pending_; + int pending_swap_buffers_; + bool begin_frame_pending_; + + // Forwarded to OutputSurfaceClient but threaded through OutputSurface + // first so OutputSurface has a chance to update the FrameRateController + bool HasClient() { return !!client_; } + void SetNeedsRedrawRect(gfx::Rect damage_rect); + void BeginFrame(base::TimeTicks frame_time); + void DidSwapBuffers(); + void OnSwapBuffersComplete(const CompositorFrameAck* ack); + void DidLoseOutputSurface(); + void SetExternalDrawConstraints(const gfx::Transform& transform, + gfx::Rect viewport); private: - void SetContext3D(scoped_ptr<WebKit::WebGraphicsContext3D> context3d); - void SwapBuffersComplete(); + OutputSurfaceClient* client_; + friend class OutputSurfaceCallbacks; - base::WeakPtrFactory<OutputSurface> weak_ptr_factory_; + void SetContext3D(scoped_ptr<WebKit::WebGraphicsContext3D> context3d); DISALLOW_COPY_AND_ASSIGN(OutputSurface); }; diff --git a/cc/output/output_surface_client.h b/cc/output/output_surface_client.h index f5999c3..d727629 100644 --- a/cc/output/output_surface_client.h +++ b/cc/output/output_surface_client.h @@ -27,8 +27,6 @@ class CC_EXPORT OutputSurfaceClient { virtual bool DeferredInitialize( scoped_refptr<ContextProvider> offscreen_context_provider) = 0; virtual void SetNeedsRedrawRect(gfx::Rect damage_rect) = 0; - virtual void OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) = 0; virtual void BeginFrame(base::TimeTicks frame_time) = 0; virtual void OnSwapBuffersComplete(const CompositorFrameAck* ack) = 0; virtual void DidLoseOutputSurface() = 0; diff --git a/cc/output/output_surface_unittest.cc b/cc/output/output_surface_unittest.cc index ebc018f..7dcb1cc 100644 --- a/cc/output/output_surface_unittest.cc +++ b/cc/output/output_surface_unittest.cc @@ -5,6 +5,7 @@ #include "cc/output/output_surface.h" #include "cc/output/output_surface_client.h" #include "cc/output/software_output_device.h" +#include "cc/test/scheduler_test_common.h" #include "cc/test/test_web_graphics_context_3d.h" #include "gpu/GLES2/gl2extchromium.h" #include "testing/gtest/include/gtest/gtest.h" @@ -25,19 +26,43 @@ class TestOutputSurface : public OutputSurface { scoped_ptr<cc::SoftwareOutputDevice> software_device) : OutputSurface(context3d.Pass(), software_device.Pass()) {} - OutputSurfaceClient* client() { return client_; } - bool InitializeNewContext3D( scoped_ptr<WebKit::WebGraphicsContext3D> new_context3d) { return InitializeAndSetContext3D(new_context3d.Pass(), scoped_refptr<ContextProvider>()); } + + bool HasClientForTesting() { + return HasClient(); + } + + void OnVSyncParametersChangedForTesting(base::TimeTicks timebase, + base::TimeDelta interval) { + OnVSyncParametersChanged(timebase, interval); + } + + void BeginFrameForTesting(base::TimeTicks frame_time) { + BeginFrame(frame_time); + } + + void DidSwapBuffersForTesting() { + DidSwapBuffers(); + } + + int pending_swap_buffers() { + return pending_swap_buffers_; + } + + void OnSwapBuffersCompleteForTesting() { + OnSwapBuffersComplete(NULL); + } }; class FakeOutputSurfaceClient : public OutputSurfaceClient { public: FakeOutputSurfaceClient() - : deferred_initialize_result_(true), + : begin_frame_count_(0), + deferred_initialize_result_(true), deferred_initialize_called_(false), did_lose_output_surface_called_(false) {} @@ -47,9 +72,9 @@ class FakeOutputSurfaceClient : public OutputSurfaceClient { return deferred_initialize_result_; } virtual void SetNeedsRedrawRect(gfx::Rect damage_rect) OVERRIDE {} - virtual void OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) OVERRIDE {} - virtual void BeginFrame(base::TimeTicks frame_time) OVERRIDE {} + virtual void BeginFrame(base::TimeTicks frame_time) OVERRIDE { + begin_frame_count_++; + } virtual void OnSwapBuffersComplete(const CompositorFrameAck* ack) OVERRIDE {} virtual void DidLoseOutputSurface() OVERRIDE { did_lose_output_surface_called_ = true; @@ -57,6 +82,10 @@ class FakeOutputSurfaceClient : public OutputSurfaceClient { virtual void SetExternalDrawConstraints(const gfx::Transform& transform, gfx::Rect viewport) OVERRIDE {} + int begin_frame_count() { + return begin_frame_count_; + } + void set_deferred_initialize_result(bool result) { deferred_initialize_result_ = result; } @@ -70,6 +99,7 @@ class FakeOutputSurfaceClient : public OutputSurfaceClient { } private: + int begin_frame_count_; bool deferred_initialize_result_; bool deferred_initialize_called_; bool did_lose_output_surface_called_; @@ -81,11 +111,11 @@ TEST(OutputSurfaceTest, ClientPointerIndicatesBindToClientSuccess) { TestOutputSurface output_surface( context3d.PassAs<WebKit::WebGraphicsContext3D>()); - EXPECT_EQ(NULL, output_surface.client()); + EXPECT_FALSE(output_surface.HasClientForTesting()); FakeOutputSurfaceClient client; EXPECT_TRUE(output_surface.BindToClient(&client)); - EXPECT_EQ(&client, output_surface.client()); + EXPECT_TRUE(output_surface.HasClientForTesting()); EXPECT_FALSE(client.deferred_initialize_called()); // Verify DidLoseOutputSurface callback is hooked up correctly. @@ -104,11 +134,11 @@ TEST(OutputSurfaceTest, ClientPointerIndicatesBindToClientFailure) { TestOutputSurface output_surface( context3d.PassAs<WebKit::WebGraphicsContext3D>()); - EXPECT_EQ(NULL, output_surface.client()); + EXPECT_FALSE(output_surface.HasClientForTesting()); FakeOutputSurfaceClient client; EXPECT_FALSE(output_surface.BindToClient(&client)); - EXPECT_EQ(NULL, output_surface.client()); + EXPECT_FALSE(output_surface.HasClientForTesting()); } class InitializeNewContext3D : public ::testing::Test { @@ -121,13 +151,13 @@ class InitializeNewContext3D : public ::testing::Test { protected: void BindOutputSurface() { EXPECT_TRUE(output_surface_.BindToClient(&client_)); - EXPECT_EQ(&client_, output_surface_.client()); + EXPECT_TRUE(output_surface_.HasClientForTesting()); } void InitializeNewContextExpectFail() { EXPECT_FALSE(output_surface_.InitializeNewContext3D( context3d_.PassAs<WebKit::WebGraphicsContext3D>())); - EXPECT_EQ(&client_, output_surface_.client()); + EXPECT_TRUE(output_surface_.HasClientForTesting()); EXPECT_FALSE(output_surface_.context3d()); EXPECT_TRUE(output_surface_.software_device()); @@ -164,5 +194,110 @@ TEST_F(InitializeNewContext3D, ClientDeferredInitializeFails) { InitializeNewContextExpectFail(); } +TEST(OutputSurfaceTest, BeginFrameEmulation) { + scoped_ptr<TestWebGraphicsContext3D> context3d = + TestWebGraphicsContext3D::Create(); + + TestOutputSurface output_surface( + context3d.PassAs<WebKit::WebGraphicsContext3D>()); + EXPECT_FALSE(output_surface.HasClientForTesting()); + + FakeOutputSurfaceClient client; + EXPECT_TRUE(output_surface.BindToClient(&client)); + EXPECT_TRUE(output_surface.HasClientForTesting()); + EXPECT_FALSE(client.deferred_initialize_called()); + + // Initialize BeginFrame emulation + FakeThread impl_thread; + bool throttle_frame_production = true; + const base::TimeDelta display_refresh_interval = + base::TimeDelta::FromMicroseconds(16666); + + output_surface.InitializeBeginFrameEmulation( + &impl_thread, + throttle_frame_production, + display_refresh_interval); + + output_surface.SetMaxFramesPending(2); + + // We should start off with 0 BeginFrames + EXPECT_EQ(client.begin_frame_count(), 0); + EXPECT_EQ(output_surface.pending_swap_buffers(), 0); + + // We should not have a pending task until a BeginFrame has been requested. + EXPECT_FALSE(impl_thread.HasPendingTask()); + output_surface.SetNeedsBeginFrame(true); + EXPECT_TRUE(impl_thread.HasPendingTask()); + + // BeginFrame should be called on the first tick. + impl_thread.RunPendingTask(); + EXPECT_EQ(client.begin_frame_count(), 1); + EXPECT_EQ(output_surface.pending_swap_buffers(), 0); + + // BeginFrame should not be called when there is a pending BeginFrame. + impl_thread.RunPendingTask(); + EXPECT_EQ(client.begin_frame_count(), 1); + EXPECT_EQ(output_surface.pending_swap_buffers(), 0); + + // DidSwapBuffers should clear the pending BeginFrame. + output_surface.DidSwapBuffersForTesting(); + EXPECT_EQ(client.begin_frame_count(), 1); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + impl_thread.RunPendingTask(); + EXPECT_EQ(client.begin_frame_count(), 2); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + + // BeginFrame should be throttled by pending swap buffers. + output_surface.DidSwapBuffersForTesting(); + EXPECT_EQ(client.begin_frame_count(), 2); + EXPECT_EQ(output_surface.pending_swap_buffers(), 2); + impl_thread.RunPendingTask(); + EXPECT_EQ(client.begin_frame_count(), 2); + EXPECT_EQ(output_surface.pending_swap_buffers(), 2); + + // SwapAck should decrement pending swap buffers and unblock BeginFrame again. + output_surface.OnSwapBuffersCompleteForTesting(); + EXPECT_EQ(client.begin_frame_count(), 2); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + impl_thread.RunPendingTask(); + EXPECT_EQ(client.begin_frame_count(), 3); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + + // Calling SetNeedsBeginFrame again indicates a swap did not occur but + // the client still wants another BeginFrame. + output_surface.SetNeedsBeginFrame(true); + impl_thread.RunPendingTask(); + EXPECT_EQ(client.begin_frame_count(), 4); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + + // Disabling SetNeedsBeginFrame should prevent further BeginFrames. + output_surface.SetNeedsBeginFrame(false); + impl_thread.RunPendingTask(); + EXPECT_FALSE(impl_thread.HasPendingTask()); + EXPECT_EQ(client.begin_frame_count(), 4); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + + // Optimistically injected BeginFrames without a SetNeedsBeginFrame should be + // allowed. + output_surface.BeginFrameForTesting(base::TimeTicks::Now()); + EXPECT_EQ(client.begin_frame_count(), 5); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + + // Optimistically injected BeginFrames without a SetNeedsBeginFrame should + // still be throttled by pending begin frames however. + output_surface.BeginFrameForTesting(base::TimeTicks::Now()); + EXPECT_EQ(client.begin_frame_count(), 5); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + + // Optimistically injected BeginFrames without a SetNeedsBeginFrame should + // also be throttled by pending swap buffers. + output_surface.DidSwapBuffersForTesting(); + EXPECT_EQ(client.begin_frame_count(), 5); + EXPECT_EQ(output_surface.pending_swap_buffers(), 2); + output_surface.BeginFrameForTesting(base::TimeTicks::Now()); + EXPECT_EQ(client.begin_frame_count(), 5); + EXPECT_EQ(output_surface.pending_swap_buffers(), 2); +} + } // namespace } // namespace cc diff --git a/cc/scheduler/frame_rate_controller.cc b/cc/scheduler/frame_rate_controller.cc index b4f1272..90cf0d7 100644 --- a/cc/scheduler/frame_rate_controller.cc +++ b/cc/scheduler/frame_rate_controller.cc @@ -21,7 +21,9 @@ class FrameRateControllerTimeSourceAdapter : public TimeSourceClient { } virtual ~FrameRateControllerTimeSourceAdapter() {} - virtual void OnTimerTick() OVERRIDE { frame_rate_controller_->OnTimerTick(); } + virtual void OnTimerTick() OVERRIDE { + frame_rate_controller_->OnTimerTick(); + } private: explicit FrameRateControllerTimeSourceAdapter( @@ -34,7 +36,7 @@ class FrameRateControllerTimeSourceAdapter : public TimeSourceClient { FrameRateController::FrameRateController(scoped_refptr<TimeSource> timer) : client_(NULL), num_frames_pending_(0), - max_frames_pending_(0), + max_swaps_pending_(0), time_source_(timer), active_(false), is_time_source_throttling_(true), @@ -48,7 +50,7 @@ FrameRateController::FrameRateController(scoped_refptr<TimeSource> timer) FrameRateController::FrameRateController(Thread* thread) : client_(NULL), num_frames_pending_(0), - max_frames_pending_(0), + max_swaps_pending_(0), active_(false), is_time_source_throttling_(false), weak_factory_(this), @@ -75,9 +77,9 @@ void FrameRateController::SetActive(bool active) { } } -void FrameRateController::SetMaxFramesPending(int max_frames_pending) { - DCHECK_GE(max_frames_pending, 0); - max_frames_pending_ = max_frames_pending; +void FrameRateController::SetMaxSwapsPending(int max_swaps_pending) { + DCHECK_GE(max_swaps_pending, 0); + max_swaps_pending_ = max_swaps_pending; } void FrameRateController::SetTimebaseAndInterval(base::TimeTicks timebase, @@ -87,15 +89,17 @@ void FrameRateController::SetTimebaseAndInterval(base::TimeTicks timebase, } void FrameRateController::OnTimerTick() { + TRACE_EVENT0("cc", "FrameRateController::OnTimerTick"); DCHECK(active_); // Check if we have too many frames in flight. bool throttled = - max_frames_pending_ && num_frames_pending_ >= max_frames_pending_; + max_swaps_pending_ && num_frames_pending_ >= max_swaps_pending_; TRACE_COUNTER_ID1("cc", "ThrottledCompositor", thread_, throttled); - if (client_) - client_->BeginFrame(throttled); + if (client_) { + client_->FrameRateControllerTick(throttled); + } if (!is_time_source_throttling_ && !throttled) PostManualTick(); @@ -108,7 +112,9 @@ void FrameRateController::PostManualTick() { } } -void FrameRateController::ManualTick() { OnTimerTick(); } +void FrameRateController::ManualTick() { + OnTimerTick(); +} void FrameRateController::DidSwapBuffers() { num_frames_pending_++; diff --git a/cc/scheduler/frame_rate_controller.h b/cc/scheduler/frame_rate_controller.h index 070c26a..70964e2 100644 --- a/cc/scheduler/frame_rate_controller.h +++ b/cc/scheduler/frame_rate_controller.h @@ -15,18 +15,21 @@ namespace cc { class Thread; class TimeSource; +class FrameRateController; class CC_EXPORT FrameRateControllerClient { - public: - // Throttled is true when we have a maximum number of frames pending. - virtual void BeginFrame(bool throttled) = 0; - protected: virtual ~FrameRateControllerClient() {} + + public: + // Throttled is true when we have a maximum number of frames pending. + virtual void FrameRateControllerTick(bool throttled) = 0; }; class FrameRateControllerTimeSourceAdapter; +// The FrameRateController is used in cases where we self-tick (i.e. BeginFrame +// is not sent by a parent compositor. class CC_EXPORT FrameRateController { public: enum { @@ -41,6 +44,7 @@ class CC_EXPORT FrameRateController { void SetClient(FrameRateControllerClient* client) { client_ = client; } void SetActive(bool active); + bool IsActive() { return active_; } // Use the following methods to adjust target frame rate. // @@ -51,9 +55,9 @@ class CC_EXPORT FrameRateController { void DidSwapBuffers(); void DidSwapBuffersComplete(); void DidAbortAllPendingFrames(); - void SetMaxFramesPending(int max_frames_pending); // 0 for unlimited. - int MaxFramesPending() const { return max_frames_pending_; } - int NumFramesPendingForTesting() const { return num_frames_pending_; } + void SetMaxSwapsPending(int max_swaps_pending); // 0 for unlimited. + int MaxSwapsPending() const { return max_swaps_pending_; } + int NumSwapsPendingForTesting() const { return num_frames_pending_; } // This returns null for unthrottled frame-rate. base::TimeTicks NextTickTime(); @@ -73,7 +77,7 @@ class CC_EXPORT FrameRateController { FrameRateControllerClient* client_; int num_frames_pending_; - int max_frames_pending_; + int max_swaps_pending_; scoped_refptr<TimeSource> time_source_; scoped_ptr<FrameRateControllerTimeSourceAdapter> time_source_client_adapter_; bool active_; @@ -83,6 +87,7 @@ class CC_EXPORT FrameRateController { base::WeakPtrFactory<FrameRateController> weak_factory_; Thread* thread_; + private: DISALLOW_COPY_AND_ASSIGN(FrameRateController); }; diff --git a/cc/scheduler/frame_rate_controller_unittest.cc b/cc/scheduler/frame_rate_controller_unittest.cc index 183f576..3fcf4a7 100644 --- a/cc/scheduler/frame_rate_controller_unittest.cc +++ b/cc/scheduler/frame_rate_controller_unittest.cc @@ -17,7 +17,7 @@ class FakeFrameRateControllerClient : public cc::FrameRateControllerClient { void Reset() { began_frame_ = false; } bool BeganFrame() const { return began_frame_; } - virtual void BeginFrame(bool throttled) OVERRIDE { + virtual void FrameRateControllerTick(bool throttled) OVERRIDE { began_frame_ = !throttled; } @@ -74,7 +74,7 @@ TEST(FrameRateControllerTest, TestFrameThrottling_TwoFramesInFlight) { controller.SetClient(&client); controller.SetActive(true); - controller.SetMaxFramesPending(2); + controller.SetMaxSwapsPending(2); base::TimeTicks elapsed; // Muck around with time a bit @@ -132,7 +132,7 @@ TEST(FrameRateControllerTest, TestFrameThrottling_Unthrottled) { FrameRateController controller(&thread); controller.SetClient(&client); - controller.SetMaxFramesPending(2); + controller.SetMaxSwapsPending(2); // SetActive triggers 1st frame, make sure the BeginFrame callback // is called diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc index d308b0d..9b9ac87 100644 --- a/cc/scheduler/scheduler.cc +++ b/cc/scheduler/scheduler.cc @@ -7,23 +7,29 @@ #include "base/auto_reset.h" #include "base/debug/trace_event.h" #include "base/logging.h" +#include "cc/base/thread.h" namespace cc { Scheduler::Scheduler(SchedulerClient* client, - scoped_ptr<FrameRateController> frame_rate_controller, const SchedulerSettings& scheduler_settings) : settings_(scheduler_settings), client_(client), - frame_rate_controller_(frame_rate_controller.Pass()), + weak_factory_(this), + last_set_needs_begin_frame_(false), + has_pending_begin_frame_(false), + last_begin_frame_time_(base::TimeTicks()), + // TODO(brianderson): Pass with BeginFrame in the near future. + interval_(base::TimeDelta::FromMicroseconds(16666)), state_machine_(scheduler_settings), inside_process_scheduled_actions_(false) { DCHECK(client_); - frame_rate_controller_->SetClient(this); - DCHECK(!state_machine_.BeginFrameNeededByImplThread()); + DCHECK(!state_machine_.BeginFrameNeededToDrawByImplThread()); } -Scheduler::~Scheduler() { frame_rate_controller_->SetActive(false); } +Scheduler::~Scheduler() { + client_->SetNeedsBeginFrameOnImplThread(false); +} void Scheduler::SetCanStart() { state_machine_.SetCanStart(); @@ -88,23 +94,6 @@ void Scheduler::BeginFrameAbortedByMainThread() { ProcessScheduledActions(); } -void Scheduler::SetMaxFramesPending(int max_frames_pending) { - frame_rate_controller_->SetMaxFramesPending(max_frames_pending); -} - -int Scheduler::MaxFramesPending() const { - return frame_rate_controller_->MaxFramesPending(); -} - -int Scheduler::NumFramesPendingForTesting() const { - return frame_rate_controller_->NumFramesPendingForTesting(); -} - -void Scheduler::DidSwapBuffersComplete() { - TRACE_EVENT0("cc", "Scheduler::DidSwapBuffersComplete"); - frame_rate_controller_->DidSwapBuffersComplete(); -} - void Scheduler::DidLoseOutputSurface() { TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface"); state_machine_.DidLoseOutputSurface(); @@ -113,31 +102,91 @@ 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; + safe_to_expect_begin_frame_ = false; ProcessScheduledActions(); } -void Scheduler::SetTimebaseAndInterval(base::TimeTicks timebase, - base::TimeDelta interval) { - frame_rate_controller_->SetTimebaseAndInterval(timebase, interval); -} - base::TimeTicks Scheduler::AnticipatedDrawTime() { - return frame_rate_controller_->NextTickTime(); + TRACE_EVENT0("cc", "Scheduler::AnticipatedDrawTime"); + if (!last_set_needs_begin_frame_) + return base::TimeTicks(); + + base::TimeTicks now = base::TimeTicks::Now(); + int64 intervals = ((now - last_begin_frame_time_) / interval_) + 1; + return last_begin_frame_time_ + (interval_ * intervals); } base::TimeTicks Scheduler::LastBeginFrameOnImplThreadTime() { - return frame_rate_controller_->LastTickTime(); + return last_begin_frame_time_; +} + +void Scheduler::SetupNextBeginFrameIfNeeded() { + bool needs_begin_frame_to_draw = + state_machine_.BeginFrameNeededToDrawByImplThread(); + // We want to avoid proactive begin frames with the synchronous compositor + // because every SetNeedsBeginFrame will force a redraw. + bool proactive_begin_frame_wanted = + state_machine_.ProactiveBeginFrameWantedByImplThread() && + !settings_.using_synchronous_renderer_compositor; + bool needs_begin_frame = needs_begin_frame_to_draw || + proactive_begin_frame_wanted; + bool immediate_disables_needed = + settings_.using_synchronous_renderer_compositor; + + if (needs_begin_frame_to_draw) + safe_to_expect_begin_frame_ = true; + + // 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. + if ((needs_begin_frame || + state_machine_.inside_begin_frame() || + immediate_disables_needed) && + (needs_begin_frame != last_set_needs_begin_frame_)) { + has_pending_begin_frame_ = false; + client_->SetNeedsBeginFrameOnImplThread(needs_begin_frame); + if (safe_to_expect_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; + } } -void Scheduler::BeginFrame(bool throttled) { - TRACE_EVENT1("cc", "Scheduler::BeginFrame", "throttled", throttled); - if (!throttled) - state_machine_.DidEnterBeginFrame(); +void Scheduler::BeginFrame(base::TimeTicks frame_time) { + TRACE_EVENT0("cc", "Scheduler::BeginFrame"); + DCHECK(!has_pending_begin_frame_); + has_pending_begin_frame_ = true; + last_begin_frame_time_ = frame_time; + safe_to_expect_begin_frame_ = true; + state_machine_.DidEnterBeginFrame(); + state_machine_.SetFrameTime(frame_time); ProcessScheduledActions(); - if (!throttled) - state_machine_.DidLeaveBeginFrame(); + state_machine_.DidLeaveBeginFrame(); +} + +void Scheduler::DrawAndSwapIfPossible() { + ScheduledActionDrawAndSwapResult result = + client_->ScheduledActionDrawAndSwapIfPossible(); + state_machine_.DidDrawIfPossibleCompleted(result.did_draw); + if (result.did_swap) + has_pending_begin_frame_ = false; +} + +void Scheduler::DrawAndSwapForced() { + ScheduledActionDrawAndSwapResult result = + client_->ScheduledActionDrawAndSwapForced(); + if (result.did_swap) + has_pending_begin_frame_ = false; } void Scheduler::ProcessScheduledActions() { @@ -151,9 +200,6 @@ void Scheduler::ProcessScheduledActions() { SchedulerStateMachine::Action action = state_machine_.NextAction(); while (action != SchedulerStateMachine::ACTION_NONE) { state_machine_.UpdateState(action); - TRACE_EVENT1( - "cc", "Scheduler::ProcessScheduledActions()", "action", action); - switch (action) { case SchedulerStateMachine::ACTION_NONE: break; @@ -169,23 +215,12 @@ void Scheduler::ProcessScheduledActions() { case SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED: client_->ScheduledActionActivatePendingTreeIfNeeded(); break; - case SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE: { - frame_rate_controller_->DidSwapBuffers(); - ScheduledActionDrawAndSwapResult result = - client_->ScheduledActionDrawAndSwapIfPossible(); - state_machine_.DidDrawIfPossibleCompleted(result.did_draw); - if (!result.did_swap) - frame_rate_controller_->DidSwapBuffersComplete(); + case SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE: + DrawAndSwapIfPossible(); break; - } - case SchedulerStateMachine::ACTION_DRAW_FORCED: { - frame_rate_controller_->DidSwapBuffers(); - ScheduledActionDrawAndSwapResult result = - client_->ScheduledActionDrawAndSwapForced(); - if (!result.did_swap) - frame_rate_controller_->DidSwapBuffersComplete(); + case SchedulerStateMachine::ACTION_DRAW_FORCED: + DrawAndSwapForced(); break; - } case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION: client_->ScheduledActionBeginOutputSurfaceCreation(); break; @@ -196,10 +231,8 @@ void Scheduler::ProcessScheduledActions() { action = state_machine_.NextAction(); } - // Activate or deactivate the frame rate controller. - frame_rate_controller_->SetActive( - state_machine_.BeginFrameNeededByImplThread()); - client_->DidAnticipatedDrawTimeChange(frame_rate_controller_->NextTickTime()); + SetupNextBeginFrameIfNeeded(); + client_->DidAnticipatedDrawTimeChange(AnticipatedDrawTime()); } bool Scheduler::WillDrawIfNeeded() const { diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h index 1f4c16e..facd7a2 100644 --- a/cc/scheduler/scheduler.h +++ b/cc/scheduler/scheduler.h @@ -11,7 +11,6 @@ #include "base/memory/scoped_ptr.h" #include "base/time.h" #include "cc/base/cc_export.h" -#include "cc/scheduler/frame_rate_controller.h" #include "cc/scheduler/scheduler_settings.h" #include "cc/scheduler/scheduler_state_machine.h" #include "cc/trees/layer_tree_host.h" @@ -33,6 +32,7 @@ struct ScheduledActionDrawAndSwapResult { class SchedulerClient { public: + virtual void SetNeedsBeginFrameOnImplThread(bool enable) = 0; virtual void ScheduledActionSendBeginFrameToMainThread() = 0; virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapIfPossible() = 0; @@ -50,14 +50,12 @@ class SchedulerClient { virtual ~SchedulerClient() {} }; -class CC_EXPORT Scheduler : FrameRateControllerClient { +class CC_EXPORT Scheduler { public: static scoped_ptr<Scheduler> Create( SchedulerClient* client, - scoped_ptr<FrameRateController> frame_rate_controller, const SchedulerSettings& scheduler_settings) { - return make_scoped_ptr(new Scheduler( - client, frame_rate_controller.Pass(), scheduler_settings)); + return make_scoped_ptr(new Scheduler(client, scheduler_settings)); } virtual ~Scheduler(); @@ -87,12 +85,6 @@ class CC_EXPORT Scheduler : FrameRateControllerClient { void FinishCommit(); void BeginFrameAbortedByMainThread(); - void SetMaxFramesPending(int max); - int MaxFramesPending() const; - int NumFramesPendingForTesting() const; - - void DidSwapBuffersComplete(); - void DidLoseOutputSurface(); void DidCreateAndInitializeOutputSurface(); bool HasInitializedOutputSurface() const { @@ -104,28 +96,35 @@ class CC_EXPORT Scheduler : FrameRateControllerClient { bool WillDrawIfNeeded() const; - void SetTimebaseAndInterval(base::TimeTicks timebase, - base::TimeDelta interval); - base::TimeTicks AnticipatedDrawTime(); base::TimeTicks LastBeginFrameOnImplThreadTime(); - // FrameRateControllerClient implementation - virtual void BeginFrame(bool throttled) OVERRIDE; + void BeginFrame(base::TimeTicks frame_time); std::string StateAsStringForTesting() { return state_machine_.ToString(); } private: Scheduler(SchedulerClient* client, - scoped_ptr<FrameRateController> frame_rate_controller, const SchedulerSettings& scheduler_settings); + void SetupNextBeginFrameIfNeeded(); + void DrawAndSwapIfPossible(); + void DrawAndSwapForced(); void ProcessScheduledActions(); const SchedulerSettings settings_; SchedulerClient* client_; - scoped_ptr<FrameRateController> frame_rate_controller_; + + base::WeakPtrFactory<Scheduler> weak_factory_; + bool last_set_needs_begin_frame_; + bool has_pending_begin_frame_; + // TODO(brianderson): crbug.com/249806 : Remove safe_to_expect_begin_frame_ + // workaround. + bool safe_to_expect_begin_frame_; + base::TimeTicks last_begin_frame_time_; + base::TimeDelta interval_; + SchedulerStateMachine state_machine_; bool inside_process_scheduled_actions_; diff --git a/cc/scheduler/scheduler_settings.cc b/cc/scheduler/scheduler_settings.cc index 29c525b..4850a108 100644 --- a/cc/scheduler/scheduler_settings.cc +++ b/cc/scheduler/scheduler_settings.cc @@ -8,7 +8,8 @@ namespace cc { SchedulerSettings::SchedulerSettings() : impl_side_painting(false), - timeout_and_draw_when_animation_checkerboards(true) {} + timeout_and_draw_when_animation_checkerboards(true), + using_synchronous_renderer_compositor(false) {} SchedulerSettings::~SchedulerSettings() {} diff --git a/cc/scheduler/scheduler_settings.h b/cc/scheduler/scheduler_settings.h index ebcfc6a..c06e28f 100644 --- a/cc/scheduler/scheduler_settings.h +++ b/cc/scheduler/scheduler_settings.h @@ -16,6 +16,7 @@ class CC_EXPORT SchedulerSettings { bool impl_side_painting; bool timeout_and_draw_when_animation_checkerboards; + bool using_synchronous_renderer_compositor; }; } // namespace cc diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc index 76f7bb1..7b8faf7 100644 --- a/cc/scheduler/scheduler_state_machine.cc +++ b/cc/scheduler/scheduler_state_machine.cc @@ -4,6 +4,7 @@ #include "cc/scheduler/scheduler_state_machine.h" +#include "base/format_macros.h" #include "base/logging.h" #include "base/strings/stringprintf.h" @@ -12,6 +13,7 @@ namespace cc { SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings) : settings_(settings), commit_state_(COMMIT_STATE_IDLE), + commit_count_(0), current_frame_number_(0), last_frame_number_where_draw_was_called_(-1), last_frame_number_where_tree_activation_attempted_(-1), @@ -42,6 +44,7 @@ std::string SchedulerStateMachine::ToString() { "settings_.impl_side_painting = %d; ", settings_.impl_side_painting); base::StringAppendF(&str, "commit_state_ = %d; ", commit_state_); + base::StringAppendF(&str, "commit_count_ = %d; ", commit_count_); base::StringAppendF( &str, "current_frame_number_ = %d; ", current_frame_number_); base::StringAppendF(&str, @@ -80,6 +83,8 @@ std::string SchedulerStateMachine::ToString() { main_thread_needs_layer_textures_); base::StringAppendF(&str, "inside_begin_frame_ = %d; ", inside_begin_frame_); + base::StringAppendF(&str, "last_frame_time_ = %"PRId64"; ", + (last_frame_time_ - base::TimeTicks()).InMilliseconds()); base::StringAppendF(&str, "visible_ = %d; ", visible_); base::StringAppendF(&str, "can_start_ = %d; ", can_start_); base::StringAppendF(&str, "can_draw_ = %d; ", can_draw_); @@ -164,7 +169,7 @@ bool SchedulerStateMachine::ShouldAcquireLayerTexturesForMainThread() const { // impl thread is not even scheduled to draw. Guards against deadlocking. if (!ScheduledToDraw()) return true; - if (!BeginFrameNeededByImplThread()) + if (!BeginFrameNeededToDrawByImplThread()) return true; return false; } @@ -273,6 +278,7 @@ void SchedulerStateMachine::UpdateState(Action action) { return; case ACTION_COMMIT: + commit_count_++; if (expect_immediate_begin_frame_for_main_thread_) commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW; else @@ -331,9 +337,10 @@ void SchedulerStateMachine::SetMainThreadNeedsLayerTextures() { main_thread_needs_layer_textures_ = true; } -bool SchedulerStateMachine::BeginFrameNeededByImplThread() const { +bool SchedulerStateMachine::BeginFrameNeededToDrawByImplThread() const { // If we have a pending tree, need to keep getting notifications until // the tree is ready to be swapped. + // TODO(brianderson): This should be moved to ProactiveBeginFrameNeeded... if (has_pending_tree_) return true; @@ -351,10 +358,23 @@ bool SchedulerStateMachine::BeginFrameNeededByImplThread() const { output_surface_state_ == OUTPUT_SURFACE_ACTIVE; } +bool SchedulerStateMachine::ProactiveBeginFrameWantedByImplThread() 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; + + return false; +} + void SchedulerStateMachine::DidEnterBeginFrame() { inside_begin_frame_ = true; } +void SchedulerStateMachine::SetFrameTime(base::TimeTicks frame_time) { + last_frame_time_ = frame_time; +} + void SchedulerStateMachine::DidLeaveBeginFrame() { current_frame_number_++; inside_begin_frame_ = false; diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h index 1be0749..79c6a6e 100644 --- a/cc/scheduler/scheduler_state_machine.h +++ b/cc/scheduler/scheduler_state_machine.h @@ -8,6 +8,7 @@ #include <string> #include "base/basictypes.h" +#include "base/time.h" #include "cc/base/cc_export.h" #include "cc/scheduler/scheduler_settings.h" @@ -72,13 +73,16 @@ class CC_EXPORT SchedulerStateMachine { // Indicates whether the main thread needs a begin frame callback in order to // make progress. - bool BeginFrameNeededByImplThread() const; + bool BeginFrameNeededToDrawByImplThread() const; + bool ProactiveBeginFrameWantedByImplThread() const; // Indicates that the system has entered and left a BeginFrame callback. // The scheduler will not draw more than once in a given BeginFrame // callback. void DidEnterBeginFrame(); + void SetFrameTime(base::TimeTicks frame_time); void DidLeaveBeginFrame(); + bool inside_begin_frame() const { return inside_begin_frame_; } // Indicates whether the LayerTreeHostImpl is visible. void SetVisible(bool visible); @@ -168,6 +172,7 @@ class CC_EXPORT SchedulerStateMachine { const SchedulerSettings settings_; CommitState commit_state_; + int commit_count_; int current_frame_number_; int last_frame_number_where_draw_was_called_; @@ -184,6 +189,7 @@ class CC_EXPORT SchedulerStateMachine { bool expect_immediate_begin_frame_for_main_thread_; bool main_thread_needs_layer_textures_; bool inside_begin_frame_; + base::TimeTicks last_frame_time_; bool visible_; bool can_start_; bool can_draw_; @@ -193,6 +199,7 @@ class CC_EXPORT SchedulerStateMachine { OutputSurfaceState output_surface_state_; bool did_create_and_initialize_first_output_surface_; + private: DISALLOW_COPY_AND_ASSIGN(SchedulerStateMachine); }; diff --git a/cc/scheduler/scheduler_state_machine_unittest.cc b/cc/scheduler/scheduler_state_machine_unittest.cc index 001a229..7d7c820 100644 --- a/cc/scheduler/scheduler_state_machine_unittest.cc +++ b/cc/scheduler/scheduler_state_machine_unittest.cc @@ -51,11 +51,11 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { state.SetNeedsRedraw(false); state.SetVisible(true); - EXPECT_FALSE(state.BeginFrameNeededByImplThread()); + EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); state.DidLeaveBeginFrame(); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); - EXPECT_FALSE(state.BeginFrameNeededByImplThread()); + EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); } @@ -67,11 +67,11 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { state.SetNeedsRedraw(false); state.SetVisible(true); - EXPECT_FALSE(state.BeginFrameNeededByImplThread()); + EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); state.DidLeaveBeginFrame(); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); - EXPECT_FALSE(state.BeginFrameNeededByImplThread()); + EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(); EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction()); } @@ -83,7 +83,7 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { state.SetCanStart(); state.SetNeedsRedraw(false); state.SetVisible(true); - EXPECT_FALSE(state.BeginFrameNeededByImplThread()); + EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); } // Begin the frame, make sure needs_commit and commit_state update correctly. @@ -98,7 +98,7 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) { EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.CommitState()); EXPECT_FALSE(state.NeedsCommit()); - EXPECT_FALSE(state.BeginFrameNeededByImplThread()); + EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); } } @@ -108,7 +108,7 @@ TEST(SchedulerStateMachineTest, TestSetForcedRedrawDoesNotSetsNormalRedraw) { state.SetCanDraw(true); state.SetNeedsForcedRedraw(); EXPECT_FALSE(state.RedrawPending()); - EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); } TEST(SchedulerStateMachineTest, @@ -122,7 +122,7 @@ TEST(SchedulerStateMachineTest, state.SetCanDraw(true); state.SetNeedsRedraw(); EXPECT_TRUE(state.RedrawPending()); - EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(); // We're drawing now. @@ -154,7 +154,7 @@ TEST(SchedulerStateMachineTest, state.SetCanDraw(true); state.SetNeedsRedraw(); EXPECT_TRUE(state.RedrawPending()); - EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(); // We're drawing now. @@ -196,7 +196,7 @@ TEST(SchedulerStateMachineTest, // Then initiate a draw. state.SetNeedsRedraw(); - EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); EXPECT_TRUE(state.RedrawPending()); @@ -239,7 +239,7 @@ TEST(SchedulerStateMachineTest, // Then initiate a draw. state.SetNeedsRedraw(); - EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); EXPECT_TRUE(state.RedrawPending()); @@ -294,7 +294,7 @@ TEST(SchedulerStateMachineTest, // Then initiate a draw. state.SetNeedsRedraw(); - EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); EXPECT_TRUE(state.RedrawPending()); @@ -330,7 +330,7 @@ TEST(SchedulerStateMachineTest, // Start a draw. state.SetNeedsRedraw(); - EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); EXPECT_TRUE(state.RedrawPending()); @@ -346,7 +346,7 @@ TEST(SchedulerStateMachineTest, state.NextAction()); state.DidLeaveBeginFrame(); - EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(); // We should try to draw again in the next begin frame on the impl thread. @@ -362,7 +362,7 @@ TEST(SchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame) { state.SetVisible(true); state.SetCanDraw(true); state.SetNeedsRedraw(); - EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); @@ -375,13 +375,13 @@ TEST(SchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame) { // Move to another frame. This should now draw. state.DidDrawIfPossibleCompleted(true); state.DidLeaveBeginFrame(); - EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); state.DidEnterBeginFrame(); EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction()); state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); state.DidDrawIfPossibleCompleted(true); - EXPECT_FALSE(state.BeginFrameNeededByImplThread()); + EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread()); } TEST(SchedulerStateMachineTest, TestNextActionDrawsOnBeginFrame) { @@ -448,12 +448,12 @@ TEST(SchedulerStateMachineTest, TestNextActionDrawsOnBeginFrame) { } // Case 1: needs_commit=false. - EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); EXPECT_EQ(expected_action, state.NextAction()); // Case 2: needs_commit=true. state.SetNeedsCommit(); - EXPECT_TRUE(state.BeginFrameNeededByImplThread()); + EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread()); EXPECT_EQ(expected_action, state.NextAction()); } } diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc index 61cdbe9..e0ddbcc 100644 --- a/cc/scheduler/scheduler_unittest.cc +++ b/cc/scheduler/scheduler_unittest.cc @@ -30,7 +30,11 @@ namespace { class FakeSchedulerClient : public SchedulerClient { public: - FakeSchedulerClient() { Reset(); } + FakeSchedulerClient() + : needs_begin_frame_(false) { + Reset(); + } + void Reset() { actions_.clear(); states_.clear(); @@ -39,15 +43,12 @@ class FakeSchedulerClient : public SchedulerClient { num_draws_ = 0; } - Scheduler* CreateScheduler( - scoped_ptr<FrameRateController> frame_rate_controller, - const SchedulerSettings& settings) { - scheduler_ = - Scheduler::Create(this, frame_rate_controller.Pass(), settings); + Scheduler* CreateScheduler(const SchedulerSettings& settings) { + scheduler_ = Scheduler::Create(this, settings); return scheduler_.get(); } - + bool needs_begin_frame() { return needs_begin_frame_; } int num_draws() const { return num_draws_; } int num_actions_() const { return static_cast<int>(actions_.size()); } const char* Action(int i) const { return actions_[i]; } @@ -68,6 +69,11 @@ class FakeSchedulerClient : public SchedulerClient { } // Scheduler Implementation. + virtual void SetNeedsBeginFrameOnImplThread(bool enable) OVERRIDE { + actions_.push_back("SetNeedsBeginFrameOnImplThread"); + states_.push_back(scheduler_->StateAsStringForTesting()); + needs_begin_frame_ = enable; + } virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE { actions_.push_back("ScheduledActionSendBeginFrameToMainThread"); states_.push_back(scheduler_->StateAsStringForTesting()); @@ -114,6 +120,7 @@ class FakeSchedulerClient : public SchedulerClient { } protected: + bool needs_begin_frame_; bool draw_will_happen_; bool swap_will_happen_if_draw_happens_; int num_draws_; @@ -124,11 +131,8 @@ class FakeSchedulerClient : public SchedulerClient { TEST(SchedulerTest, InitializeOutputSurfaceDoesNotBeginFrame) { FakeSchedulerClient client; - scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -141,11 +145,8 @@ TEST(SchedulerTest, InitializeOutputSurfaceDoesNotBeginFrame) { TEST(SchedulerTest, RequestCommit) { FakeSchedulerClient client; - scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -156,33 +157,30 @@ TEST(SchedulerTest, RequestCommit) { // SetNeedsCommit should begin the frame. scheduler->SetNeedsCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionSendBeginFrameToMainThread", client); - EXPECT_FALSE(time_source->Active()); + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + EXPECT_TRUE(client.needs_begin_frame()); client.Reset(); // FinishCommit should commit scheduler->FinishCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); - EXPECT_TRUE(time_source->Active()); + EXPECT_ACTION("ScheduledActionCommit", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + EXPECT_TRUE(client.needs_begin_frame()); client.Reset(); - // Tick should draw. - time_source->Tick(); - EXPECT_SINGLE_ACTION("ScheduledActionDrawAndSwapIfPossible", client); - EXPECT_FALSE(time_source->Active()); + // BeginFrame should draw. + scheduler->BeginFrame(base::TimeTicks::Now()); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + EXPECT_FALSE(client.needs_begin_frame()); client.Reset(); - - // Timer should be off. - EXPECT_FALSE(time_source->Active()); } TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) { FakeSchedulerClient client; - scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -193,91 +191,102 @@ TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) { // SetNedsCommit should begin the frame. scheduler->SetNeedsCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionSendBeginFrameToMainThread", client); + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); client.Reset(); // Now SetNeedsCommit again. Calling here means we need a second frame. scheduler->SetNeedsCommit(); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 0, 1); + client.Reset(); // Since another commit is needed, FinishCommit should commit, // then begin another frame. scheduler->FinishCommit(); - EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); + EXPECT_ACTION("ScheduledActionCommit", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); client.Reset(); // Tick should draw but then begin another frame. - time_source->Tick(); - EXPECT_FALSE(time_source->Active()); + scheduler->BeginFrame(base::TimeTicks::Now()); + EXPECT_TRUE(client.needs_begin_frame()); EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 2); client.Reset(); + + // Go back to quiescent state and verify we no longer request BeginFrames. + scheduler->FinishCommit(); + scheduler->BeginFrame(base::TimeTicks::Now()); + EXPECT_FALSE(client.needs_begin_frame()); } TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) { FakeSchedulerClient client; - scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); - EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client); + client.Reset(); scheduler->DidCreateAndInitializeOutputSurface(); - scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); + EXPECT_TRUE(client.needs_begin_frame()); - time_source->Tick(); - EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 1); - EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(time_source->Active()); client.Reset(); + scheduler->BeginFrame(base::TimeTicks::Now()); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); + EXPECT_FALSE(scheduler->RedrawPending()); + EXPECT_FALSE(client.needs_begin_frame()); + client.Reset(); scheduler->SetMainThreadNeedsLayerTextures(); EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread", client, 0, - 2); + 3); // A commit was started by SetMainThreadNeedsLayerTextures(). - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 2); - client.Reset(); + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 3); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 2, 3); + // We should request a BeginFrame in anticipation of a draw. + client.Reset(); scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); // No draw happens since the textures are acquired by the main thread. - time_source->Tick(); - EXPECT_EQ(0, client.num_actions_()); + client.Reset(); + scheduler->BeginFrame(base::TimeTicks::Now()); + EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); + // Commit will release the texture. + client.Reset(); scheduler->FinishCommit(); - EXPECT_ACTION("ScheduledActionCommit", client, 0, 1); + EXPECT_SINGLE_ACTION("ScheduledActionCommit", client); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); - client.Reset(); + EXPECT_TRUE(client.needs_begin_frame()); // Now we can draw again after the commit happens. - time_source->Tick(); - EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 1); + client.Reset(); + scheduler->BeginFrame(base::TimeTicks::Now()); + EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(time_source->Active()); + EXPECT_FALSE(client.needs_begin_frame()); client.Reset(); } TEST(SchedulerTest, TextureAcquisitionCollision) { FakeSchedulerClient client; - scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -288,19 +297,23 @@ TEST(SchedulerTest, TextureAcquisitionCollision) { scheduler->SetNeedsCommit(); scheduler->SetMainThreadNeedsLayerTextures(); - EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2); + EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 4); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 4); EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread", client, - 1, - 2); + 2, + 4); + EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 3, 4); client.Reset(); - // Compositor not scheduled to draw because textures are locked by main thread - EXPECT_FALSE(time_source->Active()); + // Although the compositor cannot draw because textures are locked by main + // thread, we continue requesting SetNeedsBeginFrame in anticipation of the + // unlock. + EXPECT_TRUE(client.needs_begin_frame()); // Trigger the commit scheduler->FinishCommit(); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); client.Reset(); // Between commit and draw, texture acquisition for main thread delayed, @@ -310,7 +323,7 @@ TEST(SchedulerTest, TextureAcquisitionCollision) { client.Reset(); // Once compositor draw complete, the delayed texture acquisition fires. - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 3); EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread", client, @@ -322,11 +335,8 @@ TEST(SchedulerTest, TextureAcquisitionCollision) { TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) { FakeSchedulerClient client; - scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -382,11 +392,8 @@ class SchedulerClientThatsetNeedsDrawInsideDraw : public FakeSchedulerClient { // 2. the scheduler drawing twice inside a single tick TEST(SchedulerTest, RequestRedrawInsideDraw) { SchedulerClientThatsetNeedsDrawInsideDraw client; - scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -394,28 +401,25 @@ TEST(SchedulerTest, RequestRedrawInsideDraw) { scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); EXPECT_EQ(0, client.num_draws()); - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(1, client.num_draws()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(2, client.num_draws()); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(time_source->Active()); + EXPECT_FALSE(client.needs_begin_frame()); } // Test that requesting redraw inside a failed draw doesn't lose the request. TEST(SchedulerTest, RequestRedrawInsideFailedDraw) { SchedulerClientThatsetNeedsDrawInsideDraw client; - scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -425,33 +429,33 @@ TEST(SchedulerTest, RequestRedrawInsideFailedDraw) { scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); EXPECT_EQ(0, client.num_draws()); // Fail the draw. - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(1, client.num_draws()); // We have a commit pending and the draw failed, and we didn't lose the redraw // request. EXPECT_TRUE(scheduler->CommitPending()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); // Fail the draw again. - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(2, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); // Draw successfully. client.SetDrawWillHappen(true); - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(3, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); } class SchedulerClientThatsetNeedsCommitInsideDraw : public FakeSchedulerClient { @@ -480,11 +484,8 @@ class SchedulerClientThatsetNeedsCommitInsideDraw : public FakeSchedulerClient { // happen inside a ScheduledActionDrawAndSwap TEST(SchedulerTest, RequestCommitInsideDraw) { SchedulerClientThatsetNeedsCommitInsideDraw client; - scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -493,28 +494,26 @@ TEST(SchedulerTest, RequestCommitInsideDraw) { scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); EXPECT_EQ(0, client.num_draws()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); - time_source->Tick(); - EXPECT_FALSE(time_source->Active()); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(1, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); + EXPECT_TRUE(client.needs_begin_frame()); scheduler->FinishCommit(); - time_source->Tick(); - EXPECT_EQ(2, client.num_draws()); - EXPECT_FALSE(time_source->Active()); + scheduler->BeginFrame(base::TimeTicks::Now()); + EXPECT_EQ(2, client.num_draws());; EXPECT_FALSE(scheduler->RedrawPending()); + EXPECT_FALSE(scheduler->CommitPending()); + EXPECT_FALSE(client.needs_begin_frame()); } // Tests that when a draw fails then the pending commit should not be dropped. TEST(SchedulerTest, RequestCommitInsideFailedDraw) { SchedulerClientThatsetNeedsDrawInsideDraw client; - scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler( - make_scoped_ptr(new FrameRateController(time_source)), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); @@ -524,128 +523,77 @@ TEST(SchedulerTest, RequestCommitInsideFailedDraw) { scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); EXPECT_EQ(0, client.num_draws()); // Fail the draw. - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(1, client.num_draws()); // We have a commit pending and the draw failed, and we didn't lose the commit // request. EXPECT_TRUE(scheduler->CommitPending()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); // Fail the draw again. - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(2, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); // Draw successfully. client.SetDrawWillHappen(true); - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(3, client.num_draws()); EXPECT_TRUE(scheduler->CommitPending()); EXPECT_FALSE(scheduler->RedrawPending()); - EXPECT_FALSE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); } TEST(SchedulerTest, NoSwapWhenDrawFails) { - scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); SchedulerClientThatsetNeedsCommitInsideDraw client; - scoped_ptr<FakeFrameRateController> controller( - new FakeFrameRateController(time_source)); - FakeFrameRateController* controller_ptr = controller.get(); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler( - controller.PassAs<FrameRateController>(), - default_scheduler_settings); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); scheduler->SetCanStart(); scheduler->SetVisible(true); scheduler->SetCanDraw(true); scheduler->DidCreateAndInitializeOutputSurface(); - EXPECT_EQ(0, controller_ptr->NumFramesPending()); - scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); EXPECT_EQ(0, client.num_draws()); // Draw successfully, this starts a new frame. - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(1, client.num_draws()); - EXPECT_EQ(1, controller_ptr->NumFramesPending()); - scheduler->DidSwapBuffersComplete(); - EXPECT_EQ(0, controller_ptr->NumFramesPending()); scheduler->SetNeedsRedraw(); EXPECT_TRUE(scheduler->RedrawPending()); - EXPECT_TRUE(time_source->Active()); + EXPECT_TRUE(client.needs_begin_frame()); // Fail to draw, this should not start a frame. client.SetDrawWillHappen(false); - time_source->Tick(); + scheduler->BeginFrame(base::TimeTicks::Now()); EXPECT_EQ(2, client.num_draws()); - EXPECT_EQ(0, controller_ptr->NumFramesPending()); } TEST(SchedulerTest, NoSwapWhenSwapFailsDuringForcedCommit) { - scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); FakeSchedulerClient client; - scoped_ptr<FakeFrameRateController> controller( - new FakeFrameRateController(time_source)); - FakeFrameRateController* controller_ptr = controller.get(); SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler( - controller.PassAs<FrameRateController>(), - default_scheduler_settings); - - EXPECT_EQ(0, controller_ptr->NumFramesPending()); + Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings); // Tell the client that it will fail to swap. client.SetDrawWillHappen(true); client.SetSwapWillHappenIfDrawHappens(false); // Get the compositor to do a ScheduledActionDrawAndSwapForced. + scheduler->SetCanDraw(true); scheduler->SetNeedsRedraw(); scheduler->SetNeedsForcedRedraw(); EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapForced")); - - // We should not have told the frame rate controller that we began a frame. - EXPECT_EQ(0, controller_ptr->NumFramesPending()); -} - -TEST(SchedulerTest, RecreateOutputSurfaceClearsPendingDrawCount) { - scoped_refptr<FakeTimeSource> time_source(new FakeTimeSource()); - FakeSchedulerClient client; - scoped_ptr<FakeFrameRateController> controller( - new FakeFrameRateController(time_source)); - FakeFrameRateController* controller_ptr = controller.get(); - SchedulerSettings default_scheduler_settings; - Scheduler* scheduler = client.CreateScheduler( - controller.PassAs<FrameRateController>(), - default_scheduler_settings); - - scheduler->SetCanStart(); - scheduler->SetVisible(true); - scheduler->SetCanDraw(true); - scheduler->DidCreateAndInitializeOutputSurface(); - - // Draw successfully, this starts a new frame. - scheduler->SetNeedsRedraw(); - time_source->Tick(); - EXPECT_EQ(1, controller_ptr->NumFramesPending()); - - scheduler->DidLoseOutputSurface(); - // Verifying that it's 1 so that we know that it's reset on recreate. - EXPECT_EQ(1, controller_ptr->NumFramesPending()); - - scheduler->DidCreateAndInitializeOutputSurface(); - EXPECT_EQ(0, controller_ptr->NumFramesPending()); } } // namespace diff --git a/cc/scheduler/vsync_time_source.cc b/cc/scheduler/vsync_time_source.cc deleted file mode 100644 index 206e2376..0000000 --- a/cc/scheduler/vsync_time_source.cc +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/scheduler/vsync_time_source.h" - -#include "base/logging.h" - -namespace cc { - -scoped_refptr<VSyncTimeSource> VSyncTimeSource::Create( - VSyncProvider* vsync_provider, NotificationDisableOption option) { - return make_scoped_refptr(new VSyncTimeSource(vsync_provider, option)); -} - -VSyncTimeSource::VSyncTimeSource( - VSyncProvider* vsync_provider, NotificationDisableOption option) - : active_(false), - notification_requested_(false), - vsync_provider_(vsync_provider), - client_(NULL), - disable_option_(option) {} - -VSyncTimeSource::~VSyncTimeSource() {} - -void VSyncTimeSource::SetClient(TimeSourceClient* client) { - client_ = client; -} - -void VSyncTimeSource::SetActive(bool active) { - if (active_ == active) - return; - active_ = active; - if (active_ && !notification_requested_) { - notification_requested_ = true; - vsync_provider_->RequestVSyncNotification(this); - } - if (!active_ && disable_option_ == DISABLE_SYNCHRONOUSLY) { - notification_requested_ = false; - vsync_provider_->RequestVSyncNotification(NULL); - } -} - -bool VSyncTimeSource::Active() const { - return active_; -} - -base::TimeTicks VSyncTimeSource::LastTickTime() { - return last_tick_time_; -} - -base::TimeTicks VSyncTimeSource::NextTickTime() { - return Active() ? last_tick_time_ + interval_ : base::TimeTicks(); -} - -void VSyncTimeSource::SetTimebaseAndInterval(base::TimeTicks, - base::TimeDelta interval) { - interval_ = interval; -} - -void VSyncTimeSource::DidVSync(base::TimeTicks frame_time) { - last_tick_time_ = frame_time; - if (disable_option_ == DISABLE_SYNCHRONOUSLY) { - DCHECK(active_); - } else if (!active_) { - if (notification_requested_) { - notification_requested_ = false; - vsync_provider_->RequestVSyncNotification(NULL); - } - return; - } - if (client_) - client_->OnTimerTick(); -} - -} // namespace cc diff --git a/cc/scheduler/vsync_time_source.h b/cc/scheduler/vsync_time_source.h deleted file mode 100644 index 4cd0441..0000000 --- a/cc/scheduler/vsync_time_source.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_SCHEDULER_VSYNC_TIME_SOURCE_H_ -#define CC_SCHEDULER_VSYNC_TIME_SOURCE_H_ - -#include "cc/base/cc_export.h" -#include "cc/scheduler/time_source.h" - -namespace cc { - -class CC_EXPORT VSyncClient { - public: - virtual void DidVSync(base::TimeTicks frame_time) = 0; - - protected: - virtual ~VSyncClient() {} -}; - -class VSyncProvider { - public: - // Request to be notified of future vsync events. The notifications will be - // delivered until they are disabled by calling this function with a null - // client. - virtual void RequestVSyncNotification(VSyncClient* client) = 0; - - protected: - virtual ~VSyncProvider() {} -}; - -// This timer implements a time source that is explicitly triggered by an -// external vsync signal. -class CC_EXPORT VSyncTimeSource : public TimeSource, public VSyncClient { - public: - enum NotificationDisableOption { - // The notification will be lazily disabled in the callback to ensure - // we get notified of the frame immediately following a quick on-off-on - // transition. - DISABLE_ON_NEXT_TICK, - DISABLE_SYNCHRONOUSLY - }; - - static scoped_refptr<VSyncTimeSource> Create( - VSyncProvider* vsync_provider, NotificationDisableOption option); - - // TimeSource implementation - virtual void SetClient(TimeSourceClient* client) OVERRIDE; - virtual void SetTimebaseAndInterval(base::TimeTicks timebase, - base::TimeDelta interval) OVERRIDE; - virtual void SetActive(bool active) OVERRIDE; - virtual bool Active() const OVERRIDE; - virtual base::TimeTicks LastTickTime() OVERRIDE; - virtual base::TimeTicks NextTickTime() OVERRIDE; - - // VSyncClient implementation - virtual void DidVSync(base::TimeTicks frame_time) OVERRIDE; - - protected: - explicit VSyncTimeSource(VSyncProvider* vsync_provider, - NotificationDisableOption option); - virtual ~VSyncTimeSource(); - - base::TimeTicks last_tick_time_; - base::TimeDelta interval_; - bool active_; - bool notification_requested_; - - VSyncProvider* vsync_provider_; - TimeSourceClient* client_; - NotificationDisableOption disable_option_; - - DISALLOW_COPY_AND_ASSIGN(VSyncTimeSource); -}; - -} // namespace cc - -#endif // CC_SCHEDULER_VSYNC_TIME_SOURCE_H_ diff --git a/cc/scheduler/vsync_time_source_unittest.cc b/cc/scheduler/vsync_time_source_unittest.cc deleted file mode 100644 index 4834630..0000000 --- a/cc/scheduler/vsync_time_source_unittest.cc +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/scheduler/vsync_time_source.h" - -#include "cc/test/scheduler_test_common.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cc { -namespace { - -class FakeVSyncProvider : public VSyncProvider { - public: - FakeVSyncProvider() : client_(NULL) {} - - // VSyncProvider implementation. - virtual void RequestVSyncNotification(VSyncClient* client) OVERRIDE { - client_ = client; - } - - bool IsVSyncNotificationEnabled() const { return client_ != NULL; } - - void Trigger(base::TimeTicks frame_time) { - if (client_) - client_->DidVSync(frame_time); - } - - private: - VSyncClient* client_; -}; - -class VSyncTimeSourceTest : public testing::Test { - public: - VSyncTimeSourceTest() - : timer_(VSyncTimeSource::Create( - &provider_, VSyncTimeSource::DISABLE_ON_NEXT_TICK)) { - timer_->SetClient(&client_); - } - - protected: - FakeTimeSourceClient client_; - FakeVSyncProvider provider_; - scoped_refptr<VSyncTimeSource> timer_; -}; - -TEST_F(VSyncTimeSourceTest, TaskPostedAndTickCalled) { - EXPECT_FALSE(provider_.IsVSyncNotificationEnabled()); - - timer_->SetActive(true); - EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); - - base::TimeTicks frame_time = base::TimeTicks::Now(); - provider_.Trigger(frame_time); - EXPECT_TRUE(client_.TickCalled()); - EXPECT_EQ(timer_->LastTickTime(), frame_time); -} - -TEST_F(VSyncTimeSourceTest, NotificationDisabledLazily) { - base::TimeTicks frame_time = base::TimeTicks::Now(); - - // Enable timer and trigger sync once. - timer_->SetActive(true); - EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); - provider_.Trigger(frame_time); - EXPECT_TRUE(client_.TickCalled()); - - // Disabling the timer should not disable vsync notification immediately. - client_.Reset(); - timer_->SetActive(false); - EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); - - // At the next vsync the notification is disabled, but the timer isn't ticked. - provider_.Trigger(frame_time); - EXPECT_FALSE(provider_.IsVSyncNotificationEnabled()); - EXPECT_FALSE(client_.TickCalled()); - - // The notification should not be disabled multiple times. - provider_.RequestVSyncNotification(timer_.get()); - provider_.Trigger(frame_time); - EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); - EXPECT_FALSE(client_.TickCalled()); -} - -TEST_F(VSyncTimeSourceTest, NotificationDisabledImmediatelyForSetting) { - timer_ = VSyncTimeSource::Create(&provider_, - VSyncTimeSource::DISABLE_SYNCHRONOUSLY); - timer_->SetClient(&client_); - base::TimeTicks frame_time = base::TimeTicks::Now(); - - // Enable timer and trigger sync once. - timer_->SetActive(true); - EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); - provider_.Trigger(frame_time); - EXPECT_TRUE(client_.TickCalled()); - - // Disable timer should disable vsync notification immediately. - timer_->SetActive(false); - EXPECT_FALSE(provider_.IsVSyncNotificationEnabled()); - - // Enable again and timer can be ticked again. - client_.Reset(); - timer_->SetActive(true); - EXPECT_TRUE(provider_.IsVSyncNotificationEnabled()); - provider_.Trigger(frame_time); - EXPECT_TRUE(client_.TickCalled()); -} - -TEST_F(VSyncTimeSourceTest, ValidNextTickTime) { - base::TimeTicks frame_time = base::TimeTicks::Now(); - base::TimeDelta interval = base::TimeDelta::FromSeconds(1); - - ASSERT_EQ(timer_->NextTickTime(), base::TimeTicks()); - - timer_->SetActive(true); - provider_.Trigger(frame_time); - ASSERT_EQ(timer_->NextTickTime(), frame_time); - - timer_->SetTimebaseAndInterval(frame_time, interval); - ASSERT_EQ(timer_->NextTickTime(), frame_time + interval); -} - -} // namespace -} // namespace cc diff --git a/cc/test/fake_layer_tree_host_impl_client.h b/cc/test/fake_layer_tree_host_impl_client.h index dad4014..58816cc 100644 --- a/cc/test/fake_layer_tree_host_impl_client.h +++ b/cc/test/fake_layer_tree_host_impl_client.h @@ -17,9 +17,6 @@ class FakeLayerTreeHostImplClient : public LayerTreeHostImplClient { scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE {} virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE {} virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE {} - virtual void OnVSyncParametersChanged( - base::TimeTicks, - base::TimeDelta) OVERRIDE {} virtual void BeginFrameOnImplThread(base::TimeTicks frame_time) OVERRIDE {} virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE {} diff --git a/cc/test/fake_output_surface.cc b/cc/test/fake_output_surface.cc index c3f3e1a..9ceadda 100644 --- a/cc/test/fake_output_surface.cc +++ b/cc/test/fake_output_surface.cc @@ -8,6 +8,7 @@ #include "base/message_loop.h" #include "cc/output/compositor_frame_ack.h" #include "cc/output/output_surface_client.h" +#include "testing/gtest/include/gtest/gtest.h" namespace cc { @@ -17,7 +18,8 @@ FakeOutputSurface::FakeOutputSurface( : OutputSurface(context3d.Pass()), num_sent_frames_(0), needs_begin_frame_(false), - forced_draw_to_software_device_(false) { + forced_draw_to_software_device_(false), + fake_weak_ptr_factory_(this) { if (delegated_rendering) { capabilities_.delegated_rendering = true; capabilities_.max_frames_pending = 1; @@ -28,7 +30,8 @@ FakeOutputSurface::FakeOutputSurface( scoped_ptr<SoftwareOutputDevice> software_device, bool delegated_rendering) : OutputSurface(software_device.Pass()), num_sent_frames_(0), - forced_draw_to_software_device_(false) { + forced_draw_to_software_device_(false), + fake_weak_ptr_factory_(this) { if (delegated_rendering) { capabilities_.delegated_rendering = true; capabilities_.max_frames_pending = 1; @@ -41,7 +44,8 @@ FakeOutputSurface::FakeOutputSurface( bool delegated_rendering) : OutputSurface(context3d.Pass(), software_device.Pass()), num_sent_frames_(0), - forced_draw_to_software_device_(false) { + forced_draw_to_software_device_(false), + fake_weak_ptr_factory_(this) { if (delegated_rendering) { capabilities_.delegated_rendering = true; capabilities_.max_frames_pending = 1; @@ -56,6 +60,7 @@ void FakeOutputSurface::SwapBuffers(CompositorFrame* frame) { frame->AssignTo(&last_sent_frame_); ++num_sent_frames_; PostSwapBuffersComplete(); + DidSwapBuffers(); } else { OutputSurface::SwapBuffers(frame); frame->AssignTo(&last_sent_frame_); @@ -65,12 +70,23 @@ void FakeOutputSurface::SwapBuffers(CompositorFrame* frame) { void FakeOutputSurface::SetNeedsBeginFrame(bool enable) { needs_begin_frame_ = enable; + OutputSurface::SetNeedsBeginFrame(enable); + + // If there is not BeginFrame emulation from the FrameRateController, + // then we just post a BeginFrame to emulate it as part of the test. + if (enable && !frame_rate_controller_) { + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, base::Bind(&FakeOutputSurface::OnBeginFrame, + fake_weak_ptr_factory_.GetWeakPtr()), + base::TimeDelta::FromMilliseconds(16)); + } } -void FakeOutputSurface::BeginFrame(base::TimeTicks frame_time) { - client_->BeginFrame(frame_time); +void FakeOutputSurface::OnBeginFrame() { + OutputSurface::BeginFrame(base::TimeTicks::Now()); } + bool FakeOutputSurface::ForcedDrawToSoftwareDevice() const { return forced_draw_to_software_device_; } diff --git a/cc/test/fake_output_surface.h b/cc/test/fake_output_surface.h index d54ffd3..7bc1a4f 100644 --- a/cc/test/fake_output_surface.h +++ b/cc/test/fake_output_surface.h @@ -74,7 +74,6 @@ class FakeOutputSurface : public OutputSurface { bool needs_begin_frame() const { return needs_begin_frame_; } - void BeginFrame(base::TimeTicks frame_time); void set_forced_draw_to_software_device(bool forced) { forced_draw_to_software_device_ = forced; @@ -95,10 +94,13 @@ class FakeOutputSurface : public OutputSurface { scoped_ptr<SoftwareOutputDevice> software_device, bool delegated_rendering); + void OnBeginFrame(); + CompositorFrame last_sent_frame_; size_t num_sent_frames_; bool needs_begin_frame_; bool forced_draw_to_software_device_; + base::WeakPtrFactory<FakeOutputSurface> fake_weak_ptr_factory_; }; static inline scoped_ptr<cc::OutputSurface> CreateFakeOutputSurface() { diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc index b8c5366..b6f47ab 100644 --- a/cc/test/layer_tree_test.cc +++ b/cc/test/layer_tree_test.cc @@ -589,9 +589,13 @@ void LayerTreeTest::RunTest(bool threaded, } scoped_ptr<OutputSurface> LayerTreeTest::CreateOutputSurface() { + scoped_ptr<FakeOutputSurface> output_surface; if (delegating_renderer_) - return FakeOutputSurface::CreateDelegating3d().PassAs<OutputSurface>(); - return FakeOutputSurface::Create3d().PassAs<OutputSurface>(); + output_surface = FakeOutputSurface::CreateDelegating3d(); + else + output_surface = FakeOutputSurface::Create3d(); + output_surface_ = output_surface.get(); + return output_surface.PassAs<OutputSurface>(); } scoped_refptr<cc::ContextProvider> LayerTreeTest:: diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h index 856caaa..2d67a44 100644 --- a/cc/test/layer_tree_test.h +++ b/cc/test/layer_tree_test.h @@ -24,6 +24,7 @@ class LayerImpl; class LayerTreeHost; class LayerTreeHostClient; class LayerTreeHostImpl; +class FakeOutputSurface; // Used by test stubs to notify the test when something interesting happens. class TestHooks : public WebKit::WebAnimationDelegate { @@ -160,6 +161,7 @@ class LayerTreeTest : public testing::Test, public TestHooks { LayerTreeSettings settings_; scoped_ptr<LayerTreeHostClientForTesting> client_; scoped_ptr<LayerTreeHost> layer_tree_host_; + FakeOutputSurface* output_surface_; bool beginning_; bool end_when_begin_returns_; diff --git a/cc/test/scheduler_test_common.cc b/cc/test/scheduler_test_common.cc index c1aa200..8ccf47e 100644 --- a/cc/test/scheduler_test_common.cc +++ b/cc/test/scheduler_test_common.cc @@ -35,16 +35,6 @@ void FakeThread::PostDelayedTask(base::Closure cb, base::TimeDelta delay) { bool FakeThread::BelongsToCurrentThread() const { return true; } -void FakeTimeSource::SetClient(TimeSourceClient* client) { client_ = client; } - -void FakeTimeSource::SetActive(bool b) { active_ = b; } - -bool FakeTimeSource::Active() const { return active_; } - -base::TimeTicks FakeTimeSource::LastTickTime() { return base::TimeTicks(); } - -base::TimeTicks FakeTimeSource::NextTickTime() { return base::TimeTicks(); } - base::TimeTicks FakeDelayBasedTimeSource::Now() const { return now_; } } // namespace cc diff --git a/cc/test/scheduler_test_common.h b/cc/test/scheduler_test_common.h index b3b2c2d..8834e62 100644 --- a/cc/test/scheduler_test_common.h +++ b/cc/test/scheduler_test_common.h @@ -62,36 +62,6 @@ class FakeThread : public cc::Thread { bool run_pending_task_on_overwrite_; }; -class FakeTimeSource : public cc::TimeSource { - public: - FakeTimeSource() : active_(false), client_(0) {} - - virtual void SetClient(cc::TimeSourceClient* client) OVERRIDE; - virtual void SetActive(bool b) OVERRIDE; - virtual bool Active() const OVERRIDE; - virtual void SetTimebaseAndInterval(base::TimeTicks timebase, - base::TimeDelta interval) OVERRIDE {} - virtual base::TimeTicks LastTickTime() OVERRIDE; - virtual base::TimeTicks NextTickTime() OVERRIDE; - - void Tick() { - ASSERT_TRUE(active_); - if (client_) - client_->OnTimerTick(); - } - - void SetNextTickTime(base::TimeTicks next_tick_time) { - next_tick_time_ = next_tick_time; - } - - protected: - virtual ~FakeTimeSource() {} - - bool active_; - base::TimeTicks next_tick_time_; - cc::TimeSourceClient* client_; -}; - class FakeDelayBasedTimeSource : public cc::DelayBasedTimeSource { public: static scoped_refptr<FakeDelayBasedTimeSource> Create( diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index 65821be..b01aee6 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc @@ -1071,11 +1071,6 @@ void LayerTreeHostImpl::SetNeedsRedrawRect(gfx::Rect damage_rect) { client_->SetNeedsRedrawRectOnImplThread(damage_rect); } -void LayerTreeHostImpl::OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) { - client_->OnVSyncParametersChanged(timebase, interval); -} - void LayerTreeHostImpl::BeginFrame(base::TimeTicks frame_time) { client_->BeginFrameOnImplThread(frame_time); } @@ -1514,6 +1509,25 @@ bool LayerTreeHostImpl::DoInitializeRenderer( resource_provider_ = resource_provider.Pass(); } + // Setup BeginFrameEmulation if it's not supported natively + if (!settings_.begin_frame_scheduling_enabled) { + const base::TimeDelta display_refresh_interval = + base::TimeDelta::FromMicroseconds( + base::Time::kMicrosecondsPerSecond / + settings_.refresh_rate); + + output_surface->InitializeBeginFrameEmulation( + proxy_->ImplThread(), + settings_.throttle_frame_production, + display_refresh_interval); + } + + int max_frames_pending = + output_surface->capabilities().max_frames_pending; + if (max_frames_pending <= 0) + max_frames_pending = FrameRateController::DEFAULT_MAX_FRAMES_PENDING; + output_surface->SetMaxFramesPending(max_frames_pending); + output_surface_ = output_surface.Pass(); if (!visible_) diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h index 66fa715..a5019ae 100644 --- a/cc/trees/layer_tree_host_impl.h +++ b/cc/trees/layer_tree_host_impl.h @@ -54,8 +54,6 @@ class LayerTreeHostImplClient { scoped_refptr<ContextProvider> offscreen_context_provider) = 0; virtual void DidLoseOutputSurfaceOnImplThread() = 0; virtual void OnSwapBuffersCompleteOnImplThread() = 0; - virtual void OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) = 0; virtual void BeginFrameOnImplThread(base::TimeTicks frame_time) = 0; virtual void OnCanDrawStateChanged(bool can_draw) = 0; virtual void OnHasPendingTreeStateChanged(bool has_pending_tree) = 0; @@ -183,8 +181,6 @@ class CC_EXPORT LayerTreeHostImpl virtual float DeviceScaleFactor() const OVERRIDE; virtual const LayerTreeSettings& Settings() const OVERRIDE; public: - virtual void DidLoseOutputSurface() OVERRIDE; - virtual void OnSwapBuffersComplete(const CompositorFrameAck* ack) OVERRIDE; virtual void SetFullRootLayerDamage() OVERRIDE; virtual void SetManagedMemoryPolicy(const ManagedMemoryPolicy& policy) OVERRIDE; @@ -205,12 +201,12 @@ class CC_EXPORT LayerTreeHostImpl virtual bool DeferredInitialize( scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE; virtual void SetNeedsRedrawRect(gfx::Rect rect) OVERRIDE; - virtual void OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) OVERRIDE; virtual void BeginFrame(base::TimeTicks frame_time) OVERRIDE; virtual void SetExternalDrawConstraints(const gfx::Transform& transform, gfx::Rect viewport) OVERRIDE; + virtual void DidLoseOutputSurface() OVERRIDE; + virtual void OnSwapBuffersComplete(const CompositorFrameAck* ack) OVERRIDE; // Called from LayerTreeImpl. void OnCanDrawStateChangedForTree(); diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index 75fcef7..b8b320c 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc @@ -95,8 +95,6 @@ class LayerTreeHostImplTest : public testing::Test, } virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE {} virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE {} - virtual void OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) OVERRIDE {} virtual void BeginFrameOnImplThread(base::TimeTicks frame_time) OVERRIDE {} virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE { diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc index b8a2e9c..e4d192e 100644 --- a/cc/trees/layer_tree_host_unittest.cc +++ b/cc/trees/layer_tree_host_unittest.cc @@ -2049,36 +2049,6 @@ class LayerTreeHostTestCapturePicture : public LayerTreeHostTest { MULTI_THREAD_TEST_F(LayerTreeHostTestCapturePicture); -class LayerTreeHostTestMaxPendingFrames : public LayerTreeHostTest { - public: - LayerTreeHostTestMaxPendingFrames() : LayerTreeHostTest() {} - - virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } - - virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { - DCHECK(host_impl->proxy()->HasImplThread()); - - const ThreadProxy* proxy = static_cast<ThreadProxy*>(host_impl->proxy()); - if (delegating_renderer()) { - EXPECT_EQ(1, proxy->MaxFramesPendingForTesting()); - } else { - EXPECT_EQ(FrameRateController::DEFAULT_MAX_FRAMES_PENDING, - proxy->MaxFramesPendingForTesting()); - } - EndTest(); - } - - virtual void AfterTest() OVERRIDE {} -}; - -TEST_F(LayerTreeHostTestMaxPendingFrames, DelegatingRenderer) { - RunTest(true, true, true); -} - -TEST_F(LayerTreeHostTestMaxPendingFrames, GLRenderer) { - RunTest(true, false, true); -} - class LayerTreeHostTestShutdownWithOnlySomeResourcesEvicted : public LayerTreeHostTest { public: @@ -2253,32 +2223,10 @@ class LayerTreeHostTestBeginFrameNotification : public LayerTreeHostTest { } virtual void BeginTest() OVERRIDE { + // This will trigger a SetNeedsBeginFrame which will trigger a BeginFrame. PostSetNeedsCommitToMainThread(); } - virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { - FakeOutputSurface* fake_output_surface = - reinterpret_cast<FakeOutputSurface*>(host_impl->output_surface()); - - // The BeginFrame notification is turned off now but will get - // enabled once we return, so post a task to trigger it. - ASSERT_FALSE(fake_output_surface->needs_begin_frame()); - PostBeginFrameOnImplThread(fake_output_surface); - } - - void PostBeginFrameOnImplThread(FakeOutputSurface* fake_output_surface) { - DCHECK(ImplThread()); - ImplThread()->PostTask( - base::Bind(&LayerTreeHostTestBeginFrameNotification::BeginFrame, - base::Unretained(this), - base::Unretained(fake_output_surface))); - } - - void BeginFrame(FakeOutputSurface* fake_output_surface) { - ASSERT_TRUE(fake_output_surface->needs_begin_frame()); - fake_output_surface->BeginFrame(frame_time_); - } - virtual bool PrepareToDrawOnThread( LayerTreeHostImpl* host_impl, LayerTreeHostImpl::FrameData* frame, @@ -2824,11 +2772,6 @@ class LayerTreeHostTestNumFramesPending : public LayerTreeHostTest { } } - virtual void SwapBuffersCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { - const ThreadProxy* proxy = static_cast<ThreadProxy*>(impl->proxy()); - EXPECT_EQ(0, proxy->NumFramesPendingForTesting()); - } - virtual void AfterTest() OVERRIDE {} protected: @@ -2895,6 +2838,9 @@ class LayerTreeHostTestDeferredInitialize : public LayerTreeHostTest { // Force redraw again. host_impl->SetNeedsRedrawRect(gfx::Rect(1, 1)); + + // If we didn't swap this begin frame, we need to request another one. + host_impl->SetNeedsBeginFrame(true); } virtual void AfterTest() OVERRIDE { diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc index c448002..29cc4f4 100644 --- a/cc/trees/layer_tree_host_unittest_animation.cc +++ b/cc/trees/layer_tree_host_unittest_animation.cc @@ -193,7 +193,7 @@ class LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws started_animating_ = true; } - virtual void DrawLayersOnThread(LayerTreeHostImpl* tree_impl) OVERRIDE { + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { if (started_animating_) EndTest(); } @@ -205,7 +205,7 @@ class LayerTreeHostAnimationTestCheckerboardDoesNotStarveDraws return false; } - virtual void AfterTest() OVERRIDE {} + virtual void AfterTest() OVERRIDE { } private: bool started_animating_; diff --git a/cc/trees/proxy.cc b/cc/trees/proxy.cc index 8d2d2b7..fd39ed0 100644 --- a/cc/trees/proxy.cc +++ b/cc/trees/proxy.cc @@ -78,4 +78,8 @@ Proxy::~Proxy() { DCHECK(IsMainThread()); } +std::string Proxy::SchedulerStateAsStringForTesting() { + return ""; +} + } // namespace cc diff --git a/cc/trees/proxy.h b/cc/trees/proxy.h index aaab780..4e6171c 100644 --- a/cc/trees/proxy.h +++ b/cc/trees/proxy.h @@ -5,6 +5,8 @@ #ifndef CC_TREES_PROXY_H_ #define CC_TREES_PROXY_H_ +#include <string> + #include "base/basictypes.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" @@ -98,6 +100,7 @@ class CC_EXPORT Proxy { // Testing hooks virtual bool CommitPendingForTesting() = 0; + virtual std::string SchedulerStateAsStringForTesting(); protected: explicit Proxy(scoped_ptr<Thread> impl_thread); diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h index 0c3946b..9d995ba 100644 --- a/cc/trees/single_thread_proxy.h +++ b/cc/trees/single_thread_proxy.h @@ -51,8 +51,6 @@ class SingleThreadProxy : public Proxy, LayerTreeHostImplClient { scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE; virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE; virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE {} - virtual void OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) OVERRIDE {} virtual void BeginFrameOnImplThread(base::TimeTicks frame_time) OVERRIDE {} virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE; diff --git a/cc/trees/thread_proxy.cc b/cc/trees/thread_proxy.cc index 6a96c18..f745f3a 100644 --- a/cc/trees/thread_proxy.cc +++ b/cc/trees/thread_proxy.cc @@ -4,6 +4,8 @@ #include "cc/trees/thread_proxy.h" +#include <string> + #include "base/auto_reset.h" #include "base/bind.h" #include "base/debug/trace_event.h" @@ -17,7 +19,6 @@ #include "cc/scheduler/delay_based_time_source.h" #include "cc/scheduler/frame_rate_controller.h" #include "cc/scheduler/scheduler.h" -#include "cc/scheduler/vsync_time_source.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_impl.h" @@ -37,6 +38,23 @@ const int kDrawDurationEstimatePaddingInMicroseconds = 0; 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( @@ -69,7 +87,6 @@ ThreadProxy::ThreadProxy(LayerTreeHost* layer_tree_host, layer_tree_host->settings().begin_frame_scheduling_enabled), using_synchronous_renderer_compositor_( layer_tree_host->settings().using_synchronous_renderer_compositor), - vsync_client_(NULL), inside_draw_(false), defer_commits_(false), renew_tree_priority_on_impl_thread_pending_(false), @@ -334,36 +351,21 @@ void ThreadProxy::CheckOutputSurfaceStatusOnImplThread() { void ThreadProxy::OnSwapBuffersCompleteOnImplThread() { DCHECK(IsImplThread()); TRACE_EVENT0("cc", "ThreadProxy::OnSwapBuffersCompleteOnImplThread"); - scheduler_on_impl_thread_->DidSwapBuffersComplete(); Proxy::MainThread()->PostTask( base::Bind(&ThreadProxy::DidCompleteSwapBuffers, main_thread_weak_ptr_)); } -void ThreadProxy::OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) { +void ThreadProxy::SetNeedsBeginFrameOnImplThread(bool enable) { DCHECK(IsImplThread()); - TRACE_EVENT2("cc", - "ThreadProxy::OnVSyncParametersChanged", - "timebase", - (timebase - base::TimeTicks()).InMilliseconds(), - "interval", - interval.InMilliseconds()); - scheduler_on_impl_thread_->SetTimebaseAndInterval(timebase, interval); + TRACE_EVENT1("cc", "ThreadProxy::SetNeedsBeginFrameOnImplThread", + "enable", enable); + layer_tree_host_impl_->SetNeedsBeginFrame(enable); } void ThreadProxy::BeginFrameOnImplThread(base::TimeTicks frame_time) { DCHECK(IsImplThread()); - TRACE_EVENT0("cc", "ThreadProxy::OnBeginFrameOnImplThread"); - if (vsync_client_) - vsync_client_->DidVSync(frame_time); -} - -void ThreadProxy::RequestVSyncNotification(VSyncClient* client) { - DCHECK(IsImplThread()); - TRACE_EVENT1( - "cc", "ThreadProxy::RequestVSyncNotification", "enable", !!client); - vsync_client_ = client; - layer_tree_host_impl_->SetNeedsBeginFrame(!!client); + TRACE_EVENT0("cc", "ThreadProxy::BeginFrameOnImplThread"); + scheduler_on_impl_thread_->BeginFrame(frame_time); } void ThreadProxy::OnCanDrawStateChanged(bool can_draw) { @@ -377,10 +379,8 @@ void ThreadProxy::OnCanDrawStateChanged(bool can_draw) { void ThreadProxy::OnHasPendingTreeStateChanged(bool has_pending_tree) { DCHECK(IsImplThread()); - TRACE_EVENT1("cc", - "ThreadProxy::OnHasPendingTreeStateChanged", - "has_pending_tree", - has_pending_tree); + TRACE_EVENT1("cc", "ThreadProxy::OnHasPendingTreeStateChanged", + "has_pending_tree", has_pending_tree); scheduler_on_impl_thread_->SetHasPendingTree(has_pending_tree); } @@ -1152,35 +1152,14 @@ void ThreadProxy::InitializeImplOnImplThread(CompletionEvent* completion) { TRACE_EVENT0("cc", "ThreadProxy::InitializeImplOnImplThread"); DCHECK(IsImplThread()); layer_tree_host_impl_ = layer_tree_host_->CreateLayerTreeHostImpl(this); - const base::TimeDelta display_refresh_interval = - base::TimeDelta::FromMicroseconds( - base::Time::kMicrosecondsPerSecond / - layer_tree_host_->settings().refresh_rate); - scoped_ptr<FrameRateController> frame_rate_controller; - if (throttle_frame_production_) { - if (begin_frame_scheduling_enabled_) { - frame_rate_controller.reset( - new FrameRateController(VSyncTimeSource::Create( - this, - using_synchronous_renderer_compositor_ ? - VSyncTimeSource::DISABLE_SYNCHRONOUSLY : - VSyncTimeSource::DISABLE_ON_NEXT_TICK))); - } else { - frame_rate_controller.reset( - new FrameRateController(DelayBasedTimeSource::Create( - display_refresh_interval, Proxy::ImplThread()))); - } - } else { - frame_rate_controller.reset(new FrameRateController(Proxy::ImplThread())); - } const LayerTreeSettings& settings = layer_tree_host_->settings(); SchedulerSettings scheduler_settings; scheduler_settings.impl_side_painting = settings.impl_side_painting; scheduler_settings.timeout_and_draw_when_animation_checkerboards = settings.timeout_and_draw_when_animation_checkerboards; - scheduler_on_impl_thread_ = Scheduler::Create(this, - frame_rate_controller.Pass(), - scheduler_settings); + scheduler_settings.using_synchronous_renderer_compositor = + settings.using_synchronous_renderer_compositor; + scheduler_on_impl_thread_ = Scheduler::Create(this, scheduler_settings); scheduler_on_impl_thread_->SetVisible(layer_tree_host_impl_->visible()); impl_thread_weak_ptr_ = weak_factory_on_impl_thread_.GetWeakPtr(); @@ -1206,16 +1185,6 @@ void ThreadProxy::InitializeOutputSurfaceOnImplThread( if (*success) { *capabilities = layer_tree_host_impl_->GetRendererCapabilities(); - - OutputSurface* output_surface_ptr = layer_tree_host_impl_->output_surface(); - DCHECK(output_surface_ptr); - int max_frames_pending = - output_surface_ptr->capabilities().max_frames_pending; - if (max_frames_pending <= 0) - max_frames_pending = FrameRateController::DEFAULT_MAX_FRAMES_PENDING; - - scheduler_on_impl_thread_->SetMaxFramesPending(max_frames_pending); - scheduler_on_impl_thread_->DidCreateAndInitializeOutputSurface(); } @@ -1260,7 +1229,6 @@ void ThreadProxy::LayerTreeHostClosedOnImplThread(CompletionEvent* completion) { scheduler_on_impl_thread_.reset(); layer_tree_host_impl_.reset(); weak_factory_on_impl_thread_.InvalidateWeakPtrs(); - vsync_client_ = NULL; completion->Signal(); } @@ -1320,6 +1288,29 @@ void ThreadProxy::CommitPendingOnImplThreadForTesting( request->completion.Signal(); } +std::string ThreadProxy::SchedulerStateAsStringForTesting() { + if (IsImplThread()) + return scheduler_on_impl_thread_->StateAsStringForTesting(); + + SchedulerStateRequest scheduler_state_request; + { + DebugScopedSetMainThreadBlocked main_thread_blocked(this); + Proxy::ImplThread()->PostTask( + base::Bind(&ThreadProxy::SchedulerStateAsStringOnImplThreadForTesting, + impl_thread_weak_ptr_, + &scheduler_state_request)); + scheduler_state_request.completion.Wait(); + } + return scheduler_state_request.state; +} + +void ThreadProxy::SchedulerStateAsStringOnImplThreadForTesting( + SchedulerStateRequest* request) { + DCHECK(IsImplThread()); + request->state = scheduler_on_impl_thread_->StateAsStringForTesting(); + request->completion.Signal(); +} + skia::RefPtr<SkPicture> ThreadProxy::CapturePicture() { DCHECK(IsMainThread()); CompletionEvent completion; diff --git a/cc/trees/thread_proxy.h b/cc/trees/thread_proxy.h index 7a692423..db3a502 100644 --- a/cc/trees/thread_proxy.h +++ b/cc/trees/thread_proxy.h @@ -5,6 +5,8 @@ #ifndef CC_TREES_THREAD_PROXY_H_ #define CC_TREES_THREAD_PROXY_H_ +#include <string> + #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/time.h" @@ -13,7 +15,6 @@ #include "cc/resources/resource_update_controller.h" #include "cc/scheduler/rolling_time_delta_history.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,8 +31,7 @@ class Thread; class ThreadProxy : public Proxy, LayerTreeHostImplClient, SchedulerClient, - ResourceUpdateControllerClient, - VSyncProvider { + ResourceUpdateControllerClient { public: static scoped_ptr<Proxy> Create(LayerTreeHost* layer_tree_host, scoped_ptr<Thread> impl_thread); @@ -60,6 +60,7 @@ class ThreadProxy : public Proxy, virtual skia::RefPtr<SkPicture> CapturePicture() OVERRIDE; virtual scoped_ptr<base::Value> AsValue() const OVERRIDE; virtual bool CommitPendingForTesting() OVERRIDE; + virtual std::string SchedulerStateAsStringForTesting() OVERRIDE; // LayerTreeHostImplClient implementation virtual void DidTryInitializeRendererOnImplThread( @@ -67,10 +68,7 @@ class ThreadProxy : public Proxy, scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE; virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE; virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE; - virtual void OnVSyncParametersChanged(base::TimeTicks timebase, - base::TimeDelta interval) OVERRIDE; - virtual void BeginFrameOnImplThread(base::TimeTicks frame_time) - OVERRIDE; + virtual void BeginFrameOnImplThread(base::TimeTicks frame_time) OVERRIDE; virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE; virtual void OnHasPendingTreeStateChanged(bool has_pending_tree) OVERRIDE; virtual void SetNeedsRedrawOnImplThread() OVERRIDE; @@ -92,6 +90,7 @@ class ThreadProxy : public Proxy, virtual void DidActivatePendingTree() OVERRIDE; // SchedulerClient implementation + virtual void SetNeedsBeginFrameOnImplThread(bool enable) OVERRIDE; virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE; virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapIfPossible() OVERRIDE; @@ -108,17 +107,6 @@ class ThreadProxy : public Proxy, // ResourceUpdateControllerClient implementation virtual void ReadyToFinalizeTextureUpdates() OVERRIDE; - // VSyncProvider implementation - virtual void RequestVSyncNotification(VSyncClient* client) OVERRIDE; - - int MaxFramesPendingForTesting() const { - return scheduler_on_impl_thread_->MaxFramesPending(); - } - - int NumFramesPendingForTesting() const { - return scheduler_on_impl_thread_->NumFramesPendingForTesting(); - } - private: ThreadProxy(LayerTreeHost* layer_tree_host, scoped_ptr<Thread> impl_thread); @@ -145,16 +133,10 @@ class ThreadProxy : public Proxy, const RendererCapabilities& capabilities); // Called on impl thread. - struct ReadbackRequest { - CompletionEvent completion; - bool success; - void* pixels; - gfx::Rect rect; - }; - struct CommitPendingRequest { - CompletionEvent completion; - bool commit_pending; - }; + struct ReadbackRequest; + struct CommitPendingRequest; + struct SchedulerStateRequest; + void ForceCommitOnImplThread(CompletionEvent* completion); void StartCommitOnImplThread( CompletionEvent* completion, @@ -184,6 +166,8 @@ class ThreadProxy : public Proxy, void ForceSerializeOnSwapBuffersOnImplThread(CompletionEvent* completion); void CheckOutputSurfaceStatusOnImplThread(); void CommitPendingOnImplThreadForTesting(CommitPendingRequest* request); + void SchedulerStateAsStringOnImplThreadForTesting( + SchedulerStateRequest* request); void CapturePictureOnImplThread(CompletionEvent* completion, skia::RefPtr<SkPicture>* picture); void AsValueOnImplThread(CompletionEvent* completion, @@ -252,7 +236,6 @@ class ThreadProxy : public Proxy, bool throttle_frame_production_; bool begin_frame_scheduling_enabled_; bool using_synchronous_renderer_compositor_; - VSyncClient* vsync_client_; bool inside_draw_; diff --git a/chrome/test/perf/rendering/latency_tests.cc b/chrome/test/perf/rendering/latency_tests.cc index aac0294..0f61cd9 100644 --- a/chrome/test/perf/rendering/latency_tests.cc +++ b/chrome/test/perf/rendering/latency_tests.cc @@ -371,14 +371,14 @@ void LatencyTest::RunTest(const std::vector<int>& behaviors) { if (mode_ == kWebGLThread) { // Print vsync info when in threaded mode. Query query_vsync = - Query::EventNameIs("CCThreadProxy::onVSyncParametersChanged") && - Query::EventHasNumberArg("monotonicTimebase") && - Query::EventHasNumberArg("intervalInSeconds"); + Query::EventNameIs("OutputSurface::OnVSyncParametersChanged") && + Query::EventHasNumberArg("timebase") && + Query::EventHasNumberArg("interval"); const TraceEvent* vsync_info = analyzer_->FindFirstOf(query_vsync); if (vsync_info) { - double timebase = vsync_info->GetKnownArgAsDouble("monotonicTimebase"); - double interval = vsync_info->GetKnownArgAsDouble("intervalInSeconds"); + double timebase = vsync_info->GetKnownArgAsDouble("timebase"); + double interval = vsync_info->GetKnownArgAsDouble("interval"); printf("VSync scheduling: timebase = %f; interval = %f\n", timebase, interval); } diff --git a/content/browser/android/in_process/synchronous_compositor_output_surface.cc b/content/browser/android/in_process/synchronous_compositor_output_surface.cc index 500eb6b..30f5d0e 100644 --- a/content/browser/android/in_process/synchronous_compositor_output_surface.cc +++ b/content/browser/android/in_process/synchronous_compositor_output_surface.cc @@ -124,6 +124,7 @@ void SynchronousCompositorOutputSurface::Reshape( void SynchronousCompositorOutputSurface::SetNeedsBeginFrame( bool enable) { DCHECK(CalledOnValidThread()); + cc::OutputSurface::SetNeedsBeginFrame(enable); needs_begin_frame_ = enable; SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate(); if (delegate) @@ -141,6 +142,7 @@ void SynchronousCompositorOutputSurface::SwapBuffers( delegate->UpdateFrameMetaData(frame->metadata); did_swap_buffer_ = true; + DidSwapBuffers(); } namespace { @@ -153,7 +155,7 @@ void AdjustTransformForClip(gfx::Transform* transform, gfx::Rect clip) { bool SynchronousCompositorOutputSurface::InitializeHwDraw() { DCHECK(CalledOnValidThread()); - DCHECK(client_); + DCHECK(HasClient()); DCHECK(!context3d_); // TODO(boliu): Get a context provider in constructor and pass here. @@ -166,7 +168,7 @@ bool SynchronousCompositorOutputSurface::DemandDrawHw( const gfx::Transform& transform, gfx::Rect clip) { DCHECK(CalledOnValidThread()); - DCHECK(client_); + DCHECK(HasClient()); DCHECK(context3d()); // Force a GL state restore next time a GLContextVirtual is made current. @@ -180,7 +182,7 @@ bool SynchronousCompositorOutputSurface::DemandDrawHw( gfx::Transform adjusted_transform = transform; AdjustTransformForClip(&adjusted_transform, clip); surface_size_ = surface_size; - client_->SetExternalDrawConstraints(adjusted_transform, clip); + SetExternalDrawConstraints(adjusted_transform, clip); InvokeComposite(clip.size()); // TODO(boliu): Check if context is lost here. @@ -204,7 +206,7 @@ bool SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) { surface_size_ = gfx::Size(canvas->getDeviceSize().width(), canvas->getDeviceSize().height()); - client_->SetExternalDrawConstraints(transform, clip); + SetExternalDrawConstraints(transform, clip); InvokeComposite(clip.size()); @@ -215,12 +217,12 @@ bool SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) { void SynchronousCompositorOutputSurface::InvokeComposite( gfx::Size damage_size) { did_swap_buffer_ = false; - client_->SetNeedsRedrawRect(gfx::Rect(damage_size)); + SetNeedsRedrawRect(gfx::Rect(damage_size)); if (needs_begin_frame_) - client_->BeginFrame(base::TimeTicks::Now()); + BeginFrame(base::TimeTicks::Now()); if (did_swap_buffer_) - client_->OnSwapBuffersComplete(NULL); + OnSwapBuffersComplete(NULL); } // Not using base::NonThreadSafe as we want to enforce a more exacting threading diff --git a/content/browser/renderer_host/image_transport_factory.cc b/content/browser/renderer_host/image_transport_factory.cc index a4ed8e1..a4eeacd 100644 --- a/content/browser/renderer_host/image_transport_factory.cc +++ b/content/browser/renderer_host/image_transport_factory.cc @@ -510,7 +510,7 @@ class BrowserCompositorOutputSurface virtual ~BrowserCompositorOutputSurface() { DCHECK(CalledOnValidThread()); - if (!client_) + if (!HasClient()) return; output_surface_proxy_->RemoveSurface(surface_id_); } @@ -529,8 +529,8 @@ class BrowserCompositorOutputSurface void OnUpdateVSyncParameters( base::TimeTicks timebase, base::TimeDelta interval) { DCHECK(CalledOnValidThread()); - DCHECK(client_); - client_->OnVSyncParametersChanged(timebase, interval); + DCHECK(HasClient()); + OnVSyncParametersChanged(timebase, interval); compositor_message_loop_->PostTask( FROM_HERE, base::Bind(&ui::Compositor::OnUpdateVSyncParameters, diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc index a20f628..60df325 100644 --- a/content/browser/renderer_host/render_widget_host_view_android.cc +++ b/content/browser/renderer_host/render_widget_host_view_android.cc @@ -416,6 +416,7 @@ void RenderWidgetHostViewAndroid::OnDidChangeBodyBackgroundColor( void RenderWidgetHostViewAndroid::SendBeginFrame( base::TimeTicks frame_time) { + TRACE_EVENT0("cc", "RenderWidgetHostViewAndroid::SendBeginFrame"); if (host_) host_->Send(new ViewMsg_BeginFrame(host_->GetRoutingID(), frame_time)); @@ -423,6 +424,8 @@ void RenderWidgetHostViewAndroid::SendBeginFrame( void RenderWidgetHostViewAndroid::OnSetNeedsBeginFrame( bool enabled) { + TRACE_EVENT1("cc", "RenderWidgetHostViewAndroid::OnSetNeedsBeginFrame", + "enabled", enabled); if (content_view_core_) content_view_core_->SetVSyncNotificationEnabled(enabled); } diff --git a/content/renderer/gpu/compositor_output_surface.cc b/content/renderer/gpu/compositor_output_surface.cc index c570f51..30fbc30 100644 --- a/content/renderer/gpu/compositor_output_surface.cc +++ b/content/renderer/gpu/compositor_output_surface.cc @@ -68,7 +68,7 @@ CompositorOutputSurface::CompositorOutputSurface( CompositorOutputSurface::~CompositorOutputSurface() { DCHECK(CalledOnValidThread()); - if (!client_) + if (!HasClient()) return; UpdateSmoothnessTakesPriority(false); if (output_surface_proxy_.get()) @@ -95,6 +95,7 @@ bool CompositorOutputSurface::BindToClient( void CompositorOutputSurface::SwapBuffers(cc::CompositorFrame* frame) { if (use_swap_compositor_frame_message_) { Send(new ViewHostMsg_SwapCompositorFrame(routing_id_, *frame)); + DidSwapBuffers(); return; } @@ -113,7 +114,7 @@ void CompositorOutputSurface::SwapBuffers(cc::CompositorFrame* frame) { void CompositorOutputSurface::OnMessageReceived(const IPC::Message& message) { DCHECK(CalledOnValidThread()); - if (!client_) + if (!HasClient()) return; IPC_BEGIN_MESSAGE_MAP(CompositorOutputSurface, message) IPC_MESSAGE_HANDLER(ViewMsg_UpdateVSyncParameters, OnUpdateVSyncParameters); @@ -127,23 +128,24 @@ void CompositorOutputSurface::OnMessageReceived(const IPC::Message& message) { void CompositorOutputSurface::OnUpdateVSyncParameters( base::TimeTicks timebase, base::TimeDelta interval) { DCHECK(CalledOnValidThread()); - client_->OnVSyncParametersChanged(timebase, interval); + OnVSyncParametersChanged(timebase, interval); } #if defined(OS_ANDROID) void CompositorOutputSurface::SetNeedsBeginFrame(bool enable) { DCHECK(CalledOnValidThread()); Send(new ViewHostMsg_SetNeedsBeginFrame(routing_id_, enable)); + OutputSurface::SetNeedsBeginFrame(enable); } void CompositorOutputSurface::OnBeginFrame(base::TimeTicks frame_time) { DCHECK(CalledOnValidThread()); - client_->BeginFrame(frame_time); + BeginFrame(frame_time); } #endif // defined(OS_ANDROID) void CompositorOutputSurface::OnSwapAck(const cc::CompositorFrameAck& ack) { - client_->OnSwapBuffersComplete(&ack); + OnSwapBuffersComplete(&ack); } bool CompositorOutputSurface::Send(IPC::Message* message) { |