Commit Graph

2834 Commits

Author SHA1 Message Date
Matthew Stanley 3cd6d2d1e0 TCP_COMMANDS: document the input port parameter
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 075553beed)
2026-10-07 18:20:57 -07:00
Matthew Stanley dd64bc6611 TCP input injection for port 2 (set_input/press/clear_input port=2)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 66cc81fe63)
2026-10-07 18:20:57 -07:00
Matt Stanley e372fe2f22 Merge pull request #572 from RetroPortingToolKit/fix/village-ws-margins-20261007
Native-wide: present finite 2D backdrop scenes at 4:3
2026-10-07 17:51:12 -07:00
Matthew Stanley 4b4fddb769 Present finite 2D backdrop scenes at 4:3 in native-wide
A full-screen rect backdrop that stops short of the reveal vetoes polygon overhang (Tomba village, beads-eio.4.28).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 17:46:17 -07:00
Matt Stanley edecdd1d3a Merge pull request #570 from RetroPortingToolKit/review/pr502-hd-exclusive-safety-20261007
Integrate validated rendering pipeline with native-VRAM and HD safety
2026-10-07 15:42:55 -07:00
Matthew Stanley bbd71522d5 Record PR569 ancestry; its tested fix is already included 2026-10-07 15:38:53 -07:00
Matthew Stanley 4dabdabb12 Record refreshed rendering-stack ancestry preserving tested safety fixes 2026-10-07 15:38:52 -07:00
Matthew Stanley be2bfbf6b4 test: retain refreshed HD provenance fixture stubs 2026-10-07 15:38:52 -07:00
Matthew Stanley c20c05ccc0 fix: keep HD texture authority on the synchronous renderer 2026-10-07 15:03:51 -07:00
Shokunin 86b15cfbbb Frame generation: in-between frames never rasterize native VRAM
Under HD texture-pack authority (and dual-raster netplay) every draw was
also rasterized into CPU VRAM unless a render pass was active. Replayed
draws of a generated frame were not exempt, so each in-between frame wrote
interpolated geometry into the guest's native VRAM. They are presentation
only, like a pass. Fixture: compose under HD authority, VRAM unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:51:50 -07:00
Matthew Stanley b620b13837 Integrate current framework master without dropping texture fixes 2026-10-07 14:39:05 -07:00
Matthew Stanley 7547d9257a Integrate refreshed rendering stack preserving validated retail BIOS stamp 2026-10-07 14:39:05 -07:00
Shokunin 7692b8134d Frame generation: in-between frames never rasterize native VRAM
Under HD texture-pack authority (and dual-raster netplay) every draw was
also rasterized into CPU VRAM unless a render pass was active. Replayed
draws of a generated frame were not exempt, so each in-between frame wrote
interpolated geometry into the guest's native VRAM. They are presentation
only, like a pass. Fixture: compose under HD authority, VRAM unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:32:36 -07:00
Shokunin d29390a85d Regenerate the OpenBIOS emitter stamp
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:32:36 -07:00
Shokunin ae4f45f2c3 Call frame generation "Smooth motion" in user-visible text
Log lines and doc headings say "Smooth motion (frame generation)", the
player-facing name. Config key [video] frame_generation and PSX_FRAME_GEN
are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:32:36 -07:00
Shokunin 4f25e6de83 Pipeline toggles as player settings and launcher rows
render_thread, present_thread and frame_generation are read from the
player's settings.toml [video] (over the game.toml default; PSX_* env
still overrides one run and is never saved), written back by
save_user_settings, and round-tripped through the recomp-ui Display rows
when the launcher defines RECOMP_LAUNCHER_HAS_RENDER_PIPELINE. The
pipeline starts once at boot: a first-boot launcher choice applies
immediately, an in-game change is saved and applies at next launch.
Regenerated the OpenBIOS emitter stamp for the config_loader change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:32:36 -07:00
Shokunin 38fbe239f1 Test: GP0 history guard fixture stubs the frame-generation hooks
gpu.c's precise-triangle path references Smooth motion's vertex sources;
the software-only fixture never reaches them (abort if it does).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:32:36 -07:00
Shokunin f4160b55d9 Test: GPU exec fixtures stub the frame-generation vertex-source hooks
gpu.c asks the GL backend and gte.cpp for Smooth motion's vertex sources;
master's native_wide_projection_test (cd9981cd) links gpu.c with these stubs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:32:36 -07:00
Shokunin 22fe3669cf Frame generation: shared corners are one vertex; no motion rounding
The remaining tunnel cracks came from vertex sources, not from missing
depth: 46 % of R4 lookups were marked ambiguous because the same 3D point
is projected several times per frame (corners shared by separate meshes,
other functions), so those vertices fell back to a triangle-mean guess.
- The same point projected again is one vertex (the smaller identity, so
  the choice does not follow draw order).
