Render DLSS's quality modes at their own scale by fixing the game's dynamic resolution.

The game sizes its scene each frame from a GPU-time controller. The new patch set res_fixed takes the controller's
fixed path and reads the scale from the patch page, so DLSS Quality, Balanced, Performance and Ultra Performance
render at 67, 58, 50 and 33 percent of the picture and DLAA at all of it. The set goes on once the controller has run
for a few frames, and the menu's Upscaling choice lists the modes and switches them live.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
chrg6
2026-11-07 10:56:45 -05:00
parent 1299b20c1c
commit edce57ce5d
7 changed files with 84 additions and 23 deletions

View File

@@ -15,6 +15,7 @@ set wide_scene NX2NAVI_PATCH_WIDE_SCENE 0
set wide_aspect NX2NAVI_PATCH_WIDE_ASPECT 0
set wide_hud NX2NAVI_PATCH_WIDE_HUD 0
set wide_movie NX2NAVI_PATCH_WIDE_MOVIE 0
set res_fixed NX2NAVI_PATCH_RES_FIXED 0
set ostep NX2NAVI_OCARINA_REALTIME 1
# Real-time step: the framework's per-frame update (main+0x6e6be0) gives the time manager **[main+0x4c7e890] its
@@ -39,6 +40,14 @@ site wide_out main 31f39f8 f2c0b408 f9403108 # movk x8, #0x5a0, lsl #32 -> ldr
site wide_scene main 31f3a00 d280f008 @page:x8 # mov x8, #0x780 -> adrp x8, page
site wide_scene main 31f3a04 f2c08708 f9403508 # movk x8, #0x438, lsl #32 -> ldr x8, [x8, #104]
# Render scale: the game sizes its 3D scene by a dynamic-resolution controller that follows its GPU-time reports (3/4
# of the picture here). Bit 0 of an engine config word at +0x94 picks it (main+0x805bd0); the other path takes a fixed
# scale of 1.0 (main+0x806420). res_fixed always takes the fixed path and reads its scale from +240 (a float, written by
# runtime/widescreen.c from the DLSS mode). It is switched on only once the controller has run for a while, as its
# fields would otherwise stay unset and the scene stay black.
site res_fixed main 805bd0 360041e8 1400020f # tbz w8, #0, #0x80640c -> b #0x80640c
site res_fixed main 806420 52a7f009 @page:x9 b940f129 # mov w9, #0x3f800000 -> adrp x9, page; ldr w9, [x9, #240]
# Camera aspect ratio: the camera update (main+0x4c48c0, the same code as the camera update site in
# recomp/patches-v143.txt) copies the camera's near, far, vertical angle and ratio (camera +0x134..+0x140) into the
# sead perspective projection the renderer reads (+0x98..+0xb0; the projection's own matrix update, main+0x3485cc0, does

View File

