nv2a: Simplify shader uniform declaration and update

This patch moves uniform declaration into {vsh, psh}.h headers, using
macros to generate accessory definitions. Mapping of PGRAPH state to
uniform values is factored out of parallel paths in GL/Vk renderers into
common renderer-agnostic helper functions, with renderer-specific
uniform value update paths being automated.
This commit is contained in:
Matt Borgerson 2025-06-28 00:08:45 -07:00 committed by mborgerson
parent 18872f2eb9
commit c88bac1706
14 changed files with 635 additions and 930 deletions

View File

@ -34,6 +34,8 @@
#include "hw/xbox/nv2a/pgraph/surface.h"
#include "hw/xbox/nv2a/pgraph/texture.h"
#include "hw/xbox/nv2a/pgraph/shaders.h"
#include "hw/xbox/nv2a/pgraph/glsl/vsh.h"
#include "hw/xbox/nv2a/pgraph/glsl/psh.h"
#include "gloffscreen.h"
#include "constants.h"
@ -96,39 +98,9 @@ typedef struct ShaderBinding {
GLuint gl_program;
GLenum gl_primitive_mode;
uint32_t vsh_constants[NV2A_VERTEXSHADER_CONSTANTS][4];
struct {
struct {
GLint alpha_ref;
GLint bump_mat[NV2A_MAX_TEXTURES];
GLint bump_offset[NV2A_MAX_TEXTURES];
GLint bump_scale[NV2A_MAX_TEXTURES];
GLint clip_range;
GLint clip_region[8];
GLint color_key[4];
GLint color_key_mask[4];
GLint depth_offset;
GLint fog_color;
GLint psh_constant[9][2];
GLint surface_size;
GLint tex_scale[NV2A_MAX_TEXTURES];
} psh;
struct {
GLint fog_param;
GLint light_infinite_direction[NV2A_MAX_LIGHTS];
GLint light_infinite_half_vector[NV2A_MAX_LIGHTS];
GLint light_local_attenuation[NV2A_MAX_LIGHTS];
GLint light_local_position[NV2A_MAX_LIGHTS];
GLint ltc1[NV2A_LTC1_COUNT];
GLint ltctxa[NV2A_LTCTXA_COUNT];
GLint ltctxb[NV2A_LTCTXB_COUNT];
GLint material_alpha;
GLint point_params[8];
GLint vsh_constant[NV2A_VERTEXSHADER_CONSTANTS];
int specular_power;
} vsh;
PshUniformLocs psh;
VshUniformLocs vsh;
} uniform_locs;
} ShaderBinding;

View File

