diff --git a/packages/producer/src/services/render/perfSummary.test.ts b/packages/producer/src/services/render/perfSummary.test.ts new file mode 100644 index 000000000..257d00095 --- /dev/null +++ b/packages/producer/src/services/render/perfSummary.test.ts @@ -0,0 +1,45 @@ +import { describe, it, expect } from "vitest"; +import { buildRenderPerfSummary } from "./perfSummary.js"; +import type { RenderJob } from "../renderOrchestrator.js"; + +// ponytail: minimal stub — only the fields buildRenderPerfSummary reads are real. +const baseInput = { + job: { id: "r1", config: { fps: { num: 30, den: 1 }, quality: "high" } } as unknown as RenderJob, + workerCount: 4, + enableChunkedEncode: false, + chunkedEncodeSize: 0, + compositionDurationSeconds: 5, + totalFrames: 150, + outputWidth: 1920, + outputHeight: 1080, + videoCount: 0, + audioCount: 0, + totalElapsedMs: 1234, + perfStages: {}, + videoExtractBreakdown: undefined, + tmpPeakBytes: 0, + captureAttempts: [], + hdrDiagnostics: { videoExtractionFailures: 0, imageDecodeFailures: 0 }, + peakRssBytes: 0, + peakHeapUsedBytes: 0, +}; + +describe("buildRenderPerfSummary host telemetry", () => { + it("captures host facts and threads gpuDisabled through", () => { + const summary = buildRenderPerfSummary({ ...baseInput, gpuDisabled: true }); + expect(summary.host).toBeDefined(); + expect(summary.host?.gpuDisabled).toBe(true); + expect(summary.host?.cpuCount).toBeGreaterThan(0); + expect(summary.host?.totalMemMb).toBeGreaterThan(0); + expect(summary.host?.platform).toBe(process.platform); + expect(summary.host?.arch).toBe(process.arch); + expect(summary.host?.nodeVersion).toBe(process.version); + }); + + it("reflects gpuDisabled=false", () => { + const summary = buildRenderPerfSummary({ ...baseInput, gpuDisabled: false }); + expect(summary.host?.gpuDisabled).toBe(false); + // workers vs cores is the headline correlation — both must be present. + expect(summary.workers).toBe(4); + }); +}); diff --git a/packages/producer/src/services/render/perfSummary.ts b/packages/producer/src/services/render/perfSummary.ts index cce8e6971..c67b10c9b 100644 --- a/packages/producer/src/services/render/perfSummary.ts +++ b/packages/producer/src/services/render/perfSummary.ts @@ -3,6 +3,7 @@ * the `perf-summary.json` debug artifact. */ +import { arch, cpus, platform, totalmem } from "node:os"; import { fpsToNumber } from "@hyperframes/core"; import type { CaptureCalibrationSample, CaptureCostEstimate } from "./captureCost.js"; import type { @@ -39,6 +40,8 @@ export function buildRenderPerfSummary(input: { observability?: RenderObservabilitySummary; peakRssBytes: number; peakHeapUsedBytes: number; + /** `cfg.disableGpu` — the hard `--disable-gpu` flag, for the `host` GPU picture. */ + gpuDisabled: boolean; }): RenderPerfSummary { return { renderId: input.job.id, @@ -82,5 +85,13 @@ export function buildRenderPerfSummary(input: { : undefined, peakRssMb: Math.round(input.peakRssBytes / (1024 * 1024)), peakHeapUsedMb: Math.round(input.peakHeapUsedBytes / (1024 * 1024)), + host: { + platform: platform(), + arch: arch(), + cpuCount: cpus().length, + totalMemMb: Math.round(totalmem() / (1024 * 1024)), + nodeVersion: process.version, + gpuDisabled: input.gpuDisabled, + }, }; } diff --git a/packages/producer/src/services/renderOrchestrator.ts b/packages/producer/src/services/renderOrchestrator.ts index 99137f5d4..019a36b23 100644 --- a/packages/producer/src/services/renderOrchestrator.ts +++ b/packages/producer/src/services/renderOrchestrator.ts @@ -326,6 +326,27 @@ export interface RenderPerfSummary { peakHeapUsedMb?: number; hdrDiagnostics?: HdrDiagnostics; hdrPerf?: HdrPerfSummary; + /** + * Render-host facts, captured from the orchestrator process. Lets fleet-wide + * telemetry correlate render performance with the machine it ran on — most + * importantly `cpuCount` vs the top-level `workers` (are we under-/over- + * subscribing cores?) and `totalMemMb` (does `lowMemoryMode` / single-worker + * collapse track real memory pressure?). `gpuDisabled` completes the GPU + * picture alongside `observability.browserGpuMode`. + * + * Reflects the ORCHESTRATOR host. For distributed renders the chunk workers + * may run on different machines; this is still the right signal for the + * common single-machine render. Optional for back-compat with serialized + * older summaries. + */ + host?: { + platform: string; + arch: string; + cpuCount: number; + totalMemMb: number; + nodeVersion: string; + gpuDisabled: boolean; + }; } export interface HdrDiagnostics { @@ -1639,6 +1660,7 @@ export async function executeRenderJob( observability: observabilitySummary, peakRssBytes: memSampler.peakRssBytes(), peakHeapUsedBytes: memSampler.peakHeapUsedBytes(), + gpuDisabled: cfg.disableGpu, }); job.perfSummary = perfSummary; if (job.config.debug) {