13 Commits

Author SHA1 Message Date
chrg6
58fad9128d Give a synchronous submission of a recording copy that another thread's submission still replays a clone of its own for its deferred binds, instead of rewriting the copy under that replay.
nvnQueueSubmitCommands resolves a copy's deferred binds at the submit call: the handles the game's arrays hold now are
written into the recording copy (nvn_api_cmd.c resolve_deferred). With the replay on the submitting thread, a second
guest thread that submitted the same copy while the first one replayed it, or while the first copied it for a held
continuation (vk_exec.c cont_new), rewrote those handles under that replay: the rest of it bound the next frame's
textures and samplers, and a held continuation could be copied torn (F-2-03's part left after the private copies).

Now each copy counts the synchronous submissions between their prepare and the end of their replay (copy_prefix.busy,
which shares the 8 bytes pins had, so the prefix keeps its 56 bytes). nvn_submit_prepare takes copy_lock shared once
for all handles: the first submission in flight resolves the copy in place, as before; one that finds another in
flight, unless the copy is known to hold no deferred binds, resolves a clone made under that lock, with its texture
pins counted, and nvn_submit_done frees it after the replay, as the asynchronous path does for a copy a queued
submission still reads. A copy the game releases while a submission of it is in flight becomes a zombie and is freed at
the next sweep after it ends (release_copies, zombie_sweep). The bind loop moved unchanged into
runtime/nvn_submit_guard.h (nvn_resolve_binds) so a check can build it; it reports a corrupt record or a call past 16
deep to resolve_deferred, which ends the run with the messages it had, and keeps the first six changes' trace line.
Being code the front end carries under its MIT header, the new header keeps that line and THIRD_PARTY_NOTICES.md's row
names it. A clone is logged the first four times and at each power of two ("submits the recording ... while another
thread's submission of it is replayed"). No timing change and no wait: the clone path runs only when two threads have
one copy in flight at once.

Not reproduced before the change; nobody knows yet whether the game ever submits one copy from two threads at once (the
new line will say). Checks that passed: the new tests/submit_clone_check (four threads submitting two copies 200,000
times each while their arrays change: no replay saw its handles change, both paths ran, the counts ended at 0; the
unguarded 0.10.1 shape through the same harness: about 450,000 of 800,000 replays saw them change; nvn_resolve_binds
step by step), copy_sweep_check (its stand-in prefix follows the new layout), pass_cc_check on every runtime file, no new
-Wall -Wextra warnings in nvn_api_cmd.c and nvn_api_core.c.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 20:13:02 -05:00
chrg6
cacb1318fc Count the NVN recording copies no submission used for 5 minutes, without walking them.
A recording copy is retired only when the game adds its control memory again, and in the 2026-11-13 session the live copies grew from 9,138 (0.7 MiB) to 223,753 (58.3 MiB) in 7 minutes of play, about 38,000 more at each return to an area: the one real host-memory leak of that session. Each copy now carries the minute of its last use (made, called or submitted), and counters by minute give how many copies no submission used for 5 minutes, and their bytes, in the 600-frame sizes line, with no walk of the copies under the lock the recording threads take. Measurement only: retiring such copies needs this figure first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 15:57:52 -05:00
chrg6
b5c9a9b38e Answer NVN getters asked of no object with 0, and map a buffer with no pool to memory of its own.
The game's scene-loading thread makes a few NVN calls with a null object while an area loads or Kakariko's time of day
changes: a memory pool's flags (and the other pool getters), a texture builder's storage size or alignment, and the
mapping of a buffer that has no pool, into which it then writes 0x3d0 bytes. Each read the object at address 0 plus a
field's offset, an access violation. The pool getters, nvnMemoryPoolInvalidateMappedRange and the two builder queries
now answer 0 for a null object (nvn_no_object), and nvnBufferMap of a buffer with no pool returns a block of scratch
memory (64 KiB, or the buffer's size when it records a larger one) instead of 0. The first such calls are logged with
their caller. These fit the crash reports in Castle Town and in Kakariko before Death Mountain (#605, #602), which have
not been confirmed to be this.

Checked: --probe-nvn gains three checks (pool getters of no pool, builder queries of no builder, a 0x3d0-byte write
through the mapping of a buffer with no pool) and they pass; its four other failures are the same on the baseline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 19:38:17 -05:00
chrg6
d2afdfa0f4 Under the perf trace, name the guest threads that submit and present on each queue, and log a submit or present that starts while another runs.
The synchronous replay's front end takes no lock of its own and relies on one such call at a time; a run now shows whether that holds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 22:35:29 -05:00
chrg6
0e16e58cf3 Merge the shader stutter fixes into the combined test line.
Eight commits: a draw that misses its pipeline has the rest of its submission read ahead, the library workers link
the pipelines of skipped draws, a pipeline whose fragment shader reads a target of its own pass is fast-linked like
any other, and diagnostics. The merge was clean. Their before and after matrix had not finished when this was merged.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 11:05:17 -05:00
chrg6
07721c0058 Test an event wait against the value the event had when the submission took its copies, so a frame is no longer drawn from data the game had not finished writing.
Each frame the game sets one of two events to 0 from the CPU, submits its worker queues and the main queue, whose first command waits for that event to be 1, and signals 1 with nvnEventSignal once it has written what the frame's commands read. The recording of a submission takes the VRAM mirror sync and the uniform snapshot at its start and tested the wait a moment later, so a signal that arrived in between passed the wait and the frame was drawn from copies taken before the signal: single frames with the whole scene's shadowing missing (6 to 7 of 255 brighter, the sky and the interface unchanged) or a black scene, more of them the busier the CPU.

Every nvnEventSignal is now numbered and keeps the value it replaced, the recording notes the number before it takes its copies, and a signal with a later number does not count for that submission. The submission stops at the wait like any wait reached before its signal and goes on at once in a submission of its own, whose copies are taken after the signal. NX2NAVI_EVENT_AT_SYNC=0 gives the earlier test. NX2NAVI_EVO_TRACE=1 logs the order in which every frame's submissions, waits and signals were replayed, which is how the frames were found.

Measured with the flight recorder's thumbnails, 1,800 presented frames a run, standing in Hyrule Field by day with the lazy write-back on, DLSS Performance, frame generation 4x and HDR: the build before this change showed 34 single-frame spikes with eight busy threads beside the game, 20 with four and 8 with six, most of them in frames whose signal came during the replay; with this change two runs with eight busy threads showed 0 and 0, with 667 and 724 such frames. A session of the user's with the lazy write-back off ran 25,200 frames without a spike in its three automatic dumps while the change stopped 4,744 wait tests.

Not measured: runs of this same build with the switch off (the two tried failed for other reasons, a device loss during the boot beside another game and a load with cold caches), any run with the lazy write-back off under load, the frame rate and GPU time against the switch off, and the differential tests, which this change does not touch. The flicker with the lazy write-back off that was reported has not been reproduced in a capture; its mechanism is the same signal and occurs there too.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 10:26:51 -05:00
chrg6
ca843507e9 A diagnostic says how long before its first draw the game made each program (NX2NAVI_PROG_LEAD=1).
Shaders are looked up and pipelines built when a draw first needs them. The time between nvnProgramSetShaders and a
program's first draw is the head start a build begun at the program's creation would have; the line gives it as
counts under 17 ms, 100 ms, 1 s, 5 s, 30 s and over, mesh programs apart (vk_exec_draw.c prog_lead,
nvn_api_obj.c nvn_prog_made_ns). Off unless asked for.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 10:03:54 -05:00
chrg6
22bbe674be Remove the read-back refresh of the VRAM mirror, which brought back the tree columns.
Copying the invalidated ranges from guest memory into the mirror at every submission put the guest's copy of those pages over GPU results that live only in the mirror. In adult Hyrule Field that wiped tree records, and the trees stretched into columns reaching the sky again. With NX2NAVI_READBACK_SYNC=0 the columns did not appear at the same spot. The memory check (NX2NAVI_MEMORY=verify) still finds mirror pages newer than guest memory there with or without the refresh, about 3 per 60 frames, which is left for later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 15:51:47 -05:00
chrg6
9a8f74fe53 Refresh read-back ranges in the VRAM mirror, guard the boot pipeline prebuild against driver crashes and count out-of-order event waits.
Small ranges the game invalidates and reads back (the exposure meter) are copied from guest memory into the mirror at every submission that uses their pool, so a CPU write made between recording and the GPU's run is not lost; NX2NAVI_READBACK_SYNC=0 turns it off.

While the boot builds the recorded pipelines a marker file exists next to the pipeline cache. A start that finds it left over records the GPU and driver in pipeline-prewarm-skip.txt, and starts on them build those pipelines at their first draw instead.

CommandBufferWaitEvent is now recorded as OP_WAIT_EVENT. The replay waits for nothing but counts the waits it reaches before their signal, and how many submissions later that signal comes, in the NX2NAVI_TRACE=gfx frame line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 15:51:47 -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
618b9ba4e5 Fix Ocarina's white dialogue frames, the fountain floor speckle and the button prompt text
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>
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