Commit Graph

1068 Commits

Author SHA1 Message Date
epicmeme546
5ae55db41c Keep CPU helper threads on Automatic and Start next frame early on by default in 0.10.1, as in 0.10.0, and give 0.10.1's notes the video memory fix.
The release commit for 0.10.1 had turned both off by default (replay_workers = 0, tail_early = 0). They stay as 0.10.0 has them: replay_workers = auto, half the processor's physical cores less one (3 on 8 cores), and tail_early = 1. A player's session with all four Testing settings at 0 filled the video memory as before (bug capture 7: 15.5 GiB in use, 20.7 frames a second); what filled it were the images of moved render targets, which the two commits before this one fix.

runtime/cfg.c, runtime/ui/menu.cpp, runtime/vk_exec.c and nx2navi.ini.example are back as they were at 0.10.0. The notes of 0.10.1 lose the entry about the defaults and take the fix's entry from the draft for 0.10.2, which goes.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
v0.10.1
2026-11-13 11:57:11 -05:00
epicmeme546
1dbecd464f Hand an overwritten render target's image on after 8 presents while the card is past its pressure share, and free the ones no texture took after 32.
At 3840x2160 the targets moved in 16 presents hold 3.1 GiB, which a 16 GiB card does not have beside the 10 GiB of a town. A mark reaches its entry within three frames of the draw (four read-back copies, one every four submissions), so eight presents without a stamp are enough there. The early drop waits four times that: at ten presents it took the images the reuse was about to hand on (7,301 dropped, 7,672 made, 5,226 reused in one run); at 32 the same run made 3,338 and reused 8,004.

The walk from the bug capture at 3840x2160 with the budget taken as 13.2 GiB, a 16 GiB card's: 10.9 to 11.4 GiB in use, at most 1.6 GiB of released targets, no image that could not be made and none in system memory. A player's log from such a card at that size had 12.2 GiB of released targets and 22.2 GiB in use at 2 frames a second. Without the budget, at 3416x1440: 10.0 to 10.7 GiB, as before this change.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-13 11:49:48 -05:00
epicmeme546
217fb32f6b Let a render target that was drawn over give its image to the next target of its size, so video memory no longer fills up while the camera moves.
The game lays its render targets out anew in every frame with a linear allocator. A shadow map among them changes its size with the view (1141x1141 to 1917x1917 in Kokiri Forest, a texel or two a frame), and every target behind it then lies at a new address in each frame. The entries at the old addresses are marked overwritten and released, and their images are meant to go to the next textures of their parameters (create_tex). They did not: the game finalizes a moved target's texture object only when it needs the object again, until then the object's slot holds the entry, the pool stamps every GPU-owned entry a slot holds as used in each submission (pool_touch), and the slot names it (slot_names). By those two rules the image stayed, each frame made about a dozen new images, and the released ones waited 120 presents or more: 6.7 GiB at 3416x1440 and 12.2 GiB at 3840x2160 on top of the area's 10 GiB.

An overwritten entry is now judged by what drew with it or sampled it (samp_frame, sampled_lately) while the pool's marks tell what the shaders sample, in the image reuse and in drop_overwritten. A marked slot whose own entry is released stamps that entry too. The reuse looks at up to 1024 released entries of a parameter set instead of 128. Past the VRAM pressure's share of the budget by the driver's own figure, which counts the holes in VMA's blocks, overwritten entries nothing sampled go after 18 presents instead of 600.

