optimize scheduler and button edge case

This commit is contained in:
Alex Vanderveen
2026-08-02 18:12:17 -04:00
parent 6917d4a620
commit bc99f95fff
5 changed files with 158 additions and 9 deletions
+48
View File
@@ -3185,3 +3185,51 @@ provisioning — investigate `rb wire hole` lines before raising D further.
Trade-off to feel for in play: each +1 D adds ~17 ms of local input latency;
if the WAN session feels laggy but smooth, that's the controller working —
step 3 (dense snapshots) then attacks replay cost so D can ride lower.
## 58. Dense tip snapshots + Start debounce in replay produce (2026-08-02)
**§57 soak verdict (healthiest yet — committed):** the arrival-driven
controller worked with evidence on every move (`5→6` at 153‰ miss / 11 ms
lateness, later `6→5` at 0‰ sustained). Scorecard trend across windows:
miss_need 198→150→32 (host) / 72→19→17 (guest), gap1 +116→+24 / +50→+8,
episodes ~1–2 per 30 s (was 6–8), `lead avg` sitting on the achievable
`D−1−transit_est`, `cushion rebuilt ACH` replacing the NEAR parks. One abort
per side, zero baseline mismatch / desync. Two items remained: replay
`depth_avg` 14–41 with pred_depth 1–2 (snap granularity dominates episode
cost), and one user-visible bug — a Start press opened TWO overlapping pause
menus (online only).
**1. Start doubling root cause — §47 replay produce bypassed the §34
debounce.** `np_rb_produce_local_tip_for_sim` copied the raw live snapshot
(`staged = live`) without `np_digital_debounce_staged()`, so a press
overlapping an episode could publish press/idle/press across consecutive tip
rows (SDL snapshot bounce mid-resim); MotK's pause is edge-triggered → two
menus. This soak had an episode every ~30 s, which is exactly the exposure
window. Fix: the replay producer now runs the same debounce, keeping the
sticky release state coherent across live↔replay transitions.
**2. Dense tip snapshots (`PSX_NET_SNAP_DENSE`, default 8, 0=off, max 24).**
Live path now saves EVERY tick; non-interval ticks are tracked in a sliding
FIFO and evicted from the ring once they age past the window (pinned
baseline / episode load / commit-span snaps are never dense-evicted), so
interval history is untouched. Ring depth 64→80 to absorb the extra
occupancy. Cost: one raw boot_state save per frame instead of per 16 —
watch the [FPS] guest ms/f; PSX_NET_SNAP_DENSE=0 restores §57 behavior.
**3. choose_load can now raise to the raw HC-proven tick** when a local
snap exists there (`hc_dense=` in the raise log). The §53b interval-only
raise rule predates dense snaps; with both peers keeping the same window the
snap is normally present on the follower too, and when it is not, follower
NACK → demote already covers it (one extra round trip, rare). Together with
the walk's existing HC-confirmed acceptance, a near-tip mispredict should
now load at (or within a tick of) the mismatch.
**Re-soak watch:** `rb snap ring ready … tip_dense=8`; scorecard
`depth_avg` should drop from 14–41 toward `pred_depth + handshake span`
(single digits); watch `[FPS]` guest ms/f for the per-frame save tax (if
Live drops below 60, set PSX_NET_SNAP_DENSE=4 or 0 and re-evaluate);
occasional `choose_load raise … hc_dense=…` lines are the new path working —
a burst of follower NACK/demotes right after them would mean the peer's
dense window is out of phase and the raise should be narrowed. For the Start
fix: pausing near resim activity should be single-menu; if a double recurs,
enable the pad edge log and capture the row sequence.
+6 -1
View File
@@ -18,7 +18,10 @@
extern "C" {
#endif
#define NETPLAY_SNAP_RING_DEFAULT_DEPTH 64u
/* §58: 64 → 80 — dense tip snaps (PSX_NET_SNAP_DENSE window) occupy up to
* ~24 extra slots alongside the interval snaps; keep the interval history
* span (~1024 ticks at iv=16) from shrinking under dense pressure. */
#define NETPLAY_SNAP_RING_DEFAULT_DEPTH 80u
typedef struct NetplaySnapRing NetplaySnapRing;
@@ -53,6 +56,8 @@ const uint8_t* netplay_snap_ring_peek(const NetplaySnapRing* r, uint32_t tick,
/* Invalidate every snapshot with tick > tick (dead-timeline snaps after an
* abort realign). Returns the number of slots dropped. */
uint32_t netplay_snap_ring_drop_after(NetplaySnapRing* r, uint32_t tick);
/* Drop the single snap at tick (dense-window eviction). 1 if dropped. */
int netplay_snap_ring_drop_tick(NetplaySnapRing* r, uint32_t tick);
uint32_t netplay_snap_ring_oldest_tick(const NetplaySnapRing* r);
uint32_t netplay_snap_ring_newest_tick(const NetplaySnapRing* r);
+8
View File
@@ -178,6 +178,14 @@ int netplay_snap_ring_load(NetplaySnapRing* r, uint32_t tick, CPUState* cpu,
return boot_state_load_buffer(data, size, bios_checksum, entry_pc, cpu);
}
int netplay_snap_ring_drop_tick(NetplaySnapRing* r, uint32_t tick) {
int idx = find_slot(r, tick);
if (idx < 0) return 0;
slot_clear(&r->slots[idx]);
if (r->count) r->count--;
return 1;
}
uint32_t netplay_snap_ring_drop_after(NetplaySnapRing* r, uint32_t tick) {
uint32_t i, n = 0;
if (!r) return 0;
+8
View File
@@ -2009,6 +2009,8 @@ static void np_rollback_reconcile_wire(void)
}
static void np_digital_debounce_staged(void);
/* §47: Replay participates in the protocol like Live — produce local tip
* from the live physical pad while consuming remote confirmed rows. */
static void np_rb_produce_local_tip_for_sim(rnet_u32 sim)
@@ -2023,6 +2025,12 @@ static void np_rb_produce_local_tip_for_sim(rnet_u32 sim)
g_np.staged = g_np.live;
psx_netplay_normalize_pad(&g_np.staged);
g_np.staged_valid = 1;
/* §58: this producer bypassed the §34 release debounce — a Start
* press overlapping an episode could publish press/idle/press across
* consecutive tip rows (SDL snapshot bounce mid-resim) and MotK's
* edge-triggered pause opened twice. Same debounce as stage_local so
* the sticky state stays coherent across live↔replay transitions. */
np_digital_debounce_staged();
}
g_np.latched_for_tick = 0;
(void)rnet_session_prepare_local_tip(g_np.session, sim);
+88 -8
View File
@@ -163,6 +163,40 @@ static uint32_t snap_interval(void)
}
return iv;
}
/* §58 dense tip snaps: live saves every tick inside a sliding window so a
* near-tip mispredict loads at (or within a tick of) the mismatch instead of
* up to snap_interval-1 ticks below it (§57 soak: depth_avg 14–41 with
* pred_depth 1–2 — replay work was dominated by snap granularity). The
* window is bounded: non-interval ticks older than the window are dropped
* from the ring so interval history is not evicted. PSX_NET_SNAP_DENSE
* overrides the window size (0 disables, max 24). */
#define NP_DENSE_SNAP_MAX 24u
static uint32_t g_tip_dense_fifo[NP_DENSE_SNAP_MAX];
static uint32_t g_tip_dense_fifo_n;
static uint32_t g_tip_dense_fifo_head;
static uint32_t tip_dense_window(void)
{
static int latched;
static uint32_t win = 8u;
if (!latched) {
const char *e = getenv("PSX_NET_SNAP_DENSE");
if (e && e[0]) {
unsigned v = 0;
if (sscanf(e, "%u", &v) == 1 && v <= NP_DENSE_SNAP_MAX)
win = v;
}
latched = 1;
}
return win;
}
static void tip_dense_reset(void)
{
g_tip_dense_fifo_n = 0;
g_tip_dense_fifo_head = 0;
}
static int g_local_baseline_sent;
static uint32_t g_local_baseline_digest;
static uint32_t g_local_baseline_av;
@@ -384,6 +418,32 @@ static uint32_t g_last_begin_mismatch = 0xffffffffu;
/* Baseline mismatch with no agreed tip — don't reopen every few ticks. */
#define RB_BASELINE_MISMATCH_COOLDOWN_TICKS 90u
/* §58: track a live non-interval snap tick; evict the oldest dense snap once
* the window is full (never the pinned baseline / episode load / commit
* span — those stay until normal oldest-tick eviction). */
static void tip_dense_push(uint32_t tick)
{
uint32_t win = tip_dense_window();
if (win == 0u)
return;
if (g_tip_dense_fifo_n >= win) {
uint32_t victim =
g_tip_dense_fifo[(g_tip_dense_fifo_head + NP_DENSE_SNAP_MAX -
g_tip_dense_fifo_n) % NP_DENSE_SNAP_MAX];
g_tip_dense_fifo_n--;
if (!(g_pin_valid && victim == g_pin_tick) &&
!(g_rb && rnet_rb_is_active(g_rb) &&
victim == rnet_rb_get_load_tick(g_rb)) &&
!(g_agreed_valid && victim >= g_agreed_span_lo &&
victim <= g_agreed_through)) {
(void)netplay_snap_ring_drop_tick(g_snaps, victim);
}
}
g_tip_dense_fifo[g_tip_dense_fifo_head] = tick;
g_tip_dense_fifo_head = (g_tip_dense_fifo_head + 1u) % NP_DENSE_SNAP_MAX;
g_tip_dense_fifo_n++;
}
static int g_was_in_fmv;
static uint32_t g_fmv_settle_until;
static uint32_t g_fmv_lockstep_until;
@@ -2054,8 +2114,18 @@ static int choose_load_tick_inner(uint32_t mismatch, uint32_t *out_load)
* (ADVANCE/HC-HEAL/commit advertise; never HEAL-FORCE). */
if (!post_tip_hold_holdoff_active()) {
uint32_t raised = have_shared ? shared : 0u;
uint32_t hc_dense = 0u;
if (hc_iv > raised)
raised = hc_iv;
/* §58: dense tip snaps — the raw HC-proven tick is raise-safe when a
* snap exists locally (both peers keep the same dense window; a
* missing peer snap is recoverable via follower NACK → demote,
* unlike the §53b interval-only rule which predates dense snaps). */
if (tip_dense_window() > 0u && hc_floor > raised &&
netplay_snap_ring_has(g_snaps, hc_floor)) {
raised = hc_floor;
hc_dense = hc_floor;
}
if (g_peer_resolved_through > raised)
raised = g_peer_resolved_through;
if (raised > 0u && (!have_shared || raised > shared)) {
@@ -2064,10 +2134,10 @@ static int choose_load_tick_inner(uint32_t mismatch, uint32_t *out_load)
if (s_raise_log != raised) {
fprintf(stderr,
"psxrecomp: rb choose_load raise %u→%u "
"(agreed=%u hc_iv=%u peer_resolved=%u)\n",
"(agreed=%u hc_iv=%u hc_dense=%u peer_resolved=%u)\n",
(unsigned)shared, (unsigned)raised,
(unsigned)(g_agreed_valid ? g_agreed_through : 0u),
(unsigned)hc_iv,
(unsigned)hc_iv, (unsigned)hc_dense,
(unsigned)g_peer_resolved_through);
fflush(stderr);
s_raise_log = raised;
@@ -3776,6 +3846,7 @@ void psx_netplay_rb_start(void)
return;
g_snaps = netplay_snap_ring_create(NETPLAY_SNAP_RING_DEFAULT_DEPTH);
tip_dense_reset();
if (!g_snaps) {
fprintf(stderr, "psxrecomp: rb snap ring create FAILED — rewind disabled\n");
return;
@@ -3863,8 +3934,11 @@ void psx_netplay_rb_start(void)
g_bl_fork_cap = 0u; /* §55: commit/reset clears the bisect cap */
clear_baseline_pin();
clear_episode_wire_state();
fprintf(stderr, "psxrecomp: rb snap ring ready depth=%u snap_interval=%u\n",
(unsigned)netplay_snap_ring_depth(g_snaps), (unsigned)snap_interval());
fprintf(stderr,
"psxrecomp: rb snap ring ready depth=%u snap_interval=%u "
"tip_dense=%u\n",
(unsigned)netplay_snap_ring_depth(g_snaps),
(unsigned)snap_interval(), (unsigned)tip_dense_window());
}
void psx_netplay_rb_shutdown(void)
@@ -3931,7 +4005,7 @@ void psx_netplay_rb_shutdown(void)
/* Dense tip snaps: media/settle, plus the digest lockstep/+tip window even
* after invent is allowed again (§26) — first Cross/Start after FMV still
* wants a near-tip load. */
static int rb_dense_snap_window(void)
static int rb_fmv_dense_snap_window(void)
{
RNetSession *s = sess();
uint32_t sim = s ? rnet_session_sim_tick(s) : 0u;
@@ -3962,10 +4036,16 @@ void psx_netplay_rb_request_snap(uint32_t tick)
* invent-miss loads near the mismatch (954→944 was a ~10-tick hitch;
* with tip snaps + shared frontier → load≈953). */
if (!(g_rb && rnet_rb_is_resimulating(g_rb))) {
if (!rb_dense_snap_window()) {
if (!rb_fmv_dense_snap_window()) {
iv = snap_interval();
if (iv > 1u && (tick % iv) != 0u)
return;
if (iv > 1u && (tick % iv) != 0u) {
/* §58: dense tip window — save every live tick and keep the
* last N non-interval snaps so a near-tip mispredict loads
* at the mismatch, not an interval boundary. */
if (tip_dense_window() == 0u)
return;
tip_dense_push(tick);
}
} else if (tick >= g_fmv_dense_through) {
g_fmv_dense_through = tick;
}