Enable the drivers' low-latency extensions only in a run that starts with the setting on.

With the setting off the device is made exactly as before the mode existed, so the mode cannot change anything for a player who never turns it on. Turning it on in the menu then takes a restart, and the row says so; in a run that started with it on, changes still apply at once.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
chrg6
2026-11-12 22:30:56 -05:00
parent b197136f56
commit 9100509b00
7 changed files with 33 additions and 9 deletions

View File

@@ -233,6 +233,8 @@ keeps per-core data without locks and relies on that, so the host models it (`ru
limit back while a movie plays, while no present goes out, and for the rest of the run when the frames show that the
driver does not hold it. DLSS Frame Generation brings its own low latency (`vk_dlssg.c`), and the direct path is off
in such a run; NVIDIA's is also off while another frame generation presents the frames.
The device gets the drivers' extensions only in a run that starts with the setting on (`vk_device.c`
`create_device`): turning it on takes a restart, and with it off the device is made as it was before the mode.
- ASTC textures are uploaded as they are where the device samples ASTC, and decoded by a compute shader everywhere
else (`vk_astc.c`); under 20 GiB of video memory the decoder encodes them to BC3.
- Texture detail (`texture_detail`, `vk_texture.c` "texture detail"): on a card with little video memory the large

View File

@@ -641,8 +641,9 @@
# (VK_AMD_anti_lag), whichever the driver has. On a driver with neither the setting does nothing. While it is on the
# driver holds the game to fps_limit, in place of the game's own limiter and of RivaTuner Statistics Server (rtss).
# With framegen = dlss on Windows, DLSS Frame Generation's own low latency is used and this setting is not; with
# framegen = fsr3 it runs on AMD GPUs and is off on NVIDIA ones while frames are generated. Changes while the game runs
# take effect at once.
# framegen = fsr3 it runs on AMD GPUs and is off on NVIDIA ones while frames are generated. Turning it on takes effect
# at the next start: a run that starts with 0 leaves the driver's mode off the graphics device altogether. In a run
# that started with it on, changes take effect at once.
# low_latency = 0
# HDR output, two kinds (both need HDR on in Windows, "Use HDR", or a Wayland compositor with colour management;

View File

@@ -883,6 +883,7 @@ static const char *vsync_note(void) {
// the driver's mode is NVIDIA's (AMD's has no boost; a 2 set there shows as on, which is what it does). Greyed out with
// the reason where it cannot run: a driver with neither mode, a run in which DLSS frame generation brings its own, and
// NVIDIA's while a frame generation presents the frames (vk_lowlat_status). Set and refused by the driver, it says so.
// In a run that started with it off the driver's mode is not on the device: the row can be set and says "after restart".
static void row_low_latency(void) {
static const char *const values[] = {"0", "1", "2"};
const char *names[] = {menu_text(UI_MENU_OFF), menu_text(UI_MENU_ON), menu_text(UI_MENU_LOW_LATENCY_BOOST)};
@@ -892,7 +893,8 @@ static void row_low_latency(void) {
if (st == VK_LOWLAT_NO_DRIVER) opt_disable_next(UI_MENU_LOW_LATENCY_NO_DRIVER);
else if (st == VK_LOWLAT_DLSSG) opt_disable_next(UI_MENU_LOW_LATENCY_DLSSG);
else if (st == VK_LOWLAT_FG) opt_disable_next(UI_MENU_LOW_LATENCY_FG);
const char *note = st == VK_LOWLAT_FAILED && cur ? menu_text(UI_MENU_LOW_LATENCY_FAILED) : nullptr;
const char *note = st == VK_LOWLAT_FAILED && cur ? menu_text(UI_MENU_LOW_LATENCY_FAILED)
: st == VK_LOWLAT_RESTART && cur ? menu_text(UI_MENU_AFTER_RESTART) : nullptr;
int nv = opt_choice_index("NX2NAVI_LOW_LATENCY", UI_MENU_LOW_LATENCY, names, n, cur, 0, UI_MENU_LOW_LATENCY_DESC, note);
if (nv >= 0 && nv < n) opt_apply("NX2NAVI_LOW_LATENCY", values[nv]);
}

View File

@@ -57,6 +57,7 @@ typedef struct VkCtx {
has_low_latency2, // VK_NV_low_latency2 with VK_KHR_present_id, which it needs (vk_lowlat.c)
has_present_id, // ... and the presentId feature: presents may carry a frame's number
has_anti_lag, // VK_AMD_anti_lag with its antiLag feature (vk_lowlat.c)
lists_low_latency, // the driver lists one of the two, left out: the run started with the mode off
has_mesh_queries; // meshShaderQueries, kept while NX2NAVI_GPU_PASSES or NX2NAVI_DUMP is set
int mesh_fallback; // task and mesh programs run as compute passes and are drawn from buffers
// (mesh_fallback: auto without has_mesh_shader; vk_exec_draw.c "mesh fallback")

View File

@@ -513,14 +513,22 @@ static int create_device(void) {
// Low latency without frame generation (vk_lowlat.c): NVIDIA's extension, which needs VK_KHR_present_id enabled
// with it, or AMD's, whose calls need its feature. Each only where the driver lists it; a driver lists one of them
// at most. Not on a device the DLSS Frame Generation proxies make (vk_dlssg_create_device): that path's own
// low-latency plugin asks for what it needs there, and the direct path stays off for the run.
// low-latency plugin asks for what it needs there, and the direct path stays off for the run. And only in a run
// that starts with NX2NAVI_LOW_LATENCY on: with it off the device is made exactly as before the mode existed, and
// a driver that lists one of the two is only remembered (lists_low_latency), so the menu can say that turning the
// setting on takes a restart.
g_vk.lists_low_latency = 0;
if (!vk_dlssg_dpa() && n_ext + 2 <= sizeof ext / sizeof *ext) {
if (has_ext(ep, ne, VK_NV_LOW_LATENCY_2_EXTENSION_NAME) && has_ext(ep, ne, VK_KHR_PRESENT_ID_EXTENSION_NAME)) {
int nv = has_ext(ep, ne, VK_NV_LOW_LATENCY_2_EXTENSION_NAME) && has_ext(ep, ne, VK_KHR_PRESENT_ID_EXTENSION_NAME);
int amd = !nv && has_ext(ep, ne, VK_AMD_ANTI_LAG_EXTENSION_NAME);
if (cfg_int("NX2NAVI_LOW_LATENCY") <= 0) {
g_vk.lists_low_latency = nv || amd;
} else if (nv) {
g_vk.has_low_latency2 = 1;
ext[n_ext++] = VK_KHR_PRESENT_ID_EXTENSION_NAME;
ext[n_ext++] = VK_NV_LOW_LATENCY_2_EXTENSION_NAME;
CHAIN(pid, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR);
} else if (has_ext(ep, ne, VK_AMD_ANTI_LAG_EXTENSION_NAME)) {
} else if (amd) {
g_vk.has_anti_lag = 1;
ext[n_ext++] = VK_AMD_ANTI_LAG_EXTENSION_NAME;
CHAIN(antilag, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ANTI_LAG_FEATURES_AMD);

View File

@@ -1,4 +1,6 @@
// Low latency without frame generation (NX2NAVI_LOW_LATENCY: 0 off, 1 on, 2 on with boost; default 0; live).
// Low latency without frame generation (NX2NAVI_LOW_LATENCY: 0 off, 1 on, 2 on with boost; default 0). In a run that
// starts with it on, a change takes effect at once; a run that starts with it off makes its device without the
// drivers' extensions (vk_device.c create_device), so turning it on then takes a restart.
//
// A frame's delay from the pad to the screen has two parts the runtime can shorten. The first is the time the frame
// waits after it is drawn: the frame limit used to hold a finished picture back until its slot (vk_backend.c
@@ -504,6 +506,12 @@ void vk_lowlat_init(VkCtx *c) {
}
rt_log("lowlat: VK_AMD_anti_lag is enabled but its entry point is missing");
}
if (c->lists_low_latency) {
L.why = VK_LOWLAT_RESTART;
rt_log("lowlat: the driver has a low-latency mode; this run started with NX2NAVI_LOW_LATENCY off and made its "
"device without it, so the setting takes effect at the next start");
return;
}
rt_log("lowlat: this driver has neither VK_NV_low_latency2 nor VK_AMD_anti_lag: no low-latency mode%s",
level ? " (NX2NAVI_LOW_LATENCY does nothing here)" : "");
}

View File

@@ -14,8 +14,10 @@ void vk_lowlat_release(void);
// What the menu's row shows (ui/menu.cpp row_low_latency). The first two are the paths that can run; the others say
// why none does: the driver has neither extension, DLSS Frame Generation's interposer owns low latency in this run
// (vk_dlssg.c, NX2NAVI_REFLEX), a frame generation presents the frames in the window swapchain's place, or the driver
// refused the mode or never ended its sleep (the log says which).
enum { VK_LOWLAT_NV, VK_LOWLAT_AMD, VK_LOWLAT_NO_DRIVER, VK_LOWLAT_DLSSG, VK_LOWLAT_FG, VK_LOWLAT_FAILED };
// refused the mode or never ended its sleep (the log says which), or the driver has a mode but the run started with
// the setting off, so the device was made without it (vk_device.c create_device): the next start with it on has it.
enum { VK_LOWLAT_NV, VK_LOWLAT_AMD, VK_LOWLAT_NO_DRIVER, VK_LOWLAT_DLSSG, VK_LOWLAT_FG, VK_LOWLAT_FAILED,
VK_LOWLAT_RESTART };
int vk_lowlat_status(void);
// The direct path is in effect now: the setting is on and nothing else owns the presents. Any thread, one atomic load.
int vk_lowlat_on(void);