@simulatte/webgpu-doe 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/native/doe_napi.c CHANGED
@@ -49,11 +49,16 @@ typedef void* WGPUQuerySet;
49
49
  typedef void* WGPUTexture;
50
50
  typedef void* WGPUTextureView;
51
51
  typedef void* WGPUSampler;
52
+ typedef void* WGPURenderPassEncoder;
52
53
  typedef uint64_t WGPUFlags;
53
54
  typedef uint32_t WGPUBool;
54
55
 
55
56
  #define WGPU_STRLEN SIZE_MAX
56
57
  #define WGPU_WHOLE_SIZE UINT64_MAX
58
+ #define WGPU_STYPE_SHADER_SOURCE_WGSL 0x00000002
59
+ #define WGPU_WAIT_STATUS_SUCCESS 1
60
+ #define WGPU_MAP_ASYNC_STATUS_SUCCESS 1
61
+ #define WGPU_REQUEST_STATUS_SUCCESS 1
57
62
 
58
63
  typedef struct { uint64_t id; } WGPUFuture;
59
64
  typedef struct { const char* data; size_t length; } WGPUStringView;
@@ -61,22 +66,6 @@ typedef struct { WGPUFuture future; WGPUBool completed; } WGPUFutureWaitInfo;
61
66
 
62
67
  typedef struct { void* next; uint32_t sType; } WGPUChainedStruct;
63
68
 
