Commit Graph

83 Commits

Author SHA1 Message Date
chrg6
ef896bbb02 README and LICENSE-ADDITIONS: drop the clause about machine learning and AI tools.
The additions stay all rights reserved: copying and publishing them still needs written permission.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 04:18:08 -05:00
chrg6
092379def8 Frame dumps keep each shader's guest program, and a test switch runs an edited SPIR-V in place of a compute shader.
NX2NAVI_DUMP writes sh_<hash>.code next to sh_<hash>.spv: the guest program the shader was translated from, as its
stored inputs hold it, for a disassembler (a compute program as it is, a graphics stage after its 128-byte header).
NX2NAVI_SPV_OVERRIDE=<dir> runs <dir>/<hash>.spv in place of the compute shader whose SPIR-V has that hash, an edited
copy with the same interface, to look at a shader's intermediate values in a frame dump.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 04:10:35 -05:00
chrg6
8f5e2fe76e Make Enhanced HDR's highlights brighter: above 0.67 of the game's white a boost that starts flat and grows to one white per unit of scene light replaces the old tangent, so bright light keeps climbing instead of ending near white, with mid-tones unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 04:10:35 -05:00
chrg6
d9fb0fe1ba Give characters in Ocarina's frame their own motion for DLSS and frame generation, so a running Link no longer trails.
Ocarina's frame has neither the per-object motion pass nor the class buffer the characters' mask reads, so DLSS and
FSR 3.1 frame generation moved every pixel with the camera: the character the camera follows dragged a faint copy of
itself through DLSS's history and doubled in generated frames. The motion pass (dlaa_mv.comp, vk_dlaa.c mv_pass) now
keeps each pixel's view distance for the next frame and checks the camera's vector against it. Where last frame's
depth at the place the camera's motion points to disagrees, the surface moved on its own; when last frame's depth at
the same pixel agrees, it kept its place on screen and its vector is zero. Such pixels also get the characters' bias
mask (dlss_char_mask), so DLSS takes this frame's colour where its history holds something else. The check takes the
best of 3x3 pixels, so silhouettes reprojected a pixel off are not taken for motion, and leaves the screen border to
DLSS. NX2NAVI_DLAA_ZFIT=0 turns it off; NX2NAVI_DLAA_MASKDEBUG=1 shows the mask.

Frame dumps list a border sampler's colour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 04:10:35 -05:00
chrg6
5aa3bc4119 Render widescreen pictures at the DLSS mode's real share, and size frame generation for the scene it is given.
The game sizes its scene from the scale at the patch page against its own 1440-line picture, so on a 2160-line
picture native DLAA rendered a 1440-line scene and Quality 44 %. The page now gets the share times the picture's
height over 1440, and DLAA is native at any picture size. Switching dlaa off in the menu also gives the scene back
the game's own scale instead of leaving it small with nothing to enlarge it.

FSR 3.1 frame generation was created with the window as its largest render size, but it is given the scene's depth
and motion vectors, up to the picture's size. A 3414x1440 or 5120x2160 scene in a 1920x1080 window wrote past its
resources: a crash in amd_fidelityfx_vk.dll, device faults, striped frames. The context is now made for the picture's
size once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 04:10:35 -05:00
chrg6
432cbdffe8 Hold the frame limit on screen while FSR 3.1 frame generation runs.
Frame generation presents a generated frame between each two of the game's, so a limit of 60 showed 120. While
generation is on (not paused for the menu or for want of depth and motion), the game is paced at half the limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 04:10:34 -05:00
chrg6
b62ffac721 Apply the DLSS mode's render scale every frame, so native DLAA and the other modes take effect when picked.
The res_fixed patch sends the game's dynamic-resolution controller down its fixed path, which returns its scale with
state 1 or 2. The caller makes a new scene scale only on state 0, so the menu's mode reached the scene only by chance:
a mode switch in play left the scene at its old size, and a scene reload put it back at full size. The fixed path now
returns state 0, and the page's scale applies every frame. Ocarina also no longer logs that DLSS needs
nvngx_dlss.dll and follows render_scale, which was true only of the builds with a scene patch set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 04:10:34 -05:00
chrg6
f57a327a86 Keep saves safe in Import Saves and Quit, draw no traced game art, and recompile the game for a new processor.
Import Saves: the saves folder picked in the dialog imports what waits in it (it had made the folder's README the
save); the save backed up is the one the game would load, in runtime/fs_host.c save_recover's order, so an interrupted
commit's only copy is kept, and the rest of the commit journal moves into the backup instead of being deleted; the
imported save goes into place before the old one is deleted, which comes back if that fails; another NX2navi install's
finished commit counts as its save. Tests cover each.

Quit waits while the game writes a save, as Restart does: the game runs on under the menu, and closing the window
finishes only a commit that has begun. The VSync row says "in use: Immediate" when VSync On falls back to tearing, and
its default follows the device profile (cfg_default). Without the game's textures the menu's divider is a plain line
and diamond of its own, not a tracing of the game's ornament.

make_game.py: the compile key holds the processor's features (cpu_tag), so a game folder moved to a PC without AVX2
compiles again instead of crashing; the main thread gets 16 MiB of stack with GNU ld too; any error waits for the
RomFS image, so its .part file is not cut short. The launcher tests use a neutral title id; tools/menu_preview.cpp
has cfg_default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 03:57:02 -05:00
chrg6
8daedbd806 Redraw the in-game menu with the game's own interface textures, and bring in the launcher's save import and faster builds.
In-game menu: the settings window, its tabs, rows, buttons and cursor, the performance overlay and the Preparing
shaders box are drawn with the game's interface textures where the RomFS holds them (runtime/ui/menu_skin.cpp,
perf_panel.cpp; plain shapes until they are decoded). Restart joins Continue and Quit, each behind a confirmation;
VSync is an on/off switch; DLSS is listed only on NVIDIA GPUs; the menu sounds are quieter; Picture shape is gone and
the setting descriptions are shorter; edge cases of input, layout and fonts are fixed. tools/menu_preview.py draws the
menu without the game.

