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