Commit Graph

437 Commits

Author SHA1 Message Date
Matt Stanley 15e5eb3ead Merge pull request #205 from mstan/split/pr169-fmv-filter-user-setting-clean
presentation: add opt-in FMV filter setting
2026-08-24 20:29:11 -07:00
Matthew Stanley 15099cc5b6 presentation: add opt-in FMV filter setting 2026-08-24 20:28:43 -07:00
Alex Vanderveen ffa11fa2dc cdrom: a Read that continues the current stream must not restart the drive
Legend of Mana's land-creation effect rendered as hard-edged coloured
wedges instead of a soft bloom. The cause was in the CD-ROM controller,
not the renderer.

The game streams a file by issuing Setloc(next)+ReadN roughly 10,800
guest cycles after each sector arrives. Every ReadN called
start_read_stream(), which clears the sector buffer -- destroying the
sector that had already been delivered and announced but not yet drained
by the guest. Measured at the palette load:

  buffer      psx-runtime   DuckStation
  0x0E1F18      125111        125111
  0x0E2718      125113        125112   <- the effect's colour palette
  0x0DCF18      125113        125113

Sector 125112 carries the effect's 5-colour palette (0x0888F8 =
(248,136,8), 0xB0F8F8 = (248,248,176), verified against the disc image).
Destroyed before the guest could take it, its buffer received 125113's
raw file bytes instead, and the colour routine faithfully scaled those
into 216 distinct vertex colours -- the wedges. The table now holds 5.

DuckStation only calls BeginReading -- the function that clears the
buffers and re-arms the read-start latency -- when the request actually
moves the drive:

  if ((!setloc_pending || setloc_position.ToLBA() == GetNextSectorToBeRead())
      && (drive_state == Reading || ...))
    "Ignoring read command with pending/same setloc, already reading"
  else
    BeginReading();

A Read issued while the drive already streams the sector the pending
Setloc names is the game saying "keep going", not "start over". The
command is still ACKed with the same INT3; only the restart is skipped.

Two things fall out of it. The un-drained sector survives, so the game
receives the consecutive sectors it expects (109,110,111,112,113 rather
than 109,111,111,113,113). And each request stops paying
initial_read_delay_cycles() again: inter-sector gaps drop from ~456,000
cycles to 225,792 -- exactly the 2x cadence -- so this load takes 2 guest
frames instead of 13, matching DuckStation. int1_pended goes 10 -> 0.

Also included, developed alongside and verified in the same runs:

  - An 8-slot sector buffer ring, as the hardware has (nocash;
    DuckStation NUM_SECTOR_BUFFERS = 8). A delivered sector fills the
    NEXT slot and the INT1 that announces it latches the read slot, so a
    sector arriving mid-drain no longer clobbers the FIFO. The read
    pointer follows the slot each INT1 announces, captured when the INT1
    is RAISED: this drive free-runs, so latching "newest" at
    presentation time handed the guest a sector seven ahead of the one
    it asked for.
  - The seamless no-IRQ refill advances the read pointer, because it is
    a continuation of the drain already in progress. Without it a
    multi-sector DMA starves mid-transfer (measured: 1,128,960 starved
    drains).
  - A pended data-ready is released on a short schedule rather than
    inside the guest's ack store, matching DuckStation's
    QueueDeliverAsyncInterrupt.
  - Flow control for accelerated (non-1x) reads: never overwrite a
    sector the guest has not consumed while running faster than
    hardware, capped at the authentic sector period. Inert at authentic
    timing.

New cdrom_state counters ring_starved and ring_dropped, plus
cdrom_timing_dump for per-sector deadline/delivery records, are the
regression tripwires: ring_starved and int1_lost must stay 0.

Verified on Legend of Mana (SLUS-01013): the effect renders correctly,
the palette table holds 5 colour words, sectors arrive at the 2x
cadence, and no notification is lost.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 20:05:08 -07:00
Matthew Stanley 84a901289d mods: make bezel artwork user-selected 2026-08-24 17:03:52 -07:00
Matthew Stanley 9ba703e5ed Merge remote-tracking branch 'origin/master' into split/pr169-presentation-bezel
# Conflicts:
#	runtime/runtime.cmake
2026-08-24 15:16:40 -07:00
Matthew Stanley c8e3dac678 presentation: expose bezel artwork as a mod 2026-08-24 15:15:15 -07:00
tetrisgm c78a8511b7 present: bezel artwork behind the frame ([video] bezel)
A still image drawn behind the game, filling whatever the letterbox or
pillarbox leaves over -- the trick vertical shmups use on horizontal displays,
where the dead space carries artwork so it reads as designed rather than as a
limitation. MAME calls the same idea bezel artwork.