Launcher: Import Saves takes a save from the game folder's saves/ folder or one the player picks; downloads fall back
to Windows' own certificate check when Python's refuses a server; updates are faster; the title bar is drawn in the
window and Navi is every icon. tools/make_game.py builds with CLANG64's clang where the launcher installed it, packs
the RomFS beside the lift and the compile, takes a prebuilt runtime, and compiles the game's resources without cmd.exe,
so a user folder with a space in its name builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 03:57:01 -05:00
chrg6
5b28da3a06 Make Enhanced HDR extend the game's tonemap again: its matchers take the NClamp the translator writes.
Translator patch 0031 (0.4.0) writes the tonemap's 0..1 clamp as GLSL NClamp, and vk_hdr.c looked for FClamp only, so
Enhanced HDR left the tonemap as it was and showed the SDR picture. The output clamp and the ACES clamp now match
either form, and the replacement keeps the NaN rule: NMax for an NClamp (NaN gives 0, as the clamp did).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 01:25:12 -05:00
chrg6
9bb3c6c55a Hand the frame limit to RivaTuner Statistics Server where it runs, and take it back when RTSS does not hold to it.
Where RTSS is installed and running, the frame limit from the menu goes into RTSS's profile for the game's executable
through RTSS's hooks library, loaded at run time, and the game's own pacer stands aside while RTSS has hooked the game.
Menu changes apply live, unlimited clears the limit, and closing the game (or any exit path) puts back what the profile
held, or removes a profile the game created. Each change is read back from the saved profile: without administrator
rights RTSS's profile folder cannot be written, and the game then keeps its own limiter. While RTSS limits, RTSS's own
frame counter for the game is checked against the limit; well past it for three seconds, the game takes the limiting
back for the run. The log says which limiter is in use and why, and a note under the frame-rate row says when RTSS
limits, when its profile is read-only, and when it does not hold the game to the limit. rtss = 0 and frame generation
keep RTSS out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 01:25:12 -05:00
chrg6
cfc3228cc4 Build in the NX2navi Discord application's id
The Rich Presence now speaks for the Discord application registered as NX2navi by default, so the status reads
"Playing NX2navi" without an nx2navi.ini entry. The ini example shows the id as discord_app_id's default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 01:25:12 -05:00
chrg6
7cd1b6ee23 Leave the NDSR upscaler's slow, unused compute shaders out of the install's shader preparation.
The translator turns the larger programs of agl_ndsr.pck and agl_ndsr_vr.pck into 3-12.6 MB of SPIR-V, which the GPU driver took 34 s to 44 minutes each to build: 19,400 of the 21,750 s every compute variant took together, most of the install's hour of driver builds. Play met none of them that was slow to build, so the default variants of those programs above 16 KB of code are left out; agl_ndsr_vr_light.pck, whose programs play meets, stays whole. --all-shaders prepares them too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 01:25:12 -05:00
chrg6
8f559ac945 Show the Discord status under an NX2navi application instead of the old one
Discord takes the name above the presence from the application whose id the client sends, and the built-in id
belonged to the application the earlier framework registered for its own game, so the status read as that game.
The id and the art key now live in runtime/discord_app.h (NX2NAVI_DISCORD_APP_ID_DEFAULT and
NX2NAVI_DISCORD_IMAGE_DEFAULT), the one place to paste the NX2navi application's id. Until it is set there or in
nx2navi.ini, the presence stays off and logs once how to set it, and --probe-discord skips. The ini example and the
comments no longer claim the old id is registered as NX2navi, and the details-line example uses game 1.1.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 01:25:12 -05:00
chrg6
8702ddb356 Default to the Switch's own button positions on every gamepad, and fix menu edge cases.
pad_layout defaults to nintendo: Switch A on the right face button, B bottom, X top, Y left, on an Xbox pad too, where
auto had picked the Xbox layout. The settings probe checks every button's position.

Menu: the "default" mark follows the Steam Deck profile's values (cfg_default); Left on a value the list does not hold
goes to the last entry; the HDR row says "in use: off" when the swapchain is not HDR10; the open state is read again
after taking the reset, so a menu opened by F1 while the overlay alone was drawn starts with its focus row; the font
atlas is rebuilt only for a scale more than 12% off or one that held for half a second, so dragging the window's edge
no longer rebuilds it every frame.

A shader that fails to translate keeps its request and code in the shader cache. NX2NAVI_GPA_TWIN=0 turns the guest
copy of device address table stores off (diagnosis). The memcpy override no longer prints its first 500 calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 00:34:29 -05:00
chrg6
936dd8094a Translate 40 more shaders, and build the upscaler's compute shaders five times faster in the driver.
The SM86 lowering now takes MEMBAR (as Maxwell MEMBAR.GL), drops ERRBAR, takes F2I of an immediate and MATCH.ALL.U64,
and finds a switch's jump table bound past the BSSY the compiler puts between the IMNMX and the shift: three vertex
shaders, one of them drawn in Kokiri Forest, were skipped for it. A task program and the mesh programs it feeds whose
payload size field reads 0 (it counts 256-byte units) get one unit. Of the 42,177 programs, 31 still fail, all modes
whose meaning is not certain.

