Skip to content

Commit

Permalink
GS/HW: Extend blend second pass to more blend formulas.
Browse files Browse the repository at this point in the history
`Cs + Cd*Ad, Cs - Cd*Ad, Cd*(1 - Ad), Cs*(1 + Ad), Cs*(1 - Ad).`
  • Loading branch information
lightningterror committed Apr 21, 2024
1 parent 7dd7345 commit 1a41518
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 20 deletions.
5 changes: 5 additions & 0 deletions bin/resources/shaders/dx11/tfx.fx
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,11 @@ void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy)
float color_compensate = 255.0f / max(128.0f, max_color);
Color.rgb *= (float3)color_compensate;
}
else if (PS_BLEND_HW == 4)
{
// Needed for Cd * (1 - Ad)
Color.rgb = (float3)128.0f;
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions bin/resources/shaders/opengl/tfx_fs.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,9 @@ float As = As_rgba.a;
float max_color = max(max(Color.r, Color.g), Color.b);
float color_compensate = 255.0f / max(128.0f, max_color);
Color.rgb *= vec3(color_compensate);
#elif PS_BLEND_HW == 4
// Needed for Cd * (1 - Ad)
Color.rgb = vec3(128.0f);
#endif

#endif
Expand Down
3 changes: 3 additions & 0 deletions bin/resources/shaders/vulkan/tfx.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -1200,6 +1200,9 @@ void ps_blend(inout vec4 Color, inout vec4 As_rgba)
float max_color = max(max(Color.r, Color.g), Color.b);
float color_compensate = 255.0f / max(128.0f, max_color);
Color.rgb *= vec3(color_compensate);
#elif PS_BLEND_HW == 4
// Needed for Cd * (1 - Ad)
Color.rgb = vec3(128.0f);
#endif
#endif
}
Expand Down
10 changes: 5 additions & 5 deletions pcsx2/GS/Renderers/Common/GSDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -889,7 +889,7 @@ const std::array<HWBlend, 3*3*3*3> GSDevice::m_blendMap =
{ BLEND_NO_REC , OP_ADD , CONST_ONE , CONST_ZERO} , // 0200: (Cs - 0)*As + Cs ==> Cs*(As + 1)
{ BLEND_ACCU , OP_ADD , SRC1_COLOR , CONST_ONE} , // 0201: (Cs - 0)*As + Cd ==> Cs*As + Cd
{ BLEND_NO_REC , OP_ADD , SRC1_COLOR , CONST_ZERO} , // 0202: (Cs - 0)*As + 0 ==> Cs*As
{ BLEND_A_MAX , OP_ADD , CONST_ONE , CONST_ZERO} , // 0210: (Cs - 0)*Ad + Cs ==> Cs*(Ad + 1)
{ BLEND_A_MAX | BLEND_HW8 , OP_ADD , CONST_ONE , CONST_ZERO} , // 0210: (Cs - 0)*Ad + Cs ==> Cs*(Ad + 1)
{ BLEND_HW3 , OP_ADD , DST_ALPHA , CONST_ONE} , // 0211: (Cs - 0)*Ad + Cd ==> Cs*Ad + Cd
{ BLEND_HW3 , OP_ADD , DST_ALPHA , CONST_ZERO} , // 0212: (Cs - 0)*Ad + 0 ==> Cs*Ad
{ BLEND_NO_REC , OP_ADD , CONST_ONE , CONST_ZERO} , // 0220: (Cs - 0)*F + Cs ==> Cs*(F + 1)
Expand All @@ -916,7 +916,7 @@ const std::array<HWBlend, 3*3*3*3> GSDevice::m_blendMap =
{ BLEND_HW4 , OP_ADD , CONST_ONE , SRC1_COLOR} , // 1200: (Cd - 0)*As + Cs ==> Cs + Cd*As
{ BLEND_HW1 , OP_ADD , DST_COLOR , SRC1_COLOR} , // 1201: (Cd - 0)*As + Cd ==> Cd*(1 + As)
{ BLEND_HW2 , OP_ADD , DST_COLOR , SRC1_COLOR} , // 1202: (Cd - 0)*As + 0 ==> Cd*As
{ 0 , OP_ADD , CONST_ONE , DST_ALPHA} , // 1210: (Cd - 0)*Ad + Cs ==> Cs + Cd*Ad
{ BLEND_HW6 , OP_ADD , CONST_ONE , DST_ALPHA} , // 1210: (Cd - 0)*Ad + Cs ==> Cs + Cd*Ad
{ BLEND_HW1 , OP_ADD , DST_COLOR , DST_ALPHA} , // 1211: (Cd - 0)*Ad + Cd ==> Cd*(1 + Ad)
{ BLEND_HW5 , OP_ADD , CONST_ZERO , DST_ALPHA} , // 1212: (Cd - 0)*Ad + 0 ==> Cd*Ad
{ BLEND_HW4 , OP_ADD , CONST_ONE , CONST_COLOR} , // 1220: (Cd - 0)*F + Cs ==> Cs + Cd*F
Expand All @@ -925,7 +925,7 @@ const std::array<HWBlend, 3*3*3*3> GSDevice::m_blendMap =
{ BLEND_NO_REC , OP_ADD , INV_SRC1_COLOR , CONST_ZERO} , // 2000: (0 - Cs)*As + Cs ==> Cs*(1 - As)
{ BLEND_ACCU , OP_REV_SUBTRACT , SRC1_COLOR , CONST_ONE} , // 2001: (0 - Cs)*As + Cd ==> Cd - Cs*As
{ BLEND_NO_REC , OP_REV_SUBTRACT , SRC1_COLOR , CONST_ZERO} , // 2002: (0 - Cs)*As + 0 ==> 0 - Cs*As
{ 0 , OP_ADD , INV_DST_ALPHA , CONST_ZERO} , // 2010: (0 - Cs)*Ad + Cs ==> Cs*(1 - Ad)
{ BLEND_HW9 , OP_ADD , INV_DST_ALPHA , CONST_ZERO} , // 2010: (0 - Cs)*Ad + Cs ==> Cs*(1 - Ad)
{ BLEND_HW3 , OP_REV_SUBTRACT , DST_ALPHA , CONST_ONE} , // 2011: (0 - Cs)*Ad + Cd ==> Cd - Cs*Ad
{ 0 , OP_REV_SUBTRACT , DST_ALPHA , CONST_ZERO} , // 2012: (0 - Cs)*Ad + 0 ==> 0 - Cs*Ad
{ BLEND_NO_REC , OP_ADD , INV_CONST_COLOR , CONST_ZERO} , // 2020: (0 - Cs)*F + Cs ==> Cs*(1 - F)
Expand All @@ -934,8 +934,8 @@ const std::array<HWBlend, 3*3*3*3> GSDevice::m_blendMap =
{ BLEND_HW4 , OP_SUBTRACT , CONST_ONE , SRC1_COLOR} , // 2100: (0 - Cd)*As + Cs ==> Cs - Cd*As
{ 0 , OP_ADD , CONST_ZERO , INV_SRC1_COLOR} , // 2101: (0 - Cd)*As + Cd ==> Cd*(1 - As)
{ 0 , OP_SUBTRACT , CONST_ZERO , SRC1_COLOR} , // 2102: (0 - Cd)*As + 0 ==> 0 - Cd*As
{ 0 , OP_SUBTRACT , CONST_ONE , DST_ALPHA} , // 2110: (0 - Cd)*Ad + Cs ==> Cs - Cd*Ad
{ 0 , OP_ADD , CONST_ZERO , INV_DST_ALPHA} , // 2111: (0 - Cd)*Ad + Cd ==> Cd*(1 - Ad)
{ BLEND_HW6 , OP_SUBTRACT , CONST_ONE , DST_ALPHA} , // 2110: (0 - Cd)*Ad + Cs ==> Cs - Cd*Ad
{ BLEND_HW7 , OP_ADD , CONST_ZERO , INV_DST_ALPHA} , // 2111: (0 - Cd)*Ad + Cd ==> Cd*(1 - Ad)
{ 0 , OP_SUBTRACT , CONST_ZERO , DST_ALPHA} , // 2112: (0 - Cd)*Ad + 0 ==> 0 - Cd*Ad
{ BLEND_HW4 , OP_SUBTRACT , CONST_ONE , CONST_COLOR} , // 2120: (0 - Cd)*F + Cs ==> Cs - Cd*F
{ 0 , OP_ADD , CONST_ZERO , INV_CONST_COLOR} , // 2121: (0 - Cd)*F + Cd ==> Cd*(1 - F)
Expand Down
18 changes: 11 additions & 7 deletions pcsx2/GS/Renderers/Common/GSDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,12 +227,16 @@ enum HWBlendFlags
BLEND_HW3 = 0x8, // Multiply Cs by (255/128) to compensate for wrong Ad/255 value, should be Ad/128
BLEND_HW4 = 0x10, // HW rendering is split in 2 passes
BLEND_HW5 = 0x20, // HW rendering is split in 2 passes
BLEND_MIX1 = 0x40, // Mix of hw and sw, do Cs*F or Cs*As in shader
BLEND_MIX2 = 0x80, // Mix of hw and sw, do Cs*(As + 1) or Cs*(F + 1) in shader
BLEND_MIX3 = 0x100, // Mix of hw and sw, do Cs*(1 - As) or Cs*(1 - F) in shader
BLEND_ACCU = 0x200, // Allow to use a mix of SW and HW blending to keep the best of the 2 worlds
BLEND_NO_REC = 0x400, // Doesn't require sampling of the RT as a texture
BLEND_A_MAX = 0x800, // Impossible blending uses coeff bigger than 1
BLEND_HW6 = 0x40, // HW rendering is split in 2 passes
BLEND_HW7 = 0x80, // HW rendering is split in 2 passes
BLEND_HW8 = 0x100, // HW rendering is split in 2 passes
BLEND_HW9 = 0x200, // HW rendering is split in 2 passes
BLEND_MIX1 = 0x400, // Mix of hw and sw, do Cs*F or Cs*As in shader
BLEND_MIX2 = 0x800, // Mix of hw and sw, do Cs*(As + 1) or Cs*(F + 1) in shader
BLEND_MIX3 = 0x1000, // Mix of hw and sw, do Cs*(1 - As) or Cs*(1 - F) in shader
BLEND_ACCU = 0x2000, // Allow to use a mix of SW and HW blending to keep the best of the 2 worlds
BLEND_NO_REC = 0x4000, // Doesn't require sampling of the RT as a texture
BLEND_A_MAX = 0x8000, // Impossible blending uses coeff bigger than 1
};

