Commit Graph

232 Commits

Author SHA1 Message Date
Matthew Stanley e4e2aa7a7d runtime: add per-game radial stick response 2026-07-28 00:20:45 -07:00
Matthew Stanley 64097606fb runtime: fix game-owned display and controller mods 2026-07-28 00:04:20 -07:00
Matthew Stanley eb262c4b23 feat(mods): let a trusted plugin read its own option values
The manifest schema already carries typed, validated, launcher-rendered,
persisted options ([[option]] boolean/choice/integer), and the launcher
provider already reads them. A trusted activation plugin could not: the
callback takes no arguments, so a plugin was only ever an on/off switch.

That forced a parameterised feature to be modelled as one feature per
value, and `constraint` only expresses ordered_integer WITHIN a feature, so
those pseudo-features could not even be made mutually exclusive. A game
wanting "Widescreen: 16:9 / 21:9" or "Interpolation: 90/120/144/165/240"
had no way to express it as one entry.

psx_mod_option_value(package, feature, option, out, out_size) closes the
gap against the value the player actually committed. It returns 0 with an
empty string when the plan is not committed or the ids do not resolve, so a
caller applies its own default rather than reading "" as a selection. Ids
are explicit because registration is by plugin id alone and the callback
carries no package/feature context.

Also in this commit, from the same investigation:

* Sustained overlay-autocompile failure is now LOUD. After 3 consecutive
  failed runs the runtime prints once to stdout: what failed, the exit
  code, that overlays are staying interpreted and frame times will be far
  worse than the build is capable of, what to check, and the compiler's own
  output tail. Previously the only trace was a `fails` counter reachable
  over TCP, so a wholly-interpreted title presented as "why is this slow"
  rather than as a message. Threshold is 3, not 1, because isolated
  failures (a shard racing a capture rewrite, a transient lock) are
  ordinary and self-heal. Measured cost of the silent case on Tomba!:
  p50 frame period 105ms, vs 3.1ms once shards existed.

* The autocompile child's working directory is printed at startup beside
  cache and captures. Every relative path in overlay_autocompile_cmd
  resolves against it and it comes from game.toml's detected project root,
  so a game.toml staged outside the real root silently repoints the entire
  compile command with no other symptom.

* A build tree whose cache still carries recomp-ui's OFF for
  RECOMP_UI_ENABLE_MODS now says so at configure time. 610234a's seed only
  fires on a fresh cache, and a stale OFF is indistinguishable from a
  deliberate one, so such trees kept producing Mods-less builds across
  reconfigures - the same "reads as a stale build" failure one level up,
  and invisible. The explicit-OFF contract is deliberate so this does not
  override it; it makes the state audible.

Runtime ctest 16/16. Warning verified to fire on a tree configured with
-DRECOMP_UI_ENABLE_MODS=OFF and to stay silent on a fresh configure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 17:33:44 -07:00
Matthew Stanley 5ec343ab4f fix(bios): make the boot-skip flag mean the same thing on every BIOS
`bios_hle` (default on) is the player-facing "skip the BIOS and go
straight to the game" flag, and it did nothing under the bundled
OpenBIOS: choosing OpenBIOS sat the player in the boot animation while
the identical build on retail SCPH-1001 went straight to the game.

Root cause was ordering, not policy. The tier has two axes with
different per-image requirements:

  * boot-skip     needs only `shell_entry_phys`, works under pure LLE
                  (nothing is synthesized — the shell call just returns
                  immediately), so any image exporting it can skip
  * kernel-call    needs `deliver_event_ret`, the kernel's own
                  DeliverEvent `$ra`, or a delivered callback returns to
                  the wrong PC

main.cpp correctly refused the call axis on an image without
`deliver_event_ret` by assigning `bios_hle = false` — and then derived
`boot_skip` from that already-mutated flag. OpenBIOS omits
`deliver_event_ret` (B0 semantics unvalidated) but DOES export
`shell_entry_phys`, so an available skip was cancelled by an unrelated
refusal, and one flag meant two things depending on a BIOS detail no
player can see.

Both axes are now decided by one pure, dependency-free function,
`psx_bios_hle_plan()` (runtime/src/bios_hle_plan.c): the boot decision
reads the REQUESTED `bios_hle`, each axis is gated on only the anchor it
needs, and refusals are reported at startup instead of downgrading
silently. `psx_bios_hle_configure()` clamps `boot_skip` to
`shell_entry_phys != 0` as a backstop so the banner and `hle_dump`
cannot overstate what can fire. The main.cpp local is renamed
`bios_hle_requested` and is no longer written back from the plan — the
`session_reboot` path re-enters that block, so folding a refusal into
the request would have made a netplay rematch differ from first launch.

