From 2539e9787f2c0bf778bc36389bf960df65e44500 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Thu, 24 Sep 2026 14:57:39 +0300 Subject: [PATCH] perf(agc): avoid guest memory uploads for rect-list fault buffers --- .../Graphics/include/BdaResources.hpp | 3 +- .../Graphics/include/ShaderResources.hpp | 1 + .../Graphics/src/BdaResources.cpp | 11 +++++-- .../Graphics/src/ShaderAddressResources.cpp | 4 ++- .../Graphics/src/ShaderResources.cpp | 1 + .../libSceAgcDriver/tests/BdaResources.cpp | 22 +++++++++++++ .../SpirvBackend/SpirvMemory/SpirvAtomics.hpp | 15 +++++++++ .../SpirvMemory/SpirvFormatConversion.hpp | 16 +++++++++ .../SpirvMemory/SpirvInterface.hpp | 33 +++++++++++++++++++ .../SpirvMemory/SpirvMemoryAccess.hpp | 25 ++++++++++++++ 10 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvAtomics.hpp create mode 100644 core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvFormatConversion.hpp create mode 100644 core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvInterface.hpp create mode 100644 core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvMemoryAccess.hpp diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/BdaResources.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/BdaResources.hpp index 1923dcfe1..0163b2103 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/BdaResources.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/BdaResources.hpp @@ -7,6 +7,7 @@ namespace AgcDriver::Graphics { class BdaResources { public: + explicit BdaResources(const Context& context); BdaResources(const Context& context, const GuestBufferMemory& memory); VkDescriptorBufferInfo Table() const; VkDescriptorBufferInfo Fault() const; @@ -15,7 +16,7 @@ public: private: std::unique_ptr table; std::unique_ptr fault; - std::size_t tableBytes; + std::size_t tableBytes = 0; }; } diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp index 789dca14d..059c3c611 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp @@ -51,6 +51,7 @@ private: GuestBufferMemory guestMemory; std::unique_ptr bda; bool usesBda = false; + bool usesFaultBuffer = false; VkDescriptorSetLayout _layout = VK_NULL_HANDLE; VkDescriptorSet _set = VK_NULL_HANDLE; VkDescriptorPool pool = VK_NULL_HANDLE; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/BdaResources.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/BdaResources.cpp index 691084d55..94793d333 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/BdaResources.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/BdaResources.cpp @@ -6,22 +6,27 @@ namespace AgcDriver::Graphics { -BdaResources::BdaResources(const Context& context, const GuestBufferMemory& memory) { +BdaResources::BdaResources(const Context& context) { static_assert(std::endian::native == std::endian::little); + Require(sizeof(ShaderRecompiler::BdaAbi::Fault) <= context.limits.maxStorageBufferRange, "BDA fault buffer exceeds storage buffer range limit"); + fault = std::make_unique(context, sizeof(ShaderRecompiler::BdaAbi::Fault), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); + std::memset(fault->Bytes().data(), 0, fault->Bytes().size()); +} + +BdaResources::BdaResources(const Context& context, const GuestBufferMemory& memory) : BdaResources(context) { const auto ranges = memory.AddressRanges(); Require(ranges.size() <= std::numeric_limits::max(), "BDA table range count overflow"); Require(ranges.size() <= (std::numeric_limits::max() - sizeof(ShaderRecompiler::BdaAbi::Header)) / sizeof(ShaderRecompiler::BdaAbi::Range), "BDA table size overflow"); tableBytes = sizeof(ShaderRecompiler::BdaAbi::Header) + ranges.size() * sizeof(ShaderRecompiler::BdaAbi::Range); Require(tableBytes <= context.limits.maxStorageBufferRange && sizeof(ShaderRecompiler::BdaAbi::Fault) <= context.limits.maxStorageBufferRange, "BDA descriptors exceed storage buffer range limit"); table = std::make_unique(context, tableBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); - fault = std::make_unique(context, sizeof(ShaderRecompiler::BdaAbi::Fault), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); const ShaderRecompiler::BdaAbi::Header header{ShaderRecompiler::BdaAbi::Version, static_cast(ranges.size()), sizeof(ShaderRecompiler::BdaAbi::Range), 0}; std::memcpy(table->Bytes().data(), &header, sizeof(header)); if (!ranges.empty()) std::memcpy(table->Bytes().data() + sizeof(header), ranges.data(), ranges.size() * sizeof(ranges.front())); - std::memset(fault->Bytes().data(), 0, fault->Bytes().size()); } VkDescriptorBufferInfo BdaResources::Table() const { + Require(table != nullptr, "BDA page table was not requested"); return {table->Handle(), 0, tableBytes}; } diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderAddressResources.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderAddressResources.cpp index b2b214637..2615c772d 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderAddressResources.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderAddressResources.cpp @@ -16,7 +16,9 @@ void ShaderResources::prepareAddressBindings(std::span sha const bool rectListFault = shader.stage == ShaderRecompiler::ShaderStage::TessellationControl && tables == 0 && faults == 1; Require((tables == faults || rectListFault) && tables <= 1 && faults <= 1, "invalid BDA table and fault descriptors"); Require(shader.program->bdaAbiVersion == (faults == 0 ? 0u : ShaderRecompiler::BdaAbi::Version), "incompatible BDA ABI version"); - usesBda = usesBda || faults != 0; + // Rect-list validation needs a fault buffer, but never accesses guest addresses. + usesBda = usesBda || tables != 0; + usesFaultBuffer = usesFaultBuffer || faults != 0; } if (usesBda) { Require(context.bufferDeviceAddress, "buffer device address is not enabled"); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp index 3acc1116d..ee2657c79 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp @@ -114,6 +114,7 @@ void ShaderResources::build(std::span shaders, const Color Require(storageBuffers <= context.limits.maxDescriptorSetStorageBuffers, "pipeline descriptors exceed device limits"); guestMemory.Upload(usesBda); if (usesBda) bda = std::make_unique(context, guestMemory); + else if (usesFaultBuffer) bda = std::make_unique(context); std::vector description; for (const auto& binding : bindings) description.push_back(binding.layout); VkDescriptorSetLayoutCreateInfo info{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO}; diff --git a/core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp b/core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp index 2d9041a30..6e136b79e 100644 --- a/core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp @@ -58,6 +58,28 @@ void RunBdaResourceTests(const Context& context, const BdaTestAccess& access) { auto disabled = context; disabled.bufferDeviceAddress = false; reject([&] { ShaderResources resources(disabled, compiled, snapshots); }, "not enabled"); + { + // A rect-list fault buffer must not require BDA or consume guest snapshots. + ShaderRecompiler::RecompileResult control; + control.bdaAbiVersion = ShaderRecompiler::BdaAbi::Version; + control.bindings = {binding(Role::FaultBuffer, 5)}; + auto writable = binding(Role::GuestBuffers, 6); + writable.guestDescriptor = {static_cast(address), static_cast(address >> 32) & 0xffffu, sizeof(guest), 0x31000000u}; + control.bindings.push_back(writable); + const std::array stages{{{ShaderRecompiler::ShaderStage::TessellationControl, &control, 0}}}; + const std::array unusedSnapshots{{{0, source}}}; + ShaderResources resources(disabled, stages, ColorTarget{}, 0, 0, unusedSnapshots); + const auto fault = access.bytes(access.descriptor(5).buffer); + for (const auto byte : fault) Require(byte == std::byte{}, "rect-list fault buffer was not initialized"); + const ShaderRecompiler::BdaAbi::Fault report{ShaderRecompiler::BdaAbi::FaultState::Ready, ShaderRecompiler::BdaAbi::FaultReason::InvalidRectangle, 0, 0, 0, 0, 0}; + std::memcpy(fault.data(), &report, sizeof(report)); + reject([&] { resources.WriteBack(); }, "rect-list requires"); + std::memset(fault.data(), 0, fault.size()); + changed = 456; + std::memcpy(access.bytes(access.descriptor(6).buffer).data() + sizeof(std::uint32_t), &changed, sizeof(changed)); + resources.WriteBack(); + Require(guest[1] == changed, "rect-list fault-only path lost guest buffer writes"); + } { ShaderResources resources(context, compiled, snapshots); const auto table = access.bytes(access.descriptor(4).buffer); diff --git a/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvAtomics.hpp b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvAtomics.hpp new file mode 100644 index 000000000..dba4b2c16 --- /dev/null +++ b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvAtomics.hpp @@ -0,0 +1,15 @@ +#ifndef CORE_SPIRVBACKEND_INCLUDE_SPIRVBACKEND_SPIRVATOMICS_HPP +#define CORE_SPIRVBACKEND_INCLUDE_SPIRVBACKEND_SPIRVATOMICS_HPP + +#include "SpirvBackend/SpirvMemory/SpirvEmitterState.hpp" +#include + +namespace ShaderRecompiler { + +void EmitDeviceAtomicMemoryBarrier(SpirvEmitterState& state); +std::uint32_t EmitFloatAtomicReplacement(SpirvEmitterState& state, std::uint32_t old, std::uint32_t source, bool maxValue); +std::uint32_t EmitShaderDataDwordLoad(SpirvEmitterState& state, std::uint32_t dwordIndex); + +} + +#endif diff --git a/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvFormatConversion.hpp b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvFormatConversion.hpp new file mode 100644 index 000000000..c8ebe1996 --- /dev/null +++ b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvFormatConversion.hpp @@ -0,0 +1,16 @@ +#ifndef CORE_SPIRVBACKEND_INCLUDE_SPIRVBACKEND_SPIRVFORMATCONVERSION_HPP +#define CORE_SPIRVBACKEND_INCLUDE_SPIRVBACKEND_SPIRVFORMATCONVERSION_HPP + +#include "SpirvBackend/SpirvMemory/SpirvEmitterState.hpp" +#include + +namespace ShaderRecompiler { + +bool IsSignedFormatComponent(SpirvFormatComponentType type); +std::uint32_t EmitUFloatToF32Bits(SpirvEmitterState& state, std::uint32_t raw, std::uint32_t bits); +std::uint32_t NormalizeFormatComponent(SpirvEmitterState& state, const SpirvBufferFormatInfo& info, std::uint32_t component, std::uint32_t raw); +std::uint32_t EmitTBufferBitcastU32ToI32(SpirvEmitterState& state, std::uint32_t value); + +} + +#endif diff --git a/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvInterface.hpp b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvInterface.hpp new file mode 100644 index 000000000..5fbadbbbb --- /dev/null +++ b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvInterface.hpp @@ -0,0 +1,33 @@ +#ifndef CORE_SPIRVBACKEND_INCLUDE_SPIRVBACKEND_SPIRVINTERFACE_HPP +#define CORE_SPIRVBACKEND_INCLUDE_SPIRVBACKEND_SPIRVINTERFACE_HPP + +#include "SpirvBackend/SpirvMemory/SpirvEmitterState.hpp" +#include "SpirvBackend/SpirvMemory/SpirvModule.hpp" +#include "IntermediateRepresentation/IrValue.hpp" +#include + +namespace ShaderRecompiler { + +void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const BindingAllocationResult& bindings); +void EmitModuleHeader(SpirvEmitterState& state, const BindingAllocationResult& bindings); +void DefineModule(SpirvEmitterState& state); +std::uint32_t DefineInterfaceVariable(SpirvEmitterState& state, std::uint32_t type, std::uint32_t storage, const char* name); +std::uint32_t ExecutionModelForStage(IrShaderStage stage); + +std::uint32_t GlslStd450(SpirvEmitterState& state); +std::uint32_t InputVariableForKind(const SpirvEmitterState& state, StageInputKind kind); +const SpirvInputBinding* SpirvInputBindingForParameter(const SpirvEmitterState& state, std::uint32_t location); + +std::uint32_t PixelParameterLocation(const SpirvEmitterState& state, std::uint32_t attr); +bool PixelParameterIsFlat(const SpirvEmitterState& state, std::uint32_t attr); +bool PixelParameterIsCustom(const SpirvEmitterState& state, std::uint32_t attr); + +VertexInputScalarKind VertexParameterScalarKind(const SpirvEmitterState& state, std::uint32_t location); +std::uint32_t VertexParameterComponentCount(const SpirvInputBinding& input); +std::uint32_t VertexParameterScalarType(SpirvEmitterState& state, VertexInputScalarKind kind); + +std::uint32_t OutputVariableForExport(const SpirvEmitterState& state, const ExportInfo& exp); + +} + +#endif diff --git a/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvMemoryAccess.hpp b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvMemoryAccess.hpp new file mode 100644 index 000000000..1331877d0 --- /dev/null +++ b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvMemoryAccess.hpp @@ -0,0 +1,25 @@ +#ifndef CORE_SPIRVBACKEND_INCLUDE_SPIRVBACKEND_SPIRVMEMORYACCESS_HPP +#define CORE_SPIRVBACKEND_INCLUDE_SPIRVBACKEND_SPIRVMEMORYACCESS_HPP + +#include "SpirvBackend/SpirvMemory/SpirvEmitterState.hpp" +#include + +namespace ShaderRecompiler { + +std::uint32_t LdsDwordCount(const SpirvEmitterState& state); +std::uint32_t StorageBufferPackedStride(const SpirvEmitterState& state, const MemoryInfo& mem); +IrBufferFormat StorageBufferFormat(const SpirvEmitterState& state, const MemoryInfo& mem); + +void EmitMemoryOffsets(SpirvEmitterState& state); + +MemoryResourceAccess PrepareMemoryResourceAccess(SpirvEmitterState& state, const MemoryInfo& mem); +MemoryResourceAccess PrepareStorageBufferResourceAccess(SpirvEmitterState& state, const MemoryInfo& mem, std::uint32_t variable, std::uint32_t pointerType); + +std::uint32_t EmitMemoryElementIndex(SpirvEmitterState& state, const MemoryResourceAccess& access, std::uint32_t rawIndex); +std::uint32_t EmitMemoryElementInBounds(SpirvEmitterState& state, const MemoryResourceAccess& access, std::uint32_t index); +std::uint32_t EmitMemoryElementPointer(SpirvEmitterState& state, const MemoryResourceAccess& access, std::uint32_t index); +std::uint32_t EmitStorageBufferElementPointer(SpirvEmitterState& state, const MemoryResourceAccess& access, std::uint32_t index, std::uint32_t pointerType); + +} + +#endif