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 <bclayton@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_node/binding/GPUDevice.cpp b/src/dawn_node/binding/GPUDevice.cpp
new file mode 100644
index 0000000..d87da51
--- /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 <memory>
+
+#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<interop::GPUDeviceLostReason> 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<AsyncRunner>(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<GPUDevice*>(userdata);
+                for (auto promise : self->lost_promises_) {
+                    promise.Resolve(
+                        interop::GPUDeviceLostInfo::Create<DeviceLostInfo>(self->env_, r, message));
+                }
+            },
+            this);
+    }
+
+    GPUDevice::~GPUDevice() {
+    }
+
+    interop::Interface<interop::GPUSupportedFeatures> GPUDevice::getFeatures(Napi::Env env) {
+        class Features : public interop::GPUSupportedFeatures {};
+        return interop::GPUSupportedFeatures::Create<Features>(env);
+    }
+
+    interop::Interface<interop::GPUSupportedLimits> GPUDevice::getLimits(Napi::Env env) {
+        return interop::GPUSupportedLimits::Create<GPUSupportedLimits>(env);
+    }
+
+    interop::Interface<interop::GPUQueue> GPUDevice::getQueue(Napi::Env env) {
+        // TODO(crbug.com/dawn/1144): Should probably return the same Queue JS object.
+        return interop::GPUQueue::Create<GPUQueue>(env, device_.GetQueue(), async_);
+    }
+
+    void GPUDevice::destroy(Napi::Env) {
+        device_.Release();
+    }
+
+    interop::Interface<interop::GPUBuffer> 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<GPUBuffer>(env, device_.CreateBuffer(&desc), desc,
+                                                     device_, async_);
+    }
+
+    interop::Interface<interop::GPUTexture> 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<GPUTexture>(env, device_.CreateTexture(&desc));
+    }
+
+    interop::Interface<interop::GPUSampler> GPUDevice::createSampler(
+        Napi::Env env,
+        std::optional<interop::GPUSamplerDescriptor> 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<GPUSampler>(env, device_.CreateSampler(&desc));
+        }
+
+        return interop::GPUSampler::Create<GPUSampler>(env, device_.CreateSampler());
+    }
+
+    interop::Interface<interop::GPUExternalTexture> GPUDevice::importExternalTexture(
+        Napi::Env,
+        interop::GPUExternalTextureDescriptor descriptor) {
+        UNIMPLEMENTED();
+    }
+
+    interop::Interface<interop::GPUBindGroupLayout> 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<GPUBindGroupLayout>(
+            env, device_.CreateBindGroupLayout(&desc));
+    }
+
+    interop::Interface<interop::GPUPipelineLayout> 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<GPUPipelineLayout>(
+            env, device_.CreatePipelineLayout(&desc));
+    }
+
+    interop::Interface<interop::GPUBindGroup> 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<GPUBindGroup>(env, device_.CreateBindGroup(&desc));
+    }
+
+    interop::Interface<interop::GPUShaderModule> 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<GPUShaderModule>(
+            env, device_.CreateShaderModule(&sm_desc), async_);
+    }
+
+    interop::Interface<interop::GPUComputePipeline> GPUDevice::createComputePipeline(
+        Napi::Env env,
+        interop::GPUComputePipelineDescriptor descriptor) {
+        Converter conv(env);
+
+        wgpu::ComputePipelineDescriptor desc{};
+        if (!conv(desc, descriptor)) {
+            return {};
+        }
+
+        return interop::GPUComputePipeline::Create<GPUComputePipeline>(
+            env, device_.CreateComputePipeline(&desc));
+    }
+
+    interop::Interface<interop::GPURenderPipeline> GPUDevice::createRenderPipeline(
+        Napi::Env env,
+        interop::GPURenderPipelineDescriptor descriptor) {
+        Converter conv(env);
+
+        wgpu::RenderPipelineDescriptor desc{};
+        if (!conv(desc, descriptor)) {
+            return {};
+        }
+
+        return interop::GPURenderPipeline::Create<GPURenderPipeline>(
+            env, device_.CreateRenderPipeline(&desc));
+    }
+
+    interop::Promise<interop::Interface<interop::GPUComputePipeline>>
+    GPUDevice::createComputePipelineAsync(Napi::Env env,
+                                          interop::GPUComputePipelineDescriptor descriptor) {
+        Converter conv(env);
+
+        wgpu::ComputePipelineDescriptor desc{};
+        if (!conv(desc, descriptor)) {
+            return {env};
+        }
+
+        using Promise = interop::Promise<interop::Interface<interop::GPUComputePipeline>>;
+
+        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<Context>(static_cast<Context*>(userdata));
+
+                switch (status) {
+                    case WGPUCreatePipelineAsyncStatus::WGPUCreatePipelineAsyncStatus_Success:
+                        c->promise.Resolve(interop::GPUComputePipeline::Create<GPUComputePipeline>(
+                            c->env, pipeline));
+                        break;
+                    default:
+                        c->promise.Reject(Errors::OperationError(c->env));
+                        break;
+                }
+            },
+            ctx);
+
+        return promise;
+    }
+
+    interop::Promise<interop::Interface<interop::GPURenderPipeline>>
+    GPUDevice::createRenderPipelineAsync(Napi::Env env,
+                                         interop::GPURenderPipelineDescriptor descriptor) {
+        Converter conv(env);
+
+        wgpu::RenderPipelineDescriptor desc{};
+        if (!conv(desc, descriptor)) {
+            return {env};
+        }
+
+        using Promise = interop::Promise<interop::Interface<interop::GPURenderPipeline>>;
+
+        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<Context>(static_cast<Context*>(userdata));
+
+                switch (status) {
+                    case WGPUCreatePipelineAsyncStatus::WGPUCreatePipelineAsyncStatus_Success:
+                        c->promise.Resolve(interop::GPURenderPipeline::Create<GPURenderPipeline>(
+                            c->env, pipeline));
+                        break;
+                    default:
+                        c->promise.Reject(Errors::OperationError(c->env));
+                        break;
+                }
+            },
+            ctx);
+
+        return promise;
+    }
+
+    interop::Interface<interop::GPUCommandEncoder> GPUDevice::createCommandEncoder(
+        Napi::Env env,
+        std::optional<interop::GPUCommandEncoderDescriptor> descriptor) {
+        wgpu::CommandEncoderDescriptor desc{};
+        return interop::GPUCommandEncoder::Create<GPUCommandEncoder>(
+            env, device_.CreateCommandEncoder(&desc));
+    }
+
+    interop::Interface<interop::GPURenderBundleEncoder> 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<GPURenderBundleEncoder>(
+            env, device_.CreateRenderBundleEncoder(&desc));
+    }
+
+    interop::Interface<interop::GPUQuerySet> 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<GPUQuerySet>(env, device_.CreateQuerySet(&desc));
+    }
+
+    interop::Promise<interop::Interface<interop::GPUDeviceLostInfo>> GPUDevice::getLost(
+        Napi::Env env) {
+        auto promise = interop::Promise<interop::Interface<interop::GPUDeviceLostInfo>>(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<std::optional<interop::GPUError>> GPUDevice::popErrorScope(Napi::Env env) {
+        using Promise = interop::Promise<std::optional<interop::GPUError>>;
+        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<Context>(static_cast<Context*>(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<OOMError>(env));
+                        break;
+                    case WGPUErrorType::WGPUErrorType_Unknown:
+                    case WGPUErrorType::WGPUErrorType_DeviceLost:
+                    case WGPUErrorType::WGPUErrorType_Validation:
+                        c->promise.Resolve(
+                            interop::GPUValidationError::Create<ValidationError>(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<ValidationError>(env, "failed to pop error scope"));
+        return p;
+    }
+
+    std::optional<std::string> GPUDevice::getLabel(Napi::Env) {
+        UNIMPLEMENTED();
+    };
+
+    void GPUDevice::setLabel(Napi::Env, std::optional<std::string> value) {
+        UNIMPLEMENTED();
+    };
+
+    interop::Interface<interop::EventHandler> GPUDevice::getOnuncapturederror(Napi::Env) {
+        UNIMPLEMENTED();
+    }
+
+    void GPUDevice::setOnuncapturederror(Napi::Env,
+                                         interop::Interface<interop::EventHandler> value) {
+        UNIMPLEMENTED();
+    }
+
+    void GPUDevice::addEventListener(
+        Napi::Env,
+        std::string type,
+        std::optional<interop::Interface<interop::EventListener>> callback,
+        std::optional<std::variant<interop::AddEventListenerOptions, bool>> options) {
+        UNIMPLEMENTED();
+    }
+
+    void GPUDevice::removeEventListener(
+        Napi::Env,
+        std::string type,
+        std::optional<interop::Interface<interop::EventListener>> callback,
+        std::optional<std::variant<interop::EventListenerOptions, bool>> options) {
+        UNIMPLEMENTED();
+    }
+
+    bool GPUDevice::dispatchEvent(Napi::Env, interop::Interface<interop::Event> event) {
+        UNIMPLEMENTED();
+    }
+
+}}  // namespace wgpu::binding