diff --git a/bin/resources/shaders/dx11/tfx.fx b/bin/resources/shaders/dx11/tfx.fx index 4c5e5b8183..3491ec402f 100644 --- a/bin/resources/shaders/dx11/tfx.fx +++ b/bin/resources/shaders/dx11/tfx.fx @@ -701,29 +701,30 @@ float4 tfx(float4 T, float4 C) return C_out; } -void atst(float4 C) +bool atst(float4 C) { float a = C.a; - if(PS_ATST == 0) + if(PS_ATST == 1) { - // nothing to do - } - else if(PS_ATST == 1) - { - if (a > AREF) discard; + return (a <= AREF); } else if(PS_ATST == 2) { - if (a < AREF) discard; + return (a >= AREF); } else if(PS_ATST == 3) { - if (abs(a - AREF) > 0.5f) discard; + return (abs(a - AREF) <= 0.5f); } else if(PS_ATST == 4) { - if (abs(a - AREF) < 0.5f) discard; + return (abs(a - AREF) >= 0.5f); + } + else + { + // nothing to do + return true; } } @@ -786,8 +787,6 @@ float4 ps_color(PS_INPUT input) float4 C = tfx(T, input.c); - atst(C); - C = fog(C, input.t.z); return C; @@ -964,6 +963,12 @@ void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy) PS_OUTPUT ps_main(PS_INPUT input) { float4 C = ps_color(input); + bool atst_pass = atst(C); + +#if PS_AFAIL == 0 // KEEP or ATST off + if (!atst_pass) + discard; +#endif PS_OUTPUT output; @@ -1119,6 +1124,11 @@ PS_OUTPUT ps_main(PS_INPUT input) ps_fbmask(C, input.p.xy); +#if PS_AFAIL == 3 // RGB_ONLY + // Use alpha blend factor to determine whether to update A. + alpha_blend.a = float(atst_pass); +#endif + #if !PS_NO_COLOR output.c0.a = PS_RTA_CORRECTION ? C.a / 128.0f : C.a / 255.0f; output.c0.rgb = PS_HDR ? float3(C.rgb / 65535.0f) : C.rgb / 255.0f; diff --git a/bin/resources/shaders/opengl/tfx_fs.glsl b/bin/resources/shaders/opengl/tfx_fs.glsl index 25e2faca6b..3ff82e831f 100644 --- a/bin/resources/shaders/opengl/tfx_fs.glsl +++ b/bin/resources/shaders/opengl/tfx_fs.glsl @@ -640,20 +640,21 @@ vec4 tfx(vec4 T, vec4 C) return C_out; } -void atst(vec4 C) +bool atst(vec4 C) { float a = C.a; -#if (PS_ATST == 0) - // nothing to do -#elif (PS_ATST == 1) - if (a > AREF) discard; +#if (PS_ATST == 1) + return (a <= AREF); #elif (PS_ATST == 2) - if (a < AREF) discard; + return (a >= AREF); #elif (PS_ATST == 3) - if (abs(a - AREF) > 0.5f) discard; + return (abs(a - AREF) <= 0.5f); #elif (PS_ATST == 4) - if (abs(a - AREF) < 0.5f) discard; + return (abs(a - AREF) >= 0.5f); +#else + // nothing to do + return true; #endif } @@ -984,6 +985,12 @@ void ps_main() #endif vec4 C = ps_color(); + bool atst_pass = atst(C); + +#if PS_AFAIL == 0 // KEEP or ATST off + if (!atst_pass) + discard; +#endif // Must be done before alpha correction @@ -1102,6 +1109,11 @@ void ps_main() ps_fbmask(C); +#if PS_AFAIL == 3 // RGB_ONLY + // Use alpha blend factor to determine whether to update A. + alpha_blend.a = float(atst_pass); +#endif + #if !PS_NO_COLOR #if PS_RTA_CORRECTION SV_Target0.a = C.a / 128.0f; diff --git a/bin/resources/shaders/vulkan/tfx.glsl b/bin/resources/shaders/vulkan/tfx.glsl index 5f72020064..f6ba919271 100644 --- a/bin/resources/shaders/vulkan/tfx.glsl +++ b/bin/resources/shaders/vulkan/tfx.glsl @@ -260,6 +260,7 @@ void main() #define PS_TFX 0 #define PS_TCC 1 #define PS_ATST 1 +#define PS_AFAIL 0 #define PS_FOG 0 #define PS_BLEND_HW 0 #define PS_A_MASKED 0 @@ -885,29 +886,30 @@ vec4 tfx(vec4 T, vec4 C) return C_out; } -void atst(vec4 C) +bool atst(vec4 C) { float a = C.a; - #if (PS_ATST == 0) + #if (PS_ATST == 1) { - // nothing to do - } - #elif (PS_ATST == 1) - { - if (a > AREF) discard; + return (a <= AREF); } #elif (PS_ATST == 2) { - if (a < AREF) discard; + return (a >= AREF); } #elif (PS_ATST == 3) { - if (abs(a - AREF) > 0.5f) discard; + return (abs(a - AREF) <= 0.5f); } #elif (PS_ATST == 4) { - if (abs(a - AREF) < 0.5f) discard; + return (abs(a - AREF) >= 0.5f); + } + #else + { + // nothing to do + return true; } #endif } @@ -968,8 +970,6 @@ vec4 ps_color() vec4 C = tfx(T, vsIn.c); - atst(C); - C = fog(C, vsIn.t.z); return C; @@ -1238,6 +1238,12 @@ void main() #endif vec4 C = ps_color(); + bool atst_pass = atst(C); + +#if PS_AFAIL == 0 // KEEP or ATST off + if (!atst_pass) + discard; +#endif // Must be done before alpha correction @@ -1258,7 +1264,7 @@ void main() vec4 alpha_blend = vec4(C.a / 128.0f); #endif - // Correct the ALPHA value based on the output format + // Correct the ALPHA value based on the output format #if (PS_DST_FMT == FMT_16) float A_one = 128.0f; // alpha output will be 0x80 C.a = (PS_FBA != 0) ? A_one : step(128.0f, C.a) * A_one; @@ -1357,6 +1363,11 @@ void main() ps_fbmask(C); + #if PS_AFAIL == 3 // RGB_ONLY + // Use alpha blend factor to determine whether to update A. + alpha_blend.a = float(atst_pass); + #endif + #if !PS_NO_COLOR #if PS_RTA_CORRECTION o_col0.a = C.a / 128.0f; diff --git a/pcsx2/GS/GSDrawingContext.cpp b/pcsx2/GS/GSDrawingContext.cpp index dab334c885..d3542d01d1 100644 --- a/pcsx2/GS/GSDrawingContext.cpp +++ b/pcsx2/GS/GSDrawingContext.cpp @@ -1,9 +1,10 @@ -// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team +// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team // SPDX-License-Identifier: LGPL-3.0+ -#include "GSDrawingContext.h" -#include "GSGL.h" -#include "GS.h" +#include "GS/GSDrawingContext.h" +#include "GS/GSGL.h" +#include "GS/GS.h" +#include "GS/GSUtil.h" static int findmax(int tl, int br, int limit, int wm, int minuv, int maxuv) { @@ -258,14 +259,14 @@ void GSDrawingContext::Dump(const std::string& filename) fprintf(fp, "TEST\n" "\tATE:%u\n" - "\tATST:%u\n" + "\tATST:%s\n" "\tAREF:%u\n" - "\tAFAIL:%u\n" + "\tAFAIL:%s\n" "\tDATE:%u\n" "\tDATM:%u\n" "\tZTE:%u\n" "\tZTST:%u\n\n", - TEST.ATE, TEST.ATST, TEST.AREF, TEST.AFAIL, TEST.DATE, TEST.DATM, TEST.ZTE, TEST.ZTST); + TEST.ATE, GSUtil::GetATSTName(TEST.ATST), TEST.AREF, GSUtil::GetAFAILName(TEST.AFAIL), TEST.DATE, TEST.DATM, TEST.ZTE, TEST.ZTST); fprintf(fp, "FBA\n" diff --git a/pcsx2/GS/GSUtil.cpp b/pcsx2/GS/GSUtil.cpp index 39f6da6a0b..bac0dd8a2c 100644 --- a/pcsx2/GS/GSUtil.cpp +++ b/pcsx2/GS/GSUtil.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team +// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team // SPDX-License-Identifier: LGPL-3.0+ #include "GS/GS.h" @@ -7,6 +7,8 @@ #include "MultiISA.h" #include "common/StringUtil.h" +#include + #ifdef ENABLE_VULKAN #include "GS/Renderers/Vulkan/GSDeviceVK.h" #endif @@ -115,6 +117,19 @@ void GSUtil::Init() s_maps.Init(); } +const char* GSUtil::GetATSTName(u32 atst) +{ + static constexpr const char* names[] = { + "NEVER", "ALWAYS", "LESS", "LEQUAL", "EQUAL", "GEQUAL", "GREATER", "NOTEQUAL" }; + return (atst < std::size(names)) ? names[atst] : ""; +} + +const char* GSUtil::GetAFAILName(u32 afail) +{ + static constexpr const char* names[] = {"KEEP", "FB_ONLY", "ZB_ONLY", "RGB_ONLY"}; + return (afail < std::size(names)) ? names[afail] : ""; +} + GS_PRIM_CLASS GSUtil::GetPrimClass(u32 prim) { return (GS_PRIM_CLASS)s_maps.PrimClassField[prim]; diff --git a/pcsx2/GS/GSUtil.h b/pcsx2/GS/GSUtil.h index 20c4d048e9..cc10452acf 100644 --- a/pcsx2/GS/GSUtil.h +++ b/pcsx2/GS/GSUtil.h @@ -11,6 +11,9 @@ class GSUtil public: static void Init(); + static const char* GetATSTName(u32 atst); + static const char* GetAFAILName(u32 afail); + static GS_PRIM_CLASS GetPrimClass(u32 prim); static int GetVertexCount(u32 prim); static int GetClassVertexCount(u32 primclass); diff --git a/pcsx2/GS/Renderers/Common/GSDevice.h b/pcsx2/GS/Renderers/Common/GSDevice.h index aa5ed1d57d..c46ff45cb7 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.h +++ b/pcsx2/GS/Renderers/Common/GSDevice.h @@ -304,6 +304,7 @@ struct alignas(16) GSHWDrawConfig // Pixel test u32 date : 3; u32 atst : 3; + u32 afail : 2; // Color sampling u32 fst : 1; // Investigate to do it on the VS u32 tfx : 3; diff --git a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp index 1f56b716d7..056650638a 100644 --- a/pcsx2/GS/Renderers/DX11/GSDevice11.cpp +++ b/pcsx2/GS/Renderers/DX11/GSDevice11.cpp @@ -1667,6 +1667,7 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant sm.AddMacro("PS_TCC", sel.tcc); sm.AddMacro("PS_DATE", sel.date); sm.AddMacro("PS_ATST", sel.atst); + sm.AddMacro("PS_AFAIL", sel.afail); sm.AddMacro("PS_FOG", sel.fog); sm.AddMacro("PS_IIP", sel.iip); sm.AddMacro("PS_BLEND_HW", sel.blend_hw); diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp index dbe3ec0576..3db0083909 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp @@ -2821,6 +2821,7 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector& sm.AddMacro("PS_TCC", sel.tcc); sm.AddMacro("PS_DATE", sel.date); sm.AddMacro("PS_ATST", sel.atst); + sm.AddMacro("PS_AFAIL", sel.afail); sm.AddMacro("PS_FOG", sel.fog); sm.AddMacro("PS_IIP", sel.iip); sm.AddMacro("PS_BLEND_HW", sel.blend_hw); diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index 31fb04b6cf..b34f2bf0c7 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -2290,7 +2290,7 @@ void GSRendererHW::Draw() if (PRIM->FST) { pxAssert(lcm == 1); - pxAssert(((m_vt.m_min.t.uph(m_vt.m_max.t) == GSVector4::zero()).mask() & 3) == 3); // ratchet and clank (menu) + //pxAssert(((m_vt.m_min.t.uph(m_vt.m_max.t) == GSVector4::zero()).mask() & 3) == 3); // ratchet and clank (menu) lcm = 1; } @@ -5054,9 +5054,6 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta bool DATE_BARRIER = false; bool DATE_one = false; - const bool ate_first_pass = m_cached_ctx.TEST.DoFirstPass(); - const bool ate_second_pass = m_cached_ctx.TEST.DoSecondPass(); - ResetStates(); const float scale_factor = rt ? rt->GetScale() : ds->GetScale(); @@ -5443,6 +5440,80 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta } } + // Warning must be done after EmulateZbuffer + // Depth test is always true so it can be executed in 2 passes (no order required) unlike color. + // The idea is to compute first the color which is independent of the alpha test. And then do a 2nd + // pass to handle the depth based on the alpha test. + const bool ate_first_pass = m_cached_ctx.TEST.DoFirstPass(); + bool ate_second_pass = m_cached_ctx.TEST.DoSecondPass(); + bool ate_RGBA_then_Z = false; + bool ate_RGB_then_Z = false; + GL_INS("%sAlpha Test, ATST=%s, AFAIL=%s", (ate_first_pass && ate_second_pass) ? "Complex" : "", + GSUtil::GetATSTName(m_cached_ctx.TEST.ATST), GSUtil::GetAFAILName(m_cached_ctx.TEST.AFAIL)); + if (ate_first_pass && ate_second_pass) + { + const bool commutative_depth = (m_conf.depth.ztst == ZTST_GEQUAL && m_vt.m_eq.z) || (m_conf.depth.ztst == ZTST_ALWAYS) || !m_conf.depth.zwe; + const bool commutative_alpha = (m_context->ALPHA.C != 1) || !m_conf.colormask.wa; // when either Alpha Src or a constant, or not updating A + const u32 afail = m_cached_ctx.TEST.GetAFAIL(m_cached_ctx.FRAME.PSM); + + ate_RGBA_then_Z = (afail == AFAIL_FB_ONLY) && commutative_depth; + ate_RGB_then_Z = (afail == AFAIL_RGB_ONLY) && commutative_depth && commutative_alpha; + } + + if (ate_RGBA_then_Z) + { + GL_INS("Alternate ATE handling: ate_RGBA_then_Z"); + // Render all color but don't update depth + // ATE is disabled here + m_conf.depth.zwe = false; + } + else + { + float aref = m_conf.cb_ps.FogColor_AREF.a; + EmulateATST(aref, m_conf.ps, false); + + // avoid redundant cbuffer updates + m_conf.cb_ps.FogColor_AREF.a = aref; + m_conf.alpha_second_pass.ps_aref = aref; + + if (ate_RGB_then_Z) + { + GL_INS("Alternate ATE handling: ate_RGB_then_Z"); + + // Blending might be off, ensure it's enabled. + // We write the alpha pass/fail to SRC1_ALPHA, which is used to update A. + m_conf.ps.afail = AFAIL_RGB_ONLY; + m_conf.ps.no_color1 = false; + if (!m_conf.blend.enable) + { + m_conf.blend = GSHWDrawConfig::BlendState(true, GSDevice::CONST_ONE, GSDevice::CONST_ZERO, + GSDevice::OP_ADD, GSDevice::SRC1_ALPHA, GSDevice::INV_SRC1_ALPHA, false, 0); + } + else + { + m_conf.blend.src_factor_alpha = GSDevice::SRC1_ALPHA; + m_conf.blend.dst_factor_alpha = GSDevice::INV_SRC1_ALPHA; + } + + // If Z writes are on, unfortunately we can't single pass it. + // But we can write Z in the second pass instead. + ate_RGBA_then_Z = m_conf.depth.zwe; + ate_second_pass &= ate_RGBA_then_Z; + m_conf.depth.zwe = false; + + // Swap stencil DATE for PrimID DATE, for both Z on and off cases. + // Because we're making some pixels pass, but not update A, the stencil won't be synced. + if (DATE && !DATE_BARRIER && features.primitive_id) + { + if (!DATE_PRIMID) + GL_INS("Swap stencil DATE for PrimID, due to AFAIL"); + + DATE_one = false; + DATE_PRIMID = true; + } + } + } + // No point outputting colours if we're just writing depth. // We might still need the framebuffer for DATE, though. if (!rt || m_conf.colormask.wrgba == 0) @@ -5580,47 +5651,6 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta m_conf.cb_ps.FogColor_AREF = fc.blend32<8>(m_conf.cb_ps.FogColor_AREF); } - // Warning must be done after EmulateZbuffer - // Depth test is always true so it can be executed in 2 passes (no order required) unlike color. - // The idea is to compute first the color which is independent of the alpha test. And then do a 2nd - // pass to handle the depth based on the alpha test. - bool ate_RGBA_then_Z = false; - bool ate_RGB_then_ZA = false; - if (ate_first_pass && ate_second_pass) - { - GL_DBG("Complex Alpha Test"); - const bool commutative_depth = (m_conf.depth.ztst == ZTST_GEQUAL && m_vt.m_eq.z) || (m_conf.depth.ztst == ZTST_ALWAYS); - const bool commutative_alpha = (m_context->ALPHA.C != 1); // when either Alpha Src or a constant - - ate_RGBA_then_Z = m_cached_ctx.TEST.GetAFAIL(m_cached_ctx.FRAME.PSM) == AFAIL_FB_ONLY && commutative_depth; - ate_RGB_then_ZA = m_cached_ctx.TEST.GetAFAIL(m_cached_ctx.FRAME.PSM) == AFAIL_RGB_ONLY && commutative_depth && commutative_alpha; - } - - if (ate_RGBA_then_Z) - { - GL_DBG("Alternate ATE handling: ate_RGBA_then_Z"); - // Render all color but don't update depth - // ATE is disabled here - m_conf.depth.zwe = false; - } - else if (ate_RGB_then_ZA) - { - GL_DBG("Alternate ATE handling: ate_RGB_then_ZA"); - // Render RGB color but don't update depth/alpha - // ATE is disabled here - m_conf.depth.zwe = false; - m_conf.colormask.wa = false; - } - else - { - float aref = m_conf.cb_ps.FogColor_AREF.a; - EmulateATST(aref, m_conf.ps, false); - - // avoid redundant cbuffer updates - m_conf.cb_ps.FogColor_AREF.a = aref; - m_conf.alpha_second_pass.ps_aref = aref; - } - GSTexture* tex_copy = nullptr; if (tex) { @@ -5666,11 +5696,14 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta if (ate_second_pass) { pxAssert(!env.PABE.PABE); - memcpy(&m_conf.alpha_second_pass.ps, &m_conf.ps, sizeof(m_conf.ps)); - memcpy(&m_conf.alpha_second_pass.colormask, &m_conf.colormask, sizeof(m_conf.colormask)); - memcpy(&m_conf.alpha_second_pass.depth, &m_conf.depth, sizeof(m_conf.depth)); + std::memcpy(&m_conf.alpha_second_pass.ps, &m_conf.ps, sizeof(m_conf.ps)); + std::memcpy(&m_conf.alpha_second_pass.colormask, &m_conf.colormask, sizeof(m_conf.colormask)); + std::memcpy(&m_conf.alpha_second_pass.depth, &m_conf.depth, sizeof(m_conf.depth)); - if (ate_RGBA_then_Z || ate_RGB_then_ZA) + // Not doing single pass AFAIL. + m_conf.alpha_second_pass.ps.afail = AFAIL_KEEP; + + if (ate_RGBA_then_Z) { // Enable ATE as first pass to update the depth // of pixels that passed the alpha test @@ -5683,7 +5716,6 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta EmulateATST(m_conf.alpha_second_pass.ps_aref, m_conf.alpha_second_pass.ps, true); } - bool z = m_conf.depth.zwe; bool r = m_conf.colormask.wr; bool g = m_conf.colormask.wg; @@ -5706,12 +5738,6 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta z = !m_cached_ctx.ZBUF.ZMSK; r = g = b = a = false; } - else if (ate_RGB_then_ZA) - { - z = !m_cached_ctx.ZBUF.ZMSK; - a = (m_cached_ctx.FRAME.FBMSK & 0xFF000000) != 0xFF000000; - r = g = b = false; - } if (z || r || g || b || a) { @@ -5738,9 +5764,9 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta } // RenderHW always renders first pass, replace first pass with second - memcpy(&m_conf.ps, &m_conf.alpha_second_pass.ps, sizeof(m_conf.ps)); - memcpy(&m_conf.colormask, &m_conf.alpha_second_pass.colormask, sizeof(m_conf.colormask)); - memcpy(&m_conf.depth, &m_conf.alpha_second_pass.depth, sizeof(m_conf.depth)); + std::memcpy(&m_conf.ps, &m_conf.alpha_second_pass.ps, sizeof(m_conf.ps)); + std::memcpy(&m_conf.colormask, &m_conf.alpha_second_pass.colormask, sizeof(m_conf.colormask)); + std::memcpy(&m_conf.depth, &m_conf.alpha_second_pass.depth, sizeof(m_conf.depth)); m_conf.cb_ps.FogColor_AREF.a = m_conf.alpha_second_pass.ps_aref; m_conf.alpha_second_pass.enable = false; } diff --git a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm index 252d18cce4..3ea99feb75 100644 --- a/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm +++ b/pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm @@ -1803,6 +1803,7 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr setFnConstantB(m_fn_constants, pssel.fog, GSMTLConstantIndex_PS_FOG); setFnConstantI(m_fn_constants, pssel.date, GSMTLConstantIndex_PS_DATE); setFnConstantI(m_fn_constants, pssel.atst, GSMTLConstantIndex_PS_ATST); + setFnConstantI(m_fn_constants, pssel.afail, GSMTLConstantIndex_PS_AFAIL); setFnConstantI(m_fn_constants, pssel.tfx, GSMTLConstantIndex_PS_TFX); setFnConstantB(m_fn_constants, pssel.tcc, GSMTLConstantIndex_PS_TCC); setFnConstantI(m_fn_constants, pssel.wms, GSMTLConstantIndex_PS_WMS); diff --git a/pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h b/pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h index 708f8f88ac..8fa177776d 100644 --- a/pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h +++ b/pcsx2/GS/Renderers/Metal/GSMTLSharedHeader.h @@ -162,6 +162,7 @@ enum GSMTLFnConstants GSMTLConstantIndex_PS_FOG, GSMTLConstantIndex_PS_DATE, GSMTLConstantIndex_PS_ATST, + GSMTLConstantIndex_PS_AFAIL, GSMTLConstantIndex_PS_TFX, GSMTLConstantIndex_PS_TCC, GSMTLConstantIndex_PS_WMS, diff --git a/pcsx2/GS/Renderers/Metal/tfx.metal b/pcsx2/GS/Renderers/Metal/tfx.metal index c0b72b7af3..fd2c14618b 100644 --- a/pcsx2/GS/Renderers/Metal/tfx.metal +++ b/pcsx2/GS/Renderers/Metal/tfx.metal @@ -25,6 +25,7 @@ constant bool PS_FBA [[function_constant(GSMTLConstantIndex_PS_FB constant bool PS_FOG [[function_constant(GSMTLConstantIndex_PS_FOG)]]; constant uint PS_DATE [[function_constant(GSMTLConstantIndex_PS_DATE)]]; constant uint PS_ATST [[function_constant(GSMTLConstantIndex_PS_ATST)]]; +constant uint PS_AFAIL [[function_constant(GSMTLConstantIndex_PS_AFAIL)]]; constant uint PS_TFX [[function_constant(GSMTLConstantIndex_PS_TFX)]]; constant bool PS_TCC [[function_constant(GSMTLConstantIndex_PS_TCC)]]; constant uint PS_WMS [[function_constant(GSMTLConstantIndex_PS_WMS)]]; @@ -850,8 +851,7 @@ struct PSMain } float4 C = tfx(T, IIP ? in.c : in.fc); - if (!atst(C)) - discard_fragment(); + fog(C, in.t.z); return C; @@ -1054,6 +1054,9 @@ struct PSMain } float4 C = ps_color(); + bool atst_pass = atst(C); + if (PS_AFAIL == 0 && !atst_pass) + discard_fragment(); // Must be done before alpha correction @@ -1189,6 +1192,10 @@ struct PSMain ps_fbmask(C); + // Use alpha blend factor to determine whether to update A. + if (PS_AFAIL == 3) // RGB_ONLY + alpha_blend.a = float(atst_pass); + if (PS_COLOR0) out.c0.a = PS_RTA_CORRECTION ? C.a / 128.f : C.a / 255.f; out.c0.rgb = PS_HDR ? float3(C.rgb / 65535.f) : C.rgb / 255.f; diff --git a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp index 32f775864f..4138ed01e5 100644 --- a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp +++ b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp @@ -1348,6 +1348,7 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel) + fmt::format("#define PS_TFX {}\n", sel.tfx) + fmt::format("#define PS_TCC {}\n", sel.tcc) + fmt::format("#define PS_ATST {}\n", sel.atst) + + fmt::format("#define PS_AFAIL {}\n", sel.afail) + fmt::format("#define PS_FOG {}\n", sel.fog) + fmt::format("#define PS_BLEND_HW {}\n", sel.blend_hw) + fmt::format("#define PS_A_MASKED {}\n", sel.a_masked) diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp index fa68a327ae..4b591a016b 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp @@ -4791,6 +4791,7 @@ VkShaderModule GSDeviceVK::GetTFXFragmentShader(const GSHWDrawConfig::PSSelector AddMacro(ss, "PS_TFX", sel.tfx); AddMacro(ss, "PS_TCC", sel.tcc); AddMacro(ss, "PS_ATST", sel.atst); + AddMacro(ss, "PS_AFAIL", sel.afail); AddMacro(ss, "PS_FOG", sel.fog); AddMacro(ss, "PS_BLEND_HW", sel.blend_hw); AddMacro(ss, "PS_A_MASKED", sel.a_masked); diff --git a/pcsx2/ShaderCacheVersion.h b/pcsx2/ShaderCacheVersion.h index 27ccd138c8..f73a803376 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 = 43; +static constexpr u32 SHADER_CACHE_VERSION = 44;