From 83e16c996d6f007bbeb4c5531fb6b0a81d31df05 Mon Sep 17 00:00:00 2001
From: Matt Borgerson <contact@mborgerson.com>
Date: Sat, 25 Jun 2022 20:12:50 -0700
Subject: [PATCH] nv2a: Store shading mode in CONTROL_3

---
 hw/xbox/nv2a/nv2a_int.h       |  2 -
 hw/xbox/nv2a/nv2a_regs.h      |  9 ++--
 hw/xbox/nv2a/pgraph.c         | 31 +++++++++----
 hw/xbox/nv2a/pgraph_methods.h |  2 +-
 hw/xbox/nv2a/psh.c            |  5 ++-
 hw/xbox/nv2a/psh.h            |  2 +-
 hw/xbox/nv2a/shaders.c        | 84 ++++++++++++++++++-----------------
 hw/xbox/nv2a/shaders.h        |  2 +-
 8 files changed, 78 insertions(+), 59 deletions(-)

diff --git a/hw/xbox/nv2a/nv2a_int.h b/hw/xbox/nv2a/nv2a_int.h
index 6529435e32..17c4730198 100644
--- a/hw/xbox/nv2a/nv2a_int.h
+++ b/hw/xbox/nv2a/nv2a_int.h
@@ -348,8 +348,6 @@ typedef struct PGRAPHState {
     bool ltc1_dirty[NV2A_LTC1_COUNT];
 
     float material_alpha;
-    // FIXME: Find the correct register for this.
-    uint32_t shade_model;
 
     // should figure out where these are in lighting context
     float light_infinite_half_vector[NV2A_MAX_LIGHTS][3];
diff --git a/hw/xbox/nv2a/nv2a_regs.h b/hw/xbox/nv2a/nv2a_regs.h
index 4aa5c129bf..225f075fcb 100644
--- a/hw/xbox/nv2a/nv2a_regs.h
+++ b/hw/xbox/nv2a/nv2a_regs.h
@@ -448,6 +448,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_SHADEMODE                        (1 << 7)
+#       define NV_PGRAPH_CONTROL_3_SHADEMODE_FLAT                   0
+#       define NV_PGRAPH_CONTROL_3_SHADEMODE_SMOOTH                 1
 #   define NV_PGRAPH_CONTROL_3_FOGENABLE                        (1 << 8)
 #   define NV_PGRAPH_CONTROL_3_FOG_MODE                         0x00070000
 #       define NV_PGRAPH_CONTROL_3_FOG_MODE_LINEAR                  0
@@ -974,9 +977,9 @@
 #       define NV097_SET_STENCIL_OP_V_INVERT                      0x150A
 #       define NV097_SET_STENCIL_OP_V_INCR                        0x8507
 #       define NV097_SET_STENCIL_OP_V_DECR                        0x8508
-#   define NV097_SET_SHADE_MODEL                              0x0000037C
-#       define NV097_SET_SHADE_MODEL_FLAT                         0x1D00
-#       define NV097_SET_SHADE_MODEL_SMOOTH                       0x1D01
+#   define NV097_SET_SHADE_MODE                               0x0000037C
+#       define NV097_SET_SHADE_MODE_V_FLAT                        0x1D00
+#       define NV097_SET_SHADE_MODE_V_SMOOTH                      0x1D01
 #   define NV097_SET_POLYGON_OFFSET_SCALE_FACTOR              0x00000384
 #   define NV097_SET_POLYGON_OFFSET_BIAS                      0x00000388
 #   define NV097_SET_FRONT_POLYGON_MODE                       0x0000038C
diff --git a/hw/xbox/nv2a/pgraph.c b/hw/xbox/nv2a/pgraph.c
index aaffa48683..657aba7027 100644
--- a/hw/xbox/nv2a/pgraph.c
+++ b/hw/xbox/nv2a/pgraph.c
@@ -1840,10 +1840,21 @@ DEF_METHOD(NV097, SET_STENCIL_OP_ZPASS)
              kelvin_map_stencil_op(parameter));
 }
 
