27 Commits

Author SHA1 Message Date
chrg6
b8b57cfd53 Take the file lock at the end of a save file call only when a CloseFile is pending, not at the end of every call.
The review of the save file use count found that fs_host.c file_release took `lock` exclusive whenever users dropped
to 0, which is the end of every single read_file, write_file, file_size, set_file_size and flush_file, not only when
a CloseFile had marked the slot. The exclusive acquisition waits for every shared holder, such as the fsync loop of a
non-save Commit (commit, FlushFileBuffers under the shared lock), and while it waits it holds up new file_of callers:
a new wait on the guest threads that the change had said it did not add.

file_release now takes the lock only when it finds closing set after its decrement. The decrement and the load of
closing, and close_file's store of closing and load of users, are sequentially consistent pairs, so either the last
call out sees the mark or close_file sees users at 0 and closes the file itself; the check under the lock still keeps
the close to one. closing is an int32_t read and stored with atomics; a slot is emptied by the new file_clear, which
stores users and closing as atomics because the call that has just left the slot may read closing without the lock,
and open_file fills only h, mode and save.

Not reproduced in the game before the change. tests/fs_file_use_check.py gains a case: 1000 reads with no close take
the exclusive lock 0 times, and a read ends while another thread holds the lock shared. It failed on the code before
this change (1000 exclusive takes, the read waited) and passes after it, as do its close-during-a-read case and its
20000 closes and opens beside a reader (six runs, no foreign bytes, no double close). The compile check of fs_host.c
passes with no -Wall warnings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 18:43:23 -05:00
chrg6
790d201a59 Keep a save file open until the last call through its handle is out, so a CloseFile beside a read or write cannot redirect it.
fs_host.c file_of copied the slot's host file under the shared lock and let go of the lock, and read_file,
write_file, file_size, set_file_size and flush_file then used it with no lock, while close_file on another thread
cleared the slot and closed the file at once. Windows hands a closed HANDLE's value to the next file opened (the
audit's probe got it back in 100 of 100 rounds and read the other file through the stale value), so a call under
way went on in another file: a save write could land in the wrong one. Only the game's own misuse of a handle
reaches it; this is the open file half of AUDIT's F-1-09.

Each call now counts itself in its slot from file_of to a new file_release. close_file closes at once when no
call is under way and otherwise marks the slot closing; the last call out closes the file (file_closed, with the
save bookkeeping close_file had). Nothing waits for another call, the slot and so the guest's handle value is not
handed out again before the close, and a call after the CloseFile gets FS_R_INVALID_ARGUMENT, as before for a
closed handle. Holding the lock across the I/O was not taken: opens and closes on every guest thread would then
wait for save reads and writes, and write_file's save_touched can wait for a Commit.