Translator patch 0035 inlines SHFL.IDX over the whole warp (mask 0x1f) on a 32-lane host as the subgroup shuffle,
instead of a call to the general shuffle helper. The tensor lowering shuffles thousands of times per program and the
driver inlined the helper at each call: a mid-size upscaler shader built in 185 s before and in 34 s now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 00:34:29 -05:00
chrg6
96b6e04937 Compile the resources with windres when the install folder's path holds a space.
GNU windres runs its preprocessor through cmd.exe and, left to find it, puts the full path of the gcc beside it on that
command line. When that path and the .rc's both hold a space (a Windows user name such as "Joe Budson"), cmd.exe drops
the outer quotes and splits the command at the first space, so 0.5.0's Compile step failed with "'C:\Users\Joe' is not
recognized". make_game.py, build_game.py and make_launcher.py now name gcc by bare name (the toolchain's bin is first
on PATH) with windres's own preprocessor arguments, and make_game.py and make_launcher.py run windres in the .rc's
folder with relative names. The objects are byte-identical to before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 00:34:29 -05:00
chrg6
fc8a9523a4 Prepare shaders under the code hashes play asks for, from every shader package, with better guessed bind states.
The install step seeded only 63% of the programs a play session met under their run-time code hash. A Switch 2 program with an empty c1 range past its code was hashed with that range, the Switch 2 agl and NDSR packages had no reader, and Layout2Shader/ was not walked. It now seeds 99.3% of the programs play asks for.

A default variant's answers are read again from its first translation (runtime/gfx_shader_guess.c): vertex inputs the shader reads as integers become uint, and a compute shader takes the texture and image types its translation declares. Task and mesh defaults carry the workgroup words play answers with. Against a play session's recorded variants, --prepare-shaders now matches 98.3%; the plain default guess matched 89.5%.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 00:34:29 -05:00
chrg6
d0da862c87 Release 0.5.0: fix the white haze in fog and the flickering foliage.
The float atomic add's compare-and-swap loop (patch 0033) carried its expected value round the loop in a local, but the
pass runs on SSA form and the optimizer's copy propagation takes every value as assigned once, so the loop compared
against a stale value and the froxel fog added up wrong: Kokiri Forest was washed out white. Each attempt now reads the
word with an atomic OR of 0, which also cannot return a stale cached copy.

Writes through the device address table go to the guest copy for stores only (patch 0034). An atomic on the guest copy
started from its stale value, so a counter the GPU reset with a buffer fill kept counting there, and the page's next
sync copied the wrong count into the mirror: culled foliage drew garbage for a frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.5.0
2026-11-07 22:05:43 -05:00
chrg6
cd7cce89cb README and AGENTS.md: the source is public to read, its additions still all rights reserved.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 20:41:11 -05:00
chrg6
7f50cc5e29 Third-party notices list only the code the repository copies.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 20:36:29 -05:00
chrg6
65ad25faaf Release notes and README: the tree columns are fixed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.4.0
2026-11-07 20:27:34 -05:00
chrg6
4cb8188491 Write through the device address table into guest memory too, so the GPU's writes there are no longer lost.
The table's records gain a twin: a pool with a VRAM mirror names the mirror first and its imported buffer, guest
memory, second; any other record names one buffer twice. Shaders read through the first address and store and run
atomics through both (translator patch 0034); a page without a twin sends the second write to a 4 KiB scratch buffer.
The GPU's writes through the table were not recorded as GPU writes, so they stayed in the mirror: the CPU never saw
them, and a sync of a page the CPU wrote copied the stale guest page over them. Trees drawn from records lost that
way stretched into columns reaching the sky. With NX2NAVI_MEMORY=verify the Hyrule Field walk found 16 such mirror
pages before and none or one after; GPU time per frame 5.92 ms before, 6.17 ms after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 20:27:34 -05:00
chrg6
8102b6a222 Release 0.4.0: the first public release, with its notes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 19:26:28 -05:00
chrg6
1f016a774e Menu and start-up edge cases: a chosen GPU that is gone, held Esc, the boot screen, and upscaler-dependent rows.
- A GPU index or name that is not usable, or no longer there, falls back to the best usable GPU with a log line instead
  of stopping the game before the menu can change it (vk_device.c pick_device).
- A held Esc no longer opens the menu and closes it again on its first key repeat (menu_win.cpp, menu_sdl.cpp).
- The menu does not open on the boot screen, where it was not drawn while its keys queued up and changed settings once
  the game ran.
