mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
Give back the graphics pipelines no draw has used in this run while video memory is short: over tex_vram_pressure percent of the budget, the autosave thread takes up to 256 of them out of the table every 5 seconds and destroys them once the GPU is past the submission being recorded; a draw that needs one later builds it again from the pipeline cache. Mesh pipelines, entries building or linking and compute pipelines stay, and the next pass logs the video memory in use before and after. pipe_release = 0 keeps them all.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -420,6 +420,11 @@
|
||||
# instead of by the thread that draws alone (the same bytes, 2 to 3 times sooner). 0: one thread, as before 0.10.
|
||||
# Read at start.
|
||||
# par_jobs = 1
|
||||
# While the video memory in use is over tex_vram_pressure percent of the budget, the pipelines the game built ahead
|
||||
# of play and no draw has used yet are given back, a few hundred every 5 seconds: on a card short of video memory they
|
||||
# are what costs least to lose, and a draw that needs one later builds it again from the pipeline cache. 0: every
|
||||
# pipeline stays for the run, as before 0.10. Read at start.
|
||||
# pipe_release = 1
|
||||
# Low-end GPU mode (experimental; also in the settings menu's Game tab). For older graphics cards, and ones short of
|
||||
# video memory: 1 turns on the settings written for them and measured on a Radeon RX 580, for every key below that
|
||||
# this file does not set itself. Mesh shaders run as compute where the card has none (mesh_emulation), video memory
|
||||
|
||||
@@ -45,7 +45,7 @@ static const char *const known[] = {
|
||||
"COLOR_PRESET", "COLOR_SATURATION", "CONTACT_DEBUG", "CONTACT_SHADOWS", "CORE_DIAG", "CRIT", "CRIT_OUT",
|
||||
"DMABUF", "DMABUF_MB", "DRAW_CRUMBS", "GPA_LATE", "DESC_TEMPLATE", "DEVICE_PROFILE", "DISCORD_APP_ID", "DISCORD_IMAGE", "DISCORD_RPC", "DLAA", "DLAA_CAM_KEEP", "DLAA_CUT", "DLSS_IDLE_FREE", "DLAA_EDGE_FILL", "DLAA_AT_UPSCALER", "FFX_DEBUG", "FFX_KEYS", "FSR3_SHARPEN", "DLAA_JITTER", "DLAA_MV", "DLAA_MASKDEBUG", "DLAA_MVDEBUG", "DLAA_OBJMV", "DLAA_SURVEY", "DLAA_TARGET_KEEP", "DLAA_TIMES", "DLSS", "DLSS_CHAR_MASK", "DLSS_PRESET", "DLSS_PREBLOOM", "DLSS_RANGE", "DLSS_REACTIVE", "DUMP", "DUMP_BUF", "DUMP_DIR",
|
||||
"DUMP_LOG", "DUMP_RAW", "DUMP_SHADERS", "DUMP_FILE", "DS3", "LOG_CS_BINDS", "LOG_TEX_FLAGS", "LOG_MESH", "MESH", "POOL", "NATIVE_TENSOR", "LOG_RCOND", "LOG_RT_SIZES", "DUMP_WHITE", "DUMP_AFTER", "DUMP_MOVIE", "SUBMIT_FINISH", "ACQUIRE_WAIT", "CRASH_FILTER_GUARD", "CS_FALLBACK", "EXEFS", "FEEDBACK", "FIX_C0", "FIX_ZERO", "FLIGHT", "FLIGHT_AT", "FLIGHT_DIR", "FLIGHT_FRAMES", "GPU_TIMELINE",
|
||||
"FFX_PIPE_CACHE", "FLIGHT_SPIKE_MS", "STUTTER_MAP", "PAR_JOBS", "FLIGHT_THUMBS", "FLIGHT_EVERY", "FLIGHT_FLASH", "FLIGHT_FLASH_DIFF", "FLIGHT_FLASH_CELLS", "FLUSH_SPAN", "FLUSH_UPLOAD", "FOG_CULL", "FOLIAGE_SHADOW_CULL", "FOLIAGE_SHADOW_VRS", "FOV", "FOV_PATCH", "FG_HUDLESS_SHOW", "FG_MULT", "FPS60", "FPS_LIMIT", "FRAMEGEN", "FRAMEGEN_MULTIPLIER", "FREEZE_IDLE", "FREEZE_ASYNC", "FREEZE_BUDGET_US", "FREE_CORES",
|
||||
"FFX_PIPE_CACHE", "FLIGHT_SPIKE_MS", "STUTTER_MAP", "PAR_JOBS", "PIPE_RELEASE", "FLIGHT_THUMBS", "FLIGHT_EVERY", "FLIGHT_FLASH", "FLIGHT_FLASH_DIFF", "FLIGHT_FLASH_CELLS", "FLUSH_SPAN", "FLUSH_UPLOAD", "FOG_CULL", "FOLIAGE_SHADOW_CULL", "FOLIAGE_SHADOW_VRS", "FOV", "FOV_PATCH", "FG_HUDLESS_SHOW", "FG_MULT", "FPS60", "FPS_LIMIT", "FRAMEGEN", "FRAMEGEN_MULTIPLIER", "FREEZE_IDLE", "FREEZE_ASYNC", "FREEZE_BUDGET_US", "FREE_CORES",
|
||||
"FULLSCREEN", "FXAA", "GAME_ASPECT", "GAME_RES", "GPA", "GPA_EMPTY", "GPA_TWIN", "GPA_TWIN_MAX_KB", "MIRROR_CAPTURE_KB", "WB_SKIP_MB", "GPL", "GPL_DRAW_BUILD", "GPL_FEEDBACK", "GPL_MESH", "PIPE_AHEAD", "PIPE_FAST_WORKERS", "PIPE_FRAME_WAIT_MS", "PIPE_LINK_DRAW_MS", "PIPE_SKIP_STEADY", "DRAW_WRITERS_WAIT", "PROG_LEAD", "GPU", "GPU_INDEX", "GPU_PASSES", "GPU_NAME", "HDR",
|
||||
"HDR_GAME_NITS", "HDR_LIVE", "HEAP_LAZY", "HDR_PEAK_NITS", "HDR_UI_NITS",
|
||||
// retired: the HDR look's settings of earlier versions, now fixed (vk_hdr.c, shaders/hdr_tonemap.frag); read by
|
||||
@@ -160,7 +160,7 @@ static const struct { const char *key, *section; } sections[] = {
|
||||
{"PUSH_SKIP", "performance"}, {"SPLIT_DRAWS", "performance"}, {"SPLIT_IDLE", "performance"}, {"SPLIT_IN_PASS", "performance"}, {"SPLIT_US", "performance"}, {"VK_VALIDATION", "performance"},
|
||||
{"VK_DYN_VERTEX", "performance"}, {"VK_DYN_A2C", "performance"}, {"VK_SUBGROUP_PIN", "performance"}, {"VK_RECONVERGE", "performance"},
|
||||
{"VK_NAN_PRESERVE", "performance"}, {"VK_NAN_BITS", "performance"},
|
||||
{"DRAW_CRUMBS", "diagnostics"}, {"TRACE", "diagnostics"}, {"FLIGHT", "diagnostics"}, {"PASS_CRUMBS", "diagnostics"}, {"FLIGHT_SPIKE_MS", "diagnostics"}, {"STUTTER_MAP", "diagnostics"}, {"PAR_JOBS", "performance"},
|
||||
{"DRAW_CRUMBS", "diagnostics"}, {"TRACE", "diagnostics"}, {"FLIGHT", "diagnostics"}, {"PASS_CRUMBS", "diagnostics"}, {"FLIGHT_SPIKE_MS", "diagnostics"}, {"STUTTER_MAP", "diagnostics"}, {"PAR_JOBS", "performance"}, {"PIPE_RELEASE", "performance"},
|
||||
{"FLIGHT_FLASH", "diagnostics"}, {"FLIGHT_FLASH_DIFF", "diagnostics"}, {"FLIGHT_FLASH_CELLS", "diagnostics"},
|
||||
{"WB_FENCE_LIMIT", "diagnostics"}, {"WB_MERGE", "diagnostics"}, {"WB_EXACT", "diagnostics"}, {"MEM_CENSUS", "diagnostics"}, {"WB_NO_REUP", "diagnostics"}, {"WB_PROBE", "diagnostics"}, {"WB_SPLIT", "performance"}, {"SHADOW_BULK_WRITE", "performance"}, {"WB_LAZY", "performance"}, {"HEAP_LAZY", "performance"}, {"BARRIER_GAME", "performance"}, {"MESH_AHEAD", "performance"}, {"MESH_FALLBACK", "performance"}, {"MESH_FALLBACK_MB", "performance"}, {"MESH_FALLBACK_WORKERS", "performance"}, {"WB_LAZY_TOUCH", "diagnostics"}, {"WB_LAZY_CHUNK", "performance"}, {"FLUSH_UPLOAD", "diagnostics"}, {"GPA_TWIN_MAX_KB", "diagnostics"}, {"MIRROR_CAPTURE_KB", "diagnostics"}, {"WB_SKIP_MB", "diagnostics"},
|
||||
{"WB_MERGE_MAX", "diagnostics"}, {"MV_SURVEY", "diagnostics"}, {"TEXUP_LOG_MS", "diagnostics"},
|
||||
@@ -414,6 +414,8 @@ static const CfgMeta metas[] = {
|
||||
{"STUTTER_MAP", "0", "0|1", RESTART},
|
||||
// par_jobs.c: the worker pool that splits a large texture's copy out of the game's memory (0: the thread that makes it alone)
|
||||
{"PAR_JOBS", "1", "0|1", RESTART},
|
||||
// vk_pipeline.c "pipelines no draw has used, given back while video memory is short" (0: every pipeline stays)
|
||||
{"PIPE_RELEASE", "1", "0|1", RESTART},
|
||||
// flight.c "The flash trigger": on or off, and flight_flash.h flash_test's two thresholds
|
||||
{"FLIGHT_FLASH", "1", "0|1", RESTART}, {"FLIGHT_FLASH_DIFF", "24", "4..255", RESTART}, {"FLIGHT_FLASH_CELLS", "3", "1..144", RESTART},
|
||||
{"WB_FENCE_LIMIT", "65536", "0..1073741824", RESTART}, {"WB_MERGE", "1", "0|1", RESTART}, {"WB_EXACT", "1", "0|1", RESTART}, {"MEM_CENSUS", "0", "0|1", RESTART}, {"WB_NO_REUP", "1", "0|1", RESTART}, {"WB_PROBE", "0", "0|1", RESTART}, {"WB_SPLIT", "1", "0|1|2|3", RESTART}, {"WB_LAZY", WB_LAZY_DEFAULT, "0|1", RESTART}, {"HEAP_LAZY", "0", "0|1", RESTART}, {"WB_LAZY_TOUCH", "0", "0..16", RESTART}, {"WB_LAZY_CHUNK", "0", "0|1", RESTART}, {"FLUSH_UPLOAD", "0", "0|1", RESTART}, {"GPA_TWIN_MAX_KB", "0", "0..1048576", RESTART},
|
||||
|
||||
@@ -5022,6 +5022,85 @@ static void fast_retire(Pipe *p) {
|
||||
fast_retired++;
|
||||
}
|
||||
|
||||
// ---- pipelines no draw has used, given back while video memory is short
|
||||
// The boot builds the recorded pipelines ahead of play (the prewarm and the shipped pipeline list), and the driver keeps
|
||||
// each one's compiled code in video memory until it is destroyed. In a 4K session with DLSS and frame generation the
|
||||
// device-local memory outside the allocator was 4.2 GB with 6,300 graphics pipelines alive, 3,589 of them not drawn
|
||||
// once in that run. On a card short of video memory those are the cheapest thing to give back: no draw has recorded
|
||||
// them. So while the device-local memory in use is over NX2NAVI_TEX_VRAM_PRESSURE percent of the budget (vk_mem_vram,
|
||||
// which NX2NAVI_VRAM_BUDGET_MB caps for a test), the autosave thread takes up to RELEASE_BATCH graphics pipelines that
|
||||
// no draw has looked up in this run out of the table every RELEASE_EVERY_MS, and destroys them with their entries once
|
||||
// the GPU is past the submission being recorded and one more (as fast_retire). A draw that needs one later misses it as
|
||||
// it would a pipeline never built (pipe_miss): its stage libraries link it, or the look-ahead queued them
|
||||
// (vk_pipeline_hint), and its optimised build comes from the pipeline cache, which still holds it.
|
||||
// Only entries no lookup reached go (used_min 0; pipe_minute is set before the first lookup, ensure_init), so no draw's
|
||||
// memo (vk_exec_draw.c, the PM context) nor any caller holds their VkPipeInfo. Not taken: mesh pipelines (without
|
||||
// stage libraries their build is long), entries building or with a link queued, failed ones, and compute pipelines.
|
||||
// The next pass logs the device-local use then and now, so a log says what a batch gave back.
|
||||
// NX2NAVI_PIPE_RELEASE=0 keeps every pipeline for the run, as before 0.10.0.
|
||||
#define RELEASE_EVERY_MS 5000
|
||||
#define RELEASE_BATCH 256
|
||||
static uint64_t rel_total, rel_batches;
|
||||
static uint64_t rel_used0; // device-local use (MiB) when the last batch went; 0 none to report
|
||||
|
||||
static void released_destroy_cb(void *arg) { // the completion thread: the entry is out of the table since its batch
|
||||
Pipe *p = arg;
|
||||
if (p->info.pipeline) vkDestroyPipeline(P.device, p->info.pipeline, NULL);
|
||||
free(p->bindings);
|
||||
free(p);
|
||||
}
|
||||
|
||||
// Caller holds P.lock.
|
||||
static int releasable(const Pipe *p) {
|
||||
if (p->used_min || !p->info.pipeline || p->building || p->job || p->link_queued || p->fast || p->t_miss || !p->lay)
|
||||
return 0;
|
||||
if (p->info.bind_point != VK_PIPELINE_BIND_POINT_GRAPHICS || p->key_size != sizeof(GKey)) return 0;
|
||||
const GKey *k = (const GKey *)p->key;
|
||||
return k->vs && gfx_shader_stage(k->vs) != GFX_STAGE_MS;
|
||||
}
|
||||
|
||||
static void pressure_release(uint64_t now) {
|
||||
static int on = -1, pct;
|
||||
static uint64_t last;
|
||||
if (on < 0) {
|
||||
on = cfg_bool("NX2NAVI_PIPE_RELEASE");
|
||||
pct = cfg_int("NX2NAVI_TEX_VRAM_PRESSURE");
|
||||
if (pct <= 0 || pct > 100) pct = 80;
|
||||
if (!on) rt_log("vk_pipeline: every pipeline stays for the run, also while video memory is short (NX2NAVI_PIPE_RELEASE=0)");
|
||||
}
|
||||
if (!on || now - last < RELEASE_EVERY_MS) return;
|
||||
last = now;
|
||||
uint64_t used = 0, budget = 0;
|
||||
int exact = 0;
|
||||
if (!vk_mem_vram(&used, &budget, &exact) || !budget) return;
|
||||
if (rel_used0) {
|
||||
rt_log("vk_pipeline: device-local memory in use %llu MiB when the last batch of unused pipelines went, %llu MiB "
|
||||
"now", (unsigned long long)rel_used0, (unsigned long long)(used >> 20));
|
||||
rel_used0 = 0;
|
||||
}
|
||||
if (used * 100 < budget * (uint64_t)pct) return;
|
||||
Pipe *gone[RELEASE_BATCH];
|
||||
int n = 0;
|
||||
plat_mutex_lock(&P.lock);
|
||||
for (int b = 0; b < PIPE_BUCKETS && n < RELEASE_BATCH; b++)
|
||||
for (Pipe **pp = &pipes[b]; *pp && n < RELEASE_BATCH;) {
|
||||
Pipe *p = *pp;
|
||||
if (releasable(p)) *pp = p->next, gone[n++] = p;
|
||||
else pp = &p->next;
|
||||
}
|
||||
uint64_t at = vk_sync_issued() + 2;
|
||||
plat_mutex_unlock(&P.lock);
|
||||
if (!n) return;
|
||||
for (int i = 0; i < n; i++) vk_completion_add(at, released_destroy_cb, gone[i]);
|
||||
rel_total += (uint64_t)n, rel_batches++;
|
||||
rel_used0 = used >> 20;
|
||||
if (rel_batches <= 16 || !(rel_batches & (rel_batches - 1)))
|
||||
rt_log("vk_pipeline: video memory in use %llu of %llu MiB (over %d%%): %d graphics pipelines no draw used in this "
|
||||
"run given back, %llu in %llu batches so far; a draw that needs one builds it again from the pipeline "
|
||||
"cache (NX2NAVI_PIPE_RELEASE)", (unsigned long long)(used >> 20), (unsigned long long)(budget >> 20), pct, n,
|
||||
(unsigned long long)rel_total, (unsigned long long)rel_batches);
|
||||
}
|
||||
|
||||
// The whole build of job j, which the caller took off its queue, and the job's end: its pipeline is entered, the
|
||||
// threads waiting for it are woken and j is freed. lowrun: 1 on a pool worker that took j from the low queue. Caller
|
||||
// holds P.lock, which is released for the build.
|
||||
@@ -8852,6 +8931,7 @@ static void autosave_once(void) {
|
||||
if (!P.ready) return;
|
||||
uint64_t now = plat_now_ms();
|
||||
__atomic_store_n(&pipe_minute, minute_now(), __ATOMIC_RELAXED);
|
||||
pressure_release(now); // while video memory is short: the pipelines no draw used go
|
||||
if (now - last_sizes >= SIZES_EVERY_MS && (P.cache || P.play)) last_sizes = now, cache_sizes_ask();
|
||||
if (now - last_keys >= 2000) {
|
||||
last_keys = now;
|
||||
|
||||
Reference in New Issue
Block a user