mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
Keep released textures for one minute and up to a thirty-second of the video memory budget.
The textures the game lets go of were kept for three minutes and could hold a quarter of the card's budget, up to 8 GiB with room. A run through Hyrule Field shows what that bought and what it cost (RTX 5090, 5120x2160 with DLSS Performance, about three minutes of running; the game keeps 4.8 GiB of textures live and loads about 190 a second): the released textures held 5.3 GiB on average and the video memory in use went from 11.1 to 17.9 GiB, while the game took back 1,494 MiB of them within 10 s of the release, 171 MiB within a minute and 10 MiB later, beside 33,000 textures it loaded anew. A shorter hold alone gives nothing back: with 20 s the released textures fell to 0.5 GiB and the free space inside the allocator's blocks rose to 4.9 GiB, since a block returns to the driver only when it is empty. What the released textures hold at their fullest is what the session costs, so the bound is the lever. The bound is now a thirty-second of the budget, 384 MiB to 1 GiB (vk_tex_release.h trl_cap_mb), still doubled while the card and the commit have room, and the hold 60 s, 120 for content only the GPU made. The same run with the new defaults: 13.2 GiB in use on average against 16.7, 13.8 at most against 17.9, 532 MiB of textures made again over the run, 77 fps against 75. An 8 GiB card keeps 384 MiB where it kept up to 1.9 GiB. tests/tex_release_check checks the bound by the budget and the sessions it simulates against the rule before. Compaction (tex_compact) and 16 MiB blocks were tried on the same run and gave back 0.7 and 1.2 GiB; neither is changed here. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
@@ -424,7 +424,7 @@ static const CfgMeta metas[] = {
|
||||
{"ASTC", "auto", "auto|gpu|native|cpu", RESTART}, {"MEMORY", "auto", "auto|import|mirror|verify", RESTART},
|
||||
{"TEX_ALIAS", "1", "0|1", RESTART}, {"DEPTH_INIT_INLINE", "1", "0|1", RESTART}, {"DEPTH_ALIAS_LOG", "0", "0|1", RESTART}, {"TEX_DISUSE", "1", "0|1|2", RESTART}, {"TEX_DISUSE_EVICT", "0", "0|1", RESTART}, {"TEX_GEN_UNDO", "1", "0|1", RESTART}, {"TEX_REUSE", "1", "0|1", RESTART}, {"TEX_SPLIT_FILL", "1", "0|1|2", RESTART}, {"TEX_VRAM_PRESSURE", "80", "0..100", RESTART},
|
||||
// vk_texture.c drop_aged: seconds a released texture is kept unused (vk_tex_release.h)
|
||||
{"TEX_RELEASED_HOLD", "180", "0..900", RESTART}, {"ASTC_BC", "", "0|1", RESTART}, {"ZSTD_NATIVE", "1", "0|1", RESTART}, {"ZSTD_CHECK", "0", "0|1", RESTART},
|
||||
{"TEX_RELEASED_HOLD", "60", "0..900", RESTART}, {"ASTC_BC", "", "0|1", RESTART}, {"ZSTD_NATIVE", "1", "0|1", RESTART}, {"ZSTD_CHECK", "0", "0|1", RESTART},
|
||||
{"TEX_CLOBBER", "0", "0|1", RESTART},
|
||||
// the switch of the low-end GPU mode, which is gone: still a known key, and it does nothing (lowend_set, below)
|
||||
{"LOW_END_GPU", "0", "0|1", RESTART},
|
||||
|
||||
@@ -14,29 +14,50 @@
|
||||
// never taken back.
|
||||
//
|
||||
// So a released texture goes once it has gone unused for the hold (TRL_HOLD_S by default, NX2NAVI_TEX_RELEASED_HOLD),
|
||||
// three times the longest gap seen, whether or not the bound is reached: the cache then holds what the last few
|
||||
// minutes released, not what the bound allows, and it empties within minutes of the last area change. Unused counts
|
||||
// the longest gap seen, whether or not the bound is reached: the cache then holds what the last minute released, not
|
||||
// what the bound allows, and it empties within a minute or two of the last area change. Unused counts
|
||||
// from the release or from the last time a shader sampled the released image (a finalized slot may show it), whichever
|
||||
// is later. The hold is the same for a texture uploaded from the game's memory and for an ASTC one the GPU decodes:
|
||||
// either made again shows its stand-in for a frame, the decode only costs more of the render thread. A texture whose
|
||||
// content only the GPU made (a render target) cannot be made again at all, it comes back empty: it waits twice the
|
||||
// hold.
|
||||
//
|
||||
// The size bound (released_cap_bytes, a quarter of the video memory budget by default) stays as the safety net it was:
|
||||
// past it the oldest released textures go even when young. With the hold on, young ones past it are kept while the card
|
||||
// and the system have room (trl_room), up to TRL_ROOM_TIMES the bound: the old Balanced dropped 31 young ones in a
|
||||
// burst of area changes at its bound of 4 GiB on a card with 20 GiB free, and each was made again within a minute. The
|
||||
// hold empties that excess within minutes, so the room it takes is temporary; without the hold (tex_released_hold = 0) the
|
||||
// bound acts as before.
|
||||
// The size bound (trl_cap_mb: a thirty-second of the video memory budget, TRL_CAP_MIN_MB to TRL_CAP_MAX_MB) is what
|
||||
// decides how much video memory the cache costs: past it the oldest released textures go even when young, and with the
|
||||
// hold on, young ones past it are kept while the card and the system have room (trl_room), up to TRL_ROOM_TIMES the
|
||||
// bound. Until 0.10.2 the hold was 180 s and the bound a quarter of the budget (4 GiB on a 32 GiB card, 8 with room).
|
||||
// A run through Hyrule Field showed what that bought (2026-11-14, RTX 5090, 5120x2160 with DLSS Performance, about
|
||||
// three minutes of running; the game keeps 4.8 GiB of textures live there and lets go of and loads about 190 a
|
||||
// second): the released textures held 5.3 GiB on average and 6.4 at most, the video memory in use went from 11.1 to
|
||||
// 17.9 GiB, and of what they held the game took back 1,494 MiB within 2 to 10 s of the release, 171 MiB within 10 to
|
||||
// 60 s and 10 MiB later, beside 33,000 textures it loaded anew in that time. The memory does not come back when the
|
||||
// cache shrinks either: a block of the allocator goes back to the driver only when its last allocation went, so with
|
||||
// a hold of 20 s in the same run the released textures fell to 0.5 GiB while the free space inside the blocks rose to
|
||||
// 4.9 GiB, and the driver's figure stayed at 15.8. What the cache may hold at its fullest is therefore what the
|
||||
// session costs for good. With a bound of 1 GiB (2 with room) the same run held 13.4 GiB on average and 14.8 at most,
|
||||
// 3.3 GiB less, made 1,170 MiB of textures again over the run and kept its frame rate (78 against 75 fps, within what
|
||||
// two runs differ by).
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
enum { TRL_STORAGE, TRL_GPU }; // content made again from the game's memory; content only the GPU made
|
||||
|
||||
#define TRL_HOLD_S 180 // the default hold, in seconds
|
||||
#define TRL_HOLD_S 60 // the default hold, in seconds
|
||||
#define TRL_HOLD_MAX_S 900 // the longest hold the setting takes: twice it fits the clock below
|
||||
#define TRL_ROOM_TIMES 2 // with room, the released textures may hold this many times the bound
|
||||
#define TRL_ROOM_MARGIN 10 // room: the card at least this many points of its budget under the VRAM pressure
|
||||
#define TRL_CAP_SHARE 32 // the default bound: this share of the video memory budget,
|
||||
#define TRL_CAP_MIN_MB 384 // at least this much (the render targets a frame lets go of and takes back the next
|
||||
// frame come to about 70 MiB at 2560x1080; they are not dropped, but they count),
|
||||
#define TRL_CAP_MAX_MB 1024 // and at most this much
|
||||
#define TRL_CAP_UNKNOWN_MB 512 // without a budget (no VK_EXT_memory_budget and no heap size)
|
||||
|
||||
// The default bound in MiB for a video memory budget in MiB (0: not known).
|
||||
static inline uint32_t trl_cap_mb(uint64_t budget_mb) {
|
||||
if (!budget_mb) return TRL_CAP_UNKNOWN_MB;
|
||||
uint64_t mb = budget_mb / TRL_CAP_SHARE;
|
||||
return (uint32_t)(mb < TRL_CAP_MIN_MB ? TRL_CAP_MIN_MB : mb > TRL_CAP_MAX_MB ? TRL_CAP_MAX_MB : mb);
|
||||
}
|
||||
|
||||
// The hold of a class, in seconds; 0: no hold.
|
||||
static inline uint32_t trl_hold(uint32_t hold_s, int cls) { return cls == TRL_GPU ? hold_s * 2 : hold_s; }
|
||||
|
||||
@@ -2579,11 +2579,10 @@ void vk_texture_pool_ctx(int on) { pool_ctx = on; }
|
||||
|
||||
static int idle(const Tex *e, uint64_t now);
|
||||
|
||||
// NX2NAVI_TEX_RELEASED_MB (default a thirty-second of the installed memory, 512 MiB to 2 GiB, or more by the card's
|
||||
// budget, released_cap_bytes; 0 = no limit) bounds the own memory of the released entries. With the hold on (drop_aged,
|
||||
// the default) what they hold is what the last minutes released, and the bound is the safety net for a burst of area
|
||||
// changes within the hold; sweep_pressure lets young entries past it while the card and the system have room
|
||||
// (vk_tex_release.h trl_keep). Before the hold the bound was all that limited them: RELEASED_MAX alone kept 5-7 GiB of
|
||||
// NX2NAVI_TEX_RELEASED_MB (default a thirty-second of the video memory budget, 384 MiB to 1 GiB, released_cap_bytes;
|
||||
// 0 = no limit) bounds the own memory of the released entries. With the hold on (drop_aged, the default) they hold
|
||||
// what the last minute released, up to the bound; sweep_pressure lets young entries past it while the card and the
|
||||
// system have room (vk_tex_release.h trl_keep). Before the hold the bound was all that limited them: RELEASED_MAX alone kept 5-7 GiB of
|
||||
// released render targets and decoded textures after an hour of play at a 2160p game resolution, device memory the
|
||||
// driver also charges to the system's commit (the process's private bytes reached 31 GiB). A sixteenth (1 to 4 GiB)
|
||||
// still let them hold 1.3 GiB on a 32 GB PC whose commit then ran out at an area load (device lost); only entries
|
||||
@@ -2616,7 +2615,6 @@ static int idle(const Tex *e, uint64_t now);
|
||||
#define COMMIT_LOW_KEEP ((uint64_t)256 << 20)
|
||||
#define PROC_COMMIT_MIN_MB 24576
|
||||
static uint64_t released_cap; // bytes; 0 = not read yet, UINT64_MAX = no limit
|
||||
#define RELEASED_CAP_MAX_MB 8192
|
||||
static long long commit_free_mb = -2; // -2: not read yet, -1: the default
|
||||
static long long proc_commit_mb; // this process's bound in MB; 0 = none
|
||||
static int commit_low, commit_logs; // commit_low: either of the two below at the last check
|
||||
@@ -2633,29 +2631,19 @@ static uint64_t released_cap_bytes(void) {
|
||||
const char *by = ""; // which term set the bound, for the line below
|
||||
if (s && *s) mb = atoll(s);
|
||||
else {
|
||||
float used, total, proc;
|
||||
plat_mem_info(&used, &total, &proc);
|
||||
mb = total > 0 ? (long long)(total / 32) : 1024;
|
||||
mb = mb < 512 ? 512 : mb > 2048 ? 2048 : mb;
|
||||
by = total > 0 ? ", a thirty-second of the installed memory (512 MiB to 2 GiB)"
|
||||
: ", the default without the installed memory's size";
|
||||
// By the card too: a quarter of its budget where the PC has an eighth of its memory to match, up to
|
||||
// RELEASED_CAP_MAX_MB. The game lets go of the textures of what leaves the view and takes them back when
|
||||
// it returns; walking through a village the released ones passed 1 GiB within two or three minutes, and
|
||||
// from then on each release dropped an older one that was decoded and encoded again on its way back,
|
||||
// hundreds a second, on a card with 30 GiB free. The two pressure rules still come first: released
|
||||
// entries go before anything else above NX2NAVI_TEX_VRAM_PRESSURE of the budget, and the bound falls to
|
||||
// COMMIT_LOW_KEEP while the commit is low.
|
||||
// By the card: a thirty-second of its budget, 384 MiB to 1 GiB (vk_tex_release.h trl_cap_mb, which has the
|
||||
// run that set it). What the released textures hold at their fullest stays in use for the rest of the
|
||||
// session, as free space inside the allocator's blocks once they are dropped, so the bound is the price of
|
||||
// the cache; until 0.10.2 it was a quarter of the budget, up to 8 GiB. The two pressure rules still come
|
||||
// first: released entries go before anything else above NX2NAVI_TEX_VRAM_PRESSURE of the budget, and the
|
||||
// bound falls to COMMIT_LOW_KEEP while the commit is low.
|
||||
uint64_t vused = 0, vbudget = 0;
|
||||
int exact = 0;
|
||||
if (vk_mem_vram(&vused, &vbudget, &exact) && total > 0) {
|
||||
long long by_card = (long long)(vbudget >> 22), by_ram = (long long)(total / 8); // a quarter, in MiB
|
||||
long long more = by_card < by_ram ? by_card : by_ram;
|
||||
const char *more_by = by_card <= by_ram ? ", a quarter of the video memory budget"
|
||||
: ", an eighth of the installed memory";
|
||||
if (more > RELEASED_CAP_MAX_MB) more = RELEASED_CAP_MAX_MB, more_by = ", the largest the rule by the card gives";
|
||||
if (more > mb) mb = more, by = more_by;
|
||||
}
|
||||
int known = vk_mem_vram(&vused, &vbudget, &exact) && vbudget;
|
||||
mb = trl_cap_mb(known ? vbudget >> 20 : 0);
|
||||
by = !known ? ", the default without the card's budget"
|
||||
: mb == TRL_CAP_MAX_MB ? ", the most the rule by the card gives"
|
||||
: mb == TRL_CAP_MIN_MB ? ", the least the rule by the card gives" : ", a thirty-second of the video memory budget";
|
||||
}
|
||||
released_cap = mb > 0 ? (uint64_t)mb << 20 : UINT64_MAX;
|
||||
if (mb > 0) rt_log("vk: released textures keep at most %lld MiB of their own memory%s (NX2NAVI_TEX_RELEASED_MB)", mb,
|
||||
|
||||
Reference in New Issue
Block a user