Also in this pass:

* A BIOS profile's `[runtime]` block was parsed into
  `BiosConfig::runtime` and read by NOTHING, and bios/OpenBIOS.toml
  carried an inert `bios_hle = false` there that read as the reason HLE
  was off on OpenBIOS. It was not. `load_bios_config` now REJECTS
  `[runtime]` in a BIOS profile: a profile states facts about an image,
  never preferences.
* `psx_icache_fastpath_test` had been unbuildable ("redefinition of
  `psx_advance_cycles`" — the test's counting stub vs the header's
  `static inline`), which blocked every runtime test registered after
  it. It now builds with `PSX_OVERLAY_DLL_BUILD=1`, the seam
  psx_cycles.h already provides for a TU supplying its own advance.

Tests: new `bios_hle_plan_test` pins the matrix, including a 64-case
sweep asserting the boot decision is independent of
`deliver_event_ret` and the call decision independent of
`shell_entry_phys`. Runtime ctest 15/15, recompiler ctest 30/30.

Validated on Ape Escape (SCUS-94423) built against this framework with
both backends linked, headless, all four cases:

  OpenBIOS default   bios_boot=skip to game; route=2 fire at vec 0x30000
                     ra=0xBFC06EA8 (the continuation OpenBIOS.toml
                     documents); in-game on screen
  retail default     bios_boot=skip to game; route=2 at vec 0x30000
                     ra=0xBFC0702C; in-game on screen; differs only in
                     bios_backend=HLE (LLE fallback)
  PSX_BIOS_HLE=0     bios_boot=real intro; ring empty (no hook)
  KEEP_INTRO=1       bios_boot=real intro; ring empty

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 16:36:48 -07:00
Matthew Stanley b699089d46 runtime: let games move display options into mods 2026-07-27 16:16:26 -07:00
Matthew Stanley 63d8f88dc5 runtime: group front-layer UI for widescreen 2026-07-27 15:54:23 -07:00
Matthew Stanley bb5fccea51 fix: make FPS telemetry opt-in 2026-07-27 11:44:31 -07:00
Matthew Stanley 79baa76d0a fix: stop overlay autocompile during shutdown 2026-07-27 11:01:50 -07:00
Matthew Stanley 2696c391b8 feat: add aspect-aware world participation 2026-07-27 10:48:41 -07:00
Matthew Stanley d116e0da3b feat(mods): add presentation interpolation rates 2026-07-27 10:40:49 -07:00
Matthew Stanley d34af54141 feat(mods): support native VBlank rate selection 2026-07-27 10:32:33 -07:00
Matthew Stanley 964cdb3870 feat(mods): expose adaptive display activation 2026-07-27 10:19:07 -07:00
Matthew Stanley fa408aae50 Merge trusted mod plugin support 2026-07-26 21:05:03 -07:00
Matthew Stanley 45dd4210cf Remove Tomba legacy hybrid controller path 2026-07-26 20:52:12 -07:00
Matthew Stanley 9683a1cf8c feat(mods): support mod-owned FMV skipping 2026-07-26 20:34:20 -07:00
Matthew Stanley 27a05dc09e fix(launcher): hide native-only aspect control 2026-07-26 20:26:28 -07:00
Matthew Stanley 99abda239f feat(mods): add pre-runtime activation plugins 2026-07-26 20:18:00 -07:00
Matthew Stanley 8b77d8a730 fix: make overlay autocompile cache-safe 2026-07-26 09:39:24 -07:00
Matthew Stanley 436fe14101 widescreen: support guarded object participation overrides 2026-07-25 23:24:41 -07:00
Matthew Stanley 6c1bd61a1f feat: derive modded discs from verified stock images 2026-07-25 21:14:27 -07:00
Matthew Stanley 316eaa44a7 feat: add versioned runtime mod packages 2026-07-25 21:14:27 -07:00
Matthew Stanley ef38122068 fix(launcher): honor CLI disc before verification 2026-07-25 21:14:09 -07:00
Matthew Stanley 986d8be94c Merge remote-tracking branch 'origin/master' into integrate/openbios-bundle
# Conflicts:
#	recompiler/src/full_function_emitter.cpp
#	recompiler/src/full_function_emitter.h
#	recompiler/src/main_bios.cpp
#	runtime/runtime.cmake
#	runtime/src/main.cpp
2026-07-25 12:31:05 -07:00
Matthew Stanley 33589d9c43 feat(bios): show the BIOS row on bundled builds and honour a chosen image
Two runtime-side changes the launcher's SYSTEM row needs.

