From 83e0a01540b6f78da6f293f926a7a20f4bda5e76 Mon Sep 17 00:00:00 2001 From: MathiasDevelopes <25400788+MathiasDevelopes@users.noreply.github.com> Date: Fri, 9 Oct 2026 15:22:32 +0200 Subject: [PATCH] fix(recompiler): size a typed storage heap for several images, not one dynamic image A typed storage-image heap held four elements, and a dynamic-mip storage image takes four of them for its mip slots. A shader with a dynamic-mip storage image and any other storage image of the same class needed five and failed with "shader image heap capacity exceeded". The mip slots per dynamic image stay four (StorageMipSlots); a storage heap now holds sixteen elements (StorageHeapCapacity). Heap arrays are declared with their compact length, so shaders that fitted before are unchanged. Co-Authored-By: Claude Sonnet 5.5 --- .../prx/libSceAgcDriver/tests/ShaderDeviceProfile.cpp | 9 +++++++-- .../recompiler/Optimization/src/BindingAllocator.cpp | 2 +- .../Optimization/src/DescriptorBindingBuilder.cpp | 2 +- .../recompiler/Optimization/src/ResourceMaterializer.cpp | 6 +++--- core/shader/recompiler/RuntimeAbi.hpp | 3 ++- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/ShaderDeviceProfile.cpp b/core/libs/prx/libSceAgcDriver/tests/ShaderDeviceProfile.cpp index d928bf05c..7f3366c56 100644 --- a/core/libs/prx/libSceAgcDriver/tests/ShaderDeviceProfile.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/ShaderDeviceProfile.cpp @@ -144,10 +144,15 @@ void CheckHeaps() { Reject([&] { allocate(image, 1u, RuntimeAbi::SamplerHeapCapacity + 1u); }, "metadata capacity"); image.resourceClass = ImageResourceClass::Storage; image.mipMode = ImageMipMode::DynamicStorage; - image.mipCount = RuntimeAbi::StorageHeapCapacity; + image.mipCount = RuntimeAbi::StorageMipSlots; const auto storage = allocate(image, 1u); Require(BindingAllocator{}.FindBinding(storage.layout, DescriptorBindingForImage(image)).resources.size() == image.mipCount, "storage heap did not reserve each mip"); - Reject([&] { allocate(image, 2u); }, "heap capacity exceeded"); + const auto shared = allocate(image, 2u); + Require(BindingAllocator{}.FindBinding(shared.layout, DescriptorBindingForImage(image)).resources.size() == 2u * image.mipCount, "a storage heap did not hold two dynamic-mip images"); + const auto capacity = RuntimeAbi::StorageHeapCapacity / image.mipCount; + const auto filled = allocate(image, capacity); + Require(BindingAllocator{}.FindBinding(filled.layout, DescriptorBindingForImage(image)).resources.size() == RuntimeAbi::StorageHeapCapacity, "a full storage heap was not allocated"); + Reject([&] { allocate(image, capacity + 1u); }, "heap capacity exceeded"); Reject([&] { allocate(image, 0u, RuntimeAbi::SamplerHeapCapacity / 2u + 1u); }, "sampler pairs"); const std::array dimensions{RdnaImageDimension::Dim1D, RdnaImageDimension::Dim1DArray, RdnaImageDimension::Dim2D, RdnaImageDimension::Dim2DArray, RdnaImageDimension::Dim3D, RdnaImageDimension::Dim2DMsaa, RdnaImageDimension::Dim2DMsaaArray}; std::set classes; diff --git a/core/shader/recompiler/Optimization/src/BindingAllocator.cpp b/core/shader/recompiler/Optimization/src/BindingAllocator.cpp index 518ac228e..185c7ea89 100644 --- a/core/shader/recompiler/Optimization/src/BindingAllocator.cpp +++ b/core/shader/recompiler/Optimization/src/BindingAllocator.cpp @@ -114,7 +114,7 @@ BindingAllocationResult BindingAllocator::Allocate(IrProgram& program, const Bin std::array, ImageBindingCount> imageGroups; const auto place = [&](std::uint32_t i) { const bool dynamic = info.images[i].mipMode == ImageMipMode::DynamicStorage; - const std::uint32_t count = dynamic ? RuntimeAbi::StorageHeapCapacity : 1u; + const std::uint32_t count = dynamic ? RuntimeAbi::StorageMipSlots : 1u; std::array placed{}; for (const auto& mode : ResourceMaterializer::RuntimeImageModes(info.images[i])) { const auto group = ImageBindingIndex(DescriptorBindingForImage(mode)); diff --git a/core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp b/core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp index 5a7a24e54..0c06248de 100644 --- a/core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp +++ b/core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp @@ -312,7 +312,7 @@ DescriptorBindingPlan DescriptorBindingBuilder::Prepare(const IrBindingLayout& l const auto word = snapshot.images.at(index).dwords[3]; const auto first = (word >> 12u) & 0xfu; const auto last = (word >> 16u) & 0xfu; - if (last < first || last - first >= RuntimeAbi::StorageHeapCapacity) fail("invalid dynamic storage mip range"); + if (last < first || last - first >= RuntimeAbi::StorageMipSlots) fail("invalid dynamic storage mip range"); plan.specialization.push_back({PipelineSpecialization::MipCountBase + index, last - first + 1u}); } for (std::uint32_t resource = 0; resource < info.images.size(); ++resource) { diff --git a/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp b/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp index 8bd10eac8..8f4edf617 100644 --- a/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp +++ b/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp @@ -619,7 +619,7 @@ std::vector ResourceMaterializer::RuntimeImageModes(const ImageRe mode.depthBits = depth; mode.depthUnorm16 = unorm16; mode.cube = false; - mode.mipCount = mode.mipMode == ImageMipMode::DynamicStorage ? RuntimeAbi::StorageHeapCapacity : 1u; + mode.mipCount = mode.mipMode == ImageMipMode::DynamicStorage ? RuntimeAbi::StorageMipSlots : 1u; mode.shaderSwizzle = ShaderImageIdentitySwizzle; if (conversion == IrBufferFormat::Format11_11_10UNorm || conversion == IrBufferFormat::Format10_11_11Float) mode.shaderSwizzle = 0x2acu; modes.push_back(mode); @@ -746,7 +746,7 @@ std::uint32_t ResourceMaterializer::RuntimeImageMode(const ImageResource& image, if (!exact) throw std::runtime_error(storage ? "runtime packed image bits are not reproducible through the view" : "runtime packed image bits are not recoverable from the view"); } } - if (decoded.mipCount > (image.mipMode == ImageMipMode::DynamicStorage ? RuntimeAbi::StorageHeapCapacity : 1u)) throw std::runtime_error("runtime storage image mip capacity exceeded"); + if (decoded.mipCount > (image.mipMode == ImageMipMode::DynamicStorage ? RuntimeAbi::StorageMipSlots : 1u)) throw std::runtime_error("runtime storage image mip capacity exceeded"); if ((descriptorImageSwizzle(descriptor) & 06666u) == 0u && !decoded.fmask && !decoded.depthBits && decoded.conversionFormat == IrBufferFormat::Invalid && !decoded.srgbDecode) { for (std::uint32_t index = 0u; index < modes.size(); ++index) { if (modes[index].constantSwizzle && modes[index].numericClass == decoded.numericClass) return index; @@ -795,7 +795,7 @@ void ResourceMaterializer::ApplyStaticInterface(IrProgram& program, bool nativeS image.srgbDecodeFormats = resources.srgbDecodeFormats; if (image.indirectRoot != ImageResource::NoIndirectImage) throw std::runtime_error("static image interface was already expanded"); image.numericClass = image.atomic ? IrTextureNumericClass::Uint : IrTextureNumericClass::Float; - image.mipCount = image.mipMode == ImageMipMode::DynamicStorage ? RuntimeAbi::StorageHeapCapacity : 1u; + image.mipCount = image.mipMode == ImageMipMode::DynamicStorage ? RuntimeAbi::StorageMipSlots : 1u; if (resources.descriptorSources.at(image.source).indirectImage.has_value()) { if (images.size() + slots - 1u > ShaderInfo::MaxImages) throw std::runtime_error("static bindless image capacity exceeded"); image.indirectRoot = index; diff --git a/core/shader/recompiler/RuntimeAbi.hpp b/core/shader/recompiler/RuntimeAbi.hpp index 6f6f84d93..43f7bb37c 100644 --- a/core/shader/recompiler/RuntimeAbi.hpp +++ b/core/shader/recompiler/RuntimeAbi.hpp @@ -35,7 +35,8 @@ 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 StorageHeapCapacity = 4u; +inline constexpr std::uint32_t StorageMipSlots = 4u; +inline constexpr std::uint32_t StorageHeapCapacity = 16u; inline constexpr std::uint32_t SamplerHeapCapacity = 32u; struct ResourceMetadata {