commit fcc479278961d7f1227a2b4c9e79b0958873321a
parent 8162aedae1bdd2d3936491779d4416403da36777
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Fri, 9 Oct 2026 00:05:13 -0400
Merge umtool/headless-no-display (the renderer runs without a display)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
2 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/umtool/report-to-video/compose-chrome.mjs b/umtool/report-to-video/compose-chrome.mjs
@@ -734,9 +734,21 @@ async function framesOnDisk(dir) {
}
/** Run the renderer, its chatter to stderr (stdout may be a build's NDJSON). */
+/**
+ * The renderer's environment: ours without DISPLAY and WAYLAND_DISPLAY. It is
+ * headless and needs no display, and a stale one breaks it: with DISPLAY naming
+ * an X server that has gone (Xwayland killed by the OOM killer, say), ANGLE's
+ * SwiftShader tries to connect to it, fails, and every render dies with
+ * "assertSwiftShader ... vendor=''".
+ */
+export function rendererEnv(env = process.env) {
+ const { DISPLAY: _d, WAYLAND_DISPLAY: _w, ...rest } = env;
+ return rest;
+}
+
function runRenderer(cmd, args) {
return new Promise((resolve, reject) => {
- const child = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
+ const child = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"], env: rendererEnv() });
let tail = "";
const keep = (b) => {
process.stderr.write(b);
@@ -928,7 +940,7 @@ export async function composeChrome({
"--virtual-time-budget=6000",
`--screenshot=${out}`,
`file://${path.join(projDir, "index.html")}?still=${Number(still)}`,
- ], { maxBuffer: 1 << 26 });
+ ], { maxBuffer: 1 << 26, env: rendererEnv() });
return { ...result, still: out };
}
diff --git a/umtool/report-to-video/threads.test.mjs b/umtool/report-to-video/threads.test.mjs
@@ -187,3 +187,9 @@ test("railLayout: cards share the height, capped, the stack centred", () => {
assert.equal(three.h, 190);
assert.equal(three.top, Math.floor((886 - 3 * 190 - 2 * 14) / 2));
});
+
+test("the renderer runs without a display: a stale DISPLAY would break SwiftShader", async () => {
+ const { rendererEnv } = await import("./compose-chrome.mjs");
+ const env = rendererEnv({ DISPLAY: ":0.0", WAYLAND_DISPLAY: "wayland-0", PATH: "/usr/bin", HOME: "/h" });
+ assert.deepEqual(env, { PATH: "/usr/bin", HOME: "/h" });
+});