diff --git a/docs/releases/v0.10.2.md b/docs/releases/v0.10.2.md index 828614c9..0f1aa02a 100644 --- a/docs/releases/v0.10.2.md +++ b/docs/releases/v0.10.2.md @@ -12,6 +12,18 @@ game and 1.5 GB for the launcher; a GPU with Vulkan 1.3 and `VK_EXT_external_memory_host` (current NVIDIA and AMD cards). +## Changed + +- **The Game tab's Testing settings are on by default:** + - *CPU helper threads*: Automatic (`replay_workers = auto`): half the CPU's cores less one, none on four cores or + fewer and none on AMD's driver for Radeon RX 6000 and older; + - *Helpers work ahead*: 4 (`replay_pipe = 4`); + - *Helpers take small jobs*: 16 (`replay_small = 16`); + - *Start next frame early*: On (`tail_early = 1`). + + They are still experimental. A value you chose yourself, in the settings menu or in nx2navi.ini, stays as you set + it: to keep 0.10.1's behaviour, set them back to 0 and Off. + ## Fixed - **See-through ground** (issues #569 and #824). 0.10.0 and 0.10.1 shipped an older shader translator, whose shaders diff --git a/nx2navi.ini.example b/nx2navi.ini.example index 9697070a..39d754af 100644 --- a/nx2navi.ini.example +++ b/nx2navi.ini.example @@ -102,31 +102,31 @@ # the game has sometimes rewritten by then. # replay_async = 0 # EXPERIMENTAL. Record the large groups of draws of a submission on this many replay worker threads beside the -# submitting one: auto or 0 to 16. Default: 0, the replay records everything on one thread. auto (the default in -# 0.10.0 only): half the CPU's physical cores less one (2 on 6 cores, 3 on 8, 5 on 12, at most 6), none on four cores or +# submitting one: auto or 0 to 16; 0 records everything on the submitting thread. Default: auto (0 in 0.10.1 only): +# half the CPU's physical cores less one (2 on 6 cores, 3 on 8, 5 on 12, at most 6), none on four cores or # fewer and none on AMD's driver for RX 6000 and older Radeon cards, where a player's game lost its graphics device with # workers on; the log's "replay workers automatic" line says how many. 3 gave 51.7 instead of 43.6 fps standing in Hyrule Field on an RX # 7900 XT, and more than 3 gave nothing more there. Also in the menu: Game, Testing, CPU helper threads. # With 1 a worker records each long run of draws while the submitting thread waits for it: no faster, it checks that # the pictures stay the same. -# replay_workers = 0 +# replay_workers = auto # EXPERIMENTAL. 1 lets the game start on its next frame while the graphics card still runs the end of the last one: the # upscaler (FSR 4, FSR 3.1 or DLSS) and the interface drawn after it. The graphics card runs the same work, with the # copies of what it wrote for the game moved before that end, and the game's waits end once those copies and the -# results it reads are there. A frame whose end hands the game anything else runs as before. Default: 0 (1 in 0.10.0 +# results it reads are there. A frame whose end hands the game anything else runs as before. Default: 1 (0 in 0.10.1 # only). Standing in Hyrule Field on an RX 7900 XT with FSR 4: 50.7 instead of 42.5 fps, and 63.5 # instead of 49.4 with replay_workers = 3. # Does nothing without one of those upscalers, nor with the lazy write-back (the default on NVIDIA and Intel cards). # Also in the menu: Game, Testing, Start next frame early. -# tail_early = 0 -# EXPERIMENTAL, with replay_workers. replay_pipe = N (1 to 4, default 0): the submitting thread goes on past up to N -# groups its workers still record. replay_small = N (0 to 63, default 0, needs replay_pipe): a group of N draws or more +# tail_early = 1 +# EXPERIMENTAL, with replay_workers. replay_pipe = N (0 to 4, default 4): the submitting thread goes on past up to N +# groups its workers still record. replay_small = N (0 to 63, default 16, needs replay_pipe): a group of N draws or more # that is too small to share out (fewer than 64 draws) goes whole to one worker; 64 or more reads as 63. Kokiri Forest # standing on an RX 7900 XT with FSR 4, tail_early = 1, replay_workers = 5, replay_pipe = 4 and replay_small = 16: 63 # instead of 45 fps with all of them off. Also in the menu: Game, Testing, Helpers work ahead (replay_pipe) and Helpers -# take small jobs (replay_small). -# replay_pipe = 0 -# replay_small = 0 +# take small jobs (replay_small). Neither does anything without replay workers. +# replay_pipe = 4 +# replay_small = 16 # EXPERIMENTAL, THE GAME CRASHES WITH 1. 1 lifts the virtual core gate: guest threads run without the console's limit # of one thread per core, and the game keeps per-core data without locks. With 1 the game crashes or hangs, # usually at start. Keep 0 to play. diff --git a/runtime/cfg.c b/runtime/cfg.c index 456088e1..63e66ce8 100644 --- a/runtime/cfg.c +++ b/runtime/cfg.c @@ -328,11 +328,11 @@ static const CfgMeta metas[] = { {"DISCORD_IMAGE", NX2NAVI_DISCORD_IMAGE_DEFAULT, NULL, LIVE}, // Advanced: scheduling {"ASYNC_PREEMPT", "1", "0|1|2", RESTART}, {"REPLAY_ASYNC", "0", "0|1", RESTART}, - {"REPLAY_PIPE", "0", "0..4", RESTART}, - {"REPLAY_WORKERS", "0", "auto|0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16", RESTART}, // vk_exec.c rw_init, vk_exec_replay_workers_auto - // vk_exec.c "the early tail"; off by default (on in 0.10.0 only) - {"TAIL_EARLY", "0", "0|1", RESTART}, - {"REPLAY_SMALL", "0", "0..63", RESTART}, // vk_exec.c group_start + {"REPLAY_PIPE", "4", "0..4", RESTART}, + {"REPLAY_WORKERS", "auto", "auto|0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16", RESTART}, // vk_exec.c rw_init, vk_exec_replay_workers_auto + // vk_exec.c "the early tail"; on by default (off in 0.10.1 only) + {"TAIL_EARLY", "1", "0|1", RESTART}, + {"REPLAY_SMALL", "16", "0..63", RESTART}, // vk_exec.c group_start {"FREE_CORES", "0", "0|1", RESTART}, {"TIMER_1MS", "1", "0|1", RESTART}, // dynamic resolution (dynres.c): the scene follows the GPU time toward DYN_RES_TARGET milliseconds (under 1: the // frame limit's frame time), holding each step DYN_RES_STEP 60 Hz frames of the clock diff --git a/runtime/ui/menu.cpp b/runtime/ui/menu.cpp index 32c465a5..5eedcebc 100644 --- a/runtime/ui/menu.cpp +++ b/runtime/ui/menu.cpp @@ -1476,7 +1476,7 @@ static void tab_game(float scale) { opt_group_text("Testing"); // vk_exec.c rw_init and tail_enabled read these at start: each row notes the restart and the bar's Restart does // it. A row whose setting does nothing without another one (rw_init) is greyed out until that one is set. - // Automatic first (vk_exec.c vk_exec_replay_workers_auto), named with the count it gives on this PC; 0 is the default + // Automatic first, the default (vk_exec.c vk_exec_replay_workers_auto), named with the count it gives on this PC { static const char *const vals[] = {"auto", "0", "1", "2", "3", "4", "5", "6", "7", "8"}; static char auto_name[64]; diff --git a/runtime/vk_exec.c b/runtime/vk_exec.c index a8acfe18..c9ba2572 100644 --- a/runtime/vk_exec.c +++ b/runtime/vk_exec.c @@ -1138,7 +1138,7 @@ static int split_n = -1, split_idle, split_in_pass, split_draws, split_at, part_ static uint64_t split_ns, part_t0; static uint64_t n_parts, n_subs, n_idle_parts, n_pass_splits, n_cuts; -// ---- the early tail (NX2NAVI_TAIL_EARLY, default 0; on by default in 0.10.0 only) +// ---- the early tail (NX2NAVI_TAIL_EARLY, default 1; off by default in 0.10.1 only) // Standing in Hyrule Field with FSR 4 (RX 7900 XT, 2026-11-13) a frame's main submission ends with one part of 4.7 ms // of GPU time, the temporal upscaler and the interface drawn after it, and one of 1.4 ms, the write-backs of what the // shaders wrote into VRAM mirrors. The game waits for all of it before it writes the next frame's data (the wait after @@ -1890,16 +1890,17 @@ static void op_one(VkExec *ex, const op_hdr *h) { } } -// ---- replay workers (NX2NAVI_REPLAY_WORKERS, default 0: the replay records every op on the submitting thread; auto: -// as many as the CPU's cores make worth it, vk_exec_replay_workers_auto, the default in 0.10.0 only; the Game tab's CPU -// helper threads row sets it) +// ---- replay workers (NX2NAVI_REPLAY_WORKERS, default auto: as many as the CPU's cores make worth it, +// vk_exec_replay_workers_auto; 0, the default in 0.10.1 only: the replay records every op on the submitting thread; +// the Game tab's CPU helper threads row sets it) // 3 workers: on an RX 7900 XT with a Ryzen 7 5800X3D, Hyrule Field standing went from 41.2 to 50.4 fps (2026-11-12, one // batch), with the same pictures there and in Kokiri Forest, Kakariko, Goron City, a graveyard house and Gerudo -// Valley; 5 or 7 workers measured the same as 3. Default 0 again after players' games crashed with 3 on by default +// Valley; 5 or 7 workers measured the same as 3. Players' games crashed with 3 on by default // (0.9.1, 0.9.2): an RX 6700 XT lost its device after about 18,000 frames, its last mark the game's own compute // dispatch (op_dispatch: 20x1x1 groups of cs d7566eb1, a depth probe) and nothing after it marked, since the group of // draws that followed was recorded by the workers, whose passes have no marks; an occlusion query slot was found -// unavailable after its fence just before. An RX 6600 lost it the same way twice (0.9.2). The cause is not known. +// unavailable after its fence just before. An RX 6600 lost it the same way twice (0.9.2). The cause is not known; +// auto gives that driver no workers (vk_exec_replay_workers_auto), and auto is the default again since 0.10.2. // A submission's draws come in runs between ops that have to stay in the replay's order on its own thread (dispatches, // copies, texture and buffer clears, events, the game's barriers, calls). Hyrule Field, standing (NX2NAVI_REPLAY_RUNS, // 2026-11-11): about 3,500 draws a frame in 82 runs, 96% of them in runs of 16 draws or more and 67% in runs of 256 or @@ -1922,7 +1923,7 @@ static void op_one(VkExec *ex, const op_hdr *h) { // replays the op again once in turn, and so do the contact shadows' pass and the HDR pass's source. Per-object motion // lists its draws under a lock (vk_exec_draw.c mvobj_draw), and the occlusion queries' slots and segments follow the // stream (vk_exec_xfer.c vk_exec_xfer_slots, vk_exec_xfer_group_merge). -// NX2NAVI_REPLAY_PIPE=N (1-4, default 0): the submitting thread hands the group to the workers, takes chunks of it as +// NX2NAVI_REPLAY_PIPE=N (0-4, default 4): the submitting thread hands the group to the workers, takes chunks of it as // they do until none is left to take, and goes on with the ops after it, in a new command buffer after the chunks' // places in the part (vk_sync_append_later), while the last chunks are recorded; also into the front pass of the next // group: up to N (at most LIVE_GROUPS) groups are handed out and not in their part yet, their chunks taken from one @@ -2471,7 +2472,7 @@ static const op_hdr *group_start(VkExec *ex, const op_hdr *h, int depth) { int n = (int)(draws / CHUNK_DRAWS); if (n > RW.n + 1) n = RW.n + 1; if (n > GROUP_CHUNKS) n = GROUP_CHUNKS; - // NX2NAVI_REPLAY_SMALL=N (default 0, with NX2NAVI_REPLAY_PIPE): a group of N draws or more that is too small to cut + // NX2NAVI_REPLAY_SMALL=N (default 16, with NX2NAVI_REPLAY_PIPE): a group of N draws or more that is too small to cut // goes whole to one worker, and the submitting thread goes on with what follows it without taking the chunk // itself. Kokiri Forest standing, 5 workers, 4 groups out, the early tail on (2026-11-13): about 700 draws a frame // were in such groups, 3.6 ms of the submitting thread; with 16 the replay took 10.2 instead of 11.0 ms a frame