@simulatte/webgpu 0.2.4 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +263 -71
  3. package/api-contract.md +70 -139
  4. package/assets/package-layers.svg +63 -0
  5. package/examples/direct-webgpu/compute-dispatch.js +66 -0
  6. package/examples/direct-webgpu/explicit-bind-group.js +85 -0
  7. package/examples/direct-webgpu/request-device.js +10 -0
  8. package/examples/doe-api/buffers-readback.js +9 -0
  9. package/examples/doe-api/compile-and-dispatch.js +30 -0
  10. package/examples/doe-api/compute-dispatch.js +25 -0
  11. package/examples/doe-routines/compute-once-like-input.js +36 -0
  12. package/examples/doe-routines/compute-once-matmul.js +53 -0
  13. package/examples/doe-routines/compute-once-multiple-inputs.js +27 -0
  14. package/examples/doe-routines/compute-once.js +23 -0
  15. package/headless-webgpu-comparison.md +2 -2
  16. package/layering-plan.md +1 -1
  17. package/native/doe_napi.c +102 -12
  18. package/package.json +2 -1
  19. package/prebuilds/darwin-arm64/doe_napi.node +0 -0
  20. package/prebuilds/darwin-arm64/libwebgpu_doe.dylib +0 -0
  21. package/prebuilds/darwin-arm64/metadata.json +6 -6
  22. package/prebuilds/linux-x64/doe_napi.node +0 -0
  23. package/prebuilds/linux-x64/libwebgpu_doe.so +0 -0
  24. package/prebuilds/linux-x64/metadata.json +5 -5
  25. package/scripts/generate-readme-assets.js +79 -6
  26. package/scripts/prebuild.js +23 -19
  27. package/src/auto_bind_group_layout.js +32 -0
  28. package/src/bun-ffi.js +93 -12
  29. package/src/bun.js +23 -2
  30. package/src/compute.d.ts +2 -1
  31. package/src/compute.js +671 -33
  32. package/src/doe.d.ts +127 -27
  33. package/src/doe.js +480 -114
  34. package/src/full.d.ts +8 -1
  35. package/src/full.js +28 -3
  36. package/src/index.js +1013 -38
package/src/compute.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as full from './index.js';
2
- import { doe } from './doe.js';
2
+ import { createDoeNamespace } from './doe.js';
3
3
 
