From b2ded13f6b91e52b93f4fc8741269c6c8ca18397 Mon Sep 17 00:00:00 2001 From: coldhex Date: Sat, 17 May 2025 12:34:59 +0300 Subject: [PATCH] nv2a: Fix polygon line mode and implement flat shading provoking vertex Xbox draws lines in polygon mode without trying to avoid overlaps, e.g. internal edge lines are drawn twice for triangle strips and fans. This is evidenced by using additive blending and also stencil adds. This commit removes the gl_PrimitiveIDIn==0 checks which were there to avoid drawing lines twice. This commit also implements flat shading first/last provoking vertex handling. This fixes triangle strip and fan flat shading in nxdk_pgraph_tests shade model tests. --- hw/xbox/nv2a/nv2a_regs.h | 6 + hw/xbox/nv2a/pgraph/glsl/geom.c | 272 ++++++++++++++---------------- hw/xbox/nv2a/pgraph/glsl/geom.h | 1 + hw/xbox/nv2a/pgraph/methods.h.inc | 1 + hw/xbox/nv2a/pgraph/pgraph.c | 7 + hw/xbox/nv2a/pgraph/vk/draw.c | 4 +- 6 files changed, 139 insertions(+), 152 deletions(-) diff --git a/hw/xbox/nv2a/nv2a_regs.h b/hw/xbox/nv2a/nv2a_regs.h index 97fb4c4812..e6543ae8d0 100644 --- a/hw/xbox/nv2a/nv2a_regs.h +++ b/hw/xbox/nv2a/nv2a_regs.h @@ -471,6 +471,9 @@ # 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_PROVOKING_VERTEX (1 << 0) +# define NV_PGRAPH_CONTROL_3_PROVOKING_VERTEX_LAST 0 +# define NV_PGRAPH_CONTROL_3_PROVOKING_VERTEX_FIRST 1 # define NV_PGRAPH_CONTROL_3_SHADEMODE (1 << 7) # define NV_PGRAPH_CONTROL_3_SHADEMODE_FLAT 0 # define NV_PGRAPH_CONTROL_3_SHADEMODE_SMOOTH 1 @@ -1062,6 +1065,9 @@ # define NV097_SET_TEXGEN_VIEW_MODEL_INFINITE_VIEWER 1 # define NV097_SET_FOG_PLANE 0x000009D0 # define NV097_SET_SPECULAR_PARAMS 0x000009E0 +# define NV097_SET_PROVOKING_VERTEX 0x000009FC +# define NV097_SET_PROVOKING_VERTEX_LAST 0 +# define NV097_SET_PROVOKING_VERTEX_FIRST 1 # define NV097_SET_SCENE_AMBIENT_COLOR 0x00000A10 # define NV097_SET_VIEWPORT_OFFSET 0x00000A20 # define NV097_SET_POINT_PARAMS 0x00000A30 diff --git a/hw/xbox/nv2a/pgraph/glsl/geom.c b/hw/xbox/nv2a/pgraph/glsl/geom.c index 2170696a08..dd5dc363f5 100644 --- a/hw/xbox/nv2a/pgraph/glsl/geom.c +++ b/hw/xbox/nv2a/pgraph/glsl/geom.c @@ -38,6 +38,11 @@ void pgraph_glsl_set_geom_state(PGRAPHState *pg, GeomState *state) NV_PGRAPH_CONTROL_3_SHADEMODE) == NV_PGRAPH_CONTROL_3_SHADEMODE_SMOOTH; + state->first_vertex_is_provoking = + GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_3), + NV_PGRAPH_CONTROL_3_PROVOKING_VERTEX) == + NV_PGRAPH_CONTROL_3_PROVOKING_VERTEX_FIRST; + state->z_perspective = pgraph_reg_r(pg, NV_PGRAPH_CONTROL_0) & NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE; } @@ -83,67 +88,51 @@ MString *pgraph_glsl_gen_geom(const GeomState *state, GenGeomGlslOptions opts) const char *layout_in = NULL; const char *layout_out = NULL; const char *body = NULL; + const char *provoking_index = "0"; + /* TODO: frontface/backface culling for polygon modes POLY_MODE_LINE and + * POLY_MODE_POINT. + * FIXME: OpenGL/Vulkan does not specify absolute vertex order when input + * is a triangle list, triangle strip or fan. Only vertex winding order + * is specified. Currently we assume input triangle vertex order follows + * the last provoking vertex convention. + */ switch (state->primitive_mode) { case PRIM_TYPE_POINTS: return NULL; case PRIM_TYPE_LINES: case PRIM_TYPE_LINE_LOOP: case PRIM_TYPE_LINE_STRIP: + provoking_index = state->first_vertex_is_provoking ? "0" : "1"; need_linez = true; layout_in = "layout(lines) in;\n"; layout_out = "layout(line_strip, max_vertices = 2) out;\n"; body = " mat4 pz = calc_linez(0, 1);\n" - " emit_vertex(0, 0, pz);\n" - " emit_vertex(1, 1, pz);\n" + " emit_vertex(0, pz);\n" + " emit_vertex(1, pz);\n" " EndPrimitive();\n"; break; case PRIM_TYPE_TRIANGLES: - need_triz = true; - layout_in = "layout(triangles) in;\n"; - if (polygon_mode == POLY_MODE_FILL) { - layout_out = "layout(triangle_strip, max_vertices = 3) out;\n"; - body = " mat4 pz = calc_triz(0, 1, 2);\n" - " emit_vertex(0, 0, pz);\n" - " emit_vertex(1, 1, pz);\n" - " emit_vertex(2, 2, pz);\n" - " EndPrimitive();\n"; - } else if (polygon_mode == POLY_MODE_LINE) { - need_linez = true; - layout_out = "layout(line_strip, max_vertices = 4) out;\n"; - body = " float triMZ = calc_triz(0, 1, 2)[3].x;\n" - " mat4 pz1 = calc_linez(0, 1);\n" - " pz1[3].x = triMZ;\n" - " mat4 pz2 = calc_linez(1, 2);\n" - " pz2[3].x = triMZ;\n" - " mat4 pz3 = calc_linez(2, 0);\n" - " pz3[3].x = triMZ;\n" - " emit_vertex(0, 0, pz1);\n" - " emit_vertex(1, 0, pz1);\n" - " emit_vertex(2, 0, pz2);\n" - " emit_vertex(0, 0, pz3);\n" - " EndPrimitive();\n"; - } else { - assert(polygon_mode == POLY_MODE_POINT); - layout_out = "layout(points, max_vertices = 3) out;\n"; - body = " mat4 pz = calc_triz(0, 1, 2);\n" - " emit_vertex(0, 0, mat4(pz[0], pz[0], pz[0], pz[3]));\n" - " EndPrimitive();\n" - " emit_vertex(1, 0, mat4(pz[1], pz[1], pz[1], pz[3]));\n" - " EndPrimitive();\n" - " emit_vertex(2, 0, mat4(pz[2], pz[2], pz[2], pz[3]));\n" - " EndPrimitive();\n"; - } - break; case PRIM_TYPE_TRIANGLE_STRIP: case PRIM_TYPE_TRIANGLE_FAN: + if (state->first_vertex_is_provoking) { + if (state->primitive_mode == PRIM_TYPE_TRIANGLE_STRIP) { + provoking_index = "gl_PrimitiveIDIn & 1"; + } else if (state->primitive_mode == PRIM_TYPE_TRIANGLE_FAN) { + provoking_index = "1"; + } else { + provoking_index = "0"; + } + } else { + provoking_index = "2"; + } need_triz = true; layout_in = "layout(triangles) in;\n"; if (polygon_mode == POLY_MODE_FILL) { layout_out = "layout(triangle_strip, max_vertices = 3) out;\n"; body = " mat4 pz = calc_triz(0, 1, 2);\n" - " emit_vertex(0, 0, pz);\n" - " emit_vertex(1, 1, pz);\n" - " emit_vertex(2, 2, pz);\n" + " emit_vertex(0, pz);\n" + " emit_vertex(1, pz);\n" + " emit_vertex(2, pz);\n" " EndPrimitive();\n"; } else if (polygon_mode == POLY_MODE_LINE) { need_linez = true; @@ -155,31 +144,37 @@ MString *pgraph_glsl_gen_geom(const GeomState *state, GenGeomGlslOptions opts) " pz2[3].x = triMZ;\n" " mat4 pz3 = calc_linez(2, 0);\n" " pz3[3].x = triMZ;\n" - " if (gl_PrimitiveIDIn == 0) {\n" - " emit_vertex(0, 0, pz1);\n" - " }\n" - " emit_vertex(1, 0, pz1);\n" - " emit_vertex(2, 0, pz2);\n" - " emit_vertex(0, 0, pz3);\n" + " emit_vertex(0, pz1);\n" + " emit_vertex(1, pz1);\n" + " emit_vertex(2, pz2);\n" + " emit_vertex(0, pz3);\n" " EndPrimitive();\n"; } else { assert(polygon_mode == POLY_MODE_POINT); layout_out = "layout(points, max_vertices = 3) out;\n"; body = " mat4 pz = calc_triz(0, 1, 2);\n" - " if (gl_PrimitiveIDIn == 0) {\n" - " emit_vertex(0, 0, mat4(pz[0], pz[0], pz[0], pz[3]));\n" - " EndPrimitive();\n" - " emit_vertex(1, 0, mat4(pz[1], pz[1], pz[1], pz[3]));\n" - " EndPrimitive();\n" - " }\n" - " emit_vertex(2, 0, mat4(pz[2], pz[2], pz[2], pz[3]));\n" + " emit_vertex(0, mat4(pz[0], pz[0], pz[0], pz[3]));\n" + " EndPrimitive();\n" + " emit_vertex(1, mat4(pz[1], pz[1], pz[1], pz[3]));\n" + " EndPrimitive();\n" + " emit_vertex(2, mat4(pz[2], pz[2], pz[2], pz[3]));\n" " EndPrimitive();\n"; } break; case PRIM_TYPE_QUADS: + provoking_index = "3"; need_quadz = true; layout_in = "layout(lines_adjacency) in;\n"; - if (polygon_mode == POLY_MODE_LINE) { + if (polygon_mode == POLY_MODE_FILL) { + layout_out = "layout(triangle_strip, max_vertices = 4) out;\n"; + body = " mat4 pz, pz2;\n" + " calc_quadz(0, 1, 2, 3, pz, pz2);\n" + " emit_vertex(1, pz);\n" + " emit_vertex(2, pz2);\n" + " emit_vertex(0, pz);\n" + " emit_vertex(3, pz2);\n" + " EndPrimitive();\n"; + } else if (polygon_mode == POLY_MODE_LINE) { need_linez = true; layout_out = "layout(line_strip, max_vertices = 5) out;\n"; body = " mat4 pz, pzs;\n" @@ -192,40 +187,42 @@ MString *pgraph_glsl_gen_geom(const GeomState *state, GenGeomGlslOptions opts) " pz3[3].x = pzs[3].x;\n" " mat4 pz4 = calc_linez(3, 0);\n" " pz4[3].x = pzs[3].x;\n" - " emit_vertex(0, 3, pz1);\n" - " emit_vertex(1, 3, pz1);\n" - " emit_vertex(2, 3, pz2);\n" - " emit_vertex(3, 3, pz3);\n" - " emit_vertex(0, 3, pz4);\n" - " EndPrimitive();\n"; - } else if (polygon_mode == POLY_MODE_FILL) { - layout_out = "layout(triangle_strip, max_vertices = 4) out;\n"; - body = " mat4 pz, pz2;\n" - " calc_quadz(0, 1, 2, 3, pz, pz2);\n" - " emit_vertex(1, 3, pz);\n" - " emit_vertex(2, 3, pz2);\n" - " emit_vertex(0, 3, pz);\n" - " emit_vertex(3, 3, pz2);\n" + " emit_vertex(0, pz1);\n" + " emit_vertex(1, pz1);\n" + " emit_vertex(2, pz2);\n" + " emit_vertex(3, pz3);\n" + " emit_vertex(0, pz4);\n" " EndPrimitive();\n"; } else { assert(polygon_mode == POLY_MODE_POINT); layout_out = "layout(points, max_vertices = 4) out;\n"; body = " mat4 pz, pz2;\n" " calc_quadz(0, 1, 2, 3, pz, pz2);\n" - " emit_vertex(0, 3, mat4(pz[0], pz[0], pz[0], pz[3]));\n" + " emit_vertex(0, mat4(pz[0], pz[0], pz[0], pz[3]));\n" " EndPrimitive();\n" - " emit_vertex(1, 3, mat4(pz[1], pz[1], pz[1], pz[3]));\n" + " emit_vertex(1, mat4(pz[1], pz[1], pz[1], pz[3]));\n" " EndPrimitive();\n" - " emit_vertex(2, 3, mat4(pz[2], pz[2], pz[2], pz[3]));\n" + " emit_vertex(2, mat4(pz[2], pz[2], pz[2], pz[3]));\n" " EndPrimitive();\n" - " emit_vertex(3, 3, mat4(pz2[2], pz2[2], pz2[2], pz2[3]));\n" + " emit_vertex(3, mat4(pz2[2], pz2[2], pz2[2], pz2[3]));\n" " EndPrimitive();\n"; } break; case PRIM_TYPE_QUAD_STRIP: + provoking_index = "3"; need_quadz = true; layout_in = "layout(lines_adjacency) in;\n"; - if (polygon_mode == POLY_MODE_LINE) { + if (polygon_mode == POLY_MODE_FILL) { + layout_out = "layout(triangle_strip, max_vertices = 4) out;\n"; + body = " if ((gl_PrimitiveIDIn & 1) != 0) { return; }\n" + " mat4 pz, pz2;\n" + " calc_quadz(2, 0, 1, 3, pz, pz2);\n" + " emit_vertex(0, pz);\n" + " emit_vertex(1, pz2);\n" + " emit_vertex(2, pz);\n" + " emit_vertex(3, pz2);\n" + " EndPrimitive();\n"; + } else if (polygon_mode == POLY_MODE_LINE) { need_linez = true; layout_out = "layout(line_strip, max_vertices = 5) out;\n"; body = " if ((gl_PrimitiveIDIn & 1) != 0) { return; }\n" @@ -239,23 +236,11 @@ MString *pgraph_glsl_gen_geom(const GeomState *state, GenGeomGlslOptions opts) " pz3[3].x = pzs[3].x;\n" " mat4 pz4 = calc_linez(2, 0);\n" " pz4[3].x = pz[3].x;\n" - " if (gl_PrimitiveIDIn == 0) {\n" - " emit_vertex(0, 3, pz1);\n" - " }\n" - " emit_vertex(1, 3, pz1);\n" - " emit_vertex(3, 3, pz2);\n" - " emit_vertex(2, 3, pz3);\n" - " emit_vertex(0, 3, pz4);\n" - " EndPrimitive();\n"; - } else if (polygon_mode == POLY_MODE_FILL) { - layout_out = "layout(triangle_strip, max_vertices = 4) out;\n"; - body = " if ((gl_PrimitiveIDIn & 1) != 0) { return; }\n" - " mat4 pz, pz2;\n" - " calc_quadz(2, 0, 1, 3, pz, pz2);\n" - " emit_vertex(0, 3, pz);\n" - " emit_vertex(1, 3, pz2);\n" - " emit_vertex(2, 3, pz);\n" - " emit_vertex(3, 3, pz2);\n" + " emit_vertex(0, pz1);\n" + " emit_vertex(1, pz1);\n" + " emit_vertex(3, pz2);\n" + " emit_vertex(2, pz3);\n" + " emit_vertex(0, pz4);\n" " EndPrimitive();\n"; } else { assert(polygon_mode == POLY_MODE_POINT); @@ -263,39 +248,39 @@ MString *pgraph_glsl_gen_geom(const GeomState *state, GenGeomGlslOptions opts) body = " if ((gl_PrimitiveIDIn & 1) != 0) { return; }\n" " mat4 pz, pz2;\n" " calc_quadz(2, 0, 1, 3, pz, pz2);\n" - " if (gl_PrimitiveIDIn == 0) {\n" - " emit_vertex(0, 3, mat4(pz[1], pz[1], pz[1], pz[3]));\n" - " EndPrimitive();\n" - " emit_vertex(1, 3, mat4(pz[2], pz[2], pz[2], pz[3]));\n" - " EndPrimitive();\n" - " }\n" - " emit_vertex(2, 3, mat4(pz[0], pz[0], pz[0], pz[3]));\n" + " emit_vertex(0, mat4(pz[1], pz[1], pz[1], pz[3]));\n" " EndPrimitive();\n" - " emit_vertex(3, 3, mat4(pz2[2], pz2[2], pz2[2], pz2[3]));\n" + " emit_vertex(1, mat4(pz[2], pz[2], pz[2], pz[3]));\n" + " EndPrimitive();\n" + " emit_vertex(2, mat4(pz[0], pz[0], pz[0], pz[3]));\n" + " EndPrimitive();\n" + " emit_vertex(3, mat4(pz2[2], pz2[2], pz2[2], pz2[3]));\n" " EndPrimitive();\n"; } break; case PRIM_TYPE_POLYGON: + provoking_index = "0"; if (polygon_mode == POLY_MODE_FILL) { need_triz = true; layout_in = "layout(triangles) in;\n"; layout_out = "layout(triangle_strip, max_vertices = 3) out;\n"; body = " mat4 pz = calc_triz(0, 1, 2);\n" - " emit_vertex(0, 0, pz);\n" - " emit_vertex(1, 0, pz);\n" - " emit_vertex(2, 0, pz);\n" + " emit_vertex(0, pz);\n" + " emit_vertex(1, pz);\n" + " emit_vertex(2, pz);\n" " EndPrimitive();\n"; } else if (polygon_mode == POLY_MODE_LINE) { need_linez = true; - // FIXME: input here is lines and not triangles so we cannot - // calculate triangle plane slope. Also, the first vertex of the - // polygon is unavailable so flat shading provoking vertex is - // wrong. + /* FIXME: input here is lines and not triangles so we cannot + * calculate triangle plane slope. Also, the first vertex of the + * polygon is unavailable so flat shading provoking vertex is + * wrong. + */ layout_in = "layout(lines) in;\n"; layout_out = "layout(line_strip, max_vertices = 2) out;\n"; body = " mat4 pz = calc_linez(0, 1);\n" - " emit_vertex(0, 0, pz);\n" - " emit_vertex(1, 1, pz);\n" + " emit_vertex(0, pz);\n" + " emit_vertex(1, pz);\n" " EndPrimitive();\n"; } else { assert(false); @@ -326,49 +311,34 @@ MString *pgraph_glsl_gen_geom(const GeomState *state, GenGeomGlslOptions opts) false, false, false); if (state->smooth_shading) { - mstring_append( - output, - "void emit_vertex(int index, int _unused, mat4 pz) {\n" - " gl_Position = gl_in[index].gl_Position;\n" - " gl_PointSize = gl_in[index].gl_PointSize;\n" - " vtxD0 = v_vtxD0[index];\n" - " vtxD1 = v_vtxD1[index];\n" - " vtxB0 = v_vtxB0[index];\n" - " vtxB1 = v_vtxB1[index];\n" - " vtxFog = v_vtxFog[index];\n" - " vtxT0 = v_vtxT0[index];\n" - " vtxT1 = v_vtxT1[index];\n" - " vtxT2 = v_vtxT2[index];\n" - " vtxT3 = v_vtxT3[index];\n" - " vtxPos0 = pz[0];\n" - " vtxPos1 = pz[1];\n" - " vtxPos2 = pz[2];\n" - " triMZ = (isnan(pz[3].x) || isinf(pz[3].x)) ? 0.0 : pz[3].x;\n" - " EmitVertex();\n" - "}\n"); - } else { - mstring_append( - output, - "void emit_vertex(int index, int provoking_index, mat4 pz) {\n" - " gl_Position = gl_in[index].gl_Position;\n" - " gl_PointSize = gl_in[index].gl_PointSize;\n" - " vtxD0 = v_vtxD0[provoking_index];\n" - " vtxD1 = v_vtxD1[provoking_index];\n" - " vtxB0 = v_vtxB0[provoking_index];\n" - " vtxB1 = v_vtxB1[provoking_index];\n" - " vtxFog = v_vtxFog[index];\n" - " vtxT0 = v_vtxT0[index];\n" - " vtxT1 = v_vtxT1[index];\n" - " vtxT2 = v_vtxT2[index];\n" - " vtxT3 = v_vtxT3[index];\n" - " vtxPos0 = pz[0];\n" - " vtxPos1 = pz[1];\n" - " vtxPos2 = pz[2];\n" - " triMZ = (isnan(pz[3].x) || isinf(pz[3].x)) ? 0.0 : pz[3].x;\n" - " EmitVertex();\n" - "}\n"); + provoking_index = "index"; } + mstring_append_fmt( + output, + "void emit_vertex(int index, mat4 pz) {\n" + " gl_Position = gl_in[index].gl_Position;\n" + " gl_PointSize = gl_in[index].gl_PointSize;\n" + " vtxD0 = v_vtxD0[%s];\n" + " vtxD1 = v_vtxD1[%s];\n" + " vtxB0 = v_vtxB0[%s];\n" + " vtxB1 = v_vtxB1[%s];\n" + " vtxFog = v_vtxFog[index];\n" + " vtxT0 = v_vtxT0[index];\n" + " vtxT1 = v_vtxT1[index];\n" + " vtxT2 = v_vtxT2[index];\n" + " vtxT3 = v_vtxT3[index];\n" + " vtxPos0 = pz[0];\n" + " vtxPos1 = pz[1];\n" + " vtxPos2 = pz[2];\n" + " triMZ = (isnan(pz[3].x) || isinf(pz[3].x)) ? 0.0 : pz[3].x;\n" + " EmitVertex();\n" + "}\n", + provoking_index, + provoking_index, + provoking_index, + provoking_index); + if (need_triz || need_quadz) { mstring_append( output, @@ -430,10 +400,12 @@ MString *pgraph_glsl_gen_geom(const GeomState *state, GenGeomGlslOptions opts) if (need_linez) { mstring_append( output, + // Calculate a third vertex by rotating 90 degrees so that triangle + // interpolation in fragment shader can be used as is for lines. "mat4 calc_linez(int i0, int i1) {\n" " vec2 delta = v_vtxPos[i1].xy - v_vtxPos[i0].xy;\n" " vec2 v2 = vec2(-delta.y, delta.x) + v_vtxPos[i0].xy;\n" - " return mat4(v_vtxPos[i0], v_vtxPos[i1], vec4(v2, v_vtxPos[i0].zw), vec4(0.0));\n" + " return mat4(v_vtxPos[i0], v_vtxPos[i1], v2, v_vtxPos[i0].zw, vec4(0.0));\n" "}\n"); } diff --git a/hw/xbox/nv2a/pgraph/glsl/geom.h b/hw/xbox/nv2a/pgraph/glsl/geom.h index 7dbd807345..55229d2e46 100644 --- a/hw/xbox/nv2a/pgraph/glsl/geom.h +++ b/hw/xbox/nv2a/pgraph/glsl/geom.h @@ -30,6 +30,7 @@ typedef struct { enum ShaderPolygonMode polygon_front_mode; enum ShaderPolygonMode polygon_back_mode; bool smooth_shading; + bool first_vertex_is_provoking; bool z_perspective; } GeomState; diff --git a/hw/xbox/nv2a/pgraph/methods.h.inc b/hw/xbox/nv2a/pgraph/methods.h.inc index d475f9b4b1..3f9e026103 100644 --- a/hw/xbox/nv2a/pgraph/methods.h.inc +++ b/hw/xbox/nv2a/pgraph/methods.h.inc @@ -68,6 +68,7 @@ DEF_METHOD(NV097, SET_STENCIL_OP_FAIL) DEF_METHOD(NV097, SET_STENCIL_OP_ZFAIL) DEF_METHOD(NV097, SET_STENCIL_OP_ZPASS) DEF_METHOD(NV097, SET_SHADE_MODE) +DEF_METHOD(NV097, SET_PROVOKING_VERTEX) DEF_METHOD(NV097, SET_POLYGON_OFFSET_SCALE_FACTOR) DEF_METHOD(NV097, SET_POLYGON_OFFSET_BIAS) DEF_METHOD(NV097, SET_FRONT_POLYGON_MODE) diff --git a/hw/xbox/nv2a/pgraph/pgraph.c b/hw/xbox/nv2a/pgraph/pgraph.c index cea424f810..9d55d68724 100644 --- a/hw/xbox/nv2a/pgraph/pgraph.c +++ b/hw/xbox/nv2a/pgraph/pgraph.c @@ -1534,6 +1534,13 @@ DEF_METHOD(NV097, SET_SHADE_MODE) } } +DEF_METHOD(NV097, SET_PROVOKING_VERTEX) +{ + assert((parameter & ~1) == 0); + PG_SET_MASK(NV_PGRAPH_CONTROL_3, NV_PGRAPH_CONTROL_3_PROVOKING_VERTEX, + parameter); +} + DEF_METHOD(NV097, SET_POLYGON_OFFSET_SCALE_FACTOR) { pgraph_reg_w(pg, NV_PGRAPH_ZOFFSETFACTOR, parameter); diff --git a/hw/xbox/nv2a/pgraph/vk/draw.c b/hw/xbox/nv2a/pgraph/vk/draw.c index c982d19bc4..e4bd562149 100644 --- a/hw/xbox/nv2a/pgraph/vk/draw.c +++ b/hw/xbox/nv2a/pgraph/vk/draw.c @@ -791,8 +791,8 @@ static void create_pipeline(PGRAPHState *pg) VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_state; if (r->provoking_vertex_extension_enabled) { - // TODO: remove use of provoking vertex extension since we just want - // the default last vertex convention always. + // Use last provoking vertex convention to match geometry shader + // assumption, because Vulkan default is first vertex convention. VkProvokingVertexModeEXT provoking_mode = VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT;