Commit Graph

818 Commits

Author SHA1 Message Date
Shokunin dc2cd301d3 PGXP depth: no cost when off; render-thread-safe setters; Smooth motion honours depth
- gpu.c sends gr_set_depth_triangle only while a PGXP renderer feature is
  on (gl_renderer_pgxp_render_wanted): no RTH_DEPTH per triangle otherwise.
- The SZ rides in the textured vertex's unused colour alpha, so TEXV stays
  26 floats for every title.
- gl_renderer_set_pgxp_depth / _threshold / pgxp_render_stats sync with a
  live render thread (GL_RT_SYNC) like their neighbours.
- A depth-mode change is its own batch-break reason (7); batch_diag returns
  all eight reasons.
- Smooth-motion in-between frames replay RTH_DEPTH and test depth from
  their own clear; gl_frame_gen_test gains a PGXP pass (overlapping 3D
  triangles out of painter order) where the in-between frame must match
  the real one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 02:38:58 -07:00
Shokunin 2a36efcacb PGXP depth buffer for opaque 3D polygons (opt-in)
[video] pgxp_depth_buffer (default off; PSX_PGXP_DEPTH overrides) and
pgxp_depth_threshold. gpu.c hands the GL backend each triangle's GTE SZ when
all three vertices are dataflow-precise (gr_set_depth_triangle); the backend
draws those opaque triangles with a LEQUAL depth test and write. 2D,
unproven, semi-transparent polygons, lines and rectangles never test or
write. Depth mode is a batch key and rides in every high-resolution-window,
native-wide and render-thread replay command. Clears are colourless
depth-only draws through the flat batch: after a drawing-area change or a
fill, and when the average SZ rises by the threshold (4096, as DuckStation).
Generated (Smooth motion) frames draw without depth.

Docs: ENHANCEMENTS.md G1.14.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 02:38:41 -07:00
Shokunin 91cd6db705 guest_cycle_scale: one CPU charge for every tier, declarative gate, rollback
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>
2026-10-07 21:41:39 -07:00
Shokunin 1e6e43639a Title timing constant: [timing] guest_cycle_scale (and _gated)
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>
2026-10-07 21:41:34 -07:00
Matthew Stanley 7bcb11916a GPU packet keys keep the enhancement aperture apart from the main RAM it aliases
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)
2026-10-07 18:20:59 -07:00
Matthew Stanley c55ddc4f29 Widescreen HUD size: proportional option shrinks the auto-UI HUD beyond 16:9
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 41a9889566)
2026-10-07 18:20:59 -07:00
Matthew Stanley 9032b1005e Always-on host CPU sampler ring (TCP host_profile)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 0901bb8b1e)
2026-10-07 18:20:58 -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
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
Matthew Stanley b620b13837 Integrate current framework master without dropping texture fixes 2026-10-07 14:39:05 -07:00
Matthew Stanley ed398cc37e Improve DuckStation texture capture and replacement fidelity 2026-10-07 13:40:57 -07:00
Shokunin 7b741b10f4 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 12:57:02 -07:00
Shokunin ba66fa6cac 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 12:57:02 -07:00
Shokunin dc4197c053 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 12:57:02 -07:00
Shokunin 0269264da2 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 12:57:02 -07:00
Shokunin 816d8e47f4 Frame generation: warm cost estimate, re-probe, guest-paced breaker 2026-10-07 12:57:02 -07:00
Shokunin bfbe8489de 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 12:56:48 -07:00
Shokunin f10db8a0f0 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 12:56:48 -07:00
Shokunin 4f97482f8c 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 12:53:09 -07:00
Shokunin af5e6be7d2 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 12:52:53 -07:00
Shokunin 9e1591fc12 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 12:52:52 -07:00
Shokunin cb1e445e46 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 12:51:55 -07:00
Shokunin 26b0537ff2 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 12:49:53 -07:00
Shokunin 9abcff08a4 Render thread: skip a present only when two newer frames wait
One frame behind is the normal pipelined state when the render thread is
the slower side; skipping on it dropped nearly every present at 4K
widescreen. rt_frames_ahead() counts the complete frames queued after
the one being replayed; the backend skips only at two, where the
emulation thread is at the in-flight bound waiting on the render thread.
The unit test checks the count never exceeds the bound and that both
cases occur under a slow consumer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:49:47 -07:00
Shokunin f1430d6752 OpenGL backend: record on the emulation thread, replay on the render thread
With the render thread running, the backend's facade table records every
state change, primitive, fill, copy, CPU->VRAM upload (payload copied)
and native-wide call, and the present, into the command ring; the render
thread replays them through the unchanged glb_* functions. With it off
gl_backend_get returns the existing table and nothing new runs.

Sync points: every other GL entry point reached on the emulation thread
(readbacks, savestate restage, render passes, OpenXR, settings,
diagnostics) drains the ring and takes the context (GL_RT_SYNC); the
emulation thread then runs the rest of that frame directly, exactly as
without the thread, and hands the context back at the frame boundary when
the frame may run asynchronously again (no depth24 display, dual-raster,
frame interpolation, render pass, netplay or OpenXR).

