From 4b674a72ba8b1f3632fadb7e68dcabaf26d6780c Mon Sep 17 00:00:00 2001 From: Romans Pokrovskis Date: Sat, 17 Oct 2026 01:23:02 +0300 Subject: [PATCH] fix(agc): identify invalidated multisample DCC ranges --- .../Graphics/src/MultisampleTarget.cpp | 14 +++++++++++--- .../tests/execution/AlphaToCoverage.cpp | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp index 4ca7a2255211..67c71f48d84e 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp @@ -333,6 +333,14 @@ class MultisampleTarget { return clear; } + void requireDccRange(bool valid, const GuestMemory::UnchangedQuery& range, const char* reason) const { + if (valid) return; + const auto* plane = range.address == target.address ? "color" : range.address == target.dccAddress ? "DCC" : "CMASK"; + char text[384]; + std::snprintf(text, sizeof(text), "%s (%s range 0x%llx+0x%zx, %u samples)", reason, plane, static_cast(range.address), range.bytes, target.samples); + Require(false, text); + } + void checkDccCurrent(const ColorTarget& use) const { if (dccRanges.empty()) { Require(use.dccAddress == 0, "adding DCC to a resident multisampled color target is not implemented"); @@ -342,10 +350,10 @@ class MultisampleTarget { static_cast(MultisampledDccBytes(use)); Require(use.address == target.address && use.dccAddress == target.dccAddress && use.dccControl == target.dccControl && use.dccAlphaOnMsb == target.dccAlphaOnMsb && use.dccPipeAligned == target.dccPipeAligned && use.cmaskAddress == target.cmaskAddress && use.cmaskBytes == target.cmaskBytes && use.tileMode == target.tileMode && use.elementBytes == target.elementBytes && use.pipeBankXor == target.pipeBankXor, "multisampled DCC target layout or metadata changed"); for (const auto& range : dccRanges) { - Require(GuestMemory::Watched(range.address, range.bytes), "multisampled DCC target reuse requires watched guest memory"); + requireDccRange(GuestMemory::Watched(range.address, range.bytes), range, "multisampled DCC target reuse requires watched guest memory"); GuestMemory::CollectWritesUncached(range.address, range.bytes); - Require(GuestMemory::UnchangedSince(range.address, range.bytes, range.generation) || !GuestMemory::StoredOver(range.address, range.bytes, range.generation), "writes to a resident multisampled DCC target or its metadata are not implemented"); - if (auto* recorder = Recorder::Active(); recorder != nullptr) Require(!recorder->PendingWriteOverlaps(range.address, range.bytes), "pending writes to a resident multisampled DCC target or its metadata are not implemented"); + requireDccRange(GuestMemory::UnchangedSince(range.address, range.bytes, range.generation) || !GuestMemory::StoredOver(range.address, range.bytes, range.generation), range, "writes to a resident multisampled DCC target or its metadata are not implemented"); + if (auto* recorder = Recorder::Active(); recorder != nullptr) requireDccRange(!recorder->PendingWriteOverlaps(range.address, range.bytes), range, "pending writes to a resident multisampled DCC target or its metadata are not implemented"); } } diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/AlphaToCoverage.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/AlphaToCoverage.cpp index 4f6288141b40..09713f0f5091 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/AlphaToCoverage.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/AlphaToCoverage.cpp @@ -449,6 +449,11 @@ void DitheredCoverage(AgcDriver::VulkanDevice& device) { 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) { + char detail[160]; + std::snprintf(detail, sizeof(detail), "(%s range 0x%llx+0x%zx, %u samples)", plane, static_cast(address), bytes, samples); + Require(message.find(detail) != std::string_view::npos, "a resident multisampled DCC refusal did not identify the affected plane, range and sample count"); + }; std::vector> blocks; struct ClearTargets { AgcDriver::VulkanDevice& device; @@ -542,6 +547,7 @@ void DccFastClearTests(AgcDriver::VulkanDevice& device, std::uint32_t selectedSa device.ColorMetadataPass(pass); } catch (const std::runtime_error& error) { refused = std::string_view(error.what()).find("writes to a resident multisampled DCC") != std::string_view::npos; + if (refused) expectRange(error.what(), "color", source.Address(), AgcDriver::Graphics::MultisampledColorBytes(target), samples); } Require(refused, "a same-value CPU color store did not invalidate multisampled DCC residency"); } @@ -552,9 +558,21 @@ void DccFastClearTests(AgcDriver::VulkanDevice& device, std::uint32_t selectedSa device.ColorMetadataPass(pass); } catch (const std::runtime_error& error) { refused = std::string_view(error.what()).find("writes to a resident multisampled DCC") != std::string_view::npos; + if (refused) expectRange(error.what(), "DCC", metadata.Address(), AgcDriver::Graphics::MultisampledDccBytes(target), samples); } Require(refused, "a same-value CPU DCC store did not invalidate multisampled DCC residency"); } + if (code == 0x40u && alphaOnMsb && cmaskMode == 0xccu) { + reinterpret_cast(target.cmaskAddress)[0] = 0xccu; + bool refused = false; + try { + device.ColorMetadataPass(pass); + } catch (const std::runtime_error& error) { + refused = std::string_view(error.what()).find("writes to a resident multisampled DCC") != std::string_view::npos; + if (refused) expectRange(error.what(), "CMASK", target.cmaskAddress, target.cmaskBytes, samples); + } + Require(refused, "a same-value CPU CMASK store did not invalidate multisampled DCC residency"); + } if (code == 0xc0u && alphaOnMsb && cmaskMode == 0) { AgcDriver::GuestMemory::Unwatch(metadata.Address(), 4096); bool refused = false; @@ -562,6 +580,7 @@ void DccFastClearTests(AgcDriver::VulkanDevice& device, std::uint32_t selectedSa device.ColorMetadataPass(pass); } catch (const std::runtime_error& error) { refused = std::string_view(error.what()).find("reuse requires watched guest memory") != std::string_view::npos; + if (refused) expectRange(error.what(), "DCC", metadata.Address(), AgcDriver::Graphics::MultisampledDccBytes(target), samples); } Require(refused, "an unwatched metadata range remained eligible for multisampled DCC reuse"); }