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.
This commit is contained in:
lightningterror 2022-08-05 20:54:25 +02:00
parent 1478819258
commit 6b48cf574d
5 changed files with 92 additions and 14 deletions

View File

@ -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
{

View File

@ -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

View File

@ -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

View File

@ -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
{

View File

@ -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
{