diff --git a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp index 8d162c7741..42a81f81ea 100644 --- a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp +++ b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp @@ -357,6 +357,7 @@ void GLFragmentProgram::Decompile(const RSXFragmentProgram& prog) { const auto driver_caps = gl::get_driver_caps(); decompiler.device_props.has_native_half_support = driver_caps.NV_gpu_shader5_supported || driver_caps.AMD_gpu_shader_half_float_supported; + decompiler.device_props.has_low_precision_rounding = driver_caps.vendor_NVIDIA; } decompiler.Task(); diff --git a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp index 9a82a9bead..4180aa04f5 100644 --- a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp +++ b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp @@ -828,7 +828,21 @@ std::string FragmentProgramDecompiler::BuildCode() "#define _builtin_sqrt(x) sqrt(abs(x))\n" "#define _builtin_rcp(x) (1. / x)\n" "#define _builtin_rsq(x) (1. / _builtin_sqrt(x))\n" - "#define _builtin_div(x, y) (x / y)\n\n"; + "#define _builtin_div(x, y) (x / y)\n"; + + if (device_props.has_low_precision_rounding) + { + // NVIDIA has terrible rounding errors interpolating constant values across vertices with different w + // PS3 games blindly rely on interpolating a constant to not change the values + // Calling floor/equality will fail randomly causing a moire pattern + OS << + "#define _builtin_floor(x) floor(x + 0.000001)\n\n"; + } + else + { + OS << + "#define _builtin_floor floor\n\n"; + } if (properties.has_pkg) { @@ -954,7 +968,7 @@ bool FragmentProgramDecompiler::handle_sct_scb(u32 opcode) case RSX_FP_OPCODE_DST: SetDst("$Ty(1.0, $0.y * $1.y, $0.z, $1.w)", OPFLAGS::op_extern); return true; case RSX_FP_OPCODE_REFL: SetDst(getFunction(FUNCTION::FUNCTION_REFL), OPFLAGS::op_extern); return true; case RSX_FP_OPCODE_EX2: SetDst("exp2($0.xxxx)"); return true; - case RSX_FP_OPCODE_FLR: SetDst("floor($0)"); return true; + case RSX_FP_OPCODE_FLR: SetDst("_builtin_floor($0)"); return true; case RSX_FP_OPCODE_FRC: SetDst(getFunction(FUNCTION::FUNCTION_FRACT)); return true; case RSX_FP_OPCODE_LIT: SetDst("_builtin_lit($0)"); diff --git a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.h b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.h index 4968f0c500..2b8766754a 100644 --- a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.h +++ b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.h @@ -294,6 +294,7 @@ public: { bool has_native_half_support = false; bool emulate_depth_compare = false; + bool has_low_precision_rounding = false; } device_props; diff --git a/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp b/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp index d432d3a424..748caf8222 100644 --- a/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp +++ b/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp @@ -248,7 +248,7 @@ void VKFragmentDecompilerThread::insertGlobalFunctions(std::stringstream &OS) m_shader_props.require_linear_to_srgb = properties.has_pkg; m_shader_props.emulate_coverage_tests = g_cfg.video.antialiasing_level == msaa_level::none; m_shader_props.emulate_shadow_compare = device_props.emulate_depth_compare; - m_shader_props.low_precision_tests = vk::get_driver_vendor() == vk::driver_vendor::NVIDIA; + m_shader_props.low_precision_tests = device_props.has_low_precision_rounding; m_shader_props.disable_early_discard = vk::get_driver_vendor() != vk::driver_vendor::NVIDIA; m_shader_props.supports_native_fp16 = device_props.has_native_half_support; @@ -402,6 +402,7 @@ void VKFragmentProgram::Decompile(const RSXFragmentProgram& prog) } decompiler.device_props.emulate_depth_compare = !pdev->get_formats_support().d24_unorm_s8; + decompiler.device_props.has_low_precision_rounding = vk::get_driver_vendor() == vk::driver_vendor::NVIDIA; decompiler.Task(); shader.create(::glsl::program_domain::glsl_fragment_program, source);