The row was hidden whenever the linked BIOS was bundled, on the reasoning
that there was nothing to select. With OpenBIOS AND a retail image linked
that strands the player: choosing retail — and clearing back — is exactly
what the row is for. Visibility now asks the registry (is any linked
backend NOT the bundled one?) via psx_bios_has_selectable(), which is
also correct before selection, unlike psx_bios_image.

A BIOS named in settings.toml is likewise now honoured on every build.
It was previously ignored on bundled builds to stop a stale path pointing
at a foreign image, but that guard would now silently discard the only
in-app way to opt into retail. The identity gate is the real protection,
and it explains a mismatch rather than ignoring it. An EMPTY value stays
meaningful in the other direction: that is the launcher's Clear, "go back
to the bundled BIOS", so it is not adopted as a path.

Pairs with recomp-ui feat/bios-row-clear.

Not yet compile-verified against the launcher: building with
PSX_RECOMP_UI=ON on this branch fails in the recomp-ui netplay callback
ABI (main.cpp:4495), which traces to 87491cb and is inherited from this
branch's base predating master's matching integration — the same skew
that stops MMX4/MMX5 building on current master with their pinned
recomp-ui. Needs master merged in, which this branch requires before
merge regardless.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 12:23:48 -07:00
Matthew Stanley b0f514c57e feat(bios): pick the BIOS at runtime — OpenBIOS unless the player chose one
Implements docs/BIOS_SELECTION.md on top of the backend ABI. One build
now carries both images and decides at startup, so the per-BIOS build
flavour (and its three uncross-checked restatements) is gone.

  - runtime.cmake takes PSXRECOMP_BIOS_STEMS (default "OpenBIOS;SCPH1001")
    and generates psx_bios_registry.c from it, so the stem list is the
    single source of truth for what a build carries.
  - [runtime] openbios (default true) records whether a title may use the
    bundled image. Not exposed to settings.toml: it is a developer's
    compatibility finding, not a player preference.
  - Selection: an explicit choice that matches a linked image wins;
    otherwise the bundled image; otherwise prompt. A mismatched explicit
    choice is explained and falls back to OpenBIOS when the title allows
    it, instead of dead-ending.
  - validate_bios_for_launch now ASKS WHICH backend an image is, rather
    than checking against one hardcoded identity, and activates it.

Crucially, "no BIOS" means "the player chose none" — not "no BIOS file
was discoverable". The compile-time DEFAULT_BIOS_PATH is no longer
treated as a choice: it made a stray file near the executable produce a
BIOS Mismatch dialog before the bundled image was considered.

Also fixed while wiring this: the settings.toml BIOS-adoption check read
psx_bios_image.image_bundled, which now runs before any backend is
active (all-zero), so it asks the registry instead.

Verified on Tomba! with ONE binary, both paths:
  no --bios          -> OPENBIOS, no dialog, boots to in-game save
  --bios SCPH1001.BIN-> SCPH-1001 (crc 37157331, bundled=0)
and the per-image state follows the selection, not just the label:
kernel-bless window 0x500-0x4BA0 vs 0x500-0x8500, deliver_event_ret
0x0 vs 0x80001720.

Still outstanding: savestates must record the active BIOS and refuse to
load under another (kernel RAM layout differs); a player-facing way to
clear a remembered choice; retiring PSXRECOMP_BIOS_STEM/_PROFILE, which
now only feed the staleness stamp.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 12:12:33 -07:00
Matthew Stanley fdfad8f567 cdrom: fail loudly when a requested disc does not mount
Root cause of the "black screen on a .cue that works as a .bin" reports.

cdrom_init() calls iso_open() and, on failure, simply leaves the drive empty
(stat_reg = CDSTAT_SHELL). That is correct for a BIOS-only target, which runs
with no disc on purpose -- but when a game was GIVEN a disc path and the image
does not mount, the game boots against an empty drive and renders nothing. No
error, no log, just black.

The earlier validate_disc_for_launch() pass cannot catch this, because the two
sides read the disc differently: identify_disc() opens the data-track FILE
directly, so it reported a green "Disc verified" badge for cues that the
MOUNTING reader then rejected. Verified-but-unmountable was a silent state.

Demonstrated against the pre-fix reader (a8aa640) on one cue:

    lowercase/unquoted cue -> FAILS  (empty drive -> black screen)
    canonical cue          -> MOUNTS
    bare .bin              -> MOUNTS  <- why picking the .bin "fixed" it

So the symptom was never about cue-vs-bin semantics; it was cue PARSING
strictness. The old mount-side parser required the literal uppercase "BINARY"
and a quoted filename, while the verify side uppercased the line first. The
shared cue parser landed in 68667cd already fixes that class (case-insensitive
keywords, unquoted names, CRLF, case-insensitive payload lookup), which is why
all four titles now boot from either file.

This commit closes the remaining hole -- any OTHER reason a mount fails must
not present as a black screen:

- cdrom_has_disc() exposes the drive state (has_disc() was file-static, so no
  caller could tell a successful mount from an empty drive).
- main() checks it right after cdrom_init() and, when a disc path was supplied
  but nothing mounted, reports what was selected, what it was mounted as, the
  resolver's note, and that the .bin can be selected directly -- then exits
  instead of booting into black.

Also pins the regression: the lowercase/unquoted cue must MOUNT, not merely
parse, so the verify side and the mount side can never silently disagree again.
test_disc_path_resolve: 36 -> 38 checks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 00:52:55 -07:00
Matthew Stanley 68667cd58b disc+bios: either file the user picks resolves, mounts, and verifies
Two path-resolution defects where the runtime silently accepted only one of
two equally-valid spellings of the same input.

1. Disc: picking the .cue threw away the disc's TOC.

