From 118d2dd19efbb5a827197f8788edc488d3986827 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Tue, 28 Sep 2021 13:18:40 +0000 Subject: [PATCH] dawn_node: Add binding/Converter.[cpp,h] The interop -> Dawn conversion utility class. Bug: dawn:1123 Change-Id: I8a2a352eb730a4a528f6a0262d5b21e08d85b413 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/64907 Commit-Queue: Ben Clayton Reviewed-by: Austin Eng --- src/dawn_node/binding/CMakeLists.txt | 2 + src/dawn_node/binding/Converter.cpp | 1141 ++++++++++++++++++++++++++ src/dawn_node/binding/Converter.h | 357 ++++++++ 3 files changed, 1500 insertions(+) create mode 100644 src/dawn_node/binding/Converter.cpp create mode 100644 src/dawn_node/binding/Converter.h diff --git a/src/dawn_node/binding/CMakeLists.txt b/src/dawn_node/binding/CMakeLists.txt index dd7ae4a859..514bdd9198 100644 --- a/src/dawn_node/binding/CMakeLists.txt +++ b/src/dawn_node/binding/CMakeLists.txt @@ -15,6 +15,8 @@ add_library(dawn_node_binding STATIC "AsyncRunner.cpp" "AsyncRunner.h" + "Converter.cpp" + "Converter.h" "Errors.cpp" "Errors.h" "GPU.h" diff --git a/src/dawn_node/binding/Converter.cpp b/src/dawn_node/binding/Converter.cpp new file mode 100644 index 0000000000..2ab986a547 --- /dev/null +++ b/src/dawn_node/binding/Converter.cpp @@ -0,0 +1,1141 @@ +// Copyright 2021 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/dawn_node/binding/Converter.h" + +#include "src/dawn_node/binding/GPUBuffer.h" +#include "src/dawn_node/binding/GPUPipelineLayout.h" +#include "src/dawn_node/binding/GPUSampler.h" +#include "src/dawn_node/binding/GPUShaderModule.h" +#include "src/dawn_node/binding/GPUTexture.h" +#include "src/dawn_node/binding/GPUTextureView.h" +#include "src/dawn_node/utils/Debug.h" + +namespace wgpu { namespace binding { + + Converter::~Converter() { + for (auto& free : free_) { + free(); + } + } + + bool Converter::Convert(wgpu::Extent3D& out, const interop::GPUExtent3D& in) { + out = {}; + if (auto* dict = std::get_if(&in)) { + out.depthOrArrayLayers = dict->depthOrArrayLayers; + out.width = dict->width; + out.height = dict->height; + return true; + } + if (auto* vec = std::get_if>(&in)) { + switch (vec->size()) { + default: + case 3: + out.depthOrArrayLayers = (*vec)[2]; + case 2: // fallthrough + out.height = (*vec)[1]; + case 1: // fallthrough + out.width = (*vec)[0]; + return true; + case 0: + break; + } + } + Napi::Error::New(env, "invalid value for GPUExtent3D").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::Origin3D& out, const interop::GPUOrigin3DDict& in) { + out = {}; + out.x = in.x; + out.y = in.y; + out.z = in.z; + return true; + } + + bool Converter::Convert(wgpu::Color& out, const interop::GPUColor& in) { + out = {}; + if (auto* dict = std::get_if(&in)) { + out.r = dict->r; + out.g = dict->g; + out.b = dict->b; + out.a = dict->a; + return true; + } + if (auto* vec = std::get_if>(&in)) { + switch (vec->size()) { + default: + case 4: + out.a = (*vec)[3]; + case 3: // fallthrough + out.b = (*vec)[2]; + case 2: // fallthrough + out.g = (*vec)[1]; + case 1: // fallthrough + out.r = (*vec)[0]; + return true; + case 0: + break; + } + } + Napi::Error::New(env, "invalid value for GPUColor").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::Origin3D& out, + const std::vector& in) { + out = {}; + switch (in.size()) { + default: + case 3: + out.z = in[2]; + case 2: // fallthrough + out.y = in[1]; + case 1: // fallthrough + out.x = in[0]; + case 0: + break; + } + return true; + } + + bool Converter::Convert(wgpu::TextureAspect& out, const interop::GPUTextureAspect& in) { + out = wgpu::TextureAspect::All; + switch (in) { + case interop::GPUTextureAspect::kAll: + out = wgpu::TextureAspect::All; + return true; + case interop::GPUTextureAspect::kStencilOnly: + out = wgpu::TextureAspect::StencilOnly; + return true; + case interop::GPUTextureAspect::kDepthOnly: + out = wgpu::TextureAspect::DepthOnly; + return true; + } + Napi::Error::New(env, "invalid value for GPUTextureAspect").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::ImageCopyTexture& out, const interop::GPUImageCopyTexture& in) { + out = {}; + return Convert(out.texture, in.texture) && Convert(out.mipLevel, in.mipLevel) && + Convert(out.origin, in.origin) && Convert(out.aspect, in.aspect); + } + + bool Converter::Convert(wgpu::ImageCopyBuffer& out, const interop::GPUImageCopyBuffer& in) { + out = {}; + out.buffer = *in.buffer.As(); + return Convert(out.layout.offset, in.offset) && + Convert(out.layout.bytesPerRow, in.bytesPerRow) && + Convert(out.layout.rowsPerImage, in.rowsPerImage); + } + + bool Converter::Convert(BufferSource& out, interop::BufferSource in) { + out = {}; + if (auto* view = std::get_if(&in)) { + std::visit( + [&](auto&& v) { + auto arr = v.ArrayBuffer(); + out.data = arr.Data(); + out.size = arr.ByteLength(); + }, + *view); + return true; + } + if (auto* arr = std::get_if(&in)) { + out.data = arr->Data(); + out.size = arr->ByteLength(); + return true; + } + Napi::Error::New(env, "invalid value for BufferSource").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::TextureDataLayout& out, const interop::GPUImageDataLayout& in) { + out = {}; + return Convert(out.bytesPerRow, in.bytesPerRow) && Convert(out.offset, in.offset) && + Convert(out.rowsPerImage, in.rowsPerImage); + } + + bool Converter::Convert(wgpu::TextureFormat& out, const interop::GPUTextureFormat& in) { + out = wgpu::TextureFormat::Undefined; + switch (in) { + case interop::GPUTextureFormat::kR8Unorm: + out = wgpu::TextureFormat::R8Unorm; + return true; + case interop::GPUTextureFormat::kR8Snorm: + out = wgpu::TextureFormat::R8Snorm; + return true; + case interop::GPUTextureFormat::kR8Uint: + out = wgpu::TextureFormat::R8Uint; + return true; + case interop::GPUTextureFormat::kR8Sint: + out = wgpu::TextureFormat::R8Sint; + return true; + case interop::GPUTextureFormat::kR16Uint: + out = wgpu::TextureFormat::R16Uint; + return true; + case interop::GPUTextureFormat::kR16Sint: + out = wgpu::TextureFormat::R16Sint; + return true; + case interop::GPUTextureFormat::kR16Float: + out = wgpu::TextureFormat::R16Float; + return true; + case interop::GPUTextureFormat::kRg8Unorm: + out = wgpu::TextureFormat::RG8Unorm; + return true; + case interop::GPUTextureFormat::kRg8Snorm: + out = wgpu::TextureFormat::RG8Snorm; + return true; + case interop::GPUTextureFormat::kRg8Uint: + out = wgpu::TextureFormat::RG8Uint; + return true; + case interop::GPUTextureFormat::kRg8Sint: + out = wgpu::TextureFormat::RG8Sint; + return true; + case interop::GPUTextureFormat::kR32Uint: + out = wgpu::TextureFormat::R32Uint; + return true; + case interop::GPUTextureFormat::kR32Sint: + out = wgpu::TextureFormat::R32Sint; + return true; + case interop::GPUTextureFormat::kR32Float: + out = wgpu::TextureFormat::R32Float; + return true; + case interop::GPUTextureFormat::kRg16Uint: + out = wgpu::TextureFormat::RG16Uint; + return true; + case interop::GPUTextureFormat::kRg16Sint: + out = wgpu::TextureFormat::RG16Sint; + return true; + case interop::GPUTextureFormat::kRg16Float: + out = wgpu::TextureFormat::RG16Float; + return true; + case interop::GPUTextureFormat::kRgba8Unorm: + out = wgpu::TextureFormat::RGBA8Unorm; + return true; + case interop::GPUTextureFormat::kRgba8UnormSrgb: + out = wgpu::TextureFormat::RGBA8UnormSrgb; + return true; + case interop::GPUTextureFormat::kRgba8Snorm: + out = wgpu::TextureFormat::RGBA8Snorm; + return true; + case interop::GPUTextureFormat::kRgba8Uint: + out = wgpu::TextureFormat::RGBA8Uint; + return true; + case interop::GPUTextureFormat::kRgba8Sint: + out = wgpu::TextureFormat::RGBA8Sint; + return true; + case interop::GPUTextureFormat::kBgra8Unorm: + out = wgpu::TextureFormat::BGRA8Unorm; + return true; + case interop::GPUTextureFormat::kBgra8UnormSrgb: + out = wgpu::TextureFormat::BGRA8UnormSrgb; + return true; + case interop::GPUTextureFormat::kRgb9E5Ufloat: + out = wgpu::TextureFormat::RGB9E5Ufloat; + return true; + case interop::GPUTextureFormat::kRgb10A2Unorm: + out = wgpu::TextureFormat::RGB10A2Unorm; + return true; + case interop::GPUTextureFormat::kRg11B10Ufloat: + out = wgpu::TextureFormat::RG11B10Ufloat; + return true; + case interop::GPUTextureFormat::kRg32Uint: + out = wgpu::TextureFormat::RG32Uint; + return true; + case interop::GPUTextureFormat::kRg32Sint: + out = wgpu::TextureFormat::RG32Sint; + return true; + case interop::GPUTextureFormat::kRg32Float: + out = wgpu::TextureFormat::RG32Float; + return true; + case interop::GPUTextureFormat::kRgba16Uint: + out = wgpu::TextureFormat::RGBA16Uint; + return true; + case interop::GPUTextureFormat::kRgba16Sint: + out = wgpu::TextureFormat::RGBA16Sint; + return true; + case interop::GPUTextureFormat::kRgba16Float: + out = wgpu::TextureFormat::RGBA16Float; + return true; + case interop::GPUTextureFormat::kRgba32Uint: + out = wgpu::TextureFormat::RGBA32Uint; + return true; + case interop::GPUTextureFormat::kRgba32Sint: + out = wgpu::TextureFormat::RGBA32Sint; + return true; + case interop::GPUTextureFormat::kRgba32Float: + out = wgpu::TextureFormat::RGBA32Float; + return true; + case interop::GPUTextureFormat::kStencil8: + out = wgpu::TextureFormat::Stencil8; + return true; + case interop::GPUTextureFormat::kDepth16Unorm: + break; // TODO(crbug.com/dawn/1130): Unsupported. + case interop::GPUTextureFormat::kDepth24Plus: + out = wgpu::TextureFormat::Depth24Plus; + return true; + case interop::GPUTextureFormat::kDepth24PlusStencil8: + out = wgpu::TextureFormat::Depth24PlusStencil8; + return true; + case interop::GPUTextureFormat::kDepth32Float: + out = wgpu::TextureFormat::Depth32Float; + return true; + case interop::GPUTextureFormat::kBc1RgbaUnorm: + out = wgpu::TextureFormat::BC1RGBAUnorm; + return true; + case interop::GPUTextureFormat::kBc1RgbaUnormSrgb: + out = wgpu::TextureFormat::BC1RGBAUnormSrgb; + return true; + case interop::GPUTextureFormat::kBc2RgbaUnorm: + out = wgpu::TextureFormat::BC2RGBAUnorm; + return true; + case interop::GPUTextureFormat::kBc2RgbaUnormSrgb: + out = wgpu::TextureFormat::BC2RGBAUnormSrgb; + return true; + case interop::GPUTextureFormat::kBc3RgbaUnorm: + out = wgpu::TextureFormat::BC3RGBAUnorm; + return true; + case interop::GPUTextureFormat::kBc3RgbaUnormSrgb: + out = wgpu::TextureFormat::BC3RGBAUnormSrgb; + return true; + case interop::GPUTextureFormat::kBc4RUnorm: + out = wgpu::TextureFormat::BC4RUnorm; + return true; + case interop::GPUTextureFormat::kBc4RSnorm: + out = wgpu::TextureFormat::BC4RSnorm; + return true; + case interop::GPUTextureFormat::kBc5RgUnorm: + out = wgpu::TextureFormat::BC5RGUnorm; + return true; + case interop::GPUTextureFormat::kBc5RgSnorm: + out = wgpu::TextureFormat::BC5RGSnorm; + return true; + case interop::GPUTextureFormat::kBc6HRgbUfloat: + out = wgpu::TextureFormat::BC6HRGBUfloat; + return true; + case interop::GPUTextureFormat::kBc6HRgbFloat: + out = wgpu::TextureFormat::BC6HRGBFloat; + return true; + case interop::GPUTextureFormat::kBc7RgbaUnorm: + out = wgpu::TextureFormat::BC7RGBAUnorm; + return true; + case interop::GPUTextureFormat::kBc7RgbaUnormSrgb: + out = wgpu::TextureFormat::BC7RGBAUnormSrgb; + return true; + case interop::GPUTextureFormat::kDepth24UnormStencil8: + break; // TODO(crbug.com/dawn/1130): Unsupported. + case interop::GPUTextureFormat::kDepth32FloatStencil8: + break; // TODO(crbug.com/dawn/1130): Unsupported. + } + // TODO(crbug.com/dawn/1130): Add ASTC and ETC formats. + Napi::Error::New(env, "invalid value for GPUTextureFormat").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::TextureUsage& out, const interop::GPUTextureUsageFlags& in) { + out = static_cast(in); + return true; + } + + bool Converter::Convert(wgpu::ColorWriteMask& out, const interop::GPUColorWriteFlags& in) { + out = static_cast(in); + return true; + } + + bool Converter::Convert(wgpu::BufferUsage& out, const interop::GPUBufferUsageFlags& in) { + out = static_cast(in); + return true; + } + + bool Converter::Convert(wgpu::MapMode& out, const interop::GPUMapModeFlags& in) { + out = static_cast(in); + return true; + } + + bool Converter::Convert(wgpu::ShaderStage& out, const interop::GPUShaderStageFlags& in) { + out = static_cast(in); + return true; + } + + bool Converter::Convert(wgpu::TextureDimension& out, const interop::GPUTextureDimension& in) { + out = wgpu::TextureDimension::e1D; + switch (in) { + case interop::GPUTextureDimension::k1D: + out = wgpu::TextureDimension::e1D; + return true; + case interop::GPUTextureDimension::k2D: + out = wgpu::TextureDimension::e2D; + return true; + case interop::GPUTextureDimension::k3D: + out = wgpu::TextureDimension::e3D; + return true; + } + Napi::Error::New(env, "invalid value for GPUTextureDimension").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::TextureViewDimension& out, + const interop::GPUTextureViewDimension& in) { + out = wgpu::TextureViewDimension::Undefined; + switch (in) { + case interop::GPUTextureViewDimension::k1D: + out = wgpu::TextureViewDimension::e1D; + return true; + case interop::GPUTextureViewDimension::k2D: + out = wgpu::TextureViewDimension::e2D; + return true; + case interop::GPUTextureViewDimension::k2DArray: + out = wgpu::TextureViewDimension::e2DArray; + return true; + case interop::GPUTextureViewDimension::kCube: + out = wgpu::TextureViewDimension::Cube; + return true; + case interop::GPUTextureViewDimension::kCubeArray: + out = wgpu::TextureViewDimension::CubeArray; + return true; + case interop::GPUTextureViewDimension::k3D: + out = wgpu::TextureViewDimension::e3D; + return true; + default: + break; + } + Napi::Error::New(env, "invalid value for GPUTextureViewDimension") + .ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::ProgrammableStageDescriptor& out, + const interop::GPUProgrammableStage& in) { + out = {}; + out.entryPoint = in.entryPoint.c_str(); + out.module = *in.module.As(); + return true; + } + + bool Converter::Convert(wgpu::BlendComponent& out, const interop::GPUBlendComponent& in) { + out = {}; + return Convert(out.operation, in.operation) && Convert(out.dstFactor, in.dstFactor) && + Convert(out.srcFactor, in.srcFactor); + } + + bool Converter::Convert(wgpu::BlendFactor& out, const interop::GPUBlendFactor& in) { + out = wgpu::BlendFactor::Zero; + switch (in) { + case interop::GPUBlendFactor::kZero: + out = wgpu::BlendFactor::Zero; + return true; + case interop::GPUBlendFactor::kOne: + out = wgpu::BlendFactor::One; + return true; + case interop::GPUBlendFactor::kSrc: + out = wgpu::BlendFactor::Src; + return true; + case interop::GPUBlendFactor::kOneMinusSrc: + out = wgpu::BlendFactor::OneMinusSrc; + return true; + case interop::GPUBlendFactor::kSrcAlpha: + out = wgpu::BlendFactor::SrcAlpha; + return true; + case interop::GPUBlendFactor::kOneMinusSrcAlpha: + out = wgpu::BlendFactor::OneMinusSrcAlpha; + return true; + case interop::GPUBlendFactor::kDst: + out = wgpu::BlendFactor::Dst; + return true; + case interop::GPUBlendFactor::kOneMinusDst: + out = wgpu::BlendFactor::OneMinusDst; + return true; + case interop::GPUBlendFactor::kDstAlpha: + out = wgpu::BlendFactor::DstAlpha; + return true; + case interop::GPUBlendFactor::kOneMinusDstAlpha: + out = wgpu::BlendFactor::OneMinusDstAlpha; + return true; + case interop::GPUBlendFactor::kSrcAlphaSaturated: + out = wgpu::BlendFactor::SrcAlphaSaturated; + return true; + case interop::GPUBlendFactor::kConstant: + out = wgpu::BlendFactor::Constant; + return true; + case interop::GPUBlendFactor::kOneMinusConstant: + out = wgpu::BlendFactor::OneMinusConstant; + return true; + default: + break; + } + Napi::Error::New(env, "invalid value for GPUBlendFactor").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::BlendOperation& out, const interop::GPUBlendOperation& in) { + out = wgpu::BlendOperation::Add; + switch (in) { + case interop::GPUBlendOperation::kAdd: + out = wgpu::BlendOperation::Add; + return true; + case interop::GPUBlendOperation::kSubtract: + out = wgpu::BlendOperation::Subtract; + return true; + case interop::GPUBlendOperation::kReverseSubtract: + out = wgpu::BlendOperation::ReverseSubtract; + return true; + case interop::GPUBlendOperation::kMin: + out = wgpu::BlendOperation::Min; + return true; + case interop::GPUBlendOperation::kMax: + out = wgpu::BlendOperation::Max; + return true; + default: + break; + } + Napi::Error::New(env, "invalid value for GPUBlendOperation").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::BlendState& out, const interop::GPUBlendState& in) { + out = {}; + return Convert(out.alpha, in.alpha) && Convert(out.color, in.alpha); + } + + bool Converter::Convert(wgpu::PrimitiveState& out, const interop::GPUPrimitiveState& in) { + out = {}; + return Convert(out.topology, in.topology) && + Convert(out.stripIndexFormat, in.stripIndexFormat) && + Convert(out.frontFace, in.frontFace) && Convert(out.cullMode, in.cullMode); + } + + bool Converter::Convert(wgpu::ColorTargetState& out, const interop::GPUColorTargetState& in) { + out = {}; + return Convert(out.format, in.format) && Convert(out.blend, in.blend) && + Convert(out.writeMask, in.writeMask); + } + + bool Converter::Convert(wgpu::DepthStencilState& out, const interop::GPUDepthStencilState& in) { + out = {}; + return Convert(out.format, in.format) && + Convert(out.depthWriteEnabled, in.depthWriteEnabled) && + Convert(out.depthCompare, in.depthCompare) && + Convert(out.stencilFront, in.stencilFront) && + Convert(out.stencilBack, in.stencilBack) && + Convert(out.stencilReadMask, in.stencilReadMask) && + Convert(out.stencilWriteMask, in.stencilWriteMask) && + Convert(out.depthBias, in.depthBias) && + Convert(out.depthBiasSlopeScale, in.depthBiasSlopeScale) && + Convert(out.depthBiasClamp, in.depthBiasClamp); + } + + bool Converter::Convert(wgpu::MultisampleState& out, const interop::GPUMultisampleState& in) { + out = {}; + return Convert(out.count, in.count) && Convert(out.mask, in.mask) && + Convert(out.alphaToCoverageEnabled, in.alphaToCoverageEnabled); + } + + bool Converter::Convert(wgpu::FragmentState& out, const interop::GPUFragmentState& in) { + out = {}; + return Convert(out.targets, out.targetCount, in.targets) && + Convert(out.module, in.module) && Convert(out.entryPoint, in.entryPoint); + } + + bool Converter::Convert(wgpu::PrimitiveTopology& out, const interop::GPUPrimitiveTopology& in) { + out = wgpu::PrimitiveTopology::LineList; + switch (in) { + case interop::GPUPrimitiveTopology::kPointList: + out = wgpu::PrimitiveTopology::PointList; + return true; + case interop::GPUPrimitiveTopology::kLineList: + out = wgpu::PrimitiveTopology::LineList; + return true; + case interop::GPUPrimitiveTopology::kLineStrip: + out = wgpu::PrimitiveTopology::LineStrip; + return true; + case interop::GPUPrimitiveTopology::kTriangleList: + out = wgpu::PrimitiveTopology::TriangleList; + return true; + case interop::GPUPrimitiveTopology::kTriangleStrip: + out = wgpu::PrimitiveTopology::TriangleStrip; + return true; + } + Napi::Error::New(env, "invalid value for GPUPrimitiveTopology") + .ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::FrontFace& out, const interop::GPUFrontFace& in) { + out = wgpu::FrontFace::CW; + switch (in) { + case interop::GPUFrontFace::kCw: + out = wgpu::FrontFace::CW; + return true; + case interop::GPUFrontFace::kCcw: + out = wgpu::FrontFace::CCW; + return true; + } + Napi::Error::New(env, "invalid value for GPUFrontFace").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::CullMode& out, const interop::GPUCullMode& in) { + out = wgpu::CullMode::None; + switch (in) { + case interop::GPUCullMode::kNone: + out = wgpu::CullMode::None; + return true; + case interop::GPUCullMode::kFront: + out = wgpu::CullMode::Front; + return true; + case interop::GPUCullMode::kBack: + out = wgpu::CullMode::Back; + return true; + } + Napi::Error::New(env, "invalid value for GPUCullMode").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::CompareFunction& out, const interop::GPUCompareFunction& in) { + out = wgpu::CompareFunction::Undefined; + switch (in) { + case interop::GPUCompareFunction::kNever: + out = wgpu::CompareFunction::Never; + return true; + case interop::GPUCompareFunction::kLess: + out = wgpu::CompareFunction::Less; + return true; + case interop::GPUCompareFunction::kLessEqual: + out = wgpu::CompareFunction::LessEqual; + return true; + case interop::GPUCompareFunction::kGreater: + out = wgpu::CompareFunction::Greater; + return true; + case interop::GPUCompareFunction::kGreaterEqual: + out = wgpu::CompareFunction::GreaterEqual; + return true; + case interop::GPUCompareFunction::kEqual: + out = wgpu::CompareFunction::Equal; + return true; + case interop::GPUCompareFunction::kNotEqual: + out = wgpu::CompareFunction::NotEqual; + return true; + case interop::GPUCompareFunction::kAlways: + out = wgpu::CompareFunction::Always; + return true; + } + Napi::Error::New(env, "invalid value for GPUCompareFunction").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::IndexFormat& out, const interop::GPUIndexFormat& in) { + out = wgpu::IndexFormat::Undefined; + switch (in) { + case interop::GPUIndexFormat::kUint16: + out = wgpu::IndexFormat::Uint16; + return true; + case interop::GPUIndexFormat::kUint32: + out = wgpu::IndexFormat::Uint32; + return true; + } + Napi::Error::New(env, "invalid value for GPUIndexFormat").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::StencilOperation& out, const interop::GPUStencilOperation& in) { + out = wgpu::StencilOperation::Zero; + switch (in) { + case interop::GPUStencilOperation::kKeep: + out = wgpu::StencilOperation::Keep; + return true; + case interop::GPUStencilOperation::kZero: + out = wgpu::StencilOperation::Zero; + return true; + case interop::GPUStencilOperation::kReplace: + out = wgpu::StencilOperation::Replace; + return true; + case interop::GPUStencilOperation::kInvert: + out = wgpu::StencilOperation::Invert; + return true; + case interop::GPUStencilOperation::kIncrementClamp: + out = wgpu::StencilOperation::IncrementClamp; + return true; + case interop::GPUStencilOperation::kDecrementClamp: + out = wgpu::StencilOperation::DecrementClamp; + return true; + case interop::GPUStencilOperation::kIncrementWrap: + out = wgpu::StencilOperation::IncrementWrap; + return true; + case interop::GPUStencilOperation::kDecrementWrap: + out = wgpu::StencilOperation::DecrementWrap; + return true; + } + Napi::Error::New(env, "invalid value for GPUStencilOperation").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::StencilFaceState& out, const interop::GPUStencilFaceState& in) { + return Convert(out.compare, in.compare) && Convert(out.failOp, in.failOp) && + Convert(out.depthFailOp, in.depthFailOp) && Convert(out.passOp, in.passOp); + } + + bool Converter::Convert(wgpu::VertexBufferLayout& out, + const interop::GPUVertexBufferLayout& in) { + out = {}; + return Convert(out.attributes, out.attributeCount, in.attributes) && + Convert(out.arrayStride, in.arrayStride) && Convert(out.stepMode, in.stepMode); + } + + bool Converter::Convert(wgpu::VertexState& out, const interop::GPUVertexState& in) { + out = {}; + return Convert(out.module, in.module) && + Convert(out.buffers, out.bufferCount, in.buffers) && + Convert(out.entryPoint, in.entryPoint); + } + + bool Converter::Convert(wgpu::VertexStepMode& out, const interop::GPUVertexStepMode& in) { + out = wgpu::VertexStepMode::Instance; + switch (in) { + case interop::GPUVertexStepMode::kInstance: + out = wgpu::VertexStepMode::Instance; + return true; + case interop::GPUVertexStepMode::kVertex: + out = wgpu::VertexStepMode::Vertex; + return true; + default: + break; + } + Napi::Error::New(env, "invalid value for GPUVertexStepMode").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::VertexAttribute& out, const interop::GPUVertexAttribute& in) { + return Convert(out.format, in.format) && Convert(out.offset, in.offset) && + Convert(out.shaderLocation, in.shaderLocation); + } + + bool Converter::Convert(wgpu::VertexFormat& out, const interop::GPUVertexFormat& in) { + out = wgpu::VertexFormat::Undefined; + switch (in) { + case interop::GPUVertexFormat::kUint8X2: + out = wgpu::VertexFormat::Uint8x2; + return true; + case interop::GPUVertexFormat::kUint8X4: + out = wgpu::VertexFormat::Uint8x4; + return true; + case interop::GPUVertexFormat::kSint8X2: + out = wgpu::VertexFormat::Sint8x2; + return true; + case interop::GPUVertexFormat::kSint8X4: + out = wgpu::VertexFormat::Sint8x4; + return true; + case interop::GPUVertexFormat::kUnorm8X2: + out = wgpu::VertexFormat::Unorm8x2; + return true; + case interop::GPUVertexFormat::kUnorm8X4: + out = wgpu::VertexFormat::Unorm8x4; + return true; + case interop::GPUVertexFormat::kSnorm8X2: + out = wgpu::VertexFormat::Snorm8x2; + return true; + case interop::GPUVertexFormat::kSnorm8X4: + out = wgpu::VertexFormat::Snorm8x4; + return true; + case interop::GPUVertexFormat::kUint16X2: + out = wgpu::VertexFormat::Uint16x2; + return true; + case interop::GPUVertexFormat::kUint16X4: + out = wgpu::VertexFormat::Uint16x4; + return true; + case interop::GPUVertexFormat::kSint16X2: + out = wgpu::VertexFormat::Sint16x2; + return true; + case interop::GPUVertexFormat::kSint16X4: + out = wgpu::VertexFormat::Sint16x4; + return true; + case interop::GPUVertexFormat::kUnorm16X2: + out = wgpu::VertexFormat::Unorm16x2; + return true; + case interop::GPUVertexFormat::kUnorm16X4: + out = wgpu::VertexFormat::Unorm16x4; + return true; + case interop::GPUVertexFormat::kSnorm16X2: + out = wgpu::VertexFormat::Snorm16x2; + return true; + case interop::GPUVertexFormat::kSnorm16X4: + out = wgpu::VertexFormat::Snorm16x4; + return true; + case interop::GPUVertexFormat::kFloat16X2: + out = wgpu::VertexFormat::Float16x2; + return true; + case interop::GPUVertexFormat::kFloat16X4: + out = wgpu::VertexFormat::Float16x4; + return true; + case interop::GPUVertexFormat::kFloat32: + out = wgpu::VertexFormat::Float32; + return true; + case interop::GPUVertexFormat::kFloat32X2: + out = wgpu::VertexFormat::Float32x2; + return true; + case interop::GPUVertexFormat::kFloat32X3: + out = wgpu::VertexFormat::Float32x3; + return true; + case interop::GPUVertexFormat::kFloat32X4: + out = wgpu::VertexFormat::Float32x4; + return true; + case interop::GPUVertexFormat::kUint32: + out = wgpu::VertexFormat::Uint32; + return true; + case interop::GPUVertexFormat::kUint32X2: + out = wgpu::VertexFormat::Uint32x2; + return true; + case interop::GPUVertexFormat::kUint32X3: + out = wgpu::VertexFormat::Uint32x3; + return true; + case interop::GPUVertexFormat::kUint32X4: + out = wgpu::VertexFormat::Uint32x4; + return true; + case interop::GPUVertexFormat::kSint32: + out = wgpu::VertexFormat::Sint32; + return true; + case interop::GPUVertexFormat::kSint32X2: + out = wgpu::VertexFormat::Sint32x2; + return true; + case interop::GPUVertexFormat::kSint32X3: + out = wgpu::VertexFormat::Sint32x3; + return true; + case interop::GPUVertexFormat::kSint32X4: + out = wgpu::VertexFormat::Sint32x4; + return true; + default: + break; + } + Napi::Error::New(env, "invalid value for GPUVertexFormat").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::RenderPassColorAttachment& out, + const interop::GPURenderPassColorAttachment& in) { + out = {}; + if (auto* op = std::get_if(&in.loadValue)) { + if (!Convert(out.loadOp, *op)) { + return false; + } + } else if (auto* color = std::get_if(&in.loadValue)) { + out.loadOp = wgpu::LoadOp::Clear; + if (!Convert(out.clearColor, *color)) { + return false; + } + } else { + Napi::Error::New(env, "invalid value for GPURenderPassColorAttachment.loadValue") + .ThrowAsJavaScriptException(); + return false; + } + + return Convert(out.view, in.view) && Convert(out.resolveTarget, in.resolveTarget) && + Convert(out.storeOp, in.storeOp); + } + + bool Converter::Convert(wgpu::RenderPassDepthStencilAttachment& out, + const interop::GPURenderPassDepthStencilAttachment& in) { + out = {}; + if (auto* op = std::get_if(&in.depthLoadValue)) { + if (!Convert(out.depthLoadOp, *op)) { + return false; + } + } else if (auto* value = std::get_if(&in.depthLoadValue)) { + out.stencilLoadOp = wgpu::LoadOp::Clear; + if (!Convert(out.clearDepth, *value)) { + return false; + } + } else { + Napi::Error::New(env, + "invalid value for GPURenderPassDepthStencilAttachment.depthLoadValue") + .ThrowAsJavaScriptException(); + return false; + } + + if (auto* op = std::get_if(&in.stencilLoadValue)) { + if (!Convert(out.stencilLoadOp, *op)) { + return false; + } + } else if (auto* value = std::get_if(&in.stencilLoadValue)) { + if (!Convert(out.clearStencil, *value)) { + return false; + } + } else { + Napi::Error::New(env, + "invalid value for " + "GPURenderPassDepthStencilAttachment.stencilLoadValue") + .ThrowAsJavaScriptException(); + return false; + } + + return Convert(out.view, in.view) && Convert(out.depthStoreOp, in.depthStoreOp) && + Convert(out.depthReadOnly, in.depthReadOnly) && + Convert(out.stencilStoreOp, in.stencilStoreOp) && + Convert(out.stencilReadOnly, in.stencilReadOnly); + } + + bool Converter::Convert(wgpu::LoadOp& out, const interop::GPULoadOp& in) { + out = wgpu::LoadOp::Clear; + switch (in) { + case interop::GPULoadOp::kLoad: + out = wgpu::LoadOp::Load; + return true; + } + Napi::Error::New(env, "invalid value for GPULoadOp").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::StoreOp& out, const interop::GPUStoreOp& in) { + out = wgpu::StoreOp::Store; + switch (in) { + case interop::GPUStoreOp::kStore: + out = wgpu::StoreOp::Store; + return true; + case interop::GPUStoreOp::kDiscard: + out = wgpu::StoreOp::Discard; + return true; + } + Napi::Error::New(env, "invalid value for GPUStoreOp").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::BindGroupEntry& out, const interop::GPUBindGroupEntry& in) { + out = {}; + if (!Convert(out.binding, in.binding)) { + return false; + } + + if (auto* res = std::get_if>(&in.resource)) { + return Convert(out.sampler, *res); + } + if (auto* res = std::get_if>(&in.resource)) { + return Convert(out.textureView, *res); + } + if (auto* res = std::get_if(&in.resource)) { + auto buffer = res->buffer.As(); + out.size = wgpu::kWholeSize; + if (!buffer || !Convert(out.offset, res->offset) || !Convert(out.size, res->size)) { + return false; + } + out.buffer = *buffer; + return true; + } + if (auto* res = + std::get_if>(&in.resource)) { + // TODO(crbug.com/dawn/1129): External textures + UNIMPLEMENTED(); + } + Napi::Error::New(env, "invalid value for GPUBindGroupEntry.resource") + .ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::BindGroupLayoutEntry& out, + const interop::GPUBindGroupLayoutEntry& in) { + // TODO(crbug.com/dawn/1129): External textures + return Convert(out.binding, in.binding) && Convert(out.visibility, in.visibility) && + Convert(out.buffer, in.buffer) && Convert(out.sampler, in.sampler) && + Convert(out.texture, in.texture) && Convert(out.storageTexture, in.storageTexture); + } + + bool Converter::Convert(wgpu::BufferBindingLayout& out, + const interop::GPUBufferBindingLayout& in) { + return Convert(out.type, in.type) && Convert(out.hasDynamicOffset, in.hasDynamicOffset) && + Convert(out.minBindingSize, in.minBindingSize); + } + + bool Converter::Convert(wgpu::SamplerBindingLayout& out, + const interop::GPUSamplerBindingLayout& in) { + return Convert(out.type, in.type); + } + + bool Converter::Convert(wgpu::TextureBindingLayout& out, + const interop::GPUTextureBindingLayout& in) { + return Convert(out.sampleType, in.sampleType) && + Convert(out.viewDimension, in.viewDimension) && + Convert(out.multisampled, in.multisampled); + } + + bool Converter::Convert(wgpu::StorageTextureBindingLayout& out, + const interop::GPUStorageTextureBindingLayout& in) { + return Convert(out.access, in.access) && Convert(out.format, in.format) && + Convert(out.viewDimension, in.viewDimension); + } + + bool Converter::Convert(wgpu::BufferBindingType& out, const interop::GPUBufferBindingType& in) { + out = wgpu::BufferBindingType::Undefined; + switch (in) { + case interop::GPUBufferBindingType::kUniform: + out = wgpu::BufferBindingType::Uniform; + return true; + case interop::GPUBufferBindingType::kStorage: + out = wgpu::BufferBindingType::Storage; + return true; + case interop::GPUBufferBindingType::kReadOnlyStorage: + out = wgpu::BufferBindingType::ReadOnlyStorage; + return true; + } + Napi::Error::New(env, "invalid value for GPUBufferBindingType") + .ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::TextureSampleType& out, const interop::GPUTextureSampleType& in) { + out = wgpu::TextureSampleType::Undefined; + switch (in) { + case interop::GPUTextureSampleType::kFloat: + out = wgpu::TextureSampleType::Float; + return true; + case interop::GPUTextureSampleType::kUnfilterableFloat: + out = wgpu::TextureSampleType::UnfilterableFloat; + return true; + case interop::GPUTextureSampleType::kDepth: + out = wgpu::TextureSampleType::Depth; + return true; + case interop::GPUTextureSampleType::kSint: + out = wgpu::TextureSampleType::Sint; + return true; + case interop::GPUTextureSampleType::kUint: + out = wgpu::TextureSampleType::Uint; + return true; + } + Napi::Error::New(env, "invalid value for GPUTextureSampleType") + .ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::SamplerBindingType& out, + const interop::GPUSamplerBindingType& in) { + out = wgpu::SamplerBindingType::Undefined; + switch (in) { + case interop::GPUSamplerBindingType::kFiltering: + out = wgpu::SamplerBindingType::Filtering; + return true; + case interop::GPUSamplerBindingType::kNonFiltering: + out = wgpu::SamplerBindingType::NonFiltering; + return true; + case interop::GPUSamplerBindingType::kComparison: + out = wgpu::SamplerBindingType::Comparison; + return true; + } + Napi::Error::New(env, "invalid value for GPUSamplerBindingType") + .ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::StorageTextureAccess& out, + const interop::GPUStorageTextureAccess& in) { + out = wgpu::StorageTextureAccess::Undefined; + switch (in) { + case interop::GPUStorageTextureAccess::kWriteOnly: + out = wgpu::StorageTextureAccess::WriteOnly; + return true; + } + Napi::Error::New(env, "invalid value for GPUStorageTextureAccess") + .ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::QueryType& out, const interop::GPUQueryType& in) { + out = wgpu::QueryType::Occlusion; + switch (in) { + case interop::GPUQueryType::kOcclusion: + out = wgpu::QueryType::Occlusion; + return true; + case interop::GPUQueryType::kPipelineStatistics: + out = wgpu::QueryType::PipelineStatistics; + return true; + case interop::GPUQueryType::kTimestamp: + out = wgpu::QueryType::Timestamp; + return true; + } + Napi::Error::New(env, "invalid value for GPUQueryType").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::PipelineStatisticName& out, + const interop::GPUPipelineStatisticName& in) { + out = wgpu::PipelineStatisticName::VertexShaderInvocations; + switch (in) { + case interop::GPUPipelineStatisticName::kVertexShaderInvocations: + out = wgpu::PipelineStatisticName::VertexShaderInvocations; + return true; + case interop::GPUPipelineStatisticName::kClipperInvocations: + out = wgpu::PipelineStatisticName::ClipperInvocations; + return true; + case interop::GPUPipelineStatisticName::kClipperPrimitivesOut: + out = wgpu::PipelineStatisticName::ClipperPrimitivesOut; + return true; + case interop::GPUPipelineStatisticName::kFragmentShaderInvocations: + out = wgpu::PipelineStatisticName::FragmentShaderInvocations; + return true; + case interop::GPUPipelineStatisticName::kComputeShaderInvocations: + out = wgpu::PipelineStatisticName::ComputeShaderInvocations; + return true; + } + Napi::Error::New(env, "invalid value for GPUPipelineStatisticName") + .ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::AddressMode& out, const interop::GPUAddressMode& in) { + out = wgpu::AddressMode::Repeat; + switch (in) { + case interop::GPUAddressMode::kClampToEdge: + out = wgpu::AddressMode::ClampToEdge; + return true; + case interop::GPUAddressMode::kRepeat: + out = wgpu::AddressMode::Repeat; + return true; + case interop::GPUAddressMode::kMirrorRepeat: + out = wgpu::AddressMode::MirrorRepeat; + return true; + } + Napi::Error::New(env, "invalid value for GPUAddressMode").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::FilterMode& out, const interop::GPUFilterMode& in) { + out = wgpu::FilterMode::Nearest; + switch (in) { + case interop::GPUFilterMode::kNearest: + out = wgpu::FilterMode::Nearest; + return true; + case interop::GPUFilterMode::kLinear: + out = wgpu::FilterMode::Linear; + return true; + } + Napi::Error::New(env, "invalid value for GPUFilterMode").ThrowAsJavaScriptException(); + return false; + } + + bool Converter::Convert(wgpu::ComputePipelineDescriptor& out, + const interop::GPUComputePipelineDescriptor& in) { + return Convert(out.label, in.label) && // + Convert(out.layout, in.layout) && // + Convert(out.compute, in.compute); + } + + bool Converter::Convert(wgpu::RenderPipelineDescriptor& out, + const interop::GPURenderPipelineDescriptor& in) { + wgpu::RenderPipelineDescriptor desc{}; + return Convert(out.label, in.label) && // + Convert(out.layout, in.layout) && // + Convert(out.vertex, in.vertex) && // + Convert(out.primitive, in.primitive) && // + Convert(out.depthStencil, in.depthStencil) && // + Convert(out.multisample, in.multisample) && Convert(out.fragment, in.fragment); + } + +}} // namespace wgpu::binding diff --git a/src/dawn_node/binding/Converter.h b/src/dawn_node/binding/Converter.h new file mode 100644 index 0000000000..812d405590 --- /dev/null +++ b/src/dawn_node/binding/Converter.h @@ -0,0 +1,357 @@ +// Copyright 2021 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWN_NODE_BINDING_CONVERTER_H_ +#define DAWN_NODE_BINDING_CONVERTER_H_ + +#include +#include + +#include "dawn/webgpu_cpp.h" +#include "dawn_native/DawnNative.h" +#include "napi.h" +#include "src/dawn_node/binding/Errors.h" +#include "src/dawn_node/interop/WebGPU.h" + +namespace wgpu { namespace binding { + + // ImplOfTraits is a traits helper that is used to associate the interop interface type to the + // binding implementation type. + template + struct ImplOfTraits {}; + + // DECLARE_IMPL() is a macro that declares a specialization of ImplOfTraits so that + // `typename ImplOfTraits::type` is equivalent to `binding::NAME`. +#define DECLARE_IMPL(NAME) \ + class NAME; \ + template <> \ + struct ImplOfTraits { \ + using type = binding::NAME; \ + } + + // Declare the interop interface to binding implementations + DECLARE_IMPL(GPUBindGroup); + DECLARE_IMPL(GPUBindGroupLayout); + DECLARE_IMPL(GPUBuffer); + DECLARE_IMPL(GPUPipelineLayout); + DECLARE_IMPL(GPUQuerySet); + DECLARE_IMPL(GPURenderBundle); + DECLARE_IMPL(GPURenderPipeline); + DECLARE_IMPL(GPUSampler); + DECLARE_IMPL(GPUShaderModule); + DECLARE_IMPL(GPUTexture); + DECLARE_IMPL(GPUTextureView); +#undef DECLARE_IMPL + + // Helper for obtaining the binding implementation type from the interop interface type + template + using ImplOf = typename ImplOfTraits::type; + + // Converter is a utility class for converting IDL generated interop types into Dawn types. + // As the Dawn C++ API uses raw C pointers for a number of its interfaces, Converter performs + // heap allocations for conversions of vector or optional types. These pointers are + // automatically freed when the Converter is destructed. + class Converter { + public: + Converter(Napi::Env e) : env(e) { + } + ~Converter(); + + // Conversion function. Converts the interop type IN to the Dawn type OUT. + // Returns true on success, false on failure. + template + [[nodiscard]] inline bool operator()(OUT&& out, IN&& in) { + return Convert(std::forward(out), std::forward(in)); + } + + // Vector conversion function. Converts the vector of interop type IN to a pointer of + // elements of Dawn type OUT, which is assigned to 'out_els'. + // out_count is assigned the number of elements in 'in'. + // Returns true on success, false on failure. + // The pointer assigned to 'out_els' is valid until the Converter is destructed. + template + [[nodiscard]] inline bool operator()(OUT*& out_els, + uint32_t& out_count, + const std::vector& in) { + return Convert(out_els, out_count, in); + } + + // Returns the Env that this Converter was constructed with. + inline Napi::Env Env() const { + return env; + } + + // BufferSource is the converted type of interop::BufferSource. + struct BufferSource { + void* data; + size_t size; + }; + + private: + // Below are the various overloads of Convert() used to convert the interop -> Dawn types. + [[nodiscard]] bool Convert(wgpu::Extent3D& out, const interop::GPUExtent3D& in); + + [[nodiscard]] bool Convert(wgpu::Origin3D& out, const interop::GPUOrigin3DDict& in); + + [[nodiscard]] bool Convert(wgpu::Color& out, const interop::GPUColor& in); + + [[nodiscard]] bool Convert(wgpu::Origin3D& out, + const std::vector& in); + + [[nodiscard]] bool Convert(wgpu::TextureAspect& out, const interop::GPUTextureAspect& in); + + [[nodiscard]] bool Convert(wgpu::ImageCopyTexture& out, + const interop::GPUImageCopyTexture& in); + + [[nodiscard]] bool Convert(wgpu::ImageCopyBuffer& out, + const interop::GPUImageCopyBuffer& in); + + [[nodiscard]] bool Convert(BufferSource& out, interop::BufferSource in); + + [[nodiscard]] bool Convert(wgpu::TextureDataLayout& out, + const interop::GPUImageDataLayout& in); + + [[nodiscard]] bool Convert(wgpu::TextureFormat& out, const interop::GPUTextureFormat& in); + + [[nodiscard]] bool Convert(wgpu::TextureUsage& out, + const interop::GPUTextureUsageFlags& in); + + [[nodiscard]] bool Convert(wgpu::ColorWriteMask& out, + const interop::GPUColorWriteFlags& in); + + [[nodiscard]] bool Convert(wgpu::BufferUsage& out, const interop::GPUBufferUsageFlags& in); + + [[nodiscard]] bool Convert(wgpu::MapMode& out, const interop::GPUMapModeFlags& in); + + [[nodiscard]] bool Convert(wgpu::ShaderStage& out, const interop::GPUShaderStageFlags& in); + + [[nodiscard]] bool Convert(wgpu::TextureDimension& out, + const interop::GPUTextureDimension& in); + + [[nodiscard]] bool Convert(wgpu::TextureViewDimension& out, + const interop::GPUTextureViewDimension& in); + + [[nodiscard]] bool Convert(wgpu::ProgrammableStageDescriptor& out, + const interop::GPUProgrammableStage& in); + + [[nodiscard]] bool Convert(wgpu::BlendComponent& out, const interop::GPUBlendComponent& in); + + [[nodiscard]] bool Convert(wgpu::BlendFactor& out, const interop::GPUBlendFactor& in); + + [[nodiscard]] bool Convert(wgpu::BlendOperation& out, const interop::GPUBlendOperation& in); + + [[nodiscard]] bool Convert(wgpu::BlendState& out, const interop::GPUBlendState& in); + + [[nodiscard]] bool Convert(wgpu::PrimitiveState& out, const interop::GPUPrimitiveState& in); + + [[nodiscard]] bool Convert(wgpu::ColorTargetState& out, + const interop::GPUColorTargetState& in); + + [[nodiscard]] bool Convert(wgpu::DepthStencilState& out, + const interop::GPUDepthStencilState& in); + + [[nodiscard]] bool Convert(wgpu::MultisampleState& out, + const interop::GPUMultisampleState& in); + + [[nodiscard]] bool Convert(wgpu::FragmentState& out, const interop::GPUFragmentState& in); + + [[nodiscard]] bool Convert(wgpu::PrimitiveTopology& out, + const interop::GPUPrimitiveTopology& in); + + [[nodiscard]] bool Convert(wgpu::FrontFace& out, const interop::GPUFrontFace& in); + + [[nodiscard]] bool Convert(wgpu::CullMode& out, const interop::GPUCullMode& in); + + [[nodiscard]] bool Convert(wgpu::CompareFunction& out, + const interop::GPUCompareFunction& in); + + [[nodiscard]] bool Convert(wgpu::IndexFormat& out, const interop::GPUIndexFormat& in); + + [[nodiscard]] bool Convert(wgpu::StencilOperation& out, + const interop::GPUStencilOperation& in); + + [[nodiscard]] bool Convert(wgpu::StencilFaceState& out, + const interop::GPUStencilFaceState& in); + + [[nodiscard]] bool Convert(wgpu::VertexState& out, const interop::GPUVertexState& in); + + [[nodiscard]] bool Convert(wgpu::VertexBufferLayout& out, + const interop::GPUVertexBufferLayout& in); + + [[nodiscard]] bool Convert(wgpu::VertexStepMode& out, const interop::GPUVertexStepMode& in); + + [[nodiscard]] bool Convert(wgpu::VertexAttribute& out, + const interop::GPUVertexAttribute& in); + + [[nodiscard]] bool Convert(wgpu::VertexFormat& out, const interop::GPUVertexFormat& in); + + [[nodiscard]] bool Convert(wgpu::RenderPassColorAttachment& out, + const interop::GPURenderPassColorAttachment& in); + + [[nodiscard]] bool Convert(wgpu::RenderPassDepthStencilAttachment& out, + const interop::GPURenderPassDepthStencilAttachment& in); + + [[nodiscard]] bool Convert(wgpu::LoadOp& out, const interop::GPULoadOp& in); + + [[nodiscard]] bool Convert(wgpu::StoreOp& out, const interop::GPUStoreOp& in); + + [[nodiscard]] bool Convert(wgpu::BindGroupEntry& out, const interop::GPUBindGroupEntry& in); + + [[nodiscard]] bool Convert(wgpu::BindGroupLayoutEntry& out, + const interop::GPUBindGroupLayoutEntry& in); + + [[nodiscard]] bool Convert(wgpu::BufferBindingLayout& out, + const interop::GPUBufferBindingLayout& in); + + [[nodiscard]] bool Convert(wgpu::SamplerBindingLayout& out, + const interop::GPUSamplerBindingLayout& in); + + [[nodiscard]] bool Convert(wgpu::TextureBindingLayout& out, + const interop::GPUTextureBindingLayout& in); + + [[nodiscard]] bool Convert(wgpu::StorageTextureBindingLayout& out, + const interop::GPUStorageTextureBindingLayout& in); + + [[nodiscard]] bool Convert(wgpu::BufferBindingType& out, + const interop::GPUBufferBindingType& in); + + [[nodiscard]] bool Convert(wgpu::SamplerBindingType& out, + const interop::GPUSamplerBindingType& in); + + [[nodiscard]] bool Convert(wgpu::TextureSampleType& out, + const interop::GPUTextureSampleType& in); + + [[nodiscard]] bool Convert(wgpu::StorageTextureAccess& out, + const interop::GPUStorageTextureAccess& in); + + [[nodiscard]] bool Convert(wgpu::QueryType& out, const interop::GPUQueryType& in); + + [[nodiscard]] bool Convert(wgpu::PipelineStatisticName& out, + const interop::GPUPipelineStatisticName& in); + + [[nodiscard]] bool Convert(wgpu::AddressMode& out, const interop::GPUAddressMode& in); + + [[nodiscard]] bool Convert(wgpu::FilterMode& out, const interop::GPUFilterMode& in); + + [[nodiscard]] bool Convert(wgpu::ComputePipelineDescriptor& out, + const interop::GPUComputePipelineDescriptor& in); + + [[nodiscard]] bool Convert(wgpu::RenderPipelineDescriptor& out, + const interop::GPURenderPipelineDescriptor& in); + + // std::string to C string + inline bool Convert(const char*& out, const std::string& in) { + out = in.c_str(); + return true; + } + + // Pass-through (no conversion) + template + inline bool Convert(T& out, const T& in) { + out = in; + return true; + } + + // Integral number conversion, with dynamic limit checking + template && std::is_integral_v>> + inline bool Convert(OUT& out, const IN& in) { + out = static_cast(in); + if (static_cast(out) != in) { + Napi::Error::New(env, "Integer value (" + std::to_string(in) + + ") cannot be converted to the Dawn data type without " + "truncation of the value") + .ThrowAsJavaScriptException(); + return false; + } + return true; + } + + template + inline bool Convert(OUT& out, const std::variant& in) { + return std::visit([&](auto&& i) { return Convert(out, i); }, in); + } + + // If the std::optional does not have a value, then Convert() simply returns true and 'out' + // is not assigned a new value. + template + inline bool Convert(OUT& out, const std::optional& in) { + if (in.has_value()) { + return Convert(out, in.value()); + } + return true; + } + + // std::optional -> T* + // OUT* is assigned either a pointer to the converted value, or nullptr, depending on + // whether 'in' has a value. + template >> + inline bool Convert(OUT*& out, const std::optional& in) { + if (in.has_value()) { + auto* el = Allocate>(); + if (!Convert(*el, in.value())) { + return false; + } + out = el; + } else { + out = nullptr; + } + return true; + } + + // interop::Interface -> Dawn object + template + inline bool Convert(OUT& out, const interop::Interface& in) { + using Impl = ImplOf; + out = *in.template As(); + if (!out) { + LOG("Dawn object has been destroyed. This should not happen"); + return false; + } + return true; + } + + // vector -> raw pointer + count + template + inline bool Convert(OUT*& out_els, uint32_t& out_count, const std::vector& in) { + auto* els = Allocate>(in.size()); + for (size_t i = 0; i < in.size(); i++) { + if (!Convert(els[i], in[i])) { + return false; + } + } + out_els = els; + return Convert(out_count, in.size()); + } + + Napi::Env env; + + // Allocate() allocates and constructs an array of 'n' elements, and returns a pointer to + // the first element. The array is freed when the Converter is destructed. + template + T* Allocate(size_t n = 1) { + auto* ptr = new T[n]{}; + free_.emplace_back([ptr] { delete[] ptr; }); + return ptr; + } + + std::vector> free_; + }; + +}} // namespace wgpu::binding + +#endif // DAWN_NODE_BINDING_CONVERTER_H_