blob: 0b1b75f7a84b57f99595c09f1352f2cdaa1ac427 [file] [log] [blame]
// Copyright 2019 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/modules/webgpu/gpu_canvas_context.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "gpu/command_buffer/client/raster_interface.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_union_htmlcanvaselement_offscreencanvas.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_alpha_mode.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_canvas_configuration.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_union_canvasrenderingcontext2d_gpucanvascontext_imagebitmaprenderingcontext_webgl2renderingcontext_webglrenderingcontext.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_union_gpucanvascontext_imagebitmaprenderingcontext_offscreencanvasrenderingcontext2d_webgl2renderingcontext_webglrenderingcontext.h"
#include "third_party/blink/renderer/core/html/canvas/predefined_color_space.h"
#include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
#include "third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h"
#include "third_party/blink/renderer/modules/webgpu/dawn_conversions.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_adapter.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_queue.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_texture.h"
#include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
#include "third_party/blink/renderer/platform/graphics/canvas_color_params.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_mailbox_texture.h"
#include "third_party/blink/renderer/platform/graphics/gpu/webgpu_texture_alpha_clearer.h"
#include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
namespace blink {
GPUCanvasContext::Factory::~Factory() = default;
CanvasRenderingContext* GPUCanvasContext::Factory::Create(
CanvasRenderingContextHost* host,
const CanvasContextCreationAttributesCore& attrs) {
CanvasRenderingContext* rendering_context =
MakeGarbageCollected<GPUCanvasContext>(host, attrs);
DCHECK(host);
return rendering_context;
}
CanvasRenderingContext::CanvasRenderingAPI
GPUCanvasContext::Factory::GetRenderingAPI() const {
return CanvasRenderingContext::CanvasRenderingAPI::kWebgpu;
}
GPUCanvasContext::GPUCanvasContext(
CanvasRenderingContextHost* host,
const CanvasContextCreationAttributesCore& attrs)
: CanvasRenderingContext(host, attrs, CanvasRenderingAPI::kWebgpu) {
// Set the default values of the member corresponding to
// GPUCanvasContext.[[texture_descriptor]] in the WebGPU spec.
texture_descriptor_ = {};
texture_descriptor_.dimension = WGPUTextureDimension_2D;
texture_descriptor_.mipLevelCount = 1;
texture_descriptor_.sampleCount = 1;
}
void GPUCanvasContext::Trace(Visitor* visitor) const {
visitor->Trace(device_);
visitor->Trace(texture_);
CanvasRenderingContext::Trace(visitor);
}
// CanvasRenderingContext implementation
V8RenderingContext* GPUCanvasContext::AsV8RenderingContext() {
return MakeGarbageCollected<V8RenderingContext>(this);
}
V8OffscreenRenderingContext* GPUCanvasContext::AsV8OffscreenRenderingContext() {
return MakeGarbageCollected<V8OffscreenRenderingContext>(this);
}
SkColorInfo GPUCanvasContext::CanvasRenderingContextSkColorInfo() const {
if (!swap_buffers_)
return CanvasRenderingContext::CanvasRenderingContextSkColorInfo();
return SkColorInfo(viz::ResourceFormatToClosestSkColorType(
/*gpu_compositing=*/true, swap_buffers_->Format()),
alpha_mode_ == V8GPUCanvasAlphaMode::Enum::kOpaque
? kOpaque_SkAlphaType
: kPremul_SkAlphaType,
PredefinedColorSpaceToSkColorSpace(color_space_));
}
void GPUCanvasContext::Stop() {
DetachSwapBuffers();
stopped_ = true;
}
cc::Layer* GPUCanvasContext::CcLayer() const {
if (swap_buffers_) {
return swap_buffers_->CcLayer();
}
return nullptr;
}
void GPUCanvasContext::Reshape(int width, int height) {
if (stopped_) {
return;
}
// If an explicit size was given during the last call to configure() use that
// size instead. This is deprecated behavior.
// TODO(crbug.com/1326473): Remove after deprecation period.
if (!configured_size_.IsZero()) {
return;
}
ResizeSwapbuffers(gfx::Size(width, height));
}
scoped_refptr<StaticBitmapImage> GPUCanvasContext::GetImage() {
if (!swap_buffers_)
return nullptr;
// If there is a current texture, create a snapshot from it.
if (texture_) {
return SnapshotInternal(texture_->GetHandle(), swap_buffers_->Size());
}
// If there is no current texture, we need to get the information of the last
// texture reserved, that contains the last mailbox, create a new texture for
// it, and use it to create the resource provider. We also need the size of
// the texture to create the resource provider.
auto mailbox_texture_size =
swap_buffers_->GetLastWebGPUMailboxTextureAndSize();
if (!mailbox_texture_size.mailbox_texture)
return nullptr;
scoped_refptr<WebGPUMailboxTexture> mailbox_texture =
mailbox_texture_size.mailbox_texture;
gfx::Size size = mailbox_texture_size.size;
return SnapshotInternal(mailbox_texture->GetTexture(), size);
}
bool GPUCanvasContext::PaintRenderingResultsToCanvas(
SourceDrawingBuffer source_buffer) {
if (!swap_buffers_)
return false;
if (Host()->ResourceProvider() &&
Host()->ResourceProvider()->Size() != swap_buffers_->Size()) {
Host()->DiscardResourceProvider();
}
CanvasResourceProvider* resource_provider =
Host()->GetOrCreateCanvasResourceProvider(RasterModeHint::kPreferGPU);
return CopyRenderingResultsFromDrawingBuffer(resource_provider,
source_buffer);
}
bool GPUCanvasContext::CopyRenderingResultsFromDrawingBuffer(
CanvasResourceProvider* resource_provider,
SourceDrawingBuffer source_buffer) {
// TODO(crbug.com/1367056): Handle source_buffer == kFrontBuffer.
// By returning false here the canvas will show up as black in the scenarios
// that copy the front buffer, such as printing.
if (source_buffer != kBackBuffer) {
return false;
}
if (!texture_)
return false;
return CopyTextureToResourceProvider(
texture_->GetHandle(), swap_buffers_->Size(), resource_provider);
}
bool GPUCanvasContext::CopyRenderingResultsToVideoFrame(
WebGraphicsContext3DVideoFramePool* frame_pool,
SourceDrawingBuffer src_buffer,
const gfx::ColorSpace& dst_color_space,
VideoFrameCopyCompletedCallback callback) {
return swap_buffers_->CopyToVideoFrame(frame_pool, src_buffer,
dst_color_space, std::move(callback));
}
void GPUCanvasContext::SetFilterQuality(
cc::PaintFlags::FilterQuality filter_quality) {
if (filter_quality != filter_quality_) {
filter_quality_ = filter_quality;
if (swap_buffers_) {
swap_buffers_->SetFilterQuality(filter_quality);
}
}
}
bool GPUCanvasContext::PushFrame() {
DCHECK(Host());
DCHECK(Host()->IsOffscreenCanvas());
if (!swap_buffers_)
return false;
viz::TransferableResource transferable_resource;
viz::ReleaseCallback release_callback;
if (!swap_buffers_->PrepareTransferableResource(
nullptr, &transferable_resource, &release_callback)) {
return false;
}
// Acquires a CanvasResource of type ExternalCanvasResource that will
// encapsulate an external mailbox, synctoken and release callback.
SkImageInfo resource_info = SkImageInfo::Make(
transferable_resource.size.width(), transferable_resource.size.height(),
viz::ToClosestSkColorType(
/*gpu_compositing=*/true, transferable_resource.format),
kPremul_SkAlphaType);
auto canvas_resource = ExternalCanvasResource::Create(
transferable_resource.mailbox_holder.mailbox, std::move(release_callback),
transferable_resource.mailbox_holder.sync_token, resource_info,
transferable_resource.mailbox_holder.texture_target,
GetContextProviderWeakPtr(), /*resource_provider=*/nullptr,
cc::PaintFlags::FilterQuality::kLow,
/*is_origin_top_left=*/kBottomLeft_GrSurfaceOrigin,
transferable_resource.is_overlay_candidate);
if (!canvas_resource)
return false;
const int width = canvas_resource->Size().width();
const int height = canvas_resource->Size().height();
return Host()->PushFrame(std::move(canvas_resource),
SkIRect::MakeWH(width, height));
}
ImageBitmap* GPUCanvasContext::TransferToImageBitmap(
ScriptState* script_state) {
viz::TransferableResource transferable_resource;
viz::ReleaseCallback release_callback;
if (!swap_buffers_->PrepareTransferableResource(
nullptr, &transferable_resource, &release_callback)) {
// If we can't get a mailbox, return an transparent black ImageBitmap.
// The only situation in which this could happen is when two or more calls
// to transferToImageBitmap are made back-to-back, or when the context gets
// lost. We intentionally leave the transparent black image in legacy color
// space.
SkBitmap black_bitmap;
black_bitmap.allocN32Pixels(transferable_resource.size.width(),
transferable_resource.size.height());
black_bitmap.eraseARGB(0, 0, 0, 0);
return MakeGarbageCollected<ImageBitmap>(
UnacceleratedStaticBitmapImage::Create(
SkImage::MakeFromBitmap(black_bitmap)));
}
DCHECK(release_callback);
// We reuse the same mailbox name from above since our texture id was consumed
// from it.
const auto& sk_image_mailbox = transferable_resource.mailbox_holder.mailbox;
// Use the sync token generated after producing the mailbox. Waiting for this
// before trying to use the mailbox with some other context will ensure it is
// valid.
const auto& sk_image_sync_token =
transferable_resource.mailbox_holder.sync_token;
auto sk_color_type = viz::ToClosestSkColorType(
/*gpu_compositing=*/true, transferable_resource.format);
const SkImageInfo sk_image_info = SkImageInfo::Make(
texture_descriptor_.size.width, texture_descriptor_.size.height,
sk_color_type, kPremul_SkAlphaType);
return MakeGarbageCollected<ImageBitmap>(
AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
sk_image_mailbox, sk_image_sync_token,
/* shared_image_texture_id = */ 0, sk_image_info,
transferable_resource.mailbox_holder.texture_target,
/* is_origin_top_left = */ kBottomLeft_GrSurfaceOrigin,
GetContextProviderWeakPtr(), base::PlatformThread::CurrentRef(),
ThreadScheduler::Current()->CleanupTaskRunner(),
std::move(release_callback),
/*supports_display_compositing=*/true,
transferable_resource.is_overlay_candidate));
}
// gpu_presentation_context.idl
V8UnionHTMLCanvasElementOrOffscreenCanvas*
GPUCanvasContext::getHTMLOrOffscreenCanvas() const {
if (Host()->IsOffscreenCanvas()) {
return MakeGarbageCollected<V8UnionHTMLCanvasElementOrOffscreenCanvas>(
static_cast<OffscreenCanvas*>(Host()));
}
return MakeGarbageCollected<V8UnionHTMLCanvasElementOrOffscreenCanvas>(
static_cast<HTMLCanvasElement*>(Host()));
}
void GPUCanvasContext::configure(const GPUCanvasConfiguration* descriptor,
ExceptionState& exception_state) {
DCHECK(descriptor);
if (stopped_ || !Host()) {
// This is probably not possible, or at least would only happen during page
// shutdown.
exception_state.ThrowDOMException(DOMExceptionCode::kUnknownError,
"canvas has been destroyed");
return;
}
if (!descriptor->device()->ValidateTextureFormatUsage(descriptor->format(),
exception_state)) {
return;
}
for (auto view_format : descriptor->viewFormats()) {
if (!descriptor->device()->ValidateTextureFormatUsage(view_format,
exception_state)) {
return;
}
}
// As soon as the validation for extensions for usage and formats passes, the
// canvas is "configured" and calls to getNextTexture() will return GPUTexture
// objects (valid or invalid) and not throw.
configured_ = true;
texture_descriptor_.format = AsDawnEnum(descriptor->format());
texture_descriptor_.usage =
AsDawnFlags<WGPUTextureUsage>(descriptor->usage());
view_formats_ = AsDawnEnum<WGPUTextureFormat>(descriptor->viewFormats());
texture_descriptor_.viewFormats = view_formats_.get();
texture_descriptor_.viewFormatCount = descriptor->viewFormats().size();
// This needs to happen early so that if any validation fails the swapbuffers
// are not created and getCurrentTexture() will return an error GPUTexture.
DetachSwapBuffers();
// Store the configured device separately, even if the configuration fails, so
// that errors can be generated in the appropriate error scope.
device_ = descriptor->device();
switch (texture_descriptor_.format) {
// TODO(crbug.com/1361468): support BGRA8Unorm on Android.
#if !BUILDFLAG(IS_ANDROID)
case WGPUTextureFormat_BGRA8Unorm:
#endif
// TODO(crbug.com/1298618): support RGBA8Unorm on MAC.
#if !BUILDFLAG(IS_MAC)
case WGPUTextureFormat_RGBA8Unorm:
#endif
// TODO(crbug.com/1317015): support RGBA16Float on ChromeOS.
#if !BUILDFLAG(IS_CHROMEOS)
case WGPUTextureFormat_RGBA16Float:
#endif
break;
default:
device_->InjectError(WGPUErrorType_Validation,
"unsupported swap chain format");
return;
}
alpha_mode_ = V8GPUCanvasAlphaMode::Enum::kPremultiplied;
if (descriptor->hasCompositingAlphaMode()) {
alpha_mode_ = descriptor->compositingAlphaMode().AsEnum();
device_->AddConsoleWarning(
"compositingAlphaMode is deprecated and will soon be removed. Please "
"set alphaMode instead.");
} else if (descriptor->hasAlphaMode()) {
alpha_mode_ = descriptor->alphaMode().AsEnum();
} else {
device_->AddConsoleWarning(
"The default GPUCanvasAlphaMode will change from "
"\"premultiplied\" to \"opaque\". "
"Please explicitly set alphaMode to \"premultiplied\" if you would "
"like to continue using that compositing mode.");
}
if (!ValidateAndConvertColorSpace(descriptor->colorSpace(), color_space_,
exception_state)) {
return;
}
swap_buffers_ = base::AdoptRef(new WebGPUSwapBufferProvider(
this, device_->GetDawnControlClient(), device_->GetHandle(),
static_cast<WGPUTextureUsage>(texture_descriptor_.usage),
texture_descriptor_.format, color_space_));
swap_buffers_->SetFilterQuality(filter_quality_);
// Note: SetContentsOpaque is only an optimization hint. It doesn't
// actually make the contents opaque.
switch (alpha_mode_) {
case V8GPUCanvasAlphaMode::Enum::kOpaque: {
CcLayer()->SetContentsOpaque(true);
if (!alpha_clearer_ ||
!alpha_clearer_->IsCompatible(device_->GetHandle(),
texture_descriptor_.format)) {
alpha_clearer_ = base::MakeRefCounted<WebGPUTextureAlphaClearer>(
device_->GetDawnControlClient(), device_->GetHandle(),
texture_descriptor_.format);
}
break;
}
case V8GPUCanvasAlphaMode::Enum::kPremultiplied:
alpha_clearer_ = nullptr;
CcLayer()->SetContentsOpaque(false);
break;
}
// Set the size while configuring.
if (descriptor->hasSize()) {
// TODO(crbug.com/1326473): Remove this branch after deprecation period.
device_->AddConsoleWarning(
"Setting an explicit size when calling configure() on a "
"GPUCanvasContext has been deprecated, and will soon be removed. "
"Please set the canvas width and height attributes instead. Note that "
"after the initial call to configure() changes to the canvas width and "
"height will now take effect without the need to call configure() "
"again.");
WGPUExtent3D dawn_extent = AsDawnType(descriptor->size());
configured_size_ = gfx::Size(dawn_extent.width, dawn_extent.height);
if (dawn_extent.depthOrArrayLayers != 1) {
device_->InjectError(
WGPUErrorType_Validation,
"swap chain size must have depthOrArrayLayers set to 1");
return;
}
if (configured_size_.IsEmpty()) {
device_->InjectError(WGPUErrorType_Validation,
"context width and height must be greater than 0");
return;
}
ResizeSwapbuffers(configured_size_);
} else {
configured_size_.SetSize(0, 0);
gfx::Size size = Host()->Size();
ResizeSwapbuffers(size);
}
}
void GPUCanvasContext::ResizeSwapbuffers(gfx::Size size) {
texture_descriptor_.size = {static_cast<uint32_t>(size.width()),
static_cast<uint32_t>(size.height()), 1};
// The spec indicates that when the canvas is resized the current texture is
// discarded and a new one allocated in it's place immediately.
if (swap_buffers_) {
ReplaceCurrentTexture();
}
// If we don't notify the host that something has changed it may never check
// for the new cc::Layer.
Host()->SetNeedsCompositingUpdate();
}
void GPUCanvasContext::unconfigure() {
if (stopped_) {
return;
}
DetachSwapBuffers();
// When developers call unconfigure from the page, one of the reasons for
// doing so is to expressly release the GPUCanvasContext's device reference.
// In order to fully release it, any TextureAlphaClearer that has been created
// also needs to be released.
alpha_clearer_ = nullptr;
device_ = nullptr;
configured_ = false;
}
void GPUCanvasContext::DetachSwapBuffers() {
if (swap_buffers_) {
// Tell any previous swapbuffers that it will no longer be used and can
// destroy all its resources (and produce errors when used).
swap_buffers_->Neuter();
swap_buffers_ = nullptr;
}
texture_ = nullptr;
}
String GPUCanvasContext::getPreferredFormat(ExecutionContext* execution_context,
GPUAdapter* adapter) {
adapter->AddConsoleWarning(
execution_context,
"Calling getPreferredFormat() on a GPUCanvasContext is deprecated and "
"will soon be removed. Call navigator.gpu.getPreferredCanvasFormat() "
"instead, which no longer requires an adapter.");
#if BUILDFLAG(IS_ANDROID)
return "rgba8unorm";
#else
return "bgra8unorm";
#endif
}
GPUTexture* GPUCanvasContext::getCurrentTexture(
ExceptionState& exception_state) {
if (!configured_) {
exception_state.ThrowDOMException(DOMExceptionCode::kOperationError,
"context is not configured");
return nullptr;
}
DCHECK(device_);
if (!swap_buffers_) {
device_->InjectError(WGPUErrorType_Validation,
"context configuration is invalid.");
return GPUTexture::CreateError(device_, &texture_descriptor_);
}
// Calling getCurrentTexture returns a texture that is valid until the
// animation frame it gets presented. If getCurrentTexture is called multiple
// time, the same texture should be returned. |texture_| is set to null when
// presented so that we know we should create a new one.
if (texture_ && !new_texture_required_) {
return texture_;
}
return ReplaceCurrentTexture();
}
GPUTexture* GPUCanvasContext::ReplaceCurrentTexture() {
DCHECK(device_);
DCHECK(swap_buffers_);
// Simply requesting a new canvas texture with WebGPU is enough to mark it as
// "dirty", so always call DidDraw() when a new texture is created.
DidDraw(CanvasPerformanceMonitor::DrawType::kOther);
if (texture_) {
swap_buffers_->DiscardCurrentSwapBuffer();
}
texture_ = nullptr;
SkAlphaType alpha_type = alpha_mode_ == V8GPUCanvasAlphaMode::Enum::kOpaque
? kOpaque_SkAlphaType
: kPremul_SkAlphaType;
scoped_refptr<WebGPUMailboxTexture> mailbox_texture =
swap_buffers_->GetNewTexture(texture_descriptor_, alpha_type);
if (!mailbox_texture) {
texture_ = GPUTexture::CreateError(device_, &texture_descriptor_);
return texture_;
}
mailbox_texture->SetNeedsPresent(true);
mailbox_texture->SetAlphaClearer(alpha_clearer_);
texture_ = MakeGarbageCollected<GPUTexture>(
device_, texture_descriptor_.format,
static_cast<WGPUTextureUsage>(texture_descriptor_.usage),
std::move(mailbox_texture));
new_texture_required_ = false;
return texture_;
}
void GPUCanvasContext::FinalizeFrame(bool /*printing*/) {
// In some cases, such as when a canvas is hidden of offscreen, compositing
// will never happen and thus OnTextureTransferred will never be called. In
// those cases, getCurrentTexture is still required to return a new texture
// after the current frame has ended, so we'll mark that a new texture is
// required here.
new_texture_required_ = true;
}
// WebGPUSwapBufferProvider::Client implementation
void GPUCanvasContext::OnTextureTransferred() {
DCHECK(texture_);
texture_ = nullptr;
}
bool GPUCanvasContext::CopyTextureToResourceProvider(
const WGPUTexture& texture,
const gfx::Size& size,
CanvasResourceProvider* resource_provider) const {
DCHECK(resource_provider);
DCHECK_EQ(resource_provider->Size(), size);
DCHECK(resource_provider->GetSharedImageUsageFlags() &
gpu::SHARED_IMAGE_USAGE_WEBGPU);
DCHECK(resource_provider->IsOriginTopLeft());
base::WeakPtr<WebGraphicsContext3DProviderWrapper> shared_context_wrapper =
SharedGpuContext::ContextProviderWrapper();
if (!shared_context_wrapper || !shared_context_wrapper->ContextProvider())
return false;
const auto dst_mailbox =
resource_provider->GetBackingMailboxForOverwrite(kUnverifiedSyncToken);
if (dst_mailbox.IsZero())
return false;
auto* ri = shared_context_wrapper->ContextProvider()->RasterInterface();
if (!GetContextProviderWeakPtr()) {
return false;
}
// todo(crbug/1267244) Use WebGPUMailboxTexture here instead of doing things
// manually.
gpu::webgpu::WebGPUInterface* webgpu =
GetContextProviderWeakPtr()->ContextProvider()->WebGPUInterface();
gpu::webgpu::ReservedTexture reservation =
webgpu->ReserveTexture(device_->GetHandle());
DCHECK(reservation.texture);
gpu::SyncToken sync_token;
ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
webgpu->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
webgpu->AssociateMailbox(
reservation.deviceId, reservation.deviceGeneration, reservation.id,
reservation.generation,
WGPUTextureUsage_CopyDst | WGPUTextureUsage_RenderAttachment,
dst_mailbox);
WGPUImageCopyTexture source = {
.nextInChain = nullptr,
.texture = texture,
.mipLevel = 0,
.origin = WGPUOrigin3D{0},
.aspect = WGPUTextureAspect_All,
};
WGPUImageCopyTexture destination = {
.nextInChain = nullptr,
.texture = reservation.texture,
.mipLevel = 0,
.origin = WGPUOrigin3D{0},
.aspect = WGPUTextureAspect_All,
};
WGPUExtent3D copy_size = {
.width = static_cast<uint32_t>(size.width()),
.height = static_cast<uint32_t>(size.height()),
.depthOrArrayLayers = 1,
};
if (alpha_mode_ == V8GPUCanvasAlphaMode::Enum::kOpaque) {
// Issue a copyTextureForBrowser call with internal usage turned on.
// There is a special step for srcAlphaMode == WGPUAlphaMode_Opaque that
// clears alpha channel to one.
SkImageInfo sk_dst_image_info = resource_provider->GetSkImageInfo();
WGPUAlphaMode dstAlphaMode;
switch (sk_dst_image_info.alphaType()) {
case SkAlphaType::kPremul_SkAlphaType:
dstAlphaMode = WGPUAlphaMode_Premultiplied;
break;
case SkAlphaType::kUnpremul_SkAlphaType:
dstAlphaMode = WGPUAlphaMode_Unpremultiplied;
break;
case SkAlphaType::kOpaque_SkAlphaType:
dstAlphaMode = WGPUAlphaMode_Opaque;
break;
default:
// Unknown dst alpha type, default to equal to src alpha mode
dstAlphaMode = WGPUAlphaMode_Opaque;
break;
}
WGPUCopyTextureForBrowserOptions options = {
.flipY = !resource_provider->IsOriginTopLeft(),
.srcAlphaMode = WGPUAlphaMode_Opaque,
.dstAlphaMode = dstAlphaMode,
.internalUsage = true,
};
GetProcs().queueCopyTextureForBrowser(device_->queue()->GetHandle(),
&source, &destination, &copy_size,
&options);
} else {
// Create a command encoder and call copyTextureToTexture for the image
// copy.
WGPUDawnEncoderInternalUsageDescriptor internal_usage_desc = {
.chain = {.sType = WGPUSType_DawnEncoderInternalUsageDescriptor},
.useInternalUsages = true,
};
WGPUCommandEncoderDescriptor command_encoder_desc = {
.nextInChain = &internal_usage_desc.chain,
};
WGPUCommandEncoder command_encoder = GetProcs().deviceCreateCommandEncoder(
device_->GetHandle(), &command_encoder_desc);
GetProcs().commandEncoderCopyTextureToTexture(command_encoder, &source,
&destination, &copy_size);
WGPUCommandBuffer command_buffer =
GetProcs().commandEncoderFinish(command_encoder, nullptr);
GetProcs().commandEncoderRelease(command_encoder);
GetProcs().queueSubmit(device_->queue()->GetHandle(), 1u, &command_buffer);
GetProcs().commandBufferRelease(command_buffer);
}
webgpu->DissociateMailbox(reservation.id, reservation.generation);
GetProcs().textureRelease(reservation.texture);
webgpu->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
ri->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
return true;
}
scoped_refptr<StaticBitmapImage> GPUCanvasContext::SnapshotInternal(
const WGPUTexture& texture,
const gfx::Size& size) const {
const auto canvas_context_color = CanvasRenderingContextSkColorInfo();
const auto info = SkImageInfo::Make(size.width(), size.height(),
canvas_context_color.colorType(),
canvas_context_color.alphaType());
// We tag the SharedImage inside the WebGPUImageProvider with display usage
// since there are uncommon paths which may use this snapshot for compositing.
// These paths are usually related to either printing or either video and
// usually related to OffscreenCanvas; in cases where the image created from
// this Snapshot will be sent eventually to the Display Compositor.
auto resource_provider = CanvasResourceProvider::CreateWebGPUImageProvider(
info,
/*is_origin_top_left=*/true, gpu::SHARED_IMAGE_USAGE_DISPLAY_READ);
if (!resource_provider)
return nullptr;
if (!CopyTextureToResourceProvider(texture, size, resource_provider.get()))
return nullptr;
return resource_provider->Snapshot();
}
// DawnObjectBase substitute methods
const DawnProcTable& GPUCanvasContext::GetProcs() const {
return device_->GetProcs();
}
base::WeakPtr<WebGraphicsContext3DProviderWrapper>
GPUCanvasContext::GetContextProviderWeakPtr() const {
return device_->GetDawnControlClient()->GetContextProviderWeakPtr();
}
} // namespace blink