@ -115,112 +115,22 @@ static void update_shader_constant_locations(ShaderBinding *binding)
}
}
/* lookup fragment shader uniforms */
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 2; j++) {
snprintf(tmp, sizeof(tmp), "c%d_%d", j, i);
binding->uniform_locs.psh.psh_constant[i][j] =
glGetUniformLocation(binding->gl_program, tmp);
for (int i = 0; i < ARRAY_SIZE(binding->uniform_locs.vsh); i++) {
const char *name = VshUniformInfo[i].name;
if (VshUniformInfo[i].count > 1) {
snprintf(tmp, sizeof(tmp), "%s[0]", name);
name = tmp;
}
}
binding->uniform_locs.psh.alpha_ref =
glGetUniformLocation(binding->gl_program, "alphaRef");
for (int i = 1; i < NV2A_MAX_TEXTURES; i++) {
snprintf(tmp, sizeof(tmp), "bumpMat%d", i);
binding->uniform_locs.psh.bump_mat[i] =
glGetUniformLocation(binding->gl_program, tmp);
snprintf(tmp, sizeof(tmp), "bumpScale%d", i);
binding->uniform_locs.psh.bump_scale[i] =
glGetUniformLocation(binding->gl_program, tmp);
snprintf(tmp, sizeof(tmp), "bumpOffset%d", i);
binding->uniform_locs.psh.bump_offset[i] =
glGetUniformLocation(binding->gl_program, tmp);
binding->uniform_locs.vsh[i] = glGetUniformLocation(binding->gl_program, name);
}
for (int i = 0; i < NV2A_MAX_TEXTURES; i++) {
snprintf(tmp, sizeof(tmp), "texScale%d", i);
binding->uniform_locs.psh.tex_scale[i] =
glGetUniformLocation(binding->gl_program, tmp);
}
/* lookup vertex shader uniforms */
for (int i = 0; i < NV2A_VERTEXSHADER_CONSTANTS; i++) {
snprintf(tmp, sizeof(tmp), "c[%d]", i);
binding->uniform_locs.vsh.vsh_constant[i] =
glGetUniformLocation(binding->gl_program, tmp);
}
binding->uniform_locs.psh.surface_size =
glGetUniformLocation(binding->gl_program, "surfaceSize");
binding->uniform_locs.psh.clip_range =
glGetUniformLocation(binding->gl_program, "clipRange");
binding->uniform_locs.psh.depth_offset =
glGetUniformLocation(binding->gl_program, "depthOffset");
binding->uniform_locs.psh.fog_color =
glGetUniformLocation(binding->gl_program, "fogColor");
binding->uniform_locs.vsh.fog_param =
glGetUniformLocation(binding->gl_program, "fogParam");
for (int i = 0; i < NV2A_LTCTXA_COUNT; i++) {
snprintf(tmp, sizeof(tmp), "ltctxa[%d]", i);
binding->uniform_locs.vsh.ltctxa[i] =
glGetUniformLocation(binding->gl_program, tmp);
}
for (int i = 0; i < NV2A_LTCTXB_COUNT; i++) {
snprintf(tmp, sizeof(tmp), "ltctxb[%d]", i);
binding->uniform_locs.vsh.ltctxb[i] =
glGetUniformLocation(binding->gl_program, tmp);
}
for (int i = 0; i < NV2A_LTC1_COUNT; i++) {
snprintf(tmp, sizeof(tmp), "ltc1[%d]", i);
binding->uniform_locs.vsh.ltc1[i] =
glGetUniformLocation(binding->gl_program, tmp);
}
for (int i = 0; i < NV2A_MAX_LIGHTS; i++) {
snprintf(tmp, sizeof(tmp), "lightInfiniteHalfVector%d", i);
binding->uniform_locs.vsh.light_infinite_half_vector[i] =
glGetUniformLocation(binding->gl_program, tmp);
snprintf(tmp, sizeof(tmp), "lightInfiniteDirection%d", i);
binding->uniform_locs.vsh.light_infinite_direction[i] =
glGetUniformLocation(binding->gl_program, tmp);
snprintf(tmp, sizeof(tmp), "lightLocalPosition%d", i);
binding->uniform_locs.vsh.light_local_position[i] =
glGetUniformLocation(binding->gl_program, tmp);
snprintf(tmp, sizeof(tmp), "lightLocalAttenuation%d", i);
binding->uniform_locs.vsh.light_local_attenuation[i] =
glGetUniformLocation(binding->gl_program, tmp);
}
for (int i = 0; i < 8; i++) {
snprintf(tmp, sizeof(tmp), "clipRegion[%d]", i);
binding->uniform_locs.psh.clip_region[i] =
glGetUniformLocation(binding->gl_program, tmp);
}
for (int i = 0; i < 8; ++i) {
snprintf(tmp, sizeof(tmp), "pointParams[%d]", i);
binding->uniform_locs.vsh.point_params[i] =
glGetUniformLocation(binding->gl_program, tmp);
}
if (binding->state.vsh.is_fixed_function) {
binding->uniform_locs.vsh.material_alpha =
glGetUniformLocation(binding->gl_program, "material_alpha");
binding->uniform_locs.vsh.specular_power =
glGetUniformLocation(binding->gl_program, "specularPower");
} else {
binding->uniform_locs.vsh.material_alpha = -1;
binding->uniform_locs.vsh.specular_power = -1;
}
for (int i = 0; i < 4; ++i) {
snprintf(tmp, sizeof(tmp), "colorKey[%d]", i);
binding->uniform_locs.psh.color_key[i] =
glGetUniformLocation(binding->gl_program, tmp);
snprintf(tmp, sizeof(tmp), "colorKeyMask[%d]", i);
binding->uniform_locs.psh.color_key_mask[i] =
glGetUniformLocation(binding->gl_program, tmp);
for (int i = 0; i < ARRAY_SIZE(binding->uniform_locs.psh); i++) {
const char *name = PshUniformInfo[i].name;
if (PshUniformInfo[i].count > 1) {
snprintf(tmp, sizeof(tmp), "%s[0]", name);
name = tmp;
}
binding->uniform_locs.psh[i] = glGetUniformLocation(binding->gl_program, name);
}
}
@ -723,270 +633,72 @@ void pgraph_gl_shader_cache_to_disk(ShaderBinding *binding)
qemu_thread_create(binding->save_thread, name, shader_write_to_disk, binding, QEMU_THREAD_JOINABLE);
}
static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding,
bool binding_changed)
static void apply_uniform_updates(const UniformInfo *info, int *locs,
void *values, size_t count)
{
for (int i = 0; i < count; i++) {
if (locs[i] == -1) {
continue;
}
void *value = (char*)values + info[i].val_offs;
switch (info[i].type) {
case UniformElementType_uint:
glUniform1uiv(locs[i], info[i].count, value);
break;
case UniformElementType_int:
glUniform1iv(locs[i], info[i].count, value);
break;
case UniformElementType_ivec4:
glUniform4iv(locs[i], info[i].count, value);
break;
case UniformElementType_float:
glUniform1fv(locs[i], info[i].count, value);
break;
case UniformElementType_vec2:
glUniform2fv(locs[i], info[i].count, value);
break;
case UniformElementType_vec3:
glUniform3fv(locs[i], info[i].count, value);
break;
case UniformElementType_vec4:
glUniform4fv(locs[i], info[i].count, value);
break;
case UniformElementType_mat2:
glUniformMatrix2fv(locs[i], info[i].count, GL_FALSE, value);
break;
default:
g_assert_not_reached();
}
}
assert(glGetError() == GL_NO_ERROR);
}
// FIXME: Dirty tracking
// FIXME: Consider UBO to align with VK renderer
static void update_shader_uniforms(PGRAPHState *pg, ShaderBinding *binding)
{
PGRAPHGLState *r = pg->gl_renderer_state;
ShaderState *state = &binding->state;
int i, j;
/* update combiner constants */
for (i = 0; i < 9; i++) {
uint32_t constant[2];
if (i == 8) {
/* final combiner */
constant[0] = pgraph_reg_r(pg, NV_PGRAPH_SPECFOGFACTOR0);
constant[1] = pgraph_reg_r(pg, NV_PGRAPH_SPECFOGFACTOR1);
} else {
constant[0] = pgraph_reg_r(pg, NV_PGRAPH_COMBINEFACTOR0 + i * 4);
constant[1] = pgraph_reg_r(pg, NV_PGRAPH_COMBINEFACTOR1 + i * 4);
}
VshUniformValues vsh_values;
pgraph_set_vsh_uniform_values(pg, &binding->state.vsh,
binding->uniform_locs.vsh, &vsh_values);
apply_uniform_updates(VshUniformInfo, binding->uniform_locs.vsh,
&vsh_values, VshUniform__COUNT);
for (j = 0; j < 2; j++) {
GLint loc = binding->uniform_locs.psh.psh_constant[i][j];
if (loc != -1) {
float value[4];
pgraph_argb_pack32_to_rgba_float(constant[j], value);
glUniform4fv(loc, 1, value);
}
PshUniformValues psh_values;
pgraph_set_psh_uniform_values(pg, binding->uniform_locs.psh, &psh_values);
for (int i = 0; i < 4; i++) {
if (r->texture_binding[i] != NULL) {
float scale = r->texture_binding[i]->scale;
psh_values.texScale[i] = scale;
}
}
if (binding->uniform_locs.psh.alpha_ref != -1) {
int alpha_ref = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_0),
NV_PGRAPH_CONTROL_0_ALPHAREF);
glUniform1i(binding->uniform_locs.psh.alpha_ref, alpha_ref);
}
/* For each texture stage */
for (i = 0; i < NV2A_MAX_TEXTURES; i++) {
GLint loc;
/* Bump luminance only during stages 1 - 3 */
if (i > 0) {
loc = binding->uniform_locs.psh.bump_mat[i];
if (loc != -1) {
uint32_t m_u32[4];
m_u32[0] = pgraph_reg_r(pg, NV_PGRAPH_BUMPMAT00 + 4 * (i - 1));
m_u32[1] = pgraph_reg_r(pg, NV_PGRAPH_BUMPMAT01 + 4 * (i - 1));
m_u32[2] = pgraph_reg_r(pg, NV_PGRAPH_BUMPMAT10 + 4 * (i - 1));
m_u32[3] = pgraph_reg_r(pg, NV_PGRAPH_BUMPMAT11 + 4 * (i - 1));
float m[4];
m[0] = *(float *)&m_u32[0];
m[1] = *(float *)&m_u32[1];
m[2] = *(float *)&m_u32[2];
m[3] = *(float *)&m_u32[3];
glUniformMatrix2fv(loc, 1, GL_FALSE, m);
}
loc = binding->uniform_locs.psh.bump_scale[i];
if (loc != -1) {
uint32_t v =
pgraph_reg_r(pg, NV_PGRAPH_BUMPSCALE1 + (i - 1) * 4);
glUniform1f(loc, *(float *)&v);
}
loc = binding->uniform_locs.psh.bump_offset[i];
if (loc != -1) {
uint32_t v =
pgraph_reg_r(pg, NV_PGRAPH_BUMPOFFSET1 + (i - 1) * 4);
glUniform1f(loc, *(float *)&v);
}
}
loc = r->shader_binding->uniform_locs.psh.tex_scale[i];
if (loc != -1) {
assert(r->texture_binding[i] != NULL);
glUniform1f(loc, (float)r->texture_binding[i]->scale);
}
if (binding->uniform_locs.psh.color_key[i] != -1) {
glUniform1ui(binding->uniform_locs.psh.color_key[i],
pgraph_reg_r(pg, NV_PGRAPH_COLORKEYCOLOR0 + i * 4));
}
if (binding->uniform_locs.psh.color_key_mask[i] != -1) {
glUniform1ui(binding->uniform_locs.psh.color_key_mask[i],
pgraph_get_color_key_mask_for_texture(pg, i));
}
}
if (binding->uniform_locs.psh.fog_color != -1) {
uint32_t fog_color = pgraph_reg_r(pg, NV_PGRAPH_FOGCOLOR);
glUniform4f(binding->uniform_locs.psh.fog_color,
GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_RED) / 255.0,
GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_GREEN) / 255.0,
GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_BLUE) / 255.0,
GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_ALPHA) / 255.0);
}
if (binding->uniform_locs.vsh.fog_param != -1) {
uint32_t v[2];
v[0] = pgraph_reg_r(pg, NV_PGRAPH_FOGPARAM0);
v[1] = pgraph_reg_r(pg, NV_PGRAPH_FOGPARAM1);
glUniform2f(binding->uniform_locs.vsh.fog_param, *(float *)&v[0],
*(float *)&v[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);
}
if (state->vsh.is_fixed_function) {
/* update lighting constants */
struct {
uint32_t *v;
bool *dirty;
GLint *locs;
size_t len;
} lighting_arrays[] = {
{ &pg->ltctxa[0][0], &pg->ltctxa_dirty[0],
binding->uniform_locs.vsh.ltctxa, NV2A_LTCTXA_COUNT },
{ &pg->ltctxb[0][0], &pg->ltctxb_dirty[0],
binding->uniform_locs.vsh.ltctxb, NV2A_LTCTXB_COUNT },
{ &pg->ltc1[0][0], &pg->ltc1_dirty[0],
binding->uniform_locs.vsh.ltc1, NV2A_LTC1_COUNT },
};
for (i = 0; i < ARRAY_SIZE(lighting_arrays); i++) {
uint32_t *lighting_v = lighting_arrays[i].v;
bool *lighting_dirty = lighting_arrays[i].dirty;
GLint *lighting_locs = lighting_arrays[i].locs;
size_t lighting_len = lighting_arrays[i].len;
for (j = 0; j < lighting_len; j++) {
if (!lighting_dirty[j] && !binding_changed)
continue;
GLint loc = lighting_locs[j];
if (loc != -1) {
glUniform4fv(loc, 1, (const GLfloat *)&lighting_v[j * 4]);
}
lighting_dirty[j] = false;
}
}
for (i = 0; i < NV2A_MAX_LIGHTS; i++) {
GLint loc;
loc = binding->uniform_locs.vsh.light_infinite_half_vector[i];
if (loc != -1) {
glUniform3fv(loc, 1, pg->light_infinite_half_vector[i]);
}
loc = binding->uniform_locs.vsh.light_infinite_direction[i];
if (loc != -1) {
glUniform3fv(loc, 1, pg->light_infinite_direction[i]);
}
loc = binding->uniform_locs.vsh.light_local_position[i];
if (loc != -1) {
glUniform3fv(loc, 1, pg->light_local_position[i]);
}
loc = binding->uniform_locs.vsh.light_local_attenuation[i];
if (loc != -1) {
glUniform3fv(loc, 1, pg->light_local_attenuation[i]);
}
}
if (binding->uniform_locs.vsh.specular_power != -1) {
glUniform1f(binding->uniform_locs.vsh.specular_power,
pg->specular_power);
}
}
/* update vertex program constants */
for (i = 0; i < NV2A_VERTEXSHADER_CONSTANTS; i++) {
if (!pg->vsh_constants_dirty[i] && !binding_changed)
continue;
GLint loc = binding->uniform_locs.vsh.vsh_constant[i];
if ((loc != -1) &&
memcmp(binding->vsh_constants[i], pg->vsh_constants[i],
sizeof(pg->vsh_constants[1]))) {
glUniform4fv(loc, 1, (const GLfloat *)pg->vsh_constants[i]);
memcpy(binding->vsh_constants[i], pg->vsh_constants[i],
sizeof(pg->vsh_constants[i]));
}
pg->vsh_constants_dirty[i] = false;
}
if (binding->uniform_locs.psh.surface_size != -1) {
unsigned int aa_width = 1, aa_height = 1;
pgraph_apply_anti_aliasing_factor(pg, &aa_width, &aa_height);
glUniform2f(binding->uniform_locs.psh.surface_size,
pg->surface_binding_dim.width / aa_width,
pg->surface_binding_dim.height / aa_height);
}
if (binding->uniform_locs.psh.clip_range != -1) {
uint32_t v[2];
v[0] = pgraph_reg_r(pg, NV_PGRAPH_ZCLIPMIN);
v[1] = pgraph_reg_r(pg, NV_PGRAPH_ZCLIPMAX);
float zclip_min = *(float *)&v[0];
float zclip_max = *(float *)&v[1];
glUniform4f(binding->uniform_locs.psh.clip_range, 0, zmax, zclip_min,
zclip_max);
}
if (binding->uniform_locs.psh.depth_offset != -1) {
float zbias = 0.0f;
if (pgraph_reg_r(pg, NV_PGRAPH_SETUPRASTER) &
(NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE |
NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE |
NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE)) {
uint32_t zbias_u32 = pgraph_reg_r(pg, NV_PGRAPH_ZOFFSETBIAS);
zbias = *(float *)&zbias_u32;
if (pgraph_reg_r(pg, NV_PGRAPH_ZOFFSETFACTOR) != 0 &&
(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_0) &
NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE)) {
/* TODO: emulate zfactor when z_perspective true, i.e.
* w-buffering. Perhaps calculate an additional offset based on
* triangle orientation in geometry shader and pass the result
* to fragment shader and add it to gl_FragDepth as well.
*/
NV2A_UNIMPLEMENTED("NV_PGRAPH_ZOFFSETFACTOR for w-buffering");
}
}
glUniform1f(binding->uniform_locs.psh.depth_offset, zbias);
}
/* Clipping regions */
unsigned int max_gl_width = pg->surface_binding_dim.width;
unsigned int max_gl_height = pg->surface_binding_dim.height;
pgraph_apply_scaling_factor(pg, &max_gl_width, &max_gl_height);
for (i = 0; i < 8; i++) {
uint32_t x = pgraph_reg_r(pg, NV_PGRAPH_WINDOWCLIPX0 + i * 4);
unsigned int x_min = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMIN);
unsigned int x_max = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMAX) + 1;
uint32_t y = pgraph_reg_r(pg, NV_PGRAPH_WINDOWCLIPY0 + i * 4);
unsigned int y_min = GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMIN);
unsigned int y_max = GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMAX) + 1;
pgraph_apply_anti_aliasing_factor(pg, &x_min, &y_min);
pgraph_apply_anti_aliasing_factor(pg, &x_max, &y_max);
pgraph_apply_scaling_factor(pg, &x_min, &y_min);
pgraph_apply_scaling_factor(pg, &x_max, &y_max);
glUniform4i(r->shader_binding->uniform_locs.psh.clip_region[i], x_min,
y_min, x_max, y_max);
}
for (i = 0; i < 8; ++i) {
GLint loc = binding->uniform_locs.vsh.point_params[i];
if (loc != -1) {
glUniform1f(loc, pg->point_params[i]);
}
}
if (binding->uniform_locs.vsh.material_alpha != -1) {
glUniform1f(binding->uniform_locs.vsh.material_alpha,
pg->material_alpha);
}
apply_uniform_updates(PshUniformInfo, binding->uniform_locs.psh,
&psh_values, PshUniform__COUNT);
}
static bool test_shaders_dirty(PGRAPHState *pg)
@ -1071,7 +783,7 @@ void pgraph_gl_bind_shaders(PGRAPHState *pg)
bool binding_changed = false;
if (r->shader_binding && !test_shaders_dirty(pg) && !pg->program_data_dirty) {
nv2a_profile_inc_counter(NV2A_PROF_SHADER_BIND_NOTDIRTY);
goto update_constants;
goto update_uniforms;
}
ShaderBinding *old_binding = r->shader_binding;
@ -1110,10 +822,10 @@ void pgraph_gl_bind_shaders(PGRAPHState *pg)
NV2A_GL_DGROUP_END();
update_constants:
update_uniforms:
assert(r->shader_binding);
assert(r->shader_binding->initialized);
shader_update_constants(pg, r->shader_binding, binding_changed);
update_shader_uniforms(pg, r->shader_binding);
}
GLuint pgraph_gl_compile_shader(const char *vs_src, const char *fs_src)

View File

@ -19,6 +19,11 @@
#include "common.h"
#define DECL_UNIFORM_ELEMENT_NAME(type) #type,
const char *uniform_element_type_to_str[] = {
UNIFORM_ELEMENT_TYPE_X(DECL_UNIFORM_ELEMENT_NAME)
};
MString *pgraph_get_glsl_vtx_header(MString *out, bool location, bool smooth, bool in, bool prefix, bool array)
{
const char *smooth_s = "";

View File

@ -22,8 +22,69 @@
#ifndef HW_NV2A_SHADERS_COMMON_H
#define HW_NV2A_SHADERS_COMMON_H
#include "qemu/osdep.h"
#include "qemu/mstring.h"
#include <stdbool.h>
typedef int ivec4[4];
typedef float mat2[2 * 2];
typedef unsigned int uint;
typedef float vec2[2];
typedef float vec3[3];
typedef float vec4[4];
#define UNIFORM_ELEMENT_TYPE_X(DECL) \
DECL(float) \
DECL(int) \
DECL(ivec4) \
DECL(mat2) \
DECL(uint) \
DECL(vec2) \
DECL(vec3) \
DECL(vec4)
enum UniformElementType {
#define DECL_UNIFORM_ELEMENT_TYPE(type) UniformElementType_##type,
UNIFORM_ELEMENT_TYPE_X(DECL_UNIFORM_ELEMENT_TYPE)
};
extern const char *uniform_element_type_to_str[];
#define DECL_UNIFORM_ENUM_VALUE(s, name, type, count) s##_##name,
#define DECL_UNIFORM_ENUM_TYPE(name, decls) \
enum name##Indices{ \
decls(name, DECL_UNIFORM_ENUM_VALUE) name##__COUNT, \
};
#define DECL_UNIFORM_LOC_STRUCT_TYPE(name, decls) \
typedef int name##Locs[name##__COUNT];
#define DECL_UNIFORM_VAL_STRUCT_FIELD(s, name, type, count) type name[count];
#define DECL_UNIFORM_VAL_STRUCT_TYPE(name, decls) \
typedef struct name##Values { \
decls(name, DECL_UNIFORM_VAL_STRUCT_FIELD) \
} name##Values;
typedef struct UniformInfo {
const char *name;
enum UniformElementType type;
size_t size;
size_t count;
size_t val_offs;
} UniformInfo;
#define DECL_UNIFORM_INFO_ITEM(s, name, type, count) \
{ #name, UniformElementType_##type, sizeof(type), count, \
offsetof(s##Values, name) },
#define DECL_UNIFORM_INFO_ARR(name, decls) \
extern const UniformInfo name##Info[];
#define DEF_UNIFORM_INFO_ARR(name, decls) \
const UniformInfo name##Info[] = { decls(name, DECL_UNIFORM_INFO_ITEM) };
#define DECL_UNIFORM_TYPES(name, decls) \
DECL_UNIFORM_ENUM_TYPE(name, decls) \
DECL_UNIFORM_LOC_STRUCT_TYPE(name, decls) \
DECL_UNIFORM_VAL_STRUCT_TYPE(name, decls) \
DECL_UNIFORM_INFO_ARR(name, decls)
#define GLSL_C(idx) "c[" stringify(idx) "]"
#define GLSL_LTCTXA(idx) "ltctxa[" stringify(idx) "]"

View File

@ -34,11 +34,12 @@
#include <stdbool.h>
#include <stdint.h>
#include "common.h"
#include "hw/xbox/nv2a/debug.h"
#include "hw/xbox/nv2a/pgraph/psh.h"
#include "hw/xbox/nv2a/pgraph/pgraph.h"
#include "psh.h"
DEF_UNIFORM_INFO_ARR(PshUniform, PSH_UNIFORM_DECL_X)
/*
* This implements translation of register combiners into glsl
* fragment shaders, but all terminology is in terms of Xbox DirectX
@ -755,8 +756,6 @@ static void define_colorkey_comparator(MString *preflight)
static MString* psh_convert(struct PixelShader *ps)
{
const char *u = ps->opts.vulkan ? "" : "uniform "; // FIXME: Remove
MString *preflight = mstring_new();
pgraph_get_glsl_vtx_header(preflight, ps->opts.vulkan,
ps->state.smooth_shading, true, false, false);
@ -772,25 +771,22 @@ static MString* psh_convert(struct PixelShader *ps)
"layout(location = 0) out vec4 fragColor;\n");
}
mstring_append_fmt(preflight,
"%sint alphaRef;\n"
"%svec4 fogColor;\n"
"%sivec4 clipRegion[8];\n"
"%svec4 clipRange;\n"
"%sfloat depthOffset;\n"
"%suint colorKey[4];\n"
"%suint colorKeyMask[4];\n",
u, u, u, u, u, u, u);
for (int i = 0; i < 4; i++) {
mstring_append_fmt(preflight, "%smat2 bumpMat%d;\n"
"%sfloat bumpScale%d;\n"
"%sfloat bumpOffset%d;\n"
"%sfloat texScale%d;\n",
u, i, u, i, u, i, u, i);
const char *u = ps->opts.vulkan ? "" : "uniform ";
for (int i = 0; i < ARRAY_SIZE(PshUniformInfo); i++) {
const UniformInfo *info = &PshUniformInfo[i];
const char *type_str = uniform_element_type_to_str[info->type];
if (info->count == 1) {
mstring_append_fmt(preflight, "%s%s %s;\n", u, type_str,
info->name);
} else {
mstring_append_fmt(preflight, "%s%s %s[%zd];\n", u, type_str,
info->name, info->count);
}
}
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 2; j++) {
mstring_append_fmt(preflight, "%svec4 c%d_%d;\n", u, j, i);
mstring_append_fmt(preflight, "#define c%d_%d consts[%d]\n", j, i, i*2+j);
}
}
@ -1044,7 +1040,7 @@ static MString* psh_convert(struct PixelShader *ps)
i, ps->input_tex[i], ps->input_tex[i]);
}
mstring_append_fmt(vars, "dsdt%d = bumpMat%d * dsdt%d;\n", i, i, i);
mstring_append_fmt(vars, "dsdt%d = bumpMat[%d] * dsdt%d;\n", i, i, i);
if (ps->state.dim_tex[i] == 2) {
mstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, %s(pT%d.xy + dsdt%d));\n",
@ -1070,7 +1066,7 @@ static MString* psh_convert(struct PixelShader *ps)
i, ps->input_tex[i], ps->input_tex[i], ps->input_tex[i]);
}
mstring_append_fmt(vars, "dsdtl%d.st = bumpMat%d * dsdtl%d.st;\n",
mstring_append_fmt(vars, "dsdtl%d.st = bumpMat[%d] * dsdtl%d.st;\n",
i, i, i);
if (ps->state.dim_tex[i] == 2) {
@ -1084,7 +1080,7 @@ static MString* psh_convert(struct PixelShader *ps)
assert(!"Unhandled texture dimensions");
}
mstring_append_fmt(vars, "t%d = t%d * (bumpScale%d * dsdtl%d.p + bumpOffset%d);\n",
mstring_append_fmt(vars, "t%d = t%d * (bumpScale[%d] * dsdtl%d.p + bumpOffset[%d]);\n",
i, i, i, i, i);
break;
case PS_TEXTUREMODES_BRDF:
@ -1250,7 +1246,7 @@ static MString* psh_convert(struct PixelShader *ps)
if (ps->state.rect_tex[i]) {
mstring_append_fmt(preflight,
"vec2 norm%d(vec2 coord) {\n"
" return coord / (textureSize(texSamp%d, 0) / texScale%d);\n"
" return coord / (textureSize(texSamp%d, 0) / texScale[%d]);\n"
"}\n",
i, i, i);
mstring_append_fmt(preflight,
@ -1437,3 +1433,164 @@ MString *pgraph_gen_psh_glsl(const PshState state, GenPshGlslOptions opts)
return psh_convert(&ps);
}
void pgraph_set_psh_uniform_values(PGRAPHState *pg, const PshUniformLocs locs,
PshUniformValues *values)
{
/* update combiner constants */
if (locs[PshUniform_consts] != -1) {
for (int i = 0; i < 9; i++) {
uint32_t constant[2];
if (i == 8) {
/* final combiner */
constant[0] = pgraph_reg_r(pg, NV_PGRAPH_SPECFOGFACTOR0);
constant[1] = pgraph_reg_r(pg, NV_PGRAPH_SPECFOGFACTOR1);
} else {
constant[0] =
pgraph_reg_r(pg, NV_PGRAPH_COMBINEFACTOR0 + i * 4);
constant[1] =
pgraph_reg_r(pg, NV_PGRAPH_COMBINEFACTOR1 + i * 4);
}
for (int j = 0; j < 2; j++) {
int idx = i * 2 + j;
pgraph_argb_pack32_to_rgba_float(constant[j],
values->consts[idx]);
}
}
}
if (locs[PshUniform_alphaRef] != -1) {
int alpha_ref = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_0),
NV_PGRAPH_CONTROL_0_ALPHAREF);
values->alphaRef[0] = alpha_ref;
}
if (locs[PshUniform_colorKey] != -1) {
values->colorKey[0] = pgraph_reg_r(pg, NV_PGRAPH_COLORKEYCOLOR0);
values->colorKey[1] = pgraph_reg_r(pg, NV_PGRAPH_COLORKEYCOLOR1);
values->colorKey[2] = pgraph_reg_r(pg, NV_PGRAPH_COLORKEYCOLOR2);
values->colorKey[3] = pgraph_reg_r(pg, NV_PGRAPH_COLORKEYCOLOR3);
}
if (locs[PshUniform_colorKeyMask] != -1) {
for (int i = 0; i < NV2A_MAX_TEXTURES; i++) {
values->colorKeyMask[i] =
pgraph_get_color_key_mask_for_texture(pg, i);
}
}
/* For each texture stage */
for (int i = 0; i < NV2A_MAX_TEXTURES; i++) {
/* Bump luminance only during stages 1 - 3 */
if (i > 0) {
if (locs[PshUniform_bumpMat] != -1) {
uint32_t m_u32[4];
m_u32[0] = pgraph_reg_r(pg, NV_PGRAPH_BUMPMAT00 + 4 * (i - 1));
m_u32[1] = pgraph_reg_r(pg, NV_PGRAPH_BUMPMAT01 + 4 * (i - 1));
m_u32[2] = pgraph_reg_r(pg, NV_PGRAPH_BUMPMAT10 + 4 * (i - 1));
m_u32[3] = pgraph_reg_r(pg, NV_PGRAPH_BUMPMAT11 + 4 * (i - 1));
values->bumpMat[i][0] = *(float *)&m_u32[0];
values->bumpMat[i][1] = *(float *)&m_u32[1];
values->bumpMat[i][2] = *(float *)&m_u32[2];
values->bumpMat[i][3] = *(float *)&m_u32[3];
}
if (locs[PshUniform_bumpScale] != -1) {
uint32_t v =
pgraph_reg_r(pg, NV_PGRAPH_BUMPSCALE1 + (i - 1) * 4);
values->bumpScale[i] = *(float *)&v;
}
if (locs[PshUniform_bumpOffset] != -1) {
uint32_t v =
pgraph_reg_r(pg, NV_PGRAPH_BUMPOFFSET1 + (i - 1) * 4);
values->bumpOffset[i] = *(float *)&v;
}
}
if (locs[PshUniform_texScale] != -1) {
values->texScale[0] = 1.0; /* Renderer will override this */
}
}
if (locs[PshUniform_fogColor] != -1) {
uint32_t fog_color = pgraph_reg_r(pg, NV_PGRAPH_FOGCOLOR);
values->fogColor[0][0] =
GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_RED) / 255.0;
values->fogColor[0][1] =
GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_GREEN) / 255.0;
values->fogColor[0][2] =
GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_BLUE) / 255.0;
values->fogColor[0][3] =
GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_ALPHA) / 255.0;
}
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);
}
if (locs[PshUniform_clipRange] != -1) {
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[PshUniform_depthOffset] != -1) {
float zbias = 0.0f;
if (pgraph_reg_r(pg, NV_PGRAPH_SETUPRASTER) &
(NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE |
NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE |
NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE)) {
uint32_t zbias_u32 = pgraph_reg_r(pg, NV_PGRAPH_ZOFFSETBIAS);
zbias = *(float *)&zbias_u32;
if (pgraph_reg_r(pg, NV_PGRAPH_ZOFFSETFACTOR) != 0 &&
(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_0) &
NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE)) {
/* TODO: emulate zfactor when z_perspective true, i.e.
* w-buffering. Perhaps calculate an additional offset based on
* triangle orientation in geometry shader and pass the result
* to fragment shader and add it to gl_FragDepth as well.
*/
NV2A_UNIMPLEMENTED("NV_PGRAPH_ZOFFSETFACTOR for w-buffering");
}
}
values->depthOffset[0] = zbias;
}
/* Clipping regions */
unsigned int max_gl_width = pg->surface_binding_dim.width;
unsigned int max_gl_height = pg->surface_binding_dim.height;
pgraph_apply_scaling_factor(pg, &max_gl_width, &max_gl_height);
for (int i = 0; i < 8; i++) {
uint32_t x = pgraph_reg_r(pg, NV_PGRAPH_WINDOWCLIPX0 + i * 4);
unsigned int x_min = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMIN);
unsigned int x_max = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMAX) + 1;
uint32_t y = pgraph_reg_r(pg, NV_PGRAPH_WINDOWCLIPY0 + i * 4);
unsigned int y_min = GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMIN);
unsigned int y_max = GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMAX) + 1;
pgraph_apply_anti_aliasing_factor(pg, &x_min, &y_min);
pgraph_apply_anti_aliasing_factor(pg, &x_max, &y_max);
pgraph_apply_scaling_factor(pg, &x_min, &y_min);
pgraph_apply_scaling_factor(pg, &x_max, &y_max);
values->clipRegion[i][0] = x_min;
values->clipRegion[i][1] = y_min;
values->clipRegion[i][2] = x_max;
values->clipRegion[i][3] = y_max;
}
}

