@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
@@ -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 first_existing_path(paths) {
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 require_existing_path(label, path) {
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 run_process(command, args, spawn_options = {}) {
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 read_trace_meta(path) {
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 build_bench_args(options) {
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 has_option_flag(args, flag) {
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 = first_existing_path([
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 = first_existing_path([
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 first_existing_path([
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 = first_existing_path([
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 run_process(binPath, args, {
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
- require_existing_path("commandsPath", runOptions.commandsPath);
159
- if (runOptions.quirksPath) require_existing_path("quirksPath", runOptions.quirksPath);
160
- const args = build_bench_args(runOptions);
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 = read_trace_meta(runOptions.traceMetaPath);
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 && !has_option_flag(extraArgs, "--config")) {
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 run_process(pythonBin, args, {
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
+ };