Files
Brian Degenhardt 4fc1fa7c5f PINE: add statistics and settings opcodes, plus a gsctl client
Debugging a GS performance problem meant restarting the emulator and reloading a
savestate for every "did you try setting X?", and the only way to read the
statistics that drive that decision was to look at the OSD. PINE already
provides a unix socket, a thread, framing, a config key and the RunOnCPUThread
marshalling pattern, but its opcodes stop at guest-RAM peek/poke plus savestates
and game identity -- no host statistics, no settings.

Adds four ARMSX2-local opcodes at 0x10+ (upstream PINE ends at 0xF, so a generic
client will never send them):

  MsgGetStats     PerformanceMetrics, all GSPerfMon counters and the texture
                  cache memory figures, as JSON.
  MsgGetSetting   read a setting by section/key.
  MsgSetSetting   write a setting, apply it, and report whether the key forces a
                  GS device reopen.
  MsgFrameAdvance step a paused VM.

MsgSetSetting writes the persisted key rather than poking EmuConfig directly,
because a direct poke is silently reverted by the next ApplySettings, which
re-derives EmuConfig from the INI layer stack. The restart_required answer comes
from a new GSOptions::IsRestartOption, sitting next to RestartOptionsAreEqual so
the two lists stay in sync.

Statistics are gathered on the PINE thread. PerformanceMetrics and g_perfmon are
benign scalar reads, but GSgetMemoryStats dereferences g_texture_cache and
g_gs_device, which are GS-thread owned, so that one is marshalled through
RunOnGSThread.

tools/gsctl.py is a stdlib-only client emitting JSON on stdout.

Verified against a headless gsrunner replay: toggling accurate_blending_unit
between 0 and 5 over the socket moves barriers 1.0 <-> 91.5 and draw calls
55.5 <-> 101.5, repeatably, with no restart.
2026-07-22 21:16:45 -07:00
..