blob: b586752636da9369694f9d5a5ff17d8034addb76 [file] [log] [blame]
/*
* 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 "platform/graphics/gpu/DrawingBuffer.h"
#include <memory>
#include "cc/resources/single_release_callback.h"
#include "cc/resources/texture_mailbox.h"
#include "cc/test/test_gpu_memory_buffer_manager.h"
#include "gpu/command_buffer/client/gles2_interface_stub.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "platform/graphics/CanvasColorParams.h"
#include "platform/graphics/ImageBuffer.h"
#include "platform/graphics/UnacceleratedImageBufferSurface.h"
#include "platform/graphics/gpu/DrawingBufferTestHelpers.h"
#include "platform/testing/TestingPlatformSupport.h"
#include "platform/wtf/PtrUtil.h"
#include "platform/wtf/RefPtr.h"
#include "public/platform/Platform.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::Test;
using testing::_;
namespace blink {
namespace {
class FakePlatformSupport : public TestingPlatformSupport {
gpu::GpuMemoryBufferManager* GetGpuMemoryBufferManager() override {
return &test_gpu_memory_buffer_manager_;
}
private:
cc::TestGpuMemoryBufferManager test_gpu_memory_buffer_manager_;
};
} // anonymous namespace
class DrawingBufferTest : public Test {
protected:
void SetUp() override { Init(kDisableMultisampling); }
void Init(UseMultisampling use_multisampling) {
IntSize initial_size(kInitialWidth, kInitialHeight);
std::unique_ptr<GLES2InterfaceForTests> gl =
WTF::WrapUnique(new GLES2InterfaceForTests);
std::unique_ptr<WebGraphicsContext3DProviderForTests> provider =
WTF::WrapUnique(
new WebGraphicsContext3DProviderForTests(std::move(gl)));
GLES2InterfaceForTests* gl_ =
static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
drawing_buffer_ = DrawingBufferForTests::Create(
std::move(provider), gl_, initial_size, DrawingBuffer::kPreserve,
use_multisampling);
CHECK(drawing_buffer_);
SetAndSaveRestoreState(false);
}
// Initialize GL state with unusual values, to verify that they are restored.
// The |invert| parameter will reverse all boolean parameters, so that all
// values are tested.
void SetAndSaveRestoreState(bool invert) {
GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
GLboolean scissor_enabled = !invert;
GLfloat clear_color[4] = {0.1, 0.2, 0.3, 0.4};
GLfloat clear_depth = 0.8;
GLint clear_stencil = 37;
GLboolean color_mask[4] = {invert, !invert, !invert, invert};
GLboolean depth_mask = invert;
GLboolean stencil_mask = invert;
GLint pack_alignment = 7;
GLuint active_texture2d_binding = 0xbeef1;
GLuint renderbuffer_binding = 0xbeef2;
GLuint draw_framebuffer_binding = 0xbeef3;
GLuint read_framebuffer_binding = 0xbeef4;
GLuint pixel_unpack_buffer_binding = 0xbeef5;
if (scissor_enabled)
gl_->Enable(GL_SCISSOR_TEST);
else
gl_->Disable(GL_SCISSOR_TEST);
gl_->ClearColor(clear_color[0], clear_color[1], clear_color[2],
clear_color[3]);
gl_->ClearDepthf(clear_depth);
gl_->ClearStencil(clear_stencil);
gl_->ColorMask(color_mask[0], color_mask[1], color_mask[2], color_mask[3]);
gl_->DepthMask(depth_mask);
gl_->StencilMask(stencil_mask);
gl_->PixelStorei(GL_PACK_ALIGNMENT, pack_alignment);
gl_->BindTexture(GL_TEXTURE_2D, active_texture2d_binding);
gl_->BindRenderbuffer(GL_RENDERBUFFER, renderbuffer_binding);
gl_->BindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_framebuffer_binding);
gl_->BindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer_binding);
gl_->BindBuffer(GL_PIXEL_UNPACK_BUFFER, pixel_unpack_buffer_binding);
gl_->SaveState();
}
void VerifyStateWasRestored() {
GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
gl_->VerifyStateHasNotChangedSinceSave();
}
RefPtr<DrawingBufferForTests> drawing_buffer_;
};
class DrawingBufferTestMultisample : public DrawingBufferTest {
protected:
void SetUp() override { Init(kEnableMultisampling); }
};
TEST_F(DrawingBufferTestMultisample, verifyMultisampleResolve) {
// Initial state: already marked changed, multisampled
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->ExplicitResolveOfMultisampleData());
// Resolve the multisample buffer
drawing_buffer_->ResolveAndBindForReadAndDraw();
// After resolve, acknowledge new content
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
// No new content
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
drawing_buffer_->BeginDestruction();
}
TEST_F(DrawingBufferTest, verifyResizingProperlyAffectsMailboxes) {
GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
VerifyStateWasRestored();
cc::TextureMailbox texture_mailbox;
std::unique_ptr<cc::SingleReleaseCallback> release_callback;
IntSize initial_size(kInitialWidth, kInitialHeight);
IntSize alternate_size(kInitialWidth, kAlternateHeight);
// Produce one mailbox at size 100x100.
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox,
&release_callback));
VerifyStateWasRestored();
EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
// Resize to 100x50.
drawing_buffer_->Resize(alternate_size);
VerifyStateWasRestored();
release_callback->Run(gpu::SyncToken(), false /* lostResource */);
VerifyStateWasRestored();
// Produce a mailbox at this size.
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox,
&release_callback));
EXPECT_EQ(alternate_size, gl_->MostRecentlyProducedSize());
VerifyStateWasRestored();
// Reset to initial size.
drawing_buffer_->Resize(initial_size);
VerifyStateWasRestored();
SetAndSaveRestoreState(true);
release_callback->Run(gpu::SyncToken(), false /* lostResource */);
VerifyStateWasRestored();
// Prepare another mailbox and verify that it's the correct size.
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox,
&release_callback));
EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
VerifyStateWasRestored();
// Prepare one final mailbox and verify that it's the correct size.
release_callback->Run(gpu::SyncToken(), false /* lostResource */);
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox,
&release_callback));
VerifyStateWasRestored();
EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
release_callback->Run(gpu::SyncToken(), false /* lostResource */);
drawing_buffer_->BeginDestruction();
}
TEST_F(DrawingBufferTest, verifyDestructionCompleteAfterAllMailboxesReleased) {
bool live = true;
drawing_buffer_->live_ = &live;
cc::TextureMailbox texture_mailbox1;
std::unique_ptr<cc::SingleReleaseCallback> release_callback1;
cc::TextureMailbox texture_mailbox2;
std::unique_ptr<cc::SingleReleaseCallback> release_callback2;
cc::TextureMailbox texture_mailbox3;
std::unique_ptr<cc::SingleReleaseCallback> release_callback3;
IntSize initial_size(kInitialWidth, kInitialHeight);
// Produce mailboxes.
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
drawing_buffer_->ClearFramebuffers(GL_STENCIL_BUFFER_BIT);
VerifyStateWasRestored();
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox1,
&release_callback1));
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
drawing_buffer_->ClearFramebuffers(GL_DEPTH_BUFFER_BIT);
VerifyStateWasRestored();
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox2,
&release_callback2));
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
drawing_buffer_->ClearFramebuffers(GL_COLOR_BUFFER_BIT);
VerifyStateWasRestored();
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox3,
&release_callback3));
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
release_callback1->Run(gpu::SyncToken(), false /* lostResource */);
drawing_buffer_->BeginDestruction();
ASSERT_EQ(live, true);
DrawingBufferForTests* raw_pointer = drawing_buffer_.Get();
drawing_buffer_.Clear();
ASSERT_EQ(live, true);
EXPECT_FALSE(raw_pointer->MarkContentsChanged());
release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
ASSERT_EQ(live, true);
EXPECT_FALSE(raw_pointer->MarkContentsChanged());
release_callback3->Run(gpu::SyncToken(), false /* lostResource */);
ASSERT_EQ(live, false);
}
TEST_F(DrawingBufferTest, verifyDrawingBufferStaysAliveIfResourcesAreLost) {
bool live = true;
drawing_buffer_->live_ = &live;
cc::TextureMailbox texture_mailbox1;
std::unique_ptr<cc::SingleReleaseCallback> release_callback1;
cc::TextureMailbox texture_mailbox2;
std::unique_ptr<cc::SingleReleaseCallback> release_callback2;
cc::TextureMailbox texture_mailbox3;
std::unique_ptr<cc::SingleReleaseCallback> release_callback3;
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox1,
&release_callback1));
VerifyStateWasRestored();
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox2,
&release_callback2));
VerifyStateWasRestored();
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox3,
&release_callback3));
VerifyStateWasRestored();
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
release_callback1->Run(gpu::SyncToken(), true /* lostResource */);
EXPECT_EQ(live, true);
drawing_buffer_->BeginDestruction();
EXPECT_EQ(live, true);
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
EXPECT_EQ(live, true);
DrawingBufferForTests* raw_ptr = drawing_buffer_.Get();
drawing_buffer_.Clear();
EXPECT_EQ(live, true);
EXPECT_FALSE(raw_ptr->MarkContentsChanged());
release_callback3->Run(gpu::SyncToken(), true /* lostResource */);
EXPECT_EQ(live, false);
}
TEST_F(DrawingBufferTest, verifyOnlyOneRecycledMailboxMustBeKept) {
cc::TextureMailbox texture_mailbox1;
std::unique_ptr<cc::SingleReleaseCallback> release_callback1;
cc::TextureMailbox texture_mailbox2;
std::unique_ptr<cc::SingleReleaseCallback> release_callback2;
cc::TextureMailbox texture_mailbox3;
std::unique_ptr<cc::SingleReleaseCallback> release_callback3;
// Produce mailboxes.
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox1,
&release_callback1));
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox2,
&release_callback2));
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox3,
&release_callback3));
// Release mailboxes by specific order; 1, 3, 2.
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
release_callback1->Run(gpu::SyncToken(), false /* lostResource */);
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
release_callback3->Run(gpu::SyncToken(), false /* lostResource */);
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
// The first recycled mailbox must be 2. 1 and 3 were deleted by FIFO order
// because DrawingBuffer never keeps more than one mailbox.
cc::TextureMailbox recycled_texture_mailbox1;
std::unique_ptr<cc::SingleReleaseCallback> recycled_release_callback1;
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(
&recycled_texture_mailbox1, &recycled_release_callback1));
EXPECT_EQ(texture_mailbox2.mailbox(), recycled_texture_mailbox1.mailbox());
// The second recycled mailbox must be a new mailbox.
cc::TextureMailbox recycled_texture_mailbox2;
std::unique_ptr<cc::SingleReleaseCallback> recycled_release_callback2;
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(
&recycled_texture_mailbox2, &recycled_release_callback2));
EXPECT_NE(texture_mailbox1.mailbox(), recycled_texture_mailbox2.mailbox());
EXPECT_NE(texture_mailbox2.mailbox(), recycled_texture_mailbox2.mailbox());
EXPECT_NE(texture_mailbox3.mailbox(), recycled_texture_mailbox2.mailbox());
recycled_release_callback1->Run(gpu::SyncToken(), false /* lostResource */);
recycled_release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
drawing_buffer_->BeginDestruction();
}
TEST_F(DrawingBufferTest, verifyInsertAndWaitSyncTokenCorrectly) {
GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
cc::TextureMailbox texture_mailbox;
std::unique_ptr<cc::SingleReleaseCallback> release_callback;
// Produce mailboxes.
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
EXPECT_EQ(gpu::SyncToken(), gl_->MostRecentlyWaitedSyncToken());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox,
&release_callback));
// PrepareTextureMailbox() does not wait for any sync point.
EXPECT_EQ(gpu::SyncToken(), gl_->MostRecentlyWaitedSyncToken());
gpu::SyncToken wait_sync_token;
gl_->GenSyncTokenCHROMIUM(gl_->InsertFenceSyncCHROMIUM(),
wait_sync_token.GetData());
release_callback->Run(wait_sync_token, false /* lostResource */);
// m_drawingBuffer will wait for the sync point when recycling.
EXPECT_EQ(gpu::SyncToken(), gl_->MostRecentlyWaitedSyncToken());
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox,
&release_callback));
// m_drawingBuffer waits for the sync point when recycling in
// PrepareTextureMailbox().
EXPECT_EQ(wait_sync_token, gl_->MostRecentlyWaitedSyncToken());
drawing_buffer_->BeginDestruction();
gl_->GenSyncTokenCHROMIUM(gl_->InsertFenceSyncCHROMIUM(),
wait_sync_token.GetData());
release_callback->Run(wait_sync_token, false /* lostResource */);
// m_drawingBuffer waits for the sync point because the destruction is in
// progress.
EXPECT_EQ(wait_sync_token, gl_->MostRecentlyWaitedSyncToken());
}
class DrawingBufferImageChromiumTest : public DrawingBufferTest {
protected:
void SetUp() override {
platform_.reset(new ScopedTestingPlatformSupport<FakePlatformSupport>);
IntSize initial_size(kInitialWidth, kInitialHeight);
std::unique_ptr<GLES2InterfaceForTests> gl =
WTF::WrapUnique(new GLES2InterfaceForTests);
std::unique_ptr<WebGraphicsContext3DProviderForTests> provider =
WTF::WrapUnique(
new WebGraphicsContext3DProviderForTests(std::move(gl)));
RuntimeEnabledFeatures::SetWebGLImageChromiumEnabled(true);
GLES2InterfaceForTests* gl_ =
static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
image_id0_ = gl_->NextImageIdToBeCreated();
EXPECT_CALL(*gl_, BindTexImage2DMock(image_id0_)).Times(1);
drawing_buffer_ = DrawingBufferForTests::Create(
std::move(provider), gl_, initial_size, DrawingBuffer::kPreserve,
kDisableMultisampling);
CHECK(drawing_buffer_);
SetAndSaveRestoreState(true);
testing::Mock::VerifyAndClearExpectations(gl_);
}
void TearDown() override {
RuntimeEnabledFeatures::SetWebGLImageChromiumEnabled(false);
platform_.reset();
}
GLuint image_id0_;
std::unique_ptr<ScopedTestingPlatformSupport<FakePlatformSupport>> platform_;
};
TEST_F(DrawingBufferImageChromiumTest, verifyResizingReallocatesImages) {
GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
cc::TextureMailbox texture_mailbox;
std::unique_ptr<cc::SingleReleaseCallback> release_callback;
IntSize initial_size(kInitialWidth, kInitialHeight);
IntSize alternate_size(kInitialWidth, kAlternateHeight);
GLuint image_id1 = gl_->NextImageIdToBeCreated();
EXPECT_CALL(*gl_, BindTexImage2DMock(image_id1)).Times(1);
// Produce one mailbox at size 100x100.
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox,
&release_callback));
EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
EXPECT_TRUE(texture_mailbox.is_overlay_candidate());
testing::Mock::VerifyAndClearExpectations(gl_);
VerifyStateWasRestored();
GLuint image_id2 = gl_->NextImageIdToBeCreated();
EXPECT_CALL(*gl_, BindTexImage2DMock(image_id2)).Times(1);
EXPECT_CALL(*gl_, DestroyImageMock(image_id0_)).Times(1);
EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id0_)).Times(1);
EXPECT_CALL(*gl_, DestroyImageMock(image_id1)).Times(1);
EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id1)).Times(1);
// Resize to 100x50.
drawing_buffer_->Resize(alternate_size);
VerifyStateWasRestored();
release_callback->Run(gpu::SyncToken(), false /* lostResource */);
VerifyStateWasRestored();
testing::Mock::VerifyAndClearExpectations(gl_);
GLuint image_id3 = gl_->NextImageIdToBeCreated();
EXPECT_CALL(*gl_, BindTexImage2DMock(image_id3)).Times(1);
// Produce a mailbox at this size.
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox,
&release_callback));
EXPECT_EQ(alternate_size, gl_->MostRecentlyProducedSize());
EXPECT_TRUE(texture_mailbox.is_overlay_candidate());
testing::Mock::VerifyAndClearExpectations(gl_);
GLuint image_id4 = gl_->NextImageIdToBeCreated();
EXPECT_CALL(*gl_, BindTexImage2DMock(image_id4)).Times(1);
EXPECT_CALL(*gl_, DestroyImageMock(image_id2)).Times(1);
EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id2)).Times(1);
EXPECT_CALL(*gl_, DestroyImageMock(image_id3)).Times(1);
EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id3)).Times(1);
// Reset to initial size.
drawing_buffer_->Resize(initial_size);
VerifyStateWasRestored();
release_callback->Run(gpu::SyncToken(), false /* lostResource */);
VerifyStateWasRestored();
testing::Mock::VerifyAndClearExpectations(gl_);
GLuint image_id5 = gl_->NextImageIdToBeCreated();
EXPECT_CALL(*gl_, BindTexImage2DMock(image_id5)).Times(1);
// Prepare another mailbox and verify that it's the correct size.
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox,
&release_callback));
EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
EXPECT_TRUE(texture_mailbox.is_overlay_candidate());
testing::Mock::VerifyAndClearExpectations(gl_);
// Prepare one final mailbox and verify that it's the correct size.
release_callback->Run(gpu::SyncToken(), false /* lostResource */);
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox,
&release_callback));
EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
EXPECT_TRUE(texture_mailbox.is_overlay_candidate());
release_callback->Run(gpu::SyncToken(), false /* lostResource */);
EXPECT_CALL(*gl_, DestroyImageMock(image_id5)).Times(1);
EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id5)).Times(1);
EXPECT_CALL(*gl_, DestroyImageMock(image_id4)).Times(1);
EXPECT_CALL(*gl_, ReleaseTexImage2DMock(image_id4)).Times(1);
drawing_buffer_->BeginDestruction();
testing::Mock::VerifyAndClearExpectations(gl_);
}
TEST_F(DrawingBufferImageChromiumTest, allocationFailure) {
GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
cc::TextureMailbox texture_mailbox1;
std::unique_ptr<cc::SingleReleaseCallback> release_callback1;
cc::TextureMailbox texture_mailbox2;
std::unique_ptr<cc::SingleReleaseCallback> release_callback2;
cc::TextureMailbox texture_mailbox3;
std::unique_ptr<cc::SingleReleaseCallback> release_callback3;
// Request a mailbox. An image should already be created. Everything works
// as expected.
EXPECT_CALL(*gl_, BindTexImage2DMock(_)).Times(1);
IntSize initial_size(kInitialWidth, kInitialHeight);
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox1,
&release_callback1));
EXPECT_TRUE(texture_mailbox1.is_overlay_candidate());
testing::Mock::VerifyAndClearExpectations(gl_);
VerifyStateWasRestored();
// Force image CHROMIUM creation failure. Request another mailbox. It should
// still be provided, but this time with allowOverlay = false.
gl_->SetCreateImageChromiumFail(true);
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox2,
&release_callback2));
EXPECT_FALSE(texture_mailbox2.is_overlay_candidate());
VerifyStateWasRestored();
// Check that if image CHROMIUM starts working again, mailboxes are
// correctly created with allowOverlay = true.
EXPECT_CALL(*gl_, BindTexImage2DMock(_)).Times(1);
gl_->SetCreateImageChromiumFail(false);
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox3,
&release_callback3));
EXPECT_TRUE(texture_mailbox3.is_overlay_candidate());
testing::Mock::VerifyAndClearExpectations(gl_);
VerifyStateWasRestored();
release_callback1->Run(gpu::SyncToken(), false /* lostResource */);
release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
release_callback3->Run(gpu::SyncToken(), false /* lostResource */);
EXPECT_CALL(*gl_, DestroyImageMock(_)).Times(3);
EXPECT_CALL(*gl_, ReleaseTexImage2DMock(_)).Times(3);
drawing_buffer_->BeginDestruction();
testing::Mock::VerifyAndClearExpectations(gl_);
}
class DepthStencilTrackingGLES2Interface
: public gpu::gles2::GLES2InterfaceStub {
public:
void FramebufferRenderbuffer(GLenum target,
GLenum attachment,
GLenum renderbuffertarget,
GLuint renderbuffer) override {
switch (attachment) {
case GL_DEPTH_ATTACHMENT:
depth_attachment_ = renderbuffer;
break;
case GL_STENCIL_ATTACHMENT:
stencil_attachment_ = renderbuffer;
break;
case GL_DEPTH_STENCIL_ATTACHMENT:
depth_stencil_attachment_ = renderbuffer;
break;
default:
NOTREACHED();
break;
}
}
GLenum CheckFramebufferStatus(GLenum target) override {
return GL_FRAMEBUFFER_COMPLETE;
}
void GetIntegerv(GLenum ptype, GLint* value) override {
switch (ptype) {
case GL_MAX_TEXTURE_SIZE:
*value = 1024;
return;
}
}
const GLubyte* GetString(GLenum type) override {
if (type == GL_EXTENSIONS)
return reinterpret_cast<const GLubyte*>("GL_OES_packed_depth_stencil");
return reinterpret_cast<const GLubyte*>("");
}
void GenRenderbuffers(GLsizei n, GLuint* renderbuffers) override {
for (GLsizei i = 0; i < n; ++i)
renderbuffers[i] = next_gen_renderbuffer_id_++;
}
GLuint StencilAttachment() const { return stencil_attachment_; }
GLuint DepthAttachment() const { return depth_attachment_; }
GLuint DepthStencilAttachment() const { return depth_stencil_attachment_; }
size_t NumAllocatedRenderBuffer() const {
return next_gen_renderbuffer_id_ - 1;
}
private:
GLuint next_gen_renderbuffer_id_ = 1;
GLuint depth_attachment_ = 0;
GLuint stencil_attachment_ = 0;
GLuint depth_stencil_attachment_ = 0;
};
struct DepthStencilTestCase {
DepthStencilTestCase(bool request_stencil,
bool request_depth,
int expected_render_buffers,
const char* const test_case_name)
: request_stencil(request_stencil),
request_depth(request_depth),
expected_render_buffers(expected_render_buffers),
test_case_name(test_case_name) {}
bool request_stencil;
bool request_depth;
size_t expected_render_buffers;
const char* const test_case_name;
};
// This tests that, when the packed depth+stencil extension is supported, and
// either depth or stencil is requested, DrawingBuffer always allocates a single
// packed renderbuffer 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, 0, "neither"),
DepthStencilTestCase(true, false, 1, "stencil only"),
DepthStencilTestCase(false, true, 1, "depth only"),
DepthStencilTestCase(true, true, 1, "both"),
};
for (size_t i = 0; i < WTF_ARRAY_LENGTH(cases); i++) {
SCOPED_TRACE(cases[i].test_case_name);
std::unique_ptr<DepthStencilTrackingGLES2Interface> gl =
WTF::WrapUnique(new DepthStencilTrackingGLES2Interface);
DepthStencilTrackingGLES2Interface* tracking_gl = gl.get();
std::unique_ptr<WebGraphicsContext3DProviderForTests> provider =
WTF::WrapUnique(
new WebGraphicsContext3DProviderForTests(std::move(gl)));
DrawingBuffer::PreserveDrawingBuffer preserve = DrawingBuffer::kPreserve;
bool premultiplied_alpha = false;
bool want_alpha_channel = true;
bool want_depth_buffer = cases[i].request_depth;
bool want_stencil_buffer = cases[i].request_stencil;
bool want_antialiasing = false;
RefPtr<DrawingBuffer> drawing_buffer = DrawingBuffer::Create(
std::move(provider), nullptr, IntSize(10, 10), premultiplied_alpha,
want_alpha_channel, want_depth_buffer, want_stencil_buffer,
want_antialiasing, preserve, DrawingBuffer::kWebGL1,
DrawingBuffer::kAllowChromiumImage, CanvasColorParams());
// When we request a depth or a stencil buffer, we will get both.
EXPECT_EQ(cases[i].request_depth || cases[i].request_stencil,
drawing_buffer->HasDepthBuffer());
EXPECT_EQ(cases[i].request_depth || cases[i].request_stencil,
drawing_buffer->HasStencilBuffer());
EXPECT_EQ(cases[i].expected_render_buffers,
tracking_gl->NumAllocatedRenderBuffer());
if (cases[i].request_depth || cases[i].request_stencil) {
EXPECT_NE(0u, tracking_gl->DepthStencilAttachment());
EXPECT_EQ(0u, tracking_gl->DepthAttachment());
EXPECT_EQ(0u, tracking_gl->StencilAttachment());
} else {
EXPECT_EQ(0u, tracking_gl->DepthStencilAttachment());
EXPECT_EQ(0u, tracking_gl->DepthAttachment());
EXPECT_EQ(0u, tracking_gl->StencilAttachment());
}
drawing_buffer->Resize(IntSize(10, 20));
EXPECT_EQ(cases[i].request_depth || cases[i].request_stencil,
drawing_buffer->HasDepthBuffer());
EXPECT_EQ(cases[i].request_depth || cases[i].request_stencil,
drawing_buffer->HasStencilBuffer());
EXPECT_EQ(cases[i].expected_render_buffers,
tracking_gl->NumAllocatedRenderBuffer());
if (cases[i].request_depth || cases[i].request_stencil) {
EXPECT_NE(0u, tracking_gl->DepthStencilAttachment());
EXPECT_EQ(0u, tracking_gl->DepthAttachment());
EXPECT_EQ(0u, tracking_gl->StencilAttachment());
} else {
EXPECT_EQ(0u, tracking_gl->DepthStencilAttachment());
EXPECT_EQ(0u, tracking_gl->DepthAttachment());
EXPECT_EQ(0u, tracking_gl->StencilAttachment());
}
drawing_buffer->BeginDestruction();
}
}
TEST_F(DrawingBufferTest, verifySetIsHiddenProperlyAffectsMailboxes) {
GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
cc::TextureMailbox texture_mailbox;
std::unique_ptr<cc::SingleReleaseCallback> release_callback;
// Produce mailboxes.
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTextureMailbox(&texture_mailbox,
&release_callback));
gpu::SyncToken wait_sync_token;
gl_->GenSyncTokenCHROMIUM(gl_->InsertFenceSyncCHROMIUM(),
wait_sync_token.GetData());
drawing_buffer_->SetIsHidden(true);
release_callback->Run(wait_sync_token, false /* lostResource */);
// m_drawingBuffer deletes mailbox immediately when hidden.
EXPECT_EQ(wait_sync_token, gl_->MostRecentlyWaitedSyncToken());
drawing_buffer_->BeginDestruction();
}
} // namespace blink