- The DLSS render scale follows the upscaler that runs: a DLSS or FSR 3.1 mode picked in the menu, which applies after
  a restart, or DLSS where NGX is not available, no longer shrinks the scene with nothing to scale it back up.
- Upscaling without DLSS or FSR 3.1 also turns frame generation off, which needs their motion vectors.
- The HDR peak brightness slider reaches the setting's 10000 nits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 18:44:56 -05:00
chrg6
9dcfc5723b Profiler: unwind through vendor DLLs only while they are pinned, so slow-frame captures no longer crash at start.
NX2NAVI_PROFILE_SLOW unwound threads in any module, and one being unloaded while the game started (a driver or vendor
DLL) had its unwind tables freed under the sampler. The exe and the system DLLs are walked freely; any other module is
held with GetModuleHandleEx for the step, and one that is unloading ends the chain. The block-linear copy probe gains
two cases of 256 KiB and more.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 18:44:56 -05:00
chrg6
e7f6a00407 Translate every shader, keep atomic loops from spinning on stale values, harden pool teardown, and update the README.
Shaders: a switch whose index is clamped against a register that a MOV of an immediate set before it now finds its
jump table (AmpereLowering.cs BrxTargets), so all 36,031 of the game's shader variants translate and no draw is skipped
for a failed one. The compare-and-swap loop behind storage-buffer atomics (patch 0033) takes the value a failed swap
returns as its next expected value instead of loading the word again, which on some drivers kept returning a stale
copy.

Memory: an unused physical pool outlives the submission being recorded, as virtual pages may still reach it through
device addresses; an image whose creation failed is not tried again for 2 s.

The installer and Linux texts name this game.
The README has the project icon, the current status, features, requirements and known issues.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 18:03:20 -05:00
tangerineturtle1
5b7f6269e1 Update README.md 2026-11-07 17:50:40 -05:00
chrg6
e884abc226 Build a waiting draw's shader libraries and fast link on two library workers, so the drawing thread never compiles.
A draw that waits for its pipeline used to build a library itself when every pool worker was busy (16 times in a cold
run, up to 77 ms; once in a warm run, 97 ms). Two workers now take only those jobs, wake on their own condition so a
pool job's signal always reaches a pool worker, and run the fast link too; the drawing thread only waits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 17:36:21 -05:00
chrg6
c327dc07d8 Generate frames inside the picture only, so a window of another shape keeps its black bars clean, and use the Triforce as the icon.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 17:36:21 -05:00
chrg6
37c06e6894 Sample terrain with the right mip levels, keep NaNs out of min, max and clamp, and give shadow gathers their own tap offsets.
The runtime's TXD gradient transpose (gfx_shader.c patch_txd_transpose) is off for Switch 2 titles: the SM86 lowering
already orders the interleaved derivatives for the translator, so transposing again swapped them back. The
translator's float min, max and clamp are the NaN-safe forms (patch 0031), as Maxwell FMNMX and saturate behave, so a
NaN no longer spreads through lighting and bloom on drivers that pass it on. Depth-compare gathers with per-tap offsets
are split into one gather per tap like the others (patch 0032), where all four taps took the first tap's offset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 16:28:53 -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
Claude
c403547c8b Add the NX2navi launcher: a one-click setup that builds the game from the player's dump, starts it and keeps it up to date.
NX2navi-Setup.exe installs the launcher into %LOCALAPPDATA%\Programs\NX2navi without administrator rights, with
shortcuts and an Apps entry. The launcher finds the player's .nup, downloads the build tools itself (the MSYS2 UCRT64
packages toolchain.lock.json pins, checked by sha256 and unpacked without pacman, and zbic from PyPI) and runs
tools/make_game.py, whose steps it shows with their progress. Its window follows the game's options menu: colours
sampled from it, the menu's filigree, the white glowing cursor, the Triforce in the title band and as the programs'
icon, and all text in the game's Cetra Display, decoded from the player's own dump. Mouse, keyboard and an XInput
pad drive it.

