From 3fba616bb51f675ed87e482fd17c5fa662ee022a Mon Sep 17 00:00:00 2001 From: TideOPL Date: Sat, 10 Oct 2026 01:03:15 +0100 Subject: [PATCH] test(agc): look up a pending surface aliased by a pending mip chain agc_driver_pending_exact_surface writes mip 0 of a 2-mip 256x256 surface and then a 1-mip surface at the same address. The chain's mip 0 lies past the 1-mip surface's bytes, so both images stay pending, the chain first. A lookup of the 1-mip size must return the 1-mip image, and with only the chain pending it must still return the chain. On main the first lookup returns the chain. --- .../tests/execution/PendingExactSurface.cpp | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 core/libs/prx/libSceAgcDriver/tests/execution/PendingExactSurface.cpp diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/PendingExactSurface.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/PendingExactSurface.cpp new file mode 100644 index 000000000..7f8af7065 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/tests/execution/PendingExactSurface.cpp @@ -0,0 +1,130 @@ +#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp" +#include "prx/libc/include/GuestAllocations.hpp" +#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp" +#include "prx/libSceAgcDriver/Graphics/include/GuestTextureResource.hpp" +#include "prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp" +#include "prx/libSceAgcDriver/Graphics/include/Texture.hpp" +#include "prx/libSceAgcDriver/Graphics/include/TextureTiling.hpp" +#include "Recompiler.hpp" +#include "VulkanTestDevice.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using AgcDriver::Graphics::Require; +using AgcDriver::Graphics::StorageTexture; +using ShaderRecompiler::ShaderStage; + +constexpr std::uint32_t Threads = 8; +constexpr std::uint32_t Side = 256; +constexpr std::uint32_t Format32UInt = 20; +constexpr std::uint32_t TileR64KBX = 0x1b; +constexpr std::uint32_t Type2D = 9; +constexpr std::size_t BlockBytes = 0x60000; +constexpr std::uint32_t Untouched = 0xdeadbeefu; + +alignas(256) constexpr std::array StoreCode{ + 0x7e040300, 0x7e060280, 0x7e0202ff, 0x12345678u, 0xf0200108, 0x00020102, 0xbf810000, +}; + +std::uint64_t AddressOf(const void* data) { + return static_cast(reinterpret_cast(data)); +} + +std::array TextureDescriptor(const std::uint32_t* texels, std::uint32_t maxMip) { + const auto address = AddressOf(texels); + return { + static_cast(address >> 8u), + static_cast((address >> 40u) & 0xffu) | (Format32UInt << 20u) | (((Side - 1u) & 3u) << 30u), + ((Side - 1u) >> 2u) | ((Side - 1u) << 14u), + 0xfacu | (TileR64KBX << 20u) | (Type2D << 28u), + 0u, + maxMip << 4u, + 0u, + 0u, + }; +} + +std::uint64_t GuestBytes(const std::uint32_t* texels, std::uint32_t maxMip) { + const auto words = TextureDescriptor(texels, maxMip); + return AgcDriver::Graphics::DescribeSurface(AgcDriver::Graphics::DecodeTextureResource(words)).guestBytes; +} + +void Store(AgcDriver::VulkanDevice& device, const std::uint32_t* texels, std::uint32_t maxMip) { + std::vector userData(16, 0u); + const auto texture = TextureDescriptor(texels, maxMip); + std::copy(texture.begin(), texture.end(), userData.begin() + 8); + const std::span code(StoreCode); + const std::array memory{{{reinterpret_cast(code.data()), std::as_bytes(code)}}}; + const ShaderRecompiler::ShaderComputeStageInfo compute{{Threads, 1, 1}, 0u, {false, false, false}, false, 1}; + ShaderRecompiler::RecompileRequest request{ + {ShaderStage::Compute, reinterpret_cast(code.data()), code, 0, {}}, + {32, 0, userData, compute, std::nullopt, std::nullopt, memory}, + device.Target(), + {0, 0, 0, 128} + }; + request.useCache = false; + const auto result = ShaderRecompiler::Recompile(request); + device.Dispatch(result, 1, 1, 1, {}, reinterpret_cast(code.data())); + device.WaitIdle(); +} + +std::string Describe(const std::shared_ptr& image) { + if (image == nullptr) return "no pending image"; + return "the pending image of " + std::to_string(image->GuestBytes()) + " bytes and " + std::to_string(image->Descriptor().mipCount) + " mips"; +} + +} + +int main() { + try { + const auto device = OpenVulkanTestDevice(); + if (!device) return VulkanTestSkipped; + std::vector storage((BlockBytes + 0x10000u) / 4u); + auto* texels = reinterpret_cast((reinterpret_cast(storage.data()) + 0xffffu) & ~std::uintptr_t{0xffffu}); + std::fill(texels, texels + BlockBytes / 4u, Untouched); + { + GuestAllocations::Mutation mutation; + mutation.Add(texels, BlockBytes, true, true); + } + const auto surface = AddressOf(texels); + const auto chainBytes = GuestBytes(texels, 1u); + const auto singleBytes = GuestBytes(texels, 0u); + Require(singleBytes < chainBytes && chainBytes <= BlockBytes, "the 2-mip chain must cover more guest bytes than the 1-mip surface"); + + Store(*device, texels, 1u); + const auto chain = StorageTexture::FindPending(surface, chainBytes); + Require(chain != nullptr && chain->GuestBytes() == chainBytes, "the write through the 2-mip chain left " + Describe(chain) + " at the surface"); + const auto prefix = StorageTexture::FindPending(surface, singleBytes); + Require(prefix == chain, "a lookup of the chain's first mip found " + Describe(prefix) + " instead of the pending chain"); + + Store(*device, texels, 0u); + Require(StorageTexture::FindPending(surface, chainBytes) == chain, "the write through the 1-mip surface flushed the pending chain"); + const auto single = StorageTexture::FindPending(surface, singleBytes); + Require(single != nullptr && single != chain && single->GuestBytes() == singleBytes, "a lookup of the 1-mip surface found " + Describe(single) + " instead of the pending 1-mip image"); + + { + std::lock_guard lock(AgcDriver::GuestMemory::GpuMutex()); + AgcDriver::Graphics::FlushCachedTextures(device->Device()); + } + device->WaitIdle(); + { + GuestAllocations::Mutation mutation; + mutation.Remove(texels); + } + std::puts("pending exact surface tests passed"); + return 0; + } catch (const std::exception& error) { + std::cerr << error.what() << '\n'; + return 1; + } +}