blob: 655e3765a3d15349c4bc0868a26debff8f31aee4 [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 "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
#include <memory>
#include "base/memory/scoped_refptr.h"
#include "base/stl_util.h"
#include "components/viz/common/resources/single_release_callback.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "components/viz/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 "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h"
#include "third_party/blink/renderer/platform/graphics/test/gpu_memory_buffer_test_platform.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "v8/include/v8.h"
using testing::Test;
using testing::_;
namespace blink {
class DrawingBufferTest : public Test {
protected:
void SetUp() override { Init(kDisableMultisampling); }
void Init(UseMultisampling use_multisampling) {
IntSize initial_size(kInitialWidth, kInitialHeight);
auto gl = std::make_unique<GLES2InterfaceForTests>();
auto provider =
std::make_unique<WebGraphicsContext3DProviderForTests>(std::move(gl));
GLES2InterfaceForTests* gl_ =
static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
bool gpu_compositing = true;
drawing_buffer_ = DrawingBufferForTests::Create(
std::move(provider), gpu_compositing, 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();
}
scoped_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, VerifyResizingProperlyAffectsResources) {
GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
VerifyStateWasRestored();
viz::TransferableResource resource;
std::unique_ptr<viz::SingleReleaseCallback> release_callback;
IntSize initial_size(kInitialWidth, kInitialHeight);
IntSize alternate_size(kInitialWidth, kAlternateHeight);
// Produce one resource at size 100x100.
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
&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 resource at this size.
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
&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 resource and verify that it's the correct size.
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
&release_callback));
EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
VerifyStateWasRestored();
// Prepare one final resource and verify that it's the correct size.
release_callback->Run(gpu::SyncToken(), false /* lostResource */);
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
&release_callback));
VerifyStateWasRestored();
EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
release_callback->Run(gpu::SyncToken(), false /* lostResource */);
drawing_buffer_->BeginDestruction();
}
TEST_F(DrawingBufferTest, VerifyDestructionCompleteAfterAllResourceReleased) {
bool live = true;
drawing_buffer_->live_ = &live;
viz::TransferableResource resource1;
std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
viz::TransferableResource resource2;
std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
viz::TransferableResource resource3;
std::unique_ptr<viz::SingleReleaseCallback> release_callback3;
// Produce resources.
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
drawing_buffer_->ClearFramebuffers(GL_STENCIL_BUFFER_BIT);
VerifyStateWasRestored();
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource1,
&release_callback1));
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
drawing_buffer_->ClearFramebuffers(GL_DEPTH_BUFFER_BIT);
VerifyStateWasRestored();
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource2,
&release_callback2));
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
drawing_buffer_->ClearFramebuffers(GL_COLOR_BUFFER_BIT);
VerifyStateWasRestored();
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3,
&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_ = nullptr;
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;
viz::TransferableResource resource1;
std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
viz::TransferableResource resource2;
std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
viz::TransferableResource resource3;
std::unique_ptr<viz::SingleReleaseCallback> release_callback3;
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource1,
&release_callback1));
VerifyStateWasRestored();
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource2,
&release_callback2));
VerifyStateWasRestored();
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3,
&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_ = nullptr;
EXPECT_EQ(live, true);
EXPECT_FALSE(raw_ptr->MarkContentsChanged());
release_callback3->Run(gpu::SyncToken(), true /* lostResource */);
EXPECT_EQ(live, false);
}
TEST_F(DrawingBufferTest, VerifyOnlyOneRecycledResourceMustBeKept) {
viz::TransferableResource resource1;
std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
viz::TransferableResource resource2;
std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
viz::TransferableResource resource3;
std::unique_ptr<viz::SingleReleaseCallback> release_callback3;
// Produce resources.
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource1,
&release_callback1));
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource2,
&release_callback2));
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3,
&release_callback3));
// Release resources 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 resource must be 2. 1 and 3 were deleted by FIFO order
// because DrawingBuffer never keeps more than one resource.
viz::TransferableResource recycled_resource1;
std::unique_ptr<viz::SingleReleaseCallback> recycled_release_callback1;
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(
nullptr, &recycled_resource1, &recycled_release_callback1));
EXPECT_EQ(resource2.mailbox_holder.mailbox,
recycled_resource1.mailbox_holder.mailbox);
// The second recycled resource must be a new resource.
viz::TransferableResource recycled_resource2;
std::unique_ptr<viz::SingleReleaseCallback> recycled_release_callback2;
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(
nullptr, &recycled_resource2, &recycled_release_callback2));
EXPECT_NE(resource1.mailbox_holder.mailbox,
recycled_resource2.mailbox_holder.mailbox);
EXPECT_NE(resource2.mailbox_holder.mailbox,
recycled_resource2.mailbox_holder.mailbox);
EXPECT_NE(resource3.mailbox_holder.mailbox,
recycled_resource2.mailbox_holder.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();
viz::TransferableResource resource;
std::unique_ptr<viz::SingleReleaseCallback> release_callback;
// Produce resources.
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
EXPECT_EQ(gpu::SyncToken(), gl_->MostRecentlyWaitedSyncToken());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
&release_callback));
// PrepareTransferableResource() does not wait for any sync point.
EXPECT_EQ(gpu::SyncToken(), gl_->MostRecentlyWaitedSyncToken());
gpu::SyncToken wait_sync_token;
gl_->GenSyncTokenCHROMIUM(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_->PrepareTransferableResource(nullptr, &resource,
&release_callback));
// m_drawingBuffer waits for the sync point when recycling in
// PrepareTransferableResource().
EXPECT_EQ(wait_sync_token, gl_->MostRecentlyWaitedSyncToken());
drawing_buffer_->BeginDestruction();
gl_->GenSyncTokenCHROMIUM(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,
private ScopedWebGLImageChromiumForTest {
public:
DrawingBufferImageChromiumTest() : ScopedWebGLImageChromiumForTest(true) {}
protected:
void SetUp() override {
platform_.reset(
new ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform>);
IntSize initial_size(kInitialWidth, kInitialHeight);
auto gl = std::make_unique<GLES2InterfaceForTests>();
auto provider =
std::make_unique<WebGraphicsContext3DProviderForTests>(std::move(gl));
GLES2InterfaceForTests* gl_ =
static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
image_id0_ = gl_->NextImageIdToBeCreated();
EXPECT_CALL(*gl_, BindTexImage2DMock(image_id0_)).Times(1);
bool gpu_compositing = true;
drawing_buffer_ = DrawingBufferForTests::Create(
std::move(provider), gpu_compositing, gl_, initial_size,
DrawingBuffer::kPreserve, kDisableMultisampling);
CHECK(drawing_buffer_);
SetAndSaveRestoreState(true);
testing::Mock::VerifyAndClearExpectations(gl_);
}
void TearDown() override {
platform_.reset();
}
GLuint image_id0_;
std::unique_ptr<ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform>>
platform_;
};
TEST_F(DrawingBufferImageChromiumTest, VerifyResizingReallocatesImages) {
GLES2InterfaceForTests* gl_ = drawing_buffer_->ContextGLForTests();
viz::TransferableResource resource;
std::unique_ptr<viz::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 resource at size 100x100.
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
&release_callback));
EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
EXPECT_TRUE(resource.is_overlay_candidate);
EXPECT_EQ(static_cast<gfx::Size>(initial_size), resource.size);
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 resource at this size.
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
&release_callback));
EXPECT_EQ(alternate_size, gl_->MostRecentlyProducedSize());
EXPECT_TRUE(resource.is_overlay_candidate);
EXPECT_EQ(static_cast<gfx::Size>(alternate_size), resource.size);
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 resource and verify that it's the correct size.
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
&release_callback));
EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
EXPECT_TRUE(resource.is_overlay_candidate);
EXPECT_EQ(static_cast<gfx::Size>(initial_size), resource.size);
testing::Mock::VerifyAndClearExpectations(gl_);
// Prepare one final resource and verify that it's the correct size.
release_callback->Run(gpu::SyncToken(), false /* lostResource */);
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
&release_callback));
EXPECT_EQ(initial_size, gl_->MostRecentlyProducedSize());
EXPECT_TRUE(resource.is_overlay_candidate);
EXPECT_EQ(static_cast<gfx::Size>(initial_size), resource.size);
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();
viz::TransferableResource resource1;
std::unique_ptr<viz::SingleReleaseCallback> release_callback1;
viz::TransferableResource resource2;
std::unique_ptr<viz::SingleReleaseCallback> release_callback2;
viz::TransferableResource resource3;
std::unique_ptr<viz::SingleReleaseCallback> release_callback3;
// Request a resource. An image should already be created. Everything works
// as expected.
EXPECT_CALL(*gl_, BindTexImage2DMock(_)).Times(1);
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource1,
&release_callback1));
EXPECT_TRUE(resource1.is_overlay_candidate);
testing::Mock::VerifyAndClearExpectations(gl_);
VerifyStateWasRestored();
// Force image CHROMIUM creation failure. Request another resource. It should
// still be provided, but this time with allowOverlay = false.
gl_->SetCreateImageChromiumFail(true);
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource2,
&release_callback2));
EXPECT_FALSE(resource2.is_overlay_candidate);
VerifyStateWasRestored();
// Check that if image CHROMIUM starts working again, resources are
// correctly created with allowOverlay = true.
EXPECT_CALL(*gl_, BindTexImage2DMock(_)).Times(1);
gl_->SetCreateImageChromiumFail(false);
EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3,
&release_callback3));
EXPECT_TRUE(resource3.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 < base::size(cases); i++) {
SCOPED_TRACE(cases[i].test_case_name);
auto gl = std::make_unique<DepthStencilTrackingGLES2Interface>();
DepthStencilTrackingGLES2Interface* tracking_gl = gl.get();
auto provider =
std::make_unique<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;
bool gpu_compositing = true;
scoped_refptr<DrawingBuffer> drawing_buffer = DrawingBuffer::Create(
std::move(provider), gpu_compositing, 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();
viz::TransferableResource resource;
std::unique_ptr<viz::SingleReleaseCallback> release_callback;
// Produce resources.
EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource,
&release_callback));
gpu::SyncToken wait_sync_token;
gl_->GenSyncTokenCHROMIUM(wait_sync_token.GetData());
drawing_buffer_->SetIsHidden(true);
release_callback->Run(wait_sync_token, false /* lostResource */);
// m_drawingBuffer deletes resource immediately when hidden.
EXPECT_EQ(wait_sync_token, gl_->MostRecentlyWaitedSyncToken());
drawing_buffer_->BeginDestruction();
}
TEST_F(DrawingBufferTest,
VerifyTooBigDrawingBufferExceedingV8MaxSizeFailsToCreate) {
IntSize too_big_size(1, (v8::TypedArray::kMaxLength / 4) + 1);
bool gpu_compositing = true;
scoped_refptr<DrawingBuffer> too_big_drawing_buffer = DrawingBuffer::Create(
nullptr, gpu_compositing, nullptr, too_big_size, false, false, false,
false, false, DrawingBuffer::kDiscard, DrawingBuffer::kWebGL1,
DrawingBuffer::kAllowChromiumImage, CanvasColorParams());
EXPECT_EQ(too_big_drawing_buffer, nullptr);
drawing_buffer_->BeginDestruction();
}
} // namespace blink