nv2a/gl: Unify ShaderBinding and ShaderLruNode

This commit is contained in:
Matt Borgerson 2025-01-07 17:30:59 -07:00
parent e7047efef2
commit 510c280b05
2 changed files with 105 additions and 116 deletions

View File

@ -83,6 +83,16 @@ typedef struct TextureBinding {
} TextureBinding;
typedef struct ShaderBinding {
LruNode node;
bool initialized;
bool cached;
void *program;
size_t program_size;
GLenum program_format;
ShaderState state;
QemuThread *save_thread;
GLuint gl_program;
GLenum gl_primitive_mode;
@ -117,17 +127,6 @@ typedef struct ShaderBinding {
GLint material_alpha_loc;
} ShaderBinding;
typedef struct ShaderLruNode {
LruNode node;
bool cached;
void *program;
size_t program_size;
GLenum program_format;
ShaderState state;
ShaderBinding *binding;
QemuThread *save_thread;
} ShaderLruNode;
typedef struct VertexKey {
size_t count;
size_t stride;
@ -196,7 +195,7 @@ typedef struct PGRAPHGLState {
TextureLruNode *texture_cache_entries;
Lru shader_cache;
ShaderLruNode *shader_cache_entries;
ShaderBinding *shader_cache_entries;
ShaderBinding *shader_binding;
QemuMutex shader_cache_lock;
QemuThread shader_disk_thread;
@ -279,8 +278,8 @@ SurfaceBinding *pgraph_gl_surface_get_within(NV2AState *d, hwaddr addr);
void pgraph_gl_surface_invalidate(NV2AState *d, SurfaceBinding *e);
void pgraph_gl_unbind_surface(NV2AState *d, bool color);
void pgraph_gl_upload_surface_data(NV2AState *d, SurfaceBinding *surface, bool force);
void pgraph_gl_shader_cache_to_disk(ShaderLruNode *snode);
bool pgraph_gl_shader_load_from_memory(ShaderLruNode *snode);
void pgraph_gl_shader_cache_to_disk(ShaderBinding *snode);
bool pgraph_gl_shader_load_from_memory(ShaderBinding *snode);
void pgraph_gl_shader_write_cache_reload_list(PGRAPHState *pg);
void pgraph_gl_set_surface_scale_factor(NV2AState *d, unsigned int scale);
unsigned int pgraph_gl_get_surface_scale_factor(NV2AState *d);

View File

@ -34,8 +34,6 @@
#include "debug.h"
#include "renderer.h"
static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding, bool binding_changed, bool vertex_program, bool fixed_function);
static GLenum get_gl_primitive_mode(enum ShaderPolygonMode polygon_mode, enum ShaderPrimitiveMode primitive_mode)
{
if (polygon_mode == POLY_MODE_POINT) {
@ -102,7 +100,7 @@ static GLuint create_gl_shader(GLenum gl_shader_type,
return shader;
}
static void update_shader_constant_locations(ShaderBinding *binding, const ShaderState *state)
static void update_shader_constant_locations(ShaderBinding *binding)
{
char tmp[64];
@ -191,7 +189,7 @@ static void update_shader_constant_locations(ShaderBinding *binding, const Shade
binding->clip_region_loc[i] = glGetUniformLocation(binding->gl_program, tmp);
}
if (state->fixed_function) {
if (binding->state.fixed_function) {
binding->material_alpha_loc =
glGetUniformLocation(binding->gl_program, "material_alpha");
} else {
@ -199,7 +197,7 @@ static void update_shader_constant_locations(ShaderBinding *binding, const Shade
}
}
static ShaderBinding *generate_shaders(const ShaderState *state)
static void generate_shaders(ShaderBinding *binding)
{
char *previous_numeric_locale = setlocale(LC_NUMERIC, NULL);
if (previous_numeric_locale) {
@ -210,6 +208,8 @@ static ShaderBinding *generate_shaders(const ShaderState *state)
setlocale(LC_NUMERIC, "C");
GLuint program = glCreateProgram();
ShaderState *state = &binding->state;
/* Create an optional geometry shader and find primitive type */
GLenum gl_primitive_mode =
get_gl_primitive_mode(state->polygon_front_mode, state->primitive_mode);
@ -261,18 +261,15 @@ static ShaderBinding *generate_shaders(const ShaderState *state)
glUseProgram(program);
ShaderBinding* ret = g_malloc0(sizeof(ShaderBinding));
ret->gl_program = program;
ret->gl_primitive_mode = gl_primitive_mode;
update_shader_constant_locations(ret, state);
binding->initialized = true;
binding->gl_program = program;
binding->gl_primitive_mode = gl_primitive_mode;
update_shader_constant_locations(binding);
if (previous_numeric_locale) {
setlocale(LC_NUMERIC, previous_numeric_locale);
g_free(previous_numeric_locale);
}
return ret;
}
static const char *shader_gl_vendor = NULL;
@ -328,19 +325,22 @@ void pgraph_gl_shader_write_cache_reload_list(PGRAPHState *pg)
qemu_event_set(&r->shader_cache_writeback_complete);
}
bool pgraph_gl_shader_load_from_memory(ShaderLruNode *snode)
bool pgraph_gl_shader_load_from_memory(ShaderBinding *binding)
{
assert(glGetError() == GL_NO_ERROR);
if (!snode->program) {
if (!binding->program) {
return false;
}
GLuint gl_program = glCreateProgram();
glProgramBinary(gl_program, snode->program_format, snode->program, snode->program_size);
glProgramBinary(gl_program, binding->program_format, binding->program,
binding->program_size);
GLint gl_error = glGetError();
if (gl_error != GL_NO_ERROR) {
NV2A_DPRINTF("failed to load shader binary from disk: GL error code %d\n", gl_error);
NV2A_DPRINTF(
"failed to load shader binary from disk: GL error code %d\n",
gl_error);
glDeleteProgram(gl_program);
return false;
}
@ -358,16 +358,15 @@ bool pgraph_gl_shader_load_from_memory(ShaderLruNode *snode)
glUseProgram(gl_program);
ShaderBinding* binding = g_malloc0(sizeof(ShaderBinding));
binding->gl_program = gl_program;
binding->gl_primitive_mode = get_gl_primitive_mode(snode->state.polygon_front_mode,
snode->state.primitive_mode);
snode->binding = binding;
binding->gl_primitive_mode = get_gl_primitive_mode(
binding->state.polygon_front_mode, binding->state.primitive_mode);
binding->initialized = true;
g_free(snode->program);
snode->program = NULL;
g_free(binding->program);
binding->program = NULL;
update_shader_constant_locations(binding, &snode->state);
update_shader_constant_locations(binding);
return true;
}
@ -460,18 +459,18 @@ static void shader_load_from_disk(PGRAPHState *pg, uint64_t hash)
qemu_mutex_lock(&r->shader_cache_lock);
LruNode *node = lru_lookup(&r->shader_cache, hash, &state);
ShaderLruNode *snode = container_of(node, ShaderLruNode, node);
ShaderBinding *binding = container_of(node, ShaderBinding, node);
/* If we happened to regenerate this shader already, then we may as well use the new one */
if (snode->binding) {
if (binding->initialized) {
qemu_mutex_unlock(&r->shader_cache_lock);
return;
}
snode->program_format = program_binary_format;
snode->program_size = shader_size;
snode->program = program_buffer;
snode->cached = true;
binding->program_format = program_binary_format;
binding->program_size = shader_size;
binding->program = program_buffer;
binding->cached = true;
qemu_mutex_unlock(&r->shader_cache_lock);
return;
@ -509,43 +508,38 @@ static void *shader_reload_lru_from_disk(void *arg)
static void shader_cache_entry_init(Lru *lru, LruNode *node, void *state)
{
ShaderLruNode *snode = container_of(node, ShaderLruNode, node);
memcpy(&snode->state, state, sizeof(ShaderState));
snode->cached = false;
snode->binding = NULL;
snode->program = NULL;
snode->save_thread = NULL;
ShaderBinding *binding = container_of(node, ShaderBinding, node);
memcpy(&binding->state, state, sizeof(ShaderState));
binding->initialized = false;
binding->cached = false;
binding->program = NULL;
binding->save_thread = NULL;
}
static void shader_cache_entry_post_evict(Lru *lru, LruNode *node)
{
ShaderLruNode *snode = container_of(node, ShaderLruNode, node);
ShaderBinding *binding = container_of(node, ShaderBinding, node);
if (snode->save_thread) {
qemu_thread_join(snode->save_thread);
g_free(snode->save_thread);
if (binding->save_thread) {
qemu_thread_join(binding->save_thread);
g_free(binding->save_thread);
}
if (snode->binding) {
glDeleteProgram(snode->binding->gl_program);
g_free(snode->binding);
glDeleteProgram(binding->gl_program);
if (binding->program) {
g_free(binding->program);
}
if (snode->program) {
g_free(snode->program);
}
snode->cached = false;
snode->save_thread = NULL;
snode->binding = NULL;
snode->program = NULL;
memset(&snode->state, 0, sizeof(ShaderState));
binding->cached = false;
binding->save_thread = NULL;
binding->program = NULL;
memset(&binding->state, 0, sizeof(ShaderState));
}
static bool shader_cache_entry_compare(Lru *lru, LruNode *node, void *key)
{
ShaderLruNode *snode = container_of(node, ShaderLruNode, node);
return memcmp(&snode->state, key, sizeof(ShaderState));
ShaderBinding *binding = container_of(node, ShaderBinding, node);
return memcmp(&binding->state, key, sizeof(ShaderState));
}
void pgraph_gl_init_shaders(PGRAPHState *pg)
@ -564,7 +558,7 @@ void pgraph_gl_init_shaders(PGRAPHState *pg)
/* FIXME: Make this configurable */
const size_t shader_cache_size = 50*1024;
lru_init(&r->shader_cache);
r->shader_cache_entries = malloc(shader_cache_size * sizeof(ShaderLruNode));
r->shader_cache_entries = malloc(shader_cache_size * sizeof(ShaderBinding));
assert(r->shader_cache_entries != NULL);
for (int i = 0; i < shader_cache_size; i++) {
lru_add_free(&r->shader_cache, &r->shader_cache_entries[i].node);
@ -592,10 +586,10 @@ void pgraph_gl_finalize_shaders(PGRAPHState *pg)
static void *shader_write_to_disk(void *arg)
{
ShaderLruNode *snode = (ShaderLruNode*) arg;
ShaderBinding *binding = (ShaderBinding*) arg;
char *shader_bin = shader_get_bin_directory(snode->node.hash);
char *shader_path = shader_get_binary_path(shader_bin, snode->node.hash);
char *shader_bin = shader_get_bin_directory(binding->node.hash);
char *shader_path = shader_get_binary_path(shader_bin, binding->node.hash);
static uint64_t gl_vendor_len;
if (gl_vendor_len == 0) {
@ -631,19 +625,19 @@ static void *shader_write_to_disk(void *arg)
WRITE_OR_ERR(&gl_vendor_len, sizeof(gl_vendor_len));
WRITE_OR_ERR(shader_gl_vendor, gl_vendor_len);
WRITE_OR_ERR(&snode->program_format, sizeof(snode->program_format));
WRITE_OR_ERR(&snode->state, sizeof(snode->state));
WRITE_OR_ERR(&binding->program_format, sizeof(binding->program_format));
WRITE_OR_ERR(&binding->state, sizeof(binding->state));
WRITE_OR_ERR(&snode->program_size, sizeof(snode->program_size));
WRITE_OR_ERR(snode->program, snode->program_size);
WRITE_OR_ERR(&binding->program_size, sizeof(binding->program_size));
WRITE_OR_ERR(binding->program, binding->program_size);
#undef WRITE_OR_ERR
fclose(shader_file);
g_free(shader_path);
g_free(snode->program);
snode->program = NULL;
g_free(binding->program);
binding->program = NULL;
return NULL;
@ -651,23 +645,23 @@ error:
fprintf(stderr, "nv2a: Failed to write shader binary file to %s\n", shader_path);
qemu_unlink(shader_path);
g_free(shader_path);
g_free(snode->program);
snode->program = NULL;
g_free(binding->program);
binding->program = NULL;
return NULL;
}
void pgraph_gl_shader_cache_to_disk(ShaderLruNode *snode)
void pgraph_gl_shader_cache_to_disk(ShaderBinding *binding)
{
if (!snode->binding || snode->cached) {
if (binding->cached) {
return;
}
GLint program_size;
glGetProgramiv(snode->binding->gl_program, GL_PROGRAM_BINARY_LENGTH, &program_size);
glGetProgramiv(binding->gl_program, GL_PROGRAM_BINARY_LENGTH, &program_size);
if (snode->program) {
g_free(snode->program);
snode->program = NULL;
if (binding->program) {
g_free(binding->program);
binding->program = NULL;
}
/* program_size might be zero on some systems, if no binary formats are supported */
@ -675,27 +669,23 @@ void pgraph_gl_shader_cache_to_disk(ShaderLruNode *snode)
return;
}
snode->program = g_malloc(program_size);
binding->program = g_malloc(program_size);
GLsizei program_size_copied;
glGetProgramBinary(snode->binding->gl_program, program_size, &program_size_copied,
&snode->program_format, snode->program);
glGetProgramBinary(binding->gl_program, program_size, &program_size_copied,
&binding->program_format, binding->program);
assert(glGetError() == GL_NO_ERROR);
snode->program_size = program_size_copied;
snode->cached = true;
binding->program_size = program_size_copied;
binding->cached = true;
char name[24];
snprintf(name, sizeof(name), "scache-%llx", (unsigned long long) snode->node.hash);
snode->save_thread = g_malloc0(sizeof(QemuThread));
qemu_thread_create(snode->save_thread, name, shader_write_to_disk, snode, QEMU_THREAD_JOINABLE);
snprintf(name, sizeof(name), "scache-%llx", (unsigned long long) binding->node.hash);
binding->save_thread = g_malloc0(sizeof(QemuThread));
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,
// FIXME: Remove these... We already know it from binding.state
bool vertex_program,
bool fixed_function)
bool binding_changed)
{
PGRAPHGLState *r = pg->gl_renderer_state;
int i, j;
@ -796,7 +786,7 @@ static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding,
assert(0);
}
if (fixed_function) {
if (binding->state.fixed_function) {
/* update lighting constants */
struct {
uint32_t* v;
@ -1015,10 +1005,7 @@ void pgraph_gl_bind_shaders(PGRAPHState *pg)
goto update_constants;
}
pg->program_data_dirty = false;
ShaderBinding* old_binding = r->shader_binding;
ShaderBinding *old_binding = r->shader_binding;
ShaderState state = pgraph_get_shader_state(pg);
assert(!state.vulkan);
@ -1026,22 +1013,24 @@ void pgraph_gl_bind_shaders(PGRAPHState *pg)
state.vertex_program ? "yes" : "no",
state.fixed_function ? "yes" : "no");
uint64_t shader_state_hash = fast_hash((uint8_t*) &state, sizeof(ShaderState));
qemu_mutex_lock(&r->shader_cache_lock);
LruNode *node = lru_lookup(&r->shader_cache, shader_state_hash, &state);
ShaderLruNode *snode = container_of(node, ShaderLruNode, node);
if (snode->binding || pgraph_gl_shader_load_from_memory(snode)) {
r->shader_binding = snode->binding;
} else {
r->shader_binding = generate_shaders(&state);
nv2a_profile_inc_counter(NV2A_PROF_SHADER_GEN);
/* cache it */
snode->binding = r->shader_binding;
uint64_t shader_state_hash =
fast_hash((uint8_t *)&state, sizeof(ShaderState));
LruNode *node = lru_lookup(&r->shader_cache, shader_state_hash, &state);
ShaderBinding *binding = container_of(node, ShaderBinding, node);
if (!binding->initialized && !pgraph_gl_shader_load_from_memory(binding)) {
nv2a_profile_inc_counter(NV2A_PROF_SHADER_GEN);
generate_shaders(binding);
if (g_config.perf.cache_shaders) {
pgraph_gl_shader_cache_to_disk(snode);
pgraph_gl_shader_cache_to_disk(binding);
}
}
assert(binding->initialized);
r->shader_binding = binding;
pg->program_data_dirty = false;
qemu_mutex_unlock(&r->shader_cache_lock);
@ -1054,8 +1043,9 @@ void pgraph_gl_bind_shaders(PGRAPHState *pg)
NV2A_GL_DGROUP_END();
update_constants:
shader_update_constants(pg, r->shader_binding, binding_changed,
state.vertex_program, state.fixed_function);
assert(r->shader_binding);
assert(r->shader_binding->initialized);
shader_update_constants(pg, r->shader_binding, binding_changed);
}
GLuint pgraph_gl_compile_shader(const char *vs_src, const char *fs_src)