* Use spare CPU cores to remove in-game hitches on low-power devices
Per-frame game logic and drawing are cheap; the remaining stalls on
low-power handhelds are one-off main-thread hitches from chip SFX/cry
synthesis, RED++ atlas recolouring, image decodes and the require shim.
Move those onto background threads, each with a synchronous fallback so
behaviour is identical when threads are unavailable (POKEPORT_NO_THREAD=1,
no love.thread, worker failure).
Also adds an F3 frame-time overlay and POKEPORT_GAME_PROF headless
profiling to measure these costs.
* Address code review: shared WorkerLane, F3 gating, decode/epoch fixes
- Extract src/core/WorkerLane.lua (worker lifecycle, prioritised lanes,
per-lane epochs, bounded LRU) and rebuild the SFX and atlas/decode
prefetchers on it
- Keep decoded sprite sheets in every colour mode (the OBJ palette bake
re-reads them), skip oversized decodes, and let palette changes drop
only atlas bakes
- Only bump the SFX render epoch when the channel mix actually changes
- F3 overlay toggle no longer steals the key from rebinding screens or
player bindings; profilers stop only when a quit really proceeds
- Require fast path: test hook instead of a hot-path counter
- Shared tests/drivers/perf_util.lua; drivers restore their patches
* WorkerLane: no budget leak on duplicate sends, keep priority on requeue
- Lane:pump only counts a job against the in-flight budget once per key
- requeueInflight puts entries back at the head of their own queue, so
background hints no longer overtake front requests after a bump
- shutdown() keeps a fatally failed worker off
- Hiding the F3 overlay logs one summary line instead of a full block
* F3 overlay: last-120-frame stats, font-sized box, scale on large screens
* Prefetch: skip useless hints, no main-thread fallback, mix check vs worker config
- Don't queue decodes for sprite sheets whose Image and OBJ bake are cached
- Atlas hint is dropped (sync bake at build time) when the worker can't
decode the tileset itself, instead of decoding on the main thread
- SFX epoch bump compares the live mix with what the worker was last
configured with, replacing the load-time snapshot
* Sprite decode skip: raw-path cache check, never under RED++
* Gen 2 battles prefetch both parties' cries; sprite skip keyed by raw path
* Prefetch: skip cached tilesets outside RED++, log hint failures once
* Profiler: bench survives F3 hide, frame total excludes vsync; safer SFX pump, ordered requeue, tileset skip respects pending variants
* Profiler counts GPU stalls in present; water void-fill keeps tileset hint; README + test coverage
* Profiler vsync cap: measured cadence via RefreshRate, no per-frame allocation
* Use upstream's chip-worker prewarm for SFX/cries; drop our separate SFX worker
Our extra coverage (Gen 1 common SFX + party cries, both parties' battle
cries, lead move sounds, hit and battle SFX, Gen 2 bench cries) now goes
through ChipAudio.prewarmSfx/prewarmCry and a new Sound.prewarmMove.
* Skip decode hints for composed assets; share move cache key; drop dead demoteAll
* Drop dev-only profiling extras: POKEPORT_JITP hook and perf drivers
* README: name the worker threads POKEPORT_JIT leaves alone
* Profiler: count the pre-present vblank wait as present, log on NX quit
- time PresentSync.waitBeforePresent as "present" (the X11 GLX vblank wait
lands there), so it is excluded like the wait inside present
- NX quit now emits the PROF summary / overlay line too
- love.quit's scripted check reads FrameProfiler.benchRequested, so
POKEPORT_GAME_PROF=0 no longer counts as a bench run
- prefetchAtlas skips composed tileset sheets (Assets.isComposed)
- hit sounds defined once in EffectRegistry.HIT_SOUNDS, shared with the
battle prewarm (each hit still gets its own copy)
- drop redundant obpPaths, dead FrameProfiler.visible and the unused
Lane:bump requeue mode; restore applyOptions' doc comment