From 27cf9cd4da33df7f4216788c13bf780ade08493c Mon Sep 17 00:00:00 2001 From: espes Date: Mon, 10 Aug 2015 09:21:45 -0700 Subject: [PATCH] split out shader generation from nv2a.c --- hw/xbox/nv2a.c | 565 +---------------------------------------- hw/xbox/nv2a_shaders.c | 523 ++++++++++++++++++++++++++++++++++++++ hw/xbox/nv2a_shaders.h | 104 ++++++++ 3 files changed, 641 insertions(+), 551 deletions(-) create mode 100644 hw/xbox/nv2a_shaders.c create mode 100644 hw/xbox/nv2a_shaders.h diff --git a/hw/xbox/nv2a.c b/hw/xbox/nv2a.c index 5f3195e3fd..40166856c1 100644 --- a/hw/xbox/nv2a.c +++ b/hw/xbox/nv2a.c @@ -2,6 +2,7 @@ * QEMU Geforce NV2A implementation * * Copyright (c) 2012 espes + * Copyright (c) 2015 JayFoxRox * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -32,8 +33,7 @@ #include "hw/xbox/g-lru-cache.h" #include "hw/xbox/swizzle.h" #include "hw/xbox/u_format_r11g11b10f.h" -#include "hw/xbox/nv2a_vsh.h" -#include "hw/xbox/nv2a_psh.h" +#include "hw/xbox/nv2a_shaders.h" #include "hw/xbox/nv2a.h" @@ -1269,31 +1269,11 @@ static const SurfaceColorFormatInfo kelvin_surface_color_format_map[] = { {4, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, }; -#define NV2A_VERTEX_ATTR_POSITION 0 -#define NV2A_VERTEX_ATTR_WEIGHT 1 -#define NV2A_VERTEX_ATTR_NORMAL 2 -#define NV2A_VERTEX_ATTR_DIFFUSE 3 -#define NV2A_VERTEX_ATTR_SPECULAR 4 -#define NV2A_VERTEX_ATTR_FOG 5 -#define NV2A_VERTEX_ATTR_POINT_SIZE 6 -#define NV2A_VERTEX_ATTR_BACK_DIFFUSE 7 -#define NV2A_VERTEX_ATTR_BACK_SPECULAR 8 -#define NV2A_VERTEX_ATTR_TEXTURE0 9 -#define NV2A_VERTEX_ATTR_TEXTURE1 10 -#define NV2A_VERTEX_ATTR_TEXTURE2 11 -#define NV2A_VERTEX_ATTR_TEXTURE3 12 -#define NV2A_VERTEX_ATTR_RESERVED1 13 -#define NV2A_VERTEX_ATTR_RESERVED2 14 -#define NV2A_VERTEX_ATTR_RESERVED3 15 - - #define NV2A_CRYSTAL_FREQ 13500000 #define NV2A_NUM_CHANNELS 32 #define NV2A_NUM_SUBCHANNELS 8 #define NV2A_MAX_BATCH_LENGTH 0xFFFF -#define NV2A_MAX_TRANSFORM_PROGRAM_LENGTH 136 -#define NV2A_VERTEXSHADER_CONSTANTS 192 #define NV2A_VERTEXSHADER_ATTRIBUTES 16 #define NV2A_MAX_TEXTURES 4 @@ -1379,48 +1359,6 @@ typedef struct VertexShaderConstant { uint32_t data[4]; } VertexShaderConstant; -typedef struct ShaderState { - /* fragment shader - register combiner stuff */ - uint32_t combiner_control; - uint32_t shader_stage_program; - uint32_t other_stage_input; - uint32_t final_inputs_0; - uint32_t final_inputs_1; - - uint32_t rgb_inputs[8], rgb_outputs[8]; - uint32_t alpha_inputs[8], alpha_outputs[8]; - - bool rect_tex[4]; - bool compare_mode[4][4]; - bool alphakill[4]; - - bool alpha_test; - enum PshAlphaFunc alpha_func; - - bool texture_matrix_enable[4]; - enum VshTexgen texgen[4][4]; - - enum VshSkinning skinning; - - bool normalization; - - bool fixed_function; - - /* vertex program */ - bool vertex_program; - uint32_t program_data[NV2A_MAX_TRANSFORM_PROGRAM_LENGTH][VSH_TOKEN_SIZE]; - int program_length; - - /* primitive format for geomotry shader */ - unsigned int primitive_mode; -} ShaderState; - -typedef struct ShaderBinding { - GLuint gl_program; - GLint psh_constant_loc[9][2]; - GLint vsh_constant_loc[NV2A_VERTEXSHADER_CONSTANTS]; -} ShaderBinding; - typedef struct Surface { bool draw_dirty; bool buffer_dirty; @@ -2708,492 +2646,6 @@ static void pgraph_bind_textures(NV2AState *d) NV2A_GL_DGROUP_END(); } -/* hash and equality for shader cache hash table */ -static guint shader_hash(gconstpointer key) -{ - return fnv_hash(key, sizeof(ShaderState)); -} -static gboolean shader_equal(gconstpointer a, gconstpointer b) -{ - const ShaderState *as = a, *bs = b; - return memcmp(as, bs, sizeof(ShaderState)) == 0; -} - -static void generate_geometry_shader_pass_vertex(QString* s, const char* v) -{ - qstring_append_fmt(s, " gD0 = vD0[%s];\n", v); - qstring_append_fmt(s, " gD1 = vD1[%s];\n", v); - qstring_append_fmt(s, " gB0 = vB0[%s];\n", v); - qstring_append_fmt(s, " gB1 = vB1[%s];\n", v); - qstring_append_fmt(s, " gFog = vFog[%s];\n", v); - qstring_append_fmt(s, " gT0 = vT0[%s];\n", v); - qstring_append_fmt(s, " gT1 = vT1[%s];\n", v); - qstring_append_fmt(s, " gT2 = vT2[%s];\n", v); - qstring_append_fmt(s, " gT3 = vT3[%s];\n", v); - qstring_append_fmt(s, " gPos_w = vPos_w[%s];\n", v); - qstring_append_fmt(s, " gl_Position = gl_in[%s].gl_Position;\n", v); - qstring_append(s, " EmitVertex();\n"); -} - -static QString* generate_geometry_shader(unsigned int primitive_mode) -{ - /* 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 NV097_SET_BEGIN_END_OP_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, "noperspective in float vPos_w[];\n"); - qstring_append(s, "noperspective in vec4 vD0[];\n"); - qstring_append(s, "noperspective in vec4 vD1[];\n"); - qstring_append(s, "noperspective in vec4 vB0[];\n"); - qstring_append(s, "noperspective in vec4 vB1[];\n"); - qstring_append(s, "noperspective in vec4 vFog[];\n"); - qstring_append(s, "noperspective in vec4 vT0[];\n"); - qstring_append(s, "noperspective in vec4 vT1[];\n"); - qstring_append(s, "noperspective in vec4 vT2[];\n"); - qstring_append(s, "noperspective in vec4 vT3[];\n"); - qstring_append(s, "\n"); - qstring_append(s, "noperspective out float gPos_w;\n"); - qstring_append(s, "noperspective out vec4 gD0;\n"); - qstring_append(s, "noperspective out vec4 gD1;\n"); - qstring_append(s, "noperspective out vec4 gB0;\n"); - qstring_append(s, "noperspective out vec4 gB1;\n"); - qstring_append(s, "noperspective out vec4 gFog;\n"); - qstring_append(s, "noperspective out vec4 gT0;\n"); - qstring_append(s, "noperspective out vec4 gT1;\n"); - qstring_append(s, "noperspective out vec4 gT2;\n"); - qstring_append(s, "noperspective out vec4 gT3;\n"); - qstring_append(s, "\n"); - qstring_append(s, "void main() {\n"); - switch (primitive_mode) { - case NV097_SET_BEGIN_END_OP_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; - } - qstring_append(s, "}\n"); - - return s; -} - -static ShaderBinding* generate_shaders(const ShaderState state) -{ - int i, j; - GLint compiled = 0; - - bool with_geom = state.primitive_mode == NV097_SET_BEGIN_END_OP_QUADS; - char v_prefix = with_geom ? 'v' : 'g'; - - GLuint program = glCreateProgram(); - - /* create the vertex shader */ - - QString *vertex_shader_code = NULL; - if (state.fixed_function) { - /* generate vertex shader mimicking fixed function */ - vertex_shader_code = qstring_new(); - qstring_append(vertex_shader_code, -"#version 330\n" -"\n" -"in vec4 position;\n" -"in vec4 weight;\n" -"in vec3 normal;\n" -"in vec4 diffuse;\n" -"in vec4 specular;\n" -"in float fogCoord;\n" -"in vec4 backDiffuse;\n" -"in vec4 backSpecular;\n" -"in vec4 texture0;\n" -"in vec4 texture1;\n" -"in vec4 texture2;\n" -"in vec4 texture3;\n" -"\n"); - - qstring_append_fmt(vertex_shader_code, - "noperspective out float %cPos_w;\n", v_prefix); - qstring_append_fmt(vertex_shader_code, - "noperspective out vec4 %cD0 = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); - qstring_append_fmt(vertex_shader_code, - "noperspective out vec4 %cD1 = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); - qstring_append_fmt(vertex_shader_code, - "noperspective out vec4 %cB0 = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); - qstring_append_fmt(vertex_shader_code, - "noperspective out vec4 %cB1 = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); - qstring_append_fmt(vertex_shader_code, - "noperspective out vec4 %cFog = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); - qstring_append_fmt(vertex_shader_code, - "noperspective out vec4 %cT0 = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); - qstring_append_fmt(vertex_shader_code, - "noperspective out vec4 %cT1 = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); - qstring_append_fmt(vertex_shader_code, - "noperspective out vec4 %cT2 = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); - qstring_append_fmt(vertex_shader_code, - "noperspective out vec4 %cT3 = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); - - qstring_append(vertex_shader_code, -"\n" -/* FIXME: Add these uniforms using code when they are used */ -"uniform mat4 texMat0;\n" -"uniform mat4 texMat1;\n" -"uniform mat4 texMat2;\n" -"uniform mat4 texMat3;\n" -"uniform mat4 modelViewMat0;\n" -"uniform mat4 modelViewMat1;\n" -"uniform mat4 modelViewMat2;\n" -"uniform mat4 modelViewMat3;\n" -"uniform mat4 invModelViewMat0;\n" -"uniform mat4 invModelViewMat1;\n" -"uniform mat4 invModelViewMat2;\n" -"uniform mat4 invModelViewMat3;\n" -"uniform mat4 projectionMat; /* FIXME: when is this used? */\n" -"uniform mat4 compositeMat;\n" -"uniform mat4 invViewport;\n" -"\n" -"void main() {\n"); - - /* Skinning */ - unsigned int count; - bool mix; - switch (state.skinning) { - case SKINNING_OFF: - count = 0; break; - case SKINNING_1WEIGHTS: - mix = true; count = 2; break; - case SKINNING_2WEIGHTS: - mix = true; count = 3; break; - case SKINNING_3WEIGHTS: - mix = true; count = 4; break; - case SKINNING_2WEIGHTS2MATRICES: - mix = false; count = 2; break; - case SKINNING_3WEIGHTS3MATRICES: - mix = false; count = 3; break; - case SKINNING_4WEIGHTS4MATRICES: - mix = false; count = 4; break; - default: - assert(false); - break; - } - qstring_append_fmt(vertex_shader_code, "/* Skinning mode %d */\n", - state.skinning); - if (count == 0) { - qstring_append(vertex_shader_code, "vec4 tPosition = position * modelViewMat0;\n"); - /* FIXME: Is the normal still transformed? */ - qstring_append(vertex_shader_code, "vec3 tNormal = (vec4(normal, 0.0) * invModelViewMat0).xyz;\n"); - } else { - qstring_append(vertex_shader_code, "vec4 tPosition = vec4(0.0);\n"); - qstring_append(vertex_shader_code, "vec3 tNormal = vec3(0.0);\n"); - if (mix) { - /* Tweening */ - if (count == 2) { - qstring_append(vertex_shader_code, - "tPosition += mix(position * modelViewMat1,\n" - " position * modelViewMat0, weight.x);\n" - "tNormal += mix((vec4(normal, 0.0) * invModelViewMat1).xyz,\n" - " (vec4(normal, 0.0) * invModelViewMat0).xyz, weight.x);\n"); - } else { - /* FIXME: Not sure how blend weights are calculated */ - assert(false); - } - } else { - /* Individual matrices */ - for (i = 0; i < count; i++) { - char c = "xyzw"[i]; - qstring_append_fmt(vertex_shader_code, "tPosition += position * modelViewMat%d * weight.%c;\n", - i, c); - qstring_append_fmt(vertex_shader_code, "tNormal += (vec4(normal, 0.0) * invModelViewMat%d * weight.%c).xyz;\n", - i, c); - } - assert(false); /* FIXME: Untested */ - } - } - - /* Normalization */ - if (state.normalization) { - qstring_append(vertex_shader_code, "tNormal = normalize(tNormal);\n"); - } - - /* Texgen */ - for (i = 0; i < 4; i++) { - qstring_append_fmt(vertex_shader_code, "/* Texgen for stage %d */\n", - i); - qstring_append_fmt(vertex_shader_code, "vec4 tTexture%d;\n", - i); - /* Set each component individually */ - /* FIXME: could be nicer if some channels share the same texgen */ - for (j = 0; j < 4; j++) { - char c = "xyzw"[j]; - switch (state.texgen[i][j]) { - case TEXGEN_DISABLE: - qstring_append_fmt(vertex_shader_code, "tTexture%d.%c = texture%d.%c;\n", - i, c, i, c); - break; - case TEXGEN_EYE_LINEAR: - qstring_append_fmt(vertex_shader_code, "tTexture%d.%c = tPosition.%c;\n", - i, c, c); - break; - case TEXGEN_OBJECT_LINEAR: - qstring_append_fmt(vertex_shader_code, "tTexture%d.%c = position.%c;\n", - i, c, c); - break; - case TEXGEN_SPHERE_MAP: - assert(i < 2); /* Channels S,T only! */ - assert(false); - break; - case TEXGEN_REFLECTION_MAP: - assert(i < 3); /* Channels S,T,R only! */ - qstring_append_fmt(vertex_shader_code, "tTexture%d.%c = reflect(???, tNormal).%c;\n", - i, c, c); - assert(false); /* FIXME: Code not complete yet! */ - break; - case TEXGEN_NORMAL_MAP: - assert(i < 3); /* Channels S,T,R only! */ - qstring_append_fmt(vertex_shader_code, "tTexture%d.%c = tNormal.%c;\n", - i, c, c); - break; - default: - assert(false); - break; - } - } - } - - /* Apply texture matrices */ - for (i = 0; i < 4; i++) { - if (state.texture_matrix_enable[i]) { - qstring_append_fmt(vertex_shader_code, "tTexture%d = tTexture%d * texMat%d;\n", - i, i, i); - } - } - - /* If skinning is off the composite matrix already includes the MV matrix */ - if (state.skinning == SKINNING_OFF) { - qstring_append(vertex_shader_code, "tPosition = position;\n"); - } - - qstring_append(vertex_shader_code, -" gl_Position = invViewport * (tPosition * compositeMat);\n" -/* temp hack: the composite matrix includes the view transform... */ -//" gl_Position = position * compositeMat;\n" -//" gl_Position.x = (gl_Position.x - 320.0) / 320.0;\n" -//" gl_Position.y = -(gl_Position.y - 240.0) / 240.0;\n" -" gl_Position.z = gl_Position.z * 2.0 - gl_Position.w;\n"); - - qstring_append_fmt(vertex_shader_code, - "%cPos_w = 1.0/gl_Position.w;\n", v_prefix); - qstring_append_fmt(vertex_shader_code, - "%cD0 = diffuse * %cPos_w;\n", v_prefix, v_prefix); - qstring_append_fmt(vertex_shader_code, - "%cD1 = specular * %cPos_w;\n", v_prefix, v_prefix); - qstring_append_fmt(vertex_shader_code, - "%cB0 = backDiffuse * %cPos_w;\n", v_prefix, v_prefix); - qstring_append_fmt(vertex_shader_code, - "%cB1 = backSpecular * %cPos_w;\n", v_prefix, v_prefix); - qstring_append_fmt(vertex_shader_code, - "%cT0 = tTexture0 * %cPos_w;\n", v_prefix, v_prefix); - qstring_append_fmt(vertex_shader_code, - "%cT1 = tTexture1 * %cPos_w;\n", v_prefix, v_prefix); - qstring_append_fmt(vertex_shader_code, - "%cT2 = tTexture2 * %cPos_w;\n", v_prefix, v_prefix); - qstring_append_fmt(vertex_shader_code, - "%cT3 = tTexture3 * %cPos_w;\n", v_prefix, v_prefix); - - qstring_append(vertex_shader_code, "}\n"); - - } else if (state.vertex_program) { - vertex_shader_code = vsh_translate(VSH_VERSION_XVS, - (uint32_t*)state.program_data, - state.program_length, - v_prefix); - } else { - assert(false); - } - - if (vertex_shader_code) { - const char* vertex_shader_code_str = qstring_get_str(vertex_shader_code); - - GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); - glAttachShader(program, vertex_shader); - - glShaderSource(vertex_shader, 1, &vertex_shader_code_str, 0); - glCompileShader(vertex_shader); - - NV2A_DPRINTF("bind new vertex shader, code:\n%s\n", vertex_shader_code_str); - - /* Check it compiled */ - compiled = 0; - glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &compiled); - if (!compiled) { - GLchar log[1024]; - glGetShaderInfoLog(vertex_shader, 1024, NULL, log); - fprintf(stderr, "nv2a: vertex shader compilation failed: %s\n", log); - abort(); - } - - QDECREF(vertex_shader_code); - } - - - if (state.fixed_function) { - /* bind fixed function vertex attributes */ - glBindAttribLocation(program, NV2A_VERTEX_ATTR_POSITION, "position"); - glBindAttribLocation(program, NV2A_VERTEX_ATTR_WEIGHT, "weight"); - glBindAttribLocation(program, NV2A_VERTEX_ATTR_DIFFUSE, "diffuse"); - glBindAttribLocation(program, NV2A_VERTEX_ATTR_SPECULAR, "specular"); - glBindAttribLocation(program, NV2A_VERTEX_ATTR_FOG, "fog"); - glBindAttribLocation(program, NV2A_VERTEX_ATTR_BACK_DIFFUSE, "backDiffuse"); - glBindAttribLocation(program, NV2A_VERTEX_ATTR_BACK_SPECULAR, "backSpecular"); - glBindAttribLocation(program, NV2A_VERTEX_ATTR_TEXTURE0, "texture0"); - glBindAttribLocation(program, NV2A_VERTEX_ATTR_TEXTURE1, "texture1"); - glBindAttribLocation(program, NV2A_VERTEX_ATTR_TEXTURE2, "texture2"); - glBindAttribLocation(program, NV2A_VERTEX_ATTR_TEXTURE3, "texture3"); - } else if (state.vertex_program) { - /* Bind attributes for transform program*/ - char tmp[8]; - for(i = 0; i < 16; i++) { - snprintf(tmp, sizeof(tmp), "v%d", i); - glBindAttribLocation(program, i, tmp); - } - } else { - assert(false); - } - - - /* generate a fragment shader from register combiners */ - GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); - glAttachShader(program, fragment_shader); - - QString *fragment_shader_code = psh_translate(state.combiner_control, - state.shader_stage_program, - state.other_stage_input, - state.rgb_inputs, state.rgb_outputs, - state.alpha_inputs, state.alpha_outputs, - /* constant_0, constant_1, */ - state.final_inputs_0, state.final_inputs_1, - /* final_constant_0, final_constant_1, */ - state.rect_tex, - state.compare_mode, - state.alphakill, - state.alpha_test, state.alpha_func); - - const char *fragment_shader_code_str = qstring_get_str(fragment_shader_code); - - NV2A_DPRINTF("bind new fragment shader, code:\n%s\n", fragment_shader_code_str); - - glShaderSource(fragment_shader, 1, &fragment_shader_code_str, 0); - glCompileShader(fragment_shader); - - /* Check it compiled */ - glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &compiled); - if (!compiled) { - GLchar log[1024]; - glGetShaderInfoLog(fragment_shader, 1024, NULL, log); - fprintf(stderr, "nv2a: fragment shader compilation failed: %s\n", log); - abort(); - } - - QDECREF(fragment_shader_code); - - - if (with_geom) { - GLuint geometry_shader = glCreateShader(GL_GEOMETRY_SHADER); - glAttachShader(program, geometry_shader); - - QString* geometry_shader_code = - generate_geometry_shader(state.primitive_mode); - const char* geometry_shader_code_str = - qstring_get_str(geometry_shader_code); - - NV2A_DPRINTF("bind geometry shader, code:\n%s\n", geometry_shader_code_str); - - glShaderSource(geometry_shader, 1, &geometry_shader_code_str, 0); - glCompileShader(geometry_shader); - - /* Check it compiled */ - compiled = 0; - glGetShaderiv(geometry_shader, GL_COMPILE_STATUS, &compiled); - if (!compiled) { - GLchar log[2048]; - glGetShaderInfoLog(geometry_shader, 2048, NULL, log); - fprintf(stderr, "nv2a: geometry shader compilation failed: %s\n", log); - abort(); - } - - QDECREF(geometry_shader_code); - } - - - /* link the program */ - glLinkProgram(program); - GLint linked = 0; - glGetProgramiv(program, GL_LINK_STATUS, &linked); - if(!linked) { - GLchar log[2048]; - glGetProgramInfoLog(program, 2048, NULL, log); - fprintf(stderr, "nv2a: shader linking failed: %s\n", log); - abort(); - } - - glUseProgram(program); - - /* set texture samplers */ - for (i = 0; i < NV2A_MAX_TEXTURES; i++) { - char samplerName[16]; - snprintf(samplerName, sizeof(samplerName), "texSamp%d", i); - GLint texSampLoc = glGetUniformLocation(program, samplerName); - if (texSampLoc >= 0) { - glUniform1i(texSampLoc, i); - } - } - - /* validate the program */ - glValidateProgram(program); - GLint valid = 0; - glGetProgramiv(program, GL_VALIDATE_STATUS, &valid); - if (!valid) { - GLchar log[1024]; - glGetProgramInfoLog(program, 1024, NULL, log); - fprintf(stderr, "nv2a: shader validation failed: %s\n", log); - abort(); - } - - ShaderBinding* ret = g_malloc0(sizeof(ShaderBinding)); - ret->gl_program = program; - - /* lookup fragment shader locations */ - for (i=0; i<=8; i++) { - for (j=0; j<2; j++) { - char tmp[8]; - snprintf(tmp, sizeof(tmp), "c_%d_%d", i, j); - ret->psh_constant_loc[i][j] = glGetUniformLocation(program, tmp); - } - } - if (state.vertex_program) { - /* lookup vertex shader bindings */ - for(i = 0; i < NV2A_VERTEXSHADER_CONSTANTS; i++) { - char tmp[8]; - snprintf(tmp, sizeof(tmp), "c[%d]", i); - ret->vsh_constant_loc[i] = glGetUniformLocation(program, tmp); - } - } - - return ret; -} - static void pgraph_apply_anti_aliasing_factor(PGRAPHState *pg, unsigned int *width, unsigned int *height) @@ -3228,6 +2680,17 @@ static void pgraph_get_surface_dimensions(PGRAPHState *pg, } } +/* hash and equality for shader cache hash table */ +static guint shader_hash(gconstpointer key) +{ + return fnv_hash(key, sizeof(ShaderState)); +} +static gboolean shader_equal(gconstpointer a, gconstpointer b) +{ + const ShaderState *as = a, *bs = b; + return memcmp(as, bs, sizeof(ShaderState)) == 0; +} + static void pgraph_bind_shaders(PGRAPHState *pg) { int i, j; @@ -5502,7 +4965,7 @@ static void pgraph_method(NV2AState *d, if (pg->draw_arrays_length > 0) { unsigned int last_start = pg->gl_draw_arrays_start[pg->draw_arrays_length - 1]; - unsigned int* last_count = + GLsizei* last_count = &pg->gl_draw_arrays_count[pg->draw_arrays_length - 1]; if (start == (last_start + *last_count)) { *last_count += count; diff --git a/hw/xbox/nv2a_shaders.c b/hw/xbox/nv2a_shaders.c new file mode 100644 index 0000000000..9cf305cf03 --- /dev/null +++ b/hw/xbox/nv2a_shaders.c @@ -0,0 +1,523 @@ +/* + * QEMU Geforce NV2A shader generator + * + * Copyright (c) 2015 espes + * Copyright (c) 2015 JayFoxRox + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "qemu-common.h" +#include "hw/xbox/nv2a_shaders.h" + +// #define DEBUG +#ifdef DEBUG +# define NV2A_DPRINTF(format, ...) printf("nv2a: " format, ## __VA_ARGS__) +#else +# define NV2A_DPRINTF(format, ...) do { } while (0) +#endif + +static void generate_geometry_shader_pass_vertex(QString* s, const char* v) +{ + qstring_append_fmt(s, " gD0 = vD0[%s];\n", v); + qstring_append_fmt(s, " gD1 = vD1[%s];\n", v); + qstring_append_fmt(s, " gB0 = vB0[%s];\n", v); + qstring_append_fmt(s, " gB1 = vB1[%s];\n", v); + qstring_append_fmt(s, " gFog = vFog[%s];\n", v); + qstring_append_fmt(s, " gT0 = vT0[%s];\n", v); + qstring_append_fmt(s, " gT1 = vT1[%s];\n", v); + qstring_append_fmt(s, " gT2 = vT2[%s];\n", v); + qstring_append_fmt(s, " gT3 = vT3[%s];\n", v); + qstring_append_fmt(s, " gPos_w = vPos_w[%s];\n", v); + qstring_append_fmt(s, " gl_Position = gl_in[%s].gl_Position;\n", v); + qstring_append(s, " EmitVertex();\n"); +} + +static QString* generate_geometry_shader(enum ShaderPrimitiveMode primitive_mode) +{ + /* 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, "noperspective in float vPos_w[];\n"); + qstring_append(s, "noperspective in vec4 vD0[];\n"); + qstring_append(s, "noperspective in vec4 vD1[];\n"); + qstring_append(s, "noperspective in vec4 vB0[];\n"); + qstring_append(s, "noperspective in vec4 vB1[];\n"); + qstring_append(s, "noperspective in vec4 vFog[];\n"); + qstring_append(s, "noperspective in vec4 vT0[];\n"); + qstring_append(s, "noperspective in vec4 vT1[];\n"); + qstring_append(s, "noperspective in vec4 vT2[];\n"); + qstring_append(s, "noperspective in vec4 vT3[];\n"); + qstring_append(s, "\n"); + qstring_append(s, "noperspective out float gPos_w;\n"); + qstring_append(s, "noperspective out vec4 gD0;\n"); + qstring_append(s, "noperspective out vec4 gD1;\n"); + qstring_append(s, "noperspective out vec4 gB0;\n"); + qstring_append(s, "noperspective out vec4 gB1;\n"); + qstring_append(s, "noperspective out vec4 gFog;\n"); + qstring_append(s, "noperspective out vec4 gT0;\n"); + qstring_append(s, "noperspective out vec4 gT1;\n"); + qstring_append(s, "noperspective out vec4 gT2;\n"); + qstring_append(s, "noperspective out vec4 gT3;\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; + } + qstring_append(s, "}\n"); + + return s; +} + + + +static QString* generate_fixed_function(const ShaderState state, char v_prefix) +{ + int i, j; + + /* generate vertex shader mimicking fixed function */ + QString* vertex_shader_code = qstring_new(); + qstring_append(vertex_shader_code, +"#version 330\n" +"\n" +"in vec4 position;\n" +"in vec4 weight;\n" +"in vec3 normal;\n" +"in vec4 diffuse;\n" +"in vec4 specular;\n" +"in float fogCoord;\n" +"in vec4 backDiffuse;\n" +"in vec4 backSpecular;\n" +"in vec4 texture0;\n" +"in vec4 texture1;\n" +"in vec4 texture2;\n" +"in vec4 texture3;\n" +"\n"); + + qstring_append_fmt(vertex_shader_code, + "noperspective out float %cPos_w;\n", v_prefix); + qstring_append_fmt(vertex_shader_code, + "noperspective out vec4 %cD0 = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); + qstring_append_fmt(vertex_shader_code, + "noperspective out vec4 %cD1 = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); + qstring_append_fmt(vertex_shader_code, + "noperspective out vec4 %cB0 = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); + qstring_append_fmt(vertex_shader_code, + "noperspective out vec4 %cB1 = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); + qstring_append_fmt(vertex_shader_code, + "noperspective out vec4 %cFog = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); + qstring_append_fmt(vertex_shader_code, + "noperspective out vec4 %cT0 = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); + qstring_append_fmt(vertex_shader_code, + "noperspective out vec4 %cT1 = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); + qstring_append_fmt(vertex_shader_code, + "noperspective out vec4 %cT2 = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); + qstring_append_fmt(vertex_shader_code, + "noperspective out vec4 %cT3 = vec4(0.0,0.0,0.0,1.0);\n", v_prefix); + + qstring_append(vertex_shader_code, +"\n" +/* FIXME: Add these uniforms using code when they are used */ +"uniform mat4 texMat0;\n" +"uniform mat4 texMat1;\n" +"uniform mat4 texMat2;\n" +"uniform mat4 texMat3;\n" +"uniform mat4 modelViewMat0;\n" +"uniform mat4 modelViewMat1;\n" +"uniform mat4 modelViewMat2;\n" +"uniform mat4 modelViewMat3;\n" +"uniform mat4 invModelViewMat0;\n" +"uniform mat4 invModelViewMat1;\n" +"uniform mat4 invModelViewMat2;\n" +"uniform mat4 invModelViewMat3;\n" +"uniform mat4 projectionMat; /* FIXME: when is this used? */\n" +"uniform mat4 compositeMat;\n" +"uniform mat4 invViewport;\n" +"\n" +"void main() {\n"); + + /* Skinning */ + unsigned int count; + bool mix; + switch (state.skinning) { + case SKINNING_OFF: + count = 0; break; + case SKINNING_1WEIGHTS: + mix = true; count = 2; break; + case SKINNING_2WEIGHTS: + mix = true; count = 3; break; + case SKINNING_3WEIGHTS: + mix = true; count = 4; break; + case SKINNING_2WEIGHTS2MATRICES: + mix = false; count = 2; break; + case SKINNING_3WEIGHTS3MATRICES: + mix = false; count = 3; break; + case SKINNING_4WEIGHTS4MATRICES: + mix = false; count = 4; break; + default: + assert(false); + break; + } + qstring_append_fmt(vertex_shader_code, "/* Skinning mode %d */\n", + state.skinning); + if (count == 0) { + qstring_append(vertex_shader_code, "vec4 tPosition = position * modelViewMat0;\n"); + /* FIXME: Is the normal still transformed? */ + qstring_append(vertex_shader_code, "vec3 tNormal = (vec4(normal, 0.0) * invModelViewMat0).xyz;\n"); + } else { + qstring_append(vertex_shader_code, "vec4 tPosition = vec4(0.0);\n"); + qstring_append(vertex_shader_code, "vec3 tNormal = vec3(0.0);\n"); + if (mix) { + /* Tweening */ + if (count == 2) { + qstring_append(vertex_shader_code, + "tPosition += mix(position * modelViewMat1,\n" + " position * modelViewMat0, weight.x);\n" + "tNormal += mix((vec4(normal, 0.0) * invModelViewMat1).xyz,\n" + " (vec4(normal, 0.0) * invModelViewMat0).xyz, weight.x);\n"); + } else { + /* FIXME: Not sure how blend weights are calculated */ + assert(false); + } + } else { + /* Individual matrices */ + for (i = 0; i < count; i++) { + char c = "xyzw"[i]; + qstring_append_fmt(vertex_shader_code, + "tPosition += position * modelViewMat%d * weight.%c;\n", + i, c); + qstring_append_fmt(vertex_shader_code, + "tNormal += (vec4(normal, 0.0) * invModelViewMat%d * weight.%c).xyz;\n", + i, c); + } + assert(false); /* FIXME: Untested */ + } + } + + /* Normalization */ + if (state.normalization) { + qstring_append(vertex_shader_code, "tNormal = normalize(tNormal);\n"); + } + + /* Texgen */ + for (i = 0; i < 4; i++) { + qstring_append_fmt(vertex_shader_code, "/* Texgen for stage %d */\n", + i); + qstring_append_fmt(vertex_shader_code, "vec4 tTexture%d;\n", + i); + /* Set each component individually */ + /* FIXME: could be nicer if some channels share the same texgen */ + for (j = 0; j < 4; j++) { + char c = "xyzw"[j]; + switch (state.texgen[i][j]) { + case TEXGEN_DISABLE: + qstring_append_fmt(vertex_shader_code, + "tTexture%d.%c = texture%d.%c;\n", + i, c, i, c); + break; + case TEXGEN_EYE_LINEAR: + qstring_append_fmt(vertex_shader_code, + "tTexture%d.%c = tPosition.%c;\n", + i, c, c); + break; + case TEXGEN_OBJECT_LINEAR: + qstring_append_fmt(vertex_shader_code, + "tTexture%d.%c = position.%c;\n", + i, c, c); + break; + case TEXGEN_SPHERE_MAP: + assert(i < 2); /* Channels S,T only! */ + assert(false); + break; + case TEXGEN_REFLECTION_MAP: + assert(i < 3); /* Channels S,T,R only! */ + qstring_append_fmt(vertex_shader_code, + "tTexture%d.%c = reflect(???, tNormal).%c;\n", + i, c, c); + assert(false); /* FIXME: Code not complete yet! */ + break; + case TEXGEN_NORMAL_MAP: + assert(i < 3); /* Channels S,T,R only! */ + qstring_append_fmt(vertex_shader_code, + "tTexture%d.%c = tNormal.%c;\n", + i, c, c); + break; + default: + assert(false); + break; + } + } + } + + /* Apply texture matrices */ + for (i = 0; i < 4; i++) { + if (state.texture_matrix_enable[i]) { + qstring_append_fmt(vertex_shader_code, + "tTexture%d = tTexture%d * texMat%d;\n", + i, i, i); + } + } + + /* If skinning is off the composite matrix already includes the MV matrix */ + if (state.skinning == SKINNING_OFF) { + qstring_append(vertex_shader_code, "tPosition = position;\n"); + } + + qstring_append(vertex_shader_code, + " gl_Position = invViewport * (tPosition * compositeMat);\n" +/* temp hack: the composite matrix includes the view transform... */ +//" gl_Position = position * compositeMat;\n" +//" gl_Position.x = (gl_Position.x - 320.0) / 320.0;\n" +//" gl_Position.y = -(gl_Position.y - 240.0) / 240.0;\n" + " gl_Position.z = gl_Position.z * 2.0 - gl_Position.w;\n"); + + qstring_append_fmt(vertex_shader_code, + "%cPos_w = 1.0/gl_Position.w;\n", v_prefix); + qstring_append_fmt(vertex_shader_code, + "%cD0 = diffuse * %cPos_w;\n", v_prefix, v_prefix); + qstring_append_fmt(vertex_shader_code, + "%cD1 = specular * %cPos_w;\n", v_prefix, v_prefix); + qstring_append_fmt(vertex_shader_code, + "%cB0 = backDiffuse * %cPos_w;\n", v_prefix, v_prefix); + qstring_append_fmt(vertex_shader_code, + "%cB1 = backSpecular * %cPos_w;\n", v_prefix, v_prefix); + qstring_append_fmt(vertex_shader_code, + "%cT0 = tTexture0 * %cPos_w;\n", v_prefix, v_prefix); + qstring_append_fmt(vertex_shader_code, + "%cT1 = tTexture1 * %cPos_w;\n", v_prefix, v_prefix); + qstring_append_fmt(vertex_shader_code, + "%cT2 = tTexture2 * %cPos_w;\n", v_prefix, v_prefix); + qstring_append_fmt(vertex_shader_code, + "%cT3 = tTexture3 * %cPos_w;\n", v_prefix, v_prefix); + + qstring_append(vertex_shader_code, "}\n"); + + return vertex_shader_code; +} + +ShaderBinding* generate_shaders(const ShaderState state) +{ + int i, j; + GLint compiled = 0; + + bool with_geom = state.primitive_mode == PRIM_TYPE_QUADS; + char v_prefix = with_geom ? 'v' : 'g'; + + GLuint program = glCreateProgram(); + + /* create the vertex shader */ + + QString *vertex_shader_code = NULL; + if (state.fixed_function) { + vertex_shader_code = generate_fixed_function(state, v_prefix); + + } else if (state.vertex_program) { + vertex_shader_code = vsh_translate(VSH_VERSION_XVS, + (uint32_t*)state.program_data, + state.program_length, + v_prefix); + } else { + assert(false); + } + + if (vertex_shader_code) { + const char* vertex_shader_code_str = qstring_get_str(vertex_shader_code); + + GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glAttachShader(program, vertex_shader); + + glShaderSource(vertex_shader, 1, &vertex_shader_code_str, 0); + glCompileShader(vertex_shader); + + NV2A_DPRINTF("bind new vertex shader, code:\n%s\n", vertex_shader_code_str); + + /* Check it compiled */ + compiled = 0; + glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &compiled); + if (!compiled) { + GLchar log[1024]; + glGetShaderInfoLog(vertex_shader, 1024, NULL, log); + fprintf(stderr, "nv2a: vertex shader compilation failed: %s\n", log); + abort(); + } + + QDECREF(vertex_shader_code); + } + + + if (state.fixed_function) { + /* bind fixed function vertex attributes */ + glBindAttribLocation(program, NV2A_VERTEX_ATTR_POSITION, "position"); + glBindAttribLocation(program, NV2A_VERTEX_ATTR_WEIGHT, "weight"); + glBindAttribLocation(program, NV2A_VERTEX_ATTR_DIFFUSE, "diffuse"); + glBindAttribLocation(program, NV2A_VERTEX_ATTR_SPECULAR, "specular"); + glBindAttribLocation(program, NV2A_VERTEX_ATTR_FOG, "fog"); + glBindAttribLocation(program, NV2A_VERTEX_ATTR_BACK_DIFFUSE, "backDiffuse"); + glBindAttribLocation(program, NV2A_VERTEX_ATTR_BACK_SPECULAR, "backSpecular"); + glBindAttribLocation(program, NV2A_VERTEX_ATTR_TEXTURE0, "texture0"); + glBindAttribLocation(program, NV2A_VERTEX_ATTR_TEXTURE1, "texture1"); + glBindAttribLocation(program, NV2A_VERTEX_ATTR_TEXTURE2, "texture2"); + glBindAttribLocation(program, NV2A_VERTEX_ATTR_TEXTURE3, "texture3"); + } else if (state.vertex_program) { + /* Bind attributes for transform program*/ + char tmp[8]; + for(i = 0; i < 16; i++) { + snprintf(tmp, sizeof(tmp), "v%d", i); + glBindAttribLocation(program, i, tmp); + } + } else { + assert(false); + } + + + /* generate a fragment shader from register combiners */ + GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glAttachShader(program, fragment_shader); + + QString *fragment_shader_code = psh_translate(state.combiner_control, + state.shader_stage_program, + state.other_stage_input, + state.rgb_inputs, state.rgb_outputs, + state.alpha_inputs, state.alpha_outputs, + /* constant_0, constant_1, */ + state.final_inputs_0, state.final_inputs_1, + /* final_constant_0, final_constant_1, */ + state.rect_tex, + state.compare_mode, + state.alphakill, + state.alpha_test, state.alpha_func); + + const char *fragment_shader_code_str = qstring_get_str(fragment_shader_code); + + NV2A_DPRINTF("bind new fragment shader, code:\n%s\n", fragment_shader_code_str); + + glShaderSource(fragment_shader, 1, &fragment_shader_code_str, 0); + glCompileShader(fragment_shader); + + /* Check it compiled */ + glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &compiled); + if (!compiled) { + GLchar log[1024]; + glGetShaderInfoLog(fragment_shader, 1024, NULL, log); + fprintf(stderr, "nv2a: fragment shader compilation failed: %s\n", log); + abort(); + } + + QDECREF(fragment_shader_code); + + + if (with_geom) { + GLuint geometry_shader = glCreateShader(GL_GEOMETRY_SHADER); + glAttachShader(program, geometry_shader); + + QString* geometry_shader_code = + generate_geometry_shader(state.primitive_mode); + const char* geometry_shader_code_str = + qstring_get_str(geometry_shader_code); + + NV2A_DPRINTF("bind geometry shader, code:\n%s\n", geometry_shader_code_str); + + glShaderSource(geometry_shader, 1, &geometry_shader_code_str, 0); + glCompileShader(geometry_shader); + + /* Check it compiled */ + compiled = 0; + glGetShaderiv(geometry_shader, GL_COMPILE_STATUS, &compiled); + if (!compiled) { + GLchar log[2048]; + glGetShaderInfoLog(geometry_shader, 2048, NULL, log); + fprintf(stderr, "nv2a: geometry shader compilation failed: %s\n", log); + abort(); + } + + QDECREF(geometry_shader_code); + } + + + /* link the program */ + glLinkProgram(program); + GLint linked = 0; + glGetProgramiv(program, GL_LINK_STATUS, &linked); + if(!linked) { + GLchar log[2048]; + glGetProgramInfoLog(program, 2048, NULL, log); + fprintf(stderr, "nv2a: shader linking failed: %s\n", log); + abort(); + } + + glUseProgram(program); + + /* set texture samplers */ + for (i = 0; i < 4; i++) { + char samplerName[16]; + snprintf(samplerName, sizeof(samplerName), "texSamp%d", i); + GLint texSampLoc = glGetUniformLocation(program, samplerName); + if (texSampLoc >= 0) { + glUniform1i(texSampLoc, i); + } + } + + /* validate the program */ + glValidateProgram(program); + GLint valid = 0; + glGetProgramiv(program, GL_VALIDATE_STATUS, &valid); + if (!valid) { + GLchar log[1024]; + glGetProgramInfoLog(program, 1024, NULL, log); + fprintf(stderr, "nv2a: shader validation failed: %s\n", log); + abort(); + } + + ShaderBinding* ret = g_malloc0(sizeof(ShaderBinding)); + ret->gl_program = program; + + /* lookup fragment shader locations */ + for (i=0; i<=8; i++) { + for (j=0; j<2; j++) { + char tmp[8]; + snprintf(tmp, sizeof(tmp), "c_%d_%d", i, j); + ret->psh_constant_loc[i][j] = glGetUniformLocation(program, tmp); + } + } + if (state.vertex_program) { + /* lookup vertex shader bindings */ + for(i = 0; i < NV2A_VERTEXSHADER_CONSTANTS; i++) { + char tmp[8]; + snprintf(tmp, sizeof(tmp), "c[%d]", i); + ret->vsh_constant_loc[i] = glGetUniformLocation(program, tmp); + } + } + + return ret; +} \ No newline at end of file diff --git a/hw/xbox/nv2a_shaders.h b/hw/xbox/nv2a_shaders.h new file mode 100644 index 0000000000..bd644d6e60 --- /dev/null +++ b/hw/xbox/nv2a_shaders.h @@ -0,0 +1,104 @@ +/* + * QEMU Geforce NV2A shader generator + * + * Copyright (c) 2015 espes + * Copyright (c) 2015 JayFoxRox + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "qapi/qmp/qstring.h" +#include "gl/gloffscreen.h" + +#include "nv2a_vsh.h" +#include "nv2a_psh.h" + +#define NV2A_VERTEX_ATTR_POSITION 0 +#define NV2A_VERTEX_ATTR_WEIGHT 1 +#define NV2A_VERTEX_ATTR_NORMAL 2 +#define NV2A_VERTEX_ATTR_DIFFUSE 3 +#define NV2A_VERTEX_ATTR_SPECULAR 4 +#define NV2A_VERTEX_ATTR_FOG 5 +#define NV2A_VERTEX_ATTR_POINT_SIZE 6 +#define NV2A_VERTEX_ATTR_BACK_DIFFUSE 7 +#define NV2A_VERTEX_ATTR_BACK_SPECULAR 8 +#define NV2A_VERTEX_ATTR_TEXTURE0 9 +#define NV2A_VERTEX_ATTR_TEXTURE1 10 +#define NV2A_VERTEX_ATTR_TEXTURE2 11 +#define NV2A_VERTEX_ATTR_TEXTURE3 12 +#define NV2A_VERTEX_ATTR_RESERVED1 13 +#define NV2A_VERTEX_ATTR_RESERVED2 14 +#define NV2A_VERTEX_ATTR_RESERVED3 15 + +#define NV2A_MAX_TRANSFORM_PROGRAM_LENGTH 136 +#define NV2A_VERTEXSHADER_CONSTANTS 192 + +enum ShaderPrimitiveMode { + PRIM_TYPE_NONE, + PRIM_TYPE_POINTS, + PRIM_TYPE_LINES, + PRIM_TYPE_LINE_LOOP, + PRIM_TYPE_LINE_STRIP, + PRIM_TYPE_TRIANGLES, + PRIM_TYPE_TRIANGLE_STRIP, + PRIM_TYPE_TRIANGLE_FAN, + PRIM_TYPE_QUADS, + PRIM_TYPE_QUAD_STRIP, + PRIM_TYPE_POLYGON, +}; + +typedef struct ShaderState { + /* fragment shader - register combiner stuff */ + uint32_t combiner_control; + uint32_t shader_stage_program; + uint32_t other_stage_input; + uint32_t final_inputs_0; + uint32_t final_inputs_1; + + uint32_t rgb_inputs[8], rgb_outputs[8]; + uint32_t alpha_inputs[8], alpha_outputs[8]; + + bool rect_tex[4]; + bool compare_mode[4][4]; + bool alphakill[4]; + + bool alpha_test; + enum PshAlphaFunc alpha_func; + + bool texture_matrix_enable[4]; + enum VshTexgen texgen[4][4]; + + enum VshSkinning skinning; + + bool normalization; + + bool fixed_function; + + /* vertex program */ + bool vertex_program; + uint32_t program_data[NV2A_MAX_TRANSFORM_PROGRAM_LENGTH][VSH_TOKEN_SIZE]; + int program_length; + + /* primitive format for geometry shader */ + enum ShaderPrimitiveMode primitive_mode; +} ShaderState; + +typedef struct ShaderBinding { + GLuint gl_program; + GLint psh_constant_loc[9][2]; + GLint vsh_constant_loc[NV2A_VERTEXSHADER_CONSTANTS]; +} ShaderBinding; + +ShaderBinding* generate_shaders(const ShaderState state); +