@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/layering-plan.md DELETED
@@ -1,259 +0,0 @@
1
- # Proposed layering plan for core and full
2
-
3
- Plan status: `draft`
4
-
5
- Scope:
6
-
7
- - future Doe runtime/package sharding for headless WebGPU
8
- - architecture and refactor sequencing only
9
- - no current runtime behavior changes
10
-
11
- This plan exists to keep the future `core` and `full` split honest before any
12
- Zig source moves begin.
13
-
14
- It answers four questions:
15
-
16
- 1. what boundary is being enforced
17
- 2. how that boundary is enforced in code review and CI
18
- 3. how capability coverage and gates split once the boundary exists
19
- 4. what order the refactor should happen in
20
-
21
- Use this together with:
22
-
23
- - `support-contracts.md` for product/support scope
24
- - `api-contract.md` for the current package contract (`full` default, `compute` subpath)
25
- - `compat-scope.md` for current package non-goals
26
- - `zig-source-inventory.md` for the current `zig/src` file map
27
-
28
- ## Current state
29
-
30
- The repo is now partially physically split into `core` and `full`, but the public façade boundary is still being reduced.
31
-
32
- Current reality:
33
-
34
- 1. `zig/src/core/` and `zig/src/full/` are now real source subtrees with canonical implementations for the first runtime split slices.
35
- 2. The old root ABI shims (`zig/src/wgpu_types.zig`, `zig/src/wgpu_loader.zig`) have now been retired; the remaining root compatibility façades are narrower command/resource surfaces kept only while callers finish retargeting.
36
- 3. Canonical command partition and command dispatch now live in `zig/src/core/{command_partition.zig,command_dispatch.zig}` and `zig/src/full/{command_partition.zig,command_dispatch.zig}`.
37
- 4. Canonical texture command handling now lives in `zig/src/core/resource/wgpu_texture_commands.zig`; canonical sampler and surface command handling now lives in `zig/src/full/render/wgpu_sampler_commands.zig` and `zig/src/full/surface/wgpu_surface_commands.zig`.
38
- 5. `zig/src/wgpu_commands.zig`, `zig/src/wgpu_resources.zig`, and `zig/src/wgpu_extended_commands.zig` are now compatibility façades over the canonical subtrees, while `zig/src/webgpu_ffi.zig` remains the public façade and owner of `WebGPUBackend`.
39
- 6. Dedicated Zig test lanes now exist as `zig build test-core` and `zig build test-full`, but split coverage remains thin and capability tracking is still represented by one shared coverage ledger.
40
- 7. The JS package now exposes a default `full` surface plus an explicit `compute` subpath, while the underlying JS implementation is still shared and presented through `Direct WebGPU`, `Doe API`, and `Doe routines` styles.
41
-
42
- That means this plan is now materially physicalized in the tree, and the remaining semantic split is concentrated in the public façade files and backend roots.
43
-
44
- ## Boundary definition
45
-
46
- The target architecture is:
47
-
48
- ```text
49
- Doe core
50
- ^
51
- |
52
- Doe full
53
- ^
54
- |
55
- Chromium Track A runtime artifact lane
56
- ```
57
-
58
- Rules:
59
-
60
- 1. `full` composes `core`; it does not toggle `core`.
61
- 2. `core` must never import `full`.
62
- 3. `full` may depend on `core` Zig modules, Lean modules, build outputs, and JS
63
- helpers.
64
- 4. Chromium Track A depends on the full runtime artifact and browser-specific
65
- gates, not on npm package layout.
66
-
67
- The anti-bleed rule is the core of the design:
68
-
69
- - no `if full_enabled` branches inside `core`
70
- - no `full` fields added to `core` structs
71
- - no browser-policy logic added to `full`
72
-
73
- ## Import fence rule
74
-
75
- This is the primary long-term enforcement rule.
76
-
77
- ### Contract
78
-
79
- 1. `zig/src/core/**` may not import any file under `zig/src/full/**`
80
- 2. `lean/Fawn/Core/**` may not import any file under `lean/Fawn/Full/**`
81
- 3. package-level `core` entrypoints may not import `full` entrypoints
82
- 4. any exception requires redesign, not a one-off waiver
83
-
84
- ### CI enforcement
85
-
86
- The dedicated import-fence check should fail if:
87
-
88
- 1. a Zig file under `core` references `full/`
89
- 2. a Lean file under `Core` references `Full`
90
- 3. a package `core` entrypoint reaches into a `full`-only module
91
-
92
- The check should be a simple, explicit path-dependency audit. This is not a
93
- lint preference; it is a release-blocking architectural boundary.
94
-
95
- ## Struct wrapping rule
96
-
97
- `full` must extend `core` by composition, never by mutating `core` types in
98
- place.
99
-
100
- ### Contract
101
-
102
- 1. if `full` needs shared state, it holds a `core` value or handle
103
- 2. if `full` needs extra state, that state lives in a `full` wrapper type
104
- 3. `core` structs may not gain render/surface/full-only fields just because
105
- `full` needs them
106
- 4. `core` APIs may expose stable extension points, but not latent `full`
107
- payload slots
108
-
109
- ### Example direction
110
-
111
- Good shape:
112
-
113
- ```text
114
- full.RenderPipeline
115
- - core_pipeline_layout: core.PipelineLayout
116
- - full_render_state: ...
117
- ```
118
-
119
- Bad shape:
120
-
121
- ```text
122
- core.PipelineLayout
123
- - maybe_render_state_if_full_enabled: ...
124
- ```
125
-
126
- The intent is to keep `core` independently understandable, buildable, and
127
- benchmarked.
128
-
129
- ## Coverage split rule
130
-
131
- The current shared capability ledger is not enough once `core` and `full`
132
- become separate release surfaces.
133
-
134
- ### Target split
135
-
136
- 1. `config/webgpu-core-coverage.json`
137
- - only `core` contractual capabilities
138
- 2. `config/webgpu-full-coverage.json`
139
- - `core` plus `full` contractual capabilities
140
- 3. Chromium Track A keeps its own browser/drop-in evidence and must not be
141
- represented as mere package coverage
142
-
143
- ### Gate split
144
-
145
- `core` gates should validate:
146
-
147
- 1. core package contract
148
- 2. core CTS subset
149
- 3. core package-surface benchmark cells
150
- 4. explicit unsupported taxonomy outside core scope
151
-
152
- `full` gates should validate:
153
-
154
- 1. all core gates
155
- 2. full package contract
156
- 3. expanded CTS subset for render/lifecycle/query coverage
157
- 4. full package-surface benchmark cells
158
- 5. explicit unsupported taxonomy outside full scope
159
-
160
- Track A gates remain separate:
161
-
162
- 1. drop-in symbol completeness
163
- 2. drop-in behavior suite
164
- 3. browser replay and trace parity
165
- 4. browser performance claimability
166
-
167
- ## Proposed source layout
168
-
169
- Target layout:
170
-
171
- ```text
172
- zig/src/core/
173
- mod.zig
174
- trace/
175
- replay/
176
- abi/
177
- resource/
178
- queue/
179
- compute/
180
- backend/common/
181
- backend/{metal,vulkan,d3d12}/core/
182
-
183
- zig/src/full/
184
- mod.zig
185
- render/
186
- surface/
187
- lifecycle/
188
- backend/{metal,vulkan,d3d12}/full/
189
- ```
190
-
191
- Matching Lean layout:
192
-
193
- ```text
194
- lean/Fawn/Core/
195
- lean/Fawn/Full/
196
- ```
197
-
198
- Matching package layout is currently:
199
-
200
- 1. one package with scoped exports
201
- - `@simulatte/webgpu` => `full`
202
- - `@simulatte/webgpu/compute` => compute-first subset
203
-
204
- Separate packages remain optional later, but they are not the current shape.
205
- The source boundary still comes first.
206
-
207
- ## Refactor order
208
-
209
- Do not start by renaming packages.
210
-
211
- Recommended order:
212
-
213
- 1. freeze support contracts
214
- - define what `core` and `full` promise
215
- 2. add import-fence CI checks
216
- - enforce the one-way dependency before extraction starts
217
- 3. add split coverage ledgers and split gate entrypoints
218
- - even if both initially point at the current shared runtime
219
- 4. identify shared runtime modules that are genuinely `core`
220
- - trace, replay, buffers, queue, compute, shared resource model
221
- 5. identify `full`-only modules
222
- - render, surface, broader lifecycle/parity layers
223
- 6. extract `full` wrappers around `core` types
224
- - composition only
225
- 7. move render/surface code out of the shared tree
226
- - no behavior change intended during extraction
227
- 8. split package/API contracts
228
- - only after the runtime boundary is real
229
- 9. retarget Chromium Track A to the full runtime artifact contract
230
- - no npm package dependency in architecture docs
231
-
232
- ## Review checklist for future changes
233
-
234
- Any future patch touching this split should answer:
235
-
236
- 1. does `core` now depend on `full` anywhere
237
- 2. did a `core` struct gain a `full`-only field
238
- 3. did a coverage or gate responsibility move without contract updates
239
- 4. did a browser-owned behavior get assigned to `core` or `full`
240
- 5. did packaging get ahead of the runtime boundary
241
-
242
- If any answer is yes, the patch should be treated as architecture drift until
243
- the contract is updated or the design is corrected.
244
-
245
- ## Immediate next artifacts
246
-
247
- The earliest structural groundwork now exists: the inventory and import-fence check are in place, the first canonical `core/` and `full/` subtrees exist, and the legacy root command/resource files are compatibility façades.
248
-
249
- The next technical artifacts should focus on the remaining semantic boundary:
250
-
251
- 1. split coverage contracts for `core` and `full`
252
- 2. classify tests and Lean proofs against the new `core`/`full` boundary
253
- 3. shrink the remaining public façade files:
254
- - `model.zig`
255
- - `webgpu_ffi.zig`
256
- - backend root modules
257
- 4. retire root compatibility façades once callers stop importing them
258
-
259
- The extraction work should now spend its effort on the remaining public boundary, not on re-moving files that already have canonical homes.
@@ -1,32 +0,0 @@
1
- export function inferAutoBindGroupLayouts(code, visibility) {
2
- const groups = new Map();
3
- const bindingPattern = /@group\((\d+)\)\s*@binding\((\d+)\)\s*var(?:<([^>]+)>)?\s+\w+\s*:\s*([^;]+);/g;
4
-
5
- for (const match of code.matchAll(bindingPattern)) {
6
- const group = Number(match[1]);
7
- const binding = Number(match[2]);
8
- const addressSpace = (match[3] ?? '').trim();
9
- const typeExpr = (match[4] ?? '').trim();
10
- let entry = null;
11
-
12
- if (addressSpace.startsWith('uniform')) {
13
- entry = { binding, visibility, buffer: { type: 'uniform' } };
14
- } else if (addressSpace.startsWith('storage')) {
15
- const readOnly = !addressSpace.includes('read_write');
16
- entry = { binding, visibility, buffer: { type: readOnly ? 'read-only-storage' : 'storage' } };
17
- } else if (typeExpr.startsWith('sampler')) {
18
- entry = { binding, visibility, sampler: {} };
19
- }
20
-
21
- if (!entry) continue;
22
- const entries = groups.get(group) ?? [];
23
- entries.push(entry);
24
- groups.set(group, entries);
25
- }
26
-
27
- for (const entries of groups.values()) {
28
- entries.sort((left, right) => left.binding - right.binding);
29
- }
30
-
31
- return groups;
32
- }
package/src/doe.d.ts DELETED
@@ -1,184 +0,0 @@
1
- export type DoeBufferUsage =
2
- | "upload"
3
- | "readback"
4
- | "uniform"
5
- | "storageRead"
6
- | "storageReadWrite";
7
-
8
- export type DoeWorkgroups = number | [number, number] | [number, number, number];
9
-
10
- export type DoeBindingAccess = "uniform" | "storageRead" | "storageReadWrite";
11
-
12
- export interface DoeCreateBufferOptions {
13
- size: number;
14
- usage: DoeBufferUsage | DoeBufferUsage[] | number;
15
- label?: string;
16
- mappedAtCreation?: boolean;
17
- }
18
-
19
- export interface DoeCreateBufferFromDataOptions {
20
- usage?: DoeBufferUsage | DoeBufferUsage[] | number;
21
- label?: string;
22
- }
23
-
24
- export interface DoeReadBufferOptions {
25
- size?: number;
26
- offset?: number;
27
- label?: string;
28
- }
29
-
30
- export interface DoeCreateBufferLikeOptions extends Omit<
31
- DoeCreateBufferOptions,
32
- "size"
33
- > {
34
- size?: number;
35
- }
36
-
37
- export interface DoeBindingBuffer<TBuffer> {
38
- buffer: TBuffer;
39
- access?: DoeBindingAccess;
40
- }
41
-
42
- export interface DoeComputeInputDataOptions {
43
- data: ArrayBufferView | ArrayBuffer;
44
- usage?: DoeBufferUsage | DoeBufferUsage[];
45
- access?: DoeBindingAccess;
46
- label?: string;
47
- }
48
-
49
- export type DoeComputeInput<TBuffer> =
50
- | ArrayBufferView
51
- | ArrayBuffer
52
- | TBuffer
53
- | DoeBindingBuffer<TBuffer>
54
- | DoeComputeInputDataOptions;
55
-
56
- export interface DoeComputeOnceOutputOptions<T extends ArrayBufferView> {
57
- type: { new (buffer: ArrayBuffer): T };
58
- size?: number;
59
- likeInput?: number;
60
- usage?: DoeBufferUsage | DoeBufferUsage[];
61
- access?: DoeBindingAccess;
62
- label?: string;
63
- read?: DoeReadBufferOptions;
64
- }
65
-
66
- export interface DoeComputeOnceOptions<TBuffer, T extends ArrayBufferView> {
67
- code: string;
68
- entryPoint?: string;
69
- inputs?: Array<DoeComputeInput<TBuffer>>;
70
- output: DoeComputeOnceOutputOptions<T>;
71
- workgroups: DoeWorkgroups;
72
- label?: string;
73
- }
74
-
75
- export interface DoeRunComputeOptions<TBuffer> {
76
- code: string;
77
- entryPoint?: string;
78
- bindings?: Array<TBuffer | DoeBindingBuffer<TBuffer>>;
79
- workgroups: DoeWorkgroups;
80
- label?: string;
81
- }
82
-
83
- export interface DoeKernelDispatchOptions<TBuffer> {
84
- bindings?: Array<TBuffer | DoeBindingBuffer<TBuffer>>;
85
- workgroups: DoeWorkgroups;
86
- label?: string;
87
- }
88
-
89
- export interface BoundDoeBuffersNamespace<TBuffer> {
90
- create(options: DoeCreateBufferOptions): TBuffer;
91
- fromData<T extends ArrayBufferView>(
92
- data: T | ArrayBuffer,
93
- options?: DoeCreateBufferFromDataOptions,
94
- ): TBuffer;
95
- like(
96
- source: TBuffer | ArrayBufferView | ArrayBuffer,
97
- options?: DoeCreateBufferLikeOptions,
98
- ): TBuffer;
99
- read<T extends ArrayBufferView>(
100
- buffer: TBuffer,
101
- type: { new (buffer: ArrayBuffer): T },
102
- options?: DoeReadBufferOptions,
103
- ): Promise<T>;
104
- }
105
-
106
- export interface DoeBuffersNamespace<TDevice, TBuffer> {
107
- create(device: TDevice, options: DoeCreateBufferOptions): TBuffer;
108
- fromData<T extends ArrayBufferView>(
109
- device: TDevice,
110
- data: T | ArrayBuffer,
111
- options?: DoeCreateBufferFromDataOptions,
112
- ): TBuffer;
113
- like(
114
- device: TDevice,
115
- source: TBuffer | ArrayBufferView | ArrayBuffer,
116
- options?: DoeCreateBufferLikeOptions,
117
- ): TBuffer;
118
- read<T extends ArrayBufferView>(
119
- device: TDevice,
120
- buffer: TBuffer,
121
- type: { new (buffer: ArrayBuffer): T },
122
- options?: DoeReadBufferOptions,
123
- ): Promise<T>;
124
- }
125
-
126
- export interface BoundDoeComputeNamespace<
127
- TBuffer,
128
- TKernel,
129
- TRunComputeOptions,
130
- > {
131
- run(options: TRunComputeOptions): Promise<void>;
132
- compile(options: TRunComputeOptions): TKernel;
133
- once<T extends ArrayBufferView>(
134
- options: DoeComputeOnceOptions<TBuffer, T>,
135
- ): Promise<T>;
136
- }
137
-
138
- export interface DoeComputeNamespace<
139
- TDevice,
140
- TBuffer,
141
- TKernel,
142
- TRunComputeOptions,
143
- > {
144
- run(device: TDevice, options: TRunComputeOptions): Promise<void>;
145
- compile(device: TDevice, options: TRunComputeOptions): TKernel;
146
- once<T extends ArrayBufferView>(
147
- device: TDevice,
148
- options: DoeComputeOnceOptions<TBuffer, T>,
149
- ): Promise<T>;
150
- }
151
-
152
- export interface BoundDoeNamespace<
153
- TDevice,
154
- TBuffer,
155
- TKernel,
156
- TRunComputeOptions,
157
- > {
158
- readonly device: TDevice;
159
- readonly buffers: BoundDoeBuffersNamespace<TBuffer>;
160
- readonly compute: BoundDoeComputeNamespace<
161
- TBuffer,
162
- TKernel,
163
- TRunComputeOptions
164
- >;
165
- }
166
-
167
- export interface DoeNamespace<
168
- TDevice,
169
- TBuffer,
170
- TKernel,
171
- TBoundDoe,
172
- TRunComputeOptions,
173
- TRequestDeviceOptions = unknown,
174
- > {
175
- requestDevice(options?: TRequestDeviceOptions): Promise<TBoundDoe>;
176
- bind(device: TDevice): TBoundDoe;
177
- readonly buffers: DoeBuffersNamespace<TDevice, TBuffer>;
178
- readonly compute: DoeComputeNamespace<
179
- TDevice,
180
- TBuffer,
181
- TKernel,
182
- TRunComputeOptions
183
- >;
184
- }