fix(vk): stop the VRAM compaction's hold from evicting textures in use #2198

Merged
SadTransGirl merged 9 commits from fix/vram-hold-cold into main 2026-11-11 19:06:47 +00:00
SadTransGirl commented 2026-11-11 07:13:26 +00:00 (Migrated from github.com)

With RX_VK_COMPACT=1 on a card with little room, textures that were still in use were thrown out and loaded again every time the camera turned, and at some places the game could freeze for a few seconds. Both came from the rule that holds VRAM at 85%; this fixes that rule and gives it a small reserve. With that fixed, the compaction is now on by default (RX_VK_COMPACT=0 turns it off), as hckrman101 suggested in Discord.

Tracking: #995. Follow-up to #1821 and to @hckrman101's 6 GB result there (4-5x the frames over 33 ms with the switch on).

What went wrong

The hold sets the texture budget to "what the textures hold, less the excess over 85% of the allowance". Whenever the count stays over that line (a small card, a burst of new textures, another program taking VRAM), every pass is a pass over the budget, and two things follow:

  1. What went had been unused for 60 presents. Right for a budget that is a size. Under the hold, every texture out of view for a second went and came back.
  2. Dead render targets were never taken. A pass over the budget takes the oldest textures first and stops at the first one past the excess. The render targets another has drawn over (a frame's scratch targets) are the youngest, and the sweep that takes them only runs under the budget. The other rules cut to 7/8 once, so sweeps resume on the next pass; the hold stays over. With other programs holding VRAM (10.2-10.4 GB allowed), where a new stretch of Hyrule Field comes in, the render targets' pool went from 896 to 3,456 MB in 120 presents while 2,600 live textures went, the count passed what the driver allows, and 179 of 291 frames were over 25 ms (60-128 ms).

The change

  • A pass the hold asked for counts what the dead render targets hold first and lets all of them go, and takes live textures only for the excess that is left, and only ones unused for 600 presents (vram_compact::hold_met, hold_keeps, kHoldUnused).
  • A reserve: a second line five points of the allowance under the hold's (80 for 85), over which textures unused for 1,800 presents go (reserve_pct, hold_target, kReserveUnused). With one line the cache sat at it and whatever came in next went over.
  • Passes the other rules ask for (the budget itself, the 90% and 97% rules) are as they were.
  • The compaction is on unless RX_VK_COMPACT=0 (vram_compact::enabled: not set is on, 0 or empty is off). It was opt-in while the hold could let go of textures in use.

Numbers

Built from this branch (v2.0.11-782-gf0c6e73) and from main (v2.0.11-780-g3932421). Unattended runs with scripted input, DLAA 2560x1440, RTX 4070 SUPER (617.42), Ryzen 9 9950X3D, Windows 11, nothing else running, two runs each. Frames from RX_VK_FRAME_TRACE. RX_VK_TEST_ALLOWED_MB stands in for a smaller card or for other programs.

Camera turning in place in Hyrule Field, RX_VK_COMPACT=1 RX_VK_TEST_ALLOWED_MB=7000, 50 seconds:

fps textures uploaded textures retired frames over 25 ms 1% low
main, switch off 60.0 1,313 MB 3,481 6 42.5
main 59.0, 59.2 6,573, 5,954 MB 13,701, 12,622 42, 27 34.8, 35.3
this branch 60.0, 60.0 1,284, 1,284 MB 4,044, 3,814 7, 5 42.4, 44.3

Five-minute walk across Hyrule Field, RX_VK_COMPACT=1 RX_VK_ASTC_BC_ALL=1:

allowance frames over 25 / 33 / 50 ms shadows given back windows with GPU time over 12.5 ms 1% low
main 9,500 MB (capped) 120/29/10, 93/16/3 4, 5 times 8, 11 of 124 30.6, 35.5
this branch 9,500 MB (capped) 86/17/1, 92/13/1 0, 0 0, 2 of 125 35.9, 36.9
main 11.2 GB (real) 93/24/7, 97/28/11 2, 2 times 1, 4 of 125 33.2, 31.2
this branch 11.2 GB (real) 96/19/0, 65/8/1 0, 0 2, 2 of 125 35.9, 38.2

No validation, device-lost or "not made" line in any run.

Those runs were on main as it was yesterday (3932421). After rebasing onto today's main (4d68bc8) I ran each once more, built from this branch (v2.1.0-rc.3-50-g3acf477a) and from main (v2.1.0-rc.3-48-g4d68bc83), counted from the end of the load:

fps textures uploaded frames over 25 / 33 / 50 ms shadows given back 1% low
spin at 7,000 MB, main 59.6 6,842 MB 16/0/0 0 39.1
spin at 7,000 MB, this branch 60.0 1,187 MB 0/0/0 once, while the save loaded 47.2
walk at 9,500 MB, main 59.5 7,383 MB 104/26/6 5 times 33.6
walk at 9,500 MB, this branch 59.9 4,753 MB 29/8/1 0 42.3

In that spin run the branch's count passed the allowance once while the save loaded (7,016 of 7,000 MB), which is what gave the shadows back; main's run did not, and neither did the four spin runs on the earlier base. One run, so I can't say whether it is the branch or chance.

On by default against off

Built from this branch after the default changed (v2.1.0-rc.3-58-g06ce5785, main 49f75dc merged in). No switch set against RX_VK_COMPACT=0, DLAA 2560x1440, the same card, nothing else running, counted from the end of the load.

Five-minute walk across Hyrule Field, two runs each:

fps frames over 25 / 33 / 50 ms 1% low VRAM in the mean (share of the allowance) shadows given back
on (the default now) 59.8, 59.9 66/12/2, 36/2/1 37.7, 40.9 9.05-9.07 GB (85%) 0, 0
RX_VK_COMPACT=0 56.0, 56.0 633/60/15, 408/72/28 25.8, 24.1 10.21-10.29 GB (95-96%) 8, 8 times

Camera turning in place with RX_VK_TEST_ALLOWED_MB=7000, one run each: on 60.0 fps, 1,203 MB uploaded, 1 frame over 25 ms; off 59.9 fps, 2,660 MB, 8 frames. No validation, device-lost or "not made" line in any of these.

On my local build (the same two commits on an older base, with a measuring hook that holds 768 MB of the card), the reserve alone took the share of a walk spent over 85% of the allowance from 47-68% to 2-5%, and over 87% from 7-26% to 0-1%, with uploads and frame times unchanged.

Limits

  • One Tier 1 configuration (NVIDIA RTX 40, Windows), Hyrule Field only, unattended runs without a window; plus played sessions in a real window on the local build (DLAA, with and without a recorder): no error, and the multi-second freezes gone.
  • Not tested by me: AMD, Intel, Linux, a real small card, Kokiri Forest outdoors (the scene of the 6 GB report), cutscenes. The test reports in this thread cover Linux with an RTX 4070 Ti, an RTX 5080 and an RX 6600 8 GB, each with RX_VK_COMPACT=1.
  • On by default is measured on this one card only. Nobody has run it yet on a 4-6 GB card, an Intel or integrated GPU, or the Steam Deck: RX_VK_COMPACT=0 is the way back there.
  • The small allowance is simulated on a 12 GB card, as in the report.
  • On today's main each test was run once, not twice.
  • 600 presents was tried against 300 (alike); the reserve at 80 with 1,800 presents; other values were not.
  • With VRAM really short (the count at 93-96% of the allowance), stretches of 10-20 s with the GPU at 18-21 ms a frame remain: nothing is left that the cache can let go. This PR does not address that.

Test

python -I -B tests/test_vram_compact.py: the rules' arithmetic (hold_keeps, hold_met, reserve_pct, hold_target), and source checks that the hold's pass counts the dead targets before it chooses, does not stop at the excess, judges a live texture only once it knows whether a target drew over it, and that the driver's limit clears both lines; and enabled: not set is on, 0 or empty is off. Fails on main.

🤖 Generated with Claude Code

With `RX_VK_COMPACT=1` on a card with little room, textures that were still in use were thrown out and loaded again every time the camera turned, and at some places the game could freeze for a few seconds. Both came from the rule that holds VRAM at 85%; this fixes that rule and gives it a small reserve. With that fixed, the compaction is now on by default (`RX_VK_COMPACT=0` turns it off), as hckrman101 suggested in Discord. Tracking: #995. Follow-up to #1821 and to @hckrman101's 6 GB result there (4-5x the frames over 33 ms with the switch on). ## What went wrong The hold sets the texture budget to "what the textures hold, less the excess over 85% of the allowance". Whenever the count stays over that line (a small card, a burst of new textures, another program taking VRAM), every pass is a pass over the budget, and two things follow: 1. **What went had been unused for 60 presents.** Right for a budget that is a size. Under the hold, every texture out of view for a second went and came back. 2. **Dead render targets were never taken.** A pass over the budget takes the oldest textures first and stops at the first one past the excess. The render targets another has drawn over (a frame's scratch targets) are the youngest, and the sweep that takes them only runs under the budget. The other rules cut to 7/8 once, so sweeps resume on the next pass; the hold stays over. With other programs holding VRAM (10.2-10.4 GB allowed), where a new stretch of Hyrule Field comes in, the render targets' pool went from 896 to 3,456 MB in 120 presents while 2,600 live textures went, the count passed what the driver allows, and 179 of 291 frames were over 25 ms (60-128 ms). ## The change - A pass the hold asked for counts what the dead render targets hold first and lets all of them go, and takes live textures only for the excess that is left, and only ones unused for 600 presents (`vram_compact::hold_met`, `hold_keeps`, `kHoldUnused`). - A reserve: a second line five points of the allowance under the hold's (80 for 85), over which textures unused for 1,800 presents go (`reserve_pct`, `hold_target`, `kReserveUnused`). With one line the cache sat at it and whatever came in next went over. - Passes the other rules ask for (the budget itself, the 90% and 97% rules) are as they were. - The compaction is on unless `RX_VK_COMPACT=0` (`vram_compact::enabled`: not set is on, `0` or empty is off). It was opt-in while the hold could let go of textures in use. ## Numbers Built from this branch (v2.0.11-782-gf0c6e73) and from main (v2.0.11-780-g3932421). Unattended runs with scripted input, DLAA 2560x1440, RTX 4070 SUPER (617.42), Ryzen 9 9950X3D, Windows 11, nothing else running, two runs each. Frames from `RX_VK_FRAME_TRACE`. `RX_VK_TEST_ALLOWED_MB` stands in for a smaller card or for other programs. Camera turning in place in Hyrule Field, `RX_VK_COMPACT=1 RX_VK_TEST_ALLOWED_MB=7000`, 50 seconds: | | fps | textures uploaded | textures retired | frames over 25 ms | 1% low | |---|---|---|---|---|---| | main, switch off | 60.0 | 1,313 MB | 3,481 | 6 | 42.5 | | main | 59.0, 59.2 | 6,573, 5,954 MB | 13,701, 12,622 | 42, 27 | 34.8, 35.3 | | this branch | 60.0, 60.0 | 1,284, 1,284 MB | 4,044, 3,814 | 7, 5 | 42.4, 44.3 | Five-minute walk across Hyrule Field, `RX_VK_COMPACT=1 RX_VK_ASTC_BC_ALL=1`: | | allowance | frames over 25 / 33 / 50 ms | shadows given back | windows with GPU time over 12.5 ms | 1% low | |---|---|---|---|---|---| | main | 9,500 MB (capped) | 120/29/10, 93/16/3 | 4, 5 times | 8, 11 of 124 | 30.6, 35.5 | | this branch | 9,500 MB (capped) | 86/17/1, 92/13/1 | 0, 0 | 0, 2 of 125 | 35.9, 36.9 | | main | 11.2 GB (real) | 93/24/7, 97/28/11 | 2, 2 times | 1, 4 of 125 | 33.2, 31.2 | | this branch | 11.2 GB (real) | 96/19/0, 65/8/1 | 0, 0 | 2, 2 of 125 | 35.9, 38.2 | No validation, device-lost or "not made" line in any run. Those runs were on main as it was yesterday (3932421). After rebasing onto today's main (4d68bc8) I ran each once more, built from this branch (v2.1.0-rc.3-50-g3acf477a) and from main (v2.1.0-rc.3-48-g4d68bc83), counted from the end of the load: | | fps | textures uploaded | frames over 25 / 33 / 50 ms | shadows given back | 1% low | |---|---|---|---|---|---| | spin at 7,000 MB, main | 59.6 | 6,842 MB | 16/0/0 | 0 | 39.1 | | spin at 7,000 MB, this branch | 60.0 | 1,187 MB | 0/0/0 | once, while the save loaded | 47.2 | | walk at 9,500 MB, main | 59.5 | 7,383 MB | 104/26/6 | 5 times | 33.6 | | walk at 9,500 MB, this branch | 59.9 | 4,753 MB | 29/8/1 | 0 | 42.3 | In that spin run the branch's count passed the allowance once while the save loaded (7,016 of 7,000 MB), which is what gave the shadows back; main's run did not, and neither did the four spin runs on the earlier base. One run, so I can't say whether it is the branch or chance. ### On by default against off Built from this branch after the default changed (v2.1.0-rc.3-58-g06ce5785, main 49f75dc merged in). No switch set against `RX_VK_COMPACT=0`, DLAA 2560x1440, the same card, nothing else running, counted from the end of the load. Five-minute walk across Hyrule Field, two runs each: | | fps | frames over 25 / 33 / 50 ms | 1% low | VRAM in the mean (share of the allowance) | shadows given back | |---|---|---|---|---|---| | on (the default now) | 59.8, 59.9 | 66/12/2, 36/2/1 | 37.7, 40.9 | 9.05-9.07 GB (85%) | 0, 0 | | `RX_VK_COMPACT=0` | 56.0, 56.0 | 633/60/15, 408/72/28 | 25.8, 24.1 | 10.21-10.29 GB (95-96%) | 8, 8 times | Camera turning in place with `RX_VK_TEST_ALLOWED_MB=7000`, one run each: on 60.0 fps, 1,203 MB uploaded, 1 frame over 25 ms; off 59.9 fps, 2,660 MB, 8 frames. No validation, device-lost or "not made" line in any of these. On my local build (the same two commits on an older base, with a measuring hook that holds 768 MB of the card), the reserve alone took the share of a walk spent over 85% of the allowance from 47-68% to 2-5%, and over 87% from 7-26% to 0-1%, with uploads and frame times unchanged. ## Limits - One Tier 1 configuration (NVIDIA RTX 40, Windows), Hyrule Field only, unattended runs without a window; plus played sessions in a real window on the local build (DLAA, with and without a recorder): no error, and the multi-second freezes gone. - Not tested by me: AMD, Intel, Linux, a real small card, Kokiri Forest outdoors (the scene of the 6 GB report), cutscenes. The test reports in this thread cover Linux with an RTX 4070 Ti, an RTX 5080 and an RX 6600 8 GB, each with `RX_VK_COMPACT=1`. - On by default is measured on this one card only. Nobody has run it yet on a 4-6 GB card, an Intel or integrated GPU, or the Steam Deck: `RX_VK_COMPACT=0` is the way back there. - The small allowance is simulated on a 12 GB card, as in the report. - On today's main each test was run once, not twice. - 600 presents was tried against 300 (alike); the reserve at 80 with 1,800 presents; other values were not. - With VRAM really short (the count at 93-96% of the allowance), stretches of 10-20 s with the GPU at 18-21 ms a frame remain: nothing is left that the cache can let go. This PR does not address that. ## Test `python -I -B tests/test_vram_compact.py`: the rules' arithmetic (`hold_keeps`, `hold_met`, `reserve_pct`, `hold_target`), and source checks that the hold's pass counts the dead targets before it chooses, does not stop at the excess, judges a live texture only once it knows whether a target drew over it, and that the driver's limit clears both lines; and `enabled`: not set is on, `0` or empty is off. Fails on main. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Joey-Magnus commented 2026-11-11 07:29:59 +00:00 (Migrated from github.com)

Tested on Linux, which the PR lists as untested. It holds up there too.

Setup:

  • PC: Ubuntu 26.04.1, KDE Plasma 6.6 on Wayland, three monitors. RTX 4070 Ti 12 GB with driver 595.99.02, Ryzen 7 9800X3D, 32 GB.
  • Build: this PR's two code commits cherry-picked onto a main-based build, renderer hookshot_vk.so only (v2.0.11-795-g8baccd6b host). The docs commit was left out.
  • Test: DLAA at 2560x1440, RX_VK_COMPACT=1, a player's own daytime Hyrule Field save. 50 seconds of the camera turning in place after the load, frames from RX_VK_FRAME_TRACE. A and B differ only in the renderer, and the runs alternated between them.
Run fps Frames over 25 / 33 / 50 ms 1% low Uploaded Retired
Before, RX_VK_TEST_ALLOWED_MB=7000 59.9 23 / 13 / 0 31.4 8,491 MB 14,316
Before, RX_VK_TEST_ALLOWED_MB=7000 59.9 24 / 10 / 0 31.7 7,034 MB 12,637
This PR, RX_VK_TEST_ALLOWED_MB=7000 60.0 2 / 1 / 0 48.2 1,290 MB 2,324
This PR, RX_VK_TEST_ALLOWED_MB=7000 60.0 2 / 1 / 0 47.5 1,260 MB 2,459
Before, real allowance (~9 GB) 60.0 4 / 1 / 0 45.9 1,276 MB 1,760
This PR, real allowance 60.0 2 / 0 / 0 48.7 1,294 MB 1,942

Results:

  • Under the 7,000 MB allowance, frames over 25 ms dropped by about 90%, and uploads went from 7-8.5 GB to 1.3 GB.
  • At the card's real allowance the PR is neutral to slightly better.
  • No validation, device-lost or crash lines in any run.
  • tests/test_vram_compact.py and the full tests/run_all.py pass (330 passed, 0 failed, 10 skipped).

🤖 Generated with Claude Code

Tested on Linux, which the PR lists as untested. It holds up there too. **Setup:** - **PC:** Ubuntu 26.04.1, KDE Plasma 6.6 on Wayland, three monitors. RTX 4070 Ti 12 GB with driver 595.99.02, Ryzen 7 9800X3D, 32 GB. - **Build:** this PR's two code commits cherry-picked onto a main-based build, renderer `hookshot_vk.so` only (`v2.0.11-795-g8baccd6b` host). The docs commit was left out. - **Test:** DLAA at 2560x1440, `RX_VK_COMPACT=1`, a player's own daytime Hyrule Field save. 50 seconds of the camera turning in place after the load, frames from `RX_VK_FRAME_TRACE`. A and B differ only in the renderer, and the runs alternated between them. | Run | fps | Frames over 25 / 33 / 50 ms | 1% low | Uploaded | Retired | |---|---|---|---|---|---| | Before, `RX_VK_TEST_ALLOWED_MB=7000` | 59.9 | 23 / 13 / 0 | 31.4 | 8,491 MB | 14,316 | | Before, `RX_VK_TEST_ALLOWED_MB=7000` | 59.9 | 24 / 10 / 0 | 31.7 | 7,034 MB | 12,637 | | **This PR**, `RX_VK_TEST_ALLOWED_MB=7000` | 60.0 | 2 / 1 / 0 | 48.2 | 1,290 MB | 2,324 | | **This PR**, `RX_VK_TEST_ALLOWED_MB=7000` | 60.0 | 2 / 1 / 0 | 47.5 | 1,260 MB | 2,459 | | Before, real allowance (~9 GB) | 60.0 | 4 / 1 / 0 | 45.9 | 1,276 MB | 1,760 | | **This PR**, real allowance | 60.0 | 2 / 0 / 0 | 48.7 | 1,294 MB | 1,942 | **Results:** - Under the 7,000 MB allowance, frames over 25 ms dropped by about 90%, and uploads went from 7-8.5 GB to 1.3 GB. - At the card's real allowance the PR is neutral to slightly better. - No validation, device-lost or crash lines in any run. - `tests/test_vram_compact.py` and the full `tests/run_all.py` pass (330 passed, 0 failed, 10 skipped). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
nullifyshot commented 2026-11-11 08:15:31 +00:00 (Migrated from github.com)

I tested this on Windows with an RTX 5080 (16 GB), using the same test as Joey-Magnus above. It holds up here too.

Setup:

  • PC: Windows 11, RTX 5080 16 GB.
  • Build: this PR's head (18a5be0) built with the Builder's llvm-mingw. The "before" renderer is main 4d68bc8 with the two code files reverted, built into the same folder. Only hookshot_vk.dll differs between A and B. The runs alternated: before, PR, before, PR.
  • Test: DLAA at 1920x1080, frame generation off, RX_VK_COMPACT=1, capped at 60 fps (HOS_FPS_MAX=60), daytime Hyrule Field save. 50 seconds of the camera turning in place after the load, with frames from RX_VK_FRAME_TRACE. Headless (HOS_HEADLESS=1), with the save loaded and the camera turned through HOS_INPUT_LIVE.
Run fps Frames over 25 / 33 / 50 ms 1% low Uploaded Retired
Before, RX_VK_TEST_ALLOWED_MB=7000 60.0 0 / 0 / 0 45.1 7,887 MB 17,231
This PR, RX_VK_TEST_ALLOWED_MB=7000 59.9 4 / 0 / 0 43.3 708 MB 3,264
Before, RX_VK_TEST_ALLOWED_MB=7000 58.7 11 / 0 / 0 39.8 7,844 MB 16,994
This PR, RX_VK_TEST_ALLOWED_MB=7000 59.9 3 / 0 / 0 43.7 699 MB 3,769
Before, real allowance (15,209 MB) 60.0 1 / 0 / 0 44.1 568 MB 2,894
This PR, real allowance 60.0 3 / 0 / 0 44.1 577 MB 3,151

(1% low here is the average frame rate of the slowest 1% of frames. Uploaded is the sum of the trace's upload_mb, and retired is the sum of the log's "N textures retired" lines, both over the 50 seconds.)

Results:

  • Under the 7,000 MB allowance: uploads dropped by about 91% (7.8–7.9 GB down to about 0.7 GB) and retired textures by about 79%. The churn is the same as Joey's "before" runs, and the PR removes it.
  • Frame times at 7,000 MB: about the same. The 5080 absorbs the extra uploads: one before run had 11 frames over 25 ms and a 1% low of 39.8, the other had none. The PR runs had 3–4 frames just over 25 ms and none over 33 ms. So on a fast card the gain is mostly in the work saved, while Joey's 4070 Ti shows what that churn costs in frame times.
  • At the card's real allowance: neutral. Uploads, retirements and the 1% low match within noise.
  • No validation, device-lost or "not made" lines in any of the six runs.

🤖 Generated with Claude Code

I tested this on Windows with an RTX 5080 (16 GB), using the same test as Joey-Magnus above. It holds up here too. **Setup:** - **PC:** Windows 11, RTX 5080 16 GB. - **Build:** this PR's head (18a5be0) built with the Builder's llvm-mingw. The "before" renderer is main 4d68bc8 with the two code files reverted, built into the same folder. Only `hookshot_vk.dll` differs between A and B. The runs alternated: before, PR, before, PR. - **Test:** DLAA at 1920x1080, frame generation off, `RX_VK_COMPACT=1`, capped at 60 fps (`HOS_FPS_MAX=60`), daytime Hyrule Field save. 50 seconds of the camera turning in place after the load, with frames from `RX_VK_FRAME_TRACE`. Headless (`HOS_HEADLESS=1`), with the save loaded and the camera turned through `HOS_INPUT_LIVE`. | Run | fps | Frames over 25 / 33 / 50 ms | 1% low | Uploaded | Retired | |---|---|---|---|---|---| | Before, `RX_VK_TEST_ALLOWED_MB=7000` | 60.0 | 0 / 0 / 0 | 45.1 | 7,887 MB | 17,231 | | **This PR**, `RX_VK_TEST_ALLOWED_MB=7000` | 59.9 | 4 / 0 / 0 | 43.3 | 708 MB | 3,264 | | Before, `RX_VK_TEST_ALLOWED_MB=7000` | 58.7 | 11 / 0 / 0 | 39.8 | 7,844 MB | 16,994 | | **This PR**, `RX_VK_TEST_ALLOWED_MB=7000` | 59.9 | 3 / 0 / 0 | 43.7 | 699 MB | 3,769 | | Before, real allowance (15,209 MB) | 60.0 | 1 / 0 / 0 | 44.1 | 568 MB | 2,894 | | **This PR**, real allowance | 60.0 | 3 / 0 / 0 | 44.1 | 577 MB | 3,151 | (1% low here is the average frame rate of the slowest 1% of frames. Uploaded is the sum of the trace's `upload_mb`, and retired is the sum of the log's "N textures retired" lines, both over the 50 seconds.) **Results:** - **Under the 7,000 MB allowance:** uploads dropped by about 91% (7.8–7.9 GB down to about 0.7 GB) and retired textures by about 79%. The churn is the same as Joey's "before" runs, and the PR removes it. - **Frame times at 7,000 MB:** about the same. The 5080 absorbs the extra uploads: one before run had 11 frames over 25 ms and a 1% low of 39.8, the other had none. The PR runs had 3–4 frames just over 25 ms and none over 33 ms. So on a fast card the gain is mostly in the work saved, while Joey's 4070 Ti shows what that churn costs in frame times. - **At the card's real allowance:** neutral. Uploads, retirements and the 1% low match within noise. - **No validation, device-lost or "not made" lines** in any of the six runs. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Tempus3214 commented 2026-11-11 08:17:56 +00:00 (Migrated from github.com)

Tested this on an 8 GB AMD card, where the hold matters most, and it removes most of the camera-turn hitches with RX_VK_COMPACT=1.

Setup

  • RX 6600 8 GB (Navi 23), driver 32.0.21045.16010 (Vulkan 0x800161), Ryzen 5 4500, Windows 10 21H2. The driver allows 7.23–7.26 GB.
  • 1920x1080 window, Handheld profile (1536x864).
  • Renderer rebuilt from the v2.1.0 tag (source 633eb435) with this PR's vk_texture.cpp and vram_compact.h changes. The docs hunk conflicts with the release tag and was left out. Built with the builder's llvm-mingw (clang 23.1.5), RX_VK_ONLY=1 RX_RELEASE=1. tests/test_vram_compact.py --cxx clang++ passes.
  • Both arms: RX_VK_FORCE_MESH_FALLBACK=1, RX_VK_CHECKPOINTS=1, RX_VK_RECORDER=thread. Only RX_VK_COMPACT differs.

Method

  • The same save in Kakariko Village at night (~3,600 draws a frame).
  • An identical 62 s HOS_INPUT_LIVE script after the load: camera sweeps left and right plus two short walks. Both arms ended in the same spot.
  • Frame times from RX_VK_FRAME_LOG.
  • Both arms had RX_VK_SHOT_EVERY=60 on, and each shot costs a 55–65 ms frame. Those presents (and the one after) are left out of the numbers below.
62 s, same script RX_VK_COMPACT off RX_VK_COMPACT=1 with this PR
Frames 1,998 2,223
Mean fps 32.2 35.9
Median frame 29.8 ms 27.4 ms
p99 frame 57.3 ms 42.8 ms
Frames over 40 ms 191 42
Frames over 50 ms 44 6
Frames over 70 ms 8 2
Frame-to-frame jumps over 10 ms 154 64
Free space inside the blocks (VRAM: lines) 553–1,021 MB 315–588 MB

With the PR, textures made settled at 0.4–0.8 a frame, so the churn described above did not show up. Half of the remaining frames over 50 ms came in one burst on the first camera turn, as new views streamed in. No device loss, validation or "not made" lines in either run. A normal play session afterwards showed no visual problems.

Caveats

  • One pair of runs, and the compaction arm ran first, with colder OS file caches.
  • I did not run v2.1.0's compaction without this fix, so this compares the fixed compaction against compaction off, not against the old hold.
Tested this on an 8 GB AMD card, where the hold matters most, and it removes most of the camera-turn hitches with `RX_VK_COMPACT=1`. **Setup** - RX 6600 8 GB (Navi 23), driver 32.0.21045.16010 (Vulkan 0x800161), Ryzen 5 4500, Windows 10 21H2. The driver allows 7.23–7.26 GB. - 1920x1080 window, Handheld profile (1536x864). - Renderer rebuilt from the v2.1.0 tag (source `633eb435`) with this PR's `vk_texture.cpp` and `vram_compact.h` changes. The docs hunk conflicts with the release tag and was left out. Built with the builder's llvm-mingw (clang 23.1.5), `RX_VK_ONLY=1 RX_RELEASE=1`. `tests/test_vram_compact.py --cxx clang++` passes. - Both arms: `RX_VK_FORCE_MESH_FALLBACK=1`, `RX_VK_CHECKPOINTS=1`, `RX_VK_RECORDER=thread`. Only `RX_VK_COMPACT` differs. **Method** - The same save in Kakariko Village at night (~3,600 draws a frame). - An identical 62 s `HOS_INPUT_LIVE` script after the load: camera sweeps left and right plus two short walks. Both arms ended in the same spot. - Frame times from `RX_VK_FRAME_LOG`. - Both arms had `RX_VK_SHOT_EVERY=60` on, and each shot costs a 55–65 ms frame. Those presents (and the one after) are left out of the numbers below. | 62 s, same script | `RX_VK_COMPACT` off | `RX_VK_COMPACT=1` with this PR | |---|---|---| | Frames | 1,998 | 2,223 | | Mean fps | 32.2 | 35.9 | | Median frame | 29.8 ms | 27.4 ms | | p99 frame | 57.3 ms | 42.8 ms | | Frames over 40 ms | 191 | 42 | | Frames over 50 ms | 44 | 6 | | Frames over 70 ms | 8 | 2 | | Frame-to-frame jumps over 10 ms | 154 | 64 | | Free space inside the blocks (`VRAM:` lines) | 553–1,021 MB | 315–588 MB | With the PR, textures made settled at 0.4–0.8 a frame, so the churn described above did not show up. Half of the remaining frames over 50 ms came in one burst on the first camera turn, as new views streamed in. No device loss, validation or "not made" lines in either run. A normal play session afterwards showed no visual problems. **Caveats** - One pair of runs, and the compaction arm ran first, with colder OS file caches. - I did not run v2.1.0's compaction without this fix, so this compares the fixed compaction against compaction off, not against the old hold.
buasfe commented 2026-11-11 11:50:13 +00:00 (Migrated from github.com)

Hi! I've been troubleshooting the persistent FPS degradation on an RTX 3070 8 GB and wanted to share my results, as I think they might be relevant to this PR.

System:

  • GPU: RTX 3070 Gaming X Trio, 8 GB VRAM
  • CPU: Ryzen 7 5800X3D
  • RAM: 32 GB DDR4
  • OS: Windows 11
  • Resolution: 3440×1440
  • Hookshot: v2.1.0
  • Using the Fritz v3 60 FPS patch

The issue:

The game can run smoothly at approximately 50–60 FPS while exploring Hyrule Field or Kakariko Village. However, transitioning between these areas can cause a significant, persistent performance drop.

For example:

  1. Load directly into Hyrule Field and explore normally: approximately 50–60 FPS.
  2. Enter Kakariko Village through the loading transition.
  3. Performance sometimes drops to 20–30 FPS and doesn't fully recover.
  4. Returning to Hyrule Field doesn't necessarily restore performance, even in areas that previously ran at 50–60 FPS.

I've also compared two recordings in the same location using essentially the same route. The main difference was whether I'd travelled through Kakariko beforehand. The session that had gone through Kakariko performed significantly worse.

This makes me suspect something is remaining allocated or otherwise affecting performance after an area transition.

What I've tested:

Initially, I experimented with the following settings:

  • RX_VK_VRAM_MB=2560
  • RX_VK_FAST_RECLAIM=1
  • RX_VK_FAST_PRESSURE=80
  • RX_VK_FAST_UNUSED=8

These helped somewhat, but the persistent degradation still occurred.

I also experimented with RX_VK_MIRROR_MB=1024, which didn't resolve the problem, so I removed it.

Enabling RX_VK_COMPACT=1 made the biggest difference.

With compaction enabled alongside the earlier settings, the game became noticeably smoother. Areas that previously struggled could run at approximately 50–60 FPS again, and performance after transitions initially seemed substantially better.

I then tested RX_VK_COMPACT=1 by itself, without the other VRAM overrides.

Initially, this also performed very well. However, during a longer session, I experienced another prolonged FPS collapse. Performance eventually recovered, but there were periods around 12–21 FPS.

The longer log (hookshot_log(8).txt) contains 17 instances of:

vk: over the driver's video-memory allowance

The reported allocation sometimes reached approximately 7.6 GB against an allowance of about 7.1 GB.

Compaction was actively running and returning memory to the driver, but it wasn't consistently preventing these episodes.

Why I'm posting here:

I've read the description of this PR and noticed the problem involving textures being evicted after only 60 presents, then repeatedly reloaded when needed again.

I'm wondering whether that could explain why performance sometimes remains degraded after travelling between Kakariko and Hyrule Field.

I understand that the current fix has been tested with camera movement within an area, but I thought the loading-screen transition behaviour might also be worth investigating.

I've attached logs from the different configurations, including one where compaction substantially helped and a longer run where performance degraded again.

I'd be happy to test the proposed fix on my RTX 3070, particularly with repeated Kakariko ↔ Hyrule Field transitions, if a suitable build becomes available.

Thanks for the work on this project — the improvements over the last few days have already been crazyyy
03_Additional_long_test.txt
02_Compaction_only_long_test.txt
01_Compaction_plus_tweaks.txt
!

Hi! I've been troubleshooting the persistent FPS degradation on an RTX 3070 8 GB and wanted to share my results, as I think they might be relevant to this PR. **System:** - GPU: RTX 3070 Gaming X Trio, 8 GB VRAM - CPU: Ryzen 7 5800X3D - RAM: 32 GB DDR4 - OS: Windows 11 - Resolution: 3440×1440 - Hookshot: v2.1.0 - Using the Fritz v3 60 FPS patch **The issue:** The game can run smoothly at approximately 50–60 FPS while exploring Hyrule Field or Kakariko Village. However, transitioning between these areas can cause a significant, persistent performance drop. For example: 1. Load directly into Hyrule Field and explore normally: approximately 50–60 FPS. 2. Enter Kakariko Village through the loading transition. 3. Performance sometimes drops to 20–30 FPS and doesn't fully recover. 4. Returning to Hyrule Field doesn't necessarily restore performance, even in areas that previously ran at 50–60 FPS. I've also compared two recordings in the same location using essentially the same route. The main difference was whether I'd travelled through Kakariko beforehand. The session that had gone through Kakariko performed significantly worse. This makes me suspect something is remaining allocated or otherwise affecting performance after an area transition. **What I've tested:** Initially, I experimented with the following settings: - `RX_VK_VRAM_MB=2560` - `RX_VK_FAST_RECLAIM=1` - `RX_VK_FAST_PRESSURE=80` - `RX_VK_FAST_UNUSED=8` These helped somewhat, but the persistent degradation still occurred. I also experimented with `RX_VK_MIRROR_MB=1024`, which didn't resolve the problem, so I removed it. **Enabling `RX_VK_COMPACT=1` made the biggest difference.** With compaction enabled alongside the earlier settings, the game became noticeably smoother. Areas that previously struggled could run at approximately 50–60 FPS again, and performance after transitions initially seemed substantially better. I then tested `RX_VK_COMPACT=1` by itself, without the other VRAM overrides. Initially, this also performed very well. However, during a longer session, I experienced another prolonged FPS collapse. Performance eventually recovered, but there were periods around 12–21 FPS. The longer log (`hookshot_log(8).txt`) contains 17 instances of: `vk: over the driver's video-memory allowance` The reported allocation sometimes reached approximately 7.6 GB against an allowance of about 7.1 GB. Compaction was actively running and returning memory to the driver, but it wasn't consistently preventing these episodes. **Why I'm posting here:** I've read the description of this PR and noticed the problem involving textures being evicted after only 60 presents, then repeatedly reloaded when needed again. I'm wondering whether that could explain why performance sometimes remains degraded after travelling between Kakariko and Hyrule Field. I understand that the current fix has been tested with camera movement within an area, but I thought the loading-screen transition behaviour might also be worth investigating. I've attached logs from the different configurations, including one where compaction substantially helped and a longer run where performance degraded again. I'd be happy to test the proposed fix on my RTX 3070, particularly with repeated Kakariko ↔ Hyrule Field transitions, if a suitable build becomes available. Thanks for the work on this project — the improvements over the last few days have already been crazyyy [03_Additional_long_test.txt](https://github.com/user-attachments/files/34686024/03_Additional_long_test.txt) [02_Compaction_only_long_test.txt](https://github.com/user-attachments/files/34686025/02_Compaction_only_long_test.txt) [01_Compaction_plus_tweaks.txt](https://github.com/user-attachments/files/34686026/01_Compaction_plus_tweaks.txt) !
hckrman101 (Migrated from github.com) reviewed 2026-11-11 11:59:21 +00:00
hckrman101 (Migrated from github.com) left a comment

Thanks, this is a clear diagnosis and the fix for the hold's pass reads right. Counting the dead render targets first and then not stopping at the first texture past the excess fixes the starvation you describe. The prepass uses the same conditions as the loop (need_dead, last_value > completed, shifted/drawn_over), so hold_dead is exactly what the loop then lets go: no dead target is counted and then kept. The fence check (tex->last_value > completed) still comes before any texture is chosen, so nothing the GPU may still read gets evicted. The header is plain integer math and builds the same with llvm-mingw and gcc/clang. Joey-Magnus (Linux, 4070 Ti), nullifyshot (Windows, 5080) and Tempus3214 (RX 6600 8 GB) all confirm it on hardware you didn't have. CI is green.

One thing I'd like settled before merge. The description says passes from the other rules (the budget, 90%, 97%) are unchanged, and the test says the driver's limit clears both lines. In practice that clearing almost never happens. The 90% rule and the reserve share the same pressure, so the reserve's excess is always at least the 90% rule's plus 10% of the allowance. Whenever the 90% rule fires under RX_VK_COMPACT, reserve < budget holds and the pass becomes a hold pass. The 97% rule only wins (budget < evict_to) when reclaim is over ~17% of the allowance. So past 90% and even past 97%, a live texture now has to be unused for 600 presents before it can go, where main let it go after min_unused (60). That is exactly the situation of the 8 GB reports: your own Limits note (93-96% stretches, nothing left to let go) and buasfe's log with the count over the allowance 17 times on a 3070. Paging past the allowance costs more than re-uploads. I'd keep the 600/1800 thresholds for the part of the excess that only the 85/80 lines ask for, and let textures past need_unused go for the part the 90%/97% rules ask for. For example, record the budget those rules set before the hold applies (rule_to) and give hold_target a third tier for it, so that rule's excess is served as before. A test case for "count over 90%: a texture unused 100 presents still goes" would pin it.

Nits:

  • buasfe's question (Kakariko <-> Hyrule Field transitions on an 8 GB 3070) isn't answered yet. A test build for them would cover the scene change and the small real card, both listed as untested.
  • The if (budget < evict_to) evict_to = hold_to = 0; comment ("the driver's limit asks for more") reads as if this is the usual path near the limit. A word that it rarely fires once the reserve is set would help the next reader.
Thanks, this is a clear diagnosis and the fix for the hold's pass reads right. Counting the dead render targets first and then not stopping at the first texture past the excess fixes the starvation you describe. The prepass uses the same conditions as the loop (`need_dead`, `last_value > completed`, shifted/drawn_over), so `hold_dead` is exactly what the loop then lets go: no dead target is counted and then kept. The fence check (`tex->last_value > completed`) still comes before any texture is chosen, so nothing the GPU may still read gets evicted. The header is plain integer math and builds the same with llvm-mingw and gcc/clang. Joey-Magnus (Linux, 4070 Ti), nullifyshot (Windows, 5080) and Tempus3214 (RX 6600 8 GB) all confirm it on hardware you didn't have. CI is green. One thing I'd like settled before merge. The description says passes from the other rules (the budget, 90%, 97%) are unchanged, and the test says the driver's limit clears both lines. In practice that clearing almost never happens. The 90% rule and the reserve share the same `pressure`, so the reserve's excess is always at least the 90% rule's plus 10% of the allowance. Whenever the 90% rule fires under `RX_VK_COMPACT`, `reserve < budget` holds and the pass becomes a hold pass. The 97% rule only wins (`budget < evict_to`) when `reclaim` is over ~17% of the allowance. So past 90% and even past 97%, a live texture now has to be unused for 600 presents before it can go, where main let it go after `min_unused` (60). That is exactly the situation of the 8 GB reports: your own Limits note (93-96% stretches, nothing left to let go) and buasfe's log with the count over the allowance 17 times on a 3070. Paging past the allowance costs more than re-uploads. I'd keep the 600/1800 thresholds for the part of the excess that only the 85/80 lines ask for, and let textures past `need_unused` go for the part the 90%/97% rules ask for. For example, record the budget those rules set before the hold applies (`rule_to`) and give `hold_target` a third tier for it, so that rule's excess is served as before. A test case for "count over 90%: a texture unused 100 presents still goes" would pin it. Nits: - buasfe's question (Kakariko <-> Hyrule Field transitions on an 8 GB 3070) isn't answered yet. A test build for them would cover the scene change and the small real card, both listed as untested. - The `if (budget < evict_to) evict_to = hold_to = 0;` comment ("the driver's limit asks for more") reads as if this is the usual path near the limit. A word that it rarely fires once the reserve is set would help the next reader.
hckrman101 (Migrated from github.com) commented 2026-11-11 11:59:21 +00:00

This only fires when the 97% rule's excess (by used) beats the reserve's (by used - reclaim, 17 points lower), which takes reclaim > ~17% of the allowance. So over 97% the hold pass, with its 600/1800-present thresholds, usually still decides. Unless that's intended, the driver-limit case needs its own target (review body).

This only fires when the 97% rule's excess (by `used`) beats the reserve's (by `used - reclaim`, 17 points lower), which takes `reclaim` > ~17% of the allowance. So over 97% the hold pass, with its 600/1800-present thresholds, usually still decides. Unless that's intended, the driver-limit case needs its own target (review body).
hckrman101 (Migrated from github.com) commented 2026-11-11 11:59:21 +00:00

The reserve's excess is always at least the 90% rule's (same pressure, a line 10 points lower), so whenever the 90% rule set budget above, this replaces it, and the pass that follows protects live textures for 600 presents instead of min_unused. See the review body.

The reserve's excess is always at least the 90% rule's (same `pressure`, a line 10 points lower), so whenever the 90% rule set `budget` above, this replaces it, and the pass that follows protects live textures for 600 presents instead of `min_unused`. See the review body.
hckrman101 (Migrated from github.com) commented 2026-11-11 11:59:21 +00:00

Suggestion: a third tier here, where a texture unused for need_unused may go while total_bytes - going is above what the 90%/97%/size rules alone asked for. Then the hold only changes what its own lines take.

Suggestion: a third tier here, where a texture unused for `need_unused` may go while `total_bytes - going` is above what the 90%/97%/size rules alone asked for. Then the hold only changes what *its own* lines take.
hckrman101 (Migrated from github.com) reviewed 2026-11-11 16:28:55 +00:00
hckrman101 (Migrated from github.com) left a comment

Thanks for the follow-up. Since my last review the branch picked up a merge of main, the switch to on-by-default (vram_compact::enabled, with tests for unset / 1 / 0 / empty) and the new tested-configurations rows. The default flip itself is clean: enabled() matches the docs and the test, RX_VK_COMPACT=0 is a clear way back, and the on-vs-off walk numbers (633/408 frames over 25 ms down to 66/36, shadows never given back) are a good case for it.

Still open, and it matters more now:

  • The 90%/97% rules' passes still go through the hold's thresholds. The budget code (3817-3843) and hold_target are unchanged, so what I described last time still holds: whenever the 90% rule fires, the 80% reserve sets a lower budget from the same pressure and the pass becomes a hold pass, where a live texture has to be unused 600 presents (not min_unused, 60) before it can go for the hold's line, and 1,800 for the reserve's. The 97% rule only takes over when reclaim is above ~17% of the allowance. Before this push that only affected people who set RX_VK_COMPACT=1. With the default flipped it affects every 8 GB card, which is where the count really sits past 90% (your own Limits note on 93-96% stretches, buasfe's 3070 log with 17 "over the driver's allowance" lines). I'd still like the third tier (record what the 90%/97%/size rules asked for before the hold applies, and let textures past need_unused go for that part of the excess) plus a test like "count over 90%: a texture unused 100 presents still goes" before this ships on by default. If you think 600 presents is right even past 90%, say why and I'm happy to look at numbers, but I'd want them from a real 8 GB card, not the simulated allowance.

Nits, both still open:

  • buasfe's question (Kakariko <-> Hyrule Field transitions on an 8 GB 3070) still has no reply. With the default on, a test build for them is the easiest way to cover the scene change on a small real card.
  • The comment on if (budget < evict_to) evict_to = hold_to = 0; (3843) is unchanged; it still reads as the usual path near the limit, when it rarely fires once the reserve is set.

CI: everything passes except appimage, which died on a Launchpad connection reset (RemoteDisconnected) while installing packages, not on the build. A re-run should clear it. Mergeable, no conflicts.

Thanks for the follow-up. Since my last review the branch picked up a merge of main, the switch to on-by-default (`vram_compact::enabled`, with tests for unset / `1` / `0` / empty) and the new tested-configurations rows. The default flip itself is clean: `enabled()` matches the docs and the test, `RX_VK_COMPACT=0` is a clear way back, and the on-vs-off walk numbers (633/408 frames over 25 ms down to 66/36, shadows never given back) are a good case for it. Still open, and it matters more now: - **The 90%/97% rules' passes still go through the hold's thresholds.** The budget code (3817-3843) and `hold_target` are unchanged, so what I described last time still holds: whenever the 90% rule fires, the 80% reserve sets a lower budget from the same `pressure` and the pass becomes a hold pass, where a live texture has to be unused 600 presents (not `min_unused`, 60) before it can go for the hold's line, and 1,800 for the reserve's. The 97% rule only takes over when `reclaim` is above ~17% of the allowance. Before this push that only affected people who set `RX_VK_COMPACT=1`. With the default flipped it affects every 8 GB card, which is where the count really sits past 90% (your own Limits note on 93-96% stretches, buasfe's 3070 log with 17 "over the driver's allowance" lines). I'd still like the third tier (record what the 90%/97%/size rules asked for before the hold applies, and let textures past `need_unused` go for that part of the excess) plus a test like "count over 90%: a texture unused 100 presents still goes" before this ships on by default. If you think 600 presents is right even past 90%, say why and I'm happy to look at numbers, but I'd want them from a real 8 GB card, not the simulated allowance. Nits, both still open: - buasfe's question (Kakariko <-> Hyrule Field transitions on an 8 GB 3070) still has no reply. With the default on, a test build for them is the easiest way to cover the scene change on a small real card. - The comment on `if (budget < evict_to) evict_to = hold_to = 0;` (3843) is unchanged; it still reads as the usual path near the limit, when it rarely fires once the reserve is set. CI: everything passes except `appimage`, which died on a Launchpad connection reset (`RemoteDisconnected`) while installing packages, not on the build. A re-run should clear it. Mergeable, no conflicts.
@ -281,3 +281,2 @@
bool pool_pressed; // (VRAM near what the driver allows: texture_frame's budget)
bool compact_on() { // (hookshot, RX_VK_COMPACT=1: the textures in a pool of their own, compacted: compact_step)
static const bool v = getenv("RX_VK_COMPACT") && atoi(getenv("RX_VK_COMPACT")) != 0;
bool compact_on() { // (hookshot: the textures in a pool of their own, compacted: compact_step. On unless RX_VK_COMPACT=0)
hckrman101 (Migrated from github.com) commented 2026-11-11 16:28:55 +00:00

With this on by default, the 90%/97% point from my last review (the hold's 600/1,800-present thresholds deciding passes the 90%/97% rules asked for) now applies to every small card without the switch set. I'd land the third tier before the default flips, or flip the default in a follow-up once that's in.

With this on by default, the 90%/97% point from my last review (the hold's 600/1,800-present thresholds deciding passes the 90%/97% rules asked for) now applies to every small card without the switch set. I'd land the third tier before the default flips, or flip the default in a follow-up once that's in.
hckrman101 commented 2026-11-11 16:45:45 +00:00 (Migrated from github.com)

Heads-up: this conflicts with main now. Could you rebase onto main and resolve the conflicts? The point in my last review (the 90%/97% rules' passes still using the hold's 600/1800-present thresholds, now on by default for every 8 GB card) is still open as well. Thanks!

Heads-up: this conflicts with main now. Could you rebase onto main and resolve the conflicts? The point in my last review (the 90%/97% rules' passes still using the hold's 600/1800-present thresholds, now on by default for every 8 GB card) is still open as well. Thanks!
SadTransGirl commented 2026-11-11 18:52:41 +00:00 (Migrated from github.com)

Thanks for the review. You were right, and it mattered more once the default flipped. Pushed:

  • Main merged in (2ed98c46), with the conflict in docs/tested-configurations.md resolved. It's a merge and not a rebase because I can't force-push from here.
  • The third tier (2a407e10). The budget that the size, the 90% rule and the 97% rule ask for on their own is now kept apart from the hold's (rule_budget). hold_target lets a texture unused for need_unused go until the textures are down to 7/8 of that budget, which is what a pass over the budget cuts to with the compaction off. The 600 and 1,800 presents now only decide what the 85% and 80% lines add on top.
  • Test: tests/test_vram_compact.py has "the count over 90%: a texture unused 100 presents still goes", a worked case at 94% of a 7,000 MB allowance, and source checks that rule_budget is taken before the hold's lines and joined by the 97% rule's.
  • The comment on if (budget < evict_to) evict_to = hold_to = 0; now says it rarely fires once the reserve is set, and why.

Not run in the game: this push is the rules' arithmetic and the source checks only, and I still have no real 8 GB card. The AppImage failure was a Launchpad connection reset; this push should re-run it.

@buasfe sorry for the slow answer. Your log (the count over the allowance 17 times on the 3070) is the case this push is for: past 90% the cache can again let go of textures unused for a second. I can't attach a build myself, but if you're able to build from this branch, a few Kakariko ↔ Hyrule Field transitions on your card would cover the two things nobody has tested here, a scene change and a real 8 GB card.

🤖 Generated with Claude Code

Thanks for the review. You were right, and it mattered more once the default flipped. Pushed: - **Main merged in** (2ed98c46), with the conflict in `docs/tested-configurations.md` resolved. It's a merge and not a rebase because I can't force-push from here. - **The third tier** (2a407e10). The budget that the size, the 90% rule and the 97% rule ask for on their own is now kept apart from the hold's (`rule_budget`). `hold_target` lets a texture unused for `need_unused` go until the textures are down to 7/8 of that budget, which is what a pass over the budget cuts to with the compaction off. The 600 and 1,800 presents now only decide what the 85% and 80% lines add on top. - **Test:** `tests/test_vram_compact.py` has "the count over 90%: a texture unused 100 presents still goes", a worked case at 94% of a 7,000 MB allowance, and source checks that `rule_budget` is taken before the hold's lines and joined by the 97% rule's. - **The comment** on `if (budget < evict_to) evict_to = hold_to = 0;` now says it rarely fires once the reserve is set, and why. Not run in the game: this push is the rules' arithmetic and the source checks only, and I still have no real 8 GB card. The AppImage failure was a Launchpad connection reset; this push should re-run it. @buasfe sorry for the slow answer. Your log (the count over the allowance 17 times on the 3070) is the case this push is for: past 90% the cache can again let go of textures unused for a second. I can't attach a build myself, but if you're able to build from this branch, a few Kakariko ↔ Hyrule Field transitions on your card would cover the two things nobody has tested here, a scene change and a real 8 GB card. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
hckrman101 (Migrated from github.com) reviewed 2026-11-11 18:59:06 +00:00
hckrman101 (Migrated from github.com) commented 2026-11-11 18:59:06 +00:00

Done in 2a407e10: rule_target / the need_unused tier in hold_target, with the worked 94% case in the test. Resolved from my side.

Done in 2a407e10: `rule_target` / the `need_unused` tier in `hold_target`, with the worked 94% case in the test. Resolved from my side.
hckrman101 (Migrated from github.com) reviewed 2026-11-11 18:59:08 +00:00
@ -281,3 +281,2 @@
bool pool_pressed; // (VRAM near what the driver allows: texture_frame's budget)
bool compact_on() { // (hookshot, RX_VK_COMPACT=1: the textures in a pool of their own, compacted: compact_step)
static const bool v = getenv("RX_VK_COMPACT") && atoi(getenv("RX_VK_COMPACT")) != 0;
bool compact_on() { // (hookshot: the textures in a pool of their own, compacted: compact_step. On unless RX_VK_COMPACT=0)
hckrman101 (Migrated from github.com) commented 2026-11-11 18:59:08 +00:00

With the third tier in, the 90%/97% passes again let textures unused 60 presents go for their part, so the default flip is fine by me.

With the third tier in, the 90%/97% passes again let textures unused 60 presents go for their part, so the default flip is fine by me.
hckrman101 (Migrated from github.com) approved these changes 2026-11-11 18:59:16 +00:00
hckrman101 (Migrated from github.com) left a comment

Thanks, this is what I was after. Since my last review: I merged main into the branch (40d8e17) to clear the conflict; the only conflict was the docs/development.md table, where I kept your RX_VK_COMPACT row and main's new RX_VK_REACTIVE row. Nothing else in the merge differs from what main brought in.

Earlier points:

  • 90%/97% passes going through the hold's thresholds: fixed (2a407e10). rule_budget is taken after the size and 90% rules and before the hold's lines, the 97% rule joins it, and hold_target now gives a texture unused for need_unused the rules' part (rule_target, 7/8 of their budget, the same cut a pass makes with the compaction off), then the hold's line at 600 and the reserve's at 1,800, taking the lowest that applies. The dead render targets still count first through hold_met, and the fence and keep_rendered / finalized checks still come after, so nothing in use by the GPU is newly exposed. The 94%-of-7,000 MB case in the test works the numbers through, including "unused 100 presents still goes" and stopping once the rules' part is covered. With this, the description's "passes the other rules ask for are as they were" is true again.
  • The comment on if (budget < evict_to) evict_to = hold_to = 0;: fixed, it now says it's rare once the reserve is set and why.
  • buasfe's Kakariko <-> Hyrule Field question: answered. A run of this head on their 3070 would still be the best check, since the third tier is source checks and arithmetic only so far and no real 8 GB card has run it (Tempus3214's RX 6600 run was on the earlier version).

Overlaps: #1879 landed on main right after the merge; a trial 3-way merge of vk_texture.cpp is clean (it only adds overwritten to struct Evict / evict.push_back and the retire tally), and GitHub shows no conflict. #2368 inserts its keep check just above the hold check in the same loop, so whichever lands second resolves that spot and should make the hold's pass count the kept bytes, as agreed there.

CI: all green except Python source checks (windows-2022, part 1/2), which fails tests/test_native_astc_texture_count.py (its fixture's scanf under -Werror on Windows). Main fails the same test since #2288, so it isn't this PR.

Approving.

Thanks, this is what I was after. Since my last review: I merged main into the branch (40d8e17) to clear the conflict; the only conflict was the `docs/development.md` table, where I kept your `RX_VK_COMPACT` row and main's new `RX_VK_REACTIVE` row. Nothing else in the merge differs from what main brought in. Earlier points: - **90%/97% passes going through the hold's thresholds: fixed** (2a407e10). `rule_budget` is taken after the size and 90% rules and before the hold's lines, the 97% rule joins it, and `hold_target` now gives a texture unused for `need_unused` the rules' part (`rule_target`, 7/8 of their budget, the same cut a pass makes with the compaction off), then the hold's line at 600 and the reserve's at 1,800, taking the lowest that applies. The dead render targets still count first through `hold_met`, and the fence and `keep_rendered` / finalized checks still come after, so nothing in use by the GPU is newly exposed. The 94%-of-7,000 MB case in the test works the numbers through, including "unused 100 presents still goes" and stopping once the rules' part is covered. With this, the description's "passes the other rules ask for are as they were" is true again. - **The comment on `if (budget < evict_to) evict_to = hold_to = 0;`: fixed**, it now says it's rare once the reserve is set and why. - **buasfe's Kakariko <-> Hyrule Field question: answered.** A run of this head on their 3070 would still be the best check, since the third tier is source checks and arithmetic only so far and no real 8 GB card has run it (Tempus3214's RX 6600 run was on the earlier version). Overlaps: #1879 landed on main right after the merge; a trial 3-way merge of `vk_texture.cpp` is clean (it only adds `overwritten` to `struct Evict` / `evict.push_back` and the retire tally), and GitHub shows no conflict. #2368 inserts its keep check just above the hold check in the same loop, so whichever lands second resolves that spot and should make the hold's pass count the kept bytes, as agreed there. CI: all green except `Python source checks (windows-2022, part 1/2)`, which fails `tests/test_native_astc_texture_count.py` (its fixture's `scanf` under `-Werror` on Windows). Main fails the same test since #2288, so it isn't this PR. Approving.
Commenting is not possible because the repository is archived.
No description provided.