OXYGENHUB

The RRDC control port, with contract 0.6's performance metrics (GET/POST /perf) (#1) proposal #2 landed andymccall wants rrdc-perf into main · 1 change · opened

issue #1 The RRDC control port, with contract 0.6's performance metrics (GET/POST /perf) closed → proposal #2 landed → main landed

conversation (0) changes (1) files (0) projected state 230bfa33f3d9 · 1 head · browse · json
andymccall opened this proposal

Closes issue #1.

The RRDC control port (-controlport <port>), serving contract 0.6.

  • The core: vendored as an oxy submodule at src/extern/retro-remote-debug-controller, following RRDC's perf-6 bookmark until retro-remote-debug-controller #7 lands.
  • The backend (src/control_backend.c): ported from the plain fork's. It covers memory, registers, the screen, stepping, keys, reset, audio, the pointer and pads; the pads go through this emulator's own virtual joysticks.
  • GET and POST /perf answer from the measurements the Performance panel and DAP make. DAP's JSON builders moved into perf_json.c, so both surfaces give the same fields. Added for /perf:
    • each zone's statistics per window;
    • raw frames since= a frame;
    • each scanline's bus clocks (lines=1);
    • the sprite renderer's time, not exposed remotely before.
  • perf_budget_zone_add_marker: a client names a guest marker region before the guest enters it.
  • Hooks: the main loop serves the port's requests between instructions and closes /step after the budget closes its frame. The Windows drag path stands aside while the port is on, as it does for the debuggers.
  • CMake: the sources and RRDC's include path. /perf comes with cJSON (USE_DAP); without it the port serves 0.5.

Two fixes in the measurement code, found by measuring Metal Gear:

  1. A framed guest's regions carry across the frame boundary. The module's frame runs vsync to vsync, while a game's starts where it waits, so its work runs across the boundary. Closing regions there handed the rest of the phase then running to no zone: Metal Gear's "common" read 2.8 scanlines against the true 4.3 its cycle counter gives. A guest that frames its work with $F0 … $F1 now keeps its regions until its own next $F0/$F1. A guest that never writes $F0 has them closed at the boundary, as before.
  2. An interrupt after $F1 is work. In marker mode, cycles in an IRQ handler during the idle span counted as idle, so main (work − IRQ) came up short. The docs already said interrupt cycles are never idle.

Docs: README (the control port, /perf, a run recipe) and docs/perf-budget.md (named regions, the framed-region rule, the IRQ rule, and where it's implemented).

Tests:

  • Unit tests: 40 of 40. perf_budget passes 113 checks, with new ones for the framed carry, the IRQ rule and named markers. The old "a region left open is closed by the frame boundary" test is now two cases: unframed (still closed) and framed (closed at the guest's next $F0).
  • RRDC's conformance suite: 27 of 27, including the five 0.6 tests.
  • testbench/test_dap.py: 84 passed, 0 failed, 1 skipped (needs -bp), so DAP is unchanged by the builders' move.
  • Metal Gear's harness on this build: test_smoke and test_frame_cost (64 tests) and test_perf_markers. Its per-phase means from /perf match the guest's cycle counter to within 0.1 of a scanline.

andymccall opened

andymccall landed: web, with no approval

Proposal landed

andymccall landed 1 change into main .