Skip to content

NVIDIA DLSS in place of the game's upscale (optional, RTX GPUs) - #850

Closed
BlueHeisenberg wants to merge 2 commits into
NotRexed:mainfrom
BlueHeisenberg:dlss-only
Closed

BlueHeisenberg wants to merge 2 commits into
NotRexed:mainfrom
BlueHeisenberg:dlss-only

Conversation

@BlueHeisenberg

Copy link
Copy Markdown
Contributor

NVIDIA DLSS

Builds on #849 (Output resolution); until that is merged, this PR also shows its commit.

Optional, for RTX GPUs. DLSS takes over the game's gameplay upscale. The 3D is drawn at the DLSS mode's size, and DLSS scales it up to the game's output using the game's depth and its camera's motion. Movies, menus and the HUD are unchanged. This is ported from zortos293/Lichii (its Streamline loader, camera motion and their shaders), which credits Duck912's Eglantine work.

Menu rows (Graphics tab)

Row Choices
DLSS Off, On (restart); says "no NVIDIA DLSS found" when the DLLs are missing
DLSS mode Ultra Performance, Performance, Balanced, Quality, DLAA, each shown with its size for the chosen output
DLSS preset J, K, L, M (changed at once)

While DLSS is on, the DLSS mode sets the Resolution. The DLSS mode's label and help text follow the Output resolution row as soon as it changes.

NVIDIA files: not included

third_party/streamline/ holds NVIDIA Streamline v2.14.16's headers only (MIT, license.txt; noted in gpu/PROVENANCE.md). No NVIDIA binaries are in the repo. At run time sl.interposer.dll, sl.common.dll and sl.dlss.dll (from the Streamline SDK) and nvngx_dlss.dll are loaded from a streamline folder beside the game exe. With the launcher layout, that's beside bin\hookshot.exe. If nvngx_dlss.dll is missing, the NVIDIA app's copy is used, but only if it is signed by NVIDIA. The interposer and the model must carry a valid NVIDIA Corporation signature (checked with WinVerifyTrust) or they are not loaded. Without these files, DLSS stays off and the game runs as before.

Platforms

Windows only. On Linux the DLSS code builds as a stub and the rows say DLSS is unavailable.

Overrides: RX_DLSS=0/1, RX_DLSS_PRESET=J..M, RX_DLSS_DIR=<folder>; tests: RX_DLSS_DEBUG=<n>.

Tested

On an RTX 5090 (Windows 11): the same 140-second gameplay route at every setting below. No crash and no device lost in any run.

With DLSS (3D size -> upscaled to the output)

Output DLSS Quality DLAA
2048x1152 1366x768 -> 2048x1152 2048x1152 -> 2048x1152
2560x1440 1707x960 -> 2560x1440 2560x1440 -> 2560x1440
3840x2160 2560x1440 -> 3840x2160 3840x2160 -> 3840x2160

Also tested at the 4K output: Ultra Performance (1280x720 -> 3840x2160, 4 runs) and DLAA with frame generation 2x. DLSS itself costs about 1.7 ms of GPU time per frame at 4K.

hookshot_vk.dll builds with the llvm-mingw version CI uses (20261104), and the DLSS files pass a Linux syntax check.

…ze 3D at 3840x2160, and the Resolution row reaching the 3D

- Output resolution (host/nn_applet.c, nx2/oot_patches.py apply_output_size): the game's whole frame at 2048x1152 (its own),
  2560x1440 or 3840x2160, docked, after a restart (HOS_OUTPUT_RES over it). The display size, the docked size list, the 3D's
  largest size and the scene's size follow it, and the graphics scale reads x1 for a chosen output; the widescreen picture is
  made at its height (host/hos_main.c).
- 3840x2160 3D at the 4K output: the game's render targets' GPU pool (305 MB, for 2560x1440) ran out and the next target came
  back null. It grows with the output's pixels (687 MB at 4K; HOS_RT_POOL_MB over it), with the "Graphics" module heap that
  holds it, that heap's budget check counting its shipped use, and the leftover heap made from it kept at its shipped size.
  At 2048x1152 and 2560x1440 nothing changes.
- Resolution: a fixed scale reaches the 3D (it kept the dynamic path's last one), sent in 1/10000 as the twelfth of the output
  the game draws at; the row shows the 3D's size for the chosen output, and follows the Output resolution row at once.
- Test switches: HOS_RES_LOG (the 3D's scale in the log), RX_VK_SHOT_W/H (the screenshots' size), RX_VK_MENU_SCRIPT (timed
  menu keys) and RX_VK_MENU_DUMP (each drawing of the menu as a BMP).
…A's DLLs not included)

- gpu/vk/vk_dlss.cpp, the DLSS part of vk_draw.cpp, vk/shaders/dlss_*.comp: the gameplay upscale network's place in the frame
  taken by DLSS, which draws the 3D at the DLSS mode's size and scales it to the output with the game's depth and its camera's
  motion. Ported from zortos293/Lichii (its Streamline loader, camera motion and their shaders; Lichii credits Duck912's
  Eglantine work).
- NVIDIA Streamline v2.14.16's headers in third_party/streamline (MIT, gpu/PROVENANCE.md). sl.interposer.dll, sl.common.dll,
  sl.dlss.dll and nvngx_dlss.dll are loaded from a streamline folder beside the game exe, or the NVIDIA app's DLSS; the
  interposer and the model must carry a valid NVIDIA Corporation signature (WinVerifyTrust). Windows only: elsewhere DLSS is
  unavailable.
- Menu rows DLSS (after a restart), DLSS mode (Ultra Performance .. DLAA, each with its size for the chosen output; it sets the
  3D's scale in place of Resolution) and DLSS preset (J..M, at once); RX_DLSS, RX_DLSS_PRESET, RX_DLSS_DIR over them,
  RX_DLSS_DEBUG for its inputs. The scale sent to the game is for the output it runs at (the window crop).
- readme: the rows, where NVIDIA's files come from; the Lichii credit.
@DarkzGG

DarkzGG commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

Tested the same approach on a smaller card, in case it helps: RTX 4050 Laptop (6 GB VRAM), Windows 11, Hookshot v2.0.5, Streamline 2.14.16, DLSS preset K, Resolution 50–67 %.

My build is a separate prototype based on the same Lichii path (camera reprojection from cbuf 3, no jitter, depth *2-1), and it borrows dlss_color.comp and dlss_motion.comp from #818 (now this PR). DLSS works well on this card: 1152x648 -> 1536x864, clearly sharper than the game's network, no ghosting once the motion pass bounds its values.

One thing that may matter for 6 GB cards: VRAM pressure makes Streamline refuse DLSS.

With the default budget the game went past what the card allows. The log showed vk: VRAM: 5896 MB used of 5080 MB allowed and VRAM full: a 3072x3072 image ... in system memory. Streamline then logged Exceeded VRAM budget on every evaluate, and slEvaluateFeature sometimes failed, so the game's network took over for a few frames.

In the Kokiri village, GPU time was ~37 ms/frame (≈22 fps).

Capping the texture budget with RX_VK_VRAM_MB=3600 (the Low (6GB) preset's value) fixed it:

  • Usage dropped to ~4.85 GB of 5.05 GB allowed.
  • The Streamline warnings stopped.
  • DLSS stayed on.
  • GPU time fell to ~13.5 ms in a similar scene.

One catch: changing Resolution after picking the Low preset sets the preset back to Custom, which drops the VRAM cap. So a user who picks Low and then tunes Resolution loses it without noticing. With DLSS on, it might be worth keeping a VRAM cap on 6 GB cards, or noting it in the readme.

Reflex: I also added NVIDIA Reflex through the same Streamline instance:

  • kFeatureReflex + kFeaturePCL are loaded at slInit.
  • One frame token runs per present: slReflexSleep right after the present returns, then the PCL markers at the frame's first submit and around the present.
  • DLSS evaluates with the same token.
  • It needs sl.reflex.dll, sl.pcl.dll and NvLowLatencyVk.dll beside the other DLLs.

It runs fine together with frame generation 2x and feels noticeably more responsive. If you're interested, I can port it on top of this PR as a follow-up PR.

madebyjordan added a commit to madebyjordan/Longshot that referenced this pull request Nov 8, 2026
…on top of NotRexed#850's DLSS frame code)

A second backend for NotRexed#850's replacement of OoT's gameplay upscale, ported
from the FSR 4.1 work on Lichii's OoT branch (madebyjordan/Lichii-Hyrule
oot-dev, 7f8ae5b337..c5f2fce17d). It takes the same colour, depth and
camera motion as DLSS; DLSS keeps priority when it is on.

- vk_fsr4.cpp: AMD's FSR API (FidelityFX SDK 2.4). Only the MIT headers
  are vendored (third_party/fidelityfx); AMD's signed loader and upscaler
  DLLs load at run time from RX_FSR4_DIR or beside hookshot.exe. The
  newest 4.x provider the loader lists is picked explicitly (RDNA 2 and 3
  run its INT8 model). LDR, non-linear colour, standard depth. One
  context per output size, render sizes up to it, so the Resolution row
  and the game's dynamic resolution need no new context.
- Jitter: the game draws without one, so the draws that test or write
  the scene's depth move by a Halton (2, 3) sub-pixel offset through the
  viewport, and FSR is told it. The offset is part of the state dyn_pack
  compares, or a frame whose draws match the last frame's would keep the
  old one. From Lichii: the phase moves on at the network's pass (where
  the game's frame ends), not at presents; it is on only while FSR drew
  the last network pass.
- AMD edge line (Lichii, RX 6700 XT): moved up or left, the AMD driver
  draws nothing into the scene's last column or row, a stale near-white
  value that became a flickering line in FSR's history. FSR's colour and
  the motion's depth repeat the column and row before them. Colour going
  into FSR is cleaned of NaN and Inf and clamped to [0, 1]; the output
  pass drops NaN and Inf for both upscalers.
- FSR history resets when FSR did not draw the last network pass or on
  a camera cut, not by present count.
- Menu: "AMD FSR 4.1" (Off/On, at once; ini key fsr4, RX_FSR4=0/1).
  Resolution sets what it upscales from (75% Quality, 50% Performance,
  100% anti-aliasing only). RX_FSR4_JITTER=0 turns the jitter off.
- What changes on native, compared with Lichii: no GPU-time divisor
  (Hookshot already scales the game's timer, so it keeps full quality),
  no hunt for the tonemap draw or depth reader (NotRexed#818 takes them where
  the draws are), no 4/3-only size check.
- tests/test_fsr_jitter.py: the jitter is compared and applied, moves on
  at the network's pass, and the edge repeat is in both passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
madebyjordan added a commit to madebyjordan/Longshot that referenced this pull request Nov 8, 2026
… images given back

- The FSR context is made with FFX_UPSCALE_ENABLE_DYNAMIC_RESOLUTION: the
  render size changes under it with the Resolution row and the game's
  dynamic resolution.
- vk_fsr4.cpp is Windows only (AMD's signed runtime is), with stubs
  elsewhere: the Linux build has no LoadLibraryExA.
- AMD's dispatches count as work (note_work, vk_work.h), so full_barrier
  does not leave out the barrier after them as a repeat.
- dlss_image gives a replaced image back 120 presents later, as
  typed_upscale does with its histories, instead of keeping it: DLSS
  fixes the render size, but FSR can run with Resolution Auto.
- The dlss_*.comp headers regenerated at SPIR-V 1.6, as NotRexed#850 makes them.
- readme: FSR 4 claims limited to what was tested (RX 6700 XT, INT8).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@NotRexed

NotRexed commented Nov 9, 2026

Copy link
Copy Markdown
Owner

Closing to keep the queue clear — please rebase on current main and reopen; thanks!

@NotRexed NotRexed closed this Nov 9, 2026
madebyjordan added a commit to madebyjordan/Longshot that referenced this pull request Nov 9, 2026
…on top of NotRexed#850's DLSS frame code)

A second backend for NotRexed#850's replacement of OoT's gameplay upscale, ported
from the FSR 4.1 work on Lichii's OoT branch (madebyjordan/Lichii-Hyrule
oot-dev, 7f8ae5b337..c5f2fce17d). It takes the same colour, depth and
camera motion as DLSS; DLSS keeps priority when it is on.

- vk_fsr4.cpp: AMD's FSR API (FidelityFX SDK 2.4). Only the MIT headers
  are vendored (third_party/fidelityfx); AMD's signed loader and upscaler
  DLLs load at run time from RX_FSR4_DIR or beside hookshot.exe. The
  newest 4.x provider the loader lists is picked explicitly (RDNA 2 and 3
  run its INT8 model). LDR, non-linear colour, standard depth. One
  context per output size, render sizes up to it, so the Resolution row
  and the game's dynamic resolution need no new context.
- Jitter: the game draws without one, so the draws that test or write
  the scene's depth move by a Halton (2, 3) sub-pixel offset through the
  viewport, and FSR is told it. The offset is part of the state dyn_pack
  compares, or a frame whose draws match the last frame's would keep the
  old one. From Lichii: the phase moves on at the network's pass (where
  the game's frame ends), not at presents; it is on only while FSR drew
  the last network pass.
- AMD edge line (Lichii, RX 6700 XT): moved up or left, the AMD driver
  draws nothing into the scene's last column or row, a stale near-white
  value that became a flickering line in FSR's history. FSR's colour and
  the motion's depth repeat the column and row before them. Colour going
  into FSR is cleaned of NaN and Inf and clamped to [0, 1]; the output
  pass drops NaN and Inf for both upscalers.
- FSR history resets when FSR did not draw the last network pass or on
  a camera cut, not by present count.
- Menu: "AMD FSR 4.1" (Off/On, at once; ini key fsr4, RX_FSR4=0/1).
  Resolution sets what it upscales from (75% Quality, 50% Performance,
  100% anti-aliasing only). RX_FSR4_JITTER=0 turns the jitter off.
- What changes on native, compared with Lichii: no GPU-time divisor
  (Hookshot already scales the game's timer, so it keeps full quality),
  no hunt for the tonemap draw or depth reader (NotRexed#818 takes them where
  the draws are), no 4/3-only size check.
- tests/test_fsr_jitter.py: the jitter is compared and applied, moves on
  at the network's pass, and the edge repeat is in both passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
madebyjordan added a commit to madebyjordan/Longshot that referenced this pull request Nov 9, 2026
… images given back

- The FSR context is made with FFX_UPSCALE_ENABLE_DYNAMIC_RESOLUTION: the
  render size changes under it with the Resolution row and the game's
  dynamic resolution.
- vk_fsr4.cpp is Windows only (AMD's signed runtime is), with stubs
  elsewhere: the Linux build has no LoadLibraryExA.
- AMD's dispatches count as work (note_work, vk_work.h), so full_barrier
  does not leave out the barrier after them as a repeat.
- dlss_image gives a replaced image back 120 presents later, as
  typed_upscale does with its histories, instead of keeping it: DLSS
  fixes the render size, but FSR can run with Resolution Auto.
- The dlss_*.comp headers regenerated at SPIR-V 1.6, as NotRexed#850 makes them.
- readme: FSR 4 claims limited to what was tested (RX 6700 XT, INT8).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants