nv2a: Cache shaders to disk

This commit is contained in:
Antonio Abbatangelo
2022-09-10 12:52:51 -07:00
committed by mborgerson
parent f7b2acbb79
commit 080022833d
13 changed files with 681 additions and 148 deletions
+3 -3
View File
@@ -191,6 +191,6 @@ perf:
hard_fpu:
type: bool
default: true
# cache_shaders:
# type: bool
# default: true
cache_shaders:
type: bool
default: true
+15
View File
@@ -129,6 +129,21 @@ LruNode *lru_evict_one(Lru *lru)
return found;
}
static inline
bool lru_contains_hash(Lru *lru, uint64_t hash)
{
unsigned int bin = lru_hash_to_bin(lru, hash);
LruNode *iter;
QTAILQ_FOREACH(iter, &lru->bins[bin], next_bin) {
if (iter->hash == hash) {
return true;
}
}
return false;
}
static inline
LruNode *lru_lookup(Lru *lru, uint64_t hash, void *key)
{
+8
View File
@@ -380,6 +380,14 @@ static void nv2a_vm_state_change(void *opaque, bool running, RunState state)
nv2a_lock_fifo(d);
qatomic_set(&d->pfifo.halt, false);
nv2a_unlock_fifo(d);
} else if (state == RUN_STATE_SHUTDOWN) {
nv2a_lock_fifo(d);
qatomic_set(&d->pgraph.shader_cache_writeback_pending, true);
qemu_event_reset(&d->pgraph.shader_cache_writeback_complete);
nv2a_unlock_fifo(d);
qemu_mutex_unlock_iothread();
qemu_event_wait(&d->pgraph.shader_cache_writeback_complete);
qemu_mutex_lock_iothread();
}
}
+8 -3
View File
@@ -305,12 +305,15 @@ typedef struct PGRAPHState {
hwaddr dma_a, dma_b;
Lru texture_cache;
struct TextureLruNode *texture_cache_entries;
TextureLruNode *texture_cache_entries;
bool texture_dirty[NV2A_MAX_TEXTURES];
TextureBinding *texture_binding[NV2A_MAX_TEXTURES];
GHashTable *shader_cache;
Lru shader_cache;
ShaderLruNode *shader_cache_entries;
ShaderBinding *shader_binding;
QemuMutex shader_cache_lock;
QemuThread shader_disk_thread;
bool texture_matrix_enable[NV2A_MAX_TEXTURES];
@@ -370,7 +373,7 @@ typedef struct PGRAPHState {
uint16_t compressed_attrs;
Lru element_cache;
struct VertexLruNode *element_cache_entries;
VertexLruNode *element_cache_entries;
unsigned int inline_array_length;
uint32_t inline_array[NV2A_MAX_BATCH_LENGTH];
@@ -403,10 +406,12 @@ typedef struct PGRAPHState {
bool download_dirty_surfaces_pending;
bool flush_pending;
bool gl_sync_pending;
bool shader_cache_writeback_pending;
QemuEvent downloads_complete;
QemuEvent dirty_surfaces_download_complete;
QemuEvent flush_complete;
QemuEvent gl_sync_complete;
QemuEvent shader_cache_writeback_complete;
unsigned int surface_scale_factor;
uint8_t *scale_buf;
+5 -1
View File
@@ -452,7 +452,8 @@ static void process_requests(NV2AState *d)
if (qatomic_read(&d->pgraph.downloads_pending) ||
qatomic_read(&d->pgraph.download_dirty_surfaces_pending) ||
qatomic_read(&d->pgraph.gl_sync_pending) ||
qatomic_read(&d->pgraph.flush_pending)) {
qatomic_read(&d->pgraph.flush_pending) ||
qatomic_read(&d->pgraph.shader_cache_writeback_pending)) {
qemu_mutex_unlock(&d->pfifo.lock);
qemu_mutex_lock(&d->pgraph.lock);
if (qatomic_read(&d->pgraph.downloads_pending)) {
@@ -467,6 +468,9 @@ static void process_requests(NV2AState *d)
if (qatomic_read(&d->pgraph.flush_pending)) {
pgraph_flush(d);
}
if (qatomic_read(&d->pgraph.shader_cache_writeback_pending)) {
shader_write_cache_reload_list(&d->pgraph);
}
qemu_mutex_unlock(&d->pgraph.lock);
qemu_mutex_lock(&d->pfifo.lock);
}
+19 -22
View File
@@ -451,8 +451,6 @@ static bool vertex_cache_entry_compare(Lru *lru, LruNode *node, void *key)
static void pgraph_mark_textures_possibly_dirty(NV2AState *d, hwaddr addr, hwaddr size);
static bool pgraph_check_texture_dirty(NV2AState *d, hwaddr addr, hwaddr size);
static guint shader_hash(gconstpointer key);
static gboolean shader_equal(gconstpointer a, gconstpointer b);
static unsigned int kelvin_map_stencil_op(uint32_t parameter);
static unsigned int kelvin_map_polygon_mode(uint32_t parameter);
static unsigned int kelvin_map_texgen(uint32_t parameter, unsigned int channel);
@@ -3958,10 +3956,12 @@ void pgraph_init(NV2AState *d)
pg->downloads_pending = false;
qemu_mutex_init(&pg->lock);
qemu_mutex_init(&pg->shader_cache_lock);
qemu_event_init(&pg->gl_sync_complete, false);
qemu_event_init(&pg->downloads_complete, false);
qemu_event_init(&pg->dirty_surfaces_download_complete, false);
qemu_event_init(&pg->flush_complete, false);
qemu_event_init(&pg->shader_cache_writeback_complete, false);
/* fire up opengl */
glo_set_current(g_nv2a_context_render);
@@ -4018,7 +4018,7 @@ void pgraph_init(NV2AState *d)
pg->element_cache.init_node = vertex_cache_entry_init;
pg->element_cache.compare_nodes = vertex_cache_entry_compare;
pg->shader_cache = g_hash_table_new(shader_hash, shader_equal);
shader_cache_init(pg);
pg->material_alpha = 0.0f;
SET_MASK(pg->regs[NV_PGRAPH_CONTROL_3], NV_PGRAPH_CONTROL_3_SHADEMODE,
@@ -4053,6 +4053,7 @@ void pgraph_init(NV2AState *d)
void pgraph_destroy(PGRAPHState *pg)
{
qemu_mutex_destroy(&pg->lock);
qemu_mutex_destroy(&pg->shader_cache_lock);
glo_set_current(g_nv2a_context_render);
@@ -4060,7 +4061,9 @@ void pgraph_destroy(PGRAPHState *pg)
glDeleteFramebuffers(1, &pg->gl_framebuffer);
// TODO: clear out shader cached
// Clear out shader cache
shader_write_cache_reload_list(pg);
free(pg->shader_cache_entries);
// Clear out texture cache
lru_flush(&pg->texture_cache);
@@ -4640,20 +4643,25 @@ static void pgraph_bind_shaders(PGRAPHState *pg)
state.psh.conv_tex[i] = kernel;
}
ShaderBinding* cached_shader = (ShaderBinding*)g_hash_table_lookup(pg->shader_cache, &state);
if (cached_shader) {
pg->shader_binding = cached_shader;
uint64_t shader_state_hash = fast_hash((uint8_t*) &state, sizeof(ShaderState));
qemu_mutex_lock(&pg->shader_cache_lock);
LruNode *node = lru_lookup(&pg->shader_cache, shader_state_hash, &state);
ShaderLruNode *snode = container_of(node, ShaderLruNode, node);
if (snode->binding || shader_load_from_memory(snode)) {
pg->shader_binding = snode->binding;
} else {
pg->shader_binding = generate_shaders(&state);
nv2a_profile_inc_counter(NV2A_PROF_SHADER_GEN);
/* cache it */
ShaderState *cache_state = (ShaderState *)g_malloc(sizeof(*cache_state));
memcpy(cache_state, &state, sizeof(*cache_state));
g_hash_table_insert(pg->shader_cache, cache_state,
(gpointer)pg->shader_binding);
snode->binding = pg->shader_binding;
if (g_config.perf.cache_shaders) {
shader_cache_to_disk(snode);
}
}
qemu_mutex_unlock(&pg->shader_cache_lock);
binding_changed = (pg->shader_binding != old_binding);
if (binding_changed) {
nv2a_profile_inc_counter(NV2A_PROF_SHADER_BIND);
@@ -7649,17 +7657,6 @@ static bool texture_cache_entry_compare(Lru *lru, LruNode *node, void *key)
return memcmp(&tnode->key, key, sizeof(TextureKey));
}
/* hash and equality for shader cache hash table */
static guint shader_hash(gconstpointer key)
{
return fast_hash((const uint8_t *)key, sizeof(ShaderState));
}
static gboolean shader_equal(gconstpointer a, gconstpointer b)
{
const ShaderState *as = (const ShaderState *)a, *bs = (const ShaderState *)b;
return memcmp(as, bs, sizeof(ShaderState)) == 0;
}
static unsigned int kelvin_map_stencil_op(uint32_t parameter)
{
unsigned int op;
+535 -109
View File
File diff suppressed because it is too large Load Diff
+22
View File
@@ -21,12 +21,14 @@
#ifndef HW_NV2A_SHADERS_H
#define HW_NV2A_SHADERS_H
#include "qemu/thread.h"
#include "qapi/qmp/qstring.h"
#include "gl/gloffscreen.h"
#include "nv2a_regs.h"
#include "vsh.h"
#include "psh.h"
#include "lru.h"
enum ShaderPrimitiveMode {
PRIM_TYPE_INVALID,
@@ -136,6 +138,26 @@ 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 PGRAPHState PGRAPHState;
GLenum get_gl_primitive_mode(enum ShaderPolygonMode polygon_mode, enum ShaderPrimitiveMode primitive_mode);
void update_shader_constant_locations(ShaderBinding *binding, const ShaderState *state);
ShaderBinding *generate_shaders(const ShaderState *state);
void shader_cache_init(PGRAPHState *pg);
void shader_write_cache_reload_list(PGRAPHState *pg);
bool shader_load_from_memory(ShaderLruNode *snode);
void shader_cache_to_disk(ShaderLruNode *snode);
#endif
+3
View File
@@ -584,6 +584,9 @@ int qemu_open(const char *name, int flags, Error **errp);
int qemu_create(const char *name, int flags, mode_t mode, Error **errp);
int qemu_close(int fd);
int qemu_unlink(const char *name);
#ifdef XBOX
int qemu_mkdir(const char *path);
#endif
#ifndef _WIN32
int qemu_dup_flags(int fd, int flags);
int qemu_dup(int fd);
+19 -8
View File
@@ -69,18 +69,32 @@ void xemu_settings_set_path(const char *path)
fprintf(stderr, "%s: config path: %s\n", __func__, settings_path);
}
const char *xemu_settings_get_path(void)
const char *xemu_settings_get_base_path(void)
{
if (settings_path != NULL) {
return settings_path;
static const char *base_path = NULL;
if (base_path != NULL) {
return base_path;
}
char *base = xemu_settings_detect_portable_mode()
? SDL_GetBasePath()
: SDL_GetPrefPath("xemu", "xemu");
assert(base != NULL);
settings_path = g_strdup_printf("%s%s", base, filename);
base_path = g_strdup(base);
SDL_free(base);
fprintf(stderr, "%s: base path: %s\n", __func__, base_path);
return base_path;
}
const char *xemu_settings_get_path(void)
{
if (settings_path != NULL) {
return settings_path;
}
const char *base = xemu_settings_get_base_path();
assert(base != NULL);
settings_path = g_strdup_printf("%s%s", base, filename);
fprintf(stderr, "%s: config path: %s\n", __func__, settings_path);
return settings_path;
}
@@ -92,12 +106,9 @@ const char *xemu_settings_get_default_eeprom_path(void)
return eeprom_path;
}
char *base = xemu_settings_detect_portable_mode()
? SDL_GetBasePath()
: SDL_GetPrefPath("xemu", "xemu");
const char *base = xemu_settings_get_base_path();
assert(base != NULL);
eeprom_path = g_strdup_printf("%s%s", base, "eeprom.bin");
SDL_free(base);
return eeprom_path;
}
+3
View File
@@ -38,6 +38,9 @@ extern struct config g_config;
// Override the default config file paths
void xemu_settings_set_path(const char *path);
// Get the path of the base settings dir
const char *xemu_settings_get_base_path(void);
// Get path of the config file on disk
const char *xemu_settings_get_path(void);
+2 -2
View File
@@ -56,8 +56,8 @@ void MainMenuGeneralView::Draw()
"Use hardware-accelerated floating point emulation (requires restart)");
#endif
// toggle("Cache shaders to disk", &g_config.perf.cache_shaders,
// "Reduce stutter in games by caching previously generated shaders");
Toggle("Cache shaders to disk", &g_config.perf.cache_shaders,
"Reduce stutter in games by caching previously generated shaders");
SectionTitle("Miscellaneous");
Toggle("Skip startup animation", &g_config.general.skip_boot_anim,
+39
View File
@@ -451,9 +451,48 @@ int qemu_unlink(const char *name)
return 0;
}
#ifdef _WIN32
wchar_t *namew = g_utf8_to_utf16(name, -1, NULL, NULL, NULL);
if (!namew) {
return -1;
}
int ret = _wunlink(namew);
g_free(namew);
return ret;
#else
return unlink(name);
#endif
}
#ifdef XBOX
/*
* Create a directory on the filesystem
*
* Returns: On success, zero is returned. On error, -1 is returned,
* and errno is set appropriately.
*/
int qemu_mkdir(const char *path)
{
#ifdef _WIN32
wchar_t *wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
if (!wpath) {
return -1;
}
BOOL dirResult = CreateDirectoryW(wpath, 0);
g_free(wpath);
if (!dirResult) {
return -1;
}
#else
return mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
#endif
}
#endif
/*
* A variant of write(2) which handles partial write.
*