Drawn across the whole drawable before the game quad, which is simpler and
safer than filling two margin strips: no margin arithmetic, it covers pillarbox
and letterbox alike, and the frame paints over the centre.

The reason this is the right shape and present_edge_fill was not: edge fill
stretches one edge COLUMN of the live frame across the margin. That suits a flat
menu backdrop and turns track geometry into horizontal smears, so it needs to
know whether the current frame is a menu -- a question it answers with
force_4_3, which is only ever set by the widescreen path. A still image has no
such question. It looks the same in a race and in a menu, which is the point.

Loaded after the GL context exists (the first attempt sat next to tex_pack_init,
which runs before the window, so g_gl_active was false and it silently never
loaded). Decoded with stb_image, already linked for the HD texture pack. A
relative path resolves against the disc directory, matching the pack convention,
so artwork can sit beside the disc.

Verified on a 7680-wide maximised window with 4:3 content: margins that were
pure black now carry the image on both sides, with the frame untouched in the
middle.

Known limits: the image is stretched to the drawable, so an aspect that does not
match the window will distort (cover-crop is a small follow-up), and this has
only been exercised in 4:3 pillarbox, not with the widescreen mod active.

For WipEout 3 the team wallpapers by MotorcycleEmptiness
(awesome-wipeout.github.io) suit this exactly; the shipped default is a
placeholder gradient used to verify the path, not final art.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 13:33:58 -07:00
tetrisgm e301a57279 debug: expose savestate completion receipt
(cherry picked from commit c7a3d59e1a)
2026-08-23 13:12:47 -07:00
Matthew Stanley ad55fecdcf Gate WipEout local viewport native-wide to split frames 2026-08-15 22:53:24 -07:00
Matthew Stanley 41c232fb47 Fix precise NCLIP on PGXP-backed GTE shadows 2026-08-15 21:46:47 -07:00
Matthew Stanley bdd7ade6e8 Merge remote-tracking branch 'origin/master' into codex/netplay-local-viewport 2026-08-15 21:40:56 -07:00
Matthew Stanley b2eeb0f866 Add opt-in netplay local viewport extraction 2026-08-15 20:51:23 -07:00
Matthew Stanley 200b5bf003 Add opt-in widescreen culling keep hooks 2026-08-15 19:42:43 -07:00
Matthew Stanley 38bdeeaade pgxp: emit hook macros + dual build targets + flavor-namespaced caches (phase 2)
Emitter side of the value-propagation engine (ENHANCEMENTS.md G1.10):

- recompiler/src/pgxp_hook_emitter.{h,cpp} (new, in the codegen-hash
  surface): ONE hook grammar shared by BOTH emitters, wrapping loads/
  stores/COP2 transfers and precision-carrying ALU ops (moves, add/sub,
  or-merge, shifts, LUI, HI/LO, muldiv) with PGXP_*() macros. Clobberable
  sources are captured into locals before the statement. Precision-
  destroying ops (AND/XOR/SLT/...) are deliberately unhooked - validate-
  on-read drops their stale shadows for free.
- code_generator.cpp: central append in translate_instruction; LWC2/SWC2
  emissions capture the RAW loaded/stored word (a masked GTE register
  write must validate against the word as loaded).
- strict_translator.cpp: translate() wraps translate_impl()'s c_code with
  the shared grammar; deferred load-delay variants hook the psx_ldd temp.
- BIOS regenerated, fingerprints verified CHANGED (SCPH1001 + OpenBIOS;
  42.6k/12.2k macro sites). Base runtime + 33/33 tests green against it.
- Gating: macros expand only under -DPSX_PGXP=1. runtime.cmake grows a
  PGXP option on psxrecomp_add_runtime_target (defines PSX_PGXP=1 +
  PSX_OVERLAY_FLAVOR=2, _pgxp exe suffix) and PSX_PGXP_VARIANT to build
  the sibling target from the same generated tree (framework runtime +
  game wrapper). No ABI version bump needed: the appended PGXPHooks
  table is NULL-tolerant and the flavor half of the ABI tag rejects any
  host/DLL flavor mix.