normalize_disc_path_for_launch() swapped any .cue for its same-named .bin
unconditionally, and every disc entry point (game.toml, --disc, the cached
disc.cfg, the launcher's Change Disc, the first-run dialog) went through it.
That discarded the cue's table of contents -- every CD-DA track and pregap
with it -- even though iso_reader.cpp has full multi-FILE cue support that
the swap defeated before it was ever reached. In the other direction,
picking a redump audio-track bin mounted that track as the whole disc, which
cannot boot.

resolve_disc_path() (runtime/include/disc_path.h) now canonicalizes any pick:

  .cue that resolves            -> mount the cue (TOC intact)
  .cue with a missing bin       -> the payload it still names, else a
                                   stem-matched sibling image
  .bin/.iso/.img owned by a cue -> mount that cue
  bare image, no cue            -> mount it

identify_disc() resolves through the same code, so the header/serial/CRC32
verdict is read from the data track no matter which member was picked.

While here: the mount path and the verify path had two divergent private cue
parsers (quoted-only + case-sensitive vs quoted-or-bare + case-insensitive),
so they could disagree about the same file. Both now use one parser
(runtime/include/cue_sheet.h), which additionally handles CRLF, backslash
paths, and case-insensitive payload lookup for case-sensitive filesystems.

2. BIOS: the multi-BIOS rename made configs and dump folders incompatible.

SCPH*.toml name "US-PSX-SCPH1001.BIN" while every pre-existing dump folder,
bios.cfg and tools/regen_bios.sh default name "SCPH1001.BIN". Either
combination fataled with "cannot open BIOS file" despite the right ROM being
present. resolve_bios_rom() (recompiler/include/bios_rom_alias.h) matches on
the model token -- the last '-'-separated component of the stem -- so both
conventions resolve in both directions without a hardcoded alias table.
Applied at the config loader, the positional psxrecomp-bios CLI, and each
rung of the runtime's BIOS search.

The positional CLI also derived out_stem from the raw filename, so a
region-qualified ROM would have emitted US-PSX-SCPH1001_full.c -- a name
runtime.cmake cannot link. It now derives the model token.

Also fixes SCPH5552.toml, whose rom field omitted the bios/ directory that
its two siblings have.

Tests: test_disc_path_resolve (36 checks, five dump layouts) and
test_bios_rom_alias (15 checks, both directions + negative cases), both
registered in runtime/CMakeLists.txt. Registered the pre-existing
test_iso_reader_cdda, which was wired into no build.

Verified on real dumps: Ape Escape .cue and .bin both give
SCUS-94423 / NTSC-U / crc=C6F455BC; Tomba 2's multi-track dump gives
crc=1FB8C151 and a 2-track TOC from the .cue, the data bin, and the CD-DA
bin alike.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-24 22:17:52 -07:00
Matthew Stanley a8aa640b79 runtime: name the exact CLI flag when no file picker is available
On macOS/Linux there is no native GUI file picker, and in headless mode a
picker would block, so pick_runtime_file() bails when it can't resolve the
BIOS/disc interactively. The old message ("<title> requires a command-line
path on this platform") never said WHICH flag to pass, so a first-time
from-source user on Linux hit "no BIOS selected; exiting." with no idea what
to do.

Thread the responsible flag (--bios / --disc) into pick_runtime_file and print
an actionable message with the flag and a full example command, on both the
headless and the no-GUI-picker paths. Points at the game README's "Building
From Source" for the full run line.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 13:02:44 -07:00
NyperYuhgard 2ebb2b1a6b Multi-Bios (seeds required) 2026-07-24 01:38:56 -05:00
Alex Vanderveen 09067f29dc fix title bar separator character 2026-07-23 19:05:30 -04:00
Matt Stanley 0920787d4b Merge pull request #74 from OpokXeno/fix/present-letterbox-uv-inset
present: fix short-band letterbox for native display modes + UV edge bleed
2026-07-23 13:11:58 -07:00
Henrique Guedes 6749b826d4 feat: OpenBIOS support — profile-driven BIOS recompilation
Adds PCSX-Redux's MIT-licensed OpenBIOS as a second BIOS the framework can
statically recompile, next to retail SCPH1001. Because OpenBIOS is
redistributable, a game verified against it ships its BIOS with the build —
players supply only a disc, and the launcher BIOS picker disappears.

The mechanism is general, not OpenBIOS-specific: every BIOS image gets a
bios/<STEM>.toml profile that is the single source of truth for its identity
and layout.

Recompiler
- bios/<STEM>.toml profiles: [program.image] (sha256 pin, redistributable
  flag), [recompiler.address_model] (boot-time ROM->RAM copy windows,
  kernel-bless window), install_slots, and [recompiler.runtime_exports]
  HLE anchors (0 = feature structurally unavailable on that BIOS).
- BiosAddressModel (new): validated model built from the profile, replacing
  ~9 hardcoded SCPH1001 window constants that had drifted apart across the
  emitter / discovery / game codegen. The emitted C normalize() derives
  from the same table. from_config rejects overlapping/misaligned windows
  and orderings where sequential folding != first-match. A CTest-registered
  equivalence test sweeps every RAM/ROM address against verbatim copies of
  the old helpers.
- Orphaned delay slots (the OpenBIOS-critical emitter fixes): jr/jalr
  slots are inlined before the jump resolves — previously their side
  effect was silently LOST (e.g. a case-return fragment's addiu $sp pop,
  which unwound a shifted frame and walked the exception handler's IntRP
  cursor into a wild dispatch). Orphaned branches resume at ds+4 on
  not-taken instead of re-executing the slot (a read-modify-write slot
  doubling twice broke OpenBIOS readPad). Jump targets latch pre-slot.
- MIPS-I load-delay modeling for dependent load pairs: the successor sees
  the pre-load value (OpenBIOS's cardfasttrack.s relies on it on purpose —
  "gotta break those bad emulators"). Unmodeled shapes warn at build time.
- Discovery hardening: branch-target dispatchability closure to fixpoint;
  jump tables placed at jr+8 no longer walked as code; cross-function
  continuations routed by an instruction->owner map.
- Signed MULT support (unreachable from the retail seed set; modern-GCC
  OpenBIOS emits it freely), latency-modeled like MULTU.
- Seeds from ELF symbol tables + data-section code-pointer harvest
  (tools/gen_bios_seeds_from_elf.py) — a source-built BIOS needs no
  disassembly pass. 646 OpenBIOS seeds, incl. 5 empirical dispatch-miss
  seeds.
- Stemmed outputs (<out_stem>_full.c / _dispatch.c) so two BIOSes coexist
  in one out_dir. tools/regen_bios.sh is profile-driven (--config).

Runtime
- psx_bios_image: the generated dispatch now self-describes the BIOS it
  was compiled from (identity: size/CRC32/SHA-256/wordsum; kernel-bless
  window; HLE anchors; bundled flag). Runtime SCPH1001 hardcodes removed.
- Launch check is a hard identity gate (a mismatched image under
  statically recompiled code is a guaranteed wild jump). Bundled builds
  hide the BIOS picker and treat a missing/tampered bundled image as an
  installation defect.
- Faithfulness fixes surfaced by OpenBIOS's tighter timing loops (all
  global, argued in code comments, regression-tested against SCPH1001):
  - SIO pad RX is cycle-paced like the memcard path (RX_RDY at
    shift-complete, ACK +170); total ack-IRQ latency unchanged.
    PSX_SIO_PAD_SYNC_RX=1 restores the legacy synchronous path for A/B.
  - SPUSTAT derived from SPUCNT instead of permanently-asserted busy.
  - GPUSTAT.13 (interlace field) alternates per vblank while interlaced,
    pinned 0 in progressive.
  - CD Init(0x0A) completes in ~3.9 ms instead of borrowing Beetle's
    34 ms Reset figure (OpenBIOS's short bounded retry loop livelocked).
  - Exception EPC guard accepts ROM-window block leaders, not just RAM.
- New TCP debug commands (documented in TCP_COMMANDS.md): bios_info,
  s3_smear_watch, callret_watch; write-trace ring gains $s0-$s5.

bios/openbios.bin is vendored (MIT — notice in bios/OpenBIOS.LICENSE,
attribution in THIRD_PARTY_ATTRIBUTION.md, source pin + build recipe in
bios/OpenBIOS.toml). The emitter fingerprint digest is now content-only:
existing generated/*.emitter.sha will read STALE once; re-run
tools/regen_bios.sh.
2026-07-23 17:10:23 -03:00
Matthew Stanley f86087ad94 Remove stale RmlUi launcher remnants 2026-07-23 00:29:22 -07:00
Alex Vanderveen 980d170850 fix(netplay): match recomp-ui join(guest_bind) ABI
UI now passes an in/out guest UDP bind (prefer 7778). Accept the 4th
argument, reject :0, and stop advertising ephemeral 0.0.0.0:0 on online
join so Windows CI links and online guests get a dialable endpoint.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-23 01:50:15 -04:00
Alex Vanderveen c34c1460e0 Merge remote-tracking branch 'refs/remotes/origin/master' 2026-07-23 01:27:44 -04:00
Alex Vanderveen 1347e49dfc patch FMV playback, fix artifact flicker on cutover to new video 2026-07-23 01:22:11 -04:00
Matthew Stanley cece00296a feat: add adaptive native widescreen
Add a resize-driven aspect option, keep native-wide culling and spawning hooks engaged across live aspect changes, and widen host GP0 draw-area pruning only for the active wide framebuffer.

Credit OpokXeno's independently discovered draw-area pruning fix documented in PR #73.

Co-authored-by: Opok <novelasevilliousesp@gmail.com>
2026-07-22 21:59:31 -07:00
Alex Vanderveen 1a73e9826c patch hosted lobby launch game 2026-07-23 00:44:53 -04:00
Opok d6a52cb9d8 present: fix short-band letterbox for native modes + UV edge bleed
Two present-path fixes:

1. The short GP1(07h)-band letterbox (MotK 128-line FMV) fired for any
   frame under 240 lines, which also catches games whose native display
   mode is short full-time (Xenogears runs 320x216): every 4:3
   menu/load and every FMV got a ~10% vertical squash. No numeric
   threshold separates the cases (216 < 224 too), so split by purpose:
   15-bit game frames never shrink (adjustment removed from
   gl_renderer_present_vram), and the FMV paths keep the correction
   only for genuinely windowed video bands (gl_renderer_present, and
   the SDL path gated on pin_43 && depth24_frame) with the band under
   80% of the 240-line field. MotK's 128-line FMV keeps its aspect fix;
   native 216/224-line display modes fill the rect as before.

2. present_target_quad mapped quad corners to texel edges, so with
   GL_LINEAR the outermost dest pixels blend the border texel with
   neighboring VRAM outside the content rect (visible 1-2px edge
   stripes with linear present filtering, e.g. menu frames showing the
   adjacent framebuffer). Center-map the UVs with a half-texel inset;
   interior sampling is unchanged. Covers both callers (canonical VRAM
   present and wide-FBO present).
2026-07-23 00:35:36 +02:00
Matthew Stanley 87491cb035 launcher: integrate recomp-ui netplay flow 2026-07-21 21:39:28 -07:00
Matthew Stanley 0527806ce3 Repair netplay PR rebase for MotK bring-up 2026-07-21 21:38:05 -07:00
Alex Vanderveen fe7913cbca Fix psx_cycles / audio linkage for MotK CI.
Export service_to_now, rename advance to *_slow for the header inline,
and give g_audio_host_rate C linkage.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-21 21:38:05 -07:00
Alex Vanderveen 7cf9958135 FMV optimizations 2026-07-21 21:38:04 -07:00
Alex Vanderveen 31015ceab1 feat: launcher polish and recomp-net lobby netplay
Wire MotK-style lobby (recomp-net submodule, WS client, match_caps),
netplay delay-sync, and launcher UX so host sim settings stay aligned
across peers.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-21 21:37:57 -07:00
Matthew Stanley 9228f32c00 runtime: keep Vulkan opt-in and remove old launcher
Remove the in-tree launcher implementation and its submodules now that game projects use recomp-ui for launcher support.

Keep Vulkan hidden from the launcher unless a game explicitly sets [video] offer_vulkan = true, and clamp stale Vulkan settings back to OpenGL when it is not offered.

Co-authored-by: Martin Penkava <mpenkava1337@gmail.com>
2026-07-21 16:31:36 -07:00
Steve Gilissen 1ae1cfdaf6 runtime: add mod frame hooks
Add a small generic frame-hook surface for statically linked mod code. Mods can
register C callbacks with mod_register_frame_hook(); the runtime invokes them
after normal input sampling and before presentation/widescreen frame work.

Expose the SDL window symbol with C linkage so linked mod code can integrate
with SDL APIs such as window grab control.
2026-07-21 15:45:45 -07:00
Opok 4412bc7a7d widescreen: add per-primitive X-reject bound-load cull sites
Add opt-in [widescreen.cull] xclip_load_sites: a configured `lw rt,off(rs)`
loading a per-primitive X-reject bound (sltu-compared against ANDI-masked
u16 screen X) routes through psx_ws_xclip_bound() - INT32_MAX while the
margins are revealed (reject disabled; the wide-surface scissor clips the
overflow, wrapped off-left u16 coords pass), vanilla value at 4:3. Matched
native codegen emit and interpreter hook; overlay ABI v18 forwarder (NULL
preserves the vanilla loaded value); codegen ver 8. Parser, codegen and
overlay-mismatch regression coverage.

Split from #49: replaces the xclip_globals RAM poke with a recompile-time
instruction transform so native shards and the interpreter behave identically.
2026-07-21 15:02:20 -07:00
Matthew Stanley 2ba20f5af7 runtime: make FPS stderr logging opt-in 2026-07-21 14:53:47 -07:00
Opok a5fdf1ad9a widescreen: add side frustum-plane normal-X cull sites
Add opt-in [widescreen.cull] plane_nx_sites: a configured `lw rt,off(rs)`
loading the X component of a side frustum-plane normal (sign-tested per
corner, dot = nx*px + nz*pz) routes through psx_ws_plane_nx() — scaled by
the inverse aspect factor (4*den)/(3*num) while the margins are revealed,
widening the plane cone by exactly atan((3*num)/(4*den)*tan(theta));
identity at 4:3. Matched native codegen emit and interpreter hook; overlay
ABI v17 forwarder (NULL preserves the vanilla normal); codegen ver 7.
Parser, codegen and overlay-mismatch regression coverage.

Split from #49: replaces the plane sign-test RAM pokes with a
recompile-time instruction transform so native shards and the interpreter
behave identically.
2026-07-21 14:38:55 -07:00
Matthew Stanley e4e27f02b5 runtime: optimize MDEC FMV hot paths
Reconstruct the shareable pieces from PR #58 onto current master: add contiguous MDEC DMA input bursts, reduce per-pixel/per-byte MDEC output overhead, add sparse IDCT fast paths, and keep the MDEC activity stamp sane across rematches.

Also quiet the heavy debug trace recorders while MDEC video is active so debug builds can query state without starving FMV playback.

Regression testing was done with non-debug OpenGL builds across multiple FMV-heavy titles. Debug-tool FMV playback was also exercised with the quiet gate enabled.

Co-authored-by: Alex Vanderveen <alex@technicallycomputers.ca>

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-21 14:29:38 -07:00