From 40d5c785735670c4c6c7f694a7a0620158822bf2 Mon Sep 17 00:00:00 2001 From: lightningterror <18107717+lightningterror@users.noreply.github.com> Date: Tue, 20 Aug 2024 15:40:50 +0200 Subject: [PATCH] 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. --- bin/resources/shaders/dx11/tfx.fx | 6 ++++ bin/resources/shaders/opengl/tfx_fs.glsl | 6 ++++ bin/resources/shaders/vulkan/tfx.glsl | 6 ++++ pcsx2/GS/Renderers/HW/GSRendererHW.cpp | 41 ++++++++++++++++++++---- pcsx2/GS/Renderers/Metal/tfx.metal | 6 ++++ pcsx2/ShaderCacheVersion.h | 2 +- 6 files changed, 60 insertions(+), 7 deletions(-) diff --git a/bin/resources/shaders/dx11/tfx.fx b/bin/resources/shaders/dx11/tfx.fx index 47b7a0478e..fb6811677b 100644 --- a/bin/resources/shaders/dx11/tfx.fx +++ b/bin/resources/shaders/dx11/tfx.fx @@ -862,9 +862,15 @@ void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy) // PABE if (PS_PABE) { + // As_rgba needed for accumulation blend to manipulate Cd. // No blending so early exit if (As < 1.0f) + { + As_rgba.rgb = (float3)0.0f; return; + } + + As_rgba.rgb = (float3)1.0f; } float4 RT = SW_BLEND_NEEDS_RT ? RtTexture.Load(int3(pos_xy, 0)) : (float4)0.0f; diff --git a/bin/resources/shaders/opengl/tfx_fs.glsl b/bin/resources/shaders/opengl/tfx_fs.glsl index fa53825d1e..dd3f8cb745 100644 --- a/bin/resources/shaders/opengl/tfx_fs.glsl +++ b/bin/resources/shaders/opengl/tfx_fs.glsl @@ -783,9 +783,15 @@ float As = As_rgba.a; // PABE #if PS_PABE + // As_rgba needed for accumulation blend to manipulate Cd. // No blending so early exit if (As < 1.0f) + { + As_rgba.rgb = vec3(0.0f); return; + } + + As_rgba.rgb = vec3(1.0f); #endif #if SW_BLEND_NEEDS_RT diff --git a/bin/resources/shaders/vulkan/tfx.glsl b/bin/resources/shaders/vulkan/tfx.glsl index 2cc8b1e07d..70661deb13 100644 --- a/bin/resources/shaders/vulkan/tfx.glsl +++ b/bin/resources/shaders/vulkan/tfx.glsl @@ -1050,9 +1050,15 @@ void ps_blend(inout vec4 Color, inout vec4 As_rgba) // PABE #if PS_PABE + // As_rgba needed for accumulation blend to manipulate Cd // No blending so early exit if (As < 1.0f) + { + As_rgba.rgb = vec3(0.0f); return; + } + + As_rgba.rgb = vec3(1.0f); #endif #if PS_FEEDBACK_LOOP_IS_NEEDED diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index daa7d4ed15..33f147ae9d 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -4420,13 +4420,35 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, const boo // Per pixel alpha blending 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. - if (sw_blending) + // Breath of Fire Dragon Quarter, Strawberry Shortcake, Super Robot Wars, Cartoon Network Racing, Simple 2000 Series Vol.81, SOTC. + 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"); - if (features.texture_barrier) + // 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. + 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) + { + if (accumulation_blend && (blend.op != GSDevice::OP_REV_SUBTRACT)) { - // Disable hw/sw blend and do pure sw blend with reading the framebuffer. + // 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; accumulation_blend = false; blend_mix = false; @@ -4441,8 +4463,11 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, const boo } else { + // PABE sw blend: 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; } } + 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). - m_conf.ps.no_color1 = true; + m_conf.ps.no_color1 = (m_conf.ps.pabe == 0); } else if (blend_mix) { diff --git a/pcsx2/GS/Renderers/Metal/tfx.metal b/pcsx2/GS/Renderers/Metal/tfx.metal index fabe2653aa..a13c6cdb30 100644 --- a/pcsx2/GS/Renderers/Metal/tfx.metal +++ b/pcsx2/GS/Renderers/Metal/tfx.metal @@ -925,9 +925,15 @@ struct PSMain // PABE if (PS_PABE) { + // As_rgba needed for accumulation blend to manipulate Cd. // No blending so early exit if (As < 1.f) + { + As_rgba.rgb = float3(0.f); 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; diff --git a/pcsx2/ShaderCacheVersion.h b/pcsx2/ShaderCacheVersion.h index 44d799958f..f157d82388 100644 --- a/pcsx2/ShaderCacheVersion.h +++ b/pcsx2/ShaderCacheVersion.h @@ -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 = 55; +static constexpr u32 SHADER_CACHE_VERSION = 56;