perf(engine): Macs with a hardware GPU render with more capture workers (#6636)

* perf(engine): Macs with a hardware GPU render with more capture workers

* perf(engine): Apple-GPU worker sizing skips layered renders; fewer comment lines

* test(producer): format
This commit is contained in:
Miguel Ángel
2026-10-28 08:42:31 +00:00
committed by GitHub
parent 2ddf41eb7d
commit e6476c2cd5
6 changed files with 90 additions and 5 deletions
+1
View File
@@ -310,6 +310,7 @@ export {
type WorkerResult,
type WorkerSizing,
type WorkerSizingBound,
type WorkerSizingConfig,
type LandedFrame,
type ParallelProgress,
type ParallelWorkerPhaseDiagnostic,
@@ -167,6 +167,22 @@ describe("computeWorkerSizing", () => {
expect(computeWorkerSizing(1300, undefined, { concurrency: "auto" }).workers).toBe(5);
});
it("sizes Apple-GPU disk capture by its own heap and core budget", () => {
vi.mocked(cpus).mockReturnValue(
Array.from({ length: 14 }, () => ({}) as ReturnType<typeof cpus>[number]),
);
vi.mocked(totalmem).mockReturnValue(24 * 1024 ** 3);
vi.mocked(getHeapStatistics).mockReturnValue({
heap_size_limit: 4192 * 1024 ** 2,
} as ReturnType<typeof getHeapStatistics>);
const apple = { concurrency: "auto" as const, appleGpuDiskCapture: true };
const sizing = computeWorkerSizing(1300, undefined, apple);
expect(sizing.workers).toBe(8);
expect(sizing.boundBy).toBe("memory");
expect(sizing.exceedsHeapAdvisory).toBe(false);
expect(computeWorkerSizing(1300, undefined, { ...apple, coresPerWorker: 4 }).workers).toBe(3);
});
it("matches calculateOptimalWorkers and reports every constraint", () => {
const config = { concurrency: "auto" as const };
const sizing = computeWorkerSizing(900, undefined, config);
@@ -131,6 +131,8 @@ export interface WorkerSizingConfig extends Partial<
* than a plain DOM screenshot. Explicit --workers requests ignore this hint.
*/
captureCostMultiplier?: number;
/** macOS, hardware GPU, screenshot frames written to disk (not streamed through this process). */
appleGpuDiskCapture?: boolean;
}
type WorkerBrowserPoolDecision = {
@@ -151,6 +153,10 @@ const HEAP_RESERVED_MB = 1024;
// default heap ⇒ >~500MB/worker + base. Caps auto sizing; an explicit
// `--workers N` is still the operator's call.
const HEAP_PER_WORKER_MB = 640;
// Apple-GPU disk capture measured ~15-25 MB parent heap per worker; 14 cores kept gaining to 8.
const APPLE_DISK_HEAP_PER_WORKER_MB = 128;
const APPLE_DISK_CORES_PER_WORKER = 1.75;
const APPLE_DISK_MAX_WORKERS = 8;
const MIN_WORKERS = 1;
const MAX_WORKER_DIAGNOSTIC_LINES = 8;
// Hard ceiling on explicit `--workers N` requests. Above this, the cost of
@@ -321,9 +327,13 @@ export function computeWorkerSizing(
// is immediately reclaimable.
const totalMemoryMb = getSystemTotalMb();
const heapLimitMb = Math.round(getHeapStatistics().heap_size_limit / (1024 * 1024));
const appleDisk = config?.appleGpuDiskCapture === true;
const heapBasedWorkers = Math.max(
1,
Math.floor((heapLimitMb - HEAP_RESERVED_MB) / HEAP_PER_WORKER_MB),
Math.floor(
(heapLimitMb - HEAP_RESERVED_MB) /
(appleDisk ? APPLE_DISK_HEAP_PER_WORKER_MB : HEAP_PER_WORKER_MB),
),
);
const cpuBasedWorkers = Math.max(1, cpuCount - 2);
const memoryBasedWorkers = Math.max(1, Math.floor((totalMemoryMb * 0.5) / MEMORY_PER_WORKER_MB));
@@ -362,9 +372,15 @@ export function computeWorkerSizing(
if (concurrency !== "auto") {
return Math.max(MIN_WORKERS, Math.min(ABSOLUTE_MAX_WORKERS, Math.floor(concurrency)));
}
return defaultSafeMaxWorkers();
return appleDisk
? Math.max(defaultSafeMaxWorkers(), APPLE_DISK_MAX_WORKERS)
: defaultSafeMaxWorkers();
})();
const effectiveCoresPerWorker = config?.coresPerWorker ?? DEFAULT_CONFIG.coresPerWorker;
const configuredCoresPerWorker = config?.coresPerWorker ?? DEFAULT_CONFIG.coresPerWorker;
const effectiveCoresPerWorker =
appleDisk && configuredCoresPerWorker === DEFAULT_CONFIG.coresPerWorker
? APPLE_DISK_CORES_PER_WORKER
: configuredCoresPerWorker;
const effectiveMinParallelFrames = config?.minParallelFrames ?? DEFAULT_CONFIG.minParallelFrames;
const effectiveLargeRenderThreshold =
config?.largeRenderThreshold ?? DEFAULT_CONFIG.largeRenderThreshold;
@@ -17,6 +17,7 @@ import {
type CaptureSession,
type EngineConfig,
type WorkerSizing,
type WorkerSizingConfig,
calculateOptimalWorkers,
captureFrameToBuffer,
closeCaptureSession,
@@ -138,7 +139,7 @@ export function buildHeapAdvisoryWarning(
export function resolveRenderWorkerCount(
totalFrames: number,
requestedWorkers: number | undefined,
cfg: EngineConfig,
cfg: EngineConfig & Pick<WorkerSizingConfig, "appleGpuDiskCapture">,
compiled: Pick<CompiledComposition, "hasShaderTransitions" | "renderModeHints">,
log: ProducerLogger = defaultLogger,
measuredCaptureCost?: CaptureCostEstimate,
@@ -64,6 +64,7 @@ import {
SEGMENTED_MIN_SECONDS_AFTER_SOAK,
resolveParallelCaptureMode,
shouldStreamParallelCapture,
isAppleGpuDiskCapture,
shouldUseStreamingEncode,
resolveObservedCaptureMode,
createCaptureObservabilityUpdater,
@@ -3665,6 +3666,23 @@ describe("calibrationCaptureConfig", () => {
});
});
describe("isAppleGpuDiskCapture", () => {
const disk = {
platform: "darwin",
browserGpuMode: "hardware",
layered: false,
parallelStream: false,
} as const;
it("holds only for hardware-GPU Mac renders that write frames to disk", () => {
expect(isAppleGpuDiskCapture(disk)).toBe(true);
expect(isAppleGpuDiskCapture({ ...disk, layered: true })).toBe(false);
expect(isAppleGpuDiskCapture({ ...disk, parallelStream: true })).toBe(false);
expect(isAppleGpuDiskCapture({ ...disk, browserGpuMode: "software" })).toBe(false);
expect(isAppleGpuDiskCapture({ ...disk, platform: "win32" })).toBe(false);
});
});
describe("shouldClampDefaultDrawElement (default-on drawElement clamp)", () => {
const unverifiedParallel = {
useDrawElement: true,
@@ -2797,6 +2797,21 @@ export function shouldSegmentCapture(args: {
return args.durationSeconds >= resolveSegmentedMinSeconds(args.env);
}
/** Worker sizing's Apple-GPU budget fits only frames written to disk by screenshot workers. */
export function isAppleGpuDiskCapture(args: {
platform: NodeJS.Platform;
browserGpuMode: "software" | "hardware";
layered: boolean;
parallelStream: boolean;
}): boolean {
return (
args.platform === "darwin" &&
args.browserGpuMode === "hardware" &&
!args.layered &&
!args.parallelStream
);
}
export function shouldStreamParallelCapture(args: {
/** Router switch for this render — see isCaptureParallelStreamRouterEnabled. */
routerEnabled: boolean;
@@ -4037,10 +4052,28 @@ async function executeRenderPipeline(input: {
// Low-memory safe-mode's single-worker pin lives inside
// resolveRenderWorkerCount so its "why workers=N" logging stays coherent.
const appleGpuDiskCapture = isAppleGpuDiskCapture({
platform: process.platform,
browserGpuMode: resolvedBrowserGpuMode,
layered: shouldUseLayeredComposite({
hasHdrContent,
hasShaderTransitions: compiled.hasShaderTransitions && !isGif,
isPngSequence,
}),
parallelStream: isCaptureParallelStreamRouterEnabled(
process.env,
resolveParallelCaptureMode({
platform: process.platform,
headlessShellPath: resolveHeadlessShellPath(cfg),
forceScreenshot: captureForceScreenshot,
deviceScaleFactor: captureOptions.deviceScaleFactor,
}),
),
});
workerCount = resolveRenderWorkerCount(
totalFrames,
job.config.workers,
cfg,
{ ...cfg, appleGpuDiskCapture },
compiled,
log,
captureCalibration?.estimate,