From 02974ea767e2cb2d3fa0817ff42a8bb970d3c61d Mon Sep 17 00:00:00 2001 From: Swastik-51 Date: Tue, 13 Oct 2026 12:14:10 +0530 Subject: [PATCH] fix(agc): reject DCC on multisampled targets outside FMASK decompress --- .../prx/libSceAgcDriver/Graphics/include/State.hpp | 2 +- core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp | 5 +++-- core/libs/prx/libSceAgcDriver/tests/Graphics.cpp | 10 ++++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp index a27fb8bb9c..172ca2f31c 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp @@ -141,7 +141,7 @@ VkExtent2D SampleLocationGrid(const State& state); std::array ExportMappings(const State& state); std::array ExportPackings(const State& state); ShaderRecompiler::RasterPrimitive RasterPrimitiveOf(const State& state); -ColorTarget DecodeColorBuffer(const Registers& context, std::uint32_t slot); +ColorTarget DecodeColorBuffer(const Registers& context, std::uint32_t slot, bool allowMultisampleDcc = false); std::size_t CmaskBytes(std::uint32_t width, std::uint32_t height); std::uint32_t ColorWriteMask(const Registers& context); GuestTextureResource SurfaceForTarget(const ColorTarget& color); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp index 5f50608915..e34e0d2809 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp @@ -1107,7 +1107,7 @@ GuestTextureResource SurfaceForTarget(const ColorTarget& color) { return surface; } -ColorTarget DecodeColorBuffer(const Registers& cx, std::uint32_t slot) { +ColorTarget DecodeColorBuffer(const Registers& cx, std::uint32_t slot, bool allowMultisampleDcc) { const auto stride = slot * 0xfu; ColorTarget color{}; color.slot = slot; @@ -1123,6 +1123,7 @@ ColorTarget DecodeColorBuffer(const Registers& cx, std::uint32_t slot) { Require(color.samples <= 8u, "color targets with more than eight samples are unsupported"); Require((1u << ((attrib >> 15u) & 3u)) == color.samples, "color targets with fewer fragments than samples (EQAA) are unsupported"); const auto multisampleInfo = color.samples > 1 ? 0x0c006000u : 0u; + Require(allowMultisampleDcc || color.samples == 1 || (info & 0x10000000u) == 0, "DCC over a multisampled color target is unsupported"); // ROUND_MODE (bit 18) only affects unorm rounding. With DCC_ENABLE (bit 28) the target is written if ((info & ~(0x00039f7cu | 0x00040000u | 0x10000000u | 0x00002000u | multisampleInfo)) != 0) throw std::runtime_error("AGC graphics: color compression, DCC, endian conversion, nonstandard rounding or color optimization is unsupported (CB_COLOR_INFO 0x" + [&] { char text[16]; std::snprintf(text, sizeof(text), "%08x", info); return std::string(text); }() + ")"); Require((info & 0x8000u) != 0 || number == 7 || number == 4 || number == 5, "unclamped normalized color is unsupported"); @@ -1299,7 +1300,7 @@ std::optional DecodeColorMetadataPass(const QueueState& queue const auto targetMask = read(cx, 0x8e); for (std::uint32_t slot = 0; slot < 8; ++slot) { if (((targetMask >> (4u * slot)) & 0xfu) == 0 || ((read(cx, 0x31c + slot * 0xfu) >> 2u) & 0x1fu) == 0) continue; - const auto target = DecodeColorBuffer(cx, slot); + const auto target = DecodeColorBuffer(cx, slot, fmaskDecompress); if (fmaskDecompress) { Require(target.samples > 1, "CB metadata pass over a single-sample target"); Require(target.cmaskAddress != 0, "FMASK decompress of a target without a CMASK address"); diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index 4c632cbde1..7d5a439202 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -2259,6 +2259,7 @@ VKAPI_ATTR VkResult VKAPI_CALL sampleLocationDepthProperties(VkPhysicalDevice, V void multisampleTests() { using AgcDriver::Graphics::ColorMetadataPass; + using AgcDriver::Graphics::DecodeColorBuffer; using AgcDriver::Graphics::DecodeColorMetadataPass; using AgcDriver::Graphics::DecodeState; const auto multisampled = [] { @@ -2475,6 +2476,10 @@ void multisampleTests() { queue.context[0x31f] = 0; expectFailure([&] { DecodeState(queue); }, "without a CMASK address"); queue = multisampled(); + queue.context[0x31c] |= 0x10000000u; + expectFailure([&] { DecodeState(queue); }, "DCC over a multisampled color target is unsupported"); + expectFailure([&] { DecodeColorBuffer(queue.context, 0); }, "DCC over a multisampled color target is unsupported"); + queue = multisampled(); queue.context[0x31c] |= 0x2000; setCmaskAddress(queue); std::vector reads; @@ -2505,6 +2510,9 @@ void multisampleTests() { queue = rasterizedResolve; queue.context[0x32b] = queue.context[0x31c] ^ (1u << 8u); expectFailure([&] { DecodeColorMetadataPass(queue); }, "different formats or extents"); + queue = rasterizedResolve; + queue.context[0x31c] |= 0x10000000u; + expectFailure([&] { DecodeColorMetadataPass(queue); }, "DCC over a multisampled color target is unsupported"); queue = multisampled(); queue.context[0x0] = 0; queue.context[0x202] = 0xcc0030; @@ -2520,6 +2528,8 @@ void multisampleTests() { queue.context[0x0] = 0; queue.context[0x202] = 0xcc0020; expectFailure([&] { DecodeColorMetadataPass(queue); }, "multisampled color target"); + queue.context[0x31c] |= 0x10000000u; + expectFailure([&] { DecodeColorMetadataPass(queue); }, "DCC over a multisampled color target is unsupported"); queue = multisampled(); queue.context[0x2f8] = 0; queue.context[0x0] = 0;