Game: Hyrule Town spin costs ~30% fps (Deck LCD) - GPU-written 691x432 texture re-uploaded per present + page-capture storm #750

Open
opened 2026-11-15 09:46:08 +00:00 by omgitsgela Β· 3 comments
Contributor

Deck LCD (Jupiter/Vangogh, 8 logical CPUs), game on the SD card. Found while running our scene benchmark: a new scene-pack save ("First Step into Hyrule Town", one of the save-state packs we swap per arm) loads and idles fine at ~25-27 fps, but the moment a full-speed camera spin starts, frame rate collapses to 2.0-5.9 fps (4 of 6 samples) and stays there for seconds. Walk after it recovers (~31). Draw counts during the stall are nearly flat (6,061 -> 6,077 across the sampled window) - the replay thread is spending its wall-clock elsewhere, not drawing.

What the log shows during the stall (longshot_log.txt, this exact arm, present numbers from the same window as the fps samples):

[hos Presentation Thread] vk: upload over a drawn image: #4711 0x10000420600 691x432x1 x1 NVN 0xa at present 6029 (texture frame 6030), finalized, 0 KB dirty in 0 ranges, a GPU buffer wrote it
[hos Presentation Thread] vk: texture 0x10000420600 691x432x1 x1 levels 1 NVN 0xa (1408 KB of storage) re-uploaded (its memory was written; flags 0x8): a GPU write into its storage, 1408 KB of it, over what the GPU wrote (finalized since)
[hos Presentation Thread] vk: upload over a drawn image: #1171 0x100002ec800 173x108x1 x1 NVN 0x14 at present 6055 ... finalized, 58 KB dirty in 1 ranges
[hos Presentation Thread] vk: upload over a drawn image: #1171 0x100002ec800 173x108x1 x1 NVN 0x14 at present 6056 (texture frame 6057) ... a GPU buffer wrote it
[hos host] vk: present 6058 written to ... (bridge screenshot; one present per ~1 s of wall clock at this point)
...
[hos host] vk: replay reads of guest memory as submitted: 1060000 (714844 pages from the captures)

A 691x432 texture that the GPU itself wrote (flags 0x8) is being re-uploaded every few frames, and consecutive presents at 6055/6056 show the pipeline crawling while that loops. Hyrule Castle on the same save lineage runs 29-30 fps stable in the same protocol, and Kokiri Village never stalls this hard, so something about Town's spin view arms the loop.

Build: main a4821d56 (renderer f1e064e34a99), cap row 3 (the game logs frame rate cap 144), vsync off, FSR3.1 at 50%, RX_VK_READBACK_LATE=newest, threaded off, Deck desktop mode with GPU pinned high + governors performance (so the stall is not a clock ramp - sclk was locked 1600 MHz).

Question for the p0lux PRs: is this the case #590's "no-mirror unread bookkeeping" and "a lock taken for every sampled texture" should shrink, and #567's written-range-only checks help? If so I can run this exact save + spin arm against pr-590/pr-567 branches and report LCD numbers (our Deck is Jupiter/Vangogh LCD - same GPU as OLED, ~1100 MT/s slower memory; the OLED-only tested figures probably understate this one). Happy to also send the full log and the bridge screenshots from the stall frames.

Deck LCD (Jupiter/Vangogh, 8 logical CPUs), game on the SD card. Found while running our scene benchmark: a new scene-pack save ("First Step into Hyrule Town", one of the save-state packs we swap per arm) loads and *idles fine at ~25-27 fps*, but the moment a full-speed camera spin starts, frame rate collapses to **2.0-5.9 fps** (4 of 6 samples) and stays there for seconds. Walk after it recovers (~31). Draw counts during the stall are nearly flat (6,061 -> 6,077 across the sampled window) - the replay thread is spending its wall-clock elsewhere, not drawing. What the log shows during the stall (`longshot_log.txt`, this exact arm, present numbers from the same window as the fps samples): ``` [hos Presentation Thread] vk: upload over a drawn image: #4711 0x10000420600 691x432x1 x1 NVN 0xa at present 6029 (texture frame 6030), finalized, 0 KB dirty in 0 ranges, a GPU buffer wrote it [hos Presentation Thread] vk: texture 0x10000420600 691x432x1 x1 levels 1 NVN 0xa (1408 KB of storage) re-uploaded (its memory was written; flags 0x8): a GPU write into its storage, 1408 KB of it, over what the GPU wrote (finalized since) [hos Presentation Thread] vk: upload over a drawn image: #1171 0x100002ec800 173x108x1 x1 NVN 0x14 at present 6055 ... finalized, 58 KB dirty in 1 ranges [hos Presentation Thread] vk: upload over a drawn image: #1171 0x100002ec800 173x108x1 x1 NVN 0x14 at present 6056 (texture frame 6057) ... a GPU buffer wrote it [hos host] vk: present 6058 written to ... (bridge screenshot; one present per ~1 s of wall clock at this point) ... [hos host] vk: replay reads of guest memory as submitted: 1060000 (714844 pages from the captures) ``` A 691x432 texture that the GPU itself wrote (flags 0x8) is being **re-uploaded** every few frames, and consecutive presents at 6055/6056 show the pipeline crawling while that loops. Hyrule Castle on the same save lineage runs 29-30 fps stable in the same protocol, and Kokiri Village never stalls this hard, so something about Town's spin view arms the loop. Build: main `a4821d56` (renderer `f1e064e34a99`), cap row 3 (the game logs `frame rate cap 144`), vsync off, FSR3.1 at 50%, `RX_VK_READBACK_LATE=newest`, threaded off, Deck desktop mode with GPU pinned `high` + governors `performance` (so the stall is not a clock ramp - sclk was locked 1600 MHz). Question for the p0lux PRs: is this the case #590's "no-mirror unread bookkeeping" and "a lock taken for every sampled texture" should shrink, and #567's written-range-only checks help? If so I can run this exact save + spin arm against pr-590/pr-567 branches and report **LCD** numbers (our Deck is Jupiter/Vangogh LCD - same GPU as OLED, ~1100 MT/s slower memory; the OLED-only tested figures probably understate this one). Happy to also send the full log and the bridge screenshots from the stall frames.
Author
Contributor

Scope, from a 9-save sweep on this same Deck (rt-main a4821d56, SD storage, GPU pinned 1600 MHz + performance governors, FSR3.1/50%, all env pins; medians, idle/spin/walk fps, draws column = steady draw calls per frame from the frame report):

save (NAS scene packs) idle spin walk draws/frame
Kokiri Village (benchmark save, 673-play deep) 24 26 31 6000
After meeting Zelda and Impa (Kokiri) 35 26 40 6236
Kokiri Forest, after intro 30 35 34 5928
Hyrule Field, adult Link 35 45 44 5024
Deku Tree entrance 36 33 34 6356
Dodongo Cavern entrance 34 37 37 6624
Jabu Jabu's Belly entrance 35 38 38 5834
Hyrule Castle approach 29 29 30 5864
Hyrule Town entrance 25 4 27 5630
Hyrule Field, pre-OoT, spiritual stones 29 30 36 5444

Every other scene sits at 29-45 fps in all three phases with draw counts in the same 5-6.6k range. The Town is the only place where a camera spin takes 25 fps idle to 2-6 fps and back. Draws stay ~5.6-6k throughout the stall (6,061 -> 6,077 across it), so it is not draw-count blowup - it is the per-present re-upload/capture cost in the report above. One more data point that this is specific to whatever the Town save has (map minimap? town's always-on textures?) rather than "busy scene = slow".

p0lux's #567 (all-zero check reads only written pages) and #590 (replay-thread bookkeeping) are being built as runtimes on this exact setup right now; I'll post before/after against the Town save when the arms finish. Deck LCD (Jupiter/VANGOGH) throughout.

Scope, from a 9-save sweep on this same Deck (rt-main `a4821d56`, SD storage, GPU pinned 1600 MHz + performance governors, FSR3.1/50%, all env pins; medians, idle/spin/walk fps, draws column = steady `draw calls per frame` from the frame report): | save (NAS scene packs) | idle | spin | walk | draws/frame | |---|---:|---:|---:|---:| | Kokiri Village (benchmark save, 673-play deep) | 24 | 26 | 31 | 6000 | | After meeting Zelda and Impa (Kokiri) | 35 | 26 | 40 | 6236 | | Kokiri Forest, after intro | 30 | 35 | 34 | 5928 | | Hyrule Field, adult Link | 35 | 45 | 44 | 5024 | | Deku Tree entrance | 36 | 33 | 34 | 6356 | | Dodongo Cavern entrance | 34 | 37 | 37 | 6624 | | Jabu Jabu's Belly entrance | 35 | 38 | 38 | 5834 | | Hyrule Castle approach | 29 | 29 | 30 | 5864 | | **Hyrule Town entrance** | **25** | **4** | 27 | 5630 | | Hyrule Field, pre-OoT, spiritual stones | 29 | 30 | 36 | 5444 | Every other scene sits at 29-45 fps in all three phases with draw counts in the same 5-6.6k range. The Town is the only place where a camera spin takes 25 fps idle to 2-6 fps and back. Draws stay ~5.6-6k throughout the stall (6,061 -> 6,077 across it), so it is not draw-count blowup - it is the per-present re-upload/capture cost in the report above. One more data point that this is specific to whatever the Town save has (map minimap? town's always-on textures?) rather than "busy scene = slow". p0lux's #567 (all-zero check reads only written pages) and #590 (replay-thread bookkeeping) are being built as runtimes on this exact setup right now; I'll post before/after against the Town save when the arms finish. Deck LCD (Jupiter/VANGOGH) throughout.
omgitsgela changed title from Game: Hyrule Town save collapses to 2-6 fps on a camera spin (Deck LCD) - GPU-written 691x432 texture re-uploaded every frame + 714k-page capture storm to Game: Hyrule Town spin costs ~30% fps (Deck LCD) - GPU-written 691x432 texture re-uploaded per present + page-capture storm 2026-11-15 12:44:49 +00:00
Author
Contributor

Title correction from the reporter, with the fair numbers.

The original title ("collapses to 2-6 fps") measured my v3 protocol, which at an entrance save sat idle/spin right on the level-loading boundary - Angela caught it watching live ("your numbers for castle town will be messed up by level loading"). Re-measured all three runtimes with a v4 protocol: load -> walk ~10 s INTO the town (spawn faces inward) -> 20 s settle so streaming finishes -> idle samples -> full-speed spin. Same save, same config (rt-main a4821d56, GPU pinned 1600 MHz + performance governors, FSR3.1/50%, SD storage).

arm idle spin re-upload events (log count for the arm)
rt-main (control) 25.9 (tight 25.4-26.4, no dips) 18.6 (range 8.8-22.5) 12
pr-567 runtime 24.4 (one 2.0 dip) 11.8 (3.9-23.5) 14
pr-590 runtime 25.4 (24.3-25.5) 14.6 (one 2.0 dip, 2.0-22.3) 20

Honest restatement: idle in town is clean at ~26 with no dips. The spin drop is real and reproducible - about a 30% fps loss (25.9 -> 18.6 median) - and the mechanism from the original report is still live in the logs (the GPU-written 691x432 texture re-uploaded per present, page-capture counts, 12-20 re-uploaded events per arm run). The 2-6 fps collapse is not the steady behavior at this spot; it shows up as an occasional single-sample dip, and sustained 2-6 is what the loading boundary produced, not the spin itself. Severity downgraded from "catastrophic stall" to "steady ~30% spin degradation with occasional deep dips"; title edited to match. Draws column above is post-walk-in, so the scene being measured is deeper in town than the original table's 5.6k.

p0lux's #567 and #590 (the two PRs targeting this path) did NOT fix or meaningfully shrink the drop on this Deck LCD - separate comments on both PRs have the numbers and the cold-pipeline-cache confound. Deck LCD (Jupiter/VANGOGH), not OLED.

Title correction from the reporter, with the fair numbers. The original title ("collapses to 2-6 fps") measured my v3 protocol, which at an **entrance** save sat idle/spin right on the level-loading boundary - Angela caught it watching live ("your numbers for castle town will be messed up by level loading"). Re-measured all three runtimes with a v4 protocol: load -> walk ~10 s INTO the town (spawn faces inward) -> 20 s settle so streaming finishes -> idle samples -> full-speed spin. Same save, same config (rt-main `a4821d56`, GPU pinned 1600 MHz + performance governors, FSR3.1/50%, SD storage). | arm | idle | spin | re-upload events (log count for the arm) | |---|---|---|---| | rt-main (control) | **25.9** (tight 25.4-26.4, no dips) | **18.6** (range 8.8-22.5) | 12 | | pr-567 runtime | 24.4 (one 2.0 dip) | 11.8 (3.9-23.5) | 14 | | pr-590 runtime | 25.4 (24.3-25.5) | 14.6 (one 2.0 dip, 2.0-22.3) | 20 | Honest restatement: idle in town is clean at ~26 with no dips. The spin drop is **real and reproducible - about a 30% fps loss** (25.9 -> 18.6 median) - and the mechanism from the original report is still live in the logs (the GPU-written 691x432 texture re-uploaded per present, page-capture counts, 12-20 `re-uploaded` events per arm run). The 2-6 fps collapse is not the steady behavior at this spot; it shows up as an occasional single-sample dip, and sustained 2-6 is what the loading boundary produced, not the spin itself. Severity downgraded from "catastrophic stall" to "steady ~30% spin degradation with occasional deep dips"; title edited to match. Draws column above is post-walk-in, so the scene being measured is deeper in town than the original table's 5.6k. p0lux's #567 and #590 (the two PRs targeting this path) did NOT fix or meaningfully shrink the drop on this Deck LCD - separate comments on both PRs have the numbers and the cold-pipeline-cache confound. Deck LCD (Jupiter/VANGOGH), not OLED.
Contributor

