@simulatte/webgpu 0.3.0 → 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.
- package/CHANGELOG.md +37 -10
- package/LICENSE +191 -0
- package/README.md +62 -48
- package/api-contract.md +67 -49
- package/architecture.md +317 -0
- package/assets/package-layers.svg +3 -3
- package/docs/doe-api-reference.html +1842 -0
- package/doe-api-design.md +237 -0
- package/examples/doe-api/README.md +19 -0
- package/examples/doe-api/buffers-readback.js +3 -2
- package/examples/{doe-routines/compute-once-like-input.js → doe-api/compute-one-shot-like-input.js} +1 -1
- package/examples/{doe-routines/compute-once-matmul.js → doe-api/compute-one-shot-matmul.js} +2 -2
- package/examples/{doe-routines/compute-once-multiple-inputs.js → doe-api/compute-one-shot-multiple-inputs.js} +1 -1
- package/examples/{doe-routines/compute-once.js → doe-api/compute-one-shot.js} +1 -1
- package/examples/doe-api/{compile-and-dispatch.js → kernel-create-and-dispatch.js} +4 -6
- package/examples/doe-api/{compute-dispatch.js → kernel-run.js} +4 -6
- package/headless-webgpu-comparison.md +3 -3
- package/jsdoc-style-guide.md +435 -0
- package/native/doe_napi.c +1481 -84
- package/package.json +18 -6
- package/prebuilds/darwin-arm64/doe_napi.node +0 -0
- package/prebuilds/darwin-arm64/libwebgpu_doe.dylib +0 -0
- package/prebuilds/darwin-arm64/metadata.json +5 -5
- package/prebuilds/linux-x64/metadata.json +1 -1
- package/scripts/generate-doe-api-docs.js +1607 -0
- package/scripts/generate-readme-assets.js +3 -3
- package/src/build_metadata.js +7 -4
- package/src/bun-ffi.js +1229 -474
- package/src/bun.js +5 -1
- package/src/compute.d.ts +16 -7
- package/src/compute.js +84 -53
- package/src/full.d.ts +16 -7
- package/src/full.js +12 -10
- package/src/index.js +679 -1324
- package/src/runtime_cli.js +17 -17
- package/src/shared/capabilities.js +144 -0
- package/src/shared/compiler-errors.js +78 -0
- package/src/shared/encoder-surface.js +295 -0
- package/src/shared/full-surface.js +514 -0
- package/src/shared/public-surface.js +82 -0
- package/src/shared/resource-lifecycle.js +120 -0
- package/src/shared/validation.js +495 -0
- package/src/webgpu_constants.js +30 -0
- package/support-contracts.md +2 -2
- package/compat-scope.md +0 -46
- package/layering-plan.md +0 -259
- package/src/auto_bind_group_layout.js +0 -32
- package/src/doe.d.ts +0 -184
- package/src/doe.js +0 -641
- package/zig-source-inventory.md +0 -468
package/src/runtime_cli.js
CHANGED
|
@@ -11,7 +11,7 @@ const LIB_EXTENSION_BY_PLATFORM = {
|
|
|
11
11
|
|
|
12
12
|
const WORKSPACE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../../..");
|
|
13
13
|
|
|
14
|
-
function
|
|
14
|
+
function firstExistingPath(paths) {
|
|
15
15
|
for (const path of paths) {
|
|
16
16
|
if (!path) continue;
|
|
17
17
|
if (existsSync(path)) return path;
|
|
@@ -19,7 +19,7 @@ function first_existing_path(paths) {
|
|
|
19
19
|
return null;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
function
|
|
22
|
+
function requireExistingPath(label, path) {
|
|
23
23
|
if (!path) {
|
|
24
24
|
throw new Error(`Missing ${label}.`);
|
|
25
25
|
}
|
|
@@ -28,7 +28,7 @@ function require_existing_path(label, path) {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
function
|
|
31
|
+
function runProcess(command, args, spawn_options = {}) {
|
|
32
32
|
const result = spawnSync(command, args, {
|
|
33
33
|
encoding: "utf8",
|
|
34
34
|
...spawn_options,
|
|
@@ -44,13 +44,13 @@ function run_process(command, args, spawn_options = {}) {
|
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
function
|
|
47
|
+
function readTraceMeta(path) {
|
|
48
48
|
if (!path || !existsSync(path)) return null;
|
|
49
49
|
const raw = readFileSync(path, "utf8");
|
|
50
50
|
return JSON.parse(raw);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
function
|
|
53
|
+
function buildBenchArgs(options) {
|
|
54
54
|
const args = ["--commands", options.commandsPath];
|
|
55
55
|
if (options.quirksPath) args.push("--quirks", options.quirksPath);
|
|
56
56
|
if (options.vendor) args.push("--vendor", options.vendor);
|
|
@@ -72,12 +72,12 @@ function build_bench_args(options) {
|
|
|
72
72
|
return args;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
function
|
|
75
|
+
function hasOptionFlag(args, flag) {
|
|
76
76
|
return Array.isArray(args) && args.includes(flag);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
export function resolveFawnRepoRoot(explicitPath) {
|
|
80
|
-
const resolved =
|
|
80
|
+
const resolved = firstExistingPath([
|
|
81
81
|
explicitPath ? resolve(explicitPath, "bench/compare_dawn_vs_doe.py") : null,
|
|
82
82
|
resolve(process.cwd(), "bench/compare_dawn_vs_doe.py"),
|
|
83
83
|
resolve(WORKSPACE_ROOT, "bench/compare_dawn_vs_doe.py"),
|
|
@@ -91,7 +91,7 @@ export function resolveFawnRepoRoot(explicitPath) {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
export function resolveDoeBinaryPath(explicitPath) {
|
|
94
|
-
const resolved =
|
|
94
|
+
const resolved = firstExistingPath([
|
|
95
95
|
explicitPath,
|
|
96
96
|
process.env.FAWN_DOE_BIN,
|
|
97
97
|
resolve(process.cwd(), "zig/zig-out/bin/doe-zig-runtime"),
|
|
@@ -107,7 +107,7 @@ export function resolveDoeBinaryPath(explicitPath) {
|
|
|
107
107
|
|
|
108
108
|
export function resolveDoeLibraryPath(explicitPath) {
|
|
109
109
|
const preferredExt = LIB_EXTENSION_BY_PLATFORM[process.platform] ?? "so";
|
|
110
|
-
return
|
|
110
|
+
return firstExistingPath([
|
|
111
111
|
explicitPath,
|
|
112
112
|
process.env.FAWN_DOE_LIB,
|
|
113
113
|
resolve(process.cwd(), `zig/zig-out/lib/libwebgpu_doe.${preferredExt}`),
|
|
@@ -122,7 +122,7 @@ export function resolveDoeLibraryPath(explicitPath) {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
export function resolveCompareScriptPath(explicitPath, repoRoot = null) {
|
|
125
|
-
const resolved =
|
|
125
|
+
const resolved = firstExistingPath([
|
|
126
126
|
explicitPath,
|
|
127
127
|
repoRoot ? resolve(repoRoot, "bench/compare_dawn_vs_doe.py") : null,
|
|
128
128
|
resolve(process.cwd(), "bench/compare_dawn_vs_doe.py"),
|
|
@@ -145,7 +145,7 @@ export function createDoeRuntime(options = {}) {
|
|
|
145
145
|
if (libPath) {
|
|
146
146
|
env.FAWN_DOE_LIB = libPath;
|
|
147
147
|
}
|
|
148
|
-
return
|
|
148
|
+
return runProcess(binPath, args, {
|
|
149
149
|
...spawnOptions,
|
|
150
150
|
env,
|
|
151
151
|
});
|
|
@@ -155,13 +155,13 @@ export function createDoeRuntime(options = {}) {
|
|
|
155
155
|
if (!runOptions || typeof runOptions !== "object") {
|
|
156
156
|
throw new Error("runBench requires an options object.");
|
|
157
157
|
}
|
|
158
|
-
|
|
159
|
-
if (runOptions.quirksPath)
|
|
160
|
-
const args =
|
|
158
|
+
requireExistingPath("commandsPath", runOptions.commandsPath);
|
|
159
|
+
if (runOptions.quirksPath) requireExistingPath("quirksPath", runOptions.quirksPath);
|
|
160
|
+
const args = buildBenchArgs(runOptions);
|
|
161
161
|
const result = runRaw(args, {
|
|
162
162
|
cwd: runOptions.cwd || WORKSPACE_ROOT,
|
|
163
163
|
});
|
|
164
|
-
const traceMeta =
|
|
164
|
+
const traceMeta = readTraceMeta(runOptions.traceMetaPath);
|
|
165
165
|
return {
|
|
166
166
|
...result,
|
|
167
167
|
traceJsonlPath: runOptions.traceJsonlPath ?? null,
|
|
@@ -193,11 +193,11 @@ export function runDawnVsDoeCompare(options = {}) {
|
|
|
193
193
|
}
|
|
194
194
|
args.push(...extraArgs);
|
|
195
195
|
|
|
196
|
-
if (!options.configPath && !
|
|
196
|
+
if (!options.configPath && !hasOptionFlag(extraArgs, "--config")) {
|
|
197
197
|
throw new Error("runDawnVsDoeCompare requires configPath or --config in extraArgs.");
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
return
|
|
200
|
+
return runProcess(pythonBin, args, {
|
|
201
201
|
cwd: repoRoot,
|
|
202
202
|
env: { ...process.env, ...(options.env ?? {}) },
|
|
203
203
|
});
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
const FEATURE_DEPTH_CLIP_CONTROL = 0x00000001;
|
|
2
|
+
const FEATURE_DEPTH32FLOAT_STENCIL8 = 0x00000002;
|
|
3
|
+
const FEATURE_TEXTURE_COMPRESSION_BC = 0x00000003;
|
|
4
|
+
const FEATURE_TEXTURE_COMPRESSION_BC_SLICED_3D = 0x00000004;
|
|
5
|
+
const FEATURE_TEXTURE_COMPRESSION_ETC2 = 0x00000005;
|
|
6
|
+
const FEATURE_TEXTURE_COMPRESSION_ASTC = 0x00000006;
|
|
7
|
+
const FEATURE_TEXTURE_COMPRESSION_ASTC_SLICED_3D = 0x00000007;
|
|
8
|
+
const FEATURE_RG11B10UFLOAT_RENDERABLE = 0x00000008;
|
|
9
|
+
const FEATURE_TIMESTAMP_QUERY = 0x00000009;
|
|
10
|
+
const FEATURE_BGRA8UNORM_STORAGE = 0x0000000A;
|
|
11
|
+
const FEATURE_SHADER_F16 = 0x0000000B;
|
|
12
|
+
const FEATURE_INDIRECT_FIRST_INSTANCE = 0x0000000C;
|
|
13
|
+
const FEATURE_FLOAT32_FILTERABLE = 0x0000000D;
|
|
14
|
+
const FEATURE_SUBGROUPS = 0x0000000E;
|
|
15
|
+
const FEATURE_SUBGROUPS_F16 = 0x0000000F;
|
|
16
|
+
const FEATURE_FLOAT32_BLENDABLE = 0x00000010;
|
|
17
|
+
const FEATURE_CLIP_DISTANCES = 0x00000011;
|
|
18
|
+
const FEATURE_DUAL_SOURCE_BLENDING = 0x00000012;
|
|
19
|
+
|
|
20
|
+
const SHADER_F16_FEATURE = FEATURE_SHADER_F16;
|
|
21
|
+
|
|
22
|
+
const KNOWN_FEATURES = Object.freeze([
|
|
23
|
+
['depth-clip-control', FEATURE_DEPTH_CLIP_CONTROL],
|
|
24
|
+
['depth32float-stencil8', FEATURE_DEPTH32FLOAT_STENCIL8],
|
|
25
|
+
['texture-compression-bc', FEATURE_TEXTURE_COMPRESSION_BC],
|
|
26
|
+
['texture-compression-bc-sliced-3d', FEATURE_TEXTURE_COMPRESSION_BC_SLICED_3D],
|
|
27
|
+
['texture-compression-etc2', FEATURE_TEXTURE_COMPRESSION_ETC2],
|
|
28
|
+
['texture-compression-astc', FEATURE_TEXTURE_COMPRESSION_ASTC],
|
|
29
|
+
['texture-compression-astc-sliced-3d', FEATURE_TEXTURE_COMPRESSION_ASTC_SLICED_3D],
|
|
30
|
+
['rg11b10ufloat-renderable', FEATURE_RG11B10UFLOAT_RENDERABLE],
|
|
31
|
+
['timestamp-query', FEATURE_TIMESTAMP_QUERY],
|
|
32
|
+
['bgra8unorm-storage', FEATURE_BGRA8UNORM_STORAGE],
|
|
33
|
+
['shader-f16', FEATURE_SHADER_F16],
|
|
34
|
+
['indirect-first-instance', FEATURE_INDIRECT_FIRST_INSTANCE],
|
|
35
|
+
['float32-filterable', FEATURE_FLOAT32_FILTERABLE],
|
|
36
|
+
['subgroups', FEATURE_SUBGROUPS],
|
|
37
|
+
['subgroups-f16', FEATURE_SUBGROUPS_F16],
|
|
38
|
+
['float32-blendable', FEATURE_FLOAT32_BLENDABLE],
|
|
39
|
+
['clip-distances', FEATURE_CLIP_DISTANCES],
|
|
40
|
+
['dual-source-blending', FEATURE_DUAL_SOURCE_BLENDING],
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
const DOE_LIMITS = Object.freeze({
|
|
44
|
+
maxTextureDimension1D: 16384,
|
|
45
|
+
maxTextureDimension2D: 16384,
|
|
46
|
+
maxTextureDimension3D: 2048,
|
|
47
|
+
maxTextureArrayLayers: 2048,
|
|
48
|
+
maxBindGroups: 4,
|
|
49
|
+
maxBindGroupsPlusVertexBuffers: 24,
|
|
50
|
+
maxBindingsPerBindGroup: 1000,
|
|
51
|
+
maxDynamicUniformBuffersPerPipelineLayout: 8,
|
|
52
|
+
maxDynamicStorageBuffersPerPipelineLayout: 4,
|
|
53
|
+
maxSampledTexturesPerShaderStage: 16,
|
|
54
|
+
maxSamplersPerShaderStage: 16,
|
|
55
|
+
maxStorageBuffersPerShaderStage: 8,
|
|
56
|
+
maxStorageTexturesPerShaderStage: 4,
|
|
57
|
+
maxUniformBuffersPerShaderStage: 12,
|
|
58
|
+
maxUniformBufferBindingSize: 65536,
|
|
59
|
+
maxStorageBufferBindingSize: 134217728,
|
|
60
|
+
minUniformBufferOffsetAlignment: 256,
|
|
61
|
+
minStorageBufferOffsetAlignment: 32,
|
|
62
|
+
maxVertexBuffers: 8,
|
|
63
|
+
maxBufferSize: 268435456,
|
|
64
|
+
maxVertexAttributes: 16,
|
|
65
|
+
maxVertexBufferArrayStride: 2048,
|
|
66
|
+
maxInterStageShaderVariables: 16,
|
|
67
|
+
maxColorAttachments: 8,
|
|
68
|
+
maxColorAttachmentBytesPerSample: 32,
|
|
69
|
+
maxComputeWorkgroupStorageSize: 32768,
|
|
70
|
+
maxComputeInvocationsPerWorkgroup: 1024,
|
|
71
|
+
maxComputeWorkgroupSizeX: 1024,
|
|
72
|
+
maxComputeWorkgroupSizeY: 1024,
|
|
73
|
+
maxComputeWorkgroupSizeZ: 64,
|
|
74
|
+
maxComputeWorkgroupsPerDimension: 65535,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const DOE_LIMIT_NAMES = Object.freeze(Object.keys(DOE_LIMITS));
|
|
78
|
+
const DOE_FEATURES = Object.freeze(new Set());
|
|
79
|
+
|
|
80
|
+
function featureSet(hasFeature) {
|
|
81
|
+
const supported = [];
|
|
82
|
+
for (const [name, value] of KNOWN_FEATURES) {
|
|
83
|
+
if (hasFeature(value)) supported.push(name);
|
|
84
|
+
}
|
|
85
|
+
return supported.length === 0 ? DOE_FEATURES : Object.freeze(new Set(supported));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function isPublishedLimitValue(value) {
|
|
89
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function publishLimits(queried) {
|
|
93
|
+
if (!queried || typeof queried !== 'object') {
|
|
94
|
+
return DOE_LIMITS;
|
|
95
|
+
}
|
|
96
|
+
let sawNativeValue = false;
|
|
97
|
+
const published = {};
|
|
98
|
+
for (const name of DOE_LIMIT_NAMES) {
|
|
99
|
+
const value = queried[name];
|
|
100
|
+
if (isPublishedLimitValue(value)) {
|
|
101
|
+
published[name] = value;
|
|
102
|
+
sawNativeValue = true;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
published[name] = DOE_LIMITS[name];
|
|
106
|
+
}
|
|
107
|
+
return sawNativeValue ? Object.freeze(published) : DOE_LIMITS;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function publishFeatures(hasFeature) {
|
|
111
|
+
if (typeof hasFeature !== 'function') {
|
|
112
|
+
return DOE_FEATURES;
|
|
113
|
+
}
|
|
114
|
+
return featureSet(hasFeature);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export {
|
|
118
|
+
FEATURE_DEPTH_CLIP_CONTROL,
|
|
119
|
+
FEATURE_DEPTH32FLOAT_STENCIL8,
|
|
120
|
+
FEATURE_TEXTURE_COMPRESSION_BC,
|
|
121
|
+
FEATURE_TEXTURE_COMPRESSION_BC_SLICED_3D,
|
|
122
|
+
FEATURE_TEXTURE_COMPRESSION_ETC2,
|
|
123
|
+
FEATURE_TEXTURE_COMPRESSION_ASTC,
|
|
124
|
+
FEATURE_TEXTURE_COMPRESSION_ASTC_SLICED_3D,
|
|
125
|
+
FEATURE_RG11B10UFLOAT_RENDERABLE,
|
|
126
|
+
FEATURE_TIMESTAMP_QUERY,
|
|
127
|
+
FEATURE_BGRA8UNORM_STORAGE,
|
|
128
|
+
FEATURE_SHADER_F16,
|
|
129
|
+
FEATURE_INDIRECT_FIRST_INSTANCE,
|
|
130
|
+
FEATURE_FLOAT32_FILTERABLE,
|
|
131
|
+
FEATURE_SUBGROUPS,
|
|
132
|
+
FEATURE_SUBGROUPS_F16,
|
|
133
|
+
FEATURE_FLOAT32_BLENDABLE,
|
|
134
|
+
FEATURE_CLIP_DISTANCES,
|
|
135
|
+
FEATURE_DUAL_SOURCE_BLENDING,
|
|
136
|
+
SHADER_F16_FEATURE,
|
|
137
|
+
KNOWN_FEATURES,
|
|
138
|
+
DOE_LIMITS,
|
|
139
|
+
DOE_LIMIT_NAMES,
|
|
140
|
+
DOE_FEATURES,
|
|
141
|
+
featureSet,
|
|
142
|
+
publishLimits,
|
|
143
|
+
publishFeatures,
|
|
144
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
function shaderCheckFailure(path, result) {
|
|
2
|
+
const stage = typeof result?.stage === 'string' && result.stage.length > 0 ? result.stage : 'unknown';
|
|
3
|
+
const kind = typeof result?.kind === 'string' && result.kind.length > 0 ? result.kind : 'ShaderCheckFailed';
|
|
4
|
+
const message = typeof result?.message === 'string' && result.message.length > 0
|
|
5
|
+
? result.message
|
|
6
|
+
: 'shader check failed without native detail';
|
|
7
|
+
const error = new Error(`${path}: ${message}`);
|
|
8
|
+
error.code = kind;
|
|
9
|
+
error.stage = stage;
|
|
10
|
+
if (typeof result?.line === 'number' && result.line > 0) {
|
|
11
|
+
error.line = result.line;
|
|
12
|
+
}
|
|
13
|
+
if (typeof result?.column === 'number' && result.column > 0) {
|
|
14
|
+
error.column = result.column;
|
|
15
|
+
}
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Attach structured compiler error fields to a native error object.
|
|
21
|
+
*
|
|
22
|
+
* Prefers structured `fields` (stage, kind, line, column) from the native ABI
|
|
23
|
+
* over regex parsing of the message string. Falls back to regex when `fields`
|
|
24
|
+
* is not provided, for compatibility with older native layers that do not
|
|
25
|
+
* export `doeNativeGetLastErrorLine` / `doeNativeGetLastErrorColumn`.
|
|
26
|
+
*
|
|
27
|
+
* @param {Error} error - The thrown error from the native call.
|
|
28
|
+
* @param {string} path - The WebGPU API path label (e.g. "GPUDevice.createShaderModule").
|
|
29
|
+
* @param {{ stage?: string, kind?: string, line?: number, column?: number } | null} [fields]
|
|
30
|
+
* Optional structured fields from the native ABI. When provided, skips regex parsing.
|
|
31
|
+
*/
|
|
32
|
+
function enrichNativeCompilerError(error, path, fields = null) {
|
|
33
|
+
if (!(error instanceof Error)) return error;
|
|
34
|
+
if (fields !== null && typeof fields === 'object') {
|
|
35
|
+
if (typeof fields.stage === 'string' && fields.stage.length > 0) {
|
|
36
|
+
error.stage = fields.stage;
|
|
37
|
+
}
|
|
38
|
+
if (typeof fields.kind === 'string' && fields.kind.length > 0) {
|
|
39
|
+
error.kind = fields.kind;
|
|
40
|
+
if (!error.code || error.code === 'DOE_ERROR') {
|
|
41
|
+
error.code = fields.kind;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (typeof fields.line === 'number' && fields.line > 0) {
|
|
45
|
+
error.line = fields.line;
|
|
46
|
+
}
|
|
47
|
+
if (typeof fields.column === 'number' && fields.column > 0) {
|
|
48
|
+
error.column = fields.column;
|
|
49
|
+
}
|
|
50
|
+
// Prefix the message with the API path but leave the body intact.
|
|
51
|
+
const bodyMatch = /^\[([A-Za-z0-9_]+)(?:\/([A-Za-z0-9_]+))?\]\s+([\s\S]+)$/.exec(error.message);
|
|
52
|
+
error.message = `${path}: ${bodyMatch ? bodyMatch[3] : error.message}`;
|
|
53
|
+
return error;
|
|
54
|
+
}
|
|
55
|
+
// Fallback: parse [stage/kind] prefix from the message string.
|
|
56
|
+
const match = /^\[([A-Za-z0-9_]+)(?:\/([A-Za-z0-9_]+))?\]\s+([\s\S]+)$/.exec(error.message);
|
|
57
|
+
if (match) {
|
|
58
|
+
error.stage = match[1];
|
|
59
|
+
if (match[2]) {
|
|
60
|
+
error.kind = match[2];
|
|
61
|
+
if (!error.code || error.code === 'DOE_ERROR') {
|
|
62
|
+
error.code = match[2];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
error.message = `${path}: ${match[3]}`;
|
|
66
|
+
}
|
|
67
|
+
return error;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function compilerErrorFromMessage(path, message, fields = null) {
|
|
71
|
+
return enrichNativeCompilerError(new Error(message), path, fields);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export {
|
|
75
|
+
shaderCheckFailure,
|
|
76
|
+
enrichNativeCompilerError,
|
|
77
|
+
compilerErrorFromMessage,
|
|
78
|
+
};
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import {
|
|
2
|
+
UINT32_MAX,
|
|
3
|
+
failValidation,
|
|
4
|
+
initResource,
|
|
5
|
+
assertObject,
|
|
6
|
+
assertIntegerInRange,
|
|
7
|
+
assertLiveResource,
|
|
8
|
+
} from './resource-lifecycle.js';
|
|
9
|
+
|
|
10
|
+
function createEncoderClasses(backend) {
|
|
11
|
+
let classes = null;
|
|
12
|
+
|
|
13
|
+
class DoeGPUComputePassEncoder {
|
|
14
|
+
constructor(state, encoder) {
|
|
15
|
+
this._encoder = encoder;
|
|
16
|
+
initResource(this, 'GPUComputePassEncoder', encoder);
|
|
17
|
+
backend.computePassInit(this, state);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
_assertOpen(path) {
|
|
21
|
+
if (typeof backend.computePassAssertOpen === 'function') {
|
|
22
|
+
backend.computePassAssertOpen(this, path);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
setPipeline(pipeline) {
|
|
27
|
+
this._assertOpen('GPUComputePassEncoder.setPipeline');
|
|
28
|
+
backend.computePassSetPipeline(
|
|
29
|
+
this,
|
|
30
|
+
assertLiveResource(pipeline, 'GPUComputePassEncoder.setPipeline', 'GPUComputePipeline'),
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
setBindGroup(index, bindGroup) {
|
|
35
|
+
this._assertOpen('GPUComputePassEncoder.setBindGroup');
|
|
36
|
+
assertIntegerInRange(index, 'GPUComputePassEncoder.setBindGroup', 'index', { min: 0, max: UINT32_MAX });
|
|
37
|
+
backend.computePassSetBindGroup(
|
|
38
|
+
this,
|
|
39
|
+
index,
|
|
40
|
+
assertLiveResource(bindGroup, 'GPUComputePassEncoder.setBindGroup', 'GPUBindGroup'),
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
dispatchWorkgroups(x, y = 1, z = 1) {
|
|
45
|
+
this._assertOpen('GPUComputePassEncoder.dispatchWorkgroups');
|
|
46
|
+
assertIntegerInRange(x, 'GPUComputePassEncoder.dispatchWorkgroups', 'x', { min: 0, max: UINT32_MAX });
|
|
47
|
+
assertIntegerInRange(y, 'GPUComputePassEncoder.dispatchWorkgroups', 'y', { min: 0, max: UINT32_MAX });
|
|
48
|
+
assertIntegerInRange(z, 'GPUComputePassEncoder.dispatchWorkgroups', 'z', { min: 0, max: UINT32_MAX });
|
|
49
|
+
backend.computePassDispatchWorkgroups(this, x, y, z);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
dispatchWorkgroupsIndirect(indirectBuffer, indirectOffset = 0) {
|
|
53
|
+
this._assertOpen('GPUComputePassEncoder.dispatchWorkgroupsIndirect');
|
|
54
|
+
assertIntegerInRange(indirectOffset, 'GPUComputePassEncoder.dispatchWorkgroupsIndirect', 'indirectOffset', { min: 0 });
|
|
55
|
+
backend.computePassDispatchWorkgroupsIndirect(
|
|
56
|
+
this,
|
|
57
|
+
assertLiveResource(indirectBuffer, 'GPUComputePassEncoder.dispatchWorkgroupsIndirect', 'GPUBuffer'),
|
|
58
|
+
indirectOffset,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
end() {
|
|
63
|
+
this._assertOpen('GPUComputePassEncoder.end');
|
|
64
|
+
backend.computePassEnd(this);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
class DoeGPURenderPassEncoder {
|
|
69
|
+
constructor(state, encoder) {
|
|
70
|
+
this._encoder = encoder;
|
|
71
|
+
initResource(this, 'GPURenderPassEncoder', encoder);
|
|
72
|
+
backend.renderPassInit(this, state);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
_assertOpen(path) {
|
|
76
|
+
if (typeof backend.renderPassAssertOpen === 'function') {
|
|
77
|
+
backend.renderPassAssertOpen(this, path);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
setPipeline(pipeline) {
|
|
82
|
+
this._assertOpen('GPURenderPassEncoder.setPipeline');
|
|
83
|
+
backend.renderPassSetPipeline(
|
|
84
|
+
this,
|
|
85
|
+
assertLiveResource(pipeline, 'GPURenderPassEncoder.setPipeline', 'GPURenderPipeline'),
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
setBindGroup(index, bindGroup) {
|
|
90
|
+
this._assertOpen('GPURenderPassEncoder.setBindGroup');
|
|
91
|
+
assertIntegerInRange(index, 'GPURenderPassEncoder.setBindGroup', 'index', { min: 0, max: UINT32_MAX });
|
|
92
|
+
backend.renderPassSetBindGroup(
|
|
93
|
+
this,
|
|
94
|
+
index,
|
|
95
|
+
assertLiveResource(bindGroup, 'GPURenderPassEncoder.setBindGroup', 'GPUBindGroup'),
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
setVertexBuffer(slot, buffer, offset = 0, size) {
|
|
100
|
+
this._assertOpen('GPURenderPassEncoder.setVertexBuffer');
|
|
101
|
+
assertIntegerInRange(slot, 'GPURenderPassEncoder.setVertexBuffer', 'slot', { min: 0, max: UINT32_MAX });
|
|
102
|
+
assertIntegerInRange(offset, 'GPURenderPassEncoder.setVertexBuffer', 'offset', { min: 0 });
|
|
103
|
+
if (size !== undefined) {
|
|
104
|
+
assertIntegerInRange(size, 'GPURenderPassEncoder.setVertexBuffer', 'size', { min: 0 });
|
|
105
|
+
}
|
|
106
|
+
backend.renderPassSetVertexBuffer(
|
|
107
|
+
this,
|
|
108
|
+
slot,
|
|
109
|
+
assertLiveResource(buffer, 'GPURenderPassEncoder.setVertexBuffer', 'GPUBuffer'),
|
|
110
|
+
offset,
|
|
111
|
+
size,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
setIndexBuffer(buffer, format, offset = 0, size) {
|
|
116
|
+
this._assertOpen('GPURenderPassEncoder.setIndexBuffer');
|
|
117
|
+
assertIntegerInRange(offset, 'GPURenderPassEncoder.setIndexBuffer', 'offset', { min: 0 });
|
|
118
|
+
if (size !== undefined) {
|
|
119
|
+
assertIntegerInRange(size, 'GPURenderPassEncoder.setIndexBuffer', 'size', { min: 0 });
|
|
120
|
+
}
|
|
121
|
+
backend.renderPassSetIndexBuffer(
|
|
122
|
+
this,
|
|
123
|
+
assertLiveResource(buffer, 'GPURenderPassEncoder.setIndexBuffer', 'GPUBuffer'),
|
|
124
|
+
format,
|
|
125
|
+
offset,
|
|
126
|
+
size,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
draw(vertexCount, instanceCount = 1, firstVertex = 0, firstInstance = 0) {
|
|
131
|
+
this._assertOpen('GPURenderPassEncoder.draw');
|
|
132
|
+
assertIntegerInRange(vertexCount, 'GPURenderPassEncoder.draw', 'vertexCount', { min: 0, max: UINT32_MAX });
|
|
133
|
+
assertIntegerInRange(instanceCount, 'GPURenderPassEncoder.draw', 'instanceCount', { min: 0, max: UINT32_MAX });
|
|
134
|
+
assertIntegerInRange(firstVertex, 'GPURenderPassEncoder.draw', 'firstVertex', { min: 0, max: UINT32_MAX });
|
|
135
|
+
assertIntegerInRange(firstInstance, 'GPURenderPassEncoder.draw', 'firstInstance', { min: 0, max: UINT32_MAX });
|
|
136
|
+
backend.renderPassDraw(this, vertexCount, instanceCount, firstVertex, firstInstance);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
drawIndexed(indexCount, instanceCount = 1, firstIndex = 0, baseVertex = 0, firstInstance = 0) {
|
|
140
|
+
this._assertOpen('GPURenderPassEncoder.drawIndexed');
|
|
141
|
+
assertIntegerInRange(indexCount, 'GPURenderPassEncoder.drawIndexed', 'indexCount', { min: 0, max: UINT32_MAX });
|
|
142
|
+
assertIntegerInRange(instanceCount, 'GPURenderPassEncoder.drawIndexed', 'instanceCount', { min: 0, max: UINT32_MAX });
|
|
143
|
+
assertIntegerInRange(firstIndex, 'GPURenderPassEncoder.drawIndexed', 'firstIndex', { min: 0, max: UINT32_MAX });
|
|
144
|
+
assertIntegerInRange(firstInstance, 'GPURenderPassEncoder.drawIndexed', 'firstInstance', { min: 0, max: UINT32_MAX });
|
|
145
|
+
backend.renderPassDrawIndexed(this, indexCount, instanceCount, firstIndex, baseVertex, firstInstance);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
drawIndirect(indirectBuffer, indirectOffset = 0) {
|
|
149
|
+
this._assertOpen('GPURenderPassEncoder.drawIndirect');
|
|
150
|
+
assertIntegerInRange(indirectOffset, 'GPURenderPassEncoder.drawIndirect', 'indirectOffset', { min: 0 });
|
|
151
|
+
backend.renderPassDrawIndirect(
|
|
152
|
+
this,
|
|
153
|
+
assertLiveResource(indirectBuffer, 'GPURenderPassEncoder.drawIndirect', 'GPUBuffer'),
|
|
154
|
+
indirectOffset,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
drawIndexedIndirect(indirectBuffer, indirectOffset = 0) {
|
|
159
|
+
this._assertOpen('GPURenderPassEncoder.drawIndexedIndirect');
|
|
160
|
+
assertIntegerInRange(indirectOffset, 'GPURenderPassEncoder.drawIndexedIndirect', 'indirectOffset', { min: 0 });
|
|
161
|
+
backend.renderPassDrawIndexedIndirect(
|
|
162
|
+
this,
|
|
163
|
+
assertLiveResource(indirectBuffer, 'GPURenderPassEncoder.drawIndexedIndirect', 'GPUBuffer'),
|
|
164
|
+
indirectOffset,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
end() {
|
|
169
|
+
this._assertOpen('GPURenderPassEncoder.end');
|
|
170
|
+
backend.renderPassEnd(this);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
class DoeGPUCommandEncoder {
|
|
175
|
+
constructor(state, device) {
|
|
176
|
+
this._device = device;
|
|
177
|
+
initResource(this, 'GPUCommandEncoder', device);
|
|
178
|
+
backend.commandEncoderInit(this, state);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
_assertOpen(path) {
|
|
182
|
+
assertLiveResource(this._device, path, 'GPUDevice');
|
|
183
|
+
if (typeof backend.commandEncoderAssertOpen === 'function') {
|
|
184
|
+
backend.commandEncoderAssertOpen(this, path);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
beginComputePass(descriptor) {
|
|
189
|
+
this._assertOpen('GPUCommandEncoder.beginComputePass');
|
|
190
|
+
return backend.commandEncoderBeginComputePass(this, descriptor, classes);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
beginRenderPass(descriptor) {
|
|
194
|
+
this._assertOpen('GPUCommandEncoder.beginRenderPass');
|
|
195
|
+
const passDescriptor = assertObject(descriptor, 'GPUCommandEncoder.beginRenderPass', 'descriptor');
|
|
196
|
+
return backend.commandEncoderBeginRenderPass(this, passDescriptor, classes);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
copyBufferToBuffer(src, srcOffset, dst, dstOffset, size) {
|
|
200
|
+
this._assertOpen('GPUCommandEncoder.copyBufferToBuffer');
|
|
201
|
+
assertIntegerInRange(srcOffset, 'GPUCommandEncoder.copyBufferToBuffer', 'srcOffset', { min: 0 });
|
|
202
|
+
assertIntegerInRange(dstOffset, 'GPUCommandEncoder.copyBufferToBuffer', 'dstOffset', { min: 0 });
|
|
203
|
+
assertIntegerInRange(size, 'GPUCommandEncoder.copyBufferToBuffer', 'size', { min: 1 });
|
|
204
|
+
backend.commandEncoderCopyBufferToBuffer(
|
|
205
|
+
this,
|
|
206
|
+
assertLiveResource(src, 'GPUCommandEncoder.copyBufferToBuffer', 'GPUBuffer'),
|
|
207
|
+
srcOffset,
|
|
208
|
+
assertLiveResource(dst, 'GPUCommandEncoder.copyBufferToBuffer', 'GPUBuffer'),
|
|
209
|
+
dstOffset,
|
|
210
|
+
size,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
copyTextureToBuffer(source, destination, copySize) {
|
|
215
|
+
this._assertOpen('GPUCommandEncoder.copyTextureToBuffer');
|
|
216
|
+
const sourceObject = assertObject(source, 'GPUCommandEncoder.copyTextureToBuffer', 'source');
|
|
217
|
+
const destinationObject = assertObject(destination, 'GPUCommandEncoder.copyTextureToBuffer', 'destination');
|
|
218
|
+
const sizeObject = assertObject(copySize, 'GPUCommandEncoder.copyTextureToBuffer', 'copySize');
|
|
219
|
+
if (sourceObject.origin !== undefined) {
|
|
220
|
+
assertObject(sourceObject.origin, 'GPUCommandEncoder.copyTextureToBuffer', 'source.origin');
|
|
221
|
+
}
|
|
222
|
+
assertIntegerInRange(destinationObject.offset ?? 0, 'GPUCommandEncoder.copyTextureToBuffer', 'destination.offset', { min: 0 });
|
|
223
|
+
assertIntegerInRange(destinationObject.bytesPerRow ?? 0, 'GPUCommandEncoder.copyTextureToBuffer', 'destination.bytesPerRow', { min: 0 });
|
|
224
|
+
assertIntegerInRange(destinationObject.rowsPerImage ?? 0, 'GPUCommandEncoder.copyTextureToBuffer', 'destination.rowsPerImage', { min: 0 });
|
|
225
|
+
assertIntegerInRange(sizeObject.width, 'GPUCommandEncoder.copyTextureToBuffer', 'copySize.width', { min: 1, max: UINT32_MAX });
|
|
226
|
+
assertIntegerInRange(sizeObject.height, 'GPUCommandEncoder.copyTextureToBuffer', 'copySize.height', { min: 1, max: UINT32_MAX });
|
|
227
|
+
if (sizeObject.depthOrArrayLayers !== undefined) {
|
|
228
|
+
assertIntegerInRange(sizeObject.depthOrArrayLayers, 'GPUCommandEncoder.copyTextureToBuffer', 'copySize.depthOrArrayLayers', { min: 1, max: UINT32_MAX });
|
|
229
|
+
}
|
|
230
|
+
backend.commandEncoderCopyTextureToBuffer(
|
|
231
|
+
this,
|
|
232
|
+
{
|
|
233
|
+
texture: assertLiveResource(sourceObject.texture, 'GPUCommandEncoder.copyTextureToBuffer', 'GPUTexture'),
|
|
234
|
+
mipLevel: sourceObject.mipLevel ?? 0,
|
|
235
|
+
origin: {
|
|
236
|
+
x: sourceObject.origin?.x ?? 0,
|
|
237
|
+
y: sourceObject.origin?.y ?? 0,
|
|
238
|
+
z: sourceObject.origin?.z ?? 0,
|
|
239
|
+
},
|
|
240
|
+
aspect: sourceObject.aspect,
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
buffer: assertLiveResource(destinationObject.buffer, 'GPUCommandEncoder.copyTextureToBuffer', 'GPUBuffer'),
|
|
244
|
+
offset: destinationObject.offset ?? 0,
|
|
245
|
+
bytesPerRow: destinationObject.bytesPerRow ?? 0,
|
|
246
|
+
rowsPerImage: destinationObject.rowsPerImage ?? 0,
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
width: sizeObject.width,
|
|
250
|
+
height: sizeObject.height,
|
|
251
|
+
depthOrArrayLayers: sizeObject.depthOrArrayLayers ?? 1,
|
|
252
|
+
},
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
writeTimestamp(querySet, queryIndex) {
|
|
257
|
+
this._assertOpen('GPUCommandEncoder.writeTimestamp');
|
|
258
|
+
const querySetNative = assertLiveResource(querySet, 'GPUCommandEncoder.writeTimestamp', 'GPUQuerySet');
|
|
259
|
+
assertIntegerInRange(queryIndex, 'GPUCommandEncoder.writeTimestamp', 'queryIndex', { min: 0, max: UINT32_MAX });
|
|
260
|
+
if (queryIndex >= querySet.count) {
|
|
261
|
+
failValidation('GPUCommandEncoder.writeTimestamp', `queryIndex ${queryIndex} exceeds querySet count ${querySet.count}`);
|
|
262
|
+
}
|
|
263
|
+
backend.commandEncoderWriteTimestamp(this, querySetNative, queryIndex);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
resolveQuerySet(querySet, firstQuery, queryCount, destination, destinationOffset) {
|
|
267
|
+
this._assertOpen('GPUCommandEncoder.resolveQuerySet');
|
|
268
|
+
const querySetNative = assertLiveResource(querySet, 'GPUCommandEncoder.resolveQuerySet', 'GPUQuerySet');
|
|
269
|
+
assertIntegerInRange(firstQuery, 'GPUCommandEncoder.resolveQuerySet', 'firstQuery', { min: 0, max: UINT32_MAX });
|
|
270
|
+
assertIntegerInRange(queryCount, 'GPUCommandEncoder.resolveQuerySet', 'queryCount', { min: 1, max: UINT32_MAX });
|
|
271
|
+
if (firstQuery + queryCount > querySet.count) {
|
|
272
|
+
failValidation('GPUCommandEncoder.resolveQuerySet', `firstQuery ${firstQuery} + queryCount ${queryCount} exceeds querySet count ${querySet.count}`);
|
|
273
|
+
}
|
|
274
|
+
const destinationNative = assertLiveResource(destination, 'GPUCommandEncoder.resolveQuerySet', 'GPUBuffer');
|
|
275
|
+
assertIntegerInRange(destinationOffset, 'GPUCommandEncoder.resolveQuerySet', 'destinationOffset', { min: 0 });
|
|
276
|
+
backend.commandEncoderResolveQuerySet(this, querySetNative, firstQuery, queryCount, destinationNative, destinationOffset);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
finish() {
|
|
280
|
+
this._assertOpen('GPUCommandEncoder.finish');
|
|
281
|
+
return backend.commandEncoderFinish(this);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
classes = {
|
|
286
|
+
DoeGPUComputePassEncoder,
|
|
287
|
+
DoeGPURenderPassEncoder,
|
|
288
|
+
DoeGPUCommandEncoder,
|
|
289
|
+
};
|
|
290
|
+
return classes;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export {
|
|
294
|
+
createEncoderClasses,
|
|
295
|
+
};
|