| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.h" |
| |
| #include "gpu/command_buffer/client/webgpu_interface.h" |
| #include "media/base/video_frame.h" |
| #include "media/base/wait_and_replace_sync_token_client.h" |
| #include "third_party/blink/renderer/platform/graphics/canvas_resource.h" |
| #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h" |
| #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h" |
| #include "third_party/blink/renderer/platform/graphics/gpu/webgpu_texture_alpha_clearer.h" |
| #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h" |
| #include "third_party/blink/renderer/platform/wtf/functional.h" |
| |
| namespace blink { |
| |
| // static |
| scoped_refptr<WebGPUMailboxTexture> WebGPUMailboxTexture::FromStaticBitmapImage( |
| scoped_refptr<DawnControlClientHolder> dawn_control_client, |
| WGPUDevice device, |
| WGPUTextureUsage usage, |
| scoped_refptr<StaticBitmapImage> image, |
| SkColorType color_type) { |
| DCHECK(image->IsTextureBacked()); |
| |
| // TODO(crbugs.com/1217160) Mac uses IOSurface in SharedImageBackingGLImage |
| // which can be shared to dawn directly aftter passthrough command buffer |
| // supported on mac os. |
| // We should wrap the StaticBitmapImage directly for mac when passthrough |
| // command buffer has been supported. |
| |
| // If the context is lost, the resource provider would be invalid. |
| auto context_provider_wrapper = SharedGpuContext::ContextProviderWrapper(); |
| if (!context_provider_wrapper || |
| context_provider_wrapper->ContextProvider()->IsContextLost()) |
| return nullptr; |
| |
| // Get a recyclable resource for producing WebGPU-compatible shared images. |
| std::unique_ptr<RecyclableCanvasResource> recyclable_canvas_resource = |
| dawn_control_client->GetOrCreateCanvasResource( |
| image->PaintImageForCurrentFrame().GetSkImageInfo(), |
| image->IsOriginTopLeft()); |
| |
| // Fallback to unstable intermediate resource copy path. |
| if (!recyclable_canvas_resource) { |
| auto finished_access_callback = WTF::BindOnce( |
| &StaticBitmapImage::UpdateSyncToken, WTF::RetainedRef(image)); |
| |
| WGPUTextureDescriptor desc = {}; |
| desc.usage = usage; |
| return base::AdoptRef(new WebGPUMailboxTexture( |
| std::move(dawn_control_client), device, desc, |
| image->GetMailboxHolder().mailbox, image->GetMailboxHolder().sync_token, |
| gpu::webgpu::WEBGPU_MAILBOX_NONE, std::move(finished_access_callback), |
| /*recyclable_canvas_resource=*/nullptr)); |
| } |
| |
| CanvasResourceProvider* resource_provider = |
| recyclable_canvas_resource->resource_provider(); |
| DCHECK(resource_provider); |
| |
| if (!image->CopyToResourceProvider(resource_provider)) { |
| return nullptr; |
| } |
| |
| return WebGPUMailboxTexture::FromCanvasResource( |
| dawn_control_client, device, usage, |
| std::move(recyclable_canvas_resource)); |
| } |
| |
| // static |
| scoped_refptr<WebGPUMailboxTexture> WebGPUMailboxTexture::FromCanvasResource( |
| scoped_refptr<DawnControlClientHolder> dawn_control_client, |
| WGPUDevice device, |
| WGPUTextureUsage usage, |
| std::unique_ptr<RecyclableCanvasResource> recyclable_canvas_resource) { |
| scoped_refptr<CanvasResource> canvas_resource = |
| recyclable_canvas_resource->resource_provider()->ProduceCanvasResource(); |
| DCHECK(canvas_resource->IsValid()); |
| DCHECK(canvas_resource->IsAccelerated()); |
| |
| const gpu::Mailbox& mailbox = |
| canvas_resource->GetOrCreateGpuMailbox(kUnverifiedSyncToken); |
| gpu::SyncToken sync_token = canvas_resource->GetSyncToken(); |
| |
| WGPUTextureDescriptor desc = {}; |
| desc.usage = usage; |
| return base::AdoptRef(new WebGPUMailboxTexture( |
| std::move(dawn_control_client), device, desc, mailbox, sync_token, |
| gpu::webgpu::WEBGPU_MAILBOX_NONE, |
| base::OnceCallback<void(const gpu::SyncToken&)>(), |
| std::move(recyclable_canvas_resource))); |
| } |
| |
| // static |
| scoped_refptr<WebGPUMailboxTexture> WebGPUMailboxTexture::FromExistingMailbox( |
| scoped_refptr<DawnControlClientHolder> dawn_control_client, |
| WGPUDevice device, |
| const WGPUTextureDescriptor& desc, |
| const gpu::Mailbox& mailbox, |
| const gpu::SyncToken& sync_token, |
| gpu::webgpu::MailboxFlags mailbox_flags, |
| base::OnceCallback<void(const gpu::SyncToken&)> finished_access_callback) { |
| DCHECK(dawn_control_client->GetContextProviderWeakPtr()); |
| |
| return base::AdoptRef(new WebGPUMailboxTexture( |
| std::move(dawn_control_client), device, desc, mailbox, sync_token, |
| mailbox_flags, std::move(finished_access_callback), nullptr)); |
| } |
| |
| // static |
| scoped_refptr<WebGPUMailboxTexture> WebGPUMailboxTexture::FromVideoFrame( |
| scoped_refptr<DawnControlClientHolder> dawn_control_client, |
| WGPUDevice device, |
| WGPUTextureUsage usage, |
| scoped_refptr<media::VideoFrame> video_frame) { |
| auto finished_access_callback = base::BindOnce( |
| [](base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider, |
| media::VideoFrame* frame, const gpu::SyncToken& sync_token) { |
| if (context_provider) { |
| // Update the sync token before unreferencing the video frame. |
| media::WaitAndReplaceSyncTokenClient client( |
| context_provider->ContextProvider()->WebGPUInterface()); |
| frame->UpdateReleaseSyncToken(&client); |
| } |
| }, |
| dawn_control_client->GetContextProviderWeakPtr(), |
| base::RetainedRef(video_frame)); |
| |
| WGPUTextureDescriptor desc = {}; |
| desc.usage = WGPUTextureUsage_TextureBinding; |
| return base::AdoptRef( |
| new WebGPUMailboxTexture(std::move(dawn_control_client), device, desc, |
| video_frame->mailbox_holder(0).mailbox, |
| video_frame->mailbox_holder(0).sync_token, |
| gpu::webgpu::WEBGPU_MAILBOX_NONE, |
| std::move(finished_access_callback), nullptr)); |
| } |
| |
| WebGPUMailboxTexture::WebGPUMailboxTexture( |
| scoped_refptr<DawnControlClientHolder> dawn_control_client, |
| WGPUDevice device, |
| const WGPUTextureDescriptor& desc, |
| const gpu::Mailbox& mailbox, |
| const gpu::SyncToken& sync_token, |
| gpu::webgpu::MailboxFlags mailbox_flags, |
| base::OnceCallback<void(const gpu::SyncToken&)> finished_access_callback, |
| std::unique_ptr<RecyclableCanvasResource> recyclable_canvas_resource) |
| : dawn_control_client_(std::move(dawn_control_client)), |
| device_(device), |
| finished_access_callback_(std::move(finished_access_callback)), |
| recyclable_canvas_resource_(std::move(recyclable_canvas_resource)) { |
| DCHECK(dawn_control_client_->GetContextProviderWeakPtr()); |
| |
| dawn_control_client_->GetProcs().deviceReference(device_); |
| |
| gpu::webgpu::WebGPUInterface* webgpu = |
| dawn_control_client_->GetContextProviderWeakPtr() |
| ->ContextProvider() |
| ->WebGPUInterface(); |
| |
| // Wait on any work using the image. |
| webgpu->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); |
| |
| // Produce and inject image to WebGPU texture |
| gpu::webgpu::ReservedTexture reservation = |
| webgpu->ReserveTexture(device_, &desc); |
| DCHECK(reservation.texture); |
| |
| wire_device_id_ = reservation.deviceId; |
| wire_device_generation_ = reservation.deviceGeneration; |
| wire_texture_id_ = reservation.id; |
| wire_texture_generation_ = reservation.generation; |
| texture_ = reservation.texture; |
| |
| // This may fail because gl_backing resource cannot produce dawn |
| // representation. |
| webgpu->AssociateMailbox(wire_device_id_, wire_device_generation_, |
| wire_texture_id_, wire_texture_generation_, |
| desc.usage, mailbox_flags, |
| reinterpret_cast<const GLbyte*>(&mailbox)); |
| } |
| |
| void WebGPUMailboxTexture::SetAlphaClearer( |
| scoped_refptr<WebGPUTextureAlphaClearer> alpha_clearer) { |
| alpha_clearer_ = std::move(alpha_clearer); |
| } |
| |
| void WebGPUMailboxTexture::Dissociate() { |
| if (wire_texture_id_ == 0) { |
| return; |
| } |
| if (auto context_provider = |
| dawn_control_client_->GetContextProviderWeakPtr()) { |
| gpu::webgpu::WebGPUInterface* webgpu = |
| context_provider->ContextProvider()->WebGPUInterface(); |
| if (alpha_clearer_) { |
| alpha_clearer_->ClearAlpha(texture_); |
| alpha_clearer_ = nullptr; |
| } |
| if (needs_present_) { |
| webgpu->DissociateMailboxForPresent( |
| wire_device_id_, wire_device_generation_, wire_texture_id_, |
| wire_texture_generation_); |
| } else { |
| webgpu->DissociateMailbox(wire_texture_id_, wire_texture_generation_); |
| } |
| wire_texture_id_ = 0; |
| |
| if (finished_access_callback_) { |
| gpu::SyncToken finished_access_token; |
| webgpu->GenUnverifiedSyncTokenCHROMIUM(finished_access_token.GetData()); |
| std::move(finished_access_callback_).Run(finished_access_token); |
| } |
| } |
| } |
| |
| WebGPUMailboxTexture::~WebGPUMailboxTexture() { |
| Dissociate(); |
| dawn_control_client_->GetProcs().textureRelease(texture_); |
| dawn_control_client_->GetProcs().deviceRelease(device_); |
| } |
| |
| } // namespace blink |