From ea22c8286df8e61b6d72c6c723c7acacb59db1c0 Mon Sep 17 00:00:00 2001 From: Alex Collini Date: Fri, 9 Oct 2026 17:58:17 +0200 Subject: [PATCH 1/2] fix(recompiler): hold 32 images in a sampled image heap A compute program of PPSA26344 binds 29 sampled images of one class, more than the 16 a typed sampled heap held, and failed with "shader image heap capacity exceeded". Heap arrays are declared with their compact length, so shaders that fitted before keep their layout and SPIR-V. Bindless image tables keep 16 slots: they were sized by the same constant, and each slot costs an image entry. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceAgcDriver/tests/ShaderDeviceProfile.cpp | 3 +++ .../recompiler/Optimization/src/ResourceMaterializer.cpp | 2 +- core/shader/recompiler/RuntimeAbi.hpp | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/ShaderDeviceProfile.cpp b/core/libs/prx/libSceAgcDriver/tests/ShaderDeviceProfile.cpp index 7f3366c56..5155b5759 100644 --- a/core/libs/prx/libSceAgcDriver/tests/ShaderDeviceProfile.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/ShaderDeviceProfile.cpp @@ -1,6 +1,7 @@ #include "prx/libSceAgcDriver/Execution/include/ShaderDeviceProfile.hpp" #include "BdaAbi.hpp" #include "Optimization/BindingAllocator.hpp" +#include "Optimization/ResourceMaterializer.hpp" #include #include #include @@ -141,6 +142,8 @@ void CheckHeaps() { Require(single.layout.ShaderDataDwords() == full.layout.ShaderDataDwords() && single.layout.memoryOffsetDword == full.layout.memoryOffsetDword && !full.layout.UsesPushData(), "runtime layout depends on resource count"); Require(full.layout.memoryOffsetDword == 0u && full.layout.DispatchThreadLimitDword() == 0u && full.layout.ShaderDataDwords() == 0u, "direct image resources allocated runtime metadata"); Reject([&] { allocate(image, RuntimeAbi::SampledHeapCapacity + 1u); }, "heap capacity exceeded"); + Require(BindingAllocator{}.FindBinding(allocate(image, 32u).layout, DescriptorBindingForImage(image)).resources.size() == 32u, "a sampled heap does not hold 32 images of one class"); + Require(ResourceMaterializer::BindlessSlots() == 16u, "bindless image tables changed size with the sampled heap"); Reject([&] { allocate(image, 1u, RuntimeAbi::SamplerHeapCapacity + 1u); }, "metadata capacity"); image.resourceClass = ImageResourceClass::Storage; image.mipMode = ImageMipMode::DynamicStorage; diff --git a/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp b/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp index b37f306e9..2fcebe8b8 100644 --- a/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp +++ b/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp @@ -978,7 +978,7 @@ std::uint64_t ResourceMaterializer::SpecializationNanoseconds() { } std::uint32_t ResourceMaterializer::BindlessSlots() { - return RuntimeAbi::SampledHeapCapacity; + return RuntimeAbi::BindlessTableSlots; } void ResourceMaterializer::CountBindlessRejection(BindlessRejection reason) { diff --git a/core/shader/recompiler/RuntimeAbi.hpp b/core/shader/recompiler/RuntimeAbi.hpp index 43f7bb37c..8993cb19c 100644 --- a/core/shader/recompiler/RuntimeAbi.hpp +++ b/core/shader/recompiler/RuntimeAbi.hpp @@ -34,7 +34,8 @@ enum class Stage : std::uint32_t { Main, Fragment, TessellationControl, Tessella inline constexpr std::uint32_t UserDataCapacity = 128u; inline constexpr std::uint32_t BufferCapacity = 128u; inline constexpr std::uint32_t ImageCapacity = 256u; -inline constexpr std::uint32_t SampledHeapCapacity = 16u; +inline constexpr std::uint32_t SampledHeapCapacity = 32u; +inline constexpr std::uint32_t BindlessTableSlots = 16u; inline constexpr std::uint32_t StorageMipSlots = 4u; inline constexpr std::uint32_t StorageHeapCapacity = 16u; inline constexpr std::uint32_t SamplerHeapCapacity = 32u; From 381d5b37b738c2fd835744a0d6a108acb32c1f63 Mon Sep 17 00:00:00 2001 From: Alex Collini Date: Sat, 10 Oct 2026 08:38:41 +0200 Subject: [PATCH 2/2] fix(recompiler): hold 64 images in a sampled image heap A shader reported in review binds 40 sampled images of one class, more than 32. The cap only bounds the compact heap arrays, so no layout or ABI version changes. The test covers 40 images and a full class through the constant. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceAgcDriver/tests/ShaderDeviceProfile.cpp | 3 ++- core/shader/recompiler/RuntimeAbi.hpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/ShaderDeviceProfile.cpp b/core/libs/prx/libSceAgcDriver/tests/ShaderDeviceProfile.cpp index 5155b5759..11e9c1dd1 100644 --- a/core/libs/prx/libSceAgcDriver/tests/ShaderDeviceProfile.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/ShaderDeviceProfile.cpp @@ -142,7 +142,8 @@ void CheckHeaps() { Require(single.layout.ShaderDataDwords() == full.layout.ShaderDataDwords() && single.layout.memoryOffsetDword == full.layout.memoryOffsetDword && !full.layout.UsesPushData(), "runtime layout depends on resource count"); Require(full.layout.memoryOffsetDword == 0u && full.layout.DispatchThreadLimitDword() == 0u && full.layout.ShaderDataDwords() == 0u, "direct image resources allocated runtime metadata"); Reject([&] { allocate(image, RuntimeAbi::SampledHeapCapacity + 1u); }, "heap capacity exceeded"); - Require(BindingAllocator{}.FindBinding(allocate(image, 32u).layout, DescriptorBindingForImage(image)).resources.size() == 32u, "a sampled heap does not hold 32 images of one class"); + Require(BindingAllocator{}.FindBinding(allocate(image, 40u).layout, DescriptorBindingForImage(image)).resources.size() == 40u, "a sampled heap does not hold 40 images of one class"); + Require(BindingAllocator{}.FindBinding(allocate(image, RuntimeAbi::SampledHeapCapacity).layout, DescriptorBindingForImage(image)).resources.size() == RuntimeAbi::SampledHeapCapacity, "a sampled heap does not hold a full class of images"); Require(ResourceMaterializer::BindlessSlots() == 16u, "bindless image tables changed size with the sampled heap"); Reject([&] { allocate(image, 1u, RuntimeAbi::SamplerHeapCapacity + 1u); }, "metadata capacity"); image.resourceClass = ImageResourceClass::Storage; diff --git a/core/shader/recompiler/RuntimeAbi.hpp b/core/shader/recompiler/RuntimeAbi.hpp index 8993cb19c..131372e68 100644 --- a/core/shader/recompiler/RuntimeAbi.hpp +++ b/core/shader/recompiler/RuntimeAbi.hpp @@ -34,7 +34,7 @@ enum class Stage : std::uint32_t { Main, Fragment, TessellationControl, Tessella inline constexpr std::uint32_t UserDataCapacity = 128u; inline constexpr std::uint32_t BufferCapacity = 128u; inline constexpr std::uint32_t ImageCapacity = 256u; -inline constexpr std::uint32_t SampledHeapCapacity = 32u; +inline constexpr std::uint32_t SampledHeapCapacity = 64u; inline constexpr std::uint32_t BindlessTableSlots = 16u; inline constexpr std::uint32_t StorageMipSlots = 4u; inline constexpr std::uint32_t StorageHeapCapacity = 16u;