// Determines the HW blend function for DX11/OGL
Expand Down Expand Up @@ -332,7 +336,7 @@ struct alignas(16) GSHWDrawConfig
u32 blend_c : 2;
u32 blend_d : 2;
u32 fixed_one_a : 1;
u32 blend_hw : 2;
u32 blend_hw : 3;
u32 a_masked : 1;
u32 hdr : 1;
u32 rta_correction : 1;
Expand Down
56 changes: 49 additions & 7 deletions pcsx2/GS/Renderers/HW/GSRendererHW.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4050,7 +4050,6 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT
// HW blend can be done in multiple passes when there's no overlap.
// Blend second pass is only useful when texture barriers aren't supported.
// Speed wise Texture barriers > blend second pass > texture copies.
// TODO: 24bit and 32bit formats on clamp 1 can always prefer blend second pass depending on the blend equations.
const bool blend_second_pass_support = !features.texture_barrier && no_prim_overlap && is_basic_blend;
const bool bmix1_second_pass = blend_second_pass_support && blend_mix1 && (alpha_c0_high_max_one || alpha_c2_high_one) && m_conf.ps.blend_d == 2;
// We don't want to enable blend mix if we are doing a second pass, it's useless.
Expand Down Expand Up @@ -4464,9 +4463,52 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT
m_conf.blend_second_pass.blend_hw = 1;
m_conf.blend_second_pass.blend = {true, GSDevice::DST_COLOR, GSDevice::CONST_ONE, GSDevice::OP_ADD, GSDevice::CONST_ONE, GSDevice::CONST_ZERO, false, 0};
}

