| Index: cc/CCRendererGLTest.cpp
|
| diff --git a/cc/CCRendererGLTest.cpp b/cc/CCRendererGLTest.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..14986e628dce7e8aa1e92bb5c3a99cbe6de9958f
|
| --- /dev/null
|
| +++ b/cc/CCRendererGLTest.cpp
|
| @@ -0,0 +1,428 @@
|
| +// Copyright 2012 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 "config.h"
|
| +#include "CCRendererGL.h"
|
| +
|
| +#include "CCDrawQuad.h"
|
| +#include "CCPrioritizedTextureManager.h"
|
| +#include "CCResourceProvider.h"
|
| +#include "CCSettings.h"
|
| +#include "CCSingleThreadProxy.h"
|
| +#include "CCTestCommon.h"
|
| +#include "FakeWebCompositorOutputSurface.h"
|
| +#include "FakeWebGraphicsContext3D.h"
|
| +#include "GraphicsContext3D.h"
|
| +#include "WebCompositorInitializer.h"
|
| +#include <gmock/gmock.h>
|
| +#include <gtest/gtest.h>
|
| +#include <public/WebTransformationMatrix.h>
|
| +
|
| +using namespace cc;
|
| +using namespace WebKit;
|
| +using namespace WebKitTests;
|
| +
|
| +class FrameCountingMemoryAllocationSettingContext : public FakeWebGraphicsContext3D {
|
| +public:
|
| + FrameCountingMemoryAllocationSettingContext() : m_frame(0) { }
|
| +
|
| + // WebGraphicsContext3D methods.
|
| +
|
| + // This method would normally do a glSwapBuffers under the hood.
|
| + virtual void prepareTexture() { m_frame++; }
|
| + virtual void setMemoryAllocationChangedCallbackCHROMIUM(WebGraphicsMemoryAllocationChangedCallbackCHROMIUM* callback) { m_memoryAllocationChangedCallback = callback; }
|
| + virtual WebString getString(WebKit::WGC3Denum name)
|
| + {
|
| + if (name == GraphicsContext3D::EXTENSIONS)
|
| + return WebString("GL_CHROMIUM_set_visibility GL_CHROMIUM_gpu_memory_manager GL_CHROMIUM_discard_framebuffer");
|
| + return WebString();
|
| + }
|
| +
|
| + // Methods added for test.
|
| + int frameCount() { return m_frame; }
|
| + void setMemoryAllocation(WebGraphicsMemoryAllocation allocation)
|
| + {
|
| + ASSERT(CCProxy::isImplThread());
|
| + // In single threaded mode we expect this callback on main thread.
|
| + DebugScopedSetMainThread main;
|
| + m_memoryAllocationChangedCallback->onMemoryAllocationChanged(allocation);
|
| + }
|
| +
|
| +private:
|
| + int m_frame;
|
| + WebGraphicsMemoryAllocationChangedCallbackCHROMIUM* m_memoryAllocationChangedCallback;
|
| +};
|
| +
|
| +class FakeCCRendererClient : public CCRendererClient {
|
| +public:
|
| + FakeCCRendererClient()
|
| + : m_setFullRootLayerDamageCount(0)
|
| + , m_rootLayer(CCLayerImpl::create(1))
|
| + , m_memoryAllocationLimitBytes(CCPrioritizedTextureManager::defaultMemoryAllocationLimit())
|
| + {
|
| + m_rootLayer->createRenderSurface();
|
| + CCRenderPass::Id renderPassId = m_rootLayer->renderSurface()->renderPassId();
|
| + OwnPtr<CCRenderPass> rootRenderPass = CCRenderPass::create(renderPassId, IntRect(), WebTransformationMatrix());
|
| + m_renderPassesInDrawOrder.append(rootRenderPass.get());
|
| + m_renderPasses.set(renderPassId, rootRenderPass.release());
|
| + }
|
| +
|
| + // CCRendererClient methods.
|
| + virtual const IntSize& deviceViewportSize() const OVERRIDE { static IntSize fakeSize(1, 1); return fakeSize; }
|
| + virtual const CCLayerTreeSettings& settings() const OVERRIDE { static CCLayerTreeSettings fakeSettings; return fakeSettings; }
|
| + virtual void didLoseContext() OVERRIDE { }
|
| + virtual void onSwapBuffersComplete() OVERRIDE { }
|
| + virtual void setFullRootLayerDamage() OVERRIDE { m_setFullRootLayerDamageCount++; }
|
| + virtual void releaseContentsTextures() OVERRIDE { }
|
| + virtual void setMemoryAllocationLimitBytes(size_t bytes) OVERRIDE { m_memoryAllocationLimitBytes = bytes; }
|
| +
|
| + // Methods added for test.
|
| + int setFullRootLayerDamageCount() const { return m_setFullRootLayerDamageCount; }
|
| +
|
| + CCRenderPass* rootRenderPass() { return m_renderPassesInDrawOrder.last(); }
|
| + const CCRenderPassList& renderPassesInDrawOrder() const { return m_renderPassesInDrawOrder; }
|
| + const CCRenderPassIdHashMap& renderPasses() const { return m_renderPasses; }
|
| +
|
| + size_t memoryAllocationLimitBytes() const { return m_memoryAllocationLimitBytes; }
|
| +
|
| +private:
|
| + int m_setFullRootLayerDamageCount;
|
| + DebugScopedSetImplThread m_implThread;
|
| + OwnPtr<CCLayerImpl> m_rootLayer;
|
| + CCRenderPassList m_renderPassesInDrawOrder;
|
| + CCRenderPassIdHashMap m_renderPasses;
|
| + size_t m_memoryAllocationLimitBytes;
|
| +};
|
| +
|
| +class FakeCCRendererGL : public CCRendererGL {
|
| +public:
|
| + FakeCCRendererGL(CCRendererClient* client, CCResourceProvider* resourceProvider) : CCRendererGL(client, resourceProvider) { }
|
| +
|
| + // CCRendererGL methods.
|
| +
|
| + // Changing visibility to public.
|
| + using CCRendererGL::initialize;
|
| + using CCRendererGL::isFramebufferDiscarded;
|
| +};
|
| +
|
| +class CCRendererGLTest : public testing::Test {
|
| +protected:
|
| + CCRendererGLTest()
|
| + : m_suggestHaveBackbufferYes(1, true)
|
| + , m_suggestHaveBackbufferNo(1, false)
|
| + , m_compositorInitializer(0)
|
| + , m_context(FakeWebCompositorOutputSurface::create(adoptPtr(new FrameCountingMemoryAllocationSettingContext())))
|
| + , m_resourceProvider(CCResourceProvider::create(m_context.get(), UnthrottledUploader))
|
| + , m_renderer(&m_mockClient, m_resourceProvider.get())
|
| + {
|
| + }
|
| +
|
| + virtual void SetUp()
|
| + {
|
| + m_renderer.initialize();
|
| + }
|
| +
|
| + void swapBuffers()
|
| + {
|
| + m_renderer.swapBuffers();
|
| + }
|
| +
|
| + FrameCountingMemoryAllocationSettingContext* context() { return static_cast<FrameCountingMemoryAllocationSettingContext*>(m_context->context3D()); }
|
| +
|
| + WebGraphicsMemoryAllocation m_suggestHaveBackbufferYes;
|
| + WebGraphicsMemoryAllocation m_suggestHaveBackbufferNo;
|
| +
|
| + WebCompositorInitializer m_compositorInitializer;
|
| + OwnPtr<CCGraphicsContext> m_context;
|
| + FakeCCRendererClient m_mockClient;
|
| + OwnPtr<CCResourceProvider> m_resourceProvider;
|
| + FakeCCRendererGL m_renderer;
|
| + CCScopedSettings m_scopedSettings;
|
| +};
|
| +
|
| +// Test CCRendererGL discardFramebuffer functionality:
|
| +// Suggest recreating framebuffer when one already exists.
|
| +// Expected: it does nothing.
|
| +TEST_F(CCRendererGLTest, SuggestBackbufferYesWhenItAlreadyExistsShouldDoNothing)
|
| +{
|
| + context()->setMemoryAllocation(m_suggestHaveBackbufferYes);
|
| + EXPECT_EQ(0, m_mockClient.setFullRootLayerDamageCount());
|
| + EXPECT_FALSE(m_renderer.isFramebufferDiscarded());
|
| +
|
| + swapBuffers();
|
| + EXPECT_EQ(1, context()->frameCount());
|
| +}
|
| +
|
| +// Test CCRendererGL discardFramebuffer functionality:
|
| +// Suggest discarding framebuffer when one exists and the renderer is not visible.
|
| +// Expected: it is discarded and damage tracker is reset.
|
| +TEST_F(CCRendererGLTest, SuggestBackbufferNoShouldDiscardBackbufferAndDamageRootLayerWhileNotVisible)
|
| +{
|
| + m_renderer.setVisible(false);
|
| + context()->setMemoryAllocation(m_suggestHaveBackbufferNo);
|
| + EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount());
|
| + EXPECT_TRUE(m_renderer.isFramebufferDiscarded());
|
| +}
|
| +
|
| +// Test CCRendererGL discardFramebuffer functionality:
|
| +// Suggest discarding framebuffer when one exists and the renderer is visible.
|
| +// Expected: the allocation is ignored.
|
| +TEST_F(CCRendererGLTest, SuggestBackbufferNoDoNothingWhenVisible)
|
| +{
|
| + m_renderer.setVisible(true);
|
| + context()->setMemoryAllocation(m_suggestHaveBackbufferNo);
|
| + EXPECT_EQ(0, m_mockClient.setFullRootLayerDamageCount());
|
| + EXPECT_FALSE(m_renderer.isFramebufferDiscarded());
|
| +}
|
| +
|
| +
|
| +// Test CCRendererGL discardFramebuffer functionality:
|
| +// Suggest discarding framebuffer when one does not exist.
|
| +// Expected: it does nothing.
|
| +TEST_F(CCRendererGLTest, SuggestBackbufferNoWhenItDoesntExistShouldDoNothing)
|
| +{
|
| + m_renderer.setVisible(false);
|
| + context()->setMemoryAllocation(m_suggestHaveBackbufferNo);
|
| + EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount());
|
| + EXPECT_TRUE(m_renderer.isFramebufferDiscarded());
|
| +
|
| + context()->setMemoryAllocation(m_suggestHaveBackbufferNo);
|
| + EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount());
|
| + EXPECT_TRUE(m_renderer.isFramebufferDiscarded());
|
| +}
|
| +
|
| +// Test CCRendererGL discardFramebuffer functionality:
|
| +// Begin drawing a frame while a framebuffer is discarded.
|
| +// Expected: will recreate framebuffer.
|
| +TEST_F(CCRendererGLTest, DiscardedBackbufferIsRecreatedForScopeDuration)
|
| +{
|
| + m_renderer.setVisible(false);
|
| + context()->setMemoryAllocation(m_suggestHaveBackbufferNo);
|
| + EXPECT_TRUE(m_renderer.isFramebufferDiscarded());
|
| + EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount());
|
| +
|
| + m_renderer.setVisible(true);
|
| + m_renderer.drawFrame(m_mockClient.renderPassesInDrawOrder(), m_mockClient.renderPasses());
|
| + EXPECT_FALSE(m_renderer.isFramebufferDiscarded());
|
| +
|
| + swapBuffers();
|
| + EXPECT_EQ(1, context()->frameCount());
|
| +}
|
| +
|
| +TEST_F(CCRendererGLTest, FramebufferDiscardedAfterReadbackWhenNotVisible)
|
| +{
|
| + m_renderer.setVisible(false);
|
| + context()->setMemoryAllocation(m_suggestHaveBackbufferNo);
|
| + EXPECT_TRUE(m_renderer.isFramebufferDiscarded());
|
| + EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount());
|
| +
|
| + char pixels[4];
|
| + m_renderer.drawFrame(m_mockClient.renderPassesInDrawOrder(), m_mockClient.renderPasses());
|
| + EXPECT_FALSE(m_renderer.isFramebufferDiscarded());
|
| +
|
| + m_renderer.getFramebufferPixels(pixels, IntRect(0, 0, 1, 1));
|
| + EXPECT_TRUE(m_renderer.isFramebufferDiscarded());
|
| + EXPECT_EQ(2, m_mockClient.setFullRootLayerDamageCount());
|
| +}
|
| +
|
| +class ForbidSynchronousCallContext : public FakeWebGraphicsContext3D {
|
| +public:
|
| + ForbidSynchronousCallContext() { }
|
| +
|
| + virtual bool getActiveAttrib(WebGLId program, WGC3Duint index, ActiveInfo&) { ADD_FAILURE(); return false; }
|
| + virtual bool getActiveUniform(WebGLId program, WGC3Duint index, ActiveInfo&) { ADD_FAILURE(); return false; }
|
| + virtual void getAttachedShaders(WebGLId program, WGC3Dsizei maxCount, WGC3Dsizei* count, WebGLId* shaders) { ADD_FAILURE(); }
|
| + virtual WGC3Dint getAttribLocation(WebGLId program, const WGC3Dchar* name) { ADD_FAILURE(); return 0; }
|
| + virtual void getBooleanv(WGC3Denum pname, WGC3Dboolean* value) { ADD_FAILURE(); }
|
| + virtual void getBufferParameteriv(WGC3Denum target, WGC3Denum pname, WGC3Dint* value) { ADD_FAILURE(); }
|
| + virtual Attributes getContextAttributes() { ADD_FAILURE(); return m_attrs; }
|
| + virtual WGC3Denum getError() { ADD_FAILURE(); return 0; }
|
| + virtual void getFloatv(WGC3Denum pname, WGC3Dfloat* value) { ADD_FAILURE(); }
|
| + virtual void getFramebufferAttachmentParameteriv(WGC3Denum target, WGC3Denum attachment, WGC3Denum pname, WGC3Dint* value) { ADD_FAILURE(); }
|
| + virtual void getIntegerv(WGC3Denum pname, WGC3Dint* value)
|
| + {
|
| + if (pname == GraphicsContext3D::MAX_TEXTURE_SIZE)
|
| + *value = 1024; // MAX_TEXTURE_SIZE is cached client side, so it's OK to query.
|
| + else
|
| + ADD_FAILURE();
|
| + }
|
| +
|
| + // We allow querying the shader compilation and program link status in debug mode, but not release.
|
| + virtual void getProgramiv(WebGLId program, WGC3Denum pname, WGC3Dint* value)
|
| + {
|
| +#ifndef NDEBUG
|
| + *value = 1;
|
| +#else
|
| + ADD_FAILURE();
|
| +#endif
|
| + }
|
| +
|
| + virtual void getShaderiv(WebGLId shader, WGC3Denum pname, WGC3Dint* value)
|
| + {
|
| +#ifndef NDEBUG
|
| + *value = 1;
|
| +#else
|
| + ADD_FAILURE();
|
| +#endif
|
| + }
|
| +
|
| + virtual WebString getString(WGC3Denum name)
|
| + {
|
| + // We allow querying the extension string.
|
| + // FIXME: It'd be better to check that we only do this before starting any other expensive work (like starting a compilation)
|
| + if (name != GraphicsContext3D::EXTENSIONS)
|
| + ADD_FAILURE();
|
| + return WebString();
|
| + }
|
| +
|
| + virtual WebString getProgramInfoLog(WebGLId program) { ADD_FAILURE(); return WebString(); }
|
| + virtual void getRenderbufferParameteriv(WGC3Denum target, WGC3Denum pname, WGC3Dint* value) { ADD_FAILURE(); }
|
| +
|
| + virtual WebString getShaderInfoLog(WebGLId shader) { ADD_FAILURE(); return WebString(); }
|
| + virtual void getShaderPrecisionFormat(WGC3Denum shadertype, WGC3Denum precisiontype, WGC3Dint* range, WGC3Dint* precision) { ADD_FAILURE(); }
|
| + virtual WebString getShaderSource(WebGLId shader) { ADD_FAILURE(); return WebString(); }
|
| + virtual void getTexParameterfv(WGC3Denum target, WGC3Denum pname, WGC3Dfloat* value) { ADD_FAILURE(); }
|
| + virtual void getTexParameteriv(WGC3Denum target, WGC3Denum pname, WGC3Dint* value) { ADD_FAILURE(); }
|
| + virtual void getUniformfv(WebGLId program, WGC3Dint location, WGC3Dfloat* value) { ADD_FAILURE(); }
|
| + virtual void getUniformiv(WebGLId program, WGC3Dint location, WGC3Dint* value) { ADD_FAILURE(); }
|
| + virtual WGC3Dint getUniformLocation(WebGLId program, const WGC3Dchar* name) { ADD_FAILURE(); return 0; }
|
| + virtual void getVertexAttribfv(WGC3Duint index, WGC3Denum pname, WGC3Dfloat* value) { ADD_FAILURE(); }
|
| + virtual void getVertexAttribiv(WGC3Duint index, WGC3Denum pname, WGC3Dint* value) { ADD_FAILURE(); }
|
| + virtual WGC3Dsizeiptr getVertexAttribOffset(WGC3Duint index, WGC3Denum pname) { ADD_FAILURE(); return 0; }
|
| +};
|
| +
|
| +// This test isn't using the same fixture as CCRendererGLTest, and you can't mix TEST() and TEST_F() with the same name, hence LRC2.
|
| +TEST(CCRendererGLTest2, initializationDoesNotMakeSynchronousCalls)
|
| +{
|
| + CCScopedSettings scopedSettings;
|
| + FakeCCRendererClient mockClient;
|
| + OwnPtr<CCGraphicsContext> context(FakeWebCompositorOutputSurface::create(adoptPtr(new ForbidSynchronousCallContext)));
|
| + OwnPtr<CCResourceProvider> resourceProvider(CCResourceProvider::create(context.get(), UnthrottledUploader));
|
| + FakeCCRendererGL renderer(&mockClient, resourceProvider.get());
|
| +
|
| + EXPECT_TRUE(renderer.initialize());
|
| +}
|
| +
|
| +class LoseContextOnFirstGetContext : public FakeWebGraphicsContext3D {
|
| +public:
|
| + LoseContextOnFirstGetContext()
|
| + : m_contextLost(false)
|
| + {
|
| + }
|
| +
|
| + virtual bool makeContextCurrent() OVERRIDE
|
| + {
|
| + return !m_contextLost;
|
| + }
|
| +
|
| + virtual void getProgramiv(WebGLId program, WGC3Denum pname, WGC3Dint* value) OVERRIDE
|
| + {
|
| + m_contextLost = true;
|
| + *value = 0;
|
| + }
|
| +
|
| + virtual void getShaderiv(WebGLId shader, WGC3Denum pname, WGC3Dint* value) OVERRIDE
|
| + {
|
| + m_contextLost = true;
|
| + *value = 0;
|
| + }
|
| +
|
| + virtual WGC3Denum getGraphicsResetStatusARB() OVERRIDE
|
| + {
|
| + return m_contextLost ? 1 : 0;
|
| + }
|
| +
|
| +private:
|
| + bool m_contextLost;
|
| +};
|
| +
|
| +TEST(CCRendererGLTest2, initializationWithQuicklyLostContextDoesNotAssert)
|
| +{
|
| + CCScopedSettings scopedSettings;
|
| + FakeCCRendererClient mockClient;
|
| + OwnPtr<CCGraphicsContext> context(FakeWebCompositorOutputSurface::create(adoptPtr(new LoseContextOnFirstGetContext)));
|
| + OwnPtr<CCResourceProvider> resourceProvider(CCResourceProvider::create(context.get(), UnthrottledUploader));
|
| + FakeCCRendererGL renderer(&mockClient, resourceProvider.get());
|
| +
|
| + renderer.initialize();
|
| +}
|
| +
|
| +class ContextThatDoesNotSupportMemoryManagmentExtensions : public FakeWebGraphicsContext3D {
|
| +public:
|
| + ContextThatDoesNotSupportMemoryManagmentExtensions() { }
|
| +
|
| + // WebGraphicsContext3D methods.
|
| +
|
| + // This method would normally do a glSwapBuffers under the hood.
|
| + virtual void prepareTexture() { }
|
| + virtual void setMemoryAllocationChangedCallbackCHROMIUM(WebGraphicsMemoryAllocationChangedCallbackCHROMIUM* callback) { }
|
| + virtual WebString getString(WebKit::WGC3Denum name) { return WebString(); }
|
| +};
|
| +
|
| +TEST(CCRendererGLTest2, initializationWithoutGpuMemoryManagerExtensionSupportShouldDefaultToNonZeroAllocation)
|
| +{
|
| + FakeCCRendererClient mockClient;
|
| + OwnPtr<CCGraphicsContext> context(FakeWebCompositorOutputSurface::create(adoptPtr(new ContextThatDoesNotSupportMemoryManagmentExtensions)));
|
| + OwnPtr<CCResourceProvider> resourceProvider(CCResourceProvider::create(context.get(), UnthrottledUploader));
|
| + FakeCCRendererGL renderer(&mockClient, resourceProvider.get());
|
| +
|
| + renderer.initialize();
|
| +
|
| + EXPECT_GT(mockClient.memoryAllocationLimitBytes(), 0ul);
|
| +}
|
| +
|
| +class ClearCountingContext : public FakeWebGraphicsContext3D {
|
| +public:
|
| + ClearCountingContext() : m_clear(0) { }
|
| +
|
| + virtual void clear(WGC3Dbitfield)
|
| + {
|
| + m_clear++;
|
| + }
|
| +
|
| + int clearCount() const { return m_clear; }
|
| +
|
| +private:
|
| + int m_clear;
|
| +};
|
| +
|
| +TEST(CCRendererGLTest2, opaqueBackground)
|
| +{
|
| + FakeCCRendererClient mockClient;
|
| + OwnPtr<CCGraphicsContext> ccContext(FakeWebCompositorOutputSurface::create(adoptPtr(new ClearCountingContext)));
|
| + ClearCountingContext* context = static_cast<ClearCountingContext*>(ccContext->context3D());
|
| + OwnPtr<CCResourceProvider> resourceProvider(CCResourceProvider::create(ccContext.get(), UnthrottledUploader));
|
| + FakeCCRendererGL renderer(&mockClient, resourceProvider.get());
|
| +
|
| + mockClient.rootRenderPass()->setHasTransparentBackground(false);
|
| +
|
| + EXPECT_TRUE(renderer.initialize());
|
| +
|
| + renderer.drawFrame(mockClient.renderPassesInDrawOrder(), mockClient.renderPasses());
|
| +
|
| + // On DEBUG builds, render passes with opaque background clear to blue to
|
| + // easily see regions that were not drawn on the screen.
|
| +#if defined(NDEBUG)
|
| + EXPECT_EQ(0, context->clearCount());
|
| +#else
|
| + EXPECT_EQ(1, context->clearCount());
|
| +#endif
|
| +}
|
| +
|
| +TEST(CCRendererGLTest2, transparentBackground)
|
| +{
|
| + FakeCCRendererClient mockClient;
|
| + OwnPtr<CCGraphicsContext> ccContext(FakeWebCompositorOutputSurface::create(adoptPtr(new ClearCountingContext)));
|
| + ClearCountingContext* context = static_cast<ClearCountingContext*>(ccContext->context3D());
|
| + OwnPtr<CCResourceProvider> resourceProvider(CCResourceProvider::create(ccContext.get(), UnthrottledUploader));
|
| + FakeCCRendererGL renderer(&mockClient, resourceProvider.get());
|
| +
|
| + mockClient.rootRenderPass()->setHasTransparentBackground(true);
|
| +
|
| + EXPECT_TRUE(renderer.initialize());
|
| +
|
| + renderer.drawFrame(mockClient.renderPassesInDrawOrder(), mockClient.renderPasses());
|
| +
|
| + EXPECT_EQ(1, context->clearCount());
|
| +}
|
|
|