diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp index 493436dcbc06..2a39a33b01b0 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp @@ -26,6 +26,8 @@ namespace AgcDriver::Graphics { class Recorder; +void BindResourceDescriptorSets_nid_no_patch(const Context& context, VkCommandBuffer commands, VkPipelineBindPoint bindPoint, VkPipelineLayout layout, VkDescriptorSet main, VkDescriptorSet fragment); + void FlushCachedTextures(VkDevice device); void ClearCachedTextures(VkDevice device); std::uint64_t TextureCacheBudget(const VkPhysicalDeviceMemoryProperties& memory); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp index ee37b74a072d..9dd4d56c89df 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp @@ -1765,10 +1765,7 @@ void recordDraw(const Context& context, const State& state, const Pm4::DrawParam } APS5_LOG_CHARS_OUT_DEBUG("Pipeline Begin OK"); if (drawBindings != nullptr) { - const auto set = drawBindings->allocation.set; - const auto fragmentSet = drawBindings->fragmentAllocation.set; - if (set != VK_NULL_HANDLE) context.Resolved(&DeviceFunctions::cmdBindDescriptorSets, "vkCmdBindDescriptorSets")(commands, VK_PIPELINE_BIND_POINT_GRAPHICS, record.pipeline->Layout(), 0, 1, &set, 0, nullptr); - if (fragmentSet != VK_NULL_HANDLE) context.Resolved(&DeviceFunctions::cmdBindDescriptorSets, "vkCmdBindDescriptorSets")(commands, VK_PIPELINE_BIND_POINT_GRAPHICS, record.pipeline->Layout(), ShaderRecompiler::RuntimeAbi::FragmentDescriptorSet, 1, &fragmentSet, 0, nullptr); + BindResourceDescriptorSets_nid_no_patch(context, commands, VK_PIPELINE_BIND_POINT_GRAPHICS, record.pipeline->Layout(), drawBindings->allocation.set, drawBindings->fragmentAllocation.set); } else { resources.Bind(commands, VK_PIPELINE_BIND_POINT_GRAPHICS, record.pipeline->Layout()); } diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp index 1a419984b62f..ccce8985eae7 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp @@ -4475,9 +4475,17 @@ std::shared_ptr ShaderResources::PrepareDrawBindi return result; } +void BindResourceDescriptorSets_nid_no_patch(const Context& context, VkCommandBuffer commands, VkPipelineBindPoint bindPoint, VkPipelineLayout layout, VkDescriptorSet main, VkDescriptorSet fragment) { + static_assert(ShaderRecompiler::RuntimeAbi::FragmentDescriptorSet == 1); + if (main == VK_NULL_HANDLE && fragment == VK_NULL_HANDLE) return; + const std::array sets{main, fragment}; + const std::uint32_t first = main == VK_NULL_HANDLE ? 1u : 0u; + const std::uint32_t count = main != VK_NULL_HANDLE && fragment != VK_NULL_HANDLE ? 2u : 1u; + context.Resolved(&DeviceFunctions::cmdBindDescriptorSets, "vkCmdBindDescriptorSets")(commands, bindPoint, layout, first, count, sets.data() + first, 0, nullptr); +} + void ShaderResources::Bind(VkCommandBuffer commands, VkPipelineBindPoint bindPoint, VkPipelineLayout layout) const { - if (_set != VK_NULL_HANDLE) context.Resolved(&DeviceFunctions::cmdBindDescriptorSets, "vkCmdBindDescriptorSets")(commands, bindPoint, layout, 0, 1, &_set, 0, nullptr); - if (fragment.set != VK_NULL_HANDLE) context.Resolved(&DeviceFunctions::cmdBindDescriptorSets, "vkCmdBindDescriptorSets")(commands, bindPoint, layout, ShaderRecompiler::RuntimeAbi::FragmentDescriptorSet, 1, &fragment.set, 0, nullptr); + BindResourceDescriptorSets_nid_no_patch(context, commands, bindPoint, layout, _set, fragment.set); } namespace { diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index edb679bc598c..835665aae1fa 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -2887,6 +2887,9 @@ struct MockVulkan { std::vector copies; bool allowCopies = false; std::uint32_t boundSets = 0; + std::uint32_t bindCalls = 0; + std::vector boundHandles; + std::map writtenSets; std::uint32_t boundFirst = 0; VkPipelineBindPoint boundPoint = VK_PIPELINE_BIND_POINT_MAX_ENUM; std::map pipelineLayoutPushConstantSize; @@ -3049,6 +3052,7 @@ VKAPI_ATTR void VKAPI_CALL mockUpdateDescriptorSets(VkDevice, std::uint32_t coun Require(copyCount == 0 || mock.allowCopies, "descriptor copies are not expected"); if (copyCount != 0) mock.copies.insert(mock.copies.end(), copies, copies + copyCount); for (std::uint32_t i = 0; i < count; ++i) { + mock.writtenSets[writes[i].dstBinding] = writes[i].dstSet; MockDescriptorWrite write{writes[i].dstBinding, writes[i].dstArrayElement, writes[i].descriptorCount, writes[i].descriptorType, {}, {}}; if (writes[i].pBufferInfo != nullptr) write.buffers.assign(writes[i].pBufferInfo, writes[i].pBufferInfo + writes[i].descriptorCount); if (writes[i].pImageInfo != nullptr) write.images.assign(writes[i].pImageInfo, writes[i].pImageInfo + writes[i].descriptorCount); @@ -3056,7 +3060,9 @@ VKAPI_ATTR void VKAPI_CALL mockUpdateDescriptorSets(VkDevice, std::uint32_t coun } } -VKAPI_ATTR void VKAPI_CALL mockCmdBindDescriptorSets(VkCommandBuffer, VkPipelineBindPoint point, VkPipelineLayout, std::uint32_t first, std::uint32_t count, const VkDescriptorSet*, std::uint32_t, const std::uint32_t*) { +VKAPI_ATTR void VKAPI_CALL mockCmdBindDescriptorSets(VkCommandBuffer, VkPipelineBindPoint point, VkPipelineLayout, std::uint32_t first, std::uint32_t count, const VkDescriptorSet* sets, std::uint32_t, const std::uint32_t*) { + ++mock.bindCalls; + mock.boundHandles.assign(sets, sets + count); mock.boundPoint = point; mock.boundFirst = first; mock.boundSets = count; @@ -3561,6 +3567,25 @@ void descriptorSnapshotTests() { } } +void splitResourceBindTests() { + mock = MockVulkan{}; + const auto context = mockContext(); + const auto state = AgcDriver::Graphics::DecodeState(makeState()); + { + ShaderRecompiler::RecompileResult vertex, fragment; + vertex.bindings.push_back(makeBinding(Role::GuestBuffers, 0, 1, vsharp(guestFirst.data(), 16))); + const auto fragmentBinding = ShaderRecompiler::RuntimeAbi::BindingNumber(ShaderRecompiler::RuntimeAbi::Stage::Fragment, ShaderRecompiler::RuntimeAbi::Binding::Buffers); + fragment.bindings.push_back(makeBinding(Role::GuestBuffers, fragmentBinding, 1, vsharp(guestThird.data(), 8))); + fragment.bindings.back().descriptorSet = ShaderRecompiler::RuntimeAbi::FragmentDescriptorSet; + const std::array shaders{{{ShaderRecompiler::ShaderStage::Vertex, &vertex, 0}, {ShaderRecompiler::ShaderStage::Fragment, &fragment, 0}}}; + AgcDriver::Graphics::ShaderResources resources(context, shaders, state.color, 1, 0, 0); + resources.Bind(reinterpret_cast(std::uintptr_t{1}), VK_PIPELINE_BIND_POINT_GRAPHICS, VK_NULL_HANDLE); + Require(mock.bindCalls == 1 && mock.boundFirst == 0 && mock.boundSets == 2, "split resources require one bind of consecutive sets"); + Require(mock.boundHandles == std::vector{mock.writtenSets.at(0), mock.writtenSets.at(fragmentBinding)}, "split resources lost descriptor set order or identity"); + } + Require(mock.live == 0, "split resources leaked Vulkan objects"); +} + void resourceTests() { mock = MockVulkan{}; const auto context = mockContext(); @@ -6499,6 +6524,7 @@ int main(int argc, char** argv) { descriptorPoolTests(); pushConstantTests(); resourceTests(); + splitResourceBindTests(); descriptorSnapshotTests(); descriptorCacheTests(); bufferBudgetPlacementTests();