Reproduced before the change with stand-in host files that reuse a closed handle's value: the new
tests/fs_file_use_check.py built against the old functions closed during a read, gave the next open the same
handle, and the read returned the other file's bytes. With the change it passes (a close during a read happens
after it, once; a call after the close is refused; 20000 close-and-open rounds beside a reading thread read no
foreign bytes), as do save_share_check, save_retry_check, save_zip_check and the compile checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 18:42:10 -05:00
chrg6
e39f71df39 Run every orderly exit through one sequence that puts RivaTuner's profile back before the slow pipeline save.
The window's close, the guest's ExitProcess and the end of a --seconds run each ran their own list. All three
waited for the pipeline save (up to 5 s for running builds, then the write) before they put RivaTuner Statistics
Server's profile back, stopped HD rumble, wrote the frame-time summary and flushed the save, so a console close,
a session end or an "End task" that killed the process in that wait left the game's limit in RTSS's profile (the
next run takes it for the player's own), a buzzing controller and no exit summary. ExitProcess also lacked
cfg_save and the rumble stop, and it and the window's close had no shared guard, so both could run at once and
write the summary twice while the present thread still added to it.

gfx_present.c rt_exit_begin is the one guard (the first caller runs; another thread waits for good; the same
thread again returns, and svc.c ends the process then rather than resume guest code). rt_exit_steps runs, in
order: fs_save_wait (a Commit under way finishes, none is refused), rtss_limit_restore, input_rumble_hd_stop,
cfg_save (not for --seconds), the prewarm marker clear, prof_write, vk_mem_exit_log, a non-final log flush
(the new logq.c logq_flush, bounded trylocks, queue left on), vk_pipeline_save_exit, and fs_save_flush last.
perf.c ft_stop keeps the present thread's frame-time updates out of the exit's summary and waits at most 50 ms,
on the exiting thread, for one under way. The restore relies on rtss_limit.c's closed flag to stay restored
while frames go on during the pipeline save. The log order changes: "frametime: exit" and the stutter-map exit
lines now come before the pipeline save's lines.

Not reproduced before the change. tests/exit_order_check.py (new: the step order, the once-guard with a second
thread, ft_stop's wait and bound, and statically that the three exits use the helper with the RTSS restore
before the pipeline save), tests/logq_check.py (with a logq_flush case), tests/save_share_check.py and the
compile checks pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 18:42:10 -05:00
epicmeme546
f5da269add Remove the experimental co-op through Steam.
This takes back the three commits that added it (cd2f9f1, e60677e, a01910d): its runtime layer and Steam binding, the Co-op tab and what it drew over the game, the launcher's copy of Steam's library file, its settings, tests and docs. The tree is as it was before them, with the bug capture that came after kept: its note on screen no longer makes room for co-op's lines, and dbg_area, which the co-op had brought and the capture uses, stays.

The four settings of that co-op join the retired names, so a settings file that still has them logs no unknown key. A steam_api64.dll that the launcher put beside the game is left where it is: nothing loads it any more.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-13 09:08:40 -05:00
epicmeme546
cd2f9f12be Add an experimental co-op through Steam: rooms, invites, names in the world, chat and shared progress.
Up to four players, each in their own game and save, meet in a room. Rooms are Steam lobbies and the games talk through Steam's networking, so nobody forwards a port and nobody runs a server. The game signs in to Steam as its test app (480), so Steam shows the player as playing Spacewar. Off by default; the menu has a Co-op tab with Create a room, Join a room by its ID, Invite a friend, the players, chat and Leave, in English, Russian, Brazilian Portuguese and Simplified Chinese.

runtime/steam.c loads steam_api64.dll from beside the exe at run time and declares the C functions it calls itself; nothing of the Steamworks SDK is in the repository, and a player copies the file from a Steam game they have. runtime/coop.c is the room: the lobby's owner is the host, lets games of the same protocol and game build in and lists them, and every game takes positions, chat and progress only from the players on that list. runtime/coop_game.c reads Link from the Player actor and shares progress, items, songs and quest items through the game's own flag routines, each value only ever raised; small keys, rupees, ammunition and hearts stay each player's own, and the save is copied to coop-backup before anything of a room reaches it. The other players show as a name with a distance where their Link stands, projected with the upscaler's camera.

One Steam account cannot be in a lobby twice, and this PC has not the memory for two games, so the room and the game side were run with one game and tests/coop_peer.py as the second player over runtime/coop_local.c, a stand-in for Steam on 127.0.0.1 that only the environment turns on: the peer was let in, saw the game's Link in Hyrule Field, chat went both ways, the save was copied, one flag the save lacked was set and a flag the game does not share was refused. On Steam itself one game signed in as app 480, made a room, showed its ID and the player's Steam name on the Co-op tab and left. Not run: two Steam accounts in one room, an invite, a join by room ID over Steam, a host that leaves, a removal, and the menu's text probe on this build.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-13 04:16:17 -05:00
epicmeme546
c84c035939 Remove the co-op of 0.8.0: its runtime layer, relay, menu tab, settings, tests and docs.
The hidden beta shared story progress through a relay a player had to run and never showed the other players. A new co-op replaces it. Gone are runtime/coop*.c and their headers, the menu's Co-op tab and its texts in four languages, tools/coop_relay.py, the co-op tests, docs/coop.md, the [coop] section of nx2navi.ini.example, the --probe-coop mode, the coop trace item, and Monocypher, which only this layer used.

The old settings stay known as retired keys, so an nx2navi.ini that still sets them logs no unknown key, and they stay on the list of private settings, so a bug report keeps a room's secret, a relay's address and a name out. The platform layer's UDP sockets and fs_save_snapshot stay for the new co-op; cfg_ini_now and fs_profile_path, which nothing else called, are removed.

Checked: the game builds and starts, the menu shows its four tabs, --probe-cfg and --probe-fs pass, and the launcher's private-settings test passes. tests/save_share_check.py builds without its co-op stub; it cannot run from this worktree because the folder's path is longer than Windows allows, on main too.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-13 03:37:26 -05:00
chrg6
acc1ae25bf Merge co-op, off by default: rooms through a relay, progress sync between the players' own saves, and a Co-op tab.
Without the other players drawn in the world, which are unfinished. No relay address is built in, and with co-op off nothing opens a socket. Built on Windows and tested against a relay on this PC only; two games in one room and any real network have not been tried.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 00:40:20 -05:00
chrg6
fc1a50f0db Merge the audit: its map and ledger, the perf-trace markers and size lines, and the fixes of its Phase 4.
Verified by runs in the audit: the Market's periodic stall (a pool slot was listed once per refresh pass instead of once; the list fell from 160,385 entries to at most 3,192 and no frame over 20 ms remained while standing), orderly exits delete the pipeline prebuild's running marker (left behind, the next start wrote a skip file and every later start skipped the prebuild), the log and crash report name an area a save asks for that the game does not have, and the menu atlas hash no longer overflows a signed multiply. The other fixes compile and pass the regression route but their faults were never reproduced: see AUDIT.md section 13 for each.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 00:39:08 -05:00
chrg6
7e07e13bac Add co-op, off by default: rooms through a relay, progress sync between the players' own saves, and a Co-op tab.
Each player keeps their own game and save. One player makes a room, the others join it with its secret, and story flags and, by the room's rules, items, songs, quest items, small keys (a shared pool), rupees and ammunition reach every save. The layer is a host thread with one UDP socket to a relay the players run themselves (tools/coop_relay.py, Python's standard library only); the messages between the players are sealed with a key from the room's secret (XChaCha20-Poly1305, Monocypher 4.0.2 vendored with its licence and a row in THIRD_PARTY_NOTICES.md), every change is signed by the player who made it, a player accepts a room by its members' keys and the key of its rules, the save is copied to coop-backup before anything is applied, and values are set through the game's own routines between frames. The Co-op tab makes, joins, accepts, removes and leaves, and the menu takes typed text for the name, the relay and the secret. This is the contributor's pull request #518 on this line.