- Up to two different points on one pixel are kept; gpu.c picks the one
  projected by the same function as the triangle's other vertices, else
  the one nearest their depth.
- Per-vertex motion is no longer rounded to 1/8 px: at high internal
  scales the rounding opened hairlines along long T-junction seams.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:32:36 -07:00
Shokunin 6be9b6a807 Frame generation: close cracks, skip frames the camera passes through
Corner and tunnel runs showed light cracks along seams and near the view
edge in generated frames:
- weld: every placed vertex at one screen position of a view moves alike
  (the same corner arrives from several projections);
- a vertex without a projection on an edge between placed vertices moves
  as that point of the edge (T-junctions from the game's polygon splits);
- a vertex the in-between camera passes is projected at a clamped depth,
  and such a game frame is not generated (no near-plane clipping: tunnel
  ceilings, overhead bridges);
- debug: "clamped", "guessed" (CPU-built vertices placed by a triangle
  mean over a >1 px motion spread).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:32:36 -07:00
Shokunin 64036e2ee7 Frame generation: in-between frames from an in-between camera
Pairing triangles of consecutive draw lists by key and draw order paired
the wrong ones whenever the game reordered, culled or switched LODs (R4:
a grandstand of one repeated texture turned into a staircase; on a
synthetic version 225 of 226 pairs were wrong). No primitive is paired by
draw order any more.

gte.cpp notes each RTPS/RTPT output (only while generation is on) with an
identity (issuing ra + model-space vertex), its camera-space position and
H, indexed by screen word; gpu.c records each polygon vertex's source
ahead of the triangle (RTH_FG_SRC, never drawn). Per view the camera
motion between the two frames is fitted from the paired camera-space
vertices (RANSAC + least squares); one real frame is redrawn from the
in-between camera: world vertices re-projected, vertices moving against
the world (cars) lerped in camera space, CPU-built vertices moved with
their triangle, HUD/2D unchanged. Moving forward the older frame is
redrawn; uncovered areas show the newer real frame. A verdict rejects
the generated frame (the real one shows) without projections, with few
pairs/inliers, or with a motion no camera makes. Real frames are
unchanged (gl_frame_gen_test).

PSX_PRESENT_SHOT_BURST=N captures the next N composed frames, real and
generated, for checking in-between frames against their neighbours.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:32:36 -07:00
Shokunin bf67446271 Frame generation: plan ceiling for render-thread overload, stalls judged by wait
A render thread a frame behind lowers the plan's ceiling by one (recovering
one per 2 s) instead of tripping the breaker; the guest waiting on the queue
trips it only when the wait was a stall (a quarter of the game frame). The
real-frame cost plans with CPU time; the GPU span is reported only. The
generated-frame blit copies colour only and marks the stencil stale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:32:36 -07:00
Shokunin 1f102027ec Frame generation: warm cost estimate, re-probe, guest-paced breaker 2026-10-07 14:32:36 -07:00
Shokunin ceac56b2de Regenerate the OpenBIOS emitter stamp
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:32:36 -07:00
Shokunin 06b05041e9 Docs: present thread and fast first descent
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:32:36 -07:00
Shokunin 673c1d0a1b Present thread: one owned swap call; frame-gen fixture builds on MinGW
Both swap paths go through gl_window_swap. The temporal-blending ownership
guard allows the present thread's context (pt_begin only). The frame
generation fixture used setenv, which MinGW lacks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:32:36 -07:00
Shokunin 1ed99213f2 Frame generation: PSX_PRESENT_SHOT_GENERATED captures a generated frame
Diagnostic: a staged present_shot waits for the next generated frame, so the
in-between images can be inspected in game.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:32:36 -07:00
Shokunin 1096174184 Dynamic resolution: fast first descent on the render thread
Armed at init (session start, render-thread start, a new resolution) and on
a savestate load, game entry or window resize. While armed, an overrun jumps
straight to the highest level whose pure-area prediction is at or below
0.78, then is judged like any step; a fit after a step, an up step, a failed
judgement or 6 s disarm it and single steps fine-tune. A fixed cost only
makes the target dearer than predicted, so a jump never lands below the
level that fits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:32:36 -07:00
Shokunin e3d8994099 Present thread: swap off the render thread ([video] present_thread)
Opt-in under the render thread. Composed frames go to a ring of offscreen
slots at the window's size (binds of framebuffer 0 on the composing context
land on the current slot); a present thread with a second, shared context on
the same window waits on the slot's fence, copies it 1:1 and swaps. The
compositor's wait moves off rendering; the composing side waits only while
every slot is queued or on screen. Falls back to direct swaps when the shared
context or sync objects are unavailable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:32:36 -07:00
Shokunin d18c3162f2 Test: frame generation GL fixture stubs the HD upload/copy hooks
master's facade now calls gpu_hd_textures_begin_upload/begin_copy/end_copy;
the fixture runs without a pack, so it stubs them like the other hooks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:32:35 -07:00
Shokunin 49c2f31120 Test: frame generation GL fixture stubs the HD texture pack hooks; TCP index
master's HD texture packs (#557 base) added gpu_hd_textures_* calls to the GL
renderer and the gpu_render.c facade; the fixture runs without a pack. The
command index is regenerated for frame_gen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:29:33 -07:00
Shokunin cb10c49834 Regenerate the OpenBIOS emitter stamp
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:29:33 -07:00
Shokunin f69ce834eb Frame generation on the render thread from recorded draw lists
[video] frame_generation (opt-in, with render_thread): the render thread keeps
each game frame's replayed draw list, matches the triangles of the last two
(frame_gen.c), and presents in-between frames at the display refresh by
redrawing the newer list into separate surfaces with matched vertices
interpolated. It never runs guest code. Real frames are composed at their
flip and presented after the generated ones, with the same pixels and
order. Generation uses only measured surplus: a plan from render-thread CPU,
generated-frame cost and swap time, a breaker after backpressure or late
guest frames, and a pause while dynamic resolution is over budget. The
render-thread core gains a timed tick hook. Frame-cost queries pause around
generation, so dynamic resolution measures real frames only.

