mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Re-apply stale VRAM fix and SPIRV cache persistence
- gl/surface.c, vk/surface.c: Mark DIRTY_MEMORY_NV2A on download to prevent stale buffer reads after surface write-back (PR 2800 fix) - vk/draw.c: Fallback to empty pipeline cache on corrupt data (PR 2799) - vk/glsl.c: Persist compiled SPIRV to disk cache (spirv_cache.bin) to avoid re-compiling shaders on subsequent launches (PR 2799) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
e71284cfbf
commit
1d10f6cb2a
@@ -1349,6 +1349,9 @@ static void surface_download(NV2AState *d, SurfaceBinding *surface, bool force)
|
||||
memory_region_set_client_dirty(d->vram, surface->vram_addr,
|
||||
surface->pitch * surface->height,
|
||||
DIRTY_MEMORY_NV2A_TEX);
|
||||
memory_region_set_client_dirty(d->vram, surface->vram_addr,
|
||||
surface->pitch * surface->height,
|
||||
DIRTY_MEMORY_NV2A);
|
||||
|
||||
surface->download_pending = false;
|
||||
surface->draw_dirty = false;
|
||||
|
||||
@@ -150,8 +150,14 @@ static void init_pipeline_cache(PGRAPHState *pg)
|
||||
}
|
||||
#endif
|
||||
|
||||
VK_CHECK(vkCreatePipelineCache(r->device, &cache_info, NULL,
|
||||
&r->vk_pipeline_cache));
|
||||
VkResult cache_result = vkCreatePipelineCache(r->device, &cache_info, NULL,
|
||||
&r->vk_pipeline_cache);
|
||||
if (cache_result != VK_SUCCESS) {
|
||||
cache_info.initialDataSize = 0;
|
||||
cache_info.pInitialData = NULL;
|
||||
VK_CHECK(vkCreatePipelineCache(r->device, &cache_info, NULL,
|
||||
&r->vk_pipeline_cache));
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
g_free(cache_data);
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/fast-hash.h"
|
||||
#include "ui/xemu-settings.h"
|
||||
#include "renderer.h"
|
||||
|
||||
@@ -24,6 +26,168 @@
|
||||
#include <glslang/Include/glslang_c_interface.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define SPIRV_CACHE_VERSION 1
|
||||
#define SPIRV_CACHE_MAGIC 0x53505643 /* 'SPVC' */
|
||||
|
||||
typedef struct SpirvCacheEntry {
|
||||
uint64_t hash;
|
||||
uint32_t spirv_len;
|
||||
uint8_t *spirv_data;
|
||||
} SpirvCacheEntry;
|
||||
|
||||
static GHashTable *spirv_cache;
|
||||
static bool spirv_cache_dirty;
|
||||
static bool spirv_atexit_registered;
|
||||
|
||||
static char *get_spirv_cache_path(void)
|
||||
{
|
||||
const char *base = xemu_settings_get_base_path();
|
||||
if (!base) {
|
||||
return NULL;
|
||||
}
|
||||
return g_build_filename(base, "spirv_cache.bin", NULL);
|
||||
}
|
||||
|
||||
static void spirv_cache_entry_free(gpointer data)
|
||||
{
|
||||
SpirvCacheEntry *entry = data;
|
||||
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(entry->spirv_data);
|
||||
g_free(entry);
|
||||
}
|
||||
|
||||
static void load_spirv_cache(void)
|
||||
{
|
||||
if (spirv_cache) {
|
||||
return;
|
||||
}
|
||||
|
||||
spirv_cache = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL,
|
||||
spirv_cache_entry_free);
|
||||
|
||||
g_autofree char *path = get_spirv_cache_path();
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t magic, version, count;
|
||||
if (fread(&magic, 4, 1, f) != 1 || magic != SPIRV_CACHE_MAGIC ||
|
||||
fread(&version, 4, 1, f) != 1 || version != SPIRV_CACHE_VERSION ||
|
||||
fread(&count, 4, 1, f) != 1) {
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
uint64_t hash;
|
||||
uint32_t spirv_len;
|
||||
if (fread(&hash, 8, 1, f) != 1 ||
|
||||
fread(&spirv_len, 4, 1, f) != 1 || spirv_len == 0 ||
|
||||
spirv_len > 1024 * 1024) {
|
||||
break;
|
||||
}
|
||||
|
||||
SpirvCacheEntry *entry = g_malloc(sizeof(*entry));
|
||||
entry->hash = hash;
|
||||
entry->spirv_len = spirv_len;
|
||||
entry->spirv_data = g_malloc(spirv_len);
|
||||
|
||||
if (fread(entry->spirv_data, 1, spirv_len, f) != spirv_len) {
|
||||
spirv_cache_entry_free(entry);
|
||||
break;
|
||||
}
|
||||
|
||||
g_hash_table_insert(spirv_cache, &entry->hash, entry);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static void save_spirv_cache(void)
|
||||
{
|
||||
if (!spirv_cache || !spirv_cache_dirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_autofree char *path = get_spirv_cache_path();
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
uint32_t magic = SPIRV_CACHE_MAGIC;
|
||||
uint32_t version = SPIRV_CACHE_VERSION;
|
||||
uint32_t count = g_hash_table_size(spirv_cache);
|
||||
|
||||
ok = ok && fwrite(&magic, 4, 1, f) == 1;
|
||||
ok = ok && fwrite(&version, 4, 1, f) == 1;
|
||||
ok = ok && fwrite(&count, 4, 1, f) == 1;
|
||||
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
g_hash_table_iter_init(&iter, spirv_cache);
|
||||
while (ok && g_hash_table_iter_next(&iter, &key, &value)) {
|
||||
SpirvCacheEntry *entry = value;
|
||||
ok = ok && fwrite(&entry->hash, 8, 1, f) == 1;
|
||||
ok = ok && fwrite(&entry->spirv_len, 4, 1, f) == 1;
|
||||
ok = ok && fwrite(entry->spirv_data, 1, entry->spirv_len, f) ==
|
||||
entry->spirv_len;
|
||||
}
|
||||
|
||||
if (fclose(f) == 0 && ok) {
|
||||
spirv_cache_dirty = false;
|
||||
} else {
|
||||
remove(path);
|
||||
}
|
||||
}
|
||||
|
||||
static GByteArray *spirv_cache_lookup(const char *glsl)
|
||||
{
|
||||
if (!spirv_cache) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t hash = fast_hash((const uint8_t *)glsl, strlen(glsl));
|
||||
SpirvCacheEntry *entry = g_hash_table_lookup(spirv_cache, &hash);
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GByteArray *spirv = g_byte_array_sized_new(entry->spirv_len);
|
||||
g_byte_array_append(spirv, entry->spirv_data, entry->spirv_len);
|
||||
return spirv;
|
||||
}
|
||||
|
||||
static void spirv_cache_insert(const char *glsl, GByteArray *spirv)
|
||||
{
|
||||
if (!spirv_cache || !spirv) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t hash = fast_hash((const uint8_t *)glsl, strlen(glsl));
|
||||
SpirvCacheEntry *entry = g_malloc(sizeof(*entry));
|
||||
entry->hash = hash;
|
||||
entry->spirv_len = spirv->len;
|
||||
entry->spirv_data = g_malloc(spirv->len);
|
||||
memcpy(entry->spirv_data, spirv->data, spirv->len);
|
||||
g_hash_table_insert(spirv_cache, &entry->hash, entry);
|
||||
spirv_cache_dirty = true;
|
||||
}
|
||||
|
||||
static const glslang_resource_t
|
||||
resource_limits = { .max_lights = 32,
|
||||
.max_clip_planes = 6,
|
||||
@@ -133,10 +297,21 @@ static const glslang_resource_t
|
||||
void pgraph_vk_init_glsl_compiler(void)
|
||||
{
|
||||
glslang_initialize_process();
|
||||
load_spirv_cache();
|
||||
|
||||
if (!spirv_atexit_registered) {
|
||||
atexit(save_spirv_cache);
|
||||
spirv_atexit_registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
void pgraph_vk_finalize_glsl_compiler(void)
|
||||
{
|
||||
save_spirv_cache();
|
||||
if (spirv_cache) {
|
||||
g_hash_table_destroy(spirv_cache);
|
||||
spirv_cache = NULL;
|
||||
}
|
||||
glslang_finalize_process();
|
||||
}
|
||||
|
||||
@@ -370,8 +545,12 @@ ShaderModuleInfo *pgraph_vk_create_shader_module_from_glsl(
|
||||
ShaderModuleInfo *info = g_malloc0(sizeof(*info));
|
||||
info->refcnt = 0;
|
||||
info->glsl = strdup(glsl);
|
||||
info->spirv = pgraph_vk_compile_glsl_to_spv(
|
||||
vk_shader_stage_to_glslang_stage(stage), glsl);
|
||||
info->spirv = spirv_cache_lookup(glsl);
|
||||
if (!info->spirv) {
|
||||
info->spirv = pgraph_vk_compile_glsl_to_spv(
|
||||
vk_shader_stage_to_glslang_stage(stage), glsl);
|
||||
spirv_cache_insert(glsl, info->spirv);
|
||||
}
|
||||
info->module = pgraph_vk_create_shader_module_from_spv(r, info->spirv);
|
||||
init_layout_from_spv(info);
|
||||
return info;
|
||||
|
||||
@@ -494,6 +494,9 @@ static void download_surface(NV2AState *d, SurfaceBinding *surface, bool force)
|
||||
memory_region_set_client_dirty(d->vram, surface->vram_addr,
|
||||
surface->pitch * surface->height,
|
||||
DIRTY_MEMORY_NV2A_TEX);
|
||||
memory_region_set_client_dirty(d->vram, surface->vram_addr,
|
||||
surface->pitch * surface->height,
|
||||
DIRTY_MEMORY_NV2A);
|
||||
|
||||
surface->download_pending = false;
|
||||
surface->draw_dirty = false;
|
||||
|
||||
Reference in New Issue
Block a user