Nothing reaches the network while co-op is off: the socket is opened, and the relay's name looked up, only after co-op is on, a relay is set and a room is made or joined. There is no default relay.

Left out of the pull request: the other players drawn in the world (the "puppets") with the copied engine they need. They were unfinished, added two patch sites to the game's per-frame update that could not be put through the differential tests here, and their file helpers clashed with the ones this line already has. So no position is sent: the POS message's codec stays, nothing sends it.

Changed against the pull request:
- The menu's test hook drives the Co-op tab only when it comes from the environment. From nx2navi.ini it could turn co-op on, join a room and accept its offer.
- The log's label for a room is hashed from the secret instead of being its first three characters.
- A game that has not saved yet has no save to copy, and takes nothing from the room until it has saved once; that used to count as a copy made.
- A PING's token no longer carries the PC's uptime.
- On Windows the maker's key file and its folder get an access list with the player's account alone.
- The tests that link the platform layer get the socket library, and the save test a stand-in for the title screen's hook.

Checked on Windows, where the pull request had never been compiled: the runtime builds and links; --probe-coop passes (sockets on 127.0.0.1, the codec against its byte vectors, a fuzz of every parser, two sessions through a hostile mock relay, a relay restart, the sync engine); tests/coop_relay_test.py passes its 61 tests, with the probe through the real relay on 127.0.0.1 at 0 and 30 % loss; tests/coop_page_check.py passes and its deliberate fault fails 48 checks. Not checked: two games in one room (one game runs at a time here), any real network, the tab in the game.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 00:11:41 -05:00
chrg6
8ce64407f0 Hold the save file system's lock while a directory listing is read or counted, so a CloseDirectory on another guest thread cannot free it underneath.
File handles keep their window: a read still uses its host file after the lock is dropped.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 23:28:26 -05:00
chrg6
7ccb958c47 Let a save Commit that is under way finish before the crash report or a fatal error ends the process, for at most two seconds.
The game's Commit returns once the commit is queued, so the game may already have reported the save as done while its thread still copied; ending then left an unfinished copy that the next start rolled back.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 23:26:01 -05:00
chrg6
5ddff078c3 Count the save Commits under way instead of flagging one, so a second Commit entered beside the first keeps save writes out of its copy.
With a flag the first commit thread's end cleared it while the second still waited for the transaction lock; a write to the save then passed the wait and landed in the working copy the second thread was copying.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 23:25:39 -05:00
chrg6
dda12051f5 Add heap_lazy (off by default): on Windows the game's heap is committed where it is first used.
Windows charges every committed page to the commit limit whether it is used or not, and the game sizes its whole heap up front: in Castle Town after Hyrule Field the heap held 6.8 GiB of commit of which 3.4 GiB had ever been used. With heap_lazy = 1 a change of the heap's size only moves its end, an access to a reserved heap page commits the reserved runs of the 64 KiB around it in the access-fault hook and runs again, and the paths that do not fault commit their range first: mem_thaw (file reads, the host-pointer import), mem_kernel_access (file writes, the frame-rate fix's read) and mem_host_readable (the host's pointer checks). A page another path committed is left as it is, so a frozen, probed or armed page keeps its protection. With trace = perf a line per window gives the faults, the bytes committed and the time they took.

The contributor's runs on a 16 GB PC: 4.67 instead of 6.77 GiB of heap committed and 29.3 instead of 32.9 GiB of process commit in Castle Town, no "system commit low", 12,858 faults in 440 ms over four minutes, the longest 41 ms. With the setting off nothing is hooked and the heap is committed as before.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 23:14:00 -05:00
chrg6
ded13958de Log the sizes of the caches, pools and tables under the perf trace, every 600 presents and with each memory snapshot.
Four "mem: sizes" lines give the entries and bytes of what no other line counted: the guest heap, physical maps, kernel region, lazily written ranges and the fixed kernel, setjmp, save-file, RomFS and zstd tables; pool records, mirrors, deferred destructions, upload and staging buffers, the device address table and the texture, sampler and slot caches; image views, descriptor spares, the completion queue, uniform snapshot arenas, recording copies and pins; and pipelines, layouts, stage libraries, job queues, the shader memory cache and the translator processes. Each subsystem reads its own counters under its lock; settings now count the replaced values they keep. Segment and scene lines say "-" for the world clock before it starts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 19:51:36 -05:00
chrg6
524cd23266 Merge the save export and import for bug reports into the test line.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-09 21:11:18 -05:00
chrg6
57cdfa1520 Add wb_probe = 1, which finds out who reads what the GPU wrote back into guest memory.
Per-pass GPU timestamps put the write-back of GPU-written storage buffers at 3.6 to 5.3 ms of GPU time a frame in play, against 1.8 to 2.5 ms for all of the game's own passes and dispatches: about 67 MiB a frame goes from the mirrors back into guest memory, from the same eleven pools all session, in case the CPU or a host path reads it. Nothing said whether anything does.

With wb_probe = 1 the 2 MiB regions that lie wholly inside a range just written back are made inaccessible. The first read or write of such a region faults; the handler gives the region back the protection it had (read-only when frozen, else read-write), notes the address, the instruction and the thread, and the access runs again. The bytes are in guest memory all along, so nothing changes for the game. A log block every perf window gives the regions armed, the probes a read or a write ended, and the first accesses by instruction. Every thaw ends the probe of its region first, which covers file reads into guest memory and mapping changes; a file write from guest memory thaws its buffer while the probe is on; a probed region is not frozen anew.

The platform layer gets a hook for every read or write access fault beside the write-fault hook; it is installed only with the setting. --probe-gfx-abi checks that nothing is armed without it and, run with NX2NAVI_WB_PROBE=1, that reads and writes end a probe and run again, that a frozen region thaws after it and that mem_thaw ends and holds.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-09 21:01:47 -05:00
Sebastian Choperena
59069502f3 Stop a second copy of the game before the shader prewarm, not after it.
The one-game-per-profile lock was taken at the first storage use, after the boot prewarm. A second copy started
while the game ran (a second Play, a shortcut) spent 14 to 30 s translating and building on every core and committed
about 10.9 GB before it ended with "another copy of the game is running", enough to bring a 48 GB machine to its
commit limit and stall the copy being played. rt_boot now takes the lock first; storage_init takes it again, which
plat_lock_file answers from the handle it already holds. A restart from the settings menu is unaffected: main waits
for the previous process to exit before rt_boot.
2026-11-09 13:15:47 -05:00
chrg6
b2074499de Let players export their save into one zip for a bug report and import a save someone shared, in the launcher and in the game's menu.
The launcher's home has a Saves screen: Export Save writes NX2navi-save-<date>_<time>.zip into Downloads (or a chosen folder) with a manifest, the save the game would load next and, for a bug report, the game's latest log and nx2navi.ini, never a key file; Import a Save checks a zip's manifest, game, paths and CRCs, backs the current save up into the saves folder and replaces it while the game is not running. The game's Game tab has the same exports and an import that waits in profile/save-import and becomes the save at the next start, since the running game would write its progress back over a save replaced under it.

The zip code (runtime/save_zip.c) and the import at start are checked without game files by tests/save_zip_check.py and tests/save_share_check.py; the launcher side by its test suite.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 13:13:43 -05:00
chrg6
8bd2d6aa31 Run one game per profile at a time.
Nothing kept a second copy of the game off the same profile (a double start, or the restart going ahead after 30 s
while the old process still ran). Its start took the first copy's commit in progress for an interrupted one and
deleted it, removed the first copy's working copy under its open files, and then both wrote into one save-tx/work,
so saves were lost or mixed. The save storage now takes <profile>/instance.lock (plat_lock_file: a file no other
process can open on Windows, flock on Linux) and a second copy stops with "another copy of the game is running".
--probe-fs gives the lock back before deleting its temporary profile and passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 15:51:48 -05:00
chrg6
a1f71328c0 Tell the game its own title id, 0400486024DEC000, for GetInfo and its save data checks.
GetInfo's program id and the application id IsSaveDataExisting and MountSaveDataReadOnly answer for were another
game's. The game asking about its own save data by its own id was told there was none; asking with the other id
mounted its own save. Both are now this game's id, and --probe-fs passes with it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 15:51:48 -05:00
chrg6
7da472bbb1 Keep the profile working when it is linked elsewhere or account.uid is lost, and check save write sizes safely.
- A profile, save or account folder that is a junction or symbolic link (a profile moved to another drive and linked
  back) stopped the start at "cannot create directory"; it is now taken as the folder. A save folder linked to
  another drive than save-tx cannot take commits (a folder rename cannot cross drives); the log now says so instead
  of blaming a file in use.
- A missing account.uid made a new account, and the save under the old account's folder was no longer found (a
  profile copied without the file, or only its save folder put in place). When save/ holds exactly one account
  folder, its account is taken. A file that is there but cannot be read now stops the start instead of being
  replaced.
