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:
ayo committed 2026-10-12 17:36:01 -04:00
1 parent 85175e0b27
commit d5c8be25c8
16 files changed
+1091 -39

No files matched your search

+5
View File
@@ -10,6 +10,9 @@ CLANG_CL="${CLANG_CL:-clang-cl}"
VULKAN_INCLUDE="${VULKAN_INCLUDE:-/usr/include}"
mkdir -p "$ROOT/build"
# Native configuration helper, launched automatically by InitializeASI.
"$MINGW_CXX" -std=c++20 -O2 -Wall -Wextra -Werror -static -municode \
"$ROOT/tools/d4r_asi_setup.cpp" -o "$ROOT/build/d4r-setup.exe"
bash "$ROOT/scripts/build_vulkan_staging.sh"
# Only the Vulkan headers from the host include tree, not its libc headers.
mkdir -p "$ROOT/build/vulkan-include"
@@ -24,3 +27,5 @@ PARAM_OBJECT="$ROOT/build/d4r_ngx_param_msvc.obj"
"$ROOT/engine/runtime.cpp" "$ROOT/engine/game.cpp" "$ROOT/engine/game_m.cpp" "$ROOT/engine/game_rr.cpp" "$ROOT/engine/vk_dispatch.cpp" \
-Wl,--entry,d4r_dll_entry -o "$ROOT/build/d4r_nvngx.dll"
printf 'Built %s\n' "$ROOT/build/d4r_nvngx.dll"
cp "$ROOT/build/d4r_nvngx.dll" "$ROOT/build/d4r.asi"
printf 'Built %s\n' "$ROOT/build/d4r.asi"
+32 -4
View File
@@ -7,6 +7,7 @@
#
# Inputs (environment):
# D4R_OPTISCALER OptiScaler release archive (.7z or .zip) or extracted folder (required; 0.9.4 tested)
# D4R_INSTALL_MODE proxy (default), or asi: add-on for an existing OptiScaler install
# D4R_DLSS_DLLS colon-separated nvngx_dlss.dll files whose kernel code the native kernels accept
# (required; only hashes of their PTX go into the release)
# D4R_ZLUDA_DIR ZLUDA build with patches/zluda applied (libnvcuda.so) default ~/.cache/d4r-zluda-current
@@ -53,13 +54,21 @@ case "${D4R_BUNDLE_NVIDIA:-installer}" in
esac
NAME="d4r-$VERSION"
[[ "$VARIANT" == clean ]] && NAME="$NAME-nonvidia"
INSTALL_MODE="${D4R_INSTALL_MODE:-proxy}"
case "$INSTALL_MODE" in
proxy) ;;
asi) NAME="$NAME-asi" ;;
*) echo "D4R_INSTALL_MODE must be proxy or asi" >&2; exit 2 ;;
esac
STAGE="$OUT/$NAME"
ZLUDA="${D4R_ZLUDA_DIR:-$HOME/.cache/d4r-zluda-current}"
VKD3D="${D4R_VKD3D_DIR:-$HOME/.cache/d4r-vkd3d-d4r}"
ROCM="${D4R_ROCM_DIR:-/opt/rocm}"
ROCM_RUNTIME="${D4R_ROCM_RUNTIME:-$HOME/.cache/d4r-rocm-runtime}"
ARCHS="${D4R_GPU_ARCHS:-gfx1100 gfx1101 gfx1102 gfx1103 gfx1200 gfx1201}"
: "${D4R_OPTISCALER:?set D4R_OPTISCALER to the OptiScaler release archive or folder}"
if [[ "$INSTALL_MODE" == proxy ]]; then
: "${D4R_OPTISCALER:?set D4R_OPTISCALER to the OptiScaler release archive or folder}"
fi
: "${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" "$ROCM_RUNTIME/lib/libamdhip64.so.7"; do
[[ -f "$f" ]] || { echo "missing $f (the ROCm runtime comes from scripts/fetch_rocm_runtime.sh)" >&2; exit 2; }
@@ -80,6 +89,7 @@ TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
# OptiScaler, as dxgi.dll with its INI preconfigured for d4r
if [[ "$INSTALL_MODE" == proxy ]]; then
if [[ -d "$D4R_OPTISCALER" ]]; then
OPTI="$D4R_OPTISCALER"
else
@@ -94,9 +104,20 @@ fi
cp "$OPTI/OptiScaler.dll" "$STAGE/dxgi.dll"
python3 "$ROOT/scripts/configure_optiscaler.py" \
"$OPTI/OptiScaler.ini" "$ROOT/packaging/optiscaler.settings" "$STAGE/OptiScaler.ini"
else
cp "$ROOT/build/d4r-setup.exe" "$STAGE/d4r/d4r-setup.exe"
# Same NGX exports and implementation, delivered through the ASI loader.
# A custom plugin path requires placing this file in that existing folder.
cp "$ROOT/build/d4r_nvngx.dll" "$STAGE/d4r.asi"
fi
# d4r-patched vkd3d-proton: same-frame DLSS results
cp "$VKD3D/d3d12.dll" "$VKD3D/d3d12core.dll" "$STAGE/"
if [[ "$INSTALL_MODE" == asi ]]; then
python3 "$ROOT/scripts/private_d3d12_core.py" "$VKD3D/d3d12.dll" "$STAGE/d4r/d3d12.dll"
cp "$VKD3D/d3d12core.dll" "$STAGE/d4r/d4r12core.dll"
else
cp "$VKD3D/d3d12.dll" "$VKD3D/d3d12core.dll" "$STAGE/"
fi
# d4r itself
cp "$ROOT/build/d4r_nvngx.dll" "$STAGE/d4r/nvngx.dll"
@@ -115,8 +136,10 @@ if [[ "$VARIANT" != clean ]]; then
cp "$D4R_BUNDLE_NGX" "$STAGE/d4r/ngx/_nvngx.dll"
[[ -z "${D4R_BUNDLE_DLSSD:-}" ]] || cp "$D4R_BUNDLE_DLSSD" "$STAGE/d4r/nvngx_dlssd.dll"
else
cp "$ROOT/install.sh" "$STAGE/install.sh"
chmod +x "$STAGE/install.sh"
installer_dir="$STAGE"
[[ "$INSTALL_MODE" != asi ]] || installer_dir="$STAGE/d4r"
cp "$ROOT/install.sh" "$installer_dir/install.sh"
chmod +x "$installer_dir/install.sh"
# The downloader is pinned to DLSS 310.7.0: refuse mismatched release inputs.
expected=$(sed -n "s/^DLSS_SHA256=//p" "$ROOT/install.sh")
[[ "$(sha256sum "$D4R_BUNDLE_DLSS" | cut -d' ' -f1)" == "$expected" ]] || {
@@ -277,6 +300,11 @@ if [[ -n "${D4R_BUILD_INFO:-}" ]]; then
cp "$D4R_BUILD_INFO" "$STAGE/d4r/source/BUILD_INFO.txt"
fi
sed -e "s/@VERSION@/$VERSION/g" "$ROOT/packaging/D4R_README.txt" | variant | sed 's/$/\r/' > "$STAGE/D4R_README.txt"
if [[ "$INSTALL_MODE" == asi ]]; then
rm "$STAGE/D4R_README.txt"
cp "$ROOT/packaging/ASI_README.txt" "$STAGE/d4r/README.txt"
printf '\nASI delivery: drop d4r.asi and d4r/ into the existing plugin folder.\nd4r.asi is d4r/nvngx.dll renamed; d4r/d4r-setup.exe (tools/d4r_asi_setup.cpp) installs the D3D12 pair and configures its route automatically after the game closes.\nThe dxgi.dll / OptiScaler.ini entries above apply only to proxy packages.\nIn ASI mode install.sh, d3d12.dll and the private d4r12core.dll are inside d4r/. The forwarder loader strings are retargeted by scripts/private_d3d12_core.py without changing code addresses.\n' >> "$STAGE/d4r/source/SOURCES.txt"
fi
# Last line of defence for the published zip: only what this variant contains, never values read from DLSS.
python3 "$ROOT/scripts/check_redistributable.py" release "$VARIANT" "$STAGE" ||
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env python3
"""Retarget d4r's vkd3d forwarder to a private core DLL without changing code RVAs."""
from pathlib import Path
import sys
def retarget(data):
original = b'd3d12core.dll\0'
private = b'd4r12core.dll\0'
if not data.startswith(b'MZ') or data.count(original) != 2:
raise ValueError('Expected the d4r-built vkd3d forwarder with two core-loader strings')
result = data.replace(original, private)
assert len(result) == len(data)
return result
if __name__ == '__main__':
try:
Path(sys.argv[2]).write_bytes(retarget(Path(sys.argv[1]).read_bytes()))
except (ValueError, OSError) as error:
sys.exit(str(error))