open-standards/wai/wai-ops-gpu/browser/run.mjs
Transaction Science 836d0255df wai packaging stack: #426, #438 and #452's GPU tier, squashed onto main
One squashed history of three stacked PRs, onto main at b16816671:

- #426 (wai/pack-efficiency): a title's whole AV1 ladder in one package over one
  integer base; several floor rungs (packaging §7.1), a re-pack from a parent
  (§11.5), a floor that is the ladder's own CMAF (§7.7), a floor-only profile
  (§7.8), a streaming pack (§11.4), and the independent verifier at scale (§9.4).
- #438 (wai/cross-fabric): each pack and verify step on the fabric with the least
  measured energy (§11.6); the `fabrics` member of measurement claims
  (jwp-receipts §2.4.8, energy-measurement §6.3); the wai-pack-gpu crate;
  `wai-pack fabric`; `wai-pack floor-decode`.
- #452 (wai/gpu-int-tier), its GPU-tier part only: claiming decode-equivalence on a
  GPU (determinism-tiers §5), the WGSL integer semantics suite, the rules check on
  naga's IR, and the kernels' shift masks and min(max()) clamps.

Left out, by the review's split (EN-C takes it over): #452's platform decode in
dispatch, its commits 6ad92e2e8, 8695bc75f and fed8694f4 and its CHANGELOG entry:
extensions/platform-decode.md, platform-decode-conformance/, wai::platform_decode,
wai-web's platform_decode bundle and demo, and its two SPEC.md sentences.

#438's research note is not carried as written: it is rewritten as
wai/research/cross-fabric.md, with hardware named by architecture, PCI id and OS
version, and every table marked as one run. floor_decode.rs and a wai_pack.rs
comment name the platform's media engine and a full-length film the same way.

wai/CHANGELOG.md is the union of both sides' entries, the stack's first.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 20:31:28 -04:00

99 lines
5.3 KiB
JavaScript