- WriteFile's check of offset plus size against the file size could wrap for a size near 2^64.
- The first save mount deletes the last session's working copy with the retries the start uses, so a scanner
  holding a file there no longer stops the game.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 15:51:48 -05:00
chrg6
e922b68866 Let --probe-fs wait for its commits without marking the process as exiting.
The probe waited for each Commit's background thread with fs_save_flush, which since Quit keeps saves safe also marks
the process as exiting: every later Commit was refused ("the game is closing") and 20 of the 160 checks failed. It
now waits with save_wait_commit; all checks pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 15:51:48 -05:00
chrg6
501d5bd084 Release pass: fix crashes, freezes and races, cut VRAM use, and translate more shaders.
Crashes: the render-target heap (HeapDef DynamicTextureAllocatorMem and its parent) grows with the widescreen
picture, so the pause menu no longer reads a null block at 5120x2160; texture uploads keep the game's memory mapped
while they copy it, so UnmapPhysicalMemory no longer pulls it away mid-copy; DLSS Frame Generation no longer crashes
in Streamline's set-up (physical devices and the window surface go through its proxies, its swapchain images are
counted first, resources are tagged per frame); a stale core-wait target is cleared before its thread is freed.

Freezes and races: a resize during a swapchain rebuild is kept, a failed frame generation swapchain is retried
instead of replaced by a plain one, three acquire timeouts in a row rebuild the swapchain, device waits take the
queue lock, a translator child that stops answering times out, an in-flight compute pipeline builds at normal
priority, a save Commit started before the window closes finishes and none starts after, a settings change just
before closing is saved, and a movie sample taken before a flush is dropped.

