From 6aa31d6ec2f15afe3c90a36b55f6ad71ee3c1298 Mon Sep 17 00:00:00 2001 From: GPUCode <47210458+GPUCode@users.noreply.github.com> Date: Mon, 11 Sep 2023 00:29:55 +0300 Subject: [PATCH] video_core: Use epsilons for clip planes (#6945) * video_core: Use epsilons for clip planes * video_core: Add comments --- .../renderer_opengl/gl_shader_gen.cpp | 19 ++++++++++++++++--- .../renderer_software/sw_rasterizer.cpp | 8 +++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index df1f32ca0..ee4309859 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -1717,7 +1717,12 @@ ShaderDecompiler::ProgramResult GenerateTrivialVertexShader(bool separable_shade out += UniformBlockDef; + // Certain games render 2D elements very close to clip plane 0 resulting in very tiny + // negative/positive z values when computing with f32 precision, + // causing some vertices to get erroneously clipped. To workaround this problem, + // we can use a very small epsilon value for clip plane comparison. out += R"( +const float EPSILON_Z = 0.00000001f; void main() { primary_color = vert_color; @@ -1727,10 +1732,14 @@ void main() { texcoord0_w = vert_texcoord0_w; normquat = vert_normquat; view = vert_view; - gl_Position = vert_position; + vec4 vtx_pos = vert_position; + if (abs(vtx_pos.z) < EPSILON_Z) { + vtx_pos.z = 0.f; + } + gl_Position = vtx_pos; #if !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance) - gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0 - gl_ClipDistance[1] = dot(clip_coef, vert_position); + gl_ClipDistance[0] = -vtx_pos.z; // fixed PICA clipping plane z <= 0 + gl_ClipDistance[1] = dot(clip_coef, vtx_pos); #endif // !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance) } )"; @@ -1830,6 +1839,7 @@ struct Vertex { return "0.0"; }; + out += "const float EPSILON_Z = 0.00000001f;\n\n"; out += "vec4 GetVertexQuaternion(Vertex vtx) {\n"; out += " return vec4(" + semantic(VSOutputAttributes::QUATERNION_X) + ", " + semantic(VSOutputAttributes::QUATERNION_Y) + ", " + @@ -1842,6 +1852,9 @@ struct Vertex { semantic(VSOutputAttributes::POSITION_Y) + ", " + semantic(VSOutputAttributes::POSITION_Z) + ", " + semantic(VSOutputAttributes::POSITION_W) + ");\n"; + out += " if (abs(vtx_pos.z) < EPSILON_Z) {\n"; + out += " vtx_pos.z = 0.f;\n"; + out += " }\n"; out += " gl_Position = vtx_pos;\n"; out += "#if !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance)\n"; out += " gl_ClipDistance[0] = -vtx_pos.z;\n"; // fixed PICA clipping plane z <= 0 diff --git a/src/video_core/renderer_software/sw_rasterizer.cpp b/src/video_core/renderer_software/sw_rasterizer.cpp index 9720a4d49..21a562aee 100644 --- a/src/video_core/renderer_software/sw_rasterizer.cpp +++ b/src/video_core/renderer_software/sw_rasterizer.cpp @@ -27,6 +27,12 @@ using Pica::TexturingRegs; using Pica::Texture::LookupTexture; using Pica::Texture::TextureInfo; +// Certain games render 2D elements very close to clip plane 0 resulting in very tiny +// negative/positive z values when computing with f32 precision, +// causing some vertices to get erroneously clipped. To workaround this problem, +// we can use a very small epsilon value for clip plane comparison. +constexpr f32 EPSILON_Z = 0.00000001f; + struct Vertex : Pica::Shader::OutputVertex { Vertex(const OutputVertex& v) : OutputVertex(v) {} @@ -73,7 +79,7 @@ public: : pos(f24::Zero()), coeffs(coeffs), bias(bias) {} bool IsInside(const Vertex& vertex) const { - return Common::Dot(vertex.pos + bias, coeffs) >= f24::Zero(); + return Common::Dot(vertex.pos + bias, coeffs) >= f24::FromFloat32(-EPSILON_Z); } bool IsOutSide(const Vertex& vertex) const {