Merge pull request #6475 from EagleBearerYS/fix/agc-guard-lazy-capture-pages

fix(agc): run the read guard over a shader capture's lazy pages and raw word reads
This commit is contained in:
Adrià authored and GitHub committed 2026-10-19 03:39:11 +02:00
commit 9d4382d268
4 files changed
+36 -3

No files matched your search

@@ -196,6 +196,7 @@ unsigned long long CodeOffset(const void* address);
// calls FlushGpuWrites itself.
void SetFlushHook(void (*hook)(std::uint64_t address, std::size_t bytes));
void SetReadGuard(void (*guard)(std::uint64_t address, std::size_t bytes));
void GuardRead(std::uint64_t address, std::size_t bytes);
void FlushGpuWrites(std::uint64_t address, std::size_t bytes);
// Moves with every ForgetPages call (the libc invalidator: memory unmapped or re-registered): odd
// while the call stores its page states, moved past it after, so a reader that loads an even
@@ -2234,13 +2234,17 @@ void SetReadGuard(void (*guard)(std::uint64_t address, std::size_t bytes)) {
GuestArena::GuestArenaSetHostReadHook_nid_no_patch(&PrepareHostRead);
}
void GuardRead(std::uint64_t address, std::size_t bytes) {
if (const auto guard = readGuard.load(std::memory_order_acquire)) guard(address, bytes);
}
void FlushGpuWrites(std::uint64_t address, std::size_t bytes) {
if (const auto hook = flushHook.load(std::memory_order_acquire)) hook(address, bytes);
}
void Read(std::uint64_t address, std::span<std::byte> destination, std::size_t alignment) {
if (destination.empty()) return;
if (const auto guard = readGuard.load(std::memory_order_acquire)) guard(address, destination.size());
GuardRead(address, destination.size());
FlushGpuWrites(address, destination.size());
const TimedAccess timed(CounterRead, destination.size());
CountReadCaller(__builtin_return_address(0));
@@ -2287,7 +2291,7 @@ void copyCommitted(std::uint64_t address, std::span<std::byte> destination) {
void ReadCommitted(std::uint64_t address, std::span<std::byte> destination) {
if (destination.empty()) return;
if (const auto guard = readGuard.load(std::memory_order_acquire)) guard(address, destination.size());
GuardRead(address, destination.size());
FlushGpuWrites(address, destination.size());
const TimedAccess timed(CounterRead, destination.size());
CountReadCaller(__builtin_return_address(0));
@@ -2296,7 +2300,7 @@ void ReadCommitted(std::uint64_t address, std::span<std::byte> destination) {
void ReadCommittedUnsynced(std::uint64_t address, std::span<std::byte> destination) {
if (destination.empty()) return;
if (const auto guard = readGuard.load(std::memory_order_acquire)) guard(address, destination.size());
GuardRead(address, destination.size());
const TimedAccess timed(CounterRead, destination.size());
CountReadCaller(__builtin_return_address(0));
copyCommitted(address, destination);
@@ -228,6 +228,7 @@ ShaderMemory::Page& ShaderMemory::page(std::uint64_t base) {
return page;
}
const auto started = CaptureProfiled() ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{};
GuestMemory::GuardRead(base, PageBytes);
GuestMemory::FlushGpuWrites(base, PageBytes);
if (CaptureProfiled()) CaptureTotals().pageReadNanoseconds += NanosecondsSince(started);
page.lazy = true;
@@ -289,6 +290,7 @@ bool ShaderMemory::read(void* context, std::uint64_t address, std::uint32_t* val
word = waited;
}
} else if (policy == PendingWrite::Raw || policy == PendingWrite::VerifyRaw) {
GuestMemory::GuardRead(address, sizeof(word));
std::memcpy(&word, reinterpret_cast<const void*>(address), sizeof(word));
++CaptureTotals().wordsRaw;
if (policy == PendingWrite::VerifyRaw) {
@@ -38,6 +38,7 @@
#include "VertexRange_vert_spv.h"
#include "VertexRangeProducer_frag_spv.h"
#include "prx/libSceAgcDriver/Graphics/include/ColorTargetTransfer.hpp"
#include "BdaAbi.hpp"
#include <SDL_loadso.h>
#ifdef _WIN32
#ifndef NOMINMAX
@@ -7037,6 +7038,31 @@ void multisampleGuestAccessTests(const Device& device, Recorder& recorder) {
refused([&] { AgcDriver::GuestMemory::ReadCommitted(address + 256u, texels); }, "a CPU read", "a committed CPU read");
refused([&] { AgcDriver::GuestMemory::ReadCommittedUnsynced(address + 256u, texels); }, "a CPU read", "a committed CPU read without the flush hook");
refused([&] { RequireNoMultisampleTarget(address, 64u, "a DMA copy"); }, "a DMA copy", "a DMA copy");
const auto captureSrt = [](std::uint64_t srt, AgcDriver::ShaderMemory::PendingWriteQuery pendingWrite) {
static constexpr std::array<std::uint32_t, 6> code{0xf4080100u, 0xfa000000u, 0xbf8cc07fu, 0xe0700000u, 0x80010000u, 0xbf810000u};
const std::array<std::uint32_t, 2> userData{static_cast<std::uint32_t>(srt), static_cast<std::uint32_t>(srt >> 32u)};
ShaderRecompiler::RecompileRequest request{};
request.shader = {ShaderRecompiler::ShaderStage::Compute, 0x24000u, code, 0, {}};
request.context.waveSize = 32u;
request.context.userDataBaseRegister = 0;
request.context.userData = userData;
request.context.compute = ShaderRecompiler::ShaderComputeStageInfo{{32u, 1u, 1u}, 0u, {false, false, false}, false, 1u};
request.target.vulkanVersion = 0x00401000u;
request.target.spirvVersion = 0x00010300u;
request.target.subgroupSize = 32;
request.target.bdaAbiVersion = ShaderRecompiler::BdaAbi::Version;
request.layout.pushConstantSizeBytes = 128;
AgcDriver::ShaderMemory memory({}, pendingWrite);
static_cast<void>(memory.Capture(request));
};
refused([&] { captureSrt(address + 256u, nullptr); }, "a CPU read", "a shader capture");
captureSrt(address + 4u * 65536u + 256u, nullptr);
refused([&] { captureSrt(address + 256u, nullptr); }, "a CPU read", "a shader capture on a pooled page");
const auto raw = [](std::uint64_t, std::size_t, std::span<std::byte>) { return AgcDriver::ShaderMemory::PendingWrite::Raw; };
const auto synced = [](std::uint64_t, std::size_t, std::span<std::byte>) { return AgcDriver::ShaderMemory::PendingWrite::Sync; };
refused([&] { captureSrt(address + 256u, raw); }, "a CPU read", "a shader capture's raw word read");
refused([&] { captureSrt(address + 256u, synced); }, "a CPU read", "a shader capture's synced word read");
captureSrt(address + 4u * 65536u + 256u, raw);
ShaderRecompiler::RecompileResult program;
ShaderRecompiler::DescriptorBinding binding;
binding.kind = ShaderRecompiler::DescriptorKind::SampledImage;