Give Streamline constants and null tags on a frame without inputs while frame generation is on.

A review of the DLSS and frame generation paths against NVIDIA's guides (the DLSS programming guide 310.6.0, the
DLSS-FG guide 310.7.0, Streamline 2.14.16's guides and headers).

What follows them: the texture bias (log2(render width / output width) - 1 on top of the game's own, at each change
of the mode), the jitter (Halton, 8 x (output / render)^2 phases and 16 at least, in render pixels within half a
pixel, the scene drawn with it and the motion vectors made without it), the creation flags (HDR, vectors at the
render size, auto-exposure, depth not inverted) and the constants Streamline gets (unjittered matrices, the jitter
in pixels, the vectors' scale of one over the render size).

What did not: vk_dlssg.c holds a change of generation's state for FG_SETTLE_FRAMES frames, so for up to 12 frames
after the depth, the motion vectors or the camera went away (a menu, a fade, a load) generation stayed on and
nx2sl_frame set no constants and tagged nothing. Streamline's guide asks for the constants at every frame and for
null tags where depth or motion vectors are not valid; it logged "Unable to find 'common' constants for frame" and
"Failed to find global tag 'kBufferTypeDepth'" and used the frame before's (in every play log with frame
generation). Such a frame now gets the last frame's constants with reset set and the three tags as null. Not run in
a game yet: nx2navi_dlssg.dll builds and exports its 15 functions.

What the guides leave open: the sign of the jitter offsets against the shift of the picture, which NVIDIA says to
find by trying, and the bias reaches only the tiled, trilinear samplers, not every texture of the scene. For both
there is a test switch now, read once: NX2NAVI_DLAA_JITTER_TEST=<x>,<y> tells the upscaler the jitter times two
factors, and NX2NAVI_DLSS_LOD_BIAS_TEST=<bias> puts a bias in the rule's place. Neither was measured: the runs
that would do it start the game.

The line about the presents a second (the commit before) came at 59 a second under a limit of 60: it comes under 56
now.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
chrg6
2026-11-14 12:54:08 -05:00
parent 2dd363b86e
commit 1c3fa7df2a
5 changed files with 65 additions and 10 deletions

View File

