/** * Idle render governor — the wave-2 flagship of the 2026-08-05 perf * investigation. * * The problem: Cesium's default render loop repaints every vsync forever, so * the app burned ~60% GPU + ~54% of a core with ZERO layers enabled and a * parked camera. The fix: flip the scene into Cesium's `requestRenderMode` * whenever nothing animates per frame, and return to the continuous loop the * moment something does. * * Architecture — a binary mode driven by ref-counted holds: * * - **Continuous mode** (`requestRenderMode = false`, today's behavior) * while ANY hold is registered. Every per-frame animator — fleet * interpolation, traffic sim, satellite motion, tracked-entity follow, * style crossfades, CCTV projection — registers a hold for exactly the * lifetime of its scene-loop listener or animation. While one is active, * behavior is byte-identical to pre-governor main: the locked * interpolation/tracking invariants are preserved by construction. * - **Idle mode** (`requestRenderMode = true`) when zero holds. Cesium * auto-renders on camera input and tile loads; every other scene mutation * must call `governorRequestRender()` for its one frame. Discrete * mutators (layer poll ticks, slider writes, annotation changes) route * through that. * * Holds are identity-keyed (a Set of owner ids), NOT a counter — a module * that double-holds or double-releases cannot corrupt the mode. Owners are * short stable strings ('flights', 'traffic', 'style-anim', …) so the * diagnostics read like a story. * * The governor is O(1) passive: no per-frame work of its own, ever. */ let _viewer = null; let _installed = false; const _holds = new Set(); /** Debug trail of the most recent one-shot render requests (idle mode only). */ const _recentRequests = []; const RECENT_REQUEST_CAP = 16; function applyMode() { if (!_installed || !_viewer?.scene) return; const continuous = _holds.size > 0; const scene = _viewer.scene; if (scene.requestRenderMode === !continuous) return; scene.requestRenderMode = !continuous; if (!continuous) { // Entering idle: render one settling frame so anything the last // continuous frame mutated is on screen before the loop stops. scene.requestRender?.(); } } /** * Install the governor on the viewer. Idempotent. Before install, * hold/release still record into the holds set (and apply at install time); * requests are safe no-ops — so modules can call all three unconditionally * in tests without a viewer. * @param {Cesium.Viewer} viewer * @returns {void} */ export function installRenderGovernor(viewer) { if (!viewer?.scene) throw new TypeError('installRenderGovernor requires a Cesium viewer'); _viewer = viewer; _installed = true; // Never let Cesium re-render on simulation-time deltas behind our back — // idle means idle. All re-renders are camera/tiles (Cesium-native) or // explicit requests. viewer.scene.maximumRenderTimeChange = Infinity; applyMode(); } /** * Register a continuous-render hold. Idempotent per owner. * Call where the owner's per-frame work BEGINS (scene listener installed, * animation starts, tracking begins). * @param {string} ownerId Short stable id, e.g. 'flights', 'traffic'. * @returns {void} */ export function holdContinuousRender(ownerId) { if (!ownerId) return; _holds.add(ownerId); applyMode(); } /** * Release a hold. Safe when never held. * Call where the owner's per-frame work ENDS (listener removed, animation * settled, tracking stopped, layer disabled). * @param {string} ownerId * @returns {void} */ export function releaseContinuousRender(ownerId) { if (!ownerId) return; _holds.delete(ownerId); applyMode(); } /** * One-shot render request for a discrete scene mutation (layer tick, slider * write, annotation change). Always forwards to scene.requestRender() — in * continuous mode that is a harmless flag set (and forwarding closes the * request-then-last-release race); only idle-mode requests are recorded in * diagnostics. Cheap enough to call unconditionally after any mutation. * @param {string} [reason] For diagnostics only. * @returns {void} */ export function governorRequestRender(reason = 'unspecified') { if (!_installed || !_viewer?.scene) return; if (_holds.size === 0) { _recentRequests.push({ reason, at: Date.now() }); if (_recentRequests.length > RECENT_REQUEST_CAP) _recentRequests.shift(); } _viewer.scene.requestRender?.(); } /** * @returns {{installed: boolean, mode: 'continuous'|'idle', holds: string[], * recentRequests: Array<{reason: string, at: number}>}} */ export function getRenderGovernorDiagnostics() { return { installed: _installed, mode: _holds.size > 0 ? 'continuous' : 'idle', holds: [..._holds].sort(), recentRequests: [..._recentRequests], }; } /** Release the installed viewer after its animation owners have stopped. */ export function uninstallRenderGovernor(viewer) { if (_viewer !== viewer) return; _viewer = null; _installed = false; _holds.clear(); _recentRequests.length = 0; } /** Test seam: reset module state between unit tests. */ export function _resetRenderGovernorForTest() { _viewer = null; _installed = false; _holds.clear(); _recentRequests.length = 0; }