mirror of
https://github.com/mborgerson/xemu.git
synced 2025-12-01 16:10:01 +00:00
nv2a/gl: Cache shader modules
This commit is contained in:
@ -82,6 +82,30 @@ typedef struct TextureBinding {
|
|||||||
GLuint gl_texture;
|
GLuint gl_texture;
|
||||||
} TextureBinding;
|
} TextureBinding;
|
||||||
|
|
||||||
|
typedef struct ShaderModuleCacheKey {
|
||||||
|
GLenum kind;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
VshState state;
|
||||||
|
GenVshGlslOptions glsl_opts;
|
||||||
|
} vsh;
|
||||||
|
struct {
|
||||||
|
GeomState state;
|
||||||
|
GenGeomGlslOptions glsl_opts;
|
||||||
|
} geom;
|
||||||
|
struct {
|
||||||
|
PshState state;
|
||||||
|
GenPshGlslOptions glsl_opts;
|
||||||
|
} psh;
|
||||||
|
};
|
||||||
|
} ShaderModuleCacheKey;
|
||||||
|
|
||||||
|
typedef struct ShaderModuleCacheEntry {
|
||||||
|
LruNode node;
|
||||||
|
ShaderModuleCacheKey key;
|
||||||
|
GLuint gl_shader;
|
||||||
|
} ShaderModuleCacheEntry;
|
||||||
|
|
||||||
typedef struct ShaderBinding {
|
typedef struct ShaderBinding {
|
||||||
LruNode node;
|
LruNode node;
|
||||||
bool initialized;
|
bool initialized;
|
||||||
@ -175,6 +199,9 @@ typedef struct PGRAPHGLState {
|
|||||||
QemuMutex shader_cache_lock;
|
QemuMutex shader_cache_lock;
|
||||||
QemuThread shader_disk_thread;
|
QemuThread shader_disk_thread;
|
||||||
|
|
||||||
|
Lru shader_module_cache;
|
||||||
|
ShaderModuleCacheEntry *shader_module_cache_entries;
|
||||||
|
|
||||||
unsigned int zpass_pixel_count_result;
|
unsigned int zpass_pixel_count_result;
|
||||||
unsigned int gl_zpass_pixel_count_query_count;
|
unsigned int gl_zpass_pixel_count_query_count;
|
||||||
GLuint *gl_zpass_pixel_count_queries;
|
GLuint *gl_zpass_pixel_count_queries;
|
||||||
|
|||||||
@ -132,7 +132,68 @@ static void update_shader_uniform_locs(ShaderBinding *binding)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void generate_shaders(ShaderBinding *binding)
|
static void shader_module_cache_entry_init(Lru *lru, LruNode *node, void *key)
|
||||||
|
{
|
||||||
|
ShaderModuleCacheEntry *module =
|
||||||
|
container_of(node, ShaderModuleCacheEntry, node);
|
||||||
|
memcpy(&module->key, key, sizeof(ShaderModuleCacheKey));
|
||||||
|
|
||||||
|
const char *kind_str;
|
||||||
|
MString *code;
|
||||||
|
|
||||||
|
switch (module->key.kind) {
|
||||||
|
case GL_VERTEX_SHADER:
|
||||||
|
kind_str = "vertex shader";
|
||||||
|
code = pgraph_glsl_gen_vsh(&module->key.vsh.state,
|
||||||
|
module->key.vsh.glsl_opts);
|
||||||
|
break;
|
||||||
|
case GL_GEOMETRY_SHADER:
|
||||||
|
kind_str = "geometry shader";
|
||||||
|
code = pgraph_glsl_gen_geom(&module->key.geom.state,
|
||||||
|
module->key.geom.glsl_opts);
|
||||||
|
break;
|
||||||
|
case GL_FRAGMENT_SHADER:
|
||||||
|
kind_str = "fragment shader";
|
||||||
|
code = pgraph_glsl_gen_psh(&module->key.psh.state,
|
||||||
|
module->key.psh.glsl_opts);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(!"Invalid shader module kind");
|
||||||
|
kind_str = "unknown";
|
||||||
|
code = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
module->gl_shader =
|
||||||
|
create_gl_shader(module->key.kind, mstring_get_str(code), kind_str);
|
||||||
|
mstring_unref(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shader_module_cache_entry_post_evict(Lru *lru, LruNode *node)
|
||||||
|
{
|
||||||
|
ShaderModuleCacheEntry *module =
|
||||||
|
container_of(node, ShaderModuleCacheEntry, node);
|
||||||
|
glDeleteShader(module->gl_shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool shader_module_cache_entry_compare(Lru *lru, LruNode *node,
|
||||||
|
void *key)
|
||||||
|
{
|
||||||
|
ShaderModuleCacheEntry *module =
|
||||||
|
container_of(node, ShaderModuleCacheEntry, node);
|
||||||
|
return memcmp(&module->key, key, sizeof(ShaderModuleCacheKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
static GLuint get_shader_module_for_key(PGRAPHGLState *r,
|
||||||
|
ShaderModuleCacheKey *key)
|
||||||
|
{
|
||||||
|
uint64_t hash = fast_hash((void *)key, sizeof(ShaderModuleCacheKey));
|
||||||
|
LruNode *node = lru_lookup(&r->shader_module_cache, hash, key);
|
||||||
|
ShaderModuleCacheEntry *module =
|
||||||
|
container_of(node, ShaderModuleCacheEntry, node);
|
||||||
|
return module->gl_shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void generate_shaders(PGRAPHGLState *r, ShaderBinding *binding)
|
||||||
{
|
{
|
||||||
char *previous_numeric_locale = setlocale(LC_NUMERIC, NULL);
|
char *previous_numeric_locale = setlocale(LC_NUMERIC, NULL);
|
||||||
if (previous_numeric_locale) {
|
if (previous_numeric_locale) {
|
||||||
@ -144,43 +205,28 @@ static void generate_shaders(ShaderBinding *binding)
|
|||||||
GLuint program = glCreateProgram();
|
GLuint program = glCreateProgram();
|
||||||
|
|
||||||
ShaderState *state = &binding->state;
|
ShaderState *state = &binding->state;
|
||||||
|
ShaderModuleCacheKey key;
|
||||||
|
|
||||||
/* Create an optional geometry shader and find primitive type */
|
bool need_geometry_shader = pgraph_glsl_need_geom(&state->geom);
|
||||||
GLenum gl_primitive_mode = get_gl_primitive_mode(
|
if (need_geometry_shader) {
|
||||||
state->geom.polygon_front_mode, state->geom.primitive_mode);
|
memset(&key, 0, sizeof(key));
|
||||||
MString *geometry_shader_code =
|
key.kind = GL_GEOMETRY_SHADER;
|
||||||
pgraph_glsl_gen_geom(&state->geom, (GenGeomGlslOptions){ 0 });
|
key.geom.state = state->geom;
|
||||||
if (geometry_shader_code) {
|
glAttachShader(program, get_shader_module_for_key(r, &key));
|
||||||
const char* geometry_shader_code_str =
|
|
||||||
mstring_get_str(geometry_shader_code);
|
|
||||||
GLuint geometry_shader = create_gl_shader(GL_GEOMETRY_SHADER,
|
|
||||||
geometry_shader_code_str,
|
|
||||||
"geometry shader");
|
|
||||||
glAttachShader(program, geometry_shader);
|
|
||||||
mstring_unref(geometry_shader_code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* create the vertex shader */
|
/* create the vertex shader */
|
||||||
MString *vertex_shader_code = pgraph_glsl_gen_vsh(
|
memset(&key, 0, sizeof(key));
|
||||||
&state->vsh, (GenVshGlslOptions){
|
key.kind = GL_VERTEX_SHADER;
|
||||||
.prefix_outputs = geometry_shader_code != NULL,
|
key.vsh.state = state->vsh;
|
||||||
});
|
key.vsh.glsl_opts.prefix_outputs = need_geometry_shader;
|
||||||
GLuint vertex_shader = create_gl_shader(GL_VERTEX_SHADER,
|
glAttachShader(program, get_shader_module_for_key(r, &key));
|
||||||
mstring_get_str(vertex_shader_code),
|
|
||||||
"vertex shader");
|
|
||||||
glAttachShader(program, vertex_shader);
|
|
||||||
mstring_unref(vertex_shader_code);
|
|
||||||
|
|
||||||
/* generate a fragment shader from register combiners */
|
/* generate a fragment shader from register combiners */
|
||||||
MString *fragment_shader_code =
|
memset(&key, 0, sizeof(key));
|
||||||
pgraph_glsl_gen_psh(&state->psh, (GenPshGlslOptions){ 0 });
|
key.kind = GL_FRAGMENT_SHADER;
|
||||||
const char *fragment_shader_code_str =
|
key.psh.state = state->psh;
|
||||||
mstring_get_str(fragment_shader_code);
|
glAttachShader(program, get_shader_module_for_key(r, &key));
|
||||||
GLuint fragment_shader = create_gl_shader(GL_FRAGMENT_SHADER,
|
|
||||||
fragment_shader_code_str,
|
|
||||||
"fragment shader");
|
|
||||||
glAttachShader(program, fragment_shader);
|
|
||||||
mstring_unref(fragment_shader_code);
|
|
||||||
|
|
||||||
/* link the program */
|
/* link the program */
|
||||||
glLinkProgram(program);
|
glLinkProgram(program);
|
||||||
@ -196,7 +242,8 @@ static void generate_shaders(ShaderBinding *binding)
|
|||||||
glUseProgram(program);
|
glUseProgram(program);
|
||||||
|
|
||||||
binding->gl_program = program;
|
binding->gl_program = program;
|
||||||
binding->gl_primitive_mode = gl_primitive_mode;
|
binding->gl_primitive_mode = get_gl_primitive_mode(
|
||||||
|
state->geom.polygon_front_mode, state->geom.primitive_mode);
|
||||||
binding->initialized = true;
|
binding->initialized = true;
|
||||||
|
|
||||||
set_texture_sampler_uniforms(binding);
|
set_texture_sampler_uniforms(binding);
|
||||||
@ -521,6 +568,20 @@ void pgraph_gl_init_shaders(PGRAPHState *pg)
|
|||||||
|
|
||||||
qemu_thread_create(&r->shader_disk_thread, "pgraph.renderer_state->shader_cache",
|
qemu_thread_create(&r->shader_disk_thread, "pgraph.renderer_state->shader_cache",
|
||||||
shader_reload_lru_from_disk, pg, QEMU_THREAD_JOINABLE);
|
shader_reload_lru_from_disk, pg, QEMU_THREAD_JOINABLE);
|
||||||
|
|
||||||
|
/* FIXME: Make this configurable */
|
||||||
|
const size_t shader_module_cache_size = 50*1024;
|
||||||
|
lru_init(&r->shader_module_cache);
|
||||||
|
r->shader_module_cache_entries =
|
||||||
|
g_malloc_n(shader_module_cache_size, sizeof(ShaderModuleCacheEntry));
|
||||||
|
assert(r->shader_module_cache_entries != NULL);
|
||||||
|
for (int i = 0; i < shader_module_cache_size; i++) {
|
||||||
|
lru_add_free(&r->shader_module_cache, &r->shader_module_cache_entries[i].node);
|
||||||
|
}
|
||||||
|
|
||||||
|
r->shader_module_cache.init_node = shader_module_cache_entry_init;
|
||||||
|
r->shader_module_cache.compare_nodes = shader_module_cache_entry_compare;
|
||||||
|
r->shader_module_cache.post_node_evict = shader_module_cache_entry_post_evict;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pgraph_gl_finalize_shaders(PGRAPHState *pg)
|
void pgraph_gl_finalize_shaders(PGRAPHState *pg)
|
||||||
@ -532,6 +593,10 @@ void pgraph_gl_finalize_shaders(PGRAPHState *pg)
|
|||||||
free(r->shader_cache_entries);
|
free(r->shader_cache_entries);
|
||||||
r->shader_cache_entries = NULL;
|
r->shader_cache_entries = NULL;
|
||||||
|
|
||||||
|
lru_flush(&r->shader_module_cache);
|
||||||
|
g_free(r->shader_module_cache_entries);
|
||||||
|
r->shader_module_cache_entries = NULL;
|
||||||
|
|
||||||
qemu_mutex_destroy(&r->shader_cache_lock);
|
qemu_mutex_destroy(&r->shader_cache_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -730,7 +795,7 @@ void pgraph_gl_bind_shaders(PGRAPHState *pg)
|
|||||||
|
|
||||||
if (!binding->initialized && !pgraph_gl_shader_load_from_memory(binding)) {
|
if (!binding->initialized && !pgraph_gl_shader_load_from_memory(binding)) {
|
||||||
nv2a_profile_inc_counter(NV2A_PROF_SHADER_GEN);
|
nv2a_profile_inc_counter(NV2A_PROF_SHADER_GEN);
|
||||||
generate_shaders(binding);
|
generate_shaders(r, binding);
|
||||||
if (g_config.perf.cache_shaders) {
|
if (g_config.perf.cache_shaders) {
|
||||||
pgraph_gl_shader_cache_to_disk(binding);
|
pgraph_gl_shader_cache_to_disk(binding);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,6 +39,74 @@ void pgraph_glsl_set_geom_state(PGRAPHState *pg, GeomState *state)
|
|||||||
NV_PGRAPH_CONTROL_3_SHADEMODE_SMOOTH;
|
NV_PGRAPH_CONTROL_3_SHADEMODE_SMOOTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool pgraph_glsl_need_geom(const GeomState *state)
|
||||||
|
{
|
||||||
|
/* FIXME: Missing support for 2-sided-poly mode */
|
||||||
|
assert(state->polygon_front_mode == state->polygon_back_mode);
|
||||||
|
enum ShaderPolygonMode polygon_mode = state->polygon_front_mode;
|
||||||
|
|
||||||
|
/* POINT mode shouldn't require any special work */
|
||||||
|
if (polygon_mode == POLY_MODE_POINT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (state->primitive_mode) {
|
||||||
|
case PRIM_TYPE_TRIANGLES:
|
||||||
|
if (polygon_mode == POLY_MODE_FILL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case PRIM_TYPE_TRIANGLE_STRIP:
|
||||||
|
if (polygon_mode == POLY_MODE_FILL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
assert(polygon_mode == POLY_MODE_LINE);
|
||||||
|
return true;
|
||||||
|
case PRIM_TYPE_TRIANGLE_FAN:
|
||||||
|
if (polygon_mode == POLY_MODE_FILL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
assert(polygon_mode == POLY_MODE_LINE);
|
||||||
|
return true;
|
||||||
|
case PRIM_TYPE_QUADS:
|
||||||
|
if (polygon_mode == POLY_MODE_LINE) {
|
||||||
|
return true;
|
||||||
|
} else if (polygon_mode == POLY_MODE_FILL) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PRIM_TYPE_QUAD_STRIP:
|
||||||
|
if (polygon_mode == POLY_MODE_LINE) {
|
||||||
|
return true;
|
||||||
|
} else if (polygon_mode == POLY_MODE_FILL) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PRIM_TYPE_POLYGON:
|
||||||
|
if (polygon_mode == POLY_MODE_LINE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (polygon_mode == POLY_MODE_FILL) {
|
||||||
|
if (state->smooth_shading) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MString *pgraph_glsl_gen_geom(const GeomState *state, GenGeomGlslOptions opts)
|
MString *pgraph_glsl_gen_geom(const GeomState *state, GenGeomGlslOptions opts)
|
||||||
{
|
{
|
||||||
/* FIXME: Missing support for 2-sided-poly mode */
|
/* FIXME: Missing support for 2-sided-poly mode */
|
||||||
|
|||||||
@ -38,6 +38,8 @@ typedef struct GenGeomGlslOptions {
|
|||||||
|
|
||||||
void pgraph_glsl_set_geom_state(PGRAPHState *pg, GeomState *geom);
|
void pgraph_glsl_set_geom_state(PGRAPHState *pg, GeomState *geom);
|
||||||
|
|
||||||
|
bool pgraph_glsl_need_geom(const GeomState *state);
|
||||||
|
|
||||||
MString *pgraph_glsl_gen_geom(const GeomState *state, GenGeomGlslOptions opts);
|
MString *pgraph_glsl_gen_geom(const GeomState *state, GenGeomGlslOptions opts);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user