diff --git a/docs/guides/rendering.mdx b/docs/guides/rendering.mdx index 9d429d7dd..7c935d203 100644 --- a/docs/guides/rendering.mdx +++ b/docs/guides/rendering.mdx @@ -65,7 +65,8 @@ Render your Hyperframes [compositions](/concepts/compositions) to MP4, MOV, or W **Pros:** - Fast startup, no container overhead - - Can use your system GPU for Chrome/WebGL capture by default + - Uses software Chrome/WebGL capture by default for stable frame output + - Can opt into your system GPU for Chrome/WebGL capture with `--browser-gpu` - Can use your system GPU for hardware-accelerated encoding (with `--gpu`) - Best for iterative development @@ -124,7 +125,7 @@ Render your Hyperframes [compositions](/concepts/compositions) to MP4, MOV, or W | `--workers` | 1-8 or `auto` | auto | Parallel render workers (see [Workers](#workers) below) | | `--max-concurrent-renders` | 1-10 | 2 | Max simultaneous renders via the producer server (see [Concurrent Renders](#concurrent-renders) below) | | `--gpu` | — | off | GPU encoding (NVENC, VideoToolbox, VAAPI, QSV) | -| `--browser-gpu` / `--no-browser-gpu` | — | on locally, off in Docker | Use or opt out of host GPU acceleration for local Chrome/WebGL capture | +| `--browser-gpu` / `--no-browser-gpu` | — | software | Use or opt out of host GPU acceleration for local Chrome/WebGL capture. Set `PRODUCER_BROWSER_GPU_MODE=auto` to probe hardware availability | | `--hdr` | — | off | Force HDR output even if no HDR sources are detected (MP4 only). See [HDR Rendering](/guides/hdr) | | `--sdr` | — | off | Force SDR output even if HDR sources are detected | | `--docker` | — | off | Use Docker for [deterministic rendering](/concepts/determinism) | diff --git a/docs/packages/cli.mdx b/docs/packages/cli.mdx index 943abfcc0..a6e5061a2 100644 --- a/docs/packages/cli.mdx +++ b/docs/packages/cli.mdx @@ -597,8 +597,8 @@ This is suppressed in CI environments, non-TTY shells, and when `HYPERFRAMES_NO_ # With options npx hyperframes render --output output.mp4 --fps 60 --quality high - # Opt out of local browser GPU capture - npx hyperframes render --no-browser-gpu --output cpu-browser.mp4 + # Opt in to local browser GPU capture + npx hyperframes render --browser-gpu --output gpu-browser.mp4 # Add hardware FFmpeg encoding npx hyperframes render --gpu --output gpu.mp4 @@ -617,7 +617,7 @@ This is suppressed in CI environments, non-TTY shells, and when `HYPERFRAMES_NO_ | `--sdr` | — | off | Force SDR output even if HDR sources are detected | | `--workers` | 1-8 | 4 | Parallel render workers | | `--gpu` | — | off | GPU encoding (NVENC, VideoToolbox, VAAPI, QSV) | - | `--browser-gpu` / `--no-browser-gpu` | — | on locally, off in Docker | Use or opt out of host GPU acceleration for local Chrome/WebGL capture | + | `--browser-gpu` / `--no-browser-gpu` | — | software | Use or opt out of host GPU acceleration for local Chrome/WebGL capture. Set `PRODUCER_BROWSER_GPU_MODE=auto` to probe hardware availability | | `--docker` | — | off | Use Docker for [deterministic rendering](/concepts/determinism) | | `--quiet` | — | off | Suppress verbose output | | `--variables` | JSON object | — | Variable overrides merged over `data-composition-variables` defaults. Read via `window.__hyperframes.getVariables()` | diff --git a/docs/packages/producer.mdx b/docs/packages/producer.mdx index 1886af13b..4f913aeef 100644 --- a/docs/packages/producer.mdx +++ b/docs/packages/producer.mdx @@ -226,7 +226,7 @@ When GPU encoding is enabled, Hyperframes detects the available FFmpeg hardware npx hyperframes doctor ``` -The CLI enables local Chrome/WebGL GPU capture automatically and supports `--no-browser-gpu` as an opt-out. When using the producer API directly, pass an engine config override: +The CLI uses software Chrome/WebGL capture by default for stable cross-platform frame output. Use `--browser-gpu` to opt into host GPU capture, or set `PRODUCER_BROWSER_GPU_MODE=auto` to probe hardware availability and fall back to software. When using the producer API directly, pass an engine config override: ```typescript import { resolveConfig } from '@hyperframes/producer'; diff --git a/packages/cli/src/commands/render.test.ts b/packages/cli/src/commands/render.test.ts index 57cc05425..2678b5c83 100644 --- a/packages/cli/src/commands/render.test.ts +++ b/packages/cli/src/commands/render.test.ts @@ -84,7 +84,7 @@ describe("renderLocal browser GPU config", () => { }); }, 15_000); - it("forwards browserGpuMode='auto' into producer config (probe-then-choose)", async () => { + it("forwards browserGpuMode='auto' into producer config when explicitly requested by a caller", async () => { await renderLocal("/tmp/project", "/tmp/out.mp4", { fps: { num: 30, den: 1 }, quality: "standard", @@ -102,7 +102,7 @@ describe("renderLocal browser GPU config", () => { }); }); - it("passes an explicit hardware override for default local browser GPU", async () => { + it("passes an explicit hardware override for --browser-gpu", async () => { await renderLocal("/tmp/project", "/tmp/out.mp4", { fps: { num: 30, den: 1 }, quality: "standard", @@ -121,8 +121,8 @@ describe("renderLocal browser GPU config", () => { }); it("resolves browser GPU from CLI flags, Docker mode, and env fallback", () => { - // Default (no flag, no env): auto — engine probes and chooses. - expect(resolveBrowserGpuForCli(false, undefined, undefined)).toBe("auto"); + // Default (no flag, no env): software for stable cross-platform captures. + expect(resolveBrowserGpuForCli(false, undefined, undefined)).toBe("software"); // Env override expect(resolveBrowserGpuForCli(false, undefined, "hardware")).toBe("hardware"); expect(resolveBrowserGpuForCli(false, undefined, "software")).toBe("software"); diff --git a/packages/cli/src/commands/render.ts b/packages/cli/src/commands/render.ts index 183d5a4fd..ea90b10b4 100644 --- a/packages/cli/src/commands/render.ts +++ b/packages/cli/src/commands/render.ts @@ -18,7 +18,7 @@ export const examples: Example[] = [ ["High quality at 60fps", "hyperframes render --fps 60 --quality high --output hd.mp4"], ["Deterministic render via Docker", "hyperframes render --docker --output deterministic.mp4"], ["Parallel rendering with 6 workers", "hyperframes render --workers 6 --output fast.mp4"], - ["Opt out of browser GPU render", "hyperframes render --no-browser-gpu --output cpu.mp4"], + ["Opt in to browser GPU render", "hyperframes render --browser-gpu --output gpu-browser.mp4"], ["HDR output (auto-detected)", "hyperframes render --output hdr-output.mp4"], [ "Override composition variables (parametrized render)", @@ -180,7 +180,7 @@ export default defineCommand({ "browser-gpu": { type: "boolean", description: - "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).", + "Force host GPU acceleration for Chrome/WebGL capture. Default: software (SwiftShader) for stable cross-platform frame capture. Set PRODUCER_BROWSER_GPU_MODE=auto to probe hardware availability.", }, quiet: { type: "boolean", @@ -570,8 +570,8 @@ interface RenderOptions { gpu: boolean; /** * Chrome WebGL backend mode. "auto" probes on first launch and falls back - * to "software" if no usable GPU. Defaults to "software" when omitted to - * stay backwards-compatible with callers that pre-date the tri-state. + * to "software" if no usable GPU. Defaults to "software" when omitted for + * deterministic local frame capture. */ browserGpuMode?: "auto" | "hardware" | "software"; hdrMode: "auto" | "force-hdr" | "force-sdr"; @@ -734,12 +734,13 @@ export function validateVariablesAgainstProject( * `--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. + * 4. Default = "software" — deterministic SwiftShader capture unless the + * user explicitly opts into host GPU rendering. * - * 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. + * Plain DOM renders can hit platform-specific GPU compositor bugs on local + * Chrome, especially on older Apple Silicon. Keep the default aligned with + * the engine's conservative software mode, while preserving explicit hardware + * and env-driven auto probing for power users. */ export function resolveBrowserGpuForCli( useDocker: boolean, @@ -750,7 +751,7 @@ export function resolveBrowserGpuForCli( if (browserGpuArg === true) return "hardware"; if (browserGpuArg === false) return "software"; if (envMode === "hardware" || envMode === "software" || envMode === "auto") return envMode; - return "auto"; + return "software"; } const DOCKER_IMAGE_PREFIX = "hyperframes-renderer"; diff --git a/packages/cli/src/docs/rendering.md b/packages/cli/src/docs/rendering.md index c2b49c466..00c769ee2 100644 --- a/packages/cli/src/docs/rendering.md +++ b/packages/cli/src/docs/rendering.md @@ -20,13 +20,13 @@ Requires: Docker installed and running. - `--crf` — Override encoder CRF (mutually exclusive with `--video-bitrate`) - `--video-bitrate` — Target video bitrate such as `10M` (mutually exclusive with `--crf`) - `--gpu` — Use GPU encoding (NVENC, VideoToolbox, VAAPI, QSV) -- `--browser-gpu` / `--no-browser-gpu` — Force host GPU or software (SwiftShader) for Chrome/WebGL capture. Default for local renders is `auto` — probe WebGL availability on first launch and fall back to software if no GPU is reachable. Docker mode always uses software. +- `--browser-gpu` / `--no-browser-gpu` — Force host GPU or software (SwiftShader) for Chrome/WebGL capture. Local renders default to software for stable cross-platform capture. Set `PRODUCER_BROWSER_GPU_MODE=auto` to probe hardware availability. Docker mode always uses software. - `-o, --output` — Custom output path ## Tips - Use `draft` quality for fast previews during development -- Local renders auto-detect GPU on first launch; use `--browser-gpu` to force hardware (errors if no GPU) or `--no-browser-gpu` to force SwiftShader +- Local renders use software Chrome capture by default; use `--browser-gpu` to force hardware (errors if no GPU) or `PRODUCER_BROWSER_GPU_MODE=auto` to probe and fall back to SwiftShader - Use `--gpu` when a local render also benefits from hardware FFmpeg encoding - Use `npx hyperframes benchmark` to find optimal settings - 4 workers is usually the sweet spot for most compositions