// The ops-conformance graph vectors through a browser's WebGPU.
//
// Build the page's module first:
//
// cd wai/wai-ops-gpu/browser
// cargo build --locked --release --target wasm32-unknown-unknown
// wasm-bindgen --target web --out-dir pkg --out-name wai_ops_gpu_browser \
// target/wasm32-unknown-unknown/release/wai_ops_gpu_browser.wasm
//
// then, with Playwright installed under PLAYWRIGHT_HOST:
//
// [WAI_OPS_GPU_REQUIRE=1] [WAI_OPS_GPU_BROWSER_CHANNEL=chrome] \
// PLAYWRIGHT_HOST=<dir with node_modules/playwright>/ node wai/wai-ops-gpu/browser/run.mjs
//
// It serves this directory, ../../ops-conformance and ../../wgsl-conformance on
// 127.0.0.1, opens index.html in a headless browser and prints the page's report. It
// fails on a page error, a mismatch or a device error, and, under WAI_OPS_GPU_REQUIRE=1,
// when the page found no adapter, or the report lacks a `browser webgpu: key=` line
// with mismatches=0 errors=0, the `wasm reference:` line saying the committed WGSL
// semantics vectors are equal, or a `browser webgpu semantics: key=` line with differ=0.
// Headless, macOS offers a page a hardware adapter only when the browser draws through
// Metal (`--use-angle=metal`); WAI_OPS_GPU_BROWSER_ARGS (space-separated) replaces the
// launch arguments.
import http from "node:http";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { createRequire } from "node:module";
const HERE = path.dirname(fileURLToPath(import.meta.url));
const CORPUS = path.resolve(HERE, "../../ops-conformance");
const WGSL_VECTORS = path.resolve(HERE, "../../wgsl-conformance");
const REQUIRE = process.env.WAI_OPS_GPU_REQUIRE === "1";
const PORT = Number(process.env.PORT || 8797);
const TIMEOUT_MS = Number(process.env.WAI_OPS_GPU_TIMEOUT_MS || 20 * 60 * 1000);
const ARGS = process.env.WAI_OPS_GPU_BROWSER_ARGS !== undefined
? process.env.WAI_OPS_GPU_BROWSER_ARGS.split(/\s+/).filter(Boolean)
: (process.platform === "darwin" ? ["--use-angle=metal"] : []).concat(["--enable-unsafe-webgpu"]);
const { chromium } = createRequire(process.env.PLAYWRIGHT_HOST || HERE + "/")("playwright");
const files = ["vectors", "graphs"].flatMap((d) =>
fs.readdirSync(path.join(CORPUS, d)).filter((f) => f.endsWith(".jsonl")).sort().map((f) => `${d}/${f}`));
const ROOTS = { "/ops-conformance/": CORPUS, "/wgsl-conformance/": WGSL_VECTORS, "/browser/": HERE };
const MIME = { ".js": "text/javascript", ".wasm": "application/wasm", ".html": "text/html", ".json": "application/json", ".jsonl": "text/plain" };
const server = http.createServer((req, res) => {
const url = decodeURIComponent(req.url.split("?")[0]);
if (url === "/browser/files.json") {
res.writeHead(200, { "content-type": "application/json" });
return res.end(JSON.stringify(files));
}
const prefix = Object.keys(ROOTS).find((p) => url.startsWith(p));
const root = prefix && ROOTS[prefix];
const f = root && path.resolve(root, "." + url.slice(prefix.length - 1));
if (!f || !f.startsWith(root + path.sep) || !fs.existsSync(f) || !fs.statSync(f).isFile()) {
res.writeHead(404);
return res.end("not found");
}
res.writeHead(200, { "content-type": MIME[path.extname(f)] || "application/octet-stream" });
fs.createReadStream(f).pipe(res);
});
let browser;
try {
if (!fs.existsSync(path.join(HERE, "pkg", "wai_ops_gpu_browser.js"))) {
console.error("no pkg/wai_ops_gpu_browser.js; build it as this file's header says");
process.exit(2);
}
await new Promise((r) => server.listen(PORT, "127.0.0.1", r));
const channel = process.env.WAI_OPS_GPU_BROWSER_CHANNEL || undefined;
browser = await chromium.launch({ args: ARGS, channel });
console.log(`browser: ${channel || "chromium"} ${browser.version()} args=${ARGS.join(" ")}`);
const page = await browser.newPage();
const errs = [];
page.on("pageerror", (e) => errs.push(String(e)));
await page.goto(`http://127.0.0.1:${PORT}/browser/index.html`, { waitUntil: "domcontentloaded", timeout: 30000 });
await page.waitForFunction(() => window.opsGpu && window.opsGpu.done === true, null, { timeout: TIMEOUT_MS, polling: 1000 });
const report = await page.evaluate(() => window.opsGpu.report);
const info = await page.evaluate(async () => {
if (!navigator.gpu) return "navigator.gpu is absent";
const a = await navigator.gpu.requestAdapter();
if (!a) return "requestAdapter gave none";
const i = a.info || {};
return `vendor=${i.vendor} architecture=${i.architecture} device=${i.device} description=${i.description}`;
});
console.log(`adapter info (navigator.gpu): ${info}`);
console.log(report);
for (const e of errs) console.log(`page error: ${e}`);
const lines = report.split("\n");
const ok = lines.some((l) => /^browser webgpu: key=\S+ .* mismatches=0 errors=0$/.test(l)) &&
lines.some((l) => /^wasm reference: target=wasm32 .* cases=[1-9][0-9]* equal$/.test(l)) &&
lines.some((l) => /^browser webgpu semantics: key=\S+ .* guarded=[1-9][0-9]* differ=0 /.test(l));
const bad = errs.length > 0 || /MISMATCH|DEVICE ERROR|page error|did not parse/.test(report) ||
lines.some((l) => /^browser webgpu: key=/.test(l) && !/mismatches=0 errors=0$/.test(l)) ||
lines.some((l) => /^browser webgpu semantics: key=/.test(l) && !/ differ=0 /.test(l));
if (bad || (REQUIRE && !ok)) process.exitCode = 1;
} finally {
if (browser) await browser.close();
server.close();
}