Measured on an RX 7900 XT from a player's bug capture in Kokiri Forest (walking 28 m from the save, then laps, the game's own camera): 15.9 GiB of device memory in use before and 12.3 to 14.3 GiB on the laps, 6.9 GiB of it released targets, 6,970 images made and 645 reused; 9.7 to 10.6 GiB after, at most 2.2 GiB released, 3,609 made and 8,821 reused, no image that could not be made. With the budget taken as 12.5 GiB the early drop took 3,740 entries and the use stayed under 10.6 GiB.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-13 11:49:41 -05:00
chrg6
a84a3ab12d Make the release 0.10.1: every setting under the Game tab's Testing heading is off by default again, CPU helper threads (replay_workers = 0) and Start next frame early (tail_early = 0) among them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 10:49:01 -05:00
chrg6
5190f8e932 Make the release 0.10.0, with its notes; the notes of the unreleased 0.9.3 are folded into them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.10.0
2026-11-13 09:53:26 -05:00
chrg6
26d26babe5 Bring public main's bug capture on F8 and its removal of the Steam co-op into the 0.10.0 line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	docs/architecture.md
#	launcher/steamlib.py
#	launcher/ui/app.py
#	runtime/boot.c
#	runtime/cfg.c
#	runtime/flight.c
#	runtime/flight.h
#	runtime/gfx_present.c
#	tests/launcher_test.py
2026-11-13 09:52:43 -05:00
chrg6
8a99af1080 Give back the graphics pipelines no draw has used in this run while video memory is short: over tex_vram_pressure percent of the budget, the autosave thread takes up to 256 of them out of the table every 5 seconds and destroys them once the GPU is past the submission being recorded; a draw that needs one later builds it again from the pipeline cache. Mesh pipelines, entries building or linking and compute pipelines stay, and the next pass logs the video memory in use before and after. pipe_release = 0 keeps them all.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 09:19:50 -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
chrg6
e4df494048 Turn the early tail on by default (tail_early = 1): the game starts its next frame while the graphics card runs the last one's upscaler and interface, and a frame whose end hands the game anything else runs as before; tail_early = 0 turns it off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 08:58:03 -05:00
chrg6
482f7fdfb5 Stop logging a replay worker's deferred render target lookup as a target left out, and say why a target that is left out could not be rendered.
A chunk's lookup that needs its turn returns no image and its op is replayed in turn, but the first one was logged as "render target ... is not renderable here: left out", which read as depth and cube targets the replay could not draw. Nothing else changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 08:36:46 -05:00
chrg6
5faa1687f4 Add quad_index, which draws an indexed quad list as two triangles per quad and closes an indexed line loop, from the game's own indices, and log the first such draw either way.
Vulkan has no quad or line-loop topology. Draws without indices were rewritten already; an indexed quad list went to the GPU with its indices under the triangle list topology, three at a time, joining corners of neighbouring quads, and an indexed loop lacked its closing line. Off by default and read at start; no log of Ocarina has shown such a draw so far, and the new line says when one comes. tests/index_rewrite_check.py checks the index lists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 08:36:46 -05:00
chrg6
9d5f16bda4 Add oq_hold, which makes a samples-passed report whose draws the full occlusion query ring left uncounted repeat its record's last complete count, and log how often such reports happen.
A draw that finds every query slot still waiting for the completion thread goes uncounted, and the report after it said only what the other draws counted, at least 1: the game takes the object it tested for all but hidden for that frame. With the setting the report writes the larger of that and the last complete count reported into the same record. Off by default and read at start; the count of such reports is logged at the 1st, 10th, 100th and so on either way. tests/oq_hold_check.py checks the rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 08:36:46 -05:00
chrg6
cc5c8f87ad Add shadow_pcf, which makes every depth-compare sampler filter linearly so each shadow test blends the 2x2 shadow map texels around it, and log the filters of the compare samplers made.
Through a nearest filter each of the game's shadow compares tests one texel, so shadow edges are stair steps of shadow map texels that crawl over the ground as the sun cascades follow the camera. The setting is off by default and read at start; with shadow_scale above 1 the samplers were already linear.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 08:36:46 -05:00
chrg6
fadd4a6d5e Copy large textures out of the game's memory on several threads at once: a level of 1 MiB or more goes to a small worker pool as bands of rows (par_jobs.c, tex_deswizzle.c tex_bl_copy_mt), and so does a large block copy for the GPU's ASTC decoder, so the upload's storage copy no longer runs on the drawing thread alone. The bytes are those of the one-thread copy; par_jobs = 0 turns it off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 08:35:42 -05:00
epicmeme546
346f10019c Give the bug capture page the size and time of a capture at full window size.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-13 08:25:02 -05:00
chrg6
e7c2fc6a6b Make the stutter map name the areas without the perf trace, and count a hitch only when the frames after it are shorter: a hitch is now measured against the median of the last 32 frames, so a steady change of pace (the game drawing at 60 fps after 200) is no longer counted as a run of hitches.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 08:23:11 -05:00
epicmeme546
cd0e87dd8c Say in the bug capture page what a replay does when a conversation holds Link, and what was measured.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-13 08:20:11 -05:00
chrg6
d499a828b7 Compress idle shader SPIR-V after 5 seconds while the system memory is tight, and after 1 second while it is critical.
The wait stays shader_compress_idle_s (30) at the roomy and normal levels; every level compresses losslessly, and a
shorter wait only means a shader read again soon after is decoded once more.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 07:55:56 -05:00
chrg6
0c153c124e Add mem_budget: one place that says how much room the card and the system have.
A thread samples every two seconds the video memory budget against this process's use (vk_mem_vram) and the system's
commit headroom and free physical memory (plat_commit_info, plat_mem_info), and turns each side into a level: roomy,
normal, tight or critical, with five points of hysteresis upward and a 256 MiB floor. Changes are logged, and the
memory report's system line ends with both levels. MEM_BUDGET_TEST_VRAM_MB and MEM_BUDGET_TEST_RAM_MB pretend a side
is that small, so that what gives memory back can be tried on a large machine; the texture cache's rules are unchanged.
tests/mem_budget_check.py checks the levels and the hysteresis.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 07:55:56 -05:00
chrg6
3573a96f46 Keep the SPIR-V of shaders nobody read for 30 seconds compressed in memory.
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>
2026-11-13 07:55:56 -05:00
chrg6
71e52159f9 Add spv_store.h, which keeps a shader's SPIR-V zstd-compressed while nobody reads it, and its check.
Readers pin the store and get the plain words, decoded from the frame when the plain copy is gone; one packer makes the
frame of a store unused for a while, checks it by decoding it, and frees the plain copy. The lock is held for a few
loads and stores only, never across a decode, an encode or a free. tests/spv_store_check.py walks a store through its
states and runs four reader threads against a packer for three seconds, comparing every word they read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 07:55:56 -05:00
chrg6
97671e8ea7 Add the stutter map: each hitch of a play session with its area, what the frame waited for and the pipelines and shaders its draws met missing, and every 10 minutes and at exit the areas ranked by their hitches and the pipelines that cost the most (stutter_map = 1, off by default). tools/stutter_map.py turns a log into a report and counts what a game folder recorded that a pipeline list lacks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 07:31:57 -05:00
chrg6
cae09a2bcd Add floor_watch: hardware watchpoints on the floor the scan finds log which game code reads and writes it, at 60 fps and again at 120 fps or more.
floor_watch.c takes up to twelve of the floor scan's survivors (the platform's rigid body controller unit first, then one per object at the pivot's height), watches them four at a time through the debug registers on every thread (platform.h plat_watch_set, Windows only), counts each access's call chain in a lock-free table and logs the chains with the executable's symbol names after each pass. prof_syms gains a callback form for that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 07:08:49 -05:00
chrg6
c84724da2c Make a moving floor carry Link above 100 fps as it does at 60 (set ofollow, floor_carry = 1).
The character controller's three follow-surface modules kept only a tenth of the floor's velocity whenever the physics' inverse step passed 100, which every update above 100 fps did. The gate now compares with 100 over frames per update from the patch page above 60 fps, and with the game's 100 at 60 and 30 fps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 07:08:49 -05:00
chrg6
d9429b0c00 Break the memory report down past VMA, by owner in system memory, and by growth over a session.
Every report adds a line for video memory outside VMA (the driver's usage and budget per heap against VMA's blocks, and counts of pipelines by kind and by last use, stage libraries, compute pipelines, layouts and descriptor pools, with the swapchain, DLSS and FSR sizes), a line that splits the process's commit by owner (the game's heap and maps, device-local memory, VMA's host blocks, the C runtime heap with the shader caches, pipeline tables, texture cache, log queue and flight recorder in it, the driver's pipeline cache data and the rest), and a line naming what grew by more than 64 MiB since the report before and since play began. Every ten minutes of play and at exit a ranking line orders the categories by growth and marks those that only climbed; tests/mem_growth_check.py checks that bookkeeping.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 06:46:31 -05:00
chrg6
a74503cd86 Add physics_steps = 2: at least a 60 fps frame's solver steps in every update on 1.1.1.
Link is carried by the Deku Tree's floating platform fully at 60 fps, about a tenth at 160 and not at all at 240,
which follows the solver steps an update (4, 2, 1 with physics_steps = 1) rather than those a second. The new set
osolverfull raises the rounded-up count to the 60 Hz frame's 4 in every update; physics_steps = 2 switches it on and
osolver off at start, and the mode is logged. 60 and 30 fps keep the game's 4 and 8. It costs more physics time above
60 fps: 576 solver steps a second at 144 fps and 960 at 240, against 288 and 240 with 1, the default, which stays
until a run confirms the carry. frame_step_check runs the lifted words of all three counts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 06:26:39 -05:00
chrg6
b912d72b87 Remove the Video memory use row; its low behaviour is the only one now.
Released textures go once they have gone unused for the hold, and what a switched-off upscaler or SMAA held is freed, with no setting to keep them. A vram_use line left in a settings file is known and read by nothing. Memory use is to be decided by the card's and the system's budget instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 06:23:01 -05:00
epicmeme546
4fd70e398f Make F8 write a bug capture and add a test run that starts from one.
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>
2026-11-13 06:22:18 -05:00
chrg6
75ae5b0221 Read co-op's switch by its new name, coop_steam, in the launcher too.
Main renamed the switch so that an old "coop = 1" turns nothing on; the launcher's check for Steam's library still read the old name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 06:07:26 -05:00
chrg6
b399d8f15c Keep a late HD rumble report from opening a Joy-Con pair again after the stop.
hd_tick picks a report under the locks and writes it to the pair after letting them go. When input_rumble_hd_stop
wrote the neutral value and closed the pair in between, that write opened the two devices again and left the
Joy-Con vibrating after the exit. The stop now ends the pair (hd_pair_end), and hd_pair_write checks that under the
lock it holds for the write, so nothing is written after the stop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 06:06:27 -05:00
chrg6
d59488d308 Claim the anti-aliasing setting's frame with a compare-and-swap, so SMAA's idle images are freed once.
vk_aa_mode is asked at every compute dispatch, on whichever thread records it, and the replay workers record at once.
The frame was checked and claimed without atomics, so two threads could both free the idle work images. The first
call of a frame now claims it with a compare-and-swap, and only that call frees them and takes the setting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 06:06:27 -05:00
chrg6
721da64866 Put Steam's library file beside the game only with co-op on, and look for it off the window's thread.
Every Play ran steamlib.ensure on the Tk thread, co-op on or off. Without the file that walks up to 60,000 folders
of the player's Steam libraries and checks signatures, and the window stood still meanwhile. Play and the Steam
shortcut's start now call it only when co-op is on in the game's settings (steamlib.wanted, read as the game reads
them), and Play runs it on a thread of its own and starts the game from the window's thread when it is done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 06:06:27 -05:00
chrg6
0e76baad36 Limit replay_small to 0..63, where it can match a group.
A group too small to share out has fewer than 64 draws (2 x CHUNK_DRAWS), so a threshold of 64 or more never matched
in group_start and the setting did nothing. The setting's range is now 0..63, the menu's slider ends at 60 in steps of
4, and a value of 64 or more from nx2navi.ini reads as 63 with one line in the log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 06:06:27 -05:00
chrg6
e7dead8663 Take the low-latency lock around AMD's anti-lag calls, so the switch-off is the driver's last word.
The stage calls ran on guest threads and the present thread without the lock the NVIDIA path holds, and a stage call
with the mode on could come after the switch-off on another thread and leave the driver on. The stage calls now hold
the lock shared and make none once the path is off; the switch-off holds it exclusively and waits for a stage call in
flight. Shared, because the input stage waits inside the call while the present thread makes the present stage of the
frame before, which must not wait for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 06:06:27 -05:00
chrg6
c656532cd3 Warn in the log when replay workers are set by number on AMD's driver for RX 6000 and older.
The automatic count gives that driver no workers, since a game with workers lost its device there, but a number in
nx2navi.ini or the menu starts them without a word. The number still applies; the log now says that workers on this
driver have caused device loss and that auto or 0 avoids it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 06:06:27 -05:00
chrg6
5c5b1914d5 Keep the sizes frame generation still uses when the upscaler's idle sizes are freed.
With frame generation and no upscaler, idle_release freed every DLSS size other than the last one at each present
from 600 presents after the switch, without waiting for the GPU. The scene's size can change from frame to frame, so a
size the frame before used could still be in flight. A size now goes only after it went unused for as many
evaluations; its DLSS feature, unused since the upscaler went off, is released as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 06:06:27 -05:00
chrg6
e682934bd0 Write the log file before stderr, and keep a blocked console from stopping the log and the exit.
A selection in the console window of a game started directly held back the write to stderr, and with it the log
file, the exit hook and the crash report. The log file now comes first, stderr goes past the C runtime's stream lock,
and the lines of an exit, a fatal error or a crash skip stderr while another thread is stuck in it. A game run turns
the console's QuickEdit mode off, and a closed console, Ctrl+C or Ctrl+Break ends the game the way a closed window
does, with the settings and saves written and the exit logged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 06:06:27 -05:00
chrg6
175033d49a Merge main's co-op switch name into the 0.10.0 line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	runtime/cfg.c
2026-11-13 05:58:46 -05:00
chrg6
97f1caa9b4 Of one GPU listed by two installed drivers, take the newer driver's listing.
On a laptop with the vendor's processor graphics and a card of the same vendor, both installed drivers list the
card, and pick_device took the first listing of the best device type. That listing can come from the older driver
installed for the processor graphics, which was reported to crash in its shader compiler with the card. Among
listings of equal rank with the same vendor and device ID, the one with the higher driver version is taken now, and
the log says so. A GPU chosen by index or name is unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 05:54:58 -05:00
chrg6
328554e7c6 Run the sound manager's job at 60 Hz with whole steps above 60 fps.
The job (main+0x415300) hands the time manager's step to the sound system in its phase 0 and runs the sound objects'
and the system's updates in phases 1 and 2. With the set ostep that step is a fraction of a frame above 60 fps, and fed
fractions the game has been reported to keep sounds held after an area unloaded until no voice was free, new sounds
and then the music going silent until a restart. The new set osound (sound_steps, on by default) calls the page's
+0xf10 after the job's prologue: phase 0 adds the step to what the job is owed and runs once a whole step has built
up, phases 1 and 2 follow that choice, and a job that does not run gets phase 3, which returns through its own
epilogue. After phase 0's load of the step the page's +0xf00 makes a fraction one whole step. At 60 fps and below
the job runs every frame with the game's step. Both sites were checked against the module; frame_step_check covers
the gate at 60, 30, 144 and 240 fps. Needs the game code generated again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 05:54:58 -05:00
chrg6
f512a98988 Give the water and floor surfaces' fixed-step remainders as many slots as the waters' ripples.
Every interactive floor of a place and both water managers ask frame_rate_sim_host once an update, each for its own
object. With 8 slots, a place with more such objects than that had them take each other's slot in turn, start
without a remainder every update and stand still above 60 fps, as before the set owater. The table now has 64 slots,
as the ripples' has, and frame_step_check runs 32 surfaces at 144 fps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 05:54:58 -05:00
epicmeme546
a01910d905 Give the new co-op's switch a name of its own, so an old "coop = 1" turns nothing on.
The Steam co-op took the setting name of the old beta's switch, coop. A player whose nx2navi.ini still had coop = 1 from that beta got the new co-op switched on at the first start of the new build, unasked: the game signed in to Steam as its test app. The switch is now coop_steam (NX2NAVI_COOP_STEAM), off by default, and coop is a retired key like the old co-op's other settings: known, read by nothing.

--probe-cfg now loads a file of the old co-op and checks that its keys are no unknown keys, that coop = 1 leaves the new co-op off, and that coop_steam = 1 turns it on; it passes on a build of this commit. The game itself was not started with the change.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-13 05:30:56 -05:00
chrg6
2f7ddc884b Let the floor scan dump the owner of the platform's position and the objects that refer to it.
Scan run 2 showed the platform moving at full speed above 60 fps while Link stays behind, so the fault is in what
carries him. Once the scan has its survivors, it logs the 0x500 bytes around the one deepest below Link, names the words
there that point into main's data (vtables), and walks the heap once more for the words that point at the starts of
the nearest objects, each logged with its own object's vtable, so one run names the classes to trace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 05:10:43 -05:00
Tobim6
1eadef609e Put textures and allocations past a full video memory heap only in another device-local heap, never in system memory.
On an RX 580 the second device-local heap (2 GiB) takes what the first has no room for. On a GPU with one device-local
heap (an RTX 3050 Laptop, 4 GiB, 16 GiB of RAM) the budget wrapper and the texture placement sent everything past the
heap's reserve to system memory instead: 4096 and more texture images and every other allocation, held in the
process's commit (21 GiB). The system's commit ran out (31730 of 32023 MB), host memory imports failed, a pool was
left without a GPU buffer and the GPU wrote through its address: device lost. Now an allocation leaves out a full heap
only when another device-local heap has room, and a texture's second step allocates as before, past the budget, where
the driver keeps it in video memory or moves it out itself, and NX2navi's own VRAM pressure drops textures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 05:09:47 -05:00
chrg6
a7e79b3460 Bring the line's floor scan into the co-op branch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 04:54:51 -05:00
chrg6
27dae195ab Arm the floor scan only on a sideways drift and keep what lies below Link, logged once a second.
Scan run 1 armed on a floor that only sinks and found points 3.4 m above Link that follow him (the camera's). The scan
now arms on a straight horizontal drift at a steady height, snapshots from 4 m below Link to 0.5 m above, keeps the
triplets that moved with him and lie more than 0.3 m below him or more than 0.5 m from his feet, and logs each of them
(64 at most) once a second with its move over that second.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 04:45:52 -05:00
chrg6
da65aa6099 Bring public main's Steam co-op, the removal of the old co-op and the launcher's Steam library file into the 0.10.0 line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 04:37:01 -05:00
chrg6
2970b0c7d6 Add a floor scan that finds the floor carrying Link in guest memory on 1.1.1.
The Deku Tree's floating platform is not one of the engine's ground groups, and where its position lives is not known.
With floor_scan set (off by default), once Link has been carried at 60 fps for 30 frames with the left stick at rest,
one snapshot of the game's heap and alias regions keeps every float triplet near him; 30 frames later the triplets
that moved as far as he did are kept, and from then on each frame logs the twelve of them farthest from him with their
addresses, positions and moves. The snapshot logs how long it took.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 04:36:29 -05:00
chrg6
652ed4dcce Let the floor probe list moving ground groups first, within 12 m of Link.
Run 5 named only the four nearest groups, and the still map pieces nearer to Link may have crowded out the platform's
group, whose translation is its pivot rather than its surface. A line now names up to six groups within 12 m: those
that moved since the last present first, then the nearest still ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 04:30:20 -05:00
chrg6
cdb3c59304 Let the floor probe give the time manager's step and first two time categories on each line.
The game derives every time category from the patched frames per update (main+0x3b4e300 and main+0x2c84c make the
inverse, seconds and rate from it), so the lines can show whether what a floor's motion reads matches the step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 04:28:59 -05:00