if (m_conf.ps.blend_c == 2 && m_conf.blend_second_pass.enable)
m_conf.cb_ps.TA_MaxDepth_Af.a = static_cast<float>(AFIX) / 128.0f;
else if (alpha_c1_high_no_rta_correct && (blend_flag & BLEND_HW6))
{
// Alpha = Ad.
// Cs + Cd*Alpha, Cs - Cd*Alpha.
// Render pass 1: Multiply Cs by 0.5, then do hw blend.
m_conf.ps.blend_c = 2;
AFIX = 64;
blend.src = GSDevice::CONST_COLOR;
// Render pass 2: Take result (Cd) from render pass 1 and double it.
m_conf.blend_second_pass.enable = true;
m_conf.blend_second_pass.blend_hw = 1;
m_conf.blend_second_pass.blend = {true, GSDevice::DST_COLOR, GSDevice::CONST_ONE, GSDevice::OP_ADD, GSDevice::CONST_ONE, GSDevice::CONST_ZERO, false, 0};
}
else if (alpha_c1_high_no_rta_correct && (blend_flag & BLEND_HW7))
{
// Alpha = Ad.
// Cd*(1 - Alpha).
// Render pass 1: Multiply Cd by 0.5, then do Cd - Cd*Alpha.
m_conf.ps.blend_hw = 4;
blend.src = GSDevice::DST_COLOR;
blend.dst = GSDevice::DST_ALPHA;
blend.op = GSDevice::OP_SUBTRACT;
// Render pass 2: Take result (Cd) from render pass 1 and double it.
m_conf.blend_second_pass.enable = true;
m_conf.blend_second_pass.blend_hw = 1;
m_conf.blend_second_pass.blend = {true, GSDevice::DST_COLOR, GSDevice::CONST_ONE, GSDevice::OP_ADD, GSDevice::CONST_ONE, GSDevice::CONST_ZERO, false, 0};
}
else if (blend_flag & BLEND_HW8)
{
// Alpha = Ad.
// Cs*(1 + Alpha).
// Render pass 1: Do Cs.
// Render pass 2: Try to double Cs, then take result (Cd) from render pass 1 and add Cs*Alpha to it.
m_conf.blend_second_pass.enable = true;
m_conf.blend_second_pass.blend_hw = 3;
m_conf.blend_second_pass.blend = {true, GSDevice::DST_ALPHA, GSDevice::CONST_ONE, blend_second_pass.op, GSDevice::CONST_ONE, GSDevice::CONST_ZERO, false, 0};
}
else if (alpha_c1_high_no_rta_correct && (blend_flag & BLEND_HW9))
{
// Alpha = Ad.
// Cs*(1 - Alpha).
// Render pass 1: Do Cs*(1 - Alpha).
// Render pass 2: Take result (Cd) from render pass 1 and subtract Cs*Alpha from it.
m_conf.blend_second_pass.enable = true;
m_conf.blend_second_pass.blend = {true, GSDevice::DST_ALPHA, GSDevice::CONST_ONE, GSDevice::OP_REV_SUBTRACT, GSDevice::CONST_ONE, GSDevice::CONST_ZERO, false, 0};
}
}

