GS/HW: Implement PABE(Per pixel alpha blending) on accumulation blend and add optimizations.

PABE accumulation blend:
Idea is to achieve final output Cs when As < 1, we do this with manipulating Cd using the src1 output.
This can't be done with reverse subtraction as we want Cd to be 0 when As < 1.
Blend mix is excluded as no games were found, otherwise it can be added.

PABE Disable blending:
We can disable blending here as an optimization since alpha max is 128
which if alpha is 1 in the formula Cs*Alpha + Cd*(1 - Alpha) will give us a result of Cs.
This commit is contained in:
lightningterror 2024-08-20 15:40:50 +02:00
parent fe2a9fc2cd
commit 40d5c78573
6 changed files with 60 additions and 7 deletions

View File

@ -862,11 +862,17 @@ void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy)
// PABE // PABE
if (PS_PABE) if (PS_PABE)
{ {
// As_rgba needed for accumulation blend to manipulate Cd.
// No blending so early exit // No blending so early exit
if (As < 1.0f) if (As < 1.0f)
{
As_rgba.rgb = (float3)0.0f;
return; return;
} }
As_rgba.rgb = (float3)1.0f;
}
float4 RT = SW_BLEND_NEEDS_RT ? RtTexture.Load(int3(pos_xy, 0)) : (float4)0.0f; float4 RT = SW_BLEND_NEEDS_RT ? RtTexture.Load(int3(pos_xy, 0)) : (float4)0.0f;
if (PS_SHUFFLE && SW_BLEND_NEEDS_RT) if (PS_SHUFFLE && SW_BLEND_NEEDS_RT)

View File

@ -783,9 +783,15 @@ float As = As_rgba.a;
// PABE // PABE
#if PS_PABE #if PS_PABE
// As_rgba needed for accumulation blend to manipulate Cd.
// No blending so early exit // No blending so early exit
if (As < 1.0f) if (As < 1.0f)
{
As_rgba.rgb = vec3(0.0f);
return; return;
}
As_rgba.rgb = vec3(1.0f);
#endif #endif
#if SW_BLEND_NEEDS_RT #if SW_BLEND_NEEDS_RT

View File

@ -1050,9 +1050,15 @@ void ps_blend(inout vec4 Color, inout vec4 As_rgba)
// PABE // PABE
#if PS_PABE #if PS_PABE
// As_rgba needed for accumulation blend to manipulate Cd
// No blending so early exit // No blending so early exit
if (As < 1.0f) if (As < 1.0f)
{
As_rgba.rgb = vec3(0.0f);
return; return;
}
As_rgba.rgb = vec3(1.0f);
#endif #endif
#if PS_FEEDBACK_LOOP_IS_NEEDED #if PS_FEEDBACK_LOOP_IS_NEEDED

View File

@ -4420,13 +4420,35 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, const boo
// Per pixel alpha blending // Per pixel alpha blending
if (m_draw_env->PABE.PABE && GetAlphaMinMax().min < 128) if (m_draw_env->PABE.PABE && GetAlphaMinMax().min < 128)
{ {
// Breath of Fire Dragon Quarter, Strawberry Shortcake, Super Robot Wars, Cartoon Network Racing, Simple 2000 Series Vol.81, SOTC, Super Robot Wars. // Breath of Fire Dragon Quarter, Strawberry Shortcake, Super Robot Wars, Cartoon Network Racing, Simple 2000 Series Vol.81, SOTC.
if (sw_blending) if (GetAlphaMinMax().max == 128 && m_conf.ps.blend_a == 0 && ((blend.dst == GSDevice::INV_SRC1_COLOR)
|| (blend.dst == GSDevice::INV_DST_ALPHA)
|| (blend.dst == GSDevice::INV_CONST_COLOR)))
{ {
GL_INS("PABE mode ENABLED"); // PABE disable blending:
if (features.texture_barrier) // We can disable blending here as an optimization since alpha max is 128
// which if alpha is 1 in the formula Cs*Alpha + Cd*(1 - Alpha) will give us a result of Cs.
m_conf.blend = {};
m_conf.ps.no_color1 = true;
m_conf.ps.blend_a = m_conf.ps.blend_b = m_conf.ps.blend_c = m_conf.ps.blend_d = 0;
return;
}
else if (sw_blending)
{ {
// Disable hw/sw blend and do pure sw blend with reading the framebuffer. if (accumulation_blend && (blend.op != GSDevice::OP_REV_SUBTRACT))
{
// PABE accumulation blend:
// Idea is to achieve final output Cs when As < 1, we do this with manipulating Cd using the src1 output.
// This can't be done with reverse subtraction as we want Cd to be 0 when As < 1.
// TODO: Blend mix is excluded as no games were found, otherwise it can be added.
m_conf.ps.pabe = 1;
}
else if (features.texture_barrier)
{
// PABE sw blend:
// Disable hw/sw mix and do pure sw blend with reading the framebuffer.
color_dest_blend = false; color_dest_blend = false;
accumulation_blend = false; accumulation_blend = false;
blend_mix = false; blend_mix = false;
@ -4441,8 +4463,11 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, const boo
} }
else else
{ {
// PABE sw blend:
m_conf.ps.pabe = !(accumulation_blend || blend_mix); m_conf.ps.pabe = !(accumulation_blend || blend_mix);
} }
GL_INS("PABE mode %s", m_conf.ps.pabe ? "ENABLED" : "DISABLED");
} }
} }
@ -4516,9 +4541,13 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, const boo
m_conf.ps.blend_b = 2; m_conf.ps.blend_b = 2;
} }
} }
else if (m_conf.ps.pabe)
{
m_conf.blend.dst_factor = GSDevice::SRC1_COLOR;
}
// Dual source output not needed (accumulation blend replaces it with ONE). // Dual source output not needed (accumulation blend replaces it with ONE).
m_conf.ps.no_color1 = true; m_conf.ps.no_color1 = (m_conf.ps.pabe == 0);
} }
else if (blend_mix) else if (blend_mix)
{ {

View File

@ -925,11 +925,17 @@ struct PSMain
// PABE // PABE
if (PS_PABE) if (PS_PABE)
{ {
// As_rgba needed for accumulation blend to manipulate Cd.
// No blending so early exit // No blending so early exit
if (As < 1.f) if (As < 1.f)
{
As_rgba.rgb = float3(0.f);
return; return;
} }
As_rgba.rgb = float3(1.f);
}
float Ad = PS_RTA_CORRECTION ? trunc(current_color.a * 128.1f) / 128.f : trunc(current_color.a * 255.1f) / 128.f; float Ad = PS_RTA_CORRECTION ? trunc(current_color.a * 128.1f) / 128.f : trunc(current_color.a * 255.1f) / 128.f;
if (PS_SHUFFLE && NEEDS_RT) if (PS_SHUFFLE && NEEDS_RT)

View File

@ -3,4 +3,4 @@
/// Version number for GS and other shaders. Increment whenever any of the contents of the /// Version number for GS and other shaders. Increment whenever any of the contents of the
/// shaders change, to invalidate the cache. /// shaders change, to invalidate the cache.
static constexpr u32 SHADER_CACHE_VERSION = 55; static constexpr u32 SHADER_CACHE_VERSION = 56;