@simulatte/webgpu 0.3.1 → 0.3.2

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +27 -12
  2. package/LICENSE +191 -0
  3. package/README.md +55 -41
  4. package/api-contract.md +67 -49
  5. package/architecture.md +317 -0
  6. package/assets/package-layers.svg +3 -3
  7. package/docs/doe-api-reference.html +1842 -0
  8. package/doe-api-design.md +237 -0
  9. package/examples/doe-api/README.md +19 -0
  10. package/examples/doe-api/buffers-readback.js +3 -2
  11. package/examples/{doe-routines/compute-once-like-input.js → doe-api/compute-one-shot-like-input.js} +1 -1
  12. package/examples/{doe-routines/compute-once-matmul.js → doe-api/compute-one-shot-matmul.js} +2 -2
  13. package/examples/{doe-routines/compute-once-multiple-inputs.js → doe-api/compute-one-shot-multiple-inputs.js} +1 -1
  14. package/examples/{doe-routines/compute-once.js → doe-api/compute-one-shot.js} +1 -1
  15. package/examples/doe-api/{compile-and-dispatch.js → kernel-create-and-dispatch.js} +4 -6
  16. package/examples/doe-api/{compute-dispatch.js → kernel-run.js} +4 -6
  17. package/headless-webgpu-comparison.md +3 -3
  18. package/jsdoc-style-guide.md +435 -0
  19. package/native/doe_napi.c +1481 -84
  20. package/package.json +18 -6
  21. package/prebuilds/darwin-arm64/doe_napi.node +0 -0
  22. package/prebuilds/darwin-arm64/libwebgpu_doe.dylib +0 -0
  23. package/prebuilds/darwin-arm64/metadata.json +5 -5
  24. package/prebuilds/linux-x64/metadata.json +1 -1
  25. package/scripts/generate-doe-api-docs.js +1607 -0
  26. package/scripts/generate-readme-assets.js +3 -3
  27. package/src/build_metadata.js +7 -4
  28. package/src/bun-ffi.js +1229 -474
  29. package/src/bun.js +5 -1
  30. package/src/compute.d.ts +16 -7
  31. package/src/compute.js +84 -53
  32. package/src/full.d.ts +16 -7
  33. package/src/full.js +12 -10
  34. package/src/index.js +679 -1324
  35. package/src/runtime_cli.js +17 -17
  36. package/src/shared/capabilities.js +144 -0
  37. package/src/shared/compiler-errors.js +78 -0
  38. package/src/shared/encoder-surface.js +295 -0
  39. package/src/shared/full-surface.js +514 -0
  40. package/src/shared/public-surface.js +82 -0
  41. package/src/shared/resource-lifecycle.js +120 -0
  42. package/src/shared/validation.js +495 -0
  43. package/src/webgpu_constants.js +30 -0
  44. package/support-contracts.md +2 -2
  45. package/compat-scope.md +0 -46
  46. package/layering-plan.md +0 -259
  47. package/src/auto_bind_group_layout.js +0 -32
  48. package/src/doe.d.ts +0 -184
  49. package/src/doe.js +0 -641
  50. package/zig-source-inventory.md +0 -468
package/src/bun-ffi.js CHANGED
@@ -4,89 +4,113 @@ import { dirname, resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { createDoeRuntime, runDawnVsDoeCompare } from "./runtime_cli.js";
6
6
  import { loadDoeBuildMetadata } from "./build_metadata.js";
7
- import { inferAutoBindGroupLayouts } from "./auto_bind_group_layout.js";
7
+ import { globals } from "./webgpu_constants.js";
8
+ import {
9
+ UINT32_MAX,
10
+ failValidation,
11
+ describeResourceLabel,
12
+ initResource,
13
+ assertObject,
14
+ assertArray,
15
+ assertBoolean,
16
+ assertNonEmptyString,
17
+ assertIntegerInRange,
18
+ assertOptionalIntegerInRange,
19
+ assertLiveResource,
20
+ destroyResource,
21
+ validatePositiveInteger,
22
+ } from "./shared/resource-lifecycle.js";
23
+ import {
24
+ publishLimits,
25
+ publishFeatures,
26
+ } from "./shared/capabilities.js";
27
+ import {
28
+ ALL_BUFFER_USAGE_BITS,
29
+ assertBufferDescriptor,
30
+ assertTextureSize,
31
+ assertBindGroupResource as normalizeBindGroupResource,
32
+ normalizeTextureDimension,
33
+ normalizeBindGroupLayoutEntry,
34
+ autoLayoutEntriesFromNativeBindings,
35
+ } from "./shared/validation.js";
36
+ import {
37
+ setupGlobalsOnTarget,
38
+ requestAdapterFromCreate,
39
+ requestDeviceFromRequestAdapter,
40
+ buildProviderInfo,
41
+ libraryFlavor,
42
+ } from "./shared/public-surface.js";
43
+ import {
44
+ shaderCheckFailure,
45
+ enrichNativeCompilerError,
46
+ compilerErrorFromMessage,
47
+ } from "./shared/compiler-errors.js";
48
+ import {
49
+ createFullSurfaceClasses,
50
+ } from "./shared/full-surface.js";
51
+ import {
52
+ createEncoderClasses,
53
+ } from "./shared/encoder-surface.js";
8
54
 
9
55
  const __dirname = dirname(fileURLToPath(import.meta.url));
10
56
  const PACKAGE_ROOT = resolve(__dirname, "..");
11
57
 
58
+ export { globals };
59
+
12
60
  const CALLBACK_MODE_ALLOW_PROCESS_EVENTS = 2;
61
+ const WGPU_STATUS_SUCCESS = 1;
13
62
  const REQUEST_ADAPTER_STATUS_SUCCESS = 1;
14
63
  const REQUEST_DEVICE_STATUS_SUCCESS = 1;
15
64
  const MAP_ASYNC_STATUS_SUCCESS = 1;
16
65
  const STYPE_SHADER_SOURCE_WGSL = 0x00000002;
17
66
  const PROCESS_EVENTS_TIMEOUT_NS = 5_000_000_000;
67
+ let processEventsTimeoutNs = PROCESS_EVENTS_TIMEOUT_NS;
68
+ const SAMPLER_BINDING_TYPE = Object.freeze({
69
+ filtering: 2,
70
+ "non-filtering": 3,
71
+ comparison: 4,
72
+ });
73
+ const TEXTURE_SAMPLE_TYPE = Object.freeze({
74
+ float: 2,
75
+ "unfilterable-float": 3,
76
+ depth: 4,
77
+ sint: 5,
78
+ uint: 6,
79
+ });
80
+ const TEXTURE_VIEW_DIMENSION = Object.freeze({
81
+ "1d": 1,
82
+ "2d": 2,
83
+ "2d-array": 3,
84
+ cube: 4,
85
+ "cube-array": 5,
86
+ "3d": 6,
87
+ });
88
+ const STORAGE_TEXTURE_ACCESS = Object.freeze({
89
+ "write-only": 2,
90
+ "read-only": 3,
91
+ "read-write": 4,
92
+ });
18
93
 
19
94
  // Struct layout constants for 64-bit platforms (LP64 / LLP64).
20
95
  const PTR_SIZE = 8;
21
96
  const SIZE_T_SIZE = 8;
22
-
23
- // WebGPU enum constants (standard values) — matches index.js.
24
- export const globals = {
25
- GPUBufferUsage: {
26
- MAP_READ: 0x0001,
27
- MAP_WRITE: 0x0002,
28
- COPY_SRC: 0x0004,
29
- COPY_DST: 0x0008,
30
- INDEX: 0x0010,
31
- VERTEX: 0x0020,
32
- UNIFORM: 0x0040,
33
- STORAGE: 0x0080,
34
- INDIRECT: 0x0100,
35
- QUERY_RESOLVE: 0x0200,
36
- },
37
- GPUShaderStage: {
38
- VERTEX: 0x1,
39
- FRAGMENT: 0x2,
40
- COMPUTE: 0x4,
41
- },
42
- GPUMapMode: {
43
- READ: 0x0001,
44
- WRITE: 0x0002,
45
- },
46
- GPUTextureUsage: {
47
- COPY_SRC: 0x01,
48
- COPY_DST: 0x02,
49
- TEXTURE_BINDING: 0x04,
50
- STORAGE_BINDING: 0x08,
51
- RENDER_ATTACHMENT: 0x10,
52
- },
53
- };
54
-
55
- const DOE_LIMITS = Object.freeze({
56
- maxTextureDimension1D: 16384,
57
- maxTextureDimension2D: 16384,
58
- maxTextureDimension3D: 2048,
59
- maxTextureArrayLayers: 2048,
60
- maxBindGroups: 4,
61
- maxBindGroupsPlusVertexBuffers: 24,
62
- maxBindingsPerBindGroup: 1000,
63
- maxDynamicUniformBuffersPerPipelineLayout: 8,
64
- maxDynamicStorageBuffersPerPipelineLayout: 4,
65
- maxSampledTexturesPerShaderStage: 16,
66
- maxSamplersPerShaderStage: 16,
67
- maxStorageBuffersPerShaderStage: 8,
68
- maxStorageTexturesPerShaderStage: 4,
69
- maxUniformBuffersPerShaderStage: 12,
70
- maxUniformBufferBindingSize: 65536,
71
- maxStorageBufferBindingSize: 134217728,
72
- minUniformBufferOffsetAlignment: 256,
73
- minStorageBufferOffsetAlignment: 32,
74
- maxVertexBuffers: 8,
75
- maxBufferSize: 268435456,
76
- maxVertexAttributes: 16,
77
- maxVertexBufferArrayStride: 2048,
78
- maxInterStageShaderVariables: 16,
79
- maxColorAttachments: 8,
80
- maxColorAttachmentBytesPerSample: 32,
81
- maxComputeWorkgroupStorageSize: 32768,
82
- maxComputeInvocationsPerWorkgroup: 1024,
83
- maxComputeWorkgroupSizeX: 1024,
84
- maxComputeWorkgroupSizeY: 1024,
85
- maxComputeWorkgroupSizeZ: 64,
86
- maxComputeWorkgroupsPerDimension: 65535,
87
- });
88
-
89
- const DOE_FEATURES = Object.freeze(new Set(["shader-f16"]));
97
+ const WGPU_BUFFER_DESCRIPTOR_SIZE = 48;
98
+ const WGPU_SHADER_SOURCE_WGSL_SIZE = 32;
99
+ const WGPU_SHADER_MODULE_DESCRIPTOR_SIZE = 24;
100
+ const WGPU_COMPUTE_PIPELINE_DESCRIPTOR_SIZE = 80;
101
+ const WGPU_RENDER_PIPELINE_DESCRIPTOR_SIZE = 168;
102
+ const WGPU_BIND_GROUP_LAYOUT_DESCRIPTOR_SIZE = 40;
103
+ const WGPU_BIND_GROUP_DESCRIPTOR_SIZE = 48;
104
+ const WGPU_PIPELINE_LAYOUT_DESCRIPTOR_SIZE = 48;
105
+ const WGPU_RENDER_PASS_DESCRIPTOR_SIZE = 64;
106
+ const WGPU_LIMITS_SIZE = 152;
107
+ const WGPU_RENDER_VERTEX_STATE_SIZE = 64;
108
+ const WGPU_RENDER_COLOR_TARGET_STATE_SIZE = 32;
109
+ const WGPU_RENDER_FRAGMENT_STATE_SIZE = 64;
110
+ const WGPU_VERTEX_ATTRIBUTE_SIZE = 32;
111
+ const WGPU_VERTEX_BUFFER_LAYOUT_SIZE = 40;
112
+ const WGPU_DEPTH_STENCIL_STATE_SIZE = 72;
113
+ const WGPU_RENDER_PASS_DEPTH_STENCIL_ATTACHMENT_SIZE = 48;
90
114
 
91
115
  // ---------------------------------------------------------------------------
92
116
  // Library resolution
@@ -98,8 +122,8 @@ function resolveDoeLibraryPath() {
98
122
  const ext = LIB_EXT[process.platform] ?? "so";
99
123
  const candidates = [
100
124
  process.env.DOE_WEBGPU_LIB,
101
- resolve(PACKAGE_ROOT, "prebuilds", `${process.platform}-${process.arch}`, `libwebgpu_doe.${ext}`),
102
125
  resolve(PACKAGE_ROOT, "..", "..", "zig", "zig-out", "lib", `libwebgpu_doe.${ext}`),
126
+ resolve(PACKAGE_ROOT, "prebuilds", `${process.platform}-${process.arch}`, `libwebgpu_doe.${ext}`),
103
127
  resolve(process.cwd(), "zig", "zig-out", "lib", `libwebgpu_doe.${ext}`),
104
128
  ];
105
129
  for (const c of candidates) {
@@ -131,8 +155,16 @@ function openLibrary(path) {
131
155
  // Adapter/Device (flat helpers)
132
156
  doeRequestAdapterFlat: { args: [FFIType.ptr, FFIType.ptr, FFIType.u32, FFIType.ptr, FFIType.ptr, FFIType.ptr], returns: FFIType.u64 },
133
157
  doeRequestDeviceFlat: { args: [FFIType.ptr, FFIType.ptr, FFIType.u32, FFIType.ptr, FFIType.ptr, FFIType.ptr], returns: FFIType.u64 },
158
+ doeNativeAdapterHasFeature: { args: [FFIType.ptr, FFIType.u32], returns: FFIType.u32 },
159
+ doeNativeAdapterGetLimits: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.u32 },
160
+ doeNativeDeviceHasFeature: { args: [FFIType.ptr, FFIType.u32], returns: FFIType.u32 },
161
+ doeNativeDeviceGetLimits: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.u32 },
134
162
  wgpuAdapterRelease: { args: [FFIType.ptr], returns: FFIType.void },
163
+ wgpuAdapterHasFeature: { args: [FFIType.ptr, FFIType.u32], returns: FFIType.u32 },
164
+ wgpuAdapterGetLimits: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.u32 },
135
165
  wgpuDeviceRelease: { args: [FFIType.ptr], returns: FFIType.void },
166
+ wgpuDeviceHasFeature: { args: [FFIType.ptr, FFIType.u32], returns: FFIType.u32 },
167
+ wgpuDeviceGetLimits: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.u32 },
136
168
  wgpuDeviceGetQueue: { args: [FFIType.ptr], returns: FFIType.ptr },
137
169
 
138
170
  // Buffer
@@ -153,6 +185,7 @@ function openLibrary(path) {
153
185
  // Shader
154
186
  wgpuDeviceCreateShaderModule: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.ptr },
155
187
  wgpuShaderModuleRelease: { args: [FFIType.ptr], returns: FFIType.void },
188
+ doeNativeShaderModuleGetBindings: { args: [FFIType.ptr, FFIType.ptr, FFIType.u64], returns: FFIType.u64 },
156
189
 
157
190
  // Compute pipeline
158
191
  wgpuDeviceCreateComputePipeline: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.ptr },
