mirror of https://github.com/xemu-project/xemu.git
nv2a/gl: Cache shader modules
This commit is contained in:
parent
c11c098ec6
commit
93598e9648
|
@ -82,6 +82,30 @@ typedef struct TextureBinding {
|
|||
GLuint gl_texture;
|
||||
} 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 {
|
||||
LruNode node;
|
||||
bool initialized;
|
||||
|
@ -175,6 +199,9 @@ typedef struct PGRAPHGLState {
|
|||
QemuMutex shader_cache_lock;
|
||||
QemuThread shader_disk_thread;
|
||||
|
||||
Lru shader_module_cache;
|
||||
ShaderModuleCacheEntry *shader_module_cache_entries;
|
||||
|
||||
unsigned int zpass_pixel_count_result;
|
||||
unsigned int gl_zpass_pixel_count_query_count;
|
||||
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);
|
||||
if (previous_numeric_locale) {
|
||||
|
@ -144,43 +205,28 @@ static void generate_shaders(ShaderBinding *binding)
|
|||
GLuint program = glCreateProgram();
|
||||
|
||||
ShaderState *state = &binding->state;
|
||||
ShaderModuleCacheKey key;
|
||||
|
||||
/* Create an optional geometry shader and find primitive type */
|
||||
GLenum gl_primitive_mode = get_gl_primitive_mode(
|
||||
state->geom.polygon_front_mode, state->geom.primitive_mode);
|
||||
MString *geometry_shader_code =
|
||||
pgraph_glsl_gen_geom(&state->geom, (GenGeomGlslOptions){ 0 });
|
||||
if (geometry_shader_code) {
|
||||
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);
|
||||
bool need_geometry_shader = pgraph_glsl_need_geom(&state->geom);
|
||||
if (need_geometry_shader) {
|
||||
memset(&key, 0, sizeof(key));
|
||||
key.kind = GL_GEOMETRY_SHADER;
|
||||
key.geom.state = state->geom;
|
||||
glAttachShader(program, get_shader_module_for_key(r, &key));
|
||||
}
|
||||
|
||||
/* create the vertex shader */
|
||||
MString *vertex_shader_code = pgraph_glsl_gen_vsh(
|
||||
&state->vsh, (GenVshGlslOptions){
|
||||
.prefix_outputs = geometry_shader_code != NULL,
|
||||
});
|
||||
GLuint vertex_shader = create_gl_shader(GL_VERTEX_SHADER,
|
||||
mstring_get_str(vertex_shader_code),
|
||||
"vertex shader");
|
||||
glAttachShader(program, vertex_shader);
|
||||
mstring_unref(vertex_shader_code);
|
||||
memset(&key, 0, sizeof(key));
|
||||
key.kind = GL_VERTEX_SHADER;
|
||||
key.vsh.state = state->vsh;
|
||||
key.vsh.glsl_opts.prefix_outputs = need_geometry_shader;
|
||||
glAttachShader(program, get_shader_module_for_key(r, &key));
|
||||
|
||||
/* generate a fragment shader from register combiners */
|
||||
MString *fragment_shader_code =
|
||||
pgraph_glsl_gen_psh(&state->psh, (GenPshGlslOptions){ 0 });
|
||||
const char *fragment_shader_code_str =
|
||||
mstring_get_str(fragment_shader_code);
|
||||
GLuint fragment_shader = create_gl_shader(GL_FRAGMENT_SHADER,
|
||||
fragment_shader_code_str,
|
||||
"fragment shader");
|
||||
glAttachShader(program, fragment_shader);
|
||||
mstring_unref(fragment_shader_code);
|
||||
memset(&key, 0, sizeof(key));
|
||||
key.kind = GL_FRAGMENT_SHADER;
|
||||
key.psh.state = state->psh;
|
||||
glAttachShader(program, get_shader_module_for_key(r, &key));
|
||||
|
||||
/* link the program */
|
||||
glLinkProgram(program);
|
||||
|
@ -196,7 +242,8 @@ static void generate_shaders(ShaderBinding *binding)
|
|||
glUseProgram(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;
|
||||
|
||||
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",
|
||||
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)
|
||||
|
@ -532,6 +593,10 @@ void pgraph_gl_finalize_shaders(PGRAPHState *pg)
|
|||
free(r->shader_cache_entries);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -730,7 +795,7 @@ void pgraph_gl_bind_shaders(PGRAPHState *pg)
|
|||
|
||||
if (!binding->initialized && !pgraph_gl_shader_load_from_memory(binding)) {
|
||||
nv2a_profile_inc_counter(NV2A_PROF_SHADER_GEN);
|
||||
generate_shaders(binding);
|
||||
generate_shaders(r, binding);
|
||||
if (g_config.perf.cache_shaders) {
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
/* 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);
|
||||
|
||||
bool pgraph_glsl_need_geom(const GeomState *state);
|
||||
|
||||
MString *pgraph_glsl_gen_geom(const GeomState *state, GenGeomGlslOptions opts);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue