fix(engine): pre-create __render_frame__ siblings in initializeSession#1853
Open
varo-yang wants to merge 1 commit into
Open
fix(engine): pre-create __render_frame__ siblings in initializeSession#1853varo-yang wants to merge 1 commit into
varo-yang wants to merge 1 commit into
Conversation
Under chrome-headless-shell + HeadlessExperimental.beginFrame deterministic mode, injectVideoFramesBatch's `isNewImage` branch creates the replacement <img> on the fly (createElement + insertBefore). The immediately-next beginFrame captures before the new layer lands in the compositor's tree, so that frame paints only body background + already-composed overlays. Pre-create the __render_frame__ sibling once at the end of initializeSession. By the time the first capture begins, warmup + GSAP flush + readiness polls have driven several BeginFrame ticks, so the layers are committed and every subsequent inject takes the `hasImg=true` path (just an img.src update).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hit a periodic single-frame near-black flash in chunk-lambda renders — every
chunk_frames / worker_countframes one frame comes out as body background + previously-composed overlays only. On a single-video 4-worker chunk that's every 60 frames;signalstatsreports YAVG ~22 with YMAX ~240. Single-process local renders don't see it because they don't run under BeginFrame control.It's the
isNewImagebranch ofinjectVideoFramesBatchatscreenshotService.ts:473. First inject for a givenvideoIdper session seeshasImg === falseand creates the replacement<img>on the fly —document.createElement("img") + insertBeforeat lines 482-487.captureFrameCorefiresbeginFrameCaptureimmediately after: no intermediate BeginFrame, and no paint flush either, since the flush atframeCapture.ts:1328is gated oncaptureMode !== "beginframe".Under
HeadlessExperimental.beginFramedeterministic mode, chrome's compositor doesn't include the freshly-inserted<img>layer in that immediately-next BeginFrame — it lands a tick later. So the captured PNG shows just what was already in there: body background + persistent overlays. That's the YAVG ~22 signature.Pre-creating the
__render_frame__sibling once at the end ofinitializeSession— right beforesession.isInitialized = truein both branches — fixes it. By the time the firstbeginFrameCapturefires, warmup + GSAP flush + readiness polls have driven several BeginFrame ticks, so the layers are already committed.injectVideoFramesBatchseeshasImg === trueevery time and just updatesimg.src. TheisNewImagebranch stays as a fallback.FWIW things we tried before landing here:
willChange/translateZpromotion hints on the freshly-inserted<img>, dataUri preload, awaitingimg.decode()on the pending frame, and a second BeginFrame at the sameframeTimeTicks. Chrome refuses two BeginFrames at the same tick — the CDP call just protocol-times-out at 60s. Only avoiding the on-the-fly DOM mutation resolved it.Tests cover: creates a hidden sibling for every
video[data-start]on first call, no-op for videos that already have one, leaves plain<video>(nodata-start) alone.