fix: align local WebGPU capture behavior (#2907)

* fix: align local WebGPU capture behavior

* fix: address WebGPU capture review feedback

* fix: retain overlapping GPU seek work

* fix: satisfy runtime seek completion types

* fix: drain concurrent GPU seek work

* fix: prevent WebGPU capture barrier starvation

* fix: keep WebGPU presentation active during render seeks
This commit is contained in:
James Russo
2026-07-30 21:52:30 -07:00
committed by GitHub
parent 1d636f603c
commit 3a6b7f0612
34 changed files with 1208 additions and 137 deletions
@@ -0,0 +1,34 @@
import { describe, expect, it } from "vitest";
import {
assertWebGpuRequirement,
compositionRequiresWebGpu,
resolveLocalBrowserGpuMode,
} from "./gpuPolicy.js";
describe("local browser GPU policy", () => {
it("defaults to auto and preserves explicit CLI/env overrides", () => {
expect(resolveLocalBrowserGpuMode(undefined, undefined)).toBe("auto");
expect(resolveLocalBrowserGpuMode(undefined, "hardware")).toBe("hardware");
expect(resolveLocalBrowserGpuMode(undefined, "software")).toBe("software");
expect(resolveLocalBrowserGpuMode(true, "software")).toBe("hardware");
expect(resolveLocalBrowserGpuMode(false, "hardware")).toBe("software");
});
it("detects the explicit WebGPU capability marker on the composition root", () => {
expect(
compositionRequiresWebGpu(
'<div data-requires-webgpu data-composition-id="gpu" data-duration="2"></div>',
),
).toBe(true);
expect(compositionRequiresWebGpu('<div data-composition-id="dom"></div>')).toBe(false);
});
it("rejects an auto software fallback for required WebGPU compositions", () => {
const html = '<div data-composition-id="gpu" data-requires-webgpu></div>';
expect(() => assertWebGpuRequirement(html, "auto", "software")).toThrow(
"PRODUCER_BROWSER_GPU_MODE=hardware",
);
expect(() => assertWebGpuRequirement(html, "hardware", "hardware")).not.toThrow();
expect(() => assertWebGpuRequirement(html, "software", "software")).not.toThrow();
});
});
+43
View File
@@ -0,0 +1,43 @@
export type BrowserGpuMode = "auto" | "hardware" | "software";
export type ResolvedBrowserGpuMode = Exclude<BrowserGpuMode, "auto">;
export function resolveLocalBrowserGpuMode(
browserGpuArg?: boolean,
envMode = process.env.PRODUCER_BROWSER_GPU_MODE,
): BrowserGpuMode {
if (browserGpuArg === true) return "hardware";
if (browserGpuArg === false) return "software";
if (envMode === "hardware" || envMode === "software" || envMode === "auto") return envMode;
return "auto";
}
export async function resolveCaptureBrowserGpuMode(
requestedMode: BrowserGpuMode,
chromePath?: string,
): Promise<ResolvedBrowserGpuMode> {
const { resolveBrowserGpuMode } = await import("@hyperframes/engine");
return resolveBrowserGpuMode(requestedMode, { chromePath });
}
export function compositionRequiresWebGpu(html: string): boolean {
const compositionRoot = html.match(
/<[^>]*\bdata-composition-id(?:\s*=\s*("[^"]*"|'[^']*'|[^\s>]+))?[^>]*>/i,
);
return compositionRoot ? /\bdata-requires-webgpu(?:\s|=|>)/i.test(compositionRoot[0]) : false;
}
export function assertWebGpuRequirement(
html: string,
requestedMode: BrowserGpuMode,
resolvedMode: ResolvedBrowserGpuMode,
): void {
if (requestedMode !== "auto" || resolvedMode !== "software" || !compositionRequiresWebGpu(html)) {
return;
}
throw new Error(
"This composition declares data-requires-webgpu, but browser GPU auto-detection found no hardware GPU. " +
"Run on a WebGPU-capable host or set PRODUCER_BROWSER_GPU_MODE=hardware " +
"(or pass --browser-gpu on commands that support it) to require hardware explicitly; " +
"use --no-browser-gpu only when intentionally testing the composition's software fallback.",
);
}