A scriptable DLSS host for testing ReShade add-ons that sit on the NGX path, built for dlss5-bridge. Two small executables create a real D3D11 or Vulkan device, call real NVIDIA NGX, run a real DLSS feature over a moving scene under ReShade with the add-on attached, and walk through a scenario: display-mode changes, resizes, preset changes, feature recreation, DLSS switched off and on, malformed parameter blocks. A run takes seconds and ends in a verdict read from the add-on's own log.
If it is useful to you, you can help cover the AI tooling used in its development:
- Windows, an NVIDIA GPU with DLSS, and a current driver.
- ReShade 6.x with add-on support, installed the normal way:
run.ps1stagesC:\ProgramData\ReShade\ReShade64.dllasd3d11.dllbeside the D3D11 host; the Vulkan host uses ReShade's machine-wide Vulkan layer. - To run:
nvngx_dlss.dll3.1.13 or newer insnippets\(from any game that ships DLSS). Optionally, beside it, a DLSS 5 neural rendering add-on (renodx-dlss5*.addon64) with itsnvngx_dlssnr.dll; both are then staged and the add-on is the consumer under test. The add-on under test,dlss5-bridge.addon64, goes beside the scripts or in a siblingngxbridge\checkout, or is passed with-Addon. - To build: Visual Studio Build Tools with
cl.exe, NVIDIA's DLSS SDK (github.com/NVIDIA/DLSS) asdlss-sdk\or inNGX_SDK, and the Vulkan SDK inVULKAN_SDKfor the Vulkan host. Without the Vulkan SDK the D3D11 host still builds. Prebuilt hosts are attached to each release.
.\build.cmd both hosts
.\suite.ps1 every scenario, both backends
.\suite.ps1 -Fast -Background a fraction of the frames, no window takes focus
.\suite.ps1 -Only vk -Repeat 4 one backend, four times each
.\suite.ps1 -Validate Vulkan runs with the Khronos validation layer
.\run.ps1 -Scenario modes one D3D11 scenario
.\run-vk.ps1 -Scenario display one Vulkan scenario
.\run-vk.ps1 -Register once, if the Vulkan layer never attaches
Every run stages a disposable folder under run\ with the host, the add-on,
the snippet, a ReShade configuration and one trivial effect. Nothing outside it
is written, except -Register, which appends the Vulkan host to
C:\ProgramData\ReShade\ReShadeApps.ini with a timestamped backup.
-Fast divides every frame count by eight and covers contracts rather than
cadence; a scenario that depends on the wall clock opts out with # nofast.
-Background shows the host without activating it; exclusive fullscreen is
refused in that mode. vault.ps1 keeps hashed copies of the third-party files
a run stages, recorded in vault.tsv, so an A/B between two builds of a
neighbour add-on is one command: .\vault.ps1 -Use <label> -To <folder>.
Run there by a reporter on Linux (dlss5-bridge #22), whose notes this section
follows. The D3D11 host is a plain Windows program and runs under umu/Proton;
run.ps1 runs under PowerShell for Linux (pwsh). The release binaries from
v1.1.0 on carry every verb below; v1.0.0 predates several. ReShade goes in as
dxgi.dll with a DLL override, the way it is installed in a Proton game, and
the host is started through the launcher:
pwsh ./run.ps1 -Scenario synth-nodlss-modes -Launcher umu-run -Proxy dxgi \
-Shade /path/to/ReShade64.dll -Addon /path/to/dlss5-bridge.addon64
-Launcher prefixes the host command and sets WINEDLLOVERRIDES=dxgi=n,b;
-StageOnly assembles the run folder and stops, for running the host by hand.
The verdict reads the same log lines as on Windows. The Vulkan host needs
ReShade's Vulkan layer inside the prefix and is not covered by the runner.
Three things differ under Proton:
- The driver's optical flow does not open there, so the substitute contract
needs a
texMotionVectorsprovider. With-Launcherthe runner enablesngxGym_mv.fx, a zero-motion stub, alongside the probe. - The host renders the scene at several hundred frames per second, and the
substitute arms only after 10 s of quiet. Do not use
-Fast, and raise theframescounts of a synth scenario (6000 per step is known to work) if a run ends before the arming line appears. - Wine's built-in
d3dcompiler_47rejects[fastopt], which motion-estimation effects use. A natived3dcompiler_47.dllin the prefix withWINEDLLOVERRIDES=d3dcompiler_47=n,bcompiles them; the stub above needs neither.
A scenario is a text file of verbs in scenarios\, one per line:
| Verb | Effect |
|---|---|
frames N |
render N frames |
mode windowed|borderless|exclusive |
display mode |
resize W H |
output size |
preset 0..5 |
DLSS quality; the render size follows. 5 is DLAA |
recreate |
create the feature again at an unchanged shape |
dlss on|off |
stop and restart calling DLSS. Off, no feature is created on a mode change either |
nodlss |
never initialise NGX or create a feature: a game without DLSS. The textures are output-sized |
hdr on|off |
HDR10: R10G10B10A2, PQ colour space, the scene PQ-encoded as nits up to 1000, IsHDR set. Off is the default float swapchain |
scrgb on|off [nits] [g22] |
scRGB HDR: float swapchain, linear colour space, IsHDR set, the scene's brightest pixel at nits (640 by default); g22 leaves the gamma-2.2 colour space on the swapchain, as a game that never sets one does. D3D11 host only |
sdr on|off |
8-bit SDR: R8G8B8A8, sRGB colour space. D3D11 host only |
depthcolor on|off |
hand NGX the depth as an R32_SFLOAT colour image, as RTX Remix does. Vulkan host only |
pad N |
allocate every texture N rows taller than the contract, as a game padded for dynamic resolution does. D3D11 host only |
exposure on|off |
supply an ExposureTexture |
transpose on|off |
declare the contract the wrong way round |
stale on|off |
evaluate with another feature's four scalars |
omit flags|jitter|mvscale|quality |
leave a key out of the block |
Directives in comments:
| Line | Effect |
|---|---|
# expect: text |
the add-on's log must contain it. # expect-d3d11: and # expect-vk: for one backend |
# expect-after: A :: B |
some B after the last A. # expect-after-d3d11: and # expect-after-vk: for one backend |
# host-may-fail |
the host's own NGX evaluates may fail without failing the run; -d3d11 / -vk for one backend. Alone on its line |
# cfg: key=value |
appended to the generated dlss5-bridge.cfg |
# nr: key=value |
written to the consumer's [RenoDX.DLSS5] settings for a reproducible neural profile |
# diagnostic-only |
run explicitly with the runner; excluded from the release suite (intentional failure reproducers and A/B experiments) |
# cfg-old-file |
leave the settings file unstamped, to test its replacement |
# nofast |
run in full under -Fast |
# d3d11-only |
the suite skips the scenario on the Vulkan host |
# vk-only |
the suite skips the scenario on the D3D11 host |
# proxy: d3d11|dxgi|d3d12 |
the name ReShade is staged under for this scenario; d3d12 is the arrangement of a D3D11 game that imports d3d12.dll |
A verb the parser accepts and the executor drops fails the run rather than passing it.
colourchart replaces the moving scene with 16 stationary patches. The
synth-colour scenario uses known HDR10 luminances (1–1000 nits), saturated
BT.2020 primaries and mixed colours. Both runners invoke check-colour.py
(Python 3, standard library only) for this scenario: it checks the actual input
against those known values, then compares every output channel in nits.
Use -NoConsumer to isolate the bridge and DLSS. With the neural add-on present,
the check permits its intentional changes, but rejects broad gamut contamination.
hash_out=2 enables the patch-centre readback; hash_out=1 retains ordinary
means and hashes. Run with the display already in HDR mode.
The HDR10 DLSS-off scene is rendered in the back buffer's format on D3D11; Vulkan explicitly PQ-encodes the scene before its present blit. Earlier versions copied incompatible D3D11 formats and presented unencoded Vulkan values, so their HDR log-only checks did not establish colour fidelity. The generated ReShade configuration declares normal depth, matching the host's own DLSS contract and shader, instead of inheriting ReShade's reversed-depth default.
synth-colour-bg3* are explicit diagnostic runs with the reported BG3 neural
profile. The default profile reproduces colour changes caused by neural Local
Tone; -local0 isolates that control, while -intensity0 checks the consumer's
colour codec without a requested neural change. These diagnostics are excluded
from the release suite: a configured artistic change is not a transport assertion.
A run passes when the host exits 0, the add-on registered with ReShade,
recorded no crash, built at least one feature, delivered or recorded at least
one frame, never stood down, and every # expect line holds. On Vulkan the
mirror also has to come back after every effect-runtime teardown, and the
host's own DLSS evaluates all have to succeed. Where the substitute contract
holds the session, a delivered-count line has to appear after it armed, and
the scenarios with teardowns require one after the last runtime ReShade
created.
The suite adds one check that measures the neighbour rather than the add-on:
consumer runs twice, with and without the DLSS 5 add-on staged, and the
add-on's own output hash must differ. It exists because a build of that add-on
reported active and wrote nothing, and nothing else here could tell.
A second one measures brightness: brightness builds the feature on the four
presentations a game has, plain float, scRGB HDR, HDR10 and 8-bit SDR, and the
add-on logs the mean of its output per channel 60 frames into each. With and
without the DLSS 5 add-on, per presentation, the luma ratio has to stay within
[0.5, 2]. A neural pass that overexposes an HDR frame fails here and nowhere
else. The HDR10 means are PQ code values, not nits; the ratio is still the
same direction.
run.ps1, run-vk.ps1 stage a folder and run one scenario
suite.ps1 every scenario, both backends, one summary
vault.ps1, vault.tsv copies and hashes of staged third-party files
scenarios\ the scenarios
src\ the two hosts, one .cpp each, and the scene shaders
reshade-fx\ the effect that gives ReShade something to run
notes\ measurements worth keeping
scene-pre, scene-post and scene-ofa separate scene-linear BT709 colour,
a 1000-nit shoulder, and a later opaque UI. All use full-resolution HDR10.
The first runs native DLAA before tone mapping; the second runs synthetic OFA
on the final display image; the third supplies scene colour/depth directly to
the bridge's experimental OFA entry point, with host DLSS disabled.
Build the third with ../ngxbridge/tests/build-scene.cmd and pass its
tests/scene-build/dlss5-bridge.addon64 to run.ps1 -Addon. It is excluded from
normal bridge builds. Run each scenario with and without -NoConsumer, always
-Background, preserving each run's gym-display.bin before the next run.
The capture add-on records packed HDR10 at reshade_finish_effects, before
Present. check-placement.py takes the four pre/post files, then optionally
the two OFA files; its docstring gives their order.
The assertions check plain transport and unchanged UI. Scene differences between neural paths are reported, not declared errors solely because NR changes lighting. These patch measurements do not establish temporal or whole-image equivalence, nor validate a BG3/Vulkan integration.
inspect-scene-shaders.py EXTRACTED_DIR OUTPUT_DIR --sdk-bin SDK/Bin scans
extracted BSHD containers, validates SPIR-V with spirv-val, and records CRC32,
SHA256, entry points and descriptor reflection using spirv-cross. It emits a
scene-probe.cfg for fragment and compute modules. Extraction of game packages
is separate; the inspector never modifies game files. Fingerprints establish
shader identity, not runtime execution or colour/exposure semantics.
The bridge now includes the probe; build-scene-probe.cmd builds an optional
standalone wrapper around the same implementation for older bridge builds.
The probe logs matching Vulkan pipeline creation and up to four matching
fragment pass observations/compute dispatches, including whether a render pass is active.
It does not copy resources, wait on the GPU, change shaders or perform NR.
Remove the probe add-on and profile when the observation is complete.
run-vk.ps1 -Scenario scene-probe -Background -Validate -SceneProbeProfile PROFILE
exercises real graphics and compute pipelines. A matching profile must observe
the fragment draw inside a render pass and the compute dispatch outside it;
an unknown fingerprint must produce neither. The probecompute scenario verb
is deliberately rejected by the D3D11 host. Profiles are opt-in for Gym runs.
Add -SceneProbeIntegrated to stage only the profile, using the bridge's built-in
observer. Profile lines may include CRC32 SET BINDING. The diagnostic also
exercises an unused float image descriptor: direct update, copy invalidation,
and fresh direct update. check-scene-probe.py MATCH_LOG INVALID_BINDING_LOG
checks those observations and ensures they never certify scene semantics.
reshade-fx\ReShade.fxh is from the ReShade shader repository, CC0. The NGX
headers and library come from NVIDIA's DLSS SDK under its own licence and are
not included. Everything else is MIT.