View File

@ -24,6 +24,23 @@
#include "qemu/mstring.h"
#include "hw/xbox/nv2a/pgraph/psh.h"
#include "common.h"
#define PSH_UNIFORM_DECL_X(S, DECL) \
DECL(S, alphaRef, int, 1) \
DECL(S, bumpMat, mat2, 4) \
DECL(S, bumpOffset, float, 4) \
DECL(S, bumpScale, float, 4) \
DECL(S, clipRange, vec4, 1) \
DECL(S, clipRegion, ivec4, 8) \
DECL(S, colorKey, uint, 4) \
DECL(S, colorKeyMask, uint, 4) \
DECL(S, consts, vec4, 18) \
DECL(S, depthOffset, float, 1) \
DECL(S, fogColor, vec4, 1) \
DECL(S, texScale, float, 4)
DECL_UNIFORM_TYPES(PshUniform, PSH_UNIFORM_DECL_X)
typedef struct GenPshGlslOptions {
bool vulkan;
@ -31,6 +48,12 @@ typedef struct GenPshGlslOptions {
int tex_binding;
} GenPshGlslOptions;
typedef struct PGRAPHState PGRAPHState;
MString *pgraph_gen_psh_glsl(const PshState state, GenPshGlslOptions opts);
void pgraph_set_psh_uniform_values(PGRAPHState *pg,
const PshUniformLocs locs,
PshUniformValues *values);
#endif

View File

@ -30,11 +30,10 @@ static void append_skinning_code(MString* str, bool mix,
const char* output, const char* input,
const char* matrix, const char* swizzle);
void pgraph_gen_vsh_ff_glsl(GenVshGlslOptions opts, const VshState *state,
MString *header, MString *body, MString *uniforms)
void pgraph_gen_vsh_ff_glsl(const VshState *state, MString *header,
MString *body)
{
int i, j;
const char *u = opts.vulkan ? "" : "uniform "; // FIXME: Remove
/* generate vertex shader mimicking fixed function */
mstring_append(header,
@ -55,11 +54,6 @@ void pgraph_gen_vsh_ff_glsl(GenVshGlslOptions opts, const VshState *state,
"#define reserved2 v14\n"
"#define reserved3 v15\n"
"\n");
mstring_append_fmt(uniforms,
"%svec4 ltctxa[" stringify(NV2A_LTCTXA_COUNT) "];\n"
"%svec4 ltctxb[" stringify(NV2A_LTCTXB_COUNT) "];\n"
"%svec4 ltc1[" stringify(NV2A_LTC1_COUNT) "];\n", u, u, u
);
mstring_append(header,
"\n"
GLSL_DEFINE(projectionMat, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_PMAT0))
@ -236,15 +230,11 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
mstring_append(body, " oB1 = backSpecular;\n");
} else {
//FIXME: Do 2 passes if we want 2 sided-lighting?
mstring_append_fmt(uniforms, "%sfloat specularPower;\n", u);
static char alpha_source_diffuse[] = "diffuse.a";
static char alpha_source_specular[] = "specular.a";
static char alpha_source_material[] = "material_alpha";
const char *alpha_source = alpha_source_diffuse;
if (state->fixed_function.diffuse_src == MATERIAL_COLOR_SRC_MATERIAL) {
mstring_append_fmt(uniforms, "%sfloat material_alpha;\n", u);
alpha_source = alpha_source_material;
} else if (state->fixed_function.diffuse_src == MATERIAL_COLOR_SRC_SPECULAR) {
alpha_source = alpha_source_specular;
@ -285,19 +275,15 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
if (state->fixed_function.light[i] == LIGHT_LOCAL
|| state->fixed_function.light[i] == LIGHT_SPOT) {
mstring_append_fmt(uniforms,
"%svec3 lightLocalPosition%d;\n"
"%svec3 lightLocalAttenuation%d;\n",
u, i, u, i);
mstring_append_fmt(body,
" vec3 tPos = tPosition.xyz/tPosition.w;\n"
" vec3 VP = lightLocalPosition%d - tPos;\n"
" vec3 VP = lightLocalPosition[%d] - tPos;\n"
" float d = length(VP);\n"
" if (d <= lightLocalRange(%d)) {\n" /* FIXME: Double check that range is inclusive */
" VP = normalize(VP);\n"
" float attenuation = 1.0 / (lightLocalAttenuation%d.x\n"
" + lightLocalAttenuation%d.y * d\n"
" + lightLocalAttenuation%d.z * d * d);\n"
" float attenuation = 1.0 / (lightLocalAttenuation[%d].x\n"
" + lightLocalAttenuation[%d].y * d\n"
" + lightLocalAttenuation[%d].z * d * d);\n"
" vec3 halfVector = normalize(VP + %s);\n"
" float nDotVP = max(0.0, dot(tNormal, VP));\n"
" float nDotHV = max(0.0, dot(tNormal, halfVector));\n",
@ -311,14 +297,10 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
/* lightLocalRange will be 1e+30 here */
mstring_append_fmt(uniforms,
"%svec3 lightInfiniteHalfVector%d;\n"
"%svec3 lightInfiniteDirection%d;\n",
u, i, u, i);
mstring_append_fmt(body,
" {\n"
" float attenuation = 1.0;\n"
" vec3 lightDirection = normalize(lightInfiniteDirection%d);\n"
" vec3 lightDirection = normalize(lightInfiniteDirection[%d]);\n"
" float nDotVP = max(0.0, dot(tNormal, lightDirection));\n",
i);
if (state->fixed_function.local_eye) {
@ -327,7 +309,7 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
);
} else {
mstring_append_fmt(body,
" float nDotHV = max(0.0, dot(tNormal, lightInfiniteHalfVector%d));\n",
" float nDotHV = max(0.0, dot(tNormal, lightInfiniteHalfVector[%d]));\n",
i
);
}
@ -482,7 +464,6 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
/* FIXME: Testing */
if (state->point_params_enable) {
mstring_append_fmt(uniforms, "%sfloat pointParams[8];\n", u);
mstring_append(
body,
" float d_e = length(position * modelViewMat0);\n"

View File

@ -25,7 +25,7 @@
#include "qemu/mstring.h"
#include "vsh.h"
void pgraph_gen_vsh_ff_glsl(GenVshGlslOptions opts, const VshState *state,
MString *header, MString *body, MString *uniforms);
void pgraph_gen_vsh_ff_glsl(const VshState *state, MString *header,
MString *body);
#endif

View File

@ -20,32 +20,40 @@
*/
#include "qemu/osdep.h"
#include "hw/xbox/nv2a/pgraph/shaders.h"
#include "hw/xbox/nv2a/pgraph/pgraph.h"
#include "common.h"
#include "vsh.h"
#include "vsh-ff.h"
#include "vsh-prog.h"
#include <stdbool.h>
DEF_UNIFORM_INFO_ARR(VshUniform, VSH_UNIFORM_DECL_X)
MString *pgraph_gen_vsh_glsl(const VshState *state,
GenVshGlslOptions opts)
{
int i;
MString *output = mstring_new();
mstring_append_fmt(output, "#version %d\n\n", opts.vulkan ? 450 : 400);
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 "; // 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
);
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))
@ -117,7 +125,7 @@ MString *pgraph_gen_vsh_glsl(const VshState *state,
int num_uniform_attrs = 0;
for (i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
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);
@ -147,7 +155,7 @@ MString *pgraph_gen_vsh_glsl(const VshState *state,
MString *body = mstring_from_str("void main() {\n");
for (i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
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);
@ -160,7 +168,7 @@ MString *pgraph_gen_vsh_glsl(const VshState *state,
}
if (state->is_fixed_function) {
pgraph_gen_vsh_ff_glsl(opts, state, header, body, uniforms);
pgraph_gen_vsh_ff_glsl(state, header, body);
} else {
pgraph_gen_vsh_prog_glsl(
VSH_VERSION_XVS, (uint32_t *)state->programmable.program_data,
@ -307,10 +315,10 @@ MString *pgraph_gen_vsh_glsl(const VshState *state,
mstring_append_fmt(output,
"layout(push_constant) uniform PushConstants {\n"
" vec4 inlineValue[%d];\n"
"};\n\n", num_uniform_attrs);
"};\n\n", NV2A_VERTEXSHADER_ATTRIBUTES);
} else {
mstring_append_fmt(uniforms, " vec4 inlineValue[%d];\n",
num_uniform_attrs);
NV2A_VERTEXSHADER_ATTRIBUTES);
}
}
mstring_append_fmt(
@ -331,3 +339,126 @@ MString *pgraph_gen_vsh_glsl(const VshState *state,
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 *)&param_0;
values->fogParam[0][1] = *(float *)&param_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) {
/* update lighting constants */
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;
}
}
}

View File

@ -24,6 +24,26 @@
#include "qemu/mstring.h"
#include "hw/xbox/nv2a/pgraph/vsh.h"
#include "common.h"
#define VSH_UNIFORM_DECL_X(S, DECL) \
DECL(S, c, vec4, NV2A_VERTEXSHADER_CONSTANTS) \
DECL(S, clipRange, vec4, 1) \
DECL(S, fogParam, vec2, 1) \
DECL(S, inlineValue, vec4, NV2A_VERTEXSHADER_ATTRIBUTES) \
DECL(S, lightInfiniteDirection, vec3, NV2A_MAX_LIGHTS) \
DECL(S, lightInfiniteHalfVector, vec3, NV2A_MAX_LIGHTS) \
DECL(S, lightLocalAttenuation, vec3, NV2A_MAX_LIGHTS) \
DECL(S, lightLocalPosition, vec3, NV2A_MAX_LIGHTS) \
DECL(S, ltc1, vec4, NV2A_LTC1_COUNT) \
DECL(S, ltctxa, vec4, NV2A_LTCTXA_COUNT) \
DECL(S, ltctxb, vec4, NV2A_LTCTXB_COUNT) \
DECL(S, material_alpha, float, 1) \
DECL(S, pointParams, float, 8) \
DECL(S, specularPower, float, 1) \
DECL(S, surfaceSize, vec2, 1)
DECL_UNIFORM_TYPES(VshUniform, VSH_UNIFORM_DECL_X)
typedef struct GenVshGlslOptions {
bool vulkan;
@ -32,7 +52,13 @@ typedef struct GenVshGlslOptions {
int ubo_binding;
} GenVshGlslOptions;
typedef struct PGRAPHState PGRAPHState;
MString *pgraph_gen_vsh_glsl(const VshState *state,
GenVshGlslOptions glsl_opts);
void pgraph_set_vsh_uniform_values(PGRAPHState *pg, const VshState *state,
const VshUniformLocs locs,
VshUniformValues *values);
#endif

View File

@ -269,12 +269,24 @@ static void block_to_uniforms(const SpvReflectBlockVariable *block, ShaderUnifor
assert(member->array.dims_count < 2);
int dim = 1;
for (int i = 0; i < member->array.dims_count; i++) {
dim *= member->array.dims[i];
}
int stride = MAX(member->array.stride, member->numeric.matrix.stride);
if (member->numeric.matrix.column_count) {
dim *= member->numeric.matrix.column_count;
if (member->array.stride) {
stride =
member->array.stride / member->numeric.matrix.column_count;
}
}
layout->uniforms[k] = (ShaderUniform){
.name = strdup(member->name),
.offset = member->offset,
.dim_v = MAX(1, member->numeric.vector.component_count),
.dim_a = MAX(member->array.dims_count ? member->array.dims[0] : 1, member->numeric.matrix.column_count),
.stride = MAX(member->array.stride, member->numeric.matrix.stride),
.dim_a = dim,
.stride = stride,
};
// fprintf(stderr, "<%s offset=%zd dim_v=%zd dim_a=%zd stride=%zd>\n",

View File

@ -63,7 +63,7 @@ static inline void uniform_std140(ShaderUniformLayout *layout)
align = size;
stride = 0;
}
offset = ROUND_UP(offset, align);
u->align = align;
@ -87,7 +87,7 @@ static inline void uniform_std430(ShaderUniformLayout *layout)
size *= u->dim_v;
size_t align = size;
size *= u->dim_a;
offset = ROUND_UP(offset, align);
u->align = align;
@ -120,10 +120,10 @@ void *uniform_ptr(ShaderUniformLayout *layout, int idx)
return (char *)layout->allocation + layout->uniforms[idx - 1].offset;
}
static inline
void uniform_copy(ShaderUniformLayout *layout, int idx, void *values, size_t value_size, size_t count)
static inline void uniform_copy(ShaderUniformLayout *layout, int idx,
void *values, size_t value_size, size_t count)
{
assert(idx > 0 && "invalid uniform index");
assert(idx > 0 && "invalid uniform index");
ShaderUniform *u = &layout->uniforms[idx - 1];
const size_t element_size = value_size * u->dim_v;
@ -135,14 +135,14 @@ void uniform_copy(ShaderUniformLayout *layout, int idx, void *values, size_t val
int index = 0;
while (bytes_remaining) {
assert(p_out < p_max);
assert(index < u->dim_a);
assert((p_out + element_size) <= p_max);
assert(index < u->dim_a);
memcpy(p_out, p_in, element_size);
bytes_remaining -= element_size;
p_out += u->stride;
p_in += element_size;
index += 1;
}
}
}
static inline

View File

@ -30,6 +30,8 @@
#include "hw/xbox/nv2a/pgraph/surface.h"
#include "hw/xbox/nv2a/pgraph/texture.h"
#include "hw/xbox/nv2a/pgraph/shaders.h"
#include "hw/xbox/nv2a/pgraph/glsl/vsh.h"
#include "hw/xbox/nv2a/pgraph/glsl/psh.h"
#include <vulkan/vulkan.h>
#include <glslang/Include/glslang_c_interface.h>
@ -165,39 +167,8 @@ typedef struct ShaderBinding {
ShaderModuleInfo *fragment;
struct {
struct {
int alpha_ref;
int bump_mat[NV2A_MAX_TEXTURES];
int bump_offset[NV2A_MAX_TEXTURES];
int bump_scale[NV2A_MAX_TEXTURES];
int clip_range;
int clip_region;
int color_key;
int color_key_mask;
int depth_offset;
int fog_color;
int psh_constant[9][2];
int surface_size;
int tex_scale[NV2A_MAX_TEXTURES];
} psh;
struct {
int clip_range;
int fog_param;
int light_infinite_direction[NV2A_MAX_LIGHTS];
int light_infinite_half_vector[NV2A_MAX_LIGHTS];
int light_local_attenuation[NV2A_MAX_LIGHTS];
int light_local_position[NV2A_MAX_LIGHTS];
int ltc1;
int ltctxa;
int ltctxb;
int material_alpha;
int point_params;
int specular_power;
int uniform_attrs;
int vsh_constant;
uint32_t vsh_constants[NV2A_VERTEXSHADER_CONSTANTS][4];
} vsh;
PshUniformLocs psh;
VshUniformLocs vsh;
} uniform_locs;
} ShaderBinding;
@ -555,7 +526,6 @@ void pgraph_vk_init_shaders(PGRAPHState *pg);
void pgraph_vk_finalize_shaders(PGRAPHState *pg);
void pgraph_vk_update_descriptor_sets(PGRAPHState *pg);
void pgraph_vk_bind_shaders(PGRAPHState *pg);
void pgraph_vk_update_shader_uniforms(PGRAPHState *pg);
// reports.c
void pgraph_vk_init_reports(PGRAPHState *pg);

View File

@ -242,95 +242,15 @@ void pgraph_vk_update_descriptor_sets(PGRAPHState *pg)
static void update_shader_constant_locations(ShaderBinding *binding)
{
char tmp[64];
/* lookup fragment shader uniforms */
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 2; j++) {
snprintf(tmp, sizeof(tmp), "c%d_%d", j, i);
binding->uniform_locs.psh.psh_constant[i][j] =
uniform_index(&binding->fragment->uniforms, tmp);
}
}
binding->uniform_locs.psh.alpha_ref =
uniform_index(&binding->fragment->uniforms, "alphaRef");
binding->uniform_locs.psh.fog_color =
uniform_index(&binding->fragment->uniforms, "fogColor");
for (int i = 1; i < NV2A_MAX_TEXTURES; i++) {
snprintf(tmp, sizeof(tmp), "bumpMat%d", i);
binding->uniform_locs.psh.bump_mat[i] =
uniform_index(&binding->fragment->uniforms, tmp);
snprintf(tmp, sizeof(tmp), "bumpScale%d", i);
binding->uniform_locs.psh.bump_scale[i] =
uniform_index(&binding->fragment->uniforms, tmp);
snprintf(tmp, sizeof(tmp), "bumpOffset%d", i);
binding->uniform_locs.psh.bump_offset[i] =
uniform_index(&binding->fragment->uniforms, tmp);
for (int i = 0; i < ARRAY_SIZE(binding->uniform_locs.vsh); i++) {
binding->uniform_locs.vsh[i] =
uniform_index(&binding->vertex->uniforms, VshUniformInfo[i].name);
}
for (int i = 0; i < NV2A_MAX_TEXTURES; i++) {
snprintf(tmp, sizeof(tmp), "texScale%d", i);
binding->uniform_locs.psh.tex_scale[i] =
uniform_index(&binding->fragment->uniforms, tmp);
for (int i = 0; i < ARRAY_SIZE(binding->uniform_locs.psh); i++) {
binding->uniform_locs.psh[i] =
uniform_index(&binding->fragment->uniforms, PshUniformInfo[i].name);
}
/* lookup vertex shader uniforms */
binding->uniform_locs.vsh.vsh_constant =
uniform_index(&binding->vertex->uniforms, "c");
binding->uniform_locs.psh.surface_size =
uniform_index(&binding->vertex->uniforms, "surfaceSize");
binding->uniform_locs.vsh.clip_range =
uniform_index(&binding->vertex->uniforms, "clipRange");
binding->uniform_locs.psh.clip_range =
uniform_index(&binding->fragment->uniforms, "clipRange");
binding->uniform_locs.psh.depth_offset =
uniform_index(&binding->fragment->uniforms, "depthOffset");
binding->uniform_locs.vsh.fog_param =
uniform_index(&binding->vertex->uniforms, "fogParam");
binding->uniform_locs.vsh.ltctxa =
uniform_index(&binding->vertex->uniforms, "ltctxa");
binding->uniform_locs.vsh.ltctxb =
uniform_index(&binding->vertex->uniforms, "ltctxb");
binding->uniform_locs.vsh.ltc1 =
uniform_index(&binding->vertex->uniforms, "ltc1");
for (int i = 0; i < NV2A_MAX_LIGHTS; i++) {
snprintf(tmp, sizeof(tmp), "lightInfiniteHalfVector%d", i);
binding->uniform_locs.vsh.light_infinite_half_vector[i] =
uniform_index(&binding->vertex->uniforms, tmp);
snprintf(tmp, sizeof(tmp), "lightInfiniteDirection%d", i);
binding->uniform_locs.vsh.light_infinite_direction[i] =
uniform_index(&binding->vertex->uniforms, tmp);
snprintf(tmp, sizeof(tmp), "lightLocalPosition%d", i);
binding->uniform_locs.vsh.light_local_position[i] =
uniform_index(&binding->vertex->uniforms, tmp);
snprintf(tmp, sizeof(tmp), "lightLocalAttenuation%d", i);
binding->uniform_locs.vsh.light_local_attenuation[i] =
uniform_index(&binding->vertex->uniforms, tmp);
}
binding->uniform_locs.psh.clip_region =
uniform_index(&binding->fragment->uniforms, "clipRegion");
binding->uniform_locs.vsh.point_params =
uniform_index(&binding->vertex->uniforms, "pointParams");
binding->uniform_locs.vsh.material_alpha =
uniform_index(&binding->vertex->uniforms, "material_alpha");
binding->uniform_locs.psh.color_key =
uniform_index(&binding->fragment->uniforms, "colorKey");
binding->uniform_locs.psh.color_key_mask =
uniform_index(&binding->fragment->uniforms, "colorKeyMask");
binding->uniform_locs.vsh.uniform_attrs =
uniform_index(&binding->vertex->uniforms, "inlineValue");
binding->uniform_locs.vsh.specular_power =
uniform_index(&binding->vertex->uniforms, "specularPower");
}
static void shader_cache_entry_init(Lru *lru, LruNode *node, void *state)
@ -467,310 +387,6 @@ static ShaderBinding *gen_shaders(PGRAPHState *pg, ShaderState *state)
return snode;
}
static void update_uniform_attr_values(PGRAPHState *pg, ShaderBinding *binding)
{
float values[NV2A_VERTEXSHADER_ATTRIBUTES][4];
int num_uniform_attrs = 0;
pgraph_get_inline_values(pg, binding->state.vsh.uniform_attrs, values,
&num_uniform_attrs);
if (num_uniform_attrs > 0) {
uniform1fv(&binding->vertex->uniforms,
binding->uniform_locs.vsh.uniform_attrs,
num_uniform_attrs * 4, &values[0][0]);
}
}
// FIXME: Move to common
static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding)
{
PGRAPHVkState *r = pg->vk_renderer_state;
ShaderState *state = &binding->state;
/* update combiner constants */
for (int i = 0; i < 9; i++) {
uint32_t constant[2];
if (i == 8) {
/* final combiner */
constant[0] = pgraph_reg_r(pg, NV_PGRAPH_SPECFOGFACTOR0);
constant[1] = pgraph_reg_r(pg, NV_PGRAPH_SPECFOGFACTOR1);
} else {
constant[0] = pgraph_reg_r(pg, NV_PGRAPH_COMBINEFACTOR0 + i * 4);
constant[1] = pgraph_reg_r(pg, NV_PGRAPH_COMBINEFACTOR1 + i * 4);
}
for (int j = 0; j < 2; j++) {
GLint loc = binding->uniform_locs.psh.psh_constant[i][j];
if (loc != -1) {
float value[4];
pgraph_argb_pack32_to_rgba_float(constant[j], value);
uniform1fv(&binding->fragment->uniforms, loc, 4, value);
}
}
}
if (binding->uniform_locs.psh.alpha_ref != -1) {
int alpha_ref = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_0),
NV_PGRAPH_CONTROL_0_ALPHAREF);
uniform1i(&binding->fragment->uniforms,
binding->uniform_locs.psh.alpha_ref, alpha_ref);
}
if (binding->uniform_locs.psh.color_key != -1) {
uint32_t color_key_colors[4] = {
pgraph_reg_r(pg, NV_PGRAPH_COLORKEYCOLOR0),
pgraph_reg_r(pg, NV_PGRAPH_COLORKEYCOLOR1),
pgraph_reg_r(pg, NV_PGRAPH_COLORKEYCOLOR2),
pgraph_reg_r(pg, NV_PGRAPH_COLORKEYCOLOR3),
};
uniform1uiv(&binding->fragment->uniforms,
binding->uniform_locs.psh.color_key, 4, color_key_colors);
}
uint32_t color_key_mask[4] = { 0 };
/* For each texture stage */
for (int i = 0; i < NV2A_MAX_TEXTURES; i++) {
int loc;
/* Bump luminance only during stages 1 - 3 */
if (i > 0) {
loc = binding->uniform_locs.psh.bump_mat[i];
if (loc != -1) {
uint32_t m_u32[4];
m_u32[0] = pgraph_reg_r(pg, NV_PGRAPH_BUMPMAT00 + 4 * (i - 1));
m_u32[1] = pgraph_reg_r(pg, NV_PGRAPH_BUMPMAT01 + 4 * (i - 1));
m_u32[2] = pgraph_reg_r(pg, NV_PGRAPH_BUMPMAT10 + 4 * (i - 1));
m_u32[3] = pgraph_reg_r(pg, NV_PGRAPH_BUMPMAT11 + 4 * (i - 1));
float m[4];
m[0] = *(float *)&m_u32[0];
m[1] = *(float *)&m_u32[1];
m[2] = *(float *)&m_u32[2];
m[3] = *(float *)&m_u32[3];
uniformMatrix2fv(&binding->fragment->uniforms, loc, m);
}
loc = binding->uniform_locs.psh.bump_scale[i];
if (loc != -1) {
uint32_t v =
pgraph_reg_r(pg, NV_PGRAPH_BUMPSCALE1 + (i - 1) * 4);
uniform1f(&binding->fragment->uniforms, loc, *(float *)&v);
}
loc = binding->uniform_locs.psh.bump_offset[i];
if (loc != -1) {
uint32_t v =
pgraph_reg_r(pg, NV_PGRAPH_BUMPOFFSET1 + (i - 1) * 4);
uniform1f(&binding->fragment->uniforms, loc, *(float *)&v);
}
}
loc = binding->uniform_locs.psh.tex_scale[i];
if (loc != -1) {
assert(pg->vk_renderer_state->texture_bindings[i] != NULL);
float scale = pg->vk_renderer_state->texture_bindings[i]->key.scale;
BasicColorFormatInfo f_basic =
kelvin_color_format_info_map[pg->vk_renderer_state
->texture_bindings[i]
->key.state.color_format];
if (!f_basic.linear) {
scale = 1.0;
}
uniform1f(&binding->fragment->uniforms, loc, scale);
}
color_key_mask[i] = pgraph_get_color_key_mask_for_texture(pg, i);
}
if (binding->uniform_locs.psh.color_key_mask != -1) {
uniform1uiv(&binding->fragment->uniforms,
binding->uniform_locs.psh.color_key_mask, 4,
color_key_mask);
}
if (binding->uniform_locs.psh.fog_color != -1) {
uint32_t fog_color = pgraph_reg_r(pg, NV_PGRAPH_FOGCOLOR);
uniform4f(&binding->fragment->uniforms,
binding->uniform_locs.psh.fog_color,
GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_RED) / 255.0,
GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_GREEN) / 255.0,
GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_BLUE) / 255.0,
GET_MASK(fog_color, NV_PGRAPH_FOGCOLOR_ALPHA) / 255.0);
}
if (binding->uniform_locs.vsh.fog_param != -1) {
uint32_t v[2];
v[0] = pgraph_reg_r(pg, NV_PGRAPH_FOGPARAM0);
v[1] = pgraph_reg_r(pg, NV_PGRAPH_FOGPARAM1);
uniform2f(&binding->vertex->uniforms,
binding->uniform_locs.vsh.fog_param, *(float *)&v[0],
*(float *)&v[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);
}
if (binding->state.vsh.is_fixed_function) {
/* update lighting constants */
struct {
uint32_t *v;
int locs;
size_t len;
} lighting_arrays[] = {
{ &pg->ltctxa[0][0], binding->uniform_locs.vsh.ltctxa,
NV2A_LTCTXA_COUNT },
{ &pg->ltctxb[0][0], binding->uniform_locs.vsh.ltctxb,
NV2A_LTCTXB_COUNT },
{ &pg->ltc1[0][0], binding->uniform_locs.vsh.ltc1,
NV2A_LTC1_COUNT },
};
for (int i = 0; i < ARRAY_SIZE(lighting_arrays); i++) {
uniform1iv(&binding->vertex->uniforms, lighting_arrays[i].locs,
lighting_arrays[i].len * 4,
(void *)lighting_arrays[i].v);
}
for (int i = 0; i < NV2A_MAX_LIGHTS; i++) {
int loc = binding->uniform_locs.vsh.light_infinite_half_vector[i];
if (loc != -1) {
uniform1fv(&binding->vertex->uniforms, loc, 3,
pg->light_infinite_half_vector[i]);
}
loc = binding->uniform_locs.vsh.light_infinite_direction[i];
if (loc != -1) {
uniform1fv(&binding->vertex->uniforms, loc, 3,
pg->light_infinite_direction[i]);
}
loc = binding->uniform_locs.vsh.light_local_position[i];
if (loc != -1) {
uniform1fv(&binding->vertex->uniforms, loc, 3,
pg->light_local_position[i]);
}
loc = binding->uniform_locs.vsh.light_local_attenuation[i];
if (loc != -1) {
uniform1fv(&binding->vertex->uniforms, loc, 3,
pg->light_local_attenuation[i]);
}
}
if (binding->uniform_locs.vsh.specular_power != -1) {
uniform1f(&binding->vertex->uniforms,
binding->uniform_locs.vsh.specular_power,
pg->specular_power);
}
}
/* update vertex program constants */
uniform1iv(&binding->vertex->uniforms,
binding->uniform_locs.vsh.vsh_constant,
NV2A_VERTEXSHADER_CONSTANTS * 4, (void *)pg->vsh_constants);
if (binding->uniform_locs.psh.surface_size != -1) {
unsigned int aa_width = 1, aa_height = 1;
pgraph_apply_anti_aliasing_factor(pg, &aa_width, &aa_height);
uniform2f(&binding->vertex->uniforms,
binding->uniform_locs.psh.surface_size,
pg->surface_binding_dim.width / aa_width,
pg->surface_binding_dim.height / aa_height);
}
if (binding->uniform_locs.vsh.clip_range != -1 ||
binding->uniform_locs.psh.clip_range != -1) {
uint32_t v[2];
v[0] = pgraph_reg_r(pg, NV_PGRAPH_ZCLIPMIN);
v[1] = pgraph_reg_r(pg, NV_PGRAPH_ZCLIPMAX);
float zclip_min = *(float *)&v[0];
float zclip_max = *(float *)&v[1];
if (binding->uniform_locs.vsh.clip_range != -1) {
uniform4f(&binding->vertex->uniforms,
binding->uniform_locs.vsh.clip_range, 0, zmax, zclip_min,
zclip_max);
}
if (binding->uniform_locs.psh.clip_range != -1) {
uniform4f(&binding->fragment->uniforms,
binding->uniform_locs.psh.clip_range, 0, zmax, zclip_min,
zclip_max);
}
}
if (binding->uniform_locs.psh.depth_offset != -1) {
float zbias = 0.0f;
if (pgraph_reg_r(pg, NV_PGRAPH_SETUPRASTER) &
(NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE |
NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE |
NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE)) {
uint32_t zbias_u32 = pgraph_reg_r(pg, NV_PGRAPH_ZOFFSETBIAS);
zbias = *(float *)&zbias_u32;
if (pgraph_reg_r(pg, NV_PGRAPH_ZOFFSETFACTOR) != 0 &&
(pgraph_reg_r(pg, NV_PGRAPH_CONTROL_0) &
NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE)) {
/* TODO: emulate zfactor when z_perspective true, i.e.
* w-buffering. Perhaps calculate an additional offset based on
* triangle orientation in geometry shader and pass the result
* to fragment shader and add it to gl_FragDepth as well.
*/
NV2A_UNIMPLEMENTED("NV_PGRAPH_ZOFFSETFACTOR for w-buffering");
}
}
uniform1f(&binding->fragment->uniforms,
binding->uniform_locs.psh.depth_offset, zbias);
}
/* Clipping regions */
unsigned int max_gl_width = pg->surface_binding_dim.width;
unsigned int max_gl_height = pg->surface_binding_dim.height;
pgraph_apply_scaling_factor(pg, &max_gl_width, &max_gl_height);
uint32_t clip_regions[8][4];
for (int i = 0; i < 8; i++) {
uint32_t x = pgraph_reg_r(pg, NV_PGRAPH_WINDOWCLIPX0 + i * 4);
unsigned int x_min = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMIN);
unsigned int x_max = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMAX) + 1;
uint32_t y = pgraph_reg_r(pg, NV_PGRAPH_WINDOWCLIPY0 + i * 4);
unsigned int y_min = GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMIN);
unsigned int y_max = GET_MASK(y, NV_PGRAPH_WINDOWCLIPY0_YMAX) + 1;
pgraph_apply_anti_aliasing_factor(pg, &x_min, &y_min);
pgraph_apply_anti_aliasing_factor(pg, &x_max, &y_max);
pgraph_apply_scaling_factor(pg, &x_min, &y_min);
pgraph_apply_scaling_factor(pg, &x_max, &y_max);
clip_regions[i][0] = x_min;
clip_regions[i][1] = y_min;
clip_regions[i][2] = x_max;
clip_regions[i][3] = y_max;
}
uniform1iv(&binding->fragment->uniforms,
binding->uniform_locs.psh.clip_region, 8 * 4,
(void *)clip_regions);
if (binding->uniform_locs.vsh.point_params != -1) {
uniform1iv(&binding->vertex->uniforms,
binding->uniform_locs.vsh.point_params,
ARRAY_SIZE(pg->point_params), (void *)pg->point_params);
}
if (binding->uniform_locs.vsh.material_alpha != -1) {
uniform1f(&binding->vertex->uniforms,
binding->uniform_locs.vsh.material_alpha, pg->material_alpha);
}
if (!r->use_push_constants_for_uniform_attrs && state->vsh.uniform_attrs) {
update_uniform_attr_values(pg, binding);
}
}
// Quickly check PGRAPH state to see if any registers have changed that
// necessitate a full shader state inspection.
static bool check_shaders_dirty(PGRAPHState *pg)
@ -849,6 +465,71 @@ static bool check_shaders_dirty(PGRAPHState *pg)
return false;
}
static void apply_uniform_updates(ShaderUniformLayout *layout,
const UniformInfo *info, int *locs,
void *values, size_t count)
{
for (int i = 0; i < count; i++) {
if (locs[i] != -1) {
uniform_copy(layout, locs[i], (char*)values + info[i].val_offs,
4, (info[i].size * info[i].count) / 4);
}
}
}
// FIXME: Dirty tracking
static void update_shader_uniforms(PGRAPHState *pg)
{
PGRAPHVkState *r = pg->vk_renderer_state;
NV2A_VK_DGROUP_BEGIN("%s", __func__);
nv2a_profile_inc_counter(NV2A_PROF_SHADER_BIND);
assert(r->shader_binding);
ShaderBinding *binding = r->shader_binding;
ShaderUniformLayout *layouts[] = { &binding->vertex->uniforms,
&binding->fragment->uniforms };
VshUniformValues vsh_values;
pgraph_set_vsh_uniform_values(pg, &binding->state.vsh,
binding->uniform_locs.vsh, &vsh_values);
apply_uniform_updates(&binding->vertex->uniforms, VshUniformInfo,
binding->uniform_locs.vsh, &vsh_values,
VshUniform__COUNT);
PshUniformValues psh_values;
pgraph_set_psh_uniform_values(pg, binding->uniform_locs.psh, &psh_values);
for (int i = 0; i < 4; i++) {
assert(r->texture_bindings[i] != NULL);
float scale = r->texture_bindings[i]->key.scale;
BasicColorFormatInfo f_basic =
kelvin_color_format_info_map[pg->vk_renderer_state
->texture_bindings[i]
->key.state.color_format];
if (!f_basic.linear) {
scale = 1.0;
}
psh_values.texScale[i] = scale;
}
apply_uniform_updates(&binding->fragment->uniforms, PshUniformInfo,
binding->uniform_locs.psh, &psh_values,
PshUniform__COUNT);
for (int i = 0; i < ARRAY_SIZE(layouts); i++) {
uint64_t hash =
fast_hash(layouts[i]->allocation, layouts[i]->total_size);
r->uniforms_changed |= (hash != r->uniform_buffer_hashes[i]);
r->uniform_buffer_hashes[i] = hash;
}
nv2a_profile_inc_counter(r->uniforms_changed ?
NV2A_PROF_SHADER_UBO_DIRTY :
NV2A_PROF_SHADER_UBO_NOTDIRTY);
NV2A_VK_DGROUP_END();
}
void pgraph_vk_bind_shaders(PGRAPHState *pg)
{
NV2A_VK_DGROUP_BEGIN("%s", __func__);
@ -865,33 +546,7 @@ void pgraph_vk_bind_shaders(PGRAPHState *pg)
}
}
// FIXME: Use dirty bits
pgraph_vk_update_shader_uniforms(pg);
NV2A_VK_DGROUP_END();
}
void pgraph_vk_update_shader_uniforms(PGRAPHState *pg)
{
PGRAPHVkState *r = pg->vk_renderer_state;
NV2A_VK_DGROUP_BEGIN("%s", __func__);
nv2a_profile_inc_counter(NV2A_PROF_SHADER_BIND);
assert(r->shader_binding);
ShaderBinding *binding = r->shader_binding;
ShaderUniformLayout *layouts[] = { &binding->vertex->uniforms,
&binding->fragment->uniforms };
shader_update_constants(pg, r->shader_binding);
for (int i = 0; i < ARRAY_SIZE(layouts); i++) {
uint64_t hash = fast_hash(layouts[i]->allocation, layouts[i]->total_size);
r->uniforms_changed |= (hash != r->uniform_buffer_hashes[i]);
r->uniform_buffer_hashes[i] = hash;
}
nv2a_profile_inc_counter(r->uniforms_changed ?
NV2A_PROF_SHADER_UBO_DIRTY :
NV2A_PROF_SHADER_UBO_NOTDIRTY);
update_shader_uniforms(pg);
NV2A_VK_DGROUP_END();
}