From 6122fcc896cb5e645d3ea3d885ad02f75698389a Mon Sep 17 00:00:00 2001 From: Romans Pokrovskis Date: Fri, 23 Oct 2026 03:28:25 +0300 Subject: [PATCH 1/3] test(agc): retain raw draw decodes across snapshot eviction --- .../PreparedUnregisteredGraphics.cpp | 35 +++++++++++++++++++ .../tests/execution/QueuedRawShader.cpp | 11 ++++++ 2 files changed, 46 insertions(+) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/PreparedUnregisteredGraphics.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/PreparedUnregisteredGraphics.cpp index df68d87509e9..0ee091c39980 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/PreparedUnregisteredGraphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/PreparedUnregisteredGraphics.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace { @@ -28,7 +29,41 @@ void Bind(AgcDriver::QueueState& queue, const void* code, std::uint32_t program, queue.shader[resources] = 16u << 1u; } +void CheckRetainedDecode() { + using namespace AgcDriver::DriverDetail; + alignas(256) static std::array code{0xbf810000u}; + alignas(256) static std::array tail{0xbf810000u}; + alignas(256) static std::array, 80> others{}; + const auto address = reinterpret_cast(code.data()); + DrawProgram program{}; + program.snapshot = ReadRawShader(address); + program.tailContinuation = ReadRawShader(reinterpret_cast(tail.data())); + DrawDecode decode{}; + decode.programs.push_back(std::move(program)); + const ShaderRegistry registry; + Require(DecodeProgramsCurrent(decode, registry), "a newly captured raw draw decode is not current"); + for (auto& other : others) { + other.fill(0xbf810000u); + static_cast(ReadRawShader(reinterpret_cast(other.data()))); + } + const auto reread = ReadRawShader(address); + Require(reread->code == decode.programs.front().snapshot->code, "raw snapshot churn changed the shader contents"); + std::cout << "retained raw snapshot evicted=" << (reread != decode.programs.front().snapshot) << '\n'; + Require(DecodeProgramsCurrent(decode, registry), "an unchanged retained raw draw decode was invalidated by snapshot cache eviction"); + code[0] = 0xbf800000u; + code[1] = 0xbf810000u; + Require(!DecodeProgramsCurrent(decode, registry), "a retained raw draw decode ignored changed main code"); + code[0] = 0xbf810000u; + Require(DecodeProgramsCurrent(decode, registry), "restoring raw main code did not restore its retained decode"); + tail[0] = 0xbf800000u; + tail[1] = 0xbf810000u; + Require(!DecodeProgramsCurrent(decode, registry), "a retained raw draw decode ignored changed tail code"); + tail[0] = 0xbf810000u; + Require(DecodeProgramsCurrent(decode, registry), "restoring raw tail code did not restore its retained decode"); +} + void Check(AgcDriver::VulkanDevice& device) { + CheckRetainedDecode(); using namespace AgcDriver::DriverDetail; using namespace ShaderRecompiler; AgcDriver::QueueState queue{}; diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/QueuedRawShader.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/QueuedRawShader.cpp index 703f7523d73f..00da21440302 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/QueuedRawShader.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/QueuedRawShader.cpp @@ -1,3 +1,4 @@ +#include "prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawCache.hpp" #include "prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawPipeline.hpp" #include "prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp" #include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp" @@ -11,6 +12,7 @@ #include #include #include +#include namespace { @@ -22,6 +24,12 @@ void QueuedCode(bool cached) { code[0] = 0x7e000200u; const auto address = reinterpret_cast(code.data()); const auto before = cached ? ReadRawShader(address) : nullptr; + DrawDecode decode{}; + if (before != nullptr) { + DrawProgram program{}; + program.snapshot = before; + decode.programs.push_back(std::move(program)); + } std::atomic finished{false}; std::atomic started{false}; struct Restore { @@ -42,6 +50,9 @@ void QueuedCode(bool cached) { }, {{address, address + sizeof(std::uint32_t)}}); while (!started.load(std::memory_order_acquire)) std::this_thread::yield(); DrawPipeline::Active() = true; + if (before != nullptr && DecodeProgramsCurrent(decode, ShaderRegistry{})) { + throw std::runtime_error("a retained raw draw decode ignored a queued write to its code"); + } const auto after = ReadRawShader(address); finished.store(true, std::memory_order_release); DrawPipeline::Queue0().Drain(DrawPipeline::DrainReason::Submission); From 1939f7352f39985930482231b006eaadefd9bb0b Mon Sep 17 00:00:00 2001 From: Romans Pokrovskis Date: Fri, 23 Oct 2026 03:37:17 +0300 Subject: [PATCH 2/3] perf(agc): reuse retained raw snapshots for draw validation --- .../Execution/include/Driver/Shaders/ShaderRegistry.hpp | 2 +- .../libSceAgcDriver/Execution/src/Driver/Draw/DrawCache.cpp | 4 ++-- .../Execution/src/Driver/Shaders/ShaderRegistry.cpp | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp b/core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp index 059cd7253c30..f56b2ecbfaec 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp +++ b/core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp @@ -183,7 +183,7 @@ struct ShaderSnapshot { }; -std::shared_ptr ReadRawShader(std::uint64_t address); +std::shared_ptr ReadRawShader(std::uint64_t address, const std::shared_ptr& retained = {}); std::shared_ptr ReadUserDataTailContinuation(std::span code, std::uint32_t userDataBase, std::span userData); std::shared_ptr ProgramSnapshot(const ShaderRegistry& shaders, std::uint64_t address, std::initializer_list types); void NoteRawGraphicsDraw(const ShaderSnapshot& snapshot); diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCache.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCache.cpp index 8d05f4693ff2..e9279c798d5f 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCache.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawCache.cpp @@ -208,8 +208,8 @@ void Driver::eraseDrawEntry(std::uint64_t key, const std::shared_ptr& bool DecodeProgramsCurrent(const DrawDecode& decode, const ShaderRegistry&) { return std::all_of(decode.programs.begin(), decode.programs.end(), [&](const DrawProgram& program) { if (!program.snapshot->header.empty()) return true; - return ReadRawShader(program.snapshot->codeAddress + program.codeOffset * sizeof(std::uint32_t)) == program.snapshot && - (program.tailContinuation == nullptr || ReadRawShader(program.tailContinuation->codeAddress) == program.tailContinuation); + return ReadRawShader(program.snapshot->codeAddress + program.codeOffset * sizeof(std::uint32_t), program.snapshot) == program.snapshot && + (program.tailContinuation == nullptr || ReadRawShader(program.tailContinuation->codeAddress, program.tailContinuation) == program.tailContinuation); }); } diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp index 1065f7921f56..e3a01092be90 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp @@ -76,7 +76,7 @@ std::shared_ptr PrepareShaderWithDiagnosti } } -std::shared_ptr ReadRawShader(std::uint64_t address) { +std::shared_ptr ReadRawShader(std::uint64_t address, const std::shared_ptr& retained) { const auto drain = [](std::uint64_t first, std::size_t bytes) { if (!DrawPipeline::Active()) return; auto& pipeline = DrawPipeline::Queue0(); @@ -86,8 +86,8 @@ std::shared_ptr ReadRawShader(std::uint64_t address) { static std::mutex cacheMutex; static std::list> cache; static std::size_t cacheBytes = 0; - std::shared_ptr cached; - { + std::shared_ptr cached = retained != nullptr && retained->codeAddress == address && retained->header.empty() && !retained->code.empty() ? retained : nullptr; + if (cached == nullptr) { std::lock_guard lock(cacheMutex); const auto found = std::find_if(cache.begin(), cache.end(), [address](const auto& entry) { return entry->codeAddress == address; }); if (found != cache.end()) cached = *found; From 86a3a835eaf3293d0c05d7a30bc4d7fcaa1674d5 Mon Sep 17 00:00:00 2001 From: Romans Pokrovskis Date: Fri, 23 Oct 2026 15:39:26 +0300 Subject: [PATCH 3/3] test(agc): require retained snapshot eviction with larger caches --- .../tests/execution/PreparedUnregisteredGraphics.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/PreparedUnregisteredGraphics.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/PreparedUnregisteredGraphics.cpp index 0ee091c39980..f8c2e9481115 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/PreparedUnregisteredGraphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/PreparedUnregisteredGraphics.cpp @@ -33,7 +33,7 @@ void CheckRetainedDecode() { using namespace AgcDriver::DriverDetail; alignas(256) static std::array code{0xbf810000u}; alignas(256) static std::array tail{0xbf810000u}; - alignas(256) static std::array, 80> others{}; + alignas(256) static std::array, 4097> others{}; const auto address = reinterpret_cast(code.data()); DrawProgram program{}; program.snapshot = ReadRawShader(address); @@ -47,6 +47,7 @@ void CheckRetainedDecode() { static_cast(ReadRawShader(reinterpret_cast(other.data()))); } const auto reread = ReadRawShader(address); + Require(reread != decode.programs.front().snapshot, "raw snapshot churn did not evict the retained snapshot"); Require(reread->code == decode.programs.front().snapshot->code, "raw snapshot churn changed the shader contents"); std::cout << "retained raw snapshot evicted=" << (reread != decode.programs.front().snapshot) << '\n'; Require(DecodeProgramsCurrent(decode, registry), "an unchanged retained raw draw decode was invalidated by snapshot cache eviction");