@simulatte/webgpu 0.2.3 → 0.2.4
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 +26 -4
- package/README.md +102 -256
- package/{API_CONTRACT.md → api-contract.md} +58 -8
- package/assets/fawn-icon-main-256.png +0 -0
- package/assets/package-surface-cube-snapshot.svg +7 -7
- package/{COMPAT_SCOPE.md → compat-scope.md} +1 -1
- package/{LAYERING_PLAN.md → layering-plan.md} +10 -8
- package/package.json +25 -9
- package/scripts/generate-readme-assets.js +2 -2
- package/src/bun.js +2 -2
- package/src/compute.d.ts +161 -0
- package/src/compute.js +277 -0
- package/src/doe.d.ts +84 -0
- package/src/doe.js +275 -0
- package/src/full.d.ts +112 -0
- package/src/full.js +10 -0
- 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/src/doe.js
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
const DOE_GPU_BUFFER_USAGE = {
|
|
2
|
+
MAP_READ: 0x0001,
|
|
3
|
+
COPY_SRC: 0x0004,
|
|
4
|
+
COPY_DST: 0x0008,
|
|
5
|
+
UNIFORM: 0x0040,
|
|
6
|
+
STORAGE: 0x0080,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const DOE_GPU_SHADER_STAGE = {
|
|
10
|
+
COMPUTE: 0x4,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const DOE_GPU_MAP_MODE = {
|
|
14
|
+
READ: 0x0001,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const DOE_BUFFER_META = new WeakMap();
|
|
18
|
+
|
|
19
|
+
function resolve_buffer_usage_token(token) {
|
|
20
|
+
switch (token) {
|
|
21
|
+
case 'upload':
|
|
22
|
+
return DOE_GPU_BUFFER_USAGE.COPY_DST;
|
|
23
|
+
case 'readback':
|
|
24
|
+
return DOE_GPU_BUFFER_USAGE.COPY_SRC | DOE_GPU_BUFFER_USAGE.COPY_DST | DOE_GPU_BUFFER_USAGE.MAP_READ;
|
|
25
|
+
case 'uniform':
|
|
26
|
+
return DOE_GPU_BUFFER_USAGE.UNIFORM | DOE_GPU_BUFFER_USAGE.COPY_DST;
|
|
27
|
+
case 'storage-read':
|
|
28
|
+
return DOE_GPU_BUFFER_USAGE.STORAGE | DOE_GPU_BUFFER_USAGE.COPY_DST;
|
|
29
|
+
case 'storage-readwrite':
|
|
30
|
+
return DOE_GPU_BUFFER_USAGE.STORAGE | DOE_GPU_BUFFER_USAGE.COPY_DST | DOE_GPU_BUFFER_USAGE.COPY_SRC;
|
|
31
|
+
default:
|
|
32
|
+
throw new Error(`Unknown Doe buffer usage token: ${token}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function resolve_buffer_usage(usage) {
|
|
37
|
+
if (typeof usage === 'number') return usage;
|
|
38
|
+
if (typeof usage === 'string') return resolve_buffer_usage_token(usage);
|
|
39
|
+
if (Array.isArray(usage)) {
|
|
40
|
+
return usage.reduce((mask, token) => mask | resolve_buffer_usage_token(token), 0);
|
|
41
|
+
}
|
|
42
|
+
throw new Error('Doe buffer usage must be a number, string, or string array.');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function infer_binding_access_token(token) {
|
|
46
|
+
switch (token) {
|
|
47
|
+
case 'uniform':
|
|
48
|
+
return 'uniform';
|
|
49
|
+
case 'storage-read':
|
|
50
|
+
return 'storage-read';
|
|
51
|
+
case 'storage-readwrite':
|
|
52
|
+
return 'storage-readwrite';
|
|
53
|
+
default:
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function infer_binding_access(usage) {
|
|
59
|
+
if (typeof usage === 'number' || usage == null) return null;
|
|
60
|
+
const tokens = typeof usage === 'string'
|
|
61
|
+
? [usage]
|
|
62
|
+
: Array.isArray(usage)
|
|
63
|
+
? usage
|
|
64
|
+
: null;
|
|
65
|
+
if (!tokens) {
|
|
66
|
+
throw new Error('Doe buffer usage must be a number, string, or string array.');
|
|
67
|
+
}
|
|
68
|
+
const inferred = [...new Set(tokens.map(infer_binding_access_token).filter(Boolean))];
|
|
69
|
+
if (inferred.length > 1) {
|
|
70
|
+
throw new Error(`Doe buffer usage cannot imply multiple binding access modes: ${inferred.join(', ')}`);
|
|
71
|
+
}
|
|
72
|
+
return inferred[0] ?? null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function remember_buffer_usage(buffer, usage) {
|
|
76
|
+
DOE_BUFFER_META.set(buffer, {
|
|
77
|
+
binding_access: infer_binding_access(usage),
|
|
78
|
+
});
|
|
79
|
+
return buffer;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function inferred_binding_access_for_buffer(buffer) {
|
|
83
|
+
return DOE_BUFFER_META.get(buffer)?.binding_access ?? null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function normalize_workgroups(workgroups) {
|
|
87
|
+
if (typeof workgroups === 'number') {
|
|
88
|
+
return [workgroups, 1, 1];
|
|
89
|
+
}
|
|
90
|
+
if (Array.isArray(workgroups) && workgroups.length === 3) {
|
|
91
|
+
return workgroups;
|
|
92
|
+
}
|
|
93
|
+
throw new Error('Doe workgroups must be a number or a [x, y, z] tuple.');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function normalize_data_view(data) {
|
|
97
|
+
if (ArrayBuffer.isView(data)) {
|
|
98
|
+
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
99
|
+
}
|
|
100
|
+
if (data instanceof ArrayBuffer) {
|
|
101
|
+
return new Uint8Array(data);
|
|
102
|
+
}
|
|
103
|
+
throw new Error('Doe buffer data must be an ArrayBuffer or ArrayBufferView.');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function normalize_binding(binding, index) {
|
|
107
|
+
const entry = binding && typeof binding === 'object' && 'buffer' in binding
|
|
108
|
+
? binding
|
|
109
|
+
: { buffer: binding };
|
|
110
|
+
const access = entry.access ?? inferred_binding_access_for_buffer(entry.buffer);
|
|
111
|
+
if (!access) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
'Doe binding access is required for buffers without Doe helper usage metadata. ' +
|
|
114
|
+
'Pass { buffer, access } or create the buffer through doe.createBuffer* with a bindable usage token.'
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
binding: index,
|
|
119
|
+
buffer: entry.buffer,
|
|
120
|
+
access,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function bind_group_layout_entry(binding) {
|
|
125
|
+
const buffer_type = binding.access === 'uniform'
|
|
126
|
+
? 'uniform'
|
|
127
|
+
: binding.access === 'storage-read'
|
|
128
|
+
? 'read-only-storage'
|
|
129
|
+
: 'storage';
|
|
130
|
+
return {
|
|
131
|
+
binding: binding.binding,
|
|
132
|
+
visibility: DOE_GPU_SHADER_STAGE.COMPUTE,
|
|
133
|
+
buffer: { type: buffer_type },
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function bind_group_entry(binding) {
|
|
138
|
+
return {
|
|
139
|
+
binding: binding.binding,
|
|
140
|
+
resource: { buffer: binding.buffer },
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
class DoeKernel {
|
|
145
|
+
constructor(device, pipeline, layout, entry_point) {
|
|
146
|
+
this.device = device;
|
|
147
|
+
this.pipeline = pipeline;
|
|
148
|
+
this.layout = layout;
|
|
149
|
+
this.entryPoint = entry_point;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async dispatch(options) {
|
|
153
|
+
const bindings = (options.bindings ?? []).map(normalize_binding);
|
|
154
|
+
const workgroups = normalize_workgroups(options.workgroups);
|
|
155
|
+
const bind_group = this.device.createBindGroup({
|
|
156
|
+
label: options.label ?? undefined,
|
|
157
|
+
layout: this.layout,
|
|
158
|
+
entries: bindings.map(bind_group_entry),
|
|
159
|
+
});
|
|
160
|
+
const encoder = this.device.createCommandEncoder({ label: options.label ?? undefined });
|
|
161
|
+
const pass = encoder.beginComputePass({ label: options.label ?? undefined });
|
|
162
|
+
pass.setPipeline(this.pipeline);
|
|
163
|
+
if (bindings.length > 0) {
|
|
164
|
+
pass.setBindGroup(0, bind_group);
|
|
165
|
+
}
|
|
166
|
+
pass.dispatchWorkgroups(workgroups[0], workgroups[1], workgroups[2]);
|
|
167
|
+
pass.end();
|
|
168
|
+
this.device.queue.submit([encoder.finish()]);
|
|
169
|
+
if (typeof this.device.queue.onSubmittedWorkDone === 'function') {
|
|
170
|
+
await this.device.queue.onSubmittedWorkDone();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function create_bound_doe(device) {
|
|
176
|
+
return {
|
|
177
|
+
device,
|
|
178
|
+
createBuffer(options) {
|
|
179
|
+
return doe.createBuffer(device, options);
|
|
180
|
+
},
|
|
181
|
+
createBufferFromData(data, options = {}) {
|
|
182
|
+
return doe.createBufferFromData(device, data, options);
|
|
183
|
+
},
|
|
184
|
+
readBuffer(buffer, type, options = {}) {
|
|
185
|
+
return doe.readBuffer(device, buffer, type, options);
|
|
186
|
+
},
|
|
187
|
+
runCompute(options) {
|
|
188
|
+
return doe.runCompute(device, options);
|
|
189
|
+
},
|
|
190
|
+
compileCompute(options) {
|
|
191
|
+
return doe.compileCompute(device, options);
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function compile_compute(device, options) {
|
|
197
|
+
const bindings = (options.bindings ?? []).map(normalize_binding);
|
|
198
|
+
const shader = device.createShaderModule({ code: options.code });
|
|
199
|
+
const bind_group_layout = device.createBindGroupLayout({
|
|
200
|
+
entries: bindings.map(bind_group_layout_entry),
|
|
201
|
+
});
|
|
202
|
+
const pipeline_layout = device.createPipelineLayout({
|
|
203
|
+
bindGroupLayouts: [bind_group_layout],
|
|
204
|
+
});
|
|
205
|
+
const pipeline = device.createComputePipeline({
|
|
206
|
+
layout: pipeline_layout,
|
|
207
|
+
compute: {
|
|
208
|
+
module: shader,
|
|
209
|
+
entryPoint: options.entryPoint ?? 'main',
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
return new DoeKernel(device, pipeline, bind_group_layout, options.entryPoint ?? 'main');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export const doe = {
|
|
216
|
+
bind(device) {
|
|
217
|
+
return create_bound_doe(device);
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
createBuffer(device, options) {
|
|
221
|
+
return remember_buffer_usage(device.createBuffer({
|
|
222
|
+
label: options.label ?? undefined,
|
|
223
|
+
size: options.size,
|
|
224
|
+
usage: resolve_buffer_usage(options.usage),
|
|
225
|
+
mappedAtCreation: options.mappedAtCreation ?? false,
|
|
226
|
+
}), options.usage);
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
createBufferFromData(device, data, options = {}) {
|
|
230
|
+
const view = normalize_data_view(data);
|
|
231
|
+
const usage = options.usage ?? 'storage-read';
|
|
232
|
+
const buffer = remember_buffer_usage(device.createBuffer({
|
|
233
|
+
label: options.label ?? undefined,
|
|
234
|
+
size: view.byteLength,
|
|
235
|
+
usage: resolve_buffer_usage(usage),
|
|
236
|
+
}), usage);
|
|
237
|
+
device.queue.writeBuffer(buffer, 0, view);
|
|
238
|
+
return buffer;
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
async readBuffer(device, buffer, type, options = {}) {
|
|
242
|
+
const offset = options.offset ?? 0;
|
|
243
|
+
const size = options.size ?? Math.max(0, (buffer.size ?? 0) - offset);
|
|
244
|
+
const staging = device.createBuffer({
|
|
245
|
+
label: options.label ?? undefined,
|
|
246
|
+
size,
|
|
247
|
+
usage: DOE_GPU_BUFFER_USAGE.COPY_DST | DOE_GPU_BUFFER_USAGE.MAP_READ,
|
|
248
|
+
});
|
|
249
|
+
const encoder = device.createCommandEncoder({ label: options.label ?? undefined });
|
|
250
|
+
encoder.copyBufferToBuffer(buffer, offset, staging, 0, size);
|
|
251
|
+
device.queue.submit([encoder.finish()]);
|
|
252
|
+
await staging.mapAsync(DOE_GPU_MAP_MODE.READ);
|
|
253
|
+
const copy = staging.getMappedRange().slice(0);
|
|
254
|
+
staging.unmap();
|
|
255
|
+
if (typeof staging.destroy === 'function') {
|
|
256
|
+
staging.destroy();
|
|
257
|
+
}
|
|
258
|
+
return new type(copy);
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
async runCompute(device, options) {
|
|
262
|
+
const kernel = compile_compute(device, options);
|
|
263
|
+
await kernel.dispatch({
|
|
264
|
+
bindings: options.bindings ?? [],
|
|
265
|
+
workgroups: options.workgroups,
|
|
266
|
+
label: options.label,
|
|
267
|
+
});
|
|
268
|
+
},
|
|
269
|
+
|
|
270
|
+
compileCompute(device, options) {
|
|
271
|
+
return compile_compute(device, options);
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
export default doe;
|
package/src/full.d.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BoundDoeNamespace,
|
|
3
|
+
DoeKernelDispatchOptions,
|
|
4
|
+
DoeNamespace,
|
|
5
|
+
DoeRunComputeOptions,
|
|
6
|
+
} from "./doe.js";
|
|
7
|
+
|
|
8
|
+
export interface ProviderInfo {
|
|
9
|
+
module: string;
|
|
10
|
+
loaded: boolean;
|
|
11
|
+
loadError: string;
|
|
12
|
+
defaultCreateArgs: string[];
|
|
13
|
+
doeNative: boolean;
|
|
14
|
+
libraryFlavor: string;
|
|
15
|
+
doeLibraryPath: string;
|
|
16
|
+
buildMetadataSource: string;
|
|
17
|
+
buildMetadataPath: string;
|
|
18
|
+
leanVerifiedBuild: boolean | null;
|
|
19
|
+
proofArtifactSha256: string | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface DoeRuntimeRunResult {
|
|
23
|
+
ok: boolean;
|
|
24
|
+
exitCode: number;
|
|
25
|
+
stdout: string;
|
|
26
|
+
stderr: string;
|
|
27
|
+
signal: string | null;
|
|
28
|
+
command: string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface DoeRuntimeBenchResult extends DoeRuntimeRunResult {
|
|
32
|
+
traceJsonlPath: string | null;
|
|
33
|
+
traceMetaPath: string | null;
|
|
34
|
+
traceMeta: Record<string, unknown> | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface DoeRuntimeBenchOptions {
|
|
38
|
+
commandsPath: string;
|
|
39
|
+
quirksPath?: string;
|
|
40
|
+
vendor?: string;
|
|
41
|
+
api?: string;
|
|
42
|
+
family?: string;
|
|
43
|
+
driver?: string;
|
|
44
|
+
traceJsonlPath?: string;
|
|
45
|
+
traceMetaPath?: string;
|
|
46
|
+
uploadBufferUsage?: string;
|
|
47
|
+
uploadSubmitEvery?: number;
|
|
48
|
+
queueWaitMode?: string;
|
|
49
|
+
queueSyncMode?: string;
|
|
50
|
+
extraArgs?: string[];
|
|
51
|
+
cwd?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface DoeRuntime {
|
|
55
|
+
binPath: string;
|
|
56
|
+
libPath: string | null;
|
|
57
|
+
runRaw(args: string[], spawnOptions?: Record<string, unknown>): DoeRuntimeRunResult;
|
|
58
|
+
runBench(options: DoeRuntimeBenchOptions): DoeRuntimeBenchResult;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface RequestDeviceOptions {
|
|
62
|
+
adapterOptions?: GPURequestAdapterOptions;
|
|
63
|
+
deviceDescriptor?: GPUDeviceDescriptor;
|
|
64
|
+
createArgs?: string[] | null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface FullDoeRunComputeOptions extends DoeRunComputeOptions<GPUBuffer> {}
|
|
68
|
+
|
|
69
|
+
export interface FullDoeKernelDispatchOptions extends DoeKernelDispatchOptions<GPUBuffer> {}
|
|
70
|
+
|
|
71
|
+
export interface FullDoeKernel {
|
|
72
|
+
readonly device: GPUDevice;
|
|
73
|
+
readonly entryPoint: string;
|
|
74
|
+
dispatch(options: FullDoeKernelDispatchOptions): Promise<void>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface FullBoundDoeNamespace
|
|
78
|
+
extends BoundDoeNamespace<GPUDevice, GPUBuffer, FullDoeKernel, FullDoeRunComputeOptions> {}
|
|
79
|
+
|
|
80
|
+
export interface FullDoeNamespace
|
|
81
|
+
extends DoeNamespace<GPUDevice, GPUBuffer, FullDoeKernel, FullBoundDoeNamespace, FullDoeRunComputeOptions> {}
|
|
82
|
+
|
|
83
|
+
export const globals: Record<string, unknown>;
|
|
84
|
+
export function create(createArgs?: string[] | null): GPU;
|
|
85
|
+
export function setupGlobals(target?: object, createArgs?: string[] | null): GPU;
|
|
86
|
+
export function requestAdapter(
|
|
87
|
+
adapterOptions?: GPURequestAdapterOptions,
|
|
88
|
+
createArgs?: string[] | null
|
|
89
|
+
): Promise<GPUAdapter | null>;
|
|
90
|
+
export function requestDevice(options?: RequestDeviceOptions): Promise<GPUDevice>;
|
|
91
|
+
export function providerInfo(): ProviderInfo;
|
|
92
|
+
export function createDoeRuntime(options?: {
|
|
93
|
+
binPath?: string;
|
|
94
|
+
libPath?: string;
|
|
95
|
+
}): DoeRuntime;
|
|
96
|
+
export function runDawnVsDoeCompare(options: Record<string, unknown>): DoeRuntimeRunResult;
|
|
97
|
+
|
|
98
|
+
export const doe: FullDoeNamespace;
|
|
99
|
+
|
|
100
|
+
declare const _default: {
|
|
101
|
+
create: typeof create;
|
|
102
|
+
globals: typeof globals;
|
|
103
|
+
setupGlobals: typeof setupGlobals;
|
|
104
|
+
requestAdapter: typeof requestAdapter;
|
|
105
|
+
requestDevice: typeof requestDevice;
|
|
106
|
+
providerInfo: typeof providerInfo;
|
|
107
|
+
createDoeRuntime: typeof createDoeRuntime;
|
|
108
|
+
runDawnVsDoeCompare: typeof runDawnVsDoeCompare;
|
|
109
|
+
doe: FullDoeNamespace;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export default _default;
|
package/src/full.js
ADDED
package/src/node-runtime.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export { default } from "./
|
|
1
|
+
export * from "./full.js";
|
|
2
|
+
export { default } from "./full.js";
|
package/src/node.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export { default } from "./
|
|
1
|
+
export * from "./full.js";
|
|
2
|
+
export { default } from "./full.js";
|
|
@@ -12,8 +12,8 @@ Scope:
|
|
|
12
12
|
|
|
13
13
|
This document defines three explicit layers:
|
|
14
14
|
|
|
15
|
-
1. `core`
|
|
16
|
-
- compute-first headless WebGPU
|
|
15
|
+
1. `compute` (`core` runtime boundary)
|
|
16
|
+
- compute-first headless WebGPU for AI workloads and other buffer/dispatch-heavy tasks
|
|
17
17
|
- minimal releaseable package/runtime surface
|
|
18
18
|
- explicit unsupported for sampled/render/browser gaps
|
|
19
19
|
2. `full`
|
|
@@ -30,7 +30,7 @@ depends on the full runtime artifact plus browser-specific gates; it must not
|
|
|
30
30
|
depend on npm packaging shape.
|
|
31
31
|
|
|
32
32
|
Boundary-enforcement and refactor-order details are defined in
|
|
33
|
-
`
|
|
33
|
+
[`./layering-plan.md`](./layering-plan.md).
|
|
34
34
|
|
|
35
35
|
## Dependency contract
|
|
36
36
|
|
|
@@ -62,7 +62,7 @@ Implementation intent:
|
|
|
62
62
|
|
|
63
63
|
Three surfaces are implied by this split:
|
|
64
64
|
|
|
65
|
-
1. `
|
|
65
|
+
1. `compute`
|
|
66
66
|
- headless compute-first package/runtime
|
|
67
67
|
2. `full`
|
|
68
68
|
- headless full WebGPU package/runtime
|
|
@@ -100,11 +100,11 @@ Browser-owned semantics remain outside both `core` and `full`.
|
|
|
100
100
|
| Fallback policy / denylist / kill switch | `out_of_scope` | `out_of_scope` | `required` |
|
|
101
101
|
| Chromium process behavior | `out_of_scope` | `out_of_scope` | `required` |
|
|
102
102
|
|
|
103
|
-
##
|
|
103
|
+
## Compute support contract (`core` runtime, `@simulatte/webgpu/compute` export)
|
|
104
104
|
|
|
105
105
|
### Target user
|
|
106
106
|
|
|
107
|
-
-
|
|
107
|
+
- AI workloads
|
|
108
108
|
- simulation
|
|
109
109
|
- data processing
|
|
110
110
|
- CI and benchmark orchestration
|
|
@@ -112,59 +112,44 @@ Browser-owned semantics remain outside both `core` and `full`.
|
|
|
112
112
|
|
|
113
113
|
### Promise
|
|
114
114
|
|
|
115
|
-
`
|
|
115
|
+
`compute` promises a stable compute-first headless WebGPU surface sized for AI
|
|
116
|
+
workloads and other buffer/dispatch-heavy headless execution, with explicit
|
|
116
117
|
unsupported behavior for sampled-texture, render, and browser-owned semantics.
|
|
117
118
|
|
|
118
119
|
### Included object model
|
|
119
120
|
|
|
120
|
-
`
|
|
121
|
+
`compute` includes:
|
|
121
122
|
|
|
122
123
|
- `GPU`, `GPUAdapter`, `GPUDevice`, `GPUQueue`
|
|
123
124
|
- `GPUBuffer`
|
|
124
125
|
- `GPUShaderModule` for compute WGSL
|
|
125
126
|
- `GPUBindGroupLayout`, `GPUBindGroup`, `GPUPipelineLayout`
|
|
126
127
|
- `GPUComputePipeline`
|
|
128
|
+
- `createComputePipelineAsync`
|
|
127
129
|
- `GPUCommandEncoder` for copy, upload, clear, barrier, and compute encoding
|
|
128
130
|
- `GPUComputePassEncoder`
|
|
129
131
|
- `dispatchWorkgroups`
|
|
130
132
|
- `dispatchWorkgroupsIndirect`
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
`
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
- `
|
|
139
|
-
- `COPY_DST`
|
|
140
|
-
- `TEXTURE_BINDING`
|
|
141
|
-
- `STORAGE_BINDING`
|
|
142
|
-
|
|
143
|
-
Excluded from `core`:
|
|
144
|
-
|
|
145
|
-
- `GPUSampler`
|
|
146
|
-
- sampled-texture semantics
|
|
147
|
-
- render-attachment semantics
|
|
148
|
-
- browser presentation semantics
|
|
149
|
-
|
|
150
|
-
Best rule:
|
|
151
|
-
|
|
152
|
-
- `core` gets samplerless compute textures only
|
|
153
|
-
- `full` gets sampled textures and render semantics
|
|
133
|
+
- queue `writeBuffer`
|
|
134
|
+
- buffer readback via `MAP_READ` + `copyBufferToBuffer`
|
|
135
|
+
- Node/Bun bootstrap globals required for headless execution:
|
|
136
|
+
- `navigator.gpu`
|
|
137
|
+
- `GPUBufferUsage`
|
|
138
|
+
- `GPUShaderStage`
|
|
139
|
+
- `GPUMapMode`
|
|
140
|
+
- `GPUTextureUsage`
|
|
154
141
|
|
|
155
142
|
### WGSL contract
|
|
156
143
|
|
|
157
|
-
WGSL required in `
|
|
144
|
+
WGSL required in `compute`:
|
|
158
145
|
|
|
159
146
|
- storage buffers
|
|
160
147
|
- uniform buffers
|
|
161
148
|
- workgroup buffers
|
|
162
149
|
- atomics
|
|
163
150
|
- barriers
|
|
164
|
-
- `textureLoad`
|
|
165
|
-
- `textureStore`
|
|
166
151
|
|
|
167
|
-
WGSL out of scope for `
|
|
152
|
+
WGSL out of scope for `compute`:
|
|
168
153
|
|
|
169
154
|
- sampler declarations and binding semantics
|
|
170
155
|
- `textureSample*`
|
|
@@ -174,9 +159,10 @@ WGSL out of scope for `core`:
|
|
|
174
159
|
|
|
175
160
|
### Explicit exclusions
|
|
176
161
|
|
|
177
|
-
`
|
|
162
|
+
`compute` does not own:
|
|
178
163
|
|
|
179
164
|
- `GPUSampler`
|
|
165
|
+
- sampled textures
|
|
180
166
|
- `GPURenderPipeline`
|
|
181
167
|
- `GPURenderPassEncoder`
|
|
182
168
|
- `GPURenderBundleEncoder`
|
|
@@ -191,7 +177,7 @@ WGSL out of scope for `core`:
|
|
|
191
177
|
|
|
192
178
|
### Release gates for `core`
|
|
193
179
|
|
|
194
|
-
`
|
|
180
|
+
`compute` acceptance requires:
|
|
195
181
|
|
|
196
182
|
1. schema, correctness, and trace gates green
|
|
197
183
|
2. package contract tests green for Node and declared Bun surface
|
|
@@ -199,9 +185,9 @@ WGSL out of scope for `core`:
|
|
|
199
185
|
- adapter/device acquisition
|
|
200
186
|
- buffers
|
|
201
187
|
- copy/upload
|
|
188
|
+
- readback
|
|
202
189
|
- compute pipeline
|
|
203
190
|
- compute dispatch
|
|
204
|
-
- compute-visible texture load/store paths
|
|
205
191
|
4. benchmark cube evidence limited to:
|
|
206
192
|
- upload
|
|
207
193
|
- compute e2e
|
|
@@ -209,7 +195,7 @@ WGSL out of scope for `core`:
|
|
|
209
195
|
5. explicit unsupported taxonomy for any sampler, sampled-texture, render,
|
|
210
196
|
surface, or browser API request outside the `core` contract
|
|
211
197
|
|
|
212
|
-
### Non-goals for `
|
|
198
|
+
### Non-goals for `compute`
|
|
213
199
|
|
|
214
200
|
1. full WebGPU JS object-model parity
|
|
215
201
|
2. sampled-texture semantics
|
|
@@ -229,8 +215,8 @@ WGSL out of scope for `core`:
|
|
|
229
215
|
### Promise
|
|
230
216
|
|
|
231
217
|
`full` promises a full headless WebGPU surface. It is a strict superset of
|
|
232
|
-
`
|
|
233
|
-
or Chromium wire/drop-in readiness by itself.
|
|
218
|
+
`compute`, but it still does not claim browser-process ownership, DOM
|
|
219
|
+
integration, or Chromium wire/drop-in readiness by itself.
|
|
234
220
|
|
|
235
221
|
### Added object model
|
|
236
222
|
|