mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
feat(engine): browserGpuMode "auto" — probe WebGL once, fall back to software
When the host doesn't have a usable GPU (CI containers, eval rigs without
GPU passthrough, dev VMs), Chrome's hardware-mode WebGL flags
(`--use-gl=egl/metal/d3d11`) silently leave WebGL unavailable —
`getContext("webgl")` returns null, three.js' WebGLRenderer dies, the
canvas stays black. Surfaced today by Abhay's c2v-eval failing on a
docker render of an hf bundle that uses three.js + a custom fragment
shader.
The fix that's been there: `--use-gl=angle --use-angle=swiftshader` (CPU
software WebGL, ~5-50× slower but pixel-identical). The engine already
exposed `browserGpuMode: "software"` for this. The gap was discovery —
users had to know to pass `--no-browser-gpu` on no-GPU hosts.
This change adds `browserGpuMode: "auto"` (now the CLI default for local
renders): on first launch in the process, probe Chrome with hardware
args, check `canvas.getContext("webgl") !== null`, cache the result.
~1-2 s on first render, free on every subsequent render in the same
worker. Hardware GPUs keep their fast path; no-GPU hosts get SwiftShader
without ceremony.
Behaviour matrix:
- No flag, no env, local → "auto" (NEW default)
- `--browser-gpu` → "hardware" (force; errors if no GPU)
- `--no-browser-gpu` → "software" (force SwiftShader)
- `PRODUCER_BROWSER_GPU_MODE` → "hardware" / "software" / "auto" / unset
- Docker mode → forced "software" (unchanged)
Engine-config default stays "software" (conservative for embedders); the
"auto" default lives in the CLI's `resolveBrowserGpuForCli` so producer
embedders aren't surprised by a probe-on-launch.
Also adds `--enable-unsafe-swiftshader` to the software flag set —
Chrome 120+ deprecated implicit SwiftShader fallback and emits a
deprecation warning unless the flag is set explicitly. Despite the
"unsafe" name this is exactly the pre-deprecation behaviour; the rename
is about Chrome's threat model on the open web, not about the rendering
itself.
Verification:
- Engine 535/535 + CLI 256/256 (incl. new probe tests + tri-state CLI test)
- Empirical: probe on this no-GPU devbox returns "software" in 240 ms,
cached 0 ms on subsequent calls
- Format / lint / typecheck clean across all packages
Refs the Abhay/Slack thread on c2v-eval rendering without a GPU node.
This commit is contained in:
@@ -72,6 +72,25 @@ describe("renderLocal browser GPU config", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("forwards browserGpuMode='auto' into producer config (probe-then-choose)", async () => {
|
||||
const { renderLocal } = await import("./render.js");
|
||||
await renderLocal("/tmp/project", "/tmp/out.mp4", {
|
||||
fps: 30,
|
||||
quality: "standard",
|
||||
format: "mp4",
|
||||
gpu: false,
|
||||
browserGpuMode: "auto",
|
||||
hdrMode: "auto",
|
||||
quiet: true,
|
||||
});
|
||||
|
||||
expect(producerState.resolveConfigCalls).toContainEqual({ browserGpuMode: "auto" });
|
||||
expect(producerState.createdJobs[0]?.producerConfig).toMatchObject({
|
||||
browserGpuMode: "auto",
|
||||
resolved: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("passes an explicit hardware override for default local browser GPU", async () => {
|
||||
const { renderLocal } = await import("./render.js");
|
||||
await renderLocal("/tmp/project", "/tmp/out.mp4", {
|
||||
@@ -94,12 +113,18 @@ describe("renderLocal browser GPU config", () => {
|
||||
it("resolves browser GPU from CLI flags, Docker mode, and env fallback", async () => {
|
||||
const { resolveBrowserGpuForCli } = await import("./render.js");
|
||||
|
||||
expect(resolveBrowserGpuForCli(false, undefined, undefined)).toBe(true);
|
||||
expect(resolveBrowserGpuForCli(false, undefined, "hardware")).toBe(true);
|
||||
expect(resolveBrowserGpuForCli(false, undefined, "software")).toBe(false);
|
||||
expect(resolveBrowserGpuForCli(false, true, "software")).toBe(true);
|
||||
expect(resolveBrowserGpuForCli(false, false, "hardware")).toBe(false);
|
||||
expect(resolveBrowserGpuForCli(true, undefined, "hardware")).toBe(false);
|
||||
// Default (no flag, no env): auto — engine probes and chooses.
|
||||
expect(resolveBrowserGpuForCli(false, undefined, undefined)).toBe("auto");
|
||||
// Env override
|
||||
expect(resolveBrowserGpuForCli(false, undefined, "hardware")).toBe("hardware");
|
||||
expect(resolveBrowserGpuForCli(false, undefined, "software")).toBe("software");
|
||||
expect(resolveBrowserGpuForCli(false, undefined, "auto")).toBe("auto");
|
||||
// Explicit CLI flag wins over env
|
||||
expect(resolveBrowserGpuForCli(false, true, "software")).toBe("hardware");
|
||||
expect(resolveBrowserGpuForCli(false, false, "hardware")).toBe("software");
|
||||
// Docker forces software regardless of flags/env
|
||||
expect(resolveBrowserGpuForCli(true, undefined, "hardware")).toBe("software");
|
||||
expect(resolveBrowserGpuForCli(true, undefined, "auto")).toBe("software");
|
||||
});
|
||||
|
||||
it("forwards parsed --variables payload to createRenderJob", async () => {
|
||||
|
||||
@@ -118,7 +118,7 @@ export default defineCommand({
|
||||
"browser-gpu": {
|
||||
type: "boolean",
|
||||
description:
|
||||
"Use host GPU acceleration for Chrome/WebGL capture. Enabled by default for local renders; use --no-browser-gpu to opt out.",
|
||||
"Force host GPU acceleration for Chrome/WebGL capture. Default: auto (probe on first launch; fall back to software if no GPU). Use --no-browser-gpu to force software (SwiftShader).",
|
||||
},
|
||||
quiet: {
|
||||
type: "boolean",
|
||||
@@ -224,7 +224,7 @@ export default defineCommand({
|
||||
const useDocker = args.docker ?? false;
|
||||
const useGpu = args.gpu ?? false;
|
||||
const browserGpuArg = args["browser-gpu"];
|
||||
const useBrowserGpu = resolveBrowserGpuForCli(useDocker, browserGpuArg);
|
||||
const browserGpuMode = resolveBrowserGpuForCli(useDocker, browserGpuArg);
|
||||
const quiet = args.quiet ?? false;
|
||||
const strictAll = args["strict-all"] ?? false;
|
||||
const strictErrors = (args.strict ?? false) || strictAll;
|
||||
@@ -275,10 +275,14 @@ export default defineCommand({
|
||||
c.dim(" \u2192 " + outputPath),
|
||||
);
|
||||
console.log(c.dim(" " + fps + "fps \u00B7 " + quality + " \u00B7 " + workerLabel));
|
||||
if (useGpu || useBrowserGpu) {
|
||||
if (useGpu || browserGpuMode !== "software") {
|
||||
const gpuModes = [
|
||||
useGpu ? "encoder GPU" : null,
|
||||
useBrowserGpu ? "browser GPU (auto)" : null,
|
||||
browserGpuMode === "hardware"
|
||||
? "browser GPU (forced)"
|
||||
: browserGpuMode === "auto"
|
||||
? "browser GPU (auto-detect)"
|
||||
: null,
|
||||
].filter(Boolean);
|
||||
console.log(c.dim(" GPU: " + gpuModes.join(" + ")));
|
||||
}
|
||||
@@ -397,7 +401,7 @@ export default defineCommand({
|
||||
format,
|
||||
workers,
|
||||
gpu: useGpu,
|
||||
browserGpu: useBrowserGpu,
|
||||
browserGpu: browserGpuMode === "hardware",
|
||||
hdrMode: args.sdr ? "force-sdr" : args.hdr ? "force-hdr" : "auto",
|
||||
crf,
|
||||
videoBitrate,
|
||||
@@ -412,7 +416,7 @@ export default defineCommand({
|
||||
format,
|
||||
workers,
|
||||
gpu: useGpu,
|
||||
browserGpu: useBrowserGpu,
|
||||
browserGpuMode,
|
||||
hdrMode: args.sdr ? "force-sdr" : args.hdr ? "force-hdr" : "auto",
|
||||
crf,
|
||||
videoBitrate,
|
||||
@@ -431,7 +435,15 @@ interface RenderOptions {
|
||||
format: "mp4" | "webm" | "mov";
|
||||
workers?: number;
|
||||
gpu: boolean;
|
||||
browserGpu: boolean;
|
||||
/**
|
||||
* For local renders: tri-state ("auto" | "hardware" | "software"). "auto"
|
||||
* probes WebGL availability on first launch and falls back to software.
|
||||
* Docker renders use the boolean `browserGpu` field instead because the
|
||||
* docker run-args wire the inner CLI's `--no-browser-gpu` flag.
|
||||
*/
|
||||
browserGpuMode?: "auto" | "hardware" | "software";
|
||||
/** Docker-only: true → host GPU passthrough, false → forced software. */
|
||||
browserGpu?: boolean;
|
||||
hdrMode: "auto" | "force-hdr" | "force-sdr";
|
||||
crf?: number;
|
||||
videoBitrate?: string;
|
||||
@@ -579,15 +591,33 @@ export function validateVariablesAgainstProject(
|
||||
return validateVariables(values, meta.variables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the browser-GPU mode for a CLI render invocation.
|
||||
*
|
||||
* Priority (highest first):
|
||||
* 1. Docker mode → always "software" (docker has no portable GPU
|
||||
* passthrough; the engine's render path uses SwiftShader).
|
||||
* 2. Explicit CLI flag — `--browser-gpu` → "hardware",
|
||||
* `--no-browser-gpu` → "software".
|
||||
* 3. Env var `PRODUCER_BROWSER_GPU_MODE` accepts "hardware" / "software" /
|
||||
* "auto".
|
||||
* 4. Default = "auto" — engine probes WebGL availability on first launch
|
||||
* and falls back to software if the host lacks a usable GPU.
|
||||
*
|
||||
* Returning "auto" by default lets local renders Just Work whether or not the
|
||||
* host has a GPU, while preserving the explicit overrides for CI / power
|
||||
* users who want failure-on-misconfig.
|
||||
*/
|
||||
export function resolveBrowserGpuForCli(
|
||||
useDocker: boolean,
|
||||
browserGpuArg: boolean | undefined,
|
||||
envMode = process.env.PRODUCER_BROWSER_GPU_MODE,
|
||||
): boolean {
|
||||
if (useDocker) return false;
|
||||
if (browserGpuArg !== undefined) return browserGpuArg;
|
||||
if (envMode === "software") return false;
|
||||
return true;
|
||||
): "auto" | "hardware" | "software" {
|
||||
if (useDocker) return "software";
|
||||
if (browserGpuArg === true) return "hardware";
|
||||
if (browserGpuArg === false) return "software";
|
||||
if (envMode === "hardware" || envMode === "software" || envMode === "auto") return envMode;
|
||||
return "auto";
|
||||
}
|
||||
|
||||
const DOCKER_IMAGE_PREFIX = "hyperframes-renderer";
|
||||
@@ -707,7 +737,7 @@ async function renderDocker(
|
||||
format: options.format,
|
||||
workers: options.workers,
|
||||
gpu: options.gpu,
|
||||
browserGpu: options.browserGpu,
|
||||
browserGpu: options.browserGpu ?? false,
|
||||
hdrMode: options.hdrMode,
|
||||
crf: options.crf,
|
||||
videoBitrate: options.videoBitrate,
|
||||
@@ -777,7 +807,7 @@ export async function renderLocal(
|
||||
workers: options.workers,
|
||||
useGpu: options.gpu,
|
||||
producerConfig: producer.resolveConfig({
|
||||
browserGpuMode: options.browserGpu ? "hardware" : "software",
|
||||
browserGpuMode: options.browserGpuMode ?? (options.browserGpu ? "hardware" : "software"),
|
||||
}),
|
||||
hdrMode: options.hdrMode,
|
||||
crf: options.crf,
|
||||
|
||||
@@ -97,6 +97,13 @@ describe("resolveConfig", () => {
|
||||
expect(config.browserGpuMode).toBe("hardware");
|
||||
});
|
||||
|
||||
it("accepts 'auto' as a valid browser GPU mode env value", () => {
|
||||
setEnv("PRODUCER_BROWSER_GPU_MODE", "auto");
|
||||
|
||||
const config = resolveConfig();
|
||||
expect(config.browserGpuMode).toBe("auto");
|
||||
});
|
||||
|
||||
it("falls back to software browser GPU mode for invalid env values", () => {
|
||||
setEnv("PRODUCER_BROWSER_GPU_MODE", "native");
|
||||
|
||||
|
||||
@@ -32,10 +32,15 @@ export interface EngineConfig {
|
||||
chromePath?: string;
|
||||
disableGpu: boolean;
|
||||
/**
|
||||
* Chrome/WebGL rendering backend. "software" keeps the existing SwiftShader
|
||||
* path for reproducible output; "hardware" lets Chrome use the host GPU.
|
||||
* Chrome/WebGL rendering backend.
|
||||
* - "software": SwiftShader (CPU-only). Always works; ~5-50× slower than GPU.
|
||||
* - "hardware": host GPU via platform-native ANGLE backend (Metal/D3D11/EGL).
|
||||
* Errors if no usable GPU is reachable from Chrome.
|
||||
* - "auto": probe Chrome for WebGL availability on first launch in this
|
||||
* process; fall back to software if hardware-mode WebGL is unavailable.
|
||||
* Cost: one extra Chrome launch (~1-2 s) per process; result cached.
|
||||
*/
|
||||
browserGpuMode: "software" | "hardware";
|
||||
browserGpuMode: "software" | "hardware" | "auto";
|
||||
enableBrowserPool: boolean;
|
||||
browserTimeout: number;
|
||||
protocolTimeout: number;
|
||||
@@ -173,7 +178,7 @@ export function resolveConfig(overrides?: Partial<EngineConfig>): EngineConfig {
|
||||
};
|
||||
const envBrowserGpuMode = (): EngineConfig["browserGpuMode"] => {
|
||||
const raw = env("PRODUCER_BROWSER_GPU_MODE");
|
||||
if (raw === "hardware" || raw === "software") return raw;
|
||||
if (raw === "hardware" || raw === "software" || raw === "auto") return raw;
|
||||
return DEFAULT_CONFIG.browserGpuMode;
|
||||
};
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ export {
|
||||
acquireBrowser,
|
||||
releaseBrowser,
|
||||
resolveHeadlessShellPath,
|
||||
resolveBrowserGpuMode,
|
||||
buildChromeArgs,
|
||||
ENABLE_BROWSER_POOL,
|
||||
type BuildChromeArgsOptions,
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { buildChromeArgs, forceReleaseBrowser } from "./browserManager.js";
|
||||
import {
|
||||
_resetAutoBrowserGpuModeCacheForTests,
|
||||
buildChromeArgs,
|
||||
forceReleaseBrowser,
|
||||
resolveBrowserGpuMode,
|
||||
} from "./browserManager.js";
|
||||
|
||||
describe("buildChromeArgs browser GPU mode", () => {
|
||||
const base = { width: 1920, height: 1080 };
|
||||
@@ -10,6 +15,7 @@ describe("buildChromeArgs browser GPU mode", () => {
|
||||
expect(args).toContain("--enable-features=CanvasDrawElement");
|
||||
expect(args).toContain("--use-gl=angle");
|
||||
expect(args).toContain("--use-angle=swiftshader");
|
||||
expect(args).toContain("--enable-unsafe-swiftshader");
|
||||
expect(args).not.toContain("--enable-gpu-rasterization");
|
||||
});
|
||||
|
||||
@@ -48,6 +54,57 @@ describe("buildChromeArgs browser GPU mode", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveBrowserGpuMode", () => {
|
||||
beforeEach(() => {
|
||||
_resetAutoBrowserGpuModeCacheForTests();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
_resetAutoBrowserGpuModeCacheForTests();
|
||||
});
|
||||
|
||||
it("passes 'software' through unchanged without probing", async () => {
|
||||
const mode = await resolveBrowserGpuMode("software");
|
||||
expect(mode).toBe("software");
|
||||
});
|
||||
|
||||
it("passes 'hardware' through unchanged without probing", async () => {
|
||||
const mode = await resolveBrowserGpuMode("hardware");
|
||||
expect(mode).toBe("hardware");
|
||||
});
|
||||
|
||||
it("falls back to 'software' when the probe browser cannot launch", async () => {
|
||||
// No chromePath, env unset, and (in the test env) no system Chrome to find
|
||||
// → puppeteer.launch will throw → caller catches → software fallback.
|
||||
// Force a definitely-missing chrome binary so the launch path errors fast.
|
||||
const mode = await resolveBrowserGpuMode("auto", {
|
||||
chromePath: "/definitely/not/a/real/chrome/binary",
|
||||
browserTimeout: 2000,
|
||||
});
|
||||
expect(mode).toBe("software");
|
||||
});
|
||||
|
||||
it("caches the probe result across calls", async () => {
|
||||
const first = await resolveBrowserGpuMode("auto", {
|
||||
chromePath: "/definitely/not/a/real/chrome/binary",
|
||||
browserTimeout: 2000,
|
||||
});
|
||||
// Second call uses cache — no new launch. Assert the same answer comes back
|
||||
// even with a different chromePath that would have a different probe outcome.
|
||||
const second = await resolveBrowserGpuMode("auto", {
|
||||
chromePath: "/another/definitely/missing/path",
|
||||
browserTimeout: 2000,
|
||||
});
|
||||
expect(first).toBe("software");
|
||||
expect(second).toBe("software");
|
||||
// Reset and re-probe to confirm the test-only reset works.
|
||||
_resetAutoBrowserGpuModeCacheForTests();
|
||||
const third = await resolveBrowserGpuMode("hardware");
|
||||
expect(third).toBe("hardware");
|
||||
});
|
||||
});
|
||||
|
||||
describe("forceReleaseBrowser", () => {
|
||||
it("kills the browser process and disconnects", () => {
|
||||
const killFn = vi.fn(() => true);
|
||||
|
||||
@@ -136,6 +136,97 @@ async function probeBeginFrameSupport(browser: Browser): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached result of `resolveBrowserGpuMode("auto", ...)` for the lifetime of
|
||||
* this process. The probe launches a transient Chrome with hardware args and
|
||||
* checks whether `canvas.getContext("webgl")` returns a context. Result is
|
||||
* memoised because the answer is a property of the host (GPU/driver
|
||||
* availability) and cannot meaningfully change inside one process.
|
||||
*
|
||||
* Exported for tests; production callers go through `resolveBrowserGpuMode`.
|
||||
*/
|
||||
export let _autoBrowserGpuModeCache: "software" | "hardware" | undefined;
|
||||
|
||||
/** Test-only: reset the cached probe result. */
|
||||
export function _resetAutoBrowserGpuModeCacheForTests(): void {
|
||||
_autoBrowserGpuModeCache = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve `browserGpuMode` to a concrete `"software" | "hardware"` answer.
|
||||
*
|
||||
* For `"software"` / `"hardware"` this is a pure pass-through. For `"auto"`
|
||||
* it launches a tiny Chrome with the platform's hardware GPU args, runs a
|
||||
* one-shot WebGL availability probe, and falls back to `"software"` if
|
||||
* hardware-mode WebGL is unavailable. The probe result is cached for the
|
||||
* process lifetime — a multi-render run pays the ~1-2 s cost once.
|
||||
*
|
||||
* Any failure (Chrome launch error, navigation timeout, missing canvas API,
|
||||
* etc.) is treated as a `"software"` fallback. The render path with
|
||||
* SwiftShader always works, so a misclassification toward software is the
|
||||
* safe failure mode; misclassifying toward hardware would error on the real
|
||||
* render.
|
||||
*/
|
||||
export async function resolveBrowserGpuMode(
|
||||
mode: EngineConfig["browserGpuMode"],
|
||||
options: {
|
||||
chromePath?: string;
|
||||
browserTimeout?: number;
|
||||
platform?: NodeJS.Platform;
|
||||
} = {},
|
||||
): Promise<"software" | "hardware"> {
|
||||
if (mode !== "auto") return mode;
|
||||
if (_autoBrowserGpuModeCache !== undefined) return _autoBrowserGpuModeCache;
|
||||
|
||||
const platform = options.platform ?? process.platform;
|
||||
const browserTimeout = options.browserTimeout ?? DEFAULT_CONFIG.browserTimeout;
|
||||
const executablePath = options.chromePath ?? resolveHeadlessShellPath({});
|
||||
|
||||
const probeArgs = [
|
||||
"--no-sandbox",
|
||||
"--disable-setuid-sandbox",
|
||||
"--disable-dev-shm-usage",
|
||||
"--enable-webgl",
|
||||
"--ignore-gpu-blocklist",
|
||||
...getBrowserGpuArgs("hardware", platform),
|
||||
];
|
||||
|
||||
const ppt = await getPuppeteer().catch(() => null);
|
||||
if (!ppt) {
|
||||
_autoBrowserGpuModeCache = "software";
|
||||
return _autoBrowserGpuModeCache;
|
||||
}
|
||||
|
||||
let probeBrowser: Browser | undefined;
|
||||
try {
|
||||
probeBrowser = await ppt.launch({
|
||||
headless: true,
|
||||
args: probeArgs,
|
||||
defaultViewport: { width: 64, height: 64 },
|
||||
executablePath,
|
||||
timeout: browserTimeout,
|
||||
});
|
||||
const page = await probeBrowser.newPage();
|
||||
const hasWebGL = await page.evaluate(() => {
|
||||
try {
|
||||
const c = document.createElement("canvas");
|
||||
const gl =
|
||||
c.getContext("webgl") || (c.getContext("experimental-webgl") as RenderingContext | null);
|
||||
return gl !== null;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
_autoBrowserGpuModeCache = hasWebGL ? "hardware" : "software";
|
||||
} catch {
|
||||
_autoBrowserGpuModeCache = "software";
|
||||
} finally {
|
||||
await probeBrowser?.close().catch(() => {});
|
||||
}
|
||||
|
||||
return _autoBrowserGpuModeCache;
|
||||
}
|
||||
|
||||
export async function acquireBrowser(
|
||||
chromeArgs: string[],
|
||||
config?: Partial<
|
||||
@@ -344,7 +435,20 @@ function getBrowserGpuArgs(
|
||||
platform: NodeJS.Platform,
|
||||
): string[] {
|
||||
if (mode === "software") {
|
||||
return ["--use-gl=angle", "--use-angle=swiftshader"];
|
||||
// Chrome 120+ deprecated implicit SwiftShader fallback; the explicit
|
||||
// path (--use-angle=swiftshader) keeps working but Chrome emits a
|
||||
// deprecation warning unless --enable-unsafe-swiftshader is also set.
|
||||
// Despite the name, this is exactly the behaviour Chrome had before;
|
||||
// the flag exists to make CPU rasterisation an explicit opt-in rather
|
||||
// than an implicit fallback for end users on the open web.
|
||||
return ["--use-gl=angle", "--use-angle=swiftshader", "--enable-unsafe-swiftshader"];
|
||||
}
|
||||
|
||||
if (mode === "auto") {
|
||||
// Should not reach here — `resolveBrowserGpuMode` collapses "auto" to
|
||||
// "software" or "hardware" before args are built. Be defensive: software
|
||||
// is the always-safe fallback.
|
||||
return ["--use-gl=angle", "--use-angle=swiftshader", "--enable-unsafe-swiftshader"];
|
||||
}
|
||||
|
||||
switch (platform) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
releaseBrowser,
|
||||
forceReleaseBrowser,
|
||||
buildChromeArgs,
|
||||
resolveBrowserGpuMode,
|
||||
resolveHeadlessShellPath,
|
||||
type CaptureMode,
|
||||
} from "./browserManager.js";
|
||||
@@ -115,9 +116,14 @@ export async function createCaptureSession(
|
||||
const forceScreenshot = config?.forceScreenshot ?? DEFAULT_CONFIG.forceScreenshot;
|
||||
const preMode: CaptureMode =
|
||||
headlessShell && isLinux && !forceScreenshot ? "beginframe" : "screenshot";
|
||||
const requestedGpuMode = config?.browserGpuMode ?? DEFAULT_CONFIG.browserGpuMode;
|
||||
const resolvedGpuMode = await resolveBrowserGpuMode(requestedGpuMode, {
|
||||
chromePath: headlessShell ?? undefined,
|
||||
browserTimeout: config?.browserTimeout,
|
||||
});
|
||||
const chromeArgs = buildChromeArgs(
|
||||
{ width: options.width, height: options.height, captureMode: preMode },
|
||||
config,
|
||||
{ ...config, browserGpuMode: resolvedGpuMode },
|
||||
);
|
||||
|
||||
const { browser, captureMode } = await acquireBrowser(chromeArgs, config);
|
||||
|
||||
Reference in New Issue
Block a user