mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-28 19:07:47 -04:00
fix/pgxp-mod-initialization
12 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bc2106f7c7 |
Test: the window mode refuses render passes
Once the frame-rate stack's render passes land, a pass backs up and restores only s_hr_fbo, at s_hr_scale. In the 8K window mode the presented surfaces are the tiles at s_out_scale, which a pass would not restore, and the queued native-wide replay has no pass clamp, so the backend must refuse passes there (gl_renderer_pass_unavailable: s_hiw returns PSX_MOD_RENDER_PASS_BACKEND). Nothing enforced that but prose. test_gl_internal_scale_guards: where the renderer defines gl_renderer_pass_unavailable, its s_hiw refusal must return PSX_MOD_RENDER_PASS_BACKEND and pass_begin and pass_plan must go through gl_renderer_pass_ready; skipped before render passes exist. The GL fixture gains a passes mode, built only when the renderer has render passes: with the flip-aware blend ready, the full-VRAM surface at 9x offers passes and the window mode at 9x and 18x refuses them and pass_begin opens nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9805a00a7e |
GL: queue the native-wide mirror with the high-resolution window's draws
With lines batched, 8K widescreen still switched to the wide surface and back for every textured or flat batch that reaches the margins (~29 per frame at 21:9 in an R4 race; 32:9 flushes ~4x as many textured batches). At 18x those surfaces are 10080x9216 and 15372x9216, and each switch ends a render pass: with the mirror drawn into the hr surface instead (no switch, gl_ws_ablate 3) 32:9 at 8K ran at 57 guest frames/s instead of 12. In windowed mode a draw's native-wide mirror now rides in its window queue entry (target surface, x shift, backdrop stretch) and is replayed in the queue's flush, in order, one pass per wide surface (hiw_replay_wide). The queue's sync points already cover what the mirror samples (the raw mirror) and the stencil rebuild; every other write to a wide surface (the clears, the full-screen overlay rect, reallocation) and every read of one (present, capture, dumps) now flushes the queue first. Outside windowed mode, and with the mirror ablated, the immediate mirror runs as before. - gl_scale_invariance_test: the window runs' native-wide surfaces (the scene's and the new lines runs') stay byte-identical to the full-VRAM runs, and the lines runs check that the mirrors were queued. Mutations fail it: no queueing (the queue check), a wrong x shift or no replay (the wide digests). - R4, same setup as the previous commit: 21:9 at 8K 34.2-37.7 -> 56.1-56.6 guest frames/s (4:3 at 8K: 59.4), 32:9 12.6-13.8 -> 45.1-45.9; mirror passes per frame 29 -> 0.5. With the debug server's per-frame readback on (the default debug build): 21:9 43.7, 32:9 40.0. - test_gl_internal_scale_guards pins the queued path, the replay state and the sync points. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
624fadc14f |
GL: batch lines in the high-resolution window mode too
In windowed mode (8K past a 16384 texture limit: VRAM at 1x plus the high-resolution window at S) lines stayed immediate. Each one drew GL_LINES on the 1x hr surface and then, with native-wide, switched to the wide surface (10080x9216 at 21:9 and 18x, 15372x9216 at 32:9) and back for its mirror. In an R4 Grand Prix race that was ~130 mirror passes per frame: 21:9 at 8K ran at 15 guest frames/s and 32:9 at 5.5. A batched line now carries two vertex sets. Its one-native-pixel quad joins the flat batch with the triangles, as above 1x, and is what the window and the wide surfaces draw. Its two GL_LINES vertices go in s_fbl, with the batch position of its quad in s_fbl_at. The 1x hr draw (flat_batch_draw_hr_lines) walks the batch in order, drawing triangle runs from s_fb and line runs as GL_LINES from the copy uploaded after the batch's vertices, so painter order and the 1x pixels are unchanged. A batch without windowed lines keeps its single draw; outside windowed mode nothing changes (s_out_scale > 1 is the old gate there). - gl_scale_invariance_test: new "lines" runs (lines interleaved with triangles that overlap them, over a native-wide surface) at 1x and 9x on the full-VRAM surface and 9x and 18x in windowed mode. The windowed runs' native frame equals the 1x run's, and at 9x their frame and wide surface equal the full-VRAM run's; above 1x the lines and the triangle must share one flat batch, windowed lines with their GL_LINES vertices. Every existing digest line is unchanged, and the lines runs give the same digests against the renderer without this change (immediate lines). Mutations fail it: drawing all triangles before all lines, or the quads on the 1x surface. - R4 (scratch merge with feat/adaptive-widescreen c042434c and the R4 widescreen mod, one race savestate, debug build, GL timer queries and the debug server's per-frame VRAM readback off): 21:9 at 8K 15.0 -> 34.2-37.7 guest frames/s, 32:9 5.5 -> 12.6-13.8; mirror passes per frame 132 -> 29. - test_gl_internal_scale_guards pins the gate, the second vertex set and the ordered 1x draw. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c47a9059b9 |
GL: batch line quads above 1x with the flat triangles
Found in the R4 integration build: widescreen 21:9 (native-wide) at 4K ran a Grand Prix race at 22-30 guest frames/s instead of 60 (26 with the debug build's GL timer queries off). A CPU sample put ~57% of the emulation thread in glb_draw_line -> gpu_geometry -> glDrawArrays -> the driver ending a render pass: every line was drawn on its own, flushing the textured batch and, with native-wide, binding the wide surface and the hr surface again for its mirror. R4 draws hundreds of lines per race frame; at 9x each of those render-pass switches stores and reloads a 9216x4608 surface. Above 1x (full-VRAM surface, s_hr_scale > 1, not windowed) a line is already a quad of two triangles (line_to_quad), drawn with the GEO program like a gouraud triangle. It now joins the flat batch: same vertices, same program, same painter order and batch keys (semi, mask). A line the backdrop-stretch gate would widen keeps the immediate path, since the flat batch mirrors unstretched. 1x keeps GL_LINES unchanged and the windowed high-resolution mode keeps its own path. - run_gl_scale_invariance (real GL; its fixture draws horizontal, diagonal, steep and side-by-side lines): passes, and all 48 digest lines of its 14 probe runs are identical to the build without this change. - R4 21:9 at 4K from one race savestate (debug build, GL timer queries off, widescreen only): 26.1 -> 59.9 guest frames/s over 4-10 s; 4:3 at 4K stays 59.9. - test_gl_internal_scale_guards pins the gate and that the batched path issues no draw of its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
84dbc2982a |
GL: keep side-by-side buffers at full resolution; bound copy staging
Windowed high-resolution mode kept one window over the union of displayed columns. Two 512-wide buffers side by side need 18432 px at 18x, past a 16384 GPU limit, so the second buffer was refused and every other frame presented at 1x. - The window is now up to four tiles, each its own surface over a column range. A new display first tries one surface over the union of all displays (the old behaviour, so R4's 320-wide window is unchanged), then a merge with the tiles it overlaps, then a tile of its own. Every mirrored write (queued draws, fills, uploads, copies, the depth24 clear, stencil rebuilds) goes to each tile it touches; present, captures, screenshot_hires and the native-wide centre read the tile holding the display. A display no tile can hold within the GPU limit, the memory budget (all tiles together) or the tile cap still presents at 1x, logged. - A copy into the window stages its S-scaled source in a scratch of its own, in column chunks that fit the GPU limit, walked in memmove order so an overlapping copy still reads pre-copy pixels. The shared scratch no longer grows past the hr surface for it. - Staging textures commit a new size only after the driver accepted the storage; a size past the limit is refused with a log line and the texture keeps its old storage (scratch_ensure used to record the size before a glTexImage2D that could fail). - PSX_GL_MAX_DIM lowers the GPU limit the backend plans with (testing a layout for a smaller GPU). video_info adds display_x/display_y, hires_window_tiles and hires_window_mib. - gl_scale_invariance_test: side-by-side runs flip two 512-wide buffers and require one surface at 5x and two tiles at 9x (PSX_GL_MAX_DIM=8192) to equal the full-VRAM surface, including copies between the buffers and 1000-column copies; at 18x both buffers must read back at S. A scratch request past the limit must be refused and leave the texture and its recorded size intact. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
dbfb175903 |
GL: split s_scale into s_hr_scale and s_out_scale; poison the old name
The high-resolution window made the GL backend's single scale two: the scale of s_hr_fbo (1 in windowed mode) and the scale presented (up to 32). The previous commit kept s_scale for the presented one, so code written against the old contract (s_hr_fbo at s_scale) still compiled after a merge and read the 1024x512 surface at 18x coordinates in windowed 8K. The frame-rate branches add exactly such reads (render-pass capture, backup, journal and verify). Rename the presented scale to s_out_scale, keep s_hr_scale, document the contract at the declarations, and #pragma GCC poison s_scale, so every use of the old name fails to build and must pick one: s_hr_scale for s_hr_fbo and whatever backs it up or mirrors it, s_out_scale for the window, the wide surfaces and captures of the presented image. A VRAM transaction must also back up and restore the window's rows while hiw_on(), or refuse to run. No behaviour change: a mechanical rename. gl_scale_invariance_test (real GL, window forced at 2/3/5/9/18x) passes; new guards check the poison and that a function binding s_hr_fbo at s_out_scale also handles the window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9f08b6d9b7 |
GL: queue the high-resolution window's draws into one render pass
Mirroring every batch into the window as it was flushed switched framebuffers twice per batch. On Apple's GL (Metal underneath) each switch ends a render pass, about 80 us: 10 ms of CPU per R4 frame at any window scale, and the 8K preset ran at 21 fps (frame_perf total 47 ms, cpu_flush 17.4 ms). The window's copies of textured, flat and line draws are now queued (vertices plus the state they were drawn with: blend key, set-mask, mask-check, texture window, filter, sampled texture, draw area) and replayed into the window in one pass at the next sync point: before the raw mirror changes (pack, upload, depth24 clear), before any immediate write to the window (fill, copy, upload, stencil rebuild), and before every read of it (present, capture, growth, hires readback). Order within the window is kept, so it equals an immediate mirror wherever it is observed. tex_batch_draw_passes and mask_stencil take their mask state as parameters (tex_draw_passes_ex, mask_stencil_ex) so the replay uses the recorded state; the canonical pass calls them with the live state, the same GL call sequence as before (native and 2x/4x digests unchanged). Measured on an M4 in a race: 8K total 16.68 ms per present (60 Hz cadence), cpu_flush 0.62 ms; forced window at 4K likewise. The invariance test still finds the window's frame byte-identical to the full-VRAM surface at 2/3/5/9x. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
2211ecabf3 |
GL: true 8K past a 16384 texture limit with a high-resolution window
8K is 18x at a 240-line reference, and a full-VRAM surface at 18x is 18432 px wide: over the 16384 limit of Apple's GL, Intel and MoltenVK. A title only needs its displayed frame at 18x, so past the full-VRAM limit the GL backend now splits the work: - The authoritative VRAM stays the ordinary hr surface at 1x, i.e. the native renderer unchanged: pack, CPU readback, render-to-texture and VRAM copies are exactly the native results (s_hr_scale; s_scale stays the presented scale gr_scale reports). - A presentation-only surface at S covers the displayed columns W = [x0, x1) x all 512 rows (R4: 0..319, 5760x9216 at 18x, 405 MiB). Every GPU write touching W is mirrored into it: textured and flat batches, lines (quads), fills, uploads, the depth24 clear, and VRAM copies (hi to hi when the source is in W, otherwise the 1x source upscaled; both staged before drawing so overlapping copies read pre-copy pixels). It keeps its own stencil. A separate blit program carries the window projection, so the canonical shaders are untouched. - W grows (64-column aligned) the first time a display rectangle outside it is presented, seeded by upscaling the 1x content. Present, hold-last, interpolation capture, screenshot_hires and the native-wide centre read it; a display it cannot hold presents at 1x. It engages only when the full-VRAM surface cannot hold the request (driver limit or budget), sized so a 320-wide window fits both; up to 16x on the M4 nothing changes. PSX_GL_HIRES_WINDOW=0/1 disables or forces it. The launcher note and video_info (hr_scale, windowed, hires_window_*, hires_fbo) report it. Default-off: at 1x and whenever the full-VRAM surface fits, the paths are the previous ones (same native-VRAM and present digests on a fixed scene at 1x and 2x). Tests: gl_scale_invariance_test now also forces the window at 2/3/5/9x and requires its frame at internal resolution to be byte-identical to the full-VRAM surface at the same scale (copies inside, into and across the window edge, fills/uploads across it, mask, all blend modes) with native VRAM identical to 1x, and runs it at 18x; gl_scale_limits_test covers the window clamp; guards pin that it only engages past the full-VRAM limit. Verified in R4 on an M4: the 8K preset renders 5760x4320 in a race (rear- view mirror present) and 5760x8640 on a 480i menu. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
bb2864bc60 |
Settings: an older launcher's Supersampling pick wins over a preset
A host built against a recomp-ui without the Internal resolution row (RECOMP_LAUNCHER_HAS_INTERNAL_RESOLUTION undefined) still seeded internal_resolution on first boot and applied it after the launcher. From a fresh start the seed was "native", so a Supersampling pick of 3x was reset to 1x and "native" was saved; on the netplay soft-return the sticky preset was re-applied at session_reboot. The legacy row stopped working. Both launcher exits now go through two pure helpers in internal_resolution.h: - psx_ir_launcher_seed_supersampling: with the row, the factor as before; without it, the factor itself when no preset is configured (the historical round trip), else the preset's scale capped at the row's 4x. - psx_ir_adopt_launcher: with the row, the row's preset as before; without it, a pick in the Supersampling row drops the preset, and an untouched row keeps a preset the player cannot see (game.toml or a hand-edited settings.toml). It also returns what settings.toml saves: no internal_resolution key unless a preset is kept. internal_resolution_test drives seed -> launcher -> adopt for both launcher kinds, first boot and soft-return, against a model of the legacy row (clamp to 1..4, cycle); keeping the old sticky preset fails it. gl_internal_scale_guards pins both exits to the helpers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
ecda594996 |
Settings: never persist the PSX_INTERNAL_RESOLUTION override
The environment override was written into g_video_internal_res, the configured preset. On a netplay soft-return the launcher was seeded from it and saved it to settings.toml, so one local peer started with PSX_INTERNAL_RESOLUTION=8k left 8k in the settings.toml the other peer shares: the case the override exists for. Keep the override in g_video_internal_res_env. It still wins for the run (scale, Match display, HiDPI window, video_info), but the launcher only shows, and settings.toml only saves, the configured preset. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
f34f91cc25 |
Settings: Internal resolution presets (Native ... 8K, Match display)
Add a player-facing internal resolution on top of the raised GL ceiling. A preset is a target height; the runtime renders at the integer scale S = ceil(target / reference_lines), reference 240 by default: 720p 3x, 1080p 5x (area-resolved to 1080), 1440p 6x, 4K 9x, 5K 12x, 8K 18x. The backend then clamps S to what it can allocate. - runtime/include/internal_resolution.h: the preset table, stable ids, the value encoding shared with recomp-ui (0 unset, 1 native, -1 match display, N target lines) and the pure resolver. - config: [video] internal_resolution (a preset id or a number of lines) and resolution_reference_lines in game.toml; internal_resolution in settings.toml, written as a stable id beside supersampling = min(S, 4) for older runtimes. Precedence: game default < player's legacy factor < player's preset. settings.toml window_width now accepts up to 7680. - main.cpp: resolve the preset before gpu_init; Match display re-resolves from the game window's monitor (SDL3 mode height x pixel density) before the GL context allocates. The launcher gets the vocabulary (with a synthesized "Nx (L lines)" entry for a legacy factor no preset matches) behind RECOMP_LAUNCHER_HAS_INTERNAL_RESOLUTION, on first boot and on the netplay soft-return. - The OpenGL game window gets SDL_WINDOW_HIGH_PIXEL_DENSITY (SDL2: ALLOW_HIGHDPI) only when the player chose more than native, so the drawable is in pixels on Retina; native keeps the window as it was. - debug server: video_info (preset, requested and effective scale, GL limit and clamp, hr surface, window points vs pixels), screenshot_hires reads the GL hr FBO at full size, screenshot_wide_hires captures the native-wide band at internal resolution. - docs: config_schema (keys, precedence), NETPLAY (a per-peer presentation setting), TCP_COMMANDS (index regenerated), ENHANCEMENTS IR1. Default-off: with no internal_resolution anywhere the scale, the window flags and the present path are unchanged. Tests: internal_resolution_test (presets x reference 240/480 x backend maxima, legacy mapping round trip), video_enhancement_settings_test (game and settings keys, stable-id round trip, legacy key still written), gl_internal_scale_guards (HiDPI only when opted in, launcher fields behind the capability macro). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
eee07fe910 |
GL: raise the internal-resolution ceiling to the driver's limits
The OpenGL backend capped supersampling at 4, the software mirror's limit, although its hr surface lives on the GPU. Raise the compile-time ceiling to 32 (gpu_render.h) and decide the real scale at context init: - Query GL_MAX_TEXTURE_SIZE, GL_MAX_RENDERBUFFER_SIZE and GL_MAX_VIEWPORT_DIMS and clamp S so the 1024*S x 512*S surface fits them and a 2 GiB memory budget (PSX_GL_VRAM_BUDGET_MB overrides it). Apple's GL on Metal reports 16384, so 16x is the most a full-VRAM surface can be. The clamp is logged with the limit that applied. - If the surface still fails to allocate, retry one scale lower inside GL. Only a 1x failure drops the backend to software, as before. - Draw lines above 1x as quads one native pixel thick covering both endpoints. glLineWidth(S) is capped at 1 on core profiles, so every line was one hr pixel thick (1/9 of native at 9x). - Present a supersampled source with an area resolve when it is more than 1.25x the output (1200 lines into a 1080 window, 480i menus), and inset the UV rect by half a texel instead of half a native pixel, which cropped and zoomed the image by (S-1)/2 texels per edge. - Size the copy scratch to the largest copy (copies stage at its origin) and rebuild the mask stencil in tiles, only over the primitive bbox union, so a large S does not cost another full-size surface. - Refuse a native-wide surface wider than the driver limit (uncapped Fit at a high scale) with one log line; the frame presents 4:3. - main.cpp: the offline and dual-raster clamps no longer cap GL at the software 4x. The CPU present path presents GL frames at 1x (its readout is native) and its staging buffer stays sized for at most 4x. - [video] supersampling accepts 1..32 in game.toml and settings.toml; the runtime clamps per backend. Every change is gated on S > 1: at 1x the renderer is byte-identical (same native VRAM and presented framebuffer digests as the previous source on a fixed GP0 scene). Tests: gl_scale_limits_test (clamp arithmetic), gl_internal_scale_guards (source guards + runner parsing), and gl_scale_invariance_test, a hidden real-GL run at 1,2,3,5,9x that requires identical guest-visible VRAM at every scale, one-native-pixel lines at internal resolution, and a 32x request that stays on GL at the driver's clamp (16x on Apple). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |