chrg6 2e077edc9d Fetch the lazy write-back's ranges without the freeze lock, so a touch of GPU-written memory holds up only the threads that touch that range, with wb_lazy_unlocked = 0 to go back.
A CPU touch of an armed range ran its fetch (vk_memory.c lazy_fetch: a GPU copy after everything handed over, and a wait of up to 10 s) inside the fault handler under frz_lock. Every other fault, thaw, freeze and arm waited for the whole copy meanwhile: the replay's copies of guest memory (mem_copy_readable, vk_memory.c mirror_capture) and its freeze pass, the completion thread's arms, file reads and pool finalizes. A write that lost the race to the fetch went to thaw_fault after it and released every other armed range of its 2 MiB region, a second fetch. mirror_evict and hot_check fetched their victims' ranges under the exclusive pool_lock, which the completion thread's arms and every pool lookup wait for. This is AUDIT F-4-05 (C-12); the design's probe of the unchanged code had an unrelated mem_copy_readable, arm and freeze each wait 299 ms behind a 300 ms fetch, and the losing write 378.8 ms with two fetches.

A range now leaves the armed list into a table of ranges in flight (memory.c "Ranges in flight", lz_fly) in the same lz_lock hold, and stays there, its pages inaccessible, until its bytes are down and its pages read-write again (lazy_fly_run), without frz_lock. A fault on a range in flight waits for that entry alone on lz_lock's condition and runs the access again, the losing write included; a thaw, a mapping change, a release of its buffer, an arm, a freeze and mem_lazy_touches see it. Every store to lzr is made under lz_lock from the list, the entries and the region an arm is protecting (lz_arm_r, which replaces the previous commit's restore). An entry starts under frz_lock and lz_lock and ends under lz_lock alone, so a holder of frz_lock may wait for one: its fetch takes only the queue lock, which no frz_lock taker holds. mem_thaw releases what is armed in its regions before it takes frz_lock (lazy_release_regions), so file reads and pool finalizes fetch without it; a mapping change's thaw still fetches under it, as its page calls follow. The holds a fault sets (lz_hot, lz_hots) are now set as the range goes into flight, so an arm meanwhile finds them. The fetch takes several copies in one submission and one wait (MemLazyCopy), and the back end has four slots of command pool, command buffer and fence, so fetches of different ranges run at once; a release takes up to 16 armed ranges out together.

mirror_evict and hot_check now fetch what only their victims' mirrors hold before taking pool_lock exclusively, and then, under the lock, release only what was armed since (usually nothing) before the mirror leaves the list and the device address table names the imported buffer again. The design moved the release after the lock instead, which would have had the table change before the fetch: a shader of a submission still running reads through the table as it changes, and the fetch waits for every submission handed over. A victim that vk_pool_fini took off the list meanwhile is left to it; hot_check copies its log line's fields under the lock, as its records may be freed once the lock drops.

The new setting wb_lazy_unlocked (NX2NAVI_WB_LAZY_UNLOCKED, default 1, restart) set to 0 keeps frz_lock from the take-out to the end of each flight and leaves mem_thaw's releases to the thaw, as in 0.10.1; the table, the slots and the eviction order stay. The "mem: lazy write-back" line counts segments fetched; a second line gives the touches that waited for a range in flight, the longest such wait and the most ranges in flight at once; "mem: sizes" counts fetches that found every slot busy. The frz_lock and lz_lock comments say what holds what now (B-12). mem_lazy_touches has callers outside memory.c (camera_speed.c, nvn_api_cmd.c ctl_fingerprint), which now skip a range in flight instead of faulting on it and waiting.

Not reproduced in the game before the change. tests\lazy_fetch_check.py now runs 20 scenarios as their own processes: arm races, no stall of freezes, copies, arms, thaws and releases of other ranges during a fetch, joins with frz_lock held elsewhere, a losing write that keeps the region's other range armed, two fetches at once, a thaw, a mapping change and a release meeting a range in flight, an arm refused over one, the foreign give-back, the chunk mode, the old wait with wb_lazy_unlocked = 0 (joins and losing writes as well), the report lines, 80 touching threads past the 64 entries, and a freeze refused; all pass, in six runs. Taking the join before frz_lock out fails the join scenario. cfg_rows_check reports 0 problems; every runtime file compiles, and memory.c and vk_memory.c add no -Wall -Wextra warning; the test matrix passes 75 of 77, installer_test and worktree_test failing for the environment as before. vk_memory.c's slots and the eviction's order have no check without a device.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 20:12:50 -05:00

NX2navi

NX2navi

Join the official NX2navi update discord server: https://discord.com/invite/uf6gSrsPpq

NX2navi is a static recompilation of The Legend of Zelda: Ocarina of Time for Nintendo Switch 2 (game version 1.1.1) into a native x86-64 application for Windows, with a Vulkan renderer (docs/architecture.md).

Important

No game files are included. You need your own legally obtained copy of the game, dumped from your own console. Instructions for obtaining those files will not be provided.

The game is built from your files on your machine, so the result contains the game. It is for your own use. Do not share it.

Status

Early development, playable. Kokiri Forest, Kakariko Village, Death Mountain Trail and its crater, the Temple of Time and the Great Fairy fountains play at 60 fps and above on current GPUs, from the title screen and from saves. Visual bugs and occasional crashes are still expected (Known issues).

Report problems as issues, following CONTRIBUTING.md.

