From 017b4d07daf77527bdaa61d969c6fda9b36d25ed Mon Sep 17 00:00:00 2001 From: TJnotJT Date: Sun, 28 Jun 2026 21:14:04 -0400 Subject: [PATCH] GS/HW: Improve ROV discard behavior. Try to avoid color/depth write when it's discarded. --- bin/resources/shaders/dx11/tfx.fx | 92 +++++++++++++-------------- bin/resources/shaders/vulkan/tfx.glsl | 32 ++++++---- pcsx2/ShaderCacheVersion.h | 2 +- 3 files changed, 62 insertions(+), 64 deletions(-) diff --git a/bin/resources/shaders/dx11/tfx.fx b/bin/resources/shaders/dx11/tfx.fx index 0c0d4a291a..80962b378c 100644 --- a/bin/resources/shaders/dx11/tfx.fx +++ b/bin/resources/shaders/dx11/tfx.fx @@ -192,7 +192,7 @@ struct PS_INPUT #ifdef PIXEL_SHADER -struct PS_OUTPUT_REAL +struct PS_OUTPUT { #define NUM_RTS 0 @@ -231,20 +231,6 @@ struct PS_OUTPUT_REAL #undef NUM_RTS }; -struct PS_OUTPUT -{ -#if !PS_NO_COLOR - #if PS_DATE == 1 || PS_DATE == 2 - float c; - #else - float4 c0; - #if !PS_NO_COLOR1 - float4 c1; - #endif - #endif -#endif -}; - Texture2D Texture : register(t0); Texture2D Palette : register(t1); #if !PS_ROV_COLOR @@ -1326,13 +1312,17 @@ void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy) #endif #if PS_ROV_COLOR || PS_ROV_DEPTH -#define DISCARD rov_discard = true + #define DISCARD { rov_discard_color = true; rov_discard_depth = true; } + #define DISCARD_COLOR rov_discard_color = true + #define DISCARD_DEPTH rov_discard_depth = true #else -#define DISCARD discard + #define DISCARD discard + #define DISCARD_COLOR o_col0 = RtLoad(input.p.xy) + #define DISCARD_DEPTH input.p.z = DepthLoad(input.p.xy) #endif #if (PS_RETURN_COLOR || PS_RETURN_DEPTH) -PS_OUTPUT_REAL ps_main(PS_INPUT input) +PS_OUTPUT ps_main(PS_INPUT input) #else void ps_main(PS_INPUT input) #endif @@ -1351,7 +1341,8 @@ void ps_main(PS_INPUT input) #endif #if PS_ROV_COLOR || PS_ROV_DEPTH - bool rov_discard = false; + bool rov_discard_color = false; + bool rov_discard_depth = false; #endif // Use ROV discard macro for since we cannot do @@ -1458,19 +1449,29 @@ if (bad) discard; #endif - PS_OUTPUT output; + // Output values +#if !PS_NO_COLOR + #if PS_DATE == 1 || PS_DATE == 2 + float o_col0; + #else + float4 o_col0; + #if !PS_NO_COLOR1 + float4 o_col1; + #endif + #endif +#endif // Get first primitive that will write a failling alpha value #if PS_DATE == 1 // DATM == 0 // Pixel with alpha equal to 1 will failed (128-255) - output.c = (C.a > 127.5f) ? float(input.primid) : float(0x7FFFFFFF); + o_col0 = (C.a > 127.5f) ? float(input.primid) : float(0x7FFFFFFF); #elif PS_DATE == 2 // DATM == 1 // Pixel with alpha equal to 0 will failed (0-127) - output.c = (C.a < 127.5f) ? float(input.primid) : float(0x7FFFFFFF); + o_col0 = (C.a < 127.5f) ? float(input.primid) : float(0x7FFFFFFF); #else // Not primid DATE setup @@ -1547,26 +1548,26 @@ if (bad) // Output color scaling #if !PS_NO_COLOR - output.c0.a = PS_RTA_CORRECTION ? C.a / 128.0f : C.a / 255.0f; - output.c0.rgb = PS_COLCLIP_HW ? float3(C.rgb / 65535.0f) : C.rgb / 255.0f; + o_col0.a = PS_RTA_CORRECTION ? C.a / 128.0f : C.a / 255.0f; + o_col0.rgb = PS_COLCLIP_HW ? float3(C.rgb / 65535.0f) : C.rgb / 255.0f; #if !PS_NO_COLOR1 - output.c1 = alpha_blend; + o_col1 = alpha_blend; #endif #endif // !PS_NO_COLOR // Alpha test with feedback #if PS_AFAIL == AFAIL_FB_ONLY if (!atst_pass) - input.p.z = DepthLoad(input.p.xy); + DISCARD_DEPTH; #elif PS_AFAIL == AFAIL_ZB_ONLY if (!atst_pass) - output.c0 = RtLoad(input.p.xy); + DISCARD_COLOR; #elif PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z if (!atst_pass) { - output.c0.a = RtLoad(input.p.xy).a; + o_col0.a = RtLoad(input.p.xy).a; // discard alpha #if PS_AFAIL == AFAIL_RGB_ONLY_SW_Z - input.p.z = DepthLoad(input.p.xy); + DISCARD_DEPTH; #endif } #endif @@ -1579,46 +1580,39 @@ if (bad) #if PS_AA1 == PS_AA1_TRIANGLE_SW_Z if (!bool(input.interior)) - input.p.z = DepthLoad(input.p.xy); // No depth update for triangle edges. + DISCARD_DEPTH; // No depth update for triangle edges. #endif #if (PS_RETURN_COLOR || PS_RETURN_DEPTH) - // Output struct with the actual system values output semantics. - PS_OUTPUT_REAL output_real; + PS_OUTPUT output; #endif // Color write back #if PS_RETURN_COLOR - #if PS_DATE == 1 || PS_DATE == 2 - output_real.c = output.c; - #else - output_real.c0 = output.c0; - #if !PS_NO_COLOR1 - output_real.c1 = output.c1; - #endif + output.c0 = o_col0; + #if !PS_NO_COLOR1 + output.c1 = o_col1; #endif #elif PS_RETURN_COLOR_ROV - output.c0 = (rov_discard | (FbMask == 0xFFu)) ? RtLoad(input.p.xy) : output.c0; - - RtWrite(input.p.xy, output.c0); + o_col0 = (FbMask == 0xFFu) ? RtLoad(input.p.xy) : o_col0; // channel masking + if (!rov_discard_color) + RtWrite(input.p.xy, o_col0); #endif // Depth write back #if PS_RETURN_DEPTH - output_real.depth = input.p.z; + output.depth = input.p.z; #if SW_DEPTH && PS_NO_COLOR1 && PS_DEPTH_FEEDBACK_SUPPORT == 2 // Output color clone for feedback. - output_real.depth_color = input.p.z; + output.depth_color = input.p.z; #endif #elif PS_RETURN_DEPTH_ROV - #if SW_DEPTH - input.p.z = rov_discard ? DepthLoad(input.p.xy) : input.p.z; - #endif - DepthWrite(input.p.xy, input.p.z); + if (!rov_discard_depth) + DepthWrite(input.p.xy, input.p.z); #endif #if (PS_RETURN_COLOR || PS_RETURN_DEPTH) - return output_real; + return output; #endif } diff --git a/bin/resources/shaders/vulkan/tfx.glsl b/bin/resources/shaders/vulkan/tfx.glsl index dba4f71e71..a1adbb2d91 100644 --- a/bin/resources/shaders/vulkan/tfx.glsl +++ b/bin/resources/shaders/vulkan/tfx.glsl @@ -1720,9 +1720,13 @@ layout(early_fragment_tests) in; #endif #if PS_ROV_COLOR || PS_ROV_DEPTH -#define DISCARD rov_discard = true + #define DISCARD { rov_discard_color = true; rov_discard_depth = true; } + #define DISCARD_COLOR rov_discard_color = true + #define DISCARD_DEPTH rov_discard_depth = true #else -#define DISCARD discard + #define DISCARD discard + #define DISCARD_COLOR o_col0 = sample_from_rt() + #define DISCARD_DEPTH input_z = sample_from_depth() #endif void main() @@ -1747,7 +1751,8 @@ void main() #endif #if PS_ROV_COLOR || PS_ROV_DEPTH - bool rov_discard = gl_HelperInvocation; + bool rov_discard_color = gl_HelperInvocation; + bool rov_discard_depth = gl_HelperInvocation; #endif #if PS_ZTST == ZTST_GEQUAL @@ -1940,16 +1945,16 @@ void main() // Alpha test with feedback #if PS_AFAIL == AFAIL_FB_ONLY if (!atst_pass) - input_z = sample_from_depth(); + DISCARD_DEPTH; #elif PS_AFAIL == AFAIL_ZB_ONLY if (!atst_pass) - o_col0 = sample_from_rt(); + DISCARD_COLOR; #elif (PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z) if (!atst_pass) { - o_col0.a = sample_from_rt().a; + o_col0.a = sample_from_rt().a; // discard alpha #if PS_AFAIL == AFAIL_RGB_ONLY_SW_Z - input_z = sample_from_depth(); + DISCARD_DEPTH; #endif } #endif @@ -1961,24 +1966,23 @@ void main() #if PS_AA1 == PS_AA1_TRIANGLE_SW_Z if (!bool(vsIn.interior)) - input_z = sample_from_depth(); // No depth update for triangle edges. + DISCARD_DEPTH; // No depth update for triangle edges. #endif // Writing back color (result already written to o_col0 for non-ROV) #if PS_RETURN_COLOR_ROV - bvec4 discard_channels = bvec4(uvec4(rov_discard) | uvec4(equal(FbMask, uvec4(0xFFu)))); - o_col0 = mix(o_col0, sample_from_rt(), discard_channels); + o_col0 = mix(o_col0, sample_from_rt(), equal(FbMask, uvec4(0xFFu))); // channel masking - imageStore(RtImageRov, ivec2(gl_FragCoord.xy), o_col0); + if (!rov_discard_color) + imageStore(RtImageRov, ivec2(gl_FragCoord.xy), o_col0); #endif // Writing back depth #if PS_RETURN_DEPTH gl_FragDepth = input_z; #elif PS_RETURN_DEPTH_ROV - input_z = rov_discard ? sample_from_depth() : input_z; - - imageStore(DepthImageRov, ivec2(gl_FragCoord.xy), vec4(input_z, 0, 0, 1.0f)); + if (!rov_discard_depth) + imageStore(DepthImageRov, ivec2(gl_FragCoord.xy), vec4(input_z, 0, 0, 1.0f)); #endif #if PS_ROV_COLOR || PS_ROV_DEPTH diff --git a/pcsx2/ShaderCacheVersion.h b/pcsx2/ShaderCacheVersion.h index 9be77e89ab..f767c68783 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 = 105; // Last changed in PR 14465 +static constexpr u32 SHADER_CACHE_VERSION = 106; // Last changed in PR 14643