blob: b350e038b2ca5f4f4f42ef0e3c155873b497958f [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_texture.h"
#include "base/containers/heap_array.h"
#include "gpu/command_buffer/client/webgpu_interface.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_texture_descriptor.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_texture_view_descriptor.h"
#include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h"
#include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
#include "third_party/blink/renderer/modules/webgpu/dawn_conversions.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_texture_usage.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_texture_view.h"
#include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.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_resource_provider_cache.h"
namespace blink {
namespace {
bool ConvertToDawn(const GPUTextureDescriptor* in,
wgpu::TextureDescriptor* out,
wgpu::TextureBindingViewDimensionDescriptor*
out_texture_binding_view_dimension,
std::string* label,
base::HeapArray<wgpu::TextureFormat>* view_formats,
GPUDevice* device,
ExceptionState& exception_state) {
DCHECK(in);
DCHECK(out);
DCHECK(out_texture_binding_view_dimension);
DCHECK(label);
DCHECK(view_formats);
DCHECK(device);
*out = {};
out->usage = static_cast<wgpu::TextureUsage>(in->usage());
out->dimension = AsDawnEnum(in->dimension());
out->format = AsDawnEnum(in->format());
out->mipLevelCount = in->mipLevelCount();
out->sampleCount = in->sampleCount();
if (in->hasTextureBindingViewDimension()) {
wgpu::TextureViewDimension texture_binding_view_dimension =
AsDawnEnum(in->textureBindingViewDimension());
if (texture_binding_view_dimension !=
wgpu::TextureViewDimension::Undefined) {
*out_texture_binding_view_dimension = {};
out_texture_binding_view_dimension->textureBindingViewDimension =
texture_binding_view_dimension;
out->nextInChain = out_texture_binding_view_dimension;
}
}
*label = in->label().Utf8();
if (!label->empty()) {
out->label = label->c_str();
}
*view_formats = AsDawnEnum<wgpu::TextureFormat>(in->viewFormats());
out->viewFormatCount = in->viewFormats().size();
out->viewFormats = view_formats->data();
return ConvertToDawn(in->size(), &out->size, device, exception_state);
}
void ConvertToDawnType(const GPUTextureViewDescriptor* webgpu_desc,
OwnedTextureViewDescriptor* dawn_desc_info) {
DCHECK(webgpu_desc);
DCHECK(dawn_desc_info);
if (webgpu_desc->hasFormat()) {
dawn_desc_info->dawn_desc.format = AsDawnEnum(webgpu_desc->format());
}
if (webgpu_desc->hasDimension()) {
dawn_desc_info->dawn_desc.dimension = AsDawnEnum(webgpu_desc->dimension());
}
dawn_desc_info->dawn_desc.baseMipLevel = webgpu_desc->baseMipLevel();
if (webgpu_desc->hasMipLevelCount()) {
dawn_desc_info->dawn_desc.mipLevelCount = webgpu_desc->mipLevelCount();
}
dawn_desc_info->dawn_desc.baseArrayLayer = webgpu_desc->baseArrayLayer();
if (webgpu_desc->hasArrayLayerCount()) {
dawn_desc_info->dawn_desc.arrayLayerCount = webgpu_desc->arrayLayerCount();
}
dawn_desc_info->dawn_desc.aspect = AsDawnEnum(webgpu_desc->aspect());
if (!webgpu_desc->label().empty()) {
dawn_desc_info->label = webgpu_desc->label().Utf8();
dawn_desc_info->dawn_desc.label = dawn_desc_info->label.c_str();
}
if (webgpu_desc->hasUsage()) {
dawn_desc_info->dawn_desc.usage =
static_cast<wgpu::TextureUsage>(webgpu_desc->usage());
}
const auto& swizzle = webgpu_desc->swizzle();
// Only pass the swizzle descriptor to Dawn if swizzle is non-default because
// the C API will produce validation errors if a chained struct is passed
// without its feature being enabled.
if (swizzle != "rgba") {
dawn_desc_info->swizzle_desc =
std::make_unique<wgpu::TextureComponentSwizzleDescriptor>();
dawn_desc_info->swizzle_desc->swizzle.r = AsDawnEnum(swizzle[0]);
dawn_desc_info->swizzle_desc->swizzle.g = AsDawnEnum(swizzle[1]);
dawn_desc_info->swizzle_desc->swizzle.b = AsDawnEnum(swizzle[2]);
dawn_desc_info->swizzle_desc->swizzle.a = AsDawnEnum(swizzle[3]);
dawn_desc_info->dawn_desc.nextInChain = dawn_desc_info->swizzle_desc.get();
}
}
// Validate swizzle must be a four-character string that only includes "r", "g",
// "b", "a", "0", or "1".
bool ValidateSwizzle(const String& swizzle, ExceptionState& exception_state) {
if (!RuntimeEnabledFeatures::WebGPUExperimentalFeaturesEnabled()) {
return true;
}
if (swizzle.length() != 4) {
exception_state.ThrowTypeError(String::Format(
"Swizzle ('%s') must be exactly a four-character string.",
swizzle.Utf8().c_str()));
return false;
}
if (AsDawnEnum(swizzle[0]) == wgpu::ComponentSwizzle::Undefined ||
AsDawnEnum(swizzle[1]) == wgpu::ComponentSwizzle::Undefined ||
AsDawnEnum(swizzle[2]) == wgpu::ComponentSwizzle::Undefined ||
AsDawnEnum(swizzle[3]) == wgpu::ComponentSwizzle::Undefined) {
exception_state.ThrowTypeError(String::Format(
"Swizzle ('%s') must contain only 'r', 'g', 'b', 'a', '0', "
"or '1' characters.",
swizzle.Utf8().c_str()));
return false;
}
return true;
}
// Dawn represents `undefined` as the special uint32_t value (0xFFFF'FFFF).
// Blink must make sure that an actual value of 0xFFFF'FFFF coming in from JS
// is not treated as the special `undefined` value, so it injects an error in
// that case.
std::string ValidateTextureMipLevelAndArrayLayerCounts(
const GPUTextureViewDescriptor* webgpu_desc) {
DCHECK(webgpu_desc);
if (webgpu_desc->hasMipLevelCount() &&
webgpu_desc->mipLevelCount() == wgpu::kMipLevelCountUndefined) {
std::ostringstream error;
error << "mipLevelCount (" << webgpu_desc->mipLevelCount()
<< ") is too large when validating [GPUTextureViewDescriptor";
if (!webgpu_desc->label().empty()) {
error << " '" << webgpu_desc->label().Utf8() << "'";
}
error << "].";
return error.str();
}
if (webgpu_desc->hasArrayLayerCount() &&
webgpu_desc->arrayLayerCount() == wgpu::kArrayLayerCountUndefined) {
std::ostringstream error;
error << "arrayLayerCount (" << webgpu_desc->arrayLayerCount()
<< ") is too large when validating [GPUTextureViewDescriptor";
if (!webgpu_desc->label().empty()) {
error << " '" << webgpu_desc->label().Utf8() << "'";
}
error << "].";
return error.str();
}
return std::string();
}
} // anonymous namespace
// static
GPUTexture* GPUTexture::Create(GPUDevice* device,
const GPUTextureDescriptor* webgpu_desc,
ExceptionState& exception_state) {
DCHECK(device);
DCHECK(webgpu_desc);
wgpu::TextureDescriptor dawn_desc;
wgpu::TextureBindingViewDimensionDescriptor
texture_binding_view_dimension_desc;
std::string label;
base::HeapArray<wgpu::TextureFormat> view_formats;
if (!ConvertToDawn(webgpu_desc, &dawn_desc,
&texture_binding_view_dimension_desc, &label,
&view_formats, device, exception_state)) {
return nullptr;
}
if (!device->ValidateTextureFormatUsage(webgpu_desc->format(),
exception_state)) {
return nullptr;
}
for (auto view_format : webgpu_desc->viewFormats()) {
if (!device->ValidateTextureFormatUsage(view_format, exception_state)) {
return nullptr;
}
}
GPUTexture* texture = MakeGarbageCollected<GPUTexture>(
device, device->GetHandle().CreateTexture(&dawn_desc),
webgpu_desc->label());
return texture;
}
GPUTexture* GPUTexture::Create(GPUDevice* device,
const wgpu::TextureDescriptor* desc) {
DCHECK(device);
DCHECK(desc);
return MakeGarbageCollected<GPUTexture>(
device, device->GetHandle().CreateTexture(desc),
String::FromUTF8(desc->label));
}
// static
GPUTexture* GPUTexture::CreateError(GPUDevice* device,
const wgpu::TextureDescriptor* desc) {
DCHECK(device);
DCHECK(desc);
return MakeGarbageCollected<GPUTexture>(
device, device->GetHandle().CreateErrorTexture(desc),
String::FromUTF8(desc->label));
}
GPUTexture::GPUTexture(GPUDevice* device,
wgpu::Texture texture,
const String& label)
: DawnObject<wgpu::Texture>(device, std::move(texture), label),
dimension_(GetHandle().GetDimension()),
format_(GetHandle().GetFormat()),
usage_(GetHandle().GetUsage()) {}
GPUTexture::GPUTexture(GPUDevice* device,
wgpu::TextureFormat format,
wgpu::TextureUsage usage,
scoped_refptr<WebGPUMailboxTexture> mailbox_texture,
const String& label)
: DawnObject<wgpu::Texture>(device, mailbox_texture->GetTexture(), label),
format_(format),
usage_(usage),
mailbox_texture_(std::move(mailbox_texture)) {
if (mailbox_texture_) {
device_->TrackTextureWithMailbox(this);
}
// Mailbox textures are all 2d texture.
dimension_ = wgpu::TextureDimension::e2D;
}
GPUTextureView* GPUTexture::createView(
const GPUTextureViewDescriptor* webgpu_desc,
ExceptionState& exception_state) {
DCHECK(webgpu_desc);
if (webgpu_desc->hasFormat() && !device()->ValidateTextureFormatUsage(
webgpu_desc->format(), exception_state)) {
return nullptr;
}
if (!ValidateSwizzle(webgpu_desc->swizzle(), exception_state)) {
return nullptr;
}
std::string error = ValidateTextureMipLevelAndArrayLayerCounts(webgpu_desc);
if (!error.empty()) {
device()->InjectError(wgpu::ErrorType::Validation, error.c_str());
return MakeGarbageCollected<GPUTextureView>(
device(), GetHandle().CreateErrorView(nullptr), String());
}
OwnedTextureViewDescriptor dawn_desc_info;
ConvertToDawnType(webgpu_desc, &dawn_desc_info);
GPUTextureView* view = MakeGarbageCollected<GPUTextureView>(
device_, GetHandle().CreateView(&dawn_desc_info.dawn_desc),
webgpu_desc->label());
return view;
}
GPUTexture::~GPUTexture() {
DissociateMailbox();
}
void GPUTexture::destroy() {
if (destroyed_) {
return;
}
if (destroy_callback_) {
std::move(destroy_callback_).Run();
}
if (mailbox_texture_) {
DissociateMailbox();
device_->UntrackTextureWithMailbox(this);
}
GetHandle().Destroy();
destroyed_ = true;
}
uint32_t GPUTexture::width() const {
return GetHandle().GetWidth();
}
uint32_t GPUTexture::height() const {
return GetHandle().GetHeight();
}
uint32_t GPUTexture::depthOrArrayLayers() const {
return GetHandle().GetDepthOrArrayLayers();
}
uint32_t GPUTexture::mipLevelCount() const {
return GetHandle().GetMipLevelCount();
}
uint32_t GPUTexture::sampleCount() const {
return GetHandle().GetSampleCount();
}
V8GPUTextureDimension GPUTexture::dimension() const {
return FromDawnEnum(GetHandle().GetDimension());
}
V8GPUTextureFormat GPUTexture::format() const {
return FromDawnEnum(GetHandle().GetFormat());
}
uint32_t GPUTexture::usage() const {
return static_cast<uint32_t>(GetHandle().GetUsage());
}
void GPUTexture::DissociateMailbox() {
if (mailbox_texture_) {
mailbox_texture_->Dissociate();
mailbox_texture_ = nullptr;
}
}
scoped_refptr<WebGPUMailboxTexture> GPUTexture::GetMailboxTexture() {
return mailbox_texture_;
}
void GPUTexture::SetBeforeDestroyCallback(base::OnceClosure callback) {
destroy_callback_ = std::move(callback);
}
void GPUTexture::ClearBeforeDestroyCallback() {
destroy_callback_.Reset();
}
} // namespace blink