[Tracking] Low FPS with low GPU usage (CPU-bound), FPS drops in Castle Town / Hyrule Field, shader stutter #995

Open
opened 2026-11-09 05:38:42 +00:00 by NotRexed · 53 comments
NotRexed commented 2026-11-09 05:38:42 +00:00 (Migrated from github.com)

This issue collects every open report of low FPS while the GPU is not fully used, and shader stutter, so there is one place to follow it. The individual reports were closed pointing here; their logs, screenshots and discussion stay in each linked issue.

Summary

  • Low fps with the GPU not fully used: fps stays well below what the GPU should give.
  • Drops in Castle Town and Hyrule Field since the foliage came back (a known issue in the v2.0.6 notes).
  • Stalls the first time a new effect or area appears.
  • Long first-start shader preparation.

Linux/RADV-specific low fps is in the Linux tracker, and AMD handhelds have their own.

Affected GPUs and versions

  • GPUs: desktop GPUs from the GTX 1660 to the RTX 4080 (high-end too, so this is not about weak GPUs), RTX 3050 laptops, RX 7800 XT / 7900 XTX.
  • Versions: v2.0.2-v2.0.5.

Evidence from the logs

  • #384 (RTX 4080, 45 fps): CPU replay 15.2 ms + GPU 13.4 ms a frame. With RX_VK_GPU_PROFILE, mirror write-back copies take 7.08 ms, 52.8% of GPU time (68.5 MB a frame). Changing the resolution changes nothing.
  • #470: no RAM leak (10.5-13 GB working set). HOS_PIN to P-cores gives +1-2 fps on hybrid Intel CPUs.
  • #945: GTX 1660 gets 10-15 fps in Kokiri on v2.0.5 vs 22 fps on v2.0.3 (logs for both). #824 reports 5-10 fps less since v2.0.3.
  • #865: every first-seen pipeline costs a stall. #227 (Linux RTX 3060): first-visit compile stalls of up to 47 s.
  • #711: first-run preparation took 449.8 s (31,985 programs) on v2.0.2 vs 69.6 s on an earlier build; program cache 5.6 GB. #879 (860M) is in the AMD handheld tracker.
  • #766: first uploads of large textures cost about 63 ms of host-memory import each; the reporter proposes a staging copy.
  • #639: frame generation shows 60 fps but feels like 30.
  • #738: RAM at 100% on 16 GB systems.

