diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index 5749f1e6e..efe7d2c9c 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -810,6 +810,12 @@ if(BUILD_TESTING) set_tests_properties(agc_storage_baseline_settled PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_test(NAME agc_storage_written_buffer_order COMMAND agc_driver_recorder_tests --written-buffer-order-only) + set_tests_properties(agc_storage_written_buffer_order PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30) + if(WIN32) + set_tests_properties(agc_storage_written_buffer_order PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test(NAME agc_host_import_unmap COMMAND agc_driver_recorder_tests --host-import-unmap-only) set_tests_properties(agc_host_import_unmap PROPERTIES SKIP_RETURN_CODE 77) add_test(NAME agc_parked_import_unmap COMMAND agc_driver_recorder_tests --parked-import-unmap-only) diff --git a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp index 938b00102..62eaeee54 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp @@ -2482,6 +2482,103 @@ void storageRefreshTests(const Device& device, Recorder& recorder, bool watched) recorder.Sync(); } +bool writtenBufferOrderTests(const Device& device, Recorder& recorder) { + const auto& base = device.GetContext(); + if (base.hostImportAlignment == 0) { + std::cout << "host imports unavailable: written buffer order not tested\n"; + return false; + } + constexpr std::uint32_t side = 256; + constexpr std::size_t bytes = side * side * 4; + void* block = AllocateWatched(bytes, 65536); + if (block == nullptr) { + std::cout << "no write watching: written buffer order not tested\n"; + return false; + } + std::memset(block, 0x55, bytes); + const auto address = reinterpret_cast(block); + { + GuestAllocations::Mutation mutation; + mutation.Add(block, bytes, true, true, true); + } + struct Unregister { + const Context& context; + void* block; + std::uint64_t address; + ~Unregister() { + { + GuestAllocations::Mutation mutation; + mutation.Remove(block); + } + HostImportFor(context, address, bytes); + } + } unregister{base, block, address}; + if (HostImportFor(base, address, bytes) == nullptr) { + std::cout << "host import of the written buffer order block refused: written buffer order not tested\n"; + return false; + } + std::cout << "written buffer order: host imports are " << (AgcDriver::GuestMemory::Watched(address, bytes) ? "watched" : "compared") << "\n"; + TextureDetiler detiler(base); + auto context = base; + context.detiler = &detiler; + GuestTextureResource resource{}; + resource.baseAddress = address; + resource.width = side; + resource.height = side; + resource.mipCount = 1; + resource.tileMode = TextureTileMode::kR64KBX; + resource.dimension = TextureDimension::k2D; + resource.format = 56; + resource.dstSelX = 4; + resource.dstSelY = 5; + resource.dstSelZ = 6; + resource.dstSelW = 7; + Require(DescribeSurface(resource).guestBytes == bytes, "the written buffer order surface has an unexpected size"); + const auto settle = [&] { + recorder.Submit(); + device.WaitQueue(); + recorder.Sync(); + }; + { + auto image = StorageTexture::Create(context, detiler, resource, 0); + const VkImageSubresourceRange range{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + const VkClearColorValue red{{1.0f, 0.0f, 0.0f, 1.0f}}; + { + const auto commands = recorder.Commands(); + recorder.Keep(image); + RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); + context.Function("vkCmdClearColorImage")(commands, image->Image(), VK_IMAGE_LAYOUT_GENERAL, &red, 1, &range); + RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT); + image->MarkDirty(); + } + constexpr std::size_t written = 40800; + GuestBufferMemory memory(context); + memory.AcquireRegistered(); + memory.AddWritable(address, written, false, false); + memory.Upload(true); + std::uint32_t adjustment = 0; + const auto view = memory.Descriptor(address, written, adjustment); + Require(adjustment == 0, "the written buffer view is not aligned to its element"); + { + const auto commands = recorder.Commands(); + RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); + context.Function("vkCmdFillBuffer")(commands, view.buffer, view.offset, written, 0xa5a5a5a5u); + RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT); + } + memory.RecordCopyBacks(recorder); + memory.MarkDirectWrites(); + settle(); + StorageTexture::FlushAllPending("written buffer order test"); + settle(); + std::vector read(bytes); + AgcDriver::GuestMemory::Read(address, read); + Require(std::all_of(read.begin(), read.begin() + written, [](std::byte value) { return value == std::byte{0xa5}; }), "storage results pending under a buffer a later use wrote were stored over the buffer's contents"); + for (std::size_t i = written; i < bytes; ++i) Require(std::to_integer(read[i]) == (i % 4 == 1 || i % 4 == 2 ? 0 : 255), "the storage results outside the written buffer did not reach guest memory"); + } + recorder.Sync(); + return true; +} + void hostImportUnmapTests(const Device& device, Recorder& recorder) { const auto& context = device.GetContext(); if (context.hostImportAlignment == 0) { @@ -5491,6 +5588,11 @@ int main(int argc, char** argv) { std::cout << "Completion label storage and ordering tests passed\n"; return 0; } + if (argc == 2 && std::string_view(argv[1]) == "--written-buffer-order-only") { + if (!writtenBufferOrderTests(device, recorder)) return 77; + std::cout << "written buffer order tests passed\n"; + return 0; + } if (argc == 2 && std::string_view(argv[1]) == "--host-import-unmap-only") { gpu.unlock(); hostImportUnmapTests(device, recorder); @@ -5571,6 +5673,7 @@ int main(int argc, char** argv) { unitShadowTests(device, recorder); storageRefreshTests(device, recorder, false); storageRefreshTests(device, recorder, true); + static_cast(writtenBufferOrderTests(device, recorder)); writeBackPaddingTests(device, recorder, false); writeBackPaddingTests(device, recorder, true); singlePassTests(device, recorder, false);