Files
healer-man/docs/PERFORMANCE_CHECK_2026-10-04.md
T
2026-10-04 13:13:35 -04:00

6.2 KiB
Raw Blame History

Performance check — 2026-10-04

Findings and changes

  • CPU: The production CPU profile spent about 925 ms of an eight-second sample inside filesForContentPack, which repeatedly scanned and sorted the asset catalog. src/content/contentManifest.ts now indexes each immutable file map once through a WeakMap. src/content/contentManager.ts caches usable/unavailable pack results until the next content publication; install, removal, validation, and catalog changes invalidate those results.
  • Idle CPU/rendering: src/ui/usePresentationClock.ts stops HUD, aura, and periodic-effect clocks while locally paused, viewing the map/companion, or hidden. Visible shared-online timers still run behind menus. DungeonRaceHud.tsx no longer starts its two clocks during ordinary dungeon play. Aura and periodic strips use shallow target-filtered subscriptions, and aura timer effects no longer restart on every new filtered array.
  • Retained RAM: src/game/combatAudio.ts previously retained every decoded cue for the tab lifetime. Its least-recently-used cache now holds at most 64 entries or 32 MiB of decoded PCM. Active playback keeps its own buffer, failed requests can retry, and simultaneous requests share a decode. This limits cache retention, not total browser memory or all in-flight audio allocations.
  • GPU: Local pause and hidden-page handling already switch the scene to demand rendering and stop physics. The checked stable pause/background samples made zero WebGL draw calls. No rendering-resolution, texture-quality, lighting, or culling reductions were applied.

Measurements

Production builds were exercised in headless Edge on the AMD Radeon RX 9070 XT, at a 1280 × 800 viewport with a 1920 × 1200 drawing buffer. scripts/playtest-performance.cjs creates a temporary local account/database, enters Wailing Caverns, samples entry combat and movement, pauses/resumes, exercises synthetic page visibility, and repeats entry/exit three times. The historical label active-idle means no player input; companions may fight during that sample.

Sample Before After Confirmation
Entry main-thread busy time 77.7% 44.2% 61.7%
Movement-following main-thread busy time 79.2% 69.0% 55.6%
Stable 20-second pause busy time 5.41% 0.49% 0.46%
Stable pause draw calls/second 0 0 0
Stable pause JavaScript heap 192.57 MiB 173.26 MiB 173.06 MiB
Third menu return JavaScript heap 59.88 MiB 59.59 MiB 59.71 MiB

These are individual route samples, not a deterministic combat benchmark. NPC appearances, combat progression, loaded effects, and concurrent project work vary. The heap difference and active draw-call difference must not be attributed entirely to these fixes. The CPU profile confirms removal of the catalog-scan hotspot; the stable pause results reproduce.

A separate lookup benchmark used the same 21,585-file manifest and 2,181-file combat-media pack for both algorithms: 100 old scan/sort lookups took 746.06 ms; building the new index took 14.91 ms once; 100 cached lookups took 0.023 ms. This isolates the catalog work, not total game speed.

The after run's synthetic hidden sample used 0.43% main-thread busy time and zero draw calls; resume restored continuous rendering. JavaScript heap changed only 0.024 MiB over its stable paused 20-second window. Menu returns released most scene heap and backing storage across three cycles. No page errors occurred.

One third-cycle pause window still showed a transient workload (12.3% main-thread busy time and 226 draw calls/second) despite demand mode. The confirmation run adds a paused CPU profile and an additional 20-second settled sample: both its initial third-cycle pause and settled window had zero draws and no layouts. The settled window used 0.50% main-thread busy time; the paused profile was overwhelmingly idle. This transient was not reproduced or conclusively attributed, so pause is not certified draw-free during every asynchronous transition.

Validation and evidence

  • 59 focused tests passed across content validation/persistence, audio, aura/periodic displays, scene lifecycle, and disposal. Audio regressions specifically cover byte/count eviction, concurrent reuse, failure retry, and active playback.
  • npm run build passed TypeScript, Vite, KTX2 and environment-performance audits, compression, and release-bundle verification. The environment audit reported zero regressions across 106 assets, with 60 existing budget exceptions.
  • npm run release:check passed. An initial attempt raced another task writing a document; the subsequent check passed without changing unrelated work.
  • Visually inspected active and resumed gameplay and pause screenshots: character bodies, starter gear, enemies, effects, and HUD remained visible. This is not an equipment or character-rendering change.
  • Raw measurements/screenshots/profiles: artifacts/playtest/performance/before/, after/, and confirmation/. Each run contains results.json, renderer.json, active/pause screenshots, and active.cpuprofile; confirmation also contains paused.cpuprofile.
  • Repeat with node scripts/playtest-performance.cjs; set PERFORMANCE_LABEL for a separate evidence directory. PERFORMANCE_URL can point the same UI route at a local preview, but production mode is necessary to exercise installed-content checks.

Limits and remaining targets

Main-thread busy percentage is one browser thread's occupied wall time, not whole-system CPU utilization. Forced-GC JavaScript heap and backing storage are not total process RAM, GPU memory, or VRAM. Draw calls measure submitted rendering work, not device GPU utilization. Hidden-page handling is a synthetic lifecycle test, not an OS background-throttling measurement.

This pass covers one desktop dungeon route, not every dungeon, mobile/Android, high-shadow/resolution settings, shared-online play, or a long combat soak. Original-effect graph and texture caches still retain distinct media for the tab lifetime; safe eviction needs a separate varied-spell/resource-ownership check. Remaining active profile cost is primarily rendering, transforms, and layout; larger scene and device-level GPU profiles should guide any further visual changes.