From d906ee2f6d471d4fa4b521573dd1ef08a5e7e37f Mon Sep 17 00:00:00 2001 From: Kikoarl <7354010+Kikoarl@users.noreply.github.com> Date: Mon, 12 Oct 2026 02:06:41 +0200 Subject: [PATCH] test(agc): add unit test for copied writer indirect overlap disambiguation --- .../prx/libSceAgcDriver/tests/Graphics.cpp | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index 0103b5aec..a554c8743 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -20,6 +20,7 @@ #include "CacheKey.hpp" #include "Optimization/ResourceProgram.hpp" #include "BdaAbi.hpp" +#include "prx/libc/include/GuestAllocations.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include @@ -3238,6 +3239,99 @@ void barycentricEmulationTests() { expectFailure([&] { static_cast(BuildBarycentricGeometryShader(vertex, fragment, target, limits)); }, "not exported by the vertex shader"); } +void copiedWriterIndirectOverlapTests() { + using namespace ShaderRecompiler; + using namespace AgcDriver::Graphics; + + constexpr std::size_t half = 65536; + + void* directBlock = ::operator new(half, std::align_val_t{half}); + std::memset(directBlock, 0xaa, half); + const auto directAddress = reinterpret_cast(directBlock); + + void* copiedBlock = ::operator new(half, std::align_val_t{half}); + std::memset(copiedBlock, 0xbb, half); + const auto copiedAddress = reinterpret_cast(copiedBlock); + + { + auto* mutation = GuestAllocations::GuestAllocationsBegin_nid_postfix(); + GuestAllocations::GuestAllocationsAdd_nid_postfix(mutation, directBlock, half, true, true, true); + GuestAllocations::GuestAllocationsEnd_nid_postfix(mutation); + } + + mock = MockVulkan{}; + auto context = mockContext(); + context.hostImportAlignment = half; + context.limits.minStorageBufferOffsetAlignment = 16; + context.limits.maxStorageBufferRange = 1u << 27; + + { + GuestBufferMemory memory(context); + memory.AcquireRegistered(); + Require(HostImportCovers(context, directAddress, half), "direct registered range must be covered by host import"); + Require(!HostImportCovers(context, copiedAddress, half), "copied unregistered range must NOT be covered by host import"); + + memory.AddWritable(copiedAddress, 1024); + memory.AddWritable(directAddress, 1024); + memory.Upload(false); + + Require(memory.HasCopiedWrites(), "writer with one copied region must report HasCopiedWrites() == true"); + + Require(memory.WritesOverlap(directAddress, 16), "WritesOverlap must return true for direct written region"); + Require(memory.WritesOverlap(copiedAddress, 16), "WritesOverlap must return true for copied written region"); + Require(!memory.WritesOverlap(directAddress + 2048, 16), "WritesOverlap must return false for unwritten range"); + + Require(!memory.CopiedWritesOverlap(directAddress, 16), "CopiedWritesOverlap must return false for direct written region"); + Require(memory.CopiedWritesOverlap(copiedAddress, 16), "CopiedWritesOverlap must return true for copied written region"); + Require(!memory.CopiedWritesOverlap(directAddress + 2048, 16), "CopiedWritesOverlap must return false for unwritten range"); + + memory.WriteBack(); + } + + { + RecompileResult shader; + shader.runtimeAbiVersion = RuntimeAbi::Version; + auto copiedBinding = makeBinding(Role::GuestBuffers, 0, 1, vsharp(copiedBlock, 1024)); + copiedBinding.bufferWritten = {true}; + auto directBinding = makeBinding(Role::GuestBuffers, 1, 1, vsharp(directBlock, 1024)); + directBinding.bufferWritten = {true}; + shader.bindings = {copiedBinding, directBinding}; + CompiledShader compiled{ShaderStage::Compute, &shader, 0}; + auto writer = std::make_shared(context, compiled); + + Require(writer->HasCopiedWrites(), "ShaderResources with copied region must report HasCopiedWrites() == true"); + Require(writer->WritesOverlap(directAddress, 16), "ShaderResources must report WritesOverlap for direct region"); + Require(!writer->CopiedWritesOverlap(directAddress, 16), "ShaderResources must NOT report CopiedWritesOverlap for direct region"); + Require(writer->CopiedWritesOverlap(copiedAddress, 16), "ShaderResources must report CopiedWritesOverlap for copied region"); + + auto copiedWriters = DrawCopiedWriters(); + copiedWriters->push_back(writer); + + const auto overlapsMain = [&](const auto& w) { return w->WritesOverlap(directAddress, 16); }; + const auto overlapsPR = [&](const auto& w) { return w->CopiedWritesOverlap(directAddress, 16); }; + + const bool mainOverlaps = std::any_of(copiedWriters->begin(), copiedWriters->end(), overlapsMain); + const bool prOverlaps = std::any_of(copiedWriters->begin(), copiedWriters->end(), overlapsPR); + + Require(mainOverlaps, "main's check (WritesOverlap) must flag an overlap over direct arguments"); + Require(!prOverlaps, "PR's check (CopiedWritesOverlap) must disambiguate and NOT flag an overlap over direct arguments"); + + const auto copiedOverlapsPR = [&](const auto& w) { return w->CopiedWritesOverlap(copiedAddress, 16); }; + Require(std::any_of(copiedWriters->begin(), copiedWriters->end(), copiedOverlapsPR), "PR's check must flag an overlap over copied arguments"); + + copiedWriters->erase(std::remove(copiedWriters->begin(), copiedWriters->end(), writer), copiedWriters->end()); + writer->WriteBack(); + } + + { + auto* mutation = GuestAllocations::GuestAllocationsBegin_nid_postfix(); + GuestAllocations::GuestAllocationsRemove_nid_postfix(mutation, directBlock); + GuestAllocations::GuestAllocationsEnd_nid_postfix(mutation); + } + ::operator delete(directBlock, std::align_val_t{half}); + ::operator delete(copiedBlock, std::align_val_t{half}); +} + void rectListTests() { using namespace ShaderRecompiler; using namespace AgcDriver::Graphics; @@ -4642,6 +4736,7 @@ int main(int argc, char** argv) { rectListTests(); floatControlsModeTests(); barycentricEmulationTests(); + copiedWriterIndirectOverlapTests(); mock = MockVulkan{}; auto bdaContext = mockContext(); bdaContext.bufferDeviceAddress = true;