mirror of https://github.com/xemu-project/xemu.git
nv2a/gl: Unify ShaderBinding and ShaderLruNode
This commit is contained in:
parent
e7047efef2
commit
510c280b05
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue