mirror of https://github.com/xemu-project/xemu.git
599 lines
23 KiB
C
599 lines
23 KiB
C
/*
|
|
* Geforce NV2A PGRAPH GLSL Shader Generator
|
|
*
|
|
* Copyright (c) 2015 espes
|
|
* Copyright (c) 2015 Jannik Vogel
|
|
* Copyright (c) 2020-2025 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/pgraph.h"
|
|
#include "vsh.h"
|
|
#include "vsh-ff.h"
|
|
#include "vsh-prog.h"
|
|
|
|
DEF_UNIFORM_INFO_ARR(VshUniform, VSH_UNIFORM_DECL_X)
|
|
|
|
static void set_fixed_function_vsh_state(PGRAPHState *pg,
|
|
FixedFunctionVshState *state)
|
|
{
|
|
state->skinning = (enum VshSkinning)GET_MASK(
|
|
pgraph_reg_r(pg, NV_PGRAPH_CSV0_D), NV_PGRAPH_CSV0_D_SKIN);
|
|
state->normalization = pgraph_reg_r(pg, NV_PGRAPH_CSV0_C) &
|
|
NV_PGRAPH_CSV0_C_NORMALIZATION_ENABLE;
|
|
state->local_eye =
|
|
GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CSV0_C), NV_PGRAPH_CSV0_C_LOCALEYE);
|
|
|
|
state->emission_src = (enum MaterialColorSource)GET_MASK(
|
|
pgraph_reg_r(pg, NV_PGRAPH_CSV0_C), NV_PGRAPH_CSV0_C_EMISSION);
|
|
state->ambient_src = (enum MaterialColorSource)GET_MASK(
|
|
pgraph_reg_r(pg, NV_PGRAPH_CSV0_C), NV_PGRAPH_CSV0_C_AMBIENT);
|
|
state->diffuse_src = (enum MaterialColorSource)GET_MASK(
|
|
pgraph_reg_r(pg, NV_PGRAPH_CSV0_C), NV_PGRAPH_CSV0_C_DIFFUSE);
|
|
state->specular_src = (enum MaterialColorSource)GET_MASK(
|
|
pgraph_reg_r(pg, NV_PGRAPH_CSV0_C), NV_PGRAPH_CSV0_C_SPECULAR);
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
state->texture_matrix_enable[i] = pg->texture_matrix_enable[i];
|
|
}
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
unsigned int reg = (i < 2) ? NV_PGRAPH_CSV1_A : NV_PGRAPH_CSV1_B;
|
|
for (int j = 0; j < 4; j++) {
|
|
unsigned int masks[] = {
|
|
(i % 2) ? NV_PGRAPH_CSV1_A_T1_S : NV_PGRAPH_CSV1_A_T0_S,
|
|
(i % 2) ? NV_PGRAPH_CSV1_A_T1_T : NV_PGRAPH_CSV1_A_T0_T,
|
|
(i % 2) ? NV_PGRAPH_CSV1_A_T1_R : NV_PGRAPH_CSV1_A_T0_R,
|
|
(i % 2) ? NV_PGRAPH_CSV1_A_T1_Q : NV_PGRAPH_CSV1_A_T0_Q
|
|
};
|
|
state->texgen[i][j] =
|
|
(enum VshTexgen)GET_MASK(pgraph_reg_r(pg, reg), masks[j]);
|
|
}
|
|
}
|
|
|
|
state->lighting =
|
|
GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CSV0_C), NV_PGRAPH_CSV0_C_LIGHTING);
|
|
if (state->lighting) {
|
|
for (int i = 0; i < NV2A_MAX_LIGHTS; i++) {
|
|
state->light[i] =
|
|
(enum VshLight)GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CSV0_D),
|
|
NV_PGRAPH_CSV0_D_LIGHT0 << (i * 2));
|
|
}
|
|
}
|
|
|
|
if (pgraph_reg_r(pg, NV_PGRAPH_CONTROL_3) & NV_PGRAPH_CONTROL_3_FOGENABLE) {
|
|
state->foggen = (enum VshFoggen)GET_MASK(
|
|
pgraph_reg_r(pg, NV_PGRAPH_CSV0_D), NV_PGRAPH_CSV0_D_FOGGENMODE);
|
|
}
|
|
}
|
|
|
|
static void set_programmable_vsh_state(PGRAPHState *pg,
|
|
ProgrammableVshState *prog)
|
|
{
|
|
int program_start = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CSV0_C),
|
|
NV_PGRAPH_CSV0_C_CHEOPS_PROGRAM_START);
|
|
|
|
// copy in vertex program tokens
|
|
prog->program_length = 0;
|
|
for (int i = program_start; i < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH; i++) {
|
|
uint32_t *cur_token = (uint32_t *)&pg->program_data[i];
|
|
memcpy(&prog->program_data[prog->program_length], cur_token,
|
|
VSH_TOKEN_SIZE * sizeof(uint32_t));
|
|
prog->program_length++;
|
|
|
|
if (vsh_get_field(cur_token, FLD_FINAL)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void pgraph_set_vsh_state(PGRAPHState *pg, VshState *vsh)
|
|
{
|
|
bool vertex_program = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CSV0_D),
|
|
NV_PGRAPH_CSV0_D_MODE) == 2;
|
|
|
|
bool fixed_function = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CSV0_D),
|
|
NV_PGRAPH_CSV0_D_MODE) == 0;
|
|
|
|
assert(vertex_program || fixed_function);
|
|
|
|
vsh->surface_scale_factor = pg->surface_scale_factor; // FIXME
|
|
|
|
vsh->compressed_attrs = pg->compressed_attrs;
|
|
vsh->uniform_attrs = pg->uniform_attrs;
|
|
vsh->swizzle_attrs = pg->swizzle_attrs;
|
|
|
|
vsh->specular_enable = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CSV0_C),
|
|
NV_PGRAPH_CSV0_C_SPECULAR_ENABLE);
|
|
vsh->separate_specular = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CSV0_C),
|
|
NV_PGRAPH_CSV0_C_SEPARATE_SPECULAR);
|
|
vsh->ignore_specular_alpha =
|
|
!GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CSV0_C),
|
|
NV_PGRAPH_CSV0_C_ALPHA_FROM_MATERIAL_SPECULAR);
|
|
vsh->specular_power = pg->specular_power;
|
|
vsh->specular_power_back = pg->specular_power_back;
|
|
|
|
vsh->z_perspective = pgraph_reg_r(pg, NV_PGRAPH_CONTROL_0) &
|
|
NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE;
|
|
|
|
vsh->point_params_enable = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CSV0_D),
|
|
NV_PGRAPH_CSV0_D_POINTPARAMSENABLE);
|
|
vsh->point_size = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_POINTSIZE),
|
|
NV097_SET_POINT_SIZE_V) /
|
|
8.0f;
|
|
if (vsh->point_params_enable) {
|
|
for (int i = 0; i < 8; i++) {
|
|
vsh->point_params[i] = pg->point_params[i];
|
|
}
|
|
}
|
|
|
|
vsh->smooth_shading = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_3),
|
|
NV_PGRAPH_CONTROL_3_SHADEMODE) ==
|
|
NV_PGRAPH_CONTROL_3_SHADEMODE_SMOOTH;
|
|
|
|
vsh->fog_enable =
|
|
pgraph_reg_r(pg, NV_PGRAPH_CONTROL_3) & NV_PGRAPH_CONTROL_3_FOGENABLE;
|
|
if (vsh->fog_enable) {
|
|
/*FIXME: Use CSV0_D? */
|
|
vsh->fog_mode =
|
|
(enum VshFogMode)GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_3),
|
|
NV_PGRAPH_CONTROL_3_FOG_MODE);
|
|
}
|
|
|
|
/* geometry shader stuff */
|
|
vsh->primitive_mode = (enum ShaderPrimitiveMode)pg->primitive_mode;
|
|
vsh->polygon_front_mode = (enum ShaderPolygonMode)GET_MASK(
|
|
pgraph_reg_r(pg, NV_PGRAPH_SETUPRASTER),
|
|
NV_PGRAPH_SETUPRASTER_FRONTFACEMODE);
|
|
vsh->polygon_back_mode = (enum ShaderPolygonMode)GET_MASK(
|
|
pgraph_reg_r(pg, NV_PGRAPH_SETUPRASTER),
|
|
NV_PGRAPH_SETUPRASTER_BACKFACEMODE);
|
|
|
|
vsh->is_fixed_function = fixed_function;
|
|
if (fixed_function) {
|
|
set_fixed_function_vsh_state(pg, &vsh->fixed_function);
|
|
} else {
|
|
set_programmable_vsh_state(pg, &vsh->programmable);
|
|
}
|
|
}
|
|
|
|
MString *pgraph_gen_vsh_glsl(const VshState *state,
|
|
GenVshGlslOptions opts)
|
|
{
|
|
MString *output =
|
|
mstring_from_fmt("#version %d\n\n", opts.vulkan ? 450 : 400);
|
|
|
|
MString *header = mstring_from_str("");
|
|
|
|
MString *uniforms = mstring_from_str("");
|
|
const char *u = opts.vulkan ? "" : "uniform ";
|
|
for (int i = 0; i < ARRAY_SIZE(VshUniformInfo); i++) {
|
|
const UniformInfo *info = &VshUniformInfo[i];
|
|
const char *type_str = uniform_element_type_to_str[info->type];
|
|
if (i == VshUniform_inlineValue &&
|
|
opts.use_push_constants_for_uniform_attrs) {
|
|
continue;
|
|
}
|
|
if (info->count == 1) {
|
|
mstring_append_fmt(uniforms, "%s%s %s;\n", u, type_str,
|
|
info->name);
|
|
} else {
|
|
mstring_append_fmt(uniforms, "%s%s %s[%zd];\n", u, type_str,
|
|
info->name, info->count);
|
|
}
|
|
}
|
|
|
|
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"
|
|
"#define FLOAT_MAX uintBitsToFloat(0x7F7FFFFFu)\n"
|
|
"\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"
|
|
"\n"
|
|
"vec4 NaNToOne(vec4 src) {\n"
|
|
" return mix(src, vec4(1.0), isnan(src));\n"
|
|
"}\n"
|
|
"\n"
|
|
// Xbox NV2A rasterizer appears to have 4 bit precision fixed-point
|
|
// fractional part and to convert floating-point coordinates by
|
|
// by truncating (not flooring).
|
|
"vec2 roundScreenCoords(vec2 pos) {\n"
|
|
" return trunc(pos * 16.0f) / 16.0f;\n"
|
|
"}\n");
|
|
|
|
pgraph_get_glsl_vtx_header(header, opts.vulkan, state->smooth_shading,
|
|
false, opts.prefix_outputs, false);
|
|
|
|
if (opts.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 (int 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 (int 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->is_fixed_function) {
|
|
pgraph_gen_vsh_ff_glsl(state, header, body);
|
|
} else {
|
|
pgraph_gen_vsh_prog_glsl(
|
|
VSH_VERSION_XVS, (uint32_t *)state->programmable.program_data,
|
|
state->programmable.program_length, header, body);
|
|
}
|
|
|
|
if (!state->fog_enable) {
|
|
/* FIXME: Is the fog still calculated / passed somehow?! */
|
|
mstring_append(body, " oFog = vec4(1.0);\n");
|
|
} else {
|
|
if (!state->is_fixed_function) {
|
|
/* 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;
|
|
}
|
|
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;
|
|
}
|
|
|
|
/* Fog is clamped to min/max normal float values here to match HW
|
|
* interpolation. It is then clamped to [0,1] in the pixel shader.
|
|
*/
|
|
// clang-format off
|
|
mstring_append(body,
|
|
" oFog = clamp(NaNToOne(vec4(fogFactor)), -FLOAT_MAX, FLOAT_MAX);\n");
|
|
// clang-format on
|
|
}
|
|
|
|
mstring_append(body, "\n"
|
|
" vtxD0 = clamp(NaNToOne(oD0), 0.0, 1.0);\n"
|
|
" vtxB0 = clamp(NaNToOne(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(NaNToOne(oD1), 0.0, 1.0);\n"
|
|
" vtxB1 = clamp(NaNToOne(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 (opts.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 (opts.vulkan) {
|
|
// FIXME: Optimize uniforms
|
|
if (num_uniform_attrs > 0) {
|
|
if (opts.use_push_constants_for_uniform_attrs) {
|
|
mstring_append_fmt(output,
|
|
"layout(push_constant) uniform PushConstants {\n"
|
|
" vec4 inlineValue[%d];\n"
|
|
"};\n\n", NV2A_VERTEXSHADER_ATTRIBUTES);
|
|
} else {
|
|
mstring_append_fmt(uniforms, " vec4 inlineValue[%d];\n",
|
|
NV2A_VERTEXSHADER_ATTRIBUTES);
|
|
}
|
|
}
|
|
mstring_append_fmt(
|
|
output,
|
|
"layout(binding = %d, std140) uniform VshUniforms {\n"
|
|
"%s"
|
|
"};\n\n",
|
|
opts.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;
|
|
}
|
|
|
|
void pgraph_set_vsh_uniform_values(PGRAPHState *pg, const VshState *state,
|
|
const VshUniformLocs locs,
|
|
VshUniformValues *values)
|
|
{
|
|
if (locs[VshUniform_c] != -1) {
|
|
QEMU_BUILD_BUG_MSG(sizeof(values->c) != sizeof(pg->vsh_constants),
|
|
"Uniform value size inconsistency");
|
|
memcpy(values->c, pg->vsh_constants, sizeof(pg->vsh_constants));
|
|
}
|
|
|
|
if (locs[VshUniform_clipRange] != -1) {
|
|
float zmax;
|
|
switch (pg->surface_shape.zeta_format) {
|
|
case NV097_SET_SURFACE_FORMAT_ZETA_Z16:
|
|
zmax = pg->surface_shape.z_format ? f16_max : (float)0xFFFF;
|
|
break;
|
|
case NV097_SET_SURFACE_FORMAT_ZETA_Z24S8:
|
|
zmax = pg->surface_shape.z_format ? f24_max : (float)0xFFFFFF;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
uint32_t zclip_min = pgraph_reg_r(pg, NV_PGRAPH_ZCLIPMIN);
|
|
uint32_t zclip_max = pgraph_reg_r(pg, NV_PGRAPH_ZCLIPMAX);
|
|
|
|
values->clipRange[0][0] = 0;
|
|
values->clipRange[0][1] = zmax;
|
|
values->clipRange[0][2] = *(float *)&zclip_min;
|
|
values->clipRange[0][3] = *(float *)&zclip_max;
|
|
}
|
|
|
|
if (locs[VshUniform_fogParam] != -1) {
|
|
uint32_t param_0 = pgraph_reg_r(pg, NV_PGRAPH_FOGPARAM0);
|
|
uint32_t param_1 = pgraph_reg_r(pg, NV_PGRAPH_FOGPARAM1);
|
|
values->fogParam[0][0] = *(float *)¶m_0;
|
|
values->fogParam[0][1] = *(float *)¶m_1;
|
|
}
|
|
|
|
if (locs[VshUniform_pointParams] != -1) {
|
|
QEMU_BUILD_BUG_MSG(sizeof(values->pointParams) !=
|
|
sizeof(pg->point_params),
|
|
"Uniform value size inconsistency");
|
|
memcpy(values->pointParams, pg->point_params, sizeof(pg->point_params));
|
|
}
|
|
|
|
if (locs[VshUniform_material_alpha] != -1) {
|
|
values->material_alpha[0] = pg->material_alpha;
|
|
}
|
|
|
|
if (locs[VshUniform_inlineValue] != -1) {
|
|
pgraph_get_inline_values(pg, state->uniform_attrs, values->inlineValue,
|
|
NULL);
|
|
}
|
|
|
|
if (locs[VshUniform_surfaceSize] != -1) {
|
|
unsigned int aa_width = 1, aa_height = 1;
|
|
pgraph_apply_anti_aliasing_factor(pg, &aa_width, &aa_height);
|
|
float width = (float)pg->surface_binding_dim.width / aa_width;
|
|
float height = (float)pg->surface_binding_dim.height / aa_height;
|
|
values->surfaceSize[0][0] = width;
|
|
values->surfaceSize[0][1] = height;
|
|
}
|
|
|
|
if (state->is_fixed_function) {
|
|
if (locs[VshUniform_ltctxa] != -1) {
|
|
QEMU_BUILD_BUG_MSG(sizeof(values->ltctxa) != sizeof(pg->ltctxa),
|
|
"Uniform value size inconsistency");
|
|
memcpy(values->ltctxa, pg->ltctxa, sizeof(pg->ltctxa));
|
|
}
|
|
|
|
if (locs[VshUniform_ltctxb] != -1) {
|
|
QEMU_BUILD_BUG_MSG(sizeof(values->ltctxb) != sizeof(pg->ltctxb),
|
|
"Uniform value size inconsistency");
|
|
memcpy(values->ltctxb, pg->ltctxb, sizeof(pg->ltctxb));
|
|
}
|
|
|
|
if (locs[VshUniform_ltc1] != -1) {
|
|
QEMU_BUILD_BUG_MSG(sizeof(values->ltc1) != sizeof(pg->ltc1),
|
|
"Uniform value size inconsistency");
|
|
memcpy(values->ltc1, pg->ltc1, sizeof(pg->ltc1));
|
|
}
|
|
|
|
if (locs[VshUniform_lightInfiniteHalfVector] != -1) {
|
|
QEMU_BUILD_BUG_MSG(sizeof(values->lightInfiniteHalfVector) !=
|
|
sizeof(pg->light_infinite_half_vector),
|
|
"Uniform value size inconsistency");
|
|
memcpy(values->lightInfiniteHalfVector,
|
|
pg->light_infinite_half_vector,
|
|
sizeof(pg->light_infinite_half_vector));
|
|
}
|
|
|
|
if (locs[VshUniform_lightInfiniteDirection] != -1) {
|
|
QEMU_BUILD_BUG_MSG(sizeof(values->lightInfiniteDirection) !=
|
|
sizeof(pg->light_infinite_direction),
|
|
"Uniform value size inconsistency");
|
|
memcpy(values->lightInfiniteDirection, pg->light_infinite_direction,
|
|
sizeof(pg->light_infinite_direction));
|
|
}
|
|
|
|
if (locs[VshUniform_lightLocalPosition] != -1) {
|
|
QEMU_BUILD_BUG_MSG(sizeof(values->lightLocalPosition) !=
|
|
sizeof(pg->light_local_position),
|
|
"Uniform value size inconsistency");
|
|
memcpy(values->lightLocalPosition, pg->light_local_position,
|
|
sizeof(pg->light_local_position));
|
|
}
|
|
|
|
if (locs[VshUniform_lightLocalAttenuation] != -1) {
|
|
QEMU_BUILD_BUG_MSG(sizeof(values->lightLocalAttenuation) !=
|
|
sizeof(pg->light_local_attenuation),
|
|
"Uniform value size inconsistency");
|
|
memcpy(values->lightLocalAttenuation, pg->light_local_attenuation,
|
|
sizeof(pg->light_local_attenuation));
|
|
}
|
|
|
|
if (locs[VshUniform_specularPower] != -1) {
|
|
values->specularPower[0] = pg->specular_power;
|
|
}
|
|
}
|
|
}
|