From 441e7e04857710a122a2eb8050222776a519ec37 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Tue, 16 Dec 2025 14:07:58 +0300 Subject: [PATCH] rsx: Disable early-Z optimizations if a loopback on the depth texture is active - The optimizations worked a bit too well and allowed early-Z to kick in. - Early Z inverts the logic making reads happen after writes and breaking loopback sampling. --- rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp | 19 +++++++++++++++---- rpcs3/Emu/RSX/RSXThread.cpp | 6 ++++++ rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp | 11 +++++++++++ rpcs3/Emu/RSX/gcm_enums.h | 5 +++++ 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp index 047b362bae..66f062c323 100644 --- a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp +++ b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp @@ -335,20 +335,31 @@ void GLFragmentDecompilerThread::insertMainEnd(std::stringstream & OS) OS << "\n" << " fs_main();\n\n"; + if ((m_ctrl & RSX_SHADER_CONTROL_DISABLE_EARLY_Z) && + !(m_ctrl & (CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT | RSX_SHADER_CONTROL_META_USES_DISCARD)) && + g_cfg.video.shader_precision != gpu_preset_level::low) + { + // This is effectively unreachable code, but good enough to trick the GPU to skip early Z + OS << + "// Insert NOP sequence to disable early-Z\n" + "if (isnan(gl_FragCoord.z))\n" + " discard;\n\n"; + } + glsl::insert_rop(OS, m_shader_props); if (m_ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) { if (m_parr.HasParam(PF_PARAM_NONE, "vec4", "r1")) { - //Depth writes are always from a fp32 register. See issues section on nvidia's NV_fragment_program spec - //https://www.khronos.org/registry/OpenGL/extensions/NV/NV_fragment_program.txt + // Depth writes are always from a fp32 register. See issues section on nvidia's NV_fragment_program spec + // https://www.khronos.org/registry/OpenGL/extensions/NV/NV_fragment_program.txt OS << " gl_FragDepth = r1.z;\n"; } else { - //Input not declared. Leave commented to assist in debugging the shader - OS << " //gl_FragDepth = r1.z;\n"; + // Input not declared. Leave commented to assist in debugging the shader + OS << " // gl_FragDepth = r1.z;\n"; } } diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 80ae166467..1b58bf787f 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -2240,6 +2240,12 @@ namespace rsx default: rsx_log.error("Depth texture bound to pipeline with unexpected format 0x%X", format); } + + if (sampler_descriptors[i]->is_cyclic_reference && + g_cfg.video.shader_precision != gpu_preset_level::low) + { + current_fragment_program.ctrl |= RSX_SHADER_CONTROL_DISABLE_EARLY_Z; + } } else if (!backend_config.supports_hw_renormalization /* && tex.min_filter() == rsx::texture_minify_filter::nearest && diff --git a/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp b/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp index 93e074248e..0e8e318464 100644 --- a/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp +++ b/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp @@ -471,6 +471,17 @@ void VKFragmentDecompilerThread::insertMainEnd(std::stringstream & OS) OS << "\n" << " fs_main();\n\n"; + if ((m_ctrl & RSX_SHADER_CONTROL_DISABLE_EARLY_Z) && + !(m_ctrl & (CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT | RSX_SHADER_CONTROL_META_USES_DISCARD)) && + g_cfg.video.shader_precision != gpu_preset_level::low) + { + // This is effectively unreachable code, but good enough to trick the GPU to skip early Z + OS << + "// Insert NOP sequence to disable early-Z\n" + "if (isnan(gl_FragCoord.z))\n" + " discard;\n\n"; + } + glsl::insert_rop(OS, m_shader_props); if (m_ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) diff --git a/rpcs3/Emu/RSX/gcm_enums.h b/rpcs3/Emu/RSX/gcm_enums.h index 7fd9437f66..539796a531 100644 --- a/rpcs3/Emu/RSX/gcm_enums.h +++ b/rpcs3/Emu/RSX/gcm_enums.h @@ -465,6 +465,11 @@ namespace gcm RSX_SHADER_CONTROL_ALPHA_TEST = 0x0400000, // Uses alpha test on the outputs RSX_SHADER_CONTROL_POLYGON_STIPPLE = 0x0800000, // Uses polygon stipple for dithered rendering RSX_SHADER_CONTROL_ALPHA_TO_COVERAGE = 0x1000000, // Alpha to coverage + + RSX_SHADER_CONTROL_DISABLE_EARLY_Z = 0x2000000, // Do not allow early-Z optimizations on this shader + + // Meta + RSX_SHADER_CONTROL_META_USES_DISCARD = (RSX_SHADER_CONTROL_USES_KIL | RSX_SHADER_CONTROL_TEXTURE_ALPHA_KILL | RSX_SHADER_CONTROL_ALPHA_TEST | RSX_SHADER_CONTROL_POLYGON_STIPPLE | RSX_SHADER_CONTROL_ALPHA_TO_COVERAGE) }; // GCM Reports