1/**
2 * Desktop WebGPU for the command-line scripts, via the optional `webgpu`
3 * package (prebuilt Google Dawn).
4 *
5 * Installs Dawn under the globals the transform code expects (navigator.gpu,
6 * GPUBufferUsage, ...) so everything under src/ runs here unchanged —
7 * including requestShtDevice(), which makes the same device request the
8 * browser makes.
9 */
11export const errMsg = (e: unknown): string =>
12 e instanceof Error ? e.message : String(e);
14/**
15 * Returns a human-readable runtime description. The import specifier is
16 * indirect so typechecking does not require the optional package.
17 */
18export async function installWebGpu(): Promise<string> {
19 const specifier = 'webgpu';
20 let mod: {
21 create: (flags: string[]) => GPU;
22 globals: Record<string, unknown>;
23 };
24 try {
25 mod = await import(specifier);
26 } catch (e) {
27 // Distinguish "not installed" from "installed but the prebuilt Dawn binary
28 // will not load" — the second is what a machine missing a system library
29 // looks like, and reporting it as the first sends people in circles.
30 const detail = errMsg(e);
31 if (/Cannot find (package|module) '?webgpu'?/.test(detail)) {
32 throw new Error(
33 'desktop WebGPU needs the optional `webgpu` package (prebuilt Google Dawn):\n' +
34 ' npm install webgpu\n' +
35 'It is an optionalDependency, so npm can skip it silently — `npm ls webgpu`\n' +
36 'says whether it is there.',
37 );
38 }
39 const glibc = /GLIBC_([0-9.]+)/.exec(detail);
40 throw new Error(
41 `the \`webgpu\` package is installed but did not load:\n ${detail}\n` +
42 (glibc
43 ? `Dawn's prebuilt binary wants glibc ${glibc[1]} or newer and this host is older\n` +
44 '(`ldd --version` says how old). No flag bridges that — use a container with a\n' +
45 'newer base image, or a newer host.\n'
46 : 'That is usually the prebuilt Dawn binary missing a system library.\n'),
47 );
48 }
49 Object.assign(globalThis, mod.globals);
50 // DAWN_FLAGS is ';'-separated because individual Dawn options take
51 // comma-separated lists, e.g. 'enable-dawn-features=allow_unsafe_apis,...'
52 const dawnFlags = process.env.DAWN_FLAGS?.split(';').filter(Boolean) ?? [];
53 Object.defineProperty(globalThis, 'navigator', {
54 value: { gpu: mod.create(dawnFlags) },
55 configurable: true,
56 writable: true,
57 });
58 const { version } = await import(`${specifier}/package.json`, {
59 with: { type: 'json' },
60 }).then(
61 (m) => m.default as { version: string },
62 () => ({ version: '?' }),
63 );
64 return `node-webgpu ${version} (Google Dawn)`;
65}
67/** The hint to print when Dawn loads but finds no adapter. */
68export const NO_ADAPTER_HINT =
69 ' Dawn reaches the GPU through Vulkan on Linux and Windows, Metal on macOS,\n' +
70 " so a headless box may have no adapter at all. DAWN_FLAGS='backend=vulkan'\n" +
71 ' makes it explain itself.';