From 126dc7d20fc092a73bf79c1644ba8a1c5a99edba Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Tue, 28 Sep 2021 21:51:28 +0000 Subject: [PATCH] dawn_node: Add binding/GPUBuffer.cpp Bug: dawn:1123 Change-Id: I4ba8d69bcd91a1fa0ed43a5ca063c7ff14d7d031 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/64918 Reviewed-by: Corentin Wallez Reviewed-by: Austin Eng Commit-Queue: Ben Clayton --- src/dawn_node/binding/CMakeLists.txt | 1 + src/dawn_node/binding/GPUBuffer.cpp | 167 +++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 src/dawn_node/binding/GPUBuffer.cpp diff --git a/src/dawn_node/binding/CMakeLists.txt b/src/dawn_node/binding/CMakeLists.txt index ea553c263d..bb5e64bee2 100644 --- a/src/dawn_node/binding/CMakeLists.txt +++ b/src/dawn_node/binding/CMakeLists.txt @@ -27,6 +27,7 @@ add_library(dawn_node_binding STATIC "GPUBindGroup.h" "GPUBindGroupLayout.cpp" "GPUBindGroupLayout.h" + "GPUBuffer.cpp" "GPUBuffer.h" "GPUCommandBuffer.cpp" "GPUCommandBuffer.h" diff --git a/src/dawn_node/binding/GPUBuffer.cpp b/src/dawn_node/binding/GPUBuffer.cpp new file mode 100644 index 0000000000..7b74bebbbb --- /dev/null +++ b/src/dawn_node/binding/GPUBuffer.cpp @@ -0,0 +1,167 @@ +// 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/GPUBuffer.h" + +#include + +#include "src/dawn_node/binding/Converter.h" +#include "src/dawn_node/binding/Errors.h" +#include "src/dawn_node/utils/Debug.h" + +namespace wgpu { namespace binding { + + //////////////////////////////////////////////////////////////////////////////// + // wgpu::bindings::GPUBuffer + // TODO(crbug.com/dawn/1134): We may be doing more validation here than necessary. Once CTS is + // robustly passing, pull out validation and see what / if breaks. + //////////////////////////////////////////////////////////////////////////////// + GPUBuffer::GPUBuffer(wgpu::Buffer buffer, + wgpu::BufferDescriptor desc, + wgpu::Device device, + std::shared_ptr async) + : buffer_(std::move(buffer)), + desc_(desc), + device_(std::move(device)), + async_(std::move(async)) { + if (desc.mappedAtCreation) { + state_ = State::MappedAtCreation; + } + } + + interop::Promise GPUBuffer::mapAsync(Napi::Env env, + interop::GPUMapModeFlags mode, + std::optional offset, + std::optional size) { + wgpu::MapMode md{}; + Converter conv(env); + if (!conv(md, mode)) { + return {env}; + } + + if (state_ != State::Unmapped) { + interop::Promise promise(env); + promise.Reject(Errors::OperationError(env)); + device_.InjectError(wgpu::ErrorType::Validation, + "mapAsync called on buffer that is not in the unmapped state"); + return promise; + } + + struct Context { + Napi::Env env; + interop::Promise promise; + AsyncTask task; + State& state; + }; + auto ctx = new Context{env, interop::Promise(env), async_, state_}; + auto promise = ctx->promise; + + uint64_t o = offset.has_value() ? offset.value() : 0; + uint64_t s = size.has_value() ? size.value() : (desc_.size - o); + + state_ = State::MappingPending; + + buffer_.MapAsync( + md, o, s, + [](WGPUBufferMapAsyncStatus status, void* userdata) { + auto c = std::unique_ptr(static_cast(userdata)); + + c->state = State::Unmapped; + switch (status) { + case WGPUBufferMapAsyncStatus::WGPUBufferMapAsyncStatus_Success: + c->promise.Resolve(); + c->state = State::Mapped; + break; + case WGPUBufferMapAsyncStatus::WGPUBufferMapAsyncStatus_Error: + c->promise.Reject(Errors::OperationError(c->env)); + break; + case WGPUBufferMapAsyncStatus::WGPUBufferMapAsyncStatus_UnmappedBeforeCallback: + c->promise.Reject(Errors::AbortError(c->env)); + break; + case WGPUBufferMapAsyncStatus::WGPUBufferMapAsyncStatus_Unknown: + case WGPUBufferMapAsyncStatus::WGPUBufferMapAsyncStatus_DeviceLost: + case WGPUBufferMapAsyncStatus::WGPUBufferMapAsyncStatus_DestroyedBeforeCallback: + // TODO: The spec is a bit vague around what the promise should do + // here. + c->promise.Reject(Errors::UnknownError(c->env)); + break; + } + }, + ctx); + + return promise; + } + + interop::ArrayBuffer GPUBuffer::getMappedRange(Napi::Env env, + std::optional offset, + std::optional size) { + if (state_ != State::Mapped && state_ != State::MappedAtCreation) { + Errors::OperationError(env).ThrowAsJavaScriptException(); + return {}; + } + + uint64_t o = offset.has_value() ? offset.value() : 0; + uint64_t s = size.has_value() ? size.value() : (desc_.size - o); + + uint64_t start = o; + uint64_t end = o + s; + for (auto& mapping : mapped_) { + if (mapping.Intersects(start, end)) { + Errors::OperationError(env).ThrowAsJavaScriptException(); + return {}; + } + } + + auto* ptr = (desc_.usage & wgpu::BufferUsage::MapWrite) + ? buffer_.GetMappedRange(o, s) + : const_cast(buffer_.GetConstMappedRange(o, s)); + if (!ptr) { + Errors::OperationError(env).ThrowAsJavaScriptException(); + return {}; + } + auto array_buffer = Napi::ArrayBuffer::New(env, ptr, s); + // TODO(crbug.com/dawn/1135): Ownership here is the wrong way around. + mapped_.emplace_back(Mapping{start, end, Napi::Persistent(array_buffer)}); + return array_buffer; + } + + void GPUBuffer::unmap(Napi::Env env) { + if (state_ == State::Destroyed) { + device_.InjectError(wgpu::ErrorType::Validation, + "unmap() called on a destroyed buffer"); + return; + } + + for (auto& mapping : mapped_) { + mapping.buffer.Value().Detach(); + } + mapped_.clear(); + buffer_.Unmap(); + state_ = State::Unmapped; + } + + void GPUBuffer::destroy(Napi::Env) { + buffer_.Destroy(); + state_ = State::Destroyed; + } + + std::optional GPUBuffer::getLabel(Napi::Env) { + UNIMPLEMENTED(); + } + + void GPUBuffer::setLabel(Napi::Env, std::optional value) { + UNIMPLEMENTED(); + } + +}} // namespace wgpu::binding