4
4
  function unwrap(value) {
5
5
  return value && typeof value === 'object' && '_raw' in value ? value._raw : value;
@@ -10,18 +10,90 @@ function wrap_buffer(raw) {
10
10
  _raw: raw,
11
11
  size: raw.size,
12
12
  usage: raw.usage,
13
+ /**
14
+ * Map the wrapped compute-surface buffer for host access.
15
+ *
16
+ * This forwards the mapping request to the underlying Doe buffer while
17
+ * keeping the narrower compute facade shape.
18
+ *
19
+ * This example shows the API in its basic form.
20
+ *
21
+ * ```js
22
+ * await buffer.mapAsync(GPUMapMode.READ);
23
+ * ```
24
+ *
25
+ * - This forwards directly to the underlying Doe buffer.
26
+ * - The compute facade keeps the same mapping semantics as the full surface.
27
+ */
13
28
  async mapAsync(mode, offset, size) {
14
29
  return raw.mapAsync(mode, offset, size);
15
30
  },
31
+ /**
32
+ * Return the currently mapped byte range.
33
+ *
34
+ * This exposes the mapped bytes from the wrapped buffer without changing
35
+ * the compute-only facade.
36
+ *
37
+ * This example shows the API in its basic form.
38
+ *
39
+ * ```js
40
+ * const bytes = buffer.getMappedRange();
41
+ * ```
42
+ *
43
+ * - Call this only while the buffer is mapped.
44
+ * - The returned bytes come from the wrapped full-surface buffer object.
45
+ */
16
46
  getMappedRange(offset, size) {
17
47
  return raw.getMappedRange(offset, size);
18
48
  },
49
+ /**
50
+ * Compare a mapped `f32` prefix against expected values.
51
+ *
52
+ * This is a small validation helper that mirrors the underlying Doe buffer
53
+ * behavior on the compute surface.
54
+ *
55
+ * This example shows the API in its basic form.
56
+ *
57
+ * ```js
58
+ * buffer.assertMappedPrefixF32([1, 2, 3, 4], 4);
59
+ * ```
60
+ *
61
+ * - The buffer must already be mapped.
62
+ * - This helper is most useful in tests and smoke checks.
63
+ */
19
64
  assertMappedPrefixF32(expected, count) {
20
65
  return raw.assertMappedPrefixF32(expected, count);
21
66
  },
67
+ /**
68
+ * Release the current mapping.
69
+ *
70
+ * This forwards unmapping to the wrapped buffer so the resource can return
71
+ * to normal GPU ownership.
72
+ *
73
+ * This example shows the API in its basic form.
74
+ *
75
+ * ```js
76
+ * buffer.unmap();
77
+ * ```
78
+ *
79
+ * - This forwards directly to the wrapped Doe buffer.
80
+ */
22
81
  unmap() {
23
82
  return raw.unmap();
24
83
  },
84
+ /**
85
+ * Release the wrapped native buffer.
86
+ *
87
+ * This tears down the underlying Doe buffer owned by this facade object.
88
+ *
89
+ * This example shows the API in its basic form.
90
+ *
91
+ * ```js
92
+ * buffer.destroy();
93
+ * ```
94
+ *
95
+ * - Reusing the buffer after destruction is unsupported.
96
+ */
25
97
  destroy() {
26
98
  return raw.destroy();
27
99
  },
@@ -43,6 +115,21 @@ function wrap_pipeline_layout(raw) {
43
115
  function wrap_compute_pipeline(raw) {
44
116
  return {
45
117
  _raw: raw,
118
+ /**
119
+ * Return the bind-group layout for a given group index.
120
+ *
121
+ * This forwards layout lookup to the underlying compute pipeline and wraps
122
+ * the result back into the compute facade.
123
+ *
124
+ * This example shows the API in its basic form.
125
+ *
126
+ * ```js
127
+ * const layout = pipeline.getBindGroupLayout(0);
128
+ * ```
129
+ *
130
+ * - This forwards to the underlying full-surface compute pipeline.
131
+ * - The returned layout is wrapped back into the compute facade.
132
+ */
46
133
  getBindGroupLayout(index) {
47
134
  return wrap_bind_group_layout(raw.getBindGroupLayout(index));
48
135
  },
@@ -52,24 +139,107 @@ function wrap_compute_pipeline(raw) {
52
139
  function wrap_compute_pass(raw) {
53
140
  return {
54
141
  _raw: raw,
142
+ /**
143
+ * Set the compute pipeline used by later dispatch calls.
144
+ *
145
+ * This records the pipeline state that the wrapped compute pass should use
146
+ * for subsequent dispatches.
147
+ *
148
+ * This example shows the API in its basic form.
149
+ *
150
+ * ```js
151
+ * pass.setPipeline(pipeline);
152
+ * ```
153
+ *
154
+ * - The pipeline must come from this compute facade or the same underlying device.
155
+ */
55
156
  setPipeline(pipeline) {
56
157
  return raw.setPipeline(unwrap(pipeline));
57
158
  },
58
- setBindGroup(index, bind_group) {
59
- return raw.setBindGroup(index, unwrap(bind_group));
60
- },
159
+ /**
160
+ * Bind a bind group for the compute pass.
161
+ *
162
+ * This records the resource bindings that the wrapped pass should expose to
163
+ * the shader.
164
+ *
165
+ * This example shows the API in its basic form.
166
+ *
167
+ * ```js
168
+ * pass.setBindGroup(0, bindGroup);
169
+ * ```
170
+ *
171
+ * - The bind group is unwrapped before forwarding to the underlying pass.
172
+ */
173
+ setBindGroup(index, bindGroup) {
174
+ return raw.setBindGroup(index, unwrap(bindGroup));
175
+ },
176
+ /**
177
+ * Record a direct compute dispatch.
178
+ *
179
+ * This forwards an explicit workgroup dispatch to the wrapped pass encoder.
180
+ *
181
+ * This example shows the API in its basic form.
182
+ *
183
+ * ```js
184
+ * pass.dispatchWorkgroups(4, 1, 1);
185
+ * ```
186
+ *
187
+ * - Omitted `y` and `z` default to `1`.
188
+ */
61
189
  dispatchWorkgroups(x, y = 1, z = 1) {
62
190
  return raw.dispatchWorkgroups(x, y, z);
63
191
  },
64
- dispatchWorkgroupsIndirect(indirect_buffer, indirect_offset = 0) {
65
- return raw.dispatchWorkgroupsIndirect(unwrap(indirect_buffer), indirect_offset);
66
- },
67
- writeTimestamp(query_set, query_index) {
192
+ /**
193
+ * Dispatch workgroups using counts stored in a buffer.
194
+ *
195
+ * This forwards an indirect dispatch after unwrapping the buffer passed
196
+ * through the compute facade.
197
+ *
198
+ * This example shows the API in its basic form.
199
+ *
200
+ * ```js
201
+ * pass.dispatchWorkgroupsIndirect(indirectBuffer, 0);
202
+ * ```
203
+ *
204
+ * - The indirect buffer is unwrapped before dispatch.
205
+ */
206
+ dispatchWorkgroupsIndirect(indirectBuffer, indirectOffset = 0) {
207
+ return raw.dispatchWorkgroupsIndirect(unwrap(indirectBuffer), indirectOffset);
208
+ },
209
+ /**
210
+ * Write a timestamp into a query set.
211
+ *
212
+ * This preserves the compute-facade API while forwarding timestamp writes
213
+ * only when the underlying runtime supports them.
214
+ *
215
+ * This example shows the API in its basic form.
216
+ *
217
+ * ```js
218
+ * pass.writeTimestamp(querySet, 0);
219
+ * ```
220
+ *
221
+ * - This throws when the underlying runtime does not expose timestamp query writes.
222
+ */
223
+ writeTimestamp(querySet, queryIndex) {
68
224
  if (typeof raw.writeTimestamp !== 'function') {
69
225
  throw new Error('timestamp query writes are unsupported on the compute surface');
70
226
  }
71
- return raw.writeTimestamp(unwrap(query_set), query_index);
72
- },
227
+ return raw.writeTimestamp(unwrap(querySet), queryIndex);
228
+ },
229
+ /**
230
+ * Finish the compute pass.
231
+ *
232
+ * This closes the wrapped pass so the surrounding command encoder can be
233
+ * finalized or continue recording.
234
+ *
235
+ * This example shows the API in its basic form.
236
+ *
237
+ * ```js
238
+ * pass.end();
239
+ * ```
240
+ *
241
+ * - This closes the wrapped pass but does not submit work by itself.
242
+ */
73
243
  end() {
74
244
  return raw.end();
75
245
  },
@@ -79,30 +249,87 @@ function wrap_compute_pass(raw) {
79
249
  function wrap_command_encoder(raw) {
80
250
  return {
81
251
  _raw: raw,
252
+ /**
253
+ * Begin a compute pass on the compute facade.
254
+ *
255
+ * This creates a wrapped compute pass encoder that exposes only the
256
+ * compute-surface contract.
257
+ *
258
+ * This example shows the API in its basic form.
259
+ *
260
+ * ```js
261
+ * const pass = encoder.beginComputePass();
262
+ * ```
263
+ *
264
+ * - The returned pass is wrapped back into the compute facade.
265
+ */
82
266
  beginComputePass(descriptor) {
83
267
  return wrap_compute_pass(raw.beginComputePass(descriptor));
84
268
  },
85
- copyBufferToBuffer(source, source_offset, target, target_offset, size) {
269
+ /**
270
+ * Record a buffer-to-buffer copy.
271
+ *
272
+ * This forwards the copy call after unwrapping the facade buffers to their
273
+ * underlying Doe handles.
274
+ *
275
+ * This example shows the API in its basic form.
276
+ *
277
+ * ```js
278
+ * encoder.copyBufferToBuffer(src, 0, dst, 0, src.size);
279
+ * ```
280
+ *
281
+ * - Source and destination buffers are unwrapped before forwarding.
282
+ */
283
+ copyBufferToBuffer(source, sourceOffset, target, targetOffset, size) {
86
284
  return raw.copyBufferToBuffer(
87
285
  unwrap(source),
88
- source_offset,
286
+ sourceOffset,
89
287
  unwrap(target),
90
- target_offset,
288
+ targetOffset,
91
289
  size,
92
290
  );
93
291
  },
94
- resolveQuerySet(query_set, first_query, query_count, destination, destination_offset) {
292
+ /**
293
+ * Resolve a query set into a destination buffer.
294
+ *
295
+ * This keeps the query-resolution API available on the facade only when
296
+ * the underlying runtime exposes it.
297
+ *
298
+ * This example shows the API in its basic form.
299
+ *
300
+ * ```js
301
+ * encoder.resolveQuerySet(querySet, 0, 1, dst, 0);
302
+ * ```
303
+ *
304
+ * - This throws when query resolution is not supported by the underlying runtime.
305
+ */
306
+ resolveQuerySet(querySet, firstQuery, queryCount, destination, destinationOffset) {
95
307
  if (typeof raw.resolveQuerySet !== 'function') {
96
308
  throw new Error('query resolution is unsupported on the compute surface');
97
309
  }
98
310
  return raw.resolveQuerySet(
99
- unwrap(query_set),
100
- first_query,
101
- query_count,
311
+ unwrap(querySet),
312
+ firstQuery,
313
+ queryCount,
102
314
  unwrap(destination),
103
- destination_offset,
315
+ destinationOffset,
104
316
  );
105
317
  },
318
+ /**
319
+ * Finish command recording and return a wrapped command buffer.
320
+ *
321
+ * This seals the wrapped encoder so the resulting command buffer can be
322
+ * submitted through the compute-facade queue.
323
+ *
324
+ * This example shows the API in its basic form.
325
+ *
326
+ * ```js
327
+ * const commands = encoder.finish();
328
+ * queue.submit([commands]);
329
+ * ```
330
+ *
331
+ * - The command buffer is the same underlying object used by the full surface.
332
+ */
106
333
  finish() {
107
334
  return raw.finish();
108
335
  },
@@ -112,12 +339,53 @@ function wrap_command_encoder(raw) {
112
339
  function wrap_queue(raw) {
113
340
  return {
114
341
  _raw: raw,
115
- submit(command_buffers) {
116
- return raw.submit(command_buffers.map(unwrap));
117
- },
118
- writeBuffer(buffer, buffer_offset, data, data_offset, size) {
119
- return raw.writeBuffer(unwrap(buffer), buffer_offset, data, data_offset, size);
120
- },
342
+ /**
343
+ * Submit command buffers to the queue.
344
+ *
345
+ * This unwraps the supplied command buffers and forwards them to the
346
+ * underlying Doe queue.
347
+ *
348
+ * This example shows the API in its basic form.
349
+ *
350
+ * ```js
351
+ * queue.submit([encoder.finish()]);
352
+ * ```
353
+ *
354
+ * - Command buffers are unwrapped before forwarding.
355
+ */
356
+ submit(commandBuffers) {
357
+ return raw.submit(commandBuffers.map(unwrap));
358
+ },
359
+ /**
360
+ * Write host data into a GPU buffer.
361
+ *
362
+ * This forwards queue-side uploads after unwrapping the facade buffer.
363
+ *
364
+ * This example shows the API in its basic form.
365
+ *
366
+ * ```js
367
+ * queue.writeBuffer(buffer, 0, new Float32Array([1, 2, 3, 4]));
368
+ * ```
369
+ *
370
+ * - The wrapped buffer is unwrapped before forwarding to the underlying queue.
371
+ */
372
+ writeBuffer(buffer, bufferOffset, data, dataOffset, size) {
373
+ return raw.writeBuffer(unwrap(buffer), bufferOffset, data, dataOffset, size);
374
+ },
375
+ /**
376
+ * Resolve after submitted work has drained.
377
+ *
378
+ * This keeps the queue waiting API available on the facade while treating
379
+ * missing runtime support as an immediate resolution.
380
+ *
381
+ * This example shows the API in its basic form.
382
+ *
383
+ * ```js
384
+ * await queue.onSubmittedWorkDone();
385
+ * ```
386
+ *
387
+ * - If the underlying queue does not expose this method, the facade resolves immediately.
388
+ */
121
389
  async onSubmittedWorkDone() {
122
390
  if (typeof raw.onSubmittedWorkDone === 'function') {
123
391
  return raw.onSubmittedWorkDone();
@@ -129,6 +397,20 @@ function wrap_queue(raw) {
129
397
  function wrap_query_set(raw) {
130
398
  return {
131
399
  _raw: raw,
400
+ /**
401
+ * Release the wrapped query set.
402
+ *
403
+ * This forwards destruction to the underlying query set handle returned by
404
+ * the full-surface runtime.
405
+ *
406
+ * This example shows the API in its basic form.
407
+ *
408
+ * ```js
409
+ * querySet.destroy();
410
+ * ```
411
+ *
412
+ * - Reusing the query set after destruction is unsupported.
413
+ */
132
414
  destroy() {
133
415
  return raw.destroy();
134
416
  },
@@ -141,21 +423,119 @@ function wrap_device(raw) {
141
423
  queue: wrap_queue(raw.queue),
142
424
  limits: raw.limits,
143
425
  features: raw.features,
426
+ /**
427
+ * Create a buffer on the compute-only device facade.
428
+ *
429
+ * This forwards buffer creation to Doe and wraps the result back into the
430
+ * narrower compute-only surface.
431
+ *
432
+ * This example shows the API in its basic form.
433
+ *
434
+ * ```js
435
+ * const buffer = device.createBuffer({
436
+ * size: 16,
437
+ * usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
438
+ * });
439
+ * ```
440
+ *
441
+ * - The returned buffer is wrapped back into the compute facade.
442
+ */
144
443
  createBuffer(descriptor) {
145
444
  return wrap_buffer(raw.createBuffer(descriptor));
146
445
  },
446
+ /**
447
+ * Create a shader module from WGSL source.
448
+ *
449
+ * This preserves the same shader-module behavior as the full package while
450
+ * keeping the compute-only device shape.
451
+ *
452
+ * This example shows the API in its basic form.
453
+ *
454
+ * ```js
455
+ * const shader = device.createShaderModule({ code: WGSL });
456
+ * ```
457
+ *
458
+ * - This forwards directly to the underlying Doe device.
459
+ */
147
460
  createShaderModule(descriptor) {
148
461
  return raw.createShaderModule(descriptor);
149
462
  },
463
+ /**
464
+ * Create a compute pipeline.
465
+ *
466
+ * This builds the underlying Doe pipeline and wraps it back into the
467
+ * compute facade for later dispatch use.
468
+ *
469
+ * This example shows the API in its basic form.
470
+ *
471
+ * ```js
472
+ * const pipeline = device.createComputePipeline({
473
+ * layout: "auto",
474
+ * compute: { module: shader, entryPoint: "main" },
475
+ * });
476
+ * ```
477
+ *
478
+ * - The returned pipeline is wrapped back into the compute facade.
479
+ */
150
480
  createComputePipeline(descriptor) {
151
- return wrap_compute_pipeline(raw.createComputePipeline(descriptor));
481
+ const compute = descriptor.compute ?? {};
482
+ return wrap_compute_pipeline(raw.createComputePipeline({
483
+ ...descriptor,
484
+ layout: descriptor.layout === 'auto' ? 'auto' : unwrap(descriptor.layout),
485
+ compute: {
486
+ ...compute,
487
+ module: unwrap(compute.module),
488
+ },
489
+ }));
152
490
  },
491
+ /**
492
+ * Create a compute pipeline through an async-shaped API.
493
+ *
494
+ * This preserves the async WebGPU shape while returning the wrapped
495
+ * compute-only pipeline object.
496
+ *
497
+ * This example shows the API in its basic form.
498
+ *
499
+ * ```js
500
+ * const pipeline = await device.createComputePipelineAsync(descriptor);
501
+ * ```
502
+ *
503
+ * - The returned pipeline is wrapped back into the compute facade.
504
+ */
153
505
  async createComputePipelineAsync(descriptor) {
154
506
  return wrap_compute_pipeline(await raw.createComputePipelineAsync(descriptor));
155
507
  },
508
+ /**
509
+ * Create a bind-group layout.
510
+ *
511
+ * This forwards layout creation to Doe and returns the wrapped layout used
512
+ * by the compute facade.
513
+ *
514
+ * This example shows the API in its basic form.
515
+ *
516
+ * ```js
517
+ * const layout = device.createBindGroupLayout({ entries });
518
+ * ```
519
+ *
520
+ * - The returned layout is wrapped for compute-surface use.
521
+ */
156
522
  createBindGroupLayout(descriptor) {
157
523
  return wrap_bind_group_layout(raw.createBindGroupLayout(descriptor));
158
524
  },
525
+ /**
526
+ * Create a bind group.
527
+ *
528
+ * This unwraps facade resources and creates the bind group on the
529
+ * underlying Doe device.
530
+ *
531
+ * This example shows the API in its basic form.
532
+ *
533
+ * ```js
534
+ * const bindGroup = device.createBindGroup({ layout, entries });
535
+ * ```
536
+ *
537
+ * - Wrapped layouts and buffers are unwrapped before forwarding.
538
+ */
159
539
  createBindGroup(descriptor) {
160
540
  const entries = (descriptor.entries ?? []).map((entry) => ({
161
541
  ...entry,
@@ -169,21 +549,77 @@ function wrap_device(raw) {
169
549
  entries,
170
550
  }));
171
551
  },
552
+ /**
553
+ * Create a pipeline layout.
554
+ *
555
+ * This combines wrapped bind-group layouts into a pipeline layout on the
556
+ * underlying Doe device.
557
+ *
558
+ * This example shows the API in its basic form.
559
+ *
560
+ * ```js
561
+ * const layout = device.createPipelineLayout({ bindGroupLayouts: [group0] });
562
+ * ```
563
+ *
564
+ * - Wrapped bind-group layouts are unwrapped before creation.
565
+ */
172
566
  createPipelineLayout(descriptor) {
173
567
  return wrap_pipeline_layout(raw.createPipelineLayout({
174
568
  ...descriptor,
175
569
  bindGroupLayouts: (descriptor.bindGroupLayouts ?? []).map(unwrap),
176
570
  }));
177
571
  },
572
+ /**
573
+ * Create a command encoder.
574
+ *
575
+ * This returns the compute-facade wrapper around Doe's command encoder so
576
+ * callers stay inside the narrower device contract.
577
+ *
578
+ * This example shows the API in its basic form.
579
+ *
580
+ * ```js
581
+ * const encoder = device.createCommandEncoder();
582
+ * ```
583
+ *
584
+ * - The returned encoder is wrapped back into the compute facade.
585
+ */
178
586
  createCommandEncoder(descriptor) {
179
587
  return wrap_command_encoder(raw.createCommandEncoder(descriptor));
180
588
  },
589
+ /**
590
+ * Create a query set.
591
+ *
592
+ * This forwards query-set creation when the underlying runtime supports it
593
+ * and otherwise fails explicitly.
594
+ *
595
+ * This example shows the API in its basic form.
596
+ *
597
+ * ```js
598
+ * const querySet = device.createQuerySet({ type: "timestamp", count: 2 });
599
+ * ```
600
+ *
601
+ * - This throws when query sets are unsupported by the underlying runtime.
602
+ */
181
603
  createQuerySet(descriptor) {
182
604
  if (typeof raw.createQuerySet !== 'function') {
183
605
  throw new Error('query sets are unsupported on the compute surface');
184
606
  }
185
607
  return wrap_query_set(raw.createQuerySet(descriptor));
186
608
  },
609
+ /**
610
+ * Release the wrapped device.
611
+ *
612
+ * This tears down the underlying Doe device associated with the compute
613
+ * facade wrapper.
614
+ *
615
+ * This example shows the API in its basic form.
616
+ *
617
+ * ```js
618
+ * device.destroy();
619
+ * ```
620
+ *
621
+ * - Reusing the device after destruction is unsupported.
622
+ */
187
623
  destroy() {
188
624
  return raw.destroy();
189
625
  },
@@ -195,9 +631,36 @@ function wrap_adapter(raw) {
195
631
  _raw: raw,
196
632
  features: raw.features,
197
633
  limits: raw.limits,
634
+ /**
635
+ * Request a compute-only device facade from this adapter.
636
+ *
637
+ * This asks the underlying adapter for a Doe device and then narrows it to
638
+ * the compute-only JS surface.
639
+ *
640
+ * This example shows the API in its basic form.
641
+ *
642
+ * ```js
643
+ * const device = await adapter.requestDevice();
644
+ * ```
645
+ *
646
+ * - The wrapped device intentionally omits render and surface APIs.
647
+ */
198
648
  async requestDevice(descriptor) {
199
649
  return wrap_device(await raw.requestDevice(descriptor));
200
650
  },
651
+ /**
652
+ * Release the wrapped adapter.
653
+ *
654
+ * This tears down the underlying Doe adapter associated with this facade.
655
+ *
656
+ * This example shows the API in its basic form.
657
+ *
658
+ * ```js
659
+ * adapter.destroy();
660
+ * ```
661
+ *
662
+ * - Reusing the adapter after destruction is unsupported.
663
+ */
201
664
  destroy() {
202
665
  return raw.destroy();
203
666
  },
@@ -207,19 +670,86 @@ function wrap_adapter(raw) {
207
670
  function wrap_gpu(raw) {
208
671
  return {
209
672
  _raw: raw,
673
+ /**
674
+ * Request a compute-only adapter facade.
675
+ *
676
+ * This asks the underlying GPU object for an adapter and wraps it into the
677
+ * compute-surface contract.
678
+ *
679
+ * This example shows the API in its basic form.
680
+ *
681
+ * ```js
682
+ * const adapter = await gpu.requestAdapter();
683
+ * ```
684
+ *
685
+ * - The wrapped adapter later produces compute-only devices.
686
+ */
210
687
  async requestAdapter(options) {
211
688
  return wrap_adapter(await raw.requestAdapter(options));
212
689
  },
213
690
  };
214
691
  }
215
692
 
693
+ /**
694
+ * Standard WebGPU enum objects for the compute package surface.
695
+ *
696
+ * This exposes the same package-local enum tables as the full surface so
697
+ * compute-only consumers can build usage flags without browser globals.
698
+ *
699
+ * This example shows the API in its basic form.
700
+ *
701
+ * ```js
702
+ * import { globals } from "@simulatte/webgpu/compute";
703
+ *
704
+ * const usage = globals.GPUBufferUsage.STORAGE | globals.GPUBufferUsage.COPY_DST;
705
+ * ```
706
+ *
707
+ * - The enum values are shared with the full package.
708
+ * - The difference between package surfaces is the device facade, not the constants.
709
+ */
216
710
  export const globals = full.globals;
217
711
 
218
- export function create(create_args = null) {
219
- return wrap_gpu(full.create(create_args));
712
+ /**
713
+ * Create a compute-only `GPU` facade backed by the Doe runtime.
714
+ *
715
+ * This wraps the full package GPU object and narrows the exposed adapter and
716
+ * device methods to the compute-focused contract.
717
+ *
718
+ * This example shows the API in its basic form.
719
+ *
720
+ * ```js
721
+ * import { create } from "@simulatte/webgpu/compute";
722
+ *
723
+ * const gpu = create();
724
+ * const adapter = await gpu.requestAdapter();
725
+ * ```
726
+ *
727
+ * - The underlying runtime is still Doe; this is a JS facade restriction, not a separate backend.
728
+ * - The returned device intentionally omits render, sampler, and surface methods.
729
+ */
730
+ export function create(createArgs = null) {
731
+ return wrap_gpu(full.create(createArgs));
220
732
  }
221
733
 
222
- export function setupGlobals(target = globalThis, create_args = null) {
734
+ /**
735
+ * Install compute-surface globals and `navigator.gpu` onto a target object.
736
+ *
737
+ * This adds missing enum globals and installs a compute-only GPU facade at
738
+ * `target.navigator.gpu`.
739
+ *
740
+ * This example shows the API in its basic form.
741
+ *
742
+ * ```js
743
+ * import { setupGlobals } from "@simulatte/webgpu/compute";
744
+ *
745
+ * setupGlobals(globalThis);
746
+ * const device = await navigator.gpu.requestAdapter().then((a) => a.requestDevice());
747
+ * ```
748
+ *
749
+ * - Existing globals are preserved.
750
+ * - The installed `navigator.gpu` still yields the compute-only facade, so render APIs remain intentionally absent.
751
+ */
752
+ export function setupGlobals(target = globalThis, createArgs = null) {
223
753
  for (const [name, value] of Object.entries(globals)) {
224
754
  if (target[name] === undefined) {
225
755
  Object.defineProperty(target, name, {
@@ -230,7 +760,7 @@ export function setupGlobals(target = globalThis, create_args = null) {
230
760
  });
231
761
  }
232
762
  }
233
- const gpu = create(create_args);
763
+ const gpu = create(createArgs);
234
764
  if (typeof target.navigator === 'undefined') {
235
765
  Object.defineProperty(target, 'navigator', {
236
766
  value: { gpu },
@@ -249,17 +779,127 @@ export function setupGlobals(target = globalThis, create_args = null) {
249
779
  return gpu;
250
780
  }
251
781
 
252
- export async function requestAdapter(adapter_options = undefined, create_args = null) {
253
- return create(create_args).requestAdapter(adapter_options);
782
+ /**
783
+ * Request a compute-surface adapter.
784
+ *
785
+ * This is a convenience wrapper over `create(...).requestAdapter(...)` for the
786
+ * compute package surface.
787
+ *
788
+ * This example shows the API in its basic form.
789
+ *
790
+ * ```js
791
+ * import { requestAdapter } from "@simulatte/webgpu/compute";
792
+ *
793
+ * const adapter = await requestAdapter();
794
+ * ```
795
+ *
796
+ * - Returns `null` if no adapter is available.
797
+ * - The adapter later produces a compute-only device facade.
798
+ */
799
+ export async function requestAdapter(adapterOptions = undefined, createArgs = null) {
800
+ return create(createArgs).requestAdapter(adapterOptions);
254
801
  }
255
802
 
803
+ /**
804
+ * Request a compute-only device facade from the Doe runtime.
805
+ *
806
+ * This requests an adapter, then wraps the resulting device so only the
807
+ * compute-side JS surface is exposed.
808
+ *
809
+ * This example shows the API in its basic form.
810
+ *
811
+ * ```js
812
+ * import { requestDevice } from "@simulatte/webgpu/compute";
813
+ *
814
+ * const device = await requestDevice();
815
+ * console.log(typeof device.createRenderPipeline); // "undefined"
816
+ * ```
817
+ *
818
+ * - The facade hides render, sampler, and surface methods even if the underlying runtime has them.
819
+ * - Buffer and queue operations remain available for upload, dispatch, copy, and readback workflows.
820
+ */
256
821
  export async function requestDevice(options = {}) {
257
822
  const adapter = await requestAdapter(options?.adapterOptions, options?.createArgs ?? null);
258
823
  return adapter.requestDevice(options?.deviceDescriptor);
259
824
  }
260
825
 
826
+ /**
827
+ * Shared Doe API / Doe routines namespace for the compute package surface.
828
+ *
829
+ * This exposes `await doe.requestDevice()` for the one-line Doe API entry,
830
+ * `doe.bind(device)` when you already have a device, `doe.buffers.*` and
831
+ * `doe.compute.run(...)` / `doe.compute.compile(...)` for the `Doe API`
832
+ * surface, and `doe.compute.once(...)` for `Doe routines`.
833
+ *
834
+ * The exported `doe` object here is the JS convenience surface over the Doe
835
+ * runtime, not a separate runtime.
836
+ *
837
+ * This example shows the API in its basic form.
838
+ *
839
+ * ```js
840
+ * import { doe } from "@simulatte/webgpu/compute";
841
+ *
842
+ * const gpu = await doe.requestDevice();
843
+ * const src = gpu.buffers.fromData(new Float32Array([1, 2, 3, 4]));
844
+ * const dst = gpu.buffers.like(src, { usage: "storageReadWrite" });
845
+ * ```
846
+ *
847
+ * - This Doe API and Doe routines shape is shared with the full package surface; the difference is the raw device returned underneath.
848
+ * - `gpu.compute.once(...)` is intentionally narrow and rejects raw numeric usage flags; drop to `gpu.buffers.*` if you need explicit raw control.
849
+ */
850
+ export const doe = createDoeNamespace({
851
+ requestDevice,
852
+ });
853
+
854
+ /**
855
+ * Report how the compute package surface resolved the Doe runtime.
856
+ *
857
+ * This re-exports the same provenance report as the full package.
858
+ *
859
+ * This example shows the API in its basic form.
860
+ *
861
+ * ```js
862
+ * import { providerInfo } from "@simulatte/webgpu/compute";
863
+ *
864
+ * console.log(providerInfo().loaded);
865
+ * ```
866
+ *
867
+ * - The report describes the shared package/runtime load path, not the compute facade wrapper itself.
868
+ */
261
869
  export const providerInfo = full.providerInfo;
870
+ /**
871
+ * Create a Node/Bun runtime wrapper for Doe CLI execution from the compute package.
872
+ *
873
+ * This re-exports the same runtime CLI helper as the full package for
874
+ * benchmark and command-stream execution workflows.
875
+ *
876
+ * This example shows the API in its basic form.
877
+ *
878
+ * ```js
879
+ * import { createDoeRuntime } from "@simulatte/webgpu/compute";
880
+ *
881
+ * const runtime = createDoeRuntime();
882
+ * ```
883
+ *
884
+ * - This is package/runtime orchestration, not the in-process compute facade.
885
+ */
262
886
  export const createDoeRuntime = full.createDoeRuntime;
887
+ /**
888
+ * Run the Dawn-vs-Doe compare harness from the compute package surface.
889
+ *
890
+ * This re-exports the compare wrapper used for artifact-backed benchmark runs.
891
+ *
892
+ * This example shows the API in its basic form.
893
+ *
894
+ * ```js
895
+ * import { runDawnVsDoeCompare } from "@simulatte/webgpu/compute";
896
+ *
897
+ * const result = runDawnVsDoeCompare({ configPath: "bench/config.json" });
898
+ * ```
899
+ *
900
+ * - Requires an explicit compare config path either in options or forwarded CLI args.
901
+ * - This is a tooling entrypoint, not the in-process `doe.compute.*` helper path.
902
+ */
263
903
  export const runDawnVsDoeCompare = full.runDawnVsDoeCompare;
264
904
 
265
905
  export default {
@@ -273,5 +913,3 @@ export default {
273
913
  runDawnVsDoeCompare,
274
914
  doe,
275
915
  };
276
-
277
- export { doe };