Tests: frame_gen_test, render_thread_test (tick), gl_frame_gen_test (real
frames identical off/on; phase 0/1 equal the endpoints). Docs:
docs/FRAME_GENERATION.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:29:33 -07:00
Shokunin fb6ef185a8 Dynamic resolution: shrink the copy/stencil scratch after a step
A step grows the scratch to hold the old level's displayed rect and draw
area, and it stayed that size: every tiled stencil rebuild renders into it,
so on GL-on-Metal its size cost bandwidth on each one. Return it to the size
a surface allocated at the new level starts with.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:29:32 -07:00
Shokunin 5d5c60657d Dynamic resolution: allocate the hr surface at the current level too
The hr (VRAM) surface stayed allocated at the ceiling. On GL-on-Metal every
render pass loads and stores the whole attachment, so its cost followed the
ceiling rather than the level: R4 2P VS at 5x ran 40-45 Hz for its first
seconds with a 10x ceiling and 60 Hz with surfaces allocated at 5x. Allocate
a new hr surface at each step and reseed it from the 1x image and the saved
rects; a failed allocation falls back to the in-place step down, or refuses
a step up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:29:32 -07:00
Shokunin 5488489273 Dynamic resolution: allocate native-wide surfaces at the current level
A native-wide surface was allocated at the dynamic-resolution ceiling and
rendered into its lower-left corner. On GL-on-Metal every render pass loads
and stores the whole attachment, so the wide surfaces cost the ceiling's
bandwidth at every level. Allocate them at the level they render at and
reallocate at each step, rescaling the presented rows and margins from the
old surface. The limit check stays at the ceiling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:29:32 -07:00
Shokunin 966be30a53 Docs: dynamic resolution on the render thread
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:29:32 -07:00
Shokunin b24ce9d3ab Dynamic resolution on the render thread: replay recorded steps in every build; GL equivalence test steps the level
The step record's replay sat inside the debug-tools block, so release
builds dropped every recorded step. gl_render_thread_test gains a dynres
mode: the same level steps between frames with the thread off (applied at
once) and on (recorded), identical guest readbacks, VRAM, frame and wide
surface, and the render thread's frame cost measured. Also drop the
duplicate gpu_ws_background_requires_full_composite stub the #508 merge
left in the scale-invariance fixture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:29:32 -07:00
Shokunin 281b451ef7 Render-thread dynres: cut the soft hold tails to half a second
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:29:32 -07:00
Shokunin 6b579a3c46 Render-thread frame cost: GL_TIME_ELAPSED (timestamps read 0 on macOS); frame_perf stands aside
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:29:32 -07:00
Shokunin c1aacbf2b9 Dynamic resolution on the render thread: recorded steps, per-frame render cost, render-thread controller
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:29:32 -07:00
Shokunin 961caac5cb Test: render thread GL fixture stubs the HD upload/copy hooks
master's facade now calls gpu_hd_textures_begin_upload/begin_copy/end_copy;
the fixture runs without a pack, so it stubs them like the other hooks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:29:32 -07:00
Shokunin 9e8f0170a3 Test: render thread GL fixture stubs the HD texture pack hooks
master's HD texture packs (#557 base) added gpu_hd_textures_* calls to the GL
renderer and the gpu_render.c facade; the fixture runs without a pack.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:25:57 -07:00
Shokunin c0bcb35ab8 Regenerate the OpenBIOS emitter stamp
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:25:57 -07:00
Shokunin dde7b34f95 Docs: the render thread builds and its tests run on Windows (MinGW)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:25:57 -07:00
Shokunin 779724c427 tests: build and run the render thread tests on Windows (MinGW)
render_thread_test is registered on MinGW (winpthreads provides the
fixture's pthreads and nanosleep; the core takes its Win32 path).
gl_render_thread_test gains the MinGW link path run_gl_scale_invariance.py
uses on test/gl-scale-invariance-windows (static SDL3, opengl32 and its
Win32 libraries, LTO, probe.exe), and a host with no window or GL 3.3 core
context (headless, Windows over SSH) exits 77 from a preflight run, reported
by CTest as a skip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:25:57 -07:00
Shokunin ac04b5311e Render thread: no direct GL call while recording, even for empty uploads
An empty CPU->VRAM rect fell through to glb_vram_transfer_in on the
emulation thread while the render thread owned the context; it is now
recorded like any other upload. A projected triangle whose record cannot
be reserved takes the context first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:25:57 -07:00
Shokunin 91e4e82be5 Docs: queued debug captures, ensure_cpu on the render thread, line refs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:25:57 -07:00
Shokunin f9c679e590 Render thread: queue the headless presented-image capture
--headless-opengl captured the presented image into the ring every frame
through gl_renderer_capture_display_hires, a sync point, so a headless
run (fp_identity) held the context for most of every frame. The capture
is now recorded at the same point of the frame (gl_renderer_ring_capture)
and done by the render thread; ring readers already sync first. A rect the
GL surface cannot serve is skipped there rather than resolved through a
CPU readback, and ensure_cpu refuses to run on the render thread at all:
its readback would land in the private upload source and mark the CPU
side current without gpu.c's array seeing the pixels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:25:57 -07:00
Shokunin 23270e62a8 Test: present-skip guard follows the overlay accessor
The OSD query at the two present-skip sites now goes through
ov_needs_present(), which falls through to host_osd_needs_present()
unless the render thread is replaying a present with its recorded
overlay state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:25:57 -07:00