feat: add browser GPU render mode#571
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
3688366 to
afbbd10
Compare
afbbd10 to
50406c9
Compare
vanceingalls
left a comment
There was a problem hiding this comment.
Staff re-review on the updated branch (50406c90):
-
P2:
--no-browser-gpudoes not reliably opt out when the environment enables browser GPU.In
packages/cli/src/commands/render.ts,producerConfigis only passed whenoptions.browserGpuis true:producerConfig: options.browserGpu ? producer.resolveConfig({ browserGpuMode: "hardware" }) : undefined,
With
PRODUCER_BROWSER_GPU_MODE=hardware hyperframes render --no-browser-gpu, the CLI setsoptions.browserGpu=false, but then passesproducerConfig: undefined, so the producer later reads the env var and still launches Chrome with hardware browser GPU. The documented opt-out should pass an explicit software override for the false path, and this needs a CLI regression test for env +--no-browser-gpu. -
P3: Some docs still describe browser GPU as opt-in even though this version makes it the local default.
docs/guides/rendering.mdxlabelshyperframes render --gpuas “Hardware FFmpeg encoder only,” but that command now also uses browser GPU unless--no-browser-gpuis present.packages/cli/src/docs/rendering.mdalso documents--browser-gpuwithout the new default/opt-out behavior.
I did a static re-review only; I did not run the test suite.
50406c9 to
77a1eee
Compare
|
@vanceingalls ready for re-review. I addressed both discussion items: |
vanceingalls
left a comment
There was a problem hiding this comment.
P1: Docker renders accidentally inherit browser GPU mode.
The outer CLI sets useBrowserGpu false for --docker, but renderDocker() drops that value when building the inner container command, and the Docker entrypoint runs hyperframes render "$@" without --docker. Inside the container, the new local default turns browser GPU back on, which becomes Linux EGL flags and undermines the deterministic software-GL Docker path.
Thread browserGpu through buildDockerRunArgs() and append --no-browser-gpu for Docker renders, or make CONTAINER=true default browser GPU off.
Open question: should PRODUCER_BROWSER_GPU_MODE=software be honored by the CLI default path? The engine reads it, but renderLocal() always passes an explicit override from the CLI boolean, so the env var cannot opt out unless the user also passes --no-browser-gpu.
77a1eee to
1f5a507
Compare
|
@vanceingalls fixed the Docker re-entry case too. Docker container args now always include |
Merge activity
|
Problem
HyperFrames already had
--gpu, but that flag only controlled FFmpeg hardware encoding. The browser capture path still forced Chrome/WebGL through SwiftShader software GL via--use-angle=swiftshader, so WebGL-heavy local renders could leave the biggest bottleneck on the CPU path.That made the existing flag naming easy to misread:
--gpusounded like it accelerated the whole render, but it did not change the browser frame-capture backend.What this fixes
--no-browser-gpuas the local opt-out for software Chrome/WebGL capture.--browser-gpuas an explicit local browser-GPU request.browserGpuMode: "software" | "hardware"to engine config, withPRODUCER_BROWSER_GPU_MODEenv support for lower-level producer users.--browser-gpu --dockerwith a clear error because Docker browser GPU passthrough is not cross-platform.--gpumeans FFmpeg encoder GPU and browser GPU means Chrome/WebGL capture GPU.Why two flags
There are two separate GPU surfaces in the render pipeline:
Browser GPU controls Chrome frame capture.
--no-browser-gpuwhen you want the software browser baseline.--gpucontrols FFmpeg video encoding.The controls stay independent because users may want:
hyperframes renderfor the fast local default with browser GPU capture.hyperframes render --no-browser-gpufor the software-browser local baseline.hyperframes render --gpufor browser GPU capture plus hardware FFmpeg encoding.hyperframes render --no-browser-gpu --gpufor software browser capture plus hardware FFmpeg encoding.hyperframes render --dockerfor deterministic browser capture.Why
--gpudoes not imply browser GPUKeeping
--gpuscoped to FFmpeg encoding avoids a semantic break and keeps the risk profile explicit:--gpualready means encoder acceleration. Expanding it to also change Chrome capture would silently alter behavior for users who only wanted hardware encoding.--browser-gpu --docker.--gpuwas slower and produced larger output for thisstandardH.264 run.If HyperFrames later wants a single umbrella acceleration control, it should be explicit, for example
--acceleration browser|encoder|allor--gpu=browser|encoder|all, rather than changing the meaning of the existing boolean--gpu.Root cause
buildChromeArgs()always injected--use-gl=angle --use-angle=swiftshader.disableGpuonly appended--disable-gpu; it did not provide a hardware-GPU mode. That made the public--gpuflag look broader than it was, because render capture stayed software-backed even when encoder GPU was requested.Verification
Local checks
bun installbun run build:hyperframes-runtimebun run --filter @hyperframes/engine test src/config.test.ts src/services/browserManager.test.tsbun run --filter @hyperframes/cli test src/utils/dockerRunArgs.test.ts src/commands/render.test.tsbun run --filter @hyperframes/cli typecheckbun run --filter @hyperframes/engine typecheckbun run --filter @hyperframes/producer typecheckcd packages/producer && bunx vitest run src/services/renderOrchestrator.test.tsbunx oxlint packages/cli/src/commands/render.ts packages/cli/src/commands/render.test.ts packages/cli/src/utils/dockerRunArgs.ts packages/cli/src/utils/dockerRunArgs.test.ts packages/engine/src/config.ts packages/engine/src/config.test.ts packages/engine/src/services/browserManager.ts packages/engine/src/services/browserManager.test.ts packages/producer/src/services/renderOrchestrator.test.tsbunx oxfmt --check ...on changed source/docs filesgit diff --checkbun packages/cli/src/cli.ts render --help | rg -n "browser-gpu|no-browser-gpu|GPU"bun packages/cli/src/cli.ts render packages/producer/tests/css-spinner-render-compat/src --output /tmp/hf-auto-browser-gpu-smoke.mp4 --workers 1 --quality draft --fps 24 --strictGPU: browser GPU (auto).bun packages/cli/src/cli.ts render packages/producer/tests/css-spinner-render-compat/src --no-browser-gpu --output /tmp/hf-software-browser-gpu-smoke.mp4 --workers 1 --quality draft --fps 24 --strictbun packages/cli/src/cli.ts render packages/producer/tests/css-spinner-render-compat/src --docker --browser-gpu --output /tmp/should-not-render.mp4Browser GPU is local-only.buildDockerRunArgs()regression coverage asserts Docker container args include--no-browser-gpu, preventing nested container renders from re-enabling browser GPU through the local CLI default.resolveBrowserGpuForCli()regression coverage assertsPRODUCER_BROWSER_GPU_MODE=softwareopts out when no CLI browser-GPU flag is supplied, while explicit--browser-gpu/--no-browser-gpustill win.ffmpeg -v error -i /tmp/hf-auto-browser-gpu-smoke.mp4 -f null -ffmpeg -v error -i /tmp/hf-software-browser-gpu-smoke.mp4 -f null -ffprobe -v error -show_entries format=duration:stream=codec_name,width,height,r_frame_rate -of json /tmp/hf-browser-gpu-smoke.mp4-> H.264, 1920x1080, 24fps, 5.0sApple presentation benchmark
Rendered
/Users/miguel07code/Downloads/apple-presentation.zipas supplied after extracting to/tmp/hf-apple-profile/apple-presentation.Fixed settings:
standardqualityvideotoolbox--no-browser-gpu--no-browser-gpu --gpu--gpuResult: browser GPU capture is the meaningful improvement for this WebGL/browser-capture-heavy presentation. VideoToolbox encoding was slower and produced larger files for this current
standardH.264 path, so--gpushould stay separate and opt-in.Why
--gpuplus browser GPU was slower than browser GPU alone: the combined run captured about 4.0s faster than browser GPU alone, but VideoToolbox encoding was about 8.0s slower than CPU x264 encoding, so the encode loss outweighed the capture gain.VideoToolbox flag check
I also isolated the encode stage against the already-captured Apple frames to check whether macOS GPU encoding only needed special flags.
ffmpeg -h encoder=h264_videotoolboxdoes not expose a CRF/CQ-style quality option like x264. It exposes bitrate-oriented and VideoToolbox-specific options such as-b:v,-realtime,-profile,-coder,-prio_speed,-power_efficient, and-allow_sw. That means our current-q:vmapping is not equivalent to x264 CRF and can produce very different bitrate/size behavior.Measured full-frame encode variants on this host:
-q:v 64 -allow_sw 1-allow_sw 1-b:v 500k -maxrate 750k -bufsize 1000k -profile high -coder cabac -realtime 1 -prio_speed 1 -power_efficient 0-b:v 1500k-b:v 500k -profile baseline -coder cavlc -realtime 1 -prio_speed 1 -power_efficient 0Conclusion: VideoToolbox can be made size/bitrate-predictable with explicit
--video-bitrate, but the tested speed-oriented flags did not make it faster than CPU x264 wall time for this render. That reinforces keeping--gpuencoder acceleration explicit and separate from browser GPU capture.Artifacts from the local benchmark:
/tmp/hf-apple-profile/results/cpu.mp4/tmp/hf-apple-profile/results/browser-gpu.mp4/tmp/hf-apple-profile/results/encoder-gpu.mp4/tmp/hf-apple-profile/results/full-gpu.mp4/tmp/hf-apple-profile/results/summary.jsonAll four benchmark MP4s completed
ffprobeand fullffmpeg -f nulldecode checks.Pixel comparison
Compared decoded MP4 output between software-browser and browser-GPU renders:
css-spinner-render-compatclean fixture:Interpretation: browser GPU output is not strict hash/pixel-identical to the software-browser path after lossy H.264 encode, but the measured deltas are visually tiny. Above 50 dB PSNR is typically visually indistinguishable for normal video review. Use
--no-browser-gpuor Docker when strict cross-run/cross-machine reproducibility matters more than local speed.Browser verification
packages/producer/tests/css-spinner-render-compat/src.agent-browserto openhttp://localhost:5191#project/srcand verify the composition loaded in Studio./tmp/hf-gpu-browser-proof/preview-loaded.png/tmp/hf-gpu-browser-proof/preview-playing.png/tmp/hf-gpu-browser-proof/preview-frame-60.png/tmp/hf-gpu-browser-proof/preview-playback.webm/tmp/hf-gpu-browser-proof/preview-seek.webmNotes
--no-browser-gpuis the opt-out for software Chrome/WebGL capture.--gpuremains encoder-only and opt-in.play()calls. The benchmark still compares the same supplied source across modes, but it should not be treated as a clean deterministic-composition fixture.