diff --git a/docs/README-BuildKit.md b/docs/README-BuildKit.md index 432586be..dd210b26 100644 --- a/docs/README-BuildKit.md +++ b/docs/README-BuildKit.md @@ -160,6 +160,10 @@ beside the game. driver keeps the prepared shaders' libraries in that cache, and a smaller limit drops the oldest as new ones come in (a 2048 MB cache could not hold them): every start then builds them again in the background, and play stutters, with some things missing for a few frames, until that is done. The log's `library warm` lines say when this happens. +- With 16 GB of RAM, give Windows a fixed page file of at least 32 GB (System, About, Advanced system settings, + Performance Settings, Advanced, Virtual memory: Custom size, then restart). Windows counts the game's video memory + against RAM plus page file, and a page file Windows sizes itself grows too slowly for Hyrule Field: the game then + lets textures go and the ground can flash white or black. The log says `system commit low` when this happens. - **Screen** in the menu has Texture filtering (anisotropic, up to 16x, at once), Texture detail (Reduced and Low leave out the largest versions of the big surface textures so that the game fits the video memory of 6 and 8 GB cards; Automatic picks by the card), Upscaling (DLSS on NVIDIA RTX cards, diff --git a/runtime/platform/linux.c b/runtime/platform/linux.c index ccb6eac0..7d382090 100644 --- a/runtime/platform/linux.c +++ b/runtime/platform/linux.c @@ -2229,6 +2229,9 @@ int plat_commit_note(char *out, size_t cap) { // The cgroup's memory.current and MemAvailable count system memory only: a graphics card's own memory is in neither. int plat_commit_counts_vram(void) { return 0; } +// Swap is not a page file Windows sizes; the limits plat_commit_info reads do not grow with it. +int plat_page_file_managed(void) { return -1; } + // ---- child processes struct PlatChild { diff --git a/runtime/platform/platform.h b/runtime/platform/platform.h index 81c7f741..2ac9355b 100644 --- a/runtime/platform/platform.h +++ b/runtime/platform/platform.h @@ -488,6 +488,9 @@ int plat_commit_note(char *out, size_t cap); // Whether a discrete GPU's own memory counts against that limit: on Windows the driver charges device memory to the // system's commit; Linux's limits count system memory only. int plat_commit_counts_vram(void); +// Whether Windows sizes a page file by itself (System managed size): 1; 0 when every page file has a size set; -1 +// when unknown, there is none, or not Windows. +int plat_page_file_managed(void); // This process's memory in bytes: resident (the working set) and private (Windows: committed private bytes; Linux: the // resident anonymous pages). 0 when unknown. void plat_process_memory(uint64_t *resident, uint64_t *private_bytes); diff --git a/runtime/platform/win32.c b/runtime/platform/win32.c index 9f8138d1..2bb6b679 100644 --- a/runtime/platform/win32.c +++ b/runtime/platform/win32.c @@ -1922,6 +1922,28 @@ int plat_commit_note(char *out, size_t cap) { // The driver charges the device memory it allocates to the system's commit. int plat_commit_counts_vram(void) { return 1; } +// The PagingFiles value under Memory Management, one " " per page file: a path of +// "?:\..." (System managed size on every drive) or no sizes or "0 0" (System managed size on that drive) leaves the +// size to Windows. Read once; the setting takes effect only after a restart. +int plat_page_file_managed(void) { + static int managed = -2; + if (managed != -2) return managed; + wchar_t v[2048]; + DWORD size = sizeof v - sizeof(wchar_t) * 2; + int r = -1; + if (RegGetValueW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management", + L"PagingFiles", RRF_RT_REG_MULTI_SZ, NULL, v, &size) == ERROR_SUCCESS) { + v[size / sizeof(wchar_t)] = v[size / sizeof(wchar_t) + 1] = 0; + for (wchar_t *e = v; *e; e += wcslen(e) + 1) { + wchar_t *sp = wcschr(e, L' '); + unsigned long lo = sp ? wcstoul(sp, &sp, 10) : 0, hi = sp ? wcstoul(sp, NULL, 10) : 0; + int own = e[0] == L'?' || (!lo && !hi); + r = own ? 1 : r < 0 ? 0 : r; + } + } + return managed = r; +} + void plat_process_memory(uint64_t *resident, uint64_t *private_bytes) { PROCESS_MEMORY_COUNTERS_EX pm = {.cb = sizeof pm}; *resident = *private_bytes = 0; diff --git a/runtime/vk_texture.c b/runtime/vk_texture.c index 6694fd2a..12454d3e 100644 --- a/runtime/vk_texture.c +++ b/runtime/vk_texture.c @@ -1629,6 +1629,16 @@ static int commit_is_low(void) { rt_log("vk: system commit %s: %.0f of %.0f MB in use (this process %.0f MB); released textures kept %d, %.0f MiB " "of own memory%s", low ? "low" : "no longer low", used, limit, proc, nreleased, rel_bytes / 1048576.0, low ? ": dropping the stale ones" : ""); + // A page file Windows sizes grows only slowly and by little, so the limit stays near the installed memory plus + // what the page file held at the boot: on a 16 GB PC with a 16 GB card it ran low in Hyrule Field at 32 GB, + // and textures gave way (white or black ground). A fixed page file of 32 GB or more raised the limit to 49 GB. + static int hinted; + if (low && !hinted && plat_page_file_managed() == 1) { + hinted = 1; + rt_log("vk: the page file is sized by Windows, which grows it only slowly: a fixed page file of at least " + "32 GB keeps the system's commit from running low (System > About > Advanced system settings > " + "Performance Settings > Advanced > Virtual memory > Change: Custom size, then restart Windows)"); + } } sys_low = low; return commit_low = low || high;