Merge pull request #2300 from heygen-com/07-12-fix-software-video-beyond-viewport

fix(producer): stop clipping the bottom edge of software-GPU video renders
This commit is contained in:
Vance Ingalls
2026-07-12 11:50:56 -07:00
committed by GitHub
4 changed files with 27 additions and 23 deletions
@@ -483,7 +483,6 @@ export async function renderChunk(
const videoCaptureBeyondViewport = resolveVideoCaptureBeyondViewport(
planVideos?.videos.length ?? 0,
"software",
);
// ── Per-chunk work + frames directories ──
@@ -3,15 +3,14 @@ import { resolveVideoCaptureBeyondViewport } from "./captureBeyondViewport.js";
describe("resolveVideoCaptureBeyondViewport", () => {
it("leaves no-video renders on the engine default", () => {
expect(resolveVideoCaptureBeyondViewport(0, "software")).toBeUndefined();
expect(resolveVideoCaptureBeyondViewport(0, "hardware")).toBeUndefined();
expect(resolveVideoCaptureBeyondViewport(0)).toBeUndefined();
});
it("keeps video renders on the fast viewport-bound path under software rendering", () => {
expect(resolveVideoCaptureBeyondViewport(1, "software")).toBe(false);
});
it("preserves the beyond-viewport video workaround under hardware rendering", () => {
expect(resolveVideoCaptureBeyondViewport(1, "hardware")).toBe(true);
it("forces beyond-viewport for any video render so the bottom edge is not clipped", () => {
// Regression: software hosts (including every distributed chunk render,
// which resolves as "software") previously returned false here and clipped
// ~87 bottom rows to black on video comps.
expect(resolveVideoCaptureBeyondViewport(1)).toBe(true);
expect(resolveVideoCaptureBeyondViewport(5)).toBe(true);
});
});
@@ -1,14 +1,23 @@
export type ResolvedBrowserGpuMode = "software" | "hardware";
/**
* Native video surfaces can need Chrome's beyond-viewport compositor on
* hardware-accelerated captures, but that path is a full-surface software
* re-rasterization tax on SwiftShader/CPU render hosts.
* Native video surfaces need Chrome's beyond-viewport screenshot path or the
* viewport-bound capture clips the bottom edge of the frame — the same
* #1094 tall-portrait guard the alpha capture paths already hardcode
* (`captureScreenshotWithAlpha` / `captureAlphaPng`).
*
* This used to be gated to hardware-GPU captures to skip the full-surface
* software re-rasterization tax on SwiftShader/CPU render hosts. But that left
* software hosts clipping ~87 bottom rows to black on video comps — and every
* distributed chunk render resolves as "software", so the entire distributed
* fleet shipped video renders with a black bottom band. Correct output wins
* over the software perf optimization: enable beyond-viewport for any render
* that has a native video surface, regardless of GPU mode.
*
* ponytail: blanket-on for video. If the software re-raster tax proves
* significant on the distributed fleet, narrow it back to comps whose content
* actually reaches the bottom edge — but that needs a reliable clip predictor
* first, and a black band is unshippable in the meantime.
*/
export function resolveVideoCaptureBeyondViewport(
videoCount: number,
browserGpuMode: ResolvedBrowserGpuMode,
): boolean | undefined {
export function resolveVideoCaptureBeyondViewport(videoCount: number): boolean | undefined {
if (videoCount <= 0) return undefined;
return browserGpuMode === "hardware";
return true;
}
@@ -1899,10 +1899,7 @@ export async function executeRenderJob(
browserTimeout: cfg.browserTimeout,
});
updateCaptureObservability({ browserGpuMode: resolvedBrowserGpuMode });
const videoCaptureBeyondViewport = resolveVideoCaptureBeyondViewport(
composition.videos.length,
resolvedBrowserGpuMode,
);
const videoCaptureBeyondViewport = resolveVideoCaptureBeyondViewport(composition.videos.length);
const captureOptions: CaptureOptions = {
width,