diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp index 73bbd4d9e2..e21454efab 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp @@ -2626,12 +2626,10 @@ VulkanDevice::CopyOutcome VulkanDevice::CopyBuffer(std::uint64_t destination, st } outcome.syncMs = std::chrono::duration(std::chrono::steady_clock::now() - syncStart).count(); } - // A whole surface copied into another: a device copy between their images, whose results reach - // the destination's memory through the image's deferred write-back (nothing crosses to host - // memory for the copy itself). if (AliasCopy(context, destination, source, bytes)) { noteWriter({}, 0); recorder.NotePendingWrite(destination, bytes); + Graphics::StorageTexture::FlushPending(destination, bytes, nullptr, "buffer copy destination", Graphics::PublishScope::Whole); outcome.path = 3; return outcome; } diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/AliasCopyLabel.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/AliasCopyLabel.cpp new file mode 100644 index 0000000000..6db8ed0908 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/tests/execution/AliasCopyLabel.cpp @@ -0,0 +1,148 @@ +#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 +#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 SurfaceBytes = 0x40000; +constexpr std::size_t LabelOffset = 2 * SurfaceBytes; +constexpr std::size_t BlockBytes = LabelOffset + 0x10000; +constexpr std::uint32_t LabelValue = 0x600dcafeu; + +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::uint8_t* texels) { + 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, + 0u, + 0u, + 0u, + }; +} + +void Store(AgcDriver::VulkanDevice& device, const std::uint8_t* texels) { + std::vector userData(16, 0u); + const auto texture = TextureDescriptor(texels); + 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(); +} + +} + +int main() { + try { + const auto device = OpenVulkanTestDevice(); + if (!device) return VulkanTestSkipped; + std::vector storage(BlockBytes + 0x10000u); + auto* block = reinterpret_cast((reinterpret_cast(storage.data()) + 0xffffu) & ~std::uintptr_t{0xffffu}); + auto* source = block; + auto* destination = block + SurfaceBytes; + auto* label = reinterpret_cast(block + LabelOffset); + for (std::size_t at = 0; at < SurfaceBytes; ++at) source[at] = static_cast(0x31u + at % 67u); + std::memset(destination, 0xa5, SurfaceBytes); + *label = 0; + { + GuestAllocations::Mutation mutation; + mutation.Add(block, BlockBytes, true, true, true); + } + const auto surfaceBytes = AgcDriver::Graphics::DescribeSurface(AgcDriver::Graphics::DecodeTextureResource(TextureDescriptor(source))).guestBytes; + Require(surfaceBytes == SurfaceBytes, "the 256x256 32-bit surface must cover exactly its 256 KiB block"); + + Store(*device, source); + Require(StorageTexture::FindPending(AddressOf(source), SurfaceBytes) != nullptr, "the compute store left no pending image over the source"); + + { + std::lock_guard lock(AgcDriver::GuestMemory::GpuMutex()); + const auto outcome = device->CopyBuffer(AddressOf(destination), AddressOf(source), SurfaceBytes, 0, std::numeric_limits::max(), 0, 0, 0, [](std::span, std::uint64_t) {}); + if (outcome.path != 3) { + std::printf("skipped, the copy took path %d instead of the image copy\n", outcome.path); + return VulkanTestSkipped; + } + const std::uint32_t value = LabelValue; + const int reason = device->WriteLabelOnGpu(AddressOf(const_cast(label)), std::as_bytes(std::span(&value, 1)), 1, 0); + if (reason != 0) { + std::printf("skipped, the label was not recorded on the GPU (reason %d)\n", reason); + return VulkanTestSkipped; + } + device->SubmitRecorded(true); + } + const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(10); + while (*label != LabelValue) { + Require(std::chrono::steady_clock::now() < deadline, "the label after the image copy never landed"); + std::this_thread::yield(); + } + std::vector seen(destination, destination + SurfaceBytes); + + std::vector expected(SurfaceBytes); + AgcDriver::GuestMemory::Read(AddressOf(source), expected); + std::size_t differences = 0; + for (std::size_t at = 0; at < SurfaceBytes; ++at) differences += static_cast(seen[at]) != expected[at]; + Require(differences == 0, "a CPU read after the label that followed the image copy found " + std::to_string(differences) + " of " + std::to_string(SurfaceBytes) + " destination bytes still unwritten"); + + { + std::lock_guard lock(AgcDriver::GuestMemory::GpuMutex()); + AgcDriver::Graphics::FlushCachedTextures(device->Device()); + } + device->WaitIdle(); + { + GuestAllocations::Mutation mutation; + mutation.Remove(block); + } + std::puts("alias copy label tests passed"); + return 0; + } catch (const std::exception& error) { + std::cerr << error.what() << '\n'; + return 1; + } +}