mirror of https://github.com/xemu-project/xemu.git
317 lines
11 KiB
C
317 lines
11 KiB
C
/*
|
|
* Geforce NV2A PGRAPH GLSL Shader Generator
|
|
*
|
|
* Copyright (c) 2015 espes
|
|
* Copyright (c) 2015 Jannik Vogel
|
|
* Copyright (c) 2020-2024 Matt Borgerson
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "hw/xbox/nv2a/pgraph/shaders.h"
|
|
#include "common.h"
|
|
#include "vsh.h"
|
|
#include "vsh-ff.h"
|
|
#include "vsh-prog.h"
|
|
#include <stdbool.h>
|
|
|
|
MString *pgraph_gen_vsh_glsl(const ShaderState *state, bool prefix_outputs)
|
|
{
|
|
int i;
|
|
MString *output = mstring_new();
|
|
mstring_append_fmt(output, "#version %d\n\n", state->vulkan ? 450 : 400);
|
|
|
|
MString *header = mstring_from_str("");
|
|
MString *uniforms = mstring_from_str("");
|
|
|
|
const char *u = state->vulkan ? "" : "uniform "; // FIXME: Remove
|
|
|
|
mstring_append_fmt(uniforms,
|
|
"%svec4 clipRange;\n"
|
|
"%svec2 surfaceSize;\n"
|
|
"%svec4 c[" stringify(NV2A_VERTEXSHADER_CONSTANTS) "];\n"
|
|
"%svec2 fogParam;\n",
|
|
u, u, u, u
|
|
);
|
|
|
|
mstring_append(header,
|
|
GLSL_DEFINE(fogPlane, GLSL_C(NV_IGRAPH_XF_XFCTX_FOG))
|
|
GLSL_DEFINE(texMat0, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_T0MAT))
|
|
GLSL_DEFINE(texMat1, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_T1MAT))
|
|
GLSL_DEFINE(texMat2, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_T2MAT))
|
|
GLSL_DEFINE(texMat3, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_T3MAT))
|
|
|
|
"\n"
|
|
"vec4 oPos = vec4(0.0,0.0,0.0,1.0);\n"
|
|
"vec4 oD0 = vec4(0.0,0.0,0.0,1.0);\n"
|
|
"vec4 oD1 = vec4(0.0,0.0,0.0,1.0);\n"
|
|
"vec4 oB0 = vec4(0.0,0.0,0.0,1.0);\n"
|
|
"vec4 oB1 = vec4(0.0,0.0,0.0,1.0);\n"
|
|
"vec4 oPts = vec4(0.0,0.0,0.0,1.0);\n"
|
|
"vec4 oFog = vec4(0.0,0.0,0.0,1.0);\n"
|
|
"vec4 oT0 = vec4(0.0,0.0,0.0,1.0);\n"
|
|
"vec4 oT1 = vec4(0.0,0.0,0.0,1.0);\n"
|
|
"vec4 oT2 = vec4(0.0,0.0,0.0,1.0);\n"
|
|
"vec4 oT3 = vec4(0.0,0.0,0.0,1.0);\n"
|
|
"\n"
|
|
"vec4 decompress_11_11_10(int cmp) {\n"
|
|
" float x = float(bitfieldExtract(cmp, 0, 11)) / 1023.0;\n"
|
|
" float y = float(bitfieldExtract(cmp, 11, 11)) / 1023.0;\n"
|
|
" float z = float(bitfieldExtract(cmp, 22, 10)) / 511.0;\n"
|
|
" return vec4(x, y, z, 1);\n"
|
|
"}\n"
|
|
"\n"
|
|
// Clamp to range [2^(-64), 2^64] or [-2^64, -2^(-64)].
|
|
"float clampAwayZeroInf(float t) {\n"
|
|
" if (t > 0.0 || floatBitsToUint(t) == 0) {\n"
|
|
" t = clamp(t, uintBitsToFloat(0x1F800000), uintBitsToFloat(0x5F800000));\n"
|
|
" } else {\n"
|
|
" t = clamp(t, uintBitsToFloat(0xDF800000), uintBitsToFloat(0x9F800000));\n"
|
|
" }\n"
|
|
" return t;\n"
|
|
"}\n");
|
|
|
|
pgraph_get_glsl_vtx_header(header, state->vulkan, state->smooth_shading,
|
|
false, prefix_outputs, false);
|
|
|
|
if (prefix_outputs) {
|
|
mstring_append(header,
|
|
"#define vtxD0 v_vtxD0\n"
|
|
"#define vtxD1 v_vtxD1\n"
|
|
"#define vtxB0 v_vtxB0\n"
|
|
"#define vtxB1 v_vtxB1\n"
|
|
"#define vtxFog v_vtxFog\n"
|
|
"#define vtxT0 v_vtxT0\n"
|
|
"#define vtxT1 v_vtxT1\n"
|
|
"#define vtxT2 v_vtxT2\n"
|
|
"#define vtxT3 v_vtxT3\n"
|
|
);
|
|
}
|
|
mstring_append(header, "\n");
|
|
|
|
int num_uniform_attrs = 0;
|
|
|
|
for (i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
|
|
bool is_uniform = state->uniform_attrs & (1 << i);
|
|
bool is_swizzled = state->swizzle_attrs & (1 << i);
|
|
bool is_compressed = state->compressed_attrs & (1 << i);
|
|
|
|
assert(!(is_uniform && is_compressed));
|
|
assert(!(is_uniform && is_swizzled));
|
|
|
|
if (is_uniform) {
|
|
mstring_append_fmt(header, "vec4 v%d = inlineValue[%d];\n", i,
|
|
num_uniform_attrs);
|
|
num_uniform_attrs += 1;
|
|
} else {
|
|
if (state->compressed_attrs & (1 << i)) {
|
|
mstring_append_fmt(header,
|
|
"layout(location = %d) in int v%d_cmp;\n", i, i);
|
|
} else if (state->swizzle_attrs & (1 << i)) {
|
|
mstring_append_fmt(header, "layout(location = %d) in vec4 v%d_sw;\n",
|
|
i, i);
|
|
} else {
|
|
mstring_append_fmt(header, "layout(location = %d) in vec4 v%d;\n",
|
|
i, i);
|
|
}
|
|
}
|
|
}
|
|
mstring_append(header, "\n");
|
|
|
|
MString *body = mstring_from_str("void main() {\n");
|
|
|
|
for (i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
|
|
if (state->compressed_attrs & (1 << i)) {
|
|
mstring_append_fmt(
|
|
body, "vec4 v%d = decompress_11_11_10(v%d_cmp);\n", i, i);
|
|
}
|
|
|
|
if (state->swizzle_attrs & (1 << i)) {
|
|
mstring_append_fmt(body, "vec4 v%d = v%d_sw.bgra;\n", i, i);
|
|
}
|
|
|
|
}
|
|
|
|
if (state->fixed_function) {
|
|
pgraph_gen_vsh_ff_glsl(state, header, body, uniforms);
|
|
} else if (state->vertex_program) {
|
|
pgraph_gen_vsh_prog_glsl(VSH_VERSION_XVS,
|
|
(uint32_t *)state->program_data,
|
|
state->program_length,
|
|
state->vulkan, header, body);
|
|
} else {
|
|
assert(false);
|
|
}
|
|
|
|
|
|
/* Fog */
|
|
|
|
if (state->fog_enable) {
|
|
|
|
if (state->vertex_program) {
|
|
/* FIXME: Does foggen do something here? Let's do some tracking..
|
|
*
|
|
* "RollerCoaster Tycoon" has
|
|
* state->vertex_program = true; state->foggen == FOGGEN_PLANAR
|
|
* but expects oFog.x as fogdistance?! Writes oFog.xyzw = v0.z
|
|
*/
|
|
mstring_append(body, " float fogDistance = oFog.x;\n");
|
|
}
|
|
|
|
/* FIXME: Do this per pixel? */
|
|
|
|
switch (state->fog_mode) {
|
|
case FOG_MODE_LINEAR:
|
|
case FOG_MODE_LINEAR_ABS:
|
|
|
|
/* f = (end - d) / (end - start)
|
|
* fogParam.y = -1 / (end - start)
|
|
* fogParam.x = 1 - end * fogParam.y;
|
|
*/
|
|
|
|
mstring_append(body,
|
|
" if (isinf(fogDistance)) {\n"
|
|
" fogDistance = 0.0;\n"
|
|
" }\n"
|
|
);
|
|
mstring_append(body, " float fogFactor = fogParam.x + fogDistance * fogParam.y;\n");
|
|
mstring_append(body, " fogFactor -= 1.0;\n");
|
|
break;
|
|
case FOG_MODE_EXP:
|
|
mstring_append(body,
|
|
" if (isinf(fogDistance)) {\n"
|
|
" fogDistance = 0.0;\n"
|
|
" }\n"
|
|
);
|
|
/* fallthru */
|
|
case FOG_MODE_EXP_ABS:
|
|
|
|
/* f = 1 / (e^(d * density))
|
|
* fogParam.y = -density / (2 * ln(256))
|
|
* fogParam.x = 1.5
|
|
*/
|
|
|
|
mstring_append(body, " float fogFactor = fogParam.x + exp2(fogDistance * fogParam.y * 16.0);\n");
|
|
mstring_append(body, " fogFactor -= 1.5;\n");
|
|
break;
|
|
case FOG_MODE_EXP2:
|
|
case FOG_MODE_EXP2_ABS:
|
|
|
|
/* f = 1 / (e^((d * density)^2))
|
|
* fogParam.y = -density / (2 * sqrt(ln(256)))
|
|
* fogParam.x = 1.5
|
|
*/
|
|
|
|
mstring_append(body, " float fogFactor = fogParam.x + exp2(-fogDistance * fogDistance * fogParam.y * fogParam.y * 32.0);\n");
|
|
mstring_append(body, " fogFactor -= 1.5;\n");
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
/* Calculate absolute for the modes which need it */
|
|
switch (state->fog_mode) {
|
|
case FOG_MODE_LINEAR_ABS:
|
|
case FOG_MODE_EXP_ABS:
|
|
case FOG_MODE_EXP2_ABS:
|
|
mstring_append(body, " fogFactor = abs(fogFactor);\n");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
mstring_append(body, " oFog.xyzw = vec4(fogFactor);\n");
|
|
} else {
|
|
/* FIXME: Is the fog still calculated / passed somehow?!
|
|
*/
|
|
mstring_append(body, " oFog.xyzw = vec4(1.0);\n");
|
|
}
|
|
|
|
/* Set outputs */
|
|
mstring_append(body, "\n"
|
|
" vtxD0 = clamp(oD0, 0.0, 1.0);\n"
|
|
" vtxB0 = clamp(oB0, 0.0, 1.0);\n"
|
|
" vtxFog = oFog.x;\n"
|
|
" vtxT0 = oT0;\n"
|
|
" vtxT1 = oT1;\n"
|
|
" vtxT2 = oT2;\n"
|
|
" vtxT3 = oT3;\n"
|
|
" gl_PointSize = oPts.x;\n"
|
|
);
|
|
|
|
if (state->specular_enable) {
|
|
mstring_append(body,
|
|
" vtxD1 = clamp(oD1, 0.0, 1.0);\n"
|
|
" vtxB1 = clamp(oB1, 0.0, 1.0);\n"
|
|
);
|
|
|
|
if (state->ignore_specular_alpha) {
|
|
mstring_append(body,
|
|
" vtxD1.w = 1.0;\n"
|
|
" vtxB1.w = 1.0;\n"
|
|
);
|
|
}
|
|
} else {
|
|
mstring_append(body,
|
|
" vtxD1 = vec4(0.0, 0.0, 0.0, 1.0);\n"
|
|
" vtxB1 = vec4(0.0, 0.0, 0.0, 1.0);\n"
|
|
);
|
|
}
|
|
|
|
if (state->vulkan) {
|
|
mstring_append(body,
|
|
" gl_Position = oPos;\n"
|
|
);
|
|
} else {
|
|
mstring_append(body,
|
|
" gl_Position = vec4(oPos.x, oPos.y, 2.0*oPos.z - oPos.w, oPos.w);\n"
|
|
);
|
|
}
|
|
|
|
mstring_append(body, "}\n");
|
|
|
|
/* Return combined header + source */
|
|
if (state->vulkan) {
|
|
// FIXME: Optimize uniforms
|
|
if (num_uniform_attrs > 0) {
|
|
if (state->use_push_constants_for_uniform_attrs) {
|
|
mstring_append_fmt(output,
|
|
"layout(push_constant) uniform PushConstants {\n"
|
|
" vec4 inlineValue[%d];\n"
|
|
"};\n\n", num_uniform_attrs);
|
|
} else {
|
|
mstring_append_fmt(uniforms, " vec4 inlineValue[%d];\n",
|
|
num_uniform_attrs);
|
|
}
|
|
}
|
|
mstring_append_fmt(
|
|
output,
|
|
"layout(binding = %d, std140) uniform VshUniforms {\n"
|
|
"%s"
|
|
"};\n\n",
|
|
VSH_UBO_BINDING, mstring_get_str(uniforms));
|
|
} else {
|
|
mstring_append(
|
|
output, mstring_get_str(uniforms));
|
|
}
|
|
|
|
mstring_append(output, mstring_get_str(header));
|
|
mstring_unref(header);
|
|
|
|
mstring_append(output, mstring_get_str(body));
|
|
mstring_unref(body);
|
|
return output;
|
|
}
|