From 958ae0e396f14d39c86e5c016d405fcdd96af4d5 Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Tue, 20 Oct 2026 15:43:47 -0700 Subject: [PATCH 01/29] Document manual PSX mod authoring against the current package format --- README.md | 7 +- docs/MOD_PACKAGES.md | 12 +- psxmod.md | 474 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 488 insertions(+), 5 deletions(-) create mode 100644 psxmod.md diff --git a/README.md b/README.md index da75d6ff..dfa217c5 100644 --- a/README.md +++ b/README.md @@ -233,7 +233,8 @@ These are the 3 most important folders to be aware of: | **Netplay** (rollback, SFU/ICE, dual-raster, disc gates) | [`docs/NETPLAY.md`](docs/NETPLAY.md) | | Bundled-release CI template | [`docs/ci/templates/game-release.yml`](docs/ci/templates/game-release.yml) | | Local Generate & rebuild CLI | [`docs/LOCAL_CODEGEN_SDK.md`](docs/LOCAL_CODEGEN_SDK.md) | -| Mods | [`docs/MOD_PACKAGES.md`](docs/MOD_PACKAGES.md) | +| Make mods by hand | [`psxmod.md`](psxmod.md) | +| Mod schema and integration | [`docs/MOD_PACKAGES.md`](docs/MOD_PACKAGES.md) | | Widescreen / native-wide | [`docs/WIDESCREEN.md`](docs/WIDESCREEN.md) | | Enhancement tier (PGXP, renderers, load accel) | [`docs/ENHANCEMENTS.md`](docs/ENHANCEMENTS.md) | | TCP debug command reference | [`docs/TCP_COMMANDS.md`](docs/TCP_COMMANDS.md) | @@ -301,6 +302,10 @@ See [`docs/WIDESCREEN.md`](docs/WIDESCREEN.md) for the per-game configuration. ## Mods +To make a package yourself, start with [`psxmod.md`](psxmod.md): a complete +first package, guarded patches, asset overlays, packing, and testing with +ordinary tools. No AI tools are required. + Mods are **versioned packages**, not patched discs. A `.psxmod` is an installation, provenance and trust boundary; each package contributes any number of independently toggleable **features**, and enabling one never silently diff --git a/docs/MOD_PACKAGES.md b/docs/MOD_PACKAGES.md index 60431ea6..8a3de9b8 100644 --- a/docs/MOD_PACKAGES.md +++ b/docs/MOD_PACKAGES.md @@ -1,12 +1,16 @@ # PSXRecomp mod packages and features +For a hands-on guide to making mods with ordinary tools, start with +[`../psxmod.md`](../psxmod.md). + A `.psxmod` is a versioned installation, provenance, and trust boundary. A package may contribute any number of independently configurable **features**. The launcher presents those features as the primary Mods list; package installation, version selection, and removal are a secondary management view. -Feature identity is always `(package_id, feature_id)`. Enabling one feature -never enables, disables, or reconfigures another feature. +Feature identity is always `(package_id, feature_id)`. Player selections are +independent. Explicit `[[requirement]]` declarations can additionally activate +required features for a session without changing saved player selections. The player selects a verified stock BIN/CUE. Resolution produces guarded native operations and sparse disc overlays without rewriting or replacing that stock @@ -173,12 +177,12 @@ stale the moment either side gains a mod. ## Feature manifest -Write new manifests at the current format version, which is **7**. Older +Write new manifests at the current format version, which is **10**. Older versions stay readable so installed packages survive an update, and each section below notes the version a field first required. ```toml -format_version = 7 +format_version = 10 id = "example.localization" version = "1.2.0" name = "Example Localization Pack" diff --git a/psxmod.md b/psxmod.md new file mode 100644 index 00000000..6e2a9aa9 --- /dev/null +++ b/psxmod.md @@ -0,0 +1,474 @@ +# Making a `.psxmod` by hand + +You can make PSXRecomp mods with a text editor, a ZIP packer, and the usual +tools for editing or understanding game data. No AI service, account, or +generated assets are required. Many mods need no C/C++ and no game rebuild. + +The extension is **`.psxmod`** (singular). It is a ZIP with a `manifest.toml` +at its root. The manifest describes the mod, launcher controls, and supported +operations. The game keeps using the player's stock disc; mod operations are +applied in memory or when reading sectors. + +Check the target game's pinned framework version. This guide describes +manifest format **10**; an older game release may need an update to read it. + +- [Choose the right kind of mod](#choose-the-right-kind-of-mod) +- [First package: your own bezel artwork](#first-package-your-own-bezel-artwork) +- [Make a guarded gameplay patch](#make-a-guarded-gameplay-patch) +- [Add options](#add-options) +- [Replace disc data with an overlay](#replace-disc-data-with-an-overlay) +- [When you need C or C++](#when-you-need-c-or-c) +- [Test and distribute](#test-and-distribute) +- [Troubleshooting](#troubleshooting) + +## Choose the right kind of mod + +| Goal | Mechanism | Rebuild the game? | +| --- | --- | --- | +| Change known bytes in the main executable | Guarded `[[patch]]` with `target = "main_exe"` | Usually no | +| Change a few known bytes on the disc | Guarded `disc_raw` or `disc_user` patch | Usually no | +| Replace a larger range of asset data | File-backed `[[overlay]]` | Usually no | +| Use an existing host feature | `[[plugin]]` naming an implementation the game already contains | No, if that implementation is present | +| Add new host logic or a recurring gameplay callback | Write and statically link a trusted C/C++ plugin | Yes | + +An overlay replaces bytes at a particular location. It is not an automatic +"replace an ISO file by filename" service. Formats, pointers, compression, +and file sizes still matter. A package also cannot load a DLL, compile C, or +invent a plugin implementation by naming it. + +A **package** is what you install and version. Its **features** are independent +switches; each feature can have **options**. An operation names its owning +feature and may have conditions on that feature's options. The loader resolves +the whole enabled set, checks compatibility and conflicting byte ranges, and +produces a plan before boot. Runtime guards verify original bytes when the +corresponding executable or disc data is encountered. + +## First package: your own bezel artwork + +This example uses the existing `psx.bezel` plugin to draw a player-selected +image behind the game frame in OpenGL margins. It requires a game build that +contains that plugin and supports format 10. It does not require a compiler. +You can draw the image yourself with any ordinary image editor. + +### 1. Create a source folder + +Make a folder named `my-bezel` containing `manifest.toml` and `README.txt`. +Use UTF-8 text and save the manifest with that exact name, not +`manifest.toml.txt`. Python **3.11 or newer** can run all commands in this guide. + +Save this complete manifest: + +```toml +format_version = 10 +id = "yourname.my-bezel" +version = "1.0.0" +name = "My Bezel" +author = "Your Name" +description = "Show my chosen artwork behind the game frame." +license = "MIT" +resolver = "declarative" +save_compatibility = "shared" + +[[target]] +game_id = "*" + +[[feature]] +id = "bezel" +name = "My Bezel" +description = "Choose an image, then launch with an OpenGL renderer." +group = "Visual" +default_enabled = false + +[[resource]] +feature = "bezel" +id = "artwork" +label = "Bezel image" +format = "file" +file_patterns = "*.png,*.jpg,*.jpeg,*.bmp" +file_description = "Image files" +required = false + +[[plugin]] +feature = "bezel" +id = "psx.bezel" +``` + +Here `game_id = "*"` is appropriate because the existing bezel implementation +is a generic presentation feature. Game-specific patches should target exact +game IDs and supported revisions instead. + +Use your own package namespace when publishing. Package and feature IDs are +machine identifiers: use lowercase letters, numbers, dots, underscores, and +hyphens. Names and labels are the human-facing text. `format_version` is the +schema version, while `version` is your mod's version. Keep the package ID +stable and increase the version when distributing changes. + +The image is a **resource**: the player chooses its path, and the package does +not contain or copy it. A plugin must understand the resource ID; this plugin +specifically looks for `artwork`. With no image selected, it draws nothing. +If you want packaged artwork loaded automatically, you need an implementation +that supports that behavior; adding an image to the ZIP is not sufficient. + +### 2. Check and pack it + +Put target-build requirements and instructions in `README.txt`. From the +**framework repository root**, run: + +```sh +python -c "import pathlib,tomllib; tomllib.loads(pathlib.Path('my-bezel/manifest.toml').read_text(encoding='utf-8')); print('TOML syntax OK')" +python tools/psxmod_pack.py my-bezel dist/my-bezel-1.0.0.psxmod +python -m zipfile -l dist/my-bezel-1.0.0.psxmod +python -m zipfile -t dist/my-bezel-1.0.0.psxmod +``` + +The packer uses only Python's standard library. It packages files in a stable +order with fixed ZIP timestamps. It checks that a manifest exists, but does +**not** validate the manifest schema or the mod's behavior. Keep its output +outside the source folder: it includes every file under that folder. + +The archive must look like this: + +```text +my-bezel-1.0.0.psxmod + manifest.toml + README.txt +``` + +A graphical ZIP utility works too: archive the folder's **contents**, use +ordinary ZIP storage or DEFLATE without encryption, and rename `.zip` to +`.psxmod`. The loader accepts up to 4096 entries and 256 MiB expanded. Absolute +paths and `..` paths are rejected. + +### 3. Install and try it + +Open the target game's launcher and its **Mods** package manager, install the +archive, enable **My Bezel**, select your image, and launch with OpenGL. Use +a window/aspect that leaves margins so there is somewhere to see the artwork. +Disable any other enabled bezel feature: two features claiming `psx.bezel` +conflict instead of taking turns. + +Disable your feature and relaunch to verify the normal presentation returns. +Changes generally require a new launch. Management button labels can vary +with the game's pinned recomp-ui version. + +## Make a guarded gameplay patch + +A guarded patch means "at this location, expect these original bytes, then +replace them with these bytes." The guard catches a wrong revision or a +mistaken address. It is not a substitute for understanding the change. + +### 1. Find and verify the change + +Use the game's symbols, disassembly, debugger, and existing mod source to find +the relevant code or data. Record: + +- The exact game ID, disc revision, and game build. +- The location and whether it is a guest address or a disc-stream offset. +- The original and replacement bytes, with a written explanation. +- When the code/data is loaded and what should change during play. + +For example, suppose you have verified a MIPS instruction that initializes a +value to 3: the word `0x24020003` (`addiu v0, zero, 3`). PS1 words are +little-endian, so its bytes are `03 00 02 24`. Changing the immediate to 5 +gives `05 00 02 24`. That only means "starting lives" if you have established +that the game uses the result that way. + +### 2. Write the package + +This is a complete **template**, not a cheat for a particular game. Replace +the target placeholders and the illustrative address with surveyed values. +Do not paste the address into an unrelated title. + +```toml +format_version = 10 +id = "yourname.starting-lives" +version = "1.0.0" +name = "Starting Lives" +author = "Your Name" +description = "Start a new game with five lives." +resolver = "declarative" +save_compatibility = "shared" + +[[target]] +game_id = "REPLACE_WITH_EXACT_GAME_ID" +disc_sha256 = "REPLACE_WITH_STOCK_DISC_SHA256" + +[[feature]] +id = "starting-lives" +name = "Starting Lives" +description = "Apply the surveyed new-game lives change." +group = "Gameplay" +default_enabled = false + +[[patch]] +feature = "starting-lives" +target = "main_exe" +address = 0x80012340 +expected = "03 00 02 24" +replace = "05 00 02 24" +``` + +`main_exe` uses a **guest virtual address**, not an offset in a BIN, ISO, or +PS-X EXE. These writes are applied after the BIOS loads the main executable +and before its entry point. A runtime variable that is initialized later can +overwrite your write; a streamed code overlay is not automatically part of +the main executable. Choose the operation that matches the actual load path. + +For a standard PS-X EXE, the payload begins at file offset `0x800`, and its +load address is the little-endian word at header offset `0x18`. For a location +inside that payload: + +```text +guest address = load address + (PS-X EXE file offset - 0x800) +``` + +This formula does not apply to arbitrary files or runtime-relocated overlays. +Confirm the address in a debugger. Guard a complete instruction when changing +an instruction, keep ordinary replacements equal in length to `expected`, and +account for MIPS delay slots and register use when editing logic. + +### 3. Get the right hashes + +Copy known target identities from the game's manifests and source where +possible. `exe_sha256`, when used, hashes the loose **PS-X EXE file**, not the +native PC executable; some releases do not carry that loose file, so requiring +it can make an otherwise correct package incompatible with those releases. + +For a typical BIN/CUE, `disc_sha256` hashes the resolved BIN data track, +**not the CUE text**. For CHD, the current runtime decodes the disc and hashes +its complete sequence of raw 2352-byte sectors; hashing the compressed CHD +file will give the wrong identity. Check the target title's pinned +`mod_runtime_commit()` and disc-resolution code, especially for multiple +tracks and older releases. Do not guess a hash from the disc's display name. + +For a BIN data file whose identity you have confirmed, this command prints its +hash without loading the whole disc into memory: + +```sh +python -c "import hashlib; f=open('game.bin','rb'); print(hashlib.file_digest(f,'sha256').hexdigest()); f.close()" +``` + +Use lowercase 64-character hashes. Additional `[[target]]` rows are +alternatives, not a way to translate addresses across revisions. Separate +packages or implementations may be needed if the actual byte locations differ. + +## Add options + +To make the example's value configurable, add this option to that package: + +```toml +[[option]] +feature = "starting-lives" +id = "count" +label = "Starting lives" +type = "integer" +min = 1 +max = 9 +step = 1 +default = 3 +``` + +Then **replace**, rather than add to, its original `[[patch]]`: + +```toml +[[patch]] +feature = "starting-lives" +target = "main_exe" +address = 0x80012340 +expected = "03 00 02 24" +replace_from = { option = "count", encoding = "u16le", offset = 0 } +``` + +The loader copies the expected bytes and fills the selected field with the +option value. `u8`, `u16le`, and `u32le` encode unsigned values. The declared +range must fit the encoding; the loader will not infer signed arithmetic or +arbitrary expressions. The linked schema guide covers split MIPS immediates +and sparse field ownership for more complex patches. + +Boolean and choice options can select operations with feature-local +conditions. For example, after declaring a `style` choice on this feature +with values `stock` and `extra`, a patch may contain: + +```toml +when = { style = "extra" } +``` + +All named conditions must match. Declare choice values with `[[option.choice]]` +entries immediately after their owning option; `default` must name one of +those values. Options alone do not change the game: patches consume them, or +a trusted plugin must query and implement them. + +## Replace disc data with an overlay + +Use `[[overlay]]` for a larger asset range stored in a separate package file. +Find the original asset and its format with ordinary disc/file tools, edit it +by hand, and export the replacement bytes. Preserve the game's expected +layout or explicitly implement the additional pointer/size changes needed. + +The two disc coordinate systems are: + +| Target | Offset calculation | Typical use | +| --- | --- | --- | +| `disc_raw` | `LBA * 2352 + byte within raw sector` | Raw-sector-aware changes, including formats outside a 2048-byte data payload | +| `disc_user` | `LBA * 2048 + byte within user data` | Logical 2048-byte data sectors | + +LBA is the logical sector number in the runtime's disc reader. A file-relative +offset is not an LBA. Match the runtime's track mapping; do not treat a +multi-track CUE as one flat file. A raw 2352-byte sector contains headers and +other bytes besides its data payload. The runtime maps Mode 2 Form 1 raw data +to its 2048-byte payload; `disc_user` is not the mechanism for XA/Form 2 audio +or CD audio sectors. + +Small disc `[[patch]]` writes use `offset` instead of `address` and cannot +cross a sector boundary. File-backed overlays may span sectors. For example, +add this fragment to a package that already declares a feature named `art`: + +```toml +[[overlay]] +feature = "art" +target = "disc_user" +offset = 204800 +file = "assets/replacement.bin" +sha256 = "REPLACE_WITH_PAYLOAD_SHA256" +expected_sha256 = "REPLACE_WITH_ORIGINAL_RANGE_SHA256" +``` + +The illustrative offset is LBA 100, byte 0. Use your own verified location. +`file` is relative to the package root. Its length determines the replacement +range; it does not insert space into the disc. Hash the replacement file for +`sha256`. The optional `expected_sha256` hashes the **same-length original +range in the selected target stream**, not the whole disc or raw sectors +when the target is `disc_user`. + +Every target row must declare an exact `disc_sha256` when using feature disc +overlays. Include the payload in the archive, update its hash whenever it +changes, and pack using the same command as the first example. The loader +checks payloads during installation/scanning and checks enabled payloads again +during resolution. + +To adapt an existing disc mod, inspect and understand its changes, then +reimplement the behavior as guarded operations, overlays, or a trusted plugin +against the stock disc. Do not just rename an IPS/PPF/VCDIFF to `.psxmod`. +The legacy `derived_disc` path is not supported for new feature-style mods. + +## When you need C or C++ + +Use a trusted plugin for new host behavior or a change that cannot be +expressed by the declarative operations. It lives in the **game repository**, +is reviewed and compiled into that game, and is selected by a manifest ID. + +A minimal implementation in `src/hello_mod.c` looks like this: + +```c +#include +#include "mod_plugins.h" + +static void hello_activate(void) { + fprintf(stdout, "My PSX mod is active.\n"); + fflush(stdout); +} + +PSX_MOD_CONSTRUCTOR(register_hello_mod) { + (void)psx_mod_register_activation_plugin( + "yourname.hello.startup", hello_activate); +} +``` + +Add it with `target_sources(MyGame PRIVATE src/hello_mod.c)`, using the game's +actual target name, and follow that repository's build instructions. A package +with the normal metadata, target, and a feature named `hello` selects it with: + +```toml +[[plugin]] +feature = "hello" +id = "yourname.hello.startup" +``` + +The plugin ID is a registry key, not a filename or exported symbol. Shipping +that C file inside a package does not install the implementation. Players +need the game build containing it. + +Activation callbacks run before renderer/window initialization. Deterministic +guest-VBlank callbacks can be registered with +`psx_mod_register_vblank_plugin()`; gate gameplay work until +`psx_mod_game_started()`. The narrow guest read/write services and option and +resource queries are in [`mod_plugins.h`](runtime/include/mod_plugins.h). +For guest instructions, use `psx_mod_write_code_word()` rather than an ordinary +data write so executable-RAM handling sees the change. Test actual execution +and save-state restoration; compilation alone cannot establish correctness. + +For assets or settings, read the committed package/feature option or resource +instead of opening a player's `state.toml` yourself. Register no unconditional +gameplay effects merely because the source file is linked. Disabling the +feature must restore stock behavior on a fresh launch. + +## Test and distribute + +Use a separate game installation and fresh mod selections, with normal saves +backed up. Test the **packed archive**, including its actual assets. + +- Verify the intended game revision and build, feature on/off behavior, and + each option including range limits. +- Exercise what changed: new-game initialization, relevant stages or menus, + transitions, death/retry, audio, and save/load as appropriate. +- Test wrong original bytes and missing/changed assets. A rejected guard is + evidence to investigate, not a reason to remove the guard. +- Test relevant mod combinations. Different writes to the same owned bytes + fail rather than letting a load order choose a winner. Match the reported + package, feature, target, and range to the source operations. +- Validate netplay in actual supported sessions before claiming it. Matching + compilation or version labels alone does not prove compatible simulation. + +Use `channel = "experimental"` on an unvalidated feature; it ships with a +badge and defaults off. `channel = "developer"` is for local development and +is unavailable in release builds. Features can declare their own channel. +Elective enhancements should be opt-in; engine correctness fixes +belong in the normal implementation. + +Repository release policy excludes **CD Speed** +(`psx.enhancement.cd-speed`) and **Fast Loading / host pacing** +(`psx.enhancement.fast-loading`) entirely from game releases. Do not ship +their packages or controls. Development catalogs keep both hidden and off. + +Installed and bundled packages have different owners: + +```text +/mods/ + bundled/// staged build output; rebuilt by the game + installed/// player packages installed by the launcher + state.toml player selections +``` + +Keep your editable sources outside both staged and installed trees. Game +maintainers put bundled sources under +`mods/preloaded/packages///` and pass the catalog through +`PRELOADED_MODS_DIR` when creating the runtime target. The framework stages it. + +Distribute the `.psxmod` with source, license/credits, supported game IDs and +hashes, minimum tested build, known limits, and a short changelog. State when +a custom executable is needed. The package's license covers only work you can +license; include only assets/code you have permission to redistribute. Stock +disc/BIOS images, personal saves, and `state.toml` do not belong in the ZIP. +`save_compatibility` metadata is not a promise that a gameplay change can load +every existing save; validate that behavior explicitly. + +## Troubleshooting + +| Symptom | What to check | +| --- | --- | +| No root manifest | Archive the contents, not an extra parent folder. | +| Unsupported format version | The game's pinned framework may predate format 10. Update the game or deliberately use only fields its older schema supports. | +| Package absent / manifest ignored | Read the game's scan error for the path and parsing reason. Syntax validation alone is not schema validation. | +| Target incompatible | Match exact game ID and actual runtime disc/EXE hash inputs. | +| Expected-byte guard failed | Recheck revision, load timing, byte order, and address/offset coordinates. | +| Overlay payload checksum failed | Pack the right file and update its lowercase SHA-256. | +| Missing trusted plugin | Use a game build containing the implementation. An ID alone cannot add code. | +| Conflicting features | Inspect the reported owned range or plugin ID and disable one; ZIP order is not patch priority. | +| Bezel enabled but invisible | Select an image, use OpenGL, and leave visible margins. | +| Package version already installed | Bump `version` or remove your prior test version through the manager. | +| Changes vanish after rebuilding | Edit source catalogs, not the build's `mods/bundled` output. | + +See [`docs/MOD_PACKAGES.md`](docs/MOD_PACKAGES.md) for advanced conditions, +sparse fields, dependencies, and resolution rules; the implementations are +[`mod_packages.cpp`](runtime/src/mod_packages.cpp) and +[`mod_runtime.cpp`](runtime/src/mod_runtime.cpp). From 416c9d14e85b953ecc9de7c25954daac0a5f6e2d Mon Sep 17 00:00:00 2001 From: Matt Stanley <1379tech@gmail.com> Date: Wed, 21 Oct 2026 12:21:21 -0700 Subject: [PATCH 02/29] Refresh beta README with X4 co-op and a human porting guide (#726) * docs: refresh psxrecomp beta showcase and getting started * docs: simplify project title and remove CI guidance --- README.md | 170 ++++++++++----------- docs/GAME_PROJECT_SETUP.md | 71 --------- docs/GETTING_STARTED.md | 104 +++++++++++++ docs/SHOWCASE_SOURCES.md | 21 +++ docs/assets/games/final-bout-ultrawide.png | Bin 0 -> 3629863 bytes docs/assets/games/mega-man-x4-coop.png | Bin 0 -> 230723 bytes docs/assets/games/mega-man-x4-netplay.png | Bin 0 -> 71853 bytes 7 files changed, 208 insertions(+), 158 deletions(-) create mode 100644 docs/GETTING_STARTED.md create mode 100644 docs/SHOWCASE_SOURCES.md create mode 100644 docs/assets/games/final-bout-ultrawide.png create mode 100644 docs/assets/games/mega-man-x4-coop.png create mode 100644 docs/assets/games/mega-man-x4-netplay.png diff --git a/README.md b/README.md index dfa217c5..49fe6baa 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,41 @@ > ℹ️ **Note from mstan:** This repo and I are now part of [RetroPortingToolkit](https://retroportingtoolkit.com/). I remain a primary maintainer of psxrecomp alongside the team. [More info](https://1379.tech/forming-a-collective-retro-porting-toolkit/). -**A general-purpose static recompiler for the PlayStation 1.** It turns a PS1 -disc into a native executable — MIPS R3000A translated to C, compiled to x64, -linked against a hardware-accurate runtime. Not an emulator: the game becomes a -program your CPU runs directly. +**A beta static-recompilation framework for the PlayStation 1.** PSXRecomp +translates MIPS R3000A programs into C and builds native applications around +a shared PS1 hardware runtime. Streamed overlays can be captured, compiled +and cached; unresolved code has an interpreter fallback. -Titles brought up on it ship as standalone builds with widescreen, mods, live -language switching and a launcher. They can run on either the bundled, -open-source **OpenBIOS** or a compatible **retail BIOS** supplied by the player; -OpenBIOS is the default, so a retail BIOS dump is not normally required. The -framework is general-purpose: bringing up a new game is a matter of -configuration and reverse engineering, not new engine work. +Game projects add **adaptive widescreen, higher-resolution rendering, local +and online multiplayer, runtime translations, mods and modern launchers**. +Bundled open-source **OpenBIOS** is the default; a compatible retail BIOS is +optional. Each game verifies the player's own disc image. No commercial +disc or BIOS data is included. + +**[Play a game](#games)** · **[Create your first port](docs/GETTING_STARTED.md)** +— a focused Windows setup with no AI required. + +## Mega Man X4: X and Zero in co-op + +Take X and Zero through the same campaign with **two-player local co-op or +netplay**. The host chooses the shared options. Unified camera is the +default; independent online views and local side-by-side views are +experimental. **The X4 co-op mod remains an alpha playtest**, independently +of the framework's beta status, and requires OpenGL and a fresh boot. + +

+ X and Zero together at the Jungle waterfall, with separate lifebars +
Mega Man X4 — X + Zero Co-op. Local play and online rooms from the launcher. +

+ +
+See the online and LAN launcher + +Mega Man X4 launcher offering LAN / Direct IP and Online Netplay + +
+ +## More of the PlayStation world @@ -36,32 +60,40 @@ Background on the original prototype: ## Games -Each game is its own repository that pins a framework commit as a submodule and -ships its own playable release with an in-app launcher. Validation and feature -coverage continue to improve per title. +Each project pins its framework revision and owns its disc support, platforms +and validation. **Beta describes the framework; individual ports and mods +can still be previews.** This list was checked against public documentation +and release notes on **21 October 2026**. Open each project for current setup +and testing limits; source projects do not all provide finished downloads. -| Game | Repository | Latest build | Notes | -|---|---|---|---| -| *Tomba!* | [TombaRecomp](https://github.com/mstan/TombaRecomp) | [releases](https://github.com/mstan/TombaRecomp/releases/latest) | Widescreen, supersampling, save/load, mod packages. | -| *Tomba! 2* | [Tomba2Recomp](https://github.com/mstan/Tomba2Recomp) | [releases](https://github.com/mstan/Tomba2Recomp/releases/latest) | Multi-track disc support; adaptive widescreen through 21:9. | -| *Ape Escape* | [ApeEscapeRecomp](https://github.com/mstan/ApeEscapeRecomp) | [releases](https://github.com/mstan/ApeEscapeRecomp/releases/latest) | Widescreen to 21:9, memory-card save/load, dual-analog. | -| *Mega Man X4* | [MegaManX4Recomp](https://github.com/mstan/MegaManX4Recomp) | [releases](https://github.com/mstan/MegaManX4Recomp/releases/latest) | Playable; 2D widescreen. | -| *Mega Man X5* | [MegaManX5Recomp](https://github.com/mstan/MegaManX5Recomp) | [releases](https://github.com/mstan/MegaManX5Recomp/releases/latest) | Playable; 2D widescreen. | -| *Mega Man X6* | [MegaManX6Recomp](https://github.com/mstan/MegaManX6Recomp) | [releases](https://github.com/mstan/MegaManX6Recomp/releases/latest) | Playable; stages, controller, save/load; 2D widescreen to 21:9. | -| *Tsumu Light* | [TsumuLightRecomp](https://github.com/mstan/TsumuLightRecomp) | [releases](https://github.com/mstan/TsumuLightRecomp/releases/latest) | Japanese-only title (SLPS-02253); first consumer of [live language switching](#swap-language-on-the-fly). | -| *Xenogears* — **community** | [OpokXeno/xenogears-recomp](https://github.com/OpokXeno/xenogears-recomp) | — | Independent project by [@OpokXeno](https://github.com/OpokXeno), who also contributed widescreen cull-site work upstream. | -| *Mega Man Legends* — **community** | [VuloB1/MegaManLegendsRecomp](https://github.com/VuloB1/MegaManLegendsRecomp) | [releases](https://github.com/VuloB1/MegaManLegendsRecomp/releases/latest) | Native 16:9 widescreen, analog movement with right-stick camera, 60/120 Hz presentation, skip FMVs, PGXP precision. | +| Game | Project / downloads | Features and scope | +|---|---|---| +| *Mega Man X4* | [MegaManX4Recomp](https://github.com/mstan/MegaManX4Recomp) · [downloads](https://github.com/mstan/MegaManX4Recomp/releases) | X/Zero local and online co-op alpha, optional widescreen, experimental independent cameras, offline save states and rewind. | +| *Mega Man X5* | [MegaManX5Recomp](https://github.com/mstan/MegaManX5Recomp) · [downloads](https://github.com/mstan/MegaManX5Recomp/releases) | Preview with 2D widescreen, launcher and native gameplay. | +| *Mega Man X6* | [MegaManX6Recomp](https://github.com/mstan/MegaManX6Recomp) · [downloads](https://github.com/mstan/MegaManX6Recomp/releases) | 2D widescreen, controller support, save/load and game-specific mods. | +| *Tomba!* | [TombaRecomp](https://github.com/mstan/TombaRecomp) · [downloads](https://github.com/mstan/TombaRecomp/releases) | Widescreen, supersampling, save/load and mod packages; per-title release remains a preview. | +| *Tomba! 2* | [Tomba2Recomp](https://github.com/mstan/Tomba2Recomp) · [downloads](https://github.com/mstan/Tomba2Recomp/releases) | Multi-track disc support and adaptive widescreen through 21:9. | +| *Ape Escape* | [ApeEscapeRecomp](https://github.com/mstan/ApeEscapeRecomp) · [downloads](https://github.com/mstan/ApeEscapeRecomp/releases) | Dual-analog controls, memory-card saves and widened 3D views. | +| *Tsumu Light* | [TsumuLightRecomp](https://github.com/mstan/TsumuLightRecomp) · [downloads](https://github.com/mstan/TsumuLightRecomp/releases) | Japanese title with runtime language switching. | +| *Jersey Devil* | [JerseyDevilRecomp](https://github.com/mstan/JerseyDevilRecomp) · [downloads](https://github.com/mstan/JerseyDevilRecomp/releases) | Native desktop port with disc-verifying setup, controllers and game-specific presentation work. | +| *Um Jammer Lammy* | [UmJammerLammyRecomp](https://github.com/mstan/UmJammerLammyRecomp) · [downloads](https://github.com/mstan/UmJammerLammyRecomp/releases) | Initial preview: adaptive widescreen, higher internal resolution and optional rhythm timing/latency assist. | +| *Battle Arena Toshinden* | [BattleArenaToshindenRecomp](https://github.com/mstan/BattleArenaToshindenRecomp) · [downloads](https://github.com/mstan/BattleArenaToshindenRecomp/releases) | Adaptive combat view, expanded-roster mods and two-player rollback netplay. | +| *Dragon Ball GT: Final Bout* | [DragonBallGTFinalBoutRecomp](https://github.com/mstan/DragonBallGTFinalBoutRecomp) · [downloads](https://github.com/mstan/DragonBallGTFinalBoutRecomp/releases) | Adaptive combat view, online/LAN versus and an optional donor-disc English voice mod. | +| *Wipeout 3: Special Edition* | [Wipeout-3-Special-Edition-Recomp](https://github.com/mstan/Wipeout-3-Special-Edition-Recomp) | Source development project; consult its current build and qualification notes. | +| *Mega Man Legends — community* | [MegaManLegendsRecomp](https://github.com/VuloB1/MegaManLegendsRecomp) · [downloads](https://github.com/VuloB1/MegaManLegendsRecomp/releases) | Independent port with 16:9, analog movement/right-stick camera and enhanced presentation. | +| *Xenogears — community* | [xenogears-recomp](https://github.com/OpokXeno/xenogears-recomp) | Independent development project; also contributed widescreen cull-site work upstream. | -Each game repo carries its own build/run instructions, keyboard/controller -mappings, and per-game settings. **This repository builds the framework and a -standalone BIOS runtime supporting OpenBIOS and a compatible retail BIOS** — -see [Release Package](#release-package) below. +

+ Dragon Ball GT Final Bout combat with additional stage scenery and edge-positioned HUD +
Dragon Ball GT: Final Bout — adaptive combat framing. +

-Bringing up a title of your own? Start with -[`docs/GAME_PROJECT_SETUP.md`](docs/GAME_PROJECT_SETUP.md) (submodules, -bundled-release CI template, release checklist), then -[`CONTRIBUTING.md`](CONTRIBUTING.md). Community projects are listed here -alongside the rest. +Each game supplies its own disc identity and controls. This repository also +builds a standalone BIOS runtime for OpenBIOS or a compatible retail BIOS; +that runtime is separate from the game downloads. + +Screenshot provenance and feature qualifications are recorded in +[the showcase sources](docs/SHOWCASE_SOURCES.md). ## What It Is @@ -108,6 +140,11 @@ and game code (`game.toml`, seeds, CMake) at the repo root. See ## Getting Started +For one recommended setup and a first game project, follow +[Your first PSXRecomp port](docs/GETTING_STARTED.md). It uses the bundled +toolchain and OpenBIOS, and keeps advanced choices for later. The sections +below are the broader reference. + ### Build requirements Every platform needs **Git**, **Python 3**, **CMake 3.20+**, **Ninja**, and a @@ -200,7 +237,7 @@ python3 tools/new_project_layout/update_disc_set.py --game-toml ~/src/MyGameReco ``` Then build with the generated `build.ps1` / `build.sh` in the new project. -Full flow, every flag, CI, and the release checklist: +Full flow, every flag, and the release checklist: [`docs/GAME_PROJECT_SETUP.md`](docs/GAME_PROJECT_SETUP.md). #### Project Folder Structure @@ -219,7 +256,7 @@ These are the 3 most important folders to be aware of: **Other Folders** the other folders produced are generated from template files by the setup script run above, which provide various features and tools specific to the title itself and might be referenced by the compiler, or by diagnostics/dev software. -### New here? +### New here? **The fastest way in:** @@ -229,14 +266,13 @@ These are the 3 most important folders to be aware of: | Architecture | [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) | | **AOT overlay sharding** (disc discovery, per-game recipes, validation) | [`docs/AOT_SHARDING.md`](docs/AOT_SHARDING.md) | | Build the framework | [`docs/BUILDING.md`](docs/BUILDING.md) | -| **Ship a game repo** (submodules + CI + release checklist) | [`docs/GAME_PROJECT_SETUP.md`](docs/GAME_PROJECT_SETUP.md) | +| **Ship a game repo** (submodules + release checklist) | [`docs/GAME_PROJECT_SETUP.md`](docs/GAME_PROJECT_SETUP.md) | | **Netplay** (rollback, SFU/ICE, dual-raster, disc gates) | [`docs/NETPLAY.md`](docs/NETPLAY.md) | -| Bundled-release CI template | [`docs/ci/templates/game-release.yml`](docs/ci/templates/game-release.yml) | | Local Generate & rebuild CLI | [`docs/LOCAL_CODEGEN_SDK.md`](docs/LOCAL_CODEGEN_SDK.md) | | Make mods by hand | [`psxmod.md`](psxmod.md) | | Mod schema and integration | [`docs/MOD_PACKAGES.md`](docs/MOD_PACKAGES.md) | | Widescreen / native-wide | [`docs/WIDESCREEN.md`](docs/WIDESCREEN.md) | -| Enhancement tier (PGXP, renderers, load accel) | [`docs/ENHANCEMENTS.md`](docs/ENHANCEMENTS.md) | +| Enhancement tier (PGXP and renderers) | [`docs/ENHANCEMENTS.md`](docs/ENHANCEMENTS.md) | | TCP debug command reference | [`docs/TCP_COMMANDS.md`](docs/TCP_COMMANDS.md) | | Contributing | [`CONTRIBUTING.md`](CONTRIBUTING.md) | @@ -471,19 +507,6 @@ new packages. It remains conversion scaffolding only. features, controller policy, and skip-FMV behavior; its [CMake file](https://github.com/mstan/TombaRecomp/blob/master/CMakeLists.txt) shows how to compile the plugin sources and preload the package catalog. -- [Mega Man X6's unmerged Tweaks package branch](https://github.com/mstan/MegaManX6Recomp/tree/integrate/mmx6-mod-packages-current) - is the large conversion example. The - [package generators](https://github.com/mstan/MegaManX6Recomp/tree/integrate/mmx6-mod-packages-current/tools) - preserve patcher parity while producing guarded stock-targeted operations and - content-addressed assets; the - [trusted resolvers](https://github.com/mstan/MegaManX6Recomp/tree/integrate/mmx6-mod-packages-current/src/mods) - demonstrate deterministic composition of shared records and injected code. - The earlier - [`feature/mmx6-tweaks`](https://github.com/mstan/MegaManX6Recomp/tree/feature/mmx6-tweaks) - branch records the patcher-parity and write-classification work that feeds - this conversion; its patched-disc/pre-bake experiments are history, not the - recommended package delivery model. Both branches are review material rather - than a released compatibility promise. ## Swap language on the fly @@ -624,8 +647,8 @@ a game is played; this branch is where that machinery is being built. ## Status -The LLE recompiled BIOS — either bundled OpenBIOS or the compatible retail -backend — boots and hands off to the game across supported projects. Games run +PSXRecomp is **beta software**. The recompiled BIOS — either bundled +OpenBIOS or the compatible retail backend — boots and hands off to the game across supported projects. Games run as majority-native code, with the capture-and-compile pipeline filling overlays as they're reached. The breadth-first push is essentially done; work now is depth and optimization. @@ -746,41 +769,14 @@ and deep design notes (`PLAN.md`, `FAITHFUL_TIMING_PLAN.md`, …). ## Load Times -The runtime models **authentic 1× CD-ROM timing by default** — the same read and -seek delays as real hardware. On top of that faithful baseline, load-time -acceleration is **opt-in**, per game, so the accurate path is never compromised: +The normal runtime models the disc's read, seek and streaming behavior. +Release packages exclude the CD Speed and Fast Loading/host-pacing packages; +their controls are not part of the player-facing release catalog. Developer +catalogs keep those experiments hidden and disabled by default. -- **Turbo** — a hold-to-fast-forward key that compresses loads on demand - (keyboard `[KeyMap] Turbo`, default Tab; controller `[hotkeys] - fast_forward_pad`, default Select+L1, rebindable under the launcher's - Controller → Host Shortcuts alongside Rewind and Save states). A - press-to-latch twin, **Turbo toggle** (`[KeyMap] TurboToggle`, default F9; - `[hotkeys] fast_forward_toggle_pad`, unbound by default), locks the same - speed until pressed again. Controller host shortcuts bound to a single - button or trigger are chords with Select — the launcher shows them as - `select + …`; only a two-button capture replaces the implicit Select. - Backspace while the launcher is listening clears a shortcut; in the files - that is `0` for a `[hotkeys]` pad value and `None` for a `[KeyMap]` key (a - present-but-empty or `None` line is an explicit unbind, a missing line - keeps the built-in default). -- **The "Fast Loading (host pacing)" and "CD Speed" mods** — automatic - acceleration during load waits, shipped with every title and **off by - default**, with `turbo_audio_sink` keeping the SPU timeline coherent through - the burst. Host pacing only changes how fast real time is fed to a load, so - the guest cannot desync; CD speed changes when the game receives CD - interrupts. The former `[runtime] turbo_loads` config key is deprecated and - ignored — see `docs/config_schema.md`. -- **`[runtime] idle_skip`** — proof-gated fast-forward through idle polling - loops, with guest time and device events still advancing exactly. -- **Warm CD routes (`[[runtime.warm_cd_routes]]`)** — narrowly-scoped fast - read cadence armed on a specific `SetLoc`, restoring authentic timing the - moment the read pattern diverges. - -FMV/XA and CDDA streaming, seek, and motor timing always stay authentic -regardless of the accelerators. Driving load times toward zero (via data -sharding) is an active depth-phase effort — see -[`docs/LOAD_TIME_ZERO.md`](docs/LOAD_TIME_ZERO.md) and -[`docs/disc-speed.md`](docs/disc-speed.md). +Title-specific resource caching and overlay compilation have separate +compatibility requirements. See [overlay sharding](docs/AOT_SHARDING.md) +for the native-code cache and each game's notes for its resource behavior. ## Help make your game faster — just by playing it diff --git a/docs/GAME_PROJECT_SETUP.md b/docs/GAME_PROJECT_SETUP.md index ee1a68a8..90cc5bc1 100644 --- a/docs/GAME_PROJECT_SETUP.md +++ b/docs/GAME_PROJECT_SETUP.md @@ -491,77 +491,6 @@ Composite actions (from the game repo after checkout): --- -## CI workflow template - -For an existing project, `tools/generate_ci` writes the filled workflow and -nothing else (no commit, no push, no migration plan): - -```sh -sh psxrecomp/tools/generate_ci.sh # in the project; --check, --force, --dry-run -``` - -```powershell -powershell -File psxrecomp\tools\generate_ci.ps1 -``` - -It is the single-purpose face of Project Studio's `emit_ci_workflow`: the -template comes from the project's own psxrecomp submodule, the zip prefix from -`scripts/package_release.sh` so CI uploads the zips the packager built, -and an installed workflow is compared by step name -- `--check` exits 1 when -it is missing or stale, and only `--force` overwrites one. - -The New Project Layout scaffold **copies and fills** this for you after -submodules land: - -- `.github/workflows/release.yml` ← `docs/ci/templates/game-release.yml` -(Linux x64, Windows x64, macOS arm64 + Intel) -- `scripts/package_release.sh` ← zip prefix / exe / display name - -`--exe-name` / `codegen_setup.exe_basename` should match CMake `OUTPUT_NAME` -(`MAKE_C_IDENTIFIER(WINDOW_TITLE)`, e.g. `TwistedMetal4_Recompiled`), not the -bare project folder name (`TwistedMetal4Recomp`). Scaffolding derives -`EXE_BASENAME` from `WINDOW_TITLE` for that reason. - -It is no longer fatal when they disagree. CMake is the single source of truth: -`runtime.cmake` writes the `OUTPUT_NAME` it chose to -`/psxrecomp_exe_name-.txt`, and both the build CLI and the -in-runtime self-compiler read that in preference to their own copy. A build -tree predating the marker still falls back to `exe_basename`. - -This matters because the two values are separate copies of the same window -title, and renaming a game updates one of them. Before the marker existed, that -drift produced `build succeeded but binary missing` on a build with no errors -at all — the executable was sitting in the build directory under the name CMake -had chosen. To pin the name explicitly instead of deriving it, pass `EXE_NAME` -to `psxrecomp_add_game_runtime()`. - -Zip prefix defaults to a short acronym from `--name` (e.g. `MastersOfTerasKasiRecomp` -→ `motk`); override with `--zip-prefix` / `-ZipPrefix`. - -Manual install (existing repos): - -```bash -mkdir -p .github/workflows scripts -cp psxrecomp/docs/ci/templates/game-release.yml .github/workflows/release.yml -# replace YOUR_ZIP_PREFIX / YOUR_GAME_TITLE / yourgame-release -# add scripts/package_release.sh (fill tools/new_project_layout/templates/package_release.sh.in) -``` - -Or re-run token fill against the submodule template: - -```bash -python3 psxrecomp/tools/new_project_layout/fill_tokens.py \ - psxrecomp/docs/ci/templates/game-release.yml \ - .github/workflows/release.yml \ - --ci-placeholders \ - --set ZIP_PREFIX=mygame \ - --set GAME_TITLE='My Game Recompiled' -``` - -Full action reference: `[ci/README.md](ci/README.md)`. - ---- - ## Bundled release checklist Use this before tagging a release. diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md new file mode 100644 index 00000000..3f451089 --- /dev/null +++ b/docs/GETTING_STARTED.md @@ -0,0 +1,104 @@ +# Your first PSXRecomp port + +This is one Windows x64 path from your own disc image to a generated game +project. It uses a terminal and an editor. No AI assistant or account is +required. Start with the stock game and OpenBIOS; add mods after you have a +repeatable working baseline. + +If you want to play an existing port, choose it from the [game list](../README.md#games) +and follow that game's download and disc-selection instructions instead. + +## 1. Install the tools + +Install [Git for Windows](https://git-scm.com/downloads/win) and +[Python 3](https://www.python.org/downloads/windows/), available on PATH. +Download the Windows x64 `cmake-clang-v1` toolchain ZIP from +[the toolchain releases](https://github.com/RetroPortingToolKit/RetroPorting-Toolchains/releases/latest) +and extract it to `D:\RecompDev\Toolchain`. Use the folder containing `bin`. +This supplies the compiler, CMake and Ninja for this guide. + +Open PowerShell and prepare the session: + +```powershell +New-Item -ItemType Directory -Force D:\RecompDev\Temp | Out-Null +$env:TEMP = 'D:\RecompDev\Temp' +$env:TMP = $env:TEMP +$env:PSXRECOMP_TOOLCHAIN_DIR = 'D:\RecompDev\Toolchain' +$env:Path = "D:\RecompDev\Toolchain\bin;$env:Path" +git --version +python --version +cmake --version +ninja --version +``` + +Replace `D:` with your development drive. Each version check should succeed +before you continue. Keep build output, caches and the extracted toolchain +on that drive. + +## 2. Prepare a disc and clone the framework + +Use your own supported CUE/BIN dump. Extract archives first and keep the +`.cue` with all of its referenced tracks. Leave the disc outside the source +repository. This example uses `D:\Discs\My Game\game.cue`. + +```powershell +Set-Location D:\RecompDev +git clone --recurse-submodules https://github.com/RetroPortingToolKit/psxrecomp.git +Set-Location psxrecomp +``` + +Check the [existing projects](../README.md#games) before starting another +port of the same game. Their per-disc configurations and validation may give +you a better starting point for a contribution. + +## 3. Generate one local game project + +Run the setup command with these fixed first-project choices: + +```powershell +powershell -File tools\new_project_layout\setup_project.ps1 ` + -Disc 'D:\Discs\My Game\game.cue' -Dir D:\RecompDev ` + -Name MyFirstPSXPort -GithubRepo my-first-psx-port -Players 1 -Yes ` + -EnableRecompUi -NoNetplay -NoFetchBoxart -NoGithub -Generate -EnableBuild +``` + +The output folder is `D:\RecompDev\my-first-psx-port`. The script probes +the disc, records its identity, creates the framework/UI submodules, generates +the game and open-source OpenBIOS code, then configures and builds. The +GitHub slug above controls the local folder; `-NoGithub` keeps the project +local. You do not need to supply a retail BIOS for this path. + +Wait for generation and compilation to succeed. Read errors at the step +where they occur; a created folder alone is not a completed build. Open the +executable at the path printed by setup, select the same disc if asked, and +try a cold boot with the default presentation. + +## 4. Make one short route reliable + +Your first goal is boot → title menu → first gameplay scene. Compare input, +graphics, sound and transitions with the original game in a trusted emulator. +Record what works and the smallest sequence that reproduces a failure. + +The new project owns `game.toml`, symbol/analysis inputs and game-specific +integration. The framework owns the shared hardware runtime. Keep original +disc data outside Git, and fix analysis/configuration or runtime behavior +instead of hand-editing generated functions to force a result. + +Games can stream executable overlays from disc. Reaching a title screen does +not establish that later overlays or a full campaign work. Read the +[execution model](EXECUTION_MODEL.md) and [overlay guide](AOT_SHARDING.md) +when the next failing scene needs more code coverage. + +## 5. Add enhancements after the baseline + +Keep one disc revision and the original game behavior working before adding +widescreen, multiplayer, translations or alternate content. Each feature +needs its own tested contract. Additional discs and content variants need +explicit identities and integration; selecting a different image does not +make it supported. + +Use the [project reference](GAME_PROJECT_SETUP.md) for regeneration and +packaging, and [manual mod authoring](../psxmod.md) for the mod system. Include +the required license notices when distributing a build. The +[full build reference](BUILDING.md) covers other host platforms and advanced +toolchain choices. diff --git a/docs/SHOWCASE_SOURCES.md b/docs/SHOWCASE_SOURCES.md new file mode 100644 index 00000000..d0496825 --- /dev/null +++ b/docs/SHOWCASE_SOURCES.md @@ -0,0 +1,21 @@ +# README showcase sources + +Checked against public project READMEs and release notes on **21 October +2026**. Framework beta status and per-game/mod maturity are separate. + +The X4 co-op and netplay images are copied from +[`docs/images/`](https://github.com/mstan/MegaManX4Recomp/tree/e14700432ffefcfb878a4449be9dfe4e394de844/docs/images) +in the game project (`coop-jungle.png` and `netplay.png`). See the +[current game README](https://github.com/mstan/MegaManX4Recomp) for the alpha +playtest, OpenGL requirement, camera choices and platform limits. + +The Final Bout image is the game's +[`combat-ultrawide.png`](https://github.com/mstan/DragonBallGTFinalBoutRecomp/blob/main/docs/screenshots/combat-ultrawide.png). +The existing Tomba, Ape Escape and X4-X6 gallery images remain from the earlier +framework README. Images are game captures, with no generated replacement +scenery or characters. + +The public catalog distinguishes source projects from released downloads. +The README does not advertise the removed CD Speed or Fast Loading packages +as release features. This documentation change does not certify a new game +build or change runtime behavior. diff --git a/docs/assets/games/final-bout-ultrawide.png b/docs/assets/games/final-bout-ultrawide.png new file mode 100644 index 0000000000000000000000000000000000000000..6f6b07812cd7a53ccddc3afb5ec63bdc2b6fae25 GIT binary patch literal 3629863 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

eq9xX-Jfvx;R7BUIS9|tEp9gNVfQ%gVTY91 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%0D~ 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%0DxqAQHiWzhFP=2e|y%^jS@2 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%0Dy99^^EQKhQzsM&R_B zv?KuvDfWvowILFOfV){IvhxB&e87Cv%?*BinKYH1Apk)p=~Gm{NmflLgm4nwDwt)hW{kE$7o1iQ9$c*7V%GukA3B);X1D*O@zGI9>;CbT@z#Dx>tKjqr<`AdJYw+`2+sm`U3W@g z*Rsw?aeR|XDKT;?$w_zAD@;M1fLM=xL&OUU^Hr!`QXcjd9{C_iFRJ#+cqM0V7YzXc z@W&f@ug#pLI?-r#`aB42<*?8rHmtZ+ULXhSq6;( zxw9Uq92Tap5=C7irJjuK##D;b>d>>A3G5~|i0np@Ne_aLRxmUt9bG9;ZIwoy>p=KO z@c~&JX&%Un2;4DNkeyzqwu>UbFZxRtX2YB{V(#^{e$HMaJxLkPq;Nh3q??R=B)$Ml zS|*irRNvL~W_i$I3^+@gCz_Sv|BZVd3^!f&U6a`moQ@@_gMa8?ayo!BhQd=rpq@^yIxwoE>8dsq%bzV?U3e-o$f1o-$i5So_a2rsab3A9&~-flIKed`R7g4g;N%Z=|h1w;Is(W#gIo~Ko@2;8maS{#|W-K zY+9$D5vdOnsy>8f>@k5?>9i%}r0)KH!hwiO>)@@Nvq;zZR}0vo^Tfvt{rcNp7(!r| zXbJ1A0u**mfE{38fAlaYOR(KOJ&fVkkiZVYI<~^@iHDO{E>EkiO)V|`Z%bk4_2yf^ zE+h4V$pyi{t_TXdqW-|{LT||kZ-rFYnZWMny$U;1Wast%veJ85b>(F~pHb~is44$n zx8JvE(*qlyd^q#XXGT7JBeZpKceh`Bnc`|?^8M{BJdi*mfE3V)F@Wr-jcxV*sAlH%gArp z)+;$)xApUIqUYcP3}qRdUZ=y2hAJEXj?_om3Er?|*SKN5Pt?vH#X;`SDxWU6!%;wv z18Yj0w*?>?fEw!a$+VRsXOT!-NG5mVt3+B&A67Oj>WhS`P+;Z~Kso{I-OphGGCGdC zYrTw*34`w{>EHo{Voa|>;#TT2A|8k}!lqD9c+h=>mYdd27GmHKz%q#kLb|)czYiBR zVx}OZY%N?O26$J%f>Qp)&f10uEQtC*QQ(U47obYkA=#GZqzaxz!^2c27T+ z+S=sF&c0yI9Jej=ME;yPzkxrjbcxOJJHz^8*iG}tuv6MPe43djF7lRNXy%`gO1kp$ z5hW$~8s{xn!a978^m;GHlG@slpmi{>*YJ78t@o_kv~%aHe`^}}0~!HWVQ&AjrzehO zmz$YgBdmO&ep2Zl>?cM4vdZilYHYE~1g8A?Daq#G2@8Y5?$Z;}ANKt(<-&*eN;g;} zg9Wy;E9@5EB;|GBbWqqWzuiF23B~NF&uLbD1gK)mxFlx@?-@66Kfz-Z1da64ZB>SQ zELugpLGAifjjge*9^HR9IhD>mb_|mwpiJL zdg&^w<7aKnMu!EN_jM2_ZDEt?v&=oUqXWAfGg&AijyZZ44OFw-T?>da5=eONbz<%s z@+AY{6V9YC+LJLL9RzVOjb~y~+o_8M#1t@xC`wh!HID)#1uk-N0J=>laysl$I*{-p zK|St#ch@HKa!Ge38&f4Mwkbr{d18zTq#w|!3RqEn# zH%6;5Y?o5}&4N7*oY#Z`h#LddRVDB?hahe|A5SD|;N)s(bSJkA;;=tLf$9#F=8iPu%1c2)4*;< zC;pn|$UjYBhu3>PdQiablru(P2X+b+f;+(MX5}+y;{W=_e?M?< z@{X+;n!xV9I}-0+|C5UHf9ntIDk@UL0=sFw+PVO+yU;r?46ySvyURiXyY4kL0CtZ& zd%LrP!k>ZB){nzIk_GI5`7Sk+Q4Cz)!mc?ov%~okajywT`oIav=05vIy2QfZ604%u zVc~<%OEA}di=WoPJr?i|22Qu??p{uZuW*U2S>lZ_o;}f~fgO|v~ z8f0vAQhgHR(MmR{7t`tSF?x?_EFMwVA~kCSQ~~RW`Uo))6>^6N*q!Qe0mC8o+}}?b zZ=IBO>}*&Z))&~Jnm=3U-~v0qj<7vEAAb7q{r}p%BN*5{{$TP0_og7l1Xvyb0%Wi7QhZPq7 z%nDtHf~ByVGh=vh@lTLwLK*aQ#l;sE&$_VqY82oM3$rQgRNe`1Utm`!MRu(|0o(M@ z#@#P%G|6mYM8FgoKgHx%Ff7nFp41M!ZwNej5v%T7n*Bu-? z0Xv{NxWvAF!0!^Pv~?w8sfP$GH+Nm zf&iw#(9I}xf`&Q?C1QCWL6+E7Nq@)#qTWx5FQ<5!#mm0@i;npAjnPIwIl@5OAtUc2 zy)8nKm@%WsN*5JuirPa2LxZdAY567qsrqa(DR~VoG0Ne zF`)f%SU_G>j4%?aQU^^XDh+xwbn+E`uRwLw%}GsMb5e4>i|qXhLc!u+(q|A2;f_kN zRG%x&7f-#np*}*!=GM}EQQ?J(XktSrZL^+h`2}OE*4Zp{O?92@0zSe{TC?zeDN#)UEHpp(Ax z#AQJviP(A6mxqw`W2Mv!>R!~Z)0T45baX$cC_q?A4m?<`0CQiPDO~YsEjiX>sD!Oh z$?Xpq-1i)l1(yP3);bZx?ps@OSZp0%A>V_kEV1ytJ$%~u>a=m>w9FxUKJc{x>cjJU zM*s1-39rAD{POdoVb4FA`RKMRpgQD)1-1jQ+p#qnzz#w@57Wn#l>l564({^e0e_KZBxVvCh;>=j5l0ms<7>MN9EEIx0)r~c)? zEenhf@^$$6tmEn7d?J0XYJSrr4x4s@5A4){fRp-9vA|IXK4lcJQ?w4BklF#*A?XBk zcH5rm^@N4IPSHAiuDnZfIuuslBWWGJF21FYybiHb*-2ohxAFNGn?9hSbW7yEr7uPFgHVmv>v@8dcRp(x@dmL24Tt>e52qr6EBx0rFI3CxgC(F6w}pXakG z67p-4kY9y*P>|b1CYD`CHq3vdGYqkTY89}k^1IPR|9VoIBs0%@Crfw1pjVViVHhfxn_AzG+f9i|r?C9(2SdByEmeIfL6z zWa@;mUwaD8Jhq76Ms8jU6Y4_>Qr7-()*;UoU=q`3Z@PVa2q^W z8BVP1yr?qX90S$Wa$Wz>5SWj`=~U%OK2L#<{AiL><3?Pt6b7+Ea)$3Ts>4GA`VZ#F z*mti?ZGi}e_NPNqStn>+OmGaTx0;nfzSs}Vc;FrqT8C$S>T{u0U&s9xDQMWYE$bo- z<)-E#L-U+g1DS29Pcr@M4;8S(D_{T4L|qaU*x@}N>NyZPd;V5nHO1_-ft?=|MgVp} zpy88a#*6!nz59(fzxbX5yPzFEb@JE8j{f%8*S~pW`z5!oz3}e!zqSLrhwl5ocicQ; z)5hTrfn90oZ$ksSSzvZQE}m%tyB|wnH!A?_B)L;bCrn_6bQ3TDJLwyHg;xPPZ!fUh zd>4R`oe%*YCH+9_qK*KvK*R{x0owtR+7Cf+8Nlw#vwnG<0(L-kDogCz`+9+$Me7i> z4)QvDg3|%mVL85LvL$)Zl<8^u_ko-Ht4UrWfTQIYkPWAj~OYurF})-E_K zV4Nr7@P40B(FL48(kxa?r@JVVVD=&;sm+;F?vpRQjwL7^go=kHV#3#e`mAX09 zuSJ4dI;TSuuaWMJSGsZMpP62Fn%B*lFlEL#Bqr8Nr8>cMxQ5R)+s2~<=!S-6R3xZ` zR*cUSA8q#9CoMFdfHF7>c*X`XZ~(V=gH&d3R5b*^Vw>isdCl>B&+Bl0lY8otB;Jul zD+DD}PcNFElb7&=4BAHwtPR*p(~v0EzD`m(Z*e?(>r)pe!DobOzI9581B?Q87TQkt zxR3R?PuYGwZZ5&XlRa)rVfXQAzsQcFu=8&hE61voo$YZW39K@!Z1wHa2V)A16L5+B$5+O6_xgd|^4k--H@TIq+2X$bep$X@oBP_$ZhT#Jk2|-@ z9r9rej)BxGl2soreuEiP--3OJ(lzA30@w~Wf{9K12MKjU?+?`2OUO?MP4Z4UwLOW7 z2&8_hQRGyHo>3vEnOdkGkEzeMSv862HDbd2L|{0QDYadM{>1ht<>fRM;pe7ks}jzW!Ow3?ux|9!0{(Mmyo}9ib>vHk zSXwYk8Dxc=I~`bvcw75U`t8Ko-kMgESQQR_Bi=eJZQoIytpq8*f@MbE ze_B{Y>t+^89*6Rj$-`&P_}R2+KP@U6KG%=xq^hnQ%x>0*S+hpW_M0OC!3W%7b{B_Zc(%v*3Y##~O{mXLj{D_x?;h#}c1Sd_c5BkQ?Ejbkk9m{) zg^4-MQocvMUFNqY7PNY(uh^)cR_02eh6SUeT6{KH+_c=hZLY_sf2XjS5Al7fKYLa z?5^p}Vp6>{^6YQsq+10~$eLmU>G={nXYZ4yvn9Os-<(8wCd9`FJ)W!W|tc zLWsxkw1rxbD{up<18!pgGOGzv1~YEn_9o->I)I*V@N{2Vhk$+LYh&ORizMb8ENjBA zp(0GyQweBqpw`u>y8*W&IuusoDk7^MJkbfC4)NypICx&WZO=`~d7C z(>nYLgdugqAWMhL{0lS;mM%XV%aX1o;{+-L8b!fCdqyG87lW!xChEKkKz6D2JI*50LaVb8hDUn zKLPe{h;SdgkhM9ju8FN~D)%X9_DpK@WL75FpR1jeO=nVapbx~1L-<9+k8`pKdmZ?I zV~Z1On30S7k8|WDV7NG^bWk{T_F@vkv66cDCd1hg)HqrdS0u_>9xi^+Ux#(8Q01arNR!M#5Iid#wXVpaPy>^ z^wXqE>ZAm$(Vg4oo(M%c(em2d$f-)Qj3yZujN!hQ+~9_9FT=-k0{9nD9Ti0>m&^qT z9Pd{gh#G@xW4*@Ma=s6#z85NBAKQafHPr0GxDr<;JWXo&mR=K|QJ0YIb2BWr!JW~< z^n^`oa;N&FYc|6&8~Ks)c&C!`md)wlyLReil0|Y=va^Zha@*|26EM#T+H`$C*`Ba{ z8g^)RL)(ef@|j|L7XW4qJa9C_HC>v5m3`Qq7KVoe`CarT^e=L_-P zVQB|W3?L#ocuKr=OxixJtwagz#ls&CT7fQU4vuspe(8DX6{~<9JYiJh~wyes8^co|-l^jPxz4@x3 zRfNxl0{zUKPey5Jr81l%BYT4y7V5haupJuaRVVgG5CgP??M%u$8IdLwN04yf%;;p3 zkxf_wA47Jh&O3(~Z5R#GA?q1zW&g|5u%4b_!}>4ovji|579pTsNbrS)(mj{%1+4JB zJOo6q(7~>+SgnJ70EigxP!P&+yyB1?6~@zn(X%GNF*K##$ZRG93gqg{ zXyi!8z@Q-cqTYa{4o@Rp0mn}lu&#`=gZ5d<*;Cu#fVOGs^Uxe3Kk{hXn&8%Nzmze` zc|N-TabPBcJTp!`)dW+Ncihn`wU)nKc8AeWMpUBt|a!Rb@DyPr>4I53+G>_ zX}cZZd5^u<1JST(NfPf7)ZQvqay@q55uE0j5(6+ArS!TpajXoH`>uIwJNRCP55Gl@Fd4Z=Lkpx91!+34>yKcgNbVfiLJ& zJM6Oii$j;+bnWolZuHwNYyRcNYc9Cs=01V!;1>&-UH6LNP}eET%L|s-O_?%W*;KPo zRP0Vel9FGPlw49$GNP>HqRNsuP&?4PGRf}zR);`# z)lzKdt23n1u0`yA44^6UXvfOE6~h~OLlJ&OCv~P%%-SOB^4DhOi6cD%Wz{R_Tw~om zKu(wC7i-0`gI_El9>jKUd}sjK+4I2y*(tkzTf+KBJOFo~c5^#uFXMRo#v88vsyjGk zIJxSF#e?b&2~imjWA-Lk!;Fp?3_?y#0^CM*9x`Ltc4|w~w03gdCIl|ydHq5L2NuWZ zKO!qKk{BbZ9yXUk_ew(aIc(8>^ar$z@x;^`oD&LLk*EqRj^W~{f$!7+7U2x)k{0f{ z-7&R;ke05c1-Mki&G|F8sws&ZJy|Q7@HR*7o^E}(u3|ROCq&Ld0go=8Q&;-nriTqV zM42oXVLP@%LI^mpk{DTV<$3XpKy$s^1e{LsJ_@HpAI2|8%xvcPl|nAVafySj4xDZf zQ5~dpjI5h!K4Wy5F=`$eW!wu$ryJ$N94YKnIwEOn!ba*Q_dvI%$*Gkk-j%A zXq{#s@HoQwQ_%h>kmZ9QlAhSZY{KBk77oa$=hM3M`k|q9(^h)I-Swj`FIdX?Q$Yf=SesLeAGek_5<*C8yV%rnx< zU6UFTgE|@YEA|3`%NXMc^aep2b(fU`Yi3!uS0*C8NgJ_oakx%DvSg zYk=Md2DotctayE&c===Z-u=c`C;c4ci&Ms*j~Q>Bl;%M3L<3mgoa+U4pB?9M3jJ1o zw?YSd7jgt*0H^jmevt+2;0}A+jsL>}cK6+J0g_K3?F4Mi>Rvo&aXPD@fZd##!>^u& zKz74J0K2P00lSJzycMBOjz!)mQ!rm$|Ob} z0(A?^UmPEke_42!Ul2oR{n%Gp)ewKV;lCOEJ2F;+&~TcaQMpxvA4iE#v&yHFp+O7k zyx)b97y}UK2MrOvENE>}zao4bgWO@P(27CsqV^|T1ztZ>OI={HlbgvTbUG5K0%+L& z4jj<(#5#ebyW=%(IlF|LY8%>Wvdx{U$O&}1P?MuaT_x&?5cwT#$Ah}@ z?pwdwzG=jkJEhLJ@maJucAq&;x&kJfhoSmC)TQ+@+PYfP2ez)p^n`^=Y%!LM z1gbMLPZ&O*-xjt0y0i6n+cy7(I5-^q6uV6TFAhm)J2{_Ozzz-3VKNm3u=~8n_yTq= z>2QxY+9TfmOg#0v*tu8ie6deH*dSVW=Mw@}Xa=WZGdo9;5aLV9=!)kh>PsNt2#*1UQvJ?%jqFXn+(T1uH0z6VX<KL33``(nu^k79p#unL|!w7?& z(HYQa7OETMRJy%Ww~8DJmN6!gn`CN$D)r2i!a7oYdZp<;3|a>nrj5Q)%V`Vwv@WGS zfePuY`lG!`Y+9GOAfbSDKgQKKzm?Bu7%9uF>%Y!+gC5X-_=*w0TT0kxC&v$3KL9(7 zVL_dT`d3X#rSZpirNRytgoK=Y9V%e=_ix1~=fp=^$tN^nA9PsyPzi*E*^P7Ogaz!j z-t|)?nXs6h0(PKvJGYOx;o6@9(iIl|7?7^0=qFKtUBK_PKd{3RulMJEU^g!eu$%7( zcGcbhuv=g(YW{WSqTh8b`L9?Em;Qh|Ujq(JOk8xy$CnH2)P`u&^0-vk83)c9`%W4k zo^nCf_xxYQ?pMU)FNs|*`}4ux_<%ppgq_(TdfmbsdS8TjxkJ_MFL#k^22qL?j=r&% zBl4Gyv{~K1z}jH?_jIs7ouCT-&eVB8-1m4Wsk(;X}cp5^WePS+p9MOZSmOH4$}>5jqnTJ2>7c49>=r!114 zFCy-;eKZQq-Phuba5>a-TurWtm#&M%eeYAhAu$=5KLgl~Q$YSt2B+g9iSaek*)|+B zjIbQa!ST?zQTMdk9N`x7Bp+X&G`89up+B+xX)JcFhEHmA9xvd33NKXZ%_g|MrYfx( zuFYIwpgMifyppL7^G_;B^YmC-hL4G-q#dX|AQ`}sWH0j#01!39L}G)l(Wc~zKS0hF%{P|jRTZkWlX9w zCi|r2Lf;*2ReRsEfSq%k;jek-oB?g!9}l|zaZa2FuqPBgIw>I7fdK(HU{Zay3)PPT zh$chy|8zRA@XK$_Egq8ow(mG{x$U!Z8#WsHfMpIwCerA=_-vd~*a6r*yyYhmfE`d> z41nGI3;n>Zq!-xLRM`}E<$hpS31C-aGP@e1aiM!@$N%nF^k4t@2g8pzCf=!730Zn( zn00HaL%>sH8*G-Tw?G4gXDCeb?YAq26neU zE;cAZdkct+?z@-sX2#%i&Dirjfj+MZ8 z&l^@Nh3&4so*YI{PLHJ_rku6rkOj5x8RcYhm+6g%7b;^;La>Q_4B43hS*VbaV~pho zUSOaAO>|+Ya_Pl1KpeoJ=`m|Ya*4j!*mch~8wCE

>JAX}`U1oi-wwQRQ4hgfv90M+lN&l&n1fe0L@A5aMOYA2m zu>0(sfx-hX8uvew@Z@Wr2c8qR?vTJv3G43KE$-eU%RqIomYYLUi7E9l$bZTJCH5w` zp`_Pn{pS(+f=T+dpe1oy2girezDMkAszFZ+5S{6hyQ~T~bYYrV(a^E0t`%3WC6ha3 z`vnn$WG`sfqJG7`!wZ$lm7p<%3+iL0S!AMelMgxLJ@K6jX z&j*=$Ai~wps*J}0f(7NCjy13Y-Ng9vhM3Az0ezLbD`aJ>Y0xTfspMRsUAiyEn(x(m z&4C($+pGs-Y5|Hu-9Z2V0$c#;u?@o|1^<<%fuPh>I5i zT7~-wk~)Osi!_)aj{+oh7-0ZS;vkP(kT|N$6|OS_u{@(uH@kx7t1L(jAM_}37Fh#DpjxuDb_7yV9+T?nYPb=yBq&g2WHZb=B>PR-ND>guEHD}Rxs5!)>_}iR0 z&gQ)O0oa8*+_91k#w71VxK2`+>j1mlW(~D$;39_UYB9TfT&@Gl`xjyk?7-kGVE5)1 zGB6gFobC~y_lPge@b*CnucxO^NVq`+cHk+rlouvmq@t67s{ie`Ndp4xuKt#E9RzpP_AIdhU{~u0c9#O!E%YTcHo5`qZeGjwiUoLz30eg>>w|^|>nr*{P;(&S znz6r8m>q!K!5-tI)5fP~jSo&4V0JrRF}6Hw-2a?w>$8b=AJ}{Lh}#|$ckJ>5yOPeZ z#01Q48nh8Z?d4k3?g1?8f4Lj{CAbs=AOa(5jtP*2PJ~khckur%a9dV|ZX6qczs&i% zejx~~R@fv{?oxC0&Rrv}Tp4x-_F}96TC$e#_8qD&c%e>hB}*n%8Vzgf171+_F9udp zFsk2hIqa~SR7iUg4yz5qa&SW&BbYkNhFP!iSxq9lQB0&FUZq`x5Ro{Vd1m&~LC%_r zl&Lv%ENB<>Wt+(ytU>!O77*kPd!e5|>SQEGJt5_e7HCo0@nUM-wvv<6!UA zz?R`J3;_rOCE%R|FV$QrAGIOhMYq@(7w{+~U&!X0RB9e-}y&^3E znboIt;opHBJrrs?(n_H#yAKebvL~SiY)z}>W4^rB1p6(9RL*PVXLHMK(tR++br{d< zROl13{-joBI$is4VI?6~PF)DESpEzQOx^FLYwe`A1orD0rV4vh({0f_CoV?PTVrOY zw5c6o&%4*&vnU3cUID%BGwr?8;*gcPF)D?{T!Gw4NkrA^u-8N_t1SZ>BR0G2wEp~c2?2} zi0r5eJD+ca-Kwi)RtlARLczPz;$IYBeM#}m5yi7FDlU#Ig`^HTyUIA9FV0sThh!8r z)mPN`fnA)hRyxKmYKdRb`J3*}-@W}eT7IG zGgO)rRKI9h;^cO>=KpO7A^}-?{sP@xu0?I4sGYAJQCbtku)<38b7jD{5pp^HbD;SE znN}K)_2>)k;7rPJsh-|pdJWSxVhH7iEwb;?)P~cM02k?`7_Z@okVf}ibqLx*SHtEs zBeS8;SOZK4TcuT zK^;Mz{-m~uv=%*1hq(t-r?D3pfkEYTU?`2AWO@M3WJKT^L{0}6T^K_*hm@~;PN&$4 zH$i7o#h@*OqMDowM+zRZSTJM-Mn2^r5)HX4doMJC*2&Y2yWI>MO|YKq z9Tt%4fp;}SCRrqeb-0y~)e7`fkm{-ByaIMQ*>&whl#V&coecJd=ap-%PSQhbaO}XY zxJ!7u{WfQL^p}K5&3s`3z-4UDR8C>1_*^aq^aa(IYl%F{TFp;M?yCJ+ zV)2s)`dWI#UOXZmy{KQ%EkF=B&ze(j;!|?wKbQbny=D~VdpjKD_u+4 z#PZJn)!8YYcnrrqXitaaD~2~Fb0%mSfA_wgF_Y3B|5oh(MtpfveDJwMbx0Ar_bmaJ zSf#MLX-_<8-ObM!H$E+C9jt17@72S);)N^#+f7^Qi3z!5xK9R=yJ^c4GwNJva5K`o zXCWCkJR3A&MB!pFql=T{F~~n`D$74KsPiChremYVpSb25is>q=J2Jp_$c;cHVyMqc zgo;*jbM!p6j2@~cQx*vr`Dqp$iOwa14m9VhzB4HJokC%H435-!mP@SarQ^>GV47CJ z$Yucjonn0KPes|VbxqTG70)+wL8xGPyictFa{(@(fE{}r##W0l)nXLaCM=MLQf5xn zH!iKqZAwh5N3cA))tUj|a7U!r_(~UCN)d7nNQ-3Ske?Kc4-|=Nwegbi5p7%r(?u6_ zZ+t_N?5c)kG{$GOx`xown-WG_75-|EzYcg|86P?v{MPlSul;uX zjKA>r6RwX>0ff0uo^$>AsOzuCUGJW9eQ`!E>1%z!jS|@%=`l_P4CyTKRoM$~8&Ch) zc;jQ^PoG$=ur_|r18p$~eVlW-bP1iY#*==#q}vkGu2o3^TE8!D2V{NdT#easTC!Wq*Xhg(fj{_z5u$ zxDZjxB^jTI1=tgBLS^DuBwgiMwE*0J$Dtf#h9NAQIVC18Ol0?8HO#4v9`&0A?4kk1 zyoJ)?5ys}2`QT)hZ z%aCCf{iJr%sz16semtCfslBz;PiggJ>Pdv0zh{Q4nc9#*&Bh&6e{4-smfo8%yuGM% z?0l<_cykl=yeiNfH?6_nFKm?EbKn>}KZToG|2)fMa>XvfU%?vp%GkC=F4UMDKFpAy zmj}jItRyC#8K9L+YV&)i!a)_~NL$fJErMv=A;kjoSNE#?EVS>7j-Tl*zkN(%HKeEb;H2@_V^SG+=(GjeA{7AXEUDcIGo2+uZXv1VOK%x_|7^VX zsqw)9dvjp2n0PzJ#BCEkwlV2U^h5K^6VfXd!n!?=O2^nBh24&=KShcOrLa?|&i)w| z*fss4vGHQ5u&ccYa=FmJ?kXtkOkf9pSUa$*G!=G0b?}EZfn8gRyK|`@*uDKW_DJ80 zIr^}Czd>7U_wL=##jBr+*FP11{zSaKPyGE0vG1^W;1#iHueAGL6t7 zJ4gNGoVg5IO+W|8-ZSv$J@L(DAnHT+htBAyr+}Mh5tvRi@({T~er=MO2~l*8I!yusudQEfwRgtU78i}DX;icb+j~THSFH`VRhlPAGi5ajfs2BP5T677q z)ifPf^?f#V0EB9bG!B}a4k|q4#L({vdkqs>#DpgC`$|5i8wJ!|E%G24jYiW3uEL}i z4T-G@__0_&m$u+sPH(P4&)0ikgTw4=qH0GTBr^M&^q?)QVl`tsJEQh z)`z#+gt|oM*YS`wC)DW$@Moi-gR|N61<=Y7V${XxJ^Z6#LTjxmX97pu-h8u`D!efge8M?AOtr_b#A z8E74t-Hxq@WH;i`Z9ju!EW+D?&;i(4*e+z2SW9FlAzi@geIshk7tq^pP~sy1yI}n_u(;yKS#YTDSETKd`f|C*P++PJM#Dt8A>E z7^f~yn!ZF=lL&YugWQ1!(Ql*nCNaqYTCgy&sLd1dpQJ&UMZk8za1rjd;eJxmQECsN zBV&#{1b2X~bQFN83*%>>^BR`pRLIviCkqP}iNba<4Ns@sY%@sA0!yvp%Mp}cf^t_M zrZkq>E%690#x@+wX~TMYh7Cgq!lg``vP42EP&>BQrfSG=z14&d{hNc@X<{JMaPw`1pNze~7TUvnCyH6_WD9SS+3hiEd$I9yRd!${VcB=bp> z7!!(#R%)hx#pz6OxAj+MUj(X+2@7P*I<}3ea*wLwdq~5zdR#rcb^wU%Le z;9nU{OQ|;aR%N6hc$ztLhU~MAb^l4%M<-n;&&skx``{DffhUXycN?#~Z+!KQ@#1@ad-;9o-ui8i z@&0GV+aDVrd~SSo+|Ti_FtYFIfn&xipBXQFY`k&MfX42uS^vG`#@~(^@0~C{J1y57 zY7H@wxWSDw`-X7s9nV+k*Hm1Ftw`&wL?aV3LCX+PFU9Y>#h~QMGA^@=ahfy`GTziSj_1>4|O_V zkIuK}UirZ;05r*q6MzM1?ZgpJ`RD%xc}dSt=1Ig4AgmB;ZKA(f`IY zI7@I_oE8WL5!I91UIBA8w@Hkvq1o6f>3%9-@Tt_aUSuwiVjilE2}CW*UBC=ogK4Y@ zG@wb-IS0`ekKF2P8XMRhizqrQjP`mk^26}rLEu3)x3=^}Vq&|Ps=*(pL%`cEupV9q zerVM@0^oGjTDftc7tX7WffEn;G!C5h$WaVMDpJVy?v!n^Ia=84kshK9l7$SPXh_{0 z98xjt5S*#>3C=%}krJSUs}0g>2v1)p4#DB;jIQD!;~F#$ajTfntmUYUK0q`L zPNy}D`p0g*iRzrsnSrQ@tg7J;w4I{9$g^60qt;92ics~ z-W0F)bPK$^fRj7jL9?qAsBT?YyZtF~s7HKYrgAlbodi;!d?VFzpPwSYse^}& z%{z=u+l{;KH~#oH-a2f& za?p753%L>n?EZdAI@iM9`&KRtgVmsu)5j=SLrnB`G3iWL1oOsA7X#Qm{g@+QXZMSp zJNG~Aw646o7uZ2#hx`+e(4j&_0NBlwQSbW5>?(>c_LczHU2MthD!o_ufnB)-cJ-A; ztIud#n9#XItXTFN0J|5S`R)0Nw{*f7lG7}o4}$<;hr}>{`b7K{ezE(+D}NWSeI!+P zFrYecx{XhXyPuZ@*d5P`S)CzdK*)iKOqc_~;K=KQ6sA>7Wh99Vyk1#1{qv^)&%Z3Z zKZK|?fm zqH^nBXSBIptl|8b$&8*%s*{5N>%W7Mmkgu6^FYN0#5PSM0|_`Cw0K#IwcgvQ-Tr>o zN5p?*Dd4`$!zCZ;;GD|X`0g-fEt)8IYAtCbI30vD^hj=R$^t$XAodzrtI3JU6(dI)6dyB5xwd-nU^5`)hV>W;|a|7s&S^zR*YuwO~y1AhGu>y9na=47lN*w;0-w{uEtQ_EI zb)bh1yZs%iHnG*sa4YHG1bHI{bg%6?yD1%^b#~KjDPD7InO#G6%w8ENw*NGnw>A>6 zJKQ6X)^)#B34uFE1ZFw5hQVPWFd5j{_PxgDM~qDm7=L_2YTp#6JAT&h+q!eF@!40h zA%GpcTHpHE_~W}F)??qI0(M|_?;MdURKV`zGsfE{umiI@dN%SJ^qC#T1QI#OuOe^A z$*(c#ObBfE_qTra!k$ZB*dujz%1n{nk5p0$i_nD(Tc;#-_4OCFw#NB=#c^7(|C7Q#P(6UE~cF#v+F*?`3U(T7kI1MFUAMG@lQl zV1j6FfI3NAZGCL=<@UedIbEW1eh_CMvM)ezH_A(X$x`|?+7OwHHM7>AE+XoSTeu1n zs{Foy=LxiB3<8?uKznGW&4@bq!>CzU5iHJ94l@dXeW^n#sY_cdc==kbR=GnOWzPQw zxFAvby=uJ1=lT*SHF;Eq7T#yJh9kA*ft?DS8u#6MLbFHaVx4EEV?{<*$2Jz!48(z+ zGj?okqVxu>@X)M0QKpJ@+`axaCuq3-Nt)Sc=0SxPmuo2y6*qe(HVL-nHhD5zv{J$* z*C*-(SHu;6Txo))^Xb@fw?hGQxM~yTX`Qz6lxJl_?b8axnVN;zRzJ0gFHKjn@T_WP zR962Z9e^Ffi@e&ES|?{I&TCIi~b-)EXr#3eKvN+l6sC9r{G+j_t^THLe zQR+qQ@L*Pe-?6^)^28=LJD;$MTRPS1??;F?o9JO~n4 zw$@W_TIrf7d427b(JJKYN;qbB?Ze`YPX(AA?39F4l06*___Q7hgrRdntnPi-fC1R; ze$(^mQO_%%d0zU&^TJ1-Yqm*y;%(2<|L`2|@faJ{+T_0)T=r~l!4dY`nNZ@X^W>$>?5uJzB#g0iAzjdZ3Q&Y-FCe@ti1pPxd;5NrY6;><6>B_Cu{Wl#! z&#+Tj^)c9~i7js4d$H9@9RVQo_HjqnS#CaI{5OEXhlTUu9cKS0Vc^P0{hH3@GHjq@ZwN|=%&UNI z?2~6|mt2E=okLJ=NRn=t8k{b(G9kAvQRhGp`Jqg15lGd5&ilkuZ}|;c8&gmgf8wO^`g_LSw~T##(y{hZGdsUKbJjR~ zOd9-QEqm>K_v`z`vmc5l-xuB6r9J+Rcxs=-f}fod_zCv6uf(y_As7~tUYs=T!=p)m z{yZKEJ3FvDii{LJ#>Z!keW#5R=R$YM-o4nB!R!V$Ae{p)6Jof~DhSw_nPU;pPFf5U zc1@z*<;?)BT+ zCym_2hVr|QoHYj*^V~&7(Gp|siiF}WHxg58VmtYA9`QmNbma{B6?rvbnokr6=$!5| zvYK@tknlL6ul>(okOa-0uHddx?omdA?jGf~#_P-5r$8jsDWag*@+^ES!qtj;s|Od~xoyMba0((3T`Q ztilfRNGE-YTnF;KERB~#ofk`cTT2|rE@=5|YL5Us0VmWXve`2lrj;XKZ~`tIY{P-h zF%yFzH^@*KWI5!ITOjW|~D?B_KH9xVxR*tk}G_F}TWfP-3 z)<>2%_t73#iVVc8ZPl{e)%Xvu{Pu*bCPUvC2e*Hdn4|~6O7w>pYnZYu1{%g$-C}B6uyju4 z#JFmyUtXs~cItCxN1w`8j*;@UqQbPYHDcE4UIP%c>R5&HHL`N~8c)eJo@rg4q7}h5 zx7$;8T_0=Q;CbRtetY+zUt4tIWWrzGm-Ow)=Z$Yqhkp52P~hkZ>EEhM(YkvdFko-K zWBlc#gg5qkUfJ*2zSnd6HJl3liI^N?t7_d`ZRS{c`6g#)a|DV10@Q&ih;(z@QymQ`p=l9O@uGhOb zt!qZ3nesZxyY{X?2v8tr8=M0mKsje56p(}@gpf!E9Klh>7~_lsCfX($6HGKg=sp;wJ>8`HsuKM)%-s|DO4a@fq?z>Cx5({7lg`JivmNPpa zF0tG(Hc?3o%cI(H$JpfL+wfCv?mw8kjz_hFCoDqRO}aHM=C*{mKHzl7N5N=aT%W|a zKAh#HrQO9t+QBO}JN4eUxVu>}I{>>hTV5V5D|xu8D#RyslG!qbo3yi{XF}?8|D{wB zYy^-yT(*v@hL>AAOjqot<_=R^=TE1un0`2F`h2%(?E%y0drWKhGy7t{Y4JCv#owBi zt!MViMpOJ^)93=zv!$v%Q)(JlXd034!A8w6O)Pe-`BX91wD{m1{{Qqu0h?=&hU0>O z4NXWe%_C|X*JBZVSy^+24$ET^hNlF>+18ut&7bAri0E%r3Zz!- z@dOd^EW?9=){|KUaxgrw1zWPmtD`drzA3^RC&8mg4?L}k^MxbgOp3SDLvoDAKIC22x7Vi$%HC44-0s zMOmqMCstUshU%#TP~EgTI=zl!T07oTDp;hIc&al*-P0wGys`R~p|<9Ogz_G$7O(r!NjLok4o6Bx3)WB<&fP5n>@qU` z3NQy?HyJVPSY`<|lASMLH|_RRZ@@0u71*W3A+{ZhWS5soOG+L9usd?dN9qZ*jM@)- zK>^IGn>v8+&7u)h|8#*y;K0uDM{RAaeEmA@=uj7)y-0WLquch-T?gsLpXu7I^xGZu zi=XJyHS~=y>2k!dTT54bOP`-fV=F1-a@?MufjWz*CqNkh2WCnZNTR)fMpdc?2oDz=Bv5n5MLGC1YP*GE%Suw7b5X=YtS}ph-=Wn(k!ExDm*IxmfeqRYp^dm0cW$tP^Ai*G1L5w*bY!1=)eE*S;UwOh= z&^JG?BmHVlz;3XjqU!p5dl%pJyuYOLBvU^*xrxefV-(azMP|?uGlcF46H1wmSEMtD z*BzE+i3W5F(1VqU>9Q=wP#ro+SbUH_NY2|j(9jJK$19j~271)G^#`Pw4f^nQ;^2Ns z6t2fTe92^phP03}Zx`K2jM(@Ixk?1P6NslkLRItuU>!kP7nK=g_^kuHp#`B*be<@z zo)vjwejxt`X!lwT+be!5T{R{+C3SLA`&VIh8UA3tFE zi$%5xS?19xB&w6y4Nf3qolzafc2DQH0=tV%r12v8`IxeN75RC;FM6T}cA$H2tyF>C z-XrAKqYAS-L_Yq8y!S2HfE2OKo9vu)Ekw&MIjoS`~bA!A>sPEgGt8 zQn2044D2{Dk(1G)rTL%RH}~26?QJ}a9Taw-uKfQN&;38k=6CH93jrMrz%D82FVm;p z&eKhJP}m{$M9$x#r;CTeZu;M$r-O{nMPbJmrX{kUV4Vs(WT+qz*}*Y3H{Hg8-KOy$F<*U8fMf-_S?r7hd)?S_7hbM@Hv(K`t?lg$)wQ%Z0lRH`>8=BG=K=cdHU{jz z-$fhN(N{jDuYIlpyW(YZQXaFIB09B%dZTsEmQp>f6ZC}DPrp5uF+zZe4m&~a;FE#T zqZqs!m1Y^8Au1$9vR(KFy5G+OS~515g18~3li(3WPiKi`1N!UkTN%;HFwqu#G(TCv zrs~*K!21ckSeAq_t&j0fFaL?BYBkL7Fsb2N00%U7q~p_>(<|)BLcjRkEj;Q!oE#yk z6S3al`l`)CdZvJMrHJM(U^o{z5&aWs{{-VUFoiPoZqz2srsIo+dV)wVQtRg0hGqsC zAy$Y_mlZq+C&HLz+rp-qS%L_|7Gq+`XNAm2ls$c04NNz)%oqaJKUF9xfdSWD4^_Cr z->|F!6Ond=gCv;ksUV)7ma1U{>GPcMv0gj5o zCeqEcx`q3IZ zS8)G)aaJ%~Whd&KYCSsA>YZwz(K*#3kp+Hwrob{jjYQ$;pm<9~#FDWoWL&DNy@}MW ztI4tk((o18bAs$vj4A9sX14Z6a_K4nhpT&?w&chu6StM0nqaFwXP^6zv4saunD+f< zTC>*l)=Ja&KbsCWn=ZDQI_#_fJ9gdV(WmuPlWETdW`{33{`9ilblz?{YY*a(uFXy_ zf!Ve96h_hh7fL8`lOVPGVf}63bO>g*{;NJ!CAU@<-&&H#EGh2R=buN)*FKXbvBa;_ zrri$cHhJ=2Q093gfa=_{#9|#-W+uxF%dI4@4~zjjeGI##yVH;d7N1zCC7n=E9cWz! zV%ViBs7?WPSxJ@|+5ak?`5+W_D_1_^U%UD0cv=sU zmR9V)x-%!v+P3bq?K*7Rw9oe4F59}@wy(C?KHOw`q%OZloz z$YS9>9?!KsS=+M*R>2cVHZy9n5?}JMVJFJwqpvh{;6Isc=k2 z@KZ&US>Okvufm{G;O>MjRUo*-;Rpu0iHIR%a%^LBZ2@yGPcM@MHv-L_;W@j$qQTw3 z8Nu6V;TO`{m`9{wz;#+*1?ghFWo+njd00GC-UXIY(m4g?m=qgKBwI1R${6E@>ekI} zLB7tO!CSuzDV(F}kh4`F|0AeBbia6qVDNgAaR(WZN8!lqIHDdYX9qKVoK7qjKsjD) zo(3J0;QOeAAW>DC$l>!w!>c*L6K6ouXzYCXs3mwRkwVmvE3~8Oa90HHGmH!4n934b zOK&nvzm~@sSEX?|8?|=QGHBvbHn0g?|Lb_MS ztxWI=RjNZasGTN}!k9d(_(NxOj@5B=KCbI7f0B`rL%i4VEr^G#=I6z6R}nbxaO)O1 z%W~_EDlkE37gx^=9h2rt&LsdJXqiA2Tk_XGxUWxd19o6} zF2Iht#1=kKS^02n?PEXx{4f97tq0X@j_xO;3!$sX_i_zr$AGzU)!+(JfE^=%d(V<> zr^$v!mTuy!gXEJvuu?tw%R5qMO# zgi(-avk11Tcy)eWAiFb~Rt}tQjADkz8`{fYM8!4Mo?~(;g3XQ0Ht6)K2X=-pI)1s~ ziUYhe)|kMKJVdq2=+Vg~*5$W9zjoaMxL-^+o_#VhhWXXwj%=)JN~tv}kHV3;>m%*f zb%2>fI1wC31rd*Ul(AIdI9X_fE+@3USHQGcFRRdXV9#CMtsTMIwq~kJ38)J9AzzI(pDolye~P!t=8m`(90u zCM(x?uL5>sa74Zj+0S2lL6_6I3i`ju6{OXb;=J6efZgzX6Zehf%nn&xLFint-80or zz%F3{OfTCMfP*+DK;&i7Y%+G1Wku8^6=_$KDMLOMOR1F(a=_6~Vx6$5t2 zAOU8l2X^3h3R{5?%tM;gc7*}}1YB;Vr!J|?ZtVv8)q09g7#!}l@1ciJ(1R!F;YND$ zN*5f&Be3H>u$oIO3fJru03sk@(QPY(eMnHr`3JB(!@iCV^2JBDuU!Ld*Jpn9UrO_D zh0YF^m8J%ygTfBkCti5rcBt*1dFC%DyD03kvu}smE-UMAnyOA;@Tjm$c2n5J$J`M& z3I4E5SQi&ZQsW7vcH9cHEG7B0u#nE2@nA*8V;_Bl)DxbM#cerwv4@V=ZD+H)I~971 zuUxKfhwrVGn3ronjetuPuv>rC>^U9$L!;%JBLv2Q9X?n5Ko)#MUfo3A{gI?BQh}X5 zIqb+8>?Wh#x{(w1`4Y>EB{nU>CNCh)<^%(=!^DsZ=7|^%Va{lnBk+pD;w;1Cg@;v0 zC;@Luu_I#Du@PZ%H%dt?t39}l6+rHgZ+B$opX9Fdv~q`80iW41=`7DI3_da1H5E{S zXXzD5)H@=JMyE0Br>N(+lp&x`2=4k%wZM?D!q}x3J=Khh4yK=y1XLcCL5F2AyUBoE z^b86cE#UP%aW;k5=g^r(5f!92-09V@8Vf=^$njvgZ4O1Oy@0yD%gTM@ zZT&$!{hZ5rTn!4Ko)u*P6hexXEk?@3PUXN{b}srFy&vJE;6#Ka3PpTJN`XOX7MJrI z(ARzmw(w*j6$zdSh!_>neNeyh*ieV>KMugH0;o?j8}+KL~VEjso!kRmZLK~ zUk)Cx5nl_1IW;;n?q-6*Qr?&&vxD^a2V>>2X*O&}{Oq9Hh|iA3=TpSHaUpAlzk=C8 zF2|W23L3L}p~eZ-@fGlyxh_^z$wBOpBTt;_`J>Sa+WJ zu`+c}yPb4+LN=$G$nFcw4qbNq?`bgGnUVXi{H*)Z;_ppMyEnU&*)ejLZ7IrosH7yMzW!mJ1Htc8 zAO(ry9J%)1Kk|}2r|I^Sbn_9q>1goh`%F;SL2CEP2D0RP(y*1x|4gNIqjh+0WWGal z=l=2l!8bXrL#iAkd^C199-bb|(w;hxD~x;<7$RzewCE%j>O}n6gwPxkX0jRgzM z|6hUm24|auQgCJlg(cej|3~*V2c}w-eK0Ce95@>2pM zD33#b$ml%!#=hu@*~QOi@;a`w1H1#mLoB?f;21konAs(GW_IOuq<$%xzls3My8*+UYGJevcB+*DyEPl=r<-U~i^@mZI#gf>oY%^j z9l2~Lz;Gwq$nh4^+^%h(oN4s{>@IeqI?n9=1a|H~@Ys=ix*OOf#PvaPSPj?}&ahQi z{~I<(vHJSZ>gv#{D%C1y|8GV%O-pqKc4?Nv+y_buL%{4#ow_d&ScZ=0=p;tVe*!xv zUU}j&{q+ppbCPa9LD%k~-y973Vn2ELJM!L74A>PcXKYR{ri0GTV}@%zxqGf82+U4U zTL)k<7+xT{p@qRRa!MGUigs3vm_mlg7}!7nE@4iJ$&@h71W6eoBaX_LbOBb-Il%|@ zg9n@8=e1Vx@q%8#>@WtHSiwRSM`aqHyho;+qB6|dq4i9GJO2uhyD_OYBqcL=8PLL&|8cfT#{|#9;6`U%DSpS~~su{ilj$2F9%$ zqc5>^vXE>8xM^2eparh`HZ1yfy(>Z(CywmI-J``v#hqJ-NFYP9Bp^Fz{G|+0z;IkH zgdY4lAYKNtn_>=?f$VTc5^oA{wlNtJlEh*o;yZ&afY6F}8w~p;3heX)Fuj8;g4#L6 zc5@tJyZ8k(X%RDr(oXS_ji1jV+QFVI3JlymQ9vdXs>W1$UZ~xyT1M?)3tk~_zOTlw zTlG14|6}s*M@(i1PuYts`2;u55$%u%;&i8w?nD#)@c^TCu(Qn+-gq3&Tx(Ooosuzv zQ9DTGPIQnXt>nygZTsX>2Wh-c4mUZ;?5=vEc1;}~5Mm%8$cbPGSCpaybF@QegZsPW z%B6chUHMlRnO$zy-(q5JgVGK%yXT&}4Hl@(Zq_VPQ)8>Fw7`lAY;|@2n>+VWM4|(u zLjh?d@QE^(%$=)JyV%k0uDdD*l$|6x$Ob?drJ7^^=)vZx&b%IP=z z2Q4>VqDK)f@;u#nlx{yxHyont_S5wT>DT*$-`ZpXvV-Yaod0FG&N&6qsPujp02~NKqjrw+YJ+oc#P-NkUX=52oP{%!}O|i=w-MN zonQu6F)$<0R{^*inq`z8=q7iAW$5kj1fMv|I1JH0vZa)tD6@_%unfsD8y@Wg-wn&M zKzJuUyfMB7;5!KK;N)#I`VP7o^Hvw6&6orFcOl<`+@vsZ5vVK_H3FjJ*+ORA=T*DL zAg4pPfWR#x#)LQn%(85}VGVo=wj}qGfb%u$50HYj!Wx|eS(rOU7biWTU}jf1o7O8OcS+1 zNbn#$Vw1Lo1e4J9(wW&6lnnwqK^wz-Va*cuN95|$>gFIMfIwDlEgg4^+4#*wmuEO4 zElA52Yq)@Ui%6C;t?tOIU>T>$G%7b3BU(Wln=e1d>{FFalLw$W&HvS70kn<_?DU^0 zFWa6eCEUR59-l$R=lf-#by`KJ^u{NX@rfR+Y(AO)DzimzkQJ-QTOTod`y=wfXUsP4 zAcs$rOV>1P1T5u&aa^_2i*|ahjmhi)>^APDt2fY3HZkoTBF^zD2%-f+9#%sEDK4+r zO(#3;d^_|{s$I34E-Ho;%XA8^vpeDT(D|k2}kB8}Vp%kKoc6J}8jUm?RNi$fI%uER~Kwv;B5^9R}$nzfAH!^-Y{CZ8V&ckXyFq95ghZ96!Ta`Y&zU( zrJYR#@lQ01IItotSl=l^B95?WWPo6fN92SM3lRLHkuYm z|8@&uB81vj$zzCITx^9m*m5Gqh-bLr*RL>cW_=acf_t;~(2}SW z2|@#$XbD0C+@O#L$Z*zl=Nl|ujfi(Ymo1DdvFJzndIwpLzzz$O7rQdM)Frl<8ZxO$ zwHIdxpnL(ad#SREibb!JrE8eI`WadMBU!VR*~(4i1I4x+WjZ(r+&Tqco`D_D-O3X} z0N(97O~3kuuH8)!ol^tZX{ZZpa?Kr*Ng(hYHx24xwX5w^%QJ!Cc!-k6)5G#DpgO96 z`r-4s5)iL*$Fw~(P>>Q3PZqEE?vri9SSf2 zyQ->3K;^2dA5-Anqe_|S>^QJ1D+|@+bsDf+yf}Q;%zuH|F<_Tw$x5}%%zLoB^wHU~ z|GQ@o(2{}kB~k@E)RkuixzuZsh8?a#O{U5@eOC3^5Y-F}2_J;Xi% z@9>oyfE_Hgj*craZexn+@RF{!I{?AXC^)-#pP&2r6HuMF zL=Ja&DY08er5cwM=w~i?8|~-e`&bP@|MkO5!(}PaXb0yBXT<}n$)|&%m+u|X?et@S zC(G!>;sAQShjRg?8?GIr*bWRG8Da;g33b1|@m4qj8lR%3k*GBD@HC-->Z6mb$PMGM zSIhKc6`a-FT-CwX0O??cyBE*VI#hri49H}zk^Pc|Iw-$I_D`kQnzU1Zhy)Ne3rM~P zOeb(Q^am4oTPJKf2^UQ5h`gzo+zOl^coR!GJNn01pk?$BKv?5PA$%$!!(m>`&U47X zzJl=y`avf_xh-@m2c-uO5)40rWDLL)!h3<*?Aj$06(IN3r&Mwh#5cL2LF zMM3((IxlNM?YuF&#DxxKm;E};eUm0EFvZr9DRUg?4J58hC$;Cx2@HVmg$h={`p=a) zz#qzd3jlVnea`IlFUSWQ$;Ur2V247(24){_b^yC3##X%Yj!UgfK*yQg?$eCc!G1mM z0CpPY;Oqc8J3X-D`a4typ3~q0JZ)jXd5S2HEyqR>3J*HI^K2o&ggD^97F+>`wgZ6_ zWbKS`V0YzGFb8%uWlUjLSNRW3VfW&Tj#RNw*iD_PD(pN0yMlr{7c6*a$&!$zOaIM* z9hlvsMIq(oAr%#mR8@h{{YQa!|5nN>u&b*Joi!`8q(t?H<-l(7;z0m*5aLyqKb)U! z$w{#Q)y>R)u(afnvRVIGwaWj5u~JU7wh`n$adb1oWdJ+P<6G%PxH`1I=-C3mI_pUM zqI0?QupA0I#IM`k$bj9h=_$*&UFRC-n z(%@|RL1eT_VeU_GDNBIuCO}~-;AhI}!c$d%t_)76QDWqd$L|`L zYCL3Lbeb8S?^^#cPyn_=f?0k4{}b2+z%sahE0lD`dJ+_yTo9{G=K?KgZk2+GAJiM7 z;vfm26X;eMCQPVXC=^9BOjnd9mSY;v_q z+`!;8cUN8jlG9Xp?gfqRJu}O(8mD+AZ<6I}ReN_GLv|$g8k(}HoJ^e38 z5({95Pn6Tr{+^bG{IROdne)Jc1^3t2Q{;55uVttPzix)$G#~fW1miHe(Iv~7x^FmSJHmm4gd0D18RgccA>swlUUri-jr=vqk zqZ!+Xk*3xWI*-VYr9ti`)?v5kG=NTfds4|c!2;MFVc1Rsc0V3w^18A&8L;E_EOK%e8;#tBA<>eq0bel+PbGf8H@(Q8Xg}UG1-?loS%=~p)%s_A~rO_ zI~YC|tzthhe=k0D3XciGsMXKI_qJNZWI0)6r7$~b-V&W-288=ZBr%sb6;kM%$VMiX z(D9jcya>62h?TpR8>KE_HDL)c)4$7e6iL}4ifnXod8gOisWkL>;op%b}G6QpL{id1w#hCg{A;zpf zxP}s_4(_W4E!8|A)5<)Fj9Pe_6&I&Sf5WMP51q&fip)mdRI{k%nDIz3Z0wu@RApO@ z8w5BofGR^45le>TuvxTWi^_)UtCe?7W1pZctN{h~VNfFJH<@YKM9kax!`O1XgGI_y zi8e4ZI4XyV8X|rQ25se(2Og{?kQ|fYYe!$v9laS?GNHouLe&V0Xe!kK5@fJB4p7CssgyzqYV7u~5pVET+7A_rBQx{rU`EY%0=+8g@%Wu2|r(@O+O2}#>(wUJP}l_Jp*&@-y4rLsDKwABH3R4vHauRVkh2Sd z0}+cQk%JH*B{9 z{-a#lKiL9Q=f57uWzBvuq(3IxB5}=x9as6u64AjSdSG~l&}oX!vtX)kU_LLg3ibgH zp@92e*^Gp@q{cq-_(5$cTIo zx0J_ccH!0vZ&uFfynO=Rb;<0o5h&?k&(0zqWBQc5PnT~{?VT;OVJkBf_`%m4)Vhh^5rgvKo5cN*6$~4cQgBZCtLpg0kZuh*?)$dX*QqhpkM4~ z=-$M7aFpvkyp5LrT7DKrK+X0qx5<>;_^|!;8 z#kKb=n4{|Ks%IGlc7=uJnwt9n>FVkrvZHh7((-Z!?23yYo-u z1KF`|>KL5a8Sf9BhwKQkcj6j3bb;(WPqv>T8xE1Jjm&nPBfBp!`~EP=dDX*x2{(|~ zx}fC6Hl7a$p*Eocj-TH}79Dr5|L3YfB_xdDqtgfy(+kSa@dk8MiiBL}$bcCku6YM9 z!~1(-mE0gHF*M*d1UZ5>{_N?($YKi5R>6nz%~Bmoz~MzgGi4UqNW}3A!XUp>vU=^Y znCGSnIN=F&xn2S6AoYSl3a1Un11<2Jq!Pt+0bkFGBP5POPRugJrBO<}^MDV;kIn== z-N0;{0S4iyGMS~45aM)jTm`E0V{g5!gOddq8fZ_`ua8~_ROfB`{w&997C}7ZGB-2u z734+8=MIjy@E^qc2cyqpaJq=89%g<19}5lv=1+rrTL&j1GB4Qwe{^3HTD*Y?5;z^d zMvxK0aC^E}L<F=LL%Qn0$mC$W4DH29?Q0GacbvRQ}f?K#77uEX_J3s(mhwx`|F=jE}!Gk?fF?LPK@+^NikClzowG)!DvqlOHp9NF$OVCxbZPT|zH-OaA-TrgPB|kJlvRsX zpGG2Z&zfd7ZiqVuTw++Pjz@2%H6llXyxhR0!K(n(32GamT#G~|;z`o1@3de<+m&Vg z3#JEsR89f;ObUHX`4+ zdyeMUGbGa-nJ(%v3ZK)6M8doh1T85D?~247pQE#bq_~{|*dZyHM8lIs^ses7TgRjC zs>niUNnV;`omR^%X0G+gPM6zfD%FMjC*PlO^R3BCtyAimO#Z-$?R0&uv`#2v&ah9Gn4c;&zgTI0vC3gZE6l66nm^fY{$dxi zFL#?i*xuuHz+pORGVEyhV>lYiC zeZ9r{{Wk0O+g)wlj^K3%*w!>py=|w>YxkJH-lP5(Uw*;P{vDrJbZa)Ynh!Lak9EMW z7S+A(jK@9nvQQTFleIIBX3^Tm|H3D$?veuRfa>PVVYE&GcKCcSFOSZc;Q)5?<~^VR zJNUyYF0o8o2UJ&E3t-3c$pX)z3{%Kmzxnf>fL;C4L5r3QI(zaVH$>GXzv~XE9gr4U zzRSN3bOm5C4;M>-3;Dho?-*?z=9AY7p=csWd+#6 zubIm{ZcuQR*Nl76YoE{0QGp$3ou1GkTMk%W%FC+hL7qz)>f@(o+eQ^y#KSBMLcsgZ zFdc>qk;0C6+>dWSTClNTM^3dSk#KQlhb_d*2!4`Bhi6KNVlg5+SRV*QusnPQi!vcN z_VZjF&O_qSmfW`>Fj9>O5LbDacT|L+ce+68CA^e?s8}#&e5!@zCvm$U-LNJ6R6;2xayC&HQQ;)K zHtZb4AXEo9ol)hHhf5>DzB8onbrDuOu6=!&uqO3M8K z7~1{Z8CVjPDM5zD+)jmkeAxLyjKa#x9}H&QZ6Fg0{j7 zwe-0f`utpG@P~c2+zby{h-I`Sv+$EGUcqE>uur!;)NuIv?mCu<;_YwA$#$~;9Lqa_ zEV1njX8=oi)%*N*oNn04?E4?-H=F2pThy<7q5kvRJ*I0qII`mebDM>2IKcmfVUbol zxvHeR;8`!O*a_@-D>>9mPIT~87`}GK$9}r~6exx9xV{u66x!C~#(gbBL!o7O#f|Ik z)&skwq`N&T>}Jfkv$*(PuCpsBxGOi8sp|9!yRxz%h1S{d3369iNdfHg^8xH0E-rqs zsEF3qK7hOv3l^|Qb{yCt6GcS@19m);U3K*%3a~>YyZ?4FJ5_VHXkqyL`F-n`3|#ug zkkcpo88~DGu62jh&fz_3Xs4(py=Tn7Ubd}2 zK{uafN>$jli*(Iy`srT!<2f2r&u$d{=!2%#Inla=1rFIAcbnBd0rK=WG^E_owm+^U zI0A1Ig+<SuP35ez;(b7{X=FHsg}@Ko8kFa zza-|ghY(xc*A9OIyKZdU?mKPXwhU+00U6eSd#ZPw*LO1MFMwtc{s5?Mv_LS0F{yO4 zSSUd5E)5B!w?^x7x#BM=a4IEE zUn9{Povcl~09t4GQQ@&-M7rcL1e(rp%WD-e1Q8s{ufv(c8=!R(P7@iF-ium>pX+V`c4BcJJa!&g{lTS&k1q|z>@-G+XUAu) zV0PSIsG;f0Xx?&WDCp<3F6BvcDDpFUWOm?p+H#<}XQ~~vPQ!LuVRDUGb8XX@UG6dh z$5qbk;I+yOc9y0VcG*tO+exFH98rK>V{Zg@vSeQ# zp^vYHXdJW*Z^>Wx+{5!uEUdX}{u}~ehvcx+rroZ`b{<_~)6@H8X5PgU#iCqP^v{wK zQd!A>U1=#n8NiNPK>8b)DAwg%xyLd3cLFGVTX?l>gX^WMGkz;9c+m&F}y!MPLsd8ir|cgn`x^DV29%Z z6gIMNxTYGL$>~;dqKSFMZfUgqaE5NZKzCfGKVPM557AAh=qG#WvsDh7reQU2g~{`n zm<}R4&^p|~(wBN*cF^3p06W~=UYe`k0}(d>8y}V@Bjq;;OOMhJ{bL0_TS8c*bFw*5 z%E%d^!~|w`{J0`==~!eV74{v*7Di_V56?9XgXzLVh?g;DmNNF%pGO9Q5m!naD7$O{ zIT?Doul9{GiFQ%QCIrYGrepAul;|j_owz@NopFw%HDYfl@I0-3MF4Z>os{!F=;;QE z1q&FF94sQK!|e}xI-z{biOla+|kmA0AwglnlpnTET93*v+*TGQG7NRhDzAs=my&h?2 z9pR`rvrCxAqRw$(hm;Xtb>@ip=m*B^VEA?2UZ!;G1a=US7yaoM z3POiZO<}ir2i|c`yywL5>H**OSl+b&y?W$k{8X z4(*cHoeAV8$DRo2s{AD~UF}%FJEI47%}r{eSPj_4#{NT3>ri$FcG=l?=H}kZqt^l3 zp{xgXpmq8AF2GI=Z#Q?Y2JC1>#l!BvuBz%kH8oDaZf?zgYvC;mW;Z_!{O<6+{@1Sk zN1)lc Ddplt+#IOB3Q@5kcTu3l)TXRm8};;|6043Go6vu$b&JClErjnfu~AW+-?Ac36%(TkHb$CYJjq&3S`)#llrqlh=XeX*V&K!jxK8 zop1b!GOL7FUtlZUAig#>oaU2Faa{=Vs!13^Luw~HJOHyBk}ZeXVcv=iUC`V~4@ZlK zxD*E$r<0BWFc6!|AVlyhk1yY&-IYm~GOc0pLB@M2^b^qBO$&CJSOxGkp2Dk7uHfz# z?e7sj9}a_Zx9-$4{Gbd7E(YtH=)t#3+t02GkTs&Ao$-_LuUsb?C$>wv>f#wi^{om$ z9rCmqKRUNo3X3HJkXc;tiWRFKokb8}NIy3Ss5~Uc1Z`2j1mn`y{z=w>sY2=`>_3h| zs2QP55u5IYPa|P5LTL~NO$!Pk89h*>i_L&D%y%77JvR4E*DSeAq%a=8x-Nt2)ypt3uo00|WJh!b6%!e$x$k(B*z!i4$tM8gu$rN@jv<_br6Z>!_o%WfRcNeG} zARYWr7`ucy>*WZUE74VjwJsS!; z6d=CSM0UtT0Vi2SVK?W|`gx%>HUGh{b#?z+yg2l=<)Qm`hn_q+=*kr^GAU=F_iv;N zB_>X#k^$LqLbWs7Wuc9wg<%z4WUx&g^yqbZw2l6Hh1r%fbmtX^ODsHOzdg!?ava#{ z-Rhywg+smmL9GAECB4%Z(X7`Qw##_M3C}@Whj$V%cafbwJ^zvHNycnF49+etPf`P4@$i-L23J2r|D%`TDho4kE;_69 zk0d#QmC_>xyo(YM1`W*x>obKV8jl0kH{J%;r=N@j zTE;#M&K7G%5D76)GKeD)bx`C^6*k2*G(iY+;XRX-IeVb(?_vF1f;V0+(`}KjW72GL zuMTt=54&wtRxeAfrQLZjP?WHHvfNl@yih>4fRQ;?L0*UIKz8~8zu_`6d5X6%d}^i8 zt>_#%bW6(R5B;Ln4+RTjWq7@~+&7RUR_?8UZSCfycIdz^>0u#s1D!*P*VsBW%j+~< zidW5t6$MW1IJnEL>jK}ZkM1ijif_Jp7cTzi9nm?~?>kZntQoHIq>niW9t+;1aVINStv^167 zB`4p5a(w(9pmqRpFi^Yn^gB=}DEQ~hnfGvB$3F`Sf(i>Q`S~XN3J_Obj#zd8cNUO3 z0J=_I2Wode2wr{tgSEBbbx0wr!a5Yn%TfMsRn=o)bTu`P*42g1tzqo0w)Qco>*}ii zw|GJ5iWPk~Z5*_9Q~0i3VFKQIetNgP6&ALTcb^zZfzQQzt2N z*k0h=DLCNib=AS{#N2R!?|>;nCkxGO4fHF06~xM8na_k>u??S{9`lRkc|JqqWysfX zwaXy&>WRUoguR#@ccM5Ji*O*kGyPB*`neABzDo3e4No8gAb*i^n=`r*h|++%49{AJ z<}ywvqN~ICLKYqHKEwNfLlAY7!dZZXNZ=s~T^;f=>F+OrmZM@VNaX7>FAeA`&oF7Y z8Rb?)ObMXFfcD?_%F?IkBHiA-tz>DEv>Q?Ha$^Op6BgJ-rklp(+Qezy6GimtN(sOY zX~EpjW1vf9op)JUG#1OnC~?**g4Rhvki0YBG{6qe&~A9sj~=2mZ1)wOnCTGPy;P}s zsX|~ESFhSsPt0ywS+K_J5@vM)?DSGR&^=9f7gweNJ21OvXQ<4M%j_Vk1EG8SGxE_o zvif@hLI=s6H(+FPtIa^H5Xv_{Wqk# z^bVN+z;5PDCau%k%$dPJb=)|xLpZytDw86%(*rwJo;QyJJLILfudIwQI{-T*qCf#T zW0g;!y3lGxY{!8e@>0yJedN7&L$_||yLCe-m|atoft;~7?96t@A9*IcP0p;%K(7F; z(*wK4HhQ#$?rULS4ycZs2JH5>Fn9;A)|4f#Fb=6}^i0hMtpmJ+6Yb=622g3qg7W-HhK6?!_cJRy2W$IE@=*&)lZ zRK@>QB3+IN>O0+nD55=U15zyTVHGhUNu82BHagb=Fk#H1a@fv?LpE!)B?ly!8Kgt< zRl}A*1|~A?uwfg39Y`ebVKr<+l4(>5;&m85m64eSPl2}+hxd?%565H0!AK~|Ro*f_ zqcbccGL1L5`yL|Pt+%EyuskZ)5}9s}PAB3PA;6#=Nej{xeZ5%AM`W2tzJfhtXX(*y8MMI%amG6 zY_%n>+!9l4nOw+hYKbMe+LAukFH5blBvx9Isw}Y;K~pNo)JoN!D<%_i$@4{I@dxCM zPsobTNyE3~qmAU#AIWDwkyY!-iZ94(YuM+?@5yi1$mMp%ETFJE-x0L-SIfb3NYm=s zPF}XG{K3+&#nSMDWz9~du*I@^J1d`TwOp`U8ts;Sm(1IakzI`h!L`8b4qbF8?2epw zDD1RAcAV?s^DJWMw~?(!$)S_v;uUi7BDr==9Ht<`nCD~ZiRA&OoaMj{`9)zUZ{BIH zD(M5ODC(1v;!9zdk#Q%SUqR*EfgRV@abQyVnF6WDQV$3Oqg6n0gQ)z1k9u!AV?y|)4ELN{y({b6I@rlvvN6FyG7dvxCx!0cpo zJO?ZWt%Jx;BYkJvY2$VJ+jV-NneJ;+4PRkkd5!IK`9@msCe43?PG4Z1G}q#8xI0a% zWw)j&^E~|OvVPjq;F1Pf@HT^hi~}O0b*J(U61*2WW94at&SPO*867OcO_t9-L`L0g z@{(ZbKiveU96@8#XK|avV)+Q}NjUS7Rg#y=7#Fe8QTK5fNcm*#s(ci<}lh%@}J#rU9Y2t4Q36gb+qm zji?A8WXu>v1Q$GJpiny~Jy5E5K6`-~Eh8C#)-uxlOb1JHJk<1nQalhH$D1qaE8zfb zQqqKGN=R@Ixt4Z-zncO=Ad1`pdS_j^PU8Gr<3`sV2m8C}Nd6oMfZbCiOhY%mP{8L2E#KH0*+99PVJzl(gng{J6}cI2!B z*dd~whU$0$fDit;TMv;P2gtFrXg%N=y%EQvS+Q6hCKW(i0=uu5O6>|4(t#B z4R*{9g`=e;@7A({+raFSllmZX9UNoz5$&``9wu;a_|$(bDj*})~Y zveJxo%F2QiogLg`p}Diw)KFMO1uZTHtP%GH(S8FOLhu3Tg0C{t@MzS zO5uEm8ZHmPCtv@DR)0Wezt4ivB`&n+o$+}YZu^=97zKR-8<&;w6IMuH7F6_}4cLz7 z)ZxCd5Z}GzGmnLK$I}YKW=fFSF%gUuH#N+HB~CNNHYJ$yP#t_`1dC^+=tfkhhk#I= z35d?RV_)-xoGbA*3d)J&m9k?u^G1+cMI`4kP#x>l0?){>X~6@gn1${RF=P<@ zX9e5?G0zvt?Sa`HGLedC8}*vq2-a1K|8?TO+i8eIsx3@eM36ITa(B0`zNsT*eroA9Fb4B-|NI;GP=NP z*u)_PDzF=0O0`h$;B-g;%d@|tpiNw#n{DO54xd1E0Cq1HF@86Fwh*wx)H=S-1F!>G zLxxx_RV==qY9;VC1yh6l@Eh5CmT@#Kc`S!4=dP;w<6;v7aJ;$(>^|Jw1+ddd&H3vp zxkE5JcVKtAl^nT5Hti)p{6cmgBEReOyPTb%}&2QK+9IrnQzky1=sm_OrT}6CnnB)F z5fh9qhn+_fV-X8arBSJLa3&p^XVeg866$gIy~Y`|aD=N&wL~S7XrmU1XHOy`cZ&K3 zx;QZf_BGQKnQAiJ2<{alqcbb?WZmdA^k-|Mh!OJs)U z+arD&m(Of`zUBFH%cN>%Q|4Hv*IDA`GmDvPdA@@E^g@}cu+y*|FHD&gAhTO!iF3B3 zC6*QIEX%)R_Ra?8wF>)aEAwN0Zxj36dWw0lp0_hs)}Kxw@SNqBGv>W#7_bAp)3cRx zmr1LVnBvkk^6Lq*b{lE49BN&OK1rVIjA& zGH^P;I}`XFPeV~v6;x4SnlS@zv&bt;XUqVzyPqrUlmg)01Nf=DoTZS3<__h`%7>Z4 zZgI$lPa(2v+uS(m>s;TEzRwgYweb% z4$IXx%jsszfvcANSJ~(3Hp}r=%aLZw**43uW=q}YmeeJd7y; zl^~S$hBYC<(=2_Ng*RKzM$;b>LWDa>IfU+h{GEj)jqoebv;TZE{bLp5I z+mMtX<8OQ7NkoEJN{u`dBx44SU4E5Rfp&1yTS3N!sJHqKBUpwo$Ky*0;|r1Hz-_mU zFTv|Rxo^+#9!Bt;p1wMebgWtE<jIbs{jgu{Xt^&QHAC%-1 zpvyNfYuM^Ae0}dn`u!26&0(q>yPE0s$hBakgK(!&=P#2>ipZ|Lom{v~w(ny(XOY5U z)pxA$;kRVX2D0Nfguo*wnwY_@6+dAi?3bgYt%F%}J2`rV9J-*|>FX-31GB?&thw?V z^1;{Siyz3Qon*&evh%3r_%%nkF28+O-K!vsorikwag?z#ppT5fAyO_#l(`9RDx41p#XiCB1b4%=`ef2Ai9P|sy7FfX^b`ZubVpuP z9Da`^Oz?jMB5TcoSvDky5KPg@noD4qHy~4SGZpa3W1tBtL3 z$}{ki>zOP6ekAJZJ0K+(DN{wJyC8jn{*qxSEJ&Qlbvs-P)q(LvC7L7Qk>_K!*40l# zGDVpbvO#%_6C~0y2J0pYHHAwvMkb54j7$HN@ABql7V2^!{(hB_;neAQpB-%QJbD~Z zBKQJ8o7m8bRgVvnlJZ=xQ?pA~3H0l@63Y#Pab5n=*jY4E*olV4b`iU?Il4X4%*mK( z=r?jR0XqcB@pwTAEbOLWivjH?rl33&-PjTee&?+&^uhpkuu)#J4UR}r!_vXg^@W)# zo||0ikj{a}c|>(FWo#XoIIsh!1DgY(`{-wu;Pt~D4BK&p_uV01z)tTKd%aBsc7R+h ztz_?Evg%tmV0VN?pyNDl%dceP9&+Fuv-3^lR15nF)+Ml8zJ|Q@DY@KCT3gAr7R$L7 zda>0Vzy-SGCI)tBa2H^=W%J#OYj0mL=dXnY|ImOP6m~Aa4loW%I)FI%7&9m96uboKQQtH7@QfrWEJ%1fEfuA=0@s#zhWCHEon zoeJzeK^VJ7nlFa_^uvF@`84#i)qT&Lfkw;)WBsAN2J9B?LiE}|*gI_K6%eH!zUTmU z2d^=4>%k^+sG01&M3#R?;ydG7!kbldXmv2V1*Xhbg7oywW4Y)(ue2_A1@nZRP~r6s z>Fv(xVL1;xFZ?j=@f@0ZKn9(i_^5K=Bm(xQ->i6bKBM zc~Oz)G(1)Q=*6iu1V%v;w=*!uh7*l5Zn)d1y+t6o1j29uXaH+?W=j4*Dqy?mRxw}) z%^~2LtZNAb5gWvA`Lp*oejPAG?4GQHmA)OE2<)CfvjDNdXLDGtanVzB7K;}kv=6B? zJX7f5H#m<1)ftx5q2lm#OLP(((G9Arpr?yY6{S%SSpd~Zyl07SN{Se#vq>Y9MBB%O zxv)f;5}%O>4wzu@?0fDu*qT7VK@W$9w|Nqug%EE~m!Ykb@%Tr#?>)3`c)sb0GHaI* z?S`$Oy3YundeAz7`2htI)olH}+FGA50@RLJZZH)c1roWQ{5V#?KUbs%&aznfMOSPVNj+fH3X zVbc~nbaqhKX~H_tx;49*f!uLm_vL<;$G}-(hj8^OJ-O6E!0DP>+1C!ZtX?2ZEv($s zOirC6U;n^_bzETwWgH4n)xpt~1G^2o$+tUHNO!h{oc03j5I&ECyo2Xeg`EcMIMZor zqq{HA(@lXAI+yi-SHSMzk$)<{?$#N3f75^+M0W1L4oJ>#V0SNQ9XCZ`hXfR~sK}y- z?>MkC<>duoAq;A}dGi!zSI5cS12r|sJHdcmS;>PHB`UC6`F6;6pM-q%;R9cN$Q)zW zG(5JZ;gL0~{=077V@mRkKNO^MIav4wy9T6lp>jUHw%F-KMHPFdJ(wZ#D`fWt^2<4L zu7e!G1=vguTw_b}mO6l)2I<@jsY}c=-m-c`b-owoz2y|QZbY%r%tam10r$uVWEM6} zGT{=L`3{hQ?EwC;9r=PgY%|F+K!C+~9*W~UVw5g%R+2_Q?)t})ep4l%*|7MaKM1+Q znd+Zt0jV_HnIYDLWA%V^p}9Gnb`6ueTa4^FCu;(1NQCKhT^Go&Hh{6%$vl9jO#sd6^{ksbMTup9>D|jW#X*6IU@xzql7{Z21 z;yZ&k-ZI8pxG#CT7H<4<+$W_RRQGPgi(E^vqau#Akh_!@>wh61tz&vR00kew4lcAD*dcTs_p9Z=?tHVFOcsB)yO9O9b2!p= zroZ4s?`Hl)I5QWp==j>!L9ch(!6rs6xxslEIY3`sc-!)&eP-nUEg|97sZ;+3S{D;@ z8~2S(OS=<#I=yk79Taw&&Q2f04hwU0`vBDy7vEb@fWlpXba{C!foy(0LwE%R(CA@_ z4GOzXjUEGbNG1yz9i(=1=OSG!g$j@1JY=PS=WH>4`q%PBbj@SAe+_9X2?& zgW^GXchpXg+v%^@>APEL?s6LM<|D7apNN#FEc7zem+cp6_)$ABm4r@~|Hw1;3;pudsm;)~wL5i>_EK#OFH9XlWm)6OJi8y5gk>8*<)4EKVnsdzL zrF0UF6Cok2oYoUeXU-q+vR+6^8FSzTsn~#k4~d}t)LOg83zCJXGH9mff#b~s?D|2P zDtma^+khR;QB*n^na!Bp=v*>5Q}?u0@Z88`2e1RV1G5{BJS5JyYqCI;*-TxGxxYP)T}kf!UT1xv1wEj!8kUpQzTK-(LuSp+(+ zwu8J5=_WXg^8xJezrcl94M+|Q5qtGV0(JmN<`b=yTT{CecskZX4>hZ%(K-|W>|h^% zbcZ{zn=<9EN-#SoS_dBI4(zxyERSl(fn8zY9mU1)pY2mnz=0k7XPG(=%nraVD~sgj zIsv zD3p(){-K_rC*YcEY76*x19hP@8^w7yZdZNpAwPfk-L5G@nL$3lxCakoP2yo}YK!_9TTSt6#5?+JQ|-cR|Gl z!{UOBa5SIq;pA@+G9CE9$yVxM-ipksk~QWCzwZE?xlm>z^bp;u9QOS0LTQMyJ}sGGo;Wu8R#$~o!}m& zZ}1*3OK4&7bL7t!;6Lt+d!&5r4uxHJ06SdxvK|WhF6XDhE+UOUQU^lk-dl!ckukXp z+o2GttPq_+#-tJeJH)WlM0PHPVMzpzu{eVx(n!*5#_Up&K0onsPn)EV_S-{X96X-3nLRiPPrg+-A13T`)%5`?!mG%2w43?cyl0U57 z%0zZh*qv)*B0JcTa}1xIzCq;Pl2FW4XePqdNYp9@qiZ z<>w_~N0(7Y--x5|RtvW0n;k~~_F zNAvS&F;m#l*A~&$io)*G59r!e^n(@muYNaV)jJ^#%klZh{$D~m$rRukqS`s}N*P}v zwR5;a1K6Q2b^fE_LSVZJ$(w%%;lNQk7U3$L)CCGT;=YA9Ra|!OuxQ+9>G@{zE+yyrnQF-}0U_0hiDBA@! zJSF(%Aa`gMzzW$o1>_FUN519`BVqJp|G-mEuExoofG;@|i;SAZ8P~foDgxn7j&UFW zPung%c5hMBPXGYB8~PSt6ZD)?tUE9h`n~&Z8(!k&7Gv;Ehuy%H*&Ro1Xq(V~0_cbs zTwifG=#hpEmo$%21)-2J1NVu_G~vc%*f5bEkP;-)4yZdI!v-CMVMEaOA=##(*+iB_ zWeR2Eb?K!z(Ma#BpX>>i;fU8a&L+|N$62Jz;nj^OnS2p^P?oWX{!$-uf^^&c^hG3@ z*iMQWHe&b^a8v|h%e1f|!Nb_*Bw&vSbU3HAl^$(rws`Ac^OlZ)#|603I}B6)*=jho0g**{|C2-ms;=YMZ{uHmN3<%j-C*<1Vp!g&lW{)qeum9lJ_S zTqCDjS&ml#yX{99zvIfd9}h5m_T7)<+I4d53=`R1xkfhcP?_EFGvt?pWQ8-Z!^Smc z_tE#J6V1$iyJGrelWEOHW~4IWMFPj>^Hod>r^!jQODmJ&9 zuG>w`b`u~AXdU)#=TWllFmt*6JpsGpm&xG^s$Fe^F3e%aTWyD1RNHgWrrAZi4YqbI z62;yMmsrHGgG=nBNw-d$b~|^8)dM^C7Vp2&nF# zo!+lx=1e4wHKnJUa&sA~ol?*C}{gAL0cRvcrYvkTe%OURYW`s@>cR*u7$S60~^P!~5JILz;#u&aKNxYMl^ z!Z$xx*5gGVd%~F=0@a;tBj@0p?o1&s+kcsZLeGrpse?hNsxh6`?I+fCJG~2c>Dzm;Sbsk-rVk; z*2og<7>*eC6K^{o=yIqMdz1ewdLT4d7_=LhHu5T9IM7IeRvBqtg)u@s%7VNJc+iGt zF#W0F9YDp1^dPi@;YN^Eac~-mm?9y8l9qxgOzJxuAUnCfu*~^VZrW9;5}b%c*=aEH zBuB23>kA7759pTX=UpI=R9PSqq*NThLNW+4EO>>You#x4G{k~?!mCfLkeu{lRH5Wh z$f83*FQX>5*~v2Rkm{{6MnX?>>8t3yPtA6UKYqi~kOcZikI%CDf4(5LgHVHUxjZrw zu`Zp@6#t2XJNM6tGYM>bK6$c;JY7PbDkhK5V4DK&8Pb!=$(rZ-6Sy1)bTP9?b}d^iyN)EyVH!KW95B!~wS$xm_WoDo ziyz2O2gtE2%)Z;{f!Zzqn6x0$9U{whu)?z_ywvK_caKmL(^^)o$XrzhnvYO8#d6Do9MN6 zdfkp37_{AP2C_rqS){dqf#Cg~sNLlz(g?ps#dwAf4cDBplSViVgOnnWV7tQ*#0<9g zS4QnBX8jGoEiLVDNlCY6WZa&W)h8q4@9F6djh&y=4plNT;68iDj2ZBMy$2+4=FGc` zibzor2;RNr<)oqlWrpv{%Y(|xDVBiRA&^}aqjnJ6S*j~(Rk;Pr=gbbCJDXNk(DE|+ z!K-xTYjou+boHC`)0MPg`TY&c?`v58;L3MGK6oeOz^;(XXCKv$x4R2~T_C`ID(#?S zLk~w4?|a2GZC&EHBNw}-c1_pySP$taeEccbm&Ls5a()^fLk7o^ zp|PZYcZ=$z&N$#OBRv@Pz*sUsBBfLa04NHwK7?bu>ijem{0e~=Ok`r~UKdk-2=C;x zJLuQ|qJ=y5jo1Q&cLJ(Ac1}?ptkUqog4bamL|o)k2?Vm}a{3G3nc4v}$#pQr)_N1V za_ZUObcW%iM}*hQ{9i#{5I1aawsAy-x)JF@N%C?oexK z1KGBhTtTkabL2YW-Crcz_mVT08Nd6jk$kh6ENfuvz+PMB2%3k#Z~=Cl!~xhHYo{kV z=;0Oy?B4m7zVjV@e?3EW-~CFz_?fQTPe0#DFEp`8`T%xqb_@Jsu_w^jVXq*zJAR=H zqR}Pt=shcR3G8r2famb383%SJPTvDyH+$A!;mMkn^|!RNTeGuo=VjnF%_$bZ4$iL- z)nQ3S#+}gE;S*&n$;tVrCbQ$~D=XiPpTO@53-4AWclhj6T6*v7*`&0TWuX9`gO!$$ zic$x#tE#dn@D85@!n^t^3WQfyW@%VTVat~Wz5jafns;c!tBNgu0DiFz%O5)U%fnaB zKGJ&i!QOK^v^@c>L$pTitM>wz2I`5MqkN&6p1k7t>XGiXw9!KsXh(;BHE=-JD17=I z0I&l|JaL_zZYRe(SohHX3eH=-n$bE~>OwJKS5KzbnCtY&6nVYz zBlR@d?;lUXp+b;N?vVQspiN}9>s}Y`o&bV!ca_1cn}ytAKVc$g3eZi2++kWLybt~3 zNLVbpif@7LFBJZcpjXb4RpVcI{{{=gHhZn%cH`+fG_sJlTYwbF{koF71fUH%DG}pd zu!i@F;X(lqT3*-izw?T~Pa-W~%yZXkEhMwWEnXh2g9t^UrztiyASGC&xm@ZZ2We!I zR7p#`s2`_`8)&qD&WlmW_xQzJaDH=bk-1csMdVn~hSDsTKockKIU12mhv%BaZ7}@p zWGE%E9q}f@`IXQzQuBh0FAbhpVUuek3&aL4B7>el>&B6Rcp)Lzps>RoShh_pw}IKo zwWlh(MYR{1X~f%mXqIVgo@rdZX?V6NGJ|a)B3)gAg&u(&UkGiT#_XOcb(FDi@@$f` z#4(-DL%Gh$601o<6^XATag`)x4#`!1&6rE#t4Qo@)uxt_NyQ`;!SO0pLpEByL1$O? zDof}J15M)vc(T5&06YHms-B&-7Pc|_7b~|HvTlSG{2grpcvi}&l z(n?y|$fj*%*M4&BG&y~q96L!q_?$62Zts0TK2S2#Lc6zWi*4U!+saK&hJ{C3RD<8A z_Ul{U*mj<_?Y*S_wB@jE(_Y)JjkczC%de-%uH($W?DpYLdt)Fw=&ic84!a4aU-N3S z>C`3Fny)+kbXrl^>46=@Q?Sd3BB)S20K4*%TY>6a3|cu2*!fvVP5pHc1NnpNEOPJ1flJg*OTRo2$yPpYHi%8YoqMdIz|4BMC@TU5SZ)v^BA zV%yjvR?Eey4nGYqv<-(K3!teK)nWx%^Ixd# zVii9^?oBA;Gb}!sJLNzqC(*hkl5LEW^RYj^%l@px$O$3|$!JV%BWjKcNsihMr)>Z! zc+MGh0QFVTO~D}aLLJmh@Yq4_1a}Folx`ZBW<0FdEzm#-q8TTuBG3pjNSM>+O*&ey| z_W$?)dUV|1e6Ktr$2>OOHZG0IvY|;*BlX3}ar+&a9VF#2ha?6`p>st|Xavm9fP^-J!yZ%y;NDT%S z;hZ&cCLLG8j7vYp6supy6Nk#=*A-9So!S6NCVUzR7)EOkLgjr$<(RE_S+?L3|(V+uaLb2Y6Rp%z@pVB_(&xDj}7n_%r4U?{7Rt5A1%(llly(t84#IznPko4DH6$0^08Kk1n>5 z_72v)VQwpK8u$Dwph*#H^HFDe%?%V6%Sktgv49YX$$Da;^4_; zw7}gz1zk{``#jrcY*6~Hy6kyRe1#ebj)P1p=i~D9@{_Ymte2I#-Y%|uV}b7^-7jwF zb%-Mcsx#bHe^%_>Er1JYK&HWojmtbo{fe|maziprlcP@JyaQxSF0Jbq7c7OOI5v{e z1AeB1V(%vZ`hRY}_5TL2vp)KNd@vVaELofm+Zmf`i%u{*j$K%W2A!i6rZ6hYB7T@- zWFMViLm|MTk2BkYKm7^py5pJdG;(zxAR1bNjOEh%$*4keuL5@bK^8rOIyjvI?aA|a))4ckkny?=Mi|WN;Q1N!Wd+s;6bLm06R|c(&6-4MiOVS&+OR@ z+hvxMn0%Glf!4(qFlLuHlO#FQI*_~K`Q)vYWXZdvemQyhJ(fuV&aiNFU9pN`H6XV4 zKO^gQs=yA+?x%y~i;d)`tz_#~^7H@C-g`htacu3{zW2ZP`>~J100M@nA#dd}PWU*I zb8yD?u?@$Gj6ejDMGgi`G?-vS7FlGGWpd6r3J@TKKoZI%-FJ75TCLGY8fj{5u)Wq^ zy{fCLYkIn>d%B-__ugkX;RJvkEaC?DxVfgOU@-HK=bMCl5;{~TSul^t#T zp*4>l+5I@UF@iB=0=Vqud;&W#I}w-eg)a_OU3Or%f1hReU|V?|)OOvvmG0cRWY?~Y z?ZDsw;{pOo>j+O4P=k62>`>5woos9Xb`aJfd2D$Du;Us{Y6oD4LPfwk7y~lXf)po}`dsfmQybb&TRc6k3+OPw#9nPxbr^}(1E^=#ykWD$c!*z#lorr*M|Hb^zpmoM0 zsoR$$h9SOx9v7(rm^cUQ0O{4nynDhgBnXKO=kA~KU+@fc7RYX4yr`TlLtCfN4HjMD zk1VGljNBKgR6-Lz?d7W$D(6E!P2Ij-pBZw~D6@A}^v*kMMw z0@xv?t+PL8L8AF!tF{ibjt_FQ4-|8f#DT#9&)LOxklOwDb0)Bp?OTl`>^o#W4V+Cu zNHJkgWS3KAzcJKh2(=wZZ3mG5V5`C|JFputkrphs5;SPvLRQl}XzR4dbhAQf;Ytb% zr=w@-YCJQ$EAfQ%u&cMz?|W(AK{^LDTpXP`Pe;yhoA`SS>7c4hNnwTE{{3|23dP3K z!GpA55e0{^0o8#I;uB$ZfgJ>QR}-xwI|=N-?4(^qn7j-HnGWnAvb&RL1$N2lbon-2 zxbf$(6$xk{FicC!;Suf0G7)+Xx+M5<#;Fn^U1v=up2T2F0t$wJ7h>@Ft}mE$`2nVQ5}#Rbaqm3hfS3j z)n(}HMr&m(Cy%Dk5Y2)qbQTvvX3*S;Fz=8lV(BaqaYo#^F5;qI-M0DVuyx`=mfQ2(pgo>vFV+#S9v~A=wH4uGOjvfjrw>&7wW79c-8gyRAu!#dp7Ck ziGcd5tP%cd0AJ{+)Kmcre?o%PCVf1d8N}@SB9nkZ`?!bS$GwCq`>>C%Bed@APs)`q zT^Nwg3hat8utQ~bE(38*<3LY^*1WHW!n21MA)u zn70TK=+tR(@~q~ydxL&1h&Kms2qdmjZp!~q)gJQT4x`)@Kjv!3`QbpX|BUigZcXt^ zq}m$w@#KHMowi23JwFNZY%$RDi$R`W5AkR@kXj9*ABIzF_`M?6#6bEwh^+-)N)7f! zANqC(bsWpqehfF3)?o~dUO+=d52KJ(G<6Z%vW>KHJ12pSPGT=qDB3pdql4#Y-x)e_nd0x$%>+Ue zxkFcJ;{l3$KxZQ<>K;V{?VY1*cPJ{BeNC zA%;bEu*f9Zev(%1u-b(?bTxr)r0}NG*XeQ;9lO959ZPosH4=F1p$l~UT%L5p@dncC z5Hc=D?Ov*HLTc`f0h<7rAh@%&2kD+jdiywAy=-sbd)`g=OiuTF zknRDQm<_Nya;>tHz)p9D1*e0yP6E5`-OG0GUNR`ieZT+ztdG&$k>O-Ettn(bO zz|()KXQy91+fMWhoaWhUx@Nz5wjQ5p?Iv*>-Oy5QZ8L%Qg0-7y-P(S#XT#q1ZkYBk z1fU!EnnY=!Y>g15H=~){1XKs^hU`6`^v|Ggh$k@A7xu|u-$uxJmGRU3!64sHhxmRt z$hTpCuE$o{%x3xVVBgP%`hFPX^GRP%z&mLl_4RBR$cabuZQZmV++MzQ)mgpX{H5x2 z_lELLp}qff`N=})qhkaWg&O|8$W5Wo7G(ujpet=k<7ib{fFB~c)#>5m{F97r4^AeR z2x6dtV=J&RH3DdY6K4+Q9I$f|U`a1}g}$`lbpJT3IyoqL?Ov~k51!Yv!wUy%KunNG z5h324#pF<~bye&+s5X?<6P_Tok?I7g4o(0En<UpDTS6pgurGU)wEqs|2L8w$@iQSQL~CF^_{RQ3BWBac*)#}=9kqO~`m)bN>OH|~ zfny1V8o3n(StFO-MrVm$CnMPb*a6i^`P;%3R$vFORVd(s7twEvI041tjm+Tgz}GdE zZYR*z{j~Ks?KwloBAC!cr83C7c!w5kraO=5>@B){hwj~{6IUrBmSVB>9&JBPGuKjZ z7%kXJ^S5vT7P5ub?jmHNg{}CV6UeUK!M5)l_bIzY_&n50I~UELaygcTetS-`tWVml zJ9Hc3yHvWCKzmQovC~ zIk~)LNIC)P1eq8FhVcf4-RYPrLkB|>XDhOU!mevqw@g2Av(KoP>88q@&>=cSAH_2k{-sgDW9!-N@lcDO+LG@TySTjTl~j?C44o)h(02 zE|_yrKw%d=o@PzvnPgF;`{~;hz8h_;db4{^mOibz{0(dlaW-U2<0i=E*pF3(}J7bAJuVu%m^K7psLgYok0ev!;d z{G$VMq54swc7S)SF6xp!KH=VY*$)qB0?MSk-mml30J5ywJsA?If2b?Kb6~E!Aj}lp zKMNMPEthjryQ`NqnTEp0MLGX-{W`gyaQS6(IqbYQVX9!lWJ?b7CjmPN5wp+fg4N(# z!82FsmGDn*#xsDlBsIJEA@i(`0}4<8s)M8@_lfT4R>)#isCD#C8V4$Mjswk6n;=!` zm&LbsFhOboMp?-&YW+;%!ig|*c^+8P-c_qqEecKtD^ubnIK#5JJzKii3ug0k?j=p{ zx3tJkK-MxJZdCkE!2dDN!^>9#H5Z!dt4zP#3Q)iWm9I09Q)4w8(}~}5M{jXY5wLU7 zp{lsD)Ly4{dvg)6gK*F8$30P%5R801l)mmqU-V)~C+S-UfU~h|-w(-Tc7S>?9pQn{ z0ob7p8n+q2hJ8oRqp=~3?FRgsiS1A}uIeBUg?_)VAuX(q?YfS#Wp;hWGOe4tgq>Aa ztf$qRnAu5SCo548kazkrT?ZFSpx6}Jae#InwF0|a@P5_uQ2?bOVZ~uM$v)=H4@gvc z`UY(~M(1wOxm%oDcEvUg*ll5EXBXHZd+bUL+ewOt?6iP)yHC^ly|i&ZN7f5J!dWJy z?L1}&*n!y{xIiaWD(rNomYXZ96EsKfKeGVzCgbq|yVyWI@E5&C58H>v64=STA`rzh)U~S{uw9QHB_zCS-rW4&dnMj~-(0`GrCvSvTD7b(>_G8==dk?x zYfG(KrO^Oe(WXWXbDK6C#!gy5K&g%$EwyWxK?5w&pniS#AAi)Ob{Pn7SdgW^1_XQe z=K4N;EGS5>H++zV5#F$>lg2|>XMwE=p|uODhR(OFT2OKITv{@{Qpk)7YZg+}8HxhB zMNX1+egJl1p?-&Vi^q>M&wzd^oGGNH(gQpB9I}6k{R!|W8^}wzFHXmb3yC^JbEk=ny1a1G3c*1>;2mpd))d(xWr&d|yQanL&OGC(@)h^29pk@Ag^`DjnI0JAWYueADl$v&5 zk|n28AgR-DF}lBU)S!#vFmrYfyTFxCa_BCcb0x_QjB(aJ z=%4rOt@C|F56|M3lBSa8f0Z$t%tfBF0=weRmo~pp%KW^gjHz0!k~q93*`kv;`6Gc2 z3pUg2?uE-u&9$cbDl71G#f}Pj`XBsgQt*$%Q$oI;EgWpD`fqdog8u+JgmP1DPX(}( zQ}n|gRucE^AS<(jbWZZP_M@zv4#GMRJt@SK0D$Y9lXfE?)Rfs zV7L7U=ctu-Et+CAp&wcvyg)Ir*=dWC8ud!6lytx3Y^hG6vjc#GNs*M1Mxjz)1&Fzq zVZfu3-yu0HV%VL!OQ&NQ*!?=YbcYUBU>6Wjv~%aez;*y_)vKGp>?E*j(ZUwkHE*5; z*wwF}N$bGukSO-6uS(Uf%|tH)*p&_nDg`H5O>l=oslI(Jy(F;fX$k6O>C@AKn!(_7 z12kh`S6K&kp@?J$U>9szH75huO|h(*UkRx01_Bx$qYW!GV7G`a9QTZmQ7N(mszbm* z$ABHqyq&D9h!AIkYvWVJktlI3K^O>{Z5x|ia#@AK?xAM6w818DL*$n*wDE&KEM^Gi zQqm(jc%7p_jtylSGS5oupsfRg1CoP`P8w)kx5>P;SG)1jNOfO-aSJ$VUuXWV4B498e#@iLuc2YMP&9Y`EF-B+W(9c#THO^>OX+y4_{kYp6Xw4Y*iNQ}#n2Q0zgTyH#0{jY zo!2u@<<$aC2m9-Cx?27|b^N`YdBw%vG zxmX6RgTk)F^JOxCoyB6R_bnMwpp)fK0{syGPT}{3U#(q=m*2`pIB$8Kw7eStTcaNb zuj^czDSc8zc*^{qjlWWRNNe*RIOm66MGqf$SCHq#<_bkLOoewpy(>_1<&(rESx4~b zsqW^V*;NlOB)386Ism)x1~YeqfDTJtYKK*zI&6{>JS=tb9qJ{pLu1rquc5)Lvx5QN zjhMycPBJ@SJCvo`4r~s7vJlomQU_WW@DuCoq^s;uU_N-m4Yyj)5j1v+)#f0eSg6(3 zuBBD0thQnWg@@C&ZL|l#PP5HBy|)|^;YY;!y<-1)vGb(Zc1#3^iYXx?Dqcj~6miKS z=AJlmMjSgYuEmNg@#4@WF?YTAZ7o~yY7r79mTVHMc8jGu#fmLnx;JhZ0^6;pFoe=u zO`+@P7&KOKY)C{4P^dG+)$214=IT|3gRC58M2}-BlfaehSUz^&UyfVs(M)#;*;sv6^ct^zf_w|(o@x@ zGW+VI$8;-+IrXhndRS0Jp~if120-q_nP?GqQiSYbo3uuZ4YgWOh>$jLp%}hYj9Vc_ zEX}A{Bz{^UeqP1PZgaDwn`k#(d_02PQDJaaZ8*UClK}#{m(j#$h!&`?*BPmH&ZIJFI_hZ=3=clS(NAy6mLxxSDI%i&}>iK&-37k%{R7_39Hv*-~HPAYhZ&}d8 z1174ab)A&HU3c@$e8qD52Gn4H_;jRVTBmrnIN+6BuS3<>>bg@UbA_@+%9%}wURM_K zI_V6Hg3JodZdS)wDC{JL>o|rP9F%k@ zNFJv%J6Qm^gWwKZC9so~qvu`%2ux?5W02icnNHWn)T~x`}Ry=w|6gX+35{bx9NZg0|f|UTed~aT+YC5 z_i>T*NSwVQAT_#ihnd}-L~;6xIB`vchKm_%#1wEn%@%JGt9Nh@3x5~$Hi_k0z6&_y zBKO7Gb^N(ow$SnOc>_Bz#PA*Lw0rF~Ywn&pV3!@)$QoyW-SOM3unXN#zFRkVxw-+> zb?a74y2JvsNpT#O0CO!{S`d~F7)}PZ!xDrJsIERc!!ovO+?ZWujRs&>yEZ&(-KAqJ z8f)4*WTJq)j(uSJ_BHnmbO);ItLg0e1X_k@3Oj5X!ty%S+0CBBkPe=(jOu2UUl&4a z=2Zr&1F!?O+qJe*ScnBRTbI+#Gi*_pMfiHLZYgcs=$jwMSo^N5QjCTiyLW80X?7u9 z_R36VXES^NcBigW(nDKXhh~txWv6(?-S27nh2eD$$tr*yA~PDd=Btd)LVJAN?PR|_ zS48L`TVVHdD6_c%A!7Iv=69o)Sy3HVE)*k|a^){;#jxdU{T7POvl!cT4i;aIVJZhF zQ~lQg-liX9q{cRi;B3po{8|j~Fe14x5CmX2eKRZzr!zLNDI>);+P56#t?RYFn`ljL z+vZo%emWmvo2jDx4AIc2s>|~M2@g@qHFtrW2llBUcMvG4*7wxyuG#_9_)Gn_EZ6I% zO)9124rr`iKR=j$IX@u2?4TX}OS`_-^!G)oQ)f^eBz5RS`CX~N9U?`M z%J&V&q*9Kbz{Wikzj8NduawrcrTS`VT~C$Z1GMfxV^q?*&xZLR6qL#dYoN1YT1e?AQKvBoY@^uye= zpMv=ewW%6!ZlF>n-^&RVdQQK}{{TD3Z<2p*%Vu4@3rxdv+R;S{J68ZZT+wPSBAYAG z6+Y2DGQ9zsbfvF?7{EzLCq;H}caMWsb`sc0VI5E%3j6la zp1p8T6%g6!zz)oA#!?1$^VbLj$iI^$;#0)sTVl@-TyQ^ueu#>~F2MuB>lY$=&Jo{F5T6VcjSM&psye_sTQ;ZV zg|J_V#IHdtrUR#geKF7>T8Cx`2FkQfv+u`v{4mk?$H`3V!0F_&<8PwX#Jp5>xxhQz z`H@P(=I6k<)q34Iv24u_isexS)*t1zqjW)4itQk{gAbYdqzBFoirOLm<9h?P!=$Rw zR_UZ_Lp3{k!Ua30_kusGUS?#@ZQf=jde5+GG;5WUtIwpTKcf@_N?n~oM2>H)Ff%Q> za!&SFv!ieA_TJ8Dv+B0>tmE7zsB%FYj4h88rD@h$ZV)adMnKf=g1EYg1cpa2>y2cIyJLw&Z zf(&q{r>5XocH^w-y8b^~sok%$Xxbc_5=^t^Talek?EvmJZlq0{XxA==bw`d^O+vi= zM`{0YTD!%XU^aLm&0ET@tLHA$&AW8?G$*Twh@hiK>HK+$jO2Yc{Z0#3W!PF;vWZr0 zqm4Uh(+&<{w`MyBubZ`klUTs!ucechIX^Az@-5nSm=>*Lo3oOZtk>e{?W5g?wb*(G zXw6P*M%k#_c9@AHtRr7-kg*^s#q<6nKV*eXO&8e%njwyvGzZeg+HfH&k}4RrLz)32 zwVSb|oRQigmK~zlNo0pucFs^co&HIgUC*8+dj(plU9WED?wygP))LsGY+yHYVE3}{ ziw){iCa8~jNPl-^o){2hnKHq$bOu|@d^e{eWOmY)Oy|l~n#}IX$*N54kb(kE z+kkruJytIf3HNdoy5%sAfNYs5f0NTiyu-W>uBAB*V)k*5PT~1rw>|oSCoT+-@JcGD zlt_G-IrO(vL|mFc;)*kOeb3zTK!%lzxAQ^mKpzXH>o3${yfvjn8|0>jC;b%aJc%pydYN#d!vt>p{ZG`SpLfF`zua@Q zW`}j~WR>ydkQM@YAif^$_w{g3=>?1BS3{V{=}xiG(n)n4YIIW9Y6SDTZ-1g6AV(g{ znJ^G%kE7~M@oqQ8r{fIq?Jwd3gYwUrN40u={E)Jhb3Z73`~REw1}{vQGvi`sUw~Fb zg;DL?Tr0o_W_&4DP}ah%DV_r|(4T6(7To{TgI0_Ykhl{vgStI^`KoM}$5pA6uPMC# zF;ohHIFKK5$tpZNkVNMW52(%tOXX{|UK{)68G{8{*UW)D1dhMcDvn&&hMK6>|2h6$9BHXM)23R!(+t`a2ZCt5{YeF?Vy;|~ z1$NNSL10(P{Gz$|zY3cQnWR&!To!pw^OQxZ*yr6fnVrSrR=#54e7!v5pGaeSq?vW( zJUiH?X)hI|xDD0srbdP7;9>vunmID-^4rE0zz+D&6(`>nPMl8>umgfOUgLkaGX4~| z`*8JTU;07IA1j?-!R(B&>?F3s67@N4>icV^*v`o8B)kKsgBd{{#CAawnbX1G6x(++ z%j{4dJDq0Crv*!y%^78O0CpQTu;gz4e#UJ;Z<5&^I6#ox0pT4wWW{7i6ft`#O`l6M z7qHyzZZb2wL#OESWkO?MyQnC-af9~kqjejtfDekgRa+@^D=ptjtHNpP9{PPRt=~xt z*VEk9v|u%@+01^f64>30<=&*0Ze2KqZen034aqR1?K;ezT#vS5iyXjBJ|IXgJtQVl zTpZh@$KE*sy8?-~bb>>U13LhkyN~Hw3TKEtc$2~-X!|wV6UBD$4#6qbXz#pJ49rf) zc5rev`n;kHU7Zf&P|#&|ojR3h-P+WyT~X}v{`+q4zt1u8G_xk5m43Ha_U&fwp}Eoq z_?rVYLk$Bvi12!s4g%Nf?>=d)C3vhkcw(8w(=5wpx`#|Jvv{VJ)-9L}?^p(Qi-Rre z7FZ&VREaoE5l1ZRmsA0;gDsg$JGK;T9Iea_?y&c=q-xMlyPX24oa4dn&=@ojY|ifJ zW2(rmuBX{HKr-0WhvIsYI1{JYEf47Ja+!fPn`E8|1lB_V^C2RYR_PZUrn>Z zIPc4^t7(Wv_QCN1;3SL#sza&?pgL(ikceWUb(2J6s6pn~|BmFWt82~H<{SS6}!H%)Y!DZZV+n?4`x4F(6LokVpAWQSD{I?y`l{|ZR= z{b)i?T5viT=dINa3`;#FM#st2eiHE?LI5{T;DDUo<7%}3d#7ww$OJOFLM{#V!tAf~ zwHoi^@N8D?+zfODrJw?gknw(P?Fx7#<7_A^0Cy-$ z_i?*P75__D^|oYMKNo5Mdb~9Td|#yf!;P>(Bo$@m(bc-^t(mymF0}RrJPBBCW#(3&>tzK%cQ=qaYtw z#NU5%CV%cnq9#{I*4g_v2X@H0m-{(?a<={h>~eZ8uiKu!A8K`q)d`*B1*3!P&kJbe z>`XkTb2?*T@C@oV!3yjowi`H}hW^4fY%-1ejeTKZh-J5VGow22J25H#4>iS!K;SJ7o@Cji$&LMr^4M=IK0#+M(6)Wd?11@D zg92oF*m~$Boxj2s5lMINve}>wtfr*!!AOgbx6>f|E;cm}A99LO7#w6DJ@&kx?rRiV zNo}27WRqtlSWhCG-@SC7#Efuc05LGE>ULL4J8|_s!7d~+s)HBo*?2meKnG%IF9eBK z*|uJ$;S)=?ZEMm^(mEa3)vb$Aby=)t%`CtUtFl4Eh9zp(Hr1`mz^-LWx39l0{9UVJ z-+gEPu4U0K?Td8jT%t=Cv$Wm;X1K<7?{4nd-Q2IcxmR~{j{q#o^y+2l)va`|F6Lf6 z-G3fwnL5%8n>Wd_@K^VFlgx{!m4>`-?nHn*3n(58o7OJ2oZV+Rx3AL4otBle*?l&2 z0WA#9H?Wf@5X*<@5HK<|vzy8l^%zHKI&{M->Y;|a^+4QB7g6`cl|*qWN}RlDM_Gxz zr0CKpvXk=xwC?T$tN0EDo0;(7G1pjWU7Wa(C?ZpG%ZrQeVw{r?v{g(GLWghC z+CyxMw$aShw0IL=q4U=B{h|MK8vGjtXuuAruDg~VRtI)ih15=>IzTmY9MVFd4NRtv_+o@#i-8_Mb-;EK z-T~Ob00*U#4zvzpI+>+bU+T23)hKFW6V4RK23nqshb|=r&?PwM)#~h((>?s3`U0E* zkWgS3_3_i7xkFe&rH-?JR#2R&LJHP?z;J*m6uL7?0_e0;WA?8potxF3QtnM^?h1OT z=5$p)!%m$AFjp{C90vHB5|hNY0N=~rE;ZVE)@n_)+I!dP;8QS@hze|kXIp`5 zY9MqT6udpvSq~qDw5|cPW}ST+1_)JF&)>IUSC1y$NtHDSbZRJVr$Gm=hCRJN#8g@H zAg_;*&>>r!oWkgX^>c>*o#D z=DfLr)&gBY)9UgjIai|Cor+fl5LIU1y z>MhSZOaU4+?4f1PMgnbhRyVIaZy$7bh}pFrPVMX&WONf2(AarwBWKc2!FkwF1hpGS zeG$cO5(Ryh{nObm<~( z-%V?P`PL9JzJ_hKTKG=dx|6o;rPCKEI+l)|rh~`m)OorU&j$Q==?0U&^}jRJJE0N4 zbLZ&5Fh77nJap797loHc-BQpWxF9V3&|Uv9VdehfPZdSJ7MQ zL*NywJxcd^oKe;*9V|S|vLjF+Yo?^dJtW;??o(76MZ~k6i=h+O>C7!UA43=8=m^ey zB!!=4+jxQ&ZLI`l+>bxLge6P|cE}c+A(6{M=uio7*|*B-&6>L&?o-BE zrVKa3K;;%pcAq=ZykJV{c~dO2CzgToZprjA3xBoDnNSY-Cr5Pee9*={HM#HRAZNPT4I)3NW(1fMf^tad5RC&@aa?0?gwtiVpo z4a@Tc6XsF^Z8%2j4%6o2G=C$7gjs#eCoQr%y+T9RbDCH08D9NovIS1FTGwB_0)F-C zG}){3l+4Oble}6C%Cygiczr(D>&szYEl2x&HA?(vgb(bSQ9j>}_4)Q^AFdqf^Tkj$ z6k3h(`EHy~yU9M^kK;{7`*OI?=R7c3uE^IsA>h!APIavU<>oAG_ zJDNV|pO(nt)Jx!lS$p z)Sxvr?qloVU+V`^^G8vm1MKAjoLGRr+&}%Vv9(EWMf?PSW_-{k;y;u9yuHnEVT&qEOV7m^($={xJ8b*Vs z(1bZOHkfVnj4XwpXA(?q(w9F6Q=bW(hyoTg#cE?_TeWY?*3!DIv~@Sj*0${8<<5gv zw^QJ-11A`Vow&eDpfNbKLc9Z8u#)D6P)G>Pn@2O|P;@k1x|c z-5CJHQ?&Pp=1!|II|%NMoS;jW8T^6P*#&msYY@i)#-*h(vvZKj<%In=#gX);RaY(=Cgpl?AGsJF$$GFxE0_94qX?LoG*lT8`|o9D^`(J}nQf z5ITz%&Z1qvD;TwW4NAfTioQ>=kLhL#or~s^eC&!t#{iUu;k%o}{$bfRzr=ItLyHz6L=YByu+P9TMu~un8?8(_^LHP=f2LY$;`ZHiS-J z1*&U3j=mbM{^6tmPdgm?ADLhsxU2s)+q9v2cuG|Lp!o4%@2|)B$$6z}`N05DD?lLD zQvu#1a@z&q!WE!R^^B_3!6hSAA4*2&+*-4%U+eVrbv|&U!@$lJ`bLF=2W6Z(M^VgU z2%wxqbx_p-OyqV+{2^>E`vKJU_s;1Jp@8*>ftA79oG&!#;Pq*DzXDLXoOWr}$;2M$wNiO=c}t11>qR0t-ST`{eYJFt7_GfVDhe*qQNJx*K`ctHRm>$IRU$NgIFjxZWd#^ zU*>0GJD|FsrqS>zG~!n-=)evIFuP$hGHD&K-OydJ&G;cA@58+^TJ9cxT*CPig`Z}FDL?;hX2~ktJhaGefk=6`Z!@ifAp{<9-qvv19M0J-IECzL&F}R%`?_Aa zLI8HsKmZROrhNx!-%&ocqHL)?1D-ou!F*`ZEKT%hhhxu+7xE< zH-p&$*g;;`AHnQ2on4=vC42WQmmSzm9czKj7;O$7S90!j%RCL(&6-$d_AhXfEi-Fe z$ypQ1Z3wj-`JIh{T`;X)0)AI{@f^B*LBXgwKE7yqGm$g6M#M2t1FeJI$Tqd?b_iwP zz56^~(f2q#Y|7)zBftNMM^^3?n{1_Z=sz))8^_s86)Qbq?_n}z>?n7aR2QEW_m~b} zq0I+r-+2lNr+`O!8-b7DfRn}`b=k?9Rs^y8a~Hsz+9$v{pO3Fv+Q8IZ!`~9 z>BnlrP=5GDAX)48lSHerel36UGSWJ@vO)(3Z&rxrjP*8UpgOdXk?D{`0!U8BbH)O? zf_*cR8si^I)qf>A(_vjLX=hI1wEmi?&HyElt+Zx)!bx!E9A@9QX+KYh<7_6IldIBe zyIv2KZ)%JV09Ali4Cmy@EX$Fq(?#(}IqBAF_dT?3Z(n$z7x*;h1?(_j(5yYN113FK z)sZjI1jLvtFh`I%TNTEb%Z}Tetf}MgRX{3dR}|2Z>MNyn%{qDK#N7%|5AfGSX}(&F zuaoEBX7}1k^Ybk2ciL%N9)8mg1zE-SDrqU~a;l%cTmsaC3K?;J&T z_1ahQB#SSA9a-M}k0Ih@d%yRa`T*5Qt6NtVs$sQJqgR_ZuimsNTm6==*8BDqSdETZ z&VQ`{U$rSy@}wJoZiA){2PMqkJx%o=r+6kqSm;0ZT>ZJ9?jm643f&_5%>E3mpvLa+ zqjQywUEma~CQVJY|K}W0*vZGpI_;@NZ~NHJF0gAqnA!|rYaK-0M>3lmIn^qugNrLT zovcA6fE~nls5D}`(ZMu$8V%BH;8dEikQS|^705NQjT5j!SO=4mI_T-7Nio-{OLXiU zgFK|00JCFT!$3{i%tf?f9W%94$LQW|ii@Tb2PiIvlI~H=4Q6(0R@2(G93~Ht4jC=5 z)D4cVP`yE*2Ve({o-V-yvs=2Fmafr`?SPf&A)Up=i_Gj~WIZeqFYo*%I;y$E#^0fo zxJ*7G(FTBBaxxqHv1(?Qv*fE1A?;xLF8GLCwOR*otPd0SdCQ4X{I>9W-Fq0kjVAPUf1>fSq)S#S$*Dh-%l(-`ri( z-?7L}Q`oUrtORyrOCogLyh*IE3myxXSY)37utT}*vYD1mOWijuu`HWa{_>G3XO39T z9xV@Gcjtb2eq9mH6f-2OMDkY>)>p z(VFf4zM8w*{sh&bg69OoI0D;!I@CvoJ9HA*>AQp6H9=tdz$`BgHgBB;S%pj4(?IU< zjDei>bpjNRl$G46*4!a2EK-!@3$NvzbV4ngfY$6u27eHLs&#Ty!4Mnt^j7&A1`&i( zxCSX)0U%mVyL=WXng)8QydmPXJllJPQ#AowrQ*;bqJ zqbb7iW_=%i_p9;%cF@^jX*L%^p-!7BbvsnGX;#^@c-gYh*1d?_ujXRU6@C8ymjGAJhg%k%C&2I*Q+OL)k3}GcW>0M|8o8MuYljdnl-E5 zyv1wH`_rdGmHjEF&p?%hK-Ie4Y$rDRsV-OzIG{c#stTVB;TYA*t;wLgUA*%y0@+=`zmRA`SFy>$g$tbZ zRT`2=Z2X-8A8b_^g@#%!ID}>`q@(AUhymLnn=1ANgG)-{gbzS$`;e_7jD209hC?%; zz`aKq|A7+1wNrY4-cFz!ci9;hl`=#fJ_YW&@X|eSl!2Y3b@E7#Bc>j-b%%L*`7#^o z;SU>~EruOX8i1fq>u@|0g*=%{V+=cB8aP5qkzKwNb^vw@R+ekovd|AdFs&1!5KJ;V zqlOMOautEb$=5#PCotY`JJV-V<3PbbnZ2JDKTKGCQfNv!QY} ztB~5k0P->&V3A2cH4Z@JEOx>BJA1YQJE-<@tKCDfM^t{F-}2EOc@I=4mIVuAU+Je} zQUU3x9(`n}a7AKS4jk#&Da{7#fUcmbQ}{gr{+?uCXkbL8zO;_O3rE_hb$mnm(FSc* z0;~@@`#AG@W3%sPfqo4GJygE%Kpd--PS)>f^}Ec+?K0;CF%5$J)CTECP%}p+)t}X- zPB&k*w5~B|Y&M`%r(G3GrI&PISFYTPC|C9@`dXv1dgGekRVrVha9L>UN)#&Y_H6OO zg-jZ-Grjmf@Py4wI#E0m*tvt&l|&djx2hGg06Q={-`7gN|3ziJb6N27DGO9*1MKR3 zPj9^kPRFS3-~Xm}-jU4C1BG|q@z&rSmYy|g_(E+5cn9%a!-myifOicWzVd08Y6@Y{ z2Se3kp!!b_uyciQ>2D6~0F)cxmYeO8d;)7+1nd<5+w@llcKBDhW?TBakA10KE@@(+5-IcVSDvI=IG6R?B!4O%wDo7)gh zYmhhsk>)n@zTkbpc1z(}8%n#jQ`{ZKb5Nh%yvZ~Tk?FwfA|vVSIp%KA@xjyf_<1^g znVB8H9Pk{1&`Dqi=fRi+f=@0g!ltoO&X^z;4eO3SLwu zJFo+W1D=D74yX=gR{^_rCPcdfvx7l}X9acx%wvPhGk<2AJd9DD1a|X(F(2K|v~KTu z^OO-BiL97~5T(P3N#5$rR20iZdaq?2s+ zW5!HNOZPHnp~XSoOA`@sjO`-h#feC<=d1`jB3AAb!Qsr{#;+7pH;8d-#DIlN-+Ijw zqcmU#NH=UT6F1m^5HW6*m=Y$YZ4`qSTiG1y2QOg^hb8s}o&&0r?SbtizZw zPQp9hI?u1yY~L%*$a+mXq zLjg9)bdyM0nlB;GU#c+>glaiX&90(mHy1obD;;|4Y(Y3Sq)@P#Y0j&|+^r(=>} z^wo;b{1GwGuVxot#pJFBpHc`pb3dyYzz$ytibsWV8(bmiGoSyv{&DT`WuHh+harID zn_{{FC;S}#7%xD5!?qqD1^C(!H)mEq4)j$lPw1rb`_^xdOzJ9WonnPce?qojl~=C9 zn}F684o{&x1kQI2F`sil165uw=8nX#n!-*3J1{$#_sgZ;`mlVhA1c53k)>QYQ^^u9 z6f085O%l2cU43G7Nd&++Wso?}{vq!XnW*il6Z>`KVI6LKkp{ z5wL5}(&AP8-c2aG3`zGCvrW@Cpi=jQD&LMsrmZwj13R>vqps)j~gLm!8iyR#f@$wKm z4>7;MH4sD}E`{ukfH6Uv^;GFitR&bX4hWKSREtAtJ z`4J`D*SsdPHIvK^k<0F6*!3j3c#GADFzK6&Qv|HJb}KDhpJ}VMbJ7Xjf)|$um4mzv zh63DUt5th0cVPF{Tc*a1vltY1AAg+1Agt>If7p)2rNXXVo8m3MDe=wcrdD4TZPm)$ z>MOT)-!iachuMxL{M(xc_cBj{ye`PZrs?d)4>V62W}Y+}^~D!VE_QHp@dKNf*@5EC z9A9eA1ozd8%m;UxPwuts-)5OK%A#R4-}Ll?W4jb&kVv9Ci4>cr=@v2Bti$J<@=(O4 zWm@DtaW_TWN)%V)#DzPVb}e4qP8N4lMNE=NdT2*&7ynSCJQfeqMQXb5{YM^fj0Lm9 zP(FA>DG%vR8pWnk@&mebmoDC+y-22fks|N%kf;MJ>u8(jNxW%78BNqL5tajYht>wp#lbl9YC9` zAF@h6q7!C$nhfP>YLZ`P!Gt^6*XR`?u4_g8C{HJ&$?W}ZwHR3sx5t$B< z4h88&3rSs@@$~h`Y@9ATJps8x23VjLr39yTfOpM~Pw-p0t8oF|`c;#=8eK%4o}ykq z_0MT)hn{=Gi~_k+t+|7*bd9INR~kGDqpuqKAWLuE?w)FSXx#wLQ>;HD84DS}PRXSL zB^?Z!HP!F@PiTjoIXR0Xi76eu_8^Mx<)iYo2k5GuI(h}o8Z7&-OMz>oPD&k29tEJTIqm`(rnt&afQzt~gaYC3Hyx8{kthOnXbdj zHI44XbE3@DhwSTj{SF;I%fYrFDLQ|fLxVwN1ag6h`-l++sG*1Gx_Fx_PegJ{$i#M? zVydnSvuA6V|Kw>dZ+nBB`SKZ}AfjGffkAy6GEWemIe^;r`K z64yq{DS?P}0Cw0*H#9~AY(*PJa{lHPElh9A=y>QyOGMkUCD;46DbcM1%j*VoV{ILy zI>vV9aRbbghnpu2(JF(?v&WnFgqaU-H6Pn)4w+IC*(cyFyLGwcNI2W>b>*iFH=dl_ z&biGwtmfG$I0B7g;OE ztQBL!M4u4hA1wT*c^ToFRJ{SKWl!V^=+2E{muMO-&(h~Qmo$Bw|-a8`XEHA-cRr^ zI^c_QkI`9nN5swU>h2woW2pU~zk_O2*T=VhKR;FPU`_jqk9#RYbuAnyL(}?`MKh5|1!Hh3sh$Z z*cH*7VT%^B1$Gqx?95a(M_?y>EuIxh0oV~)ps)k5^VA$;t5quxuJ^_p64-&`3O zht&#izFFzbH#2D+>R)}8SE06h_gw*ahXR;g&6*tEu1*8>Z||xGs0kj_Fom3%f!X&3 zd3VM0pXz_Daf<+~QS0HZYFx7Szhx)(?Hk&ZKlVsBDh z6x~DMyu0)`g)Kc-US_}|E&+DvANa=bDOt*ZbXJd7O^au@85DeXGwhlH*d2_rBDv|C zY=PbS(^+WU{9W82;HL^kU(l`q?8TPfgMA-B4Y;>9gBkIAq##7V7GUDu_NI{ zj)WHv9?yQV!Q;%Em*xcQGF4_acrN>Dc3^ihR={yp0=u*fV0S0Q7qkxe7*i26Ehk`y z0CNC#QJ{4h!2byNaSEr5U1CpAU8oACpSs72ULgE?2XYLs1CcJ z0a~JfCG4Xhdk(M$){_gA0R= zezl>s&t--bU=w^=Yv-WhQa>x4RD*$7xd1NJ3Q!MiA+>|6ta>(u2z59En#XE^fE~^t z#HFymiIOhkYeN27tB0u5Tb=wBRH&y9%eYj%ncyWyAqa!O0T2gp&9R@ByqlpbKuLw~ z_BcX??{xv5^?P{L$Dkt$t)>j#)L(*G>4s6;{U7}UIX3HT~H2uKo6uuH8j3Z+6 zA^x4?_nG%P`jxHlf>hYSH&z$c>1w-DrCuynv`|qKn*??!WKq}^eO7aZ%>wKyLSa`? z19lnmI$k$n&tJ^D{7Fg66&i} ztMuAy^zzFUgeWiR-s`V3ynE*zpZDJLh4XC9n%+&jc{&LfT(Ns2T+vRyuG{v2390Iy zqxXZyJ{Qos#x4LWt{5gf_92z6`m>5#y7JMN{~sv2DOrD8q4w>qp4K68kg6XGV?RxT z)XQlb_tL^eJNfjS!|u)lJ2{KK>_Ke?Qimb-ft|#5ZM6mx?FEd;%n6GWvAHliY=RJ{wRiuu+nyF&~tDM_1zr3cKql#Pak;q6q-I1Lp~uUN7CG)Qp}DUp70!gHtf4 zzes>*ZZb!&+j~wd*)A4uXJ$8lE6eD9S<7MR;1UbRSgWlUz;i#Z6HwLtw2BSdx}l*4 zS|^zumQy{AcrKT+BzJbB)L|3g9X3Hy2ec=}cUU4B#g{|X{N^IQRm~1h2e!k1LiO+; zp8njWqyR@w$l?^emu2>YK2^Jj0a@bD2de<53;wTlkuh1-O`|`QxxUIs9o}xh z))c}zyfncSYFyBj+Lt>Vo(9sFM#xA#=}c8n9K?xh(1 zuZ`9Dzaok~{9hdr)!Ds}E5BL{N;{Y_ke#GEf1zN8(hgZndB|g9|5GGl#R|h<0XQ-*RZ+;Zf4Z$1JIxT)zIg1>nwDch5RQNZ6{>12te`j_h1<;Q-hw{MFFAa)o5G0bF5n0B)-4>#CaxKlICu+Sw@Z z{D(1)gWD@CC$XL0VDcQA63jMzHjC|~U~cPHc76r8LpoMqI)FQPqoM$Vv#g|cd-rAr zvNOu;P~N?Rk`n03c_tMx_gH3!9I$c7KM})PqAMBhumE&AwX-R}aTa=}$V98-EWq8- z%j{wcq0*gLMs_;2gR^W3kVrBkJ82+zkKhptGq99Q41#();LfuEASh42*!$_8c0rsx z7#nH_E~lp`$UNE%=SyTth)m7&i*=mZ&DlnwdzjjVpP@oA%KR45NSI*IN-;`xkKOyjGXp6@86qPVOOlJN>%y~RQH z4Bs)ga^geZ*!xT-W760ucJ&@HXQP<6SuEKhmh5JCSZT9<=ZJPN;Je?pGNv=y$kq0c zon&`2{c?eI*d%$KT~Qr2Ny(i~>SO`D?yKSIe{*Xiq=928B~{(k3)~M>TCeYxgWKt=QLx@*M__L~?sDWhn<<$Ag zyr|vZ(&yE9S~;wwLR1G=OZdO4)zzu>Ny6K*LjPC%-GWzoyo9TInn!O6L)GEU5TZJD z{;y5?DfWMD*2%k3JEf=&XAeK6ie8Bubyw{FiZm6q0-XAb;^k)Ljlix{sTU-z!xA2` z63p2GJB`z6@J<7EZW^#N7b;cidFYoD&_F&Pp7d3!J zQ`L1X`@yYD;mt=UQ!a>Hr##zp!A8N6yq=K!!nMuDU(`mH>H-D7pY4kRf0LB4_A<_omzawf0?vs<>U zQjOZrI|%H4{PCq$t)Bh%TPxjr{q+}Ld+i0FJpj9wEem0*4(uSa`?m({-mK0AEU^hX zJXYgOkjsZ6aaSX7h0zN?SW&%49YQ(Zj(!$v#Jk5mP4~8o?Tr4fC`)fwa5@xhAf3MYVgG`A!RE;h;L-Th=aj8>|Eg9iPW#k^>&Drholrnl%%Ab4cO^E zuP*Q%R&vCDOeeLvaRwM=PvrPp04XKlcB$%Z1lR=UreQDS+fkPA zRsy@SWnTntD_Qc{vSnYA%&utBXPL^G3cc{c|9#1*utWL9|KUwg-f@Ol06S!-0I=g8 zEC6<#Z`KOzDpbg-u=BHcR4i>lM%hwDAgrrgwwR@4;Y#I-OSPR*VW$H-6f|aM0jkr= zWqf?V>B^u?R1T=Fa^;d0E27Ns4(8)izFM`)ue?&}<(Dn*Hm>C0DRpVHCU_!Ic=M56 z;R09v%)}kKz!%m<*;ZXKWdB62Q+`&VfO7?{LpVg`huWkM)$ZtWZJktM)KPvzXiZ=D zbQIfl8cG8v+DCP|vn=A>0ocJQcG?`8F_!@B;1|1gEdx6!?7-trp0oyzlL|Y~I@BOF z#leGCW;bss!9qeg}P62_pHfc!)Hi@tkuaIa%DhCmyGWbmX6Sn1$uZ zt{y)2(IcK^|6DLQ_`)U_ESJQya`(t&x|CrXHUC%LFLv^BnzWQ{!)aP`lGdIg(7K3J z+7rRRZsk7OaLj5y&n#EF-b+SFolRL+*a67_%t>JP>Z{K~nuldhz^?imrs}Vms=uy< zynEf$8l3Js#&%q2X&Tro6WB4ivjgl*N5V}T7Z=eLcE}k!WjGvUEfWU2A3jh(V26aR z7>kGL> z*}uMg@Q_0|K19zT&fKAIWP&&s&A@KYc^1}%?%|vh$UL#?uvm6TgdP!#_h$k-NbRO= z6%#g!aqAh|<)*FEy<=tJH(&o>J?vsTIK0B`RY!7m3ktnp^L!9OCdsFTu+)JAlk(>} zI7}$dNa32$cCL`xxhTd7a)%UHQ29si=mILPl> zc@rvSz(g0kt0?Fb!bW7L=&mmQ1AfwA3bCo$t~wK(bCL0)TD{U{v#C_6=iu2Y!`Xqj zpbRZYue&a`0j7urZ!K)y`WN^@eM~1%L1&9Gv>E@0vq(Mf%pp14PT=itU?W;)9;b z9ukbBQ?uN9a7l)?K2=Xu5HS*@W!vDxlj9I6OSlflq{ip@5{w z^B?F-a3y_fP6c!rmF9sFfm{w}9-MACQvNRB zj3d$sdd+?wj2OWgt6V&KO-1v~J2s^Vq>LX(B3>8^?p^rn9px zmVkrO>lPwDIgZI0?zd=sJxScYFF@Qb#E6JEwzyPAir6uyLBE|Wt|#!;WG!|g+Q+By zjyF@pp=)B}ak2WK*b|XyJ1&UuGi*S3u+1k$*a@b1FyOqgYptnbWfobv9JXA*j5T_> z*KBJRSwK1%(ox`UgMwX1Co6x*%K;V&rl-d1&^3`0T;a;SAOkEU;~nQ{gD7WLHm+hv(ZK+Gzz^*_MQ8gZ{t5Ah-5jwwQQ=%;a4&xT!;rIe`S z@1+)mp~JfV$_`I*I^aJ?sM(*=rcrONrv0oS!}$7LuyV6L)Ue|djOsvDdimDxt1f{d z)QE89b`e=n(JeTg?)X-%UP%BuIKaYmU`!EXkAkm#QQLw8HMGB$e68Sy1Kd( zXkhirLE+a&Y8mB6?utWoQTo;o2PsB%V1Hi^^=sT!*$=VJ=+yZGgO$4o*kKxfJcwG2 zp}=73F_XH^$PL?doknAp6LeTp!swT^namETPFDUJMzhv4u$!}ina-9&R$vFvBMF++ z99>UhY= z(&-5Hgav>DWxEnXM>WS-q{@V_P6u}2aT3O%8T5CkM*%(91$Ia?8=b(CJ(x~jpjiVo zgU_I>;T!;pfF0ZwA-RGo3e4_a8eP-E@ZrF2Bv2G$^C8@xmP-J^%M$Lhn`9a^ROfj0+W!o`My1RZa*Ef0N|7-C2bFaVd_UfyJGg+JF*81+draE=97(8L6Gpua+ z_S?Mmy?39j_3pE><;NeJzWm(u<(Jy>Bh#0kX}i}j)p^HMrzV#>e`^JHR>xQk*bVGq z8qnD^xU1Xvf##q4yLI};)b+b!J$^Lz_^~jS!+Mx!{_GBs-IxKTB(RfeHGR0V7a&06 zlmD0p3s4n?RlS)U6lfmr-nZKAl#FI+P|11UOB1lf2O{YKGq?+P#mOjf>J}GHM~ibY zV&7G9HdfqyAZ{dxxHNJ5fw=pK?MAXdPFjo(hC2@X0^uccR}UUp85}lYOo9K<7~%6! zxSnXGb>M&!-P%kQ;~W6yd+=B!+!r?!#G}XJ+C6bHTAYs)F%PX^4s6Z{>~>z{`YorJ z)?tbAnxkUTK9=2$US&(=!084q5M}uJfzbrmSA?d&^kE4Hgq79 z=Tqd$S}@X)$6jqcYWb^YVXp9XXck1S_^U4qzIn0{UhM$yJVx_LLTt(pwN@AP6O~6F zVi&mkIG3RK?+)zhb`_1`$*7Lgeb~#VNjGI^10W1!O(Ef`>FuaJY_Uei;Vt1lxzxN<|&{-i<*yFv77w?!B`__PAp6>@jC z`ok6}V)cj31a=weCP3>lfgRFKaMFoFjOwIi0=ptcg`Kyzd*zBHN`uyEz|Ml`btsf| z1G9rmtfsKD1$L6S!L#+1S1P{p3X)D#dgHZbZ*TnI z_QsE*Ha3UFZEq2~{j=NKKZ@JgqF4LZfB4YP%d70x`7O?E@V~LE)!>=`#@y~V{mpGV zn#V=7Oug1RIpX`f=e}u)-v)(~%4J_ATot%F((Xz7W)5R3H2N!VvYe?u&uG5Ctngt9pljB(Q^;Pybwng~ox_8S=$2YCWF1 z&7dw*b7OYBXVJv9G<5?ryGd(UYKJAt6IU~{o4TH6Z>Bk$Xx=7Ttx+EEo$bfj0MsNk z16Bh#lZiCJ)DA|_wsW-oJng(_RoDUBE!{^8!Wq~}W;bseEkD4iH4mO=Z@vqWtl9%M z0*OP;Sj6o@5((KqbatrUb&5YrI>yQ+q;>#y_&Bh+6W6V14#yU-F}@aLvjKRr6bZx`?m^)ZPQn_~R9EV3!!HozKbQo3d2 zGI^H4eYAbOFWq8V2W{QLa2|r4rqs^2-KsoCfS{_#IH4?hK1CcGapqC*d8k&Z1s6ZqeM-x)m$we*FF=R@Hr{ z_4%r)I@EUXldW!Q^QCD>H`9>5Y`Vg3KsQs6KY(44@dG*aM9b!;A3iPCx`nxQi^8p2 zlo-$%{;*{L>_+!3DS=&{;|M(+Ldb#A#sS!A<04bG8pj&cPj12zSs*}N0b8{O-p2EP zGnJ)wpkPqd9lgcMy91HDdLfqa9Mo-*iQ-B;%j=+(1BS!MO5z3)Twm zE3-2KJ6Ya!iM4ebj)}0tV(EUd^q?5GE(@&#svESJQ5}}Lp3bH)WT_SBp;?!yUOJ~k z0$3YtS1S=)`jFHV}hwK6qMZkO@SScs+PZx!pCTkM0BY32o?4Q z*VlSI)VaO}v0Md`x+fQc!1cA8(v*cDKFFM_`u$v0hj`0vl51XCbGhe>7YBC(vwN25 zo5fVP@Uw*qJ@edi{~N##G3>HriPdA+X*xSiod>0;r~3e6o_ zGNfZQpQ)5ABZeIcWzdW>#`^iXOGsz4tX7TlNWA$bRe!_sny_^K+Uv&Z_oBjT+}+YB zF1-1z@a9qB&Exj8fZf{OJbKTEaeG_D9sJ_*%7Dc|4cdSH>gRPmzN%AoT*n%hHhvKw z(UMV}W^rd)#-C}KdZy*A?JXO(Rhp~B1S^64eN{d%*d3rh64>EE-0m?rS*Zg%d5yOk&F*P-U1F`3zf!MY z`fV%C+)BS~VN?fy*zqfQH8?!ekWO^P9xL|&(~<76Ks-|QCOuVU`Pg;Zae)o-(bgZK zjmMc8E#FToHD^rtW5NLJ(D{zDbnzygyh1VYRw@Tbhd6Xdi@Eot)$11YO)@)t779{l zhXk^y-*lXrAU@?tB%ucT0z64aS{#q=i)zF%Hsw4uSvKu+T1`x0uU^>oI}8ee?e1oH z!rA~ka6Jg|4EhKcdnX54BH<6o%3b8ZWpX)!oDCxf)vJ9(kepmyI`x2j}hMBCD#K;PQ7rM>Mi;7J^AV!$UHv*>OGWWYg)&#V&BgRs*neauAQ~OR?S9fjkLOwOd zg42Q4@dE(6z<7l$4#4hVHU)OjxM2*b9k?6M?DF1HOYEz6wD?nro4~BRj1IsKV_XTC zkBb3g%)vlaT#2)+dJRC$m-C8px@5FUl(V^z>ETIp+4y|+d>n05cQA;(6kRB_`}a0` zG=#l;htfJUw&M%a)|@D$azJ%U4i%z0+=RRiYgB0+Rszx$tND9P_)i0k+@5 zPwLBt4s>4&wou1@Lck?F0gafi&!u{OB?g?wo6_C?mQ)p3V zH@B4bULW^q&T~4Dg)bM?xfxOSt5AOmfTRu=^Ykwl)xr9TdhglP?^u%qc2p~eLI4Fg zohq=aQflZ#abO3ptFrje2_D$hYS!%MI&~Hvrj1UL940gy39TazR>blA8 zod>nH?byie*S6bdnlGEsIV*T}<{ih3I3=(flo>N9FWT|;9>d1nyU&Ibo1Fh>dQ?Tz0~24xZS3&a zYY|i#m%s_`!s6)mLEm7NMO}ckHk6zXCs(3rhAeN1y%I}1wOx)SArC1x#7eZf1GD2B z-hqp~S7G;FO;!P^Q6ifTa=B_5^Ape-FAeV33>tZX=Pe7^!4eC39f+Qa6SGjar37|h zb|{b+RW9>^W6CR|9#FX)4#=h36xf}-Nhux=?Dn52>{y4^b?9M-a|&9g1a{ZLn$8?l zd0ZC}59~@ZIFLJ`fL;3*;<3XNz;5|W1+ZJ@W>?Ej?^9YlopS6Sjf|*!T9UvT-5i6aKr{ z4(cr%0i8gKpFbw*klLw3urJu}HKBg@mAxHE?acqfUG2;#=&M(1#~*p#En5L{_c6fE zat5pJvYNqm6SSbG1I_6I5mlksmf_GtIEaY$9NFgINiUw7<-PoMEy74U`b1W`g=5#aJ*-hgM&>o`tQRvkm$$ zkq%f^p|bc*R~6X({Ig~;Usr$<>q+>^dWv5OCmX@rI4Qpd4Q7;VSej zXiuXCA1F=GhPzhK!hYhf3l(}%O5I!q27P8rdrlX)@Fy<8ceU?A{l1Hh(PK{7>aoyh z@KWKcC-UO)N1)GAmQLie6}}`0`$^-;Kgf_`I@A1l?+aY9%^V~_nPDu z?v;UDZp7D1xlxwfD5Icq3)U+qe8_+TJ1G8;!+SfP_(l+V+#XO$#REGml@Sx5b^JkA z1$HY(<02zXzLBAFylNvRa3hVkZT#W0uckxRlsxOI);M)3q0s0CuDL6#;hF zFY3zW(hseySJL}wv`Rkk#XD(ywxSZVqzReQw3o_hh2ZRdu0mMHXHZZ}>_hhU6ZU>O zO^pDj1G9^T`0fdtkV?DDsdBnv^zDt@DD$n+vwWkxcSdjJv|h`N^5jPEa6P;+%715s z31yx7?AJyw-x~XkjpCm(l#aih-LK9n9N9*Ua5*gq!>{EHR$FlirJ( zxm__I1Jw9+21DHSY5yrf%VNq`<2p1SPmm%iY}w8=w4G-pY;~I_?XkcBlp$Uv}R@fmF^2%FYHx{ts(4{wx25-YYKT6K4HpEsQ3_IHhIfY5)w;Z;64U2V3zPy3)8t4UHN#Ag`-dzdpouA{6kP zw)XcJXjT0dSX1Xmjd}&Bj!xz{9R_vl z(u4|XiLEQf`)*Bkb2a z&I6l)`O%cw<%W)W>f@Gj_eRbU=ZuM>B~Eodp4lN~ScqVwVjB@0DwVpy{!5JW|4;wr z=H_&V^`qoF3$YbKAJy*aN$nUg+t>jOUInfGbBszoU!SrPv`+BkR%{TE=u`HpoX39r zTT%_qbOCn;4PMK6)gN~kQew+FxidUjnHQ}^I~4=|rgFLEB)j(_yZb!5;|zQ7D(e$a zP&cWu?)F1A>IoYfPb(&g*1hL+`5>K*OQG&RKnhIYn`DJNRW$a(O~3o>r5N_=9mQQ2 z;@Hz@dw+-Ziy_euN!&wn{|W762gU@_1lAUg*op$p7eEq#9i(~x+#u(|iU2z(_>Kh; zDD@z_1F(Z;3h}kP$POEX(yeI2xYC&&I@zgmx02&LImzUdSsXV}1$Mv4<@$0tpc`Kp z)3{hD`*I81msxU>{*JmVpA;zmV96}?88I_MrE=f5S0(oo>M(u_FXZG|Ha(cR`ShjT zyhmbEsCPKv0rB*qDI(}32vc*Zg=i6EQ{b+(?zJ5|Rvv31YTx|lx2*}STDG5A%%o4{whc&+4o*jHgDC1_zNEIqqESoUMD-xDY{1PIIGBdMidF$?r2X5+dCw*Xi_HH+h5lCM%1P^~(H=@VOjfSMqcC~El7j!T!B$H9(fTPXkxKcsxDKtR5L={9 z3H|LwEgJ+16#7i5Yl9WgRek6ovYz(e6>kC{Lc6N23l-PZ0<}Xh#g&HVD)C;c5U_)u z&StLK_tIX*_7e`(AxEN%3u`$|Fj@y?86;+<`~WNYldNKE(1c4+RHtv{VhmxOP&iER z*NeOh0j<8~kkkok^%d~Us%2BaYLGh#_=64LPf%2+n$!Amzs<^el!Lor#ZjGdX0z5c zfaJj7YS$M3^b_TDN?=#@J-|-03}8o7Vw*J4sa-=;hvvyvL*F{gEC<*%2e4xsL0;Fo zbu%=r(@x2%9sc{j)ZJE_5)Zd*O~bH%5e?-+lAknG9zU zfL(@@XqXfGF_#Ul4WYlBfh*xJ3T2&TVb|Y?x%_|1>+&}oURuiMzzpHP@AjA{{2}c# z-(cV@6Jb#uw$^u^@Ca<+62o$U&d1#~e7Q-Vg@Q$jm%OmM;g^qqpX6W6rn)-|e<|YV z4P9nj&UqZSmYugv!R(grW8J-3ESK$7l>0u`>nywfGJEuT0jlGfT@ZUYjt%BeUF2g0 zq`Q+!@dRicR7EfYg4_Yyp>9BJl*$G_VQ)WWqn@%6V0@3*>+uR&7yOV8uHC1=?rgLH zjEA?-y6y={PA74RG-#D)c2V~Wm0xE=$iKm~(2fmYj&GI+`~+Y}TeAgFJ1tNh42(#k zzy#>;BJPqq4@qLmdte-Z$a@ubueru&1r6`aR1|jRa=GfwHvHDI@@ZBq-!6^EaA0;1 zIba7allQI=%3!{-FjJM;eFU)M_mh8KRbYpwc#@?6c0e|K^hZfrhj3WRMz7Pt`1Ex$ zty`s07T;K#RxZilKX~$}`S8UjTN(`OX37s8sGm0D#)r0dzjd7*~Jxyxd z8q`}tA3CHGLmKu4%HKZEF5yD80*4X$s46xH#tI$W=efG@X@kEL6ugk~fSsi)wZ5u* zw|dfUk%D!U>pPF_HJk0?Dj25&W&*k@<)>H0s9f;}PNkla^84kN{C;1~njZ5Ekb(+S zCm^R2@b&U3x7eUiy9YfTwua3XKd>KnI==izftv{1K@Wd?0ICz{g(Ei;*Kyx2Mt=-)aHR5$J8{mw5S1=SJ-_Juxr?m6U|9z_c}ejT6ODIuT{MY z5A1kahb6LN;e@4wI@Ki&VVG4Y>`Y9WvK+8u*(NO87~~FuJ77DwVzKPhNosA)T3CSA z>04MBnwc5@@=MdUEhN9R)V=QAEA{HY(7mG;kL=(w*mTBF)BUpUX&2{Z`VEdh*Z=yq zk$xL{WCaaI7)4&la7y>k>h2aW-{9PvLpHc}`D?)#9@r&1X&m{p+xWRHKMWASvsv&z zgir&!3UnSU=SujXyB6*m{eMgAqVIki!)OGsT{F_b=yo) zU0JpN3Z->N{Mo~PiaHv=UW{f#64>ac6wIM`F%jRcD;-U^-q2i2m;nLoz~gQ`rZJjP zDbyzz{+I$gtmF#_FYFN;0)&^q?z?Uf^nlz;CHIra(=;lwLvF*PXCxt+L_Z+6(4~%R ztoCmZrF96efVC6d?)ZFIWKCR(RscIpgBg|wdGOVSClK&H0J{fI$lJFhIfLXwyY!}z zjwvx&MF!v|C@>TbnxKcBs>n`tY^mHDy4q1!>?@M_mOOt$3!Tw`>ayO@dikz!Pau_o zgbD=_Uv_~+4njEqJ4}1=h88u_zNGRi<@Wgjf3I?h?L!R6?`<{`X){S~K_V^o{B{z# z7)9$sBuxSV-$dcKo%Ex1Hi$wx6x!Ya*zq`T{T!`@eMO7&EozrP05?xPM=9{~qH0z3IyCU9`Jf zYIV1*)~g3q*i9NLo-|tQGEwY0nwc?1W7atF^pVvj4P~Z|(B!3d(?^QkoNCUQpfhK} zk8VynJ2x~e3BoGA2AB-}<4Vp4l2aBM$)N!3UcO^9U$IGfY)m$-xD0xE^W~So9*bms zBG^lDRB)GzoK|kLh;4nI!=_}hX)ox*l~2TL^e`uj>sW`~K<_!kNiv> z9?cyWEL$3|oBBC89jvbca=L*s7VcM}s1Uy$0l5|Z@rGQ>*}{FDT|l)e!oFT%t*>~= z!ZxadI&F_-c4xc5TxL4>Z@WUG;lV2S_n>UtdB<9SlrNBt}M(=FMqJtf^@eQ&T-|K=~aC z9qu}HBCVS1vPN~6O>Vbgx+VJ7E&T15gY#{ZPLD@`Y}&;!{@b0-{@nusvbg~c`8ORi z0|#N8&lLul0li<`a#%6Z{Grpg&zwYo^Gz@B=B!% z-=rVEoxSHtV%F)=THhqXLlWvCiR6}{D{2S1>x8=N_%Eq1F>}^6qt^E(9Vbalo%Bqd z2$pTfN?6CXgGTjQAiT_~1w2Wtb zy~um<(OI0qNk1-FcSv_GP`Vuot4(8~3Rh z7Dx`j4rW*|J1Qy4r>*XxKY~!`*=rWEJ08qYgR|BGrOQWR6qwdhpY)Su)|8kDZmB`{Dcfy+{IT+ zJPCh5DIR=+nOP)1pVrHlh15fdKi)e4I#HR^S_RmF(4kbBT?@IqZNZw(poAG&Ef?~xc zCe<*IN~?n9siwqQT2^&*WDpAr4=e^KTifCZ6vg)Xj%-G@8>}|#NX3W4Y zjDx+{p;xs5)*5}Rs`l+Bp89Kz-+F6|>WAnF@n4I@SZ-YY^ZKPa?^Gz55{Ic{07+d2 zuIfxSG?gmpPyw7ol)soS-DMAjvIm3NEBDz)d4*u^)mt_>lTFH?%nlS!b;m&Kp5?P? zuN1ai@IhcftOUFR&f`OVm8TCU9aqa66~c?Vczg^w9^M5nUXu7!N~rP1K?_A(_5l%8 zUI$vY=R%2Rw%{mY{V!v*RfqZt{h@u%^%eTmFC9 zP}!*Ahq~o7>6ghqO74~54h(gCM^bF_^N687{aHYN%D?J9>fZlyya}#b7=+bvREuvD zp9X69Kqy9sivT+d7tZ?n>CY;X%SEDcuU_ct1opo|4k(=e6|6~E|0`N23#&u=!u_w7 z&V~Z=I>0*4|N5WN)xk0a|0{ZkRgkf~cN%nc0{UN_N!RIW>2Y7|_Zku=hOad#2I4;S zCa4-;TwrW9vG}`^)DG;fT2)wPe^5buJhjtMrFQk|(Y|&(sjKy)24W|QQ#)wyptJ+f z`>AT}0%}Ky#JG^!)#lh8@Lg>KgSsGiV0T!yZcV`*mMvOH+Oz@3Bgm-0`j#yvR-MQg zd!rR&+6QgymE}7c)b8RM+d~VwKl-wS`_g_)aUlt&{A#)9mtTl<<#34H`HWskXlf-$FsP#?4JtW|D zQ67@8)%8Q&Ylp6=3uG6#_@@Awc-44QVmVP_Fm$0XX3ne;H(Y5 z&G>WT$k{uu-HfXU!8>+&cae?!uXCNyX6XKlrNfq+lrO#dh zLSUEnhEh8e)JVW|Kz68`kkdUaq;?oXoCL`o`rQHjf&2O0GeUbhB*Y>u_E9P& zY-m6S!4Rym&O(navI2q$1iISpl10{q(Bml;$)UL=F1O~hX$PR_r z37*+=1CZk0xN{if3|+5mKdQ@e>y3To%nM|;X3ZP%f-T!z(L=2+v; zG1WIsrnPQjjb#&bHY};Gq;_FJh=*041@#Wfp?25qvZo{1^LN;whiv3?`YurJRsK85 z>p<KZ_UQ{Yn|mRc1N5E4iTvKkO zp||7nRN#eu#F=7o!QW1#?;23M+jq(3P^vvW>_@@fzDs29MOv!VZq?yJ6kPd-kVHwwcFT&lXY%2N@i4P>cLD z`UDjbAJ!^hgq_`7gRZj-g;m0R$y>8_HOA{L>nF+wuSbN&Oah;cP(N&J#v52qkP3JJ zg}wZ2!RhdypMabW)DNic6W$S}J{B3UAABl5P51LDPiJKLGawx&hNDV5U_0IstEE+0 zDYK)%PLuYyD*^17I(4ckWp)5|HEU8`9jYcobzX z0K2+`=-?(-=!RFDkor<-eJqWQ8vxIN>p^}85gx`yM)gq`psd^JZk%bCb#6%RrT!Tg z>{n0ebYZPs+Qk7W7y74O8u;AT;hAr5fV})`4*9c~nrClAE9XK>6 zgxY6wZw)ve%CZm{*BxXHjc9DNI30)%z zUDGghtt9yOdXU!L;>z1q&%$nq#B7Shj7sYoSwoIKr-}7UiP>~LGxWiouGe9Tq~*va zEk_#LuVQ^NdXH`n&>-Jg;ZATRD#c6{q^^Xyjswfw*y?=3slyZ-K2>NQ+)rRDfv zIyiRgFrr)MQ9U}1w8PlMel~0GZfJ*|(=wwTOEK2(x)5UwP#8GP-*fQPtpoqw^ga<3 z5*e=qc@L8wbemSt?-bH%HDp2zT4D5|XZD?I6_!oLL#cY(k($u#G zf50nw5vpsAdL*8LpVo3V*n9p506U<)Q7hRwTiM0CXdFc`;oY_~3TB7ac1Qg6Pu`$S z?NNN=IQUcVq|&;dMiMe9kOGRrAT)_we?T>G=OZXn17-7#DJ+0C-joR#hYoc|FOe&^ z$esHnG?c`}k(d|~5kVe2Ade745l_OR$*o9oJ&b}R-obhGCUyQ@LoNk3IPFVL`I5sI zsG;-fZ4w+yf)PX!O+v!R)f*)C9!X6lIhjNbp%OH9P^uMGEWHCzgOVwmyQ{>#Mfn#Z zLko#a!5HDO-Q;q+QtOS}l=nfx`-m8Wm<&bT&0(LuqYcxZ!4~;eVTHtVK{y3j62q@4i6D=2;oPhZlrb~S<1*xix(#_A(31+q?DY$12agJ&c>kp`b2^(4JO z2Z@U+FONKaOddWYDJk^oLg6~ZjzJ*@Eh!o{;)L>u4M-QKCie}&8BcpbO|FQM;B2o3 z`EnCjW^wWGR}sHI(6fT;VGTkuvR<)Ia%fAvsAu#7OUPhD6WI&V?BQFicR*q7y2^U_ z(%N*MU3r2Xw~2M~EK=h=ji-3(yKEuTHUdiL|k!zsEzyTCg zTwpa(;L{z+hO~1N@}2ULSICFpKg)5tTv{wsy*hv$3eZgpsE)^W zKy?_4Id^Pzj=mK{#nQ<@b&%BIA$99gN34#HCWU$&s#Be;SXmH3K}B|4)vqgVs#l98 z5+ef&<9HIsF*_dHHPF@7Y1|kT4+yUwmWGBD&lwm{qQ{H!Fj=4In6_>_)ZQ@Tw0*|8 zz8Ss_XV!I1I&J^#e7|SD4oPSGV4QwwV79LVP#wi~z78*Z2VjtWwLh?34)k|e-uxAv z?y^FMKxgN)(xj}z?&@Z^V+MPCX{!Ja(*TbaH$B*hjckl3igG$s^u}gz!76H1J#f04 z?)9Ls3tFtRYnpyD&Ik)sN6XAfw>{K)GDTU>zmF<)T9+yMHj|CIOg3uKe-!r3q}Otu zzF~}kNiUE%cWHNuWCX)^mCBz~Q@t)m6{H0wTF&kyQBhzyiSF-Tyg^rLZ2d=5(t`7xvyc}=JIvPPy z(k8q6q9TIA`afK%+%ZN#F+lgN7{I)+>iCHE6adOFEh(wq`V4sGsud11%}G{|Y|Z0$_)MlGec`ToTxU zRVgRG7uYGELOFnlFI`T2qsb|xO@xx;L5efQpRYKM)&bb<*xGn*Ptm--BG(?G->gN0 z%tT$=X~Gl>W~Z;OfdR`FZ)DfMzZMLy$cBaamBzz5VGh%}g@&!ExT~pFS5szmKh2e{ z;y+w9|8QkCxM^*kqrGr2Gsl5hHk|QtX7)^CeB79e3p6h;)w;Uq`-oNF1+Ub)ze_X8 zhe&;s)E^pi}9xzv9Xb93P&vOi@k{p_&()sIPBnb0CtEx0jhpoz=E+qSy|-nU3y6a z+u;hth5h_F$$v?kG~)W?Q;~5_F%!zi+*h zHnWpFrC@eSFgJfMK`C!yFk?Fz_6ev?^*@O2GT}kM*OVfQ|I5_zzl_klLyc@jh_5%r z{`dSJvr`AQ!!|9U^!h9|R>7|~H3B>6>0q=K5Y<6XCm<_8T3x(_4Eo=gWfX7Wu zo9Z&t5Gq2||Fk}Fi~zl^5QPauuOO+jm@HTz2aX{c{Rw#a(O+d>w)M5x??wS*jX&`^ zsrbX~W;Lxy04|724 z6u^#C(qRpXLb_Vo3XvTL?11^2=+#0aI|JInjsiQW6a&yY3hbITZ6J{V*y%KCgsP2Y zlO`0~q1hcb0n0kp?Hby4XcBv1RL1E(sep8+d#9Z4lYFxGljHV}Pxeka-6!#M@5iU@ zpP%iMfdbJx4Pa;g!nY3vc9;87VCPp+U^i&K{;s(#{#j&lenoS?waxw4umK(hA?r<| zJPpJDBw-#+Vm6TIKRIBxN)ozSa&tw!fMq&?OLYR=YxnN&An81*QRnfJ4xn|vNlYCj zreh>+e`{h1@AM+lYcc6Qx2gGby>{b?>2Hl&j?iuXo3Y(;wx>JWMaG)T+Vq}M!4rx` zpk-l>>5Ip$T)TSHrp=4jx(%LVR_xNn&F0;Q829+qxa(M{^<36|nY8=j_W?T!=E??k zznk=0jR|0R#&(Nz4f^uHu5Isj{8OVFo`Cwn1-faP&1`NKnKJFo&sRU7e{O4@MVspy52@O}OoE!@y z-T~xLP!Sa*aw&ome~EZzg}NGU>S!%;?I~LBAX+d$`{Vg~-}y_F`1f}yTbb;`m9tM(etB6d^L+J;nEL$d!b^Z-l&6^~#gC7^$_b)p4FM8B zT~MoGz{4+elY?|F^cfqF#D=7^p&1HKD^3}(9f%me1pvF?r-i^SIF(I(X_E2Ah^M6q z9J70X){A)xUIV8?i12NU{(Od47nn7{_zX9MrDzA@~XF!o3w zyZaiu?Gn50JiF@#yEm9U8piJRr?u!HJ85entplW+yp8g^@qg37l}znROYb zFRb+y{rCjV#ORoMC8HtnkVEp8v!q#_ijq3fhG^*IIN~!vnJ(h zJfth4uu}j#>Tmt&C#tZc42~;wva4CMN;L@aw7=)^9KYn%cKlcY>_F~*s>(kKz)nxE zhLkik;B2rE&~ZFYr&+T?U_BOcwx9y>YR^8i4CiOFX zaC}_K3Hu};`=`h4lTO+{Jl-?jr$^#Hz2Z;W#~-s#@Uc(&*FGIOyOTXK{_T-}*8atL z`;4>p*%$lfUv|j3(hu$Ja{LicF)S-+2-J3^D(sx+8y=Ol@sYJSE@RIvG4^$5eSeqw zuOz{1N%&tR)I$%dx^RzXA!{4ma&Hji-T=$MCAF{1YVC7tW@ z=y=)&cHyof*lyJ!_DBGGJlODT1UVB%&V*86hk~aS+zm@;jv%;0rUFEEAQ!RE$*sqf z*##!h3QAPKJJ325B3>cQ1Lg~VNLe0&N&me{f+I-EGZGw05|gONE;^dJVgc-;qR90d z)Z7X&9SRukN|+))_8N90lw7(_ao#_^6xdN$Y$yqfrI0Q>j!r{%1=TU75jpIlh*~X? zwu+W^uqy)C@j7h zyr<~VmhZCneEaHLCHXDU+aL{jfcRZPZT_VJ*ddSiVJfW>zzztk5OU#Ufpg*D^Hmbq zfz~O3o$}1`gM^n9u+mCl6LZ-#4*XHu>|5iER|eqJQMh6=>6Onb>=5f3pH9-!Nn{MY zhM}-~@`PeLXzre8Q%(nFhjWVai`~X#ycWC==p~agDYFB=!*-S;?vhu`iH)d{`c8yF;rW3Aret86xhK7yXzXe3CEJLI-~5?8Q#q!A{@By6k4%yxG|Y*aZiqJg@_~bKNGGs;<=kbb837g3D^G zXBk^eGWaS83&J^tPmu+)Lr&&bp|-<*qF9}W>XckSn1OKm zK?Uyv`%|;8>g#G+SMri$ZEJ|d--6lw{4-EpWi72rKy1Z(*i}OhyFwVp(K>BRtIoBh z!&0^39md*4$z3f@WCz(D8r#*ZUKPFV5F0CLK#jB#$Uu?_XIz3pW?z)IiK#q^%HZTx=QcpuyN!?y9>w)YO%#rxRb zJ!BVyG1g$ck8P5VZHAA1#))1T7#!=$tL?DNIo}T^*=*l_87NL|R}9z55oG9BLqI;))8>A?z0 zJ+Pdrt~p5DP{25pj*&xc{r-~XgBo`jPwW;EYd3<-*jB#*;qi0y+D{=CGS)`Mn$0Dq z)AUW9SyN}D){}b9Q>eWrEgiIO-MU@7b{#u*?ELxj0RaKAv9V8|K2^QiyEz-W%yC`3 zbmj6DtH&)EjJs?m>ep`8;U|f$4(VNwIOq_EI?YFLdpJqb!MT|gghnz$g;94Yhu(^` zr6abuNqHQK*VggaPPq~Ta6OcAO_NSDl(f#w)X>qE8Qnw6$zJSaui@OEaTz3ba}>J` zW@b5R$i|3ePTDdjX4yo|)z0Eo&YEkSne}d3o^!>X^Qw5t7%zA6Zg;VlJLA1VF zAWjOvl6ihhDB<<5j2H~GG0u+B%#RW0#As%OGZ~RubbWZ0j8G;oMvV0sWQ1zCt!p%J zaWjy8!N0BA&HXxPU9Y*MugnnL43nNeD)~WFY)x4JTJ;JSZ2a{DyZ#FeKa|rIo4;rm zyWG17w)^`OyZ0(>b_SFKNXIifeo0M&=zRZ>8UvxQi+)CKKB8>y+I?~w-M%2EL$WKP zUpeI+fL(MF72E~#iLZCawVULmFQs+&5~++1j04gnUStQE5=3@NUNFF81KKH%+aV`D`fO7$R;rKv?R>BfX zl%7_ebCm>k8E;sq=VEf$z+_76N(FY%vZ1|QTqc{(SyOQn=LEux|xe-RMVdzF7ufq})7)TC#3xyp}ED8e2+yWsd`quGkJ8TLHJ4E~9 zHDYUz(kYb08tx@i*lalAg(#;2@PjP0!By@do~yJ1g7pq% z4@a=4W7v)7*#-O9iGM2y9Uz^uT=!5;hnjPcmHDu957F_w!zQawlCkSP$A2Y|+NtBW zI!%!sr(SF!6ca=XHo?CJ*s3AlLmj7yZWFsIG_ynA>DO}hg}RFVPqqVcN8PetDjgO8 z*xAfwEnHc1;c{99_a4IfDqv1)_c>AoEP!4K$cDb;P1(#a>ORMy=X}9hl6Q9&Qc`C# zP9N4+bKy`O(j;Jg6|zqvL~sI1>Nqq>fgNOtYNmDgRaxwJ=2v&qYJ#q|HqDfPr?qNT zQK>1hnlyBx7_g&4Jg)Oywf6!$%;XEv6Ud{$xJDtc`w{L}9@y#W@xZRW3b3oi8E+}D zt5>fEtN*jtoVJ0UmVut;!CTCOH{1AoTE^_Qx#Mk%irs4)v(q+qk1bGLYsdR;FuN0s%LQW zEa~>Ctk-m7uj%^UvkZOa8~Ds6$L2LYzp$yVd$Y^#5`T9|$XZFz3Q52covVv<0v7A| zEv$9OtzP?XBX!LtQ%PNO#|A)kEgb8zgLEj58w4t+L+l&09o?+EjPzVay3A{8HoXZV zVp{>{DS#cIUegXUSTp>E$BmdelQzzD*?gQqtI-xCJ7W)*uUWEb)20Il4xBxE_UhHE zVPRp3iHVONKUQAwqg_T!pS58AqGjXe4eR6D7VGs|x6_eW)&i~5>(M|uP{IzWXEIdM zazx|y6PlPgH-f^B19tc-4Q!`c(E6dcH9W8b%aa*)U4oTZa_f1zM>$==WI9Hv0=tf_ z42O4NMt9fXf!#QJ#@RvaI!HX#fpHtGF>i!eHcDGInprYV)7@FT!dY{LGvnc^wcbs< z!L5qt9L8&**lW4iYaz4KUBi2=_@IaOVGriSI?dCb;?tY3WG-ye^4%r&-Brcko4IkA z0=r-zChQmmb}^^5V@@;oE@?izCI+^9?8l_upujF8lw!54Sn-QE@v}H4HI4&z_bG$R z0IG`@KfkM)62qj$attnp0y_@UiBrOv)Y}}ei_+#G9k`zOd4y(4D3cbZl@YGkGB*&X z1mo#6p4}2B_=_(e{dtd9!=)a2LVaO{UQhLk{Q}25XE$rtr4g!CYjWnyKVKrSL-w)I zzaKvH20tCxf!{f;EduP&-fsO#>W4+Yv#Ymha0DP-@O?T^0y}tP(b6t5nXG3PdUkWx3r5*)3 zd_jkWz+4n`So|79*{0`GGL@W8D1gXe_lYqXHA8 zaakRJ@DLbs=zOEztwnuWiw3p(p?_;;aA(n&E}}`@M2iN97W5VkwfLb$3$d}Wrhx&2 zCC1jjX!JE>ezn#Z*PR*LQ#7Hc2&%eqT}4xRiDvZ`$s9z>hl@PMh&;!OwoRtB*I9I7 zxybKNQRq5R;!aVDx9I6!QSv^~v;CrsBb6TQ{o&ER?=y~ya{dv$Ju6Dt_g!*u9ndkn zH1I5R{lh`Xcu7(rSi@n-q|2COTB&&oNtzPaMWwTR-Opkl=CFw`D6osnq?TA7*xk)# zAHSe?#b>e!S#0tv>RAS%LlOmwM|FIkJKTK1VLL3}yfJzAjwXjdjYBrZs&hJ7X^kLN{$EU4qR4(m_&(nzf&uxsQtM79L~W4zLbuSol@#WI}p_%Ky&@ z&n5WJemG?R(*d#&uYE;QJE6lUy38b&lfMpP`+vgP1bkA9$p%)g!oPn%{R83SfnAsR zH1$D+DR};9dCUVkkbi% ziY+G@S&ugm%=)Tw=(K~E4o&N7gUa#b zP6_Pl*RKixEKnT}>@bEmcHQ(=*EgC4c$z_H7eS?VW>GuLWA@la?CKKgWgYHi6X|6e zyv-_fn_1*eYw)|+9oF}DS|@nhK8EIQkJQ6^l&raKXx`uFx=wIdN~_%NxqYDcqz6&K+mNtOM zF2KEkuT1B%tmb8z&PHb=NvBB?0JvYqHfS}XHan<3I|L-IwzR(v=_@fF(Bzk~O>Ab9 z9*arOrKJ0UrZ%(n%%(Q_Wo&)JUnRsrVx&;v>9w0=WH#HVlN)IZR5y{doIqG7TFsq0 z_qSiS!E^KG%|1Rpmo8npdGls;bTs63v2k&DH}!V18b5W)G`IQAvz&WQXkjs2YGT=3 ztkbS$eKQ?BTb-uefD1V?IJ_Pde=VuPPOr1G#0(0%8N?O>FgP}`M0!OLY**qYP#ut* zDzKw+Jf72W14{Q)fnA4IhJ!7b-)xyNJsBrE#tFb~fEdiqc_1@=kOri7a~-wkk6;#! z(R806cAu!}K9N~HRqGEo@gHtgHo7reWnwRxc)N_*u~@^~UF^M5d;em~N}s^Ytg3D_^bbwnJnp9wyq5qVMp?Cze|jQ15M`eMmEy{`25$_*T=0H`)6U}`T4uZrhm!VFv{sb>%i$SfZ7g)zWcBTqKDnx zbaJ!61A9Gz_##`i2W^%ceN_CaX z6_x+0(~H+I$*SI~I+iMd9on1$)nWH2uuG>52->T(T*{=d5|ygL8Ts8Px}<8XidSkb z#XXmSdy;5H=X81^w?o~3Wq0Sb-LP&NU0G4zRzEnj`QFh|G|W;o(o(eISJ9IGqUHTX zW4l&njjI|Erj=n83zq58=7)YAzH>DHVKh9jmK4=Z?ID_JFLJi0@;U%J*#Nr4!#x~D zzYi683={dT5?x;}y17vly|q&Oo^K!S{Wfv`5BK&}O*}v=a;Nt7KQ!-new)8v^a?od zbS+i&9ItuRrYrx~wsw&Et1FA+kLPlbB`g8W&->q>e{K_SF+81pM3 z%mgBQ`Of%ho<78P@I*g&N}}+(?iQMw{o`nA?bT=kck{ntWZNYo^CAm(kR`jxoSpQ& zjGlDpOb65JyghUvj{PT_mQNdRVNr2?A-0a(JONeF&9A(vK=ms#pY zx=;HYzrKo*HFews5d64bTp#qPesc|*vT(&JL~wAkg1+&g6Ih9{SggQ#{2ac?9mX)m z)~ZE~vGC7g$v3>?o8N)@Y2txJX&s+L@l!z$JEX))BsI{n4uKK`MH+(E@u&`Kl+3Q! zpiZ6Yz;;s{3<5S-1cKRZHv^uF^0Eo_0>85a!VC4Xy5(thbBh%n|7{Vv#VpFJON^Io zjF%be9v#@w@h%AQy2c!|gB&l$$1cXlK4fRNvwwCTJ-dbSlV`N6)YI9-)0u3YF5T@$ zylFK#lYzR;2NT#u?cEKq>Po6zbPe@F>dhNzYCub*Tw@K4o9VS_I z8AGI z4kCDTNx@iZyNHD)mP2(|Vl1`g5-Mz$VO%iT-Pm?H#>mi6vBa{Dwqi%ysxZcm>8U-* zUQ9)H{g`PEVmF6sZVt?B$11Y1Fve=ioEQMRRnFp-&Q(2}nGG|vJ!gwOXED?myGXoi zc{Q(PjQ8(Ve5f&2(?_YZ^Q?Mi3*)<6JHT5ES8RbXwpyqU6Mno(>}m1cbDH-qFbTez zN&ey_zpCJ9X~Ei@&JKb&j@eO*t2isJdVYdV&Z8P2aY+%HNx*ZrtI~0lHXUO?3xjIh z02B{nO>T^(IOzu6a#Aor9z}IrVNeaqzIfm2cQ5G#pU}CuxBiLkdI$cLZd%N)a%;ZC zxznsM7DFet{-Ag67dW}++-7#J%~T;BkLudAX=-Xp4jkB@mzP&Tg&mhBU-Xycr+fh@ z_pzT7*x{_H<1ame|7S6CJN>B-VCU{lHlHHrZc)$%E`Wk77GvfJCcYNl68nG&E|0kA_` zG2DxK`-bv6kQY_`os#v)xwMLRNbux3juoyifI(<9md>TSzI;WKK;OyR6h(4<g!z?iXO+g6NStM7(bRvu-G zzr=h4VV#Se$PGR3l+{NxtG8%oZ@OkqKhgYtqNRf?%N&?}3z*~X%+-xd&=yT!53%2R zaj2I#a+g-DH*;@4lY5bg-NBq$%mn>jHOu>l)Xk!t0)U0x2Fya`zGd&{QD*m)19k|0 z&3vT{j!+yZU^qw!FC#C7>%EAsi$KguU>BB5;&aHo=OmU3Z{?39FdwCLXqLy}Ilw!< zSKzC6^jsj?Re8d^-sAQTHVDyW3R+fr|OM|EGQli?4XPTouENd?MdbB6 zoMW9sc;0?w##Z{Cddd@kh=ZP$|?XuD3eHh7Hr2Og3tOsE%ln0u^$HPENA`?)qOmv1gy20VE|pL zU?(2EX@ry10o7TIC+5O+Nw9DtHiDu$fUqwa)j?8+XxdUKn@}rzsNbWCrn!P=U6uT9 z^m)7q17;gLxUn63>w>uPkggc8!%d#SX=|gm-M5&gEDqSwo23FfkiKd~fgS(A0$`_! z&iy$Z`_h7jmlsJu>rk7gnRMtj zQqq2$Zo4tM?M5s7twxlK)s^HmfW;Si^){T>)b(6sfODx>q)A>4&!dU=j=al{P5vZr%qkGcI`$;Xk>6m_{@1r$4+wT zGof=gCqveXZD>GIof6p9qRh^=ezRT@FkOFyAEyG;VQsx9PMPsOyZ2OE)LaTjGaBW zip)_g8=)y1$t-cwbe|+%F|q3J&deVGc5dSJZVZ?mM0Q>at9i+oUCXN+SS>!hTJwkp zbNo-v80%RT#@LJ7v@h>~&W^dhj|to_4mnyaYELMM6F&;Ani$GFrD8g9;w>gIhyuH(A=MHC zm`6br*gd|fiSgq=&7>glqrj>dKMmAIzr2S548|Bdy>M8Yf%7L+Q!zNAY zwrpwS(E_Z{2)6xh^{*vV7P;&S588s>v&g76Bx{pCR2k~7N;wiXRWyL0>zaXiI z_(b5w3);dj_iZs?hxT@bz%F0jCBF=X-CKEEz9}299cUe*GoUEs3$Rkj@_35(K7}1# zMJW*(z&+*Ei`Q&KCiREL=9s3w?V1LW9S7{LK5Th1NPoH8kF%^rO9y>3vv;LIW};us zzkxp5(X#U3UqpjDerIpW^fCRezo}@H^|zCIR+`>Zgpdjz)w%Q(P3|t5j)d9nmB!nO zCinPus{IdRyZkV|>-ST8R(0v4{kN;eUYW+(HC6mQt6blxk+A36^dnW$jxsNgi{#h8 zk%xbidrFkJ@ta#KH9|bJ^7ef5yu_;jutPVxThG~8&f*G@U`n1+n1_{KDy2U*ui~_O+ji0 zV3)6=xP+OSW=bIbHB0&^pN&psZ$9GkTNBy)9K}P+JYG)+ksXj+-di^34ee%#JqN^9gOC?3_*aW58+h13od51PBD?`X;}?tPSH{2)mmdCeE2XdyY|&^2-@ zfL{FPL&^TDWa)mYs#|k}tUgRu9wZC*DL5T~-LfNO>0ts8@AL=hga5Y+zUq`YfWHz7 zxH5hi3%njrq=qblGGE$zUwb(dAoKZbhvgFwKJa2WFXW543qSd-r>hfM38AVflZbs4 z8;9m@va#UGn^HH(W1P=);g!(6z8~0iSwO9?7XOpfeJsl5tgnJLp%m2m+GUEs)>m*k zD?vFOQeVGhPKS3j_E0D!SJrmAP9#0mAC;(%z8l59`hW)ZkO`knS?niw@A>)zXPJQ3 z@r5pRKx|-Y#ekg>&p|YY4tHX4p~V#q?0B^uPx^`}?8*dokml7w)T}15WdZDzFisWN z>FQE@Y^hbZ?oUwI0odVQqh_`C$=ZU=UEkQ*Z+%Dq4V^H?^6Fphum07+Z@t--KRaFa z=oqlh?8XMO!1c5sz6;vW*>7DZe@}~mtv3E!t^BrH`fjq?w5)wkw<7NQ0j^~I1Ov}0 z%{-l&Y@XbB2bkS7J#S~pAs5g(R9zo8oihvSU6SdXoLhhK=+?wynjX?%+x#YJIb70W zn8a{kL(;dQL7&DZL-jgNL9&ENuO-Gk7f~E$L%%3BvzZAOYGX5`TRH2sAB~Q3Tzfl5 ziSZDL5rCZ|s9fWgV`%p}US0=52ISTZGNh2sv`lgGgSh+8Wh-B=``!r(5R1&e(yQ}cC7*I5Qs5~19nr1 z<#fvIY-dwTEKIxBZiZbJ;P31OU+n=I7ht7<%^a+hs(NN^WW-u#*hR)oE<|`(*~nyw zQeoGjmGNL}tsz!bh9P>Qr#9Egjx)x>73)yV*@1BxTxE`tw~(Ko7gDgr5>aevA*Q8V@!Xto=Axi~&~|RKp-4pvq%_x$BzoeoVZ-W_*A+ z{%TcNZXfw;V?746{KWCss@=J$8FQw3)M=fY|L9yjpmWNr!J+j{x2_;-=Nh}aw3#!Z z^Tc1xoE*%Y9c_nBZ{25}+CKq`eIR_qO*mdV4btVaV)XR^+-n;c6dGUw?DX~PBHWpC zvez0gpdWx8{>fyrTVa6}c{)Vjex|Q|Zsc>e0)&0F*k}Hy-^BkK6*;6II%lvunYfWG z+)dmMlATwGKjd|s9~N!wkT?Ng2k9KB9F{1A!}`ZlPRAGD!w!w@cxJ~pwhMSj^JK4t zlk*|u6l|?wl#C$uAti;TYQ`kcu-C|YR9hE)pM>2dH)2TmeRA(H?cAT3O5qDwA$Z@7 zC{lcS83@q9$)Rqe(z#di_OITxdn<=SwuegfQssAYxs6he_g3EN zSvE}(O-QD|?oJ|o?FhZ)oA-g=;Vv8>+zV!>4q(T#yr?WiPdAV|kT3ryts%1GYwI4? zed3Sv`hT;epJ-;UZzlGvJg(cfqs&F4EGrG`{KL?W-wo|3nqnuKVNX4<;B>A%znRue zG}2o1o5eRHZ7UDAWQLek9%26dNT{l9z6Zsd)%V*41DJjDD_>qy>CSdEu&b1^SCn}` zB)=k(`-$XNMe<0IJmQs` zH?*4}XkFS{_F*>r^aTZTpmMx7mT!#6CuV@$@vRT({C8~XYxZ6Sdnb*(lVpHygLp*% z=m)Nlb2sT5>UbboeTXdBO_m&>z;4Y^vhpx-KSWj^C4c@yRvstN)=lv&VSs%vwfUTy zvabX%(f^e`=VAFoYzL_E-n0A#Ot5hwh|vAIx!9Kv?ypXu5|j0yLa zEEm`EM*HeN#42ng{Vgjfi z)~img3L032=Fjql&eT~!1GcLLogJ!FLZ`Bl22h}f9oE#Y{Uay<9%9g}j<1Jl z$-214^t8L_h1KnSJ1K zjwc>Yjs9@b^_)UBPm*kMl6Xz54`Ao*T+iEC=di2J2{)Z%Za*HM0S<-+a7cq4P5DNVOh;;gairDIx{ysM3*{eoh2Q|Q;i!9kaehM=+IEv z!%lywggELpA4ys{OIRme{Skn4P1w;5O@_~1vN<$7{K}PU$4;EtbNKMa?Yoz*UhleS z>G+uoex2wd)?t6F-JIietuYqsv;wf>t7iR!b+J!D_tNt^#llQC$N zEjroZPZMrUV2s6nQmLIAG3UnoXEavgCdSrt=mhGYolUIfa+A?-Oh(LS(J=*lG7MPr z39W3M8tL~yHAWfq_@%XUpgHra4Kt!EGps8EV=NSQe2wdk01C#{fuR8uzcTY288AE4 zQYS6Gnq8W1>{p>Vp0L29@=|-l^9gLp(7(uYF*hWSe`FbuyDB)7TkfT2CC_8Nd!T zZjd>!UGFclu^mjWCvlSFEfwZKxwG#oxs^x|G=T#0;5nV@62MN`!w!~M zrMwPMk6(`o$dkQtn_LQ~;j^f?CnOtMwk%51qMwk62b9f$UO{MfD~?=?AP9$zjiVm>7?D8%T89Zha^QDBdl}HssmP{y z90Ll&7or|8jn`PIGP|rig<19aE0O~5B;qXOB!_cTX1|3{3-T^i>ucAdzz+I5)qC}` z5jBuYUozU0@$xz_yIYZpF@H4h@^d+3uj+1;7=N(BPD!}Jxzto1*oCBbQUW^&?vR1o zzgG?G)>UWr5-l4@Yv};d(!Qcu-9*2ceKW?a(r63O7#mu!%?|5S$)Uq{L(CY5&ObPq ziiVg0?Qwosd(onPB6kPTnjxaUM~QB(6UBPbxQRC>Me++Ar}KrjP9zT$$-_nR08#qE zZ=P?f^yYx*#U4@U%J0Cll)K8y*0SHSq0eZQ4eYokch3xQp+bp<0x8A?30jA=2ma7K zdr2N9KIW8w2*x|B#o<+i7^uB8dU>BOiCcmU4 z?m0*30O_#vSU$_4J?v6na`HL=xHpvRCFM~AEbp1c3k046u!9^AibA-6F#yGT@|Id< zsU(PZTTvSL43d4xhb=;=_4_LFrdi05fqD~^7` zA3vad`$T5+K@a~*027r2e$easKb#CnAotzv%U@dId-0c#z5YMgLy)_Vl~{e~13~US z+l41Wz-!5}K|qF!0kSfMst-K`pLsslR?q-`RY3|{!~TQTL0czCSvyN@Cvi!m2pbis zx{RZKRYA&%)1yKJCu|9L4G3~tOKt?c2$6ar?5i%hHJhmh_-UusvYSirbEv1xK&{n% z9$tvPT3-k@ZZpHM%QUuR*-SNIdpFXzs$W3{Cl)IJTt$GL5(SFU@J{o)8a2=Yj{-X| zH~>2pDpw5K;jSOi>W-U$B>(lP+nH9~?{@w!1 z)9x)!ySD+h`)9G~iAAPo-D&;1h~C7?e^<0S?rwT)S-W3nnOILN{z25k*=V&B@%T;h zhm*w9NwRr-15YQN?GtP7b*=+i#{s)q$J}&=^s|xtGM*M_UGrfL*ufGbhx(+qPNQy( zJ4|i{omy8!H{ggvDKuNxd^$KCF`J4=R!O^Yl##V_lC&9Jzd6Dm2J^tKej%_M1Yiem zY!h~jL^`q|8CKt*w@x!VvBYfK&V$#3Zv1=h;=UtCH}BlL#&heOiF0+6m$U}yECu63=(wmSOuI_y9IyV}hMbFNqp*mayl%w6@&U9g``tf5kN(>0$e zF`EkOF0BG!*VNh#;a5Aku2D*yh$obwb@ zX19&Gyh8&zyFhO=wqtG`(!A{>jyeWx$J{%k6@Nh-e~troeiYa}3aF9U@pd2BMxBhqIL{cUI3~S)8$#_|FL%_ za53%S|39wnNOD};q0&(4D`F6$5TYn#4@Jr{_K__lgODY%XV0E}?AfztOZM!^mMx(O zY5t$@Ib%!-U1s{-`}_aAo_p>y-#K%dv&@-u-t+l9&&@+cFdny`0=u~VNGPz2i=@qJ zFYe+_Z{VULN*rFt?_R-go@Ww1({f&r_2i*d{d-p*+`S%XU2lQe$vm3&>(Z=yZ#(=* z@XOF2snN!2RUJ%i>YCt_XlV&ZN1g1*t*p>72HHA|ltJk_0K1A6OG7-XL3Kz>O-uRs z_-F&W)+20la+Yg8whApUoGalvuCn&Z>NVHXSBXB}GlYlYo@yOP`pzK}R*~78X~Wh7 z=cuR+-D3}3qk(GosHFu#N)T*Q3`qSiM)rKQmB0fTct3kb{mSO-vK)?yH;NV@XjGU!y)aHLu#7CyZ1ES zraIhwSoQWZg1GyF+dGaMg`YcWQ;Jx%_lJxm!=hrGXFQ*U$sAJ6{>?#-ZsGF~=J=>*0-foVxD}}cs)3w20h+fUu?v4e! zHD(66F##Q!z>ds}F3dt7W~Gc-En_wgV(u+xUPUk}C8OHSeA>mN?`1OfF{*t`vXV*J z%e>zC*Y&XcmqQ9ZTAVj!4fA1Ro==J8RVp-QrSVy1ou;x*PP2}ELgPxJb#wxar>W{0 zG;_u#SknMR{tE08P~7l~dP{=CCVwGMKV$>!5>sfkJG51KEy(V$wPBdm-sYkPQv`Nk zyAWw!c|fh=G50G$*7xk4^@A7IZ$4V5exUIw-8wbRI{6*V6W>`UeXxF;ZvEzy_45?# zq%`YKU)M`qwG5SYdWLlpLS_Bxvvukh>klgHj~UkQ(ygyPv%d7u`tfV)YftI=o_$ye zn!z*hm?w~hJIV6BBs`KV*r^6~Fu+dRPEj3ncCwko_0QGH(6Hnha>C#Gj{qjxG(!*J zHcr>~8sjRp2&}C8FH^{xP<2YD(QGiSJ@uc#c7v)?x2cZ920blvBL-KZ2unv`=jY3D z9x13I^n5wTXk(Ga@~TeqDmu|{F)9_qh)V))YApZvDy0O+sxF0%;B{E0_ICY~97G5@ zvz&{ua(T}GK(p4NHcf}?OEfLA*+1F8Ie9JqioU6>sg!m7~HYGBMog$r}nCFAlN1wY8crwS-O3^bmzpHyC>D& zKEC>vvC`cNyX^|QEs9!O$JgF6$!^nxS_=Yewi-cyKKMTTdRfi#DIe-Bncc$-)GkbB zDo{Iq4MldP*2}q3J#1+|*xJ3z!3A5ZyQI38apjI>&Ht`ex7r>S*&GwV(u zb;Zgz;w_t&lsHQ3dz_1jIehfEa_8<9Yu3+MvSL!`{9%fqUPDK9AK+)(uw^D{S4zs2 z&MdQ&G%6-(^mhdp2m$$O?fGgQ%2e+x^q=TODWbywYSN{uLAj;laFDn1j-YlRjTG4d zHd=z<(PTIVEgXj-uSg&ITM09ir}9Ao?g@j=jy7GIab8V}cskMcu(H;LW$p&Fa$Tn) zL%p~m-3pELW&>rMVgQ1jIHb@te{RNTqv@mBITH-Srf^}wMZ=~Ut(eQL3**)-G>BMS zcqG3ZWN8>fK_gcS~MBY;a*W8@r#qc8HCQWH0S21X6eXkP-5$`yt)qu0|RnzZO{p z`Q<$ZvAY55fb2LVmv$DqvWJV=${mm3j;t=RKb+sWl;1RuUpdoaVNj*XBPxyVFCE;& zu21KN-W?o!ccfxFO}G!Y{rT+@eoDx4GSSIH$mtq*R;ce;-q;l6ytqKl=_m9%Y9|q? zot2ew`SK-kLik#&QqAsKwrrt|>^ujx&gqG6j9%nu*=|f7wp!K6#{c&$M69q0k_{WnQuoI0qFRR4Stm z+RV5H55eW$iLETqGxjqrWCz0YDxC)Gl^@km=v!&K43!fiv$XD8nLCKsw4c(1`8GkPih})InDHHHd)QgsnK@p}q8`jrUuJnvW>p{N z)HLSkbmrVFCN`A0GM_m;E6?fadC$zsdwx#-v-8=ra|<4wULZ1%Jvk}=z4^@N?aaH! zWj_LnexXOji&XORJ$VkWhQ_~1B;hr=^MXd=JCclAg+ib;NJ<)YQKP3Z0$}<41L}VT zS=HWdpmx}OV!VRV?jtqDqL5wkXDYu#8)T>_p|rzG(uKN+0DX{eLc|Hl+fS&{?)pRWAc+>ZgYXWWLS7`151;7FP;(-GiyiuouEoa;S}_mQ7Y%8Vi;_?* z97;Y+v8M0P=;8|%Zff4``*g}4u|}uvk~0b9Hu_Jzq_y~t$CG)xNvM*nh$O4_)5gv- zlq6V5rtKg@mytnpiHCxQ4yYZ~)m#6(Ph+;H*54e2GVH&ix6=<`)JLfXRNT>h74Cql zF1U--sl5$LYcfEWMdS6$0QDnTYb37*Km@9$^KF~W0*4S6{7^qa?>C|Jasv8Pz+T`x zJ^Tuwrgpl5b($^p{axa}YDDK8M7L|F*IuK4&h>!mbi2MfUz!?u>*t#WpGgSpbpD>d zU!cC%0xJTJmJ9bzV1k{!U`BobPZE=%D4zhXr?Xl^bhRA2{eXKBud_BD?RCPDz!5g{nE)5NZz zDoO>x6eCK77`94?^6)i?(e*NWDy|bOisBkaiWSb%7-mjDZ?MMZ+k}qg06ICuuOqs=**) z4>em)cxS0BkG{4uIGup%+EXiS737|h%BcM2A*t5g96e*9v?Jc8mD`oJat5|5E^!<= zX4=VNk9hXP?2LTZszvy;G!aF z9NW*GKUyL-nvabx9(#tn9$V^G93L0Q-9uZ~cs?<)bYdd+{Q2L>$^5%_C1N(QS2r8p z+|50`T=>=j_WnMj2S>RFhuH^54WFIio}S^Jz#w~8=p2h)6tP@N9G7;l#Jk(YlW%hG z<4axI$3-hyL@Y2|WKrO`%aM@NsZkw(9r7!Yg)x3T(ukru%&DZpH#`rp*;Kw6Aq^4(wV7T4ni2iVrO;Ag41iH#h!jhZRvBpTz|W1TV69OkrX7_5G%? z>m%#^56*A72<)&ubKyMyU;GoA{e^pe-1&E#NX9NAb2d_H7I~g>H&hAJ_+R09)dqGT zcTuphLJ0?uhrSeFU9%#vJ9w2ch;#SI#k*8j2P&5!kh{ByB=I##`bf2R*X~hZ2gx05 zqhNN|5~!#Sin>!*D6NBe6jM9|J%sWPz%KC-Nkf6MSL8EvX@D#OXQ8cP5uZtVDtQl{ z@|b*rGZJhLd@YsQWWnszWCY1)br)F;E6I|8r-0OmaylRtD6yc&g8A|e-r*aH*aS&d zEo}_P3@bFK89kAVJRRa;&z0GiI?OIk!xGYi2hI6!2 zM^`6yoI5kTB{QNGBX?oYLKfX(y&4ttaAf+oU}ktRAzsYnj?B2W%(xCrP$y;zI?c9Z z0^FI29jQllsf-Piu?xM~B{FtVUz%gOW*8gcZ*YEUzR2E~USFg95lz;z{_0+>$FXg!Xbt7le z5E3!bFKE-Dy|sV#zx`)>i}4$S&hkoGwu~Mh>ah3cFF(Ax#&(Qu0Vjld;HJN^!*b^( zxQT_fQHLio=;`2p)zzENglq+aDe7@gHpg99Q~Y z5qt{t@f%l`dp5Kv#J92zKXKVE3j6&}c1m3z>+-crWee<3tuBYa4jMg!LU}kNs@f}F z9KKipyCMK~s7+V4EN^B;30;{oGy$#C06V}t3kwSDX7@4KH__r?ka^_z3j3#6?VC*Y zDk|(ySVaa~@0n~3NVk1_r5zKjF-5LaU|MZ+K;;O3>y4wUtsf;_A-7&4w_fEZUEx

kP+Ag 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тVtSx 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 literal 0 HcmV?d00001 diff --git a/docs/assets/games/mega-man-x4-coop.png b/docs/assets/games/mega-man-x4-coop.png new file mode 100644 index 0000000000000000000000000000000000000000..76140f3c785912f13e5e4713ec7dcd95dbfed10d GIT binary patch literal 230723 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 ZfBx~$|Mw05{`l|z@o)e8hyVKT{|6ilPH+GK literal 0 HcmV?d00001 diff --git a/docs/assets/games/mega-man-x4-netplay.png b/docs/assets/games/mega-man-x4-netplay.png new file mode 100644 index 0000000000000000000000000000000000000000..c11b4467575219c118496afcb7a8b59dd64755f1 GIT binary patch literal 71853 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%0Anp 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⁢#HR=+-E0rrl=M=WN);feG`dz$wqGLY#F;~(v|#%;LFGX zKoF*xsfA=@xK6o+NPR7fE$RxobjX7ST?j&zrp|~v-VMM4SOnRp699@!Yi7In(I8bO z5tl@4KTZ_N3?C+KXiw z3=4Hu8-e-mmR_*fDu??6ACEzf7}w~O>U7s1O#{4EY4$?!hU@DW)1aMors>xk{-Iak zpOx8w_84zXCffz||JkbVh!+9OJ;!C%cP@qR6b#>}F#xTHO;WPiW39vYOcxhBWRcgt zV(xjhJ4VY;{m!Fu6B5ulDw@e{2{BcCTr>L*u7ICR3FTZ8Wd>E^p88r;7Df<<4R6}8+OCm(JIW$!-vU;94i4~glo6i&UPgzN+d-g1x>by5n>Ffg=8+>WM&807Cfy0DMHQLZ+@0<<0 zHtdQqS7z`&-y>*@&-)i7XXNkt)>raPF_y7oO=fj_jb{2njK-Lq4S6 G~zmGZhgt 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≫^V$@E;3*m&k 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 literal 0 HcmV?d00001 From dfe475cfd68ba14022e24ccefc16e7e4eac1a245 Mon Sep 17 00:00:00 2001 From: Vulito Date: Wed, 21 Oct 2026 17:08:16 -0300 Subject: [PATCH 03/29] List both community Gran Turismo 2 projects in the refreshed games table (#725) Co-authored-by: Matthew Stanley <1379tech@gmail.com> --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 49fe6baa..5e368bf6 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,8 @@ and testing limits; source projects do not all provide finished downloads. | *Wipeout 3: Special Edition* | [Wipeout-3-Special-Edition-Recomp](https://github.com/mstan/Wipeout-3-Special-Edition-Recomp) | Source development project; consult its current build and qualification notes. | | *Mega Man Legends — community* | [MegaManLegendsRecomp](https://github.com/VuloB1/MegaManLegendsRecomp) · [downloads](https://github.com/VuloB1/MegaManLegendsRecomp/releases) | Independent port with 16:9, analog movement/right-stick camera and enhanced presentation. | | *Xenogears — community* | [xenogears-recomp](https://github.com/OpokXeno/xenogears-recomp) | Independent development project; also contributed widescreen cull-site work upstream. | +| *Gran Turismo 2 (Arcade Mode) — community* | [GranTurismo2Recomp](https://github.com/VuloB1/GranTurismo2Recomp) | Early: menus through a race. Experimental 16:9, analog pad, optional PGXP; no release builds yet. | +| *Gran Turismo 2 (Simulation Mode) — community* | [GranTurismo2SimRecomp](https://github.com/VuloB1/GranTurismo2SimRecomp) | Separate Simulation-disc program. Builds; not yet tested in play. |

Dragon Ball GT Final Bout combat with additional stage scenery and edge-positioned HUD From e3ea739d7c7d6d58e5ef24c075108841c50451ce Mon Sep 17 00:00:00 2001 From: Matt Stanley <1379tech@gmail.com> Date: Wed, 21 Oct 2026 13:25:01 -0700 Subject: [PATCH 04/29] Check BIOS stamps on PRs and clarify regeneration and commit guidance (#729) --- .github/workflows/pr-bios-stamps.yml | 17 +++++++++++++++++ AGENTS.md | 4 ++++ CONTRIBUTING.md | 20 ++++++++++++++++++++ runtime/runtime.cmake | 9 ++++++++- 4 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/pr-bios-stamps.yml diff --git a/.github/workflows/pr-bios-stamps.yml b/.github/workflows/pr-bios-stamps.yml new file mode 100644 index 00000000..d6fa5e16 --- /dev/null +++ b/.github/workflows/pr-bios-stamps.yml @@ -0,0 +1,17 @@ +name: BIOS emitter stamps + +on: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + bios-stamps: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Check committed BIOS emitter stamps + shell: bash + run: bash tools/ci/check_bios_stamps.sh --framework . diff --git a/AGENTS.md b/AGENTS.md index 36e7ca73..24dbeb89 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,3 +13,7 @@ shared HLE work is `beads-wuhs`. Do not initialize a local issue database. Work solo unless the owner authorizes delegation for the task. For the approved parity campaign, serialize title builds and game runs: two compile jobs per build, one game instance, and preserve the owner's sessions and personal saves. + +Commit messages use a subject only by default. Add a body only when the reason +is not clear from the diff, and keep it to 1-3 lines. Do not add per-file +changelogs or repeat the diff or PR description. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c0871f53..33ffc99e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -82,6 +82,26 @@ written — is likely to be rejected if it: - Contains code the contributor cannot reasonably explain, or whose verification cannot be stated. +### Commit messages + +Use a subject only by default. Add a body only when the reason is not clear +from the diff, and keep it to 1-3 lines. Do not add per-file changelogs or +repeat the diff or PR description. + +### BIOS emitter changes + +After changing a BIOS fingerprint input, run +`bash tools/regen_bios.sh --config bios/OpenBIOS.toml` and commit the regenerated +OpenBIOS output and stamp together. PR CI checks the committed stamps without +requiring a retail BIOS dump. Never update a stamp without regenerating. + +Retail-dump users must also regenerate each linked retail stem, for example +`bash tools/regen_bios.sh --config bios/SCPH1001.toml`. Retail dumps and generated +retail C/stamps stay local and gitignored. In the PR validation notes, name the +BIOS stems checked and whether their output changed; explicitly say when a +retail stem was not checked. Unchanged OpenBIOS output does not establish +unchanged SCPH1001 output. + ## Game-specific work belongs in game repos The framework stays game-agnostic. Per-game patches, launcher UI, assets, memory diff --git a/runtime/runtime.cmake b/runtime/runtime.cmake index f84eab05..f4d8d849 100644 --- a/runtime/runtime.cmake +++ b/runtime/runtime.cmake @@ -907,6 +907,13 @@ if(NOT PSXRECOMP_SKIP_BIOS_STALE_CHECK AND _psxrt_bios_linked) else() set(_psxrt_stale_sev WARNING) endif() + if(_psxrt_chk_stem MATCHES "^SCPH") + set(_psxrt_stale_publish + "Retail dump/generated outputs stay local and gitignored; do not commit them.") + else() + set(_psxrt_stale_publish + "Commit the regenerated ${_psxrt_chk_stem}_*.c and .emitter.sha together.") + endif() message(${_psxrt_stale_sev} "BIOS generated/${_psxrt_chk_stem}_*.c is STALE vs the recompiler " "emitter (fingerprint mismatch).\n" @@ -915,7 +922,7 @@ if(NOT PSXRECOMP_SKIP_BIOS_STALE_CHECK AND _psxrt_bios_linked) " The committed C may not match the current emitter source, " "seeds or profile.\n" " Fix: tools/regen_bios.sh --config bios/${_psxrt_chk_stem}.toml " - "and commit generated/${_psxrt_chk_stem}_*.c + .emitter.sha\n" + "\n ${_psxrt_stale_publish}\n" " (Suppress locally: -DPSXRECOMP_SKIP_BIOS_STALE_CHECK=ON)") endif() endif() From da0c98b936f19ce0eaac27a2bbb6de0053a5f231 Mon Sep 17 00:00:00 2001 From: Zap0xfce2 <902946+zap0xfce2@users.noreply.github.com> Date: Wed, 21 Oct 2026 23:45:02 +0200 Subject: [PATCH 05/29] feat(runtime): MEMCARD_SLOTS for titles that only use port 1 (#704) --- runtime/CMakeLists.txt | 4 ++ runtime/runtime.cmake | 17 +++++ runtime/src/main.cpp | 11 +++- .../tests/test_game_runtime_memcard_slots.py | 66 +++++++++++++++++++ 4 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 runtime/tests/test_game_runtime_memcard_slots.py diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 0340c49d..ea1d52bc 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -2127,6 +2127,10 @@ if(BUILD_TESTING) COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_game_runtime_pgxp.py --cmake ${CMAKE_COMMAND}) + add_test(NAME game_runtime_memcard_slots_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_game_runtime_memcard_slots.py + --cmake ${CMAKE_COMMAND}) # tools/mod_channel_filter.py strips developer features from a staged # catalog; every entry that served one ([[requirement]] included) must # leave with it. diff --git a/runtime/runtime.cmake b/runtime/runtime.cmake index f4d8d849..3879f5f4 100644 --- a/runtime/runtime.cmake +++ b/runtime/runtime.cmake @@ -1453,6 +1453,18 @@ function(_psxrt_stage_mod_catalog target preloaded_dir) endif() endfunction() +# Memory-card slots a title uses (MEMCARD_SLOTS, default 2). 1 for games that +# only talk to port 1: the launcher hides slot 2 and the runtime leaves it empty. +function(psxrt_resolve_memcard_slots out_var value) + if(value STREQUAL "") + set(value 2) + endif() + if(NOT "${value}" MATCHES "^[12]$") + message(FATAL_ERROR "MEMCARD_SLOTS must be 1 or 2 (got ${value})") + endif() + set(${out_var} ${value} PARENT_SCOPE) +endfunction() + function(psxrecomp_add_runtime_target target) # PGXP: build this target's objects with -DPSX_PGXP=1 so the PGXP_*() # hook macros the emitter writes into ALL generated C become real calls @@ -1479,6 +1491,7 @@ function(psxrecomp_add_runtime_target target) EXE_NAME GAME_VERSION MAX_PLAYERS + MEMCARD_SLOTS APP_ICON # The title's own mod catalog source directory -- the one shaped like # /mods/preloaded, holding packages///manifest.toml @@ -2010,6 +2023,8 @@ function(psxrecomp_add_runtime_target target) endif() message(STATUS "psxrecomp ${target}: PSX_MAX_PLAYERS=${PSXRT_MAX_PLAYERS}") + psxrt_resolve_memcard_slots(PSXRT_MEMCARD_SLOTS "${PSXRT_MEMCARD_SLOTS}") + target_compile_definitions(${target} PRIVATE DEFAULT_DEBUG_PORT=${PSXRT_DEBUG_PORT} PSX_DEFAULT_BIOS_PATH="${PSXRT_DEFAULT_BIOS_PATH}" @@ -2025,6 +2040,7 @@ function(psxrecomp_add_runtime_target target) PSX_DEFAULT_GAME_CONFIG_PATH="${PSXRT_DEFAULT_GAME_CONFIG_PATH}" PSX_WINDOW_TITLE="${PSXRT_WINDOW_TITLE}" PSX_MAX_PLAYERS=${PSXRT_MAX_PLAYERS} + PSX_MEMCARD_SLOTS=${PSXRT_MEMCARD_SLOTS} FMT_HEADER_ONLY=1 $<$:NOMINMAX> $<$:PSX_SDL3=1> @@ -2630,6 +2646,7 @@ endfunction() # LAUNCHER_BOXART "${CMAKE_CURRENT_SOURCE_DIR}/launcher_assets/img/boxart.tga" # APP_ICON "${CMAKE_CURRENT_SOURCE_DIR}/assets/psxrecomp.ico" # MAX_PLAYERS 2 +# MEMCARD_SLOTS 1 # optional; title only uses memory card port 1 # ENABLE_NETPLAY_IF_PRESENT # ENABLE_SETUP_WIZARD # PRELOADED_MODS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/mods/preloaded" diff --git a/runtime/src/main.cpp b/runtime/src/main.cpp index b46655f8..4cf6c749 100644 --- a/runtime/src/main.cpp +++ b/runtime/src/main.cpp @@ -82,6 +82,9 @@ extern "C" uint32_t g_guest_last_store_addr, g_guest_last_store_val; #ifndef PSX_MAX_PLAYERS #define PSX_MAX_PLAYERS 2 #endif +#ifndef PSX_MEMCARD_SLOTS +#define PSX_MEMCARD_SLOTS 2 /* runtime.cmake MEMCARD_SLOTS */ +#endif #include "psx_netplay.h" #include "psx_stick.h" /* radial SDL-stick -> DualShock response transform */ #include "psx_trigger.h" /* continuous SDL trigger -> 0..255 magnitude */ @@ -16552,6 +16555,9 @@ namespace { * the generic Display -> View mode launcher row. */ gi->widescreen_supported = 0; gi->num_players = game_players_n; +#if defined(RECOMP_LAUNCHER_HAS_MEMCARD_SLOTS) + gi->memcard_slots = PSX_MEMCARD_SLOTS; +#endif #if defined(RECOMP_LAUNCHER_HAS_SHARED_GAMEPAD) && !defined(PSX_NO_DEBUG_TOOLS) gi->allow_shared_gamepad = 1; #endif @@ -20715,9 +20721,12 @@ session_reboot: { std::string mc1 = memcard1_path.string(); std::string mc2 = memcard2_path.string(); + /* A one-slot title (MEMCARD_SLOTS 1) never reads port 2: leave it empty + * whatever settings.toml or the launcher stored. */ + const bool slot2_on = memcard2_enabled && PSX_MEMCARD_SLOTS >= 2; const MemcardSlotConfig slots[2] = { { mc1.empty() ? nullptr : mc1.c_str(), memcard1_enabled ? 1 : 0 }, - { mc2.empty() ? nullptr : mc2.c_str(), memcard2_enabled ? 1 : 0 }, + { mc2.empty() ? nullptr : mc2.c_str(), slot2_on ? 1 : 0 }, }; memcard_init_slots(memcard_dir_str.c_str(), slots); } diff --git a/runtime/tests/test_game_runtime_memcard_slots.py b/runtime/tests/test_game_runtime_memcard_slots.py new file mode 100644 index 00000000..4fd34376 --- /dev/null +++ b/runtime/tests/test_game_runtime_memcard_slots.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +"""psxrecomp_add_game_runtime(... MEMCARD_SLOTS n): runs the REAL +psxrt_resolve_memcard_slots from runtime.cmake under `cmake -P`. Unset keeps +both slots, 1 and 2 pass through, anything else fails the configure. +""" +import argparse +import re +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path + +REPO = Path(__file__).resolve().parents[2] +RUNTIME_CMAKE = REPO / "runtime" / "runtime.cmake" + + +def extract_function(text: str, name: str) -> str: + m = re.search(r"^function\(%s\b.*?^endfunction\(\)" % re.escape(name), + text, re.S | re.M) + if not m: + raise SystemExit("FAIL: %s not found in runtime.cmake" % name) + return m.group(0) + + +def resolve(cmake: str, body: str, value: str): + with tempfile.TemporaryDirectory() as td: + driver = Path(td) / "driver.cmake" + driver.write_text(body + '\npsxrt_resolve_memcard_slots(_n "%s")\n' + 'message(STATUS "SLOTS=${_n}")\n' % value) + r = subprocess.run([cmake, "-P", str(driver)], capture_output=True, text=True) + m = re.search(r"SLOTS=(\S+)", r.stdout + r.stderr) + return (m.group(1) if r.returncode == 0 and m else None) + + +def main() -> int: + ap = argparse.ArgumentParser() + ap.add_argument("--cmake", default=shutil.which("cmake") or "cmake") + args = ap.parse_args() + if shutil.which(args.cmake) is None and not Path(args.cmake).is_file(): + print("SKIP: no cmake executable available (%s)" % args.cmake) + return 0 + body = extract_function(RUNTIME_CMAKE.read_text(encoding="utf-8"), + "psxrt_resolve_memcard_slots") + cases = (("", "2", "unset keeps both slots"), + ("1", "1", "a port-1-only title gets one slot"), + ("2", "2", "two slots pass through"), + ("3", None, "three slots fail the configure"), + ("0", None, "zero slots fail the configure"), + ("1.5", None, "fractional slots fail the configure"), + ("one", None, "non-numeric slots fail the configure"), + ("1;2", None, "lists fail the configure")) + failures = 0 + for value, want, what in cases: + ok = resolve(args.cmake, body, value) == want + print(("ok " if ok else "FAIL ") + what) + failures += not ok + if failures: + print("test_game_runtime_memcard_slots: %d FAILURES" % failures) + return 1 + print("test_game_runtime_memcard_slots: all checks passed") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From 030a50415e97e60c77501abf7965fefa7e36ee5b Mon Sep 17 00:00:00 2001 From: yacineolivier-maker Date: Wed, 21 Oct 2026 23:45:08 +0200 Subject: [PATCH 06/29] Mods: named debug commands (psx_mod_register_command, TCP mod_cmd) (#711) --- docs/TCP_COMMANDS.md | 24 +++++++++++++-- runtime/CMakeLists.txt | 1 + runtime/include/mod_plugins.h | 14 +++++++++ runtime/src/debug_server.c | 21 +++++++++++++ runtime/src/mod_runtime.cpp | 35 +++++++++++++++++++++ runtime/tests/test_mod_runtime.cpp | 49 ++++++++++++++++++++++++++++++ 6 files changed, 142 insertions(+), 2 deletions(-) diff --git a/docs/TCP_COMMANDS.md b/docs/TCP_COMMANDS.md index d937707a..0d11ca8c 100644 --- a/docs/TCP_COMMANDS.md +++ b/docs/TCP_COMMANDS.md @@ -493,9 +493,9 @@ between Play and TCP availability. ## Complete command index (generated) -**376 commands registered** — 363 on the native server (`runtime/src/debug_server.c`), 61 on the Beetle server (`runtime/src/beetle_debug_server.c`). +**383 commands registered** — 370 on the native server (`runtime/src/debug_server.c`), 61 on the Beetle server (`runtime/src/beetle_debug_server.c`). -84 of 376 have prose above; **292 are index-only**. An index-only command still works — it just has no description here yet. Send it `{"cmd":""}` and read the reply, or find its `handle_*` function in the server source. +85 of 383 have prose above; **298 are index-only**. An index-only command still works — it just has no description here yet. Send it `{"cmd":""}` and read the reply, or find its `handle_*` function in the server source. Regenerate with `python tools/gen_tcp_commands.py`; `--check` fails if this block has drifted from the code. @@ -574,6 +574,7 @@ Regenerate with `python tools/gen_tcp_commands.py`; `--check` fails if this bloc | `dirty_ram_unsupported` | ✓ | | | | `disasm` | ✓ | | | | `disc_select` | ✓ | | | +| `disc_tray_log` | ✓ | | | | `disp_ring` | ✓ | | | | `dispatch_check` | ✓ | | | | `dispatch_stats` | ✓ | | | @@ -626,6 +627,7 @@ Regenerate with `python tools/gen_tcp_commands.py`; `--check` fails if this bloc | `frame_perf` | ✓ | | | | `frame_range` | ✓ | ✓ | ✓ | | `frame_timeseries` | ✓ | ✓ | ✓ | +| `frame_wall_ring` | ✓ | | | | `freeze_check` | ✓ | | | | `game_options` | ✓ | | | | `gcp_ring` | ✓ | | ✓ | @@ -657,12 +659,14 @@ Regenerate with `python tools/gen_tcp_commands.py`; `--check` fails if this bloc | `hd_textures` | ✓ | | ✓ | | `history` | ✓ | ✓ | ✓ | | `hle_dump` | ✓ | | ✓ | +| `host_button` | ✓ | | | | `host_key` | ✓ | | | | `host_launch_timings` | ✓ | | ✓ | | `host_mouse` | ✓ | | | | `host_osd_shot` | ✓ | | | | `host_pause` | ✓ | | | | `host_profile` | ✓ | | | +| `host_span_ring` | ✓ | | | | `idle_skip` | ✓ | | | | `imask_trace` | ✓ | | | | `input_route_append` | ✓ | | ✓ | @@ -691,6 +695,7 @@ Regenerate with `python tools/gen_tcp_commands.py`; `--check` fails if this bloc | `mmio_clear` | ✓ | | ✓ | | `mmio_dump` | ✓ | | ✓ | | `mmx6_freshfix` | ✓ | | | +| `mod_cmd` | ✓ | | ✓ | | `mod_counters` | ✓ | | | | `nclip_stats` | ✓ | | | | `netplay_status` | ✓ | | | @@ -772,6 +777,7 @@ Regenerate with `python tools/gen_tcp_commands.py`; `--check` fails if this bloc | `rtrace_reset` | | ✓ | | | `rtrace_stats` | ✓ | ✓ | | | `run_to_frame` | ✓ | | ✓ | +| `runtime_menu` | ✓ | | | | `s3_smear_watch` | ✓ | | ✓ | | `savestate` | ✓ | | | | `savestate_status` | ✓ | | ✓ | @@ -781,6 +787,7 @@ Regenerate with `python tools/gen_tcp_commands.py`; `--check` fails if this bloc | `screenshot_hires` | ✓ | | ✓ | | `screenshot_wide_hires` | ✓ | | ✓ | | `segment_misses` | ✓ | | | +| `session_restart` | ✓ | | | | `set_input` | ✓ | ✓ | ✓ | | `set_snapshot` | ✓ | ✓ | ✓ | | `sio_arm_audit` | ✓ | | | @@ -882,3 +889,16 @@ Regenerate with `python tools/gen_tcp_commands.py`; `--check` fails if this bloc `wtrace_dump pc_lo= pc_hi=` filters the recorded producer PC, masked to the physical address (KSEG0/KSEG1/KUSEG alias, like `addr_lo`/`addr_hi`), with inclusive lower and exclusive upper bounds, before limiting results. Omitted bounds match every entry; DMA entries use their recorded initiator, and a DMA whose initiator is unknown (0) never matches an explicit PC bound. Address/frame/order filters still apply. `disasm addr= count=<1..256>` inspects aligned guest instructions; default count is 16. Resolved targets use the guest PC. Unknown words are shown verbatim. + +## Trusted mod tuning commands + +| Command | Parameters | Behavior | +|---|---|---| +| `mod_cmd` | `name`, optional `args` | Calls a statically linked trusted debug callback and returns its integer `result` and escaped text `reply`. | + +The process owns up to 16 names, each 1-63 bytes. Registering the same name +replaces its callback. Registration is independent of package activation; +this is a debug-control registry, not a way to activate a disabled mod. +Callbacks run on the debug-server thread and may only touch state guarded +against its other owners. Guest/shared-state changes must be requested through +the plugin's existing emulation callback and obey its session/netplay policy. diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index ea1d52bc..11e7afbc 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -1423,6 +1423,7 @@ if(BUILD_TESTING) endif() endif() add_test(NAME mod_runtime_test COMMAND mod_runtime_test) + add_test(NAME mod_commands_test COMMAND mod_runtime_test --commands-only) add_test(NAME mod_netplay_missing_plugin_test COMMAND mod_runtime_test --missing-profile) add_test(NAME mod_netplay_missing_renderer_test COMMAND mod_runtime_test --missing-renderer) add_test(NAME mod_netplay_profile_options_test COMMAND mod_runtime_test --profile-options) diff --git a/runtime/include/mod_plugins.h b/runtime/include/mod_plugins.h index 8c6743a3..1038cd8e 100644 --- a/runtime/include/mod_plugins.h +++ b/runtime/include/mod_plugins.h @@ -102,6 +102,20 @@ extern uint32_t g_psx_mod_function_entry_hooks; * kept, further names are counted in an overflow bucket. Emulation-thread only. * TCP: {"cmd":"mod_counters"} lists every counter with its last frame. */ void psx_mod_counter_add(const char* name, uint32_t delta); +/* Named debug commands for trusted plugins (test benches, tuning): `fn` gets the + * argument string and writes a short text reply (truncated to `cap`); it runs on + * the debug-server thread, so it must only touch state it guards itself. Up to 16 + * names (1..63 bytes, copied by the registry); callbacks have process lifetime. + * Re-registering a name replaces its callback, allowing restart registration. + * This debug-control registry is independent of package activation. It must + * not directly mutate guest/shared simulation state from the server thread: + * signal a guarded control request for the plugin's existing emulation hook. + * Callbacks must not throw. TCP: {"cmd":"mod_cmd","name":"","args":"..."}. */ +typedef int (*PSXModCommandFn)(const char* args, char* reply, int cap); +int psx_mod_register_command(const char* name, PSXModCommandFn fn); +/* Runs a registered command; -1 for unknown name/invalid output, otherwise the + * callback result. Valid replies are always terminated within cap bytes. */ +int psx_mod_run_command(const char* name, const char* args, char* reply, int cap); /* Presentation-only filtering: 0 nearest, 1 bilinear, 2 stable minification. * Mode 2 uses a bounded palette-aware footprint for proven 3D polygons on * OpenGL; untracked UI stays nearest. Other backends use bilinear. A session diff --git a/runtime/src/debug_server.c b/runtime/src/debug_server.c index 31f1b0bd..af45cb55 100644 --- a/runtime/src/debug_server.c +++ b/runtime/src/debug_server.c @@ -8543,6 +8543,26 @@ static void handle_host_launch_timings(int id, const char *json) free(events); } +/* mod_cmd: {"cmd":"mod_cmd","name":"...","args":"..."} runs a plugin's named + * debug command (psx_mod_register_command) and returns its text reply. */ +static void handle_mod_cmd(int id, const char *json) +{ + extern int psx_mod_run_command(const char *name, const char *args, char *reply, int cap); + char name[64], args[1024], reply[1024], esc[2048]; + if (!json_get_str(json, "name", name, sizeof name)) { send_err(id, "name required"); return; } + if (!json_get_str(json, "args", args, sizeof args)) args[0] = '\0'; + const int r = psx_mod_run_command(name, args, reply, (int)sizeof reply); + if (r == -1) { send_err(id, "unknown mod command"); return; } + size_t e = 0; + for (const char *p = reply; *p && e + 3 < sizeof esc; p++) { + if (*p == '"' || *p == '\\') esc[e++] = '\\'; + if ((unsigned char)*p >= 0x20) esc[e++] = *p; + else if (*p == '\n') { esc[e++] = '\\'; esc[e++] = 'n'; } + } + esc[e] = '\0'; + send_fmt("{\"id\":%d,\"ok\":true,\"result\":%d,\"reply\":\"%s\"}\n", id, r, esc); +} + static void handle_mod_counters(int id, const char *json) { extern int psx_mod_counters_snapshot(const char **names, uint64_t *counts, @@ -16168,6 +16188,7 @@ static const CmdEntry s_commands[] = { { "ws_hud_mode", handle_ws_hud_mode }, { "ws_tag_stats", handle_ws_tag_stats }, { "mod_counters", handle_mod_counters }, + { "mod_cmd", handle_mod_cmd }, { "host_launch_timings", handle_host_launch_timings }, { "resident_status", handle_resident_status }, { "netplay_status", handle_netplay_status }, diff --git a/runtime/src/mod_runtime.cpp b/runtime/src/mod_runtime.cpp index 5bfe6257..7fa5a8c2 100644 --- a/runtime/src/mod_runtime.cpp +++ b/runtime/src/mod_runtime.cpp @@ -45,6 +45,7 @@ extern "C" const char* host_launch_timing_stage_name(uint32_t stage) { #include #include #include +#include #include #include #include @@ -3311,6 +3312,40 @@ size_t g_mod_counter_count = 0; uint64_t g_mod_counter_overflow = 0; } // namespace +namespace { +struct ModCommand { char name[64]; PSXModCommandFn fn; }; +std::mutex g_mod_commands_lock; +ModCommand g_mod_commands[16]; +int g_mod_command_count = 0; +} // namespace + +extern "C" int psx_mod_register_command(const char* name, PSXModCommandFn fn) { + if (!name || !name[0] || std::strlen(name) >= sizeof(g_mod_commands[0].name) || !fn) return 0; + std::lock_guard lock(g_mod_commands_lock); + for (int i = 0; i < g_mod_command_count; ++i) + if (std::strcmp(g_mod_commands[i].name, name) == 0) { g_mod_commands[i].fn = fn; return 1; } + if (g_mod_command_count >= 16) return 0; + auto& entry = g_mod_commands[g_mod_command_count++]; + std::memcpy(entry.name, name, std::strlen(name) + 1); + entry.fn = fn; + return 1; +} + +extern "C" int psx_mod_run_command(const char* name, const char* args, char* reply, int cap) { + if (!reply || cap <= 0) return -1; + PSXModCommandFn fn = nullptr; + { + std::lock_guard lock(g_mod_commands_lock); + for (int i = 0; i < g_mod_command_count; ++i) + if (name && std::strcmp(g_mod_commands[i].name, name) == 0) fn = g_mod_commands[i].fn; + } + if (cap > 0) reply[0] = '\0'; + if (!fn) return -1; + const int result = fn(args ? args : "", reply, cap); + reply[cap - 1] = '\0'; + return result; +} + extern "C" void psx_mod_counter_add(const char* name, uint32_t delta) { if (!name || !name[0]) return; for (size_t i = 0; i < g_mod_counter_count; i++) { diff --git a/runtime/tests/test_mod_runtime.cpp b/runtime/tests/test_mod_runtime.cpp index 71680a21..c478d9ba 100644 --- a/runtime/tests/test_mod_runtime.cpp +++ b/runtime/tests/test_mod_runtime.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -438,7 +439,55 @@ static uint64_t startup_counter(const char* requested) { return 0; } +static int test_command_calls; +static int command_fill_reply(const char* args, char* reply, int cap) { + ++test_command_calls; + std::memset(reply, 'x', (size_t)cap); // deliberately fills the entire bound + return std::strcmp(args, "test") == 0 ? 17 : 11; +} +static int command_replacement(const char*, char* reply, int) { reply[0] = 0; return 99; } +static void test_commands(void) { + char name[] = "qa.owned"; + check(psx_mod_register_command(name, command_fill_reply) == 1, "register owned command name"); + std::memset(name, '?', sizeof(name)-1); + struct { char reply[8]; char guard; } buffer = {{0}, 'g'}; + check(psx_mod_run_command("qa.owned", "test", buffer.reply, sizeof(buffer.reply)) == 17, + "command owns name and forwards args/result"); + check(buffer.reply[7] == 0 && std::strlen(buffer.reply) == 7 && buffer.guard == 'g', + "full-capacity reply is bounded and terminated"); + check(psx_mod_run_command("qa.owned", nullptr, buffer.reply, 1) == 11 && buffer.reply[0] == 0, + "one-byte reply and null args are bounded"); + const int before = test_command_calls; + check(psx_mod_run_command("qa.owned", "", nullptr, 8) == -1 && + psx_mod_run_command("qa.owned", "", buffer.reply, 0) == -1 && + psx_mod_run_command("missing", "", buffer.reply, 8) == -1 && + test_command_calls == before, "invalid or unknown command does not invoke callback"); + check(!psx_mod_register_command(nullptr, command_fill_reply) && + !psx_mod_register_command("", command_fill_reply) && + !psx_mod_register_command("missing.callback", nullptr) && + !psx_mod_register_command(std::string(64, 'a').c_str(), command_fill_reply), + "invalid and unreachable names are refused"); + check(psx_mod_register_command("qa.owned", command_replacement) && + psx_mod_run_command("qa.owned", "", buffer.reply, 8) == 99, + "duplicate registration explicitly replaces callback"); + const auto maximum = std::string(63, 'b'); + check(psx_mod_register_command(maximum.c_str(), command_replacement) == 1, + "63-byte command name is reachable"); + for (int i = 0; i < 14; ++i) + check(psx_mod_register_command(("qa.extra." + std::to_string(i)).c_str(), command_replacement) == 1, + "temporary names fill bounded registry"); + check(!psx_mod_register_command("qa.overflow", command_replacement) && + psx_mod_run_command("qa.extra.13", "", buffer.reply, 8) == 99 && + psx_mod_run_command("qa.owned", "", buffer.reply, 8) == 99, + "overflow preserves existing owned registrations"); +} + int main(int argc, char** argv) { + test_commands(); + if (argc > 1 && std::string(argv[1]) == "--commands-only") { + std::cout << "mod command tests: " << failures << " failures\n"; + return failures ? 1 : 0; + } #if defined(PSX_LAUNCHER_MOD_COMMIT_WORKER_SAFE) const fs::path root = fs::temp_directory_path() / "psxrecomp-mod-runtime-cache-test"; const auto isolated_cache = root / "test-cache"; From 5c07d42f6a9df6c1c0b1f9a388802dc941f83dc7 Mon Sep 17 00:00:00 2001 From: yacineolivier-maker Date: Wed, 21 Oct 2026 23:45:14 +0200 Subject: [PATCH 07/29] Present cadence: re-probe vsync when the guest video standard changes (#713) --- runtime/src/main.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/runtime/src/main.cpp b/runtime/src/main.cpp index 4cf6c749..13309dd1 100644 --- a/runtime/src/main.cpp +++ b/runtime/src/main.cpp @@ -1774,6 +1774,7 @@ static int present_effective_swap_interval(void); static int present_should_wall_pace(void); static void apply_present_cadence(void); static void refresh_host_display_cadence(int force_log, int force_probe); +static void log_present_cadence(void); /* FMV auto-skip detection hooks (cdrom.c / mdec.c). */ @@ -3942,8 +3943,14 @@ static void sync_guest_cadence_to_video_standard(void) { psx_video_timing_is_pal() ? "PAL" : "NTSC", 1000.0 / period_ms); #ifndef PSX_SDL_NO_RENDER - if (sdl_window) - refresh_host_display_cadence(1, 0); + /* Force the probe too: the once-a-second probe throttle returns before + * force_log is looked at, so a standard switch inside that second (PAL + * boot -> NTSC title) kept the boot decision -- driver vsync off with a + * matching 60 Hz panel, an unsynchronised present for the whole session. */ + if (sdl_window) { + refresh_host_display_cadence(1, 1); + log_present_cadence(); + } if (g_frame_interpolation && g_gl_active) gl_renderer_set_interpolation(g_frame_interpolation, g_host_refresh_hz, (double)g_frame_interpolation_fps, From 0d1389e7d3f90b753fde9f9376da501ebd35c0f5 Mon Sep 17 00:00:00 2001 From: yacineolivier-maker Date: Wed, 21 Oct 2026 23:45:21 +0200 Subject: [PATCH 08/29] Overlay autocompile: compiler scratch files beside the overlay cache (#714) --- runtime/CMakeLists.txt | 4 ++ runtime/src/autocompile.c | 13 ++++- runtime/src/autocompile_child_env.h | 53 +++++++++++++++++++ runtime/tests/test_autocompile_child_env.c | 60 ++++++++++++++++++++++ 4 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 runtime/src/autocompile_child_env.h create mode 100644 runtime/tests/test_autocompile_child_env.c diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 11e7afbc..f1c45888 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -1516,6 +1516,10 @@ if(BUILD_TESTING) add_test(NAME mod_resident_test COMMAND mod_resident_test) if(WIN32) + add_executable(autocompile_child_env_test tests/test_autocompile_child_env.c) + target_include_directories(autocompile_child_env_test PRIVATE src) + add_test(NAME autocompile_child_env_test COMMAND autocompile_child_env_test) + add_executable(autocompile_publication_test tests/test_autocompile_publication.c src/autocompile.c diff --git a/runtime/src/autocompile.c b/runtime/src/autocompile.c index 22842246..3300923f 100644 --- a/runtime/src/autocompile.c +++ b/runtime/src/autocompile.c @@ -46,6 +46,9 @@ static char s_cwd[512]; * the loader's READ location, regardless of what the configured command says. */ static char s_cache_dir[512]; static char s_captures[512]; +#ifdef _WIN32 +#include "autocompile_child_env.h" +#endif enum { AC_IDLE = 0, AC_RUNNING = 1, AC_DONE = 2 }; #ifdef _WIN32 @@ -1191,10 +1194,18 @@ static int autocompile_request_impl(void) { /* Compilation is opportunistic: it must never outrank the interpreter that * keeps the current frame alive. cmd -> python -> gcc inherit below-normal * priority, while the live compile script also defaults to one worker. */ + char *child_env = s_cache_dir[0] ? ac_child_environment(s_cache_dir) : NULL; + if (s_cache_dir[0] && !child_env) { + if (job) CloseHandle(job); + CloseHandle(rd); CloseHandle(wr); + autocompile_note_failure(); + return 0; + } BOOL ok = CreateProcessA(NULL, full, NULL, NULL, TRUE, CREATE_NO_WINDOW | BELOW_NORMAL_PRIORITY_CLASS | CREATE_SUSPENDED, - NULL, s_cwd[0] ? s_cwd : NULL, &si, &pi); + child_env, s_cwd[0] ? s_cwd : NULL, &si, &pi); + free(child_env); CloseHandle(wr); if (!ok) { if (job) CloseHandle(job); diff --git a/runtime/src/autocompile_child_env.h b/runtime/src/autocompile_child_env.h new file mode 100644 index 00000000..3e0a7087 --- /dev/null +++ b/runtime/src/autocompile_child_env.h @@ -0,0 +1,53 @@ +/* Windows compiler scratch is private to the child, never the game process. */ +#ifndef PSX_AUTOCOMPILE_CHILD_ENV_H +#define PSX_AUTOCOMPILE_CHILD_ENV_H +#include +#include +#include +#include +static int ac_env_compare(const void *a, const void *b) { + return _stricmp(*(const char *const *)a, *(const char *const *)b); +} +/* cache is the canonical existing cache directory. Caller frees the returned + * sorted ANSI environment block after CreateProcessA. NULL is a hard failure; + * do not silently inherit a system-drive TEMP after a scratch setup failure. */ +static char *ac_child_environment(const char *cache) { + char temp_dir[sizeof(s_cache_dir) + 5]; + char temp_var[sizeof(temp_dir) + 6], tmp_var[sizeof(temp_dir) + 5]; + int n = snprintf(temp_dir, sizeof(temp_dir), "%s\\tmp", cache); + if (n < 0 || (size_t)n >= sizeof(temp_dir)) return NULL; + if (!CreateDirectoryA(temp_dir, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) + return NULL; + DWORD attr = GetFileAttributesA(temp_dir); + if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY)) return NULL; + snprintf(temp_var, sizeof(temp_var), "TEMP=%s", temp_dir); + snprintf(tmp_var, sizeof(tmp_var), "TMP=%s", temp_dir); + char *inherited = GetEnvironmentStringsA(); + if (!inherited) return NULL; + size_t count = 0; + for (char *p = inherited; *p; p += strlen(p) + 1) ++count; + const char **entries = (const char **)malloc((count + 2) * sizeof(*entries)); + if (!entries) { FreeEnvironmentStringsA(inherited); return NULL; } + size_t used = 0, bytes = 1; + for (char *p = inherited; *p; p += strlen(p) + 1) { + if (!_strnicmp(p, "TEMP=", 5) || !_strnicmp(p, "TMP=", 4)) continue; + entries[used++] = p; + } + entries[used++] = temp_var; + entries[used++] = tmp_var; + qsort(entries, used, sizeof(*entries), ac_env_compare); + for (size_t i = 0; i < used; ++i) bytes += strlen(entries[i]) + 1; + char *block = (char *)malloc(bytes); + if (block) { + char *out = block; + for (size_t i = 0; i < used; ++i) { + size_t length = strlen(entries[i]) + 1; + memcpy(out, entries[i], length); out += length; + } + *out = 0; + } + free(entries); + FreeEnvironmentStringsA(inherited); + return block; +} +#endif diff --git a/runtime/tests/test_autocompile_child_env.c b/runtime/tests/test_autocompile_child_env.c new file mode 100644 index 00000000..c56627da --- /dev/null +++ b/runtime/tests/test_autocompile_child_env.c @@ -0,0 +1,60 @@ +/* Real child launch: private TEMP/TMP, inherited variables, parent isolation. */ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +static char s_cache_dir[512]; /* same input capacity as autocompile.c */ +#include "autocompile_child_env.h" +#define CHECK(x) do { if (!(x)) { fprintf(stderr,"FAIL line %d: %s\n",__LINE__,#x); return 1; } } while (0) +int main(int argc, char **argv) { + char temp[1024], tmp[1024]; + if (argc == 3 && !strcmp(argv[1], "--child")) { + CHECK(GetEnvironmentVariableA("TEMP", temp, sizeof(temp)) > 0); + CHECK(GetEnvironmentVariableA("TMP", tmp, sizeof(tmp)) > 0); + CHECK(!strcmp(temp, argv[2]) && !strcmp(tmp, argv[2])); + char marker[32]; + CHECK(GetEnvironmentVariableA("PSX_AC_TEMP_TEST", marker, sizeof(marker)) > 0); + CHECK(!strcmp(marker, "preserved")); + return 0; + } + char cwd[512], cache[600], expected[610], exe[1024], cmd[2300]; + CHECK(GetCurrentDirectoryA(sizeof(cwd), cwd) > 0); + CHECK(GetEnvironmentVariableA("TEMP", temp, sizeof(temp)) > 0); + CHECK(GetEnvironmentVariableA("TMP", tmp, sizeof(tmp)) > 0); + CHECK(SetEnvironmentVariableA("PSX_AC_TEMP_TEST", "preserved")); + snprintf(cache, sizeof(cache), "%s\\autocompile-child-cache", cwd); + CHECK(CreateDirectoryA(cache, NULL) || GetLastError() == ERROR_ALREADY_EXISTS); + char *env = ac_child_environment(cache); + CHECK(env != NULL); + int temps = 0, tmps = 0; + const char *previous = NULL; + for (char *p = env; *p; p += strlen(p) + 1) { + CHECK(!previous || _stricmp(previous, p) <= 0); + if (!_strnicmp(p, "TEMP=", 5)) ++temps; + if (!_strnicmp(p, "TMP=", 4)) ++tmps; + previous = p; + } + CHECK(temps == 1 && tmps == 1); + snprintf(expected, sizeof(expected), "%s\\tmp", cache); + CHECK(GetModuleFileNameA(NULL, exe, sizeof(exe)) > 0); + snprintf(cmd, sizeof(cmd), "\"%s\" --child \"%s\"", exe, expected); + STARTUPINFOA si = {0}; PROCESS_INFORMATION pi = {0}; si.cb = sizeof(si); + CHECK(CreateProcessA(NULL, cmd, NULL, NULL, FALSE, CREATE_NO_WINDOW, + env, cwd, &si, &pi)); + free(env); + CHECK(WaitForSingleObject(pi.hProcess, 10000) == WAIT_OBJECT_0); + DWORD code = 1; CHECK(GetExitCodeProcess(pi.hProcess, &code)); + CloseHandle(pi.hThread); CloseHandle(pi.hProcess); CHECK(code == 0); + char after[1024]; + CHECK(GetEnvironmentVariableA("TEMP", after, sizeof(after)) > 0 && !strcmp(after,temp)); + CHECK(GetEnvironmentVariableA("TMP", after, sizeof(after)) > 0 && !strcmp(after,tmp)); + /* A file occupying /tmp must refuse, never inherit another TEMP. */ + snprintf(cache, sizeof(cache), "%s\\autocompile-blocked-cache", cwd); + CHECK(CreateDirectoryA(cache, NULL) || GetLastError() == ERROR_ALREADY_EXISTS); + snprintf(expected, sizeof(expected), "%s\\tmp", cache); + FILE *file = fopen(expected, "wb"); CHECK(file != NULL); fclose(file); + CHECK(ac_child_environment(cache) == NULL); + puts("PASS: compiler child gets cache scratch; parent TEMP/TMP stay unchanged; invalid scratch refuses"); + return 0; +} From 44a51191d9d356d2083ceea48b36c3516d26ab3e Mon Sep 17 00:00:00 2001 From: Shokunin <30949000+tetrisgm@users.noreply.github.com> Date: Wed, 21 Oct 2026 14:45:27 -0700 Subject: [PATCH 09/29] [video] match_display_max_lines: opt-in cap on Match display height (#722) --- docs/config_schema.md | 8 +++++ generated/OpenBIOS.emitter.sha | 2 +- recompiler/src/config_loader.cpp | 8 +++++ recompiler/src/config_loader.h | 3 ++ .../tests/video_enhancement_settings_test.cpp | 11 +++++++ runtime/src/main.cpp | 33 +++++++++++++------ 6 files changed, 54 insertions(+), 11 deletions(-) diff --git a/docs/config_schema.md b/docs/config_schema.md index f6ffd56e..cdce0fcc 100644 --- a/docs/config_schema.md +++ b/docs/config_schema.md @@ -702,8 +702,16 @@ player's `settings.toml`, and a game may ship a default the same way: [video] internal_resolution = "4k" # native | 720p | 1080p | 1440p | 4k | 5k | 8k | display, or a number of lines resolution_reference_lines = 240 # game.toml only: the title's usual display height (120..1024) +match_display_max_lines = 0 # game.toml only: cap on the display height Match display uses (0 = none, 240..8640) ``` +`match_display_max_lines` caps the monitor height that Match display, its +dynamic-resolution floor (`display`) and graphics-preset detection resolve +against. With no cap, an 8K panel asks 18x (27x with `supersample = 1.5`), which is +past the full-VRAM surface. The high-resolution window then engages, and +dynamic resolution cannot step there. A title whose GPU budget is set for +4K lines sets 2160. + A preset is a target height. The runtime renders at the integer scale `S = ceil(target / resolution_reference_lines)`: at the default 240 lines, 720p is 3x, 1080p is 5x (1200 lines, area-resolved to 1080), 1440p 6x, 4K 9x, diff --git a/generated/OpenBIOS.emitter.sha b/generated/OpenBIOS.emitter.sha index 31ebf084..e8134784 100644 --- a/generated/OpenBIOS.emitter.sha +++ b/generated/OpenBIOS.emitter.sha @@ -1 +1 @@ -8d1ed472c570e347918c0097fc6703a8707b1751fb3c8e93fa254454d7fa38ed +75a8d9834efb1c994169a939fbe043073b1b4b133bfe7b0235b80338af7f2f08 diff --git a/recompiler/src/config_loader.cpp b/recompiler/src/config_loader.cpp index a8cb2764..46161cf4 100644 --- a/recompiler/src/config_loader.cpp +++ b/recompiler/src/config_loader.cpp @@ -772,6 +772,14 @@ static RuntimeConfig parse_runtime_block(const toml::value& cfg, const fs::path& } rt.video_resolution_reference_lines = static_cast(n); } + if (video.contains("match_display_max_lines")) { + const auto n = toml::find(video, "match_display_max_lines"); + if (n != 0 && (n < 240 || n > 8640)) { + throw std::runtime_error(fmt::format( + "[video] match_display_max_lines must be 0 (no cap) or 240..8640: {}", n)); + } + rt.video_match_display_max_lines = static_cast(n); + } if (video.contains("window_width")) { const auto n = toml::find(video, "window_width"); if (n < 640 || n > 7680) { diff --git a/recompiler/src/config_loader.h b/recompiler/src/config_loader.h index 3766dffd..7fb4375b 100644 --- a/recompiler/src/config_loader.h +++ b/recompiler/src/config_loader.h @@ -469,6 +469,9 @@ struct RuntimeConfig { // preset divides into (S = ceil(target / reference)). 240 for NTSC // 320x240 games; 120..1024. int video_resolution_reference_lines = 240; + /* [video] match_display_max_lines: cap on the display height Match + * display (and its dynamic-resolution floor) resolves against; 0 = none. */ + int video_match_display_max_lines = 0; // Optional initial window width declared by the title profile. Zero keeps // the historical fit-to-display behavior; player settings may override it. diff --git a/recompiler/tests/video_enhancement_settings_test.cpp b/recompiler/tests/video_enhancement_settings_test.cpp index 33dda58c..002bbd8f 100644 --- a/recompiler/tests/video_enhancement_settings_test.cpp +++ b/recompiler/tests/video_enhancement_settings_test.cpp @@ -224,6 +224,16 @@ static void test_internal_resolution_game_toml() { "internal_resolution defaults unset (supersampling stands)"); check(gc.runtime.video_resolution_reference_lines == 240, "resolution_reference_lines defaults to 240"); + check(gc.runtime.video_match_display_max_lines == 0, + "match_display_max_lines defaults to 0 (no cap)"); + fs::remove(p); + + p = write_game_toml("psxrecomp_ir_match_cap.toml", + "[video]\n" + "internal_resolution = \"display\"\n" + "match_display_max_lines = 2160\n"); + gc = PSXRecompV4::load_game_config(p); + check(gc.runtime.video_match_display_max_lines == 2160, "match_display_max_lines parses"); fs::remove(p); p = write_game_toml("psxrecomp_ir_4k.toml", @@ -246,6 +256,7 @@ static void test_internal_resolution_game_toml() { for (const char* bad : { "internal_resolution = \"9k\"\n", "internal_resolution = 1\n", "resolution_reference_lines = 50\n", + "match_display_max_lines = 100\n", "supersampling = 33\n" }) { p = write_game_toml("psxrecomp_ir_bad.toml", std::string("[video]\n") + bad); bool rejected = false; diff --git a/runtime/src/main.cpp b/runtime/src/main.cpp index 13309dd1..026be2ec 100644 --- a/runtime/src/main.cpp +++ b/runtime/src/main.cpp @@ -1265,6 +1265,18 @@ static int g_video_post_aa = 0; /* [video] antialiasing_mode / PSX_A static float g_video_bloom = 0.0f; /* [video] bloom (PSX_BLOOM overrides) */ static std::string g_video_post_shader; /* [video] post_shader (PSX_POST_SHADER overrides) */ static int g_video_ss_milli = 1000; /* [video] supersample / PSX_SUPERSAMPLE */ + +/* [video] match_display_max_lines (0 = none): the display height Match + * display, its dynamic-resolution floor and the quality detection resolve + * against, capped. On an 8K panel Match display x1.5 asks 27x, past the + * full-VRAM surface, where the high-resolution window engages and dynamic + * resolution cannot step; a title can cap it at, say, 4K lines. */ +static int g_video_match_max_lines = 0; +static int match_display_px_h(SDL_Window *window) { + const int h = psx_sdl_display_pixel_height(window); + return (g_video_match_max_lines > 0 && h > g_video_match_max_lines) + ? g_video_match_max_lines : h; +} /* FMV present reconstruction (VIDEO_FMV_FILTER_*), pushed to the GL renderer * once the config is resolved. Only consulted while g_video_aa is on. */ static int g_video_fmv_filter = PSXRecompV4::VIDEO_FMV_FILTER_DEFAULT; @@ -10194,7 +10206,7 @@ static void dynres_setup(void) { int floor_s = psx_dynres_floor_scale(dynres_min_value(), effective_internal_resolution(), g_video_ref_lines, - psx_sdl_display_pixel_height(nullptr), + match_display_px_h(nullptr), ceiling); if (floor_s > ceiling) floor_s = ceiling; DynresParams params; @@ -16470,7 +16482,7 @@ namespace { void quality_fill_game_info(RecompLauncherCGameInfo* gi) { if (!g_quality_offered) return; quality_publish_strings(); - g_quality_display_h = psx_sdl_display_pixel_height(nullptr); + g_quality_display_h = match_display_px_h(nullptr); gi->quality_offered_mask = (int)g_quality_offered; gi->quality_detected = (g_quality.detected >= 0 && g_quality.detected < PSX_QUALITY_COUNT) ? g_quality.detected + 1 : 0; @@ -17102,7 +17114,7 @@ static void recomp_overlay_setup(const char* argv0, const std::string& title) { g_ovl_q_reason = g_quality.reason; g_ovl_q_detected = (g_quality.detected >= 0 && g_quality.detected < PSX_QUALITY_COUNT) ? g_quality.detected + 1 : 0; - g_quality_display_h = psx_sdl_display_pixel_height(nullptr); + g_quality_display_h = match_display_px_h(nullptr); { std::lock_guard lk(g_ovl_mu); g_ovl_q_sel.reset(); @@ -17847,6 +17859,7 @@ int main(int argc, char** argv) { gc.runtime.video_depth24_trailing_margin; g_video_internal_res = gc.runtime.video_internal_resolution; g_video_ref_lines = gc.runtime.video_resolution_reference_lines; + g_video_match_max_lines = gc.runtime.video_match_display_max_lines; g_video_dynres = gc.runtime.video_dynamic_resolution ? 1 : 0; g_video_dynres_min = gc.runtime.video_dynamic_resolution_min; if (gc.runtime.video_window_width > 0) { @@ -19009,7 +19022,7 @@ int main(int argc, char** argv) { const int ir_preset_seeded = g_video_internal_res; const int ir_ss_seeded = psx_ir_launcher_seed_supersampling( kLauncherHasInternalResolution, g_video_internal_res, g_video_scale, - g_video_ref_lines, psx_sdl_display_pixel_height(nullptr)); + g_video_ref_lines, match_display_px_h(nullptr)); seed.supersampling = ir_ss_seeded; seed.has_supersampling = true; int ir_row_result = PSX_IR_UNSET; /* the row's pick, when it has one */ #if defined(RECOMP_LAUNCHER_HAS_INTERNAL_RESOLUTION) @@ -19885,7 +19898,7 @@ int main(int argc, char** argv) { const PsxIrAdopted ir = psx_ir_adopt_launcher( kLauncherHasInternalResolution, ir_preset_seeded, ir_ss_seeded, seed.supersampling, ir_row_result, g_video_ref_lines, - psx_sdl_display_pixel_height(nullptr), video_scale_ceiling()); + match_display_px_h(nullptr), video_scale_ceiling()); g_video_internal_res = ir.preset; g_video_scale = ir.scale; seed.supersampling = ir.save_ss; @@ -20340,7 +20353,7 @@ session_reboot: if (g_video_ss_milli != 1000) std::fprintf(stdout, "psxrecomp: supersample %.3gx on the internal-resolution target\n", g_video_ss_milli / 1000.0); - apply_internal_resolution(psx_sdl_display_pixel_height(nullptr)); + apply_internal_resolution(match_display_px_h(nullptr)); if (g_video_scale < 1) g_video_scale = 1; if (const char* e = std::getenv("PSX_DYNRES")) g_video_dynres_env = (*e && *e != '0') ? 1 : 0; @@ -20981,7 +20994,7 @@ session_reboot: * which monitor. glb_set_scale only records the request; the hr * surface is allocated at context init below. */ if (effective_internal_resolution() == PSX_IR_DISPLAY && g_video_scale_applies) { - const int dh = psx_sdl_display_pixel_height(sdl_window); + const int dh = match_display_px_h(sdl_window); const int s = psx_resolve_internal_scale_ss(PSX_IR_DISPLAY, g_video_ref_lines, dh, GL_MAX_INTERNAL_SCALE, g_video_ss_milli); @@ -21002,7 +21015,7 @@ session_reboot: if (dbg_live_scale) { const int ds = psx_resolve_internal_scale( PSX_IR_DISPLAY, g_video_ref_lines, - psx_sdl_display_pixel_height(sdl_window), GL_MAX_INTERNAL_SCALE); + match_display_px_h(sdl_window), GL_MAX_INTERNAL_SCALE); if (ds > dbg_start_scale) gr_set_scale(ds); } #else @@ -21847,7 +21860,7 @@ soft_return_lobby: const int ir_preset_seeded = g_video_internal_res; const int ir_ss_seeded = psx_ir_launcher_seed_supersampling( kLauncherHasInternalResolution, g_video_internal_res, g_video_scale, - g_video_ref_lines, psx_sdl_display_pixel_height(nullptr)); + g_video_ref_lines, match_display_px_h(nullptr)); ls.supersampling = ir_ss_seeded; #if defined(RECOMP_LAUNCHER_HAS_INTERNAL_RESOLUTION) ls.internal_resolution = internal_resolution_for_launcher(); @@ -22177,7 +22190,7 @@ soft_return_lobby: const PsxIrAdopted ir = psx_ir_adopt_launcher( kLauncherHasInternalResolution, ir_preset_seeded, ir_ss_seeded, ls.supersampling, ir_row_result, g_video_ref_lines, - psx_sdl_display_pixel_height(nullptr), + match_display_px_h(nullptr), ls.renderer == 1 ? GL_MAX_INTERNAL_SCALE : SW_MAX_INTERNAL_SCALE); /* Persist controller (and rematch video) choices without wiping * the rest of settings.toml — merge into the on-disk file. */ From f521768fd0b6c2a4248e05233d87c8aa6c60e6e8 Mon Sep 17 00:00:00 2001 From: Shokunin <30949000+tetrisgm@users.noreply.github.com> Date: Wed, 21 Oct 2026 14:45:33 -0700 Subject: [PATCH 10/29] Reprojection: correct depth, two-frame warp, no un-warped fills (#724) --- runtime/include/frame_gen.h | 5 + runtime/src/frame_gen.c | 20 ++ runtime/src/gpu_gl_renderer.c | 433 +++++++++++++++++++++++++++--- runtime/tests/run_gl_frame_gen.py | 17 ++ runtime/tests/test_frame_gen.c | 28 ++ runtime/tests/test_gl_frame_gen.c | 116 ++++++++ 6 files changed, 588 insertions(+), 31 deletions(-) diff --git a/runtime/include/frame_gen.h b/runtime/include/frame_gen.h index 587ed518..b987e7ee 100644 --- a/runtime/include/frame_gen.h +++ b/runtime/include/frame_gen.h @@ -57,6 +57,8 @@ typedef struct FgPrim { float x[3], y[3]; /* native px, relative to the list's display origin */ float p[3][3]; /* camera-space positions (GTE units) */ float h[3]; /* projection distance */ + float pz[3]; /* PGXP depth (GTE SZ) per vertex when the renderer had it, else 0: + * depth for geometry without a projection source */ } FgPrim; typedef struct FgPrimList { @@ -151,6 +153,9 @@ double fg_step_s(double flip_s, double refresh_hz, int n); double fg_clock_phase(uint64_t since_real_ns, uint64_t step_ns, uint64_t flip_ns); /* View vi's in-between camera at phase t as P_t = A P + b (row-major A). */ int fg_view_affine(const FgCamFit *fit, int vi, double t, float A[9], float b[3]); +/* The older frame's camera at phase t (P_t = A P_old + b): for warping the + * older frame's own pixels to the in-between camera. */ +int fg_view_affine_older(const FgCamFit *fit, int vi, double t, float A[9], float b[3]); void fg_hud_match(const FgPrimList *L, const FgPrimList *O, float max_px, uint8_t *moved); /* HUD motion between two frames' 2D triangles (frame_gen.c). */ void fg_hud_lerp(const FgPrimList *L, const FgPrimList *O, double u, float *x, float *y, diff --git a/runtime/src/frame_gen.c b/runtime/src/frame_gen.c index 5c4b1580..db265d36 100644 --- a/runtime/src/frame_gen.c +++ b/runtime/src/frame_gen.c @@ -923,3 +923,23 @@ int fg_view_affine(const FgCamFit *fit, int vi, double t, float A[9], float b[3] b[r] = (float)(t * v->t[r] - (Am[3 * r] * v->t[0] + Am[3 * r + 1] * v->t[1] + Am[3 * r + 2] * v->t[2])); return 1; } + +/* The older frame's camera at phase t: P_t = Rt P_old + t T (the fit's + * rotation interpolated from identity, its translation scaled), so the + * older frame's own pixels can be warped to the in-between camera too. */ +int fg_view_affine_older(const FgCamFit *fit, int vi, double t, float A[9], float b[3]) { + if (!fit || vi < 0 || (uint32_t)vi >= fit->nviews) return 0; + const FgView *v = &fit->v[vi]; + const double w = fmin(1.0, fabs(v->q[0])); + const double ang = 2.0 * acos(w), sn = sqrt(fmax(0.0, 1.0 - w * w)); + double qt[4] = { 1, 0, 0, 0 }, Rt[9]; + if (sn > 1e-9) { + const double sg = v->q[0] < 0 ? -1.0 : 1.0; + qt[0] = cos(0.5 * ang * t); + for (int k = 0; k < 3; k++) qt[k + 1] = sg * v->q[k + 1] / sn * sin(0.5 * ang * t); + } + q_to_m(qt, Rt); + for (int k = 0; k < 9; k++) A[k] = (float)Rt[k]; + for (int r = 0; r < 3; r++) b[r] = (float)(t * v->t[r]); + return 1; +} diff --git a/runtime/src/gpu_gl_renderer.c b/runtime/src/gpu_gl_renderer.c index dab81837..098646d3 100644 --- a/runtime/src/gpu_gl_renderer.c +++ b/runtime/src/gpu_gl_renderer.c @@ -11541,7 +11541,7 @@ typedef struct { } FgList; static int rp_is_car(const FgList *L, uint32_t pi); /* Raw triangle captured while recording a list (absolute coords). */ -typedef struct { uint32_t rec, key0, vid[3]; float x[3], y[3], p[3][3], h[3]; int area[4]; } FgRaw; +typedef struct { uint32_t rec, key0, vid[3]; float x[3], y[3], p[3][3], h[3], pz[3]; int area[4]; } FgRaw; static int s_fg_on = 0; /* configured (any thread reads) */ static int s_fg_force = 0; /* PSX_FRAME_GEN_FORCE: tests */ @@ -11554,6 +11554,7 @@ static int s_fg_older = 0, s_fg_newer = 1, s_fg_cur = 2; static int s_fg_pa = -1, s_fg_pb = -1; /* the pair a schedule draws from */ static FgRaw *s_fg_raw = NULL; static uint32_t s_fg_nraw = 0, s_fg_rawcap = 0; static int s_fg_pc_valid = 0; static int32_t s_fg_pc[6]; /* pending PRECISE */ +static int s_fg_dz_valid = 0; static float s_fg_dz[3]; /* pending PGXP DEPTH */ static int s_fg_have_last = 0, s_fg_last_dx = 0, s_fg_last_dy = 0; /* Display buffers seen in presents (x, y, w, h), and the one the capturing * list draws into: a list is one game frame's drawing into one buffer, closed @@ -11949,9 +11950,17 @@ static void fg_capture(const RtCmd *c, const void *payload) { if (s_fg_pc_valid) memcpy(s_fg_pc, v + 1, sizeof s_fg_pc); return; } + if (c->op == RTH_DEPTH) { /* PGXP per-vertex depth of the next triangle */ + const int32_t *v = (const int32_t *)payload; + s_fg_dz_valid = v[0] != 0; + if (s_fg_dz_valid) memcpy(s_fg_dz, v + 1, sizeof s_fg_dz); + return; + } FgRaw raw; if (fg_tri_geom(c->op, payload, s_fg_pc_valid ? s_fg_pc : NULL, raw.x, raw.y, &raw.key0)) { raw.rec = (uint32_t)r; + for (int k = 0; k < 3; k++) + raw.pz[k] = (s_fg_dz_valid && s_fg_dz[k] >= 1.0f) ? s_fg_dz[k] : 0.0f; /* The identities belong to this triangle only if it is drawn where * they were looked up (a culled triangle leaves its record behind). */ for (int k = 0; k < 3; k++) raw.vid[k] = 0; @@ -11974,6 +11983,7 @@ static void fg_capture(const RtCmd *c, const void *payload) { case RTH_FLAT_TRI: case RTH_GOURAUD_TRI: case RTH_TEX_TRI: case RTH_SHADED_TEX_TRI: case RTH_PROJ_TRI: s_fg_pc_valid = 0; + s_fg_dz_valid = 0; s_fg_src_valid = 0; break; default: break; @@ -12011,6 +12021,7 @@ static void fg_list_close(FgList *l, const int disp[4]) { p.vid[k] = r->vid[k]; memcpy(p.p[k], r->p[k], sizeof p.p[k]); p.h[k] = r->h[k]; + p.pz[k] = r->pz[k]; } for (int k = 0; k < 3; k++) { p.x[k] = r->x[k] - (float)disp[0]; p.y[k] = r->y[k] - (float)disp[1]; } if (!fg_prims_add(&l->prims, &p)) { l->valid = 0; return; } @@ -12440,12 +12451,17 @@ static void fg_compose(const FgList *b, GLuint wide_fbo, GLuint wide_tex, int li * allocates none of this. The object rules below (cars, needles, small * views) were tuned on R4 (docs/FRAME_GENERATION.md). */ static GLuint s_rp_zprog = 0, s_rp_wprog = 0, s_rp_vao = 0, s_rp_vbo = 0, s_rp_evao = 0; -static GLuint s_rp_ztex = 0, s_rp_zfbo = 0; +static GLuint s_rp_ztex = 0, s_rp_zfbo = 0, s_rp_zrb = 0; /* zrb: the depth image's own depth test */ +/* The older frame's depth image (two-frame warp): same size and format. */ +static GLuint s_rp_ztex2 = 0, s_rp_zfbo2 = 0, s_rp_zrb2 = 0; +static int s_rp_zw2 = 0, s_rp_zh2 = 0; +static uint64_t s_rp_key2 = 0, s_rp_older_frames = 0; static int s_rp_zw = 0, s_rp_zh = 0; static float *s_rp_v = NULL; static size_t s_rp_vcap = 0; static uint64_t s_rp_key = 0; /* the game frame the depth image is of */ static uint64_t s_rp_frames = 0, s_rp_fallback = 0; static GLint s_rp_z_uSize = -1; +static GLint s_rp_w_uDbias = -1; static GLint s_rp_w_uZ, s_rp_w_uZsize, s_rp_w_uZs, s_rp_w_uGrid, s_rp_w_uSize, s_rp_w_uOx, s_rp_w_uView, s_rp_w_uM, s_rp_w_uSrc, s_rp_w_uSrcOrg, s_rp_w_uS, s_rp_w_uDbg; static GLint s_rp_r_uSrc, s_rp_r_uZ, s_rp_r_uZsize, s_rp_r_uOrg, s_rp_r_uSrcOrg, s_rp_r_uS, @@ -12463,7 +12479,8 @@ static const char *RP_Z_VS = "uniform vec2 u_size;\n" "out float v_iz; flat out float v_id; flat out float v_view;\n" "void main(){ v_iz=a.z; v_id=a.w; v_view=av;\n" - " gl_Position=vec4(a.x/u_size.x*2.0-1.0, a.y/u_size.y*2.0-1.0, 0.0, 1.0); }\n"; + " float d = a.z > 0.0 ? clamp((1.0/a.z)/262144.0, 0.0, 0.99999) : 0.0;\n" + " gl_Position=vec4(a.x/u_size.x*2.0-1.0, a.y/u_size.y*2.0-1.0, d*2.0-1.0, 1.0); }\n"; static const char *RP_Z_FS = "#version 330\n" "in float v_iz; flat in float v_id; flat in float v_view; out vec4 frag;\n" @@ -12472,28 +12489,38 @@ static const char *RP_W_VS = "#version 330\n" "uniform sampler2D u_z; uniform ivec2 u_zsize; uniform float u_zs;\n" "uniform ivec2 u_grid; uniform vec2 u_size; uniform vec2 u_ox;\n" - "uniform vec4 u_view[4]; uniform vec4 u_M[12];\n" + "uniform vec4 u_view[4]; uniform vec4 u_M[12]; uniform float u_dbias;\n" "out vec2 v_src; out float v_bad;\n" "vec4 zat(ivec2 g){ return texelFetch(u_z, clamp(ivec2(vec2(g)*u_zs), ivec2(0), u_zsize-1), 0); }\n" + /* A grid point moved by its own depth and view: (x, y, depth). */ + "vec3 warp(vec2 p, vec4 z){\n" + " int vi=int(z.z+0.5); vec4 V=u_view[vi]; float Z=1.0/z.x; vec2 rel=p-u_ox;\n" + " vec3 P=vec3((rel.x-V.x)*Z/V.z,(rel.y-V.y)*Z/V.z,Z);\n" + " vec4 r0=u_M[vi*3], r1=u_M[vi*3+1], r2=u_M[vi*3+2];\n" + " vec3 Pt=vec3(dot(r0.xyz,P)+r0.w, dot(r1.xyz,P)+r1.w, dot(r2.xyz,P)+r2.w);\n" + " Pt.z=max(Pt.z, max(16.0, 0.25*Z));\n" + " return vec3(vec2(V.z*Pt.x/Pt.z+V.x, V.z*Pt.y/Pt.z+V.y)+u_ox, Pt.z); }\n" "void main(){\n" " int id=gl_VertexID, cell=id/6, c=id-cell*6;\n" " int cw=u_grid.x-1; ivec2 base=ivec2(cell-(cell/cw)*cw, cell/cw);\n" " ivec2 o = (c==0)?ivec2(0,0):(c==1||c==3)?ivec2(1,0):(c==2||c==5)?ivec2(0,1):ivec2(1,1);\n" " ivec2 gp=base+o; vec2 p=vec2(gp);\n" - " float zmin=1e30, zmax=0.0; float bad=0.0; float v0=-1.0;\n" - " for(int k=0;k<4;k++){ vec4 q=zat(base+ivec2(k&1,k>>1));\n" - " if(q.y<0.5||q.y>1.5||q.x<=0.0){bad=1.0;} else { float Z=1.0/q.x; zmin=min(zmin,Z); zmax=max(zmax,Z);\n" - " if(v0<0.0) v0=q.z; else if(q.z!=v0) bad=1.0; } }\n" - " if(zmax>zmin*1.06+4.0) bad=1.0;\n" - " vec4 z=zat(gp); vec2 np=p; float depth=1.0;\n" + " float bad=0.0; float v0=-1.0; vec4 q[4];\n" + " for(int k=0;k<4;k++){ q[k]=zat(base+ivec2(k&1,k>>1));\n" + " bool wk=(q[k].y>0.5&&q[k].y<1.5)||(q[k].y>3.5&&q[k].y<4.5);\n" + " if(!wk||q[k].x<=0.0) bad=1.0; else if(v0<0.0) v0=q[k].z; else if(q[k].z!=v0) bad=1.0; }\n" + " vec2 np=p; float depth=1.0;\n" " if(bad<0.5){\n" - " int vi=int(z.z+0.5); vec4 V=u_view[vi]; float Z=1.0/z.x; vec2 rel=p-u_ox;\n" - " vec3 P=vec3((rel.x-V.x)*Z/V.z,(rel.y-V.y)*Z/V.z,Z);\n" - " vec4 r0=u_M[vi*3], r1=u_M[vi*3+1], r2=u_M[vi*3+2];\n" - " vec3 Pt=vec3(dot(r0.xyz,P)+r0.w, dot(r1.xyz,P)+r1.w, dot(r2.xyz,P)+r2.w);\n" - " Pt.z=max(Pt.z, max(16.0, 0.25*Z));\n" - " np=vec2(V.z*Pt.x/Pt.z+V.x, V.z*Pt.y/Pt.z+V.y)+u_ox;\n" - " depth=clamp(Pt.z/262144.0, 0.0, 1.0);\n" + /* A cell is an edge (not drawn) only where the warp tears it: a side + * stretched past 1.5 native px. A depth ratio test also rejected the + * inside of near surfaces seen at a grazing angle, and the un-warped + * frame showed through them (doubled text on a passing billboard). */ + " vec3 w0=warp(vec2(base),q[0]), w1=warp(vec2(base+ivec2(1,0)),q[1]),\n" + " w2=warp(vec2(base+ivec2(0,1)),q[2]), w3=warp(vec2(base+ivec2(1,1)),q[3]);\n" + " float m=max(max(distance(w0.xy,w1.xy),distance(w0.xy,w2.xy)),max(distance(w1.xy,w3.xy),distance(w2.xy,w3.xy)));\n" + " if(m>1.5) bad=1.0;\n" + " vec3 w = (o==ivec2(0,0))?w0:(o==ivec2(1,0))?w1:(o==ivec2(0,1))?w2:w3;\n" + " np=w.xy; depth=clamp(w.z*(1.0+u_dbias)/262144.0, 0.0, 0.99999);\n" " }\n" " v_bad=bad; v_src=p;\n" " gl_Position=vec4(np.x/u_size.x*2.0-1.0, np.y/u_size.y*2.0-1.0, depth*2.0-1.0, 1.0);\n" @@ -12517,7 +12544,7 @@ static const char *RP_R_FS = "void main(){ vec2 p=(gl_FragCoord.xy-u_org)/u_S;\n" " vec4 z=texelFetch(u_z, clamp(ivec2(p*2.0), ivec2(0), u_zsize-1), 0);\n" " ivec2 t=ivec2(gl_FragCoord.xy-u_org+u_srcorg*u_S);\n" " if(u_dbg==2){ frag=vec4(z.y==1.0?0.0:1.0, z.y==2.0?1.0:0.0, z.y==3.0?1.0:(z.y==0.0?0.5:0.0), 1.0); return; }\n" - " if(z.y>2.5){ frag = u_has_prev==1 ? texelFetch(u_prev, t, 0) : texelFetch(u_src, t, 0); return; }\n" + " if(z.y>2.5&&z.y<3.5){ frag = u_has_prev==1 ? texelFetch(u_prev, t, 0) : texelFetch(u_src, t, 0); return; }\n" " if(z.y>0.5) discard;\n" " frag=texelFetch(u_src, t, 0); }\n"; static GLuint s_rp_rprog = 0; @@ -12537,6 +12564,7 @@ static int rp_resources(void) { s_rp_w_uOx = p_glGetUniformLocation(s_rp_wprog, "u_ox"); s_rp_w_uView = p_glGetUniformLocation(s_rp_wprog, "u_view"); s_rp_w_uM = p_glGetUniformLocation(s_rp_wprog, "u_M"); + s_rp_w_uDbias = p_glGetUniformLocation(s_rp_wprog, "u_dbias"); s_rp_w_uSrc = p_glGetUniformLocation(s_rp_wprog, "u_src"); s_rp_w_uSrcOrg = p_glGetUniformLocation(s_rp_wprog, "u_srcorg"); s_rp_w_uS = p_glGetUniformLocation(s_rp_wprog, "u_S"); @@ -12640,6 +12668,171 @@ static int rp_is_car(const FgList *L, uint32_t pi) { * surface; the region is (x0, y0, W, H) native px, rel x = x - ox. */ static double s_rp_t_snap = 0, s_rp_t_z = 0, s_rp_t_w = 0; static uint32_t s_rp_zn = 0; +/* Geometry without a projection source (a game's own near-plane + * subdivision) is world when the renderer had its PGXP depth: the view is the + * fitted one of its draw area. -1 otherwise. */ +/* PGXP depth is the GTE's SZ, which a game may scale against the camera + * space its projected vertices carry: the scale is measured per list as the + * median camera-z / SZ over vertices that have both (1 when none do). */ +static float *s_rp_pzr = NULL; static size_t s_rp_pzr_cap = 0; +static float s_rp_pzs_last = 0.0f; /* diagnostics: the newer list's scale */ +static int rp_float_cmp(const void *a, const void *b) { + const float x = *(const float *)a, y = *(const float *)b; + return x < y ? -1 : x > y; +} +static float rp_pz_scale(const FgList *L) { + size_t n = 0; + if ((size_t)L->prims.n * 3u > s_rp_pzr_cap) { + float *nb = (float *)realloc(s_rp_pzr, (size_t)L->prims.n * 3u * sizeof *nb); + if (!nb) return 1.0f; + s_rp_pzr = nb; s_rp_pzr_cap = (size_t)L->prims.n * 3u; + } + for (uint32_t j = 0; j < L->prims.n; j++) { + const FgPrim *p = &L->prims.v[j]; + for (int k = 0; k < 3; k++) + if (p->vid[k] && p->p[k][2] >= 1.0f && p->pz[k] >= 1.0f) s_rp_pzr[n++] = p->p[k][2] / p->pz[k]; + } + if (n < 16) return 1.0f; + qsort(s_rp_pzr, n, sizeof *s_rp_pzr, rp_float_cmp); + return s_rp_pzr[n / 2]; +} +static int rp_pgxp_world_view(const FgPrim *p) { + if (p->vid[0] || p->vid[1] || p->vid[2]) return -1; + if (p->pz[0] < 1.0f || p->pz[1] < 1.0f || p->pz[2] < 1.0f) return -1; + for (uint32_t vi = 0; vi < s_fg_fit.nviews && vi < FG_MAX_VIEWS; vi++) + if (s_fg_fit.v[vi].view == p->view && s_fg_fit.v[vi].ok) return (int)vi; + return -1; +} + +/* Two-frame warp: the older frame's own depth image. Its world is what the + * fit explains by the camera: a vertex with a projection in a fitted view, + * not one the fit called an object in the newer frame (cars are drawn again + * at their in-between position). Vertices only the older frame has (what + * the newer frame's nearer geometry now covers) are world too: they are what + * fills the newer frame's disocclusions. Nearest world depth wins, the rest + * keeps painter order as id 0 (not warped). */ +typedef struct { uint32_t vid; int8_t mode, view; } RpVidMode; +static RpVidMode *s_rp_vm = NULL; static size_t s_rp_vmcap = 0; +static int rp_vm_cmp(const void *a, const void *b) { + const uint32_t x = ((const RpVidMode *)a)->vid, y = ((const RpVidMode *)b)->vid; + return x < y ? -1 : x > y; +} +static int rp_build_older(const FgList *L, const FgList *O, float qox, int zw, int zh, float big) { + if (!O || !O->valid || !O->rec2prim) return 0; + /* The newer frame's placement by projection identity. */ + size_t nvm = 0; + const size_t need = (size_t)L->prims.n * 3u; + if (need > s_rp_vmcap) { + RpVidMode *nv = (RpVidMode *)realloc(s_rp_vm, need * sizeof *nv); + if (!nv) return 0; + s_rp_vm = nv; s_rp_vmcap = need; + } + for (uint32_t j = 0; j < L->prims.n; j++) + for (int k = 0; k < 3; k++) { + const uint32_t vid = L->prims.v[j].vid[k]; + if (!vid) continue; + const FgVert *fv = &s_fg_verts[3 * j + k]; + s_rp_vm[nvm].vid = vid; s_rp_vm[nvm].mode = (int8_t)fv->mode; s_rp_vm[nvm].view = fv->view; + nvm++; + } + qsort(s_rp_vm, nvm, sizeof *s_rp_vm, rp_vm_cmp); + if (zw != s_rp_zw2 || zh != s_rp_zh2) { + if (s_rp_zfbo2) { p_glDeleteFramebuffers(1, &s_rp_zfbo2); glDeleteTextures(1, &s_rp_ztex2); } + if (s_rp_zrb2) { p_glDeleteRenderbuffers(1, &s_rp_zrb2); s_rp_zrb2 = 0; } + s_rp_ztex2 = make_tex(0x8814 /* GL_RGBA32F */, zw, zh, GL_RGBA, GL_FLOAT); + p_glGenRenderbuffers(1, &s_rp_zrb2); + p_glBindRenderbuffer(PSXGL_RENDERBUFFER, s_rp_zrb2); + p_glRenderbufferStorage(PSXGL_RENDERBUFFER, PSXGL_DEPTH24_STENCIL8, zw, zh); + p_glBindRenderbuffer(PSXGL_RENDERBUFFER, 0); + if (!make_fbo(&s_rp_zfbo2, s_rp_ztex2, s_rp_zrb2)) { s_rp_zfbo2 = 0; s_rp_zw2 = s_rp_zh2 = 0; return 0; } + s_rp_zw2 = zw; s_rp_zh2 = zh; + } + size_t n = 0; + const float pzs_old = rp_pz_scale(O); + for (uint32_t r = 0; r < O->n; r++) { + FgRec h; + memcpy(&h, O->buf + O->off[r], sizeof h); + const int32_t *v = (const int32_t *)(O->buf + O->off[r] + sizeof h); + if (h.op == RTH_FLAT_RECT || h.op == RTH_TEX_RECT || h.op == RTH_TEX_RECT_SCALED) { + if (!rp_push_quad(&n, ((float)(v[0] - O->disp[0]) + qox) * RP_ZS, (float)(v[1] - O->disp[1]) * RP_ZS, + ((float)(v[0] - O->disp[0] + v[2]) + qox) * RP_ZS, + (float)(v[1] - O->disp[1] + v[3]) * RP_ZS)) return 0; + continue; + } + if (h.op != RTH_FLAT_TRI && h.op != RTH_GOURAUD_TRI && h.op != RTH_TEX_TRI && + h.op != RTH_SHADED_TEX_TRI && h.op != RTH_PROJ_TRI) continue; + const int pi = O->rec2prim[r]; + if (pi < 0) continue; + const FgPrim *p = &O->prims.v[pi]; + int world = 1, view = -1, objv = 0; + const int pgv = rp_pgxp_world_view(p); + for (int k = 0; k < 3 && pgv < 0; k++) { + if (!p->vid[k] || p->p[k][2] < 1.0f) { world = 0; break; } + RpVidMode key; key.vid = p->vid[k]; + const RpVidMode *m = (const RpVidMode *)bsearch(&key, s_rp_vm, nvm, sizeof *s_rp_vm, rp_vm_cmp); + int vk = -1; + if (m) { + if (m->mode == FG_PLACE_OBJECT) { world = 0; objv = 1; break; } + vk = m->view; + } else { + for (uint32_t vi = 0; vi < s_fg_fit.nviews; vi++) + if (s_fg_fit.v[vi].view == p->view) { vk = (int)vi; break; } + } + if (vk < 0) { world = 0; break; } + if (view < 0) view = vk; else if (view != vk) { world = 0; break; } + } + if (pgv >= 0) view = pgv; + if (world && (view < 0 || view >= FG_MAX_VIEWS || !s_fg_fit.v[view].ok)) world = 0; + if (world) { + const float *ar = s_fg_fit.v[view].area; + if ((ar[2] - ar[0]) * (ar[3] - ar[1]) < 0.15f * big) world = 0; + } + /* 1 world, 4 a big moving object (sky dome: warped as infinitely + * far), 2 a car (drawn again), 0 the rest (2D, HUD). */ + int far_obj = 0, fview = -1; + if (objv) { + const float bw = fmaxf(p->x[0], fmaxf(p->x[1], p->x[2])) - fminf(p->x[0], fminf(p->x[1], p->x[2])); + const float bh = fmaxf(p->y[0], fmaxf(p->y[1], p->y[2])) - fminf(p->y[0], fminf(p->y[1], p->y[2])); + if (bw > 96.0f || bh > 64.0f) + for (uint32_t vi = 0; vi < s_fg_fit.nviews; vi++) + if (s_fg_fit.v[vi].view == p->view && s_fg_fit.v[vi].ok) { fview = (int)vi; far_obj = 1; break; } + } + const float id = world ? 1.0f : far_obj ? 4.0f : objv ? 2.0f : 0.0f; + for (int k = 0; k < 3; k++) + if (!rp_push(&n, (p->x[k] + qox) * RP_ZS, p->y[k] * RP_ZS, + world ? 1.0f / (pgv >= 0 ? p->pz[k] * pzs_old : p->p[k][2]) : far_obj ? 1e-6f : 0.0f, id, + world ? (float)view : far_obj ? (float)fview : 0.0f)) return 0; + } + p_glBindFramebuffer(PSXGL_FRAMEBUFFER, s_rp_zfbo2); + glViewport(0, 0, zw, zh); + glClearColor(0.f, 0.f, 0.f, 0.f); + glClearDepth(1.0); + glDepthMask(GL_TRUE); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + if (n) { + p_glUseProgram(s_rp_zprog); + p_glUniform2f(s_rp_z_uSize, (float)zw, (float)zh); + p_glBindVertexArray(s_rp_vao); + p_glBindBuffer(PSXGL_ARRAY_BUFFER, s_rp_vbo); + p_glBufferData(PSXGL_ARRAY_BUFFER, (GLsizeiptr)(n * 5 * sizeof(float)), s_rp_v, PSXGL_STREAM_DRAW); + glEnable(GL_DEPTH_TEST); + size_t r0 = 0; + while (r0 < n) { + const int w0 = s_rp_v[r0 * 5 + 3] == 1.0f || s_rp_v[r0 * 5 + 3] == 4.0f; + size_t r1 = r0 + 3; + while (r1 < n && (s_rp_v[r1 * 5 + 3] == 1.0f || s_rp_v[r1 * 5 + 3] == 4.0f) == w0) r1 += 3; + glDepthFunc(w0 ? GL_LESS : GL_ALWAYS); + glDepthMask(w0 ? GL_TRUE : GL_FALSE); + glDrawArrays(GL_TRIANGLES, (GLint)r0, (GLsizei)(r1 - r0)); + r0 = r1; + } + glDepthMask(GL_TRUE); + glDepthFunc(GL_LESS); + glDisable(GL_DEPTH_TEST); + } + return 1; +} + static int fg_reproject(const FgList *L, const FgList *O, double t, GLuint dst_fbo, GLuint src_fbo, int x0, int y0, int W, int H, float ox, int S) { if (!rp_resources() || W <= 0 || H <= 0) return 0; @@ -12668,15 +12861,21 @@ static int fg_reproject(const FgList *L, const FgList *O, double t, GLuint dst_f const int ok_ = O ? rp_snap_slot((int)(O - s_fg_l)) : -1; const int ok = (ok_ >= 0 && s_rp_ls_w[ok_] == W * S && s_rp_ls_h[ok_] == H * S) ? ok_ : -1; (void)src_fbo; - /* The picture under the warp: the snapshot. */ - fg_blit2(s_rp_snap_fbo, dst_fbo, 0, 0, x0 * S, y0 * S, W * S, H * S); + /* The picture under the warp, where neither frame's warp is valid: the + * nearest real frame (never a mix). */ + fg_blit2((t < 0.5 && ok >= 0) ? s_rp_ls_fbo[ok] : s_rp_snap_fbo, dst_fbo, 0, 0, x0 * S, y0 * S, W * S, H * S); const uint64_t qz = host_now_ns_rthf(); if (key != s_rp_key || zw != s_rp_zw || zh != s_rp_zh) { if (zw != s_rp_zw || zh != s_rp_zh) { if (s_rp_zfbo) { p_glDeleteFramebuffers(1, &s_rp_zfbo); glDeleteTextures(1, &s_rp_ztex); } + if (s_rp_zrb) { p_glDeleteRenderbuffers(1, &s_rp_zrb); s_rp_zrb = 0; } fg_cost_cold(fg_cost(), 3); s_rp_ztex = make_tex(0x8814 /* GL_RGBA32F */, zw, zh, GL_RGBA, GL_FLOAT); - if (!make_fbo(&s_rp_zfbo, s_rp_ztex, 0)) { s_rp_zfbo = 0; s_rp_zw = s_rp_zh = 0; goto fail; } + p_glGenRenderbuffers(1, &s_rp_zrb); + p_glBindRenderbuffer(PSXGL_RENDERBUFFER, s_rp_zrb); + p_glRenderbufferStorage(PSXGL_RENDERBUFFER, PSXGL_DEPTH24_STENCIL8, zw, zh); + p_glBindRenderbuffer(PSXGL_RENDERBUFFER, 0); + if (!make_fbo(&s_rp_zfbo, s_rp_ztex, s_rp_zrb)) { s_rp_zfbo = 0; s_rp_zw = s_rp_zh = 0; goto fail; } s_rp_zw = zw; s_rp_zh = zh; } size_t n = 0; @@ -12687,6 +12886,8 @@ static int fg_reproject(const FgList *L, const FgList *O, double t, GLuint dst_f } fg_hud_match(&L->prims, O ? &O->prims : NULL, 24.0f, s_rp_needle); s_rp_needle_n = L->prims.n; + const float pzs_new = rp_pz_scale(L); + s_rp_pzs_last = pzs_new; for (uint32_t r = 0; r < L->n; r++) { FgRec h; memcpy(&h, L->buf + L->off[r], sizeof h); @@ -12709,6 +12910,9 @@ static int fg_reproject(const FgList *L, const FgList *O, double t, GLuint dst_f } if (world && (view < 0 || view >= FG_MAX_VIEWS || !vp[view][3] || !s_fg_fit.v[view].ok)) world = 0; + /* Source-less geometry with PGXP depth: world at that depth. */ + const int pgv = world ? -1 : rp_pgxp_world_view(p); + if (pgv >= 0 && vp[pgv][3]) { world = 1; view = pgv; } if (world) { const float *ar = s_fg_fit.v[view].area; if ((ar[2] - ar[0]) * (ar[3] - ar[1]) < 0.15f * big) world = 0; @@ -12723,11 +12927,23 @@ static int fg_reproject(const FgList *L, const FgList *O, double t, GLuint dst_f const float *ar = s_fg_fit.v[view].area; if ((ar[2] - ar[0]) * (ar[3] - ar[1]) < 0.15f * big) obj = 0; } - const float id = world ? 1.0f : obj ? 2.0f : s_rp_needle[pi] ? 3.0f : 0.0f; + /* A big object the fit moves on its own (the sky dome, a + * camera-attached backdrop; cars are small, at most 96 x 64) + * is warped as infinitely far: the camera's turn only. */ + int far_obj = 0, fview = view; + if (!world && obj) { + const float bw = fmaxf(p->x[0], fmaxf(p->x[1], p->x[2])) - fminf(p->x[0], fminf(p->x[1], p->x[2])); + const float bh = fmaxf(p->y[0], fmaxf(p->y[1], p->y[2])) - fminf(p->y[0], fminf(p->y[1], p->y[2])); + if (bw > 96.0f || bh > 64.0f) { + for (int k = 0; k < 3 && fview < 0; k++) fview = s_fg_verts[3 * pi + k].view; + far_obj = fview >= 0 && fview < FG_MAX_VIEWS && s_fg_fit.v[fview].ok && vp[fview][3] != 0.0f; + } + } + const float id = world ? 1.0f : far_obj ? 4.0f : obj ? 2.0f : s_rp_needle[pi] ? 3.0f : 0.0f; for (int k = 0; k < 3; k++) if (!rp_push(&n, (p->x[k] + ox) * RP_ZS, p->y[k] * RP_ZS, - world ? 1.0f / p->p[k][2] : 0.0f, id, - world ? (float)view : 0.0f)) goto fail; + world ? 1.0f / (pgv >= 0 ? p->pz[k] * pzs_new : p->p[k][2]) : far_obj ? 1e-6f : 0.0f, id, + world ? (float)view : far_obj ? (float)fview : 0.0f)) goto fail; break; } case RTH_FLAT_RECT: case RTH_TEX_RECT: case RTH_TEX_RECT_SCALED: @@ -12788,19 +13004,51 @@ static int fg_reproject(const FgList *L, const FgList *O, double t, GLuint dst_f p_glBindFramebuffer(PSXGL_FRAMEBUFFER, s_rp_zfbo); glViewport(0, 0, zw, zh); glClearColor(0.f, 0.f, 0.f, 0.f); - glClear(GL_COLOR_BUFFER_BIT); + glClearDepth(1.0); + glDepthMask(GL_TRUE); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (n) { p_glUseProgram(s_rp_zprog); p_glUniform2f(s_rp_z_uSize, (float)zw, (float)zh); p_glBindVertexArray(s_rp_vao); p_glBindBuffer(PSXGL_ARRAY_BUFFER, s_rp_vbo); p_glBufferData(PSXGL_ARRAY_BUFFER, (GLsizeiptr)(n * 5 * sizeof(float)), s_rp_v, PSXGL_STREAM_DRAW); - glDrawArrays(GL_TRIANGLES, 0, (GLsizei)n); + /* World triangles take the nearest depth, like the PGXP depth + * buffer the real frame was drawn with: in painter order a far + * section drawn after a near wall (the game's own sort, or Max + * Detail's far sections) left far depth on the wall's pixels, + * and the warp moved them as distant scenery (objects showing + * through walls, doubled signs, sheared lane lines). Everything + * else keeps painter order: it always passes and writes no depth + * (a backdrop under the world, a car over the road, the HUD and + * mirror overlay last). Drawn in runs of the same kind. */ + glEnable(GL_DEPTH_TEST); + size_t r0 = 0; + while (r0 < n) { + const int w0 = s_rp_v[r0 * 5 + 3] == 1.0f || s_rp_v[r0 * 5 + 3] == 4.0f; + size_t r1 = r0 + 3; + while (r1 < n && (s_rp_v[r1 * 5 + 3] == 1.0f || s_rp_v[r1 * 5 + 3] == 4.0f) == w0) r1 += 3; + glDepthFunc(w0 ? GL_LESS : GL_ALWAYS); + glDepthMask(w0 ? GL_TRUE : GL_FALSE); + glDrawArrays(GL_TRIANGLES, (GLint)r0, (GLsizei)(r1 - r0)); + r0 = r1; + } + glDepthMask(GL_TRUE); + glDepthFunc(GL_LESS); + glDisable(GL_DEPTH_TEST); } s_rp_key = key; s_rp_zn = (uint32_t)n; s_rp_t_z = (double)(host_now_ns_rthf() - qz) * 1e-6; } + /* 1b. The older frame's depth image (two-frame warp), once per pair. */ + int older = 0; + if (t < 1.0 - 1e-3 && ok >= 0 && O && O->valid && O->wide_off == L->wide_off) { + const uint64_t key2 = key * 8u + (uint64_t)(O - s_fg_l) + 1u; + if (key2 == s_rp_key2 && zw == s_rp_zw2 && zh == s_rp_zh2) older = 1; + else if (rp_build_older(L, O, ox, zw, zh, big)) { s_rp_key2 = key2; older = 1; } + else s_rp_key2 = 0; + } const uint64_t qw = host_now_ns_rthf(); /* 2. The warp. */ { @@ -12847,14 +13095,78 @@ static int fg_reproject(const FgList *L, const FgList *O, double t, GLuint dst_f p_glUniform1f(s_rp_w_uS, (float)S); p_glUniform1i(s_rp_w_uDbg, s_rp_dbg); p_glBindVertexArray(s_rp_evao); - glDrawArrays(GL_TRIANGLES, 0, (GLsizei)(W * H * 6)); + /* Both bracketing real frames are warped to the in-between camera. + * The frame nearer in time is drawn first and marks the stencil + * wherever it has a valid sample; the other frame fills only the + * unmarked pixels (what the nearer frame cannot show: a cell over a + * depth edge the camera uncovers, a pixel that is not world). The two + * never compete by depth: a valid sample of the nearer frame always + * wins (otherwise the older frame's near ceiling covered the newer + * frame's sky at a tunnel exit, and two copies of one surface a few + * px apart interleaved). Where neither is valid the nearest real + * frame underneath shows. Each frame keeps its own depth test. */ + float Mo[12][4]; memset(Mo, 0, sizeof Mo); + if (older) { + for (uint32_t vi = 0; vi < s_fg_fit.nviews && vi < FG_MAX_VIEWS; vi++) { + float A[9], b[3]; + if (!fg_view_affine_older(&s_fg_fit, (int)vi, t, A, b)) continue; + for (int r = 0; r < 3; r++) { + Mo[vi * 3 + r][0] = A[3 * r]; Mo[vi * 3 + r][1] = A[3 * r + 1]; + Mo[vi * 3 + r][2] = A[3 * r + 2]; Mo[vi * 3 + r][3] = b[r]; + } + } + s_rp_older_frames++; + } + p_glUniform1f(s_rp_w_uDbias, 0.0f); + const int older_first = older && t < 0.5; + glClearStencil(0); + glStencilMask(0xFF); + glClear(GL_STENCIL_BUFFER_BIT); + glEnable(GL_STENCIL_TEST); + for (int pass = 0; pass < 2; pass++) { + const int use_older = pass == 0 ? older_first : !older_first; + if (use_older && !older) continue; + /* At the endpoints the nearer frame underneath is exact. */ + if (use_older && t <= 1e-3) continue; + if (!use_older && older && t <= 1e-3) continue; + p_glActiveTexture(PSXGL_TEXTURE0 + 1); + glBindTexture(GL_TEXTURE_2D, use_older ? s_rp_ztex2 : s_rp_ztex); + p_glActiveTexture(PSXGL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, use_older ? s_rp_ls_tex[ok] : s_rp_snap_tex); + p_glUniform4fv(s_rp_w_uM, 12, use_older ? &Mo[0][0] : &M[0][0]); + if (pass == 0) { + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + } else { + glClear(GL_DEPTH_BUFFER_BIT); + glStencilFunc(GL_EQUAL, 0, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + } + glDrawArrays(GL_TRIANGLES, 0, (GLsizei)(W * H * 6)); + } + glDisable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 0, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + p_glActiveTexture(PSXGL_TEXTURE0 + 1); + glBindTexture(GL_TEXTURE_2D, s_rp_ztex); + p_glActiveTexture(PSXGL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, s_rp_snap_tex); glDisable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); /* The warp's depth (nearest wins, its own scale) is not the scene's: * the cars drawn next test only against each other, as on a fresh * frame (with PGXP depth they would test LEQUAL against it). */ glClear(GL_DEPTH_BUFFER_BIT); - /* 2D and HUD pixels back over the warp, where the real frame has them. */ + /* 2D and HUD pixels back over the warp, where the nearer real frame + * has them (its classification and its picture: never the other + * frame's pixels un-warped). */ + const int keep_older = older && t < 0.5; + if (keep_older) { + p_glActiveTexture(PSXGL_TEXTURE0 + 1); + glBindTexture(GL_TEXTURE_2D, s_rp_ztex2); + p_glActiveTexture(PSXGL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, s_rp_ls_tex[ok]); + } p_glUseProgram(s_rp_rprog); p_glUniform1i(s_rp_r_uSrc, 0); p_glUniform1i(s_rp_r_uZ, 1); @@ -12868,7 +13180,7 @@ static int fg_reproject(const FgList *L, const FgList *O, double t, GLuint dst_f glBindTexture(GL_TEXTURE_2D, ok >= 0 ? s_rp_ls_tex[ok] : s_rp_snap_tex); p_glActiveTexture(PSXGL_TEXTURE0); p_glUniform1i(s_rp_r_uPrev, 2); - p_glUniform1i(s_rp_r_uHasPrev, ok >= 0 && !s_rp_noprev ? 1 : 0); + p_glUniform1i(s_rp_r_uHasPrev, ok >= 0 && !s_rp_noprev && !keep_older ? 1 : 0); p_glUniform1i(s_rp_r_uDbg, s_rp_dbg); glDrawArrays(GL_TRIANGLES, 0, 3); s_rp_t_w = (double)(host_now_ns_rthf() - qw) * 1e-6; @@ -12911,6 +13223,14 @@ static void rp_free(void) { } if (s_rp_zfbo) p_glDeleteFramebuffers(1, &s_rp_zfbo); if (s_rp_ztex) glDeleteTextures(1, &s_rp_ztex); + if (s_rp_zrb) p_glDeleteRenderbuffers(1, &s_rp_zrb); + s_rp_zrb = 0; + if (s_rp_zfbo2) p_glDeleteFramebuffers(1, &s_rp_zfbo2); + if (s_rp_ztex2) glDeleteTextures(1, &s_rp_ztex2); + if (s_rp_zrb2) p_glDeleteRenderbuffers(1, &s_rp_zrb2); + s_rp_zfbo2 = s_rp_ztex2 = s_rp_zrb2 = 0; s_rp_zw2 = s_rp_zh2 = 0; s_rp_key2 = 0; + free(s_rp_vm); s_rp_vm = NULL; s_rp_vmcap = 0; + free(s_rp_pzr); s_rp_pzr = NULL; s_rp_pzr_cap = 0; s_rp_zfbo = s_rp_ztex = 0; s_rp_zw = s_rp_zh = 0; s_rp_key = 0; if (s_rp_vbo) p_glDeleteBuffers(1, &s_rp_vbo); if (s_rp_vao) p_glDeleteVertexArrays(1, &s_rp_vao); @@ -12928,16 +13248,66 @@ static void rp_free(void) { /* Tests: reprojection holds nothing (no program, surface or buffer). */ static int rp_allocated(void) { int any = s_rp_zprog || s_rp_wprog || s_rp_rprog || s_rp_vao || s_rp_vbo || s_rp_evao || - s_rp_zfbo || s_rp_ztex || s_rp_v || s_rp_needle; + s_rp_zfbo || s_rp_ztex || s_rp_v || s_rp_needle || s_rp_zfbo2 || s_rp_ztex2 || s_rp_vm; for (int k = 0; k < 2; k++) any |= s_rp_ls_fbo[k] || s_rp_ls_tex[k]; return any; } +/* Share of the main view covered by 3D geometry the generated frame cannot + * place: triangles drawn after the scene's projected geometry started that + * carry no projection source at all (a game's own near-plane subdivision + * computes those vertices without the GTE). Their depth is unknown, so + * neither a warp nor a redraw can move them with the camera; left in place + * they split from the moving geometry around them (doubled text on a + * passing billboard). Small 2D pieces (HUD) are not counted, nor geometry + * with PGXP depth (the depth images place it). */ +static uint64_t s_fg_skip_unplaced = 0; +static double fg_unplaced_share(const FgList *L) { + if (!L || !L->valid || !L->rec2prim || !s_fg_fit.nviews) return 0.0; + double big = 0.0; + for (uint32_t vi = 0; vi < s_fg_fit.nviews; vi++) { + const float *ar = s_fg_fit.v[vi].area; + const double a = (double)(ar[2] - ar[0]) * (ar[3] - ar[1]); + if (a > big) big = a; + } + if (big <= 0.0) return 0.0; + int seen3d = 0; double area = 0.0; + for (uint32_t r = 0; r < L->n; r++) { + FgRec h; + memcpy(&h, L->buf + L->off[r], sizeof h); + if (h.op != RTH_FLAT_TRI && h.op != RTH_GOURAUD_TRI && h.op != RTH_TEX_TRI && + h.op != RTH_SHADED_TEX_TRI && h.op != RTH_PROJ_TRI) continue; + const int pi = L->rec2prim[r]; + if (pi < 0) continue; + const FgPrim *p = &L->prims.v[pi]; + const int proj = p->vid[0] || p->vid[1] || p->vid[2]; + if (proj) { seen3d = 1; continue; } + if (p->pz[0] >= 1.0f && p->pz[1] >= 1.0f && p->pz[2] >= 1.0f) continue; /* PGXP depth */ + if (!seen3d) continue; /* a backdrop under the scene */ + const float bw = fmaxf(p->x[0], fmaxf(p->x[1], p->x[2])) - fminf(p->x[0], fminf(p->x[1], p->x[2])); + const float bh = fmaxf(p->y[0], fmaxf(p->y[1], p->y[2])) - fminf(p->y[0], fminf(p->y[1], p->y[2])); + if (bw <= 160.0f && bh <= 120.0f && (bw < 24.0f || bh < 24.0f)) continue; /* HUD-sized */ + area += 0.5 * fabs((double)(p->x[1] - p->x[0]) * (p->y[2] - p->y[0]) - + (double)(p->x[2] - p->x[0]) * (p->y[1] - p->y[0])); + } + return area / big; +} +/* PSX_FG_MAX_UNPLACED: the share above which the step shows the real frame + * (default 0.02; 1 = never skip). */ +static double fg_max_unplaced(void) { + static double v = -1.0; + if (v < 0.0) { const char *e = getenv("PSX_FG_MAX_UNPLACED"); v = e && *e ? atof(e) : 0.02; } + return v; +} + static int fg_generate(double t, int swap) { if (s_fg_pa < 0 || s_fg_pb < 0) return 0; FgList *a = &s_fg_l[s_fg_pa], *b = &s_fg_l[s_fg_pb]; if (!a->valid || !b->valid || s_hiw) return 0; if (b->wide && (g_wide_w <= 0 || g_wide_w != b->wide_w)) return 0; + /* No visible artifact first: geometry the camera cannot place covers + * too much of the view, so this step shows the real frame. */ + if (s_fg_reproject && s_fg_fit.ok && fg_unplaced_share(b) > fg_max_unplaced()) { s_fg_skip_unplaced++; return 0; } int iw = -1; if (b->wide) { for (int i = 0; i < WIDE_MAX_SURF; i++) @@ -13665,7 +14035,7 @@ int gl_renderer_frame_gen_json(char *out, int cap) { "\"real_cpu_ms\":%.3f,\"real_gpu_ms\":%.3f,\"ceiling\":%d," "\"trips_late\":%u,\"trips_backed_up\":%u,\"trips_behind\":%u," "\"place_camera\":%u,\"place_object\":%u,\"place_neighbour\":%u," - "\"place_unchanged\":%u,\"cam_angle_deg\":%.3f,\"cam_shift\":%.1f," + "\"place_unchanged\":%u,\"skip_unplaced\":%llu,\"rp_older\":%llu,\"pz_scale\":%.4f,\"cam_angle_deg\":%.3f,\"cam_shift\":%.1f," "\"clamped\":%u,\"guessed\":%u,\"verdict_ok\":%d,\"rejected\":%llu,\"reject_why\":\"%s\",\"noplan_why\":\"%s\",\"rp_ms\":[%.3f,%.3f,%.3f],\"rp_zn\":%u,\"reprojected\":%llu,\"reproject_fallback\":%llu,\"reproject\":%d,\"np\":[%llu,%llu,%llu,%llu],\"end_ahead\":%llu,\"end_bp\":%llu,\"end_phase\":%llu", s_fg_on, s_fg_on && s_rth_on && open, s_fg_force, (unsigned long long)s_fg_generated, (unsigned long long)s_fg_real_presents, @@ -13686,6 +14056,7 @@ int gl_renderer_frame_gen_json(char *out, int cap) { s_fg_real_ema * 1e3, s_fg_real_gpu_ema * 1e3, fg_ceil()->cap, s_fg_trip_late, s_fg_trip_bp, s_fg_trip_behind, s_fg_fit.camera, s_fg_fit.object, s_fg_fit.neighbour, s_fg_fit.unchanged, + (unsigned long long)s_fg_skip_unplaced, (unsigned long long)s_rp_older_frames, (double)s_rp_pzs_last, 2.0 * acos(fmin(1.0, fabs(s_fg_fit.v[0].q[0]))) * 57.29578, sqrt(s_fg_fit.v[0].t[0] * s_fg_fit.v[0].t[0] + s_fg_fit.v[0].t[1] * s_fg_fit.v[0].t[1] + s_fg_fit.v[0].t[2] * s_fg_fit.v[0].t[2]), diff --git a/runtime/tests/run_gl_frame_gen.py b/runtime/tests/run_gl_frame_gen.py index e57ec21d..0186908d 100644 --- a/runtime/tests/run_gl_frame_gen.py +++ b/runtime/tests/run_gl_frame_gen.py @@ -176,6 +176,23 @@ def main(): or (pan == "1" and p["real"] != runs[0]["real"])): print(f"FAIL scale {s} {path} {timing} reprojection pgxp={pgxp} pan={pan}") ok = False + # Occlusion (FG_OCCLUDE=1): a far quad drawn after a near wall, + # hidden in the real frames by PGXP depth; no reprojected frame + # may show it through the wall (the depth image takes the nearest + # world depth, not painter order). + if path == "vram": # the occlusion scene draws through VRAM only + env = dict(os.environ, FG_METHOD="reprojection", FG_OCCLUDE="1") + command = [str(c) for c in (probe, s, 1, path, args.frames, timing)] + r = subprocess.run(command, cwd=dest, capture_output=True, text=True, + encoding="utf-8", errors="replace", env=env) + receipt.append({"cmd": command, "env": {"FG_METHOD": "reprojection", "FG_OCCLUDE": "1"}, + "exit": r.returncode, "stdout": r.stdout, "stderr": r.stderr[-4000:]}) + occl = [l for l in r.stdout.splitlines() if l.startswith("occlusion")] + print(f"scale {s} {path} {timing} reprojection occlusion: exit={r.returncode}", occl, + r.stderr.strip()[-800:]) + if r.returncode or not occl: + print(f"FAIL scale {s} {path} {timing} reprojection occlusion") + ok = False # Present thread ([video] present_thread): the fixture checks the # window shows every composed image bit for bit, in order. The real # images match the direct-swap run: bit for bit, except that diff --git a/runtime/tests/test_frame_gen.c b/runtime/tests/test_frame_gen.c index 3d15d1d7..f3540589 100644 --- a/runtime/tests/test_frame_gen.c +++ b/runtime/tests/test_frame_gen.c @@ -445,6 +445,34 @@ static void test_view_affine(void) { const double c = cos(2 * a), sn = sin(2 * a), dx = P[0] - 30, dz = P[2] - 400; check(fabs(o0 - (c * dx - sn * dz)) < 1e-2 && fabs(o2 - (sn * dx + c * dz)) < 1e-2, "t = 0: the older frame's position"); + /* Older frame (two-frame warp): t = 0 is the older camera itself, t = 1 + * the newer one (P_new = M P_old + T), and at any t an older point lands + * where the same point seen from the newer frame lands. */ + float Ao[9], bo[3]; + check(fg_view_affine_older(&f, 0, 0.0, Ao, bo) && + fabsf(Ao[0] - 1) < 1e-5f && fabsf(Ao[4] - 1) < 1e-5f && fabsf(Ao[8] - 1) < 1e-5f && + fabsf(bo[0]) < 1e-5f && fabsf(bo[2]) < 1e-5f, "older, t = 0: the older camera itself"); + for (int k = 0; k <= 4; k++) { + const double t = k / 4.0; + const float Po[3] = { -50, 10, 900 }; + double M[9]; + { const double w = f.v[0].q[0], x = f.v[0].q[1], y = f.v[0].q[2], z = f.v[0].q[3]; + M[0] = 1 - 2*(y*y + z*z); M[1] = 2*(x*y - w*z); M[2] = 2*(x*z + w*y); + M[3] = 2*(x*y + w*z); M[4] = 1 - 2*(x*x + z*z); M[5] = 2*(y*z - w*x); + M[6] = 2*(x*z - w*y); M[7] = 2*(y*z + w*x); M[8] = 1 - 2*(x*x + y*y); } + float Pn[3]; + for (int r = 0; r < 3; r++) + Pn[r] = (float)(M[3*r] * Po[0] + M[3*r+1] * Po[1] + M[3*r+2] * Po[2] + f.v[0].t[r]); + fg_view_affine(&f, 0, t, A, b); + fg_view_affine_older(&f, 0, t, Ao, bo); + int same = 1; + for (int r = 0; r < 3; r++) { + const float vn = A[3*r] * Pn[0] + A[3*r+1] * Pn[1] + A[3*r+2] * Pn[2] + b[r]; + const float vo = Ao[3*r] * Po[0] + Ao[3*r+1] * Po[1] + Ao[3*r+2] * Po[2] + bo[r]; + same &= fabsf(vn - vo) < 0.05f; + } + check(same, "older and newer warps agree on the same point at every phase"); + } } /* A 320x240 main view and a 64x48 inset with the same valid camera motion. diff --git a/runtime/tests/test_gl_frame_gen.c b/runtime/tests/test_gl_frame_gen.c index 85c31496..f3f65c3e 100644 --- a/runtime/tests/test_gl_frame_gen.c +++ b/runtime/tests/test_gl_frame_gen.c @@ -57,6 +57,14 @@ uint32_t psx_mod_gpu_dma_memory_alloc(uint32_t n,uint32_t a){(void)n;(void)a;ret int psx_mod_host_output_scope(void){return 0;} uint32_t psx_mod_read_word(uint32_t a){(void)a;return 0;} int g_psx_vram_dirty_tracking=0; +/* Host menu overlay and video telemetry (psx_host_menu.c, video_telemetry.cpp): + * none here. */ +int psx_host_menu_overlay_image(const uint32_t **pixels, int *w, int *h){(void)pixels;(void)w;(void)h;return 0;} +void psx_video_telemetry_present(int result){(void)result;} +void psx_video_telemetry_compose(int renderer, int scale, int source_w, int source_h, + int drawable_w, int drawable_h, int viewport_w, int viewport_h){ + (void)renderer;(void)scale;(void)source_w;(void)source_h;(void)drawable_w;(void)drawable_h; + (void)viewport_w;(void)viewport_h;} uint64_t s_frame_count=0; void gpu_vram_dirty_mark_row_impl(uint32_t y){(void)y;} void gpu_vram_dirty_mark_rect(int x,int y,int w,int h){(void)x;(void)y;(void)w;(void)h;} @@ -267,6 +275,31 @@ static void backdrop(int g, int bx) { } } +/* FG_OCCLUDE=1 (reprojection, on the FG_PAN = 6 scene): after the near + * triangles (z = 1000) and over the backdrop (z = 2000), a far quad + * (z = 3000, moving with the same camera) is drawn last, as a game's sort or + * Max Detail's far sections can leave far geometry. The reprojection depth + * image must keep the nearest world depth there: painter order put the far + * quad's depth over the near triangles, and the warp moved them as distant + * scenery (objects through walls, doubled signs). */ +static int g_occlude; +static void occlude_quad(int g, int bx) { + const int T = (g % 9) * g_pan; /* the triangles' camera: T units at z = 1000 */ + const int qx = bx + 60 + T / 3, qy = 40, qw = 120, qh = 100, z = 3000; + const int xs4[4] = { qx, qx + qw, qx, qx + qw }, ys4[4] = { qy, qy, qy + qh, qy + qh }; + static const int tri[2][3] = { { 0, 1, 2 }, { 1, 3, 2 } }; + for (int t = 0; t < 2; t++) { + uint32_t id[3]; int32_t pc[9], h[3], xs[3], ys[3]; + for (int k = 0; k < 3; k++) { + const int c = tri[t][k]; + id[k] = 3000u + (uint32_t)c; xs[k] = xs4[c]; ys[k] = ys4[c]; + pc[3 * k] = (xs[k] - bx - T / 3) * 3 + T; pc[3 * k + 1] = ys[k] * 3; pc[3 * k + 2] = z; h[k] = 1000; + } + gl_renderer_fg_source(id, pc, h, xs, ys); + gr_draw_flat_triangle(xs[0], ys[0], xs[1], ys[1], xs[2], ys[2], 0x7c1f); + } +} + /* One game frame drawn into buffer bx. Positions move by whole pixels with * g; every fifth frame triangle 0 jumps across the screen. */ static void game_frame_part(int g, int bx, int part) { @@ -297,6 +330,7 @@ static void game_frame_part(int g, int bx, int part) { } } if (part == 0) return; + if (g_occlude) occlude_quad(g, bx); gr_draw_flat_rect(bx + 8, 220, 80, 12, 0x7fff); /* HUD */ gr_draw_flat_rect(900, 300, 32, 32, (uint16_t)(0x03e0 + g)); /* render to texture */ gr_copy_rect(bx + 10, 10, 900, 400, 16, 16); /* copy out of the display */ @@ -362,6 +396,8 @@ int main(int argc, char **argv) { g_pgxp_color = g_pgxp && strstr(e, "color") != NULL; g_pgxp_seam = g_pgxp && strstr(e, "seam") != NULL; } + g_occlude = getenv("FG_OCCLUDE") && getenv("FG_OCCLUDE")[0] == '1'; + if (g_occlude) g_pan = 6; if (SDL_Init(SDL_INIT_VIDEO) != 0) { fprintf(stderr, "SKIP no video (%s)\n", SDL_GetError()); return 77; @@ -452,6 +488,37 @@ int main(int argc, char **argv) { SDL_GL_GetDrawableSize(s_win, &ww, &wh); uint32_t *img = (uint32_t *)malloc((size_t)ww * wh * 4); check(img_last[0] && img_last[1], "two real images kept"); + if (g_occlude) { + /* The reprojection depth image under the wall must hold the + * wall's depth (the nearest world surface), whatever the draw + * order: painter order left the far quad's depth there, and the + * warp moved the wall's pixels as distant scenery. */ + check(fg_generate(0.5, 0) == 1, "occlusion: generated"); + check(s_rp_ztex && s_rp_zw > 0 && s_rp_zh > 0, "occlusion: depth image built"); + float *z = (float *)malloc((size_t)s_rp_zw * s_rp_zh * 4 * sizeof(float)); + glBindTexture(GL_TEXTURE_2D, s_rp_ztex); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, z); + glBindTexture(GL_TEXTURE_2D, 0); + long near_px = 0, far_px = 0; + /* The quad's box at the newer frame (g = 23: T = 30, +10 px). */ + for (int y = 45; y < 135; y++) + for (int x = 75; x < 185; x++) { + const int zx = x * RP_ZS, zy = y * RP_ZS; + if (zx >= s_rp_zw || zy >= s_rp_zh) continue; + const float *t = &z[((size_t)zy * s_rp_zw + zx) * 4]; + if (t[1] != 1.0f || t[0] <= 0.0f) continue; /* world pixels only */ + const float Z = 1.0f / t[0]; + near_px += Z < 1500.0f; far_px += Z > 2500.0f; + } + free(z); + printf("occlusion depth_image near=%ld far=%ld reprojected=%llu\n", near_px, far_px, + (unsigned long long)s_rp_frames); + check(near_px > 100, "occlusion: the near triangles are in the depth image"); + check(far_px == 0, "occlusion: the far quad drawn last leaves no depth over nearer world (nearest wins)"); + free(img); + printf(failures ? "FAIL\n" : "PASS\n"); + return failures ? 1 : 0; + } check(fg_generate(1.0, 0) == 1, "compose at phase 1"); uint64_t d1 = read_back(img); uint64_t r1 = fnv(img_last[1], (size_t)ww * wh * 4, 0xcbf29ce484222325ull); @@ -555,6 +622,15 @@ int main(int argc, char **argv) { "reprojection: the warp follows the camera (under half the newer frame's difference)"); } check(none * 100 < (long)ww * wh, "reprojection: no holes, every pixel from a real frame or the redraw (<1%)"); + /* Edge disocclusion (FG_PAN > 1: near triangles over a backdrop + * with parallax): what the in-between camera uncovers at a near + * object's trailing edge comes from the older frame's own warp + * (two-frame reprojection), not the newer frame's pixels + * un-warped. The single-frame warp left 0.5 % here. */ + if (g_pan > 1) { + check(s_rp_older_frames > 0, "reprojection: the older frame was warped too"); + check(none * 300 < (long)ww * wh, "reprojection: uncovered edges filled by the older frame's warp (<0.33%)"); + } check(hud > 0 && hud_bad == 0, "reprojection: HUD exactly the newer frame's"); free(ref); } @@ -567,6 +643,46 @@ int main(int argc, char **argv) { check(memcmp(before, s_vram, sizeof before) == 0, "generated frame leaves native VRAM unchanged under HD authority"); gl_renderer_set_hd_texture_mode(0); + /* A large unprojected triangle after projected geometry must reject + * reprojection, while preserving the pre-existing redraw route. */ + { + FgList *b = &s_fg_l[s_fg_pb]; + FgPrim *unplaced = NULL; + int seen = 0; + double view_area = 0; + for (uint32_t vi = 0; vi < s_fg_fit.nviews; ++vi) { + const float *a = s_fg_fit.v[vi].area; + double area = (double)(a[2]-a[0])*(a[3]-a[1]); + if (area > view_area) view_area = area; + } + for (uint32_t i = 0; i < b->n; ++i) { + int pi = b->rec2prim[i]; + if (pi < 0) continue; + FgPrim *p = &b->prims.v[pi]; + if (!(p->vid[0] || p->vid[1] || p->vid[2])) continue; + double area = 0.5*fabs((double)(p->x[1]-p->x[0])*(p->y[2]-p->y[0])- + (double)(p->x[2]-p->x[0])*(p->y[1]-p->y[0])); + if (seen) { unplaced = p; break; } + seen = 1; + } + check(unplaced != NULL && view_area > 0, "method scope: large geometry witness"); + if (unplaced) { + FgPrim saved = *unplaced; + int saved_method = s_fg_reproject; + const float *a = s_fg_fit.v[0].area; + unplaced->x[0] = a[0]; unplaced->x[1] = a[2]; unplaced->x[2] = a[0]; + unplaced->y[0] = a[1]; unplaced->y[1] = a[1]; unplaced->y[2] = a[3]; + memset(unplaced->vid, 0, sizeof(unplaced->vid)); + memset(unplaced->pz, 0, sizeof(unplaced->pz)); + check(fg_unplaced_share(b) > fg_max_unplaced(), "method scope: unplaced geometry exceeds threshold"); + s_fg_reproject = 1; + check(fg_generate(0.5, 0) == 0, "method scope: reprojection refuses unplaceable geometry"); + s_fg_reproject = 0; + check(fg_generate(0.5, 0) == 1, "method scope: redraw keeps its original route"); + *unplaced = saved; + s_fg_reproject = saved_method; + } + } free(img); check(n_gen > 0, "generated frames were presented"); } From b285f08f168f5ef1e0a83ee03f67acb2f5f78cc2 Mon Sep 17 00:00:00 2001 From: PeriBluGaming Date: Wed, 21 Oct 2026 23:45:40 +0200 Subject: [PATCH 11/29] Widescreen: honor screen_x_sites in the dirty-RAM interpreter (#728) --- docs/WIDESCREEN.md | 8 +- runtime/include/gpu.h | 2 + runtime/src/dirty_ram_interp.c | 18 +++- runtime/src/gpu.c | 13 +++ runtime/src/main.cpp | 3 + runtime/tests/test_ws_cull_edge_interp.c | 129 ++++++++++++++++++++++- 6 files changed, 166 insertions(+), 7 deletions(-) diff --git a/docs/WIDESCREEN.md b/docs/WIDESCREEN.md index fb36c53d..7f7d35b5 100644 --- a/docs/WIDESCREEN.md +++ b/docs/WIDESCREEN.md @@ -340,8 +340,12 @@ screen_x_sites = [ ] ``` -`screen_x_sites` is applied when code is generated: native code and overlay -shards. The dirty-RAM interpreter does not consult this list. In the main EXE +`screen_x_sites` is applied when code is generated (native code and overlay +shards) and, at runtime, by the dirty-RAM interpreter: code that runs from +RAM through the interpreter (including under `PSX_FORCE_INTERP=1`) widens the +same four instruction forms at the listed addresses, with the same helpers and +the same identity at 4:3. A listed `bltz` follows the precedence `bltz_sites`, +then `screen_x_sites`, then the `auto_screen_x` classifier. In the main EXE a listed address holding another non-branch instruction is a hard build error; a branch other than `bltz` at a listed address is left as it is. diff --git a/runtime/include/gpu.h b/runtime/include/gpu.h index c78d21c7..9e6289b2 100644 --- a/runtime/include/gpu.h +++ b/runtime/include/gpu.h @@ -447,6 +447,8 @@ uint32_t psx_ws_clip_edge_x(uint32_t v, uint32_t w); * 4:3. */ uint32_t psx_ws_cull_lui_hi(uint32_t imm); int psx_ws_cull_bltz_hi(uint32_t v); +void gpu_ws_set_cull_screen_x_sites(const uint32_t *sites, int nsites); +int psx_ws_is_cull_screen_x_site(uint32_t pc); int psx_ws_cull_vxrange(uint32_t x, uint32_t imm); /* True if a run of instruction words carries the screen-extent reject signature * (a width compare AND a height compare from the configured immediate sets). diff --git a/runtime/src/dirty_ram_interp.c b/runtime/src/dirty_ram_interp.c index afe81bf5..f7c85b4c 100644 --- a/runtime/src/dirty_ram_interp.c +++ b/runtime/src/dirty_ram_interp.c @@ -1973,8 +1973,11 @@ static int exec_one_fetched_inner(CPUState *cpu, uint32_t pc, uint32_t insn, { uint32_t vanilla = (cpu->gpr[rs] < cpu->gpr[rt]) ? 1u : 0u; uint32_t kept = vanilla; - if (!psx_ws_cull_scale_site(pc, insn, cpu->gpr[rs], cpu->gpr[rt], &kept)) - (void)psx_ws_cull_keep_site(pc, insn, vanilla, &kept); + /* Same precedence as the code generator: scale and keep sites first. */ + if (!psx_ws_cull_scale_site(pc, insn, cpu->gpr[rs], cpu->gpr[rt], &kept) && + !psx_ws_cull_keep_site(pc, insn, vanilla, &kept) && + psx_ws_is_cull_screen_x_site(pc)) + kept = (uint32_t)psx_ws_cull_sltu_band(cpu->gpr[rs], cpu->gpr[rt]); cpu->gpr[rd] = kept; psx_pgxp_alu(cpu, insn, kept, 0, 0); cpu->gpr[0] = 0; @@ -2092,8 +2095,11 @@ static int exec_one_fetched_inner(CPUState *cpu, uint32_t pc, uint32_t insn, /* Widescreen render-funnel LEFT-edge widen (auto_screen_x): a * classified funnel bltz rejects only past the revealed margin. * Identity at 4:3 (margin 0). Gated per-game, cheap cached scan. */ - if (psx_ws_is_cull_bltz_site(pc) || - (psx_ws_auto_cull_on() && ws_cull_bltz_site(pc))) + if (psx_ws_is_cull_bltz_site(pc)) + taken = psx_ws_cull_bltz(cpu->gpr[rs]); + else if (psx_ws_is_cull_screen_x_site(pc)) + taken = psx_ws_cull_bltz_hi(cpu->gpr[rs]); + else if (psx_ws_auto_cull_on() && ws_cull_bltz_site(pc)) taken = psx_ws_cull_bltz(cpu->gpr[rs]); else taken = ((int32_t)cpu->gpr[rs] < 0); @@ -2200,6 +2206,8 @@ static int exec_one_fetched_inner(CPUState *cpu, uint32_t pc, uint32_t insn, ((uint32_t)simm + 2u * (uint32_t)psx_ws_activation_margin())) ? 1u : 0u; } + else if (psx_ws_is_cull_screen_x_site(pc)) + cpu->gpr[rt] = (uint32_t)psx_ws_cull_sltiu(cpu->gpr[rs], imm); else if (psx_ws_auto_cull_on() && psx_ws_is_cull_w_imm(imm) && ws_cull_site(pc)) cpu->gpr[rt] = (uint32_t)psx_ws_cull_sltiu(cpu->gpr[rs], imm); else @@ -2235,6 +2243,8 @@ static int exec_one_fetched_inner(CPUState *cpu, uint32_t pc, uint32_t insn, case 0x0F: /* LUI rt, imm */ if (psx_ws_is_signed_x_bound_site(pc, insn)) cpu->gpr[rt] = (uint32_t)psx_ws_player_x_bound((int32_t)(imm << 16)); + else if (psx_ws_is_cull_screen_x_site(pc)) + cpu->gpr[rt] = psx_ws_cull_lui_hi(imm); else cpu->gpr[rt] = imm << 16; psx_pgxp_alu(cpu, insn, cpu->gpr[rt], 0, 0); diff --git a/runtime/src/gpu.c b/runtime/src/gpu.c index b09a29e9..eb25d051 100644 --- a/runtime/src/gpu.c +++ b/runtime/src/gpu.c @@ -784,8 +784,21 @@ WS_EXPLICIT_LIST(bltz); WS_EXPLICIT_LIST(bgez); WS_EXPLICIT_LIST(branch_keep); WS_EXPLICIT_LIST(clip_edge_x_load); +WS_EXPLICIT_LIST(screen_x); static uint32_t ws_clip_edge_width = 320; +/* [widescreen.cull] screen_x_sites for interpreted code: native code compiles + * the list in (code_generator.cpp); this keeps a dirty-RAM copy of a listed + * site on the same semantics. */ +void gpu_ws_set_cull_screen_x_sites(const uint32_t *sites, int nsites) { + ws_explicit_screen_x_n = ws_explicit_store( + ws_explicit_screen_x_sites, sites, nsites, "screen_x_sites"); +} +int psx_ws_is_cull_screen_x_site(uint32_t pc) { + return ws_explicit_site(ws_explicit_screen_x_sites, + ws_explicit_screen_x_n, pc); +} + void gpu_ws_set_explicit_cull_sites(const uint32_t *bias, int nbias, const uint32_t *slti, int nslti, const uint32_t *range, int nrange) { diff --git a/runtime/src/main.cpp b/runtime/src/main.cpp index 026be2ec..484a98f8 100644 --- a/runtime/src/main.cpp +++ b/runtime/src/main.cpp @@ -18165,6 +18165,9 @@ int main(int argc, char** argv) { * must never have its live code scanned and rewritten. */ gpu_ws_set_auto_hooks(gc.ws_auto_screen_x_cull ? 1 : 0, gc.ws_auto_backdrop_preload ? 1 : 0); + gpu_ws_set_cull_screen_x_sites( + gc.ws_cull_screen_x_sites.data(), + (int)gc.ws_cull_screen_x_sites.size()); if (!gc.ws_cull_w_imms.empty() || !gc.ws_cull_h_imms.empty()) gpu_ws_set_cull_imms(gc.ws_cull_w_imms.data(), (int)gc.ws_cull_w_imms.size(), gc.ws_cull_h_imms.data(), (int)gc.ws_cull_h_imms.size()); diff --git a/runtime/tests/test_ws_cull_edge_interp.c b/runtime/tests/test_ws_cull_edge_interp.c index 149452d4..3b946d3b 100644 --- a/runtime/tests/test_ws_cull_edge_interp.c +++ b/runtime/tests/test_ws_cull_edge_interp.c @@ -8,7 +8,7 @@ * * - the store: masking to physical addresses, sort, de-duplication, lookups * at both ends and between entries, the 256-entry cap and its log line, - * empty and negative-count lists, the clip-edge width default; + * empty and negative-count lists, and explicit screen-X sites; * - psx_ws_cull_bgez / psx_ws_clip_edge_x through a forced margin; * - real interpreted instructions (exec_one_fetched) at margin 0 and at a * forced margin: BGEZ and BLTZ sites, BNE and BLEZ branch-keep sites, and @@ -248,6 +248,17 @@ static void test_store(void) { !psx_ws_is_cull_slti_site(0x80030008u), "slti_sites store"); gpu_ws_set_explicit_cull_sites(NULL, 0, NULL, 0, NULL, 0); + const uint32_t screen_x[] = { 0x80030020u, 0x00030010u, 0x80030020u }; + gpu_ws_set_cull_screen_x_sites(screen_x, 3); + CHECK(ws_explicit_screen_x_n == 2, "screen_x_sites de-duplicated"); + CHECK(psx_ws_is_cull_screen_x_site(0xA0030020u) && + psx_ws_is_cull_screen_x_site(0x80030010u) && + !psx_ws_is_cull_screen_x_site(0x80030018u), + "screen_x_sites uses physical addresses and exact lookup"); + gpu_ws_set_cull_screen_x_sites(NULL, 0); + CHECK(!psx_ws_is_cull_screen_x_site(0x80030020u), + "empty screen_x_sites clears prior entries"); + /* Cap: exactly 256 is silent and complete. */ for (int i = 0; i < 300; i++) big_list[i] = 0x80040000u + 4u * (uint32_t)(299 - i); /* descending */ @@ -319,6 +330,11 @@ static void test_interpreter(void) { const uint32_t blez_site = CODE + 0x030, blez_plain = CODE + 0x130; const uint32_t lh_site = CODE + 0x040, lh_plain = CODE + 0x140; const uint32_t lhu_site = CODE + 0x050, lw_site = CODE + 0x060; + const uint32_t sltiu_x_site = CODE + 0x070; + const uint32_t lui_x_site = CODE + 0x080; + const uint32_t bltz_x_site = CODE + 0x090; + const uint32_t sltiu_x_plain = CODE + 0x170; + const uint32_t sltu_x_site = CODE + 0x0A0, sltu_x_plain = CODE + 0x1A0; const uint32_t bgez = i_type(0x01, V0, 0x01, 3); /* bgez v0,+3 */ const uint32_t bltz = i_type(0x01, V0, 0x00, 3); /* bltz v0,+3 */ @@ -327,17 +343,59 @@ static void test_interpreter(void) { const uint32_t lh = i_type(0x21, A0, V0, 0x70); /* lh v0,0x70(a0) */ const uint32_t lhu = i_type(0x25, A0, V0, 0x70); /* lhu v0,0x70(a0) */ const uint32_t lw = i_type(0x23, A0, V0, 0x70); /* lw v0,0x70(a0) */ + const uint32_t sltiu_x = i_type(0x0B, V1, V0, 0x200); + const uint32_t lui_x = i_type(0x0F, 0, V1, 0x200); + const uint32_t sltu_x = (V1 << 21) | (A0 << 16) | (V0 << 11) | 0x2Bu; /* sltu v0,v1,a0 */ const uint32_t bltz_l[] = { bltz_site }, bgez_l[] = { bgez_site }; const uint32_t keep_l[] = { bne_site, blez_site }; const uint32_t clip_l[] = { lh_site, lhu_site, lw_site }; + const uint32_t screen_x_l[] = { sltiu_x_site, lui_x_site, bltz_x_site, sltu_x_site }; gpu_ws_set_branch_cull_sites(bltz_l, 1, bgez_l, 1, keep_l, 2); gpu_ws_set_clip_edge_x_load_sites(clip_l, 3, 320); + gpu_ws_set_cull_screen_x_sites(screen_x_l, 4); for (int pass = 0; pass < 2; pass++) { const int m = pass ? 53 : 0; gpu_ws_set_margin_override(m); + cpu.gpr[V1] = (uint32_t)-53; + CHECK(load(&cpu, sltiu_x_site, sltiu_x) == (m > 0 ? 1u : 0u), + "explicit screen-X sltiu left margin, m=%d", m); + cpu.gpr[V1] = 564; + CHECK(load(&cpu, sltiu_x_site, sltiu_x) == (m > 0 ? 1u : 0u), + "explicit screen-X sltiu right edge, m=%d", m); + cpu.gpr[V1] = 565; + CHECK(load(&cpu, sltiu_x_site, sltiu_x) == 0u, + "explicit screen-X sltiu right margin, m=%d", m); + cpu.gpr[V1] = (uint32_t)-53; + CHECK(load(&cpu, sltiu_x_plain, sltiu_x) == 0u, + "unlisted screen-X sltiu remains vanilla, m=%d", m); + + cpu.gpr[A0] = 320; + cpu.gpr[V1] = (uint32_t)-100; + (void)step(&cpu, sltu_x_site, sltu_x); + CHECK(cpu.gpr[V0] == (m > 0 ? 1u : 0u), + "explicit screen-X sltu left band, m=%d", m); + cpu.gpr[V1] = (uint32_t)-300; + (void)step(&cpu, sltu_x_site, sltu_x); + CHECK(cpu.gpr[V0] == 0u, "explicit screen-X sltu past left reach, m=%d", m); + cpu.gpr[V1] = (uint32_t)-100; + (void)step(&cpu, sltu_x_plain, sltu_x); + CHECK(cpu.gpr[V0] == 0u, "unlisted sltu remains vanilla, m=%d", m); + + cpu.gpr[V1] = 0; + (void)step(&cpu, lui_x_site, lui_x); + CHECK(cpu.gpr[V1] == (uint32_t)(0x200 + m) << 16, + "explicit screen-X lui bound, m=%d", m); + + cpu.gpr[V0] = (uint32_t)-10 << 16; + CHECK(branch_taken(&cpu, bltz_x_site, bltz) == (m == 0), + "explicit screen-X high-half bltz at -10, m=%d", m); + cpu.gpr[V0] = (uint32_t)-54 << 16; + CHECK(branch_taken(&cpu, bltz_x_site, bltz) == 1, + "explicit screen-X high-half bltz at -54, m=%d", m); + /* bgez: x0 = -10 sits in the revealed band when m = 53. */ cpu.gpr[V0] = (uint32_t)-10; CHECK(branch_taken(&cpu, bgez_site, bgez) == (m > 0), @@ -403,6 +461,74 @@ static void test_interpreter(void) { gpu_ws_set_margin_override(-1); gpu_ws_set_branch_cull_sites(NULL, 0, NULL, 0, NULL, 0); gpu_ws_set_clip_edge_x_load_sites(NULL, 0, 0); + gpu_ws_set_cull_screen_x_sites(NULL, 0); +} + +/* A site listed in screen_x_sites and in another explicit list: the code + * generator returns for the other list first, so the interpreter must too. */ +static void test_screen_x_precedence(void) { + CPUState cpu; + memset(&cpu, 0, sizeof cpu); + memset(test_ram, 0, sizeof test_ram); + + const uint32_t sltu_keep = CODE + 0x200, sltu_scale = CODE + 0x210; + const uint32_t sltu_only = CODE + 0x220, lui_signed = CODE + 0x230; + const uint32_t lui_only = CODE + 0x240; + const uint32_t sltu = (V1 << 21) | (A0 << 16) | (V0 << 11) | 0x2Bu; /* sltu v0,v1,a0 */ + const uint32_t lui = i_type(0x0F, 0, V1, 0x200); + + const uint32_t screen_x[] = { sltu_keep, sltu_scale, sltu_only, + lui_signed, lui_only }; + const uint32_t keep_a[] = { sltu_keep }, keep_e[] = { sltu }, + keep_r[] = { 0 }; + const uint32_t scale_a[] = { sltu_scale }, scale_e[] = { sltu }, + scale_o[] = { 1 }, scale_h[] = { 160 }; + const uint32_t sb_a[] = { lui_signed }, sb_e[] = { lui }; + gpu_ws_set_cull_screen_x_sites(screen_x, 5); + gpu_ws_set_cull_keep_sites(keep_a, keep_e, keep_r, 1); + gpu_ws_set_cull_scale_sites(scale_a, scale_e, scale_o, scale_h, 1); + gpu_ws_set_signed_x_bound_sites(sb_a, sb_e, 1); + + for (int pass = 0; pass < 2; pass++) { + const int m = pass ? 53 : 0; + gpu_ws_set_margin_override(m); + + /* -100 < 320 as unsigned is false; the band would accept it at m=53. */ + cpu.gpr[V1] = (uint32_t)-100; + cpu.gpr[A0] = 320; + (void)step(&cpu, sltu_only, sltu); + CHECK(cpu.gpr[V0] == (m > 0 ? 1u : 0u), + "screen_x sltu alone uses the band, m=%d", m); + + /* keep site (forced 0 while widened) beats the band */ + cpu.gpr[V0] = 7; + (void)step(&cpu, sltu_keep, sltu); + CHECK(cpu.gpr[V0] == 0u, "keep site beats screen_x sltu, m=%d", m); + + /* scale site beats the band: 0xFFFFFF9C < scaled(320) is false */ + cpu.gpr[V0] = 7; + (void)step(&cpu, sltu_scale, sltu); + CHECK(cpu.gpr[V0] == 0u, "scale site beats screen_x sltu, m=%d", m); + + /* signed_x_bound beats the screen_x lui edge */ + cpu.gpr[V1] = 0; + (void)step(&cpu, lui_signed, lui); + CHECK(cpu.gpr[V1] == (uint32_t)psx_ws_player_x_bound((int32_t)(0x200u << 16)), + "signed_x_bound beats screen_x lui, m=%d", m); + CHECK(m == 0 || cpu.gpr[V1] != (uint32_t)(0x200 + m) << 16, + "signed_x_bound and screen_x lui differ while widened, m=%d", m); + cpu.gpr[V1] = 0; + (void)step(&cpu, lui_only, lui); + CHECK(cpu.gpr[V1] == (uint32_t)(0x200 + m) << 16, + "screen_x lui alone, m=%d", m); + } + + + gpu_ws_set_margin_override(-1); + gpu_ws_set_cull_screen_x_sites(NULL, 0); + gpu_ws_set_cull_keep_sites(NULL, NULL, NULL, 0); + gpu_ws_set_cull_scale_sites(NULL, NULL, NULL, NULL, 0); + gpu_ws_set_signed_x_bound_sites(NULL, NULL, 0); } static void test_masked_reject(void) { @@ -473,6 +599,7 @@ int main(int argc, char **argv) { test_store(); test_helpers(); test_interpreter(); + test_screen_x_precedence(); test_masked_reject(); test_wide_nclip_branches(); if (failures) { From 9caf4c22bfc1fd859d08184d11013c2630af5610 Mon Sep 17 00:00:00 2001 From: Matt Stanley <1379tech@gmail.com> Date: Wed, 21 Oct 2026 14:47:02 -0700 Subject: [PATCH 12/29] test: supply the screen-tag fixture DMA dependency (#730) --- runtime/tests/test_ws_screen_tags_exec.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/runtime/tests/test_ws_screen_tags_exec.py b/runtime/tests/test_ws_screen_tags_exec.py index aa79eaa9..2cc90e92 100644 --- a/runtime/tests/test_ws_screen_tags_exec.py +++ b/runtime/tests/test_ws_screen_tags_exec.py @@ -44,6 +44,12 @@ static int gp0_words_needed = 5; static int ws_engaged(void) { return engaged; } static int ws_active(void) { return active; } static uint32_t psx_ram_live_bytes(void) { return sizeof(ram); } +/* This fixture owns only ordinary RAM, with no extended GPU-DMA arena. */ +static int psx_mod_gpu_dma_memory_contains(uint32_t address, uint32_t bytes) { + (void)address; + (void)bytes; + return 0; +} static uint32_t psx_read_word(uint32_t address) { uint32_t phys = address & 0x1fffffffu; assert(!(phys & 3) && phys < sizeof(ram)); From 8400b55dc02d3fa787df1280a9fb474901adfcf4 Mon Sep 17 00:00:00 2001 From: Yves Date: Fri, 23 Oct 2026 03:22:42 +0800 Subject: [PATCH 13/29] Add opt-in stereo and XR frame attribution Co-authored-by: Yves --- docs/FRAME_TRACE.md | 72 +++ docs/TCP_COMMANDS.md | 5 +- docs/internal/upstream/vr-frame-tracing.md | 21 + runtime/CMakeLists.txt | 12 + runtime/include/frame_trace.h | 243 ++++++++++ runtime/include/gpu_gl_renderer.h | 3 + runtime/include/mod_plugins.h | 19 + runtime/runtime.cmake | 1 + runtime/src/debug_server.c | 30 ++ runtime/src/frame_trace.c | 494 +++++++++++++++++++++ runtime/src/gpu_gl_renderer.c | 8 + runtime/src/psx_openxr.c | 83 +++- runtime/src/render_pass.c | 145 +++++- runtime/tests/test_frame_trace.c | 340 ++++++++++++++ runtime/tests/test_frame_trace_guard.py | 117 +++++ runtime/tests/test_render_pass_abort.c | 7 + runtime/tests/test_render_pass_sandbox.c | 145 ++++++ 17 files changed, 1738 insertions(+), 7 deletions(-) create mode 100644 docs/FRAME_TRACE.md create mode 100644 docs/internal/upstream/vr-frame-tracing.md create mode 100644 runtime/include/frame_trace.h create mode 100644 runtime/src/frame_trace.c create mode 100644 runtime/tests/test_frame_trace.c create mode 100644 runtime/tests/test_frame_trace_guard.py diff --git a/docs/FRAME_TRACE.md b/docs/FRAME_TRACE.md new file mode 100644 index 00000000..dedf1db2 --- /dev/null +++ b/docs/FRAME_TRACE.md @@ -0,0 +1,72 @@ +# Frame tracing (stereo / OpenXR) + +`runtime/src/frame_trace.c` records one row per traced stereo frame so a visible +VR hitch can be attributed to a stage instead of guessed at. It is the +measurement floor for the VR optimization work: nothing else should be tuned +before there are traces. + +## Enable + + PSX_FRAME_TRACE=1 # off by default; one branch when off + PSX_FRAME_TRACE_CAP=2048 # optional power-of-two ring size (default 2048) + +The ring is static, the record holds fixed-size fields, and the record path +allocates nothing and writes no files (`runtime/tests/test_frame_trace_guard.py` +fails the build if that changes). Instrumentation off vs on is an A/B control. + +## Read + +Over the debug TCP server: + + {"cmd":"frame_trace","format":"json","max":240} + {"cmd":"frame_trace","format":"chrome","max":240} # Perfetto trace-event JSON + +`max` bounds the newest records (0 = all retained). The game-side helper is +`vr/trace_frames.py`, which writes `frame_trace.json`, `frame_trace.csv`, +optional `frame_trace_perfetto.json`, and a `summary.json` with P50/P95/P99 of +`pair_total`, each eye's total, and `xr_wait_frame`, plus stale/shed/failed +counts and the worst frames. + +## Stages (microseconds) + + guest_sim host time between traced frames + pair_total both eyes, pair open -> publish + eye_{begin,guest,end,restore}_{l,r} + one eye's VRAM backup+checkpoint, guest draw, + flush/capture/VRAM restore, machine restore + xr_wait_frame / xr_begin_frame xrWaitFrame / xrBeginFrame + xr_acquire / xr_wait_image / xr_copy / xr_release / xr_end_frame + hud_capture / hud_draw / hud_readback + a HUD capture transaction, its guest draw and + its readback + game_env / game_world / game_text / game_traverse / game_icon / + game_submit / game_laser + the guest scene rebuild one eye replays, split + by the call the game hook makes: draw env and + ordering-table setup, world passes, text, + scene traversal, HUD icon, DrawOTag, and the + hook's own per-eye raycast. PAIR totals (both + eyes write the same slots), so compare them + against eye_guest_l + eye_guest_r. Written by + the hook through + psx_mod_frame_trace_stage_begin/end, which + only accepts them inside a traced stereo eye. + +Per frame: `seq`, `open_ms`, guest frame/cycle, stereo attempt id, published +pair id, `pair_age_ms` (age of the retained whole pair), `source` (1 pair / 2 +native), `eye_failed`, shed skipped/streak, `prims`/`tex_prims` (the last eye's +own count), `prims_l`/`prims_r`, `tex_prims_l`/`tex_prims_r`, +`cycles_l`/`cycles_r` (guest cycles each eye's replay executed, separating +emulated machine work from host submission time), `vram_leaks`, `tex_bytes`, and +flags (`published`, `stale`, `shed`, `failed`, `xr_empty`, `scene`, `io`, +`no_telemetry`). + +## Guarantees + +* `xr_*` stages are CPU wall-clock waits, never GPU execution time. +* Only a complete pair is published; a stale frame retains the previous whole + pair (`pair_age_ms` says how old it is). +* A frame is `stale` when it published nothing, `shed` when the pair was skipped + on purpose, `failed` when a pair was attempted and lost, and + `no_telemetry` when an expected-to-be-instrumented frame recorded no stage. +* Timestamps are monotonic even if the host clock steps backwards. diff --git a/docs/TCP_COMMANDS.md b/docs/TCP_COMMANDS.md index 0d11ca8c..bece7d73 100644 --- a/docs/TCP_COMMANDS.md +++ b/docs/TCP_COMMANDS.md @@ -493,9 +493,9 @@ between Play and TCP availability. ## Complete command index (generated) -**383 commands registered** — 370 on the native server (`runtime/src/debug_server.c`), 61 on the Beetle server (`runtime/src/beetle_debug_server.c`). +**384 commands registered** — 371 on the native server (`runtime/src/debug_server.c`), 61 on the Beetle server (`runtime/src/beetle_debug_server.c`). -85 of 383 have prose above; **298 are index-only**. An index-only command still works — it just has no description here yet. Send it `{"cmd":""}` and read the reply, or find its `handle_*` function in the server source. +85 of 384 have prose above; **299 are index-only**. An index-only command still works — it just has no description here yet. Send it `{"cmd":""}` and read the reply, or find its `handle_*` function in the server source. Regenerate with `python tools/gen_tcp_commands.py`; `--check` fails if this block has drifted from the code. @@ -627,6 +627,7 @@ Regenerate with `python tools/gen_tcp_commands.py`; `--check` fails if this bloc | `frame_perf` | ✓ | | | | `frame_range` | ✓ | ✓ | ✓ | | `frame_timeseries` | ✓ | ✓ | ✓ | +| `frame_trace` | ✓ | | | | `frame_wall_ring` | ✓ | | | | `freeze_check` | ✓ | | | | `game_options` | ✓ | | | diff --git a/docs/internal/upstream/vr-frame-tracing.md b/docs/internal/upstream/vr-frame-tracing.md new file mode 100644 index 00000000..db6e69c6 --- /dev/null +++ b/docs/internal/upstream/vr-frame-tracing.md @@ -0,0 +1,21 @@ +# VR frame tracing: source and validation + +Adapted from [FractalEngineer/psxrecomp](https://github.com/FractalEngineer/psxrecomp): +- [022eb1ed](https://github.com/FractalEngineer/psxrecomp/commit/022eb1edb2d6b2d9d505641e198bd9aeb5eb8ce1) +- [0e0d2de8](https://github.com/FractalEngineer/psxrecomp/commit/0e0d2de82777857248ae6c1bf7e9b7cba05e3d0a) +- [5c67dced](https://github.com/FractalEngineer/psxrecomp/commit/5c67dced708a67e905b77be455263009f566d4bb) +- [63271ce7](https://github.com/FractalEngineer/psxrecomp/commit/63271ce7b4e2fb5f616050e7d59a2e4642a33b55) +- [571a8f10](https://github.com/FractalEngineer/psxrecomp/commit/571a8f1082dbbc95af19e90ce0226a0cb40f302d) +- [87a3e822](https://github.com/FractalEngineer/psxrecomp/commit/87a3e8223cf7c05761326bfce06e80ffc527e132) + +Original author: Yves ; retained as commit author +and Co-authored-by on this reconstructed port. + +Adapted: opt-in trace ring, XR cadence, HUD timing, nested replay attribution, +exports and tests. Excluded: shedding policy, checkpoint reuse, game hooks and +chronological research. Rebuilt on upstream master `9caf4c22`. + +Validation: Windows/GCC 13.2 Release: trace, source guard, sandbox, abort and +XR-off input tests pass (5/5); command index is current. XR-on integration is +checked separately after assembling the feature branches. +Consumer: [Medal of Honor v0.1.6](https://github.com/FractalEngineer/Medal-Of-Honor-PSX-Recomp/tree/1957e28a5dc7f5daa79e2b9b6ed867eb06b41ace); pin bump and rebuild, no regeneration. diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index f1c45888..833731f5 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -1629,6 +1629,11 @@ if(BUILD_TESTING) add_test(NAME present_sync_test COMMAND present_sync_test) target_include_directories(frame_pacing_test PRIVATE include) add_test(NAME frame_pacing_test COMMAND frame_pacing_test) + # Opt-in stereo/OpenXR frame-trace ring (frame_trace.c): disabled-by-default, + # ring wrap, timestamp monotonicity, missing telemetry, export validity. + add_executable(frame_trace_test tests/test_frame_trace.c src/frame_trace.c) + target_include_directories(frame_trace_test PRIVATE include) + add_test(NAME frame_trace_test COMMAND frame_trace_test) # Render thread core ([video] render_thread, docs/RENDER_THREAD.md): # ordering across ring wraps, the frame bound, stale-frame detection and # the drain + context hand-off sync point, against a fake executor. @@ -1774,6 +1779,7 @@ if(BUILD_TESTING) add_executable(render_pass_abort_test tests/test_render_pass_abort.c src/render_pass.c + src/frame_trace.c src/psx_cycles.c) target_include_directories(render_pass_abort_test PRIVATE include) add_test(NAME render_pass_abort_test COMMAND render_pass_abort_test) @@ -1785,6 +1791,7 @@ if(BUILD_TESTING) add_executable(render_pass_sandbox_test tests/test_render_pass_sandbox.c src/render_pass.c + src/frame_trace.c src/render_pass_plan.c src/psx_cycles.c src/timers.c @@ -2042,6 +2049,11 @@ if(BUILD_TESTING) COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_memory_debug_trace_guard.py --cc ${CMAKE_C_COMPILER}) + # The frame-trace record path must stay allocation- and IO-free, or + # it perturbs the frame it measures and no functional test notices. + add_test(NAME frame_trace_guard_test + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_frame_trace_guard.py) endif() add_test(NAME fntrace_c_linkage_test COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_fntrace_c_linkage.py) add_test(NAME depth24_present_hold_vblank_test diff --git a/runtime/include/frame_trace.h b/runtime/include/frame_trace.h new file mode 100644 index 00000000..7edd64c2 --- /dev/null +++ b/runtime/include/frame_trace.h @@ -0,0 +1,243 @@ +#ifndef PSX_FRAME_TRACE_H +#define PSX_FRAME_TRACE_H + +/* Opt-in, fixed-capacity frame-time tracing for the stereo/OpenXR path. + * + * WHY THIS EXISTS + * --------------- + * VR stutter reports ("it hitches when enemies spawn", "fast head turns judder", + * "one heavy frame causes a run of stutters") could not be attributed. The + * renderer already smoothed a few averages, but only for a handful of stages, + * they were never reset, and there was no per-frame history to correlate a + * visible hitch with. This module is the measurement floor the VR optimization + * program is built on: one record per stereo frame, monotonic, allocation-free + * on the hot path, exported only on demand. + * + * DESIGN CONTRACT + * --------------- + * * DISABLED BY DEFAULT (PSX_FRAME_TRACE=0). Every entry point returns + * immediately while disabled, so an instrumented build costs one branch. + * * NO per-frame heap allocation and NO file I/O on the record path. The ring + * is static; the record holds fixed-size fields only. Export writes into a + * caller buffer, outside the frame. + * * Stages are supplied by the caller in MICROSECONDS, measured with whatever + * host clock it already uses. The module never reads a clock for durations, + * only for the record's open timestamp, through one injectable function + * pointer. That keeps the module dependency-free and unit-testable. + * * CPU submission time is NOT GPU execution time. Stages named "xr_*" are + * wall-clock waits; nothing here claims GPU completion. + * * Emulation/main thread only, like the OpenXR lifecycle it observes. + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Per-frame stages, in the order one stereo frame produces them. The header + * order IS the ABI; keep frame_trace.c's name table in sync. */ +enum { + PSX_TRACE_GUEST_SIM = 0, /* guest work outside the stereo pair */ + PSX_TRACE_PAIR_TOTAL, /* both eyes, pair open to publish */ + PSX_TRACE_EYE_BEGIN_L, /* VRAM backup + checkpoint */ + PSX_TRACE_EYE_GUEST_L, /* the plugin's guest draw */ + PSX_TRACE_EYE_END_L, /* GPU flush, capture, VRAM restore */ + PSX_TRACE_EYE_RESTORE_L, /* machine state restore */ + PSX_TRACE_EYE_BEGIN_R, + PSX_TRACE_EYE_GUEST_R, + PSX_TRACE_EYE_END_R, + PSX_TRACE_EYE_RESTORE_R, + PSX_TRACE_XR_WAIT_FRAME, /* xrWaitFrame */ + PSX_TRACE_XR_BEGIN_FRAME, /* xrBeginFrame */ + PSX_TRACE_XR_ACQUIRE, /* swapchain acquire (both eyes) */ + PSX_TRACE_XR_WAIT_IMAGE, /* xrWaitSwapchainImage (both eyes) */ + PSX_TRACE_XR_COPY, /* eye blit into the runtime image */ + PSX_TRACE_XR_RELEASE, /* swapchain release (both eyes) */ + PSX_TRACE_XR_END_FRAME, /* xrEndFrame */ + /* How old the submitted pose was: xrLocateViews return to xrEndFrame + * return. This is the pose the compositor reprojects from, so it bounds + * the correction a head turn can need. */ + PSX_TRACE_POSE_AGE, + /* Host time a HUD capture transaction costs (psx_mod_render_capture_rgba). + * The wrist HUD captures the same guest slice twice per frame, once per + * matte colour, and both are full render transactions on the frame's own + * thread, so this is frame time the pair stats never showed. */ + PSX_TRACE_HUD_CAPTURE, + /* The capture's two halves, so the cost can be aimed at: the transaction is + * the checkpoint plus the guest HUD draw plus its verify, the readback is + * the blit out of the pair target and the synchronous glReadPixels. */ + PSX_TRACE_HUD_DRAW, + PSX_TRACE_HUD_READBACK, + /* The guest scene rebuild one eye replays, split by the call the game hook + * makes, so a hitch can be aimed at the guest call that grew instead of at + * the whole eye. These are PAIR totals: both eyes write the same slots. + * Written by the hook through psx_mod_frame_trace_stage_begin/end, which + * only accepts them inside a traced stereo eye. + * game_env draw environment, GP0 clear, ordering-table setup + * game_world the world/level passes + * game_text the generic text renderer + * game_traverse scene traversal: level groups and entities + * game_icon the HUD icon + * game_submit DrawOTag: walks the ordering table and submits it + * game_laser the hook's own per-eye raycast and line (host work) + * game_select the hook's own level visibility override (the LVTX + * entry filter): the eye-view AABB test and the leaf + * queue it writes. It runs INSIDE the guest traversal, + * and game_traverse is that call's wall time, so the + * guest's own traversal work is game_traverse minus + * game_select. Per eye and summed over the pair like the + * rest, and the only one that nests. */ + PSX_TRACE_GAME_ENV, + PSX_TRACE_GAME_WORLD, + PSX_TRACE_GAME_TEXT, + PSX_TRACE_GAME_TRAVERSE, + PSX_TRACE_GAME_ICON, + PSX_TRACE_GAME_SUBMIT, + PSX_TRACE_GAME_LASER, + PSX_TRACE_GAME_SELECT, + PSX_TRACE_STAGE_COUNT +}; + +/* Record flags. A hitch is the interesting case, so absent conditions are + * explicit: a stale frame is one that published no new pair. */ +enum { + PSX_FRAME_TRACE_STEREO_PUBLISHED = 1u << 0, + PSX_FRAME_TRACE_STALE_PAIR = 1u << 1, + PSX_FRAME_TRACE_SHED = 1u << 2, + PSX_FRAME_TRACE_FAILED = 1u << 3, + PSX_FRAME_TRACE_XR_EMPTY = 1u << 4, + PSX_FRAME_TRACE_SCENE_TRANSITION = 1u << 5, + PSX_FRAME_TRACE_IO_EVENT = 1u << 6, + PSX_FRAME_TRACE_NO_TELEMETRY = 1u << 7, + /* A stereo eye rendered from the pair's shared baseline instead of taking + * its own machine snapshot (see PR3's pair-scoped checkpoint reuse). */ + PSX_FRAME_TRACE_BASELINE_REUSED = 1u << 8, + /* The XR frame carried no new pair, so the retained one was submitted (or + * nothing was, when resubmission is off). */ + PSX_FRAME_TRACE_XR_REPEAT = 1u << 9 +}; + +enum { + PSX_FRAME_TRACE_NOTE = 24, /* scene / IO tag */ + PSX_FRAME_TRACE_REASON = 24 /* failure reason text */ +}; + +typedef struct PSXFrameTraceRecord { + uint64_t seq; /* monotonic record id (starts at 1) */ + uint64_t open_ms; /* host monotonic ms at open */ + uint64_t guest_frame; /* guest frame counter, 0 if unknown */ + uint64_t guest_cycle; /* guest cycle at open */ + uint64_t stereo_attempt; /* stereo attempt id (0 if none) */ + uint64_t pair_id; /* published pair id (0 if none) */ + uint64_t pair_age_ms; /* ms since the last published pair (0 if none) */ + uint32_t stages_us[PSX_TRACE_STAGE_COUNT]; + uint32_t shed_skipped; /* pair attempts shed immediately before this frame */ + uint32_t shed_streak_ms; /* how long that shed streak had lasted */ + uint32_t prims; /* guest draw primitives (0 = not measured) */ + uint32_t textured_prims; /* of which textured */ + /* The same two counters per eye. `prims`/`textured_prims` above keep their + * meaning (the last eye of the pair wrote them); a change to what ONE eye + * selects needs these, because the pair's total hides which eye moved. */ + uint32_t prims_l, prims_r; + uint32_t tex_prims_l, tex_prims_r; + /* Guest cycles each eye's replay executed, the machine work the eye cost in + * the emulated CPU rather than in host submission. 0 = not measured. */ + uint32_t eye_cycles_l, eye_cycles_r; + uint32_t vram_leaks; /* GPU writes outside the pass rect this eye */ + uint64_t tex_upload_bytes; /* texture bytes uploaded (0 = not measured) */ + /* Microseconds between this submission's XrFrameState.predictedDisplayTime + * and the previous one, from the runtime's clock. Divided by the session's + * target_period_us this is how many granted display periods one presented + * pair consumed, which is the cadence the compositor actually saw. 0 on the + * first submission of a session or when there was no frame this record. */ + uint32_t xr_display_delta_us; + uint32_t flags; /* PSX_FRAME_TRACE_* */ + int32_t eye_failed; /* last eye that failed, -1 if none */ + uint8_t source; /* 1 = stereo pair, 2 = native render */ + uint8_t xr_submit_fresh; /* 1 = carried a new pair, 0 = repeat/none */ + uint8_t reserved[2]; + char note[PSX_FRAME_TRACE_NOTE]; + char failure[PSX_FRAME_TRACE_REASON]; +} PSXFrameTraceRecord; + +/* Injectable monotonic millisecond clock. NULL (the default) records 0; the + * runtime installs psx_host_mono_ms(). Tests install a fake. */ +typedef uint64_t (*PSXFrameTraceClock)(void); +void psx_frame_trace_set_clock(PSXFrameTraceClock fn); + +/* Enables/disables capture. Disabling keeps the ring (read it, then reset). + * Reads PSX_FRAME_TRACE / PSX_FRAME_TRACE_CAP once. */ +void psx_frame_trace_configure_from_env(void); +void psx_frame_trace_set_enabled(int on); +int psx_frame_trace_enabled(void); + +/* Clears the ring, counters and any open record. */ +void psx_frame_trace_reset(void); + +/* Opens a record for the frame that is starting. Returns 1 if this call opened + * it, 0 if capture is off or a record is already open (the caller must then NOT + * commit). Cost is a branch while disabled. */ +int psx_frame_trace_open(void); +/* Commits the open record, stamping pair age from the last published pair. + * No-op when nothing is open. */ +void psx_frame_trace_commit(void); + +/* Adds `us` microseconds to `stage` of the open record. Ignores unknown stages; + * saturates instead of wrapping. Cheap while closed. */ +void psx_frame_trace_add(uint32_t stage, uint64_t us); + +/* Per-frame annotations (open record). Each is a no-op when closed. */ +void psx_frame_trace_ids(uint64_t guest_frame, uint64_t guest_cycle); +void psx_frame_trace_attempt(uint64_t attempt_id, uint32_t shed_skipped, + uint32_t shed_streak_ms); +void psx_frame_trace_result(int published, uint64_t pair_id, int eye_failed, + const char *failure); +void psx_frame_trace_source(uint8_t source); +/* Draw counters for `eye` (PSX_MOD_EYE_LEFT/0 or RIGHT/1). Also fills `prims` + * and `textured_prims`, which therefore remain the last eye's values. */ +void psx_frame_trace_draw(int eye, uint32_t prims, uint32_t textured); +/* Guest cycles one eye's replay executed (the transaction's own delta). */ +void psx_frame_trace_eye_cycles(int eye, uint32_t cycles); +void psx_frame_trace_gpu(uint64_t tex_upload_bytes, uint32_t vram_leaks); +/* XR cadence annotation (open record): the runtime's predicted-display-time + * delta for this submission and whether it carried a newly rendered pair. A + * repeat also sets PSX_FRAME_TRACE_XR_REPEAT. No-op when closed. */ +void psx_frame_trace_xr_cadence(uint32_t display_delta_us, int submit_fresh); +void psx_frame_trace_flag(uint32_t flag); +void psx_frame_trace_note(const char *tag); +void psx_frame_trace_io(const char *tag); /* note + IO_EVENT flag */ + +/* Session-level metadata, emitted once in the export header rather than per + * record: the OpenXR runtime actually in use and the target display period in + * microseconds (0 = unknown, e.g. no headset). Without these a trace cannot be + * interpreted later, because the same pair time is over budget at 90 Hz and + * comfortably under it at 60 Hz. Change-checked internally, so calling it every + * frame is a strcmp; it is a no-op while capture is off. */ +void psx_frame_trace_session(const char *runtime, uint32_t target_period_us); + +/* Queries. `capacity` is the caller's array length; returns how many records + * were written (oldest first, at most the newest `capacity`). */ +uint32_t psx_frame_trace_read(PSXFrameTraceRecord *out, uint32_t capacity); +uint32_t psx_frame_trace_capacity(void); +uint64_t psx_frame_trace_committed(void); /* records committed since reset */ +uint64_t psx_frame_trace_dropped(void); /* records overwritten (ring wrap) */ +uint64_t psx_frame_trace_missing(void); /* commits with no stage at all */ + +/* Exports. Each returns the number of bytes the full text needs (like + * snprintf); the buffer is always NUL-terminated when cap > 0. `max_records` + * (0 = all retained) bounds the newest records emitted. */ +int psx_frame_trace_to_json(char *buf, int cap, uint32_t max_records); +int psx_frame_trace_to_csv(char *buf, int cap, uint32_t max_records); +/* Chrome trace-event JSON (Perfetto compatible): one span per stage. */ +int psx_frame_trace_to_chrome_json(char *buf, int cap, uint32_t max_records); + +const char *psx_frame_trace_stage_name(uint32_t stage); +/* Renders the set bits as "a|b|c" into buf; always NUL-terminated. */ +const char *psx_frame_trace_flag_names(uint32_t flags, char *buf, int cap); + +#ifdef __cplusplus +} +#endif + +#endif /* PSX_FRAME_TRACE_H */ diff --git a/runtime/include/gpu_gl_renderer.h b/runtime/include/gpu_gl_renderer.h index 4a27bdb2..cca2306b 100644 --- a/runtime/include/gpu_gl_renderer.h +++ b/runtime/include/gpu_gl_renderer.h @@ -164,6 +164,9 @@ void gl_renderer_interpolation_diag(int *enabled, int *suspended, /* Cumulative CPU-upload diagnostics: calls, rects, pixels, conversion ticks, * texture-upload ticks, FBO-draw ticks. Active only with PSX_RUNTIME_PERF_DIAG. */ void gl_renderer_runtime_diag(uint64_t out[6]); +/* Cumulative scene primitive counters (pre double-draw): all submitted, and of + * those textured. Callers take deltas; the counters are never reset here. */ +void gl_renderer_scene_prim_counts(uint64_t *prims, uint64_t *textured); /* Present an ARGB8888 image (BGRA byte order) as a letterboxed quad + swap. * Used for 24-bit (FMV) frames and the PSX_GL_FORCE_CPU_PRESENT diagnostic. diff --git a/runtime/include/mod_plugins.h b/runtime/include/mod_plugins.h index 1038cd8e..bf458abc 100644 --- a/runtime/include/mod_plugins.h +++ b/runtime/include/mod_plugins.h @@ -733,6 +733,25 @@ typedef int (*PSXModStereoFn)(struct CPUState*, void*, uint32_t eye); int psx_mod_render_stereo(struct CPUState *cpu, const PSXModStereoFrame *frame, PSXModStereoFn fn, void *user); uint32_t psx_mod_render_stereo_status(void); +/* Annotate the current frame's trace record with a short tag (scene transition, + * state change). Only meaningful while PSX_FRAME_TRACE is on and a traced frame + * is open (a headset frame, opened by psx_mod_openxr_begin); a no-op otherwise. */ +void psx_mod_frame_trace_note(const char *tag); +/* Bracket one guest call of a stereo eye's replay with a frame-trace sub-stage, + * so a hitch can be aimed at the call that grew instead of at the whole eye + * (the PSX_TRACE_GAME_* stages in frame_trace.h). The runtime does the timing, + * with the same clock and conversion as the eye's own stages, so the parts sum + * against eye_guest. A no-op unless PSX_FRAME_TRACE is on AND the call is made + * inside a traced stereo eye: a HUD capture's guest draws, the flat path and + * anything outside the eye's callback are ignored. A begin without a matching + * end (or an eye aborted mid-entry) contributes nothing; the unmeasured time + * stays visible as the difference between eye_guest and the sub-stages. + * Spans may nest: each stage keeps its own start tick, so a stage bracketed + * inside another is timed on its own and reports a subset of the outer span's + * wall time rather than a sibling of it. The hook's visibility override nests + * inside the guest traversal it is part of (game_select in game_traverse). */ +void psx_mod_frame_trace_stage_begin(uint32_t stage); +void psx_mod_frame_trace_stage_end(uint32_t stage); /* 0 disables stereo output; 1 shows a complete pair side by side. Capture * itself does not enable presentation. Default 0; applies to this session. */ int psx_mod_set_stereo_presentation(uint32_t mode); diff --git a/runtime/runtime.cmake b/runtime/runtime.cmake index 3879f5f4..1236eea2 100644 --- a/runtime/runtime.cmake +++ b/runtime/runtime.cmake @@ -486,6 +486,7 @@ set(PSXRECOMP_RUNTIME_SOURCES ${PSXRECOMP_ROOT}/runtime/src/starvation_ring.c ${PSXRECOMP_ROOT}/runtime/src/guest_cycle_profile.c ${PSXRECOMP_ROOT}/runtime/src/latency_ring.c + ${PSXRECOMP_ROOT}/runtime/src/frame_trace.c ${PSXRECOMP_ROOT}/runtime/src/present_image_ring.c ${PSXRECOMP_ROOT}/runtime/src/gpu_timeline.c ${PSXRECOMP_ROOT}/runtime/src/data_shards.c diff --git a/runtime/src/debug_server.c b/runtime/src/debug_server.c index af45cb55..341dee89 100644 --- a/runtime/src/debug_server.c +++ b/runtime/src/debug_server.c @@ -66,6 +66,7 @@ #include "crash_trace.h" #include "gpu_gl_renderer.h" #include "render_pass.h" +#include "frame_trace.h" #include "mod_plugins.h" /* psx_mod_render_pass_status */ #include "lockstep.h" #include "guest_tty.h" @@ -9332,6 +9333,34 @@ static void handle_render_pass_dump(int id, const char *json) send_fmt("{\"id\":%d,\"ok\":true,\"count\":%d}", id, count); } +/* frame_trace [format=json|chrome] [max=]: the opt-in stereo/OpenXR frame + * trace (frame_trace.c). Capturing requires PSX_FRAME_TRACE=1 at launch; this + * command only reads the ring and never runs in the frame path. `max` bounds the + * newest records returned (default 240). Both formats are JSON objects, so the + * reply embeds the export verbatim. */ +static void handle_frame_trace(int id, const char *json) { + char format[16] = "json"; + int max = json_get_int(json, "max", 240); + (void)json_get_str(json, "format", format, sizeof format); + if (max < 0) max = 0; + int chrome = strcmp(format, "chrome") == 0; + int need = chrome ? psx_frame_trace_to_chrome_json(NULL, 0, (uint32_t)max) + : psx_frame_trace_to_json(NULL, 0, (uint32_t)max); + if (need <= 0) { send_err(id, "frame trace export failed"); return; } + char *buf = (char *)malloc((size_t)need + 1); + if (!buf) { send_err(id, "out of memory"); return; } + if (chrome) (void)psx_frame_trace_to_chrome_json(buf, need + 1, (uint32_t)max); + else (void)psx_frame_trace_to_json(buf, need + 1, (uint32_t)max); + send_fmt("{\"id\":%d,\"ok\":true,\"enabled\":%d,\"capacity\":%u," + "\"count\":%llu,\"dropped\":%llu,\"missing\":%llu," + "\"format\":\"%s\",\"max\":%d,\"trace\":%s}", + id, psx_frame_trace_enabled(), psx_frame_trace_capacity(), + (unsigned long long)psx_frame_trace_committed(), + (unsigned long long)psx_frame_trace_dropped(), + (unsigned long long)psx_frame_trace_missing(), format, max, buf); + free(buf); +} + /* gl_wide_fast on=<0|1>: native-wide centre-blit fast path. 1 (default) = skip * the redundant centre mirror and copy the canonical 4:3 frame into the wide * surface centre at present (fast). 0 = re-rasterize the whole wide surface @@ -16255,6 +16284,7 @@ static const CmdEntry s_commands[] = { { "openxr_hands_override", handle_openxr_hands_override }, #endif { "stereo_stats", handle_stereo_stats }, + { "frame_trace", handle_frame_trace }, { "stereo_dump", handle_stereo_dump }, { "render_pass_dump", handle_render_pass_dump }, { "render_pass_refuse", handle_render_pass_refuse }, diff --git a/runtime/src/frame_trace.c b/runtime/src/frame_trace.c new file mode 100644 index 00000000..7dba7663 --- /dev/null +++ b/runtime/src/frame_trace.c @@ -0,0 +1,494 @@ +/* frame_trace.c — opt-in per-frame stereo/OpenXR timing ring. See frame_trace.h. + * + * Emulation/main thread only. The record path allocates nothing and writes no + * files; export functions fill a caller buffer and are called outside the frame. + */ + +#include "frame_trace.h" + +#include +#include +#include +#include + +/* Compile-time ring bound. The live capacity is a power of two <= this, chosen + * by PSX_FRAME_TRACE_CAP (default 2048, ~34 s of 60 Hz or ~68 s of 30 Hz pairs). + * Static, so enabling the trace never adds an allocation to the frame path. */ +#define PSX_FRAME_TRACE_MAX 4096 +#define PSX_FRAME_TRACE_DEFAULT 2048 + +static PSXFrameTraceRecord s_ring[PSX_FRAME_TRACE_MAX]; + +static int s_configured; +static int s_enabled; +static uint32_t s_cap = PSX_FRAME_TRACE_DEFAULT; + +static uint64_t s_opened; /* records opened since reset (== newest seq) */ +static uint64_t s_committed; /* records committed since reset */ +static uint64_t s_missing; /* commits that carried no stage at all */ +static uint64_t s_mono_high; /* last timestamp handed out (monotonic floor) */ + +static int s_have_open; +static int s_have_publish; +static uint64_t s_last_publish_ms; + +/* Session metadata (see psx_frame_trace_session). */ +static char s_runtime[128]; +static uint32_t s_target_period_us; + +static PSXFrameTraceClock s_clock; + +static const char *const s_stage_names[PSX_TRACE_STAGE_COUNT] = { + "guest_sim", "pair_total", + "eye_begin_l", "eye_guest_l", "eye_end_l", "eye_restore_l", + "eye_begin_r", "eye_guest_r", "eye_end_r", "eye_restore_r", + "xr_wait_frame", "xr_begin_frame", "xr_acquire", "xr_wait_image", + "xr_copy", "xr_release", "xr_end_frame", "pose_age", "hud_capture", + "hud_draw", "hud_readback", + "game_env", "game_world", "game_text", "game_traverse", "game_icon", + "game_submit", "game_laser", "game_select" +}; + +static const struct { uint32_t bit; const char *name; } s_flag_names[] = { + { PSX_FRAME_TRACE_STEREO_PUBLISHED, "published" }, + { PSX_FRAME_TRACE_STALE_PAIR, "stale" }, + { PSX_FRAME_TRACE_SHED, "shed" }, + { PSX_FRAME_TRACE_FAILED, "failed" }, + { PSX_FRAME_TRACE_XR_EMPTY, "xr_empty" }, + { PSX_FRAME_TRACE_SCENE_TRANSITION, "scene" }, + { PSX_FRAME_TRACE_IO_EVENT, "io" }, + { PSX_FRAME_TRACE_NO_TELEMETRY, "no_telemetry" }, + { PSX_FRAME_TRACE_BASELINE_REUSED, "baseline_reused" }, + { PSX_FRAME_TRACE_XR_REPEAT, "xr_repeat" } +}; + +const char *psx_frame_trace_stage_name(uint32_t stage) { + return stage < PSX_TRACE_STAGE_COUNT ? s_stage_names[stage] : "?"; +} + +const char *psx_frame_trace_flag_names(uint32_t flags, char *buf, int cap) { + int off = 0; + if (!buf || cap <= 0) return ""; + buf[0] = '\0'; + for (size_t i = 0; i < sizeof s_flag_names / sizeof s_flag_names[0]; ++i) { + if (!(flags & s_flag_names[i].bit)) continue; + off += snprintf(buf + off, (size_t)(cap - off), "%s%s", + off ? "|" : "", s_flag_names[i].name); + if (off >= cap) break; + } + return buf; +} + +static uint32_t next_pow2(uint32_t v) { + uint32_t p = 1; + while (p < v && p < PSX_FRAME_TRACE_MAX) p <<= 1; + return p; +} + +static void configure_from_env(void) { + const char *e = getenv("PSX_FRAME_TRACE"); + s_enabled = (e && e[0] && e[0] != '0') ? 1 : 0; + e = getenv("PSX_FRAME_TRACE_CAP"); + if (e && e[0]) { + long v = strtol(e, NULL, 0); + if (v >= 64) s_cap = next_pow2((uint32_t)v); + } +} + +static void ensure_configured(void) { + if (!s_configured) { + s_configured = 1; + configure_from_env(); + } +} + +void psx_frame_trace_configure_from_env(void) { + s_configured = 1; + configure_from_env(); +} + +void psx_frame_trace_set_clock(PSXFrameTraceClock fn) { s_clock = fn; } + +void psx_frame_trace_set_enabled(int on) { + ensure_configured(); + s_enabled = on ? 1 : 0; +} + +int psx_frame_trace_enabled(void) { + ensure_configured(); + return s_enabled; +} + +void psx_frame_trace_reset(void) { + memset(s_ring, 0, sizeof s_ring); + s_opened = s_committed = s_missing = s_mono_high = 0; + s_have_open = s_have_publish = 0; + s_last_publish_ms = 0; + s_runtime[0] = '\0'; + s_target_period_us = 0; +} + +static PSXFrameTraceRecord *open_record(void) { + return &s_ring[(s_opened - 1u) & (uint64_t)(s_cap - 1u)]; +} + +static uint64_t stamp_now(void) { + uint64_t now = s_clock ? s_clock() : 0; + /* Monotonic floor: a coarse or throttled clock must never make a later + * record look older than the one before it. */ + if (now < s_mono_high) now = s_mono_high; + s_mono_high = now; + return now; +} + +int psx_frame_trace_open(void) { + ensure_configured(); + if (!s_enabled || s_have_open) return 0; + uint64_t seq = s_opened + 1u; + PSXFrameTraceRecord *r = &s_ring[(seq - 1u) & (uint64_t)(s_cap - 1u)]; + memset(r, 0, sizeof *r); + r->seq = seq; + r->eye_failed = -1; + r->open_ms = stamp_now(); + s_opened = seq; + s_have_open = 1; + return 1; +} + +void psx_frame_trace_add(uint32_t stage, uint64_t us) { + if (!s_have_open || stage >= PSX_TRACE_STAGE_COUNT) return; + PSXFrameTraceRecord *r = open_record(); + uint64_t v = (uint64_t)r->stages_us[stage] + us; + r->stages_us[stage] = v > 0xFFFFFFFFu ? 0xFFFFFFFFu : (uint32_t)v; +} + +void psx_frame_trace_ids(uint64_t guest_frame, uint64_t guest_cycle) { + if (!s_have_open) return; + PSXFrameTraceRecord *r = open_record(); + r->guest_frame = guest_frame; + r->guest_cycle = guest_cycle; +} + +void psx_frame_trace_attempt(uint64_t attempt_id, uint32_t shed_skipped, + uint32_t shed_streak_ms) { + if (!s_have_open) return; + PSXFrameTraceRecord *r = open_record(); + r->stereo_attempt = attempt_id; + r->shed_skipped = shed_skipped; + r->shed_streak_ms = shed_streak_ms; + if (shed_skipped) r->flags |= PSX_FRAME_TRACE_SHED; +} + +void psx_frame_trace_result(int published, uint64_t pair_id, int eye_failed, + const char *failure) { + if (!s_have_open) return; + PSXFrameTraceRecord *r = open_record(); + r->pair_id = pair_id; + r->eye_failed = eye_failed < 0 ? -1 : eye_failed; + if (published) { + r->flags |= PSX_FRAME_TRACE_STEREO_PUBLISHED; + r->source = 1; + return; + } + r->flags |= PSX_FRAME_TRACE_STALE_PAIR; + if (failure && failure[0]) { + r->flags |= PSX_FRAME_TRACE_FAILED; + snprintf(r->failure, sizeof r->failure, "%s", failure); + } +} + +void psx_frame_trace_source(uint8_t source) { + if (!s_have_open) return; + PSXFrameTraceRecord *r = open_record(); + /* First writer wins: a published stereo pair must not be relabelled + * "native" by a later end-of-frame annotation. */ + if (!r->source) r->source = source; +} + +void psx_frame_trace_draw(int eye, uint32_t prims, uint32_t textured) { + if (!s_have_open || eye < 0 || eye > 1) return; + PSXFrameTraceRecord *r = open_record(); + /* Per-eye fields first: `prims`/`textured_prims` are the legacy pair-level + * view of the same counter and stay the last eye's values, as before. */ + if (eye == 0) { r->prims_l = prims; r->tex_prims_l = textured; } + else { r->prims_r = prims; r->tex_prims_r = textured; } + r->prims = prims; + r->textured_prims = textured; +} + +void psx_frame_trace_eye_cycles(int eye, uint32_t cycles) { + if (!s_have_open || eye < 0 || eye > 1) return; + PSXFrameTraceRecord *r = open_record(); + if (eye == 0) r->eye_cycles_l = cycles; + else r->eye_cycles_r = cycles; +} + +void psx_frame_trace_gpu(uint64_t tex_upload_bytes, uint32_t vram_leaks) { + if (!s_have_open) return; + PSXFrameTraceRecord *r = open_record(); + r->tex_upload_bytes += tex_upload_bytes; + uint64_t leaks = (uint64_t)r->vram_leaks + vram_leaks; + r->vram_leaks = leaks > 0xFFFFFFFFu ? 0xFFFFFFFFu : (uint32_t)leaks; +} + +void psx_frame_trace_xr_cadence(uint32_t display_delta_us, int submit_fresh) { + if (!s_have_open) return; + PSXFrameTraceRecord *r = open_record(); + r->xr_display_delta_us = display_delta_us; + r->xr_submit_fresh = submit_fresh ? 1u : 0u; + if (!submit_fresh) r->flags |= PSX_FRAME_TRACE_XR_REPEAT; +} + +void psx_frame_trace_flag(uint32_t flag) { + if (!s_have_open) return; + open_record()->flags |= flag; +} + +void psx_frame_trace_note(const char *tag) { + if (!s_have_open || !tag) return; + snprintf(open_record()->note, PSX_FRAME_TRACE_NOTE, "%s", tag); +} + +void psx_frame_trace_io(const char *tag) { + if (!s_have_open) return; + PSXFrameTraceRecord *r = open_record(); + r->flags |= PSX_FRAME_TRACE_IO_EVENT; + if (tag) snprintf(r->note, PSX_FRAME_TRACE_NOTE, "%s", tag); +} + +void psx_frame_trace_session(const char *runtime, uint32_t target_period_us) { + if (!s_enabled) return; + if (target_period_us != s_target_period_us) s_target_period_us = target_period_us; + if (runtime && runtime != s_runtime && strcmp(runtime, s_runtime) != 0) + snprintf(s_runtime, sizeof s_runtime, "%s", runtime); +} + +void psx_frame_trace_commit(void) { + if (!s_have_open) return; + PSXFrameTraceRecord *r = open_record(); + int any = 0; + for (uint32_t i = 0; i < PSX_TRACE_STAGE_COUNT; ++i) + if (r->stages_us[i]) { any = 1; break; } + /* A frame that only shed its pair genuinely has no stage: that is "stale", + * not "no telemetry". Only an expected-to-be-instrumented frame that + * recorded nothing at all is missing. */ + if (!any && !(r->flags & PSX_FRAME_TRACE_SHED)) { + s_missing++; + r->flags |= PSX_FRAME_TRACE_NO_TELEMETRY; + } + if (r->flags & PSX_FRAME_TRACE_STEREO_PUBLISHED) { + s_have_publish = 1; + s_last_publish_ms = r->open_ms; + r->pair_age_ms = 0; + } else if (s_have_publish) { + r->pair_age_ms = r->open_ms - s_last_publish_ms; + } + s_committed++; + s_have_open = 0; +} + +uint32_t psx_frame_trace_capacity(void) { return s_cap; } +uint64_t psx_frame_trace_committed(void) { return s_committed; } +uint64_t psx_frame_trace_dropped(void) { + return s_committed > s_cap ? s_committed - s_cap : 0; +} +uint64_t psx_frame_trace_missing(void) { return s_missing; } + +uint32_t psx_frame_trace_read(PSXFrameTraceRecord *out, uint32_t capacity) { + uint32_t n = 0; + if (!out || !capacity || !s_committed) return 0; + uint64_t retained = s_committed < s_cap ? s_committed : s_cap; + uint64_t take = capacity < retained ? capacity : retained; + uint64_t first = s_committed - take + 1u; /* oldest of the newest `take` */ + for (uint64_t seq = first; seq <= s_committed; ++seq) { + const PSXFrameTraceRecord *r = + &s_ring[(seq - 1u) & (uint64_t)(s_cap - 1u)]; + if (r->seq != seq) continue; /* overwritten / not ours */ + out[n++] = *r; + } + return n; +} + +/* ---- export ------------------------------------------------------------- */ + +static int app(char *buf, int cap, int off, const char *fmt, ...) { + va_list ap; + if (off < 0) off = 0; + if (buf && off < cap) { + va_start(ap, fmt); + int n = vsnprintf(buf + off, (size_t)(cap - off), fmt, ap); + va_end(ap); + return n < 0 ? off : off + n; + } + va_start(ap, fmt); + int n = vsnprintf(NULL, 0, fmt, ap); /* measure only */ + va_end(ap); + return n < 0 ? off : off + n; +} + +/* JSON string body with the few escapes a generated tool must not break on. */ +static const char *jstr(const char *s, char *tmp, int cap) { + int o = 0; + if (cap <= 0) return ""; + tmp[0] = '\0'; + for (const char *p = s; p && *p && o < cap - 2; ++p) { + unsigned char c = (unsigned char)*p; + if (c == '"' || c == '\\') { tmp[o++] = '\\'; tmp[o++] = (char)c; } + else if (c == '\n') { tmp[o++] = '\\'; if (o < cap - 1) tmp[o++] = 'n'; } + else if (c < 0x20) { o += snprintf(tmp + o, (size_t)(cap - o), "\\u%04x", c); } + else tmp[o++] = (char)c; + } + if (o > cap - 1) o = cap - 1; + tmp[o] = '\0'; + return tmp; +} + +static int rec_json(char *buf, int cap, int off, const PSXFrameTraceRecord *r) { + char t1[PSX_FRAME_TRACE_REASON * 2 + 8], t2[PSX_FRAME_TRACE_NOTE * 2 + 8]; + off = app(buf, cap, off, + "{\"seq\":%llu,\"open_ms\":%llu,\"guest_frame\":%llu," + "\"guest_cycle\":%llu,\"attempt\":%llu,\"pair_id\":%llu," + "\"pair_age_ms\":%llu,\"source\":%u,\"eye_failed\":%d," + "\"shed\":%u,\"shed_ms\":%u,\"prims\":%u,\"tex_prims\":%u," + "\"prims_l\":%u,\"prims_r\":%u,\"tex_prims_l\":%u," + "\"tex_prims_r\":%u,\"cycles_l\":%u,\"cycles_r\":%u," + "\"vram_leaks\":%u,\"tex_bytes\":%llu,\"flags\":%u," + "\"xr_display_delta_us\":%u,\"xr_submit_fresh\":%u," + "\"failure\":\"%s\",\"note\":\"%s\",\"stages_us\":{", + (unsigned long long)r->seq, (unsigned long long)r->open_ms, + (unsigned long long)r->guest_frame, + (unsigned long long)r->guest_cycle, + (unsigned long long)r->stereo_attempt, + (unsigned long long)r->pair_id, + (unsigned long long)r->pair_age_ms, r->source, r->eye_failed, + r->shed_skipped, r->shed_streak_ms, r->prims, r->textured_prims, + r->prims_l, r->prims_r, r->tex_prims_l, r->tex_prims_r, + r->eye_cycles_l, r->eye_cycles_r, + r->vram_leaks, (unsigned long long)r->tex_upload_bytes, r->flags, + r->xr_display_delta_us, (unsigned)r->xr_submit_fresh, + jstr(r->failure, t1, sizeof t1), jstr(r->note, t2, sizeof t2)); + for (uint32_t s = 0; s < PSX_TRACE_STAGE_COUNT; ++s) + off = app(buf, cap, off, "%s\"%s\":%u", s ? "," : "", + s_stage_names[s], r->stages_us[s]); + return app(buf, cap, off, "}}"); +} + +/* Newest `max_records` (0 = all retained), oldest first. Returns the count and + * the seq of the first record to read; iteration indexes the ring in place so + * no export call touches the stack with a whole-ring copy. */ +static uint32_t export_window(uint32_t max_records, uint64_t *first_seq) { + uint64_t retained = s_committed < s_cap ? s_committed : s_cap; + if (!retained) { *first_seq = 1; return 0; } + uint32_t n = (uint32_t)retained; + uint32_t take = (max_records && max_records < n) ? max_records : n; + *first_seq = s_committed - take + 1u; + return take; +} + +static const PSXFrameTraceRecord *at_seq(uint64_t seq) { + const PSXFrameTraceRecord *r = &s_ring[(seq - 1u) & (uint64_t)(s_cap - 1u)]; + return r->seq == seq ? r : NULL; +} + +int psx_frame_trace_to_json(char *buf, int cap, uint32_t max_records) { + char t1[256]; + uint64_t first; + if (cap < 0 || (buf && cap == 0)) return 0; + if (buf) buf[0] = '\0'; + uint32_t take = export_window(max_records, &first); + int off = app(buf, cap, 0, + "{\"enabled\":%d,\"capacity\":%u,\"count\":%llu," + "\"dropped\":%llu,\"missing\":%llu," + "\"runtime\":\"%s\",\"target_period_us\":%u,\"records\":[", + s_enabled, s_cap, (unsigned long long)s_committed, + (unsigned long long)psx_frame_trace_dropped(), + (unsigned long long)s_missing, + jstr(s_runtime, t1, sizeof t1), s_target_period_us); + int emitted = 0; + for (uint64_t seq = first; emitted < (int)take; ++seq) { + const PSXFrameTraceRecord *r = at_seq(seq); + if (!r) continue; + off = app(buf, cap, off, "%s", emitted ? "," : ""); + off = rec_json(buf, cap, off, r); + emitted++; + } + return app(buf, cap, off, "]}"); +} + +int psx_frame_trace_to_csv(char *buf, int cap, uint32_t max_records) { + char flags[96]; + uint64_t first; + if (cap < 0 || (buf && cap == 0)) return 0; + if (buf) buf[0] = '\0'; + uint32_t take = export_window(max_records, &first); + int off = app(buf, cap, 0, + "seq,open_ms,guest_frame,guest_cycle,attempt,pair_id," + "pair_age_ms,source,eye_failed,flags,shed,streak_ms,prims," + "tex_prims,prims_l,prims_r,tex_prims_l,tex_prims_r,cycles_l," + "cycles_r,vram_leaks,tex_bytes,xr_display_delta_us," + "xr_submit_fresh"); + for (uint32_t s = 0; s < PSX_TRACE_STAGE_COUNT; ++s) + off = app(buf, cap, off, ",%s", s_stage_names[s]); + off = app(buf, cap, off, ",failure,note\n"); + int emitted = 0; + for (uint64_t seq = first; emitted < (int)take; ++seq) { + const PSXFrameTraceRecord *r = at_seq(seq); + if (!r) continue; + emitted++; + off = app(buf, cap, off, + "%llu,%llu,%llu,%llu,%llu,%llu,%llu,%u,%d,\"%s\",%u,%u,%u," + "%u,%u,%u,%u,%u,%u,%u,%u,%llu,%u,%u", + (unsigned long long)r->seq, (unsigned long long)r->open_ms, + (unsigned long long)r->guest_frame, + (unsigned long long)r->guest_cycle, + (unsigned long long)r->stereo_attempt, + (unsigned long long)r->pair_id, + (unsigned long long)r->pair_age_ms, r->source, r->eye_failed, + psx_frame_trace_flag_names(r->flags, flags, sizeof flags), + r->shed_skipped, r->shed_streak_ms, r->prims, r->textured_prims, + r->prims_l, r->prims_r, r->tex_prims_l, r->tex_prims_r, + r->eye_cycles_l, r->eye_cycles_r, + r->vram_leaks, (unsigned long long)r->tex_upload_bytes, + r->xr_display_delta_us, (unsigned)r->xr_submit_fresh); + for (uint32_t s = 0; s < PSX_TRACE_STAGE_COUNT; ++s) + off = app(buf, cap, off, ",%u", r->stages_us[s]); + off = app(buf, cap, off, ",\"%s\",\"%s\"\n", r->failure, r->note); + } + return off; +} + +int psx_frame_trace_to_chrome_json(char *buf, int cap, uint32_t max_records) { + char flags[96]; + uint64_t first; + if (cap < 0 || (buf && cap == 0)) return 0; + if (buf) buf[0] = '\0'; + uint32_t take = export_window(max_records, &first); + int off = app(buf, cap, 0, "{\"traceEvents\":["); + int first_ev = 1, emitted = 0; + for (uint64_t seq = first; emitted < (int)take; ++seq) { + const PSXFrameTraceRecord *r = at_seq(seq); + if (!r) continue; + emitted++; + uint64_t base_us = r->open_ms * 1000u, at = 0; + for (uint32_t s = 0; s < PSX_TRACE_STAGE_COUNT; ++s) { + uint64_t dur = r->stages_us[s]; + if (dur) { + off = app(buf, cap, off, + "%s{\"name\":\"%s\",\"ph\":\"X\",\"ts\":%llu," + "\"dur\":%llu,\"pid\":1,\"tid\":1,\"args\":{" + "\"seq\":%llu,\"attempt\":%llu,\"pair_id\":%llu," + "\"flags\":\"%s\"}}", + first_ev ? "" : ",", s_stage_names[s], + (unsigned long long)(base_us + at), + (unsigned long long)dur, + (unsigned long long)r->seq, + (unsigned long long)r->stereo_attempt, + (unsigned long long)r->pair_id, + psx_frame_trace_flag_names(r->flags, flags, sizeof flags)); + first_ev = 0; + } + at += dur; + } + } + return app(buf, cap, off, "]}"); +} diff --git a/runtime/src/gpu_gl_renderer.c b/runtime/src/gpu_gl_renderer.c index 098646d3..ddecb0f6 100644 --- a/runtime/src/gpu_gl_renderer.c +++ b/runtime/src/gpu_gl_renderer.c @@ -7588,6 +7588,14 @@ uint64_t gl_renderer_perf_prim_split(double *out_tex_frac) { return s_scene_prims; } +/* Cumulative scene primitives for per-frame attribution (frame_trace). Callers + * take deltas; these counters are never reset here. */ +void gl_renderer_scene_prim_counts(uint64_t *prims, uint64_t *textured) { + GL_RT_SYNC("scene_prim_counts"); + if (prims) *prims = s_scene_prims; + if (textured) *textured = s_scene_prims_tex; +} + int gl_renderer_perf_aggregate(int wide_filter, double out[18]) { GL_RT_SYNC("perf_aggregate"); for (int i = 0; i < 18; i++) out[i] = 0.0; diff --git a/runtime/src/psx_openxr.c b/runtime/src/psx_openxr.c index e9244553..1cf463ba 100644 --- a/runtime/src/psx_openxr.c +++ b/runtime/src/psx_openxr.c @@ -9,6 +9,8 @@ #include "psx_openxr.h" #include "vr_pose_math.h" #include "openxr_color.h" +#include "frame_trace.h" +#include "gpu_gl_renderer.h" #include #include #include @@ -103,6 +105,17 @@ int psx_openxr_surface_valid(const PSXModOpenXRSurface *v) { #define XR_USE_GRAPHICS_API_OPENGL #include #include + +/* Frame-trace stage helper: perf ticks -> microseconds (CPU submission/wait + * wall time only, never GPU execution time). */ +static uint64_t xr_trace_us(uint64_t ticks) { + static double inv = 0.0; + if (inv == 0.0) { + double f = (double)gl_renderer_perf_frequency(); + inv = f > 0.0 ? 1000000.0 / f : 1.0; + } + return (uint64_t)((double)ticks * inv); +} static XrInstance s_instance; static XrSystemId s_system; static XrSession s_session; @@ -123,6 +136,13 @@ static XrView s_views[2]; static PSXModRenderView s_render[2]; static double s_oq[4],s_op[3]; static XrTime s_time; +/* Cadence telemetry for the frame trace: the runtime's own predicted display + * time delta between submissions (what the compositor actually saw), and when + * the pose for the current frame was sampled (xrLocateViews return). */ +static XrTime s_prev_display_time; +static int s_prev_display_valid; +static uint32_t s_display_delta_us; +static uint64_t s_pose_sample_ticks; /* Loss handling. A lost session/instance (or an unusable swapchain) is only * flagged where it is detected; service_loss() tears the stack down at the end * of the public call so no caller loses handles mid-sequence. */ @@ -354,6 +374,9 @@ static void hands_locate(int focused) { /* Destroy every XR object (children before parents). Safe on a partially * initialised stack and on a lost session: destroy calls need no live session. */ static void xr_destroy(void) { + /* A new session must not inherit the previous one's display times: the + * first delta after a re-init would be meaningless. */ + s_prev_display_valid=0;s_prev_display_time=0; for(int eye=0;eye<2;eye++) { for(int k=0;k<2;k++) { if(s_pose_space[eye][k])xrDestroySpace(s_pose_space[eye][k]); @@ -547,11 +570,38 @@ int psx_openxr_begin(int width,int height,double units) { s_recovering=0; } if(!events() || !s_stats.running){(void)service_loss();return 0;} + /* Open this frame's trace record before the blocking waits. The record is + * committed by psx_openxr_end(); the two failure returns below commit it + * themselves so a lost session cannot leave a record open forever. */ + psx_frame_trace_open(); + /* Cleared so a frame that never reaches xrLocateViews (no render request, + * lost tracking, bad view maths) cannot report a stale pose age. */ + s_pose_sample_ticks=0; + uint64_t trace_t = gl_renderer_perf_ticks(); XrFrameWaitInfo wait={XR_TYPE_FRAME_WAIT_INFO};XrFrameState fs={XR_TYPE_FRAME_STATE}; - if(!check(xrWaitFrame(s_session,&wait,&fs),"wait_frame")){(void)service_loss();return 0;} + if(!check(xrWaitFrame(s_session,&wait,&fs),"wait_frame")){ + psx_frame_trace_add(PSX_TRACE_XR_WAIT_FRAME, xr_trace_us(gl_renderer_perf_ticks()-trace_t)); + (void)service_loss();psx_frame_trace_commit();return 0;} + psx_frame_trace_add(PSX_TRACE_XR_WAIT_FRAME, xr_trace_us(gl_renderer_perf_ticks()-trace_t)); s_stats.waits++;s_time=fs.predictedDisplayTime;s_stats.predicted_time=(uint64_t)s_time; + /* The runtime's own clock between submissions. This is the cadence the + * compositor saw, independent of our frame timing: delta / display period + * is how many granted periods one presented frame consumed. */ + s_display_delta_us=0; + if(s_prev_display_valid && s_time>s_prev_display_time) { + uint64_t d=(uint64_t)(s_time-s_prev_display_time)/1000u; + s_display_delta_us=d>0xFFFFFFFFull?0xFFFFFFFFu:(uint32_t)d; + } + s_prev_display_time=s_time;s_prev_display_valid=1; + /* Record the runtime display period with its name for cadence analysis. */ + psx_frame_trace_session(s_stats.runtime, + fs.predictedDisplayPeriod>0?(uint32_t)(fs.predictedDisplayPeriod/1000):0u); + trace_t = gl_renderer_perf_ticks(); XrFrameBeginInfo begin={XR_TYPE_FRAME_BEGIN_INFO}; - if(!check(xrBeginFrame(s_session,&begin),"begin_frame")){(void)service_loss();return 0;} + if(!check(xrBeginFrame(s_session,&begin),"begin_frame")){ + psx_frame_trace_add(PSX_TRACE_XR_BEGIN_FRAME, xr_trace_us(gl_renderer_perf_ticks()-trace_t)); + (void)service_loss();psx_frame_trace_commit();return 0;} + psx_frame_trace_add(PSX_TRACE_XR_BEGIN_FRAME, xr_trace_us(gl_renderer_perf_ticks()-trace_t)); s_stats.frame_open=1;s_stats.tracking=0; if(!fs.shouldRender){psx_openxr_end(0,NULL);return 0;} XrViewLocateInfo loc={XR_TYPE_VIEW_LOCATE_INFO};loc.viewConfigurationType=XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; @@ -559,6 +609,7 @@ int psx_openxr_begin(int width,int height,double units) { XrViewState vs={XR_TYPE_VIEW_STATE};uint32_t count=0; memset(s_views,0,sizeof s_views);s_views[0].type=s_views[1].type=XR_TYPE_VIEW; int located=check(xrLocateViews(s_session,&loc,&vs,2,&count,s_views),"locate_views"); + s_pose_sample_ticks=gl_renderer_perf_ticks(); s_stats.view_flags=(uint64_t)vs.viewStateFlags; if(!located || count!=2 || (vs.viewStateFlags&(XR_VIEW_STATE_ORIENTATION_VALID_BIT|XR_VIEW_STATE_POSITION_VALID_BIT))!= @@ -619,26 +670,37 @@ int psx_openxr_quad(double distance,double width,double height) { int psx_openxr_end(int keep,PSXOpenXRCopy copy) { #if defined(PSX_OPENXR) if(!s_stats.frame_open)return 0; + /* Defensive open: normally psx_openxr_begin() already opened the record. */ + psx_frame_trace_open(); + uint64_t trace_t; XrCompositionLayerProjectionView pv[2]={{XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW},{XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}}; int complete=keep && copy && s_stats.tracking; int quad=s_quad_distance>0; for(int eye=0;eye<(quad?1:2) && complete;eye++) { uint32_t index=0; + trace_t = gl_renderer_perf_ticks(); XrSwapchainImageAcquireInfo ai={XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO}; if(!check(xrAcquireSwapchainImage(s_chain[eye],&ai,&index),"acquire")){complete=0;break;} + psx_frame_trace_add(PSX_TRACE_XR_ACQUIRE, xr_trace_us(gl_renderer_perf_ticks()-trace_t)); XrSwapchainImageWaitInfo wi={XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO};wi.timeout=1000000000; + trace_t = gl_renderer_perf_ticks(); XrResult wr=xrWaitSwapchainImage(s_chain[eye],&wi); /* A timeout leaves ownership acquired: wait again (bounded) before * releasing. Only XR_SUCCESS means the image is writable; the timeout * code is a success code and must not be mistaken for it. */ if(wr==XR_TIMEOUT_EXPIRED){wi.timeout=2000000000;wr=xrWaitSwapchainImage(s_chain[eye],&wi);} + psx_frame_trace_add(PSX_TRACE_XR_WAIT_IMAGE, xr_trace_us(gl_renderer_perf_ticks()-trace_t)); int writable=check(wr,"wait_image") && wr==XR_SUCCESS; if(writable) { + trace_t = gl_renderer_perf_ticks(); if(index>=s_count[eye] || !copy(eye,s_images[eye][index].image,s_w[eye],s_h[eye])) { s_stats.stage="copy_image";s_stats.failures++;complete=0; } + psx_frame_trace_add(PSX_TRACE_XR_COPY, xr_trace_us(gl_renderer_perf_ticks()-trace_t)); + trace_t = gl_renderer_perf_ticks(); XrSwapchainImageReleaseInfo ri={XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO}; if(!check(xrReleaseSwapchainImage(s_chain[eye],&ri),"release"))complete=0; + psx_frame_trace_add(PSX_TRACE_XR_RELEASE, xr_trace_us(gl_renderer_perf_ticks()-trace_t)); } else { /* Acquired but never writable. Release it best-effort so the acquire * count cannot leak; a runtime that refuses (the wait never completed) @@ -665,7 +727,13 @@ int psx_openxr_end(int keep,PSXOpenXRCopy copy) { XrFrameEndInfo end={XR_TYPE_FRAME_END_INFO};end.displayTime=s_time; end.environmentBlendMode=XR_ENVIRONMENT_BLEND_MODE_OPAQUE; end.layerCount=complete?1:0;end.layers=complete?layers:NULL; + trace_t = gl_renderer_perf_ticks(); int ok=check(xrEndFrame(s_session,&end),"end_frame");s_stats.frame_open=0; + psx_frame_trace_add(PSX_TRACE_XR_END_FRAME, xr_trace_us(gl_renderer_perf_ticks()-trace_t)); + if(s_pose_sample_ticks) + psx_frame_trace_add(PSX_TRACE_POSE_AGE, + xr_trace_us(gl_renderer_perf_ticks()-s_pose_sample_ticks)); + psx_frame_trace_xr_cadence(s_display_delta_us, complete); if(ok && complete) { s_stats.submitted_surfaces=quad?0:((s_surface_mask&1)!=0)+((s_surface_mask&2)!=0); s_stats.submitted++;s_stats.submitted_pair_id=s_pair_id; @@ -675,9 +743,18 @@ int psx_openxr_end(int keep,PSXOpenXRCopy copy) { if(s_source==2)s_stats.native_submitted++; if(quad){s_stats.quad_submitted++;s_stats.quad_distance_m=s_quad_distance; s_stats.quad_width_m=s_quad_width;s_stats.quad_height_m=s_quad_height;} - } else {s_stats.empty++;s_stats.submitted_surfaces=0;} + /* A fresh stereo pair already labelled the record source 1; only + * annotate native presentation when nothing else claimed it. */ + psx_frame_trace_source(2); + } else { + s_stats.empty++;s_stats.submitted_surfaces=0; + psx_frame_trace_flag(PSX_FRAME_TRACE_XR_EMPTY); + psx_frame_trace_flag(PSX_FRAME_TRACE_STALE_PAIR); + psx_frame_trace_source(2); + } if(ok && complete)s_stats.stage="submitted"; s_quad_distance=s_quad_width=s_quad_height=0; + psx_frame_trace_commit(); (void)service_loss(); /* after the frame is closed: lost session / bad swapchain */ return ok && complete; #else diff --git a/runtime/src/render_pass.c b/runtime/src/render_pass.c index 705600c0..e25ab04c 100644 --- a/runtime/src/render_pass.c +++ b/runtime/src/render_pass.c @@ -26,8 +26,10 @@ #include "cpu_state.h" #include "dirty_ram_interp.h" #include "dma.h" +#include "frame_trace.h" #include "gpu.h" #include "gpu_gl_renderer.h" +#include "host_time.h" #include "mod_plugins.h" #include "mod_runtime.h" #include "interrupts.h" @@ -282,6 +284,9 @@ static const char *s_checkpoint_failure; static RenderStereoStats s_stereo; static int s_pair_busy; static uint32_t s_pair_retry; +/* A HUD-only capture transaction reuses eye 0's slot but is not an eye draw; + * frame tracing must not attribute its cost to the left eye. */ +static int s_capture_only; static int pass_refuse(const char *reason, uint32_t status) { /* A refusal always carries a reason: last_failure.reason == NULL means @@ -338,6 +343,11 @@ void render_pass_reset_session(void) { s_stereo.last_eye = -1; s_pair_busy = 0; s_pair_retry = 0; + /* Frame tracing is opt-in per session: re-read the env, install the host + * clock, and clear any previous session's records. */ + psx_frame_trace_configure_from_env(); + psx_frame_trace_set_clock(psx_host_mono_ms); + psx_frame_trace_reset(); gl_renderer_stereo_reset(); gl_renderer_pass_set_flip_shown(0); } @@ -393,6 +403,14 @@ void render_stereo_get_stats(RenderStereoStats *out) { if (out) *out = s_stereo; int psx_mod_set_stereo_presentation(uint32_t mode) { return mode <= 1u ? gl_renderer_stereo_set_presentation(mode) : 0; } + +/* Game-side annotation for the frame trace (mod_plugins.h): a title hook marks + * a scene/state transition on the record the runtime opened for this frame. A + * no-op when tracing is off or no record is open. */ +void psx_mod_frame_trace_note(const char *tag) { + psx_frame_trace_note(tag); + psx_frame_trace_flag(PSX_FRAME_TRACE_SCENE_TRANSITION); +} int psx_mod_render_view_offset(int32_t x, int32_t y, int32_t z) { int32_t xyz[3] = {x, y, z}; if (!g_psx_render_pass_active) return 0; @@ -605,6 +623,51 @@ static double ema_ms(double cur, uint64_t ticks) { return cur > 0.0 ? cur * 0.9 + ms * 0.1 : ms; } +/* Ticks -> microseconds for frame_trace. CPU submission time only: it is not + * GPU execution time. */ +static double s_us_per_tick = 0.0; +static uint64_t trace_ticks_to_us(uint64_t ticks) { + if (s_us_per_tick == 0.0) { + double f = (double)gl_renderer_perf_frequency(); + s_us_per_tick = f > 0.0 ? 1000000.0 / f : 1.0; + } + return (uint64_t)((double)ticks * s_us_per_tick); +} + +/* ---- guest sub-stage spans (mod_plugins.h) ------------------------------ + * The game hook brackets the guest calls it replays once per eye, and the + * runtime times them with its own tick clock, exactly as the eye stages are + * timed, so the parts sum against eye_guest. Accepted only inside a traced + * stereo eye (s_sub_eye): a HUD capture's guest draws and the flat path report + * their own stages and must not write these slots. */ +static int s_sub_eye = -1; /* traced eye accepting spans, -1 = none */ +/* One start tick per stage rather than one open span at a time, so a stage can + * be bracketed inside another: the hook's visibility override runs inside the + * guest traversal it is part of. The outer span keeps its own wall time, so a + * nested stage is a subset of it rather than a sibling. */ +static int s_sub_open[PSX_TRACE_STAGE_COUNT]; +static uint64_t s_sub_ticks[PSX_TRACE_STAGE_COUNT]; + +/* No span survives an eye boundary, so an eye aborted mid-entry cannot carry a + * start tick into the next one. */ +static void sub_spans_forget(void) { + memset(s_sub_open, 0, sizeof s_sub_open); +} + +void psx_mod_frame_trace_stage_begin(uint32_t stage) { + if (s_sub_eye < 0 || stage >= PSX_TRACE_STAGE_COUNT) return; + s_sub_open[stage] = 1; /* an unpaired begin restarts that span */ + s_sub_ticks[stage] = gl_renderer_perf_ticks(); +} + +void psx_mod_frame_trace_stage_end(uint32_t stage) { + if (s_sub_eye < 0 || stage >= PSX_TRACE_STAGE_COUNT) return; + if (!s_sub_open[stage]) return; /* an end with no begin records nothing */ + uint64_t now = gl_renderer_perf_ticks(); + psx_frame_trace_add(stage, trace_ticks_to_us(now - s_sub_ticks[stage])); + s_sub_open[stage] = 0; +} + static void watchdog_overrun(void) { s_stats.watchdog++; s_stats.watchdog_flag = 1; @@ -692,6 +755,10 @@ static int render_transaction(struct CPUState *cpu, const PSXModRenderPass *pass s_attempt.attempt = ++s_stats.pass_attempts; s_attempt.plan = s_plan_serial; s_attempt.guest_cycle = psx_cycle_count; + /* No guest sub-stage span may survive into this transaction: the eye that + * accepts them is decided below, once the trace decision is known. */ + s_sub_eye = -1; + sub_spans_forget(); if (pass) { s_attempt.struct_size = pass->struct_size; if (pass->struct_size >= sizeof *pass) { @@ -726,6 +793,16 @@ static int render_transaction(struct CPUState *cpu, const PSXModRenderPass *pass if (inv == 0.0) inv = 1000.0 / (double)gl_renderer_perf_frequency(); s_ms_per_tick = inv; } + /* Per-eye trace sampling (opt-in): primitive counts and upload pixels are + * cumulative, so the eye's cost is the delta across its transaction. */ + int trace_on = eye >= 0 && !s_capture_only && psx_frame_trace_enabled(); + uint64_t trace_prims0 = 0, trace_tex0 = 0, trace_diag0[6] = {0,0,0,0,0,0}; + if (trace_on) { + gl_renderer_scene_prim_counts(&trace_prims0, &trace_tex0); + gl_renderer_runtime_diag(trace_diag0); + } + /* This eye, and only this eye, accepts the hook's guest sub-stage spans. */ + s_sub_eye = trace_on ? (int)eye : -1; reuse = !open && eye != RP_EYE_LOCAL && s_restored_valid && s_restored_plan == s_plan_serial && s_restored_cycle == psx_cycle_count && @@ -794,7 +871,13 @@ static int render_transaction(struct CPUState *cpu, const PSXModRenderPass *pass "%s", s_abort_detail); ok = 0; } - s_stats.guest_cycles_last = psx_cycle_count - cycles_before; + /* Any span still open here was never ended by the hook, or the eye aborted + * mid-entry through the longjmp above. Drop them instead of closing them: + * the elapsed time is then not charged to an entry that never finished, and + * it shows up as the difference between eye_guest and the guest sub-stages. */ + sub_spans_forget(); + uint64_t guest_cycles = psx_cycle_count - cycles_before; + s_stats.guest_cycles_last = guest_cycles; if (eye >= 0) gte_render_view_get(s_stereo.eye_view[eye]); tg = gl_renderer_perf_ticks(); s_pass_cpu = NULL; @@ -827,6 +910,26 @@ static int render_transaction(struct CPUState *cpu, const PSXModRenderPass *pass s_stats.avg_end_ms = ema_ms(s_stats.avg_end_ms, te - tg); s_stats.avg_restore_ms = ema_ms(s_stats.avg_restore_ms, tr - te); + if (trace_on) { + uint32_t base = eye == PSX_MOD_EYE_LEFT ? PSX_TRACE_EYE_BEGIN_L + : PSX_TRACE_EYE_BEGIN_R; + psx_frame_trace_add(base + 0, trace_ticks_to_us(tb - t0)); + psx_frame_trace_add(base + 1, trace_ticks_to_us(tg - tb)); + psx_frame_trace_add(base + 2, trace_ticks_to_us(te - tg)); + psx_frame_trace_add(base + 3, trace_ticks_to_us(tr - te)); + uint64_t p1 = 0, t1x = 0, diag1[6] = {0,0,0,0,0,0}; + gl_renderer_scene_prim_counts(&p1, &t1x); + gl_renderer_runtime_diag(diag1); + psx_frame_trace_draw(eye, (uint32_t)(p1 - trace_prims0), + (uint32_t)(t1x - trace_tex0)); + psx_frame_trace_eye_cycles(eye, (uint32_t)guest_cycles); + /* Upload pixels -> bytes assumes the 16-bit VRAM format uploads use. */ + psx_frame_trace_gpu((diag1[2] - trace_diag0[2]) * 2u, + (uint32_t)leaks); + s_sub_eye = -1; + sub_spans_forget(); + } + if (verify_on()) { hash_after = state_hash(cpu); s_stats.verify_checks++; @@ -920,10 +1023,31 @@ int psx_mod_render_stereo(CPUState *cpu, const PSXModStereoFrame *frame, if (status != PSX_MOD_RENDER_PASS_READY) { s_stereo.refused++; s_stereo.last_failure = "status"; return 0; } + /* Frame trace (opt-in). The runtime already opened this frame's record in + * psx_openxr_begin() when a headset is active; opening here covers the + * desktop stereo path, and the guest-sim gap is the host time since the + * previous traced frame. */ + int trace_on = psx_frame_trace_enabled(); + int trace_opened = trace_on ? psx_frame_trace_open() : 0; + if (trace_on) { + static uint64_t prev_ms; + static int prev_valid; + uint64_t now_ms = psx_host_mono_ms(); + if (prev_valid) + psx_frame_trace_add(PSX_TRACE_GUEST_SIM, (now_ms - prev_ms) * 1000u); + prev_ms = now_ms; prev_valid = 1; + psx_frame_trace_attempt(s_stereo.attempts, 0, 0); + } budget_ms = frame->period_vblanks * (double)g_psx_vblank_cycles / 33868.8 * 0.8; if (s_stereo.avg_pair_ms > budget_ms && ++s_pair_retry < 30u) { - s_stereo.shed++; return 0; + s_stereo.shed++; + if (trace_on) { + psx_frame_trace_attempt(s_stereo.attempts, s_pair_retry, 0); + psx_frame_trace_flag(PSX_FRAME_TRACE_STALE_PAIR); + if (trace_opened) psx_frame_trace_commit(); + } + return 0; } s_pair_retry = 0; if (g_overlay_flush_pending_cycles) g_overlay_flush_pending_cycles(); @@ -978,6 +1102,13 @@ int psx_mod_render_stereo(CPUState *cpu, const PSXModStereoFrame *frame, s_stereo.retained_pair_id = s_stereo.last_pair_id; gl_renderer_stereo_stage_reset(); } + if (trace_on) { + psx_frame_trace_add(PSX_TRACE_PAIR_TOTAL, (uint64_t)(elapsed * 1000.0)); + psx_frame_trace_result(ok, ok ? s_stereo.last_pair_id : 0, + ok ? -1 : s_stereo.last_eye, + ok ? NULL : s_stereo.last_failure); + if (trace_opened) psx_frame_trace_commit(); + } s_pair_busy = 0; return ok; } @@ -989,11 +1120,21 @@ int psx_mod_render_capture_rgba(CPUState *cpu,const PSXModRenderPass *rect, !rect->w || !rect->h || rect->x+rect->w>1024 || rect->y+rect->h>512 || (uint64_t)rect->w*rect->h*4>bytes || transaction_status(1)!=PSX_MOD_RENDER_PASS_READY)return 0; if(g_overlay_flush_pending_cycles)g_overlay_flush_pending_cycles(); + uint64_t trace_t0=gl_renderer_perf_ticks(); s_pair_busy=1;s_plan_serial++; gl_renderer_stereo_stage_reset(); PSXModRenderPass capture=*rect;capture.alpha_q16=0; + s_capture_only=1; /* this eye-0 transaction is a HUD capture, not an eye draw */ int ok=render_transaction(cpu,&capture,fn,user,0); + s_capture_only=0; + uint64_t trace_t1=gl_renderer_perf_ticks(); + psx_frame_trace_add(PSX_TRACE_HUD_DRAW, + trace_ticks_to_us(trace_t1-trace_t0)); if(ok)ok=gl_renderer_stereo_stage_readback(0,rect->w,rect->h,rgba); + psx_frame_trace_add(PSX_TRACE_HUD_READBACK, + trace_ticks_to_us(gl_renderer_perf_ticks()-trace_t1)); gl_renderer_stereo_stage_reset();s_pair_busy=0; + psx_frame_trace_add(PSX_TRACE_HUD_CAPTURE, + trace_ticks_to_us(gl_renderer_perf_ticks()-trace_t0)); return ok; } diff --git a/runtime/tests/test_frame_trace.c b/runtime/tests/test_frame_trace.c new file mode 100644 index 00000000..3f887fe0 --- /dev/null +++ b/runtime/tests/test_frame_trace.c @@ -0,0 +1,340 @@ +/* Unit tests for the opt-in frame-trace ring (frame_trace.c). + * + * The trace is the floor the VR stutter investigation stands on, so its + * contracts must hold without a headset: a disabled trace costs nothing and + * records nothing, timestamps never go backwards even when the host clock does, + * the ring wraps without losing the newest records, a frame that measured + * nothing is marked as such, and every export is NUL-terminated and safe to + * truncate. ctest -R frame_trace_test + */ +#include "frame_trace.h" + +#include +#include + +/* Evaluate side effects and failures even when NDEBUG is defined. */ +#define CHECK(expr) do { \ + if (!(expr)) { \ + fprintf(stderr, "FAIL line %d: %s\n", __LINE__, #expr); \ + return 1; \ + } \ +} while (0) + +static uint64_t g_fake_ms; +static uint64_t fake_clock(void) { return g_fake_ms; } + +/* A drifting clock: monotonic except one deliberate backwards step. */ +static uint64_t g_step; +static uint64_t stepping_clock(void) { + static const uint64_t seq[] = { 100, 120, 90, 140, 200 }; + return seq[(g_step++) % (sizeof seq / sizeof seq[0])]; +} + +static int frame_with(uint64_t base_us, int publish, uint64_t pair_id, + int eye_failed, const char *failure) { + uint64_t us = base_us; + if (!psx_frame_trace_open()) return 0; + psx_frame_trace_ids(0x1000 + pair_id, 0x2000 + pair_id); + psx_frame_trace_add(PSX_TRACE_EYE_BEGIN_L, us); + psx_frame_trace_add(PSX_TRACE_EYE_GUEST_L, us * 2); + psx_frame_trace_add(PSX_TRACE_EYE_END_L, us + 3); + psx_frame_trace_add(PSX_TRACE_EYE_RESTORE_L, 5); + psx_frame_trace_add(PSX_TRACE_EYE_BEGIN_R, us + 1); + psx_frame_trace_add(PSX_TRACE_EYE_GUEST_R, us * 2 + 1); + psx_frame_trace_add(PSX_TRACE_XR_WAIT_FRAME, 700); + psx_frame_trace_add(PSX_TRACE_XR_END_FRAME, 40); + psx_frame_trace_result(publish, pair_id, eye_failed, failure); + psx_frame_trace_commit(); + return 1; +} + +int main(void) { + /* ---- disabled by default: nothing recorded, nothing to export ---- */ + psx_frame_trace_set_clock(fake_clock); + psx_frame_trace_reset(); + psx_frame_trace_set_enabled(0); + CHECK(psx_frame_trace_enabled() == 0); + CHECK(psx_frame_trace_open() == 0); + psx_frame_trace_add(PSX_TRACE_PAIR_TOTAL, 999); + psx_frame_trace_commit(); + CHECK(psx_frame_trace_committed() == 0); + { + char buf[256]; + int n = psx_frame_trace_to_json(buf, sizeof buf, 0); + CHECK(n > 0 && n < (int)sizeof buf); + CHECK(strstr(buf, "\"enabled\":0") != NULL); + CHECK(strstr(buf, "\"records\":[]") != NULL); + /* Session metadata is a no-op while capture is off. */ + psx_frame_trace_session("ShouldNotAppear", 12345); + CHECK(psx_frame_trace_to_json(buf, sizeof buf, 0) > 0); + CHECK(strstr(buf, "\"runtime\":\"\"") != NULL); + CHECK(strstr(buf, "\"target_period_us\":0") != NULL); + } + + /* ---- enabled: stages land, result/pair age, monotonic seq ---- */ + psx_frame_trace_set_enabled(1); + CHECK(psx_frame_trace_capacity() >= 64); + g_fake_ms = 1000; + CHECK(frame_with(10, 1, 7, -1, NULL)); + g_fake_ms = 1033; + CHECK(frame_with(100, 0, 7, 1, "eye_capture")); /* stale: publish failed */ + g_fake_ms = 1066; + CHECK(frame_with(12, 1, 8, -1, NULL)); + CHECK(psx_frame_trace_committed() == 3); + + { + PSXFrameTraceRecord recs[8]; + uint32_t n = psx_frame_trace_read(recs, 8); + CHECK(n == 3); + CHECK(recs[0].seq == 1 && recs[2].seq == 3); + CHECK(recs[0].open_ms <= recs[1].open_ms && + recs[1].open_ms <= recs[2].open_ms); + /* stage accumulation is exact */ + CHECK(recs[0].stages_us[PSX_TRACE_EYE_GUEST_L] == 20); + CHECK(recs[0].stages_us[PSX_TRACE_EYE_BEGIN_R] == 11); + CHECK(recs[0].stages_us[PSX_TRACE_XR_WAIT_FRAME] == 700); + /* published then stale */ + CHECK((recs[0].flags & PSX_FRAME_TRACE_STEREO_PUBLISHED) != 0); + CHECK(recs[0].pair_id == 7 && recs[0].pair_age_ms == 0); + CHECK((recs[1].flags & PSX_FRAME_TRACE_STALE_PAIR) != 0); + CHECK((recs[1].flags & PSX_FRAME_TRACE_FAILED) != 0); + CHECK(recs[1].eye_failed == 1); + CHECK(strcmp(recs[1].failure, "eye_capture") == 0); + CHECK(recs[1].pair_age_ms == 33); + CHECK((recs[2].flags & PSX_FRAME_TRACE_STEREO_PUBLISHED) != 0); + CHECK(recs[2].pair_id == 8 && recs[2].pair_age_ms == 0); + } + + /* ---- missing telemetry: an open + commit with no stage is marked ---- */ + g_fake_ms = 1100; + CHECK(psx_frame_trace_open() == 1); + CHECK(psx_frame_trace_open() == 0); /* no double open */ + psx_frame_trace_commit(); + CHECK(psx_frame_trace_missing() == 1); + { + PSXFrameTraceRecord recs[8]; + uint32_t n = psx_frame_trace_read(recs, 8); + CHECK(n == 4); + CHECK((recs[3].flags & PSX_FRAME_TRACE_NO_TELEMETRY) != 0); + } + + /* ---- shed annotation and flag rendering ---- */ + g_fake_ms = 1133; + CHECK(psx_frame_trace_open() == 1); + psx_frame_trace_attempt(42, 3, 250); + psx_frame_trace_flag(PSX_FRAME_TRACE_STALE_PAIR); + psx_frame_trace_commit(); + { + PSXFrameTraceRecord recs[8]; + char names[96]; + uint32_t n = psx_frame_trace_read(recs, 8); + CHECK(n == 5); + CHECK(recs[4].stereo_attempt == 42); + CHECK(recs[4].shed_skipped == 3 && recs[4].shed_streak_ms == 250); + CHECK((recs[4].flags & PSX_FRAME_TRACE_SHED) != 0); + /* A shed-only frame is stale, not "missing telemetry". */ + CHECK((recs[4].flags & PSX_FRAME_TRACE_NO_TELEMETRY) == 0); + CHECK(psx_frame_trace_missing() == 1); + psx_frame_trace_flag_names(recs[4].flags, names, sizeof names); + CHECK(strstr(names, "stale") && strstr(names, "shed")); + } + + /* ---- stage saturation rather than wrap ---- */ + g_fake_ms = 1200; + CHECK(psx_frame_trace_open() == 1); + psx_frame_trace_add(PSX_TRACE_PAIR_TOTAL, 0xF0000000u); + psx_frame_trace_add(PSX_TRACE_PAIR_TOTAL, 0xF0000000u); + psx_frame_trace_commit(); + { + PSXFrameTraceRecord recs[8]; + uint32_t n = psx_frame_trace_read(recs, 8); + CHECK(recs[n - 1].stages_us[PSX_TRACE_PAIR_TOTAL] == 0xFFFFFFFFu); + } + + /* ---- a backwards clock step must not produce a backwards timestamp ---- */ + psx_frame_trace_reset(); + { + PSXFrameTraceRecord recs[8]; + psx_frame_trace_set_clock(stepping_clock); + for (int i = 0; i < 5; ++i) { + CHECK(psx_frame_trace_open() == 1); + psx_frame_trace_add(PSX_TRACE_GUEST_SIM, (uint32_t)(1 + i)); + psx_frame_trace_commit(); + } + uint32_t n = psx_frame_trace_read(recs, 8); + CHECK(n == 5); + for (uint32_t i = 1; i < n; ++i) + CHECK(recs[i].open_ms >= recs[i - 1].open_ms); + CHECK(recs[0].open_ms == 100); + CHECK(recs[2].open_ms >= recs[1].open_ms); /* the 90 was clamped up */ + } + + /* ---- ring wrap keeps the newest records ---- */ + psx_frame_trace_reset(); + psx_frame_trace_set_clock(fake_clock); + { + uint32_t cap = psx_frame_trace_capacity(); + uint32_t extra = cap + 10u; + for (uint32_t i = 0; i < extra; ++i) { + g_fake_ms = 2000u + i; + CHECK(psx_frame_trace_open() == 1); + psx_frame_trace_add(PSX_TRACE_GUEST_SIM, i + 1u); + psx_frame_trace_commit(); + } + CHECK(psx_frame_trace_committed() == extra); + CHECK(psx_frame_trace_dropped() == extra - cap); + PSXFrameTraceRecord recs[8]; + CHECK(psx_frame_trace_read(recs, 8) == 8); + CHECK(recs[7].seq == extra); /* newest present */ + CHECK(recs[0].seq == extra - 7u); + CHECK(recs[7].stages_us[PSX_TRACE_GUEST_SIM] == extra); + /* capacity-limited read returns exactly `capacity` of the newest */ + PSXFrameTraceRecord one[1]; + CHECK(psx_frame_trace_read(one, 1) == 1); + CHECK(one[0].seq == extra); + } + + /* ---- exports: valid, NULL-terminated, truncation-safe ---- */ + { + static char big[1 << 20]; + int n = psx_frame_trace_to_json(big, sizeof big, 16); + CHECK(n > 0 && n < (int)sizeof big); + CHECK(big[0] == '{' && strstr(big, "\"records\":[") != NULL); + CHECK(big[n - 1] == '}' && big[n - 2] == ']'); + CHECK(strstr(big, "\"stages_us\"") != NULL); + /* The record limit caps the emitted count. */ + CHECK(strstr(big, "\"seq\":") != NULL); + + n = psx_frame_trace_to_csv(big, sizeof big, 4); + CHECK(n > 0 && n < (int)sizeof big); + CHECK(strncmp(big, "seq,open_ms,", 12) == 0); + CHECK(strstr(big, "guest_sim") && strstr(big, "xr_end_frame")); + + n = psx_frame_trace_to_chrome_json(big, sizeof big, 4); + CHECK(n > 0 && n < (int)sizeof big); + CHECK(strncmp(big, "{\"traceEvents\":[", 16) == 0 && big[n - 1] == '}'); + CHECK(strstr(big, "\"ph\":\"X\"") != NULL); + + /* A tiny buffer must still be terminated and report the full length. */ + char small[8]; + int need = psx_frame_trace_to_json(small, sizeof small, 0); + CHECK(need > (int)sizeof small); + CHECK(small[sizeof small - 1] == '\0'); + CHECK(small[0] == '{'); + } + + /* ---- session metadata: runtime + target refresh, JSON-escaped ---- */ + psx_frame_trace_session("TestRuntime/1.0", 11111); /* 90 Hz headset */ + { + static char meta[1 << 16]; + int m = psx_frame_trace_to_json(meta, sizeof meta, 1); + CHECK(m > 0 && m < (int)sizeof meta); + CHECK(strstr(meta, "\"runtime\":\"TestRuntime/1.0\"") != NULL); + CHECK(strstr(meta, "\"target_period_us\":11111") != NULL); + /* A runtime name with a quote must not break the document. */ + psx_frame_trace_session("Odd\"Runtime", 0); + m = psx_frame_trace_to_json(meta, sizeof meta, 1); + CHECK(m > 0 && m < (int)sizeof meta); + CHECK(strstr(meta, "\"runtime\":\"Odd\\\"Runtime\"") != NULL); + CHECK(strstr(meta, "\"target_period_us\":0") != NULL); + } + + /* ---- XR cadence: the runtime's display-time delta and repeat marking ---- + * One presented pair per granted display period is the pacing target, so a + * frame that carried no new pair must be distinguishable from one that did + * without reading the counterpart stats. */ + psx_frame_trace_reset(); + psx_frame_trace_set_enabled(1); + { + PSXFrameTraceRecord recs[8]; + static char cad[1 << 16]; + char names[96]; + + CHECK(psx_frame_trace_open()); /* carried a new pair */ + psx_frame_trace_add(PSX_TRACE_POSE_AGE, 4200); + psx_frame_trace_result(1, 11, -1, NULL); + psx_frame_trace_xr_cadence(22222, 1); + psx_frame_trace_commit(); + + CHECK(psx_frame_trace_open()); /* carried no new pair */ + psx_frame_trace_result(0, 11, -1, NULL); + psx_frame_trace_xr_cadence(22222, 0); + psx_frame_trace_commit(); + + CHECK(psx_frame_trace_read(recs, 8) == 2); + CHECK(recs[0].xr_display_delta_us == 22222); + CHECK(recs[0].xr_submit_fresh == 1); + CHECK((recs[0].flags & PSX_FRAME_TRACE_XR_REPEAT) == 0); + CHECK(recs[0].stages_us[PSX_TRACE_POSE_AGE] == 4200); + CHECK(recs[1].xr_submit_fresh == 0); + CHECK((recs[1].flags & PSX_FRAME_TRACE_XR_REPEAT) != 0); + /* The pose was never sampled for the repeat, so no age is claimed. */ + CHECK(recs[1].stages_us[PSX_TRACE_POSE_AGE] == 0); + + CHECK(psx_frame_trace_to_json(cad, sizeof cad, 0) > 0); + CHECK(strstr(cad, "\"xr_display_delta_us\":22222") != NULL); + CHECK(strstr(cad, "\"xr_submit_fresh\":1") != NULL); + CHECK(strstr(cad, "\"pose_age\":4200") != NULL); + CHECK(psx_frame_trace_to_csv(cad, sizeof cad, 0) > 0); + CHECK(strstr(cad, "xr_display_delta_us,xr_submit_fresh") != NULL); + CHECK(strstr(cad, ",22222,1,") != NULL); + CHECK(strstr(cad, ",22222,0,") != NULL); + CHECK(strstr(psx_frame_trace_flag_names(recs[1].flags, names, sizeof names), + "xr_repeat") != NULL); + } + + /* ---- per-eye draw counters and guest cycles --------------------------- + * The pair's own prim count is the last eye's, which is useless for asking + * which eye a change moved, and the guest sub-stages of the replay are pair + * totals written by the game hook. */ + psx_frame_trace_reset(); + psx_frame_trace_set_enabled(1); + { + PSXFrameTraceRecord recs[4]; + static char buf[1 << 16]; + + CHECK(psx_frame_trace_open()); + /* An unknown eye or a closed record must not write anything. */ + psx_frame_trace_draw(2, 999, 999); + psx_frame_trace_eye_cycles(-1, 999); + psx_frame_trace_add(PSX_TRACE_GAME_TRAVERSE, 0); + psx_frame_trace_draw(0, 669, 667); + psx_frame_trace_draw(1, 947, 944); + psx_frame_trace_eye_cycles(0, 145000); + psx_frame_trace_eye_cycles(1, 151000); + psx_frame_trace_add(PSX_TRACE_GAME_TRAVERSE, 1200); + psx_frame_trace_add(PSX_TRACE_GAME_TRAVERSE, 1400); + psx_frame_trace_add(PSX_TRACE_GAME_SUBMIT, 800); + psx_frame_trace_commit(); + + CHECK(psx_frame_trace_read(recs, 4) == 1); + CHECK(recs[0].prims_l == 669 && recs[0].prims_r == 947); + CHECK(recs[0].tex_prims_l == 667 && recs[0].tex_prims_r == 944); + /* `prims`/`tex_prims` keep their meaning: the last eye of the pair. */ + CHECK(recs[0].prims == 947 && recs[0].textured_prims == 944); + CHECK(recs[0].eye_cycles_l == 145000 && recs[0].eye_cycles_r == 151000); + /* A guest sub-stage accumulates both eyes' spans of that call. */ + CHECK(recs[0].stages_us[PSX_TRACE_GAME_TRAVERSE] == 2600); + CHECK(recs[0].stages_us[PSX_TRACE_GAME_SUBMIT] == 800); + + CHECK(psx_frame_trace_to_json(buf, sizeof buf, 0) > 0); + CHECK(strstr(buf, "\"prims_l\":669") && strstr(buf, "\"prims_r\":947")); + CHECK(strstr(buf, "\"cycles_l\":145000") && + strstr(buf, "\"cycles_r\":151000")); + CHECK(strstr(buf, "\"game_traverse\":2600") && + strstr(buf, "\"game_submit\":800") && + strstr(buf, "\"game_laser\":0")); + CHECK(psx_frame_trace_to_csv(buf, sizeof buf, 0) > 0); + CHECK(strstr(buf, "prims_l,prims_r,tex_prims_l,tex_prims_r,cycles_l," + "cycles_r") != NULL); + CHECK(strstr(buf, ",669,947,667,944,145000,151000,") != NULL); + /* The name table must stay in enum order (the header order is the ABI). */ + CHECK(strcmp(psx_frame_trace_stage_name(PSX_TRACE_GAME_ENV), "game_env") == 0 + && strcmp(psx_frame_trace_stage_name(PSX_TRACE_GAME_LASER), + "game_laser") == 0); + } + + psx_frame_trace_set_enabled(0); + printf("frame_trace_test: all checks passed\n"); + return 0; +} \ No newline at end of file diff --git a/runtime/tests/test_frame_trace_guard.py b/runtime/tests/test_frame_trace_guard.py new file mode 100644 index 00000000..a8b06f77 --- /dev/null +++ b/runtime/tests/test_frame_trace_guard.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +"""Guard: the frame-trace RECORD path must not allocate or touch files. + +frame_trace.c exists so a visible VR hitch can be correlated with a stage +without changing the thing being measured. A malloc(), a stdio file write, or a +formatted print added to the per-frame path would perturb the frame it is +supposed to observe, and no functional test would notice. This check reads the +source and fails if such a call appears in a record-path function. + +Export functions (to_json/to_csv/to_chrome_json) are explicitly allowed to use +vsnprintf, and are never called from the record path; they are listed as +exceptions by name so the intent is not mistaken for an oversight. + +ctest -R frame_trace_guard_test +""" +from __future__ import annotations + +import re +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +SRC = ROOT / "runtime" / "src" / "frame_trace.c" + +# Functions that run per frame. Their bodies must stay allocation- and IO-free. +RECORD_PATH = [ + "psx_frame_trace_open", + "psx_frame_trace_commit", + "psx_frame_trace_add", + "psx_frame_trace_ids", + "psx_frame_trace_attempt", + "psx_frame_trace_result", + "psx_frame_trace_source", + "psx_frame_trace_draw", + "psx_frame_trace_eye_cycles", + "psx_frame_trace_gpu", + "psx_frame_trace_flag", + "psx_frame_trace_note", + "psx_frame_trace_io", + "psx_frame_trace_reset", + "psx_frame_trace_enabled", + "stamp_now", + "open_record", +] + +# Forbidden call patterns in a record-path body. Word boundaries matter: +# "printf" must not match "snprintf"/"vsnprintf", which write into the record's +# own fixed buffers and touch neither heap nor files. +FORBIDDEN = [ + r"(? str | None: + """The definition of `name` (top-level, so the signature starts at column 0).""" + lines = text.splitlines() + for i, line in enumerate(lines): + if re.match(r"^[A-Za-z_][^\n]*\b" + re.escape(name) + r"\s*\(", line): + body = [line] + for j in range(i + 1, len(lines)): + body.append(lines[j]) + if lines[j].rstrip() == "}": + break + return "\n".join(body) + return None + + +def main() -> int: + if not SRC.exists(): + print(f"frame_trace_guard_test: SKIP: {SRC} not found", file=sys.stderr) + return 77 + text = SRC.read_text(encoding="utf-8", errors="replace") + + failures: list[str] = [] + + if not re.search(r"static\s+PSXFrameTraceRecord\s+s_ring\s*\[", text): + failures.append("frame_trace.c: the ring is not a static array " + "(the record path must not own heap)") + + for name in RECORD_PATH: + body = function_body(text, name) + if body is None: + failures.append(f"frame_trace.c: record-path function {name}() not found") + continue + for pattern in FORBIDDEN: + if re.search(pattern, body): + failures.append(f"frame_trace.c: {name}() matches {pattern} " + "(the per-frame path must not allocate or do IO)") + + if failures: + print("frame_trace_guard_test: FAIL", file=sys.stderr) + for f in failures: + print(" " + f, file=sys.stderr) + return 1 + + print(f"frame_trace_guard_test: {len(RECORD_PATH)} record-path functions are " + "allocation- and IO-free") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/runtime/tests/test_render_pass_abort.c b/runtime/tests/test_render_pass_abort.c index 2adba0e5..d623e8c3 100644 --- a/runtime/tests/test_render_pass_abort.c +++ b/runtime/tests/test_render_pass_abort.c @@ -232,6 +232,13 @@ int gl_renderer_stereo_publish(uint64_t id, uint64_t cycle, const int32_t view[2 s_stereo_published = id; return 1; } +/* Referenced by render_pass.c's frame tracing and stereo shedding. */ +void gl_renderer_runtime_diag(uint64_t out[6]) { memset(out, 0, 6 * sizeof *out); } +void gl_renderer_scene_prim_counts(uint64_t *prims, uint64_t *textured) { + if (prims) *prims = 0; + if (textured) *textured = 0; +} +uint64_t psx_host_mono_ms(void) { return 1; } /* ---- an overlay shard's cycle shim (overlay_dispatch_preamble.c.inc) --- */ static uint32_t s_shard_pending; diff --git a/runtime/tests/test_render_pass_sandbox.c b/runtime/tests/test_render_pass_sandbox.c index 37732ce8..caedab1c 100644 --- a/runtime/tests/test_render_pass_sandbox.c +++ b/runtime/tests/test_render_pass_sandbox.c @@ -23,6 +23,7 @@ * The GPU, presenter and remaining devices are mocks. Build/run: * ctest -R render_pass_sandbox_test */ +#include "frame_trace.h" #include "render_pass.h" #include "render_pass_plan.h" @@ -222,6 +223,9 @@ void gpu_pass_checkpoint_restore(void) {} static uint64_t s_ticks; uint64_t gl_renderer_perf_ticks(void) { return s_ticks += 1000; } uint64_t gl_renderer_perf_frequency(void) { return 1000000000u; } +/* 1000 ticks per sample and a 1 GHz tick make one runtime sample exactly 1 us, + * so a bracketed guest span is the caller's own bump plus a known microsecond. */ +static void tick_bump(uint64_t ticks) { s_ticks += ticks; } static uint32_t s_gl_status = PSX_MOD_RENDER_PASS_READY; uint32_t gl_renderer_pass_unavailable(void) { return s_gl_status; } uint32_t gl_renderer_pass_plan(uint32_t p, uint32_t s, uint32_t *a, @@ -301,6 +305,18 @@ int gl_renderer_stereo_publish(uint64_t id, uint64_t cycle, const int32_t view[2 return 1; } +/* Frame-trace draw/diag inputs and the host clock the stereo shedding and + * tracing use. Not GL state this sandbox models, but render_pass.c references + * them, so they must exist to link. */ +void gl_renderer_runtime_diag(uint64_t out[6]) { memset(out, 0, 6 * sizeof *out); } +static uint64_t s_prims_stub; +void gl_renderer_scene_prim_counts(uint64_t *prims, uint64_t *textured) { + if (prims) *prims = s_prims_stub; + if (textured) *textured = s_prims_stub / 2u; +} +static uint64_t s_host_ms; +uint64_t psx_host_mono_ms(void) { return s_host_ms; } + /* ---- 1. store policy --------------------------------------------------- */ static void test_store_policy(void) { RenderPassStoreTarget t = target(); @@ -763,6 +779,134 @@ static void test_stereo(void) { "capture failure still restores live state"); f.period_vblanks = 0; CHECK(!psx_mod_render_stereo(&cpu, &f, pair_draw, NULL), "invalid cadence refused"); + + /* Tracing is observation only: the same pair must publish the same id and + * reach the same guest-state hashes with capture on as with it off. */ + { + uint64_t off_pub, off_hash[2], off_pair; + f.period_vblanks = 2; + psx_frame_trace_set_enabled(0); + render_pass_reset_session(); psx_frame_trace_set_enabled(0); + s_gl_status = 0; s_begin_ok = 1; s_eye_calls = 0; + cpu.gpr[8] = 123; s_ram[0x456] = 0x77; + CHECK(psx_mod_render_stereo(&cpu, &f, pair_draw, NULL) == 1, "pair with tracing off"); + render_stereo_get_stats(&stats); + off_pub = s_stereo_published; + off_hash[0] = stats.eye_hash[0]; off_hash[1] = stats.eye_hash[1]; + off_pair = stats.last_pair_id; + + render_pass_reset_session(); + psx_frame_trace_set_enabled(1); psx_frame_trace_reset(); + s_gl_status = 0; s_begin_ok = 1; s_eye_calls = 0; + cpu.gpr[8] = 123; s_ram[0x456] = 0x77; + CHECK(psx_mod_render_stereo(&cpu, &f, pair_draw, NULL) == 1, "pair with tracing on"); + render_stereo_get_stats(&stats); + CHECK(s_stereo_published == off_pub && stats.last_pair_id == off_pair && + stats.eye_hash[0] == off_hash[0] && stats.eye_hash[1] == off_hash[1], + "tracing changes neither the published pair nor the guest state"); + CHECK(psx_frame_trace_committed() > 0, "the traced pair was recorded"); + psx_frame_trace_set_enabled(0); psx_frame_trace_reset(); + } +} + +/* ---- 4. per-eye trace counters and guest sub-stage spans ---------------- */ +/* The shape of the game hook's per-eye replay: bracket the guest calls, and let + * the runtime time them. See vr/psx_vr_stereo.c vr_draw_scene. */ +static int s_traced_calls; +static int traced_draw(CPUState *cpu, void *user, uint32_t eye) { + (void)user; + s_traced_calls++; + s_prims_stub += 100u + 100u * eye; /* 100 prims, then 200 */ + psx_mod_frame_trace_stage_begin(PSX_TRACE_GAME_TRAVERSE); + tick_bump(3000); /* 3 us, plus 1 per sample */ + /* The visibility override runs inside the traversal it belongs to, so its + * span nests in that one: it must be timed on its own without truncating + * the span it is inside. */ + psx_mod_frame_trace_stage_begin(PSX_TRACE_GAME_SELECT); + tick_bump(2000); + psx_mod_frame_trace_stage_end(PSX_TRACE_GAME_SELECT); + tick_bump(1000); + psx_mod_frame_trace_stage_end(PSX_TRACE_GAME_TRAVERSE); + /* An end for a stage that was never opened, and a begin that is never + * ended: neither may reach the record. */ + psx_mod_frame_trace_stage_begin(PSX_TRACE_GAME_ENV); + tick_bump(1000); + psx_mod_frame_trace_stage_end(PSX_TRACE_GAME_SUBMIT); + psx_mod_frame_trace_stage_begin(PSX_TRACE_GAME_ICON); + tick_bump(1000); + psx_advance_cycles(4000u + 1000u * eye); + return 1; +} + +static void test_trace_substages(void) { + CPUState cpu = {0}; + PSXModStereoFrame f = {sizeof f, 2, 0, 240, 320, 240}; + PSXFrameTraceRecord rec; + PSXModRenderPass rect = {sizeof rect, 0, 0, 0, 2, 2}; + int first = 1, second = 2; + uint8_t pixels[32] = {0}; + + /* A session reset re-reads PSX_FRAME_TRACE from the env, so enabling the + * trace has to come after it (as it does in a real session). */ + render_pass_reset_session(); + s_gl_status = 0; s_begin_ok = 1; s_eye_decline = -1; s_eye_calls = 0; + s_prims_stub = 0; s_traced_calls = 0; + psx_frame_trace_set_enabled(1); + psx_frame_trace_reset(); + + CHECK(psx_mod_render_stereo(&cpu, &f, traced_draw, NULL) == 1, + "the traced pair rendered"); + CHECK(s_traced_calls == 2, "both eyes replayed the guest draw"); + CHECK(psx_frame_trace_read(&rec, 1) == 1, "the pair left one record"); + CHECK(rec.prims_l == 100 && rec.prims_r == 200, + "each eye's own primitive delta is recorded"); + CHECK(rec.prims == 200 && rec.textured_prims == 100, + "the pair-level count is still the last eye's"); + CHECK(rec.eye_cycles_l >= 4000u && rec.eye_cycles_r >= 5000u && + rec.eye_cycles_r > rec.eye_cycles_l, + "each eye's own guest cycles are recorded"); + CHECK(rec.stages_us[PSX_TRACE_GAME_TRAVERSE] == 18, + "both eyes' guest spans are recorded (9 us per eye, nesting included)"); + CHECK(rec.stages_us[PSX_TRACE_GAME_SELECT] == 6, + "a nested span is timed on its own (3 us per eye) and stays a subset"); + CHECK(rec.stages_us[PSX_TRACE_GAME_ENV] == 0 && + rec.stages_us[PSX_TRACE_GAME_SUBMIT] == 0, + "an end that does not match an open span records nothing"); + CHECK(rec.stages_us[PSX_TRACE_GAME_ICON] == 0, + "an unpaired begin records nothing rather than leak into the next span"); + + /* A HUD capture's guest draws run outside a traced eye: the hook's spans + * must not land in the frame they belong to. */ + psx_frame_trace_reset(); + s_prims_stub = 0; + s_traced_calls = 0; + render_pass_reset_session(); s_gl_status = 0; s_begin_ok = 1; + psx_frame_trace_set_enabled(1); + CHECK(psx_frame_trace_open(), "a frame record is open across the capture"); + CHECK(psx_mod_render_capture_rgba(&cpu, &rect, traced_draw, &first, + pixels, sizeof pixels) && + psx_mod_render_capture_rgba(&cpu, &rect, traced_draw, &second, + pixels, sizeof pixels), + "the captures rendered both mattes"); + CHECK(s_traced_calls == 2 && psx_frame_trace_read(&rec, 1) == 0, + "the capture replayed the draw and left no record of its own"); + psx_frame_trace_commit(); + CHECK(psx_frame_trace_read(&rec, 1) == 1 && + rec.stages_us[PSX_TRACE_GAME_TRAVERSE] == 0 && + rec.prims_l == 0 && rec.prims_r == 0, + "a capture's guest draws write no per-eye or sub-stage slots"); + + /* Same with the trace off: the hook may bracket freely. */ + psx_frame_trace_set_enabled(0); + psx_frame_trace_reset(); + s_prims_stub = 0; + s_traced_calls = 0; + render_pass_reset_session(); s_gl_status = 0; s_begin_ok = 1; + CHECK(psx_mod_render_stereo(&cpu, &f, traced_draw, NULL) == 1, + "the pair renders with tracing off"); + CHECK(s_traced_calls == 2 && psx_frame_trace_committed() == 0, + "nothing is recorded while tracing is off"); + render_pass_reset_session(); } static int local_draw(CPUState *cpu, void *user, uint32_t alpha) { @@ -879,6 +1023,7 @@ int main(void) { test_journal(); test_stereo(); test_isolated_capture(); + test_trace_substages(); test_local_view(); test_texture_stream_journal(); CHECK(s_prec_open == 0, "precision checkpoints balanced"); From 0cdf95957c3e34916aef5ff5b0b4e2b77fdc8790 Mon Sep 17 00:00:00 2001 From: Yves Date: Fri, 23 Oct 2026 03:24:01 +0800 Subject: [PATCH 14/29] Bound stereo shedding and recover at the display deadline Co-authored-by: Yves --- docs/STEREO_RENDERING.md | 21 +- docs/internal/upstream/vr-bounded-shedding.md | 17 ++ runtime/CMakeLists.txt | 8 + runtime/include/mod_plugins.h | 10 + runtime/include/render_pass.h | 11 + runtime/include/stereo_shed_policy.h | 95 ++++++++ runtime/runtime.cmake | 1 + runtime/src/debug_server.c | 8 + runtime/src/psx_openxr.c | 10 + runtime/src/render_pass.c | 128 +++++++++-- runtime/src/stereo_shed_policy.c | 145 ++++++++++++ runtime/tests/test_render_pass_sandbox.c | 17 ++ runtime/tests/test_stereo_shed_policy.c | 210 ++++++++++++++++++ 13 files changed, 660 insertions(+), 21 deletions(-) create mode 100644 docs/internal/upstream/vr-bounded-shedding.md create mode 100644 runtime/include/stereo_shed_policy.h create mode 100644 runtime/src/stereo_shed_policy.c create mode 100644 runtime/tests/test_stereo_shed_policy.c diff --git a/docs/STEREO_RENDERING.md b/docs/STEREO_RENDERING.md index c8536b87..87ade4da 100644 --- a/docs/STEREO_RENDERING.md +++ b/docs/STEREO_RENDERING.md @@ -16,9 +16,18 @@ that must be cleared after watchdog rollback. `period_vblanks` describes the game's draw cadence (1..8). An EMA of complete-pair host cost is compared with 80% of that cadence, using the runtime's live NTSC/PAL -period. Expensive work is shed as a complete pair; every thirtieth request retries -to recover a stale estimate. This is a conservative cadence budget, not a measured -headset idle budget. Allocating or slow initial pairs may exceed it; completed +period, unless `PSX_STEREO_SHED_BUDGET_MS` supplies the real headset deadline (the +VBlank cadence is a guest-budget fallback, not an OpenXR display deadline). +Expensive work is shed as a complete pair, never as a single eye, and the previous +complete pair is retained. Shedding is bounded both by wall clock +(`PSX_STEREO_SHED_PROBE_MS`, default 250 ms) and by a consecutive count +(`PSX_STEREO_SHED_MAX_STREAK`, default 8), so a stale estimate cannot suppress +indefinitely: whichever bound trips first forces a probe, and the probe's +measurement replaces the estimate rather than blending into it, so a single spike +ends after one bounded streak. Recovery uses a hysteresis band +(`PSX_STEREO_SHED_HYSTERESIS`, default 0.9) and scene/mode transitions reset the +estimate. `PSX_STEREO_SHED=0` disables shedding entirely for a baseline +diagnostic. Allocating or slow initial pairs may exceed the budget; completed guest work cannot be preempted by a host-time budget. Guest-cycle watchdogs remain the runaway bound. @@ -59,8 +68,10 @@ sees it. `test_render_pose` covers the override together with `fov_scale`. TCP inspection: - `stereo_stats`: pair outcomes, eye entry cycles/state hashes under VERIFY, - sampled view offsets, latched failed attempt/eye/retained pair, and published - pair metadata. `render_pass_stats` continues to + sampled view offsets, latched failed attempt/eye/retained pair, published + pair metadata, and the shedding policy (`shedding`, `shed_streak`, + `shed_streak_ms`, `shed_streak_max`, `stale_frames`, `budget_resets`, + `last_pair_age_ms`). `render_pass_stats` continues to expose shared transaction checks, aborts, leaks and dropped stores; its eye transactions also increment pass counters. - `stereo_dump path= count=N`: arm dumps of the next N diff --git a/docs/internal/upstream/vr-bounded-shedding.md b/docs/internal/upstream/vr-bounded-shedding.md new file mode 100644 index 00000000..a52c58df --- /dev/null +++ b/docs/internal/upstream/vr-bounded-shedding.md @@ -0,0 +1,17 @@ +# Bound stereo shedding and recover at the display deadline + +Source: [FractalEngineer/psxrecomp](https://github.com/FractalEngineer/psxrecomp). +- [cae62891](https://github.com/FractalEngineer/psxrecomp/commit/cae62891e9236e9252e43b80aa9dd47f7554aea4) +- [b6a671c1](https://github.com/FractalEngineer/psxrecomp/commit/b6a671c1db4ba15d02369ce5561754d485881a55) + +Original author: Yves ; retained as commit author +and Co-authored-by on this reconstructed port. + +Adapted: this feature and its tests. Excluded: other VR features, game policy, +assets and chronological investigation notes. +Base: `8400b55dc02d3fa787df1280a9fb474901adfcf4` (current upstream master or the preceding feature). + +Validation: Windows/GCC 13.2 Release: policy, sandbox and abort tests pass (3/3). Sustained overload remains expensive; the MoH launcher disables shedding by default. + +Consumer: [Medal of Honor v0.1.6](https://github.com/FractalEngineer/Medal-Of-Honor-PSX-Recomp/tree/1957e28a5dc7f5daa79e2b9b6ed867eb06b41ace). +Requires a framework pin bump and rebuild; no game/BIOS regeneration. diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 833731f5..b0347d69 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -1634,6 +1634,12 @@ if(BUILD_TESTING) add_executable(frame_trace_test tests/test_frame_trace.c src/frame_trace.c) target_include_directories(frame_trace_test PRIVATE include) add_test(NAME frame_trace_test COMMAND frame_trace_test) + # Stereo shedding/recovery policy (docs/STEREO_RENDERING.md): bounded streaks, + # authoritative probes, hysteresis, pair age separate from the estimate. + add_executable(stereo_shed_policy_test tests/test_stereo_shed_policy.c + src/stereo_shed_policy.c) + target_include_directories(stereo_shed_policy_test PRIVATE include) + add_test(NAME stereo_shed_policy_test COMMAND stereo_shed_policy_test) # Render thread core ([video] render_thread, docs/RENDER_THREAD.md): # ordering across ring wraps, the frame bound, stale-frame detection and # the drain + context hand-off sync point, against a fake executor. @@ -1780,6 +1786,7 @@ if(BUILD_TESTING) tests/test_render_pass_abort.c src/render_pass.c src/frame_trace.c + src/stereo_shed_policy.c src/psx_cycles.c) target_include_directories(render_pass_abort_test PRIVATE include) add_test(NAME render_pass_abort_test COMMAND render_pass_abort_test) @@ -1792,6 +1799,7 @@ if(BUILD_TESTING) tests/test_render_pass_sandbox.c src/render_pass.c src/frame_trace.c + src/stereo_shed_policy.c src/render_pass_plan.c src/psx_cycles.c src/timers.c diff --git a/runtime/include/mod_plugins.h b/runtime/include/mod_plugins.h index bf458abc..07f87920 100644 --- a/runtime/include/mod_plugins.h +++ b/runtime/include/mod_plugins.h @@ -752,6 +752,16 @@ void psx_mod_frame_trace_note(const char *tag); * inside the guest traversal it is part of (game_select in game_traverse). */ void psx_mod_frame_trace_stage_begin(uint32_t stage); void psx_mod_frame_trace_stage_end(uint32_t stage); +/* Drop the stereo shedding policy's pair-time estimate and derived budget. Call + * on a scene/mode transition so a new scene does not inherit the previous one's + * timing; cheap and idempotent. Shedding itself is unchanged. */ +void psx_mod_stereo_reset_budget(void); +/* Set the stereo pair budget from a real headset deadline in milliseconds, + * pushed by the OpenXR layer from the target display period (11.1 ms at 90 Hz, + * 8.3 ms at 120 Hz). 0 clears it. The guest-VBlank-derived value is only a + * fallback: a PSX redraw budget is not an OpenXR display deadline, and the two + * disagree on every non-60 Hz headset. PSX_STEREO_SHED_BUDGET_MS still wins. */ +void psx_mod_stereo_set_budget_ms(double ms); /* 0 disables stereo output; 1 shows a complete pair side by side. Capture * itself does not enable presentation. Default 0; applies to this session. */ int psx_mod_set_stereo_presentation(uint32_t mode); diff --git a/runtime/include/render_pass.h b/runtime/include/render_pass.h index 13594b9e..a8be5f7f 100644 --- a/runtime/include/render_pass.h +++ b/runtime/include/render_pass.h @@ -91,6 +91,17 @@ typedef struct RenderStereoStats { uint64_t last_failed_attempt, retained_pair_id; int last_failed_eye; const char *last_failed_reason; /* latched across later successes */ + /* Shedding/recovery policy (stereo_shed_policy.c). A shed streak is bounded + * by time and count; these report how the last one ended and how stale the + * retained pair got. */ + uint64_t shed_streak, shed_streak_max; /* consecutive sheds now / worst seen */ + uint32_t shed_streak_ms; /* wall clock of the current streak */ + uint64_t stale_frames; /* frames that published no new pair */ + uint64_t budget_resets; /* estimates reset on scene/mode change */ + double budget_ms; /* pair budget actually in use */ + uint32_t budget_source; /* 0 vblank (fallback), 1 headset, 2 env */ + uint64_t last_pair_age_ms; /* age of the retained pair, this frame */ + int shedding; /* policy is currently suppressing */ } RenderStereoStats; void render_stereo_get_stats(RenderStereoStats *out); int render_pass_netplay_enabled(void); diff --git a/runtime/include/stereo_shed_policy.h b/runtime/include/stereo_shed_policy.h new file mode 100644 index 00000000..82c66d55 --- /dev/null +++ b/runtime/include/stereo_shed_policy.h @@ -0,0 +1,95 @@ +#ifndef PSX_STEREO_SHED_POLICY_H +#define PSX_STEREO_SHED_POLICY_H + +/* Whole-pair stereo shedding policy, extracted so it can be reasoned about and + * tested without a GPU, a headset or a guest. + * + * WHY THIS EXISTS + * --------------- + * The original rule was `avg_pair_ms > budget_ms && ++retry < 30` — an + * attempt counter with no notion of time. One short overload latched the + * average above budget and then silently suppressed the next 29 pair requests: + * a single heavy frame produced a long run of stale frames. There was also no + * way to say "stop shedding and just measure", and no bound on how long + * suppression could last. + * + * THIS POLICY + * ----------- + * * Enters shedding when the pair-time EMA is over budget, and leaves it only + * when a fresh measurement is clearly under `hysteresis * budget` (so noise + * at the boundary does not flap). + * * While shedding, suppression lasts at most `probe_ms` of wall clock and at + * most `max_streak` consecutive sheds, whichever comes first. Whichever way + * it ends, the next frame is rendered, which takes a fresh measurement. + * A stale average can therefore never suppress indefinitely: recovery is + * bounded in TIME, not in attempt count. + * * A forced probe is authoritative: it REPLACES the running estimate rather + * than blending into it. That is what makes a one-off spike cheap — one + * good probe ends the streak, instead of the EMA having to decay over + * several probes while the headset keeps showing stale pairs. + * * `enabled = 0` disables shedding entirely (baseline diagnostic: measure + * what happens with no suppression at all). + * + * The policy decides only whether to skip a pair. It never decides which eyes + * to publish; the caller keeps whole-pair publish (a stale frame retains the + * previous complete pair, never an unmatched eye). + * + * All time is passed in by the caller (host monotonic ms), so there is no clock + * dependency here and the interesting cases are unit-testable. + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct StereoShedPolicy { + /* configuration */ + int enabled; /* 0 = never shed (diagnostic) */ + double budget_ms; /* per-pair budget; set each frame from the cadence */ + double probe_ms; /* bound on one shedding streak (wall clock) */ + uint32_t max_streak; /* bound on one shedding streak (count) */ + double hysteresis; /* resume threshold as a fraction of budget (0..1) */ + /* state */ + double avg_ms; /* EMA of measured pair time */ + int shedding; /* currently suppressing */ + int probing; /* the last decision forced a probe; the next + * measurement is authoritative */ + uint32_t streak; /* consecutive sheds in this streak */ + uint64_t streak_start_ms;/* when this streak began */ + uint64_t last_pair_ms; /* when the last pair was published */ + int have_pair; + uint64_t pairs_seen; /* measurements folded into the average */ + uint64_t shed_total; /* pairs skipped since reset */ +} StereoShedPolicy; + +void stereo_shed_policy_init(StereoShedPolicy *p); +/* Reads PSX_STEREO_SHED (0 disables), PSX_STEREO_SHED_PROBE_MS, + * PSX_STEREO_SHED_MAX_STREAK and PSX_STEREO_SHED_HYSTERESIS, clamping each to a + * safe range. Called once per session. */ +void stereo_shed_policy_configure_from_env(StereoShedPolicy *p); +void stereo_shed_policy_set_budget(StereoShedPolicy *p, double budget_ms); +/* Clears the timing estimate and streak but keeps configuration. Call on a + * scene/mode transition: a new scene must not inherit the previous one's + * estimate, and a cadence change invalidates the budget. */ +void stereo_shed_policy_reset(StereoShedPolicy *p); + +/* 1 = skip this frame's pair, 0 = render it. */ +int stereo_shed_policy_should_shed(StereoShedPolicy *p, uint64_t now_ms); +/* Fold a measured pair attempt into the estimate. `ok` = a complete pair was + * published (so the retained pair is now fresh). */ +void stereo_shed_policy_note_pair(StereoShedPolicy *p, double pair_ms, + uint64_t now_ms, int ok); + +uint32_t stereo_shed_policy_streak_sheds(const StereoShedPolicy *p); +uint32_t stereo_shed_policy_streak_ms(const StereoShedPolicy *p, uint64_t now_ms); +double stereo_shed_policy_avg_ms(const StereoShedPolicy *p); +/* Age of the retained pair, or 0 when no pair has been published yet. */ +uint64_t stereo_shed_policy_pair_age_ms(const StereoShedPolicy *p, uint64_t now_ms); + +#ifdef __cplusplus +} +#endif + +#endif /* PSX_STEREO_SHED_POLICY_H */ diff --git a/runtime/runtime.cmake b/runtime/runtime.cmake index 1236eea2..bfaa49cb 100644 --- a/runtime/runtime.cmake +++ b/runtime/runtime.cmake @@ -487,6 +487,7 @@ set(PSXRECOMP_RUNTIME_SOURCES ${PSXRECOMP_ROOT}/runtime/src/guest_cycle_profile.c ${PSXRECOMP_ROOT}/runtime/src/latency_ring.c ${PSXRECOMP_ROOT}/runtime/src/frame_trace.c + ${PSXRECOMP_ROOT}/runtime/src/stereo_shed_policy.c ${PSXRECOMP_ROOT}/runtime/src/present_image_ring.c ${PSXRECOMP_ROOT}/runtime/src/gpu_timeline.c ${PSXRECOMP_ROOT}/runtime/src/data_shards.c diff --git a/runtime/src/debug_server.c b/runtime/src/debug_server.c index 341dee89..f814a288 100644 --- a/runtime/src/debug_server.c +++ b/runtime/src/debug_server.c @@ -9292,6 +9292,10 @@ static void handle_stereo_stats(int id, const char *json) { "\"last_eye\":%d,\"last_failure\":\"%s\"," "\"last_failed_attempt\":%llu,\"last_failed_eye\":%d," "\"last_failed_reason\":\"%s\",\"retained_pair_id\":%llu," + "\"shedding\":%d,\"shed_streak\":%llu,\"shed_streak_ms\":%u," + "\"shed_streak_max\":%llu,\"stale_frames\":%llu,\"budget_resets\":%llu," + "\"budget_ms\":%.3f,\"budget_source\":%u," + "\"last_pair_age_ms\":%llu," "\"last_pair_ms\":%.3f,\"avg_pair_ms\":%.3f," "\"published\":{\"valid\":%u,\"pair_id\":%llu,\"guest_cycle\":%llu," "\"width\":%u,\"height\":%u,\"staged_mask\":%u,\"mode\":%u,\"presents\":%llu},\"captures\":%s}", @@ -9307,6 +9311,10 @@ static void handle_stereo_stats(int id, const char *json) { (unsigned long long)s.last_failed_attempt, s.last_failed_eye, s.last_failed_reason ? s.last_failed_reason : "", (unsigned long long)s.retained_pair_id, + s.shedding, (unsigned long long)s.shed_streak, s.shed_streak_ms, + (unsigned long long)s.shed_streak_max, (unsigned long long)s.stale_frames, + (unsigned long long)s.budget_resets, s.budget_ms, s.budget_source, + (unsigned long long)s.last_pair_age_ms, s.last_pair_ms, s.avg_pair_ms, g.valid, (unsigned long long)g.pair_id, (unsigned long long)g.guest_cycle, g.width, g.height, g.staged_mask, g.mode, (unsigned long long)g.presents, captures); diff --git a/runtime/src/psx_openxr.c b/runtime/src/psx_openxr.c index 1cf463ba..3404dedf 100644 --- a/runtime/src/psx_openxr.c +++ b/runtime/src/psx_openxr.c @@ -581,6 +581,10 @@ int psx_openxr_begin(int width,int height,double units) { XrFrameWaitInfo wait={XR_TYPE_FRAME_WAIT_INFO};XrFrameState fs={XR_TYPE_FRAME_STATE}; if(!check(xrWaitFrame(s_session,&wait,&fs),"wait_frame")){ psx_frame_trace_add(PSX_TRACE_XR_WAIT_FRAME, xr_trace_us(gl_renderer_perf_ticks()-trace_t)); + /* Session-level submission failure: the stereo cadence context is gone, + * so drop any stale pair-time estimate rather than carrying it into the + * recovery attempt. */ + psx_mod_stereo_reset_budget(); (void)service_loss();psx_frame_trace_commit();return 0;} psx_frame_trace_add(PSX_TRACE_XR_WAIT_FRAME, xr_trace_us(gl_renderer_perf_ticks()-trace_t)); s_stats.waits++;s_time=fs.predictedDisplayTime;s_stats.predicted_time=(uint64_t)s_time; @@ -594,12 +598,18 @@ int psx_openxr_begin(int width,int height,double units) { } s_prev_display_time=s_time;s_prev_display_valid=1; /* Record the runtime display period with its name for cadence analysis. */ + /* What the runtime wants us to hit is the only true deadline for the pair. + * Hand it to stereo shedding (PSX VBlank is a guest redraw budget, not a + * compositor deadline) and record it with the runtime name so a trace can be + * read without guessing the headset's refresh. */ + psx_mod_stereo_set_budget_ms(fs.predictedDisplayPeriod>0?(double)fs.predictedDisplayPeriod/1e6:0.0); psx_frame_trace_session(s_stats.runtime, fs.predictedDisplayPeriod>0?(uint32_t)(fs.predictedDisplayPeriod/1000):0u); trace_t = gl_renderer_perf_ticks(); XrFrameBeginInfo begin={XR_TYPE_FRAME_BEGIN_INFO}; if(!check(xrBeginFrame(s_session,&begin),"begin_frame")){ psx_frame_trace_add(PSX_TRACE_XR_BEGIN_FRAME, xr_trace_us(gl_renderer_perf_ticks()-trace_t)); + psx_mod_stereo_reset_budget(); (void)service_loss();psx_frame_trace_commit();return 0;} psx_frame_trace_add(PSX_TRACE_XR_BEGIN_FRAME, xr_trace_us(gl_renderer_perf_ticks()-trace_t)); s_stats.frame_open=1;s_stats.tracking=0; diff --git a/runtime/src/render_pass.c b/runtime/src/render_pass.c index e25ab04c..a4617f08 100644 --- a/runtime/src/render_pass.c +++ b/runtime/src/render_pass.c @@ -38,6 +38,7 @@ #include "timers.h" #include "psx_video_timing.h" #include "gte_view.h" +#include "stereo_shed_policy.h" extern uint8_t *memory_get_ram_ptr(void); extern uint8_t *memory_get_scratchpad_ptr(void); @@ -283,7 +284,16 @@ static RenderPassFailure s_attempt; static const char *s_checkpoint_failure; static RenderStereoStats s_stereo; static int s_pair_busy; -static uint32_t s_pair_retry; +/* Whole-pair shedding/recovery policy (bounded in time and count; see + * stereo_shed_policy.c). Replaces the old attempt counter, which could suppress + * many pairs after a single over-budget frame. */ +static StereoShedPolicy s_shed; +/* Headset deadline pushed by the OpenXR layer (target display period, ms). + * 0 = no headset: fall back to the guest-VBlank redraw budget. */ +static double s_shed_xr_budget_ms; +static uint32_t s_shed_period; +static int32_t s_shed_w, s_shed_h; +static int s_shed_geom_valid; /* A HUD-only capture transaction reuses eye 0's slot but is not an eye draw; * frame tracing must not attribute its cost to the left eye. */ static int s_capture_only; @@ -322,6 +332,32 @@ static int verify_on(void) { return s_verify; } +/* PSX_STEREO_SHED_BUDGET_MS, when set, replaces the guest-VBlank-derived budget + * with a real headset deadline (e.g. 11.1 at 90 Hz, 8.3 at 120 Hz). The VBlank + * cadence is only a fallback: it is a guest redraw budget, not an OpenXR + * display deadline, and conflating the two is wrong on every non-60 Hz headset. + * Returns 0 when unset so the caller derives the fallback. */ +static double shed_budget_override_ms(void) { + static double v = -1.0; + if (v < 0.0) { + const char *e = getenv("PSX_STEREO_SHED_BUDGET_MS"); + double d = e ? atof(e) : 0.0; + v = (d > 0.0 && d < 1000.0) ? d : 0.0; + } + return v; +} + +/* The pair budget actually used, in priority order: the explicit env override, + * the headset display deadline the OpenXR layer pushed, then the guest-VBlank + * redraw budget as a last resort. */ +static double shed_budget_ms(uint32_t period_vblanks, uint32_t *source) { + double ov = shed_budget_override_ms(); + if (ov > 0.0) { if (source) *source = 2; return ov; } + if (s_shed_xr_budget_ms > 0.0) { if (source) *source = 1; return s_shed_xr_budget_ms; } + if (source) *source = 0; + return period_vblanks * (double)g_psx_vblank_cycles / 33868.8 * 0.8; +} + void render_pass_get_stats(RenderPassStats *out) { if (!out) return; *out = s_stats; @@ -342,7 +378,12 @@ void render_pass_reset_session(void) { memset(&s_stereo, 0, sizeof s_stereo); s_stereo.last_eye = -1; s_pair_busy = 0; - s_pair_retry = 0; + /* Shedding policy: defaults, then env overrides (PSX_STEREO_SHED=0 gives the + * baseline diagnostic with shedding fully disabled). */ + stereo_shed_policy_init(&s_shed); + stereo_shed_policy_configure_from_env(&s_shed); + stereo_shed_policy_reset(&s_shed); + s_shed_geom_valid = 0; /* Frame tracing is opt-in per session: re-read the env, install the host * clock, and clear any previous session's records. */ psx_frame_trace_configure_from_env(); @@ -1029,27 +1070,55 @@ int psx_mod_render_stereo(CPUState *cpu, const PSXModStereoFrame *frame, * previous traced frame. */ int trace_on = psx_frame_trace_enabled(); int trace_opened = trace_on ? psx_frame_trace_open() : 0; + uint64_t now_ms = psx_host_mono_ms(); if (trace_on) { static uint64_t prev_ms; static int prev_valid; - uint64_t now_ms = psx_host_mono_ms(); if (prev_valid) psx_frame_trace_add(PSX_TRACE_GUEST_SIM, (now_ms - prev_ms) * 1000u); prev_ms = now_ms; prev_valid = 1; psx_frame_trace_attempt(s_stereo.attempts, 0, 0); } - budget_ms = frame->period_vblanks * (double)g_psx_vblank_cycles / - 33868.8 * 0.8; - if (s_stereo.avg_pair_ms > budget_ms && ++s_pair_retry < 30u) { - s_stereo.shed++; - if (trace_on) { - psx_frame_trace_attempt(s_stereo.attempts, s_pair_retry, 0); - psx_frame_trace_flag(PSX_FRAME_TRACE_STALE_PAIR); - if (trace_opened) psx_frame_trace_commit(); - } - return 0; + budget_ms = shed_budget_ms(frame->period_vblanks, &s_stereo.budget_source); + s_stereo.budget_ms = budget_ms; + /* A scene/mode transition changes cadence and workload, so the previous + * average and budget do not carry over. */ + if (!s_shed_geom_valid || frame->period_vblanks != s_shed_period || + (int32_t)frame->w != s_shed_w || (int32_t)frame->h != s_shed_h) { + if (s_shed_geom_valid) s_stereo.budget_resets++; + s_shed_period = frame->period_vblanks; + s_shed_w = (int32_t)frame->w; s_shed_h = (int32_t)frame->h; + s_shed_geom_valid = 1; + stereo_shed_policy_reset(&s_shed); } - s_pair_retry = 0; + stereo_shed_policy_set_budget(&s_shed, budget_ms); + { + int was_shedding = s_shed.shedding; + if (stereo_shed_policy_should_shed(&s_shed, now_ms)) { + s_stereo.shed++; + s_stereo.stale_frames++; + s_stereo.shed_streak = stereo_shed_policy_streak_sheds(&s_shed); + if (s_stereo.shed_streak > s_stereo.shed_streak_max) + s_stereo.shed_streak_max = s_stereo.shed_streak; + s_stereo.shed_streak_ms = stereo_shed_policy_streak_ms(&s_shed, now_ms); + s_stereo.shedding = 1; + if (trace_on) { + psx_frame_trace_attempt(s_stereo.attempts, s_stereo.shed_streak, + s_stereo.shed_streak_ms); + psx_frame_trace_flag(PSX_FRAME_TRACE_STALE_PAIR | PSX_FRAME_TRACE_SHED); + if (trace_opened) psx_frame_trace_commit(); + } + return 0; + } + /* Log once per streak (not per frame), with both the skipped count and + * the elapsed wall clock the old attempt counter never tracked. */ + if (was_shedding) + fprintf(stderr, "psxrecomp: stereo shed streak ended after %llu " + "skipped, %u ms\n", + (unsigned long long)s_stereo.shed_streak, + s_stereo.shed_streak_ms); + } + s_stereo.shedding = s_shed.shedding; if (g_overlay_flush_pending_cycles) g_overlay_flush_pending_cycles(); cycle = psx_cycle_count; stores = g_guest_store_count; mismatches = s_stats.verify_mismatch; aborted = s_stats.aborted; @@ -1085,8 +1154,6 @@ int psx_mod_render_stereo(CPUState *cpu, const PSXModStereoFrame *frame, elapsed = (double)(gl_renderer_perf_ticks() - t0) * 1000.0 / (double)gl_renderer_perf_frequency(); s_stereo.last_pair_ms = elapsed; - s_stereo.avg_pair_ms = s_stereo.avg_pair_ms > 0.0 - ? s_stereo.avg_pair_ms * 0.9 + elapsed * 0.1 : elapsed; if (ok && !gl_renderer_stereo_publish(s_stereo.attempts, cycle, s_stereo.eye_view)) { ok = 0; s_stereo.last_failure = "pair_publish"; } @@ -1102,6 +1169,17 @@ int psx_mod_render_stereo(CPUState *cpu, const PSXModStereoFrame *frame, s_stereo.retained_pair_id = s_stereo.last_pair_id; gl_renderer_stereo_stage_reset(); } + /* Fold the attempt into the policy's estimate (it owns the EMA now). A + * failed or aborted attempt still counts as a measurement, so a stale high + * average cannot outlive the condition that produced it; only a published + * pair refreshes the retained-pair age. Pair age is tracked separately from + * the compute-time estimate so "how old is the picture" never hides behind + * "how fast was the last attempt". */ + stereo_shed_policy_note_pair(&s_shed, elapsed, now_ms, ok); + s_stereo.avg_pair_ms = stereo_shed_policy_avg_ms(&s_shed); + s_stereo.last_pair_age_ms = stereo_shed_policy_pair_age_ms(&s_shed, now_ms); + s_stereo.shedding = s_shed.shedding; + if (!ok) s_stereo.stale_frames++; if (trace_on) { psx_frame_trace_add(PSX_TRACE_PAIR_TOTAL, (uint64_t)(elapsed * 1000.0)); psx_frame_trace_result(ok, ok ? s_stereo.last_pair_id : 0, @@ -1113,6 +1191,24 @@ int psx_mod_render_stereo(CPUState *cpu, const PSXModStereoFrame *frame, return ok; } +/* Game hook: a scene or mode transition invalidates the pair-time estimate and + * the derived budget, so the new scene does not inherit the old one's. Cheap + * and idempotent; safe to call on every transition. */ +void psx_mod_stereo_reset_budget(void) { + stereo_shed_policy_reset(&s_shed); + s_shed_geom_valid = 0; + /* The deadline came from a session that is now gone or whose cadence just + * changed; the OpenXR layer re-pushes it on the next frame it runs. */ + s_shed_xr_budget_ms = 0.0; + s_stereo.budget_resets++; +} + +void psx_mod_stereo_set_budget_ms(double ms) { + /* Push the headset deadline. Ignore nonsense rather than letting a bad + * value widen or collapse the budget. */ + s_shed_xr_budget_ms = (ms > 0.0 && ms < 1000.0) ? ms : 0.0; +} + int psx_mod_render_capture_rgba(CPUState *cpu,const PSXModRenderPass *rect, PSXModRenderPassFn fn,void *user, uint8_t *rgba,uint32_t bytes) { diff --git a/runtime/src/stereo_shed_policy.c b/runtime/src/stereo_shed_policy.c new file mode 100644 index 00000000..dd3621a2 --- /dev/null +++ b/runtime/src/stereo_shed_policy.c @@ -0,0 +1,145 @@ +/* stereo_shed_policy.c — bounded whole-pair shedding. See stereo_shed_policy.h. */ + +#include "stereo_shed_policy.h" + +#include +#include + +#define SHED_EMA_ALPHA 0.1 +#define SHED_DEFAULT_PROBE_MS 250.0 +#define SHED_DEFAULT_MAX_STREAK 8u +#define SHED_DEFAULT_HYSTERESIS 0.9 + +static double clamp_double(double v, double lo, double hi) { + if (!(v == v)) return lo; /* NaN */ + if (v < lo) return lo; + if (v > hi) return hi; + return v; +} + +void stereo_shed_policy_init(StereoShedPolicy *p) { + if (!p) return; + memset(p, 0, sizeof *p); + p->enabled = 1; + p->probe_ms = SHED_DEFAULT_PROBE_MS; + p->max_streak = SHED_DEFAULT_MAX_STREAK; + p->hysteresis = SHED_DEFAULT_HYSTERESIS; +} + +void stereo_shed_policy_configure_from_env(StereoShedPolicy *p) { + const char *e; + if (!p) return; + e = getenv("PSX_STEREO_SHED"); + if (e) p->enabled = (e[0] && e[0] != '0') ? 1 : 0; + e = getenv("PSX_STEREO_SHED_PROBE_MS"); + if (e) p->probe_ms = clamp_double(strtod(e, NULL), 10.0, 5000.0); + e = getenv("PSX_STEREO_SHED_MAX_STREAK"); + if (e) { + long v = strtol(e, NULL, 0); + p->max_streak = (uint32_t)(v < 1 ? 1 : (v > 240 ? 240 : v)); + } + e = getenv("PSX_STEREO_SHED_HYSTERESIS"); + if (e) p->hysteresis = clamp_double(strtod(e, NULL), 0.5, 1.0); +} + +void stereo_shed_policy_set_budget(StereoShedPolicy *p, double budget_ms) { + if (!p) return; + p->budget_ms = budget_ms > 0.0 ? budget_ms : 0.0; +} + +void stereo_shed_policy_reset(StereoShedPolicy *p) { + if (!p) return; + p->avg_ms = 0.0; + p->shedding = 0; + p->probing = 0; + p->streak = 0; + p->streak_start_ms = 0; + p->pairs_seen = 0; +} + +int stereo_shed_policy_should_shed(StereoShedPolicy *p, uint64_t now_ms) { + if (!p || !p->enabled) return 0; + + if (p->avg_ms > p->budget_ms) { + if (!p->shedding) { /* enter shedding at the first over-budget sample */ + p->shedding = 1; + p->streak = 0; + p->streak_start_ms = now_ms; + } + } else if (p->avg_ms < p->budget_ms * p->hysteresis) { + p->shedding = 0; /* clearly recovered */ + p->streak = 0; + } else { + /* hysteresis band: hold whatever state we are in */ + } + + if (!p->shedding) return 0; + + /* Bounded recovery: neither an elapsed-time nor an attempt-count rule may + * suppress indefinitely. Whichever bound trips first forces a probe, whose + * result note_pair() treats as authoritative. The streak's timer restarts + * here so a persistent overload probes again after the same bound rather + * than every frame. */ + uint64_t elapsed = now_ms >= p->streak_start_ms ? now_ms - p->streak_start_ms : 0; + if (p->streak >= p->max_streak || elapsed >= (uint64_t)p->probe_ms) { + p->probing = 1; + p->streak = 0; + p->streak_start_ms = now_ms; + return 0; /* render this one: take a fresh sample */ + } + p->streak++; + p->shed_total++; + return 1; +} + +void stereo_shed_policy_note_pair(StereoShedPolicy *p, double pair_ms, + uint64_t now_ms, int ok) { + if (!p) return; + if (pair_ms < 0.0) pair_ms = 0.0; + if (p->probing) { + /* A forced probe is a deliberate fresh measurement: use it directly + * instead of blending it away. Otherwise one spike would keep the + * estimate high for many probes, and the headset would keep showing + * stale pairs long after the spike ended. */ + p->probing = 0; + p->avg_ms = pair_ms; + /* Exit on the deadline itself, not on the hysteresis threshold. A pair + * time that settles between the two (here 10.0 to 11.111 ms) would + * otherwise sit in the band forever: the band holds whatever state it + * is in, so a streak entered during a heavy moment never ends and + * shedding starves rendering while the pair is in fact meeting its + * deadline. A latched streak is bounded by the probe interval, which is + * still the recovery guarantee this policy promises. */ + if (pair_ms <= p->budget_ms) { + p->shedding = 0; /* the overload is over */ + p->streak = 0; + } + } else { + p->avg_ms = p->pairs_seen ? p->avg_ms * (1.0 - SHED_EMA_ALPHA) + pair_ms * SHED_EMA_ALPHA + : pair_ms; + } + p->pairs_seen++; + if (ok) { + p->last_pair_ms = now_ms; + p->have_pair = 1; + } +} + +uint32_t stereo_shed_policy_streak_sheds(const StereoShedPolicy *p) { + return p ? p->streak : 0; +} + +uint32_t stereo_shed_policy_streak_ms(const StereoShedPolicy *p, uint64_t now_ms) { + if (!p || !p->shedding || now_ms < p->streak_start_ms) return 0; + uint64_t d = now_ms - p->streak_start_ms; + return d > 0xFFFFFFFFu ? 0xFFFFFFFFu : (uint32_t)d; +} + +double stereo_shed_policy_avg_ms(const StereoShedPolicy *p) { + return p ? p->avg_ms : 0.0; +} + +uint64_t stereo_shed_policy_pair_age_ms(const StereoShedPolicy *p, uint64_t now_ms) { + if (!p || !p->have_pair || now_ms < p->last_pair_ms) return 0; + return now_ms - p->last_pair_ms; +} diff --git a/runtime/tests/test_render_pass_sandbox.c b/runtime/tests/test_render_pass_sandbox.c index caedab1c..ed260bf5 100644 --- a/runtime/tests/test_render_pass_sandbox.c +++ b/runtime/tests/test_render_pass_sandbox.c @@ -780,6 +780,23 @@ static void test_stereo(void) { f.period_vblanks = 0; CHECK(!psx_mod_render_stereo(&cpu, &f, pair_draw, NULL), "invalid cadence refused"); + /* The headset deadline the OpenXR layer pushes outranks the guest-VBlank + * fallback, and dropping it restores the fallback. */ + f.period_vblanks = 2; + psx_mod_stereo_reset_budget(); + psx_mod_stereo_set_budget_ms(11.1); + CHECK(psx_mod_render_stereo(&cpu, &f, pair_draw, NULL) == 1, + "pair renders under a headset deadline"); + render_stereo_get_stats(&stats); + CHECK(stats.budget_source == 1 && stats.budget_ms > 11.0 && stats.budget_ms < 11.2, + "headset deadline drives the pair budget"); + psx_mod_stereo_reset_budget(); + CHECK(psx_mod_render_stereo(&cpu, &f, pair_draw, NULL) == 1, + "pair renders after the deadline is dropped"); + render_stereo_get_stats(&stats); + CHECK(stats.budget_source == 0 && stats.budget_ms > 0.0, + "guest-VBlank fallback restored when no headset deadline is set"); + /* Tracing is observation only: the same pair must publish the same id and * reach the same guest-state hashes with capture on as with it off. */ { diff --git a/runtime/tests/test_stereo_shed_policy.c b/runtime/tests/test_stereo_shed_policy.c new file mode 100644 index 00000000..dfb87f72 --- /dev/null +++ b/runtime/tests/test_stereo_shed_policy.c @@ -0,0 +1,210 @@ +/* test_stereo_shed_policy.c — PR2 shedding/recovery policy. + * + * Covers the plan's PR2 test list at the policy level: short spike, persistent + * overload, alternating load (hysteresis), stale estimate, no unbounded streak, + * transient measurement gaps, and the disable switch. Both-eye integrity and + * guest-cycle accounting are structural (the policy has no eye or guest state); + * the render-pass side already asserts eye-cycle equality. + */ + +#include "stereo_shed_policy.h" + +#include +#include + +static int g_fail; +#define CHECK(cond) do { \ + if (!(cond)) { printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); g_fail = 1; } \ +} while (0) + +/* Drive `budget`/`probe`/`streak` config the way a session would, without env. */ +static void configure(StereoShedPolicy *p, double avg_ms, uint64_t probe_ms, + uint32_t max_streak, double hysteresis) { + stereo_shed_policy_init(p); + p->budget_ms = 16.0; + p->probe_ms = (double)probe_ms; + p->max_streak = max_streak; + p->hysteresis = hysteresis; + if (avg_ms > 0.0) { p->avg_ms = avg_ms; p->pairs_seen = 1; } +} + +/* 1: disabled means never shed, whatever the timing. */ +static void test_disabled(void) { + StereoShedPolicy p; + configure(&p, 40.0, 250, 8, 0.9); + p.enabled = 0; + for (uint64_t t = 0; t < 2000; t += 8) + CHECK(stereo_shed_policy_should_shed(&p, t) == 0); +} + +/* 2: a single spike sheds, but the streak is bounded and one good probe ends it + * (the probe replaces the estimate instead of decaying into it). */ +static void test_short_spike_bounded(void) { + StereoShedPolicy p; + uint32_t sheds = 0; + configure(&p, 0.0, 250, 8, 0.9); + uint64_t t = 0; + for (int i = 0; i < 20; i++) { /* establish a fast baseline (~12 ms) */ + stereo_shed_policy_note_pair(&p, 12.0, t, 1); + t += 16; + CHECK(stereo_shed_policy_should_shed(&p, t) == 0); + } + stereo_shed_policy_note_pair(&p, 80.0, t, 1); /* one heavy frame */ + for (int i = 0; i < 200; i++) { + if (stereo_shed_policy_should_shed(&p, t)) { sheds++; t += 16; continue; } + stereo_shed_policy_note_pair(&p, 12.0, t, 1); /* the probe measures fast again */ + t += 16; + } + CHECK(sheds > 0); /* the spike did suppress something */ + CHECK(sheds <= 8u); /* ...but never more than one bounded streak */ + CHECK(stereo_shed_policy_avg_ms(&p) < 16.0); /* and recovery was immediate */ +} + +/* 3: sustained overload keeps shedding but must still probe periodically (so a + * stale average can never suppress forever), and each streak stays bounded. */ +static void test_persistent_overload_probes(void) { + StereoShedPolicy p; + uint32_t sheds = 0, attempts = 0; + uint32_t run = 0, max_run = 0; + configure(&p, 30.0, 250, 8, 0.9); + uint64_t t = 0; + for (int i = 0; i < 500; i++) { /* 500 * 8 ms = 4 s of sustained overload */ + if (stereo_shed_policy_should_shed(&p, t)) { sheds++; run++; } + else { attempts++; if (run > max_run) max_run = run; run = 0; + stereo_shed_policy_note_pair(&p, 30.0, t, 1); } /* still slow */ + t += 8; + } + CHECK(sheds > 0); + CHECK(attempts > 0); /* overload does not suppress everything */ + CHECK(max_run <= 8u); /* every streak bounded by max_streak */ + CHECK(sheds <= 500u - attempts); +} + +/* 4: alternating load must not flap: a value inside the hysteresis band holds + * the current state instead of toggling every frame. */ +static void test_alternating_load_hysteresis(void) { + StereoShedPolicy p; + int transitions = 0, prev = 0; + configure(&p, 20.0, 100000, 240, 0.9); /* wide bounds: isolate hysteresis */ + uint64_t t = 0; + for (int i = 0; i < 200; i++) { + int s = stereo_shed_policy_should_shed(&p, t); + if (i && s != prev) transitions++; + prev = s; + /* alternate just under budget and inside the band (14.4..16) */ + stereo_shed_policy_note_pair(&p, (i & 1) ? 15.8 : 15.0, t, 1); + t += 8; + } + CHECK(transitions <= 2); /* entered once, at most one exit: no flapping */ +} + +/* 5: stale estimate — a long gap with no measurement must still be interrupted + * by a probe within probe_ms, and must not suppress indefinitely. */ +static void test_stale_estimate_recovers(void) { + StereoShedPolicy p; + configure(&p, 40.0, 250, 1000, 0.9); /* high avg, count bound far away */ + CHECK(stereo_shed_policy_should_shed(&p, 0) == 1); /* enter */ + CHECK(stereo_shed_policy_should_shed(&p, 8) == 1); + CHECK(stereo_shed_policy_should_shed(&p, 260) == 0); /* >= probe_ms: probe */ + CHECK(stereo_shed_policy_should_shed(&p, 260) == 1); /* fresh streak begins */ + CHECK(stereo_shed_policy_should_shed(&p, 520) == 0); /* bounded again */ +} + +/* 6: a measurement gap (e.g. OpenXR failing so render_stereo never runs) leaves + * the average untouched, but the time bound still forces a probe. */ +static void test_transient_measurement_gap(void) { + StereoShedPolicy p; + uint64_t first_probe = 0; + configure(&p, 30.0, 250, 8, 0.9); + uint64_t t = 0; + for (int i = 0; i < 200; i++) { + if (stereo_shed_policy_should_shed(&p, t) == 0) { first_probe = t; break; } + t += 8; /* no note_pair: the average is never refreshed */ + } + CHECK(first_probe > 0 && first_probe <= 256u); +} + +/* 7: a failure that aborts the attempt early is still a measurement, so the + * average falls and shedding ends (no unbounded failure loop). */ +static void test_failure_ends_streak(void) { + StereoShedPolicy p; + configure(&p, 30.0, 100000, 240, 0.9); + CHECK(stereo_shed_policy_should_shed(&p, 0) == 1); + stereo_shed_policy_note_pair(&p, 3.0, 8, 0); /* aborted early, not published */ + for (int i = 0; i < 40; i++) + stereo_shed_policy_note_pair(&p, 3.0, (uint64_t)(16 + i * 8), 0); + CHECK(stereo_shed_policy_should_shed(&p, 1000) == 0); +} + +/* 8: pair age tracks the retained pair independently of the compute estimate. */ +static void test_pair_age_separate(void) { + StereoShedPolicy p; + configure(&p, 0.0, 250, 8, 0.9); + CHECK(stereo_shed_policy_pair_age_ms(&p, 100) == 0); /* nothing published yet */ + stereo_shed_policy_note_pair(&p, 12.0, 1000, 1); + CHECK(stereo_shed_policy_pair_age_ms(&p, 1000) == 0); + CHECK(stereo_shed_policy_pair_age_ms(&p, 1050) == 50); + /* a later failed attempt must not refresh the age */ + stereo_shed_policy_note_pair(&p, 12.0, 1100, 0); + CHECK(stereo_shed_policy_pair_age_ms(&p, 1100) == 100); +} + +/* 9: reset clears the estimate so a new scene does not inherit it. */ +static void test_reset_on_transition(void) { + StereoShedPolicy p; + configure(&p, 40.0, 250, 8, 0.9); + CHECK(stereo_shed_policy_should_shed(&p, 0) == 1); + stereo_shed_policy_reset(&p); + CHECK(stereo_shed_policy_avg_ms(&p) == 0.0); + CHECK(stereo_shed_policy_should_shed(&p, 10) == 0); /* fresh, under budget */ +} + +/* 11: a pair time that settles *inside* the hysteresis band must not latch the + * shedding state. Measured case (SteamVR null HMD at 90 Hz): deadline + * 11.111 ms, pair 10.4 ms, so the band is 10.0 to 11.111. Shedding entered + * during a heavy moment and then never ended, because the band holds whatever + * state it is in and the old probe exit test was the band's lower edge. That + * cost 26 sheds/s against 3.7 published pairs/s. The probe is authoritative and + * must exit on the deadline itself. */ +static void test_band_latch_recovers(void) { + StereoShedPolicy p; + uint32_t sheds = 0; + configure(&p, 0.0, 250, 8, 0.9); + p.budget_ms = 11.111; + uint64_t t = 0; + stereo_shed_policy_note_pair(&p, 15.0, t, 1); /* heavy moment: enter shedding */ + for (int i = 0; i < 60; i++) { /* 2 s at a 33 ms frame */ + if (stereo_shed_policy_should_shed(&p, t)) { sheds++; t += 33; continue; } + stereo_shed_policy_note_pair(&p, 10.4, t, 1); /* steady state inside the band */ + t += 33; + } + CHECK(sheds <= 12u); /* one bounded streak, then rendering resumes */ + CHECK(stereo_shed_policy_avg_ms(&p) <= 11.111); +} + +/* 10: env parsing clamps to safe ranges (and PSX_STEREO_SHED=0 disables). */ +static void test_env(void) { + StereoShedPolicy p; + stereo_shed_policy_init(&p); + CHECK(p.enabled == 1 && p.max_streak >= 1u && p.probe_ms > 0.0); + stereo_shed_policy_configure_from_env(&p); /* no env set in CI: defaults hold */ + CHECK(p.max_streak >= 1u && p.max_streak <= 240u); + CHECK(p.hysteresis >= 0.5 && p.hysteresis <= 1.0); +} + +int main(void) { + test_disabled(); + test_short_spike_bounded(); + test_persistent_overload_probes(); + test_alternating_load_hysteresis(); + test_stale_estimate_recovers(); + test_transient_measurement_gap(); + test_failure_ends_streak(); + test_pair_age_separate(); + test_reset_on_transition(); + test_band_latch_recovers(); + test_env(); + if (g_fail) { printf("stereo_shed_policy: FAILED\n"); return 1; } + printf("stereo_shed_policy: all checks passed\n"); + return 0; +} From 4419d234ab66c839da94dba597794ac47a09eb00 Mon Sep 17 00:00:00 2001 From: Yves Date: Fri, 23 Oct 2026 03:24:41 +0800 Subject: [PATCH 15/29] Reuse one guest checkpoint per stereo pair Co-authored-by: Yves --- docs/internal/upstream/vr-pair-checkpoint.md | 16 +++ runtime/include/render_pass.h | 5 + runtime/src/debug_server.c | 5 + runtime/src/render_pass.c | 125 ++++++++++++++----- runtime/tests/test_render_pass_sandbox.c | 33 +++++ 5 files changed, 153 insertions(+), 31 deletions(-) create mode 100644 docs/internal/upstream/vr-pair-checkpoint.md diff --git a/docs/internal/upstream/vr-pair-checkpoint.md b/docs/internal/upstream/vr-pair-checkpoint.md new file mode 100644 index 00000000..d4f7a352 --- /dev/null +++ b/docs/internal/upstream/vr-pair-checkpoint.md @@ -0,0 +1,16 @@ +# Reuse one guest checkpoint per stereo pair + +Source: [FractalEngineer/psxrecomp](https://github.com/FractalEngineer/psxrecomp). +- [063b082a](https://github.com/FractalEngineer/psxrecomp/commit/063b082ae959488436596d90e6933d825e22e780) + +Original author: Yves ; retained as commit author +and Co-authored-by on this reconstructed port. + +Adapted: this feature and its tests. Excluded: other VR features, game policy, +assets and chronological investigation notes. +Base: `0cdf95957c3e34916aef5ff5b0b4e2b77fdc8790` (current upstream master or the preceding feature). + +Validation: Windows/GCC 13.2 Release: trace, sandbox and abort tests pass (3/3), including shared-baseline state and per-eye precision arms. + +Consumer: [Medal of Honor v0.1.6](https://github.com/FractalEngineer/Medal-Of-Honor-PSX-Recomp/tree/1957e28a5dc7f5daa79e2b9b6ed867eb06b41ace). +Requires a framework pin bump and rebuild; no game/BIOS regeneration. diff --git a/runtime/include/render_pass.h b/runtime/include/render_pass.h index a8be5f7f..1bcfc64c 100644 --- a/runtime/include/render_pass.h +++ b/runtime/include/render_pass.h @@ -102,6 +102,11 @@ typedef struct RenderStereoStats { uint32_t budget_source; /* 0 vblank (fallback), 1 headset, 2 env */ uint64_t last_pair_age_ms; /* age of the retained pair, this frame */ int shedding; /* policy is currently suppressing */ + /* Pair-scoped baseline: how often a machine snapshot was captured, and how + * often a later eye of the same pair reused it instead of re-capturing. */ + uint64_t checkpoint_saves; /* snapshots captured */ + uint64_t checkpoint_reuses; /* eyes that reused the pair baseline */ + uint64_t checkpoint_ram_bytes; /* RAM bytes copied by those captures */ } RenderStereoStats; void render_stereo_get_stats(RenderStereoStats *out); int render_pass_netplay_enabled(void); diff --git a/runtime/src/debug_server.c b/runtime/src/debug_server.c index f814a288..bca89a24 100644 --- a/runtime/src/debug_server.c +++ b/runtime/src/debug_server.c @@ -9295,6 +9295,8 @@ static void handle_stereo_stats(int id, const char *json) { "\"shedding\":%d,\"shed_streak\":%llu,\"shed_streak_ms\":%u," "\"shed_streak_max\":%llu,\"stale_frames\":%llu,\"budget_resets\":%llu," "\"budget_ms\":%.3f,\"budget_source\":%u," + "\"checkpoint_saves\":%llu,\"checkpoint_reuses\":%llu," + "\"checkpoint_ram_bytes\":%llu," "\"last_pair_age_ms\":%llu," "\"last_pair_ms\":%.3f,\"avg_pair_ms\":%.3f," "\"published\":{\"valid\":%u,\"pair_id\":%llu,\"guest_cycle\":%llu," @@ -9314,6 +9316,9 @@ static void handle_stereo_stats(int id, const char *json) { s.shedding, (unsigned long long)s.shed_streak, s.shed_streak_ms, (unsigned long long)s.shed_streak_max, (unsigned long long)s.stale_frames, (unsigned long long)s.budget_resets, s.budget_ms, s.budget_source, + (unsigned long long)s.checkpoint_saves, + (unsigned long long)s.checkpoint_reuses, + (unsigned long long)s.checkpoint_ram_bytes, (unsigned long long)s.last_pair_age_ms, s.last_pair_ms, s.avg_pair_ms, g.valid, (unsigned long long)g.pair_id, (unsigned long long)g.guest_cycle, g.width, g.height, g.staged_mask, diff --git a/runtime/src/render_pass.c b/runtime/src/render_pass.c index a4617f08..73862abc 100644 --- a/runtime/src/render_pass.c +++ b/runtime/src/render_pass.c @@ -292,6 +292,17 @@ static StereoShedPolicy s_shed; * 0 = no headset: fall back to the guest-VBlank redraw budget. */ static double s_shed_xr_budget_ms; static uint32_t s_shed_period; +/* Pair-scoped baseline (PR3). Both eyes of a stereo pair render from the same + * guest checkpoint: eye L captures it and the restore that ends eye L puts the + * live machine back to exactly that captured state, so eye R can reuse the + * capture instead of copying the whole live RAM a second time. Guards: + * - only stereo eyes inside one pair reuse it (s_baseline_ready is cleared by + * any non-stereo transaction and at the start of every pair); + * - a verify mismatch while reusing it retires the optimization for the + * session, falling back to a capture per eye (PSX_STEREO_BASELINE=0 does + * the same up front). */ +static int s_baseline_ready; /* live machine == captured baseline */ +static int s_baseline_enabled = -1; /* PSX_STEREO_BASELINE; -1 = not read yet */ static int32_t s_shed_w, s_shed_h; static int s_shed_geom_valid; /* A HUD-only capture transaction reuses eye 0's slot but is not an eye draw; @@ -358,6 +369,17 @@ static double shed_budget_ms(uint32_t period_vblanks, uint32_t *source) { return period_vblanks * (double)g_psx_vblank_cycles / 33868.8 * 0.8; } +/* Pair-scoped checkpoint reuse is on by default; PSX_STEREO_BASELINE=0 forces a + * machine capture per eye (the pre-PR3 behavior) so the two can be compared, and + * a restore-verify mismatch turns it off for the rest of the session. */ +static int baseline_reuse_enabled(void) { + if (s_baseline_enabled < 0) { + const char *e = getenv("PSX_STEREO_BASELINE"); + s_baseline_enabled = (e && e[0] == '0') ? 0 : 1; + } + return s_baseline_enabled; +} + void render_pass_get_stats(RenderPassStats *out) { if (!out) return; *out = s_stats; @@ -384,6 +406,9 @@ void render_pass_reset_session(void) { stereo_shed_policy_configure_from_env(&s_shed); stereo_shed_policy_reset(&s_shed); s_shed_geom_valid = 0; + /* Pair-scoped baseline: re-read the env and start uncaptured. */ + s_baseline_ready = 0; + s_baseline_enabled = -1; /* Frame tracing is opt-in per session: re-read the env, install the host * clock, and clear any previous session's records. */ psx_frame_trace_configure_from_env(); @@ -590,46 +615,58 @@ static uint64_t state_hash(const CPUState *cpu) { return h; } -static int checkpoint_save(const CPUState *cpu) { +/* `reuse` skips the bulk capture (RAM, scratchpad, icache, DMA, CPU, timers) + * when a previous eye of the same pair already captured this exact machine + * state. The per-eye arms are never skipped: the GPU transaction snapshot and + * gte_precision_checkpoint_begin() bound the pass and end with the restore. */ +static int checkpoint_save(const CPUState *cpu, int reuse) { uint32_t dma_len = dma_snapshot_bytes(); /* The whole live RAM: a pass's stores fold through the same geometry * (render_pass_store_to), so every byte one can reach is in here. */ uint32_t ram_bytes = memory_get_ram_bytes(); s_checkpoint_failure = NULL; - if (s_ram_copy_cap < ram_bytes) { - uint8_t *p = (uint8_t *)realloc(s_ram_copy, ram_bytes); - if (!p) { s_checkpoint_failure = "checkpoint_ram"; return 0; } - s_ram_copy = p; - s_ram_copy_cap = ram_bytes; - } - if (!s_dma_copy) { - s_dma_copy = (uint8_t *)malloc(dma_len); - if (!s_dma_copy) { s_checkpoint_failure = "checkpoint_dma"; return 0; } + /* A reused baseline must own its buffers and describe this machine (RAM + * and DMA geometry cannot change inside a session). */ + int can_reuse = reuse && s_ram_copy && s_dma_copy && + s_ck.ram_bytes == ram_bytes && s_ck.dma_len == dma_len; + if (!can_reuse) { + if (s_ram_copy_cap < ram_bytes) { + uint8_t *p = (uint8_t *)realloc(s_ram_copy, ram_bytes); + if (!p) { s_checkpoint_failure = "checkpoint_ram"; return 0; } + s_ram_copy = p; + s_ram_copy_cap = ram_bytes; + } + if (!s_dma_copy) { + s_dma_copy = (uint8_t *)malloc(dma_len); + if (!s_dma_copy) { s_checkpoint_failure = "checkpoint_dma"; return 0; } + } } if (!gpu_pass_checkpoint_save()) { s_checkpoint_failure = "checkpoint_gpu"; return 0; } - s_ck.cpu = *cpu; - s_ck.ram_bytes = ram_bytes; - memcpy(s_ram_copy, memory_get_ram_ptr(), ram_bytes); - memcpy(s_ck.spad, memory_get_scratchpad_ptr(), RP_SPAD_SIZE); - memcpy(s_ck.icache, g_psx_icache_tv, sizeof s_ck.icache); - s_ck.i_stat = i_stat; - s_ck.i_mask = i_mask; - s_ck.csv = interrupts_get_cycles_since_vblank(); - timers_get_snapshot(s_ck.t_counter, s_ck.t_mode, s_ck.t_target, - s_ck.t_irq, s_ck.t_frac); - s_ck.dma_len = dma_len; - dma_snapshot_write(s_dma_copy); - s_ck.store_count = g_guest_store_count; - s_ck.mmio_count = g_mmio_access_count; - s_ck.last_store_pc = g_debug_last_store_pc; - s_ck.current_func = g_debug_current_func_addr; - s_ck.dispatch_depth = g_psx_dispatch_depth; - s_ck.call_bail = g_psx_call_bail; - memset(&s_ck.nest, 0, sizeof s_ck.nest); - nesting_save(&s_ck.nest); + if (!can_reuse) { + s_ck.cpu = *cpu; + s_ck.ram_bytes = ram_bytes; + memcpy(s_ram_copy, memory_get_ram_ptr(), ram_bytes); + memcpy(s_ck.spad, memory_get_scratchpad_ptr(), RP_SPAD_SIZE); + memcpy(s_ck.icache, g_psx_icache_tv, sizeof s_ck.icache); + s_ck.i_stat = i_stat; + s_ck.i_mask = i_mask; + s_ck.csv = interrupts_get_cycles_since_vblank(); + timers_get_snapshot(s_ck.t_counter, s_ck.t_mode, s_ck.t_target, + s_ck.t_irq, s_ck.t_frac); + s_ck.dma_len = dma_len; + dma_snapshot_write(s_dma_copy); + s_ck.store_count = g_guest_store_count; + s_ck.mmio_count = g_mmio_access_count; + s_ck.last_store_pc = g_debug_last_store_pc; + s_ck.current_func = g_debug_current_func_addr; + s_ck.dispatch_depth = g_psx_dispatch_depth; + s_ck.call_bail = g_psx_call_bail; + memset(&s_ck.nest, 0, sizeof s_ck.nest); + nesting_save(&s_ck.nest); + } gte_precision_checkpoint_begin(); return 1; } @@ -863,7 +900,11 @@ static int render_transaction(struct CPUState *cpu, const PSXModRenderPass *pass s_open_generation = 0; s_leaks_before = gl_renderer_pass_leaks(); - if (!checkpoint_save(cpu)) { + /* A stereo eye whose machine state still equals the pair's captured + * baseline skips re-capturing it. */ + int reuse_baseline = eye >= 0 && !s_capture_only && + s_baseline_ready && baseline_reuse_enabled(); + if (!checkpoint_save(cpu, reuse_baseline)) { gl_renderer_pass_begin_diag(&s_attempt.gl); s_stats.checkpoint_refused++; /* Capture the rejection status before rolling the GL transaction back. */ @@ -871,6 +912,8 @@ static int render_transaction(struct CPUState *cpu, const PSXModRenderPass *pass transaction_end(eye, 0, 0); return pass_refuse(s_checkpoint_failure ? s_checkpoint_failure : "checkpoint", status); } + if (reuse_baseline) s_stereo.checkpoint_reuses++; + else { s_stereo.checkpoint_saves++; s_stereo.checkpoint_ram_bytes += s_ck.ram_bytes; } if (verify_on()) hash_before = state_hash(cpu); tb = gl_renderer_perf_ticks(); @@ -969,14 +1012,25 @@ static int render_transaction(struct CPUState *cpu, const PSXModRenderPass *pass (uint32_t)leaks); s_sub_eye = -1; sub_spans_forget(); + if (reuse_baseline) psx_frame_trace_flag(PSX_FRAME_TRACE_BASELINE_REUSED); } + int verify_ok = 1; if (verify_on()) { hash_after = state_hash(cpu); s_stats.verify_checks++; if (hash_after != hash_before || !gl_renderer_pass_verify_vram()) { + verify_ok = 0; s_stats.verify_mismatch++; if (eye >= 0) { ok = 0; s_stereo.last_failure = "restore_verify"; } + /* A mismatch while the eye rendered from the pair's shared baseline + * is exactly what this guard is for: retire the reuse for the + * session and capture per eye again from here on. */ + if (reuse_baseline && baseline_reuse_enabled()) { + s_baseline_enabled = 0; + fprintf(stderr, "psxrecomp: pair baseline reuse disabled after " + "a restore-verify mismatch\n"); + } if (s_stats.verify_mismatch <= 8) fprintf(stderr, "psxrecomp: RENDER PASS VERIFY mismatch " "(state %016llx -> %016llx)\n", @@ -984,6 +1038,11 @@ static int render_transaction(struct CPUState *cpu, const PSXModRenderPass *pass (unsigned long long)hash_after); } } + /* The restore just put the live machine back to the captured baseline, so + * the other eye of this pair may reuse it. Anything else - a non-stereo + * transaction, a HUD-only capture, a failed eye or an unverified restore - + * leaves nothing safe to reuse. */ + s_baseline_ready = (eye >= 0 && !s_capture_only && ok && verify_ok) ? 1 : 0; t1 = gl_renderer_perf_ticks(); { @@ -1132,6 +1191,10 @@ int psx_mod_render_stereo(CPUState *cpu, const PSXModStereoFrame *frame, call.fn = fn; call.user = user; s_pair_busy = 1; s_plan_serial++; /* separate the backup lease from any temporal plan */ + /* The guest has run since the previous pair, so the retained snapshot no + * longer describes the machine: only the second eye of this pair may reuse + * what the first one captures. */ + s_baseline_ready = 0; t0 = gl_renderer_perf_ticks(); for (call.eye = 0; call.eye < 2u; call.eye++) { s_stereo.last_eye = (int)call.eye; diff --git a/runtime/tests/test_render_pass_sandbox.c b/runtime/tests/test_render_pass_sandbox.c index ed260bf5..f80d7c6f 100644 --- a/runtime/tests/test_render_pass_sandbox.c +++ b/runtime/tests/test_render_pass_sandbox.c @@ -759,6 +759,13 @@ static void test_stereo(void) { "two restored eye transactions form one pair"); CHECK(stats.eye_cycle[0] == cycle && stats.eye_cycle[1] == cycle && stats.eye_hash[0] == stats.eye_hash[1], "same checkpoint identifiers"); + /* PR3: the pair captures one machine baseline and the second eye reuses it, + * yet both eyes still start from that same checkpoint (checked above). */ + CHECK(stats.checkpoint_saves == 1 && stats.checkpoint_reuses == 1, + "one baseline per pair, reused by the second eye"); + CHECK(stats.checkpoint_ram_bytes > 0, + "the captured baseline reports the RAM it copied"); + CHECK(s_prec_open == 0, "precision tracking is armed and rolled back per eye"); CHECK(cpu.gpr[8] == 123 && s_ram[0x456] == 0x77 && psx_cycle_count == cycle, "live state restored after the pair"); CHECK(s_view[0] == 0 && stats.eye_view[0][0] == -24 && stats.eye_view[1][0] == 24, @@ -824,6 +831,32 @@ static void test_stereo(void) { CHECK(psx_frame_trace_committed() > 0, "the traced pair was recorded"); psx_frame_trace_set_enabled(0); psx_frame_trace_reset(); } + + /* PSX_STEREO_BASELINE=0 restores the pre-PR3 capture per eye, and a pair + * rendered that way must still start both eyes from one checkpoint. */ + { +#ifdef _WIN32 + _putenv("PSX_STEREO_BASELINE=0"); +#else + setenv("PSX_STEREO_BASELINE", "0", 1); +#endif + f.period_vblanks = 2; + render_pass_reset_session(); s_gl_status = 0; s_begin_ok = 1; s_eye_calls = 0; + cpu.gpr[8] = 123; s_ram[0x456] = 0x77; + CHECK(psx_mod_render_stereo(&cpu, &f, pair_draw, NULL) == 1, + "pair renders with baseline reuse disabled"); + render_stereo_get_stats(&stats); + CHECK(stats.checkpoint_saves == 2 && stats.checkpoint_reuses == 0, + "reuse off captures a baseline per eye"); + CHECK(stats.eye_hash[0] == stats.eye_hash[1] && s_eye_calls == 2, + "capture-per-eye still starts both eyes from one checkpoint"); +#ifdef _WIN32 + _putenv("PSX_STEREO_BASELINE=1"); +#else + setenv("PSX_STEREO_BASELINE", "1", 1); +#endif + render_pass_reset_session(); + } } /* ---- 4. per-eye trace counters and guest sub-stage spans ---------------- */ From e4f1f985428101eb6c8f83fce834e281860b1fd2 Mon Sep 17 00:00:00 2001 From: Yves Date: Fri, 23 Oct 2026 03:25:18 +0800 Subject: [PATCH 16/29] Capture both HUD mattes with one checkpoint and readback Co-authored-by: Yves --- .../upstream/vr-paired-hud-capture.md | 17 ++++++ runtime/include/gpu_gl_renderer.h | 2 +- runtime/include/mod_plugins.h | 22 ++++--- runtime/src/gpu_gl_renderer.c | 22 ++++--- runtime/src/render_pass.c | 55 ++++++++++++------ runtime/tests/test_render_pass_abort.c | 4 +- runtime/tests/test_render_pass_sandbox.c | 57 ++++++++++++++----- 7 files changed, 128 insertions(+), 51 deletions(-) create mode 100644 docs/internal/upstream/vr-paired-hud-capture.md diff --git a/docs/internal/upstream/vr-paired-hud-capture.md b/docs/internal/upstream/vr-paired-hud-capture.md new file mode 100644 index 00000000..143dc40a --- /dev/null +++ b/docs/internal/upstream/vr-paired-hud-capture.md @@ -0,0 +1,17 @@ +# Capture both HUD mattes with one checkpoint and readback + +Source: [FractalEngineer/psxrecomp](https://github.com/FractalEngineer/psxrecomp). +- [a876bae3](https://github.com/FractalEngineer/psxrecomp/commit/a876bae3385a8b972d8a5cf083ad5e30cf05c4bd) +- [d2554481](https://github.com/FractalEngineer/psxrecomp/commit/d2554481fac39409cebef7602e59bd155e000fe6) + +Original author: Yves ; retained as commit author +and Co-authored-by on this reconstructed port. + +Adapted: this feature and its tests. Excluded: other VR features, game policy, +assets and chronological investigation notes. +Base: `4419d234ab66c839da94dba597794ac47a09eb00` (current upstream master or the preceding feature). + +Validation: Windows/GCC 13.2 Release: sandbox and abort tests pass (2/2), including both matte buffers and capture-only checkpoint reuse. + +Consumer: [Medal of Honor v0.1.6](https://github.com/FractalEngineer/Medal-Of-Honor-PSX-Recomp/tree/1957e28a5dc7f5daa79e2b9b6ed867eb06b41ace). +Requires a framework pin bump and rebuild; no game/BIOS regeneration. diff --git a/runtime/include/gpu_gl_renderer.h b/runtime/include/gpu_gl_renderer.h index cca2306b..9b287802 100644 --- a/runtime/include/gpu_gl_renderer.h +++ b/runtime/include/gpu_gl_renderer.h @@ -136,7 +136,7 @@ uint32_t gl_renderer_stereo_unavailable(void); int gl_renderer_stereo_begin(int x, int y, int w, int h, int reuse_backup); int gl_renderer_stereo_end(uint32_t eye, int keep); void gl_renderer_stereo_stage_reset(void); -int gl_renderer_stereo_stage_readback(uint32_t eye,uint32_t width,uint32_t height,uint8_t *rgba); +int gl_renderer_stereo_stage_readback_pair(uint32_t width,uint32_t height,uint8_t *rgba); int gl_renderer_stereo_publish(uint64_t pair_id, uint64_t guest_cycle, const int32_t eye_view[2][3]); void gl_renderer_stereo_reset(void); diff --git a/runtime/include/mod_plugins.h b/runtime/include/mod_plugins.h index 07f87920..9ddd2c3c 100644 --- a/runtime/include/mod_plugins.h +++ b/runtime/include/mod_plugins.h @@ -651,14 +651,20 @@ uint32_t psx_mod_render_pass_plan(uint32_t period_vblanks, * partially updated plugin state) across guest code, or tolerate the abort. */ int psx_mod_render_pass(struct CPUState* cpu, const PSXModRenderPass* pass, PSXModRenderPassFn fn, void* user); -/* Isolated draw/readback using the stereo-capable rollback transaction. - * Never publishes an eye pair or changes presentation. Returns native-sized - * top-to-bottom RGBA8 pixels (alpha remains the PSX mask bit, not coverage). - * Call outside another pass. rgba_bytes must cover rect->w*rect->h*4. */ -int psx_mod_render_capture_rgba(struct CPUState *cpu,const PSXModRenderPass *rect, - PSXModRenderPassFn fn,void *user, - uint8_t *rgba,uint32_t rgba_bytes); -#define PSX_MOD_RENDER_CAPTURE_RGBA 1 +/* Isolated dual-matte draw/readback using the stereo-capable rollback + * transaction. Draws two mattes of the same slice, the first into eye 0 of the + * stage surface and the second into eye 1, so both planes of a frame cost one + * readback instead of two. Never publishes an eye pair or changes presentation. + * Returns native-sized top-to-bottom RGBA8 pixels (alpha remains the PSX mask + * bit, not coverage). Call outside another pass. rgba_bytes must cover + * 2*rect->w*rect->h*4; the first matte arrives in the buffer's first half and + * the second in its second half. */ +int psx_mod_render_capture_rgba_pair(struct CPUState *cpu, + const PSXModRenderPass *rect, + PSXModRenderPassFn fn,void *user_first, + void *user_second,uint8_t *rgba, + uint32_t rgba_bytes); +#define PSX_MOD_RENDER_CAPTURE_RGBA_PAIR 1 /* * Why passes cannot run right now, the host-time budget aside (a plan that * returns 0 while this says READY was shed for time). A plugin that relies on diff --git a/runtime/src/gpu_gl_renderer.c b/runtime/src/gpu_gl_renderer.c index ddecb0f6..56db186d 100644 --- a/runtime/src/gpu_gl_renderer.c +++ b/runtime/src/gpu_gl_renderer.c @@ -8807,27 +8807,33 @@ void gl_renderer_stereo_stage_reset(void) { GL_RT_SYNC("stereo_stage_reset"); s_stereo_pair[1 - s_stereo_current].mask = 0; } -int gl_renderer_stereo_stage_readback(uint32_t eye,uint32_t w,uint32_t h,uint8_t *rgba) { - GL_RT_SYNC("capture_readback"); - if(eye>=2 || !rgba || !w || !h || w>1024 || h>512)return 0; +/* Both staged eye images in one read. The blit target is double height, eye 0 + * into its lower half and eye 1 into its upper half, so one glReadPixels + * returns the two height-sized planes contiguously, eye 0 first. */ +int gl_renderer_stereo_stage_readback_pair(uint32_t w,uint32_t h,uint8_t *rgba) { + GL_RT_SYNC("capture_readback_pair"); + if(!rgba || !w || !h || w>1024 || h>512)return 0; StereoPair *pair=&s_stereo_pair[1-s_stereo_current]; - if(!(pair->mask&(1u<mask&3u)!=3u)return 0; GLint read,draw,texture,pack,pbo,row,skipr,skipp; GLboolean scissor=glIsEnabled(GL_SCISSOR_TEST),srgb=glIsEnabled(0x8DB9); glGetIntegerv(0x8CAA,&read);glGetIntegerv(0x8CA6,&draw);glGetIntegerv(GL_TEXTURE_BINDING_2D,&texture); glGetIntegerv(GL_PACK_ALIGNMENT,&pack);glGetIntegerv(0x88ED,&pbo);glGetIntegerv(GL_PACK_ROW_LENGTH,&row); glGetIntegerv(GL_PACK_SKIP_ROWS,&skipr);glGetIntegerv(GL_PACK_SKIP_PIXELS,&skipp); int ok=pass_make_color_fbo(&s_capture_read_tex,NULL,&s_capture_read_fbo,&s_capture_read_w,&s_capture_read_h, - w,h,GL_RGBA8,GL_RGBA,GL_UNSIGNED_BYTE); + w,h*2u,GL_RGBA8,GL_RGBA,GL_UNSIGNED_BYTE); if(ok) { glDisable(GL_SCISSOR_TEST);glDisable(0x8DB9); - p_glBindFramebuffer(PSXGL_READ_FRAMEBUFFER,pair->fbo[eye]); p_glBindFramebuffer(PSXGL_DRAW_FRAMEBUFFER,s_capture_read_fbo); - p_glBlitFramebuffer(0,0,pair->tw[eye],pair->th[eye],0,0,w,h,GL_COLOR_BUFFER_BIT,GL_NEAREST); + for(uint32_t eye=0;eye<2u;eye++) { + p_glBindFramebuffer(PSXGL_READ_FRAMEBUFFER,pair->fbo[eye]); + p_glBlitFramebuffer(0,0,pair->tw[eye],pair->th[eye],0,(GLint)(eye*h),w,(GLint)((eye+1u)*h), + GL_COLOR_BUFFER_BIT,GL_NEAREST); + } p_glBindFramebuffer(PSXGL_READ_FRAMEBUFFER,s_capture_read_fbo); p_glBindBuffer(0x88EB,0);glPixelStorei(GL_PACK_ALIGNMENT,1);glPixelStorei(GL_PACK_ROW_LENGTH,0); glPixelStorei(GL_PACK_SKIP_ROWS,0);glPixelStorei(GL_PACK_SKIP_PIXELS,0); - glReadPixels(0,0,w,h,GL_RGBA,GL_UNSIGNED_BYTE,rgba); + glReadPixels(0,0,w,h*2u,GL_RGBA,GL_UNSIGNED_BYTE,rgba); } p_glBindFramebuffer(PSXGL_READ_FRAMEBUFFER,read);p_glBindFramebuffer(PSXGL_DRAW_FRAMEBUFFER,draw); glBindTexture(GL_TEXTURE_2D,texture);glPixelStorei(GL_PACK_ALIGNMENT,pack); diff --git a/runtime/src/render_pass.c b/runtime/src/render_pass.c index 73862abc..0c1d44ed 100644 --- a/runtime/src/render_pass.c +++ b/runtime/src/render_pass.c @@ -308,6 +308,12 @@ static int s_shed_geom_valid; /* A HUD-only capture transaction reuses eye 0's slot but is not an eye draw; * frame tracing must not attribute its cost to the left eye. */ static int s_capture_only; +/* The two transactions of one HUD capture call are a pair of their own: the + * second matte draws from exactly the state the restore that ended the first + * one put back, so it may reuse the first one's checkpoint the way a pair's + * right eye reuses the left's. Scoped to the capture call, so the reuse never + * reaches the pair that renders later in the same frame. */ +static int s_capture_pair; static int pass_refuse(const char *reason, uint32_t status) { /* A refusal always carries a reason: last_failure.reason == NULL means @@ -900,10 +906,10 @@ static int render_transaction(struct CPUState *cpu, const PSXModRenderPass *pass s_open_generation = 0; s_leaks_before = gl_renderer_pass_leaks(); - /* A stereo eye whose machine state still equals the pair's captured - * baseline skips re-capturing it. */ - int reuse_baseline = eye >= 0 && !s_capture_only && - s_baseline_ready && baseline_reuse_enabled(); + /* A stereo eye, or the second matte of a capture, whose machine state still + * equals the baseline captured for it skips re-capturing that state. */ + int reuse_baseline = eye >= 0 && s_baseline_ready && baseline_reuse_enabled() && + (!s_capture_only || s_capture_pair); if (!checkpoint_save(cpu, reuse_baseline)) { gl_renderer_pass_begin_diag(&s_attempt.gl); s_stats.checkpoint_refused++; @@ -1039,10 +1045,12 @@ static int render_transaction(struct CPUState *cpu, const PSXModRenderPass *pass } } /* The restore just put the live machine back to the captured baseline, so - * the other eye of this pair may reuse it. Anything else - a non-stereo - * transaction, a HUD-only capture, a failed eye or an unverified restore - - * leaves nothing safe to reuse. */ - s_baseline_ready = (eye >= 0 && !s_capture_only && ok && verify_ok) ? 1 : 0; + * the other eye of this pair, or the other matte of this capture, may reuse + * it. Anything else - a non-stereo transaction, a capture that is not part + * of a matte pair, a failed eye or an unverified restore - leaves nothing + * safe to reuse. */ + s_baseline_ready = (eye >= 0 && ok && verify_ok && + (!s_capture_only || s_capture_pair)) ? 1 : 0; t1 = gl_renderer_perf_ticks(); { @@ -1272,24 +1280,37 @@ void psx_mod_stereo_set_budget_ms(double ms) { s_shed_xr_budget_ms = (ms > 0.0 && ms < 1000.0) ? ms : 0.0; } -int psx_mod_render_capture_rgba(CPUState *cpu,const PSXModRenderPass *rect, - PSXModRenderPassFn fn,void *user, - uint8_t *rgba,uint32_t bytes) { - if(s_pair_busy || !cpu || !rect || !fn || !rgba || rect->struct_sizestruct_sizew || !rect->h || rect->x+rect->w>1024 || rect->y+rect->h>512 || - (uint64_t)rect->w*rect->h*4>bytes || transaction_status(1)!=PSX_MOD_RENDER_PASS_READY)return 0; + (uint64_t)rect->w*rect->h*8>bytes || transaction_status(1)!=PSX_MOD_RENDER_PASS_READY)return 0; if(g_overlay_flush_pending_cycles)g_overlay_flush_pending_cycles(); uint64_t trace_t0=gl_renderer_perf_ticks(); s_pair_busy=1;s_plan_serial++; gl_renderer_stereo_stage_reset(); PSXModRenderPass capture=*rect;capture.alpha_q16=0; - s_capture_only=1; /* this eye-0 transaction is a HUD capture, not an eye draw */ - int ok=render_transaction(cpu,&capture,fn,user,0); - s_capture_only=0; + /* Start uncaptured: the ready flag carries the pair's baseline from the + * previous frame, which describes a machine the guest has since changed. */ + s_capture_only=1;s_capture_pair=1;s_baseline_ready=0; + uint64_t cycle=psx_cycle_count,stores=g_guest_store_count; + int ok=render_transaction(cpu,&capture,fn,user_first,0); + /* The second matte must draw from the state the first one's restore put + * back. If the clock or the store count moved, that baseline no longer + * describes it, so capture the machine again instead of reusing it. */ + if(psx_cycle_count!=cycle || g_guest_store_count!=stores)s_baseline_ready=0; + if(ok)ok=render_transaction(cpu,&capture,fn,user_second,1); + s_capture_only=0;s_capture_pair=0;s_baseline_ready=0; uint64_t trace_t1=gl_renderer_perf_ticks(); psx_frame_trace_add(PSX_TRACE_HUD_DRAW, trace_ticks_to_us(trace_t1-trace_t0)); - if(ok)ok=gl_renderer_stereo_stage_readback(0,rect->w,rect->h,rgba); + if(ok)ok=gl_renderer_stereo_stage_readback_pair(rect->w,rect->h,rgba); psx_frame_trace_add(PSX_TRACE_HUD_READBACK, trace_ticks_to_us(gl_renderer_perf_ticks()-trace_t1)); gl_renderer_stereo_stage_reset();s_pair_busy=0; diff --git a/runtime/tests/test_render_pass_abort.c b/runtime/tests/test_render_pass_abort.c index d623e8c3..f161d86f 100644 --- a/runtime/tests/test_render_pass_abort.c +++ b/runtime/tests/test_render_pass_abort.c @@ -215,8 +215,8 @@ int gl_renderer_stereo_end(uint32_t eye, int keep) { return 1; } void gl_renderer_stereo_stage_reset(void) { s_stereo_mask = 0; } -int gl_renderer_stereo_stage_readback(uint32_t eye,uint32_t w,uint32_t h,uint8_t *rgba) { - (void)eye;(void)w;(void)h;(void)rgba;return 0; +int gl_renderer_stereo_stage_readback_pair(uint32_t w,uint32_t h,uint8_t *rgba) { + (void)w;(void)h;(void)rgba;return 0; } void gl_renderer_stereo_reset(void) { s_stereo_mask = 0; s_stereo_published = 0; } int gl_renderer_stereo_set_presentation(uint32_t mode) { return mode <= 1; } diff --git a/runtime/tests/test_render_pass_sandbox.c b/runtime/tests/test_render_pass_sandbox.c index f80d7c6f..d54332d4 100644 --- a/runtime/tests/test_render_pass_sandbox.c +++ b/runtime/tests/test_render_pass_sandbox.c @@ -285,9 +285,9 @@ int gl_renderer_stereo_end(uint32_t eye, int keep) { return 1; } void gl_renderer_stereo_stage_reset(void) { s_stereo_mask = 0; } -int gl_renderer_stereo_stage_readback(uint32_t eye,uint32_t w,uint32_t h,uint8_t *rgba) { - if(!(s_stereo_mask & (1u<gpr[8]=0x1234;s_ram[0x456]=0x42;psx_advance_cycles(5000); return 1; } static void test_isolated_capture(void) { CPUState cpu={0};PSXModRenderPass rect={sizeof rect,0,0,0,2,2}; - uint8_t pixels[16]={0}; + int first=1,second=2; + uint8_t pixels[32]={0}; render_pass_reset_session();s_gl_status=0;s_begin_ok=1;s_netplay=0; cpu.gpr[8]=123;s_ram[0x456]=0x77; uint64_t cycle=psx_cycle_count,published=s_stereo_published; - CHECK(psx_mod_render_capture_rgba(&cpu,&rect,isolated_draw,NULL,pixels,sizeof pixels), - "isolated capture succeeds without publishing a stereo pair"); + s_dual_draws=0; + CHECK(psx_mod_render_capture_rgba_pair(&cpu,&rect,isolated_draw,&first,&second, + pixels,sizeof pixels), + "isolated dual capture succeeds without publishing a stereo pair"); + CHECK(s_dual_draws==2 && s_dual_users[0]==&first && s_dual_users[1]==&second, + "one transaction per matte, each with its own user"); CHECK(cpu.gpr[8]==123 && s_ram[0x456]==0x77 && psx_cycle_count==cycle, "isolated capture restores CPU, RAM and the guest clock"); - CHECK(s_stereo_published==published && s_stereo_mask==0 && pixels[0]==0x5A, - "isolated pixels copied and temporary staging discarded"); - CHECK(!psx_mod_render_capture_rgba(&cpu,&rect,isolated_draw,NULL,pixels,sizeof pixels-1), + CHECK(s_stereo_published==published && s_stereo_mask==0 && + pixels[0]==0x5A && pixels[31]==0x5A, + "both isolated planes copied and temporary staging discarded"); + /* The two mattes draw from one machine state, so the second one reuses the + * checkpoint the first one captured, the way a pair's right eye does. */ + RenderStereoStats stats; + render_stereo_get_stats(&stats); + CHECK(stats.checkpoint_saves==1 && stats.checkpoint_reuses==1, + "the two mattes of one capture share one checkpoint"); + s_dual_draws=0; + CHECK(!psx_mod_render_capture_rgba_pair(&cpu,&rect,isolated_draw,&first,&second, + pixels,sizeof pixels-1), "undersized pixel buffer refused"); + CHECK(s_dual_draws==0 && !psx_mod_render_capture_rgba_pair(&cpu,&rect,isolated_draw, + &first,NULL,pixels,sizeof pixels), + "a missing second matte is refused before any draw"); + /* The capture's shared checkpoint is scoped to the capture: the pair that + * renders later in the same frame must still capture its first eye. */ + PSXModStereoFrame f={sizeof f,2,0,240,320,240}; + render_pass_reset_session();s_gl_status=0;s_begin_ok=1;s_eye_decline=-1;s_eye_calls=0; + CHECK(psx_mod_render_stereo(&cpu,&f,pair_draw,NULL)==1, + "a pair still renders after a capture"); + render_stereo_get_stats(&stats); + CHECK(stats.checkpoint_saves==1 && stats.checkpoint_reuses==1, + "the pair after a capture captures its own baseline"); } int main(void) { test_store_policy(); From 18d52c8bfbe5afc380292ef3116af2920225a92e Mon Sep 17 00:00:00 2001 From: Yves Date: Fri, 23 Oct 2026 03:26:53 +0800 Subject: [PATCH 17/29] Add debug synthetic head translation and pitch Co-authored-by: Yves --- docs/TCP_COMMANDS.md | 5 ++- .../upstream/vr-synthetic-head-pose.md | 17 +++++++++ runtime/src/debug_server.c | 22 ++++++++++- runtime/src/psx_openxr.c | 38 +++++++++++++++++++ runtime/tests/test_openxr_input.c | 24 ++++++++++++ 5 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 docs/internal/upstream/vr-synthetic-head-pose.md diff --git a/docs/TCP_COMMANDS.md b/docs/TCP_COMMANDS.md index bece7d73..f2be50f7 100644 --- a/docs/TCP_COMMANDS.md +++ b/docs/TCP_COMMANDS.md @@ -493,9 +493,9 @@ between Play and TCP availability. ## Complete command index (generated) -**384 commands registered** — 371 on the native server (`runtime/src/debug_server.c`), 61 on the Beetle server (`runtime/src/beetle_debug_server.c`). +**385 commands registered** — 372 on the native server (`runtime/src/debug_server.c`), 61 on the Beetle server (`runtime/src/beetle_debug_server.c`). -85 of 384 have prose above; **299 are index-only**. An index-only command still works — it just has no description here yet. Send it `{"cmd":""}` and read the reply, or find its `handle_*` function in the server source. +85 of 385 have prose above; **300 are index-only**. An index-only command still works — it just has no description here yet. Send it `{"cmd":""}` and read the reply, or find its `handle_*` function in the server source. Regenerate with `python tools/gen_tcp_commands.py`; `--check` fails if this block has drifted from the code. @@ -705,6 +705,7 @@ Regenerate with `python tools/gen_tcp_commands.py`; `--check` fails if this bloc | `openxr_hands_override` | ✓ | | | | `openxr_input` | ✓ | | | | `openxr_input_override` | ✓ | | | +| `openxr_pose_override` | ✓ | | | | `openxr_stats` | ✓ | | | | `openxr_views` | ✓ | | | | `overlay_candidates` | ✓ | | | diff --git a/docs/internal/upstream/vr-synthetic-head-pose.md b/docs/internal/upstream/vr-synthetic-head-pose.md new file mode 100644 index 00000000..d4ea41db --- /dev/null +++ b/docs/internal/upstream/vr-synthetic-head-pose.md @@ -0,0 +1,17 @@ +# Add debug synthetic head translation and pitch + +Source: [FractalEngineer/psxrecomp](https://github.com/FractalEngineer/psxrecomp). +- [2ed12640](https://github.com/FractalEngineer/psxrecomp/commit/2ed12640adab3602acbf3307d42e9c2739b7d0eb) +- [a3714fad](https://github.com/FractalEngineer/psxrecomp/commit/a3714fada4d0582500001b0c22e4dfa00840403e) + +Original author: Yves ; retained as commit author +and Co-authored-by on this reconstructed port. + +Adapted: this feature and its tests. Excluded: other VR features, game policy, +assets and chronological investigation notes. +Base: `e4f1f985428101eb6c8f83fce834e281860b1fd2` (current upstream master or the preceding feature). + +Validation: Windows/GCC 13.2 Release: XR-off input fixture passes, including finite/range rejection, binocular translation/pitch, disabled state and clearing; command index is current. + +Consumer: [Medal of Honor v0.1.6](https://github.com/FractalEngineer/Medal-Of-Honor-PSX-Recomp/tree/1957e28a5dc7f5daa79e2b9b6ed867eb06b41ace). +Requires a framework pin bump and rebuild; no game/BIOS regeneration. diff --git a/runtime/src/debug_server.c b/runtime/src/debug_server.c index bca89a24..43ac2544 100644 --- a/runtime/src/debug_server.c +++ b/runtime/src/debug_server.c @@ -9218,6 +9218,25 @@ static void handle_openxr_hands_override(int id,const char *json) { /* Debug-only synthetic action sample; axes are signed thousandths [-1000,1000]. * Explicitly tagged so desktop controls cannot be mistaken for device evidence. */ #ifndef PSX_NO_DEBUG_TOOLS +/* openxr_pose_override: test-only synthetic head motion in millimetres, with an optional pitch_deg + * look angle, for headless head-motion checks. clear=1 disarms. Each call is absolute. The pitch is + * what reaches the defects that only appear when looking down, which no translation can show. */ +static void handle_openxr_pose_override(int id,const char *json) { + extern int psx_openxr_pose_override(double,double,double); + extern int psx_openxr_pose_pitch_override(double); + extern void psx_openxr_pose_clear(void); + if(json_get_int(json,"clear",0)){ psx_openxr_pose_clear(); send_fmt("{\"id\":%d,\"ok\":true,\"cleared\":1}",id); return; } + int x=json_get_int(json,"x_mm",0),y=json_get_int(json,"y_mm",0),z=json_get_int(json,"z_mm",0); + int pitch_deg=json_get_int(json,"pitch_deg",0); + if(!psx_openxr_pose_override((double)x/1000.0,(double)y/1000.0,(double)z/1000.0)){ + send_err(id,"pose offset must be finite and within +/-10 m");return; + } + if(!psx_openxr_pose_pitch_override((double)pitch_deg)){ + send_err(id,"pitch_deg must be finite and within +/-90");return; + } + send_fmt("{\"id\":%d,\"ok\":true,\"x_mm\":%d,\"y_mm\":%d,\"z_mm\":%d,\"pitch_deg\":%d}",id,x,y,z,pitch_deg); +} + static void handle_openxr_input_override(int id,const char *json) { if(json_get_int(json,"clear",0)) psx_openxr_input_override(NULL); else { @@ -16294,7 +16313,8 @@ static const CmdEntry s_commands[] = { #endif { "openxr_hands", handle_openxr_hands }, #ifndef PSX_NO_DEBUG_TOOLS - { "openxr_hands_override", handle_openxr_hands_override }, + { "openxr_hands_override", handle_openxr_hands_override }, + { "openxr_pose_override", handle_openxr_pose_override }, #endif { "stereo_stats", handle_stereo_stats }, { "frame_trace", handle_frame_trace }, diff --git a/runtime/src/psx_openxr.c b/runtime/src/psx_openxr.c index 3404dedf..ada0af65 100644 --- a/runtime/src/psx_openxr.c +++ b/runtime/src/psx_openxr.c @@ -43,6 +43,41 @@ static int valid_pose_numbers(const double q[4],const double p[3]) { for(int i=0;i<3;i++)if(!isfinite(p[i]) || fabs(p[i])>100)return 0; return n>=.5 && n<=1.5; } +/* Test-only synthetic head motion: translation in room-space metres and an optional pitch, added to + * both eyes after the origin is established so it reads as the head moving in the world. The + * translation checks that the world shifts rigidly with the head. The pitch exists because the + * defects that only appear when looking down cannot be reached by translation, and because the only + * other look path is unwired. Armed by the first override call; default off, so shipped behaviour + * is unchanged. */ +static int s_pose_bob_armed; +static double s_pose_bob_m[3]; +static double s_pose_bob_pitch_deg; +void psx_openxr_pose_bob(double q[2][4],double p[2][3]) { + if(!s_pose_bob_armed)return; + for(int i=0;i<2;i++)for(int j=0;j<3;j++)p[i][j]+=s_pose_bob_m[j]; + if(s_pose_bob_pitch_deg!=0.0) { + double h=s_pose_bob_pitch_deg*(3.141592653589793/180.0)*0.5,sq=sin(h),cq=cos(h); + for(int i=0;i<2;i++) { + double x=q[i][0],y=q[i][1],z=q[i][2],w=q[i][3]; + /* Left multiply by a rotation about the XR X axis: a look up or down. */ + q[i][0]=sq*w+cq*x;q[i][1]=cq*y-sq*z;q[i][2]=sq*y+cq*z;q[i][3]=cq*w-sq*x; + } + } +} +int psx_openxr_pose_override(double x_m,double y_m,double z_m) { + if(!isfinite(x_m)||!isfinite(y_m)||!isfinite(z_m))return 0; + if(fabs(x_m)>10.0||fabs(y_m)>10.0||fabs(z_m)>10.0)return 0; + s_pose_bob_m[0]=x_m;s_pose_bob_m[1]=y_m;s_pose_bob_m[2]=z_m;s_pose_bob_armed=1; + return 1; +} +int psx_openxr_pose_pitch_override(double pitch_deg) { + if(!isfinite(pitch_deg) || fabs(pitch_deg)>90.0)return 0; + s_pose_bob_pitch_deg=pitch_deg;s_pose_bob_armed=1; + return 1; +} +void psx_openxr_pose_clear(void) { + s_pose_bob_m[0]=s_pose_bob_m[1]=s_pose_bob_m[2]=0.0;s_pose_bob_pitch_deg=0.0;s_pose_bob_armed=0; +} int psx_openxr_hands_override(const PSXModOpenXRHands *hands) { if(!hands){s_hands_override=0;hands_clear();return 1;} if(hands->struct_size!=sizeof *hands || hands->focused>1 || hands->origin_valid>1)return 0; @@ -643,6 +678,9 @@ int psx_openxr_begin(int width,int height,double units) { double separation=0; for(int j=0;j<3;j++)separation+=(p[1][j]-p[0][j])*(p[1][j]-p[0][j]); s_stats.ipd_m=(float)sqrt(separation); +#ifndef PSX_NO_DEBUG_TOOLS + psx_openxr_pose_bob(q, p); /* test-only synthetic head motion; no-op unless armed */ +#endif for(int i=0;i<2;i++) { XrFovf *v=&s_views[i].fov; double f[4]={v->angleLeft,v->angleRight,v->angleUp,v->angleDown}; diff --git a/runtime/tests/test_openxr_input.c b/runtime/tests/test_openxr_input.c index d9b7254a..416044e1 100644 --- a/runtime/tests/test_openxr_input.c +++ b/runtime/tests/test_openxr_input.c @@ -2,6 +2,10 @@ #include #include #include "xr_surface_math.h" +extern void psx_openxr_pose_bob(double q[2][4], double p[2][3]); +extern int psx_openxr_pose_override(double, double, double); +extern int psx_openxr_pose_pitch_override(double); +extern void psx_openxr_pose_clear(void); /* Evaluate side effects and failures even when NDEBUG is defined. */ #define CHECK(expr) do { \ if (!(expr)) { \ @@ -10,6 +14,26 @@ } \ } while (0) int main(void) { + /* Synthetic controls are absolute, bounded and inert after clearing. */ + double bob_q[2][4]={{0,0,0,1},{0,0,0,1}}; + double bob_p[2][3]={{-.03,0,0},{.03,0,0}}; + psx_openxr_pose_clear(); + CHECK(!psx_openxr_pose_override(NAN,0,0)); + CHECK(!psx_openxr_pose_override(11,0,0)); + CHECK(!psx_openxr_pose_pitch_override(INFINITY)); + CHECK(!psx_openxr_pose_pitch_override(91)); + psx_openxr_pose_bob(bob_q,bob_p); + CHECK(bob_p[0][0]==-.03 && bob_q[0][3]==1); + CHECK(psx_openxr_pose_override(.1,.2,-.3)); + CHECK(psx_openxr_pose_pitch_override(30)); + psx_openxr_pose_bob(bob_q,bob_p); + CHECK(fabs(bob_p[0][0]-.07)<1e-12 && fabs(bob_p[1][0]-.13)<1e-12); + CHECK(bob_p[0][1]==.2 && bob_p[1][2]==-.3); + CHECK(fabs(bob_q[0][0]-sin(3.141592653589793/12))<1e-12); + CHECK(bob_q[0][0]==bob_q[1][0] && bob_q[0][3]==bob_q[1][3]); + psx_openxr_pose_clear(); + psx_openxr_pose_bob(bob_q,bob_p); + CHECK(fabs(bob_p[0][0]-.07)<1e-12); /* Shared head center gives coherent binocular geometry. Billboarding must * ignore attachment roll, respond to head motion, and reject near-plane * crossings. Common translation of eye/head/surface preserves projection. */ From 4f40f6f3c7b58c24a54f71a635e2a094b81b9c86 Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Thu, 22 Oct 2026 14:03:16 -0700 Subject: [PATCH 18/29] Publish frame trace records only when committed --- runtime/src/frame_trace.c | 7 +++++-- runtime/tests/test_frame_trace.c | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/runtime/src/frame_trace.c b/runtime/src/frame_trace.c index 7dba7663..620287cc 100644 --- a/runtime/src/frame_trace.c +++ b/runtime/src/frame_trace.c @@ -18,6 +18,8 @@ #define PSX_FRAME_TRACE_DEFAULT 2048 static PSXFrameTraceRecord s_ring[PSX_FRAME_TRACE_MAX]; +/* An in-progress frame never overwrites the retained committed window. */ +static PSXFrameTraceRecord s_pending; static int s_configured; static int s_enabled; @@ -129,7 +131,7 @@ void psx_frame_trace_reset(void) { } static PSXFrameTraceRecord *open_record(void) { - return &s_ring[(s_opened - 1u) & (uint64_t)(s_cap - 1u)]; + return &s_pending; } static uint64_t stamp_now(void) { @@ -145,7 +147,7 @@ int psx_frame_trace_open(void) { ensure_configured(); if (!s_enabled || s_have_open) return 0; uint64_t seq = s_opened + 1u; - PSXFrameTraceRecord *r = &s_ring[(seq - 1u) & (uint64_t)(s_cap - 1u)]; + PSXFrameTraceRecord *r = &s_pending; memset(r, 0, sizeof *r); r->seq = seq; r->eye_failed = -1; @@ -283,6 +285,7 @@ void psx_frame_trace_commit(void) { } else if (s_have_publish) { r->pair_age_ms = r->open_ms - s_last_publish_ms; } + s_ring[(r->seq - 1u) & (uint64_t)(s_cap - 1u)] = *r; s_committed++; s_have_open = 0; } diff --git a/runtime/tests/test_frame_trace.c b/runtime/tests/test_frame_trace.c index 3f887fe0..821b46be 100644 --- a/runtime/tests/test_frame_trace.c +++ b/runtime/tests/test_frame_trace.c @@ -194,6 +194,21 @@ int main(void) { CHECK(one[0].seq == extra); } + /* Opening the next frame must not retire a committed slot before commit. */ + { + static PSXFrameTraceRecord retained[4096]; + uint32_t capacity = psx_frame_trace_capacity(); + uint64_t committed = psx_frame_trace_committed(); + CHECK(psx_frame_trace_open() == 1); + psx_frame_trace_note("not committed"); + CHECK(psx_frame_trace_read(retained, capacity) == capacity); + CHECK(retained[0].seq == committed - capacity + 1); + CHECK(retained[capacity - 1].seq == committed); + psx_frame_trace_commit(); + CHECK(psx_frame_trace_read(retained, capacity) == capacity); + CHECK(retained[capacity - 1].seq == committed + 1); + } + /* ---- exports: valid, NULL-terminated, truncation-safe ---- */ { static char big[1 << 20]; From 6c9d618f1f2c081ba3e0591a1e9c3f42747e6365 Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Thu, 22 Oct 2026 14:07:34 -0700 Subject: [PATCH 19/29] Validate synthetic pose requests before applying them --- runtime/src/debug_server.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/runtime/src/debug_server.c b/runtime/src/debug_server.c index 43ac2544..11d4bd67 100644 --- a/runtime/src/debug_server.c +++ b/runtime/src/debug_server.c @@ -9227,14 +9227,20 @@ static void handle_openxr_pose_override(int id,const char *json) { extern void psx_openxr_pose_clear(void); if(json_get_int(json,"clear",0)){ psx_openxr_pose_clear(); send_fmt("{\"id\":%d,\"ok\":true,\"cleared\":1}",id); return; } int x=json_get_int(json,"x_mm",0),y=json_get_int(json,"y_mm",0),z=json_get_int(json,"z_mm",0); - int pitch_deg=json_get_int(json,"pitch_deg",0); + double pitch_deg=(double)json_get_int(json,"pitch_deg",0) + + (double)json_get_int(json,"pitch_mdeg",0)/1000.0; + /* Validate the complete absolute request before changing either override. */ + if(x < -10000 || x > 10000 || y < -10000 || y > 10000 || + z < -10000 || z > 10000 || pitch_deg < -90.0 || pitch_deg > 90.0) { + send_err(id,"pose must be within +/-10 m and pitch within +/-90 degrees");return; + } if(!psx_openxr_pose_override((double)x/1000.0,(double)y/1000.0,(double)z/1000.0)){ send_err(id,"pose offset must be finite and within +/-10 m");return; } if(!psx_openxr_pose_pitch_override((double)pitch_deg)){ send_err(id,"pitch_deg must be finite and within +/-90");return; } - send_fmt("{\"id\":%d,\"ok\":true,\"x_mm\":%d,\"y_mm\":%d,\"z_mm\":%d,\"pitch_deg\":%d}",id,x,y,z,pitch_deg); + send_fmt("{\"id\":%d,\"ok\":true,\"x_mm\":%d,\"y_mm\":%d,\"z_mm\":%d,\"pitch_deg\":%.6f}",id,x,y,z,pitch_deg); } static void handle_openxr_input_override(int id,const char *json) { From 89161e93f36d169e1a1c61d6d7dfdd329a283199 Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Thu, 22 Oct 2026 14:08:27 -0700 Subject: [PATCH 20/29] Keep truncated trace JSON string escapes complete --- runtime/src/frame_trace.c | 11 ++++++----- runtime/tests/test_frame_trace.c | 13 +++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/runtime/src/frame_trace.c b/runtime/src/frame_trace.c index 620287cc..4567fbff 100644 --- a/runtime/src/frame_trace.c +++ b/runtime/src/frame_trace.c @@ -334,14 +334,15 @@ static const char *jstr(const char *s, char *tmp, int cap) { int o = 0; if (cap <= 0) return ""; tmp[0] = '\0'; - for (const char *p = s; p && *p && o < cap - 2; ++p) { - unsigned char c = (unsigned char)*p; + for (const unsigned char *p = (const unsigned char *)s; p && *p; ++p) { + const unsigned char c = *p; + const int need = c == '"' || c == '\\' || c == '\n' ? 2 : c < 0x20 ? 6 : 1; + if (need > cap - 1 - o) break; if (c == '"' || c == '\\') { tmp[o++] = '\\'; tmp[o++] = (char)c; } - else if (c == '\n') { tmp[o++] = '\\'; if (o < cap - 1) tmp[o++] = 'n'; } - else if (c < 0x20) { o += snprintf(tmp + o, (size_t)(cap - o), "\\u%04x", c); } + else if (c == '\n') { tmp[o++] = '\\'; tmp[o++] = 'n'; } + else if (c < 0x20) o += snprintf(tmp + o, (size_t)(cap - o), "\\u%04x", c); else tmp[o++] = (char)c; } - if (o > cap - 1) o = cap - 1; tmp[o] = '\0'; return tmp; } diff --git a/runtime/tests/test_frame_trace.c b/runtime/tests/test_frame_trace.c index 821b46be..8650980c 100644 --- a/runtime/tests/test_frame_trace.c +++ b/runtime/tests/test_frame_trace.c @@ -254,6 +254,19 @@ int main(void) { CHECK(strstr(meta, "\"target_period_us\":0") != NULL); } + /* Fixed scratch buffers may shorten a string, never split a JSON escape. */ + { + char controls[128]; static char json[1 << 16]; + memset(controls, 1, sizeof controls - 1); controls[127] = 0; + psx_frame_trace_session(controls, 0); + CHECK(psx_frame_trace_to_json(json, sizeof json, 1) > 0); + const char *value = strstr(json, "\"runtime\":\""); + CHECK(value != NULL); value += strlen("\"runtime\":\""); + const char *end = strchr(value, '\"'); + CHECK(end && (end - value) % 6 == 0); + for (; value < end; value += 6) CHECK(!memcmp(value, "\\u0001", 6)); + } + /* ---- XR cadence: the runtime's display-time delta and repeat marking ---- * One presented pair per granted display period is the pacing target, so a * frame that carried no new pair must be distinguishable from one that did From ea3b1e32047fae2e6ba2bc24e71efd214af98a2a Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Thu, 22 Oct 2026 14:11:20 -0700 Subject: [PATCH 21/29] Document the paired capture pixel and context contract --- runtime/include/mod_plugins.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/include/mod_plugins.h b/runtime/include/mod_plugins.h index 9ddd2c3c..c5be3c59 100644 --- a/runtime/include/mod_plugins.h +++ b/runtime/include/mod_plugins.h @@ -655,7 +655,7 @@ int psx_mod_render_pass(struct CPUState* cpu, const PSXModRenderPass* pass, * transaction. Draws two mattes of the same slice, the first into eye 0 of the * stage surface and the second into eye 1, so both planes of a frame cost one * readback instead of two. Never publishes an eye pair or changes presentation. - * Returns native-sized top-to-bottom RGBA8 pixels (alpha remains the PSX mask + * Returns native-sized bottom-to-top RGBA8 pixels (alpha remains the PSX mask * bit, not coverage). Call outside another pass. rgba_bytes must cover * 2*rect->w*rect->h*4; the first matte arrives in the buffer's first half and * the second in its second half. */ From e3705227b56721112c35106780878b41cea2daa0 Mon Sep 17 00:00:00 2001 From: Alexandros Mandravillis Date: Fri, 23 Oct 2026 00:23:14 +0300 Subject: [PATCH 22/29] Document runtime provenance and hardware references (#731) --- CONTRIBUTING.md | 5 +++ docs/PROVENANCE.md | 76 ++++++++++++++++++++++++++++++++++ runtime/include/psx_icache.h | 14 +++---- runtime/src/cdrom.c | 16 +++---- runtime/src/dirty_ram_interp.c | 5 ++- runtime/src/dma.c | 2 +- runtime/src/gpu.c | 28 ++++++++----- runtime/src/gte.cpp | 16 +++---- runtime/src/interrupts.c | 17 ++++---- runtime/src/memcard.c | 4 +- runtime/src/memory.c | 10 ++--- runtime/src/psx_icache.c | 28 ++++++------- runtime/src/sio.c | 17 ++++---- 13 files changed, 166 insertions(+), 72 deletions(-) create mode 100644 docs/PROVENANCE.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 33ffc99e..9240f5b0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -273,6 +273,11 @@ By contributing, you agree your contribution is licensed under the same license the project. Do not submit code you don't have the right to contribute, and do not copy from incompatible licenses or proprietary sources. +Runtime comments cite hardware facts to PSX-SPX, and emulators are used only as +black-box references whose outputs we compare. See +[`docs/PROVENANCE.md`](docs/PROVENANCE.md) for the rules and for the passages +still to be replaced. + ### Upstream-derived work and credit When a change adapts code or behavior from another branch or repository, add a diff --git a/docs/PROVENANCE.md b/docs/PROVENANCE.md new file mode 100644 index 00000000..4b0bac5d --- /dev/null +++ b/docs/PROVENANCE.md @@ -0,0 +1,76 @@ +# Where runtime code and its facts come from + +This note is for anyone who writes or reviews runtime code (`runtime/src`, +`runtime/include`). It explains which outside sources we use and how, and lists +the places that still need work. Issue #443 tracks the series of changes that +removes the remaining passages. + +## The rules + +1. **Hardware facts are cited to documentation.** When a comment states how the + PlayStation behaves (a register bit, a table, a timing, a quirk), it names + PSX-SPX and the page and section where the fact is written. PSX-SPX is + published at . A page is cited by its name + in the address, for example `graphicsprocessingunitgpu` for + , followed by + the section heading in quotes. +2. **Emulators are black-box references.** We run Beetle PSX, DuckStation, + Mednafen or PCSX-Redux and compare what they output with what we output. + That is how the oracle tools (`psx-beetle`, the trace and diff commands) are + used, and comments may say "the Beetle reference gives the same result". +3. **We do not copy from emulator source.** Do not copy code, tables, constants, + identifiers or the structure of a routine from an emulator's source, and do + not quote emulator source in a comment. Their licences (GPL for Beetle, + Mednafen and PCSX-Redux, CC BY-NC-ND for DuckStation) differ from this + project's, and [`CONTRIBUTING.md`](../CONTRIBUTING.md) already rules out + copying from incompatible licences. +4. **When documentation is silent, measure.** If PSX-SPX does not state a fact, + write a small test program, run it on hardware or on the references, and + record what it shows. Say in the comment that the value was measured and + where the measurement lives. +5. **When PSX-SPX and the code disagree, say so.** Write both in the comment and + leave the behaviour as it is. Changing it is a separate change with its own + evidence. + +## What the first change of the series did + +It changed comments and documentation only; no code changed. + +- Comments that named an emulator's source file, function or line as the + authority for a hardware fact now cite PSX-SPX, where PSX-SPX states the + fact. Each new citation was checked against the PSX-SPX text. +- Two comments quoted emulator source: the I-cache store rule in + `psx_icache.c` and the interrupt line rule in `interrupts.c`. The quotes are + removed. The comments now describe the behaviour and cite PSX-SPX. +- Where PSX-SPX says something different from the code, the comment now says + so (GP0 commands 03h and FFh in `gpu.c`, the I-cache tag value in + `psx_icache.c`). + +## What is still to do + +These passages say that code follows an emulator's source, or they name a +source file, function or line. Rewording would hide that, so they stay as they +are until the code itself is replaced. The order is the one agreed on #443. + +| Change | Files | What the comments say today | +|---|---|---| +| 2. spu | `spu.c`, `spu_gauss.h` | The envelope rate decoder is "ported verbatim" from Beetle's `CalcVCDelta`; envelope steps follow `RunEnvelope`; several Beetle `spu.cpp` line references; the Gaussian table names DuckStation's `spu.cpp`. | +| 3. cpu/icache and memory/cycles | `psx_icache.c`, `psx_icache.h`, `psx_instr_cost.h`, `psx_cyc.h`, `psx_cycles.c`, `memory.c`, `cpu_state.h`, `dirty_ram_interp.c`, `main.cpp`, `docs/internal/CYCLE_MODEL_BEETLE.md` | The fetch, load-timing and I-cache models are "transcribed" from Beetle `cpu.cpp`; MULT/DIV and GTE latencies are transcribed from Beetle; `ReadAbsorb`/`LDWhich` names; the MTC0 software-interrupt check is labelled with a PCSX-Redux function name. | +| 4. mdec | `mdec.c` | The zig-zag table, IDCT matrix load and passes, dequantisation and RGB packing follow Beetle `mdec.cpp` with line references. | +| 5. cdrom | `cdrom.c`, `cdrom.h` | Command latencies (ID, ReadTOC, Pause, Init) and the data-ready notification follow Beetle `PS_CDC` functions; DuckStation `cdrom.cpp` handlers are cited for command cancellation and read restarts. | + +Passages found in areas the agreed series does not cover yet: + +| Area | Files | What the comments say today | +|---|---|---| +| GPU rasteriser | `gpu.c` | The triangle edge stepping cites DuckStation `gpu_sw_rasterizer.inl` (`makefp_xy`, `makestep_xy`). | +| GPU display crop | `gpu.c` | The vertical crop follows DuckStation `GetFullDisplayResolution`. | +| Texture sampling | `gpu_uv.h`, `gpu_sw_renderer.c`, `gpu_gl_renderer.c` | The UV offset model and bilinear filter are described as the Beetle / parallel-psx formulation (`Calc_UVOffsets_Adjust_Verts`). | +| SPU DMA cost | `dma.c` | The 48-cycle per-word cost cites Beetle `dma.cpp` lines. | +| PGXP cache | `gte.cpp` | The vertex cache layout names Beetle `pgxp_gpu.c` and DuckStation `cpu_pgxp.cpp`. | + +Comparisons with the references (what the oracle showed, which emulator agrees), +the oracle tools themselves, and the DuckStation texture-pack file format +(`duckstation_texture_pack.*`, `docs/DUCKSTATION_TEXTURE_FORMAT.md`) are not on +these lists: they compare outputs or read a published file format, which the +rules allow. diff --git a/runtime/include/psx_icache.h b/runtime/include/psx_icache.h index e45b9381..96692f95 100644 --- a/runtime/include/psx_icache.h +++ b/runtime/include/psx_icache.h @@ -14,13 +14,13 @@ void psx_icache_reset(void); void psx_icache_fetch(CPUState *cpu, uint32_t addr); void psx_icache_fetch_miss(CPUState *cpu, uint32_t addr); -/* BIU / cache control (0xFFFE0130) bits the cache model reads; names and values - * as Beetle's cpu.cpp BIU_* defines. */ -#define PSX_BIU_ICACHE_ENABLE 0x800u /* BIU_ENABLE_ICACHE_S1 */ -#define PSX_BIU_DCACHE_ENABLE 0x080u /* BIU_ENABLE_DCACHE */ -#define PSX_BIU_TAG_TEST 0x004u /* BIU_TAG_TEST_MODE */ -#define PSX_BIU_INVALIDATE 0x002u /* BIU_INVALIDATE_MODE */ -#define PSX_BIU_LOCK 0x001u /* BIU_LOCK_MODE */ +/* BIU / cache control (0xFFFE0130) bits the cache model reads (PSX-SPX + * memorycontrol, "FFFE0130h - BCC, BIU/Cache Configuration Register"). */ +#define PSX_BIU_ICACHE_ENABLE 0x800u /* bit 11, IS1 */ +#define PSX_BIU_DCACHE_ENABLE 0x080u /* bit 7, DS */ +#define PSX_BIU_TAG_TEST 0x004u /* bit 2, TAG */ +#define PSX_BIU_INVALIDATE 0x002u /* bit 1, INV */ +#define PSX_BIU_LOCK 0x001u /* bit 0, LOCK */ /* A CPU store made while SR.IsC is set, applied to the I-cache model: the * I-cache half of Beetle PS_CPU::WriteMemory's IsC branch. With the I-cache diff --git a/runtime/src/cdrom.c b/runtime/src/cdrom.c index d667ba3f..1d104243 100644 --- a/runtime/src/cdrom.c +++ b/runtime/src/cdrom.c @@ -1220,8 +1220,9 @@ static void xa_reset_decode(void) { } /* CD-audio volume matrix ([data_source][output_port], 0x80 = unity) applied - * to every decoded XA/CD-DA sample, exactly as the CD controller does on - * real hardware (Beetle PS_CDC::ApplyVolume, cdc.cpp:524). Written via the + * to every decoded XA/CD-DA sample, as the CD controller does on real + * hardware (PSX-SPX cdromdrive, the ATV0..ATV3 volume registers and + * ADPCTL). Written via the * index-2/3 register banks and latched by the "apply changes" bit; games * drive it CONSTANTLY — X5 fades music between scenes with it (measured * 0x7E steady, ramping to 0x00 at transitions on the Beetle oracle). Not @@ -2902,8 +2903,9 @@ uint32_t cdrom_read(uint32_t addr) { case 0x1F801800: { uint8_t s = index_reg & 0x03; /* Bit 2 is ADPBUSY (XA-ADPCM playback in progress), NOT "ADPCM - * empty" — it must idle at 0. The oracle (beetle cdc.cpp, Read - * A==0) never raises it. Raising it permanently made every BIOS + * empty" — it must idle at 0 (PSX-SPX cdromdrive, HSTS: "ADPBUSY + * ADPCM busy (R, 1=playing XA-ADPCM)"; the Beetle reference never + * raises it either). Raising it permanently made every BIOS * status poll see "XA busy" and steered the kernel CD driver * init down a different branch from real hardware. */ if (param_count == 0) s |= (1 << 3); @@ -2981,9 +2983,9 @@ void cdrom_write(uint32_t addr, uint32_t value) { if (index_reg == 2) { cd_pending_vol[0][1] = val; /* Left-CD -> Right-SPU */ } else if (index_reg == 3) { - /* Apply-changes latch (bit 5) copies pending -> live, exactly - * like Beetle cdc.cpp case 0x0B. (ADPMute bit 0 is not modeled - * by the Beetle oracle either.) */ + /* Apply-changes latch (bit 5) copies pending -> live (PSX-SPX + * cdromdrive, ADPCTL bit 5 CHNGATV). (ADPMUTE, bit 0, is not + * modeled; the Beetle reference does not model it either.) */ if (val & 0x20) { memcpy(cd_decode_vol, cd_pending_vol, sizeof(cd_decode_vol)); } diff --git a/runtime/src/dirty_ram_interp.c b/runtime/src/dirty_ram_interp.c index f7c85b4c..0d8856fa 100644 --- a/runtime/src/dirty_ram_interp.c +++ b/runtime/src/dirty_ram_interp.c @@ -2277,8 +2277,9 @@ static int exec_one_fetched_inner(CPUState *cpu, uint32_t pc, uint32_t insn, if (rd == 13) { /* Cause register: only the software-interrupt pending bits * [9:8] are writable; hardware IP [15:10], ExcCode [6:2] and - * BD [31] are read-only from software (psx-spx, matches - * PCSX-Redux/Beetle MTC0). */ + * BD [31] are read-only from software (PSX-SPX + * cpuspecifications, "cop0r13 - CAUSE - (Read-only, except, + * Bit8-9 are R/W)"). */ cpu->cop0[13] = (cpu->cop0[13] & ~0x0300u) | (val & 0x0300u); } else { cpu->cop0[rd] = val; diff --git a/runtime/src/dma.c b/runtime/src/dma.c index b60d8add..5bf713a3 100644 --- a/runtime/src/dma.c +++ b/runtime/src/dma.c @@ -9,7 +9,7 @@ * request-mode transfers are advanced from the guest cycle clock so games * which synchronize video decode through DMA busy/request state see realistic * backpressure. - * Reference: nocash PSX specs, DuckStation src/core/dma.cpp + * Reference: PSX-SPX, page dmachannels (DMA Channels). */ #include "dma.h" diff --git a/runtime/src/gpu.c b/runtime/src/gpu.c index eb25d051..9cee28e1 100644 --- a/runtime/src/gpu.c +++ b/runtime/src/gpu.c @@ -7,7 +7,7 @@ * - GP0 command write — ABORTS (not yet implemented) * - GPUREAD — returns last latched value * - * Reference: nocash PSX specs, DuckStation src/core/gpu.cpp + * Reference: PSX-SPX, page graphicsprocessingunitgpu (Graphics Processing Unit). */ #include "gpu.h" @@ -4485,7 +4485,8 @@ void gpu_get_display_info(GpuDisplayInfo* out) { out->depth24 = (int)(display_depth & 1u); out->disabled = (int)display_disabled; - /* Dot-clock divider from GP1(08h) hres (psx-spx / DuckStation). */ + /* Dot-clock divider from GP1(08h) hres (PSX-SPX graphicsprocessingunitgpu, + * "Horizontal Timings", Dotclocks: /10, /8, /7, /5, /4). */ uint32_t cycles; uint32_t mode_w; if (hres2) { @@ -5324,7 +5325,8 @@ static uint16_t current_texpage(void) { * (GPUSTAT bits 0-8) exactly like GP0(E1) bits 0-8 — the poly's own word, not * the last E1, decides its semi-transparency mode, and later rectangle/sprite * prims (which carry no texpage word) consume the state the poly left behind. - * Beetle: SetTPage(CB[4 + ((cc>>4)&1)] >> 16) on every textured poly. + * PSX-SPX graphicsprocessingunitgpu, "TPage Attribute (Parameter for textured polygon + * commands)": bits 0-8 and 11 are the same as GP0(E1h), bits 9-10 unused. * Bits 9-10 (dither / draw-to-display) exist only in E1; bit 11 (texture * disable) only latches when GP1(09h) allowed it, which polys can't grant. */ static void set_tpage_from_poly(uint16_t tpage_word) { @@ -6414,7 +6416,10 @@ static int gp0_command_word_count(uint8_t opcode) { case 0xE6: return 1; /* mask bits */ default: - /* 0x03-0x1E, 0xE7-0xEF, 0xFF: NOP (1 word) per DuckStation */ + /* 0x03-0x1E, 0xE7-0xEF, 0xFF: NOP (1 word). PSX-SPX graphicsprocessingunitgpu, + * "GP0(04h..1Eh,E0h,E7h..EFh) - Mirrors of GP0(00h) - NOP (?)", + * lists 04h-1Eh and E7h-EFh; it calls 03h unknown and does not + * list FFh. This code also treats 03h and FFh as NOPs. */ if ((opcode >= 0x03 && opcode <= 0x1E) || (opcode >= 0xE7 && opcode <= 0xEF) || opcode == 0xFF) { @@ -7552,8 +7557,9 @@ static void gpu_write_gp0_body(uint32_t val) { return; } - /* Polyline terminator rule (Beetle mednafen/psx/gpu.c INCMD_PLINE, - * DuckStation gpu.cpp HandleRenderPolyLineCommand/DrawingPolyLine): + /* Polyline terminator rule (PSX-SPX graphicsprocessingunitgpu, "GPU Render Line + * Commands": at least two vertices; the terminator is tested on the + * first word of a vertex, the colour word when shaded): * * 1. A polyline always has at least two vertices. The words of the first * two vertices are consumed unconditionally — mono [V0][V1], shaded @@ -7817,8 +7823,10 @@ static void gp1_display_mode(uint32_t val) { static void gp1_get_info(uint32_t val) { /* GP1(10h): Get GPU info — writes result to GPUREAD latch. - * Mednafen-psx masks the subcommand to 4 bits (val & 0x0F) and - * services cases 2..5, 7, 8. Tomba's ResetGraph() uses param 7 to + * PSX-SPX graphicsprocessingunitgpu, "GP1(10h) - Read GPU internal + * register" (v2 GPU): indices 10h and up mirror 00h..0Fh, so the + * subcommand is val & 0x0F; 02h..05h, 07h and 08h return data. + * Tomba's ResetGraph() uses param 7 to * read the GPU version (must be 2) to pick its video-mode path — * the wrong value here lands the game on a no-draw branch. */ uint32_t which = val & 0x0F; @@ -7836,10 +7844,10 @@ static void gp1_get_info(uint32_t val) { gpuread_latch = ((uint32_t)draw_offset_x & 0x7FFu) | (((uint32_t)draw_offset_y & 0x7FFu) << 11); break; - case 7: /* GPU version (real-hw + mednafen return 2) */ + case 7: /* GPU version: PSX-SPX gives 1 or 2; this models 2 */ gpuread_latch = 2; break; - case 8: /* unknown info index, real hw / mednafen return 0 */ + case 8: /* unknown info index, returns 0 (PSX-SPX) */ gpuread_latch = 0; break; default: diff --git a/runtime/src/gte.cpp b/runtime/src/gte.cpp index 59b7adc4..8b5447d2 100644 --- a/runtime/src/gte.cpp +++ b/runtime/src/gte.cpp @@ -33,8 +33,8 @@ namespace GTE { // // The real PS1 GTE does NOT compute an exact H*0x20000/SZ3. It uses an // Unsigned Newton-Raphson (UNR) reciprocal approximation driven by a 257-entry -// seed table (documented in PSX-SPX "GTE Division Inaccuracy"; identical to the -// mednafen/Beetle oracle in beetle-psx/mednafen/psx/gte.cpp). Exact division +// seed table (documented in PSX-SPX geometrytransformationenginegte, "GTE Division +// Inaccuracy"; the Beetle reference gives the same results). Exact division // diverges from hardware by +/-1 (occasionally up to a few units) on ~25% of // inputs, which is enough to flip games' distance/intensity threshold branches // (e.g. Ape Escape's additive-glow CLUT semi-transparency bit). This is the @@ -1080,7 +1080,7 @@ static int32_t gte_h_scaled(const GTEState* gte) { // --------------------------------------------------------------------------- // RTPS — Perspective Transformation (internal, operates on given vertex V) // -// Matches DuckStation/Beetle (psx-spx): +// PSX-SPX geometrytransformationenginegte, "COP2 0180001h - 15 Cycles - RTPS": // MAC1/2/3 = (TR*1000h + RT*V) SAR (sf*12) // IR1/IR2 = limB(MAC, lm) // IR3 FLAG = limB(MAC3_unshifted SAR 12, lm=0); stored IR3 = limB(MAC3, lm) @@ -1124,7 +1124,7 @@ void gte_rtps_internal(GTEState* gte, int16_t* V, bool setMac0, uint32_t instr) gte->IR1 = gte->saturate_ir(gte->MAC1, 1, lm); gte->IR2 = gte->saturate_ir(gte->MAC2, 2, lm); - // IR3 quirk (psx-spx / DuckStation): FLAG.22 from (mac3>>12) as if lm=0; + // IR3 quirk (PSX-SPX, RTPS note): FLAG.22 from (mac3>>12) as if lm=0; // stored IR3 clamps MAC3 with the real lm bit and does not touch FLAG again. (void)gte->saturate_ir(static_cast(mac3 >> 12), 3, false); { @@ -1509,7 +1509,7 @@ void gte_nclip(GTEState* gte, uint32_t instr) { // --------------------------------------------------------------------------- // AVSZ3 (0x2D) — Average Z (3 points) // --------------------------------------------------------------------------- -// Hardware (psx-spx / DuckStation): MAC0 keeps ZSF3*(SZ1+SZ2+SZ3); only OTZ +// Hardware (PSX-SPX, "AVSZ3"): MAC0 keeps ZSF3*(SZ1+SZ2+SZ3); only OTZ // gets MAC0/1000h. CTR NdIntroWoodEmitHelper indexes the batch OT with // MAC0>>17 (== OTZ>>5). Shifting MAC0 here collapsed every face into slot 0 // (arms/trophy/chest z-fight + missing banner under later same-bucket paint). @@ -1736,8 +1736,10 @@ void gte_mvmva(GTEState* gte, uint32_t instr) { case 1: std::memcpy(M, gte->L, sizeof(M)); break; case 2: std::memcpy(M, gte->LC, sizeof(M)); break; default: { - /* Undocumented matrix selector wiring, verified by hardware - * register vectors (also used by Beetle/DuckStation). */ + /* Matrix selector 3: PSX-SPX geometrytransformationenginegte, + * "MVMVA": "Setting Mx=3 selects a garbage matrix" (-R*10h, + * +R*10h, IR0, RT13 x3, RT22 x3), verified by hardware + * register vectors. */ const int16_t color = static_cast( static_cast(gte->RGBC & 0xFFu) << 4); M[0][0] = static_cast(-color); diff --git a/runtime/src/interrupts.c b/runtime/src/interrupts.c index 275c9a02..c229733b 100644 --- a/runtime/src/interrupts.c +++ b/runtime/src/interrupts.c @@ -145,14 +145,13 @@ extern uint32_t i_mask; * again sees a pending interrupt that no longer exists and can spin in its * event scan forever. * - * Verified against the independent Beetle oracle rather than asserted: - * beetle-psx/mednafen/psx/irq.cpp defines - * #define Recalc() PSX_CPU->AssertIRQ(0, (bool)(Status & Mask)) - * and calls it from IRQ_Assert (raise), from IRQ_Write for BOTH the Status ack - * and the Mask write, and at power-on; cpu.cpp's AssertIRQ clears bit (10+n) - * unconditionally and re-sets it only when the level is asserted. So the line - * is recomputed at every point (I_STAT & I_MASK) can change, which is exactly - * the set of call sites below. + * PSX-SPX interrupts, "PSX specific COP0 Notes": cop0r13.bit10 "is NOT a + * latch, ie. it gets automatically cleared as soon as (I_STAT AND + * I_MASK)=zero", and "Interrupt Request / Execution": it is set while + * (I_STAT AND I_MASK) is nonzero. So the bit is recomputed at every point + * (I_STAT & I_MASK) can change: a raise, a Status ack, a Mask write and + * power-on, which is exactly the set of call sites below. The Beetle + * reference behaves the same. * * Ownership: this function is the ONLY writer of CAUSE bit 10. The delivery * path no longer ORs it in separately — one owner, no divergence. @@ -173,7 +172,7 @@ void psx_irq_refresh_cause_ip2(void) void psx_irq_set_cause_ptr(uint32_t *p) { s_cause_ptr = p; - /* Power-on recompute, mirroring Beetle's IRQ_Power() -> Recalc(). Without + /* Power-on recompute (the Beetle reference also recomputes here). Without * this the first mirror only happens at the first raise/ack, so a CAUSE * read before any interrupt activity would show a stale bit. */ psx_irq_refresh_cause_ip2(); diff --git a/runtime/src/memcard.c b/runtime/src/memcard.c index 8ff08baa..d3ba4b45 100644 --- a/runtime/src/memcard.c +++ b/runtime/src/memcard.c @@ -173,7 +173,9 @@ static uint8_t frame_checksum(const uint8_t *frame) { } /* Format a 128KB card image to match a real PS1 blank card. - * Layout verified against DuckStation's MemoryCardImage::Format(). */ + * Layout per PSX-SPX controllersandmemorycards, "Memory Card Data Format": + * header frame, directory frames (free A0h, next FFFFh), broken sector + * list (FFFFFFFFh = none) and the write test frame 63. */ static void memcard_format(uint8_t *data) { memset(data, 0xFF, MEMCARD_SIZE); diff --git a/runtime/src/memory.c b/runtime/src/memory.c index f994d3cc..53b3c460 100644 --- a/runtime/src/memory.c +++ b/runtime/src/memory.c @@ -1689,8 +1689,8 @@ uint32_t psx_read_word(uint32_t addr) { } /* Physical address of a CPU/DMA main-RAM access. Fold KUSEG/KSEG0/KSEG1 first * (0x1FFFFFFF), then fold the 2nd-4th main-RAM mirrors: real hardware mirrors the - * 2 MB DRAM across the WHOLE 0..0x7FFFFF physical window (Beetle libretro.cpp:874 - * `A < 0x00800000` routes to main RAM; psx-spx "2048K RAM ... mirrored 4x"). A game + * 2 MB DRAM across the WHOLE 0..0x7FFFFF physical window (PSX-SPX memorymap, + * "Memory Mirrors": "2MB RAM can be mirrored to the first 8MB"). A game * may legitimately place its stack at the top of that window — Tsumu Light computes * sp = (ramtop-8)|0x80000000 with its ramtop constant 0x00800000, giving sp=0x807FFFF8 * (top of the 4th mirror). Without this fold those accesses miss DRAM (`< RAM_SIZE` @@ -1701,9 +1701,9 @@ static uint32_t psx_read_word_raw(uint32_t addr) { /* KSEG2 cache control — before physical translation. */ if (addr == 0xFFFE0130u) return cache_ctrl; /* KSEG2 (0xC0000000+): only cache control (0xFFFE0130, above where - * applicable) exists there. Real hardware maps NOTHING else — Beetle - * (cpu.cpp addr_mask[6..7]=0xFFFFFFFF) leaves KSEG2 addresses unmasked - * so they fall to unmapped space and the access is a no-op. Our flat + * applicable) exists there. PSX-SPX memorymap ("Memory Map") lists only + * the cache control registers at FFFE0000h in KSEG2; every other KSEG2 + * access is unmapped and is a no-op here, as on the Beetle reference. Our flat * 0x1FFFFFFF masking routed KSEG2 garbage onto LIVE registers: Tomba2's * attract runs a BIOS bzero over a wild 0xDF80xxxx pointer, which zeroed * the SIO/memctrl I/O block byte-by-byte and then hit the unmapped-MMIO diff --git a/runtime/src/psx_icache.c b/runtime/src/psx_icache.c index 60749aa4..1aa40992 100644 --- a/runtime/src/psx_icache.c +++ b/runtime/src/psx_icache.c @@ -108,23 +108,19 @@ void psx_icache_fetch_miss(CPUState* cpu, uint32_t addr) { #endif } -/* IsC store -> I-cache, transcribed from Beetle PS_CPU::WriteMemory - * (mednafen/psx/cpu.cpp:484-501): +/* IsC store -> I-cache. This function was transcribed from Beetle's + * PS_CPU::WriteMemory; it is to be replaced by the cpu/icache change + * tracked in issue #443 (docs/PROVENANCE.md). * - * if(BIU & BIU_ENABLE_ICACHE_S1) { - * if(BIU & (BIU_TAG_TEST_MODE | BIU_INVALIDATE_MODE | BIU_LOCK_MODE)) { - * valid_bits = (BIU & BIU_TAG_TEST_MODE) ? ((value << ((address & 0x3) * 8)) & 0x0F) : 0x00; - * ICI = &ICache[(address & 0xFF0) >> 2]; - * for(i = 0; i < 4; i++) - * ICI[i].TV = ((valid_bits & (1U << i)) ? 0x00 : 0x02) | (address & 0xFFFFFFF0) | (i << 2); - * } else - * ICache[(address & 0xFFC) >> 2].Data = value << ((address & 0x3) * 8); - * } - * - * The else arm writes an instruction word and leaves TV alone. This model keeps - * no instruction words (compiled code supplies them), so it has nothing to do. - * Beetle does not model SR.SwC (swapped caches), so neither does this. The - * store keeps the full virtual address as the tag, as Beetle's refill does. */ + * With SR.IsC set and the I-cache enabled (BCC bit 11, IS1), a store made + * with BCC TAG, INV or LOCK set rewrites the tag and valid bits of the four + * words of its line (PSX-SPX memorycontrol, "FFFE0130h - BCC"); that is + * how FlushCache (A 44h) invalidates the cache. A store with none of those + * bits writes an instruction word; this model keeps no instruction words + * (compiled code supplies them), so it does nothing. SR.SwC is not + * modeled. PSX-SPX gives the stored tag as (data AND 0Fh) OR (offset AND + * FFFFF000h); this model stores the line address with its own valid-bit + * encoding, and the replacement is to be checked against that text. */ void psx_icache_isc_store(uint32_t biu, uint32_t addr, uint32_t value) { #ifdef PSX_ENABLE_BLOCK_CYCLES /* Same gate as a refill: lockstep replay must not move the shared tags; diff --git a/runtime/src/sio.c b/runtime/src/sio.c index 2d82ca85..e85f8b46 100644 --- a/runtime/src/sio.c +++ b/runtime/src/sio.c @@ -69,8 +69,9 @@ static uint8_t pad_analog_device[PSX_MAX_PLAYERS] = { /* DualShock command 0x4D maps the six writable bytes in a 0x42 poll onto the * two motors: 0x00 = small/high-frequency, 0x01 = large/low-frequency, - * 0xFF = unused. The map powers up unassigned and is echoed back while a new - * map is latched, matching the physical pad/Mednafen protocol. */ + * 0xFF = unused (PSX-SPX controllersandmemorycards, "Config Mode - Command 4Dh"). + * The map powers up unassigned and is echoed back while a new map is + * latched, as on the Mednafen reference. */ static uint8_t pad_rumble_map[PSX_MAX_PLAYERS][6] = { PSX_PAD_INIT({ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }) }; @@ -274,8 +275,9 @@ static uint8_t pad_protocol_id(int logical) { } /* NeGcon (NPC-101) poll payload: buttons, twist, then analog I, II and L - * (Mednafen psx/input/negcon.cpp). The device has no config mode and no - * motors; every byte is fixed by the current host sample. */ + * (PSX-SPX controllersandmemorycards, "neGcon Racing Controller"). + * The device has no config mode and no motors; every byte is fixed by the + * current host sample. */ static void pad_fill_negcon_poll(int logical, uint8_t out[8]) { out[0] = 0x23; out[1] = 0x5A; @@ -1412,7 +1414,7 @@ static void pad_process_byte(uint8_t tx_byte) { if (pad_analog[lp] == SIO_PAD_NEGCON) { /* NeGcon answers only the 0x42 poll. Its ID byte goes out with any * command, but for anything else it does not ACK and the - * transaction ends (Mednafen negcon.cpp phase 1). */ + * transaction ends, as on the Mednafen reference. */ pad_fill_negcon_poll(lp, pad_response); sio_rx_data = pad_response[0]; if (tx_byte == 0x42) { @@ -1817,8 +1819,9 @@ static void mc_process_byte(uint8_t tx_byte) { } else { mc_data_idx = 0; mc_state = MC_WRITE_LSB_ECHO; - /* Hardware/Beetle echo the high address byte on the address-LSB - * transfer (zero for all 1Mbit card sectors), then echo the low + /* Hardware echoes the high address byte on the address-LSB + * transfer (PSX-SPX controllersandmemorycards, "Writing Data to Memory + * Card": reply "pre"; zero for all 1Mbit card sectors), then echo the low * address byte while receiving the first payload byte. Some BIOS * card-write paths validate this handshake before they schedule the * follow-up directory reads. */ From 689f3b348e544cfeb6fb582ac12381f70f35b02d Mon Sep 17 00:00:00 2001 From: Yves Date: Fri, 23 Oct 2026 03:27:42 +0800 Subject: [PATCH 23/29] Report ambiguous XR origin diagnostics without recentering Co-authored-by: Yves --- docs/OPENXR_RENDERING.md | 4 +++ docs/internal/upstream/vr-origin-recovery.md | 21 +++++++++++++++ runtime/include/psx_openxr.h | 2 ++ runtime/include/xr_origin_diagnostics.h | 28 ++++++++++++++++++++ runtime/src/debug_server.c | 6 +++-- runtime/src/psx_openxr.c | 23 +++++++++++++--- runtime/tests/test_openxr_input.c | 18 +++++++++++++ 7 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 docs/internal/upstream/vr-origin-recovery.md create mode 100644 runtime/include/xr_origin_diagnostics.h diff --git a/docs/OPENXR_RENDERING.md b/docs/OPENXR_RENDERING.md index be7b4a7e..75eeee77 100644 --- a/docs/OPENXR_RENDERING.md +++ b/docs/OPENXR_RENDERING.md @@ -55,6 +55,10 @@ frame without recycling an old pair. Private swapchains receive eye textures; guest execution, simulation time and guest TR registers are not owned by XR. `psx_mod_openxr_recenter` establishes the shared pose origin. World scale, native camera/body mapping, aiming and gameplay policy belong to the plugin. +An inverted relative head pose can be a deliberate sustained roll, so the +backend reports its up-dot and inversion streak through `openxr_stats` without +changing the origin. Explicit recenter and OpenXR reference-space change events +own origin changes. ## Offline actions and controller snapshots diff --git a/docs/internal/upstream/vr-origin-recovery.md b/docs/internal/upstream/vr-origin-recovery.md new file mode 100644 index 00000000..0c22c75b --- /dev/null +++ b/docs/internal/upstream/vr-origin-recovery.md @@ -0,0 +1,21 @@ +# Report ambiguous XR origin diagnostics without recentering + +Source: [FractalEngineer/psxrecomp](https://github.com/FractalEngineer/psxrecomp). +- [96981469](https://github.com/FractalEngineer/psxrecomp/commit/96981469c4a3de0b31c3d2a282efdc7892f81361) + +Original author: Yves ; retained as commit author +and Co-authored-by on this reconstructed port. + +Adapted: diagnostic up-dot and sustained-inversion reporting. Automatic +recentering was deliberately excluded because a valid sustained roll is +indistinguishable from a corrupt origin in the relative quaternion. Explicit +recenter and OpenXR reference-space change events retain ownership of origin +changes. +Base: `18d52c8bfbe5afc380292ef3116af2920225a92e` (current upstream master or the preceding feature). + +Validation: Windows/GCC 13.2 Release: the XR input fixture covers upright, +ordinary yaw, ten-frame ambiguous inversion, valid sustained roll, invalid +tracking reset, and reference-change timing. Physical reconnect is not retested. + +Consumer: [Medal of Honor v0.1.6](https://github.com/FractalEngineer/Medal-Of-Honor-PSX-Recomp/tree/1957e28a5dc7f5daa79e2b9b6ed867eb06b41ace). +Requires a framework pin bump and rebuild; no game/BIOS regeneration. diff --git a/runtime/include/psx_openxr.h b/runtime/include/psx_openxr.h index 4455b098..86fd5e1a 100644 --- a/runtime/include/psx_openxr.h +++ b/runtime/include/psx_openxr.h @@ -11,6 +11,8 @@ typedef struct PSXOpenXRStats { const char *stage; uint64_t waits, submitted, empty, failures, predicted_time; float ipd_m; + float origin_up_dot; + uint32_t origin_inversion_streak, origin_ambiguous; double units_per_meter; uint64_t gl_version, min_gl_version, max_gl_version; int64_t swapchain_format; /* GL_SRGB8_ALPHA8 preferred; GL_RGBA8 decoded copy */ diff --git a/runtime/include/xr_origin_diagnostics.h b/runtime/include/xr_origin_diagnostics.h new file mode 100644 index 00000000..aae8eaee --- /dev/null +++ b/runtime/include/xr_origin_diagnostics.h @@ -0,0 +1,28 @@ +#pragma once + +/* Observe an inverted relative head pose for diagnostics only. A sustained + * roll is valid user motion and must never trigger an automatic recenter. */ +static inline unsigned xr_origin_observe_inversion(unsigned streak, int valid, + const double origin[4], + const double pose[4], + double *up_dot) { + if (!valid) { + if (up_dot) *up_dot = 1.0; + return 0; + } + double rx = origin[3]*pose[0] - origin[0]*pose[3] - + origin[1]*pose[2] + origin[2]*pose[1]; + double rz = origin[3]*pose[2] - origin[0]*pose[1] + + origin[1]*pose[0] - origin[2]*pose[3]; + double up = 1.0 - 2.0*(rx*rx + rz*rz); + if (up_dot) *up_dot = up; + if (up >= -0.5) return 0; + return streak < 10u ? streak + 1u : streak; +} + +/* Only the runtime's reference-space change event authorizes an origin reset. */ +static inline int xr_origin_reference_reset_due(int pending, + long long change_time, + long long now) { + return pending && now >= change_time; +} diff --git a/runtime/src/debug_server.c b/runtime/src/debug_server.c index 43ac2544..dc559b45 100644 --- a/runtime/src/debug_server.c +++ b/runtime/src/debug_server.c @@ -9088,7 +9088,8 @@ static void handle_openxr_stats(int id, const char *json) { "\"frame_open\":%u,\"result\":%d,\"stage\":\"%s\",\"runtime\":\"%s\"," "\"last_failure\":\"%s\",\"last_failure_result\":%d,\"view_flags\":%llu," "\"waits\":%llu,\"submitted\":%llu,\"empty\":%llu,\"failures\":%llu," - "\"predicted_time\":%llu,\"ipd_m\":%.6f,\"units_per_meter\":%.6f," + "\"predicted_time\":%llu,\"ipd_m\":%.6f,\"origin_up_dot\":%.6f," + "\"origin_inversion_streak\":%u,\"origin_ambiguous\":%u,\"units_per_meter\":%.6f," "\"gl_version\":%llu,\"min_gl_version\":%llu,\"max_gl_version\":%llu,\"swapchain_format\":%lld," "\"submitted_layer\":%u,\"quad_submitted\":%llu," "\"submitted_source\":%u,\"native_submitted\":%llu,\"submitted_native_frame\":%llu," @@ -9098,7 +9099,8 @@ static void handle_openxr_stats(int id, const char *json) { s.stage?s.stage:"off",s.runtime,s.last_failure?s.last_failure:"",s.last_failure_result, (unsigned long long)s.view_flags,(unsigned long long)s.waits, (unsigned long long)s.submitted,(unsigned long long)s.empty, - (unsigned long long)s.failures,(unsigned long long)s.predicted_time,s.ipd_m,s.units_per_meter, + (unsigned long long)s.failures,(unsigned long long)s.predicted_time,s.ipd_m, + s.origin_up_dot,s.origin_inversion_streak,s.origin_ambiguous,s.units_per_meter, (unsigned long long)s.gl_version,(unsigned long long)s.min_gl_version, (unsigned long long)s.max_gl_version,(long long)s.swapchain_format,s.submitted_layer, (unsigned long long)s.quad_submitted,s.submitted_source, diff --git a/runtime/src/psx_openxr.c b/runtime/src/psx_openxr.c index ada0af65..26b44ccd 100644 --- a/runtime/src/psx_openxr.c +++ b/runtime/src/psx_openxr.c @@ -1,3 +1,4 @@ +#include "xr_origin_diagnostics.h" /* Opt-in Win32 OpenXR lifecycle. No guest state, console diagnostics or timing * phases: located poses and projection submission belong to one host frame. * @@ -163,6 +164,7 @@ static XrAction s_pose_action[2]; static XrSpace s_pose_space[2][2]; static XrPath s_hand[2]; static XrTime s_origin_reset_time; +static unsigned s_origin_inversion_streak; static XrSwapchain s_chain[2]; static XrSwapchainImageOpenGLKHR *s_images[2]; static uint32_t s_count[2]; @@ -427,7 +429,9 @@ static void xr_destroy(void) { s_trigger=s_squeeze=XR_NULL_HANDLE;memset(s_click,0,sizeof s_click); memset(s_pose_action,0,sizeof s_pose_action); if(s_instance)xrDestroyInstance(s_instance);s_instance=XR_NULL_HANDLE; - s_origin_valid=0;s_origin_reset_time=0;s_stats.state=0; + s_origin_valid=0;s_origin_reset_time=0;s_origin_inversion_streak=0; + s_stats.origin_inversion_streak=s_stats.origin_ambiguous=0;s_stats.origin_up_dot=1.0f; + s_stats.state=0; s_stats.initialized=s_stats.running=s_stats.tracking=s_stats.frame_open=0; } /* Act on a flagged loss. A lost session/instance or an unusable swapchain tears @@ -483,7 +487,8 @@ void psx_openxr_stats(PSXOpenXRStats *out) { void psx_openxr_recenter(void) { hands_clear(); #if defined(PSX_OPENXR) - s_origin_valid=0; + s_origin_valid=0;s_origin_inversion_streak=0; + s_stats.origin_inversion_streak=s_stats.origin_ambiguous=0; #endif } void psx_openxr_shutdown(void) { @@ -667,14 +672,24 @@ int psx_openxr_begin(int width,int height,double units) { q[i][0]=v->orientation.x;q[i][1]=v->orientation.y;q[i][2]=v->orientation.z;q[i][3]=v->orientation.w; p[i][0]=v->position.x;p[i][1]=v->position.y;p[i][2]=v->position.z; } - if(s_origin_reset_time && s_time>=s_origin_reset_time) { - s_origin_valid=0;s_origin_reset_time=0; + if(xr_origin_reference_reset_due(s_origin_reset_time!=0,s_origin_reset_time,s_time)) { + s_origin_valid=0;s_origin_reset_time=0;s_origin_inversion_streak=0; } if(!s_origin_valid) { memcpy(s_oq,q[0],sizeof s_oq); for(int j=0;j<3;j++)s_op[j]=(p[0][j]+p[1][j])*.5; s_origin_valid=1; + s_origin_inversion_streak=0; } + /* A sustained roll can be a legitimate headset pose. Report the ambiguous + * case without changing the reference; explicit recenter or the OpenXR + * reference-space change event owns origin recovery. */ + double origin_up_dot=1.0; + s_origin_inversion_streak=xr_origin_observe_inversion( + s_origin_inversion_streak,s_origin_valid,s_oq,q[0],&origin_up_dot); + s_stats.origin_up_dot=(float)origin_up_dot; + s_stats.origin_inversion_streak=s_origin_inversion_streak; + s_stats.origin_ambiguous=s_origin_inversion_streak>=10u; double separation=0; for(int j=0;j<3;j++)separation+=(p[1][j]-p[0][j])*(p[1][j]-p[0][j]); s_stats.ipd_m=(float)sqrt(separation); diff --git a/runtime/tests/test_openxr_input.c b/runtime/tests/test_openxr_input.c index 416044e1..b5cf9981 100644 --- a/runtime/tests/test_openxr_input.c +++ b/runtime/tests/test_openxr_input.c @@ -2,6 +2,7 @@ #include #include #include "xr_surface_math.h" +#include "xr_origin_diagnostics.h" extern void psx_openxr_pose_bob(double q[2][4], double p[2][3]); extern int psx_openxr_pose_override(double, double, double); extern int psx_openxr_pose_pitch_override(double); @@ -14,6 +15,23 @@ extern void psx_openxr_pose_clear(void); } \ } while (0) int main(void) { + const double upright[4]={0,0,0,1}, upside[4]={0,0,1,0}, yaw180[4]={0,1,0,0}; + unsigned inversion=0;double up_dot=0; + for(int i=0;i<9;i++) + inversion=xr_origin_observe_inversion(inversion,1,upright,upside,&up_dot); + CHECK(inversion==9 && up_dot < -0.5); + inversion=xr_origin_observe_inversion(inversion,1,upright,upside,&up_dot); + CHECK(inversion==10 && up_dot < -0.5); + /* A valid sustained roll stays ambiguous but never requests a reset. */ + inversion=xr_origin_observe_inversion(inversion,1,upright,yaw180,&up_dot); + CHECK(inversion==0 && fabs(up_dot-1.0)<1e-12); + inversion=xr_origin_observe_inversion(7,0,upright,upside,&up_dot); + CHECK(inversion==0 && up_dot==1.0); + inversion=xr_origin_observe_inversion(7,1,upside,upside,&up_dot); + CHECK(inversion==0 && fabs(up_dot-1.0)<1e-12); + CHECK(!xr_origin_reference_reset_due(0,100,100)); + CHECK(!xr_origin_reference_reset_due(1,101,100)); + CHECK(xr_origin_reference_reset_due(1,100,100)); /* Synthetic controls are absolute, bounded and inert after clearing. */ double bob_q[2][4]={{0,0,0,1},{0,0,0,1}}; double bob_p[2][3]={{-.03,0,0},{.03,0,0}}; From 510560e599d3387416e9fca95359220ffbb84997 Mon Sep 17 00:00:00 2001 From: Yves Date: Fri, 23 Oct 2026 03:29:17 +0800 Subject: [PATCH 24/29] Preserve precise eye poses and clip near-camera geometry Co-authored-by: Yves --- docs/internal/FAITHFUL_TIMING_PLAN.md | 5 ++ .../internal/upstream/vr-projective-replay.md | 22 +++++++ runtime/include/mod_plugins.h | 7 +++ runtime/include/pgxp.h | 9 +++ runtime/src/debug_server.c | 5 +- runtime/src/gpu.c | 40 +++++++++++-- runtime/src/gpu_gl_renderer.c | 2 + runtime/src/gte.cpp | 59 ++++++++++++++++++- runtime/src/pgxp.cpp | 23 ++++++++ runtime/src/render_pass.c | 21 ++++++- runtime/src/vr_pose_math.c | 5 +- runtime/tests/gpu_exec_stubs.inc | 23 +++++++- runtime/tests/test_gpu_gp0_history_guard.py | 20 ++++--- runtime/tests/test_gte_register_access.cpp | 50 ++++++++++++++++ runtime/tests/test_native_wide_projection.c | 23 ++++++++ runtime/tests/test_pgxp.cpp | 18 ++++++ runtime/tests/test_render_pass_abort.c | 3 + runtime/tests/test_render_pass_sandbox.c | 28 ++++++++- runtime/tests/test_vr_pose_math.c | 4 ++ 19 files changed, 345 insertions(+), 22 deletions(-) create mode 100644 docs/internal/upstream/vr-projective-replay.md diff --git a/docs/internal/FAITHFUL_TIMING_PLAN.md b/docs/internal/FAITHFUL_TIMING_PLAN.md index a61a0bc4..bae4ca9b 100644 --- a/docs/internal/FAITHFUL_TIMING_PLAN.md +++ b/docs/internal/FAITHFUL_TIMING_PLAN.md @@ -213,6 +213,11 @@ on a fixed region -> next. ## 5. Status / Log (update every session) +- **2026-10-23 — VR upstream split.** Precise projective replay reconstructed on + current master; six focused runtime regressions and the GPU-history guard pass. + Prior fork/headset acceptance remains historical. Other VR features are + separate single-commit branches. Central Beads F: is unavailable. + - **2026-10-07 — DuckStation texture authoring fidelity (beads-qi66).** The early Tomba capture report exposed per-draw crops and automatic page fallback in the opt-in HD texture enhancement. An independent synthetic diff --git a/docs/internal/upstream/vr-projective-replay.md b/docs/internal/upstream/vr-projective-replay.md new file mode 100644 index 00000000..92deaee4 --- /dev/null +++ b/docs/internal/upstream/vr-projective-replay.md @@ -0,0 +1,22 @@ +# Preserve precise eye poses and clip near-camera geometry + +Source: [FractalEngineer/psxrecomp](https://github.com/FractalEngineer/psxrecomp). +- [efa10eee](https://github.com/FractalEngineer/psxrecomp/commit/efa10eee331bf9d6c5ab4c7ea987bc80e434fa9a) + +Original author: Yves ; retained as commit author +and Co-authored-by on this reconstructed port. + +Adapted: this feature and its tests. Projective PGXP mode opens after the +precision checkpoint and closes before rollback, preserving the real PGXP +generation and prior enabled/CPU-mode state through success and abort. +Excluded: other VR features, game policy, assets and chronological +investigation notes. +Base: `36f1ff3e905b4437cb2933c4f107f7dbe1c825e2` (current upstream master or the preceding feature). + +Validation: Windows/GCC 13.2 Release: precise pose, GTE projective/facing, GPU +projection/provenance, real PGXP scope rollback, sandbox and abort tests pass; +production GPU-history guard passes. Prior Quest 3/VDXR acceptance applies to +the source fork, not this reconstructed branch. + +Consumer: [Medal of Honor v0.1.6](https://github.com/FractalEngineer/Medal-Of-Honor-PSX-Recomp/tree/1957e28a5dc7f5daa79e2b9b6ed867eb06b41ace). +Requires a framework pin bump and rebuild; no game/BIOS regeneration. diff --git a/runtime/include/mod_plugins.h b/runtime/include/mod_plugins.h index 9ddd2c3c..258e7b97 100644 --- a/runtime/include/mod_plugins.h +++ b/runtime/include/mod_plugins.h @@ -790,8 +790,15 @@ typedef struct PSXModRenderView { uint32_t projection; uint32_t projection_h_ref; /* 0 absolute FOV; otherwise scale focal lengths by guest H/ref */ int32_t fx_q16, fy_q16, cx_delta_q16, cy_delta_q16; + /* Optional host-only pose for projective replay. The ordinary path uses + * Q12/integer values; precise 1 preserves sub-unit movement inside replay. */ + uint32_t precise; + double rotation[9], translation_precise[3]; } PSXModRenderView; int psx_mod_render_view(const PSXModRenderView *view); +/* Signed homogeneous projection and near clipping for replayed 3D packets. + * Requires intact PGXP provenance; native gameplay stays architectural. */ +void psx_mod_set_render_projective(int enabled); /* Begin locates both views at one predicted time. End submits only a fresh * complete pair; failed/shed redraws submit zero layers. */ int psx_mod_openxr_enable(int enabled); diff --git a/runtime/include/pgxp.h b/runtime/include/pgxp.h index 8b5d4455..af6bebcc 100644 --- a/runtime/include/pgxp.h +++ b/runtime/include/pgxp.h @@ -304,6 +304,15 @@ int pgxp_load_precise_word(uint32_t addr, uint32_t packed, void pgxp_checkpoint_begin(void); void pgxp_checkpoint_rollback(void); +/* Scoped PGXP mode change for an exact-projection render pass. Open after the + * precision checkpoint and close before rollback so the restored generation + * remains the pre-pass generation. */ +typedef struct PGXPProjectiveScope { + int saved_enabled, saved_cpu_mode, active; +} PGXPProjectiveScope; +int pgxp_projective_scope_begin(PGXPProjectiveScope *scope); +void pgxp_projective_scope_end(PGXPProjectiveScope *scope); + /* Exact address-keyed packet precision, for a renderer preserving completed * static packets around a smaller interpolated drawing section. `expected` * MUST be the actual destination RAM/scratchpad word, read by the caller diff --git a/runtime/src/debug_server.c b/runtime/src/debug_server.c index dc559b45..913b8a93 100644 --- a/runtime/src/debug_server.c +++ b/runtime/src/debug_server.c @@ -9229,14 +9229,15 @@ static void handle_openxr_pose_override(int id,const char *json) { extern void psx_openxr_pose_clear(void); if(json_get_int(json,"clear",0)){ psx_openxr_pose_clear(); send_fmt("{\"id\":%d,\"ok\":true,\"cleared\":1}",id); return; } int x=json_get_int(json,"x_mm",0),y=json_get_int(json,"y_mm",0),z=json_get_int(json,"z_mm",0); - int pitch_deg=json_get_int(json,"pitch_deg",0); + double pitch_deg=(double)json_get_int(json,"pitch_deg",0) + + (double)json_get_int(json,"pitch_mdeg",0)/1000.0; if(!psx_openxr_pose_override((double)x/1000.0,(double)y/1000.0,(double)z/1000.0)){ send_err(id,"pose offset must be finite and within +/-10 m");return; } if(!psx_openxr_pose_pitch_override((double)pitch_deg)){ send_err(id,"pitch_deg must be finite and within +/-90");return; } - send_fmt("{\"id\":%d,\"ok\":true,\"x_mm\":%d,\"y_mm\":%d,\"z_mm\":%d,\"pitch_deg\":%d}",id,x,y,z,pitch_deg); + send_fmt("{\"id\":%d,\"ok\":true,\"x_mm\":%d,\"y_mm\":%d,\"z_mm\":%d,\"pitch_deg\":%.6f}",id,x,y,z,pitch_deg); } static void handle_openxr_input_override(int id,const char *json) { diff --git a/runtime/src/gpu.c b/runtime/src/gpu.c index eb25d051..478af8be 100644 --- a/runtime/src/gpu.c +++ b/runtime/src/gpu.c @@ -4593,6 +4593,27 @@ static void parse_vertex(uint32_t word, int32_t* x, int32_t* y) { extern int gte_geometry_correction_enabled(void); static int s_texture_correction_enabled = 0; static int s_native_wide_projection_correction = 0; +static PGXPProjectiveScope s_render_pgxp_scope; +static int s_render_projective, s_render_scope; +extern int g_psx_render_pass_active; +int gpu_render_projective_enabled(void) { + return s_render_projective && s_render_scope && g_psx_render_pass_active; +} +int gpu_render_projective_begin(void) { + if (!s_render_projective || s_render_scope) return 1; + if (!pgxp_projective_scope_begin(&s_render_pgxp_scope)) return 0; + s_render_scope = 1; + return 1; +} +void gpu_render_projective_end(void) { + if (!s_render_scope) return; + pgxp_projective_scope_end(&s_render_pgxp_scope); + s_render_scope = 0; +} +void psx_mod_set_render_projective(int enabled) { + s_render_projective = !!enabled; + pgxp_set_projection_tracking(s_render_projective || s_native_wide_projection_correction); +} static uint64_t s_native_wide_projection_vertices = 0; extern int gte_precision_load_word(uint32_t addr, uint32_t packed, int32_t *x16, int32_t *y16, uint16_t *z); @@ -4611,7 +4632,7 @@ void psx_mod_set_native_wide_projection_correction(int enabled) { s_native_wide_projection_vertices = 0; /* Signed camera-space provenance is required for correct native-wide * faces, including corners behind the camera. Arm it with the mode. */ - pgxp_set_projection_tracking(s_native_wide_projection_correction); + pgxp_set_projection_tracking(s_render_projective || s_native_wide_projection_correction); gpu_pgxp_rederive_enable(); } @@ -5350,16 +5371,22 @@ static void setup_textured_draw(uint32_t color24, int semi_trans, int raw_textur * Use only intact GTE-derived words, clip their signed homogeneous projection, * and submit through the normal ordered GL passes and VRAM authority path. */ static int native_wide_projective_draw(void) { - if (!gpu_ws_projective_enabled() || gp0_cmd_source_addr == UINT32_MAX) return 0; + const int render_projective = gpu_render_projective_enabled() && + gr_backend() == GR_BACKEND_OPENGL && gl_renderer_projective_supported(); + if ((!render_projective && !gpu_ws_projective_enabled()) || gp0_cmd_source_addr == UINT32_MAX) return 0; + if (render_projective) psx_mod_counter_add("vr.projective.packet",1); const unsigned op=gp0_cmd_buf[0]>>24; const unsigned stride=(op&16u)?3u:2u, n=(op&8u)?4u:3u; PSXProjectedVertex vertices[4]; - int needs_clip=0; + int needs_clip=render_projective; for (unsigned i=0;i>8)&255)/255.0,((color>>16)&255)/255.0}; } if (!needs_clip) return 0; + if (render_projective) psx_mod_counter_add("vr.projective.draw",1); const uint16_t clut=gp0_cmd_buf[2]>>16; const uint16_t tpage=gp0_cmd_buf[2+stride]>>16; set_tpage_from_poly(tpage); const int semi=(op&2u)?(int)semi_transparency:-1; gl_renderer_draw_projected_triangle(vertices,tpage&0x1ff, - (clut&63)*16,(clut>>6)&511,op&1u,semi,s_texture_correction_enabled); + (clut&63)*16,(clut>>6)&511,op&1u,semi,render_projective || s_texture_correction_enabled); if (n==4) { const PSXProjectedVertex second[3]={vertices[2],vertices[1],vertices[3]}; gl_renderer_draw_projected_triangle(second,tpage&0x1ff, - (clut&63)*16,(clut>>6)&511,op&1u,semi,s_texture_correction_enabled); + (clut&63)*16,(clut>>6)&511,op&1u,semi,render_projective || s_texture_correction_enabled); } return 1; } diff --git a/runtime/src/gpu_gl_renderer.c b/runtime/src/gpu_gl_renderer.c index 56db186d..87394aa5 100644 --- a/runtime/src/gpu_gl_renderer.c +++ b/runtime/src/gpu_gl_renderer.c @@ -5330,6 +5330,8 @@ static void native_draw_end(int previous) { sw_set_faithful_authority(previous); static inline void precise_consumed(void) { s_pc_valid = 0; s_pq_valid = 0; s_projected_uv_valid = 0; s_pz_valid = 0; } int gl_renderer_projective_supported(void) { + /* Eye replay owns a separate FBO even when native-wide presentation is off. */ + if (s_pass_active) return s_raster_ok; if (s_rth_on && !rt_on_render_thread() && !rt_held()) return s_raster_ok && s_rthm_cur && !cpu_vram_authoritative(); return s_raster_ok && g_wide_cur && !cpu_vram_authoritative(); diff --git a/runtime/src/gte.cpp b/runtime/src/gte.cpp index 59b7adc4..7bf9998e 100644 --- a/runtime/src/gte.cpp +++ b/runtime/src/gte.cpp @@ -1,3 +1,4 @@ +#include #include "gte.h" #include "gte_view.h" #include "cpu_state.h" @@ -766,6 +767,7 @@ static inline int gte_sxx(int32_t p){ int v=p&0xFFFF; return v>=0x8000? v-0x1000 static inline int gte_syy(int32_t p){ int v=(p>>16)&0xFFFF; return v>=0x8000? v-0x10000:v; } static int32_t gte_h_scaled(const GTEState* gte); +extern "C" int gpu_render_projective_enabled(void); static int32_t s_render_view[3]; static PSXModRenderView s_render_pose; static bool s_render_rotate; @@ -1091,8 +1093,10 @@ void gte_rtps_internal(GTEState* gte, int16_t* V, bool setMac0, uint32_t instr) const bool lm = gte_instr_lm(instr); // Step 1: Matrix multiplication + translation + const bool render_projective = s_render_pose.projection && gpu_render_projective_enabled(); + const bool precise_render = render_projective && s_render_pose.precise; auto dot = [&](int row, int mac_num) { - int64_t acc = ((int64_t)gte->TR[row] + (s_render_rotate ? 0 : s_render_view[row])) * 4096 + + int64_t acc = ((int64_t)gte->TR[row] + ((s_render_rotate || precise_render) ? 0 : s_render_view[row])) * 4096 + (int64_t)gte->RT[row][0] * V[0]; acc = gte_mac44_stage(gte, acc, mac_num); acc += (int64_t)gte->RT[row][1] * V[1]; @@ -1102,6 +1106,7 @@ void gte_rtps_internal(GTEState* gte, int16_t* V, bool setMac0, uint32_t instr) int64_t mac1 = dot(0, 1); int64_t mac2 = dot(1, 2); int64_t mac3 = dot(2, 3); + const int64_t render_source[3] = {mac1, mac2, mac3}; if (s_render_rotate) { const int64_t v[3] = {mac1, mac2, mac3}; int64_t result[3]; @@ -1114,6 +1119,17 @@ void gte_rtps_internal(GTEState* gte, int16_t* V, bool setMac0, uint32_t instr) mac1 = result[0]; mac2 = result[1]; mac3 = result[2]; } + if (precise_render) { + int64_t camera[3]; + for (int row=0;row<3;++row) { + double acc = s_render_pose.translation_precise[row]*4096.0; + for (int col=0;col<3;++col) + acc += render_source[col]*s_render_pose.rotation[row*3+col]; + camera[row] = (int64_t)std::llround(acc); + } + mac1=camera[0];mac2=camera[1];mac3=camera[2]; + } + // MAC1..3 overflow flags observe the unshifted 44-bit accumulator. gte->check_mac_overflow(mac1, 1); gte->check_mac_overflow(mac2, 2); @@ -1218,6 +1234,14 @@ void gte_rtps_internal(GTEState* gte, int16_t* V, bool setMac0, uint32_t instr) ((int64_t)gte->IR2 * s_render_pose.fy_q16 / gte->SZ[3]) * h / ref; } } + if (render_projective && mac3 >= 4096 && shift == 12 && !lm) { + const double scale = s_render_pose.projection_h_ref ? + (double)gte_h_scaled(gte) / s_render_pose.projection_h_ref : 1.0; + sx16 = (int64_t)gte->OFX + s_render_pose.cx_delta_q16 + + (int64_t)std::llround((double)mac1*s_render_pose.fx_q16/mac3*scale); + sy16 = (int64_t)gte->OFY + s_render_pose.cy_delta_q16 + + (int64_t)std::llround((double)mac2*s_render_pose.fy_q16/mac3*scale); + } /* RTPS/RTPT reuse MAC0's overflow flags for each projected X/Y * accumulator before MAC0 is replaced by the depth-cue result. */ gte->check_mac0_overflow(sx16); @@ -1267,6 +1291,24 @@ void gte_rtps_internal(GTEState* gte, int16_t* V, bool setMac0, uint32_t instr) } geom_note((uint32_t)gte->SXY[2], sx16, sy16); + /* A replay eye can see triangles crossing the camera plane. Preserve the + * signed, unsaturated projection alongside the architectural SXY word so + * the GPU clips in homogeneous space before perspective division. */ + if (s_render_pose.projection && gpu_render_projective_enabled() && + !s_gte_replay_sandbox && pgxp_projection_tracking() && shift == 12 && !lm) { + const double camera[3] = {mac1/4096.0, mac2/4096.0, mac3/4096.0}; + const double z = camera[2]; + const double scale = s_render_pose.projection_h_ref ? + (double)gte_h_scaled(gte) / s_render_pose.projection_h_ref : 1.0; + const double fx = s_render_pose.fx_q16 / 65536.0 * scale; + const double fy = s_render_pose.fy_q16 / 65536.0 * scale; + const double cx = (gte->OFX + (double)s_render_pose.cx_delta_q16) / 65536.0; + const double cy = (gte->OFY + (double)s_render_pose.cy_delta_q16) / 65536.0; + const PGXPProjection p = {(float)(cx*z + camera[0]*fx), + (float)(cy*z + camera[1]*fy), (float)z, 1.0f}; + pgxp_gte_set_projection(&p); + } + // Step 5: Depth cueing (MAC0/IR0) — only for last vertex of RTPT or RTPS if (setMac0) { int64_t mac0 = (int64_t)gte->DQA * h_div_sz + gte->DQB; @@ -1502,6 +1544,21 @@ void gte_nclip(GTEState* gte, uint32_t instr) { !s_gte_replay_sandbox && s_speculative_depth == 0) { s_nclip_fallbacks++; } + /* Screen-coordinate saturation can reverse winding at an eye's near + * plane. The signed homogeneous determinant keeps the true facing even + * when one or two corners lie behind the eye. Scope to the render pass. */ + if (s_render_pose.projection && gpu_render_projective_enabled()) { + PGXPProjection p[3]; + if (pgxp_get_gte_projection(0, gte->SXY[0], &p[0]) && + pgxp_get_gte_projection(1, gte->SXY[1], &p[1]) && + pgxp_get_gte_projection(2, gte->SXY[2], &p[2])) { + const double det = (double)p[0].x*((double)p[1].y*p[2].z-(double)p[2].y*p[1].z) + -(double)p[0].y*((double)p[1].x*p[2].z-(double)p[2].x*p[1].z) + +(double)p[0].z*((double)p[1].x*p[2].y-(double)p[2].x*p[1].y); + const int sign = (det > 0) - (det < 0); + if (sign && sign != ((out > 0) - (out < 0))) out = sign; + } + } gte->MAC0 = out; gte->set_error_flag(); } diff --git a/runtime/src/pgxp.cpp b/runtime/src/pgxp.cpp index df92faf0..b5ea3d29 100644 --- a/runtime/src/pgxp.cpp +++ b/runtime/src/pgxp.cpp @@ -542,6 +542,29 @@ extern "C" void pgxp_checkpoint_rollback(void) { } } +extern "C" int pgxp_projective_scope_begin(PGXPProjectiveScope *scope) { + if (!scope || scope->active) return 0; + scope->saved_enabled = s_enabled; + scope->saved_cpu_mode = s_cpu_mode; + if (!scope->saved_enabled) { + pgxp_set_enabled(1); + if (!s_enabled) { + s_cpu_mode = scope->saved_cpu_mode; + return 0; + } + } + s_cpu_mode = 1; + scope->active = 1; + return 1; +} + +extern "C" void pgxp_projective_scope_end(PGXPProjectiveScope *scope) { + if (!scope || !scope->active) return; + s_cpu_mode = scope->saved_cpu_mode; + if (!scope->saved_enabled) pgxp_set_enabled(0); + scope->active = 0; +} + static inline int pv_live(const PGXPValue *pv) { return pv && pv->gen == s_gen && (pv->flags & PGXP_F_VXY) != 0; } diff --git a/runtime/src/render_pass.c b/runtime/src/render_pass.c index 0c1d44ed..71d97ec4 100644 --- a/runtime/src/render_pass.c +++ b/runtime/src/render_pass.c @@ -19,6 +19,7 @@ #include "render_pass.h" #include +#include #include #include #include @@ -40,6 +41,9 @@ #include "gte_view.h" #include "stereo_shed_policy.h" +extern int gpu_render_projective_begin(void); +extern void gpu_render_projective_end(void); + extern uint8_t *memory_get_ram_ptr(void); extern uint8_t *memory_get_scratchpad_ptr(void); /* Live main-RAM geometry (psx_ram_geometry.c): 2 MiB retail, or 8 MiB with @@ -396,6 +400,7 @@ void render_pass_get_stats(RenderPassStats *out) { int render_pass_verification_enabled(void) { return verify_on(); } void render_pass_reset_session(void) { + psx_mod_set_render_projective(0); s_netplay_allowed = 0; memset(&s_stats, 0, sizeof s_stats); memset(g_render_pass_dropped_writes, 0, sizeof g_render_pass_dropped_writes); @@ -493,13 +498,19 @@ int psx_mod_render_view_offset(int32_t x, int32_t y, int32_t z) { int psx_mod_render_view(const PSXModRenderView *view) { if (!g_psx_render_pass_active || !view || view->struct_size < sizeof *view || - view->projection > 1u || view->projection_h_ref > 65535u) return 0; + view->projection > 1u || view->precise > 1u || view->projection_h_ref > 65535u) return 0; /* Bound products well inside int64. Rigid matrices are supplied by caller. */ for (int i = 0; i < 9; ++i) if (view->rotation_q12[i] < -4096 || view->rotation_q12[i] > 4096) return 0; for (int i = 0; i < 3; ++i) if (view->translation[i] < -65536 || view->translation[i] > 65536) return 0; if (view->projection && (view->fx_q16 <= 0 || view->fy_q16 <= 0)) return 0; + if (view->precise) { + for (int i=0;i<9;++i) + if (!isfinite(view->rotation[i]) || fabs(view->rotation[i])>1.001) return 0; + for (int i=0;i<3;++i) + if (!isfinite(view->translation_precise[i]) || fabs(view->translation_precise[i])>65536) return 0; + } gte_render_pose_set(view); return 1; } @@ -918,6 +929,13 @@ static int render_transaction(struct CPUState *cpu, const PSXModRenderPass *pass transaction_end(eye, 0, 0); return pass_refuse(s_checkpoint_failure ? s_checkpoint_failure : "checkpoint", status); } + if (eye >= 0 && !s_capture_only && !gpu_render_projective_begin()) { + gl_renderer_pass_begin_diag(&s_attempt.gl); + status = transaction_status(transaction_kind(eye)); + transaction_end(eye, 0, 0); + checkpoint_restore(cpu); + return pass_refuse("projective_pgxp", status); + } if (reuse_baseline) s_stereo.checkpoint_reuses++; else { s_stereo.checkpoint_saves++; s_stereo.checkpoint_ram_bytes += s_ck.ram_bytes; } if (verify_on()) hash_before = state_hash(cpu); @@ -985,6 +1003,7 @@ static int render_transaction(struct CPUState *cpu, const PSXModRenderPass *pass } te = gl_renderer_perf_ticks(); local_scope_set(0); + gpu_render_projective_end(); checkpoint_restore(cpu); psx_cycle_freeze_end(&s_freeze); s_nesting = 0; diff --git a/runtime/src/vr_pose_math.c b/runtime/src/vr_pose_math.c index 5a09cba4..1c6a295b 100644 --- a/runtime/src/vr_pose_math.c +++ b/runtime/src/vr_pose_math.c @@ -25,10 +25,12 @@ int vr_pose_to_view(const double q[4], const double p[3], for(int row=0;row<3;row++) { for(int col=0;col<3;col++) { double a=0;for(int k=0;k<3;k++)a+=e[k*3+row]*o[k*3+col]; - v.rotation_q12[row*3+col]=(int32_t)lround(a*sign[row]*sign[col]*4096); + v.rotation[row*3+col]=a*sign[row]*sign[col]; + v.rotation_q12[row*3+col]=(int32_t)lround(v.rotation[row*3+col]*4096); } double a=0;for(int k=0;k<3;k++)a+=e[k*3+row]*(op[k]-p[k]); a*=sign[row]*units;if(fabs(a)>65536) return 0; + v.translation_precise[row]=a; v.translation[row]=(int32_t)lround(a); } double fx=w/(r-l),fy=h/(u-d),cx=-l*fx-w*.5,cy=u*fy-h*.5; @@ -36,6 +38,7 @@ int vr_pose_to_view(const double q[4], const double p[3], for(int i=0;i<4;i++) if(!isfinite(pix[i]) || fabs(pix[i])>32767) return 0; v.fx_q16=(int32_t)lround(fx*65536);v.fy_q16=(int32_t)lround(fy*65536); v.cx_delta_q16=(int32_t)lround(cx*65536);v.cy_delta_q16=(int32_t)lround(cy*65536); + v.precise=1; *out=v;return 1; } int vr_pose_to_transform(const double q[4],const double p[3], diff --git a/runtime/tests/gpu_exec_stubs.inc b/runtime/tests/gpu_exec_stubs.inc index 249ff1ed..72c8b16a 100644 --- a/runtime/tests/gpu_exec_stubs.inc +++ b/runtime/tests/gpu_exec_stubs.inc @@ -7,6 +7,28 @@ uint64_t s_frame_count; uint64_t psx_cycle_count; /* gpu_timeline.c stamps notes with it */ int g_exec_phase; +int g_psx_render_pass_active; +void psx_mod_counter_add(const char *name, uint32_t delta) { (void)name; (void)delta; } +static int fixture_pgxp_enabled; +int pgxp_enabled(void) { return fixture_pgxp_enabled; } +static int fixture_pgxp_cpu_mode; +int pgxp_cpu_mode(void) { return fixture_pgxp_cpu_mode; } +void pgxp_set_cpu_mode(int enabled) { fixture_pgxp_cpu_mode=enabled; } +int pgxp_projective_scope_begin(PGXPProjectiveScope *scope) { + if (!scope || scope->active) return 0; + scope->saved_enabled=fixture_pgxp_enabled; + scope->saved_cpu_mode=fixture_pgxp_cpu_mode; + fixture_pgxp_enabled=1; + fixture_pgxp_cpu_mode=1; + scope->active=1; + return 1; +} +void pgxp_projective_scope_end(PGXPProjectiveScope *scope) { + if (!scope || !scope->active) return; + fixture_pgxp_cpu_mode=scope->saved_cpu_mode; + fixture_pgxp_enabled=scope->saved_enabled; + scope->active=0; +} uint32_t g_debug_current_func_addr; uint32_t g_debug_last_store_pc; CPUState *debug_cpu_ptr; @@ -141,7 +163,6 @@ void psx_fatal_halt(const char *reason) { assert(!"psx_fatal_halt called"); } -static int fixture_pgxp_enabled; void pgxp_set_enabled(int enabled) { fixture_pgxp_enabled = enabled; } int gte_geometry_correction_enabled(void) { return g_test_geometry_correction; } int pgxp_get_precise_vertex(uint32_t addr, uint32_t packet_word, diff --git a/runtime/tests/test_gpu_gp0_history_guard.py b/runtime/tests/test_gpu_gp0_history_guard.py index db644c3e..fa7b3629 100644 --- a/runtime/tests/test_gpu_gp0_history_guard.py +++ b/runtime/tests/test_gpu_gp0_history_guard.py @@ -236,11 +236,6 @@ GrBackend gr_backend(void) { return GR_BACKEND_SOFTWARE; } int gl_renderer_texture_banks_supported(void) { return 0; } int gl_renderer_select_texture_bank(uint16_t id) { return id == 0; } int gl_renderer_select_texture_bank_live_clut(uint16_t id) { return id == 0; } -void gl_renderer_note_wide_triangle_recovery(int enabled) { (void)enabled; } -int gl_renderer_projective_supported(void) { return 0; } -void gl_renderer_draw_projected_triangle(const PSXProjectedVertex vertices[3], - uint16_t texpage, uint16_t clut_x, uint16_t clut_y, int raw, int semi, int perspective) -{ (void)vertices; (void)texpage; (void)clut_x; (void)clut_y; (void)raw; (void)semi; (void)perspective; abort(); } uint16_t mod_texture_packet_bank(uint32_t p, const uint32_t *words, uint32_t n) { (void)p; (void)words; (void)n; return 0; } int mod_texture_packet_precision(uint32_t p, float q[3], float xy[6]) @@ -263,6 +258,12 @@ void crash_trace_note_gpu_fatal(uint32_t opcode, uint32_t word) { (void)opcode; void event_ring_note_gpu_irq(uint32_t stat) { (void)stat; } int g_psx_vram_dirty_tracking = 0; int g_exec_phase = 0; +int g_psx_render_pass_active = 0; +int pgxp_enabled(void) { return 0; } +int pgxp_cpu_mode(void) { return 0; } +void pgxp_set_cpu_mode(int enabled) { if (enabled) abort(); } +void psx_mod_counter_add(const char *name, uint32_t delta) +{ (void)name; (void)delta; abort(); } void gpu_vram_dirty_mark_all(void) {} void gpu_vram_dirty_mark_row_impl(uint32_t row) { (void)row; } void gpu_vram_dirty_mark_rect(uint32_t x, uint32_t y, uint32_t w, uint32_t h) @@ -273,6 +274,7 @@ void text_xlate_vram_upload(uint16_t x, uint16_t y, uint16_t w, uint16_t h) int ws_cull_should_keep(uint32_t addr) { (void)addr; return 1; } int ws_ui_group_should_keep(uint32_t addr) { (void)addr; return 1; } uint32_t psx_mod_gpu_dma_resolve_address(uint32_t address) { return address; } +int psx_mod_gpu_dma_memory_contains(uint32_t addr, uint32_t len) { (void)addr; (void)len; return 0; } uint32_t psx_gpu_packet_key(uint32_t address) { return address & 0x001FFFFCu; } void ws_ui_group_assign(WsUiGroupItem *items, size_t count, int32_t display_width, int dense_menu, int in_place) @@ -289,6 +291,10 @@ int gte_nclip_native_wide_previous_sign(int32_t mac0, int *sign) int pgxp_projection_tracking(void) { return 0; } void pgxp_set_projection_tracking(int enabled) { if (enabled) abort(); } +int pgxp_projective_scope_begin(PGXPProjectiveScope *scope) +{ (void)scope; abort(); } +void pgxp_projective_scope_end(PGXPProjectiveScope *scope) +{ (void)scope; abort(); } int pgxp_load_projection(uint32_t addr, uint32_t packed, PGXPProjection *out) { (void)addr; (void)packed; (void)out; abort(); } int gl_renderer_projective_supported(void) { abort(); } @@ -308,10 +314,6 @@ int gte_fg_source_enabled(void) { return 0; } int gte_fg_source_lookup(uint32_t packed, GteFgSrc out[2]) { (void)packed; (void)out; abort(); } void pgxp_set_enabled(int enabled) { (void)enabled; } -void pgxp_set_projection_tracking(int enabled) { (void)enabled; } -int pgxp_projection_tracking(void) { return 0; } -int pgxp_load_projection(uint32_t addr, uint32_t packed, PGXPProjection *out) -{ (void)addr; (void)packed; (void)out; return 0; } int pgxp_get_gte_sxy_checked(uint32_t slot, uint32_t packed, int require_valid, int32_t *x16, int32_t *y16) { (void)slot; (void)packed; (void)require_valid; (void)x16; (void)y16; return 0; } diff --git a/runtime/tests/test_gte_register_access.cpp b/runtime/tests/test_gte_register_access.cpp index 0ed00e40..c8f1f279 100644 --- a/runtime/tests/test_gte_register_access.cpp +++ b/runtime/tests/test_gte_register_access.cpp @@ -62,6 +62,8 @@ extern "C" void gte_test_execute_reference(CPUState *cpu, uint32_t cmd); /* gte.cpp runtime dependencies that are irrelevant to register-transfer tests. */ extern "C" int gpu_ws_present_native_43(void) { return 0; } static int g_test_precise_nclip_enabled; +static int g_test_render_projective; +extern "C" int gpu_render_projective_enabled(void) { return g_test_render_projective; } extern "C" int gpu_ws_precise_nclip_enabled(void) { return g_test_precise_nclip_enabled; } @@ -1413,6 +1415,54 @@ int main() { } gte_set_fov_scale(1,1); pgxp_set_projection_tracking(0); + // VR homogeneous provenance spans the eye plane, including fractional MACs. + PSXModRenderView eye = {}; eye.struct_size=sizeof eye; eye.projection=1; + eye.rotation_q12[0]=eye.rotation_q12[4]=eye.rotation_q12[8]=4096; + eye.fx_q16=200*65536; eye.fy_q16=100*65536; + eye.cx_delta_q16=10*65536; eye.cy_delta_q16=-5*65536; + gte_render_pose_set(&eye); pgxp_set_projection_tracking(1); + for (int depth : {-158,-1,0,1,149,900}) { + GTEState native{}, enhanced{}; + native.RT[0][0]=native.RT[1][1]=native.RT[2][2]=4096; + native.RT[0][1]=1; native.V0[0]=100; native.V0[1]=30; + native.TR[2]=depth; native.H=400; + native.OFX=256*65536; native.OFY=120*65536; + enhanced=native; + g_test_render_projective=0; + PSXRecomp::GTE::gte_rtps(&native,0x0180001u); + g_test_render_projective=1; + PSXRecomp::GTE::gte_rtps(&enhanced,0x0180001u); + PGXPProjection p; + CHECK(!std::memcmp(&native,&enhanced,sizeof native)); + CHECK(pgxp_get_gte_projection(2,enhanced.SXY[2],&p)); + CHECK(p.z==depth && p.near_z==1); + CHECK(std::fabs(p.x-(266.0*depth+(100+30.0/4096)*200))<0.02); + CHECK(std::fabs(p.y-(115.0*depth+3000))<0.02); + } + // A tenth of a camera unit survives even though the architectural pose rounds to zero. + eye.precise=1; eye.rotation[0]=eye.rotation[4]=eye.rotation[8]=1; + eye.translation[0]=17; // precision replaces the integer pose; never adds it twice. + eye.translation_precise[0]=.1; gte_render_pose_set(&eye); + GTEState fractional{}; + fractional.RT[0][0]=fractional.RT[1][1]=fractional.RT[2][2]=4096; + fractional.V0[2]=1000; fractional.H=400; + PSXRecomp::GTE::gte_rtps(&fractional,0x0180001u); + PGXPProjection exact; + CHECK(pgxp_get_gte_projection(2,fractional.SXY[2],&exact)); + CHECK(std::fabs(exact.x-(10*1000+20))<.05); + eye.precise=0;eye.translation[0]=0;gte_render_pose_set(&eye); + for (int winding : {1,-1}) { + GTEState face{}; + face.RT[0][0]=face.RT[1][1]=face.RT[2][2]=4096; + face.V0[0]=-20*winding;face.V0[1]=10;face.V0[2]=100; + face.V1[0]=20*winding;face.V1[1]=10;face.V1[2]=100; + face.V2[1]=10;face.V2[2]=-10;face.H=400; + PSXRecomp::GTE::gte_rtpt(&face,0x0280030u); + PSXRecomp::GTE::gte_nclip(&face,0x1400006u); + CHECK((face.MAC0>0)-(face.MAC0<0)==winding); + } + g_test_render_projective=0; eye={}; gte_render_pose_set(&eye); + pgxp_set_projection_tracking(0); std::puts("PASS: canonical GTE register helpers match GTEState transfer oracle"); return 0; } diff --git a/runtime/tests/test_native_wide_projection.c b/runtime/tests/test_native_wide_projection.c index e11b399b..3feef6a7 100644 --- a/runtime/tests/test_native_wide_projection.c +++ b/runtime/tests/test_native_wide_projection.c @@ -165,6 +165,29 @@ int main(void) { fixture_projective_supported=1;gp0_cmd_source_addr=UINT32_MAX; assert(!native_wide_projective_draw()); assert(fixture_projective_draws==2); + /* VR replay uses the clipping transport at ordinary 4:3, including + * unsaturated positive vertices (smooth motion), only inside its pass. */ + gp0_cmd_source_addr=0x10004u; ws_cfg_num=4;ws_cfg_den=3; + fixture_projective_supported=1; + fixture_pgxp_enabled=0;fixture_pgxp_cpu_mode=0; + psx_mod_set_render_projective(1); + assert(!fixture_pgxp_enabled && fixture_projection_tracking); + assert(!native_wide_projective_draw()); + assert(gpu_render_projective_begin()); + assert(fixture_pgxp_enabled && fixture_pgxp_cpu_mode); + g_psx_render_pass_active=1; + assert(native_wide_projective_draw()); + fixture_homogeneous[1].valid=0; + assert(!native_wide_projective_draw()); + fixture_homogeneous[1].valid=1; + fixture_backend=GR_BACKEND_SOFTWARE; + assert(!native_wide_projective_draw()); + fixture_backend=GR_BACKEND_OPENGL; + g_psx_render_pass_active=0; + assert(!native_wide_projective_draw()); + gpu_render_projective_end(); + assert(!fixture_pgxp_enabled && !fixture_pgxp_cpu_mode); + psx_mod_set_render_projective(0); puts("native_wide_projection_test: PASS"); return 0; } diff --git a/runtime/tests/test_pgxp.cpp b/runtime/tests/test_pgxp.cpp index 6128e68f..7150f6fb 100644 --- a/runtime/tests/test_pgxp.cpp +++ b/runtime/tests/test_pgxp.cpp @@ -113,6 +113,24 @@ int main(void) { pgxp_set_tolerance(-1.0f); pgxp_set_cpu_mode(0); + /* PGXP mode is opened after the precision checkpoint and closed before + * rollback. The real generation and prior mode must survive the pass. */ + { + pgxp_set_enabled(0); + const uint32_t gen = pgxp_test_generation(); + pgxp_checkpoint_begin(); + PGXPProjectiveScope scope = {}; + CHECK(pgxp_projective_scope_begin(&scope)); + CHECK(pgxp_enabled() && pgxp_cpu_mode()); + CHECK(!pgxp_projective_scope_begin(&scope)); + produce_at(ADDR_A); + pgxp_projective_scope_end(&scope); + pgxp_checkpoint_rollback(); + CHECK(!pgxp_enabled() && !pgxp_cpu_mode()); + CHECK(pgxp_test_generation() == gen); + } + pgxp_set_enabled(1); + /* --- SWC2 produce -> GPU consume (the perspective-texturing spine) --- */ produce_at(ADDR_A); { diff --git a/runtime/tests/test_render_pass_abort.c b/runtime/tests/test_render_pass_abort.c index f161d86f..58eb58aa 100644 --- a/runtime/tests/test_render_pass_abort.c +++ b/runtime/tests/test_render_pass_abort.c @@ -225,6 +225,9 @@ void gte_render_view_get(int32_t xyz[3]) { memcpy(xyz, s_view, sizeof s_view); } void gte_render_view_set(const int32_t xyz[3]) { memcpy(s_view, xyz, sizeof s_view); } static PSXModRenderView s_pose; void gte_render_pose_get(PSXModRenderView *v) { *v = s_pose; memcpy(v->translation, s_view, sizeof s_view); } +int gpu_render_projective_begin(void) { return 1; } +void gpu_render_projective_end(void) {} +void psx_mod_set_render_projective(int enabled) { (void)enabled; } void gte_render_pose_set(const PSXModRenderView *v) { s_pose = *v; memcpy(s_view, v->translation, sizeof s_view); } int gl_renderer_stereo_publish(uint64_t id, uint64_t cycle, const int32_t view[2][3]) { (void)cycle; (void)view; diff --git a/runtime/tests/test_render_pass_sandbox.c b/runtime/tests/test_render_pass_sandbox.c index d54332d4..8c17493b 100644 --- a/runtime/tests/test_render_pass_sandbox.c +++ b/runtime/tests/test_render_pass_sandbox.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "cpu_state.h" @@ -724,6 +725,14 @@ static void test_journal(void) { #undef POLICY } +static int s_projective_scope, s_projective_refuse; +int gpu_render_projective_begin(void) { + if (s_projective_refuse) return 0; + ++s_projective_scope; + return 1; +} +void gpu_render_projective_end(void) { s_projective_scope=0; } +void psx_mod_set_render_projective(int enabled) { (void)enabled; } static int s_eye_calls, s_eye_decline = -1; static int pair_draw(CPUState *cpu, void *user, uint32_t eye) { (void)user; @@ -739,6 +748,15 @@ static int pair_draw(CPUState *cpu, void *user, uint32_t eye) { v.projection = 1; v.fx_q16 = 200 << 16; v.fy_q16 = 100 << 16; v.translation[0] = eye ? 24 : -24; CHECK(psx_mod_render_view(&v), "full scoped pose accepted"); + v.precise=1; v.rotation[2]=1;v.rotation[4]=1;v.rotation[6]=-1; + v.translation_precise[0]=v.translation[0]+.125; + CHECK(psx_mod_render_view(&v), "fractional scoped pose accepted"); + v.translation_precise[1]=NAN; + CHECK(!psx_mod_render_view(&v), "nonfinite precise pose rejected"); + v.translation_precise[1]=0; + v.rotation[0]=2; + CHECK(!psx_mod_render_view(&v), "unbounded precise rotation rejected"); + cpu->gpr[8] = eye + 1; s_ram[0x456] = (uint8_t)eye; psx_advance_cycles(5000u + eye); @@ -765,7 +783,7 @@ static void test_stereo(void) { "one baseline per pair, reused by the second eye"); CHECK(stats.checkpoint_ram_bytes > 0, "the captured baseline reports the RAM it copied"); - CHECK(s_prec_open == 0, "precision tracking is armed and rolled back per eye"); + CHECK(s_prec_open == 0 && s_projective_scope == 0, "precision tracking is armed and rolled back per eye"); CHECK(cpu.gpr[8] == 123 && s_ram[0x456] == 0x77 && psx_cycle_count == cycle, "live state restored after the pair"); CHECK(s_view[0] == 0 && stats.eye_view[0][0] == -24 && stats.eye_view[1][0] == 24, @@ -773,6 +791,14 @@ static void test_stereo(void) { CHECK(s_pose.struct_size == 0 && !s_pose.projection, "pose restored after pair"); CHECK(!psx_mod_render_view_offset(1, 0, 0), "view rejected outside render callback"); published = s_stereo_published; + s_projective_refuse = 1; + CHECK(!psx_mod_render_stereo(&cpu, &f, pair_draw, NULL) && + s_stereo_published == published && s_stereo_mask == 0, + "projective PGXP refusal rolls the checkpoint back without publication"); + CHECK(s_prec_open == 0 && s_projective_scope == 0 && cpu.gpr[8] == 123 && + s_ram[0x456] == 0x77 && psx_cycle_count == cycle, + "projective refusal restores the complete live state"); + s_projective_refuse = 0; s_eye_decline = 1; CHECK(!psx_mod_render_stereo(&cpu, &f, pair_draw, NULL) && s_stereo_published == published && s_stereo_mask == 0, diff --git a/runtime/tests/test_vr_pose_math.c b/runtime/tests/test_vr_pose_math.c index 01af3db3..f5814cee 100644 --- a/runtime/tests/test_vr_pose_math.c +++ b/runtime/tests/test_vr_pose_math.c @@ -11,6 +11,10 @@ int main(void) { CHECK(v.rotation_q12[0]==4096 && v.rotation_q12[4]==4096 && v.rotation_q12[8]==4096); CHECK(v.fx_q16==256*65536 && v.fy_q16==240*65536); CHECK(v.cx_delta_q16==0 && v.cy_delta_q16==0); + CHECK(v.precise && fabs(v.translation_precise[0]-33.5)<1e-12); + const double tiny[3]={.0001,0,0}; + CHECK(vr_pose_to_view(q,tiny,q,origin,f,1000,512,240,&v)); + CHECK(v.translation[0]==0 && fabs(v.translation_precise[0]+.1)<1e-12); double move[3]={0,.1,-.2}; CHECK(vr_pose_to_view(q,move,q,origin,f,1000,512,240,&v)); CHECK(v.translation[1]==100 && v.translation[2]==-200); From e7c3c57c13324dba0a2c5778b0199a4ea7a53d72 Mon Sep 17 00:00:00 2001 From: Shokunin <30949000+tetrisgm@users.noreply.github.com> Date: Thu, 22 Oct 2026 15:26:37 -0700 Subject: [PATCH 25/29] DuckStation texture pack: stop quadratic work on every VRAM write --- runtime/src/duckstation_texture_pack.cpp | 32 ++++++++++++++++++------ 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/runtime/src/duckstation_texture_pack.cpp b/runtime/src/duckstation_texture_pack.cpp index 12c5e730..e577638f 100644 --- a/runtime/src/duckstation_texture_pack.cpp +++ b/runtime/src/duckstation_texture_pack.cpp @@ -589,14 +589,16 @@ bool finalize_source(DuckTexturePack& p, Source& source, bool keep_observations } return success; } -bool has_residency(const DuckTexturePack& p, const Source* source) { - for (const auto& upload : p.uploads) if (upload.source.get() == source) return true; - for (const auto& page : p.pages) if (page.source.get() == source) return true; - return false; -} void retire_unused(DuckTexturePack& p) { + /* One pass over the residency records, then one over the sources: this + * runs on every VRAM write, and a per-source scan of up to kMaxUploads + * records each cost ~140 ms a frame after a course load filled them. */ + std::unordered_set resident; + resident.reserve(p.uploads.size() + p.pages.size()); + for (const auto& upload : p.uploads) resident.insert(upload.source.get()); + for (const auto& page : p.pages) resident.insert(page.source.get()); for (auto it = p.sources.begin(); it != p.sources.end();) { - if (has_residency(p,it->get())) { ++it; continue; } + if (resident.count(it->get())) { ++it; continue; } if (!finalize_source(p,**it)) { /* Keep failed pending records bounded and visible to the caller. */ p.lifecycle_error = p.lifecycle_error.empty() ? "texture dump could not be finalized" : p.lifecycle_error; @@ -826,11 +828,21 @@ void duck_texture_pack_invalidate(DuckTexturePack* p, uint16_t x, uint16_t y, ui const std::array regions{{{px,py,w0,h0},{0,py,unsigned(w)-w0,h0}, {px,0,w0,unsigned(h)-h0},{0,0,unsigned(w)-w0,unsigned(h)-h0}}}; ++p->revision; + bool released = false; /* a residency record dropped or split */ for (const auto& r : regions) if (r.w && r.h) { + /* Most writes (render targets, CLUT animation) touch no tracked + * upload: skip rebuilding the list then. */ + const bool upload_hit = std::any_of(p->uploads.begin(), p->uploads.end(), + [&](const Upload& u) { const Rect c = intersection(u.active, r); return c.w && c.h; }); + if (upload_hit) { std::vector survivors; survivors.reserve(p->uploads.size()); for (const auto& upload : p->uploads) { const Rect cut = intersection(upload.active,r); - if (!cut.w || !cut.h) { if (survivors.size() < kMaxUploads) survivors.push_back(upload); continue; } + if (!cut.w || !cut.h) { + if (survivors.size() < kMaxUploads) survivors.push_back(upload); else released = true; + continue; + } + released = true; if (upload.splits >= p->config.max_vram_write_splits || contains(r,upload.active)) continue; const Rect a = upload.active; const Rect fragments[] = { @@ -842,12 +854,15 @@ void duck_texture_pack_invalidate(DuckTexturePack* p, uint16_t x, uint16_t y, ui survivors.push_back({upload.source,fragment,upload.splits+1}); } p->uploads.swap(survivors); + } + const size_t pages_before = p->pages.size(); p->pages.erase(std::remove_if(p->pages.begin(),p->pages.end(),[&](const PageSource& page) { if (intersects(page.source->rect,r)) return true; for (const auto& record : page.source->records) if (record.depth < 2 && intersects({record.clut_x,record.clut_y,record.palette_size,1},r)) return true; return false; }),p->pages.end()); + released = released || p->pages.size() != pages_before; p->rect_cache.erase(std::remove_if(p->rect_cache.begin(), p->rect_cache.end(), [&](const RectHash& v){ return intersects(v.rect,r); }), p->rect_cache.end()); p->match_cache.erase(std::remove_if(p->match_cache.begin(), p->match_cache.end(), [&](const MatchCache& c) { const auto& q = c.query; @@ -856,7 +871,8 @@ void duck_texture_pack_invalidate(DuckTexturePack* p, uint16_t x, uint16_t y, ui return intersects(page,r) || (q.depth < 2 && intersects(clut,r)); }), p->match_cache.end()); } - retire_unused(*p); + /* Only a dropped record can leave a source unreferenced. */ + if (released) retire_unused(*p); } catch (...) { /* Allocation/finalization failure must never preserve stale residency * after a native write. Pending immutable observations remain owned. */ From ed5b74f4eed75744f1f37b631dea83311482f028 Mon Sep 17 00:00:00 2001 From: Shokunin <30949000+tetrisgm@users.noreply.github.com> Date: Thu, 22 Oct 2026 15:26:42 -0700 Subject: [PATCH 26/29] Windows: give concurrent sleepers independent timers --- runtime/CMakeLists.txt | 4 ++ runtime/src/host_time.c | 59 +++++++++++++++++--------- runtime/tests/test_host_time_threads.c | 57 +++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 19 deletions(-) create mode 100644 runtime/tests/test_host_time_threads.c diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index f1c45888..5e6cc7c1 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -26,6 +26,10 @@ if(BUILD_TESTING) # Windows UTF-8 text boundary (issue #371): the helpers legacy Windows # relies on, and — with the shipped manifest — a UTF-8 process code page. if(WIN32) + add_executable(host_time_threads_test tests/test_host_time_threads.c src/host_time.c) + target_include_directories(host_time_threads_test PRIVATE include) + add_test(NAME host_time_threads_test COMMAND host_time_threads_test) + set_tests_properties(host_time_threads_test PROPERTIES TIMEOUT 15) add_executable(win_utf8_helpers_test tests/test_win_utf8.cpp) target_include_directories(win_utf8_helpers_test PRIVATE include) add_test(NAME win_utf8_helpers_test COMMAND win_utf8_helpers_test) diff --git a/runtime/src/host_time.c b/runtime/src/host_time.c index c70cd94b..4fb5f435 100644 --- a/runtime/src/host_time.c +++ b/runtime/src/host_time.c @@ -21,31 +21,49 @@ #endif static LARGE_INTEGER s_qpc_freq; -static int s_qpc_freq_init; -static HANDLE s_sleep_timer; -static int s_sleep_timer_init; +static INIT_ONCE s_time_once = INIT_ONCE_STATIC_INIT; +static DWORD s_sleep_timer_slot = FLS_OUT_OF_INDEXES; -static void sleep_timer_ensure(void) +static void CALLBACK sleep_timer_close(void *timer) { - if (s_sleep_timer_init) - return; - s_sleep_timer_init = 1; - s_sleep_timer = + if (timer) CloseHandle((HANDLE)timer); +} + +static BOOL CALLBACK time_initialize(PINIT_ONCE once, PVOID parameter, PVOID *context) +{ + (void)once; (void)parameter; (void)context; + if (!QueryPerformanceFrequency(&s_qpc_freq)) s_qpc_freq.QuadPart = 0; + /* An auto-reset timer has one pending deadline and wakes one waiter. + * Sharing it lets a render/present worker cancel the guest pacer's wait. + * FLS also releases the handle when its thread/guest fiber is destroyed. */ + s_sleep_timer_slot = FlsAlloc(sleep_timer_close); + return TRUE; +} + +static HANDLE sleep_timer_get(void) +{ + HANDLE timer; + if (!InitOnceExecuteOnce(&s_time_once, time_initialize, NULL, NULL) || + s_sleep_timer_slot == FLS_OUT_OF_INDEXES) + return NULL; + timer = (HANDLE)FlsGetValue(s_sleep_timer_slot); + if (timer) return timer; + timer = CreateWaitableTimerExW(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS); - if (!s_sleep_timer) - s_sleep_timer = CreateWaitableTimerW(NULL, FALSE, NULL); + if (!timer) timer = CreateWaitableTimerW(NULL, FALSE, NULL); + if (timer && !FlsSetValue(s_sleep_timer_slot, timer)) { + CloseHandle(timer); + return NULL; + } + return timer; } uint64_t psx_host_mono_ms(void) { LARGE_INTEGER counter; - if (!s_qpc_freq_init) { - if (!QueryPerformanceFrequency(&s_qpc_freq)) - s_qpc_freq.QuadPart = 0; - s_qpc_freq_init = 1; - } + InitOnceExecuteOnce(&s_time_once, time_initialize, NULL, NULL); if (s_qpc_freq.QuadPart == 0) return (uint64_t)GetTickCount64(); if (!QueryPerformanceCounter(&counter)) @@ -57,18 +75,21 @@ void psx_host_sleep_micros(unsigned usec) { LARGE_INTEGER due; LONGLONG hundred_ns; + HANDLE timer; if (usec == 0u) return; - sleep_timer_ensure(); - if (s_sleep_timer) { + timer = sleep_timer_get(); + if (timer) { hundred_ns = -((LONGLONG)usec * 10LL); if (hundred_ns >= 0) hundred_ns = -1LL; due.QuadPart = hundred_ns; - if (SetWaitableTimer(s_sleep_timer, &due, 0, NULL, NULL, FALSE)) { - (void)WaitForSingleObject(s_sleep_timer, INFINITE); + /* Bounded: a missed signal costs at most the sleep plus 2 ms, never + * an indefinitely parked thread. */ + if (SetWaitableTimer(timer, &due, 0, NULL, NULL, FALSE)) { + (void)WaitForSingleObject(timer, (DWORD)(usec / 1000u) + 2u); return; } } diff --git a/runtime/tests/test_host_time_threads.c b/runtime/tests/test_host_time_threads.c new file mode 100644 index 00000000..a8eae238 --- /dev/null +++ b/runtime/tests/test_host_time_threads.c @@ -0,0 +1,57 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "host_time.h" + +static HANDLE start_event; +static volatile LONG errors; +static DWORD WINAPI sleeper(void *arg) { + (void)arg; + WaitForSingleObject(start_event, INFINITE); + uint64_t previous = psx_host_mono_ms(); + for (unsigned i=0; i<250; ++i) { + psx_host_sleep_micros(1000); + uint64_t now = psx_host_mono_ms(); + if (now < previous) InterlockedIncrement(&errors); + previous = now; + } + return 0; +} +static int run_wave(void) { + HANDLE threads[8]; + start_event = CreateEventW(NULL, TRUE, FALSE, NULL); + if (!start_event) return 2; + for (unsigned i=0; i<8; ++i) { + threads[i] = CreateThread(NULL, 0, sleeper, NULL, 0, NULL); + if (!threads[i]) return 2; + } + SetEvent(start_event); + DWORD result = WaitForMultipleObjects(8, threads, TRUE, 10000); + if (result != WAIT_OBJECT_0) { + puts("FAIL: concurrent sleepers lost a wakeup"); + return 1; + } + for (unsigned i=0; i<8; ++i) CloseHandle(threads[i]); + CloseHandle(start_event); + return errors ? 1 : 0; +} +int main(void) { + /* The cold wave still tests concurrent InitOnce initialization. Measure + * handle growth only after process/CRT/Windows lazy initialization, then + * repeat whole thread lifetimes: a leaked caller timer grows every wave. */ + int result = run_wave(); + if (result) return result; + DWORD before, after; + if (!GetProcessHandleCount(GetCurrentProcess(), &before)) return 2; + for (int wave = 0; wave < 3; ++wave) { + result = run_wave(); + if (result) return result; + } + if (!GetProcessHandleCount(GetCurrentProcess(), &after) || after > before) { + printf("FAIL: sleeper timer handles leaked after thread exit: before=%lu after=%lu\n", before, after); + return 1; + } + if (errors) return 1; + puts("host time concurrent sleepers: PASS"); + return 0; +} From 74c7cbf94ada5944acacc01d20eca4e57dde628e Mon Sep 17 00:00:00 2001 From: Shokunin <30949000+tetrisgm@users.noreply.github.com> Date: Thu, 22 Oct 2026 15:26:47 -0700 Subject: [PATCH 27/29] [video] supersample_disable_at_lines: drop SSAA on 4K/8K displays (opt-in) --- docs/config_schema.md | 9 ++++- generated/OpenBIOS.emitter.sha | 2 +- recompiler/src/config_loader.cpp | 7 ++++ recompiler/src/config_loader.h | 2 ++ .../tests/video_enhancement_settings_test.cpp | 6 +++- runtime/include/internal_resolution.h | 19 +++++++++++ runtime/src/main.cpp | 33 ++++++++++++------- runtime/tests/test_internal_resolution.cpp | 15 +++++++++ 8 files changed, 79 insertions(+), 14 deletions(-) diff --git a/docs/config_schema.md b/docs/config_schema.md index cdce0fcc..b2989de5 100644 --- a/docs/config_schema.md +++ b/docs/config_schema.md @@ -702,16 +702,23 @@ player's `settings.toml`, and a game may ship a default the same way: [video] internal_resolution = "4k" # native | 720p | 1080p | 1440p | 4k | 5k | 8k | display, or a number of lines resolution_reference_lines = 240 # game.toml only: the title's usual display height (120..1024) +supersample_disable_at_lines = 0 # game.toml only: supersample 1.0 at/above this display height (0 = off, 240..8640) match_display_max_lines = 0 # game.toml only: cap on the display height Match display uses (0 = none, 240..8640) ``` `match_display_max_lines` caps the monitor height that Match display, its dynamic-resolution floor (`display`) and graphics-preset detection resolve -against. With no cap, an 8K panel asks 18x (27x with `supersample = 1.5`), which is +against. It also caps the final Match display target after supersampling, +so a larger saved factor cannot exceed it. Fixed presets keep their own target. +With no cap, an 8K panel asks 18x (27x with `supersample = 1.5`), which is past the full-VRAM surface. The high-resolution window then engages, and dynamic resolution cannot step there. A title whose GPU budget is set for 4K lines sets 2160. +`supersample_disable_at_lines` uses the physical monitor height before the cap. +At or above its threshold the effective factor is 1.0; the saved factor is +retained for smaller monitors. Zero preserves the default behaviour. + A preset is a target height. The runtime renders at the integer scale `S = ceil(target / resolution_reference_lines)`: at the default 240 lines, 720p is 3x, 1080p is 5x (1200 lines, area-resolved to 1080), 1440p 6x, 4K 9x, diff --git a/generated/OpenBIOS.emitter.sha b/generated/OpenBIOS.emitter.sha index e8134784..70cb65a7 100644 --- a/generated/OpenBIOS.emitter.sha +++ b/generated/OpenBIOS.emitter.sha @@ -1 +1 @@ -75a8d9834efb1c994169a939fbe043073b1b4b133bfe7b0235b80338af7f2f08 +61bf4f6d29680623d215fbe407958408cab0a1e08dbda3c62baa25e607d63b7b diff --git a/recompiler/src/config_loader.cpp b/recompiler/src/config_loader.cpp index 46161cf4..768350b7 100644 --- a/recompiler/src/config_loader.cpp +++ b/recompiler/src/config_loader.cpp @@ -780,6 +780,13 @@ static RuntimeConfig parse_runtime_block(const toml::value& cfg, const fs::path& } rt.video_match_display_max_lines = static_cast(n); } + if (video.contains("supersample_disable_at_lines")) { + const auto n = toml::find(video, "supersample_disable_at_lines"); + if (n != 0 && (n < 240 || n > 8640)) + throw std::runtime_error(fmt::format( + "[video] supersample_disable_at_lines must be 0 or 240..8640: {}", n)); + rt.video_supersample_disable_at_lines = static_cast(n); + } if (video.contains("window_width")) { const auto n = toml::find(video, "window_width"); if (n < 640 || n > 7680) { diff --git a/recompiler/src/config_loader.h b/recompiler/src/config_loader.h index 7fb4375b..1cca1185 100644 --- a/recompiler/src/config_loader.h +++ b/recompiler/src/config_loader.h @@ -472,6 +472,8 @@ struct RuntimeConfig { /* [video] match_display_max_lines: cap on the display height Match * display (and its dynamic-resolution floor) resolves against; 0 = none. */ int video_match_display_max_lines = 0; + /* Disable target supersampling on displays at least this tall (0 = off). */ + int video_supersample_disable_at_lines = 0; // Optional initial window width declared by the title profile. Zero keeps // the historical fit-to-display behavior; player settings may override it. diff --git a/recompiler/tests/video_enhancement_settings_test.cpp b/recompiler/tests/video_enhancement_settings_test.cpp index 002bbd8f..cdc5b83a 100644 --- a/recompiler/tests/video_enhancement_settings_test.cpp +++ b/recompiler/tests/video_enhancement_settings_test.cpp @@ -226,14 +226,17 @@ static void test_internal_resolution_game_toml() { "resolution_reference_lines defaults to 240"); check(gc.runtime.video_match_display_max_lines == 0, "match_display_max_lines defaults to 0 (no cap)"); + check(gc.runtime.video_supersample_disable_at_lines == 0, "display SS policy defaults off"); fs::remove(p); p = write_game_toml("psxrecomp_ir_match_cap.toml", "[video]\n" "internal_resolution = \"display\"\n" - "match_display_max_lines = 2160\n"); + "match_display_max_lines = 2160\n" + "supersample_disable_at_lines = 2160\n"); gc = PSXRecompV4::load_game_config(p); check(gc.runtime.video_match_display_max_lines == 2160, "match_display_max_lines parses"); + check(gc.runtime.video_supersample_disable_at_lines == 2160, "display SS policy parses"); fs::remove(p); p = write_game_toml("psxrecomp_ir_4k.toml", @@ -257,6 +260,7 @@ static void test_internal_resolution_game_toml() { "internal_resolution = 1\n", "resolution_reference_lines = 50\n", "match_display_max_lines = 100\n", + "supersample_disable_at_lines = 100\n", "supersampling = 33\n" }) { p = write_game_toml("psxrecomp_ir_bad.toml", std::string("[video]\n") + bad); bool rejected = false; diff --git a/runtime/include/internal_resolution.h b/runtime/include/internal_resolution.h index 50e61016..c37dc32a 100644 --- a/runtime/include/internal_resolution.h +++ b/runtime/include/internal_resolution.h @@ -143,6 +143,13 @@ static inline int psx_ss_parse_milli(const char *s) { return (int)m; } +/* Opt-in display policy; keep the saved factor intact for smaller displays. */ +static inline int psx_ir_display_supersample(int ss_milli, int display_px_h, + int disable_at_lines) { + return disable_at_lines > 0 && display_px_h >= disable_at_lines + ? PSX_SS_MILLI_MIN : ss_milli; +} + /* psx_resolve_internal_scale with the supersample factor on the target. */ static inline int psx_resolve_internal_scale_ss(int value, int ref_lines, int display_px_h, int s_max, @@ -160,6 +167,18 @@ static inline int psx_resolve_internal_scale_ss(int value, int ref_lines, return psx_resolve_internal_scale(value, ref_lines, display_px_h, s_max); } +/* A Match display cap bounds the final target, including supersampling. + * Fixed presets and the legacy integer scale retain their own ceilings. */ +static inline int psx_resolve_internal_scale_ss_cap(int value, int ref_lines, + int display_px_h, int s_max, int ss_milli, int max_display_lines) { + int s = psx_resolve_internal_scale_ss(value, ref_lines, display_px_h, s_max, ss_milli); + if (value == PSX_IR_DISPLAY && max_display_lines > 0) { + int cap = psx_resolve_internal_scale(max_display_lines, ref_lines, 0, s_max); + if (s > cap) s = cap; + } + return s; +} + /* A legacy [video] supersampling factor as a value the launcher can show: * the preset that resolves to the same scale (1 -> Native, 3 -> 720p, * 5 -> 1080p, 6 -> 1440p, 9 -> 4K, 12 -> 5K, 18 -> 8K at 240 lines), diff --git a/runtime/src/main.cpp b/runtime/src/main.cpp index 484a98f8..6bf5eea9 100644 --- a/runtime/src/main.cpp +++ b/runtime/src/main.cpp @@ -1272,6 +1272,11 @@ static int g_video_ss_milli = 1000; /* [video] supersample / PSX_SUPER * full-VRAM surface, where the high-resolution window engages and dynamic * resolution cannot step; a title can cap it at, say, 4K lines. */ static int g_video_match_max_lines = 0; +static int g_video_ss_disable_at_lines = 0; +static int display_supersample(SDL_Window *window) { + return psx_ir_display_supersample(g_video_ss_milli, + psx_sdl_display_pixel_height(window), g_video_ss_disable_at_lines); +} static int match_display_px_h(SDL_Window *window) { const int h = psx_sdl_display_pixel_height(window); return (g_video_match_max_lines > 0 && h > g_video_match_max_lines) @@ -1454,9 +1459,8 @@ static int effective_internal_resolution(void) { static void apply_internal_resolution(int display_px_h) { const int preset = effective_internal_resolution(); if (preset == PSX_IR_UNSET) return; - g_video_scale = psx_resolve_internal_scale_ss(preset, g_video_ref_lines, - display_px_h, video_scale_ceiling(), - g_video_ss_milli); + g_video_scale = psx_resolve_internal_scale_ss_cap(preset, g_video_ref_lines, + display_px_h, video_scale_ceiling(), display_supersample(nullptr), g_video_match_max_lines); } /* The value the launcher row starts on: the preset, or the legacy factor shown @@ -17860,6 +17864,7 @@ int main(int argc, char** argv) { g_video_internal_res = gc.runtime.video_internal_resolution; g_video_ref_lines = gc.runtime.video_resolution_reference_lines; g_video_match_max_lines = gc.runtime.video_match_display_max_lines; + g_video_ss_disable_at_lines = gc.runtime.video_supersample_disable_at_lines; g_video_dynres = gc.runtime.video_dynamic_resolution ? 1 : 0; g_video_dynres_min = gc.runtime.video_dynamic_resolution_min; if (gc.runtime.video_window_width > 0) { @@ -20353,9 +20358,10 @@ session_reboot: else if (!std::strcmp(e, "fxaa_hq")) g_video_post_aa = GL_POST_AA_FXAA_HQ; else std::fprintf(stdout, "psxrecomp: PSX_AA_MODE=%s not understood (off, fxaa, fxaa_hq)\n", e); } - if (g_video_ss_milli != 1000) + const int effective_ss = display_supersample(nullptr); + if (!g_video_ss_disable_at_lines && effective_ss != 1000) std::fprintf(stdout, "psxrecomp: supersample %.3gx on the internal-resolution target\n", - g_video_ss_milli / 1000.0); + effective_ss / 1000.0); apply_internal_resolution(match_display_px_h(nullptr)); if (g_video_scale < 1) g_video_scale = 1; if (const char* e = std::getenv("PSX_DYNRES")) @@ -20996,17 +21002,22 @@ session_reboot: /* Match display: the monitor's pixel height, now that the window says * which monitor. glb_set_scale only records the request; the hr * surface is allocated at context init below. */ - if (effective_internal_resolution() == PSX_IR_DISPLAY && g_video_scale_applies) { + const int ir = effective_internal_resolution(); + if (g_video_scale_applies && ir != PSX_IR_UNSET && + (ir == PSX_IR_DISPLAY || g_video_ss_disable_at_lines > 0)) { const int dh = match_display_px_h(sdl_window); - const int s = psx_resolve_internal_scale_ss(PSX_IR_DISPLAY, g_video_ref_lines, - dh, GL_MAX_INTERNAL_SCALE, - g_video_ss_milli); + const int ss = display_supersample(sdl_window); + const int s = psx_resolve_internal_scale_ss_cap(ir, g_video_ref_lines, + dh, GL_MAX_INTERNAL_SCALE, ss, g_video_match_max_lines); gr_set_scale(s); g_video_requested_scale = s; - std::fprintf(stdout, "psxrecomp: internal resolution Match display: " - "%d px -> %dx requested\n", dh, s); + const char *label = psx_ir_label_for(ir); + std::fprintf(stdout, "psxrecomp: internal resolution %s: " + "%d px -> %dx requested (supersample %.3gx)\n", + label ? label : "custom", dh, s, ss / 1000.0); } gl_renderer_set_swap_interval(present_effective_swap_interval()); /* applied at context init */ + /* Dynamic resolution: the surfaces are allocated at the scale above * (the ceiling) and the level steps under it (dynres_setup). */ #ifndef PSX_NO_DEBUG_TOOLS diff --git a/runtime/tests/test_internal_resolution.cpp b/runtime/tests/test_internal_resolution.cpp index 25b04cdd..597c271c 100644 --- a/runtime/tests/test_internal_resolution.cpp +++ b/runtime/tests/test_internal_resolution.cpp @@ -222,6 +222,21 @@ int main() { expect("ss clamps to the ceiling", psx_resolve_internal_scale_ss(4320, 240, 0, 32, 4000), 32); expect("ss unknown display stays 1", psx_resolve_internal_scale_ss(PSX_IR_DISPLAY, 240, 0, 32, 2000), 1); + expect("ss policy opt-out", psx_ir_display_supersample(1500, 4320, 0), 1500); + expect("ss below threshold", psx_ir_display_supersample(1500, 2159, 2160), 1500); + expect("ss at threshold", psx_ir_display_supersample(1500, 2160, 2160), 1000); + expect("ss on 8K", psx_ir_display_supersample(1500, 4320, 2160), 1000); + expect("ss unknown display", psx_ir_display_supersample(1500, 0, 2160), 1500); + expect("cap includes SS below threshold", psx_resolve_internal_scale_ss_cap( + PSX_IR_DISPLAY, 240, 1440, 32, 4000, 4320), 18); + expect("cap below 8K", psx_resolve_internal_scale_ss_cap( + PSX_IR_DISPLAY, 240, 1080, 32, 1500, 4320), 7); + expect("8K capped after SS", psx_resolve_internal_scale_ss_cap( + PSX_IR_DISPLAY, 240, 8640, 32, 4000, 4320), 18); + expect("fixed preset keeps own ceiling", psx_resolve_internal_scale_ss_cap( + 4320, 240, 8640, 32, 4000, 4320), 32); + expect("cap opt-out", psx_resolve_internal_scale_ss_cap( + PSX_IR_DISPLAY, 240, 4320, 32, 1500, 0), 27); launcher_round_trips(); if (g_failures) return 1; From 67200909e4b6993e39ddb8e84b7441e42999c170 Mon Sep 17 00:00:00 2001 From: Shokunin <30949000+tetrisgm@users.noreply.github.com> Date: Thu, 22 Oct 2026 15:26:53 -0700 Subject: [PATCH 28/29] PGXP: keep full depth and unclamped XY for far geometry --- runtime/include/pgxp.h | 9 ++++- runtime/src/gpu.c | 16 +++++++-- runtime/src/gpu_gl_renderer.c | 35 +++++++++++++++---- runtime/src/gte.cpp | 12 ++++--- runtime/src/pgxp.cpp | 64 +++++++++++++++++++++++++++-------- runtime/tests/test_pgxp.cpp | 46 +++++++++++++++++++++++++ 6 files changed, 153 insertions(+), 29 deletions(-) diff --git a/runtime/include/pgxp.h b/runtime/include/pgxp.h index 8b5d4455..62a4ad8e 100644 --- a/runtime/include/pgxp.h +++ b/runtime/include/pgxp.h @@ -62,7 +62,8 @@ int pgxp_position_fallback(void); * near geometry. Guest-visible SXY, MAC0 and FLAG are unchanged either way. * Because that position can then differ from the guest integer by more than * the fraction, truncation agreement becomes a window (PGXP_PPP_AGREE_*) and - * a vertex at the GTE's saturation limit must still agree exactly. One + * unclamped IR projections need an intact, value-validated GTE word; halves + * and cache fallbacks keep the strict saturation checks. One * consumer is not visual: [widescreen] precise_nclip takes its branch sign * from these shadows, so in a title that sets both, that (already * non-faithful) widescreen sign follows the exact projection too. */ @@ -91,6 +92,7 @@ int pgxp_preserve_projection(void); * when none). Returns 1 with the position in x16/y16 when the vertex * qualifies (sf=1, IR1/IR2 not saturated, SZ3 not saturated and nonzero, and * no divide overflow: H < 2*SZ3), else 0 and the caller keeps the IR path. */ +/* Return 2 for an opt-in IR-unclamped projection, 1 for ordinary precision. */ int pgxp_project_precise(int64_t mac1, int64_t mac2, int64_t mac3, int shift, int32_t ir1, int32_t ir2, uint32_t sz3, uint32_t h, int32_t ofx, int32_t ofy, int64_t x_num, int64_t x_den, @@ -164,6 +166,11 @@ void pgxp_suppress_end(void); /* Called at the RTPS/RTPT projection with the pre-truncation 16.16 screen * coordinates, the projected depth (SZ3), and the packed SXY word the guest * sees. Shifts the shadow FIFO exactly like push_sxy (regs 12..15). */ +/* Tag only immediately after push_sxy of a qualified unclamped projection. */ +void pgxp_gte_mark_unclamped(uint32_t depth); +/* Full positive MAC3 depth of an intact, validated exact-projection word; + * zero when unavailable. The legacy SZ APIs keep their hardware u16 value. */ +float pgxp_get_unclamped_depth(uint32_t addr, uint32_t word); void pgxp_gte_push_sxy(int32_t x16, int32_t y16, uint16_t sz3, uint32_t packed); /* Host-only homogeneous screen projection, before SZ/SXY/divider saturation. diff --git a/runtime/src/gpu.c b/runtime/src/gpu.c index 9cee28e1..bd7f9fb5 100644 --- a/runtime/src/gpu.c +++ b/runtime/src/gpu.c @@ -4770,7 +4770,7 @@ static void prepare_precise_triangle(int i0, int i1, int i2, } const int idx[3] = { i0, i1, i2 }; int32_t fx[3], fy[3]; - uint16_t vz[3] = {0, 0, 0}; + float vz[3] = {0, 0, 0}; int any_precise = 0, n_precise = 0; PGXPTriRecord rec; memset(&rec, 0, sizeof rec); @@ -4796,6 +4796,10 @@ static void prepare_precise_triangle(int i0, int i1, int i2, /* PGXP depth (G1.14): only a value-validated dataflow shadow carries * SZ, also when the seam clamp kept the native position. */ vz[i] = sz; + if (sz == 65535u) { + float full = pgxp_get_unclamped_depth(addr, word); + if (full > 0.0f) vz[i] = full; + } /* Native-wide edge recovery is not PGXP: it does not count in n_precise. */ if (native_wide_projection_x(addr, word, raw_x, raw_y, &px)) any_precise = 1; @@ -4808,7 +4812,7 @@ static void prepare_precise_triangle(int i0, int i1, int i2, rec.word[i] = word; rec.x16[i] = fx[i]; rec.y16[i] = fy[i]; - rec.z[i] = vz[i]; + rec.z[i] = sz; /* receipt retains the hardware SZ, not the visual depth */ } if (geometry) { pgxp_note_triangle(n_precise); /* G1.1 crack exposure: mixed share */ @@ -4896,6 +4900,7 @@ static void prepare_texture_triangle(int i0, int i1, int i2) { if (gp0_cmd_source_addr == 0xFFFFFFFFu) { s_texcorr.no_source++; return; } int indices[3] = { i0, i1, i2 }; uint16_t z[3]; + float depth[3]; for (int i = 0; i < 3; i++) { uint32_t addr = GPU_RAM_KEY((gp0_cmd_source_addr + (uint32_t)indices[i] * 4u)); if (!gte_precision_load_word(addr, gp0_cmd_buf[indices[i]], NULL, NULL, &z[i]) || @@ -4903,8 +4908,13 @@ static void prepare_texture_triangle(int i0, int i1, int i2) { s_texcorr.no_depth++; return; } + depth[i] = z[i]; + if (z[i] == 65535u) { + float full = pgxp_get_unclamped_depth(addr, gp0_cmd_buf[indices[i]]); + if (full > 0.0f) depth[i] = full; + } } - float q[3] = { 1.0f / (float)z[0], 1.0f / (float)z[1], 1.0f / (float)z[2] }; + float q[3] = { 1.0f / depth[0], 1.0f / depth[1], 1.0f / depth[2] }; float qmax = q[0]; if (q[1] > qmax) qmax = q[1]; if (q[2] > qmax) qmax = q[2]; diff --git a/runtime/src/gpu_gl_renderer.c b/runtime/src/gpu_gl_renderer.c index 098646d3..977b09f8 100644 --- a/runtime/src/gpu_gl_renderer.c +++ b/runtime/src/gpu_gl_renderer.c @@ -1816,12 +1816,15 @@ static const char *GEO_VS = " float a = a_col.a, code = floor(a * 0.5), zn = 0.0, w = 1.0;\n" " float m = a - 2.0 * code;\n" " /* + 2*131072: the primitive is dither-eligible (GPU_GEO_DITHER_CODE). */\n" - " float dth = (a >= 0.0 && code >= 131072.0) ? 1.0 : 0.0; code -= 131072.0 * dth; v_dith = int(dth);\n" + " float dth = (a >= 0.0 && code >= 131072.0) ? 1.0 : 0.0; code -= 131072.0 * dth;\n" " float cp = code >= 65536.0 ? 1.0 : 0.0, sz = code - 65536.0 * cp;\n" - " if (a < 0.0) { m = 0.0; zn = 0.9999; sz = 0.0; cp = 0.0; } /* inside the far plane: never clipped */\n" + " bool far_code = a < -1.5;\n" + " if (far_code) { uint bits = floatBitsToUint(-a); m = float(bits & 1u); cp = float((bits >> 1u) & 1u); dth = float((bits >> 2u) & 1u); sz = uintBitsToFloat(bits & 0xfffffff8u) - 2.0; }\n" + " v_dith = int(dth);\n" + " if (a < 0.0 && !far_code) { m = 0.0; zn = 1.0; sz = 0.0; cp = 0.0; }\n" " else if (sz > 0.5) { zn = 1.0 - 512.0 / (max(sz * (1.0 - u_zbias) - (u_zbias > 0.0 ? 48.0 : 0.0), 1.0) + 256.0); if (cp > 0.5) w = sz / 1024.0; }\n" " v_col = vec4(a_col.rgb, m); v_col_p = a_col.rgb; v_cp = int(cp);\n" - " v_b3d = (a >= 0.0 && sz > 0.5) ? 1.0 : 0.0;\n" + " v_b3d = ((a >= 0.0 || far_code) && sz > 0.5) ? 1.0 : 0.0;\n" " float xb = a_pos.x;\n" " if (u_xscale < 0.0) {\n" " float s = -u_xscale; float h = u_xhalf / s;\n" @@ -1918,6 +1921,7 @@ static const char *TEX_VS = " * the vertex stays TEXV floats for every title. */\n" " float pz = a_col.a < 0.0 ? -a_col.a : 0.0;\n" " float cp = pz >= 65536.0 ? 1.0 : 0.0, sz = pz - 65536.0 * cp, zn = 0.0;\n" + " if (a_col.a > 1.5) { uint bits = floatBitsToUint(a_col.a); cp = float((bits >> 1u) & 1u); sz = uintBitsToFloat(bits & 0xfffffff8u) - 2.0; }\n" " if (sz > 0.5) { zn = 1.0 - 512.0 / (max(sz * (1.0 - u_zbias) - (u_zbias > 0.0 ? 48.0 : 0.0), 1.0) + 256.0); if (a_q <= 0.0 && cp > 0.5) w = sz / 1024.0; }\n" " v_cp = int(cp); v_b3d = sz > 0.5 ? 1.0 : 0.0;\n" " vec2 ndc = vec2((xb+u_shift+u_xoff)/u_xhalf - 1.0, (a_pos.y+u_shift)/256.0 - 1.0);\n" @@ -4404,6 +4408,21 @@ static float pgxp_vertex_code(float sz) { return sz + (s_pgxp_cpersp ? 65536.0f : 0.0f); } +/* Depth beyond the hardware SZ range cannot share its old numeric flag + * offsets. Keep that format byte-identical for normal vertices. Far vertices + * carry a finite positive float (depth+2), with three low mantissa bits for + * mask/colour/dither. Integer depths through 2^20 retain every depth bit; + * larger depths lose at most seven float ULPs. Negative GEO / positive TEX + * alpha distinguishes this format from legacy draws and depth-clear (-1). */ +static float pgxp_far_vertex_code(float sz, int mask, int dither) { + float value = sz + 2.0f; + uint32_t bits; + memcpy(&bits, &value, sizeof bits); + bits = (bits & ~7u) | (mask ? 1u : 0u) | (s_pgxp_cpersp ? 2u : 0u) | (dither ? 4u : 0u); + memcpy(&value, &bits, sizeof value); + return value; +} + /* The depth buffer is cleared (beyond every SZ, over the drawing area and * the native-wide margins) by a colourless depth-only draw through the flat * batch, so every surface a draw reaches (the hr surface, the high-resolution @@ -4523,13 +4542,14 @@ static void seam_q(const float B[3][3], float q[3]) { for (int k = 0; k < 3; k++) q[k] = o[k] > 1e-9f ? o[k] : q[k]; } static void seam_sz(const float B[3][3], float z[3]) { + float limit = fmaxf(z[0], fmaxf(z[1], z[2])) > 65535.0f ? 2147483648.0f : 65535.0f; float o[3]; for (int k = 0; k < 3; k++) { float iz = 0.0f; for (int i = 0; i < 3; i++) iz += B[k][i] / z[i]; o[k] = iz > 1e-9f ? 1.0f / iz : z[k]; if (o[k] < 1.0f) o[k] = 1.0f; - if (o[k] > 65535.0f) o[k] = 65535.0f; + if (o[k] > limit) o[k] = limit; } for (int k = 0; k < 3; k++) z[k] = o[k]; } @@ -4741,7 +4761,9 @@ static void gpu_geometry(GLenum mode, const int *xs, const int *ys, } } for (int i = 0; i < 3; i++) - v0[i * 6 + 5] = mask_a + 2.0f * pgxp_vertex_code(z[i]); + v0[i * 6 + 5] = z[i] > 65535.0f + ? -pgxp_far_vertex_code(z[i], ((int)mask_a & 1), mask_a >= GPU_GEO_DITHER_CODE) + : mask_a + 2.0f * pgxp_vertex_code(z[i]); } if (n == 3) pgxp_tri_log_tri('G', dmode, semi, v0, 6); } @@ -4924,7 +4946,8 @@ static void gpu_textured_triangle(const int *xs, const int *ys, } for (int i = 0; i < 3; i++) { const float code = pgxp_vertex_code(z[i]); - if (code > 0.0f) t0[i * TEXV + 7] = -code; /* a_col.a (TEX_VS) */ + if (z[i] > 65535.0f) t0[i * TEXV + 7] = pgxp_far_vertex_code(z[i], 0, 0); + else if (code > 0.0f) t0[i * TEXV + 7] = -code; /* a_col.a (TEX_VS) */ } } pgxp_tri_log_tri('T', tdmode, semi, &s_tb[s_tb_n * TEXV], TEXV); diff --git a/runtime/src/gte.cpp b/runtime/src/gte.cpp index 8b5447d2..3b0ed842 100644 --- a/runtime/src/gte.cpp +++ b/runtime/src/gte.cpp @@ -1232,6 +1232,7 @@ void gte_rtps_internal(GTEState* gte, int16_t* V, bool setMac0, uint32_t instr) int64_t cx16 = sx16 < -kLim ? -kLim : (sx16 > kLim - 1 ? kLim - 1 : sx16); int64_t cy16 = sy16 < -kLim ? -kLim : (sy16 > kLim - 1 ? kLim - 1 : sy16); int32_t px16 = (int32_t)cx16, py16 = (int32_t)cy16; + bool unclamped_projection = false; /* Preserve projection precision (G1.11): shadow the exact projection * instead when the vertex qualifies and it lies inside the GPU's * truncation-agreement window; outside it the IR path's shadow is @@ -1241,15 +1242,18 @@ void gte_rtps_internal(GTEState* gte, int16_t* V, bool setMac0, uint32_t instr) * passes), where the shadow would be dropped anyway. */ if (!proj_override && pgxp_preserve_projection() && pgxp_active()) { int32_t ex16, ey16; - if (pgxp_project_precise(mac1, mac2, mac3, shift, gte->IR1, gte->IR2, - gte->SZ[3], gte_h_scaled(gte), gte->OFX, gte->OFY, - x_num, x_den, &ex16, &ey16) && - pgxp_ppp_accept(ex16, ey16, (uint32_t)gte->SXY[2])) { + const int precise = pgxp_project_precise(mac1, mac2, mac3, shift, + gte->IR1, gte->IR2, gte->SZ[3], gte_h_scaled(gte), + gte->OFX, gte->OFY, x_num, x_den, &ex16, &ey16); + if (precise == 2 || (precise == 1 && + pgxp_ppp_accept(ex16, ey16, (uint32_t)gte->SXY[2]))) { + unclamped_projection = precise == 2; px16 = ex16; py16 = ey16; } } pgxp_gte_push_sxy(px16, py16, gte->SZ[3], (uint32_t)gte->SXY[2]); + if (unclamped_projection) pgxp_gte_mark_unclamped((uint32_t)(mac3 >> 12)); if (!proj_override && pgxp_projection_tracking() && shift == 12 && !lm) { const double z = (double)(mac3 >> 12); const double h_proj = (double)gte_h_scaled(gte); diff --git a/runtime/src/pgxp.cpp b/runtime/src/pgxp.cpp index df92faf0..1bed9ffb 100644 --- a/runtime/src/pgxp.cpp +++ b/runtime/src/pgxp.cpp @@ -63,6 +63,7 @@ enum { * precise value may sit up to one pixel below or two above it. */ PGXP_F_DX = 1u << 4, PGXP_F_DY = 1u << 5, + PGXP_F_UNCLAMPED = 1u << 6, /* exact GTE projection; intact word only */ PGXP_F_VXY = PGXP_F_VX | PGXP_F_VY, }; @@ -551,7 +552,7 @@ static inline int pv_live(const PGXPValue *pv) { static inline void pv_validate(PGXPValue *pv, uint32_t actual) { if (pv->gen != s_gen) return; uint32_t diff = pv->value ^ actual; - if (diff) pv->flags &= (uint16_t)~PGXP_F_PROJECTION; + if (diff) pv->flags &= (uint16_t)~(PGXP_F_PROJECTION | PGXP_F_UNCLAMPED); if ((pv->flags & PGXP_F_VX) && (diff & 0x0000FFFFu)) pv->flags &= (uint16_t)~(PGXP_F_VX | PGXP_F_DX | PGXP_F_VZ); if ((pv->flags & PGXP_F_VY) && (diff & 0xFFFF0000u)) @@ -696,7 +697,7 @@ extern "C" void psx_pgxp_load(struct CPUState *cpu, uint32_t instr, * cpu-mode keeps working. */ dst->y16 = ((int32_t)value >> 16) << 16; dst->flags = PGXP_F_VY; - if (src && src->gen == s_gen) { + if (src && src->gen == s_gen && !(src->flags & PGXP_F_UNCLAMPED)) { uint32_t actual_half = (value & 0xFFFFu) << (hi_half ? 16 : 0); uint32_t mask = hi_half ? 0xFFFF0000u : 0x0000FFFFu; uint16_t want = hi_half ? PGXP_F_VY : PGXP_F_VX; @@ -768,7 +769,7 @@ extern "C" void psx_pgxp_store(struct CPUState *cpu, uint32_t instr, if (hi_half) dst->value = (dst->value & 0x0000FFFFu) | (half << 16); else dst->value = (dst->value & 0xFFFF0000u) | half; dst->flags &= (uint16_t)~((hi_half ? (PGXP_F_VY | PGXP_F_DY) : (PGXP_F_VX | PGXP_F_DX)) | - PGXP_F_VZ | PGXP_F_PROJECTION); + PGXP_F_VZ | PGXP_F_PROJECTION | PGXP_F_UNCLAMPED); dst->z = 0; int ok = 0, derived = 0; int32_t v16 = 0; @@ -782,7 +783,8 @@ extern "C" void psx_pgxp_store(struct CPUState *cpu, uint32_t instr, * coordinate half here. */ if (hi_half) { dst->y16 = v16; dst->flags |= (uint16_t)(PGXP_F_VY | (derived ? PGXP_F_DY : 0)); } else { dst->x16 = v16; dst->flags |= (uint16_t)(PGXP_F_VX | (derived ? PGXP_F_DX : 0)); } - } else if (src && src->gen == s_gen && (src->flags & PGXP_F_VX) && + } else if (src && src->gen == s_gen && !(src->flags & PGXP_F_UNCLAMPED) && + (src->flags & PGXP_F_VX) && ((src->value ^ value) & 0xFFFFu) == 0) { const uint16_t d = (src->flags & PGXP_F_DX) ? (hi_half ? PGXP_F_DY : PGXP_F_DX) : 0; if (hi_half) { dst->y16 = src->x16; dst->flags |= (uint16_t)(PGXP_F_VY | d); } @@ -797,7 +799,7 @@ extern "C" void psx_pgxp_store(struct CPUState *cpu, uint32_t instr, dst->value = (dst->value & ~(0xFFu << shift)) | ((value & 0xFFu) << shift); dst->flags &= (uint16_t)~(((addr & 2u) ? (PGXP_F_VY | PGXP_F_DY) : (PGXP_F_VX | PGXP_F_DX)) | - PGXP_F_VZ | PGXP_F_PROJECTION); + PGXP_F_VZ | PGXP_F_PROJECTION | PGXP_F_UNCLAMPED); dst->z = 0; return; } @@ -937,7 +939,7 @@ extern "C" void psx_pgxp_cop2(struct CPUState *cpu, uint32_t instr, * sub-pixel value; an untracked half contributes its exact integer value * (adding an exact offset to a tracked coordinate keeps the fraction). */ static inline int32_t comp16(const PGXPValue *pv, uint32_t value, int hi_half) { - if (pv && pv->gen == s_gen) { + if (pv && pv->gen == s_gen && !(pv->flags & PGXP_F_UNCLAMPED)) { if (!hi_half && (pv->flags & PGXP_F_VX) && ((pv->value ^ value) & 0x0000FFFFu) == 0) return pv->x16; @@ -951,7 +953,7 @@ static inline int32_t comp16(const PGXPValue *pv, uint32_t value, int hi_half) { /* The derived-half marks (PGXP_F_DX / DY) an operand contributes through * comp16: a tracked derived half keeps its rounding window in the sum. */ static inline uint16_t derived_bits(const PGXPValue *pv, uint32_t value) { - if (!pv || pv->gen != s_gen) return 0; + if (!pv || pv->gen != s_gen || (pv->flags & PGXP_F_UNCLAMPED)) return 0; uint16_t d = 0; if ((pv->flags & PGXP_F_DX) && (pv->flags & PGXP_F_VX) && ((pv->value ^ value) & 0x0000FFFFu) == 0) d |= PGXP_F_DX; @@ -961,7 +963,7 @@ static inline uint16_t derived_bits(const PGXPValue *pv, uint32_t value) { } static inline int comp_tracked(const PGXPValue *pv, uint32_t value, int hi_half) { - if (!pv || pv->gen != s_gen) return 0; + if (!pv || pv->gen != s_gen || (pv->flags & PGXP_F_UNCLAMPED)) return 0; if (!hi_half) return (pv->flags & PGXP_F_VX) && ((pv->value ^ value) & 0x0000FFFFu) == 0; @@ -1504,13 +1506,26 @@ extern "C" int pgxp_project_precise(int64_t mac1, int64_t mac2, int64_t mac3, int64_t x_num, int64_t x_den, int32_t *x16, int32_t *y16) { /* Qualify exactly the vertices whose guest projection is the plain - * IR * (H / SZ3) of the continuous one: sf=1, no IR1/IR2 clamp (the - * stored IR equals MAC >> 12), SZ3 unclamped and nonzero, and no UNR + * IR * (H / SZ3) of the continuous one: sf=1, verified IR1/IR2 conversion, SZ3 unclamped and nonzero, and no UNR * divide overflow (the GTE saturates the quotient when H >= 2*SZ3). */ if (shift != 12 || x_den <= 0) return 0; - if ((mac1 >> 12) != (int64_t)ir1 || (mac2 >> 12) != (int64_t)ir2) return 0; + const int64_t ix = mac1 >> 12, iy = mac2 >> 12; const int64_t z_int = mac3 >> 12; - if (z_int <= 0 || z_int > 0xFFFF || (uint32_t)z_int != sz3) return 0; + if (z_int <= 0) return 0; + const bool xy_clamped = ix != ir1 || iy != ir2; + const bool z_clamped = z_int > 0xFFFF; + const bool unclamped = xy_clamped || z_clamped; + if (unclamped) { + /* Only the opt-in exact projection may bypass IR saturation. Reject + * wrapped accumulators, lm=1 sign clamps and every other mismatch. */ + const int64_t lo = -(INT64_C(1) << 43), hi = (INT64_C(1) << 43) - 1; + const int64_t cx = ix < -32768 ? -32768 : ix > 32767 ? 32767 : ix; + const int64_t cy = iy < -32768 ? -32768 : iy > 32767 ? 32767 : iy; + if (!s_preserve_projection || mac1 < lo || mac1 > hi || + mac2 < lo || mac2 > hi || mac3 < lo || mac3 > hi || + cx != ir1 || cy != ir2) return 0; + } + if ((uint32_t)(z_clamped ? 0xFFFF : z_int) != sz3) return 0; if ((int64_t)h >= 2 * z_int) return 0; /* (MAC/4096) * H / (MAC3/4096): the 4096s cancel. Double keeps ~52 bits, * far below the 1/65536 px the shadow carries. Host-only and visual-only, @@ -1528,7 +1543,7 @@ extern "C" int pgxp_project_precise(int64_t mac1, int64_t mac2, int64_t mac3, if (fy > lim - 1.0) fy = lim - 1.0; *x16 = (int32_t)std::floor(fx); *y16 = (int32_t)std::floor(fy); - return 1; + return unclamped ? 2 : 1; } /* Defined below with the GPU consumer. */ @@ -1562,6 +1577,21 @@ extern "C" void pgxp_gte_push_sxy(int32_t x16, int32_t y16, uint16_t sz3, s_gte[15] = *pv; /* SXYP mirrors SXY2 */ } +extern "C" void pgxp_gte_mark_unclamped(uint32_t depth) { + if (!g_pgxp_active) return; + s_gte[14].flags |= PGXP_F_UNCLAMPED; + s_gte[14].projection.z = (float)depth; + s_gte[15] = s_gte[14]; +} + +extern "C" float pgxp_get_unclamped_depth(uint32_t addr, uint32_t word) { + if (!g_pgxp_active || !s_preserve_projection) return 0.0f; + const PGXPValue *pv = pgxp_ptr(addr); + if (!pv || pv->gen != s_gen || pv->value != word || + !(pv->flags & PGXP_F_UNCLAMPED) || !(pv->flags & PGXP_F_VZ)) return 0.0f; + return pv->projection.z; +} + extern "C" int pgxp_get_gte_sxy(uint32_t index, int32_t *x16, int32_t *y16) { return pgxp_get_gte_sxy_checked(index, 0u, 0, x16, y16); } @@ -1695,8 +1725,12 @@ static inline int pgxp_accept(int32_t px, int32_t py, int32_t int_x, int32_t int_y, uint32_t word, uint16_t flags) { const int dx_derived = (flags & PGXP_F_DX) != 0; const int dy_derived = (flags & PGXP_F_DY) != 0; - if (!pgxp_agrees(px, int_x, (int16_t)(word & 0xFFFFu), dx_derived) || - !pgxp_agrees(py, int_y, (int16_t)(word >> 16), dy_derived)) + /* This tag can only ride an intact, value-validated GTE word. Arithmetic + * constructs new flags and loses it; the position-cache tier never has it. + * Clamped IR is an integer hardware limit, not the enhanced visual vertex. */ + if (!(s_preserve_projection && (flags & PGXP_F_UNCLAMPED)) && + (!pgxp_agrees(px, int_x, (int16_t)(word & 0xFFFFu), dx_derived) || + !pgxp_agrees(py, int_y, (int16_t)(word >> 16), dy_derived))) return 1; if (s_tolerance >= 0.0f) { if (pgxp_moved(px, int_x, dx_derived) > s_tolerance || diff --git a/runtime/tests/test_pgxp.cpp b/runtime/tests/test_pgxp.cpp index 6128e68f..e8693df3 100644 --- a/runtime/tests/test_pgxp.cpp +++ b/runtime/tests/test_pgxp.cpp @@ -622,6 +622,52 @@ int main(void) { &x, &y) == 1); } + /* Far-world projection: exact dataflow may recover IR saturation, while + * stale words, lm sign clamps and half writes must stay rejected. */ + { + const int saved_preserve = pgxp_preserve_projection(); + pgxp_set_preserve_projection(1); + pgxp_set_tolerance(-1.0f); + int32_t x, y; + CHECK(pgxp_project_precise(INT64_C(40000)*4096, INT64_C(-35000)*4096, + INT64_C(60000)*4096, 12, 32767, -32768, 60000, 300, + 160<<16, 120<<16, 1, 1, &x, &y) == 2); + /* Fused/non-fused host arithmetic may straddle the final floor by + * one 16.16 quantum (less than 0.00002 pixel). */ + CHECK(std::abs(x - 360*65536) <= 1 && std::abs(y + 55*65536) <= 1); + const uint32_t packed = (uint16_t)323 | ((uint32_t)(uint16_t)-44 << 16); + pgxp_gte_push_sxy(x, y, 60000, packed); + pgxp_gte_mark_unclamped(60000); + pgxp_store_gte_reg(0x80005000, 14); + int32_t px, py; uint16_t z; + CHECK(pgxp_get_precise_vertex(0x80005000, packed, 323, -44, &px, &py, &z)); + CHECK(px == x && py == y && z == 60000); + CHECK(pgxp_get_unclamped_depth(0x80005000, packed) == 60000.0f); + CHECK(!pgxp_get_precise_vertex(0x80005000, packed^1u, 322, -44, &px, &py, &z)); + psx_pgxp_cop2(nullptr, MFC2(2, 14), packed, 0); + psx_pgxp_store(nullptr, SH(1, 2), 0x80005000, packed); + CHECK(!pgxp_get_precise_vertex(0x80005000, packed, 323, -44, &px, &py, &z)); + CHECK(pgxp_project_precise(-4096, 0, INT64_C(60000)*4096, 12, 0, 0, + 60000, 300, 0, 0, 1, 1, &x, &y) == 0); + CHECK(pgxp_project_precise(INT64_C(40000)*4096, INT64_C(20000)*4096, + INT64_C(100000)*4096, 12, 32767, 20000, 65535, 300, + 160<<16, 120<<16, 1, 1, &x, &y) == 2); + CHECK(x == 280*65536 && y == 180*65536); + const uint32_t far_word = 310u | (211u << 16); + pgxp_gte_push_sxy(x, y, 65535, far_word); + pgxp_gte_mark_unclamped(100000); + pgxp_store_gte_reg(0x80005004, 14); + CHECK(pgxp_get_precise_vertex(0x80005004, far_word, 310, 211, &px, &py, &z)); + CHECK(px == x && py == y && z == 65535); + CHECK(pgxp_get_unclamped_depth(0x80005004, far_word) == 100000.0f); + CHECK(pgxp_get_unclamped_depth(0x80005004, far_word^1) == 0.0f); + pgxp_set_preserve_projection(0); + CHECK(pgxp_get_unclamped_depth(0x80005004, far_word) == 0.0f); + CHECK(pgxp_project_precise(INT64_C(40000)*4096, 0, INT64_C(60000)*4096, + 12, 32767, 0, 60000, 300, 0, 0, 1, 1, &x, &y) == 0); + pgxp_set_preserve_projection(saved_preserve); + } + /* --- probe: the same decision, not counted --- */ { produce_at(ADDR_A); From 2b5117738e27a1da31eb90a0b7f32b0b4785e53a Mon Sep 17 00:00:00 2001 From: Matt Stanley <1379tech@gmail.com> Date: Thu, 22 Oct 2026 15:27:27 -0700 Subject: [PATCH 29/29] Vulkan: order swapchain clears before presentation transfers --- runtime/src/gpu_vk_renderer.c | 38 ++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/runtime/src/gpu_vk_renderer.c b/runtime/src/gpu_vk_renderer.c index 6ad261f3..0faa63c6 100644 --- a/runtime/src/gpu_vk_renderer.c +++ b/runtime/src/gpu_vk_renderer.c @@ -688,6 +688,20 @@ static void img_barrier(VkCommandBuffer cb, VkImage img, p_vkCmdPipelineBarrier(cb, src_stage, dst_stage, 0, 0, NULL, 0, NULL, 1, &b); } +/* Clear a swapchain image (TRANSFER_DST_OPTIMAL) to opaque black, then order + * that clear before the next transfer write to the same image. Validation + * reported SYNC-HAZARD-WRITE-AFTER-WRITE for clear -> blit without a barrier; + * on RDNA3 the clear overlapped the blit and tiles of the frame (24-bit FMV + * via vk_renderer_present_cpu) came out black. */ +static void sc_clear_black(VkCommandBuffer cb, VkImage sc) { + VkClearColorValue black = {{0, 0, 0, 1}}; + VkImageSubresourceRange rng = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + p_vkCmdClearColorImage(cb, sc, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &black, 1, &rng); + img_barrier(cb, sc, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); +} + /* Map a layout to its (access mask, pipeline stage) for barriers. */ static void layout_access(VkImageLayout l, VkAccessFlags *acc, VkPipelineStageFlags *stage) { switch (l) { @@ -1935,11 +1949,9 @@ int vk_renderer_present_vram(int disp_x, int disp_y, int w, int h, img_barrier(cb, sc, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); /* clear the swapchain (letterbox bars) to black */ - VkClearColorValue black = {{0,0,0,1}}; - VkImageSubresourceRange rng = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - p_vkCmdClearColorImage(cb, sc, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &black, 1, &rng); + sc_clear_black(cb, sc); VkImageBlit blit = {0}; blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; @@ -1968,10 +1980,8 @@ void vk_renderer_present_blank(void) { if (!acquire_present(&sc, &cb, &idx, &fr)) return; img_barrier(cb, sc, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); - VkClearColorValue black = {{0,0,0,1}}; - VkImageSubresourceRange rng = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - p_vkCmdClearColorImage(cb, sc, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &black, 1, &rng); + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + sc_clear_black(cb, sc); finish_present(cb, sc, idx, fr); perf_snapshot_present(); } @@ -2042,10 +2052,8 @@ void vk_renderer_present_cpu(const uint32_t *pixels, int src_w, int src_h, img_barrier(cb, sc, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); - VkClearColorValue black = {{0,0,0,1}}; - VkImageSubresourceRange rng = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - p_vkCmdClearColorImage(cb, sc, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &black, 1, &rng); + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + sc_clear_black(cb, sc); VkOffset3D dst[2]; letterbox((int)s_sc_extent.width, (int)s_sc_extent.height, force_4_3 ? 4 : s_aspect_num, force_4_3 ? 3 : s_aspect_den, dst); @@ -2111,10 +2119,8 @@ int vk_renderer_present_wide(int disp_x, int disp_y, int disp_h, int linear) { img_barrier(cb, sc, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); - VkClearColorValue black = {{0, 0, 0, 1}}; - VkImageSubresourceRange rng = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - p_vkCmdClearColorImage(cb, sc, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &black, 1, &rng); + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + sc_clear_black(cb, sc); img_to(cb, s_wide_img[i], &s_wide_layout[i], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);

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