summaryrefslogtreecommitdiff
path: root/chromium/cc/output/output_surface_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/cc/output/output_surface_unittest.cc')
-rw-r--r--chromium/cc/output/output_surface_unittest.cc428
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