Repository navigation
fix(vk): RADV on GCN 1-4 skips the two mesh fallback programs its compiler never finishes - #2525
Merged
Merged
Conversation
…piler never finishes On an RX 580 (Polaris, RADV), the shader preparation's precompile ran out of memory past 24 GB, on main too. Two mesh programs' fallback compute stages (93e3e999f2fb9fd8 and ea4dd64df9c8f2b7, 4.3 MB of SPIR-V each: ~28,000 blocks, ~80,000 phis, ~400 values live at once) never finish in ACO's spiller on GFX6-8, which grows without bound and keeps the memory on the driver's thread. GFX9 and later build the same modules in ~14 s and 2.9 GB, and the next biggest mesh programs (up to 29,400 phis) build on Polaris in 4-6 s and under 700 MB. Each stage's OpPhi count is now noted where its SPIR-V passes through the program cache (vk_progcache.cpp, outside the translator stamp), and fallback_compute_pipelines leaves a program out, as a refused one, when the device is RADV on GCN 1-4 (no storageInputOutput16) and a compute stage has more than 50,000 phis. RX_VK_FALLBACK_PHI_LIMIT changes the limit (0: none). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y whether the GPU is RADV on GCN 1-4
A value that is not a number from 0 to 4294967295 ("off", "-1", "12k") was read by
strtoul as no limit or a wrapped one. It is now ignored with a warning, and the
limit in use is logged once when the fallback first builds on such a card (or
whenever the variable is set). The subgroups line says "RADV on GCN 1-4: yes/no".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
Tested on the real card: RX 580 (Polaris 10), Mesa 26.2.6 RADV, i7-9700K, 15 GB. This PR (42d84fc) built natively, with a cold The prepare finishes. On main it was killed in the precompile every time, past 24 GB.
Peak memory: 10.6 GB (8.4 GB RAM + 2.1 GB swap, at the start of the precompile). The log has A warm second start and a play run in Kokiri and Hyrule Field follow; I'll add them here. |
hckrman101
marked this pull request as ready for review
November 11, 2026 23:05
Collaborator
Author
|
The rest of the RX 580 check:
So whatever the two skipped programs draw, it isn't in those two areas. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
On an RX 580 (Polaris 10, Mesa 26.2.6 RADV), a cold
--prepare-shadersgot through the translation and the prediction, then ran out of memory in the precompile. It grew past 24 GB and was killed, on main too. The game's own first start does the same precompile, so a player with an RX 400/500-class card can't get through the first start: it pushes the desktop into swap or the OOM killer.The cause is two mesh programs' fallback compute pipelines,
93e3e999f2fb9fd8(stage 1) andea4dd64df9c8f2b7(stage 0). RADV never finishes compiling them on GCN 1-4 (GFX6-8), and its thread keeps the memory it grew to. A time limit wouldn't help for that reason.What makes them different from the other mesh programs:
I compiled the dumped modules with RADV itself, through Mesa's amdgpu drm-shim (no AMD GPU needed), under a 6 GB cap:
ea4dd64df9c8f2b7mesh (79,947 phis)ecbbff63280b68e2mesh (29,372 phis)cf987f56b971bdb3mesh (28,506 phis)RX_VK_NOOPT(DISABLE_OPTIMIZATION) doesn't change it. The spiller runs either way. GFX6-8 have no scratch instructions. ACO spills there through buffer instructions, and these programs' scratch is past their offset range, so ACO takes its overflow path for every spill and reload (aco_spill.cpp). With this many spills it doesn't finish. That's my reading of the source, not profiled.I couldn't find a translation-side change that compiles these two normally. Their register pressure and block count come from the native program and the shared goto-elimination structurizer. Changing either would retranslate every shader for every GPU. So this PR leaves just these two out on the cards whose compiler can't build them.
What changes
gpu/vk/fallback_size_policy.h(new):spirv_phis()counts a module's OpPhi instructions.too_big()decides whether a mesh fallback compute stage should be skipped: the device is RADV on GCN 1-4, and the stage has more than 50,000 phis. 50,000 sits between the 29,400 that build in seconds and the 80,000 that never finish.vk_progcache.cpp: each stage's phi count is recorded where its SPIR-V passes through the program cache. That is on save, which every translated program goes through, and on read. It's done there and not invk_shader.cppbecausevk_shader.cppis part of the translator stamp, and touching it would retranslate every player's programs. The record format is unchanged.vk_main.cpp/vk_internal.h:dev.radv_gcnis set when the driver is RADV andstorageInputOutput16is false. RADV sets that feature from packed 16-bit math, which means GFX9 and later. Checked through the drm-shim: false on Polaris and Pitcairn, true on Vega 10 and Navi 10.vk_draw.cppfallback_compute_pipelines: before anything is built, a program with a stage over the limit goes into the same "failed" set as a pipeline the driver refuses. It logs one line, and its draws are left out (as fix(vk): mesh fallback grass is left out while its shaders build, not drawn as holes #2382 leaves out draws whose fallback isn't ready). The precompile and the in-play builds both go through this function.RX_VK_FALLBACK_PHI_LIMIT=<n>sets another limit, and0turns it off. It's documented indocs/development.md.tests/test_fallback_size_policy.pychecks the counter on well-formed, cut-short and non-SPIR-V input, checks the limit, and checks that the source still calls the policy before any fallback pipeline is built.program_cache_cleanup_test.cppnow includes the header.What's lost: on RADV with an RX 400/500, R9 2xx/3xx or Fury card, the draws of these two mesh programs. Neither was drawn in the lab's Kokiri Forest or Hyrule Field runs, so I don't know yet which objects they are. Nothing changes on Vega and later, on AMD's Windows driver, on NVIDIA, or on Intel. The SPIR-V of every program is unchanged.
Verified
RX_VK_DUMP_MESH_SPV). Both modules passspirv-val --target-env vulkan1.3 --uniform-buffer-standard-layout --scalar-block-layout. This PR doesn't change any SPIR-V.python -I -B tests/run_all.py fallback_size_policy program_cache_cleanup mesh_fallback mesh_fallback_failed_once program_cache_reader driver_faults run_all_declarations(8 passed), plus the program cache, prediction and recorder tests (all pass).hookshot_vk.sobuilds on the current main, ABI check ok.Not tested
RX_VK_FALLBACK_PHI_LIMIT=0or a version check is added.