From bc207f71937b2bc99d36fbbaa38452c36706f87e Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Wed, 29 Sep 2021 08:48:43 +0000 Subject: [PATCH] dawn_node: Add binding/GPUDevice.cpp Bug: dawn:1123 Change-Id: Ied4baca4d5eea41ad13ee488978a8f2354d5f8d0 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/64919 Commit-Queue: Ben Clayton Reviewed-by: Austin Eng --- src/dawn_node/binding/CMakeLists.txt | 1 + src/dawn_node/binding/GPUDevice.cpp | 510 +++++++++++++++++++++++++++ 2 files changed, 511 insertions(+) create mode 100644 src/dawn_node/binding/GPUDevice.cpp diff --git a/src/dawn_node/binding/CMakeLists.txt b/src/dawn_node/binding/CMakeLists.txt index bb5e64bee2..b160644cf8 100644 --- a/src/dawn_node/binding/CMakeLists.txt +++ b/src/dawn_node/binding/CMakeLists.txt @@ -37,6 +37,7 @@ add_library(dawn_node_binding STATIC "GPUComputePassEncoder.h" "GPUComputePipeline.cpp" "GPUComputePipeline.h" + "GPUDevice.cpp" "GPUDevice.h" "GPUPipelineLayout.cpp" "GPUPipelineLayout.h" diff --git a/src/dawn_node/binding/GPUDevice.cpp b/src/dawn_node/binding/GPUDevice.cpp new file mode 100644 index 0000000000..d87da51c94 --- /dev/null +++ b/src/dawn_node/binding/GPUDevice.cpp @@ -0,0 +1,510 @@ +// 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/GPUDevice.h" + +#include + +#include "src/dawn_node/binding/Converter.h" +#include "src/dawn_node/binding/Errors.h" +#include "src/dawn_node/binding/GPUBindGroup.h" +#include "src/dawn_node/binding/GPUBindGroupLayout.h" +#include "src/dawn_node/binding/GPUBuffer.h" +#include "src/dawn_node/binding/GPUCommandBuffer.h" +#include "src/dawn_node/binding/GPUCommandEncoder.h" +#include "src/dawn_node/binding/GPUComputePipeline.h" +#include "src/dawn_node/binding/GPUPipelineLayout.h" +#include "src/dawn_node/binding/GPUQuerySet.h" +#include "src/dawn_node/binding/GPUQueue.h" +#include "src/dawn_node/binding/GPURenderBundleEncoder.h" +#include "src/dawn_node/binding/GPURenderPipeline.h" +#include "src/dawn_node/binding/GPUSampler.h" +#include "src/dawn_node/binding/GPUShaderModule.h" +#include "src/dawn_node/binding/GPUSupportedLimits.h" +#include "src/dawn_node/binding/GPUTexture.h" +#include "src/dawn_node/utils/Debug.h" + +namespace wgpu { namespace binding { + + namespace { + + class DeviceLostInfo : public interop::GPUDeviceLostInfo { + public: + DeviceLostInfo(interop::GPUDeviceLostReason reason, std::string message) + : reason_(reason), message_(message) { + } + std::variant getReason(Napi::Env env) override { + return reason_; + } + std::string getMessage(Napi::Env) override { + return message_; + } + + private: + interop::GPUDeviceLostReason reason_; + std::string message_; + }; + + class OOMError : public interop::GPUOutOfMemoryError {}; + class ValidationError : public interop::GPUValidationError { + public: + ValidationError(std::string message) : message_(std::move(message)) { + } + + std::string getMessage(Napi::Env) override { + return message_; + }; + + private: + std::string message_; + }; + + } // namespace + + //////////////////////////////////////////////////////////////////////////////// + // wgpu::bindings::GPUDevice + //////////////////////////////////////////////////////////////////////////////// + GPUDevice::GPUDevice(Napi::Env env, wgpu::Device device) + : env_(env), device_(device), async_(std::make_shared(env, device)) { + device_.SetLoggingCallback( + [](WGPULoggingType type, char const* message, void* userdata) { + std::cout << type << ": " << message << std::endl; + }, + nullptr); + device_.SetUncapturedErrorCallback( + [](WGPUErrorType type, char const* message, void* userdata) { + std::cout << type << ": " << message << std::endl; + }, + nullptr); + + device_.SetDeviceLostCallback( + [](WGPUDeviceLostReason reason, char const* message, void* userdata) { + auto r = interop::GPUDeviceLostReason::kDestroyed; + switch (reason) { + case WGPUDeviceLostReason_Destroyed: + r = interop::GPUDeviceLostReason::kDestroyed; + break; + } + auto* self = static_cast(userdata); + for (auto promise : self->lost_promises_) { + promise.Resolve( + interop::GPUDeviceLostInfo::Create(self->env_, r, message)); + } + }, + this); + } + + GPUDevice::~GPUDevice() { + } + + interop::Interface GPUDevice::getFeatures(Napi::Env env) { + class Features : public interop::GPUSupportedFeatures {}; + return interop::GPUSupportedFeatures::Create(env); + } + + interop::Interface GPUDevice::getLimits(Napi::Env env) { + return interop::GPUSupportedLimits::Create(env); + } + + interop::Interface GPUDevice::getQueue(Napi::Env env) { + // TODO(crbug.com/dawn/1144): Should probably return the same Queue JS object. + return interop::GPUQueue::Create(env, device_.GetQueue(), async_); + } + + void GPUDevice::destroy(Napi::Env) { + device_.Release(); + } + + interop::Interface GPUDevice::createBuffer( + Napi::Env env, + interop::GPUBufferDescriptor descriptor) { + Converter conv(env); + + wgpu::BufferDescriptor desc{}; + if (!conv(desc.label, descriptor.label) || + !conv(desc.mappedAtCreation, descriptor.mappedAtCreation) || + !conv(desc.size, descriptor.size) || !conv(desc.usage, descriptor.usage)) { + return {}; + } + return interop::GPUBuffer::Create(env, device_.CreateBuffer(&desc), desc, + device_, async_); + } + + interop::Interface GPUDevice::createTexture( + Napi::Env env, + interop::GPUTextureDescriptor descriptor) { + Converter conv(env); + + wgpu::TextureDescriptor desc{}; + if (!conv(desc.label, descriptor.label) || !conv(desc.usage, descriptor.usage) || // + !conv(desc.size, descriptor.size) || // + !conv(desc.dimension, descriptor.dimension) || // + !conv(desc.mipLevelCount, descriptor.mipLevelCount) || // + !conv(desc.sampleCount, descriptor.sampleCount) || // + !conv(desc.format, descriptor.format)) { + return {}; + } + return interop::GPUTexture::Create(env, device_.CreateTexture(&desc)); + } + + interop::Interface GPUDevice::createSampler( + Napi::Env env, + std::optional descriptor) { + Converter conv(env); + + if (descriptor.has_value()) { + wgpu::SamplerDescriptor desc{}; + if (!conv(desc.label, descriptor->label) || // + !conv(desc.addressModeU, descriptor->addressModeU) || // + !conv(desc.addressModeV, descriptor->addressModeV) || // + !conv(desc.addressModeW, descriptor->addressModeW) || // + !conv(desc.magFilter, descriptor->magFilter) || // + !conv(desc.minFilter, descriptor->minFilter) || // + !conv(desc.mipmapFilter, descriptor->mipmapFilter) || // + !conv(desc.lodMinClamp, descriptor->lodMinClamp) || // + !conv(desc.lodMaxClamp, descriptor->lodMaxClamp) || // + !conv(desc.compare, descriptor->compare) || // + !conv(desc.maxAnisotropy, descriptor->maxAnisotropy)) { + return {}; + } + return interop::GPUSampler::Create(env, device_.CreateSampler(&desc)); + } + + return interop::GPUSampler::Create(env, device_.CreateSampler()); + } + + interop::Interface GPUDevice::importExternalTexture( + Napi::Env, + interop::GPUExternalTextureDescriptor descriptor) { + UNIMPLEMENTED(); + } + + interop::Interface GPUDevice::createBindGroupLayout( + Napi::Env env, + interop::GPUBindGroupLayoutDescriptor descriptor) { + Converter conv(env); + + wgpu::BindGroupLayoutDescriptor desc{}; + if (!conv(desc.label, descriptor.label) || + !conv(desc.entries, desc.entryCount, descriptor.entries)) { + return {}; + } + + return interop::GPUBindGroupLayout::Create( + env, device_.CreateBindGroupLayout(&desc)); + } + + interop::Interface GPUDevice::createPipelineLayout( + Napi::Env env, + interop::GPUPipelineLayoutDescriptor descriptor) { + Converter conv(env); + + wgpu::PipelineLayoutDescriptor desc{}; + if (!conv(desc.label, descriptor.label) || + !conv(desc.bindGroupLayouts, desc.bindGroupLayoutCount, descriptor.bindGroupLayouts)) { + return {}; + } + + return interop::GPUPipelineLayout::Create( + env, device_.CreatePipelineLayout(&desc)); + } + + interop::Interface GPUDevice::createBindGroup( + Napi::Env env, + interop::GPUBindGroupDescriptor descriptor) { + Converter conv(env); + + wgpu::BindGroupDescriptor desc{}; + if (!conv(desc.label, descriptor.label) || !conv(desc.layout, descriptor.layout) || + !conv(desc.entries, desc.entryCount, descriptor.entries)) { + return {}; + } + + return interop::GPUBindGroup::Create(env, device_.CreateBindGroup(&desc)); + } + + interop::Interface GPUDevice::createShaderModule( + Napi::Env env, + interop::GPUShaderModuleDescriptor descriptor) { + Converter conv(env); + + wgpu::ShaderModuleWGSLDescriptor wgsl_desc{}; + wgpu::ShaderModuleDescriptor sm_desc{}; + if (!conv(wgsl_desc.source, descriptor.code) || !conv(sm_desc.label, descriptor.label)) { + return {}; + } + sm_desc.nextInChain = &wgsl_desc; + + return interop::GPUShaderModule::Create( + env, device_.CreateShaderModule(&sm_desc), async_); + } + + interop::Interface GPUDevice::createComputePipeline( + Napi::Env env, + interop::GPUComputePipelineDescriptor descriptor) { + Converter conv(env); + + wgpu::ComputePipelineDescriptor desc{}; + if (!conv(desc, descriptor)) { + return {}; + } + + return interop::GPUComputePipeline::Create( + env, device_.CreateComputePipeline(&desc)); + } + + interop::Interface GPUDevice::createRenderPipeline( + Napi::Env env, + interop::GPURenderPipelineDescriptor descriptor) { + Converter conv(env); + + wgpu::RenderPipelineDescriptor desc{}; + if (!conv(desc, descriptor)) { + return {}; + } + + return interop::GPURenderPipeline::Create( + env, device_.CreateRenderPipeline(&desc)); + } + + interop::Promise> + GPUDevice::createComputePipelineAsync(Napi::Env env, + interop::GPUComputePipelineDescriptor descriptor) { + Converter conv(env); + + wgpu::ComputePipelineDescriptor desc{}; + if (!conv(desc, descriptor)) { + return {env}; + } + + using Promise = interop::Promise>; + + struct Context { + Napi::Env env; + Promise promise; + AsyncTask task; + }; + auto ctx = new Context{env, env, async_}; + auto promise = ctx->promise; + + device_.CreateComputePipelineAsync( + &desc, + [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline pipeline, + char const* message, void* userdata) { + auto c = std::unique_ptr(static_cast(userdata)); + + switch (status) { + case WGPUCreatePipelineAsyncStatus::WGPUCreatePipelineAsyncStatus_Success: + c->promise.Resolve(interop::GPUComputePipeline::Create( + c->env, pipeline)); + break; + default: + c->promise.Reject(Errors::OperationError(c->env)); + break; + } + }, + ctx); + + return promise; + } + + interop::Promise> + GPUDevice::createRenderPipelineAsync(Napi::Env env, + interop::GPURenderPipelineDescriptor descriptor) { + Converter conv(env); + + wgpu::RenderPipelineDescriptor desc{}; + if (!conv(desc, descriptor)) { + return {env}; + } + + using Promise = interop::Promise>; + + struct Context { + Napi::Env env; + Promise promise; + AsyncTask task; + }; + auto ctx = new Context{env, env, async_}; + auto promise = ctx->promise; + + device_.CreateRenderPipelineAsync( + &desc, + [](WGPUCreatePipelineAsyncStatus status, WGPURenderPipeline pipeline, + char const* message, void* userdata) { + auto c = std::unique_ptr(static_cast(userdata)); + + switch (status) { + case WGPUCreatePipelineAsyncStatus::WGPUCreatePipelineAsyncStatus_Success: + c->promise.Resolve(interop::GPURenderPipeline::Create( + c->env, pipeline)); + break; + default: + c->promise.Reject(Errors::OperationError(c->env)); + break; + } + }, + ctx); + + return promise; + } + + interop::Interface GPUDevice::createCommandEncoder( + Napi::Env env, + std::optional descriptor) { + wgpu::CommandEncoderDescriptor desc{}; + return interop::GPUCommandEncoder::Create( + env, device_.CreateCommandEncoder(&desc)); + } + + interop::Interface GPUDevice::createRenderBundleEncoder( + Napi::Env env, + interop::GPURenderBundleEncoderDescriptor descriptor) { + Converter conv(env); + + wgpu::RenderBundleEncoderDescriptor desc{}; + if (!conv(desc.label, descriptor.label) || + !conv(desc.colorFormats, desc.colorFormatsCount, descriptor.colorFormats) || + !conv(desc.depthStencilFormat, descriptor.depthStencilFormat) || + !conv(desc.sampleCount, descriptor.sampleCount)) { + return {}; + } + + return interop::GPURenderBundleEncoder::Create( + env, device_.CreateRenderBundleEncoder(&desc)); + } + + interop::Interface GPUDevice::createQuerySet( + Napi::Env env, + interop::GPUQuerySetDescriptor descriptor) { + Converter conv(env); + + wgpu::QuerySetDescriptor desc{}; + if (!conv(desc.label, descriptor.label) || !conv(desc.type, descriptor.type) || + !conv(desc.count, descriptor.count) || + !conv(desc.pipelineStatistics, desc.pipelineStatisticsCount, + descriptor.pipelineStatistics)) { + return {}; + } + + return interop::GPUQuerySet::Create(env, device_.CreateQuerySet(&desc)); + } + + interop::Promise> GPUDevice::getLost( + Napi::Env env) { + auto promise = interop::Promise>(env); + lost_promises_.emplace_back(promise); + return promise; + } + + void GPUDevice::pushErrorScope(Napi::Env env, interop::GPUErrorFilter filter) { + wgpu::ErrorFilter f = wgpu::ErrorFilter::None; + switch (filter) { + case interop::GPUErrorFilter::kOutOfMemory: + f = wgpu::ErrorFilter::OutOfMemory; + break; + case interop::GPUErrorFilter::kValidation: + f = wgpu::ErrorFilter::Validation; + break; + default: + Napi::Error::New(env, "unhandled GPUErrorFilter value") + .ThrowAsJavaScriptException(); + return; + } + device_.PushErrorScope(f); + } + + interop::Promise> GPUDevice::popErrorScope(Napi::Env env) { + using Promise = interop::Promise>; + struct Context { + Napi::Env env; + Promise promise; + AsyncTask task; + }; + auto* ctx = new Context{env, env, async_}; + auto promise = ctx->promise; + + bool ok = device_.PopErrorScope( + [](WGPUErrorType type, char const* message, void* userdata) { + auto c = std::unique_ptr(static_cast(userdata)); + auto env = c->env; + switch (type) { + case WGPUErrorType::WGPUErrorType_NoError: + c->promise.Resolve({}); + break; + case WGPUErrorType::WGPUErrorType_OutOfMemory: + c->promise.Resolve(interop::GPUOutOfMemoryError::Create(env)); + break; + case WGPUErrorType::WGPUErrorType_Unknown: + case WGPUErrorType::WGPUErrorType_DeviceLost: + case WGPUErrorType::WGPUErrorType_Validation: + c->promise.Resolve( + interop::GPUValidationError::Create(env, message)); + break; + default: + c->promise.Reject("unhandled error type"); + break; + } + }, + ctx); + + if (ok) { + return promise; + } + + delete ctx; + Promise p(env); + p.Resolve( + interop::GPUValidationError::Create(env, "failed to pop error scope")); + return p; + } + + std::optional GPUDevice::getLabel(Napi::Env) { + UNIMPLEMENTED(); + }; + + void GPUDevice::setLabel(Napi::Env, std::optional value) { + UNIMPLEMENTED(); + }; + + interop::Interface GPUDevice::getOnuncapturederror(Napi::Env) { + UNIMPLEMENTED(); + } + + void GPUDevice::setOnuncapturederror(Napi::Env, + interop::Interface value) { + UNIMPLEMENTED(); + } + + void GPUDevice::addEventListener( + Napi::Env, + std::string type, + std::optional> callback, + std::optional> options) { + UNIMPLEMENTED(); + } + + void GPUDevice::removeEventListener( + Napi::Env, + std::string type, + std::optional> callback, + std::optional> options) { + UNIMPLEMENTED(); + } + + bool GPUDevice::dispatchEvent(Napi::Env, interop::Interface event) { + UNIMPLEMENTED(); + } + +}} // namespace wgpu::binding