/* * Copyright (C) 2013 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "sky/engine/platform/graphics/gpu/DrawingBuffer.h" #include "gen/sky/platform/RuntimeEnabledFeatures.h" #include "sky/engine/platform/graphics/ImageBuffer.h" #include "sky/engine/platform/graphics/UnacceleratedImageBufferSurface.h" #include "sky/engine/platform/graphics/gpu/Extensions3DUtil.h" #include "sky/engine/platform/graphics/test/MockWebGraphicsContext3D.h" #include "sky/engine/public/platform/Platform.h" #include "sky/engine/public/platform/WebExternalTextureMailbox.h" #include "sky/engine/wtf/RefPtr.h" #include #include using namespace blink; using testing::Test; using testing::_; namespace { class FakeContextEvictionManager : public ContextEvictionManager { public: void forciblyLoseOldestContext(const String& reason) { } IntSize oldestContextSize() { return IntSize(); } }; class WebGraphicsContext3DForTests : public MockWebGraphicsContext3D { public: WebGraphicsContext3DForTests() : MockWebGraphicsContext3D() , m_boundTexture(0) , m_currentMailboxByte(0) , m_mostRecentlyWaitedSyncPoint(0) , m_currentImageId(1) { } virtual void bindTexture(WGC3Denum target, WebGLId texture) { if (target == GL_TEXTURE_2D) { m_boundTexture = texture; } } virtual void texImage2D(WGC3Denum target, WGC3Dint level, WGC3Denum internalformat, WGC3Dsizei width, WGC3Dsizei height, WGC3Dint border, WGC3Denum format, WGC3Denum type, const void* pixels) { if (target == GL_TEXTURE_2D && !level) { m_textureSizes.set(m_boundTexture, IntSize(width, height)); } } virtual void genMailboxCHROMIUM(WGC3Dbyte* mailbox) { ++m_currentMailboxByte; WebExternalTextureMailbox temp; memset(mailbox, m_currentMailboxByte, sizeof(temp.name)); } virtual void produceTextureDirectCHROMIUM(WebGLId texture, WGC3Denum target, const WGC3Dbyte* mailbox) { ASSERT_EQ(target, static_cast(GL_TEXTURE_2D)); ASSERT_TRUE(m_textureSizes.contains(texture)); m_mostRecentlyProducedSize = m_textureSizes.get(texture); } IntSize mostRecentlyProducedSize() { return m_mostRecentlyProducedSize; } virtual unsigned insertSyncPoint() { static unsigned syncPointGenerator = 0; return ++syncPointGenerator; } virtual void waitSyncPoint(unsigned syncPoint) { m_mostRecentlyWaitedSyncPoint = syncPoint; } virtual WGC3Duint createImageCHROMIUM(WGC3Dsizei width, WGC3Dsizei height, WGC3Denum internalformat, WGC3Denum usage) { m_imageSizes.set(m_currentImageId, IntSize(width, height)); return m_currentImageId++; } MOCK_METHOD1(destroyImageMock, void(WGC3Duint imageId)); void destroyImageCHROMIUM(WGC3Duint imageId) { m_imageSizes.remove(imageId); // No textures should be bound to this. ASSERT(m_imageToTextureMap.find(imageId) == m_imageToTextureMap.end()); m_imageSizes.remove(imageId); destroyImageMock(imageId); } MOCK_METHOD1(bindTexImage2DMock, void(WGC3Dint imageId)); void bindTexImage2DCHROMIUM(WGC3Denum target, WGC3Dint imageId) { if (target == GL_TEXTURE_2D) { m_textureSizes.set(m_boundTexture, m_imageSizes.find(imageId)->value); m_imageToTextureMap.set(imageId, m_boundTexture); bindTexImage2DMock(imageId); } } MOCK_METHOD1(releaseTexImage2DMock, void(WGC3Dint imageId)); void releaseTexImage2DCHROMIUM(WGC3Denum target, WGC3Dint imageId) { if (target == GL_TEXTURE_2D) { m_imageSizes.set(m_currentImageId, IntSize()); m_imageToTextureMap.remove(imageId); releaseTexImage2DMock(imageId); } } unsigned mostRecentlyWaitedSyncPoint() { return m_mostRecentlyWaitedSyncPoint; } WGC3Duint nextImageIdToBeCreated() { return m_currentImageId; } private: WebGLId m_boundTexture; HashMap m_textureSizes; WGC3Dbyte m_currentMailboxByte; IntSize m_mostRecentlyProducedSize; unsigned m_mostRecentlyWaitedSyncPoint; WGC3Duint m_currentImageId; HashMap m_imageSizes; HashMap m_imageToTextureMap; }; static const int initialWidth = 100; static const int initialHeight = 100; static const int alternateHeight = 50; class DrawingBufferForTests : public DrawingBuffer { public: static PassRefPtr create(PassOwnPtr context, const IntSize& size, PreserveDrawingBuffer preserve, PassRefPtr contextEvictionManager) { OwnPtr extensionsUtil = Extensions3DUtil::create(context.get()); RefPtr drawingBuffer = adoptRef(new DrawingBufferForTests(context, extensionsUtil.release(), preserve, contextEvictionManager)); if (!drawingBuffer->initialize(size)) { drawingBuffer->beginDestruction(); return PassRefPtr(); } return drawingBuffer.release(); } DrawingBufferForTests(PassOwnPtr context, PassOwnPtr extensionsUtil, PreserveDrawingBuffer preserve, PassRefPtr contextEvictionManager) : DrawingBuffer(context, extensionsUtil, false /* multisampleExtensionSupported */, false /* packedDepthStencilExtensionSupported */, preserve, WebGraphicsContext3D::Attributes(), contextEvictionManager) , m_live(0) { } virtual ~DrawingBufferForTests() { if (m_live) *m_live = false; } bool* m_live; }; class DrawingBufferTest : public Test { protected: void SetUp() override { RefPtr contextEvictionManager = adoptRef(new FakeContextEvictionManager()); OwnPtr context = adoptPtr(new WebGraphicsContext3DForTests); m_context = context.get(); m_drawingBuffer = DrawingBufferForTests::create(context.release(), IntSize(initialWidth, initialHeight), DrawingBuffer::Preserve, contextEvictionManager.release()); } WebGraphicsContext3DForTests* webContext() { return m_context; } WebGraphicsContext3DForTests* m_context; RefPtr m_drawingBuffer; }; TEST_F(DrawingBufferTest, testPaintRenderingResultsToCanvas) { OwnPtr imageBufferSurface = adoptPtr(new UnacceleratedImageBufferSurface(IntSize(initialWidth, initialHeight))); EXPECT_FALSE(!imageBufferSurface); EXPECT_TRUE(imageBufferSurface->isValid()); OwnPtr imageBuffer = ImageBuffer::create(imageBufferSurface.release()); EXPECT_FALSE(!imageBuffer); EXPECT_FALSE(imageBuffer->isAccelerated()); EXPECT_FALSE(imageBuffer->bitmap().isNull()); m_drawingBuffer->paintRenderingResultsToCanvas(imageBuffer.get()); EXPECT_FALSE(imageBuffer->isAccelerated()); EXPECT_FALSE(imageBuffer->bitmap().isNull()); m_drawingBuffer->beginDestruction(); } TEST_F(DrawingBufferTest, verifyResizingProperlyAffectsMailboxes) { WebExternalTextureMailbox mailbox; IntSize initialSize(initialWidth, initialHeight); IntSize alternateSize(initialWidth, alternateHeight); // Produce one mailbox at size 100x100. m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); EXPECT_EQ(initialSize, webContext()->mostRecentlyProducedSize()); // Resize to 100x50. m_drawingBuffer->reset(IntSize(initialWidth, alternateHeight)); m_drawingBuffer->mailboxReleased(mailbox, false); // Produce a mailbox at this size. m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); EXPECT_EQ(alternateSize, webContext()->mostRecentlyProducedSize()); // Reset to initial size. m_drawingBuffer->reset(IntSize(initialWidth, initialHeight)); m_drawingBuffer->mailboxReleased(mailbox, false); // Prepare another mailbox and verify that it's the correct size. m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); EXPECT_EQ(initialSize, webContext()->mostRecentlyProducedSize()); // Prepare one final mailbox and verify that it's the correct size. m_drawingBuffer->mailboxReleased(mailbox, false); m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); EXPECT_EQ(initialSize, webContext()->mostRecentlyProducedSize()); m_drawingBuffer->beginDestruction(); } TEST_F(DrawingBufferTest, verifyDestructionCompleteAfterAllMailboxesReleased) { bool live = true; m_drawingBuffer->m_live = &live; WebExternalTextureMailbox mailbox1; WebExternalTextureMailbox mailbox2; WebExternalTextureMailbox mailbox3; IntSize initialSize(initialWidth, initialHeight); // Produce mailboxes. m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox1, 0)); m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox2, 0)); m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox3, 0)); m_drawingBuffer->markContentsChanged(); m_drawingBuffer->mailboxReleased(mailbox1, false); m_drawingBuffer->beginDestruction(); EXPECT_EQ(live, true); DrawingBufferForTests* weakPointer = m_drawingBuffer.get(); m_drawingBuffer.clear(); EXPECT_EQ(live, true); weakPointer->markContentsChanged(); weakPointer->mailboxReleased(mailbox2, false); EXPECT_EQ(live, true); weakPointer->markContentsChanged(); weakPointer->mailboxReleased(mailbox3, false); EXPECT_EQ(live, false); } TEST_F(DrawingBufferTest, verifyDrawingBufferStaysAliveIfResourcesAreLost) { bool live = true; m_drawingBuffer->m_live = &live; WebExternalTextureMailbox mailbox1; WebExternalTextureMailbox mailbox2; WebExternalTextureMailbox mailbox3; m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox1, 0)); m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox2, 0)); m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox3, 0)); m_drawingBuffer->markContentsChanged(); m_drawingBuffer->mailboxReleased(mailbox1, true); EXPECT_EQ(live, true); m_drawingBuffer->beginDestruction(); EXPECT_EQ(live, true); m_drawingBuffer->markContentsChanged(); m_drawingBuffer->mailboxReleased(mailbox2, false); EXPECT_EQ(live, true); DrawingBufferForTests* weakPtr = m_drawingBuffer.get(); m_drawingBuffer.clear(); EXPECT_EQ(live, true); weakPtr->markContentsChanged(); weakPtr->mailboxReleased(mailbox3, true); EXPECT_EQ(live, false); } class TextureMailboxWrapper { public: explicit TextureMailboxWrapper(const WebExternalTextureMailbox& mailbox) : m_mailbox(mailbox) { } bool operator==(const TextureMailboxWrapper& other) const { return !memcmp(m_mailbox.name, other.m_mailbox.name, sizeof(m_mailbox.name)); } bool operator!=(const TextureMailboxWrapper& other) const { return !(*this == other); } private: WebExternalTextureMailbox m_mailbox; }; TEST_F(DrawingBufferTest, verifyOnlyOneRecycledMailboxMustBeKept) { WebExternalTextureMailbox mailbox1; WebExternalTextureMailbox mailbox2; WebExternalTextureMailbox mailbox3; // Produce mailboxes. m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox1, 0)); m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox2, 0)); m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox3, 0)); // Release mailboxes by specific order; 1, 3, 2. m_drawingBuffer->markContentsChanged(); m_drawingBuffer->mailboxReleased(mailbox1, false); m_drawingBuffer->markContentsChanged(); m_drawingBuffer->mailboxReleased(mailbox3, false); m_drawingBuffer->markContentsChanged(); m_drawingBuffer->mailboxReleased(mailbox2, false); // The first recycled mailbox must be 2. 1 and 3 were deleted by FIFO order because // DrawingBuffer never keeps more than one mailbox. WebExternalTextureMailbox recycledMailbox1; m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&recycledMailbox1, 0)); EXPECT_EQ(TextureMailboxWrapper(mailbox2), TextureMailboxWrapper(recycledMailbox1)); // The second recycled mailbox must be a new mailbox. WebExternalTextureMailbox recycledMailbox2; m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&recycledMailbox2, 0)); EXPECT_NE(TextureMailboxWrapper(mailbox1), TextureMailboxWrapper(recycledMailbox2)); EXPECT_NE(TextureMailboxWrapper(mailbox2), TextureMailboxWrapper(recycledMailbox2)); EXPECT_NE(TextureMailboxWrapper(mailbox3), TextureMailboxWrapper(recycledMailbox2)); m_drawingBuffer->mailboxReleased(recycledMailbox1, false); m_drawingBuffer->mailboxReleased(recycledMailbox2, false); m_drawingBuffer->beginDestruction(); } TEST_F(DrawingBufferTest, verifyInsertAndWaitSyncPointCorrectly) { WebExternalTextureMailbox mailbox; // Produce mailboxes. m_drawingBuffer->markContentsChanged(); EXPECT_EQ(0u, webContext()->mostRecentlyWaitedSyncPoint()); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); // prepareMailbox() does not wait for any sync point. EXPECT_EQ(0u, webContext()->mostRecentlyWaitedSyncPoint()); unsigned waitSyncPoint = webContext()->insertSyncPoint(); mailbox.syncPoint = waitSyncPoint; m_drawingBuffer->mailboxReleased(mailbox, false); // m_drawingBuffer will wait for the sync point when recycling. EXPECT_EQ(0u, webContext()->mostRecentlyWaitedSyncPoint()); m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); // m_drawingBuffer waits for the sync point when recycling in prepareMailbox(). EXPECT_EQ(waitSyncPoint, webContext()->mostRecentlyWaitedSyncPoint()); m_drawingBuffer->beginDestruction(); waitSyncPoint = webContext()->insertSyncPoint(); mailbox.syncPoint = waitSyncPoint; m_drawingBuffer->mailboxReleased(mailbox, false); // m_drawingBuffer waits for the sync point because the destruction is in progress. EXPECT_EQ(waitSyncPoint, webContext()->mostRecentlyWaitedSyncPoint()); } class DrawingBufferImageChromiumTest : public DrawingBufferTest { protected: void SetUp() override { RefPtr contextEvictionManager = adoptRef(new FakeContextEvictionManager()); OwnPtr context = adoptPtr(new WebGraphicsContext3DForTests); m_context = context.get(); RuntimeEnabledFeatures::setWebGLImageChromiumEnabled(true); m_imageId0 = webContext()->nextImageIdToBeCreated(); EXPECT_CALL(*webContext(), bindTexImage2DMock(m_imageId0)).Times(1); m_drawingBuffer = DrawingBufferForTests::create(context.release(), IntSize(initialWidth, initialHeight), DrawingBuffer::Preserve, contextEvictionManager.release()); testing::Mock::VerifyAndClearExpectations(webContext()); } void TearDown() override { RuntimeEnabledFeatures::setWebGLImageChromiumEnabled(false); } WGC3Duint m_imageId0; }; TEST_F(DrawingBufferImageChromiumTest, verifyResizingReallocatesImages) { WebExternalTextureMailbox mailbox; IntSize initialSize(initialWidth, initialHeight); IntSize alternateSize(initialWidth, alternateHeight); WGC3Duint m_imageId1 = webContext()->nextImageIdToBeCreated(); EXPECT_CALL(*webContext(), bindTexImage2DMock(m_imageId1)).Times(1); // Produce one mailbox at size 100x100. m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); EXPECT_EQ(initialSize, webContext()->mostRecentlyProducedSize()); EXPECT_TRUE(mailbox.allowOverlay); testing::Mock::VerifyAndClearExpectations(webContext()); WGC3Duint m_imageId2 = webContext()->nextImageIdToBeCreated(); EXPECT_CALL(*webContext(), bindTexImage2DMock(m_imageId2)).Times(1); EXPECT_CALL(*webContext(), destroyImageMock(m_imageId0)).Times(1); EXPECT_CALL(*webContext(), releaseTexImage2DMock(m_imageId0)).Times(1); // Resize to 100x50. m_drawingBuffer->reset(IntSize(initialWidth, alternateHeight)); m_drawingBuffer->mailboxReleased(mailbox, false); testing::Mock::VerifyAndClearExpectations(webContext()); WGC3Duint m_imageId3 = webContext()->nextImageIdToBeCreated(); EXPECT_CALL(*webContext(), bindTexImage2DMock(m_imageId3)).Times(1); EXPECT_CALL(*webContext(), destroyImageMock(m_imageId1)).Times(1); EXPECT_CALL(*webContext(), releaseTexImage2DMock(m_imageId1)).Times(1); // Produce a mailbox at this size. m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); EXPECT_EQ(alternateSize, webContext()->mostRecentlyProducedSize()); EXPECT_TRUE(mailbox.allowOverlay); testing::Mock::VerifyAndClearExpectations(webContext()); WGC3Duint m_imageId4 = webContext()->nextImageIdToBeCreated(); EXPECT_CALL(*webContext(), bindTexImage2DMock(m_imageId4)).Times(1); EXPECT_CALL(*webContext(), destroyImageMock(m_imageId2)).Times(1); EXPECT_CALL(*webContext(), releaseTexImage2DMock(m_imageId2)).Times(1); // Reset to initial size. m_drawingBuffer->reset(IntSize(initialWidth, initialHeight)); m_drawingBuffer->mailboxReleased(mailbox, false); testing::Mock::VerifyAndClearExpectations(webContext()); WGC3Duint m_imageId5 = webContext()->nextImageIdToBeCreated(); EXPECT_CALL(*webContext(), bindTexImage2DMock(m_imageId5)).Times(1); EXPECT_CALL(*webContext(), destroyImageMock(m_imageId3)).Times(1); EXPECT_CALL(*webContext(), releaseTexImage2DMock(m_imageId3)).Times(1); // Prepare another mailbox and verify that it's the correct size. m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); EXPECT_EQ(initialSize, webContext()->mostRecentlyProducedSize()); EXPECT_TRUE(mailbox.allowOverlay); testing::Mock::VerifyAndClearExpectations(webContext()); // Prepare one final mailbox and verify that it's the correct size. m_drawingBuffer->mailboxReleased(mailbox, false); m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); EXPECT_EQ(initialSize, webContext()->mostRecentlyProducedSize()); EXPECT_TRUE(mailbox.allowOverlay); m_drawingBuffer->mailboxReleased(mailbox, false); EXPECT_CALL(*webContext(), destroyImageMock(m_imageId5)).Times(1); EXPECT_CALL(*webContext(), releaseTexImage2DMock(m_imageId5)).Times(1); EXPECT_CALL(*webContext(), destroyImageMock(m_imageId4)).Times(1); EXPECT_CALL(*webContext(), releaseTexImage2DMock(m_imageId4)).Times(1); m_drawingBuffer->beginDestruction(); testing::Mock::VerifyAndClearExpectations(webContext()); } class DepthStencilTrackingContext : public MockWebGraphicsContext3D { public: DepthStencilTrackingContext() : m_nextRenderBufferId(1) , m_stencilAttachment(0) , m_depthAttachment(0) { } virtual ~DepthStencilTrackingContext() { } int numAllocatedRenderBuffer() const { return m_nextRenderBufferId - 1; } WebGLId stencilAttachment() const { return m_stencilAttachment; } WebGLId depthAttachment() const { return m_depthAttachment; } virtual WebString getString(WGC3Denum type) override { if (type == GL_EXTENSIONS) { return WebString::fromUTF8("GL_OES_packed_depth_stencil"); } return WebString(); } virtual WebGLId createRenderbuffer() override { return ++m_nextRenderBufferId; } virtual void framebufferRenderbuffer(WGC3Denum target, WGC3Denum attachment, WGC3Denum renderbuffertarget, WebGLId renderbuffer) override { if (attachment == GL_STENCIL_ATTACHMENT) { m_stencilAttachment = renderbuffer; } else { m_depthAttachment = renderbuffer; } } virtual void getIntegerv(WGC3Denum ptype, WGC3Dint* value) override { switch (ptype) { case GL_DEPTH_BITS: *value = m_depthAttachment ? 24 : 0; return; case GL_STENCIL_BITS: *value = m_stencilAttachment ? 8 : 0; return; } MockWebGraphicsContext3D::getIntegerv(ptype, value); } private: WebGLId m_nextRenderBufferId; WebGLId m_stencilAttachment; WebGLId m_depthAttachment; }; struct DepthStencilTestCase { DepthStencilTestCase(bool requestStencil, bool requestDepth, int expectedRenderBuffers, bool expectDepthStencil, const char* const testCaseName) : requestStencil(requestStencil) , requestDepth(requestDepth) , expectDepthStencil(expectDepthStencil) , expectedRenderBuffers(expectedRenderBuffers) , testCaseName(testCaseName) { } bool requestStencil; bool requestDepth; bool expectDepthStencil; int expectedRenderBuffers; const char* const testCaseName; }; // This tests that when the packed depth+stencil extension is supported DrawingBuffer always allocates // a single packed renderbuffer if either is requested and properly computes the actual context attributes // as defined by WebGL. We always allocate a packed buffer in this case since many desktop OpenGL drivers // that support this extension do not consider a framebuffer with only a depth or a stencil buffer attached // to be complete. TEST(DrawingBufferDepthStencilTest, packedDepthStencilSupported) { DepthStencilTestCase cases[] = { DepthStencilTestCase(false, false, false, 0, "neither"), DepthStencilTestCase(true, false, true, 1, "stencil only"), DepthStencilTestCase(false, true, true, 1, "depth only"), DepthStencilTestCase(true, true, true, 1, "both"), }; for (size_t i = 0; i < arraysize(cases); i++) { SCOPED_TRACE(cases[i].testCaseName); OwnPtr context = adoptPtr(new DepthStencilTrackingContext); DepthStencilTrackingContext* trackingContext = context.get(); DrawingBuffer::PreserveDrawingBuffer preserve = DrawingBuffer::Preserve; RefPtr contextEvictionManager = adoptRef(new FakeContextEvictionManager); WebGraphicsContext3D::Attributes requestedAttributes; requestedAttributes.stencil = cases[i].requestStencil; requestedAttributes.depth = cases[i].requestDepth; RefPtr drawingBuffer = DrawingBuffer::create(context.release(), IntSize(10, 10), preserve, requestedAttributes, contextEvictionManager); EXPECT_EQ(cases[i].requestDepth, drawingBuffer->getActualAttributes().depth); EXPECT_EQ(cases[i].requestStencil, drawingBuffer->getActualAttributes().stencil); EXPECT_EQ(cases[i].expectedRenderBuffers, trackingContext->numAllocatedRenderBuffer()); if (cases[i].expectDepthStencil) { EXPECT_EQ(trackingContext->stencilAttachment(), trackingContext->depthAttachment()); } else if (cases[i].requestStencil || cases[i].requestDepth) { EXPECT_NE(trackingContext->stencilAttachment(), trackingContext->depthAttachment()); } else { EXPECT_EQ(0u, trackingContext->stencilAttachment()); EXPECT_EQ(0u, trackingContext->depthAttachment()); } drawingBuffer->reset(IntSize(10, 20)); EXPECT_EQ(cases[i].requestDepth, drawingBuffer->getActualAttributes().depth); EXPECT_EQ(cases[i].requestStencil, drawingBuffer->getActualAttributes().stencil); EXPECT_EQ(cases[i].expectedRenderBuffers, trackingContext->numAllocatedRenderBuffer()); if (cases[i].expectDepthStencil) { EXPECT_EQ(trackingContext->stencilAttachment(), trackingContext->depthAttachment()); } else if (cases[i].requestStencil || cases[i].requestDepth) { EXPECT_NE(trackingContext->stencilAttachment(), trackingContext->depthAttachment()); } else { EXPECT_EQ(0u, trackingContext->stencilAttachment()); EXPECT_EQ(0u, trackingContext->depthAttachment()); } drawingBuffer->beginDestruction(); } } TEST_F(DrawingBufferTest, verifySetIsHiddenProperlyAffectsMailboxes) { blink::WebExternalTextureMailbox mailbox; // Produce mailboxes. m_drawingBuffer->markContentsChanged(); EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); unsigned waitSyncPoint = webContext()->insertSyncPoint(); mailbox.syncPoint = waitSyncPoint; m_drawingBuffer->setIsHidden(true); m_drawingBuffer->mailboxReleased(mailbox); // m_drawingBuffer deletes mailbox immediately when hidden. EXPECT_EQ(waitSyncPoint, webContext()->mostRecentlyWaitedSyncPoint()); m_drawingBuffer->beginDestruction(); } } // namespace