-DEF_METHOD(NV097, SET_SHADE_MODEL)
+DEF_METHOD(NV097, SET_SHADE_MODE)
 {
-    // FIXME: Find the correct register for this.
-    pg->shade_model = parameter;
+    switch (parameter) {
+    case NV097_SET_SHADE_MODE_V_FLAT:
+        SET_MASK(pg->regs[NV_PGRAPH_CONTROL_3], NV_PGRAPH_CONTROL_3_SHADEMODE,
+                 NV_PGRAPH_CONTROL_3_SHADEMODE_FLAT);
+        break;
+    case NV097_SET_SHADE_MODE_V_SMOOTH:
+        SET_MASK(pg->regs[NV_PGRAPH_CONTROL_3], NV_PGRAPH_CONTROL_3_SHADEMODE,
+                 NV_PGRAPH_CONTROL_3_SHADEMODE_SMOOTH);
+        break;
+    default:
+        /* Discard */
+        break;
+    }
 }
 
 DEF_METHOD(NV097, SET_POLYGON_OFFSET_SCALE_FACTOR)
@@ -2979,7 +2990,9 @@ DEF_METHOD(NV097, SET_BEGIN_END)
             glDisable(GL_DEPTH_CLAMP);
         }
 
-        if (pg->shade_model == NV097_SET_SHADE_MODEL_FLAT) {
+        if (GET_MASK(pg->regs[NV_PGRAPH_CONTROL_3],
+                     NV_PGRAPH_CONTROL_3_SHADEMODE) ==
+            NV_PGRAPH_CONTROL_3_SHADEMODE_FLAT) {
             glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
         }
 
@@ -4006,7 +4019,8 @@ void pgraph_init(NV2AState *d)
     pg->shader_cache = g_hash_table_new(shader_hash, shader_equal);
 
     pg->material_alpha = 0.0f;
-    pg->shade_model = NV097_SET_SHADE_MODEL_SMOOTH;
+    SET_MASK(pg->regs[NV_PGRAPH_CONTROL_3], NV_PGRAPH_CONTROL_3_SHADEMODE,
+         NV_PGRAPH_CONTROL_3_SHADEMODE_SMOOTH);
     pg->primitive_mode = PRIM_TYPE_INVALID;
 
     for (i=0; i<NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
@@ -4331,7 +4345,6 @@ static bool pgraph_bind_shaders_test_dirty(PGRAPHState *pg)
         CR_8(NV_PGRAPH_WINDOWCLIPX0) \
         CR_8(NV_PGRAPH_WINDOWCLIPY0) \
         CF(pg->primitive_mode, primitive_mode) \
-        CF(pg->shade_model, shade_model) \
         CF(pg->surface_scale_factor, surface_scale_factor) \
         CF(pg->compressed_attrs, compressed_attrs) \
         CFA(pg->texture_matrix_enable, texture_matrix_enable)
@@ -4453,8 +4466,10 @@ static void pgraph_bind_shaders(PGRAPHState *pg)
     state.polygon_back_mode = (enum ShaderPolygonMode)GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER],
                                                           NV_PGRAPH_SETUPRASTER_BACKFACEMODE);
 
-    state.shade_model_flat = pg->shade_model == NV097_SET_SHADE_MODEL_FLAT;
-    state.psh.shade_model_flat = pg->shade_model == NV097_SET_SHADE_MODEL_FLAT;
+    state.smooth_shading = GET_MASK(pg->regs[NV_PGRAPH_CONTROL_3],
+                                      NV_PGRAPH_CONTROL_3_SHADEMODE) ==
+                             NV_PGRAPH_CONTROL_3_SHADEMODE_SMOOTH;
+    state.psh.smooth_shading = state.smooth_shading;
 
     state.program_length = 0;
 
diff --git a/hw/xbox/nv2a/pgraph_methods.h b/hw/xbox/nv2a/pgraph_methods.h
index 8b94b43744..b3de47959d 100644
--- a/hw/xbox/nv2a/pgraph_methods.h
+++ b/hw/xbox/nv2a/pgraph_methods.h
@@ -66,7 +66,7 @@ DEF_METHOD(NV097, SET_STENCIL_FUNC_MASK)
 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_MODEL)
+DEF_METHOD(NV097, SET_SHADE_MODE)
 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/psh.c b/hw/xbox/nv2a/psh.c
index b041cfba55..bf3d4bc79e 100644
--- a/hw/xbox/nv2a/psh.c
+++ b/hw/xbox/nv2a/psh.c
@@ -690,8 +690,9 @@ static MString* psh_convert(struct PixelShader *ps)
     int i;
 
     MString *preflight = mstring_new();
