From 837926d8e18ca93ed487277f4b4ab2d2b9f890aa Mon Sep 17 00:00:00 2001 From: chrg6 Date: Mon, 9 Nov 2026 08:19:13 -0500 Subject: [PATCH] Let shader_cache_flush = 1 flush the shader cache files to the disk again. The faster cache writes leave the .spv, .res.json and .use files unflushed and rely on the checks every read makes. The setting puts the earlier flushed, written-through writes back for a drive or setup where a torn file shows up. Co-Authored-By: Claude Opus 5.5 --- nx2navi.ini.example | 3 +++ runtime/cfg.c | 6 +++--- runtime/gfx_shader.c | 16 +++++++++++++--- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/nx2navi.ini.example b/nx2navi.ini.example index 2ee99ddc..cbb85b6e 100644 --- a/nx2navi.ini.example +++ b/nx2navi.ini.example @@ -150,6 +150,9 @@ # skip push constants that did not change. # bind_skip = 1 # shader_memo = 1 +# 1 flushes every shader cache file to the disk before it is used, as earlier versions did; 0 (default) flushes only +# the stored game code, which makes shader preparation about twice as fast (a torn file is translated again). +# shader_cache_flush = 0 # pipe_memo = 1 # tex_memo = 1 # desc_template = 1 diff --git a/runtime/cfg.c b/runtime/cfg.c index 61aeb9b3..6aee9bb4 100644 --- a/runtime/cfg.c +++ b/runtime/cfg.c @@ -56,7 +56,7 @@ static const char *const known[] = { "REFLEX", "RENDER_SCALE", "REPLAY_ASYNC", "RTSS", "REPLAY_CHECK", "RESOLVE_CHECK", "RESTART_WAIT", "ROMFS", "ROMFS_INDEX", "ROMFS_MMAP", "ROMFS_MODS", "ROMFS_PATCHES", "ROMFS_PROOF_DIR", "ROMFS_VERIFY", "RSTICK_DEADZONE", "RUMBLE", "RUMBLE_CURVE", "RUMBLE_STRENGTH", "RUMBLE_TRIGGERS", "SHADER_CACHE", - "SHADER_MEMO", "SHADOW_COMPARE", "SHADOW_RANGE", "SHADOW_RES", "SHADOW_SCALE", "SHARED_FONTS", "SHARPNESS", "SHOTS", "SHOTS_DIR", + "SHADER_CACHE_FLUSH", "SHADER_MEMO", "SHADOW_COMPARE", "SHADOW_RANGE", "SHADOW_RES", "SHADOW_SCALE", "SHARED_FONTS", "SHARPNESS", "SHOTS", "SHOTS_DIR", "SVC_SLOW_MS", "SHOTS_FULL", "SPEED_PROBE", "SPIN", "SPIN_HANDOFF_US", "SPLIT_DRAWS", "SPLIT_US", "SPV_OVERRIDE", "STICK_DEADZONE", "STICK_SENS", "SWKBD_ANSWER", @@ -110,7 +110,7 @@ static const struct { const char *key, *section; } sections[] = { {"LIB_PREBUILD", "performance"}, {"LIB_PREBUILD_THREADS", "performance"}, {"PIPE_PREBUILD", "performance"}, {"PREWARM_XLATE", "performance"}, {"GPL", "performance"}, {"ASTC", "performance"}, {"MEMORY", "performance"}, {"TEX_ALIAS", "performance"}, {"TEX_VRAM_PRESSURE", "performance"}, {"ASTC_BC", "performance"}, {"LOD_GUARD", "performance"}, {"FIX_C0", "performance"}, {"FIX_ZERO", "performance"}, - {"BIND_SKIP", "performance"}, {"SHADER_MEMO", "performance"}, {"PIPE_MEMO", "performance"}, {"TEX_MEMO", "performance"}, {"DESC_TEMPLATE", "performance"}, + {"BIND_SKIP", "performance"}, {"SHADER_CACHE_FLUSH", "performance"}, {"SHADER_MEMO", "performance"}, {"PIPE_MEMO", "performance"}, {"TEX_MEMO", "performance"}, {"DESC_TEMPLATE", "performance"}, {"PUSH_SKIP", "performance"}, {"SPLIT_DRAWS", "performance"}, {"SPLIT_US", "performance"}, {"VK_VALIDATION", "performance"}, {"VK_DYN_VERTEX", "performance"}, {"VK_DYN_A2C", "performance"}, {"VK_SUBGROUP_PIN", "performance"}, {"VK_RECONVERGE", "performance"}, {"TRACE", "diagnostics"}, {"FLIGHT", "diagnostics"}, {"PASS_CRUMBS", "diagnostics"}, {"FLIGHT_SPIKE_MS", "diagnostics"}, @@ -209,7 +209,7 @@ static const CfgMeta metas[] = { {"ASTC_SCRATCH_KEEP", "0", "0|1", RESTART}, {"ASTC_ARENA", "0", "0|1", RESTART}, // vk_astc.c scratch_get, vk_texture.c astc_arena // render fixes {"LOD_GUARD", "1", "0|1", RESTART}, {"FIX_C0", "1", "0|1", RESTART}, {"FIX_ZERO", "1", "0|1", RESTART}, - {"BIND_SKIP", "1", "0|1", RESTART}, {"SHADER_MEMO", "1", "0|1", RESTART}, {"PIPE_MEMO", "1", "0|1", RESTART}, {"TEX_MEMO", "1", "0|1", RESTART}, + {"BIND_SKIP", "1", "0|1", RESTART}, {"SHADER_CACHE_FLUSH", "0", "0|1", RESTART}, {"SHADER_MEMO", "1", "0|1", RESTART}, {"PIPE_MEMO", "1", "0|1", RESTART}, {"TEX_MEMO", "1", "0|1", RESTART}, {"BIND_REUSE", "1", "0|1", RESTART}, {"DYN_DELTA", "1", "0|1", RESTART}, {"DESC_TEMPLATE", "1", "0|1", RESTART}, {"PUSH_SKIP", "1", "0|1", RESTART}, {"SPLIT_DRAWS", "64", "0..1000", RESTART}, diff --git a/runtime/gfx_shader.c b/runtime/gfx_shader.c index ecd1e4ce..d54fe8a1 100644 --- a/runtime/gfx_shader.c +++ b/runtime/gfx_shader.c @@ -1063,11 +1063,20 @@ static int use_find(const uint8_t h[32], UseMask *m) { return known; } +// NX2NAVI_SHADER_CACHE_FLUSH (default 0): 1 flushes the .spv, .res.json and .use files to the disk too, as the cache +// did before (see "Durability" at the top), for a drive or setup where a torn file was seen. +static int cache_flush(void) { + static int on = -1; + if (on < 0) on = rt_knob("SHADER_CACHE_FLUSH", "0") == 1; + return on; +} + static void use_store(const uint8_t h[32], const UseMask *m) { char p[PATH_CAP + 80]; use_path(h, p); uint32_t f[5] = {USE_MAGIC, USE_VERSION, m->slots, m->attrs, m->fouts}; - plat_file_write_cached(p, f, sizeof f); // not flushed: a torn mask fails its magic and size check (see the top) + if (cache_flush()) plat_file_write_atomic(p, f, sizeof f); + else plat_file_write_cached(p, f, sizeof f); // not flushed: a torn mask fails its magic and size check (see the top) plat_mutex_lock(&cache_lock); use_put(h, 1, m); plat_mutex_unlock(&cache_lock); @@ -1529,6 +1538,7 @@ static const GfxShader *translate_one(const Req *r, int stage, const uint8_t *co return fail(reply, hex); } // Not flushed (see "Durability" at the top): the translator wrote them whole, and disk_load checks both. + if (cache_flush()) plat_file_flush(spv_path), plat_file_flush(res_path); if (!(s = disk_load(key, r->iid))) return fail("translator output missing or malformed", hex); if (!r->final) { // name the files after the cache key, remember the mask m = use_of(s); @@ -1536,9 +1546,9 @@ static const GfxShader *translate_one(const Req *r, int stage, const uint8_t *co char mhex[65], to[PATH_CAP + 80]; key_hex(mk, mhex); snprintf(to, PATH_CAP + 80, "%s/%s.spv", cache_dir, mhex); - int moved = plat_file_replace(spv_path, to, 0); + int moved = plat_file_replace(spv_path, to, cache_flush()); snprintf(to, PATH_CAP + 80, "%s/%s.res.json", cache_dir, mhex); - moved = moved && plat_file_replace(res_path, to, 0); + moved = moved && plat_file_replace(res_path, to, cache_flush()); if (moved) { use_store(r->code_hash, &m); } else { // keep the mask for this run only