// 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 { public: bool has(Napi::Env, std::string feature) override { UNIMPLEMENTED(); } }; 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