blob: 04d7322136ad192f44fb45c25c7e9d021b8bb306 [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_command_encoder.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_command_buffer_descriptor.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_command_encoder_descriptor.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_compute_pass_descriptor.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_compute_pass_timestamp_writes.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_render_pass_color_attachment.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_render_pass_depth_stencil_attachment.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_render_pass_descriptor.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_render_pass_timestamp_writes.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_texel_copy_buffer_info.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_texel_copy_texture_info.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_union_gputexture_gputextureview.h"
#include "third_party/blink/renderer/modules/webgpu/dawn_conversions.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_buffer.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_command_buffer.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_compute_pass_encoder.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_query_set.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_render_pass_encoder.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_supported_features.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_texture.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_texture_view.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
namespace blink {
wgpu::TextureView GetTextureView(
V8UnionGPUTextureOrGPUTextureView* textureOrView) {
switch (textureOrView->GetContentType()) {
case V8UnionGPUTextureOrGPUTextureView::ContentType::kGPUTexture: {
wgpu::Texture texture = AsDawnType(textureOrView->GetAsGPUTexture());
return texture.CreateView();
}
case V8UnionGPUTextureOrGPUTextureView::ContentType::kGPUTextureView:
return AsDawnType(textureOrView->GetAsGPUTextureView());
}
}
bool ConvertToDawn(const GPURenderPassColorAttachment* in,
wgpu::RenderPassColorAttachment* out,
ExceptionState& exception_state) {
DCHECK(in);
DCHECK(out);
*out = {
.view = GetTextureView(in->view()),
.loadOp = AsDawnEnum(in->loadOp()),
.storeOp = AsDawnEnum(in->storeOp()),
};
if (in->hasDepthSlice()) {
out->depthSlice = in->depthSlice();
}
if (in->hasResolveTarget()) {
out->resolveTarget = GetTextureView(in->resolveTarget());
}
if (in->hasClearValue() &&
!ConvertToDawn(in->clearValue(), &out->clearValue, exception_state)) {
return false;
}
return true;
}
namespace {
// Dawn represents `undefined` as the special uint32_t value
// wgpu::kDepthSliceUndefined (0xFFFF'FFFF). Blink must make sure that an
// actual value of 0xFFFF'FFFF coming in from JS is not treated as
// wgpu::kDepthSliceUndefined, so it injects an error in that case.
std::string ValidateColorAttachmentsDepthSlice(
const HeapVector<Member<GPURenderPassColorAttachment>>& in,
const char* desc_label) {
for (wtf_size_t i = 0; i < in.size(); ++i) {
if (!in[i]) {
continue;
}
const GPURenderPassColorAttachment* attachment = in[i].Get();
if (attachment->hasDepthSlice() &&
attachment->depthSlice() == wgpu::kDepthSliceUndefined) {
std::ostringstream error;
error << "depthSlice (" << attachment->depthSlice()
<< ") is too large when validating [GPURenderPassDescriptor";
if (desc_label != nullptr && strlen(desc_label) != 0) {
error << " '" << desc_label << "'";
}
error << "] against the colorAttachment (" << i << ").";
return error.str();
}
}
return std::string();
}
// Dawn represents `undefined` as the special uint32_t value
// wgpu::kQuerySetIndexUndefined (0xFFFF'FFFF). Blink must make sure that an
// actual value of 0xFFFF'FFFF coming in from JS is not treated as
// wgpu::kQuerySetIndexUndefined, so it injects an error in that case.
template <typename GPUTimestampWrites>
std::string ValidateAndConvertTimestampWrites(
const GPUTimestampWrites* webgpu_desc,
wgpu::PassTimestampWrites* dawn_desc,
const char* desc_type,
const char* desc_label) {
DCHECK(webgpu_desc);
DCHECK(webgpu_desc->querySet());
uint32_t beginningOfPassWriteIndex = 0;
if (webgpu_desc->hasBeginningOfPassWriteIndex()) {
beginningOfPassWriteIndex = webgpu_desc->beginningOfPassWriteIndex();
if (beginningOfPassWriteIndex == wgpu::kQuerySetIndexUndefined) {
std::ostringstream error;
error << "beginningOfPassWriteIndex (" << beginningOfPassWriteIndex
<< ") is too large when validating [" << desc_type;
if (desc_label != nullptr && strlen(desc_label) != 0) {
error << " '" << desc_label << "'";
}
error << "].";
return error.str();
}
} else {
beginningOfPassWriteIndex = wgpu::kQuerySetIndexUndefined;
}
uint32_t endOfPassWriteIndex = 0;
if (webgpu_desc->hasEndOfPassWriteIndex()) {
endOfPassWriteIndex = webgpu_desc->endOfPassWriteIndex();
if (endOfPassWriteIndex == wgpu::kQuerySetIndexUndefined) {
std::ostringstream error;
error << "endOfPassWriteIndex (" << endOfPassWriteIndex
<< ") is too large when validating [" << desc_type;
if (desc_label != nullptr && strlen(desc_label) != 0) {
error << " '" << desc_label << "'";
}
error << "].";
return error.str();
}
} else {
endOfPassWriteIndex = wgpu::kQuerySetIndexUndefined;
}
*dawn_desc = {
.querySet = webgpu_desc->querySet()->GetHandle(),
.beginningOfPassWriteIndex = beginningOfPassWriteIndex,
.endOfPassWriteIndex = endOfPassWriteIndex,
};
return std::string();
}
wgpu::RenderPassDepthStencilAttachment AsDawnType(
GPUDevice* device,
const GPURenderPassDepthStencilAttachment* webgpu_desc) {
DCHECK(webgpu_desc);
wgpu::RenderPassDepthStencilAttachment dawn_desc = {
.view = GetTextureView(webgpu_desc->view()),
// NaN is the default value in Dawn
.depthClearValue = webgpu_desc->getDepthClearValueOr(
std::numeric_limits<float>::quiet_NaN()),
.depthReadOnly = webgpu_desc->depthReadOnly(),
.stencilReadOnly = webgpu_desc->stencilReadOnly(),
};
if (webgpu_desc->hasDepthLoadOp()) {
dawn_desc.depthLoadOp = AsDawnEnum(webgpu_desc->depthLoadOp());
}
if (webgpu_desc->hasDepthStoreOp()) {
dawn_desc.depthStoreOp = AsDawnEnum(webgpu_desc->depthStoreOp());
}
if (webgpu_desc->hasStencilLoadOp()) {
dawn_desc.stencilLoadOp = AsDawnEnum(webgpu_desc->stencilLoadOp());
dawn_desc.stencilClearValue = webgpu_desc->stencilClearValue();
}
if (webgpu_desc->hasStencilStoreOp()) {
dawn_desc.stencilStoreOp = AsDawnEnum(webgpu_desc->stencilStoreOp());
}
return dawn_desc;
}
wgpu::TexelCopyBufferInfo ValidateAndConvertTexelCopyBufferInfo(
const GPUTexelCopyBufferInfo* webgpu_view,
const char** error) {
DCHECK(webgpu_view);
DCHECK(webgpu_view->buffer());
wgpu::TexelCopyBufferInfo dawn_view = {
.buffer = webgpu_view->buffer()->GetHandle()};
*error = ValidateTexelCopyBufferLayout(webgpu_view, &dawn_view.layout);
return dawn_view;
}
wgpu::CommandEncoderDescriptor AsDawnType(
const GPUCommandEncoderDescriptor* webgpu_desc,
std::string* label) {
DCHECK(webgpu_desc);
DCHECK(label);
wgpu::CommandEncoderDescriptor dawn_desc = {};
*label = webgpu_desc->label().Utf8();
if (!label->empty()) {
dawn_desc.label = label->c_str();
}
return dawn_desc;
}
} // anonymous namespace
// static
GPUCommandEncoder* GPUCommandEncoder::Create(
GPUDevice* device,
const GPUCommandEncoderDescriptor* webgpu_desc) {
DCHECK(device);
DCHECK(webgpu_desc);
std::string label;
wgpu::CommandEncoderDescriptor dawn_desc = AsDawnType(webgpu_desc, &label);
GPUCommandEncoder* encoder = MakeGarbageCollected<GPUCommandEncoder>(
device, device->GetHandle().CreateCommandEncoder(&dawn_desc),
webgpu_desc->label());
return encoder;
}
GPUCommandEncoder::GPUCommandEncoder(GPUDevice* device,
wgpu::CommandEncoder command_encoder,
const String& label)
: DawnObject<wgpu::CommandEncoder>(device,
std::move(command_encoder),
label) {}
GPURenderPassEncoder* GPUCommandEncoder::beginRenderPass(
const GPURenderPassDescriptor* descriptor,
ExceptionState& exception_state) {
DCHECK(descriptor);
wgpu::RenderPassDescriptor dawn_desc = {};
std::string label = descriptor->label().Utf8();
if (!label.empty()) {
dawn_desc.label = label.c_str();
}
std::unique_ptr<wgpu::RenderPassColorAttachment[]> color_attachments;
dawn_desc.colorAttachmentCount = descriptor->colorAttachments().size();
if (dawn_desc.colorAttachmentCount > 0) {
std::string error = ValidateColorAttachmentsDepthSlice(
descriptor->colorAttachments(), label.c_str());
if (!error.empty()) {
GetHandle().InjectValidationError(error.c_str());
}
if (!ConvertToDawn(descriptor->colorAttachments(), &color_attachments,
exception_state)) {
return nullptr;
}
dawn_desc.colorAttachments = color_attachments.get();
}
wgpu::RenderPassDepthStencilAttachment depthStencilAttachment = {};
if (descriptor->hasDepthStencilAttachment()) {
const GPURenderPassDepthStencilAttachment* depth_stencil =
descriptor->depthStencilAttachment();
depthStencilAttachment = AsDawnType(device_, depth_stencil);
dawn_desc.depthStencilAttachment = &depthStencilAttachment;
}
if (descriptor->hasOcclusionQuerySet()) {
dawn_desc.occlusionQuerySet = AsDawnType(descriptor->occlusionQuerySet());
}
wgpu::PassTimestampWrites timestampWrites = {};
if (descriptor->hasTimestampWrites()) {
GPURenderPassTimestampWrites* timestamp_writes =
descriptor->timestampWrites();
std::string error = ValidateAndConvertTimestampWrites(
timestamp_writes, &timestampWrites, "GPURenderPassDescriptor",
label.c_str());
if (!error.empty()) {
GetHandle().InjectValidationError(error.c_str());
} else {
dawn_desc.timestampWrites = &timestampWrites;
}
}
wgpu::RenderPassMaxDrawCount max_draw_count = {};
if (descriptor->hasMaxDrawCount()) {
max_draw_count.maxDrawCount = descriptor->maxDrawCount();
dawn_desc.nextInChain = &max_draw_count;
}
GPURenderPassEncoder* encoder = MakeGarbageCollected<GPURenderPassEncoder>(
device_, GetHandle().BeginRenderPass(&dawn_desc), descriptor->label());
return encoder;
}
GPUComputePassEncoder* GPUCommandEncoder::beginComputePass(
const GPUComputePassDescriptor* descriptor,
ExceptionState& exception_state) {
wgpu::ComputePassDescriptor dawn_desc = {};
std::string label = descriptor->label().Utf8();
if (!label.empty()) {
dawn_desc.label = label.c_str();
}
wgpu::PassTimestampWrites timestampWrites = {};
if (descriptor->hasTimestampWrites()) {
GPUComputePassTimestampWrites* timestamp_writes =
descriptor->timestampWrites();
std::string error = ValidateAndConvertTimestampWrites(
timestamp_writes, &timestampWrites, "GPUComputePassDescriptor",
label.c_str());
if (!error.empty()) {
GetHandle().InjectValidationError(error.c_str());
} else {
dawn_desc.timestampWrites = &timestampWrites;
}
}
GPUComputePassEncoder* encoder = MakeGarbageCollected<GPUComputePassEncoder>(
device_, GetHandle().BeginComputePass(&dawn_desc), descriptor->label());
return encoder;
}
void GPUCommandEncoder::copyBufferToTexture(
GPUTexelCopyBufferInfo* source,
GPUTexelCopyTextureInfo* destination,
const V8GPUExtent3D* copy_size,
ExceptionState& exception_state) {
wgpu::Extent3D dawn_copy_size;
wgpu::TexelCopyTextureInfo dawn_destination;
if (!ConvertToDawn(copy_size, &dawn_copy_size, device_, exception_state) ||
!ConvertToDawn(destination, &dawn_destination, exception_state)) {
return;
}
const char* error = nullptr;
wgpu::TexelCopyBufferInfo dawn_source =
ValidateAndConvertTexelCopyBufferInfo(source, &error);
if (error) {
GetHandle().InjectValidationError(error);
return;
}
GetHandle().CopyBufferToTexture(&dawn_source, &dawn_destination,
&dawn_copy_size);
}
void GPUCommandEncoder::copyTextureToBuffer(GPUTexelCopyTextureInfo* source,
GPUTexelCopyBufferInfo* destination,
const V8GPUExtent3D* copy_size,
ExceptionState& exception_state) {
wgpu::Extent3D dawn_copy_size;
wgpu::TexelCopyTextureInfo dawn_source;
if (!ConvertToDawn(copy_size, &dawn_copy_size, device_, exception_state) ||
!ConvertToDawn(source, &dawn_source, exception_state)) {
return;
}
const char* error = nullptr;
wgpu::TexelCopyBufferInfo dawn_destination =
ValidateAndConvertTexelCopyBufferInfo(destination, &error);
if (error) {
GetHandle().InjectValidationError(error);
return;
}
GetHandle().CopyTextureToBuffer(&dawn_source, &dawn_destination,
&dawn_copy_size);
}
void GPUCommandEncoder::copyTextureToTexture(
GPUTexelCopyTextureInfo* source,
GPUTexelCopyTextureInfo* destination,
const V8GPUExtent3D* copy_size,
ExceptionState& exception_state) {
wgpu::Extent3D dawn_copy_size;
wgpu::TexelCopyTextureInfo dawn_source;
wgpu::TexelCopyTextureInfo dawn_destination;
if (!ConvertToDawn(copy_size, &dawn_copy_size, device_, exception_state) ||
!ConvertToDawn(source, &dawn_source, exception_state) ||
!ConvertToDawn(destination, &dawn_destination, exception_state)) {
return;
}
GetHandle().CopyTextureToTexture(&dawn_source, &dawn_destination,
&dawn_copy_size);
}
void GPUCommandEncoder::writeTimestamp(DawnObject<wgpu::QuerySet>* querySet,
uint32_t queryIndex,
ExceptionState& exception_state) {
V8GPUFeatureName::Enum requiredFeatureEnum =
V8GPUFeatureName::Enum::kTimestampQuery;
if (!device_->features()->Has(requiredFeatureEnum)) {
exception_state.ThrowTypeError(
String::Format("Use of the writeTimestamp() method requires the '%s' "
"feature to be enabled on %s.",
V8GPUFeatureName(requiredFeatureEnum).AsCStr(),
device_->GetFormattedLabel().c_str()));
return;
}
GetHandle().WriteTimestamp(querySet->GetHandle(), queryIndex);
}
GPUCommandBuffer* GPUCommandEncoder::finish(
const GPUCommandBufferDescriptor* descriptor) {
wgpu::CommandBufferDescriptor dawn_desc = {};
std::string label = descriptor->label().Utf8();
if (!label.empty()) {
dawn_desc.label = label.c_str();
}
GPUCommandBuffer* command_buffer = MakeGarbageCollected<GPUCommandBuffer>(
device_, GetHandle().Finish(&dawn_desc), descriptor->label());
return command_buffer;
}
} // namespace blink