Updates come from GitHub releases (NX2navi-update.json): the new version is downloaded, checked and staged beside
the running one, the stub restarts into it and falls back to the previous version when it fails to start, and the
game is rebuilt without the dump. make_game.py now keys its exefs and romfs steps on the game files alone, has
--rebuild and --events, compiles only the files whose source, headers, flags or compiler changed, and replaces
ocarina.exe only once the new one links. tools/make_launcher.py builds the release on Windows or Linux.

Tested: tests/launcher_test.py (19 tests), the real toolchain download and install, and under Wine 11 the setup,
the stub, the window on the bundled Windows Python, an update from a local feed by clicking Updates, the rollback
of a version that fails to start, and the downloaded clang compiling and linking a program against the game's
libraries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Merged onto the current port: make_game.py keeps the launcher's step keys, cached link and in-place swap, and its
own runtime manifest (windres), AMD's FidelityFX library and the shader preparation, which becomes a step of its own
("shaders") that the launcher shows in its install, rebuild and update jobs. make_launcher.py ships the shader tools
and, with --ffx, AMD's library.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 14:50:52 -05:00
chrg6
514bb8eaa9 Translate the 8- and 16-bit uniform constant loads whose unused register field is 0, not 63.
ULDC's immediate form (0xab9) has no register index; the sub-word path required the field to hold URZ, as the 32-
and 64-bit paths do not, and compilers leave 0 there too. Of the game's 36,031 shaders, 40 more translate: 20 failures
are left, all the lowering's room for uniform register groups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 13:42:09 -05:00
chrg6
cedee5ceff Menu: sharpness works under FSR 3.1 too, and frame generation asks for a DLSS or FSR 3.1 mode first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 13:33:43 -05:00
chrg6
8f6bcd367e Build kit: ship AMD's amd_fidelityfx_vk.dll for FSR 3.1 (make_kit.py --ffx) and let its DLLs past the size check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 13:31:07 -05:00
chrg6
ab5450da15 Describe DLSS, FSR 3.1, frame generation, HDR and the build kit in the READMEs; name a null call's caller.
The README takes the name NX2navi as on GitHub and lists the new upscalers, frame generation, the HDR modes, the
higher resolutions and the shader preparation, and points to the build kit for installing. A crash calling address 0
now logs the return address on top of the stack and its module (it was inside sl.common.dll for Streamline).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 13:31:07 -05:00
chrg6
a42648e27a Add the game's native HDR, render at the monitor's real size, and refresh movie frames on the GPU.
HDR is now a menu row: Off, Enhanced (the runtime's own HDR tonemap) or Native. Native reports an HDR10 display to
the game with the settings' peak and paper white; the game then renders its own HDR picture and presents it as PQ in
A2B10G10R10, which goes to the HDR10 swapchain as it is. The peak, game, HUD and (for Enhanced) hue rows follow it.

The build kit's link left out runtime/nx2navi.rc, so the game had no manifest and no icon: without per-monitor DPI
awareness, a 5120x2160 monitor at 150 % gave a 3413x1440 window that Windows stretched. It is compiled with windres
and linked again.

The movie decoder's frames are copied into the game's buffer by the host; the back end is now told of that write, so
the GPU's copy of the buffer does not keep older or empty bytes (green stripes in NV12).

A longjmp out of the game's JPEG decoder now logs the guest functions that called it, to trace a later crash.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 13:31:07 -05:00
chrg6
b9a042969e Keep the HUD out of frame generation, show the frame generation rate, and start DLSS Frame Generation.
Frame generation now gets the picture without the HUD each frame: the upscaler's output before the HUD is drawn over
it, encoded to sRGB as the game's last pass encodes its picture and put through the same colour, scaling and borders
as the presented frame, so it tells the HUD apart and leaves it unwarped in the generated frames
(NX2NAVI_FG_HUDLESS_SHOW=1 shows that picture, a test). The performance overlay gains an FG row and the fps line the
display's rate, generated frames included. The device enables VK_KHR_get_memory_requirements2 whenever AMD's frame
generation is on, which crashed at start with DLSS upscaling.

DLSS Frame Generation through NVIDIA Streamline (vk_dlssg.c, and nx2navi_dlssg.dll from runtime/dlssg, built with
MSVC by tools/build_dlssg.py against the SDK kept outside the repository) creates the instance and device through
Streamline's proxies and calls its swapchain hooks; Streamline still fails in its plugin start-up here, so the menu
lists it only while it is set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 13:31:07 -05:00
chrg6
7c11ff6816 Add AMD FSR 3.1 frame generation: a generated frame between every two the game draws.
With Frame generation set to AMD FSR 3.1 (after a restart), AMD's frame generation swapchain takes the place of the
present path's own: the device creates two more graphics queues for its presents and image acquires, the present
path acquires, gets images and presents through its replacement functions, and every frame configures generation
and records its preparation from the depth and camera motion vectors the DLSS or FSR 3.1 upscaling pass made (one of
them must be on). Generation pauses while the menu is open. On an RTX 5090 at a 60 fps game, about one frame was
generated for every frame drawn in play.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 13:31:06 -05:00
chrg6
ca5e5eb5a4 Add AMD FSR 3.1 upscaling, and run DLSS's smaller modes on a 16:9 picture too.
The Upscaling choice gains FSR 3.1 Native AA, Quality, Balanced, Performance and Ultra Performance. FSR 3.1 runs
through AMD's signed amd_fidelityfx_vk.dll (loaded at run time; the build kit ships it) in DLSS's place, on the same
scene light, depth and camera motion vectors and at the same render scales. Its Vulkan back end needs
VK_KHR_get_memory_requirements2, which the device now enables when FSR 3.1 is chosen.

A half-size effect pass with three targets and a half-size depth was taken for a G-buffer, which switched every later
frame to the deferred frame shape: there the output pass of a smaller scene was never found, so DLSS Quality,
Balanced, Performance and Ultra Performance did nothing on a 16:9 picture. Such a pass smaller than the scene is now
left alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 13:31:06 -05:00
chrg6
fc6d84aa63 Prepare every one of the game's shaders when the build kit builds the game, so play does not stop to compile them.
The shader reader now reads Switch 2 (NVN2) programs, whose 0xe0-byte control sections and task and mesh stages the
runtime keys as it does, and runs on Python 3.12 through the zstandard package. The kit's new last step writes the
36,031 shaders of the player's game files as a seed and runs ocarina.exe --prepare-shaders, which translates them and
has the GPU driver build each one's library, about 5 minutes. On a cold walk into play this took the stalls from
14 seconds of pipeline builds and 9 of translations to none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 13:31:06 -05:00
chrg6
ee6fbaf0bb Zero shader locals with DLSS and DLAA on too, which removes the speckled floor tiles there.
With DLAA on, function-local variables stayed undefined, and NVIDIA's driver filled the game's clustered-light reads
with other warp lanes' values: floors around Link lit in 4x8-pixel tiles. The setting was also part of every shader
cache key, so shaders prepared with DLSS off missed with it on. Zeroing is now on for every setting; DLSS still finds
the game's FXAA by its zeroed hash and keeps its camera motion. A log line now says when DLSS replaces the game's
scaling draw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 13:31:06 -05:00
chrg6
b48b05dacf Build kit: take each module from the .nup package that holds it whole, and keep the PC awake while it builds.
A .nup with the base game and its update has the update tried first; a module the update leaves out, or holds as
something other than an NSO, comes from the base. The build holds off sleep while it runs, since it runs for many
minutes without input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 10:59:59 -05:00
chrg6
1d7bc90848 Fix shadows, colour writes and present pacing on drivers that read Vulkan's undefined cases differently.
A depth-compare read through a sampler whose compare is off now gets the slot's comparing sampler, as the Switch
compares there and only NVIDIA's driver did so on its own. Colour components a fragment shader does not write are
masked out, as Maxwell leaves them alone. Without mailbox presentation the swapchain takes relaxed FIFO, or
immediate, so a late frame no longer halves the frame rate. NX2NAVI_VK_NOOPT=1 builds pipelines without the
driver's optimiser, for a driver whose compiler fails on one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 10:59:59 -05:00
chrg6
edce57ce5d Render DLSS's quality modes at their own scale by fixing the game's dynamic resolution.
The game sizes its scene each frame from a GPU-time controller. The new patch set res_fixed takes the controller's
fixed path and reads the scale from the patch page, so DLSS Quality, Balanced, Performance and Ultra Performance
render at 67, 58, 50 and 33 percent of the picture and DLAA at all of it. The set goes on once the controller has run
for a few frames, and the menu's Upscaling choice lists the modes and switches them live.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 10:59:59 -05:00
chrg6
1299b20c1c Note where Ocarina's scene size comes from, as far as it is traced.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 10:16:34 -05:00
chrg6
43a8096f37 Give DLSS Ocarina's camera, so it keeps its history along the camera's motion.
Ocarina's opaque scene draws bind a 0x3500-byte scene context at c3, and their vertex shaders take the world position
through its rows 0-2 (the view) and then rows 7-10 (the projection: an infinite-far projection whose z row is its w row
less twice the 0.1 near plane). The camera motion pass now uses that block's view-projection, from the block most of
the frame's light-target draws bind, and DLSS keeps its history instead of starting anew every frame. Measured with
NX2NAVI_MV_CHECK while turning: 176 to 227 px of camera motion a frame, a warp error of 0.006 to 0.015 with the vectors
against about 0.025 without.

There is no jitter on Ocarina's frame yet: with it the picture went white within seconds, with or without motion
vectors, while the same frames without it look right.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 10:16:34 -05:00
chrg6
56a46cadba Draw the picture above 1440p, and fold scaling, FSR 1 and DLSS into one Upscaling choice.
NX2NAVI_GAME_RES (1440, 1800, 2160, 2880 or window, read at start) sets the picture's height; the widescreen patch
sets now also run at 16:9 when the height is not the game's, with the width following the shape and the scene 3/4 of
both as the game draws it. Measured at 2160p with 21:9: a 5120x2160 picture and a 3840x1620 scene at 60 fps.

The Screen tab's Scaling-to-the-window row and the DLSS rows become one Upscaling row (Bilinear, AMD FSR 1, NVIDIA
DLSS) over NX2NAVI_UPSCALER and NX2NAVI_DLAA (option_rows: opt_choice_index and opt_apply for a row over several
settings), with the FSR strength greyed out unless FSR is chosen and a Resolution row beside the widescreen one. DLSS
always uses NVIDIA's model for its scale (no model row). There are no DLSS quality modes: the game sizes its scene
from the picture by its own 3/4 share, and writing a smaller scene into wide_scene's constant left the light target
as it was, so every mode would render the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 09:26:51 -05:00
chrg6
a4fde2090a Put DLSS back in the settings menu, and build the kit's runtime at -O2 without strict aliasing.
The Screen tab gets the DLSS switch and its model choice after the picture rows. There is no quality-mode row: this
build cannot change the game's scene size, so every mode would render the same. The description says that DLSS works
on each frame on its own for now and takes effect after a restart; the model row is greyed out while DLSS is off.

tools/make_game.py compiles with tools/build_game.py's aliasing rule: -fno-strict-aliasing everywhere, since the lifted
code reaches guest memory through casts of one address to several types, and the runtime at -O2 (the lifted code
stays at -O1). x86-64-v3 is left out so that the kit runs on CPUs without AVX2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 09:26:51 -05:00