Workarounds

  • HOS_PIN (pin to P-cores on hybrid Intel CPUs): +1-2 fps in #470.
  • RX_VK_WAIT_GPU=0 helped some players.
  • Lowering the resolution does not help when the CPU is the limit (#384).
  • v2.0.6 starts faster on a warm cache (PR #859); first start after an update prepares shaders again.

Reports merged here (16 issues, about 27 people)

Issue Title People GPU Version Key detail
#38 Running low fps after update green screeen fixed stuck at 6fps 3 RX 9060 XT pre-movie-sync Movies/title at 6 fps
#227 very bad fps on 30 series 3 RTX 3050 Laptop, RTX 3060 (Linux) compile-retry Low fps on 30-series laptops; Linux RTX 3060 headless numbers: 27 fps walking, GPU 15-18 ms, VRAM 10.8/11.4 GB, first-visit compile stalls up to 47 s
#639 frame gen don't work 3 - - Frame generation shows 60 fps but feels like 30 (no smoothness gain), also x3/x4
#738 Feature Request: RAM Usage Optimization for 16 GB Systems 3 RTX 4060 + i7-4790 16 GB DDR3 v2.0.2 RAM at 100% on 16 GB systems -> 3-15 fps; Linux user says process stays under 8 GB
#384 Performance: replay submit cost scales with draw count; pipeline-crea… 2 RTX 5070 Ti, RTX 4080 v2.0.5 RTX 4080: 45 fps, CPU replay 15.2 ms + GPU 13.4 ms; RX_VK_GPU_PROFILE: mirror write-back copies 7.08 ms = 52.8% of GPU time (68.5 MB/frame); resolution changes nothing
#470 CPU high usage, GPU not fully utilized, and memory leaked on RAM 2 RTX 4080 v2.0.5 No RAM leak: 10.5-13 GB working set, 21-26 GB committed in play; HOS_PIN to P-cores +1-2 fps on hybrid Intel
#865 AMD RX 7800 XT (v2.0.5): sudden stutters and late/wrong textures when… 2 RX 7800 XT v2.0.5 First-seen pipeline stalls; a commenter gets 20-30 fps on a 7900 XTX
#58 Big hangs on cutscenes 1 RTX 2080S Max-Q old Hangs/audio loss in cutscenes
#109 Stuck at 10 fps? 1 RTX 4060 brighter Stuck at 10 fps: log never left the intro movie, upscaler networks 83-93 ms GPU on a desktop RTX 4060; slow-movie auto switch to linear upscale planned since
#427 Shader stuttering (AMD) 1 RX 7900 XTX foliage-linux-updater Shader stutter after pre-loading; background compute + more precompile since ultrawide/v2.0.5
#711 Shader preparation takes ~7.5 minutes on first run (RX 6650 XT) and a… 1 RX 6650 XT v2.0.2 First-run shader preparation 449.8 s (31985 programs) on v2.0.2 vs 69.6 s on device-lost-fix; 5.6 GB program cache; PR #727 for area-entry lag
#714 Severe performance drops / Modifying custom in-game settings yields z… 1 RTX 3050 Laptop 4GB v2.0.2 8 fps regardless of preset/resolution/frame generation (FG counter shows but no gain); log
#766 Avoid ~63 ms Vulkan host memory imports on first texture upload 1 RTX 3070 honest-eta Avoid ~63 ms host-memory imports on first 2000x2000 texture uploads by staging (local experiment code included)
#813 low fps,stuttering on gtx 1660 super and ryzen 5 4500 1 GTX 1660 Super v2.0.5 Graphics bugs gone on v2.0.5 but fps very low (log)
#844 PERFORMANCE IS HORRENDOUS ON A 5070 and a 5700x 1 RTX 5070 + R7 5700X 16 GB v2.0.5 "No bugs but fps very low" in Kokiri (screenshots, no log)
#945 Optimizaton for low hardware 1 GTX 1660 v2.0.5 10-15 fps in Kokiri on v2.0.5 vs 22 fps on v2.0.3 (logs for both)

Adding a report

Please update to v2.0.6 first (https://github.com/NotRexed/Hookshot/releases/tag/v2.0.6; Update next to Play in the launcher). Please add: CPU, GPU, RAM, the place and fps, GPU usage if you can see it, and hookshot_log.txt.

All tracking issues

  • #993 Garbled / rainbow textures that get worse over play time (VRAM over budget, mostly 4-8 GB cards)
  • #994 Floating trees, missing mountains and vanishing near vegetation
  • #995 Low FPS with low GPU usage (CPU-bound), FPS drops in Castle Town / Hyrule Field, shader stutter
  • #996 AMD on Windows: driver crash, device lost or freeze (RX 5000-9000, Deku Tree / after cutscenes)
  • #997 AMD handhelds and APUs (ROG Ally / Ally X, Legion Go, Radeon 780M/860M/890M): black screen, device lost, reboot
  • #998 Black screen with audio after the intro / at the Deku Tree cutscene
  • #999 Intel Arc and Core Ultra iGPUs: device lost, black screen after the intro, Linux ANV crashes
  • #1000 Linux and Steam Deck: GPU hangs, crashes, low FPS and build failures
  • #1001 GPUs without mesh shaders (GTX 9xx/10xx, RX 400/500/5000, Iris Xe/UHD): v2.0.6 fallback reports
  • #1002 Invisible or missing objects, walls and effects
  • #1003 Antivirus false positives on HookshotBuilder.exe (Defender Trojan:Script/Wacatac.H!ml)
  • #1004 Lighting: overexposure, over-bright bloom, crushed or black shadows, black faces
  • #1005 Flicker and flashing: brightness pulses, one-frame flashes, doubled image with frame pacing
  • #1006 AMD rendering: white silhouettes, block squares / grid lines, no anti-aliasing (networks in software)
  • #1007 Feature requests (collected)
This issue collects every open report of low FPS while the GPU is not fully used, and shader stutter, so there is one place to follow it. The individual reports were closed pointing here; their logs, screenshots and discussion stay in each linked issue. ## Summary - **Low fps with the GPU not fully used:** fps stays well below what the GPU should give. - **Drops in Castle Town and Hyrule Field** since the foliage came back (a known issue in the v2.0.6 notes). - **Stalls** the first time a new effect or area appears. - **Long first-start shader preparation.** Linux/RADV-specific low fps is in the Linux tracker, and AMD handhelds have their own. ## Affected GPUs and versions - **GPUs:** desktop GPUs from the GTX 1660 to the RTX 4080 (high-end too, so this is not about weak GPUs), RTX 3050 laptops, RX 7800 XT / 7900 XTX. - **Versions:** v2.0.2-v2.0.5. ## Evidence from the logs - **#384** (RTX 4080, 45 fps): CPU replay 15.2 ms + GPU 13.4 ms a frame. With `RX_VK_GPU_PROFILE`, mirror write-back copies take 7.08 ms, 52.8% of GPU time (68.5 MB a frame). Changing the resolution changes nothing. - **#470:** no RAM leak (10.5-13 GB working set). `HOS_PIN` to P-cores gives +1-2 fps on hybrid Intel CPUs. - **#945:** GTX 1660 gets 10-15 fps in Kokiri on v2.0.5 vs 22 fps on v2.0.3 (logs for both). #824 reports 5-10 fps less since v2.0.3. - **#865:** every first-seen pipeline costs a stall. **#227** (Linux RTX 3060): first-visit compile stalls of up to 47 s. - **#711:** first-run preparation took 449.8 s (31,985 programs) on v2.0.2 vs 69.6 s on an earlier build; program cache 5.6 GB. **#879** (860M) is in the AMD handheld tracker. - **#766:** first uploads of large textures cost about 63 ms of host-memory import each; the reporter proposes a staging copy. - **#639:** frame generation shows 60 fps but feels like 30. - **#738:** RAM at 100% on 16 GB systems. ## Workarounds - `HOS_PIN` (pin to P-cores on hybrid Intel CPUs): +1-2 fps in #470. - `RX_VK_WAIT_GPU=0` helped some players. - Lowering the resolution does not help when the CPU is the limit (#384). - v2.0.6 starts faster on a warm cache (PR #859); first start after an update prepares shaders again. ## Reports merged here (16 issues, about 27 people) | Issue | Title | People | GPU | Version | Key detail | |---|---|---|---|---|---| | #38 | Running low fps after update green screeen fixed stuck at 6fps | 3 | RX 9060 XT | pre-movie-sync | Movies/title at 6 fps | | #227 | very bad fps on 30 series | 3 | RTX 3050 Laptop, RTX 3060 (Linux) | compile-retry | Low fps on 30-series laptops; Linux RTX 3060 headless numbers: 27 fps walking, GPU 15-18 ms, VRAM 10.8/11.4 GB, first-visit compile stalls up to 47 s | | #639 | frame gen don't work | 3 | - | - | Frame generation shows 60 fps but feels like 30 (no smoothness gain), also x3/x4 | | #738 | Feature Request: RAM Usage Optimization for 16 GB Systems | 3 | RTX 4060 + i7-4790 16 GB DDR3 | v2.0.2 | RAM at 100% on 16 GB systems -> 3-15 fps; Linux user says process stays under 8 GB | | #384 | Performance: replay submit cost scales with draw count; pipeline-crea… | 2 | RTX 5070 Ti, RTX 4080 | v2.0.5 | RTX 4080: 45 fps, CPU replay 15.2 ms + GPU 13.4 ms; RX_VK_GPU_PROFILE: mirror write-back copies 7.08 ms = 52.8% of GPU time (68.5 MB/frame); resolution changes nothing | | #470 | CPU high usage, GPU not fully utilized, and memory leaked on RAM | 2 | RTX 4080 | v2.0.5 | No RAM leak: 10.5-13 GB working set, 21-26 GB committed in play; HOS_PIN to P-cores +1-2 fps on hybrid Intel | | #865 | AMD RX 7800 XT (v2.0.5): sudden stutters and late/wrong textures when… | 2 | RX 7800 XT | v2.0.5 | First-seen pipeline stalls; a commenter gets 20-30 fps on a 7900 XTX | | #58 | Big hangs on cutscenes | 1 | RTX 2080S Max-Q | old | Hangs/audio loss in cutscenes | | #109 | Stuck at 10 fps? | 1 | RTX 4060 | brighter | Stuck at 10 fps: log never left the intro movie, upscaler networks 83-93 ms GPU on a desktop RTX 4060; slow-movie auto switch to linear upscale planned since | | #427 | Shader stuttering (AMD) | 1 | RX 7900 XTX | foliage-linux-updater | Shader stutter after pre-loading; background compute + more precompile since ultrawide/v2.0.5 | | #711 | Shader preparation takes ~7.5 minutes on first run (RX 6650 XT) and a… | 1 | RX 6650 XT | v2.0.2 | First-run shader preparation 449.8 s (31985 programs) on v2.0.2 vs 69.6 s on device-lost-fix; 5.6 GB program cache; PR #727 for area-entry lag | | #714 | Severe performance drops / Modifying custom in-game settings yields z… | 1 | RTX 3050 Laptop 4GB | v2.0.2 | 8 fps regardless of preset/resolution/frame generation (FG counter shows but no gain); log | | #766 | Avoid ~63 ms Vulkan host memory imports on first texture upload | 1 | RTX 3070 | honest-eta | Avoid ~63 ms host-memory imports on first 2000x2000 texture uploads by staging (local experiment code included) | | #813 | low fps,stuttering on gtx 1660 super and ryzen 5 4500 | 1 | GTX 1660 Super | v2.0.5 | Graphics bugs gone on v2.0.5 but fps very low (log) | | #844 | PERFORMANCE IS HORRENDOUS ON A 5070 and a 5700x | 1 | RTX 5070 + R7 5700X 16 GB | v2.0.5 | "No bugs but fps very low" in Kokiri (screenshots, no log) | | #945 | Optimizaton for low hardware | 1 | GTX 1660 | v2.0.5 | 10-15 fps in Kokiri on v2.0.5 vs 22 fps on v2.0.3 (logs for both) | ## Adding a report Please update to v2.0.6 first (https://github.com/NotRexed/Hookshot/releases/tag/v2.0.6; Update next to Play in the launcher). Please add: CPU, GPU, RAM, the place and fps, GPU usage if you can see it, and `hookshot_log.txt`. ## All tracking issues - #993 Garbled / rainbow textures that get worse over play time (VRAM over budget, mostly 4-8 GB cards) - #994 Floating trees, missing mountains and vanishing near vegetation - #995 Low FPS with low GPU usage (CPU-bound), FPS drops in Castle Town / Hyrule Field, shader stutter - #996 AMD on Windows: driver crash, device lost or freeze (RX 5000-9000, Deku Tree / after cutscenes) - #997 AMD handhelds and APUs (ROG Ally / Ally X, Legion Go, Radeon 780M/860M/890M): black screen, device lost, reboot - #998 Black screen with audio after the intro / at the Deku Tree cutscene - #999 Intel Arc and Core Ultra iGPUs: device lost, black screen after the intro, Linux ANV crashes - #1000 Linux and Steam Deck: GPU hangs, crashes, low FPS and build failures - #1001 GPUs without mesh shaders (GTX 9xx/10xx, RX 400/500/5000, Iris Xe/UHD): v2.0.6 fallback reports - #1002 Invisible or missing objects, walls and effects - #1003 Antivirus false positives on HookshotBuilder.exe (Defender Trojan:Script/Wacatac.H!ml) - #1004 Lighting: overexposure, over-bright bloom, crushed or black shadows, black faces - #1005 Flicker and flashing: brightness pulses, one-frame flashes, doubled image with frame pacing - #1006 AMD rendering: white silhouettes, block squares / grid lines, no anti-aliasing (networks in software) - #1007 Feature requests (collected)
lucasmazzoni45-del commented 2026-11-09 12:40:01 +00:00 (Migrated from github.com)

v2.0.7 on RX 7800 XT (16 GB), Ryzen 7 5800X3D, 32 GB, 1440p, frame generation 3x. Note: this is a non-stock hookshot_vk.dll (v2.0.7 plus my own patch), so numbers may differ from stock.

  • First start after the update: shader preparation took 1041.7 s (38,809 programs; the driver step on AMD is most of it). Closing the window during it made the next start skip that step, which is why my patch counts two failed starts instead of one.
  • In play: 59-60 game fps, reported GPU-bound. During one session 849 graphics pipelines took over 200 ms to compile, several over 1 s (e.g. 7060695868b1daa0: 1001 ms, 0224bbeb1376afde: 1042 ms).
  • The rc.1 and rc.3 changes (#854, #876, #908, #901, #939, #860) look aimed at exactly this; I will report after testing them.

Log available on request.

v2.0.7 on RX 7800 XT (16 GB), Ryzen 7 5800X3D, 32 GB, 1440p, frame generation 3x. Note: this is a non-stock `hookshot_vk.dll` (v2.0.7 plus my own patch), so numbers may differ from stock. - First start after the update: shader preparation took 1041.7 s (38,809 programs; the driver step on AMD is most of it). Closing the window during it made the next start skip that step, which is why my patch counts two failed starts instead of one. - In play: 59-60 game fps, reported GPU-bound. During one session 849 graphics pipelines took over 200 ms to compile, several over 1 s (e.g. 7060695868b1daa0: 1001 ms, 0224bbeb1376afde: 1042 ms). - The rc.1 and rc.3 changes (#854, #876, #908, #901, #939, #860) look aimed at exactly this; I will report after testing them. Log available on request.
Nitnek commented 2026-11-09 18:13:57 +00:00 (Migrated from github.com)

Low FPS with low GPU use: a small buffer-mirror budget costs a CPU↔GPU wait at every submit (v2.0.8-rc.5, RTX 3070 8 GB, Windows 11)

Scripted test, same every run: load a Hyrule Field save, then run the same 45 s route. Only RX_VK_MIRROR_MB changes. Times are from RX_VK_FRAME_LOG. "Memory sync" is the per-frame figure from the log's a frame's replay time line.

RX_VK_MIRROR_MB avg FPS p50 frame memory sync / frame save load
0 5 194 ms 165 ms 28 s
1024 (4 runs) 34-48 20-29 ms 5-21 ms 14-17 s
2048 (the default on 8 GB cards) 55 17.2 ms 1.1 ms 14 s
3072 57 16.8 ms 1.1 ms 14 s

Where the time goes. submit_memory_work → memory_take_capture. At 1024 MB the log shows a submit's memory work (ms): capture taken 2.4-3.0, 6 submits a frame. In a local build I timed the parts of take_capture apart:

  • nearly all of it is the vk_timeline_wait(c->late_value) at its top: the CPU waits for the GPU to finish the submissions that write the pages being captured;
  • pools_mu costs 0.000 ms.

The fewer pools are mirrored, the more pages the GPU writes straight into guest memory, and the more often a submit waits for the GPU. Hence low FPS with the GPU half idle.

Who is likely hit.

  • mirror_budget() gives cards of ≤6.5 GB only VRAM/8: 768 MB on a 6 GB card.
  • The Insert menu's "Minimum (4GB)" preset gives 256 MB on the cards it is for.
  • Anyone who lowered RX_VK_MIRROR_MB against VRAM pressure, as I did with 1024 yesterday. Removing it took the field from ~40 to ~55 FPS, and Castle Town got much smoother too.

Possible directions (not tried).

  • A floor on the budget.
  • Putting the pools the GPU writes first in line for a mirror.
  • Waiting only for the late pages the capture really needs.

Happy to test a build on this card.

**Low FPS with low GPU use: a small buffer-mirror budget costs a CPU↔GPU wait at every submit (v2.0.8-rc.5, RTX 3070 8 GB, Windows 11)** Scripted test, same every run: load a Hyrule Field save, then run the same 45 s route. Only `RX_VK_MIRROR_MB` changes. Times are from `RX_VK_FRAME_LOG`. "Memory sync" is the per-frame figure from the log's `a frame's replay time` line. | `RX_VK_MIRROR_MB` | avg FPS | p50 frame | memory sync / frame | save load | |---|---|---|---|---| | 0 | 5 | 194 ms | 165 ms | 28 s | | 1024 (4 runs) | 34-48 | 20-29 ms | 5-21 ms | 14-17 s | | 2048 (the default on 8 GB cards) | 55 | 17.2 ms | 1.1 ms | 14 s | | 3072 | 57 | 16.8 ms | 1.1 ms | 14 s | **Where the time goes.** `submit_memory_work` → `memory_take_capture`. At 1024 MB the log shows `a submit's memory work (ms): capture taken 2.4-3.0`, 6 submits a frame. In a local build I timed the parts of `take_capture` apart: - nearly all of it is the `vk_timeline_wait(c->late_value)` at its top: the CPU waits for the GPU to finish the submissions that write the pages being captured; - `pools_mu` costs 0.000 ms. The fewer pools are mirrored, the more pages the GPU writes straight into guest memory, and the more often a submit waits for the GPU. Hence low FPS with the GPU half idle. **Who is likely hit.** - `mirror_budget()` gives cards of ≤6.5 GB only VRAM/8: 768 MB on a 6 GB card. - The Insert menu's "Minimum (4GB)" preset gives 256 MB on the cards it is for. - Anyone who lowered `RX_VK_MIRROR_MB` against VRAM pressure, as I did with 1024 yesterday. Removing it took the field from ~40 to ~55 FPS, and Castle Town got much smoother too. **Possible directions (not tried).** - A floor on the budget. - Putting the pools the GPU writes first in line for a mirror. - Waiting only for the late pages the capture really needs. Happy to test a build on this card.
lucasmazzoni45-del commented 2026-11-09 18:34:59 +00:00 (Migrated from github.com)

Data point, AMD RX 7800 XT + Ryzen 7 5800X3D (8c/16t): replay CPU time roughly doubled between v2.0.7 and v2.0.10, possible cause: the render thread pinned to the reserved core (#1034)

Setup: Windows 11, RX 7800 XT, 5800X3D, 2560x1440, frame generation x2, frame pacing on. Both sessions below ran my own patched hookshot_vk.dll (AMD memory/pool tweaks), not the stock one, so this is not a clean stock-vs-stock comparison. The scenes were also not identical (normal play, Hyrule Field / combat). Treat it as a hint, not a measurement.

From hookshot_log.txt (sessions of a few minutes each):

v2.0.7 v2.0.10
mean fps (60 target) 56.4 54.3
present intervals over 22 ms 1.4% 11.3%
present intervals over 40 ms 20 of 4210 73 of 7852
replay submit CPU in heavy moments 6.2 ms 11.4 ms
of that draw 3.2 ms 6.4 ms
of that memory sync 0.45 ms 1.6 ms
GPU time per frame 9.4 ms 10.3 ms

The GPU time is about the same; the extra time is on the CPU, so it fits the CPU-bound reports here.

What changed between the two for a 16-thread machine, from the log and the sources:

  • v2.0.10 logs threads: Presentation Thread on the reserved core (logical processors 0xc000; HOS_PIN_RENDER=0: off). The v2.0.7 log has no such line. In the default synchronous mode the replay runs on that thread, and the reserved core is also the one the replay thread is pinned to.
  • RX_VK_CAPTURE_THREADS defaults to 4 helpers with 12 or more logical processors (it was 2 before).
  • In the same v2.0.10 log the number of device memory blocks goes up and down by 10-15 during play (107 -> 94 -> 109 ...). On AMD's Windows driver one vkAllocateMemory of a 128 MB block takes about 170 ms, which would match the occasional frames over 40 ms.

What I changed, in hookshot_env.txt, to try to get v2.0.7's behaviour back:

HOS_PIN_RENDER=0
RX_VK_CAPTURE_THREADS=2
RX_VK_IMAGE_PREALLOC=30

(RX_VK_IMAGE_PREALLOC only exists in my patched DLL.)

Not verified yet. I have not measured these settings in game, so I do not know if they remove the stutter. If someone with a many-core Ryzen or another AMD card can compare HOS_PIN_RENDER=0 and RX_VK_CAPTURE_THREADS=2 against the defaults on v2.0.8+, that would show whether the pinning is the cause. I will post my own result after I have tried it.

Separate small finding: on this card the log says matrix multiplies in software, so the default RX_VK_TAAU_KEYS=fd029a317a9a4b94 uses the plain upscale stand-in instead of the temporal network, and the shadows of grass and bushes look blocky. RX_VK_TAAU_KEYS=- runs the real network (about 1.3 ms more GPU here).

**Data point, AMD RX 7800 XT + Ryzen 7 5800X3D (8c/16t): replay CPU time roughly doubled between v2.0.7 and v2.0.10, possible cause: the render thread pinned to the reserved core (#1034)** Setup: Windows 11, RX 7800 XT, 5800X3D, 2560x1440, frame generation x2, frame pacing on. Both sessions below ran **my own patched `hookshot_vk.dll`** (AMD memory/pool tweaks), not the stock one, so this is not a clean stock-vs-stock comparison. The scenes were also not identical (normal play, Hyrule Field / combat). Treat it as a hint, not a measurement. From `hookshot_log.txt` (sessions of a few minutes each): | | v2.0.7 | v2.0.10 | |---|---|---| | mean fps (60 target) | 56.4 | 54.3 | | present intervals over 22 ms | 1.4% | 11.3% | | present intervals over 40 ms | 20 of 4210 | 73 of 7852 | | replay `submit` CPU in heavy moments | 6.2 ms | 11.4 ms | | of that `draw` | 3.2 ms | 6.4 ms | | of that `memory sync` | 0.45 ms | 1.6 ms | | GPU time per frame | 9.4 ms | 10.3 ms | The GPU time is about the same; the extra time is on the CPU, so it fits the CPU-bound reports here. What changed between the two for a 16-thread machine, from the log and the sources: - v2.0.10 logs `threads: Presentation Thread on the reserved core (logical processors 0xc000; HOS_PIN_RENDER=0: off)`. The v2.0.7 log has no such line. In the default synchronous mode the replay runs on that thread, and the reserved core is also the one the replay thread is pinned to. - `RX_VK_CAPTURE_THREADS` defaults to 4 helpers with 12 or more logical processors (it was 2 before). - In the same v2.0.10 log the number of device memory blocks goes up and down by 10-15 during play (107 -> 94 -> 109 ...). On AMD's Windows driver one `vkAllocateMemory` of a 128 MB block takes about 170 ms, which would match the occasional frames over 40 ms. What I changed, in `hookshot_env.txt`, to try to get v2.0.7's behaviour back: ``` HOS_PIN_RENDER=0 RX_VK_CAPTURE_THREADS=2 RX_VK_IMAGE_PREALLOC=30 ``` (`RX_VK_IMAGE_PREALLOC` only exists in my patched DLL.) **Not verified yet.** I have not measured these settings in game, so I do not know if they remove the stutter. If someone with a many-core Ryzen or another AMD card can compare `HOS_PIN_RENDER=0` and `RX_VK_CAPTURE_THREADS=2` against the defaults on v2.0.8+, that would show whether the pinning is the cause. I will post my own result after I have tried it. Separate small finding: on this card the log says `matrix multiplies in software`, so the default `RX_VK_TAAU_KEYS=fd029a317a9a4b94` uses the plain upscale stand-in instead of the temporal network, and the shadows of grass and bushes look blocky. `RX_VK_TAAU_KEYS=-` runs the real network (about 1.3 ms more GPU here).
Nitnek commented 2026-11-09 19:18:45 +00:00 (Migrated from github.com)

Data point for @lucasmazzoni45-del's question: no doubling of the replay time between v2.0.8-rc.3 (before #1034's pin) and v2.0.10 on Zen 3 + NVIDIA.

The setup:

  • Ryzen Zen 3, 8 cores / 16 threads (the reserved core is logical processors 0xc000), RTX 3070, Windows 11, stock settings;
  • the same scripted Hyrule Field run each time (load a save, then a 45 s route), builds alternated;
  • "submit" is the replay's per-frame time from the log's a frame's replay time line, last three samples.
Build submit (ms / frame) avg FPS
v2.0.8-rc.3 (no pin), run 1 16.3-16.9 48.3
v2.0.8-rc.3 (no pin), run 2 13.2-14.2 51.6
v2.0.10 (Presentation Thread pinned), run 1 10.9-11.3 53.2
v2.0.10 (Presentation Thread pinned), run 2 13.1-13.9 50.8
v2.0.10, HOS_PIN_RENDER=0 16.0-17.3 50.0

Run-to-run spread is a few ms, so read this as "no regression, the pin neutral to slightly better" on this CPU. Hybrid Intel CPUs are another matter (see the E-core reports in #1158). A doubling like the one you saw may come from the patched hookshot_vk.dll or the different scenes rather than #1034. A stock A/B with and without HOS_PIN_RENDER=0, same spot, would tell.

**Data point for @lucasmazzoni45-del's question: no doubling of the replay time between v2.0.8-rc.3 (before #1034's pin) and v2.0.10 on Zen 3 + NVIDIA.** The setup: - Ryzen Zen 3, 8 cores / 16 threads (the reserved core is logical processors `0xc000`), RTX 3070, Windows 11, stock settings; - the same scripted Hyrule Field run each time (load a save, then a 45 s route), builds alternated; - "submit" is the replay's per-frame time from the log's `a frame's replay time` line, last three samples. | Build | submit (ms / frame) | avg FPS | |---|---|---| | v2.0.8-rc.3 (no pin), run 1 | 16.3-16.9 | 48.3 | | v2.0.8-rc.3 (no pin), run 2 | 13.2-14.2 | 51.6 | | v2.0.10 (Presentation Thread pinned), run 1 | 10.9-11.3 | 53.2 | | v2.0.10 (Presentation Thread pinned), run 2 | 13.1-13.9 | 50.8 | | v2.0.10, `HOS_PIN_RENDER=0` | 16.0-17.3 | 50.0 | Run-to-run spread is a few ms, so read this as "no regression, the pin neutral to slightly better" on this CPU. Hybrid Intel CPUs are another matter (see the E-core reports in #1158). A doubling like the one you saw may come from the patched `hookshot_vk.dll` or the different scenes rather than #1034. A stock A/B with and without `HOS_PIN_RENDER=0`, same spot, would tell.
lucasmazzoni45-del commented 2026-11-09 19:41:43 +00:00 (Migrated from github.com)

Thanks @Nitnek, that is a useful A/B, and it does not support my guess: on your Zen 3 the pin is neutral to slightly better and HOS_PIN_RENDER=0 is not faster. I have taken HOS_PIN_RENDER=0 and RX_VK_CAPTURE_THREADS=2 out of my settings. As you say, the doubling I saw may come from my patched DLL or from the scenes being different; with the two logs I had I cannot separate those.

What I can add from the same 7800 XT logs: in the v2.0.10 session the replay wrote back about 33-60 MB a frame and memory sync was 1.6 ms, against 0.45 ms in the older session, which fits the write-back diff being off by default in v2.0.9/v2.0.10. I am on v2.0.11 now (write-back diff on) and will post the same numbers from it here, with and without my DLL, so the comparison is stock against stock.

Thanks @Nitnek, that is a useful A/B, and it does not support my guess: on your Zen 3 the pin is neutral to slightly better and `HOS_PIN_RENDER=0` is not faster. I have taken `HOS_PIN_RENDER=0` and `RX_VK_CAPTURE_THREADS=2` out of my settings. As you say, the doubling I saw may come from my patched DLL or from the scenes being different; with the two logs I had I cannot separate those. What I can add from the same 7800 XT logs: in the v2.0.10 session the replay wrote back about 33-60 MB a frame and `memory sync` was 1.6 ms, against 0.45 ms in the older session, which fits the write-back diff being off by default in v2.0.9/v2.0.10. I am on v2.0.11 now (write-back diff on) and will post the same numbers from it here, with and without my DLL, so the comparison is stock against stock.
SaKenLpB commented 2026-11-09 20:17:36 +00:00 (Migrated from github.com)

@NotRexed @hckrman101 quick one that may save you time on this issue:

TL;DR: on hybrid Intel CPUs the "reserved core" from #1034 is an E-core. Setting HOS_PIN_RENDER=0 gives ~+24% game fps (heavy scenes: 33 → 48 fps). Quick fix: don't pin by default when the CPU has more than one EfficiencyClass, or pick a P-core for the reserved core.


Intel hybrid CPUs: the render thread is pinned to an E-core, unpinning it gives ~+24% fps

Setup: i7-12700KF (8 P-cores + 4 E-cores), RTX 5070 12 GB, 32 GB DDR5-6000, Windows 11, NVIDIA 621 driver, output 2560x1440, frame generation 2x, RX_VK_WB_DIFF=1 and RX_VK_PRESENT_QUEUE=1 in both runs. Hookshot v2.0.11 (same pinning seen on v2.0.9/v2.0.10).

The log shows:
threads: Presentation Thread on the reserved core (logical processors 0x80000; HOS_PIN_RENDER=0: off)

On this CPU, logical processors 0–15 are the P-core threads and 16–19 are the E-cores, so the "reserved core" (LP 19) is an E-core, the slowest kind, running the CPU-bound replay thread.

With HOS_PIN_RENDER=0 in hookshot_env.txt (the line disappears from the log), same version, walking around Kokiri Village:

pinned (LP 19, E-core) HOS_PIN_RENDER=0
game fps, mean 45.8 56.7
median 45.3 59.6
10th percentile 39.9 49.4
min 23.7 33.9
1000–2000 draws 46.1 57.8
2000–3000 draws 46.7 53.5
3000+ draws 33.3 48.6

Caveats: the pinned run was the first start after updating, and the routes were not identical. But a longer earlier v2.0.10 session (pinned) averaged 39.2 fps at 3000+ draws, against 48.6 now.

Note: the workaround listed above (HOS_PIN to P-cores, +1-2 fps, #470) predates #1034. Since v2.0.8 the render thread gets the "reserved core", which is the last logical processor, and on hybrid Intel CPUs that is an E-core, so the gap is now much larger.

Suggested fix: on Windows, GetSystemCpuSetInformation gives each logical processor's EfficiencyClass (higher = faster core). When more than one class exists (Intel 12th–14th gen, Core Ultra), reserve a logical processor from the highest class instead of the last one, or skip pinning by default. This likely affects every Intel 12th gen and newer player. Logs available on request.

Side note: updating v2.0.10 → v2.0.11 from the launcher brought back an older copy of my hookshot_env.txt (a line I had added was gone), so players who set switches may lose them on update.

@NotRexed @hckrman101 quick one that may save you time on this issue: **TL;DR: on hybrid Intel CPUs the "reserved core" from #1034 is an E-core. Setting `HOS_PIN_RENDER=0` gives ~+24% game fps (heavy scenes: 33 → 48 fps).** Quick fix: don't pin by default when the CPU has more than one EfficiencyClass, or pick a P-core for the reserved core. --- **Intel hybrid CPUs: the render thread is pinned to an E-core, unpinning it gives ~+24% fps** Setup: i7-12700KF (8 P-cores + 4 E-cores), RTX 5070 12 GB, 32 GB DDR5-6000, Windows 11, NVIDIA 621 driver, output 2560x1440, frame generation 2x, `RX_VK_WB_DIFF=1` and `RX_VK_PRESENT_QUEUE=1` in both runs. Hookshot v2.0.11 (same pinning seen on v2.0.9/v2.0.10). The log shows: `threads: Presentation Thread on the reserved core (logical processors 0x80000; HOS_PIN_RENDER=0: off)` On this CPU, logical processors 0–15 are the P-core threads and 16–19 are the E-cores, so the "reserved core" (LP 19) is an **E-core**, the slowest kind, running the CPU-bound replay thread. With `HOS_PIN_RENDER=0` in `hookshot_env.txt` (the line disappears from the log), same version, walking around Kokiri Village: | | pinned (LP 19, E-core) | HOS_PIN_RENDER=0 | |---|---|---| | game fps, mean | 45.8 | **56.7** | | median | 45.3 | **59.6** | | 10th percentile | 39.9 | **49.4** | | min | 23.7 | **33.9** | | 1000–2000 draws | 46.1 | **57.8** | | 2000–3000 draws | 46.7 | **53.5** | | 3000+ draws | 33.3 | **48.6** | Caveats: the pinned run was the first start after updating, and the routes were not identical. But a longer earlier v2.0.10 session (pinned) averaged 39.2 fps at 3000+ draws, against 48.6 now. Note: the workaround listed above (HOS_PIN to P-cores, +1-2 fps, #470) predates #1034. Since v2.0.8 the render thread gets the "reserved core", which is the last logical processor, and on hybrid Intel CPUs that is an E-core, so the gap is now much larger. Suggested fix: on Windows, `GetSystemCpuSetInformation` gives each logical processor's `EfficiencyClass` (higher = faster core). When more than one class exists (Intel 12th–14th gen, Core Ultra), reserve a logical processor from the highest class instead of the last one, or skip pinning by default. This likely affects every Intel 12th gen and newer player. Logs available on request. Side note: updating v2.0.10 → v2.0.11 from the launcher brought back an older copy of my `hookshot_env.txt` (a line I had added was gone), so players who set switches may lose them on update.
Nitnek commented 2026-11-09 20:25:43 +00:00 (Migrated from github.com)

@SaKenLpB this is what #1158 (@jordanella) addresses. It moves the replay onto the reserved core only while it replays, and it picks that core among the fastest kind (P-cores), keeping one only with 4+ of them. Its thread has hybrid data like yours: the 14700KF E-core pin, and a 13700K going from 48.7 fps to a vsync-capped 60. A run of that PR on your 12700KF, against HOS_PIN_RENDER=0, would be a useful third hybrid data point. On a non-hybrid Zen 3 it made no difference (my numbers there).

@SaKenLpB this is what #1158 (@jordanella) addresses. It moves the replay onto the reserved core only while it replays, and it picks that core among the fastest kind (P-cores), keeping one only with 4+ of them. Its thread has hybrid data like yours: the 14700KF E-core pin, and a 13700K going from 48.7 fps to a vsync-capped 60. A run of that PR on your 12700KF, against `HOS_PIN_RENDER=0`, would be a useful third hybrid data point. On a non-hybrid Zen 3 it made no difference (my numbers there).
SaKenLpB commented 2026-11-09 20:38:05 +00:00 (Migrated from github.com)

Thanks @Nitnek, and sorry @jordanella, I hadn't seen #1158 before posting. Good to see the same conclusion from another angle: the 12700KF numbers above (reserved core on an E-core vs HOS_PIN_RENDER=0) can count as a third hybrid data point.

I can't build PR branches myself, but I'll test #1158 as soon as it lands in a release and post the before/after numbers here, same route through Kokiri Village.

Thanks @Nitnek, and sorry @jordanella, I hadn't seen #1158 before posting. Good to see the same conclusion from another angle: the 12700KF numbers above (reserved core on an E-core vs `HOS_PIN_RENDER=0`) can count as a third hybrid data point. I can't build PR branches myself, but I'll test #1158 as soon as it lands in a release and post the before/after numbers here, same route through Kokiri Village.
lucasmazzoni45-del commented 2026-11-09 20:39:15 +00:00 (Migrated from github.com)

As promised, the stock-against-stock numbers on v2.0.11 (write-back diff on) on the 7800 XT. Short version: on a scripted route with no new areas, the stock DLL and my patched DLL both hold the 60 game fps cap and neither stutters. I could not show a difference between them, and this route does not reproduce my stutter.

Setup: RX 7800 XT, Ryzen 7 5800X3D (8c/16t), Windows 11, windowed 2560x1440, frame generation x2, frame pacing on, RX_VK_WB_DIFF=1, RX_VK_TAAU_KEYS=-. The same scripted input (HOS_INPUT_SCRIPT: load the save, then walking, sword swings and camera for about 270 s) in both runs, 420 s each, started 150 s late because the first start after a DLL swap redoes the shader and pipeline preparation (155-156 s in both runs). Frame times from RX_VK_FRAME_LOG, taken from second 300 on, after the load:

patched DLL stock v2.0.11 DLL
frames 14496 14233
shown fps (cap 120 with x2) 119.9 118.0
median / p95 / p99 frame 8.3 / 8.6 / 9.2 ms 8.3 / 8.6 / 9.5 ms
frames over 40 ms 1 11
longest frame 90 ms 1172 ms
replay submit / draw / memory sync (second half of the run) 6.0 / 3.5 / 0.35 ms 6.6 / 3.8 / 0.38 ms
GPU time a frame 8.2 ms 8.3 ms

What this does and does not say:

  • One run per DLL, so the 1 against 11 frames over 40 ms is not a result I would lean on.
  • The patched DLL ran first, on a colder pipeline cache: its log has 243 SLOW compile lines (up to 12.8 s each, on the background threads) and the stock run has none, because the shared mkw_vk_pipelines.bin was warm by then. That favours the stock run. The route stayed smooth in the patched run anyway.
  • The route walks through already-compiled areas. My stutter shows up when entering an area the cache has not seen: in my last play session, 21 pipelines took 17 s each to compile (max 22 s) at one spot, with up to 51 draws left out a frame and 7-16 fps for a while. That is the cold cache after the update (the start-up prediction only made 403 of 32729 pipelines inside its 90 s budget), not something this A/B can measure.
  • My DLL = v2.0.11 plus: a larger image-pool preallocation (RX_VK_IMAGE_PREALLOC=30), capture-buffer reuse, the pipeline-cache read over 2 GB on Windows (commit 0921695), and hand ports of #1130, #1129, #1046 and #840 (the latter with the review's points).

Also, since this thread is about the reserved core: it did not matter on this 5800X3D either (both runs have the Presentation Thread pinned, as in stock). I will post again if I get a clean stock-against-stock run through a new area.

As promised, the stock-against-stock numbers on v2.0.11 (write-back diff on) on the 7800 XT. **Short version: on a scripted route with no new areas, the stock DLL and my patched DLL both hold the 60 game fps cap and neither stutters.** I could not show a difference between them, and this route does not reproduce my stutter. Setup: RX 7800 XT, Ryzen 7 5800X3D (8c/16t), Windows 11, windowed 2560x1440, frame generation x2, frame pacing on, `RX_VK_WB_DIFF=1`, `RX_VK_TAAU_KEYS=-`. The same scripted input (`HOS_INPUT_SCRIPT`: load the save, then walking, sword swings and camera for about 270 s) in both runs, 420 s each, started 150 s late because the first start after a DLL swap redoes the shader and pipeline preparation (155-156 s in both runs). Frame times from `RX_VK_FRAME_LOG`, taken from second 300 on, after the load: | | patched DLL | stock v2.0.11 DLL | |---|---|---| | frames | 14496 | 14233 | | shown fps (cap 120 with x2) | 119.9 | 118.0 | | median / p95 / p99 frame | 8.3 / 8.6 / 9.2 ms | 8.3 / 8.6 / 9.5 ms | | frames over 40 ms | 1 | 11 | | longest frame | 90 ms | 1172 ms | | replay `submit` / `draw` / `memory sync` (second half of the run) | 6.0 / 3.5 / 0.35 ms | 6.6 / 3.8 / 0.38 ms | | GPU time a frame | 8.2 ms | 8.3 ms | What this does and does not say: - One run per DLL, so the 1 against 11 frames over 40 ms is not a result I would lean on. - The patched DLL ran first, on a colder pipeline cache: its log has 243 `SLOW compile` lines (up to 12.8 s each, on the background threads) and the stock run has none, because the shared `mkw_vk_pipelines.bin` was warm by then. That favours the stock run. The route stayed smooth in the patched run anyway. - The route walks through already-compiled areas. My stutter shows up when entering an area the cache has not seen: in my last play session, 21 pipelines took 17 s each to compile (max 22 s) at one spot, with up to 51 draws left out a frame and 7-16 fps for a while. That is the cold cache after the update (the start-up prediction only made 403 of 32729 pipelines inside its 90 s budget), not something this A/B can measure. - My DLL = v2.0.11 plus: a larger image-pool preallocation (`RX_VK_IMAGE_PREALLOC=30`), capture-buffer reuse, the pipeline-cache read over 2 GB on Windows (commit 0921695), and hand ports of #1130, #1129, #1046 and #840 (the latter with the review's points). Also, since this thread is about the reserved core: it did not matter on this 5800X3D either (both runs have the Presentation Thread pinned, as in stock). I will post again if I get a clean stock-against-stock run through a new area.
DeAuthenticated commented 2026-11-09 21:50:40 +00:00 (Migrated from github.com)

RX 9070 XT works (Windows), and two switches took Kokiri from ~35 to ~52 fps on a hybrid Intel CPU

The README's AMD row doesn't list the RX 9070 XT yet, so for the record: Hookshot v2.0.11 runs on an RX 9070 XT (RDNA 4). I've had no device lost or crash in Kokiri Forest, and the picture is right (lighting, foliage, no black or white artifacts).

Setup: RX 9070 XT 16 GB, AMD Adrenalin 26.10.2 (32.0.32015.12012, Vulkan driver 0x8001a0), Intel i5-13400F (6P + 4E), 32 GB, Windows 11 26H2. v2.0.11, output 2560x1440, resolution 100%, Lanczos, frame generation 3x, frame pacing on, game speed Real time, robust buffers Auto.

Kokiri Forest, free walking. The fps is the game's own rate from hookshot_log.txt, averaged over the 5 s samples with more than 1500 draws:

hookshot_env.txt Avg fps Min Max Replay "submit" per frame
(none, stock v2.0.11) ~35 23 ~50 ~22 ms
RX_VK_PRESENT_QUEUE=1 42.8 (25 samples) 23.2 53.1 ~22 ms
RX_VK_PRESENT_QUEUE=1 + HOS_PIN_RENDER=0 51.6 (21 samples) 38.0 59.8 17-18 ms

GPU time stays at 11-14 ms a frame, so it's CPU-bound, as this issue describes.

1. The reserved core lands on an E-core on the i5-13400F. The stock log says:

threads: Presentation Thread on the reserved core (logical processors 0x8000; HOS_PIN_RENDER=0: off)

Logical processor 15 is one of the four E-cores (0-11 are the six P-cores with HT). With HOS_PIN_RENDER=0, replay time drops from ~22 to ~17-18 ms a frame and the average goes up by about 9 fps. That's another hybrid data point for #1158: it should pick a P-core on this CPU.

2. RX_VK_PRESENT_QUEUE=1 is fine on AMD. Re #1161: with the separate present queue on this card, there's no black screen and no device lost, and it was worth about +7 fps here:

vk: presents on a queue of their own, of compute family 1 (RX_VK_PRESENT_QUEUE=0: off)

If it stays off by default because of NVIDIA's 621 driver, turning it on by default for AMD might be worth considering.

These numbers come from free play, not a scripted route, so treat them as approximate. I'm happy to run a scripted route (HOS_INPUT_SCRIPT) if that's more useful.

**RX 9070 XT works (Windows), and two switches took Kokiri from ~35 to ~52 fps on a hybrid Intel CPU** The README's AMD row doesn't list the RX 9070 XT yet, so for the record: **Hookshot v2.0.11 runs on an RX 9070 XT (RDNA 4).** I've had no device lost or crash in Kokiri Forest, and the picture is right (lighting, foliage, no black or white artifacts). **Setup:** RX 9070 XT 16 GB, AMD Adrenalin 26.10.2 (`32.0.32015.12012`, Vulkan driver `0x8001a0`), Intel i5-13400F (6P + 4E), 32 GB, Windows 11 26H2. v2.0.11, output 2560x1440, resolution 100%, Lanczos, frame generation 3x, frame pacing on, game speed Real time, robust buffers Auto. **Kokiri Forest, free walking.** The fps is the game's own rate from `hookshot_log.txt`, averaged over the 5 s samples with more than 1500 draws: | `hookshot_env.txt` | Avg fps | Min | Max | Replay "submit" per frame | |---|---|---|---|---| | (none, stock v2.0.11) | ~35 | 23 | ~50 | ~22 ms | | `RX_VK_PRESENT_QUEUE=1` | 42.8 (25 samples) | 23.2 | 53.1 | ~22 ms | | `RX_VK_PRESENT_QUEUE=1` + `HOS_PIN_RENDER=0` | **51.6** (21 samples) | 38.0 | 59.8 | **17-18 ms** | GPU time stays at 11-14 ms a frame, so it's CPU-bound, as this issue describes. **1. The reserved core lands on an E-core on the i5-13400F.** The stock log says: ``` threads: Presentation Thread on the reserved core (logical processors 0x8000; HOS_PIN_RENDER=0: off) ``` Logical processor 15 is one of the four E-cores (0-11 are the six P-cores with HT). With `HOS_PIN_RENDER=0`, replay time drops from ~22 to ~17-18 ms a frame and the average goes up by about 9 fps. That's another hybrid data point for #1158: it should pick a P-core on this CPU. **2. `RX_VK_PRESENT_QUEUE=1` is fine on AMD.** Re #1161: with the separate present queue on this card, there's no black screen and no device lost, and it was worth about +7 fps here: ``` vk: presents on a queue of their own, of compute family 1 (RX_VK_PRESENT_QUEUE=0: off) ``` If it stays off by default because of NVIDIA's 621 driver, turning it on by default for AMD might be worth considering. These numbers come from free play, not a scripted route, so treat them as approximate. I'm happy to run a scripted route (`HOS_INPUT_SCRIPT`) if that's more useful.
felipefl142 commented 2026-11-09 22:33:01 +00:00 (Migrated from github.com)

run-A_defaults_hookshot_log.txt
run-B_mirror3072_hookshot_log.txt
run-C_capturewait0_hookshot_log.txt

RTX 3070 Laptop + i7-11800H, Linux, v2.0.11: in Hyrule Field the replay waits 2-3.5 ms per submit in take_capture for late write-back pages, ~20 ms of "memory sync" a frame. RX_VK_CAPTURE_WAIT=0 removes the wait but brings the exposure flashes back.

Setup

  • Hookshot v2.0.11 (source 10f983ad, first line of the log), built with the Linux AppImage.
  • GPU: RTX 3070 Laptop 8 GB, NVIDIA 620.70.06. The game runs on it (gpu_device=2), and the external 2560x1440 120 Hz monitor is wired to it.
  • CPU: i7-11800H (8 cores / 16 threads, all the same kind, no E-cores). It reaches 95-100 °C in play and the kernel's thermal throttle counters climb, so some of the CPU time below is a laptop running hot.
  • RAM: 32 GB. OS: CachyOS, kernel 7.3.3, GNOME 51 on Wayland.
  • Changes from the defaults (hookshot_options.ini): fullscreen=1, gpu_device=2, frame_generation=2, frame_pacing=1, upscaling=1, sharpness=30, handheld_profile=1, robust_buffers=2. In the screenshots the overlay shows a 1706x960 game image and a 1600x900 (83%) 3D scene.
  • Hooked in: MangoHud (a Vulkan layer) was loaded in runs A and C below. Run B had no overlay. All runs went through CachyOS's game-performance wrapper, which only switches the power profile to performance.

Where: Hyrule Field, walking and turning the camera in front of Hyrule Castle (young Link, just after the Deku Tree). Every time. Run B also went into Castle Town Market, which drops the same way.

What the log shows. The numbers are the medians of the log's per-interval lines (a frame's replay time, GPU time, a submit's memory work) over the intervals with 1000 or more draws (1500 or more for B and C).

Run (attached log) hookshot_env.txt Overlay game fps submit draw programs memory sync capture taken per submit GPU time
A: Field only (26 intervals) none MangoHud 21.4 (min 15.4) 41.0 ms 15.1 5.8 20.4 2.2 ms (max 3.6) 21.4 ms
B: Field + Market (22) RX_VK_MIRROR_MB=3072 none 29.8 (min 12.4) 29.9 ms 13.4 5.1 13.0 2.0 ms 16.6 ms
C: Field (5, ~2900 draws) RX_VK_CAPTURE_WAIT=0 MangoHud 26.5 (min 23.4) 32.5 ms 23.8 11.5 1.4 0.1 ms 12.8 ms

For comparison, earlier in run A (Kokiri Forest, around 670 draws): 50 fps, submit 12.8 ms, memory sync 1.5 ms, capture 0.1 ms per submit.

  1. The jump comes from the capture's late wait, not from the mirror budget. Run A's memory sync goes from 2-3 ms to 10-36 ms the moment the Field loads, while the MB synced and written back per frame stay the same (about 11 MB synced and 67 MB written back). Over the same stretch, capture taken goes from 0.3-0.5 ms to 1.5-3.6 ms per submit, at 6 submits a frame. The mirrors stay at 1.4-1.76 GB, under the 2048 MB an 8 GB card gets. Raising RX_VK_MIRROR_MB to 3072 in run B changed nothing: the mirrors stayed at about 1.5 GB and the wait was still there. So this does not look like the small-mirror-budget case @Nitnek measured above.
  2. RX_VK_CAPTURE_WAIT=0 confirms it, and confirms why the wait is there. Memory sync falls to 1.4 ms and capture to 0.1 ms per submit, but the brightness flashes all the time while walking and turning the camera. That looks like the exposure-meter flashes the comment in capture_make describes (#1005). I went back to the default.
  3. Even without the wait, the Field is not at 60. At about 2900 draws, recording takes about 24 ms on its own (programs 11.5 ms, descriptors 5.5 ms), so draw recording is the next limit on this CPU.
  4. The in-game overlay in the screenshots (lighter moments, 37-43 fps): GPU 75-80 %, GPU busy 6.7-6.8 ms a frame, frame prep 13.5-14.8 ms. VRAM 6.1-7.3 GB of the 7.46-7.55 GB allowed, and RAM 22-26 GB of 31 GB in use, of which the game's working set is about 16 GB.

Possible direction (not tried): the late wait seems to matter only for the exposure-meter page(s). If the replay waited for those pages alone, or once a frame instead of at every submit's capture, the Field could keep most of run C's memory-sync gain without the flashes.

Logs attached: run A (hookshot_log.2.txt at the time), run B (hookshot_log.1.txt), run C (hookshot_log.txt). No game files.

I ran the game and the tests.

[run-A_defaults_hookshot_log.txt](https://github.com/user-attachments/files/34604190/run-A_defaults_hookshot_log.txt) [run-B_mirror3072_hookshot_log.txt](https://github.com/user-attachments/files/34604191/run-B_mirror3072_hookshot_log.txt) [run-C_capturewait0_hookshot_log.txt](https://github.com/user-attachments/files/34604192/run-C_capturewait0_hookshot_log.txt) **RTX 3070 Laptop + i7-11800H, Linux, v2.0.11: in Hyrule Field the replay waits 2-3.5 ms per submit in `take_capture` for late write-back pages, ~20 ms of "memory sync" a frame. `RX_VK_CAPTURE_WAIT=0` removes the wait but brings the exposure flashes back.** **Setup** - Hookshot v2.0.11 (`source 10f983ad`, first line of the log), built with the Linux AppImage. - GPU: RTX 3070 Laptop 8 GB, NVIDIA 620.70.06. The game runs on it (`gpu_device=2`), and the external 2560x1440 120 Hz monitor is wired to it. - CPU: i7-11800H (8 cores / 16 threads, all the same kind, no E-cores). It reaches 95-100 °C in play and the kernel's thermal throttle counters climb, so some of the CPU time below is a laptop running hot. - RAM: 32 GB. OS: CachyOS, kernel 7.3.3, GNOME 51 on Wayland. - Changes from the defaults (`hookshot_options.ini`): `fullscreen=1`, `gpu_device=2`, `frame_generation=2`, `frame_pacing=1`, `upscaling=1`, `sharpness=30`, `handheld_profile=1`, `robust_buffers=2`. In the screenshots the overlay shows a 1706x960 game image and a 1600x900 (83%) 3D scene. - Hooked in: MangoHud (a Vulkan layer) was loaded in runs A and C below. Run B had no overlay. All runs went through CachyOS's `game-performance` wrapper, which only switches the power profile to performance. **Where:** Hyrule Field, walking and turning the camera in front of Hyrule Castle (young Link, just after the Deku Tree). Every time. Run B also went into Castle Town Market, which drops the same way. **What the log shows.** The numbers are the medians of the log's per-interval lines (`a frame's replay time`, `GPU time`, `a submit's memory work`) over the intervals with 1000 or more draws (1500 or more for B and C). | Run (attached log) | `hookshot_env.txt` | Overlay | game fps | submit | draw | programs | memory sync | capture taken per submit | GPU time | |---|---|---|---|---|---|---|---|---|---| | A: Field only (26 intervals) | none | MangoHud | 21.4 (min 15.4) | 41.0 ms | 15.1 | 5.8 | **20.4** | **2.2 ms** (max 3.6) | 21.4 ms | | B: Field + Market (22) | `RX_VK_MIRROR_MB=3072` | none | 29.8 (min 12.4) | 29.9 ms | 13.4 | 5.1 | **13.0** | **2.0 ms** | 16.6 ms | | C: Field (5, ~2900 draws) | `RX_VK_CAPTURE_WAIT=0` | MangoHud | 26.5 (min 23.4) | 32.5 ms | 23.8 | 11.5 | **1.4** | **0.1 ms** | 12.8 ms | For comparison, earlier in run A (Kokiri Forest, around 670 draws): 50 fps, submit 12.8 ms, memory sync 1.5 ms, capture 0.1 ms per submit. 1. **The jump comes from the capture's late wait, not from the mirror budget.** Run A's memory sync goes from 2-3 ms to 10-36 ms the moment the Field loads, while the MB synced and written back per frame stay the same (about 11 MB synced and 67 MB written back). Over the same stretch, `capture taken` goes from 0.3-0.5 ms to 1.5-3.6 ms per submit, at 6 submits a frame. The mirrors stay at 1.4-1.76 GB, under the 2048 MB an 8 GB card gets. Raising `RX_VK_MIRROR_MB` to 3072 in run B changed nothing: the mirrors stayed at about 1.5 GB and the wait was still there. So this does not look like the small-mirror-budget case @Nitnek measured above. 2. **`RX_VK_CAPTURE_WAIT=0` confirms it, and confirms why the wait is there.** Memory sync falls to 1.4 ms and capture to 0.1 ms per submit, but the brightness flashes all the time while walking and turning the camera. That looks like the exposure-meter flashes the comment in `capture_make` describes (#1005). I went back to the default. 3. **Even without the wait, the Field is not at 60.** At about 2900 draws, recording takes about 24 ms on its own (programs 11.5 ms, descriptors 5.5 ms), so draw recording is the next limit on this CPU. 4. The in-game overlay in the screenshots (lighter moments, 37-43 fps): GPU 75-80 %, GPU busy 6.7-6.8 ms a frame, frame prep 13.5-14.8 ms. VRAM 6.1-7.3 GB of the 7.46-7.55 GB allowed, and RAM 22-26 GB of 31 GB in use, of which the game's working set is about 16 GB. **Possible direction (not tried):** the late wait seems to matter only for the exposure-meter page(s). If the replay waited for those pages alone, or once a frame instead of at every submit's capture, the Field could keep most of run C's memory-sync gain without the flashes. Logs attached: run A (`hookshot_log.2.txt` at the time), run B (`hookshot_log.1.txt`), run C (`hookshot_log.txt`). No game files. *I ran the game and the tests.*
lucasmazzoni45-del commented 2026-11-09 23:33:55 +00:00 (Migrated from github.com)

Thanks @Nitnek for the numbers. Tomorrow I'll run as many tests as I can on
my RX 7800 XT + 5800X3D: a stock A/B with and without HOS_PIN_RENDER=0 in
the same spot (without my patched hookshot_vk.dll), and I'll try to find
the cause of the CPU-bound FPS drops and the stutter. I'll post logs here.

On Mon, Nov 09, 2026 11:33 PM, Felipe Ferraz Lima @.***>
wrote:

felipefl142 left a comment (NotRexed/Hookshot#995)
https://git.magicbean.dev/MagicBeanDev/Hookshot/issues/995

RTX 3070 Laptop + i7-11800H, Linux, v2.0.11: in Hyrule Field the replay
waits 2-3.5 ms per submit in take_capture for late write-back pages, ~20 ms
of "memory sync" a frame. RX_VK_CAPTURE_WAIT=0 removes the wait but brings
the exposure flashes back.

Setup

  • Hookshot v2.0.11 (source 10f983ad, first line of the log), built
    with the Linux AppImage.
  • GPU: RTX 3070 Laptop 8 GB, NVIDIA 620.70.06. The game runs on it (
    gpu_device=2), and the external 2560x1440 120 Hz monitor is wired to
    it.
  • CPU: i7-11800H (8 cores / 16 threads, all the same kind, no
    E-cores). It reaches 95-100 °C in play and the kernel's thermal throttle
    counters climb, so some of the CPU time below is a laptop running hot.
  • RAM: 32 GB. OS: CachyOS, kernel 7.3.3, GNOME 51 on Wayland.
  • Changes from the defaults (hookshot_options.ini): fullscreen=1,
    gpu_device=2, frame_generation=2, frame_pacing=1, upscaling=1,
    sharpness=30, handheld_profile=1, robust_buffers=2. In the screenshots
    the overlay shows a 1706x960 game image and a 1600x900 (83%) 3D scene.
  • Hooked in: MangoHud (a Vulkan layer) was loaded in runs A and C
    below. Run B had no overlay. All runs went through CachyOS's
    game-performance wrapper, which only switches the power profile to
    performance.

Where: Hyrule Field, walking and turning the camera in front of Hyrule
Castle (young Link, just after the Deku Tree). Every time. Run B also went
into Castle Town Market, which drops the same way.

What the log shows. The numbers are the medians of the log's
per-interval lines (a frame's replay time, GPU time, a submit's memory
work) over the intervals with 1000 or more draws (1500 or more for B and
C).
Run (attached log) hookshot_env.txt Overlay game fps submit draw programs memory
sync capture taken per submit GPU time
A: Field only (26 intervals) none MangoHud 21.4 (min 15.4) 41.0 ms 15.1
5.8 20.4 2.2 ms (max 3.6) 21.4 ms
B: Field + Market (22) RX_VK_MIRROR_MB=3072 none 29.8 (min 12.4) 29.9 ms
13.4 5.1 13.0 2.0 ms 16.6 ms
C: Field (5, ~2900 draws) RX_VK_CAPTURE_WAIT=0 MangoHud 26.5 (min 23.4) 32.5
ms 23.8 11.5 1.4 0.1 ms 12.8 ms

For comparison, earlier in run A (Kokiri Forest, around 670 draws): 50
fps, submit 12.8 ms, memory sync 1.5 ms, capture 0.1 ms per submit.

  1. The jump comes from the capture's late wait, not from the mirror
    budget.
    Run A's memory sync goes from 2-3 ms to 10-36 ms the moment
    the Field loads, while the MB synced and written back per frame stay the
    same (about 11 MB synced and 67 MB written back). Over the same stretch, capture
    taken goes from 0.3-0.5 ms to 1.5-3.6 ms per submit, at 6 submits a
    frame. The mirrors stay at 1.4-1.76 GB, under the 2048 MB an 8 GB card
    gets. Raising RX_VK_MIRROR_MB to 3072 in run B changed nothing: the
    mirrors stayed at about 1.5 GB and the wait was still there. So this does
    not look like the small-mirror-budget case @Nitnek
    https://github.com/Nitnek measured above.
  2. RX_VK_CAPTURE_WAIT=0 confirms it, and confirms why the wait is
    there.
    Memory sync falls to 1.4 ms and capture to 0.1 ms per submit,
    but the brightness flashes all the time while walking and turning the
    camera. That looks like the exposure-meter flashes the comment in
    capture_make describes (#1005
    https://git.magicbean.dev/MagicBeanDev/Hookshot/issues/1005). I went back to
    the default.
  3. Even without the wait, the Field is not at 60. At about 2900
    draws, recording takes about 24 ms on its own (programs 11.5 ms,
    descriptors 5.5 ms), so draw recording is the next limit on this CPU.
  4. The in-game overlay in the screenshots (lighter moments, 37-43
    fps): GPU 75-80 %, GPU busy 6.7-6.8 ms a frame, frame prep 13.5-14.8 ms.
    VRAM 6.1-7.3 GB of the 7.46-7.55 GB allowed, and RAM 22-26 GB of 31 GB in
    use, of which the game's working set is about 16 GB.

Possible direction (not tried): the late wait seems to matter only for
the exposure-meter page(s). If the replay waited for those pages alone, or
once a frame instead of at every submit's capture, the Field could keep
most of run C's memory-sync gain without the flashes.

Logs attached: run A (hookshot_log.2.txt at the time), run B (
hookshot_log.1.txt), run C (hookshot_log.txt). No game files.

I ran the game and the tests. I read the logs and the source with help
from an AI assistant (Claude).

—
Reply to this email directly, view it on GitHub
https://git.magicbean.dev/MagicBeanDev/Hookshot/issues/995?email_source=notifications&email_token=BXKPXOUMMTINZX26FKPXOOD5YJA3LA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTMNZVGMZDMMJXGI32M4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLDGN5XXIZLSL5RWY2LDNM#issuecomment-6753261727,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/BXKPXOQOPRWF4M27U3YSZT35YJA3LAVCNFSNUABGKJSXA33TNF2G64TZHMYTINBWHE3DENJSGA5US43TOVSTWNRSHAYDMMRWG43DPILWAI
.
You are receiving this because you were mentioned.Message ID:
@.***>

Thanks @Nitnek for the numbers. Tomorrow I'll run as many tests as I can on my RX 7800 XT + 5800X3D: a stock A/B with and without `HOS_PIN_RENDER=0` in the same spot (without my patched `hookshot_vk.dll`), and I'll try to find the cause of the CPU-bound FPS drops and the stutter. I'll post logs here. On Mon, Nov 09, 2026 11:33 PM, Felipe Ferraz Lima ***@***.***> wrote: > *felipefl142* left a comment (NotRexed/Hookshot#995) > <https://git.magicbean.dev/MagicBeanDev/Hookshot/issues/995> > > *RTX 3070 Laptop + i7-11800H, Linux, v2.0.11: in Hyrule Field the replay > waits 2-3.5 ms per submit in take_capture for late write-back pages, ~20 ms > of "memory sync" a frame. RX_VK_CAPTURE_WAIT=0 removes the wait but brings > the exposure flashes back.* > > *Setup* > > - Hookshot v2.0.11 (source 10f983ad, first line of the log), built > with the Linux AppImage. > - GPU: RTX 3070 Laptop 8 GB, NVIDIA 620.70.06. The game runs on it ( > gpu_device=2), and the external 2560x1440 120 Hz monitor is wired to > it. > - CPU: i7-11800H (8 cores / 16 threads, all the same kind, no > E-cores). It reaches 95-100 °C in play and the kernel's thermal throttle > counters climb, so some of the CPU time below is a laptop running hot. > - RAM: 32 GB. OS: CachyOS, kernel 7.3.3, GNOME 51 on Wayland. > - Changes from the defaults (hookshot_options.ini): fullscreen=1, > gpu_device=2, frame_generation=2, frame_pacing=1, upscaling=1, > sharpness=30, handheld_profile=1, robust_buffers=2. In the screenshots > the overlay shows a 1706x960 game image and a 1600x900 (83%) 3D scene. > - Hooked in: MangoHud (a Vulkan layer) was loaded in runs A and C > below. Run B had no overlay. All runs went through CachyOS's > game-performance wrapper, which only switches the power profile to > performance. > > *Where:* Hyrule Field, walking and turning the camera in front of Hyrule > Castle (young Link, just after the Deku Tree). Every time. Run B also went > into Castle Town Market, which drops the same way. > > *What the log shows.* The numbers are the medians of the log's > per-interval lines (a frame's replay time, GPU time, a submit's memory > work) over the intervals with 1000 or more draws (1500 or more for B and > C). > Run (attached log) hookshot_env.txt Overlay game fps submit draw programs memory > sync capture taken per submit GPU time > A: Field only (26 intervals) none MangoHud 21.4 (min 15.4) 41.0 ms 15.1 > 5.8 *20.4* *2.2 ms* (max 3.6) 21.4 ms > B: Field + Market (22) RX_VK_MIRROR_MB=3072 none 29.8 (min 12.4) 29.9 ms > 13.4 5.1 *13.0* *2.0 ms* 16.6 ms > C: Field (5, ~2900 draws) RX_VK_CAPTURE_WAIT=0 MangoHud 26.5 (min 23.4) 32.5 > ms 23.8 11.5 *1.4* *0.1 ms* 12.8 ms > > For comparison, earlier in run A (Kokiri Forest, around 670 draws): 50 > fps, submit 12.8 ms, memory sync 1.5 ms, capture 0.1 ms per submit. > > 1. *The jump comes from the capture's late wait, not from the mirror > budget.* Run A's memory sync goes from 2-3 ms to 10-36 ms the moment > the Field loads, while the MB synced and written back per frame stay the > same (about 11 MB synced and 67 MB written back). Over the same stretch, capture > taken goes from 0.3-0.5 ms to 1.5-3.6 ms per submit, at 6 submits a > frame. The mirrors stay at 1.4-1.76 GB, under the 2048 MB an 8 GB card > gets. Raising RX_VK_MIRROR_MB to 3072 in run B changed nothing: the > mirrors stayed at about 1.5 GB and the wait was still there. So this does > not look like the small-mirror-budget case @Nitnek > <https://github.com/Nitnek> measured above. > 2. *RX_VK_CAPTURE_WAIT=0 confirms it, and confirms why the wait is > there.* Memory sync falls to 1.4 ms and capture to 0.1 ms per submit, > but the brightness flashes all the time while walking and turning the > camera. That looks like the exposure-meter flashes the comment in > capture_make describes (#1005 > <https://git.magicbean.dev/MagicBeanDev/Hookshot/issues/1005>). I went back to > the default. > 3. *Even without the wait, the Field is not at 60.* At about 2900 > draws, recording takes about 24 ms on its own (programs 11.5 ms, > descriptors 5.5 ms), so draw recording is the next limit on this CPU. > 4. The in-game overlay in the screenshots (lighter moments, 37-43 > fps): GPU 75-80 %, GPU busy 6.7-6.8 ms a frame, frame prep 13.5-14.8 ms. > VRAM 6.1-7.3 GB of the 7.46-7.55 GB allowed, and RAM 22-26 GB of 31 GB in > use, of which the game's working set is about 16 GB. > > *Possible direction (not tried):* the late wait seems to matter only for > the exposure-meter page(s). If the replay waited for those pages alone, or > once a frame instead of at every submit's capture, the Field could keep > most of run C's memory-sync gain without the flashes. > > Logs attached: run A (hookshot_log.2.txt at the time), run B ( > hookshot_log.1.txt), run C (hookshot_log.txt). No game files. > > *I ran the game and the tests. I read the logs and the source with help > from an AI assistant (Claude).* > > — > Reply to this email directly, view it on GitHub > <https://git.magicbean.dev/MagicBeanDev/Hookshot/issues/995?email_source=notifications&email_token=BXKPXOUMMTINZX26FKPXOOD5YJA3LA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTMNZVGMZDMMJXGI32M4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLDGN5XXIZLSL5RWY2LDNM#issuecomment-6753261727>, > or unsubscribe > <https://github.com/notifications/unsubscribe-auth/BXKPXOQOPRWF4M27U3YSZT35YJA3LAVCNFSNUABGKJSXA33TNF2G64TZHMYTINBWHE3DENJSGA5US43TOVSTWNRSHAYDMMRWG43DPILWAI> > . > You are receiving this because you were mentioned.Message ID: > ***@***.***> >
Zarigis commented 2026-11-09 23:47:35 +00:00 (Migrated from github.com)

RTX 4080 SUPER + i9-14900KF, Windows, 4K: the same take_capture wait in Hyrule Field, and what the late pages are. RX_VK_CAPTURE_WAIT=0 takes the field from 52.5 to 59.9 fps here.

This matches @felipefl142's report above, on a faster machine, with some detail on which pages the capture waits for.

Setup

  • RTX 4080 SUPER 16 GB (the log's driver code is 0x9a4a8000, which I read as 617.42), i9-14900KF (8P + 16E), Windows 11.
  • Output 3840x2160, resolution 100%, the game's own upscaler, frame generation off (RX_VK_FRAMEGEN=0), frame pacing off.
  • Source: main at 7dbdcdc with #1158 applied (before it merged), plus a local, unpublished change that reuses render-target images. That change only affects the camera spin.
  • Scene: a save at the Hyrule Field overlook with the castle in view. Scripted: 15 s standing, then 15 s spinning the camera. One run per row; repeat runs in this scene differ by under 1 fps.

Result

Setting Standing fps Spin fps Memory sync Submit
default 52.5 56.2 4.76 ms 16.5 ms
RX_VK_CAPTURE_WAIT=0 59.9 59.8 0.45 ms 12.3 ms

GPU time is about 12.4 ms a frame in both, so the default is not GPU-bound. I did not check for exposure flashes with the wait off. @felipefl142 saw them while walking, and I only stood and spun.

What the wait is here

  • About 4.3 ms of the 4.76 ms is one GPU wait a frame: vk_timeline_wait(c->late_value) at the top of take_capture. 100 of 600 captures wait, 3.1-4.7 ms each.
  • It waits for the newest submission still on the GPU (4 timeline values ahead), so the whole queue drains.
  • The rest of memory sync is about 0.45 ms. Copying, hashing and the write-back diff cost almost nothing.
  • Kokiri Forest on the same build syncs more data (13.9 MB and 82k dirty pages per 600 submits, against 11.2 MB and 49k in the field) and pays no wait: memory sync 0.6 ms. Its longest submission is 2.6 ms against 5-6 ms in the field, so the GPU is already done when the capture is taken.

What the late pages are

With temporary logging, I compared each waiting capture's copy with guest memory after the wait:

  • A waiting capture has about 116 late pages.
  • Only one word on one page differed: the NVN event word that the stream's SIGNAL_EVENT writes through gpu_write. The replay already tracks that value on the CPU.
  • The other ~115 pages are 4-8 KB runs in two pools. They did not change at all in this window.
  • Exempting either group alone changed nothing: the other group still forces the full wait.

This was 15 s of standing and spinning, with no walking. The exposure-meter page must be among the late pages while moving, given the flashes reported above, so "did not change" here is not a safe rule on its own.

Tried and dropped

  • Putting the wait off until just before vkQueueSubmit: worse, 48.5 / 53.0 fps. The awaited submission is still running when recording ends, so the wait only moves, and GPU time rose to 14.4 ms.

Hybrid CPU data point for #1158 (same scene, 30 s phases, before the capture work)

Setting Standing fps Spin fps
main at 7dbdcdc, default (Presentation Thread on logical processor 31, an E-core) 43.4 50.5
HOS_PIN_RENDER=0 51.5 55.7
#1158 (replays on logical processors 0xc000, the last P-core) 53.7 55.6

Direction I'm trying: note a page as late only when its write-back can change bytes the capture copies, decided by the kind of write (event signals and writes whose bytes the replay already knows are out; the exposure meter's storage write stays in). I'll check it for flashes while walking in the field, with RX_VK_CAPTURE_WAIT=0 as the control, and post the result. If someone is already working on this, say so and I'll send what I have.

The runs were scripted and the analysis was done with Claude Code. Logs available on request.

**RTX 4080 SUPER + i9-14900KF, Windows, 4K: the same `take_capture` wait in Hyrule Field, and what the late pages are. `RX_VK_CAPTURE_WAIT=0` takes the field from 52.5 to 59.9 fps here.** This matches @felipefl142's report above, on a faster machine, with some detail on which pages the capture waits for. **Setup** - RTX 4080 SUPER 16 GB (the log's driver code is `0x9a4a8000`, which I read as 617.42), i9-14900KF (8P + 16E), Windows 11. - Output 3840x2160, resolution 100%, the game's own upscaler, frame generation off (`RX_VK_FRAMEGEN=0`), frame pacing off. - Source: main at 7dbdcdc with #1158 applied (before it merged), plus a local, unpublished change that reuses render-target images. That change only affects the camera spin. - Scene: a save at the Hyrule Field overlook with the castle in view. Scripted: 15 s standing, then 15 s spinning the camera. One run per row; repeat runs in this scene differ by under 1 fps. **Result** | Setting | Standing fps | Spin fps | Memory sync | Submit | |---|---|---|---|---| | default | 52.5 | 56.2 | 4.76 ms | 16.5 ms | | `RX_VK_CAPTURE_WAIT=0` | 59.9 | 59.8 | 0.45 ms | 12.3 ms | GPU time is about 12.4 ms a frame in both, so the default is not GPU-bound. I did not check for exposure flashes with the wait off. @felipefl142 saw them while walking, and I only stood and spun. **What the wait is here** - About 4.3 ms of the 4.76 ms is one GPU wait a frame: `vk_timeline_wait(c->late_value)` at the top of `take_capture`. 100 of 600 captures wait, 3.1-4.7 ms each. - It waits for the newest submission still on the GPU (4 timeline values ahead), so the whole queue drains. - The rest of memory sync is about 0.45 ms. Copying, hashing and the write-back diff cost almost nothing. - Kokiri Forest on the same build syncs more data (13.9 MB and 82k dirty pages per 600 submits, against 11.2 MB and 49k in the field) and pays no wait: memory sync 0.6 ms. Its longest submission is 2.6 ms against 5-6 ms in the field, so the GPU is already done when the capture is taken. **What the late pages are** With temporary logging, I compared each waiting capture's copy with guest memory after the wait: - A waiting capture has about 116 late pages. - Only one word on one page differed: the NVN event word that the stream's `SIGNAL_EVENT` writes through `gpu_write`. The replay already tracks that value on the CPU. - The other ~115 pages are 4-8 KB runs in two pools. They did not change at all in this window. - Exempting either group alone changed nothing: the other group still forces the full wait. This was 15 s of standing and spinning, with no walking. The exposure-meter page must be among the late pages while moving, given the flashes reported above, so "did not change" here is not a safe rule on its own. **Tried and dropped** - Putting the wait off until just before `vkQueueSubmit`: worse, 48.5 / 53.0 fps. The awaited submission is still running when recording ends, so the wait only moves, and GPU time rose to 14.4 ms. **Hybrid CPU data point for #1158** (same scene, 30 s phases, before the capture work) | Setting | Standing fps | Spin fps | |---|---|---| | main at 7dbdcdc, default (Presentation Thread on logical processor 31, an E-core) | 43.4 | 50.5 | | `HOS_PIN_RENDER=0` | 51.5 | 55.7 | | #1158 (replays on logical processors 0xc000, the last P-core) | 53.7 | 55.6 | **Direction I'm trying:** note a page as late only when its write-back can change bytes the capture copies, decided by the kind of write (event signals and writes whose bytes the replay already knows are out; the exposure meter's storage write stays in). I'll check it for flashes while walking in the field, with `RX_VK_CAPTURE_WAIT=0` as the control, and post the result. If someone is already working on this, say so and I'll send what I have. *The runs were scripted and the analysis was done with Claude Code. Logs available on request.*
Zarigis commented 2026-11-10 00:02:36 +00:00 (Migrated from github.com)

Correction to my comment above: most of that wait came from the tree bug (#994), not from the capture.

I rebuilt on main at 581afaf. Same machine, scene and settings:

Standing fps Spin fps GPU time Memory sync Late-page wait per capture
7dbdcdc + #1158 (my numbers above) 52.5 56.2 12.4 ms 4.76 ms 3.1-4.7 ms, 100 of 600
581afaf 59.7 59.3 9.7-10.2 ms 1.2-1.7 ms 0.01-0.27 ms, 8-100 of 600
  • The earlier build drew the wall of trees over the castle hill on this NVIDIA card. On 581afaf it is gone, and I think #994's fix is what cut the GPU time, though I did not bisect. With the GPU done sooner, the capture rarely finds a write-back still pending.
  • RX_VK_CAPTURE_WAIT=0 now gains about 1 fps in the spin here, within noise. Walking toward the castle holds 59.7 fps too.
  • The wait is still about 2 ms a frame at the overlook. It fits in the frame on a 4080 SUPER, but it would not on a slower GPU, so @felipefl142's case may still stand: worth re-measuring on a build with #994's fix.
  • What I found about the late pages (one event word changes, about 115 pages don't, while standing) was measured on the old build and I have not repeated it.
  • I am not pursuing the narrow fix for now. I have a draft of the rule (patch late pages from the stream's own gpu_write bytes; wait only for pages that shaders, copies or clears write back), unbuilt and untested, if it helps anyone.

The runs were scripted and the analysis was done with Claude Code.

**Correction to my comment above: most of that wait came from the tree bug (#994), not from the capture.** I rebuilt on main at 581afaf. Same machine, scene and settings: | | Standing fps | Spin fps | GPU time | Memory sync | Late-page wait per capture | |---|---|---|---|---|---| | 7dbdcdc + #1158 (my numbers above) | 52.5 | 56.2 | 12.4 ms | 4.76 ms | 3.1-4.7 ms, 100 of 600 | | 581afaf | 59.7 | 59.3 | 9.7-10.2 ms | 1.2-1.7 ms | 0.01-0.27 ms, 8-100 of 600 | - The earlier build drew the wall of trees over the castle hill on this NVIDIA card. On 581afaf it is gone, and I think #994's fix is what cut the GPU time, though I did not bisect. With the GPU done sooner, the capture rarely finds a write-back still pending. - `RX_VK_CAPTURE_WAIT=0` now gains about 1 fps in the spin here, within noise. Walking toward the castle holds 59.7 fps too. - The wait is still about 2 ms a frame at the overlook. It fits in the frame on a 4080 SUPER, but it would not on a slower GPU, so @felipefl142's case may still stand: worth re-measuring on a build with #994's fix. - What I found about the late pages (one event word changes, about 115 pages don't, while standing) was measured on the old build and I have not repeated it. - I am not pursuing the narrow fix for now. I have a draft of the rule (patch late pages from the stream's own `gpu_write` bytes; wait only for pages that shaders, copies or clears write back), unbuilt and untested, if it helps anyone. *The runs were scripted and the analysis was done with Claude Code.*
felipefl142 commented 2026-11-10 00:08:45 +00:00 (Migrated from github.com)

run-D_castle-market-castle_hookshot_log.txt

Follow-up (RTX 3070 Laptop, v2.0.11): at Hyrule Castle the wait appears only after a visit to Castle Town Market, and stays. Same spot: 37 fps fresh, 25 fps after the Market.

@Zarigis thanks for the correction. These runs are on v2.0.11, without the #994 fix, and the floating trees are on screen at the castle, so this may have the same cause. I will measure again on the first release with the fix.

Setup: as in my comment above, with MangoHud loaded. hookshot_env.txt had RX_VK_VRAM_MB=4096 and RX_VK_IMAGE_BLOCK_MB=64 (I was testing VRAM use). One launch: Hyrule Castle grounds, then the Market, then back to the castle grounds.

Result (medians of the log's per-interval lines)

Part of the run Intervals (draws) game fps submit draw memory sync capture taken per submit GPU time
Castle, fresh 4 (946-1519) 37.4 22.2 ms 12.3 6.4 0.04-1.5 ms 17.2 ms
Market 3 (1471-2242) 23.2 33.3 ms 22.7 6.4 0.2-1.8 ms 9.9 ms
Castle, after the Market 5 (1266-1970) 24.7 (min 18.1) 36.1 ms 15.4 18.5 (max 29.4) 2.0-5.0 ms 20.7 ms
  • Back at the castle, the capture wait and memory sync are 3x what they were at the start, and GPU time is about 3.5 ms higher. That fits your reading: the GPU is busier, so the capture finds a write-back still pending. The draw counts are a bit higher on the way back, so the two castle rows are not an exact match.
  • VRAM after the Market: 6.9-7.4 GB used of 7.68 GB allowed, with 0.8-1.0 GB of it free inside VMA's blocks. Before the Market it was 5.3 GB. The mirrors went from 1200 MB to 1968 MB, close to the 2048 MB budget on an 8 GB card.
  • Restarting the game and loading the castle again brings back the fresh numbers.

Log attached. No game files.

[run-D_castle-market-castle_hookshot_log.txt](https://github.com/user-attachments/files/34605267/run-D_castle-market-castle_hookshot_log.txt) **Follow-up (RTX 3070 Laptop, v2.0.11): at Hyrule Castle the wait appears only after a visit to Castle Town Market, and stays. Same spot: 37 fps fresh, 25 fps after the Market.** @Zarigis thanks for the correction. These runs are on v2.0.11, without the #994 fix, and the floating trees are on screen at the castle, so this may have the same cause. I will measure again on the first release with the fix. **Setup:** as in my comment above, with MangoHud loaded. `hookshot_env.txt` had `RX_VK_VRAM_MB=4096` and `RX_VK_IMAGE_BLOCK_MB=64` (I was testing VRAM use). One launch: Hyrule Castle grounds, then the Market, then back to the castle grounds. **Result** (medians of the log's per-interval lines) | Part of the run | Intervals (draws) | game fps | submit | draw | memory sync | capture taken per submit | GPU time | |---|---|---|---|---|---|---|---| | Castle, fresh | 4 (946-1519) | 37.4 | 22.2 ms | 12.3 | 6.4 | 0.04-1.5 ms | 17.2 ms | | Market | 3 (1471-2242) | 23.2 | 33.3 ms | 22.7 | 6.4 | 0.2-1.8 ms | 9.9 ms | | Castle, after the Market | 5 (1266-1970) | **24.7** (min 18.1) | 36.1 ms | 15.4 | **18.5** (max 29.4) | **2.0-5.0 ms** | 20.7 ms | - Back at the castle, the capture wait and memory sync are 3x what they were at the start, and GPU time is about 3.5 ms higher. That fits your reading: the GPU is busier, so the capture finds a write-back still pending. The draw counts are a bit higher on the way back, so the two castle rows are not an exact match. - VRAM after the Market: 6.9-7.4 GB used of 7.68 GB allowed, with 0.8-1.0 GB of it free inside VMA's blocks. Before the Market it was 5.3 GB. The mirrors went from 1200 MB to 1968 MB, close to the 2048 MB budget on an 8 GB card. - Restarting the game and loading the castle again brings back the fresh numbers. Log attached. No game files.
bunbeans2-arch commented 2026-11-10 00:13:12 +00:00 (Migrated from github.com)

RX 9070 XT + i9-9900K: measured A/B results for a few switches, plus where the CPU time goes in a heavy frame 📊

Adding to DeAuthenticated's RX 9070 XT numbers above, from a different CPU. Everything below was run (scripted input, same save, alternating arms), not estimated, unless it says otherwise.

Setup: RX 9070 XT, i9-9900K (8c/16t), 32 GB DDR4-3200, Windows 11, Hookshot v2.0.9, fullscreen 4K TV. Base env for all arms: RX_VK_WB_DIFF=1, RX_VK_PRESENT_QUEUE=1 (no crashes on AMD across ~70 runs today, matching the comment above).

1. RX_VK_PREFETCH_CHUNKS=2 helps on this card (8 runs, 4 on / 4 off)

The comment near RX_VK_PREFETCH_CHUNKS in vk_memory.cpp says import-ahead hurt on AMD. On RDNA 4 it was the other way around. Same 127 s movement recording (running around Kokiri), output 2048x1152:

off (0) on (2)
avg fps 57.67 58.22
1% low fps 30.9 35.9
frames over 25 ms 56.0 28.5
host-memory import time in the window 126 ms 65 ms
total time in hitches over 50 ms 551 ms 209 ms

The fps gain is small (t ≈ 2.4), the stutter drop is the useful part. Not checked: other AMD generations, or any picture difference (it only moves when imports happen).

2. RX_VK_CHUNK=480 (default 120)

Standing in Kokiri, ~54 fps with 480 vs ~51 with the default. 960 and up, and 0, were slower than 480.

3. The profile-guided Vulkan build (RX_VK_PGO, #965) is worth it on AMD too

A local PGO build of hookshot_vk.dll, profiled on our own movement recording: +3.6 fps standing. Long-run A/B over 2 rounds of the same recording: PGO build + the switches above 57.55 / 58.37 fps vs stock DLL with no env 54.82 / 55.14.

4. Resolution 75% at 4K output: +10 fps here, because at 100% the GPU becomes the limit

Output 3840x2160, standing in Kokiri, ABBA:

Resolution row runs avg GPU time a frame
100% (3840x2160) 49.72 / 49.99 49.86 14.65 ms
75% (2880x1620) 59.93 / 60.00 59.97 (cap) 9.45 ms

So "lowering resolution doesn't help" in the summary is true when the CPU is the limit, but at 4K / 100% on this card it isn't. In a heavier spot (3,000-3,900 draws a frame) 100% gave 34 fps with ~20.5 ms of GPU time a frame.

5. Where the CPU time goes in a heavy frame (log analysis)

From a timer build (thread CPU + import time around the slow-submit points), 87 five-second windows grouped by draws a frame. Done by a second agent over our logs, so the fits are analysis, not a separate run:

Phase draws presentation-thread lock chain frame ceiling if the 60 cap were gone
heavy 2435 16.46 ms 17.43 ms ~60.8 fps
mid 1346 11.85 ms 17.23 ms ~84.4 fps
light 676 9.02 ms 16.70 ms ~110.9 fps

Fit: lock hold ≈ 5.55 ms + 4.25 µs × draws (r² 0.94). So in heavy views, the per-draw cost is what has to shrink before raising the frame rate (#1367) pays off. The two biggest per-draw costs we saw in the counters:

  • Program picks: 31.9% of 16 M picks take the "with other textures" path in program_for, because only one validated texture-handle set is remembered per key. Remembering a few sets per key would make most of those fast hits.
  • Descriptors: only 3.0% of draws in a heavy frame reuse the neighbour's descriptor set, because a uniform buffer is bound per draw. Caching the texture writes per program and rebuilding only the uniform ranges looks like the bigger win, but we haven't sized it yet (RX_VK_PROF_FINE run pending).

6. Hitches during real play are first-time costs

A 13-minute hand-played session (warm caches): every hitch the player called out was the first time something appeared (a new shader translated in play, a large first host-memory import, or both). None repeated in the same spot. At a new output resolution, ~10 compute pipelines were built in play on the first launch.

7. Tooling note for anyone profiling

A Windows WPR CPU capture (wpr -start CPU) started during movement froze the game for 9-21 s in 3 of 3 runs. Standing captures were fine. Use a timer build or the present trace for movement.

Happy to run any A/B someone wants checked on this card. 🙂


AI-assisted: yes (Claude Code)

**RX 9070 XT + i9-9900K: measured A/B results for a few switches, plus where the CPU time goes in a heavy frame** 📊 Adding to DeAuthenticated's RX 9070 XT numbers above, from a different CPU. Everything below was **run** (scripted input, same save, alternating arms), not estimated, unless it says otherwise. **Setup:** RX 9070 XT, i9-9900K (8c/16t), 32 GB DDR4-3200, Windows 11, Hookshot **v2.0.9**, fullscreen 4K TV. Base env for all arms: `RX_VK_WB_DIFF=1`, `RX_VK_PRESENT_QUEUE=1` (no crashes on AMD across ~70 runs today, matching the comment above). ### 1. `RX_VK_PREFETCH_CHUNKS=2` helps on this card (8 runs, 4 on / 4 off) The comment near `RX_VK_PREFETCH_CHUNKS` in `vk_memory.cpp` says import-ahead hurt on AMD. On RDNA 4 it was the other way around. Same 127 s movement recording (running around Kokiri), output 2048x1152: | | off (0) | on (2) | |---|---|---| | avg fps | 57.67 | 58.22 | | 1% low fps | 30.9 | 35.9 | | frames over 25 ms | 56.0 | 28.5 | | host-memory import time in the window | 126 ms | 65 ms | | total time in hitches over 50 ms | 551 ms | 209 ms | The fps gain is small (t ≈ 2.4), the stutter drop is the useful part. Not checked: other AMD generations, or any picture difference (it only moves when imports happen). ### 2. `RX_VK_CHUNK=480` (default 120) Standing in Kokiri, ~54 fps with 480 vs ~51 with the default. 960 and up, and 0, were **slower** than 480. ### 3. The profile-guided Vulkan build (`RX_VK_PGO`, #965) is worth it on AMD too A local PGO build of `hookshot_vk.dll`, profiled on our own movement recording: **+3.6 fps** standing. Long-run A/B over 2 rounds of the same recording: PGO build + the switches above **57.55 / 58.37 fps** vs stock DLL with no env **54.82 / 55.14**. ### 4. Resolution 75% at 4K output: +10 fps here, because at 100% the GPU becomes the limit Output 3840x2160, standing in Kokiri, ABBA: | Resolution row | runs | avg | GPU time a frame | |---|---|---|---| | 100% (3840x2160) | 49.72 / 49.99 | 49.86 | 14.65 ms | | 75% (2880x1620) | 59.93 / 60.00 | 59.97 (cap) | 9.45 ms | So "lowering resolution doesn't help" in the summary is true when the CPU is the limit, but at 4K / 100% on this card it isn't. In a heavier spot (3,000-3,900 draws a frame) 100% gave 34 fps with ~20.5 ms of GPU time a frame. ### 5. Where the CPU time goes in a heavy frame (log analysis) From a timer build (thread CPU + import time around the slow-submit points), 87 five-second windows grouped by draws a frame. Done by a second agent over our logs, so the fits are analysis, not a separate run: | Phase | draws | presentation-thread lock chain | frame | ceiling if the 60 cap were gone | |---|---|---|---|---| | heavy | 2435 | 16.46 ms | 17.43 ms | ~60.8 fps | | mid | 1346 | 11.85 ms | 17.23 ms | ~84.4 fps | | light | 676 | 9.02 ms | 16.70 ms | ~110.9 fps | Fit: lock hold ≈ 5.55 ms + 4.25 µs × draws (r² 0.94). So in heavy views, the per-draw cost is what has to shrink before raising the frame rate (#1367) pays off. The two biggest per-draw costs we saw in the counters: - **Program picks:** 31.9% of 16 M picks take the "with other textures" path in `program_for`, because only one validated texture-handle set is remembered per key. Remembering a few sets per key would make most of those fast hits. - **Descriptors:** only 3.0% of draws in a heavy frame reuse the neighbour's descriptor set, because a uniform buffer is bound per draw. Caching the texture writes per program and rebuilding only the uniform ranges looks like the bigger win, but we haven't sized it yet (`RX_VK_PROF_FINE` run pending). ### 6. Hitches during real play are first-time costs A 13-minute hand-played session (warm caches): every hitch the player called out was the **first** time something appeared (a new shader translated in play, a large first host-memory import, or both). None repeated in the same spot. At a new output resolution, ~10 compute pipelines were built in play on the first launch. ### 7. Tooling note for anyone profiling A Windows WPR CPU capture (`wpr -start CPU`) started during movement froze the game for 9-21 s in 3 of 3 runs. Standing captures were fine. Use a timer build or the present trace for movement. Happy to run any A/B someone wants checked on this card. 🙂 --- AI-assisted: yes (Claude Code)
felipefl142 commented 2026-11-10 01:07:58 +00:00 (Migrated from github.com)

run-E_main_multi-loop_hookshot_log.txt
run-F_main_texcapture0_hookshot_log.txt

Re-measured on main (0828863, with the #994 fix): the late-page wait is mostly gone here too, but on this build a second capture cost grows with every trip between Hyrule Castle and Castle Town Market. RX_VK_TEX_CAPTURE=0 removes it, and I saw no flashes.

@Zarigis as you asked, on a build with #994's fix. The floating trees at the castle are gone.

Setup: same machine as above (RTX 3070 Laptop 8 GB, i7-11800H, Linux). main at 0828863, built locally with the v2.0.11 AppImage's tools (the Vulkan back end built the way scripts/linux/appimage.sh builds it). MangoHud loaded. hookshot_env.txt had RX_VK_VRAM_MB=4096 and RX_VK_IMAGE_BLOCK_MB=64 in both runs, plus RX_VK_TEX_CAPTURE=0 in run F. Route: castle grounds and the Market, back and forth many times in one launch.

1. The late-page wait is small now. The log's GPU wait for late pages is 0-1.3 ms (0-1.6 ms in run F). On v2.0.11 the whole capture took 2-5 ms per submit in the same places, so the #994 fix helps here as it did for you.

2. retires and stamps in a capture taken grows instead (run E, default settings). Second launch in the attached log (line 5521 on; the first launch was a shader warm-up):

Point in the run retires and stamps per capture texture pages copied (per 600 takes) memory sync a frame game fps
Castle, first visit 0.14-0.16 ms ~7,400 3-5 ms 36-42
middle of the run 1.2-2.8 ms 3,500-25,000 15-36 ms 17-28
end of the run 5.0-9.0 ms 16,000-24,000 33-55 ms 12-19
  • The cost per copied texture page went up about 20x: 0.15 ms for about 7,400 pages early on, 6.3-7.9 ms for about 16,300 later. It came back down only when the game was restarted.
  • What the mirror syncs move stayed the same the whole time: about 13 MB synced in about 1,900 copies, and 63 MB written back a frame. GPU time stayed at 12-21 ms and did not grow with it. In the first launch, standing still for about three minutes (about 1,230 draws), memory sync rose from 16 to 28 ms.

3. With RX_VK_TEX_CAPTURE=0 (run F) it stays flat. Over a similar number of trips, retires and stamps was 0.001-0.02 ms and memory sync 1-10 ms. The Market held 23-31 fps on every trip. The castle was 46-57 fps at first and about 35 fps later, when GPU time had risen from about 12 to 20 ms; memory sync stayed low throughout. Walking and turning the camera in both areas, I saw no flashes and no texture glitches.

My guess from reading take_capture in vk_memory.cpp (not verified): once the texture shadow reaches kShadowMax (65536 pages), every capture that copies texture pages walks the whole shadow map looking for pages older than 64 captures. When none can go, it stops, and the next capture walks it again. Each trip to a new area adds texture pages, which would fit a cost that steps up with every trip and stays up until a restart.

Logs attached: run E (two launches in one file; texture capture on) and run F (RX_VK_TEX_CAPTURE=0). No game files.

[run-E_main_multi-loop_hookshot_log.txt](https://github.com/user-attachments/files/34607554/run-E_main_multi-loop_hookshot_log.txt) [run-F_main_texcapture0_hookshot_log.txt](https://github.com/user-attachments/files/34607555/run-F_main_texcapture0_hookshot_log.txt) **Re-measured on main (0828863, with the #994 fix): the late-page wait is mostly gone here too, but on this build a second capture cost grows with every trip between Hyrule Castle and Castle Town Market. `RX_VK_TEX_CAPTURE=0` removes it, and I saw no flashes.** @Zarigis as you asked, on a build with #994's fix. The floating trees at the castle are gone. **Setup:** same machine as above (RTX 3070 Laptop 8 GB, i7-11800H, Linux). main at 0828863, built locally with the v2.0.11 AppImage's tools (the Vulkan back end built the way `scripts/linux/appimage.sh` builds it). MangoHud loaded. `hookshot_env.txt` had `RX_VK_VRAM_MB=4096` and `RX_VK_IMAGE_BLOCK_MB=64` in both runs, plus `RX_VK_TEX_CAPTURE=0` in run F. Route: castle grounds and the Market, back and forth many times in one launch. **1. The late-page wait is small now.** The log's `GPU wait for late pages` is 0-1.3 ms (0-1.6 ms in run F). On v2.0.11 the whole capture took 2-5 ms per submit in the same places, so the #994 fix helps here as it did for you. **2. `retires and stamps` in `a capture taken` grows instead (run E, default settings).** Second launch in the attached log (line 5521 on; the first launch was a shader warm-up): | Point in the run | `retires and stamps` per capture | texture pages copied (per 600 takes) | memory sync a frame | game fps | |---|---|---|---|---| | Castle, first visit | 0.14-0.16 ms | ~7,400 | 3-5 ms | 36-42 | | middle of the run | 1.2-2.8 ms | 3,500-25,000 | 15-36 ms | 17-28 | | end of the run | **5.0-9.0 ms** | 16,000-24,000 | **33-55 ms** | **12-19** | - The cost per copied texture page went up about 20x: 0.15 ms for about 7,400 pages early on, 6.3-7.9 ms for about 16,300 later. It came back down only when the game was restarted. - What the mirror syncs move stayed the same the whole time: about 13 MB synced in about 1,900 copies, and 63 MB written back a frame. GPU time stayed at 12-21 ms and did not grow with it. In the first launch, standing still for about three minutes (about 1,230 draws), memory sync rose from 16 to 28 ms. **3. With `RX_VK_TEX_CAPTURE=0` (run F) it stays flat.** Over a similar number of trips, `retires and stamps` was 0.001-0.02 ms and memory sync 1-10 ms. The Market held 23-31 fps on every trip. The castle was 46-57 fps at first and about 35 fps later, when GPU time had risen from about 12 to 20 ms; memory sync stayed low throughout. Walking and turning the camera in both areas, I saw no flashes and no texture glitches. **My guess from reading `take_capture` in `vk_memory.cpp` (not verified):** once the texture shadow reaches `kShadowMax` (65536 pages), every capture that copies texture pages walks the whole `shadow` map looking for pages older than 64 captures. When none can go, it stops, and the next capture walks it again. Each trip to a new area adds texture pages, which would fit a cost that steps up with every trip and stays up until a restart. Logs attached: run E (two launches in one file; texture capture on) and run F (`RX_VK_TEX_CAPTURE=0`). No game files.
NotRexed commented 2026-11-10 01:59:45 +00:00 (Migrated from github.com)

The Intel hybrid (E-core) stutter part of this (#1286) is fixed in the v2.0.12 branch and will be closed when it ships. This tracker stays open for the rest of the CPU-bound FPS reports.

The Intel hybrid (E-core) stutter part of this (#1286) is fixed in the v2.0.12 branch and will be closed when it ships. This tracker stays open for the rest of the CPU-bound FPS reports.
blushOFF commented 2026-11-10 03:02:44 +00:00 (Migrated from github.com)

Castle Town Market: with a damaged texture in the dump, the game reloads the Fairy Fountain model about once a frame, and that keeps 1.2 to 1.6 CPU cores busy (RTX 3070, i7-14700KF, Windows 11, main at 08288630)

While profiling the Market for this issue, I found a CPU cost that doesn't come from Hookshot's code but from the dump.

What the game does in the Market. With HOS_FS_TRACE=1, Link standing in the Market (scripted teleport, 20 s window), the ARes Loading threads open these two files over and over: 869 times each in one 20 s window (the model 1 145 times in another run), about one open per frame, for as long as Link stays there:

  • Model/Land_FairyFountain_Base_A.Land_FairyFountain_Base_A_01.bfres.zs
  • Tex/CmnTex_Map_Soil_FairyFountain_Floor_D_Nrm.bntx.zs

Why. In my dump, that texture is one of the three damaged files from #1308 (the other two are there as well: RBake_Scene_GanonStage_G_2_1_d0_t0 and Rock_WaterTemple_Gate_M_01_Nrm). It decompresses, but to 12 288 zero bytes and then data that isn't a BNTX. As far as I can tell, the model that uses it never finishes loading, and the game asks for it again on the next frame.

What it costs. Per-thread CPU (HOS_PROFILE=5) in the same spot, 4 runs: OptimizeChangeBanc at 71-91 % of a core and ParallelDecompThread at 49-69 %, through the 20 s window.

With the three files replaced. I used HOS_FS_OVERLAY and #1308's own picker (nup.stand_in_for), so the files are the same ones it chooses. The result:

  • the model is opened 0 times in the window;
  • while Link stands, neither thread is among the 10 busiest any more (the 10th is at 18-23 % of a core);
  • frame rate standing in the Market, alternating runs:
without with the stand-ins
round 1 (N S S N N S S N) 38.8, 38.6, 39.6, 35.9 42.3, 40.9, 42.2, 40.5
round 2, same order 20.9, 34.0, 38.9, 29.6 31.6, 33.2, 39.8, 40.5
round 3 (Market then the castle, N S S N) 35.9, 30.5 31.2, 39.6
median of the 10 35.9 40.1

(Each figure is the mean of the 5 s samples taken while Link stands in the view, about 2 900 draws a frame; I left out the camera turn at the end.)

Runs mostly land either around 30 or around 40 fps, in both arms, and I don't know why yet. So I'd read this as "about 4 fps, noisy" (a one-sided permutation test over the 20 runs gives p ≈ 0.05), not as an exact figure. The CPU freed is the clearer part.

At the castle, after the Market (@felipefl142's case): the reloads go on there, though far fewer: 60 opens of the model in the castle window after 1 145 in the Market's. With the stand-ins it was 1. The castle's frame rate showed no clear difference in 2 + 2 runs, so this doesn't explain the drop you saw.

Two things for #1308's check:

  • My three files start with the zstd magic (28 b5 2f fd), so damaged_game_files(), which reads the first 4 bytes, doesn't find them. A check of the decompressed start (BNTX for .bntx.zs) would find all three. I scanned all 20 667 textures: exactly these three fail it.
  • A --romfs folder isn't checked at all. That's by design, but those players get this cost with no message.

I don't know which dump mine is. I didn't test a dump whose files start with zeros, as #1308 describes, though I'd expect the same retry. To check your own: if HOS_FS_TRACE=1 shows Land_FairyFountain_Base_A opened again and again in the Market, you have it.

**Castle Town Market: with a damaged texture in the dump, the game reloads the Fairy Fountain model about once a frame, and that keeps 1.2 to 1.6 CPU cores busy** (RTX 3070, i7-14700KF, Windows 11, main at `08288630`) While profiling the Market for this issue, I found a CPU cost that doesn't come from Hookshot's code but from the dump. **What the game does in the Market.** With `HOS_FS_TRACE=1`, Link standing in the Market (scripted teleport, 20 s window), the `ARes Loading` threads open these two files over and over: 869 times each in one 20 s window (the model 1 145 times in another run), about one open per frame, for as long as Link stays there: - `Model/Land_FairyFountain_Base_A.Land_FairyFountain_Base_A_01.bfres.zs` - `Tex/CmnTex_Map_Soil_FairyFountain_Floor_D_Nrm.bntx.zs` **Why.** In my dump, that texture is one of the three damaged files from #1308 (the other two are there as well: `RBake_Scene_GanonStage_G_2_1_d0_t0` and `Rock_WaterTemple_Gate_M_01_Nrm`). It decompresses, but to 12 288 zero bytes and then data that isn't a BNTX. As far as I can tell, the model that uses it never finishes loading, and the game asks for it again on the next frame. **What it costs.** Per-thread CPU (`HOS_PROFILE=5`) in the same spot, 4 runs: `OptimizeChangeBanc` at 71-91 % of a core and `ParallelDecompThread` at 49-69 %, through the 20 s window. **With the three files replaced.** I used `HOS_FS_OVERLAY` and #1308's own picker (`nup.stand_in_for`), so the files are the same ones it chooses. The result: - the model is opened 0 times in the window; - while Link stands, neither thread is among the 10 busiest any more (the 10th is at 18-23 % of a core); - frame rate standing in the Market, alternating runs: | | without | with the stand-ins | |---|---|---| | round 1 (N S S N N S S N) | 38.8, 38.6, 39.6, 35.9 | 42.3, 40.9, 42.2, 40.5 | | round 2, same order | 20.9, 34.0, 38.9, 29.6 | 31.6, 33.2, 39.8, 40.5 | | round 3 (Market then the castle, N S S N) | 35.9, 30.5 | 31.2, 39.6 | | median of the 10 | 35.9 | 40.1 | (Each figure is the mean of the 5 s samples taken while Link stands in the view, about 2 900 draws a frame; I left out the camera turn at the end.) Runs mostly land either around 30 or around 40 fps, in both arms, and I don't know why yet. So I'd read this as "about 4 fps, noisy" (a one-sided permutation test over the 20 runs gives p ≈ 0.05), not as an exact figure. The CPU freed is the clearer part. **At the castle, after the Market** (@felipefl142's case): the reloads go on there, though far fewer: 60 opens of the model in the castle window after 1 145 in the Market's. With the stand-ins it was 1. The castle's frame rate showed no clear difference in 2 + 2 runs, so this doesn't explain the drop you saw. **Two things for #1308's check:** - My three files start with the zstd magic (`28 b5 2f fd`), so `damaged_game_files()`, which reads the first 4 bytes, doesn't find them. A check of the decompressed start (`BNTX` for `.bntx.zs`) would find all three. I scanned all 20 667 textures: exactly these three fail it. - A `--romfs` folder isn't checked at all. That's by design, but those players get this cost with no message. I don't know which dump mine is. I didn't test a dump whose files start with zeros, as #1308 describes, though I'd expect the same retry. To check your own: if `HOS_FS_TRACE=1` shows `Land_FairyFountain_Base_A` opened again and again in the Market, you have it.
blushOFF commented 2026-11-10 06:25:22 +00:00 (Migrated from github.com)

Follow-up, same machine (RTX 3070 8 GB, i7-14700KF, Windows 11, main 08288630 with the three stand-ins from my comment above): why the Market's frame rate jumps between runs, which matters for anyone A/B-ing here

1. Frame time in the Market ≈ GPU time + replay time. I took the 5 s samples with Link standing in the Market (about 2 900 draws a frame): 609 samples from 82 runs. The median difference between the logged fps and 1000 / (GPU time + replay submit time) is 0.06 fps. So here the CPU's replay of a frame and the GPU's work on it don't overlap. The replay is steady (median 11.1 ms).

2. The run-to-run spread is GPU time, and it comes per launch. Each run lands either around 12-14 ms or around 18-23 ms of GPU time for the same scene, and stays there. nvidia-smi every 500 ms in the 6 runs I logged: 1905-1920 MHz, P2, no throttle reason, in fast and slow runs alike. So it isn't clocks.

3. In the slow runs, some passes get several times slower. With RX_VK_PASS_TIMING=1:

slow (3 runs) fast (2 runs)
m pass total 12.6-13.7 ms 6.6-7.1 ms
a 1536x864 pass, 0-4 draws, program 1a7482c0ec9991ea 1.08-1.12 ms 0.17 ms
write-backs 3.4-3.8 ms, about 61 MB the same

4. VRAM is past what the driver allows in most runs, e.g. used 5866 of 5833 MB, and 671 MB of that is free space inside VMA's blocks. On this PC about 2.2 GB of the card was already in use before the game started.

With all three budgets lowered at once (RX_VK_MIRROR_MB=1408, RX_VK_VRAM_MB=1400, RX_VK_WB_SHADOW_MB=256), alternating 6 + 6 runs:

  • used stayed under allowed in all 6 runs;
  • GPU time was 15.4-16.3 ms in all 6, against 14.0-20.0 ms in the 6 default runs. The spread is gone.
  • Mean fps was about the same (36.0 against 35.1): the smaller budgets cost GPU time of their own.

That fits the slow state being memory the driver moved out of VRAM, but I haven't seen it directly.

These didn't remove the spread:

  • RX_VK_MIRROR_MB=3072: worse, 33.2 against 39.1 fps (4 + 4 runs);
  • RX_VK_VRAM_MB=1400 alone: 2 slow runs of 6, against 3 of 6.

A local experiment, not a PR. The driver (610.88) has VK_EXT_memory_priority and VK_EXT_pageable_device_local_memory, and Hookshot enables neither. With both on and the images' memory at priority 1.0, over 6 + 6 runs:

  • 37.5 against 35.1 fps;
  • GPU time median 14.9 against 17.8 ms;
  • but one of the 6 runs was still slow.

So it's not conclusive yet.

For anyone comparing switches on an 8 GB card: read the GPU time of each run. One run per arm can land in either state, which is a 6-10 fps swing on its own.

**Follow-up, same machine (RTX 3070 8 GB, i7-14700KF, Windows 11, main `08288630` with the three stand-ins from my comment above): why the Market's frame rate jumps between runs, which matters for anyone A/B-ing here** **1. Frame time in the Market ≈ GPU time + replay time.** I took the 5 s samples with Link standing in the Market (about 2 900 draws a frame): 609 samples from 82 runs. The median difference between the logged fps and `1000 / (GPU time + replay submit time)` is 0.06 fps. So here the CPU's replay of a frame and the GPU's work on it don't overlap. The replay is steady (median 11.1 ms). **2. The run-to-run spread is GPU time, and it comes per launch.** Each run lands either around 12-14 ms or around 18-23 ms of GPU time for the same scene, and stays there. `nvidia-smi` every 500 ms in the 6 runs I logged: 1905-1920 MHz, P2, no throttle reason, in fast and slow runs alike. So it isn't clocks. **3. In the slow runs, some passes get several times slower.** With `RX_VK_PASS_TIMING=1`: | | slow (3 runs) | fast (2 runs) | |---|---|---| | `m pass` total | 12.6-13.7 ms | 6.6-7.1 ms | | a 1536x864 pass, 0-4 draws, program `1a7482c0ec9991ea` | 1.08-1.12 ms | 0.17 ms | | write-backs | 3.4-3.8 ms, about 61 MB | the same | **4. VRAM is past what the driver allows in most runs**, e.g. `used 5866 of 5833 MB`, and 671 MB of that is free space inside VMA's blocks. On this PC about 2.2 GB of the card was already in use before the game started. **With all three budgets lowered at once** (`RX_VK_MIRROR_MB=1408`, `RX_VK_VRAM_MB=1400`, `RX_VK_WB_SHADOW_MB=256`), alternating 6 + 6 runs: - used stayed under allowed in all 6 runs; - GPU time was 15.4-16.3 ms in all 6, against 14.0-20.0 ms in the 6 default runs. The spread is gone. - Mean fps was about the same (36.0 against 35.1): the smaller budgets cost GPU time of their own. That fits the slow state being memory the driver moved out of VRAM, but I haven't seen it directly. **These didn't remove the spread:** - `RX_VK_MIRROR_MB=3072`: worse, 33.2 against 39.1 fps (4 + 4 runs); - `RX_VK_VRAM_MB=1400` alone: 2 slow runs of 6, against 3 of 6. **A local experiment, not a PR.** The driver (610.88) has `VK_EXT_memory_priority` and `VK_EXT_pageable_device_local_memory`, and Hookshot enables neither. With both on and the images' memory at priority 1.0, over 6 + 6 runs: - 37.5 against 35.1 fps; - GPU time median 14.9 against 17.8 ms; - but one of the 6 runs was still slow. So it's not conclusive yet. **For anyone comparing switches on an 8 GB card:** read the GPU time of each run. One run per arm can land in either state, which is a 6-10 fps swing on its own.
lucasmazzoni45-del commented 2026-11-10 10:04:01 +00:00 (Migrated from github.com)

AMD data point from reading a 27-minute play log (RX 7800 XT, Ryzen 7 5800X3D, 2560x1440, FG 2x, v2.0.11 plus #1130; VRAM budget 14.5 GB) for what still stalls. Steady play was fine: replay about 0.5-1 ms a submit.

All 17 submits of 40 ms or more were at loading or scene changes:

  • one of 720 ms: a draw blocked on a graphics pipeline (draw 717 pipelines 707), although 11406 pipelines had been precompiled. RX_VK_GPL is off by default and I have not tried it on AMD (see #1271 for NVIDIA);
  • 500 and 457 ms: texture lookups, descriptors and uploads at scene changes;
  • six of 31-147 ms in "memory sync"; the log does not say which part (maps, capture, collect, or pools_mu). I added the timing of each part to the slow-submit line in a local build so the next log will say;
  • pool imports of 15-34 ms each (vkAllocateMemory in import_pool): 53 of them took 665 ms, mostly early. The VRAM line showed 13.5 of 14.5 GB at the peak.

Two local changes against vkAllocateMemory stalls on AMD (about 170 ms a 128 MB block on this driver), not in upstream: RX_VK_IMAGE_PREALLOC (part of the image pool made at the start: 15% of VRAM, 2-24 blocks; 19 here) and capture buffers kept in 32/64/128/256 MB classes up to 768 MB. On an older build with a headless scripted walk the mean went 52.9 to 59.8 fps and the min 41.6 to 56.7; I have not measured them on v2.0.12 or in play, and they cost VRAM. If this is useful I can turn them into a small PR with switches to turn them off.

AMD data point from reading a 27-minute play log (RX 7800 XT, Ryzen 7 5800X3D, 2560x1440, FG 2x, v2.0.11 plus #1130; VRAM budget 14.5 GB) for what still stalls. Steady play was fine: replay about 0.5-1 ms a submit. All 17 submits of 40 ms or more were at loading or scene changes: - one of **720 ms**: a draw blocked on a graphics pipeline (`draw 717 pipelines 707`), although 11406 pipelines had been precompiled. `RX_VK_GPL` is off by default and I have not tried it on AMD (see #1271 for NVIDIA); - **500 and 457 ms**: texture lookups, descriptors and uploads at scene changes; - **six of 31-147 ms in "memory sync"**; the log does not say which part (maps, capture, collect, or `pools_mu`). I added the timing of each part to the slow-submit line in a local build so the next log will say; - pool imports of 15-34 ms each (`vkAllocateMemory` in `import_pool`): 53 of them took 665 ms, mostly early. The VRAM line showed 13.5 of 14.5 GB at the peak. Two local changes against `vkAllocateMemory` stalls on AMD (about 170 ms a 128 MB block on this driver), not in upstream: `RX_VK_IMAGE_PREALLOC` (part of the image pool made at the start: 15% of VRAM, 2-24 blocks; 19 here) and capture buffers kept in 32/64/128/256 MB classes up to 768 MB. On an older build with a headless scripted walk the mean went 52.9 to 59.8 fps and the min 41.6 to 56.7; **I have not measured them on v2.0.12 or in play**, and they cost VRAM. If this is useful I can turn them into a small PR with switches to turn them off.
blushOFF commented 2026-11-10 11:13:11 +00:00 (Migrated from github.com)

Correction to my two comments above: they weren't measured with default settings.

The test folder had a local hookshot_env.txt I'd forgotten about, so every run in both comments had:
RX_VK_WB_DIFF=0, RX_VK_PER_QUEUE_FENCES=0, RX_VK_ASTC_BC_ALL=1, RX_VK_IMAGE_BLOCK_MB=32, RX_VK_KEEP_RETIRED=0.

The Fountain reload loop in the first comment doesn't depend on these. Point 1 of the second one ("the CPU's replay of a frame and the GPU's work on it don't overlap") does: it comes from RX_VK_PER_QUEUE_FENCES=0.

Same Market spot, same stand-ins. Defaults, then each setting alone on top of them, 2 runs each:

late-page waits per 600 captures fps GPU time replay submit 1000 / (GPU + replay)
defaults, fast run 47-99 52.3 12.6 ms 15.6 ms 35.5
RX_VK_PER_QUEUE_FENCES=0, fast run 0-1 38.6 14.0 ms 11.2 ms 39.7
  • Defaults: about one capture a frame (6 submits a frame) waits in take_capture for the GPU (GPU wait for late pages). That wait counts in the replay time, yet the run is faster than 1000 / (GPU + replay). So CPU and GPU overlap.
  • RX_VK_PER_QUEUE_FENCES=0: 0-1 waits per 600 in both runs, and fps matches 1000 / (GPU + replay). No overlap, as in my comment.
  • The other three settings alone (RX_VK_WB_DIFF=0; RX_VK_ASTC_BC_ALL=1 with RX_VK_IMAGE_BLOCK_MB=32; RX_VK_KEEP_RETIRED=0) still have the waits and beat the formula in all their runs.

GPU time still lands per launch in a fast or a slow state (12.2-14.0 ms against 18.5-53.0 ms in these 10 runs). So 2 runs per setting don't rank the settings by fps.

**Correction to my two comments above: they weren't measured with default settings.** The test folder had a local `hookshot_env.txt` I'd forgotten about, so every run in both comments had: `RX_VK_WB_DIFF=0`, `RX_VK_PER_QUEUE_FENCES=0`, `RX_VK_ASTC_BC_ALL=1`, `RX_VK_IMAGE_BLOCK_MB=32`, `RX_VK_KEEP_RETIRED=0`. The Fountain reload loop in the first comment doesn't depend on these. Point 1 of the second one ("the CPU's replay of a frame and the GPU's work on it don't overlap") does: it comes from `RX_VK_PER_QUEUE_FENCES=0`. Same Market spot, same stand-ins. Defaults, then each setting alone on top of them, 2 runs each: | | late-page waits per 600 captures | fps | GPU time | replay submit | `1000 / (GPU + replay)` | |---|---|---|---|---|---| | defaults, fast run | 47-99 | 52.3 | 12.6 ms | 15.6 ms | 35.5 | | `RX_VK_PER_QUEUE_FENCES=0`, fast run | 0-1 | 38.6 | 14.0 ms | 11.2 ms | 39.7 | - **Defaults:** about one capture a frame (6 submits a frame) waits in `take_capture` for the GPU (`GPU wait for late pages`). That wait counts in the replay time, yet the run is faster than `1000 / (GPU + replay)`. So CPU and GPU overlap. - **`RX_VK_PER_QUEUE_FENCES=0`:** 0-1 waits per 600 in both runs, and fps matches `1000 / (GPU + replay)`. No overlap, as in my comment. - **The other three settings alone** (`RX_VK_WB_DIFF=0`; `RX_VK_ASTC_BC_ALL=1` with `RX_VK_IMAGE_BLOCK_MB=32`; `RX_VK_KEEP_RETIRED=0`) still have the waits and beat the formula in all their runs. GPU time still lands per launch in a fast or a slow state (12.2-14.0 ms against 18.5-53.0 ms in these 10 runs). So 2 runs per setting don't rank the settings by fps.
rakefire-impala commented 2026-11-10 11:15:42 +00:00 (Migrated from github.com)

Measurements from a low-end laptop: RX 5500M (4 GB VRAM), a 12-thread laptop CPU and 16 GB RAM, in Hyrule Field, on main 779bdcf with the open fallback PRs. They may help show where the frame goes on this class of card.

  • About 33 fps, limited by the replay thread: 25-27 ms a frame (draw 10-12 ms, memory sync 8-10, commands 4, descriptors 3.5-4). GPU time is 21-23 ms.
  • Memory sync is mostly waiting for the GPU: GPU wait for late pages is 1.2-1.5 ms per capture, six captures a frame.
  • Mirrors: on a card of 6.5 GB or less, the budget is VRAM/8, here 478 MB. Pools the GPU reads were refused (state 1) and read over PCIe: 28 GB per 600 submits.
    • With RX_VK_MIRROR_MB=1024: 4.2 GB (-85%), GPU 22.5 → 21.2 ms, memory sync about 1 ms less, fps the same within noise.
    • The hottest pool (0x1178000000, 160 MB, where the tree culling's lists live) still didn't get a mirror.
  • RX_VK_PASS_TIMING=2:
    • 24 command buffers a frame, with 8 ms of GPU idle between them.
    • Write-back of 12.6 MB a frame (2 ms of GPU).
    • Passes 7.9 ms (the scene is 768×432), dispatches 4.9 ms.

So on this card the frame is bounded by the CPU-GPU round trips around the game's mid-frame readbacks, not by GPU throughput or resolution. Lowering settings won't help much here; fewer or later readback waits would. Happy to measure any change on this hardware.

**Measurements from a low-end laptop:** RX 5500M (4 GB VRAM), a 12-thread laptop CPU and 16 GB RAM, in Hyrule Field, on main `779bdcf` with the open fallback PRs. They may help show where the frame goes on this class of card. - **About 33 fps, limited by the replay thread:** 25-27 ms a frame (draw 10-12 ms, memory sync 8-10, commands 4, descriptors 3.5-4). GPU time is 21-23 ms. - **Memory sync is mostly waiting for the GPU:** `GPU wait for late pages` is 1.2-1.5 ms per capture, six captures a frame. - **Mirrors:** on a card of 6.5 GB or less, the budget is VRAM/8, here 478 MB. Pools the GPU reads were refused (state 1) and read over PCIe: 28 GB per 600 submits. - With `RX_VK_MIRROR_MB=1024`: 4.2 GB (-85%), GPU 22.5 → 21.2 ms, memory sync about 1 ms less, fps the same within noise. - The hottest pool (`0x1178000000`, 160 MB, where the tree culling's lists live) still didn't get a mirror. - **`RX_VK_PASS_TIMING=2`:** - 24 command buffers a frame, with 8 ms of GPU idle between them. - Write-back of 12.6 MB a frame (2 ms of GPU). - Passes 7.9 ms (the scene is 768×432), dispatches 4.9 ms. So on this card the frame is bounded by the CPU-GPU round trips around the game's mid-frame readbacks, not by GPU throughput or resolution. Lowering settings won't help much here; fewer or later readback waits would. Happy to measure any change on this hardware.
lucasmazzoni45-del commented 2026-11-10 12:21:31 +00:00 (Migrated from github.com)

Replay (CPU) times from logs on a Ryzen 7 5800X3D + RX 7800 XT, v2.0.12-based builds, to put numbers on the CPU-bound ceiling: the a frame's replay time line in the scenes with 2,400 draws or more a frame (3 sessions, 67% render scale, and one at 100% at 2560x1440):

session samples with >=2400 draws replay submit mean (min-max) of it draw
67%, 14 min 23 15.4 ms (10.1-19.0) 9.5
67%, 7 min 9 14.3 ms (12.1-16.4) 8.5
67%, 10 min 13 15.4 ms (8.2-27.1) 9.9
100%, 1440p 16 15.2 ms (11.9-19.2) 8.6

So the replay is about 15 ms a frame in heavy scenes whatever the render resolution, with draw 8-10 ms, descriptors 2.4-3.1, commands 3.2-4.0, programs 1.3-1.7 and memory sync 1.5-2.0. That already leaves almost nothing under a 16.6 ms frame, and at 100% 1440p the GPU adds 13 ms (GPU time 12-13.5 ms in the same scenes), so the game ran at 45-51 fps there (28% of the 5-second samples under 50). Lowering the render scale only shortens the GPU part; it does not move the 15 ms.

For anyone thinking of RX_VK_PARALLEL: #1576 reports missing textures with it, and the code comments say too-bright frames with 4 threads, so I have not used it. Whether the 15 ms can be cut (descriptors, the draw loop, fewer mid-frame syncs) is the open question here; I can measure a change on this CPU if one is proposed.

Replay (CPU) times from logs on a **Ryzen 7 5800X3D** + RX 7800 XT, v2.0.12-based builds, to put numbers on the CPU-bound ceiling: the `a frame's replay time` line in the scenes with 2,400 draws or more a frame (3 sessions, 67% render scale, and one at 100% at 2560x1440): | session | samples with >=2400 draws | replay `submit` mean (min-max) | of it `draw` | |---|---|---|---| | 67%, 14 min | 23 | 15.4 ms (10.1-19.0) | 9.5 | | 67%, 7 min | 9 | 14.3 ms (12.1-16.4) | 8.5 | | 67%, 10 min | 13 | 15.4 ms (8.2-27.1) | 9.9 | | **100%, 1440p** | 16 | 15.2 ms (11.9-19.2) | 8.6 | So **the replay is about 15 ms a frame in heavy scenes whatever the render resolution**, with `draw` 8-10 ms, `descriptors` 2.4-3.1, `commands` 3.2-4.0, `programs` 1.3-1.7 and `memory sync` 1.5-2.0. That already leaves almost nothing under a 16.6 ms frame, and at 100% 1440p the GPU adds 13 ms (GPU time 12-13.5 ms in the same scenes), so the game ran at 45-51 fps there (28% of the 5-second samples under 50). Lowering the render scale only shortens the GPU part; it does not move the 15 ms. For anyone thinking of `RX_VK_PARALLEL`: #1576 reports missing textures with it, and the code comments say too-bright frames with 4 threads, so I have not used it. Whether the 15 ms can be cut (descriptors, the draw loop, fewer mid-frame syncs) is the open question here; I can measure a change on this CPU if one is proposed.
SaKenLpB commented 2026-11-10 12:28:21 +00:00 (Migrated from github.com)

Follow-up as promised: v2.0.12 (#1158 merged), i7-12700KF + RTX 5070 (driver 621), 1440p, FG 2x, same Kokiri Village route.

Kokiri Village, frames with 2000+ draws:

  • v2.0.11, replay on the E-core (0x80000): 38.0 fps avg, 10th pct 33.8, replay 20.2 ms/frame
  • v2.0.11, HOS_PIN_RENDER=0: 48.5-49.3 fps avg, 10th pct ~46, replay 16-18 ms
  • v2.0.12, replay pinned to P-core (0xc000): 59.0 fps avg (60 cap), 10th pct 56.8, replay 11.0 ms, GPU 9.2 ms

The log shows "replays run on logical processors 0xc000", so a P-core as intended. Thanks @jordanella!

Note: with Frame generation 2x, v2.0.12 turned on FSR 3.1 frame interpolation by default. For me it made the HUD (hearts, top-left button) flicker and cost ~1.3 ms per frame in the village (46.9 fps). RX_VK_FSR_FG=0 in hookshot_env.txt fixed both; the numbers above are with it.

Follow-up as promised: v2.0.12 (#1158 merged), i7-12700KF + RTX 5070 (driver 621), 1440p, FG 2x, same Kokiri Village route. Kokiri Village, frames with 2000+ draws: - v2.0.11, replay on the E-core (0x80000): 38.0 fps avg, 10th pct 33.8, replay 20.2 ms/frame - v2.0.11, HOS_PIN_RENDER=0: 48.5-49.3 fps avg, 10th pct ~46, replay 16-18 ms - v2.0.12, replay pinned to P-core (0xc000): 59.0 fps avg (60 cap), 10th pct 56.8, replay 11.0 ms, GPU 9.2 ms The log shows "replays run on logical processors 0xc000", so a P-core as intended. Thanks @jordanella! Note: with Frame generation 2x, v2.0.12 turned on FSR 3.1 frame interpolation by default. For me it made the HUD (hearts, top-left button) flicker and cost ~1.3 ms per frame in the village (46.9 fps). RX_VK_FSR_FG=0 in hookshot_env.txt fixed both; the numbers above are with it.
lucasmazzoni45-del commented 2026-11-10 13:14:55 +00:00 (Migrated from github.com)

Two more observations from my 100% / 1440p log (RX 7800 XT, Ryzen 7 5800X3D; builds based on v2.0.12), both about the stalls at scene loads:

1. The memory-sync spikes are in the capture part and only at loads. In the 19-minute session 5 submits had memory sync of 15 ms or more; the part breakdown I added to the slow-submit line shows them all in capture: 17.4, 17.4, 22.6, 66.8 and 107.7 ms, with maps 0.0 (lock waits 0.0) and GPU wait for late pages 0.000. Each follows a load (one right after a frame's uploads took 152 ms ... 503 MB, one after 893 textures retired and a 15 MB re-upload) and none occurs in steady play; they run on sead::MainThread. The sizes scale like an allocation: ~17-22, 67 and 108 ms. From reading capture_make/cap_buf_get (vk_memory.cpp), a capture that needs a size class with no spare buffer makes a new host buffer as its own memory object, and on AMD's Windows driver that kind of allocation cost about 0.8 ms per MB in my earlier measurements (170 ms for a 128 MB device block); this is the explanation I suspect, not proven. A slow capture: ... the buffer of M MB A ms, the copy C ms line in a local build will separate the allocation from the copy.

2. A cost after any frame that deferred textures (code reading, not measured). texture_frame() (vk_texture.cpp ~3383) does destroy_gen++ after a frame in which ASTC uploads were deferred (RX_VK_UPLOAD_BUDGET_MS), which is right (the deferred texture is looked up again). But the same bump makes every handle_hits entry and every reused descriptor stale, so the next frame re-resolves and rebuilds the views and descriptors of all draws (about 2,700 here). In an area load that defers over several frames, that may be a few ms of replay time on each of them. A per-texture invalidation would avoid it; I have no measurement yet.

Two more observations from my 100% / 1440p log (RX 7800 XT, Ryzen 7 5800X3D; builds based on v2.0.12), both about the stalls at scene loads: **1. The memory-sync spikes are in the capture part and only at loads.** In the 19-minute session 5 submits had `memory sync` of 15 ms or more; the part breakdown I added to the slow-submit line shows them all in `capture`: 17.4, 17.4, 22.6, 66.8 and 107.7 ms, with `maps 0.0 (lock waits 0.0)` and `GPU wait for late pages 0.000`. Each follows a load (one right after `a frame's uploads took 152 ms ... 503 MB`, one after `893 textures retired` and a 15 MB re-upload) and none occurs in steady play; they run on `sead::MainThread`. The sizes scale like an allocation: ~17-22, 67 and 108 ms. From reading `capture_make`/`cap_buf_get` (`vk_memory.cpp`), a capture that needs a size class with no spare buffer makes a new host buffer as its own memory object, and on AMD's Windows driver that kind of allocation cost about 0.8 ms per MB in my earlier measurements (170 ms for a 128 MB device block); this is the explanation I suspect, not proven. A `slow capture: ... the buffer of M MB A ms, the copy C ms` line in a local build will separate the allocation from the copy. **2. A cost after any frame that deferred textures (code reading, not measured).** `texture_frame()` (`vk_texture.cpp` ~3383) does `destroy_gen++` after a frame in which ASTC uploads were deferred (RX_VK_UPLOAD_BUDGET_MS), which is right (the deferred texture is looked up again). But the same bump makes every `handle_hits` entry and every reused descriptor stale, so the next frame re-resolves and rebuilds the views and descriptors of all draws (about 2,700 here). In an area load that defers over several frames, that may be a few ms of replay time on each of them. A per-texture invalidation would avoid it; I have no measurement yet.
lucasmazzoni45-del commented 2026-11-10 13:33:52 +00:00 (Migrated from github.com)

A practical note for triage and for anyone testing a stutter or a flicker report (CONTRIBUTING asks for tests without overlays, and the issue form asks "other software hooked into the game"): a PC can have several implicit Vulkan layers registered that are injected into every Vulkan game, without the user having anything "hooked" on purpose. On my machine (Windows 11, loader 1.4.357) HKLM\SOFTWARE\Khronos\Vulkan\ImplicitLayers lists six: the GOG Galaxy overlay, OBS's capture hook (VK_LAYER_OBS_HOOK), the Epic Online Services overlay, a TikTok LIVE Studio media layer, the Steam overlay and Steam Fossilize. vulkaninfo prints loader warnings about three of them (a layer name that does not follow the naming policy, layers built for Vulkan 1.3). Each layer wraps calls such as submit and present.

hookshot_env.txt cannot turn them off on purpose: env_line_allowed() in host/hos_main.c only takes RX_, HOS_, HOOKSHOT_, EDEN_, NVN_, TOTK_ and SDL_ names, because the loader's variables can load code. A way to test without them is to set the loader's own switch in the environment of the process, for example a .bat next to hookshot.exe:

set VK_LOADER_LAYERS_DISABLE=~implicit~
start "" hookshot.exe

(~implicit~ is documented in the Vulkan-Loader LayerInterface doc; single layers have their own DISABLE_* variables in their JSON files, e.g. DISABLE_VULKAN_OBS_CAPTURE=1.)

I have not measured whether these layers cost anything or change any result (I have not yet run the same route with and without); I am posting it because it is cheap to check and the stutter, flicker and overlay reports here all assume no hooks. If someone compares a session with and without VK_LOADER_LAYERS_DISABLE=~implicit~, replay submit times in heavy scenes would show it.

A practical note for triage and for anyone testing a stutter or a flicker report (CONTRIBUTING asks for tests without overlays, and the issue form asks "other software hooked into the game"): **a PC can have several implicit Vulkan layers registered that are injected into every Vulkan game, without the user having anything "hooked" on purpose.** On my machine (Windows 11, loader 1.4.357) `HKLM\SOFTWARE\Khronos\Vulkan\ImplicitLayers` lists six: the GOG Galaxy overlay, OBS's capture hook (`VK_LAYER_OBS_HOOK`), the Epic Online Services overlay, a TikTok LIVE Studio media layer, the Steam overlay and Steam Fossilize. `vulkaninfo` prints loader warnings about three of them (a layer name that does not follow the naming policy, layers built for Vulkan 1.3). Each layer wraps calls such as submit and present. `hookshot_env.txt` cannot turn them off on purpose: `env_line_allowed()` in `host/hos_main.c` only takes `RX_`, `HOS_`, `HOOKSHOT_`, `EDEN_`, `NVN_`, `TOTK_` and `SDL_` names, because the loader's variables can load code. A way to test without them is to set the loader's own switch in the environment of the process, for example a `.bat` next to `hookshot.exe`: ``` set VK_LOADER_LAYERS_DISABLE=~implicit~ start "" hookshot.exe ``` (`~implicit~` is documented in the Vulkan-Loader LayerInterface doc; single layers have their own `DISABLE_*` variables in their JSON files, e.g. `DISABLE_VULKAN_OBS_CAPTURE=1`.) **I have not measured whether these layers cost anything or change any result** (I have not yet run the same route with and without); I am posting it because it is cheap to check and the stutter, flicker and overlay reports here all assume no hooks. If someone compares a session with and without `VK_LOADER_LAYERS_DISABLE=~implicit~`, replay `submit` times in heavy scenes would show it.
davjack commented 2026-11-10 17:29:46 +00:00 (Migrated from github.com)

Data point from a Ryzen 9 3950X (Zen 2, 16 cores) with an RTX 4090 on Linux, v2.0.12 built with the AppImage.

Setup: Ubuntu 26.04, kernel 7.0, NVIDIA 595.99.02, 62 GB RAM, output 2560x1440, no upscaler and no frame generation. Headless runs (HOS_HEADLESS=1) with scripted input from the same Kokiri Forest autosave, then Cheats page teleports to Kakariko, Castle Town Market and Lake Hylia. 30 s standing in each place; values are the last four fps reports.

Variant Kokiri Kakariko Market Lake Hylia
RX_VK_PARALLEL=4 46 37 21 19
+ RX_VK_TEX_CAPTURE=0 45 34 29 36
+ RX_VK_ASYNC=1 28 25 16 17
+ HOS_CORES=8 46 38 20 17

At the same Kokiri spot, no RX_VK_PARALLEL gives 40-41 fps and RX_VK_PARALLEL=8 gives 44-50, no better than 4.

GPU time stays at 3-6 ms a frame everywhere. The replay takes about 18 ms a frame in Kokiri and 45-55 ms at Lake Hylia, where memory sync is about 26 ms of it (around 2,900 mirror copies and 68 MB written back a frame). With RX_VK_TEX_CAPTURE=0 the write-back drops to 14 MB a frame.

Comparing ten screenshots per run at the same present counts, with and without RX_VK_TEX_CAPTURE=0 (ground lighting in Kokiri, the light shaft at Lake Hylia), I found no measurable difference.

Data point from a Ryzen 9 3950X (Zen 2, 16 cores) with an RTX 4090 on Linux, v2.0.12 built with the AppImage. Setup: Ubuntu 26.04, kernel 7.0, NVIDIA 595.99.02, 62 GB RAM, output 2560x1440, no upscaler and no frame generation. Headless runs (`HOS_HEADLESS=1`) with scripted input from the same Kokiri Forest autosave, then Cheats page teleports to Kakariko, Castle Town Market and Lake Hylia. 30 s standing in each place; values are the last four fps reports. | Variant | Kokiri | Kakariko | Market | Lake Hylia | |---|---|---|---|---| | `RX_VK_PARALLEL=4` | 46 | 37 | 21 | 19 | | + `RX_VK_TEX_CAPTURE=0` | 45 | 34 | 29 | 36 | | + `RX_VK_ASYNC=1` | 28 | 25 | 16 | 17 | | + `HOS_CORES=8` | 46 | 38 | 20 | 17 | At the same Kokiri spot, no `RX_VK_PARALLEL` gives 40-41 fps and `RX_VK_PARALLEL=8` gives 44-50, no better than 4. GPU time stays at 3-6 ms a frame everywhere. The replay takes about 18 ms a frame in Kokiri and 45-55 ms at Lake Hylia, where `memory sync` is about 26 ms of it (around 2,900 mirror copies and 68 MB written back a frame). With `RX_VK_TEX_CAPTURE=0` the write-back drops to 14 MB a frame. Comparing ten screenshots per run at the same present counts, with and without `RX_VK_TEX_CAPTURE=0` (ground lighting in Kokiri, the light shaft at Lake Hylia), I found no measurable difference.
jcap14 commented 2026-11-10 19:56:27 +00:00 (Migrated from github.com)

Chiming in with another data point. I'm also experiencing poor performance that mirrors other reports.
GPU: RTX 5090
CPU: Intel i9 7980xe, 18 cores 4.2ghz (all)
RAM: 128GB DDR4 3200 MHz (quad channel)
OS: Windows 11
Hookshot: 2.0.14

Ingame the GPU is barely being 10% utilized. Hyrule field, town, and Kakariko barely squeak by with 30fps as others report. This is the same performance regardless of the render resolution or other features. Oddly, no single CPU core is being hammered either. I don't have memory leaks or video playback issues.

The user above me, davjack, has a nearly identical system to mine from the same era.

By contrast, if I play this on an Intel Ultra 265K with 32GB of RAM and a 5060 Ti, it fully maximizes its use of the GPU resources and gets full expected performance.

Chiming in with another data point. I'm also experiencing poor performance that mirrors other reports. GPU: RTX 5090 CPU: Intel i9 7980xe, 18 cores 4.2ghz (all) RAM: 128GB DDR4 3200 MHz (quad channel) OS: Windows 11 Hookshot: 2.0.14 Ingame the GPU is barely being 10% utilized. Hyrule field, town, and Kakariko barely squeak by with 30fps as others report. This is the same performance regardless of the render resolution or other features. Oddly, no single CPU core is being hammered either. I don't have memory leaks or video playback issues. The user above me, davjack, has a nearly identical system to mine from the same era. By contrast, if I play this on an Intel Ultra 265K with 32GB of RAM and a 5060 Ti, it fully maximizes its use of the GPU resources and gets full expected performance.
D4rkGh057 commented 2026-11-10 21:14:44 +00:00 (Migrated from github.com)

hookshot_log_pass-timing.txt
hookshot_log_unprofiled_fg-off.txt

Data point: Ryzen 5 5600XT + RTX 4060, v2.0.14, Frame generation Off: per-thread CPU times and GPU pass timing (write-back is about half of the GPU time in the slow stretch)

  • Hookshot v2.0.14 (source 3a9998c3), Windows 11
  • CPU: AMD Ryzen 5 5600XT (6 cores / 12 threads), 16 GB RAM (16273 MB, 20480 MB swap, no LOW MEMORY line)
  • GPU: NVIDIA GeForce RTX 4060 (8 GB), Vulkan 1.4, mesh shaders yes, NVIDIA driver 621.14
  • Settings: output 2048x1152, Frame generation Off, Frame pacing On (no effect while Frame generation is Off), Reflex On, DLSS off, RX_VK_FSR_FG=0 in hookshot_env.txt
  • Nothing hooked into the game.

1. Unprofiled run, HOS_THREAD_TIMES=10 (269 presents windows, Frame generation turned off a minute after start-up; only the part after it is counted)

Measure Result
Average fps 41.2 (about 24 ms a frame)
Windows under 30 fps / 30-45 / 45-58 / 58 or more 11 / 209 / 15 / 34
Draws a frame: windows under 45 fps / 45 fps or more 1131 / 544 on average
GPU time a frame 17.8 ms on average, 28.5 ms at most
GPU-bound wait 0.02 ms on average
CPU used by all threads over 5% 3.1 cores of 6 on average
sead::MainThread / Presentation Thread 29% (max 44%) / 34% (max 55%) of a core

No thread is near 100% in gameplay and the CPU waits almost nothing for the GPU, but the GPU is busy about three quarters of the frame. At start-up, 12 unnamed threads sit at 96-100% for a long stretch (shader and pipeline preparation using every logical core).

2. Separate run with RX_VK_PASS_TIMING=1 (20 reports of 600 presents, Frame generation Off from start to end)

Fast stretch (8 reports) Slow stretch (10 reports, later in the run)
fps 55.9-60.1 32.4-38.6
draws a frame 168-1814 864-2117
GPU ms a frame, all spans 6.4-13.3 17.8-25.7
wb (write-back, mirror -> guest memory) 0.43-0.92 ms (7-8%) 8.6-11.1 ms (49-55%)
wb data a frame about 25-31 MB (blocks I checked) 54-69 MB
pass (the draws) 3.3-7.2 ms (61-65%) 4.4-7.2 ms (25-33%)
dispatch 0.7-2.0 ms 1.7-2.9 ms
  • In the slow stretch, wb copies 54-69 MB a frame and takes about half of the GPU time. The time in the draws barely changes (a fast block with 1814 draws ran at 55.9 fps; a slow one with 864 draws ran at 38.6 fps), so the draw count alone does not explain the drop.
  • This matches the README's note on RX_VK_WB_MAX_KB (an RTX 3080 in Hyrule Field: about 66 MB and 4.4 ms a submit, 45-48 fps; 59-60 fps with RX_VK_WB_MAX_KB=1024).
  • That switch is in main since 30911ca ("perf(vk): skip write-backs of large GPU-written bindings"), but not in v2.0.14: GitHub's compare of v2.0.14...30911ca says diverged, 54 ahead, and I added RX_VK_WB_MAX_KB=1024 to hookshot_env.txt on v2.0.14 and the log has no vk: write-backs only of written bindings up to ... line. So I could not test it. I will test it when a release has it.

Caveats

  • Each timestamp waits for the GPU, so profiled spans add up to more than an unprofiled frame (the log says so): read the shares, not the absolute ms.
  • One report I read in full says 5251 spans not timed (a command buffer's timestamps ran out).
  • This is a correlation, not proof that wb causes the drop. I do not know which area of the game each stretch is.
  • The log does not mark when the stutters happened, so I cannot tie the slow windows to what I felt.

No fix asked: this is a hardware data point for the CPU-bound / low-GPU-usage family, with the write-back share as the finding.

Logs attached: hookshot_log_unprofiled_fg-off.txt (part 1) and hookshot_log_pass-timing.txt (part 2), with the Windows user name in file paths replaced.

[hookshot_log_pass-timing.txt](https://github.com/user-attachments/files/34651519/hookshot_log_pass-timing.txt) [hookshot_log_unprofiled_fg-off.txt](https://github.com/user-attachments/files/34651520/hookshot_log_unprofiled_fg-off.txt) **Data point: Ryzen 5 5600XT + RTX 4060, v2.0.14, Frame generation Off: per-thread CPU times and GPU pass timing (write-back is about half of the GPU time in the slow stretch)** - Hookshot v2.0.14 (source 3a9998c3), Windows 11 - CPU: AMD Ryzen 5 5600XT (6 cores / 12 threads), 16 GB RAM (16273 MB, 20480 MB swap, no `LOW MEMORY` line) - GPU: NVIDIA GeForce RTX 4060 (8 GB), Vulkan 1.4, mesh shaders yes, NVIDIA driver 621.14 - Settings: output 2048x1152, **Frame generation Off**, Frame pacing On (no effect while Frame generation is Off), Reflex On, DLSS off, `RX_VK_FSR_FG=0` in `hookshot_env.txt` - Nothing hooked into the game. **1. Unprofiled run, `HOS_THREAD_TIMES=10`** (269 `presents` windows, Frame generation turned off a minute after start-up; only the part after it is counted) | Measure | Result | |---|---| | Average fps | 41.2 (about 24 ms a frame) | | Windows under 30 fps / 30-45 / 45-58 / 58 or more | 11 / 209 / 15 / 34 | | Draws a frame: windows under 45 fps / 45 fps or more | 1131 / 544 on average | | `GPU time` a frame | 17.8 ms on average, 28.5 ms at most | | `GPU-bound wait` | 0.02 ms on average | | CPU used by all threads over 5% | 3.1 cores of 6 on average | | sead::MainThread / Presentation Thread | 29% (max 44%) / 34% (max 55%) of a core | No thread is near 100% in gameplay and the CPU waits almost nothing for the GPU, but the GPU is busy about three quarters of the frame. At start-up, 12 unnamed threads sit at 96-100% for a long stretch (shader and pipeline preparation using every logical core). **2. Separate run with `RX_VK_PASS_TIMING=1`** (20 reports of 600 presents, Frame generation Off from start to end) | | Fast stretch (8 reports) | Slow stretch (10 reports, later in the run) | |---|---|---| | fps | 55.9-60.1 | 32.4-38.6 | | draws a frame | 168-1814 | 864-2117 | | GPU ms a frame, all spans | 6.4-13.3 | 17.8-25.7 | | `wb` (write-back, mirror -> guest memory) | 0.43-0.92 ms (7-8%) | **8.6-11.1 ms (49-55%)** | | `wb` data a frame | about 25-31 MB (blocks I checked) | 54-69 MB | | `pass` (the draws) | 3.3-7.2 ms (61-65%) | 4.4-7.2 ms (25-33%) | | `dispatch` | 0.7-2.0 ms | 1.7-2.9 ms | - In the slow stretch, `wb` copies 54-69 MB a frame and takes about half of the GPU time. The time in the draws barely changes (a fast block with 1814 draws ran at 55.9 fps; a slow one with 864 draws ran at 38.6 fps), so the draw count alone does not explain the drop. - This matches the README's note on `RX_VK_WB_MAX_KB` (an RTX 3080 in Hyrule Field: about 66 MB and 4.4 ms a submit, 45-48 fps; 59-60 fps with `RX_VK_WB_MAX_KB=1024`). - That switch is in main since 30911ca ("perf(vk): skip write-backs of large GPU-written bindings"), but not in v2.0.14: GitHub's compare of v2.0.14...30911ca says diverged, 54 ahead, and I added `RX_VK_WB_MAX_KB=1024` to `hookshot_env.txt` on v2.0.14 and the log has no `vk: write-backs only of written bindings up to ...` line. So I could not test it. I will test it when a release has it. **Caveats** - Each timestamp waits for the GPU, so profiled spans add up to more than an unprofiled frame (the log says so): read the shares, not the absolute ms. - One report I read in full says `5251 spans not timed (a command buffer's timestamps ran out)`. - This is a correlation, not proof that `wb` causes the drop. I do not know which area of the game each stretch is. - The log does not mark when the stutters happened, so I cannot tie the slow windows to what I felt. No fix asked: this is a hardware data point for the CPU-bound / low-GPU-usage family, with the write-back share as the finding. Logs attached: `hookshot_log_unprofiled_fg-off.txt` (part 1) and `hookshot_log_pass-timing.txt` (part 2), with the Windows user name in file paths replaced.
sena-senpai commented 2026-11-10 21:31:55 +00:00 (Migrated from github.com)

v2.0.14 log attached (from #1925). RTX 5060 Laptop 8 GB (driver 621.14), Core 7 240H, 16 GB, Windows 11.
Output 1920x1080, Render resolution Auto, Frame generation Off, cap 60. ~15 min in Castle Town and Kakariko.

  • Game fps: median 39.6, lowest 10% 27.4

  • GPU-bound wait: 0.0 ms median

  • Replay submit: median 16.8 ms, p90 28.1 ms

  • Memory sync: median 7.9 ms, p90 17.8 ms, max 74 ms

v2.0.13, same settings and areas: submit 19.1 / 28.7 ms, memory sync 8.9 / 16.6 ms, fps median 37.6.

hookshot_log_v2.0.14.txt

v2.0.14 log attached (from #1925). RTX 5060 Laptop 8 GB (driver 621.14), Core 7 240H, 16 GB, Windows 11. Output 1920x1080, Render resolution Auto, Frame generation Off, cap 60. ~15 min in Castle Town and Kakariko. - Game fps: median 39.6, lowest 10% 27.4 - GPU-bound wait: 0.0 ms median - Replay submit: median 16.8 ms, p90 28.1 ms - Memory sync: median 7.9 ms, p90 17.8 ms, max 74 ms v2.0.13, same settings and areas: submit 19.1 / 28.7 ms, memory sync 8.9 / 16.6 ms, fps median 37.6. [hookshot_log_v2.0.14.txt](https://github.com/user-attachments/files/34652050/hookshot_log_v2.0.14.txt)
lucasmazzoni45-del commented 2026-11-10 22:41:47 +00:00 (Migrated from github.com)

Stability data from a 112-minute real session (RX 7800 XT, Ryzen 7 5800X3D, 2560x1440, FG, v2.0.12 plus #1130 and my own non-stock hookshot_vk.dll with larger image-pool preallocation and capture-buffer size classes), analysed from hookshot_log.txt:

  • Mean 58.6 fps, 5% worst 52.5 fps, GPU 5.7 ms/frame.
  • 35 hitches of 40 ms or more (7 of 100 ms or more, worst 408 ms), about 3 per 10 minutes.
  • Of 79 logged slow submits: 53 dominated by draw, 11 by dispatch, 8 by memory sync.
  • 19 log lines "a draw waited 59-306 ms for a pipeline". This is the biggest hitch source still open on this machine, and exactly the case #1946 (quick_pipeline) targets.

I hand-ported #1946's quick_pipeline onto v2.0.12 together with #1637, #1821, #1438, #1133 and the uniform-word cache fixes (diffs available on request), and kept the shader-cache stamp unchanged so the program cache stays valid. Scripted headless run, 150 s, same input each time: mean 59.7 / 59.8 fps, min 57.5 / 57.6, versus 59.0 / 59.3 mean and min 27.8 / 43.5 with my previous build. No regression, fewer low minimums.

Not verified: the scripted route never hit a first-seen effect, so the "a waiting draw's pipeline built without the driver's optimizer" line never appeared and the gain on the 59-306 ms waits is unmeasured. Zone-load hitches and the other ported commits are also untested in real play. I will post a real-play log when I have one.

Stability data from a 112-minute real session (RX 7800 XT, Ryzen 7 5800X3D, 2560x1440, FG, v2.0.12 plus #1130 and my own non-stock `hookshot_vk.dll` with larger image-pool preallocation and capture-buffer size classes), analysed from `hookshot_log.txt`: - Mean 58.6 fps, 5% worst 52.5 fps, GPU 5.7 ms/frame. - 35 hitches of 40 ms or more (7 of 100 ms or more, worst 408 ms), about 3 per 10 minutes. - Of 79 logged slow submits: 53 dominated by `draw`, 11 by `dispatch`, 8 by `memory sync`. - 19 log lines "a draw waited 59-306 ms for a pipeline". This is the biggest hitch source still open on this machine, and exactly the case #1946 (`quick_pipeline`) targets. I hand-ported #1946's `quick_pipeline` onto v2.0.12 together with #1637, #1821, #1438, #1133 and the uniform-word cache fixes (diffs available on request), and kept the shader-cache stamp unchanged so the program cache stays valid. Scripted headless run, 150 s, same input each time: mean 59.7 / 59.8 fps, min 57.5 / 57.6, versus 59.0 / 59.3 mean and min 27.8 / 43.5 with my previous build. No regression, fewer low minimums. **Not verified:** the scripted route never hit a first-seen effect, so the "a waiting draw's pipeline built without the driver's optimizer" line never appeared and the gain on the 59-306 ms waits is unmeasured. Zone-load hitches and the other ported commits are also untested in real play. I will post a real-play log when I have one.
benjdidj commented 2026-11-10 22:43:29 +00:00 (Migrated from github.com)

I have a 19 13900hx, with Rtx 4070 8g, 32g de ram. Cachyos.
When i start the game its pretty good 15 mns it gets unplayable, gets more and more laggy.
can go from 40/50 to 20/30 pretty fast.

hookshot_log.1.txt
hookshot_log.txt

sometimes it feels going in and out of zone dont help.

I have a 19 13900hx, with Rtx 4070 8g, 32g de ram. Cachyos. When i start the game its pretty good 15 mns it gets unplayable, gets more and more laggy. can go from 40/50 to 20/30 pretty fast. [hookshot_log.1.txt](https://github.com/user-attachments/files/34654428/hookshot_log.1.txt) [hookshot_log.txt](https://github.com/user-attachments/files/34654452/hookshot_log.txt) sometimes it feels going in and out of zone dont help.
lucasmazzoni45-del commented 2026-11-10 23:18:34 +00:00 (Migrated from github.com)

Second data set from my RX 7800 XT / 5800X3D logs (v2.0.12 + #1130, non-stock DLL), on two things I could not find already written up here:

1. The "111 failed" programs in the v2.0.12 prepare log (earlier builds: 0). I re-ran --prepare-shaders with the full log:

  • 68 are "a network left out on this card" (the movie networks, deliberately excluded on non-NVIDIA).
  • 43 do not translate: 23 compute, 12 task/mesh, 7 fragment, 1 vertex. Reasons: SULD.P 1D (17), IMNMX with a wide immediate (3), TLD.MS (3), scratch register (3), LDG/STG [URn+imm] (2), no EXIT (1), ISBERD.O (1).
  • All of these already have a fix in main (lower_to_maxwell.h). Cherry-picking 8 of those lowering commits onto v2.0.12 took the failed count from 111 to 72 (68 intended + 4 left) and compute variants from 10095 to 11125. I left out d0f3511 (scratch register): with it --prepare-shaders crashes with 0xc0000409 on my build, cause not found. In play the log only shows "10 failed" (the movie networks), so I cannot say the 43 are used in normal scenes.

2. Skipped draws as a possible cause of missing or flickering grass and bushes. In a 112-minute session about 960 new pipelines were compiled in play (265 s of compile, 100-1500 ms each on AMD). Draws whose pipeline is not ready are skipped by design: 143 of 3367 log windows had skipped draws, up to 30 per frame on average in one burst. That fits grass and bushes appearing and disappearing in bursts. This is a hypothesis, not proven on foliage. Two untested things to try: RX_VK_PIPE_SKIP=0 (wait for the pipeline: stutter instead of holes) and RX_VK_WAIT_GPU=1. Compile queue depth was 0 during this, so more workers would not help.

Also still unexplained: one program (da7a7ab28b8e73d9, stage 1) logs "storage buffer NOT bound (reads zeros)" in every session, including around the time of the sky blocks in #1130; the bound address looks like a float, not a pointer. And with texture budget 6144 MB and about 5.8 GB of textures resident, the VRAM was 12.2 of 14.3 GB; a long session with RX_VK_LOG_REMADE=1 is needed to see what gets evicted.

Not verified in real play: none of the texture fixes I ported (#1672, #1568, #1133, #1505, #1637 and others) were exercised by my scripted route.

Second data set from my RX 7800 XT / 5800X3D logs (v2.0.12 + #1130, non-stock DLL), on two things I could not find already written up here: **1. The "111 failed" programs in the v2.0.12 prepare log (earlier builds: 0).** I re-ran `--prepare-shaders` with the full log: - 68 are "a network left out on this card" (the movie networks, deliberately excluded on non-NVIDIA). - 43 do not translate: 23 compute, 12 task/mesh, 7 fragment, 1 vertex. Reasons: `SULD.P` 1D (17), `IMNMX` with a wide immediate (3), `TLD.MS` (3), scratch register (3), `LDG/STG [URn+imm]` (2), no `EXIT` (1), `ISBERD.O` (1). - All of these already have a fix in main (`lower_to_maxwell.h`). Cherry-picking 8 of those lowering commits onto v2.0.12 took the failed count from 111 to 72 (68 intended + 4 left) and compute variants from 10095 to 11125. I left out d0f3511 (scratch register): with it `--prepare-shaders` crashes with 0xc0000409 on my build, cause not found. In play the log only shows "10 failed" (the movie networks), so I cannot say the 43 are used in normal scenes. **2. Skipped draws as a possible cause of missing or flickering grass and bushes.** In a 112-minute session about 960 new pipelines were compiled in play (265 s of compile, 100-1500 ms each on AMD). Draws whose pipeline is not ready are skipped by design: 143 of 3367 log windows had skipped draws, up to 30 per frame on average in one burst. That fits grass and bushes appearing and disappearing in bursts. **This is a hypothesis, not proven on foliage.** Two untested things to try: `RX_VK_PIPE_SKIP=0` (wait for the pipeline: stutter instead of holes) and `RX_VK_WAIT_GPU=1`. Compile queue depth was 0 during this, so more workers would not help. Also still unexplained: one program (`da7a7ab28b8e73d9`, stage 1) logs "storage buffer NOT bound (reads zeros)" in every session, including around the time of the sky blocks in #1130; the bound address looks like a float, not a pointer. And with texture budget 6144 MB and about 5.8 GB of textures resident, the VRAM was 12.2 of 14.3 GB; a long session with `RX_VK_LOG_REMADE=1` is needed to see what gets evicted. Not verified in real play: none of the texture fixes I ported (#1672, #1568, #1133, #1505, #1637 and others) were exercised by my scripted route.
lucasmazzoni45-del commented 2026-11-10 23:23:14 +00:00 (Migrated from github.com)

Summary of the measurements behind my earlier comments here (RX 7800 XT, Ryzen 7 5800X3D, 2560x1440 windowed, FG on, v2.0.12 + #1130, non-stock hookshot_vk.dll). Scripted runs use the same HOS_INPUT_SCRIPT each time; one or two runs per build and scenes not identical, so this is an indication, not a benchmark. Not yet tested in a long real play session.

Stutter pass: #1946 quick_pipeline hand-ported + #1637, #1821, #1438, #1133, uniform-word cache, flat texture-answer table, 6 worker threads (16+ hw threads), 150 s run

build mean fps 5% worst min hitches >= 40 ms
my previous build, run a / b 59.0 / 59.3 58.4 / 58.2 27.8 / 43.5 11 / 9
my earlier stutter build 55.4 49.9 48.6 14
ported build, run a / b 59.7 / 59.8 58.1 / 58.1 57.5 / 57.6 10 / 9

Real 112-minute session on the previous build: mean 58.6 fps, 5% worst 52.5, GPU 5.7 ms, 35 hitches >= 40 ms (worst 408 ms), 53 of 79 slow submits dominated by draw, 19 "a draw waited 59-306 ms for a pipeline".

Texture / shader pass (upstream cherry-picks only): the no-translator-change build ran 58.8 fps mean, 0 errors, 0 device lost; the build with 8 lower_to_maxwell.h fixes ran 60.0 fps mean (min 59.6), 0 errors, failed programs 111 -> 72, compute variants 10095 -> 11125 (cache must be rebuilt).

Not proven: the quick_pipeline gain on the long waits (the scripted route never produced a first-seen effect), any of the texture fixes (compiled and started, not exercised), skipped draws as the cause of missing grass, and the cause of the sky blocks (#1130), one-frame black frames and texture eviction.

I have the diffs (source only) and the session logs and can attach them on request or to a PR if the maintainers want them.

Summary of the measurements behind my earlier comments here (RX 7800 XT, Ryzen 7 5800X3D, 2560x1440 windowed, FG on, v2.0.12 + #1130, non-stock `hookshot_vk.dll`). Scripted runs use the same `HOS_INPUT_SCRIPT` each time; one or two runs per build and scenes not identical, so this is an indication, not a benchmark. **Not yet tested in a long real play session.** **Stutter pass: #1946 `quick_pipeline` hand-ported + #1637, #1821, #1438, #1133, uniform-word cache, flat texture-answer table, 6 worker threads (16+ hw threads), 150 s run** | build | mean fps | 5% worst | min | hitches >= 40 ms | |---|---|---|---|---| | my previous build, run a / b | 59.0 / 59.3 | 58.4 / 58.2 | 27.8 / 43.5 | 11 / 9 | | my earlier stutter build | 55.4 | 49.9 | 48.6 | 14 | | ported build, run a / b | 59.7 / 59.8 | 58.1 / 58.1 | 57.5 / 57.6 | 10 / 9 | Real 112-minute session on the previous build: mean 58.6 fps, 5% worst 52.5, GPU 5.7 ms, 35 hitches >= 40 ms (worst 408 ms), 53 of 79 slow submits dominated by `draw`, 19 "a draw waited 59-306 ms for a pipeline". **Texture / shader pass (upstream cherry-picks only):** the no-translator-change build ran 58.8 fps mean, 0 errors, 0 device lost; the build with 8 `lower_to_maxwell.h` fixes ran 60.0 fps mean (min 59.6), 0 errors, failed programs 111 -> 72, compute variants 10095 -> 11125 (cache must be rebuilt). **Not proven:** the `quick_pipeline` gain on the long waits (the scripted route never produced a first-seen effect), any of the texture fixes (compiled and started, not exercised), skipped draws as the cause of missing grass, and the cause of the sky blocks (#1130), one-frame black frames and texture eviction. I have the diffs (source only) and the session logs and can attach them on request or to a PR if the maintainers want them.
lucasmazzoni45-del commented 2026-11-10 23:26:27 +00:00 (Migrated from github.com)

Where the changes are (all under gpu/vk/ of v2.0.12 + #1130; my own build, not stock), so anyone can look at the exact spots. Everything below is source-only and I can attach the full diffs or open a PR if wanted.

Stutter pass (stutter build -> perf2), the parts that matter

  • vk_draw.cpp, build_graphics() / graphics_pipeline() / draw(): hand port of #1946 quick_pipeline (switch RX_VK_QUICK_WAIT=0 = old wait). Also skip_report_impl() (the "draws left out" counters) and program_pick().
  • vk_jobs.cpp, worker(): 6 worker threads when the machine has 16+ hardware threads (RX_VK_WORKERS=<n>), and new programs queued as urgent.
  • vk_memory.cpp: gpu_buffer_bind() (#1133 direct-buffer-write bookkeeping), shadow_room() / shadow_make() / shadows_reap() (#1637 shadows by allocated memory), ub_read() + struct UbWord (uniform-word cache).
  • vk_vma.cpp, images_pool(), create_image(), create_device_buffer(): image pool preallocation (RX_VK_IMAGE_PREALLOC, 15% of VRAM) and capture-buffer size classes; vram_compact.h / vram_pressure.h carry #1821 VRAM compaction.
  • vk_shader.cpp, descriptor_handle(): flat texture-answer table. To keep the program cache valid I forced the old shader stamp in nx2/build.py (env override RX_SHADER_STAMP_FORCE, not in the diff); a vk_shader.cpp change otherwise makes the game drop the whole program cache.
  • vk_texture.cpp / vk_texture.h: texture_frame(), Texture::create() (#1438 AMD ASTC), struct Texture fields.

Texture / rendering pass (cherry-picks from upstream, resolved against #1130)

  • vk_texture.cpp: upload_astc_gpu() (#1505 failed BC3 upload retried instead of noise), sampler_make() (failed samplers not accepted), texture_touch(), texture_read_texels(), forget_gpu(), texture_frame(); new evict_reason.h (PR1879 diagnostics: budget / drawn over / dead targets / "goes with no copy of its texels").
  • vk_memory.cpp: staging() (#1672 staging published before the GPU read), memory_end_submission(), memory_collect_written(), memory_cpu_owned(), import_pool() / import_dma_buf_pieces() (#1434 CPU write watches kept across imports), shadow_forget(), stamp_page(), copy_pages(), reap_mirrors(), bg_drain().
  • vk_replay.cpp, replay(): write-back stream (#1568). host/nvn_layout.c, nvn_tex_layout() / nvn_tex_level_tiles(): texture storage size in 64 bits (the host part needs an exe rebuild, so my renderer-only build only has the vk part).
  • vk_draw.cpp: my diagnostic line "new pipeline in play ..." (RX_VK_LOG_NEWPIPE=0 turns it off), to see which programs/states are first seen in real play.

Shader lowering (the 111 -> 72 failed programs; changes the translator, so the program cache must be rebuilt)

  • eden/shader_recompiler/frontend/ampere/lower_to_maxwell.h, LowerToMaxwellWith(): the main fixes (SULD.P 1D, IMNMX wide immediate, TLD.MS, LDG/STG [URn+imm], missing EXIT).
  • frontend/maxwell/translate/impl/internal_stage_buffer_entry_read.cpp, ISBE_nx2() / ISBERD() plus ir_emitter.cpp IsbeRead(), ir/opcodes.* and backend/spirv/emit_spirv_mesh.cpp EmitIsbeRead(): the ISBERD.O path.
  • Left out: d0f3511 (scratch register). With it --prepare-shaders crashes (0xc0000409) on my build, cause not found.

Test logs (headless 150 s scripted route, same input each time) show the build lines vk: device 0: AMD Radeon RX 7800 XT ... mesh shaders yes ... pipeline libraries yes, 46566 program keys kept (the cache stayed valid with the forced stamp), and zero device lost / errors in the ported build. The "a waiting draw's pipeline built without the driver's optimizer" line never appears in them, i.e. the quick_pipeline stand-in path was not exercised yet. I will post a real-play log for that.

Where the changes are (all under `gpu/vk/` of v2.0.12 + #1130; my own build, not stock), so anyone can look at the exact spots. Everything below is source-only and I can attach the full diffs or open a PR if wanted. **Stutter pass (stutter build -> perf2), the parts that matter** - `vk_draw.cpp`, `build_graphics()` / `graphics_pipeline()` / `draw()`: hand port of #1946 `quick_pipeline` (switch `RX_VK_QUICK_WAIT=0` = old wait). Also `skip_report_impl()` (the "draws left out" counters) and `program_pick()`. - `vk_jobs.cpp`, `worker()`: 6 worker threads when the machine has 16+ hardware threads (`RX_VK_WORKERS=<n>`), and new programs queued as urgent. - `vk_memory.cpp`: `gpu_buffer_bind()` (#1133 direct-buffer-write bookkeeping), `shadow_room()` / `shadow_make()` / `shadows_reap()` (#1637 shadows by allocated memory), `ub_read()` + `struct UbWord` (uniform-word cache). - `vk_vma.cpp`, `images_pool()`, `create_image()`, `create_device_buffer()`: image pool preallocation (`RX_VK_IMAGE_PREALLOC`, 15% of VRAM) and capture-buffer size classes; `vram_compact.h` / `vram_pressure.h` carry #1821 VRAM compaction. - `vk_shader.cpp`, `descriptor_handle()`: flat texture-answer table. To keep the program cache valid I forced the old shader stamp in `nx2/build.py` (env override `RX_SHADER_STAMP_FORCE`, not in the diff); a `vk_shader.cpp` change otherwise makes the game drop the whole program cache. - `vk_texture.cpp` / `vk_texture.h`: `texture_frame()`, `Texture::create()` (#1438 AMD ASTC), `struct Texture` fields. **Texture / rendering pass (cherry-picks from upstream, resolved against #1130)** - `vk_texture.cpp`: `upload_astc_gpu()` (#1505 failed BC3 upload retried instead of noise), `sampler_make()` (failed samplers not accepted), `texture_touch()`, `texture_read_texels()`, `forget_gpu()`, `texture_frame()`; new `evict_reason.h` (PR1879 diagnostics: budget / drawn over / dead targets / "goes with no copy of its texels"). - `vk_memory.cpp`: `staging()` (#1672 staging published before the GPU read), `memory_end_submission()`, `memory_collect_written()`, `memory_cpu_owned()`, `import_pool()` / `import_dma_buf_pieces()` (#1434 CPU write watches kept across imports), `shadow_forget()`, `stamp_page()`, `copy_pages()`, `reap_mirrors()`, `bg_drain()`. - `vk_replay.cpp`, `replay()`: write-back stream (#1568). `host/nvn_layout.c`, `nvn_tex_layout()` / `nvn_tex_level_tiles()`: texture storage size in 64 bits (the host part needs an exe rebuild, so my renderer-only build only has the vk part). - `vk_draw.cpp`: my diagnostic line "new pipeline in play ..." (`RX_VK_LOG_NEWPIPE=0` turns it off), to see which programs/states are first seen in real play. **Shader lowering (the 111 -> 72 failed programs; changes the translator, so the program cache must be rebuilt)** - `eden/shader_recompiler/frontend/ampere/lower_to_maxwell.h`, `LowerToMaxwellWith()`: the main fixes (`SULD.P` 1D, `IMNMX` wide immediate, `TLD.MS`, `LDG/STG [URn+imm]`, missing `EXIT`). - `frontend/maxwell/translate/impl/internal_stage_buffer_entry_read.cpp`, `ISBE_nx2()` / `ISBERD()` plus `ir_emitter.cpp` `IsbeRead()`, `ir/opcodes.*` and `backend/spirv/emit_spirv_mesh.cpp` `EmitIsbeRead()`: the `ISBERD.O` path. - Left out: d0f3511 (scratch register). With it `--prepare-shaders` crashes (0xc0000409) on my build, cause not found. **Test logs (headless 150 s scripted route, same input each time)** show the build lines `vk: device 0: AMD Radeon RX 7800 XT ... mesh shaders yes ... pipeline libraries yes`, `46566 program keys kept` (the cache stayed valid with the forced stamp), and zero `device lost` / errors in the ported build. The "a waiting draw's pipeline built without the driver's optimizer" line never appears in them, i.e. the `quick_pipeline` stand-in path was not exercised yet. I will post a real-play log for that.
xzx456490-wq commented 2026-11-11 00:33:44 +00:00 (Migrated from github.com)

RTX 4060 Laptop (8 GB) on Windows — about 17 FPS in an open outdoor area

My laptop is a Lenovo Legion R9000P 2023 with an AMD Ryzen 9 7945HX, an NVIDIA GeForce RTX 4060 Laptop GPU (8 GB VRAM), and 32 GB of RAM. I was testing around Hookshot v2.0.12–v2.0.13. I couldn't confirm the exact build for the 17 FPS reading.

I previously saw about 50 FPS without frame generation and about 100 FPS with 2× frame generation in Kokiri Forest. In a later open outdoor area I saw about 17 FPS. These were observations from different scenes.

A separate Windows Task Manager screenshot showed 43% GPU utilization, 7.3/8.0 GB dedicated GPU memory, 4.6/15.8 GB shared GPU memory, 12.0/23.8 GB total GPU memory, and a GPU temperature of 53 °C. This screenshot was taken at a different time from the FPS reading. The memory figures are for the whole GPU, so I can't determine from these readings whether VRAM pressure contributed to the slowdown.

I also saw rainbow textures in an earlier build, similar to #993. I don't have a hookshot_log.txt for this run.

Could you look into the outdoor frame-rate drop on this configuration? Thanks for your work on Hookshot.

**RTX 4060 Laptop (8 GB) on Windows — about 17 FPS in an open outdoor area** My laptop is a Lenovo Legion R9000P 2023 with an AMD Ryzen 9 7945HX, an NVIDIA GeForce RTX 4060 Laptop GPU (8 GB VRAM), and 32 GB of RAM. I was testing around Hookshot v2.0.12–v2.0.13. I couldn't confirm the exact build for the 17 FPS reading. I previously saw about 50 FPS without frame generation and about 100 FPS with 2× frame generation in Kokiri Forest. In a later open outdoor area I saw about 17 FPS. These were observations from different scenes. A separate Windows Task Manager screenshot showed 43% GPU utilization, 7.3/8.0 GB dedicated GPU memory, 4.6/15.8 GB shared GPU memory, 12.0/23.8 GB total GPU memory, and a GPU temperature of 53 °C. This screenshot was taken at a different time from the FPS reading. The memory figures are for the whole GPU, so I can't determine from these readings whether VRAM pressure contributed to the slowdown. I also saw rainbow textures in an earlier build, similar to #993. I don't have a `hookshot_log.txt` for this run. Could you look into the outdoor frame-rate drop on this configuration? Thanks for your work on Hookshot.
D4rkGh057 commented 2026-11-11 05:09:20 +00:00 (Migrated from github.com)

Follow-up on my earlier data point: v2.1.0 includes RX_VK_WB_MAX_KB, and write-back is no longer half of the GPU time

Same PC as before (Ryzen 5 5600XT, RTX 4060, Windows 11), now on v2.1.0 (source 633eb435), Frame generation Auto, Frame pacing On, Reflex On. hookshot_env.txt has only RX_VK_WB_MAX_KB=1024; the log shows vk: write-backs only of written bindings up to 1024 KB (RX_VK_WB_MAX_KB).

1. Write-back share (RX_VK_PASS_TIMING=1, 15 reports of 600 presents that had a wb line)

v2.0.14, no limit (my earlier comment, slow stretch) v2.1.0, RX_VK_WB_MAX_KB=1024
wb a frame 8.6-11.1 ms 0.005-1.8 ms
wb share of GPU time 49-55% 0.1-14.5% (under 8.2% in 11 of 15)
wb data a frame 54-69 MB 0-10.4 MB

pass (the draws) is now 46-60% of the GPU time and dispatch 17-21%. The three highest wb shares (10.5-14.5%) are in the last reports of the run, with about 10 MB a frame, i.e. bindings under 1 MB that the limit still copies.

2. Frame rates (no profiling)

  • A 40-minute session (245 windows of 600 presents): 52.2 fps on average, 4 windows under 30 fps, up to 3372 draws a frame. Windows with 2400 draws or more: 44.1 fps on average (28 windows).
  • No device loss, no LOW MEMORY, 0 failed in every window.
  • RX_VK_FSR_FG=0 removed from hookshot_env.txt: no HUD ghosting or doubling, no fsr3fi lines in the log.

Caveats

  • This is not a controlled A/B. I ran the same route (Kokiri Forest entrance to the tunnels) with and without the limit, but neither pass reached the heaviest scenes (about 3100 draws), so the fps difference between them is inside the run-to-run noise (about 4 fps between two runs with identical settings).
  • The only clear gain I saw (about 47-52 fps against 29-40 at about 3100 draws) comes from one run each side and a different mix of scenes, so I do not claim it.
  • What is solid is the mechanism: with the limit, the write-back stops being half of the GPU time.

Logs available if useful, with the Windows user name replaced in file paths.

**Follow-up on my earlier data point: v2.1.0 includes `RX_VK_WB_MAX_KB`, and write-back is no longer half of the GPU time** Same PC as before (Ryzen 5 5600XT, RTX 4060, Windows 11), now on v2.1.0 (source 633eb435), Frame generation Auto, Frame pacing On, Reflex On. `hookshot_env.txt` has only `RX_VK_WB_MAX_KB=1024`; the log shows `vk: write-backs only of written bindings up to 1024 KB (RX_VK_WB_MAX_KB)`. **1. Write-back share (`RX_VK_PASS_TIMING=1`, 15 reports of 600 presents that had a `wb` line)** | | v2.0.14, no limit (my earlier comment, slow stretch) | v2.1.0, `RX_VK_WB_MAX_KB=1024` | |---|---|---| | `wb` a frame | 8.6-11.1 ms | 0.005-1.8 ms | | `wb` share of GPU time | 49-55% | 0.1-14.5% (under 8.2% in 11 of 15) | | `wb` data a frame | 54-69 MB | 0-10.4 MB | `pass` (the draws) is now 46-60% of the GPU time and `dispatch` 17-21%. The three highest `wb` shares (10.5-14.5%) are in the last reports of the run, with about 10 MB a frame, i.e. bindings under 1 MB that the limit still copies. **2. Frame rates (no profiling)** - A 40-minute session (245 windows of 600 presents): 52.2 fps on average, 4 windows under 30 fps, up to 3372 draws a frame. Windows with 2400 draws or more: 44.1 fps on average (28 windows). - No device loss, no `LOW MEMORY`, `0 failed` in every window. - `RX_VK_FSR_FG=0` removed from `hookshot_env.txt`: no HUD ghosting or doubling, no `fsr3fi` lines in the log. **Caveats** - This is not a controlled A/B. I ran the same route (Kokiri Forest entrance to the tunnels) with and without the limit, but neither pass reached the heaviest scenes (about 3100 draws), so the fps difference between them is inside the run-to-run noise (about 4 fps between two runs with identical settings). - The only clear gain I saw (about 47-52 fps against 29-40 at about 3100 draws) comes from one run each side and a different mix of scenes, so I do not claim it. - What is solid is the mechanism: with the limit, the write-back stops being half of the GPU time. Logs available if useful, with the Windows user name replaced in file paths.
defmonk0 commented 2026-11-11 05:57:27 +00:00 (Migrated from github.com)

Ryzen 9 5900X @3.7 Ghz, boost to ~4.5 Ghz, sitting around 30% utilization
RTX 4090, 24G VRAM, also at about 30% utilization
32GB DDR4 @3600 Mhz
EDIT: My XMP profile lapsed at some point; I was running the ram at 2133 Mhz. After fixing that, I got a ~15% boost to around 40fps. Relevant, but seemingly not the whole cause. Maybe people getting 60+ native are using DDR5, and this is extremely memory speed dependant?

Just hanging in Kokiri Forest, near the entrance to the Deku Tree's area in front of Mido, looking back at the rest of the village. Getting around 35 fps just standing there. Changing resolution, DLSS scale, etc, doesn't generally make a difference.

2026 11 11-00 54 00 2026 11 11-00 52 36

hookshot_log.txt
hookshot_options.ini: https://pastebin.com/6b8XRhKX

Ryzen 9 5900X @3.7 Ghz, boost to ~4.5 Ghz, sitting around 30% utilization RTX 4090, 24G VRAM, also at about 30% utilization 32GB DDR4 @3600 Mhz **_EDIT: My XMP profile lapsed at some point; I was running the ram at 2133 Mhz. After fixing that, I got a ~15% boost to around 40fps. Relevant, but seemingly not the whole cause. Maybe people getting 60+ native are using DDR5, and this is extremely memory speed dependant?_** Just hanging in Kokiri Forest, near the entrance to the Deku Tree's area in front of Mido, looking back at the rest of the village. Getting around 35 fps just standing there. Changing resolution, DLSS scale, etc, doesn't generally make a difference. <img width="3440" height="1440" alt="2026 11 11-00 54 00" src="https://github.com/user-attachments/assets/357c2b35-4b5c-49b5-9a41-0d7fc04a94e5" /> <img width="3440" height="1440" alt="2026 11 11-00 52 36" src="https://github.com/user-attachments/assets/d74f57c4-c3ba-4b58-9465-ab246a715da9" /> [hookshot_log.txt](https://github.com/user-attachments/files/34672662/hookshot_log.txt) hookshot_options.ini: https://pastebin.com/6b8XRhKX
benjdidj commented 2026-11-11 14:37:51 +00:00 (Migrated from github.com)

OS: CachyOS x86_64
Host: Predator PHN16-71 (V1.18)
Kernel: Linux 7.3.3-1-cachyos
Display (BOE0A2C): 2560x1600 @ 1.5x in 16", 165 Hz [Built-in]
Display (LC27G5xT): 2560x1440 in 27", 144 Hz [External] *
CPU: 13th Gen Intel(R) Core(TM) i9-13900HX (16+16) @ 5.40 GHz
GPU: NVIDIA GeForce RTX 4070 Max-Q / Mobile 8g [Discrete]
Memory: 20.5 GiB / 31.05 GiB (14%)

I get a pretty big ram usage while playing.
Gpu usage not max, cpu pretty low.

Frames were very good in forest castle, but in cokiri village and forest. very laggy, 30-45 Fps without framegen but felt worse.

Thanks for Your work.

Capture d'écran_20261111_165318 Capture d'écran_20261111_154736

hookshot_log.txt

Edit : 8g Vram video card

OS: CachyOS x86_64 Host: Predator PHN16-71 (V1.18) Kernel: Linux 7.3.3-1-cachyos Display (BOE0A2C): 2560x1600 @ 1.5x in 16", 165 Hz [Built-in] Display (LC27G5xT): 2560x1440 in 27", 144 Hz [External] * CPU: 13th Gen Intel(R) Core(TM) i9-13900HX (16+16) @ 5.40 GHz GPU: NVIDIA GeForce RTX 4070 Max-Q / Mobile 8g [Discrete] Memory: 20.5 GiB / 31.05 GiB (14%) I get a pretty big ram usage while playing. Gpu usage not max, cpu pretty low. Frames were very good in forest castle, but in cokiri village and forest. very laggy, 30-45 Fps without framegen but felt worse. Thanks for Your work. <img width="1697" height="29" alt="Capture d&#39;écran_20261111_165318" src="https://github.com/user-attachments/assets/f0deca7b-9724-4a88-aefb-4c8903d27d66" /> <img width="2560" height="1439" alt="Capture d&#39;écran_20261111_154736" src="https://github.com/user-attachments/assets/249d9f4e-351c-4ac5-b7d1-db83ef965e36" /> [hookshot_log.txt](https://github.com/user-attachments/files/34692210/hookshot_log.txt) Edit : 8g Vram video card
lucasmazzoni45-del commented 2026-11-11 17:17:39 +00:00 (Migrated from github.com)

The raw log lines behind my earlier comments here, as text, so they can be checked without downloading anything. RX 7800 XT (driver 0x8001a0), Ryzen 7 5800X3D, Windows 11, v2.0.12 + #1130, my own non-stock hookshot_vk.dll. Scripted runs, one or two per build, scenes not identical: an indication, not a benchmark. Local paths removed.

1. The "111 failed" in the v2.0.12 --prepare-shaders log (first start, empty program cache)

vk: program cache hookshot_programs.bin: 0 program keys kept, 0 KB
vk: prepared 38809 shader programs (46556 new, 111 failed, 10091 stage libraries; 10095 compute variants, 754 programs with texture variants, 65 at the limit) in 115.1 s

Reasons printed for the ones that do not translate (examples, same text as the log):

vk: NVN2 program 045df6f2e27d05e6 (stage 5) does not lower at +0x1c760: native program uses lowering scratch registers (native op 0x812, e7e77812 fffffffe 078ec0ff 000fc600)
vk: NVN2 program aa743d1af6f58645 (stage 5) does not lower at +0x10: native formatted surface load mode (native op 0xb97, 00007b97 00001000 001e0100 000f6200)
vk: NVN2 program 9d515c29a359def1 (stage 5) does not lower at +0x10: native formatted surface load mode (native op 0xb97, 00007b97 00001000 001e0100 000f6200)

Count by reason: 68 "a network left out on this card" (movie networks, excluded on purpose on non-NVIDIA) + 43 that do not lower (SULD.P 1D 17, IMNMX wide immediate 3, TLD.MS 3, scratch register 3, LDG/STG [URn+imm] 2, no EXIT 1, ISBERD.O 1, others). With 8 lowering commits from main cherry-picked (not the scratch-register one: it crashed my prepare step with 0xc0000409) the count went 111 -> 72 (68 networks + 4).

2. Worst "slow submit" lines of two 150 s scripted runs on the same route

My previous build:

vk: slow submit: 360.7 ms: draw 59.0 descriptors 48.1 texture lookups 54.7 uploads 24.9 programs 2.9 commands 2.5 dispatch 22.8 queue submit 9.7
vk: slow submit: 151.9 ms: draw 62.4 descriptors 12.4 texture lookups 9.4 uploads 6.1 programs 46.4 commands 2.9 dispatch 34.8
vk: slow submit: 28.1 ms: memory sync 28.1 [memory sync: maps 0.0 (lock waits 0.0), capture 27.9, collect 0.2]
vk: slow submit: 69.6 ms: dispatch 69.6

perf2 build (#1946 quick_pipeline hand-port etc.):

vk: slow submit: 318.3 ms: draw 293.2 descriptors 273.7 texture lookups 37.5 uploads 14.3 programs 8.3 commands 4.0 dispatch 25.8 queue submit 11.2
vk: slow submit: 73.7 ms: dispatch 73.7
vk: slow submit: 197.1 ms: draw 372.8 descriptors 12.9 texture lookups 9.9 uploads 6.3 programs 58.5 commands 300.7 dispatch 43.9
vk: slow submit: 31.8 ms: draw 28.2 descriptors 8.1 texture lookups 5.7 uploads 3.9 programs 17.7 commands 1.9

Slow-submit line counts (>= 40 ms threshold of the log): 32 / 37 / 29 for my earlier builds, 28 / 26 for perf2. That is a small difference, within what two runs can give, and I do not claim the stutter is fixed. The 150-300 ms submits are still there in both.

Typical steady-state replay line in the perf2 run (CPU ms a frame):

vk: a frame's replay time (ms): submit 0.86, present 1.00, draw 0.04, descriptors 0.00, texture lookups 0.00, uploads 0.00, pipelines 0.00, programs 0.00, commands 0.03, dispatch 0.05, memory sync 0.48
vk: a frame's replay time (ms): submit 8.68, present 0.30, draw 1.67, descriptors 1.65, texture lookups 0.64, uploads 0.06, pipelines 0.00, programs 0.00, commands 0.01, dispatch 0.00, memory sync 0.63

3. Stock DLL against my DLL, same scripted route, v2.0.11, frame generation x2 (frame table from my runner)

run          frames  fps    p50 ms  p95 ms  p99 ms  max ms  >22ms %  >40ms n  submit CPU  draw CPU  mem sync  slow compiles
mine         14496   119.9  8.3     8.6     9.2     90.5    0        1        4.8         2.7       0.38      243
official     14233   118.0  8.3     8.6     9.5     1172    0.1      11       3.8         2.1       0.34      0

So on this route: same median and p95, my build has fewer long frames (1 against 11, max 90 ms against 1172 ms), but its CPU time a frame is higher. One run each, so the long frames may be a first-start effect (shader step) rather than the DLL.

I can attach the full logs if a maintainer wants them; I did not want to upload multi-MB files here without being asked.

The raw log lines behind my earlier comments here, as text, so they can be checked without downloading anything. RX 7800 XT (driver 0x8001a0), Ryzen 7 5800X3D, Windows 11, v2.0.12 + #1130, my own non-stock `hookshot_vk.dll`. Scripted runs, one or two per build, scenes not identical: an indication, not a benchmark. Local paths removed. **1. The "111 failed" in the v2.0.12 `--prepare-shaders` log (first start, empty program cache)** ``` vk: program cache hookshot_programs.bin: 0 program keys kept, 0 KB vk: prepared 38809 shader programs (46556 new, 111 failed, 10091 stage libraries; 10095 compute variants, 754 programs with texture variants, 65 at the limit) in 115.1 s ``` Reasons printed for the ones that do not translate (examples, same text as the log): ``` vk: NVN2 program 045df6f2e27d05e6 (stage 5) does not lower at +0x1c760: native program uses lowering scratch registers (native op 0x812, e7e77812 fffffffe 078ec0ff 000fc600) vk: NVN2 program aa743d1af6f58645 (stage 5) does not lower at +0x10: native formatted surface load mode (native op 0xb97, 00007b97 00001000 001e0100 000f6200) vk: NVN2 program 9d515c29a359def1 (stage 5) does not lower at +0x10: native formatted surface load mode (native op 0xb97, 00007b97 00001000 001e0100 000f6200) ``` Count by reason: 68 "a network left out on this card" (movie networks, excluded on purpose on non-NVIDIA) + 43 that do not lower (SULD.P 1D 17, IMNMX wide immediate 3, TLD.MS 3, scratch register 3, LDG/STG [URn+imm] 2, no EXIT 1, ISBERD.O 1, others). With 8 lowering commits from main cherry-picked (not the scratch-register one: it crashed my prepare step with 0xc0000409) the count went 111 -> 72 (68 networks + 4). **2. Worst "slow submit" lines of two 150 s scripted runs on the same route** My previous build: ``` vk: slow submit: 360.7 ms: draw 59.0 descriptors 48.1 texture lookups 54.7 uploads 24.9 programs 2.9 commands 2.5 dispatch 22.8 queue submit 9.7 vk: slow submit: 151.9 ms: draw 62.4 descriptors 12.4 texture lookups 9.4 uploads 6.1 programs 46.4 commands 2.9 dispatch 34.8 vk: slow submit: 28.1 ms: memory sync 28.1 [memory sync: maps 0.0 (lock waits 0.0), capture 27.9, collect 0.2] vk: slow submit: 69.6 ms: dispatch 69.6 ``` perf2 build (#1946 `quick_pipeline` hand-port etc.): ``` vk: slow submit: 318.3 ms: draw 293.2 descriptors 273.7 texture lookups 37.5 uploads 14.3 programs 8.3 commands 4.0 dispatch 25.8 queue submit 11.2 vk: slow submit: 73.7 ms: dispatch 73.7 vk: slow submit: 197.1 ms: draw 372.8 descriptors 12.9 texture lookups 9.9 uploads 6.3 programs 58.5 commands 300.7 dispatch 43.9 vk: slow submit: 31.8 ms: draw 28.2 descriptors 8.1 texture lookups 5.7 uploads 3.9 programs 17.7 commands 1.9 ``` Slow-submit line counts (>= 40 ms threshold of the log): 32 / 37 / 29 for my earlier builds, 28 / 26 for perf2. **That is a small difference, within what two runs can give, and I do not claim the stutter is fixed.** The 150-300 ms submits are still there in both. Typical steady-state replay line in the perf2 run (CPU ms a frame): ``` vk: a frame's replay time (ms): submit 0.86, present 1.00, draw 0.04, descriptors 0.00, texture lookups 0.00, uploads 0.00, pipelines 0.00, programs 0.00, commands 0.03, dispatch 0.05, memory sync 0.48 vk: a frame's replay time (ms): submit 8.68, present 0.30, draw 1.67, descriptors 1.65, texture lookups 0.64, uploads 0.06, pipelines 0.00, programs 0.00, commands 0.01, dispatch 0.00, memory sync 0.63 ``` **3. Stock DLL against my DLL, same scripted route, v2.0.11, frame generation x2 (frame table from my runner)** ``` run frames fps p50 ms p95 ms p99 ms max ms >22ms % >40ms n submit CPU draw CPU mem sync slow compiles mine 14496 119.9 8.3 8.6 9.2 90.5 0 1 4.8 2.7 0.38 243 official 14233 118.0 8.3 8.6 9.5 1172 0.1 11 3.8 2.1 0.34 0 ``` So on this route: same median and p95, my build has fewer long frames (1 against 11, max 90 ms against 1172 ms), but its CPU time a frame is higher. One run each, so the long frames may be a first-start effect (shader step) rather than the DLL. I can attach the full logs if a maintainer wants them; I did not want to upload multi-MB files here without being asked.
max855k commented 2026-11-11 18:22:04 +00:00 (Migrated from github.com)

[Linux/NVIDIA] Hookshot v2.1.0 — CPU-side Vulkan replay limits Kokiri Forest to ~47–50 FPS on RTX 5080

Summary

Hookshot v2.1.0 runs successfully on my Ubuntu desktop, but demanding outdoor scenes in Kokiri Forest consistently run below the 60 FPS cap, despite substantial unused GPU capacity.

Profiling points to CPU-side Vulkan draw/descriptor replay overhead. Lowering render resolution does not meaningfully improve FPS.

This appears related to the existing CPU-bound performance tracking issue #995 and earlier investigation #384.

Hardware and software

  • CPU: AMD Ryzen 5 5600X (6 cores / 12 threads)
  • GPU: NVIDIA GeForce RTX 5080, 16 GB VRAM
  • NVIDIA driver: NVIDIA-SMI 595.99.02
  • RAM: 32 GB
  • OS: Ubuntu 26.04.1 LTS, 64-bit
  • Kernel: 7.0.0-30-generic
  • Desktop: GNOME 50, Wayland
  • Storage: Samsung 850 EVO SSD, ext4
  • Hookshot: Official Linux AppImage, v2.1.0

Graphics configuration

  • Output resolution: 1920×1080
  • Render resolution: 100%
  • Upscaler: Game's own
  • Fullscreen: On
  • Game frame-rate cap: 60 FPS
  • Frame generation: Off
  • Frame pacing: Off during baseline tests
  • NVIDIA Reflex: On
  • Anisotropic filtering: 16x
  • Profile: Docked
  • Mesh shaders: Automatic
  • Descriptor reuse: On
  • Robust buffers: Auto for baseline

Reproduction

  1. Launch Hookshot and load into Kokiri Forest.
  2. Visit an outdoor area overlooking the central village and surrounding foliage.
  3. Allow shader/pipeline preparation to settle.
  4. Stand still and observe the FPS counter.
  5. Capture Vulkan replay and GPU-time statistics from hookshot_log.txt.

Actual versus expected performance

The 60 FPS cap is not maintained in demanding village scenes.

  • Typical repeatable log measurements: approximately 46–48 FPS with default CPU scheduling.
  • GPU utilization observed around 48%, with temperatures around 55°C.
  • GPU memory usage remains well below the 16 GB capacity.
  • Other, lighter scenes can reach the 60 FPS cap.

I expected closer to 60 FPS given the available GPU capacity, although Hookshot's renderer is still experimental.

Vulkan diagnostics during the slow scene

Representative sustained measurements:

Metric Value
FPS 46.7–47.5
Draws per frame ~2,820–2,840
Dispatches ~140–145
Passes ~172
CPU replay/submit time ~17.6–18.0 ms
Draw processing ~12.8–13.1 ms
Descriptor processing (included in draw) ~4.8–4.9 ms
Vulkan GPU execution time in earlier comparable scene ~7.3 ms
GPU-bound wait 0.0 ms
Shader/pipeline waits 0.0 ms

CPU profiling in the heavy scene:

  • Presentation Thread: 87–88% of one core
  • MainThread: approximately 35%
  • BaseProcExecutor workers: approximately 29–36% each
  • ModuleSystemWorker threads: approximately 24–28% each

Representative log:

vk: 4560 presents, 46.8 fps; a frame: 2833 draws (0 skipped), 145 dispatches, 172 passes, 150 barriers, 6 submits, GPU-bound wait 0.0 ms

vk: a frame's replay time (ms): submit 17.87, present 0.06, draw 13.14, descriptors 4.91, programs 2.20, commands 4.77, memory sync 0.58, queue submit 0.28

prof: busiest threads (% of a core): Presentation Thread 88, sead::MainThread 35, BaseProcExecutor0 34, BaseProcExecutor1 32

Optimization experiments

Experiment Observation
Render resolution 100% → 75% No convincing FPS improvement
RX_VK_WB_MAX_KB=1024 No meaningful improvement
Robust buffers Off No convincing improvement
RX_VK_ASYNC=1 Regression, approximately 40 FPS, with a later screenshot showing 30 FPS
HOS_RESERVE_CORE=0 Possible improvement to approximately 49–51 FPS
Descriptor reuse On Already enabled
Sampler cache On Approximately 48–50 FPS; no clear improvement

Experiments were not fully identical automated benchmarks. Camera angle, draw count and run-to-run variation may affect comparisons.

With HOS_RESERVE_CORE=0, representative logs showed around 17.0–17.4 ms CPU replay time and approximately 2,880 draws per frame.

Interpretation

The available logs suggest the bottleneck is predominantly CPU-side Vulkan replay and descriptor/draw processing, rather than raw GPU throughput, shader compilation or VRAM exhaustion.

This is consistent with earlier investigations showing that replay cost increases with draw count.

Questions for developers

  1. Are further CPU-side draw or descriptor-reuse optimizations planned for dense outdoor scenes?
  2. Is there a recommended CPU-affinity or scheduler configuration for a non-hybrid Ryzen 5 5600X?
  3. Would a sampled Presentation Thread profile be useful for investigating this behavior?

I can provide hookshot_log.txt from a slow gameplay session and additional controlled comparisons if needed.

No crashes or device-loss errors were observed during these tests.

# [Linux/NVIDIA] Hookshot v2.1.0 — CPU-side Vulkan replay limits Kokiri Forest to ~47–50 FPS on RTX 5080 ### Summary Hookshot v2.1.0 runs successfully on my Ubuntu desktop, but demanding outdoor scenes in Kokiri Forest consistently run below the 60 FPS cap, despite substantial unused GPU capacity. Profiling points to CPU-side Vulkan draw/descriptor replay overhead. Lowering render resolution does not meaningfully improve FPS. This appears related to the existing CPU-bound performance tracking issue #995 and earlier investigation #384. ### Hardware and software - **CPU:** AMD Ryzen 5 5600X (6 cores / 12 threads) - **GPU:** NVIDIA GeForce RTX 5080, 16 GB VRAM - **NVIDIA driver:** NVIDIA-SMI 595.99.02 - **RAM:** 32 GB - **OS:** Ubuntu 26.04.1 LTS, 64-bit - **Kernel:** 7.0.0-30-generic - **Desktop:** GNOME 50, Wayland - **Storage:** Samsung 850 EVO SSD, ext4 - **Hookshot:** Official Linux AppImage, v2.1.0 ### Graphics configuration - Output resolution: 1920×1080 - Render resolution: 100% - Upscaler: Game's own - Fullscreen: On - Game frame-rate cap: 60 FPS - Frame generation: Off - Frame pacing: Off during baseline tests - NVIDIA Reflex: On - Anisotropic filtering: 16x - Profile: Docked - Mesh shaders: Automatic - Descriptor reuse: On - Robust buffers: Auto for baseline ### Reproduction 1. Launch Hookshot and load into Kokiri Forest. 2. Visit an outdoor area overlooking the central village and surrounding foliage. 3. Allow shader/pipeline preparation to settle. 4. Stand still and observe the FPS counter. 5. Capture Vulkan replay and GPU-time statistics from `hookshot_log.txt`. ### Actual versus expected performance The 60 FPS cap is not maintained in demanding village scenes. - Typical repeatable log measurements: approximately 46–48 FPS with default CPU scheduling. - GPU utilization observed around 48%, with temperatures around 55°C. - GPU memory usage remains well below the 16 GB capacity. - Other, lighter scenes can reach the 60 FPS cap. I expected closer to 60 FPS given the available GPU capacity, although Hookshot's renderer is still experimental. ### Vulkan diagnostics during the slow scene Representative sustained measurements: | Metric | Value | |---|---:| | FPS | 46.7–47.5 | | Draws per frame | ~2,820–2,840 | | Dispatches | ~140–145 | | Passes | ~172 | | CPU replay/submit time | ~17.6–18.0 ms | | Draw processing | ~12.8–13.1 ms | | Descriptor processing (included in draw) | ~4.8–4.9 ms | | Vulkan GPU execution time in earlier comparable scene | ~7.3 ms | | GPU-bound wait | 0.0 ms | | Shader/pipeline waits | 0.0 ms | CPU profiling in the heavy scene: - Presentation Thread: 87–88% of one core - MainThread: approximately 35% - BaseProcExecutor workers: approximately 29–36% each - ModuleSystemWorker threads: approximately 24–28% each Representative log: ```text vk: 4560 presents, 46.8 fps; a frame: 2833 draws (0 skipped), 145 dispatches, 172 passes, 150 barriers, 6 submits, GPU-bound wait 0.0 ms vk: a frame's replay time (ms): submit 17.87, present 0.06, draw 13.14, descriptors 4.91, programs 2.20, commands 4.77, memory sync 0.58, queue submit 0.28 prof: busiest threads (% of a core): Presentation Thread 88, sead::MainThread 35, BaseProcExecutor0 34, BaseProcExecutor1 32 ``` ### Optimization experiments | Experiment | Observation | |---|---| | Render resolution 100% → 75% | No convincing FPS improvement | | `RX_VK_WB_MAX_KB=1024` | No meaningful improvement | | Robust buffers Off | No convincing improvement | | `RX_VK_ASYNC=1` | Regression, approximately 40 FPS, with a later screenshot showing 30 FPS | | `HOS_RESERVE_CORE=0` | Possible improvement to approximately 49–51 FPS | | Descriptor reuse On | Already enabled | | Sampler cache On | Approximately 48–50 FPS; no clear improvement | Experiments were not fully identical automated benchmarks. Camera angle, draw count and run-to-run variation may affect comparisons. With `HOS_RESERVE_CORE=0`, representative logs showed around 17.0–17.4 ms CPU replay time and approximately 2,880 draws per frame. ### Interpretation The available logs suggest the bottleneck is predominantly CPU-side Vulkan replay and descriptor/draw processing, rather than raw GPU throughput, shader compilation or VRAM exhaustion. This is consistent with earlier investigations showing that replay cost increases with draw count. ### Questions for developers 1. Are further CPU-side draw or descriptor-reuse optimizations planned for dense outdoor scenes? 2. Is there a recommended CPU-affinity or scheduler configuration for a non-hybrid Ryzen 5 5600X? 3. Would a sampled Presentation Thread profile be useful for investigating this behavior? I can provide `hookshot_log.txt` from a slow gameplay session and additional controlled comparisons if needed. No crashes or device-loss errors were observed during these tests.
UsagiiiSan commented 2026-11-11 18:23:41 +00:00 (Migrated from github.com)

Data point, v2.1.0 (source 633eb435), Windows 11: replay cost scales with draw count, write-back ruled out

PC: RTX 4070 Ti Super (16 GB), Ryzen 5700X3D, 32 GB DDR4-3600 (XMP on), page file fixed at 32000 MB, game on NVMe, single hookshot.exe instance.
Settings: Frame generation off, Frame pacing on, Reflex on, Render resolution 100%, RX_VK_WB_MAX_KB=1024 and RX_VK_PASS_TIMING=1 in hookshot_env.txt.
Place: Hyrule Field, standing still at the same spot (the little river after kokiri exit ), two passes of ~30 s each, looking in one direction that drops, then at the sky.

slow direction sky
fps 52-46 (pass 1), 52-47 (pass 2) 60.0 steady, both passes
draws a frame ~2,770 (2,550-2,780) ~680
replay submit ~16.5 ms ~7.5 ms
GPU time a frame ~8.3 ms (about 40% of a ~19.5 ms frame) ~4.3 ms
waits for shaders or pipelines 0 0

Slow direction, replay breakdown: draw 10.4 ms (descriptors 3.3), commands 4.4, dispatch 1.3, memory sync ~1.0.
Write-back with RX_VK_WB_MAX_KB=1024: 0.2 ms a frame, 2.5% of profiled GPU time (about 9 MB a frame). Unlike the v2.0.14 write-back reports in this thread, it is not the limit on this PC.
Cost is about 4 us per draw and the GPU is idle for more than half of each frame. Nothing in the log points to shaders: 0 waits, no pipeline stalls, slowest frame 23.5 ms.

hookshot_log.txt attached (includes RX_VK_PASS_TIMING=1; Windows user name replaced in paths).

hookshot_log.txt

Data point, v2.1.0 (source 633eb435), Windows 11: replay cost scales with draw count, write-back ruled out PC: RTX 4070 Ti Super (16 GB), Ryzen 5700X3D, 32 GB DDR4-3600 (XMP on), page file fixed at 32000 MB, game on NVMe, single hookshot.exe instance. Settings: Frame generation off, Frame pacing on, Reflex on, Render resolution 100%, RX_VK_WB_MAX_KB=1024 and RX_VK_PASS_TIMING=1 in hookshot_env.txt. Place: Hyrule Field, standing still at the same spot (the little river after kokiri exit ), two passes of ~30 s each, looking in one direction that drops, then at the sky. | | slow direction | sky | |---|---|---| | fps | 52-46 (pass 1), 52-47 (pass 2) | 60.0 steady, both passes | | draws a frame | ~2,770 (2,550-2,780) | ~680 | | replay submit | ~16.5 ms | ~7.5 ms | | GPU time a frame | ~8.3 ms (about 40% of a ~19.5 ms frame) | ~4.3 ms | | waits for shaders or pipelines | 0 | 0 | Slow direction, replay breakdown: draw 10.4 ms (descriptors 3.3), commands 4.4, dispatch 1.3, memory sync ~1.0. Write-back with RX_VK_WB_MAX_KB=1024: 0.2 ms a frame, 2.5% of profiled GPU time (about 9 MB a frame). Unlike the v2.0.14 write-back reports in this thread, it is not the limit on this PC. Cost is about 4 us per draw and the GPU is idle for more than half of each frame. Nothing in the log points to shaders: 0 waits, no pipeline stalls, slowest frame 23.5 ms. hookshot_log.txt attached (includes RX_VK_PASS_TIMING=1; Windows user name replaced in paths). [hookshot_log.txt](https://github.com/user-attachments/files/34700686/hookshot_log.txt)
jabbaxipil commented 2026-11-11 18:26:55 +00:00 (Migrated from github.com)

For the reports here where fps is fine at first and drops after 10-15 minutes of play (for example @benjdidj's above): on NVIDIA Linux that may be what PR #2314 addresses. It makes the texture-shadow copies read pages staged in cached RAM instead of the write-combined capture buffer.

How to tell if it is this: in hookshot_log.txt, compare the a frame's replay time (ms): ... memory sync X value early in the session with a later one at the same spot. If memory sync grows from a few ms to 10 ms or more while draws stay about the same, it is likely this. If memory sync stays low and flat through the session and draw / descriptors are high, it is the draw-recording cost tracked here, and #2314 will not change it.

Measured with the PR on an RTX 4070 12 GB (driver 615.71.09, 5800X3D, 1440p, frame generation off), at one fixed Kokiri view, after about 10 minutes of exploring: 33.1 fps with the change off against 49.5 fps with it on (memory sync 15.9 ms against 6.3 ms). Fresh launches were about 60 fps either way. Details and the full table: #2314

The PR is still open, so this needs a build with it applied. It does not remove the whole slowdown: with it on, the same view still went from 59 to 49.5 fps after exploring.

For the reports here where fps is fine at first and drops after 10-15 minutes of play (for example @benjdidj's above): on NVIDIA Linux that may be what PR #2314 addresses. It makes the texture-shadow copies read pages staged in cached RAM instead of the write-combined capture buffer. How to tell if it is this: in `hookshot_log.txt`, compare the `a frame's replay time (ms): ... memory sync X` value early in the session with a later one at the same spot. If `memory sync` grows from a few ms to 10 ms or more while draws stay about the same, it is likely this. If `memory sync` stays low and flat through the session and `draw` / `descriptors` are high, it is the draw-recording cost tracked here, and #2314 will not change it. Measured with the PR on an RTX 4070 12 GB (driver 615.71.09, 5800X3D, 1440p, frame generation off), at one fixed Kokiri view, after about 10 minutes of exploring: 33.1 fps with the change off against 49.5 fps with it on (memory sync 15.9 ms against 6.3 ms). Fresh launches were about 60 fps either way. Details and the full table: https://git.magicbean.dev/MagicBeanDev/Hookshot/pulls/2314 The PR is still open, so this needs a build with it applied. It does not remove the whole slowdown: with it on, the same view still went from 59 to 49.5 fps after exploring.
lucasmazzoni45-del commented 2026-11-11 19:16:14 +00:00 (Migrated from github.com)

The pieces of my build that came out of the measurements above are now four small PRs on main, one topic each:

  • #2437 capture buffers in size classes, kept up to 256 MB and made ahead on a worker (the capture part of the memory-sync spikes at loads)
  • #2438 part of the image pool's blocks made at the start (the 128 MB vkAllocateMemory calls on AMD)
  • #2439 flat table instead of a hash map in texture_answer
  • #2440 6 background workers on 16+ hardware threads

All of them build on v2.1.0 and boot on an RX 7800 XT; the full test suite was not run and I have not played a long session on this build yet, so the effect in real play is still unproven.

The pieces of my build that came out of the measurements above are now four small PRs on main, one topic each: - #2437 capture buffers in size classes, kept up to 256 MB and made ahead on a worker (the capture part of the memory-sync spikes at loads) - #2438 part of the image pool's blocks made at the start (the 128 MB vkAllocateMemory calls on AMD) - #2439 flat table instead of a hash map in `texture_answer` - #2440 6 background workers on 16+ hardware threads All of them build on v2.1.0 and boot on an RX 7800 XT; the full test suite was not run and I have not played a long session on this build yet, so the effect in real play is still unproven.
NotRexed commented 2026-11-11 21:21:49 +00:00 (Migrated from github.com)

More reports of this, collected here:

  • #1271: Shader stage libraries (RX_VK_GPL) on by default on NVIDIA: about 7x less waiting on a first play
  • #1278: v2.0.11: ~11-15 fps in-game on RX 6700 XT, GPU time ~60-100 ms a frame (v2.0.6: 6-12 ms) (v2.0.11; AMD Radeon RX 6700 XT (12 GB), Vulkan 1.4, Windows, 2560x1440; AMD Ryzen 5 7500F, Windows 11 Home)
  • #1293: FPS dips when using a microphone with the ocarina (Tested with the dualsense micrphone through USB) (v2.0.11; RX 9070 XT, 26.10.1; Ryzen 7 5700X3D, 32GB, Windows 11)
  • #1400: After a scene change, compute program ab7699ee7f536755 goes from 0.13 ms to 125 ms per run and the game stays
  • #1733: MASSIVE FPS drop after updating to v2.0.13 (v2.0.13; GTX 1650 Super, NVIDIA 596.49; Ryzen 5 3600, 8 GB, Windows 10)
  • #1993: Batched submits: send the held batch as soon as the GPU is idle (+40% fps in Kokiri Forest on RX 9070 XT / RAD
  • #2073: FPS WON’T GO UP NOT EVEN SCALING, GLITCHES (v2.0.14; RTX 4060, NVIDIA 621.14; Intel i7-13650HX, 32 GB DDR5, WINDOWS 11 HOME)
  • #2084: First time textures are taking a long time to load in (v2.0.14; RX 7900 XTX, Adrenaline 26.10.2; Ryzen 5 7600X, 32GB DDR5, Windows 11 Pro 25H2)
  • #2099: perf(vk): 4K is CPU-bound by the memory sync (35.6 fps at 4K vs 60 at 1080p, GPU starved) — finish the remaini
  • #2156: [REGRESSION v2.1.0] Severe FPS drops in Kokiri Forest - worked fine on v2.0.14 (RTX 5050 8GB) (v2.1.0; NVIDIA GeForce RTX 5050 8GB, driver 616.56; Ryzen 5 8600G, 32GB RAM 5600mhz DDR5, Windows 11)
  • #2192: RTX 3050 6gb
  • #2234: low gpu and cpu util when using reshade/dlss 5 (v2.1.0; RTX 5080 16GB, Driver 621.01; Ryzen 5 9600X, 32GB RAM, Win11 V26H2)
  • #2278: FPS Drops to 25–30 In-Game but Reaches 60 FPS in Menu (V.2.1.0; RTX 3050 (8 GB); AMD Ryzen 7 5700X)
  • #2349: using only 40% of my gpu (2.1.0; RTX 4060 mobile, 621.01; ryzen 7 7735hs, 16gb ddr5 ram on windows 11)
  • #2398: Significant FPS drop when entering Hyrule Town (area with villagers) (2.1; NVIDIA GeForce RTX 5070 12GB ver. 621.14; ryzen 7 7800x3D)
  • #2489: FPS drops when talking too much on ocarina (v2.1.0; RTX 4060, 621.14; Intel i7-9700, 2667mhz ddr4 ram 32gb, Windows 11 25H2)
More reports of this, collected here: - #1271: Shader stage libraries (RX_VK_GPL) on by default on NVIDIA: about 7x less waiting on a first play - #1278: v2.0.11: ~11-15 fps in-game on RX 6700 XT, GPU time ~60-100 ms a frame (v2.0.6: 6-12 ms) (v2.0.11; AMD Radeon RX 6700 XT (12 GB), Vulkan 1.4, Windows, 2560x1440; AMD Ryzen 5 7500F, Windows 11 Home) - #1293: FPS dips when using a microphone with the ocarina (Tested with the dualsense micrphone through USB) (v2.0.11; RX 9070 XT, 26.10.1; Ryzen 7 5700X3D, 32GB, Windows 11) - #1400: After a scene change, compute program ab7699ee7f536755 goes from 0.13 ms to 125 ms per run and the game stays - #1733: MASSIVE FPS drop after updating to v2.0.13 (v2.0.13; GTX 1650 Super, NVIDIA 596.49; Ryzen 5 3600, 8 GB, Windows 10) - #1993: Batched submits: send the held batch as soon as the GPU is idle (+40% fps in Kokiri Forest on RX 9070 XT / RAD - #2073: FPS WON’T GO UP NOT EVEN SCALING, GLITCHES (v2.0.14; RTX 4060, NVIDIA 621.14; Intel i7-13650HX, 32 GB DDR5, WINDOWS 11 HOME) - #2084: First time textures are taking a long time to load in (v2.0.14; RX 7900 XTX, Adrenaline 26.10.2; Ryzen 5 7600X, 32GB DDR5, Windows 11 Pro 25H2) - #2099: perf(vk): 4K is CPU-bound by the memory sync (35.6 fps at 4K vs 60 at 1080p, GPU starved) — finish the remaini - #2156: [REGRESSION v2.1.0] Severe FPS drops in Kokiri Forest - worked fine on v2.0.14 (RTX 5050 8GB) (v2.1.0; NVIDIA GeForce RTX 5050 8GB, driver 616.56; Ryzen 5 8600G, 32GB RAM 5600mhz DDR5, Windows 11) - #2192: RTX 3050 6gb - #2234: low gpu and cpu util when using reshade/dlss 5 (v2.1.0; RTX 5080 16GB, Driver 621.01; Ryzen 5 9600X, 32GB RAM, Win11 V26H2) - #2278: FPS Drops to 25–30 In-Game but Reaches 60 FPS in Menu (V.2.1.0; RTX 3050 (8 GB); AMD Ryzen 7 5700X) - #2349: using only 40% of my gpu (2.1.0; RTX 4060 mobile, 621.01; ryzen 7 7735hs, 16gb ddr5 ram on windows 11) - #2398: Significant FPS drop when entering Hyrule Town (area with villagers) (2.1; NVIDIA GeForce RTX 5070 12GB ver. 621.14; ryzen 7 7800x3D) - #2489: FPS drops when talking too much on ocarina (v2.1.0; RTX 4060, 621.14; Intel i7-9700, 2667mhz ddr4 ram 32gb, Windows 11 25H2)
Efraim2704 commented 2026-11-12 00:52:04 +00:00 (Migrated from github.com)

Kokiri Forest, CPU-bound at ~54 fps on i9-10900K + RTX 3080 (v2.1.0, Windows, 2560x1440) — measurements and what did not work

Disclosure: the analysis and the experimental patches were done with an AI assistant (Claude); all in-game testing was done by me on my own machine. Nothing here is a PR, just data in case it helps.

Setup: i9-10900K, RTX 3080, 32 GB, Windows, v2.1.0, Robust buffers off. Save loads at the heaviest spot of Kokiri Forest (~4,200 draws per frame).

Best clean configuration found: RX_VK_PARALLEL=6 + RX_VK_WB_MAX_KB=1024 → about 54 fps mean, no visual issues. RX_VK_PARALLEL=8 was worse (51.7).

Where the frame goes (heaviest spot):

  • Main thread and presentation thread alternate rather than overlap: presentation ~10 ms (6 submits + replay), main ~7–9 ms.
  • The capture wait for late pages (CPU-written pages with a pending write-back) costs ~3.7 ms per frame.

Experiments (local patches on v2.1.0), in case they save someone time:

  1. RX_VK_CAPTURE_WAIT=0 reaches ~60 fps but gives constant full-screen white flashes.
  2. The white flashes come from the auto-exposure readback (guest f_80357060: invalidate, read the luminance sum, write zero back). Handling those words separately — the CPU gets the GPU's value without waiting, and the word is kept out of write-backs and syncs — removed the white flashes without the wait.
  3. With the exposure handled, removing the late-page wait still left individual elements flickering, at 56–57 fps mean.
  4. Resolving late pages word-accurately (applying the CPU writes and the GPU write-backs in order, late pages → ~0) did not remove that flicker.
  5. A diagnostic per-frame fence (wait for the GPU once per frame, no late-page wait) removed all flicker, but cost about the same as the stock wait. A narrower fence around the scene submissions only cost about the same too.

Conclusion: on this machine the late-page wait is acting as a general frame fence. Without it, the GPU reads guest data from the next frame somewhere other than the late pages and the exposure words; I could not identify where. So the remaining gap to 60 here looks like replay CPU cost on the submitting thread plus that fence, not the write-back handling itself.

Also: on v2.1.0 with parallel replay, the readback wait returned after the first segment's recording. That is already fixed on main by #2419, so these numbers should be re-measured on a build that includes it.

Log attached. Happy to run further tests on this hardware.

hookshot_log.txt

**Kokiri Forest, CPU-bound at ~54 fps on i9-10900K + RTX 3080 (v2.1.0, Windows, 2560x1440) — measurements and what did not work** Disclosure: the analysis and the experimental patches were done with an AI assistant (Claude); all in-game testing was done by me on my own machine. Nothing here is a PR, just data in case it helps. **Setup:** i9-10900K, RTX 3080, 32 GB, Windows, v2.1.0, Robust buffers off. Save loads at the heaviest spot of Kokiri Forest (~4,200 draws per frame). **Best clean configuration found:** `RX_VK_PARALLEL=6` + `RX_VK_WB_MAX_KB=1024` → about 54 fps mean, no visual issues. `RX_VK_PARALLEL=8` was worse (51.7). **Where the frame goes (heaviest spot):** - Main thread and presentation thread alternate rather than overlap: presentation ~10 ms (6 submits + replay), main ~7–9 ms. - The capture wait for late pages (CPU-written pages with a pending write-back) costs ~3.7 ms per frame. **Experiments (local patches on v2.1.0), in case they save someone time:** 1. `RX_VK_CAPTURE_WAIT=0` reaches ~60 fps but gives constant full-screen white flashes. 2. The white flashes come from the auto-exposure readback (guest `f_80357060`: invalidate, read the luminance sum, write zero back). Handling those words separately — the CPU gets the GPU's value without waiting, and the word is kept out of write-backs and syncs — removed the white flashes without the wait. 3. With the exposure handled, removing the late-page wait still left individual elements flickering, at 56–57 fps mean. 4. Resolving late pages word-accurately (applying the CPU writes and the GPU write-backs in order, late pages → ~0) did not remove that flicker. 5. A diagnostic per-frame fence (wait for the GPU once per frame, no late-page wait) removed all flicker, but cost about the same as the stock wait. A narrower fence around the scene submissions only cost about the same too. **Conclusion:** on this machine the late-page wait is acting as a general frame fence. Without it, the GPU reads guest data from the next frame somewhere other than the late pages and the exposure words; I could not identify where. So the remaining gap to 60 here looks like replay CPU cost on the submitting thread plus that fence, not the write-back handling itself. Also: on v2.1.0 with parallel replay, the readback wait returned after the first segment's recording. That is already fixed on main by #2419, so these numbers should be re-measured on a build that includes it. Log attached. Happy to run further tests on this hardware. [hookshot_log.txt](https://github.com/user-attachments/files/34721610/hookshot_log.txt)
NotRexed commented 2026-11-12 03:07:09 +00:00 (Migrated from github.com)

More reports of this, collected here:

  • #2534: I get 8 fps when I use Ocarina (V2.1.0; RX 5700 XT; E5 2680 v4 16GB RAM Windows 11)
  • #2601: Extremely low FPS (v2.1.0; GTX 1650 Ti 4GB; I5 10300H)
More reports of this, collected here: - #2534: I get 8 fps when I use Ocarina (V2.1.0; RX 5700 XT; E5 2680 v4 16GB RAM Windows 11) - #2601: Extremely low FPS (v2.1.0; GTX 1650 Ti 4GB; I5 10300H)
Owner

@YakuzaFanA commented on GitHub · 2026-11-12 04:42 UTC · original

Some data from an 8 GB card on the Castle Town market, mostly about which of the suggested switches help there. Short version: on 8 GB, RX_VK_PARALLEL made it worse and RX_VK_ASYNC=1 didn't help.

Setup: RTX 5060 Ti 8 GB, driver 610.88, Core Ultra 7 265F (20 threads), Windows 11, 1080p. Built on main at 497060f with #2482 and #2484 merged, so before #2419, #2602 and the other CPU changes that landed since. #2419 fixes the fence wait with a split submission, so the RX_VK_PARALLEL numbers below should be checked again on a build that has it; I'll do that on my next build. I compared fps at the same draw count, because the market's crowd swings between about 1,000 and 3,400 draws a frame.

RX_VK_PARALLEL=6 (this one on the build before #2484): worse on 8 GB. The market dropped to 11-17 fps (about 22 without it on that build). VRAM went to 7,667 MB of 6,975 allowed, the texture budget shrank to 314 MB, and memory sync went to about 30 ms a frame. I also saw a thin black line on the drawbridge going into the market. I think the extra workers just need VRAM an 8 GB card doesn't have spare. Efraim's 54 fps result was on a 10 GB 3080, so it may be fine there.

RX_VK_PARALLEL=2: no real difference at the same draw count:

draws a frame off PARALLEL=2 ASYNC=1
1,500-1,999 49.1 48.3 -
2,000-2,499 44.1 41.8 -
2,500-2,999 36.6 41.1 37.4

RX_VK_ASYNC=1: no fps gain on this build, and a lot of flashing. A 45-second RX_VK_FRAMELOG capture in the market had 315 one-frame events in 1,719 frames (scripts/framelog.py), whole-screen brighter or darker. The comment in async_on() still says 28 per 1000, from early November.

From an all-thread sample in the market, the main thread is blocked about 87% of the time, mostly waiting on the presentation thread, which matches the alternating Efraim described. I also checked whether the texture view cache was being thrown away by every texture destroy (texture_destroy_generation bumps on any eviction), since texture_view_of was the biggest part of build_descriptors. RX_VK_LOG_HITS=1 said no: 90-98.6% hits in the market, and misses from a destroyed texture mostly under 2%.

(I used Claude to help read the logs and profiles; the playing and testing were mine.)

> [@YakuzaFanA](https://github.com/YakuzaFanA) commented on GitHub · 2026-11-12 04:42 UTC · [original](https://github.com/NotRexed/Hookshot/issues/995#issuecomment-6814745844) Some data from an 8 GB card on the Castle Town market, mostly about which of the suggested switches help there. Short version: on 8 GB, `RX_VK_PARALLEL` made it worse and `RX_VK_ASYNC=1` didn't help. Setup: RTX 5060 Ti 8 GB, driver 610.88, Core Ultra 7 265F (20 threads), Windows 11, 1080p. Built on main at 497060f with #2482 and #2484 merged, so before #2419, #2602 and the other CPU changes that landed since. #2419 fixes the fence wait with a split submission, so the `RX_VK_PARALLEL` numbers below should be checked again on a build that has it; I'll do that on my next build. I compared fps at the same draw count, because the market's crowd swings between about 1,000 and 3,400 draws a frame. **`RX_VK_PARALLEL=6`** (this one on the build before #2484): worse on 8 GB. The market dropped to 11-17 fps (about 22 without it on that build). VRAM went to 7,667 MB of 6,975 allowed, the texture budget shrank to 314 MB, and memory sync went to about 30 ms a frame. I also saw a thin black line on the drawbridge going into the market. I think the extra workers just need VRAM an 8 GB card doesn't have spare. Efraim's 54 fps result was on a 10 GB 3080, so it may be fine there. **`RX_VK_PARALLEL=2`:** no real difference at the same draw count: | draws a frame | off | PARALLEL=2 | ASYNC=1 | |---|---|---|---| | 1,500-1,999 | 49.1 | 48.3 | - | | 2,000-2,499 | 44.1 | 41.8 | - | | 2,500-2,999 | 36.6 | 41.1 | 37.4 | **`RX_VK_ASYNC=1`:** no fps gain on this build, and a lot of flashing. A 45-second `RX_VK_FRAMELOG` capture in the market had 315 one-frame events in 1,719 frames (`scripts/framelog.py`), whole-screen brighter or darker. The comment in `async_on()` still says 28 per 1000, from early November. From an all-thread sample in the market, the main thread is blocked about 87% of the time, mostly waiting on the presentation thread, which matches the alternating Efraim described. I also checked whether the texture view cache was being thrown away by every texture destroy (`texture_destroy_generation` bumps on any eviction), since `texture_view_of` was the biggest part of `build_descriptors`. `RX_VK_LOG_HITS=1` said no: 90-98.6% hits in the market, and misses from a destroyed texture mostly under 2%. (I used Claude to help read the logs and profiles; the playing and testing were mine.) <!-- github-sync:c:6814745844 -->
Owner

@Waryxxful commented on GitHub · 2026-11-12 11:21 UTC · original

Where the GPU time goes on a card without mesh shaders (RX_VK_PASS_TIMING=2): RX 5600 XT 6 GB (RDNA1), Windows 11, main de866017, Hyrule Field, ~1,040 draws.

kind ms a frame share
dispatch (incl. the mesh fallback's) 5.84 32%
pass (1,040 draws) 4.96 27%
barrier 1.32 7%
fsr3 1.16 6% (accumulate alone 0.79 ms)
wb (mirror → guest) 0.76 4% (12.4 MB a frame)
msync (uploads, CPU pages → mirror) 0.70

Top single dispatches: 755a777ada52677d (58 a frame, 0.64 ms), 435a0138b9013040 (0.57), b612216258832b9c (0.44), 27ee9883e2a20323 (0.39), ab7699ee7f536755 (0.37). The replay's submit is ~10 ms a frame at ~1,040 draws, and GPU-bound wait stays at 0.

> [@Waryxxful](https://github.com/Waryxxful) commented on GitHub · 2026-11-12 11:21 UTC · [original](https://github.com/NotRexed/Hookshot/issues/995#issuecomment-6822203087) **Where the GPU time goes on a card without mesh shaders (`RX_VK_PASS_TIMING=2`): RX 5600 XT 6 GB (RDNA1), Windows 11, main `de866017`, Hyrule Field, ~1,040 draws.** | kind | ms a frame | share | |---|---|---| | dispatch (incl. the mesh fallback's) | 5.84 | 32% | | pass (1,040 draws) | 4.96 | 27% | | barrier | 1.32 | 7% | | fsr3 | 1.16 | 6% (accumulate alone 0.79 ms) | | wb (mirror → guest) | 0.76 | 4% (12.4 MB a frame) | | msync (uploads, CPU pages → mirror) | 0.70 | | Top single dispatches: `755a777ada52677d` (58 a frame, 0.64 ms), `435a0138b9013040` (0.57), `b612216258832b9c` (0.44), `27ee9883e2a20323` (0.39), `ab7699ee7f536755` (0.37). The replay's submit is ~10 ms a frame at ~1,040 draws, and `GPU-bound wait` stays at 0. <!-- github-sync:c:6822203087 -->
Owner

@mooki000 commented on GitHub · 2026-11-12 15:18 UTC · original

Tracker upkeep note (merged pull requests and code checked against main 51da0464; no new test run was made for it).

Main now contains #1158's hybrid-CPU replay-affinity change, #2314's cached-memory shadow copies, #2437–#2440's capture/image/lookup/worker changes, and #2602's readback-wait repair. On 51da0464, gpu/vk/vk_replay.cpp retains range-specific waits and whole-submission protection for direct readers. These can be marked as completed subcases, but they do not prove all stutter or low-FPS reports resolved. A remaining slow-scene report needs a matching current-build log and the same view, settings and cache state; several related optimizations already have owners. No new performance measurement was made for this note, and the tracker should stay open.

> [@mooki000](https://github.com/mooki000) commented on GitHub · 2026-11-12 15:18 UTC · [original](https://github.com/NotRexed/Hookshot/issues/995#issuecomment-6827615773) **Tracker upkeep note** (merged pull requests and code checked against main `51da0464`; no new test run was made for it). Main now contains #1158's hybrid-CPU replay-affinity change, #2314's cached-memory shadow copies, #2437–#2440's capture/image/lookup/worker changes, and #2602's readback-wait repair. On `51da0464`, `gpu/vk/vk_replay.cpp` retains range-specific waits and whole-submission protection for direct readers. These can be marked as completed subcases, but they do not prove all stutter or low-FPS reports resolved. A remaining slow-scene report needs a matching current-build log and the same view, settings and cache state; several related optimizations already have owners. No new performance measurement was made for this note, and the tracker should stay open. <!-- github-sync:c:6827615773 -->
Owner

@PedroFlaminio commented on GitHub · 2026-11-12 15:49 UTC · original

A mid-range NVIDIA card on Linux that is GPU-limited in Hyrule Field, not CPU-limited, although GPU-bound wait reads 0.0 ms. RTX 3060 12 GB, NVIDIA 615.71.09, Manjaro (kernel 6.12), Core Ultra 7 265KF, Wayland; main 51da0464 (AppImage built from source). Picture 2576x1080 (Aspect ratio: monitor's on a 3440x1440 screen), Hyrule Field from the same save, standing, ~3,400-3,750 draws.

Frame rate and GPU time

setting fps GPU time a frame notes
Render resolution 100%, frame interpolation 2x 42.7 — baseline, 3.5 min
100%, frame interpolation off, RX_VK_PASS_TIMING=1 43 (42.0-43.9) 19.2-20.5 ms nvidia-smi: 84-90 % busy, 1,965 MHz, ~122 W
Render resolution 75% (1932x810), frame interpolation off 49-51 — under a minute in the spot; +16 %, though the 3D has 44 % fewer pixels
  • GPU-bound wait 0.0 ms in every run, yet the capture waited for the GPU about 9 ms a frame (GPU wait for late pages 1.49 ms a take, 100 of 600 takes). With #2622 merged locally that wait left the capture (memory sync 9 ms to 0.4 ms) and the frame rate stayed the same: the hold's flush then waited for the GPU instead (comment on #2622). So reports here that read "low GPU use" from that counter may be GPU-limited too.
  • #2633, #2644 and #2559 made no difference here (comments on each).

Where the GPU time goes (RX_VK_PASS_TIMING=2, 75%, frame interpolation 2x, mesh shaders native): 20.1 ms a frame, of it passes 11.3, dispatches 3.3, barriers 1.1, write-back copies 0.7 (68.5 MB a frame), msync copies 0.7 (1,671 copies, 12.2 MB a frame).

Four single-draw passes cost about the same at 100% and 75%, so they do not scale with resolution:

program 100% (2576x1080) 75% (1932x810)
43ec86f35fd937b9 1.12 ms 0.96 ms
d8e59341cf4f629c 0.57 ms 0.57 ms
4f0c0b40ab3a32cd 0.55 ms 0.55 ms
739acd678ae08655 0.55 ms 0.51 ms

With Mesh shaders: Compute they left the list (passes 11.3 to 6.8 ms), but barriers rose to 2.1 ms and 6.8 ms appeared between the timed spans, and the total stayed the same (20.9 ms, 44.1 fps profiled against 43.3). So they look like the scenery's task/mesh programs, whose cost here is per meshlet, not per pixel, either path. That would explain why lowering Render resolution helps this card less than expected; the task shaders' culling (the depth-grid walk described for RX_VK_MESH_NOCULL) may be the place to look.

Also on Linux: the replay's reserved core is the last one, an E-core on hybrid Intel CPUs (#1158 pinned it on Windows only); #2805 picks a P-core there. Pinning it to a P-core by hand (HOS_RESERVE_CORE_MASK=80) made no measurable difference on this machine, consistent with it being GPU-limited.

> [@PedroFlaminio](https://github.com/PedroFlaminio) commented on GitHub · 2026-11-12 15:49 UTC · [original](https://github.com/NotRexed/Hookshot/issues/995#issuecomment-6828404093) **A mid-range NVIDIA card on Linux that is GPU-limited in Hyrule Field, not CPU-limited, although `GPU-bound wait` reads 0.0 ms.** RTX 3060 12 GB, NVIDIA 615.71.09, Manjaro (kernel 6.12), Core Ultra 7 265KF, Wayland; main `51da0464` (AppImage built from source). Picture 2576x1080 (Aspect ratio: monitor's on a 3440x1440 screen), Hyrule Field from the same save, standing, ~3,400-3,750 draws. **Frame rate and GPU time** | setting | fps | GPU time a frame | notes | |---|---|---|---| | Render resolution 100%, frame interpolation 2x | 42.7 | — | baseline, 3.5 min | | 100%, frame interpolation off, `RX_VK_PASS_TIMING=1` | 43 (42.0-43.9) | 19.2-20.5 ms | `nvidia-smi`: 84-90 % busy, 1,965 MHz, ~122 W | | Render resolution 75% (1932x810), frame interpolation off | 49-51 | — | under a minute in the spot; +16 %, though the 3D has 44 % fewer pixels | - `GPU-bound wait 0.0 ms` in every run, yet the capture waited for the GPU about 9 ms a frame (`GPU wait for late pages 1.49 ms` a take, 100 of 600 takes). With #2622 merged locally that wait left the capture (memory sync 9 ms to 0.4 ms) and the frame rate stayed the same: the hold's flush then waited for the GPU instead (comment on #2622). So reports here that read "low GPU use" from that counter may be GPU-limited too. - #2633, #2644 and #2559 made no difference here (comments on each). **Where the GPU time goes** (`RX_VK_PASS_TIMING=2`, 75%, frame interpolation 2x, mesh shaders native): 20.1 ms a frame, of it passes 11.3, dispatches 3.3, barriers 1.1, write-back copies 0.7 (68.5 MB a frame), msync copies 0.7 (1,671 copies, 12.2 MB a frame). Four single-draw passes cost about the same at 100% and 75%, so they do not scale with resolution: | program | 100% (2576x1080) | 75% (1932x810) | |---|---|---| | `43ec86f35fd937b9` | 1.12 ms | 0.96 ms | | `d8e59341cf4f629c` | 0.57 ms | 0.57 ms | | `4f0c0b40ab3a32cd` | 0.55 ms | 0.55 ms | | `739acd678ae08655` | 0.55 ms | 0.51 ms | With **Mesh shaders: Compute** they left the list (passes 11.3 to 6.8 ms), but barriers rose to 2.1 ms and 6.8 ms appeared between the timed spans, and the total stayed the same (20.9 ms, 44.1 fps profiled against 43.3). So they look like the scenery's task/mesh programs, whose cost here is per meshlet, not per pixel, either path. That would explain why lowering Render resolution helps this card less than expected; the task shaders' culling (the depth-grid walk described for `RX_VK_MESH_NOCULL`) may be the place to look. Also on Linux: the replay's reserved core is the last one, an E-core on hybrid Intel CPUs (#1158 pinned it on Windows only); #2805 picks a P-core there. Pinning it to a P-core by hand (`HOS_RESERVE_CORE_MASK=80`) made no measurable difference on this machine, consistent with it being GPU-limited. <!-- github-sync:c:6828404093 -->
Owner

@mooki000 commented on GitHub · 2026-11-12 18:55 UTC · original

Next performance work: four measured scenes

November 12 survey of main 51da0464: i9-13900K, RTX 4090, NVIDIA 615.78.08, Arch Linux; 2048×1152, 100% render scale, no frame generation, cap 240.
Kokiri Forest, Hyrule Field, Castle Town Market and Kakariko each had two unprofiled runs around one CPU/GPU profile, alone on the card.

  1. Descriptor construction: 1.50–2.12 ms/frame. Repeated descriptor walks are the first target; #2644 already works here and has not demonstrated a frame-time gain.
  2. Draw command/driver work: 1.53–2.03 ms/frame. Exactly matching pipeline layouts may avoid full descriptor pushes; #2644 leaves layout sharing separate. This needs an equivalence test, not a cache shortcut.
  3. Program selection/revalidation: 0.63–0.91 ms/frame. Existing work is #2644 and #2476; avoid another competing cache.
  4. Barriers: 0.787–0.887 profiled GPU ms/frame. #2559 explores ordering; rendering correctness and unprofiled frames must decide the gain.
  5. Four reported scenery-program runs: 0.591 profiled GPU ms/frame combined in Hyrule Field. A culling bottleneck remains a hypothesis; the ordinary 75% resolution run did not provide a complete matched measurement interval.
    These are ranked opportunities, not additive frame costs or promised savings: CPU phases nest, GPU timestamps serialize work, and program lists are truncated.
    Hyrule Field and Market repeats differed by 6.51% and 7.35%; they are not stable bands for declaring a small improvement.
    One diagnostic finding, relevant to @PedroFlaminio's report above that GPU-bound wait reads 0.0 ms on a GPU-limited card: that figure only counts waits for a recording slot. At 5120×2880 a counter for the other waits (fences, timelines, idle calls, blocking query reads) reported 2.93 ms/frame while the old value read 0.0 ms. #2897 adds that second line to the log and labels the old one; it changes what is reported, not how the game runs, and that one ordinary run proves a reporting gap, not a speed difference or an isolated bottleneck.
    No results here are re-labelled as current-main measurements after the later rebase; no further game runs were made.
> [@mooki000](https://github.com/mooki000) commented on GitHub · 2026-11-12 18:55 UTC · [original](https://github.com/NotRexed/Hookshot/issues/995#issuecomment-6832979999) ## Next performance work: four measured scenes November 12 survey of main `51da0464`: i9-13900K, RTX 4090, NVIDIA 615.78.08, Arch Linux; 2048×1152, 100% render scale, no frame generation, cap 240. Kokiri Forest, Hyrule Field, Castle Town Market and Kakariko each had two unprofiled runs around one CPU/GPU profile, alone on the card. 1. **Descriptor construction: 1.50–2.12 ms/frame.** Repeated descriptor walks are the first target; [#2644](https://git.magicbean.dev/MagicBeanDev/Hookshot/pulls/2644) already works here and has not demonstrated a frame-time gain. 2. **Draw command/driver work: 1.53–2.03 ms/frame.** Exactly matching pipeline layouts may avoid full descriptor pushes; #2644 leaves layout sharing separate. This needs an equivalence test, not a cache shortcut. 3. **Program selection/revalidation: 0.63–0.91 ms/frame.** Existing work is #2644 and [#2476](https://git.magicbean.dev/MagicBeanDev/Hookshot/pulls/2476); avoid another competing cache. 4. **Barriers: 0.787–0.887 profiled GPU ms/frame.** [#2559](https://git.magicbean.dev/MagicBeanDev/Hookshot/pulls/2559) explores ordering; rendering correctness and unprofiled frames must decide the gain. 5. **Four reported scenery-program runs: 0.591 profiled GPU ms/frame combined in Hyrule Field.** A culling bottleneck remains a hypothesis; the ordinary 75% resolution run did not provide a complete matched measurement interval. These are ranked opportunities, not additive frame costs or promised savings: CPU phases nest, GPU timestamps serialize work, and program lists are truncated. Hyrule Field and Market repeats differed by 6.51% and 7.35%; they are not stable bands for declaring a small improvement. One diagnostic finding, relevant to @PedroFlaminio's report above that `GPU-bound wait` reads 0.0 ms on a GPU-limited card: that figure only counts waits for a recording slot. At 5120×2880 a counter for the other waits (fences, timelines, idle calls, blocking query reads) reported 2.93 ms/frame while the old value read 0.0 ms. #2897 adds that second line to the log and labels the old one; it changes what is reported, not how the game runs, and that one ordinary run proves a reporting gap, not a speed difference or an isolated bottleneck. No results here are re-labelled as current-main measurements after the later rebase; no further game runs were made. <!-- github-sync:c:6832979999 -->
Commenting is not possible because the repository is archived.
No milestone
No assignees
2 participants
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Hookshot#995
No description provided.