fix(producer): render telemetry reports beyond-viewport capture as the workers used it

This commit is contained in:
Miguel Ángel
2026-10-28 20:34:45 -04:00
parent cfa653af4d
commit 276e3cf27e
6 changed files with 57 additions and 9 deletions
@@ -210,3 +210,15 @@ describe("discardWarmupCapture", () => {
}
});
});
describe("getCapturePerfSummary", () => {
it("reports the beyond-viewport value the session captured with", () => {
const session = makeFakeSession();
try {
session.options.captureBeyondViewport = false;
expect(getCapturePerfSummary(session).captureBeyondViewport).toBe(false);
} finally {
rmSync(session.outputDir, { recursive: true, force: true });
}
});
});
@@ -5557,6 +5557,7 @@ export function getCapturePerfSummary(session: CaptureSession): CapturePerfSumma
avgSeekMs: Math.round(session.capturePerf.seekMs / frames),
avgBeforeCaptureMs: Math.round(session.capturePerf.beforeCaptureMs / frames),
avgScreenshotMs: Math.round(session.capturePerf.screenshotMs / frames),
captureBeyondViewport: session.options.captureBeyondViewport,
p50TotalMs: medianOf(session.capturePerf.frameMs),
p95TotalMs: percentileOf(session.capturePerf.frameMs, 0.95),
p99TotalMs: percentileOf(session.capturePerf.frameMs, 0.99),
+2
View File
@@ -272,6 +272,8 @@ export interface CapturePerfSummary {
avgSeekMs: number;
avgBeforeCaptureMs: number;
avgScreenshotMs: number;
/** Whether the session captured beyond the viewport, after init turned the request off for a page that fits. */
captureBeyondViewport?: boolean;
/**
* Median per-frame capture time — warmup-robust, unlike `avgTotalMs`
* (first frames pay font/image decode + GC that swamps short renders'
@@ -1,5 +1,6 @@
import { describe, expect, it } from "vitest";
import { buildRenderPerfSummary, roundDb } from "./perfSummary.js";
import { buildRenderPerfSummary, capturedBeyondViewport, roundDb } from "./perfSummary.js";
import type { CapturePerfSummary } from "@hyperframes/engine";
import type { RenderJob } from "../renderOrchestrator.js";
describe("roundDb", () => {
@@ -75,3 +76,21 @@ describe("buildRenderPerfSummary drawElement gate reason", () => {
expect(summary.drawElement?.gateReason).toBe("css_effect:filter");
});
});
describe("capturedBeyondViewport", () => {
const perf = (captureBeyondViewport?: boolean) =>
({ captureBeyondViewport }) as CapturePerfSummary;
it("reports off when every worker's session turned the requested capture off", () => {
expect(capturedBeyondViewport([perf(false), perf(false)], true)).toBe(false);
});
it("reports on when any worker kept it", () => {
expect(capturedBeyondViewport([perf(false), perf(true)], false)).toBe(true);
});
it("keeps the stage's value when no worker reported one", () => {
expect(capturedBeyondViewport([perf()], true)).toBe(true);
expect(capturedBeyondViewport([], undefined)).toBeUndefined();
});
});
@@ -54,6 +54,15 @@ export function pushWorkerDedupPerfs(
}
}
/** Beyond-viewport capture as the workers' sessions used it: on if any kept it, `fallback` when none reported. */
export function capturedBeyondViewport(
perfs: ReadonlyArray<CapturePerfSummary>,
fallback: boolean | undefined,
): boolean | undefined {
const used = perfs.flatMap((perf) => perf.captureBeyondViewport ?? []);
return used.length > 0 ? used.includes(true) : fallback;
}
/** Adds each worker's page-error console lines to `sink`, once per distinct line. */
export function pushWorkerPageErrors(
results: ReadonlyArray<{ pageErrorLines?: string[] }>,
@@ -177,6 +177,7 @@ import { formatCaptureFrameName } from "../utils/paths.js";
import { findRenderHdrAutoPromotionTrigger, resolveEffectiveHdrMode } from "./render/hdrMode.js";
import {
buildRenderPerfSummary,
capturedBeyondViewport,
pushWorkerDedupPerfs,
pushWorkerPageErrors,
roundDb,
@@ -5150,10 +5151,12 @@ async function executeRenderPipeline(input: {
deDrainStats = streamingRes.deDrainStats;
workerCount = streamingRes.workerCount;
updateCaptureObservability({ workerCount });
if (streamingRes.captureBeyondViewport !== undefined) {
updateCaptureObservability({
captureBeyondViewport: streamingRes.captureBeyondViewport,
});
const usedBeyondViewport = capturedBeyondViewport(
dedupPerfs,
streamingRes.captureBeyondViewport,
);
if (usedBeyondViewport !== undefined) {
updateCaptureObservability({ captureBeyondViewport: usedBeyondViewport });
}
probeSession = streamingRes.probeSession;
lastBrowserConsole = streamingRes.lastBrowserConsole;
@@ -5337,10 +5340,12 @@ async function executeRenderPipeline(input: {
const captureFrameMs = Date.now() - captureFrameStart;
workerCount = captureRes.workerCount;
updateCaptureObservability({ workerCount });
if (captureRes.captureBeyondViewport !== undefined) {
updateCaptureObservability({
captureBeyondViewport: captureRes.captureBeyondViewport,
});
const usedBeyondViewport = capturedBeyondViewport(
dedupPerfs,
captureRes.captureBeyondViewport,
);
if (usedBeyondViewport !== undefined) {
updateCaptureObservability({ captureBeyondViewport: usedBeyondViewport });
}
probeSession = captureRes.probeSession;
lastBrowserConsole = captureRes.lastBrowserConsole;