Review of #561. Guest time now has two entry points (psx_cycles.h): the
CPU charge psx_cpu_charge() (instruction base, load fudge/region/completion,
i-cache refills, BIOS HLE costs), which applies [timing] guest_cycle_scale,
and the unscaled device advance psx_advance_cycles() (DMA, idle skips,
mul/div and GTE deadline waits). The emitters' block-mode charges emit
psx_cpu_charge; overlay DLLs accumulate it raw and publish through the
cpu_charge callback (was advance_cycles, same slot) into the same host
function, so nothing is scaled at the DLL boundary. The batched
psx_cyc_charge path, the dirty-RAM interpreter, psx_cyc_readmem, i-cache
refills and bios_hle.c all charge through it. At scale 1 it is exactly
psx_advance_cycles.
Gate in psxrecomp: [timing] guest_cycle_scale_gate, RAM predicates judged
at every VBlank edge (deterministic, works online, no mod needed), plus
psx_mod_set_guest_cycle_scale_gate() in mod_plugins.h for custom logic or
to tie the scale to a mod (guest_cycle_scale_gated = true; every mod
session reset shuts it). The carried fraction and both gate states are a
boot_state section (BS_SEC_GCS, written only when the scale is not 1), so
savestates and the netplay rollback snapshot re-simulate the same cycles.
psx_guest_cycle_scale_gate_test pins tier identity, the gates and rollback
re-simulation against the real psx_cycles.c.
OpenBIOS regenerated (emitter stamp); retail SCPH1001 is not.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No CPU is emulated: recompiled code charges the guest clock a fixed cost
per instruction. guest_cycle_scale = N charges 1/N of it (instruction,
load, mul/div, GTE latencies, overlay DLL batches) while VBlank, timers,
CD, SPU and DMA keep hardware time, so the game never runs out of frame
time. A title constant in game.toml (1 = faithful, 1..64), never a player
setting; guest_cycle_scale_gated applies it only while the title's mod
opens the gate (psx_guest_cycle_scale_gate_open). PSX_GUEST_CYCLE_SCALE
overrides for testing. A scale other than 1 is folded into the netplay
content fingerprint as a safety net.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UI prepass re-read aperture node payloads through the RAM-folded key, so
THPS2's relocated prim arena discarded the HUD squash every frame.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit fd6267e952)
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>