@@ -50,7 +50,7 @@ static const char *const known[] = {
"BIND_SKIP", "BIND_STATS", "BIND_UP", "BIND_X", "BIND_Y",
"BIND_ZL", "BIND_ZR", "BLOCK_VIEWS_3D", "CACHE_DIE", "CLOTH_REALTIME", "COLOR_BRIGHTNESS", "COMPUTE_KEEP", "COLOR_CONTRAST", "COLOR_GAMMA",
"COLOR_PRESET", "COLOR_SATURATION", "CONTACT_DEBUG", "CONTACT_SHADOWS", "CORE_DIAG", "CRIT", "CRIT_OUT",
"DMABUF", "DMABUF_MB", "DRAW_CRUMBS", "GPA_LATE", "DESC_TEMPLATE", "DEVICE_PROFILE", "DISCORD_APP_ID", "DISCORD_IMAGE", "DISCORD_RPC", "DLAA", "DLAA_CAM_KEEP", "DLAA_CUT", "DLSS_IDLE_FREE", "DLAA_EDGE_FILL", "DLAA_AT_UPSCALER", "FFX_DEBUG", "FFX_KEYS", "FSR3_SHARPEN", "DLAA_JITTER", "DLAA_MV", "DLAA_MASKDEBUG", "DLAA_MVDEBUG", "DLAA_OBJMV", "DLAA_SURVEY", "DLAA_TARGET_KEEP", "DLAA_TIMES", "DLSS", "DLSS_CHAR_MASK", "DLSS_PRESET", "DLSS_PREBLOOM", "DLSS_RANGE", "DLSS_REACTIVE", "DUMP", "DUMP_BUF", "DUMP_DIR",
"DMABUF", "DMABUF_MB", "DRAW_CRUMBS", "GPA_LATE", "DESC_TEMPLATE", "DEVICE_PROFILE", "DISCORD_APP_ID", "DISCORD_IMAGE", "DISCORD_RPC", "DLAA", "DLAA_CAM_KEEP", "DLAA_CUT", "DLSS_IDLE_FREE", "DLAA_EDGE_FILL", "DLAA_AT_UPSCALER", "FFX_DEBUG", "FFX_KEYS", "FSR3_SHARPEN", "DLAA_JITTER", "DLAA_JITTER_TEST", "DLSS_LOD_BIAS_TEST", "DLAA_MV", "DLAA_MASKDEBUG", "DLAA_MVDEBUG", "DLAA_OBJMV", "DLAA_SURVEY", "DLAA_TARGET_KEEP", "DLAA_TIMES", "DLSS", "DLSS_CHAR_MASK", "DLSS_PRESET", "DLSS_PREBLOOM", "DLSS_RANGE", "DLSS_REACTIVE", "DUMP", "DUMP_BUF", "DUMP_DIR",
"UPSCALER_IDLE_WAIT",
"DUMP_LOG", "DUMP_RAW", "DUMP_SHADERS", "DUMP_FILE", "DS3", "LOG_CS_BINDS", "LOG_TEX_FLAGS", "LOG_MESH", "MESH", "POOL", "NATIVE_TENSOR", "LOG_RCOND", "LOG_RT_SIZES", "DUMP_WHITE", "DUMP_AFTER", "DUMP_MOVIE", "SUBMIT_FINISH", "ACQUIRE_WAIT", "CRASH_FILTER_GUARD", "CS_FALLBACK", "EXEFS", "FEEDBACK", "FIX_C0", "FIX_ZERO", "FLIGHT", "FLIGHT_AT", "FLIGHT_DIR", "FLIGHT_FRAMES", "GPU_TIMELINE",
"FFX_PIPE_CACHE", "FLIGHT_SPIKE_MS", "FREEZE_QUIET_MS", "FREEZE_LOAD_HOLD_MS", "STUTTER_MAP", "PAR_JOBS", "PIPE_RELEASE", "FLIGHT_THUMBS", "FLIGHT_EVERY", "FLIGHT_FLASH", "FLIGHT_FLASH_DIFF", "FLIGHT_FLASH_CELLS", "FLUSH_SPAN", "FLUSH_UPLOAD", "FOG_CULL", "FOLIAGE_SHADOW_CULL", "FOLIAGE_SHADOW_VRS", "FOV", "FOV_PATCH", "FG_HUDLESS_SHOW", "FG_MULT", "FPS60", "FPS_LIMIT", "FRAMEGEN", "FRAMEGEN_MULTIPLIER", "FREEZE_IDLE", "FREEZE_ASYNC", "FREEZE_BUDGET_US", "FREE_CORES",

View File

@@ -237,6 +237,26 @@ extern "C" NX2SL_API int nx2sl_frame(VkCommandBuffer cb, const Nx2SlImage *depth
uint32_t max = g_max.load();
int n = enable > 0 ? enable : 0;
if (n > (int)(max ? max : 1)) n = (int)(max ? max : 1);
static sl::Constants last; // the constants of the last frame that had its inputs
static bool have_last;
if (n && !(depth && mv && cam) && have_last) {
// Generation is on for this present and the frame has no depth, motion vectors or camera: a menu, a fade or a
// load, while vk_dlssg.c holds the change of state for a few frames. Streamline's guide asks for the common
// constants at every frame and for null tags wherever depth or motion vectors are not valid
// (ProgrammingGuideDLSS_G.md 5.2 and 7.0). This frame gave neither, and Streamline logged "Unable to find
// 'common' constants for frame" and "Failed to find global tag 'kBufferTypeDepth'" and went on with what the
// frame before had set. It gets the last frame's constants again, as a new start, and the three tags as null.
sl::Constants c = last;
c.reset = sl::Boolean::eTrue;
p_constants(c, *t, vp);
sl::ResourceTag none[3] = {sl::ResourceTag(nullptr, sl::kBufferTypeDepth, sl::ResourceLifecycle::eValidUntilPresent),
sl::ResourceTag(nullptr, sl::kBufferTypeMotionVectors, sl::ResourceLifecycle::eValidUntilPresent),
sl::ResourceTag(nullptr, sl::kBufferTypeHUDLessColor, sl::ResourceLifecycle::eValidUntilPresent)};
if ((t = token(frame))) p_tag(*t, vp, none, 3, (sl::CommandBuffer *)cb);
static int said;
if (!said++) say("dlssg: a frame without depth and motion vectors while generation is on gets the last constants "
"as a new start and null tags");
}
if (n && depth && mv && cam) {
sl::Constants c{};
matrix(c.cameraViewToClip, cam->view_to_clip);
@@ -259,6 +279,7 @@ extern "C" NX2SL_API int nx2sl_frame(VkCommandBuffer cb, const Nx2SlImage *depth
c.motionVectors3D = sl::Boolean::eFalse;
c.reset = cam->reset ? sl::Boolean::eTrue : sl::Boolean::eFalse;
p_constants(c, *t, vp);
last = c, have_last = true;
sl::Resource rd = resource(depth), rm = resource(mv), rh = hudless ? resource(hudless) : sl::Resource{};
sl::Extent ed{0, 0, depth->width, depth->height}, em{0, 0, mv->width, mv->height};
sl::Extent eh{0, 0, hudless ? hudless->width : 0, hudless ? hudless->height : 0};

View File

@@ -2451,6 +2451,21 @@ static void evaluate(VkExec *ex, const VkExecTarget *dst) {
VkImageView zv = vk_exec_target_view(z->img, z->format, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_DEPTH_BIT, z->level,
z->base_layer, 1, 0);
float jx = D.jit_used ? D.ux : 0.0f, jy = D.jit_used ? D.uy : 0.0f;
// NX2NAVI_DLAA_JITTER_TEST=<x>,<y> (test, read once): the upscaler is told the jitter the scene was drawn with
// times these two factors. NVIDIA's DLSS guide gives no sign for the offsets against the projection's shift and
// says to find it by trying (8.4.1: its development library negates, halves or doubles an axis by a key): the
// picture of a still scene is sharp and stands still only with the right ones. The scene, the motion vectors and
// frame generation keep the real jitter.
static float jt[2] = {1.0f, 1.0f};
static int jt_read;
if (!jt_read) {
jt_read = 1;
const char *s = cfg_get("NX2NAVI_DLAA_JITTER_TEST");
if (s && *s && sscanf(s, "%f,%f", &jt[0], &jt[1]) == 2)
rt_log("dlaa: the upscaler is told the jitter times %g, %g (NX2NAVI_DLAA_JITTER_TEST, a test)", jt[0], jt[1]);
else jt[0] = jt[1] = 1.0f;
}
const float tjx = jx * jt[0], tjy = jy * jt[1];
edge_fill(cb, t, z, D.jit_used); // before the motion vectors read the depth
static int no_mv = -1;
if (no_mv < 0) no_mv = !rt_knob("DLAA_MV", "1"); // NX2NAVI_DLAA_MV=0: zero motion (test)
@@ -2482,7 +2497,7 @@ static void evaluate(VkExec *ex, const VkExecTarget *dst) {
fm = {e->mv, VK_FORMAT_R16G16_SFLOAT, w, h, 1}, fo = {e->out, VK_FORMAT_R16G16B16A16_SFLOAT, ow, oh, 1};
VkFfxCamera fcam;
if (!vk_dlaa_ffx_camera(&fcam)) memset(&fcam, 0, sizeof fcam); // no camera this frame: FSR's fixed values
if (!vk_ffx_upscale(cb, &fc, &fz, &fm, &fo, jx, jy, D.reset, dt > 0 && dt < 100 ? (float)dt : 16.67f, &fcam))
if (!vk_ffx_upscale(cb, &fc, &fz, &fm, &fo, tjx, tjy, D.reset, dt > 0 && dt < 100 ? (float)dt : 16.67f, &fcam))
return dl_fail("FSR 3.1", 0);
static int told;
if (!told++) rt_log("dlaa: FSR 3.1 at %ux%u -> %ux%u (scene format %d, depth format %d)", w, h, ow, oh,
@@ -2557,8 +2572,8 @@ own_feature:
p_setp(p, "Output", &ro);
p_setp(p, "Depth", &rz);
p_setp(p, "MotionVectors", &rm);
p_setf(p, "Jitter.Offset.X", jx);
p_setf(p, "Jitter.Offset.Y", jy);
p_setf(p, "Jitter.Offset.X", tjx);
p_setf(p, "Jitter.Offset.Y", tjy);
p_setf(p, "Sharpness", 0.0f);
p_seti(p, "Reset", D.reset);
// One bias mask: the reactive one (NX2NAVI_DLSS_REACTIVE, a test) where it ran, else the characters' (mv_pass), else
@@ -3221,8 +3236,20 @@ static void dlss_mode(void) {
bias_on = -1;
}
int b = dl_active();
if (b != bias_on)
bias_on = b, vk_sampler_dlss_bias(b ? res_dlss_lod_bias() + (jitter_on() ? JITTER_LOD_BIAS : 0.0f) : 0.0f);
if (b != bias_on) {
// NX2NAVI_DLSS_LOD_BIAS_TEST=<bias> (test, read once): this bias in the place of the rule's while an upscaler
// runs, 0 for none: what the rule's bias does to the picture is the difference between two runs.
static float test;
static int test_on = -1;
if (test_on < 0) {
const char *s = cfg_get("NX2NAVI_DLSS_LOD_BIAS_TEST");
test_on = s && *s;
if (test_on) test = (float)atof(s), rt_log("dlaa: texture LOD bias %g while an upscaler runs "
"(NX2NAVI_DLSS_LOD_BIAS_TEST, a test)", test);
}
float rule = res_dlss_lod_bias() + (jitter_on() ? JITTER_LOD_BIAS : 0.0f);
bias_on = b, vk_sampler_dlss_bias(b ? (test_on ? test : rule) : 0.0f);
}
}

View File

@@ -187,6 +187,7 @@ static uint32_t submit_epoch = 1; // bumped per queue submit: draw bindings ch
// was over its budget.
#define RATE_N 8
#define RATE_SAY 16
#define RATE_SAY_UNDER 240u // the log's line: under 56 presents a second (a limit of 60 measures 59 now and then)
static uint32_t tex_rate = TRL_RATE_60;
#define PRESENTS(n) trl_presents((uint64_t)(n), tex_rate)
@@ -225,10 +226,11 @@ static void rate_tick(void) { // each submission, tex_lock held
ring[at++ % RATE_N] = trl_rate_sample(f - f0, t - t0, low);
t0 = t, f0 = f;
uint32_t m = trl_rate(ring, RATE_N);
// one line at each crossing of 60, RATE_SAY times a run: the log then shows which waits the drops went by
if ((m < TRL_RATE_60) != (tex_rate < TRL_RATE_60) && said < RATE_SAY) {
said++;
if (m < TRL_RATE_60)
// one line at each crossing of RATE_SAY_UNDER, RATE_SAY times a run: the log then shows which waits the drops went by
static int slow;
if ((m < RATE_SAY_UNDER) != slow && said < RATE_SAY) {
said++, slow = m < RATE_SAY_UNDER;
if (slow)
rt_log("vk: the game presents %u frames a second, fewer than 60, for %d seconds: the textures' waits count "
"their seconds in these (2 s are %llu presents, not 120; NX2NAVI_TEX_RATE_LOW=0: 120)",
m * 60 >> 8, RATE_N, (unsigned long long)trl_presents(120, m));