From cac29ed2c391bec3ac71300985dd1ca105600cb7 Mon Sep 17 00:00:00 2001 From: polymetal0 Date: Tue, 31 Dec 2024 19:07:17 +0100 Subject: [PATCH 01/11] See through fix Passing clipRange variable as a uniform and performing perspective divide in fragment shader for z_perspective = true solves the see through issue that affects many games. Shader code for z_perspective = false is virtually unaltered. --- hw/xbox/nv2a/pgraph/gl/shaders.c | 6 ++- hw/xbox/nv2a/pgraph/glsl/common.c | 6 +-- hw/xbox/nv2a/pgraph/glsl/common.h | 2 +- hw/xbox/nv2a/pgraph/glsl/geom.c | 7 +-- hw/xbox/nv2a/pgraph/glsl/geom.h | 3 +- hw/xbox/nv2a/pgraph/glsl/psh.c | 62 ++++++++++++++++++-------- hw/xbox/nv2a/pgraph/glsl/psh.h | 2 +- hw/xbox/nv2a/pgraph/glsl/vsh-prog.c | 19 ++++---- hw/xbox/nv2a/pgraph/glsl/vsh.c | 67 +++++++++++++++++++---------- hw/xbox/nv2a/pgraph/vk/renderer.h | 1 + hw/xbox/nv2a/pgraph/vk/shaders.c | 24 +++++++---- 11 files changed, 130 insertions(+), 69 deletions(-) diff --git a/hw/xbox/nv2a/pgraph/gl/shaders.c b/hw/xbox/nv2a/pgraph/gl/shaders.c index 5da89e10e3e..84fef5ea6ab 100644 --- a/hw/xbox/nv2a/pgraph/gl/shaders.c +++ b/hw/xbox/nv2a/pgraph/gl/shaders.c @@ -219,7 +219,9 @@ static ShaderBinding *generate_shaders(const ShaderState *state) state->polygon_back_mode, state->primitive_mode, state->smooth_shading, - false); + false, + state->z_perspective + ); if (geometry_shader_code) { const char* geometry_shader_code_str = mstring_get_str(geometry_shader_code); @@ -240,7 +242,7 @@ static ShaderBinding *generate_shaders(const ShaderState *state) mstring_unref(vertex_shader_code); /* generate a fragment shader from register combiners */ - MString *fragment_shader_code = pgraph_gen_psh_glsl(state->psh); + MString *fragment_shader_code = pgraph_gen_psh_glsl(state->psh, state->z_perspective); const char *fragment_shader_code_str = mstring_get_str(fragment_shader_code); GLuint fragment_shader = create_gl_shader(GL_FRAGMENT_SHADER, diff --git a/hw/xbox/nv2a/pgraph/glsl/common.c b/hw/xbox/nv2a/pgraph/glsl/common.c index 7059880373d..78ff2d30101 100644 --- a/hw/xbox/nv2a/pgraph/glsl/common.c +++ b/hw/xbox/nv2a/pgraph/glsl/common.c @@ -21,13 +21,13 @@ #include "common.h" -MString *pgraph_get_glsl_vtx_header(MString *out, bool location, bool smooth, bool in, bool prefix, bool array) +MString *pgraph_get_glsl_vtx_header(MString *out, bool location, bool smooth, bool in, bool prefix, bool array, bool z_perspective) { const char *flat_s = "flat"; - const char *noperspective_s = "noperspective"; + const char *noperspective_s = z_perspective ? "" : "noperspective"; const char *qualifier_s = smooth ? noperspective_s : flat_s; const char *qualifiers[11] = { - noperspective_s, flat_s, qualifier_s, qualifier_s, + "noperspective", flat_s, qualifier_s, qualifier_s, qualifier_s, qualifier_s, noperspective_s, noperspective_s, noperspective_s, noperspective_s, noperspective_s }; diff --git a/hw/xbox/nv2a/pgraph/glsl/common.h b/hw/xbox/nv2a/pgraph/glsl/common.h index 6820a1dcb19..573d87d2239 100644 --- a/hw/xbox/nv2a/pgraph/glsl/common.h +++ b/hw/xbox/nv2a/pgraph/glsl/common.h @@ -33,6 +33,6 @@ #define GLSL_DEFINE(a, b) "#define " stringify(a) " " b "\n" -MString *pgraph_get_glsl_vtx_header(MString *out, bool location, bool smooth, bool in, bool prefix, bool array); +MString *pgraph_get_glsl_vtx_header(MString *out, bool location, bool smooth, bool in, bool prefix, bool array, bool z_perspective); #endif diff --git a/hw/xbox/nv2a/pgraph/glsl/geom.c b/hw/xbox/nv2a/pgraph/glsl/geom.c index 0e738f02806..17ed1637872 100644 --- a/hw/xbox/nv2a/pgraph/glsl/geom.c +++ b/hw/xbox/nv2a/pgraph/glsl/geom.c @@ -27,7 +27,8 @@ MString *pgraph_gen_geom_glsl(enum ShaderPolygonMode polygon_front_mode, enum ShaderPolygonMode polygon_back_mode, enum ShaderPrimitiveMode primitive_mode, bool smooth_shading, - bool vulkan) + bool vulkan, + bool z_perspective) { /* FIXME: Missing support for 2-sided-poly mode */ assert(polygon_front_mode == polygon_back_mode); @@ -174,8 +175,8 @@ MString *pgraph_gen_geom_glsl(enum ShaderPolygonMode polygon_front_mode, mstring_append(s, layout_in); mstring_append(s, layout_out); mstring_append(s, "\n"); - pgraph_get_glsl_vtx_header(s, vulkan, smooth_shading, true, true, true); - pgraph_get_glsl_vtx_header(s, vulkan, smooth_shading, false, false, false); + pgraph_get_glsl_vtx_header(s, vulkan, smooth_shading, true, true, true, z_perspective); + pgraph_get_glsl_vtx_header(s, vulkan, smooth_shading, false, false, false, z_perspective); if (smooth_shading) { mstring_append(s, diff --git a/hw/xbox/nv2a/pgraph/glsl/geom.h b/hw/xbox/nv2a/pgraph/glsl/geom.h index 9ca605be71b..0deaecc4f5e 100644 --- a/hw/xbox/nv2a/pgraph/glsl/geom.h +++ b/hw/xbox/nv2a/pgraph/glsl/geom.h @@ -29,6 +29,7 @@ MString *pgraph_gen_geom_glsl(enum ShaderPolygonMode polygon_front_mode, enum ShaderPolygonMode polygon_back_mode, enum ShaderPrimitiveMode primitive_mode, bool smooth_shading, - bool vulkan); + bool vulkan, + bool z_perspective); #endif diff --git a/hw/xbox/nv2a/pgraph/glsl/psh.c b/hw/xbox/nv2a/pgraph/glsl/psh.c index fcb594d5009..bb830b6528f 100644 --- a/hw/xbox/nv2a/pgraph/glsl/psh.c +++ b/hw/xbox/nv2a/pgraph/glsl/psh.c @@ -726,7 +726,7 @@ static void apply_convolution_filter(const struct PixelShader *ps, MString *vars "}\n", tex, tex, tex, tex, tex_remap, tex); } -static MString* psh_convert(struct PixelShader *ps) +static MString *psh_convert(struct PixelShader *ps, bool z_perspective) { int i; @@ -734,7 +734,7 @@ static MString* psh_convert(struct PixelShader *ps) MString *preflight = mstring_new(); pgraph_get_glsl_vtx_header(preflight, ps->state.vulkan, - ps->state.smooth_shading, true, false, false); + ps->state.smooth_shading, true, false, false, z_perspective); if (ps->state.vulkan) { mstring_append_fmt(preflight, @@ -744,7 +744,11 @@ static MString* psh_convert(struct PixelShader *ps) mstring_append_fmt(preflight, "layout(location = 0) out vec4 fragColor;\n"); } - + if (z_perspective) { + mstring_append_fmt(preflight, + "%svec4 clipRange;\n", + u); + } mstring_append_fmt(preflight, "%sfloat alphaRef;\n" "%svec4 fogColor;\n" "%sivec4 clipRegion[8];\n", @@ -865,26 +869,42 @@ static MString* psh_convert(struct PixelShader *ps) /* calculate perspective-correct inputs */ MString *vars = mstring_new(); - if (ps->state.smooth_shading) { - mstring_append(vars, "vec4 pD0 = vtxD0 / vtx_inv_w;\n"); - mstring_append(vars, "vec4 pD1 = vtxD1 / vtx_inv_w;\n"); - mstring_append(vars, "vec4 pB0 = vtxB0 / vtx_inv_w;\n"); - mstring_append(vars, "vec4 pB1 = vtxB1 / vtx_inv_w;\n"); + if (!z_perspective) { + if (ps->state.smooth_shading) { + mstring_append(vars, "vec4 pD0 = vtxD0 / vtx_inv_w;\n"); + mstring_append(vars, "vec4 pD1 = vtxD1 / vtx_inv_w;\n"); + mstring_append(vars, "vec4 pB0 = vtxB0 / vtx_inv_w;\n"); + mstring_append(vars, "vec4 pB1 = vtxB1 / vtx_inv_w;\n"); + } else { + mstring_append(vars, "vec4 pD0 = vtxD0 / vtx_inv_w_flat;\n"); + mstring_append(vars, "vec4 pD1 = vtxD1 / vtx_inv_w_flat;\n"); + mstring_append(vars, "vec4 pB0 = vtxB0 / vtx_inv_w_flat;\n"); + mstring_append(vars, "vec4 pB1 = vtxB1 / vtx_inv_w_flat;\n"); + } + mstring_append(vars, "vec4 pFog = vec4(fogColor.rgb, clamp(vtxFog / vtx_inv_w, 0.0, 1.0));\n"); + mstring_append(vars, "vec4 pT0 = vtxT0 / vtx_inv_w;\n"); + mstring_append(vars, "vec4 pT1 = vtxT1 / vtx_inv_w;\n"); + mstring_append(vars, "vec4 pT2 = vtxT2 / vtx_inv_w;\n"); } else { - mstring_append(vars, "vec4 pD0 = vtxD0 / vtx_inv_w_flat;\n"); - mstring_append(vars, "vec4 pD1 = vtxD1 / vtx_inv_w_flat;\n"); - mstring_append(vars, "vec4 pB0 = vtxB0 / vtx_inv_w_flat;\n"); - mstring_append(vars, "vec4 pB1 = vtxB1 / vtx_inv_w_flat;\n"); + mstring_append(vars, "vec4 pD0 = vtxD0;\n"); + mstring_append(vars, "vec4 pD1 = vtxD1;\n"); + mstring_append(vars, "vec4 pB0 = vtxB0;\n"); + mstring_append(vars, "vec4 pB1 = vtxB1;\n"); + mstring_append(vars, "vec4 pFog = vec4(fogColor.rgb, clamp(vtxFog, 0.0, 1.0));\n"); + mstring_append(vars, "vec4 pT0 = vtxT0;\n"); + mstring_append(vars, "vec4 pT1 = vtxT1;\n"); + mstring_append(vars, "vec4 pT2 = vtxT2;\n"); } - mstring_append(vars, "vec4 pFog = vec4(fogColor.rgb, clamp(vtxFog / vtx_inv_w, 0.0, 1.0));\n"); - mstring_append(vars, "vec4 pT0 = vtxT0 / vtx_inv_w;\n"); - mstring_append(vars, "vec4 pT1 = vtxT1 / vtx_inv_w;\n"); - mstring_append(vars, "vec4 pT2 = vtxT2 / vtx_inv_w;\n"); + if (ps->state.point_sprite) { assert(!ps->state.rect_tex[3]); mstring_append(vars, "vec4 pT3 = vec4(gl_PointCoord, 1.0, 1.0);\n"); } else { - mstring_append(vars, "vec4 pT3 = vtxT3 / vtx_inv_w;\n"); + if (!z_perspective) { + mstring_append(vars, "vec4 pT3 = vtxT3 / vtx_inv_w;\n"); + } else { + mstring_append(vars, "vec4 pT3 = vtxT3;\n"); + } } mstring_append(vars, "\n"); mstring_append(vars, "vec4 v0 = pD0;\n"); @@ -1198,6 +1218,10 @@ static MString* psh_convert(struct PixelShader *ps) } } + if (z_perspective) { + mstring_append(ps->code, "gl_FragDepth = (1.0/gl_FragCoord.w)/clipRange.y;\n"); + } + for (i = 0; i < ps->num_var_refs; i++) { mstring_append_fmt(vars, "vec4 %s = vec4(0);\n", ps->var_refs[i]); if (strcmp(ps->var_refs[i], "r0") == 0) { @@ -1257,7 +1281,7 @@ static void parse_combiner_output(uint32_t value, struct OutputInfo *out) out->cd_alphablue = flags & 0x40; } -MString *pgraph_gen_psh_glsl(const PshState state) +MString *pgraph_gen_psh_glsl(const PshState state, bool z_perspective) { int i; struct PixelShader ps; @@ -1307,5 +1331,5 @@ MString *pgraph_gen_psh_glsl(const PshState state) ps.final_input.inv_r0 = flags & PS_FINALCOMBINERSETTING_COMPLEMENT_R0; } - return psh_convert(&ps); + return psh_convert(&ps, z_perspective); } diff --git a/hw/xbox/nv2a/pgraph/glsl/psh.h b/hw/xbox/nv2a/pgraph/glsl/psh.h index 1ae0b0db7ed..89f376fd72f 100644 --- a/hw/xbox/nv2a/pgraph/glsl/psh.h +++ b/hw/xbox/nv2a/pgraph/glsl/psh.h @@ -36,6 +36,6 @@ #define PSH_UBO_BINDING 1 #define PSH_TEX_BINDING 2 -MString *pgraph_gen_psh_glsl(const PshState state); +MString *pgraph_gen_psh_glsl(const PshState state, bool z_perspective); #endif diff --git a/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c b/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c index 7bebed71e85..6fa76b6ae7c 100644 --- a/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c +++ b/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c @@ -831,11 +831,12 @@ void pgraph_gen_vsh_prog_glsl(uint16_t version, * interpolation manually. OpenGL can't, since we give it a W of 1 to work * around the perspective divide */ mstring_append(body, - " if (oPos.w == 0.0 || isinf(oPos.w)) {\n" - " vtx_inv_w = 1.0;\n" + " if (oPos.w < 0.0) {\n" + " oPos.w = clamp(oPos.w, -1.884467e+019, -5.421011e-20);\n" " } else {\n" - " vtx_inv_w = 1.0 / oPos.w;\n" + " oPos.w = clamp(oPos.w, 5.421011e-20, 1.884467e+019);\n" " }\n" + " vtx_inv_w = 1.0 / oPos.w;\n" " vtx_inv_w_flat = vtx_inv_w;\n" ); @@ -855,10 +856,6 @@ void pgraph_gen_vsh_prog_glsl(uint16_t version, "/ surfaceSize.y;\n"); } - if (z_perspective) { - mstring_append(body, " oPos.z = oPos.w;\n"); - } - mstring_append(body, " if (clipRange.y != clipRange.x) {\n"); if (vulkan) { @@ -870,6 +867,12 @@ void pgraph_gen_vsh_prog_glsl(uint16_t version, } mstring_append(body, " }\n" + ); + if(z_perspective) { + mstring_append(body, " oPos.xyz *= oPos.w;\n"); + } else { + mstring_append( + body, /* Correct for the perspective divide */ " if (oPos.w < 0.0) {\n" @@ -882,5 +885,5 @@ void pgraph_gen_vsh_prog_glsl(uint16_t version, " oPos.w = 1.0;\n" " }\n" ); - + } } diff --git a/hw/xbox/nv2a/pgraph/glsl/vsh.c b/hw/xbox/nv2a/pgraph/glsl/vsh.c index a60fbe265dd..6cf76505942 100644 --- a/hw/xbox/nv2a/pgraph/glsl/vsh.c +++ b/hw/xbox/nv2a/pgraph/glsl/vsh.c @@ -75,7 +75,7 @@ MString *pgraph_gen_vsh_glsl(const ShaderState *state, bool prefix_outputs) "}\n"); pgraph_get_glsl_vtx_header(header, state->vulkan, state->smooth_shading, - false, prefix_outputs, false); + false, prefix_outputs, false, state->z_perspective); if (prefix_outputs) { mstring_append(header, @@ -233,28 +233,49 @@ MString *pgraph_gen_vsh_glsl(const ShaderState *state, bool prefix_outputs) } /* Set outputs */ - const char *shade_model_mult = state->smooth_shading ? "vtx_inv_w" : "vtx_inv_w_flat"; - mstring_append_fmt(body, "\n" - " vtxD0 = clamp(oD0, 0.0, 1.0) * %s;\n" - " vtxD1 = clamp(oD1, 0.0, 1.0) * %s;\n" - " vtxB0 = clamp(oB0, 0.0, 1.0) * %s;\n" - " vtxB1 = clamp(oB1, 0.0, 1.0) * %s;\n" - " vtxFog = oFog.x * vtx_inv_w;\n" - " vtxT0 = oT0 * vtx_inv_w;\n" - " vtxT1 = oT1 * vtx_inv_w;\n" - " vtxT2 = oT2 * vtx_inv_w;\n" - " vtxT3 = oT3 * vtx_inv_w;\n" - " gl_Position = oPos;\n" - " gl_PointSize = oPts.x;\n" - // " gl_ClipDistance[0] = oPos.z - oPos.w*clipRange.z;\n" // Near - // " gl_ClipDistance[1] = oPos.w*clipRange.w - oPos.z;\n" // Far - "\n" - "}\n", - shade_model_mult, - shade_model_mult, - shade_model_mult, - shade_model_mult); - + if (state->z_perspective == false) { + const char *shade_model_mult = state->smooth_shading ? "vtx_inv_w" : "vtx_inv_w_flat"; + mstring_append_fmt(body, "\n" + " vtxD0 = clamp(oD0, 0.0, 1.0) * %s;\n" + " vtxD1 = clamp(oD1, 0.0, 1.0) * %s;\n" + " vtxB0 = clamp(oB0, 0.0, 1.0) * %s;\n" + " vtxB1 = clamp(oB1, 0.0, 1.0) * %s;\n" + " vtxFog = oFog.x * vtx_inv_w;\n" + " vtxT0 = oT0 * vtx_inv_w;\n" + " vtxT1 = oT1 * vtx_inv_w;\n" + " vtxT2 = oT2 * vtx_inv_w;\n" + " vtxT3 = oT3 * vtx_inv_w;\n" + " gl_Position = oPos;\n" + " gl_PointSize = oPts.x;\n" + // " gl_ClipDistance[0] = oPos.z - oPos.w*clipRange.z;\n" // Near + // " gl_ClipDistance[1] = oPos.w*clipRange.w - oPos.z;\n" // Far + "\n" + "}\n", + shade_model_mult, + shade_model_mult, + shade_model_mult, + shade_model_mult + ); + } else { + mstring_append_fmt( + body, "\n" + " vtxD0 = clamp(oD0, 0.0, 1.0);\n" + " vtxD1 = clamp(oD1, 0.0, 1.0);\n" + " vtxB0 = clamp(oB0, 0.0, 1.0);\n" + " vtxB1 = clamp(oB1, 0.0, 1.0);\n" + " vtxFog = oFog.x;\n" + " vtxT0 = oT0;\n" + " vtxT1 = oT1;\n" + " vtxT2 = oT2;\n" + " vtxT3 = oT3;\n" + " gl_Position = oPos;\n" + " gl_PointSize = oPts.x;\n" + //" gl_ClipDistance[0] = oPos.w - clipRange.z;\n" // Near + //" gl_ClipDistance[1] = clipRange.w - oPos.w;\n" // Far + "\n" + "}\n" + ); + } /* Return combined header + source */ if (state->vulkan) { // FIXME: Optimize uniforms diff --git a/hw/xbox/nv2a/pgraph/vk/renderer.h b/hw/xbox/nv2a/pgraph/vk/renderer.h index 781cc8dc498..8b565b6e63d 100644 --- a/hw/xbox/nv2a/pgraph/vk/renderer.h +++ b/hw/xbox/nv2a/pgraph/vk/renderer.h @@ -173,6 +173,7 @@ typedef struct ShaderBinding { int surface_size_loc; int clip_range_loc; + int clip_range_loc_frag; int vsh_constant_loc; uint32_t vsh_constants[NV2A_VERTEXSHADER_CONSTANTS][4]; diff --git a/hw/xbox/nv2a/pgraph/vk/shaders.c b/hw/xbox/nv2a/pgraph/vk/shaders.c index f831dece469..69555e4624d 100644 --- a/hw/xbox/nv2a/pgraph/vk/shaders.c +++ b/hw/xbox/nv2a/pgraph/vk/shaders.c @@ -310,6 +310,9 @@ static void update_shader_constant_locations(ShaderBinding *binding) binding->uniform_attrs_loc = uniform_index(&binding->vertex->uniforms, "inlineValue"); + + binding->clip_range_loc_frag = + uniform_index(&binding->fragment->uniforms, "clipRange"); } static void shader_cache_entry_init(Lru *lru, LruNode *node, void *state) @@ -393,7 +396,7 @@ static ShaderBinding *gen_shaders(PGRAPHState *pg, ShaderState *state) MString *geometry_shader_code = pgraph_gen_geom_glsl( state->polygon_front_mode, state->polygon_back_mode, - state->primitive_mode, state->smooth_shading, true); + state->primitive_mode, state->smooth_shading, true, state->z_perspective); if (geometry_shader_code) { NV2A_VK_DPRINTF("geometry shader: \n%s", mstring_get_str(geometry_shader_code)); @@ -414,7 +417,7 @@ static ShaderBinding *gen_shaders(PGRAPHState *pg, ShaderState *state) mstring_get_str(vertex_shader_code)); mstring_unref(vertex_shader_code); - MString *fragment_shader_code = pgraph_gen_psh_glsl(state->psh); + MString *fragment_shader_code = pgraph_gen_psh_glsl(state->psh, state->z_perspective); NV2A_VK_DPRINTF("fragment shader: \n%s", mstring_get_str(fragment_shader_code)); snode->fragment = pgraph_vk_create_shader_module_from_glsl( @@ -640,16 +643,21 @@ static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding, pg->surface_binding_dim.height / aa_height); } - if (binding->clip_range_loc != -1) { - uint32_t v[2]; - v[0] = pgraph_reg_r(pg, NV_PGRAPH_ZCLIPMIN); - v[1] = pgraph_reg_r(pg, NV_PGRAPH_ZCLIPMAX); - float zclip_min = *(float *)&v[0] / zmax * 2.0 - 1.0; - float zclip_max = *(float *)&v[1] / zmax * 2.0 - 1.0; + uint32_t v[2]; + v[0] = pgraph_reg_r(pg, NV_PGRAPH_ZCLIPMIN); + v[1] = pgraph_reg_r(pg, NV_PGRAPH_ZCLIPMAX); + float zclip_min = *(float *)&v[0] / zmax * 2.0 - 1.0; + float zclip_max = *(float *)&v[1] / zmax * 2.0 - 1.0; + + if (binding->clip_range_loc != -1) { uniform4f(&binding->vertex->uniforms, binding->clip_range_loc, 0, zmax, zclip_min, zclip_max); } + if (binding->clip_range_loc_frag != -1) { + uniform4f(&binding->fragment->uniforms, binding->clip_range_loc_frag, 0, + zmax, zclip_min, zclip_max); + } /* Clipping regions */ unsigned int max_gl_width = pg->surface_binding_dim.width; unsigned int max_gl_height = pg->surface_binding_dim.height; From aa084b9c19cc7bc476c1250e76bcf026a34daa4f Mon Sep 17 00:00:00 2001 From: polymetal0 Date: Fri, 3 Jan 2025 17:22:49 +0100 Subject: [PATCH 02/11] Use NV_PGRAPH_ZCLIP min and max --- hw/xbox/nv2a/pgraph/gl/shaders.c | 4 ++-- hw/xbox/nv2a/pgraph/glsl/vsh-prog.c | 10 +++++++--- hw/xbox/nv2a/pgraph/vk/shaders.c | 4 ++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/hw/xbox/nv2a/pgraph/gl/shaders.c b/hw/xbox/nv2a/pgraph/gl/shaders.c index 84fef5ea6ab..ba17afbac2f 100644 --- a/hw/xbox/nv2a/pgraph/gl/shaders.c +++ b/hw/xbox/nv2a/pgraph/gl/shaders.c @@ -899,8 +899,8 @@ static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding, uint32_t v[2]; v[0] = pgraph_reg_r(pg, NV_PGRAPH_ZCLIPMIN); v[1] = pgraph_reg_r(pg, NV_PGRAPH_ZCLIPMAX); - float zclip_min = *(float*)&v[0] / zmax * 2.0 - 1.0; - float zclip_max = *(float*)&v[1] / zmax * 2.0 - 1.0; + float zclip_min = *(float*)&v[0];// / zmax * 2.0 - 1.0; + float zclip_max = *(float*)&v[1];// / zmax * 2.0 - 1.0; glUniform4f(binding->clip_range_loc, 0, zmax, zclip_min, zclip_max); } diff --git a/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c b/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c index 6fa76b6ae7c..c7df12d5093 100644 --- a/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c +++ b/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c @@ -856,14 +856,18 @@ void pgraph_gen_vsh_prog_glsl(uint16_t version, "/ surfaceSize.y;\n"); } + if (z_perspective) { + mstring_append(body, " oPos.z = oPos.w;\n"); + } + mstring_append(body, " if (clipRange.y != clipRange.x) {\n"); if (vulkan) { - mstring_append(body, " oPos.z /= clipRange.y;\n"); + mstring_append(body, " oPos.z = (oPos.z - clipRange.z)/(clipRange.w - clipRange.z);\n"); } else { mstring_append(body, - " oPos.z = (oPos.z - clipRange.x)/(0.5*(clipRange.y " - "- clipRange.x)) - 1;\n"); + " oPos.z = (oPos.z - clipRange.z)/(0.5*(clipRange.w " + "- clipRange.z)) - 1;\n"); } mstring_append(body, " }\n" diff --git a/hw/xbox/nv2a/pgraph/vk/shaders.c b/hw/xbox/nv2a/pgraph/vk/shaders.c index 69555e4624d..b3bb77e21d6 100644 --- a/hw/xbox/nv2a/pgraph/vk/shaders.c +++ b/hw/xbox/nv2a/pgraph/vk/shaders.c @@ -646,8 +646,8 @@ static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding, uint32_t v[2]; v[0] = pgraph_reg_r(pg, NV_PGRAPH_ZCLIPMIN); v[1] = pgraph_reg_r(pg, NV_PGRAPH_ZCLIPMAX); - float zclip_min = *(float *)&v[0] / zmax * 2.0 - 1.0; - float zclip_max = *(float *)&v[1] / zmax * 2.0 - 1.0; + float zclip_min = *(float *)&v[0];// / zmax * 2.0 - 1.0; + float zclip_max = *(float *)&v[1];// / zmax * 2.0 - 1.0; if (binding->clip_range_loc != -1) { uniform4f(&binding->vertex->uniforms, binding->clip_range_loc, 0, From 6f4a9b112e7d40892a1549a4f79839ee973c3603 Mon Sep 17 00:00:00 2001 From: polymetal0 Date: Sat, 4 Jan 2025 02:05:36 +0100 Subject: [PATCH 03/11] Use NV_PGRAPH_ZCLIPMIN as threshold for undoing perspective divide --- hw/xbox/nv2a/pgraph/glsl/psh.c | 2 +- hw/xbox/nv2a/pgraph/glsl/vsh-prog.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/xbox/nv2a/pgraph/glsl/psh.c b/hw/xbox/nv2a/pgraph/glsl/psh.c index bb830b6528f..3d6fcf4dc32 100644 --- a/hw/xbox/nv2a/pgraph/glsl/psh.c +++ b/hw/xbox/nv2a/pgraph/glsl/psh.c @@ -1219,7 +1219,7 @@ static MString *psh_convert(struct PixelShader *ps, bool z_perspective) } if (z_perspective) { - mstring_append(ps->code, "gl_FragDepth = (1.0/gl_FragCoord.w)/clipRange.y;\n"); + mstring_append(ps->code, "gl_FragDepth = 1.0/(gl_FragCoord.w * clipRange.y);\n"); } for (i = 0; i < ps->num_var_refs; i++) { diff --git a/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c b/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c index c7df12d5093..32e32510563 100644 --- a/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c +++ b/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c @@ -879,7 +879,7 @@ void pgraph_gen_vsh_prog_glsl(uint16_t version, body, /* Correct for the perspective divide */ - " if (oPos.w < 0.0) {\n" + " if (oPos.w < clipRange.z) {\n" /* undo the perspective divide in the case where the point would be * clipped so opengl can clip it correctly */ " oPos.xyz *= oPos.w;\n" From 97bee8d0e7aab4322e9c8f01a1cfa718b7ef7c86 Mon Sep 17 00:00:00 2001 From: polymetal0 Date: Fri, 10 Jan 2025 18:55:48 +0100 Subject: [PATCH 04/11] Remove vtx_inv_w and vtx_inv_w_flat variables They are still there, but unused --- hw/xbox/nv2a/pgraph/glsl/psh.c | 40 +++++--------------- hw/xbox/nv2a/pgraph/glsl/vsh-ff.c | 7 ++-- hw/xbox/nv2a/pgraph/glsl/vsh.c | 61 +++++++++---------------------- 3 files changed, 31 insertions(+), 77 deletions(-) diff --git a/hw/xbox/nv2a/pgraph/glsl/psh.c b/hw/xbox/nv2a/pgraph/glsl/psh.c index 3d6fcf4dc32..d43bc11a4b6 100644 --- a/hw/xbox/nv2a/pgraph/glsl/psh.c +++ b/hw/xbox/nv2a/pgraph/glsl/psh.c @@ -869,42 +869,20 @@ static MString *psh_convert(struct PixelShader *ps, bool z_perspective) /* calculate perspective-correct inputs */ MString *vars = mstring_new(); - if (!z_perspective) { - if (ps->state.smooth_shading) { - mstring_append(vars, "vec4 pD0 = vtxD0 / vtx_inv_w;\n"); - mstring_append(vars, "vec4 pD1 = vtxD1 / vtx_inv_w;\n"); - mstring_append(vars, "vec4 pB0 = vtxB0 / vtx_inv_w;\n"); - mstring_append(vars, "vec4 pB1 = vtxB1 / vtx_inv_w;\n"); - } else { - mstring_append(vars, "vec4 pD0 = vtxD0 / vtx_inv_w_flat;\n"); - mstring_append(vars, "vec4 pD1 = vtxD1 / vtx_inv_w_flat;\n"); - mstring_append(vars, "vec4 pB0 = vtxB0 / vtx_inv_w_flat;\n"); - mstring_append(vars, "vec4 pB1 = vtxB1 / vtx_inv_w_flat;\n"); - } - mstring_append(vars, "vec4 pFog = vec4(fogColor.rgb, clamp(vtxFog / vtx_inv_w, 0.0, 1.0));\n"); - mstring_append(vars, "vec4 pT0 = vtxT0 / vtx_inv_w;\n"); - mstring_append(vars, "vec4 pT1 = vtxT1 / vtx_inv_w;\n"); - mstring_append(vars, "vec4 pT2 = vtxT2 / vtx_inv_w;\n"); - } else { - mstring_append(vars, "vec4 pD0 = vtxD0;\n"); - mstring_append(vars, "vec4 pD1 = vtxD1;\n"); - mstring_append(vars, "vec4 pB0 = vtxB0;\n"); - mstring_append(vars, "vec4 pB1 = vtxB1;\n"); - mstring_append(vars, "vec4 pFog = vec4(fogColor.rgb, clamp(vtxFog, 0.0, 1.0));\n"); - mstring_append(vars, "vec4 pT0 = vtxT0;\n"); - mstring_append(vars, "vec4 pT1 = vtxT1;\n"); - mstring_append(vars, "vec4 pT2 = vtxT2;\n"); - } + mstring_append(vars, "vec4 pD0 = vtxD0;\n"); + mstring_append(vars, "vec4 pD1 = vtxD1;\n"); + mstring_append(vars, "vec4 pB0 = vtxB0;\n"); + mstring_append(vars, "vec4 pB1 = vtxB1;\n"); + mstring_append(vars, "vec4 pFog = vec4(fogColor.rgb, clamp(vtxFog, 0.0, 1.0));\n"); + mstring_append(vars, "vec4 pT0 = vtxT0;\n"); + mstring_append(vars, "vec4 pT1 = vtxT1;\n"); + mstring_append(vars, "vec4 pT2 = vtxT2;\n"); if (ps->state.point_sprite) { assert(!ps->state.rect_tex[3]); mstring_append(vars, "vec4 pT3 = vec4(gl_PointCoord, 1.0, 1.0);\n"); } else { - if (!z_perspective) { - mstring_append(vars, "vec4 pT3 = vtxT3 / vtx_inv_w;\n"); - } else { - mstring_append(vars, "vec4 pT3 = vtxT3;\n"); - } + mstring_append(vars, "vec4 pT3 = vtxT3;\n"); } mstring_append(vars, "\n"); mstring_append(vars, "vec4 v0 = pD0;\n"); diff --git a/hw/xbox/nv2a/pgraph/glsl/vsh-ff.c b/hw/xbox/nv2a/pgraph/glsl/vsh-ff.c index 59749003cda..8a20092cfa0 100644 --- a/hw/xbox/nv2a/pgraph/glsl/vsh-ff.c +++ b/hw/xbox/nv2a/pgraph/glsl/vsh-ff.c @@ -447,11 +447,12 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz } mstring_append(body, - " if (oPos.w == 0.0 || isinf(oPos.w)) {\n" - " vtx_inv_w = 1.0;\n" + " if (oPos.w < 0.0) {\n" + " oPos.w = clamp(oPos.w, -1.884467e+019, -5.421011e-20);\n" " } else {\n" - " vtx_inv_w = 1.0 / oPos.w;\n" + " oPos.w = clamp(oPos.w, 5.421011e-20, 1.884467e+019);\n" " }\n" + " vtx_inv_w = 1.0 / oPos.w;\n" " vtx_inv_w_flat = vtx_inv_w;\n"); } diff --git a/hw/xbox/nv2a/pgraph/glsl/vsh.c b/hw/xbox/nv2a/pgraph/glsl/vsh.c index 6cf76505942..6655c823d83 100644 --- a/hw/xbox/nv2a/pgraph/glsl/vsh.c +++ b/hw/xbox/nv2a/pgraph/glsl/vsh.c @@ -233,49 +233,24 @@ MString *pgraph_gen_vsh_glsl(const ShaderState *state, bool prefix_outputs) } /* Set outputs */ - if (state->z_perspective == false) { - const char *shade_model_mult = state->smooth_shading ? "vtx_inv_w" : "vtx_inv_w_flat"; - mstring_append_fmt(body, "\n" - " vtxD0 = clamp(oD0, 0.0, 1.0) * %s;\n" - " vtxD1 = clamp(oD1, 0.0, 1.0) * %s;\n" - " vtxB0 = clamp(oB0, 0.0, 1.0) * %s;\n" - " vtxB1 = clamp(oB1, 0.0, 1.0) * %s;\n" - " vtxFog = oFog.x * vtx_inv_w;\n" - " vtxT0 = oT0 * vtx_inv_w;\n" - " vtxT1 = oT1 * vtx_inv_w;\n" - " vtxT2 = oT2 * vtx_inv_w;\n" - " vtxT3 = oT3 * vtx_inv_w;\n" - " gl_Position = oPos;\n" - " gl_PointSize = oPts.x;\n" - // " gl_ClipDistance[0] = oPos.z - oPos.w*clipRange.z;\n" // Near - // " gl_ClipDistance[1] = oPos.w*clipRange.w - oPos.z;\n" // Far - "\n" - "}\n", - shade_model_mult, - shade_model_mult, - shade_model_mult, - shade_model_mult - ); - } else { - mstring_append_fmt( - body, "\n" - " vtxD0 = clamp(oD0, 0.0, 1.0);\n" - " vtxD1 = clamp(oD1, 0.0, 1.0);\n" - " vtxB0 = clamp(oB0, 0.0, 1.0);\n" - " vtxB1 = clamp(oB1, 0.0, 1.0);\n" - " vtxFog = oFog.x;\n" - " vtxT0 = oT0;\n" - " vtxT1 = oT1;\n" - " vtxT2 = oT2;\n" - " vtxT3 = oT3;\n" - " gl_Position = oPos;\n" - " gl_PointSize = oPts.x;\n" - //" gl_ClipDistance[0] = oPos.w - clipRange.z;\n" // Near - //" gl_ClipDistance[1] = clipRange.w - oPos.w;\n" // Far - "\n" - "}\n" - ); - } + mstring_append_fmt( + body, "\n" + " vtxD0 = clamp(oD0, 0.0, 1.0);\n" + " vtxD1 = clamp(oD1, 0.0, 1.0);\n" + " vtxB0 = clamp(oB0, 0.0, 1.0);\n" + " vtxB1 = clamp(oB1, 0.0, 1.0);\n" + " vtxFog = oFog.x;\n" + " vtxT0 = oT0;\n" + " vtxT1 = oT1;\n" + " vtxT2 = oT2;\n" + " vtxT3 = oT3;\n" + " gl_Position = oPos;\n" + " gl_PointSize = oPts.x;\n" + //" gl_ClipDistance[0] = oPos.w - clipRange.z;\n" // Near + //" gl_ClipDistance[1] = clipRange.w - oPos.w;\n" // Far + "\n" + "}\n" + ); /* Return combined header + source */ if (state->vulkan) { // FIXME: Optimize uniforms From 1fed2a030dd4e46c08eee2fa091931dde1488e7c Mon Sep 17 00:00:00 2001 From: polymetal0 Date: Sat, 11 Jan 2025 22:28:06 +0100 Subject: [PATCH 05/11] W_param tests fixed - z_perspective refactor to PixelShader and texture_perspective var added - vtx_inv_w and vtx_inv_w_flat vars totally removed - Fixes warping of textures from previous commit w/o breaking tests - Also fixes dashboard menu background regression --- hw/xbox/nv2a/nv2a_regs.h | 2 ++ hw/xbox/nv2a/pgraph/gl/shaders.c | 4 ++-- hw/xbox/nv2a/pgraph/glsl/common.c | 16 ++++++------- hw/xbox/nv2a/pgraph/glsl/geom.c | 4 ---- hw/xbox/nv2a/pgraph/glsl/psh.c | 16 +++++++++---- hw/xbox/nv2a/pgraph/glsl/psh.h | 2 +- hw/xbox/nv2a/pgraph/glsl/vsh-ff.c | 4 +--- hw/xbox/nv2a/pgraph/glsl/vsh-prog.c | 36 ++++++++++++++++++----------- hw/xbox/nv2a/pgraph/glsl/vsh-prog.h | 2 +- hw/xbox/nv2a/pgraph/glsl/vsh.c | 6 ++--- hw/xbox/nv2a/pgraph/pgraph.c | 6 +++++ hw/xbox/nv2a/pgraph/psh.h | 2 ++ hw/xbox/nv2a/pgraph/shaders.c | 6 ++++- hw/xbox/nv2a/pgraph/shaders.h | 1 + hw/xbox/nv2a/pgraph/vk/shaders.c | 4 ++-- 15 files changed, 67 insertions(+), 44 deletions(-) diff --git a/hw/xbox/nv2a/nv2a_regs.h b/hw/xbox/nv2a/nv2a_regs.h index 78a9091eb54..3b58de681f5 100644 --- a/hw/xbox/nv2a/nv2a_regs.h +++ b/hw/xbox/nv2a/nv2a_regs.h @@ -461,6 +461,7 @@ # define NV_PGRAPH_CONTROL_2_STENCIL_OP_V_INCR 7 # define NV_PGRAPH_CONTROL_2_STENCIL_OP_V_DECR 8 #define NV_PGRAPH_CONTROL_3 0x00001958 +# define NV_PGRAPH_CONTROL_3_TEXTURE_PERSPECTIVE_ENABLE (1 << 6) # define NV_PGRAPH_CONTROL_3_SHADEMODE (1 << 7) # define NV_PGRAPH_CONTROL_3_SHADEMODE_FLAT 0 # define NV_PGRAPH_CONTROL_3_SHADEMODE_SMOOTH 1 @@ -879,6 +880,7 @@ # define NV097_SET_CONTROL0_STENCIL_WRITE_ENABLE (1 << 0) # define NV097_SET_CONTROL0_Z_FORMAT (1 << 12) # define NV097_SET_CONTROL0_Z_PERSPECTIVE_ENABLE (1 << 16) +# define NV097_SET_CONTROL0_TEXTURE_PERSPECTIVE_ENABLE (1 << 20) # define NV097_SET_COLOR_MATERIAL 0x00000298 # define NV097_SET_FOG_MODE 0x0000029C # define NV097_SET_FOG_MODE_V_LINEAR 0x2601 diff --git a/hw/xbox/nv2a/pgraph/gl/shaders.c b/hw/xbox/nv2a/pgraph/gl/shaders.c index ba17afbac2f..73e6b5f1508 100644 --- a/hw/xbox/nv2a/pgraph/gl/shaders.c +++ b/hw/xbox/nv2a/pgraph/gl/shaders.c @@ -220,7 +220,7 @@ static ShaderBinding *generate_shaders(const ShaderState *state) state->primitive_mode, state->smooth_shading, false, - state->z_perspective + state->z_perspective || state->texture_perspective ); if (geometry_shader_code) { const char* geometry_shader_code_str = @@ -242,7 +242,7 @@ static ShaderBinding *generate_shaders(const ShaderState *state) mstring_unref(vertex_shader_code); /* generate a fragment shader from register combiners */ - MString *fragment_shader_code = pgraph_gen_psh_glsl(state->psh, state->z_perspective); + MString *fragment_shader_code = pgraph_gen_psh_glsl(state->psh); const char *fragment_shader_code_str = mstring_get_str(fragment_shader_code); GLuint fragment_shader = create_gl_shader(GL_FRAGMENT_SHADER, diff --git a/hw/xbox/nv2a/pgraph/glsl/common.c b/hw/xbox/nv2a/pgraph/glsl/common.c index 78ff2d30101..fd762b31139 100644 --- a/hw/xbox/nv2a/pgraph/glsl/common.c +++ b/hw/xbox/nv2a/pgraph/glsl/common.c @@ -26,9 +26,9 @@ MString *pgraph_get_glsl_vtx_header(MString *out, bool location, bool smooth, bo const char *flat_s = "flat"; const char *noperspective_s = z_perspective ? "" : "noperspective"; const char *qualifier_s = smooth ? noperspective_s : flat_s; - const char *qualifiers[11] = { - "noperspective", flat_s, qualifier_s, qualifier_s, - qualifier_s, qualifier_s, noperspective_s, noperspective_s, + const char *qualifiers[9] = { + qualifier_s, qualifier_s, qualifier_s, + qualifier_s, noperspective_s, noperspective_s, noperspective_s, noperspective_s, noperspective_s }; @@ -36,17 +36,17 @@ MString *pgraph_get_glsl_vtx_header(MString *out, bool location, bool smooth, bo const char *float_s = "float"; const char *vec4_s = "vec4"; - const char *types[11] = { float_s, float_s, vec4_s, vec4_s, vec4_s, vec4_s, + const char *types[9] = { vec4_s, vec4_s, vec4_s, vec4_s, float_s, vec4_s, vec4_s, vec4_s, vec4_s }; const char *prefix_s = prefix ? "v_" : ""; - const char *names[11] = { - "vtx_inv_w", "vtx_inv_w_flat", "vtxD0", "vtxD1", "vtxB0", "vtxB1", - "vtxFog", "vtxT0", "vtxT1", "vtxT2", "vtxT3", + const char *names[9] = { + "vtxD0", "vtxD1", "vtxB0", "vtxB1", + "vtxFog", "vtxT0", "vtxT1", "vtxT2", "vtxT3", }; const char *suffix_s = array ? "[]" : ""; - for (int i = 0; i < 11; i++) { + for (int i = 0; i < 9; i++) { if (location) { mstring_append_fmt(out, "layout(location = %d) ", i); } diff --git a/hw/xbox/nv2a/pgraph/glsl/geom.c b/hw/xbox/nv2a/pgraph/glsl/geom.c index 17ed1637872..3c229e0e150 100644 --- a/hw/xbox/nv2a/pgraph/glsl/geom.c +++ b/hw/xbox/nv2a/pgraph/glsl/geom.c @@ -185,8 +185,6 @@ MString *pgraph_gen_geom_glsl(enum ShaderPolygonMode polygon_front_mode, " gl_PointSize = gl_in[index].gl_PointSize;\n" // " gl_ClipDistance[0] = gl_in[index].gl_ClipDistance[0];\n" // " gl_ClipDistance[1] = gl_in[index].gl_ClipDistance[1];\n" - " vtx_inv_w = v_vtx_inv_w[index];\n" - " vtx_inv_w_flat = v_vtx_inv_w[index];\n" " vtxD0 = v_vtxD0[index];\n" " vtxD1 = v_vtxD1[index];\n" " vtxB0 = v_vtxB0[index];\n" @@ -205,8 +203,6 @@ MString *pgraph_gen_geom_glsl(enum ShaderPolygonMode polygon_front_mode, " gl_PointSize = gl_in[index].gl_PointSize;\n" // " gl_ClipDistance[0] = gl_in[index].gl_ClipDistance[0];\n" // " gl_ClipDistance[1] = gl_in[index].gl_ClipDistance[1];\n" - " vtx_inv_w = v_vtx_inv_w[index];\n" - " vtx_inv_w_flat = v_vtx_inv_w[provoking_index];\n" " vtxD0 = v_vtxD0[provoking_index];\n" " vtxD1 = v_vtxD1[provoking_index];\n" " vtxB0 = v_vtxB0[provoking_index];\n" diff --git a/hw/xbox/nv2a/pgraph/glsl/psh.c b/hw/xbox/nv2a/pgraph/glsl/psh.c index d43bc11a4b6..e7cc94d74c3 100644 --- a/hw/xbox/nv2a/pgraph/glsl/psh.c +++ b/hw/xbox/nv2a/pgraph/glsl/psh.c @@ -726,15 +726,17 @@ static void apply_convolution_filter(const struct PixelShader *ps, MString *vars "}\n", tex, tex, tex, tex, tex_remap, tex); } -static MString *psh_convert(struct PixelShader *ps, bool z_perspective) +static MString *psh_convert(struct PixelShader *ps) { int i; + bool z_perspective = ps->state.z_perspective; + bool tex = ps->state.texture_perspective; const char *u = ps->state.vulkan ? "" : "uniform "; // FIXME: Remove MString *preflight = mstring_new(); pgraph_get_glsl_vtx_header(preflight, ps->state.vulkan, - ps->state.smooth_shading, true, false, false, z_perspective); + ps->state.smooth_shading, true, false, false, tex || z_perspective); if (ps->state.vulkan) { mstring_append_fmt(preflight, @@ -1196,11 +1198,15 @@ static MString *psh_convert(struct PixelShader *ps, bool z_perspective) } } + /* NV097_SET_CONTROL0_Z_PERSPECTIVE_ENABLE enables w-buffering + * not only gl_Position gets divided by the homogeneous coordinate, + * but also all other interpolated variables, which requires + * the division to be after the rasterization */ if (z_perspective) { mstring_append(ps->code, "gl_FragDepth = 1.0/(gl_FragCoord.w * clipRange.y);\n"); } - for (i = 0; i < ps->num_var_refs; i++) { + for (int i = 0; i < ps->num_var_refs; i++) { mstring_append_fmt(vars, "vec4 %s = vec4(0);\n", ps->var_refs[i]); if (strcmp(ps->var_refs[i], "r0") == 0) { if (ps->tex_modes[0] != PS_TEXTUREMODES_NONE) { @@ -1259,7 +1265,7 @@ static void parse_combiner_output(uint32_t value, struct OutputInfo *out) out->cd_alphablue = flags & 0x40; } -MString *pgraph_gen_psh_glsl(const PshState state, bool z_perspective) +MString *pgraph_gen_psh_glsl(const PshState state) { int i; struct PixelShader ps; @@ -1309,5 +1315,5 @@ MString *pgraph_gen_psh_glsl(const PshState state, bool z_perspective) ps.final_input.inv_r0 = flags & PS_FINALCOMBINERSETTING_COMPLEMENT_R0; } - return psh_convert(&ps, z_perspective); + return psh_convert(&ps); } diff --git a/hw/xbox/nv2a/pgraph/glsl/psh.h b/hw/xbox/nv2a/pgraph/glsl/psh.h index 89f376fd72f..1ae0b0db7ed 100644 --- a/hw/xbox/nv2a/pgraph/glsl/psh.h +++ b/hw/xbox/nv2a/pgraph/glsl/psh.h @@ -36,6 +36,6 @@ #define PSH_UBO_BINDING 1 #define PSH_TEX_BINDING 2 -MString *pgraph_gen_psh_glsl(const PshState state, bool z_perspective); +MString *pgraph_gen_psh_glsl(const PshState state); #endif diff --git a/hw/xbox/nv2a/pgraph/glsl/vsh-ff.c b/hw/xbox/nv2a/pgraph/glsl/vsh-ff.c index 8a20092cfa0..8d4d1d3998f 100644 --- a/hw/xbox/nv2a/pgraph/glsl/vsh-ff.c +++ b/hw/xbox/nv2a/pgraph/glsl/vsh-ff.c @@ -451,9 +451,7 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz " oPos.w = clamp(oPos.w, -1.884467e+019, -5.421011e-20);\n" " } else {\n" " oPos.w = clamp(oPos.w, 5.421011e-20, 1.884467e+019);\n" - " }\n" - " vtx_inv_w = 1.0 / oPos.w;\n" - " vtx_inv_w_flat = vtx_inv_w;\n"); + " }\n"); } static void append_skinning_code(MString* str, bool mix, diff --git a/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c b/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c index 32e32510563..ea3f4c84f44 100644 --- a/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c +++ b/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c @@ -799,6 +799,7 @@ void pgraph_gen_vsh_prog_glsl(uint16_t version, const uint32_t *tokens, unsigned int length, bool z_perspective, + bool texture, bool vulkan, MString *header, MString *body) { @@ -836,8 +837,6 @@ void pgraph_gen_vsh_prog_glsl(uint16_t version, " } else {\n" " oPos.w = clamp(oPos.w, 5.421011e-20, 1.884467e+019);\n" " }\n" - " vtx_inv_w = 1.0 / oPos.w;\n" - " vtx_inv_w_flat = vtx_inv_w;\n" ); mstring_append(body, @@ -862,24 +861,35 @@ void pgraph_gen_vsh_prog_glsl(uint16_t version, mstring_append(body, " if (clipRange.y != clipRange.x) {\n"); - if (vulkan) { - mstring_append(body, " oPos.z = (oPos.z - clipRange.z)/(clipRange.w - clipRange.z);\n"); - } else { + if (texture || z_perspective) { + if (vulkan) { + mstring_append(body, " oPos.z = (oPos.z - clipRange.z)/(clipRange.w - clipRange.z);\n"); + } else { + mstring_append(body, + " oPos.z = (oPos.z - clipRange.z)/(0.5*(clipRange.w " + "- clipRange.z)) - 1;\n"); + } mstring_append(body, - " oPos.z = (oPos.z - clipRange.z)/(0.5*(clipRange.w " - "- clipRange.z)) - 1;\n"); - } - mstring_append(body, - " }\n" - ); - if(z_perspective) { + " }\n" + ); + mstring_append(body, " oPos.xyz *= oPos.w;\n"); } else { + if (vulkan) { + mstring_append(body, " oPos.z = (oPos.z - clipRange.x)/(clipRange.y - clipRange.x);\n"); + } else { + mstring_append(body, + " oPos.z = (oPos.z - clipRange.x)/(0.5*(clipRange.y " + "- clipRange.x)) - 1;\n"); + } + mstring_append(body, + " }\n" + ); mstring_append( body, /* Correct for the perspective divide */ - " if (oPos.w < clipRange.z) {\n" + " if (oPos.w < 0.0) {\n" /* undo the perspective divide in the case where the point would be * clipped so opengl can clip it correctly */ " oPos.xyz *= oPos.w;\n" diff --git a/hw/xbox/nv2a/pgraph/glsl/vsh-prog.h b/hw/xbox/nv2a/pgraph/glsl/vsh-prog.h index 84d8141c5e5..40f59765fb0 100644 --- a/hw/xbox/nv2a/pgraph/glsl/vsh-prog.h +++ b/hw/xbox/nv2a/pgraph/glsl/vsh-prog.h @@ -29,7 +29,7 @@ #define HW_XBOX_NV2A_PGRAPH_GLSL_VSH_PROG_H void pgraph_gen_vsh_prog_glsl(uint16_t version, const uint32_t *tokens, - unsigned int length, bool z_perspective, + unsigned int length, bool z_perspective, bool texture, bool vulkan, MString *header, MString *body); #endif diff --git a/hw/xbox/nv2a/pgraph/glsl/vsh.c b/hw/xbox/nv2a/pgraph/glsl/vsh.c index 6655c823d83..23e0875e113 100644 --- a/hw/xbox/nv2a/pgraph/glsl/vsh.c +++ b/hw/xbox/nv2a/pgraph/glsl/vsh.c @@ -75,12 +75,10 @@ MString *pgraph_gen_vsh_glsl(const ShaderState *state, bool prefix_outputs) "}\n"); pgraph_get_glsl_vtx_header(header, state->vulkan, state->smooth_shading, - false, prefix_outputs, false, state->z_perspective); + false, prefix_outputs, false, state->texture_perspective || state->z_perspective); if (prefix_outputs) { mstring_append(header, - "#define vtx_inv_w v_vtx_inv_w\n" - "#define vtx_inv_w_flat v_vtx_inv_w_flat\n" "#define vtxD0 v_vtxD0\n" "#define vtxD1 v_vtxD1\n" "#define vtxB0 v_vtxB0\n" @@ -142,7 +140,7 @@ MString *pgraph_gen_vsh_glsl(const ShaderState *state, bool prefix_outputs) } else if (state->vertex_program) { pgraph_gen_vsh_prog_glsl(VSH_VERSION_XVS, (uint32_t *)state->program_data, - state->program_length, state->z_perspective, + state->program_length, state->z_perspective, state->texture_perspective, state->vulkan, header, body); } else { assert(false); diff --git a/hw/xbox/nv2a/pgraph/pgraph.c b/hw/xbox/nv2a/pgraph/pgraph.c index 534daa3c0fe..5d9acfef33a 100644 --- a/hw/xbox/nv2a/pgraph/pgraph.c +++ b/hw/xbox/nv2a/pgraph/pgraph.c @@ -1073,6 +1073,12 @@ DEF_METHOD(NV097, SET_CONTROL0) PG_SET_MASK(NV_PGRAPH_CONTROL_0, NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE, z_perspective); + + bool texture_perspective = + parameter & NV097_SET_CONTROL0_TEXTURE_PERSPECTIVE_ENABLE; + PG_SET_MASK(NV_PGRAPH_CONTROL_3, + NV_PGRAPH_CONTROL_3_TEXTURE_PERSPECTIVE_ENABLE, + texture_perspective); } DEF_METHOD(NV097, SET_COLOR_MATERIAL) diff --git a/hw/xbox/nv2a/pgraph/psh.h b/hw/xbox/nv2a/pgraph/psh.h index 13660457078..811bce10928 100644 --- a/hw/xbox/nv2a/pgraph/psh.h +++ b/hw/xbox/nv2a/pgraph/psh.h @@ -85,6 +85,8 @@ typedef struct PshState { bool window_clip_exclusive; bool smooth_shading; + bool z_perspective; + bool texture_perspective; } PshState; #endif diff --git a/hw/xbox/nv2a/pgraph/shaders.c b/hw/xbox/nv2a/pgraph/shaders.c index 8d2c77a535b..f3036f29756 100644 --- a/hw/xbox/nv2a/pgraph/shaders.c +++ b/hw/xbox/nv2a/pgraph/shaders.c @@ -91,10 +91,14 @@ ShaderState pgraph_get_shader_state(PGRAPHState *pg) } /* vertex program stuff */ - state.vertex_program = vertex_program, + state.vertex_program = vertex_program; state.z_perspective = pgraph_reg_r(pg, NV_PGRAPH_CONTROL_0) & NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE; + state.psh.z_perspective = state.z_perspective; + state.texture_perspective = pgraph_reg_r(pg, NV_PGRAPH_CONTROL_3) & + NV_PGRAPH_CONTROL_3_TEXTURE_PERSPECTIVE_ENABLE; + state.psh.texture_perspective = state.texture_perspective; state.point_params_enable = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CSV0_D), NV_PGRAPH_CSV0_D_POINTPARAMSENABLE); state.point_size = diff --git a/hw/xbox/nv2a/pgraph/shaders.h b/hw/xbox/nv2a/pgraph/shaders.h index 71febe2e2f8..9c216a971b4 100644 --- a/hw/xbox/nv2a/pgraph/shaders.h +++ b/hw/xbox/nv2a/pgraph/shaders.h @@ -89,6 +89,7 @@ typedef struct ShaderState { uint32_t program_data[NV2A_MAX_TRANSFORM_PROGRAM_LENGTH][VSH_TOKEN_SIZE]; int program_length; bool z_perspective; + bool texture_perspective; /* primitive format for geometry shader */ enum ShaderPolygonMode polygon_front_mode; diff --git a/hw/xbox/nv2a/pgraph/vk/shaders.c b/hw/xbox/nv2a/pgraph/vk/shaders.c index b3bb77e21d6..06a2c75146a 100644 --- a/hw/xbox/nv2a/pgraph/vk/shaders.c +++ b/hw/xbox/nv2a/pgraph/vk/shaders.c @@ -396,7 +396,7 @@ static ShaderBinding *gen_shaders(PGRAPHState *pg, ShaderState *state) MString *geometry_shader_code = pgraph_gen_geom_glsl( state->polygon_front_mode, state->polygon_back_mode, - state->primitive_mode, state->smooth_shading, true, state->z_perspective); + state->primitive_mode, state->smooth_shading, true, state->z_perspective || state->texture_perspective); if (geometry_shader_code) { NV2A_VK_DPRINTF("geometry shader: \n%s", mstring_get_str(geometry_shader_code)); @@ -417,7 +417,7 @@ static ShaderBinding *gen_shaders(PGRAPHState *pg, ShaderState *state) mstring_get_str(vertex_shader_code)); mstring_unref(vertex_shader_code); - MString *fragment_shader_code = pgraph_gen_psh_glsl(state->psh, state->z_perspective); + MString *fragment_shader_code = pgraph_gen_psh_glsl(state->psh); NV2A_VK_DPRINTF("fragment shader: \n%s", mstring_get_str(fragment_shader_code)); snode->fragment = pgraph_vk_create_shader_module_from_glsl( From bcb11b7a08857c06b22b080158e38b7d5111f9bd Mon Sep 17 00:00:00 2001 From: polymetal0 Date: Sun, 12 Jan 2025 00:03:31 +0100 Subject: [PATCH 06/11] Syntax --- hw/xbox/nv2a/pgraph/glsl/psh.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/xbox/nv2a/pgraph/glsl/psh.c b/hw/xbox/nv2a/pgraph/glsl/psh.c index e7cc94d74c3..dd412840dbf 100644 --- a/hw/xbox/nv2a/pgraph/glsl/psh.c +++ b/hw/xbox/nv2a/pgraph/glsl/psh.c @@ -250,9 +250,9 @@ static MString* get_var(struct PixelShader *ps, int reg, bool is_dest) break; case PS_REGISTER_C0: if (ps->flags & PS_COMBINERCOUNT_UNIQUE_C0 || ps->cur_stage == 8) { - MString *reg = mstring_from_fmt("c0_%d", ps->cur_stage); - add_const_ref(ps, mstring_get_str(reg)); - return reg; + MString *reg_name = mstring_from_fmt("c0_%d", ps->cur_stage); + add_const_ref(ps, mstring_get_str(reg_name)); + return reg_name; } else { // Same c0 add_const_ref(ps, "c0_0"); return mstring_from_str("c0_0"); @@ -260,9 +260,9 @@ static MString* get_var(struct PixelShader *ps, int reg, bool is_dest) break; case PS_REGISTER_C1: if (ps->flags & PS_COMBINERCOUNT_UNIQUE_C1 || ps->cur_stage == 8) { - MString *reg = mstring_from_fmt("c1_%d", ps->cur_stage); - add_const_ref(ps, mstring_get_str(reg)); - return reg; + MString *reg_name = mstring_from_fmt("c1_%d", ps->cur_stage); + add_const_ref(ps, mstring_get_str(reg_name)); + return reg_name; } else { // Same c1 add_const_ref(ps, "c1_0"); return mstring_from_str("c1_0"); @@ -726,7 +726,7 @@ static void apply_convolution_filter(const struct PixelShader *ps, MString *vars "}\n", tex, tex, tex, tex, tex_remap, tex); } -static MString *psh_convert(struct PixelShader *ps) +static MString* psh_convert(struct PixelShader *ps) { int i; From 4da74dbee17736febbff022d0ea957f07ba9db47 Mon Sep 17 00:00:00 2001 From: polymetal0 Date: Sun, 12 Jan 2025 00:06:04 +0100 Subject: [PATCH 07/11] more syntax --- hw/xbox/nv2a/pgraph/glsl/psh.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/hw/xbox/nv2a/pgraph/glsl/psh.c b/hw/xbox/nv2a/pgraph/glsl/psh.c index dd412840dbf..64613466d4a 100644 --- a/hw/xbox/nv2a/pgraph/glsl/psh.c +++ b/hw/xbox/nv2a/pgraph/glsl/psh.c @@ -250,9 +250,9 @@ static MString* get_var(struct PixelShader *ps, int reg, bool is_dest) break; case PS_REGISTER_C0: if (ps->flags & PS_COMBINERCOUNT_UNIQUE_C0 || ps->cur_stage == 8) { - MString *reg_name = mstring_from_fmt("c0_%d", ps->cur_stage); - add_const_ref(ps, mstring_get_str(reg_name)); - return reg_name; + MString *reg = mstring_from_fmt("c0_%d", ps->cur_stage); + add_const_ref(ps, mstring_get_str(reg)); + return reg; } else { // Same c0 add_const_ref(ps, "c0_0"); return mstring_from_str("c0_0"); @@ -260,9 +260,9 @@ static MString* get_var(struct PixelShader *ps, int reg, bool is_dest) break; case PS_REGISTER_C1: if (ps->flags & PS_COMBINERCOUNT_UNIQUE_C1 || ps->cur_stage == 8) { - MString *reg_name = mstring_from_fmt("c1_%d", ps->cur_stage); - add_const_ref(ps, mstring_get_str(reg_name)); - return reg_name; + MString *reg = mstring_from_fmt("c1_%d", ps->cur_stage); + add_const_ref(ps, mstring_get_str(reg)); + return reg; } else { // Same c1 add_const_ref(ps, "c1_0"); return mstring_from_str("c1_0"); @@ -726,10 +726,8 @@ static void apply_convolution_filter(const struct PixelShader *ps, MString *vars "}\n", tex, tex, tex, tex, tex_remap, tex); } -static MString* psh_convert(struct PixelShader *ps) +static MString *psh_convert(struct PixelShader *ps) { - int i; - bool z_perspective = ps->state.z_perspective; bool tex = ps->state.texture_perspective; const char *u = ps->state.vulkan ? "" : "uniform "; // FIXME: Remove @@ -895,7 +893,7 @@ static MString* psh_convert(struct PixelShader *ps) ps->code = mstring_new(); - for (i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) { const char *sampler_type = get_sampler_type(ps->tex_modes[i], &ps->state, i); From 687787e557f48e120f84f54b263dceadc8deaa73 Mon Sep 17 00:00:00 2001 From: polymetal0 Date: Sun, 12 Jan 2025 00:08:59 +0100 Subject: [PATCH 08/11] Revert "more syntax" This reverts commit 4da74dbee17736febbff022d0ea957f07ba9db47. --- hw/xbox/nv2a/pgraph/glsl/psh.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/hw/xbox/nv2a/pgraph/glsl/psh.c b/hw/xbox/nv2a/pgraph/glsl/psh.c index 64613466d4a..dd412840dbf 100644 --- a/hw/xbox/nv2a/pgraph/glsl/psh.c +++ b/hw/xbox/nv2a/pgraph/glsl/psh.c @@ -250,9 +250,9 @@ static MString* get_var(struct PixelShader *ps, int reg, bool is_dest) break; case PS_REGISTER_C0: if (ps->flags & PS_COMBINERCOUNT_UNIQUE_C0 || ps->cur_stage == 8) { - MString *reg = mstring_from_fmt("c0_%d", ps->cur_stage); - add_const_ref(ps, mstring_get_str(reg)); - return reg; + MString *reg_name = mstring_from_fmt("c0_%d", ps->cur_stage); + add_const_ref(ps, mstring_get_str(reg_name)); + return reg_name; } else { // Same c0 add_const_ref(ps, "c0_0"); return mstring_from_str("c0_0"); @@ -260,9 +260,9 @@ static MString* get_var(struct PixelShader *ps, int reg, bool is_dest) break; case PS_REGISTER_C1: if (ps->flags & PS_COMBINERCOUNT_UNIQUE_C1 || ps->cur_stage == 8) { - MString *reg = mstring_from_fmt("c1_%d", ps->cur_stage); - add_const_ref(ps, mstring_get_str(reg)); - return reg; + MString *reg_name = mstring_from_fmt("c1_%d", ps->cur_stage); + add_const_ref(ps, mstring_get_str(reg_name)); + return reg_name; } else { // Same c1 add_const_ref(ps, "c1_0"); return mstring_from_str("c1_0"); @@ -726,8 +726,10 @@ static void apply_convolution_filter(const struct PixelShader *ps, MString *vars "}\n", tex, tex, tex, tex, tex_remap, tex); } -static MString *psh_convert(struct PixelShader *ps) +static MString* psh_convert(struct PixelShader *ps) { + int i; + bool z_perspective = ps->state.z_perspective; bool tex = ps->state.texture_perspective; const char *u = ps->state.vulkan ? "" : "uniform "; // FIXME: Remove @@ -893,7 +895,7 @@ static MString *psh_convert(struct PixelShader *ps) ps->code = mstring_new(); - for (int i = 0; i < 4; i++) { + for (i = 0; i < 4; i++) { const char *sampler_type = get_sampler_type(ps->tex_modes[i], &ps->state, i); From 27d67817a59f9c3f0347c7c464f7cb08ebeeccbc Mon Sep 17 00:00:00 2001 From: polymetal0 Date: Sun, 12 Jan 2025 00:10:43 +0100 Subject: [PATCH 09/11] fix syntax --- hw/xbox/nv2a/pgraph/glsl/psh.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/xbox/nv2a/pgraph/glsl/psh.c b/hw/xbox/nv2a/pgraph/glsl/psh.c index dd412840dbf..2c9e3f6ff76 100644 --- a/hw/xbox/nv2a/pgraph/glsl/psh.c +++ b/hw/xbox/nv2a/pgraph/glsl/psh.c @@ -728,8 +728,6 @@ static void apply_convolution_filter(const struct PixelShader *ps, MString *vars static MString* psh_convert(struct PixelShader *ps) { - int i; - bool z_perspective = ps->state.z_perspective; bool tex = ps->state.texture_perspective; const char *u = ps->state.vulkan ? "" : "uniform "; // FIXME: Remove @@ -895,7 +893,7 @@ static MString* psh_convert(struct PixelShader *ps) ps->code = mstring_new(); - for (i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) { const char *sampler_type = get_sampler_type(ps->tex_modes[i], &ps->state, i); From 4673bb9616700adb5e4f163a93a4abfe5e1b41c3 Mon Sep 17 00:00:00 2001 From: polymetal0 Date: Sun, 12 Jan 2025 00:15:19 +0100 Subject: [PATCH 10/11] end fix syntax --- hw/xbox/nv2a/pgraph/glsl/psh.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/xbox/nv2a/pgraph/glsl/psh.c b/hw/xbox/nv2a/pgraph/glsl/psh.c index 2c9e3f6ff76..218ea1f53c0 100644 --- a/hw/xbox/nv2a/pgraph/glsl/psh.c +++ b/hw/xbox/nv2a/pgraph/glsl/psh.c @@ -967,8 +967,7 @@ static MString* psh_convert(struct PixelShader *ps) i, ps->input_tex[i], ps->input_tex[i]); } - mstring_append_fmt(vars, "dsdt%d = bumpMat%d * dsdt%d;\n", - i, i, i, i); + mstring_append_fmt(vars, "dsdt%d = bumpMat%d * dsdt%d;\n", i, i, i); if (ps->state.dim_tex[i] == 2) { mstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, %s(pT%d.xy + dsdt%d));\n", @@ -995,7 +994,7 @@ static MString* psh_convert(struct PixelShader *ps) } mstring_append_fmt(vars, "dsdtl%d.st = bumpMat%d * dsdtl%d.st;\n", - i, i, i, i); + i, i, i); if (ps->state.dim_tex[i] == 2) { mstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, %s(pT%d.xy + dsdtl%d.st));\n", @@ -1157,7 +1156,7 @@ static MString* psh_convert(struct PixelShader *ps) } } - for (i = 0; i < ps->num_stages; i++) { + for (int i = 0; i < ps->num_stages; i++) { ps->cur_stage = i; mstring_append_fmt(ps->code, "// Stage %d\n", i); MString* color = add_stage_code(ps, ps->stage[i].rgb_input, ps->stage[i].rgb_output, "rgb", false); From 8ac1fce533d40a90f1af6068cfdaf2d89d955679 Mon Sep 17 00:00:00 2001 From: polymetal0 Date: Sun, 12 Jan 2025 17:08:45 +0100 Subject: [PATCH 11/11] Fix dashboard menu background - Regression of some fixed missing shadows from #577, identified the source of the problem --- hw/xbox/nv2a/pgraph/glsl/vsh-prog.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c b/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c index 96e9a77c7ff..1ffbc897393 100644 --- a/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c +++ b/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c @@ -860,13 +860,23 @@ void pgraph_gen_vsh_prog_glsl(uint16_t version, mstring_append(body, " if (clipRange.y != clipRange.x) {\n"); - if (texture || z_perspective) { - if (vulkan) { - mstring_append(body, " oPos.z = (oPos.z - clipRange.z)/(clipRange.w - clipRange.z);\n"); + if (texture) { + if (z_perspective) { + if (vulkan) { + mstring_append(body, " oPos.z = (oPos.z - clipRange.z)/(clipRange.w - clipRange.z);\n"); + } else { + mstring_append(body, " oPos.z = (oPos.z - clipRange.z)/(0.5*(clipRange.w - clipRange.z)) - 1;\n"); + } } else { - mstring_append(body, - " oPos.z = (oPos.z - clipRange.z)/(0.5*(clipRange.w " - "- clipRange.z)) - 1;\n"); + /* There is some clip distance / clip range issue here; if using x and y as near and far planes, + * character shadows in many games get clipped (e.g. Conker, Halo CE, Wallace & Gromit), + * and using z and w as near and far planes restores them but the Xbox dashboard background + * gets clipped. Set / disable gl_clipDistance? ( x = 0, y = zmax, z = zclipmin, w = zclipmax ) */ + if (vulkan) { + mstring_append(body, " oPos.z = (oPos.z - clipRange.x)/(clipRange.y - clipRange.x);\n"); + } else { + mstring_append(body, " oPos.z = (oPos.z - clipRange.x)/(0.5*(clipRange.y - clipRange.x)) - 1;\n"); + } } mstring_append(body, " }\n"