The game keeps about ten vertex streams bound while a draw reads a few of them, and bind_vertex_buffers resolved and
compared all 17 slots on every draw. vk_pipeline_set_dynamic now returns the bindings of the state's vertex input,
from the same vertex_input that builds the dynamic input or the pipeline's, and the other slots get the zero buffer.
A stream is taken from the previous bind only when it was resolved there. In the fort, bind_vertex_buffers drops from
9.0% to 7.2% of the replay thread's work (two profiles, recorder on). TOTK_VB_USED=0 binds every stream as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
df9ba65 keeps a content mod's heap sizes when its table is the one served, that
is when the mod is above the texture pack. With the pack on top (HDPacks.com's
4kTextures sorts first), the pack's table was still swapped for the game's own,
and The Second Quest's larger RSDB and UI heaps were lost: the game crashed at
start writing to address 0 in main+0x1d4200. heapdef_overlay now grows the
table of the highest switched-on mod that changes other heaps only, and falls
back to the game's own table when no mod has one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An IPS, IPS32 or pchtxt file for main that writes main's .rodata, .data or .bss was refused as a whole. Such writes
need no lifting: tools/exefs_patches.py now puts them in build-cache/exefs.data, and runtime/mods.c stores them into
the loaded main before any game code runs, as emulators and Atmosphere apply a patch file to the module before its
dynamic linker runs. A DT_RELR word gets main's base added; bytes over words an explicit-addend relocation fills change
nothing and are dropped; a write into the dynamic linker's tables, a lazily bound function pointer or outside main's
image keeps the file unsupported. A mod whose data lines do not all fit the build keeps its code patches off too.
The mods are applied from the bottom one up (mods_exefs_apply), so where two mods write the same bytes the higher one
wins, as it does for files. --probe-mods checks it with two mods writing one byte.
The lifted code reads some of main's bytes at lift time (the jump tables). recomp/gen.py lists those ranges in
<gen>/main/folded.txt, and the setup checks the data writes against them after the lift: a file that writes them
becomes unsupported, since the change would not reach the generated code. Generated C without mods is unchanged.
This is the data part of #790 (ericdallo's ExeFS work), split out on its own; the plugin part follows separately.
Co-Authored-By: itaskaev2 <165683571+itaskaev2@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The recorder spins on head while it waits, and tail, put_h, head and the producer's statistics shared one cache line,
so every packet moved that line between the two cores: put took 6% of the replay thread in the fort with the recorder
on. head, tail and sleeping now have a line each, the producer keeps its own state and the last tail it read, and pub
publishes head once 64 words wait (TOTK_REC_PUB; 0 publishes every packet). Every wait publishes everything first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
heapdef_overlay replaced the served mod table whenever any switched-on mod grew the texture heap. With an HD pack and
another mod that ships a HeapDef for other heaps on top of it, the other mod's sizes were lost. It now checks the
served table itself (resolution.c res_heapdef_tex_grown): only a table with TextureHeap over the shipped size is
swapped, any other stays the base and gets the growth on top.
A mod HeapDef the port cannot read no longer logs "TextureHeap 0 KiB": the failure is logged with the mod's name.
read_original sat between read_served and its comment; each function has its own comment again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three game threads flatten recordings, inline them and add control memory at once, and every large copy came
from a fresh VirtualAlloc, so each one paid zero-page faults, and release_copies freed them under the exclusive
copy lock. Copies are now freed after the lock through a per-thread dead list, blocks of 256 KiB to 16 MiB come
from a small best-fit cache, and each copy keeps its pin list so a free or clone no longer walks the records again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The block below the module window stays 6 GiB, as on dev, and grows by the texture heap extra (tex_heap_extra_mb or
a mod's HeapDef) rounded up to a GiB. A player without an HD pack reserves nothing more.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HD texture packs made for the emulators ship their own HeapDef with TextureHeap and its parent grown for the
emulators' 8 GB memory option (HDPacks.com's 4K pack: by 3,419,000 KiB). Served as it is, the game asked for room the
application pool does not have and crashed at start (#673); without the file it looped the panic Blood Moon.
Before mem_init sizes the pool, resolution.c reads the HeapDef of every switched-on mod (mods.c mods_peek_romfs, which
lists the mods' RomFS roots without the rest of the scan: mods_scan needs paths main sets later) and counts the largest
TextureHeap growth over the shipped 506,880 KiB like TOTK_TEX_HEAP_EXTRA_MB (the larger of the two, at most 4 GiB). The
pool and the heap cap grow by it, and romfs.c heapdef_overlay serves the game's own table grown by that amount in the
mod's place, so the other heaps keep the sizes of the game version that runs.
Tested with the HDPacks.com 4K Texture Pack v1.0 as players install it, on 1.4.3 and 1.2.1 (RTX 4080 SUPER): the game
loads a save and runs at 60 fps with 0 validation errors, and holds 4.5 GB of game memory pools against 1.2 GB without
the pack. docs/mods.md, README.md and totk.ini.example say so; other packs come with no guarantee.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
heapdef_overlay returned at once when res_heap_extra_kib() was 0, which it is at 1920x1080 with the game's scene size
and shadow map, so tex_heap_extra_mb did nothing there and a 1080p player with an HD pack still hit the Blood Moon
loop. It now also builds the table when res_tex_heap_extra_bytes() is set. Found by itaskaev2 in review.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HD texture packs made for emulators with 8 GB of DRAM, such as the HDPacks 4K pack, grow "TextureHeap" in their own
HeapDef table. Served as a mod, that table replaces the one the port grows for the resolution and the shadow map, and
the game crashes at boot; without it the pack's textures do not fit, and the game loads and then starts its panic
Blood Moon over and over.
tex_heap_extra_mb ([mods], 0 to 4096 MiB, read at start) grows TextureHeap and its parent ゲームグラフィックス in the
port's HeapDef, and the application pool and the heap cap by the same amount. LOW_SIZE goes from 6 to 10 GiB so the
stack and heap block holds the largest heap. At 0 the pool, the heap cap and the HeapDef are what they were.
The selftest's "beyond the pool" MapPhysicalMemory asked for 4 GiB, which a grown pool holds; it now asks for just
past the pool.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A --seconds run (benches, menu shots, input scripts) reads the total so the Game tab shows it, but starts no counting
thread. The file is now read once at start instead of on the thread. A playtime.txt with a UTF-8 byte order mark reads
as valid, and a damaged one is kept whole as playtime.txt.bad instead of its first 63 bytes. --probe-playtime checks the
parser, a missing, valid and damaged file, the 30 s save and the 2 s cap of one tick.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A host thread adds each second in which the window has the focus and the game presents frames, keeps the total in
<profile>/playtime.txt (every 30 s and at exit) and the Game tab of the settings menu shows it with this run's share.
Texts in English, Russian, Brazilian Portuguese and Simplified Chinese. Closes#720.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The part command buffers and their pool were used by two threads. The submitting thread still waits for the
submission, so the queue order is unchanged. TOTK_REC_END=0 brings the old split back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Storage-buffer stores, copies and fills land in imported guest memory without a fault or a write-watch bit, so a
region only the GPU wrote looked idle and was frozen while the GPU kept writing it. LAS Recomp never changes the
protection of imported memory because of NVIDIA. A possible cause of the NVIDIA "write invalid" device faults
(#788, #802), not proven.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A game resolution that is not 16:9 widened the camera while the HUD stayed at 16:9 stretched across the picture whenever romfs-patches-<w>x<h> was not next to romfs-index.bin, which is always so for a build from source. resolution.c now falls back to the largest 16:9 size inside the asked one and keeps the camera aspect patch off, logging the command that makes the layout. build_game.py copies every romfs-patches* folder with a manifest.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Keep 1920x1080 as the default with prompts = xbox or pc: the setup builds those prompts' folders for 16:9 only, so the
shaped default would have swapped the chosen prompts for the Switch ones.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 72 mouse_direct sites of patches-v143.txt mapped to the 1.2.1 main by enclosing function, each original word
checked: the camera input update, free orbit, Ultrahand and the 16 player camera modes. 1.2.1 keeps some values in
other registers and stack slots (camera input x in s11, orbit angles at sp+0x3c/0x40, Ultrahand x in s9), so the
hooks read a per-version MouseLayout; the 1.4.3 values and behaviour are unchanged.
Where 1.2.1 adds a turn in a shared helper, the degrees go to the store of the helper's result (Aiming's pitch
target, new ROLE_BASE_STORE) or to the turn it is given (Horse yaw). Wakeboard, ShieldSurfing and Tail read the stick
twice (REPEAT on the first read), the dialogue cameras' angle loads are pre-indexed (mov -> add, as ClimbObj).
The player state (ability, bow in hand) is not known on 1.2.1 and reads as not found.
tests/mouse_direct_check.py runs the hooks with both layouts and checks each load-site table against its patch file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR #753 unified the cameras at 70/1500 = 0.0467 degrees a count and doubled Ultrahand's rate, which made every
existing mouse_sens turn the free camera 42% slower. The free camera keeps 0.08 degrees a count at mouse_sens 1, every
camera #753 unifies takes the same 0.08, and the held object keeps 0.01 stick units a count over the frame ratio (with
#753's cap of three full tilts a step). A mouse shake now needs 125 counts a swing at sensitivity 1 (10 degrees).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The stick loads returned the sample's pad stick even in a step without mouse motion, which drops anything the game
writes to the camera input's stick after the input update (main has 426 stores to +0x420/+0x424). The base loads added
0 degrees, which turns a -0 into +0. Both now return the loaded value untouched when the step has no mouse motion and no
mouse shake.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolved the conflict with #760 in runtime/input_sdl.c by keeping both the window enter/leave cases and the new wheel case.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The game loads each member of a pack (Pack/**/*.pack.zs) as a resource of its own, in a heap sized by that member's
own entry in the resource size table. The port raised the entries of the mods' loose files only, so a mod that changed
a file inside a pack and shipped no table for it left the game's entry. A crash when heavy snow started in Hebra was
that: an .ainb member of about 13 KB loaded into a heap sized for the game's smaller copy, 0x48 bytes stayed free after
the file, the frame heap gave null for the 0x68-byte object the resource builds next, and main+0x21794c stored that
object's vtable at address 0.
restbl_overlay now unpacks each pack a mod serves and the game's copy of it with the dictionaries of
Pack/ZsDic.pack.zs, and raises each member's entry as it does a loose file's: by the growth for a member the game's
copy has, by the estimate of its type for one it lacks. It never lowers an entry, a mod's own table included, and the
log names the first members it raised. --probe-mods checks restbl_pack_raise on archives of its own, and docs/mods.md
says that the files inside packs count too.
🤖 Generated with [ECA](https://eca.dev) (nubank-anthropic/opus-5.5 - max)
Co-Authored-By: eca-agent <git@eca.dev>
Under pressure only clean mirrored entries could go, so once those ran out the render targets stayed: 1.7-2.0 GiB of released images with the use still over the line. Most of them sit on storage the game has since given to another texture, and they are never taken back. The pressure path now drops those first, 30 s after their release and when no finalized slot has bound them for 600 submits. Clean entries wait 5 s, which ends the re-creation churn in fights, and both kinds stop once the use is back under the line.
TOTK_TRACE=perf gains mem: released lines that split released memory by kind, age, overlap and dedicated memory, and count take-backs and re-creations. TOTK_VRAM_LIMIT_MIB caps the budget the port sees, so the path can be driven on a large card.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ClimbObj pitch hook replaced the pre-indexed load ldr s1, [x22, #0x88]! and wrote the write-back to x22 from the
host call. The lifter keeps X19-X29 in locals across a call and does not reload them, so the write-back was lost and
the following str s0, [x22] wrote the pitch over the camera object's vtable pointer; the next virtual call crashed
(seen climbing a Stone Talus). The mov x22, x19 before the load is now patched to add x22, x19, #0x88, the hook
performs a plain ldr s1, [x22], and load hooks no longer support write-back loads at all.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
146 of the 254 names in known[] (runtime/cfg.c) were described nowhere for a reader: diagnostics, test hooks, paths and
experiments. docs/settings.md gives each one what it does, the effect of changing it and a safety level. The rows come
from reading the code, and most effects have not been checked in a run.
tests/settings_doc_check.py fails when a known name is in neither file, when the page lists an unknown name, or when a
default on the page differs from metas[] or an rt_knob call.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the map the mouse moves the cursor, so it had no way to zoom: the zoom is the right stick's y, which only the
keys and a pad reached. While the map is open, each wheel notch is now a full-tilt push of that stick for 100 ms with
50 ms of neutral after it (a turn away from the player zooms in). A fast scroll's notches wait their turn, at most four,
and a turn the other way drops them. The wheel stays unbound elsewhere.
The back ends count the notches while the cursor is captured (input_win.c from raw input's usButtonData, input_sdl.c
from SDL_EVENT_MOUSE_WHEEL), keeping a high-resolution wheel's part of a notch (ikb_wheel_take).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only the free camera took the mouse as degrees. The other camera modes took it as stick input, which their own speeds,
frame time, slow motion and curves reshaped, with the on-foot camera's square-root pitch shaping on top: bow aiming was
far too sensitive vertically on slow movements, abilities and the Purah Pad camera eased their pitch, lock-on turned
27% slower, and climbing, shield surfing and the rest each turned at their own rate.
Each camera mode's update now loads the gamepad's part of the stick where it computes its turn and adds the mouse's
degrees where it adds that turn to its angle (runtime/mouse_direct.c camera_load, the load sites of
recomp/patches-v143.txt): aiming, scope, abilities (and Recall's cursor), horse, Purah Pad camera, lock-on, bow
lock-on, wall and object climbing, the waterfall's turn, shield surfing, wakeboard and the dialogue cameras. Recall's
cursor moves so that its aim turns as many degrees a count. The waterfall's height and the horse's empty lock-on are
not angles and stay stick input, now linear. A step whose mouse motion no hook took is logged with the camera's name.
Holding L to soothe the horse gave the mouse to the ability wheel's virtual cursor, a held stick for the camera: the
mouse goes to the wheel and the D-pad strips only while their screen has the focus (menu_cursor.c
menu_cursor_shortcut). A mouse shake no longer plays a 0.8 s swing after it: the right stick follows the mouse's own
swings and lets go when it rests, and the camera does not turn by that swing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>