fix(vk): count natively sampled ASTC textures by their blocks in the texture budget #2288
No reviewers
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!2288
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/vk-native-astc-texture-count"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Intel GPUs sample ASTC textures directly, and the texture budget counts each of those textures 16 to 144 times too big. On an Intel UHD 770 the count in Kokiri Forest reached 17.6 GB against the integrated GPU's 2.5 GB budget, while the whole process held 4.8 GB of GPU memory, so the cache kept evicting textures it had room for.
Fixes #2333.
What changed
gpu/vk/vk_texture.cppcreate: the budget counts an image as its level chain's units times the bytes of one unit. An ASTC image the GPU samples natively keeps its blocks (vk: ASTC sampled natively:astcis false,is_astc(x.format)). For those the unit was the texel, but its size was the block's 16 bytes (bpb). A 4x4-block texture counted 16 times its size, a 12x12 one 144 times.texture_level_unitsnow counts such a level in blocks (ceil(w / bw) * ceil(h / bh)), and every other image in texels, as before. The floor at the allocation's size (x.pad) is unchanged.tests/test_native_astc_texture_count.py(new): extractstexture_level_unitsand runs 182 cases: every ASTC block size, part blocks, levels down to 1x1, and 1x1 units for other images, which must equal the old count. It also checks thatcreatecounts through it for native ASTC only. Without the change it fails, because the helper isn't there.GPUs that decode ASTC on upload (NVIDIA and AMD:
vk: ASTC not sampled by this GPU: blocks are decoded on upload) take the 1x1 path, so their counts don't change.How it was tested
Intel UHD 770 (Raptor Lake-S GT1, no mesh shaders), Mesa 26.2.5 ANV on i915, Arch Linux, i9-13900K, 93 GB RAM. The game ran headless, capped at 32 GB. Same save in Kokiri Forest, the same scripted input (load the save, then camera turns), the same shader caches, 8 minutes each. The fix was tested as a cherry-pick (defddca0) onto main
872cb68, where those caches were made. Rebased onto49f75dc8, the commit is the same apart from line numbers. Numbers come fromhookshot_log.txt(VRAM: ... textures N MB (budget B MB), T textures):VRAM: ... used)872cb68On main the count jumps 3410 MB for one 1365x1365 array of 90 layers, ASTC 8x8, whose blocks are 54 MB. With the change the count stays under the budget, the cache keeps about 20% more textures, and the process's allocations stay where they were. The log still says
vk: ASTC sampled natively. No errors, no device lost, nothing from i915 in the kernel log. The picture after loading is the same as main's, which is wrong on this GPU for an unrelated reason (the upscaler's input; a separate PR). 9.4 fps against 8.4, GPU-bound at about 100 ms a frame and at different load, so I make no speed claim.Checks:
python -I -B tests/run_all.pyon the branch: 362 passed, 3 failed, 10 skipped. The failures aretest_updater_asset_digest.py(needs the .NET 8 SDK; only .NET 10 here),test_wayland_libdecor_hint.py(clang 23's-Wunused-but-set-globalunder-Werror), which both fail the same way on main here, andnx2/test_texture_phi_dominance.py, which timed out under load and passes alone.test_native_astc_texture_count.pypasses with g++ and clang++.RTX 4090 (NVIDIA 615.78.08): its log says
vk: ASTC not sampled by this GPU: blocks are decoded on upload, so its textures take the 1x1 path, whose count the test checks is the old one. A run of the branch on it is queued behind other work and not finished; I'll add it here.Not tested
Windows (Intel's own driver), Intel Arc, and other Intel iGPUs. Mesa's other drivers that sample ASTC natively (none on this machine). AMD. Other areas of the game. How much the extra textures held change frame pacing on Intel: the UHD 770 is GPU-bound at about 100 ms a frame in this scene, so I made no speed claim.
Refs #1001 and #999 (the Intel trackers). The over-count is the same on any GPU that reports ASTC as sampled natively; I could only run it on the UHD 770.
Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 [email protected]
Thanks, this is a clean fix and the numbers in the description make the case well.
I checked the math: for a natively sampled ASTC image the level chain is now
ceil(w_l / bw) * ceil(h_l / bh) * dunits of 16 bytes, which is what Vulkan says a level of a block-compressed image takes (mip extent first, then rounded up to whole blocks), times layers. That holds for all 14 block sizes; the test covers each of them, part blocks and 1x1 levels.bw/bhcome fromnvn_block_sizeintexture_get, so they are the real block dims for these formats, and every other image (including decoded ASTC, which keepsastc ? 4per texel) goes through the 1x1 path and counts exactly as before. On NVIDIA/AMD nothing changes.Budget/eviction:
x.padstill raises the count toallocation_size, so for these imagesbytes + padis now about the allocation instead of 16 to 144 times it, andtotal_bytesgoes back to meaning what the budget assumes. Removal (tex.bytes + tex.pad), the eviction sweep'spending_bytesand compaction'smoved_bytesall use the stored value, so they stay consistent. Thegpu_writtensize thresholds in the sweep don't apply to sampled ASTC. This doesn't touch the lines #2198 or #2368 change, and it should help both: the compaction hold'sheld_budget(budget, total_bytes, ...)in #2198 would otherwise see the inflated total on Intel.Nits, none blocking:
bytes_atincreate,texel = astc ? 4 : std::max(1, bpb / std::max(1, bw * bh))) still counts native ASTC per texel at 1 byte, which is exact for 4x4 but 4x too big for 8x8 and 9x for 12x12. It only matters pastRX_VK_TEX_MAX_MB(1536 MB) or for the sparse decision, so it's fine to leave, buttexture_level_unitswould fit there too if you want the two to agree.CI: only the PR title check has run; the build and game-free regression workflows are waiting for a maintainer to approve the run. The new test declares
RUN_ALL_ARGS, so run_all picks it up. Approving on the code; it should go in once those workflows are green. Looking forward to the RTX 4090 run you mentioned.Not for this PR: BC gets the same kind of over-count at a smaller scale.
texelis 1 byte for every BC format, but BC1/BC4 are 8 bytes per 4x4 block (0.5 B/texel), so they count 2x until thex.padfloor (which only raises, never lowers). Counting BC by blocks throughtexture_level_unitswithbpbwould fix that the same way, if you feel like a follow-up.@ -0,0 +87,4 @@assert int(o) == max(1, w) * max(1, h) * max(1, d) # (unchanged for everything but native ASTC)# The array the UHD 770's log named: 1365 x 1365, 90 layers, 11 levels, ASTC 8 x 8. Counted as texels at 16 bytes,# 3410 MB; its blocks are 56 MBNit: 56365920 bytes is 56 MB decimal but 53.75 MiB, and the print below shifts by 20, so the output says 53 while this comment says 56 (and the PR body says 54). 3410 is MiB too. Maybe say MiB here, or 54 MB, so they agree.
@hckrman101 a heads-up, mine again: this pull request's test does not compile on Windows, so
Python source checks (windows-2022, part 1/2)fails on main since the merge. The fixture reads its cases withstd::scanfunder-Werror, and the Windows CRT marksscanfdeprecated. Its workflows had not run before the merge, and I had only run it on Linux. The fix is one line, in #2397 (issue #2396); its own windows-2022 job will show whether it is enough, and I'll confirm there.The owed RTX 4090 check has finished. Both logs say
ASTC not sampled by this GPU: blocks are decoded on upload, so this is coverage of the decoded-ASTC path, not the native-ASTC path this PR fixes.Arch Linux, NVIDIA 615.78.08, headless, same save and scripted camera input, 170 s limits,
RX_VK_PRECOMPILE=0in both. The reference is the olderbdfe09cbuild; the candidate is91ede0a4on49f75dc8. These are not two builds of today's main.Numbers below come from each run's highest texture-count log line. Units are MiB (the log prints “MB”, but shifts byte counts by 20):
bdfe09c91ede0a4Both runs reached their time limits without a fatal/crash/device-loss report or kernel driver-fault line. The live texture populations and system loads differ, so these peaks are not an equal-workload memory or frame-rate comparison; I am not attributing the difference to this patch.
This closes the owed NVIDIA-path run for the merged ASTC fix. It does not test the later proposed BC block-counting/pre-creation-estimate followup, Windows, or AMD.
Thanks. #2624 counts native BC textures by their blocks, uses
texture_level_unitsin the estimate before creation for native BC and ASTC, counts re-encoded BC3 by its own 4x4 output blocks, and makes the test's comment and output agree in MiB. GCC and Clang each pass 4,845 accounting cases plus the original 182 helper cases, and main fails 3,040 of them; putting back only the old estimate or only the old final count fails as well.github-sync referenced this pull request2026-11-12 06:22:54 +00:00