-    mstring_append(preflight,
-                   ps->state.shade_model_flat ? STRUCT_VERTEX_DATA_IN_FLAT : STRUCT_VERTEX_DATA_IN_SMOOTH);
+    mstring_append(preflight, ps->state.smooth_shading ?
+                                  STRUCT_VERTEX_DATA_IN_SMOOTH :
+                                  STRUCT_VERTEX_DATA_IN_FLAT);
     mstring_append(preflight, "\n");
     mstring_append(preflight, "out vec4 fragColor;\n");
     mstring_append(preflight, "\n");
diff --git a/hw/xbox/nv2a/psh.h b/hw/xbox/nv2a/psh.h
index a80d3ea7c1..449997a0c6 100644
--- a/hw/xbox/nv2a/psh.h
+++ b/hw/xbox/nv2a/psh.h
@@ -79,7 +79,7 @@ typedef struct PshState {
 
     bool window_clip_exclusive;
 
-    bool shade_model_flat;
+    bool smooth_shading;
 } PshState;
 
 MString *psh_translate(const PshState state);
diff --git a/hw/xbox/nv2a/shaders.c b/hw/xbox/nv2a/shaders.c
index 7d837b5645..7e4af1405b 100644
--- a/hw/xbox/nv2a/shaders.c
+++ b/hw/xbox/nv2a/shaders.c
@@ -76,7 +76,7 @@ static MString* generate_geometry_shader(
                                       enum ShaderPolygonMode polygon_back_mode,
                                       enum ShaderPrimitiveMode primitive_mode,
                                       GLenum *gl_primitive_mode,
-                                      bool shade_model_flat)
+                                      bool smooth_shading)
 {
 
     /* FIXME: Missing support for 2-sided-poly mode */
@@ -203,7 +203,7 @@ static MString* generate_geometry_shader(
         }
         if (polygon_mode == POLY_MODE_FILL) {
             *gl_primitive_mode = GL_TRIANGLE_FAN;
-            if (!shade_model_flat) {
+            if (smooth_shading) {
                 return NULL;
             }
             layout_in = "layout(triangles) in;\n";
@@ -232,28 +232,7 @@ static MString* generate_geometry_shader(
     mstring_append(s, layout_in);
     mstring_append(s, layout_out);
     mstring_append(s, "\n");
-    if (shade_model_flat) {
-        mstring_append(s,
-                       STRUCT_V_VERTEX_DATA_IN_ARRAY_FLAT
-                       "\n"
-                       STRUCT_VERTEX_DATA_OUT_FLAT
-                       "\n"
-                       "void emit_vertex(int index, int provoking_index) {\n"
-                       "  gl_Position = gl_in[index].gl_Position;\n"
-                       "  gl_PointSize = gl_in[index].gl_PointSize;\n"
-                       "  vtx_inv_w = v_vtx_inv_w[index];\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"
-                       "  EmitVertex();\n"
-                       "}\n");
-    } else {
+    if (smooth_shading) {
         mstring_append(s,
                        STRUCT_V_VERTEX_DATA_IN_ARRAY_SMOOTH
                        "\n"
@@ -274,6 +253,27 @@ static MString* generate_geometry_shader(
                        "  vtxT3 = v_vtxT3[index];\n"
                        "  EmitVertex();\n"
                        "}\n");
+    } else {
+        mstring_append(s,
+                       STRUCT_V_VERTEX_DATA_IN_ARRAY_FLAT
+                       "\n"
+                       STRUCT_VERTEX_DATA_OUT_FLAT
+                       "\n"
+                       "void emit_vertex(int index, int provoking_index) {\n"
+                       "  gl_Position = gl_in[index].gl_Position;\n"
+                       "  gl_PointSize = gl_in[index].gl_PointSize;\n"
+                       "  vtx_inv_w = v_vtx_inv_w[index];\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"
+                       "  EmitVertex();\n"
+                       "}\n");
     }
 
     mstring_append(s, "\n"
@@ -807,11 +807,9 @@ GLSL_DEFINE(texMat3, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_T3MAT))
 "    return vec4(x, y, z, 1);\n"
 "}\n");
     if (prefix_outputs) {
-        if (state->shade_model_flat) {
-            mstring_append(header, STRUCT_V_VERTEX_DATA_OUT_FLAT);
-        } else {
-            mstring_append(header, STRUCT_V_VERTEX_DATA_OUT_SMOOTH);
-        }
+        mstring_append(header, state->smooth_shading ?
+                                   STRUCT_V_VERTEX_DATA_OUT_SMOOTH :
+                                   STRUCT_V_VERTEX_DATA_OUT_FLAT);
         mstring_append(header,
                        "#define vtx_inv_w v_vtx_inv_w\n"
                        "#define vtxD0 v_vtxD0\n"
@@ -825,11 +823,9 @@ GLSL_DEFINE(texMat3, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_T3MAT))
                        "#define vtxT3 v_vtxT3\n"
                        );
     } else {
-        if (state->shade_model_flat) {
-            mstring_append(header, STRUCT_VERTEX_DATA_OUT_FLAT);
-        } else {
-            mstring_append(header, STRUCT_VERTEX_DATA_OUT_SMOOTH);
-        }
+        mstring_append(header, state->smooth_shading ?
+                                   STRUCT_VERTEX_DATA_OUT_SMOOTH :
+                                   STRUCT_VERTEX_DATA_OUT_FLAT);
     }
     mstring_append(header, "\n");
     for (i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
@@ -1030,7 +1026,7 @@ ShaderBinding *generate_shaders(const ShaderState *state)
                                  state->polygon_back_mode,
                                  state->primitive_mode,
                                  &gl_primitive_mode,
-                                 state->shade_model_flat);
+                                 state->smooth_shading);
     if (geometry_shader_code) {
         const char* geometry_shader_code_str =
              mstring_get_str(geometry_shader_code);
@@ -1042,7 +1038,8 @@ ShaderBinding *generate_shaders(const ShaderState *state)
     }
 
     /* create the vertex shader */
-    MString *vertex_shader_code = generate_vertex_shader(state, geometry_shader_code);
+    MString *vertex_shader_code =
+        generate_vertex_shader(state, geometry_shader_code != NULL);
     GLuint vertex_shader = create_gl_shader(GL_VERTEX_SHADER,
                                             mstring_get_str(vertex_shader_code),
                                             "vertex shader");
@@ -1051,7 +1048,8 @@ ShaderBinding *generate_shaders(const ShaderState *state)
 
     /* generate a fragment shader from register combiners */
     MString *fragment_shader_code = psh_translate(state->psh);
-    const char *fragment_shader_code_str = mstring_get_str(fragment_shader_code);
+    const char *fragment_shader_code_str =
+        mstring_get_str(fragment_shader_code);
     GLuint fragment_shader = create_gl_shader(GL_FRAGMENT_SHADER,
                                               fragment_shader_code_str,
                                               "fragment shader");
@@ -1144,14 +1142,17 @@ ShaderBinding *generate_shaders(const ShaderState *state)
     }
     for (i = 0; i < NV2A_MAX_LIGHTS; i++) {
         snprintf(tmp, sizeof(tmp), "lightInfiniteHalfVector%d", i);
-        ret->light_infinite_half_vector_loc[i] = glGetUniformLocation(program, tmp);
+        ret->light_infinite_half_vector_loc[i] =
+            glGetUniformLocation(program, tmp);
         snprintf(tmp, sizeof(tmp), "lightInfiniteDirection%d", i);
-        ret->light_infinite_direction_loc[i] = glGetUniformLocation(program, tmp);
+        ret->light_infinite_direction_loc[i] =
+            glGetUniformLocation(program, tmp);
 
         snprintf(tmp, sizeof(tmp), "lightLocalPosition%d", i);
         ret->light_local_position_loc[i] = glGetUniformLocation(program, tmp);
         snprintf(tmp, sizeof(tmp), "lightLocalAttenuation%d", i);
-        ret->light_local_attenuation_loc[i] = glGetUniformLocation(program, tmp);
+        ret->light_local_attenuation_loc[i] =
+            glGetUniformLocation(program, tmp);
     }
     for (i = 0; i < 8; i++) {
         snprintf(tmp, sizeof(tmp), "clipRegion[%d]", i);
@@ -1159,7 +1160,8 @@ ShaderBinding *generate_shaders(const ShaderState *state)
     }
 
     if (state->fixed_function) {
-        ret->material_alpha_loc = glGetUniformLocation(program, "material_alpha");
+        ret->material_alpha_loc =
+            glGetUniformLocation(program, "material_alpha");
     } else {
         ret->material_alpha_loc = -1;
     }
diff --git a/hw/xbox/nv2a/shaders.h b/hw/xbox/nv2a/shaders.h
index 01ef47258f..a1543eb69c 100644
--- a/hw/xbox/nv2a/shaders.h
+++ b/hw/xbox/nv2a/shaders.h
@@ -98,7 +98,7 @@ typedef struct ShaderState {
     float point_size;
     float point_params[8];
 
-    bool shade_model_flat;
+    bool smooth_shading;
 } ShaderState;
 
 typedef struct ShaderBinding {