diff --git a/README.md b/README.md index 2c2d131..44d266f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,15 @@ # d4r (dlss 4 radeon) +**This fork includes a native Windows 11 / RX 9070 XT (gfx1201) port.** +Start with [Windows build and validation](docs/windows-rdna4-port.md) and +[OptiScaler installation](docs/windows-game.md). K/M have hardware validation; +K performance is still being improved. The Linux upstream description follows. + d4r runs NVIDIA's official DLSS Super Resolution library (`nvngx_dlss.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 DLSS kernels replaced by hand-written RDNA3 and RDNA4 code. -**Supported DLSS models:** DLSS 3 CNN (E), DLSS 4 transformer (K, default), and DLSS 4.5 transformer (M). 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 experimental L). 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. **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 has been tested on one GPU in a handful of games and depends on unreleased patches to ZLUDA and vkd3d-proton. @@ -14,15 +21,19 @@ submission, and returns output in the same frame without CPU image copies. RGBA16/32F, R11G11B10, RGBA/BGRA8, depth, exposure and motion formats have independent D3D12 typed SRV/UAV conversion tests. Four-frame K/M runs with packed output match the FP16 baseline exactly after resource-format quantisation. -The build incorporates upstream updates through `f0d1a65`. Patched OptiScaler now +The build incorporates upstream updates through `aa447b2`. Patched OptiScaler now passes standalone K/M native-network tests with exact RGB agreement against the direct-d4r baseline; see [the source patch](patches/optiscaler/README.md). Silent Hill 2 now renders K through the native Windows backend; M also renders a saved gameplay level with GPU output NaN/Inf checks. Startup depth/stencil and swap-chain lifetime issues found in the game have regression checks. Both K and M now pass 3840x2160 game output scans. K reaches a user-reported -49-51 FPS in the measured scene after native output stores and command-hook -lookup caching. Native M FP8 is separately replay/network validated; the +62 FPS / 63% GPU with optional async interop, compared with the earlier +49-51 FPS / 53% synchronous run. The fixed async build completes 16941 finite +4K frames including feature recreation; queued-frame/recreation outputs match +synchronous controls exactly. These user readings are not a controlled FSR4 +benchmark; the K performance gate remains open. Windows preset L is not yet +validated. Native M FP8 is separately replay/network validated; the default keeps the FP16-equivalent baseline based on measured performance. All sixteen CTest gates pass with the final native Windows ZLUDA runtime. See [the reversible game package](docs/windows-game.md) and @@ -67,7 +78,7 @@ d4r builds for RDNA3 and RDNA4. A newly built release compiles native DLSS 4 and | 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) | emulator only | +| 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 | @@ -106,6 +117,8 @@ The release zip works like an OptiScaler release: its contents go into the folde - an `d4r` folder with the shim, the CUDA bridge, ZLUDA, the ROCm 7.2.4 runtime, the native kernels, this game's `d4r.ini`, and NVIDIA's `nvngx_dlss.dll` (310.7) and `_nvngx.dll`. 2. 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` and `d3d12core.dll` together with `d4r/nvngx.dll`. The shim requires their matching resource-lifetime extension. + 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 zip's NVIDIA files and the 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)). ## Requirements (building from source) diff --git a/config/d4r.ini.default b/config/d4r.ini.default index bdd17db..e37e406 100644 --- a/config/d4r.ini.default +++ b/config/d4r.ini.default @@ -17,14 +17,20 @@ CompatDataDir = GameExe = [DLSS] -; Which DLSS network ("preset") to run. fps measured on an RX 7700 XT, Townfall 2560x1440, same street -; walk, for Quality / Balanced / Performance / Ultra Performance (FSR 4: 76 / 85 / 94 / 107): -; E (or CNN) = DLSS 3 CNN, the fastest at Quality..Performance 72 / 81 / 88 / 89 -; K (or DLSS4) = DLSS 4 transformer, sharper; best at Ultra Perf 69 / 77 / 84 / 95 -; M (or DLSS4.5) = DLSS 4.5 transformer, best image, slowest 52 / 60 / 70 / 90 -; F = the other CNN (same speed as E); A, B, C, D, G, J run the K network in this DLSS version; -; L (DLSS 4.5 Ultra Performance model) is not accelerated here and runs very slowly. -; auto = whatever the game/OptiScaler asks for +; DLSS model recommendations: +; +; Model | DLSS | Recommended modes | Notes +; ------+-------+---------------------------------+---------------------------------- +; K | 4 | DLAA / Quality / Balanced | Default; faster alternative to M/L +; M | 4.5 | Performance | More demanding on RDNA3 +; L | 4.5 | Ultra Performance | Especially 4K; experimental +; E | 3 CNN | Quality / Balanced / Performance | Legacy faster fallback +; F | 3 CNN | DLAA / Ultra Performance | Deprecated; prefer K or L +; +; Select the upscaling mode separately in game/OptiScaler. These are recommendations; +; other modes are allowed. L requires the L texture build. +; Aliases: CNN = E, DLSS4 = K, DLSS4.5 = M. A/B/C/D/G/J use K in this DLL version. +; auto = use the game/OptiScaler choice. Benchmarks: docs/performance.md. Model = K [Latency] ; How many frames old the upscaled image the game presents may be. @@ -42,6 +48,11 @@ FrameAge = 0 ; directory directly). Missing accuracy kernels use translated PTX, never the fast set. Slower; this ; aims for fidelity, but RTX image parity is not proven. (D4R_PREFER_ACCURACY) PreferAccuracy = false +; false: use original translated enc1/enc2 layers for L/M, retaining other native kernels. +; Reduces the native encoders' motion trails seen in Townfall; best paired with PreferAccuracy. +; Costs some GPU time. Manifest required for selective fallback; flat sets fall back entirely. +; (D4R_NATIVE_SWIN_ENCODERS) +NativeSwinEncoders = true ; Native RDNA3 / RDNA4 kernels for the DLSS 4 and 4.5 network layers (replace ZLUDA's translated code). ; fast = native kernels with relaxed numerics (unless PreferAccuracy is true) ; exact = a separately built directory with conservative FP8/f16 rounding; not an RTX parity guarantee @@ -60,6 +71,12 @@ IgnoreDenormals = true ImplicitMaxBlock = 256 [Interop] +; Experimental foreground motion coverage for thin geometry. 0 = original vectors; +; 1/2 = opaque-surface dilation radius in render pixels; both automatically fill sky gaps +; from the nearest foreground within 8 render pixels. Needs render- or display-resolution, unjittered motion vectors +; and same-frame linear VRAM inputs; unsupported features retain original vectors. +; Default off. May affect disocclusion edges. (D4R_MOTION_DILATION) +MotionVectorDilation = 0 ; Keep DLSS inputs and output in VRAM (vkd3d-proton Vulkan interop) instead of copying through ; system memory. Falls back automatically when the game's resources do not qualify. VramInterop = true @@ -67,19 +84,18 @@ VramInterop = true InputSync = true ; Faster hand-offs between the game and DLSS (same frame, same result). Need FrameAge = 0 and VRAM ; interop, and a runtime built from this checkout (scripts/install_d4r_runtime.sh). -; GpuWait: DLSS is queued ahead and starts on the GPU the moment the game's inputs are ready -; LinearInputs: DLSS reads the game's inputs in place (no input copies; needs GpuWait) +; Input readiness is checked with CPU sleeps; recorded-but-discarded lists never queue GPU waits. +; LinearInputs: DLSS reads the game's inputs in place once the input marker is ready ; DirectOutput: the result is written straight into the game-side buffer (preset K, and only while ; NGX runs the native output kernel; drops NVIDIA's "DLSS SDK" watermark) ; ElideNgxSync: removes NGX's internal CPU waits between its GPU stages ; EvalSync = false: no CPU wait between DLSS and the output copy -; MarkerPollUs: how often (microseconds) the CPU checks for the game's inputs without GpuWait -GpuWait = true +; MarkerPollUs: how often (microseconds) the CPU checks for the game's inputs LinearInputs = true DirectOutput = true ElideNgxSync = true EvalSync = false -MarkerPollUs = 20 +MarkerPollUs = 200 [Paths] ; auto = defaults of scripts/d4r_proton_env.sh @@ -133,4 +149,4 @@ LogDir = auto [Env] ; Anything else, passed through as environment variables (one per line): ; D4R_SHIM_SPLIT_TIMEOUT_MS = 200 -; D4R_SHIM_BLOCKING_SYNC = 1 ; yield CPU while waiting for DLSS output +; D4R_SHIM_BLOCKING_SYNC = 0 ; A/B only: restore context waits instead of default event queries/sleeps diff --git a/docs/architecture.md b/docs/architecture.md index 3a1533d..d1b40ba 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -12,7 +12,7 @@ d4r keeps NVIDIA's DLSS library unmodified and gives it what it expects: an NGX | CUDA bridge `nvcuda.dll` | `tools/wine_nvcuda_bridge.c` | A Wine builtin whose Windows exports forward to ZLUDA's Linux `libcuda.so`, plus d4r helpers (Vulkan memory import, asynchronous 2D copies, GPU-side waits, output redirect, linear textures) | | ZLUDA | `patches/zluda` | CUDA driver API on HIP; compiles NVIDIA's PTX to AMDGPU code; serves native kernels in place of PTX kernels | | Native kernels | `kernels/` | RDNA3 implementations of the DLSS network layers and parts of texture kernels | -| vkd3d-proton patch | `patches/vkd3d-proton` | Lets the shim split a D3D12 command list around the DLSS call | +| vkd3d-proton patch | `patches/vkd3d-proton` | Splits a D3D12 command list around DLSS and retains external resources through command allocator completion | | NVAPI identity | `tools/d4r_nvapi_identity.c` | Reports an Ada (sm_89) GPU so NGX selects the network weights matching the PTX ZLUDA compiles | ## One frame @@ -24,23 +24,22 @@ With VRAM interop and split frames (the defaults when the patched vkd3d-proton i - A frame marker. - A split. vkd3d-proton submits everything the game records after the DLSS call separately, gated on a timeline semaphore reaching this frame's number. - At the start of that second part, a copy of the DLSS result into the game's output texture. Linear R11G11B10, RGB10A2, and RGBA8/BGRA8 outputs are converted from RGBA16F on the GPU. sRGB formats continue through host staging to preserve their existing byte interpretation. -2. **The CUDA worker queues DLSS.** A worker thread owns the CUDA context. For each frame it queues on the GPU, without waiting on the CPU: - - a wait for the marker (`GpuWait`); +2. **The CUDA worker queues DLSS.** The prep thread checks the GPU-written input marker with short CPU sleeps. It drops recorded-but-unsubmitted frames when they time out or their feature retires, before any DLSS work is queued. The CUDA worker then queues: - the input copies into CUDA arrays, or none when NGX samples the buffers in place (`LinearInputs`); - NGX's evaluation (NGX's own CPU synchronisations are elided with `ElideNgxSync`); - the result, written straight into the output buffer by the native output kernel (`DirectOutput`, preset K) or copied there. 3. **Release.** When the GPU finishes, the worker signals the timeline semaphore and the rest of the game's frame runs, now with this frame's result. -A watchdog releases any frame that never completes (for example a command list that is recorded but never executed), so the game cannot stall on DLSS. +The watchdog reports delayed output but never signals an unfinished producer as ready. Retiring features remain registered until their CPU/CUDA jobs drain. The lifetime-aware vkd3d extension retains their staging buffers, imported buffers, conversion images and split semaphore through command-allocator reset or destruction after GPU completion. Discarded recordings are covered too; cleanup no longer relies on a 50 ms delay. -Without the vkd3d-proton patch the shim shows the newest finished result instead (one or more frames old). Without VRAM interop, inputs and output are staged through host memory. +The shim requires the matching lifetime-aware d3d12.dll and d3d12core.dll, even for host staging. It rejects older runtimes rather than recording references whose lifetime it cannot protect. FrameAge > 0 shows the newest finished result; without VRAM interop, inputs and output are staged through host memory. ## Why each piece exists - **CUDA NGX instead of D3D12 NGX.** NVIDIA's D3D12 path needs NVIDIA's driver. The CUDA path only needs a CUDA driver API, which ZLUDA provides. - **The Ada identity.** NGX picks network weights by GPU architecture. The weights have to match the PTX that ZLUDA compiles, which is sm_89 for DLSS 310. - **Native kernels.** Translating NVIDIA's warp-level matrix code is slow (register pressure, lane shuffles, per-instruction mode switches). Earlier translated K layers also produced non-finite outputs that the final kernel masked. Native layers avoid that path; the compiler now preserves FP16 denormal requirements too, which fixes the translated failure in recorded captures. Numerical and image-quality parity with RTX hardware remains unverified. -- **The GPU-side hand-offs.** With DLSS itself fast, most of the remaining gap to FSR 4 was the GPU idling while CPU threads waited on each other. Moving the waits onto the GPU raised GPU utilisation from about 95% to 99%. +- **Sleeping readiness checks.** Waiting for input submission before queueing DLSS avoids an uncancellable GPU wait. Explicit output event queries with short sleeps avoid HIP event/context waits that can consume a CPU core. Submillisecond waits use NtDelayExecution because MinGW std::sleep_for can truncate them to zero. Linear inputs and direct output remain available. ## Configuration @@ -53,7 +52,6 @@ Without the vkd3d-proton patch the shim shows the newest finished result instead | `[Kernels] NativeKernels` | `D4R_ZLUDA_NATIVE_DIR` | native kernel directory (`fast` / `exact` / `off`) | | `[Kernels] PreferAccuracy` | `D4R_PREFER_ACCURACY` | default off; accuracy variants for every native family and conservative translation/synchronization settings | | `[Interop] VramInterop` | `D4R_SHIM_VRAM_INTEROP` | keep inputs and output in VRAM | -| `[Interop] GpuWait` | `D4R_SHIM_GPU_WAIT` | GPU-side wait for the game's inputs | | `[Interop] LinearInputs` | `D4R_SHIM_LINEAR_INPUTS` | NGX samples the input buffers in place | | `[Interop] DirectOutput` | `D4R_SHIM_OUTPUT_DIRECT` | the output kernel writes the game-side buffer directly (preset K) | | `[Interop] ElideNgxSync` | `D4R_ELIDE_NGX_SYNC` | skip NGX's internal CPU waits | @@ -90,11 +88,11 @@ The release zip (`scripts/package_release.sh`) is unpacked into the folder that The launcher writes each run's logs to `~/.cache/d4r-dlss-captures/game--