diff options
Diffstat (limited to 'chromium/cc/output/output_surface_unittest.cc')
-rw-r--r-- | chromium/cc/output/output_surface_unittest.cc | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/chromium/cc/output/output_surface_unittest.cc b/chromium/cc/output/output_surface_unittest.cc new file mode 100644 index 00000000000..4f9e370e1f0 --- /dev/null +++ b/chromium/cc/output/output_surface_unittest.cc @@ -0,0 +1,428 @@ +// 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/output/output_surface.h" + +#include "base/test/test_simple_task_runner.h" +#include "cc/debug/test_web_graphics_context_3d.h" +#include "cc/output/managed_memory_policy.h" +#include "cc/output/output_surface_client.h" +#include "cc/output/software_output_device.h" +#include "cc/test/fake_output_surface.h" +#include "cc/test/fake_output_surface_client.h" +#include "cc/test/scheduler_test_common.h" +#include "gpu/GLES2/gl2extchromium.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebGraphicsMemoryAllocation.h" + +using WebKit::WebGraphicsMemoryAllocation; + +namespace cc { +namespace { + +class TestOutputSurface : public OutputSurface { + public: + explicit TestOutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D> context3d) + : OutputSurface(context3d.Pass()) {} + + explicit TestOutputSurface( + scoped_ptr<cc::SoftwareOutputDevice> software_device) + : OutputSurface(software_device.Pass()) {} + + TestOutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D> context3d, + scoped_ptr<cc::SoftwareOutputDevice> software_device) + : OutputSurface(context3d.Pass(), software_device.Pass()) {} + + bool InitializeNewContext3D( + scoped_ptr<WebKit::WebGraphicsContext3D> new_context3d) { + return InitializeAndSetContext3D(new_context3d.Pass(), + scoped_refptr<ContextProvider>()); + } + + using OutputSurface::ReleaseGL; + + bool HasClientForTesting() { + return HasClient(); + } + + void OnVSyncParametersChangedForTesting(base::TimeTicks timebase, + base::TimeDelta interval) { + OnVSyncParametersChanged(timebase, interval); + } + + void BeginFrameForTesting() { + OutputSurface::BeginFrame(BeginFrameArgs::CreateExpiredForTesting()); + } + + void DidSwapBuffersForTesting() { + DidSwapBuffers(); + } + + int pending_swap_buffers() { + return pending_swap_buffers_; + } + + void OnSwapBuffersCompleteForTesting() { + OnSwapBuffersComplete(NULL); + } + + void SetAlternateRetroactiveBeginFramePeriod(base::TimeDelta period) { + alternate_retroactive_begin_frame_period_ = period; + } + + protected: + virtual void PostCheckForRetroactiveBeginFrame() OVERRIDE { + // For testing purposes, we check immediately rather than posting a task. + CheckForRetroactiveBeginFrame(); + } + + virtual base::TimeDelta AlternateRetroactiveBeginFramePeriod() OVERRIDE { + return alternate_retroactive_begin_frame_period_; + } + + base::TimeDelta alternate_retroactive_begin_frame_period_; +}; + +TEST(OutputSurfaceTest, ClientPointerIndicatesBindToClientSuccess) { + 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()); + + // Verify DidLoseOutputSurface callback is hooked up correctly. + EXPECT_FALSE(client.did_lose_output_surface_called()); + output_surface.context3d()->loseContextCHROMIUM( + GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB); + EXPECT_TRUE(client.did_lose_output_surface_called()); +} + +TEST(OutputSurfaceTest, ClientPointerIndicatesBindToClientFailure) { + scoped_ptr<TestWebGraphicsContext3D> context3d = + TestWebGraphicsContext3D::Create(); + + // Lose the context so BindToClient fails. + context3d->set_times_make_current_succeeds(0); + + TestOutputSurface output_surface( + context3d.PassAs<WebKit::WebGraphicsContext3D>()); + EXPECT_FALSE(output_surface.HasClientForTesting()); + + FakeOutputSurfaceClient client; + EXPECT_FALSE(output_surface.BindToClient(&client)); + EXPECT_FALSE(output_surface.HasClientForTesting()); +} + +class InitializeNewContext3D : public ::testing::Test { + public: + InitializeNewContext3D() + : context3d_(TestWebGraphicsContext3D::Create()), + output_surface_( + scoped_ptr<SoftwareOutputDevice>(new SoftwareOutputDevice)) {} + + protected: + void BindOutputSurface() { + EXPECT_TRUE(output_surface_.BindToClient(&client_)); + EXPECT_TRUE(output_surface_.HasClientForTesting()); + } + + void InitializeNewContextExpectFail() { + EXPECT_FALSE(output_surface_.InitializeNewContext3D( + context3d_.PassAs<WebKit::WebGraphicsContext3D>())); + EXPECT_TRUE(output_surface_.HasClientForTesting()); + + EXPECT_FALSE(output_surface_.context3d()); + EXPECT_TRUE(output_surface_.software_device()); + } + + scoped_ptr<TestWebGraphicsContext3D> context3d_; + TestOutputSurface output_surface_; + FakeOutputSurfaceClient client_; +}; + +TEST_F(InitializeNewContext3D, Success) { + BindOutputSurface(); + EXPECT_FALSE(client_.deferred_initialize_called()); + + EXPECT_TRUE(output_surface_.InitializeNewContext3D( + context3d_.PassAs<WebKit::WebGraphicsContext3D>())); + EXPECT_TRUE(client_.deferred_initialize_called()); + + EXPECT_FALSE(client_.did_lose_output_surface_called()); + output_surface_.context3d()->loseContextCHROMIUM( + GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB); + EXPECT_TRUE(client_.did_lose_output_surface_called()); + + output_surface_.ReleaseGL(); + EXPECT_FALSE(output_surface_.context3d()); +} + +TEST_F(InitializeNewContext3D, Context3dMakeCurrentFails) { + BindOutputSurface(); + context3d_->set_times_make_current_succeeds(0); + InitializeNewContextExpectFail(); +} + +TEST_F(InitializeNewContext3D, ClientDeferredInitializeFails) { + BindOutputSurface(); + client_.set_deferred_initialize_result(false); + 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 + scoped_refptr<base::TestSimpleTaskRunner> task_runner = + new base::TestSimpleTaskRunner; + bool throttle_frame_production = true; + const base::TimeDelta display_refresh_interval = + BeginFrameArgs::DefaultInterval(); + + output_surface.InitializeBeginFrameEmulation( + task_runner.get(), + throttle_frame_production, + display_refresh_interval); + + output_surface.SetMaxFramesPending(2); + output_surface.SetAlternateRetroactiveBeginFramePeriod( + base::TimeDelta::FromSeconds(-1)); + + // 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(task_runner->HasPendingTask()); + output_surface.SetNeedsBeginFrame(true); + EXPECT_TRUE(task_runner->HasPendingTask()); + + // BeginFrame should be called on the first tick. + task_runner->RunPendingTasks(); + 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. + task_runner->RunPendingTasks(); + 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); + task_runner->RunPendingTasks(); + 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); + task_runner->RunPendingTasks(); + 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); + task_runner->RunPendingTasks(); + 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); + task_runner->RunPendingTasks(); + 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); + task_runner->RunPendingTasks(); + EXPECT_FALSE(task_runner->HasPendingTask()); + EXPECT_EQ(client.begin_frame_count(), 4); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); +} + +TEST(OutputSurfaceTest, OptimisticAndRetroactiveBeginFrames) { + 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()); + + output_surface.SetMaxFramesPending(2); + + // Enable retroactive BeginFrames. + output_surface.SetAlternateRetroactiveBeginFramePeriod( + base::TimeDelta::FromSeconds(100000)); + + // Optimistically injected BeginFrames should be throttled if + // SetNeedsBeginFrame is false... + output_surface.SetNeedsBeginFrame(false); + output_surface.BeginFrameForTesting(); + EXPECT_EQ(client.begin_frame_count(), 0); + // ...and retroactively triggered by a SetNeedsBeginFrame. + output_surface.SetNeedsBeginFrame(true); + EXPECT_EQ(client.begin_frame_count(), 1); + + // Optimistically injected BeginFrames should be throttled by pending + // BeginFrames... + output_surface.BeginFrameForTesting(); + EXPECT_EQ(client.begin_frame_count(), 1); + // ...and retroactively triggered by a SetNeedsBeginFrame. + output_surface.SetNeedsBeginFrame(true); + EXPECT_EQ(client.begin_frame_count(), 2); + // ...or retroactively triggered by a Swap. + output_surface.BeginFrameForTesting(); + EXPECT_EQ(client.begin_frame_count(), 2); + output_surface.DidSwapBuffersForTesting(); + EXPECT_EQ(client.begin_frame_count(), 3); + EXPECT_EQ(output_surface.pending_swap_buffers(), 1); + + // Optimistically injected BeginFrames should be by throttled by pending + // swap buffers... + output_surface.DidSwapBuffersForTesting(); + EXPECT_EQ(client.begin_frame_count(), 3); + EXPECT_EQ(output_surface.pending_swap_buffers(), 2); + output_surface.BeginFrameForTesting(); + EXPECT_EQ(client.begin_frame_count(), 3); + // ...and retroactively triggered by OnSwapBuffersComplete + output_surface.OnSwapBuffersCompleteForTesting(); + EXPECT_EQ(client.begin_frame_count(), 4); +} + +TEST(OutputSurfaceTest, RetroactiveBeginFrameDoesNotDoubleTickWhenEmulating) { + 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()); + + base::TimeDelta big_interval = base::TimeDelta::FromSeconds(1000); + + // Initialize BeginFrame emulation + scoped_refptr<base::TestSimpleTaskRunner> task_runner = + new base::TestSimpleTaskRunner; + bool throttle_frame_production = true; + const base::TimeDelta display_refresh_interval = big_interval; + + output_surface.InitializeBeginFrameEmulation( + task_runner.get(), + throttle_frame_production, + display_refresh_interval); + + // We need to subtract an epsilon from Now() because some platforms have + // a slow clock. + output_surface.OnVSyncParametersChangedForTesting( + base::TimeTicks::Now() - base::TimeDelta::FromMilliseconds(1), + display_refresh_interval); + + output_surface.SetMaxFramesPending(2); + output_surface.SetAlternateRetroactiveBeginFramePeriod( + base::TimeDelta::FromSeconds(-1)); + + // We should start off with 0 BeginFrames + EXPECT_EQ(client.begin_frame_count(), 0); + EXPECT_EQ(output_surface.pending_swap_buffers(), 0); + + // The first SetNeedsBeginFrame(true) should start a retroactive BeginFrame. + output_surface.SetNeedsBeginFrame(true); + EXPECT_TRUE(task_runner->HasPendingTask()); + EXPECT_GT(task_runner->NextPendingTaskDelay(), big_interval / 2); + EXPECT_EQ(client.begin_frame_count(), 1); + + output_surface.SetNeedsBeginFrame(false); + EXPECT_TRUE(task_runner->HasPendingTask()); + EXPECT_EQ(client.begin_frame_count(), 1); + + // The second SetNeedBeginFrame(true) should not retroactively start a + // BeginFrame if the timestamp would be the same as the previous BeginFrame. + output_surface.SetNeedsBeginFrame(true); + EXPECT_TRUE(task_runner->HasPendingTask()); + EXPECT_EQ(client.begin_frame_count(), 1); +} + +TEST(OutputSurfaceTest, MemoryAllocation) { + scoped_ptr<TestWebGraphicsContext3D> scoped_context = + TestWebGraphicsContext3D::Create(); + TestWebGraphicsContext3D* context = scoped_context.get(); + + TestOutputSurface output_surface( + scoped_context.PassAs<WebKit::WebGraphicsContext3D>()); + + FakeOutputSurfaceClient client; + EXPECT_TRUE(output_surface.BindToClient(&client)); + + WebGraphicsMemoryAllocation allocation; + allocation.suggestHaveBackbuffer = true; + allocation.bytesLimitWhenVisible = 1234; + allocation.priorityCutoffWhenVisible = + WebGraphicsMemoryAllocation::PriorityCutoffAllowVisibleOnly; + allocation.bytesLimitWhenNotVisible = 4567; + allocation.priorityCutoffWhenNotVisible = + WebGraphicsMemoryAllocation::PriorityCutoffAllowNothing; + + context->SetMemoryAllocation(allocation); + + EXPECT_EQ(1234u, client.memory_policy().bytes_limit_when_visible); + EXPECT_EQ(ManagedMemoryPolicy::CUTOFF_ALLOW_REQUIRED_ONLY, + client.memory_policy().priority_cutoff_when_visible); + EXPECT_EQ(4567u, client.memory_policy().bytes_limit_when_not_visible); + EXPECT_EQ(ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING, + client.memory_policy().priority_cutoff_when_not_visible); + EXPECT_FALSE(client.discard_backbuffer_when_not_visible()); + + allocation.suggestHaveBackbuffer = false; + context->SetMemoryAllocation(allocation); + EXPECT_TRUE(client.discard_backbuffer_when_not_visible()); + + allocation.priorityCutoffWhenVisible = + WebGraphicsMemoryAllocation::PriorityCutoffAllowEverything; + allocation.priorityCutoffWhenNotVisible = + WebGraphicsMemoryAllocation::PriorityCutoffAllowVisibleAndNearby; + context->SetMemoryAllocation(allocation); + EXPECT_EQ(ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING, + client.memory_policy().priority_cutoff_when_visible); + EXPECT_EQ(ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE, + client.memory_policy().priority_cutoff_when_not_visible); + + // 0 bytes limit should be ignored. + allocation.bytesLimitWhenVisible = 0; + context->SetMemoryAllocation(allocation); + EXPECT_EQ(1234u, client.memory_policy().bytes_limit_when_visible); +} + +} // namespace +} // namespace cc |