From 85360f370ae0569aa6b34a13067d3ca771cf2183 Mon Sep 17 00:00:00 2001 From: "Abdullah M. Nazif" Date: Thu, 8 Oct 2026 20:07:01 +0000 Subject: [PATCH] fix(cli): explain hardware GPU screenshot fallback (#5200) --- packages/cli/src/ui/format.test.ts | 46 ++++++++++++++++++++++++++++-- packages/cli/src/ui/format.ts | 19 ++++++++++-- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/ui/format.test.ts b/packages/cli/src/ui/format.test.ts index 131b207151..f1d13fe249 100644 --- a/packages/cli/src/ui/format.test.ts +++ b/packages/cli/src/ui/format.test.ts @@ -64,16 +64,58 @@ describe("formatScreenshotFallbackHint", () => { platform: "linux" as const, }; - it("explains the slow path only for linux + auto-probed software gpu + screenshot", () => { + it("explains Linux software screenshot capture after automatic GPU selection", () => { expect(formatScreenshotFallbackHint(slow)).toContain("BeginFrame did not run"); expect(formatScreenshotFallbackHint({ ...slow, platform: "darwin" })).toBeUndefined(); - expect(formatScreenshotFallbackHint({ ...slow, browserGpuMode: "hardware" })).toBeUndefined(); expect(formatScreenshotFallbackHint({ ...slow, captureMode: "beginframe" })).toBeUndefined(); }); + it.each(["auto", "hardware"] as const)( + "explains hardware screenshot capture when GPU mode %s was requested", + (requestedGpuMode) => { + const hint = formatScreenshotFallbackHint({ + ...slow, + browserGpuMode: "hardware", + requestedGpuMode, + }); + + expect(hint).toContain("BeginFrame did not run"); + expect(hint).toContain("probe"); + expect(hint).toContain("fewer --workers"); + expect(hint).not.toContain("software GL"); + }, + ); + + it.each(["beginframe", "drawelement", "beginframe|screenshot", undefined])( + "does not claim BeginFrame was absent for capture mode %s", + (captureMode) => { + expect( + formatScreenshotFallbackHint({ ...slow, captureMode, browserGpuMode: "hardware" }), + ).toBeUndefined(); + }, + ); + + it.each(["darwin", "win32"] as const)("stays silent on %s", (platform) => { + expect( + formatScreenshotFallbackHint({ ...slow, platform, browserGpuMode: "hardware" }), + ).toBeUndefined(); + }); + it("stays silent when software gpu was requested, as --docker and --no-browser-gpu do", () => { expect(formatScreenshotFallbackHint({ ...slow, requestedGpuMode: "software" })).toBeUndefined(); expect(formatScreenshotFallbackHint({ ...slow, requestedGpuMode: undefined })).toBeUndefined(); + expect( + formatScreenshotFallbackHint({ + ...slow, + browserGpuMode: "hardware", + requestedGpuMode: "software", + }), + ).toBeUndefined(); + }); + + it("stays silent when the observed GPU mode is unavailable", () => { + expect(formatScreenshotFallbackHint({ ...slow, browserGpuMode: undefined })).toBeUndefined(); + expect(formatScreenshotFallbackHint({ ...slow, browserGpuMode: "unknown" })).toBeUndefined(); }); }); diff --git a/packages/cli/src/ui/format.ts b/packages/cli/src/ui/format.ts index 79bc1252d8..df3d2683d2 100644 --- a/packages/cli/src/ui/format.ts +++ b/packages/cli/src/ui/format.ts @@ -94,7 +94,7 @@ export function formatRenderPipelineDetail(input: { } /** - * Why a Linux auto render stayed on screenshot after BeginFrame was requested. + * Why a Linux render stayed on screenshot after automatic or hardware GPU selection. * Silent when software was requested (--docker, --no-browser-gpu) or off Linux. */ export function formatScreenshotFallbackHint(input: { @@ -103,8 +103,21 @@ export function formatScreenshotFallbackHint(input: { requestedGpuMode?: BrowserGpuMode; platform: NodeJS.Platform; }): string | undefined { - if (input.platform !== "linux" || input.requestedGpuMode !== "auto") return undefined; - if (input.captureMode !== "screenshot" || input.browserGpuMode !== "software") return undefined; + if ( + input.platform !== "linux" || + (input.requestedGpuMode !== "auto" && input.requestedGpuMode !== "hardware") + ) { + return undefined; + } + if (input.captureMode !== "screenshot") return undefined; + if (input.browserGpuMode === "hardware") { + return ( + "Screenshot capture (slower): BeginFrame did not run. Needs chrome-headless-shell and no " + + "--resolution upscale. A BeginFrame probe timeout can occur when browsers start together " + + "on one GPU; fewer --workers may help." + ); + } + if (input.browserGpuMode !== "software" || input.requestedGpuMode !== "auto") return undefined; return ( "Screenshot capture (slower): BeginFrame did not run. Needs chrome-headless-shell and no " + "--resolution upscale. Heavy compositions can stall on software GL."