psx_mod_read_local_pad_sticks(player, out[4]) returns the mapped local
stick axes (lx, ly, rx, ry; 0..255, centre 128) without touching the
SIO sample, so a presentation plugin (a look-around camera) can read the
right stick even when the game selected a digital pad. It returns 0 and
centred axes for an invalid or disconnected player, in netplay, and
during rollback resimulation; peer input is never presentation input.
The debug-server axis override feeds it for deterministic tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reconciled with #470 (PGXP): gpu.c precise vertices take PGXP only under geometry correction and count it, then native-wide edge recovery; the PGXP triangle stat stays PGXP-only. Stubs and tests from both kept; stamps recomputed.
Master moved the textured-dot test stubs into gpu_exec_stubs.inc; the PGXP stubs move there too. pgxp_note_rect_bypass stub takes (int). Stamps recomputed.
A renderer that keeps a screen X as SX << 16 tests the right edge against
a lui-held W << 16 and the left edge with bltz on the shifted value. No
explicit [widescreen.cull] kind fits either: bltz_sites compares the whole
register with -margin, and a lui at a screen_x site was a build error.
screen_x_sites now also takes
lui rt, W -> rt = (W + margin) << 16
bltz x, outside -> taken while SX < -margin (x = SX << 16)
Both are the identity at 4:3. The helpers are defined for native code and
for overlay shards; their math is in ws_cull_edge.h. The two prototypes are
written only for a config that lists screen_x_sites, so the generated code
of every other title is unchanged.
A failed start printed "built without recomp-net, or bind/peer invalid" for every cause. A player whose system held the UDP port read first that the build had no netplay.
The library answers a refused bind, a listen address it cannot read and a peer address it cannot use with the same -1. main.cpp now does those steps again (it reads the listen address as the library does, looks up a name, binds without address reuse) and reports the system's answer. The sentence names a cause only when the system does: address in use, access denied (a port range Windows keeps for Hyper-V or WSL, a port below 1024 elsewhere), address not available. Any other error gets the number and the system's text. The peer address is named only when the listen address opened and the peer address cannot be read. The build is named only when it has no netplay.
The sentences are in netplay_exit_reason.c with unit cases; a source guard checks the wiring in main.cpp.
An online room this build hosts asks the lobby server for transport "host"
(match_caps.relay): the host carries the match on its own UDP port and
guests dial it; the server's relay (SFU) is the fallback when a guest's
probe fails (recomp-net-server WS_LOBBY.md "Host relay"). The mechanics are
recomp-net's recomp_net/host_relay.h, pinned here (lib/recomp-net b0751b7);
this engine's own lobby-client copy gets the same glue as rnet_lobby_client:
the cap (emitted only when asked), the launch's transport ->
PsxLobbyJoinInfo.transport_host (force_input_relay 0; the host accepts
first / hubs instead of dialling the guest's LAN address), each seat's
published path / path_fresh, host_relay_step() from psx_lobby_pump, and
unmap on leave / disconnect.
psx_netplay: transport_host takes the LAN / hub path instead of the "MotK
online needs the SFU" rule. main.cpp: relay_host_get/set + relay_status
for recomp-ui (Network Settings / MATCH SETTINGS / ROOM "Match Relay"),
transport_host on the launch and into PsxNetplayConfig, and the
host==guest-advertise SFU inference skipped on a host launch; all under
RECOMP_LAUNCHER_HAS_HOST_RELAY so older recomp-ui pins still build.
Compile-checked against workspace recomp-ui 58604b0 (MOTK scratch
configure: full build and link).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The hub holds the lobby seat; the game settles everything itself
(retcomm-launcher docs/NETPLAY_HANDOFF.md):
- psx_lobby_ingest_record replays the hub's seat/room/launch messages
through handle_server_json, so fill_launch answers as in-game.
- --netplay-query answers what this build's lobby would send: disc_fp,
set_ready (BIOS offer), start (default caps; settled from a room).
- set_ready/start builders and the host start-caps settle are factored out
unchanged and shared by in-game PLAY and the query.
- A handed-off match ends the process (the hub holds the room).
Two CTR processes from records, local relay: all 80 live digests equal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Many PS1 renderers drop a primitive whose depth index falls past the end
of their ordering table. Raising that limit can write past the table, so
a title can now list such guards in game.toml with the register that
carries the index and the last safe value. While a trusted mod has called
psx_mod_set_draw_distance_clamp(1), generated main-EXE code clamps that
register before the guard runs, so far geometry is kept in the farthest
slot instead of popping in later.
- Off by default and reset at every session start, so netplay and every
title that lists no sites run unchanged; with no sites the generated
code and declarations are byte-identical.
- Main executable only: the full instruction word is the guard,
generation fails on a mismatch or a load-delay hazard, captured
overlay code keeps its own code (the sites are not part of the overlay
config identity), and the dirty-RAM interpreter applies the clamp
where the game's text image holds the listed word.
- Compatibility: code_generator.cpp is a codegen-hash input, so the
codegen hash and the overlay-cache tag change for every title, sites
or not. Savestates from earlier builds are refused, and overlay shards
rebuild once on first visit. Generated code and behaviour are
unchanged for a title without sites or with the switch off.
- Tests: draw_distance_codegen_test (config rules, emission, guards,
declarations, overlay identity) and draw_distance_interp_test (store,
switch, interpreted guards); the session-reset guard covers the switch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With every drawn vertex precise, a title can still show cracks where the game
culled a face on its integer winding. R4's road emitter runs NCLIP on each
far row and skips rows whose native height rounds to 0. Natively their
neighbours meet at the same integer y. With PGXP they sit at their exact y,
and the skipped row becomes a strip of sky across the road beyond the start
gantry, at 4K and in 2P. tri_mixed cannot see this: the missing face draws
no triangle.
Precise culling (DuckStation's "PGXP culling", minimal footprint):
- While geometry correction is armed, gte_nclip computes the exact
determinant of the three SXY register shadows (pgxp_gte_nclip_precise:
live, current words, the GPU's own truncation agreement and tolerance).
When its sign is nonzero and differs from the integer MAC0's, MAC0 takes
that sign, with the rounded exact doubled area (at least 1) as magnitude.
FLAG keeps the integer overflow bits.
- It changes guest-visible MAC0, so it is a `culling` option of
psx.enhancement.pgxp, false in the builtin, with no [video] key, so netplay
(which clears every mod) never runs it. It needs geometry correction.
It is held off in every pass compared against another execution: the
speculative and replay passes, and the whole overlay shadow diff
(psx_overlay_shadow_diff_active), interpreter pass included.
- Counters whenever geometry correction is armed: nclip_precise,
nclip_disagree (exact sign differs: the culled-face crack exposure),
nclip_corrected. TCP pgxp {"culling":0|1}; env PSX_PGXP_CULLING.
Session arming in one place (src/pgxp_session.cpp, psx_pgxp_session_arm):
main.cpp's renderer setup now calls the same code the test drives. The arming
takes the mod request (reads and clears it), so a session with an empty plan
cannot inherit an earlier session's request even if the reset were skipped.
pgxp_session_test now links the real mod runtime, the builtin plugin and the
builtin manifest, and covers:
- a default-on override;
- a netplay clear, with and without the reset;
- the off switch;
- the options;
- env overrides;
- pgxp_mod_only.
[video] pgxp_mod_only (game.toml, default false): for a title that ships
PGXP through the mod, the mod is the one switch. The [video]
geometry_correction / perspective_texturing / pgxp_cpu_mode values are not
applied, from game.toml or the player's settings.toml. The launcher also
hides its Perspective textures row (has_geometry_precision = 0). That row
showed "off" while a default-on mod corrected textures, and it could keep
texture correction on after the player switched the mod off.
geom_correction also reports texture_correction and the culling switch.
R4, Helter Skelter: 28.7% of NCLIPs disagree in sign. With culling, the
ladder is gone in same-frame 4K captures (1P straight and crest, 2P VS), and
the game emits about 24% more triangles. Its packet buffers peak at 60%.
fp_identity over 12000 frames: stock, PGXP off, and PGXP on with culling off
are IDENTICAL; with culling on, the RAM writes differ from the attract race
on, while cyc/mmio/mc agree throughout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two cases that could still draw a triangle with precise and native corners:
- The rectangle shortcut bypass now needs all four vertices of an
axis-aligned textured quad to be dataflow-precise. A quad with only some
precise corners, such as a CPU-built sprite with one corner copied from
SXY, keeps the shortcut and counts as rect_partial; two triangles would
have mixed precise and native corners. R4's facade quads are fully
precise (rect_partial 0 in every R4 run).
- Under preserve-projection, RTPS now applies the GPU's truncation window
itself (pgxp_ppp_accept): an exact projection outside it is not
shadowed, the vertex keeps the IR path's shadow, which always agrees, and
it draws precise instead of being rejected to native at the GPU
(ppp_window_fallback counts these). The exact math is skipped while the
hooks record nothing (pgxp_active(): speculative passes).
Also:
- The prepare_precise_triangle comment no longer claims a native neighbour
can only disagree by the sub-pixel fraction. Under preserve-projection it
can disagree by up to the window.
- gpu_textured_dot_nw_shift_exec_test links with -dead_strip on Darwin,
as isc_store_test does. GNU --gc-sections kept it from linking on macOS,
so the rectangle cases never ran there. The test now covers no, one and
all four precise corners.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A textured quad whose four packet vertices form an axis-aligned rectangle in
integer screen space and in UV takes the 2D rectangle shortcut
(gr_draw_textured_rect), which draws the native integer position with no
perspective. That is right for a CPU-built sprite, but a GTE-projected world
quad can land on such a rectangle too: a facade seen straight on. Its
neighbours are drawn precise, so at a high internal resolution the shortcut
left a background-coloured seam along the shared edges. On R4's Helter
Skelter start straight it showed as an L-shaped sky line along a building
panel at 4K, with tolerance off, both with and without preserve projection.
When geometry or texture correction is on and any of the quad's vertices
resolves to a dataflow-precise position (pgxp_probe_precise_vertex: the same
decision as pgxp_get_precise_vertex, not counted), the quad is drawn as its
two triangles like any other world quad. A CPU-built sprite has no dataflow
shadow and keeps the shortcut. With PGXP off nothing changes. geom_correction
counts the bypassed quads (rect_bypass).
Tests: gpu_textured_dot_nw_shift_exec_test gains the quad case (PGXP off,
on without a precise vertex, on with one); pgxp_test checks that the probe
does not count; gpu_gp0_history_guard_test's gpu.c stubs gain the two new
pgxp entry points.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>