HDR: Enhanced HDR extends the game's own tonemap so highlights go above the picture's white (the hue slider,
which no longer applies, is gone from the menu); frame generation gets a HUD-less picture, the HDR flag and the HDR10
metadata under HDR too.

Memory: ASTC textures are encoded to BC3 unless the PC has 20 GiB of VRAM and 48 GiB of RAM, as Windows charges video
memory to the commit charge (a long session held 17 GiB of RGBA8 textures and 45.7 GiB of commit on a 32 GiB PC).

Shaders: the translator lowers spill-mode programs with more than eight uniform register groups in a wider spill
layout, and local loads and stores with a uniform-register address (36029 of the game's 36031 variants translate,
20 failed before).

The launcher release ships the shader tools and AMD's FidelityFX library (make_launcher.py --ffx); windres there gets
the toolchain on PATH.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 16:02:30 -05:00
chrg6
99de7b943f Give each file of copied code one license line.
Each file that contains copied code names its copyright holder and license and points at THIRD_PARTY_NOTICES.md; zlib code keeps its full notice and the mark that it was altered. Comments and probe text only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 05:04:40 -05:00
chrg6
1b39aabd37 Describe what the code does in comments and docs.
Comments say what the code does and why, docs/maxwell.md keeps the Maxwell facts, and fetch_refs.sh clones only the checkout the build reads. Code is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 05:04:40 -05:00
chrg6
77d0c0b84e Port Ocarina of Time (Switch 2) to the recomp host
The NVN2 front end and the SM86 to Maxwell shader lowering (tools/xlate/AmpereLowering.cs, mesh and task stages,
bindless texture pools, BRX jump tables, the NVN2 compute c0 layout) with four translator patches (0017-0020) bring the
opening of the game to 58-60 fps on the Vulkan renderer.

This round fixes the haze: SM86 IPA is lowered as a plain Maxwell IPA.PASS (texture coordinates were corrected for
perspective twice); LDSM, TXD, both F2IP byte forms, HMMA, MOVM and HFMA2.RELU are lowered and spill mode has eight
uniform register groups; conditional rendering keeps the newest counter report per address and treats a report as
pending until its value is in guest memory, so occlusion tests and the depth clears around the depth copy behave; and
the upscaler stand-in decodes the post pass's sRGB picture before writing the linear target. Tensor-core programs keep
the stand-in unless NX2NAVI_NATIVE_TENSOR=1, because their lowering does not match the hardware yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 05:04:39 -05:00
Valiantsin Dzerakh
0478c13e45 Import the base tree (MIT, see LICENSE) 2026-11-07 05:04:38 -05:00