From 6c5b181b68319d8045aab94664a0b19549a650e6 Mon Sep 17 00:00:00 2001 From: Scott Schuster Date: Thu, 8 Oct 2026 01:27:33 -0500 Subject: [PATCH 1/3] fix(agc): accept PA_SC_MODE_CNTL_1's out-of-order watermark RADV sets OUT_OF_ORDER_WATER_MARK to 7 on every GFX10 draw, also when OUT_OF_ORDER_PRIMITIVE_ENABLE is off. The watermark only tunes out-of-order rasterization, which stays rejected, so the field is accepted alone. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp | 2 +- core/libs/prx/libSceAgcDriver/tests/Graphics.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp index dc0ab6fb9..739502acf 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp @@ -105,7 +105,7 @@ constexpr std::uint32_t ShaderControlMask = ~(0x0000f870u | 0x00020600u | 0x0001 constexpr std::uint32_t PixelStageRunsMask = 0x00020747u; constexpr std::uint32_t AlphaToCoverageMask = ~0x0001ff00u; constexpr std::uint32_t ScanModeMask = ~0x22u; -constexpr std::uint32_t ScanControlMask = ~0x06023fffu; +constexpr std::uint32_t ScanControlMask = ~0x76023fffu; constexpr std::uint32_t ScreenOffsetMask = ~0x01ff01ffu; // Bits 26/27 (ZCLIP_NEAR/FAR_DISABLE) become depth clamping; bit 19 selects the [0, 1] clip space. constexpr std::uint32_t ClipControlMask = ~(0x80000u | 0x01000000u | 0x0c000000u); diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index e762ee96f..f4173e0ea 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -123,6 +123,11 @@ void stateTests() { queue.context[0x293] = 0x06020000u; (void)AgcDriver::Graphics::DecodeState(queue); Require(AgcDriver::Graphics::DrawRejection(queue, false).find("sample iteration") == std::string::npos, "per-engine primitive discard was rejected"); + queue.context[0x293] = 0x760201bcu; + (void)AgcDriver::Graphics::DecodeState(queue); + Require(AgcDriver::Graphics::DrawRejection(queue, false).find("sample iteration") == std::string::npos, "an out-of-order watermark without out-of-order rasterization was rejected"); + queue.context[0x293] = 0x7e0201bcu; + Require(AgcDriver::Graphics::DrawRejection(queue, false).find("sample iteration") != std::string::npos, "out-of-order rasterization was accepted"); queue.context[0x293] = 0x06030000u; Require(AgcDriver::Graphics::DrawRejection(queue, false).find("sample iteration") != std::string::npos, "per-sample shading was accepted"); expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "sample iteration"); From 88cc2ea47744fa59f45c22626e631426ea5b1c66 Mon Sep 17 00:00:00 2001 From: Scott Schuster Date: Thu, 8 Oct 2026 01:48:39 -0500 Subject: [PATCH 2/3] fix(agc): accept RADV's disabled conservative rasterization state With conservative rasterization disabled, RADV programs PA_SC_CONSERVATIVE_RASTERIZATION_CNTL to NULL_SQUAD_AA_MASK_ENABLE alone, where libSceAgc writes the two INNER_TO_NORMAL overrides (0x6000). A value made of only those bits now decodes as disabled; overestimation (0x6001) keeps its checks, and every other mode is still rejected. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp | 6 +++--- core/libs/prx/libSceAgcDriver/tests/Graphics.cpp | 12 ++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp index 739502acf..089279da3 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp @@ -58,7 +58,7 @@ std::string vteMessage(std::uint32_t viewportControl) { std::string conservativeMessage(std::uint32_t control) { std::ostringstream message; - message << "AGC graphics: PA_SC_CONSERVATIVE_RASTERIZATION_CNTL=0x" << std::hex << control << ": only 0x6000 (off) and 0x6001 (overestimation) are supported"; + message << "AGC graphics: PA_SC_CONSERVATIVE_RASTERIZATION_CNTL=0x" << std::hex << control << ": only off (0x6000, or RADV's 0x100000) and 0x6001 (overestimation) are supported"; return message.str(); } @@ -72,7 +72,7 @@ void requireConservativeTriangles(bool triangles, const char* primitive, const c VkConservativeRasterizationModeEXT decodeConservativeRasterization(const QueueState& queue) { const auto& cx = queue.context; const auto control = read(cx, 0x313); - if (control == 0x6000u) return VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT; + if ((control & ~0x00106000u) == 0) return VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT; if (control != 0x6001u) throw std::runtime_error(conservativeMessage(control)); const auto stages = read(cx, 0x2d5); if ((stages & 0x20u) != 0) { @@ -863,7 +863,7 @@ std::string DrawRejection(const QueueState& queue, bool indexed) { if (value(cx, 0x83, word) && word != 0xffffu) return require(false, "clip rectangles are unsupported"); if (value(cx, 0x8c, word) && (word & 0xfu) != 0xau) return require(false, "nonstandard triangle edge rules are unsupported"); if (value(cx, 0x2f9, word) && word != 0x2du) return require(false, "nonstandard pixel center or vertex quantization is unsupported"); - if (value(cx, 0x313, word) && word != 0x6000u && word != 0x6001u) return conservativeMessage(word); + if (value(cx, 0x313, word) && (word & ~0x00106000u) != 0 && word != 0x6001u) return conservativeMessage(word); std::uint32_t other = 0; if (value(cx, 0x30e, word) && value(cx, 0x30f, other) && (word != 0xffffffffu || other != 0xffffffffu)) return require(false, "sample masks are unsupported"); if (value(cx, 0x206, word) && word != 0x43fu) return vteMessage(word); diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index f4173e0ea..597409d3f 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -131,6 +131,18 @@ void stateTests() { queue.context[0x293] = 0x06030000u; Require(AgcDriver::Graphics::DrawRejection(queue, false).find("sample iteration") != std::string::npos, "per-sample shading was accepted"); expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "sample iteration"); + queue.context[0x293] = 0; + for (const auto disabled : {0x6000u, 0x00100000u, 0u}) { + queue.context[0x313] = disabled; + Require(AgcDriver::Graphics::DecodeState(queue).conservativeRasterization == VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT, "disabled conservative rasterization decoded as enabled"); + Require(AgcDriver::Graphics::DrawRejection(queue, false).find("PA_SC_CONSERVATIVE") == std::string::npos, "disabled conservative rasterization was rejected"); + } + for (const auto enabled : {0x00e00001u, 0x01e00022u}) { + queue.context[0x313] = enabled; + Require(AgcDriver::Graphics::DrawRejection(queue, false).find("PA_SC_CONSERVATIVE") != std::string::npos, "conservative rasterization was accepted"); + expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "PA_SC_CONSERVATIVE"); + } + queue.context[0x313] = 0x6000u; queue = makeState(); queue.userConfig.erase(0x24b); queue.context[0x2a5] = 0; From af9e60b690b3d3ebfcd0fc8e044ffc26f338b4a3 Mon Sep 17 00:00:00 2001 From: Scott Schuster Date: Fri, 9 Oct 2026 00:54:22 -0500 Subject: [PATCH 3/3] fix(agc): accept RADV's point and line depth bias enable on triangle draws With depth bias enabled, RADV sets POLY_OFFSET_PARA_ENABLE in PA_SU_SC_MODE_CNTL next to the front and back enables, whatever the topology; vkQuake's draws carry 0x3a46. The bit applies the offset to points and lines, so it changes nothing for triangles, and Vulkan applies depth bias only to polygons. It is now accepted with depth bias on triangle topologies; point and line draws with it are still rejected. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp | 4 +++- core/libs/prx/libSceAgcDriver/tests/Graphics.cpp | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp index 089279da3..9b616a2dc 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp @@ -553,7 +553,9 @@ State DecodeState(const QueueState& queue) { const auto raster = read(cx, 0x205); // Bits 5-10 give the front/back polygon type (2 = filled triangles), which POLY_MODE (bit 3) turns on // explicitly; KEEP_TOGETHER_ENABLE (bit 24) only affects primitive distribution across the chip. - const auto rasterMode = raster & ~0x7u & ~(1u << 24u) & ~0x1800u; + const bool pointsOrLines = result.topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST || result.topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + const auto lineOffset = (raster & 0x1800u) != 0 && !pointsOrLines ? 0x2000u : 0u; + const auto rasterMode = raster & ~0x7u & ~(1u << 24u) & ~0x1800u & ~lineOffset; if (rasterMode != 0 && rasterMode != 0x240u && rasterMode != 0x248u) throw std::runtime_error("AGC graphics: " + zeroMessage(0x205, raster, "polygon mode, depth bias, provoking vertex or nonstandard rasterization")); result.cullMode = ((raster & 1u) != 0 ? VK_CULL_MODE_FRONT_BIT : 0u) | ((raster & 2u) != 0 ? VK_CULL_MODE_BACK_BIT : 0u); if (result.rectList) result.cullMode = VK_CULL_MODE_NONE; diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index 597409d3f..e04c75780 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -878,6 +878,13 @@ void DepthBoundsBiasTests() { queue.context[0x205] = 0x00001a4au; state = AgcDriver::Graphics::DecodeState(queue); Require(state.depthBias && state.depthBiasConstant == 4.0f, "culled back faces must not constrain the front depth bias"); + queue.context[0x205] = 0x00003a46u; + state = AgcDriver::Graphics::DecodeState(queue); + Require(state.depthBias && state.depthBiasConstant == 4.0f && state.cullMode == VK_CULL_MODE_BACK_BIT, "a triangle draw with the point and line offset enable lost its depth bias"); + queue.userConfig[0x242] = 2; + expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "polygon mode, depth bias, provoking vertex"); + queue.userConfig[0x242] = 4; + queue.context[0x205] = 0x00001a4au; queue.context[0x2de] = 0x1f0u; expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "units other than the depth format"); queue.context[0x2de] = 0x1e9u;