From 0e767334f7f6ea75ad2ade52c30ba8730a069237 Mon Sep 17 00:00:00 2001 From: Maxime Morel Date: Thu, 22 Oct 2026 19:10:56 +0200 Subject: [PATCH] Draw order more fixes --- port/fast3d/gfx_opengl.cpp | 49 ++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/port/fast3d/gfx_opengl.cpp b/port/fast3d/gfx_opengl.cpp index 19f11a07..99db68f9 100644 --- a/port/fast3d/gfx_opengl.cpp +++ b/port/fast3d/gfx_opengl.cpp @@ -42,6 +42,8 @@ struct ShaderProgram { GLint decal_scale_location; int decal_mode; float decal_scale; + GLint depth_prepass_location; + int depth_prepass; }; struct Framebuffer { @@ -83,6 +85,14 @@ static uint32_t depth_copy_width, depth_copy_height; static bool current_depth_test; static bool current_depth_decal; +// Translucent surfaces write no depth and are only drawn in rough back-to-front order, so a farther +// railing drawn later blended over a nearer one. Their nearly opaque texels (railing bars) first get +// a depth-only pass, then the surface is drawn as usual: semi-transparent texels are unchanged. +#define XLU_DEPTH_PREPASS_ALPHA "0.98" +static bool current_depth_compare; +static GLenum current_depth_func = GL_LESS; +static bool current_blend; + static int gl_glsl_version = 130; static char gl_glsl_version_str[16] = "130"; static GLenum gl_mirror_clamp = GL_MIRROR_CLAMP_TO_EDGE; @@ -417,6 +427,7 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad append_line(fs_buf, &fs_len, "uniform int frame_count;"); append_line(fs_buf, &fs_len, "uniform float noise_scale;"); append_line(fs_buf, &fs_len, "uniform int uDecalMode;"); + append_line(fs_buf, &fs_len, "uniform int uDepthPrepass;"); append_line(fs_buf, &fs_len, "uniform highp float uDecalPixelScale;"); append_line(fs_buf, &fs_len, "uniform highp sampler2D uDepthTex;"); @@ -617,6 +628,7 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad if (cc_features.opt_invisible) { append_line(fs_buf, &fs_len, " texel.a = 0.0;"); } + append_line(fs_buf, &fs_len, " if (uDepthPrepass != 0 && texel.a < " XLU_DEPTH_PREPASS_ALPHA ") discard;"); append_line(fs_buf, &fs_len, " OUTPUT_COLOR = texel;"); } else { @@ -740,6 +752,11 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad prg->decal_scale_location = glGetUniformLocation(shader_program, "uDecalPixelScale"); prg->decal_mode = 0; prg->decal_scale = 0.0f; + prg->depth_prepass_location = glGetUniformLocation(shader_program, "uDepthPrepass"); + prg->depth_prepass = 0; + if (prg->depth_prepass_location >= 0) { + glUniform1i(prg->depth_prepass_location, 0); + } if (prg->decal_mode_location >= 0) { glUniform1i(prg->decal_mode_location, 0); glUniform1i(glGetUniformLocation(shader_program, "uDepthTex"), DECAL_DEPTH_TEXTURE_UNIT); @@ -902,6 +919,7 @@ static void gfx_opengl_set_sampler_parameters(int tile, bool linear_filter, uint static void gfx_opengl_set_depth_mode(bool depth_test, bool depth_update, bool depth_compare, bool depth_source_prim, uint16_t zmode) { current_depth_test = depth_test; + current_depth_compare = depth_compare; current_depth_decal = depth_test && depth_compare && zmode == ZMODE_DEC; if (depth_test) { @@ -912,7 +930,7 @@ static void gfx_opengl_set_depth_mode(bool depth_test, bool depth_update, bool d if (depth_compare) { switch (zmode) { case ZMODE_INTER: - glDepthFunc(GL_LEQUAL); + glDepthFunc(current_depth_func = GL_LEQUAL); glDisable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(0, 0); break; @@ -920,22 +938,22 @@ static void gfx_opengl_set_depth_mode(bool depth_test, bool depth_update, bool d case ZMODE_OPA: case ZMODE_XLU: if (depth_source_prim) { - glDepthFunc(GL_LEQUAL); + glDepthFunc(current_depth_func = GL_LEQUAL); } else { - glDepthFunc(GL_LESS); + glDepthFunc(current_depth_func = GL_LESS); } glDisable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(0, 0); break; case ZMODE_DEC: - glDepthFunc(GL_LEQUAL); + glDepthFunc(current_depth_func = GL_LEQUAL); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(-2, -2); break; } } else { - glDepthFunc(GL_ALWAYS); + glDepthFunc(current_depth_func = GL_ALWAYS); glDisable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(0, 0); } @@ -961,6 +979,7 @@ static void gfx_opengl_set_scissor(int x, int y, int width, int height) { } static void gfx_opengl_set_use_alpha(bool use_alpha, bool modulate) { + current_blend = use_alpha && !modulate; if (use_alpha) { glEnable(GL_BLEND); } else { @@ -1084,6 +1103,26 @@ static void gfx_opengl_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_ } glBufferData(GL_ARRAY_BUFFER, sizeof(float) * buf_vbo_len, buf_vbo, GL_STREAM_DRAW); + + struct ShaderProgram* prg = current_shader_program; + if (!current_depth_decal && current_depth_test && current_depth_compare && !current_depth_mask && current_blend && + prg != nullptr && prg->depth_prepass_location >= 0) { + glUniform1i(prg->depth_prepass_location, 1); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glDepthMask(GL_TRUE); + glDrawArrays(GL_TRIANGLES, 0, 3 * buf_vbo_num_tris); + glUniform1i(prg->depth_prepass_location, 0); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_FALSE); + + // The colour pass must pass where the prepass just stored this surface's own depth + glDepthFunc(GL_LEQUAL); + glDrawArrays(GL_TRIANGLES, 0, 3 * buf_vbo_num_tris); + glDepthFunc(current_depth_func); + depth_copy_dirty = true; + return; + } + glDrawArrays(GL_TRIANGLES, 0, 3 * buf_vbo_num_tris); if (current_depth_test && current_depth_mask) {