Commit Graph

1951 Commits

Author SHA1 Message Date
Shokunin f14af5594d GL: opt-in PS1 dithering ([video] dithering = off|on|scaled)
The OpenGL renderer has always drawn shaded and texture-modulated pixels
at 8 bits per channel with no dither pattern (true colour). That stays the
default. 'on' applies the PS1 4x4 offset matrix and 5-bit truncation per
internal-resolution pixel; 'scaled' puts the pattern on the native grid,
so it reads like the console at any scale and the native VRAM is the same
at every internal resolution.

GP0(E1h) bit 9 reaches the backend through a new optional facade hook
(gr_set_dither) recorded for the render thread and Smooth motion redraws.
With the mode off nothing flushes and no uniform changes. Flat, raw and
other 5-bit-exact pixels are never touched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:41:40 -07:00
Matt Stanley e6223c9c42 Merge pull request #648 from RetroPortingToolKit/fix/host-sampler-new-thread
host sampler: no pthread_self() in the SIGPROF handler on macOS
2026-10-12 23:36:26 -07:00
Matt Stanley 1f51d7e617 Merge pull request #650 from RetroPortingToolKit/fix/debug-savestate-slot-range
debug server: name an out-of-range savestate slot
2026-10-12 23:35:48 -07:00
Shokunin 7dc8fdde9a debug server: name an out-of-range savestate slot
savestate save/load with slot >= SAVESTATE_SLOTS (12) was refused with the
generic message that blames LLE runs, which sent a test chasing the wrong
cause. It now replies "slot N out of range (0..11)". Guarded in
test_savestate_status_protocol.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:21:53 -07:00
Shokunin 7ef25d50ff host sampler: tell the emulation thread apart by stack, not pthread_self
SIGPROF can land on a thread that is still being created. On macOS arm64
pthread_self() there hits a pointer-authentication trap (EXC_BREAKPOINT in
on_prof), which killed debug-tools builds at boot when a second instance
started lobby threads. The handler now compares the interrupted stack
pointer with the emulation thread's stack range; Linux keeps pthread_equal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:36:26 -07:00
Shokunin d185dac101 tests: drop duplicate stubs in test_gl_scale_invariance.c
gpu_ws_background_requires_full_composite and gpu_timeline_note were defined
twice after a merge, so every GL fixture that includes this file
(gl_post_aa, gl_bloom, ...) failed to compile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 23:01:52 -07:00
Alex Vanderveen c47f7391e8 fix netplay 2026-10-11 16:16:33 -04:00
Matthew Stanley f55a156c0b Adapt private sample-bank regression to guest-clock block mixer 2026-10-10 22:15:55 -07:00
Matthew Stanley 9fcbf43e3d Keep per-primitive filtering and PGXP lookup consistent after co-op merge 2026-10-10 20:53:30 -07:00
Matthew Stanley 7dfef44598 Reconcile current master with validated X4 co-op support for PR23 playtest 2026-10-10 20:49:35 -07:00
Matt Stanley 26b3878e78 fix: reduce CPU spin before high-refresh blended presents (#627) 2026-10-10 12:56:57 -07:00
Matt Stanley f9db2561d0 Fix persistent HD texture cache churn and missing live background dumps (#626)
* fix: reuse resident HD textures and publish live background dumps

* test: keep residency stubs local to the filtering fixture
2026-10-10 12:55:54 -07:00
Matt Stanley 19785e9f51 fix: guard ICE waiting-room helpers in offline builds (#625) 2026-10-10 12:55:50 -07:00
Matthew Stanley 4b96c039f6 Integrate current ICE lobby transports and preserve settled LAN boot settings 2026-10-10 10:53:54 -07:00
Shokunin 0b0641cbae Lobby: honour transport "ice" launches (same-NAT peers no longer dial themselves)
The lobby server starts a 2-player room the host relay cannot carry on ICE
(recomp-net-server start_transport, launch transport "ice"). This client
knew only "host" and the legacy server relay. It inferred a relay from
equal host/guest endpoints -- which is exactly what two peers behind one NAT
advertise -- and each peer dialled its own public address: the match stayed
at "linking" forever.

- psx_lobby_launch_transport.h: one pure decision (host / ice / relay /
  direct) with a unit test covering ICE, same-NAT endpoints, host relay and
  the legacy relay cases.
- PsxLobbyJoinInfo.transport_ice; the launch handler records it and never
  turns it into a relay; launch fill and resolve_use_ice start ICE p2p
  (signalled over the lobby) for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 10:37:31 -07:00
Matt Stanley 8cb420660e Merge pull request #624 from RetroPortingToolKit/fix/pgxp-qualified-defaults-20261010
Use dataflow-only PGXP defaults automatically in hook builds
2026-10-10 09:52:57 -07:00
Matt Stanley f17a946e6a Merge pull request #623 from RetroPortingToolKit/fix/native-wide-default-clipping-20261010
Make camera-plane clipping automatic with native-wide projection correction
2026-10-10 09:52:51 -07:00
Alex Vanderveen 1a8994ac0b Merge feat/ice-hub-relay: host relay over ICE for online rooms
Resolved against master: resolve_transport keeps master's single-agent
transport=ice launch (2-player rooms) ahead of psx_transport_decide; match_caps
JSON keeps the mod-plan fields and adds relay_via=ice; recomp-net bumped to
a48d355 (host_ice).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-10 04:20:24 -04:00
Matthew Stanley 8f6867225c Support independent co-op audio voices and diagnostic OSD rows 2026-10-10 00:53:39 -07:00
Matthew Stanley e7a059643c fix: select dataflow-only PGXP defaults in hook builds 2026-10-10 00:48:22 -07:00
Matthew Stanley ddbb979f18 fix: enable camera-plane clipping with native-wide projection correction 2026-10-10 00:48:17 -07:00
Matt Stanley 6fd3d1c326 Merge pull request #587 from RetroPortingToolKit/feat/gl-accurate-blending
GL: correct PS1 blending by default + blend/mask accuracy test
2026-10-09 23:56:33 -07:00
Matthew Stanley 1dd2afec07 Merge master and enable PS1 blend correction by default 2026-10-09 23:55:29 -07:00
Matt Stanley 6beb7d821b Merge pull request #621 from RetroPortingToolKit/fix/no-fp-contract
Runtime: build with -ffp-contract=off for cross-host determinism
2026-10-09 23:54:18 -07:00
Alex Vanderveen 50b53cc6d0 Merge master into fix/lobby-ice-launch
Keep both new test targets in runtime/CMakeLists.txt.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-10 02:22:03 -04:00
Alex Vanderveen 9d6f0d4319 Merge master into fix/rb-multipeer-frontier
Keep both netplay_peer_frontier.h and netplay_fmv_confirm.h includes; advance lib/recomp-net to a48d355 (main, includes #38).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-10 02:20:17 -04:00
Matt Stanley eb4c76fc47 Merge pull request #586 from RetroPortingToolKit/feat/gl-xbr-filter
GL: opt-in xBR edge-directed texture filter (texture_filtering = "xbr")
2026-10-09 23:19:31 -07:00
Shokunin bd0425d7ab Netplay rb: re-anchor dirty-RAM IRQ poll stride each tick; av skips d24 helpers
- The dirty-RAM interpreter's IRQ poll stride is host-only; a load resets
  it to phase 0 while Live carried an arbitrary phase, so a replay could
  take the VBlank in RAM code at another instruction. It is now
  re-anchored at every tick boundary in Live and Replay alike.
- The av digest zeroes the three depth-24 present helpers at the end of
  the GPU snapshot (upload span, present hold, previous height): host
  presentation state a suppressed-present replay leaves different, not
  guest-visible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 22:40:01 -07:00
Alex Vanderveen 1d2f4f95d3 sync 2026-10-10 01:38:54 -04:00
Matt Stanley 501a27c2fc Merge pull request #611 from RetroPortingToolKit/fix/fmv-confirm-lag
Netplay rollback: count post-movie agreement in confirmed ticks
2026-10-09 22:36:12 -07:00
Matt Stanley cf5bf1c88a Merge pull request #608 from RetroPortingToolKit/feat/mod-host-rumble
Mod API: title-supplied host rumble (psx_mod_set_host_rumble)
2026-10-09 22:36:08 -07:00
Matt Stanley 5a6527acc1 Merge pull request #605 from RetroPortingToolKit/fix/launcher-vsync-roundtrip
Launcher V-Sync row round-trips to [video] vsync
2026-10-09 22:36:04 -07:00
Matt Stanley 16758c1e24 Merge pull request #604 from RetroPortingToolKit/codex/mod-internal-resolution
Support session-only internal resolution requests from graphics mods
2026-10-09 22:35:59 -07:00
Shokunin c7d5b873cd Rollback snaps: the canonical machine at the tick boundary
PSX_RB_DET_SELFTEST on R4 4P showed a replayed save of tick T differing
from the live save of T. Each cause, and the fix:

- Replay's finish_frame poll passed no PC hint (Live used the IRQ resume
  chain); it now uses psx_netplay_snap_resume_hint.
- Devices are serviced lazily up to the next event, so root counters and
  cycles_since_vblank lagged the clock by a host-path amount at save; a
  load re-anchors them at the clock and drops the lag. The save now
  publishes pending cycle charges and services every device to the clock
  first, and renders the SPU up to it.
- GTE / mul-div completion stamps and the load-delay give-back were
  re-anchored at the restored clock on every load, so a stall in flight at
  the snap point cost the live run cycles but the replay none. New
  BS_SEC_CPUTIM carries them relative to the clock.
- The SPU CD-input FIFO was outside the snap and wiped on rollback loads;
  new BS_SEC_SPUCD carries the queued frames and the load keeps them.
- LZCR is serialized as derived from LZCS (the raw backing stays 0 until
  the first LZCS write; guests only ever read the derived value).

Older snaps without the new sections load as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 22:16:06 -07:00
Shokunin 0ae1725627 Netplay rb: Live and Replay tick snaps use one resume-PC rule
Replay's finish_frame saved its tick snap with no PC hint while Live used
the IRQ resume / last check / cpu->pc chain; the same tick's snap could
carry a different resume PC (psx_netplay_snap_resume_hint now serves both).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 22:15:57 -07:00
Shokunin 56f1e7bd3a Netplay rb: PSX_RB_DET_SELFTEST forces no-op rollbacks for the determinism diff
Every K ticks the host rewinds 16 ticks with nothing mispredicted, so each
seat replays confirmed rows over ticks it ran live; with PSX_RB_SNAP_AUDIT
the diff tool then compares replayed saves with live ones field by field.
On R4 4P it shows replay saves taken at a different guest point than the
live save of the same tick (CPU pc, cycles_since_vblank, root counters).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:34:14 -07:00
Shokunin 2943e50cb1 Lobby: re-assert ready at most once a second (no lobby_update storm)
The waiting-room pump sent set_ready every frame while the local seat was
not yet echoed as ready. Each set_ready is a lobby_update broadcast to the
whole room, so two waiting peers produced hundreds of updates; the host then
missed the seat update (seat 2 shown open, Start disabled) and, after Start,
the launch itself. Offer changes still go out immediately.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:07:08 -07:00
Shokunin 1751c6973b Netplay rb: PSX_RB_SNAP_AUDIT replay-determinism log + diff tool
Per ring save, the section CRCs (and small sections in hex) of the
boot_state blob, marked live or replay. tools/rb_snap_audit_diff.py
checks a seat's replay against its own live save and seats against each
other, naming the first differing section and bytes. Off unless set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:40:50 -07:00
Shokunin 2337f56764 Netplay rb: fix resim audit row print (else bound to the analog line)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:40:09 -07:00
Shokunin e88823ee4f Netplay rb: admit the Live tick a realign resumes into
A Live realign restores the snap at T, sets the session to T+1 and
longjmps back into the guest from the admit spin. The resume ran before
T+1 was admitted, so that frame latched no inputs and did not advance;
the next frame was then counted as T+1 and the realigned seat ran one
frame ahead of its tick label from there on (ring saves after a NACK
realign carried the next tick's clock, 2x the cycles in the first
tick). Only the realigned seat shifted, so it forked from every other
seat; with four seats this produced the "resim core diverge" splits
and the later hc-fork / hang cascade.

flush_resume now admits the tick first on the Live realign path and
keeps spinning until it can.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:03:14 -07:00
Shokunin 75dc9e84a7 CMake: register the facade GL fixtures where their SDL include and target exist
gl_texture_lod_test (#585) and gl_blend_accuracy_test sat in the
SDL3::SDL3-static block after _gl_scale_sdl_inc was unset, so they got an
empty --sdl-include and were not registered at all where the static
target is absent. Move them into the gl_scale_invariance block that
defines both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 19:18:36 -07:00
Shokunin 0e3c53720b GL: opt-in accurate blending + PS1 blend/mask accuracy test
Verifies the four PS1 semi-transparency modes, set-mask, mask check and
texel STP at 4x internal resolution with PGXP depth on and off, directly
and on the render thread, against the PS1 formulas and the software
rasterizer (new gl_blend_accuracy_test).

Finding: with the historical surface bases (fills and texels stored as
k*255/31, everything else as k*8) single-layer blends are off by one 5-bit
step in places (1312 of the test's checked pixels, e.g. B+F/4 over a fill).
[video] accurate_blending = true stores every VRAM write as k*8 so GL's
8-bit blend truncated by the readback is exactly the PS1 arithmetic; whites
of fills/texels then present as 248 like uploads already do. Default off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 19:18:16 -07:00
Shokunin ff573219bf CMake: register the facade GL fixtures where their SDL include and target exist
gl_texture_lod_test (#585) and gl_xbr_filter_test sat in the
SDL3::SDL3-static block after _gl_scale_sdl_inc was unset, so they got an
empty --sdl-include and were not registered where the static target is
absent. Move them into the gl_scale_invariance block that defines both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 19:18:12 -07:00
Shokunin b55d92124d GL: opt-in xBR edge-directed texture filter (texture_filtering = "xbr")
Filter mode 3 magnifies textures with an edge-directed rule after Hyllian's
xBR (written from the published description, no code reused): for the
texel corner nearest a sample, weighted YUV colour distances over a 5x5
neighbourhood decide whether an edge runs along the corner's diagonal; if
so the corner is cut with an anti-aliased boundary and takes the closer
side neighbour. Flat areas, straight edges, gradients, cutouts and 1:1 draws
stay nearest. Selectable from [video] texture_filtering, settings.toml,
PSX_TEXTURE_FILTER and the new texture_filter TCP command. Default unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 19:17:27 -07:00
Shokunin 09ac81e920 Netplay rb: resim audit prints sealed analog bytes
Equal buttons hid sealed-row stick differences when chasing a 4-seat
resim divergence (36 guest cycles on the last replayed tick).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:57:46 -07:00
Shokunin 55000de820 Netplay rb: an hc-fork heal is not blocked by an empty bisect window
After a resim divergence the §55 fork cap bounds the next load from
above and the §62 peer NACK floor bounds it from below. On a 4-seat WAN
race the two met (cap 3296, floor 3280): hc-fork recovery refused with
"no mutually-loadable snap", nothing else could open an episode, and two
pairs of seats finished the race on different states.

Bisecting cannot heal that fork, but a MEDIA_KF heal (which hc-fork
recovery requests) can: it transfers the initiator's pin to every
follower, so any recent mutual snap works. When the window is empty and
that heal is requested, choose_load now falls back to the uncapped
choice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:28:04 -07:00
Shokunin d9ea46249f Lobby: honour transport "ice" launches (same-NAT peers no longer dial themselves)
The lobby server starts a 2-player room the host relay cannot carry on ICE
(recomp-net-server start_transport, launch transport "ice"). This client
knew only "host" and the legacy server relay. It inferred a relay from
equal host/guest endpoints -- which is exactly what two peers behind one NAT
advertise -- and each peer dialled its own public address: the match stayed
at "linking" forever.

- psx_lobby_launch_transport.h: one pure decision (host / ice / relay /
  direct) with a unit test covering ICE, same-NAT endpoints, host relay and
  the legacy relay cases.
- PsxLobbyJoinInfo.transport_ice; the launch handler records it and never
  turns it into a relay; launch fill and resolve_use_ice start ICE p2p
  (signalled over the lobby) for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:22:15 -07:00
Shokunin 1da2055078 Runtime: build with -ffp-contract=off for cross-host determinism
Clang on arm64 contracts a*b+c into an FMA by default; x86-64 GCC without
-mfma never does. The software rasterizer's float edge/shading math then
rounded differently on a Mac and a Windows peer: +-1 colour steps on single
pixels, so VRAM (the av digest) diverged from ~sim 1800 in every
cross-machine R4 rollback race while the core digest still matched. Each
rollback baseline compare then aborted on "baseline av mismatch", and the
realigns forked the session (3P never reached the race; 4P diverged
mid-race).

VRAM dumps from the same Mac-host 2P race at 16 ticks between sim 1600
and 2080: 1-3 differing pixels in 5 of 16 frames before, 0 after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:05:26 -07:00
Shokunin a069f1ca0c Netplay rb: followers apply the dual-initiation tie-break
With three or more seats two guests can mispredict the same tick and both
BEGIN. The tie-break (lower initiator slot wins) ran only on initiators,
so a follower followed whichever BEGIN arrived first and stashed the rest.
On a 4-seat WAN race the host followed slot 3's epoch while slot 1's won;
the seats split across epochs, each waited for baselines that never came,
and the match ended after 20 s at rb_baseline.

The epoch id names its initiator, so a follower now applies the same rule
to the episode it follows (netplay_rb_concurrent_begin): yield during the
handshake and follow the winner, or drop the loser's BEGIN. A follower
seeing a new epoch from the initiator it already follows (abort and
reopen, the only case two seats can produce) is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:31:47 -07:00
Matthew Stanley 80f19e166d Keep playtest execution identity aligned with validated Windows build
The X4 co-op contract already fingerprints SPU and private audio sources. Defer the additional global execution-contract inputs, added after the approved Windows build, until a coordinated rebuild of both platforms. This preserves the owner-tested Windows artifact for playtest 2.
2026-10-09 16:58:40 -07:00