ASI add-on for existing OptiScaler installs
Add an optional D4R_INSTALL_MODE=asi release (d4r-<version>-asi.zip) holding only d4r.asi and a companion d4r/ folder, to drop into an existing OptiScaler plugin directory. - Shim: export InitializeASI. It identifies OptiScaler by its PE export name to find the active INI, then launches d4r-setup.exe out of process (no GPU or network work under the loader lock). The .asi build loads its runtime and config from the companion d4r/ folder. - tools/d4r_asi_setup.cpp: native helper. It waits for the game to exit, installs the D3D12 wrapper and the private d4r12core.dll beside the EXE, and merges the NGX route into the loader's INI (including GE-Proton's prefix-local INI) with backups. Unchanged launches do nothing. It skips Proton helpers and background utilities and runs setup for one plugin install at a time. - scripts/private_d3d12_core.py: points vkd3d's d3d12.dll forwarder at d4r12core.dll without moving code addresses. - install.sh runs from inside the ASI's d4r/ as well as the proxy release root. d4r-check.sh checks ASI installs (loader, plugin path, LoadAsiPlugins, HookOriginalNvngxOnly, Wine drive paths). - Packaging, build script, docs, ASI_README.txt and tests.
This commit is contained in:
1 parent
85175e0b27
commit
d5c8be25c8
16 files changed
+1091
-39
No files matched your search
@@ -0,0 +1,65 @@
|
||||
d4r ASI add-on for Linux / Proton
|
||||
================================
|
||||
|
||||
1. Drop d4r.asi and the d4r folder into OptiScaler's existing plugins folder.
|
||||
For example: <game>/plugins/d4r.asi and <game>/plugins/d4r/.
|
||||
Custom plugin folders work too: keep the ASI and its companion folder together.
|
||||
2. Run bash install.sh inside that d4r folder once to download and verify the
|
||||
official NVIDIA DLSS/NGX DLLs. No Python or configuration command is needed.
|
||||
3. Launch the game. At the first setup message, close the game. Setup installs
|
||||
the bundled D3D12 wrapper and private core and updates OptiScaler's settings after the game exits,
|
||||
with backups. Wait for the ready message, then launch the game again.
|
||||
|
||||
Requires OptiScaler 0.9.4 through a game-local proxy (dxgi.dll, winmm.dll, etc.)
|
||||
or GE-Proton's built-in loader, with [Plugins] LoadAsiPlugins=true already enabled. A plugin cannot enable
|
||||
its own loader before it has been loaded. OptiScaler's nvngx.dll mode does not
|
||||
load ASIs. OptiScaler installed as d3d12.dll conflicts with the patched D3D12 pair;
|
||||
use another supported proxy name first. GE-Proton's built-in OptiScaler also
|
||||
works with its prefix-local plugins folder. Setup edits OptiScaler.ini beside
|
||||
the active loader, including windows/system32/umu/OptiScaler.ini. Explicit
|
||||
PROTON_OPTISCALER_CONFIG values are applied again by Proton at launch; keep
|
||||
those consistent with d4r's route if you use that environment option.
|
||||
|
||||
The runtime, kernels, NVIDIA files, logs and d4r.ini stay inside plugins/d4r/.
|
||||
The patched d3d12.dll and d4r12core.dll are kept there as an installation payload;
|
||||
the helper installs d3d12.dll and d4r12core.dll beside the main game EXE.
|
||||
The game's original D3D12Core.dll remains untouched. d4r's forwarder uses the
|
||||
private core name to avoid Agility SDK loader interception.
|
||||
Setup uses OptiScaler's regular Windows library hooks and disables its Agility
|
||||
SDK upgrade so the supplied D3D12 implementation stays selected.
|
||||
It waits for the running game to exit before changing either DLL or the INI.
|
||||
Changed files get unique .d4r-backup.* copies. Unrelated settings, your existing
|
||||
OptiScaler proxy and other plugins are preserved. Matching files are skipped on
|
||||
later launches. Background utilities such as xalia.exe, launchers and crash
|
||||
reporters do not run setup; one helper per plugin installation owns the setup. The helper never changes the Proton installation or system packages.
|
||||
|
||||
install.sh requires Bash, curl, sha256sum and 7zz, 7z or 7za; no sudo is needed.
|
||||
The NVIDIA driver archive (~915 MiB on first use) is extracted without being
|
||||
executed, and temporary downloads are removed afterward. For a nonvidia archive,
|
||||
supply the NVIDIA DLLs inside this d4r folder before launching instead; full
|
||||
archives already include them. Internet access and NVIDIA's license terms apply:
|
||||
https://github.com/NVIDIA/DLSS/blob/v310.7.0/LICENSE.txt
|
||||
https://www.nvidia.com/en-us/drivers/nvidia-license/
|
||||
|
||||
Use GE-Proton 11 with these Steam launch options:
|
||||
PROTON_FORCE_NVAPI=1 DXVK_NVAPI_GPU_ARCH=AD100 %command%
|
||||
Keep PROTON_USE_OPTISCALER for Proton's built-in OptiScaler. Remove it only when
|
||||
using a separate game-local OptiScaler install.
|
||||
Select DLSS in OptiScaler; choose your model in plugins/d4r/d4r.ini.
|
||||
|
||||
Errors and completion status are recorded in d4r/installation-status.txt.
|
||||
Setup retries on the next launch if it fails. Moving the game/plugin folder
|
||||
refreshes the paths automatically after another close-and-relaunch cycle.
|
||||
The ASI is the NGX backend itself; NvngxPath selects it, so only one backend is
|
||||
active. HookOriginalNvngxOnly=true keeps the internal NVIDIA core load separate.
|
||||
CUDA/GPU initialization stays lazy until NGX Init. Installation and loader tests
|
||||
do not prove successful rendering in a game; native Vulkan remains experimental.
|
||||
|
||||
Uninstall: close the game and setup helper, restore your OptiScaler.ini backup beside its loader and
|
||||
the D3D12 backups beside the EXE (or delete this setup's D3D12 files if none existed
|
||||
before), then remove only d4r.asi and this d4r folder from the plugins directory.
|
||||
Keep your existing OptiScaler loader and other plugins.
|
||||
|
||||
d4r is GPL-3.0-only. Third-party notices and provenance are in d4r/licenses/ and
|
||||
d4r/source/SOURCES.txt. NVIDIA files and derived kernels have separate terms.
|
||||
https://github.com/countervolts/d4r
|
||||
@@ -39,6 +39,10 @@ What you need
|
||||
|
||||
Install
|
||||
-------
|
||||
For an existing game-local OptiScaler installation, the separate -asi.zip add-on
|
||||
uses its ASI loader and keeps its proxy DLL and unrelated INI settings. Follow
|
||||
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.
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<!-- Modified in this fork for CUDA Ray Reconstruction support and validation (2026). -->
|
||||
# Unreleased
|
||||
|
||||
- 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.
|
||||
|
||||
- ASI setup now finds the INI beside the active OptiScaler loader, including GE-Proton's `system32/umu` installation. Missing-file errors identify the actual missing path, and INI updates are staged beside their destination even when the game and prefix are on different filesystems.
|
||||
- Optional OptiScaler ASI add-on (`D4R_INSTALL_MODE=asi`): drop `d4r.asi` and its companion `d4r/` folder into the existing plugin directory, run its `install.sh` once for NVIDIA's DLLs, then launch. The ASI installs the supplied D3D12 wrapper/private core beside the EXE and configures NGX after the game closes, with backups. The runtime stays in the plugin folder; no Python is needed. Game rendering still requires validation.
|
||||
- Native engine, preset L (DLSS 4.5, render preset 12): the engine now runs L ([Engine] Enabled, models in `engine/l` and `engine/l-ldr`, built by `engine/compile_m.py` from a capture of preset L; `scripts/install_d4r_runtime.sh` stages `D4R_ENGINE_MODEL_L` / `D4R_ENGINE_MODEL_L_LDR`). Every stage, `d4r-m` on captured frames and the D3D12 harness are byte-identical to the CUDA backend running NVIDIA's translated kernels, apart from isolated single-frame knife edges (96-122 dB). 1280x720 -> 3840x2160 takes 3.8 ms on an RX 9070 XT. Display-resolution motion vectors keep the CUDA backend, as for M. Details: `engine/L_NOTES.md`.
|
||||
- Native engine fixes found with L, which also apply to M (rebuild M packages with the current `compile_m.py`): the encoders' patch merge is clamped to ±2π as NVIDIA's kernels do; the automatic exposure is measured on NGX's grid at Ultra Performance (half the render size, at least 256, when the output is more than twice the render size; half the output was used, which changed every value downstream); the downsample's filter weights are exact (glslang rounded them toward zero: 92% of the output values were identical, now all); and the network uses the summation order of NVIDIA's kernels as ZLUDA runs them. M is now byte-identical to that CUDA backend in the harness too, where it measured 36-41 dB.
|
||||
- `scripts/check_redistributable.py` refuses NVIDIA's binaries, NGX SDK headers, PTX and code objects, d4r's PTX translations and the engine's model data. `scripts/package_release.sh` runs it on every release before zipping, and `git config core.hooksPath scripts/git-hooks` runs it before each commit.
|
||||
|
||||
+83
-19
@@ -1,20 +1,60 @@
|
||||
#!/bin/sh
|
||||
# Checks an d4r install: run it from the game folder (the one holding the game's .exe and d4r/),
|
||||
# or give that folder as the argument. It only reads files and prints what it finds.
|
||||
# usage: sh d4r/d4r-check.sh [GAME_FOLDER] [ROCM_DIR]
|
||||
# usage: sh plugins/d4r/d4r-check.sh GAME_FOLDER [ROCM_DIR] (or sh d4r/d4r-check.sh for proxy installs)
|
||||
GAME="${1:-.}"
|
||||
D4R="$GAME/d4r"
|
||||
INI="${D4R_OPTISCALER_INI:-$GAME/OptiScaler.ini}"
|
||||
script_dir=$(cd "$(dirname "$0")" 2>/dev/null && pwd -P)
|
||||
if [ -z "${D4R_OPTISCALER_INI:-}" ] && [ ! -f "$INI" ] && [ "${script_dir##*/}" = d4r ]; then
|
||||
loader_dir=$(cd "$script_dir/../.." 2>/dev/null && pwd -P)
|
||||
[ ! -f "$loader_dir/OptiScaler.ini" ] || INI="$loader_dir/OptiScaler.ini"
|
||||
fi
|
||||
wine_file() {
|
||||
windows_path=$(printf '%s' "$1" | tr '\\' '/')
|
||||
case "$windows_path" in
|
||||
[zZ]:*) printf '%s\n' "${windows_path#??}" ;;
|
||||
[a-zA-Z]:*)
|
||||
case "$INI" in
|
||||
*/drive_c/*)
|
||||
prefix="${INI%/drive_c/*}"
|
||||
drive=$(printf '%s' "${windows_path%%:*}" | tr '[:upper:]' '[:lower:]')
|
||||
drive_root=$(readlink -f "$prefix/dosdevices/$drive:" 2>/dev/null)
|
||||
if [ -n "$drive_root" ]; then printf '%s%s\n' "$drive_root" "${windows_path#??}"; else return 1; fi ;;
|
||||
*) return 1 ;;
|
||||
esac ;;
|
||||
*) printf '%s/%s\n' "$GAME" "$windows_path" ;;
|
||||
esac
|
||||
}
|
||||
problems=0
|
||||
ok() { printf ' ok %s\n' "$1"; }
|
||||
bad() { printf ' MISSING %s\n' "$1"; problems=$((problems + 1)); }
|
||||
note() { printf ' note %s\n' "$1"; }
|
||||
misconfigured() { printf ' CONFIG %s\n' "$1"; problems=$((problems + 1)); }
|
||||
ini_value() {
|
||||
awk -v section="$1" -v key="$2" '
|
||||
{ sub(/\r$/, ""); line = $0; sub(/^[ \t]+/, "", line); sub(/[ \t]+$/, "", line) }
|
||||
line ~ /^\[/ { active = (tolower(line) == "[" section "]"); next }
|
||||
active && tolower(line) ~ "^" key "[ \t]*=" {
|
||||
count++; sub(/^[^=]*=[ \t]*/, "", line); value = line
|
||||
}
|
||||
END { if (count != 1) exit 1; print value }
|
||||
' "$INI"
|
||||
}
|
||||
nvngx_path=$(ini_value libraries nvngxpath 2>/dev/null) && unique_path=yes || unique_path=no
|
||||
normalized_path=$(printf '%s' "$nvngx_path" | tr '\\' '/' | tr '[:upper:]' '[:lower:]')
|
||||
asi_backend=no
|
||||
case "$normalized_path" in */d4r.asi) asi_backend=yes ;; esac
|
||||
if [ "$asi_backend" = yes ]; then
|
||||
asi_file=$(wine_file "$nvngx_path") || asi_file=
|
||||
D4R="$(dirname "$asi_file")/d4r"
|
||||
fi
|
||||
# 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" = auto ] && ini_rocm=
|
||||
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}}}"
|
||||
problems=0
|
||||
ok() { printf ' ok %s\n' "$1"; }
|
||||
bad() { printf ' MISSING %s\n' "$1"; problems=$((problems + 1)); }
|
||||
note() { printf ' note %s\n' "$1"; }
|
||||
misconfigured() { printf ' CONFIG %s\n' "$1"; problems=$((problems + 1)); }
|
||||
external_backend=$(awk '
|
||||
{ sub(/\r$/, ""); line = $0; sub(/^[ \t]+/, "", line); sub(/[ \t]+$/, "", line) }
|
||||
line ~ /^\[/ { dlss = (tolower(line) == "[dlss]"); next }
|
||||
@@ -22,31 +62,55 @@ external_backend=$(awk '
|
||||
count++; sub(/^[^=]*=[ \t]*/, "", line); value = tolower(line)
|
||||
}
|
||||
END { if (count == 1 && value == "true") print "true" }
|
||||
' "$GAME/OptiScaler.ini" 2>/dev/null)
|
||||
' "$INI" 2>/dev/null)
|
||||
|
||||
printf 'd4r install in %s\n' "$(cd "$GAME" 2>/dev/null && pwd || echo "$GAME")"
|
||||
[ -d "$D4R" ] || { printf 'no d4r folder here; run this from the folder with the game .exe\n'; exit 1; }
|
||||
ls "$GAME"/*.exe >/dev/null 2>&1 && ok "game executable next to d4r/" || note "no .exe next to d4r/ (it must be the game's main executable folder)"
|
||||
for f in dxgi.dll OptiScaler.ini d3d12.dll d3d12core.dll d4r/nvngx.dll d4r/nvcuda.dll d4r/zluda/libcuda.so d4r/d4r.ini; do
|
||||
if [ "$asi_backend" = yes ]; then
|
||||
loader=no
|
||||
loader_dir=$(dirname "$INI")
|
||||
for folder in "$GAME" "$loader_dir"; do
|
||||
for f in dxgi.dll winmm.dll version.dll winhttp.dll wininet.dll dbghelp.dll OptiScaler.dll; do
|
||||
[ ! -f "$folder/$f" ] || { ok "$folder/$f (verify this is your existing OptiScaler loader)"; loader=yes; }
|
||||
done
|
||||
done
|
||||
[ "$loader" = yes ] || bad 'OptiScaler proxy loader (dxgi.dll, winmm.dll, etc.; nvngx.dll mode does not load ASIs)'
|
||||
else
|
||||
[ -f "$GAME/dxgi.dll" ] && ok dxgi.dll || bad 'dxgi.dll (re-extract the d4r zip)'
|
||||
fi
|
||||
[ -f "$INI" ] && ok "OptiScaler config: $INI" || bad "OptiScaler config: $INI"
|
||||
core_name=d3d12core.dll
|
||||
[ "$asi_backend" != yes ] || [ ! -f "$D4R/d4r12core.dll" ] || core_name=d4r12core.dll
|
||||
for f in d3d12.dll "$core_name"; do
|
||||
[ -f "$GAME/$f" ] && ok "$f" || bad "$f (re-extract the d4r zip)"
|
||||
done
|
||||
for f in nvcuda.dll zluda/libcuda.so d4r.ini; do
|
||||
[ -f "$D4R/$f" ] && ok "$D4R/$f" || bad "$D4R/$f (re-extract the d4r zip)"
|
||||
done
|
||||
[ "$asi_backend" = yes ] || { [ -f "$GAME/d4r/nvngx.dll" ] && ok d4r/nvngx.dll || bad 'd4r/nvngx.dll (re-extract the d4r zip)'; }
|
||||
# File presence alone does not establish NGX routing: OptiDllPath=d4r can still
|
||||
# select system32/_nvngx.dll before d4r/nvngx.dll. Check the per-DLL override.
|
||||
if [ -f "$GAME/OptiScaler.ini" ]; then
|
||||
nvngx_path=$(awk '
|
||||
{ sub(/\r$/, ""); line = $0; sub(/^[ \t]+/, "", line); sub(/[ \t]+$/, "", line) }
|
||||
line ~ /^\[/ { libraries = (tolower(line) == "[libraries]"); next }
|
||||
libraries && tolower(line) ~ /^nvngxpath[ \t]*=/ {
|
||||
count++; sub(/^[^=]*=[ \t]*/, "", line); value = line
|
||||
}
|
||||
END { if (count != 1) exit 1; print value }
|
||||
' "$GAME/OptiScaler.ini") && unique_path=yes || unique_path=no
|
||||
normalized_path=$(printf '%s' "$nvngx_path" | tr '\\' '/' | tr '[:upper:]' '[:lower:]')
|
||||
if [ -f "$INI" ]; then
|
||||
# Also recognize an absolute Wine Z: path to this install's shim (as used
|
||||
# by developer launches); do not accept an arbitrary nvngx.dll elsewhere.
|
||||
game_path=$(cd "$GAME" 2>/dev/null && pwd -P)
|
||||
absolute_path=$(printf 'z:%s/d4r/nvngx.dll' "$game_path" | tr '[:upper:]' '[:lower:]')
|
||||
if [ "$unique_path" = yes ] && { [ "$normalized_path" = 'd4r/nvngx.dll' ] ||
|
||||
if [ "$asi_backend" = yes ]; then
|
||||
plugins_path=$(ini_value plugins path 2>/dev/null) || plugins_path=auto
|
||||
case "$plugins_path" in auto|'') plugins_path=plugins ;; esac
|
||||
plugin_route=$(printf '%s/d4r.asi' "$plugins_path" | tr '\\' '/' | tr '[:upper:]' '[:lower:]')
|
||||
if [ "$unique_path" = yes ] && [ "$normalized_path" = "$plugin_route" ]; then
|
||||
ok "OptiScaler NGX routing: NvngxPath=$nvngx_path (d4r ASI)"
|
||||
else
|
||||
misconfigured 'NvngxPath must select d4r.asi in the configured [Plugins] Path; relaunch to retry automatic setup'
|
||||
fi
|
||||
[ -f "$asi_file" ] && ok "ASI backend: $asi_file" || bad "ASI backend: $asi_file"
|
||||
load_asi=$(ini_value plugins loadasiplugins 2>/dev/null | tr '[:upper:]' '[:lower:]')
|
||||
[ "$load_asi" = true ] || misconfigured 'Set [Plugins] LoadAsiPlugins=true'
|
||||
original_hook=$(ini_value hooks hookoriginalnvngxonly 2>/dev/null | tr '[:upper:]' '[:lower:]')
|
||||
[ "$original_hook" = true ] || misconfigured 'Set [Hooks] HookOriginalNvngxOnly=true'
|
||||
elif [ "$unique_path" = yes ] && { [ "$normalized_path" = 'd4r/nvngx.dll' ] ||
|
||||
[ "$normalized_path" = './d4r/nvngx.dll' ] || [ "$normalized_path" = "$absolute_path" ]; }; then
|
||||
ok "OptiScaler NGX routing: NvngxPath=$nvngx_path (d4r shim)"
|
||||
case "$normalized_path" in
|
||||
|
||||
Reference in new issue
Block a user