mirror of https://github.com/xemu-project/xemu.git
split out shader generation from nv2a.c
This commit is contained in:
parent
87060feaac
commit
27cf9cd4da
565
hw/xbox/nv2a.c
565
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;
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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);
|
||||
|
Loading…
Reference in New Issue