flutter_flutter/engine/platform/graphics/gpu/DrawingBufferTest.cpp
Eric Seidel e0fd75b5ab Make absolute and sort all Sky headers
This caused us to lose our gn check certification. :(

Turns out gn check was just ignoring all the header
paths it didn't understand and so gn check passing
for sky wasn't meaning much.  I tried to straighten
out some of the mess in this CL, but its going to take
several more rounds of massaging before gn check
passes again.  On the bright side (almost) all of
our headers are absolute now.  Turns out my script
(attached to the bug) didn't notice ../ includes
but I'll fix that in the next patch.

R=abarth@chromium.org
BUG=435361

Review URL: https://codereview.chromium.org/746023002
2014-11-20 17:42:05 -08:00

680 lines
26 KiB
C++

/*
* 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