fix(vk): make the blocks at the start in the pool the textures are made in (#2690) #2711

Closed
github-sync wants to merge 1 commit from github/pr/2711 into main
Owner

@hckrman101 opened this pull request on GitHub · 2026-11-12 08:17 UTC · original
Merged on GitHub in 1c0cd9803d

On an 8 GB card the image blocks made at the start (#2438) sit empty and are counted against the textures, so the texture budget is cut by about their size: #2690 measured Castle Town Market at 31-36 fps on an RTX 3070 against 54-56 with RX_VK_IMAGE_PREALLOC=0. This makes those blocks in the pool the textures are actually made in.

Fixes #2690

What was wrong

#2438 makes 15% of the card's memory (2 to 24 blocks of 128 MB) when image_pool is made, so that play makes fewer blocks: each is a vkAllocateMemory on the replay thread, ~170 ms on AMD's Windows driver. It was measured on v2.0.5-v2.0.12, where the sampled textures were made in that pool.

Since the compaction is on by default (#2198), sampled textures are made in texture_pool and render targets in target_image_pool. image_pool is left with DLSS's, the frame interpolation's, the menu's and the scratch images. So:

  • The blocks made at the start stay empty. On an 8 GB card that is 9 blocks, 1152 MB, which the driver counts and no texture can take. In my run they held 4 images, 16 MB.
  • The budget's hold takes only the textures' pool's free space off the driver's count (vram_compact::reclaimable), which is right: nothing else can be given back. So the empty blocks stay in the count and the hold cuts the texture budget by about as much.
  • #2438 no longer covers the allocations it was made for: the textures' blocks are made in play again, in texture_pool.

The fix

The blocks are made in the pool the sampled textures are made in: texture_pool, or image_pool with RX_VK_COMPACT=0. The count (15%, 2 to 24) and RX_VK_IMAGE_PREALLOC are unchanged, and the rule is the same for every vendor. On by default; no new switch.

  • What the blocks hold free is then the textures' pool's free space, which the budget already takes off the count, and the scene's textures fill them before VMA makes a new block.
  • VMA keeps those blocks whatever is in them (minBlockCount), so two rules are told about them:
    • a compaction run does not start for free space it cannot give back (vram_compact::returnable);
    • the squeeze at 97% of the allowance, which goes by the driver's count alone, waits while the textures' pool is down to those blocks (vram_compact::kept_only): an eviction there takes nothing off the count, the ratchet that rule's room_in_blocks wait already guards against.
  • The log says where they went: textures from a pool of their own, 128 MB blocks (RX_VK_COMPACT), 9 made at the start, and images from a pool of ... 0 made at the start.

Test

tests/test_blocks_at_start.py (with tests/test_blocks_at_start.cpp): compiles vram_compact.h and checks the block count for 2 to 32 GB cards (8 GB: 9, 12 GB: 14, 16 GB: 19, 24 GB: 24; none in a pool that holds no textures), returnable, kept_only, and what the budget's hold leaves the textures with the blocks in either pool (an 8 GB card with 7000 MB allowed, 3700 MB of other VRAM and 2300 MB of textures: 2250 MB with the blocks in the textures' pool or with none, 1098 MB with them in a pool no texture is made in). It also checks that vk_vma.cpp and vk_texture.cpp use the rules. It fails on the parent and passes here. test_vram_compact, test_texture_squeeze, test_target_image_pool, test_preset_texture_budget and test_vram_pressure pass; the full suite was not run.

Measured

RTX 3090 24 GB, Linux, NVIDIA, 2560x1440, modelling an 8 GB card with RX_VK_TEST_ALLOWED_MB=7000 (the allowance) and RX_VK_IMAGE_PREALLOC=9 (an 8 GB card's count; the count follows the real heap, 24 here). Castle Town Market, standing, 60 s settle, 120 s measured, one session a run, in the order D Z F F Z D. This reproduces the renderer's own eviction logic only, not Windows paging VRAM out on a real 8 GB card.

D = before the fix, 9 blocks at the start. Z = before the fix, RX_VK_IMAGE_PREALLOC=0. F = the fix, 9 blocks at the start. The renderer for F is this change on the commit the test folder's game was built from (main a016b7a6 plus two unrelated local commits; vk_vma.cpp, vk_texture.cpp and vram_compact.h are the same there as on main).

run last VRAM line image_pool textures' pool GPU a frame fps
D1 6720 MB used of 7000; 1568 MB free in the blocks; textures 2037 MB (budget 980 MB) 9 blocks 1152 MB, 4 images 16 MB 11 blocks 1408 MB, 1344 MB used 11.2 ms 45.5
Z1 6112 MB used; 751 MB free; textures 2039 MB (budget 1845 MB) 1 block, 16 MB 13 blocks 1664 MB, 1345 MB used 9.0 ms 50.0
F1 6160 MB used; 790 MB free; textures 2066 MB (budget 1798 MB) 1 block, 16 MB 13 blocks 1664 MB, 1371 MB used 9.2 ms 49.7
F2 6256 MB used; 762 MB free; textures 2078 MB (budget 1701 MB) 1 block, 16 MB 13 blocks 1664 MB, 1384 MB used 9.1 ms 47.8
Z2 6096 MB used; 759 MB free; textures 2064 MB (budget 1845 MB) 1 block, 16 MB 13 blocks 1664 MB, 1385 MB used 9.1 ms 46.4
D2 6832 MB used; 1641 MB free; textures 2062 MB (budget 867 MB) 9 blocks 1152 MB, 4 images 16 MB 11 blocks 1408 MB, 1370 MB used 11.5 ms 46.1
  • The budget reproduces #2690 before the fix (867-980 MB against 1845; the issue has 472-965 against 1117-1445), and with the fix it is where it is without the blocks (1701-1798 MB), with the driver's count 560-670 MB lower than D.
  • GPU time a frame (the renderer's own line, mean over the measured span): D 11.2 and 11.5 ms, Z 9.0 and 9.1, F 9.2 and 9.1.
  • The fps column is not a measurement. Every run had swap-in from other work on the PC (69,000 to 280,000 pages in the measured span), which disturbs frame times; only the VRAM lines and, with less confidence, the GPU times stand. The fps gap is also much smaller here than in the issue (no paging by the OS in this model).
  • Why D's GPU time is higher was not isolated (few textures were made again in any arm: 0.0-0.15 a frame); D sits at 96-98% of the allowance, where the renderer gives back its target pool and write-back shadows.
  • F's log: textures from a pool of their own, 128 MB blocks (RX_VK_COMPACT), 9 made at the start and images from a pool of 128 MB blocks (memory type 1), 0 made at the start.

Without the cap (24 GB, 24 blocks, 60 s measured), one run each: the fix 9601 MB used of 21814 (594 MB free in the blocks, textures 5736 MB, budget 6144 MB), before it 13089 MB used of 21846 (3782 MB free, textures 6057 MB, budget 6144 MB): the same budget, 3.5 GB less counted. GPU time 10.4 ms (fix) against 9.4 ms and fps 44.4 against 52.7 in that single pair, both with swap-in (54,000 and 63,000 pages); a second pair is being run to see whether that difference is real.

Not tested

  • No clean timing: every run above had swap-in, so there is no valid fps or frame-time comparison yet.
  • No real 8 GB card. The model caps what the renderer believes it is allowed, not what the card holds.
  • Not Windows.
  • Not AMD, where #2438's 170 ms allocations were measured. There the fix should bring back what #2438 gave: the first 15% of the card's memory in texture blocks exists before play, so a walk makes fewer vkAllocateMemory calls on the replay thread. That needs a run on an AMD card on Windows: the count of texture_pool blocks made in play, and the frame times when new ground comes in.
  • Known limit: while a scene has fewer textures than the blocks made at the start hold, the driver's count stays above what the hold aims for by the unused part (as it did before this change, by the whole of it). Without the blocks (RX_VK_IMAGE_PREALLOC=0) it would not.
> [@hckrman101](https://github.com/hckrman101) opened this pull request on GitHub · 2026-11-12 08:17 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2711) > **Merged on GitHub** in https://git.magicbean.dev/MagicBeanDev/Hookshot/commit/1c0cd9803d5951889535edbfadfb2be89d56e432 On an 8 GB card the image blocks made at the start (#2438) sit empty and are counted against the textures, so the texture budget is cut by about their size: #2690 measured Castle Town Market at 31-36 fps on an RTX 3070 against 54-56 with `RX_VK_IMAGE_PREALLOC=0`. This makes those blocks in the pool the textures are actually made in. Fixes #2690 ## What was wrong #2438 makes 15% of the card's memory (2 to 24 blocks of 128 MB) when `image_pool` is made, so that play makes fewer blocks: each is a `vkAllocateMemory` on the replay thread, ~170 ms on AMD's Windows driver. It was measured on v2.0.5-v2.0.12, where the sampled textures were made in that pool. Since the compaction is on by default (#2198), sampled textures are made in `texture_pool` and render targets in `target_image_pool`. `image_pool` is left with DLSS's, the frame interpolation's, the menu's and the scratch images. So: - The blocks made at the start stay empty. On an 8 GB card that is 9 blocks, 1152 MB, which the driver counts and no texture can take. In my run they held 4 images, 16 MB. - The budget's hold takes only the textures' pool's free space off the driver's count (`vram_compact::reclaimable`), which is right: nothing else can be given back. So the empty blocks stay in the count and the hold cuts the texture budget by about as much. - #2438 no longer covers the allocations it was made for: the textures' blocks are made in play again, in `texture_pool`. ## The fix The blocks are made in the pool the sampled textures are made in: `texture_pool`, or `image_pool` with `RX_VK_COMPACT=0`. The count (15%, 2 to 24) and `RX_VK_IMAGE_PREALLOC` are unchanged, and the rule is the same for every vendor. On by default; no new switch. - What the blocks hold free is then the textures' pool's free space, which the budget already takes off the count, and the scene's textures fill them before VMA makes a new block. - VMA keeps those blocks whatever is in them (`minBlockCount`), so two rules are told about them: - a compaction run does not start for free space it cannot give back (`vram_compact::returnable`); - the squeeze at 97% of the allowance, which goes by the driver's count alone, waits while the textures' pool is down to those blocks (`vram_compact::kept_only`): an eviction there takes nothing off the count, the ratchet that rule's `room_in_blocks` wait already guards against. - The log says where they went: `textures from a pool of their own, 128 MB blocks (RX_VK_COMPACT), 9 made at the start`, and `images from a pool of ... 0 made at the start`. ## Test `tests/test_blocks_at_start.py` (with `tests/test_blocks_at_start.cpp`): compiles `vram_compact.h` and checks the block count for 2 to 32 GB cards (8 GB: 9, 12 GB: 14, 16 GB: 19, 24 GB: 24; none in a pool that holds no textures), `returnable`, `kept_only`, and what the budget's hold leaves the textures with the blocks in either pool (an 8 GB card with 7000 MB allowed, 3700 MB of other VRAM and 2300 MB of textures: 2250 MB with the blocks in the textures' pool or with none, 1098 MB with them in a pool no texture is made in). It also checks that `vk_vma.cpp` and `vk_texture.cpp` use the rules. It fails on the parent and passes here. `test_vram_compact`, `test_texture_squeeze`, `test_target_image_pool`, `test_preset_texture_budget` and `test_vram_pressure` pass; the full suite was not run. ## Measured RTX 3090 24 GB, Linux, NVIDIA, 2560x1440, modelling an 8 GB card with `RX_VK_TEST_ALLOWED_MB=7000` (the allowance) and `RX_VK_IMAGE_PREALLOC=9` (an 8 GB card's count; the count follows the real heap, 24 here). Castle Town Market, standing, 60 s settle, 120 s measured, one session a run, in the order D Z F F Z D. **This reproduces the renderer's own eviction logic only, not Windows paging VRAM out on a real 8 GB card.** D = before the fix, 9 blocks at the start. Z = before the fix, `RX_VK_IMAGE_PREALLOC=0`. F = the fix, 9 blocks at the start. The renderer for F is this change on the commit the test folder's game was built from (main a016b7a6 plus two unrelated local commits; `vk_vma.cpp`, `vk_texture.cpp` and `vram_compact.h` are the same there as on main). | run | last VRAM line | `image_pool` | textures' pool | GPU a frame | fps | |---|---|---|---|---|---| | D1 | 6720 MB used of 7000; 1568 MB free in the blocks; textures 2037 MB (budget 980 MB) | 9 blocks 1152 MB, 4 images 16 MB | 11 blocks 1408 MB, 1344 MB used | 11.2 ms | 45.5 | | Z1 | 6112 MB used; 751 MB free; textures 2039 MB (budget 1845 MB) | 1 block, 16 MB | 13 blocks 1664 MB, 1345 MB used | 9.0 ms | 50.0 | | F1 | 6160 MB used; 790 MB free; textures 2066 MB (budget 1798 MB) | 1 block, 16 MB | 13 blocks 1664 MB, 1371 MB used | 9.2 ms | 49.7 | | F2 | 6256 MB used; 762 MB free; textures 2078 MB (budget 1701 MB) | 1 block, 16 MB | 13 blocks 1664 MB, 1384 MB used | 9.1 ms | 47.8 | | Z2 | 6096 MB used; 759 MB free; textures 2064 MB (budget 1845 MB) | 1 block, 16 MB | 13 blocks 1664 MB, 1385 MB used | 9.1 ms | 46.4 | | D2 | 6832 MB used; 1641 MB free; textures 2062 MB (budget 867 MB) | 9 blocks 1152 MB, 4 images 16 MB | 11 blocks 1408 MB, 1370 MB used | 11.5 ms | 46.1 | - The budget reproduces #2690 before the fix (867-980 MB against 1845; the issue has 472-965 against 1117-1445), and with the fix it is where it is without the blocks (1701-1798 MB), with the driver's count 560-670 MB lower than D. - GPU time a frame (the renderer's own line, mean over the measured span): D 11.2 and 11.5 ms, Z 9.0 and 9.1, F 9.2 and 9.1. - **The fps column is not a measurement.** Every run had swap-in from other work on the PC (69,000 to 280,000 pages in the measured span), which disturbs frame times; only the VRAM lines and, with less confidence, the GPU times stand. The fps gap is also much smaller here than in the issue (no paging by the OS in this model). - Why D's GPU time is higher was not isolated (few textures were made again in any arm: 0.0-0.15 a frame); D sits at 96-98% of the allowance, where the renderer gives back its target pool and write-back shadows. - F's log: `textures from a pool of their own, 128 MB blocks (RX_VK_COMPACT), 9 made at the start` and `images from a pool of 128 MB blocks (memory type 1), 0 made at the start`. Without the cap (24 GB, 24 blocks, 60 s measured), one run each: the fix 9601 MB used of 21814 (594 MB free in the blocks, textures 5736 MB, budget 6144 MB), before it 13089 MB used of 21846 (3782 MB free, textures 6057 MB, budget 6144 MB): the same budget, 3.5 GB less counted. GPU time 10.4 ms (fix) against 9.4 ms and fps 44.4 against 52.7 in that single pair, both with swap-in (54,000 and 63,000 pages); a second pair is being run to see whether that difference is real. ## Not tested - No clean timing: every run above had swap-in, so there is no valid fps or frame-time comparison yet. - **No real 8 GB card.** The model caps what the renderer believes it is allowed, not what the card holds. - **Not Windows.** - **Not AMD**, where #2438's 170 ms allocations were measured. There the fix should bring back what #2438 gave: the first 15% of the card's memory in texture blocks exists before play, so a walk makes fewer `vkAllocateMemory` calls on the replay thread. That needs a run on an AMD card on Windows: the count of `texture_pool` blocks made in play, and the frame times when new ground comes in. - Known limit: while a scene has fewer textures than the blocks made at the start hold, the driver's count stays above what the hold aims for by the unused part (as it did before this change, by the whole of it). Without the blocks (`RX_VK_IMAGE_PREALLOC=0`) it would not. <!-- github-sync:item:2711 -->
#2438 makes 15% of the card's memory (2 to 24 blocks of 128 MB) when the
images' pool is made, so that play makes fewer of them: each is a
vkAllocateMemory on the replay thread, ~170 ms on AMD's Windows driver.
It was measured on v2.0.5-v2.0.12, where the sampled textures were made
in that pool. Since the compaction is on by default (#2198) they are
made in a pool of their own, and the images' pool holds DLSS's, the
frame interpolation's and the scratch images only.

So the blocks made at the start stayed empty: on an 8 GB card 1152 MB
that the driver counts and no texture can take. The budget's hold takes
only the textures' pool's free space off the count, rightly, so it cut
the textures by about as much. And the textures' blocks were still made
in play, which is what #2438 was for.

The blocks are now made in the pool the sampled textures are made in:
the textures' own, or the images' with RX_VK_COMPACT=0. The count, the
15% and RX_VK_IMAGE_PREALLOC are as before, for every vendor. What the
blocks hold free is then the textures' pool's free space, which the
budget takes off the count already, and the scene's textures fill them.

VMA keeps those blocks whatever is in them, so two rules know of them:
a compaction run does not start for their free space
(vram_compact::returnable), and the squeeze at 97% of the allowance,
which goes by the driver's count alone, waits while the textures' pool
is down to them (vram_compact::kept_only): an eviction there takes
nothing off the count.

tests/test_blocks_at_start.py runs the rules for 2 to 32 GB cards and
what the budget's hold leaves the textures with the blocks in either
pool.

Fixes #2690

Co-Authored-By: Claude Fable 5.2 <[email protected]>
Author
Owner

@SadTransGirl commented on GitHub · 2026-11-12 10:01 UTC · original

Tested #2711 on Windows 11, RTX 4070 SUPER (12 GB), NVIDIA, DLAA 2560x1440, default settings, nothing else running on the PC. Your model: RX_VK_TEST_ALLOWED_MB=7000 and RX_VK_IMAGE_PREALLOC=9; Castle Town Market standing (walked in from the south gate, the last 120 s measured); order D Z F F Z D, one run each. D = main c544828f, Z = main with RX_VK_IMAGE_PREALLOC=0, F = #2711 (2124d15c).

run fps (last 120 s) frames > 25 ms VRAM lines over the allowance last VRAM line image_pool GPU ms a frame
D1 56.9 35 17 of 19 8283 MB used of 7000; 1687 free in the blocks; textures 2991 (budget 349) 9 blocks 1152 MB, 102 MB used 11.1
Z1 58.9 14 0 of 19 6619; 724 free; textures 2643 (budget 1794) 1 block 9.7
F1 59.3 33 0 of 19 6587; 498 free; textures 2804 (budget 1833) 1 block 9.5
F2 59.0 28 1 of 19 6759; 770 free; textures 2659 (budget 1606) 1 block 9.7
Z2 58.6 27 0 of 19 6557; 431 free; textures 2873 (budget 2057) 1 block 9.6
D2 56.0 50 11 of 18 7721; 1375 free; textures 3287 (budget 1244) 9 blocks 1152 MB, 102 MB used 11.0

F's log: "textures from a pool of their own, 128 MB blocks (RX_VK_COMPACT), 9 made at the start" and "images from a pool of 128 MB blocks (memory type 1), 0 made at the start".

A 5-minute Hyrule Field walk under the same cap, one run each:

fps frames > 25 ms VRAM mean / peak textures uploaded GPU ms
D 57.9 314 7153 / 8013 MB 15.8 GB 11.5
Z 59.0 125 6472 / 6995 MB 11.0 GB 10.4
F 59.5 58 6441 / 6963 MB 6.8 GB 10.1

Without the cap (12 GB card, 14 blocks at the start), Market, one pair (the second pair you asked about): F 58.1 fps over the whole run, GPU 9.1 ms, textures 4322 MB (budget 6144); D 49.4 fps, GPU 10.7 ms, textures 2944 MB (budget 2890), 4.4 GB of textures uploaded against 1.0 GB. Both at 60 fps in the last 60 s. So here no sign of F costing GPU time; the other way round.

So on Windows/NVIDIA: F matches Z on VRAM and the texture budget, and is at least as fast. The one thing F didn't match was frames over 25 ms standing in the Market (28-33 against Z's 14-27); in the walk F had the fewest. Not tested: AMD, a real 8 GB card, Linux.

> [@SadTransGirl](https://github.com/SadTransGirl) commented on GitHub · 2026-11-12 10:01 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2711#issuecomment-6820563932) Tested #2711 on Windows 11, RTX 4070 SUPER (12 GB), NVIDIA, DLAA 2560x1440, default settings, nothing else running on the PC. Your model: `RX_VK_TEST_ALLOWED_MB=7000` and `RX_VK_IMAGE_PREALLOC=9`; Castle Town Market standing (walked in from the south gate, the last 120 s measured); order D Z F F Z D, one run each. D = main c544828f, Z = main with `RX_VK_IMAGE_PREALLOC=0`, F = #2711 (2124d15c). | run | fps (last 120 s) | frames > 25 ms | VRAM lines over the allowance | last VRAM line | image_pool | GPU ms a frame | |---|---|---|---|---|---|---| | D1 | 56.9 | 35 | 17 of 19 | 8283 MB used of 7000; 1687 free in the blocks; textures 2991 (budget 349) | 9 blocks 1152 MB, 102 MB used | 11.1 | | Z1 | 58.9 | 14 | 0 of 19 | 6619; 724 free; textures 2643 (budget 1794) | 1 block | 9.7 | | F1 | 59.3 | 33 | 0 of 19 | 6587; 498 free; textures 2804 (budget 1833) | 1 block | 9.5 | | F2 | 59.0 | 28 | 1 of 19 | 6759; 770 free; textures 2659 (budget 1606) | 1 block | 9.7 | | Z2 | 58.6 | 27 | 0 of 19 | 6557; 431 free; textures 2873 (budget 2057) | 1 block | 9.6 | | D2 | 56.0 | 50 | 11 of 18 | 7721; 1375 free; textures 3287 (budget 1244) | 9 blocks 1152 MB, 102 MB used | 11.0 | F's log: "textures from a pool of their own, 128 MB blocks (RX_VK_COMPACT), 9 made at the start" and "images from a pool of 128 MB blocks (memory type 1), 0 made at the start". A 5-minute Hyrule Field walk under the same cap, one run each: | | fps | frames > 25 ms | VRAM mean / peak | textures uploaded | GPU ms | |---|---|---|---|---|---| | D | 57.9 | 314 | 7153 / 8013 MB | 15.8 GB | 11.5 | | Z | 59.0 | 125 | 6472 / 6995 MB | 11.0 GB | 10.4 | | F | 59.5 | 58 | 6441 / 6963 MB | 6.8 GB | 10.1 | Without the cap (12 GB card, 14 blocks at the start), Market, one pair (the second pair you asked about): F 58.1 fps over the whole run, GPU 9.1 ms, textures 4322 MB (budget 6144); D 49.4 fps, GPU 10.7 ms, textures 2944 MB (budget 2890), 4.4 GB of textures uploaded against 1.0 GB. Both at 60 fps in the last 60 s. So here no sign of F costing GPU time; the other way round. So on Windows/NVIDIA: F matches Z on VRAM and the texture budget, and is at least as fast. The one thing F didn't match was frames over 25 ms standing in the Market (28-33 against Z's 14-27); in the walk F had the fewest. Not tested: AMD, a real 8 GB card, Linux. <!-- github-sync:c:6820563932 -->
Author
Owner

@klikso commented on GitHub · 2026-11-12 10:06 UTC · original

Tested on a real 8 GB card: with this PR the default is as fast as RX_VK_IMAGE_PREALLOC=0, and both are well above main.

Windows 11, RTX 3060 Ti 8 GB (driver 32.0.16.2114), Ryzen 5 5600X, 2560x1440 DLSS. main ba005ca4 with this PR merged locally; 40 s with the camera turning after a settle, RX_VK_FRAMEGEN=0, HOS_FPS_MAX=240; arms alternated D Z D Z D Z.

The log says what it should: images from a pool of 128 MB blocks (memory type 1), 0 made at the start and textures from a pool of their own, 128 MB blocks (RX_VK_COMPACT), 9 made at the start (D), 0 made at the start for both with Z.

fps median fps mean frames over 50 ms in 40 s last VRAM line
Hyrule Field, D (this PR as is) 57.2 / 59.3 / 53.8 55.5 / 57.7 / 51.6 2 / 0 / 11 520 MB free in the blocks; textures 1991 MB (budget 1342 MB)
Hyrule Field, Z (RX_VK_IMAGE_PREALLOC=0) 58.2 / 57.7 / 59.4 56.5 / 55.9 / 57.4 1 / 6 / 2 651 MB free in the blocks; textures 2203 MB (budget 1501 MB)
Castle Town Market, D 38.1 / 38.7 37.8 / 36.4 80 / 98 948 MB free in the blocks; textures 2686 MB (budget 1821 MB)
Castle Town Market, Z 30.8 / 38.3 33.1 / 38.6 143 / 81 778 MB free in the blocks; textures 2599 MB (budget 1726 MB)

For reference, main 5b96a55a without this PR in the same Hyrule Field test (my comment on #2690): 44.9 / 46.5 / 46.0 fps mean, 30-53 frames over 50 ms, texture budget ~1 GB. The third D run in the field is the slowest of the six; the rest are within each other's noise, and the Market's GPU time moves from launch to launch on this card, so I read it as no difference between D and Z.

> [@klikso](https://github.com/klikso) commented on GitHub · 2026-11-12 10:06 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2711#issuecomment-6820669701) Tested on a real 8 GB card: with this PR the default is as fast as `RX_VK_IMAGE_PREALLOC=0`, and both are well above `main`. Windows 11, RTX 3060 Ti 8 GB (driver 32.0.16.2114), Ryzen 5 5600X, 2560x1440 DLSS. `main` ba005ca4 with this PR merged locally; 40 s with the camera turning after a settle, `RX_VK_FRAMEGEN=0`, `HOS_FPS_MAX=240`; arms alternated D Z D Z D Z. The log says what it should: `images from a pool of 128 MB blocks (memory type 1), 0 made at the start` and `textures from a pool of their own, 128 MB blocks (RX_VK_COMPACT), 9 made at the start` (D), `0 made at the start` for both with Z. | | fps median | fps mean | frames over 50 ms in 40 s | last VRAM line | |---|---|---|---|---| | Hyrule Field, D (this PR as is) | 57.2 / 59.3 / 53.8 | 55.5 / 57.7 / 51.6 | 2 / 0 / 11 | 520 MB free in the blocks; textures 1991 MB (budget 1342 MB) | | Hyrule Field, Z (`RX_VK_IMAGE_PREALLOC=0`) | 58.2 / 57.7 / 59.4 | 56.5 / 55.9 / 57.4 | 1 / 6 / 2 | 651 MB free in the blocks; textures 2203 MB (budget 1501 MB) | | Castle Town Market, D | 38.1 / 38.7 | 37.8 / 36.4 | 80 / 98 | 948 MB free in the blocks; textures 2686 MB (budget 1821 MB) | | Castle Town Market, Z | 30.8 / 38.3 | 33.1 / 38.6 | 143 / 81 | 778 MB free in the blocks; textures 2599 MB (budget 1726 MB) | For reference, `main` 5b96a55a without this PR in the same Hyrule Field test (my comment on #2690): 44.9 / 46.5 / 46.0 fps mean, 30-53 frames over 50 ms, texture budget ~1 GB. The third D run in the field is the slowest of the six; the rest are within each other's noise, and the Market's GPU time moves from launch to launch on this card, so I read it as no difference between D and Z. <!-- github-sync:c:6820669701 -->
Author
Owner

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

@hckrman101 I'll take this one over, since you offered it in the team room and nobody has picked it up there or here.

What I'll do, on a branch that keeps your commits as they are with mine on top (it has to be a new pull request, as I can't push to yours; I'll link it here and you can close this one then):

  • rebase onto current main, your tests both ways with GCC and Clang, the C++ fixture for the Windows target, the full suite;
  • Linux, RTX 4090 under your 8 GB model (RX_VK_TEST_ALLOWED_MB=7000 RX_VK_IMAGE_PREALLOC=9), Castle Town Market: main, preallocation off, and the fix, with the pool line, the budget, retired textures, compactions and pictures; then one alternated timing hold, main against the fix;
  • the cases still open in the description: the real 24 GB allowance, compaction off, the allocation-failure fallback, and few textures with the kept blocks holding the driver's count high;
  • how it sits with #2759's preallocation hunks, and with my #2624 (@klikso's slow Market runs this morning had both; I am measuring #2624 on its own under the same model right now);
  • the description brought up to date with @SadTransGirl's and @klikso's results as theirs.

Not something this PC can give: AMD, and a real 8 GB card; those stay listed as not tested by me.

> [@mooki000](https://github.com/mooki000) commented on GitHub · 2026-11-12 12:37 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2711#issuecomment-6823812784) @hckrman101 I'll take this one over, since you offered it in the team room and nobody has picked it up there or here. What I'll do, on a branch that keeps your commits as they are with mine on top (it has to be a new pull request, as I can't push to yours; I'll link it here and you can close this one then): - rebase onto current main, your tests both ways with GCC and Clang, the C++ fixture for the Windows target, the full suite; - Linux, RTX 4090 under your 8 GB model (`RX_VK_TEST_ALLOWED_MB=7000 RX_VK_IMAGE_PREALLOC=9`), Castle Town Market: main, preallocation off, and the fix, with the pool line, the budget, retired textures, compactions and pictures; then one alternated timing hold, main against the fix; - the cases still open in the description: the real 24 GB allowance, compaction off, the allocation-failure fallback, and few textures with the kept blocks holding the driver's count high; - how it sits with #2759's preallocation hunks, and with my #2624 (@klikso's slow Market runs this morning had both; I am measuring #2624 on its own under the same model right now); - the description brought up to date with @SadTransGirl's and @klikso's results as theirs. Not something this PC can give: AMD, and a real 8 GB card; those stay listed as not tested by me. <!-- github-sync:c:6823812784 -->
Author
Owner

@Brett-D commented on GitHub · 2026-11-12 13:03 UTC · original

Tested on a real 24+ GB card (RTX 5090, 32 GB VRAM, Windows 11, 98 GB RAM, NVIDIA 621.14) as part of isolating main's +5.86 GB Private Bytes / +2.51 GB Working Set increase since 49f75dc8.

Setup & method: origin/main (c544828f) built with RX_RELEASE=1 (17,888 programs, 25,303 graphics and 799 compute pipelines precompiled at startup), 2048x1152 uncapped (RX_VK_FRAMEGEN=0, HOS_FPS_MAX=240), Kokiri Forest autosave (80 s run: 40 s settle + 30 s measurement window), compared against main control, main + RX_VK_IMAGE_PREALLOC=0 (the zero-preallocation bound), and #2759.

Why main has +2.94 GB of stranded VMA blocks on 24+ GB cards:
On main, images_pool() pre-allocates minBlockCount = 24 (3,072 MB on a >=20 GB allowance), while RX_VK_COMPACT (textures_pool()) and RX_VK_TARGET_BLOCKS (target_images_pool()) are on by default. In Kokiri Forest, only 8 images (33 MB, 1 block) go into image_pool, leaving 23 of 24 blocks (2,944 MB / 2.88 GB) permanently empty while textures_pool allocates another 18 blocks (2,304 MB for 1,208 textures / 2,192 MB) on top (24 + 18 = 42 blocks = 5,376 MB across both pools).

Arm (RX_RELEASE=1, 25,303 pipelines) Peak Working Set Peak Private Bytes Δ Private vs main VMA Heap 0 Blocks image_pool / target / texture blocks Free in VMA blocks Uncapped FPS (p95 / p99 ms) Frames > 50 ms
main control 17,458 MB 33,818 MB baseline 142 (9,357 MB) 24 (3072 MB) / 6 (768 MB) / 18 (2304 MB) 3,484 MB 67.2 (17.07 / 19.61) 0
main + RX_VK_IMAGE_PREALLOC=0 (bound) 17,411 MB 29,053 MB (main2) -3,087 MB 118 (6,477 MB) 1 (128 MB) / 7 (896 MB) / 18 (2304 MB) 680 MB 63.8 (18.06 / 19.40) 0
#2711 (hckrman101) 17,455 MB 31,601 MB -2,217 MB (-2.17 GB) 123 (7,021 MB) 1 (128 MB) / 5 (640 MB) / 24 (3072 MB) 1,157 MB 68.2 (16.79 / 20.25) 0
#2759 (Hinkiii) 17,303 MB 30,534 MB -3,284 MB (-3.21 GB) 115 (6,237 MB) 1 (128 MB) / 6 (768 MB) / 18 (2304 MB) 544 MB 65.5 (17.38 / 19.98) 0

Verdict on a 24+ GB card:

  • Moving minBlockCount = 24 from images_pool() to textures_pool() drops image_pool from 24 blocks (3,072 MB) to 1 block (128 MB, 8 images 33 MB) and places the 18 active Kokiri Forest texture blocks (2,192 MB) inside the 24 pre-allocated textures_pool blocks (1 + 24 = 25 blocks = 3,200 MB instead of 24 + 18 = 42 blocks = 5,376 MB).
  • That recovers 17 VMA blocks = 2,176 MB (2.13 GB) of device pool memory and -2,217 MB (-2.17 GB) of Peak Private Bytes (74% of the 2,944 MB stranded image_pool pre-allocation in this scene), while keeping 6 spare blocks (768 MB) pre-allocated ahead of demand in textures_pool (at the title screen, where only 1 texture block is used, 23 blocks = 2,944 MB remain pre-allocated in textures_pool, whereas RX_VK_IMAGE_PREALLOC=0 / #2759 allocate texture blocks on demand with 0 frames > 50 ms here).
> [@Brett-D](https://github.com/Brett-D) commented on GitHub · 2026-11-12 13:03 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2711#issuecomment-6824364611) Tested on a real 24+ GB card (`RTX 5090`, 32 GB VRAM, Windows 11, 98 GB RAM, NVIDIA 621.14) as part of isolating `main`'s +5.86 GB Private Bytes / +2.51 GB Working Set increase since `49f75dc8`. **Setup & method:** `origin/main` (`c544828f`) built with `RX_RELEASE=1` (17,888 programs, 25,303 graphics and 799 compute pipelines precompiled at startup), 2048x1152 uncapped (`RX_VK_FRAMEGEN=0`, `HOS_FPS_MAX=240`), Kokiri Forest autosave (80 s run: 40 s settle + 30 s measurement window), compared against `main` control, `main + RX_VK_IMAGE_PREALLOC=0` (the zero-preallocation bound), and #2759. **Why `main` has +2.94 GB of stranded VMA blocks on 24+ GB cards:** On `main`, `images_pool()` pre-allocates `minBlockCount = 24` (`3,072 MB` on a >=20 GB allowance), while `RX_VK_COMPACT` (`textures_pool()`) and `RX_VK_TARGET_BLOCKS` (`target_images_pool()`) are on by default. In Kokiri Forest, only 8 images (`33 MB`, 1 block) go into `image_pool`, leaving **23 of 24 blocks (`2,944 MB` / `2.88 GB`) permanently empty** while `textures_pool` allocates another 18 blocks (`2,304 MB` for 1,208 textures / `2,192 MB`) on top (`24 + 18 = 42 blocks = 5,376 MB` across both pools). | Arm (`RX_RELEASE=1`, 25,303 pipelines) | Peak Working Set | Peak Private Bytes | Δ Private vs `main` | VMA Heap 0 Blocks | `image_pool` / `target` / `texture` blocks | Free in VMA blocks | Uncapped FPS (`p95` / `p99` ms) | Frames > 50 ms | |---|---|---|---|---|---|---|---|---| | `main` control | 17,458 MB | 33,818 MB | baseline | 142 (`9,357 MB`) | `24 (3072 MB)` / `6 (768 MB)` / `18 (2304 MB)` | 3,484 MB | 67.2 (`17.07` / `19.61`) | 0 | | `main` + `RX_VK_IMAGE_PREALLOC=0` (bound) | 17,411 MB | 29,053 MB (`main2`) | **-3,087 MB** | 118 (`6,477 MB`) | `1 (128 MB)` / `7 (896 MB)` / `18 (2304 MB)` | 680 MB | 63.8 (`18.06` / `19.40`) | 0 | | **#2711 (`hckrman101`)** | **17,455 MB** | **31,601 MB** | **-2,217 MB (-2.17 GB)** | **123 (`7,021 MB`)** | **`1 (128 MB)` / `5 (640 MB)` / `24 (3072 MB)`** | **1,157 MB** | **68.2 (`16.79` / `20.25`)** | **0** | | #2759 (`Hinkiii`) | 17,303 MB | 30,534 MB | -3,284 MB (-3.21 GB) | 115 (`6,237 MB`) | `1 (128 MB)` / `6 (768 MB)` / `18 (2304 MB)` | 544 MB | 65.5 (`17.38` / `19.98`) | 0 | **Verdict on a 24+ GB card:** - Moving `minBlockCount = 24` from `images_pool()` to `textures_pool()` drops `image_pool` from `24 blocks (3,072 MB)` to `1 block (128 MB, 8 images 33 MB)` and places the 18 active Kokiri Forest texture blocks (`2,192 MB`) *inside* the 24 pre-allocated `textures_pool` blocks (`1 + 24 = 25 blocks = 3,200 MB` instead of `24 + 18 = 42 blocks = 5,376 MB`). - That recovers **17 VMA blocks = `2,176 MB` (`2.13 GB`) of device pool memory and `-2,217 MB` (`-2.17 GB`) of Peak Private Bytes** (`74%` of the `2,944 MB` stranded `image_pool` pre-allocation in this scene), while keeping 6 spare blocks (`768 MB`) pre-allocated ahead of demand in `textures_pool` (at the title screen, where only 1 texture block is used, 23 blocks = `2,944 MB` remain pre-allocated in `textures_pool`, whereas `RX_VK_IMAGE_PREALLOC=0` / #2759 allocate texture blocks on demand with 0 frames > 50 ms here). <!-- github-sync:c:6824364611 -->
github-sync closed this pull request 2026-11-12 16:55:50 +00:00
Author
Owner

@klikso commented on GitHub · 2026-11-12 16:09 UTC · original

A note on my measurements in this thread: this PC's DLSS model row was on Transformer 2 (L), not the default K, for all of them. L costs ~4 ms more GPU time a frame on this card and more VRAM (#2836), so my absolute fps are ~10 lower than the default would give in Hyrule Field and the card was shorter of memory than a default setup. The comparisons were all made with the same setting in both arms, so their direction stands, but their size is for that harder case.

> [@klikso](https://github.com/klikso) commented on GitHub · 2026-11-12 16:09 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2711#issuecomment-6828922588) A note on my measurements in this thread: this PC's DLSS model row was on Transformer 2 (L), not the default K, for all of them. L costs ~4 ms more GPU time a frame on this card and more VRAM (#2836), so my absolute fps are ~10 lower than the default would give in Hyrule Field and the card was shorter of memory than a default setup. The comparisons were all made with the same setting in both arms, so their direction stands, but their size is for that harder case. <!-- github-sync:c:6828922588 -->
Author
Owner

@mooki000 commented on GitHub · 2026-11-12 17:27 UTC · original

Linux numbers for the record, now that this is merged. My Linux correctness runs predate the merge: main 51da0464, the fix build 234f72a3 (your unchanged patch plus tests/docs), and the combined experiment 3972579c (+#2624).

RTX 4090 24 GB, NVIDIA 615.78.08, Arch Linux 7.2.15-1-omarchy; Castle Town Market, 2560×1440, DLSS model K (the default model), DLAA/100%, FG off, cap 240. Klikso's measurements used model L.

Under RX_VK_TEST_ALLOWED_MB=7000 RX_VK_IMAGE_PREALLOC=9 RX_VK_MIRROR_MB=2048, main kept nine utility-image blocks for 102 MiB of images; the fix kept one for the same images and started nine texture blocks.

Final texture budgets: main 835 MiB, main with preallocation off 2003 MiB, fix 1771 MiB, fix+#2624 1921 MiB. These are different-population snapshots from ordinary 150-second runs, not a timing or an isolated subtraction.

No fatal/device/driver fault; eight inspected Market frames showed no obvious missing or garbled textures.

The small-card model does not reproduce Windows paging or a real 8-GB heap. No speed claim: the exclusive timing holds were cancelled after the merge, before either was queued.

A small test-only follow-up is coming as its own pull request: it runs the real pool constructors, including a refused first allocation, which the merged test checks by source text only.

> [@mooki000](https://github.com/mooki000) commented on GitHub · 2026-11-12 17:27 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2711#issuecomment-6830907242) Linux numbers for the record, now that this is merged. My Linux correctness runs predate the merge: main `51da0464`, the fix build `234f72a3` (your unchanged patch plus tests/docs), and the combined experiment `3972579c` (+#2624). RTX 4090 24 GB, NVIDIA 615.78.08, Arch Linux 7.2.15-1-omarchy; Castle Town Market, 2560×1440, **DLSS model K (the default model), DLAA/100%**, FG off, cap 240. Klikso's measurements used model L. Under `RX_VK_TEST_ALLOWED_MB=7000 RX_VK_IMAGE_PREALLOC=9 RX_VK_MIRROR_MB=2048`, main kept nine utility-image blocks for 102 MiB of images; the fix kept one for the same images and started nine texture blocks. Final texture budgets: main 835 MiB, main with preallocation off 2003 MiB, fix 1771 MiB, fix+#2624 1921 MiB. These are different-population snapshots from ordinary 150-second runs, not a timing or an isolated subtraction. No fatal/device/driver fault; eight inspected Market frames showed no obvious missing or garbled textures. The small-card model does not reproduce Windows paging or a real 8-GB heap. No speed claim: the exclusive timing holds were cancelled after the merge, before either was queued. A small test-only follow-up is coming as its own pull request: it runs the real pool constructors, including a refused first allocation, which the merged test checks by source text only. <!-- github-sync:c:6830907242 -->
Commenting is not possible because the repository is archived.
No description provided.