@@ -172,6 +205,8 @@ function openLibrary(path) {
172
205
  wgpuCommandEncoderRelease: { args: [FFIType.ptr], returns: FFIType.void },
173
206
  wgpuCommandEncoderBeginComputePass: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.ptr },
174
207
  wgpuCommandEncoderCopyBufferToBuffer: { args: [FFIType.ptr, FFIType.ptr, FFIType.u64, FFIType.ptr, FFIType.u64, FFIType.u64], returns: FFIType.void },
208
+ wgpuCommandEncoderCopyTextureToBuffer: { args: [FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.ptr], returns: FFIType.void },
209
+ doeNativeCommandEncoderCopyTextureToBuffer: { args: [FFIType.ptr, FFIType.ptr, FFIType.u32, FFIType.ptr, FFIType.u64, FFIType.u32, FFIType.u32, FFIType.u32, FFIType.u32, FFIType.u32], returns: FFIType.void },
175
210
  wgpuCommandEncoderFinish: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.ptr },
176
211
  wgpuCommandBufferRelease: { args: [FFIType.ptr], returns: FFIType.void },
177
212
 
@@ -200,7 +235,11 @@ function openLibrary(path) {
200
235
  // Render pass
201
236
  wgpuCommandEncoderBeginRenderPass: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.ptr },
202
237
  wgpuRenderPassEncoderSetPipeline: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.void },
238
+ wgpuRenderPassEncoderSetBindGroup: { args: [FFIType.ptr, FFIType.u32, FFIType.ptr, FFIType.u64, FFIType.ptr], returns: FFIType.void },
239
+ wgpuRenderPassEncoderSetVertexBuffer: { args: [FFIType.ptr, FFIType.u32, FFIType.ptr, FFIType.u64, FFIType.u64], returns: FFIType.void },
240
+ wgpuRenderPassEncoderSetIndexBuffer: { args: [FFIType.ptr, FFIType.ptr, FFIType.u32, FFIType.u64, FFIType.u64], returns: FFIType.void },
203
241
  wgpuRenderPassEncoderDraw: { args: [FFIType.ptr, FFIType.u32, FFIType.u32, FFIType.u32, FFIType.u32], returns: FFIType.void },
242
+ wgpuRenderPassEncoderDrawIndexed: { args: [FFIType.ptr, FFIType.u32, FFIType.u32, FFIType.u32, FFIType.i32, FFIType.u32], returns: FFIType.void },
204
243
  wgpuRenderPassEncoderEnd: { args: [FFIType.ptr], returns: FFIType.void },
205
244
  wgpuRenderPassEncoderRelease: { args: [FFIType.ptr], returns: FFIType.void },
206
245
  };
@@ -209,6 +248,67 @@ function openLibrary(path) {
209
248
  args: [FFIType.ptr, FFIType.u32],
210
249
  returns: FFIType.ptr,
211
250
  };
251
+ symbols.doeNativeCheckShaderSource = {
252
+ args: [FFIType.ptr, FFIType.u64],
253
+ returns: FFIType.u32,
254
+ };
255
+ symbols.doeNativeCopyLastErrorMessage = {
256
+ args: [FFIType.ptr, FFIType.u64],
257
+ returns: FFIType.u64,
258
+ };
259
+ symbols.doeNativeCopyLastErrorStage = {
260
+ args: [FFIType.ptr, FFIType.u64],
261
+ returns: FFIType.u64,
262
+ };
263
+ symbols.doeNativeCopyLastErrorKind = {
264
+ args: [FFIType.ptr, FFIType.u64],
265
+ returns: FFIType.u64,
266
+ };
267
+ symbols.doeNativeGetLastErrorLine = {
268
+ args: [],
269
+ returns: FFIType.u32,
270
+ };
271
+ symbols.doeNativeGetLastErrorColumn = {
272
+ args: [],
273
+ returns: FFIType.u32,
274
+ };
275
+ symbols.doeNativeDeviceCreateQuerySet = {
276
+ args: [FFIType.ptr, FFIType.u32, FFIType.u32],
277
+ returns: FFIType.ptr,
278
+ };
279
+ symbols.doeNativeCommandEncoderWriteTimestamp = {
280
+ args: [FFIType.ptr, FFIType.ptr, FFIType.u32],
281
+ returns: FFIType.void,
282
+ };
283
+ symbols.doeNativeCommandEncoderResolveQuerySet = {
284
+ args: [FFIType.ptr, FFIType.ptr, FFIType.u32, FFIType.u32, FFIType.ptr, FFIType.u64],
285
+ returns: FFIType.void,
286
+ };
287
+ symbols.doeNativeQuerySetDestroy = {
288
+ args: [FFIType.ptr],
289
+ returns: FFIType.void,
290
+ };
291
+ symbols.doeNativeQueueFlush = {
292
+ args: [FFIType.ptr],
293
+ returns: FFIType.void,
294
+ };
295
+ symbols.doeNativeComputeDispatchFlush = {
296
+ args: [
297
+ FFIType.ptr, // queue
298
+ FFIType.ptr, // pipeline
299
+ FFIType.ptr, // bindGroups (ptr array)
300
+ FFIType.u32, // bgCount
301
+ FFIType.u32, // x
302
+ FFIType.u32, // y
303
+ FFIType.u32, // z
304
+ FFIType.ptr, // copySrc
305
+ FFIType.u64, // copySrcOff
306
+ FFIType.ptr, // copyDst
307
+ FFIType.u64, // copyDstOff
308
+ FFIType.u64, // copySize
309
+ ],
310
+ returns: FFIType.void,
311
+ };
212
312
  }
213
313
  return dlopen(path, symbols);
214
314
  }
