Merge pull request #60 from JayFoxRox/prim-modes-line

Implement polygon modes in geometry shader
This commit is contained in:
Jannik Vogel 2015-09-06 21:03:18 +02:00
commit f68e558e4a
3 changed files with 204 additions and 101 deletions

View File

@ -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] &

View File

@ -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++) {

View File

@ -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;