Commit Graph

466 Commits

Author SHA1 Message Date
Matthew Stanley d2e1629aca Review integration: review/717-residency-20261024
# Conflicts:
#	runtime/src/debug_server.c
#	runtime/tests/test_duckstation_texture_pack.cpp
2026-10-24 09:28:21 -07:00
Matthew Stanley 7a0ba0e509 Review integration: review/703-encoder-lifecycle-20261024
# Conflicts:
#	docs/TCP_COMMANDS.md
2026-10-24 09:27:56 -07:00
yacineolivier-maker aed7b1be1e HD textures: filtered sampling, a cache sized for a whole pack, indexed keys
- Filtered replacement sampling: with any texture filter but nearest, the
  replacement is sampled through its mip chain (textureGrad), the footprint
  clamped to the primitive's own UV bounds so atlas neighbours cannot bleed
  in. Texture windows (repeat) and UVs outside the page keep hd_texel.
- Edge clamp: an out-of-bounds sample clamped to the primitive's UV bounds
  takes the edge's sub-texel position too (keeping the outside fraction read
  a 1 px sliver of the next glyph at sprite borders).
- GL cache: 4096 slots and a PSX_HD_GL_CACHE_MIB budget (default 2048 MiB)
  with a direct-mapped hint table; a 4x pack of one race scene is several
  hundred textures, and the old 256 slot / 128 MiB cache re-uploaded and
  re-mipped them every frame.
- Invalidation: duck_texture_pack_invalidate returns early when a native
  write touches no residency or cache entry (almost every draw under HD
  authority lands on the frame buffer), and retire_unused walks the
  residency lists once instead of once per source.
- Indexed replacements: a TTTTTTTT-index.png key resolves through the palette
  in use at draw time, for packs whose textures are shared by several CLUTs.
- Debug: TCP {"cmd":"hd_uploads","x":..,"y":..} lists the texture uploads
  covering a VRAM point (pack authoring).

Measured with a full HD HUD and course pack for R4: steady 60 fps in races
where the old cache dropped to the high 20s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rkyma8rbyPiccs9n2bkyG6
2026-10-24 09:04:34 -07:00
Matthew Stanley 7f0f77b9e1 WIP: video recorder, offscreen window, input_route axes
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 08:52:20 -07:00
Matthew Stanley 182ea3d8d3 Merge branch 'review/718-fmv-safety-20261024' into review/psx-prs-20261024
# Conflicts:
#	runtime/src/debug_server.c
2026-10-24 08:52:19 -07:00
Matthew Stanley 6640236dd8 Bound HD movie allocations and join decoder workers at teardown
Reject invalid index products and unsafe paths, cap encoded/decoded residency, recover allocation and partial worker-start failures, release GL movie textures with their context, and serialize diagnostics through the presenter. Real SDL decode/restart/failure test passes and SDL2 C seam compiles warning-free. Guest/present timestamp ownership remains a separate design gate. Tracking: beads-eio.3.388.
2026-10-24 08:51:46 -07:00
yacineolivier-maker 2d03e09d60 FMV: HD replacement of 24-bit movie frames (gpu_fmv_hd, fmv_hd/ packs)
An optional pack replaces a title's MDEC movies with high-definition frames
while the game keeps its own audio, timing and skip control:
- fmv_hd/<movie>/index.bin: header, then a luminance signature per source
  frame; fmv_hd/<movie>/frames/NNNNNN.jpg: `sub` HD frames per source frame.
- Every presented 24-bit frame is matched by signature (mean absolute
  difference), first around the last recognised frame, then over the whole
  pack when tracking is lost; a near global match is also tried a few lines
  up or down (each movie has its own vertical placement on screen) and the
  offset kept. Worker threads decode the upcoming frames ahead into a small
  RGBA ring.
- Format FMVHD002 records the native display the movie is shown in and its
  position, so the HD frame lands exactly where the original was.
- The last HD frame is held briefly when the next one is not ready or not
  recognised (no flicker back to the 4:3 original mid-movie), and is
  uploaded to the GPU only when it changes.
- Debug: TCP {"cmd":"fmv_hd"} reports the pack, the matches and lost frames.
No pack: no effect. Pack frames are the user's own upscale of their own disc;
nothing is distributed with the runtime.

