• 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/GPUDevice.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/binding/GPUBindGroup.h"
22 #include "src/dawn_node/binding/GPUBindGroupLayout.h"
23 #include "src/dawn_node/binding/GPUBuffer.h"
24 #include "src/dawn_node/binding/GPUCommandBuffer.h"
25 #include "src/dawn_node/binding/GPUCommandEncoder.h"
26 #include "src/dawn_node/binding/GPUComputePipeline.h"
27 #include "src/dawn_node/binding/GPUPipelineLayout.h"
28 #include "src/dawn_node/binding/GPUQuerySet.h"
29 #include "src/dawn_node/binding/GPUQueue.h"
30 #include "src/dawn_node/binding/GPURenderBundleEncoder.h"
31 #include "src/dawn_node/binding/GPURenderPipeline.h"
32 #include "src/dawn_node/binding/GPUSampler.h"
33 #include "src/dawn_node/binding/GPUShaderModule.h"
34 #include "src/dawn_node/binding/GPUSupportedLimits.h"
35 #include "src/dawn_node/binding/GPUTexture.h"
36 #include "src/dawn_node/utils/Debug.h"
37 
38 namespace wgpu { namespace binding {
39 
40     namespace {
41 
42         class DeviceLostInfo : public interop::GPUDeviceLostInfo {
43           public:
DeviceLostInfo(interop::GPUDeviceLostReason reason,std::string message)44             DeviceLostInfo(interop::GPUDeviceLostReason reason, std::string message)
45                 : reason_(reason), message_(message) {
46             }
getReason(Napi::Env env)47             std::variant<interop::GPUDeviceLostReason> getReason(Napi::Env env) override {
48                 return reason_;
49             }
getMessage(Napi::Env)50             std::string getMessage(Napi::Env) override {
51                 return message_;
52             }
53 
54           private:
55             interop::GPUDeviceLostReason reason_;
56             std::string message_;
57         };
58 
59         class OOMError : public interop::GPUOutOfMemoryError {};
60         class ValidationError : public interop::GPUValidationError {
61           public:
ValidationError(std::string message)62             ValidationError(std::string message) : message_(std::move(message)) {
63             }
64 
getMessage(Napi::Env)65             std::string getMessage(Napi::Env) override {
66                 return message_;
67             };
68 
69           private:
70             std::string message_;
71         };
72 
73     }  // namespace
74 
75     ////////////////////////////////////////////////////////////////////////////////
76     // wgpu::bindings::GPUDevice
77     ////////////////////////////////////////////////////////////////////////////////
GPUDevice(Napi::Env env,wgpu::Device device)78     GPUDevice::GPUDevice(Napi::Env env, wgpu::Device device)
79         : env_(env), device_(device), async_(std::make_shared<AsyncRunner>(env, device)) {
80         device_.SetLoggingCallback(
81             [](WGPULoggingType type, char const* message, void* userdata) {
82                 std::cout << type << ": " << message << std::endl;
83             },
84             nullptr);
85         device_.SetUncapturedErrorCallback(
86             [](WGPUErrorType type, char const* message, void* userdata) {
87                 std::cout << type << ": " << message << std::endl;
88             },
89             nullptr);
90 
91         device_.SetDeviceLostCallback(
92             [](WGPUDeviceLostReason reason, char const* message, void* userdata) {
93                 auto r = interop::GPUDeviceLostReason::kDestroyed;
94                 switch (reason) {
95                     case WGPUDeviceLostReason_Force32:
96                         UNREACHABLE("WGPUDeviceLostReason_Force32");
97                         break;
98                     case WGPUDeviceLostReason_Destroyed:
99                     case WGPUDeviceLostReason_Undefined:
100                         r = interop::GPUDeviceLostReason::kDestroyed;
101                         break;
102                 }
103                 auto* self = static_cast<GPUDevice*>(userdata);
104                 for (auto promise : self->lost_promises_) {
105                     promise.Resolve(
106                         interop::GPUDeviceLostInfo::Create<DeviceLostInfo>(self->env_, r, message));
107                 }
108             },
109             this);
110     }
111 
~GPUDevice()112     GPUDevice::~GPUDevice() {
113     }
114 
getFeatures(Napi::Env env)115     interop::Interface<interop::GPUSupportedFeatures> GPUDevice::getFeatures(Napi::Env env) {
116         class Features : public interop::GPUSupportedFeatures {
117           public:
118             bool has(Napi::Env, std::string feature) override {
119                 UNIMPLEMENTED();
120             }
121             std::vector<std::string> keys(Napi::Env) override {
122                 UNIMPLEMENTED();
123             }
124         };
125         return interop::GPUSupportedFeatures::Create<Features>(env);
126     }
127 
getLimits(Napi::Env env)128     interop::Interface<interop::GPUSupportedLimits> GPUDevice::getLimits(Napi::Env env) {
129         wgpu::SupportedLimits limits{};
130         if (!device_.GetLimits(&limits)) {
131             Napi::Error::New(env, "failed to get device limits").ThrowAsJavaScriptException();
132         }
133         return interop::GPUSupportedLimits::Create<GPUSupportedLimits>(env, limits);
134     }
135 
getQueue(Napi::Env env)136     interop::Interface<interop::GPUQueue> GPUDevice::getQueue(Napi::Env env) {
137         // TODO(crbug.com/dawn/1144): Should probably return the same Queue JS object.
138         return interop::GPUQueue::Create<GPUQueue>(env, device_.GetQueue(), async_);
139     }
140 
destroy(Napi::Env env)141     void GPUDevice::destroy(Napi::Env env) {
142         for (auto promise : lost_promises_) {
143             promise.Resolve(interop::GPUDeviceLostInfo::Create<DeviceLostInfo>(
144                 env_, interop::GPUDeviceLostReason::kDestroyed, "device was destroyed"));
145         }
146         lost_promises_.clear();
147         device_.Release();
148     }
149 
createBuffer(Napi::Env env,interop::GPUBufferDescriptor descriptor)150     interop::Interface<interop::GPUBuffer> GPUDevice::createBuffer(
151         Napi::Env env,
152         interop::GPUBufferDescriptor descriptor) {
153         Converter conv(env);
154 
155         wgpu::BufferDescriptor desc{};
156         if (!conv(desc.label, descriptor.label) ||
157             !conv(desc.mappedAtCreation, descriptor.mappedAtCreation) ||
158             !conv(desc.size, descriptor.size) || !conv(desc.usage, descriptor.usage)) {
159             return {};
160         }
161         return interop::GPUBuffer::Create<GPUBuffer>(env, device_.CreateBuffer(&desc), desc,
162                                                      device_, async_);
163     }
164 
createTexture(Napi::Env env,interop::GPUTextureDescriptor descriptor)165     interop::Interface<interop::GPUTexture> GPUDevice::createTexture(
166         Napi::Env env,
167         interop::GPUTextureDescriptor descriptor) {
168         Converter conv(env);
169 
170         wgpu::TextureDescriptor desc{};
171         if (!conv(desc.label, descriptor.label) || !conv(desc.usage, descriptor.usage) ||  //
172             !conv(desc.size, descriptor.size) ||                                           //
173             !conv(desc.dimension, descriptor.dimension) ||                                 //
174             !conv(desc.mipLevelCount, descriptor.mipLevelCount) ||                         //
175             !conv(desc.sampleCount, descriptor.sampleCount) ||                             //
176             !conv(desc.format, descriptor.format)) {
177             return {};
178         }
179         return interop::GPUTexture::Create<GPUTexture>(env, device_.CreateTexture(&desc));
180     }
181 
createSampler(Napi::Env env,interop::GPUSamplerDescriptor descriptor)182     interop::Interface<interop::GPUSampler> GPUDevice::createSampler(
183         Napi::Env env,
184         interop::GPUSamplerDescriptor descriptor) {
185         Converter conv(env);
186 
187         wgpu::SamplerDescriptor desc{};
188         if (!conv(desc.label, descriptor.label) ||                //
189             !conv(desc.addressModeU, descriptor.addressModeU) ||  //
190             !conv(desc.addressModeV, descriptor.addressModeV) ||  //
191             !conv(desc.addressModeW, descriptor.addressModeW) ||  //
192             !conv(desc.magFilter, descriptor.magFilter) ||        //
193             !conv(desc.minFilter, descriptor.minFilter) ||        //
194             !conv(desc.mipmapFilter, descriptor.mipmapFilter) ||  //
195             !conv(desc.lodMinClamp, descriptor.lodMinClamp) ||    //
196             !conv(desc.lodMaxClamp, descriptor.lodMaxClamp) ||    //
197             !conv(desc.compare, descriptor.compare) ||            //
198             !conv(desc.maxAnisotropy, descriptor.maxAnisotropy)) {
199             return {};
200         }
201         return interop::GPUSampler::Create<GPUSampler>(env, device_.CreateSampler(&desc));
202     }
203 
importExternalTexture(Napi::Env,interop::GPUExternalTextureDescriptor descriptor)204     interop::Interface<interop::GPUExternalTexture> GPUDevice::importExternalTexture(
205         Napi::Env,
206         interop::GPUExternalTextureDescriptor descriptor) {
207         UNIMPLEMENTED();
208     }
209 
createBindGroupLayout(Napi::Env env,interop::GPUBindGroupLayoutDescriptor descriptor)210     interop::Interface<interop::GPUBindGroupLayout> GPUDevice::createBindGroupLayout(
211         Napi::Env env,
212         interop::GPUBindGroupLayoutDescriptor descriptor) {
213         Converter conv(env);
214 
215         wgpu::BindGroupLayoutDescriptor desc{};
216         if (!conv(desc.label, descriptor.label) ||
217             !conv(desc.entries, desc.entryCount, descriptor.entries)) {
218             return {};
219         }
220 
221         return interop::GPUBindGroupLayout::Create<GPUBindGroupLayout>(
222             env, device_.CreateBindGroupLayout(&desc));
223     }
224 
createPipelineLayout(Napi::Env env,interop::GPUPipelineLayoutDescriptor descriptor)225     interop::Interface<interop::GPUPipelineLayout> GPUDevice::createPipelineLayout(
226         Napi::Env env,
227         interop::GPUPipelineLayoutDescriptor descriptor) {
228         Converter conv(env);
229 
230         wgpu::PipelineLayoutDescriptor desc{};
231         if (!conv(desc.label, descriptor.label) ||
232             !conv(desc.bindGroupLayouts, desc.bindGroupLayoutCount, descriptor.bindGroupLayouts)) {
233             return {};
234         }
235 
236         return interop::GPUPipelineLayout::Create<GPUPipelineLayout>(
237             env, device_.CreatePipelineLayout(&desc));
238     }
239 
createBindGroup(Napi::Env env,interop::GPUBindGroupDescriptor descriptor)240     interop::Interface<interop::GPUBindGroup> GPUDevice::createBindGroup(
241         Napi::Env env,
242         interop::GPUBindGroupDescriptor descriptor) {
243         Converter conv(env);
244 
245         wgpu::BindGroupDescriptor desc{};
246         if (!conv(desc.label, descriptor.label) || !conv(desc.layout, descriptor.layout) ||
247             !conv(desc.entries, desc.entryCount, descriptor.entries)) {
248             return {};
249         }
250 
251         return interop::GPUBindGroup::Create<GPUBindGroup>(env, device_.CreateBindGroup(&desc));
252     }
253 
createShaderModule(Napi::Env env,interop::GPUShaderModuleDescriptor descriptor)254     interop::Interface<interop::GPUShaderModule> GPUDevice::createShaderModule(
255         Napi::Env env,
256         interop::GPUShaderModuleDescriptor descriptor) {
257         Converter conv(env);
258 
259         wgpu::ShaderModuleWGSLDescriptor wgsl_desc{};
260         wgpu::ShaderModuleDescriptor sm_desc{};
261         if (!conv(wgsl_desc.source, descriptor.code) || !conv(sm_desc.label, descriptor.label)) {
262             return {};
263         }
264         sm_desc.nextInChain = &wgsl_desc;
265 
266         return interop::GPUShaderModule::Create<GPUShaderModule>(
267             env, device_.CreateShaderModule(&sm_desc), async_);
268     }
269 
createComputePipeline(Napi::Env env,interop::GPUComputePipelineDescriptor descriptor)270     interop::Interface<interop::GPUComputePipeline> GPUDevice::createComputePipeline(
271         Napi::Env env,
272         interop::GPUComputePipelineDescriptor descriptor) {
273         Converter conv(env);
274 
275         wgpu::ComputePipelineDescriptor desc{};
276         if (!conv(desc, descriptor)) {
277             return {};
278         }
279 
280         return interop::GPUComputePipeline::Create<GPUComputePipeline>(
281             env, device_.CreateComputePipeline(&desc));
282     }
283 
createRenderPipeline(Napi::Env env,interop::GPURenderPipelineDescriptor descriptor)284     interop::Interface<interop::GPURenderPipeline> GPUDevice::createRenderPipeline(
285         Napi::Env env,
286         interop::GPURenderPipelineDescriptor descriptor) {
287         Converter conv(env);
288 
289         wgpu::RenderPipelineDescriptor desc{};
290         if (!conv(desc, descriptor)) {
291             return {};
292         }
293 
294         return interop::GPURenderPipeline::Create<GPURenderPipeline>(
295             env, device_.CreateRenderPipeline(&desc));
296     }
297 
298     interop::Promise<interop::Interface<interop::GPUComputePipeline>>
createComputePipelineAsync(Napi::Env env,interop::GPUComputePipelineDescriptor descriptor)299     GPUDevice::createComputePipelineAsync(Napi::Env env,
300                                           interop::GPUComputePipelineDescriptor descriptor) {
301         using Promise = interop::Promise<interop::Interface<interop::GPUComputePipeline>>;
302 
303         Converter conv(env);
304 
305         wgpu::ComputePipelineDescriptor desc{};
306         if (!conv(desc, descriptor)) {
307             Promise promise(env, PROMISE_INFO);
308             promise.Reject(Errors::OperationError(env));
309             return promise;
310         }
311 
312         struct Context {
313             Napi::Env env;
314             Promise promise;
315             AsyncTask task;
316         };
317         auto ctx = new Context{env, Promise(env, PROMISE_INFO), async_};
318         auto promise = ctx->promise;
319 
320         device_.CreateComputePipelineAsync(
321             &desc,
322             [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline pipeline,
323                char const* message, void* userdata) {
324                 auto c = std::unique_ptr<Context>(static_cast<Context*>(userdata));
325 
326                 switch (status) {
327                     case WGPUCreatePipelineAsyncStatus::WGPUCreatePipelineAsyncStatus_Success:
328                         c->promise.Resolve(interop::GPUComputePipeline::Create<GPUComputePipeline>(
329                             c->env, pipeline));
330                         break;
331                     default:
332                         c->promise.Reject(Errors::OperationError(c->env));
333                         break;
334                 }
335             },
336             ctx);
337 
338         return promise;
339     }
340 
341     interop::Promise<interop::Interface<interop::GPURenderPipeline>>
createRenderPipelineAsync(Napi::Env env,interop::GPURenderPipelineDescriptor descriptor)342     GPUDevice::createRenderPipelineAsync(Napi::Env env,
343                                          interop::GPURenderPipelineDescriptor descriptor) {
344         using Promise = interop::Promise<interop::Interface<interop::GPURenderPipeline>>;
345 
346         Converter conv(env);
347 
348         wgpu::RenderPipelineDescriptor desc{};
349         if (!conv(desc, descriptor)) {
350             Promise promise(env, PROMISE_INFO);
351             promise.Reject(Errors::OperationError(env));
352             return promise;
353         }
354 
355         struct Context {
356             Napi::Env env;
357             Promise promise;
358             AsyncTask task;
359         };
360         auto ctx = new Context{env, Promise(env, PROMISE_INFO), async_};
361         auto promise = ctx->promise;
362 
363         device_.CreateRenderPipelineAsync(
364             &desc,
365             [](WGPUCreatePipelineAsyncStatus status, WGPURenderPipeline pipeline,
366                char const* message, void* userdata) {
367                 auto c = std::unique_ptr<Context>(static_cast<Context*>(userdata));
368 
369                 switch (status) {
370                     case WGPUCreatePipelineAsyncStatus::WGPUCreatePipelineAsyncStatus_Success:
371                         c->promise.Resolve(interop::GPURenderPipeline::Create<GPURenderPipeline>(
372                             c->env, pipeline));
373                         break;
374                     default:
375                         c->promise.Reject(Errors::OperationError(c->env));
376                         break;
377                 }
378             },
379             ctx);
380 
381         return promise;
382     }
383 
createCommandEncoder(Napi::Env env,interop::GPUCommandEncoderDescriptor descriptor)384     interop::Interface<interop::GPUCommandEncoder> GPUDevice::createCommandEncoder(
385         Napi::Env env,
386         interop::GPUCommandEncoderDescriptor descriptor) {
387         wgpu::CommandEncoderDescriptor desc{};
388         return interop::GPUCommandEncoder::Create<GPUCommandEncoder>(
389             env, device_.CreateCommandEncoder(&desc));
390     }
391 
createRenderBundleEncoder(Napi::Env env,interop::GPURenderBundleEncoderDescriptor descriptor)392     interop::Interface<interop::GPURenderBundleEncoder> GPUDevice::createRenderBundleEncoder(
393         Napi::Env env,
394         interop::GPURenderBundleEncoderDescriptor descriptor) {
395         Converter conv(env);
396 
397         wgpu::RenderBundleEncoderDescriptor desc{};
398         if (!conv(desc.label, descriptor.label) ||
399             !conv(desc.colorFormats, desc.colorFormatsCount, descriptor.colorFormats) ||
400             !conv(desc.depthStencilFormat, descriptor.depthStencilFormat) ||
401             !conv(desc.sampleCount, descriptor.sampleCount)) {
402             return {};
403         }
404 
405         return interop::GPURenderBundleEncoder::Create<GPURenderBundleEncoder>(
406             env, device_.CreateRenderBundleEncoder(&desc));
407     }
408 
createQuerySet(Napi::Env env,interop::GPUQuerySetDescriptor descriptor)409     interop::Interface<interop::GPUQuerySet> GPUDevice::createQuerySet(
410         Napi::Env env,
411         interop::GPUQuerySetDescriptor descriptor) {
412         Converter conv(env);
413 
414         wgpu::QuerySetDescriptor desc{};
415         if (!conv(desc.label, descriptor.label) || !conv(desc.type, descriptor.type) ||
416             !conv(desc.count, descriptor.count) ||
417             !conv(desc.pipelineStatistics, desc.pipelineStatisticsCount,
418                   descriptor.pipelineStatistics)) {
419             return {};
420         }
421 
422         return interop::GPUQuerySet::Create<GPUQuerySet>(env, device_.CreateQuerySet(&desc));
423     }
424 
getLost(Napi::Env env)425     interop::Promise<interop::Interface<interop::GPUDeviceLostInfo>> GPUDevice::getLost(
426         Napi::Env env) {
427         auto promise =
428             interop::Promise<interop::Interface<interop::GPUDeviceLostInfo>>(env, PROMISE_INFO);
429         lost_promises_.emplace_back(promise);
430         return promise;
431     }
432 
pushErrorScope(Napi::Env env,interop::GPUErrorFilter filter)433     void GPUDevice::pushErrorScope(Napi::Env env, interop::GPUErrorFilter filter) {
434         wgpu::ErrorFilter f = wgpu::ErrorFilter::None;
435         switch (filter) {
436             case interop::GPUErrorFilter::kOutOfMemory:
437                 f = wgpu::ErrorFilter::OutOfMemory;
438                 break;
439             case interop::GPUErrorFilter::kValidation:
440                 f = wgpu::ErrorFilter::Validation;
441                 break;
442             default:
443                 Napi::Error::New(env, "unhandled GPUErrorFilter value")
444                     .ThrowAsJavaScriptException();
445                 return;
446         }
447         device_.PushErrorScope(f);
448     }
449 
popErrorScope(Napi::Env env)450     interop::Promise<std::optional<interop::GPUError>> GPUDevice::popErrorScope(Napi::Env env) {
451         using Promise = interop::Promise<std::optional<interop::GPUError>>;
452         struct Context {
453             Napi::Env env;
454             Promise promise;
455             AsyncTask task;
456         };
457         auto* ctx = new Context{env, Promise(env, PROMISE_INFO), async_};
458         auto promise = ctx->promise;
459 
460         bool ok = device_.PopErrorScope(
461             [](WGPUErrorType type, char const* message, void* userdata) {
462                 auto c = std::unique_ptr<Context>(static_cast<Context*>(userdata));
463                 auto env = c->env;
464                 switch (type) {
465                     case WGPUErrorType::WGPUErrorType_NoError:
466                         c->promise.Resolve({});
467                         break;
468                     case WGPUErrorType::WGPUErrorType_OutOfMemory:
469                         c->promise.Resolve(interop::GPUOutOfMemoryError::Create<OOMError>(env));
470                         break;
471                     case WGPUErrorType::WGPUErrorType_Unknown:
472                     case WGPUErrorType::WGPUErrorType_DeviceLost:
473                     case WGPUErrorType::WGPUErrorType_Validation:
474                         c->promise.Resolve(
475                             interop::GPUValidationError::Create<ValidationError>(env, message));
476                         break;
477                     default:
478                         c->promise.Reject("unhandled error type");
479                         break;
480                 }
481             },
482             ctx);
483 
484         if (ok) {
485             return promise;
486         }
487 
488         delete ctx;
489         promise.Reject(Errors::OperationError(env));
490         return promise;
491     }
492 
getLabel(Napi::Env)493     std::optional<std::string> GPUDevice::getLabel(Napi::Env) {
494         UNIMPLEMENTED();
495     };
496 
setLabel(Napi::Env,std::optional<std::string> value)497     void GPUDevice::setLabel(Napi::Env, std::optional<std::string> value) {
498         UNIMPLEMENTED();
499     };
500 
getOnuncapturederror(Napi::Env)501     interop::Interface<interop::EventHandler> GPUDevice::getOnuncapturederror(Napi::Env) {
502         UNIMPLEMENTED();
503     }
504 
setOnuncapturederror(Napi::Env,interop::Interface<interop::EventHandler> value)505     void GPUDevice::setOnuncapturederror(Napi::Env,
506                                          interop::Interface<interop::EventHandler> value) {
507         UNIMPLEMENTED();
508     }
509 
addEventListener(Napi::Env,std::string type,std::optional<interop::Interface<interop::EventListener>> callback,std::optional<std::variant<interop::AddEventListenerOptions,bool>> options)510     void GPUDevice::addEventListener(
511         Napi::Env,
512         std::string type,
513         std::optional<interop::Interface<interop::EventListener>> callback,
514         std::optional<std::variant<interop::AddEventListenerOptions, bool>> options) {
515         UNIMPLEMENTED();
516     }
517 
removeEventListener(Napi::Env,std::string type,std::optional<interop::Interface<interop::EventListener>> callback,std::optional<std::variant<interop::EventListenerOptions,bool>> options)518     void GPUDevice::removeEventListener(
519         Napi::Env,
520         std::string type,
521         std::optional<interop::Interface<interop::EventListener>> callback,
522         std::optional<std::variant<interop::EventListenerOptions, bool>> options) {
523         UNIMPLEMENTED();
524     }
525 
dispatchEvent(Napi::Env,interop::Interface<interop::Event> event)526     bool GPUDevice::dispatchEvent(Napi::Env, interop::Interface<interop::Event> event) {
527         UNIMPLEMENTED();
528     }
529 
530 }}  // namespace wgpu::binding
531