Skip to content

fix(engine): long films show progress during the effect check and never wait minutes on it - #6697

Merged
miguel-heygen merged 3 commits into
mainfrom
fix/effect-scan-budget
Oct 29, 2026
Merged

miguel-heygen merged 3 commits into
mainfrom
fix/effect-scan-budget

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Description

A long film exported from the Desktop app on a Mac can sit at "prepare" for many minutes with no progress, then end with zero frames written. One report: a 670 s, 1080p 60 fps SVG/canvas film with a blur, about 16 minutes at 4%, then "Streaming encoder exited before frame 0 was written". The app reports an export as stalled after 180 s with no progress line.

The cause is the per-frame effect scan from #6318. When a film has a blur, drop-shadow or backdrop-filter element and takes the single-worker macOS GPU path, scanCssEffectLiveFrames seeks every frame of the film in a scratch browser before frame 0 is captured, to find the frames the effect shows on. It prints nothing while it runs, and it stops early only once the effect covers more than 60% of the film. At 6 to 24 ms a seek, a long film spends minutes there.

Two changes:

The in-page chunk loop moved into scanChunk unchanged, and the scan takes an options object, so the scan loop stays within the complexity limits.

Before and after, on a real render

Old Mac (M4 Pro), hyperframes render --workers 1 --gpu --fps 60, a 1080p fixture with 2,000 animated SVG circles and one blurred card on screen for 10 s. 0.8.214 against this branch, one render at a time, each stopped after 300 s:

Film Build First frame Progress lines before it Capture Rate
300 s, 18,000 frames 0.8.214 107.8 s 0 per-frame, 602 frames by screenshot 28.4 fps
300 s this branch 106.4 s 75, one every 1.3 s per-frame, 602 frames by screenshot 28.0 fps
600 s, 36,000 frames 0.8.214 216.4 s 0 per-frame 25.4 fps
600 s this branch 4.2 s 1 whole film by screenshot (scan projected 211 s, over the budget) 19.1 fps

The 300 s film scans for 103 s as before, now with a line every chunk, and keeps the fast path. The 600 s film starts at once. Its capture is slower, so in a terminal, where nothing reports stalls, that film takes about 16% longer in total than on 0.8.214 (about 1,890 s against 1,630 s, projected from the rates). In the app, the same film is reported stalled after 180 s on 0.8.214. Scanning ahead of capture instead of before it would remove that trade-off, and is the follow-up.

Testing

fails without the fix: stops after one chunk when the whole scan would hold back the first frame too long and reports the frames scanned after each chunk in riskFrames.test.ts. On main the scan seeks all 40,219 frames and returns an empty set (expected Set{} to be null), and no progress is reported.

  • riskFrames.test.ts, riskFrameCapture.test.ts, threeDProjection-cssEffectRisk.test.ts: 31 passed; riskFrames.test.ts passed 3 runs in a row.
  • tsc --noEmit for the engine and producer packages: clean.

What I did NOT exercise

No export from the Desktop app itself. The app's stall watchdog resets on any stdout line its progress parser accepts, and 25% Checking effects, 240 of 36000 frames is one: a percent of 25 or less reads as Preparing. I checked that by reading the app's parser, not by running the app. The 120 s budget is my choice, accepted by the team: under the app's 180 s limit, and long enough for the 300 s fixture's 103 s scan.

@github-actions

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 271 (base branch 271), smooth 177 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 (0)

Unstable (1)

  • seqplainkeys-none-pct-r0-root-z100: tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo false, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, 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 08d10bf1

  • nit: renderOrchestrator.ts:3688. The "Checking effects" status passes no stageProgress, unlike the other 25% steps (start_browsers). That clears job.stageProgress during the scan, and renderMachineProgress writes no @hf-progress line for it. The human progress line still prints, so the desktop stall beat still fires.
Scope

I read riskFrames.ts, the only caller (cssEffectRiskFrames in frameCapture.ts), and the updateJobStatus → CLI → desktop followProgress path. Null still means whole-film screenshot capture. The 120 s budget is under desktop's STALL_REPORT_MS of 180 s. Fails without the fix: I confirmed it locally. With main's riskFrames.ts, the named test fails with expected Set{} to be null. At head, 19/19 tests in riskFrames + riskFrameCapture pass, and the engine tsc is clean. CI: every run for 08d10bf1 is green, and the required jobs passed or were skipped. The earlier heads f409f130 and 67cb1c6f were superseded mid-review; this approval covers 08d10bf1 only.

— Genesis reviewer

@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 28, 2026
@miguel-heygen miguel-heygen changed the title fix(engine): long films start capturing at once instead of after a full effect scan fix(engine): long films show progress during the effect check and never wait minutes on it Oct 28, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 28, 2026
@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 29, 2026
Merged via the queue into main with commit e1a8c7f Oct 29, 2026
170 checks passed
@miguel-heygen
miguel-heygen deleted the fix/effect-scan-budget branch October 29, 2026 10:56
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Nov 5, 2026
Ports: heygen-com#7165 (font preflight and system TTC names), heygen-com#7166 (font
shorthand var(), weight-suffixed names, linked font sheets), heygen-com#7168
(render lock and --wait, effect-scan/session watchdog without the heygen-com#7104
retry, font_face_local_only lint), heygen-com#6972, heygen-com#7183 (adapted), heygen-com#7184; docs
heygen-com#7132, heygen-com#7196; prerequisites heygen-com#6665, heygen-com#6697, heygen-com#6716 (cache-dir helper only).
Deferred heygen-com#7182, heygen-com#7176. Package size budgets follow upstream's heygen-com#7168
increments. Watermarks: commit 15b1de4, PR 7189; monitored 92.

Co-authored-by: Miguel Ángel <[email protected]>
Co-authored-by: WaterrrForever <[email protected]>
Co-authored-by: Ular Kimsanov <[email protected]>
Co-authored-by: James Russo <[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