@@ -48,7 +48,7 @@ static const char *const known[] = {
"MENU_CURSOR", "MENU_CURSOR_LOG", "MENU_TEST_DIR", "MENU_TEST_SET", "MINIMAL_LAYOUT", "MIRROR_GAP", "MODS_DIR", "MODS_DISABLED", "MODS_ORDER", "MODS_REBUILD", "MOTION", "MOUSE_MODE", "MOUSE_DIRECT_PATCH", "MOUSE_SENS", "MOVIE_HOST", "MOVIE_PACE",
"MVOBJ_ALL", "MVOBJ_BANKS", "MVOBJ_EQUAL", "MVOBJ_EXACT", "MVOBJ_EXEC", "MVOBJ_NOC4", "MVOBJ_ONLY", "MVOBJ_TIMES", "MVPREV_CHECK", "MVPREV_GROW", "MVPREV_SKIP", "MVPREV_TRUST", "MV_CHECK",
"MV_SURVEY",
"ONE_PER_CORE", "OPERATION_MODE", "OUTPUT_RES", "PAD_LAYOUT", "PACE_HZ", "PASS_CRUMBS", "PATCH_OUTPUT", "PATCH_SCENE", "PATCH_WIDE_ASPECT", "PATCH_WIDE_HUD", "PATCH_WIDE_MOVIE", "PATCH_WIDE_OUT", "PATCH_WIDE_SCENE", "PCSS", "PCSS_K", "PERF_OVERLAY", "PERF_SLOW_MS", "PIPELINE_CACHE", "PIPE_MEMO", "PIPE_PREBUILD", "PIPE_SKIP", "PIPE_SYNC", "PIPE_WAIT",
"ONE_PER_CORE", "OPERATION_MODE", "OUTPUT_RES", "PAD_LAYOUT", "PACE_HZ", "PASS_CRUMBS", "PATCH_OUTPUT", "PATCH_RES_FIXED", "PATCH_SCENE", "PATCH_WIDE_ASPECT", "PATCH_WIDE_HUD", "PATCH_WIDE_MOVIE", "PATCH_WIDE_OUT", "PATCH_WIDE_SCENE", "PCSS", "PCSS_K", "PERF_OVERLAY", "PERF_SLOW_MS", "PIPELINE_CACHE", "PIPE_MEMO", "PIPE_PREBUILD", "PIPE_SKIP", "PIPE_SYNC", "PIPE_WAIT",
"PREWARM_WAIT_MS", "PREWARM_XLATE", "PROBE_MOVIE", "PROBE_MOVIE_SHOT", "PROFILE", "PROFILE_CHAINS", "PROFILE_DIR", "PROFILE_FRAMES",
"PROFILE_MAIN", "PROFILE_RENDER", "PROMPTS", "PUSH_SKIP", "PUSH_STAGE", "RDOC", "RDOC_DIR", "RDOC_DLL", "REALTIME", "REC_END", "REEXEC",
"RENDER_SCALE", "REPLAY_ASYNC",

View File

@@ -265,7 +265,8 @@ uint64_t res_heap_extra_kib(void); // added to the HeapD
int res_heapdef_build(const void *in, size_t n, void **out, size_t *out_n); // the HeapDef table (.byml.zs) with
// the grown heaps, malloc'd; 0 = cannot (logged)
void res_heapdef_served(void); // romfs.c serves the grown table
void wide_init(void); // after rt_patches_init: widescreen.c, NX2NAVI_GAME_ASPECT
void wide_init(void);
void wide_present(void); // each present: DLSS's render scale (res_fixed) // after rt_patches_init: widescreen.c, NX2NAVI_GAME_ASPECT
int wide_available(void); // the generated C has the widescreen sets
void res_init(void); // after rt_patches_init: the patch page and the sets
float res_dlss_lod_bias(void); // NX2NAVI_DLSS upscaling: texture LOD bias (<= 0)

View File

@@ -834,6 +834,7 @@ NVN_API(nvnQueuePresentTexture) {
int idx = (int)arg_x(c, 2);
movie_tick(c); // ov_movie.c: hand over decoded movie frames that are due; needs the core, so before the wait
res_present(c); // resolution.c: a DLSS mode change sets the game's render size again, between frames
wide_present(); // widescreen.c: Ocarina's DLSS render scale
NVN_BLOCKING();
if (o->magic != NVN_WIN_MAGIC || idx < 0 || idx >= o->ntex || idx >= 4 || !o->tex[idx])
rt_fatal("nvn: QueuePresentTexture of image %d on window %p", idx, (void *)o);

View File

@@ -724,19 +724,31 @@ static const char *present_note(void) {
// ---- the tabs
// One upscaling choice over two settings: NX2NAVI_UPSCALER (how the picture is fitted to the window) and NX2NAVI_DLAA
// (DLSS on). DLSS runs at the game's own render scale (widescreen.c: the scene stays 3/4 of the picture, so DLSS's
// quality modes would all render the same) and fits the picture with bilinear; its model is NVIDIA's own for the
// scale (vk_dlaa.c evaluate, dlss_preset at its default).
// One upscaling choice over three settings: NX2NAVI_UPSCALER (how the picture is fitted to the window), NX2NAVI_DLAA
// (DLSS on) and NX2NAVI_DLSS (its mode, the render scale: widescreen.c wide_present). A DLSS mode fits the picture with
// bilinear, DLSS having made it; the model is NVIDIA's own for the mode (vk_dlaa.c evaluate, dlss_preset at its default).
static void row_upscaling(void) {
const char *names[3] = {menu_text(UI_MENU_UPSCALER_BILINEAR), menu_text(UI_MENU_UPSCALER_FSR), "NVIDIA DLSS"};
int cur = cfg_bool("NX2NAVI_DLAA") ? 2 : strcmp(cfg_value("NX2NAVI_UPSCALER"), "fsr") ? 0 : 1;
int nv = opt_choice_index("NX2NAVI_UPSCALER", UI_MENU_UPSCALING, names, 3, cur, 1, UI_MENU_UPSCALING_DESC,
cur == 2 ? menu_text(UI_MENU_UPSCALING_DLSS_NOTE) : nullptr);
static const char *const modes[] = {"dlaa", "quality", "balanced", "performance", "ultra_performance"};
static const UiTextId mode_names[] = {UI_MENU_DLSS_DLAA, UI_MENU_DLSS_QUALITY, UI_MENU_DLSS_BALANCED,
UI_MENU_DLSS_PERFORMANCE, UI_MENU_DLSS_ULTRA};
static char dl[5][96];
const char *names[7] = {menu_text(UI_MENU_UPSCALER_BILINEAR), menu_text(UI_MENU_UPSCALER_FSR)};
for (int i = 0; i < 5; i++) {
snprintf(dl[i], sizeof dl[i], i ? "DLSS %s" : "NVIDIA %s", menu_text(mode_names[i]));
names[2 + i] = dl[i];
}
int cur = strcmp(cfg_value("NX2NAVI_UPSCALER"), "fsr") ? 0 : 1;
if (cfg_bool("NX2NAVI_DLAA")) {
cur = 2;
for (int i = 0; i < 5; i++)
if (!strcmp(cfg_value("NX2NAVI_DLSS"), modes[i])) cur = 2 + i;
}
int nv = opt_choice_index("NX2NAVI_UPSCALER", UI_MENU_UPSCALING, names, 7, cur, 1, UI_MENU_UPSCALING_DESC,
cur >= 2 ? menu_text(UI_MENU_UPSCALING_DLSS_NOTE) : nullptr);
if (nv < 0) return;
opt_apply("NX2NAVI_UPSCALER", nv == 1 ? "fsr" : "bilinear");
opt_apply("NX2NAVI_DLAA", nv == 2 ? "1" : "0");
if (nv == 2) opt_apply("NX2NAVI_DLSS", "dlaa");
opt_apply("NX2NAVI_DLAA", nv >= 2 ? "1" : "0");
if (nv >= 2) opt_apply("NX2NAVI_DLSS", modes[nv - 2]);
}
// The picture's height (widescreen.c picture_height): the game's 1440 or more.

View File

@@ -505,11 +505,12 @@ static const char *const text_en[UI_TEXT_COUNT] = {
[UI_MENU_UPSCALING] = "Upscaling",
[UI_MENU_UPSCALING_DESC] = "How the picture is made smooth and fitted to the window. Bilinear: plain scaling. AMD FSR 1: "
"sharper edges and detail when the window is larger than the picture (strength below). "
"NVIDIA DLSS (RTX GPUs; " DLSS_FILE " next to the game's .exe): anti-aliasing of the 3D "
"scene at the game's own render scale, with NVIDIA's newest model for it; the HUD is not "
"touched. DLSS still works on each frame on its own for now, without motion history. "
"Turning DLSS on or off takes effect after a restart.",
[UI_MENU_UPSCALING_DLSS_NOTE] = "DLSS: after restart",
"NVIDIA DLSS (RTX GPUs; " DLSS_FILE " next to the game's .exe) renders the 3D scene at "
"its mode's share of the picture and scales it up with NVIDIA's newest model for the mode: "
"DLAA 100 %, Quality 67 %, Balanced 58 %, Performance 50 %, Ultra Performance 33 %. The "
"HUD is not touched. Turning DLSS on or off takes effect after a restart; the mode changes "
"at once.",
[UI_MENU_UPSCALING_DLSS_NOTE] = "DLSS on or off: after restart",
[UI_MENU_GAME_RES] = "Resolution",
[UI_MENU_GAME_RES_DESC] = "The height of the game's picture: 1440p is the game's own; higher draws a sharper picture "
"and its scene in proportion, at a matching cost on the graphics card. Native: the "

View File

@@ -68,15 +68,52 @@ static unsigned picture_height(void) {
// ---- DLSS's render scale (recomp/patches-v111.txt, set res_fixed)
// While DLSS is on, the scene is the DLSS mode's share of the picture (NVIDIA's ratios: DLAA all of it, Quality 2/3,
// Balanced 0.58, Performance 1/2, Ultra Performance 1/3) instead of what the game's dynamic-resolution controller
// picks. The share is written to +240 at every change of the setting (the menu switches the mode live); the set goes on
// RES_FIXED_AFTER presents after the first, once the controller has filled the fields the fixed path keeps.
#define RES_FIXED_AFTER 120
static double dlss_share(void) {
if (!cfg_bool("NX2NAVI_DLAA")) return 0;
const char *m = cfg_value("NX2NAVI_DLSS");
return !strcmp(m, "quality") ? 2.0 / 3 : !strcmp(m, "balanced") ? 0.58 : !strcmp(m, "performance") ? 0.5
: !strcmp(m, "ultra_performance") ? 1.0 / 3 : 1.0;
}
void wide_present(void) {
static int set = -2;
static uint64_t presents;
static unsigned seen = ~0u;
if (set == -2) set = rt_patch_set("res_fixed");
if (set < 0) return;
unsigned v = cfg_version();
if (v != seen) {
seen = v;
float s = (float)dlss_share();
if (s > 0) memcpy((uint8_t *)rt_patch_page() + 240, &s, 4);
if (s <= 0 && rt_patch_enabled(set)) {
rt_patch_switch(set, 0);
rt_log("widescreen: DLSS off: the game's own render scale again");
} else if (s > 0 && rt_patch_enabled(set)) {
rt_log("widescreen: DLSS render scale %.0f%% of the picture", s * 100);
}
}
if (++presents == RES_FIXED_AFTER && dlss_share() > 0) {
rt_patch_switch(set, 1);
rt_log("widescreen: DLSS render scale %.0f%% of the picture (the game's dynamic resolution off)", dlss_share() * 100);
} else if (presents > RES_FIXED_AFTER && dlss_share() > 0 && !rt_patch_enabled(set)) {
rt_patch_switch(set, 1); // DLSS switched on later in the run
}
}
void wide_init(void) {
int so = rt_patch_set("wide_out"), ss = rt_patch_set("wide_scene"), sa = rt_patch_set("wide_aspect"),
sh = rt_patch_set("wide_hud"), sm = rt_patch_set("wide_movie");
// The scene keeps the game's 3/4 of the picture, a share its renderer applies itself: writing another size into
// wide_scene's constant (main+0x31f3a00) or into the 1920x1080 / 1280x720 render sizes at main+0x108b688 left the
// light target at 3/4. Found so far (for DLSS's quality modes, which need a smaller scene): the scene targets come
// from the view objects' render-target descriptors ({u16 w, u16 h, ...} at +0x3ab0, made by main+0x2507b0 through
// main+0x3ed00), which main+0x6590d0 copies in from a float rectangle at +0x3a10 of another object (width
// [+0x3a18] - [+0x3a10]); where that rectangle's 2562 comes from is still open.
// The scene's share of the picture is not a size in this set: the game's GPU-time dynamic resolution sets it each
// frame (3/4 of the picture at most), and writing other sizes into wide_scene's constant or the 1920x1080 render
// sizes left the light target where the controller put it. DLSS's modes fix the share through res_fixed instead
// (wide_present).
double r = wanted_ratio(), game = (double)GAME_W / GAME_H, f = 0.75;
unsigned oh = picture_height();
if (r <= 0 || r < game * 1.01) {