blob: 0388ef794260669be521db0f95c9ee9c2c541b46 [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DRAWING_BUFFER_TEST_HELPERS_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DRAWING_BUFFER_TEST_HELPERS_H_
#include "build/build_config.h"
#include "cc/test/stub_decode_cache.h"
#include "components/viz/test/test_context_provider.h"
#include "gpu/command_buffer/client/webgpu_interface.h"
#include "gpu/command_buffer/common/capabilities.h"
#include "gpu/config/gpu_feature_info.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
#include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
#include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
#include "ui/gl/gpu_preference.h"
namespace blink {
enum {
kInitialWidth = 100,
kInitialHeight = 100,
kAlternateHeight = 50,
};
enum UseMultisampling {
kDisableMultisampling,
kEnableMultisampling,
};
class WebGraphicsContext3DProviderForTests
: public WebGraphicsContext3DProvider {
public:
WebGraphicsContext3DProviderForTests(
std::unique_ptr<gpu::gles2::GLES2Interface> gl)
: gl_(std::move(gl)) {}
WebGraphicsContext3DProviderForTests(
std::unique_ptr<gpu::webgpu::WebGPUInterface> webgpu)
: webgpu_(std::move(webgpu)) {}
gpu::InterfaceBase* InterfaceBase() override { return gl_.get(); }
gpu::gles2::GLES2Interface* ContextGL() override { return gl_.get(); }
gpu::raster::RasterInterface* RasterInterface() override { return nullptr; }
bool IsContextLost() override { return false; }
GrDirectContext* GetGrContext() override { return nullptr; }
gpu::webgpu::WebGPUInterface* WebGPUInterface() override {
return webgpu_.get();
}
bool BindToCurrentSequence() override { return false; }
const gpu::Capabilities& GetCapabilities() const override {
return capabilities_;
}
const gpu::GpuFeatureInfo& GetGpuFeatureInfo() const override {
return gpu_feature_info_;
}
const WebglPreferences& GetWebglPreferences() const override {
return webgl_preferences_;
}
gpu::GLHelper* GetGLHelper() override { return nullptr; }
void SetLostContextCallback(base::RepeatingClosure) override {}
void SetErrorMessageCallback(
base::RepeatingCallback<void(const char*, int32_t id)>) override {}
cc::ImageDecodeCache* ImageDecodeCache(SkColorType color_type) override {
return &image_decode_cache_;
}
viz::TestSharedImageInterface* SharedImageInterface() override {
return &test_shared_image_interface_;
}
void CopyVideoFrame(media::PaintCanvasVideoRenderer* video_render,
media::VideoFrame* video_frame,
cc::PaintCanvas* canvas) override {}
viz::RasterContextProvider* RasterContextProvider() const override {
return nullptr;
}
private:
cc::StubDecodeCache image_decode_cache_;
std::unique_ptr<gpu::gles2::GLES2Interface> gl_;
std::unique_ptr<gpu::webgpu::WebGPUInterface> webgpu_;
gpu::Capabilities capabilities_;
gpu::GpuFeatureInfo gpu_feature_info_;
WebglPreferences webgl_preferences_;
viz::TestSharedImageInterface test_shared_image_interface_;
};
class GLES2InterfaceForTests : public gpu::gles2::GLES2InterfaceStub,
public DrawingBuffer::Client {
public:
// GLES2InterfaceStub implementation:
void BindTexture(GLenum target, GLuint texture) override {
if (target == GL_TEXTURE_2D)
state_.active_texture2d_binding = texture;
if (target == GL_TEXTURE_CUBE_MAP)
state_.active_texturecubemap_binding = texture;
bound_textures_.insert(target, texture);
}
void BindFramebuffer(GLenum target, GLuint framebuffer) override {
switch (target) {
case GL_FRAMEBUFFER:
state_.draw_framebuffer_binding = framebuffer;
state_.read_framebuffer_binding = framebuffer;
break;
case GL_DRAW_FRAMEBUFFER:
state_.draw_framebuffer_binding = framebuffer;
break;
case GL_READ_FRAMEBUFFER:
state_.read_framebuffer_binding = framebuffer;
break;
default:
break;
}
}
void BindRenderbuffer(GLenum target, GLuint renderbuffer) override {
state_.renderbuffer_binding = renderbuffer;
}
void Enable(GLenum cap) override {
if (cap == GL_SCISSOR_TEST)
state_.scissor_enabled = true;
}
void Disable(GLenum cap) override {
if (cap == GL_SCISSOR_TEST)
state_.scissor_enabled = false;
}
void ClearColor(GLfloat red,
GLfloat green,
GLfloat blue,
GLfloat alpha) override {
state_.clear_color[0] = red;
state_.clear_color[1] = green;
state_.clear_color[2] = blue;
state_.clear_color[3] = alpha;
}
void ClearDepthf(GLfloat depth) override { state_.clear_depth = depth; }
void ClearStencil(GLint s) override { state_.clear_stencil = s; }
void ColorMask(GLboolean red,
GLboolean green,
GLboolean blue,
GLboolean alpha) override {
state_.color_mask[0] = red;
state_.color_mask[1] = green;
state_.color_mask[2] = blue;
state_.color_mask[3] = alpha;
}
void DepthMask(GLboolean flag) override { state_.depth_mask = flag; }
void StencilMask(GLuint mask) override { state_.stencil_mask = mask; }
void StencilMaskSeparate(GLenum face, GLuint mask) override {
if (face == GL_FRONT)
state_.stencil_mask = mask;
}
void PixelStorei(GLenum pname, GLint param) override {
if (pname == GL_PACK_ALIGNMENT)
state_.pack_alignment = param;
}
void BindBuffer(GLenum target, GLuint buffer) override {
switch (target) {
case GL_PIXEL_UNPACK_BUFFER:
state_.pixel_unpack_buffer_binding = buffer;
break;
case GL_PIXEL_PACK_BUFFER:
state_.pixel_pack_buffer_binding = buffer;
break;
default:
break;
}
}
GLenum CheckFramebufferStatus(GLenum target) override {
return GL_FRAMEBUFFER_COMPLETE;
}
void GetIntegerv(GLenum pname, GLint* value) override {
switch (pname) {
case GL_DRAW_FRAMEBUFFER_BINDING:
*value = state_.draw_framebuffer_binding;
break;
case GL_READ_FRAMEBUFFER_BINDING:
*value = state_.read_framebuffer_binding;
break;
case GL_MAX_TEXTURE_SIZE:
*value = 1024;
break;
default:
break;
}
}
void ProduceTextureDirectCHROMIUM(GLuint texture, GLbyte* mailbox) override {
++current_mailbox_byte_;
memset(mailbox, current_mailbox_byte_, GL_MAILBOX_SIZE_CHROMIUM);
ASSERT_TRUE(texture_sizes_.Contains(texture));
most_recently_produced_size_ = texture_sizes_.at(texture);
}
void TexImage2D(GLenum target,
GLint level,
GLint internalformat,
GLsizei width,
GLsizei height,
GLint border,
GLenum format,
GLenum type,
const void* pixels) override {
if (target == GL_TEXTURE_2D && !level) {
texture_sizes_.Set(bound_textures_.find(target)->value,
gfx::Size(width, height));
}
}
void GenTextures(GLsizei n, GLuint* textures) override {
static GLuint id = 1;
for (GLsizei i = 0; i < n; ++i)
textures[i] = id++;
}
MOCK_METHOD1(CreateAndTexStorage2DSharedImageCHROMIUMMock,
void(const GLbyte*));
GLuint CreateAndTexStorage2DSharedImageCHROMIUM(
const GLbyte* mailbox) override {
CreateAndTexStorage2DSharedImageCHROMIUMMock(mailbox);
GLuint texture_id;
GenTextures(1, &texture_id);
last_imported_shared_image_.SetZero();
last_imported_shared_image_.SetName(
reinterpret_cast<const gpu::Mailbox*>(mailbox)->name);
return texture_id;
}
// ImplementationBase implementation
void GenSyncTokenCHROMIUM(GLbyte* sync_token) override {
static gpu::CommandBufferId::Generator command_buffer_id_generator;
gpu::SyncToken source(gpu::GPU_IO,
command_buffer_id_generator.GenerateNextId(), 2);
memcpy(sync_token, &source, sizeof(source));
}
MOCK_METHOD1(WaitSyncTokenCHROMIUMMock, void(const GLbyte* sync_token));
void WaitSyncTokenCHROMIUM(const GLbyte* sync_token) override {
memcpy(&most_recently_waited_sync_token_, sync_token,
sizeof(most_recently_waited_sync_token_));
WaitSyncTokenCHROMIUMMock(sync_token);
}
// DrawingBuffer::Client implementation.
bool DrawingBufferClientIsBoundForDraw() override {
return !state_.draw_framebuffer_binding;
}
void DrawingBufferClientRestoreScissorTest() override {
state_.scissor_enabled = saved_state_.scissor_enabled;
}
void DrawingBufferClientRestoreMaskAndClearValues() override {
memcpy(state_.color_mask, saved_state_.color_mask,
sizeof(state_.color_mask));
state_.clear_depth = saved_state_.clear_depth;
state_.clear_stencil = saved_state_.clear_stencil;
memcpy(state_.clear_color, saved_state_.clear_color,
sizeof(state_.clear_color));
state_.depth_mask = saved_state_.depth_mask;
state_.stencil_mask = saved_state_.stencil_mask;
}
void DrawingBufferClientRestorePixelPackParameters() override {
// TODO(zmo): restore ES3 pack parameters?
state_.pack_alignment = saved_state_.pack_alignment;
}
void DrawingBufferClientRestoreTexture2DBinding() override {
state_.active_texture2d_binding = saved_state_.active_texture2d_binding;
}
void DrawingBufferClientRestoreTextureCubeMapBinding() override {
state_.active_texturecubemap_binding =
saved_state_.active_texturecubemap_binding;
}
void DrawingBufferClientRestoreRenderbufferBinding() override {
state_.renderbuffer_binding = saved_state_.renderbuffer_binding;
}
void DrawingBufferClientRestoreFramebufferBinding() override {
state_.draw_framebuffer_binding = saved_state_.draw_framebuffer_binding;
state_.read_framebuffer_binding = saved_state_.read_framebuffer_binding;
}
void DrawingBufferClientRestorePixelUnpackBufferBinding() override {
state_.pixel_unpack_buffer_binding =
saved_state_.pixel_unpack_buffer_binding;
}
void DrawingBufferClientRestorePixelPackBufferBinding() override {
state_.pixel_pack_buffer_binding = saved_state_.pixel_pack_buffer_binding;
}
bool DrawingBufferClientUserAllocatedMultisampledRenderbuffers() override {
// Not unit tested yet. Tested with end-to-end tests.
return false;
}
void DrawingBufferClientForceLostContextWithAutoRecovery() override {
// Not unit tested yet. Tested with end-to-end tests.
}
// Testing methods.
gpu::SyncToken MostRecentlyWaitedSyncToken() const {
return most_recently_waited_sync_token_;
}
GLuint NextImageIdToBeCreated() const { return current_image_id_; }
gfx::Size MostRecentlyProducedSize() const {
return most_recently_produced_size_;
}
// Saves current GL state for later verification.
void SaveState() { saved_state_ = state_; }
void VerifyStateHasNotChangedSinceSave() const {
for (size_t i = 0; i < 4; ++i) {
EXPECT_EQ(state_.clear_color[0], saved_state_.clear_color[0]);
EXPECT_EQ(state_.color_mask[0], saved_state_.color_mask[0]);
}
EXPECT_EQ(state_.clear_depth, saved_state_.clear_depth);
EXPECT_EQ(state_.clear_stencil, saved_state_.clear_stencil);
EXPECT_EQ(state_.depth_mask, saved_state_.depth_mask);
EXPECT_EQ(state_.stencil_mask, saved_state_.stencil_mask);
EXPECT_EQ(state_.pack_alignment, saved_state_.pack_alignment);
EXPECT_EQ(state_.active_texture2d_binding,
saved_state_.active_texture2d_binding);
EXPECT_EQ(state_.active_texturecubemap_binding,
saved_state_.active_texturecubemap_binding);
EXPECT_EQ(state_.renderbuffer_binding, saved_state_.renderbuffer_binding);
EXPECT_EQ(state_.draw_framebuffer_binding,
saved_state_.draw_framebuffer_binding);
EXPECT_EQ(state_.read_framebuffer_binding,
saved_state_.read_framebuffer_binding);
EXPECT_EQ(state_.pixel_unpack_buffer_binding,
saved_state_.pixel_unpack_buffer_binding);
EXPECT_EQ(state_.pixel_pack_buffer_binding,
saved_state_.pixel_pack_buffer_binding);
}
gpu::Mailbox* last_imported_shared_image() {
return &last_imported_shared_image_;
}
private:
// The target to use when binding a texture to a Chromium image.
#if BUILDFLAG(IS_MAC)
static constexpr GLuint kImageCHROMIUMTarget = GC3D_TEXTURE_RECTANGLE_ARB;
#else
static constexpr GLuint kImageCHROMIUMTarget = GL_TEXTURE_2D;
#endif
HashMap<GLenum, GLuint> bound_textures_;
// State tracked to verify that it is restored correctly.
struct State {
bool scissor_enabled = false;
GLfloat clear_color[4] = {0, 0, 0, 0};
GLfloat clear_depth = 0;
GLint clear_stencil = 0;
GLboolean color_mask[4] = {0, 0, 0, 0};
GLboolean depth_mask = 0;
GLuint stencil_mask = 0;
GLint pack_alignment = 4;
// The bound 2D texture for the active texture unit.
GLuint active_texture2d_binding = 0;
// The bound cube map texture for the active texture unit.
GLuint active_texturecubemap_binding = 0;
GLuint renderbuffer_binding = 0;
GLuint draw_framebuffer_binding = 0;
GLuint read_framebuffer_binding = 0;
GLuint pixel_unpack_buffer_binding = 0;
GLuint pixel_pack_buffer_binding = 0;
};
State state_;
State saved_state_;
gpu::SyncToken most_recently_waited_sync_token_;
GLbyte current_mailbox_byte_ = 0;
gfx::Size most_recently_produced_size_;
GLuint current_image_id_ = 1;
HashMap<GLuint, gfx::Size> texture_sizes_;
HashMap<GLuint, gfx::Size> image_sizes_;
HashMap<GLuint, GLuint> image_to_texture_map_;
gpu::Mailbox last_imported_shared_image_;
};
class DrawingBufferForTests : public DrawingBuffer {
public:
static scoped_refptr<DrawingBufferForTests> Create(
std::unique_ptr<WebGraphicsContext3DProvider> context_provider,
const Platform::GraphicsInfo& graphics_info,
DrawingBuffer::Client* client,
const gfx::Size& size,
PreserveDrawingBuffer preserve,
UseMultisampling use_multisampling) {
std::unique_ptr<Extensions3DUtil> extensions_util =
Extensions3DUtil::Create(context_provider->ContextGL());
scoped_refptr<DrawingBufferForTests> drawing_buffer =
base::AdoptRef(new DrawingBufferForTests(
std::move(context_provider), graphics_info,
std::move(extensions_util), client, preserve));
if (!drawing_buffer->Initialize(
size, use_multisampling != kDisableMultisampling)) {
drawing_buffer->BeginDestruction();
return nullptr;
}
return drawing_buffer;
}
DrawingBufferForTests(
std::unique_ptr<WebGraphicsContext3DProvider> context_provider,
const Platform::GraphicsInfo& graphics_info,
std::unique_ptr<Extensions3DUtil> extensions_util,
DrawingBuffer::Client* client,
PreserveDrawingBuffer preserve)
: DrawingBuffer(
std::move(context_provider),
graphics_info,
false /* usingSwapChain */,
false /* desynchronized */,
std::move(extensions_util),
client,
false /* discardFramebufferSupported */,
true /* wantAlphaChannel */,
true /* premultipliedAlpha */,
preserve,
kWebGL1,
false /* wantDepth */,
false /* wantStencil */,
DrawingBuffer::kAllowChromiumImage /* ChromiumImageUsage */,
cc::PaintFlags::FilterQuality::kLow,
PredefinedColorSpace::kSRGB,
CanvasPixelFormat::kUint8,
gl::GpuPreference::kHighPerformance),
live_(nullptr) {}
~DrawingBufferForTests() override {
if (live_)
*live_ = false;
}
GLES2InterfaceForTests* ContextGLForTests() {
return static_cast<GLES2InterfaceForTests*>(ContextGL());
}
viz::TestSharedImageInterface* SharedImageInterfaceForTests() {
return static_cast<viz::TestSharedImageInterface*>(
ContextProvider()->SharedImageInterface());
}
bool* live_;
int RecycledBitmapCount() { return recycled_bitmaps_.size(); }
};
} // blink
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_DRAWING_BUFFER_TEST_HELPERS_H_