Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions INSTALL-DLSSNR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Install DLSS Neural Rendering

This fork is experimental. Do not use injection mods in anti-cheat-protected multiplayer games.

## Requirements

- A 64-bit game whose temporal upscaler reaches an OptiScaler D3D12 path. Native D3D12 is preferred;
supported D3D11 and Vulkan games can use OptiScaler's D3D12 bridges.
- NVIDIA driver 616.56 or newer.
- The complete release archive from this repository. It includes `setup_windows.bat`, the
`OptiScaler` backend folder, `OptiScaler.dll`, `OptiScaler.ini`, and `nvngx.dll_dlssnr.dll`.
- A separately obtained `nvngx_dlssnr.dll` 310.8 runtime appropriate for the GPU.

The two similarly named files are different and both are required:

| File | Purpose |
|---|---|
| `nvngx.dll_dlssnr.dll` | Open-source forwarder supplied by this project |
| `nvngx_dlssnr.dll` | NVIDIA-derived Neural Rendering runtime supplied separately by the user |

## Choose the correct runtime

| GPU | Runtime | SHA-256 |
|---|---|---|
| RTX 50 | Original NVIDIA-signed 310.8 | `E16BCF15E16E13F527491CDF7845B2FE6521A738D8F7C9C721866A8496E1FC8E` |
| RTX 20 / 30 / 40 | ShortFuse cross-generation 310.8 | `E67DEE209320CDAFE0E93E45675D7AA34323A53ACC57A72B2E40A181581C989A` |