Features

  • Native code, not emulation
  • 60 fps by default, or a limit of 30 to 240 fps, the display's refresh rate or unlimited
  • Native widescreen and ultrawide (the window's shape, 21:9 or 32:9) with the HUD, menus and movies kept 16:9
  • Game resolutions from 1080p to above the game's 1440p: 1800p, 4K, 5K or the window's own size, sharp on high-DPI displays
  • Upscaling and anti-aliasing: NVIDIA DLSS (DLAA, Quality, Balanced, Performance, Ultra Performance; RTX GPUs) or AMD FSR 3.1 (Native AA and the same modes; any GPU) or AMD FSR 4 (the same modes; Radeon RX 6000, RX 7000 and RX 9000 GPUs), which render the 3D scene at the mode's share and keep the HUD sharp; or AMD FSR 1 or bilinear scaling to the window, with a sharpness setting; black bars or stretch
  • Frame generation: NVIDIA DLSS Frame Generation (RTX 40 and 50 GPUs, paced with Reflex) at 2x, or 3x and 4x on RTX 50 GPUs (one, two or three generated frames for every frame the game draws), or AMD FSR 3.1 (any GPU), a generated frame between every two the game draws, with the HUD kept out of it, for about twice the frame rate on a fast display; in a window of another shape than the picture, frames are generated inside the picture only, so the black bars stay clean. The frame limit counts the frames the game draws, and frame generation adds its frames on top of it. The frame limit can also be handed to RivaTuner Statistics Server, which then limits the game in its place once it is seen holding the game; the game paces movies itself, and takes the limit back while it is minimised and whenever RivaTuner is not seen holding it; where frame generation or low latency keeps it out, the game says when a limit set in RivaTuner itself holds the game as well
  • HDR on HDR displays, two kinds: the game's scene tone mapped to the display's peak, matched to the game's mid grey and tone, with true blacks, or the game's own native HDR; peak, game and HUD brightness in the menu
  • The game's known shaders prepared when the game is built, and the pipelines met in play built again in the background at each start, so play rarely stops to compile them; a shader or pipeline met for the first time can still hold a frame, and a draw that must wait for its pipeline has it built on worker threads, never on the game's drawing thread
  • In-game settings menu (F1, or Minus and Plus together on a gamepad) and performance overlay (F10)
  • Keyboard, mouse and gamepads with remapping, Nintendo, Xbox or PlayStation button layout, rumble and gyro; the game follows whichever connected gamepad you press, and DualShock 3 controllers are read directly when enabled
  • Xbox, PlayStation, Steam Deck and keyboard and mouse button prompts in the game's own text, drawn on your PC when the game is built; the keyboard prompts follow your key bindings
  • Stereo or 5.1 surround sound output, on the output device of your choice; the default microphone for the game's microphone input
  • The monitor the game opens on, straight in fullscreen when fullscreen is on
  • Your Discord status shows "Playing NX2navi" with the play time (on by default; the Game tab of the menu or the launcher's Settings turns it off)
  • Docked or handheld console mode
  • The game's textures stay compressed (BC3, about half the video memory at the same speed) unless the PC has 20 GiB of video memory and 48 GiB of RAM, as Windows counts video memory against the system's commit limit

Installing

Download NX2navi-Setup-<version>.exe from the releases and start it next to your .nup dump. It is the only file you need. The launcher it installs finds the dump, downloads the build tools, builds the game on your PC (20 to 45 minutes, once) and starts it; afterwards it keeps the port up to date and rebuilds only what changed (docs/launcher.md). docs/README-BuildKit.md describes the same build by hand, docs/building.md building from source. Settings are in the in-game menu and in nx2navi.ini (nx2navi.ini.example lists every key). Controls: docs/controls.md.

Requirements: Windows 10 or later; an x86-64 CPU with SSE4.2 (AVX2/FMA machines get a faster build); 16 GB of RAM; about 34 GB free for the game and 1.5 GB for the launcher and its tools; a GPU with Vulkan 1.3, VK_EXT_external_memory_host and VK_EXT_mesh_shader. The setup is not signed, so SmartScreen warns on its first start.

Known issues

  • The first start after an install or update, and the first visit to an area, can pause briefly while the graphics driver builds what it has not seen; later starts are smooth.
  • Machines with 16 GB of RAM can run out of system memory in Hyrule Field.
  • Integrated AMD GPUs can fail at the title screen (their host memory heap is too small for the game's memory).
  • A rare crash in the game's particle code.

License

The source is public to read. Its additions and changes are all rights reserved (LICENSE-ADDITIONS): no part of them may be copied or published without written permission. The base import stays under its MIT License, and third-party code keeps its own license (THIRD_PARTY_NOTICES.md). No license grants anything for Nintendo's game.

NX2navi is an independent, non-commercial fan project, not affiliated with or endorsed by Nintendo. The Legend of Zelda, Ocarina of Time and Nintendo Switch are trademarks of Nintendo.

Building from source

docs/building.md lists the toolchain and the steps. tools/make_game.py also builds the game natively on Linux (docs/README-BuildKit.md, "Linux"); the launcher and the setup are Windows only. Everything the build writes stays under build\ and generated\, which git ignores, as it does the runtime's shaders the build compiles (runtime\*_spv.h; the repository holds their source only); run tools/check_content.py before a commit.

Description
A mirror of NX2Navi for safe keeping
Readme MIT 98 MiB
Languages
C 70.1%
Python 23.7%
C++ 3.6%
C# 1.7%
GLSL 0.7%
Other 0.2%