diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp index 6506f69d1..3fb8f9920 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp @@ -318,6 +318,23 @@ VkBlendFactor blendFactor(std::uint32_t value) { } } +VkBlendFactor colorFactorWithOpaqueDestination(VkBlendFactor factor) { + switch (factor) { + case VK_BLEND_FACTOR_DST_ALPHA: return VK_BLEND_FACTOR_ONE; + case VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA: return VK_BLEND_FACTOR_ZERO; + case VK_BLEND_FACTOR_SRC_ALPHA_SATURATE: return VK_BLEND_FACTOR_ZERO; + default: return factor; + } +} + +VkBlendFactor alphaFactorWithOpaqueDestination(VkBlendFactor factor) { + switch (factor) { + case VK_BLEND_FACTOR_DST_ALPHA: case VK_BLEND_FACTOR_DST_COLOR: return VK_BLEND_FACTOR_ONE; + case VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA: case VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR: return VK_BLEND_FACTOR_ZERO; + default: return factor; + } +} + VkBlendOp blendOp(std::uint32_t value) { switch (value) { case 0: return VK_BLEND_OP_ADD; @@ -684,6 +701,12 @@ State DecodeState(const QueueState& queue) { state.dstColorBlendFactor = state.dstAlphaBlendFactor; state.colorBlendOp = state.alphaBlendOp; } + if ((read(cx, 0x31d + slot * 0xfu) & 0x20000u) != 0) { + state.srcColorBlendFactor = colorFactorWithOpaqueDestination(state.srcColorBlendFactor); + state.dstColorBlendFactor = colorFactorWithOpaqueDestination(state.dstColorBlendFactor); + state.srcAlphaBlendFactor = alphaFactorWithOpaqueDestination(state.srcAlphaBlendFactor); + state.dstAlphaBlendFactor = alphaFactorWithOpaqueDestination(state.dstAlphaBlendFactor); + } for (std::uint32_t i = 0; i < 4; ++i) result.blendConstants[i] = readFloat(cx, 0x105 + i); } result.blends[color.exportIndex] = state; @@ -738,7 +761,7 @@ ColorTarget DecodeColorBuffer(const Registers& cx, std::uint32_t slot) { const auto slice = view & 0x1fffu; Require(slice == ((view >> 13u) & 0x1fffu), "color views of several array slices are unsupported"); const auto viewMip = (view >> 26u) & 0xfu; - zero(cx, 0x31d + stride, ~0u, "color samples, fragments or destination alpha override"); + zero(cx, 0x31d + stride, ~0x20000u, "color samples, fragments or destination alpha override"); const auto attrib2 = read(cx, 0x3b0 + slot); const auto maxMip = attrib2 >> 28u; Require(viewMip <= maxMip, "color view mip exceeds the surface"); diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index 630b2eeb4..93be672ff 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -572,6 +572,25 @@ void PixelInputLayoutTests() { Require(!readsBuiltin(read, spv::BuiltInFragCoord) && !readsBuiltin(read, spv::BuiltInBaryCoordKHR), "an ADDR-only centroid pair was loaded"); } +void OpaqueDestinationAlphaTests() { + auto queue = makeState(); + for (std::uint32_t constant = 0x105; constant < 0x109; ++constant) queue.context[constant] = 0; + queue.context[0x1e0] = (1u << 30u) | (1u << 29u) | 6u | (7u << 8u) | (8u << 16u) | (10u << 24u); + auto blend = AgcDriver::Graphics::DecodeState(queue).blend; + Require(blend.srcColorBlendFactor == VK_BLEND_FACTOR_DST_ALPHA && blend.dstColorBlendFactor == VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA, "color blend factors changed"); + Require(blend.srcAlphaBlendFactor == VK_BLEND_FACTOR_DST_COLOR && blend.dstAlphaBlendFactor == VK_BLEND_FACTOR_SRC_ALPHA_SATURATE, "alpha blend factors changed"); + queue.context[0x31d] = 0x20000u; + blend = AgcDriver::Graphics::DecodeState(queue).blend; + Require(blend.srcColorBlendFactor == VK_BLEND_FACTOR_ONE && blend.dstColorBlendFactor == VK_BLEND_FACTOR_ZERO, "an opaque destination left a destination-alpha color factor"); + Require(blend.srcAlphaBlendFactor == VK_BLEND_FACTOR_ONE && blend.dstAlphaBlendFactor == VK_BLEND_FACTOR_SRC_ALPHA_SATURATE, "an opaque destination left a destination alpha factor"); + queue.context[0x1e0] = (1u << 30u) | 10u | (9u << 8u); + blend = AgcDriver::Graphics::DecodeState(queue).blend; + Require(blend.srcColorBlendFactor == VK_BLEND_FACTOR_ZERO && blend.dstColorBlendFactor == VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR, "alpha saturation with an opaque destination"); + Require(blend.dstAlphaBlendFactor == VK_BLEND_FACTOR_ZERO, "an opaque destination left a destination-color alpha factor"); + queue.context[0x31d] = 0x40000u; + expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "destination alpha override"); +} + void DisabledColorTests() { auto queue = makeState(); queue.context[0x8e] = 0; @@ -2982,6 +3001,7 @@ int main() { orderedPixelShaderTests(); ConservativeRasterizationTests(); DisabledColorTests(); + OpaqueDestinationAlphaTests(); CompactedExportTests(); ReversedComponentOrderTests(); metadataPassTests();