For RTX 20/30/40, obtain the compatibility runtime from
[ShortFuse's pinned RenoDX thread](https://discord.com/channels/1408098019194310818/1543976771920330884).
The compatibility DLL automatically selects an FP16-oriented path on RTX 20/30, an Ada-compatible
path on RTX 40, and leaves the RTX 50 path unchanged.

The compatibility runtime is modified, so Windows reports the original NVIDIA signature as invalid.
That is expected for this exact hash, but it removes the assurance provided by Authenticode. Keep
security protection enabled, use only the pinned developer attachment, and verify the SHA-256 value:

```powershell
Get-FileHash .\nvngx_dlssnr.dll -Algorithm SHA256
```

## Install

1. Close the game and its launcher.
2. Find the directory containing the real game executable, which is often below the game's root.
3. Back up any existing proxy DLL, `OptiScaler.ini`, and OptiScaler installation.
4. Extract the entire release archive into that executable directory. Do not copy only the two DLLs.
5. Put the correct `nvngx_dlssnr.dll` from the table above in the same directory.
6. Run `setup_windows.bat`. It renames `OptiScaler.dll` to a proxy filename the game will load and
creates an uninstaller. `dxgi.dll` is the usual first choice. The validated Cyberpunk 2077 setup
used `dbghelp.dll` to coexist with its existing loaders.
7. Enable Neural Rendering in the `Insert` overlay, or edit `OptiScaler.ini`:

```ini
[DlssNr]
Enabled=true
RunBeforeSR=true
Passes=1
WorkingScale=1.0
```

Begin with one pass. RTX 20/30 use a much heavier FP16 path, so reduced model resolution may be
necessary. With `RunBeforeSR=true`, DLSS Performance at 3840x2160 gives the model a 1920x1080 input
before Super Resolution. `WorkingScale=0.5` lowers only the model's work resolution further.

For a portable setup, leave the process filter disabled:

```ini
[ProcessFilter]
TargetProcessName=auto
```

Do not copy an INI containing another game's executable name. A mismatch intentionally puts
OptiScaler into pass-through mode, which means no menu and no Neural Rendering.

## Game notes

- **Baldur's Gate 3:** install beside `bg3.exe` / `bg3_dx11.exe` in `Baldurs Gate 3\bin`.
Use `Dx12Upscaler=dlss` for `bg3.exe`, or `Dx11Upscaler=dlss_12` for `bg3_dx11.exe`.
- **Hogwarts Legacy:** install in `Phoenix\Binaries\Win64`; `dxgi.dll` was validated.
- **Cyberpunk 2077:** install in `bin\x64`; `dbghelp.dll` was validated on the development machine.
Existing CET/RED4ext/ReShade loaders can require a different proxy or correct chaining.

Do not install the RenoDX DLSS add-on merely to obtain its compatibility runtime. This OptiScaler
fork drives `nvngx_dlssnr.dll` itself, and two Neural Rendering injectors can conflict.

## Individual pass controls

Under **DLSS Neural Rendering → Model passes**, expand Pass 1, Pass 2, or Pass 3. Each contains
Style, Intensity, Local structure, Local tone, Skin structure, and Auto skin mask. Sliders commit
when released to avoid rebuilding the model on every movement. Set the model pass count to 2 or 3
to activate later passes; editing inactive passes prepares their settings without running them.

Later passes inherit pass 1 unless overridden, except Local tone, which defaults to 0 to preserve
the earlier build's appearance. Reset on a later-pass slider clears its override. Intensity,
local structure, and local tone range from 0 to 2; skin structure ranges from -1 to 2, with -1
following local structure. The corresponding INI keys are `Pass2Intensity`, `Pass2LocalStructure`,
`Pass2LocalTone`, `Pass2SkinStructure`, and `Pass2AutoMask`, with matching `Pass3...` keys.
Use `auto` for the default behavior. Styles retain `Pass2Style` / `Pass3Style`.

These controls apply to D3D12 multipass and its bridges, both before/after SR and after native RR.
Native Vulkan and the driver-proxy backend remain single-pass. Preset hints are still transmitted
at model creation, but a changed hint is not proof of a changed model. They are preserved under
**Advanced preset hints (effect unverified)** and in the INI for compatibility.

## Neural Rendering with native Ray Reconstruction

In a game that already supports RR, enable RR in the game's settings and enable
**Apply after Ray Reconstruction (DX12)** in OptiScaler's Neural Rendering menu. The master
**Enable Neural Rendering** switch must also be on. Equivalent INI settings:

```ini
[DlssNr]
Enabled=true
ApplyAfterRR=true
RRPasses=1
RRWorkingScale=0.5
```

RR reconstructs and upscales first. NR then processes that output before frame generation.
`RunBeforeSR` does not override this order. At 4K output, `RRWorkingScale=0.5` runs NR at
1920x1080 and resizes its edit for composition; it does not reduce RR's own resolution.
`RRPasses=1..3` is independent of ordinary `Passes`, while per-pass model profiles are shared.
Switching between SR and RR rebuilds NR history even when their dimensions match.
These controls apply to D3D12 and its bridges, not the upstream native Vulkan NR path.

If Cyberpunk's RR option is greyed out with this fork, avoid the `d3d12.dll` proxy: an
[upstream report](https://github.com/Dagherbou/OptiScaler_DLSSNR/issues/8) confirmed that using
`dxgi.dll` resolved a Streamline conflict. Back up existing loaders before changing the proxy.
Keep the game's genuine `nvngx_dlssd.dll` (RR) separate from `nvngx_dlssnr.dll` (NR).
For an RR-only comparison, disable the master NR switch; “Apply the model” merely hides the edit
and still incurs NR's GPU cost. Successful RR initialization alone does not prove image quality.

## Diagnose a missing menu

Set:

```ini
[Log]
LogToFile=true
LogLevel=2
```

Then launch into a rendered scene and press `Insert` (`Alt+Insert` can help on some keyboard layouts).

- No `OptiScaler.log` beside the executable: the proxy was not loaded. Check the directory, proxy
filename, antivirus quarantine, and conflicts with another DLL using the same proxy name.
- The log says `OptiScaler ... loaded` and `working as ...`: injection succeeded. A remaining problem
belongs to the overlay input or Neural Rendering initialization, not the loader.
- `the model would not initialise`: on RTX 20/30/40, first check that the runtime hash is the
compatibility `E67DEE...` build rather than the original `E16BC...` build.
- The menu toggles but does not accept input: try `[Hotfix] ManualInputPolling=true` and test without
conflicting overlays.
59 changes: 53 additions & 6 deletions OptiScaler.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1553,14 +1553,38 @@ OutputResourceBarrier=auto
ToggleKey=auto
; DLSS 5 Neural Rendering. Synthesises detail in the upscaler's output, before frame generation sees it.
;
; Needs nvngx_dlssnr.dll from a driver that ships it, placed beside OptiScaler or the game executable,
; plus nvngx.dll_dlssnr.dll from this package. The snippet refuses callers whose module path does not
; contain "nvngx.dll", which is the only reason that second file exists.
; Needs nvngx_dlssnr.dll 310.8 placed beside OptiScaler or the game executable, plus
; nvngx.dll_dlssnr.dll from this package. RTX 50 uses the original NVIDIA runtime. RTX 20/30/40 needs
; ShortFuse's cross-generation compatibility runtime from the pinned RenoDX thread; see
; INSTALL-DLSSNR.md for its SHA-256. The snippet refuses callers whose module path does not contain
; "nvngx.dll", which is the only reason that second file exists.
;
; Undocumented and driven directly, so none of it is officially supported.
; true or false - Default (auto) is false
Enabled=auto

; Run Neural Rendering over the colour input immediately before Super Resolution, at render
; resolution. Ray Reconstruction remains on the supported post-upscale path. false preserves the
; normal v0.2.0 placement. Padded/offset dynamic-resolution inputs safely fall back post-upscale.
; true or false - Default (auto) is false
RunBeforeSR=auto

; D3D12 and its bridges: opt in to NR after the game's native Ray Reconstruction.
; RR performs denoising/upscaling first. This never enables RR in unsupported games.
; Defaults: false, one pass, half of RR output width/height (1080p NR at 4K output).
; RRPasses is clamped to 1..3; RRWorkingScale to 0.25..2.0.
ApplyAfterRR=auto
RRPasses=auto
RRWorkingScale=auto

; Number of sequential model layers between one encode and one final composition. Each extra layer
; consumes the preceding model output and owns a separate persistent feature/history. 1 is normal;
; 2 and 3 are deliberately over-processed and cost almost exactly 2x and 3x the model time. The final
; answer is composed once against the immutable base proxy, so colour and transfer do not compound.
; Values are clamped to 1..3.
; Default (auto) is 1
Passes=auto

; How far the frame moves toward the model's picture. Its answer is not added to the frame: it is a
; complete picture, rescaled so its luminance sits where the original says it should, and this blends
; between the two. 0 gives back exactly what the upscaler produced, 1 is the model's picture, and above
Expand Down Expand Up @@ -1610,12 +1634,35 @@ DebugView=auto
; true or false - Default (auto) is true
AutoCapture=auto

; Model controls. The preset is baked in when the model is created, so it needs a restart; the rest are
; live. Presets and styles are undocumented, and the preset scale is not the same one super resolution
; or ray reconstruction use.
; Model controls. Preset and Style are baked into each feature, so changing them causes a guarded
; feature rebuild after the current GPU work retires. The values are undocumented, and the preset
; scale is not the same one Super Resolution or Ray Reconstruction uses.
; Default (auto) is 0
Preset=auto
Style=auto

; Passes 2 and 3 inherit the pass 1 Preset/Style above when left on auto. Set an override to use a
; different built-in model profile for that layer: preset 0..3; style 0 standard, 1 natural,
; 2 cinematic. These select profiles inside the same NVIDIA model DLL, not different model files.
Pass2Preset=auto
Pass2Style=auto
Pass3Preset=auto
Pass3Style=auto

; Per-pass model strengths (DX12 multipass, including NR after RR).
; Auto inherits pass 1, except LocalTone: auto keeps the legacy later-pass value 0.
; Intensity/LocalStructure/LocalTone: 0..2; SkinStructure: -1..2 (-1 follows structure).
Pass2Intensity=auto
Pass2LocalStructure=auto
Pass2LocalTone=auto
Pass2SkinStructure=auto
Pass2AutoMask=auto
Pass3Intensity=auto
Pass3LocalStructure=auto
Pass3LocalTone=auto
Pass3SkinStructure=auto
Pass3AutoMask=auto

; Default (auto) is 1.0
Intensity=auto
LocalStructure=auto
Expand Down
44 changes: 44 additions & 0 deletions OptiScaler/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,10 @@ bool Config::Reload(std::filesystem::path iniPath)

// --- DLSS 5 Neural Rendering (OptiScaler/dlssnr) ---
DlssNrEnabled.set_from_config(readBool("DlssNr", "Enabled"));
DlssNrRunBeforeSr.set_from_config(readBool("DlssNr", "RunBeforeSR"));
DlssNrApplyAfterRR.set_from_config(readBool("DlssNr", "ApplyAfterRR"));
DlssNrRRPasses.set_from_config(readUInt("DlssNr", "RRPasses"));
DlssNrRRWorkingScale.set_from_config(readFloat("DlssNr", "RRWorkingScale"));
DlssNrToggleKey.set_from_config(readInt("DlssNr", "ToggleKey"));
DlssNrTransferStrength.set_from_config(readFloat("DlssNr", "TransferStrength"));
DlssNrColourStrength.set_from_config(readFloat("DlssNr", "ColourStrength"));
Expand Down Expand Up @@ -361,10 +365,24 @@ bool Config::Reload(std::filesystem::path iniPath)
DlssNrPreset.set_from_config(readUInt("DlssNr", "Preset"));
DlssNrIntensity.set_from_config(readFloat("DlssNr", "Intensity"));
DlssNrStyle.set_from_config(readUInt("DlssNr", "Style"));
DlssNrPass2Preset.set_from_config(readUInt("DlssNr", "Pass2Preset"));
DlssNrPass2Style.set_from_config(readUInt("DlssNr", "Pass2Style"));
DlssNrPass3Preset.set_from_config(readUInt("DlssNr", "Pass3Preset"));
DlssNrPass3Style.set_from_config(readUInt("DlssNr", "Pass3Style"));
DlssNrLocalStructure.set_from_config(readFloat("DlssNr", "LocalStructure"));
DlssNrLocalTone.set_from_config(readFloat("DlssNr", "LocalTone"));
DlssNrSkinStructure.set_from_config(readFloat("DlssNr", "SkinStructure"));
DlssNrAutoMask.set_from_config(readBool("DlssNr", "AutoMask"));
DlssNrPass2Intensity.set_from_config(readFloat("DlssNr", "Pass2Intensity"));
DlssNrPass2LocalStructure.set_from_config(readFloat("DlssNr", "Pass2LocalStructure"));
DlssNrPass2LocalTone.set_from_config(readFloat("DlssNr", "Pass2LocalTone"));
DlssNrPass2SkinStructure.set_from_config(readFloat("DlssNr", "Pass2SkinStructure"));
DlssNrPass2AutoMask.set_from_config(readBool("DlssNr", "Pass2AutoMask"));
DlssNrPass3Intensity.set_from_config(readFloat("DlssNr", "Pass3Intensity"));
DlssNrPass3LocalStructure.set_from_config(readFloat("DlssNr", "Pass3LocalStructure"));
DlssNrPass3LocalTone.set_from_config(readFloat("DlssNr", "Pass3LocalTone"));
DlssNrPass3SkinStructure.set_from_config(readFloat("DlssNr", "Pass3SkinStructure"));
DlssNrPass3AutoMask.set_from_config(readBool("DlssNr", "Pass3AutoMask"));
DlssNrReversibleMode.set_from_config(readUInt("DlssNr", "ReversibleMode"));
DlssNrApplyModel.set_from_config(readBool("DlssNr", "ApplyModel"));
DlssNrHoldFrame.set_from_config(readBool("DlssNr", "HoldFrame"));
Expand Down Expand Up @@ -1198,6 +1216,14 @@ bool Config::SaveIni()

// --- DLSS 5 Neural Rendering (OptiScaler/dlssnr) ---
ini.SetValue("DlssNr", "Enabled", GetBoolValue(Instance()->DlssNrEnabled.value_for_config()).c_str());
ini.SetValue("DlssNr", "RunBeforeSR",
GetBoolValue(Instance()->DlssNrRunBeforeSr.value_for_config()).c_str());
ini.SetValue("DlssNr", "ApplyAfterRR",
GetBoolValue(Instance()->DlssNrApplyAfterRR.value_for_config()).c_str());
ini.SetValue("DlssNr", "RRPasses",
GetIntValue(Instance()->DlssNrRRPasses.value_for_config()).c_str());
ini.SetValue("DlssNr", "RRWorkingScale",
GetFloatValue(Instance()->DlssNrRRWorkingScale.value_for_config()).c_str());
{
auto toggle = Instance()->DlssNrToggleKey.value_for_config();
ini.SetValue("DlssNr", "ToggleKey", GetIntValue(toggle, toggle > 0).c_str());
Expand Down Expand Up @@ -1250,12 +1276,30 @@ bool Config::SaveIni()
ini.SetValue("DlssNr", "Preset", GetIntValue(Instance()->DlssNrPreset.value_for_config()).c_str());
ini.SetValue("DlssNr", "Intensity", GetFloatValue(Instance()->DlssNrIntensity.value_for_config()).c_str());
ini.SetValue("DlssNr", "Style", GetIntValue(Instance()->DlssNrStyle.value_for_config()).c_str());
ini.SetValue("DlssNr", "Pass2Preset",
GetIntValue(Instance()->DlssNrPass2Preset.value_for_config()).c_str());
ini.SetValue("DlssNr", "Pass2Style",
GetIntValue(Instance()->DlssNrPass2Style.value_for_config()).c_str());
ini.SetValue("DlssNr", "Pass3Preset",
GetIntValue(Instance()->DlssNrPass3Preset.value_for_config()).c_str());
ini.SetValue("DlssNr", "Pass3Style",
GetIntValue(Instance()->DlssNrPass3Style.value_for_config()).c_str());
ini.SetValue("DlssNr", "LocalStructure",
GetFloatValue(Instance()->DlssNrLocalStructure.value_for_config()).c_str());
ini.SetValue("DlssNr", "LocalTone", GetFloatValue(Instance()->DlssNrLocalTone.value_for_config()).c_str());
ini.SetValue("DlssNr", "SkinStructure",
GetFloatValue(Instance()->DlssNrSkinStructure.value_for_config()).c_str());
ini.SetValue("DlssNr", "AutoMask", GetBoolValue(Instance()->DlssNrAutoMask.value_for_config()).c_str());
ini.SetValue("DlssNr", "Pass2Intensity", GetFloatValue(Instance()->DlssNrPass2Intensity.value_for_config()).c_str());
ini.SetValue("DlssNr", "Pass2LocalStructure", GetFloatValue(Instance()->DlssNrPass2LocalStructure.value_for_config()).c_str());
ini.SetValue("DlssNr", "Pass2LocalTone", GetFloatValue(Instance()->DlssNrPass2LocalTone.value_for_config()).c_str());
ini.SetValue("DlssNr", "Pass2SkinStructure", GetFloatValue(Instance()->DlssNrPass2SkinStructure.value_for_config()).c_str());
ini.SetValue("DlssNr", "Pass2AutoMask", GetBoolValue(Instance()->DlssNrPass2AutoMask.value_for_config()).c_str());
ini.SetValue("DlssNr", "Pass3Intensity", GetFloatValue(Instance()->DlssNrPass3Intensity.value_for_config()).c_str());
ini.SetValue("DlssNr", "Pass3LocalStructure", GetFloatValue(Instance()->DlssNrPass3LocalStructure.value_for_config()).c_str());
ini.SetValue("DlssNr", "Pass3LocalTone", GetFloatValue(Instance()->DlssNrPass3LocalTone.value_for_config()).c_str());
ini.SetValue("DlssNr", "Pass3SkinStructure", GetFloatValue(Instance()->DlssNrPass3SkinStructure.value_for_config()).c_str());
ini.SetValue("DlssNr", "Pass3AutoMask", GetBoolValue(Instance()->DlssNrPass3AutoMask.value_for_config()).c_str());
ini.SetValue("DlssNr", "ReversibleMode", GetIntValue(Instance()->DlssNrReversibleMode.value_for_config()).c_str());
ini.SetValue("DlssNr", "ApplyModel", GetBoolValue(Instance()->DlssNrApplyModel.value_for_config()).c_str());
ini.SetValue("DlssNr", "HoldFrame", GetBoolValue(Instance()->DlssNrHoldFrame.value_for_config()).c_str());
Expand Down
Loading