diff --git a/docs/README-BuildKit.md b/docs/README-BuildKit.md index dd210b26..77acad6b 100644 --- a/docs/README-BuildKit.md +++ b/docs/README-BuildKit.md @@ -160,8 +160,9 @@ 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 +- With 16 GB of RAM, give Windows a fixed page file whose initial size is at least 32 GB (System, About, Advanced + system settings, Performance Settings, Advanced, Virtual memory: untick the automatic management, Custom size with + an initial size of 32768 MB or more, Set, 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 diff --git a/docs/releases/v0.7.2.md b/docs/releases/v0.7.2.md index c645f21b..ef450de3 100644 --- a/docs/releases/v0.7.2.md +++ b/docs/releases/v0.7.2.md @@ -33,13 +33,14 @@ Draft notes for the release after 0.7.1. The version number is not decided yet. shader compiler"); with it each NaN test is now made on the bits of the number instead, which no compiler can drop. It is on by default only with that driver (`vk_nan_bits`); the first start after the update compiles the pipelines again. Untested on those cards before this release: please report whether the blocks are gone. -- AMD RX 5000 and RX 6000 cards: the game no longer ends (code 71) when an enemy is killed. The Windows driver for - these cards faults when it links a burst of new pipelines from shader libraries, which is what the first kill of a - session asks for; players with an RX 6700 XT and an RX 5700 XT had it at every kill, and one of them found that - `gpl = 0` stops it. That is now the default with this driver (the log reads "gpl no ... off by default on AMD's - driver for RDNA 1 and 2"). The price: a pipeline the game needs for the first time is built whole while its draw - waits, so a new area hitches more on these cards. `gpl = 1` in `nx2navi.ini` turns the libraries back on. A - contributor's change, checked on their card; none of us has such a card. +- AMD RX 6000 and older cards: the game should no longer end (code 71) when an enemy is killed. A player with an + RX 6700 XT had it at every kill since 0.5 and found that `gpl = 0` (no graphics pipeline libraries) stops it; a + player with an RX 5700 XT reported the same ending. That is now the default with AMD's Windows driver for these + cards (the log reads "gpl no ... off by default on AMD's driver for RDNA 2 and older"). The price: a pipeline the + game needs for the first time is built whole while its draw is left out, so new things can appear some frames + late, and the start waits until every pipeline of earlier sessions is built instead of 20 seconds at most. + `gpl = 1` in `nx2navi.ini` turns the libraries back on. One player's finding on one card; none of us has such a + card, so please report whether the game stays up. - The older installer's last step no longer writes `nx2navi.ini` again as a whole to set the language: it changes the `lang` line and keeps every other byte (it turned Windows line ends into Unix ones and dropped a byte order mark). diff --git a/nx2navi.ini.example b/nx2navi.ini.example index ee298122..d05446ac 100644 --- a/nx2navi.ini.example +++ b/nx2navi.ini.example @@ -184,9 +184,10 @@ # the new translator cannot translate keeps its old translation. 0: shaders the new translator has not translated yet # are translated before they are used (at start or when the game first draws them), as earlier versions did. # shader_carry = 1 -# Vulkan graphics pipeline library, 0 or 1. Default: on, except with AMD's Windows driver for RX 5000 and RX 6000 -# cards, which faults when it links a burst of new pipelines (the game ended at every enemy killed); there it is off, -# new pipelines are built whole, and gpl = 1 turns it on again. +# Vulkan graphics pipeline library, 0 or 1. Default: on, except with AMD's Windows driver for RX 6000 and older cards +# (RX 400 to RX 6000), where the game ended at every enemy killed with it on. There it is off: a pipeline needed for +# the first time is built whole while its draw is left out for some frames, and the start waits until every pipeline +# of earlier sessions is built. gpl = 1 turns it on again. # gpl = 1 # Textures and memory. @@ -744,7 +745,8 @@ # wb_probe = 0 # 1: what the GPU wrote goes back to the game's memory in a GPU submission of its own, and the wait after each frame's # first submission ends when that submission's passes are through, without waiting for the copy back (3 to 4 ms of -# every frame). 0: the copy back is part of the submission, and the wait covers it. +# every frame). 0: the copy back is part of the submission, and the wait covers it. With shadow storage (Linux, Wine) +# the wait covers the copy back either way: the game must not go on before it. # wb_split = 1 # 1: what the game's shaders write into its memory is copied back from the video card once, and after that only when # the game's CPU code touches it (most of it never is: 66 MiB a frame in Hyrule Field), for about a third more frames @@ -773,7 +775,8 @@ # Windows, 1 (experimental): the game's heap is committed a 64 KiB run at a time where it is first used, instead of # all of it when the game sizes it. Windows counts committed memory against RAM plus the page file whether it is used # or not, and in Castle Town after Hyrule Field about 3.4 of the heap's 6.8 GiB had never been used. 0: committed up -# front. Linux commits pages on first use anyway. +# front. Linux commits pages on first use anyway. With 1, a PC whose commit is used up ends the game at the moment a +# heap page is first used, in the middle of play, where with 0 it would not have started. # heap_lazy = 0 # Linux, shadow storage: 1 clears the write watch's protection of a range in one call before the GPU's bytes are copied # back into the game's memory, instead of taking a fault at the first store into each page. diff --git a/runtime/action_prompt.c b/runtime/action_prompt.c index 22634380..8293a87f 100644 --- a/runtime/action_prompt.c +++ b/runtime/action_prompt.c @@ -1,6 +1,8 @@ // The action prompt's diamond of face buttons for the pad layout at start. The action prompt (top right) shows the // four face buttons as a diamond in the Switch's places, A on the right. Under a layout by label (NX2NAVI_PAD_LAYOUT = -// xbox, playstation or auto; input_map.c input_pad_layout_pick) A is the bottom button, so romfs.c +// xbox or playstation; input_map.c input_pad_layout_pick) A is the bottom button, and auto is treated the same at +// start, when no pad is open yet: a pad that labels its right button A then plays by position under auto and sees +// the diamond the other way round, which docs/controls.md says and nintendo avoids. So romfs.c // (action_prompt_overlay) serves the shared interface layouts with the diamond laid out as such a pad has its buttons: // A bottom, B right, X left, Y top. The game reads the layouts once at start, so a change of layout shows after a // restart. diff --git a/runtime/camera_speed.c b/runtime/camera_speed.c index bd330519..50237675 100644 --- a/runtime/camera_speed.c +++ b/runtime/camera_speed.c @@ -183,6 +183,11 @@ static int rewrite(float k, float to, int fresh) { continue; } while (a < stop) { + uint64_t skip = mem_untouched_run(a, stop); // heap pages the lazy heap never committed hold nothing + if (skip) { + a = skip; + continue; + } uint64_t n = stop - a < PIECE ? stop - a : PIECE; if (n >= len && mem_read_hold(a, n)) { const uint8_t *p = (const uint8_t *)(uintptr_t)a, *e = p + n; diff --git a/runtime/cfg.c b/runtime/cfg.c index 7f727670..d9799363 100644 --- a/runtime/cfg.c +++ b/runtime/cfg.c @@ -254,7 +254,7 @@ static const CfgMeta metas[] = { {"GATE_WAIT_MS", "10", "0..100", RESTART}, // vk_exec.c vk_exec_gate_wait {"LIB_PREBUILD", "1", "0|1", RESTART}, {"LIB_PREBUILD_THREADS", "", "1..64", RESTART}, {"LIB_WARM", "1", "0|1|force", RESTART}, // vk_pipeline.c lw_start: the driver's cache of stage libraries, warmed when cold - {"PIPE_PREBUILD", "1", "0|1", RESTART}, {"PREWARM_XLATE", "", "1..64", RESTART}, {"GPL", "", "0|1", RESTART}, // vk_caps.c vk_caps_gpl: by the driver, off with AMD's for RDNA 1 and 2 + {"PIPE_PREBUILD", "1", "0|1", RESTART}, {"PREWARM_XLATE", "", "1..64", RESTART}, {"GPL", "", "0|1", RESTART}, // vk_caps.c vk_caps_gpl: by the driver, off with AMD's for RDNA 2 and older {"LIB_PREBUILD_BIG_BG", "1", "0|1", RESTART}, {"PIPE_FEEDBACK", "1", "0|1|2", RESTART}, {"PIPELINE_CACHE_MAX_MB", "512", "0..65536", RESTART}, {"PIPE_BINARIES", "0", "0|1", RESTART}, {"ONESHOT_WAIT_MS", "30000", "0..60000", RESTART}, {"ONESHOT_BUDGET", "1", "0|1", RESTART}, {"PIPE_BINARIES_MAX_MB", "1024", "0..65536", RESTART}, {"BIG_BUILD_FREE_MB", "8192", "0..262144", RESTART}, {"BIG_BUILD_MAX_MB", "4096", "0..262144", RESTART}, diff --git a/runtime/dlssg/nx2_sl.cpp b/runtime/dlssg/nx2_sl.cpp index 0307e7e3..fc6caa2a 100644 --- a/runtime/dlssg/nx2_sl.cpp +++ b/runtime/dlssg/nx2_sl.cpp @@ -165,7 +165,7 @@ extern "C" NX2SL_API void nx2sl_log_unavailable(VkPhysicalDevice physical) { sl::AdapterInfo adapter{}; adapter.vkPhysicalDevice = physical; sl::Result r = supported(sl::kFeatureDLSS_G, adapter); - say("dlssg: frame generation support on the selected Vulkan adapter: %s (%d)", sl::getResultAsStr(r), (int)r); + say("dlssg: Streamline's support check for frame generation: %s (%d)", sl::getResultAsStr(r), (int)r); } // Streamline sets Reflex's mode on the swapchain it was told of last (VK_NV_low_latency2) and drops it while there is diff --git a/runtime/dlssg/nx2_sl.h b/runtime/dlssg/nx2_sl.h index ab97a5c0..fcbf4eff 100644 --- a/runtime/dlssg/nx2_sl.h +++ b/runtime/dlssg/nx2_sl.h @@ -44,7 +44,8 @@ typedef PFN_vkGetInstanceProcAddr (*PFN_nx2sl_instance_proc)(void); typedef PFN_vkGetDeviceProcAddr (*PFN_nx2sl_device_proc)(void); // 1 when DLSS-G loaded and supports this GPU (after the device exists) typedef int (*PFN_nx2sl_available)(void); -// Optional: log Streamline's support result on the selected Vulkan device after availability failed. +// Optional: log Streamline's support result for frame generation after availability failed (its general check of +// the system, driver and adapters: Streamline looks at one adapter only when given its LUID, which Vulkan has not). typedef void (*PFN_nx2sl_log_unavailable)(VkPhysicalDevice physical); // Reflex's low-latency mode, 0 off, 1 on, 2 on with boost: after the device, and after each swapchain the game creates // through the interposer (the mode is the swapchain's) diff --git a/runtime/host.h b/runtime/host.h index 23be2bc7..9419b36d 100644 --- a/runtime/host.h +++ b/runtime/host.h @@ -227,6 +227,8 @@ void mem_thaw(uint64_t a, uint64_t n); // makes the frozen r // also commits pages the lazy heap left reserved void mem_kernel_access(uint64_t a, uint64_t n); // NX2NAVI_HEAP_LAZY: commits the reserved heap pages of // [a, a+n) before the kernel reads them (a file write) +uint64_t mem_untouched_run(uint64_t a, uint64_t end); // NX2NAVI_HEAP_LAZY: the end of the run of heap pages at a + // that were never committed (a scan steps over it), else 0 int mem_host_readable(uint64_t a, uint64_t n); // plat_mem_readable that first commits the untouched // pages of the lazy heap in [a, a+n), which read zero void mem_freeze_stats(uint64_t *frozen, uint64_t *freezes, uint64_t *fault_thaws, uint64_t *other_thaws); diff --git a/runtime/memory.c b/runtime/memory.c index d122f9d1..e871c6bb 100644 --- a/runtime/memory.c +++ b/runtime/memory.c @@ -658,6 +658,15 @@ void mem_kernel_access(uint64_t a, uint64_t n) { plat_mutex_unlock(&frz_lock); } +// For a host scan of guest memory (camera_speed.c rewrite): the end of the run of pages at a that the lazy heap left +// reserved, at most end, or 0 when the page at a is not such a page. Nothing was ever written there, so a scan steps +// over the run; reading it would commit every page of the heap and undo what the setting saves. +uint64_t mem_untouched_run(uint64_t a, uint64_t end) { + if (!heap_lazy || a < L.heap.base || a >= L.heap.base + L.heap.size) return 0; + uint64_t y, x = a & ~(PAGE - 1); + return plat_mem_state(x, end, &y) == PLAT_MEM_RESERVED && y > a ? y : 0; +} + // plat_mem_readable for a pointer the host is about to read: untouched heap pages of a lazy heap count as readable // (they are committed here, and read zero as they would have). int mem_host_readable(uint64_t a, uint64_t n) { @@ -1417,9 +1426,6 @@ void mem_kernel_free(void *p, uint64_t size) { plat_mutex_unlock(&klock); } -// NX2NAVI_TRACE=perf: how many bytes of each guest region are committed, so a process that has grown says which region -// holds the memory. A region is reserved whole at start-up and committed page by page, so the walk reads each run of -// pages of the same state; the alias window is the largest and costs the most steps when it is full. // NX2NAVI_TRACE=perf: what the lazy heap's first-use faults cost since the last line (perf.c, every perf window that had // some), so that a hitch can be told from them: the time includes the wait for frz_lock. void mem_lazy_heap_report(uint64_t frame) { @@ -1437,6 +1443,9 @@ void mem_lazy_heap_report(uint64_t frame) { faults_prv = faults, bytes_prv = bytes; } +// NX2NAVI_TRACE=perf: how many bytes of each guest region are committed, so a process that has grown says which region +// holds the memory. A region is reserved whole at start-up and committed page by page, so the walk reads each run of +// pages of the same state; the alias window is the largest and costs the most steps when it is full. void mem_guest_census(uint64_t frame) { const RtGuestLayout *g = mem_layout(); const struct { const char *name; RtRange r; } reg[] = { diff --git a/runtime/nvn_api_core.c b/runtime/nvn_api_core.c index 9e9b6a91..b0236a68 100644 --- a/runtime/nvn_api_core.c +++ b/runtime/nvn_api_core.c @@ -700,8 +700,10 @@ NVN_API(nvnQueueSubmitCommands) { // NX2NAVI_SUBMIT_FINISH=: bit i waits after the i-th submission of a frame (default 0x1); a plain 1 waits // after every submission, 0 never. // The wait is for the submission's passes, which is what the rewrite races with: exec_wait leaves out the - // write-backs of what they wrote (vk_sync.c exec_tl), which nothing here needs before the game goes on. The fences - // the game itself waits on cover them as before. + // write-backs of what they wrote (vk_sync.c exec_tl), which nothing here needs before the game goes on where the + // pools are imports or mirrors. With shadow storage the write-backs reach guest memory later and the game must + // not rewrite a pool before them, so there the wait covers them (vk_sync_exec_wait). The fences the game itself + // waits on cover them as before. static long long finish_mask = -2; if (finish_mask == -2) { const char *e = cfg_get("NX2NAVI_SUBMIT_FINISH"); diff --git a/runtime/platform/linux.c b/runtime/platform/linux.c index 7d382090..fff631a3 100644 --- a/runtime/platform/linux.c +++ b/runtime/platform/linux.c @@ -2229,7 +2229,7 @@ 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. +// Swap is not a page file Windows sizes, and nothing here advises on its size. int plat_page_file_managed(void) { return -1; } // ---- child processes diff --git a/runtime/platform/platform.h b/runtime/platform/platform.h index 2ac9355b..858cd022 100644 --- a/runtime/platform/platform.h +++ b/runtime/platform/platform.h @@ -488,7 +488,7 @@ 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 +// Whether Windows sizes every page file by itself (System managed size): 1; 0 when a page file has its sizes 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 diff --git a/runtime/platform/win32.c b/runtime/platform/win32.c index 2bb6b679..8311cb1f 100644 --- a/runtime/platform/win32.c +++ b/runtime/platform/win32.c @@ -1938,7 +1938,7 @@ int plat_page_file_managed(void) { 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; + r = own && r != 0 ? 1 : 0; // a page file with its sizes set is the user's choice: no advice then } } return managed = r; diff --git a/runtime/ui_text.c b/runtime/ui_text.c index 53191f72..261667a3 100644 --- a/runtime/ui_text.c +++ b/runtime/ui_text.c @@ -434,8 +434,7 @@ static const char *const text_en[UI_TEXT_COUNT] = { [UI_MENU_PREWARM_XLATE] = "Translator prewarm", [UI_MENU_PREWARM_XLATE_DESC] = "Threads that translate known shaders at start; Automatic takes half the CPU threads, at most 16.", [UI_MENU_GPL] = "Graphics pipeline library", - [UI_MENU_GPL_DESC] = "Uses the Vulkan pipeline library extension. Default: off on AMD's driver for RDNA 1 and 2, " - "which crashes when it links a burst of new pipelines, on elsewhere.", + [UI_MENU_GPL_DESC] = "Uses the Vulkan pipeline library extension; turn off if your driver misbehaves with it.", [UI_MENU_ASTC] = "ASTC texture decoding", [UI_MENU_ASTC_DESC] = "Where compressed ASTC textures are decoded.", [UI_MENU_ASTC_AUTO] = "Automatic", [UI_MENU_ASTC_GPU] = "GPU shader", [UI_MENU_ASTC_NATIVE] = "Native", [UI_MENU_ASTC_CPU] = "CPU", @@ -593,7 +592,7 @@ static const char *const text_en[UI_TEXT_COUNT] = { [UI_MENU_FRAMEGEN_MULT_RTX50] = "3x and 4x need an RTX 50 GPU", [UI_MENU_FRAMEGEN_MULT_UPTO] = "up to %dx on this GPU and driver", [UI_MENU_FRAMEGEN_MULT_OLD_DLL] = "2x only: update the DLSS frame generation files", - [UI_MENU_FRAMEGEN_UNAVAILABLE] = "in use: off, see dlssg in the game log", + [UI_MENU_FRAMEGEN_UNAVAILABLE] = "in use: off: it needs an RTX 40 or newer; if you have one, see dlssg in the log", [UI_MENU_FRAMEGEN_OFF_FILES] = "in use: off, its files are missing", [UI_MENU_GAME_RES] = "Resolution", [UI_MENU_GAME_RES_DESC] = "Height of the game's picture. Higher is sharper but costs more GPU power.", @@ -1008,8 +1007,7 @@ static const char *const text_ru[UI_TEXT_COUNT] = { [UI_MENU_PREWARM_XLATE] = "Прогрев транслятора", [UI_MENU_PREWARM_XLATE_DESC] = "Потоки, которые при запуске переводят известные шейдеры; «Автоматически» — половина потоков процессора, не больше 16.", [UI_MENU_GPL] = "Библиотека графических конвейеров", - [UI_MENU_GPL_DESC] = "Использует расширение Vulkan для библиотек конвейеров. По умолчанию выключено на драйвере AMD " - "для RDNA 1 и 2, который падает, когда связывает много новых конвейеров, включено везде.", + [UI_MENU_GPL_DESC] = "Использует расширение Vulkan для библиотек конвейеров; выключите, если драйвер с ним сбоит.", [UI_MENU_ASTC] = "Распаковка текстур ASTC", [UI_MENU_ASTC_DESC] = "Где распаковываются сжатые текстуры ASTC.", [UI_MENU_ASTC_AUTO] = "Автоматически", [UI_MENU_ASTC_GPU] = "Шейдер GPU", [UI_MENU_ASTC_NATIVE] = "Аппаратно", [UI_MENU_ASTC_CPU] = "Процессор", @@ -1399,8 +1397,7 @@ static const char *const text_pt_br[UI_TEXT_COUNT] = { [UI_MENU_PREWARM_XLATE] = "Pré-aquecimento do tradutor", [UI_MENU_PREWARM_XLATE_DESC] = "Threads que traduzem os shaders conhecidos ao iniciar; Automático usa metade das threads da CPU, no máximo 16.", [UI_MENU_GPL] = "Biblioteca de pipelines gráficos", - [UI_MENU_GPL_DESC] = "Usa a extensão de biblioteca de pipelines do Vulkan. Padrão: desligado no driver da AMD para " - "RDNA 1 e 2, que trava ao vincular uma rajada de pipelines novos, ligado nos demais.", + [UI_MENU_GPL_DESC] = "Usa a extensão de biblioteca de pipelines do Vulkan; desligue se o seu driver tiver problemas com ela.", [UI_MENU_ASTC] = "Decodificação de texturas ASTC", [UI_MENU_ASTC_DESC] = "Onde as texturas comprimidas em ASTC são decodificadas.", [UI_MENU_ASTC_AUTO] = "Automático", [UI_MENU_ASTC_GPU] = "Shader da GPU", [UI_MENU_ASTC_NATIVE] = "Nativo", @@ -2061,8 +2058,7 @@ static const char *const text_zh_hans[UI_TEXT_COUNT] = { [UI_MENU_PREWARM_XLATE_DESC] = "启动时转译已知着色器的线程数;自动取 CPU 线程数的一半," "最多 16。", [UI_MENU_GPL] = "图形管线库", - [UI_MENU_GPL_DESC] = "使用 Vulkan 管线库扩展。默认:在 AMD RDNA 1 和 2 驱动上关闭,因为驱动在链接大量新管线时会崩溃," - "其他平台默认开启。", + [UI_MENU_GPL_DESC] = "使用 Vulkan 管线库扩展;驱动对此有问题时请关闭。", [UI_MENU_ASTC] = "ASTC 纹理解码", [UI_MENU_ASTC_DESC] = "压缩的 ASTC 纹理在哪里解码。", [UI_MENU_ASTC_AUTO] = "自动", diff --git a/runtime/vk.h b/runtime/vk.h index 8c2e1b03..0ba62f80 100644 --- a/runtime/vk.h +++ b/runtime/vk.h @@ -58,7 +58,7 @@ typedef struct { uint32_t sbuf_align; // nonzero: storage buffers bind at their address rounded down to this uint32_t sg_size; // requiredSubgroupSize on sg_pin_stages (32) VkShaderStageFlags sg_pin_stages; // 0: no pin, stages run at the device's subgroupSize - int gpl; // VK_EXT_graphics_pipeline_library enabled with fast linking (NX2NAVI_GPL=0/1; unset: off on AMD RDNA 1/2) + int gpl; // VK_EXT_graphics_pipeline_library enabled with fast linking (NX2NAVI_GPL=0/1; unset: off on AMD's driver for RDNA 2 and older) int amd; // an AMD GPU (vendor 0x1002), any driver int amd_sc; // AMD's Windows driver branch for RDNA 1 and 2 ("AMD proprietary shader compiler") int amd_llpc; // AMD's own driver for RDNA 3 and newer (its driverInfo names LLPC) diff --git a/runtime/vk_astc.c b/runtime/vk_astc.c index 04bda92b..c62e9b03 100644 --- a/runtime/vk_astc.c +++ b/runtime/vk_astc.c @@ -435,7 +435,8 @@ static void mem_barrier(VkCommandBuffer cb, VkPipelineStageFlags2 src, VkAccessF // So each is kept to half a VMA block (vk_vma_block_size's default 64 MiB), where VMA still places it in a block, not in // memory of its own: 16 MiB of texels, their blocks and BC3 fit in 32 MiB. #define CHUNK_OUT (16ull << 20) -#define SCRATCH_MIN (32ull << 20) // the shared scratch's least size: one chunk's texels plus its blocks and BC3 +#define SCRATCH_MIN (32ull << 20) // the shared scratch's usual least size: one chunk's texels plus its blocks and BC3 + // (smaller only after the device ran out of memory: scratch_get's second try) // The shared scratch and the command buffer it was last recorded into. An upload batch's command buffer is used again // only after that batch completed (vk_memory.c: completed batches go to the spare list), so the same handle means the diff --git a/runtime/vk_caps.c b/runtime/vk_caps.c index 584e255c..bcd0bde2 100644 --- a/runtime/vk_caps.c +++ b/runtime/vk_caps.c @@ -6,8 +6,8 @@ // - minStorageBufferOffsetAlignment above 4 (NX2NAVI_VK_SSBO_ALIGN= raises it): the translator computes // storage offsets from the guest address rounded down to that alignment (the translator's unaligned storage path), and // vk_exec_draw.c ssbo_info binds the rounded-down address, so shader writes reach guest memory instead of a copy. -// - no graphics pipeline library with fast linking (NX2NAVI_GPL=0 forces it; it also defaults off on AMD's RDNA 1/2 -// Windows driver branch, see vk_caps_gpl): vk_caps.gpl = 0, the extension stays disabled, and pipelines are built whole. +// - no graphics pipeline library with fast linking (NX2NAVI_GPL=0 forces it; it also defaults off on AMD's Windows +// driver for RDNA 2 and older, see vk_caps_gpl): vk_caps.gpl = 0, the extension stays disabled, and pipelines are built whole. // - no 32-lane pin (no subgroupSizeControl, 32 outside min..max, or NX2NAVI_VK_SUBGROUP_PIN=0): stages run at the // device's subgroupSize (SPIR-V 1.3 without a required size) and the translator is told that size. // NX2NAVI_VK_SUBGROUP= pins n instead of 32 where the driver allows it. @@ -56,14 +56,16 @@ void vk_caps_driver(VkPhysicalDevice p) { } // NX2NAVI_GPL=0 turns graphics pipeline libraries off and 1 turns them on; unset, they are on wherever the driver has -// fast linking, except on AMD's Windows driver for RDNA 1 and 2 (vk_caps.amd_sc). That driver faults inside -// vkCreateGraphicsPipelines when it links the stage libraries of a burst of new pipelines, which the game asks for when -// an enemy dies and its effects are drawn for the first time: the game ended with code 71 at every kill on an RX 6700 XT -// and an RX 5700 XT (issue #418), and no longer with gpl = 0. The price there is that a new pipeline is built whole on -// the draw's path (frames of 0.5-1.2 s on an RX 6600 while an area is new). The same driver had libraries off before, -// for device losses at boot (issue #5), whose cause was GPU writes past 3D BC images (vk_exec.c, "3D BC render -// targets"); it boots with them since that was fixed, so gpl = 1 is there to try on a newer driver. RDNA 4 under RADV -// is reported to hang the GPU at the title screen with GPL. +// fast linking, except on AMD's Windows driver for RDNA 2 and older (vk_caps.amd_sc: RX 400 to RX 6000). On an +// RX 6700 XT the game ended with code 71 at every enemy killed, from 0.5 on, and no longer with gpl = 0; an RX 5700 XT +// showed the same ending (issue #418). Where in the driver it faults is not established: a kill draws its effects +// for the first time, which asks for a burst of new pipelines linked from stage libraries, and building them whole +// avoids it. The price there: with libraries off a pipeline the game needs for the first time is built whole by a +// worker while its draw is left out (vk_exec_draw.c, "pipeline still building"), so new things appear some frames +// late, and the start waits for every recorded pipeline instead of 20 s at most (vk_pipeline.c prewarm_run). The +// same driver had libraries off before, for device losses at boot (issue #5), whose cause was GPU writes past 3D BC +// images (vk_exec.c, "3D BC render targets"); it boots with them since that was fixed, so gpl = 1 is there to try on +// a newer driver. RDNA 4 under RADV is reported to hang the GPU at the title screen with GPL. int vk_caps_gpl(const VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT *f, const VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT *p) { gpl_lib = f->graphicsPipelineLibrary; @@ -74,7 +76,7 @@ int vk_caps_gpl(const VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT *f, gpl_off = vk_caps.gpl ? NULL : !gpl_lib || !gpl_fast ? "the driver has no fast linking" : e && *e ? "NX2NAVI_GPL=0" - : "default on AMD's driver for RDNA 1 and 2 (NX2NAVI_GPL=1 turns it on)"; + : "default on AMD's driver for RDNA 2 and older (NX2NAVI_GPL=1 turns it on)"; return vk_caps.gpl; } diff --git a/runtime/vk_texture.c b/runtime/vk_texture.c index 3ae53776..39b11a15 100644 --- a/runtime/vk_texture.c +++ b/runtime/vk_texture.c @@ -1555,6 +1555,9 @@ static int idle(const Tex *e, uint64_t now); // or the page file, and the VRAM pressure and tex_disuse already bound it. Counted in, it held the bound all through // Hyrule Field on a 16 GB PC with a 16 GB card (RTX 5060 Ti): the process at 28.9 to 30.7 GB, 9 to 10 GB of it video // memory, while the system had 8 to 10 GB of its 49 GB limit free, and the ground flickered white as textures gave way. +// So on such a PC the 42 GB process above, half of it device memory, no longer reaches this bound: the system's +// figure and the VRAM pressure are what act there. The part of a card's memory the driver has moved into shared +// system memory is subtracted too, which it should not be; the driver does not report it apart. #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 @@ -1616,8 +1619,9 @@ static int commit_is_low(void) { char note[2048]; // the platform layer's own lines go to stderr only, so the log file gets this through here if (plat_commit_note(note, sizeof note)) rt_log("platform: %s", note); if (proc_commit_mb) - rt_log("vk: textures give way as under low commit while this process's commit is above %lld MB " - "(NX2NAVI_TEX_PROC_COMMIT_MB; 0 = off)", proc_commit_mb); + rt_log("vk: textures give way as under low commit while this process's commit%s is above %lld MB " + "(NX2NAVI_TEX_PROC_COMMIT_MB; 0 = off)", + plat_commit_counts_vram() ? ", less the video memory in use on a discrete card," : "", proc_commit_mb); } if (!commit_free_mb && !proc_commit_mb) return 0; double proc, used, limit; @@ -1655,9 +1659,10 @@ static int commit_is_low(void) { 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)"); + rt_log("vk: the page file is sized by Windows, which grows it only slowly: a fixed page file whose initial " + "size is at least 32 GB keeps the system's commit from running low (System > About > Advanced system " + "settings > Performance Settings > Advanced > Virtual memory > Change: untick the automatic " + "management, Custom size with an initial size of 32768 MB or more, Set, then restart Windows)"); } } sys_low = low; diff --git a/tests/frame_step_check.c b/tests/frame_step_check.c index 7628ec1a..3277bc63 100644 --- a/tests/frame_step_check.c +++ b/tests/frame_step_check.c @@ -39,6 +39,8 @@ void rtss_limit_want(int64_t millihz) { (void)millihz; } int rtss_limit_active(void) { return 0; } int plat_mem_read(uint64_t a, void *out, size_t n) { (void)a, (void)out, (void)n; return 0; } int plat_mem_readable(uint64_t a, size_t n) { (void)a, (void)n; return 0; } +void mem_kernel_access(uint64_t a, uint64_t n) { (void)a, (void)n; } +int mem_host_readable(uint64_t a, uint64_t n) { (void)a, (void)n; return 0; } uint64_t kern_now_ns(void) { return 0; } // ---- one present: the pacer asks for the period, then reports the display time. Returns the step in 60 Hz frames. diff --git a/tests/save_share_check.c b/tests/save_share_check.c index bd131037..1ce7cde2 100644 --- a/tests/save_share_check.c +++ b/tests/save_share_check.c @@ -63,6 +63,8 @@ NEVER(romfs_overlay_count) NEVER(romfs_read) NEVER(romfs_size) NEVER(rt_call) NEVER(perf_size) // fs_host_sizes: only the perf trace's size lines call it int mem_probing(void) { return 0; } // the write-back probe is off int romfs_owns(const char *path) { (void)path; return 0; } +int mem_probing(void) { return 0; } // write_file asks before each write: no probe, no lazy heap here +void mem_kernel_access(uint64_t a, uint64_t n) { (void)a, (void)n; } void fs_trace(const char *what, const char *path) { (void)what, (void)path; } int main(int argc, char **argv) {