Give nx2navi_dlssg.dll an export that hands Reflex a frame limit along with its mode.

With DLSS Frame Generation, nx2sl_reflex set only Reflex's mode, so Reflex's sleep at the frame's start held no
limit and the game's own pacer held the finished picture until its turn, a second wait after the frame was drawn
(A-16, P-16). nx2sl_reflex2(mode, frame_limit_us) fills sl::ReflexOptions::frameLimitUs as well, which the driver's
sleep then holds as its minimum interval. Streamline drops both with the swapchain and a call without the limit
clears it, so every call carries both. It returns 1 only when Streamline took them and, for a limit, did not report
Reflex's sleep as unavailable on this GPU and driver (slReflexGetState, asked from nx2sl_available on the device's
thread and from nx2sl_reflex2 under the runtime's present lock, never at once). nx2sl_reflex stays exported for older
runtimes and sets the mode alone; NX2SL_VERSION stays 3, since the runtime refuses any other version and the new
export is optional. Built with tools/build_dlssg.py against the pinned Streamline 2.14.16 SDK: clean at /W3, 15
exports including nx2sl_reflex2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
chrg6
2026-11-13 18:52:18 -05:00
parent 405a75f3b1
commit 660c42f6d2
2 changed files with 37 additions and 5 deletions

View File

@@ -42,9 +42,11 @@ PFun_slDLSSGGetState *p_dlssg_state;
PFun_slPCLSetMarker *p_marker;
PFun_slReflexSetOptions *p_reflex;
PFun_slReflexSleep *p_sleep;
PFun_slReflexGetState *p_reflex_state;
PFN_vkGetInstanceProcAddr p_gipa;
PFN_vkGetDeviceProcAddr p_gdpa;
int g_available = -1;
int g_low_latency = -1; // Reflex's low-latency sleep on this GPU and driver (ReflexState), -1 not asked
int g_frames = -1; // the frames to generate as last set (0 off), -1 before the first nx2sl_frame
std::atomic<uint32_t> g_max{0}; // numFramesToGenerateMax as Streamline last reported it, 0 before it did
uint64_t g_presented, g_shown;
@@ -67,6 +69,16 @@ void sl_log(sl::LogType type, const char *msg) {
template <class T> T *proc(const char *name) { return reinterpret_cast<T *>(GetProcAddress(g_mod, name)); }
// Whether Reflex's sleep runs on this GPU and driver: Streamline takes a frame limit without it and then sets nothing
// (its slReflexSetOptions hands the limit to the driver only with the low-latency sleep available). Asked until
// Streamline answers, from nx2sl_available on the device's thread and from nx2sl_reflex2 under the runtime's present
// lock, never at once: slReflexGetState is not thread safe.
void reflex_state() {
static sl::ReflexState rs;
if (g_low_latency < 0 && p_reflex_state && p_reflex_state(rs) == sl::Result::eOk)
g_low_latency = rs.lowLatencyAvailable ? 1 : 0;
}
// The token of frame n. Tokens come from a small ring Streamline reuses, so each call takes its own right before use.
sl::FrameToken *token(uint32_t frame) {
sl::FrameToken *t = nullptr;
@@ -146,6 +158,8 @@ extern "C" NX2SL_API int nx2sl_available(void) {
if (p_feature(sl::kFeaturePCL, "slPCLSetMarker", f) == sl::Result::eOk) p_marker = (PFun_slPCLSetMarker *)f;
if (p_feature(sl::kFeatureReflex, "slReflexSetOptions", f) == sl::Result::eOk) p_reflex = (PFun_slReflexSetOptions *)f;
if (p_feature(sl::kFeatureReflex, "slReflexSleep", f) == sl::Result::eOk) p_sleep = (PFun_slReflexSleep *)f;
if (p_feature(sl::kFeatureReflex, "slReflexGetState", f) == sl::Result::eOk) p_reflex_state = (PFun_slReflexGetState *)f;
reflex_state();
if (!p_dlssg_options || !p_marker) g_available = 0;
// How many frames this GPU and driver generate per rendered frame: known once the device exists (else after the
// first presents, nx2sl_present_marker)
@@ -168,17 +182,30 @@ extern "C" NX2SL_API void nx2sl_log_unavailable(VkPhysicalDevice physical) {
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
// none, so it is set again after each swapchain.
extern "C" NX2SL_API void nx2sl_reflex(int mode) {
if (g_available != 1 || !p_reflex) return;
// Streamline sets Reflex's mode and frame limit on the swapchain it was told of last (VK_NV_low_latency2, the sleep
// mode's minimum interval) and drops them while there is none, so both are set again after each swapchain. Every call
// carries both: a call without the limit clears it. 1 when Streamline took them, unless Streamline said Reflex's sleep
// does not run here (then nothing would hold the limit). Where it has not said, the runtime's own check of the frames
// (dlssg_pace.h) finds a limit nothing holds.
extern "C" NX2SL_API int nx2sl_reflex2(int mode, uint32_t frame_limit_us) {
if (g_available != 1 || !p_reflex) return 0;
reflex_state();
sl::ReflexOptions o{};
o.mode = mode == 2 ? sl::ReflexMode::eLowLatencyWithBoost : mode ? sl::ReflexMode::eLowLatency : sl::ReflexMode::eOff;
o.frameLimitUs = frame_limit_us;
sl::Result r = p_reflex(o);
static int n;
if (n++ < 8) say("dlssg: Reflex low-latency mode %s (%d)", mode == 2 ? "on with boost" : mode ? "on" : "off", (int)r);
if (n++ < 8 || (r != sl::Result::eOk && n < 40)) { // the first calls, and later ones Streamline refused
char limit[48] = "no frame limit";
if (frame_limit_us) snprintf(limit, sizeof limit, "frame limit %.3f ms", frame_limit_us / 1000.0);
say("dlssg: Reflex low-latency mode %s, %s (%d)", mode == 2 ? "on with boost" : mode ? "on" : "off", limit, (int)r);
}
return r == sl::Result::eOk && (!frame_limit_us || g_low_latency != 0);
}
// The runtime before nx2sl_reflex2: the mode alone, no frame limit.
extern "C" NX2SL_API void nx2sl_reflex(int mode) { nx2sl_reflex2(mode, 0); }
// The game's simulation and its recording of the frame's commands are one span here: the guest's threads cannot be
// told apart, so the simulation markers sit at the frame's start and the render submission runs until the frame's
// submissions went out (nx2sl_submit_end).

View File

@@ -50,6 +50,11 @@ 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)
typedef void (*PFN_nx2sl_reflex)(int mode);
// The same with Reflex's frame limit, frame_limit_us microseconds from one frame's start to the next (0 none), which
// the driver's sleep then holds (vk_dlssg.c vk_dlssg_pace). Each call sets both, so the runtime calls only this one when
// the DLL has it. 1 when Streamline took them and, for a limit, Reflex's sleep runs on this GPU and driver. Optional:
// an older DLL lacks it, and the game's own limiter then keeps the frame limit.
typedef int (*PFN_nx2sl_reflex2)(int mode, uint32_t frame_limit_us);
// Frames are numbered by the game's presents: frame n ends with its n-th present (vk_dlssg.c vk_dlssg_game_present).
// Where the game starts a frame: with hold, Reflex's sleep, which holds the start until the time the driver computes
// from the measured render and queue times; then the frame's simulation and render-submit-start markers.