diff --git a/core/shader/recompiler/Optimization/include/Optimization/BindingAllocator.hpp b/core/shader/recompiler/Optimization/include/Optimization/BindingAllocator.hpp index 4b3755e656..7396e8e96d 100644 --- a/core/shader/recompiler/Optimization/include/Optimization/BindingAllocator.hpp +++ b/core/shader/recompiler/Optimization/include/Optimization/BindingAllocator.hpp @@ -19,6 +19,7 @@ struct BindingAllocationResult : CompiledBindingLayout { std::vector specialization; std::vector bindings; std::vector pushConstants; + std::vector shaderDataUserWords; }; class BindingAllocator { diff --git a/core/shader/recompiler/Optimization/include/Optimization/ResourceProgram.hpp b/core/shader/recompiler/Optimization/include/Optimization/ResourceProgram.hpp index 77328b8672..f83da69525 100644 --- a/core/shader/recompiler/Optimization/include/Optimization/ResourceProgram.hpp +++ b/core/shader/recompiler/Optimization/include/Optimization/ResourceProgram.hpp @@ -38,6 +38,7 @@ struct ResourceCapture { // The walk's read addresses when the plan has pure flat slots (the leaf of each pure slot, // and every other read sorted and deduplicated); empty otherwise. SrtReadTrace readTrace; + SrtUserDataUse userDataUse; // APS5_PROFILE_DRAW: what CaptureResources spent resolving the source (the stage inputs, the // key over the code, the plan) before the walk. std::uint64_t sourceNanoseconds = 0; diff --git a/core/shader/recompiler/Optimization/include/Optimization/SrtWalker.hpp b/core/shader/recompiler/Optimization/include/Optimization/SrtWalker.hpp index 46dc84574f..8d345285de 100644 --- a/core/shader/recompiler/Optimization/include/Optimization/SrtWalker.hpp +++ b/core/shader/recompiler/Optimization/include/Optimization/SrtWalker.hpp @@ -2,6 +2,7 @@ #define CORE_SHADER_RECOMPILIER_OPTIMIZATION_INCLUDE_OPTIMIZATION_SRTWALKER_HPP #include "IntermediateRepresentation/IrProgram.hpp" +#include "UserDataUse.hpp" #include #include @@ -35,6 +36,7 @@ struct SrtRuntime { SrtMemoryProbe accessible = nullptr; SrtMemoryProbe pendingWrite = nullptr; SrtMemoryBulkReader readWords = nullptr; + SrtUserDataUse* userDataUse = nullptr; }; enum class RuntimeValueType { diff --git a/core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp b/core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp index d988925a91..f0350d9109 100644 --- a/core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp +++ b/core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp @@ -525,6 +525,10 @@ void DescriptorBindingBuilder::Populate(BindingAllocationResult& allocation, con const auto& layout = compiled.layout; const bool needsShaderData = layout.UsesPushData() || plan.storageData; auto shaderData = needsShaderData ? ShaderDataDwordsFor(layout, userDataBase, snapshot, partialThreads, plan.imageMetadata, plan.runtimeState) : std::vector{}; + allocation.shaderDataUserWords.clear(); + if (needsShaderData) { + for (const auto reg : layout.userDataRegisters) allocation.shaderDataUserWords.push_back(reg - userDataBase); + } std::vector bindings; bindings.reserve(plan.bindings.size()); std::size_t writtenHere = 0; diff --git a/core/shader/recompiler/Optimization/src/SrtWalker/SrtEvaluator.cpp b/core/shader/recompiler/Optimization/src/SrtWalker/SrtEvaluator.cpp index 190e6af479..d9a5fbccf7 100644 --- a/core/shader/recompiler/Optimization/src/SrtWalker/SrtEvaluator.cpp +++ b/core/shader/recompiler/Optimization/src/SrtWalker/SrtEvaluator.cpp @@ -194,7 +194,18 @@ bool BasicEvaluator::EvaluateRawRead(const TNode& inst, std::uint64_t& re std::uint64_t low = 0; std::uint64_t high = 0; std::uint64_t offset = 0; - if (!Arg(handle, 0, low) || !Arg(handle, 1, high) || !Arg(inst, 1, offset)) { + std::int32_t origin = -1; + const auto baseWord = [&] { + const TNode source = handle.Argument(0); + if (_runtime.userDataUse == nullptr || source.HasImmediate() || source.Opcode() != IrOpcode::GetUserData) return Arg(handle, 0, low); + const auto reg = RegIndex(static_cast(source.Argument(0).RegisterIndex())); + if (reg < _program.userDataBase || reg - _program.userDataBase >= _runtime.userData.size() || reg - _program.userDataBase >= SrtUserDataUse::TrackedWords) return Arg(handle, 0, low); + origin = static_cast(reg - _program.userDataBase); + low = _runtime.userData[reg - _program.userDataBase]; + _runtime.userDataUse->asBase |= std::uint64_t {1} << origin; + return true; + }; + if (!baseWord() || !Arg(handle, 1, high) || !Arg(inst, 1, offset)) { return false; } const auto base = ((high << 32u) | static_cast(low)) & AddressMask; @@ -229,6 +240,7 @@ bool BasicEvaluator::EvaluateRawRead(const TNode& inst, std::uint64_t& re result = 0; return true; } + if (_runtime.userDataUse != nullptr) _runtime.userDataUse->reads.emplace_back(address, origin); if (_inaccessible != nullptr && _runtime.accessible != nullptr && !_runtime.accessible(_runtime.userContext, address, sizeof(std::uint32_t)) && LoadedFromMemory(*handle.Value())) { *_inaccessible = {inst.Value(), address}; return false; @@ -407,6 +419,7 @@ bool BasicEvaluator::EvaluateInst(const TNode& inst, std::uint64_t& resul if (reg < _program.userDataBase || reg - _program.userDataBase >= _runtime.userData.size()) { return false; } + if (_runtime.userDataUse != nullptr) _runtime.userDataUse->asValue |= reg - _program.userDataBase < SrtUserDataUse::TrackedWords ? std::uint64_t {1} << (reg - _program.userDataBase) : ~std::uint64_t {0}; result = _runtime.userData[reg - _program.userDataBase]; return true; } diff --git a/core/shader/recompiler/Recompiler.cpp b/core/shader/recompiler/Recompiler.cpp index 32dcb23ba2..7a67b3e3b1 100644 --- a/core/shader/recompiler/Recompiler.cpp +++ b/core/shader/recompiler/Recompiler.cpp @@ -992,6 +992,7 @@ RecompileResult materializeResult(const CompiledVariant& variant, const Recompil std::copy_n(runtimeSelectors.begin(), layout.runtimeStateDwords, data->guestDescriptor.begin() + first); } } + result.shaderDataUserWords = std::move(bindings.shaderDataUserWords); result.poisonedSrtReads = static_cast(snapshot.srtPoison.size()) + snapshot.nullRootReads; if (!tables.map.empty()) { result.imageTablePoison.reserve(tables.poison.size()); @@ -1326,11 +1327,12 @@ namespace { // traced into the capture (ResourceCapture::readTrace). void materializeCapture(ResourceCapture& capture, const SrtRuntime& runtime) { const auto& plan = *capture.plan; + SrtRuntime traced = runtime; + traced.userDataUse = &capture.userDataUse; if (std::none_of(plan.pureFlatSlots.begin(), plan.pureFlatSlots.end(), [](std::uint8_t pure) { return pure != 0u; })) { - ResourceMaterializer{}.Materialize(plan, runtime, capture.snapshot); + ResourceMaterializer{}.Materialize(plan, traced, capture.snapshot); return; } - SrtRuntime traced = runtime; traced.readTrace = &capture.readTrace; ResourceMaterializer{}.Materialize(plan, traced, capture.snapshot); auto& other = capture.readTrace.otherReads; diff --git a/core/shader/recompiler/Recompiler.hpp b/core/shader/recompiler/Recompiler.hpp index 157ab11feb..ff93352beb 100644 --- a/core/shader/recompiler/Recompiler.hpp +++ b/core/shader/recompiler/Recompiler.hpp @@ -579,6 +579,7 @@ struct ShaderInvocation { struct RecompileResult : CompiledShaderArtifact, ShaderInvocation { bool cacheHit = false; std::uint32_t workgroupMemoryDwords = 0; + std::vector shaderDataUserWords; [[nodiscard]] std::uint64_t PipelineVariantId() const { return specializationId != 0 ? specializationId : variantId; } }; diff --git a/core/shader/recompiler/UserDataUse.hpp b/core/shader/recompiler/UserDataUse.hpp new file mode 100644 index 0000000000..c193714270 --- /dev/null +++ b/core/shader/recompiler/UserDataUse.hpp @@ -0,0 +1,19 @@ +#ifndef CORE_SHADER_RECOMPILER_USERDATAUSE_HPP +#define CORE_SHADER_RECOMPILER_USERDATAUSE_HPP + +#include +#include +#include + +namespace ShaderRecompiler { + +struct SrtUserDataUse { + static constexpr std::uint32_t TrackedWords = 64; + std::uint64_t asBase = 0; + std::uint64_t asValue = 0; + std::vector> reads; +}; + +} + +#endif