Guest-visible VRAM stays gpu.c's array: while the render thread owns the
context, uploads stage from a private copy that receives each payload in
command order. Guest state the backend reads while drawing (per-primitive
widescreen tags, the native-wide fast-path latch) is captured at record
time; host overlays travel with the present; a present that a newer
recorded frame already supersedes is skipped. The emulation thread answers
projective/wide-present/draw-area queries from mirrors of the recorded
calls. The per-frame display-ring readback is queued (fbo_peek_deferred)
instead of being a sync point.

The GL fixtures link render_thread.c and stub the two facade hooks; the
scale fixture also gains the gpu_ws_background_requires_full_composite
stub it was already missing (it failed to link on master).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:49:31 -07:00
Shokunin 01f9cd34d6 Render thread core: SPSC command ring, frame bound, sync-point hand-off
Backend-neutral core for the opt-in [video] render_thread: the emulation
thread appends records to a single-producer/single-consumer ring and the
render thread replays them in order with the graphics context current.
rt_frame_end bounds closed frames in flight (backpressure), the render
thread can tell a frame is stale, and rt_acquire is the sync point:
drain, then move the context to the emulation thread until rt_release.
No lock is held while a record executes.

render_thread_test drives it with a fake executor and context token:
ordering and payload integrity across ring wraps, oversize refusal, the
in-flight bound under a slow consumer, stale detection, randomized
acquire/release interleaving and stop while held (TSan clean).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:49:14 -07:00
Shokunin ecbbb5eceb Add the opt-in dynamic resolution controller, config and telemetry
A step controller (dynamic_resolution.c) lowers or raises the GL internal scale from frame-time telemetry between a floor and the configured ceiling; off unless a title or the player enables it. Tested by dynamic_resolution_test (synthetic load) and the real-GL gl_scale_invariance_test step modes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:44:59 -07:00
Shokunin c9eaeb04d3 Change the GL internal scale at run time (opt-in)
gl_renderer_set_dynamic_resolution(1) keeps the hr surface and the
native-wide surfaces allocated at the scale context init gave them (the
ceiling) and lets the internal scale step to any integer level from 1 to
it while the game runs. A lower level renders into the surfaces'
lower-left VRAM*S corner; every viewport, scissor, pack, copy and capture
already takes the scale at use, so only allocation and the present's
normalised UVs read the allocated size. Off (the default) nothing is
allocated differently and no step can run.

A step runs after a present, never inside a render pass:
- it takes the top-left sample of every native block of the old surface
  (the sample the pack reads) into a 1x image and reseeds the new corner
  from it, so the guest-visible VRAM is unchanged byte for byte;
- it rescales the displayed rect and the draw area from their old pixels,
  so neither the frame on screen nor the one being drawn shows 1x blocks;
- it rescales the native-wide margins in place, in row bands ordered so
  no source row is overwritten before it is read;
- it marks the mask stencil stale (rebuilt at once while mask checking is
  on) and drops the render-pass backup and images.

Reallocating per step instead measured 30-106 ms at 7x-10x on an Apple
M4, mostly the first touch of the new surface. Steps are not offered in
the windowed high-resolution mode or with dual raster.

gl_scale_invariance_test gains "steps" runs: rendering after a step into
a ceiling-sized surface equals the fixed-scale run at that level; a chain
of steps (9x down to 1x and back, 5x) leaves the native VRAM unchanged at
every step, the two rects and the wide margins exact nearest rescales of
their old pixels, everything else the old top-left samples, and mask
checking right after a step correct; the window mode refuses steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:44:46 -07:00
Matthew Stanley 62d5277174 Add guarded screen-space and tiled-strip widescreen tags 2026-10-07 12:06:57 -07:00
Matthew Stanley 3a11aea16d Refresh netplay validation against master retaining audited startup cache and local views 2026-10-07 11:59:16 -07:00
Matthew Stanley 9a1bfdcee2 Merge PR 554 for local netplay validation; clear new peer state on rematch 2026-10-07 11:21:55 -07:00
Matthew Stanley 7d0a18ebc6 Integrate netplay pad and own-view stack without dropping current-master behavior
Preserve negotiated content and dual-raster pass gates. Retain both fixture assertion sets and HD-off seams. Regenerate both BIOS with actual native emitter. Fix type-only rollback corrections and exclude extra-pad changes from digital-only promote gates.
2026-10-07 11:06:57 -07:00
Matthew Stanley dea71f2261 Integrate verified duplicate LOAD guard into current netplay validation bundle 2026-10-07 10:52:06 -07:00
Matthew Stanley 62ee64d0a3 Merge reviewed validated warm startup cache (PR555)
# Conflicts:
#	runtime/src/mod_packages.cpp
#	runtime/src/mod_runtime.cpp
#	runtime/tests/test_mod_runtime.cpp
2026-10-07 10:51:41 -07:00
Matthew Stanley 14001565cf Merge reviewed launcher worker capability opt-in (PR553) 2026-10-07 10:49:04 -07:00
Matthew Stanley ee140c9519 Merge reviewed sky scheduler opt-in (PR552) 2026-10-07 10:48:55 -07:00
Matthew Stanley a99ad54763 Merge reviewed host launch timing scopes (PR551)
# Conflicts:
#	runtime/src/mod_runtime.cpp
2026-10-07 10:48:55 -07:00
Matthew Stanley c6b089b896 Combine controller stack with validated PGXP fixes and current master; regenerate both BIOS backends 2026-10-07 10:01:37 -07:00
Matthew Stanley d5d9a65889 Integrate current master retaining strict GP0 fixture guards 2026-10-07 09:55:51 -07:00
Matthew Stanley 29a9e8d598 Contain best-effort preload failures and isolate audited cache tests 2026-10-07 09:30:57 -07:00
Matthew Stanley c124f6ba52 Wire warm preboot readiness and cover cache invalidation contracts 2026-10-07 09:25:56 -07:00
Matthew Stanley 0fb60d2617 Keep cached digest reads restricted to audited titles 2026-10-07 09:15:17 -07:00
Matthew Stanley 6e54d0f462 Reuse audited launcher preparation with validated dependency tickets 2026-10-07 09:14:22 -07:00
Shokunin c37b827e97 netplay rollback: every seat answers the episode handshake
The episode kept one peer-baseline / ready / POST latch, so with three or
four seats the first reply answered for everyone, and the one early
BASELINE stash was overwritten by the next seat's. In R4's 4-peer run with
the host loaded every frame, two guests replayed a light-tip episode and
resumed while the host had not opened it; host and guests then waited on
each other (pcap_freeze / wire_hole) until the stall timeout.

- BASELINE, ready-ACK and POST go through rnet_rb_quorum: the episode moves
  on only when every other occupied seat has answered, and a digest from
  any seat that differs fails the comparison. Early BASELINEs stash per
  seat. Two seats behave as before.
- Light tip skips the ready RTT only with one peer; with more, every seat's
  ready is how the others know it rolled back to the same load.
- A follower that has not simulated the load tick yet defers and joins when
  it gets there (no NACK, no zombie-load refusal for that join).
- Seal, ready and verify timeouts restart while a seat still owing a reply
  advances its input tip: they fire only when nothing moves.
- psx_netplay_peer_seats(); pins recomp-net rb_quorum.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 08:09:13 -07:00
Shokunin 465f926ecf netplay: confirm against every peer, shed the own view when behind, progress-based stall timeout
Four-peer rollback desynced under load (R4, host behind): the guests
predicted the slow host's pad, hash-confirmed the tick among themselves
through the one shared peer ring, then promoted the host's real pad into
history only (hc-silent promote) while the host simulated it. The host
stayed forked; its recovery episode was then refused as a "zombie load"
behind the guests' (wrongly) agreed tip.

- Hash confirm is a quorum over every occupied peer seat
  (rnet_hc_set_peer_mask / note_peer_from, recomp-net) for three or more
  seats; two seats keep the one-peer chain.
- A peer more than 4 ticks behind the others' inputs sheds its own view
  (psx_mod_render_local_view status FAST_FORWARD, held 60 ticks), so the
  presentation-only pass goes before the simulation slips.
  PSX_NET_LOCAL_VIEW_SHED=0 keeps it.
- The admit-stall watchdog counts time without progress: a remote input tip
  that advances restarts it, so a slow peer is waited for.
  PSX_NET_ADMIT_STALL_MS (20000) and PSX_NET_LIVENESS_MS (1500) configure it.
- Pins recomp-net fix/rollback-span-stall.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:59:03 -07:00
Matthew Stanley 72622466f8 Allow audited titles to opt into worker-safe launcher preparation 2026-10-06 23:32:26 -07:00
Matthew Stanley bceadc6124 Add DuckStation-compatible texture pack mod and gameplay dumping
Load modern texpage/texupload PNG replacements independently of DuckStation source. Integrate pack folders and options with mods, preserve native VRAM, and validate GL rendering, format matching, dump safety, and upload invalidation.
2026-10-06 23:24:47 -07:00
Matthew Stanley e4b6ddc99a Require measured host credit before admitting render replay 2026-10-06 23:03:48 -07:00
Matthew Stanley 8ba237ccf5 Retain host launch and mod commit stage timings for TCP diagnostics 2026-10-06 22:46:05 -07:00
Shokunin 5f72282fa5 Merge remote-tracking branch 'origin/feat/title-rewind-block' into feat/netplay-input-transform
# Conflicts:
#	generated/OpenBIOS.emitter.sha
#	runtime/src/debug_server.c
2026-10-06 19:37:46 -07:00