From 8b7ff670a74de27038dad74b31b8b11c5d034ff5 Mon Sep 17 00:00:00 2001 From: XairRrR Date: Fri, 9 Oct 2026 12:28:32 +0200 Subject: [PATCH] fix(recompiler): bind the rect-list fault buffer in the tessellation control group The rect-list control and evaluation shaders put their fault buffer one past the highest vertex or fragment binding. Bindings are grouped per stage (RuntimeAbi::BindingNumber), so that number could fall inside another stage's group and collide with one of its bindings, and the draw failed with a duplicate binding. The fault buffer now uses the TessellationControl FaultBuffer binding. Uncharted: Legacy of Thieves Collection (PPSA05684) stopped at its first rect-list draw with "duplicate shader binding". --- core/libs/prx/libSceAgcDriver/tests/Graphics.cpp | 4 ++++ .../recompiler/SpirvBackend/src/RectListShaders.cpp | 9 ++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index a035d3bf6..b7626a276 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -2041,6 +2041,10 @@ void rectListTests() { target.spirvVersion = 0x00010300u; target.supportedCapabilities = capabilities; target.tessellation = TessellationTargetLimits{32, 128, 128, 120, 4096, 128, 128}; + vertex.bindings = {{DescriptorKind::StorageBuffer, DescriptorRole::GuestBuffers, 0, 0, 1, {}}}; + const auto faultAuxiliary = BuildRectListShaders(vertex, fragment, target); + Require(faultAuxiliary.control.bindings.size() == 1 && faultAuxiliary.control.bindings[0].role == DescriptorRole::FaultBuffer && faultAuxiliary.control.bindings[0].binding == RuntimeAbi::BindingNumber(RuntimeAbi::Stage::TessellationControl, RuntimeAbi::Binding::FaultBuffer), "the rect-list control shader's fault buffer is not in the tessellation control binding group"); + vertex.bindings.clear(); for (const auto version : {0x00010300u, 0x00010400u}) { target.spirvVersion = version; auto auxiliary = BuildRectListShaders(vertex, fragment, target); diff --git a/core/shader/recompiler/SpirvBackend/src/RectListShaders.cpp b/core/shader/recompiler/SpirvBackend/src/RectListShaders.cpp index ed7a17055..f8af830fa 100644 --- a/core/shader/recompiler/SpirvBackend/src/RectListShaders.cpp +++ b/core/shader/recompiler/SpirvBackend/src/RectListShaders.cpp @@ -1,5 +1,6 @@ #include "Recompiler.hpp" #include "BdaAbi.hpp" +#include "RuntimeAbi.hpp" #include "SpirvBackend/SpirvModule.hpp" #if ANYPS5_ENABLE_SPIRV_TOOLS #include "SpirvBackend/SpirvOptimizer.hpp" @@ -393,13 +394,7 @@ RectListShaders BuildRectListShaders(const RecompileResult& vertex, const Recomp } const auto components = static_cast((parameters.size() + 1) * 4); require(limits.maxPatchSize >= 4 && components <= limits.maxControlPerVertexInputComponents && components <= limits.maxControlPerVertexOutputComponents && components <= limits.maxEvaluationInputComponents && components <= limits.maxEvaluationOutputComponents && limits.maxControlPerPatchOutputComponents >= 6 && components * 4 + 6 <= limits.maxControlTotalOutputComponents, "tessellation interface exceeds device limits"); - std::uint32_t faultBinding = 0; - for (const auto* shader : {&vertex, &fragment}) { - for (const auto& binding : shader->bindings) { - require(binding.binding < std::numeric_limits::max(), "descriptor binding overflow"); - faultBinding = std::max(faultBinding, binding.binding + 1); - } - } + const auto faultBinding = RuntimeAbi::BindingNumber(RuntimeAbi::Stage::TessellationControl, RuntimeAbi::Binding::FaultBuffer); RectListEmitter control(parameters, spv::ExecutionModelTessellationControl, target.spirvVersion, faultBinding); RectListEmitter evaluation(parameters, spv::ExecutionModelTessellationEvaluation, target.spirvVersion, faultBinding); RectListShaders shaders;