diff --git a/hw/xbox/nv2a.c b/hw/xbox/nv2a.c index 34109acb21..e80aa64794 100644 --- a/hw/xbox/nv2a.c +++ b/hw/xbox/nv2a.c @@ -1091,21 +1091,6 @@ # define NV097_SET_TRANSFORM_PROGRAM_START 0x00971EA0 # define NV097_SET_TRANSFORM_CONSTANT_LOAD 0x00971EA4 - -static const GLenum kelvin_primitive_map[] = { - 0, - GL_POINTS, - GL_LINES, - GL_LINE_LOOP, - GL_LINE_STRIP, - GL_TRIANGLES, - GL_TRIANGLE_STRIP, - GL_TRIANGLE_FAN, - GL_LINES_ADJACENCY, // GL_QUADS, - // GL_QUAD_STRIP, - // GL_POLYGON, -}; - static const GLenum pgraph_texture_min_filter_map[] = { 0, GL_NEAREST, @@ -1189,12 +1174,6 @@ static const GLenum pgraph_cull_face_map[] = { GL_FRONT_AND_BACK }; -static const GLenum pgraph_polygon_mode_map[] = { - GL_FILL, - GL_POINT, - GL_LINE -}; - static const GLenum pgraph_depth_func_map[] = { GL_NEVER, GL_LESS, @@ -1636,7 +1615,6 @@ typedef struct PGRAPHState { hwaddr dma_vertex_a, dma_vertex_b; unsigned int primitive_mode; - GLenum gl_primitive_mode; bool enable_vertex_program_write; @@ -2969,6 +2947,10 @@ static void pgraph_bind_shaders(PGRAPHState *pg) /* geometry shader stuff */ .primitive_mode = pg->primitive_mode, + .polygon_front_mode = GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_FRONTFACEMODE), + .polygon_back_mode = GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_BACKFACEMODE), }; state.program_length = 0; @@ -5155,6 +5137,8 @@ static void pgraph_method(NV2AState *d, if (parameter == NV097_SET_BEGIN_END_OP_END) { + assert(pg->shader_binding); + if (pg->draw_arrays_length) { NV2A_GL_DPRINTF(false, "Draw Arrays"); @@ -5165,7 +5149,7 @@ static void pgraph_method(NV2AState *d, pgraph_bind_vertex_attributes(d, pg->draw_arrays_max_count, false, 0); - glMultiDrawArrays(pg->gl_primitive_mode, + glMultiDrawArrays(pg->shader_binding->gl_primitive_mode, pg->gl_draw_arrays_start, pg->gl_draw_arrays_count, pg->draw_arrays_length); @@ -5204,7 +5188,7 @@ static void pgraph_method(NV2AState *d, } - glDrawArrays(pg->gl_primitive_mode, + glDrawArrays(pg->shader_binding->gl_primitive_mode, 0, pg->inline_buffer_length); } else if (pg->inline_array_length) { @@ -5215,7 +5199,8 @@ static void pgraph_method(NV2AState *d, assert(pg->inline_elements_length == 0); unsigned int index_count = pgraph_bind_inline_array(d); - glDrawArrays(pg->gl_primitive_mode, 0, index_count); + glDrawArrays(pg->shader_binding->gl_primitive_mode, + 0, index_count); } else if (pg->inline_elements_length) { NV2A_GL_DPRINTF(false, "Inline Elements"); @@ -5239,7 +5224,7 @@ static void pgraph_method(NV2AState *d, pg->inline_elements, GL_DYNAMIC_DRAW); - glDrawRangeElements(pg->gl_primitive_mode, + glDrawRangeElements(pg->shader_binding->gl_primitive_mode, min_element, max_element, pg->inline_elements_length, GL_UNSIGNED_INT, @@ -5262,9 +5247,7 @@ static void pgraph_method(NV2AState *d, pgraph_update_surface(d, true, true, depth_test || stencil_test); - assert(parameter < ARRAYSIZE(kelvin_primitive_map)); pg->primitive_mode = parameter; - pg->gl_primitive_mode = kelvin_primitive_map[parameter]; uint32_t control_0 = pg->regs[NV_PGRAPH_CONTROL_0]; @@ -5319,17 +5302,6 @@ static void pgraph_method(NV2AState *d, & NV_PGRAPH_SETUPRASTER_FRONTFACE ? GL_CCW : GL_CW); - /* Polygon mode */ - uint32_t front_mode = GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_FRONTFACEMODE); - uint32_t back_mode = GET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_BACKFACEMODE); - /* FIXME: GL3+ only supports GL_FRONT_AND_BACK, how to handle? */ - assert(front_mode == back_mode); - assert(front_mode < ARRAYSIZE(pgraph_polygon_mode_map)); - glPolygonMode(GL_FRONT_AND_BACK, - pgraph_polygon_mode_map[front_mode]); - /* Polygon offset */ /* FIXME: GL implementation-specific, maybe do this in VS? */ if (pg->regs[NV_PGRAPH_SETUPRASTER] & diff --git a/hw/xbox/nv2a_shaders.c b/hw/xbox/nv2a_shaders.c index 082d883c9d..87647794fb 100644 --- a/hw/xbox/nv2a_shaders.c +++ b/hw/xbox/nv2a_shaders.c @@ -23,50 +23,166 @@ #include "hw/xbox/nv2a_shaders_common.h" #include "hw/xbox/nv2a_shaders.h" -static void generate_geometry_shader_pass_vertex(QString* s, const char* v) +static QString* generate_geometry_shader( + enum ShaderPolygonMode polygon_front_mode, + enum ShaderPolygonMode polygon_back_mode, + enum ShaderPrimitiveMode primitive_mode, + GLenum *gl_primitive_mode) { - qstring_append_fmt(s, " gl_Position = gl_in[%s].gl_Position;\n", v); - qstring_append_fmt(s, " gl_PointSize = gl_in[%s].gl_PointSize;\n", v); - qstring_append_fmt(s, " g_vtx = v_vtx[%s];\n", v); - qstring_append(s, " EmitVertex();\n"); -} -static QString* generate_geometry_shader(enum ShaderPrimitiveMode primitive_mode) -{ + /* FIXME: Missing support for 2-sided-poly mode */ + assert(polygon_front_mode == polygon_back_mode); + enum ShaderPolygonMode polygon_mode = polygon_front_mode; + + /* POINT mode shouldn't require any special work */ + if (polygon_mode == POLY_MODE_POINT) { + *gl_primitive_mode = GL_POINTS; + return NULL; + } + + /* Handle LINE and FILL mode */ + const char *layout_in = NULL; + const char *layout_out = NULL; + const char *body = NULL; + switch (primitive_mode) { + case PRIM_TYPE_POINTS: *gl_primitive_mode = GL_POINTS; return NULL; + case PRIM_TYPE_LINES: *gl_primitive_mode = GL_LINES; return NULL; + case PRIM_TYPE_LINE_LOOP: *gl_primitive_mode = GL_LINE_LOOP; return NULL; + case PRIM_TYPE_LINE_STRIP: *gl_primitive_mode = GL_LINE_STRIP; return NULL; + case PRIM_TYPE_TRIANGLES: + *gl_primitive_mode = GL_TRIANGLES; + if (polygon_mode == POLY_MODE_FILL) { return NULL; } + assert(polygon_mode == POLY_MODE_LINE); + layout_in = "layout(triangles) in;\n"; + layout_out = "layout(line_strip, max_vertices = 4) out;\n"; + body = " emit_vertex(0);\n" + " emit_vertex(1);\n" + " emit_vertex(2);\n" + " emit_vertex(0);\n" + " EndPrimitive();\n"; + break; + case PRIM_TYPE_TRIANGLE_STRIP: + *gl_primitive_mode = GL_TRIANGLE_STRIP; + if (polygon_mode == POLY_MODE_FILL) { return NULL; } + assert(polygon_mode == POLY_MODE_LINE); + layout_in = "layout(triangles) in;\n"; + layout_out = "layout(line_strip, max_vertices = 4) out;\n"; + /* Imagine a quad made of a tristrip, the comments tell you which + * vertex we are using */ + body = " if ((gl_PrimitiveIDIn & 1) == 0) {\n" + " if (gl_PrimitiveIDIn == 0) {\n" + " emit_vertex(0);\n" /* bottom right */ + " }\n" + " emit_vertex(1);\n" /* top right */ + " emit_vertex(2);\n" /* bottom left */ + " emit_vertex(0);\n" /* bottom right */ + " } else {\n" + " emit_vertex(2);\n" /* bottom left */ + " emit_vertex(1);\n" /* top left */ + " emit_vertex(0);\n" /* top right */ + " }\n" + " EndPrimitive();\n"; + break; + case PRIM_TYPE_TRIANGLE_FAN: + *gl_primitive_mode = GL_TRIANGLE_FAN; + if (polygon_mode == POLY_MODE_FILL) { return NULL; } + assert(polygon_mode == POLY_MODE_LINE); + layout_in = "layout(triangles) in;\n"; + layout_out = "layout(line_strip, max_vertices = 4) out;\n"; + body = " if (gl_PrimitiveIDIn == 0) {\n" + " emit_vertex(0);\n" + " }\n" + " emit_vertex(1);\n" + " emit_vertex(2);\n" + " emit_vertex(0);\n" + " EndPrimitive();\n"; + break; + case PRIM_TYPE_QUADS: + *gl_primitive_mode = GL_LINES_ADJACENCY; + layout_in = "layout(lines_adjacency) in;\n"; + if (polygon_mode == POLY_MODE_LINE) { + layout_out = "layout(line_strip, max_vertices = 5) out;\n"; + body = " emit_vertex(0);\n" + " emit_vertex(1);\n" + " emit_vertex(2);\n" + " emit_vertex(3);\n" + " emit_vertex(0);\n" + " EndPrimitive();\n"; + } else if (polygon_mode == POLY_MODE_FILL) { + layout_out = "layout(triangle_strip, max_vertices = 4) out;\n"; + body = " emit_vertex(0);\n" + " emit_vertex(1);\n" + " emit_vertex(3);\n" + " emit_vertex(2);\n" + " EndPrimitive();\n"; + } else { + assert(false); + return NULL; + } + break; + case PRIM_TYPE_QUAD_STRIP: + *gl_primitive_mode = GL_LINE_STRIP_ADJACENCY; + layout_in = "layout(lines_adjacency) in;\n"; + if (polygon_mode == POLY_MODE_LINE) { + layout_out = "layout(line_strip, max_vertices = 5) out;\n"; + body = " if ((gl_PrimitiveIDIn & 1) != 0) { return; }\n" + " if (gl_PrimitiveIDIn == 0) {\n" + " emit_vertex(0);\n" + " }\n" + " emit_vertex(1);\n" + " emit_vertex(3);\n" + " emit_vertex(2);\n" + " emit_vertex(0);\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" + " emit_vertex(0);\n" + " emit_vertex(1);\n" + " emit_vertex(2);\n" + " emit_vertex(3);\n" + " EndPrimitive();\n"; + } else { + assert(false); + return NULL; + } + break; + case PRIM_TYPE_POLYGON: + if (polygon_mode == POLY_MODE_LINE) { + *gl_primitive_mode = GL_LINE_LOOP; + } else if (polygon_mode == POLY_MODE_FILL) { + *gl_primitive_mode = GL_TRIANGLE_FAN; + } else { + assert(false); + } + return NULL; + default: + assert(false); + return NULL; + } + /* generate a geometry shader to support deprecated primitive types */ - QString* s = qstring_new(); - qstring_append(s, "#version 330\n"); - qstring_append(s, "\n"); - switch (primitive_mode) { - case PRIM_TYPE_QUADS: - qstring_append(s, "layout(lines_adjacency) in;\n"); - qstring_append(s, "layout(triangle_strip, max_vertices = 4) out;\n"); - break; - default: - assert(false); - break; - } - qstring_append(s, "\n"); - qstring_append(s, STRUCT_VERTEX_DATA); - qstring_append(s, - "noperspective in VertexData v_vtx[];\n"); - qstring_append(s, - "noperspective out VertexData g_vtx;\n"); - qstring_append(s, "\n"); - - qstring_append(s, "void main() {\n"); - switch (primitive_mode) { - case PRIM_TYPE_QUADS: - generate_geometry_shader_pass_vertex(s, "0"); - generate_geometry_shader_pass_vertex(s, "1"); - generate_geometry_shader_pass_vertex(s, "3"); - generate_geometry_shader_pass_vertex(s, "2"); - qstring_append(s, "EndPrimitive();\n"); - break; - default: - assert(false); - break; - } + assert(layout_in); + assert(layout_out); + assert(body); + QString* s = qstring_from_str("#version 330\n" + "\n"); + qstring_append(s, layout_in); + qstring_append(s, layout_out); + qstring_append(s, "\n" + STRUCT_VERTEX_DATA + "noperspective in VertexData v_vtx[];\n" + "noperspective out VertexData g_vtx;\n" + "\n" + "void emit_vertex(int index) {\n" + " gl_Position = gl_in[index].gl_Position;\n" + " gl_PointSize = gl_in[index].gl_PointSize;\n" + " g_vtx = v_vtx[index];\n" + " EmitVertex();\n" + "}\n" + "\n" + "void main() {\n"); + qstring_append(s, body); qstring_append(s, "}\n"); return s; @@ -592,12 +708,33 @@ static GLuint create_gl_shader(GLenum gl_shader_type, ShaderBinding* generate_shaders(const ShaderState state) { int i, j; - - bool with_geom = state.primitive_mode == PRIM_TYPE_QUADS; - char vtx_prefix = with_geom ? 'v' : 'g'; - + char vtx_prefix; GLuint program = glCreateProgram(); + /* Create an option geometry shader and find primitive type */ + + GLenum gl_primitive_mode; + QString* geometry_shader_code = + generate_geometry_shader(state.polygon_front_mode, + state.polygon_back_mode, + state.primitive_mode, + &gl_primitive_mode); + if (geometry_shader_code) { + const char* geometry_shader_code_str = + qstring_get_str(geometry_shader_code); + + GLuint geometry_shader = create_gl_shader(GL_GEOMETRY_SHADER, + geometry_shader_code_str, + "geometry shader"); + glAttachShader(program, geometry_shader); + + QDECREF(geometry_shader_code); + + vtx_prefix = 'v'; + } else { + vtx_prefix = 'g'; + } + /* create the vertex shader */ QString *s = NULL; @@ -605,8 +742,7 @@ ShaderBinding* generate_shaders(const ShaderState state) s = generate_fixed_function(state, vtx_prefix); } else if (state.vertex_program) { - s = vsh_translate(VSH_VERSION_XVS, - (uint32_t*)state.program_data, + s = vsh_translate(VSH_VERSION_XVS, (uint32_t*)state.program_data, state.program_length, vtx_prefix); } else { @@ -658,21 +794,6 @@ ShaderBinding* generate_shaders(const ShaderState state) QDECREF(fragment_shader_code); - if (with_geom) { - QString* geometry_shader_code = - generate_geometry_shader(state.primitive_mode); - const char* geometry_shader_code_str = - qstring_get_str(geometry_shader_code); - - GLuint geometry_shader = create_gl_shader(GL_GEOMETRY_SHADER, - geometry_shader_code_str, - "geometry shader"); - glAttachShader(program, geometry_shader); - - QDECREF(geometry_shader_code); - } - - /* link the program */ glLinkProgram(program); GLint linked = 0; @@ -709,6 +830,7 @@ ShaderBinding* generate_shaders(const ShaderState state) ShaderBinding* ret = g_malloc0(sizeof(ShaderBinding)); ret->gl_program = program; + ret->gl_primitive_mode = gl_primitive_mode; /* lookup fragment shader locations */ for (i=0; i<=8; i++) { diff --git a/hw/xbox/nv2a_shaders.h b/hw/xbox/nv2a_shaders.h index b3c7c72a9e..57a99426d1 100644 --- a/hw/xbox/nv2a_shaders.h +++ b/hw/xbox/nv2a_shaders.h @@ -45,6 +45,12 @@ enum ShaderPrimitiveMode { PRIM_TYPE_POLYGON, }; +enum ShaderPolygonMode { + POLY_MODE_FILL, + POLY_MODE_POINT, + POLY_MODE_LINE, +}; + typedef struct ShaderState { /* fragment shader - register combiner stuff */ uint32_t combiner_control; @@ -85,11 +91,14 @@ typedef struct ShaderState { int program_length; /* primitive format for geometry shader */ + enum ShaderPolygonMode polygon_front_mode; + enum ShaderPolygonMode polygon_back_mode; enum ShaderPrimitiveMode primitive_mode; } ShaderState; typedef struct ShaderBinding { GLuint gl_program; + GLenum gl_primitive_mode; GLint psh_constant_loc[9][2]; GLint gl_constants_loc; } ShaderBinding;