Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da263ecf75 | ||
|
|
1952452830 | ||
|
|
bc31abdd50 | ||
|
|
00cb497ede | ||
|
|
db51da64ec |
No files matched your search
+2
-1
@@ -23,6 +23,7 @@ __pycache__/
|
|||||||
/Archive.zip
|
/Archive.zip
|
||||||
/vkd3d-proton.cache
|
/vkd3d-proton.cache
|
||||||
.vscode/
|
.vscode/
|
||||||
|
videos/
|
||||||
|
|
||||||
# Machine-specific notes, CI runner setup, local experiments, benchmark automation (kept locally, never published)
|
# Machine-specific notes, CI runner setup, local experiments, benchmark automation (kept locally, never published)
|
||||||
/private/
|
/private/
|
||||||
@@ -32,4 +33,4 @@ __pycache__/
|
|||||||
/kernels/out/
|
/kernels/out/
|
||||||
*.hsaco
|
*.hsaco
|
||||||
*.ptx
|
*.ptx
|
||||||
/kernels/extracted/
|
/kernels/extracted/
|
||||||
@@ -2,7 +2,11 @@
|
|||||||
|
|
||||||
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 code.
|
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 code.
|
||||||
|
|
||||||
It is an experiment. It has been tested on one GPU in two games, it depends on unreleased patches to ZLUDA and vkd3d-proton, and it needs NVIDIA files that are not part of this repository.
|
**Supported DLSS models:** DLSS 3 CNN (E), DLSS 4 transformer (K, default), and DLSS 4.5 transformer (M). DLSS 5 is not supported.
|
||||||
|
|
||||||
|
It is an experiment. It has been tested on one GPU in a handful of games, it depends on unreleased patches to ZLUDA and vkd3d-proton.
|
||||||
|
|
||||||
|
See [supported games](SUPPORTED_GAMES.md) for the tested games and DLSS models.
|
||||||
|
|
||||||
> **Not affiliated with NVIDIA or AMD.**
|
> **Not affiliated with NVIDIA or AMD.**
|
||||||
|
|
||||||
@@ -10,7 +14,7 @@ It is an experiment. It has been tested on one GPU in two games, it depends on u
|
|||||||
|
|
||||||
[DLSS Ultra Performance demo video](https://cdn.ayois.gay/dlss).
|
[DLSS Ultra Performance demo video](https://cdn.ayois.gay/dlss).
|
||||||
|
|
||||||
Radeon RX 7700 XT (RDNA3, gfx1101), SILENT HILL Townfall at 2560×1440, the same 62-second street walk for every run, frame rates from MangoHud frametime logs. Every DLSS result is presented in the frame it belongs to (no added latency).
|
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).
|
||||||
|
|
||||||
| Mode | DLSS 3 CNN (preset E) | DLSS 4 (preset K) | DLSS 4.5 (preset M) | FSR 4 |
|
| Mode | DLSS 3 CNN (preset E) | DLSS 4 (preset K) | DLSS 4.5 (preset M) | FSR 4 |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
@@ -25,7 +29,20 @@ On the same GPU DLSS starts at a disadvantage: its networks were designed for NV
|
|||||||
|
|
||||||
## Known issues
|
## Known issues
|
||||||
|
|
||||||
- Native upscaling can show visible artifacting.
|
- In some games native upscaling can show visible artifacting.
|
||||||
|
|
||||||
|
## GPU support
|
||||||
|
|
||||||
|
d4r needs RDNA3 (Radeon RX 7000 series), but full speed depends on the chip: the release ships its native kernels only for **gfx1101 (Navi 32)**, and on other GPUs DLSS 4 and 4.5 fall back to the much slower translated NVIDIA code.
|
||||||
|
|
||||||
|
| GPU | Chip | DLSS 3 CNN (E) | DLSS 4 (K) | DLSS 4.5 (M) |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| RX 7700 XT (tested), RX 7800 XT, RX 7700, Radeon PRO W7700 | gfx1101 | full speed | full speed | full speed |
|
||||||
|
| RX 7900 GRE / XT / XTX, Radeon PRO W7800 / W7900 | gfx1100 | full speed | runs without its transformer (worse image, slower) | about half speed |
|
||||||
|
| RX 7600 / 7600 XT, RX 7650 GRE | gfx1102 | full speed | runs without its transformer (worse image, slower) | about half speed |
|
||||||
|
| RDNA4, RDNA2 and older | | not supported | not supported | not supported |
|
||||||
|
|
||||||
|
Only the RX 7700 XT has been tested; the other gfx1101 cards use the same chip. Without the native kernels, NVIDIA's translated DLSS 4 layers produce invalid values and DLSS discards the network's output, so K loses the transformer's image quality. Preset E is unaffected on any RDNA3 GPU. The kernels can be built for gfx1100 and gfx1102 (`D4R_GPU_ARCHS` in `scripts/package_release.sh`), but those builds are untested.
|
||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
|
|
||||||
@@ -57,14 +74,14 @@ The release zip works like an OptiScaler release: its contents go into the folde
|
|||||||
1. Extract `d4r-<version>.zip` there. It contains:
|
1. Extract `d4r-<version>.zip` there. It contains:
|
||||||
- OptiScaler 0.9.4 as `dxgi.dll`, with an `OptiScaler.ini` set up for d4r;
|
- OptiScaler 0.9.4 as `dxgi.dll`, with an `OptiScaler.ini` set up for d4r;
|
||||||
- the d4r-patched vkd3d-proton (`d3d12.dll`, `d3d12core.dll`);
|
- the d4r-patched vkd3d-proton (`d3d12.dll`, `d3d12core.dll`);
|
||||||
- an `d4r` folder with the shim, the CUDA bridge, ZLUDA, the native kernels, this game's `d4r.ini`, and NVIDIA's `nvngx_dlss.dll` (310.7) and `_nvngx.dll`.
|
- 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%`.
|
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%`.
|
||||||
|
|
||||||
The only other requirement is ROCm's HIP runtime 7.x, installed from your 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)).
|
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)
|
## Requirements (building from source)
|
||||||
|
|
||||||
- Linux with an AMD RDNA3 GPU. The native kernels use gfx11 WMMA; they are built for gfx1101 by default (`D4R_GPU_ARCH` for other RDNA3 chips, untested). Without them DLSS still runs through ZLUDA, much more slowly.
|
- Linux with an AMD RDNA3 GPU. The native kernels use gfx11 WMMA; they are built for gfx1101 by default (`D4R_GPU_ARCH` for other RDNA3 chips, untested). Without them DLSS runs through ZLUDA alone: much more slowly, and DLSS 4 (K) without its transformer (see [GPU support](#gpu-support)).
|
||||||
- ROCm with HIP and its clang (tested with ROCm 7.2).
|
- ROCm with HIP and its clang (tested with ROCm 7.2).
|
||||||
- GE-Proton with OptiScaler integration (tested with GE-Proton11-3).
|
- 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.
|
- 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.
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# Supported games
|
||||||
|
|
||||||
|
The following DirectX 12 games run DLSS Super Resolution through d4r on the tested Radeon RX 7700 XT Linux setup with GE-Proton 11-3. All three documented DLSS models run in each game; image-quality issues are noted in the table.
|
||||||
|
|
||||||
|
| Game | E: DLSS 3 CNN | K: DLSS 4 | M: DLSS 4.5 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| SILENT HILL Townfall | Works | Works | Works |
|
||||||
|
| Ghost of Tsushima DIRECTOR'S CUT | Works | Works | Works |
|
||||||
|
| Ready or Not | Works | Works; visible graphical artifacts with hdr enabled | Works |
|
||||||
|
| Clair Obscur: Expedition 33 | Works | Works | Works |
|
||||||
|
| Marvel's Spider-Man: Miles Morales | Works | Works; minor artifacts when HDR enabled and while the game is paused | Works |
|
||||||
|
| The Last of Us Part I | Works | Works; visible graphical artifacts, including with HDR enabled | Works |
|
||||||
|
| Control Ultimate Edition | Works | Works | Works |
|
||||||
|
| Cyberpunk 2077 | Works; occasional visual artifacts during gameplay | Works | Works |
|
||||||
|
| Alan Wake 2 | Works | Works | Works |
|
||||||
|
| SILENT HILL 2 (2024) | Works | Works | Works |
|
||||||
|
| Subnautica 2 | Works | Works; visible artifacts at Quality (other quality modes are fine) | Works |
|
||||||
|
| Horizon Zero Dawn Remastered | Works; stutters | Works | Works |
|
||||||
|
| Ratchet & Clank: Rift Apart | Works; occasional artifacts in the pause menu | Works; minor artifacts with HDR enabled and occasionally in the pause menu | Works; occasional artifacts in the pause menu |
|
||||||
|
| Dying Light: The Beast | Works | Works | Works; small visual artifacts throughout |
|
||||||
|
|
||||||
|
General notes:
|
||||||
|
|
||||||
|
- Select DLSS in the game's graphics settings where it is offered. OptiScaler can also feed DLSS from a game's FSR or XeSS inputs, but those are a fallback for games that hide DLSS and can ghost or show a black screen.
|
||||||
|
- d4r supports DLSS Super Resolution only. Keep DLSS Ray Reconstruction off in games that offer it (such as Cyberpunk 2077 and Alan Wake 2); Control requests it whenever ray tracing is on and crashes.
|
||||||
|
- Saving settings from the OptiScaler overlay ("Save INI") rewrites `OptiScaler.ini` and resets d4r's values to `auto`, including frame generation (`[FrameGen] Enabled=false`, `FGInput=nofg`, `FGOutput=nofg`) and `[Inputs] EnableDlssInputs=true`. Restore them afterwards, or keep a copy of the original file.
|
||||||
|
|
||||||
|
Game-specific setup:
|
||||||
|
|
||||||
|
- **The Last of Us Part I:** the game's Streamline integration crashes on boot with OptiScaler. Rename `sl.common.dll` in the game folder to `sl.common.dll.bak` and select FSR 3.1 in the game's graphics settings; OptiScaler passes it to DLSS through d4r.
|
||||||
|
|
||||||
|
- **Control Ultimate Edition:** turn ray tracing off. With ray tracing on, the game requests DLSS Ray Reconstruction, which d4r does not support, and crashes when loading into gameplay.
|
||||||
|
|
||||||
|
- **Cyberpunk 2077:** GE-Proton 11-3 loads Wine's own ICU libraries instead of the game's, and the game aborts at launch (`unimplemented function icuuc.dll.u_setMemoryFunctions_65`). Add `WINEDLLOVERRIDES="icuuc,icuin=n,b"` to the launch options (fixed in GE-Proton 11-4).
|
||||||
|
|
||||||
|
- **Alan Wake 2:** with the game's own DLSS inputs, every model shows heavy ghosting. Set `AutoExposure=true` under `[InitFlags]` in `OptiScaler.ini` (or enable auto exposure in the OptiScaler overlay), or select FSR 3.1 in the game instead so OptiScaler passes FSR inputs to DLSS.
|
||||||
|
|
||||||
|
- **Horizon Zero Dawn Remastered:** the game loads `dxgi.dll` only from the Windows system folder, so OptiScaler is never loaded under that name. Rename `dxgi.dll` to `version.dll` and add `WINEDLLOVERRIDES="version=n,b"` to the launch options. d4r's `d3d12core.dll` is still picked up from the game folder.
|
||||||
|
|
||||||
|
- **Ratchet & Clank: Rift Apart:** set `Dxgi=false` in `OptiScaler.ini`. With ray tracing on, OptiScaler's DXGI spoofing crashes the game at startup on AMD GPUs; the `PROTON_FORCE_NVAPI=1` launch option already reports an NVIDIA GPU.
|
||||||
|
|
||||||
|
- **Dying Light: The Beast:** use DLSS inputs; with FSR or XeSS inputs the screen stays black. The game passes its motion vectors as `R16G16B16A16_TYPELESS`, which needs a d4r build from 2026-09-28 or later (older builds reject every frame and show a black screen).
|
||||||
|
|
||||||
|
Choose a model with `Model = E`, `Model = K`, or `Model = M` under `[DLSS]` in the game's `d4r/d4r.ini`. Model compatibility does not imply equal performance; see the [Townfall measurements](docs/performance.md) for measured frame rates.
|
||||||
@@ -20,7 +20,7 @@ d4r keeps NVIDIA's DLSS library unmodified and gives it what it expects: an NGX
|
|||||||
With VRAM interop and split frames (the defaults when the patched vkd3d-proton is present):
|
With VRAM interop and split frames (the defaults when the patched vkd3d-proton is present):
|
||||||
|
|
||||||
1. **The game records DLSS.** Its `EvaluateFeature` call reaches the shim with the game's command list. Into that list the shim records:
|
1. **The game records DLSS.** Its `EvaluateFeature` call reaches the shim with the game's command list. Into that list the shim records:
|
||||||
- Vulkan commands (vkd3d-proton interop) that copy colour, depth and motion vectors into buffers exported to HIP. Linear R11G11B10, RGB10A2, and RGBA8/BGRA8 colour inputs are blitted to RGBA16F on the way when the device supports the conversion.
|
- Vulkan commands (vkd3d-proton interop) that copy colour, depth, motion vectors and optional exposure into buffers exported to HIP. Linear R11G11B10, RGB10A2, and RGBA8/BGRA8 colour inputs are blitted to RGBA16F; multichannel float exposure is blitted to R32F using its first component when the device supports the conversion.
|
||||||
- A frame marker.
|
- 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.
|
- 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.
|
- 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.
|
||||||
@@ -75,7 +75,7 @@ The release zip (`scripts/package_release.sh`) is unpacked into the folder that
|
|||||||
**Settings.** When an `d4r.ini` sits next to the shim, the shim turns it into the same environment variables the developer launcher sets, with the release's defaults. A variable that is already set, for example in the launch options, wins. Settings for the Linux side (ZLUDA's and the bridge's) are handed to the bridge through `d4rSetEnv` right after it is loaded, before its first CUDA call.
|
**Settings.** When an `d4r.ini` sits next to the shim, the shim turns it into the same environment variables the developer launcher sets, with the release's defaults. A variable that is already set, for example in the launch options, wins. Settings for the Linux side (ZLUDA's and the bridge's) are handed to the bridge through `d4rSetEnv` right after it is loaded, before its first CUDA call.
|
||||||
|
|
||||||
**Loading ROCm.** Steam runs GE-Proton 11 inside its container runtime (SteamLinuxRuntime_4), whose library search path has no ROCm. The bridge therefore:
|
**Loading ROCm.** Steam runs GE-Proton 11 inside its container runtime (SteamLinuxRuntime_4), whose library search path has no ROCm. The bridge therefore:
|
||||||
- loads HIP, HSA and comgr by path, from `RocmDir` (default `/opt/rocm`);
|
- loads HIP, HSA and comgr by path, from `RocmDir` (default: the release's bundled `d4r/rocm`, else `/opt/rocm`);
|
||||||
- looks up any dependency they lack in the host's library directories, which the container mounts under `/run/host`;
|
- looks up any dependency they lack in the host's library directories, which the container mounts under `/run/host`;
|
||||||
- sets `XDG_CACHE_HOME` only while ZLUDA initialises, so ZLUDA's kernel cache goes to `~/.cache/d4r` without affecting the rest of the game.
|
- sets `XDG_CACHE_HOME` only while ZLUDA initialises, so ZLUDA's kernel cache goes to `~/.cache/d4r` without affecting the rest of the game.
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -84,6 +84,7 @@ In the game, pick DLSS as the upscaler (OptiScaler intercepts it). Every setting
|
|||||||
## 7. Package a release
|
## 7. Package a release
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
scripts/fetch_rocm_runtime.sh # -> ~/.cache/d4r-rocm-runtime (AMD's ROCm 7.2.4 runtime, checksummed)
|
||||||
D4R_OPTISCALER=/path/to/OptiScaler_0.9.4.7z \
|
D4R_OPTISCALER=/path/to/OptiScaler_0.9.4.7z \
|
||||||
D4R_DLSS_DLLS=/path/to/310.7/nvngx_dlss.dll:/path/to/310.9/nvngx_dlss.dll \
|
D4R_DLSS_DLLS=/path/to/310.7/nvngx_dlss.dll:/path/to/310.9/nvngx_dlss.dll \
|
||||||
D4R_BUNDLE_DLSS=/path/to/nvngx_dlss.dll D4R_BUNDLE_NGX=/path/to/_nvngx.dll D4R_BUNDLE_TEX=kernels/out/native \
|
D4R_BUNDLE_DLSS=/path/to/nvngx_dlss.dll D4R_BUNDLE_NGX=/path/to/_nvngx.dll D4R_BUNDLE_TEX=kernels/out/native \
|
||||||
@@ -96,7 +97,7 @@ The script:
|
|||||||
- writes the kernel manifest from the DLLs you list (it records hashes of their PTX, nothing else);
|
- writes the kernel manifest from the DLLs you list (it records hashes of their PTX, nothing else);
|
||||||
- stages OptiScaler as `dxgi.dll` with the settings in `packaging/optiscaler.settings`;
|
- stages OptiScaler as `dxgi.dll` with the settings in `packaging/optiscaler.settings`;
|
||||||
- adds NVIDIA's two DLLs and the texture kernels (built from NVIDIA's PTX by `kernels/build.sh tex`);
|
- adds NVIDIA's two DLLs and the texture kernels (built from NVIDIA's PTX by `kernels/build.sh tex`);
|
||||||
- zips the result together with the ZLUDA and vkd3d-proton builds, `packaging/d4r.ini`, the licenses and the patches.
|
- zips the result together with the ZLUDA and vkd3d-proton builds, the ROCm runtime (as `d4r/rocm`), `packaging/d4r.ini`, the licenses and the patches.
|
||||||
|
|
||||||
The NVIDIA files are not covered by d4r's license; redistributing them is up to whoever publishes the zip. `D4R_BUNDLE_NVIDIA=0` builds `d4r-<version>-nonvidia.zip` without them and without the texture kernels. [architecture.md](architecture.md#portable-installs-the-release-zip) describes how the installed files work together.
|
The NVIDIA files are not covered by d4r's license; redistributing them is up to whoever publishes the zip. `D4R_BUNDLE_NVIDIA=0` builds `d4r-<version>-nonvidia.zip` without them and without the texture kernels. [architecture.md](architecture.md#portable-installs-the-release-zip) describes how the installed files work together.
|
||||||
|
|
||||||
|
|||||||
@@ -14,9 +14,8 @@ What you need
|
|||||||
-------------
|
-------------
|
||||||
- An AMD Radeon RX 7000 series GPU (RDNA3). The fast kernels are built for gfx1101
|
- An AMD Radeon RX 7000 series GPU (RDNA3). The fast kernels are built for gfx1101
|
||||||
(RX 7700 XT, RX 7800 XT). Other RDNA3 cards run DLSS without them, which is much slower.
|
(RX 7700 XT, RX 7800 XT). Other RDNA3 cards run DLSS without them, which is much slower.
|
||||||
- Linux with ROCm's HIP runtime, version 7 (the file libamdhip64.so.7). ROCm 7.2 was tested.
|
- Linux with the amdgpu kernel driver (/dev/kfd). ROCm itself is not needed: the zip includes the
|
||||||
Arch Linux: the hip-runtime-amd package. Ubuntu, Fedora and others: the ROCm HIP runtime package
|
ROCm 7.2.4 runtime in d4r/rocm. RocmDir in d4r/d4r.ini selects another ROCm installation instead.
|
||||||
of your distribution or of AMD's ROCm repository.
|
|
||||||
- GE-Proton 11 (tested: GE-Proton11-3), selected for the game in Steam.
|
- GE-Proton 11 (tested: GE-Proton11-3), selected for the game in Steam.
|
||||||
@clean - Two NVIDIA files, which this zip does not include:
|
@clean - Two NVIDIA files, which this zip does not include:
|
||||||
@clean - nvngx_dlss.dll, the DLSS library, version 310.7 or 310.9 (tested: 310.7.0 and 310.9.1).
|
@clean - nvngx_dlss.dll, the DLSS library, version 310.7 or 310.9 (tested: 310.7.0 and 310.9.1).
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ It is an early release, tested on one GPU (RX 7700 XT) in SILENT HILL Townfall a
|
|||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- AMD Radeon RX 7000 series. The fast kernels are built for gfx1101 (RX 7700 XT, RX 7800 XT). Other RDNA3 cards run DLSS without them, which is much slower.
|
- AMD Radeon RX 7000 series. The fast kernels are built for gfx1101 (RX 7700 XT, RX 7800 XT). Other RDNA3 cards run DLSS without them, which is much slower.
|
||||||
- The ROCm HIP runtime 7.x (`libamdhip64.so.7`), from your distribution. ROCm 7.2 was tested.
|
- The amdgpu kernel driver (`/dev/kfd`). The ROCm 7.2.4 runtime is included in `d4r/rocm`; nothing needs to be installed.
|
||||||
- GE-Proton 11 (tested: GE-Proton11-3).
|
- GE-Proton 11 (tested: GE-Proton11-3).
|
||||||
|
|
||||||
## What is in the zip
|
## What is in the zip
|
||||||
@@ -29,6 +29,7 @@ It is an early release, tested on one GPU (RX 7700 XT) in SILENT HILL Townfall a
|
|||||||
- `d4r/d4r.ini` holds the settings for that game. The default model is DLSS 4 (K); E (DLSS 3 CNN) and M (DLSS 4.5) are one line away.
|
- `d4r/d4r.ini` holds the settings for that game. The default model is DLSS 4 (K); E (DLSS 3 CNN) and M (DLSS 4.5) are one line away.
|
||||||
- **NVIDIA:** DLSS 310.7.0 (`nvngx_dlss.dll`), the NGX runtime from driver 596.36 (`_nvngx.dll`), and five DLSS kernels built from NVIDIA's code with parts replaced by d4r's.
|
- **NVIDIA:** DLSS 310.7.0 (`nvngx_dlss.dll`), the NGX runtime from driver 596.36 (`_nvngx.dll`), and five DLSS kernels built from NVIDIA's code with parts replaced by d4r's.
|
||||||
- **ZLUDA** with d4r's patches: CUDA on ROCm.
|
- **ZLUDA** with d4r's patches: CUDA on ROCm.
|
||||||
|
- **ROCm 7.2.4 runtime** (HIP, HSA, comgr), unmodified, from AMD's Ubuntu 22.04 packages.
|
||||||
- **vkd3d-proton** with d4r's patch, so every frame shows its own DLSS result (no added latency).
|
- **vkd3d-proton** with d4r's patch, so every frame shows its own DLSS result (no added latency).
|
||||||
- **OptiScaler 0.9.4**, unmodified, set up to hand the game's DLSS calls to d4r.
|
- **OptiScaler 0.9.4**, unmodified, set up to hand the game's DLSS calls to d4r.
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,16 @@ Third-party software, unmodified or patched by d4r:
|
|||||||
commit @VKD3D_COMMIT@ with d4r/source/patches/vkd3d-proton/0001 applied,
|
commit @VKD3D_COMMIT@ with d4r/source/patches/vkd3d-proton/0001 applied,
|
||||||
built with its build-win64.txt cross file (meson --buildtype release).
|
built with its build-win64.txt cross file (meson --buildtype release).
|
||||||
Licenses: d4r/licenses/vkd3d-proton-*.txt
|
Licenses: d4r/licenses/vkd3d-proton-*.txt
|
||||||
|
d4r/rocm/lib/ ROCm 7.2.4 runtime, unmodified, from AMD's Ubuntu 22.04 packages
|
||||||
|
(https://repo.radeon.com/rocm/apt/7.2.4): HIP (hip-runtime-amd, MIT),
|
||||||
|
comgr (NCSA; contains LLVM, Apache License 2.0 with LLVM exceptions),
|
||||||
|
ROCr (hsa-rocr, NCSA) and rocprofiler-register (MIT).
|
||||||
|
Sources: https://github.com/ROCm/rocm-systems and https://github.com/ROCm/llvm-project,
|
||||||
|
tag rocm-7.2.4. Licenses: d4r/licenses/ROCm-*
|
||||||
|
libelf.so.1 (elfutils 0.186, GNU LGPL 3.0 or GPL 2.0) and libnuma.so.1 (numactl 2.0.14,
|
||||||
|
GNU LGPL 2.1), unmodified, from Ubuntu 22.04's libelf1 and libnuma1 packages; their
|
||||||
|
sources are Ubuntu's elfutils 0.186-1ubuntu0.1 and numactl 2.0.14-3ubuntu2 source packages.
|
||||||
|
Licenses: d4r/licenses/ROCm-lib*-copyright.txt
|
||||||
dxgi.dll OptiScaler 0.9.4 (GNU GPL 3.0), unmodified OptiScaler.dll renamed to dxgi.dll.
|
dxgi.dll OptiScaler 0.9.4 (GNU GPL 3.0), unmodified OptiScaler.dll renamed to dxgi.dll.
|
||||||
Source: https://github.com/optiscaler/OptiScaler/tree/v0.9.4
|
Source: https://github.com/optiscaler/OptiScaler/tree/v0.9.4
|
||||||
License: d4r/licenses/OptiScaler-LICENSE-GPL-3.0.txt
|
License: d4r/licenses/OptiScaler-LICENSE-GPL-3.0.txt
|
||||||
|
|||||||
@@ -4,10 +4,11 @@
|
|||||||
# usage: sh d4r/d4r-check.sh [GAME_FOLDER] [ROCM_DIR]
|
# usage: sh d4r/d4r-check.sh [GAME_FOLDER] [ROCM_DIR]
|
||||||
GAME="${1:-.}"
|
GAME="${1:-.}"
|
||||||
D4R="$GAME/d4r"
|
D4R="$GAME/d4r"
|
||||||
# ROCm: the argument, else d4r.ini's RocmDir, else D4R_ROCM_DIR, else /opt/rocm
|
# ROCm: the argument, else d4r.ini's RocmDir, else the bundled d4r/rocm, else D4R_ROCM_DIR, else /opt/rocm
|
||||||
ini_rocm=$(sed -n 's/^[[:space:]]*RocmDir[[:space:]]*=[[:space:]]*\([^;#]*\).*/\1/p' "$D4R/d4r.ini" 2>/dev/null | tail -1 | sed 's/[[:space:]]*$//')
|
ini_rocm=$(sed -n 's/^[[:space:]]*RocmDir[[:space:]]*=[[:space:]]*\([^;#]*\).*/\1/p' "$D4R/d4r.ini" 2>/dev/null | tail -1 | sed 's/[[:space:]]*$//')
|
||||||
[ "$ini_rocm" = auto ] && ini_rocm=
|
[ "$ini_rocm" = auto ] && ini_rocm=
|
||||||
case "$ini_rocm" in "~/"*) ini_rocm="$HOME/${ini_rocm#\~/}" ;; esac
|
case "$ini_rocm" in "~/"*) ini_rocm="$HOME/${ini_rocm#\~/}" ;; esac
|
||||||
|
[ -z "$ini_rocm" ] && [ -d "$D4R/rocm/lib" ] && ini_rocm="$D4R/rocm"
|
||||||
ROCM="${2:-${ini_rocm:-${D4R_ROCM_DIR:-/opt/rocm}}}"
|
ROCM="${2:-${ini_rocm:-${D4R_ROCM_DIR:-/opt/rocm}}}"
|
||||||
problems=0
|
problems=0
|
||||||
ok() { printf ' ok %s\n' "$1"; }
|
ok() { printf ' ok %s\n' "$1"; }
|
||||||
@@ -35,7 +36,7 @@ found=
|
|||||||
for dir in "$ROCM/lib" /opt/rocm/lib /usr/lib /usr/lib64 /usr/lib/x86_64-linux-gnu; do
|
for dir in "$ROCM/lib" /opt/rocm/lib /usr/lib /usr/lib64 /usr/lib/x86_64-linux-gnu; do
|
||||||
[ -e "$dir/libamdhip64.so.7" ] && { found="$dir"; break; }
|
[ -e "$dir/libamdhip64.so.7" ] && { found="$dir"; break; }
|
||||||
done
|
done
|
||||||
[ -n "$found" ] && ok "ROCm HIP runtime ($found/libamdhip64.so.7)" || bad "ROCm HIP runtime 7.x (libamdhip64.so.7); install your distribution's HIP runtime package"
|
[ -n "$found" ] && ok "ROCm HIP runtime ($found/libamdhip64.so.7)" || bad "ROCm HIP runtime 7.x (libamdhip64.so.7); re-extract the d4r zip, which includes it in d4r/rocm"
|
||||||
[ -e /dev/kfd ] && ok "/dev/kfd (ROCm compute device)" || bad "/dev/kfd: the amdgpu compute interface is not available"
|
[ -e /dev/kfd ] && ok "/dev/kfd (ROCm compute device)" || bad "/dev/kfd: the amdgpu compute interface is not available"
|
||||||
target=""
|
target=""
|
||||||
for props in /sys/class/kfd/kfd/topology/nodes/*/properties; do
|
for props in /sys/class/kfd/kfd/topology/nodes/*/properties; do
|
||||||
|
|||||||
+1
-1
@@ -39,7 +39,7 @@ DirectOutput = auto
|
|||||||
|
|
||||||
[Paths]
|
[Paths]
|
||||||
; Linux paths.
|
; Linux paths.
|
||||||
; ROCm installation (default /opt/rocm)
|
; ROCm installation (default: the ROCm runtime in d4r\rocm, else /opt/rocm)
|
||||||
RocmDir = auto
|
RocmDir = auto
|
||||||
; Cache for the compiled DLSS kernels (default ~/.cache/d4r). The first launch fills it.
|
; Cache for the compiled DLSS kernels (default ~/.cache/d4r). The first launch fills it.
|
||||||
CacheDir = auto
|
CacheDir = auto
|
||||||
|
|||||||
Executable
+63
@@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Fetches the ROCm user-space runtime the release bundles in d4r/rocm: HIP, HSA, comgr and
|
||||||
|
# rocprofiler-register from AMD's ROCm 7.2.4 packages for Ubuntu 22.04, plus the libelf and libnuma
|
||||||
|
# they need, which Steam's container runtime lacks. These builds need only glibc 2.34, so they run
|
||||||
|
# in SteamLinuxRuntime_4 on any distribution (a rolling distribution's ROCm may need a newer glibc).
|
||||||
|
#
|
||||||
|
# usage: scripts/fetch_rocm_runtime.sh [OUT_DIR] (default: ~/.cache/d4r-rocm-runtime)
|
||||||
|
# Result: OUT_DIR/lib/<soname> for each library and OUT_DIR/licenses/.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
OUT="$(realpath -m "${1:-$HOME/.cache/d4r-rocm-runtime}")"
|
||||||
|
AMD=https://repo.radeon.com/rocm/apt/7.2.4/pool/main
|
||||||
|
UBUNTU=http://archive.ubuntu.com/ubuntu/pool/main
|
||||||
|
# url sha256
|
||||||
|
PACKAGES=(
|
||||||
|
"$AMD/h/hip-runtime-amd/hip-runtime-amd_7.2.53211.70204-93~22.04_amd64.deb 2b0fc15d4456bc35370f33672b202b43f2af1aceac47a42c22d1370b2f46968f"
|
||||||
|
"$AMD/c/comgr/comgr_3.0.0.70204-93~22.04_amd64.deb 58460d117131369a248d334ba749609abea8591cd05752ac572c9a10f31ac006"
|
||||||
|
"$AMD/h/hsa-rocr/hsa-rocr_1.18.0.70204-93~22.04_amd64.deb 7fccbb91e38fadd126538f15e02c66e20db717a60eb03f2a045e47497bf67405"
|
||||||
|
"$AMD/r/rocprofiler-register/rocprofiler-register_0.6.0.70204-93~22.04_amd64.deb c2780e37a7217329b5d3d40e682ae6979481b1a59b86415a0cdf849b9c142bfd"
|
||||||
|
"$UBUNTU/e/elfutils/libelf1_0.186-1ubuntu0.1_amd64.deb 2cc781614b045b450a1e073ee3b0902f393ee58ede1d2ea4526ad805fcccf538"
|
||||||
|
"$UBUNTU/n/numactl/libnuma1_2.0.14-3ubuntu2_amd64.deb 0721c89001fbbd1ada23e89da5d60e762763c1a7b3dc814a2e9a518480a8043d"
|
||||||
|
)
|
||||||
|
# soname, the file it names inside the packages
|
||||||
|
LIBRARIES=(
|
||||||
|
"libamdhip64.so.7 opt/rocm-7.2.4/lib/libamdhip64.so.7"
|
||||||
|
"libamd_comgr.so.3 opt/rocm-7.2.4/lib/libamd_comgr.so.3"
|
||||||
|
"libhsa-runtime64.so.1 opt/rocm-7.2.4/lib/libhsa-runtime64.so.1"
|
||||||
|
"librocprofiler-register.so.0 opt/rocm-7.2.4/lib/librocprofiler-register.so.0"
|
||||||
|
"libelf.so.1 usr/lib/x86_64-linux-gnu/libelf.so.1"
|
||||||
|
"libnuma.so.1 usr/lib/x86_64-linux-gnu/libnuma.so.1"
|
||||||
|
)
|
||||||
|
LICENSES=(
|
||||||
|
"HIP-LICENSE.md opt/rocm-7.2.4/share/doc/hip/LICENSE.md"
|
||||||
|
"comgr-LICENSE.txt opt/rocm-7.2.4/share/doc/amd_comgr/LICENSE.txt"
|
||||||
|
"ROCr-LICENSE.md opt/rocm-7.2.4/share/doc/hsa-rocr/LICENSE.md"
|
||||||
|
"rocprofiler-register-LICENSE.md opt/rocm-7.2.4/share/doc/rocprofiler-register/LICENSE.md"
|
||||||
|
"libelf-copyright.txt usr/share/doc/libelf1/copyright"
|
||||||
|
"libnuma-copyright.txt usr/share/doc/libnuma1/copyright"
|
||||||
|
)
|
||||||
|
|
||||||
|
TMP="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$TMP"' EXIT
|
||||||
|
mkdir -p "$TMP/root"
|
||||||
|
for entry in "${PACKAGES[@]}"; do
|
||||||
|
read -r url sha <<< "$entry"
|
||||||
|
deb="$TMP/$(basename "$url")"
|
||||||
|
curl -fsSL -o "$deb" "$url"
|
||||||
|
echo "$sha $deb" | sha256sum -c --quiet
|
||||||
|
data="$(ar t "$deb" | grep '^data\.tar')"
|
||||||
|
ar p "$deb" "$data" | tar -x -C "$TMP/root" -f - --use-compress-program="$(case "$data" in *.zst) echo zstd -d ;; *.xz) echo xz -d ;; *) echo gzip -d ;; esac)"
|
||||||
|
done
|
||||||
|
|
||||||
|
rm -rf "$OUT"
|
||||||
|
mkdir -p "$OUT/lib" "$OUT/licenses"
|
||||||
|
for entry in "${LIBRARIES[@]}"; do
|
||||||
|
read -r soname path <<< "$entry"
|
||||||
|
cp -L "$TMP/root/$path" "$OUT/lib/$soname"
|
||||||
|
done
|
||||||
|
for entry in "${LICENSES[@]}"; do
|
||||||
|
read -r name path <<< "$entry"
|
||||||
|
cp "$TMP/root/$path" "$OUT/licenses/$name"
|
||||||
|
done
|
||||||
|
printf 'ROCm runtime in %s (%s)\n' "$OUT" "$(du -sh "$OUT" | cut -f1)"
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
# D4R_ZLUDA_DIR ZLUDA build with patches/zluda applied (libnvcuda.so) default ~/.cache/d4r-zluda-current
|
# D4R_ZLUDA_DIR ZLUDA build with patches/zluda applied (libnvcuda.so) default ~/.cache/d4r-zluda-current
|
||||||
# D4R_VKD3D_DIR d4r-patched vkd3d-proton (d3d12.dll, d3d12core.dll) default ~/.cache/d4r-vkd3d-d4r
|
# D4R_VKD3D_DIR d4r-patched vkd3d-proton (d3d12.dll, d3d12core.dll) default ~/.cache/d4r-vkd3d-d4r
|
||||||
# D4R_ROCM_DIR ROCm with clang, for the kernels default /opt/rocm
|
# D4R_ROCM_DIR ROCm with clang, for the kernels default /opt/rocm
|
||||||
|
# D4R_ROCM_RUNTIME ROCm runtime bundled as d4r/rocm (scripts/fetch_rocm_runtime.sh)
|
||||||
|
# default ~/.cache/d4r-rocm-runtime
|
||||||
# D4R_GPU_ARCHS GPU targets to build kernels for default gfx1101
|
# D4R_GPU_ARCHS GPU targets to build kernels for default gfx1101
|
||||||
# D4R_OPTISCALER_LICENSE OptiScaler's LICENSE (GPL-3.0) text; default: the system's SPDX copy
|
# D4R_OPTISCALER_LICENSE OptiScaler's LICENSE (GPL-3.0) text; default: the system's SPDX copy
|
||||||
# D4R_VKD3D_SRC vkd3d-proton source checkout, for its license files default ~/.cache/d4r-vkd3d-proton
|
# D4R_VKD3D_SRC vkd3d-proton source checkout, for its license files default ~/.cache/d4r-vkd3d-proton
|
||||||
@@ -34,11 +36,12 @@ STAGE="$OUT/$NAME"
|
|||||||
ZLUDA="${D4R_ZLUDA_DIR:-$HOME/.cache/d4r-zluda-current}"
|
ZLUDA="${D4R_ZLUDA_DIR:-$HOME/.cache/d4r-zluda-current}"
|
||||||
VKD3D="${D4R_VKD3D_DIR:-$HOME/.cache/d4r-vkd3d-d4r}"
|
VKD3D="${D4R_VKD3D_DIR:-$HOME/.cache/d4r-vkd3d-d4r}"
|
||||||
ROCM="${D4R_ROCM_DIR:-/opt/rocm}"
|
ROCM="${D4R_ROCM_DIR:-/opt/rocm}"
|
||||||
|
ROCM_RUNTIME="${D4R_ROCM_RUNTIME:-$HOME/.cache/d4r-rocm-runtime}"
|
||||||
ARCHS="${D4R_GPU_ARCHS:-gfx1101}"
|
ARCHS="${D4R_GPU_ARCHS:-gfx1101}"
|
||||||
: "${D4R_OPTISCALER:?set D4R_OPTISCALER to the OptiScaler release archive or folder}"
|
: "${D4R_OPTISCALER:?set D4R_OPTISCALER to the OptiScaler release archive or folder}"
|
||||||
: "${D4R_DLSS_DLLS:?set D4R_DLSS_DLLS to the nvngx_dlss.dll files the kernel manifest accepts}"
|
: "${D4R_DLSS_DLLS:?set D4R_DLSS_DLLS to the nvngx_dlss.dll files the kernel manifest accepts}"
|
||||||
for f in "$ZLUDA/libnvcuda.so" "$VKD3D/d3d12.dll" "$VKD3D/d3d12core.dll"; do
|
for f in "$ZLUDA/libnvcuda.so" "$VKD3D/d3d12.dll" "$VKD3D/d3d12core.dll" "$ROCM_RUNTIME/lib/libamdhip64.so.7"; do
|
||||||
[[ -f "$f" ]] || { echo "missing $f" >&2; exit 2; }
|
[[ -f "$f" ]] || { echo "missing $f (the ROCm runtime comes from scripts/fetch_rocm_runtime.sh)" >&2; exit 2; }
|
||||||
done
|
done
|
||||||
|
|
||||||
# D4R_SKIP_BUILD=1 packages the shim and bridge already in build/ (e.g. the binaries that were tested)
|
# D4R_SKIP_BUILD=1 packages the shim and bridge already in build/ (e.g. the binaries that were tested)
|
||||||
@@ -48,7 +51,7 @@ if [[ "${D4R_SKIP_BUILD:-0}" != 1 ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
rm -rf "$STAGE"
|
rm -rf "$STAGE"
|
||||||
mkdir -p "$STAGE/d4r/zluda" "$STAGE/d4r/ngx" "$STAGE/d4r/kernels" "$STAGE/d4r/licenses" "$STAGE/d4r/source"
|
mkdir -p "$STAGE/d4r/zluda" "$STAGE/d4r/rocm" "$STAGE/d4r/ngx" "$STAGE/d4r/kernels" "$STAGE/d4r/licenses" "$STAGE/d4r/source"
|
||||||
TMP="$(mktemp -d)"
|
TMP="$(mktemp -d)"
|
||||||
trap 'rm -rf "$TMP"' EXIT
|
trap 'rm -rf "$TMP"' EXIT
|
||||||
|
|
||||||
@@ -94,6 +97,7 @@ cp "$VKD3D/d3d12.dll" "$VKD3D/d3d12core.dll" "$STAGE/"
|
|||||||
cp "$ROOT/build/d4r_nvngx.dll" "$STAGE/d4r/nvngx.dll"
|
cp "$ROOT/build/d4r_nvngx.dll" "$STAGE/d4r/nvngx.dll"
|
||||||
cp "$ROOT/build/wine-nvcuda/x86_64-unix/nvcuda.dll.so" "$STAGE/d4r/nvcuda.dll"
|
cp "$ROOT/build/wine-nvcuda/x86_64-unix/nvcuda.dll.so" "$STAGE/d4r/nvcuda.dll"
|
||||||
cp "$ZLUDA/libnvcuda.so" "$STAGE/d4r/zluda/libcuda.so"
|
cp "$ZLUDA/libnvcuda.so" "$STAGE/d4r/zluda/libcuda.so"
|
||||||
|
cp -r "$ROCM_RUNTIME/lib" "$STAGE/d4r/rocm/lib"
|
||||||
cp "$ROOT/packaging/d4r.ini" "$STAGE/d4r/d4r.ini"
|
cp "$ROOT/packaging/d4r.ini" "$STAGE/d4r/d4r.ini"
|
||||||
cp "$ROOT/packaging/d4r-check.sh" "$STAGE/d4r/d4r-check.sh"
|
cp "$ROOT/packaging/d4r-check.sh" "$STAGE/d4r/d4r-check.sh"
|
||||||
if [[ "$VARIANT" == full ]]; then
|
if [[ "$VARIANT" == full ]]; then
|
||||||
@@ -128,6 +132,7 @@ cp "$ZLUDA_SRC/ext/llvm-project/llvm/LICENSE.TXT" "$L/LLVM-LICENSE.txt"
|
|||||||
cp "$VKD3D_SRC/LICENSE" "$L/vkd3d-proton-LICENSE.txt"
|
cp "$VKD3D_SRC/LICENSE" "$L/vkd3d-proton-LICENSE.txt"
|
||||||
cp "$VKD3D_SRC/COPYING" "$L/vkd3d-proton-COPYING.txt"
|
cp "$VKD3D_SRC/COPYING" "$L/vkd3d-proton-COPYING.txt"
|
||||||
cp "${D4R_OPTISCALER_LICENSE:-/usr/share/licenses/spdx/GPL-3.0-only.txt}" "$L/OptiScaler-LICENSE-GPL-3.0.txt"
|
cp "${D4R_OPTISCALER_LICENSE:-/usr/share/licenses/spdx/GPL-3.0-only.txt}" "$L/OptiScaler-LICENSE-GPL-3.0.txt"
|
||||||
|
for f in "$ROCM_RUNTIME"/licenses/*; do cp "$f" "$L/ROCm-$(basename "$f")"; done
|
||||||
mkdir -p "$STAGE/d4r/source/patches"
|
mkdir -p "$STAGE/d4r/source/patches"
|
||||||
cp -r "$ROOT/patches/zluda" "$ROOT/patches/vkd3d-proton" "$STAGE/d4r/source/patches/"
|
cp -r "$ROOT/patches/zluda" "$ROOT/patches/vkd3d-proton" "$STAGE/d4r/source/patches/"
|
||||||
ZLUDA_COMMIT="$(git -C "$ZLUDA_SRC" rev-parse HEAD 2>/dev/null || echo unknown)"
|
ZLUDA_COMMIT="$(git -C "$ZLUDA_SRC" rev-parse HEAD 2>/dev/null || echo unknown)"
|
||||||
|
|||||||
@@ -52,6 +52,35 @@ static ID3D12Fence* g_fence;
|
|||||||
static HANDLE g_event;
|
static HANDLE g_event;
|
||||||
static UINT64 g_fenceValue;
|
static UINT64 g_fenceValue;
|
||||||
|
|
||||||
|
static unsigned long long process_vram_kib()
|
||||||
|
{
|
||||||
|
std::vector<unsigned long long> clients;
|
||||||
|
unsigned long long total = 0;
|
||||||
|
for (int fd = 0; fd < 4096; ++fd)
|
||||||
|
{
|
||||||
|
char path[64];
|
||||||
|
std::snprintf(path, sizeof(path), "Z:\\proc\\self\\fdinfo\\%d", fd);
|
||||||
|
FILE* file = std::fopen(path, "r");
|
||||||
|
if (file == nullptr)
|
||||||
|
continue;
|
||||||
|
unsigned long long client = 0, vram = 0;
|
||||||
|
bool drm = false;
|
||||||
|
char line[256];
|
||||||
|
while (std::fgets(line, sizeof(line), file) != nullptr)
|
||||||
|
if (std::sscanf(line, "drm-client-id: %llu", &client) == 1)
|
||||||
|
drm = true;
|
||||||
|
else
|
||||||
|
std::sscanf(line, "drm-memory-vram: %llu", &vram);
|
||||||
|
std::fclose(file);
|
||||||
|
if (drm && std::find(clients.begin(), clients.end(), client) == clients.end())
|
||||||
|
{
|
||||||
|
clients.push_back(client);
|
||||||
|
total += vram;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
static bool check(HRESULT hr, const char* what)
|
static bool check(HRESULT hr, const char* what)
|
||||||
{
|
{
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
@@ -385,6 +414,7 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
const bool qualityScene = std::getenv("D4R_HARNESS_QUALITY_SCENE") != nullptr;
|
const bool qualityScene = std::getenv("D4R_HARNESS_QUALITY_SCENE") != nullptr;
|
||||||
const bool poleScene = std::getenv("D4R_HARNESS_POLE_SCENE") != nullptr;
|
const bool poleScene = std::getenv("D4R_HARNESS_POLE_SCENE") != nullptr;
|
||||||
|
const bool exposureRGBA32 = std::getenv("D4R_HARNESS_EXPOSURE_RGBA32") != nullptr;
|
||||||
const char* replayDir = std::getenv("D4R_HARNESS_REPLAY_DIR");
|
const char* replayDir = std::getenv("D4R_HARNESS_REPLAY_DIR");
|
||||||
const bool rgba8 = std::getenv("D4R_HARNESS_RGBA8") != nullptr;
|
const bool rgba8 = std::getenv("D4R_HARNESS_RGBA8") != nullptr;
|
||||||
if (rgba8 && (qualityScene || motionScene || jitterScene || replayDir != nullptr))
|
if (rgba8 && (qualityScene || motionScene || jitterScene || replayDir != nullptr))
|
||||||
@@ -469,6 +499,7 @@ int main(int argc, char** argv)
|
|||||||
const UINT motionWidth = motionHighRes ? outWidth : resourceWidth, motionHeight = motionHighRes ? outHeight : inHeight;
|
const UINT motionWidth = motionHighRes ? outWidth : resourceWidth, motionHeight = motionHighRes ? outHeight : inHeight;
|
||||||
std::vector<uint16_t> motion(static_cast<size_t>(motionWidth) * motionHeight * 2, 0);
|
std::vector<uint16_t> motion(static_cast<size_t>(motionWidth) * motionHeight * 2, 0);
|
||||||
const float exposureValue = 1.0f;
|
const float exposureValue = 1.0f;
|
||||||
|
const std::array<float, 4> exposureRGBA = {exposureValue, 0.25f, 0.5f, 0.75f};
|
||||||
std::vector<uint16_t> outputInit(static_cast<size_t>(outWidth) * outHeight * 4, 0);
|
std::vector<uint16_t> outputInit(static_cast<size_t>(outWidth) * outHeight * 4, 0);
|
||||||
std::vector<uint8_t> color8;
|
std::vector<uint8_t> color8;
|
||||||
std::vector<uint8_t> outputInit8;
|
std::vector<uint8_t> outputInit8;
|
||||||
@@ -485,16 +516,39 @@ int main(int argc, char** argv)
|
|||||||
ID3D12Resource* colorTexture = create_texture(resourceWidth, inHeight,
|
ID3D12Resource* colorTexture = create_texture(resourceWidth, inHeight,
|
||||||
rgba8 ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_R16G16B16A16_FLOAT, D3D12_RESOURCE_FLAG_NONE);
|
rgba8 ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_R16G16B16A16_FLOAT, D3D12_RESOURCE_FLAG_NONE);
|
||||||
ID3D12Resource* depthTexture = create_texture(resourceWidth, inHeight, DXGI_FORMAT_R32_FLOAT, D3D12_RESOURCE_FLAG_NONE);
|
ID3D12Resource* depthTexture = create_texture(resourceWidth, inHeight, DXGI_FORMAT_R32_FLOAT, D3D12_RESOURCE_FLAG_NONE);
|
||||||
ID3D12Resource* motionTexture = create_texture(motionWidth, motionHeight, DXGI_FORMAT_R16G16_FLOAT, D3D12_RESOURCE_FLAG_NONE);
|
// D4R_HARNESS_MV_RGBA16=1: motion vectors in an R16G16B16A16_TYPELESS texture (as some games pass them), the
|
||||||
ID3D12Resource* exposureTexture = create_texture(1, 1, DXGI_FORMAT_R32_FLOAT, D3D12_RESOURCE_FLAG_NONE);
|
// unused components filled with junk that must not reach DLSS.
|
||||||
|
const bool mvRgba16 = std::getenv("D4R_HARNESS_MV_RGBA16") != nullptr && std::atoi(std::getenv("D4R_HARNESS_MV_RGBA16")) != 0;
|
||||||
|
ID3D12Resource* motionTexture = create_texture(motionWidth, motionHeight,
|
||||||
|
mvRgba16 ? DXGI_FORMAT_R16G16B16A16_TYPELESS : DXGI_FORMAT_R16G16_FLOAT, D3D12_RESOURCE_FLAG_NONE);
|
||||||
|
std::vector<uint16_t> motionWide;
|
||||||
|
auto motion_rows = [&](const uint16_t* rg) -> const void* {
|
||||||
|
if (!mvRgba16)
|
||||||
|
return rg;
|
||||||
|
const size_t pixels = static_cast<size_t>(motionWidth) * motionHeight;
|
||||||
|
motionWide.resize(pixels * 4);
|
||||||
|
for (size_t pixel = 0; pixel < pixels; ++pixel)
|
||||||
|
{
|
||||||
|
motionWide[pixel * 4 + 0] = rg[pixel * 2 + 0];
|
||||||
|
motionWide[pixel * 4 + 1] = rg[pixel * 2 + 1];
|
||||||
|
motionWide[pixel * 4 + 2] = 0x5640; // 100.0
|
||||||
|
motionWide[pixel * 4 + 3] = 0xd640; // -100.0
|
||||||
|
}
|
||||||
|
return motionWide.data();
|
||||||
|
};
|
||||||
|
const UINT motionRowBytes = motionWidth * (mvRgba16 ? 8 : 4);
|
||||||
|
ID3D12Resource* exposureTexture = create_texture(1, 1,
|
||||||
|
exposureRGBA32 ? DXGI_FORMAT_R32G32B32A32_FLOAT : DXGI_FORMAT_R32_FLOAT, D3D12_RESOURCE_FLAG_NONE);
|
||||||
ID3D12Resource* outputTexture = create_texture(outWidth, outHeight,
|
ID3D12Resource* outputTexture = create_texture(outWidth, outHeight,
|
||||||
rgba8 ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_R16G16B16A16_FLOAT,
|
rgba8 ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_R16G16B16A16_FLOAT,
|
||||||
D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS);
|
D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS);
|
||||||
upload(colorTexture, rgba8 ? static_cast<const void*>(color8.data()) : static_cast<const void*>(color.data()),
|
upload(colorTexture, rgba8 ? static_cast<const void*>(color8.data()) : static_cast<const void*>(color.data()),
|
||||||
resourceWidth * (rgba8 ? 4 : 8), srv);
|
resourceWidth * (rgba8 ? 4 : 8), srv);
|
||||||
upload(depthTexture, depth.data(), resourceWidth * 4, srv);
|
upload(depthTexture, depth.data(), resourceWidth * 4, srv);
|
||||||
upload(motionTexture, motion.data(), motionWidth * 4, srv);
|
upload(motionTexture, motion_rows(motion.data()), motionRowBytes, srv);
|
||||||
upload(exposureTexture, &exposureValue, 4, srv);
|
upload(exposureTexture, exposureRGBA32 ? static_cast<const void*>(exposureRGBA.data())
|
||||||
|
: static_cast<const void*>(&exposureValue),
|
||||||
|
exposureRGBA32 ? 16 : 4, srv);
|
||||||
upload(outputTexture, rgba8 ? static_cast<const void*>(outputInit8.data()) : static_cast<const void*>(outputInit.data()),
|
upload(outputTexture, rgba8 ? static_cast<const void*>(outputInit8.data()) : static_cast<const void*>(outputInit.data()),
|
||||||
outWidth * (rgba8 ? 4 : 8), D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
outWidth * (rgba8 ? 4 : 8), D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
|
||||||
std::printf("synthetic inputs uploaded\n");
|
std::printf("synthetic inputs uploaded\n");
|
||||||
@@ -657,7 +711,7 @@ int main(int argc, char** argv)
|
|||||||
motion[pixel * 2 + 0] = mvX;
|
motion[pixel * 2 + 0] = mvX;
|
||||||
motion[pixel * 2 + 1] = mvY;
|
motion[pixel * 2 + 1] = mvY;
|
||||||
}
|
}
|
||||||
update_texture(motionTexture, motion.data(), motionWidth * 4, srv);
|
update_texture(motionTexture, motion_rows(motion.data()), motionRowBytes, srv);
|
||||||
}
|
}
|
||||||
d4r_ngx_set_float(parameters, "Jitter.Offset.X", signX * jx);
|
d4r_ngx_set_float(parameters, "Jitter.Offset.X", signX * jx);
|
||||||
d4r_ngx_set_float(parameters, "Jitter.Offset.Y", signY * jy);
|
d4r_ngx_set_float(parameters, "Jitter.Offset.Y", signY * jy);
|
||||||
@@ -695,7 +749,7 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
update_texture(colorTexture, color.data(), resourceWidth * 8, srv);
|
update_texture(colorTexture, color.data(), resourceWidth * 8, srv);
|
||||||
update_texture(depthTexture, depth.data(), resourceWidth * 4, srv);
|
update_texture(depthTexture, depth.data(), resourceWidth * 4, srv);
|
||||||
update_texture(motionTexture, motion.data(), motionWidth * 4, srv);
|
update_texture(motionTexture, motion_rows(motion.data()), motionRowBytes, srv);
|
||||||
}
|
}
|
||||||
if (replayDir != nullptr)
|
if (replayDir != nullptr)
|
||||||
{
|
{
|
||||||
@@ -706,7 +760,7 @@ int main(int argc, char** argv)
|
|||||||
return 1;
|
return 1;
|
||||||
update_texture(colorTexture, replay.color.data(), resourceWidth * 8, srv);
|
update_texture(colorTexture, replay.color.data(), resourceWidth * 8, srv);
|
||||||
update_texture(depthTexture, replay.depth.data(), resourceWidth * 4, srv);
|
update_texture(depthTexture, replay.depth.data(), resourceWidth * 4, srv);
|
||||||
update_texture(motionTexture, replay.motion.data(), motionWidth * 4, srv);
|
update_texture(motionTexture, motion_rows(reinterpret_cast<const uint16_t*>(replay.motion.data())), motionRowBytes, srv);
|
||||||
d4r_ngx_set_float(parameters, "Jitter.Offset.X", replay.jitterX);
|
d4r_ngx_set_float(parameters, "Jitter.Offset.X", replay.jitterX);
|
||||||
d4r_ngx_set_float(parameters, "Jitter.Offset.Y", replay.jitterY);
|
d4r_ngx_set_float(parameters, "Jitter.Offset.Y", replay.jitterY);
|
||||||
d4r_ngx_set_float(parameters, "MV.Scale.X", replay.mvScaleX);
|
d4r_ngx_set_float(parameters, "MV.Scale.X", replay.mvScaleX);
|
||||||
@@ -831,6 +885,50 @@ int main(int argc, char** argv)
|
|||||||
nonzero += value != 0;
|
nonzero += value != 0;
|
||||||
std::printf("output read back: %zu of %zu bytes nonzero, written to %s\n", nonzero, output.size(), argv[2]);
|
std::printf("output read back: %zu of %zu bytes nonzero, written to %s\n", nonzero, output.size(), argv[2]);
|
||||||
|
|
||||||
|
// D4R_HARNESS_RECREATE=N: N more release/create cycles at alternating render sizes (as when a game's DLSS
|
||||||
|
// quality setting changes), a few evaluations each, logging this process's VRAM to find leaks per cycle.
|
||||||
|
const int recreateCycles = std::getenv("D4R_HARNESS_RECREATE") != nullptr ? std::atoi(std::getenv("D4R_HARNESS_RECREATE")) : 0;
|
||||||
|
for (int cycle = 1; cycle <= recreateCycles; ++cycle)
|
||||||
|
{
|
||||||
|
release(feature);
|
||||||
|
feature = nullptr;
|
||||||
|
const UINT width = cycle % 2 != 0 ? inWidth * 10 / 13 : inWidth;
|
||||||
|
const UINT height = cycle % 2 != 0 ? inHeight * 10 / 13 : inHeight;
|
||||||
|
d4r_ngx_set_uint(parameters, "Width", width);
|
||||||
|
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);
|
||||||
|
result = createFeature(g_list, 1, parameters, &feature);
|
||||||
|
if (result != NGX_SUCCESS)
|
||||||
|
{
|
||||||
|
std::printf("RECREATE cycle %d: CreateFeature -> 0x%08x\n", cycle, result);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
for (int frame = 0; frame < 4; ++frame)
|
||||||
|
{
|
||||||
|
d4r_ngx_set_int(parameters, "Reset", frame == 0 ? 1 : 0);
|
||||||
|
evaluate(g_list, feature, parameters, nullptr);
|
||||||
|
submit_and_wait();
|
||||||
|
Sleep(frameWaitMs);
|
||||||
|
}
|
||||||
|
const std::vector<uint8_t> cycleOutput = read_back(outputTexture, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
|
||||||
|
outWidth * (rgba8 ? 4 : 8));
|
||||||
|
if (FILE* cycleFile = std::fopen((std::string(argv[2]) + ".cycle" + std::to_string(cycle)).c_str(), "wb"))
|
||||||
|
{
|
||||||
|
std::fwrite(cycleOutput.data(), 1, cycleOutput.size(), cycleFile);
|
||||||
|
std::fclose(cycleFile);
|
||||||
|
}
|
||||||
|
// Also to the quality report file, which survives when Proton drops stdout.
|
||||||
|
const char* reportPath = std::getenv("D4R_HARNESS_QUALITY_SCENE");
|
||||||
|
FILE* report = reportPath != nullptr ? std::fopen(reportPath, "a") : nullptr;
|
||||||
|
for (FILE* out : {stdout, report})
|
||||||
|
if (out != nullptr)
|
||||||
|
std::fprintf(out, "RECREATE cycle %d (%ux%u): process VRAM %llu KiB\n", cycle, width, height,
|
||||||
|
process_vram_kib());
|
||||||
|
if (report != nullptr)
|
||||||
|
std::fclose(report);
|
||||||
|
}
|
||||||
|
|
||||||
release(feature);
|
release(feature);
|
||||||
shutdown();
|
shutdown();
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+146
-30
@@ -495,8 +495,12 @@ static void load_portable_config()
|
|||||||
|
|
||||||
const std::string zluda = ini_value(ini, "paths", "ZludaDir");
|
const std::string zluda = ini_value(ini, "paths", "ZludaDir");
|
||||||
portable_set_unix("D4R_ZLUDA_LIBCUDA", (zluda.empty() ? g_portable.unixDir + "/zluda" : zluda) + "/libcuda.so");
|
portable_set_unix("D4R_ZLUDA_LIBCUDA", (zluda.empty() ? g_portable.unixDir + "/zluda" : zluda) + "/libcuda.so");
|
||||||
if (const std::string rocm = ini_value(ini, "paths", "RocmDir"); !rocm.empty())
|
// The release's bundled ROCm runtime unless d4r.ini names another
|
||||||
|
const std::string rocm = ini_value(ini, "paths", "RocmDir");
|
||||||
|
if (!rocm.empty())
|
||||||
portable_set_unix("D4R_ROCM_DIR", rocm);
|
portable_set_unix("D4R_ROCM_DIR", rocm);
|
||||||
|
else if (!g_portable.unixDir.empty() && GetFileAttributesW((dir + L"\\rocm\\lib").c_str()) != INVALID_FILE_ATTRIBUTES)
|
||||||
|
portable_set_unix("D4R_ROCM_DIR", g_portable.unixDir + "/rocm");
|
||||||
const std::string cache = ini_value(ini, "paths", "CacheDir");
|
const std::string cache = ini_value(ini, "paths", "CacheDir");
|
||||||
portable_set_unix("D4R_ZLUDA_CACHE_HOME", cache.empty() ? "~/.cache/d4r" : cache);
|
portable_set_unix("D4R_ZLUDA_CACHE_HOME", cache.empty() ? "~/.cache/d4r" : cache);
|
||||||
|
|
||||||
@@ -832,7 +836,8 @@ static bool supported_input(Plane plane, DXGI_FORMAT format)
|
|||||||
case Plane::Motion:
|
case Plane::Motion:
|
||||||
return format == DXGI_FORMAT_R16G16_FLOAT || format == DXGI_FORMAT_R16G16_TYPELESS ||
|
return format == DXGI_FORMAT_R16G16_FLOAT || format == DXGI_FORMAT_R16G16_TYPELESS ||
|
||||||
format == DXGI_FORMAT_R32G32_FLOAT || format == DXGI_FORMAT_R32G32_TYPELESS ||
|
format == DXGI_FORMAT_R32G32_FLOAT || format == DXGI_FORMAT_R32G32_TYPELESS ||
|
||||||
format == DXGI_FORMAT_R16G16B16A16_FLOAT || format == DXGI_FORMAT_R32G32B32A32_FLOAT;
|
format == DXGI_FORMAT_R16G16B16A16_FLOAT || format == DXGI_FORMAT_R16G16B16A16_TYPELESS ||
|
||||||
|
format == DXGI_FORMAT_R32G32B32A32_FLOAT;
|
||||||
case Plane::Exposure:
|
case Plane::Exposure:
|
||||||
return format == DXGI_FORMAT_R32_FLOAT || format == DXGI_FORMAT_R32_TYPELESS ||
|
return format == DXGI_FORMAT_R32_FLOAT || format == DXGI_FORMAT_R32_TYPELESS ||
|
||||||
format == DXGI_FORMAT_R16_FLOAT || format == DXGI_FORMAT_R16_TYPELESS ||
|
format == DXGI_FORMAT_R16_FLOAT || format == DXGI_FORMAT_R16_TYPELESS ||
|
||||||
@@ -955,6 +960,7 @@ static void convert_row_in(Plane plane, DXGI_FORMAT format, const uint8_t* sourc
|
|||||||
std::memcpy(half + x * 2, source + x * 4, 4);
|
std::memcpy(half + x * 2, source + x * 4, 4);
|
||||||
break;
|
break;
|
||||||
case DXGI_FORMAT_R16G16B16A16_FLOAT:
|
case DXGI_FORMAT_R16G16B16A16_FLOAT:
|
||||||
|
case DXGI_FORMAT_R16G16B16A16_TYPELESS:
|
||||||
std::memcpy(half + x * 2, source + x * 8, 4);
|
std::memcpy(half + x * 2, source + x * 8, 4);
|
||||||
break;
|
break;
|
||||||
case DXGI_FORMAT_R32G32B32A32_FLOAT:
|
case DXGI_FORMAT_R32G32B32A32_FLOAT:
|
||||||
@@ -1418,12 +1424,13 @@ struct VramBuffer
|
|||||||
size_t bytes = 0;
|
size_t bytes = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// An RGBA16F image used to convert a noncanonical color input or output.
|
// A temporary image for colour or exposure format conversion.
|
||||||
struct VramImage
|
struct VramImage
|
||||||
{
|
{
|
||||||
VkImage image = VK_NULL_HANDLE;
|
VkImage image = VK_NULL_HANDLE;
|
||||||
VkDeviceMemory memory = VK_NULL_HANDLE;
|
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||||
UINT width = 0, height = 0;
|
UINT width = 0, height = 0;
|
||||||
|
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Staging
|
struct Staging
|
||||||
@@ -1551,6 +1558,8 @@ struct Feature
|
|||||||
// formats later stop qualifying.
|
// formats later stop qualifying.
|
||||||
bool vram = false, vramDecided = false;
|
bool vram = false, vramDecided = false;
|
||||||
VramImage colorConversion;
|
VramImage colorConversion;
|
||||||
|
VramImage motionConversion;
|
||||||
|
VramImage exposureConversion;
|
||||||
VramImage outputConversion;
|
VramImage outputConversion;
|
||||||
// Split frames: the rest of frame N's command list is submitted only once
|
// Split frames: the rest of frame N's command list is submitted only once
|
||||||
// splitSemaphore reaches N (signalled when N retires, or by the watchdog).
|
// splitSemaphore reaches N (signalled when N retires, or by the watchdog).
|
||||||
@@ -2153,9 +2162,33 @@ static int device_local_type(uint32_t typeBits)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Buffers of released features, reused by later ones. ROCm up to at least 7.2.4 never frees memory mapped with
|
||||||
|
// hipExternalMemoryGetMappedBuffer (the mapping's buffer view is retained twice but released once by hipFree), so
|
||||||
|
// destroying a buffer would leak its memory: each DLSS quality change (a new feature) would add ~100 MB of VRAM.
|
||||||
|
static std::mutex g_vramPoolMutex;
|
||||||
|
static std::vector<VramBuffer> g_vramPool;
|
||||||
|
constexpr size_t kVramPoolLimit = 64;
|
||||||
|
|
||||||
|
// The smallest pooled buffer that holds `bytes`.
|
||||||
|
static bool take_pooled_vram_buffer(VramBuffer& target, size_t bytes)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(g_vramPoolMutex);
|
||||||
|
auto best = g_vramPool.end();
|
||||||
|
for (auto it = g_vramPool.begin(); it != g_vramPool.end(); ++it)
|
||||||
|
if (it->bytes >= bytes && (best == g_vramPool.end() || it->bytes < best->bytes))
|
||||||
|
best = it;
|
||||||
|
if (best == g_vramPool.end())
|
||||||
|
return false;
|
||||||
|
target = *best;
|
||||||
|
g_vramPool.erase(best);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Game thread. The CUDA import runs on the worker, which owns the context.
|
// Game thread. The CUDA import runs on the worker, which owns the context.
|
||||||
static bool create_vram_buffer(VramBuffer& target, size_t bytes)
|
static bool create_vram_buffer(VramBuffer& target, size_t bytes)
|
||||||
{
|
{
|
||||||
|
if (take_pooled_vram_buffer(target, bytes))
|
||||||
|
return true;
|
||||||
VramBuffer buffer;
|
VramBuffer buffer;
|
||||||
VkExternalMemoryBufferCreateInfo external = {VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO};
|
VkExternalMemoryBufferCreateInfo external = {VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO};
|
||||||
external.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
|
external.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
|
||||||
@@ -2206,8 +2239,11 @@ static void destroy_vram_buffer(VramBuffer& buffer)
|
|||||||
{
|
{
|
||||||
if (buffer.external != nullptr)
|
if (buffer.external != nullptr)
|
||||||
g.worker.call([&buffer] {
|
g.worker.call([&buffer] {
|
||||||
g.cu.memFree(buffer.device);
|
const int freed = g.cu.memFree(buffer.device);
|
||||||
return g_vk.release(buffer.external);
|
const int released = g_vk.release(buffer.external);
|
||||||
|
if (freed != 0 || released != 0)
|
||||||
|
logf("VRAM interop: freeing %zu-byte buffer: cuMemFree %d, release %d", buffer.bytes, freed, released);
|
||||||
|
return released;
|
||||||
});
|
});
|
||||||
if (buffer.memory != VK_NULL_HANDLE)
|
if (buffer.memory != VK_NULL_HANDLE)
|
||||||
g_vk.free(g_vk.device, buffer.memory, nullptr);
|
g_vk.free(g_vk.device, buffer.memory, nullptr);
|
||||||
@@ -2216,6 +2252,34 @@ static void destroy_vram_buffer(VramBuffer& buffer)
|
|||||||
buffer = VramBuffer{};
|
buffer = VramBuffer{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only once no command list or CUDA work uses the buffer: keeps it for a later feature (see g_vramPool).
|
||||||
|
static void recycle_vram_buffer(VramBuffer& buffer)
|
||||||
|
{
|
||||||
|
if (buffer.buffer == VK_NULL_HANDLE)
|
||||||
|
return;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(g_vramPoolMutex);
|
||||||
|
if (g_vramPool.size() < kVramPoolLimit)
|
||||||
|
{
|
||||||
|
g_vramPool.push_back(buffer);
|
||||||
|
buffer = VramBuffer{};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
destroy_vram_buffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_vram_pool()
|
||||||
|
{
|
||||||
|
std::vector<VramBuffer> pool;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(g_vramPoolMutex);
|
||||||
|
pool.swap(g_vramPool);
|
||||||
|
}
|
||||||
|
for (VramBuffer& buffer : pool)
|
||||||
|
destroy_vram_buffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
static void destroy_vram_image(VramImage& image)
|
static void destroy_vram_image(VramImage& image)
|
||||||
{
|
{
|
||||||
if (image.memory != VK_NULL_HANDLE)
|
if (image.memory != VK_NULL_HANDLE)
|
||||||
@@ -2225,11 +2289,11 @@ static void destroy_vram_image(VramImage& image)
|
|||||||
image = VramImage{};
|
image = VramImage{};
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool create_vram_image(VramImage& image, UINT width, UINT height)
|
static bool create_vram_image(VramImage& image, UINT width, UINT height, VkFormat format)
|
||||||
{
|
{
|
||||||
VkImageCreateInfo info = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
|
VkImageCreateInfo info = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
|
||||||
info.imageType = VK_IMAGE_TYPE_2D;
|
info.imageType = VK_IMAGE_TYPE_2D;
|
||||||
info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
|
info.format = format;
|
||||||
info.extent = {width, height, 1};
|
info.extent = {width, height, 1};
|
||||||
info.mipLevels = 1;
|
info.mipLevels = 1;
|
||||||
info.arrayLayers = 1;
|
info.arrayLayers = 1;
|
||||||
@@ -2260,6 +2324,7 @@ static bool create_vram_image(VramImage& image, UINT width, UINT height)
|
|||||||
}
|
}
|
||||||
image.width = width;
|
image.width = width;
|
||||||
image.height = height;
|
image.height = height;
|
||||||
|
image.format = format;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2297,11 +2362,19 @@ struct VramCopy
|
|||||||
ID3D12Resource* resource = nullptr;
|
ID3D12Resource* resource = nullptr;
|
||||||
VkImage image = VK_NULL_HANDLE;
|
VkImage image = VK_NULL_HANDLE;
|
||||||
VkImageAspectFlags aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
VkImageAspectFlags aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
bool convert = false; // colour blit between this format and RGBA16F
|
bool convert = false; // blit to/from the plane's canonical format
|
||||||
UINT width = 0, height = 0;
|
UINT width = 0, height = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Whether a linear color format can be blitted to/from the canonical RGBA16F.
|
static bool vram_blit_supported(VkFormat source, VkFormat destination)
|
||||||
|
{
|
||||||
|
VkFormatProperties sourceProperties = {}, destinationProperties = {};
|
||||||
|
g_vk.formatProperties(g_vk.physical, source, &sourceProperties);
|
||||||
|
g_vk.formatProperties(g_vk.physical, destination, &destinationProperties);
|
||||||
|
return (sourceProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) != 0 &&
|
||||||
|
(destinationProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
// sRGB is deliberately excluded: the host staging path treats its bytes as
|
// sRGB is deliberately excluded: the host staging path treats its bytes as
|
||||||
// UNORM and a Vulkan blit would apply an sRGB transfer function instead.
|
// UNORM and a Vulkan blit would apply an sRGB transfer function instead.
|
||||||
static bool vram_color_blit_supported(VkFormat format, bool output)
|
static bool vram_color_blit_supported(VkFormat format, bool output)
|
||||||
@@ -2316,16 +2389,39 @@ static bool vram_color_blit_supported(VkFormat format, bool output)
|
|||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
VkFormatProperties resourceProperties = {}, canonicalProperties = {};
|
return output ? vram_blit_supported(VK_FORMAT_R16G16B16A16_SFLOAT, format)
|
||||||
g_vk.formatProperties(g_vk.physical, format, &resourceProperties);
|
: vram_blit_supported(format, VK_FORMAT_R16G16B16A16_SFLOAT);
|
||||||
g_vk.formatProperties(g_vk.physical, VK_FORMAT_R16G16B16A16_SFLOAT, &canonicalProperties);
|
|
||||||
const VkFormatFeatureFlags resourceFeature = output ? VK_FORMAT_FEATURE_BLIT_DST_BIT : VK_FORMAT_FEATURE_BLIT_SRC_BIT;
|
|
||||||
const VkFormatFeatureFlags canonicalFeature = output ? VK_FORMAT_FEATURE_BLIT_SRC_BIT : VK_FORMAT_FEATURE_BLIT_DST_BIT;
|
|
||||||
return (resourceProperties.optimalTilingFeatures & resourceFeature) != 0 &&
|
|
||||||
(canonicalProperties.optimalTilingFeatures & canonicalFeature) != 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whether `resource` can be copied raw (or, for linear colour, blitted)
|
// The host path takes only the first (R) component of an exposure texel.
|
||||||
|
static bool vram_exposure_blit_supported(VkFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case VK_FORMAT_R16_SFLOAT:
|
||||||
|
case VK_FORMAT_R16G16B16A16_SFLOAT:
|
||||||
|
case VK_FORMAT_R32G32B32A32_SFLOAT:
|
||||||
|
return vram_blit_supported(format, VK_FORMAT_R32_SFLOAT);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Motion vectors in a wider float format keep their first two components (as the host path does).
|
||||||
|
static bool vram_motion_blit_supported(VkFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case VK_FORMAT_R16G16B16A16_SFLOAT:
|
||||||
|
case VK_FORMAT_R32G32_SFLOAT:
|
||||||
|
case VK_FORMAT_R32G32B32A32_SFLOAT:
|
||||||
|
return vram_blit_supported(format, VK_FORMAT_R16G16_SFLOAT);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether `resource` can be copied raw (or, for colour/motion/exposure, blitted)
|
||||||
// to/from the canonical layout of `plane`.
|
// to/from the canonical layout of `plane`.
|
||||||
static bool describe_vram_copy(ID3D12Resource* resource, Plane plane, VramCopy& copy, bool output = false)
|
static bool describe_vram_copy(ID3D12Resource* resource, Plane plane, VramCopy& copy, bool output = false)
|
||||||
{
|
{
|
||||||
@@ -2348,7 +2444,11 @@ static bool describe_vram_copy(ID3D12Resource* resource, Plane plane, VramCopy&
|
|||||||
copy.convert = vram_color_blit_supported(format, output);
|
copy.convert = vram_color_blit_supported(format, output);
|
||||||
return copy.convert || format == VK_FORMAT_R16G16B16A16_SFLOAT;
|
return copy.convert || format == VK_FORMAT_R16G16B16A16_SFLOAT;
|
||||||
}
|
}
|
||||||
return vk_texel_bytes(format) == canonical_texel_bytes(plane);
|
if (plane == Plane::Exposure)
|
||||||
|
copy.convert = vram_exposure_blit_supported(format);
|
||||||
|
else if (plane == Plane::Motion)
|
||||||
|
copy.convert = vram_motion_blit_supported(format);
|
||||||
|
return copy.convert || vk_texel_bytes(format) == canonical_texel_bytes(plane);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const VkMemoryBarrier kBeforeTransfer = {VK_STRUCTURE_TYPE_MEMORY_BARRIER, nullptr, VK_ACCESS_MEMORY_WRITE_BIT,
|
static const VkMemoryBarrier kBeforeTransfer = {VK_STRUCTURE_TYPE_MEMORY_BARRIER, nullptr, VK_ACCESS_MEMORY_WRITE_BIT,
|
||||||
@@ -2389,9 +2489,11 @@ static bool record_vram_inputs(Feature& feature, ID3D12GraphicsCommandList* list
|
|||||||
g_vk.copyImageToBuffer(cmd, copy.image, layout, slot.vram[index].buffer, 1, ®ion);
|
g_vk.copyImageToBuffer(cmd, copy.image, layout, slot.vram[index].buffer, 1, ®ion);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Colour conversion: blit (exact for R11G11B10 -> half) into the
|
// Convert colour to RGBA16F, motion to RG16F or exposure's first component
|
||||||
// RGBA16F image, then copy that.
|
// to R32F, then copy from the canonical image into the shared buffer.
|
||||||
VkImage conversion = feature.colorConversion.image;
|
VkImage conversion = kPlanes[index] == Plane::Exposure ? feature.exposureConversion.image
|
||||||
|
: kPlanes[index] == Plane::Motion ? feature.motionConversion.image
|
||||||
|
: feature.colorConversion.image;
|
||||||
VkImageMemoryBarrier toDestination = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
|
VkImageMemoryBarrier toDestination = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
|
||||||
toDestination.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
toDestination.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||||
toDestination.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
toDestination.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
@@ -2590,14 +2692,14 @@ static bool ensure_vram_buffer(Feature& feature, VramBuffer& buffer, size_t byte
|
|||||||
return create_vram_buffer(buffer, bytes);
|
return create_vram_buffer(buffer, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ensure_conversion_image(Feature& feature, VramImage& image, UINT width, UINT height)
|
static bool ensure_conversion_image(Feature& feature, VramImage& image, UINT width, UINT height, VkFormat format)
|
||||||
{
|
{
|
||||||
if (image.image != VK_NULL_HANDLE && image.width == width && image.height == height)
|
if (image.image != VK_NULL_HANDLE && image.width == width && image.height == height && image.format == format)
|
||||||
return true;
|
return true;
|
||||||
if (image.image != VK_NULL_HANDLE)
|
if (image.image != VK_NULL_HANDLE)
|
||||||
feature.retiredImages.push_back(image);
|
feature.retiredImages.push_back(image);
|
||||||
image = VramImage{};
|
image = VramImage{};
|
||||||
return create_vram_image(image, width, height);
|
return create_vram_image(image, width, height, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void release_vram(Feature& feature)
|
static void release_vram(Feature& feature)
|
||||||
@@ -2606,14 +2708,16 @@ static void release_vram(Feature& feature)
|
|||||||
g_vk.destroySemaphore(g_vk.device, feature.splitSemaphore, nullptr);
|
g_vk.destroySemaphore(g_vk.device, feature.splitSemaphore, nullptr);
|
||||||
for (InputSlot& slot : feature.inputs)
|
for (InputSlot& slot : feature.inputs)
|
||||||
for (VramBuffer& buffer : slot.vram)
|
for (VramBuffer& buffer : slot.vram)
|
||||||
destroy_vram_buffer(buffer);
|
recycle_vram_buffer(buffer);
|
||||||
for (OutputSlot& slot : feature.outputs)
|
for (OutputSlot& slot : feature.outputs)
|
||||||
destroy_vram_buffer(slot.vram);
|
recycle_vram_buffer(slot.vram);
|
||||||
destroy_vram_buffer(feature.gpuMarker);
|
recycle_vram_buffer(feature.gpuMarker);
|
||||||
for (VramBuffer& buffer : feature.retiredBuffers)
|
for (VramBuffer& buffer : feature.retiredBuffers)
|
||||||
destroy_vram_buffer(buffer);
|
recycle_vram_buffer(buffer);
|
||||||
feature.retiredBuffers.clear();
|
feature.retiredBuffers.clear();
|
||||||
destroy_vram_image(feature.colorConversion);
|
destroy_vram_image(feature.colorConversion);
|
||||||
|
destroy_vram_image(feature.motionConversion);
|
||||||
|
destroy_vram_image(feature.exposureConversion);
|
||||||
destroy_vram_image(feature.outputConversion);
|
destroy_vram_image(feature.outputConversion);
|
||||||
for (VramImage& image : feature.retiredImages)
|
for (VramImage& image : feature.retiredImages)
|
||||||
destroy_vram_image(image);
|
destroy_vram_image(image);
|
||||||
@@ -4406,10 +4510,20 @@ static NgxResult evaluate(ID3D12GraphicsCommandList* list, const NgxHandle* hand
|
|||||||
return NGX_FAIL_PLATFORM_ERROR;
|
return NGX_FAIL_PLATFORM_ERROR;
|
||||||
}
|
}
|
||||||
if (vramInputs[0].convert && !ensure_conversion_image(*feature, feature->colorConversion,
|
if (vramInputs[0].convert && !ensure_conversion_image(*feature, feature->colorConversion,
|
||||||
vramInputs[0].width, vramInputs[0].height))
|
vramInputs[0].width, vramInputs[0].height,
|
||||||
|
VK_FORMAT_R16G16B16A16_SFLOAT))
|
||||||
|
return NGX_FAIL_PLATFORM_ERROR;
|
||||||
|
if (vramInputs[2].convert && !ensure_conversion_image(*feature, feature->motionConversion,
|
||||||
|
vramInputs[2].width, vramInputs[2].height,
|
||||||
|
VK_FORMAT_R16G16_SFLOAT))
|
||||||
|
return NGX_FAIL_PLATFORM_ERROR;
|
||||||
|
if (inputCount == 4 && vramInputs[3].convert &&
|
||||||
|
!ensure_conversion_image(*feature, feature->exposureConversion,
|
||||||
|
vramInputs[3].width, vramInputs[3].height, VK_FORMAT_R32_SFLOAT))
|
||||||
return NGX_FAIL_PLATFORM_ERROR;
|
return NGX_FAIL_PLATFORM_ERROR;
|
||||||
if (vramOutput.convert && !ensure_conversion_image(*feature, feature->outputConversion,
|
if (vramOutput.convert && !ensure_conversion_image(*feature, feature->outputConversion,
|
||||||
vramOutput.width, vramOutput.height))
|
vramOutput.width, vramOutput.height,
|
||||||
|
VK_FORMAT_R16G16B16A16_SFLOAT))
|
||||||
return NGX_FAIL_PLATFORM_ERROR;
|
return NGX_FAIL_PLATFORM_ERROR;
|
||||||
}
|
}
|
||||||
if ((!p.vram || verify) &&
|
if ((!p.vram || verify) &&
|
||||||
@@ -4654,6 +4768,8 @@ D4R_EXPORT NgxResult NVSDK_NGX_D3D12_Shutdown()
|
|||||||
}
|
}
|
||||||
for (Feature* feature : features)
|
for (Feature* feature : features)
|
||||||
release_feature(feature);
|
release_feature(feature);
|
||||||
|
if (g_vk.ready)
|
||||||
|
free_vram_pool();
|
||||||
std::lock_guard<std::mutex> lock(g.mutex);
|
std::lock_guard<std::mutex> lock(g.mutex);
|
||||||
if (g.ngxInitialized)
|
if (g.ngxInitialized)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2589,8 +2589,10 @@ CUresult WINAPI cuGetProcAddress(const char* symbol, void** pointer, int cuda_ve
|
|||||||
handles (Wine 11 layout: a VkDevice client object holds the unix object
|
handles (Wine 11 layout: a VkDevice client object holds the unix object
|
||||||
pointer at offset 8; a non-dispatchable client handle is the unix object
|
pointer at offset 8; a non-dispatchable client handle is the unix object
|
||||||
pointer; every unix object starts with its host handle), the host fd is
|
pointer; every unix object starts with its host handle), the host fd is
|
||||||
exported with vkGetMemoryFdKHR and imported into HIP, which takes ownership
|
exported with vkGetMemoryFdKHR and imported into HIP. HIP does not take
|
||||||
of the fd. */
|
ownership of the fd (hipDestroyExternalMemory leaves it open, and an open fd
|
||||||
|
keeps the memory alive after vkFreeMemory), so it is closed once imported, as
|
||||||
|
ROCm's own GL interop does after mapping. */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int type; /* hipExternalMemoryHandleTypeOpaqueFd = 1 */
|
int type; /* hipExternalMemoryHandleTypeOpaqueFd = 1 */
|
||||||
@@ -2697,11 +2699,11 @@ CUresult WINAPI d4rImportVulkanMemory(void* client_device, uint64_t client_memor
|
|||||||
desc.flags = 1; /* dedicated allocation */
|
desc.flags = 1; /* dedicated allocation */
|
||||||
void* external = NULL;
|
void* external = NULL;
|
||||||
int result = import(&external, &desc);
|
int result = import(&external, &desc);
|
||||||
|
close(fd);
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
{
|
{
|
||||||
tracef("d4rImportVulkanMemory: hipImportExternalMemory(fd=%d, %llu bytes) failed: %d", fd,
|
tracef("d4rImportVulkanMemory: hipImportExternalMemory(fd=%d, %llu bytes) failed: %d", fd,
|
||||||
(unsigned long long)bytes, result);
|
(unsigned long long)bytes, result);
|
||||||
close(fd);
|
|
||||||
return CUDA_ERROR_INVALID_VALUE;
|
return CUDA_ERROR_INVALID_VALUE;
|
||||||
}
|
}
|
||||||
D4rHipExternalMemoryBufferDesc buffer;
|
D4rHipExternalMemoryBufferDesc buffer;
|
||||||
@@ -2835,5 +2837,7 @@ CUresult WINAPI d4rSetArrayRedirect(CUarray array, CUdeviceptr pointer, uint32_t
|
|||||||
CUresult WINAPI d4rReleaseVulkanMemory(void* memory)
|
CUresult WINAPI d4rReleaseVulkanMemory(void* memory)
|
||||||
{
|
{
|
||||||
HIP_DESTROY_EXTERNAL_MEMORY_FN destroy = (HIP_DESTROY_EXTERNAL_MEMORY_FN)hip_symbol("hipDestroyExternalMemory");
|
HIP_DESTROY_EXTERNAL_MEMORY_FN destroy = (HIP_DESTROY_EXTERNAL_MEMORY_FN)hip_symbol("hipDestroyExternalMemory");
|
||||||
return destroy != NULL && destroy(memory) == 0 ? CUDA_SUCCESS : CUDA_ERROR_INVALID_VALUE;
|
const int result = destroy != NULL ? destroy(memory) : -1;
|
||||||
|
tracef("d4rReleaseVulkanMemory: hipDestroyExternalMemory(%p) -> %d", memory, result);
|
||||||
|
return result == 0 ? CUDA_SUCCESS : CUDA_ERROR_INVALID_VALUE;
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user