@simulatte/webgpu 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -4
- package/README.md +273 -235
- package/api-contract.md +163 -0
- package/assets/fawn-icon-main-256.png +0 -0
- package/assets/package-layers.svg +63 -0
- package/assets/package-surface-cube-snapshot.svg +7 -7
- package/{COMPAT_SCOPE.md → compat-scope.md} +1 -1
- package/examples/direct-webgpu/compute-dispatch.js +66 -0
- package/examples/direct-webgpu/explicit-bind-group.js +85 -0
- package/examples/direct-webgpu/request-device.js +10 -0
- package/examples/doe-api/buffers-readback.js +9 -0
- package/examples/doe-api/compile-and-dispatch.js +30 -0
- package/examples/doe-api/compute-dispatch.js +25 -0
- package/examples/doe-routines/compute-once-like-input.js +36 -0
- package/examples/doe-routines/compute-once-matmul.js +53 -0
- package/examples/doe-routines/compute-once-multiple-inputs.js +27 -0
- package/examples/doe-routines/compute-once.js +23 -0
- package/headless-webgpu-comparison.md +2 -2
- package/{LAYERING_PLAN.md → layering-plan.md} +10 -8
- package/native/doe_napi.c +102 -12
- package/package.json +26 -9
- package/prebuilds/darwin-arm64/doe_napi.node +0 -0
- package/prebuilds/darwin-arm64/libwebgpu_doe.dylib +0 -0
- package/prebuilds/darwin-arm64/metadata.json +6 -6
- package/prebuilds/linux-x64/doe_napi.node +0 -0
- package/prebuilds/linux-x64/libwebgpu_doe.so +0 -0
- package/prebuilds/linux-x64/metadata.json +5 -5
- package/scripts/generate-readme-assets.js +81 -8
- package/scripts/prebuild.js +23 -19
- package/src/auto_bind_group_layout.js +32 -0
- package/src/bun-ffi.js +93 -12
- package/src/bun.js +23 -2
- package/src/compute.d.ts +162 -0
- package/src/compute.js +915 -0
- package/src/doe.d.ts +184 -0
- package/src/doe.js +641 -0
- package/src/full.d.ts +119 -0
- package/src/full.js +35 -0
- package/src/index.js +1013 -38
- package/src/node-runtime.js +2 -2
- package/src/node.js +2 -2
- package/{SUPPORT_CONTRACTS.md → support-contracts.md} +27 -41
- package/{ZIG_SOURCE_INVENTORY.md → zig-source-inventory.md} +2 -2
- package/API_CONTRACT.md +0 -182
package/src/index.js
CHANGED
|
@@ -2,8 +2,12 @@ import { createRequire } from 'node:module';
|
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import { resolve, dirname } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
createDoeRuntime as createDoeRuntimeCli,
|
|
7
|
+
runDawnVsDoeCompare as runDawnVsDoeCompareCli,
|
|
8
|
+
} from './runtime_cli.js';
|
|
6
9
|
import { loadDoeBuildMetadata } from './build_metadata.js';
|
|
10
|
+
import { inferAutoBindGroupLayouts } from './auto_bind_group_layout.js';
|
|
7
11
|
|
|
8
12
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
13
|
const require = createRequire(import.meta.url);
|
|
@@ -78,7 +82,24 @@ function ensureLibrary() {
|
|
|
78
82
|
libraryLoaded = true;
|
|
79
83
|
}
|
|
80
84
|
|
|
81
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Standard WebGPU enum objects exposed by the Doe package runtime.
|
|
87
|
+
*
|
|
88
|
+
* This is a package-local copy of the enum tables commonly needed by Node and
|
|
89
|
+
* Bun callers that want WebGPU constants without relying on browser globals.
|
|
90
|
+
*
|
|
91
|
+
* This example shows the API in its basic form.
|
|
92
|
+
*
|
|
93
|
+
* ```js
|
|
94
|
+
* import { globals } from "@simulatte/webgpu";
|
|
95
|
+
*
|
|
96
|
+
* const usage = globals.GPUBufferUsage.STORAGE | globals.GPUBufferUsage.COPY_DST;
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* - These values mirror the standard WebGPU numeric constants.
|
|
100
|
+
* - They do not install themselves on `globalThis`; use `setupGlobals(...)` if needed.
|
|
101
|
+
* - `@simulatte/webgpu/compute` shares the same constants even though its device facade is narrower.
|
|
102
|
+
*/
|
|
82
103
|
export const globals = {
|
|
83
104
|
GPUBufferUsage: {
|
|
84
105
|
MAP_READ: 0x0001,
|
|
@@ -110,6 +131,24 @@ export const globals = {
|
|
|
110
131
|
},
|
|
111
132
|
};
|
|
112
133
|
|
|
134
|
+
/**
|
|
135
|
+
* WebGPU buffer returned by the Doe full package surface.
|
|
136
|
+
*
|
|
137
|
+
* Instances come from `device.createBuffer(...)` and expose buffer metadata,
|
|
138
|
+
* mapping, and destruction operations for headless workflows.
|
|
139
|
+
*
|
|
140
|
+
* This example shows the API in its basic form.
|
|
141
|
+
*
|
|
142
|
+
* ```js
|
|
143
|
+
* const buffer = device.createBuffer({
|
|
144
|
+
* size: 16,
|
|
145
|
+
* usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
146
|
+
* });
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
149
|
+
* - `size` and `usage` are copied onto the JS object for convenience.
|
|
150
|
+
* - Destroying the buffer releases the native handle but does not remove the JS object itself.
|
|
151
|
+
*/
|
|
113
152
|
class DoeGPUBuffer {
|
|
114
153
|
constructor(native, instance, size, usage, queue) {
|
|
115
154
|
this._native = native;
|
|
@@ -119,7 +158,22 @@ class DoeGPUBuffer {
|
|
|
119
158
|
this.usage = usage;
|
|
120
159
|
}
|
|
121
160
|
|
|
122
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Map the buffer for host access.
|
|
163
|
+
*
|
|
164
|
+
* This resolves after Doe has flushed any pending queue work needed to make
|
|
165
|
+
* the requested range readable or writable from JS.
|
|
166
|
+
*
|
|
167
|
+
* This example shows the API in its basic form.
|
|
168
|
+
*
|
|
169
|
+
* ```js
|
|
170
|
+
* await buffer.mapAsync(GPUMapMode.READ);
|
|
171
|
+
* ```
|
|
172
|
+
*
|
|
173
|
+
* - When `size` is omitted, Doe maps the remaining bytes from `offset` to the end of the buffer.
|
|
174
|
+
* - When the queue still has pending submissions, Doe flushes them before mapping.
|
|
175
|
+
*/
|
|
176
|
+
async mapAsync(mode, offset = 0, size = Math.max(0, this.size - offset)) {
|
|
123
177
|
if (this._queue) {
|
|
124
178
|
if (this._queue.hasPendingSubmissions()) {
|
|
125
179
|
addon.flushAndMapSync(this._instance, this._queue._native, this._native, mode, offset, size);
|
|
@@ -132,24 +186,98 @@ class DoeGPUBuffer {
|
|
|
132
186
|
}
|
|
133
187
|
}
|
|
134
188
|
|
|
135
|
-
|
|
189
|
+
/**
|
|
190
|
+
* Return the currently mapped byte range.
|
|
191
|
+
*
|
|
192
|
+
* This exposes the mapped bytes as an `ArrayBuffer`-backed view after a
|
|
193
|
+
* successful `mapAsync(...)` call.
|
|
194
|
+
*
|
|
195
|
+
* This example shows the API in its basic form.
|
|
196
|
+
*
|
|
197
|
+
* ```js
|
|
198
|
+
* const bytes = buffer.getMappedRange();
|
|
199
|
+
* ```
|
|
200
|
+
*
|
|
201
|
+
* - Call this only while the buffer is mapped.
|
|
202
|
+
* - When `size` is omitted, Doe returns the remaining bytes from `offset` to the end of the buffer.
|
|
203
|
+
*/
|
|
204
|
+
getMappedRange(offset = 0, size = Math.max(0, this.size - offset)) {
|
|
136
205
|
return addon.bufferGetMappedRange(this._native, offset, size);
|
|
137
206
|
}
|
|
138
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Compare a mapped `f32` prefix against expected values.
|
|
210
|
+
*
|
|
211
|
+
* This is a small assertion helper used by smoke tests and quick validation
|
|
212
|
+
* flows after mapping a buffer for read.
|
|
213
|
+
*
|
|
214
|
+
* This example shows the API in its basic form.
|
|
215
|
+
*
|
|
216
|
+
* ```js
|
|
217
|
+
* buffer.assertMappedPrefixF32([1, 2, 3, 4], 4);
|
|
218
|
+
* ```
|
|
219
|
+
*
|
|
220
|
+
* - The buffer must already be mapped.
|
|
221
|
+
* - This checks only the requested prefix rather than the whole buffer.
|
|
222
|
+
*/
|
|
139
223
|
assertMappedPrefixF32(expected, count) {
|
|
140
224
|
return addon.bufferAssertMappedPrefixF32(this._native, expected, count);
|
|
141
225
|
}
|
|
142
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Release the current mapping.
|
|
229
|
+
*
|
|
230
|
+
* This returns the buffer to normal GPU ownership after `mapAsync(...)`.
|
|
231
|
+
*
|
|
232
|
+
* This example shows the API in its basic form.
|
|
233
|
+
*
|
|
234
|
+
* ```js
|
|
235
|
+
* buffer.unmap();
|
|
236
|
+
* ```
|
|
237
|
+
*
|
|
238
|
+
* - Call this after reading or writing mapped bytes.
|
|
239
|
+
* - `getMappedRange(...)` is not valid again until the buffer is remapped.
|
|
240
|
+
*/
|
|
143
241
|
unmap() {
|
|
144
242
|
addon.bufferUnmap(this._native);
|
|
145
243
|
}
|
|
146
244
|
|
|
245
|
+
/**
|
|
246
|
+
* Release the native buffer.
|
|
247
|
+
*
|
|
248
|
+
* This tears down the underlying Doe buffer and marks the JS wrapper as
|
|
249
|
+
* released.
|
|
250
|
+
*
|
|
251
|
+
* This example shows the API in its basic form.
|
|
252
|
+
*
|
|
253
|
+
* ```js
|
|
254
|
+
* buffer.destroy();
|
|
255
|
+
* ```
|
|
256
|
+
*
|
|
257
|
+
* - Reusing a destroyed buffer is unsupported.
|
|
258
|
+
* - The wrapper remains reachable in JS but no longer owns a live native handle.
|
|
259
|
+
*/
|
|
147
260
|
destroy() {
|
|
148
261
|
addon.bufferRelease(this._native);
|
|
149
262
|
this._native = null;
|
|
150
263
|
}
|
|
151
264
|
}
|
|
152
265
|
|
|
266
|
+
/**
|
|
267
|
+
* Compute pass encoder returned by `commandEncoder.beginComputePass(...)`.
|
|
268
|
+
*
|
|
269
|
+
* This records a compute pass on the full package surface.
|
|
270
|
+
*
|
|
271
|
+
* This example shows the API in its basic form.
|
|
272
|
+
*
|
|
273
|
+
* ```js
|
|
274
|
+
* const pass = encoder.beginComputePass();
|
|
275
|
+
* pass.setPipeline(pipeline);
|
|
276
|
+
* ```
|
|
277
|
+
*
|
|
278
|
+
* - Dispatches may be batched until the command encoder is finalized.
|
|
279
|
+
* - The encoder only supports the compute commands exposed by Doe here.
|
|
280
|
+
*/
|
|
153
281
|
class DoeGPUComputePassEncoder {
|
|
154
282
|
constructor(encoder) {
|
|
155
283
|
this._encoder = encoder;
|
|
@@ -157,16 +285,74 @@ class DoeGPUComputePassEncoder {
|
|
|
157
285
|
this._bindGroups = [];
|
|
158
286
|
}
|
|
159
287
|
|
|
288
|
+
/**
|
|
289
|
+
* Set the compute pipeline used by later dispatch calls.
|
|
290
|
+
*
|
|
291
|
+
* This stores the pipeline handle on the pass so later dispatches use the
|
|
292
|
+
* expected compiled shader and layout.
|
|
293
|
+
*
|
|
294
|
+
* This example shows the API in its basic form.
|
|
295
|
+
*
|
|
296
|
+
* ```js
|
|
297
|
+
* pass.setPipeline(pipeline);
|
|
298
|
+
* ```
|
|
299
|
+
*
|
|
300
|
+
* - Call this before dispatching workgroups.
|
|
301
|
+
* - The pipeline object must come from the same device.
|
|
302
|
+
*/
|
|
160
303
|
setPipeline(pipeline) { this._pipeline = pipeline._native; }
|
|
161
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Bind a bind group for the compute pass.
|
|
307
|
+
*
|
|
308
|
+
* This records the resource bindings that the next dispatches should see.
|
|
309
|
+
*
|
|
310
|
+
* This example shows the API in its basic form.
|
|
311
|
+
*
|
|
312
|
+
* ```js
|
|
313
|
+
* pass.setBindGroup(0, bindGroup);
|
|
314
|
+
* ```
|
|
315
|
+
*
|
|
316
|
+
* - Later calls for the same index replace the previous bind group.
|
|
317
|
+
* - Sparse indices are allowed, but the shader layout still has to match.
|
|
318
|
+
*/
|
|
162
319
|
setBindGroup(index, bindGroup) { this._bindGroups[index] = bindGroup._native; }
|
|
163
320
|
|
|
321
|
+
/**
|
|
322
|
+
* Record a direct compute dispatch.
|
|
323
|
+
*
|
|
324
|
+
* This queues an explicit workgroup dispatch on the current pass.
|
|
325
|
+
*
|
|
326
|
+
* This example shows the API in its basic form.
|
|
327
|
+
*
|
|
328
|
+
* ```js
|
|
329
|
+
* pass.dispatchWorkgroups(4, 1, 1);
|
|
330
|
+
* ```
|
|
331
|
+
*
|
|
332
|
+
* - Omitted `y` and `z` default to `1`.
|
|
333
|
+
* - The pipeline and required bind groups should already be set.
|
|
334
|
+
*/
|
|
164
335
|
dispatchWorkgroups(x, y = 1, z = 1) {
|
|
165
336
|
this._encoder._commands.push({
|
|
166
337
|
t: 0, p: this._pipeline, bg: [...this._bindGroups], x, y, z,
|
|
167
338
|
});
|
|
168
339
|
}
|
|
169
340
|
|
|
341
|
+
/**
|
|
342
|
+
* Dispatch compute workgroups using counts stored in a buffer.
|
|
343
|
+
*
|
|
344
|
+
* This switches to the native encoder path and forwards the indirect dispatch
|
|
345
|
+
* parameters from the supplied buffer.
|
|
346
|
+
*
|
|
347
|
+
* This example shows the API in its basic form.
|
|
348
|
+
*
|
|
349
|
+
* ```js
|
|
350
|
+
* pass.dispatchWorkgroupsIndirect(indirectBuffer, 0);
|
|
351
|
+
* ```
|
|
352
|
+
*
|
|
353
|
+
* - This forces the command encoder to materialize a native encoder immediately.
|
|
354
|
+
* - The indirect buffer must contain the expected dispatch layout.
|
|
355
|
+
*/
|
|
170
356
|
dispatchWorkgroupsIndirect(indirectBuffer, indirectOffset = 0) {
|
|
171
357
|
this._encoder._ensureNative();
|
|
172
358
|
const pass = addon.beginComputePass(this._encoder._native);
|
|
@@ -179,9 +365,39 @@ class DoeGPUComputePassEncoder {
|
|
|
179
365
|
addon.computePassRelease(pass);
|
|
180
366
|
}
|
|
181
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Finish the compute pass.
|
|
370
|
+
*
|
|
371
|
+
* This closes the pass so the surrounding command encoder can continue or
|
|
372
|
+
* be finalized.
|
|
373
|
+
*
|
|
374
|
+
* This example shows the API in its basic form.
|
|
375
|
+
*
|
|
376
|
+
* ```js
|
|
377
|
+
* pass.end();
|
|
378
|
+
* ```
|
|
379
|
+
*
|
|
380
|
+
* - Doe records most work on the surrounding command encoder, so this is lightweight.
|
|
381
|
+
* - Finishing the pass does not submit it; submit the finished command buffer on the queue.
|
|
382
|
+
*/
|
|
182
383
|
end() {}
|
|
183
384
|
}
|
|
184
385
|
|
|
386
|
+
/**
|
|
387
|
+
* Command encoder returned by `device.createCommandEncoder(...)`.
|
|
388
|
+
*
|
|
389
|
+
* This records compute, render, and buffer-copy commands before they are
|
|
390
|
+
* turned into a command buffer for queue submission.
|
|
391
|
+
*
|
|
392
|
+
* This example shows the API in its basic form.
|
|
393
|
+
*
|
|
394
|
+
* ```js
|
|
395
|
+
* const encoder = device.createCommandEncoder();
|
|
396
|
+
* ```
|
|
397
|
+
*
|
|
398
|
+
* - Doe may batch simple command sequences before a native encoder is required.
|
|
399
|
+
* - Submission still happens through `device.queue.submit(...)`.
|
|
400
|
+
*/
|
|
185
401
|
class DoeGPUCommandEncoder {
|
|
186
402
|
constructor(device) {
|
|
187
403
|
this._device = device;
|
|
@@ -209,10 +425,42 @@ class DoeGPUCommandEncoder {
|
|
|
209
425
|
this._commands = [];
|
|
210
426
|
}
|
|
211
427
|
|
|
428
|
+
/**
|
|
429
|
+
* Begin a compute pass.
|
|
430
|
+
*
|
|
431
|
+
* This creates a pass encoder that records compute state and dispatches on
|
|
432
|
+
* this command encoder.
|
|
433
|
+
*
|
|
434
|
+
* This example shows the API in its basic form.
|
|
435
|
+
*
|
|
436
|
+
* ```js
|
|
437
|
+
* const pass = encoder.beginComputePass();
|
|
438
|
+
* ```
|
|
439
|
+
*
|
|
440
|
+
* - The descriptor is accepted for WebGPU shape compatibility.
|
|
441
|
+
* - The returned pass is valid until `pass.end()`.
|
|
442
|
+
*/
|
|
212
443
|
beginComputePass(descriptor) {
|
|
213
444
|
return new DoeGPUComputePassEncoder(this);
|
|
214
445
|
}
|
|
215
446
|
|
|
447
|
+
/**
|
|
448
|
+
* Begin a render pass.
|
|
449
|
+
*
|
|
450
|
+
* This starts a headless render pass with the provided attachments on the
|
|
451
|
+
* underlying native command encoder.
|
|
452
|
+
*
|
|
453
|
+
* This example shows the API in its basic form.
|
|
454
|
+
*
|
|
455
|
+
* ```js
|
|
456
|
+
* const pass = encoder.beginRenderPass({
|
|
457
|
+
* colorAttachments: [{ view }],
|
|
458
|
+
* });
|
|
459
|
+
* ```
|
|
460
|
+
*
|
|
461
|
+
* - Doe materializes the native encoder before starting the render pass.
|
|
462
|
+
* - Color attachments default their clear color when one is not provided.
|
|
463
|
+
*/
|
|
216
464
|
beginRenderPass(descriptor) {
|
|
217
465
|
this._ensureNative();
|
|
218
466
|
const colorAttachments = (descriptor.colorAttachments || []).map((a) => ({
|
|
@@ -223,6 +471,21 @@ class DoeGPUCommandEncoder {
|
|
|
223
471
|
return new DoeGPURenderPassEncoder(pass);
|
|
224
472
|
}
|
|
225
473
|
|
|
474
|
+
/**
|
|
475
|
+
* Record a buffer-to-buffer copy.
|
|
476
|
+
*
|
|
477
|
+
* This schedules a transfer from one buffer range into another on the
|
|
478
|
+
* command encoder.
|
|
479
|
+
*
|
|
480
|
+
* This example shows the API in its basic form.
|
|
481
|
+
*
|
|
482
|
+
* ```js
|
|
483
|
+
* encoder.copyBufferToBuffer(src, 0, dst, 0, src.size);
|
|
484
|
+
* ```
|
|
485
|
+
*
|
|
486
|
+
* - Copies can be batched until the encoder is finalized.
|
|
487
|
+
* - Buffer ranges still need to be valid for the underlying WebGPU rules.
|
|
488
|
+
*/
|
|
226
489
|
copyBufferToBuffer(src, srcOffset, dst, dstOffset, size) {
|
|
227
490
|
if (this._native) {
|
|
228
491
|
addon.commandEncoderCopyBufferToBuffer(this._native, src._native, srcOffset, dst._native, dstOffset, size);
|
|
@@ -231,6 +494,21 @@ class DoeGPUCommandEncoder {
|
|
|
231
494
|
}
|
|
232
495
|
}
|
|
233
496
|
|
|
497
|
+
/**
|
|
498
|
+
* Finish command recording and return a command buffer.
|
|
499
|
+
*
|
|
500
|
+
* This seals the recorded commands so they can be submitted on a queue.
|
|
501
|
+
*
|
|
502
|
+
* This example shows the API in its basic form.
|
|
503
|
+
*
|
|
504
|
+
* ```js
|
|
505
|
+
* const commands = encoder.finish();
|
|
506
|
+
* device.queue.submit([commands]);
|
|
507
|
+
* ```
|
|
508
|
+
*
|
|
509
|
+
* - Doe may return a lightweight batched command buffer representation.
|
|
510
|
+
* - The returned object is meant for queue submission, not direct inspection.
|
|
511
|
+
*/
|
|
234
512
|
finish() {
|
|
235
513
|
if (this._native) {
|
|
236
514
|
const cmd = addon.commandEncoderFinish(this._native);
|
|
@@ -240,6 +518,21 @@ class DoeGPUCommandEncoder {
|
|
|
240
518
|
}
|
|
241
519
|
}
|
|
242
520
|
|
|
521
|
+
/**
|
|
522
|
+
* Queue exposed on `device.queue`.
|
|
523
|
+
*
|
|
524
|
+
* This submits finished command buffers, uploads host data into buffers, and
|
|
525
|
+
* lets callers wait for queued work to drain.
|
|
526
|
+
*
|
|
527
|
+
* This example shows the API in its basic form.
|
|
528
|
+
*
|
|
529
|
+
* ```js
|
|
530
|
+
* device.queue.submit([encoder.finish()]);
|
|
531
|
+
* ```
|
|
532
|
+
*
|
|
533
|
+
* - Queue writes and submissions stay package-local and headless.
|
|
534
|
+
* - The queue also tracks lightweight submission state used by Doe's sync mapping path.
|
|
535
|
+
*/
|
|
243
536
|
class DoeGPUQueue {
|
|
244
537
|
constructor(native, instance, device) {
|
|
245
538
|
this._native = native;
|
|
@@ -249,14 +542,59 @@ class DoeGPUQueue {
|
|
|
249
542
|
this._completedSerial = 0;
|
|
250
543
|
}
|
|
251
544
|
|
|
545
|
+
/**
|
|
546
|
+
* Report whether this queue still has unflushed submitted work.
|
|
547
|
+
*
|
|
548
|
+
* This exposes Doe's lightweight submission bookkeeping for callers that
|
|
549
|
+
* need to understand queue state.
|
|
550
|
+
*
|
|
551
|
+
* This example shows the API in its basic form.
|
|
552
|
+
*
|
|
553
|
+
* ```js
|
|
554
|
+
* const busy = device.queue.hasPendingSubmissions();
|
|
555
|
+
* ```
|
|
556
|
+
*
|
|
557
|
+
* - This is a Doe queue-state helper rather than a standard WebGPU method.
|
|
558
|
+
* - It reflects Doe's tracked submission serials, not a browser event model.
|
|
559
|
+
*/
|
|
252
560
|
hasPendingSubmissions() {
|
|
253
561
|
return this._completedSerial < this._submittedSerial;
|
|
254
562
|
}
|
|
255
563
|
|
|
564
|
+
/**
|
|
565
|
+
* Mark the current tracked submissions as completed.
|
|
566
|
+
*
|
|
567
|
+
* This updates Doe's internal queue bookkeeping without waiting on any
|
|
568
|
+
* external event source.
|
|
569
|
+
*
|
|
570
|
+
* This example shows the API in its basic form.
|
|
571
|
+
*
|
|
572
|
+
* ```js
|
|
573
|
+
* device.queue.markSubmittedWorkDone();
|
|
574
|
+
* ```
|
|
575
|
+
*
|
|
576
|
+
* - This is primarily useful for Doe's own queue bookkeeping.
|
|
577
|
+
* - Most callers should prefer `await queue.onSubmittedWorkDone()`.
|
|
578
|
+
*/
|
|
256
579
|
markSubmittedWorkDone() {
|
|
257
580
|
this._completedSerial = this._submittedSerial;
|
|
258
581
|
}
|
|
259
582
|
|
|
583
|
+
/**
|
|
584
|
+
* Submit command buffers to the queue.
|
|
585
|
+
*
|
|
586
|
+
* This forwards one or more finished command buffers to the Doe queue for
|
|
587
|
+
* execution.
|
|
588
|
+
*
|
|
589
|
+
* This example shows the API in its basic form.
|
|
590
|
+
*
|
|
591
|
+
* ```js
|
|
592
|
+
* device.queue.submit([encoder.finish()]);
|
|
593
|
+
* ```
|
|
594
|
+
*
|
|
595
|
+
* - Empty submissions are ignored.
|
|
596
|
+
* - Simple batched compute-copy sequences may take a Doe fast path.
|
|
597
|
+
*/
|
|
260
598
|
submit(commandBuffers) {
|
|
261
599
|
if (commandBuffers.length === 0) return;
|
|
262
600
|
this._submittedSerial += 1;
|
|
@@ -302,6 +640,21 @@ class DoeGPUQueue {
|
|
|
302
640
|
}
|
|
303
641
|
}
|
|
304
642
|
|
|
643
|
+
/**
|
|
644
|
+
* Write host data into a GPU buffer.
|
|
645
|
+
*
|
|
646
|
+
* This copies bytes from JS-owned memory into the destination GPU buffer
|
|
647
|
+
* range on the queue.
|
|
648
|
+
*
|
|
649
|
+
* This example shows the API in its basic form.
|
|
650
|
+
*
|
|
651
|
+
* ```js
|
|
652
|
+
* device.queue.writeBuffer(buffer, 0, new Float32Array([1, 2, 3, 4]));
|
|
653
|
+
* ```
|
|
654
|
+
*
|
|
655
|
+
* - `dataOffset` and `size` are interpreted in element units for typed arrays.
|
|
656
|
+
* - Doe converts the requested range into bytes before writing it.
|
|
657
|
+
*/
|
|
305
658
|
writeBuffer(buffer, bufferOffset, data, dataOffset = 0, size) {
|
|
306
659
|
let view = data;
|
|
307
660
|
if (dataOffset > 0 || size !== undefined) {
|
|
@@ -314,55 +667,230 @@ class DoeGPUQueue {
|
|
|
314
667
|
addon.queueWriteBuffer(this._native, buffer._native, bufferOffset, view);
|
|
315
668
|
}
|
|
316
669
|
|
|
670
|
+
/**
|
|
671
|
+
* Resolve after submitted work has been flushed.
|
|
672
|
+
*
|
|
673
|
+
* This gives callers a simple way to wait until Doe has drained the tracked
|
|
674
|
+
* queue work relevant to this device.
|
|
675
|
+
*
|
|
676
|
+
* This example shows the API in its basic form.
|
|
677
|
+
*
|
|
678
|
+
* ```js
|
|
679
|
+
* await device.queue.onSubmittedWorkDone();
|
|
680
|
+
* ```
|
|
681
|
+
*
|
|
682
|
+
* - If no submissions are pending, this resolves immediately.
|
|
683
|
+
* - Doe flushes the native queue before marking the tracked work complete.
|
|
684
|
+
*/
|
|
317
685
|
async onSubmittedWorkDone() {
|
|
318
686
|
if (!this.hasPendingSubmissions()) return;
|
|
319
|
-
|
|
687
|
+
try {
|
|
688
|
+
addon.queueFlush(this._instance, this._native);
|
|
689
|
+
} catch (error) {
|
|
690
|
+
if (/queueFlush: wgpuInstanceWaitAny failed|queueFlush: doeNativeQueueFlush not available/.test(String(error?.message ?? error))) {
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
throw error;
|
|
694
|
+
}
|
|
320
695
|
this.markSubmittedWorkDone();
|
|
321
696
|
}
|
|
322
697
|
}
|
|
323
698
|
|
|
699
|
+
/**
|
|
700
|
+
* Render pass encoder returned by `commandEncoder.beginRenderPass(...)`.
|
|
701
|
+
*
|
|
702
|
+
* This provides the subset of render-pass methods currently surfaced by the
|
|
703
|
+
* full headless package.
|
|
704
|
+
*
|
|
705
|
+
* This example shows the API in its basic form.
|
|
706
|
+
*
|
|
707
|
+
* ```js
|
|
708
|
+
* const pass = encoder.beginRenderPass({ colorAttachments: [{ view }] });
|
|
709
|
+
* ```
|
|
710
|
+
*
|
|
711
|
+
* - The exposed render API is intentionally narrower than a browser implementation.
|
|
712
|
+
* - Submission still happens through the command encoder and queue.
|
|
713
|
+
*/
|
|
324
714
|
class DoeGPURenderPassEncoder {
|
|
325
715
|
constructor(native) { this._native = native; }
|
|
326
716
|
|
|
717
|
+
/**
|
|
718
|
+
* Set the render pipeline used by later draw calls.
|
|
719
|
+
*
|
|
720
|
+
* This records the pipeline state that subsequent draw calls in the pass
|
|
721
|
+
* should use.
|
|
722
|
+
*
|
|
723
|
+
* This example shows the API in its basic form.
|
|
724
|
+
*
|
|
725
|
+
* ```js
|
|
726
|
+
* pass.setPipeline(pipeline);
|
|
727
|
+
* ```
|
|
728
|
+
*
|
|
729
|
+
* - The pipeline must come from the same device.
|
|
730
|
+
* - Call this before `draw(...)`.
|
|
731
|
+
*/
|
|
327
732
|
setPipeline(pipeline) {
|
|
328
733
|
addon.renderPassSetPipeline(this._native, pipeline._native);
|
|
329
734
|
}
|
|
330
735
|
|
|
736
|
+
/**
|
|
737
|
+
* Record a non-indexed draw.
|
|
738
|
+
*
|
|
739
|
+
* This queues a draw call using the current render pipeline and bound
|
|
740
|
+
* attachments.
|
|
741
|
+
*
|
|
742
|
+
* This example shows the API in its basic form.
|
|
743
|
+
*
|
|
744
|
+
* ```js
|
|
745
|
+
* pass.draw(3);
|
|
746
|
+
* ```
|
|
747
|
+
*
|
|
748
|
+
* - Omitted instance and offset arguments default to the WebGPU-style values.
|
|
749
|
+
* - Draw calls only become visible after the command buffer is submitted.
|
|
750
|
+
*/
|
|
331
751
|
draw(vertexCount, instanceCount = 1, firstVertex = 0, firstInstance = 0) {
|
|
332
752
|
addon.renderPassDraw(this._native, vertexCount, instanceCount, firstVertex, firstInstance);
|
|
333
753
|
}
|
|
334
754
|
|
|
755
|
+
/**
|
|
756
|
+
* Finish the render pass.
|
|
757
|
+
*
|
|
758
|
+
* This closes the native render-pass encoder so the command encoder can
|
|
759
|
+
* continue recording.
|
|
760
|
+
*
|
|
761
|
+
* This example shows the API in its basic form.
|
|
762
|
+
*
|
|
763
|
+
* ```js
|
|
764
|
+
* pass.end();
|
|
765
|
+
* ```
|
|
766
|
+
*
|
|
767
|
+
* - This closes the native render pass encoder.
|
|
768
|
+
* - It does not submit work by itself.
|
|
769
|
+
*/
|
|
335
770
|
end() {
|
|
336
771
|
addon.renderPassEnd(this._native);
|
|
337
772
|
}
|
|
338
773
|
}
|
|
339
774
|
|
|
775
|
+
/**
|
|
776
|
+
* Texture returned by `device.createTexture(...)`.
|
|
777
|
+
*
|
|
778
|
+
* This represents a headless Doe texture resource and can create default views
|
|
779
|
+
* for render or sampling usage.
|
|
780
|
+
*
|
|
781
|
+
* This example shows the API in its basic form.
|
|
782
|
+
*
|
|
783
|
+
* ```js
|
|
784
|
+
* const texture = device.createTexture({
|
|
785
|
+
* size: [64, 64, 1],
|
|
786
|
+
* format: "rgba8unorm",
|
|
787
|
+
* usage: GPUTextureUsage.RENDER_ATTACHMENT,
|
|
788
|
+
* });
|
|
789
|
+
* ```
|
|
790
|
+
*
|
|
791
|
+
* - The package currently exposes the texture operations needed by its headless surface.
|
|
792
|
+
* - Texture views are created through `createView(...)`.
|
|
793
|
+
*/
|
|
340
794
|
class DoeGPUTexture {
|
|
341
795
|
constructor(native) { this._native = native; }
|
|
342
796
|
|
|
797
|
+
/**
|
|
798
|
+
* Create a texture view.
|
|
799
|
+
*
|
|
800
|
+
* This returns a default texture view wrapper for the texture so it can be
|
|
801
|
+
* used in render or sampling APIs.
|
|
802
|
+
*
|
|
803
|
+
* This example shows the API in its basic form.
|
|
804
|
+
*
|
|
805
|
+
* ```js
|
|
806
|
+
* const view = texture.createView();
|
|
807
|
+
* ```
|
|
808
|
+
*
|
|
809
|
+
* - Doe currently ignores most descriptor variation here and creates a default view.
|
|
810
|
+
* - The returned view is suitable for the package's headless render paths.
|
|
811
|
+
*/
|
|
343
812
|
createView(descriptor) {
|
|
344
813
|
const view = addon.textureCreateView(this._native);
|
|
345
814
|
return new DoeGPUTextureView(view);
|
|
346
815
|
}
|
|
347
816
|
|
|
817
|
+
/**
|
|
818
|
+
* Release the native texture.
|
|
819
|
+
*
|
|
820
|
+
* This tears down the underlying Doe texture allocation associated with the
|
|
821
|
+
* wrapper.
|
|
822
|
+
*
|
|
823
|
+
* This example shows the API in its basic form.
|
|
824
|
+
*
|
|
825
|
+
* ```js
|
|
826
|
+
* texture.destroy();
|
|
827
|
+
* ```
|
|
828
|
+
*
|
|
829
|
+
* - Reusing the texture after destruction is unsupported.
|
|
830
|
+
* - Views already created are plain JS wrappers and do not keep the texture alive.
|
|
831
|
+
*/
|
|
348
832
|
destroy() {
|
|
349
833
|
addon.textureRelease(this._native);
|
|
350
834
|
this._native = null;
|
|
351
835
|
}
|
|
352
836
|
}
|
|
353
837
|
|
|
838
|
+
/**
|
|
839
|
+
* Texture view wrapper returned by `texture.createView()`.
|
|
840
|
+
*
|
|
841
|
+
* This example shows the API in its basic form.
|
|
842
|
+
*
|
|
843
|
+
* ```js
|
|
844
|
+
* const view = texture.createView();
|
|
845
|
+
* ```
|
|
846
|
+
*
|
|
847
|
+
* - This package currently treats the view as a lightweight opaque handle.
|
|
848
|
+
*/
|
|
354
849
|
class DoeGPUTextureView {
|
|
355
850
|
constructor(native) { this._native = native; }
|
|
356
851
|
}
|
|
357
852
|
|
|
853
|
+
/**
|
|
854
|
+
* Sampler wrapper returned by `device.createSampler(...)`.
|
|
855
|
+
*
|
|
856
|
+
* This example shows the API in its basic form.
|
|
857
|
+
*
|
|
858
|
+
* ```js
|
|
859
|
+
* const sampler = device.createSampler();
|
|
860
|
+
* ```
|
|
861
|
+
*
|
|
862
|
+
* - The sampler is currently an opaque handle on the JS side.
|
|
863
|
+
*/
|
|
358
864
|
class DoeGPUSampler {
|
|
359
865
|
constructor(native) { this._native = native; }
|
|
360
866
|
}
|
|
361
867
|
|
|
868
|
+
/**
|
|
869
|
+
* Render pipeline returned by `device.createRenderPipeline(...)`.
|
|
870
|
+
*
|
|
871
|
+
* This example shows the API in its basic form.
|
|
872
|
+
*
|
|
873
|
+
* ```js
|
|
874
|
+
* const pipeline = device.createRenderPipeline(descriptor);
|
|
875
|
+
* ```
|
|
876
|
+
*
|
|
877
|
+
* - The JS wrapper is currently an opaque handle used by render passes.
|
|
878
|
+
*/
|
|
362
879
|
class DoeGPURenderPipeline {
|
|
363
880
|
constructor(native) { this._native = native; }
|
|
364
881
|
}
|
|
365
882
|
|
|
883
|
+
/**
|
|
884
|
+
* Shader module returned by `device.createShaderModule(...)`.
|
|
885
|
+
*
|
|
886
|
+
* This example shows the API in its basic form.
|
|
887
|
+
*
|
|
888
|
+
* ```js
|
|
889
|
+
* const shader = device.createShaderModule({ code: WGSL });
|
|
890
|
+
* ```
|
|
891
|
+
*
|
|
892
|
+
* - Doe keeps the WGSL source on the wrapper for pipeline creation and auto-layout work.
|
|
893
|
+
*/
|
|
366
894
|
class DoeGPUShaderModule {
|
|
367
895
|
constructor(native, code) {
|
|
368
896
|
this._native = native;
|
|
@@ -370,6 +898,24 @@ class DoeGPUShaderModule {
|
|
|
370
898
|
}
|
|
371
899
|
}
|
|
372
900
|
|
|
901
|
+
/**
|
|
902
|
+
* Compute pipeline returned by `device.createComputePipeline(...)`.
|
|
903
|
+
*
|
|
904
|
+
* This wrapper exposes pipeline layout lookup for bind-group creation and
|
|
905
|
+
* dispatch setup.
|
|
906
|
+
*
|
|
907
|
+
* This example shows the API in its basic form.
|
|
908
|
+
*
|
|
909
|
+
* ```js
|
|
910
|
+
* const pipeline = device.createComputePipeline({
|
|
911
|
+
* layout: "auto",
|
|
912
|
+
* compute: { module: shader, entryPoint: "main" },
|
|
913
|
+
* });
|
|
914
|
+
* ```
|
|
915
|
+
*
|
|
916
|
+
* - Auto-layout pipelines derive bind-group layouts from the shader source.
|
|
917
|
+
* - Explicit-layout pipelines return the layout they were created with.
|
|
918
|
+
*/
|
|
373
919
|
class DoeGPUComputePipeline {
|
|
374
920
|
constructor(native, device, explicitLayout, autoLayoutEntriesByGroup) {
|
|
375
921
|
this._native = native;
|
|
@@ -379,29 +925,88 @@ class DoeGPUComputePipeline {
|
|
|
379
925
|
this._cachedLayouts = new Map();
|
|
380
926
|
}
|
|
381
927
|
|
|
928
|
+
/**
|
|
929
|
+
* Return the bind-group layout for a given group index.
|
|
930
|
+
*
|
|
931
|
+
* This gives callers the layout object needed to construct compatible bind
|
|
932
|
+
* groups for the pipeline.
|
|
933
|
+
*
|
|
934
|
+
* This example shows the API in its basic form.
|
|
935
|
+
*
|
|
936
|
+
* ```js
|
|
937
|
+
* const layout = pipeline.getBindGroupLayout(0);
|
|
938
|
+
* ```
|
|
939
|
+
*
|
|
940
|
+
* - Auto-layout pipelines lazily build and cache layouts by group index.
|
|
941
|
+
* - Explicit-layout pipelines return their original layout for any requested index.
|
|
942
|
+
*/
|
|
382
943
|
getBindGroupLayout(index) {
|
|
383
944
|
if (this._explicitLayout) return this._explicitLayout;
|
|
384
945
|
if (this._cachedLayouts.has(index)) return this._cachedLayouts.get(index);
|
|
385
|
-
|
|
386
|
-
|
|
946
|
+
let layout;
|
|
947
|
+
if (this._autoLayoutEntriesByGroup && process.platform === 'darwin') {
|
|
948
|
+
const entries = this._autoLayoutEntriesByGroup.get(index) ?? [];
|
|
949
|
+
layout = this._device.createBindGroupLayout({ entries });
|
|
950
|
+
} else if (typeof addon.computePipelineGetBindGroupLayout === 'function') {
|
|
951
|
+
layout = new DoeGPUBindGroupLayout(
|
|
952
|
+
addon.computePipelineGetBindGroupLayout(this._native, index),
|
|
953
|
+
);
|
|
954
|
+
} else if (this._autoLayoutEntriesByGroup) {
|
|
955
|
+
const entries = this._autoLayoutEntriesByGroup.get(index) ?? [];
|
|
956
|
+
layout = this._device.createBindGroupLayout({ entries });
|
|
957
|
+
} else {
|
|
958
|
+
layout = this._device.createBindGroupLayout({ entries: [] });
|
|
959
|
+
}
|
|
387
960
|
this._cachedLayouts.set(index, layout);
|
|
388
961
|
return layout;
|
|
389
962
|
}
|
|
390
963
|
}
|
|
391
964
|
|
|
965
|
+
/**
|
|
966
|
+
* Bind-group layout returned by `device.createBindGroupLayout(...)`.
|
|
967
|
+
*
|
|
968
|
+
* This example shows the API in its basic form.
|
|
969
|
+
*
|
|
970
|
+
* ```js
|
|
971
|
+
* const layout = device.createBindGroupLayout({ entries });
|
|
972
|
+
* ```
|
|
973
|
+
*
|
|
974
|
+
* - The JS wrapper is an opaque handle used when creating bind groups and pipelines.
|
|
975
|
+
*/
|
|
392
976
|
class DoeGPUBindGroupLayout {
|
|
393
977
|
constructor(native) { this._native = native; }
|
|
394
978
|
}
|
|
395
979
|
|
|
980
|
+
/**
|
|
981
|
+
* Bind group returned by `device.createBindGroup(...)`.
|
|
982
|
+
*
|
|
983
|
+
* This example shows the API in its basic form.
|
|
984
|
+
*
|
|
985
|
+
* ```js
|
|
986
|
+
* const bindGroup = device.createBindGroup({ layout, entries });
|
|
987
|
+
* ```
|
|
988
|
+
*
|
|
989
|
+
* - The JS wrapper is an opaque handle consumed by pass encoders.
|
|
990
|
+
*/
|
|
396
991
|
class DoeGPUBindGroup {
|
|
397
992
|
constructor(native) { this._native = native; }
|
|
398
993
|
}
|
|
399
994
|
|
|
995
|
+
/**
|
|
996
|
+
* Pipeline layout returned by `device.createPipelineLayout(...)`.
|
|
997
|
+
*
|
|
998
|
+
* This example shows the API in its basic form.
|
|
999
|
+
*
|
|
1000
|
+
* ```js
|
|
1001
|
+
* const layout = device.createPipelineLayout({ bindGroupLayouts: [group0] });
|
|
1002
|
+
* ```
|
|
1003
|
+
*
|
|
1004
|
+
* - The JS wrapper is an opaque handle passed into pipeline creation.
|
|
1005
|
+
*/
|
|
400
1006
|
class DoeGPUPipelineLayout {
|
|
401
1007
|
constructor(native) { this._native = native; }
|
|
402
1008
|
}
|
|
403
1009
|
|
|
404
|
-
// Metal defaults for Apple Silicon — matches doe_device_caps.zig METAL_LIMITS.
|
|
405
1010
|
const DOE_LIMITS = Object.freeze({
|
|
406
1011
|
maxTextureDimension1D: 16384,
|
|
407
1012
|
maxTextureDimension2D: 16384,
|
|
@@ -438,34 +1043,20 @@ const DOE_LIMITS = Object.freeze({
|
|
|
438
1043
|
|
|
439
1044
|
const DOE_FEATURES = Object.freeze(new Set(['shader-f16']));
|
|
440
1045
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
} else if (typeExpr.startsWith("sampler")) {
|
|
456
|
-
entry = { binding, visibility, sampler: {} };
|
|
457
|
-
}
|
|
458
|
-
if (!entry) continue;
|
|
459
|
-
const entries = groups.get(group) ?? [];
|
|
460
|
-
entries.push(entry);
|
|
461
|
-
groups.set(group, entries);
|
|
462
|
-
}
|
|
463
|
-
for (const entries of groups.values()) {
|
|
464
|
-
entries.sort((left, right) => left.binding - right.binding);
|
|
465
|
-
}
|
|
466
|
-
return groups;
|
|
467
|
-
}
|
|
468
|
-
|
|
1046
|
+
/**
|
|
1047
|
+
* Device returned by `adapter.requestDevice()`.
|
|
1048
|
+
*
|
|
1049
|
+
* This is the main full-surface headless WebGPU object exposed by the package.
|
|
1050
|
+
*
|
|
1051
|
+
* This example shows the API in its basic form.
|
|
1052
|
+
*
|
|
1053
|
+
* ```js
|
|
1054
|
+
* const device = await adapter.requestDevice();
|
|
1055
|
+
* ```
|
|
1056
|
+
*
|
|
1057
|
+
* - `queue`, `limits`, and `features` are available as data properties.
|
|
1058
|
+
* - The full package keeps render, texture, sampler, and command APIs on this object.
|
|
1059
|
+
*/
|
|
469
1060
|
class DoeGPUDevice {
|
|
470
1061
|
constructor(native, instance) {
|
|
471
1062
|
this._native = native;
|
|
@@ -476,11 +1067,44 @@ class DoeGPUDevice {
|
|
|
476
1067
|
this.features = DOE_FEATURES;
|
|
477
1068
|
}
|
|
478
1069
|
|
|
1070
|
+
/**
|
|
1071
|
+
* Create a buffer.
|
|
1072
|
+
*
|
|
1073
|
+
* This allocates a Doe buffer using the supplied WebGPU-shaped descriptor and
|
|
1074
|
+
* returns the package wrapper for it.
|
|
1075
|
+
*
|
|
1076
|
+
* This example shows the API in its basic form.
|
|
1077
|
+
*
|
|
1078
|
+
* ```js
|
|
1079
|
+
* const buffer = device.createBuffer({
|
|
1080
|
+
* size: 16,
|
|
1081
|
+
* usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
1082
|
+
* });
|
|
1083
|
+
* ```
|
|
1084
|
+
*
|
|
1085
|
+
* - The descriptor follows the standard WebGPU buffer shape.
|
|
1086
|
+
* - The returned wrapper exposes `size`, `usage`, mapping, and destruction helpers.
|
|
1087
|
+
*/
|
|
479
1088
|
createBuffer(descriptor) {
|
|
480
1089
|
const buf = addon.createBuffer(this._native, descriptor);
|
|
481
1090
|
return new DoeGPUBuffer(buf, this._instance, descriptor.size, descriptor.usage, this.queue);
|
|
482
1091
|
}
|
|
483
1092
|
|
|
1093
|
+
/**
|
|
1094
|
+
* Create a shader module from WGSL source.
|
|
1095
|
+
*
|
|
1096
|
+
* This compiles WGSL into a shader module wrapper that can be used by
|
|
1097
|
+
* compute or render pipeline creation.
|
|
1098
|
+
*
|
|
1099
|
+
* This example shows the API in its basic form.
|
|
1100
|
+
*
|
|
1101
|
+
* ```js
|
|
1102
|
+
* const shader = device.createShaderModule({ code: WGSL });
|
|
1103
|
+
* ```
|
|
1104
|
+
*
|
|
1105
|
+
* - `descriptor.code` is required on this surface.
|
|
1106
|
+
* - The package also accepts `descriptor.source` as a convenience alias.
|
|
1107
|
+
*/
|
|
484
1108
|
createShaderModule(descriptor) {
|
|
485
1109
|
const code = descriptor.code || descriptor.source;
|
|
486
1110
|
if (!code) throw new Error('createShaderModule: descriptor.code is required');
|
|
@@ -488,21 +1112,72 @@ class DoeGPUDevice {
|
|
|
488
1112
|
return new DoeGPUShaderModule(mod, code);
|
|
489
1113
|
}
|
|
490
1114
|
|
|
1115
|
+
/**
|
|
1116
|
+
* Create a compute pipeline.
|
|
1117
|
+
*
|
|
1118
|
+
* This builds a pipeline wrapper from a shader module, entry point, and
|
|
1119
|
+
* optional explicit layout information.
|
|
1120
|
+
*
|
|
1121
|
+
* This example shows the API in its basic form.
|
|
1122
|
+
*
|
|
1123
|
+
* ```js
|
|
1124
|
+
* const pipeline = device.createComputePipeline({
|
|
1125
|
+
* layout: "auto",
|
|
1126
|
+
* compute: { module: shader, entryPoint: "main" },
|
|
1127
|
+
* });
|
|
1128
|
+
* ```
|
|
1129
|
+
*
|
|
1130
|
+
* - `layout: "auto"` derives bind-group layouts from the WGSL.
|
|
1131
|
+
* - Explicit pipeline layouts are passed through directly.
|
|
1132
|
+
*/
|
|
491
1133
|
createComputePipeline(descriptor) {
|
|
492
1134
|
const shader = descriptor.compute?.module;
|
|
493
1135
|
const entryPoint = descriptor.compute?.entryPoint || 'main';
|
|
494
1136
|
const layout = descriptor.layout === 'auto' ? null : descriptor.layout;
|
|
495
|
-
const autoLayoutEntriesByGroup = layout ? null : inferAutoBindGroupLayouts(
|
|
1137
|
+
const autoLayoutEntriesByGroup = layout ? null : inferAutoBindGroupLayouts(
|
|
1138
|
+
shader?._code || '',
|
|
1139
|
+
globals.GPUShaderStage.COMPUTE,
|
|
1140
|
+
);
|
|
496
1141
|
const native = addon.createComputePipeline(
|
|
497
1142
|
this._native, shader._native, entryPoint,
|
|
498
1143
|
layout?._native ?? null);
|
|
499
1144
|
return new DoeGPUComputePipeline(native, this, layout, autoLayoutEntriesByGroup);
|
|
500
1145
|
}
|
|
501
1146
|
|
|
1147
|
+
/**
|
|
1148
|
+
* Create a compute pipeline through an async-shaped API.
|
|
1149
|
+
*
|
|
1150
|
+
* This preserves the async WebGPU API shape while using Doe's current
|
|
1151
|
+
* synchronous pipeline creation underneath.
|
|
1152
|
+
*
|
|
1153
|
+
* This example shows the API in its basic form.
|
|
1154
|
+
*
|
|
1155
|
+
* ```js
|
|
1156
|
+
* const pipeline = await device.createComputePipelineAsync(descriptor);
|
|
1157
|
+
* ```
|
|
1158
|
+
*
|
|
1159
|
+
* - Doe currently resolves this by calling the synchronous pipeline creation path.
|
|
1160
|
+
* - The async shape exists for WebGPU API compatibility.
|
|
1161
|
+
*/
|
|
502
1162
|
async createComputePipelineAsync(descriptor) {
|
|
503
1163
|
return this.createComputePipeline(descriptor);
|
|
504
1164
|
}
|
|
505
1165
|
|
|
1166
|
+
/**
|
|
1167
|
+
* Create a bind-group layout.
|
|
1168
|
+
*
|
|
1169
|
+
* This normalizes the descriptor into the shape expected by Doe and returns
|
|
1170
|
+
* a layout wrapper for later resource binding.
|
|
1171
|
+
*
|
|
1172
|
+
* This example shows the API in its basic form.
|
|
1173
|
+
*
|
|
1174
|
+
* ```js
|
|
1175
|
+
* const layout = device.createBindGroupLayout({ entries });
|
|
1176
|
+
* ```
|
|
1177
|
+
*
|
|
1178
|
+
* - Missing buffer entry fields are normalized to WebGPU-style defaults.
|
|
1179
|
+
* - Storage-texture entries are forwarded when present.
|
|
1180
|
+
*/
|
|
506
1181
|
createBindGroupLayout(descriptor) {
|
|
507
1182
|
const entries = (descriptor.entries || []).map((e) => ({
|
|
508
1183
|
binding: e.binding,
|
|
@@ -518,6 +1193,21 @@ class DoeGPUDevice {
|
|
|
518
1193
|
return new DoeGPUBindGroupLayout(native);
|
|
519
1194
|
}
|
|
520
1195
|
|
|
1196
|
+
/**
|
|
1197
|
+
* Create a bind group.
|
|
1198
|
+
*
|
|
1199
|
+
* This binds resources to a previously created layout and returns the bind
|
|
1200
|
+
* group wrapper used by pass encoders.
|
|
1201
|
+
*
|
|
1202
|
+
* This example shows the API in its basic form.
|
|
1203
|
+
*
|
|
1204
|
+
* ```js
|
|
1205
|
+
* const bindGroup = device.createBindGroup({ layout, entries });
|
|
1206
|
+
* ```
|
|
1207
|
+
*
|
|
1208
|
+
* - Resource buffers may be passed either as `{ buffer, offset, size }` or as bare buffer wrappers.
|
|
1209
|
+
* - Layout and buffer wrappers must come from the same device.
|
|
1210
|
+
*/
|
|
521
1211
|
createBindGroup(descriptor) {
|
|
522
1212
|
const entries = (descriptor.entries || []).map((e) => {
|
|
523
1213
|
const entry = {
|
|
@@ -533,12 +1223,46 @@ class DoeGPUDevice {
|
|
|
533
1223
|
return new DoeGPUBindGroup(native);
|
|
534
1224
|
}
|
|
535
1225
|
|
|
1226
|
+
/**
|
|
1227
|
+
* Create a pipeline layout.
|
|
1228
|
+
*
|
|
1229
|
+
* This combines one or more bind-group layouts into the pipeline layout
|
|
1230
|
+
* wrapper used during pipeline creation.
|
|
1231
|
+
*
|
|
1232
|
+
* This example shows the API in its basic form.
|
|
1233
|
+
*
|
|
1234
|
+
* ```js
|
|
1235
|
+
* const layout = device.createPipelineLayout({ bindGroupLayouts: [group0] });
|
|
1236
|
+
* ```
|
|
1237
|
+
*
|
|
1238
|
+
* - Bind-group layouts are unwrapped to their native handles before creation.
|
|
1239
|
+
* - The returned wrapper is opaque on the JS side.
|
|
1240
|
+
*/
|
|
536
1241
|
createPipelineLayout(descriptor) {
|
|
537
1242
|
const layouts = (descriptor.bindGroupLayouts || []).map((l) => l._native);
|
|
538
1243
|
const native = addon.createPipelineLayout(this._native, layouts);
|
|
539
1244
|
return new DoeGPUPipelineLayout(native);
|
|
540
1245
|
}
|
|
541
1246
|
|
|
1247
|
+
/**
|
|
1248
|
+
* Create a texture.
|
|
1249
|
+
*
|
|
1250
|
+
* This allocates a Doe texture resource from a WebGPU-shaped descriptor and
|
|
1251
|
+
* returns the package wrapper for it.
|
|
1252
|
+
*
|
|
1253
|
+
* This example shows the API in its basic form.
|
|
1254
|
+
*
|
|
1255
|
+
* ```js
|
|
1256
|
+
* const texture = device.createTexture({
|
|
1257
|
+
* size: [64, 64, 1],
|
|
1258
|
+
* format: "rgba8unorm",
|
|
1259
|
+
* usage: GPUTextureUsage.RENDER_ATTACHMENT,
|
|
1260
|
+
* });
|
|
1261
|
+
* ```
|
|
1262
|
+
*
|
|
1263
|
+
* - `descriptor.size` may be a scalar, tuple, or width/height object.
|
|
1264
|
+
* - Omitted format and mip-count fields fall back to package defaults.
|
|
1265
|
+
*/
|
|
542
1266
|
createTexture(descriptor) {
|
|
543
1267
|
const native = addon.createTexture(this._native, {
|
|
544
1268
|
format: descriptor.format || 'rgba8unorm',
|
|
@@ -551,26 +1275,97 @@ class DoeGPUDevice {
|
|
|
551
1275
|
return new DoeGPUTexture(native);
|
|
552
1276
|
}
|
|
553
1277
|
|
|
1278
|
+
/**
|
|
1279
|
+
* Create a sampler.
|
|
1280
|
+
*
|
|
1281
|
+
* This allocates a sampler wrapper that can be used by the package's render
|
|
1282
|
+
* and texture-binding paths.
|
|
1283
|
+
*
|
|
1284
|
+
* This example shows the API in its basic form.
|
|
1285
|
+
*
|
|
1286
|
+
* ```js
|
|
1287
|
+
* const sampler = device.createSampler();
|
|
1288
|
+
* ```
|
|
1289
|
+
*
|
|
1290
|
+
* - An empty descriptor is allowed.
|
|
1291
|
+
* - The returned wrapper is currently an opaque handle on the JS side.
|
|
1292
|
+
*/
|
|
554
1293
|
createSampler(descriptor = {}) {
|
|
555
1294
|
const native = addon.createSampler(this._native, descriptor);
|
|
556
1295
|
return new DoeGPUSampler(native);
|
|
557
1296
|
}
|
|
558
1297
|
|
|
1298
|
+
/**
|
|
1299
|
+
* Create a render pipeline.
|
|
1300
|
+
*
|
|
1301
|
+
* This builds the package's render-pipeline wrapper for use with render-pass
|
|
1302
|
+
* encoders on the full surface.
|
|
1303
|
+
*
|
|
1304
|
+
* This example shows the API in its basic form.
|
|
1305
|
+
*
|
|
1306
|
+
* ```js
|
|
1307
|
+
* const pipeline = device.createRenderPipeline(descriptor);
|
|
1308
|
+
* ```
|
|
1309
|
+
*
|
|
1310
|
+
* - The returned wrapper is consumed by render-pass encoders.
|
|
1311
|
+
* - Descriptor handling on this package surface is intentionally narrower than browser engines.
|
|
1312
|
+
*/
|
|
559
1313
|
createRenderPipeline(descriptor) {
|
|
560
1314
|
const native = addon.createRenderPipeline(this._native);
|
|
561
1315
|
return new DoeGPURenderPipeline(native);
|
|
562
1316
|
}
|
|
563
1317
|
|
|
1318
|
+
/**
|
|
1319
|
+
* Create a command encoder.
|
|
1320
|
+
*
|
|
1321
|
+
* This creates the object that records compute, render, and copy commands
|
|
1322
|
+
* before queue submission.
|
|
1323
|
+
*
|
|
1324
|
+
* This example shows the API in its basic form.
|
|
1325
|
+
*
|
|
1326
|
+
* ```js
|
|
1327
|
+
* const encoder = device.createCommandEncoder();
|
|
1328
|
+
* ```
|
|
1329
|
+
*
|
|
1330
|
+
* - The descriptor is accepted for API shape compatibility.
|
|
1331
|
+
* - The returned encoder records work until `finish()` is called.
|
|
1332
|
+
*/
|
|
564
1333
|
createCommandEncoder(descriptor) {
|
|
565
1334
|
return new DoeGPUCommandEncoder(this._native);
|
|
566
1335
|
}
|
|
567
1336
|
|
|
1337
|
+
/**
|
|
1338
|
+
* Release the native device.
|
|
1339
|
+
*
|
|
1340
|
+
* This tears down the underlying Doe device associated with the wrapper.
|
|
1341
|
+
*
|
|
1342
|
+
* This example shows the API in its basic form.
|
|
1343
|
+
*
|
|
1344
|
+
* ```js
|
|
1345
|
+
* device.destroy();
|
|
1346
|
+
* ```
|
|
1347
|
+
*
|
|
1348
|
+
* - Reusing the device after destruction is unsupported.
|
|
1349
|
+
* - Existing wrappers created from the device do not regain validity afterward.
|
|
1350
|
+
*/
|
|
568
1351
|
destroy() {
|
|
569
1352
|
addon.deviceRelease(this._native);
|
|
570
1353
|
this._native = null;
|
|
571
1354
|
}
|
|
572
1355
|
}
|
|
573
1356
|
|
|
1357
|
+
/**
|
|
1358
|
+
* Adapter returned by `gpu.requestAdapter()`.
|
|
1359
|
+
*
|
|
1360
|
+
* This example shows the API in its basic form.
|
|
1361
|
+
*
|
|
1362
|
+
* ```js
|
|
1363
|
+
* const adapter = await gpu.requestAdapter();
|
|
1364
|
+
* ```
|
|
1365
|
+
*
|
|
1366
|
+
* - `features` and `limits` are exposed as data properties.
|
|
1367
|
+
* - The adapter produces full-surface devices on this package entrypoint.
|
|
1368
|
+
*/
|
|
574
1369
|
class DoeGPUAdapter {
|
|
575
1370
|
constructor(native, instance) {
|
|
576
1371
|
this._native = native;
|
|
@@ -579,34 +1374,124 @@ class DoeGPUAdapter {
|
|
|
579
1374
|
this.limits = DOE_LIMITS;
|
|
580
1375
|
}
|
|
581
1376
|
|
|
1377
|
+
/**
|
|
1378
|
+
* Request a device from this adapter.
|
|
1379
|
+
*
|
|
1380
|
+
* This creates the full-surface Doe device associated with the adapter.
|
|
1381
|
+
*
|
|
1382
|
+
* This example shows the API in its basic form.
|
|
1383
|
+
*
|
|
1384
|
+
* ```js
|
|
1385
|
+
* const device = await adapter.requestDevice();
|
|
1386
|
+
* ```
|
|
1387
|
+
*
|
|
1388
|
+
* - The descriptor is accepted for WebGPU API shape compatibility.
|
|
1389
|
+
* - The returned device includes the full package surface.
|
|
1390
|
+
*/
|
|
582
1391
|
async requestDevice(descriptor) {
|
|
583
1392
|
const device = addon.requestDevice(this._instance, this._native);
|
|
584
1393
|
return new DoeGPUDevice(device, this._instance);
|
|
585
1394
|
}
|
|
586
1395
|
|
|
1396
|
+
/**
|
|
1397
|
+
* Release the native adapter.
|
|
1398
|
+
*
|
|
1399
|
+
* This tears down the adapter handle that was returned by Doe for this GPU.
|
|
1400
|
+
*
|
|
1401
|
+
* This example shows the API in its basic form.
|
|
1402
|
+
*
|
|
1403
|
+
* ```js
|
|
1404
|
+
* adapter.destroy();
|
|
1405
|
+
* ```
|
|
1406
|
+
*
|
|
1407
|
+
* - Reusing the adapter after destruction is unsupported.
|
|
1408
|
+
*/
|
|
587
1409
|
destroy() {
|
|
588
1410
|
addon.adapterRelease(this._native);
|
|
589
1411
|
this._native = null;
|
|
590
1412
|
}
|
|
591
1413
|
}
|
|
592
1414
|
|
|
1415
|
+
/**
|
|
1416
|
+
* GPU root object returned by `create()` or installed at `navigator.gpu`.
|
|
1417
|
+
*
|
|
1418
|
+
* This example shows the API in its basic form.
|
|
1419
|
+
*
|
|
1420
|
+
* ```js
|
|
1421
|
+
* const gpu = create();
|
|
1422
|
+
* ```
|
|
1423
|
+
*
|
|
1424
|
+
* - This is a headless package-owned GPU object, not a browser-owned DOM object.
|
|
1425
|
+
*/
|
|
593
1426
|
class DoeGPU {
|
|
594
1427
|
constructor(instance) {
|
|
595
1428
|
this._instance = instance;
|
|
596
1429
|
}
|
|
597
1430
|
|
|
1431
|
+
/**
|
|
1432
|
+
* Request an adapter from the Doe runtime.
|
|
1433
|
+
*
|
|
1434
|
+
* This asks the package-owned GPU object for an adapter wrapper that can
|
|
1435
|
+
* later create full-surface devices.
|
|
1436
|
+
*
|
|
1437
|
+
* This example shows the API in its basic form.
|
|
1438
|
+
*
|
|
1439
|
+
* ```js
|
|
1440
|
+
* const adapter = await gpu.requestAdapter();
|
|
1441
|
+
* ```
|
|
1442
|
+
*
|
|
1443
|
+
* - The current Doe package path ignores adapter filtering options.
|
|
1444
|
+
* - The returned adapter exposes full-surface device creation.
|
|
1445
|
+
*/
|
|
598
1446
|
async requestAdapter(options) {
|
|
599
1447
|
const adapter = addon.requestAdapter(this._instance);
|
|
600
1448
|
return new DoeGPUAdapter(adapter, this._instance);
|
|
601
1449
|
}
|
|
602
1450
|
}
|
|
603
1451
|
|
|
1452
|
+
/**
|
|
1453
|
+
* Create a package-local `GPU` object backed by the Doe native runtime.
|
|
1454
|
+
*
|
|
1455
|
+
* This loads the addon/runtime if needed, creates a fresh GPU instance, and
|
|
1456
|
+
* returns an object with `requestAdapter(...)`.
|
|
1457
|
+
*
|
|
1458
|
+
* This example shows the API in its basic form.
|
|
1459
|
+
*
|
|
1460
|
+
* ```js
|
|
1461
|
+
* import { create } from "@simulatte/webgpu";
|
|
1462
|
+
*
|
|
1463
|
+
* const gpu = create();
|
|
1464
|
+
* const adapter = await gpu.requestAdapter();
|
|
1465
|
+
* ```
|
|
1466
|
+
*
|
|
1467
|
+
* - Throws if the native addon or `libwebgpu_doe` cannot be found.
|
|
1468
|
+
* - `createArgs` are currently accepted for API stability but ignored by the default Doe-native provider path.
|
|
1469
|
+
*/
|
|
604
1470
|
export function create(createArgs = null) {
|
|
605
1471
|
ensureLibrary();
|
|
606
1472
|
const instance = addon.createInstance();
|
|
607
1473
|
return new DoeGPU(instance);
|
|
608
1474
|
}
|
|
609
1475
|
|
|
1476
|
+
/**
|
|
1477
|
+
* Install the package WebGPU globals onto a target object and return its GPU.
|
|
1478
|
+
*
|
|
1479
|
+
* This adds missing enum globals plus `navigator.gpu` to `target`, then
|
|
1480
|
+
* returns the created package-local GPU object.
|
|
1481
|
+
*
|
|
1482
|
+
* This example shows the API in its basic form.
|
|
1483
|
+
*
|
|
1484
|
+
* ```js
|
|
1485
|
+
* import { setupGlobals } from "@simulatte/webgpu";
|
|
1486
|
+
*
|
|
1487
|
+
* setupGlobals(globalThis);
|
|
1488
|
+
* const adapter = await navigator.gpu.requestAdapter();
|
|
1489
|
+
* ```
|
|
1490
|
+
*
|
|
1491
|
+
* - Existing properties are preserved; this only fills in missing globals.
|
|
1492
|
+
* - If `target.navigator` exists without `gpu`, only `navigator.gpu` is added.
|
|
1493
|
+
* - The returned GPU is still headless/package-owned, not browser DOM ownership or browser-process parity.
|
|
1494
|
+
*/
|
|
610
1495
|
export function setupGlobals(target = globalThis, createArgs = null) {
|
|
611
1496
|
for (const [name, value] of Object.entries(globals)) {
|
|
612
1497
|
if (target[name] === undefined) {
|
|
@@ -637,17 +1522,72 @@ export function setupGlobals(target = globalThis, createArgs = null) {
|
|
|
637
1522
|
return gpu;
|
|
638
1523
|
}
|
|
639
1524
|
|
|
1525
|
+
/**
|
|
1526
|
+
* Request a Doe-backed adapter from the full package surface.
|
|
1527
|
+
*
|
|
1528
|
+
* This is a convenience wrapper over `create(...).requestAdapter(...)`.
|
|
1529
|
+
*
|
|
1530
|
+
* This example shows the API in its basic form.
|
|
1531
|
+
*
|
|
1532
|
+
* ```js
|
|
1533
|
+
* import { requestAdapter } from "@simulatte/webgpu";
|
|
1534
|
+
*
|
|
1535
|
+
* const adapter = await requestAdapter();
|
|
1536
|
+
* ```
|
|
1537
|
+
*
|
|
1538
|
+
* - Returns `null` if no adapter is available.
|
|
1539
|
+
* - `adapterOptions` are accepted for WebGPU shape compatibility; the current Doe package path does not use them for adapter filtering.
|
|
1540
|
+
*/
|
|
640
1541
|
export async function requestAdapter(adapterOptions = undefined, createArgs = null) {
|
|
641
1542
|
const gpu = create(createArgs);
|
|
642
1543
|
return gpu.requestAdapter(adapterOptions);
|
|
643
1544
|
}
|
|
644
1545
|
|
|
1546
|
+
/**
|
|
1547
|
+
* Request a Doe-backed device from the full package surface.
|
|
1548
|
+
*
|
|
1549
|
+
* This creates a package-local GPU, requests an adapter, then requests a
|
|
1550
|
+
* device from that adapter.
|
|
1551
|
+
*
|
|
1552
|
+
* This example shows the API in its basic form.
|
|
1553
|
+
*
|
|
1554
|
+
* ```js
|
|
1555
|
+
* import { requestDevice } from "@simulatte/webgpu";
|
|
1556
|
+
*
|
|
1557
|
+
* const device = await requestDevice();
|
|
1558
|
+
* const buffer = device.createBuffer({
|
|
1559
|
+
* size: 16,
|
|
1560
|
+
* usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
1561
|
+
* });
|
|
1562
|
+
* ```
|
|
1563
|
+
*
|
|
1564
|
+
* - On the full package surface, the returned device includes render, texture, sampler, and surface APIs when the runtime supports them.
|
|
1565
|
+
* - Missing runtime prerequisites still fail at request time through the same addon/library checks as `create()`.
|
|
1566
|
+
*/
|
|
645
1567
|
export async function requestDevice(options = {}) {
|
|
646
1568
|
const createArgs = options?.createArgs ?? null;
|
|
647
1569
|
const adapter = await requestAdapter(options?.adapterOptions, createArgs);
|
|
648
1570
|
return adapter.requestDevice(options?.deviceDescriptor);
|
|
649
1571
|
}
|
|
650
1572
|
|
|
1573
|
+
/**
|
|
1574
|
+
* Report how the package resolved and loaded the Doe runtime.
|
|
1575
|
+
*
|
|
1576
|
+
* This returns package/runtime provenance such as whether the native path is
|
|
1577
|
+
* loaded, which library flavor was chosen, and whether build metadata says the
|
|
1578
|
+
* runtime was built with Lean-verified mode.
|
|
1579
|
+
*
|
|
1580
|
+
* This example shows the API in its basic form.
|
|
1581
|
+
*
|
|
1582
|
+
* ```js
|
|
1583
|
+
* import { providerInfo } from "@simulatte/webgpu";
|
|
1584
|
+
*
|
|
1585
|
+
* console.log(providerInfo());
|
|
1586
|
+
* ```
|
|
1587
|
+
*
|
|
1588
|
+
* - If metadata is unavailable, `leanVerifiedBuild` is `null` rather than a guess.
|
|
1589
|
+
* - `loaded: false` is still diagnostically useful before attempting `requestDevice()`.
|
|
1590
|
+
*/
|
|
651
1591
|
export function providerInfo() {
|
|
652
1592
|
const flavor = libraryFlavor(DOE_LIB_PATH);
|
|
653
1593
|
return {
|
|
@@ -665,7 +1605,42 @@ export function providerInfo() {
|
|
|
665
1605
|
};
|
|
666
1606
|
}
|
|
667
1607
|
|
|
668
|
-
|
|
1608
|
+
/**
|
|
1609
|
+
* Create a Node or Bun runtime wrapper for Doe CLI execution.
|
|
1610
|
+
*
|
|
1611
|
+
* This exposes the package-side CLI bridge used for benchmark and command
|
|
1612
|
+
* stream execution workflows.
|
|
1613
|
+
*
|
|
1614
|
+
* This example shows the API in its basic form.
|
|
1615
|
+
*
|
|
1616
|
+
* ```js
|
|
1617
|
+
* import { createDoeRuntime } from "@simulatte/webgpu";
|
|
1618
|
+
*
|
|
1619
|
+
* const runtime = createDoeRuntime();
|
|
1620
|
+
* ```
|
|
1621
|
+
*
|
|
1622
|
+
* - This is package/runtime orchestration, not the in-process WebGPU device path.
|
|
1623
|
+
*/
|
|
1624
|
+
export const createDoeRuntime = createDoeRuntimeCli;
|
|
1625
|
+
|
|
1626
|
+
/**
|
|
1627
|
+
* Run the Dawn-vs-Doe compare harness from the full package surface.
|
|
1628
|
+
*
|
|
1629
|
+
* This forwards into the artifact-backed compare wrapper used by benchmark and
|
|
1630
|
+
* verification tooling.
|
|
1631
|
+
*
|
|
1632
|
+
* This example shows the API in its basic form.
|
|
1633
|
+
*
|
|
1634
|
+
* ```js
|
|
1635
|
+
* import { runDawnVsDoeCompare } from "@simulatte/webgpu";
|
|
1636
|
+
*
|
|
1637
|
+
* const result = runDawnVsDoeCompare({ configPath: "bench/config.json" });
|
|
1638
|
+
* ```
|
|
1639
|
+
*
|
|
1640
|
+
* - Requires an explicit compare config path either in options or forwarded CLI args.
|
|
1641
|
+
* - This is a tooling entrypoint, not the in-process `device` or `doe` helper path.
|
|
1642
|
+
*/
|
|
1643
|
+
export const runDawnVsDoeCompare = runDawnVsDoeCompareCli;
|
|
669
1644
|
|
|
670
1645
|
export default {
|
|
671
1646
|
create,
|