#include "base/compiler_specific.h"
#include "build/build_config.h"
#include "components/viz/test/test_gpu_service_holder.h"
#include "gpu/command_buffer/client/client_shared_image.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/client/webgpu_implementation.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/webgpu_decoder.h"
#include "gpu/command_buffer/tests/webgpu_test.h"
#include "gpu/config/gpu_finch_features.h"
#include "gpu/config/gpu_test_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/color_space.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/gl_utils.h"
#include "ui/gl/init/gl_factory.h"
#define SKIP_TEST_IF(condition) \
if (condition) \
GTEST_SKIP() << #condition
namespace gpu {
namespace {
class MockBufferMapCallback {
public:
MOCK_METHOD(void,
Call,
(wgpu::MapAsyncStatus status, wgpu::StringView message));
};
std::unique_ptr<testing::StrictMock<MockBufferMapCallback>>
mock_buffer_map_callback;
void ToMockBufferMapCallback(wgpu::MapAsyncStatus status,
wgpu::StringView message) {
mock_buffer_map_callback->Call(status, message);
}
struct WebGPUMailboxTextureTestParams : WebGPUTest::Options {
viz::SharedImageFormat format;
};
std::ostream& operator<<(std::ostream& os,
const WebGPUMailboxTextureTestParams& options) {
DCHECK(options.format == viz::SinglePlaneFormat::kRGBA_8888 ||
options.format == viz::SinglePlaneFormat::kBGRA_8888 ||
options.format == viz::SinglePlaneFormat::kRGBA_F16);
os << options.format.ToTestParamString();
if (options.use_skia_graphite) {
os << "_SkiaGraphite";
}
if (options.enable_unsafe_webgpu) {
os << "_UnsafeWebGPU";
}
if (options.force_fallback_adapter) {
os << "_FallbackAdapter";
}
return os;
}
uint32_t BytesPerTexel(viz::SharedImageFormat format) {
if ((format == viz::SinglePlaneFormat::kRGBA_8888) ||
(format == viz::SinglePlaneFormat::kBGRA_8888)) {
return 4;
}
if (format == viz::SinglePlaneFormat::kRGBA_F16) {
return 8;
}
NOTREACHED();
}
wgpu::TextureFormat VizToWGPUFormat(const viz::SharedImageFormat& format) {
if (format == viz::SinglePlaneFormat::kBGRA_8888) {
return wgpu::TextureFormat::BGRA8Unorm;
}
if (format == viz::SinglePlaneFormat::kRGBA_8888) {
return wgpu::TextureFormat::RGBA8Unorm;
}
if (format == viz::SinglePlaneFormat::kRGBA_F16) {
return wgpu::TextureFormat::RGBA16Float;
}
NOTREACHED() << "Unsupported format: " << format.ToString();
}
}
class WebGPUMailboxTestBase : public WebGPUTest {
protected:
template <typename T>
static error::Error ExecuteCmd(webgpu::WebGPUDecoder* decoder, const T& cmd) {
static_assert(T::kArgFlags == cmd::kFixed,
"T::kArgFlags should equal cmd::kFixed");
int entries_processed = 0;
return decoder->DoCommands(1, (const void*)&cmd,
ComputeNumEntries(sizeof(cmd)),
&entries_processed);
}
template <typename T>
static error::Error ExecuteImmediateCmd(webgpu::WebGPUDecoder* decoder,
const T& cmd,
size_t data_size) {
static_assert(T::kArgFlags == cmd::kAtLeastN,
"T::kArgFlags should equal cmd::kAtLeastN");
int entries_processed = 0;
return decoder->DoCommands(1, (const void*)&cmd,
ComputeNumEntries(sizeof(cmd) + data_size),
&entries_processed);
}
};
class WebGPUMailboxTextureTest
: public WebGPUMailboxTestBase,
public testing::WithParamInterface<WebGPUMailboxTextureTestParams> {
public:
static std::vector<WebGPUMailboxTextureTestParams> TestParams() {
WebGPUMailboxTextureTestParams options = {};
WebGPUMailboxTextureTestParams fallback_options = {};
fallback_options.force_fallback_adapter = true;
fallback_options.enable_unsafe_webgpu = true;
std::vector<WebGPUMailboxTextureTestParams> params;
for (viz::SharedImageFormat format : {
#if !BUILDFLAG(IS_MAC)
viz::SinglePlaneFormat::kRGBA_8888,
#endif
viz::SinglePlaneFormat::kBGRA_8888,
viz::SinglePlaneFormat::kRGBA_F16,
}) {
WebGPUMailboxTextureTestParams o = options;
o.format = format;
#if BUILDFLAG(IS_LINUX)
if (o.format != viz::SinglePlaneFormat::kRGBA_F16) {
params.push_back(o);
}
#else
params.push_back(o);
#endif
o = fallback_options;
o.format = format;
o.use_skia_graphite = false;
params.push_back(o);
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
o.use_skia_graphite = true;
params.push_back(o);
#endif
}
return params;
}
protected:
void SetUp() override {
SKIP_TEST_IF(!WebGPUSupported());
SKIP_TEST_IF(!WebGPUSharedImageSupported());
WebGPUTest::SetUp();
Initialize(GetParam());
device_ = GetNewDevice();
mock_buffer_map_callback =
std::make_unique<testing::StrictMock<MockBufferMapCallback>>();
}
void TearDown() override {
mock_buffer_map_callback = nullptr;
PollUntilIdle();
device_ = nullptr;
WebGPUTest::TearDown();
}
struct AssociateMailboxCmdStorage {
webgpu::cmds::AssociateMailboxImmediate cmd;
GLbyte data[GL_MAILBOX_SIZE_CHROMIUM];
std::array<WGPUTextureFormat, 2u> view_formats;
};
enum class AccessType { Read, Write, ReadWrite };
SharedImageUsageSet GetSharedImageUsage(AccessType access_type) {
SharedImageUsageSet webgpu_usage;
SharedImageUsageSet fallback_usage;
switch (access_type) {
case AccessType::Read:
webgpu_usage = SHARED_IMAGE_USAGE_WEBGPU_READ;
fallback_usage = SHARED_IMAGE_USAGE_RASTER_READ;
break;
case AccessType::Write:
webgpu_usage = SHARED_IMAGE_USAGE_WEBGPU_WRITE;
fallback_usage = SHARED_IMAGE_USAGE_RASTER_WRITE;
break;
case AccessType::ReadWrite:
webgpu_usage =
SHARED_IMAGE_USAGE_WEBGPU_READ | SHARED_IMAGE_USAGE_WEBGPU_WRITE;
fallback_usage =
SHARED_IMAGE_USAGE_RASTER_READ | SHARED_IMAGE_USAGE_RASTER_WRITE;
break;
}
auto si_usage = webgpu_usage;
if (IsUsingFallbackAdapter()) {
si_usage |= fallback_usage;
}
return si_usage;
}
void InitializeTextureColor(
wgpu::Device device,
scoped_refptr<gpu::ClientSharedImage> shared_image,
wgpu::Color clearValue) {
wgpu::TextureDescriptor desc = {
.usage = wgpu::TextureUsage::RenderAttachment,
};
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access =
shared_image->BeginWebGPUTextureAccess(
webgpu(), GetSharedImageInterface()->GenVerifiedSyncToken(), device,
desc, 0, webgpu::WEBGPU_MAILBOX_NONE);
wgpu::RenderPassColorAttachment color_desc = {};
color_desc.view = webgpu_scoped_access->texture().CreateView();
color_desc.loadOp = wgpu::LoadOp::Clear;
color_desc.storeOp = wgpu::StoreOp::Store;
color_desc.clearValue = clearValue;
wgpu::RenderPassDescriptor render_pass_desc = {};
render_pass_desc.colorAttachmentCount = 1;
render_pass_desc.colorAttachments = &color_desc;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&render_pass_desc);
pass.End();
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device.GetQueue();
queue.Submit(1, &commands);
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access));
}
void UninitializeTexture(wgpu::Device device, wgpu::Texture texture) {
wgpu::RenderPassColorAttachment color_desc = {};
color_desc.view = texture.CreateView();
color_desc.loadOp = wgpu::LoadOp::Load;
color_desc.storeOp = wgpu::StoreOp::Discard;
wgpu::RenderPassDescriptor render_pass_desc = {};
render_pass_desc.colorAttachmentCount = 1;
render_pass_desc.colorAttachments = &color_desc;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&render_pass_desc);
pass.End();
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device.GetQueue();
queue.Submit(1, &commands);
}
wgpu::Device device_;
};
TEST_P(WebGPUMailboxTextureTest, AssociateMailboxCmd) {
SharedImageInterface* sii = GetSharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> shared_image =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::Read),
"TestLabel"},
kNullSurfaceHandle);
webgpu::ReservedTexture reservation = webgpu()->ReserveTexture(device_.Get());
GetGpuServiceHolder()->ScheduleGpuMainTask(base::BindOnce(
[](webgpu::WebGPUDecoder* decoder, webgpu::ReservedTexture reservation,
scoped_refptr<gpu::ClientSharedImage> shared_image) {
const gpu::Mailbox& mailbox = shared_image->mailbox();
{
AssociateMailboxCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId + 1, reservation.deviceGeneration,
reservation.id, reservation.generation,
WGPUTextureUsage_TextureBinding, 0u,
webgpu::WEBGPU_MAILBOX_NONE, 0u,
ComputeNumEntries(sizeof(mailbox.name)),
reinterpret_cast<const GLuint*>(&mailbox.name));
EXPECT_EQ(
error::kInvalidArguments,
ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(mailbox.name)));
}
{
AssociateMailboxCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration + 1,
reservation.id, reservation.generation,
WGPUTextureUsage_TextureBinding, 0u,
webgpu::WEBGPU_MAILBOX_NONE, 0u,
ComputeNumEntries(sizeof(mailbox.name)),
reinterpret_cast<const GLuint*>(&mailbox.name));
EXPECT_EQ(
error::kInvalidArguments,
ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(mailbox.name)));
}
{
AssociateMailboxCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
reservation.id + 1, reservation.generation,
WGPUTextureUsage_TextureBinding, 0u,
webgpu::WEBGPU_MAILBOX_NONE, 0u,
ComputeNumEntries(sizeof(mailbox.name)),
reinterpret_cast<const GLuint*>(&mailbox.name));
EXPECT_EQ(
error::kInvalidArguments,
ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(mailbox.name)));
}
{
AssociateMailboxCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
reservation.id, reservation.generation,
UINT64_MAX, 0u,
webgpu::WEBGPU_MAILBOX_NONE, 0u,
ComputeNumEntries(sizeof(mailbox.name)),
reinterpret_cast<const GLuint*>(&mailbox.name));
EXPECT_EQ(
error::kInvalidArguments,
ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(mailbox.name)));
}
{
AssociateMailboxCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
reservation.id, reservation.generation,
WGPUTextureUsage_TextureBinding,
UINT64_MAX, webgpu::WEBGPU_MAILBOX_NONE,
0u, ComputeNumEntries(sizeof(mailbox.name)),
reinterpret_cast<const GLuint*>(&mailbox.name));
EXPECT_EQ(
error::kInvalidArguments,
ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(mailbox.name)));
}
std::vector<GLuint> packed_data;
packed_data.resize(sizeof(mailbox.name) / sizeof(uint32_t));
UNSAFE_TODO(memcpy(reinterpret_cast<char*>(packed_data.data()),
&mailbox.name, sizeof(mailbox.name)));
uint32_t view_format_count = 0u;
if (GetParam().format == viz::SinglePlaneFormat::kRGBA_F16) {
} else if (GetParam().format == viz::SinglePlaneFormat::kRGBA_8888) {
view_format_count = 1u;
packed_data.push_back(
static_cast<uint32_t>(WGPUTextureFormat_RGBA8UnormSrgb));
} else if (GetParam().format == viz::SinglePlaneFormat::kBGRA_8888) {
view_format_count = 2u;
packed_data.push_back(
static_cast<uint32_t>(WGPUTextureFormat_BGRA8UnormSrgb));
packed_data.push_back(
static_cast<uint32_t>(WGPUTextureFormat_BGRA8Unorm));
} else {
NOTREACHED();
}
{
AssociateMailboxCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
reservation.id, reservation.generation, UINT64_MAX, 0u,
webgpu::WEBGPU_MAILBOX_NONE, 0u, 0u, packed_data.data());
EXPECT_EQ(error::kOutOfBounds,
ExecuteImmediateCmd(decoder, cmd.cmd, 0u));
}
{
AssociateMailboxCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
reservation.id, reservation.generation, UINT64_MAX, 0u,
webgpu::WEBGPU_MAILBOX_NONE, view_format_count,
ComputeNumEntries(sizeof(mailbox.name)) - 1u,
packed_data.data());
EXPECT_EQ(error::kOutOfBounds,
ExecuteImmediateCmd(decoder, cmd.cmd,
sizeof(uint32_t) * packed_data.size()));
}
for (int adjustment : {-1, -2}) {
AssociateMailboxCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
reservation.id, reservation.generation,
WGPUTextureUsage_TextureBinding, 0u,
webgpu::WEBGPU_MAILBOX_NONE, view_format_count,
packed_data.size() + adjustment, packed_data.data());
EXPECT_EQ(error::kOutOfBounds,
ExecuteImmediateCmd(decoder, cmd.cmd,
sizeof(uint32_t) * packed_data.size()));
}
for (int adjustment : {-1, 1}) {
AssociateMailboxCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
reservation.id, reservation.generation,
WGPUTextureUsage_TextureBinding, 0u,
webgpu::WEBGPU_MAILBOX_NONE,
view_format_count + adjustment, packed_data.size(),
packed_data.data());
EXPECT_EQ(error::kOutOfBounds,
ExecuteImmediateCmd(decoder, cmd.cmd,
sizeof(uint32_t) * packed_data.size()));
}
{
AssociateMailboxCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
reservation.id, reservation.generation,
WGPUTextureUsage_TextureBinding, 0u,
webgpu::WEBGPU_MAILBOX_NONE, view_format_count,
packed_data.size(), packed_data.data());
EXPECT_EQ(error::kNoError,
ExecuteImmediateCmd(decoder, cmd.cmd,
sizeof(uint32_t) * packed_data.size()));
}
{
AssociateMailboxCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
reservation.id, reservation.generation,
WGPUTextureUsage_TextureBinding, 0u,
webgpu::WEBGPU_MAILBOX_NONE, 0u,
ComputeNumEntries(sizeof(mailbox.name)),
reinterpret_cast<const GLuint*>(&mailbox.name));
EXPECT_EQ(
error::kInvalidArguments,
ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(mailbox.name)));
}
{
webgpu::cmds::DissociateMailbox cmd;
cmd.Init(reservation.id, reservation.generation);
EXPECT_EQ(error::kNoError, ExecuteCmd(decoder, cmd));
}
},
GetDecoder(), reservation, std::move(shared_image)));
GetGpuServiceHolder()
->gpu_main_thread_task_runner()
->RunsTasksInCurrentSequence();
}
TEST_P(WebGPUMailboxTextureTest,
AssociateMailboxCmdBadMailboxMakesErrorTexture) {
SharedImageInterface* sii = GetSharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> shared_image =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::Read),
"TestLabel"},
kNullSurfaceHandle);
webgpu::ReservedTexture reservation = webgpu()->ReserveTexture(device_.Get());
GetGpuServiceHolder()->ScheduleGpuMainTask(base::BindOnce(
[](webgpu::WebGPUDecoder* decoder, webgpu::ReservedTexture reservation,
scoped_refptr<gpu::ClientSharedImage> shared_image) {
{
gpu::Mailbox bad_mailbox;
AssociateMailboxCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
reservation.id, reservation.generation,
WGPUTextureUsage_TextureBinding, 0u,
webgpu::WEBGPU_MAILBOX_NONE, 0u,
ComputeNumEntries(sizeof(bad_mailbox.name)),
reinterpret_cast<const GLuint*>(&bad_mailbox.name));
EXPECT_EQ(
error::kNoError,
ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(bad_mailbox.name)));
}
},
GetDecoder(), reservation, std::move(shared_image)));
wgpu::Texture texture = wgpu::Texture::Acquire(reservation.texture);
EXPECT_WEBGPU_ERROR(device_, wgpu::ErrorType::Validation,
texture.CreateView());
}
TEST_P(WebGPUMailboxTextureTest, DissociateMailboxCmd) {
SharedImageInterface* sii = GetSharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> shared_image =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::Read),
"TestLabel"},
kNullSurfaceHandle);
webgpu::ReservedTexture reservation = webgpu()->ReserveTexture(device_.Get());
GetGpuServiceHolder()->ScheduleGpuMainTask(base::BindOnce(
[](webgpu::WebGPUDecoder* decoder, webgpu::ReservedTexture reservation,
scoped_refptr<gpu::ClientSharedImage> shared_image) {
const gpu::Mailbox& mailbox = shared_image->mailbox();
{
AssociateMailboxCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
reservation.id, reservation.generation,
WGPUTextureUsage_TextureBinding, 0u,
webgpu::WEBGPU_MAILBOX_NONE, 0u,
ComputeNumEntries(sizeof(mailbox.name)),
reinterpret_cast<const GLuint*>(&mailbox.name));
EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(decoder, cmd.cmd,
sizeof(mailbox.name)));
}
{
webgpu::cmds::DissociateMailbox cmd;
cmd.Init(reservation.id + 1, reservation.generation);
EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(decoder, cmd));
}
{
webgpu::cmds::DissociateMailbox cmd;
cmd.Init(reservation.id, reservation.generation + 1);
EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(decoder, cmd));
}
{
webgpu::cmds::DissociateMailbox cmd;
cmd.Init(reservation.id, reservation.generation);
EXPECT_EQ(error::kNoError, ExecuteCmd(decoder, cmd));
}
{
webgpu::cmds::DissociateMailbox cmd;
cmd.Init(reservation.id, reservation.generation);
EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(decoder, cmd));
}
},
GetDecoder(), reservation, std::move(shared_image)));
GetGpuServiceHolder()
->gpu_main_thread_task_runner()
->RunsTasksInCurrentSequence();
}
TEST_P(WebGPUMailboxTextureTest, WriteToMailboxThenReadFromIt) {
SKIP_TEST_IF(GetParam().format == viz::SinglePlaneFormat::kRGBA_F16);
SharedImageInterface* sii = GetSharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> shared_image =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::ReadWrite),
"TestLabel"},
kNullSurfaceHandle);
InitializeTextureColor(device_, shared_image, {0.0, 0.0, 1.0, 1.0});
{
wgpu::TextureDescriptor desc = {
.usage = wgpu::TextureUsage::CopySrc,
};
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access =
shared_image->BeginWebGPUTextureAccess(webgpu(), gpu::SyncToken(),
device_, desc, 0,
webgpu::WEBGPU_MAILBOX_NONE);
wgpu::BufferDescriptor buffer_desc;
buffer_desc.size = 4;
buffer_desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
wgpu::Buffer readback_buffer = device_.CreateBuffer(&buffer_desc);
wgpu::TexelCopyTextureInfo copy_src = {};
copy_src.texture = webgpu_scoped_access->texture();
copy_src.mipLevel = 0;
copy_src.origin = {0, 0, 0};
wgpu::TexelCopyBufferInfo copy_dst = {};
copy_dst.buffer = readback_buffer;
copy_dst.layout.offset = 0;
copy_dst.layout.bytesPerRow = 256;
wgpu::Extent3D copy_size = {1, 1, 1};
wgpu::CommandEncoder encoder = device_.CreateCommandEncoder();
encoder.CopyTextureToBuffer(©_src, ©_dst, ©_size);
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device_.GetQueue();
queue.Submit(1, &commands);
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access));
readback_buffer.MapAsync(wgpu::MapMode::Read, 0, 4,
wgpu::CallbackMode::AllowSpontaneous,
ToMockBufferMapCallback);
EXPECT_CALL(*mock_buffer_map_callback,
Call(wgpu::MapAsyncStatus::Success, testing::_))
.Times(1);
WaitForCompletion(device_);
const void* data = readback_buffer.GetConstMappedRange();
if (GetParam().format == viz::SinglePlaneFormat::kRGBA_8888) {
EXPECT_EQ(0xFFFF0000u, *static_cast<const uint32_t*>(data));
} else if (GetParam().format == viz::SinglePlaneFormat::kBGRA_8888) {
EXPECT_EQ(0xFF0000FFu, *static_cast<const uint32_t*>(data));
} else {
NOTREACHED();
}
}
}
TEST_P(WebGPUMailboxTextureTest,
PassWriteUsagesWhenAssociatingReadOnlyMailbox) {
SharedImageInterface* sii = GetSharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> shared_image =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::Read),
"TestLabel"},
kNullSurfaceHandle);
wgpu::TextureDescriptor desc = {
.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst,
};
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access =
shared_image->BeginWebGPUTextureAccess(
webgpu(), sii->GenVerifiedSyncToken(), device_, desc, 0,
webgpu::WEBGPU_MAILBOX_NONE);
wgpu::BufferDescriptor buffer_desc;
buffer_desc.size = BytesPerTexel(GetParam().format);
buffer_desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
wgpu::Buffer readback_buffer = device_.CreateBuffer(&buffer_desc);
wgpu::TexelCopyTextureInfo copy_src = {};
copy_src.texture = webgpu_scoped_access->texture();
copy_src.mipLevel = 0;
copy_src.origin = {0, 0, 0};
wgpu::TexelCopyBufferInfo copy_dst = {};
copy_dst.buffer = readback_buffer;
copy_dst.layout.offset = 0;
copy_dst.layout.bytesPerRow = 256;
wgpu::Extent3D copy_size = {1, 1, 1};
wgpu::CommandEncoder encoder = device_.CreateCommandEncoder();
encoder.CopyTextureToBuffer(©_src, ©_dst, ©_size);
EXPECT_WEBGPU_ERROR(device_, wgpu::ErrorType::Validation, encoder.Finish());
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access));
WaitForCompletion(device_);
}
TEST_P(WebGPUMailboxTextureTest,
PassInternalWriteUsagesWhenAssociatingReadOnlyMailbox) {
SharedImageInterface* sii = GetSharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> shared_image =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::Read),
"TestLabel"},
kNullSurfaceHandle);
wgpu::TextureDescriptor desc = {
.usage = wgpu::TextureUsage::CopySrc,
};
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access =
shared_image->BeginWebGPUTextureAccess(
webgpu(), sii->GenVerifiedSyncToken(), device_, desc,
WGPUTextureUsage_CopyDst, webgpu::WEBGPU_MAILBOX_NONE);
wgpu::BufferDescriptor buffer_desc;
buffer_desc.size = BytesPerTexel(GetParam().format);
buffer_desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
wgpu::Buffer readback_buffer = device_.CreateBuffer(&buffer_desc);
wgpu::TexelCopyTextureInfo copy_src = {};
copy_src.texture = webgpu_scoped_access->texture();
copy_src.mipLevel = 0;
copy_src.origin = {0, 0, 0};
wgpu::TexelCopyBufferInfo copy_dst = {};
copy_dst.buffer = readback_buffer;
copy_dst.layout.offset = 0;
copy_dst.layout.bytesPerRow = 256;
wgpu::Extent3D copy_size = {1, 1, 1};
wgpu::CommandEncoder encoder = device_.CreateCommandEncoder();
encoder.CopyTextureToBuffer(©_src, ©_dst, ©_size);
EXPECT_WEBGPU_ERROR(device_, wgpu::ErrorType::Validation, encoder.Finish());
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access));
WaitForCompletion(device_);
}
TEST_P(WebGPUMailboxTextureTest, PassDiscardWhenAssociatingReadOnlyMailbox) {
SharedImageInterface* sii = GetSharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> shared_image =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::Read),
"TestLabel"},
kNullSurfaceHandle);
wgpu::TextureDescriptor desc = {
.usage = wgpu::TextureUsage::CopySrc,
};
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access =
shared_image->BeginWebGPUTextureAccess(
webgpu(), sii->GenVerifiedSyncToken(), device_, desc, 0,
webgpu::WEBGPU_MAILBOX_DISCARD);
wgpu::BufferDescriptor buffer_desc;
buffer_desc.size = BytesPerTexel(GetParam().format);
buffer_desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
wgpu::Buffer readback_buffer = device_.CreateBuffer(&buffer_desc);
wgpu::TexelCopyTextureInfo copy_src = {};
copy_src.texture = webgpu_scoped_access->texture();
copy_src.mipLevel = 0;
copy_src.origin = {0, 0, 0};
wgpu::TexelCopyBufferInfo copy_dst = {};
copy_dst.buffer = readback_buffer;
copy_dst.layout.offset = 0;
copy_dst.layout.bytesPerRow = 256;
wgpu::Extent3D copy_size = {1, 1, 1};
wgpu::CommandEncoder encoder = device_.CreateCommandEncoder();
encoder.CopyTextureToBuffer(©_src, ©_dst, ©_size);
EXPECT_WEBGPU_ERROR(device_, wgpu::ErrorType::Validation, encoder.Finish());
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access));
WaitForCompletion(device_);
}
TEST_P(WebGPUMailboxTextureTest,
PassDiscardWhenAssociatingMailboxWithoutUsageSupportingClearing) {
SharedImageInterface* sii = GetSharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> shared_image =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::ReadWrite),
"TestLabel"},
kNullSurfaceHandle);
wgpu::TextureDescriptor desc = {
.usage = wgpu::TextureUsage::CopySrc,
};
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access =
shared_image->BeginWebGPUTextureAccess(
webgpu(), sii->GenVerifiedSyncToken(), device_, desc, 0,
webgpu::WEBGPU_MAILBOX_DISCARD);
wgpu::BufferDescriptor buffer_desc;
buffer_desc.size = BytesPerTexel(GetParam().format);
buffer_desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
wgpu::Buffer readback_buffer = device_.CreateBuffer(&buffer_desc);
wgpu::TexelCopyTextureInfo copy_src = {};
copy_src.texture = webgpu_scoped_access->texture();
copy_src.mipLevel = 0;
copy_src.origin = {0, 0, 0};
wgpu::TexelCopyBufferInfo copy_dst = {};
copy_dst.buffer = readback_buffer;
copy_dst.layout.offset = 0;
copy_dst.layout.bytesPerRow = 256;
wgpu::Extent3D copy_size = {1, 1, 1};
wgpu::CommandEncoder encoder = device_.CreateCommandEncoder();
encoder.CopyTextureToBuffer(©_src, ©_dst, ©_size);
EXPECT_WEBGPU_ERROR(device_, wgpu::ErrorType::Validation, encoder.Finish());
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access));
WaitForCompletion(device_);
}
TEST_P(WebGPUMailboxTextureTest,
ReadWritableUninitializedSharedImageWhenAccessedWithInternalWriteUsage) {
SharedImageInterface* sii = GetSharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> shared_image =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::ReadWrite),
"TestLabel"},
kNullSurfaceHandle);
wgpu::TextureDescriptor desc = {
.usage = wgpu::TextureUsage::CopySrc,
};
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access =
shared_image->BeginWebGPUTextureAccess(
webgpu(), sii->GenVerifiedSyncToken(), device_, desc,
WGPUTextureUsage_RenderAttachment, webgpu::WEBGPU_MAILBOX_NONE);
wgpu::CommandEncoder encoder = device_.CreateCommandEncoder();
wgpu::BufferDescriptor buffer_desc;
buffer_desc.size = BytesPerTexel(GetParam().format);
buffer_desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
wgpu::Buffer readback_buffer = device_.CreateBuffer(&buffer_desc);
wgpu::TexelCopyTextureInfo copy_src = {};
copy_src.texture = webgpu_scoped_access->texture();
copy_src.mipLevel = 0;
copy_src.origin = {0, 0, 0};
wgpu::TexelCopyBufferInfo copy_dst = {};
copy_dst.buffer = readback_buffer;
copy_dst.layout.offset = 0;
copy_dst.layout.bytesPerRow = 256;
wgpu::Extent3D copy_size = {1, 1, 1};
encoder.CopyTextureToBuffer(©_src, ©_dst, ©_size);
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device_.GetQueue();
queue.Submit(1, &commands);
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access));
readback_buffer.MapAsync(wgpu::MapMode::Read, 0, buffer_desc.size,
wgpu::CallbackMode::AllowSpontaneous,
ToMockBufferMapCallback);
EXPECT_CALL(*mock_buffer_map_callback,
Call(wgpu::MapAsyncStatus::Success, testing::_))
.Times(1);
WaitForCompletion(device_);
const uint8_t* data = static_cast<const uint8_t*>(
readback_buffer.GetConstMappedRange(0, buffer_desc.size));
for (uint32_t i = 0; i < buffer_desc.size; ++i) {
UNSAFE_TODO(EXPECT_EQ(data[i], uint8_t(0)));
}
EXPECT_EQ(webgpu_scoped_access, nullptr);
webgpu_scoped_access = shared_image->BeginWebGPUTextureAccess(
webgpu(), sii->GenVerifiedSyncToken(), device_, desc, 0,
webgpu::WEBGPU_MAILBOX_NONE);
copy_src.texture = webgpu_scoped_access->texture();
wgpu::Buffer readback_buffer2 = device_.CreateBuffer(&buffer_desc);
copy_dst.buffer = readback_buffer2;
wgpu::CommandEncoder encoder2 = device_.CreateCommandEncoder();
encoder2.CopyTextureToBuffer(©_src, ©_dst, ©_size);
commands = encoder2.Finish();
queue.Submit(1, &commands);
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access));
readback_buffer2.MapAsync(wgpu::MapMode::Read, 0, buffer_desc.size,
wgpu::CallbackMode::AllowSpontaneous,
ToMockBufferMapCallback);
EXPECT_CALL(*mock_buffer_map_callback,
Call(wgpu::MapAsyncStatus::Success, testing::_))
.Times(1);
WaitForCompletion(device_);
}
TEST_P(WebGPUMailboxTextureTest,
ReadWritableUninitializedSharedImageWithUsageSupportingLazyClearing) {
SharedImageInterface* sii = GetSharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> shared_image =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::ReadWrite),
"TestLabel"},
kNullSurfaceHandle);
InitializeTextureColor(device_, shared_image, {1.0, 0, 0, 1.0});
wgpu::TextureDescriptor desc = {
.usage =
wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment,
};
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access =
shared_image->BeginWebGPUTextureAccess(webgpu(), gpu::SyncToken(),
device_, desc, 0,
webgpu::WEBGPU_MAILBOX_DISCARD);
wgpu::RenderPassColorAttachment color_desc = {};
color_desc.view = webgpu_scoped_access->texture().CreateView();
color_desc.loadOp = wgpu::LoadOp::Load;
color_desc.storeOp = wgpu::StoreOp::Store;
wgpu::RenderPassDescriptor render_pass_desc = {};
render_pass_desc.colorAttachmentCount = 1;
render_pass_desc.colorAttachments = &color_desc;
wgpu::CommandEncoder encoder = device_.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&render_pass_desc);
pass.End();
wgpu::BufferDescriptor buffer_desc;
buffer_desc.size = BytesPerTexel(GetParam().format);
buffer_desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
wgpu::Buffer readback_buffer = device_.CreateBuffer(&buffer_desc);
wgpu::TexelCopyTextureInfo copy_src = {};
copy_src.texture = webgpu_scoped_access->texture();
copy_src.mipLevel = 0;
copy_src.origin = {0, 0, 0};
wgpu::TexelCopyBufferInfo copy_dst = {};
copy_dst.buffer = readback_buffer;
copy_dst.layout.offset = 0;
copy_dst.layout.bytesPerRow = 256;
wgpu::Extent3D copy_size = {1, 1, 1};
encoder.CopyTextureToBuffer(©_src, ©_dst, ©_size);
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device_.GetQueue();
queue.Submit(1, &commands);
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access));
readback_buffer.MapAsync(wgpu::MapMode::Read, 0, buffer_desc.size,
wgpu::CallbackMode::AllowSpontaneous,
ToMockBufferMapCallback);
EXPECT_CALL(*mock_buffer_map_callback,
Call(wgpu::MapAsyncStatus::Success, testing::_))
.Times(1);
WaitForCompletion(device_);
const uint8_t* data = static_cast<const uint8_t*>(
readback_buffer.GetConstMappedRange(0, buffer_desc.size));
for (uint32_t i = 0; i < buffer_desc.size; ++i) {
UNSAFE_TODO(EXPECT_EQ(data[i], uint8_t(0)));
}
}
TEST_P(
WebGPUMailboxTextureTest,
ReadWritableUninitializedSharedImageWithInternalUsageSupportingLazyClearing) {
SharedImageInterface* sii = GetSharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> shared_image =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::ReadWrite),
"TestLabel"},
kNullSurfaceHandle);
InitializeTextureColor(device_, shared_image, {1.0, 0, 0, 1.0});
wgpu::TextureDescriptor desc = {
.usage = wgpu::TextureUsage::CopySrc,
};
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access =
shared_image->BeginWebGPUTextureAccess(
webgpu(), gpu::SyncToken(), device_, desc,
WGPUTextureUsage_RenderAttachment, webgpu::WEBGPU_MAILBOX_DISCARD);
wgpu::BufferDescriptor buffer_desc;
buffer_desc.size = BytesPerTexel(GetParam().format);
buffer_desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
wgpu::Buffer readback_buffer = device_.CreateBuffer(&buffer_desc);
wgpu::TexelCopyTextureInfo copy_src = {};
copy_src.texture = webgpu_scoped_access->texture();
copy_src.mipLevel = 0;
copy_src.origin = {0, 0, 0};
wgpu::TexelCopyBufferInfo copy_dst = {};
copy_dst.buffer = readback_buffer;
copy_dst.layout.offset = 0;
copy_dst.layout.bytesPerRow = 256;
wgpu::Extent3D copy_size = {1, 1, 1};
wgpu::CommandEncoder encoder = device_.CreateCommandEncoder();
encoder.CopyTextureToBuffer(©_src, ©_dst, ©_size);
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device_.GetQueue();
queue.Submit(1, &commands);
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access));
readback_buffer.MapAsync(wgpu::MapMode::Read, 0, buffer_desc.size,
wgpu::CallbackMode::AllowSpontaneous,
ToMockBufferMapCallback);
EXPECT_CALL(*mock_buffer_map_callback,
Call(wgpu::MapAsyncStatus::Success, testing::_))
.Times(1);
WaitForCompletion(device_);
const uint8_t* data = static_cast<const uint8_t*>(
readback_buffer.GetConstMappedRange(0, buffer_desc.size));
for (uint32_t i = 0; i < buffer_desc.size; ++i) {
UNSAFE_TODO(EXPECT_EQ(data[i], uint8_t(0)));
}
}
TEST_P(WebGPUMailboxTextureTest, ErrorWhenUsingTextureAfterDissociate) {
SharedImageInterface* sii = GetSharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> shared_image =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::ReadWrite),
"TestLabel"},
kNullSurfaceHandle);
wgpu::TextureDescriptor desc = {.usage =
wgpu::TextureUsage::CopySrc |
wgpu::TextureUsage::RenderAttachment};
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access =
shared_image->BeginWebGPUTextureAccess(
webgpu(), sii->GenVerifiedSyncToken(), device_, desc, 0,
webgpu::WEBGPU_MAILBOX_NONE);
wgpu::Texture texture = webgpu_scoped_access->texture();
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access));
wgpu::TextureDescriptor dst_desc = {};
dst_desc.size = {1, 1};
dst_desc.usage = wgpu::TextureUsage::CopyDst;
DCHECK(GetParam().format == viz::SinglePlaneFormat::kRGBA_8888 ||
GetParam().format == viz::SinglePlaneFormat::kBGRA_8888 ||
GetParam().format == viz::SinglePlaneFormat::kRGBA_F16);
dst_desc.format = VizToWGPUFormat(GetParam().format);
wgpu::TexelCopyTextureInfo src_image = {};
src_image.texture = texture;
wgpu::TexelCopyTextureInfo dst_image = {};
dst_image.texture = device_.CreateTexture(&dst_desc);
wgpu::Extent3D extent = {1, 1};
wgpu::CommandEncoder encoder = device_.CreateCommandEncoder();
encoder.CopyTextureToTexture(&src_image, &dst_image, &extent);
wgpu::CommandBuffer commandBuffer = encoder.Finish();
WaitForCompletion(device_);
EXPECT_WEBGPU_ERROR(device_, wgpu::ErrorType::Validation,
device_.GetQueue().Submit(1, &commandBuffer));
}
TEST_P(WebGPUMailboxTextureTest, UseA_UseB_DestroyA_DestroyB) {
SharedImageInterface* sii = GetSharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> shared_image_a =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::ReadWrite),
"TestLabel"},
kNullSurfaceHandle);
scoped_refptr<gpu::ClientSharedImage> shared_image_b =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::ReadWrite),
"TestLabel"},
kNullSurfaceHandle);
wgpu::TextureDescriptor desc = {.usage =
wgpu::TextureUsage::RenderAttachment};
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access_a =
shared_image_a->BeginWebGPUTextureAccess(
webgpu(), sii->GenVerifiedSyncToken(), device_, desc, 0,
webgpu::WEBGPU_MAILBOX_NONE);
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access_b =
shared_image_b->BeginWebGPUTextureAccess(
webgpu(), sii->GenVerifiedSyncToken(), device_, desc, 0,
webgpu::WEBGPU_MAILBOX_NONE);
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access_a));
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access_b));
webgpu()->FlushCommands();
}
TEST_P(WebGPUMailboxTextureTest, AssociateOnTwoDevicesAtTheSameTime) {
SharedImageInterface* sii = GetSharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> shared_image_a =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::ReadWrite),
"TestLabel"},
kNullSurfaceHandle);
scoped_refptr<gpu::ClientSharedImage> shared_image_b =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::ReadWrite),
"TestLabel"},
kNullSurfaceHandle);
wgpu::Device device_a = GetNewDevice();
wgpu::Device device_b = GetNewDevice();
wgpu::TextureDescriptor desc = {.usage =
wgpu::TextureUsage::RenderAttachment};
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access_a =
shared_image_a->BeginWebGPUTextureAccess(
webgpu(), sii->GenVerifiedSyncToken(), device_a, desc, 0,
webgpu::WEBGPU_MAILBOX_NONE);
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access_b =
shared_image_b->BeginWebGPUTextureAccess(
webgpu(), sii->GenVerifiedSyncToken(), device_b, desc, 0,
webgpu::WEBGPU_MAILBOX_NONE);
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access_a));
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access_b));
webgpu()->FlushCommands();
}
TEST_P(WebGPUMailboxTextureTest, ReflectionOfDescriptor) {
SharedImageInterface* sii = GetSharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> shared_image1 =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::ReadWrite),
"TestLabel"},
kNullSurfaceHandle);
scoped_refptr<gpu::ClientSharedImage> shared_image2 =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::Read),
"TestLabel"},
kNullSurfaceHandle);
wgpu::TextureDescriptor desc1 = {};
desc1.size = {1, 2, 3};
desc1.format = wgpu::TextureFormat::R32Float;
desc1.usage = wgpu::TextureUsage::CopyDst;
desc1.dimension = wgpu::TextureDimension::e2D;
desc1.sampleCount = 1;
desc1.mipLevelCount = 1;
wgpu::TextureDescriptor desc2 = {};
desc2.size = {4, 5, 6};
desc2.format = wgpu::TextureFormat::RGBA8Unorm;
desc2.usage = wgpu::TextureUsage::CopySrc;
desc2.dimension = wgpu::TextureDimension::e1D;
desc2.sampleCount = 4;
desc2.mipLevelCount = 3;
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access1 =
shared_image1->BeginWebGPUTextureAccess(
webgpu(), sii->GenVerifiedSyncToken(), device_, desc1, 0,
webgpu::WEBGPU_MAILBOX_NONE);
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access2 =
shared_image2->BeginWebGPUTextureAccess(
webgpu(), sii->GenVerifiedSyncToken(), device_, desc2, 0,
webgpu::WEBGPU_MAILBOX_NONE);
wgpu::Texture texture1 = webgpu_scoped_access1->texture();
wgpu::Texture texture2 = webgpu_scoped_access2->texture();
ASSERT_EQ(desc1.size.width, texture1.GetWidth());
ASSERT_EQ(desc1.size.height, texture1.GetHeight());
ASSERT_EQ(desc1.size.depthOrArrayLayers, texture1.GetDepthOrArrayLayers());
ASSERT_EQ(desc1.format, texture1.GetFormat());
ASSERT_EQ(desc1.usage, texture1.GetUsage());
ASSERT_EQ(desc1.dimension, texture1.GetDimension());
ASSERT_EQ(desc1.sampleCount, texture1.GetSampleCount());
ASSERT_EQ(desc1.mipLevelCount, texture1.GetMipLevelCount());
ASSERT_EQ(desc2.size.width, texture2.GetWidth());
ASSERT_EQ(desc2.size.height, texture2.GetHeight());
ASSERT_EQ(desc2.size.depthOrArrayLayers, texture2.GetDepthOrArrayLayers());
ASSERT_EQ(desc2.format, texture2.GetFormat());
ASSERT_EQ(desc2.usage, texture2.GetUsage());
ASSERT_EQ(desc2.dimension, texture2.GetDimension());
ASSERT_EQ(desc2.sampleCount, texture2.GetSampleCount());
ASSERT_EQ(desc2.mipLevelCount, texture2.GetMipLevelCount());
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access1));
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access2));
}
TEST_P(WebGPUMailboxTextureTest, AssociateInvalidViewFormats) {
wgpu::TextureDescriptor desc = {};
desc.size = {1, 1, 1};
desc.format = VizToWGPUFormat(GetParam().format);
desc.usage = wgpu::TextureUsage::RenderAttachment;
desc.dimension = wgpu::TextureDimension::e2D;
desc.sampleCount = 1;
desc.mipLevelCount = 1;
SharedImageInterface* sii = GetSharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> shared_image =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::ReadWrite),
"TestLabel"},
kNullSurfaceHandle);
wgpu::TextureFormat view_formats = wgpu::TextureFormat::R8Unorm;
desc.viewFormats = &view_formats;
desc.viewFormatCount = 1;
device_.PushErrorScope(wgpu::ErrorFilter::Validation);
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access =
shared_image->BeginWebGPUTextureAccess(
webgpu(), sii->GenVerifiedSyncToken(), device_, desc, 0,
webgpu::WEBGPU_MAILBOX_NONE);
device_.PopErrorScope(
wgpu::CallbackMode::AllowSpontaneous,
[](wgpu::PopErrorScopeStatus, wgpu::ErrorType, wgpu::StringView) {});
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access));
}
TEST_P(WebGPUMailboxTextureTest, AssociateDissociateMailboxWhenNotCurrent) {
SharedImageInterface* sii = GetSharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> shared_image =
sii->CreateSharedImage({GetParam().format,
{1, 1},
gfx::ColorSpace::CreateSRGB(),
GetSharedImageUsage(AccessType::ReadWrite),
"TestLabel"},
kNullSurfaceHandle);
scoped_refptr<gl::GLContext> gl_context1;
scoped_refptr<gl::GLContext> gl_context2;
scoped_refptr<gl::GLSurface> gl_surface1;
scoped_refptr<gl::GLSurface> gl_surface2;
auto CreateAndMakeGLContextCurrent =
[&](scoped_refptr<gl::GLContext>* gl_context_out,
scoped_refptr<gl::GLSurface>* gl_surface_out) {
GetGpuServiceHolder()->ScheduleGpuMainTask(base::BindOnce(
[](scoped_refptr<gl::GLContext>* gl_context_out,
scoped_refptr<gl::GLSurface>* gl_surface_out) {
auto gl_surface = gl::init::CreateOffscreenGLSurface(
gl::GetDefaultDisplay(), gfx::Size(4, 4));
auto gl_context = gl::init::CreateGLContext(
nullptr, gl_surface.get(), gl::GLContextAttribs());
EXPECT_TRUE(gl_context->MakeCurrent(gl_surface.get()))
<< "Failed to make GL context current";
*gl_context_out = std::move(gl_context);
*gl_surface_out = std::move(gl_surface);
},
gl_context_out, gl_surface_out));
GetGpuServiceHolder()
->gpu_main_thread_task_runner()
->RunsTasksInCurrentSequence();
};
CreateAndMakeGLContextCurrent(&gl_context1, &gl_surface1);
wgpu::TextureDescriptor desc = {
.usage = wgpu::TextureUsage::RenderAttachment,
};
std::unique_ptr<WebGPUTextureScopedAccess> webgpu_scoped_access =
shared_image->BeginWebGPUTextureAccess(
webgpu(), GetSharedImageInterface()->GenVerifiedSyncToken(), device_,
desc, 0, webgpu::WEBGPU_MAILBOX_NONE);
wgpu::RenderPassColorAttachment color_desc = {};
color_desc.view = webgpu_scoped_access->texture().CreateView();
color_desc.loadOp = wgpu::LoadOp::Clear;
color_desc.storeOp = wgpu::StoreOp::Store;
color_desc.clearValue = {0.0, 1.0, 0.0, 1.0};
wgpu::RenderPassDescriptor render_pass_desc = {};
render_pass_desc.colorAttachmentCount = 1;
render_pass_desc.colorAttachments = &color_desc;
wgpu::CommandEncoder encoder = device_.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&render_pass_desc);
pass.End();
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device_.GetQueue();
queue.Submit(1, &commands);
WaitForCompletion(device_);
CreateAndMakeGLContextCurrent(&gl_context2, &gl_surface2);
WebGPUTextureScopedAccess::EndAccess(std::move(webgpu_scoped_access));
WaitForCompletion(device_);
GetGpuServiceHolder()->ScheduleGpuMainTask(
base::BindOnce([](scoped_refptr<gl::GLContext> gl_context1,
scoped_refptr<gl::GLContext> gl_context2,
scoped_refptr<gl::GLSurface> gl_surface1,
scoped_refptr<gl::GLSurface> gl_surface2) {},
std::move(gl_context1), std::move(gl_context2),
std::move(gl_surface1), std::move(gl_surface2)));
}
INSTANTIATE_TEST_SUITE_P(
,
WebGPUMailboxTextureTest,
::testing::ValuesIn(WebGPUMailboxTextureTest::TestParams()),
::testing::PrintToStringParamName());
class WebGPUMailboxBufferTest : public WebGPUMailboxTestBase {
public:
void SetUp() override {
SKIP_TEST_IF(!WebGPUSupported());
SKIP_TEST_IF(!WebGPUSharedImageSupported());
WebGPUTest::SetUp();
WebGPUTest::Options options = {};
options.enable_unsafe_webgpu = true;
Initialize(options);
wgpu::AdapterInfo info;
adapter_.GetInfo(&info);
SKIP_TEST_IF(info.backendType != wgpu::BackendType::D3D12);
device_ = GetNewDevice(kRequiredFeatures);
mock_buffer_map_callback =
std::make_unique<testing::StrictMock<MockBufferMapCallback>>();
SharedImageInterface* sii = GetSharedImageInterface();
shared_image_ = sii->CreateSharedImage(
{viz::SharedImageFormat(),
{kBufferSize, 1},
gfx::ColorSpace(),
kTopLeft_GrSurfaceOrigin,
kUnknown_SkAlphaType,
gpu::SHARED_IMAGE_USAGE_WEBGPU_READ |
gpu::SHARED_IMAGE_USAGE_WEBGPU_WRITE |
gpu::SHARED_IMAGE_USAGE_WEBGPU_SHARED_BUFFER,
"TestLabel"},
kNullSurfaceHandle);
}
void TearDown() override {
mock_buffer_map_callback = nullptr;
PollUntilIdle();
device_ = nullptr;
WebGPUTest::TearDown();
}
struct AssociateMailboxForBufferCmdStorage {
webgpu::cmds::AssociateMailboxForBufferImmediate cmd;
GLbyte data[GL_MAILBOX_SIZE_CHROMIUM];
};
protected:
std::vector<wgpu::FeatureName> kRequiredFeatures = {
wgpu::FeatureName::SharedBufferMemoryD3D12Resource};
const int kBufferSize = 4;
const uint32_t kBufferData = 0x12345678;
wgpu::Device device_;
scoped_refptr<gpu::ClientSharedImage> shared_image_;
};
TEST_F(WebGPUMailboxBufferTest, AssociateMailboxForBufferCmd) {
webgpu::ReservedBuffer reservation = webgpu()->ReserveBuffer(device_.Get());
GetGpuServiceHolder()->ScheduleGpuMainTask(base::BindOnce(
[](webgpu::WebGPUDecoder* decoder, webgpu::ReservedBuffer reservation,
scoped_refptr<gpu::ClientSharedImage> shared_image) {
const gpu::Mailbox& mailbox = shared_image->mailbox();
{
AssociateMailboxForBufferCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId + 1, reservation.deviceGeneration,
reservation.id, reservation.generation,
WGPUBufferUsage_Storage,
reinterpret_cast<const GLuint*>(&mailbox.name));
EXPECT_EQ(
error::kInvalidArguments,
ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(mailbox.name)));
}
{
AssociateMailboxForBufferCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration + 1,
reservation.id, reservation.generation,
WGPUBufferUsage_Storage,
reinterpret_cast<const GLuint*>(&mailbox.name));
EXPECT_EQ(
error::kInvalidArguments,
ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(mailbox.name)));
}
{
AssociateMailboxForBufferCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
reservation.id + 1, reservation.generation,
WGPUBufferUsage_Storage,
reinterpret_cast<const GLuint*>(&mailbox.name));
EXPECT_EQ(
error::kInvalidArguments,
ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(mailbox.name)));
}
{
AssociateMailboxForBufferCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
reservation.id, reservation.generation,
WGPUBufferUsage_Storage,
reinterpret_cast<const GLuint*>(&mailbox.name));
EXPECT_EQ(error::kOutOfBounds,
ExecuteImmediateCmd(decoder, cmd.cmd, 0u));
}
{
AssociateMailboxForBufferCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
reservation.id, reservation.generation,
WGPUBufferUsage_Storage,
reinterpret_cast<const GLuint*>(&mailbox.name));
EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(decoder, cmd.cmd,
sizeof(mailbox.name)));
}
{
AssociateMailboxForBufferCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
reservation.id, reservation.generation,
WGPUBufferUsage_Storage,
reinterpret_cast<const GLuint*>(&mailbox.name));
EXPECT_EQ(
error::kInvalidArguments,
ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(mailbox.name)));
}
{
webgpu::cmds::DissociateMailboxForBuffer cmd;
cmd.Init(reservation.id, reservation.generation);
EXPECT_EQ(error::kNoError, ExecuteCmd(decoder, cmd));
}
},
GetDecoder(), reservation, std::move(shared_image_)));
GetGpuServiceHolder()
->gpu_main_thread_task_runner()
->RunsTasksInCurrentSequence();
}
TEST_F(WebGPUMailboxBufferTest,
AssociateMailboxForBufferCmdBadMailboxMakesErrorBuffer) {
webgpu::ReservedBuffer reservation = webgpu()->ReserveBuffer(device_.Get());
GetGpuServiceHolder()->ScheduleGpuMainTask(base::BindOnce(
[](webgpu::WebGPUDecoder* decoder, webgpu::ReservedBuffer reservation,
scoped_refptr<gpu::ClientSharedImage> shared_image) {
{
gpu::Mailbox bad_mailbox;
AssociateMailboxForBufferCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
reservation.id, reservation.generation,
WGPUBufferUsage_CopyDst,
reinterpret_cast<const GLuint*>(&bad_mailbox.name));
EXPECT_EQ(
error::kNoError,
ExecuteImmediateCmd(decoder, cmd.cmd, sizeof(bad_mailbox.name)));
}
},
GetDecoder(), reservation, std::move(shared_image_)));
wgpu::Buffer buffer = wgpu::Buffer::Acquire(reservation.buffer);
wgpu::CommandEncoder encoder = device_.CreateCommandEncoder();
encoder.ClearBuffer(buffer, 0, kBufferSize);
EXPECT_WEBGPU_ERROR(device_, wgpu::ErrorType::Validation,
wgpu::CommandBuffer commands = encoder.Finish());
}
TEST_F(WebGPUMailboxBufferTest, DissociateMailboxForBufferCmd) {
webgpu::ReservedBuffer reservation = webgpu()->ReserveBuffer(device_.Get());
GetGpuServiceHolder()->ScheduleGpuMainTask(base::BindOnce(
[](webgpu::WebGPUDecoder* decoder, webgpu::ReservedBuffer reservation,
scoped_refptr<gpu::ClientSharedImage> shared_image) {
const gpu::Mailbox& mailbox = shared_image->mailbox();
{
AssociateMailboxForBufferCmdStorage cmd;
cmd.cmd.Init(reservation.deviceId, reservation.deviceGeneration,
reservation.id, reservation.generation,
WGPUBufferUsage_Storage,
reinterpret_cast<const GLuint*>(&mailbox.name));
EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(decoder, cmd.cmd,
sizeof(mailbox.name)));
}
{
webgpu::cmds::DissociateMailboxForBuffer cmd;
cmd.Init(reservation.id + 1, reservation.generation);
EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(decoder, cmd));
}
{
webgpu::cmds::DissociateMailboxForBuffer cmd;
cmd.Init(reservation.id, reservation.generation + 1);
EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(decoder, cmd));
}
{
webgpu::cmds::DissociateMailboxForBuffer cmd;
cmd.Init(reservation.id, reservation.generation);
EXPECT_EQ(error::kNoError, ExecuteCmd(decoder, cmd));
}
{
webgpu::cmds::DissociateMailboxForBuffer cmd;
cmd.Init(reservation.id, reservation.generation);
EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(decoder, cmd));
}
},
GetDecoder(), reservation, std::move(shared_image_)));
GetGpuServiceHolder()
->gpu_main_thread_task_runner()
->RunsTasksInCurrentSequence();
}
TEST_F(WebGPUMailboxBufferTest, WriteToMailboxThenReadFromIt) {
webgpu::ReservedBuffer reservation = webgpu()->ReserveBuffer(device_.Get());
SharedImageInterface* sii = GetSharedImageInterface();
SyncToken mailbox_produced_token = sii->GenVerifiedSyncToken();
webgpu()->WaitSyncTokenCHROMIUM(mailbox_produced_token.GetConstData());
webgpu()->AssociateMailboxForBuffer(
reservation.deviceId, reservation.deviceGeneration, reservation.id,
reservation.generation,
WGPUBufferUsage_Storage | WGPUBufferUsage_CopySrc |
WGPUBufferUsage_CopyDst,
shared_image_->mailbox());
wgpu::Buffer mailbox_buffer = wgpu::Buffer::Acquire(reservation.buffer);
wgpu::BufferDescriptor data_buffer_desc;
data_buffer_desc.size = kBufferSize;
data_buffer_desc.usage =
wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
wgpu::Buffer data_buffer = device_.CreateBuffer(&data_buffer_desc);
wgpu::Queue queue = device_.GetQueue();
queue.WriteBuffer(data_buffer, 0, &kBufferData, kBufferSize);
wgpu::BufferDescriptor readback_buffer_desc;
readback_buffer_desc.size = kBufferSize;
readback_buffer_desc.usage =
wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
wgpu::Buffer readback_buffer = device_.CreateBuffer(&readback_buffer_desc);
wgpu::CommandEncoder encoder = device_.CreateCommandEncoder();
encoder.CopyBufferToBuffer(data_buffer, 0, mailbox_buffer, 0, kBufferSize);
encoder.CopyBufferToBuffer(mailbox_buffer, 0, readback_buffer, 0,
kBufferSize);
wgpu::CommandBuffer commands = encoder.Finish();
queue = device_.GetQueue();
queue.Submit(1, &commands);
webgpu()->DissociateMailboxForBuffer(reservation.id, reservation.generation);
readback_buffer.MapAsync(wgpu::MapMode::Read, 0, 4,
wgpu::CallbackMode::AllowSpontaneous,
ToMockBufferMapCallback);
EXPECT_CALL(*mock_buffer_map_callback,
Call(wgpu::MapAsyncStatus::Success, testing::_))
.Times(1);
WaitForCompletion(device_);
const void* readback_data = readback_buffer.GetConstMappedRange();
EXPECT_EQ(kBufferData, *static_cast<const uint32_t*>(readback_data));
}
TEST_F(WebGPUMailboxBufferTest, ErrorWhenUsingBufferAfterDissociate) {
webgpu::ReservedBuffer reservation = webgpu()->ReserveBuffer(device_.Get());
SharedImageInterface* sii = GetSharedImageInterface();
SyncToken mailbox_produced_token = sii->GenVerifiedSyncToken();
webgpu()->WaitSyncTokenCHROMIUM(mailbox_produced_token.GetConstData());
wgpu::Buffer mailbox_buffer = wgpu::Buffer::Acquire(reservation.buffer);
webgpu()->AssociateMailboxForBuffer(
reservation.deviceId, reservation.deviceGeneration, reservation.id,
reservation.generation,
WGPUBufferUsage_Storage | WGPUBufferUsage_CopySrc |
WGPUBufferUsage_CopyDst,
shared_image_->mailbox());
webgpu()->DissociateMailboxForBuffer(reservation.id, reservation.generation);
wgpu::CommandEncoder encoder = device_.CreateCommandEncoder();
encoder.ClearBuffer(mailbox_buffer, 0, kBufferSize);
wgpu::CommandBuffer commands = encoder.Finish();
WaitForCompletion(device_);
EXPECT_WEBGPU_ERROR(device_, wgpu::ErrorType::Validation,
device_.GetQueue().Submit(1, &commands));
WaitForCompletion(device_);
}
}