release: prepare 0.1.5 with RDNA2 kernels and in-game DLSS/FSR switching

- Add native preset K kernels for gfx1030–gfx1036 using software matrix
  products, with fast and accuracy builds and RDNA2 motion kernels.
- Follow OptiScaler's DLSS and RR preset overrides, sanitize unsupported
  values and select the preset matching the feature's quality mode.
- Enable FSR 4 under NVIDIA GPU spoofing by exposing real AMD PCI IDs
  to OptiScaler and FidelityFX modules; bundle AMD's loader and upscaler.
- Keep K on native kernels in engine auto mode unless HIP is selected,
  and fall back to CUDA for M/L when shader_float8 is unavailable.
- Add native-engine K GPU timings, preset recreation harness controls
  and regression coverage; fix the input-wait test's glibc name clash.
- Update configuration, installer guidance and release documentation,
  simplify the README and move gameplay videos to docs/videos.md.

RDNA2 kernels remain untested on RDNA2 hardware; software matrix
arithmetic was validated against WMMA on RDNA3.
This commit is contained in:
ayo committed 2026-10-17 16:05:09 -04:00
1 parent 34e6b0fe71
commit 0e851b7f90
24 files changed
+703 -174

No files matched your search

+36 -148
View File
@@ -3,35 +3,19 @@
want to run xess (xmx) on radeon? check out [x4r](https://github.com/countervolts/x4r)!
d4r runs NVIDIA's official DLSS Super Resolution (`nvngx_dlss.dll`) and experimental Ray Reconstruction (`nvngx_dlssd.dll`) in Windows games on AMD Radeon GPUs under Linux and Proton. The game asks for DLSS as usual; the DLSS network runs on the AMD GPU through [ZLUDA](https://github.com/vosen/ZLUDA) (CUDA on ROCm/HIP), with the heaviest Super Resolution kernels replaced by hand-written RDNA3 and RDNA4 code.
d4r runs NVIDIA's official DLSS Super Resolution (`nvngx_dlss.dll`) and experimental Ray Reconstruction (`nvngx_dlssd.dll`) in Windows games on AMD Radeon GPUs under Linux and Proton. The game asks for DLSS as usual; the DLSS network runs on the AMD GPU through [ZLUDA](https://github.com/vosen/ZLUDA) (CUDA on ROCm/HIP), with the heaviest kernels replaced by hand-written RDNA2, RDNA3 and RDNA4 code.
**Supported DLSS models:** DLSS 3 CNN (E), DLSS 4 transformer (K, default), and DLSS 4.5 transformer (M, plus experimental L). DLSS 5 is PURPOSELY not supported.
**Supported DLSS models:** DLSS 3 CNN (E), DLSS 4 transformer (K, default), and DLSS 4.5 transformer (M, plus L for Ultra Performance). Ray Reconstruction presets D, E and F are experimental ([building](docs/building.md)). DLSS 5 is PURPOSELY not supported.
Preset L is NVIDIA's DLSS 4.5 model for Ultra Performance, especially at 4K. Set `[DLSS] Model = L` in `d4r.ini` and select Ultra Performance in game or OptiScaler; selecting L alone does not change the input resolution. L shares M's native Swin layers and has its own texture-kernel build. Its unfolded input/output arithmetic remains translated, so performance and image quality need game testing; the M benchmarks below do not apply to L.
Ray Reconstruction presets D and E use the signed CUDA-capable 3.10.7 denoiser and the translated NVIDIA network, independently of the Super Resolution model. Temporal reconstruction and separate alpha output have been verified on Radeon RX 9070 XT (gfx1201) with native FP8 WMMA enabled. Newer DLSS 4.5 RR preset F is not implemented or validated. The scalar FP8 path produced nonfinite temporal RR output and is not validated; other GPU targets and image-quality parity with RTX hardware remain unverified. See [building](docs/building.md) for the required runtime, denoiser placement, and FP8 settings.
**Proof of concept:** d4r shows that DLSS can run on an AMD GPU, but it is not really that practical for everyday use yet. Super Resolution game testing primarily uses Radeon RX 7700 XT; Ray Reconstruction validation additionally uses RX 9070 XT. It depends on unreleased patches to ZLUDA and vkd3d-proton.
See [supported games](SUPPORTED_GAMES.md) for the tested games and DLSS models.
The development shim also has [experimental native Vulkan support](docs/vulkan.md).
A [standalone native inference engine](engine/README.md) runs presets K and M from weights extracted from your own
`nvngx_dlss.dll`, with d4r's own Vulkan input, network and reconstruction shaders (optionally the native HIP network
layers) and GPU-resident temporal history. The game shim uses the CUDA/ZLUDA path described below by default;
`[Engine] Enabled = true` (`D4R_ENGINE=1`) records supported K and M evaluations with the native engine into the
game's own command list instead and hands anything else to the CUDA path (see the engine's README). Presets E and L
remain CUDA-only.
**Proof of concept:** d4r shows that DLSS can run on an AMD GPU, but it is not really that practical for everyday use yet. It depends on unreleased patches to ZLUDA and vkd3d-proton. See [supported games](SUPPORTED_GAMES.md) for the tested games and models.
> **Not affiliated with NVIDIA or AMD.**
To contact me my discord is `._ayo`. or join the [d4r discord server](https://discord.gg/mgTWpSq8f5)
## Results
[DLSS Ultra Performance demo video](https://cdn.ayois.gay/dlss).
[DLSS Ultra Performance demo video](https://cdn.ayois.gay/dlss) · [more videos](docs/videos.md)
Radeon RX 7700 XT (RDNA3, gfx1101), SILENT HILL Townfall at 2560×1440, every DLSS result is presented in the frame it belongs to (no added latency).
@@ -42,162 +26,66 @@ Radeon RX 7700 XT (RDNA3, gfx1101), SILENT HILL Townfall at 2560×1440, every DL
| Performance (1280×720) | **89.7** | 84.1 | 69.0 | 94.0 |
| Ultra Performance (853×480) | 89.1 | **95.5** | 92.2 | 107.3 |
\* FSR 4 was measured in an earlier session (2026-09-27); the DLSS columns on 2026-09-29, when the same machine ran about 2–4% slower overall. Run back to back on the same day, this release is faster than 0.1.1 at Quality with every model (E 71.2 → 71.9, K 67.9 → 68.1, M 48.4 → 49.3 fps), and more in games whose DLSS settings differ from Townfall's, whose kernel variants now run natively too.
\* FSR 4 was measured on 2026-09-27, the DLSS columns on 2026-09-29, when the same machine ran about 2–4% slower overall. Native 2560×1440 without upscaling (the game's TSR at 100%) runs at 49.1 fps on the same walk. How the numbers were measured: [docs/performance.md](docs/performance.md).
For reference, native 2560×1440 without upscaling (the game's TSR at 100%) runs at 49.1 fps on the same walk.
## Switching models and FSR in game
On the same GPU DLSS starts at a disadvantage: its networks were designed for NVIDIA's tensor cores, and parts of them still run as translated NVIDIA code. How the numbers were measured, and what each optimisation contributed, is in [docs/performance.md](docs/performance.md).
Open OptiScaler's menu (Insert):
## FSR 4.1.1 vs. DLSS 4.5 comparison
- **DLSS model:** under DLSS Settings, tick *Render Presets Override*, pick the preset (E, K, L or M) and press *Apply Changes*. Unticking it returns to `[DLSS] Model` in `d4r/d4r.ini`.
- **FSR:** set *Upscaler (DX12)* to *FSR 3.X/4*. RDNA3 and RDNA4 get FSR 4.1 (needs GE-Proton, which supplies AMD's FSR 4 library); other GPUs get FSR 3.1.
- [Side-by-side comparison: FSR 4.1.1 (left) and DLSS 4.5 preset M (right)](https://cdn.ayois.gay/sk-ant-api03-4Dr0yN5YAzsuAOcyp7KEfeRyB0pbh2iAv2vfywa9QITHIbQScnVQnef9jsxJzDcvWMD13r8C1qGAoQQevjLqzGPT0bAQ9AA)
- [FSR 4.1.1 standalone video](https://cdn.ayois.gay/sk-ant-api03-YZpWqINTI2HBt6KWJTeyY2b9JJbxUTZdBVRR06KZD7b1x27QLjvQrZZX8Um5sZqkquuOlzJTHUh84FOOLsetNy-PAIiWxAA)
- [DLSS 4.5 preset M standalone video](https://cdn.ayois.gay/sk-ant-api03-0GLTr6qEYGfUKWYoZeseLdTzPslh1AvlVLdlBEUvV893bi19b7qha5wKnuQXN2tl6l4ffaRKj5warfAFDDjOlYJNYWVQ3AA)
DLSS 4.5 was using the following settings,
```
PreferAccuracy = true
NativeSwinEncoders = false
MotionVectorDilation = 2
MotionDilationThinSky = false ; opt-in thin-only sky fill; keep false for current behavior
```
Both are on by default; `[OptiScaler] FollowPresets` and `Fsr4` in `d4r.ini` turn them off.
## Known issues
- In some games using some models native upscaling can show visible artifacting.
For users who prioritize fidelity over speed, set `[Kernels] PreferAccuracy = true` in `d4r/d4r.ini` and restart the game. It defaults to `false`. This selects accuracy variants of every native kernel and restores conservative translation settings. It aims to match NVIDIA's arithmetic; 1:1 image quality against RTX DLSS is not yet proven. See [native kernel numerics](docs/native-kernels.md#numerics).
- In some games using some models native upscaling can show visible artifacting. For fidelity over speed, set `[Kernels] PreferAccuracy = true` in `d4r/d4r.ini` ([native kernel numerics](docs/native-kernels.md#numerics)).
## GPU support
d4r builds for RDNA3 and RDNA4. A newly built release compiles native DLSS 4 and 4.5 network kernels for the targets below and selects the matching set at runtime. Only the RX 7700 XT has been tested by this project on a real GPU; an external video reports DLSS 4.5 running through d4r 0.1.2 on an RX 7900 XTX. RDNA4 runtime and performance remain unverified on hardware.
!WE ARE LOOKING FOR VIDEOS OF IT WORKING, IF YOU HAVE A MENTIONED HERE AND DOESNT HAVE A VIDEO PLEASE TAKE ONE!
| GPU | Chip | FP8 math | Runtime testing |
| GPU | Chip | Native kernels | Runtime testing |
|---|---|---|---|
| RX 7700 XT, RX 7800 XT, RX 7700, Radeon PRO W7700 | gfx1101 | widened to f16 | RX 7700 XT only |
| RX 7900 GRE / XT / XTX, Radeon PRO W7800 / W7900 | gfx1100 | widened to f16 | RX 7900 XTX: [external video](https://www.youtube.com/watch?v=_GLjJ2Dn5pU) (DLSS 4.5); other cards untested |
| RX 7600 / 7600 XT, RX 7650 GRE | gfx1102 | widened to f16 | untested |
| RDNA3 integrated GPUs | gfx1103 | widened to f16 | untested |
| RX 9070 XT / 9070 / 9070 GRE, Radeon AI PRO R9700 | gfx1201 | native FP8 (`NativeFp8`, default on) | RX 9070 XT: [external video](https://www.youtube.com/watch?v=lZ0BLYqAtCs), other cards untested |
| RX 9060 XT / 9060 | gfx1200 | native FP8 (`NativeFp8`, default on) | compile only |
| RDNA2 and older | | | unsupported |
| RX 7700 XT, RX 7800 XT, RX 7700, Radeon PRO W7700 | gfx1101 | K, M, L (WMMA) | RX 7700 XT |
| RX 7900 GRE / XT / XTX, Radeon PRO W7800 / W7900 | gfx1100 | K, M, L (WMMA) | RX 7900 XTX: [external video](https://www.youtube.com/watch?v=_GLjJ2Dn5pU) |
| RX 7600 / 7600 XT, RX 7650 GRE, RDNA3 integrated GPUs | gfx1102, gfx1103 | K, M, L (WMMA) | untested |
| RX 9070 XT / 9070 / 9070 GRE, Radeon AI PRO R9700 | gfx1201 | K, M, L (WMMA, native FP8) | RX 9070 XT: [external video](https://www.youtube.com/watch?v=lZ0BLYqAtCs) |
| RX 9060 XT / 9060 | gfx1200 | K, M, L (WMMA, native FP8) | compile only |
| RX 6000 series, Steam Deck, RDNA2 integrated GPUs | gfx1030–gfx1036 | K (software matrix products) | validated on RDNA3 only |
| RDNA1 and older | | | unsupported |
Native network kernels are built for all listed gfx11/gfx12 targets. Texture kernels are compiled for each target by `d4r_emit` when supplied to the package script; RDNA4 also has a `-fp8` variant. The bridge selects the KFD GPU with the most SIMDs, avoiding an integrated GPU when a discrete GPU is present; `D4R_GPU_ARCH` overrides that choice. Missing native kernels fall back to ZLUDA and can be much slower. Earlier translated K layers produced invalid values; preserving FP16 denormal handling fixes that failure in recorded captures, but RTX image-quality parity remains unverified. Preset E does not use the native network kernels.
RDNA2 has no WMMA instructions: its K kernels do the same matrix products with `v_dot2` and DPP broadcasts, bit-identical to the WMMA build. Running that RDNA2 path on an RX 7700 XT for testing, K took 3.6 ms per 1080p frame, against 46.8 ms fully translated (what RDNA2 had before); RDNA3 itself keeps WMMA (1.48 ms). E and K are the models to use there; M and L run fully translated. Kernels missing for a GPU fall back to ZLUDA and can be much slower. Details: [docs/native-kernels.md](docs/native-kernels.md).
## How it works
```
game (D3D12) ──► OptiScaler (DLSS inputs) ──► d4r_nvngx.dll (NGX D3D12 API, tools/d4r_nvngx_shim.cpp)
│ inputs/output stay in VRAM (vkd3d-proton Vulkan interop,
│ command list split around DLSS for same-frame results)
▼
official NGX core + nvngx_dlss.dll (their CUDA path)
▼
nvcuda.dll (Wine CUDA bridge, tools/wine_nvcuda_bridge.c)
▼
ZLUDA (patches/zluda: PTX → AMDGPU, WMMA, native kernel overrides)
▼
native RDNA3 and RDNA4 kernels (kernels/: DLSS 4 and 4.5 network layers)
game (D3D12) ──► OptiScaler ──► d4r nvngx.dll (NGX D3D12 API) ──► NVIDIA's NGX core + nvngx_dlss.dll (CUDA path)
▼
native RDNA kernels ◄── ZLUDA (PTX → AMDGPU) ◄── nvcuda.dll (Wine CUDA bridge)
```
- **The shim** (`d4r_nvngx.dll`) implements the D3D12 NGX entry points OptiScaler calls, copies the game's colour, depth and motion vectors into buffers shared with HIP, evaluates DLSS through the CUDA version of NGX, and writes the result back into the game's output texture.
- **The bridge** is a Wine builtin `nvcuda.dll` that forwards the CUDA driver API to ZLUDA on the Linux side, plus a few helpers the shim needs (Vulkan memory import, asynchronous array copies, GPU-side waits).
- **ZLUDA** compiles NVIDIA's PTX for the AMD GPU. The patches add what DLSS needs (textures, surfaces, FP8 and tensor-core MMA on RDNA3/RDNA4 WMMA) and a hook that serves hand-written kernels in place of selected PTX kernels.
- **Native kernels** reimplement the DLSS 4 and 4.5 network layers for RDNA3 and RDNA4 and replace parts of a few texture-heavy kernels. The 2–3× speedup was measured on the RX 7700 XT; RDNA4 speed is unverified.
Details: [docs/architecture.md](docs/architecture.md) and [docs/native-kernels.md](docs/native-kernels.md).
The shim keeps the game's inputs and output in VRAM through vkd3d-proton's Vulkan interop; ZLUDA compiles NVIDIA's PTX and serves hand-written kernels in place of the heaviest ones. An optional [native inference engine](engine/README.md) (`[Engine]`) runs K, L and M with d4r's own Vulkan shaders. Details: [docs/architecture.md](docs/architecture.md).
## Install the release
The release zip works like an OptiScaler release: its contents go into the folder that holds the game's main `.exe`.
1. Extract `d4r-<version>.zip` into the folder that holds the game's main `.exe` (OptiScaler 0.9.4 as `dxgi.dll`, the d4r-patched vkd3d-proton, AMD's FSR files and the `d4r` folder).
2. Run `bash install.sh` there. It downloads NVIDIA's DLSS and NGX files from NVIDIA, checks their SHA-256 hashes and puts them in `d4r/`. Needs Bash, curl, sha256sum and 7-Zip; no sudo, ROCm not required.
3. In Steam, select GE-Proton 11 for the game and set the launch options `PROTON_FORCE_NVAPI=1 DXVK_NVAPI_GPU_ARCH=AD100 %command%`.
1. Extract `d4r-<version>.zip` there. It contains:
- OptiScaler 0.9.4 as `dxgi.dll`, with an `OptiScaler.ini` set up for d4r;
- the d4r-patched vkd3d-proton (`d3d12.dll`, `d3d12core.dll`);
- a `d4r` folder with the shim, the CUDA bridge, ZLUDA, the ROCm 7.2.4 runtime, the native kernels, this game's `d4r.ini`, and an `install.sh` beside the `d4r` folder.
2. Run `bash install.sh` in the extracted release folder. It downloads NVIDIA DLSS 310.7.0 and NGX from official NVIDIA sources, verifies SHA-256 hashes, and places them in `d4r/nvngx_dlss.dll` and `d4r/ngx/_nvngx.dll`. Requires Bash, curl, sha256sum and 7-Zip (`7zz`, `7z` or `7za`); no sudo. The NGX download is a roughly 915 MiB driver archive which is extracted without executing it. Matching files are skipped and differing files are backed up.
3. In Steam, select GE-Proton 11 for the game and set these launch options: `PROTON_FORCE_NVAPI=1 DXVK_NVAPI_GPU_ARCH=AD100 %command%`.
When updating, replace `d3d12.dll`, `d3d12core.dll` and `d4r/nvngx.dll` together. Already using OptiScaler? The `-asi` zip adds d4r as a plugin ([ASI guide](packaging/ASI_README.txt)). The full guide ships in the zip as [D4R_README.txt](packaging/D4R_README.txt).
When updating, replace `d3d12.dll` and `d3d12core.dll` together with `d4r/nvngx.dll`. The shim requires their matching resource-lifetime extension.
## Building from source
ROCm does not need to be installed: the zip includes its runtime (from AMD's Ubuntu 22.04 packages, which run under Steam's container runtime on any distribution). The downloaded NVIDIA files and the bundled kernels built from NVIDIA's code are not covered by this repository's license (see [NOTICE](NOTICE)). The Proton prefix and the system are not changed. [packaging/D4R_README.txt](packaging/D4R_README.txt) is the full guide that ships in the zip; `scripts/package_release.sh` builds the zip (see [docs/building.md](docs/building.md#7-package-a-release)).
### Existing OptiScaler: ASI add-on
The optional `d4r-<version>-asi.zip` contains only `d4r.asi` and a companion
`d4r/` folder. Drop both into your existing OptiScaler plugin folder, then run
`bash install.sh` inside that `d4r/` folder once for NVIDIA's DLLs. Keep the Steam
launch options above and ensure `[Plugins] LoadAsiPlugins=true` is already enabled.
On the first game launch, the ASI starts its native setup helper. Close the game
at the setup message; the helper installs the D3D12 wrapper and private core beside the EXE
and configures OptiScaler after the game exits, with backups. Launch again when
the ready message appears. Unrelated settings, other plugins and the existing
OptiScaler loader are preserved. The rest of the runtime stays beside the ASI
in its companion folder. No Python or configuration command is needed.
Use OptiScaler 0.9.4 through its game-local proxy or GE-Proton's built-in loader;
setup finds the active loader's INI even when it lives in the prefix. OptiScaler's
`nvngx.dll` mode does not load ASIs. See [the ASI installation guide](packaging/ASI_README.txt)
for custom plugin paths, updating and removal. ASI loading still needs game testing.
## Requirements (building from source)
- Linux with an AMD RDNA3 or RDNA4 GPU. The native kernels use gfx11 or gfx12 WMMA. `scripts/package_release.sh` builds network kernels for gfx1100–gfx1103 and gfx1200–gfx1201 by default; `D4R_GPU_ARCH` selects one target for a standalone `kernels/build.sh` invocation. Runtime support outside the RX 7700 XT remains unverified on hardware (see [GPU support](#gpu-support)).
- ROCm with HIP and its clang (tested with ROCm 7.2).
- GE-Proton with OptiScaler integration (tested with GE-Proton11-3).
- Build tools: a Rust toolchain and git-lfs (ZLUDA), meson and ninja (vkd3d-proton), `winegcc`/`winebuild` (bridge), `x86_64-w64-mingw32-g++` and `clang-cl` (shim), Python 3.
- NVIDIA's `nvngx_dlss.dll` 310.7 and a matching NGX core `_nvngx.dll`.
## Build and run
The full sequence is in [docs/building.md](docs/building.md). In short:
1. Build ZLUDA `ee2f25a` with `patches/zluda/0002`–`0013` applied, including its `d4r_emit` example for offline texture builds.
2. Build the patched vkd3d-proton: `scripts/build_vkd3d_proton_d4r.sh OUT_DIR`.
3. Build the shim and bridge: `scripts/build_d4r_nvngx_shim.sh`, `scripts/build_wine_nvcuda_bridge.sh`.
4. Stage the runtime with your NVIDIA files: `scripts/install_d4r_runtime.sh _nvngx.dll nvngx_dlss.dll`.
5. Build the native kernels: `D4R_ROCM_DIR=… D4R_DLSS_DLL=… D4R_ZLUDA_EMIT=… kernels/build.sh`.
6. Fill in `~/.config/d4r/d4r.ini` (created from `config/d4r.ini.default` on first launch) and start the game with `scripts/d4r_play.sh`.
The launcher backs up and restores everything it touches in the Proton prefix: OptiScaler.ini, the prefix's `d3d12.dll`/`d3d12core.dll`, and the game's Engine.ini when cvars are set.
## Preset videos
Gameplay recordings of each DLSS model at the Quality, Performance and Ultra Performance presets.
**Ready or Not**
| Model | Quality | Performance |
|---|---|---|
| DLSS 4.5 | [video](https://cdn.ayois.gay/sk-ant-api03-OPG1vqqsmDkNduG5fXJBelDI-1APyL9_ytJrnOSekZxoh-2NL8NqNuqL3pCI24kJQsudaB896GYpu5TFv93tabAgK1H2sAA) | [video](https://cdn.ayois.gay/sk-ant-api03-d-4XS1it5mtMkm-QRz0Qnkfu9FAjz_dFJURvsh_3gPZTnC1sljpWc28F6fyjUWIC6xlZm-FckWGjaP-KuIipYmroa3dQ8AA) |
| DLSS 4 | [video](https://cdn.ayois.gay/sk-ant-api03-uXTWm4hN5jZksiSm4fvgIOr0dw43hGJ0TpZLe2ZghIbGb7aR9CGGlTV-1paBYO7G6lzIf83tOrCdaKdJerJ4re6epRWDEAA) | [video](https://cdn.ayois.gay/sk-ant-api03-fcZCkctdFOv513z1u6rPBrRseY47Qnw43Gnv5mZf_GekHw-yUoWYcBixjp2qqqbBIf7sVmht0Y676U96n2cwmFIsRgWkHAA) |
| DLSS 3 | [video](https://cdn.ayois.gay/sk-ant-api03-_vuywLcsMzZiJxcNgucBeeXFzWi44mes5A1777bg_4XQ2X0MH22J9M7JFpTS0_q6yaGWMXf3FvL8MDiv_dc7UANxrCmlGAA) | [video](https://cdn.ayois.gay/sk-ant-api03-WzaIy34WIesQeOOTVQNXZwpPPE8khmbUynvMw9B-Luj8GFcZ_sJvGXfg6aW00TD0Rr3nWu43ELJGx2K6qs_HL6FkLdmb1AA) |
**Townfall**
| Model | Quality | Ultra Performance |
|---|---|---|
| DLSS 4.5 | [video](https://cdn.ayois.gay/sk-ant-api03-p8uL0hOUklvA-BSeEafS8LQU-gayzbqAE3NacWAmckLoXOSf2sXGf7OeG1LmvOqeGFRh_ceCWi4pLBl9yxJlvYwQWH3Y1AA) | [video](https://cdn.ayois.gay/sk-ant-api03-Mz0BzYTrLjaYSuQXtOEHTw4ydYVbPvHmMjflm8UhFp976ftXqMExD8BCMYlo93JLEGwVL3rMWoZR0CH_deJJU7jX8_s3GAA) |
| DLSS 4 | [video](https://cdn.ayois.gay/sk-ant-api03-WgNoUPTwH0vt55Ag7RaWprXXINU5OOOEtuDXOvIjHgfAScSiIPIJGVaERoF-EA3JO7LS8xkohUlb6AFsW8AVZ2vMB14JwAA) | [video](https://cdn.ayois.gay/sk-ant-api03-skXZVD6G4DUdAfQw0RLz_UJW8GOfBlpZx1N4ezaF_UIXm7OIY1ciQysfRu4zRsxSXA8plZJi4NSzkXkV7Oxmg9ytg9zaaAA) |
## Repository layout
Linux with an RDNA2, RDNA3 or RDNA4 GPU, ROCm 7.2 with HIP and its clang, GE-Proton 11, a Rust toolchain, meson/ninja, `winegcc`, `x86_64-w64-mingw32-g++`, `clang-cl` and Python 3. The full sequence is in [docs/building.md](docs/building.md); `scripts/package_release.sh` builds the release zip.
| Path | Contents |
|---|---|
| `tools/` | the NGX shim, the Wine CUDA bridge, a D3D12 DLSS harness, probes and kernel replay tools |
| `kernels/` | native RDNA3/RDNA4 kernels (`k/` DLSS 4, `m/` DLSS 4.5, `tex/` texture-kernel parts, `common/` WMMA layouts), their build script, validation tools and numpy reference models |
| `engine/` | standalone weight-based preset K and M inference runtime (Vulkan shaders, optional native HIP network), model packaging and full-frame validation |
| `patches/` | ZLUDA and vkd3d-proton patches |
| `scripts/` | build, install, launch and probe scripts |
| `config/` | the default `d4r.ini` for the developer launcher |
| `packaging/` | the release's `d4r.ini`, OptiScaler settings, user guide and install check |
| `docs/` | architecture, build, native kernel and performance notes |
| `tools/` | the NGX shim, the Wine CUDA bridge, a D3D12 DLSS harness, probes |
| `kernels/` | native kernels (`k/` DLSS 4, `m/` DLSS 4.5, `tex/` texture tails, `common/` matrix layouts) and their tools |
| `engine/` | the native K/L/M inference engine |
| `patches/` | ZLUDA, vkd3d-proton and OptiScaler patches |
| `scripts/`, `config/`, `packaging/`, `docs/` | build/launch scripts, the developer `d4r.ini`, release files, documentation |
## Star History
@@ -211,4 +99,4 @@ Gameplay recordings of each DLSS model at the Quality, Performance and Ultra Per
## License
GNU General Public License, version 3 only (GPL-3.0-only; see [LICENSE](LICENSE)). The patches in `patches/` are offered under the licenses of the projects they modify: ZLUDA (Apache-2.0 or MIT) and vkd3d-proton (LGPL-2.1). See [NOTICE](NOTICE).
GNU General Public License, version 3 only (GPL-3.0-only; see [LICENSE](LICENSE)). The patches in `patches/` are offered under the licenses of the projects they modify: ZLUDA (Apache-2.0 or MIT) and vkd3d-proton (LGPL-2.1). See [NOTICE](NOTICE). The downloaded NVIDIA files and the kernels built from NVIDIA's code are not covered by this license.
+8
View File
@@ -179,6 +179,14 @@ EngineIni =
Cvars =
[OptiScaler]
; Switching in game from OptiScaler's menu (Insert):
; - DLSS model: DLSS Settings > tick Render Presets Override, pick the preset (E, K, L or M), Apply Changes.
; Unticking it goes back to [DLSS] Model, as do presets left at DEFAULT. false always uses [DLSS] Model.
FollowPresets = true
; - FSR: Upscalers > Upscaler (DX12) > FSR 3.X/4. FSR 4.1 needs an RDNA3/RDNA4 GPU and GE-Proton (which provides
; AMD's FSR 4 library); other GPUs get FSR 3.1. true lets OptiScaler and AMD's FSR see the real GPU (the game
; still sees an NVIDIA one).
Fsr4 = true
; OptiScaler's own FPS overlay (top left)
FpsOverlay = true
; 0 = simple ... 3 = detailed with frame-time graph
+3 -1
View File
@@ -263,7 +263,9 @@ kernels/build.sh all DIR # everything into DIR (default kernels/out/native)
D4R_PREFER_ACCURACY=1 kernels/build.sh all DIR/accuracy # conservative versions of every family
```
`D4R_GPU_ARCH` selects one target for this standalone build (default gfx1101). The release script builds the network layers for gfx1100–gfx1103 and gfx1200–gfx1201 by default and puts each set in its own directory. The network kernels need gfx11 or gfx12 WMMA. Texture-kernel builds use `D4R_ZLUDA_EMIT` to generate code objects for the selected target without that GPU. Set `D4R_NATIVE_FP8=1` on gfx12 for the matching FP8 M and texture variants.
`D4R_GPU_ARCH` selects one target for this standalone build (default gfx1101). The release script builds the network layers for gfx1100–gfx1103 and gfx1200–gfx1201 by default and puts each set in its own directory, plus K's layers for the RDNA2 targets gfx1030–gfx1036. The network kernels use gfx11 or gfx12 WMMA through `kernels/common/wmma_layout.h`.
RDNA2 has no WMMA instruction. On gfx103x (or with `D4R_WMMA_SOFT=1` on an RDNA3 target, for validation) `wm_mma` keeps the gfx11 register layout and does each 16×16×16 product with 64 `v_dot2c_f32_f16` instructions whose `row_share` DPP broadcast supplies the A row, after one `row_ror` per A dword that offsets half 1 by a row. Every accumulator sums its K pairs in the WMMA order: on the RX 7700 XT the result is bit-identical to the WMMA instruction for random 64-step chains (denormals included) and for K's whole network over a moving 24-frame harness sequence. There K takes 3.6 ms per 1080p frame against 1.48 ms with WMMA and 46.8 ms fully translated without WMMA (ZLUDA's dot-product fallback, what RDNA2 ran before). M's layers and the texture tails still call WMMA directly, so RDNA2 builds only K; LLVM does not fold a `v_mov_b32_dpp` into `v_dot2c`, so the fused instruction is inline assembly. Texture-kernel builds use `D4R_ZLUDA_EMIT` to generate code objects for the selected target without that GPU. Set `D4R_NATIVE_FP8=1` on gfx12 for the matching FP8 M and texture variants.
The release includes both fast and accuracy network sets. Full releases also include accuracy texture sets: supply them in `D4R_BUNDLE_TEX/accuracy/<target>` or set `D4R_ZLUDA_EMIT` so packaging builds them. Build accuracy sets into a separate directory; the builder refuses to mark a directory containing unmarked older binaries as accurate.
+37
View File
@@ -0,0 +1,37 @@
# Videos
## FSR 4.1.1 vs. DLSS 4.5 comparison
- [Side-by-side comparison: FSR 4.1.1 (left) and DLSS 4.5 preset M (right)](https://cdn.ayois.gay/sk-ant-api03-4Dr0yN5YAzsuAOcyp7KEfeRyB0pbh2iAv2vfywa9QITHIbQScnVQnef9jsxJzDcvWMD13r8C1qGAoQQevjLqzGPT0bAQ9AA)
- [FSR 4.1.1 standalone video](https://cdn.ayois.gay/sk-ant-api03-YZpWqINTI2HBt6KWJTeyY2b9JJbxUTZdBVRR06KZD7b1x27QLjvQrZZX8Um5sZqkquuOlzJTHUh84FOOLsetNy-PAIiWxAA)
- [DLSS 4.5 preset M standalone video](https://cdn.ayois.gay/sk-ant-api03-0GLTr6qEYGfUKWYoZeseLdTzPslh1AvlVLdlBEUvV893bi19b7qha5wKnuQXN2tl6l4ffaRKj5warfAFDDjOlYJNYWVQ3AA)
DLSS 4.5 was using the following settings,
```
PreferAccuracy = true
NativeSwinEncoders = false
MotionVectorDilation = 2
MotionDilationThinSky = false ; opt-in thin-only sky fill; keep false for current behavior
```
## Preset videos
Gameplay recordings of each DLSS model at the Quality, Performance and Ultra Performance presets.
**Ready or Not**
| Model | Quality | Performance |
|---|---|---|
| DLSS 4.5 | [video](https://cdn.ayois.gay/sk-ant-api03-OPG1vqqsmDkNduG5fXJBelDI-1APyL9_ytJrnOSekZxoh-2NL8NqNuqL3pCI24kJQsudaB896GYpu5TFv93tabAgK1H2sAA) | [video](https://cdn.ayois.gay/sk-ant-api03-d-4XS1it5mtMkm-QRz0Qnkfu9FAjz_dFJURvsh_3gPZTnC1sljpWc28F6fyjUWIC6xlZm-FckWGjaP-KuIipYmroa3dQ8AA) |
| DLSS 4 | [video](https://cdn.ayois.gay/sk-ant-api03-uXTWm4hN5jZksiSm4fvgIOr0dw43hGJ0TpZLe2ZghIbGb7aR9CGGlTV-1paBYO7G6lzIf83tOrCdaKdJerJ4re6epRWDEAA) | [video](https://cdn.ayois.gay/sk-ant-api03-fcZCkctdFOv513z1u6rPBrRseY47Qnw43Gnv5mZf_GekHw-yUoWYcBixjp2qqqbBIf7sVmht0Y676U96n2cwmFIsRgWkHAA) |
| DLSS 3 | [video](https://cdn.ayois.gay/sk-ant-api03-_vuywLcsMzZiJxcNgucBeeXFzWi44mes5A1777bg_4XQ2X0MH22J9M7JFpTS0_q6yaGWMXf3FvL8MDiv_dc7UANxrCmlGAA) | [video](https://cdn.ayois.gay/sk-ant-api03-WzaIy34WIesQeOOTVQNXZwpPPE8khmbUynvMw9B-Luj8GFcZ_sJvGXfg6aW00TD0Rr3nWu43ELJGx2K6qs_HL6FkLdmb1AA) |
**Townfall**
| Model | Quality | Ultra Performance |
|---|---|---|
| DLSS 4.5 | [video](https://cdn.ayois.gay/sk-ant-api03-p8uL0hOUklvA-BSeEafS8LQU-gayzbqAE3NacWAmckLoXOSf2sXGf7OeG1LmvOqeGFRh_ceCWi4pLBl9yxJlvYwQWH3Y1AA) | [video](https://cdn.ayois.gay/sk-ant-api03-Mz0BzYTrLjaYSuQXtOEHTw4ydYVbPvHmMjflm8UhFp976ftXqMExD8BCMYlo93JLEGwVL3rMWoZR0CH_deJJU7jX8_s3GAA) |
| DLSS 4 | [video](https://cdn.ayois.gay/sk-ant-api03-WgNoUPTwH0vt55Ag7RaWprXXINU5OOOEtuDXOvIjHgfAScSiIPIJGVaERoF-EA3JO7LS8xkohUlb6AFsW8AVZ2vMB14JwAA) | [video](https://cdn.ayois.gay/sk-ant-api03-skXZVD6G4DUdAfQw0RLz_UJW8GOfBlpZx1N4ezaF_UIXm7OIY1ciQysfRu4zRsxSXA8plZJi4NSzkXkV7Oxmg9ytg9zaaAA) |
+3 -2
View File
@@ -24,7 +24,7 @@ usage() {
'1. Downloads official NVIDIA DLSS 310.7.0 (Super Resolution), DLSS Ray Reconstruction 310.9.1 and NGX from' \
' driver 596.36 (~1 GiB, once: verified copies are kept in ~/.cache/d4r/nvidia).' \
'2. Builds the native engine'"'"'s models from those DLLs into d4r/engine (once per DLL set and builder version:' \
' ~/.cache/d4r/engine-models). The engine is the faster path; d4r.ini [Engine] Enabled = auto uses it.' \
' ~/.cache/d4r/engine-models). Faster on RDNA4; d4r.ini [Engine] Enabled = auto uses it.' \
'Requires Bash, curl, sha256sum, 7zz/7z/7za, python3 with numpy, and GE-Proton 11 in Steam (or' \
'D4R_PROTON=/path/to/GE-Proton11-3). No sudo needed. NVIDIA license terms apply. D4R_CACHE_DIR moves the cache.'
}
@@ -188,12 +188,13 @@ if [[ "$DO_ENGINE" == ask ]] && ! interactive; then DO_ENGINE=1; fi
if [[ "$DO_ENGINE" == ask ]]; then
echo
echo "Set up d4r's own DLSS engine (recommended)?"
echo ' + Faster: DLSS 4 / 4.5 and Ray Reconstruction run on d4r'"'"'s Vulkan engine instead of NVIDIA'"'"'s kernels'
echo ' + Faster on RDNA4: DLSS 4 / 4.5 and Ray Reconstruction run on d4r'"'"'s Vulkan engine instead of NVIDIA'"'"'s kernels'
echo ' through CUDA emulation (for example Ray Reconstruction at 720p -> 4K in about 3.6 ms per frame).'
echo ' + Lower latency: the game shows the upscaled result of the frame it just rendered, not an older one.'
echo ' + No minute-long freeze while DLSS'"'"'s CUDA kernels compile the first time a model is used.'
echo ' - Longer setup, once: building its models takes a minute or two (it renders a test frame per model'
echo ' through GE-Proton) and needs ~300 MB of temporary space. Later installs reuse the models.'
echo ' - Slower for K and M on RDNA3; there d4r keeps those two models on its normal path.'
echo ' Without it, d4r uses its normal path; anything the engine does not cover uses that path anyway.'
if ask 'Set up the engine now?' y; then DO_ENGINE=1; else DO_ENGINE=0; fi
fi
+6 -1
View File
@@ -10,6 +10,8 @@
# D4R_NATIVE_FP8 1 (RDNA4 only): the variant for ZLUDA's native FP8 WMMA (d4r.ini [Kernels] NativeFp8):
# M's layers on FP8 WMMA with NVIDIA's e4m3 activations, texture tails matching ZLUDA's
# D4R_ZLUDA_WMMA_FP8_NATIVE lowering. K is the same in both variants.
# D4R_WMMA_SOFT 1: RDNA2's software matrix products (kernels/common/wmma_layout.h) on an RDNA3 target, to
# validate and time the RDNA2 kernels on RDNA3 hardware (gfx103x targets always use them)
# D4R_PREFER_ACCURACY 1: preserve FP8 quantization, per-MMA f16 rounding and denormal handling;
# build a separate set (default output: kernels/out/native/accuracy).
# Texture kernels (tex) are NVIDIA's PTX with parts replaced, so they additionally need:
@@ -35,7 +37,9 @@ FP8="${D4R_NATIVE_FP8:-0}"
case "$ARCH" in
gfx11[0-9][0-9]) [[ "$FP8" == 1 ]] && { echo "D4R_NATIVE_FP8 needs an RDNA4 (gfx12) target" >&2; exit 2; } ;;
gfx12[0-9][0-9]) ;;
*) echo "unsupported D4R_GPU_ARCH $ARCH (RDNA3 gfx110x or RDNA4 gfx120x)" >&2; exit 2 ;;
# RDNA2: no WMMA; kernels/common/wmma_layout.h multiplies in software (D4R_WMMA_SOFT)
gfx103[0-9]) [[ "$FP8" == 1 ]] && { echo "D4R_NATIVE_FP8 needs an RDNA4 (gfx12) target" >&2; exit 2; } ;;
*) echo "unsupported D4R_GPU_ARCH $ARCH (RDNA2 gfx103x, RDNA3 gfx110x or RDNA4 gfx120x)" >&2; exit 2 ;;
esac
CLANG="$ROCM/lib/llvm/bin/clang++"
[[ -x "$CLANG" ]] || { echo "clang++ not found in $ROCM/lib/llvm/bin (set D4R_ROCM_DIR)" >&2; exit 2; }
@@ -65,6 +69,7 @@ build_hip() {
fi
done
[[ "$ACCURACY" == 1 ]] && extra+=" -DD4R_ACCURACY"
[[ "${D4R_WMMA_SOFT:-0}" == 1 ]] && extra+=" -DD4R_WMMA_SOFT"
# shellcheck disable=SC2086
(cd "$tmp" && "$CLANG" -x hip --offload-arch="$ARCH" --offload-device-only -O3 $flags $extra -I"$ROCM/include" \
--rocm-path="$ROCM" --rocm-device-lib-path="$ROCM/amdgcn/bitcode" -I"$(dirname "$src")" \
+56
View File
@@ -32,6 +32,17 @@
#if D4R_WMMA_LAYOUT == 11 && defined(__GFX12__)
#error "the gfx11 WMMA layout does not exist on gfx12"
#endif
// RDNA2 (gfx10.3): no WMMA instruction; the gfx11 layout with the products done in software (wm_mma below).
// The gfx11 true16 assembly (v_cvt_f16_f32 %0.l/.h) does not exist there either; the C conversions replace it.
#if defined(__GFX10__) && !defined(D4R_WMMA_SOFT)
#define D4R_WMMA_SOFT 1
#endif
#ifdef __GFX10__
#undef WM_CVT_ASM
#endif
#if defined(__GFX10__) && D4R_WMMA_LAYOUT != 11
#error "RDNA2 kernels use the gfx11 WMMA layout"
#endif
#define WM_FN __attribute__((device, always_inline)) static inline
@@ -324,10 +335,12 @@ WM_FN wm_f8v wm_mma(const wm_op& a, const wm_op& b, wm_f8v c)
return __builtin_amdgcn_wmma_f32_16x16x16_f16_w32_gfx12(__builtin_bit_cast(wm_h8, a), __builtin_bit_cast(wm_h8, b), c);
}
#else
#ifndef __GFX10__
WM_FN wm_f8v wm_mma11(const wm_u8v& a, const wm_u8v& b, wm_f8v c)
{
return __builtin_amdgcn_wmma_f32_16x16x16_f16_w32(__builtin_bit_cast(wm_h16, a), __builtin_bit_cast(wm_h16, b), c);
}
#endif
#if D4R_WMMA_LAYOUT == 12
// layout shim: gfx12 operand halves -> one gfx11 operand in K order (half 0's 8 K, then half 1's)
WM_FN wm_u8v wm_shim_operand(const wm_u4v& v)
@@ -404,6 +417,49 @@ WM_FN wm_f8v wm_mma_zluda(const wm_op& a, const wm_op& b, wm_f8v c)
{
return wm_shim_acc_to12(wm_mma11(wm_shim_operand_zluda(a), wm_shim_operand_zluda(b), wm_shim_acc_to11(c)));
}
#elif defined(D4R_WMMA_SOFT)
// RDNA2 (gfx103x) has no WMMA: the same gfx11 register layout, multiplied with v_dot2_f32_f16. Lane (r, h) holds
// column r of B and needs rows 2i + h of A, which lanes 2i + h (any half) hold. Half 1's A is rotated by one row
// first (row_ror:15 on DPP row 1 only), so that one row_share:2i broadcast gives half 0 row 2i and half 1 row
// 2i + 1. 8 + 64 DPP moves and 64 dot2 per 16x16x16 product, f32 accumulation in K order (two products per step).
// D4R_WMMA_SOFT on gfx11 runs this path on RDNA3 hardware for validation: there it is bit-identical to the WMMA
// instruction (random 64-step chains with denormals, and K's whole network over a moving 24-frame sequence).
WM_FN uint32_t wm_dpp_rot_half1(uint32_t v)
{
// DPP control 0x12f = row_ror:15 (lane j of a 16-lane row reads lane (j + 1) & 15); row mask 2 = half 1 only
return (uint32_t)__builtin_amdgcn_update_dpp((int)v, (int)v, 0x12f, 0x2, 0xf, false);
}
// One dot2 per A row with the row_share broadcast folded into the instruction (VOP2 DPP: v_dot2c_f32_f16 on gfx10,
// v_dot2acc_f32_f16 on gfx11). LLVM's DPP combiner does not fold a v_mov_b32_dpp into these, and a separate move
// per row doubled the instruction count and kept 64 broadcast values live (spills). gfx10 and gfx11 resolve the
// VALU -> DPP read dependency in hardware, so no wait states are needed around the assembly.
#ifdef __GFX10__
#define WM_DOT2_DPP "v_dot2c_f32_f16_dpp"
#else
#define WM_DOT2_DPP "v_dot2acc_f32_f16_dpp"
#endif
#define WM_SOFT_ROW(I, LANE) \
asm(WM_DOT2_DPP " %0, %1, %2 row_share:" #LANE " row_mask:0xf bank_mask:0xf" : "+v"(c[I]) : "v"(x), "v"(column))
WM_FN wm_f8v wm_mma(const wm_op& a, const wm_op& b, wm_f8v c)
{
// Each accumulator sums its K pairs in order 0..7, as the WMMA instruction does on gfx11 (bit-identical there).
#pragma unroll
for (int d = 0; d < 8; ++d)
{
const uint32_t x = wm_dpp_rot_half1(a[d]);
const uint32_t column = b[d];
WM_SOFT_ROW(0, 0);
WM_SOFT_ROW(1, 2);
WM_SOFT_ROW(2, 4);
WM_SOFT_ROW(3, 6);
WM_SOFT_ROW(4, 8);
WM_SOFT_ROW(5, 10);
WM_SOFT_ROW(6, 12);
WM_SOFT_ROW(7, 14);
}
return c;
}
#undef WM_SOFT_ROW
#else
WM_FN wm_f8v wm_mma(const wm_op& a, const wm_op& b, wm_f8v c)
{
+5
View File
@@ -56,6 +56,11 @@ __device__ __forceinline__ uint32_t lane_id()
return __builtin_amdgcn_mbcnt_lo(~0u, 0u);
}
// RDNA2: the f16-accumulator WMMA variants need the instruction; the f32 path (software products) serves them
#ifdef __GFX10__
#undef PWIN_NATIVE_F16ACC
#undef PWIN_F16ACC_NATIVE
#endif
typedef wm_op op_t; // WMMA operand: u8v (gfx11) or u4v (gfx12)
// one k16 step with the f16 accumulator of NVIDIA's f16 wmma (rounded after the step)
+16 -5
View File
@@ -1,12 +1,12 @@
Modified in this fork for CUDA Ray Reconstruction support and validation (2026).
d4r @VERSION@: NVIDIA DLSS on AMD Radeon RDNA3 and RDNA4 under Linux and Proton
=====================================================================
d4r @VERSION@: NVIDIA DLSS on AMD Radeon RDNA2, RDNA3 and RDNA4 under Linux and Proton
=====================================================================================
d4r runs NVIDIA's own DLSS Super Resolution (DLSS 4 by default), plus experimental Ray Reconstruction,
on AMD Radeon GPUs in DirectX 12 games under Proton. OptiScaler (included) catches the game's DLSS calls
and hands them to d4r. d4r runs NVIDIA's DLSS through ZLUDA; the heaviest Super Resolution kernels are
replaced by kernels written for RDNA3 and RDNA4.
replaced by kernels written for RDNA2, RDNA3 and RDNA4.
This is an early release. Super Resolution game testing primarily uses Radeon RX 7700 XT.
Ray Reconstruction presets D/E, temporal history, and separate alpha were validated on RX 9070 XT
@@ -16,11 +16,15 @@ SUPPORTED_GAMES.md in the source repository lists per-game setup notes. Expect p
What you need
-------------
- An AMD RDNA3 or RDNA4 GPU. This build includes native DLSS 4 and 4.5 network kernels for
- An AMD RDNA2, RDNA3 or RDNA4 GPU. This build includes native DLSS 4 and 4.5 network kernels for
gfx1100–gfx1103 and gfx1200–gfx1201 unless the builder selected fewer targets. Only the RX 7700 XT
(gfx1101) has been tested on a real GPU. gfx1201 has emulator testing; gfx1200 has compile testing
only. Neither establishes RDNA4 runtime support, performance or driver stability. Texture-kernel
optimisations are included only for targets supplied by the builder.
RDNA2 (RX 6000 series, Steam Deck, RDNA2 integrated GPUs; gfx1030–gfx1036) has no WMMA instructions:
this build gives it native DLSS 4 (K) layers that do the matrix products in software, bit-identical
to the WMMA kernels when run on RDNA3. Use K or E there; M and L run fully translated (slow). RDNA2
itself has not been tested on a real GPU.
- Linux with the amdgpu kernel driver (/dev/kfd). ROCm itself is not needed: the zip includes the
ROCm 7.2.4 runtime in d4r/rocm. RocmDir in d4r/d4r.ini selects another ROCm installation instead.
- GE-Proton 11 (tested: GE-Proton11-3), selected for the game in Steam.
@@ -46,7 +50,8 @@ that archive's d4r/README.txt: setup runs automatically when the ASI loads.
1. Extract everything in this zip into the folder that holds the game's main .exe. For Unreal
Engine games that is <game>/<Project>/Binaries/Win64/, next to <Project>-Win64-Shipping.exe.
You get dxgi.dll, OptiScaler.ini, d3d12.dll, d3d12core.dll, this file and a d4r folder.
You get dxgi.dll, OptiScaler.ini, d3d12.dll, d3d12core.dll, AMD's amd_fidelityfx_dx12.dll and
amd_fidelityfx_upscaler_dx12.dll (OptiScaler's FSR option), this file and a d4r folder.
When updating, replace both d3d12 DLLs with the shim: it requires their resource-lifetime extension.
If the folder already has a dxgi.dll or OptiScaler.ini (another OptiScaler install), move those
out of the way first. Also remove PROTON_USE_OPTISCALER from the game's launch options if you
@@ -122,6 +127,12 @@ Settings
@full there, d4r uses the normal path.
@clean Build the models with the tools in d4r's source repository (engine/README.md); until they are
@clean there, d4r uses the normal path.
- Switching in game, from OptiScaler's menu (Insert):
- DLSS model: DLSS Settings > tick Render Presets Override, pick the preset (E, K, L or M) and press
Apply Changes. Unticking it goes back to Model above. ([OptiScaler] FollowPresets = false ignores it.)
- FSR: set Upscaler (DX12) to FSR 3.X/4. RDNA3 and RDNA4 get FSR 4.1 from GE-Proton's AMD FSR 4
library; other GPUs get FSR 3.1. d4r lets OptiScaler and AMD's FSR see the real GPU while the game
still sees an NVIDIA one ([OptiScaler] Fsr4 = false turns that off).
- OptiScaler.ini: OptiScaler's settings, such as its fps overlay and the render resolution of each
quality mode. Press Insert in game for OptiScaler's menu.
[Libraries] NvngxPath=d4r\nvngx.dll explicitly selects d4r's shim; OptiDllPath=d4r is also
+6 -2
View File
@@ -1,8 +1,12 @@
<!-- Modified in this fork for CUDA Ray Reconstruction support and validation (2026). -->
# Unreleased
# 0.1.5
- Switch the DLSS model in game from OptiScaler's menu: DLSS Settings > Render Presets Override, pick E, K, L or M, Apply Changes. A preset chosen there (or `RenderPresetOverride` in OptiScaler.ini) now wins over `[DLSS] Model`, which still fills quality modes left at Default and applies again when the override is unticked (`[OptiScaler] FollowPresets`, default true). The shim now also uses the preset of the feature's own quality mode instead of always the Quality slot.
- Switch to FSR from the same menu (Upscaler (DX12) > FSR 3.X/4): the zip ships AMD's FidelityFX loader and FSR 4.1.1 upscaler from the OptiScaler release, and the shim lets OptiScaler and AMD's FidelityFX modules read the GPU's real PCI ids (from sysfs) while the game, vkd3d-proton and NVIDIA's libraries keep the NVIDIA identity (`[OptiScaler] Fsr4`, default true). Under the NVIDIA identity OptiScaler saw no RDNA GPU and FSR 4.1.1 produced FSR 3.1's output byte for byte; now RDNA3 and RDNA4 get FSR 4.1 (through GE-Proton's FSR 4 library; the harness output is byte-identical to FSR 4 without the spoof) and other GPUs FSR 3.1. DLSS output is unchanged.
- RDNA2 (gfx1030–gfx1036: RX 6000 series, Steam Deck, RDNA2 integrated GPUs): native DLSS 4 (K) layers. RDNA2 has no WMMA, so `kernels/common/wmma_layout.h` does each 16×16×16 product with `v_dot2c_f32_f16` and a `row_share` DPP broadcast folded into the instruction, in the gfx11 register layout. On an RX 7700 XT this path is bit-identical to the WMMA instruction (random chains and K's whole network over 24 frames) and runs K in 3.6 ms per 1080p frame (WMMA: 1.48 ms; fully translated without WMMA, which RDNA2 got before: 46.8 ms; RDNA2 also keeps K's output tail translated, about +0.7 ms on RDNA3). E runs translated, as on every GPU; M and L are translated only on RDNA2. Not yet run on RDNA2 hardware.
- `install.sh` sets up the native engine: after the NVIDIA DLLs (now also DLSS Ray Reconstruction 310.9.1) it offers to build the engine's models on the user's machine (`d4r/tools/build-engine-models.sh`): Ray Reconstruction D, E and F straight from `nvngx_dlssd.dll`, Super Resolution K, M and L (and their LDR variants) from one test frame per model rendered through d4r under GE-Proton. The verified DLLs and the built models are kept in `~/.cache/d4r`, so the next game's install neither downloads nor builds again. Questions replace the old command-line choices (`--help` lists options for scripted installs).
- `[Engine] Enabled = auto` is the new default: the engine (the faster path) runs whenever `d4r/engine` holds its models, with the fastest model builds; anything it cannot run still uses the CUDA backend.
- `[Engine] Enabled = auto` is the new default: the engine runs whenever `d4r/engine` holds its models, with the fastest model builds; it is faster on RDNA4. Anything it cannot run still uses the CUDA backend.
- Not in this build: the experimental AMD FSR Ray Regeneration backend for Ray Reconstruction (`[RayReconstruction] Backend = fsr`) that an earlier nightly carried. Its code is not part of d4r's sources; Ray Reconstruction runs on NVIDIA's DLSS denoiser (engine or CUDA backend).
- ASI setup ignores Proton accessibility helpers and other background utilities, and serializes setup per plugin installation. Its D3D12 configuration uses regular Windows library hooks and disables Agility SDK upgrades. Game-specific startup remains a separate runtime check.
+4
View File
@@ -42,6 +42,10 @@ Third-party software, unmodified or patched by d4r:
License: d4r/licenses/OptiScaler-LICENSE-GPL-3.0.txt
OptiScaler.ini OptiScaler 0.9.4's OptiScaler.ini with the values d4r needs set
(packaging/optiscaler.settings in the d4r repository)
amd_fidelityfx_dx12.dll, amd_fidelityfx_upscaler_dx12.dll
AMD FidelityFX SDK 2.3 loader and FSR 4.1.1 upscaler (MIT), unmodified, as shipped in
the OptiScaler 0.9.4 release. https://github.com/GPUOpen-LibrariesAndSDKs/FidelityFX-SDK
License: d4r/licenses/FidelityFX-LICENSE.txt
@clean Not included: NVIDIA's DLSS libraries (nvngx_dlss.dll, nvngx_dlssd.dll) and NGX runtime (_nvngx.dll). You supply them
@clean and are responsible for complying with NVIDIA's license terms.
+1 -1
View File
@@ -1 +1 @@
0.1.4
0.1.5
+13 -1
View File
@@ -22,6 +22,16 @@ Model = K
; true shows it.
ShowWatermark = false
[OptiScaler]
; Switching in game from OptiScaler's menu (Insert):
; - DLSS model: DLSS Settings > tick Render Presets Override, pick the preset (E, K, L or M), Apply Changes.
; Unticking it goes back to [DLSS] Model, as do presets left at DEFAULT. false always uses [DLSS] Model.
FollowPresets = true
; - FSR: Upscalers > Upscaler (DX12) > FSR 3.X/4. FSR 4.1 needs an RDNA3/RDNA4 GPU and GE-Proton (which provides
; AMD's FSR 4 library); other GPUs get FSR 3.1. true lets OptiScaler and AMD's FSR see the real GPU (the game
; still sees an NVIDIA one).
Fsr4 = true
[RayReconstruction]
; Requires a CUDA-capable nvngx_dlssd.dll (tested: 310.7) beside nvngx.dll and RR enabled in game.
; auto uses the denoiser when available; false disables the route.
@@ -94,7 +104,9 @@ DirectOutput = auto
; ([Latency] / [Interop] do not apply). Needs Mesa RADV with VK_KHR_cooperative_matrix and the models install.sh
; builds in d4r\engine (k, k-ldr, m, m-ldr, l, l-ldr, rr-d, rr-e, rr-f). Games or settings it does not cover,
; and a device that cannot run it, use the normal path; d4r_nvngx.log says why.
; auto = on when d4r\engine holds a model, true / false = always / never.
; auto = on when d4r\engine holds a model, except model K, which stays on the native kernels (faster than the
; engine's Vulkan network: 2.35 vs 3.59 ms at 1440p Quality on a 7700 XT) unless Network = hip; and M and L need
; VK_EXT_shader_float8 (RDNA4), elsewhere they stay on the normal path. true / false = always / never.
Enabled = auto
; Another model K folder for games with the HDR flag (a path relative to the d4r folder, or a Windows path
; such as Z:\home\me\k)
+1 -1
View File
@@ -7,7 +7,7 @@ ROCM="${D4R_ROCM_DIR:-/opt/rocm}"
CLANG="$ROCM/lib/llvm/bin/clang++"
[[ -x "$CLANG" ]] || { echo "motion kernels need ROCm clang (D4R_ROCM_DIR)" >&2; exit 2; }
mkdir -p "$OUT"
for arch in gfx1100 gfx1101 gfx1102 gfx1103 gfx1200 gfx1201; do
for arch in gfx1030 gfx1031 gfx1032 gfx1033 gfx1034 gfx1035 gfx1036 gfx1100 gfx1101 gfx1102 gfx1103 gfx1200 gfx1201; do
"$CLANG" -x hip -nogpuinc -nogpulib --offload-device-only --offload-arch="$arch" \
--no-gpu-bundle-output -O3 -mno-wavefrontsize64 -o "$OUT/$arch.hsaco" \
"$ROOT/kernels/support/motion_dilate.hip"
+3 -1
View File
@@ -76,6 +76,8 @@ export D4R_DLSS_PTX_DIR="$BUILD/ptx"
python3 "$ROOT/kernels/tools/extract_dlss_ptx.py" "$D4R_BUNDLE_DLSS" "$D4R_DLSS_PTX_DIR" > "$BUILD/logs/extract.log"
build_textures() {
local arch="$1" folder fp8 accuracy out
# RDNA2 has no texture tails (they need WMMA); package_release.sh builds its K layers
[[ "$arch" == gfx103* ]] && return 0
folders=("$arch")
[[ "$arch" == gfx12* ]] && folders+=("$arch-fp8")
for folder in "${folders[@]}"; do
@@ -93,7 +95,7 @@ build_textures() {
}
pending=()
failed=0
for arch in ${D4R_GPU_ARCHS:-gfx1100 gfx1101 gfx1102 gfx1103 gfx1200 gfx1201}; do
for arch in ${D4R_GPU_ARCHS:-gfx1030 gfx1031 gfx1032 gfx1033 gfx1034 gfx1035 gfx1036 gfx1100 gfx1101 gfx1102 gfx1103 gfx1200 gfx1201}; do
build_textures "$arch" &
pending+=("$!")
if (( ${#pending[@]} >= ${D4R_TEX_JOBS:-4} )); then
+5
View File
@@ -87,6 +87,11 @@ def main(argv):
else:
fail(f"[DLSS] Model {model!r}: use one of {', '.join(list(PRESETS) + list(MODEL_ALIASES))} or a preset number")
if flag("OptiScaler", "FollowPresets") is False:
env["D4R_FOLLOW_OPTISCALER"] = "0"
if flag("OptiScaler", "Fsr4") is False:
env["D4R_FSR4_IDENTITY"] = "0"
rr_enabled = flag("RayReconstruction", "Enable")
if rr_enabled is not None:
env["D4R_RR_ENABLE"] = "1" if rr_enabled else "0"
+20 -2
View File
@@ -16,7 +16,7 @@
# D4R_ROCM_RUNTIME ROCm runtime bundled as d4r/rocm (scripts/fetch_rocm_runtime.sh)
# default ~/.cache/d4r-rocm-runtime
# D4R_MAX_GLIBC highest GLIBC ABI the Linux libraries may require default 2.41
# D4R_GPU_ARCHS GPU targets to build kernels for default gfx1100 gfx1101 gfx1102 gfx1103 gfx1200 gfx1201
# D4R_GPU_ARCHS GPU targets to build kernels for default gfx1030-gfx1036 (K only), gfx1100-gfx1103, gfx1200, gfx1201
# (RDNA4 targets also get a <target>-fp8 folder: the kernels for d4r.ini NativeFp8 = on)
# D4R_OPTISCALER_LICENSE OptiScaler's LICENSE (GPL-3.0) text; default: system SPDX or common-licenses copy
# D4R_VKD3D_SRC vkd3d-proton source checkout, for its license files default ~/.cache/d4r-vkd3d-proton
@@ -65,7 +65,7 @@ ZLUDA="${D4R_ZLUDA_DIR:-$HOME/.cache/d4r-zluda-current}"
VKD3D="${D4R_VKD3D_DIR:-$HOME/.cache/d4r-vkd3d-d4r}"
ROCM="${D4R_ROCM_DIR:-/opt/rocm}"
ROCM_RUNTIME="${D4R_ROCM_RUNTIME:-$HOME/.cache/d4r-rocm-runtime}"
ARCHS="${D4R_GPU_ARCHS:-gfx1100 gfx1101 gfx1102 gfx1103 gfx1200 gfx1201}"
ARCHS="${D4R_GPU_ARCHS:-gfx1030 gfx1031 gfx1032 gfx1033 gfx1034 gfx1035 gfx1036 gfx1100 gfx1101 gfx1102 gfx1103 gfx1200 gfx1201}"
if [[ "$INSTALL_MODE" == proxy ]]; then
: "${D4R_OPTISCALER:?set D4R_OPTISCALER to the OptiScaler release archive or folder}"
fi
@@ -104,6 +104,14 @@ fi
cp "$OPTI/OptiScaler.dll" "$STAGE/dxgi.dll"
python3 "$ROOT/scripts/configure_optiscaler.py" \
"$OPTI/OptiScaler.ini" "$ROOT/packaging/optiscaler.settings" "$STAGE/OptiScaler.ini"
# AMD's FidelityFX loader and upscaler (MIT) from the OptiScaler release, beside OptiScaler: its "FSR 3.X/4"
# upscaler. FSR 4 on RDNA3/RDNA4 comes from Proton's amdxcffx64.dll; the shim gives OptiScaler and these
# modules the real GPU identity (tools/d4r_fsr4_identity.h).
for ffx in amd_fidelityfx_dx12.dll amd_fidelityfx_upscaler_dx12.dll; do
[[ -f "$OPTI/$ffx" ]] || { echo "the OptiScaler release has no $ffx" >&2; exit 2; }
cp "$OPTI/$ffx" "$STAGE/$ffx"
done
cp "$OPTI/Licenses/FidelityFX_v2_LICENSE.md" "$STAGE/d4r/licenses/FidelityFX-LICENSE.txt"
else
cp "$ROOT/build/d4r-setup.exe" "$STAGE/d4r/d4r-setup.exe"
# Same NGX exports and implementation, delivered through the ASI loader.
@@ -212,6 +220,16 @@ build_target() {
arch="${folder%-fp8}"
fp8=0
[[ "$folder" == *-fp8 ]] && fp8=1
# RDNA2 (gfx103x): only DLSS 4's layers (preset K) have native kernels there, built with software matrix
# products (kernels/common/wmma_layout.h); M, L, the texture tails and Ray Reconstruction stay translated by ZLUDA
if [[ "$arch" == gfx103* ]]; then
D4R_PREFER_ACCURACY=0 D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" "$ROOT/kernels/build.sh" k "$STAGE/d4r/kernels/$folder" >/dev/null
D4R_PREFER_ACCURACY=1 D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" "$ROOT/kernels/build.sh" k "$STAGE/d4r/kernels/accuracy/$folder" >/dev/null
rm -f "$STAGE/d4r/kernels/$folder"/*.resolution.txt "$STAGE/d4r/kernels/accuracy/$folder"/*.resolution.txt
python3 "$ROOT/kernels/tools/kernel_manifest.py" "$STAGE/d4r/kernels/$folder" "${DLLS[@]}" "${RR_DLLS[@]}"
python3 "$ROOT/kernels/tools/kernel_manifest.py" "$STAGE/d4r/kernels/accuracy/$folder" "${DLLS[@]}" "${RR_DLLS[@]}"
return 0
fi
D4R_PREFER_ACCURACY=0 D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" "$ROOT/kernels/build.sh" k "$STAGE/d4r/kernels/$folder" >/dev/null
D4R_PREFER_ACCURACY=0 D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" "$ROOT/kernels/build.sh" m "$STAGE/d4r/kernels/$folder" >/dev/null
rm -f "$STAGE/d4r/kernels/$folder"/*.resolution.txt # empty LTO notes from clang's -save-temps
+2 -2
View File
@@ -125,13 +125,13 @@ struct Feature {
uint32_t words[4] = {0, 3, 4, 5}, markers[4] = {6, 3, 4, 5};
Feature* current;
int drains = 0, drainResult = 0;
int sync() {
int drain_sync() { // not sync(): glibc's <unistd.h> declares void sync()
++drains;
assert(words[2] == 7); // drain may only run after the CPU released the blocked queue
assert(current->ngxWaitActive); // ticket and input ownership survive the drain
return drainResult;
}
struct { struct { int(*ctxSynchronize)() = sync; void(*timelineCancel)(void*) = nullptr; } cu; } g;
struct { struct { int(*ctxSynchronize)() = drain_sync; void(*timelineCancel)(void*) = nullptr; } cu; } g;
''' + release + '\n' + complete + r'''
int main() {
Feature f; f.ngxInputSync.mapped = words; current = &f;
+6
View File
@@ -138,6 +138,8 @@ class OptiScalerConfigTests(unittest.TestCase):
for name in ('d3d12.dll', 'd3d12core.dll'):
fixture(inputs / 'vkd3d' / name, b'MZ fixture d3d12core.dll\0 then d3d12core.dll\0' if name == 'd3d12.dll' else b'fixture')
fixture(inputs / 'opti/OptiScaler.dll')
for name in ('amd_fidelityfx_dx12.dll', 'amd_fidelityfx_upscaler_dx12.dll', 'Licenses/FidelityFX_v2_LICENSE.md'):
fixture(inputs / 'opti' / name)
fixture(inputs / 'opti/OptiScaler.ini', upstream_ini().replace('\n', '\r\n').encode())
dlss = fixture(inputs / 'nvngx_dlss.dll')
core = fixture(inputs / '_nvngx.dll')
@@ -193,6 +195,10 @@ class OptiScalerConfigTests(unittest.TestCase):
self.assert_routing(data)
configs.append(data)
self.assertEqual(data, (repo / 'dist' / name / 'OptiScaler.ini').read_bytes())
# OptiScaler's FSR 3.X/4 upscaler: AMD's loader and upscaler beside it
for ffx in ('amd_fidelityfx_dx12.dll', 'amd_fidelityfx_upscaler_dx12.dll',
'd4r/licenses/FidelityFX-LICENSE.txt'):
self.assertIn(ffx, archive.namelist())
else:
self.assertNotIn('OptiScaler.ini', archive.namelist())
self.assertNotIn('dxgi.dll', archive.namelist())
+95
View File
@@ -154,6 +154,101 @@ int main(int argc, char** argv) {
self.assertEqual(self.select(rr=True, D4R_RR_PRESET='0'), [0] * 6)
class OptiScalerPresetFollowerTests(unittest.TestCase):
"""OptiScaler's Render Presets Override wins over [DLSS] Model once it changes the game's presets."""
def run_policy(self, body):
source = r'''
#include "d4r_render_presets.h"
#include <cassert>
#include <cstring>
static bool same(const unsigned int (&a)[6], const unsigned int (&b)[6]) { return std::memcmp(a, b, sizeof(a)) == 0; }
int main() {
''' + body + '\n}\n'
with tempfile.TemporaryDirectory() as directory:
runner = Path(directory) / 'follower'
subprocess.run(['c++', '-std=c++17', '-Wall', '-Wextra', '-Werror', '-Wno-unused-function',
'-I', str(ROOT / 'tools'), '-x', 'c++', '-', '-o', str(runner)],
input=source, text=True, capture_output=True, check=True)
subprocess.run([str(runner)], check=True, timeout=5)
def test_game_presets_keep_the_d4r_model_until_optiscaler_changes_them(self):
self.run_policy(r'''
D4rPresetFollower follower;
const unsigned int game[6] = {0, 0, 0, 0, 0, 0};
assert(!follower.follow(game, false)); // first creation: the game's presets
assert(!follower.follow(game, false)); // a resolution or quality change recreates with the same
const unsigned int m[6] = {13, 13, 13, 13, 13, 13};
assert(follower.follow(m, false)); // OptiScaler: override M, Apply Changes
const unsigned int e[6] = {5, 5, 5, 5, 5, 5};
assert(follower.follow(e, false)); // then E
assert(!follower.follow(game, false)); // override unticked: OptiScaler restores the game's presets
''')
def test_explicit_game_presets_are_the_baseline_too(self):
self.run_policy(r'''
D4rPresetFollower follower;
const unsigned int game[6] = {11, 11, 11, 13, 12, 11};
assert(!follower.follow(game, false));
const unsigned int perMode[6] = {11, 11, 11, 13, 13, 11}; // OptiScaler's per-mode presets
assert(follower.follow(perMode, false));
''')
def test_ini_override_is_followed_from_the_first_creation(self):
self.run_policy(r'''
D4rPresetFollower follower;
const unsigned int k[6] = {11, 11, 11, 11, 11, 11};
assert(follower.follow(k, true));
assert(follower.follow(k, true));
''')
def test_default_and_latest_slots_take_the_d4r_model(self):
self.run_policy(r'''
unsigned int presets[6] = {0, 13, kD4rPresetLatest, 5, 12, 0};
d4r_apply_followed_presets(presets, d4r_parse_render_preset_override("11"));
const unsigned int expected[6] = {11, 13, 11, 5, 12, 11};
assert(same(presets, expected));
unsigned int untouched[6] = {0, 13, 0, 5, 12, 0};
const unsigned int copy[6] = {0, 13, 0, 5, 12, 0};
d4r_apply_followed_presets(untouched, d4r_parse_render_preset_override(""));
assert(same(untouched, copy));
''')
def test_flagged_and_retired_presets_read_as_default(self):
self.run_policy(r'''
// Alan Wake 2 (Streamline): E and F with the high bit set, then OptiScaler recreates with zeros
unsigned int game[6] = {0x80000006u, 0x80000005u, 0x80000005u, 0x80000005u, 0x80000006u, 0x80000006u};
d4r_sanitize_presets(game, false);
const unsigned int zeros[6] = {0, 0, 0, 0, 0, 0};
assert(same(game, zeros));
D4rPresetFollower follower;
assert(!follower.follow(game, false));
unsigned int reset[6] = {0, 0, 0, 0, 0, 0x80000006u};
d4r_sanitize_presets(reset, false);
assert(!follower.follow(reset, false)); // not a choice made in OptiScaler
unsigned int mixed[6] = {1, 4, 7, 11, 13, kD4rPresetLatest};
d4r_sanitize_presets(mixed, false);
const unsigned int expected[6] = {0, 0, 0, 11, 13, kD4rPresetLatest};
assert(same(mixed, expected));
unsigned int rr[6] = {4, 5, 6, 11, 0x80000004u, 0};
d4r_sanitize_presets(rr, true);
const unsigned int rrExpected[6] = {4, 5, 6, 0, 0, 0};
assert(same(rr, rrExpected));
''')
def test_quality_mode_selects_ngx_preset_slot(self):
self.run_policy(r'''
// slots: DLAA, Quality, Balanced, Performance, UltraPerformance, UltraQuality
assert(d4r_preset_slot(0) == 3); // MaxPerf
assert(d4r_preset_slot(1) == 2); // Balanced
assert(d4r_preset_slot(2) == 1); // MaxQuality
assert(d4r_preset_slot(3) == 4); // UltraPerformance
assert(d4r_preset_slot(4) == 5); // UltraQuality
assert(d4r_preset_slot(5) == 0); // DLAA
assert(d4r_preset_slot(-1) == 1 && d4r_preset_slot(9) == 1);
''')
if __name__ == '__main__':
unittest.main()
+16
View File
@@ -2445,6 +2445,22 @@ int main(int argc, char** argv)
d4r_ngx_set_uint(parameters, "Height", height);
d4r_ngx_set_uint(parameters, "DLSS.Render.Subrect.Dimensions.Width", width);
d4r_ngx_set_uint(parameters, "DLSS.Render.Subrect.Dimensions.Height", height);
// D4R_HARNESS_RECREATE_PRESETS=11,5,13: every Super Sampling preset slot of cycle N takes entry N-1 (cycling),
// as OptiScaler's Render Presets Override + Apply Changes recreates the feature with the picked model
if (const char* list = std::getenv("D4R_HARNESS_RECREATE_PRESETS"); list != nullptr && *list != '\0')
{
std::vector<unsigned int> presets;
for (const char* cursor = list; *cursor != '\0';)
{
char* end = nullptr;
presets.push_back(static_cast<unsigned int>(std::strtoul(cursor, &end, 0)));
cursor = *end != '\0' ? end + 1 : end;
}
const unsigned int preset = presets[(cycle - 1) % presets.size()];
for (const char* mode : {"DLAA", "Quality", "Balanced", "Performance", "UltraPerformance", "UltraQuality"})
d4r_ngx_set_uint(parameters, (std::string("DLSS.Hint.Render.Preset.") + mode).c_str(), preset);
std::printf("RECREATE cycle %d: presets %u\n", cycle, preset);
}
result = createFeature(g_list, featureId, parameters, &feature);
if (result != NGX_SUCCESS)
{
+173
View File
@@ -0,0 +1,173 @@
// FSR 4 from OptiScaler's upscaler menu while the game sees an NVIDIA GPU.
//
// d4r's games run with WINE_HIDE_AMD_GPU (PROTON_FORCE_NVAPI), so every DXGI adapter reports vendor 0x10de. Three
// parties then refuse FSR 4 on the AMD GPU that runs it: OptiScaler's own GPU check (RDNA3/RDNA4 -> FSR 4 update,
// INT8 or FP8 model), AMD's amd_fidelityfx_upscaler_dx12.dll and the FSR 4 provider amdxcffx64.dll, each of which
// reads the adapter's vendor before using the driver extension (2026-10-17: with the identity hidden, FSR 4.1.1
// produced FSR 3.1's output byte for byte; amdxcffx64.dll never loaded unless Fsr4Update was forced, and then the
// upscaler still chose FSR 3.1).
//
// The adapter's GetDesc family is wrapped so that these callers alone - OptiScaler (found by its PE export name,
// whatever its loader file is called) and AMD's FidelityFX modules - read the GPU's real PCI vendor and device ids,
// from sysfs. The game, vkd3d-proton, NVIDIA's NGX libraries and everything else keep the NVIDIA identity, and only
// the adapter behind the game's D3D12 device is changed.
#pragma once
#include <windows.h>
#include <dxgi1_6.h>
#include <cstdint>
#include <cstring>
#include <string>
#include "d4r_fsr_rr_identity.h"
namespace d4r_fsr4_identity
{
inline D4rPciIdentity g_real;
inline LUID g_luid = {};
using GetDescFn = HRESULT(STDMETHODCALLTYPE*)(IDXGIAdapter*, DXGI_ADAPTER_DESC*);
using GetDesc1Fn = HRESULT(STDMETHODCALLTYPE*)(IDXGIAdapter1*, DXGI_ADAPTER_DESC1*);
using GetDesc2Fn = HRESULT(STDMETHODCALLTYPE*)(IDXGIAdapter2*, DXGI_ADAPTER_DESC2*);
using GetDesc3Fn = HRESULT(STDMETHODCALLTYPE*)(IDXGIAdapter4*, DXGI_ADAPTER_DESC3*);
inline GetDescFn g_getDesc = nullptr;
inline GetDesc1Fn g_getDesc1 = nullptr;
inline GetDesc2Fn g_getDesc2 = nullptr;
inline GetDesc3Fn g_getDesc3 = nullptr;
// The DLL name in a module's PE export directory ("OptiScaler.dll" for OptiScaler under any file name).
inline bool export_name_is(HMODULE module, const char* expected)
{
auto* base = reinterpret_cast<const unsigned char*>(module);
auto* dos = reinterpret_cast<const IMAGE_DOS_HEADER*>(base);
if (dos->e_magic != IMAGE_DOS_SIGNATURE)
return false;
auto* nt = reinterpret_cast<const IMAGE_NT_HEADERS*>(base + dos->e_lfanew);
if (nt->Signature != IMAGE_NT_SIGNATURE)
return false;
const IMAGE_DATA_DIRECTORY& directory = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (directory.VirtualAddress == 0 || directory.Size == 0)
return false;
auto* exports = reinterpret_cast<const IMAGE_EXPORT_DIRECTORY*>(base + directory.VirtualAddress);
return exports->Name != 0 && _stricmp(reinterpret_cast<const char*>(base + exports->Name), expected) == 0;
}
// Whether the code at `address` belongs to OptiScaler or to one of AMD's FidelityFX/FSR 4 modules.
inline bool caller_sees_real(void* address)
{
HMODULE module = nullptr;
if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
static_cast<const char*>(address), &module) || module == nullptr)
return false;
char path[MAX_PATH] = {};
const DWORD length = GetModuleFileNameA(module, path, MAX_PATH);
if (length == 0 || length >= MAX_PATH)
return false;
const char* slash = std::strrchr(path, '\\');
const char* name = slash != nullptr ? slash + 1 : path;
if (_strnicmp(name, "amd_fidelityfx_", 15) == 0 || _stricmp(name, "amdxcffx64.dll") == 0 ||
_stricmp(name, "amdxc64.dll") == 0)
return true;
return export_name_is(module, "OptiScaler.dll");
}
template <typename Desc> inline void correct(void* caller, Desc* out)
{
if (out->AdapterLuid.LowPart == g_luid.LowPart && out->AdapterLuid.HighPart == g_luid.HighPart &&
caller_sees_real(caller))
{
out->VendorId = g_real.vendor;
out->DeviceId = g_real.device;
}
}
inline HRESULT STDMETHODCALLTYPE get_desc(IDXGIAdapter* self, DXGI_ADAPTER_DESC* desc)
{
const HRESULT result = g_getDesc(self, desc);
if (SUCCEEDED(result) && desc != nullptr)
correct(__builtin_return_address(0), desc);
return result;
}
inline HRESULT STDMETHODCALLTYPE get_desc1(IDXGIAdapter1* self, DXGI_ADAPTER_DESC1* desc)
{
const HRESULT result = g_getDesc1(self, desc);
if (SUCCEEDED(result) && desc != nullptr)
correct(__builtin_return_address(0), desc);
return result;
}
inline HRESULT STDMETHODCALLTYPE get_desc2(IDXGIAdapter2* self, DXGI_ADAPTER_DESC2* desc)
{
const HRESULT result = g_getDesc2(self, desc);
if (SUCCEEDED(result) && desc != nullptr)
correct(__builtin_return_address(0), desc);
return result;
}
inline HRESULT STDMETHODCALLTYPE get_desc3(IDXGIAdapter4* self, DXGI_ADAPTER_DESC3* desc)
{
const HRESULT result = g_getDesc3(self, desc);
if (SUCCEEDED(result) && desc != nullptr)
correct(__builtin_return_address(0), desc);
return result;
}
template <typename Fn> inline void patch_slot(void** vtable, int slot, Fn hook, Fn& original)
{
if (vtable[slot] == reinterpret_cast<void*>(hook))
return;
DWORD protect = 0;
if (!VirtualProtect(&vtable[slot], sizeof(void*), PAGE_READWRITE, &protect))
return;
original = reinterpret_cast<Fn>(vtable[slot]);
vtable[slot] = reinterpret_cast<void*>(hook);
VirtualProtect(&vtable[slot], sizeof(void*), protect, &protect);
}
// Installs the wrapper for the adapter of `device` when the GPU is really AMD's and DXGI reports someone else.
// Once per process; `detail` says what was found and done.
inline bool install(ID3D12Device* device, std::string& detail)
{
const D4rPciIdentity reported = d4r_dxgi_identity(device);
std::string pci;
const D4rPciIdentity real = d4r_true_identity(device, pci);
char text[640];
std::snprintf(text, sizeof(text), "DXGI reports %04x:%04x; %s", reported.vendor, reported.device,
real.known ? pci.c_str() : ("real PCI identity unknown (" + pci + ")").c_str());
detail = text;
if (!real.known || real.vendor != 0x1002)
{
detail += "; not an AMD GPU: nothing to do";
return false;
}
if (reported.known && reported.vendor == real.vendor)
{
detail += "; DXGI already reports the real vendor: nothing to do";
return false;
}
g_real = real;
device->GetAdapterLuid(&g_luid);
IDXGIFactory4* factory = nullptr;
if (FAILED(CreateDXGIFactory2(0, __uuidof(IDXGIFactory4), reinterpret_cast<void**>(&factory))))
{
detail += "; no DXGI factory";
return false;
}
IDXGIAdapter4* adapter = nullptr;
const HRESULT found = factory->EnumAdapterByLuid(g_luid, __uuidof(IDXGIAdapter4), reinterpret_cast<void**>(&adapter));
factory->Release();
if (FAILED(found) || adapter == nullptr)
{
detail += "; the device's adapter has no IDXGIAdapter4";
return false;
}
// IDXGIAdapter4's vtable: GetDesc 8, GetDesc1 10, GetDesc2 11, GetDesc3 18. All adapters of the
// implementation share it; the LUID check above keeps other adapters unchanged.
void** vtable = *reinterpret_cast<void***>(adapter);
patch_slot(vtable, 8, &get_desc, g_getDesc);
patch_slot(vtable, 10, &get_desc1, g_getDesc1);
patch_slot(vtable, 11, &get_desc2, g_getDesc2);
patch_slot(vtable, 18, &get_desc3, g_getDesc3);
adapter->Release();
detail += "; OptiScaler and AMD's FidelityFX modules read the real ids (FSR 4)";
return g_getDesc != nullptr && g_getDesc1 != nullptr && g_getDesc2 != nullptr && g_getDesc3 != nullptr;
}
} // namespace d4r_fsr4_identity
+112 -7
View File
@@ -100,6 +100,7 @@
#include <bcrypt.h>
#include "d4r_fsr_rr.h"
#include "d4r_fsr_rr_identity.h"
#include "d4r_fsr4_identity.h"
#include "d4r_fsr_rr_passes.h"
#include "../engine/game.h"
#include "../engine/game_m.h"
@@ -625,6 +626,12 @@ static void load_portable_config()
model.clear();
if (!model.empty())
portable_set("D4R_DLSS_PRESET", model);
// [OptiScaler] FollowPresets: its Render Presets Override picks the model in game (d4r_render_presets.h);
// Fsr4: OptiScaler and AMD's FidelityFX modules see the real GPU, for its FSR 4 option (d4r_fsr4_identity.h).
if (ini_flag(ini, "optiscaler", "FollowPresets", 1) == 0)
portable_set("D4R_FOLLOW_OPTISCALER", "0");
if (ini_flag(ini, "optiscaler", "Fsr4", 1) == 0)
portable_set("D4R_FSR4_IDENTITY", "0");
// Ray Reconstruction is configured on its own section and drives its own feature, so
// nothing here reaches [DLSS] Model and nothing there reaches this.
@@ -755,6 +762,10 @@ static void load_portable_config()
else
on = ini_flag(ini, "engine", "Enabled", 0) != 0;
portable_set("D4R_ENGINE", on ? "1" : "0");
// auto leaves K on the native kernels: K's Vulkan network is slower (2026-10-17, 7700 XT, 1706x960 -> 1440p:
// 3.59 ms vs 2.35 ms dlss_gpu). Enabled = true, or Network = hip with a model that has the HIP layers, keeps it.
if (enabled.empty() || enabled == "auto")
portable_set("D4R_ENGINE_K", "0");
}
if (const std::string engineModel = ini_value(ini, "engine", "ModelDir"); !engineModel.empty())
portable_set(L"D4R_ENGINE_MODEL_DIR", portable_path(engineModel));
@@ -1982,6 +1993,13 @@ static NgxResult initialize(unsigned long long applicationId, const wchar_t* dat
LUID luid;
device->GetAdapterLuid(&luid);
set_cuda_adapter_luid(luid);
// OptiScaler's FSR 4 option needs the real AMD identity for OptiScaler and AMD's modules (d4r_fsr4_identity.h)
if (env_string("D4R_FSR4_IDENTITY") != "0")
{
std::string detail;
d4r_fsr4_identity::install(device, detail);
logf("FSR 4 identity: %s", detail.c_str());
}
}
g.paths.clear();
@@ -3364,6 +3382,9 @@ struct VulkanInterop
VkDevice device = VK_NULL_HANDLE;
VkPhysicalDeviceMemoryProperties memory = {};
VkPhysicalDevice physical = VK_NULL_HANDLE;
// VK_EXT_shader_float8 (vkd3d-proton enables it when the GPU has it): M's and L's engine layers are FP8
// cooperative-matrix shaders, which RADV's compiler crashes on without it (RDNA3)
bool float8 = false;
PFN_vkGetPhysicalDeviceFormatProperties formatProperties = nullptr;
PFN_vkCreateBuffer createBuffer = nullptr;
PFN_vkDestroyBuffer destroyBuffer = nullptr;
@@ -9027,6 +9048,36 @@ static NgxResult create_cuda_feature(Feature& feature)
return result;
}
// Whether the presets of this creation were picked in OptiScaler (d4r_render_presets.h): its override is on in
// OptiScaler.ini, or the presets changed since the game's first creation. (OptiScaler forwards the game's
// AllocateParameters to this DLL, so the table itself does not say who filled it.) D4R_FOLLOW_OPTISCALER=0 always
// keeps d4r's model. g.mutex is held.
static bool optiscaler_chose_presets(bool rayReconstruction, const unsigned int (&presets)[6])
{
static D4rPresetFollower followers[2];
static bool iniOverride[2] = {};
static std::once_flag iniOnce;
if (env_string("D4R_FOLLOW_OPTISCALER") == "0")
return false;
std::call_once(iniOnce, [] {
// OptiScaler reads OptiScaler.ini beside its own DLL, which d4r's install puts beside the game's EXE
wchar_t exe[MAX_PATH] = {};
const DWORD length = GetModuleFileNameW(nullptr, exe, MAX_PATH);
if (length == 0 || length >= MAX_PATH)
return;
std::wstring path(exe, length);
path.resize(path.find_last_of(L"\\/") + 1);
std::string text;
if (!read_whole_file(path + L"OptiScaler.ini", text))
return;
const std::vector<IniEntry> ini = parse_ini(text);
iniOverride[0] = ini_flag(ini, "dlss", "RenderPresetOverride", 0) == 1;
iniOverride[1] = ini_flag(ini, "dlssd", "RenderPresetOverride", 0) == 1;
logf("OptiScaler.ini: RenderPresetOverride DLSS=%d DLSSD=%d", iniOverride[0], iniOverride[1]);
});
return followers[rayReconstruction].follow(presets, iniOverride[rayReconstruction]);
}
static NgxResult fsr_rr_create(Feature& feature, void* parameters);
static NgxResult create_feature_locked(unsigned int featureId, void* parameters, NgxHandle** handle,
bool nativeVulkan);
@@ -9111,27 +9162,40 @@ static NgxResult create_feature_locked(unsigned int featureId, void* parameters,
presets[index] = get_uint_or(parameters, presetNames[index], 0);
const std::string forced = env_string(d4r_render_preset_variable(rayReconstruction));
const auto presetOverride = d4r_parse_render_preset_override(forced.c_str());
// Flagged, retired or unknown values count as Default, both for spotting OptiScaler's change and in what is followed
if (presetOverride.enabled)
d4r_sanitize_presets(presets, rayReconstruction);
const bool followed = presetOverride.enabled && optiscaler_chose_presets(rayReconstruction, presets);
if (followed)
{
logf("DLSS model: OptiScaler's preset override [DLAA=%u Quality=%u Balanced=%u Performance=%u "
"UltraPerformance=%u UltraQuality=%u] wins over %s=%s (which still fills Default slots)",
presets[0], presets[1], presets[2], presets[3], presets[4], presets[5],
d4r_render_preset_variable(rayReconstruction), forced.c_str());
d4r_apply_followed_presets(presets, presetOverride);
}
else if (presetOverride.enabled)
{
const unsigned int value = presetOverride.value;
const char* model = value == 5 ? "E" : value == 11 ? "K" : value == 12 ? "L" : value == 13 ? "M" : "custom";
logf("DLSS model override: D4R_DLSS_PRESET=%s (model %s) overrides game/OptiScaler presets "
logf("DLSS model override: D4R_DLSS_PRESET=%s (model %s) overrides game presets "
"[DLAA=%u Quality=%u Balanced=%u Performance=%u UltraPerformance=%u UltraQuality=%u]; "
"using preset=%u for all quality modes",
forced.c_str(), model, presets[0], presets[1], presets[2], presets[3], presets[4], presets[5], value);
presetOverride.apply(presets);
}
presetOverride.apply(presets);
feature->preset = presets[1];
// NGX runs the preset of the feature's quality mode.
feature->preset = presets[d4r_preset_slot(quality)];
// Direct output needs the native output kernel of the preset in use (verified: K = 11 through
// hiluma_engine_output, M = 13 through rrlite_downsample_kernel, its last kernel). Only a forced
// Super Sampling preset qualifies; a Ray Reconstruction preset never does here.
// hiluma_engine_output, M = 13 through rrlite_downsample_kernel, its last kernel). Only a forced or
// OptiScaler-chosen Super Sampling preset qualifies; a Ray Reconstruction preset never does here.
if (!forced.empty() && !rayReconstruction)
{
char allowed[128] = "11,13";
if (const std::string list = env_string("D4R_SHIM_OUTPUT_DIRECT_PRESETS"); !list.empty())
snprintf(allowed, sizeof(allowed), "%s", list.c_str());
for (char* token = strtok(allowed, ","); token != nullptr; token = strtok(nullptr, ","))
feature->outputDirectAllowed |= strtoul(token, nullptr, 0) == presets[1];
feature->outputDirectAllowed |= strtoul(token, nullptr, 0) == feature->preset;
}
feature->rayReconstruction = rayReconstruction;
if (rayReconstruction)
@@ -9139,7 +9203,7 @@ static NgxResult create_feature_locked(unsigned int featureId, void* parameters,
feature->auxScalarBaseline[index] = get_int_or(parameters, kAuxScalars[index].name, 0);
logf("NVSDK_NGX_D3D12_CreateFeature(feature=%u%s): %ux%u -> %ux%u quality=%d flags=0x%x subrects=%d preset=%u",
featureId, rayReconstruction ? ", Ray Reconstruction" : "", feature->width, feature->height,
feature->outWidth, feature->outHeight, quality, flags, subrects, presets[1]);
feature->outWidth, feature->outHeight, quality, flags, subrects, feature->preset);
feature->subrects = subrects;
std::copy(std::begin(presets), std::end(presets), feature->creationPresets.begin());
@@ -9244,6 +9308,18 @@ static void init_engine_vulkan()
logf("engine backend: Vulkan entry points are unavailable");
return;
}
auto enumerate = reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>(
instanceProc(instance, "vkEnumerateDeviceExtensionProperties"));
uint32_t count = 0;
if (enumerate != nullptr && enumerate(g_vk.physical, nullptr, &count, nullptr) == VK_SUCCESS)
{
std::vector<VkExtensionProperties> extensions(count);
if (enumerate(g_vk.physical, nullptr, &count, extensions.data()) == VK_SUCCESS)
for (uint32_t index = 0; index < count; ++index)
g_vk.float8 |= std::strcmp(extensions[index].extensionName, "VK_EXT_shader_float8") == 0;
}
logf("engine backend: VK_EXT_shader_float8 %s", g_vk.float8 ? "available (M and L engine layers)" : "unavailable: "
"M and L run on the CUDA backend");
}
static void init_engine_backend()
@@ -9982,6 +10058,10 @@ static bool engine_evaluate_m(Feature& feature, ID3D12GraphicsCommandList* list,
};
// NVSDK_NGX_DLSS_Feature_Flags: IsHDR 1, MVLowRes 2, MVJittered 4, DepthInverted 8, AutoExposure 0x40.
const bool ldr = (feature.flags & 0x1) == 0; // NGX's LDR stages: a model directory of its own, no exposure
// The model's pipelines include the FP8 layers even when the HIP network runs them; creating those without
// VK_EXT_shader_float8 crashes the driver (RADV on RDNA3, vkCreateComputePipelines), so never load the model.
if (!g_vk.float8)
return refuse("the GPU has no VK_EXT_shader_float8 for the FP8 layers");
std::call_once(g_engineM.once[presetL][ldr], [presetL, ldr] { init_engine_backend_m_variant(presetL, ldr); });
d4r::MModel* const model = g_engineM.models[presetL][ldr].get();
if (model == nullptr)
@@ -10561,6 +10641,8 @@ static bool engine_evaluate(Feature& feature, ID3D12GraphicsCommandList* list, I
return engine_evaluate_rr(feature, list, color, depth, motion, output, parameters, p, frame);
if (feature.preset == 13 || feature.preset == 12)
return engine_evaluate_m(feature, list, color, depth, motion, output, p, frame);
if (feature.preset == 11 && env_string("D4R_ENGINE_K") == "0" && env_string("D4R_ENGINE_NETWORK") != "hip")
return refuse("[Engine] Enabled = auto keeps K on the native kernels (faster than the Vulkan network)");
// Without the HDR flag NGX runs its LDR input and output kernels: a model directory of its own.
const bool ldr = (feature.flags & 0x1) == 0;
if (ldr)
@@ -10745,6 +10827,24 @@ static bool engine_evaluate(Feature& feature, ID3D12GraphicsCommandList* list, I
}
if (FAILED(g_vk.lifetime->RetainExternalResources(list, feature.resources)))
return refuse("the command list cannot retain the engine's resources");
// D4R_PROFILE: the Vulkan network's GPU time on the game's queue (timestamps around the whole recording)
const bool profile = profile_enabled() && env_uint("D4R_PROFILE_GPU_TIMESTAMPS", 1) != 0 &&
profile_timestamps_ready(feature);
if (profile)
{
FrameTiming measured;
read_profile_timestamps(feature, frame, measured);
const auto now = ProfileClock::now();
const double interval = feature.engineRProfileAt.time_since_epoch().count() ?
profile_ms(feature.engineRProfileAt, now) : 0.0;
feature.engineRProfileAt = now;
if (profile_log_frame(frame) && measured.vkTotal >= 0)
logf("D4R_ENGINE_K_PROFILE frame=%u gpu_frame=%u render=%ux%u output=%ux%u gpu_eval=%.3f "
"game_interval=%.3f age=0", frame, frame - kOutputSlots, p.renderWidth, p.renderHeight,
feature.outWidth, feature.outHeight, measured.vkTotal, interval);
profile_timestamp(feature, list, frame, 0);
profile_timestamp(feature, list, frame, 1);
}
for (const Source& source : sources)
transition(list, source.resource, source.state, source.copyState);
VkCommandBuffer command = VK_NULL_HANDLE;
@@ -10772,6 +10872,11 @@ static bool engine_evaluate(Feature& feature, ID3D12GraphicsCommandList* list, I
transition(list, source.resource, source.copyState, source.state);
if (!recorded)
return refuse(failure);
if (profile)
{
profile_timestamp(feature, list, frame, 2);
profile_timestamp(feature, list, frame, 3);
}
feature.engineJitter[0] = p.jitterX;
feature.engineJitter[1] = p.jitterY;
if (frame <= 3 || frame % 600 == 0)
+76
View File
@@ -30,3 +30,79 @@ inline D4rRenderPresetOverride d4r_parse_render_preset_override(const char* over
return {false, 0};
return {true, static_cast<unsigned int>(std::strtoul(overrideValue, nullptr, 0))};
}
// OptiScaler's own preset override (its menu's "Render Presets Override" and "Apply Changes", or
// RenderPresetOverride=true in OptiScaler.ini) recreates the feature with the presets the player picked. The
// first creation carries the game's presets; a later one that carries different presets was changed in
// OptiScaler, and those win over [DLSS] Model / [RayReconstruction] Model until they change back to the game's.
// Super Sampling and Ray Reconstruction keep a follower each.
inline constexpr unsigned int kD4rPresetLatest = 0x00FFFFFF; // OptiScaler's "Latest": the DLL's choice
struct D4rPresetFollower
{
bool haveBaseline = false;
unsigned int baseline[6] = {};
// true when `presets` were chosen in OptiScaler. `iniOverride`: OptiScaler.ini enables the override, so
// even the first creation carries the player's choice.
bool follow(const unsigned int (&presets)[6], bool iniOverride)
{
if (!haveBaseline)
{
for (int index = 0; index < 6; ++index)
baseline[index] = presets[index];
haveBaseline = true;
return iniOverride;
}
if (iniOverride)
return true;
for (int index = 0; index < 6; ++index)
if (presets[index] != baseline[index])
return true;
return false;
}
};
// A slot the follower may honour: Super Sampling E, F (CNN), J, K (DLSS 4), L, M (DLSS 4.5); Ray Reconstruction
// D, E (DLSS 4), F (DLSS 4.5); or Default/Latest. Everything else reads as Default (0): flagged values such as
// Alan Wake 2's 0x80000005/0x80000006 (Streamline's "default", E and F with the high bit set), and presets DLSS 310
// no longer ships (A-D, G-I) or that do not exist.
inline unsigned int d4r_followable_preset(unsigned int preset, bool rayReconstruction)
{
if (preset == kD4rPresetLatest)
return preset;
if (rayReconstruction)
return preset >= 4 && preset <= 6 ? preset : 0;
switch (preset)
{
case 5: case 6: case 10: case 11: case 12: case 13:
return preset;
default:
return 0;
}
}
inline void d4r_sanitize_presets(unsigned int (&presets)[6], bool rayReconstruction)
{
for (unsigned int& preset : presets)
preset = d4r_followable_preset(preset, rayReconstruction);
}
// Followed presets keep every quality mode that names a model; Default (0) and "Latest" take d4r's override.
inline void d4r_apply_followed_presets(unsigned int (&presets)[6], const D4rRenderPresetOverride& overrideValue)
{
if (!overrideValue.enabled)
return;
for (unsigned int& preset : presets)
if (preset == 0 || preset == kD4rPresetLatest)
preset = overrideValue.value;
}
// The preset slot ([DLAA, Quality, Balanced, Performance, UltraPerformance, UltraQuality]) NGX uses for
// NVSDK_NGX_PerfQuality_Value `quality` (MaxPerf 0, Balanced 1, MaxQuality 2, UltraPerformance 3,
// UltraQuality 4, DLAA 5).
inline int d4r_preset_slot(int quality)
{
static const int slots[6] = {3, 2, 1, 4, 5, 0};
return quality >= 0 && quality < 6 ? slots[quality] : 1;
}