- Shard caches: flavor folded into the cache tag (cg<V>_<hash>_gc<hash>
  _f<flavor>) in overlay_loader.c + compile_overlays.py so base and pgxp
  caches never share a directory; compile_overlays.py passes
  -DPSX_PGXP=1 when flavor bit 2 is set; overlay preamble forwards
  psx_pgxp_* through the table. PSX_OVERLAY_CODEGEN_VER 9 -> 10.
- Env overrides PSX_GEOMETRY_CORRECTION / PSX_PERSPECTIVE_TEXTURING /
  PSX_PGXP_CPU_MODE (validation path: free-running headless boots arm
  from the first vertex); census grew produced/swc2_stores and the pgxp
  verb reports suppress/active gate state.

Validated: PSXRecomp_pgxp.exe boots the recompiled BIOS with hooks armed
(engine active, per-vertex lookups flowing; the BIOS shell has no RTPS
content so produced=0 there is correct - coverage measurement moves to a
3D title in phase 3).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 19:03:36 -07:00
Matthew Stanley 2ba74f3b4b pgxp: land clean-room value-propagation engine (phases 0+1, issue #92)
The G1.2/G1.3 mechanism: per-word RAM/scratchpad + per-GPR + per-GTE-reg
shadows carrying sub-pixel projections BY PROVENANCE, validated against
the actual guest word on every read (never guessed from rounded position
- the measured 93.1%-ambiguous G1.4 dead end). Clean-room from psx-spx +
public design docs; vendored duckstation (CC BY-NC-ND) and beetle-psx
(GPL) trees remain black-box oracles only - no code ported.

- runtime/src/pgxp.cpp + pgxp.h: engine, per-vertex GetPreciseVertex
  (dataflow -> ambiguity-gated position cache -> native) with truncation
  agreement + tolerance safeguards; stats; test accessors.
- pgxp_hooks.h: 5-entry hook funnel; PGXP_*() macros no-op unless
  -DPSX_PGXP=1 (codegen-time gate over ONE generated tree); appended
  PGXPHooks* table member in OverlayCallbacks (ABI bump ships with the
  Phase-2 emitter change).
- Both interpreters wired: loads/stores/COP2 + precision-carrying
  arithmetic (moves, add/sub, or-merge, 16-bit shifts, LUI, HI/LO).
- gte.cpp: RTPS feeds the GTE reg shadows; retired s_precise_sxy FIFO,
  hashed s_precision_store, and plain-store invalidation (redundant
  under validate-on-read; store hot path gets 3 calls back).
- gpu.c: prepare_precise_triangle rewritten per-vertex; perspective UVs
  share the same provenance and compose per triangle.
- [video] pgxp_cpu_mode / pgxp_tolerance keys; TCP pgxp verb live-
  toggles geometry/texture/cpu_mode/tolerance; geom_correction census
  grew the pgxp dataflow counters (G1.9 gate metric).
- tests: new pgxp_test (registered); gte_register_access_test updated
  to precise invalidation contract. 33/33 enabled tests green.

Also fixed while landing (both broke a fresh master tree):
- runtime.cmake: once-only psx_game_version.txt stamp (multi-target
  file(GENERATE) collision broke every fresh single-config configure).
- test_overlay_capture_retry: SDL3 rot (SDL_SetMainReady gone, inverted
  SDL_Init check) -> psx_sdl_init.

Tracked as beads-eio.3.48; postmortem + this landing: ENHANCEMENTS.md
G1-G1.10.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 18:34:47 -07:00
Matt Stanley d4cc25dd4d Merge pull request #144 from Alexbeav/workbench/libcrypt-sbi-v1-20260815
runtime: add SBI subchannel replacements for LibCrypt
2026-08-15 16:00:04 -07:00
Matthew Stanley 3de7f9cb52 Bump boot-state version for SBI subchannel state 2026-08-15 15:35:21 -07:00
Alexandros Mandravillis 992f5b0bf0 build: keep SDL3 main implementation in one translation unit 2026-08-15 12:19:16 +03:00
Alexandros Mandravillis cebe738fd1 runtime: add SBI subchannel replacements for LibCrypt 2026-08-15 12:15:23 +03:00
Matthew Stanley 32c6b34986 fix(runtime): clarify PSX overlay controls 2026-08-14 19:12:37 -07:00
Matthew Stanley cab7d00f9a Fix PSX launcher hotkeys and audio persistence 2026-08-14 15:22:29 -07:00
Alex Vanderveen a9a61b4d47 Merge origin/master into feat/rbengine.
Keep recomp-net at occupied_mask (d65918f); master's pin was an ancestor.
Brings in rewind/save-state controls from master.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 02:57:25 -04:00
Alex Vanderveen 31e18d1e9c netplay fmv patch again 2026-08-13 17:38:22 -04:00
Alex Vanderveen fa9b5e027d rb patch cross platform + post fmv 2026-08-13 16:37:22 -04:00
Alex Vanderveen 853e5808c4 post fmv heal + cross os pacing and rb engine bump 2026-08-13 15:20:40 -04:00
Alex Vanderveen 6640c331b6 post FMV hotfix 2026-08-13 14:46:29 -04:00
Alex Vanderveen 9253f229a1 fmv heal patch + appicon 2026-08-13 10:37:18 -04:00
Alex Vanderveen 5d61770322 post FMV heal patch 2026-08-13 10:10:17 -04:00
Alex Vanderveen 4274a76a19 fmv hardening 2026-08-13 01:28:56 -04:00
Alex Vanderveen 9482615b29 Pass lobby occupied_mask into recomp-net for sparse seats.
Bumps recomp-net so empty lobby seats no longer block READY/admit; launch
builds the mask from seated members (online + LAN).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-12 22:37:08 -04:00
Matthew Stanley cee9b97239 Add PSX rewind and save-state controls 2026-08-12 11:12:24 -07:00
Alex Vanderveen a42dd63b61 overlay rewind 2026-08-12 04:34:41 -04:00
Alex Vanderveen add8ea46e0 update overlay ui 2026-08-12 04:07:18 -04:00
Alex Vanderveen 554476457f rewind support 2026-08-12 03:51:50 -04:00
Alex Vanderveen 31e16f4e40 chore: update submodule 2026-08-12 03:32:42 -04:00
Alex Vanderveen 1c070dfe40 chore: bump recomp-net + retcomm-rbengine 2026-08-12 03:25:31 -04:00
Alex Vanderveen db24878132 chore: update submodule 2026-08-12 02:54:48 -04:00
Alex Vanderveen a73a5c1093 rewind feature 2026-08-12 02:42:30 -04:00
Alex Vanderveen 3acb8f6d59 Integrate retcomm-rbengine as shared rollback host policy.
Wire lib/retcomm-rbengine beside recomp-net and replace MotK in-tree sched,
hash_confirm, input hist, snap ring, and rb_post with thin PSX shims so the
ecosystem can share invent/pacing policy outside each title facade.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 23:52:31 -04:00
Alex Vanderveen 12f4e3281e lobby rematch & bios hotswap stability 2026-08-11 18:59:19 -04:00
Alex Vanderveen 599d326dd0 default multitap and multitap hack, and savestate optimizations 2026-08-11 14:20:26 -04:00
Alex Vanderveen 61279a9d01 bios select and rematch pathing fix 2026-08-10 20:44:18 -04:00
Alex Vanderveen 2918a4ef0c Merge branch 'master' of https://github.com/mstan/psxrecomp 2026-08-09 22:25:24 -04:00
Alex Vanderveen 47d1bf147b stomp mods in netplay 2026-08-09 22:24:48 -04:00
Alexandros Mandravillis 63db8f430d keybinds: dual bindings per control (primary + alt, comma ini syntax) 2026-08-09 19:07:55 -07:00
Alex Vanderveen 1bbda282a2 ungate ape escape patch, doesn't break determinism 2026-08-09 14:27:24 -04:00
Alex Vanderveen 4338ed9207 document ape-escape memcard patches 2026-08-09 00:39:32 -04:00
Alex Vanderveen d0bd40ceea Merge branch 'integrate/game-codegen-hooks-into-rollback' of https://github.com/mstan/psxrecomp into integrate/game-codegen-hooks-into-rollback 2026-08-09 00:36:09 -04:00
Alex Vanderveen 251456ee86 fix ape-escape 2026-08-09 00:36:06 -04:00
Alex Vanderveen 3aa626eaf3 fix PSX_BSS section attribute for Mach-O / macOS
Apple Clang requires segment,section form; ELF .bss breaks Bomberman World CI.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 23:02:52 -04:00