The shader memory cache held 777 MiB of SPIR-V for 7115 shaders for the whole session. A background thread now
compresses the SPIR-V of each shader no reader pinned for shader_compress_idle_s seconds (zstd level 1, 26% of the
bytes on the test install's 44,151 modules) and frees the plain copy; readers pin it through gfx_shader_spirv_pin and
get it decoded back. What the drawing thread compares at pipeline misses and draws, the module's FNV-1a and its Binding
decorations, is kept with the shader, so module_binding_missing, the water corrections and the DLSS, crumbs and skip
lists decode nothing. A compute pipeline created at a dispatch and a mesh shader's first pipeline key still decode on
that thread, and the memory report counts those decodes apart, with the SPIR-V as held against uncompressed and the
shaders held compressed. shader_compress = 0 keeps it plain.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
F8 now always writes a folder under logs/captures: the area, Link's position and rotation, the camera of the frame on screen and the game's own, the presented frame as a PNG, frame times around the press, the machine, the settings that differ from the defaults, the end of the log, the save, and the game's flags and texts. A note on screen names the capture. The flight recorder's F8 dump goes into the capture's folder.
NX2NAVI_CAPTURE_REPLAY starts a run on a capture's settings, teleports to its area, walks Link to where he stood and puts the free camera where the camera was; tools/capture_run.py prepares the profile from the capture's save, runs it and compares the two captures. docs/bug-capture.md has the files and the steps.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Conflicts: the settings key lists take both sides; frame generation keeps the camera-cut handling and logs its counters every 600 frames with the cuts among them; the pass-time mark moves into draw_viewport; mo_desc keeps the pose depth and notes the reader.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
A draw can be left out before it opens a render pass, and the pass list of a frame capture then showed nothing of it.
passes.txt now ends with the rejected draws of the captured frame by mesh or vertex draw and reason, each with the
first program it met, and says so when there were none. A draw left out by NX2NAVI_SKIP_PROG or NX2NAVI_SKIP_SHADERS
is counted under a reason of its own. The frame to capture and its files are set together under the recorder's lock.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
F10 is the performance overlay's key, so a press dumped a frame and changed the overlay. dump_key = 1 or pause now
means the Pause key; scrolllock, insert, home, end, pageup, pagedown, f2-f7, f9 and f12 can be chosen instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A frame dump needed its frame number in advance (NX2NAVI_DUMP) or a depth target address (NX2NAVI_DUMP_AFTER); with
dump_key = 1 each press of F10 in the game window dumps the next frame to <dump dir>/f<frame>, as often as wanted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This pull request fixed lighting that added up frame after frame under shadow storage on Linux: a GPU write
noted while a part was recorded kept that part's CPU inputs (the game's resets of its lighting accumulators)
out of the shadow. Its fix marked noted ranges as submitted in vk_memory.c; the base copies that came with
the black-world fix already move every CPU-changed byte in before the part runs, so that part is not taken
and no SHADOW_PENDING_INPUTS setting is added. Taken from it:
- tex_probe.c (--probe-vkmem): a range noted as a future GPU write still gets the CPU's bytes before the
GPU reads it. The pull request's other check, that a CPU write made after a GPU result loses to it until
the write-back, is left out: with base copies the later CPU write wins, as it does on the console.
- vk_dump.c: NX2NAVI_DUMP_SHADERS=<hash>,... copies images only for passes that use those shaders, and
NX2NAVI_DUMP_RAW=1 also saves each copied image's texel bytes.
- tools/fix_linux_ambient_power.py, as it is: patches one translated shader of a player's own cache so a
zero exponent no longer gives NaN. It holds two hashes and no shader data.
Pull request #264 by intellq. The Windows files compile; the probe was not run, and nothing was run on
Linux here.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
NX2NAVI_DUMP_FILE=<path>: every 8th present looks for that file; when it is there, the present deletes it and dumps the
next frame to dump/f<present>, or that many frames in a row when the file holds a number up to 8, so a script takes
dumps when it likes. Dumps list up to 1024 pipeline and texture sets per pass (was 48) and copy every layer of a
sampled 2D array texture. From the local fixes package (fix 8); its mesh draw and movie dump parts are in main
already, in the form main has.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Performance: draws reuse the bindings that did not change and record only the dynamic states that differ from what
the command buffer holds; small uploads come from a staging arena; finished submissions are held briefly and handed to
the queue in batches; the recorder thread can keep a CPU core of its own; ASTC block-linear textures are deswizzled on
the GPU; dynamic resolution steps the upscaler mode to hold a frame-time target. Each has a setting in nx2navi.ini
(bind_reuse, dyn_delta, stg_arena, submit_batch, recorder_core, recorder_prio, astc_gpu_deswizzle, dyn_res) and the
staging cache now frees what it holds at shutdown.
Capture: NX2NAVI_DUMP=key saves the frame after each press of Insert, with mesh draws listed by task, mesh and fragment
shader, depth test and face culling, and mesh_prims.txt giving each mesh draw's primitive, clipping and fragment counts.
All of it is off unless NX2NAVI_DUMP is set.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The foliage's sun shadows took most of the GPU time: one mesh draw shaded about 1,000 million fragments in the far cascade. The far cascade now shades them at a 2x2 rate, and the two far cascades drop one side of the double-sided cards. At the forest entrance the frame rate went from 18 to about 43 fps on an RX 7900 XT.
FSR 3.1, its frame generation and the console mode now take effect without a restart. The full performance overlay lists the settings shown in the options menu.
NX2NAVI_GPU_PASSES times each pass and counts its fragments, and NX2NAVI_SET_AT changes settings at a given frame for test runs. Both are off by default.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NX2NAVI_TRACE=audio logs voices starting, stopping and sharing a wave buffer; NX2NAVI_DUMP_MOVIE dumps movie frames; NX2NAVI_LOG_CS_BINDS says why the stand-in for an untranslated compute program skipped a dispatch; and NX2NAVI_TRACE=perf adds heap and commit census lines on their own thread, with VMA maps when write-combined memory grows by 2 GiB. From a tester's fixes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
NVN2 compute banks get the dispatch's group counts in bytes 0-0xb, for direct and indirect dispatches; the fog pass
after the integration divided by them. Indirect mesh draws read NVN's {count, first} record as {x, y, z}, so y = 0
drew nothing: each now draws from a ring entry holding {count, 1, 1}, copied on the GPU before the draw. An empty set 1
no longer leaves set 0 pushed with 0 bytes. Compute stages with subgroup operations whose width is a whole number of
subgroups require full subgroups.
The texture pool refresh fell from 3.1-3.7 ms to 1.0 ms a frame in Kokiri Forest: slots with the same descriptor,
address generation and entry generation share one lookup (about 14,500 of 15,000 refills a frame), and registering a
texture and view identical to what the slot holds keeps its resolution. Under 20 GiB of VRAM, ASTC textures are
encoded to BC3 by default (Kokiri: 15.9 to 8.2 GiB, same GPU time). The pool log counts refills by cause and what
invalidates them; frame dumps write BC textures as raw blocks and print a storage buffer's backing span; a profile with
NX2NAVI_PROFILE_FRAMES is written when its window ends; the alpha test logs once that it is not applied.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The translator dropped 36113 global accesses per Kokiri boot (loads read 0, stores and atomics deleted); 837 remain.
AmpereLowering computes a predicated LEA's carry sum unconditionally, so a base like c[0][0x110] no longer reaches the
address as a phi; the edge AA's tile list is written and its blend weights follow the edges. Patch 0022 extends
GlobalToStorage: a constant-buffer base inside a sum of terms, and buffers reached through a pointer the program loads
from another storage buffer at a constant offset (the pointer tables Ocarina's vertex, pixel, mesh and compute shaders
read, also through register spills and phis). Such a buffer is listed with its outer buffer and offset, and the runtime
binds it at the address read from guest memory when the stage is bound (vk_exec_draw.c ptr_addr). The fog injection's
64x36x96 volumes now hold in-scattering.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The texture pool descriptors are now a ring of sets, one per submission, brought up to date from a change log, and the first submission of each frame waits for its GPU work before the game goes on: the GPU ran late and part of a frame read data of the next one, and distant characters drew NaN that bloom spread over the frame. The window acquire also waits for the image's previous frame. Function locals start at zero unless DLAA is on, which removes the per-warp light tiles on the Great Fairy Fountain floor and Link's dark, speckled look. Texture slots follow only live entries of their own target, so the volumetric fog gets its 3D volume. The pool refresh walks only the slots that changed.
The translator implements PRMT (patch 0021), which it only logged before; the button prompts render again. HFMA2.RELU no longer depends on it, HMMA.F16 rounds once and HMMA.F32 is lowered, checked against GPU vectors with a new Maxwell warp emulator.
The frame dump gains a re-arming white-frame trigger, a trigger on a depth target's first pass, NaN pixels in magenta, image handles and view targets, and depth copies of sampled depth textures.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>