From 6b48cf574d2ca2ab23df197f90a2fdb4c75f6569 Mon Sep 17 00:00:00 2001 From: lightningterror <18107717+lightningterror@users.noreply.github.com> Date: Fri, 5 Aug 2022 20:54:25 +0200 Subject: [PATCH] GS-hw: Adjust how we handle specific blend mix cases. Replace Cs*As + Cd*(1 - As) with Cs*As - Cd*(As - 1). Replace Cs*F + Cd*(1 - F) with Cs*F - Cd*(F - 1). As - 1 or F - 1 subtraction is only done for the dual source output (hw blending part) since we are changing the equation. Af will be replaced with As in shader and send it to dual source output. Also check if A*Alpha in the shader overflows, if it does then adjust the alpha that is sent for HW blending further to compensate. --- bin/resources/shaders/dx11/tfx.fx | 22 +++++++++++++++++++--- bin/resources/shaders/opengl/tfx_fs.glsl | 22 +++++++++++++++++++--- bin/resources/shaders/vulkan/tfx.glsl | 20 ++++++++++++++++++-- pcsx2/GS/Renderers/HW/GSRendererHW.cpp | 19 ++++++++++++++++--- pcsx2/GS/Renderers/Metal/tfx.metal | 23 ++++++++++++++++++++--- 5 files changed, 92 insertions(+), 14 deletions(-) diff --git a/bin/resources/shaders/dx11/tfx.fx b/bin/resources/shaders/dx11/tfx.fx index d42686e5da..c0218829cf 100644 --- a/bin/resources/shaders/dx11/tfx.fx +++ b/bin/resources/shaders/dx11/tfx.fx @@ -750,7 +750,7 @@ void ps_color_clamp_wrap(inout float3 C) } } -void ps_blend(inout float4 Color, float As, float2 pos_xy) +void ps_blend(inout float4 Color, inout float As, float2 pos_xy) { if (SW_BLEND) { @@ -775,10 +775,26 @@ void ps_blend(inout float4 Color, float As, float2 pos_xy) float3 D = (PS_BLEND_D == 0) ? Cs : ((PS_BLEND_D == 1) ? Cd : (float3)0.0f); // As/Af clamp alpha for Blend mix - if (PS_BLEND_MIX) - C = min(C, (float)1.0f); + // We shouldn't clamp blend mix with clr1 as we want alpha higher + if (PS_BLEND_MIX && PS_CLR_HW != 1) + C = min(C, 1.0f); Color.rgb = (PS_BLEND_A == PS_BLEND_B) ? D : trunc(((A - B) * C) + D); + + if (PS_CLR_HW == 1) + { + // Replace Af with As so we can do proper compensation for Alpha. + if (PS_BLEND_C == 2) + As = Af; + // Subtract 1 for alpha to compensate for the changed equation, + // if c.rgb > 255.0f then we further need to adjust alpha accordingly, + // we pick the lowest overflow from all colors because it's the safest, + // we divide by 255 the color because we don't know Cd value, + // changed alpha should only be done for hw blend. + float min_color = min(min(Color.r, Color.g), Color.b); + float alpha_compensate = max(1.0f, min_color / 255.0f); + As -= alpha_compensate; + } } else { diff --git a/bin/resources/shaders/opengl/tfx_fs.glsl b/bin/resources/shaders/opengl/tfx_fs.glsl index 939f0cc0f5..fab38d8333 100644 --- a/bin/resources/shaders/opengl/tfx_fs.glsl +++ b/bin/resources/shaders/opengl/tfx_fs.glsl @@ -689,7 +689,7 @@ void ps_color_clamp_wrap(inout vec3 C) #endif } -void ps_blend(inout vec4 Color, float As) +void ps_blend(inout vec4 Color, inout float As) { #if SW_BLEND @@ -754,8 +754,9 @@ void ps_blend(inout vec4 Color, float As) #endif // As/Af clamp alpha for Blend mix -#if PS_BLEND_MIX - C = min(C, float(1.0f)); + // We shouldn't clamp blend mix with clr1 as we want alpha higher +#if PS_BLEND_MIX && PS_CLR_HW != 1 + C = min(C, 1.0f); #endif #if PS_BLEND_A == PS_BLEND_B @@ -764,6 +765,21 @@ void ps_blend(inout vec4 Color, float As) Color.rgb = trunc((A - B) * C + D); #endif +#if PS_CLR_HW == 1 + // Replace Af with As so we can do proper compensation for Alpha. +#if PS_BLEND_C == 2 + As = Af; +#endif + // Subtract 1 for alpha to compensate for the changed equation, + // if c.rgb > 255.0f then we further need to adjust alpha accordingly, + // we pick the lowest overflow from all colors because it's the safest, + // we divide by 255 the color because we don't know Cd value, + // changed alpha should only be done for hw blend. + float min_color = min(min(Color.r, Color.g), Color.b); + float alpha_compensate = max(1.0f, min_color / 255.0f); + As -= alpha_compensate; +#endif + #else // Needed for Cd * (As/Ad/F + 1) blending modes #if PS_CLR_HW == 1 || PS_CLR_HW == 5 diff --git a/bin/resources/shaders/vulkan/tfx.glsl b/bin/resources/shaders/vulkan/tfx.glsl index 128ee7077a..411f8bc8d2 100644 --- a/bin/resources/shaders/vulkan/tfx.glsl +++ b/bin/resources/shaders/vulkan/tfx.glsl @@ -990,7 +990,7 @@ void ps_color_clamp_wrap(inout vec3 C) #endif } -void ps_blend(inout vec4 Color, float As) +void ps_blend(inout vec4 Color, inout float As) { #if SW_BLEND @@ -1053,7 +1053,8 @@ void ps_blend(inout vec4 Color, float As) #endif // As/Af clamp alpha for Blend mix - #if PS_BLEND_MIX + // We shouldn't clamp blend mix with clr1 as we want alpha higher + #if PS_BLEND_MIX && PS_CLR_HW != 1 C = min(C, 1.0f); #endif @@ -1063,6 +1064,21 @@ void ps_blend(inout vec4 Color, float As) Color.rgb = trunc((A - B) * C + D); #endif + #if PS_CLR_HW == 1 + // Replace Af with As so we can do proper compensation for Alpha. + #if PS_BLEND_C == 2 + As = Af; + #endif + // Subtract 1 for alpha to compensate for the changed equation, + // if c.rgb > 255.0f then we further need to adjust alpha accordingly, + // we pick the lowest overflow from all colors because it's the safest, + // we divide by 255 the color because we don't know Cd value, + // changed alpha should only be done for hw blend. + float min_color = min(min(Color.r, Color.g), Color.b); + float alpha_compensate = max(1.0f, min_color / 255.0f); + As -= alpha_compensate; + #endif + #else #if PS_CLR_HW == 1 || PS_CLR_HW == 5 // Needed for Cd * (As/Ad/F + 1) blending modes diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index 1587e53b9a..276a683d3a 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -2359,6 +2359,7 @@ void GSRendererHW::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool& // Get alpha value const bool alpha_c0_zero = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().max == 0) && !IsCoverageAlpha(); const bool alpha_c0_one = (m_conf.ps.blend_c == 0 && (GetAlphaMinMax().min == 128) && (GetAlphaMinMax().max == 128)) || IsCoverageAlpha(); + const bool alpha_c0_high_min_one = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().min > 128) && !IsCoverageAlpha(); const bool alpha_c0_high_max_one = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().max > 128) && !IsCoverageAlpha(); const bool alpha_c2_zero = (m_conf.ps.blend_c == 2 && ALPHA.FIX == 0u); const bool alpha_c2_one = (m_conf.ps.blend_c == 2 && ALPHA.FIX == 128u); @@ -2713,11 +2714,20 @@ void GSRendererHW::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool& m_conf.blend = {true, GSDevice::CONST_ONE, blend.dst, blend.op, m_conf.ps.blend_c == 2, ALPHA.FIX}; m_conf.ps.blend_mix = 1; - // Elide DSB colour output if not used by dest. - m_conf.ps.no_color1 |= !GSDevice::IsDualSourceBlendFactor(blend.dst); - if (blend_mix1) { + if (m_conf.ps.blend_b == m_conf.ps.blend_d && (alpha_c0_high_min_one || alpha_c2_high_one)) + { + // Replace Cs*As + Cd*(1 - As) with Cs*As - Cd*(As - 1). + // Replace Cs*F + Cd*(1 - F) with Cs*F - Cd*(F - 1). + // As - 1 or F - 1 subtraction is only done for the dual source output (hw blending part) since we are changing the equation. + // Af will be replaced with As in shader and send it to dual source output. + m_conf.blend = {true, GSDevice::CONST_ONE, GSDevice::SRC1_ALPHA, GSDevice::OP_SUBTRACT, false, 0}; + // clr_hw 1 will disable alpha clamp, we can reuse the old bits. + m_conf.ps.clr_hw = 1; + //m_conf.ps.blend_mix = 0; + } + m_conf.ps.blend_a = 0; m_conf.ps.blend_b = 2; m_conf.ps.blend_d = 2; @@ -2742,6 +2752,9 @@ void GSRendererHW::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool& // Swap Ad with As for hw blend m_conf.ps.clr_hw = 6; } + + // Elide DSB colour output if not used by dest. + m_conf.ps.no_color1 |= !GSDevice::IsDualSourceBlendFactor(blend.dst); } else { diff --git a/pcsx2/GS/Renderers/Metal/tfx.metal b/pcsx2/GS/Renderers/Metal/tfx.metal index 5ed97ce421..320464dda5 100644 --- a/pcsx2/GS/Renderers/Metal/tfx.metal +++ b/pcsx2/GS/Renderers/Metal/tfx.metal @@ -721,7 +721,7 @@ struct PSMain return selector == 0 ? zero : selector == 1 ? one : two; } - void ps_blend(thread float4& Color, float As) + void ps_blend(thread float4& Color, thread float& As) { if (SW_BLEND) { @@ -729,7 +729,7 @@ struct PSMain if (PS_PABE) { // No blending so early exit - if (As < 1.0f) + if (As < 1.f) return; } @@ -743,13 +743,30 @@ struct PSMain float C = pick(PS_BLEND_C, As, Ad, cb.alpha_fix); float3 D = pick(PS_BLEND_D, Cs, Cd, float3(0.f)); - if (PS_BLEND_MIX) + // As/Af clamp alpha for Blend mix + // We shouldn't clamp blend mix with clr1 as we want alpha higher + if (PS_BLEND_MIX && PS_CLR_HW != 1) C = min(C, 1.f); if (PS_BLEND_A == PS_BLEND_B) Color.rgb = D; else Color.rgb = trunc((A - B) * C + D); + + if (PS_CLR_HW == 1) + { + // Replace Af with As so we can do proper compensation for Alpha. + if (PS_BLEND_C == 2) + As = cb.alpha_fix; + // Subtract 1 for alpha to compensate for the changed equation, + // if c.rgb > 255.0f then we further need to adjust alpha accordingly, + // we pick the lowest overflow from all colors because it's the safest, + // we divide by 255 the color because we don't know Cd value, + // changed alpha should only be done for hw blend. + float min_color = min(min(Color.r, Color.g), Color.b); + float alpha_compensate = max(1.f, min_color / 255.f); + As -= alpha_compensate; + } } else {