#1092's pointer image (runtime/cursor_art.c: a gold arrowhead with a dark edge, a turquoise gem and a soft shadow,
drawn at the window's DPI) was left out of dev because it changed the pointer for every player with no way to keep
the system arrow. It now comes with pointer_image (input section, live, default 0): 0 leaves the window class's arrow
as dev has it, 1 shows the image while the cursor is not captured. Win32 reads the setting at WM_SETCURSOR (the
next mouse move after a change); SDL sets the image or its default cursor when the settings change (input_sdl_idle).
The setting has its row under the mouse rows of the Controls tab, its text in the four menu languages, and
totk.ini.example and docs/controls.md describe it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
By default the left button is Y, which opens the dialogue log in a dialogue box. While MessageDialog_00 is open the
left button now presses B, which advances the text and shows the rest of a scrolling line, and the right one A.
Dialogue choices keep the menu pointer, where a left click picks the choice under it, so clicking through a
conversation does not pick the choice the focus starts on. The setting is live and independent of menu_cursor.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The game grows grass blades only inside its near grass map, a 62.5 m square around the camera drawn into 256x256 and
128x128 textures, so on open meadows the grass appears a short way in front of the player. grass_range (percent, 50
to 400, default 100) scales the map's extent, step and both textures together through a new grass patch set (1.2.1
and 1.4.3) that loads them from patch page +128..+144, and grows the HeapDef grass heap and the application pool for
the larger textures. At 100 the set stays off and nothing changes. A row in the Display menu, text in all four menu
languages, and totk.ini.example describe it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Enable VK_NV_low_latency2 and VK_KHR_present_id with present ID features
- Add VkSwapchainLatencyCreateInfoNV to swapchain creation
- Link present and submission IDs with standard Vulkan latency types
- Insert latency markers: simulation start/end, input sample, render submit, and present
- Gate all extensions and latency chains on reflex != off (RESTART setting)
- Log graceful notice when Reflex is requested on unsupported hardware
- Add UI menu setting and translations for en, ru, pt-BR, zh-Hans
- Document reflex setting in totk.ini.example and docs/settings.md
tests/settings_doc_check.py failed on v0.4.0: CLIMB_FIX is in known[] and described in neither file. It is a player
setting with a menu toggle under [frame rate], so it goes next to highfps_fix; frame_rate.c turns it off without the
real-time step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With dynamic_res at 0 by default the fixres set held the scene at 100 % for every player, the reason #573 was turned
down. The default is now 1, so the set stays off unless a player turns dynamic resolution off in the menu or
totk.ini, and the Steam Deck profile no longer needs its own entry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The setup wrote romfs-patches-xbox and romfs-patches-xbox-label for 16:9 only, so prompts = xbox on a 21:9, 32:9,
16:10 or 3:2 screen (the Steam Deck's 1280x800 among them) kept the game resolution at 1920x1080 (cfg.c) or, with
output_res set to the screen's shape, fell back to the Switch prompts. #979 fixed this for prompts = pc; this extends
the same mechanism to both Xbox folders, the shared-files option of #378 by way of #979's --reuse.
- The setup builds romfs-patches-xbox-<w>x<h> and romfs-patches-xbox-label-<w>x<h> for each of ASPECT_SIZES, from
their 16:9 folder with --reuse, as it does for romfs-patches-pc-<w>x<h>.
- romfs.c overlay_init: at a game resolution of another shape any prompts' folder loads from <folder>-<w>x<h> in place
of romfs-patches-<w>x<h>, not only the PC one; without it the Switch prompts stay, and the log names the missing
folder and the command that makes it. romfs_shaped_pc_ready becomes romfs_shaped_prompts_ready(folder, w, h).
- cfg.c: the display-shaped output_res default applies with prompts = xbox when the folder of the pad layout
(romfs-patches-xbox with the Nintendo layout, romfs-patches-xbox-label with any other) is there for that size.
- The menu's note for a missing shaped folder reads "off at this game resolution (no layout made for it)" in every
menu language, since 16:9 is no longer the only shape with prompts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UHD Graphics (i3-1215U, i3-1305U) and Iris Xe drew coloured noise on foliage with dynamic alpha to coverage and black
patches over the scenery with dynamic vertex input; players confirmed that vk_dyn_a2c = 0 and vk_dyn_vertex = 0 clear
both. vk_caps.intel_igpu_win marks Intel's proprietary Windows driver on an integrated GPU, and both defaults follow it.
An explicit ini value still wins. Arc discrete GPUs and Mesa's ANV keep both on: no report there, and the ANV report
(#989) had both off with its glitch still present.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DLSS kept the replay on one thread: its decisions follow the passes in replay order, its jitter and camera are noted
draw by draw, and its evaluation reads the per-object motion list the scene's draws build. Now the scan takes DLSS's
decisions on its own state (vk_dlaa_plan, split out of vk_dlaa_pass) and notes the scene camera in replay order
(vk_dlaa_scan_draw), and the copy of the lit scene and the evaluation are records of the chunk that holds the op
(vk_dlaa_exec). A chunk's draws are jittered while they come before the op the scan ended the scene at. The
evaluation waits until the chunks before it are recorded, and the chunks after it wait for it (a hold), since they
sample its output. Chunks note their draws for the per-object motion one at a time (mo_lock in vk_exec_draw.c); the
uniform overrides of a motion draw and shader_for's last-frame variant flag are per thread, as the other chunks' draws
go on meanwhile; the FXAA shader table takes concurrent lookups. fxaa = 0 runs in chunks too.
With dlaa = 1 at 4K (DLSS Quality) on 1.2.1 and an RTX 5070, replay_workers = 4 takes the frame rate from 70 to 78
fps where the CPU is the limit, with no one-frame glitch in 241 consecutive frames.
(cherry picked from commit 04f8bb1b57)
replay_workers = N (0, the default, keeps the sequential replay) records a submission of enough draws in chunks that start between passes, on N threads and in twice as many chunks, and submits them in order. In the town at 4K on an RTX 5070, 4 threads take the replay from 8.2 to 4.5 ms a frame and the frame rate from 71 to 79.
A sequential scan on the submitting thread applies only what is queue state (fixed-function and bind ops, render targets, render enable and conditions, samples-passed counting) and keeps a copy of it where each chunk starts, with how far its lists got: inline uniform updates, RENDER_COND results, contact shadow marches and the ops that drop uniform copies. A chunk takes those from the lists instead of from the chunks before it, starts with every image in GENERAL and ends with a full barrier, so its recording depends only on the copy and the lists.
Each thread records with a context of its own: the replay's memos, descriptor state, occlusion query segments and per-command-buffer state moved from file statics into VkExec and VkExecDraw, and the tables the contexts share (stand-ins, depth twins, views, mirrors, timestamps, completion callbacks, crumbs) take locks or atomics. The scan makes render targets live in replay order and notes the op (vk_texture_revive), since a chunk can sample a storage before the chunk that renders it got there or after the scan went past a later target there: a chunk's texture slots resolve as of its own op, and the chunks after a texture with no entry yet wait for the chunk that makes it. Uniform copies are dropped at the op that writes them (vk_usnap_drop_at).
Contact shadows plan their march in replay order (vk_contact_plan) and the chunk that holds its op records it; the HDR source keeps the copy that comes last in replay order. DLSS and fxaa = 0 act on the frame draw by draw and keep the replay sequential. TOTK_REPLAY_STATE_CHECK=1 compares the state after the last chunk with the scan's.
A draw or dispatch whose shaders write storage buffers drops the uniform copies they cover from its own op on, as the other GPU writes do (scan_writes). The scan still translates no shader: it reads the shaders the recording contexts translated from a table shared with them (vk_exec_draw_writes), and an op whose shader no context translated yet holds the chunks after it until it is recorded. Before, the chunk that recorded such a write dropped the copies for every position at that moment, so a later chunk recorded first bound the copy taken at the submit call: on an RX 7800 XT on RADV the grass near the camera came out dark or unshaded in some runs with 4 threads.
TOTK_TRACE=perf counts the chunks held per submission by reason and logs when each chunk of the last submission began. The settings menu has the thread count under Advanced, Scheduling, with the count this run uses under it.
The recorder thread begins, ends and submits the submission's open part (vk_rec.c rec_end_cb), so a chunk's mirror prologue stays off it: vk_sync_part waits until the recorder is idle, as the part pool allows one thread at a time, then records, ends and submits the prologue itself. Through the recorder the prologue could reach the queue before it was ended, and the GPU lost the device at the title screen.
(cherry picked from commit b94e2e9642)
The setup wrote romfs-patches-pc for 16:9 only, so prompts = pc on a 21:9, 32:9, 16:10 or 3:2 screen kept the game
resolution at 1920x1080 (cfg.c) or, with output_res set to the screen's shape, fell back to the Switch prompts.
- make_romfs_patches.py --reuse <folder>: a file of an earlier overlay whose contents match is hard-linked (copied
where links fail) instead of compressed again; the output is byte for byte the same.
- The setup builds romfs-patches-pc-<w>x<h> for each of ASPECT_SIZES from romfs-patches-pc with --reuse: only the
Common archive is compressed, so the fifteen folders take about two minutes and 6 MiB each instead of 22 minutes
and 73 MiB each.
- cfg.c: the display-shaped output_res default applies with prompts = pc when romfs-patches-pc-<w>x<h> is there
(romfs_shaped_pc_ready); Xbox prompts keep 1920x1080 as before.
On 1.4.3 the per-frame wait is the second of the two nvnSyncWait calls in f_70002db8ac (return address 0x70002db99c). Like on 1.2.1, its sync comes from a two-entry ring indexed per frame; the first call waits on one object's sync behind a mode check. wdiasjunior found the site and measured it on an RX 7800 XT (#676): about 67-70 to 81-83 fps on Great Sky Island.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each frame the game waits in one nvnSyncWait (1.2.1 lr 0x7000b23dfc) for the frame it presented last. On the Switch
that work is long done by then; here it is still in the replay and on the GPU, and on Great Sky Island the wait is
about 4.6 ms of a 13 ms frame. With TOTK_SYNC_AHEAD=1 that one wait waits for the frame before it instead, once the
replay thread submitted the asked value, so the game builds the next frame while the GPU runs the last one.
The game then writes guest memory that GPU work still in flight reads. Shadow storage makes that safe: a shadow update
of a pool with a reader in flight is queued and copied by the GPU in the next part's prologue, in queue order, instead
of memcpy'd under that reader (TOTK_SHADOW_DEFER=1, the default with the switch; 0 makes the replay thread wait for
the reader instead). The guard is read under the shadow lock and the wait takes that lock once after raising it, so no
sync that read the old guard is still copying when the game writes again. The switch needs shadow storage (Linux and
Wine by default, TOTK_SHADOW=1 on native Windows) and the 1.2.1 call site; otherwise it stays off with a log line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The game picks a scene tier of 100, 90 or 80 % mostly from a fixed cost model made for the Switch that grows with the
scene width squared, so on a wide output it drops to 80 % on any GPU: at 5120x1440 every frame drew 80 % of the scene.
Patch set fixres replaces the one instruction that moves the tier's scale into the controller's result (1.2.1
main+0xcc2c00, 1.4.3 main+0x5848a8 as MacNativeGames patches it) with 1.0. The new setting dynamic_res (menu toggle,
live) brings the game's stepping back and is on by default in the Steam Deck profile.
The scene cap goes from 4096 to 8192 a side, so render_scale above 80 works on a 32:9 output, and the scene also stays
within a DynamicTextureAllocatorMem under 2 GiB; the log says when it was made smaller. The memory selftest now maps
past the real pool size instead of a fixed 4 GiB, which a larger scene grows past.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
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 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>
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>
The free-orbit camera turned 0.08 degrees per count while the other cameras (the bow, the abilities) took the mouse as
0.01 stick units a count, about 0.023 degrees for yaw and through the camera's signed-square pitch for y, so the bow
turned at half the speed and small vertical moves barely moved it. Every camera now turns 70/1500 = 0.0467 degrees
per count: the stick path gets 0.02 a count for x and the signed square root for y, so pitch is linear and as fast as
yaw. While the player uses an ability (the action verb UseSpecialPower, read from the player actor with the 1.4.3
placement), the ability camera's own linear scales are undone instead. stick_sens is divided out, Ultrahand's held
object gets the camera's stick value a count (at most three full tilts a step), and mouse_aim_scale (0.1 to 3)
scales the mouse while the bow is drawn.
The game's lock-on, recenter and aiming override released the mouse's pitch hold and restored the target from before
it, so after a shot the camera sprang back to that pitch, or to the game's default target. The mouse now lets go
without restoring, and when the override ends (or the camera went 100 ms without a step) it holds the pitch the
camera has then. A gamepad's vertical input still hands the pitch back to the game.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A tab switch or a popup opening drops the game's focus box for a frame or two. The menu pointer read that as the menu
closing: the mouse camera captured the cursor again and, when the next tab took the focus, the pointer came back in the
middle of the window. A focus gap of up to 300 ms now keeps the pointer where it is.
The map has no focus box, so it stayed gameplay and the mouse drove the right stick, which zooms the map. The open
screens in eui::ScreenMgr's list now find the map (the Purah Pad's frame with AppMap_00 and the menu background; the
Scope opens AppMap_00 without the background), and there the mouse is the left stick, which moves the map's cursor.
The list is read every 100 ms, as each screen costs memory queries.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TotK's fire spread reads its 260x260 RG8 grass maps after a fence wait with no invalidate. The fence write-back took targets up to 64 KiB only, so the CPU saw stale maps and fire died where it hit (#132).
A target over wb_fence_limit now goes back unless the GPU drew it in each of the last 16 frames. Raising the limit instead pulled in four 256x256 targets drawn every frame, about 2.7 ms a frame.
Every level and layer of every texture in a write-back now shares one upload batch and one wait. A burst of 9 grass maps took 7.8 ms with one round trip per level and takes 2 to 2.5 ms. The fence write-back on the field replay fell from about 45 to 22 ms per second of play.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
83d2302 turned it on from one measurement where the game waits for the GPU (a GTX 1650 in the Depths, #88).
Nobody checked frame pacing at the 60 fps cap with GPU headroom or on a 6-thread CPU. #659 (GTX 1660 Ti,
i5-9400F, GPU at 26 %) reports uneven motion on 0.3.7 at a steady 60 fps that 0.3.4 did not have, and this
is the only change in the frame path between the two releases that reaches that machine. Players it helped
set replay_async = 1.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Button prompts row gets a third choice, PC: every button glyph of the game's button font and every image of the
button guide is drawn as the key or mouse button the keyboard defaults press it with (E, Shift, Space, left click, Q,
R, right click, F, Tab, M, 1 to 4, W A S D, Ctrl, C) and the right stick as the mouse, whatever the pad layout. The
setup writes romfs-patches-pc next to the Xbox folders (make_romfs_patches.py --prompts pc, button_icons.py pc_icons);
--binds <totk.ini> draws the keys of its bind_ lines instead. At a game resolution of another shape the game takes
romfs-patches-pc-<w>x<h> (--prompts pc --aspect) in place of romfs-patches-<w>x<h> when it is there.
(cherry picked from commit 3f81cf02be664100bd9c569c53588d620d90b527)