blob: 46fdb18149d57f7d7b6d52a32cc0aff5b27b498b [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gpu/command_buffer/service/shared_image_backing_factory_d3d.h"
#include <memory>
#include <utility>
#include "base/callback_helpers.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/service_utils.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/shared_image_backing_d3d.h"
#include "gpu/command_buffer/service/shared_image_factory.h"
#include "gpu/command_buffer/service/shared_image_manager.h"
#include "gpu/command_buffer/service/shared_image_representation.h"
#include "gpu/config/gpu_test_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkPromiseImageTexture.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/gpu_memory_buffer.h"
#include "ui/gl/buildflags.h"
#include "ui/gl/gl_angle_util_win.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_image_d3d.h"
#include "ui/gl/gl_image_memory.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/init/gl_factory.h"
#if BUILDFLAG(USE_DAWN)
#include <dawn/dawn_proc.h>
#include <dawn/webgpu_cpp.h>
#include <dawn_native/DawnNative.h>
#endif // BUILDFLAG(USE_DAWN)
#define SCOPED_GL_CLEANUP_VAR(api, func, var) \
base::ScopedClosureRunner delete_##var(base::BindOnce( \
[](gl::GLApi* api, GLuint var) { api->gl##func##Fn(var); }, api, var))
#define SCOPED_GL_CLEANUP_PTR(api, func, n, var) \
base::ScopedClosureRunner delete_##var(base::BindOnce( \
[](gl::GLApi* api, GLuint var) { api->gl##func##Fn(n, &var); }, api, \
var))
namespace gpu {
namespace {
static const char* kVertexShaderSrc =
"attribute vec2 a_position;\n"
"varying vec2 v_texCoord;\n"
"void main() {\n"
" gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0);\n"
" v_texCoord = (a_position + vec2(1.0, 1.0)) * 0.5;\n"
"}\n";
static const char* kFragmentShaderSrc =
"precision mediump float;\n"
"uniform mediump sampler2D u_texture;\n"
"varying vec2 v_texCoord;\n"
"void main() {\n"
" gl_FragColor = texture2D(u_texture, v_texCoord);"
"}\n";
void FillYUV(uint8_t* data,
const gfx::Size& size,
uint8_t y_fill_value,
uint8_t u_fill_value,
uint8_t v_fill_value) {
const size_t kYPlaneSize = size.width() * size.height();
memset(data, y_fill_value, kYPlaneSize);
uint8_t* uv_data = data + kYPlaneSize;
const size_t kUVPlaneSize = kYPlaneSize / 2;
for (size_t i = 0; i < kUVPlaneSize; i += 2) {
uv_data[i] = u_fill_value;
uv_data[i + 1] = v_fill_value;
}
}
void CheckYUV(const uint8_t* data,
size_t stride,
const gfx::Size& size,
uint8_t y_fill_value,
uint8_t u_fill_value,
uint8_t v_fill_value) {
const size_t kYPlaneSize = stride * size.height();
const uint8_t* uv_data = data + kYPlaneSize;
for (int i = 0; i < size.height(); i++) {
for (int j = 0; j < size.width(); j++) {
// ASSERT instead of EXPECT to exit on first failure to avoid log spam.
ASSERT_EQ(*(data + i * stride + j), y_fill_value);
if (i < size.height() / 2) {
const uint8_t uv_value = (j % 2 == 0) ? u_fill_value : v_fill_value;
ASSERT_EQ(*(uv_data + i * stride + j), uv_value);
}
}
}
}
GLuint MakeTextureAndSetParameters(gl::GLApi* api, GLenum target, bool fbo) {
GLuint texture_id = 0;
api->glGenTexturesFn(1, &texture_id);
api->glBindTextureFn(target, texture_id);
api->glTexParameteriFn(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
api->glTexParameteriFn(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
api->glTexParameteriFn(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
api->glTexParameteriFn(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (fbo) {
api->glTexParameteriFn(target, GL_TEXTURE_USAGE_ANGLE,
GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
}
return texture_id;
}
bool IsD3DSharedImageSupported() {
// D3D shared images with the current group of flags only works on Win8+
// OSes. If we need shared images on Win7, we can create them but a more
// insecure group of flags is required.
if (GPUTestBotConfig::CurrentConfigMatches("Win7"))
return false;
return true;
}
class SharedImageBackingFactoryD3DTestBase : public testing::Test {
public:
void SetUp() override {
surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size());
ASSERT_TRUE(surface_);
context_ = gl::init::CreateGLContext(nullptr, surface_.get(),
gl::GLContextAttribs());
ASSERT_TRUE(context_);
bool result = context_->MakeCurrent(surface_.get());
ASSERT_TRUE(result);
memory_type_tracker_ = std::make_unique<MemoryTypeTracker>(nullptr);
shared_image_representation_factory_ =
std::make_unique<SharedImageRepresentationFactory>(
&shared_image_manager_, nullptr);
shared_image_factory_ = std::make_unique<SharedImageBackingFactoryD3D>();
}
protected:
scoped_refptr<gl::GLSurface> surface_;
scoped_refptr<gl::GLContext> context_;
SharedImageManager shared_image_manager_;
std::unique_ptr<MemoryTypeTracker> memory_type_tracker_;
std::unique_ptr<SharedImageRepresentationFactory>
shared_image_representation_factory_;
std::unique_ptr<SharedImageBackingFactoryD3D> shared_image_factory_;
};
class SharedImageBackingFactoryD3DTestSwapChain
: public SharedImageBackingFactoryD3DTestBase {
public:
void SetUp() override {
if (!SharedImageBackingFactoryD3D::IsSwapChainSupported())
return;
SharedImageBackingFactoryD3DTestBase::SetUp();
}
};
TEST_F(SharedImageBackingFactoryD3DTestSwapChain, InvalidFormat) {
if (!SharedImageBackingFactoryD3D::IsSwapChainSupported())
return;
auto front_buffer_mailbox = Mailbox::GenerateForSharedImage();
auto back_buffer_mailbox = Mailbox::GenerateForSharedImage();
gfx::Size size(1, 1);
auto color_space = gfx::ColorSpace::CreateSRGB();
auto surface_origin = kTopLeft_GrSurfaceOrigin;
auto alpha_type = kPremul_SkAlphaType;
uint32_t usage = gpu::SHARED_IMAGE_USAGE_SCANOUT;
{
auto valid_format = viz::RGBA_8888;
auto backings = shared_image_factory_->CreateSwapChain(
front_buffer_mailbox, back_buffer_mailbox, valid_format, size,
color_space, surface_origin, alpha_type, usage);
EXPECT_TRUE(backings.front_buffer);
EXPECT_TRUE(backings.back_buffer);
}
{
auto valid_format = viz::BGRA_8888;
auto backings = shared_image_factory_->CreateSwapChain(
front_buffer_mailbox, back_buffer_mailbox, valid_format, size,
color_space, surface_origin, alpha_type, usage);
EXPECT_TRUE(backings.front_buffer);
EXPECT_TRUE(backings.back_buffer);
}
{
auto valid_format = viz::RGBA_F16;
auto backings = shared_image_factory_->CreateSwapChain(
front_buffer_mailbox, back_buffer_mailbox, valid_format, size,
color_space, surface_origin, alpha_type, usage);
EXPECT_TRUE(backings.front_buffer);
EXPECT_TRUE(backings.back_buffer);
}
{
auto invalid_format = viz::RGBA_4444;
auto backings = shared_image_factory_->CreateSwapChain(
front_buffer_mailbox, back_buffer_mailbox, invalid_format, size,
color_space, surface_origin, alpha_type, usage);
EXPECT_FALSE(backings.front_buffer);
EXPECT_FALSE(backings.back_buffer);
}
}
TEST_F(SharedImageBackingFactoryD3DTestSwapChain, CreateAndPresentSwapChain) {
if (!SharedImageBackingFactoryD3D::IsSwapChainSupported())
return;
auto front_buffer_mailbox = Mailbox::GenerateForSharedImage();
auto back_buffer_mailbox = Mailbox::GenerateForSharedImage();
auto format = viz::RGBA_8888;
gfx::Size size(1, 1);
auto color_space = gfx::ColorSpace::CreateSRGB();
auto surface_origin = kTopLeft_GrSurfaceOrigin;
auto alpha_type = kPremul_SkAlphaType;
uint32_t usage = gpu::SHARED_IMAGE_USAGE_GLES2 |
gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT |
gpu::SHARED_IMAGE_USAGE_DISPLAY |
gpu::SHARED_IMAGE_USAGE_SCANOUT;
auto backings = shared_image_factory_->CreateSwapChain(
front_buffer_mailbox, back_buffer_mailbox, format, size, color_space,
surface_origin, alpha_type, usage);
ASSERT_TRUE(backings.front_buffer);
EXPECT_TRUE(backings.front_buffer->IsCleared());
ASSERT_TRUE(backings.back_buffer);
EXPECT_TRUE(backings.back_buffer->IsCleared());
std::unique_ptr<SharedImageRepresentationFactoryRef> back_factory_ref =
shared_image_manager_.Register(std::move(backings.back_buffer),
memory_type_tracker_.get());
std::unique_ptr<SharedImageRepresentationFactoryRef> front_factory_ref =
shared_image_manager_.Register(std::move(backings.front_buffer),
memory_type_tracker_.get());
auto back_texture = shared_image_representation_factory_
->ProduceGLTexturePassthrough(back_buffer_mailbox)
->GetTexturePassthrough();
ASSERT_TRUE(back_texture);
EXPECT_EQ(back_texture->target(), static_cast<unsigned>(GL_TEXTURE_2D));
GLuint back_texture_id = back_texture->service_id();
EXPECT_NE(back_texture_id, 0u);
auto* back_image = gl::GLImageD3D::FromGLImage(
back_texture->GetLevelImage(GL_TEXTURE_2D, 0));
auto front_texture = shared_image_representation_factory_
->ProduceGLTexturePassthrough(front_buffer_mailbox)
->GetTexturePassthrough();
ASSERT_TRUE(front_texture);
EXPECT_EQ(front_texture->target(), static_cast<unsigned>(GL_TEXTURE_2D));
GLuint front_texture_id = front_texture->service_id();
EXPECT_NE(front_texture_id, 0u);
auto* front_image = gl::GLImageD3D::FromGLImage(
front_texture->GetLevelImage(GL_TEXTURE_2D, 0));
ASSERT_TRUE(back_image);
EXPECT_EQ(back_image->ShouldBindOrCopy(), gl::GLImage::BIND);
Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
EXPECT_EQ(S_OK, back_image->swap_chain()->GetBuffer(
0 /* buffer_index */, IID_PPV_ARGS(&d3d11_texture)));
EXPECT_TRUE(d3d11_texture);
EXPECT_EQ(d3d11_texture, back_image->texture());
d3d11_texture.Reset();
ASSERT_TRUE(front_image);
EXPECT_EQ(front_image->ShouldBindOrCopy(), gl::GLImage::BIND);
EXPECT_EQ(S_OK, front_image->swap_chain()->GetBuffer(
1 /* buffer_index */, IID_PPV_ARGS(&d3d11_texture)));
EXPECT_TRUE(d3d11_texture);
EXPECT_EQ(d3d11_texture, front_image->texture());
d3d11_texture.Reset();
gl::GLApi* api = gl::g_current_gl_context;
// Create a multisampled FBO.
GLuint multisample_fbo, renderbuffer = 0u;
api->glGenFramebuffersEXTFn(1, &multisample_fbo);
api->glBindFramebufferEXTFn(GL_FRAMEBUFFER, multisample_fbo);
api->glGenRenderbuffersEXTFn(1, &renderbuffer);
api->glBindRenderbufferEXTFn(GL_RENDERBUFFER, renderbuffer);
ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
api->glRenderbufferStorageMultisampleFn(GL_RENDERBUFFER, 4 /* sample_count */,
GL_RGBA8_OES, 1, 1);
api->glFramebufferRenderbufferEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, renderbuffer);
EXPECT_EQ(api->glCheckFramebufferStatusEXTFn(GL_FRAMEBUFFER),
static_cast<unsigned>(GL_FRAMEBUFFER_COMPLETE));
ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
// Set the clear color to green.
api->glViewportFn(0, 0, size.width(), size.height());
api->glClearColorFn(0.0f, 1.0f, 0.0f, 1.0f);
api->glClearFn(GL_COLOR_BUFFER_BIT);
ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
api->glBindFramebufferEXTFn(GL_READ_FRAMEBUFFER, multisample_fbo);
// Attach the back buffer texture to an FBO.
GLuint fbo = 0u;
api->glGenFramebuffersEXTFn(1, &fbo);
api->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER, fbo);
api->glBindTextureFn(GL_TEXTURE_2D, back_texture_id);
api->glFramebufferTexture2DEXTFn(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, back_texture_id, 0);
EXPECT_EQ(api->glCheckFramebufferStatusEXTFn(GL_DRAW_FRAMEBUFFER),
static_cast<unsigned>(GL_FRAMEBUFFER_COMPLETE));
ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
api->glBlitFramebufferFn(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
api->glBindFramebufferEXTFn(GL_FRAMEBUFFER, fbo);
// Checks if rendering to back buffer was successful.
{
GLubyte pixel_color[4];
const uint8_t expected_color[4] = {0, 255, 0, 255};
api->glReadPixelsFn(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_color);
EXPECT_EQ(expected_color[0], pixel_color[0]);
EXPECT_EQ(expected_color[1], pixel_color[1]);
EXPECT_EQ(expected_color[2], pixel_color[2]);
EXPECT_EQ(expected_color[3], pixel_color[3]);
}
EXPECT_TRUE(back_factory_ref->PresentSwapChain());
// After present, back buffer should now have a clear texture.
{
GLubyte pixel_color[4];
const uint8_t expected_color[4] = {0, 0, 0, 255};
api->glReadPixelsFn(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_color);
EXPECT_EQ(expected_color[0], pixel_color[0]);
EXPECT_EQ(expected_color[1], pixel_color[1]);
EXPECT_EQ(expected_color[2], pixel_color[2]);
EXPECT_EQ(expected_color[3], pixel_color[3]);
}
// And front buffer should have the rendered contents. Test that binding
// front buffer as a sampler works.
{
// Create a destination texture to render into since we can't bind front
// buffer to an FBO.
GLuint dest_texture_id =
MakeTextureAndSetParameters(api, GL_TEXTURE_2D, true);
api->glTexImage2DFn(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
api->glFramebufferTexture2DEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, dest_texture_id, 0);
EXPECT_EQ(api->glCheckFramebufferStatusEXTFn(GL_FRAMEBUFFER),
static_cast<unsigned>(GL_FRAMEBUFFER_COMPLETE));
api->glClearColorFn(0.0f, 0.0f, 0.0f, 0.0f);
api->glClearFn(GL_COLOR_BUFFER_BIT);
ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
GLint status = 0;
GLuint vertex_shader = api->glCreateShaderFn(GL_VERTEX_SHADER);
ASSERT_NE(vertex_shader, 0u);
api->glShaderSourceFn(vertex_shader, 1, &kVertexShaderSrc, nullptr);
api->glCompileShaderFn(vertex_shader);
api->glGetShaderivFn(vertex_shader, GL_COMPILE_STATUS, &status);
ASSERT_NE(status, 0);
GLuint fragment_shader = api->glCreateShaderFn(GL_FRAGMENT_SHADER);
ASSERT_NE(fragment_shader, 0u);
api->glShaderSourceFn(fragment_shader, 1, &kFragmentShaderSrc, nullptr);
api->glCompileShaderFn(fragment_shader);
api->glGetShaderivFn(fragment_shader, GL_COMPILE_STATUS, &status);
ASSERT_NE(status, 0);
GLuint program = api->glCreateProgramFn();
ASSERT_NE(program, 0u);
api->glAttachShaderFn(program, vertex_shader);
api->glAttachShaderFn(program, fragment_shader);
api->glLinkProgramFn(program);
api->glGetProgramivFn(program, GL_LINK_STATUS, &status);
ASSERT_NE(status, 0);
GLuint vbo = 0u;
api->glGenBuffersARBFn(1, &vbo);
ASSERT_NE(vbo, 0u);
api->glBindBufferFn(GL_ARRAY_BUFFER, vbo);
static const float vertices[] = {
1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
};
api->glBufferDataFn(GL_ARRAY_BUFFER, sizeof(vertices), vertices,
GL_STATIC_DRAW);
GLint vertex_location = api->glGetAttribLocationFn(program, "a_position");
ASSERT_NE(vertex_location, -1);
api->glEnableVertexAttribArrayFn(vertex_location);
api->glVertexAttribPointerFn(vertex_location, 2, GL_FLOAT, GL_FALSE, 0,
nullptr);
GLint sampler_location = api->glGetUniformLocationFn(program, "u_texture");
ASSERT_NE(sampler_location, -1);
api->glActiveTextureFn(GL_TEXTURE0);
// ExpectUnboundAndBindOrCopyTexImage(front_buffer_mailbox);
api->glBindTextureFn(GL_TEXTURE_2D, front_texture_id);
api->glUniform1iFn(sampler_location, 0);
api->glUseProgramFn(program);
api->glDrawArraysFn(GL_TRIANGLES, 0, 6);
{
GLubyte pixel_color[4];
const uint8_t expected_color[4] = {0, 255, 0, 255};
api->glReadPixelsFn(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_color);
EXPECT_EQ(expected_color[0], pixel_color[0]);
EXPECT_EQ(expected_color[1], pixel_color[1]);
EXPECT_EQ(expected_color[2], pixel_color[2]);
EXPECT_EQ(expected_color[3], pixel_color[3]);
}
api->glDeleteProgramFn(program);
api->glDeleteShaderFn(vertex_shader);
api->glDeleteShaderFn(fragment_shader);
api->glDeleteBuffersARBFn(1, &vbo);
}
api->glDeleteFramebuffersEXTFn(1, &fbo);
}
class SharedImageBackingFactoryD3DTest
: public SharedImageBackingFactoryD3DTestBase {
public:
void SetUp() override {
if (!IsD3DSharedImageSupported())
return;
SharedImageBackingFactoryD3DTestBase::SetUp();
GpuDriverBugWorkarounds workarounds;
scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup();
context_state_ = base::MakeRefCounted<SharedContextState>(
std::move(share_group), surface_, context_,
/*use_virtualized_gl_contexts=*/false, base::DoNothing());
context_state_->InitializeGrContext(GpuPreferences(), workarounds, nullptr);
auto feature_info =
base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo());
context_state_->InitializeGL(GpuPreferences(), std::move(feature_info));
}
protected:
GrDirectContext* gr_context() const { return context_state_->gr_context(); }
void CheckSkiaPixels(const Mailbox& mailbox,
const gfx::Size& size,
const std::vector<uint8_t> expected_color) const {
auto skia_representation =
shared_image_representation_factory_->ProduceSkia(mailbox,
context_state_);
ASSERT_NE(skia_representation, nullptr);
std::unique_ptr<SharedImageRepresentationSkia::ScopedReadAccess>
scoped_read_access =
skia_representation->BeginScopedReadAccess(nullptr, nullptr);
EXPECT_TRUE(scoped_read_access);
auto* promise_texture = scoped_read_access->promise_image_texture();
GrBackendTexture backend_texture = promise_texture->backendTexture();
EXPECT_TRUE(backend_texture.isValid());
EXPECT_EQ(size.width(), backend_texture.width());
EXPECT_EQ(size.height(), backend_texture.height());
// Create an Sk Image from GrBackendTexture.
auto sk_image = SkImage::MakeFromTexture(
gr_context(), backend_texture, kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType, kOpaque_SkAlphaType, nullptr);
const SkImageInfo dst_info =
SkImageInfo::Make(size.width(), size.height(), kRGBA_8888_SkColorType,
kOpaque_SkAlphaType, nullptr);
const int num_pixels = size.width() * size.height();
std::vector<uint8_t> dst_pixels(num_pixels * 4);
// Read back pixels from Sk Image.
EXPECT_TRUE(sk_image->readPixels(dst_info, dst_pixels.data(),
dst_info.minRowBytes(), 0, 0));
for (int i = 0; i < num_pixels; i++) {
// Compare the pixel values.
const uint8_t* pixel = dst_pixels.data() + (i * 4);
EXPECT_EQ(pixel[0], expected_color[0]);
EXPECT_EQ(pixel[1], expected_color[1]);
EXPECT_EQ(pixel[2], expected_color[2]);
EXPECT_EQ(pixel[3], expected_color[3]);
}
}
std::vector<std::unique_ptr<SharedImageRepresentationFactoryRef>>
CreateVideoImages(const gfx::Size& size,
uint8_t y_fill_value,
uint8_t u_fill_value,
uint8_t v_fill_value,
bool use_shared_handle,
bool use_factory);
void RunVideoTest(bool use_shared_handle, bool use_factory);
void RunOverlayTest(bool use_shared_handle, bool use_factory);
void RunCreateSharedImageFromHandleTest(DXGI_FORMAT dxgi_format);
scoped_refptr<SharedContextState> context_state_;
};
// Test to check interaction between Gl and skia GL representations.
// We write to a GL texture using gl representation and then read from skia
// representation.
TEST_F(SharedImageBackingFactoryD3DTest, GL_SkiaGL) {
if (!IsD3DSharedImageSupported())
return;
// Create a backing using mailbox.
auto mailbox = Mailbox::GenerateForSharedImage();
const auto format = viz::ResourceFormat::RGBA_8888;
const gfx::Size size(1, 1);
const auto color_space = gfx::ColorSpace::CreateSRGB();
const uint32_t usage = SHARED_IMAGE_USAGE_GLES2 | SHARED_IMAGE_USAGE_DISPLAY;
const gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle;
auto backing = shared_image_factory_->CreateSharedImage(
mailbox, format, surface_handle, size, color_space,
kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage,
false /* is_thread_safe */);
ASSERT_NE(backing, nullptr);
GLenum expected_target = GL_TEXTURE_2D;
std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
shared_image_manager_.Register(std::move(backing),
memory_type_tracker_.get());
// Create a SharedImageRepresentationGLTexture.
auto gl_representation =
shared_image_representation_factory_->ProduceGLTexturePassthrough(
mailbox);
EXPECT_EQ(expected_target,
gl_representation->GetTexturePassthrough()->target());
std::unique_ptr<SharedImageRepresentationGLTexturePassthrough::ScopedAccess>
scoped_access = gl_representation->BeginScopedAccess(
GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM,
SharedImageRepresentation::AllowUnclearedAccess::kYes);
EXPECT_TRUE(scoped_access);
// Create an FBO.
GLuint fbo = 0;
gl::GLApi* api = gl::g_current_gl_context;
api->glGenFramebuffersEXTFn(1, &fbo);
api->glBindFramebufferEXTFn(GL_FRAMEBUFFER, fbo);
// Attach the texture to FBO.
api->glFramebufferTexture2DEXTFn(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
gl_representation->GetTexturePassthrough()->target(),
gl_representation->GetTexturePassthrough()->service_id(), 0);
// Set the clear color to green.
api->glClearColorFn(0.0f, 1.0f, 0.0f, 1.0f);
api->glClearFn(GL_COLOR_BUFFER_BIT);
gl_representation->SetCleared();
scoped_access.reset();
gl_representation.reset();
CheckSkiaPixels(mailbox, size, {0, 255, 0, 255});
factory_ref.reset();
}
#if BUILDFLAG(USE_DAWN)
// Test to check interaction between Dawn and skia GL representations.
TEST_F(SharedImageBackingFactoryD3DTest, Dawn_SkiaGL) {
if (!IsD3DSharedImageSupported())
return;
// Create a Dawn D3D12 device
dawn_native::Instance instance;
instance.DiscoverDefaultAdapters();
std::vector<dawn_native::Adapter> adapters = instance.GetAdapters();
auto adapter_it = std::find_if(
adapters.begin(), adapters.end(), [](dawn_native::Adapter adapter) {
return adapter.GetBackendType() == dawn_native::BackendType::D3D12;
});
ASSERT_NE(adapter_it, adapters.end());
dawn_native::DeviceDescriptor device_descriptor;
// We need to request internal usage to be able to do operations with
// internal methods that would need specific usages.
device_descriptor.requiredFeatures.push_back("dawn-internal-usages");
wgpu::Device device =
wgpu::Device::Acquire(adapter_it->CreateDevice(&device_descriptor));
DawnProcTable procs = dawn_native::GetProcs();
dawnProcSetProcs(&procs);
// Create a backing using mailbox.
const auto mailbox = Mailbox::GenerateForSharedImage();
const auto format = viz::ResourceFormat::RGBA_8888;
const gfx::Size size(1, 1);
const auto color_space = gfx::ColorSpace::CreateSRGB();
const gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle;
const uint32_t usage = SHARED_IMAGE_USAGE_WEBGPU | SHARED_IMAGE_USAGE_DISPLAY;
auto backing = shared_image_factory_->CreateSharedImage(
mailbox, format, surface_handle, size, color_space,
kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage,
false /* is_thread_safe */);
ASSERT_NE(backing, nullptr);
std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
shared_image_manager_.Register(std::move(backing),
memory_type_tracker_.get());
// Clear the shared image to green using Dawn.
{
// Create a SharedImageRepresentationDawn.
auto dawn_representation =
shared_image_representation_factory_->ProduceDawn(
mailbox, device.Get(), WGPUBackendType_D3D12);
ASSERT_TRUE(dawn_representation);
auto scoped_access = dawn_representation->BeginScopedAccess(
WGPUTextureUsage_RenderAttachment,
SharedImageRepresentation::AllowUnclearedAccess::kYes);
ASSERT_TRUE(scoped_access);
wgpu::Texture texture(scoped_access->texture());
wgpu::RenderPassColorAttachment color_desc;
color_desc.view = texture.CreateView();
color_desc.resolveTarget = nullptr;
color_desc.loadOp = wgpu::LoadOp::Clear;
color_desc.storeOp = wgpu::StoreOp::Store;
color_desc.clearColor = {0, 255, 0, 255};
wgpu::RenderPassDescriptor renderPassDesc = {};
renderPassDesc.colorAttachmentCount = 1;
renderPassDesc.colorAttachments = &color_desc;
renderPassDesc.depthStencilAttachment = nullptr;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
pass.EndPass();
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device.GetQueue();
queue.Submit(1, &commands);
}
CheckSkiaPixels(mailbox, size, {0, 255, 0, 255});
// Shut down Dawn
device = wgpu::Device();
dawnProcSetProcs(nullptr);
factory_ref.reset();
}
// 1. Draw a color to texture through GL
// 2. Do not call SetCleared so we can test Dawn Lazy clear
// 3. Begin render pass in Dawn, but do not do anything
// 4. Verify through CheckSkiaPixel that GL drawn color not seen
TEST_F(SharedImageBackingFactoryD3DTest, GL_Dawn_Skia_UnclearTexture) {
if (!IsD3DSharedImageSupported())
return;
// Create a backing using mailbox.
auto mailbox = Mailbox::GenerateForSharedImage();
const auto format = viz::ResourceFormat::RGBA_8888;
const gfx::Size size(1, 1);
const auto color_space = gfx::ColorSpace::CreateSRGB();
const uint32_t usage = SHARED_IMAGE_USAGE_GLES2 | SHARED_IMAGE_USAGE_DISPLAY |
SHARED_IMAGE_USAGE_WEBGPU;
const gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle;
auto backing = shared_image_factory_->CreateSharedImage(
mailbox, format, surface_handle, size, color_space,
kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage,
false /* is_thread_safe */);
ASSERT_NE(backing, nullptr);
GLenum expected_target = GL_TEXTURE_2D;
std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
shared_image_manager_.Register(std::move(backing),
memory_type_tracker_.get());
{
// Create a SharedImageRepresentationGLTexture.
auto gl_representation =
shared_image_representation_factory_->ProduceGLTexturePassthrough(
mailbox);
EXPECT_EQ(expected_target,
gl_representation->GetTexturePassthrough()->target());
std::unique_ptr<SharedImageRepresentationGLTexturePassthrough::ScopedAccess>
gl_scoped_access = gl_representation->BeginScopedAccess(
GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM,
SharedImageRepresentation::AllowUnclearedAccess::kYes);
EXPECT_TRUE(gl_scoped_access);
// Create an FBO.
GLuint fbo = 0;
gl::GLApi* api = gl::g_current_gl_context;
api->glGenFramebuffersEXTFn(1, &fbo);
api->glBindFramebufferEXTFn(GL_FRAMEBUFFER, fbo);
// Attach the texture to FBO.
api->glFramebufferTexture2DEXTFn(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
gl_representation->GetTexturePassthrough()->target(),
gl_representation->GetTexturePassthrough()->service_id(), 0);
// Set the clear color to green.
api->glClearColorFn(0.0f, 1.0f, 0.0f, 1.0f);
api->glClearFn(GL_COLOR_BUFFER_BIT);
// Don't call SetCleared, we want to see if Dawn will lazy clear the texture
EXPECT_FALSE(factory_ref->IsCleared());
}
// Create a Dawn D3D12 device
dawn_native::Instance instance;
instance.DiscoverDefaultAdapters();
std::vector<dawn_native::Adapter> adapters = instance.GetAdapters();
auto adapter_it = std::find_if(
adapters.begin(), adapters.end(), [](dawn_native::Adapter adapter) {
return adapter.GetBackendType() == dawn_native::BackendType::D3D12;
});
ASSERT_NE(adapter_it, adapters.end());
dawn_native::DeviceDescriptor device_descriptor;
// We need to request internal usage to be able to do operations with
// internal methods that would need specific usages.
device_descriptor.requiredFeatures.push_back("dawn-internal-usages");
wgpu::Device device =
wgpu::Device::Acquire(adapter_it->CreateDevice(&device_descriptor));
DawnProcTable procs = dawn_native::GetProcs();
dawnProcSetProcs(&procs);
{
auto dawn_representation =
shared_image_representation_factory_->ProduceDawn(
mailbox, device.Get(), WGPUBackendType_D3D12);
ASSERT_TRUE(dawn_representation);
auto dawn_scoped_access = dawn_representation->BeginScopedAccess(
WGPUTextureUsage_RenderAttachment,
SharedImageRepresentation::AllowUnclearedAccess::kYes);
ASSERT_TRUE(dawn_scoped_access);
wgpu::Texture texture(dawn_scoped_access->texture());
wgpu::RenderPassColorAttachment color_desc;
color_desc.view = texture.CreateView();
color_desc.resolveTarget = nullptr;
color_desc.loadOp = wgpu::LoadOp::Load;
color_desc.storeOp = wgpu::StoreOp::Store;
wgpu::RenderPassDescriptor renderPassDesc = {};
renderPassDesc.colorAttachmentCount = 1;
renderPassDesc.colorAttachments = &color_desc;
renderPassDesc.depthStencilAttachment = nullptr;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
pass.EndPass();
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device.GetQueue();
queue.Submit(1, &commands);
}
// Check skia pixels returns black since texture was lazy cleared in Dawn
EXPECT_TRUE(factory_ref->IsCleared());
CheckSkiaPixels(mailbox, size, {0, 0, 0, 0});
// Shut down Dawn
device = wgpu::Device();
dawnProcSetProcs(nullptr);
factory_ref.reset();
}
// 1. Draw a color to texture through Dawn
// 2. Set the renderpass storeOp = Discard
// 3. Texture in Dawn will stay as uninitialized
// 3. Expect skia to fail to access the texture because texture is not
// initialized
TEST_F(SharedImageBackingFactoryD3DTest, UnclearDawn_SkiaFails) {
if (!IsD3DSharedImageSupported())
return;
// Create a backing using mailbox.
auto mailbox = Mailbox::GenerateForSharedImage();
const auto format = viz::ResourceFormat::RGBA_8888;
const gfx::Size size(1, 1);
const auto color_space = gfx::ColorSpace::CreateSRGB();
const uint32_t usage = SHARED_IMAGE_USAGE_GLES2 | SHARED_IMAGE_USAGE_DISPLAY |
SHARED_IMAGE_USAGE_WEBGPU;
const gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle;
auto backing = shared_image_factory_->CreateSharedImage(
mailbox, format, surface_handle, size, color_space,
kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage,
false /* is_thread_safe */);
ASSERT_NE(backing, nullptr);
std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
shared_image_manager_.Register(std::move(backing),
memory_type_tracker_.get());
// Create dawn device
dawn_native::Instance instance;
instance.DiscoverDefaultAdapters();
std::vector<dawn_native::Adapter> adapters = instance.GetAdapters();
auto adapter_it = std::find_if(
adapters.begin(), adapters.end(), [](dawn_native::Adapter adapter) {
return adapter.GetBackendType() == dawn_native::BackendType::D3D12;
});
ASSERT_NE(adapter_it, adapters.end());
dawn_native::DeviceDescriptor device_descriptor;
// We need to request internal usage to be able to do operations with
// internal methods that would need specific usages.
device_descriptor.requiredFeatures.push_back("dawn-internal-usages");
wgpu::Device device =
wgpu::Device::Acquire(adapter_it->CreateDevice(&device_descriptor));
DawnProcTable procs = dawn_native::GetProcs();
dawnProcSetProcs(&procs);
{
auto dawn_representation =
shared_image_representation_factory_->ProduceDawn(
mailbox, device.Get(), WGPUBackendType_D3D12);
ASSERT_TRUE(dawn_representation);
auto dawn_scoped_access = dawn_representation->BeginScopedAccess(
WGPUTextureUsage_RenderAttachment,
SharedImageRepresentation::AllowUnclearedAccess::kYes);
ASSERT_TRUE(dawn_scoped_access);
wgpu::Texture texture(dawn_scoped_access->texture());
wgpu::RenderPassColorAttachment color_desc;
color_desc.view = texture.CreateView();
color_desc.resolveTarget = nullptr;
color_desc.loadOp = wgpu::LoadOp::Clear;
color_desc.storeOp = wgpu::StoreOp::Discard;
color_desc.clearColor = {0, 255, 0, 255};
wgpu::RenderPassDescriptor renderPassDesc = {};
renderPassDesc.colorAttachmentCount = 1;
renderPassDesc.colorAttachments = &color_desc;
renderPassDesc.depthStencilAttachment = nullptr;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
pass.EndPass();
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device.GetQueue();
queue.Submit(1, &commands);
}
// Shut down Dawn
device = wgpu::Device();
dawnProcSetProcs(nullptr);
EXPECT_FALSE(factory_ref->IsCleared());
// Produce skia representation
auto skia_representation = shared_image_representation_factory_->ProduceSkia(
mailbox, context_state_);
ASSERT_NE(skia_representation, nullptr);
// Expect BeginScopedReadAccess to fail because sharedImage is uninitialized
std::unique_ptr<SharedImageRepresentationSkia::ScopedReadAccess>
scoped_read_access =
skia_representation->BeginScopedReadAccess(nullptr, nullptr);
EXPECT_EQ(scoped_read_access, nullptr);
}
#endif // BUILDFLAG(USE_DAWN)
// Test that Skia trying to access uninitialized SharedImage will fail
TEST_F(SharedImageBackingFactoryD3DTest, SkiaAccessFirstFails) {
if (!IsD3DSharedImageSupported())
return;
// Create a mailbox.
auto mailbox = Mailbox::GenerateForSharedImage();
const auto format = viz::ResourceFormat::RGBA_8888;
const gfx::Size size(1, 1);
const auto color_space = gfx::ColorSpace::CreateSRGB();
const uint32_t usage = SHARED_IMAGE_USAGE_GLES2 | SHARED_IMAGE_USAGE_DISPLAY;
const gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle;
auto backing = shared_image_factory_->CreateSharedImage(
mailbox, format, surface_handle, size, color_space,
kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage,
false /* is_thread_safe */);
ASSERT_NE(backing, nullptr);
std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
shared_image_manager_.Register(std::move(backing),
memory_type_tracker_.get());
// Produce skia representation
auto skia_representation = shared_image_representation_factory_->ProduceSkia(
mailbox, context_state_);
ASSERT_NE(skia_representation, nullptr);
EXPECT_FALSE(skia_representation->IsCleared());
// Expect BeginScopedReadAccess to fail because sharedImage is uninitialized
std::unique_ptr<SharedImageRepresentationSkia::ScopedReadAccess>
scoped_read_access =
skia_representation->BeginScopedReadAccess(nullptr, nullptr);
EXPECT_EQ(scoped_read_access, nullptr);
}
void SharedImageBackingFactoryD3DTest::RunCreateSharedImageFromHandleTest(
DXGI_FORMAT dxgi_format) {
if (!IsD3DSharedImageSupported())
return;
EXPECT_TRUE(shared_image_factory_->CanImportGpuMemoryBuffer(
gfx::DXGI_SHARED_HANDLE, viz::RGBA_8888));
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
shared_image_factory_->GetDeviceForTesting();
const gfx::Size size(1, 1);
D3D11_TEXTURE2D_DESC desc;
desc.Width = size.width();
desc.Height = size.height();
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = dxgi_format;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
desc.CPUAccessFlags = 0;
desc.MiscFlags =
D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED;
Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
HRESULT hr = d3d11_device->CreateTexture2D(&desc, nullptr, &d3d11_texture);
ASSERT_EQ(hr, S_OK);
Microsoft::WRL::ComPtr<IDXGIResource1> dxgi_resource;
hr = d3d11_texture.As(&dxgi_resource);
ASSERT_EQ(hr, S_OK);
HANDLE shared_handle;
hr = dxgi_resource->CreateSharedHandle(
nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr,
&shared_handle);
ASSERT_EQ(hr, S_OK);
gfx::GpuMemoryBufferHandle gpu_memory_buffer_handle;
gpu_memory_buffer_handle.dxgi_handle.Set(shared_handle);
gpu_memory_buffer_handle.type = gfx::DXGI_SHARED_HANDLE;
auto mailbox = Mailbox::GenerateForSharedImage();
const auto format = gfx::BufferFormat::RGBA_8888;
const auto plane = gfx::BufferPlane::DEFAULT;
const auto color_space = gfx::ColorSpace::CreateSRGB();
const uint32_t usage = SHARED_IMAGE_USAGE_GLES2 | SHARED_IMAGE_USAGE_DISPLAY;
const gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle;
const GrSurfaceOrigin surface_origin = kTopLeft_GrSurfaceOrigin;
const SkAlphaType alpha_type = kPremul_SkAlphaType;
auto backing = shared_image_factory_->CreateSharedImage(
mailbox, 0, std::move(gpu_memory_buffer_handle), format, plane,
surface_handle, size, color_space, surface_origin, alpha_type, usage);
ASSERT_NE(backing, nullptr);
EXPECT_EQ(backing->format(), viz::RGBA_8888);
EXPECT_EQ(backing->size(), size);
EXPECT_EQ(backing->color_space(), color_space);
EXPECT_EQ(backing->surface_origin(), surface_origin);
EXPECT_EQ(backing->alpha_type(), alpha_type);
EXPECT_EQ(backing->mailbox(), mailbox);
EXPECT_TRUE(backing->IsCleared());
SharedImageBackingD3D* backing_d3d =
static_cast<SharedImageBackingD3D*>(backing.get());
EXPECT_EQ(backing_d3d->GetDXGISharedHandleForTesting(), shared_handle);
}
TEST_F(SharedImageBackingFactoryD3DTest,
CreateSharedImageFromHandleFormatUNORM) {
RunCreateSharedImageFromHandleTest(DXGI_FORMAT_R8G8B8A8_UNORM);
}
TEST_F(SharedImageBackingFactoryD3DTest,
CreateSharedImageFromHandleFormatTYPELESS) {
RunCreateSharedImageFromHandleTest(DXGI_FORMAT_R8G8B8A8_TYPELESS);
}
#if BUILDFLAG(USE_DAWN)
// Test to check external image stored in the backing can be reused
TEST_F(SharedImageBackingFactoryD3DTest, Dawn_ReuseExternalImage) {
if (!IsD3DSharedImageSupported())
return;
// Create a backing using mailbox.
auto mailbox = Mailbox::GenerateForSharedImage();
const auto format = viz::ResourceFormat::RGBA_8888;
const gfx::Size size(1, 1);
const auto color_space = gfx::ColorSpace::CreateSRGB();
const uint32_t usage = SHARED_IMAGE_USAGE_GLES2 | SHARED_IMAGE_USAGE_DISPLAY |
SHARED_IMAGE_USAGE_WEBGPU;
const gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle;
auto backing = shared_image_factory_->CreateSharedImage(
mailbox, format, surface_handle, size, color_space,
kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage,
false /* is_thread_safe */);
ASSERT_NE(backing, nullptr);
std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
shared_image_manager_.Register(std::move(backing),
memory_type_tracker_.get());
// Create a Dawn D3D12 device
dawn_native::Instance instance;
instance.DiscoverDefaultAdapters();
std::vector<dawn_native::Adapter> adapters = instance.GetAdapters();
auto adapter_it = std::find_if(
adapters.begin(), adapters.end(), [](dawn_native::Adapter adapter) {
return adapter.GetBackendType() == dawn_native::BackendType::D3D12;
});
ASSERT_NE(adapter_it, adapters.end());
dawn_native::DeviceDescriptor device_descriptor;
// We need to request internal usage to be able to do operations with
// internal methods that would need specific usages.
device_descriptor.requiredFeatures.push_back("dawn-internal-usages");
wgpu::Device device =
wgpu::Device::Acquire(adapter_it->CreateDevice(&device_descriptor));
DawnProcTable procs = dawn_native::GetProcs();
dawnProcSetProcs(&procs);
const WGPUTextureUsage texture_usage = WGPUTextureUsage_RenderAttachment;
// Create the first Dawn texture then clear it to green.
{
auto dawn_representation =
shared_image_representation_factory_->ProduceDawn(
mailbox, device.Get(), WGPUBackendType_D3D12);
ASSERT_TRUE(dawn_representation);
auto scoped_access = dawn_representation->BeginScopedAccess(
texture_usage, SharedImageRepresentation::AllowUnclearedAccess::kYes);
ASSERT_TRUE(scoped_access);
wgpu::Texture texture(scoped_access->texture());
wgpu::RenderPassColorAttachment color_desc;
color_desc.view = texture.CreateView();
color_desc.resolveTarget = nullptr;
color_desc.loadOp = wgpu::LoadOp::Clear;
color_desc.storeOp = wgpu::StoreOp::Store;
color_desc.clearColor = {0, 255, 0, 255};
wgpu::RenderPassDescriptor renderPassDesc = {};
renderPassDesc.colorAttachmentCount = 1;
renderPassDesc.colorAttachments = &color_desc;
renderPassDesc.depthStencilAttachment = nullptr;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
pass.EndPass();
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device.GetQueue();
queue.Submit(1, &commands);
}
CheckSkiaPixels(mailbox, size, {0, 255, 0, 255});
// Create another Dawn texture then clear it with another color.
{
auto dawn_representation =
shared_image_representation_factory_->ProduceDawn(
mailbox, device.Get(), WGPUBackendType_D3D12);
ASSERT_TRUE(dawn_representation);
// Check again that the texture is still green
CheckSkiaPixels(mailbox, size, {0, 255, 0, 255});
auto scoped_access = dawn_representation->BeginScopedAccess(
texture_usage, SharedImageRepresentation::AllowUnclearedAccess::kYes);
ASSERT_TRUE(scoped_access);
wgpu::Texture texture(scoped_access->texture());
wgpu::RenderPassColorAttachment color_desc;
color_desc.view = texture.CreateView();
color_desc.resolveTarget = nullptr;
color_desc.loadOp = wgpu::LoadOp::Clear;
color_desc.storeOp = wgpu::StoreOp::Store;
color_desc.clearColor = {255, 0, 0, 255};
wgpu::RenderPassDescriptor renderPassDesc = {};
renderPassDesc.colorAttachmentCount = 1;
renderPassDesc.colorAttachments = &color_desc;
renderPassDesc.depthStencilAttachment = nullptr;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
pass.EndPass();
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device.GetQueue();
queue.Submit(1, &commands);
}
CheckSkiaPixels(mailbox, size, {255, 0, 0, 255});
// Shut down Dawn
device = wgpu::Device();
dawnProcSetProcs(nullptr);
factory_ref.reset();
}
// Check if making Dawn have the last ref works without a current GL context.
TEST_F(SharedImageBackingFactoryD3DTest, Dawn_HasLastRef) {
if (!IsD3DSharedImageSupported())
return;
// Create a backing using mailbox.
auto mailbox = Mailbox::GenerateForSharedImage();
const auto format = viz::ResourceFormat::RGBA_8888;
const gfx::Size size(1, 1);
const auto color_space = gfx::ColorSpace::CreateSRGB();
const uint32_t usage = SHARED_IMAGE_USAGE_GLES2 | SHARED_IMAGE_USAGE_DISPLAY |
SHARED_IMAGE_USAGE_WEBGPU;
const gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle;
auto backing = shared_image_factory_->CreateSharedImage(
mailbox, format, surface_handle, size, color_space,
kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage,
false /* is_thread_safe */);
ASSERT_NE(backing, nullptr);
std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
shared_image_manager_.Register(std::move(backing),
memory_type_tracker_.get());
// Create a Dawn D3D12 device
dawn_native::Instance instance;
instance.DiscoverDefaultAdapters();
std::vector<dawn_native::Adapter> adapters = instance.GetAdapters();
auto adapter_it = std::find_if(
adapters.begin(), adapters.end(), [](dawn_native::Adapter adapter) {
return adapter.GetBackendType() == dawn_native::BackendType::D3D12;
});
ASSERT_NE(adapter_it, adapters.end());
dawn_native::DeviceDescriptor device_descriptor;
// We need to request internal usage to be able to do operations with
// internal methods that would need specific usages.
device_descriptor.requiredFeatures.push_back("dawn-internal-usages");
wgpu::Device device =
wgpu::Device::Acquire(adapter_it->CreateDevice(&device_descriptor));
DawnProcTable procs = dawn_native::GetProcs();
dawnProcSetProcs(&procs);
auto dawn_representation = shared_image_representation_factory_->ProduceDawn(
mailbox, device.Get(), WGPUBackendType_D3D12);
ASSERT_NE(dawn_representation, nullptr);
// Creating the Skia representation will also create a temporary GL texture.
auto skia_representation = shared_image_representation_factory_->ProduceSkia(
mailbox, context_state_);
ASSERT_NE(skia_representation, nullptr);
// Drop Skia representation and factory ref so that the Dawn representation
// has the last ref.
skia_representation.reset();
factory_ref.reset();
// Ensure no GL context is current.
context_->ReleaseCurrent(surface_.get());
// This shouldn't crash due to no GL context being current.
dawn_representation.reset();
// Shut down Dawn
device = wgpu::Device();
dawnProcSetProcs(nullptr);
// Make context current so that it can be destroyed.
context_->MakeCurrent(surface_.get());
}
#endif // BUILDFLAG(USE_DAWN)
std::vector<std::unique_ptr<SharedImageRepresentationFactoryRef>>
SharedImageBackingFactoryD3DTest::CreateVideoImages(const gfx::Size& size,
uint8_t y_fill_value,
uint8_t u_fill_value,
uint8_t v_fill_value,
bool use_shared_handle,
bool use_factory) {
DCHECK(IsD3DSharedImageSupported());
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
shared_image_factory_->GetDeviceForTesting();
const size_t kDataSize = size.width() * size.height() * 3 / 2;
std::vector<uint8_t> video_data(kDataSize);
FillYUV(video_data.data(), size, y_fill_value, u_fill_value, v_fill_value);
D3D11_SUBRESOURCE_DATA data = {};
data.pSysMem = static_cast<const void*>(video_data.data());
data.SysMemPitch = static_cast<UINT>(size.width());
CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_NV12, size.width(), size.height(), 1,
1, D3D11_BIND_SHADER_RESOURCE);
if (use_shared_handle) {
desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE |
D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
}
Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
HRESULT hr = d3d11_device->CreateTexture2D(&desc, &data, &d3d11_texture);
if (FAILED(hr))
return {};
uint32_t usage =
gpu::SHARED_IMAGE_USAGE_VIDEO_DECODE | gpu::SHARED_IMAGE_USAGE_GLES2 |
gpu::SHARED_IMAGE_USAGE_RASTER | gpu::SHARED_IMAGE_USAGE_DISPLAY |
gpu::SHARED_IMAGE_USAGE_SCANOUT;
base::win::ScopedHandle shared_handle;
if (use_shared_handle) {
Microsoft::WRL::ComPtr<IDXGIResource1> dxgi_resource;
hr = d3d11_texture.As(&dxgi_resource);
DCHECK_EQ(hr, S_OK);
HANDLE handle;
hr = dxgi_resource->CreateSharedHandle(
nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE,
nullptr, &handle);
if (FAILED(hr))
return {};
shared_handle.Set(handle);
DCHECK(shared_handle.IsValid());
usage |= gpu::SHARED_IMAGE_USAGE_WEBGPU;
}
const size_t kNumPlanes = 2;
const gpu::Mailbox mailboxes[kNumPlanes] = {
gpu::Mailbox::GenerateForSharedImage(),
gpu::Mailbox::GenerateForSharedImage()};
std::vector<std::unique_ptr<SharedImageBacking>> shared_image_backings;
if (use_factory) {
gfx::GpuMemoryBufferHandle gmb_handle;
gmb_handle.type = gfx::DXGI_SHARED_HANDLE;
gmb_handle.dxgi_handle = std::move(shared_handle);
shared_image_backings = shared_image_factory_->CreateSharedImageVideoPlanes(
mailboxes, std::move(gmb_handle), gfx::BufferFormat::YUV_420_BIPLANAR,
size, usage);
} else {
shared_image_backings = SharedImageBackingD3D::CreateFromVideoTexture(
mailboxes, DXGI_FORMAT_NV12, size, usage, d3d11_texture,
/*array_slice=*/0, std::move(shared_handle));
}
EXPECT_EQ(shared_image_backings.size(), kNumPlanes);
const gfx::Size plane_sizes[kNumPlanes] = {
size, gfx::Size(size.width() / 2, size.height() / 2)};
const viz::ResourceFormat plane_formats[kNumPlanes] = {viz::RED_8,
viz::RG_88};
std::vector<std::unique_ptr<SharedImageRepresentationFactoryRef>>
shared_image_refs;
for (size_t i = 0; i < std::min(shared_image_backings.size(), kNumPlanes);
i++) {
auto& backing = shared_image_backings[i];
EXPECT_EQ(backing->mailbox(), mailboxes[i]);
EXPECT_EQ(backing->size(), plane_sizes[i]);
EXPECT_EQ(backing->format(), plane_formats[i]);
EXPECT_EQ(backing->color_space(), gfx::ColorSpace());
EXPECT_EQ(backing->surface_origin(), kTopLeft_GrSurfaceOrigin);
EXPECT_EQ(backing->alpha_type(), kPremul_SkAlphaType);
EXPECT_EQ(backing->usage(), usage);
EXPECT_TRUE(backing->IsCleared());
shared_image_refs.push_back(shared_image_manager_.Register(
std::move(backing), memory_type_tracker_.get()));
}
return shared_image_refs;
}
void SharedImageBackingFactoryD3DTest::RunVideoTest(bool use_shared_handle,
bool use_factory) {
if (!IsD3DSharedImageSupported())
return;
const gfx::Size size(32, 32);
const uint8_t kYFillValue = 0x12;
const uint8_t kUFillValue = 0x23;
const uint8_t kVFillValue = 0x34;
auto shared_image_refs =
CreateVideoImages(size, kYFillValue, kUFillValue, kVFillValue,
use_shared_handle, use_factory);
ASSERT_EQ(shared_image_refs.size(), 2u);
// Setup GL shaders, framebuffers, uniforms, etc.
static const char* kVideoFragmentShaderSrc =
"#extension GL_OES_EGL_image_external : require\n"
"precision mediump float;\n"
"uniform samplerExternalOES u_texture_y;\n"
"uniform samplerExternalOES u_texture_uv;\n"
"varying vec2 v_texCoord;\n"
"void main() {\n"
" gl_FragColor.r = texture2D(u_texture_y, v_texCoord).r;\n"
" gl_FragColor.gb = texture2D(u_texture_uv, v_texCoord).rg;\n"
" gl_FragColor.a = 1.0;\n"
"}\n";
gl::GLApi* api = gl::g_current_gl_context;
GLint status = 0;
GLuint vertex_shader = api->glCreateShaderFn(GL_VERTEX_SHADER);
SCOPED_GL_CLEANUP_VAR(api, DeleteShader, vertex_shader);
ASSERT_NE(vertex_shader, 0u);
api->glShaderSourceFn(vertex_shader, 1, &kVertexShaderSrc, nullptr);
api->glCompileShaderFn(vertex_shader);
api->glGetShaderivFn(vertex_shader, GL_COMPILE_STATUS, &status);
ASSERT_NE(status, 0);
GLuint fragment_shader = api->glCreateShaderFn(GL_FRAGMENT_SHADER);
SCOPED_GL_CLEANUP_VAR(api, DeleteShader, fragment_shader);
ASSERT_NE(fragment_shader, 0u);
api->glShaderSourceFn(fragment_shader, 1, &kVideoFragmentShaderSrc, nullptr);
api->glCompileShaderFn(fragment_shader);
api->glGetShaderivFn(fragment_shader, GL_COMPILE_STATUS, &status);
ASSERT_NE(status, 0);
GLuint program = api->glCreateProgramFn();
ASSERT_NE(program, 0u);
SCOPED_GL_CLEANUP_VAR(api, DeleteProgram, program);
api->glAttachShaderFn(program, vertex_shader);
api->glAttachShaderFn(program, fragment_shader);
api->glLinkProgramFn(program);
api->glGetProgramivFn(program, GL_LINK_STATUS, &status);
ASSERT_NE(status, 0);
GLint vertex_location = api->glGetAttribLocationFn(program, "a_position");
ASSERT_NE(vertex_location, -1);
GLint y_texture_location =
api->glGetUniformLocationFn(program, "u_texture_y");
ASSERT_NE(y_texture_location, -1);
GLint uv_texture_location =
api->glGetUniformLocationFn(program, "u_texture_uv");
ASSERT_NE(uv_texture_location, -1);
GLuint fbo, renderbuffer = 0u;
api->glGenFramebuffersEXTFn(1, &fbo);
ASSERT_NE(fbo, 0u);
SCOPED_GL_CLEANUP_PTR(api, DeleteFramebuffersEXT, 1, fbo);
api->glBindFramebufferEXTFn(GL_FRAMEBUFFER, fbo);
api->glGenRenderbuffersEXTFn(1, &renderbuffer);
ASSERT_NE(renderbuffer, 0u);
SCOPED_GL_CLEANUP_PTR(api, DeleteRenderbuffersEXT, 1, renderbuffer);
api->glBindRenderbufferEXTFn(GL_RENDERBUFFER, renderbuffer);
api->glRenderbufferStorageEXTFn(GL_RENDERBUFFER, GL_RGBA8_OES, size.width(),
size.height());
api->glFramebufferRenderbufferEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, renderbuffer);
ASSERT_EQ(api->glCheckFramebufferStatusEXTFn(GL_FRAMEBUFFER),
static_cast<unsigned>(GL_FRAMEBUFFER_COMPLETE));
// Set the clear color to green.
api->glViewportFn(0, 0, size.width(), size.height());
api->glClearColorFn(0.0f, 1.0f, 0.0f, 1.0f);
api->glClearFn(GL_COLOR_BUFFER_BIT);
GLuint vbo = 0u;
api->glGenBuffersARBFn(1, &vbo);
ASSERT_NE(vbo, 0u);
SCOPED_GL_CLEANUP_PTR(api, DeleteBuffersARB, 1, vbo);
api->glBindBufferFn(GL_ARRAY_BUFFER, vbo);
static const float vertices[] = {
1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
};
api->glBufferDataFn(GL_ARRAY_BUFFER, sizeof(vertices), vertices,
GL_STATIC_DRAW);
ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
// Create the representations for the planes, get the texture ids, bind to
// samplers, and draw.
{
auto y_texture =
shared_image_representation_factory_->ProduceGLTexturePassthrough(
shared_image_refs[0]->mailbox());
ASSERT_NE(y_texture, nullptr);
auto uv_texture =
shared_image_representation_factory_->ProduceGLTexturePassthrough(
shared_image_refs[1]->mailbox());
ASSERT_NE(uv_texture, nullptr);
auto y_texture_access = y_texture->BeginScopedAccess(
GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM,
SharedImageRepresentation::AllowUnclearedAccess::kNo);
ASSERT_NE(y_texture_access, nullptr);
auto uv_texture_access = uv_texture->BeginScopedAccess(
GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM,
SharedImageRepresentation::AllowUnclearedAccess::kNo);
ASSERT_NE(uv_texture_access, nullptr);
api->glActiveTextureFn(GL_TEXTURE0);
api->glBindTextureFn(GL_TEXTURE_EXTERNAL_OES,
y_texture->GetTextureBase()->service_id());
ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
api->glActiveTextureFn(GL_TEXTURE1);
api->glBindTextureFn(GL_TEXTURE_EXTERNAL_OES,
uv_texture->GetTextureBase()->service_id());
ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
api->glUseProgramFn(program);
api->glEnableVertexAttribArrayFn(vertex_location);
api->glVertexAttribPointerFn(vertex_location, 2, GL_FLOAT, GL_FALSE, 0,
nullptr);
api->glUniform1iFn(y_texture_location, 0);
api->glUniform1iFn(uv_texture_location, 1);
api->glDrawArraysFn(GL_TRIANGLES, 0, 6);
ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
GLubyte pixel_color[4];
api->glReadPixelsFn(size.width() / 2, size.height() / 2, 1, 1, GL_RGBA,
GL_UNSIGNED_BYTE, pixel_color);
EXPECT_EQ(kYFillValue, pixel_color[0]);
EXPECT_EQ(kUFillValue, pixel_color[1]);
EXPECT_EQ(kVFillValue, pixel_color[2]);
EXPECT_EQ(255, pixel_color[3]);
}
// TODO(dawn:551): Test Dawn access after multi-planar support lands in Dawn.
}
TEST_F(SharedImageBackingFactoryD3DTest, CreateFromVideoTexture) {
RunVideoTest(/*use_shared_handle=*/false, /*use_factory=*/false);
}
TEST_F(SharedImageBackingFactoryD3DTest, CreateFromVideoTextureSharedHandle) {
RunVideoTest(/*use_shared_handle=*/true, /*use_factory=*/false);
}
TEST_F(SharedImageBackingFactoryD3DTest, CreateSharedImageVideoPlanes) {
RunVideoTest(/*use_shared_handle=*/true, /*use_factory=*/true);
}
void SharedImageBackingFactoryD3DTest::RunOverlayTest(bool use_shared_handle,
bool use_factory) {
if (!IsD3DSharedImageSupported())
return;
constexpr gfx::Size size(32, 32);
constexpr uint8_t kYFillValue = 0x12;
constexpr uint8_t kUFillValue = 0x23;
constexpr uint8_t kVFillValue = 0x34;
auto shared_image_refs =
CreateVideoImages(size, kYFillValue, kUFillValue, kVFillValue,
use_shared_handle, use_factory);
ASSERT_EQ(shared_image_refs.size(), 2u);
auto overlay_representation =
shared_image_representation_factory_->ProduceOverlay(
shared_image_refs[0]->mailbox());
auto scoped_read_access =
overlay_representation->BeginScopedReadAccess(/*needs_gl_image=*/true);
ASSERT_TRUE(scoped_read_access);
auto* gl_image_d3d =
gl::GLImageD3D::FromGLImage(scoped_read_access->gl_image());
ASSERT_TRUE(gl_image_d3d);
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
shared_image_factory_->GetDeviceForTesting();
CD3D11_TEXTURE2D_DESC staging_desc(
DXGI_FORMAT_NV12, size.width(), size.height(), 1, 1, 0,
D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ);
Microsoft::WRL::ComPtr<ID3D11Texture2D> staging_texture;
HRESULT hr =
d3d11_device->CreateTexture2D(&staging_desc, nullptr, &staging_texture);
ASSERT_EQ(hr, S_OK);
Microsoft::WRL::ComPtr<ID3D11DeviceContext> device_context;
d3d11_device->GetImmediateContext(&device_context);
device_context->CopyResource(staging_texture.Get(),
gl_image_d3d->texture().Get());
D3D11_MAPPED_SUBRESOURCE mapped_resource = {};
hr = device_context->Map(staging_texture.Get(), 0, D3D11_MAP_READ, 0,
&mapped_resource);
ASSERT_EQ(hr, S_OK);
CheckYUV(static_cast<const uint8_t*>(mapped_resource.pData),
mapped_resource.RowPitch, size, kYFillValue, kUFillValue,
kVFillValue);
device_context->Unmap(staging_texture.Get(), 0);
}
TEST_F(SharedImageBackingFactoryD3DTest, CreateFromVideoTextureOverlay) {
RunOverlayTest(/*use_shared_handle=*/false, /*use_factory=*/false);
}
TEST_F(SharedImageBackingFactoryD3DTest,
CreateFromVideoTextureSharedHandleOverlay) {
RunOverlayTest(/*use_shared_handle=*/true, /*use_factory=*/false);
}
TEST_F(SharedImageBackingFactoryD3DTest, CreateSharedImageVideoPlanesOverlay) {
RunOverlayTest(/*use_shared_handle=*/true, /*use_factory=*/true);
}
TEST_F(SharedImageBackingFactoryD3DTest, CreateFromSharedMemory) {
if (!IsD3DSharedImageSupported())
return;
constexpr gfx::Size size(32, 32);
constexpr size_t kDataSize = size.width() * size.height() * 3 / 2;
base::UnsafeSharedMemoryRegion shm_region =
base::UnsafeSharedMemoryRegion::Create(kDataSize);
{
base::WritableSharedMemoryMapping shm_mapping = shm_region.Map();
FillYUV(shm_mapping.GetMemoryAs<uint8_t>(), size, 255, 255, 255);
}
constexpr size_t kNumPlanes = 2;
const gpu::Mailbox mailboxes[kNumPlanes] = {
gpu::Mailbox::GenerateForSharedImage(),
gpu::Mailbox::GenerateForSharedImage()};
const gfx::BufferPlane planes[kNumPlanes] = {gfx::BufferPlane::Y,
gfx::BufferPlane::UV};
constexpr uint32_t usage =
gpu::SHARED_IMAGE_USAGE_VIDEO_DECODE | gpu::SHARED_IMAGE_USAGE_GLES2 |
gpu::SHARED_IMAGE_USAGE_RASTER | gpu::SHARED_IMAGE_USAGE_DISPLAY |
gpu::SHARED_IMAGE_USAGE_SCANOUT;
std::vector<std::unique_ptr<SharedImageBacking>> shared_image_backings;
for (size_t i = 0; i < kNumPlanes; i++) {
gfx::GpuMemoryBufferHandle shm_gmb_handle;
shm_gmb_handle.type = gfx::SHARED_MEMORY_BUFFER;
shm_gmb_handle.region = shm_region.Duplicate();
DCHECK(shm_gmb_handle.region.IsValid());
shm_gmb_handle.stride = size.width();
auto backing = shared_image_factory_->CreateSharedImage(
mailboxes[i], /*client_id=*/0, std::move(shm_gmb_handle),
gfx::BufferFormat::YUV_420_BIPLANAR, planes[i], kNullSurfaceHandle,
size, gfx::ColorSpace(), kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType,
usage);
EXPECT_NE(backing, nullptr);
shared_image_backings.push_back(std::move(backing));
}
EXPECT_EQ(shared_image_backings.size(), kNumPlanes);
const gfx::Size plane_sizes[kNumPlanes] = {
size, gfx::Size(size.width() / 2, size.height() / 2)};
const viz::ResourceFormat plane_formats[kNumPlanes] = {viz::RED_8,
viz::RG_88};
std::vector<std::unique_ptr<SharedImageRepresentationFactoryRef>>
shared_image_refs;
for (size_t i = 0; i < shared_image_backings.size(); i++) {
auto& backing = shared_image_backings[i];
EXPECT_EQ(backing->mailbox(), mailboxes[i]);
EXPECT_EQ(backing->size(), plane_sizes[i]);
EXPECT_EQ(backing->format(), plane_formats[i]);
EXPECT_EQ(backing->color_space(), gfx::ColorSpace());
EXPECT_EQ(backing->surface_origin(), kTopLeft_GrSurfaceOrigin);
EXPECT_EQ(backing->alpha_type(), kPremul_SkAlphaType);
EXPECT_EQ(backing->usage(), usage);
EXPECT_TRUE(backing->IsCleared());
shared_image_refs.push_back(shared_image_manager_.Register(
std::move(backing), memory_type_tracker_.get()));
}
ASSERT_EQ(shared_image_refs.size(), 2u);
constexpr uint8_t kYClearValue = 0x12;
constexpr uint8_t kUClearValue = 0x23;
constexpr uint8_t kVClearValue = 0x34;
gl::GLApi* api = gl::g_current_gl_context;
GLuint fbo;
api->glGenFramebuffersEXTFn(1, &fbo);
ASSERT_NE(fbo, 0u);
SCOPED_GL_CLEANUP_PTR(api, DeleteFramebuffersEXT, 1, fbo);
api->glBindFramebufferEXTFn(GL_FRAMEBUFFER, fbo);
auto y_texture =
shared_image_representation_factory_->ProduceGLTexturePassthrough(
shared_image_refs[0]->mailbox());
ASSERT_NE(y_texture, nullptr);
GLuint y_texture_id = y_texture->GetTextureBase()->service_id();
api->glBindTextureFn(GL_TEXTURE_2D, y_texture_id);
api->glFramebufferTexture2DEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, y_texture_id, 0);
ASSERT_EQ(api->glCheckFramebufferStatusEXTFn(GL_FRAMEBUFFER),
static_cast<unsigned>(GL_FRAMEBUFFER_COMPLETE));
ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
GLubyte y_value;
api->glReadPixelsFn(size.width() / 2, size.height() / 2, 1, 1, GL_RED,
GL_UNSIGNED_BYTE, &y_value);
EXPECT_EQ(255, y_value);
api->glViewportFn(0, 0, size.width(), size.height());
api->glClearColorFn(kYClearValue / 255.0f, 0, 0, 0);
api->glClearFn(GL_COLOR_BUFFER_BIT);
api->glReadPixelsFn(size.width() / 2, size.height() / 2, 1, 1, GL_RED,
GL_UNSIGNED_BYTE, &y_value);
EXPECT_EQ(kYClearValue, y_value);
y_texture.reset();
EXPECT_TRUE(shared_image_refs[0]->CopyToGpuMemoryBuffer());
auto uv_texture =
shared_image_representation_factory_->ProduceGLTexturePassthrough(
shared_image_refs[1]->mailbox());
ASSERT_NE(uv_texture, nullptr);
GLuint uv_texture_id = uv_texture->GetTextureBase()->service_id();
api->glBindTextureFn(GL_TEXTURE_2D, uv_texture_id);
api->glFramebufferTexture2DEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, uv_texture_id, 0);
ASSERT_EQ(api->glCheckFramebufferStatusEXTFn(GL_FRAMEBUFFER),
static_cast<unsigned>(GL_FRAMEBUFFER_COMPLETE));
ASSERT_EQ(api->glGetErrorFn(), static_cast<GLenum>(GL_NO_ERROR));
GLubyte uv_value[2];
api->glReadPixelsFn(size.width() / 4, size.height() / 4, 1, 1, GL_RG,
GL_UNSIGNED_BYTE, uv_value);
EXPECT_EQ(255, uv_value[0]);
EXPECT_EQ(255, uv_value[1]);
api->glViewportFn(0, 0, size.width(), size.height());
api->glClearColorFn(kUClearValue / 255.0f, kVClearValue / 255.0f, 0, 0);
api->glClearFn(GL_COLOR_BUFFER_BIT);
api->glReadPixelsFn(size.width() / 4, size.height() / 4, 1, 1, GL_RG,
GL_UNSIGNED_BYTE, uv_value);
EXPECT_EQ(kUClearValue, uv_value[0]);
EXPECT_EQ(kVClearValue, uv_value[1]);
uv_texture.reset();
EXPECT_TRUE(shared_image_refs[1]->CopyToGpuMemoryBuffer());
{
base::WritableSharedMemoryMapping shm_mapping = shm_region.Map();
CheckYUV(shm_mapping.GetMemoryAs<uint8_t>(), size.width(), size,
kYClearValue, kUClearValue, kVClearValue);
}
// Both planes use the same underlying shared memory buffer with different
// offsets. Accessing via the Y plane overlay allows reading both planes.
{
auto overlay_representation =
shared_image_representation_factory_->ProduceOverlay(
shared_image_refs[0]->mailbox());
auto scoped_read_access =
overlay_representation->BeginScopedReadAccess(/*needs_gl_image=*/true);
ASSERT_TRUE(scoped_read_access);
auto* gl_image_memory =
gl::GLImageMemory::FromGLImage(scoped_read_access->gl_image());
ASSERT_TRUE(gl_image_memory);
CheckYUV(gl_image_memory->memory(), gl_image_memory->stride(), size,
kYClearValue, kUClearValue, kVClearValue);
}
}
} // anonymous namespace
} // namespace gpu