1 // Copyright 2021 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "src/dawn_node/binding/GPUBuffer.h" 16 17 #include <memory> 18 19 #include "src/dawn_node/binding/Converter.h" 20 #include "src/dawn_node/binding/Errors.h" 21 #include "src/dawn_node/utils/Debug.h" 22 23 namespace wgpu { namespace binding { 24 25 //////////////////////////////////////////////////////////////////////////////// 26 // wgpu::bindings::GPUBuffer 27 // TODO(crbug.com/dawn/1134): We may be doing more validation here than necessary. Once CTS is 28 // robustly passing, pull out validation and see what / if breaks. 29 //////////////////////////////////////////////////////////////////////////////// GPUBuffer(wgpu::Buffer buffer,wgpu::BufferDescriptor desc,wgpu::Device device,std::shared_ptr<AsyncRunner> async)30 GPUBuffer::GPUBuffer(wgpu::Buffer buffer, 31 wgpu::BufferDescriptor desc, 32 wgpu::Device device, 33 std::shared_ptr<AsyncRunner> async) 34 : buffer_(std::move(buffer)), 35 desc_(desc), 36 device_(std::move(device)), 37 async_(std::move(async)) { 38 if (desc.mappedAtCreation) { 39 state_ = State::MappedAtCreation; 40 } 41 } 42 mapAsync(Napi::Env env,interop::GPUMapModeFlags mode,interop::GPUSize64 offset,std::optional<interop::GPUSize64> size)43 interop::Promise<void> GPUBuffer::mapAsync(Napi::Env env, 44 interop::GPUMapModeFlags mode, 45 interop::GPUSize64 offset, 46 std::optional<interop::GPUSize64> size) { 47 wgpu::MapMode md{}; 48 Converter conv(env); 49 if (!conv(md, mode)) { 50 interop::Promise<void> promise(env, PROMISE_INFO); 51 promise.Reject(Errors::OperationError(env)); 52 return promise; 53 } 54 55 if (state_ != State::Unmapped) { 56 interop::Promise<void> promise(env, PROMISE_INFO); 57 promise.Reject(Errors::OperationError(env)); 58 device_.InjectError(wgpu::ErrorType::Validation, 59 "mapAsync called on buffer that is not in the unmapped state"); 60 return promise; 61 } 62 63 struct Context { 64 Napi::Env env; 65 interop::Promise<void> promise; 66 AsyncTask task; 67 State& state; 68 }; 69 auto ctx = new Context{env, interop::Promise<void>(env, PROMISE_INFO), async_, state_}; 70 auto promise = ctx->promise; 71 72 uint64_t s = size.has_value() ? size.value() : (desc_.size - offset); 73 74 state_ = State::MappingPending; 75 76 buffer_.MapAsync( 77 md, offset, s, 78 [](WGPUBufferMapAsyncStatus status, void* userdata) { 79 auto c = std::unique_ptr<Context>(static_cast<Context*>(userdata)); 80 c->state = State::Unmapped; 81 switch (status) { 82 case WGPUBufferMapAsyncStatus_Force32: 83 UNREACHABLE("WGPUBufferMapAsyncStatus_Force32"); 84 break; 85 case WGPUBufferMapAsyncStatus_Success: 86 c->promise.Resolve(); 87 c->state = State::Mapped; 88 break; 89 case WGPUBufferMapAsyncStatus_Error: 90 c->promise.Reject(Errors::OperationError(c->env)); 91 break; 92 case WGPUBufferMapAsyncStatus_UnmappedBeforeCallback: 93 case WGPUBufferMapAsyncStatus_DestroyedBeforeCallback: 94 c->promise.Reject(Errors::AbortError(c->env)); 95 break; 96 case WGPUBufferMapAsyncStatus_Unknown: 97 case WGPUBufferMapAsyncStatus_DeviceLost: 98 // TODO: The spec is a bit vague around what the promise should do 99 // here. 100 c->promise.Reject(Errors::UnknownError(c->env)); 101 break; 102 } 103 }, 104 ctx); 105 106 return promise; 107 } 108 getMappedRange(Napi::Env env,interop::GPUSize64 offset,std::optional<interop::GPUSize64> size)109 interop::ArrayBuffer GPUBuffer::getMappedRange(Napi::Env env, 110 interop::GPUSize64 offset, 111 std::optional<interop::GPUSize64> size) { 112 if (state_ != State::Mapped && state_ != State::MappedAtCreation) { 113 Errors::OperationError(env).ThrowAsJavaScriptException(); 114 return {}; 115 } 116 117 uint64_t s = size.has_value() ? size.value() : (desc_.size - offset); 118 119 uint64_t start = offset; 120 uint64_t end = offset + s; 121 for (auto& mapping : mapped_) { 122 if (mapping.Intersects(start, end)) { 123 Errors::OperationError(env).ThrowAsJavaScriptException(); 124 return {}; 125 } 126 } 127 128 auto* ptr = (desc_.usage & wgpu::BufferUsage::MapWrite) 129 ? buffer_.GetMappedRange(offset, s) 130 : const_cast<void*>(buffer_.GetConstMappedRange(offset, s)); 131 if (!ptr) { 132 Errors::OperationError(env).ThrowAsJavaScriptException(); 133 return {}; 134 } 135 auto array_buffer = Napi::ArrayBuffer::New(env, ptr, s); 136 // TODO(crbug.com/dawn/1135): Ownership here is the wrong way around. 137 mapped_.emplace_back(Mapping{start, end, Napi::Persistent(array_buffer)}); 138 return array_buffer; 139 } 140 unmap(Napi::Env env)141 void GPUBuffer::unmap(Napi::Env env) { 142 if (state_ == State::Destroyed) { 143 device_.InjectError(wgpu::ErrorType::Validation, 144 "unmap() called on a destroyed buffer"); 145 return; 146 } 147 148 for (auto& mapping : mapped_) { 149 mapping.buffer.Value().Detach(); 150 } 151 mapped_.clear(); 152 buffer_.Unmap(); 153 state_ = State::Unmapped; 154 } 155 destroy(Napi::Env)156 void GPUBuffer::destroy(Napi::Env) { 157 buffer_.Destroy(); 158 state_ = State::Destroyed; 159 } 160 getLabel(Napi::Env)161 std::optional<std::string> GPUBuffer::getLabel(Napi::Env) { 162 UNIMPLEMENTED(); 163 } 164 setLabel(Napi::Env,std::optional<std::string> value)165 void GPUBuffer::setLabel(Napi::Env, std::optional<std::string> value) { 166 UNIMPLEMENTED(); 167 } 168 169 }} // namespace wgpu::binding 170