@simulatte/webgpu 0.2.0

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.
@@ -0,0 +1,202 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { dirname, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ const LIB_EXTENSION_BY_PLATFORM = {
7
+ darwin: "dylib",
8
+ linux: "so",
9
+ win32: "dll",
10
+ };
11
+
12
+ const WORKSPACE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../../..");
13
+
14
+ function first_existing_path(paths) {
15
+ for (const path of paths) {
16
+ if (!path) continue;
17
+ if (existsSync(path)) return path;
18
+ }
19
+ return null;
20
+ }
21
+
22
+ function require_existing_path(label, path) {
23
+ if (!path) {
24
+ throw new Error(`Missing ${label}.`);
25
+ }
26
+ if (!existsSync(path)) {
27
+ throw new Error(`${label} does not exist: ${path}`);
28
+ }
29
+ }
30
+
31
+ function run_process(command, args, spawn_options = {}) {
32
+ const result = spawnSync(command, args, {
33
+ encoding: "utf8",
34
+ ...spawn_options,
35
+ });
36
+ if (result.error) throw result.error;
37
+ return {
38
+ ok: result.status === 0,
39
+ exitCode: result.status ?? 1,
40
+ stdout: result.stdout ?? "",
41
+ stderr: result.stderr ?? "",
42
+ signal: result.signal ?? null,
43
+ command: [command, ...args],
44
+ };
45
+ }
46
+
47
+ function read_trace_meta(path) {
48
+ if (!path || !existsSync(path)) return null;
49
+ const raw = readFileSync(path, "utf8");
50
+ return JSON.parse(raw);
51
+ }
52
+
53
+ function build_bench_args(options) {
54
+ const args = ["--commands", options.commandsPath];
55
+ if (options.quirksPath) args.push("--quirks", options.quirksPath);
56
+ if (options.vendor) args.push("--vendor", options.vendor);
57
+ if (options.api) args.push("--api", options.api);
58
+ if (options.family) args.push("--family", options.family);
59
+ if (options.driver) args.push("--driver", options.driver);
60
+ if (options.queueWaitMode) args.push("--queue-wait-mode", options.queueWaitMode);
61
+ if (options.queueSyncMode) args.push("--queue-sync-mode", options.queueSyncMode);
62
+ if (options.uploadBufferUsage) args.push("--upload-buffer-usage", options.uploadBufferUsage);
63
+ if (Number.isInteger(options.uploadSubmitEvery) && options.uploadSubmitEvery > 0) {
64
+ args.push("--upload-submit-every", String(options.uploadSubmitEvery));
65
+ }
66
+ args.push("--backend", "native", "--execute");
67
+ if (options.traceJsonlPath) args.push("--trace-jsonl", options.traceJsonlPath);
68
+ if (options.traceMetaPath) args.push("--trace-meta", options.traceMetaPath);
69
+ if (Array.isArray(options.extraArgs) && options.extraArgs.length > 0) {
70
+ args.push(...options.extraArgs);
71
+ }
72
+ return args;
73
+ }
74
+
75
+ function has_option_flag(args, flag) {
76
+ return Array.isArray(args) && args.includes(flag);
77
+ }
78
+
79
+ export function resolveFawnRepoRoot(explicitPath) {
80
+ const resolved = first_existing_path([
81
+ explicitPath ? resolve(explicitPath, "bench/compare_dawn_vs_doe.py") : null,
82
+ resolve(process.cwd(), "bench/compare_dawn_vs_doe.py"),
83
+ resolve(WORKSPACE_ROOT, "bench/compare_dawn_vs_doe.py"),
84
+ ]);
85
+ if (!resolved) {
86
+ throw new Error(
87
+ "Could not locate Fawn repo root. Pass { repoRoot } or run from a Fawn checkout."
88
+ );
89
+ }
90
+ return resolve(resolved, "..", "..");
91
+ }
92
+
93
+ export function resolveDoeBinaryPath(explicitPath) {
94
+ const resolved = first_existing_path([
95
+ explicitPath,
96
+ process.env.FAWN_DOE_BIN,
97
+ resolve(process.cwd(), "zig/zig-out/bin/doe-zig-runtime"),
98
+ resolve(WORKSPACE_ROOT, "zig/zig-out/bin/doe-zig-runtime"),
99
+ ]);
100
+ if (!resolved) {
101
+ throw new Error(
102
+ "Could not locate doe-zig-runtime. Set FAWN_DOE_BIN or pass { binPath }."
103
+ );
104
+ }
105
+ return resolved;
106
+ }
107
+
108
+ export function resolveDoeLibraryPath(explicitPath) {
109
+ const preferredExt = LIB_EXTENSION_BY_PLATFORM[process.platform] ?? "so";
110
+ return first_existing_path([
111
+ explicitPath,
112
+ process.env.FAWN_DOE_LIB,
113
+ resolve(process.cwd(), `zig/zig-out/lib/libwebgpu_doe.${preferredExt}`),
114
+ resolve(WORKSPACE_ROOT, `zig/zig-out/lib/libwebgpu_doe.${preferredExt}`),
115
+ resolve(process.cwd(), "zig/zig-out/lib/libwebgpu_doe.dylib"),
116
+ resolve(process.cwd(), "zig/zig-out/lib/libwebgpu_doe.so"),
117
+ resolve(process.cwd(), "zig/zig-out/lib/libwebgpu_doe.dll"),
118
+ resolve(WORKSPACE_ROOT, "zig/zig-out/lib/libwebgpu_doe.dylib"),
119
+ resolve(WORKSPACE_ROOT, "zig/zig-out/lib/libwebgpu_doe.so"),
120
+ resolve(WORKSPACE_ROOT, "zig/zig-out/lib/libwebgpu_doe.dll"),
121
+ ]);
122
+ }
123
+
124
+ export function resolveCompareScriptPath(explicitPath, repoRoot = null) {
125
+ const resolved = first_existing_path([
126
+ explicitPath,
127
+ repoRoot ? resolve(repoRoot, "bench/compare_dawn_vs_doe.py") : null,
128
+ resolve(process.cwd(), "bench/compare_dawn_vs_doe.py"),
129
+ resolve(WORKSPACE_ROOT, "bench/compare_dawn_vs_doe.py"),
130
+ ]);
131
+ if (!resolved) {
132
+ throw new Error(
133
+ "Could not locate bench/compare_dawn_vs_doe.py. Pass { compareScriptPath }."
134
+ );
135
+ }
136
+ return resolved;
137
+ }
138
+
139
+ export function createDoeRuntime(options = {}) {
140
+ const binPath = resolveDoeBinaryPath(options.binPath);
141
+ const libPath = resolveDoeLibraryPath(options.libPath);
142
+
143
+ function runRaw(args, spawnOptions = {}) {
144
+ const env = { ...process.env, ...(spawnOptions.env ?? {}) };
145
+ if (libPath) {
146
+ env.FAWN_DOE_LIB = libPath;
147
+ }
148
+ return run_process(binPath, args, {
149
+ ...spawnOptions,
150
+ env,
151
+ });
152
+ }
153
+
154
+ function runBench(runOptions) {
155
+ if (!runOptions || typeof runOptions !== "object") {
156
+ throw new Error("runBench requires an options object.");
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);
161
+ const result = runRaw(args);
162
+ const traceMeta = read_trace_meta(runOptions.traceMetaPath);
163
+ return {
164
+ ...result,
165
+ traceJsonlPath: runOptions.traceJsonlPath ?? null,
166
+ traceMetaPath: runOptions.traceMetaPath ?? null,
167
+ traceMeta,
168
+ };
169
+ }
170
+
171
+ return {
172
+ binPath,
173
+ libPath,
174
+ runRaw,
175
+ runBench,
176
+ };
177
+ }
178
+
179
+ export function runDawnVsDoeCompare(options = {}) {
180
+ const repoRoot = resolveFawnRepoRoot(options.repoRoot);
181
+ const scriptPath = resolveCompareScriptPath(options.compareScriptPath, repoRoot);
182
+ const pythonBin = options.pythonBin || process.env.PYTHON_BIN || "python3";
183
+ const extraArgs = Array.isArray(options.extraArgs) ? options.extraArgs : [];
184
+
185
+ const args = [scriptPath];
186
+ if (options.configPath) {
187
+ args.push("--config", resolve(options.configPath));
188
+ }
189
+ if (options.outPath) {
190
+ args.push("--out", resolve(options.outPath));
191
+ }
192
+ args.push(...extraArgs);
193
+
194
+ if (!options.configPath && !has_option_flag(extraArgs, "--config")) {
195
+ throw new Error("runDawnVsDoeCompare requires configPath or --config in extraArgs.");
196
+ }
197
+
198
+ return run_process(pythonBin, args, {
199
+ cwd: repoRoot,
200
+ env: { ...process.env, ...(options.env ?? {}) },
201
+ });
202
+ }