Tried on a Steam Deck OLED on current main, without your save. The camera spin at the Market's entrance cost about a tenth of the frame rate here, with single-second dips to 9–11 fps. The re-upload of a GPU-written texture did not show.

Build and Deck: main 0557080f, a Linux build (x86-64-v3, LLVM 23.1.5). Steam Deck OLED (RADV VANGOGH, Mesa 26.2.0-devel), SteamOS 3.9.5, docked. GPU pinned at 1600 MHz; CPU governors left as they are.

Settings: yours:

  • FSR 3.1 at 50% (the log: fsr3: 640x400 -> 1280x800)
  • Game frame rate cap row 3, V-Sync off, Threaded rendering Off
  • RX_VK_READBACK_LATE=newest

Frame generation was off. Plus RX_VK_LOAD=create: without it, main keeps every noted graphics pipeline ready before the title and runs out of memory on this Deck at start-up.

How the Market was reached: the control bridge's debug teleport (Market_Child, the Market's entrance) from a Kokiri Forest save, not your "First Step into Hyrule Town" save. Arriving this way may differ from your save, for example in the time of day or what has loaded.

Then your v4 protocol: walk in for 10 s, settle 20 s, idle 15 s, a full-speed camera spin for 20 s, walk 10 s. The game's frame rate was read every second through the bridge. 2 runs:

run idle fps: median (range) spin fps: median (range) re-uploaded lines in the Market upload over a drawn image lines
1 18.6 (15.6–20.5) 16.6 (8.8–22.5) 14 0
2 20.6 (15.6–22.5) 18.6 (10.8–22.4) 3 0
  • Re-uploads: every re-uploaded line was a texture the CPU wrote (flags 0: 1144x3056 x3 and 576x576 ones). None was GPU-written (flags 0x8), and no 691x432 texture appeared.
  • GPU time a frame: idle 24.5 / 24.3 ms, spin 23.0 / 23.0 ms.

So on this OLED at the Market's entrance, the spin costs about a tenth rather than a third, and the loop in your report did not appear. Whether your save or the LCD is what brings it on is not shown here.

Tried on a Steam Deck OLED on current main, without your save. The camera spin at the Market's entrance cost about a tenth of the frame rate here, with single-second dips to 9–11 fps. The re-upload of a GPU-written texture did not show. **Build and Deck:** main 0557080f, a Linux build (x86-64-v3, LLVM 23.1.5). Steam Deck OLED (RADV VANGOGH, Mesa 26.2.0-devel), SteamOS 3.9.5, docked. GPU pinned at 1600 MHz; CPU governors left as they are. **Settings:** yours: - FSR 3.1 at 50% (the log: `fsr3: 640x400 -> 1280x800`) - Game frame rate cap row 3, V-Sync off, Threaded rendering Off - `RX_VK_READBACK_LATE=newest` Frame generation was off. Plus `RX_VK_LOAD=create`: without it, main keeps every noted graphics pipeline ready before the title and runs out of memory on this Deck at start-up. **How the Market was reached:** the control bridge's debug teleport (`Market_Child`, the Market's entrance) from a Kokiri Forest save, not your "First Step into Hyrule Town" save. Arriving this way may differ from your save, for example in the time of day or what has loaded. Then your v4 protocol: walk in for 10 s, settle 20 s, idle 15 s, a full-speed camera spin for 20 s, walk 10 s. The game's frame rate was read every second through the bridge. 2 runs: | run | idle fps: median (range) | spin fps: median (range) | `re-uploaded` lines in the Market | `upload over a drawn image` lines | |---|---|---|---|---| | 1 | 18.6 (15.6–20.5) | 16.6 (8.8–22.5) | 14 | 0 | | 2 | 20.6 (15.6–22.5) | 18.6 (10.8–22.4) | 3 | 0 | - **Re-uploads:** every `re-uploaded` line was a texture the CPU wrote (`flags 0`: 1144x3056 x3 and 576x576 ones). None was GPU-written (`flags 0x8`), and no 691x432 texture appeared. - **GPU time a frame:** idle 24.5 / 24.3 ms, spin 23.0 / 23.0 ms. So on this OLED at the Market's entrance, the spin costs about a tenth rather than a third, and the loop in your report did not appear. Whether your save or the LCD is what brings it on is not shown here.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
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/Longshot#750
No description provided.