diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index 25e74dc606..6f3e73062e 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -1117,12 +1117,14 @@ if(BUILD_TESTING) add_test(NAME agc_resident_present COMMAND agc_driver_recorder_tests --resident-present-only) set_tests_properties(agc_resident_present PROPERTIES SKIP_RETURN_CODE 77) add_test(NAME agc_queued_write_groups COMMAND agc_driver_recorder_tests --queued-write-groups-only) + add_test(NAME agc_tiled_resolve COMMAND agc_driver_recorder_tests --tiled-resolve-only) + set_tests_properties(agc_tiled_resolve PROPERTIES SKIP_RETURN_CODE 77) add_test(NAME agc_slice_dirty COMMAND agc_driver_recorder_tests --slice-dirty-only) set_tests_properties(agc_slice_dirty PROPERTIES SKIP_RETURN_CODE 77) add_test(NAME agc_metadata_pass COMMAND agc_driver_recorder_tests --metadata-pass-only) set_tests_properties(agc_metadata_pass PROPERTIES SKIP_RETURN_CODE 77) if(WIN32) - set_tests_properties(agc_recorder_read_list agc_host_import_unmap agc_parked_import_unmap agc_host_import_retirement agc_sparse_run_import agc_sparse_run_import_copied agc_unit_shadow agc_descriptor_pool_recycling agc_draw_snapshot_cap agc_draw_snapshot_epoch agc_multisample_guest_access agc_draw_inputs_in_place agc_resident_present agc_slice_dirty agc_queued_write_groups agc_metadata_pass PROPERTIES ENVIRONMENT_MODIFICATION + set_tests_properties(agc_recorder_read_list agc_host_import_unmap agc_parked_import_unmap agc_host_import_retirement agc_sparse_run_import agc_sparse_run_import_copied agc_unit_shadow agc_descriptor_pool_recycling agc_draw_snapshot_cap agc_draw_snapshot_epoch agc_multisample_guest_access agc_draw_inputs_in_place agc_resident_present agc_slice_dirty agc_queued_write_groups agc_metadata_pass agc_tiled_resolve PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") add_test(NAME agc_host_import_second_mapping COMMAND agc_driver_recorder_tests --host-import-second-mapping-only) set_tests_properties(agc_host_import_second_mapping PROPERTIES SKIP_RETURN_CODE 77 ENVIRONMENT_MODIFICATION diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp index a4a0189916..f38a09b934 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp @@ -2671,7 +2671,7 @@ void RunColorMetadataPass(const Context& context, const ColorMetadataPass& pass) if (pass.mode == ColorMetadataPass::Mode::Resolve) { Require(pass.source.has_value() && pass.targets.size() == 1, "CB resolve without its source and destination"); const auto& destination = pass.targets.front(); - Require(destination.tileMode == ColorTileMode::RenderTarget && context.detiler != nullptr, "CB resolve into a target without a resident image is not implemented"); + Require(destination.tileMode != ColorTileMode::Linear && context.detiler != nullptr, "CB resolve into a target without a resident image is not implemented"); const auto resident = CachedStorageSurface(context, SurfaceForTarget(destination)); Require(resident->Attachable(), "storage format cannot be a color attachment"); ResolveMultisampleTarget(context, *pass.source, *resident, resident->AttachmentView(destination.format, destination.mip)); diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index ef6223d78a..e799009234 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -2610,6 +2610,16 @@ void multisampleTests() { for (std::uint32_t pixel = 0; pixel < 4; ++pixel) queue.context[0x2fe + pixel * 4u] = 0x622ae6aeu; expectFailure([&] { DecodeColorMetadataPass(queue); }, "differs from the rasterizer's"); queue = rasterizedResolve; + std::vector resolvedBlock(2u * 65536u); + const auto resolvedAddress = (reinterpret_cast(resolvedBlock.data()) + 65535u) / 65536u * 65536u; + for (const auto mode : {AgcDriver::Graphics::ColorTileMode::Standard4KB, AgcDriver::Graphics::ColorTileMode::Standard64KB, AgcDriver::Graphics::ColorTileMode::RenderTarget}) { + auto tiled = queue; + tiled.context[0x327] = static_cast(resolvedAddress >> 8u); + tiled.context[0x391] = static_cast(resolvedAddress >> 40u); + tiled.context[0x3b9] = (tiled.context[0x3b9] & ~(0x1fu << 14u)) | (static_cast(mode) << 14u); + const auto tiledPass = DecodeColorMetadataPass(tiled); + Require(tiledPass.has_value() && tiledPass->mode == ColorMetadataPass::Mode::Resolve && tiledPass->targets.size() == 1 && tiledPass->targets[0].tileMode == mode && tiledPass->targets[0].samples == 1, "a CB resolve into a tile mode " + std::to_string(static_cast(mode)) + " destination did not decode"); + } queue.context[0x32b] = queue.context[0x31c] ^ (1u << 8u); expectFailure([&] { DecodeColorMetadataPass(queue); }, "different formats or extents"); queue = rasterizedResolve; diff --git a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp index 251be75a8e..19559de8cb 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp @@ -6435,6 +6435,103 @@ void fmaskDecompressMsaaTests(const Device& device, Recorder& recorder) { std::cout << "FMASK decompress MSAA clear and resolve tests passed\n"; } +void tiledResolveTests(const Device& device, Recorder& recorder) { + namespace GuestMemory = AgcDriver::GuestMemory; + auto context = device.GetContext(); + if ((context.limits.framebufferColorSampleCounts & VK_SAMPLE_COUNT_4_BIT) == 0 || !device.Graphics()) { + std::cout << "4x color targets or graphics unavailable: CB resolves into tiled targets not tested\n"; + return; + } + context.hostImportAlignment = 0; + TextureDetiler detiler(context); + context.detiler = &detiler; + constexpr std::uint32_t side = 128; + constexpr std::size_t targetBytes = side * side * 4u; + constexpr std::size_t sourceBytes = targetBytes * 4u; + constexpr std::size_t bytes = targetBytes + sourceBytes + 65536u; +#ifdef _WIN32 + void* block = VirtualAlloc(nullptr, bytes, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); +#else + void* block = std::aligned_alloc(65536, bytes); +#endif + Require(block != nullptr, "cannot allocate the tiled resolve block"); + const auto address = reinterpret_cast(block); + { + GuestAllocations::Mutation mutation; + mutation.Add(block, bytes, true, true, true); + } + struct Release { + const Context& context; + void* block; + ~Release() { + ClearMultisampleTargets(context.device); + ClearCachedTextures(context.device); + { + GuestAllocations::Mutation mutation; + mutation.Remove(block); + } +#ifdef _WIN32 + VirtualFree(block, 0, MEM_RELEASE); +#else + std::free(block); +#endif + } + } release{context, block}; + ColorTarget source{}; + source.address = address + targetBytes; + source.surfaceAddress = source.address; + source.extent = {side, side}; + source.surfaceExtent = source.extent; + source.format = VK_FORMAT_R8G8B8A8_UNORM; + source.bytes = targetBytes; + source.componentMapping = 0xe4u; + source.tileMode = ColorTileMode::RenderTarget; + source.samples = 4; + source.cmaskAddress = address + targetBytes + sourceBytes; + source.cmaskBytes = 4096; + source.cmaskFastClear = true; + const std::array, 3> modes{{{ColorTileMode::Standard4KB, 0x80402010u}, {ColorTileMode::Standard64KB, 0x10204080u}, {ColorTileMode::RenderTarget, 0x40801020u}}}; + for (const auto& [mode, clear] : modes) { + const auto name = std::to_string(static_cast(mode)); + std::memset(block, 0x55, targetBytes); + std::memset(static_cast(block) + targetBytes + sourceBytes, 0, 4096); + ColorTarget destination{}; + destination.address = address; + destination.surfaceAddress = address; + destination.extent = {side, side}; + destination.surfaceExtent = destination.extent; + destination.format = VK_FORMAT_R8G8B8A8_UNORM; + destination.bytes = targetBytes; + destination.componentMapping = 0xe4u; + destination.tileMode = mode; + source.clearWords = {clear, 0}; + RunColorMetadataPass(context, {ColorMetadataPass::Mode::Resolve, {destination}, source}); + std::vector resolved(targetBytes / 4); + GuestMemory::Read(address, std::as_writable_bytes(std::span(resolved))); + const auto texels = std::ranges::count(resolved, clear); + Require(texels == static_cast(resolved.size()), "a CB resolve into a tile mode " + name + " target left " + std::to_string(resolved.size() - static_cast(texels)) + " texels without the resolved color"); + recorder.Sync(); + ClearMultisampleTargets(context.device); + ClearCachedTextures(context.device); + } + { + ColorTarget destination{}; + destination.address = address; + destination.extent = {side, side}; + destination.format = VK_FORMAT_R8G8B8A8_UNORM; + destination.bytes = targetBytes; + destination.tileMode = ColorTileMode::Linear; + std::string message; + try { + RunColorMetadataPass(context, {ColorMetadataPass::Mode::Resolve, {destination}, source}); + } catch (const std::exception& error) { + message = error.what(); + } + Require(message.find("without a resident image is not implemented") != std::string::npos, "a CB resolve into a linear target did not throw: " + message); + } + recorder.Sync(); +} + void depthSurfaceProofTests(const Device& device, Recorder& recorder) { const auto& base = device.GetContext(); if (!device.NullDescriptors()) { @@ -7879,6 +7976,15 @@ int main(int argc, char** argv) { storageUploadLifetimeTests(device, recorder, std::string_view(argv[1]) == "--storage-upload-failure-only"); return 0; } + if (argc == 2 && std::string_view(argv[1]) == "--tiled-resolve-only") { + if ((device.GetContext().limits.framebufferColorSampleCounts & VK_SAMPLE_COUNT_4_BIT) == 0 || !device.Graphics()) { + std::cout << "skipped, the device lacks 4x color targets or a graphics queue\n"; + return 77; + } + tiledResolveTests(device, recorder); + std::cout << "CB resolves into tiled targets tests passed\n"; + return 0; + } if (argc == 2 && std::string_view(argv[1]) == "--cube-only") { singleCubeTests(device, recorder); std::cout << "Single cube snapshot and storage sampling tests passed\n"; @@ -8044,6 +8150,7 @@ int main(int argc, char** argv) { readWrittenStagingTests(device, recorder); depthSurfaceProofTests(device, recorder); multisampleGuestAccessTests(device, recorder); + tiledResolveTests(device, recorder); fmaskDecompressMsaaTests(device, recorder); misalignedSnapshotTests(device, recorder); misalignedRegionTests(device, recorder);