• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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