Commit 89285ad1 authored by Yan, Shaobo's avatar Yan, Shaobo Committed by Commit Bot

Support ProduceDawn for SharedImageBackingFactoryGLTexturePassthrough

Current Shared Image system lacks of supporting for producing dawn
object from a resource created by gl backing factory.

This ability is required in an important API CopyImageBitmapToTexture
from WebGPU.

This patch support ProduceDawn in
SharedImageBackingFactoryGLTexturePassthrough by:

1. Creating an internal used backing buffer and use copyTexutureCHROMIUM
   to copy from original gl backing resource to it. Passthrough command
   buffer rely on ANGLE's support of copyTextureCHROMIUM
2. ProduceDawn on the internal backing buffer.

For validating command buffer, a copyTextureCHROMIUM like API needs to
be implemented.

BUG=1036142,966582

Change-Id: I24ccaaa589d90a7a1101228155c6eafaf173b2e5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2073858
Commit-Queue: Shaobo Yan <shaobo.yan@intel.com>
Reviewed-by: default avatarKhushal <khushalsagar@chromium.org>
Reviewed-by: default avatarKenneth Russell <kbr@chromium.org>
Reviewed-by: default avatarAustin Eng <enga@chromium.org>
Cr-Commit-Position: refs/heads/master@{#758751}
parent 6ad348f8
......@@ -348,6 +348,7 @@ test("gl_tests") {
if (use_dawn) {
sources += [
"command_buffer/service/webgpu_decoder_unittest.cc",
"command_buffer/tests/shared_image_gl_backing_produce_dawn_unittest.cc",
"command_buffer/tests/webgpu_fence_unittest.cc",
"command_buffer/tests/webgpu_mailbox_unittest.cc",
"command_buffer/tests/webgpu_test.cc",
......
......@@ -6,6 +6,7 @@
#include "gpu/command_buffer/service/memory_tracking.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/shared_image_factory.h"
#include "gpu/command_buffer/service/shared_image_representation.h"
#include "gpu/command_buffer/service/texture_manager.h"
......@@ -24,6 +25,8 @@ SharedImageBacking::SharedImageBacking(const Mailbox& mailbox,
color_space_(color_space),
usage_(usage),
estimated_size_(estimated_size) {
DCHECK_CALLED_ON_VALID_THREAD(factory_thread_checker_);
if (is_thread_safe)
lock_.emplace();
}
......@@ -119,6 +122,19 @@ void SharedImageBacking::ReleaseRef(SharedImageRepresentation* representation) {
}
}
void SharedImageBacking::RegisterImageFactory(SharedImageFactory* factory) {
DCHECK_CALLED_ON_VALID_THREAD(factory_thread_checker_);
DCHECK(!factory_);
factory_ = factory;
}
void SharedImageBacking::UnregisterImageFactory() {
DCHECK_CALLED_ON_VALID_THREAD(factory_thread_checker_);
factory_ = nullptr;
}
bool SharedImageBacking::HasAnyRefs() const {
AutoLock auto_lock(this);
......
......@@ -14,6 +14,7 @@
#include "base/metrics/histogram_macros.h"
#include "base/optional.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "components/viz/common/resources/resource_format.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/gpu_gles2_export.h"
......@@ -45,10 +46,11 @@ class SharedImageRepresentationDawn;
class SharedImageRepresentationOverlay;
class SharedImageRepresentationVaapi;
class MemoryTypeTracker;
class SharedImageFactory;
class VaapiDependenciesFactory;
// Represents the actual storage (GL texture, VkImage, GMB) for a SharedImage.
// Should not be accessed direclty, instead is accessed through a
// Should not be accessed directly, instead is accessed through a
// SharedImageRepresentation.
class GPU_GLES2_EXPORT SharedImageBacking {
public:
......@@ -81,6 +83,15 @@ class GPU_GLES2_EXPORT SharedImageBacking {
// Notify backing a write access is succeeded.
void OnWriteSucceeded();
// This factory is registered when creating backing to help
// create intermediate interop backing buffer
// and share resource from gl backing buffer to dawn.
// The factory pointer needs to be reset if the origin
// factory is destructed. This will handled by destructor of
// SharedImageRepresentationFactoryRef.
void RegisterImageFactory(SharedImageFactory* factory);
void UnregisterImageFactory();
// Returns the initialized / cleared region of the SharedImage.
virtual gfx::Rect ClearedRect() const = 0;
......@@ -149,6 +160,12 @@ class GPU_GLES2_EXPORT SharedImageBacking {
// Used by subclasses during destruction.
bool have_context() const EXCLUSIVE_LOCKS_REQUIRED(lock_);
// Used by SharedImageBackingFactoryGLTexture to get register factory.
SharedImageFactory* factory() {
DCHECK_CALLED_ON_VALID_THREAD(factory_thread_checker_);
return factory_;
}
// Helper class used by subclasses to acquire |lock_| if it exists.
class SCOPED_LOCKABLE GPU_GLES2_EXPORT AutoLock {
public:
......@@ -195,6 +212,12 @@ class GPU_GLES2_EXPORT SharedImageBacking {
const uint32_t usage_;
const size_t estimated_size_;
SharedImageFactory* factory_ = nullptr;
// Bound to the thread on which the backing is created. The |factory_|
// can only be used from this thread.
THREAD_CHECKER(factory_thread_checker_);
bool have_context_ GUARDED_BY(lock_) = true;
// A scoped object for recording write UMA.
......
......@@ -18,12 +18,14 @@
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
#include "gpu/command_buffer/common/shared_image_trace_utils.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/context_state.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/image_factory.h"
#include "gpu/command_buffer/service/mailbox_manager.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.h"
#include "gpu/command_buffer/service/shared_image_factory.h"
#include "gpu/command_buffer/service/shared_image_representation.h"
#include "gpu/command_buffer/service/skia_utils.h"
#include "gpu/config/gpu_finch_features.h"
......@@ -204,6 +206,101 @@ GLuint MakeTextureAndSetParameters(gl::GLApi* api,
return service_id;
}
std::unique_ptr<SharedImageRepresentationDawn> ProduceDawnCommon(
SharedImageFactory* factory,
SharedImageManager* manager,
MemoryTypeTracker* tracker,
WGPUDevice device,
SharedImageBacking* backing,
bool use_passthrough) {
DCHECK(factory);
// Make SharedContextState from factory the current context
SharedContextState* shared_context_state = factory->GetSharedContextState();
if (!shared_context_state->MakeCurrent(nullptr, true)) {
DLOG(ERROR) << "Cannot make util SharedContextState the current context";
return nullptr;
}
Mailbox dst_mailbox = Mailbox::GenerateForSharedImage();
bool success = factory->CreateSharedImage(
dst_mailbox, backing->format(), backing->size(), backing->color_space(),
gpu::kNullSurfaceHandle, backing->usage() | SHARED_IMAGE_USAGE_WEBGPU);
if (!success) {
DLOG(ERROR) << "Cannot create a shared image resource for internal blit";
return nullptr;
}
// Create a representation for current backing to avoid non-expected release
// and using scope access methods.
std::unique_ptr<SharedImageRepresentationGLTextureBase> src_image;
std::unique_ptr<SharedImageRepresentationGLTextureBase> dst_image;
if (use_passthrough) {
src_image =
manager->ProduceGLTexturePassthrough(backing->mailbox(), tracker);
dst_image = manager->ProduceGLTexturePassthrough(dst_mailbox, tracker);
} else {
src_image = manager->ProduceGLTexture(backing->mailbox(), tracker);
dst_image = manager->ProduceGLTexture(dst_mailbox, tracker);
}
if (!src_image || !dst_image) {
DLOG(ERROR) << "ProduceDawn: Couldn't produce shared image for copy";
return nullptr;
}
std::unique_ptr<SharedImageRepresentationGLTextureBase::ScopedAccess>
source_access = src_image->BeginScopedAccess(
GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM,
SharedImageRepresentation::AllowUnclearedAccess::kNo);
if (!source_access) {
DLOG(ERROR) << "ProduceDawn: Couldn't access shared image for copy.";
return nullptr;
}
std::unique_ptr<SharedImageRepresentationGLTextureBase::ScopedAccess>
dest_access = dst_image->BeginScopedAccess(
GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM,
SharedImageRepresentation::AllowUnclearedAccess::kYes);
if (!dest_access) {
DLOG(ERROR) << "ProduceDawn: Couldn't access shared image for copy.";
return nullptr;
}
GLuint source_texture = src_image->GetTextureBase()->service_id();
GLuint dest_texture = dst_image->GetTextureBase()->service_id();
DCHECK_NE(source_texture, dest_texture);
GLenum target = dst_image->GetTextureBase()->target();
// Ensure skia's internal cache of GL context state is reset before using it.
// TODO(crbug.com/1036142: Figure out cases that need this invocation).
shared_context_state->PessimisticallyResetGrContext();
if (use_passthrough) {
gl::GLApi* gl = shared_context_state->context_state()->api();
gl->glCopyTextureCHROMIUMFn(source_texture, 0, target, dest_texture, 0,
viz::GLDataFormat(backing->format()),
viz::GLDataType(backing->format()), false,
false, false);
} else {
// TODO(crbug.com/1036142: Implement copyTextureCHROMIUM for validating
// path).
NOTREACHED();
return nullptr;
}
// Set cleared flag for internal backing to prevent auto clear.
dst_image->SetCleared();
// Safe to destroy factory's ref. The backing is kept alive by GL
// representation ref.
factory->DestroySharedImage(dst_mailbox);
return manager->ProduceDawn(dst_mailbox, tracker, device);
}
} // anonymous namespace
// Representation of a SharedImageBackingGLTexture as a GL Texture.
......@@ -578,6 +675,18 @@ class SharedImageBackingGLTexture : public SharedImageBackingWithReadAccess {
return result;
}
std::unique_ptr<SharedImageRepresentationDawn> ProduceDawn(
SharedImageManager* manager,
MemoryTypeTracker* tracker,
WGPUDevice device) override {
if (!factory()) {
DLOG(ERROR) << "No SharedImageFactory to create a dawn representation.";
return nullptr;
}
return ProduceDawnCommon(factory(), manager, tracker, device, this, false);
}
private:
gles2::Texture* texture_ = nullptr;
gles2::Texture* rgb_emulation_texture_ = nullptr;
......@@ -688,6 +797,18 @@ class SharedImageBackingPassthroughGLTexture
return result;
}
std::unique_ptr<SharedImageRepresentationDawn> ProduceDawn(
SharedImageManager* manager,
MemoryTypeTracker* tracker,
WGPUDevice device) override {
if (!factory()) {
DLOG(ERROR) << "No SharedImageFactory to create a dawn representation.";
return nullptr;
}
return ProduceDawnCommon(factory(), manager, tracker, device, this, true);
}
private:
scoped_refptr<gles2::TexturePassthrough> texture_passthrough_;
sk_sp<SkPromiseImageTexture> cached_promise_texture_;
......
......@@ -84,6 +84,7 @@ SharedImageFactory::SharedImageFactory(
bool enable_wrapped_sk_image)
: mailbox_manager_(mailbox_manager),
shared_image_manager_(shared_image_manager),
shared_context_state_(context_state),
memory_tracker_(std::make_unique<MemoryTypeTracker>(memory_tracker)),
using_vulkan_(context_state && context_state->GrContextIsVulkan()),
using_metal_(context_state && context_state->GrContextIsMetal()),
......@@ -481,6 +482,8 @@ bool SharedImageFactory::RegisterBacking(
return false;
}
shared_image->RegisterImageFactory(this);
// TODO(ericrk): Remove this once no legacy cases remain.
if (allow_legacy_mailbox &&
!shared_image->ProduceLegacyMailbox(mailbox_manager_)) {
......
......@@ -48,6 +48,7 @@ class WrappedSkImageFactory;
// SharedImageRepresentationFactory.
class GPU_GLES2_EXPORT SharedImageFactory {
public:
// All objects passed are expected to outlive this class.
SharedImageFactory(const GpuPreferences& gpu_preferences,
const GpuDriverBugWorkarounds& workarounds,
const GpuFeatureInfo& gpu_feature_info,
......@@ -109,6 +110,10 @@ class GPU_GLES2_EXPORT SharedImageFactory {
bool RegisterBacking(std::unique_ptr<SharedImageBacking> backing,
bool allow_legacy_mailbox);
SharedContextState* GetSharedContextState() const {
return shared_context_state_;
}
void RegisterSharedImageBackingFactoryForTesting(
SharedImageBackingFactory* factory);
......@@ -121,6 +126,7 @@ class GPU_GLES2_EXPORT SharedImageFactory {
gfx::GpuMemoryBufferType gmb_type = gfx::EMPTY_BUFFER);
MailboxManager* mailbox_manager_;
SharedImageManager* shared_image_manager_;
SharedContextState* shared_context_state_;
std::unique_ptr<MemoryTypeTracker> memory_tracker_;
const bool using_vulkan_;
const bool using_metal_;
......
......@@ -210,6 +210,7 @@ SharedImageRepresentationDawn::BeginScopedAccess(
}
SharedImageRepresentationFactoryRef::~SharedImageRepresentationFactoryRef() {
backing()->UnregisterImageFactory();
backing()->MarkForDestruction();
}
......
......@@ -138,6 +138,9 @@ class SharedImageRepresentationFactoryRef : public SharedImageRepresentation {
return backing()->ProduceLegacyMailbox(mailbox_manager);
}
bool PresentSwapChain() { return backing()->PresentSwapChain(); }
void RegisterImageFactory(SharedImageFactory* factory) {
backing()->RegisterImageFactory(factory);
}
};
class GPU_GLES2_EXPORT SharedImageRepresentationGLTextureBase
......
// Copyright 2020 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 "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "components/viz/test/test_gpu_service_holder.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/gles2_implementation.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/tests/webgpu_test.h"
#include "gpu/ipc/gl_in_process_context.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/color_space.h"
namespace gpu {
namespace {
class MockBufferMapReadCallback {
public:
MOCK_METHOD4(Call,
void(WGPUBufferMapAsyncStatus status,
const uint32_t* ptr,
uint64_t data_length,
void* userdata));
};
std::unique_ptr<testing::StrictMock<MockBufferMapReadCallback>>
mock_buffer_map_read_callback;
void ToMockBufferMapReadCallback(WGPUBufferMapAsyncStatus status,
const void* ptr,
uint64_t data_length,
void* userdata) {
// Assume the data is uint32_t
mock_buffer_map_read_callback->Call(status, static_cast<const uint32_t*>(ptr),
data_length, userdata);
}
} // namespace
class SharedImageGLBackingProduceDawnTest : public WebGPUTest {
protected:
void SetUp() override {
WebGPUTest::SetUp();
WebGPUTest::Options option;
Initialize(option);
gpu::ContextCreationAttribs attributes;
attributes.alpha_size = 8;
attributes.depth_size = 24;
attributes.red_size = 8;
attributes.green_size = 8;
attributes.blue_size = 8;
attributes.stencil_size = 8;
attributes.samples = 4;
attributes.sample_buffers = 1;
attributes.bind_generates_resource = false;
gl_context_ = std::make_unique<GLInProcessContext>();
ContextResult result = gl_context_->Initialize(
GetGpuServiceHolder()->task_executor(), nullptr, true,
gpu::kNullSurfaceHandle, attributes, option.shared_memory_limits,
nullptr, nullptr, base::ThreadTaskRunnerHandle::Get());
ASSERT_EQ(result, ContextResult::kSuccess);
mock_buffer_map_read_callback =
std::make_unique<testing::StrictMock<MockBufferMapReadCallback>>();
}
void TearDown() override {
WebGPUTest::TearDown();
gl_context_.reset();
mock_buffer_map_read_callback = nullptr;
}
bool ShouldSkipTest() {
// Windows is the only platform enabled passthrough in this test.
#if defined(OS_WIN)
return false;
#else
return true;
#endif // defined(OS_WIN)
}
gles2::GLES2Implementation* gl() { return gl_context_->GetImplementation(); }
std::unique_ptr<GLInProcessContext> gl_context_;
};
// Tests using Associate/DissociateMailbox to share an image with Dawn.
// For simplicity of the test the image is shared between a Dawn device and
// itself: we render to it using the Dawn device, then re-associate it to a
// Dawn texture and read back the values that were written.
TEST_F(SharedImageGLBackingProduceDawnTest, Basic) {
if (ShouldSkipTest())
return;
if (!WebGPUSupported()) {
LOG(ERROR) << "Test skipped because WebGPU isn't supported";
return;
}
if (!WebGPUSharedImageSupported()) {
LOG(ERROR) << "Test skipped because WebGPUSharedImage isn't supported";
return;
}
// Create the shared image
SharedImageInterface* sii = gl_context_->GetSharedImageInterface();
Mailbox gl_mailbox = sii->CreateSharedImage(
viz::ResourceFormat::RGBA_8888, {1, 1}, gfx::ColorSpace::CreateSRGB(),
SHARED_IMAGE_USAGE_GLES2);
SyncToken mailbox_produced_token = sii->GenVerifiedSyncToken();
gl()->WaitSyncTokenCHROMIUM(mailbox_produced_token.GetConstData());
GLuint texture =
gl()->CreateAndTexStorage2DSharedImageCHROMIUM(gl_mailbox.name);
gl()->BeginSharedImageAccessDirectCHROMIUM(
texture, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
GLuint fbo = 0;
gl()->GenFramebuffers(1, &fbo);
gl()->BindFramebuffer(GL_FRAMEBUFFER, fbo);
// Attach the texture to FBO.
gl()->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, /* Hard code */
texture, 0);
// Set the clear color to green.
gl()->ClearColor(0.0f, 1.0f, 0.0f, 1.0f);
gl()->Clear(GL_COLOR_BUFFER_BIT);
gl()->EndSharedImageAccessDirectCHROMIUM(texture);
SyncToken gl_op_token;
gl()->GenUnverifiedSyncTokenCHROMIUM(gl_op_token.GetData());
webgpu()->WaitSyncTokenCHROMIUM(gl_op_token.GetConstData());
DeviceAndClientID device_and_id = GetNewDeviceAndClientID();
wgpu::Device device = device_and_id.device;
webgpu::DawnDeviceClientID device_client_id = device_and_id.client_id;
{
// Register the shared image as a Dawn texture in the wire.
gpu::webgpu::ReservedTexture reservation =
webgpu()->ReserveTexture(device_client_id);
webgpu()->AssociateMailbox(device_client_id, 0, reservation.id,
reservation.generation, WGPUTextureUsage_CopySrc,
reinterpret_cast<GLbyte*>(&gl_mailbox));
wgpu::Texture texture = wgpu::Texture::Acquire(reservation.texture);
// Copy the texture in a mappable buffer.
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::TextureCopyView copy_src;
copy_src.texture = texture;
copy_src.mipLevel = 0;
copy_src.arrayLayer = 0;
copy_src.origin = {0, 0, 0};
wgpu::BufferCopyView copy_dst;
copy_dst.buffer = readback_buffer;
copy_dst.offset = 0;
copy_dst.rowPitch = 256;
copy_dst.imageHeight = 0;
wgpu::Extent3D copy_size = {1, 1, 1};
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyTextureToBuffer(&copy_src, &copy_dst, &copy_size);
wgpu::CommandBuffer commands = encoder.Finish();
wgpu::Queue queue = device.CreateQueue();
queue.Submit(1, &commands);
webgpu()->DissociateMailbox(device_client_id, reservation.id,
reservation.generation);
// Map the buffer and assert the pixel is the correct value.
readback_buffer.MapReadAsync(ToMockBufferMapReadCallback, this);
uint32_t buffer_contents = 0xFF00FF00;
EXPECT_CALL(*mock_buffer_map_read_callback,
Call(WGPUBufferMapAsyncStatus_Success,
testing::Pointee(testing::Eq(buffer_contents)),
sizeof(uint32_t), this))
.Times(1);
WaitForCompletion(device);
}
}
} // namespace gpu
......@@ -63,6 +63,10 @@ class WebGPUTest : public testing::Test {
};
DeviceAndClientID GetNewDeviceAndClientID();
viz::TestGpuServiceHolder* GetGpuServiceHolder() {
return gpu_service_holder_.get();
}
const uint32_t kAdapterServiceID = 0u;
private:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment