Skip to content

fix(engine): drawElement falls back on gpu-less linux and beginframe launches instead of hanging self-verify - #7324

Merged
vanceingalls merged 2 commits into
mainfrom
fix/gpuless-linux-verify-hang
Nov 6, 2026
Merged

vanceingalls merged 2 commits into
mainfrom
fix/gpuless-linux-verify-hang

Conversation

@vanceingalls

Copy link
Copy Markdown
Collaborator

What

drawElement fast capture now falls back cleanly in two cases where it used to hang the render for about 300 s:

  • A GPU-less Linux host with browserGpuMode: "hardware". detectGpuBackend read a null or throwing WebGL context as a real GPU, so drawElement engaged on a BeginFrame launch. It now falls back with deGateReason: "webgl_unavailable". Software renderers other than SwiftShader (llvmpipe, lavapipe, softpipe, Mesa offscreen, Microsoft Basic Render Driver) count as software, using the token list browserManager.ts already had.
  • Any BeginFrame launch. drawElement's self-verify reads its ground truth on a page with no BeginFrame driver, and that never finishes under --enable-begin-frame-control. Since Linux headless-shell launches BeginFrame, this hit Linux hosts with a GPU too. drawElement now doesn't engage there, with deGateReason: "beginframe_no_self_verify", rather than running without its self-verify.

Why

drawElement on software GL gives no speedup, so the engine is meant to fall back there (team notes, "SwiftShader drawElement speedup" in the graveyard). And config.ts treats self-verify as the safety net drawElement must not ship without, which #7255 extended to segmented and sequential capture. Both paths hung and failed before, so gating them loses nothing that worked.

Related work

How

  • drawElementService.ts: detectGpuBackend reports webglAvailable, and the shared SOFTWARE_WEBGL_RENDERER_TOKENS decide what counts as software.
  • frameCapture.ts: drawElementGate decides the fallback in order: backend first, then launch mode. HF_FORCE_DRAWELEMENT bypasses only the launch gate.
  • The gate reasons are added to the observability unions.

Test plan

  • Unit tests: a null or throwing WebGL context reads as unavailable. llvmpipe and Microsoft Basic Render Driver read as software. A real GPU and real SwiftShader are unchanged. A BeginFrame launch with a GPU backend gates. A screenshot launch still engages. Fails without the fix: the new drawElementService tests failed on the missing webglAvailable result, and the gate tests failed before drawElementGate existed.
  • On a GPU-less Linux devbox (Chrome headless-shell 153.0.8010.36, CI's build), a hardware render took 305 s before, stalling at self-verify. After: 1.9 s, webgl_unavailable, completed. With the GPU reading faked: 1.9 s, beginframe_no_self_verify.
  • windowedFrames.integration.test.ts: the drawElement case runs drawElement on macOS (8 samples armed, 1 recycle, on-demand folders seen, none left). On Linux it asserts the gated fallback. HF_TEST_REQUIRE_DRAWELEMENT=1 makes a fallback fail, and is meant for macOS and Windows GPU hosts.
  • Engine and producer typecheck, comment checks.

Not exercised

  • Linux with a real GPU: the BeginFrame gate is inferred from the launch path and the flag, not measured on one.
  • Which call stalls: whether it's Page.captureScreenshot itself, or the truth page's goto or pollHfReady, isn't confirmed. The gate sits before all of them.
  • Self-verify on macOS has no deadline. Under heavy machine load (load average about 90 from other Chromes), one macOS run hung 600 s at a session's self-verify. A retry at load 34 passed. That's a screenshot launch, which this PR doesn't change, so I'm flagging it as a follow-up.

🤖 Generated with Claude Code

vanceingalls and others added 2 commits November 6, 2026 07:45
…d of hanging self-verify

On a GPU-less Linux headless shell asked for browserGpuMode "hardware", Chrome has
GPU args and no GPU, so getContext("webgl") returns null. detectGpuBackend read that
as { isSwiftShader: false }, the SwiftShader gate let drawElement engage on a
BeginFrame-launched browser, and self-verify on its truth page then stalled until
the render failed after about 300 s, with no fallback.

drawElementGate now decides the init-time fallback, each with a named deGateReason:

- swiftshader: any software GL (SwiftShader, llvmpipe, lavapipe, softpipe, Microsoft
  Basic Render Driver), from one shared token list. Detection on real GPUs is unchanged.
- webgl_unavailable: no WebGL context could be created (null, or getContext throws).
- beginframe_no_self_verify: the browser was launched for BeginFrame. Its self-verify
  truth page has no BeginFrame driver, so drawElement frames could not be checked, and
  a Linux host with a GPU would stall the same way. HF_FORCE_DRAWELEMENT bypasses only
  this gate.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…and a recycle

Adds a drawElement case to the windowed-frames integration test: drawElement capture,
self-verify through the on-demand hook, and a browser recycle. It covers that hook
only on a macOS or Windows host with a GPU. Linux always gates drawElement away
(headless-shell launches for BeginFrame, which has no self-verify), and so does any
host with no GPU; there the case asserts the named gate fallback (swiftshader,
webgl_unavailable or beginframe_no_self_verify) and prints that drawElement did not
run. HF_TEST_REQUIRE_DRAWELEMENT=1 turns that fallback into a failure, for the hosts
that are meant to run it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 267 (base branch 267), smooth 108 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (8)

Unstable (2)

  • seqrepeat-none-px-r0-root-z100: tracking 0.05, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true / tracking 0.05, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true / tracking 0.05, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true
  • seqplainkeys-none-pct-r0-nested-z100: tracking 0.05, pressJump 0, drop 0, reload 0, render 0.02, renderKey -, undo false, teleport true / tracking 0.05, pressJump 0, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true / tracking 0.05, pressJump 0, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE · 0 blocker · 0 important · 1 nit · head 16de58e

CI is green on this head, Flake gate included. The Linux integration lane hit the new case and fell back as webgl_unavailable in 4.6 s.

Scope

Audited: drawElementService.ts, frameCapture.ts (gate site and captureDeVerificationFrames), browserManager.ts, both test files, and the observability/type comment unions.

Both init paths (screenshot :3135, BeginFrame :3290) go through the one gate, so no other site needs it. session.isSwiftShader gets written but nothing reads it, so the wider software-token meaning doesn't change any other decision. Linux drawElement only runs on explicit opt-in (config.ts clamp), so the BeginFrame gate is limited to opted-in hosts. Engine unit tests pass locally (drawElementService + frameCapture*, 379 passed). No prior reviews.

— Claude (Genesis V2)

const { backend, launchCaptureMode, forceDE } = args;
if (backend.isSwiftShader) return "swiftshader";
if (!backend.webglAvailable) return "webgl_unavailable";
if (launchCaptureMode === "beginframe" && !forceDE) return "beginframe_no_self_verify";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this also gates sessions that never open a truth page. captureDeVerificationFrames returns early for HF_DE_VERIFY=0 and for PNG/transparent output (frameCapture.ts:5603-5604), and also for comps under 10 frames. On an opted-in Linux GPU host, those ran drawElement before this change and now take BeginFrame. That's fine if it's intended ("no drawElement without self-verify"). But the PR body's "both paths hung and failed before, so gating them loses nothing that worked" doesn't hold for these sessions, so it's worth a word in the body.

@vanceingalls
vanceingalls merged commit 18fc75d into main Nov 6, 2026
91 checks passed
@vanceingalls
vanceingalls deleted the fix/gpuless-linux-verify-hang branch November 6, 2026 16:59
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Nov 6, 2026
…view to heygen-com#7265

Adopt heygen-com#7284 (Studio Storyboard tab and `doc: "storyboard"` Desktop handoff,
adapted to the fork's desktopApp without heygen-com#6897) and heygen-com#7324 (drawElement gate:
webgl_unavailable / beginframe_no_self_verify). PR axis heygen-com#7260-heygen-com#7265 carries
earlier verdicts; re-pin heygen-com#6695 and heygen-com#6816, resolve heygen-com#7150 as superseded by heygen-com#5746.
Watermarks: commit 18fc75d, PR heygen-com#7265, issue heygen-com#7223, monitored 99.

Co-authored-by: WaterrrForever <[email protected]>
Co-authored-by: Vance Ingalls <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant