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 {