Skip to content

Sampled image heap of 16 rejects a retail shader before the metadata limit #1789

Closed

Description

@darklinkworker

A Unreal title (PPSA20612) aborts during the first shader compile on current main (55e8959b) with:

ShaderRecompiler::Recompile: shader image heap capacity exceeded

That throw is BindingAllocator, when one image binding group is larger than RuntimeAbi::HeapCapacity. On this commit the sampled heap is 16 and the storage heap is 4 (core/shader/recompiler/RuntimeAbi.hpp). The earlier metadata check (ImageCapacity 256, SamplerHeapCapacity 16) had not fired, so the shader fit in the metadata arrays and still overflowed one descriptor group.

SampledHeapCapacity is also ResourceMaterializer::BindlessSlots(), and StorageHeapCapacity is the mip reservation for one dynamic storage image. Raising either constant to a device's raw maxPerStageDescriptorSampledImages (8,388,606 on an RX 6650 XT / RADV NAVI23) would size every bindless table and every dynamic storage image to that count. The per-stage limit is a shared budget, not a safe per-heap size.

Locally, sampled images and ImageCapacity were raised to 65,536, samplers to 1,024, and the storage mip chain to 16 (enough for a 16,384 texture). The libraries were rebuilt and the same title got past shader image heap capacity exceeded. The next abort was a non-constant texel offset, which already requires VK_KHR_maintenance8 and is covered by the closed test issue #827. This driver does not expose that extension.

Nothing in docs/ or the open issues records the 16-wide sampled heap as a title-facing limit.

Activity

  1. Priyasmit-work commented on Oct 8, 2026

    @Priyasmit-work

    @darklinkworker I’d like to take this one. I’ll investigate the bindless sampled/storage heap capacity handling and work toward a fix with appropriate limits and tests.

  2. darklinkworker commented on Oct 8, 2026

    @darklinkworker
    Author

    sounds good to me I just based it on the limits of my current GPU to see if it would get passed that point and it did. It doesn't to seem like AMD GPU's work yet since I saw another issue having driver issues like my did and my agent said we would have to put a patch in the mesa driver for linux which I am not doing. there has to be a better way around it. anyways I am off topic of this issue

  3. EdouardSence commented on Oct 8, 2026

    @EdouardSence
    Contributor

    Another title and an AMD data point: Astro Bot (PPSA21567 v01.019.000, retail) hits this on main 4b6ab6b on an RX 9070 XT (Mesa 26.2.4 RADV). To get that far I needed #1836 and #1837 applied; on plain main it stops earlier (#1817).

    I added a local print before the throw in BindingAllocator (not part of any PR). These groups overflow:

    image group (FirstImageBinding + i) images in group capacity images in the shader
    2 (sampled) 40 16 44
    30 (storage) 5 4 5
    30 (storage) 5 4 6
    30 (storage) 6 4 8

    So a single sampled group reaches 40 here, more than double the current 16, and storage groups go to 6.

    RADV on this device reports maxPerStageDescriptorSampledImages, maxPerStageDescriptorStorageImages and the update-after-bind variants all at 8388606. On this hardware, the device limit wouldn't be what caps these heaps.

    Happy to run a candidate fix on this title and GPU.

    Tested and written by Claude (AI assistant) on a contributor's machine.

  4. CouchRiv commented on Oct 9, 2026

    @CouchRiv
    Contributor

    @Priyasmit-work are you still on this one? I have a candidate on a branch and don't want to step on your claim: it splits RuntimeAbi's heap constant into the sampled group size, the storage group size, the storage mips (4) and the bindless slots (16), so groups go up to the 256 images the metadata allows while layouts stay sized by the shader's count. A new agc_driver_typed_heaps case with 40 sampled images (the Astro Bot group size above) fails on main f145139 with this exact message on an RTX 4070 Ti and a Radeon 610M (RADV), and passes with the change; full ctest 497/497 on both.

    If you have a PR close, I'll leave it to you and can compare notes there. Otherwise I'll open mine tomorrow.

  5. NahuelGenchi commented on Oct 9, 2026

    @NahuelGenchi
    Contributor

    Another data point: Sonic Origins (PPSA05324 v02.002.000) on main f145139 + #2004, R9 380 (RADV), hits this 7 s in, at shader registration. With the group, size and capacity added to the message locally:

    shader image heap capacity exceeded: group 31 holds 6 of capacity 4, images in shader 13
    

    With the storage group capacity raised to 16 locally (the dynamic mip reservation kept at 4), the next registration stops at shader sampler pairs exceed runtime heap capacity. A sampler heap of 32 gets past it. That needs the ShaderData static assertions updated: the struct grows to 14528 bytes and ExportMappingsDword moves to 3624. Happy to test a candidate fix on this title and card.

    Bring-up thread: #2021

  6. CouchRiv commented on Oct 9, 2026

    @CouchRiv
    Contributor

    @NahuelGenchi thanks, that log helps. My candidate is on CouchRiv:fix-shader-sampled-heap-capacity (bd5f22c, on main f145139). A storage group now holds up to 1024 elements instead of 4, with a dynamic storage image still reserving 4 mips. So group 31 holds 6 of capacity 4 should go through. It doesn't touch the sampler pair limit, so I'd expect Sonic Origins to stop next at shader sampler pairs exceed runtime heap capacity, as in your local run. That would be a separate PR.

    If you can run it on Sonic Origins, the first line it stops on would tell us whether the image heap part is enough. Still no PR from me while @Priyasmit-work's claim stands.

  7. EdouardSence commented on Oct 9, 2026

    @EdouardSence
    Contributor

    @CouchRiv Astro Bot (PPSA21567) data for your branch bd5f22c, on RX 9070 XT (RADV / Mesa 26.2.4, Linux).

    Tested and written by Claude (AI assistant) on a contributor's machine.

  8. Priyasmit-work commented on Oct 9, 2026

    @Priyasmit-work

    @CouchRiv Thanks for investigating this. I'm still interested in contributing to #1789, but I haven't opened a PR yet. Your candidate looks promising, especially with the Astro Bot reproduction. I'll review the changes and investigate the remaining capacity assumptions, including the sampler-pair limit seen with Sonic Origins. If you're planning to open a PR, I'd be happy to help validate the implementation and look for additional regressions rather than duplicate your work

  9. MarcZX10 commented on Oct 9, 2026

    @MarcZX10
    Contributor

    I reviewed CouchRiv's candidate bd5f22c9 at source level against its f1451396 base. The split among the sampled group ceiling (256 metadata entries), storage group ceiling (256 × 4 mips), and 16 bindless table slots matches the reported failure and keeps the raw Vulkan per-stage limits out of the static ABI. The existing graphics code still checks actual sampled/storage descriptor counts against both per-stage and descriptor-set limits. git diff --check passes, and a merge-tree check onto current main 6cc0503d is conflict-free.

    One test detail to consider when preparing the PR: the new typed-heap execution case exercises 40 sampled images, while the reports here also include storage groups of 5–6. If the Astro Bot run on this candidate exercises those storage groups, calling that out in the Tested section would make the evidence clear; otherwise, a small storage-heap execution case would cover the second half of the reported limit. The current shader-device-profile case proves planning capacity, not Vulkan execution of a storage group larger than four.

  10. mrk1987 commented on Oct 9, 2026

    @mrk1987
    Contributor

    Windows + NVIDIA data point for @CouchRiv's bd5f22c9: a retail Unreal Engine title on an RTX 5090 (Windows 11, MinGW-w64 build), main d70b8998 + #1836 + bd5f22c9 merged cleanly.

    • Without the branch, 58 shaders per run stop on shader image heap capacity exceeded (plus one shader sampler pairs exceed runtime heap capacity). With it, 0 heap-capacity failures in four runs. The sampler-pair message did not appear either.
    • There's a side effect worth noting for anyone else seeing Unreal's heartbeat hang (FThreadHeartBeat deliberately writing 0xE0000001 to address 3). Because those shaders never prepare, they are never stored in the shader disk cache. Every run had 57 permanent misses, recompiled on each attempt under ShaderPreparationTransaction's process-wide State::writers mutex. A CDB dump at the hang showed 11 UE worker threads waiting in the ShaderPreparationTransaction constructor while one thread was in SPIR-V emission. That stalled the game thread past UE's hang timeout every run.
    • With the branch and a warm cache, misses go to 0 (902 hits, 0 misses) and the heartbeat hang is gone in both runs. The title now stops later on an unrelated issue.

    Tested and written by Claude (Claude Code) on a contributor's machine.

  11. darklinkworker commented on Oct 9, 2026

    @darklinkworker
    Author

    Retried PPSA20612 on another machine. After the local heap change, the previous machine stopped here:

    SPIR-V emission failed: hash=0x139752974974976 stage=4 opcode=ImageSampleRaw
    reason=has a texel offset that is not a constant, which image sampling takes only with VK_KHR_maintenance8 and shaderImageGatherExtended
    

    That box was Fedora 38, an RX 6650 XT, and Mesa 23.1.9. Its kernel and Mesa were too old: that Mesa does not advertise VK_KHR_maintenance8.

    This machine does (VK_KHR_maintenance8 revision 1, and shaderImageGatherExtended is true). The same title, with the same local RuntimeAbi heap sizes, selected the integrated GPU and compiled 1,275 shaders. The ImageSampleRaw non-constant texel offset abort did not recur.

    Host:

    • Nobara Linux 44, kernel 7.1.3-200.nobara.fc44.x86_64, glibc 2.43
    • AMD Ryzen 5 7640U
    • AMD Radeon 760M (Phoenix, PCI 1002:15bf), RADV, Mesa 26.2.3, Vulkan 1.4.354
    • Integrated GPU only

    This seems like a doc issue to state that you should be on a newer Linux kernel or at least have mesa 25.2 drivers if you want the game to run.

    The run still aborts later, on packed pixel ancillary input has an unsupported live use. That is separate from this heap issue. (still in process to see if this is a new issue to report or my own fault at the moment)

  12. oneandonlydean commented on Oct 9, 2026

    @oneandonlydean
    Collaborator

    Open PRs for these limits: #2347 (sampled heap 16 -> 32), #2282 (storage heap 4 -> 16) and #2141 (sampler pairs 16 -> 32). The sampled group of 40 reported above is still over #2347's 32.

  13. SleglJan commented on Oct 10, 2026

    @SleglJan
    Contributor

    #2282 (85268f8) on a second title, on Linux with NVIDIA: 007 First Light (PPSA11386 v01.018.000), CachyOS (kernel 7.2.9), GCC 16.2.1, RTX 3080 (NVIDIA 615.71.09), Release build of main 8b393b6 plus the changes that get the title this far (SleglJan#15 to #20 and #1964; none of them touches BindingAllocator or RuntimeAbi).

    AI-assisted: yes (Claude Code)

  14. oneandonlydean commented on Oct 10, 2026

    @oneandonlydean
    Collaborator

    Closing: #2347, #2282 and #2141 are merged, so the sampled heap holds 64 images, the storage heap 16 and the sampler heap 32 (core/shader/recompiler/RuntimeAbi.hpp:37-41). Please reopen if a title still stops on a heap capacity check on current main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions