diff --git a/bin/resources/shaders/dx11/tfx.fx b/bin/resources/shaders/dx11/tfx.fx index 3ecdff056a..c4ae8a939b 100644 --- a/bin/resources/shaders/dx11/tfx.fx +++ b/bin/resources/shaders/dx11/tfx.fx @@ -784,7 +784,7 @@ void ps_fbmask(inout float4 C, float2 pos_xy) } } -void ps_dither(inout float3 C, float2 pos_xy, float alpha_blend) +void ps_dither(inout float3 C, float As, float2 pos_xy) { if (PS_DITHER) { @@ -800,7 +800,10 @@ void ps_dither(inout float3 C, float2 pos_xy, float alpha_blend) // The idea here is we add on the dither amount adjusted by the alpha before it goes to the hw blend // so after the alpha blend the resulting value should be the same as (Cs - Cd) * As + Cd + Dither. if (PS_DITHER_ADJUST) - value *= alpha_blend > 0.0f ? min(1.0f / alpha_blend, 1.0f) : 1.0f; + { + float Alpha = PS_BLEND_C == 2 ? Af : As; + value *= Alpha > 0.0f ? min(1.0f / Alpha, 1.0f) : 1.0f; + } if (PS_ROUND_INV) C -= value; @@ -1068,7 +1071,7 @@ PS_OUTPUT ps_main(PS_INPUT input) } } - ps_dither(C.rgb, input.p.xy, alpha_blend.a); + ps_dither(C.rgb, alpha_blend.a, input.p.xy); // Color clamp/wrap needs to be done after sw blending and dithering ps_color_clamp_wrap(C.rgb); diff --git a/bin/resources/shaders/opengl/tfx_fs.glsl b/bin/resources/shaders/opengl/tfx_fs.glsl index 1b0655241f..d6fbda5090 100644 --- a/bin/resources/shaders/opengl/tfx_fs.glsl +++ b/bin/resources/shaders/opengl/tfx_fs.glsl @@ -720,7 +720,7 @@ void ps_fbmask(inout vec4 C) #endif } -void ps_dither(inout vec3 C, float alpha_blend) +void ps_dither(inout vec3 C, float As) { #if PS_DITHER #if PS_DITHER == 2 @@ -733,7 +733,13 @@ void ps_dither(inout vec3 C, float alpha_blend) // The idea here is we add on the dither amount adjusted by the alpha before it goes to the hw blend // so after the alpha blend the resulting value should be the same as (Cs - Cd) * As + Cd + Dither. #if PS_DITHER_ADJUST - value *= alpha_blend > 0.0f ? min(1.0f / alpha_blend, 1.0f) : 1.0f; + #if PS_BLEND_C == 2 + float Alpha = Af; + #else + float Alpha = As; + #endif + + value *= Alpha > 0.0f ? min(1.0f / Alpha, 1.0f) : 1.0f; #endif #if PS_ROUND_INV diff --git a/bin/resources/shaders/vulkan/tfx.glsl b/bin/resources/shaders/vulkan/tfx.glsl index 9b0a90fd57..36695b4b88 100644 --- a/bin/resources/shaders/vulkan/tfx.glsl +++ b/bin/resources/shaders/vulkan/tfx.glsl @@ -970,7 +970,7 @@ void ps_fbmask(inout vec4 C) #endif } -void ps_dither(inout vec3 C, float alpha_blend) +void ps_dither(inout vec3 C, float As) { #if PS_DITHER ivec2 fpos; @@ -986,7 +986,13 @@ void ps_dither(inout vec3 C, float alpha_blend) // The idea here is we add on the dither amount adjusted by the alpha before it goes to the hw blend // so after the alpha blend the resulting value should be the same as (Cs - Cd) * As + Cd + Dither. #if PS_DITHER_ADJUST - value *= alpha_blend > 0.0f ? min(1.0f / alpha_blend, 1.0f) : 1.0f; + #if PS_BLEND_C == 2 + float Alpha = Af; + #else + float Alpha = As; + #endif + + value *= Alpha > 0.0f ? min(1.0f / Alpha, 1.0f) : 1.0f; #endif #if PS_ROUND_INV diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index d196f6a893..92683c7186 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -3769,10 +3769,13 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT const bool alpha_c0_one = (m_conf.ps.blend_c == 0 && (GetAlphaMinMax().min == 128) && (GetAlphaMinMax().max == 128)); const bool alpha_c0_high_min_one = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().min > 128); const bool alpha_c0_high_max_one = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().max > 128); + const bool alpha_c0_less_max_one = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().max <= 128); const bool alpha_c2_zero = (m_conf.ps.blend_c == 2 && AFIX == 0u); const bool alpha_c2_one = (m_conf.ps.blend_c == 2 && AFIX == 128u); + const bool alpha_c2_less_one = (m_conf.ps.blend_c == 2 && AFIX <= 128u); const bool alpha_c2_high_one = (m_conf.ps.blend_c == 2 && AFIX > 128u); const bool alpha_one = alpha_c0_one || alpha_c2_one; + const bool alpha_less_one = alpha_c0_less_max_one || alpha_c2_less_one; // Optimize blending equations, must be done before index calculation if ((m_conf.ps.blend_a == m_conf.ps.blend_b) || ((m_conf.ps.blend_b == m_conf.ps.blend_d) && alpha_one)) @@ -3930,8 +3933,6 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT // Do not run BLEND MIX if sw blending is already present, it's less accurate. blend_mix &= !sw_blending; sw_blending |= blend_mix; - // Disable dithering on blend mix. - m_conf.ps.dither &= !blend_mix || (m_conf.ps.blend_a == 0 && m_conf.ps.blend_b == 1 && m_conf.ps.blend_c == 0 && GetAlphaMinMax().max <= 128); [[fallthrough]]; case AccBlendLevel::Minimum: break; @@ -3984,8 +3985,6 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT // Do not run BLEND MIX if sw blending is already present, it's less accurate. blend_mix &= !sw_blending; sw_blending |= blend_mix; - // Disable dithering on blend mix. - m_conf.ps.dither &= !blend_mix || (m_conf.ps.blend_a == 0 && m_conf.ps.blend_b == 1 && m_conf.ps.blend_c == 0 && GetAlphaMinMax().max <= 128); [[fallthrough]]; case AccBlendLevel::Minimum: break; @@ -4191,6 +4190,14 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT } else if (blend_mix) { + // Disable dithering on blend mix if needed. + if (m_conf.ps.dither) + { + const bool can_dither = (m_conf.ps.blend_a == 0 && m_conf.ps.blend_b == 1 && alpha_less_one); + m_conf.ps.dither = can_dither; + m_conf.ps.dither_adjust = can_dither; + } + // For mixed blend, the source blend is done in the shader (so we use CONST_ONE as a factor). m_conf.blend = {true, GSDevice::CONST_ONE, blend.dst, blend.op, m_conf.ps.blend_c == 2, AFIX}; m_conf.ps.blend_mix = (blend.op == GSDevice::OP_REV_SUBTRACT) ? 2 : 1; @@ -5381,7 +5388,6 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta m_conf.cb_ps.DitherMatrix[1] = GSVector4(DIMX.DM10, DIMX.DM11, DIMX.DM12, DIMX.DM13); m_conf.cb_ps.DitherMatrix[2] = GSVector4(DIMX.DM20, DIMX.DM21, DIMX.DM22, DIMX.DM23); m_conf.cb_ps.DitherMatrix[3] = GSVector4(DIMX.DM30, DIMX.DM31, DIMX.DM32, DIMX.DM33); - m_conf.ps.dither_adjust = m_conf.ps.blend_a == 0 && m_conf.ps.blend_b == 1 && m_conf.ps.blend_c == 0 && GetAlphaMinMax().max <= 128; } if (PRIM->FGE) diff --git a/pcsx2/GS/Renderers/Metal/tfx.metal b/pcsx2/GS/Renderers/Metal/tfx.metal index 102802f826..65c7f79a67 100644 --- a/pcsx2/GS/Renderers/Metal/tfx.metal +++ b/pcsx2/GS/Renderers/Metal/tfx.metal @@ -843,7 +843,7 @@ struct PSMain C = float4((uint4(int4(C)) & (cb.fbmask ^ 0xff)) | (uint4(current_color * 255.5) & cb.fbmask)); } - void ps_dither(thread float4& C, float alpha_blend) + void ps_dither(thread float4& C, float As) { if (PS_DITHER == 0) return; @@ -857,7 +857,10 @@ struct PSMain // The idea here is we add on the dither amount adjusted by the alpha before it goes to the hw blend // so after the alpha blend the resulting value should be the same as (Cs - Cd) * As + Cd + Dither. if (PS_DITHER_ADJUST) - value *= alpha_blend > 0.f ? min(1.f / alpha_blend, 1.f) : 1.f; + { + float Alpha = PS_BLEND_C == 2 ? cb.alpha_fix : As; + value *= Alpha > 0.f ? min(1.f / Alpha, 1.f) : 1.f; + } if (PS_ROUND_INV) C.rgb -= value;