@@ -221,6 +321,194 @@ function openLibrary(path) {
221
321
  // ---------------------------------------------------------------------------
222
322
 
223
323
  const encoder = new TextEncoder();
324
+ const decoder = new TextDecoder();
325
+
326
+ const LIMIT_OFFSETS = Object.freeze({
327
+ maxTextureDimension1D: 8,
328
+ maxTextureDimension2D: 12,
329
+ maxTextureDimension3D: 16,
330
+ maxTextureArrayLayers: 20,
331
+ maxBindGroups: 24,
332
+ maxBindGroupsPlusVertexBuffers: 28,
333
+ maxBindingsPerBindGroup: 32,
334
+ maxDynamicUniformBuffersPerPipelineLayout: 36,
335
+ maxDynamicStorageBuffersPerPipelineLayout: 40,
336
+ maxSampledTexturesPerShaderStage: 44,
337
+ maxSamplersPerShaderStage: 48,
338
+ maxStorageBuffersPerShaderStage: 52,
339
+ maxStorageTexturesPerShaderStage: 56,
340
+ maxUniformBuffersPerShaderStage: 60,
341
+ maxUniformBufferBindingSize: 64,
342
+ maxStorageBufferBindingSize: 72,
343
+ minUniformBufferOffsetAlignment: 80,
344
+ minStorageBufferOffsetAlignment: 84,
345
+ maxVertexBuffers: 88,
346
+ maxBufferSize: 96,
347
+ maxVertexAttributes: 104,
348
+ maxVertexBufferArrayStride: 108,
349
+ maxInterStageShaderVariables: 112,
350
+ maxColorAttachments: 116,
351
+ maxColorAttachmentBytesPerSample: 120,
352
+ maxComputeWorkgroupStorageSize: 124,
353
+ maxComputeInvocationsPerWorkgroup: 128,
354
+ maxComputeWorkgroupSizeX: 132,
355
+ maxComputeWorkgroupSizeY: 136,
356
+ maxComputeWorkgroupSizeZ: 140,
357
+ maxComputeWorkgroupsPerDimension: 144,
358
+ });
359
+
360
+ function copyLastErrorMessage() {
361
+ const fn = wgpu?.symbols?.doeNativeCopyLastErrorMessage;
362
+ if (typeof fn !== "function") return "";
363
+ const buf = new Uint8Array(4096);
364
+ const len = Number(fn(buf, BigInt(buf.length)));
365
+ if (len <= 1) return "";
366
+ return decoder.decode(buf.subarray(0, Math.max(0, len - 1)));
367
+ }
368
+
369
+ function decodeLimits(raw) {
370
+ const view = new DataView(raw);
371
+ return Object.freeze({
372
+ maxTextureDimension1D: view.getUint32(LIMIT_OFFSETS.maxTextureDimension1D, true),
373
+ maxTextureDimension2D: view.getUint32(LIMIT_OFFSETS.maxTextureDimension2D, true),
374
+ maxTextureDimension3D: view.getUint32(LIMIT_OFFSETS.maxTextureDimension3D, true),
375
+ maxTextureArrayLayers: view.getUint32(LIMIT_OFFSETS.maxTextureArrayLayers, true),
376
+ maxBindGroups: view.getUint32(LIMIT_OFFSETS.maxBindGroups, true),
377
+ maxBindGroupsPlusVertexBuffers: view.getUint32(LIMIT_OFFSETS.maxBindGroupsPlusVertexBuffers, true),
378
+ maxBindingsPerBindGroup: view.getUint32(LIMIT_OFFSETS.maxBindingsPerBindGroup, true),
379
+ maxDynamicUniformBuffersPerPipelineLayout: view.getUint32(LIMIT_OFFSETS.maxDynamicUniformBuffersPerPipelineLayout, true),
380
+ maxDynamicStorageBuffersPerPipelineLayout: view.getUint32(LIMIT_OFFSETS.maxDynamicStorageBuffersPerPipelineLayout, true),
381
+ maxSampledTexturesPerShaderStage: view.getUint32(LIMIT_OFFSETS.maxSampledTexturesPerShaderStage, true),
382
+ maxSamplersPerShaderStage: view.getUint32(LIMIT_OFFSETS.maxSamplersPerShaderStage, true),
383
+ maxStorageBuffersPerShaderStage: view.getUint32(LIMIT_OFFSETS.maxStorageBuffersPerShaderStage, true),
384
+ maxStorageTexturesPerShaderStage: view.getUint32(LIMIT_OFFSETS.maxStorageTexturesPerShaderStage, true),
385
+ maxUniformBuffersPerShaderStage: view.getUint32(LIMIT_OFFSETS.maxUniformBuffersPerShaderStage, true),
386
+ maxUniformBufferBindingSize: Number(view.getBigUint64(LIMIT_OFFSETS.maxUniformBufferBindingSize, true)),
387
+ maxStorageBufferBindingSize: Number(view.getBigUint64(LIMIT_OFFSETS.maxStorageBufferBindingSize, true)),
388
+ minUniformBufferOffsetAlignment: view.getUint32(LIMIT_OFFSETS.minUniformBufferOffsetAlignment, true),
389
+ minStorageBufferOffsetAlignment: view.getUint32(LIMIT_OFFSETS.minStorageBufferOffsetAlignment, true),
390
+ maxVertexBuffers: view.getUint32(LIMIT_OFFSETS.maxVertexBuffers, true),
391
+ maxBufferSize: Number(view.getBigUint64(LIMIT_OFFSETS.maxBufferSize, true)),
392
+ maxVertexAttributes: view.getUint32(LIMIT_OFFSETS.maxVertexAttributes, true),
393
+ maxVertexBufferArrayStride: view.getUint32(LIMIT_OFFSETS.maxVertexBufferArrayStride, true),
394
+ maxInterStageShaderVariables: view.getUint32(LIMIT_OFFSETS.maxInterStageShaderVariables, true),
395
+ maxColorAttachments: view.getUint32(LIMIT_OFFSETS.maxColorAttachments, true),
396
+ maxColorAttachmentBytesPerSample: view.getUint32(LIMIT_OFFSETS.maxColorAttachmentBytesPerSample, true),
397
+ maxComputeWorkgroupStorageSize: view.getUint32(LIMIT_OFFSETS.maxComputeWorkgroupStorageSize, true),
398
+ maxComputeInvocationsPerWorkgroup: view.getUint32(LIMIT_OFFSETS.maxComputeInvocationsPerWorkgroup, true),
399
+ maxComputeWorkgroupSizeX: view.getUint32(LIMIT_OFFSETS.maxComputeWorkgroupSizeX, true),
400
+ maxComputeWorkgroupSizeY: view.getUint32(LIMIT_OFFSETS.maxComputeWorkgroupSizeY, true),
401
+ maxComputeWorkgroupSizeZ: view.getUint32(LIMIT_OFFSETS.maxComputeWorkgroupSizeZ, true),
402
+ maxComputeWorkgroupsPerDimension: view.getUint32(LIMIT_OFFSETS.maxComputeWorkgroupsPerDimension, true),
403
+ });
404
+ }
405
+
406
+ function queryLimits(handle, fnName) {
407
+ const fn = wgpu?.symbols?.[fnName];
408
+ if (typeof fn !== "function" || !handle) return publishLimits(null);
409
+ const raw = new ArrayBuffer(WGPU_LIMITS_SIZE);
410
+ const status = Number(fn(handle, new Uint8Array(raw)));
411
+ if (status !== WGPU_STATUS_SUCCESS) return publishLimits(null);
412
+ return publishLimits(decodeLimits(raw));
413
+ }
414
+
415
+ function queryLimitsByPreference(handle, fnNames) {
416
+ for (const fnName of fnNames) {
417
+ const fn = wgpu?.symbols?.[fnName];
418
+ if (typeof fn !== "function" || !handle) continue;
419
+ const raw = new ArrayBuffer(WGPU_LIMITS_SIZE);
420
+ const status = Number(fn(handle, new Uint8Array(raw)));
421
+ if (status !== WGPU_STATUS_SUCCESS) continue;
422
+ return publishLimits(decodeLimits(raw));
423
+ }
424
+ return publishLimits(null);
425
+ }
426
+
427
+ function adapterLimits(handle) {
428
+ return queryLimitsByPreference(handle, ["doeNativeAdapterGetLimits", "wgpuAdapterGetLimits"]);
429
+ }
430
+
431
+ function deviceLimits(handle) {
432
+ return queryLimitsByPreference(handle, ["doeNativeDeviceGetLimits", "wgpuDeviceGetLimits"]);
433
+ }
434
+
435
+ function adapterFeatures(handle) {
436
+ const fn = wgpu?.symbols?.doeNativeAdapterHasFeature ?? wgpu?.symbols?.wgpuAdapterHasFeature;
437
+ return publishFeatures(
438
+ typeof fn === "function" && handle
439
+ ? (feature) => Number(fn(handle, feature)) !== 0
440
+ : null,
441
+ );
442
+ }
443
+
444
+ function deviceFeatures(handle) {
445
+ const fn = wgpu?.symbols?.doeNativeDeviceHasFeature ?? wgpu?.symbols?.wgpuDeviceHasFeature;
446
+ return publishFeatures(
447
+ typeof fn === "function" && handle
448
+ ? (feature) => Number(fn(handle, feature)) !== 0
449
+ : null,
450
+ );
451
+ }
452
+
453
+ function copyNativeErrorMeta(symbolName) {
454
+ const fn = wgpu?.symbols?.[symbolName];
455
+ if (typeof fn !== "function") return "";
456
+ const scratch = new Uint8Array(256);
457
+ const len = Number(fn(scratch, scratch.length));
458
+ if (!len) return "";
459
+ return decoder.decode(scratch.subarray(0, Math.min(len, scratch.length - 1)));
460
+ }
461
+
462
+ const fastPathStats = { dispatchFlush: 0, flushAndMap: 0 };
463
+
464
+ /**
465
+ * Read structured error fields (stage, kind, line, column) from the native
466
+ * last-error ABI. Uses `doeNativeGetLastErrorLine` / `doeNativeGetLastErrorColumn`
467
+ * when available; falls back to string copy functions for stage/kind.
468
+ * Returns null when the native symbols are absent (pre-structured-error builds).
469
+ */
470
+ function readLastErrorFields() {
471
+ const stageFn = wgpu?.symbols?.doeNativeCopyLastErrorStage;
472
+ const kindFn = wgpu?.symbols?.doeNativeCopyLastErrorKind;
473
+ if (typeof stageFn !== "function" && typeof kindFn !== "function") return null;
474
+ const stage = copyNativeErrorMeta("doeNativeCopyLastErrorStage");
475
+ const kind = copyNativeErrorMeta("doeNativeCopyLastErrorKind");
476
+ const lineFn = wgpu?.symbols?.doeNativeGetLastErrorLine;
477
+ const colFn = wgpu?.symbols?.doeNativeGetLastErrorColumn;
478
+ const line = typeof lineFn === "function" ? Number(lineFn()) : 0;
479
+ const column = typeof colFn === "function" ? Number(colFn()) : 0;
480
+ return {
481
+ stage: stage || undefined,
482
+ kind: kind || undefined,
483
+ line: line > 0 ? line : undefined,
484
+ column: column > 0 ? column : undefined,
485
+ };
486
+ }
487
+
488
+ function preflightShaderSource(code) {
489
+ const fn = wgpu?.symbols?.doeNativeCheckShaderSource;
490
+ if (typeof fn !== "function") {
491
+ return { ok: true, stage: "", kind: "", message: "", reasons: [] };
492
+ }
493
+ const codeBytes = encoder.encode(code);
494
+ const ok = Number(fn(codeBytes, codeBytes.length)) !== 0;
495
+ if (ok) return { ok: true, stage: "", kind: "", message: "", reasons: [] };
496
+ const message = copyNativeErrorMeta("doeNativeCopyLastErrorMessage");
497
+ const lineFn = wgpu?.symbols?.doeNativeGetLastErrorLine;
498
+ const colFn = wgpu?.symbols?.doeNativeGetLastErrorColumn;
499
+ const line = typeof lineFn === "function" ? Number(lineFn()) : 0;
500
+ const column = typeof colFn === "function" ? Number(colFn()) : 0;
501
+ const out = {
502
+ ok: false,
503
+ stage: copyNativeErrorMeta("doeNativeCopyLastErrorStage"),
504
+ kind: copyNativeErrorMeta("doeNativeCopyLastErrorKind"),
505
+ message,
506
+ reasons: message ? [message] : [],
507
+ };
508
+ if (line > 0) out.line = line;
509
+ if (column > 0) out.column = column;
510
+ return out;
511
+ }
224
512
 
225
513
  function writeStringView(view, offset, strBytes) {
226
514
  if (strBytes) {
@@ -238,7 +526,7 @@ function writePtr(view, offset, ptr) {
238
526
 
239
527
  // WGPUBufferDescriptor: { nextInChain:ptr@0, label:sv@8, usage:u64@24, size:u64@32, mappedAtCreation:u32@40 } = 48
240
528
  function buildBufferDescriptor(descriptor) {
241
- const buf = new ArrayBuffer(48);
529
+ const buf = new ArrayBuffer(WGPU_BUFFER_DESCRIPTOR_SIZE);
242
530
  const v = new DataView(buf);
243
531
  // nextInChain = null
244
532
  writePtr(v, 0, null);
@@ -258,14 +546,14 @@ function buildBufferDescriptor(descriptor) {
258
546
  function buildShaderModuleDescriptor(code) {
259
547
  const codeBytes = encoder.encode(code);
260
548
 
261
- const wgslBuf = new ArrayBuffer(32);
549
+ const wgslBuf = new ArrayBuffer(WGPU_SHADER_SOURCE_WGSL_SIZE);
262
550
  const wgslView = new DataView(wgslBuf);
263
551
  writePtr(wgslView, 0, null);
264
552
  wgslView.setUint32(8, STYPE_SHADER_SOURCE_WGSL, true);
265
553
  writeStringView(wgslView, 16, codeBytes);
266
554
  const wgslArr = new Uint8Array(wgslBuf);
267
555
 
268
- const descBuf = new ArrayBuffer(24);
556
+ const descBuf = new ArrayBuffer(WGPU_SHADER_MODULE_DESCRIPTOR_SIZE);
269
557
  const descView = new DataView(descBuf);
270
558
  writePtr(descView, 0, bunPtr(wgslArr));
271
559
  writeStringView(descView, 8, null);
@@ -279,7 +567,7 @@ function buildShaderModuleDescriptor(code) {
279
567
  // Total descriptor: 24 + 8 (layout) + 48 (compute) = 80
280
568
  function buildComputePipelineDescriptor(shaderModulePtr, entryPoint, layoutPtr) {
281
569
  const epBytes = encoder.encode(entryPoint);
282
- const buf = new ArrayBuffer(80);
570
+ const buf = new ArrayBuffer(WGPU_COMPUTE_PIPELINE_DESCRIPTOR_SIZE);
283
571
  const v = new DataView(buf);
284
572
  // nextInChain
285
573
  writePtr(v, 0, null);
@@ -300,6 +588,113 @@ function buildComputePipelineDescriptor(shaderModulePtr, entryPoint, layoutPtr)
300
588
  return { desc: new Uint8Array(buf), _refs: [epBytes] };
301
589
  }
302
590
 
591
+ function buildRenderPipelineDescriptor(descriptor) {
592
+ const vertexEntryBytes = encoder.encode(descriptor.vertexEntryPoint);
593
+ const fragmentEntryBytes = encoder.encode(descriptor.fragmentEntryPoint);
594
+ const vertexBuffers = descriptor.vertexBuffers ?? [];
595
+
596
+ const colorTargetBuf = new ArrayBuffer(WGPU_RENDER_COLOR_TARGET_STATE_SIZE);
597
+ const colorTargetView = new DataView(colorTargetBuf);
598
+ writePtr(colorTargetView, 0, null);
599
+ colorTargetView.setUint32(8, TEXTURE_FORMAT_MAP[descriptor.colorFormat] ?? 0x00000016, true);
600
+ writePtr(colorTargetView, 16, null);
601
+ colorTargetView.setBigUint64(24, 0xFn, true);
602
+ const colorTargetArr = new Uint8Array(colorTargetBuf);
603
+
604
+ const fragmentBuf = new ArrayBuffer(WGPU_RENDER_FRAGMENT_STATE_SIZE);
605
+ const fragmentView = new DataView(fragmentBuf);
606
+ writePtr(fragmentView, 0, null);
607
+ writePtr(fragmentView, 8, descriptor.fragmentModule);
608
+ writeStringView(fragmentView, 16, fragmentEntryBytes);
609
+ fragmentView.setBigUint64(32, 0n, true);
610
+ writePtr(fragmentView, 40, null);
611
+ fragmentView.setBigUint64(48, 1n, true);
612
+ writePtr(fragmentView, 56, bunPtr(colorTargetArr));
613
+ const fragmentArr = new Uint8Array(fragmentBuf);
614
+
615
+ let vertexAttributeArr = null;
616
+ let vertexBufferArr = null;
617
+ if (vertexBuffers.length > 0) {
618
+ let totalAttributeCount = 0;
619
+ for (const buffer of vertexBuffers) {
620
+ totalAttributeCount += (buffer.attributes ?? []).length;
621
+ }
622
+ vertexAttributeArr = new Uint8Array(totalAttributeCount * WGPU_VERTEX_ATTRIBUTE_SIZE);
623
+ vertexBufferArr = new Uint8Array(vertexBuffers.length * WGPU_VERTEX_BUFFER_LAYOUT_SIZE);
624
+ const attrView = new DataView(vertexAttributeArr.buffer);
625
+ const layoutView = new DataView(vertexBufferArr.buffer);
626
+ let attrIndex = 0;
627
+ for (let bufferIndex = 0; bufferIndex < vertexBuffers.length; bufferIndex += 1) {
628
+ const buffer = vertexBuffers[bufferIndex] ?? {};
629
+ const attributes = buffer.attributes ?? [];
630
+ const layoutOffset = bufferIndex * WGPU_VERTEX_BUFFER_LAYOUT_SIZE;
631
+ writePtr(layoutView, layoutOffset + 0, null);
632
+ layoutView.setUint32(layoutOffset + 8, VERTEX_STEP_MODE_MAP[buffer.stepMode ?? "vertex"] ?? VERTEX_STEP_MODE_MAP.vertex, true);
633
+ layoutView.setBigUint64(layoutOffset + 16, BigInt(buffer.arrayStride ?? 0), true);
634
+ layoutView.setBigUint64(layoutOffset + 24, BigInt(attributes.length), true);
635
+ writePtr(layoutView, layoutOffset + 32, attributes.length > 0 ? bunPtr(vertexAttributeArr) + attrIndex * WGPU_VERTEX_ATTRIBUTE_SIZE : null);
636
+ for (const attribute of attributes) {
637
+ const attrOffset = attrIndex * WGPU_VERTEX_ATTRIBUTE_SIZE;
638
+ writePtr(attrView, attrOffset + 0, null);
639
+ attrView.setUint32(attrOffset + 8, VERTEX_FORMAT_MAP[attribute.format] ?? 0, true);
640
+ attrView.setBigUint64(attrOffset + 16, BigInt(attribute.offset ?? 0), true);
641
+ attrView.setUint32(attrOffset + 24, attribute.shaderLocation ?? 0, true);
642
+ attrIndex += 1;
643
+ }
644
+ }
645
+ }
646
+
647
+ let depthStencilArr = null;
648
+ if (descriptor.depthStencil) {
649
+ const depthStencilBuf = new ArrayBuffer(WGPU_DEPTH_STENCIL_STATE_SIZE);
650
+ const depthStencilView = new DataView(depthStencilBuf);
651
+ writePtr(depthStencilView, 0, null);
652
+ depthStencilView.setUint32(8, TEXTURE_FORMAT_MAP[descriptor.depthStencil.format] ?? TEXTURE_FORMAT_MAP.depth32float, true);
653
+ depthStencilView.setUint32(12, descriptor.depthStencil.depthWriteEnabled ? 1 : 0, true);
654
+ depthStencilView.setUint32(16, COMPARE_FUNC_MAP[descriptor.depthStencil.depthCompare ?? "always"] ?? COMPARE_FUNC_MAP.always, true);
655
+ depthStencilView.setUint32(48, 0xFFFFFFFF, true);
656
+ depthStencilView.setUint32(52, 0xFFFFFFFF, true);
657
+ depthStencilArr = new Uint8Array(depthStencilBuf);
658
+ }
659
+
660
+ const primitive = descriptor.primitive ?? {};
661
+ const multisample = descriptor.multisample ?? {};
662
+ const buf = new ArrayBuffer(WGPU_RENDER_PIPELINE_DESCRIPTOR_SIZE);
663
+ const view = new DataView(buf);
664
+ writePtr(view, 0, null);
665
+ writeStringView(view, 8, null);
666
+ writePtr(view, 24, descriptor.layout);
667
+ writePtr(view, 32, null);
668
+ writePtr(view, 40, descriptor.vertexModule);
669
+ writeStringView(view, 48, vertexEntryBytes);
670
+ view.setBigUint64(64, 0n, true);
671
+ writePtr(view, 72, null);
672
+ view.setBigUint64(80, BigInt(vertexBuffers.length), true);
673
+ writePtr(view, 88, vertexBuffers.length > 0 ? bunPtr(vertexBufferArr) : null);
674
+ writePtr(view, 96, null);
675
+ view.setUint32(104, {
676
+ "point-list": 0x00000001,
677
+ "line-list": 0x00000002,
678
+ "line-strip": 0x00000003,
679
+ "triangle-list": 0x00000004,
680
+ "triangle-strip": 0x00000005,
681
+ }[primitive.topology ?? "triangle-list"] ?? 0x00000004, true);
682
+ view.setUint32(108, 0, true);
683
+ view.setUint32(112, { ccw: 0x00000001, cw: 0x00000002 }[primitive.frontFace ?? "ccw"] ?? 0x00000001, true);
684
+ view.setUint32(116, { none: 0x00000001, front: 0x00000002, back: 0x00000003 }[primitive.cullMode ?? "none"] ?? 0x00000001, true);
685
+ view.setUint32(120, primitive.unclippedDepth ? 1 : 0, true);
686
+ writePtr(view, 128, depthStencilArr ? bunPtr(depthStencilArr) : null);
687
+ writePtr(view, 136, null);
688
+ view.setUint32(144, multisample.count ?? 1, true);
689
+ view.setUint32(148, multisample.mask ?? 0xFFFF_FFFF, true);
690
+ view.setUint32(152, multisample.alphaToCoverageEnabled ? 1 : 0, true);
691
+ writePtr(view, 160, bunPtr(fragmentArr));
692
+ return {
693
+ desc: new Uint8Array(buf),
694
+ _refs: [vertexEntryBytes, fragmentEntryBytes, colorTargetArr, fragmentArr, vertexAttributeArr, vertexBufferArr, depthStencilArr].filter(Boolean),
695
+ };
696
+ }
697
+
303
698
  // WGPUBindGroupLayoutEntry: { nextInChain:ptr@0, binding:u32@8, visibility:u64@12(actually u32@12 + pad), ...complex }
304
699
  // The full entry is large. We build a minimal version matching what doe_napi.c marshals.
305
700
  // For simplicity, we build the entry array matching the C struct layout.
@@ -342,11 +737,26 @@ function buildBindGroupLayoutDescriptor(entries) {
342
737
  // buffer.minBindingSize: u64@48
343
738
  entryView.setBigUint64(off + 48, BigInt(e.buffer.minBindingSize || 0), true);
344
739
  }
345
- // sampler/texture/storageTexture sub-structs (@56..120) remain zeroed
740
+ if (e.sampler) {
741
+ writePtr(entryView, off + 56, null);
742
+ entryView.setUint32(off + 64, SAMPLER_BINDING_TYPE[e.sampler.type] || 2, true);
743
+ }
744
+ if (e.texture) {
745
+ writePtr(entryView, off + 72, null);
746
+ entryView.setUint32(off + 80, TEXTURE_SAMPLE_TYPE[e.texture.sampleType] || 2, true);
747
+ entryView.setUint32(off + 84, TEXTURE_VIEW_DIMENSION[e.texture.viewDimension] || 2, true);
748
+ entryView.setUint32(off + 88, e.texture.multisampled ? 1 : 0, true);
749
+ }
750
+ if (e.storageTexture) {
751
+ writePtr(entryView, off + 96, null);
752
+ entryView.setUint32(off + 104, STORAGE_TEXTURE_ACCESS[e.storageTexture.access] || 2, true);
753
+ entryView.setUint32(off + 108, TEXTURE_FORMATS[e.storageTexture.format] || 18, true);
754
+ entryView.setUint32(off + 112, TEXTURE_VIEW_DIMENSION[e.storageTexture.viewDimension] || 2, true);
755
+ }
346
756
  }
347
757
 
348
758
  // WGPUBindGroupLayoutDescriptor: { nextInChain:ptr@0, label:sv@8, entryCount:size_t@24, entries:ptr@32 } = 40
349
- const descBuf = new ArrayBuffer(40);
759
+ const descBuf = new ArrayBuffer(WGPU_BIND_GROUP_LAYOUT_DESCRIPTOR_SIZE);
350
760
  const descView = new DataView(descBuf);
351
761
  writePtr(descView, 0, null);
352
762
  writeStringView(descView, 8, null);
@@ -370,16 +780,16 @@ function buildBindGroupDescriptor(layoutPtr, entries) {
370
780
  const off = i * BIND_GROUP_ENTRY_SIZE;
371
781
  writePtr(entryView, off + 0, null);
372
782
  entryView.setUint32(off + 8, e.binding, true);
373
- const bufferPtr = e.resource?.buffer?._native ?? e.resource?._native ?? null;
783
+ const bufferPtr = e.resource?.buffer ?? null;
374
784
  writePtr(entryView, off + 16, bufferPtr);
375
785
  entryView.setBigUint64(off + 24, BigInt(e.resource?.offset ?? 0), true);
376
786
  entryView.setBigUint64(off + 32, e.resource?.size !== undefined ? BigInt(e.resource.size) : WHOLE_SIZE, true);
377
- writePtr(entryView, off + 40, null); // sampler
378
- writePtr(entryView, off + 48, null); // textureView
787
+ writePtr(entryView, off + 40, e.resource?.sampler ?? null);
788
+ writePtr(entryView, off + 48, e.resource?.textureView ?? null);
379
789
  }
380
790
 
381
791
  // WGPUBindGroupDescriptor: { nextInChain:ptr@0, label:sv@8, layout:ptr@24, entryCount:size_t@32, entries:ptr@40 } = 48
382
- const descBuf = new ArrayBuffer(48);
792
+ const descBuf = new ArrayBuffer(WGPU_BIND_GROUP_DESCRIPTOR_SIZE);
383
793
  const descView = new DataView(descBuf);
384
794
  writePtr(descView, 0, null);
385
795
  writeStringView(descView, 8, null);
@@ -398,7 +808,7 @@ function buildPipelineLayoutDescriptor(layouts) {
398
808
  ptrs[i] = BigInt(layouts[i]);
399
809
  }
400
810
 
401
- const descBuf = new ArrayBuffer(48);
811
+ const descBuf = new ArrayBuffer(WGPU_PIPELINE_LAYOUT_DESCRIPTOR_SIZE);
402
812
  const descView = new DataView(descBuf);
403
813
  writePtr(descView, 0, null);
404
814
  writeStringView(descView, 8, null);
@@ -440,19 +850,104 @@ function buildPipelineLayoutDescriptor(layouts) {
440
850
  const TEXTURE_DESC_SIZE = 80;
441
851
 
442
852
  const TEXTURE_FORMAT_MAP = {
443
- rgba8unorm: 18, "rgba8unorm-srgb": 19, bgra8unorm: 23, "bgra8unorm-srgb": 24,
444
- r32float: 33, rg32float: 43, rgba32float: 52, depth32float: 55,
853
+ r8unorm: 0x01, r8snorm: 0x02, r8uint: 0x03, r8sint: 0x04,
854
+ r16uint: 0x07, r16sint: 0x08, r16float: 0x09,
855
+ rg8unorm: 0x0A, rg8snorm: 0x0B, rg8uint: 0x0C, rg8sint: 0x0D,
856
+ r32float: 0x0E, r32uint: 0x0F, r32sint: 0x10,
857
+ rg16uint: 0x13, rg16sint: 0x14, rg16float: 0x15,
858
+ rgba8unorm: 0x16, "rgba8unorm-srgb": 0x17, rgba8snorm: 0x18, rgba8uint: 0x19, rgba8sint: 0x1A,
859
+ bgra8unorm: 0x1B, "bgra8unorm-srgb": 0x1C,
860
+ rgb10a2uint: 0x1D, rgb10a2unorm: 0x1E, rg11b10ufloat: 0x1F, rgb9e5ufloat: 0x20,
861
+ rg32float: 0x21, rg32uint: 0x22, rg32sint: 0x23,
862
+ rgba16uint: 0x24, rgba16sint: 0x25, rgba16float: 0x26,
863
+ rgba32float: 0x27, rgba32uint: 0x28, rgba32sint: 0x29,
864
+ stencil8: 0x2C, depth16unorm: 0x2D,
865
+ depth24plus: 0x2E, "depth24plus-stencil8": 0x2F,
866
+ depth32float: 0x30, "depth32float-stencil8": 0x31,
867
+ // BC compressed formats (texture-compression-bc feature)
868
+ "bc1-rgba-unorm": 0x32, "bc1-rgba-unorm-srgb": 0x33,
869
+ "bc2-rgba-unorm": 0x34, "bc2-rgba-unorm-srgb": 0x35,
870
+ "bc3-rgba-unorm": 0x36, "bc3-rgba-unorm-srgb": 0x37,
871
+ "bc4-r-unorm": 0x38, "bc4-r-snorm": 0x39,
872
+ "bc5-rg-unorm": 0x3A, "bc5-rg-snorm": 0x3B,
873
+ "bc6h-rgb-ufloat": 0x3C, "bc6h-rgb-float": 0x3D,
874
+ "bc7-rgba-unorm": 0x3E, "bc7-rgba-unorm-srgb": 0x3F,
875
+ // ETC2/EAC compressed formats (texture-compression-etc2 feature)
876
+ "etc2-rgb8unorm": 0x40, "etc2-rgb8unorm-srgb": 0x41,
877
+ "etc2-rgb8a1unorm": 0x42, "etc2-rgb8a1unorm-srgb": 0x43,
878
+ "etc2-rgba8unorm": 0x44, "etc2-rgba8unorm-srgb": 0x45,
879
+ "eac-r11unorm": 0x46, "eac-r11snorm": 0x47,
880
+ "eac-rg11unorm": 0x48, "eac-rg11snorm": 0x49,
881
+ // ASTC compressed formats (texture-compression-astc feature)
882
+ "astc-4x4-unorm": 0x4A, "astc-4x4-unorm-srgb": 0x4B,
883
+ "astc-5x4-unorm": 0x4C, "astc-5x4-unorm-srgb": 0x4D,
884
+ "astc-5x5-unorm": 0x4E, "astc-5x5-unorm-srgb": 0x4F,
885
+ "astc-6x5-unorm": 0x50, "astc-6x5-unorm-srgb": 0x51,
886
+ "astc-6x6-unorm": 0x52, "astc-6x6-unorm-srgb": 0x53,
887
+ "astc-8x5-unorm": 0x54, "astc-8x5-unorm-srgb": 0x55,
888
+ "astc-8x6-unorm": 0x56, "astc-8x6-unorm-srgb": 0x57,
889
+ "astc-8x8-unorm": 0x58, "astc-8x8-unorm-srgb": 0x59,
890
+ "astc-10x5-unorm": 0x5A, "astc-10x5-unorm-srgb": 0x5B,
891
+ "astc-10x6-unorm": 0x5C, "astc-10x6-unorm-srgb": 0x5D,
892
+ "astc-10x8-unorm": 0x5E, "astc-10x8-unorm-srgb": 0x5F,
893
+ "astc-10x10-unorm": 0x60, "astc-10x10-unorm-srgb": 0x61,
894
+ "astc-12x10-unorm": 0x62, "astc-12x10-unorm-srgb": 0x63,
895
+ "astc-12x12-unorm": 0x64, "astc-12x12-unorm-srgb": 0x65,
445
896
  };
446
897
 
898
+ // Alias used by bind group layout storage texture format lookup
899
+ const TEXTURE_FORMATS = TEXTURE_FORMAT_MAP;
900
+
901
+ const VERTEX_FORMAT_MAP = {
902
+ float32: 0x00000019,
903
+ float32x2: 0x0000001A,
904
+ float32x3: 0x0000001B,
905
+ float32x4: 0x0000001C,
906
+ uint32: 0x00000021,
907
+ uint32x2: 0x00000022,
908
+ uint32x3: 0x00000023,
909
+ uint32x4: 0x00000024,
910
+ sint32: 0x00000025,
911
+ sint32x2: 0x00000026,
912
+ sint32x3: 0x00000027,
913
+ sint32x4: 0x00000028,
914
+ };
915
+
916
+ const VERTEX_STEP_MODE_MAP = {
917
+ vertex: 0x00000001,
918
+ instance: 0x00000002,
919
+ };
920
+
921
+ const COMPARE_FUNC_MAP = {
922
+ never: 0x00000001,
923
+ less: 0x00000002,
924
+ equal: 0x00000003,
925
+ "less-equal": 0x00000004,
926
+ greater: 0x00000005,
927
+ "not-equal": 0x00000006,
928
+ "greater-equal": 0x00000007,
929
+ always: 0x00000008,
930
+ };
931
+
932
+ const INDEX_FORMAT_MAP = {
933
+ uint16: 0x00000001,
934
+ uint32: 0x00000002,
935
+ };
936
+
937
+ const TEXTURE_DIMENSION_MAP = Object.freeze({
938
+ "1d": 1,
939
+ "2d": 2,
940
+ "3d": 3,
941
+ });
942
+
447
943
  function buildTextureDescriptor(descriptor) {
448
944
  const buf = new ArrayBuffer(TEXTURE_DESC_SIZE);
449
945
  const v = new DataView(buf);
450
946
  writePtr(v, 0, null);
451
947
  writeStringView(v, 8, null);
452
948
  v.setBigUint64(24, BigInt(descriptor.usage || 0), true);
453
- v.setUint32(32, 1, true); // dimension = 2D (WGPUTextureDimension_2D = 1... actually 0x00000002)
454
- // WGPUTextureDimension: 1D=1, 2D=2, 3D=3 in standard. Let's use 2.
455
- v.setUint32(32, 2, true);
949
+ const dimension = descriptor.dimension ?? "2d";
950
+ v.setUint32(32, typeof dimension === "number" ? dimension : (TEXTURE_DIMENSION_MAP[dimension] ?? 2), true);
456
951
  const w = descriptor.size?.[0] ?? descriptor.size?.width ?? descriptor.size ?? 1;
457
952
  const h = descriptor.size?.[1] ?? descriptor.size?.height ?? 1;
458
953
  const d = descriptor.size?.[2] ?? descriptor.size?.depthOrArrayLayers ?? 1;
@@ -460,7 +955,7 @@ function buildTextureDescriptor(descriptor) {
460
955
  v.setUint32(40, h, true);
461
956
  v.setUint32(44, d, true);
462
957
  const fmt = descriptor.format || "rgba8unorm";
463
- v.setUint32(48, TEXTURE_FORMAT_MAP[fmt] ?? 18, true);
958
+ v.setUint32(48, TEXTURE_FORMAT_MAP[fmt] ?? 0x16, true);
464
959
  v.setUint32(52, descriptor.mipLevelCount || 1, true);
465
960
  v.setUint32(56, 1, true); // sampleCount
466
961
  v.setBigUint64(64, 0n, true); // viewFormatCount
@@ -496,6 +991,9 @@ function buildSamplerDescriptor(descriptor) {
496
991
  // { nextInChain:ptr@0, view:ptr@8, depthSlice:u32@16, pad@20, resolveTarget:ptr@24,
497
992
  // loadOp:u32@32, storeOp:u32@36, clearValue:{r:f64@40, g:f64@48, b:f64@56, a:f64@64} } = 72
498
993
  const RENDER_PASS_COLOR_ATTACHMENT_SIZE = 72;
994
+ const TEXEL_COPY_TEXTURE_INFO_SIZE = 32;
995
+ const TEXEL_COPY_BUFFER_INFO_SIZE = 24;
996
+ const EXTENT3D_SIZE = 12;
499
997
 
500
998
  // WGPURenderPassDescriptor:
501
999
  // { nextInChain:ptr@0, label:sv@8, colorAttachmentCount:size_t@24, colorAttachments:ptr@32,
@@ -521,17 +1019,64 @@ function buildRenderPassDescriptor(descriptor) {
521
1019
  attView.setFloat64(off + 64, cv.a ?? 1, true);
522
1020
  }
523
1021
 
524
- const descBuf = new ArrayBuffer(64);
1022
+ let depthStencilAttachmentArr = null;
1023
+ if (descriptor.depthStencilAttachment?.view) {
1024
+ const depthBuf = new ArrayBuffer(WGPU_RENDER_PASS_DEPTH_STENCIL_ATTACHMENT_SIZE);
1025
+ const depthView = new DataView(depthBuf);
1026
+ writePtr(depthView, 0, descriptor.depthStencilAttachment.view._native);
1027
+ depthView.setUint32(8, 1, true); // clear
1028
+ depthView.setUint32(12, 1, true); // store
1029
+ depthView.setFloat32(16, descriptor.depthStencilAttachment.depthClearValue ?? 1.0, true);
1030
+ depthView.setUint32(20, descriptor.depthStencilAttachment.depthReadOnly ? 1 : 0, true);
1031
+ depthView.setUint32(24, 1, true); // clear
1032
+ depthView.setUint32(28, 1, true); // store
1033
+ depthView.setUint32(32, descriptor.depthStencilAttachment.stencilClearValue ?? 0, true);
1034
+ depthView.setUint32(36, descriptor.depthStencilAttachment.stencilReadOnly ? 1 : 0, true);
1035
+ depthStencilAttachmentArr = new Uint8Array(depthBuf);
1036
+ }
1037
+
1038
+ const descBuf = new ArrayBuffer(WGPU_RENDER_PASS_DESCRIPTOR_SIZE);
525
1039
  const descView = new DataView(descBuf);
526
1040
  writePtr(descView, 0, null);
527
1041
  writeStringView(descView, 8, null);
528
1042
  descView.setBigUint64(24, BigInt(colorAttachments.length), true);
529
1043
  writePtr(descView, 32, colorAttachments.length > 0 ? bunPtr(attBuf) : null);
530
- writePtr(descView, 40, null); // depthStencilAttachment
1044
+ writePtr(descView, 40, depthStencilAttachmentArr ? bunPtr(depthStencilAttachmentArr) : null);
531
1045
  writePtr(descView, 48, null); // occlusionQuerySet
532
1046
  writePtr(descView, 56, null); // timestampWrites
533
1047
 
534
- return { desc: new Uint8Array(descBuf), _refs: [attBuf] };
1048
+ return { desc: new Uint8Array(descBuf), _refs: [attBuf, depthStencilAttachmentArr].filter(Boolean) };
1049
+ }
1050
+
1051
+ function buildTexelCopyTextureInfo(source) {
1052
+ const buf = new ArrayBuffer(TEXEL_COPY_TEXTURE_INFO_SIZE);
1053
+ const view = new DataView(buf);
1054
+ writePtr(view, 0, source.texture);
1055
+ view.setUint32(8, source.mipLevel ?? 0, true);
1056
+ view.setUint32(12, source.origin?.x ?? 0, true);
1057
+ view.setUint32(16, source.origin?.y ?? 0, true);
1058
+ view.setUint32(20, source.origin?.z ?? 0, true);
1059
+ view.setUint32(24, source.aspect ?? 1, true);
1060
+ return { desc: new Uint8Array(buf), srcRefs: null };
1061
+ }
1062
+
1063
+ function buildTexelCopyBufferInfo(destination) {
1064
+ const buf = new ArrayBuffer(TEXEL_COPY_BUFFER_INFO_SIZE);
1065
+ const view = new DataView(buf);
1066
+ view.setBigUint64(0, BigInt(destination.offset ?? 0), true);
1067
+ view.setUint32(8, destination.bytesPerRow ?? 0, true);
1068
+ view.setUint32(12, destination.rowsPerImage ?? 0, true);
1069
+ writePtr(view, 16, destination.buffer);
1070
+ return { desc: new Uint8Array(buf), dstRefs: null };
1071
+ }
1072
+
1073
+ function buildExtent3D(size) {
1074
+ const buf = new ArrayBuffer(EXTENT3D_SIZE);
1075
+ const view = new DataView(buf);
1076
+ view.setUint32(0, size.width, true);
1077
+ view.setUint32(4, size.height, true);
1078
+ view.setUint32(8, size.depthOrArrayLayers ?? 1, true);
1079
+ return new Uint8Array(buf);
535
1080
  }
536
1081
 
537
1082
  // ---------------------------------------------------------------------------
@@ -542,7 +1087,7 @@ function buildRenderPassDescriptor(descriptor) {
542
1087
  // not supported on all backends (e.g. Vulkan/Dawn).
543
1088
  // ---------------------------------------------------------------------------
544
1089
 
545
- function processEventsUntilDone(instancePtr, isDone, timeoutNs = PROCESS_EVENTS_TIMEOUT_NS) {
1090
+ function processEventsUntilDone(instancePtr, isDone, timeoutNs = processEventsTimeoutNs) {
546
1091
  const start = Number(process.hrtime.bigint());
547
1092
  while (!isDone()) {
548
1093
  wgpu.symbols.wgpuInstanceProcessEvents(instancePtr);
@@ -552,6 +1097,46 @@ function processEventsUntilDone(instancePtr, isDone, timeoutNs = PROCESS_EVENTS_
552
1097
  }
553
1098
  }
554
1099
 
1100
+ function shaderModuleBindings(shaderModule) {
1101
+ const fn = wgpu?.symbols?.doeNativeShaderModuleGetBindings;
1102
+ if (typeof fn !== "function" || !shaderModule?._native) return null;
1103
+ const count = Number(fn(shaderModule._native, null, 0n));
1104
+ if (count <= 0) return [];
1105
+ const raw = new ArrayBuffer(count * 20);
1106
+ fn(shaderModule._native, new Uint8Array(raw), BigInt(count));
1107
+ const view = new DataView(raw);
1108
+ const bindings = [];
1109
+ for (let index = 0; index < count; index += 1) {
1110
+ const offset = index * 20;
1111
+ const group = view.getUint32(offset + 0, true);
1112
+ const binding = view.getUint32(offset + 4, true);
1113
+ const kind = view.getUint32(offset + 8, true);
1114
+ const addrSpace = view.getUint32(offset + 12, true);
1115
+ const access = view.getUint32(offset + 16, true);
1116
+ bindings.push({
1117
+ group,
1118
+ binding,
1119
+ type: ["buffer", "sampler", "texture", "storage_texture"][kind] ?? "unknown",
1120
+ space: ["function", "private", "workgroup", "uniform", "storage", "handle"][addrSpace] ?? "unknown",
1121
+ access: ["read", "write", "read_write"][access] ?? "unknown",
1122
+ });
1123
+ }
1124
+ return bindings;
1125
+ }
1126
+
1127
+ function requireAutoLayoutEntriesFromNative(shaderModule, visibility, path) {
1128
+ const bindings = shaderModuleBindings(shaderModule);
1129
+ if (!Array.isArray(bindings)) {
1130
+ throw new Error(`${path}: layout: "auto" requires native shader binding metadata on this package surface`);
1131
+ }
1132
+ return autoLayoutEntriesFromNativeBindings(bindings, visibility);
1133
+ }
1134
+
1135
+ function nativeFailureMessage(prefix) {
1136
+ const detail = copyLastErrorMessage();
1137
+ return detail ? `${prefix}: ${detail}` : prefix;
1138
+ }
1139
+
555
1140
  function requestAdapterSync(instancePtr) {
556
1141
  let resolvedAdapter = null;
557
1142
  let resolvedStatus = null;
@@ -570,7 +1155,7 @@ function requestAdapterSync(instancePtr) {
570
1155
  if (futureId === 0 || futureId === 0n) throw new Error("[fawn-webgpu] requestAdapter future unavailable");
571
1156
  processEventsUntilDone(instancePtr, () => done);
572
1157
  if (resolvedStatus !== REQUEST_ADAPTER_STATUS_SUCCESS || !resolvedAdapter) {
573
- throw new Error(`[fawn-webgpu] requestAdapter failed (status=${resolvedStatus})`);
1158
+ throw new Error(nativeFailureMessage(`[fawn-webgpu] requestAdapter failed (status=${resolvedStatus})`));
574
1159
  }
575
1160
  return resolvedAdapter;
576
1161
  } finally {
@@ -596,7 +1181,7 @@ function requestDeviceSync(instancePtr, adapterPtr) {
596
1181
  if (futureId === 0 || futureId === 0n) throw new Error("[fawn-webgpu] requestDevice future unavailable");
597
1182
  processEventsUntilDone(instancePtr, () => done);
598
1183
  if (resolvedStatus !== REQUEST_DEVICE_STATUS_SUCCESS || !resolvedDevice) {
599
- throw new Error(`[fawn-webgpu] requestDevice failed (status=${resolvedStatus})`);
1184
+ throw new Error(nativeFailureMessage(`[fawn-webgpu] requestDevice failed (status=${resolvedStatus})`));
600
1185
  }
601
1186
  return resolvedDevice;
602
1187
  } finally {
@@ -609,7 +1194,7 @@ function bufferMapSync(instancePtr, bufferPtr, mode, offset, size) {
609
1194
  const status = wgpu.symbols.doeBufferMapSyncFlat(
610
1195
  instancePtr, bufferPtr, BigInt(mode), BigInt(offset), BigInt(size));
611
1196
  if (status !== MAP_ASYNC_STATUS_SUCCESS) {
612
- throw new Error(`[fawn-webgpu] bufferMapAsync failed (status=${status})`);
1197
+ throw new Error(nativeFailureMessage(`[fawn-webgpu] bufferMapAsync failed (status=${status})`));
613
1198
  }
614
1199
  return;
615
1200
  }
@@ -626,7 +1211,7 @@ function bufferMapSync(instancePtr, bufferPtr, mode, offset, size) {
626
1211
  if (futureId === 0 || futureId === 0n) throw new Error("[fawn-webgpu] bufferMapAsync future unavailable");
627
1212
  processEventsUntilDone(instancePtr, () => done);
628
1213
  if (mapStatus !== MAP_ASYNC_STATUS_SUCCESS) {
629
- throw new Error(`[fawn-webgpu] bufferMapAsync failed (status=${mapStatus})`);
1214
+ throw new Error(nativeFailureMessage(`[fawn-webgpu] bufferMapAsync failed (status=${mapStatus})`));
630
1215
  }
631
1216
  } finally {
632
1217
  cb.close();
@@ -652,11 +1237,17 @@ function waitForSubmittedWorkDoneSync(instancePtr, queuePtr) {
652
1237
  null,
653
1238
  );
654
1239
  if (futureId === 0 || futureId === 0n) {
655
- throw new Error("[fawn-webgpu] queue work-done future unavailable");
1240
+ const error = new Error("[fawn-webgpu] queue work-done future unavailable");
1241
+ error.code = "DOE_QUEUE_UNAVAILABLE";
1242
+ throw error;
656
1243
  }
657
- processEventsUntilDone(instancePtr, () => done);
1244
+ processEventsUntilDone(instancePtr, () => done, processEventsTimeoutNs);
658
1245
  if (queueStatus !== REQUEST_DEVICE_STATUS_SUCCESS) {
659
- throw new Error(`[fawn-webgpu] queue work-done failed (status=${queueStatus})`);
1246
+ const error = new Error(nativeFailureMessage(`[fawn-webgpu] queue work-done failed (status=${queueStatus})`));
1247
+ if (queueStatus === 0) {
1248
+ error.code = "DOE_QUEUE_UNAVAILABLE";
1249
+ }
1250
+ throw error;
660
1251
  }
661
1252
  } finally {
662
1253
  cb.close();
@@ -667,32 +1258,250 @@ function waitForSubmittedWorkDoneSync(instancePtr, queuePtr) {
667
1258
  // WebGPU wrapper classes — matches index.js surface exactly
668
1259
  // ---------------------------------------------------------------------------
669
1260
 
670
- class DoeGPUBuffer {
671
- constructor(native, instance, size, usage, queue) {
672
- this._native = native;
673
- this._instance = instance;
674
- this._queue = queue;
675
- this.size = size;
676
- this.usage = usage;
1261
+ function ensureBunCommandEncoderNative(encoder) {
1262
+ encoder._assertOpen("GPUCommandEncoder");
1263
+ if (encoder._native) return;
1264
+ encoder._native = wgpu.symbols.wgpuDeviceCreateCommandEncoder(
1265
+ assertLiveResource(encoder._device, "GPUCommandEncoder", "GPUDevice"), null);
1266
+ for (const cmd of encoder._commands) {
1267
+ if (cmd.t === 0) {
1268
+ const pass = wgpu.symbols.wgpuCommandEncoderBeginComputePass(encoder._native, null);
1269
+ wgpu.symbols.wgpuComputePassEncoderSetPipeline(pass, cmd.p);
1270
+ for (let i = 0; i < cmd.bg.length; i += 1) {
1271
+ if (cmd.bg[i]) {
1272
+ wgpu.symbols.wgpuComputePassEncoderSetBindGroup(pass, i, cmd.bg[i], BigInt(0), null);
1273
+ }
1274
+ }
1275
+ wgpu.symbols.wgpuComputePassEncoderDispatchWorkgroups(pass, cmd.x, cmd.y, cmd.z);
1276
+ wgpu.symbols.wgpuComputePassEncoderEnd(pass);
1277
+ wgpu.symbols.wgpuComputePassEncoderRelease(pass);
1278
+ } else if (cmd.t === 1) {
1279
+ wgpu.symbols.wgpuCommandEncoderCopyBufferToBuffer(
1280
+ encoder._native, cmd.s, BigInt(cmd.so), cmd.d, BigInt(cmd.do), BigInt(cmd.sz));
1281
+ }
677
1282
  }
1283
+ encoder._commands = [];
1284
+ }
678
1285
 
679
- async mapAsync(mode, offset = 0, size = this.size) {
680
- if (this._queue?.hasPendingSubmissions()) {
681
- waitForSubmittedWorkDoneSync(this._instance, this._queue._native);
682
- this._queue.markSubmittedWorkDone();
683
- }
684
- bufferMapSync(this._instance, this._native, mode, offset, size);
685
- this._mapMode = mode;
1286
+ function readIndirectDispatchCounts(bufferNative, offset) {
1287
+ const dataPtr = wgpu.symbols.wgpuBufferGetConstMappedRange(bufferNative, BigInt(offset), BigInt(12));
1288
+ if (!dataPtr) {
1289
+ throw new Error("[fawn-webgpu] indirect dispatch buffer is not CPU-readable");
686
1290
  }
1291
+ const countsBytes = new Uint8Array(toArrayBuffer(dataPtr, 0, 12)).slice(0);
1292
+ const counts = new DataView(countsBytes.buffer, countsBytes.byteOffset, countsBytes.byteLength);
1293
+ return {
1294
+ x: counts.getUint32(0, true),
1295
+ y: counts.getUint32(4, true),
1296
+ z: counts.getUint32(8, true),
1297
+ };
1298
+ }
1299
+
1300
+ const bunEncoderBackend = {
1301
+ computePassInit(pass) {
1302
+ pass._pipeline = null;
1303
+ pass._bindGroups = [];
1304
+ pass._ended = false;
1305
+ },
1306
+ computePassAssertOpen(pass, path) {
1307
+ if (pass._ended) failValidation(path, "compute pass is already ended");
1308
+ if (pass._encoder._finished) failValidation(path, "command encoder is already finished");
1309
+ },
1310
+ computePassSetPipeline(pass, pipelineNative) {
1311
+ pass._pipeline = pipelineNative;
1312
+ },
1313
+ computePassSetBindGroup(pass, index, bindGroupNative) {
1314
+ pass._bindGroups[index] = bindGroupNative;
1315
+ },
1316
+ computePassDispatchWorkgroups(pass, x, y, z) {
1317
+ if (pass._pipeline == null) {
1318
+ failValidation("GPUComputePassEncoder.dispatchWorkgroups", "setPipeline() must be called before dispatch");
1319
+ }
1320
+ pass._encoder._commands.push({ t: 0, p: pass._pipeline, bg: [...pass._bindGroups], x, y, z });
1321
+ },
1322
+ computePassDispatchWorkgroupsIndirect(pass, indirectBufferNative, indirectOffset) {
1323
+ if (pass._pipeline == null) {
1324
+ failValidation("GPUComputePassEncoder.dispatchWorkgroupsIndirect", "setPipeline() must be called before dispatch");
1325
+ }
1326
+ const counts = readIndirectDispatchCounts(indirectBufferNative, indirectOffset);
1327
+ pass._encoder._commands.push({ t: 0, p: pass._pipeline, bg: [...pass._bindGroups], x: counts.x, y: counts.y, z: counts.z });
1328
+ },
1329
+ computePassEnd(pass) {
1330
+ pass._ended = true;
1331
+ },
1332
+ renderPassInit(pass, native) {
1333
+ pass._native = native;
1334
+ pass._ended = false;
1335
+ },
1336
+ renderPassAssertOpen(pass, path) {
1337
+ if (pass._ended) failValidation(path, "render pass is already ended");
1338
+ if (pass._encoder._finished) failValidation(path, "command encoder is already finished");
1339
+ },
1340
+ renderPassSetPipeline(pass, pipelineNative) {
1341
+ wgpu.symbols.wgpuRenderPassEncoderSetPipeline(
1342
+ assertLiveResource(pass, "GPURenderPassEncoder.setPipeline", "GPURenderPassEncoder"),
1343
+ pipelineNative,
1344
+ );
1345
+ },
1346
+ renderPassSetBindGroup(pass, index, bindGroupNative) {
1347
+ wgpu.symbols.wgpuRenderPassEncoderSetBindGroup(
1348
+ assertLiveResource(pass, "GPURenderPassEncoder.setBindGroup", "GPURenderPassEncoder"),
1349
+ index,
1350
+ bindGroupNative,
1351
+ BigInt(0),
1352
+ null,
1353
+ );
1354
+ },
1355
+ renderPassSetVertexBuffer(pass, slot, bufferNative, offset, size) {
1356
+ wgpu.symbols.wgpuRenderPassEncoderSetVertexBuffer(
1357
+ assertLiveResource(pass, "GPURenderPassEncoder.setVertexBuffer", "GPURenderPassEncoder"),
1358
+ slot,
1359
+ bufferNative,
1360
+ BigInt(offset),
1361
+ BigInt(size ?? 0),
1362
+ );
1363
+ },
1364
+ renderPassSetIndexBuffer(pass, bufferNative, format, offset, size) {
1365
+ wgpu.symbols.wgpuRenderPassEncoderSetIndexBuffer(
1366
+ assertLiveResource(pass, "GPURenderPassEncoder.setIndexBuffer", "GPURenderPassEncoder"),
1367
+ bufferNative,
1368
+ INDEX_FORMAT_MAP[format] ?? INDEX_FORMAT_MAP.uint16,
1369
+ BigInt(offset),
1370
+ BigInt(size ?? 0),
1371
+ );
1372
+ },
1373
+ renderPassDraw(pass, vertexCount, instanceCount, firstVertex, firstInstance) {
1374
+ wgpu.symbols.wgpuRenderPassEncoderDraw(pass._native, vertexCount, instanceCount, firstVertex, firstInstance);
1375
+ },
1376
+ renderPassDrawIndexed(pass, indexCount, instanceCount, firstIndex, baseVertex, firstInstance) {
1377
+ wgpu.symbols.wgpuRenderPassEncoderDrawIndexed(
1378
+ assertLiveResource(pass, "GPURenderPassEncoder.drawIndexed", "GPURenderPassEncoder"),
1379
+ indexCount,
1380
+ instanceCount,
1381
+ firstIndex,
1382
+ baseVertex,
1383
+ firstInstance,
1384
+ );
1385
+ },
1386
+ renderPassEnd(pass) {
1387
+ wgpu.symbols.wgpuRenderPassEncoderEnd(assertLiveResource(pass, "GPURenderPassEncoder.end", "GPURenderPassEncoder"));
1388
+ pass._ended = true;
1389
+ },
1390
+ commandEncoderInit(encoder) {
1391
+ encoder._commands = [];
1392
+ encoder._native = null;
1393
+ encoder._finished = false;
1394
+ },
1395
+ commandEncoderAssertOpen(encoder, path) {
1396
+ if (encoder._finished) failValidation(path, "command encoder is already finished");
1397
+ },
1398
+ commandEncoderBeginComputePass(encoder, _descriptor, classes) {
1399
+ return new classes.DoeGPUComputePassEncoder(null, encoder);
1400
+ },
1401
+ commandEncoderBeginRenderPass(encoder, descriptor, classes) {
1402
+ ensureBunCommandEncoderNative(encoder);
1403
+ const { desc, _refs } = buildRenderPassDescriptor(descriptor);
1404
+ const pass = wgpu.symbols.wgpuCommandEncoderBeginRenderPass(encoder._native, desc);
1405
+ void _refs;
1406
+ return new classes.DoeGPURenderPassEncoder(pass, encoder);
1407
+ },
1408
+ commandEncoderCopyBufferToBuffer(encoder, srcNative, srcOffset, dstNative, dstOffset, size) {
1409
+ if (encoder._native) {
1410
+ wgpu.symbols.wgpuCommandEncoderCopyBufferToBuffer(
1411
+ encoder._native, srcNative, BigInt(srcOffset), dstNative, BigInt(dstOffset), BigInt(size));
1412
+ return;
1413
+ }
1414
+ encoder._commands.push({ t: 1, s: srcNative, so: srcOffset, d: dstNative, do: dstOffset, sz: size });
1415
+ },
1416
+ commandEncoderWriteTimestamp(encoder, querySetNative, queryIndex) {
1417
+ ensureBunCommandEncoderNative(encoder);
1418
+ if (typeof wgpu.symbols.doeNativeCommandEncoderWriteTimestamp === "function") {
1419
+ wgpu.symbols.doeNativeCommandEncoderWriteTimestamp(encoder._native, querySetNative, queryIndex);
1420
+ }
1421
+ },
1422
+ commandEncoderResolveQuerySet(encoder, querySetNative, firstQuery, queryCount, destinationNative, destinationOffset) {
1423
+ ensureBunCommandEncoderNative(encoder);
1424
+ if (typeof wgpu.symbols.doeNativeCommandEncoderResolveQuerySet === "function") {
1425
+ wgpu.symbols.doeNativeCommandEncoderResolveQuerySet(
1426
+ encoder._native, querySetNative, firstQuery, queryCount, destinationNative, BigInt(destinationOffset));
1427
+ }
1428
+ },
1429
+ commandEncoderCopyTextureToBuffer(encoder, source, destination, copySize) {
1430
+ ensureBunCommandEncoderNative(encoder);
1431
+ if (typeof wgpu.symbols.doeNativeCommandEncoderCopyTextureToBuffer === "function") {
1432
+ wgpu.symbols.doeNativeCommandEncoderCopyTextureToBuffer(
1433
+ encoder._native,
1434
+ source.texture,
1435
+ source.mipLevel ?? 0,
1436
+ destination.buffer,
1437
+ BigInt(destination.offset ?? 0),
1438
+ destination.bytesPerRow ?? 0,
1439
+ destination.rowsPerImage ?? 0,
1440
+ copySize.width,
1441
+ copySize.height,
1442
+ copySize.depthOrArrayLayers ?? 1,
1443
+ );
1444
+ return;
1445
+ }
1446
+ const { desc: srcDesc } = buildTexelCopyTextureInfo({
1447
+ ...source,
1448
+ texture: source.texture,
1449
+ });
1450
+ const { desc: dstDesc } = buildTexelCopyBufferInfo({
1451
+ ...destination,
1452
+ buffer: destination.buffer,
1453
+ });
1454
+ const extent = buildExtent3D(copySize);
1455
+ wgpu.symbols.wgpuCommandEncoderCopyTextureToBuffer(encoder._native, srcDesc, dstDesc, extent);
1456
+ },
1457
+ commandEncoderFinish(encoder) {
1458
+ encoder._finished = true;
1459
+ if (encoder._native) {
1460
+ const cmd = wgpu.symbols.wgpuCommandEncoderFinish(encoder._native, null);
1461
+ encoder._native = null;
1462
+ return { _native: cmd, _batched: false };
1463
+ }
1464
+ return { _commands: encoder._commands, _batched: true };
1465
+ },
1466
+ };
1467
+
1468
+ const {
1469
+ DoeGPUComputePassEncoder,
1470
+ DoeGPUCommandEncoder,
1471
+ DoeGPURenderPassEncoder,
1472
+ } = createEncoderClasses(bunEncoderBackend);
687
1473
 
688
- getMappedRange(offset = 0, size = this.size) {
689
- const isWrite = (this._mapMode & 0x0002) !== 0;
1474
+ const fullSurfaceBackend = {
1475
+ initBufferState(buffer) {
1476
+ buffer._mapMode = 0;
1477
+ buffer._mappedWriteRanges = [];
1478
+ },
1479
+ bufferMarkMappedAtCreation(buffer) {
1480
+ buffer._mapMode = 0x0002;
1481
+ buffer._mappedWriteRanges = [];
1482
+ },
1483
+ bufferMapAsync(wrapper, native, mode, offset, size) {
1484
+ if (wrapper._queue?.hasPendingSubmissions()) {
1485
+ const queueNative = assertLiveResource(wrapper._queue, "GPUBuffer.mapAsync", "GPUQueue");
1486
+ if (typeof wgpu.symbols.doeNativeQueueFlush === "function") {
1487
+ wgpu.symbols.doeNativeQueueFlush(queueNative);
1488
+ fastPathStats.flushAndMap += 1;
1489
+ } else {
1490
+ waitForSubmittedWorkDoneSync(wrapper._instance, queueNative);
1491
+ }
1492
+ wrapper._queue.markSubmittedWorkDone();
1493
+ }
1494
+ bufferMapSync(wrapper._instance, native, mode, offset, size);
1495
+ wrapper._mapMode = mode;
1496
+ },
1497
+ bufferGetMappedRange(wrapper, native, offset, size) {
1498
+ const isWrite = (wrapper._mapMode & 0x0002) !== 0;
690
1499
  if (isWrite) {
691
- const dataPtr = wgpu.symbols.wgpuBufferGetMappedRange(this._native, BigInt(offset), BigInt(size));
1500
+ const dataPtr = wgpu.symbols.wgpuBufferGetMappedRange(native, BigInt(offset), BigInt(size));
692
1501
  if (!dataPtr) throw new Error("[fawn-webgpu] getMappedRange (write) returned NULL");
693
1502
  return toArrayBuffer(dataPtr, 0, size);
694
1503
  }
695
- const dataPtr = wgpu.symbols.wgpuBufferGetConstMappedRange(this._native, BigInt(offset), BigInt(size));
1504
+ const dataPtr = wgpu.symbols.wgpuBufferGetConstMappedRange(native, BigInt(offset), BigInt(size));
696
1505
  if (!dataPtr) throw new Error("[fawn-webgpu] getMappedRange returned NULL");
697
1506
  if (DOE_LIBRARY_FLAVOR === "doe-dropin") {
698
1507
  return toArrayBuffer(dataPtr, 0, size);
@@ -701,337 +1510,309 @@ class DoeGPUBuffer {
701
1510
  const copy = new ArrayBuffer(size);
702
1511
  new Uint8Array(copy).set(new Uint8Array(nativeView));
703
1512
  return copy;
704
- }
705
-
706
- unmap() {
707
- wgpu.symbols.wgpuBufferUnmap(this._native);
708
- this._mapMode = 0;
709
- }
710
-
711
- destroy() {
712
- wgpu.symbols.wgpuBufferRelease(this._native);
713
- this._native = null;
714
- }
715
- }
716
-
717
- class DoeGPUComputePassEncoder {
718
- constructor(native) { this._native = native; }
719
-
720
- setPipeline(pipeline) {
721
- wgpu.symbols.wgpuComputePassEncoderSetPipeline(this._native, pipeline._native);
722
- }
723
-
724
- setBindGroup(index, bindGroup) {
725
- wgpu.symbols.wgpuComputePassEncoderSetBindGroup(this._native, index, bindGroup._native, BigInt(0), null);
726
- }
727
-
728
- dispatchWorkgroups(x, y = 1, z = 1) {
729
- wgpu.symbols.wgpuComputePassEncoderDispatchWorkgroups(this._native, x, y, z);
730
- }
731
-
732
- dispatchWorkgroupsIndirect(indirectBuffer, indirectOffset = 0) {
733
- wgpu.symbols.wgpuComputePassEncoderDispatchWorkgroupsIndirect(this._native, indirectBuffer._native, BigInt(indirectOffset));
734
- }
735
-
736
- end() {
737
- wgpu.symbols.wgpuComputePassEncoderEnd(this._native);
738
- }
739
- }
740
-
741
- class DoeGPUCommandEncoder {
742
- constructor(native) { this._native = native; }
743
-
744
- beginComputePass(_descriptor) {
745
- const pass = wgpu.symbols.wgpuCommandEncoderBeginComputePass(this._native, null);
746
- return new DoeGPUComputePassEncoder(pass);
747
- }
748
-
749
- beginRenderPass(descriptor) {
750
- const { desc, _refs } = buildRenderPassDescriptor(descriptor);
751
- const pass = wgpu.symbols.wgpuCommandEncoderBeginRenderPass(this._native, desc);
752
- void _refs;
753
- return new DoeGPURenderPassEncoder(pass);
754
- }
755
-
756
- copyBufferToBuffer(src, srcOffset, dst, dstOffset, size) {
757
- wgpu.symbols.wgpuCommandEncoderCopyBufferToBuffer(
758
- this._native, src._native, BigInt(srcOffset), dst._native, BigInt(dstOffset), BigInt(size));
759
- }
760
-
761
- finish() {
762
- const cmd = wgpu.symbols.wgpuCommandEncoderFinish(this._native, null);
763
- return { _native: cmd };
764
- }
765
- }
766
-
767
- class DoeGPUQueue {
768
- constructor(native, instance) {
769
- this._native = native;
770
- this._instance = instance;
771
- this._pendingSubmissions = 0;
772
- }
773
-
774
- hasPendingSubmissions() {
775
- return this._pendingSubmissions > 0;
776
- }
777
-
778
- markSubmittedWorkDone() {
779
- this._pendingSubmissions = 0;
780
- }
781
-
782
- submit(commandBuffers) {
783
- const ptrs = new BigUint64Array(commandBuffers.length);
784
- for (let i = 0; i < commandBuffers.length; i++) {
785
- ptrs[i] = BigInt(commandBuffers[i]._native);
1513
+ },
1514
+ bufferUnmap(native, wrapper) {
1515
+ wgpu.symbols.wgpuBufferUnmap(native);
1516
+ wrapper._mapMode = 0;
1517
+ wrapper._mappedWriteRanges = [];
1518
+ },
1519
+ bufferDestroy(native) {
1520
+ wgpu.symbols.wgpuBufferRelease(native);
1521
+ },
1522
+ initQueueState(queue) {
1523
+ queue._pendingSubmissions = 0;
1524
+ },
1525
+ queueHasPendingSubmissions(queue) {
1526
+ return queue._pendingSubmissions > 0;
1527
+ },
1528
+ queueMarkSubmittedWorkDone(queue) {
1529
+ queue._pendingSubmissions = 0;
1530
+ },
1531
+ queueSubmit(queue, queueNative, buffers) {
1532
+ const deviceNative = assertLiveResource(queue._device, "GPUQueue.submit", "GPUDevice");
1533
+ queue._pendingSubmissions += 1;
1534
+ const dispatchFlush = wgpu.symbols.doeNativeComputeDispatchFlush;
1535
+ if (dispatchFlush && buffers.length === 1 && buffers[0]?._batched) {
1536
+ const cmds = buffers[0]._commands;
1537
+ if (cmds.length >= 1 && cmds.length <= 2 && cmds[0]?.t === 0 && (cmds.length === 1 || cmds[1]?.t === 1)) {
1538
+ const cmd0 = cmds[0];
1539
+ const bgPtrs = new BigUint64Array(cmd0.bg.length);
1540
+ for (let i = 0; i < cmd0.bg.length; i += 1) {
1541
+ bgPtrs[i] = BigInt(cmd0.bg[i] ?? 0);
1542
+ }
1543
+ const cmd1 = cmds.length === 2 ? cmds[1] : null;
1544
+ dispatchFlush(
1545
+ queueNative, cmd0.p, bgPtrs, cmd0.bg.length,
1546
+ cmd0.x, cmd0.y, cmd0.z,
1547
+ cmd1?.s ?? null, BigInt(cmd1?.so ?? 0),
1548
+ cmd1?.d ?? null, BigInt(cmd1?.do ?? 0), BigInt(cmd1?.sz ?? 0));
1549
+ if (cmd1) queue.markSubmittedWorkDone();
1550
+ fastPathStats.dispatchFlush += 1;
1551
+ return;
1552
+ }
786
1553
  }
787
- wgpu.symbols.wgpuQueueSubmit(this._native, BigInt(commandBuffers.length), ptrs);
788
- if (commandBuffers.length > 0) {
789
- this._pendingSubmissions += commandBuffers.length;
1554
+ if (buffers.every((cb) => cb?._batched && Array.isArray(cb._commands))) {
1555
+ const allCommands = [];
1556
+ for (const cb of buffers) allCommands.push(...cb._commands);
1557
+ const encoder = wgpu.symbols.wgpuDeviceCreateCommandEncoder(deviceNative, null);
1558
+ for (const cmd of allCommands) {
1559
+ if (cmd.t === 0) {
1560
+ const pass = wgpu.symbols.wgpuCommandEncoderBeginComputePass(encoder, null);
1561
+ wgpu.symbols.wgpuComputePassEncoderSetPipeline(pass, cmd.p);
1562
+ for (let i = 0; i < cmd.bg.length; i += 1) {
1563
+ if (cmd.bg[i]) wgpu.symbols.wgpuComputePassEncoderSetBindGroup(pass, i, cmd.bg[i], BigInt(0), null);
1564
+ }
1565
+ wgpu.symbols.wgpuComputePassEncoderDispatchWorkgroups(pass, cmd.x, cmd.y, cmd.z);
1566
+ wgpu.symbols.wgpuComputePassEncoderEnd(pass);
1567
+ wgpu.symbols.wgpuComputePassEncoderRelease(pass);
1568
+ } else if (cmd.t === 1) {
1569
+ wgpu.symbols.wgpuCommandEncoderCopyBufferToBuffer(
1570
+ encoder, cmd.s, BigInt(cmd.so), cmd.d, BigInt(cmd.do), BigInt(cmd.sz));
1571
+ }
1572
+ }
1573
+ const cmdBuf = wgpu.symbols.wgpuCommandEncoderFinish(encoder, null);
1574
+ const ptrs = new BigUint64Array([BigInt(cmdBuf)]);
1575
+ wgpu.symbols.wgpuQueueSubmit(queueNative, BigInt(1), ptrs);
1576
+ wgpu.symbols.wgpuCommandBufferRelease(cmdBuf);
1577
+ wgpu.symbols.wgpuCommandEncoderRelease(encoder);
1578
+ return;
790
1579
  }
791
- }
792
-
793
- writeBuffer(buffer, bufferOffset, data, dataOffset = 0, size) {
794
- let view = data;
795
- if (dataOffset > 0 || size !== undefined) {
796
- const byteOffset = data.byteOffset + dataOffset * (data.BYTES_PER_ELEMENT || 1);
797
- const byteLength = size !== undefined
798
- ? size * (data.BYTES_PER_ELEMENT || 1)
799
- : data.byteLength - dataOffset * (data.BYTES_PER_ELEMENT || 1);
800
- view = new Uint8Array(data.buffer, byteOffset, byteLength);
1580
+ const ptrs = new BigUint64Array(buffers.length);
1581
+ for (let index = 0; index < buffers.length; index += 1) {
1582
+ ptrs[index] = BigInt(assertLiveResource(buffers[index], "GPUQueue.submit", "GPUCommandBuffer"));
801
1583
  }
802
- wgpu.symbols.wgpuQueueWriteBuffer(this._native, buffer._native, BigInt(bufferOffset), view, BigInt(view.byteLength));
803
- }
804
-
805
- async onSubmittedWorkDone() {
806
- if (!this.hasPendingSubmissions()) return;
807
- waitForSubmittedWorkDoneSync(this._instance, this._native);
808
- this.markSubmittedWorkDone();
809
- }
810
- }
811
-
812
- class DoeGPURenderPassEncoder {
813
- constructor(native) { this._native = native; }
814
-
815
- setPipeline(pipeline) {
816
- wgpu.symbols.wgpuRenderPassEncoderSetPipeline(this._native, pipeline._native);
817
- }
818
-
819
- draw(vertexCount, instanceCount = 1, firstVertex = 0, firstInstance = 0) {
820
- wgpu.symbols.wgpuRenderPassEncoderDraw(this._native, vertexCount, instanceCount, firstVertex, firstInstance);
821
- }
822
-
823
- end() {
824
- wgpu.symbols.wgpuRenderPassEncoderEnd(this._native);
825
- }
826
- }
827
-
828
- class DoeGPUTexture {
829
- constructor(native) { this._native = native; }
830
-
831
- createView(_descriptor) {
832
- const view = wgpu.symbols.wgpuTextureCreateView(this._native, null);
833
- return new DoeGPUTextureView(view);
834
- }
835
-
836
- destroy() {
837
- wgpu.symbols.wgpuTextureRelease(this._native);
838
- this._native = null;
839
- }
840
- }
841
-
842
- class DoeGPUTextureView {
843
- constructor(native) { this._native = native; }
844
- }
845
-
846
- class DoeGPUSampler {
847
- constructor(native) { this._native = native; }
848
- }
849
-
850
- class DoeGPURenderPipeline {
851
- constructor(native) { this._native = native; }
852
- }
853
-
854
- class DoeGPUShaderModule {
855
- constructor(native, code) {
856
- this._native = native;
857
- this._code = code;
858
- }
859
- }
860
-
861
- class DoeGPUComputePipeline {
862
- constructor(native, device, explicitLayout, autoLayoutEntriesByGroup) {
863
- this._native = native;
864
- this._device = device;
865
- this._explicitLayout = explicitLayout;
866
- this._autoLayoutEntriesByGroup = autoLayoutEntriesByGroup;
867
- this._cachedLayouts = new Map();
868
- }
869
-
870
- getBindGroupLayout(index) {
871
- if (this._explicitLayout) return this._explicitLayout;
872
- if (this._cachedLayouts.has(index)) return this._cachedLayouts.get(index);
873
-
874
- let layout;
875
- if (this._autoLayoutEntriesByGroup && process.platform === "darwin") {
876
- const entries = this._autoLayoutEntriesByGroup.get(index) ?? [];
877
- layout = this._device.createBindGroupLayout({ entries });
878
- } else {
879
- const native = process.platform === "darwin"
880
- ? wgpu.symbols.doeNativeComputePipelineGetBindGroupLayout(this._native, index)
881
- : wgpu.symbols.wgpuComputePipelineGetBindGroupLayout(this._native, index);
882
- layout = new DoeGPUBindGroupLayout(native);
1584
+ wgpu.symbols.wgpuQueueSubmit(queueNative, BigInt(buffers.length), ptrs);
1585
+ },
1586
+ queueWriteBuffer(_queue, native, bufferNative, bufferOffset, view) {
1587
+ wgpu.symbols.wgpuQueueWriteBuffer(native, bufferNative, BigInt(bufferOffset), view, BigInt(view.byteLength));
1588
+ },
1589
+ async queueOnSubmittedWorkDone(queue, native) {
1590
+ try {
1591
+ waitForSubmittedWorkDoneSync(queue._instance, native);
1592
+ } catch (error) {
1593
+ if (error?.code === "DOE_QUEUE_UNAVAILABLE") {
1594
+ return;
1595
+ }
1596
+ throw error;
883
1597
  }
884
-
885
- this._cachedLayouts.set(index, layout);
886
- return layout;
887
- }
888
- }
889
-
890
- class DoeGPUBindGroupLayout {
891
- constructor(native) { this._native = native; }
892
- }
893
-
894
- class DoeGPUBindGroup {
895
- constructor(native) { this._native = native; }
896
- }
897
-
898
- class DoeGPUPipelineLayout {
899
- constructor(native) { this._native = native; }
900
- }
901
-
902
- class DoeGPUDevice {
903
- constructor(native, instance) {
904
- this._native = native;
905
- this._instance = instance;
906
- const q = wgpu.symbols.wgpuDeviceGetQueue(native);
907
- this.queue = new DoeGPUQueue(q, instance);
908
- this.limits = DOE_LIMITS;
909
- this.features = DOE_FEATURES;
910
- }
911
-
912
- createBuffer(descriptor) {
913
- const descBytes = buildBufferDescriptor(descriptor);
914
- const buf = wgpu.symbols.wgpuDeviceCreateBuffer(this._native, descBytes);
915
- return new DoeGPUBuffer(buf, this._instance, descriptor.size, descriptor.usage, this.queue);
916
- }
917
-
918
- createShaderModule(descriptor) {
919
- const code = descriptor.code || descriptor.source;
920
- if (!code) throw new Error("createShaderModule: descriptor.code is required");
1598
+ },
1599
+ textureCreateView(_texture, native) {
1600
+ return wgpu.symbols.wgpuTextureCreateView(native, null);
1601
+ },
1602
+ textureDestroy(native) {
1603
+ wgpu.symbols.wgpuTextureRelease(native);
1604
+ },
1605
+ shaderModuleDestroy(native) {
1606
+ wgpu.symbols.wgpuShaderModuleRelease(native);
1607
+ },
1608
+ computePipelineGetBindGroupLayout(pipeline, index, classes) {
1609
+ if (pipeline._autoLayoutEntriesByGroup && process.platform === "darwin") {
1610
+ const entries = pipeline._autoLayoutEntriesByGroup.get(index) ?? [];
1611
+ return pipeline._device.createBindGroupLayout({ entries });
1612
+ }
1613
+ const native = process.platform === "darwin"
1614
+ ? wgpu.symbols.doeNativeComputePipelineGetBindGroupLayout(pipeline._native, index)
1615
+ : wgpu.symbols.wgpuComputePipelineGetBindGroupLayout(pipeline._native, index);
1616
+ return new classes.DoeGPUBindGroupLayout(native, pipeline._device);
1617
+ },
1618
+ deviceLimits,
1619
+ deviceFeatures,
1620
+ adapterLimits,
1621
+ adapterFeatures,
1622
+ preflightShaderSource,
1623
+ requireAutoLayoutEntriesFromNative,
1624
+ deviceGetQueue(native) {
1625
+ return wgpu.symbols.wgpuDeviceGetQueue(native);
1626
+ },
1627
+ deviceCreateBuffer(device, validated) {
1628
+ const descBytes = buildBufferDescriptor(validated);
1629
+ return wgpu.symbols.wgpuDeviceCreateBuffer(assertLiveResource(device, "GPUDevice.createBuffer", "GPUDevice"), descBytes);
1630
+ },
1631
+ deviceCreateShaderModule(device, code) {
921
1632
  const { desc, _refs } = buildShaderModuleDescriptor(code);
922
- const mod = wgpu.symbols.wgpuDeviceCreateShaderModule(this._native, desc);
1633
+ let mod;
1634
+ try {
1635
+ mod = wgpu.symbols.wgpuDeviceCreateShaderModule(assertLiveResource(device, "GPUDevice.createShaderModule", "GPUDevice"), desc);
1636
+ } catch (error) {
1637
+ throw enrichNativeCompilerError(error, "GPUDevice.createShaderModule", readLastErrorFields());
1638
+ }
923
1639
  void _refs;
924
- return new DoeGPUShaderModule(mod, code);
925
- }
926
-
927
- createComputePipeline(descriptor) {
928
- const shader = descriptor.compute?.module;
929
- const entryPoint = descriptor.compute?.entryPoint || "main";
930
- const layout = descriptor.layout === "auto" ? null : descriptor.layout;
931
- const autoLayoutEntriesByGroup = layout ? null : inferAutoBindGroupLayouts(
932
- shader?._code || "",
933
- globals.GPUShaderStage.COMPUTE,
934
- );
935
- const { desc, _refs } = buildComputePipelineDescriptor(
936
- shader._native, entryPoint, layout?._native ?? null);
937
- const native = wgpu.symbols.wgpuDeviceCreateComputePipeline(this._native, desc);
1640
+ if (!mod) {
1641
+ throw compilerErrorFromMessage("GPUDevice.createShaderModule", nativeFailureMessage("createShaderModule failed"), readLastErrorFields());
1642
+ }
1643
+ return mod;
1644
+ },
1645
+ deviceCreateComputePipeline(device, shaderNative, entryPoint, layoutNative) {
1646
+ const { desc, _refs } = buildComputePipelineDescriptor(shaderNative, entryPoint, layoutNative);
1647
+ let native;
1648
+ try {
1649
+ native = wgpu.symbols.wgpuDeviceCreateComputePipeline(assertLiveResource(device, "GPUDevice.createComputePipeline", "GPUDevice"), desc);
1650
+ } catch (error) {
1651
+ throw enrichNativeCompilerError(error, "GPUDevice.createComputePipeline", readLastErrorFields());
1652
+ }
938
1653
  void _refs;
939
- return new DoeGPUComputePipeline(native, this, layout, autoLayoutEntriesByGroup);
940
- }
941
-
942
- async createComputePipelineAsync(descriptor) {
943
- return this.createComputePipeline(descriptor);
944
- }
945
-
946
- createBindGroupLayout(descriptor) {
947
- const entries = (descriptor.entries || []).map((e) => ({
948
- binding: e.binding,
949
- visibility: e.visibility,
950
- buffer: e.buffer ? {
951
- type: e.buffer.type || "uniform",
952
- hasDynamicOffset: e.buffer.hasDynamicOffset || false,
953
- minBindingSize: e.buffer.minBindingSize || 0,
954
- } : undefined,
955
- }));
1654
+ if (!native) {
1655
+ throw compilerErrorFromMessage("GPUDevice.createComputePipeline", nativeFailureMessage("createComputePipeline failed"), readLastErrorFields());
1656
+ }
1657
+ return native;
1658
+ },
1659
+ deviceCreateBindGroupLayout(device, entries) {
956
1660
  const { desc, _refs } = buildBindGroupLayoutDescriptor(entries);
957
- const native = wgpu.symbols.wgpuDeviceCreateBindGroupLayout(this._native, desc);
1661
+ const native = wgpu.symbols.wgpuDeviceCreateBindGroupLayout(assertLiveResource(device, "GPUDevice.createBindGroupLayout", "GPUDevice"), desc);
958
1662
  void _refs;
959
- return new DoeGPUBindGroupLayout(native);
960
- }
961
-
962
- createBindGroup(descriptor) {
963
- const { desc, _refs } = buildBindGroupDescriptor(descriptor.layout._native, descriptor.entries || []);
964
- const native = wgpu.symbols.wgpuDeviceCreateBindGroup(this._native, desc);
1663
+ return native;
1664
+ },
1665
+ deviceCreateBindGroup(device, layoutNative, entries) {
1666
+ const normalizedEntries = entries.map((entry) => ({
1667
+ binding: entry.binding,
1668
+ resource: entry.buffer
1669
+ ? { buffer: entry.buffer, offset: entry.offset ?? 0, size: entry.size }
1670
+ : entry.sampler
1671
+ ? { sampler: entry.sampler }
1672
+ : { textureView: entry.textureView },
1673
+ }));
1674
+ const { desc, _refs } = buildBindGroupDescriptor(layoutNative, normalizedEntries);
1675
+ const native = wgpu.symbols.wgpuDeviceCreateBindGroup(assertLiveResource(device, "GPUDevice.createBindGroup", "GPUDevice"), desc);
965
1676
  void _refs;
966
- return new DoeGPUBindGroup(native);
967
- }
968
-
969
- createPipelineLayout(descriptor) {
970
- const layouts = (descriptor.bindGroupLayouts || []).map((l) => l._native);
1677
+ return native;
1678
+ },
1679
+ deviceCreatePipelineLayout(device, layouts) {
971
1680
  const { desc, _refs } = buildPipelineLayoutDescriptor(layouts);
972
- const native = wgpu.symbols.wgpuDeviceCreatePipelineLayout(this._native, desc);
1681
+ const native = wgpu.symbols.wgpuDeviceCreatePipelineLayout(assertLiveResource(device, "GPUDevice.createPipelineLayout", "GPUDevice"), desc);
973
1682
  void _refs;
974
- return new DoeGPUPipelineLayout(native);
975
- }
976
-
977
- createTexture(descriptor) {
978
- const descBytes = buildTextureDescriptor(descriptor);
979
- const native = wgpu.symbols.wgpuDeviceCreateTexture(this._native, descBytes);
980
- return new DoeGPUTexture(native);
981
- }
982
-
983
- createSampler(descriptor = {}) {
1683
+ return native;
1684
+ },
1685
+ deviceCreateTexture(device, textureDescriptor, size, usage) {
1686
+ const descBytes = buildTextureDescriptor({
1687
+ ...textureDescriptor,
1688
+ dimension: normalizeTextureDimension(textureDescriptor.dimension, "GPUDevice.createTexture"),
1689
+ usage,
1690
+ size,
1691
+ mipLevelCount: assertIntegerInRange(textureDescriptor.mipLevelCount ?? 1, "GPUDevice.createTexture", "descriptor.mipLevelCount", { min: 1, max: UINT32_MAX }),
1692
+ });
1693
+ return wgpu.symbols.wgpuDeviceCreateTexture(assertLiveResource(device, "GPUDevice.createTexture", "GPUDevice"), descBytes);
1694
+ },
1695
+ deviceCreateSampler(device, descriptor) {
984
1696
  const descBytes = buildSamplerDescriptor(descriptor);
985
- const native = wgpu.symbols.wgpuDeviceCreateSampler(this._native, descBytes);
986
- return new DoeGPUSampler(native);
987
- }
988
-
989
- createRenderPipeline(_descriptor) {
990
- // Stub: descriptor is not marshaled yet (matches Node N-API stub).
991
- const native = wgpu.symbols.wgpuDeviceCreateRenderPipeline(this._native, null);
992
- return new DoeGPURenderPipeline(native);
993
- }
994
-
995
- createCommandEncoder(_descriptor) {
996
- const native = wgpu.symbols.wgpuDeviceCreateCommandEncoder(this._native, null);
997
- return new DoeGPUCommandEncoder(native);
998
- }
999
-
1000
- destroy() {
1001
- wgpu.symbols.wgpuDeviceRelease(this._native);
1002
- this._native = null;
1003
- }
1004
- }
1005
-
1006
- class DoeGPUAdapter {
1007
- constructor(native, instance) {
1008
- this._native = native;
1009
- this._instance = instance;
1010
- this.features = DOE_FEATURES;
1011
- this.limits = DOE_LIMITS;
1012
- }
1013
-
1014
- async requestDevice(_descriptor) {
1015
- const device = requestDeviceSync(this._instance, this._native);
1016
- return new DoeGPUDevice(device, this._instance);
1017
- }
1018
-
1019
- destroy() {
1020
- wgpu.symbols.wgpuAdapterRelease(this._native);
1021
- this._native = null;
1022
- }
1023
- }
1024
-
1025
- class DoeGPU {
1026
- constructor(instance) {
1027
- this._instance = instance;
1028
- }
1697
+ return wgpu.symbols.wgpuDeviceCreateSampler(assertLiveResource(device, "GPUDevice.createSampler", "GPUDevice"), descBytes);
1698
+ },
1699
+ deviceCreateRenderPipeline(device, descriptor) {
1700
+ const { desc, _refs } = buildRenderPipelineDescriptor({
1701
+ layout: descriptor.layout,
1702
+ vertexModule: descriptor.vertexModule,
1703
+ vertexEntryPoint: descriptor.vertexEntryPoint,
1704
+ vertexBuffers: descriptor.vertexBuffers ?? [],
1705
+ fragmentModule: descriptor.fragmentModule,
1706
+ fragmentEntryPoint: descriptor.fragmentEntryPoint,
1707
+ colorFormat: descriptor.colorFormat,
1708
+ primitive: descriptor.primitive ?? null,
1709
+ depthStencil: descriptor.depthStencil ?? null,
1710
+ multisample: descriptor.multisample ?? null,
1711
+ });
1712
+ const native = wgpu.symbols.wgpuDeviceCreateRenderPipeline(
1713
+ assertLiveResource(device, "GPUDevice.createRenderPipeline", "GPUDevice"),
1714
+ desc,
1715
+ );
1716
+ void _refs;
1717
+ if (!native) {
1718
+ throw compilerErrorFromMessage("GPUDevice.createRenderPipeline", nativeFailureMessage("createRenderPipeline failed"));
1719
+ }
1720
+ return native;
1721
+ },
1722
+ deviceCreateQuerySet(device, descriptor) {
1723
+ const QUERY_TYPE_TIMESTAMP = 2;
1724
+ const fn = wgpu.symbols.doeNativeDeviceCreateQuerySet;
1725
+ if (typeof fn !== "function") {
1726
+ throw new Error("[fawn-webgpu] doeNativeDeviceCreateQuerySet not available");
1727
+ }
1728
+ const native = fn(
1729
+ assertLiveResource(device, "GPUDevice.createQuerySet", "GPUDevice"),
1730
+ QUERY_TYPE_TIMESTAMP,
1731
+ descriptor.count,
1732
+ );
1733
+ if (!native) throw new Error("[fawn-webgpu] createQuerySet failed");
1734
+ return native;
1735
+ },
1736
+ querySetDestroy(native) {
1737
+ if (typeof wgpu.symbols.doeNativeQuerySetDestroy === "function") {
1738
+ wgpu.symbols.doeNativeQuerySetDestroy(native);
1739
+ }
1740
+ },
1741
+ deviceCreateCommandEncoder(device) {
1742
+ return new DoeGPUCommandEncoder(null, device);
1743
+ },
1744
+ deviceDestroy(native) {
1745
+ wgpu.symbols.wgpuDeviceRelease(native);
1746
+ },
1747
+ adapterRequestDevice(adapter, _descriptor, classes) {
1748
+ const native = requestDeviceSync(adapter._instance, assertLiveResource(adapter, "GPUAdapter.requestDevice", "GPUAdapter"));
1749
+ const device = {
1750
+ _destroyed: false,
1751
+ _resourceLabel: "GPUDevice",
1752
+ _resourceOwner: null,
1753
+ createBuffer: classes.DoeGPUDevice.prototype.createBuffer,
1754
+ createShaderModule: classes.DoeGPUDevice.prototype.createShaderModule,
1755
+ createComputePipeline: classes.DoeGPUDevice.prototype.createComputePipeline,
1756
+ createComputePipelineAsync: classes.DoeGPUDevice.prototype.createComputePipelineAsync,
1757
+ createBindGroupLayout: classes.DoeGPUDevice.prototype.createBindGroupLayout,
1758
+ createBindGroup: classes.DoeGPUDevice.prototype.createBindGroup,
1759
+ createPipelineLayout: classes.DoeGPUDevice.prototype.createPipelineLayout,
1760
+ createTexture: classes.DoeGPUDevice.prototype.createTexture,
1761
+ createSampler: classes.DoeGPUDevice.prototype.createSampler,
1762
+ createRenderPipeline: classes.DoeGPUDevice.prototype.createRenderPipeline,
1763
+ createQuerySet: classes.DoeGPUDevice.prototype.createQuerySet,
1764
+ createCommandEncoder: classes.DoeGPUDevice.prototype.createCommandEncoder,
1765
+ destroy: classes.DoeGPUDevice.prototype.destroy,
1766
+ };
1767
+ device._native = native;
1768
+ device._instance = adapter._instance;
1769
+ device.limits = deviceLimits(native);
1770
+ device.features = deviceFeatures(native);
1771
+ const queue = {
1772
+ _destroyed: false,
1773
+ _resourceLabel: "GPUQueue",
1774
+ _resourceOwner: device,
1775
+ hasPendingSubmissions: classes.DoeGPUQueue.prototype.hasPendingSubmissions,
1776
+ markSubmittedWorkDone: classes.DoeGPUQueue.prototype.markSubmittedWorkDone,
1777
+ submit: classes.DoeGPUQueue.prototype.submit,
1778
+ writeBuffer: classes.DoeGPUQueue.prototype.writeBuffer,
1779
+ onSubmittedWorkDone: classes.DoeGPUQueue.prototype.onSubmittedWorkDone,
1780
+ };
1781
+ queue._native = this.deviceGetQueue(native);
1782
+ queue._instance = adapter._instance;
1783
+ queue._device = device;
1784
+ this.initQueueState(queue);
1785
+ device.queue = queue;
1786
+ return device;
1787
+ },
1788
+ adapterDestroy(native) {
1789
+ wgpu.symbols.wgpuAdapterRelease(native);
1790
+ },
1791
+ gpuRequestAdapter(gpu, _options, classes) {
1792
+ const adapter = requestAdapterSync(gpu._instance);
1793
+ return new classes.DoeGPUAdapter(adapter, gpu._instance);
1794
+ },
1795
+ };
1029
1796
 
1030
- async requestAdapter(_options) {
1031
- const adapter = requestAdapterSync(this._instance);
1032
- return new DoeGPUAdapter(adapter, this._instance);
1033
- }
1034
- }
1797
+ const {
1798
+ DoeGPUBuffer,
1799
+ DoeGPUQueue,
1800
+ DoeGPUTexture,
1801
+ DoeGPUTextureView,
1802
+ DoeGPUSampler,
1803
+ DoeGPURenderPipeline,
1804
+ DoeGPUShaderModule,
1805
+ DoeGPUComputePipeline,
1806
+ DoeGPUBindGroupLayout,
1807
+ DoeGPUBindGroup,
1808
+ DoeGPUPipelineLayout,
1809
+ DoeGPUDevice,
1810
+ DoeGPUAdapter,
1811
+ DoeGPU,
1812
+ } = createFullSurfaceClasses({
1813
+ globals,
1814
+ backend: fullSurfaceBackend,
1815
+ });
1035
1816
 
1036
1817
  // ---------------------------------------------------------------------------
1037
1818
  // Library initialization
@@ -1061,57 +1842,21 @@ export function create(createArgs = null) {
1061
1842
  }
1062
1843
 
1063
1844
  export function setupGlobals(target = globalThis, createArgs = null) {
1064
- for (const [name, value] of Object.entries(globals)) {
1065
- if (target[name] === undefined) {
1066
- Object.defineProperty(target, name, {
1067
- value,
1068
- writable: true,
1069
- configurable: true,
1070
- enumerable: false,
1071
- });
1072
- }
1073
- }
1074
1845
  const gpu = create(createArgs);
1075
- if (typeof target.navigator === "undefined") {
1076
- Object.defineProperty(target, "navigator", {
1077
- value: { gpu },
1078
- writable: true,
1079
- configurable: true,
1080
- enumerable: false,
1081
- });
1082
- } else if (!target.navigator.gpu) {
1083
- Object.defineProperty(target.navigator, "gpu", {
1084
- value: gpu,
1085
- writable: true,
1086
- configurable: true,
1087
- enumerable: false,
1088
- });
1089
- }
1090
- return gpu;
1846
+ return setupGlobalsOnTarget(target, gpu, globals);
1091
1847
  }
1092
1848
 
1093
1849
  export async function requestAdapter(adapterOptions = undefined, createArgs = null) {
1094
- const gpu = create(createArgs);
1095
- return gpu.requestAdapter(adapterOptions);
1850
+ return requestAdapterFromCreate(create, adapterOptions, createArgs);
1096
1851
  }
1097
1852
 
1098
1853
  export async function requestDevice(options = {}) {
1099
- const createArgs = options?.createArgs ?? null;
1100
- const adapter = await requestAdapter(options?.adapterOptions, createArgs);
1101
- return adapter.requestDevice(options?.deviceDescriptor);
1102
- }
1103
-
1104
- function libraryFlavor(libraryPath) {
1105
- if (!libraryPath) return "missing";
1106
- if (/libwebgpu_doe\.(so|dylib|dll)$/.test(libraryPath)) return "doe-dropin";
1107
- if (/lib(webgpu|webgpu_dawn|wgpu_native)\.(so|dylib|dll)/.test(libraryPath)) return "delegate";
1108
- return "unknown";
1854
+ return requestDeviceFromRequestAdapter(requestAdapter, options);
1109
1855
  }
1110
1856
 
1111
1857
  export function providerInfo() {
1112
1858
  const flavor = DOE_LIBRARY_FLAVOR;
1113
- return {
1114
- module: "@simulatte/webgpu",
1859
+ return buildProviderInfo({
1115
1860
  loaded: !!DOE_LIB_PATH,
1116
1861
  loadError: !DOE_LIB_PATH ? "libwebgpu_doe not found" : "",
1117
1862
  defaultCreateArgs: [],
@@ -1122,10 +1867,17 @@ export function providerInfo() {
1122
1867
  buildMetadataPath: DOE_BUILD_METADATA.path,
1123
1868
  leanVerifiedBuild: DOE_BUILD_METADATA.leanVerifiedBuild,
1124
1869
  proofArtifactSha256: DOE_BUILD_METADATA.proofArtifactSha256,
1125
- };
1870
+ });
1126
1871
  }
1127
1872
 
1128
1873
  export { createDoeRuntime, runDawnVsDoeCompare };
1874
+ export { preflightShaderSource };
1875
+ export { fastPathStats };
1876
+
1877
+ export function setNativeTimeoutMs(timeoutMs) {
1878
+ validatePositiveInteger(timeoutMs, 'native timeout');
1879
+ processEventsTimeoutNs = timeoutMs * 1_000_000;
1880
+ }
1129
1881
 
1130
1882
  export default {
1131
1883
  create,
@@ -1134,6 +1886,9 @@ export default {
1134
1886
  requestAdapter,
1135
1887
  requestDevice,
1136
1888
  providerInfo,
1889
+ preflightShaderSource,
1890
+ setNativeTimeoutMs,
1137
1891
  createDoeRuntime,
1138
1892
  runDawnVsDoeCompare,
1893
+ fastPathStats,
1139
1894
  };