if (blend_flag & BLEND_HW1)
Expand All @@ -4475,16 +4517,16 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT
}
else if (blend_flag & BLEND_HW2)
{
if (m_conf.ps.blend_c == 2)
m_conf.cb_ps.TA_MaxDepth_Af.a = static_cast<float>(AFIX) / 128.0f;

m_conf.ps.blend_hw = 2;
}
else if (!m_conf.blend_second_pass.enable && alpha_c1_high_no_rta_correct && (blend_flag & BLEND_HW3))
{
m_conf.ps.blend_hw = 3;
}

if (m_conf.ps.blend_c == 2 && (m_conf.ps.blend_hw == 2 || m_conf.blend_second_pass.blend_hw == 2))
m_conf.cb_ps.TA_MaxDepth_Af.a = static_cast<float>(AFIX) / 128.0f;

const GSDevice::BlendFactor src_factor_alpha = m_conf.blend_second_pass.enable ? GSDevice::CONST_ZERO : GSDevice::CONST_ONE;
const GSDevice::BlendFactor dst_factor_alpha = m_conf.blend_second_pass.enable ? GSDevice::CONST_ONE : GSDevice::CONST_ZERO;
m_conf.blend = {true, blend.src, blend.dst, blend.op, src_factor_alpha, dst_factor_alpha, m_conf.ps.blend_c == 2, AFIX};
Expand Down
2 changes: 1 addition & 1 deletion pcsx2/ShaderCacheVersion.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@

/// Version number for GS and other shaders. Increment whenever any of the contents of the
/// shaders change, to invalidate the cache.
static constexpr u32 SHADER_CACHE_VERSION = 46;
static constexpr u32 SHADER_CACHE_VERSION = 47;

0 comments on commit 1a41518

Please sign in to comment.