feat(vk): DLSS 5 neural rendering through NGX, in the temporal chain before the HUD #1833

Open
FIocker wants to merge 1 commit from FIocker/super-build/neural into main
FIocker commented 2026-11-10 17:46:32 +00:00 (Migrated from github.com)

This adds NVIDIA's DLSS 5 neural rendering model, through NGX, running in the temporal chain on the upscaler's picture before the HUD. Over the finished frame it left a dark smudge behind the hearts and text. Off by default.

Rebuilt on main (51da0464) as reviewers suggested: NGX direct plus the in-chain placement, on #2118's NGX session. It builds on its own now. Streamline and the vendored OpenDLSS-NR are gone from this PR; they could come later as follow-ups if wanted. The menu rows and hooks it needs, which #1834 used to carry, are folded in.

What it does

  • The Neural tab: on/off, Stage, Model, Style, Preset, Intensity, Local tone, Local structure, Global tone, Skin structure and Auto mask. Every row reaches the next frame (tests/test_nr_settings_live.py). They're saved as neural_rendering and nr_<name>. The rows sit before Bug report, so Restart and Quit stay the table's last two.
  • Where it runs (Stage):
    • After upscaler (the default): in vk_temporal.cpp, on the engine's picture before the game draws its HUD. That's DLSS, DLAA, FSR 4 or FSR 3.1 on the chain.
    • Before upscaler: on the render-size scene, which the engine then takes. Here the model gets the chain's own depth (with depth_reversed for FSR 3.1's 1 − d), motion, jitter and cut resets (NrFrame). It's much cheaper: on an RTX card at 4168×1976, DLSS Quality, 47 fps against 37 after.
    • Without a temporal upscaler: at the present, before anything of ours is drawn on top (headless the same way). The present's pass stays out while the chain runs it (dlss_nr_before_hud).
  • Native HDR: left out when HDR is set to Native, because the game's light past 1 would be cut by the model's 8 bits. The row says why.

On #2118's NGX session

vk_dlss.cpp exports four small things; there's no second NVSDK_NGX_VULKAN_Init:

  • dlss_ngx_ready(): its ngx_init(), once.
  • dlss_ngx_runtime(name, …): looks the runtime up in runtime_dirs(), the same folders #2118 gives NGX. nvngx_dlssnr.dll goes where nvngx_dlss.dll and nvngx_dlssg.dll do: the game folder's ngx, RX_VK_DLSS_DIR, or %LOCALAPPDATA%\Hookshot\ngx. The log and the row name where it looked.
  • dlss_ngx_feature_mutex(): DLSS Frame Gen's own lock. Neural rendering holds it around its create, evaluate and release, so with DLSS-G on, the two features' NGX calls never overlap. The order matches DLSS-G's inputs: the chain's lock, then this one. The device idle before a release is taken outside it, under the queue's lock, as fg_release does.
  • dlss_nr_device_extensions(): DLSS-G's own discovery code, made a helper that takes the feature. It's asked only on a device DLSS may run on, so AMD and Intel device creation is unchanged.

Fixed from the earlier reviews

  • Threads: nr::apply's two callers (the chain's, under its lock; present_image's) take one at a time.
  • Memory: turning the row off lets go of the feature, the parameter block and the four images.
  • Recorder (#1473): vk_work.h comes last, with B_DIRECT before NGX records.
  • Real inputs before the upscaler (NrFrame) and resets at cuts.
  • No more: the non-NVIDIA device-creation change and the detached checker thread went with OpenDLSS-NR.

Known limits

  • The feature and its images are made for one size. A size change (Resolution Auto with Stage on Before upscaler) makes them again with a device idle, and six refusals turn it off for the run.
  • The picture is handed over as RGBA8.

For maintainers

  • #1590 also adds the feature-18 path, so only one of the two can land.
  • Runtime: NVIDIA's own nvngx_dlssnr.dll runs on RTX 50. The docs don't describe anything else, and Hookshot doesn't read or change NGX's settings or NVIDIA's files.

Testing

  • Builds: on this branch, vk_dlssnr_ngx.cpp and vk_dlss.cpp compile with -DHOOKSHOT_NGX against NVIDIA's NGX SDK headers and without it, with clang and nx2/build.py's renderer flags. So do vk_temporal.cpp, vk_replay.cpp, vk_main.cpp and vk_menu.cpp.
  • Tests: tests/run_all.py, the whole suite, locally (clang targeting MSVC, the CPM Vulkan headers, glslang 16.6): 434 passed. The failures are exactly the ones main at 51da0464 has on this machine, all down to the local toolchain. test_recorder_sync.py and test_nr_settings_live.py pass.
  • In game: the numbers above are from before the rebuild (Windows 11, NVIDIA RTX, OoT v131072). A run of this head with DLSS-G on and off is still to come, with screenshots of the Neural tab.

🤖 Generated with Claude Code

This adds NVIDIA's DLSS 5 neural rendering model, through NGX, running in the temporal chain on the upscaler's picture **before the HUD**. Over the finished frame it left a dark smudge behind the hearts and text. Off by default. **Rebuilt on main (51da0464) as reviewers suggested: NGX direct plus the in-chain placement, on #2118's NGX session.** It builds on its own now. Streamline and the vendored OpenDLSS-NR are gone from this PR; they could come later as follow-ups if wanted. The menu rows and hooks it needs, which #1834 used to carry, are folded in. ## What it does - **The Neural tab**: on/off, Stage, Model, Style, Preset, Intensity, Local tone, Local structure, Global tone, Skin structure and Auto mask. Every row reaches the next frame (`tests/test_nr_settings_live.py`). They're saved as `neural_rendering` and `nr_<name>`. The rows sit before Bug report, so Restart and Quit stay the table's last two. - **Where it runs** (Stage): - **After upscaler** (the default): in `vk_temporal.cpp`, on the engine's picture before the game draws its HUD. That's DLSS, DLAA, FSR 4 or FSR 3.1 on the chain. - **Before upscaler**: on the render-size scene, which the engine then takes. Here the model gets the chain's own depth (with `depth_reversed` for FSR 3.1's 1 − d), motion, jitter and cut resets (`NrFrame`). It's much cheaper: on an RTX card at 4168×1976, DLSS Quality, 47 fps against 37 after. - **Without a temporal upscaler**: at the present, before anything of ours is drawn on top (headless the same way). The present's pass stays out while the chain runs it (`dlss_nr_before_hud`). - **Native HDR**: left out when HDR is set to Native, because the game's light past 1 would be cut by the model's 8 bits. The row says why. ## On #2118's NGX session `vk_dlss.cpp` exports four small things; there's no second `NVSDK_NGX_VULKAN_Init`: - **`dlss_ngx_ready()`**: its `ngx_init()`, once. - **`dlss_ngx_runtime(name, …)`**: looks the runtime up in `runtime_dirs()`, the same folders #2118 gives NGX. `nvngx_dlssnr.dll` goes where `nvngx_dlss.dll` and `nvngx_dlssg.dll` do: the game folder's `ngx`, `RX_VK_DLSS_DIR`, or `%LOCALAPPDATA%\Hookshot\ngx`. The log and the row name where it looked. - **`dlss_ngx_feature_mutex()`**: DLSS Frame Gen's own lock. Neural rendering holds it around its create, evaluate and release, so with DLSS-G on, the two features' NGX calls never overlap. The order matches DLSS-G's inputs: the chain's lock, then this one. The device idle before a release is taken outside it, under the queue's lock, as `fg_release` does. - **`dlss_nr_device_extensions()`**: DLSS-G's own discovery code, made a helper that takes the feature. It's asked only on a device DLSS may run on, so AMD and Intel device creation is unchanged. ## Fixed from the earlier reviews - **Threads**: `nr::apply`'s two callers (the chain's, under its lock; `present_image`'s) take one at a time. - **Memory**: turning the row off lets go of the feature, the parameter block and the four images. - **Recorder** (#1473): `vk_work.h` comes last, with `B_DIRECT` before NGX records. - **Real inputs before the upscaler** (`NrFrame`) and resets at cuts. - **No more**: the non-NVIDIA device-creation change and the detached checker thread went with OpenDLSS-NR. ## Known limits - The feature and its images are made for one size. A size change (Resolution Auto with Stage on Before upscaler) makes them again with a device idle, and six refusals turn it off for the run. - The picture is handed over as RGBA8. ## For maintainers - **#1590** also adds the feature-18 path, so only one of the two can land. - **Runtime**: NVIDIA's own `nvngx_dlssnr.dll` runs on RTX 50. The docs don't describe anything else, and Hookshot doesn't read or change NGX's settings or NVIDIA's files. ## Testing - **Builds**: on this branch, `vk_dlssnr_ngx.cpp` and `vk_dlss.cpp` compile with `-DHOOKSHOT_NGX` against NVIDIA's NGX SDK headers and without it, with clang and `nx2/build.py`'s renderer flags. So do `vk_temporal.cpp`, `vk_replay.cpp`, `vk_main.cpp` and `vk_menu.cpp`. - **Tests**: `tests/run_all.py`, the whole suite, locally (clang targeting MSVC, the CPM Vulkan headers, glslang 16.6): 434 passed. The failures are exactly the ones main at 51da0464 has on this machine, all down to the local toolchain. `test_recorder_sync.py` and `test_nr_settings_live.py` pass. - **In game**: the numbers above are from before the rebuild (Windows 11, NVIDIA RTX, OoT v131072). A run of this head with DLSS-G on and off is still to come, with screenshots of the Neural tab. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
hckrman101 (Migrated from github.com) reviewed 2026-11-10 18:06:19 +00:00
hckrman101 (Migrated from github.com) left a comment

Thanks, this is a lot of careful work, and putting the model inside the upscale chain (before the HUD) is the right fix for the HUD smudge. I read this PR along with the parts of #1831 and #1834 that wire it in, since this PR can't be judged on its own. Several things need sorting before it can land:

This PR can't merge on its own. nx2/build.py on main compiles every gpu/vk/*.cpp, so the four new .cpp files get built right away. vk_dlssnr_ngx.cpp then fails to compile with no NGX SDK: asked() and nr:: use nr_settings() and opts.nr_backend, which only #1834 adds. vk_opennr.cpp needs opennr_ptx.h (generated by #1834's build.py) and vk_camera_motion.h / scene_frame_inputs (from #1831). Please mark it as depending on those two, or fold the three into one PR. As things stand, merging them in any other order breaks every build.

Tests. main's tests/run_all.py retired the central ARGS/SKIP tables. Each test now declares RUN_ALL_ARGS / RUN_ALL_SKIP in its own file, and any entry added to the old tables stops the whole run ("ARGS, MODULES and SKIP are retired"). #1834 adds the opennr tests to those retired tables, and these test files declare nothing. So test_opennr_kernels/harness/khr.py run with no arguments and fail on their required=True args, and test_nr_settings_live.py / test_opennr_backend.py fail on main without #1834's vk_menu.h and vk_main.cpp. CI hasn't run on the branch ("no checks reported").

Two threads, one unlocked state. nr::apply is now called from temporal_upscale (the replay thread, with the chain's lock held) and also from present_image (the display thread). Only dlss_nr_before_hud()'s frame window keeps them apart. On the first chain frame after a gap (2 frames in the default stage, 600 before the upscaler), both threads can be inside dlssnr_ngx::apply / opennr::apply at once. Neither has a lock. They share one NVSDK_NGX_Parameter block (each points it at its own stack NVSDK_NGX_Resource_VKs) and one set of images. If the two sizes differ (render size vs window size in Before upscaler mode), each call frees and rebuilds the feature and images the other thread just recorded. vk_dlssnr.cpp's resize comment ("apply() runs on the display thread") no longer holds.

Compared with #1590:

  • Avoided: there is no DLSS SR history reset tied to NR. Refusals are held against the exact settings, so there's no once-a-second pulse and no per-frame reset with HDR Native. The Neural tab being shown in every build is justified here because the Open backend is in every build.
  • Still the same:
    • The NGX feature (and the Open graph) is rebuilt, with a full device idle, on every size change. With Resolution Auto in Before upscaler mode, every render-size change stalls, and six refusals turn NR off for the run.
    • Nothing frees NR's VRAM when it's turned off. No shutdown() of the three backends is called anywhere in the four PRs, so none is called even at exit. Separately, opennr's checker std::thread can be left joinable in a static at exit (inline comment).
    • The model gets jitter 0, no motion and a flat depth. In the new Before upscaler stage that's worse: it gets jittered render-size colour while the real s.depth, s.motion and cam.jitter are right there in temporal_upscale. If the model is temporal, it will average the jitter away before DLSS sees it ("slightly softer on fine foliage" fits that) and ghost on camera motion.
  • On the overall approach, this PR is the better base: the in-chain placement, the separation from SR history, and the explained refusals. The two can't both merge, since both add a DLSS 5 tab and the feature-18 path. I'd suggest maintainers pick this design and close #1590 once it lands. This PR would be much easier to review if trimmed to NGX direct plus the in-chain placement first, with Streamline and the vendored OpenDLSS-NR (~50k lines, ~2.8 MB of generated SPIR-V headers) as follow-ups.

Other notes:

  • #1473 (recorder thread) is still open, not merged, so B_DIRECT doesn't exist on main yet. If it lands first, every NGX create/evaluate here and the direct vkCmd* in these files need rec::boundary(B_DIRECT). Its test_recorder_sync.py would flag these files.
  • I checked env names and option values against #1008 and #1773. No collisions: the RX_VK_DLSS_NR* names are distinct, RX_VK_DLSS=0 deliberately turns off NGX NR too, and NR's quality values are separate from opts.dlss 3/4/5. This PR adds no numeric option ids (the Neural rows in #1834 are keyed by name).
  • Docs and maintainer call: the NGX path documents running a community-patched nvngx_dlssnr.dll with NGX's signature override turned on. The Open backend needs a model folder that can only come from NVIDIA's weights. Maintainers should decide whether they want to document either.
  • Nit: the Before upscaler path takes the 16F scene colour down to RGBA8 and back into the 16F colour input, an 8-bit hop before DLSS.
Thanks, this is a lot of careful work, and putting the model inside the upscale chain (before the HUD) is the right fix for the HUD smudge. I read this PR along with the parts of #1831 and #1834 that wire it in, since this PR can't be judged on its own. Several things need sorting before it can land: **This PR can't merge on its own.** `nx2/build.py` on main compiles every `gpu/vk/*.cpp`, so the four new `.cpp` files get built right away. `vk_dlssnr_ngx.cpp` then fails to compile with no NGX SDK: `asked()` and `nr::` use `nr_settings()` and `opts.nr_backend`, which only #1834 adds. `vk_opennr.cpp` needs `opennr_ptx.h` (generated by #1834's build.py) and `vk_camera_motion.h` / `scene_frame_inputs` (from #1831). Please mark it as depending on those two, or fold the three into one PR. As things stand, merging them in any other order breaks every build. **Tests.** main's `tests/run_all.py` retired the central ARGS/SKIP tables. Each test now declares `RUN_ALL_ARGS` / `RUN_ALL_SKIP` in its own file, and any entry added to the old tables stops the whole run ("ARGS, MODULES and SKIP are retired"). #1834 adds the opennr tests to those retired tables, and these test files declare nothing. So `test_opennr_kernels/harness/khr.py` run with no arguments and fail on their `required=True` args, and `test_nr_settings_live.py` / `test_opennr_backend.py` fail on main without #1834's `vk_menu.h` and `vk_main.cpp`. CI hasn't run on the branch ("no checks reported"). **Two threads, one unlocked state.** `nr::apply` is now called from `temporal_upscale` (the replay thread, with the chain's lock held) and also from `present_image` (the display thread). Only `dlss_nr_before_hud()`'s frame window keeps them apart. On the first chain frame after a gap (2 frames in the default stage, 600 before the upscaler), both threads can be inside `dlssnr_ngx::apply` / `opennr::apply` at once. Neither has a lock. They share one `NVSDK_NGX_Parameter` block (each points it at its own stack `NVSDK_NGX_Resource_VK`s) and one set of images. If the two sizes differ (render size vs window size in Before upscaler mode), each call frees and rebuilds the feature and images the other thread just recorded. `vk_dlssnr.cpp`'s resize comment ("apply() runs on the display thread") no longer holds. **Compared with #1590:** - Avoided: there is no DLSS SR history reset tied to NR. Refusals are held against the exact settings, so there's no once-a-second pulse and no per-frame reset with HDR Native. The Neural tab being shown in every build is justified here because the Open backend is in every build. - Still the same: - The NGX feature (and the Open graph) is rebuilt, with a full device idle, on every size change. With Resolution Auto in Before upscaler mode, every render-size change stalls, and six refusals turn NR off for the run. - Nothing frees NR's VRAM when it's turned off. No `shutdown()` of the three backends is called anywhere in the four PRs, so none is called even at exit. Separately, `opennr`'s `checker` std::thread can be left joinable in a static at exit (inline comment). - The model gets jitter 0, no motion and a flat depth. In the new Before upscaler stage that's worse: it gets jittered render-size colour while the real `s.depth`, `s.motion` and `cam.jitter` are right there in `temporal_upscale`. If the model is temporal, it will average the jitter away before DLSS sees it ("slightly softer on fine foliage" fits that) and ghost on camera motion. - On the overall approach, this PR is the better base: the in-chain placement, the separation from SR history, and the explained refusals. The two can't both merge, since both add a DLSS 5 tab and the feature-18 path. I'd suggest maintainers pick this design and close #1590 once it lands. This PR would be much easier to review if trimmed to NGX direct plus the in-chain placement first, with Streamline and the vendored OpenDLSS-NR (~50k lines, ~2.8 MB of generated SPIR-V headers) as follow-ups. **Other notes:** - #1473 (recorder thread) is still open, not merged, so `B_DIRECT` doesn't exist on main yet. If it lands first, every NGX create/evaluate here and the direct `vkCmd*` in these files need `rec::boundary(B_DIRECT)`. Its `test_recorder_sync.py` would flag these files. - I checked env names and option values against #1008 and #1773. No collisions: the `RX_VK_DLSS_NR*` names are distinct, `RX_VK_DLSS=0` deliberately turns off NGX NR too, and NR's quality values are separate from `opts.dlss` 3/4/5. This PR adds no numeric option ids (the Neural rows in #1834 are keyed by name). - Docs and maintainer call: the NGX path documents running a community-patched `nvngx_dlssnr.dll` with NGX's signature override turned on. The Open backend needs a model folder that can only come from NVIDIA's weights. Maintainers should decide whether they want to document either. - Nit: the Before upscaler path takes the 16F scene colour down to RGBA8 and back into the 16F colour input, an 8-bit hop before DLSS.
@ -0,0 +1,686 @@
// (hookshot) DLSS 5 neural rendering through NGX directly; gpu/vk/vk_dlssnr_ngx.h says why there are two paths and where
hckrman101 (Migrated from github.com) commented 2026-11-10 18:06:20 +00:00

Fine at the present, but #1831 now also calls this from temporal_upscale on the jittered render-size scene (Before upscaler). The model is told there is no jitter and no motion, and gets a flat depth, while the real s.depth, s.motion and cam.jitter are right there. If the model keeps history, it will average out the jitter DLSS needs and ghost on camera motion. Can that path pass the real inputs, or at least reset on cuts?

Fine at the present, but #1831 now also calls this from `temporal_upscale` on the jittered render-size scene (Before upscaler). The model is told there is no jitter and no motion, and gets a flat depth, while the real `s.depth`, `s.motion` and `cam.jitter` are right there. If the model keeps history, it will average out the jitter DLSS needs and ghost on camera motion. Can that path pass the real inputs, or at least reset on cuts?
@ -0,0 +37,4 @@
/// reach the next frame (tests/test_nr_settings_live.py says why that matters here).
bool asked() {
if (const char* e = getenv("RX_VK_DLSS_NR")) return atoi(e) != 0;
return nr_settings().on;
hckrman101 (Migrated from github.com) commented 2026-11-10 18:06:19 +00:00

This (and nr::asked_backend reading opts.nr_backend below) is compiled in every build, with or without NGX, and nr_settings() / opts.nr_backend only exist after #1834. Since build.py globs gpu/vk/*.cpp, this PR alone breaks the build on main.

This (and `nr::asked_backend` reading `opts.nr_backend` below) is compiled in every build, with or without NGX, and `nr_settings()` / `opts.nr_backend` only exist after #1834. Since build.py globs `gpu/vk/*.cpp`, this PR alone breaks the build on main.
@ -0,0 +77,4 @@
VmaAllocation memory{};
};
struct State {
hckrman101 (Migrated from github.com) commented 2026-11-10 18:06:19 +00:00

There's no lock on this state, and apply now has two callers on different threads: temporal_upscale (replay thread, Before upscaler / before HUD) and present_image (display thread). Only dlss_nr_before_hud()'s frame window keeps them apart, and on the first chain frame after a gap both can be in here at once. They share params (each points it at its own stack resources) and in/out, and when the sizes differ, ensure_feature / resize free what the other thread just recorded. A mutex around apply, or one owner per stage, would fix it. vk_opennr.cpp has the same problem.

There's no lock on this state, and `apply` now has two callers on different threads: `temporal_upscale` (replay thread, Before upscaler / before HUD) and `present_image` (display thread). Only `dlss_nr_before_hud()`'s frame window keeps them apart, and on the first chain frame after a gap both can be in here at once. They share `params` (each points it at its own stack resources) and `in`/`out`, and when the sizes differ, `ensure_feature` / `resize` free what the other thread just recorded. A mutex around `apply`, or one owner per stage, would fix it. `vk_opennr.cpp` has the same problem.
@ -0,0 +385,4 @@
/// The feature, made for this size, preset and quality: those are creation parameters, so a change of any of them makes
/// it again. Creation records into `cmd`.
bool ensure_feature(State& s, VkCommandBuffer cmd, const NrSettings& want, uint32_t w, uint32_t h) {
if (s.feature && s.feature_w == w && s.feature_h == h && s.feature_preset == want.preset &&
hckrman101 (Migrated from github.com) commented 2026-11-10 18:06:19 +00:00

Same as #1590: any size change releases the feature and recreates it, and release_feature / resize each do a full device idle. In Before upscaler mode with Resolution Auto, that's a stall on every render-size change. Each refusal is also held against the size, and after six NR is off for the run. Could the feature be created at the max render size and evaluated with subrects, or at least kept for a size seen recently?

Same as #1590: any size change releases the feature and recreates it, and `release_feature` / `resize` each do a full device idle. In Before upscaler mode with Resolution Auto, that's a stall on every render-size change. Each refusal is also held against the size, and after six NR is off for the run. Could the feature be created at the max render size and evaluated with subrects, or at least kept for a size seen recently?
@ -0,0 +485,4 @@
VkImage apply(VkCommandBuffer cmd, VkImage src, uint32_t sw, uint32_t sh, VkImageView* out_view, const NrFrame* frame) {
State& s = state();
if (!src || !sw || !sh || !asked()) return VK_NULL_HANDLE;
hckrman101 (Migrated from github.com) commented 2026-11-10 18:06:19 +00:00

When NR is turned off (or another backend is picked), this returns before anything is released. The feature, the parameter block and four full-size images stay allocated for the rest of the run. No shutdown() of the three backends is called anywhere in this series, so none is called at exit either.

When NR is turned off (or another backend is picked), this returns before anything is released. The feature, the parameter block and four full-size images stay allocated for the rest of the run. No `shutdown()` of the three backends is called anywhere in this series, so none is called at exit either.
@ -0,0 +645,4 @@
s.check_done = false;
s.phase = kChecking;
if (s.checker.joinable()) s.checker.join();
s.checker = std::thread([&s] {
hckrman101 (Migrated from github.com) commented 2026-11-10 18:06:20 +00:00

This thread is joined only on a later apply with NR on (the kChecking branch) or in shutdown(), and nothing calls shutdown(). If the row is turned off while the check runs, apply returns early from then on, so the std::thread stays joinable in the static State. Its destructor at exit then calls std::terminate.

This thread is joined only on a later `apply` with NR on (the kChecking branch) or in `shutdown()`, and nothing calls `shutdown()`. If the row is turned off while the check runs, `apply` returns early from then on, so the `std::thread` stays joinable in the static `State`. Its destructor at exit then calls `std::terminate`.
@ -0,0 +34,4 @@
def main():
p = argparse.ArgumentParser()
p.add_argument("--glslang", required=True)
hckrman101 (Migrated from github.com) commented 2026-11-10 18:06:20 +00:00

main's run_all.py has retired the ARGS/SKIP tables (#1834 adds these tests there, which stops the whole run). Each test now declares RUN_ALL_ARGS = ["--glslang", "{glslang}", "--spirv-val", "{spirv_val}"] and RUN_ALL_SKIP = {...} at top level. Without that, this test runs with no args and fails here. The same applies to test_opennr_harness.py and test_opennr_khr.py.

main's run_all.py has retired the ARGS/SKIP tables (#1834 adds these tests there, which stops the whole run). Each test now declares `RUN_ALL_ARGS = ["--glslang", "{glslang}", "--spirv-val", "{spirv_val}"]` and `RUN_ALL_SKIP = {...}` at top level. Without that, this test runs with no args and fails here. The same applies to test_opennr_harness.py and test_opennr_khr.py.
FIocker commented 2026-11-11 10:45:37 +00:00 (Migrated from github.com)

Thanks; this review found real bugs. I rebuilt the series on main at d7a73062, and the description is rewritten.

  • Can't merge on its own: agreed. All three descriptions (#1831, #1833, #1834) now say at the top that they build only together and should be reviewed as one unit. super-build/all is the three together.
  • Tests: the opennr tests declare RUN_ALL_ARGS / RUN_ALL_SKIP themselves, and nothing goes in the retired tables. Locally, run_all.py on the three together gives 328 passed. The only failures are the same 18 main has on this machine, all down to the toolchain.
  • Two threads, one state: nr::apply now takes a lock, so the chain's call (replay thread, chain lock held) and present_image's (display thread) never overlap. The order is always the chain's lock, then apply_mu, then a backend's own; neither caller holds present_mu there.
  • Size changes: not fixed. Feature 18's subrect behaviour isn't documented and I'd rather not guess, so it's listed as a known limit. With a fixed render resolution the size doesn't change.
  • Memory never released: the backend that stops running (row off, or another picked) is now shut down from nr::apply, which frees its feature or network and images. There's no device teardown path in the renderer to hook at exit; the process just ends.
  • The checker thread: State lets go of a still-running check, so a static destroyed joinable no longer calls std::terminate at exit.
  • No jitter, motion or depth before the upscaler: fixed. The chain hands over its own depth (R32F), motion (RG16F, render pixels), jitter and cut resets (NrFrame). NGX gets them as DLSSNR.Depth, MVec, JitterOffset and Reset; the open backend drops its history on a reset.
  • #1473: merged since, so I adapted to it. vk_dlssnr, vk_dlssnr_ngx and vk_opennr include vk_work.h last, with B_DIRECT before NGX's, Streamline's and the open network's own recording. test_recorder_sync.py passes.
  • Env names and options: thanks for checking against #1008 and #1773.
  • The community DLL, the model folder, and splitting off Streamline and OpenDLSS-NR: these are under "For maintainers to decide" in the description. I'm happy to trim the PR to NGX direct plus the in-chain placement if that's easier to review.
  • The 8-bit hop: noted as a known limit. The backends' colour input is RGBA8 today.
Thanks; this review found real bugs. I rebuilt the series on main at d7a73062, and the description is rewritten. - **Can't merge on its own**: agreed. All three descriptions (#1831, #1833, #1834) now say at the top that they build only together and should be reviewed as one unit. `super-build/all` is the three together. - **Tests**: the opennr tests declare `RUN_ALL_ARGS` / `RUN_ALL_SKIP` themselves, and nothing goes in the retired tables. Locally, `run_all.py` on the three together gives 328 passed. The only failures are the same 18 main has on this machine, all down to the toolchain. - **Two threads, one state**: `nr::apply` now takes a lock, so the chain's call (replay thread, chain lock held) and `present_image`'s (display thread) never overlap. The order is always the chain's lock, then `apply_mu`, then a backend's own; neither caller holds `present_mu` there. - **Size changes**: not fixed. Feature 18's subrect behaviour isn't documented and I'd rather not guess, so it's listed as a known limit. With a fixed render resolution the size doesn't change. - **Memory never released**: the backend that stops running (row off, or another picked) is now shut down from `nr::apply`, which frees its feature or network and images. There's no device teardown path in the renderer to hook at exit; the process just ends. - **The checker thread**: `State` lets go of a still-running check, so a static destroyed joinable no longer calls `std::terminate` at exit. - **No jitter, motion or depth before the upscaler**: fixed. The chain hands over its own depth (R32F), motion (RG16F, render pixels), jitter and cut resets (`NrFrame`). NGX gets them as `DLSSNR.Depth`, `MVec`, `JitterOffset` and `Reset`; the open backend drops its history on a reset. - **#1473**: merged since, so I adapted to it. `vk_dlssnr`, `vk_dlssnr_ngx` and `vk_opennr` include `vk_work.h` last, with `B_DIRECT` before NGX's, Streamline's and the open network's own recording. `test_recorder_sync.py` passes. - **Env names and options**: thanks for checking against #1008 and #1773. - **The community DLL, the model folder, and splitting off Streamline and OpenDLSS-NR**: these are under "For maintainers to decide" in the description. I'm happy to trim the PR to NGX direct plus the in-chain placement if that's easier to review. - **The 8-bit hop**: noted as a known limit. The backends' colour input is RGBA8 today.
hckrman101 (Migrated from github.com) reviewed 2026-11-11 18:26:23 +00:00
hckrman101 (Migrated from github.com) left a comment

Thanks for the thorough follow-up. Most of what I raised is fixed, but the ground under this PR moved today, so it can't land as it stands.

Fixed since my last review:

  • Two threads, one state: nr::apply now takes apply_mu, and the lock order (chain, then apply_mu, then the backend's own) holds in the code.
  • VRAM: the backend that stops running is now released from nr::apply (row off or another backend picked).
  • The checker thread no longer reaches std::terminate at exit (one nit inline).
  • Real inputs before the upscaler: NrFrame carries depth, motion, jitter and the cut reset, and both NGX and Open use them.
  • Tests: the three opennr tests declare RUN_ALL_ARGS / RUN_ALL_SKIP, and the placeholders (cxx, vulkan_headers, glslang, spirv_val) are ones main's run_all.py provides.
  • #1473: vk_work.h comes last, with B_DIRECT before NGX's, Streamline's and the network's own recording.

Answered, kept as known limits: the rebuild with a device idle on every size change, and the RGBA8 hop. Both are listed in the description, which is fine for an off-by-default feature.

Still open, and now blocking: the series no longer has a base. #1831 was closed today in favour of #2118 (DLSS-G through NGX, merged). This PR still relies on things only #1831 added:

  • vk_dlssnr_ngx.cpp needs dlss_ngx_session() / dlss_ngx_runtime() from vk_dlss.h and vk_ngx.h. Main's vk_dlss.h has neither, and vk_ngx.h doesn't exist. #2118's NGX session lives in vk_dlss.cpp's own state (ngx_init(), file-local).
  • vk_opennr.cpp includes vk_camera_motion.h and calls scene_frame_inputs() / camera_motion::record, which are also #1831-only. That include isn't behind HOOKSHOT_NGX, so it breaks every build, not just NGX ones.
  • It still needs #1834 for nr_settings(), opts.nr_*, opennr_ptx.h from build.py, the menu rows and the callers in the chain and the present path. #1834 also patches the vk_dlss.cpp / vk_framegen.cpp that #2118 has since rewritten.

So this has to be rebased onto main as it is now. The NGX direct backend should use #2118's session: one small exported accessor from vk_dlss.cpp (e.g. "NGX is up on this device", running ngx_init() once), and no second NVSDK_NGX_VULKAN_Init. The camera motion for the Open backend should either come from the chain's NrFrame (which already has motion before the upscaler) or be dropped from the present-time path. Before that, please check two things against #2118. Is nvngx_dlssnr found in runtime_dirs(), the paths #2118 gives NGX? And with DLSS-G on, do NR's feature create/release (with its device idle) and DLSS-G's evaluate stay apart? vk_dlss.cpp serialises its own NGX calls under s.mu, but NR's run outside that lock.

New:

  • Non-NVIDIA GPUs aren't untouched (inline): on any non-NVIDIA device, opennr::selected() is true whether or not NR is on, so device creation asks for VK_EXT_shader_float8 (and chains its feature struct) on AMD/Intel even when NR is off. NR off should mean no change to device creation there. If Open on AMD must be possible without a restart, that's worth a sentence in the description and docs, plus a test on a non-RDNA GPU.
  • Licensing looks right: third_party/opendlss-nr is MIT with LICENSE, NOTICE and PROVENANCE pinned to an upstream commit, no weights, and the NGX path copies no SDK code. The maintainer questions from last time (documenting the community-patched DLL and a model folder that only NVIDIA's weights can fill) are still for @NotRexed.
  • CI: only the title check ran on this head, so no build or tests ran here.

Overlap: #1590 (DLSS 5 tab plus feature 18 inside vk_dlss.cpp) and this PR can't both land. #1590 now conflicts with #2118's vk_dlss.cpp as well. #1542 is a different mechanism (an external AGPL Vulkan layer driven over IPC, with its own F1 page), so the overlap there is the menu and the "DLSS 5" naming, not code. My suggestion from last time still stands: rebuild this onto main as NGX direct plus the in-chain placement (with the #1834 plumbing it needs folded in, now that #1831 is gone), and send Streamline and the vendored OpenDLSS-NR as follow-ups. That would also make the non-NVIDIA question moot for the first PR.

Thanks for the thorough follow-up. Most of what I raised is fixed, but the ground under this PR moved today, so it can't land as it stands. **Fixed since my last review:** - Two threads, one state: `nr::apply` now takes `apply_mu`, and the lock order (chain, then `apply_mu`, then the backend's own) holds in the code. - VRAM: the backend that stops running is now released from `nr::apply` (row off or another backend picked). - The checker thread no longer reaches `std::terminate` at exit (one nit inline). - Real inputs before the upscaler: `NrFrame` carries depth, motion, jitter and the cut reset, and both NGX and Open use them. - Tests: the three opennr tests declare `RUN_ALL_ARGS` / `RUN_ALL_SKIP`, and the placeholders (`cxx`, `vulkan_headers`, `glslang`, `spirv_val`) are ones main's `run_all.py` provides. - #1473: `vk_work.h` comes last, with `B_DIRECT` before NGX's, Streamline's and the network's own recording. **Answered, kept as known limits:** the rebuild with a device idle on every size change, and the RGBA8 hop. Both are listed in the description, which is fine for an off-by-default feature. **Still open, and now blocking: the series no longer has a base.** #1831 was closed today in favour of #2118 (DLSS-G through NGX, merged). This PR still relies on things only #1831 added: - `vk_dlssnr_ngx.cpp` needs `dlss_ngx_session()` / `dlss_ngx_runtime()` from `vk_dlss.h` and `vk_ngx.h`. Main's `vk_dlss.h` has neither, and `vk_ngx.h` doesn't exist. #2118's NGX session lives in `vk_dlss.cpp`'s own state (`ngx_init()`, file-local). - `vk_opennr.cpp` includes `vk_camera_motion.h` and calls `scene_frame_inputs()` / `camera_motion::record`, which are also #1831-only. That include isn't behind `HOOKSHOT_NGX`, so it breaks every build, not just NGX ones. - It still needs #1834 for `nr_settings()`, `opts.nr_*`, `opennr_ptx.h` from build.py, the menu rows and the callers in the chain and the present path. #1834 also patches the `vk_dlss.cpp` / `vk_framegen.cpp` that #2118 has since rewritten. So this has to be rebased onto main as it is now. The NGX direct backend should use #2118's session: one small exported accessor from `vk_dlss.cpp` (e.g. "NGX is up on this device", running `ngx_init()` once), and no second `NVSDK_NGX_VULKAN_Init`. The camera motion for the Open backend should either come from the chain's `NrFrame` (which already has motion before the upscaler) or be dropped from the present-time path. Before that, please check two things against #2118. Is `nvngx_dlssnr` found in `runtime_dirs()`, the paths #2118 gives NGX? And with DLSS-G on, do NR's feature create/release (with its device idle) and DLSS-G's evaluate stay apart? `vk_dlss.cpp` serialises its own NGX calls under `s.mu`, but NR's run outside that lock. **New:** - Non-NVIDIA GPUs aren't untouched (inline): on any non-NVIDIA device, `opennr::selected()` is true whether or not NR is on, so device creation asks for `VK_EXT_shader_float8` (and chains its feature struct) on AMD/Intel even when NR is off. NR off should mean no change to device creation there. If Open on AMD must be possible without a restart, that's worth a sentence in the description and docs, plus a test on a non-RDNA GPU. - Licensing looks right: `third_party/opendlss-nr` is MIT with LICENSE, NOTICE and PROVENANCE pinned to an upstream commit, no weights, and the NGX path copies no SDK code. The maintainer questions from last time (documenting the community-patched DLL and a model folder that only NVIDIA's weights can fill) are still for @NotRexed. - CI: only the title check ran on this head, so no build or tests ran here. **Overlap:** #1590 (DLSS 5 tab plus feature 18 inside `vk_dlss.cpp`) and this PR can't both land. #1590 now conflicts with #2118's `vk_dlss.cpp` as well. #1542 is a different mechanism (an external AGPL Vulkan layer driven over IPC, with its own F1 page), so the overlap there is the menu and the "DLSS 5" naming, not code. My suggestion from last time still stands: rebuild this onto main as NGX direct plus the in-chain placement (with the #1834 plumbing it needs folded in, now that #1831 is gone), and send Streamline and the vendored OpenDLSS-NR as follow-ups. That would also make the non-NVIDIA question moot for the first PR.
@ -0,0 +16,4 @@
#include "vk_streamline.h"
#if defined(HOOKSHOT_NGX)
#include "vk_dlss.h" // (dlss_ngx_session, dlss_ngx_runtime: the one NGX session and the folders it was given)
hckrman101 (Migrated from github.com) commented 2026-11-11 18:26:23 +00:00

dlss_ngx_session() / dlss_ngx_runtime() and vk_ngx.h came from #1831, which is closed. Main's vk_dlss.h (after #2118) exports neither, and its NGX init is file-local in vk_dlss.cpp. This needs a small accessor there, sharing #2118's single NVSDK_NGX_VULKAN_Init, rather than bringing #1831's version back.

`dlss_ngx_session()` / `dlss_ngx_runtime()` and `vk_ngx.h` came from #1831, which is closed. Main's `vk_dlss.h` (after #2118) exports neither, and its NGX init is file-local in `vk_dlss.cpp`. This needs a small accessor there, sharing #2118's single `NVSDK_NGX_VULKAN_Init`, rather than bringing #1831's version back.
@ -0,0 +29,4 @@
#include "shaders/opennr_khr_spv.h"
#include "shaders/opennr_post_comp_spv.h"
#include "shaders/opennr_pre_comp_spv.h"
#include "vk_camera_motion.h"
hckrman101 (Migrated from github.com) commented 2026-11-11 18:26:23 +00:00

vk_camera_motion.h, scene_frame_inputs() and camera_motion::record are #1831-only too, and this isn't behind HOOKSHOT_NGX, so every build fails on main. Before the upscaler, NrFrame already carries the chain's motion. Could the present-time path go without it, or get it from the chain?

`vk_camera_motion.h`, `scene_frame_inputs()` and `camera_motion::record` are #1831-only too, and this isn't behind `HOOKSHOT_NGX`, so every build fails on main. Before the upscaler, `NrFrame` already carries the chain's motion. Could the present-time path go without it, or get it from the chain?
@ -0,0 +76,4 @@
// (a check still running at exit: let go of, not destroyed joinable, which is std::terminate. It is joined on a later
// apply with the row on, or in shutdown; the row turned off mid-check left it joinable in this static for good)
~State() {
if (checker.joinable()) checker.detach();
hckrman101 (Migrated from github.com) commented 2026-11-11 18:26:24 +00:00

Nit: this avoids std::terminate, but the detached lambda captures &s and still writes s.check / s.check_done into a State that is being destroyed. It's only at exit and only mid-check, so it's minor. Giving the thread its own heap result (a shared_ptr it writes into) would make the detach clean.

Nit: this avoids `std::terminate`, but the detached lambda captures `&s` and still writes `s.check` / `s.check_done` into a `State` that is being destroyed. It's only at exit and only mid-check, so it's minor. Giving the thread its own heap result (a `shared_ptr` it writes into) would make the detach clean.
@ -0,0 +461,4 @@
if (const char* e = getenv("RX_VK_NR_BACKEND"); e && !strcmp(e, "open")) return true;
if (const char* e = getenv("RX_VK_DLSS_NR_BACKEND"); e && (!strcmp(e, "open") || !strcmp(e, "3"))) return true;
if (opts.nr_backend == 3) return true;
if (dev.physical && dev.props.vendorID != 0x10de) return true; // (not NVIDIA: Open, the khr route)
hckrman101 (Migrated from github.com) commented 2026-11-11 18:26:24 +00:00

This makes selected() true on every non-NVIDIA GPU, so device_extensions / device_features add VK_EXT_shader_float8 and its feature struct on AMD/Intel even with NR off. NR off should leave device creation on non-NVIDIA cards as it is on main. Could this be gated on the row or env asking for Open (or on a model folder being there, as on NVIDIA)?

This makes `selected()` true on every non-NVIDIA GPU, so `device_extensions` / `device_features` add `VK_EXT_shader_float8` and its feature struct on AMD/Intel even with NR off. NR off should leave device creation on non-NVIDIA cards as it is on main. Could this be gated on the row or env asking for Open (or on a model folder being there, as on NVIDIA)?
Joey-Magnus (Migrated from github.com) requested changes 2026-11-11 21:46:09 +00:00
Joey-Magnus (Migrated from github.com) left a comment

I think this should be closed. It only builds together with #1831 and #1834, and #1831 was closed today in favour of #2118 (merged). vk_opennr.cpp still includes vk_camera_motion.h and calls scene_frame_inputs, which only #1831 added, and neither is on main. vk_dlssnr_ngx.cpp needs nr_settings() from #1834. GitHub shows it as mergeable, but nx2/build.py compiles every gpu/vk/.cpp, so merging it would break every build. Separately, the ~29k lines of opennr__spv.h come from a new scripts/opennr_kernels.py instead of scripts/host_shaders.py, which the repo rules require. The Open backend's model folder can also only come from NVIDIA's weights. #1590 covers the same NGX feature 18 path, builds on its own and is in active review. If you want to keep going, I'd offer the in-chain placement before the HUD to #1590 as a follow-up.

I think this should be closed. It only builds together with #1831 and #1834, and #1831 was closed today in favour of #2118 (merged). vk_opennr.cpp still includes vk_camera_motion.h and calls scene_frame_inputs, which only #1831 added, and neither is on main. vk_dlssnr_ngx.cpp needs nr_settings() from #1834. GitHub shows it as mergeable, but nx2/build.py compiles every gpu/vk/*.cpp, so merging it would break every build. Separately, the ~29k lines of opennr_*_spv.h come from a new scripts/opennr_kernels.py instead of scripts/host_shaders.py, which the repo rules require. The Open backend's model folder can also only come from NVIDIA's weights. #1590 covers the same NGX feature 18 path, builds on its own and is in active review. If you want to keep going, I'd offer the in-chain placement before the HUD to #1590 as a follow-up.
hckrman101 (Migrated from github.com) reviewed 2026-11-11 22:20:54 +00:00
hckrman101 (Migrated from github.com) left a comment

No new commits since my review at this head, so everything in it still stands: the series has lost its base now that #1831 is closed (vk_ngx.h, dlss_ngx_session() / dlss_ngx_runtime(), vk_camera_motion.h and scene_frame_inputs() aren't on main), it still needs #1834's plumbing, and the non-NVIDIA device-creation change (VK_EXT_shader_float8 asked for with NR off) is still open.

On @Joey-Magnus's review: I agree on the build break, which is the same blocker as above. One point I'd correct: scripts/host_shaders.py only builds the texture shaders in gpu/vk/host_shaders/ (a fixed list of three), not gpu/vk/shaders/, and main already has a separate generator in scripts/fsr3_shaders.py. The rule in AGENTS.md is that *_spv.h headers are compiled by glslang from their sources rather than hand-edited. scripts/opennr_kernels.py does that, and tests/test_opennr_kernels.py --check recompiles and compares. The real cost of those headers is size (~29k lines of SPIR-V for the vendored network), which is one more reason to send OpenDLSS-NR as a follow-up, as suggested before.

On closing in favour of #1590: #1590 currently shows conflicts with main too (it touches the vk_dlss.cpp that #2118 rewrote), so both need a rebase before either builds on main. Which design lands stays with @NotRexed. My technical view is unchanged: the in-chain placement before the HUD and the separation from SR history are the better base, and the cleanest next step for this PR is a rebuild onto main as NGX direct plus the in-chain placement, using #2118's NGX session, with the #1834 pieces it needs folded in.

No new commits since my review at this head, so everything in it still stands: the series has lost its base now that #1831 is closed (`vk_ngx.h`, `dlss_ngx_session()` / `dlss_ngx_runtime()`, `vk_camera_motion.h` and `scene_frame_inputs()` aren't on main), it still needs #1834's plumbing, and the non-NVIDIA device-creation change (`VK_EXT_shader_float8` asked for with NR off) is still open. On @Joey-Magnus's review: I agree on the build break, which is the same blocker as above. One point I'd correct: `scripts/host_shaders.py` only builds the texture shaders in `gpu/vk/host_shaders/` (a fixed list of three), not `gpu/vk/shaders/`, and main already has a separate generator in `scripts/fsr3_shaders.py`. The rule in AGENTS.md is that `*_spv.h` headers are compiled by glslang from their sources rather than hand-edited. `scripts/opennr_kernels.py` does that, and `tests/test_opennr_kernels.py --check` recompiles and compares. The real cost of those headers is size (~29k lines of SPIR-V for the vendored network), which is one more reason to send OpenDLSS-NR as a follow-up, as suggested before. On closing in favour of #1590: #1590 currently shows conflicts with main too (it touches the `vk_dlss.cpp` that #2118 rewrote), so both need a rebase before either builds on main. Which design lands stays with @NotRexed. My technical view is unchanged: the in-chain placement before the HUD and the separation from SR history are the better base, and the cleanest next step for this PR is a rebuild onto main as NGX direct plus the in-chain placement, using #2118's NGX session, with the #1834 pieces it needs folded in.
github-sync changed title from feat(vk): DLSS 5 neural rendering (NGX direct, Streamline, OpenDLSS-NR), in the upscale chain (builds with #1831, #1834) to feat(vk): DLSS 5 neural rendering through NGX, in the temporal chain before the HUD 2026-11-12 13:38:29 +00:00
Owner

@FIocker commented on GitHub · 2026-11-12 13:09 UTC · original

Rebuilt on main at 51da0464 (ef1b7b81), as hckrman101 suggested: NGX direct plus the in-chain placement, on #2118's session, with the plumbing it needs folded in. It builds on its own now. Streamline and OpenDLSS-NR are gone from it.

Your two questions:

  • runtime_dirs(): yes. dlss_ngx_runtime looks the runtime up in runtime_dirs(), and NGX is told the same folders, so nvngx_dlssnr.dll goes where #2118's nvngx_dlssg.dll goes.
  • NR vs DLSS-G: NR's create, evaluate and release now run under dlss_ngx_feature_mutex(), which is DLSS-G's own Fg::mu, so the two never overlap. The order matches dlss_fg_inputs: the chain's lock first. The device idle before a release is taken outside it, under the queue's lock, as fg_release does.

The rest:

  • Non-NVIDIA devices: that change went with OpenDLSS-NR. Feature 18's extensions are asked only where the NVX extensions are, as DLSS-G's are.
  • Checker thread: gone too.
  • Native HDR: one new case, since main has it now. NR stands aside under Native HDR, and the row says why.

@Joey-Magnus: thanks for the review. The build break is gone: vk_camera_motion.h, scene_frame_inputs, vk_ngx.h and the SPIR-V headers are no longer in this PR, and nr_settings() comes with it. Which of this and #1590 lands is NotRexed's call. If it's #1590, I'm happy to offer the before-HUD placement there.

Locally, the whole run_all.py matches main on this machine (434 passed). An in-game run of this head with DLSS-G on and off, plus screenshots, is still to come.

> [@FIocker](https://github.com/FIocker) commented on GitHub · 2026-11-12 13:09 UTC · [original](https://github.com/NotRexed/Hookshot/pull/1833#issuecomment-6824499872) Rebuilt on main at 51da0464 (ef1b7b81), as hckrman101 suggested: NGX direct plus the in-chain placement, on #2118's session, with the plumbing it needs folded in. **It builds on its own now.** Streamline and OpenDLSS-NR are gone from it. Your two questions: - **`runtime_dirs()`**: yes. `dlss_ngx_runtime` looks the runtime up in `runtime_dirs()`, and NGX is told the same folders, so `nvngx_dlssnr.dll` goes where #2118's `nvngx_dlssg.dll` goes. - **NR vs DLSS-G**: NR's create, evaluate and release now run under `dlss_ngx_feature_mutex()`, which is DLSS-G's own `Fg::mu`, so the two never overlap. The order matches `dlss_fg_inputs`: the chain's lock first. The device idle before a release is taken outside it, under the queue's lock, as `fg_release` does. **The rest**: - **Non-NVIDIA devices**: that change went with OpenDLSS-NR. Feature 18's extensions are asked only where the NVX extensions are, as DLSS-G's are. - **Checker thread**: gone too. - **Native HDR**: one new case, since main has it now. NR stands aside under Native HDR, and the row says why. @Joey-Magnus: thanks for the review. The build break is gone: `vk_camera_motion.h`, `scene_frame_inputs`, `vk_ngx.h` and the SPIR-V headers are no longer in this PR, and `nr_settings()` comes with it. Which of this and #1590 lands is NotRexed's call. If it's #1590, I'm happy to offer the before-HUD placement there. Locally, the whole `run_all.py` matches main on this machine (434 passed). An in-game run of this head with DLSS-G on and off, plus screenshots, is still to come. <!-- github-sync:c:6824499872 -->
Owner

@Joey-Magnus reviewed on GitHub · 2026-11-12 14:32 UTC · original

The rebuild fixes what I raised. It builds on its own now (no vk_camera_motion.h, no scene_frame_inputs, nothing from #1834), the OpenDLSS-NR SPIR-V headers and Streamline are gone, and the NGX session, the runtime folders and the DLSS-G lock all come from #2118. CI is green on ef1b7b8, which sits on current main.

Still open:

  • The Neural tab shows in every build. TAB_NEURAL is a full tab of 11 rows, and nothing in row_shown hides it without NGX, so AMD, Intel and Linux users get a tab that can only say "not available". hckrman101 asked #1590 for a row under Graphics that opens its own page and hides when !dlss_built(). The same would fit here.
  • There are no screenshots of the tab or of the result, and the in-game run of this head hasn't happened yet. The 47 vs 37 fps figure is from before the rebuild.
  • The new menu strings aren't in menu_i18n.h. #1590 has its strings there in 13 languages.

#1590 adds the same feature-18 path, so only one of them can land. Which one is @NotRexed's call.

> [@Joey-Magnus](https://github.com/Joey-Magnus) reviewed on GitHub · 2026-11-12 14:32 UTC · [original](https://github.com/NotRexed/Hookshot/pull/1833#pullrequestreview-5876202158) The rebuild fixes what I raised. It builds on its own now (no vk_camera_motion.h, no scene_frame_inputs, nothing from #1834), the OpenDLSS-NR SPIR-V headers and Streamline are gone, and the NGX session, the runtime folders and the DLSS-G lock all come from #2118. CI is green on ef1b7b8, which sits on current main. Still open: - The Neural tab shows in every build. TAB_NEURAL is a full tab of 11 rows, and nothing in row_shown hides it without NGX, so AMD, Intel and Linux users get a tab that can only say "not available". hckrman101 asked #1590 for a row under Graphics that opens its own page and hides when !dlss_built(). The same would fit here. - There are no screenshots of the tab or of the result, and the in-game run of this head hasn't happened yet. The 47 vs 37 fps figure is from before the rebuild. - The new menu strings aren't in menu_i18n.h. #1590 has its strings there in 13 languages. #1590 adds the same feature-18 path, so only one of them can land. Which one is @NotRexed's call. <!-- github-sync:r:5876202158 -->
Commenting is not possible because the repository is archived.
No description provided.