PURPOSE
Automated performance stress-test screen mounted at /perf-benchmark. Launches a real mission with an invulnerable player and walks through nine escalating phases that isolate enemy count, bullet count, particle load, and combined load, then logs detailed telemetry to the console. Used to diagnose subsystem bottlenecks without relying on browser devtools profiling.
OWNS
- The
PHASEStable — nineBenchPhaseentries (warmup, enemies_light, enemies_medium, enemies_heavy, enemies_extreme, bullets_flood, particles_max, combined_hell, cooldown) with per-phasedurationSec,targetEnemies,forceFire, andparticleBurst. BenchPhaseandBenchLogEntrytypes.- The module-level
_benchLogarray that accumulates telemetry entries for the run. buildSummary()— aggregates the log by phase into per-phase FPS min/avg/max, mean frame time, top-5 render passes by cost, spike count, and peak entity/bullet/particle counts.buildBenchRunDef()— clonesDEFAULT_RUNand overrides combat stats to make the player effectively invulnerable while keeping shots from killing enemies (huge HP, huge shield, max regen, max damage reduction, large magnet range, max fire-rate, weapon damage at -90%).PerfBenchmarkScreenReact component — mounts the canvas, wires the bridge mission, and runs the benchmark control loop.- The HUD overlay: status string, large color-coded FPS readout (red < 30, amber < 55, green otherwise), current phase, and completion hint.
READS FROM
../engine/bridge—createMissionandMissionHandletype for starting/stopping the mission.../data/run-config—DEFAULT_RUNandRunDefinitiontype as the base for the benchmark run.../engine/core/state—setDebugOverlay,game,ship,worldglobals (readsship.x/y/hp/hpMax/shieldHp/shieldMax,world.enemies,world.particles).../engine/core/render-diag—diagGetPerfSnapshot(per-tick FPS, frame time, pass timings, entity counts),diagDrainSpikes(queued spike snapshots), and theRenderPerfSnapshot/SpikeSnapshottypes.../engine/enemies/spawner—GameMaster.spawnEnemyfor force-spawning the four canned typesorb_common,charger_common,shooter_common,mortar_common.window.devicePixelRatio,window.innerWidth,window.innerHeightfor canvas sizing.
PUSHES TO
- The browser console: phase-start banners, per-2-second telemetry lines (
[PERF-BENCH] <phase> @<t>s | FPS:<n> frame:<ms>ms | enemies/bullets/particles | TOP: <top-5 passes>), spike counts, and at completion the full log JSON plus the summary JSON. setDebugOverlay(true)to turn on the in-engine debug overlay for visual monitoring.mission.start()andmission.destroy()on the bridgeMissionHandle.- React state:
status,currentFps,currentPhase,done(all rendered in the HUD). - Mutates
ship.hp,ship.shieldHp, every enemy’shp,hpMax,fireTimer, and_aoeCooldown, and pushes syntheticsparkparticles ontoworld.particles. - The canvas DOM element (width/height/style sized to the window each mount).
DOES NOT
- Score, persist, or upload benchmark results — output is console-only.
- Render the game itself; rendering is done by the bridge mission. This screen only sizes the canvas and overlays a HUD.
- Spawn through the normal
GameMastercadence — it bypasses spawn pacing and force-fills totargetEnemiesin batches of up to 20 per tick. - Honor the player’s run config beyond
DEFAULT_RUN— there is no ship selection, no shop, no progression. - Handle resize after mount; canvas dimensions are captured once on effect entry.
- Read or write Supabase, telemetry, or Sentry — no cloud logging.
- Pause or respond to
onPhaseChange/onGameOvercallbacks (both wired as no-ops). - Cap
world.particlesabove 500 — particle bursts skip when at or above that count.
Signals
- React state:
status: string,currentFps: number,currentPhase: string,done: boolean. - Refs:
canvasRef: HTMLCanvasElement | null,missionRef: MissionHandle | null. - Effect-scoped locals driving the bench loop:
phaseIdx,benchStart,phaseStart,lastLog,benchDone,benchRAF. - Telemetry shape (
BenchLogEntry):t,phase,phaseT,perf(RenderPerfSnapshot),spikes(SpikeSnapshot[]),meta({ targetEnemies, forceFire, particleBurst }). - Console tag is
[PERF-BENCH].
Entry points
- Default-exported React component
PerfBenchmarkScreen— mounted at the/perf-benchmarkroute. - The component is self-driving once mounted: a single
useEffectwith empty deps starts the mission, schedulesbenchTickviarequestAnimationFrame, and registers a cleanup that flags the loop done, cancels the rAF, destroys the mission, and clearsmissionRef.
Pattern notes
- Module-level
_benchLogis intentionally a singleton — every mount resets it before the loop begins, so reloading the route discards the prior run. - The bench loop is a separate
requestAnimationFramechain from the bridge mission loop; it piggybacks on the engine’s own tick rather than driving it. - Player invulnerability is maintained both via config (huge stats in
buildBenchRunDef) and via per-tick clamping (ship.hp = ship.hpMax, shield refilled) to defeat any in-engine damage path. - Enemy invulnerability is set per-spawn (
hp = hpMax = 999999) so they survive incidental player damage from the negative-weapon-damage run. - Bullet-flood phase forces fire by zeroing every enemy’s
fireTimerand_aoeCooldownevery tick. - Particle-stress phase pushes 30 sparks per tick toward the ship’s surroundings until the global particle array reaches 500.
- Cooldown phase drains by setting every alive enemy’s
hpto 0 rather than calling a kill helper. - Telemetry cadence is fixed at 2000 ms wall-clock; the per-tick work above runs every animation frame regardless.
- Phase transition resets
phaseStartand returns early from the tick, skipping the mutation block for the boundary frame. - HUD overlay uses inline styles only — no CSS module or design-system component — and pins itself with
pointerEvents: 'none'so it never blocks input to the canvas. - The bench logs the full per-entry array (
FULL LOG) and the aggregatedSUMMARYseparately at completion; the summary’stopPassesis the 5 most expensive render passes averaged across that phase’s snapshots.