Used for R4's intro and ending movies (SeedVR2 upscales, 25 fps sources).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rkyma8rbyPiccs9n2bkyG6
2026-10-24 08:34:12 -07:00
Matthew Stanley 84ba185176 Publish cadence through shared video telemetry without reader races
Use a bounded atomic snapshot and presenter-owned reset across all backends. Readers sort their own snapshots; report host API completion cadence. Add deterministic and concurrent reader/reset coverage.
2026-10-24 08:32:29 -07:00
yacineolivier-maker b6b3578282 Debug: perf command, the cadence of real swaps (what the player sees)
The GL swap records the interval between real swaps (ring of 4096, single
writer). TCP {"cmd":"perf","window_ms":N,"reset":0|1} returns count, fps,
mean / p50 / p95 / p99 / max interval and hitches (intervals over 1.5x the
median) for the last N ms; independent of guest vblank counting, so frame
generation, pacing and driver stalls are all visible. PSX_FPS_TELEMETRY adds
the same summary to the window title.

Used to measure R4 runs on a test bench (60.0 fps, p99 18 ms after the
cadence fix of the video-standard switch).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rkyma8rbyPiccs9n2bkyG6
2026-10-24 08:26:31 -07:00
Matthew Stanley 910c47602c Merge branch 'master' of https://github.com/RetroPortingToolKit/psxrecomp into fix/held-746-20261022
# Conflicts:
#	runtime/src/main.cpp
2026-10-22 18:01:49 -07:00
Matthew Stanley a2359ca095 Merge remote-tracking branch 'origin/master' into fix/held-737-20261022
# Conflicts:
#	docs/TCP_COMMANDS.md
#	generated/OpenBIOS.emitter.sha
2026-10-22 17:21:40 -07:00
Yves ebca6c5aaf Report XR readiness without the debug server 2026-10-23 06:53:57 +08:00
Matthew Stanley 182073bca9 Integrate current master and complete GPU fixture APIs 2026-10-22 15:35:38 -07:00
Matt Stanley e4301fb8fa VR: diagnose ambiguous XR origin poses without recentering 2026-10-22 15:34:35 -07:00
Yves 510560e599 Preserve precise eye poses and clip near-camera geometry
Co-authored-by: Yves <calibratedbeats@gmail.com>
2026-10-23 05:45:20 +08:00
Yves 689f3b348e Report ambiguous XR origin diagnostics without recentering
Co-authored-by: Yves <calibratedbeats@gmail.com>
2026-10-23 05:37:28 +08:00
Shokunin b1ce3be00a Live resolution: truthful restart contract per backend
Keep the requested internal resolution / supersample (shown and saved)
apart from what the backend last applied. When the live change is
unavailable (no GL, windowed high-resolution mode, CPU-authoritative dual
rendering, no context) or refused, the session scale is left as applied and
PSX_VIDEO_RESTART_RESOLUTION stays set, in both psx_video_apply_live and
the menu's restart bits (psx_host_video_restart_bits_backend, unit-tested
for the capable and refused cases). video_set rejects invalid presets and
factors without applying or saving, and reports applied values, capability
and pending restart. TCP command index regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 14:20:55 -07:00
Matthew Stanley 6c9d618f1f Validate synthetic pose requests before applying them 2026-10-22 14:07:34 -07:00
Yves 18d52c8bfb Add debug synthetic head translation and pitch
Co-authored-by: Yves <calibratedbeats@gmail.com>
2026-10-23 03:26:53 +08:00
Yves 4419d234ab Reuse one guest checkpoint per stereo pair
Co-authored-by: Yves <calibratedbeats@gmail.com>
2026-10-23 03:24:41 +08:00
Yves 0cdf95957c Bound stereo shedding and recover at the display deadline
Co-authored-by: Yves <calibratedbeats@gmail.com>
2026-10-23 03:24:01 +08:00
Yves 8400b55dc0 Add opt-in stereo and XR frame attribution
Co-authored-by: Yves <calibratedbeats@gmail.com>
2026-10-23 03:22:42 +08:00
Shokunin 8003db07de Live resolution: keep the legacy factor as booted; video_set debug command
psx_video_current reported the live scale as the legacy supersampling
factor, so a second live change looked like a legacy edit and asked for
a relaunch. Report the boot value. Debug-tools builds gain video_set
(internal_resolution, supersample) to drive the live path in tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 10:55:28 -07:00
yacineolivier-maker 030a50415e Mods: named debug commands (psx_mod_register_command, TCP mod_cmd) (#711) 2026-10-21 14:45:08 -07:00
Matthew Stanley 665f7eb823 Guard session_restart debug command in no-debug-tools builds
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 18:27:56 -07:00
Matt Stanley 916da6f80d Integrate shared disc tray with the maintained menu provider (#652) 2026-10-19 18:13:38 -07:00
Matt Stanley c0ffd6d4c5 Support guarded mod address adapters and atomic extended memory cards
Extract the audited adapters and card transport onto master without the held selector/menu stack. Keep adapters and codecs tied to mod activation, use overlay ABI 33, retain rejected-load protection on codec registration, and refuse extended-card codecs in netplay before frame zero. Preserve default atomic save publication and guest-visible write failures.
2026-10-19 16:00:11 -07:00
Matt Stanley bc574c5700 Isolate runtime publication and wall-time diagnostics from held mod selector stack 2026-10-19 14:07:22 -07:00
Matt Stanley 422238d0fa Merge current master and qualified review fixes into PR #642 2026-10-19 14:00:58 -07:00
Zap0xfce2 1b778616b7 feat(widescreen): continue framebuffer-feedback sprites into native-wide bands (#692)
* feat(widescreen): continue framebuffer-feedback sprites into native-wide bands

Titles tag a 15-bit sprite that copies a framebuffer 1:1 (gpu_ws_tag_feedback_rect / psx_mod_tag_feedback_rect). Where it touches the draw area's edge, gpu.c emits band spans and the new optional backend op wide_feedback_rect (GL incl. render thread, software) samples the source buffer's wide surface. Band texels count as bit 15 set, as the centre does once the trail blends.

The software rasterizer now writes the texel's bit 15 into the framebuffer, and wide_full can dump the surface alpha ("alpha":1).

* fix(widescreen): quantise band feedback like packed VRAM

The centre samples 5-bit packed VRAM each frame; the band copy read the 8-bit wide surface and modulated with /128, so the bands drifted ~3 grey levels brighter and left a step at the 4:3 edge. Truncate like PACK_FS and modulate like TEX_FS.

* Reject fully clipped sprites before extending feedback margins

* Complete isolated resident and PGXP fixture collaborators

* Preserve existing line endings in the review fixes

---------

Co-authored-by: Matthew Stanley <1379tech@gmail.com>
2026-10-19 11:44:13 -07:00
Matthew Stanley cf9bf1f9fd Allow developer pad injections to exercise serial hotplug 2026-10-17 09:40:22 -07:00
Matt Stanley bf7582bb3e Merge pull request #666 from FractalEngineer/feat/vr-hud-surfaces
Add isolated render capture and head-facing surfaces for VR HUDs
2026-10-15 00:33:52 -07:00
Yves 890fed78f5 Add frame-local positioned RGBA surfaces to stereo rendering 2026-10-15 05:17:37 +08:00
Shokunin c3a2f2c068 Menu host follow-ups: restart bits for touched groups, local return, default provider
- Restart bits: a group waits only when the edit touches it (next differs
  from the live value) and the result differs from boot. Dynamic
  resolution moves the live internal resolution off boot, so an in-game
  texture filter change relaunched the game. test_menu_video_snapshot
  covers both cases.
- Return to the frontend from a local game opens the launcher as at
  boot; only a lobby match start (the runtime's pending-launch exit, or
  the provider's lobby view asking once its match started) keeps the
  launch and resumes the room.
- Default provider: with no title choice, the UI library's provider is
  used when it ships one (recomp_ui_psx_menu.h), so recomp-ui stays the
  default; PsxMenuHost.default_selection tells it to keep the title's
  default presentation.
- Debug: menu_host reports provider, open, pause, capture and snapshot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:17:18 -07:00
Matt Stanley d3db61b199 Merge pull request #598 from RetroPortingToolKit/feat/host-pause
Host pause API (in-game overlay P1)
2026-10-14 11:55:36 -07:00
Matthew Stanley 8a5559cc7c Cancel debug socket waits portably without timing out valid fragmented commands 2026-10-14 11:29:13 -07:00
Matthew Stanley d66916f4b6 Bring portable debugger shutdown repair onto current master 2026-10-14 11:23:51 -07:00
Shokunin 475a3ad6c5 Debug host_mouse: inject a click or wheel step at a window point
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:33:32 -07:00
Shokunin e21226feb7 Overlay input sink and UI capture gate (in-game overlay P3)
psx_host_set_input_sink: an overlay provider sees host SDL events first,
in the event drain and the host pause loop, and may consume them.
psx_host_set_ui_capture: while on, local game input reads released and
centred (netplay included, so peers receive "nothing pressed") and the
external-input gate stays closed. Debug builds gain host_key, which pushes
a real SDL key event for scripted runs. Inert without a provider.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:33:32 -07:00
Shokunin 138b44a58e Host pause API (in-game overlay P1)
psx_host_pause_push/pop: a nesting, host-owned pause. While it is held the
runtime freezes the guest at the vblank present (as the save-state menu
does), keeps presenting the last frame and pauses audio. Push refuses
during netplay. Nothing pushes unless an overlay provider (or the debug
server's new host_pause command) asks, so behaviour is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:33:17 -07:00
rain 6ba9967390 debug server: bounded accept() so shutdown join cannot hang window-close
Windowed close ran shutdown_runtime() -> debug_server_shutdown(), which
closed the listen socket expecting that to break the I/O thread's blocking
accept(). Linux gives no such guarantee, so SDL_WaitThread joined forever
and the window sat dead until SIGKILL (SIGABRT variants seen when the racy
fd-close collided with thread teardown). SO_RCVTIMEO makes accept() return
every 200 ms to re-check s_io_running; the existing loop already treats the
timeout as retry/exit. Reproduced headless on Xvfb with xdotool windowclose;
core backtrace pinned the block at debug_server_shutdown+0x42 (WaitThread).
Exit after close: <1 s, code 0.
2026-10-13 16:58:21 -04:00
Matthew Stanley 1473c66772 Merge validated renderer fixes into generic post-effect pipeline 2026-10-13 10:18:03 -07:00
Matt Stanley 6eaf078699 Merge pull request #584 from RetroPortingToolKit/feat/gl-dithering
GL: PS1 dithering by default (native grid), elective fine/off modes
2026-10-13 10:10:53 -07:00
Matt Stanley 2095249191 Merge pull request #640 from RetroPortingToolKit/fix/display-ring-full-frame
display_ring_get: whole display (480i, depth24) from the frame VRAM snapshot
2026-10-13 10:09:30 -07:00
Shokunin 8dbe2165ff bloom: cost of real and generated frames
Smooth motion lost most of its generated frames with bloom on (R4 race,
120 Hz: ~30% of bloom-off). Measured and fixed in order of cost:

- Depth-stencil blits (CPU milliseconds each on macOS) are gone:
  generation surfaces no longer copy the marks and are not marked while
  drawn (generated frames reuse the newer real frame's mask); the
  native-wide centre copy is colour only again and the mask takes the
  centre's marks from the hr surface; the mask itself is resolved in the
  presented surface's own pixels through an R8 target that shares the
  surface's depth-stencil renderbuffer, then mapped to the window.
- Marking needs one pass per batch, not two: 3D batches only set, 2D
  opaque ones only clear, semi-transparent ones only set; only batches of
  unknown make (queued replays, lines) still do both.
- No full-size copy of the image: `source_down` (a blit straight to the
  pass's size) feeds the bright pass, and the composite screen-blends onto
  the window (`blend screen`; unlit pixels are discarded, so they keep their
  exact value). The debug split/bloom-only views go with the copy.

M4, R4 race, render thread + reprojection, PSX_FRAME_GEN_FORCE, a host
loaded by other work (absolute rates low): bloom pass on real / generated
frames 1.2 / 0.7 ms at 1080p, 1.3 / 0.9 ms at 1440p, 2.6 / 1.6 ms at 4K
(glFinish-bracketed); generated frames with bloom 79-80% of bloom-off at
all three (was ~30%). A 1/8-resolution blur chain was tried and kept no
more frames, so the 1/4 chain (the look) stays.

Tests: parser (blend, source_down, bloom preset reads no full copy), GL
fixture (source_down + blend screen chain exact; HUD and mask checks
unchanged), FXAA unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 04:03:35 -07:00
Shokunin de8d286180 Dithering: native-grid pattern is the default, internal-pixel is elective
Review of #584: the default must not make guest-visible VRAM depend on the
internal scale. Mode 1 ("on", default; "scaled" kept as an alias) now
puts the PS1 pattern on the native VRAM grid; mode 2 ("fine") is the
elective per-internal-pixel pattern. gl_dithering_test checks the default
mode's native VRAM is identical at 4x and 1x (equal-colour Gouraud now
averages 15.50 in the default at every scale). Docs keep the
semi-transparent approximation explicit. OpenBIOS stamp regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 02:18:29 -07:00
Shokunin 023af6d9d4 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-13 02:18:29 -07:00
Shokunin 18e10693c4 post effects: MinGW build fix, generated frames reuse bloom's glow, no glGet in marking
- config: [video] post_shader is a std::filesystem::path (like memcard_dir);
  the frontend turns it into UTF-8 bytes for the GL API. Assigning the path
  to a std::string did not compile on MinGW.
- .pfx `generated reuse`: a Smooth motion generated frame keeps that pass's
  target from the last real frame. The bloom preset marks its bright and
  blur passes so; generated frames run only the masked composite (over their
  own picture and mask). bloom debug reports real/generated pass times and
  reused passes.
- Mask marking no longer queries GL state per batch: each call site puts
  back the blend and mask-bit stencil it set (no driver round trips on the
  render thread).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 02:15:46 -07:00
Shokunin 74dadc6004 GL: one post-effect pipeline; bloom and FXAA as built-in chains, user chains via [video] post_shader
Post effects now run as chains of full-screen fragment passes on one
executor (gl_postfx.inc), as asked in review. A chain is a small .pfx preset
(postfx_chain.c, GL-free): passes with a shader (a file next to the preset or
builtin:<name>), inputs (source, the optional primitive-derived mask,
previous or a named earlier pass), a scale, format, filter and uniforms.
The executor owns target allocation and lifetime, program build/cache,
uniform binding (u_rcp, u_size, u_org) and GL state save/restore.

Stages, in order, each from a fresh copy of the image: the built-in "bloom"
chain ([video] bloom), a user chain ([video] post_shader / PSX_POST_SHADER,
elective, off by default), and the built-in "fxaa"/"fxaa_hq" chain
([video] antialiasing_mode). FXAA folds in as a one-pass chain and its
output is byte-identical to the former fixed pass; it still runs on real
frames only. Generated (Smooth motion) frames run bloom and the user chain.

The "may glow" mask is an optional pipeline input: draws keep stencil bit 1
only while an active chain reads `mask`, and the window-sized mask texture is
built once per present for the chains that use it.

Tests: postfx_chain_test (format, built-ins, errors, file import, no GL);
gl_bloom_test gains an imported two-pass chain (byte-exact invert), the mask
input (HUD unlit, 3D lit), a bad preset, a pass that does not compile, and GL
state restored with bloom + a mask chain + FXAA on, all on the caller thread
and the render thread. docs/POST_EFFECTS.md documents the format.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 02:15:46 -07:00
Shokunin e730e03a8a GL: opt-in bloom ([video] bloom), sourced from 3D and additive primitives only
[video] bloom = 0.0 (off, default) .. 2.0 adds a glow around lights on the
OpenGL backend. PSX_BLOOM overrides it for one run; {"cmd":"bloom"} reads or
changes strength, threshold and a debug view live. It is a plain [video] key,
so [quality.*] presets can switch it off on low-end machines.

The HUD never glows: with bloom on every textured and untextured draw is
followed by two stencil-only passes of the same vertices that keep a
"may glow" mark in stencil bit 1 (bit 0 stays the mask-bit mirror). Opaque
primitives clear it under their coverage, then PGXP-proven 3D primitives and
additive semi-transparent texels/primitives (modes 1 and 3) set it, painter
ordered like the colour; 3D and non-3D primitives never share a batch while
bloom is on. Semi-transparent primitives leave the mark alone and the PGXP
depth-clear pass (colour writes off) marks nothing. Without PGXP geometry only
additive primitives glow.

The pass runs on the composed game image before AA and the OSD: the presented
surface's marks are blitted into a quarter-resolution stencil, a soft-knee
bright pass runs under it, three separable Gaussian iterations spread it and a
screen blend adds it back. The marks follow the picture: native-wide centre
copies and Smooth motion surfaces (redraw and reprojection) copy stencil with
colour, and generated frames get the same pass. Off: nothing runs, no state
changes, and the composed frame is byte-identical to master.

test_gl_bloom (gl_bloom_test): off is byte-identical before/after bloom and
with --dump against master; on, a bright 2D panel and a panel over a lit 3D
polygon are byte-identical to off, an additive and a 3D polygon glow; guest
VRAM is unchanged; on this thread and on the render thread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 02:15:46 -07:00