diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index f38dfcc8a5..61160e3083 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -2377,6 +2377,9 @@ if(BUILD_TESTING) add_test(NAME agc_driver_two_sample_dithered_alpha_to_coverage COMMAND agc_driver_alpha_to_coverage_tests --dithered2) set(multisampledDccClearTests) foreach(samples IN ITEMS 2 4 8) + set(testName "agc_driver_msaa_dcc_srgb_clear_${samples}") + add_test(NAME ${testName} COMMAND agc_driver_alpha_to_coverage_tests --dcc-srgb-clear ${samples}) + list(APPEND multisampledDccClearTests ${testName}) set(testName "agc_driver_msaa_dcc_clear_${samples}") add_test(NAME ${testName} COMMAND agc_driver_alpha_to_coverage_tests --dcc-fast-clear ${samples}) list(APPEND multisampledDccClearTests ${testName}) diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index 79f5d7365a..9fe51b3437 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -4320,6 +4320,31 @@ void multisampleDccStateTests() { target.cmaskFastClear = true; expectFailure([&] { static_cast(AgcDriver::Graphics::MultisampledDccClear(target, dcc, cmask)); }, "multisampled DCC CMASK compression or register clear"); } + for (const auto samples : {2u, 4u, 8u}) { + for (const auto format : {VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_B8G8R8A8_SRGB}) { + for (const auto key : {0x00u, 0x40u, 0x80u, 0xc0u}) { + for (const auto alphaOnMsb : {false, true}) { + AgcDriver::Graphics::ColorTarget target{}; + target.extent = {64, 64}; + target.format = format; + target.tileMode = AgcDriver::Graphics::ColorTileMode::RenderTarget; + target.elementBytes = 4; + target.samples = samples; + target.dccAddress = 0x200000u; + target.dccPipeAligned = true; + target.dccAlphaOnMsb = alphaOnMsb; + const std::vector dcc(AgcDriver::Graphics::MultisampledDccBytes(target), static_cast(key)); + const auto clear = AgcDriver::Graphics::MultisampledDccClear(target, dcc, {}); + const auto alpha = alphaOnMsb ? 3u : format == VK_FORMAT_B8G8R8A8_SRGB ? 2u : 0u; + for (std::uint32_t channel = 0; channel < 4; ++channel) { + const bool one = channel == alpha ? key == 0x40u || key == 0xc0u : key == 0x80u || key == 0xc0u; + Require(clear.float32[channel] == static_cast(one), "an sRGB multisampled DCC endpoint did not produce its exact linear clear value"); + } + } + } + } + } + } void multisampledDepthTests() { diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/AlphaToCoverage.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/AlphaToCoverage.cpp index 591732ee62..947428e83d 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/AlphaToCoverage.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/AlphaToCoverage.cpp @@ -522,6 +522,64 @@ constexpr std::array TwoSampleDitherCases{{ {0x00007e00u, 4u, {0x00u, 0x00u, 0x00u, 0x00u}}, }}; +void DccSrgbClearTests(AgcDriver::VulkanDevice& device, std::uint32_t samples) { + const ColorTargetLayout layout(Width, Height, ColorTileMode::RenderTarget, 4); + const ColorTargetLayout allocation(Width, Height, ColorTileMode::RenderTarget, 8); + std::vector> blocks; + struct ClearTargets { + AgcDriver::VulkanDevice& device; + ~ClearTargets() { + std::lock_guard lock(AgcDriver::GuestMemory::GpuMutex()); + device.SubmitRecorded(true); + device.WaitIdle(); + AgcDriver::Graphics::ClearMultisampleTargets(device.Device()); + AgcDriver::Graphics::ClearCachedTextures(device.Device()); + } + } clearTargets{device}; + const auto targetFor = [&](const Block& block, std::uint32_t count, VkFormat format) { + auto target = DecodeTarget(block, count); + target.format = format; + target.elementBytes = 4; + target.bytes = layout.Bytes(); + return target; + }; + for (const auto format : {VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_B8G8R8A8_SRGB}) { + for (const auto key : {0x00u, 0x40u, 0x80u, 0xc0u}) { + for (const auto alphaOnMsb : {false, true}) { + blocks.push_back(std::make_unique(allocation.Bytes() * samples)); + auto& source = *blocks.back(); + std::memset(source.data, 0x5a, source.bytes); + blocks.push_back(std::make_unique(BlockBytes)); + auto& metadata = *blocks.back(); + std::memset(metadata.data, key, metadata.bytes); + auto target = targetFor(source, samples, format); + target.dccAddress = metadata.Address(); + target.dccPipeAligned = true; + target.dccAlphaOnMsb = alphaOnMsb; + Draw(device, target, false, Float32Export, Program(Color{0.25f, 0.5f, 0.75f, 0.125f}, Float32Export, 0u), false, true); + blocks.push_back(std::make_unique(allocation.Bytes())); + auto& resolved = *blocks.back(); + std::memset(resolved.data, 0xa5, resolved.bytes); + AgcDriver::Graphics::ColorMetadataPass pass{}; + pass.mode = AgcDriver::Graphics::ColorMetadataPass::Mode::Resolve; + pass.source = target; + pass.targets = {targetFor(resolved, 1u, format)}; + device.ColorMetadataPass(pass); + const auto stored = ReadBack(device, resolved); + const auto bytes = std::as_bytes(std::span(stored)); + std::array expected{}; + const auto keys = key == 0x00u ? AgcDriver::Graphics::DccKeys::Clear0000 : key == 0x40u ? AgcDriver::Graphics::DccKeys::Clear0001 : key == 0x80u ? AgcDriver::Graphics::DccKeys::Clear1110 : AgcDriver::Graphics::DccKeys::Clear1111; + Require(AgcDriver::Graphics::FillDccClear(format, keys, alphaOnMsb, expected), "the sRGB DCC expected texel was not encoded"); + for (std::uint32_t y = 0; y < Height; ++y) { + for (std::uint32_t x = 0; x < Width; ++x) { + for (std::uint32_t channel = 0; channel < 4; ++channel) Require(bytes[layout.Offset(x, y) + channel] == expected[channel], "an sRGB DCC import or resolve changed an undrawn sample endpoint"); + } + } + } + } + } +} + void DccFastClearTests(AgcDriver::VulkanDevice& device, std::uint32_t selectedSamples) { const ColorTargetLayout layout(Width, Height, ColorTileMode::RenderTarget, 8); const auto expectRange = [](std::string_view message, const char* plane, std::uint64_t address, std::size_t bytes, std::uint32_t samples) { @@ -1147,6 +1205,13 @@ int main(int argc, char** argv) { try { const auto device = OpenVulkanTestDevice(); if (!device) return VulkanTestSkipped; + if (argc == 3 && std::string_view(argv[1]) == "--dcc-srgb-clear") { + const auto samples = std::string_view(argv[2]) == "2" ? 2u : std::string_view(argv[2]) == "4" ? 4u : std::string_view(argv[2]) == "8" ? 8u : 0u; + Require(samples != 0, "unknown sRGB DCC test sample count"); + DccSrgbClearTests(*device, samples); + std::cout << "Multisampled sRGB DCC clear tests passed\n"; + return 0; + } if ((argc == 2 || argc == 3) && std::string_view(argv[1]) == "--dcc-fast-clear") { const auto samples = argc == 2 ? 0u : std::string_view(argv[2]) == "2" ? 2u : std::string_view(argv[2]) == "4" ? 4u : std::string_view(argv[2]) == "8" ? 8u : 0u; Require(argc == 2 || samples != 0, "unknown DCC test sample count");