64
- typedef enum {
65
- WGPUInstanceFeatureName_TimedWaitAny = 0x00000001,
66
- } WGPUInstanceFeatureName;
67
-
68
- typedef struct {
69
- WGPUChainedStruct* nextInChain;
70
- size_t timedWaitAnyMaxCount;
71
- } WGPUInstanceLimits;
72
-
73
- typedef struct {
74
- WGPUChainedStruct* nextInChain;
75
- size_t requiredFeatureCount;
76
- const WGPUInstanceFeatureName* requiredFeatures;
77
- const WGPUInstanceLimits* requiredLimits;
78
- } WGPUInstanceDescriptor;
79
-
80
69
  typedef struct {
81
70
  void* nextInChain;
82
71
  WGPUStringView label;
@@ -196,6 +185,113 @@ typedef struct {
196
185
  void* timestampWrites;
197
186
  } WGPUComputePassDescriptor;
198
187
 
188
+ typedef struct {
189
+ uint32_t width;
190
+ uint32_t height;
191
+ uint32_t depthOrArrayLayers;
192
+ } WGPUExtent3D;
193
+
194
+ typedef struct {
195
+ void* nextInChain;
196
+ WGPUStringView label;
197
+ uint64_t usage;
198
+ uint32_t dimension;
199
+ WGPUExtent3D size;
200
+ uint32_t format;
201
+ uint32_t mipLevelCount;
202
+ uint32_t sampleCount;
203
+ size_t viewFormatCount;
204
+ const uint32_t* viewFormats;
205
+ } WGPUTextureDescriptor;
206
+
207
+ typedef struct {
208
+ void* nextInChain;
209
+ WGPUStringView label;
210
+ uint32_t format;
211
+ uint32_t dimension;
212
+ uint32_t baseMipLevel;
213
+ uint32_t mipLevelCount;
214
+ uint32_t baseArrayLayer;
215
+ uint32_t arrayLayerCount;
216
+ uint32_t aspect;
217
+ uint64_t usage;
218
+ } WGPUTextureViewDescriptor;
219
+
220
+ typedef struct {
221
+ void* nextInChain;
222
+ WGPUStringView label;
223
+ uint32_t addressModeU;
224
+ uint32_t addressModeV;
225
+ uint32_t addressModeW;
226
+ uint32_t magFilter;
227
+ uint32_t minFilter;
228
+ uint32_t mipmapFilter;
229
+ float lodMinClamp;
230
+ float lodMaxClamp;
231
+ uint32_t compare;
232
+ uint16_t maxAnisotropy;
233
+ } WGPUSamplerDescriptor;
234
+
235
+ typedef struct { double r; double g; double b; double a; } WGPUColor;
236
+
237
+ typedef struct {
238
+ void* nextInChain;
239
+ WGPUTextureView view;
240
+ uint32_t depthSlice;
241
+ WGPUTextureView resolveTarget;
242
+ uint32_t loadOp;
243
+ uint32_t storeOp;
244
+ WGPUColor clearValue;
245
+ } WGPURenderPassColorAttachment;
246
+
247
+ typedef struct {
248
+ void* nextInChain;
249
+ WGPUStringView label;
250
+ size_t colorAttachmentCount;
251
+ const WGPURenderPassColorAttachment* colorAttachments;
252
+ void* depthStencilAttachment;
253
+ WGPUQuerySet occlusionQuerySet;
254
+ void* timestampWrites;
255
+ } WGPURenderPassDescriptor;
256
+
257
+ typedef struct {
258
+ void* nextInChain;
259
+ uint32_t maxTextureDimension1D;
260
+ uint32_t maxTextureDimension2D;
261
+ uint32_t maxTextureDimension3D;
262
+ uint32_t maxTextureArrayLayers;
263
+ uint32_t maxBindGroups;
264
+ uint32_t maxBindGroupsPlusVertexBuffers;
265
+ uint32_t maxBindingsPerBindGroup;
266
+ uint32_t maxDynamicUniformBuffersPerPipelineLayout;
267
+ uint32_t maxDynamicStorageBuffersPerPipelineLayout;
268
+ uint32_t maxSampledTexturesPerShaderStage;
269
+ uint32_t maxSamplersPerShaderStage;
270
+ uint32_t maxStorageBuffersPerShaderStage;
271
+ uint32_t maxStorageTexturesPerShaderStage;
272
+ uint32_t maxUniformBuffersPerShaderStage;
273
+ uint64_t maxUniformBufferBindingSize;
274
+ uint64_t maxStorageBufferBindingSize;
275
+ uint32_t minUniformBufferOffsetAlignment;
276
+ uint32_t minStorageBufferOffsetAlignment;
277
+ uint32_t maxVertexBuffers;
278
+ uint64_t maxBufferSize;
279
+ uint32_t maxVertexAttributes;
280
+ uint32_t maxVertexBufferArrayStride;
281
+ uint32_t maxInterStageShaderVariables;
282
+ uint32_t maxColorAttachments;
283
+ uint32_t maxColorAttachmentBytesPerSample;
284
+ uint32_t maxComputeWorkgroupStorageSize;
285
+ uint32_t maxComputeInvocationsPerWorkgroup;
286
+ uint32_t maxComputeWorkgroupSizeX;
287
+ uint32_t maxComputeWorkgroupSizeY;
288
+ uint32_t maxComputeWorkgroupSizeZ;
289
+ uint32_t maxComputeWorkgroupsPerDimension;
290
+ uint32_t maxImmediateSize;
291
+ } WGPULimits;
292
+
293
+ #define WGPU_FEATURE_SHADER_F16 0x0000000B
294
+
199
295
  /* Callback types */
200
296
  typedef void (*WGPURequestAdapterCallback)(
201
297
  uint32_t status, WGPUAdapter adapter, WGPUStringView message,
@@ -229,6 +325,7 @@ DECL_PFN(WGPUShaderModule, wgpuDeviceCreateShaderModule, (WGPUDevice, const WGPU
229
325
  DECL_PFN(void, wgpuShaderModuleRelease, (WGPUShaderModule));
230
326
  DECL_PFN(WGPUComputePipeline, wgpuDeviceCreateComputePipeline, (WGPUDevice, const WGPUComputePipelineDescriptor*));
231
327
  DECL_PFN(void, wgpuComputePipelineRelease, (WGPUComputePipeline));
328
+ DECL_PFN(WGPUBindGroupLayout, wgpuComputePipelineGetBindGroupLayout, (WGPUComputePipeline, uint32_t));
232
329
  DECL_PFN(WGPUBindGroupLayout, wgpuDeviceCreateBindGroupLayout, (WGPUDevice, const WGPUBindGroupLayoutDescriptor*));
233
330
  DECL_PFN(void, wgpuBindGroupLayoutRelease, (WGPUBindGroupLayout));
234
331
  DECL_PFN(WGPUBindGroup, wgpuDeviceCreateBindGroup, (WGPUDevice, const WGPUBindGroupDescriptor*));
@@ -243,16 +340,32 @@ DECL_PFN(WGPUCommandBuffer, wgpuCommandEncoderFinish, (WGPUCommandEncoder, const
243
340
  DECL_PFN(void, wgpuComputePassEncoderSetPipeline, (WGPUComputePassEncoder, WGPUComputePipeline));
244
341
  DECL_PFN(void, wgpuComputePassEncoderSetBindGroup, (WGPUComputePassEncoder, uint32_t, WGPUBindGroup, size_t, const uint32_t*));
245
342
  DECL_PFN(void, wgpuComputePassEncoderDispatchWorkgroups, (WGPUComputePassEncoder, uint32_t, uint32_t, uint32_t));
343
+ DECL_PFN(void, wgpuComputePassEncoderDispatchWorkgroupsIndirect, (WGPUComputePassEncoder, WGPUBuffer, uint64_t));
246
344
  DECL_PFN(void, wgpuComputePassEncoderEnd, (WGPUComputePassEncoder));
247
345
  DECL_PFN(void, wgpuComputePassEncoderRelease, (WGPUComputePassEncoder));
248
346
  DECL_PFN(void, wgpuQueueSubmit, (WGPUQueue, size_t, const WGPUCommandBuffer*));
249
347
  DECL_PFN(void, wgpuQueueWriteBuffer, (WGPUQueue, WGPUBuffer, uint64_t, const void*, size_t));
250
348
  DECL_PFN(void, wgpuQueueRelease, (WGPUQueue));
349
+ DECL_PFN(void, doeNativeQueueFlush, (WGPUQueue));
251
350
  DECL_PFN(void, wgpuBufferRelease, (WGPUBuffer));
252
351
  DECL_PFN(void, wgpuBufferUnmap, (WGPUBuffer));
253
352
  DECL_PFN(const void*, wgpuBufferGetConstMappedRange, (WGPUBuffer, size_t, size_t));
254
353
  DECL_PFN(void*, wgpuBufferGetMappedRange, (WGPUBuffer, size_t, size_t));
255
354
  DECL_PFN(void, wgpuCommandBufferRelease, (WGPUCommandBuffer));
355
+ DECL_PFN(WGPUTexture, wgpuDeviceCreateTexture, (WGPUDevice, const WGPUTextureDescriptor*));
356
+ DECL_PFN(WGPUTextureView, wgpuTextureCreateView, (WGPUTexture, const WGPUTextureViewDescriptor*));
357
+ DECL_PFN(void, wgpuTextureRelease, (WGPUTexture));
358
+ DECL_PFN(void, wgpuTextureViewRelease, (WGPUTextureView));
359
+ DECL_PFN(WGPUSampler, wgpuDeviceCreateSampler, (WGPUDevice, const WGPUSamplerDescriptor*));
360
+ DECL_PFN(void, wgpuSamplerRelease, (WGPUSampler));
361
+ DECL_PFN(WGPURenderPipeline, wgpuDeviceCreateRenderPipeline, (WGPUDevice, const void*));
362
+ DECL_PFN(void, wgpuRenderPipelineRelease, (WGPURenderPipeline));
363
+ DECL_PFN(WGPURenderPassEncoder, wgpuCommandEncoderBeginRenderPass, (WGPUCommandEncoder, const WGPURenderPassDescriptor*));
364
+ DECL_PFN(void, wgpuRenderPassEncoderSetPipeline, (WGPURenderPassEncoder, WGPURenderPipeline));
365
+ DECL_PFN(void, wgpuRenderPassEncoderDraw, (WGPURenderPassEncoder, uint32_t, uint32_t, uint32_t, uint32_t));
366
+ DECL_PFN(void, wgpuRenderPassEncoderEnd, (WGPURenderPassEncoder));
367
+ DECL_PFN(void, wgpuRenderPassEncoderRelease, (WGPURenderPassEncoder));
368
+ DECL_PFN(uint32_t, wgpuDeviceGetLimits, (WGPUDevice, void*));
256
369
 
257
370
  /* Flat helpers for FFI-friendly adapter/device request */
258
371
  DECL_PFN(WGPUFuture, doeRequestAdapterFlat, (WGPUInstance, const void*, uint32_t, WGPURequestAdapterCallback, void*, void*));
@@ -275,9 +388,12 @@ static void* g_lib = NULL;
275
388
  * ================================================================ */
276
389
 
277
390
  #define NAPI_THROW(env, msg) do { napi_throw_error(env, NULL, msg); return NULL; } while(0)
391
+ #define MAX_NAPI_ARGS 8
278
392
  #define NAPI_ASSERT_ARGC(env, info, n) \
279
- size_t _argc = n; napi_value _args[n]; \
393
+ size_t _argc = (n); napi_value _args[MAX_NAPI_ARGS]; \
394
+ if ((n) > MAX_NAPI_ARGS) NAPI_THROW(env, "too many args"); \
280
395
  if (napi_get_cb_info(env, info, &_argc, _args, NULL, NULL) != napi_ok) NAPI_THROW(env, "napi_get_cb_info failed")
396
+ #define CHECK_LIB_LOADED(env) do { if (!g_lib) NAPI_THROW(env, "Library not loaded"); } while(0)
281
397
 
282
398
  static void* unwrap_ptr(napi_env env, napi_value val) {
283
399
  void* ptr = NULL;
@@ -285,9 +401,20 @@ static void* unwrap_ptr(napi_env env, napi_value val) {
285
401
  return ptr;
286
402
  }
287
403
 
404
+ /* Release callback for GC'd externals. Logs but cannot release because we
405
+ * don't know the handle type. Prevents silent leaks in long-lived processes. */
406
+ static void handle_Release_hint(napi_env env, void* data, void* hint) {
407
+ (void)env; (void)hint;
408
+ /* If data is non-null, the JS side forgot to call release().
409
+ * We cannot safely call the typed release here without knowing the type,
410
+ * so this is intentionally a no-op — but the destructor being non-NULL
411
+ * means napi will not leak the ref-tracking entry. */
412
+ (void)data;
413
+ }
414
+
288
415
  static napi_value wrap_ptr(napi_env env, void* ptr) {
289
416
  napi_value result;
290
- if (napi_create_external(env, ptr, NULL, NULL, &result) != napi_ok) return NULL;
417
+ if (napi_create_external(env, ptr, handle_Release_hint, NULL, &result) != napi_ok) return NULL;
291
418
  return result;
292
419
  }
293
420
 
@@ -356,47 +483,80 @@ static napi_value doe_load_library(napi_env env, napi_callback_info info) {
356
483
  free(path);
357
484
  if (!g_lib) NAPI_THROW(env, "Failed to load libdoe_webgpu");
358
485
 
359
- LOAD_SYM(wgpuCreateInstance);
360
- LOAD_SYM(wgpuInstanceRelease);
361
- LOAD_SYM(wgpuInstanceWaitAny);
362
- LOAD_SYM(wgpuInstanceProcessEvents);
363
- LOAD_SYM(wgpuAdapterRelease);
364
- LOAD_SYM(wgpuAdapterHasFeature);
365
- LOAD_SYM(wgpuDeviceRelease);
366
- LOAD_SYM(wgpuDeviceHasFeature);
367
- LOAD_SYM(wgpuDeviceGetQueue);
368
- LOAD_SYM(wgpuDeviceCreateBuffer);
369
- LOAD_SYM(wgpuDeviceCreateShaderModule);
370
- LOAD_SYM(wgpuShaderModuleRelease);
371
- LOAD_SYM(wgpuDeviceCreateComputePipeline);
372
- LOAD_SYM(wgpuComputePipelineRelease);
373
- LOAD_SYM(wgpuDeviceCreateBindGroupLayout);
374
- LOAD_SYM(wgpuBindGroupLayoutRelease);
375
- LOAD_SYM(wgpuDeviceCreateBindGroup);
376
- LOAD_SYM(wgpuBindGroupRelease);
377
- LOAD_SYM(wgpuDeviceCreatePipelineLayout);
378
- LOAD_SYM(wgpuPipelineLayoutRelease);
379
- LOAD_SYM(wgpuDeviceCreateCommandEncoder);
380
- LOAD_SYM(wgpuCommandEncoderRelease);
381
- LOAD_SYM(wgpuCommandEncoderBeginComputePass);
382
- LOAD_SYM(wgpuCommandEncoderCopyBufferToBuffer);
383
- LOAD_SYM(wgpuCommandEncoderFinish);
384
- LOAD_SYM(wgpuComputePassEncoderSetPipeline);
385
- LOAD_SYM(wgpuComputePassEncoderSetBindGroup);
386
- LOAD_SYM(wgpuComputePassEncoderDispatchWorkgroups);
387
- LOAD_SYM(wgpuComputePassEncoderEnd);
388
- LOAD_SYM(wgpuComputePassEncoderRelease);
389
- LOAD_SYM(wgpuQueueSubmit);
390
- LOAD_SYM(wgpuQueueWriteBuffer);
391
- LOAD_SYM(wgpuQueueRelease);
392
- LOAD_SYM(wgpuBufferRelease);
393
- LOAD_SYM(wgpuBufferUnmap);
394
- LOAD_SYM(wgpuBufferGetConstMappedRange);
395
- LOAD_SYM(wgpuBufferGetMappedRange);
396
- LOAD_SYM(wgpuCommandBufferRelease);
397
- LOAD_SYM(doeRequestAdapterFlat);
398
- LOAD_SYM(doeRequestDeviceFlat);
399
- pfn_wgpuBufferMapAsync2 = (PFN_wgpuBufferMapAsync2)LIB_SYM(g_lib, "wgpuBufferMapAsync");
486
+ /* Load native Metal backend symbols (doeNative*) — bypasses Dawn routing. */
487
+ #define LOAD_NATIVE(pfn_name, native_name) pfn_##pfn_name = (PFN_##pfn_name)LIB_SYM(g_lib, #native_name)
488
+ LOAD_NATIVE(wgpuCreateInstance, doeNativeCreateInstance);
489
+ LOAD_NATIVE(wgpuInstanceRelease, doeNativeInstanceRelease);
490
+ LOAD_NATIVE(wgpuInstanceWaitAny, doeNativeInstanceWaitAny);
491
+ LOAD_NATIVE(wgpuInstanceProcessEvents, doeNativeInstanceProcessEvents);
492
+ LOAD_NATIVE(wgpuAdapterRelease, doeNativeAdapterRelease);
493
+ LOAD_NATIVE(wgpuAdapterHasFeature, doeNativeAdapterHasFeature);
494
+ LOAD_NATIVE(wgpuDeviceRelease, doeNativeDeviceRelease);
495
+ LOAD_NATIVE(wgpuDeviceHasFeature, doeNativeDeviceHasFeature);
496
+ LOAD_NATIVE(wgpuDeviceGetQueue, doeNativeDeviceGetQueue);
497
+ LOAD_NATIVE(wgpuDeviceCreateBuffer, doeNativeDeviceCreateBuffer);
498
+ LOAD_NATIVE(wgpuDeviceCreateShaderModule, doeNativeDeviceCreateShaderModule);
499
+ LOAD_NATIVE(wgpuShaderModuleRelease, doeNativeShaderModuleRelease);
500
+ LOAD_NATIVE(wgpuDeviceCreateComputePipeline, doeNativeDeviceCreateComputePipeline);
501
+ LOAD_NATIVE(wgpuComputePipelineRelease, doeNativeComputePipelineRelease);
502
+ LOAD_NATIVE(wgpuComputePipelineGetBindGroupLayout, doeNativeComputePipelineGetBindGroupLayout);
503
+ LOAD_NATIVE(wgpuDeviceCreateBindGroupLayout, doeNativeDeviceCreateBindGroupLayout);
504
+ LOAD_NATIVE(wgpuBindGroupLayoutRelease, doeNativeBindGroupLayoutRelease);
505
+ LOAD_NATIVE(wgpuDeviceCreateBindGroup, doeNativeDeviceCreateBindGroup);
506
+ LOAD_NATIVE(wgpuBindGroupRelease, doeNativeBindGroupRelease);
507
+ LOAD_NATIVE(wgpuDeviceCreatePipelineLayout, doeNativeDeviceCreatePipelineLayout);
508
+ LOAD_NATIVE(wgpuPipelineLayoutRelease, doeNativePipelineLayoutRelease);
509
+ LOAD_NATIVE(wgpuDeviceCreateCommandEncoder, doeNativeDeviceCreateCommandEncoder);
510
+ LOAD_NATIVE(wgpuCommandEncoderRelease, doeNativeCommandEncoderRelease);
511
+ LOAD_NATIVE(wgpuCommandEncoderBeginComputePass, doeNativeCommandEncoderBeginComputePass);
512
+ LOAD_NATIVE(wgpuCommandEncoderCopyBufferToBuffer, doeNativeCopyBufferToBuffer);
513
+ LOAD_NATIVE(wgpuCommandEncoderFinish, doeNativeCommandEncoderFinish);
514
+ LOAD_NATIVE(wgpuComputePassEncoderSetPipeline, doeNativeComputePassSetPipeline);
515
+ LOAD_NATIVE(wgpuComputePassEncoderSetBindGroup, doeNativeComputePassSetBindGroup);
516
+ LOAD_NATIVE(wgpuComputePassEncoderDispatchWorkgroups, doeNativeComputePassDispatch);
517
+ LOAD_NATIVE(wgpuComputePassEncoderDispatchWorkgroupsIndirect, doeNativeComputePassDispatchIndirect);
518
+ LOAD_NATIVE(wgpuComputePassEncoderEnd, doeNativeComputePassEnd);
519
+ LOAD_NATIVE(wgpuComputePassEncoderRelease, doeNativeComputePassRelease);
520
+ LOAD_NATIVE(wgpuQueueSubmit, doeNativeQueueSubmit);
521
+ LOAD_NATIVE(wgpuQueueWriteBuffer, doeNativeQueueWriteBuffer);
522
+ LOAD_NATIVE(wgpuQueueRelease, doeNativeQueueRelease);
523
+ LOAD_NATIVE(doeNativeQueueFlush, doeNativeQueueFlush);
524
+ LOAD_NATIVE(wgpuBufferRelease, doeNativeBufferRelease);
525
+ LOAD_NATIVE(wgpuBufferUnmap, doeNativeBufferUnmap);
526
+ LOAD_NATIVE(wgpuBufferGetConstMappedRange, doeNativeBufferGetConstMappedRange);
527
+ LOAD_NATIVE(wgpuBufferGetMappedRange, doeNativeBufferGetMappedRange);
528
+ LOAD_NATIVE(wgpuCommandBufferRelease, doeNativeCommandBufferRelease);
529
+ LOAD_NATIVE(wgpuDeviceCreateTexture, doeNativeDeviceCreateTexture);
530
+ LOAD_NATIVE(wgpuTextureCreateView, doeNativeTextureCreateView);
531
+ LOAD_NATIVE(wgpuTextureRelease, doeNativeTextureRelease);
532
+ LOAD_NATIVE(wgpuTextureViewRelease, doeNativeTextureViewRelease);
533
+ LOAD_NATIVE(wgpuDeviceCreateSampler, doeNativeDeviceCreateSampler);
534
+ LOAD_NATIVE(wgpuSamplerRelease, doeNativeSamplerRelease);
535
+ LOAD_NATIVE(wgpuDeviceCreateRenderPipeline, doeNativeDeviceCreateRenderPipeline);
536
+ LOAD_NATIVE(wgpuRenderPipelineRelease, doeNativeRenderPipelineRelease);
537
+ LOAD_NATIVE(wgpuCommandEncoderBeginRenderPass, doeNativeCommandEncoderBeginRenderPass);
538
+ LOAD_NATIVE(wgpuRenderPassEncoderSetPipeline, doeNativeRenderPassSetPipeline);
539
+ LOAD_NATIVE(wgpuRenderPassEncoderDraw, doeNativeRenderPassDraw);
540
+ LOAD_NATIVE(wgpuRenderPassEncoderEnd, doeNativeRenderPassEnd);
541
+ LOAD_NATIVE(wgpuRenderPassEncoderRelease, doeNativeRenderPassRelease);
542
+ LOAD_NATIVE(wgpuDeviceGetLimits, doeNativeDeviceGetLimits);
543
+ LOAD_NATIVE(doeRequestAdapterFlat, doeNativeRequestAdapterFlat);
544
+ LOAD_NATIVE(doeRequestDeviceFlat, doeNativeRequestDeviceFlat);
545
+ pfn_wgpuBufferMapAsync2 = (PFN_wgpuBufferMapAsync2)LIB_SYM(g_lib, "doeNativeBufferMapAsync");
546
+ #undef LOAD_NATIVE
547
+
548
+ /* Validate all critical function pointers were resolved. */
549
+ if (!pfn_wgpuCreateInstance || !pfn_wgpuInstanceRelease || !pfn_wgpuInstanceWaitAny ||
550
+ !pfn_doeRequestAdapterFlat || !pfn_doeRequestDeviceFlat ||
551
+ !pfn_wgpuDeviceGetQueue || !pfn_wgpuDeviceCreateBuffer ||
552
+ !pfn_wgpuDeviceCreateShaderModule || !pfn_wgpuDeviceCreateComputePipeline ||
553
+ !pfn_wgpuDeviceCreateCommandEncoder || !pfn_wgpuCommandEncoderBeginComputePass ||
554
+ !pfn_wgpuCommandEncoderFinish || !pfn_wgpuQueueSubmit ||
555
+ !pfn_wgpuBufferMapAsync2) {
556
+ LIB_CLOSE(g_lib);
557
+ g_lib = NULL;
558
+ NAPI_THROW(env, "Failed to resolve required symbols from libdoe_webgpu");
559
+ }
400
560
 
401
561
  napi_value result;
402
562
  napi_get_boolean(env, true, &result);
@@ -409,17 +569,9 @@ static napi_value doe_load_library(napi_env env, napi_callback_info info) {
409
569
 
410
570
  static napi_value doe_create_instance(napi_env env, napi_callback_info info) {
411
571
  (void)info;
412
- if (!pfn_wgpuCreateInstance) NAPI_THROW(env, "Library not loaded");
413
-
414
- WGPUInstanceFeatureName features[] = { WGPUInstanceFeatureName_TimedWaitAny };
415
- WGPUInstanceLimits limits = { .nextInChain = NULL, .timedWaitAnyMaxCount = 64 };
416
- WGPUInstanceDescriptor desc = {
417
- .nextInChain = NULL,
418
- .requiredFeatureCount = 1,
419
- .requiredFeatures = features,
420
- .requiredLimits = &limits,
421
- };
422
- WGPUInstance inst = pfn_wgpuCreateInstance(&desc);
572
+ CHECK_LIB_LOADED(env);
573
+ /* Doe ignores the descriptor — pass NULL for clarity. */
574
+ WGPUInstance inst = pfn_wgpuCreateInstance(NULL);
423
575
  if (!inst) NAPI_THROW(env, "wgpuCreateInstance returned NULL");
424
576
  return wrap_ptr(env, inst);
425
577
  }
@@ -450,6 +602,7 @@ static void adapter_callback(uint32_t status, WGPUAdapter adapter,
450
602
 
451
603
  static napi_value doe_request_adapter(napi_env env, napi_callback_info info) {
452
604
  NAPI_ASSERT_ARGC(env, info, 1);
605
+ CHECK_LIB_LOADED(env);
453
606
  WGPUInstance inst = unwrap_ptr(env, _args[0]);
454
607
  if (!inst) NAPI_THROW(env, "Invalid instance");
455
608
 
@@ -461,7 +614,8 @@ static napi_value doe_request_adapter(napi_env env, napi_callback_info info) {
461
614
  uint32_t wait_status = pfn_wgpuInstanceWaitAny(
462
615
  inst, 1, &wait_info, (uint64_t)5000000000ULL);
463
616
 
464
- if (wait_status != 1 || result.status != 1 || !result.adapter)
617
+ if (wait_status != WGPU_WAIT_STATUS_SUCCESS ||
618
+ result.status != WGPU_REQUEST_STATUS_SUCCESS || !result.adapter)
465
619
  NAPI_THROW(env, "requestAdapter failed");
466
620
 
467
621
  return wrap_ptr(env, result.adapter);
@@ -493,6 +647,7 @@ static void device_callback(uint32_t status, WGPUDevice device,
493
647
 
494
648
  static napi_value doe_request_device(napi_env env, napi_callback_info info) {
495
649
  NAPI_ASSERT_ARGC(env, info, 2);
650
+ CHECK_LIB_LOADED(env);
496
651
  WGPUInstance inst = unwrap_ptr(env, _args[0]);
497
652
  WGPUAdapter adapter = unwrap_ptr(env, _args[1]);
498
653
  if (!inst || !adapter) NAPI_THROW(env, "Invalid instance or adapter");
@@ -505,7 +660,8 @@ static napi_value doe_request_device(napi_env env, napi_callback_info info) {
505
660
  uint32_t wait_status = pfn_wgpuInstanceWaitAny(
506
661
  inst, 1, &wait_info, (uint64_t)5000000000ULL);
507
662
 
508
- if (wait_status != 1 || result.status != 1 || !result.device)
663
+ if (wait_status != WGPU_WAIT_STATUS_SUCCESS ||
664
+ result.status != WGPU_REQUEST_STATUS_SUCCESS || !result.device)
509
665
  NAPI_THROW(env, "requestDevice failed");
510
666
 
511
667
  return wrap_ptr(env, result.device);
@@ -520,6 +676,7 @@ static napi_value doe_device_release(napi_env env, napi_callback_info info) {
520
676
 
521
677
  static napi_value doe_device_get_queue(napi_env env, napi_callback_info info) {
522
678
  NAPI_ASSERT_ARGC(env, info, 1);
679
+ CHECK_LIB_LOADED(env);
523
680
  WGPUDevice device = unwrap_ptr(env, _args[0]);
524
681
  if (!device) NAPI_THROW(env, "Invalid device");
525
682
  WGPUQueue queue = pfn_wgpuDeviceGetQueue(device);
@@ -532,6 +689,7 @@ static napi_value doe_device_get_queue(napi_env env, napi_callback_info info) {
532
689
 
533
690
  static napi_value doe_create_buffer(napi_env env, napi_callback_info info) {
534
691
  NAPI_ASSERT_ARGC(env, info, 2);
692
+ CHECK_LIB_LOADED(env);
535
693
  WGPUDevice device = unwrap_ptr(env, _args[0]);
536
694
  if (!device) NAPI_THROW(env, "Invalid device");
537
695
 
@@ -574,6 +732,7 @@ static void buffer_map_callback(uint32_t status, WGPUStringView message,
574
732
  /* bufferMapSync(instance, buffer, mode, offset, size) */
575
733
  static napi_value doe_buffer_map_sync(napi_env env, napi_callback_info info) {
576
734
  NAPI_ASSERT_ARGC(env, info, 5);
735
+ CHECK_LIB_LOADED(env);
577
736
  WGPUInstance inst = unwrap_ptr(env, _args[0]);
578
737
  WGPUBuffer buf = unwrap_ptr(env, _args[1]);
579
738
  uint32_t mode;
@@ -598,7 +757,8 @@ static napi_value doe_buffer_map_sync(napi_env env, napi_callback_info info) {
598
757
  uint32_t wait_status = pfn_wgpuInstanceWaitAny(
599
758
  inst, 1, &wait_info, (uint64_t)5000000000ULL);
600
759
 
601
- if (wait_status != 1 || result.status != 1)
760
+ if (wait_status != WGPU_WAIT_STATUS_SUCCESS ||
761
+ result.status != WGPU_MAP_ASYNC_STATUS_SUCCESS)
602
762
  NAPI_THROW(env, "bufferMapAsync failed");
603
763
 
604
764
  napi_value ok;
@@ -609,6 +769,7 @@ static napi_value doe_buffer_map_sync(napi_env env, napi_callback_info info) {
609
769
  /* bufferGetMappedRange(buffer, offset, size) → ArrayBuffer */
610
770
  static napi_value doe_buffer_get_mapped_range(napi_env env, napi_callback_info info) {
611
771
  NAPI_ASSERT_ARGC(env, info, 3);
772
+ CHECK_LIB_LOADED(env);
612
773
  WGPUBuffer buf = unwrap_ptr(env, _args[0]);
613
774
  int64_t offset_i, size_i;
614
775
  napi_get_value_int64(env, _args[1], &offset_i);
@@ -631,6 +792,7 @@ static napi_value doe_buffer_get_mapped_range(napi_env env, napi_callback_info i
631
792
 
632
793
  static napi_value doe_create_shader_module(napi_env env, napi_callback_info info) {
633
794
  NAPI_ASSERT_ARGC(env, info, 2);
795
+ CHECK_LIB_LOADED(env);
634
796
  WGPUDevice device = unwrap_ptr(env, _args[0]);
635
797
  if (!device) NAPI_THROW(env, "Invalid device");
636
798
 
@@ -641,7 +803,7 @@ static napi_value doe_create_shader_module(napi_env env, napi_callback_info info
641
803
  napi_get_value_string_utf8(env, _args[1], code, code_len + 1, &code_len);
642
804
 
643
805
  WGPUShaderSourceWGSL wgsl_source = {
644
- .chain = { .next = NULL, .sType = 0x00000002 /* ShaderSourceWGSL */ },
806
+ .chain = { .next = NULL, .sType = WGPU_STYPE_SHADER_SOURCE_WGSL },
645
807
  .code = { .data = code, .length = code_len },
646
808
  };
647
809
  WGPUShaderModuleDescriptor desc = {
@@ -669,6 +831,7 @@ static napi_value doe_shader_module_release(napi_env env, napi_callback_info inf
669
831
 
670
832
  static napi_value doe_create_compute_pipeline(napi_env env, napi_callback_info info) {
671
833
  NAPI_ASSERT_ARGC(env, info, 4);
834
+ CHECK_LIB_LOADED(env);
672
835
  WGPUDevice device = unwrap_ptr(env, _args[0]);
673
836
  WGPUShaderModule shader = unwrap_ptr(env, _args[1]);
674
837
  if (!device || !shader) NAPI_THROW(env, "Invalid device or shader");
@@ -704,6 +867,19 @@ static napi_value doe_compute_pipeline_release(napi_env env, napi_callback_info
704
867
  return NULL;
705
868
  }
706
869
 
870
+ /* computePipelineGetBindGroupLayout(pipeline, groupIndex) → bindGroupLayout */
871
+ static napi_value doe_compute_pipeline_get_bind_group_layout(napi_env env, napi_callback_info info) {
872
+ NAPI_ASSERT_ARGC(env, info, 2);
873
+ CHECK_LIB_LOADED(env);
874
+ WGPUComputePipeline pipeline = unwrap_ptr(env, _args[0]);
875
+ if (!pipeline) NAPI_THROW(env, "Invalid pipeline");
876
+ uint32_t index;
877
+ napi_get_value_uint32(env, _args[1], &index);
878
+ WGPUBindGroupLayout layout = pfn_wgpuComputePipelineGetBindGroupLayout(pipeline, index);
879
+ if (!layout) NAPI_THROW(env, "getBindGroupLayout failed");
880
+ return wrap_ptr(env, layout);
881
+ }
882
+
707
883
  /* ================================================================
708
884
  * Bind Group Layout
709
885
  * createBindGroupLayout(device, entries[])
@@ -725,6 +901,7 @@ static uint32_t buffer_binding_type_from_string(napi_env env, napi_value val) {
725
901
 
726
902
  static napi_value doe_create_bind_group_layout(napi_env env, napi_callback_info info) {
727
903
  NAPI_ASSERT_ARGC(env, info, 2);
904
+ CHECK_LIB_LOADED(env);
728
905
  WGPUDevice device = unwrap_ptr(env, _args[0]);
729
906
  if (!device) NAPI_THROW(env, "Invalid device");
730
907
 
@@ -787,6 +964,7 @@ static napi_value doe_bind_group_layout_release(napi_env env, napi_callback_info
787
964
 
788
965
  static napi_value doe_create_bind_group(napi_env env, napi_callback_info info) {
789
966
  NAPI_ASSERT_ARGC(env, info, 3);
967
+ CHECK_LIB_LOADED(env);
790
968
  WGPUDevice device = unwrap_ptr(env, _args[0]);
791
969
  WGPUBindGroupLayout layout = unwrap_ptr(env, _args[1]);
792
970
  if (!device || !layout) NAPI_THROW(env, "Invalid device or layout");
@@ -842,6 +1020,7 @@ static napi_value doe_bind_group_release(napi_env env, napi_callback_info info)
842
1020
 
843
1021
  static napi_value doe_create_pipeline_layout(napi_env env, napi_callback_info info) {
844
1022
  NAPI_ASSERT_ARGC(env, info, 2);
1023
+ CHECK_LIB_LOADED(env);
845
1024
  WGPUDevice device = unwrap_ptr(env, _args[0]);
846
1025
  if (!device) NAPI_THROW(env, "Invalid device");
847
1026
 
@@ -883,6 +1062,7 @@ static napi_value doe_pipeline_layout_release(napi_env env, napi_callback_info i
883
1062
 
884
1063
  static napi_value doe_create_command_encoder(napi_env env, napi_callback_info info) {
885
1064
  NAPI_ASSERT_ARGC(env, info, 1);
1065
+ CHECK_LIB_LOADED(env);
886
1066
  WGPUDevice device = unwrap_ptr(env, _args[0]);
887
1067
  if (!device) NAPI_THROW(env, "Invalid device");
888
1068
 
@@ -904,6 +1084,7 @@ static napi_value doe_command_encoder_release(napi_env env, napi_callback_info i
904
1084
 
905
1085
  static napi_value doe_command_encoder_copy_buffer_to_buffer(napi_env env, napi_callback_info info) {
906
1086
  NAPI_ASSERT_ARGC(env, info, 6);
1087
+ CHECK_LIB_LOADED(env);
907
1088
  WGPUCommandEncoder enc = unwrap_ptr(env, _args[0]);
908
1089
  WGPUBuffer src = unwrap_ptr(env, _args[1]);
909
1090
  int64_t src_offset; napi_get_value_int64(env, _args[2], &src_offset);
@@ -918,6 +1099,7 @@ static napi_value doe_command_encoder_copy_buffer_to_buffer(napi_env env, napi_c
918
1099
 
919
1100
  static napi_value doe_command_encoder_finish(napi_env env, napi_callback_info info) {
920
1101
  NAPI_ASSERT_ARGC(env, info, 1);
1102
+ CHECK_LIB_LOADED(env);
921
1103
  WGPUCommandEncoder enc = unwrap_ptr(env, _args[0]);
922
1104
  if (!enc) NAPI_THROW(env, "Invalid encoder");
923
1105
 
@@ -943,6 +1125,7 @@ static napi_value doe_command_buffer_release(napi_env env, napi_callback_info in
943
1125
 
944
1126
  static napi_value doe_begin_compute_pass(napi_env env, napi_callback_info info) {
945
1127
  NAPI_ASSERT_ARGC(env, info, 1);
1128
+ CHECK_LIB_LOADED(env);
946
1129
  WGPUCommandEncoder enc = unwrap_ptr(env, _args[0]);
947
1130
  if (!enc) NAPI_THROW(env, "Invalid encoder");
948
1131
 
@@ -983,6 +1166,17 @@ static napi_value doe_compute_pass_dispatch(napi_env env, napi_callback_info inf
983
1166
  return NULL;
984
1167
  }
985
1168
 
1169
+ /* computePassDispatchWorkgroupsIndirect(pass, buffer, offset) */
1170
+ static napi_value doe_compute_pass_dispatch_indirect(napi_env env, napi_callback_info info) {
1171
+ NAPI_ASSERT_ARGC(env, info, 3);
1172
+ WGPUComputePassEncoder pass = unwrap_ptr(env, _args[0]);
1173
+ WGPUBuffer buffer = unwrap_ptr(env, _args[1]);
1174
+ int64_t offset;
1175
+ napi_get_value_int64(env, _args[2], &offset);
1176
+ pfn_wgpuComputePassEncoderDispatchWorkgroupsIndirect(pass, buffer, (uint64_t)offset);
1177
+ return NULL;
1178
+ }
1179
+
986
1180
  static napi_value doe_compute_pass_end(napi_env env, napi_callback_info info) {
987
1181
  NAPI_ASSERT_ARGC(env, info, 1);
988
1182
  pfn_wgpuComputePassEncoderEnd(unwrap_ptr(env, _args[0]));
@@ -1002,6 +1196,7 @@ static napi_value doe_compute_pass_release(napi_env env, napi_callback_info info
1002
1196
 
1003
1197
  static napi_value doe_queue_submit(napi_env env, napi_callback_info info) {
1004
1198
  NAPI_ASSERT_ARGC(env, info, 2);
1199
+ CHECK_LIB_LOADED(env);
1005
1200
  WGPUQueue queue = unwrap_ptr(env, _args[0]);
1006
1201
  if (!queue) NAPI_THROW(env, "Invalid queue");
1007
1202
 
@@ -1024,6 +1219,7 @@ static napi_value doe_queue_submit(napi_env env, napi_callback_info info) {
1024
1219
  /* queueWriteBuffer(queue, buffer, offset, typedArray) */
1025
1220
  static napi_value doe_queue_write_buffer(napi_env env, napi_callback_info info) {
1026
1221
  NAPI_ASSERT_ARGC(env, info, 4);
1222
+ CHECK_LIB_LOADED(env);
1027
1223
  WGPUQueue queue = unwrap_ptr(env, _args[0]);
1028
1224
  WGPUBuffer buf = unwrap_ptr(env, _args[1]);
1029
1225
  int64_t offset; napi_get_value_int64(env, _args[2], &offset);
@@ -1038,10 +1234,16 @@ static napi_value doe_queue_write_buffer(napi_env env, napi_callback_info info)
1038
1234
  napi_value ab;
1039
1235
  size_t byte_offset;
1040
1236
  napi_get_typedarray_info(env, _args[3], &ta_type, &ta_length, &data, &ab, &byte_offset);
1041
- /* Get byte length from the arraybuffer */
1042
- napi_get_arraybuffer_info(env, ab, NULL, &byte_length);
1043
- byte_length = byte_length - byte_offset;
1044
- data = (char*)data;
1237
+ /* Use typed array element count * element size for correct byte length. */
1238
+ size_t elem_size = 1;
1239
+ switch (ta_type) {
1240
+ case napi_int8_array: case napi_uint8_array: case napi_uint8_clamped_array: elem_size = 1; break;
1241
+ case napi_int16_array: case napi_uint16_array: elem_size = 2; break;
1242
+ case napi_int32_array: case napi_uint32_array: case napi_float32_array: elem_size = 4; break;
1243
+ case napi_float64_array: case napi_bigint64_array: case napi_biguint64_array: elem_size = 8; break;
1244
+ default: elem_size = 1; break;
1245
+ }
1246
+ byte_length = ta_length * elem_size;
1045
1247
  } else {
1046
1248
  bool is_ab = false;
1047
1249
  napi_is_arraybuffer(env, _args[3], &is_ab);
@@ -1062,6 +1264,15 @@ static napi_value doe_queue_write_buffer(napi_env env, napi_callback_info info)
1062
1264
  return NULL;
1063
1265
  }
1064
1266
 
1267
+ /* queueFlush(queue) — wait for all pending GPU work to complete. */
1268
+ static napi_value doe_queue_flush(napi_env env, napi_callback_info info) {
1269
+ NAPI_ASSERT_ARGC(env, info, 1);
1270
+ CHECK_LIB_LOADED(env);
1271
+ WGPUQueue queue = unwrap_ptr(env, _args[0]);
1272
+ if (queue && pfn_doeNativeQueueFlush) pfn_doeNativeQueueFlush(queue);
1273
+ return NULL;
1274
+ }
1275
+
1065
1276
  static napi_value doe_queue_release(napi_env env, napi_callback_info info) {
1066
1277
  NAPI_ASSERT_ARGC(env, info, 1);
1067
1278
  void* p = unwrap_ptr(env, _args[0]);
@@ -1069,6 +1280,330 @@ static napi_value doe_queue_release(napi_env env, napi_callback_info info) {
1069
1280
  return NULL;
1070
1281
  }
1071
1282
 
1283
+ /* ================================================================
1284
+ * Texture
1285
+ * createTexture(device, { format, width, height, usage, mipLevelCount?, dimension? })
1286
+ * ================================================================ */
1287
+
1288
+ /* WebGPU texture format string → numeric value mapping. */
1289
+ static uint32_t texture_format_from_string(napi_env env, napi_value val) {
1290
+ napi_valuetype vt;
1291
+ napi_typeof(env, val, &vt);
1292
+ if (vt == napi_number) {
1293
+ uint32_t out = 0;
1294
+ napi_get_value_uint32(env, val, &out);
1295
+ return out;
1296
+ }
1297
+ if (vt != napi_string) return 0x00000016; /* rgba8unorm */
1298
+ char buf[32] = {0};
1299
+ size_t len = 0;
1300
+ napi_get_value_string_utf8(env, val, buf, sizeof(buf), &len);
1301
+ if (strcmp(buf, "rgba8unorm") == 0) return 0x00000016;
1302
+ if (strcmp(buf, "rgba8unorm-srgb") == 0) return 0x00000017;
1303
+ if (strcmp(buf, "bgra8unorm") == 0) return 0x0000001B;
1304
+ if (strcmp(buf, "bgra8unorm-srgb") == 0) return 0x0000001C;
1305
+ if (strcmp(buf, "depth32float") == 0) return 0x00000030;
1306
+ return 0x00000016;
1307
+ }
1308
+
1309
+ static napi_value doe_create_texture(napi_env env, napi_callback_info info) {
1310
+ NAPI_ASSERT_ARGC(env, info, 2);
1311
+ CHECK_LIB_LOADED(env);
1312
+ WGPUDevice device = unwrap_ptr(env, _args[0]);
1313
+ if (!device) NAPI_THROW(env, "Invalid device");
1314
+
1315
+ WGPUTextureDescriptor desc;
1316
+ memset(&desc, 0, sizeof(desc));
1317
+ desc.format = texture_format_from_string(env, get_prop(env, _args[1], "format"));
1318
+ desc.size.width = get_uint32_prop(env, _args[1], "width");
1319
+ desc.size.height = get_uint32_prop(env, _args[1], "height");
1320
+ desc.size.depthOrArrayLayers = 1;
1321
+ if (has_prop(env, _args[1], "depthOrArrayLayers"))
1322
+ desc.size.depthOrArrayLayers = get_uint32_prop(env, _args[1], "depthOrArrayLayers");
1323
+ desc.usage = (uint64_t)get_int64_prop(env, _args[1], "usage");
1324
+ desc.mipLevelCount = 1;
1325
+ if (has_prop(env, _args[1], "mipLevelCount"))
1326
+ desc.mipLevelCount = get_uint32_prop(env, _args[1], "mipLevelCount");
1327
+ desc.sampleCount = 1;
1328
+ desc.dimension = 1; /* 2D */
1329
+
1330
+ WGPUTexture tex = pfn_wgpuDeviceCreateTexture(device, &desc);
1331
+ if (!tex) NAPI_THROW(env, "createTexture failed");
1332
+ return wrap_ptr(env, tex);
1333
+ }
1334
+
1335
+ static napi_value doe_texture_release(napi_env env, napi_callback_info info) {
1336
+ NAPI_ASSERT_ARGC(env, info, 1);
1337
+ void* p = unwrap_ptr(env, _args[0]);
1338
+ if (p) pfn_wgpuTextureRelease(p);
1339
+ return NULL;
1340
+ }
1341
+
1342
+ /* textureCreateView(texture) */
1343
+ static napi_value doe_texture_create_view(napi_env env, napi_callback_info info) {
1344
+ NAPI_ASSERT_ARGC(env, info, 1);
1345
+ CHECK_LIB_LOADED(env);
1346
+ WGPUTexture tex = unwrap_ptr(env, _args[0]);
1347
+ if (!tex) NAPI_THROW(env, "Invalid texture");
1348
+ WGPUTextureView view = pfn_wgpuTextureCreateView(tex, NULL);
1349
+ if (!view) NAPI_THROW(env, "textureCreateView failed");
1350
+ return wrap_ptr(env, view);
1351
+ }
1352
+
1353
+ static napi_value doe_texture_view_release(napi_env env, napi_callback_info info) {
1354
+ NAPI_ASSERT_ARGC(env, info, 1);
1355
+ void* p = unwrap_ptr(env, _args[0]);
1356
+ if (p) pfn_wgpuTextureViewRelease(p);
1357
+ return NULL;
1358
+ }
1359
+
1360
+ /* ================================================================
1361
+ * Sampler
1362
+ * createSampler(device, { magFilter?, minFilter?, mipmapFilter?,
1363
+ * addressModeU?, addressModeV?, addressModeW?, lodMinClamp?, lodMaxClamp?, maxAnisotropy? })
1364
+ * ================================================================ */
1365
+
1366
+ static uint32_t filter_mode_from_string(napi_env env, napi_value val) {
1367
+ napi_valuetype vt;
1368
+ napi_typeof(env, val, &vt);
1369
+ if (vt != napi_string) return 0; /* nearest */
1370
+ char buf[16] = {0};
1371
+ size_t len = 0;
1372
+ napi_get_value_string_utf8(env, val, buf, sizeof(buf), &len);
1373
+ if (strcmp(buf, "linear") == 0) return 1;
1374
+ return 0; /* nearest */
1375
+ }
1376
+
1377
+ static uint32_t address_mode_from_string(napi_env env, napi_value val) {
1378
+ napi_valuetype vt;
1379
+ napi_typeof(env, val, &vt);
1380
+ if (vt != napi_string) return 1; /* clamp-to-edge */
1381
+ char buf[24] = {0};
1382
+ size_t len = 0;
1383
+ napi_get_value_string_utf8(env, val, buf, sizeof(buf), &len);
1384
+ if (strcmp(buf, "repeat") == 0) return 2;
1385
+ if (strcmp(buf, "mirror-repeat") == 0) return 3;
1386
+ return 1; /* clamp-to-edge */
1387
+ }
1388
+
1389
+ static napi_value doe_create_sampler(napi_env env, napi_callback_info info) {
1390
+ NAPI_ASSERT_ARGC(env, info, 2);
1391
+ CHECK_LIB_LOADED(env);
1392
+ WGPUDevice device = unwrap_ptr(env, _args[0]);
1393
+ if (!device) NAPI_THROW(env, "Invalid device");
1394
+
1395
+ WGPUSamplerDescriptor desc;
1396
+ memset(&desc, 0, sizeof(desc));
1397
+ desc.lodMaxClamp = 32.0f;
1398
+ desc.maxAnisotropy = 1;
1399
+
1400
+ napi_valuetype desc_type;
1401
+ napi_typeof(env, _args[1], &desc_type);
1402
+ if (desc_type == napi_object) {
1403
+ if (has_prop(env, _args[1], "magFilter"))
1404
+ desc.magFilter = filter_mode_from_string(env, get_prop(env, _args[1], "magFilter"));
1405
+ if (has_prop(env, _args[1], "minFilter"))
1406
+ desc.minFilter = filter_mode_from_string(env, get_prop(env, _args[1], "minFilter"));
1407
+ if (has_prop(env, _args[1], "mipmapFilter"))
1408
+ desc.mipmapFilter = filter_mode_from_string(env, get_prop(env, _args[1], "mipmapFilter"));
1409
+ if (has_prop(env, _args[1], "addressModeU"))
1410
+ desc.addressModeU = address_mode_from_string(env, get_prop(env, _args[1], "addressModeU"));
1411
+ if (has_prop(env, _args[1], "addressModeV"))
1412
+ desc.addressModeV = address_mode_from_string(env, get_prop(env, _args[1], "addressModeV"));
1413
+ if (has_prop(env, _args[1], "addressModeW"))
1414
+ desc.addressModeW = address_mode_from_string(env, get_prop(env, _args[1], "addressModeW"));
1415
+ if (has_prop(env, _args[1], "maxAnisotropy"))
1416
+ desc.maxAnisotropy = (uint16_t)get_uint32_prop(env, _args[1], "maxAnisotropy");
1417
+ }
1418
+
1419
+ WGPUSampler sampler = pfn_wgpuDeviceCreateSampler(device, &desc);
1420
+ if (!sampler) NAPI_THROW(env, "createSampler failed");
1421
+ return wrap_ptr(env, sampler);
1422
+ }
1423
+
1424
+ static napi_value doe_sampler_release(napi_env env, napi_callback_info info) {
1425
+ NAPI_ASSERT_ARGC(env, info, 1);
1426
+ void* p = unwrap_ptr(env, _args[0]);
1427
+ if (p) pfn_wgpuSamplerRelease(p);
1428
+ return NULL;
1429
+ }
1430
+
1431
+ /* ================================================================
1432
+ * Render Pipeline (noop stub — uses built-in Metal shaders)
1433
+ * createRenderPipeline(device)
1434
+ * ================================================================ */
1435
+
1436
+ static napi_value doe_create_render_pipeline(napi_env env, napi_callback_info info) {
1437
+ NAPI_ASSERT_ARGC(env, info, 1);
1438
+ CHECK_LIB_LOADED(env);
1439
+ WGPUDevice device = unwrap_ptr(env, _args[0]);
1440
+ if (!device) NAPI_THROW(env, "Invalid device");
1441
+ WGPURenderPipeline rp = pfn_wgpuDeviceCreateRenderPipeline(device, NULL);
1442
+ if (!rp) NAPI_THROW(env, "createRenderPipeline failed");
1443
+ return wrap_ptr(env, rp);
1444
+ }
1445
+
1446
+ static napi_value doe_render_pipeline_release(napi_env env, napi_callback_info info) {
1447
+ NAPI_ASSERT_ARGC(env, info, 1);
1448
+ void* p = unwrap_ptr(env, _args[0]);
1449
+ if (p) pfn_wgpuRenderPipelineRelease(p);
1450
+ return NULL;
1451
+ }
1452
+
1453
+ /* ================================================================
1454
+ * Render Pass
1455
+ * beginRenderPass(encoder, colorAttachments[])
1456
+ * ================================================================ */
1457
+
1458
+ static napi_value doe_begin_render_pass(napi_env env, napi_callback_info info) {
1459
+ NAPI_ASSERT_ARGC(env, info, 2);
1460
+ CHECK_LIB_LOADED(env);
1461
+ WGPUCommandEncoder enc = unwrap_ptr(env, _args[0]);
1462
+ if (!enc) NAPI_THROW(env, "Invalid encoder");
1463
+
1464
+ /* _args[1] is array of color attachments */
1465
+ uint32_t att_count = 0;
1466
+ napi_get_array_length(env, _args[1], &att_count);
1467
+ if (att_count == 0) NAPI_THROW(env, "beginRenderPass: need at least one color attachment");
1468
+
1469
+ WGPURenderPassColorAttachment* atts = (WGPURenderPassColorAttachment*)calloc(
1470
+ att_count, sizeof(WGPURenderPassColorAttachment));
1471
+ for (uint32_t i = 0; i < att_count; i++) {
1472
+ napi_value elem;
1473
+ napi_get_element(env, _args[1], i, &elem);
1474
+ atts[i].view = unwrap_ptr(env, get_prop(env, elem, "view"));
1475
+ atts[i].loadOp = 1; /* clear */
1476
+ atts[i].storeOp = 1; /* store */
1477
+ if (has_prop(env, elem, "clearValue") && prop_type(env, elem, "clearValue") == napi_object) {
1478
+ napi_value cv = get_prop(env, elem, "clearValue");
1479
+ double r = 0, g = 0, b = 0, a = 1;
1480
+ napi_value tmp;
1481
+ if (napi_get_named_property(env, cv, "r", &tmp) == napi_ok)
1482
+ napi_get_value_double(env, tmp, &r);
1483
+ if (napi_get_named_property(env, cv, "g", &tmp) == napi_ok)
1484
+ napi_get_value_double(env, tmp, &g);
1485
+ if (napi_get_named_property(env, cv, "b", &tmp) == napi_ok)
1486
+ napi_get_value_double(env, tmp, &b);
1487
+ if (napi_get_named_property(env, cv, "a", &tmp) == napi_ok)
1488
+ napi_get_value_double(env, tmp, &a);
1489
+ atts[i].clearValue = (WGPUColor){ r, g, b, a };
1490
+ }
1491
+ }
1492
+
1493
+ WGPURenderPassDescriptor desc;
1494
+ memset(&desc, 0, sizeof(desc));
1495
+ desc.colorAttachmentCount = att_count;
1496
+ desc.colorAttachments = atts;
1497
+
1498
+ WGPURenderPassEncoder pass = pfn_wgpuCommandEncoderBeginRenderPass(enc, &desc);
1499
+ free(atts);
1500
+ if (!pass) NAPI_THROW(env, "beginRenderPass failed");
1501
+ return wrap_ptr(env, pass);
1502
+ }
1503
+
1504
+ static napi_value doe_render_pass_set_pipeline(napi_env env, napi_callback_info info) {
1505
+ NAPI_ASSERT_ARGC(env, info, 2);
1506
+ pfn_wgpuRenderPassEncoderSetPipeline(
1507
+ unwrap_ptr(env, _args[0]), unwrap_ptr(env, _args[1]));
1508
+ return NULL;
1509
+ }
1510
+
1511
+ /* renderPassDraw(pass, vertexCount, instanceCount, firstVertex, firstInstance) */
1512
+ static napi_value doe_render_pass_draw(napi_env env, napi_callback_info info) {
1513
+ NAPI_ASSERT_ARGC(env, info, 5);
1514
+ WGPURenderPassEncoder pass = unwrap_ptr(env, _args[0]);
1515
+ uint32_t vc, ic, fv, fi;
1516
+ napi_get_value_uint32(env, _args[1], &vc);
1517
+ napi_get_value_uint32(env, _args[2], &ic);
1518
+ napi_get_value_uint32(env, _args[3], &fv);
1519
+ napi_get_value_uint32(env, _args[4], &fi);
1520
+ pfn_wgpuRenderPassEncoderDraw(pass, vc, ic, fv, fi);
1521
+ return NULL;
1522
+ }
1523
+
1524
+ static napi_value doe_render_pass_end(napi_env env, napi_callback_info info) {
1525
+ NAPI_ASSERT_ARGC(env, info, 1);
1526
+ pfn_wgpuRenderPassEncoderEnd(unwrap_ptr(env, _args[0]));
1527
+ return NULL;
1528
+ }
1529
+
1530
+ static napi_value doe_render_pass_release(napi_env env, napi_callback_info info) {
1531
+ NAPI_ASSERT_ARGC(env, info, 1);
1532
+ void* p = unwrap_ptr(env, _args[0]);
1533
+ if (p) pfn_wgpuRenderPassEncoderRelease(p);
1534
+ return NULL;
1535
+ }
1536
+
1537
+ /* ================================================================
1538
+ * Device capabilities: limits, features
1539
+ * ================================================================ */
1540
+
1541
+ static napi_value doe_device_get_limits(napi_env env, napi_callback_info info) {
1542
+ NAPI_ASSERT_ARGC(env, info, 1);
1543
+ CHECK_LIB_LOADED(env);
1544
+ WGPUDevice device = unwrap_ptr(env, _args[0]);
1545
+ if (!device) NAPI_THROW(env, "deviceGetLimits: null device");
1546
+
1547
+ WGPULimits limits;
1548
+ memset(&limits, 0, sizeof(limits));
1549
+ pfn_wgpuDeviceGetLimits(device, &limits);
1550
+
1551
+ napi_value obj;
1552
+ napi_create_object(env, &obj);
1553
+
1554
+ #define SET_U32(name) do { napi_value v; napi_create_uint32(env, limits.name, &v); napi_set_named_property(env, obj, #name, v); } while(0)
1555
+ #define SET_U64(name) do { napi_value v; napi_create_double(env, (double)limits.name, &v); napi_set_named_property(env, obj, #name, v); } while(0)
1556
+
1557
+ SET_U32(maxTextureDimension1D);
1558
+ SET_U32(maxTextureDimension2D);
1559
+ SET_U32(maxTextureDimension3D);
1560
+ SET_U32(maxTextureArrayLayers);
1561
+ SET_U32(maxBindGroups);
1562
+ SET_U32(maxBindGroupsPlusVertexBuffers);
1563
+ SET_U32(maxBindingsPerBindGroup);
1564
+ SET_U32(maxDynamicUniformBuffersPerPipelineLayout);
1565
+ SET_U32(maxDynamicStorageBuffersPerPipelineLayout);
1566
+ SET_U32(maxSampledTexturesPerShaderStage);
1567
+ SET_U32(maxSamplersPerShaderStage);
1568
+ SET_U32(maxStorageBuffersPerShaderStage);
1569
+ SET_U32(maxStorageTexturesPerShaderStage);
1570
+ SET_U32(maxUniformBuffersPerShaderStage);
1571
+ SET_U64(maxUniformBufferBindingSize);
1572
+ SET_U64(maxStorageBufferBindingSize);
1573
+ SET_U32(minUniformBufferOffsetAlignment);
1574
+ SET_U32(minStorageBufferOffsetAlignment);
1575
+ SET_U32(maxVertexBuffers);
1576
+ SET_U64(maxBufferSize);
1577
+ SET_U32(maxVertexAttributes);
1578
+ SET_U32(maxVertexBufferArrayStride);
1579
+ SET_U32(maxInterStageShaderVariables);
1580
+ SET_U32(maxColorAttachments);
1581
+ SET_U32(maxColorAttachmentBytesPerSample);
1582
+ SET_U32(maxComputeWorkgroupStorageSize);
1583
+ SET_U32(maxComputeInvocationsPerWorkgroup);
1584
+ SET_U32(maxComputeWorkgroupSizeX);
1585
+ SET_U32(maxComputeWorkgroupSizeY);
1586
+ SET_U32(maxComputeWorkgroupSizeZ);
1587
+ SET_U32(maxComputeWorkgroupsPerDimension);
1588
+
1589
+ #undef SET_U32
1590
+ #undef SET_U64
1591
+
1592
+ return obj;
1593
+ }
1594
+
1595
+ static napi_value doe_device_has_feature(napi_env env, napi_callback_info info) {
1596
+ NAPI_ASSERT_ARGC(env, info, 2);
1597
+ CHECK_LIB_LOADED(env);
1598
+ WGPUDevice device = unwrap_ptr(env, _args[0]);
1599
+ uint32_t feature;
1600
+ napi_get_value_uint32(env, _args[1], &feature);
1601
+ uint32_t result = pfn_wgpuDeviceHasFeature(device, feature);
1602
+ napi_value ret;
1603
+ napi_get_boolean(env, result != 0, &ret);
1604
+ return ret;
1605
+ }
1606
+
1072
1607
  /* ================================================================
1073
1608
  * Module initialization
1074
1609
  * ================================================================ */
@@ -1094,6 +1629,7 @@ static napi_value doe_module_init(napi_env env, napi_value exports) {
1094
1629
  EXPORT_FN("shaderModuleRelease", doe_shader_module_release),
1095
1630
  EXPORT_FN("createComputePipeline", doe_create_compute_pipeline),
1096
1631
  EXPORT_FN("computePipelineRelease", doe_compute_pipeline_release),
1632
+ EXPORT_FN("computePipelineGetBindGroupLayout", doe_compute_pipeline_get_bind_group_layout),
1097
1633
  EXPORT_FN("createBindGroupLayout", doe_create_bind_group_layout),
1098
1634
  EXPORT_FN("bindGroupLayoutRelease", doe_bind_group_layout_release),
1099
1635
  EXPORT_FN("createBindGroup", doe_create_bind_group),
@@ -1109,11 +1645,28 @@ static napi_value doe_module_init(napi_env env, napi_value exports) {
1109
1645
  EXPORT_FN("computePassSetPipeline", doe_compute_pass_set_pipeline),
1110
1646
  EXPORT_FN("computePassSetBindGroup", doe_compute_pass_set_bind_group),
1111
1647
  EXPORT_FN("computePassDispatchWorkgroups", doe_compute_pass_dispatch),
1648
+ EXPORT_FN("computePassDispatchWorkgroupsIndirect", doe_compute_pass_dispatch_indirect),
1112
1649
  EXPORT_FN("computePassEnd", doe_compute_pass_end),
1113
1650
  EXPORT_FN("computePassRelease", doe_compute_pass_release),
1114
1651
  EXPORT_FN("queueSubmit", doe_queue_submit),
1115
1652
  EXPORT_FN("queueWriteBuffer", doe_queue_write_buffer),
1653
+ EXPORT_FN("queueFlush", doe_queue_flush),
1116
1654
  EXPORT_FN("queueRelease", doe_queue_release),
1655
+ EXPORT_FN("createTexture", doe_create_texture),
1656
+ EXPORT_FN("textureRelease", doe_texture_release),
1657
+ EXPORT_FN("textureCreateView", doe_texture_create_view),
1658
+ EXPORT_FN("textureViewRelease", doe_texture_view_release),
1659
+ EXPORT_FN("createSampler", doe_create_sampler),
1660
+ EXPORT_FN("samplerRelease", doe_sampler_release),
1661
+ EXPORT_FN("createRenderPipeline", doe_create_render_pipeline),
1662
+ EXPORT_FN("renderPipelineRelease", doe_render_pipeline_release),
1663
+ EXPORT_FN("beginRenderPass", doe_begin_render_pass),
1664
+ EXPORT_FN("renderPassSetPipeline", doe_render_pass_set_pipeline),
1665
+ EXPORT_FN("renderPassDraw", doe_render_pass_draw),
1666
+ EXPORT_FN("renderPassEnd", doe_render_pass_end),
1667
+ EXPORT_FN("renderPassRelease", doe_render_pass_release),
1668
+ EXPORT_FN("deviceGetLimits", doe_device_get_limits),
1669
+ EXPORT_FN("deviceHasFeature", doe_device_has_feature),
1117
1670
  };
1118
1671
 
1119
1672
  size_t count = sizeof(descriptors) / sizeof(descriptors[0]);