| /* |
| * 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/config.h" |
| |
| #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 <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| 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<WGC3Denum>(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<WebGLId, IntSize> m_textureSizes; |
| WGC3Dbyte m_currentMailboxByte; |
| IntSize m_mostRecentlyProducedSize; |
| unsigned m_mostRecentlyWaitedSyncPoint; |
| WGC3Duint m_currentImageId; |
| HashMap<WGC3Duint, IntSize> m_imageSizes; |
| HashMap<WGC3Duint, WebGLId> m_imageToTextureMap; |
| }; |
| |
| static const int initialWidth = 100; |
| static const int initialHeight = 100; |
| static const int alternateHeight = 50; |
| |
| class DrawingBufferForTests : public DrawingBuffer { |
| public: |
| static PassRefPtr<DrawingBufferForTests> create(PassOwnPtr<WebGraphicsContext3D> context, |
| const IntSize& size, PreserveDrawingBuffer preserve, PassRefPtr<ContextEvictionManager> contextEvictionManager) |
| { |
| OwnPtr<Extensions3DUtil> extensionsUtil = Extensions3DUtil::create(context.get()); |
| RefPtr<DrawingBufferForTests> drawingBuffer = |
| adoptRef(new DrawingBufferForTests(context, extensionsUtil.release(), preserve, contextEvictionManager)); |
| if (!drawingBuffer->initialize(size)) { |
| drawingBuffer->beginDestruction(); |
| return PassRefPtr<DrawingBufferForTests>(); |
| } |
| return drawingBuffer.release(); |
| } |
| |
| DrawingBufferForTests(PassOwnPtr<WebGraphicsContext3D> context, |
| PassOwnPtr<Extensions3DUtil> extensionsUtil, |
| PreserveDrawingBuffer preserve, |
| PassRefPtr<ContextEvictionManager> 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: |
| virtual void SetUp() |
| { |
| RefPtr<FakeContextEvictionManager> contextEvictionManager = adoptRef(new FakeContextEvictionManager()); |
| OwnPtr<WebGraphicsContext3DForTests> 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<DrawingBufferForTests> m_drawingBuffer; |
| }; |
| |
| TEST_F(DrawingBufferTest, testPaintRenderingResultsToCanvas) |
| { |
| OwnPtr<ImageBufferSurface> imageBufferSurface = adoptPtr(new UnacceleratedImageBufferSurface(IntSize(initialWidth, initialHeight))); |
| EXPECT_FALSE(!imageBufferSurface); |
| EXPECT_TRUE(imageBufferSurface->isValid()); |
| OwnPtr<ImageBuffer> 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: |
| virtual void SetUp() |
| { |
| RefPtr<FakeContextEvictionManager> contextEvictionManager = adoptRef(new FakeContextEvictionManager()); |
| OwnPtr<WebGraphicsContext3DForTests> 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()); |
| } |
| |
| virtual void TearDown() |
| { |
| 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<DepthStencilTrackingContext> context = adoptPtr(new DepthStencilTrackingContext); |
| DepthStencilTrackingContext* trackingContext = context.get(); |
| DrawingBuffer::PreserveDrawingBuffer preserve = DrawingBuffer::Preserve; |
| RefPtr<ContextEvictionManager> contextEvictionManager = adoptRef(new FakeContextEvictionManager); |
| |
| WebGraphicsContext3D::Attributes requestedAttributes; |
| requestedAttributes.stencil = cases[i].requestStencil; |
| requestedAttributes.depth = cases[i].requestDepth; |
| RefPtr<DrawingBuffer> 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 |