From 86cdec94927ad7e50d9c6a586d0efa49386b6885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Sat, 13 Jun 2026 15:46:39 -0400 Subject: [PATCH 1/2] refactor(producer): remove typecasts and deduplicate HDR capture patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Extract seekInjectAndQueryStacking() and seekAndInject() helpers to deduplicate the seek+inject+query pattern across sequential loop, hybrid loop, and per-scene transition capture (3 call sites → 1 helper) - Fix sceneBuf as Buffer casts by properly typing the scene-capture arrays as [Buffer, Set][] instead of using as const + cast - Replace as NonNullable<> cast on outputFormat with as const fallback - Add explanatory comments on inherent linkedom DOM casts --- .../render/stages/captureHdrFrameShared.ts | 97 ++++++++++++++----- .../render/stages/captureHdrHybridLoop.ts | 24 ++--- .../render/stages/captureHdrSequentialLoop.ts | 31 +++--- .../src/services/renderOrchestrator.ts | 9 +- 4 files changed, 106 insertions(+), 55 deletions(-) diff --git a/packages/producer/src/services/render/stages/captureHdrFrameShared.ts b/packages/producer/src/services/render/stages/captureHdrFrameShared.ts index a6c0458677..484c11f286 100644 --- a/packages/producer/src/services/render/stages/captureHdrFrameShared.ts +++ b/packages/producer/src/services/render/stages/captureHdrFrameShared.ts @@ -11,6 +11,7 @@ import { rmSync } from "node:fs"; import { + type BeforeCaptureHook, type CaptureSession, type ElementStackingInfo, applyDomLayerMask, @@ -29,7 +30,12 @@ import { blitHdrVideoLayer, closeHdrVideoFrameSource, } from "../../hdrCompositor.js"; -import { type HdrPerfCollector, timeHdrPhase, timeHdrPhaseAsync } from "../hdrPerf.js"; +import { + type HdrPerfCollector, + type HdrPerfTimingKey, + timeHdrPhase, + timeHdrPhaseAsync, +} from "../hdrPerf.js"; // ─── Hybrid path gating + partitioning ───────────────────────────────────── @@ -101,6 +107,53 @@ export interface LayeredTransitionBuffers { output: Buffer; } +// ─── Seek + inject + stacking query (shared across all three loop paths) ── + +/** + * Seek the page to `time` and run the optional before-capture hook. + * Used by `captureSceneIntoBuffer` which receives stacking info from + * its caller, and by `seekInjectAndQueryStacking` which appends a + * stacking query. + */ +async function seekAndInject( + page: CaptureSession["page"], + time: number, + beforeCaptureHook: BeforeCaptureHook | null, + hdrPerf: HdrPerfCollector | undefined, + seekKey: HdrPerfTimingKey, + injectKey: HdrPerfTimingKey, +): Promise { + await timeHdrPhaseAsync(hdrPerf, seekKey, () => + page.evaluate((t: number) => { + if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t); + }, time), + ); + if (beforeCaptureHook) { + await timeHdrPhaseAsync(hdrPerf, injectKey, () => beforeCaptureHook(page, time)); + } +} + +/** + * Seek the page to `time`, run the optional before-capture hook, then + * query element stacking order. Each phase is individually timed via the + * caller-provided perf keys so the sequential loop, hybrid worker, and + * per-scene transition capture each emit the correct telemetry label + * (`frameSeekMs` vs. `domLayerSeekMs`, etc.). + */ +export async function seekInjectAndQueryStacking( + page: CaptureSession["page"], + time: number, + beforeCaptureHook: BeforeCaptureHook | null, + nativeHdrIds: Set, + hdrPerf: HdrPerfCollector | undefined, + seekKey: HdrPerfTimingKey, + injectKey: HdrPerfTimingKey, + stackingKey: HdrPerfTimingKey, +): Promise { + await seekAndInject(page, time, beforeCaptureHook, hdrPerf, seekKey, injectKey); + return timeHdrPhaseAsync(hdrPerf, stackingKey, () => queryElementStacking(page, nativeHdrIds)); +} + // ─── Per-scene capture (shared by sequential transition + hybrid worker) ── export interface CaptureSceneArgs { @@ -141,16 +194,14 @@ export async function captureSceneIntoBuffer(a: CaptureSceneArgs): Promise log, frameIdx, } = a; - await timeHdrPhaseAsync(hdrPerf, "domLayerSeekMs", () => - session.page.evaluate((t: number) => { - if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t); - }, time), + await seekAndInject( + session.page, + time, + beforeCaptureHook, + hdrPerf, + "domLayerSeekMs", + "domLayerInjectMs", ); - if (beforeCaptureHook) { - await timeHdrPhaseAsync(hdrPerf, "domLayerInjectMs", () => - beforeCaptureHook(session.page, time), - ); - } for (const el of stackingInfo) { if (!el.isHdr || !sceneIds.has(el.id)) continue; if (nativeHdrImageIds.has(el.id)) { @@ -256,28 +307,28 @@ export async function captureTransitionFrameOnWorker( hdrPerf.frames += 1; hdrPerf.transitionFrames += 1; } - await timeHdrPhaseAsync(hdrPerf, "frameSeekMs", () => - session.page.evaluate((t: number) => { - if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t); - }, time), - ); - if (beforeCaptureHook) { - await timeHdrPhaseAsync(hdrPerf, "frameInjectMs", () => beforeCaptureHook(session.page, time)); - } - const stackingInfo = await timeHdrPhaseAsync(hdrPerf, "stackingQueryMs", () => - queryElementStacking(session.page, nativeHdrIds), + const stackingInfo = await seekInjectAndQueryStacking( + session.page, + time, + beforeCaptureHook, + nativeHdrIds, + hdrPerf, + "frameSeekMs", + "frameInjectMs", + "stackingQueryMs", ); const sceneAIds = new Set(sceneElements[transition.fromScene] ?? []); const sceneBIds = new Set(sceneElements[transition.toScene] ?? []); buffers.bufferA.fill(0); buffers.bufferB.fill(0); - for (const [sceneBuf, sceneIds] of [ + const sceneCaptures: [Buffer, Set][] = [ [buffers.bufferA, sceneAIds], [buffers.bufferB, sceneBIds], - ] as const) { + ]; + for (const [sceneBuf, sceneIds] of sceneCaptures) { await captureSceneIntoBuffer({ session, - sceneBuf: sceneBuf as Buffer, + sceneBuf, sceneIds, stackingInfo, time, diff --git a/packages/producer/src/services/render/stages/captureHdrHybridLoop.ts b/packages/producer/src/services/render/stages/captureHdrHybridLoop.ts index 5479cd2356..90e056cb32 100644 --- a/packages/producer/src/services/render/stages/captureHdrHybridLoop.ts +++ b/packages/producer/src/services/render/stages/captureHdrHybridLoop.ts @@ -32,7 +32,6 @@ import { crossfade, initTransparentBackground, initializeSession, - queryElementStacking, } from "@hyperframes/engine"; import type { FileServerHandle } from "../../fileServer.js"; import type { ProducerLogger } from "../../../logger.js"; @@ -54,6 +53,7 @@ import { distributeLayeredHybridFrameRanges, ensureFrameWritten, partitionTransitionFrames, + seekInjectAndQueryStacking, } from "./captureHdrFrameShared.js"; import { updateJobStatus } from "../shared.js"; @@ -305,19 +305,15 @@ export async function runHybridLayeredFrameLoop(input: HybridLoopInput): Promise throw err instanceof Error ? err : new Error(String(err)); }); } else { - const beforeCaptureHook = session.onBeforeCapture; - await timeHdrPhaseAsync(hdrPerf, "frameSeekMs", () => - session.page.evaluate((t: number) => { - if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t); - }, time), - ); - if (beforeCaptureHook) { - await timeHdrPhaseAsync(hdrPerf, "frameInjectMs", () => - beforeCaptureHook(session.page, time), - ); - } - const stackingInfo = await timeHdrPhaseAsync(hdrPerf, "stackingQueryMs", () => - queryElementStacking(session.page, nativeHdrIds), + const stackingInfo = await seekInjectAndQueryStacking( + session.page, + time, + session.onBeforeCapture, + nativeHdrIds, + hdrPerf, + "frameSeekMs", + "frameInjectMs", + "stackingQueryMs", ); canvas.fill(0); // Rebind ctx to this worker's session for per-layer captures diff --git a/packages/producer/src/services/render/stages/captureHdrSequentialLoop.ts b/packages/producer/src/services/render/stages/captureHdrSequentialLoop.ts index 11141c2051..5928378378 100644 --- a/packages/producer/src/services/render/stages/captureHdrSequentialLoop.ts +++ b/packages/producer/src/services/render/stages/captureHdrSequentialLoop.ts @@ -18,7 +18,6 @@ import { type TransitionFn, TRANSITIONS, crossfade, - queryElementStacking, } from "@hyperframes/engine"; import type { ProducerLogger } from "../../../logger.js"; import { @@ -39,6 +38,7 @@ import { cleanupEndedHdrVideos, ensureFrameWritten, type LayeredTransitionBuffers, + seekInjectAndQueryStacking, } from "./captureHdrFrameShared.js"; import { updateJobStatus } from "../shared.js"; @@ -109,19 +109,15 @@ export async function runSequentialLayeredFrameLoop(input: SequentialLoopInput): const time = (i * job.config.fps.den) / job.config.fps.num; if (hdrPerf) hdrPerf.frames += 1; - await timeHdrPhaseAsync(hdrPerf, "frameSeekMs", () => - domSession.page.evaluate((t: number) => { - if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t); - }, time), - ); - - if (beforeCaptureHook) { - await timeHdrPhaseAsync(hdrPerf, "frameInjectMs", () => - beforeCaptureHook(domSession.page, time), - ); - } - const stackingInfo = await timeHdrPhaseAsync(hdrPerf, "stackingQueryMs", () => - queryElementStacking(domSession.page, nativeHdrIds), + const stackingInfo = await seekInjectAndQueryStacking( + domSession.page, + time, + beforeCaptureHook, + nativeHdrIds, + hdrPerf, + "frameSeekMs", + "frameInjectMs", + "stackingQueryMs", ); const activeTransition = transitionRanges.find((t) => i >= t.startFrame && i <= t.endFrame); @@ -151,14 +147,15 @@ export async function runSequentialLayeredFrameLoop(input: SequentialLoopInput): transitionBuffers.bufferB.fill(0); }); - for (const [sceneBuf, sceneIds] of [ + const sceneCaptures: [Buffer, Set][] = [ [transitionBuffers.bufferA, sceneAIds], [transitionBuffers.bufferB, sceneBIds], - ] as const) { + ]; + for (const [sceneBuf, sceneIds] of sceneCaptures) { assertNotAborted(); await captureSceneIntoBuffer({ session: domSession, - sceneBuf: sceneBuf as Buffer, + sceneBuf, sceneIds, stackingInfo, time, diff --git a/packages/producer/src/services/renderOrchestrator.ts b/packages/producer/src/services/renderOrchestrator.ts index e6b99b10f6..984d4c532f 100644 --- a/packages/producer/src/services/renderOrchestrator.ts +++ b/packages/producer/src/services/renderOrchestrator.ts @@ -696,6 +696,8 @@ function normalizeCompositionSrcPath(srcPath: string): string { } function createStandaloneEntryRenderClone(root: Element, host: Element): Element { + // linkedom's cloneNode returns `any` (not `Node`), so the Element cast + // is needed to access setAttribute/appendChild without losing type safety. const hostClone = host.cloneNode(true) as Element; hostClone.setAttribute("data-start", "0"); @@ -741,6 +743,9 @@ export function extractStandaloneEntryFromIndex( const body = document.querySelector("body"); if (!body) return null; + // linkedom's querySelectorAll returns `any` on Document and `NodeList` on + // the ParentNode mixin. Neither types the elements as `Element`, so the + // cast is required to call getAttribute / hasAttribute without `any`. const hosts = Array.from(document.querySelectorAll("[data-composition-src]")) as Element[]; const host = hosts.find( (candidate) => @@ -749,6 +754,8 @@ export function extractStandaloneEntryFromIndex( ); if (!host) return null; + // linkedom's `children` is typed as `NodeList` (not `HTMLCollection`), + // so the Element[] cast is needed. const root = (Array.from(body.children) as Element[]).find((candidate) => candidate.hasAttribute("data-composition-id"), @@ -807,7 +814,7 @@ export async function executeRenderJob( log, renderJobId: job.id, }); - const outputFormat = (job.config.format ?? "mp4") as NonNullable; + const outputFormat = job.config.format ?? ("mp4" as const); const isWebm = outputFormat === "webm"; const isMov = outputFormat === "mov"; const isPngSequence = outputFormat === "png-sequence"; From 0e98e9e15185b00e1a52d0f038e364e05d094745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Sat, 13 Jun 2026 15:57:51 -0400 Subject: [PATCH 2/2] refactor(producer): name constants, type matrix, extract opacity helper - Replace magic 0.001/0.999 with TRANSFORM_IDENTITY_EPSILON and OPAQUE_ALPHA_THRESHOLD; replace BPP=6 with RGB48_BYTES_PER_PIXEL - Add AffineMatrix tuple type + isAffineMatrix guard, eliminating all 4 non-null assertions on matrix indices - Extract resolveBlitOpacity() to replace 5 identical ternaries - Narrow fallow-ignore-file to line-level complexity suppressions --- .../producer/src/services/hdrCompositor.ts | 78 ++++++++++--------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/packages/producer/src/services/hdrCompositor.ts b/packages/producer/src/services/hdrCompositor.ts index 4659dde380..f188bd8cd5 100644 --- a/packages/producer/src/services/hdrCompositor.ts +++ b/packages/producer/src/services/hdrCompositor.ts @@ -1,4 +1,3 @@ -// fallow-ignore-file complexity code-duplication /** * HDR Compositor — pixel-level compositing primitives for the HDR * layered render path. @@ -66,6 +65,22 @@ function countNonZeroRgb48(buf: Uint8Array): number { return n; } +// ─── Constants ──────────────────────────────────────────────────────────── + +const TRANSFORM_IDENTITY_EPSILON = 0.001; +const OPAQUE_ALPHA_THRESHOLD = 0.999; +const RGB48_BYTES_PER_PIXEL = 6; + +type AffineMatrix = [number, number, number, number, number, number]; + +function isAffineMatrix(m: number[]): m is AffineMatrix { + return m.length === 6; +} + +function resolveBlitOpacity(opacity: number): number | undefined { + return opacity < OPAQUE_ALPHA_THRESHOLD ? opacity : undefined; +} + // ─── Types ───────────────────────────────────────────────────────────────── /** @@ -96,14 +111,13 @@ function cropRgb48le( cropW: number, cropH: number, ): Buffer { - const BPP = 6; - const dst = Buffer.alloc(cropW * cropH * BPP); + const dst = Buffer.alloc(cropW * cropH * RGB48_BYTES_PER_PIXEL); for (let row = 0; row < cropH; row++) { const srcRow = cropY + row; if (srcRow < 0 || srcRow >= srcH) continue; - const srcOff = (srcRow * srcW + cropX) * BPP; - const dstOff = row * cropW * BPP; - const copyLen = Math.min(cropW, srcW - cropX) * BPP; + const srcOff = (srcRow * srcW + cropX) * RGB48_BYTES_PER_PIXEL; + const dstOff = row * cropW * RGB48_BYTES_PER_PIXEL; + const copyLen = Math.min(cropW, srcW - cropX) * RGB48_BYTES_PER_PIXEL; if (copyLen > 0) src.copy(dst, dstOff, srcOff, srcOff + copyLen); } return dst; @@ -138,6 +152,7 @@ export function closeHdrVideoFrameSource(source: HdrVideoFrameSource, log?: Prod } } +// fallow-ignore-next-line complexity export function blitHdrVideoLayer( canvas: Buffer, el: ElementStackingInfo, @@ -184,18 +199,13 @@ export function blitHdrVideoLayer( ); } - const viewportMatrix = parseTransformMatrix(el.transform); + const rawMatrix = parseTransformMatrix(el.transform); + const matrix = rawMatrix && isAffineMatrix(rawMatrix) ? rawMatrix : null; - // Pass border-radius for rounded-corner masking (only when non-zero) const br = el.borderRadius; const hasBorderRadius = br[0] > 0 || br[1] > 0 || br[2] > 0 || br[3] > 0; const borderRadiusParam = hasBorderRadius ? br : undefined; - // Apply ancestor overflow:hidden clip rect by constraining the blit - // bounds. For the no-transform (region) path, we crop the source - // image and adjust the destination position. For the affine path, - // clip rect support is not yet implemented (would require per-pixel - // scissor in the affine blit); log a warning and skip clipping. let blitX = el.x; let blitY = el.y; let blitSrcX = 0; @@ -210,7 +220,7 @@ export function blitHdrVideoLayer( const cy1 = Math.max(blitY, cr.y); const cx2 = Math.min(blitX + blitW, cr.x + cr.width); const cy2 = Math.min(blitY + blitH, cr.y + cr.height); - if (cx2 <= cx1 || cy2 <= cy1) return; // fully clipped + if (cx2 <= cx1 || cy2 <= cy1) return; blitSrcX = cx1 - blitX; blitSrcY = cy1 - blitY; blitW = cx2 - cx1; @@ -220,23 +230,16 @@ export function blitHdrVideoLayer( clipped = true; } - // Detect translation-only matrix (no scale/rotation) — route through the - // region path which supports clip rects. Chrome reports a viewport matrix - // for all HDR elements, even untransformed ones or those with only layout - // translation (e.g. `left: 960px` → `matrix(1,0,0,1,960,0)`). The region - // blit handles translation via el.x/el.y, so we only need the affine path - // for actual scale/rotation transforms. - // parseTransformMatrix returns a 6-element array or null — length check unnecessary. const isTranslationOnly = !!( - viewportMatrix && - Math.abs(viewportMatrix[0]! - 1) < 0.001 && - Math.abs(viewportMatrix[1]!) < 0.001 && - Math.abs(viewportMatrix[2]!) < 0.001 && - Math.abs(viewportMatrix[3]! - 1) < 0.001 + matrix && + Math.abs(matrix[0] - 1) < TRANSFORM_IDENTITY_EPSILON && + Math.abs(matrix[1]) < TRANSFORM_IDENTITY_EPSILON && + Math.abs(matrix[2]) < TRANSFORM_IDENTITY_EPSILON && + Math.abs(matrix[3] - 1) < TRANSFORM_IDENTITY_EPSILON ); timeHdrPhase(hdrPerf, "hdrVideoBlitMs", () => { - if (viewportMatrix && !isTranslationOnly) { + if (matrix && !isTranslationOnly) { if (clipped && log) { log.debug( `HDR clip rect on affine-transformed element ${el.id} — clip not applied (affine scissor not yet supported)`, @@ -245,16 +248,15 @@ export function blitHdrVideoLayer( blitRgb48leAffine( canvas, hdrRgb, - viewportMatrix, + matrix, srcW, srcH, width, height, - el.opacity < 0.999 ? el.opacity : undefined, + resolveBlitOpacity(el.opacity), borderRadiusParam, ); } else if (clipped) { - // Crop the source buffer to the clipped region before blitting const croppedBuf = cropRgb48le(hdrRgb, srcW, srcH, blitSrcX, blitSrcY, blitW, blitH); blitRgb48leRegion( canvas, @@ -265,7 +267,7 @@ export function blitHdrVideoLayer( blitH, width, height, - el.opacity < 0.999 ? el.opacity : undefined, + resolveBlitOpacity(el.opacity), borderRadiusParam, ); } else { @@ -278,7 +280,7 @@ export function blitHdrVideoLayer( srcH, width, height, - el.opacity < 0.999 ? el.opacity : undefined, + resolveBlitOpacity(el.opacity), borderRadiusParam, ); } @@ -343,23 +345,24 @@ export function blitHdrImageLayer( : buf.data, ); - const viewportMatrix = parseTransformMatrix(el.transform); + const rawMatrix = parseTransformMatrix(el.transform); + const matrix = rawMatrix && isAffineMatrix(rawMatrix) ? rawMatrix : null; const br = el.borderRadius; const hasBorderRadius = br[0] > 0 || br[1] > 0 || br[2] > 0 || br[3] > 0; const borderRadiusParam = hasBorderRadius ? br : undefined; timeHdrPhase(hdrPerf, "hdrImageBlitMs", () => { - if (viewportMatrix) { + if (matrix) { blitRgb48leAffine( canvas, hdrRgb, - viewportMatrix, + matrix, buf.width, buf.height, width, height, - el.opacity < 0.999 ? el.opacity : undefined, + resolveBlitOpacity(el.opacity), borderRadiusParam, ); } else { @@ -372,7 +375,7 @@ export function blitHdrImageLayer( buf.height, width, height, - el.opacity < 0.999 ? el.opacity : undefined, + resolveBlitOpacity(el.opacity), borderRadiusParam, ); } @@ -462,6 +465,7 @@ export interface HdrCompositeContext { * dumps. Pass `-1` to disable per-layer dumps even * when `KEEP_TEMP=1` (e.g. for warmup frames). */ +// fallow-ignore-next-line complexity export async function compositeHdrFrame( ctx: HdrCompositeContext, canvas: Buffer,