From baaad78e81d85214dc1738d750bfed9e96b06863 Mon Sep 17 00:00:00 2001 From: Cesar Rodriguez <11452991+Tamalero@users.noreply.github.com> Date: Sun, 11 Oct 2026 00:32:34 -0700 Subject: [PATCH] fix(agc): leave blending off for a color target with CB_COLOR_INFO.BLEND_BYPASS DecodeState threw "blend bypass conflicts with enabled blending" when CB_BLENDn_CONTROL enabled blending for a target whose CB_COLORn_INFO has BLEND_BYPASS (bit 16, Mesa src/amd/registers/gfx10.json) set. The bit makes the color block skip the blender for that target: Mesa sets it "according to docs" for UINT/SINT and 8_24/24_8/X24_8_32_FLOAT targets, which the blender cannot process (src/amd/common/ac_descriptors.c, shared by radeonsi and RADV). Marvel's Guardians of the Galaxy (PPSA01748) issues such draws right after its loading screen. Blending is now enabled only when it is requested and not bypassed. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp | 3 +-- core/libs/prx/libSceAgcDriver/tests/Graphics.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp index f151a5f0b..b2eb57f05 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp @@ -868,11 +868,10 @@ State DecodeState(const QueueState& queue, bool vertexWorkgroup) { for (std::uint32_t component = 0; component < 4; ++component) { if (((exportedMask >> ((mapping >> (2u * component)) & 3u)) & 1u) != 0) state.colorWriteMask |= 1u << component; } - state.blendEnable = (blend >> 30u) & 1u; + state.blendEnable = ((blend >> 30u) & 1u) != 0 && (read(cx, 0x31c + slot * 0xfu) & 0x10000u) == 0; if (state.blendEnable && (mapping == 0x1bu || mapping == 0x93u)) throw std::runtime_error("AGC graphics: blending into a color target with a reversed component order is not implemented"); Require(!state.blendEnable || !color.uintExport, "blending into an unsigned integer target is unsupported"); if (state.blendEnable) { - Require((read(cx, 0x31c + slot * 0xfu) & 0x10000u) == 0, "blend bypass conflicts with enabled blending"); state.srcColorBlendFactor = blendFactor(blend & 0x1fu); state.dstColorBlendFactor = blendFactor((blend >> 8u) & 0x1fu); state.colorBlendOp = blendOp((blend >> 5u) & 7u); diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index 22b5c62dd..93505b5ed 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -963,6 +963,16 @@ void PixelInputLayoutTests() { Require(!readsBuiltin(read, spv::BuiltInFragCoord) && !readsBuiltin(read, spv::BuiltInBaryCoordKHR), "an ADDR-only centroid pair was loaded"); } +void BlendBypassTests() { + auto queue = makeState(); + for (std::uint32_t constant = 0x105; constant < 0x109; ++constant) queue.context[constant] = 0; + queue.context[0x1e0] = (1u << 30u) | 4u | (5u << 8u); + Require(AgcDriver::Graphics::DecodeState(queue).blends.at(0).blendEnable, "enabled blending was not decoded"); + queue.context[0x31c] |= 0x10000u; + const auto bypassed = AgcDriver::Graphics::DecodeState(queue); + Require(!bypassed.blends.at(0).blendEnable && bypassed.blends.at(0).colorWriteMask != 0, "a target with BLEND_BYPASS still blends"); +} + void OpaqueDestinationAlphaTests() { auto queue = makeState(); for (std::uint32_t constant = 0x105; constant < 0x109; ++constant) queue.context[constant] = 0; @@ -5130,6 +5140,7 @@ int main(int argc, char** argv) { DisabledColorTests(); OpaqueDestinationAlphaTests(); Color1555TargetTests(); + BlendBypassTests(); CompactedExportTests(); ReversedComponentOrderTests(); ShadersWriteMemoryTests();