feat: add d4r inference engines and Windows parity tooling
Add native K/M/RR inference engines, HIP kernels, replay and validation harnesses, and Windows DLSS setup and runtime support. Include GPU profiling tools, build and packaging scripts, platform integration patches, and implementation documentation.
This commit is contained in:
1 parent
25493b3198
commit
7c5d3c9157
430 files changed
+76191
No files matched your search
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CXX="${CXX:-g++}"
|
||||
|
||||
mkdir -p "$ROOT/build"
|
||||
"$CXX" -std=c++20 -O2 -Wall -Wextra \
|
||||
"$ROOT/tools/bench_dlss_kernel.cpp" -ldl \
|
||||
-o "$ROOT/build/bench_dlss_kernel"
|
||||
printf 'Built %s\n' "$ROOT/build/bench_dlss_kernel"
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
MINGW_CXX="${MINGW_CXX:-x86_64-w64-mingw32-g++}"
|
||||
|
||||
"$ROOT/scripts/build_d4r_nvngx_shim.sh"
|
||||
"$MINGW_CXX" -std=c++20 -O2 -Wall -Wextra -I"$ROOT/build/vulkan-include" -static -static-libgcc -static-libstdc++ \
|
||||
"$ROOT/tools/d3d12_dlss_harness.cpp" "$ROOT/tools/d4r_ngx_param_host.cpp" "$ROOT/build/d4r_ngx_param_msvc.obj" \
|
||||
-o "$ROOT/build/d3d12_dlss_harness.exe"
|
||||
printf 'Built %s\n' "$ROOT/build/d3d12_dlss_harness.exe"
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
MINGW_CXX="${MINGW_CXX:-x86_64-w64-mingw32-g++}"
|
||||
|
||||
VULKAN_INCLUDE="${VULKAN_INCLUDE:-/usr/include}"
|
||||
|
||||
# Only the Vulkan headers from the host include tree, not its libc headers.
|
||||
mkdir -p "$ROOT/build/vulkan-include"
|
||||
ln -sfn "$VULKAN_INCLUDE/vulkan" "$ROOT/build/vulkan-include/vulkan"
|
||||
ln -sfn "$VULKAN_INCLUDE/vk_video" "$ROOT/build/vulkan-include/vk_video"
|
||||
"$MINGW_CXX" -I"$ROOT/build/vulkan-include" -std=c++20 -O2 -Wall -Wextra -Wno-missing-field-initializers -static -static-libgcc -static-libstdc++ \
|
||||
"$ROOT/tools/d3d12_hip_interop_probe.cpp" -ld3d12 -o "$ROOT/build/d3d12_hip_interop_probe.exe"
|
||||
printf 'Built %s\n' "$ROOT/build/d3d12_hip_interop_probe.exe"
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build d4r-owned motion processing kernels and embed them in the Wine bridge.
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
OUT="${1:-$ROOT/build/wine-nvcuda/generated}"
|
||||
ROCM="${D4R_ROCM_DIR:-/opt/rocm}"
|
||||
CLANG="$ROCM/lib/llvm/bin/clang++"
|
||||
[[ -x "$CLANG" ]] || { echo "motion kernels need ROCm clang (D4R_ROCM_DIR)" >&2; exit 2; }
|
||||
mkdir -p "$OUT"
|
||||
for arch in gfx1100 gfx1101 gfx1102 gfx1103 gfx1200 gfx1201; do
|
||||
"$CLANG" -x hip -nogpuinc -nogpulib --offload-device-only --offload-arch="$arch" \
|
||||
--no-gpu-bundle-output -O3 -mno-wavefrontsize64 -o "$OUT/$arch.hsaco" \
|
||||
"$ROOT/kernels/support/motion_dilate.hip"
|
||||
done
|
||||
python3 - "$OUT" <<'PY'
|
||||
from pathlib import Path
|
||||
import sys
|
||||
root = Path(sys.argv[1])
|
||||
lines = ['/* Generated from d4r-owned HIP source; do not edit. */', '#include <string.h>']
|
||||
for p in sorted(root.glob('gfx*.hsaco')):
|
||||
data = p.read_bytes()
|
||||
lines.append(f'static const unsigned char motion_{p.stem}[] __attribute__((aligned(64))) = {{')
|
||||
lines.extend(','.join(f'0x{x:02x}' for x in data[i:i+24]) + ',' for i in range(0,len(data),24))
|
||||
lines.append('};')
|
||||
lines.append('static const void* d4r_motion_image(const char* arch) {')
|
||||
for p in sorted(root.glob('gfx*.hsaco')):
|
||||
lines.append(f' if (strcmp(arch, "{p.stem}") == 0) return motion_{p.stem};')
|
||||
lines.append(' return NULL;\n}')
|
||||
(root/'d4r_motion_kernels.h').write_text('\n'.join(lines)+'\n')
|
||||
PY
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CC="${MINGW_CC:-x86_64-w64-mingw32-gcc}"
|
||||
mkdir -p "$ROOT/build"
|
||||
"$CC" -std=c11 -O2 -Wall -Wextra -shared -static-libgcc \
|
||||
"$ROOT/tools/d4r_nvapi_identity.c" -o "$ROOT/build/nvapi64.dll"
|
||||
printf 'Built %s\n' "$ROOT/build/nvapi64.dll"
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CXX="${CXX:-g++}"
|
||||
ROCM_ROOT="${ROCM_ROOT:-/opt/rocm}"
|
||||
|
||||
if [[ ! -f "$ROCM_ROOT/include/hip/hip_runtime_api.h" || ! -f "$ROCM_ROOT/lib/libamdhip64.so" ]]; then
|
||||
printf 'Set ROCM_ROOT to a ROCm installation containing HIP headers and libamdhip64.so\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
mkdir -p "$ROOT/build"
|
||||
"$CXX" -std=c++20 -O2 -Wall -Wextra -D__HIP_PLATFORM_AMD__=1 \
|
||||
-I"$ROCM_ROOT/include" \
|
||||
"$ROOT/tools/hip_vk_interop_probe.cpp" \
|
||||
-L"$ROCM_ROOT/lib" -lamdhip64 -lvulkan \
|
||||
-Wl,-rpath,"$ROCM_ROOT/lib" \
|
||||
-o "$ROOT/build/hip_vk_interop_probe"
|
||||
printf 'Built %s\n' "$ROOT/build/hip_vk_interop_probe"
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
MINGW_CXX="${MINGW_CXX:-x86_64-w64-mingw32-g++}"
|
||||
CLANG_CL="${CLANG_CL:-clang-cl}"
|
||||
|
||||
if [[ -z "${NVSDK_NGX_INCLUDE:-}" || ! -f "$NVSDK_NGX_INCLUDE/nvsdk_ngx.h" ]]; then
|
||||
printf '%s\n' "Set NVSDK_NGX_INCLUDE to a directory containing NVIDIA's nvsdk_ngx.h and nvsdk_ngx_defs.h" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
mkdir -p "$ROOT/build"
|
||||
ABI_OBJECT="$ROOT/build/ngx_param_abi_probe_msvc.obj"
|
||||
"$CLANG_CL" --target=x86_64-pc-windows-msvc /nologo /std:c++20 /O2 /c /GS- /GR- /EHs-c- /Zl \
|
||||
"/Fo$ABI_OBJECT" "$ROOT/tools/ngx_param_abi_probe_msvc.cpp"
|
||||
"$MINGW_CXX" -std=c++20 -O2 -Wall -Wextra -static -static-libgcc -static-libstdc++ \
|
||||
-I"$NVSDK_NGX_INCLUDE" \
|
||||
"$ROOT/tools/ngx_cuda_probe.cpp" "$ABI_OBJECT" \
|
||||
-o "$ROOT/build/ngx_cuda_probe.exe"
|
||||
printf 'Built %s\n' "$ROOT/build/ngx_cuda_probe.exe"
|
||||
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
MINGW_CXX="${MINGW_CXX:-x86_64-w64-mingw32-g++}"
|
||||
|
||||
if [[ -z "${NVSDK_NGX_INCLUDE:-}" || ! -f "$NVSDK_NGX_INCLUDE/nvsdk_ngx.h" ]]; then
|
||||
printf '%s\n' "Set NVSDK_NGX_INCLUDE to a directory containing NVIDIA's nvsdk_ngx.h and nvsdk_ngx_defs.h" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
mkdir -p "$ROOT/build"
|
||||
"$MINGW_CXX" -std=c++20 -O2 -Wall -Wextra -static -static-libgcc -static-libstdc++ \
|
||||
-I"$NVSDK_NGX_INCLUDE" \
|
||||
"$ROOT/tools/ngx_d3d12_probe.cpp" \
|
||||
-o "$ROOT/build/ngx_d3d12_probe.exe"
|
||||
printf 'Built %s\n' "$ROOT/build/ngx_d3d12_probe.exe"
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
MINGW_CXX="${MINGW_CXX:-x86_64-w64-mingw32-g++}"
|
||||
|
||||
mkdir -p "$ROOT/build"
|
||||
"$MINGW_CXX" -std=c++20 -O2 -Wall -Wextra -Werror \
|
||||
"$ROOT/tools/ngx_load_probe.cpp" \
|
||||
-o "$ROOT/build/ngx_load_probe.exe"
|
||||
printf 'Built %s\n' "$ROOT/build/ngx_load_probe.exe"
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
MINGW_CXX="${MINGW_CXX:-x86_64-w64-mingw32-g++}"
|
||||
|
||||
if [[ -z "${NVSDK_NGX_INCLUDE:-}" || ! -f "$NVSDK_NGX_INCLUDE/nvsdk_ngx_vk.h" ]]; then
|
||||
printf '%s\n' "Set NVSDK_NGX_INCLUDE to a directory containing NVIDIA's nvsdk_ngx_vk.h and nvsdk_ngx_defs.h" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
VULKAN_INCLUDE="${VULKAN_INCLUDE:-$(dirname "$NVSDK_NGX_INCLUDE")/vulkan/include}"
|
||||
if [[ ! -f "$VULKAN_INCLUDE/vulkan/vulkan.h" ]]; then
|
||||
printf '%s\n' "Set VULKAN_INCLUDE to a Windows-target Vulkan-Headers include root" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
mkdir -p "$ROOT/build"
|
||||
"$MINGW_CXX" -std=c++20 -O2 -Wall -Wextra -static -static-libgcc -static-libstdc++ \
|
||||
-I"$NVSDK_NGX_INCLUDE" -I"$VULKAN_INCLUDE" \
|
||||
"$ROOT/tools/ngx_vulkan_probe.cpp" \
|
||||
-o "$ROOT/build/ngx_vulkan_probe.exe"
|
||||
printf 'Built %s\n' "$ROOT/build/ngx_vulkan_probe.exe"
|
||||
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run inside packaging/build/Dockerfile.glibc241 with a recent official Rust toolchain and ROCm mounted.
|
||||
# Supply isolated, patched ZLUDA/vkd3d checkouts and the usual package_release.sh inputs.
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
: "${D4R_RELEASE_BUILD_ROOT:?set the build output directory}"
|
||||
: "${D4R_ZLUDA_SRC:?set a build-local ZLUDA checkout with patches 0002-0007 applied}"
|
||||
: "${D4R_VKD3D_SRC:?set a build-local vkd3d-proton checkout with patches 0001 and 0002 applied}"
|
||||
: "${D4R_ROCM_DIR:?set ROCm with clang and device libraries}"
|
||||
BUILD="$(realpath -m "$D4R_RELEASE_BUILD_ROOT")"
|
||||
[[ "$(getconf GNU_LIBC_VERSION)" == 'glibc 2.41' ]] || {
|
||||
echo "This release build requires GLIBC 2.41; use the pinned Debian build image" >&2; exit 2;
|
||||
}
|
||||
# Helper generation modifies its source checkout. Refuse the external source-cache directory.
|
||||
case "$(realpath "$D4R_ZLUDA_SRC")" in "$BUILD"/*) ;; *) echo "ZLUDA source must be inside $BUILD" >&2; exit 2 ;; esac
|
||||
case "$(realpath "$D4R_VKD3D_SRC")" in "$BUILD"/*) ;; *) echo "vkd3d source must be inside $BUILD" >&2; exit 2 ;; esac
|
||||
mkdir -p "$BUILD/logs"
|
||||
for source in "$D4R_ZLUDA_SRC/ext/llvm-project/llvm/CMakeLists.txt" "$D4R_ZLUDA_SRC/ext/HiGHS/CMakeLists.txt"; do
|
||||
[[ -f "$source" ]] || { echo "missing pinned source submodule: $source" >&2; exit 2; }
|
||||
done
|
||||
export CARGO_TARGET_DIR="$BUILD/zluda-target"
|
||||
export CARGO_BUILD_JOBS="${CARGO_BUILD_JOBS:-8}"
|
||||
export CMAKE_BUILD_PARALLEL_LEVEL="$CARGO_BUILD_JOBS"
|
||||
export LIBRARY_PATH="$D4R_ROCM_DIR/lib${D4R_ROCM_LINK_STUBS:+:$D4R_ROCM_LINK_STUBS}"
|
||||
export LD_LIBRARY_PATH="$D4R_ROCM_DIR/lib"
|
||||
export HIP_PATH="$D4R_ROCM_DIR"
|
||||
export ROCM_PATH="$D4R_ROCM_DIR"
|
||||
export HIP_CLANG_PATH="$D4R_ROCM_DIR/lib/llvm/bin"
|
||||
{
|
||||
getconf GNU_LIBC_VERSION
|
||||
rustc --version
|
||||
cargo --version
|
||||
gcc --version | head -1
|
||||
winegcc --version | head -1
|
||||
"$D4R_ROCM_DIR/lib/llvm/bin/clang++" --version | head -1
|
||||
} > "$BUILD/toolchain.txt"
|
||||
|
||||
ZLUDA_SOURCE_ROOT="$D4R_ZLUDA_SRC" ROCM_ROOT="$D4R_ROCM_DIR" \
|
||||
D4R_ZLUDA_PTX_HELPER_WORK="$BUILD/helpers" "$ROOT/scripts/build_zluda_ptx_helpers.sh" \
|
||||
> "$BUILD/logs/helpers.log" 2>&1
|
||||
echo 'Device helpers rebuilt'
|
||||
(cd "$D4R_ZLUDA_SRC" && cargo build --offline --locked --release -p zluda \
|
||||
&& cargo build --offline --locked --release -p ptx --example d4r_emit) \
|
||||
> "$BUILD/logs/zluda.log" 2>&1
|
||||
export D4R_ZLUDA_DIR="$CARGO_TARGET_DIR/release"
|
||||
export D4R_ZLUDA_EMIT="$D4R_ZLUDA_DIR/examples/d4r_emit"
|
||||
"$ROOT/scripts/check_glibc_compat.sh" 2.41 "$D4R_ZLUDA_DIR/libnvcuda.so"
|
||||
echo 'ZLUDA and the offline emitter rebuilt; GLIBC check passed'
|
||||
# A minimal ROCm compiler extraction may omit lld. Use the matching LLVM built above so
|
||||
# all GPU targets link with the same AMDGPU/ELF support, inside the compatible environment.
|
||||
for llvm_build in "$CARGO_TARGET_DIR"/release/build/llvm_zluda-*/out/build; do
|
||||
[[ -f "$llvm_build/build.ninja" ]] || continue
|
||||
ninja -j "$CARGO_BUILD_JOBS" -C "$llvm_build" lld > "$BUILD/logs/lld.log" 2>&1
|
||||
export PATH="$llvm_build/bin:$PATH"
|
||||
break
|
||||
done
|
||||
command -v lld >/dev/null || { echo 'missing AMDGPU linker' >&2; exit 2; }
|
||||
|
||||
"$ROOT/scripts/build_d4r_nvngx_shim.sh" > "$BUILD/logs/shim.log" 2>&1
|
||||
"$ROOT/scripts/build_wine_nvcuda_bridge.sh" > "$BUILD/logs/bridge.log" 2>&1
|
||||
"$ROOT/scripts/check_glibc_compat.sh" 2.41 "$ROOT/build/wine-nvcuda/x86_64-unix/nvcuda.dll.so"
|
||||
if [[ ! -f "$BUILD/vkd3d-build/build.ninja" ]]; then
|
||||
meson setup --cross-file "$D4R_VKD3D_SRC/build-win64.txt" --buildtype release \
|
||||
-Denable_tests=false "$BUILD/vkd3d-build" "$D4R_VKD3D_SRC" > "$BUILD/logs/vkd3d.log" 2>&1
|
||||
fi
|
||||
ninja -j "$CARGO_BUILD_JOBS" -C "$BUILD/vkd3d-build" >> "$BUILD/logs/vkd3d.log" 2>&1
|
||||
export D4R_VKD3D_DIR="$BUILD/vkd3d"
|
||||
mkdir -p "$D4R_VKD3D_DIR"
|
||||
x86_64-w64-mingw32-strip -o "$D4R_VKD3D_DIR/d3d12.dll" "$BUILD/vkd3d-build/libs/d3d12/d3d12.dll"
|
||||
x86_64-w64-mingw32-strip -o "$D4R_VKD3D_DIR/d3d12core.dll" "$BUILD/vkd3d-build/libs/d3d12core/d3d12core.dll"
|
||||
echo 'Shim, bridge and vkd3d-proton rebuilt'
|
||||
|
||||
export D4R_BUNDLE_TEX="$BUILD/kernels"
|
||||
export D4R_DLSS_PTX_DIR="$BUILD/ptx"
|
||||
python3 "$ROOT/kernels/tools/extract_dlss_ptx.py" "$D4R_BUNDLE_DLSS" "$D4R_DLSS_PTX_DIR" > "$BUILD/logs/extract.log"
|
||||
build_textures() {
|
||||
local arch="$1" folder fp8 accuracy out
|
||||
folders=("$arch")
|
||||
[[ "$arch" == gfx12* ]] && folders+=("$arch-fp8")
|
||||
for folder in "${folders[@]}"; do
|
||||
fp8=0
|
||||
[[ "$folder" == *-fp8 ]] && fp8=1
|
||||
for accuracy in 0 1; do
|
||||
out="$D4R_BUNDLE_TEX/$folder"
|
||||
[[ "$accuracy" == 1 ]] && out="$D4R_BUNDLE_TEX/accuracy/$folder"
|
||||
D4R_PREFER_ACCURACY="$accuracy" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" \
|
||||
D4R_DLSS_DLL="$D4R_BUNDLE_DLSS" "$ROOT/kernels/build.sh" tex "$out" \
|
||||
> "$BUILD/logs/tex-$folder-$accuracy.log" 2>&1
|
||||
echo "Texture kernels rebuilt: $folder accuracy=$accuracy"
|
||||
done
|
||||
done
|
||||
}
|
||||
pending=()
|
||||
failed=0
|
||||
for arch in ${D4R_GPU_ARCHS:-gfx1100 gfx1101 gfx1102 gfx1103 gfx1200 gfx1201}; do
|
||||
build_textures "$arch" &
|
||||
pending+=("$!")
|
||||
if (( ${#pending[@]} >= 4 )); then
|
||||
wait "${pending[0]}" || failed=1
|
||||
pending=("${pending[@]:1}")
|
||||
fi
|
||||
done
|
||||
for pid in "${pending[@]}"; do wait "$pid" || failed=1; done
|
||||
[[ "$failed" == 0 ]] || { echo 'Texture target build failed' >&2; exit 1; }
|
||||
python3 -m unittest discover -s "$ROOT/tests" -v > "$BUILD/logs/tests.log" 2>&1
|
||||
export D4R_SKIP_BUILD=1
|
||||
export D4R_MAX_GLIBC=2.41
|
||||
export D4R_BUILD_INFO="$BUILD/toolchain.txt"
|
||||
export D4R_PACKAGE_KERNEL_JOBS=4
|
||||
"$ROOT/scripts/package_release.sh" "$BUILD/dist" > "$BUILD/logs/package.log" 2>&1
|
||||
echo "Release packaged in $BUILD/dist"
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CXX="${CXX:-g++}"
|
||||
|
||||
mkdir -p "$ROOT/build"
|
||||
"$CXX" -std=c++20 -O2 -Wall -Wextra \
|
||||
"$ROOT/tools/replay_dlss_fp8_ops.cpp" -ldl \
|
||||
-o "$ROOT/build/replay_dlss_fp8_ops"
|
||||
printf 'Built %s\n' "$ROOT/build/replay_dlss_fp8_ops"
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CXX="${CXX:-g++}"
|
||||
ROCM_ROOT="${ROCM_ROOT:-/opt/rocm}"
|
||||
if [[ ! -f "$ROCM_ROOT/include/hip/hip_runtime_api.h" || ! -f "$ROCM_ROOT/lib/libamdhip64.so" ]]; then
|
||||
printf 'Set ROCM_ROOT to a ROCm installation containing HIP headers and libamdhip64.so\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
mkdir -p "$ROOT/build"
|
||||
"$CXX" -std=c++20 -O2 -Wall -Wextra -D__HIP_PLATFORM_AMD__=1 \
|
||||
-I"$ROCM_ROOT/include" \
|
||||
"$ROOT/tools/replay_dlss_histogram.cpp" -L"$ROCM_ROOT/lib" -lamdhip64 -ldl \
|
||||
-Wl,-rpath,"$ROCM_ROOT/lib" \
|
||||
-o "$ROOT/build/replay_dlss_histogram"
|
||||
printf 'Built %s\n' "$ROOT/build/replay_dlss_histogram"
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CXX="${CXX:-g++}"
|
||||
|
||||
mkdir -p "$ROOT/build"
|
||||
"$CXX" -std=c++20 -O2 -Wall -Wextra \
|
||||
"$ROOT/tools/replay_dlss_kernel.cpp" -ldl \
|
||||
-o "$ROOT/build/replay_dlss_kernel"
|
||||
printf 'Built %s\n' "$ROOT/build/replay_dlss_kernel"
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CXX="${CXX:-g++}"
|
||||
|
||||
mkdir -p "$ROOT/build"
|
||||
"$CXX" -std=c++20 -O2 -Wall -Wextra \
|
||||
"$ROOT/tools/replay_dlss_layer.cpp" -ldl \
|
||||
-o "$ROOT/build/replay_dlss_layer"
|
||||
printf 'Built %s\n' "$ROOT/build/replay_dlss_layer"
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CXX="${CXX:-g++}"
|
||||
|
||||
mkdir -p "$ROOT/build"
|
||||
"$CXX" -std=c++20 -O2 -Wall -Wextra \
|
||||
"$ROOT/tools/replay_dlss_surface_half_store.cpp" -ldl \
|
||||
-o "$ROOT/build/replay_dlss_surface_half_store"
|
||||
printf 'Built %s\n' "$ROOT/build/replay_dlss_surface_half_store"
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CXX="${CXX:-g++}"
|
||||
|
||||
mkdir -p "$ROOT/build"
|
||||
"$CXX" -std=c++20 -O2 -Wall -Wextra \
|
||||
"$ROOT/tools/replay_dlss_surface_store.cpp" -ldl \
|
||||
-o "$ROOT/build/replay_dlss_surface_store"
|
||||
printf 'Built %s\n' "$ROOT/build/replay_dlss_surface_store"
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CXX="${CXX:-g++}"
|
||||
|
||||
mkdir -p "$ROOT/build"
|
||||
"$CXX" -std=c++20 -O2 -Wall -Wextra \
|
||||
"$ROOT/tools/replay_dlss_texture_sample.cpp" -ldl \
|
||||
-o "$ROOT/build/replay_dlss_texture_sample"
|
||||
printf 'Built %s\n' "$ROOT/build/replay_dlss_texture_sample"
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Builds vkd3d-proton with patches/vkd3d-proton/ applied (split command lists
|
||||
# for the shim's same-frame DLSS, D4R_SHIM_SPLIT_FRAME) into OUT_DIR, which
|
||||
# the game launcher takes as D4R_VKD3D_DIR.
|
||||
# usage: build_vkd3d_proton_d4r.sh OUT_DIR
|
||||
# Env: VKD3D_PROTON_SRC (clone, default ~/.cache/d4r-vkd3d-proton),
|
||||
# VKD3D_PROTON_COMMIT (default 3dfc6f07, the base GE-Proton11-3 ships).
|
||||
if [[ $# -ne 1 ]]; then
|
||||
printf 'usage: %s OUT_DIR\n' "$0" >&2
|
||||
exit 2
|
||||
fi
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
OUT="$(realpath -m "$1")"
|
||||
SRC="${VKD3D_PROTON_SRC:-$HOME/.cache/d4r-vkd3d-proton}"
|
||||
COMMIT="${VKD3D_PROTON_COMMIT:-3dfc6f07}"
|
||||
BUILD="$SRC-build"
|
||||
|
||||
[[ -d "$SRC/.git" ]] || git clone https://github.com/HansKristian-Work/vkd3d-proton.git "$SRC"
|
||||
# -f: the clone is a build cache; the patch is reapplied below.
|
||||
git -C "$SRC" checkout -q -f -B d4r-split "$COMMIT"
|
||||
git -C "$SRC" submodule update --init --recursive -q
|
||||
git -C "$SRC" apply "$ROOT"/patches/vkd3d-proton/*.patch
|
||||
[[ -d "$BUILD" ]] || meson setup --cross-file "$SRC/build-win64.txt" --buildtype release -Denable_tests=false "$BUILD" "$SRC"
|
||||
ninja -C "$BUILD"
|
||||
mkdir -p "$OUT"
|
||||
x86_64-w64-mingw32-strip -o "$OUT/d3d12.dll" "$BUILD/libs/d3d12/d3d12.dll"
|
||||
x86_64-w64-mingw32-strip -o "$OUT/d3d12core.dll" "$BUILD/libs/d3d12core/d3d12core.dll"
|
||||
printf 'Built %s/d3d12.dll and d3d12core.dll\n' "$OUT"
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
WINEGCC="${WINEGCC:-winegcc}"
|
||||
WINEBUILD="${WINEBUILD:-winebuild}"
|
||||
BRIDGE_ROOT="$ROOT/build/wine-nvcuda"
|
||||
|
||||
mkdir -p "$BRIDGE_ROOT/x86_64-unix" "$BRIDGE_ROOT/x86_64-windows"
|
||||
MOTION_FLAGS=()
|
||||
if [[ -x "${D4R_ROCM_DIR:-/opt/rocm}/lib/llvm/bin/clang++" ]]; then
|
||||
"$ROOT/scripts/build_d4r_motion_kernels.sh" "$BRIDGE_ROOT/generated"
|
||||
MOTION_FLAGS=(-DD4R_MOTION_KERNELS -I"$BRIDGE_ROOT/generated")
|
||||
fi
|
||||
"$WINEGCC" -m64 -shared -o "$BRIDGE_ROOT/x86_64-unix/nvcuda.dll" \
|
||||
"${MOTION_FLAGS[@]}" \
|
||||
"$ROOT/tools/wine_nvcuda_bridge.c" \
|
||||
-Wb,--export="$ROOT/tools/wine_nvcuda_bridge.spec" -ldl -lpthread
|
||||
"$WINEBUILD" --dll --fake-module -E "$ROOT/tools/wine_nvcuda_bridge.spec" \
|
||||
-F nvcuda.dll -b x86_64-windows -o "$BRIDGE_ROOT/x86_64-windows/nvcuda.dll"
|
||||
printf 'Built Wine nvcuda bridge in %s\n' "$BRIDGE_ROOT"
|
||||
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ZLUDA_SOURCE_ROOT="${ZLUDA_SOURCE_ROOT:-}"
|
||||
ROCM_ROOT="${ROCM_ROOT:-/opt/rocm}"
|
||||
HIPCC="${HIPCC:-$ROCM_ROOT/bin/hipcc}"
|
||||
LLVM_DIS="${LLVM_DIS:-$ROCM_ROOT/lib/llvm/bin/llvm-dis}"
|
||||
LLVM_AS="${LLVM_AS:-$ROCM_ROOT/lib/llvm/bin/llvm-as}"
|
||||
OCML="${OCML:-$ROCM_ROOT/amdgcn/bitcode/ocml.bc}"
|
||||
WORK_ROOT="${D4R_ZLUDA_PTX_HELPER_WORK:-${TMPDIR:-/tmp}/d4r-zluda-ptx-helpers}"
|
||||
# D4R_ZLUDA_HELPER_FLAGS: extra compiler flags, e.g. -DD4R_WMMA12_SHIM for the gfx12-layout test build (never
|
||||
# ship it: its gfx12 helpers use gfx11 WMMAs)
|
||||
read -r -a EXTRA_FLAGS <<< "${D4R_ZLUDA_HELPER_FLAGS:-}"
|
||||
|
||||
if [[ -z "$ZLUDA_SOURCE_ROOT" || ! -f "$ZLUDA_SOURCE_ROOT/ptx/lib/zluda_ptx_impl.cpp" ]]; then
|
||||
printf 'Set ZLUDA_SOURCE_ROOT to a ZLUDA source checkout\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
for dependency in "$HIPCC" "$LLVM_DIS" "$LLVM_AS" "$OCML"; do
|
||||
if [[ ! -e "$dependency" ]]; then
|
||||
printf 'Required ROCm/LLVM helper is missing: %s\n' "$dependency" >&2
|
||||
exit 2
|
||||
fi
|
||||
done
|
||||
|
||||
mkdir -p "$WORK_ROOT"
|
||||
SOURCE="$ZLUDA_SOURCE_ROOT/ptx/lib/zluda_ptx_impl.cpp"
|
||||
PTX_LIB="$ZLUDA_SOURCE_ROOT/ptx/lib"
|
||||
COMMON_FLAGS=(-DHIP_ENABLE_WARP_SYNC_BUILTINS -std=c++20 -Xclang -fdenormal-fp-math=dynamic
|
||||
--rocm-path="$ROCM_ROOT" -I"$ROCM_ROOT/include"
|
||||
-Wall -Wextra -Wsign-compare -Wconversion -x hip "$SOURCE" -nogpulib -O3
|
||||
-mno-wavefrontsize64 --offload-device-only --offload-arch=gfx1030 -emit-llvm -c
|
||||
-Xclang -mlink-bitcode-file -Xclang "$OCML" "${EXTRA_FLAGS[@]}")
|
||||
|
||||
build_one() {
|
||||
local suffix="$1"
|
||||
shift
|
||||
local raw="$WORK_ROOT/zluda_ptx_impl${suffix}.raw.bc"
|
||||
local clean="$WORK_ROOT/zluda_ptx_impl${suffix}.clean.bc"
|
||||
local ll="$WORK_ROOT/zluda_ptx_impl${suffix}.ll"
|
||||
local output="$PTX_LIB/zluda_ptx_impl${suffix}.bc"
|
||||
|
||||
"$HIPCC" "${COMMON_FLAGS[@]}" "$@" -o "$raw"
|
||||
"$LLVM_DIS" "$raw" -o "$ll"
|
||||
sed -E \
|
||||
-e '/@llvm.used/d' \
|
||||
-e '/wchar_size/d' \
|
||||
-e '/llvm.module.flags/d' \
|
||||
-e '/__hip_cuid/d' \
|
||||
-e 's/optnone//g' \
|
||||
-e 's/define hidden/define linkonce_odr/g' \
|
||||
-e 's/"target-cpu"="gfx1030"//g' \
|
||||
-e 's/"target-features"="[^"]+"//g' "$ll" |
|
||||
"$LLVM_AS" -o "$clean" -
|
||||
mv "$clean" "$output"
|
||||
printf 'Wrote %s (%s bytes)\n' "$output" "$(stat -c '%s' "$output")"
|
||||
}
|
||||
|
||||
build_one ""
|
||||
build_one "_constrained" -ffp-model=strict -ffp-exception-behavior=ignore
|
||||
# wave64 helpers (D4R_ZLUDA_WAVE64: two CUDA warps per wave; see d4r_warp_base in zluda_ptx_impl.cpp)
|
||||
build_one "_w64" -mwavefrontsize64 -DD4R_WAVE64
|
||||
build_one "_constrained_w64" -ffp-model=strict -ffp-exception-behavior=ignore -mwavefrontsize64 -DD4R_WAVE64
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
section() {
|
||||
printf '\n## %s\n' "$1"
|
||||
}
|
||||
|
||||
show_if_available() {
|
||||
local tool="$1"
|
||||
shift
|
||||
if command -v "$tool" >/dev/null 2>&1; then
|
||||
"$tool" "$@" 2>&1 || true
|
||||
else
|
||||
printf '%s: not found\n' "$tool"
|
||||
fi
|
||||
}
|
||||
|
||||
section 'Operating system'
|
||||
uname -a
|
||||
|
||||
section 'Display adapters and kernel drivers'
|
||||
show_if_available lspci -nnk | rg -A4 -i 'vga|3d|display' || true
|
||||
|
||||
section 'ROCm / HIP'
|
||||
show_if_available rocminfo | head -80 || true
|
||||
show_if_available hipcc --version | head -20 || true
|
||||
if command -v rocm-smi >/dev/null 2>&1; then
|
||||
rocm-smi --showproductname --showdriverversion 2>&1 | head -50 || true
|
||||
elif [[ -x /opt/rocm/bin/rocm-smi ]]; then
|
||||
/opt/rocm/bin/rocm-smi --showproductname --showdriverversion 2>&1 | head -50 || true
|
||||
fi
|
||||
|
||||
section 'Vulkan'
|
||||
if command -v vulkaninfo >/dev/null 2>&1; then
|
||||
vulkaninfo --summary 2>&1 | rg -i 'VULKANINFO|Instance Version|GPU[0-9]|apiVersion|driverVersion|vendorID|deviceID|deviceName|driverID|driverName|driverInfo' || true
|
||||
else
|
||||
printf 'vulkaninfo: not found\n'
|
||||
fi
|
||||
|
||||
section 'Wine / Proton'
|
||||
show_if_available wine --version
|
||||
show_if_available protontricks --version
|
||||
find "$HOME/.local/share/Steam/compatibilitytools.d" -maxdepth 3 -type f -name proton -print 2>/dev/null | head -20 || true
|
||||
|
||||
section 'Build and PE inspection tools'
|
||||
for tool in x86_64-w64-mingw32-g++ cmake ninja llvm-objdump llvm-readobj objdump strings; do
|
||||
if command -v "$tool" >/dev/null 2>&1; then
|
||||
printf '%-28s %s\n' "$tool" "$(command -v "$tool")"
|
||||
else
|
||||
printf '%-28s %s\n' "$tool" 'not found'
|
||||
fi
|
||||
done
|
||||
|
||||
section 'Installed relevant Arch packages'
|
||||
if command -v pacman >/dev/null 2>&1; then
|
||||
pacman -Q 2>/dev/null | rg -i '(^| )(rocm|hip|vulkan|wine|mingw|clang|mesa)(-| )' || true
|
||||
else
|
||||
printf 'pacman: not found\n'
|
||||
fi
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
# Reject Linux release libraries that need a newer GLIBC than the target runtime.
|
||||
# Usage: check_glibc_compat.sh MAX_GLIBC LIBRARY [LIBRARY ...]
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -lt 2 || ! "$1" =~ ^[0-9]+\.[0-9]+$ ]]; then
|
||||
printf 'usage: %s MAX_GLIBC LIBRARY [LIBRARY ...]\n' "$0" >&2
|
||||
exit 2
|
||||
fi
|
||||
max="GLIBC_$1"
|
||||
shift
|
||||
command -v readelf >/dev/null || { echo 'readelf is required to check GLIBC compatibility' >&2; exit 2; }
|
||||
|
||||
failed=0
|
||||
for library in "$@"; do
|
||||
if [[ ! -f "$library" ]]; then
|
||||
printf 'missing library: %s\n' "$library" >&2
|
||||
failed=1
|
||||
continue
|
||||
fi
|
||||
if ! versions="$(readelf -W --version-info "$library" 2>&1)"; then
|
||||
printf 'cannot inspect %s: %s\n' "$library" "$versions" >&2
|
||||
failed=1
|
||||
continue
|
||||
fi
|
||||
required="$(printf '%s\n' "$versions" | grep -Eo 'GLIBC_[0-9]+\.[0-9]+' | sort -Vu | tail -1 || true)"
|
||||
if [[ -n "$required" && "$(printf '%s\n%s\n' "$max" "$required" | sort -Vu | tail -1)" != "$max" ]]; then
|
||||
printf '%s requires %s (target: at most %s)\n' "$library" "$required" "$max" >&2
|
||||
failed=1
|
||||
fi
|
||||
done
|
||||
exit "$failed"
|
||||
@@ -0,0 +1,210 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Translate d4r.ini (see config/d4r.ini.default) into shell exports for the game launcher.
|
||||
|
||||
usage: d4r_config.py [--config PATH] [--print | --launch]
|
||||
Prints "export VAR=value" lines for every setting the INI decides, skipping variables that are
|
||||
already set in the environment (explicit environment always wins). --print lists the resulting
|
||||
settings for humans instead. Without --config: $D4R_CONFIG, else ~/.config/d4r/d4r.ini, which is
|
||||
created from config/d4r.ini.default on first use.
|
||||
"""
|
||||
import configparser
|
||||
import os
|
||||
import shlex
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
DEFAULT_TEMPLATE = ROOT / "config" / "d4r.ini.default"
|
||||
PRESETS = {"A": 1, "B": 2, "C": 3, "D": 4, "E": 5, "F": 6, "G": 7, "J": 10, "K": 11, "L": 12, "M": 13}
|
||||
# friendly names for [DLSS] Model
|
||||
MODEL_ALIASES = {"CNN": "E", "DLSS3": "E", "DLSS4": "K", "DLSS4.5": "M", "TRANSFORMER": "K"}
|
||||
|
||||
|
||||
def fail(message):
|
||||
print(f"d4r config: {message}", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
def config_path(argv):
|
||||
if "--config" in argv:
|
||||
return Path(argv[argv.index("--config") + 1]).expanduser()
|
||||
if os.environ.get("D4R_CONFIG"):
|
||||
return Path(os.environ["D4R_CONFIG"]).expanduser()
|
||||
path = Path.home() / ".config" / "d4r" / "d4r.ini"
|
||||
if not path.exists() and DEFAULT_TEMPLATE.exists():
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copyfile(DEFAULT_TEMPLATE, path)
|
||||
print(f"d4r config: created {path} from the default template; edit it to change settings",
|
||||
file=sys.stderr)
|
||||
return path
|
||||
|
||||
|
||||
def main(argv):
|
||||
path = config_path(argv)
|
||||
ini = configparser.ConfigParser(inline_comment_prefixes=(";", "#"), interpolation=None)
|
||||
ini.optionxform = str # keep [Env] variable names as written
|
||||
if path.exists():
|
||||
ini.read(path)
|
||||
|
||||
def get(section, key):
|
||||
value = ini.get(section, key, fallback="").strip()
|
||||
return None if value == "" or value.lower() == "auto" else value
|
||||
|
||||
def flag(section, key):
|
||||
value = get(section, key)
|
||||
if value is None:
|
||||
return None
|
||||
if value.lower() in ("1", "true", "yes", "on"):
|
||||
return True
|
||||
if value.lower() in ("0", "false", "no", "off"):
|
||||
return False
|
||||
fail(f"[{section}] {key} must be true or false, not {value!r}")
|
||||
|
||||
def path_value(section, key):
|
||||
value = get(section, key)
|
||||
return os.path.expanduser(value) if value else None
|
||||
|
||||
if "--launch" in argv:
|
||||
# PROTON_DIR, COMPAT_DATA_DIR and GAME_EXE for scripts/d4r_play.sh, one per line
|
||||
values = [path_value("Launch", key) for key in ("ProtonDir", "CompatDataDir", "GameExe")]
|
||||
missing = [key for key, value in zip(("ProtonDir", "CompatDataDir", "GameExe"), values) if not value]
|
||||
if missing:
|
||||
fail(f"set [Launch] {', '.join(missing)} in {path}")
|
||||
print("\n".join(values))
|
||||
return
|
||||
|
||||
env = {}
|
||||
env["D4R_PREFER_ACCURACY"] = "1" if flag("Kernels", "PreferAccuracy") else "0"
|
||||
|
||||
model = get("DLSS", "Model")
|
||||
if model is not None:
|
||||
if model.isdigit():
|
||||
env["D4R_DLSS_PRESET"] = model
|
||||
elif model.upper() in PRESETS or model.upper() in MODEL_ALIASES:
|
||||
env["D4R_DLSS_PRESET"] = str(PRESETS[MODEL_ALIASES.get(model.upper(), model.upper())])
|
||||
else:
|
||||
fail(f"[DLSS] Model {model!r}: use one of {', '.join(list(PRESETS) + list(MODEL_ALIASES))} or a preset number")
|
||||
|
||||
age = get("Latency", "FrameAge")
|
||||
if age is not None:
|
||||
if age not in ("0", "1", "2", "3"):
|
||||
fail(f"[Latency] FrameAge must be 0-3, not {age!r}")
|
||||
if age == "0":
|
||||
# same-frame results: the game's command list is split around DLSS (patched vkd3d-proton)
|
||||
env["D4R_SHIM_SPLIT_FRAME"] = "1"
|
||||
else:
|
||||
env["D4R_SHIM_SPLIT_FRAME"] = "0"
|
||||
env["D4R_SHIM_MAX_IN_FLIGHT"] = age
|
||||
|
||||
for key, var in (("RuntimeDir", "D4R_RUNTIME_DIR"), ("ZludaDir", "D4R_ZLUDA_DIR"), ("RocmDir", "D4R_ROCM_DIR"),
|
||||
("VkD3DDir", "D4R_VKD3D_DIR")):
|
||||
value = path_value("Paths", key)
|
||||
if value:
|
||||
env[var] = value
|
||||
# JIT caches (ZLUDA's compiled kernels among them): a directory, or per-zluda for the one the test
|
||||
# runs warm for each ZLUDA build (~/.cache/d4r-perf-game-jit-<ZludaDir name>)
|
||||
jit = get("Paths", "JitCacheDir")
|
||||
if jit is not None:
|
||||
if jit.lower() == "per-zluda":
|
||||
zluda = env.get("D4R_ZLUDA_DIR") or os.environ.get("D4R_ZLUDA_DIR")
|
||||
if not zluda:
|
||||
fail("[Paths] JitCacheDir = per-zluda needs [Paths] ZludaDir")
|
||||
jit = f"~/.cache/d4r-perf-game-jit-{Path(zluda).name}"
|
||||
env["XDG_CACHE_HOME"] = os.path.expanduser(jit)
|
||||
if age == "0" and "D4R_VKD3D_DIR" not in env and not os.environ.get("D4R_VKD3D_DIR"):
|
||||
print("d4r config: FrameAge = 0 needs [Paths] VkD3DDir (the d4r-patched vkd3d-proton); without it the "
|
||||
"shim falls back to older frames", file=sys.stderr)
|
||||
|
||||
mode = (get("Kernels", "NativeKernels") or "off").lower()
|
||||
if mode not in ("fast", "exact", "off"):
|
||||
fail(f"[Kernels] NativeKernels must be fast, exact or off, not {mode!r}")
|
||||
if mode != "off":
|
||||
directory = path_value("Paths", "NativeKernelDirFast" if mode == "fast" else "NativeKernelDirExact")
|
||||
if not directory:
|
||||
fail(f"[Kernels] NativeKernels = {mode} needs [Paths] NativeKernelDir{mode.capitalize()}")
|
||||
if not Path(directory).is_dir():
|
||||
fail(f"native kernel directory {directory} does not exist")
|
||||
env["D4R_ZLUDA_NATIVE_DIR"] = directory
|
||||
for key, var in (("Fp8Wmma", "D4R_ZLUDA_WMMA_FP8"), ("IgnoreDenormals", "D4R_ZLUDA_IGNORE_DENORMAL")):
|
||||
value = flag("Kernels", key)
|
||||
if value is not None:
|
||||
env[var] = "1" if value else "0"
|
||||
# RDNA4's native FP8 WMMA (on unless set off; ZLUDA and the bridge ignore it on other GPUs)
|
||||
value = flag("Kernels", "NativeFp8")
|
||||
env["D4R_ZLUDA_WMMA_FP8_NATIVE"] = "0" if value is False else "1"
|
||||
value = flag("Kernels", "NativeSwinEncoders")
|
||||
env["D4R_NATIVE_SWIN_ENCODERS"] = "0" if value is False else "1"
|
||||
value = get("Kernels", "ImplicitMaxBlock")
|
||||
if value:
|
||||
env["D4R_ZLUDA_IMPLICIT_MAX_BLOCK"] = value
|
||||
|
||||
for key, var in (("VramInterop", "D4R_SHIM_VRAM_INTEROP"), ("InputSync", "D4R_SHIM_INPUT_SYNC"),
|
||||
("LinearInputs", "D4R_SHIM_LINEAR_INPUTS"),
|
||||
("DirectOutput", "D4R_SHIM_OUTPUT_DIRECT"), ("ElideNgxSync", "D4R_ELIDE_NGX_SYNC"),
|
||||
("EvalSync", "D4R_SHIM_EVAL_SYNC")):
|
||||
value = flag("Interop", key)
|
||||
if value is not None:
|
||||
env[var] = "1" if value else "0"
|
||||
value = get("Interop", "MarkerPollUs")
|
||||
dilation = get("Interop", "MotionVectorDilation")
|
||||
if dilation is not None and dilation not in ("0", "1", "2"):
|
||||
fail("[Interop] MotionVectorDilation must be 0, 1 or 2")
|
||||
env["D4R_MOTION_DILATION"] = dilation or "0"
|
||||
if value:
|
||||
if not value.isdigit():
|
||||
fail(f"[Interop] MarkerPollUs must be a number of microseconds, not {value!r}")
|
||||
env["D4R_SHIM_MARKER_POLL_US"] = value
|
||||
|
||||
engine = path_value("Game", "EngineIni")
|
||||
cvars = get("Game", "Cvars")
|
||||
if engine and cvars:
|
||||
env["D4R_UE_ENGINE_INI"] = engine
|
||||
env["D4R_UE_CVARS"] = cvars
|
||||
|
||||
value = flag("OptiScaler", "FpsOverlay")
|
||||
if value is False:
|
||||
env["D4R_OPTISCALER_FPS_OVERLAY_TYPE"] = "0"
|
||||
env["D4R_OPTISCALER_SHOW_FPS"] = "false"
|
||||
elif get("OptiScaler", "FpsOverlayType"):
|
||||
env["D4R_OPTISCALER_FPS_OVERLAY_TYPE"] = get("OptiScaler", "FpsOverlayType")
|
||||
if get("OptiScaler", "LogLevel"):
|
||||
env["D4R_OPTISCALER_LOG_LEVEL"] = get("OptiScaler", "LogLevel")
|
||||
extra = []
|
||||
ratios = [(mode_name, get("OptiScaler", f"QualityRatio{mode_name}"))
|
||||
for mode_name in ("Quality", "Balanced", "Performance", "UltraPerformance")]
|
||||
if any(ratio for _, ratio in ratios):
|
||||
extra.append("QualityOverrides.QualityRatioOverrideEnabled=true")
|
||||
extra += [f"QualityOverrides.QualityRatio{name}={ratio}" for name, ratio in ratios if ratio]
|
||||
if get("OptiScaler", "Extra"):
|
||||
extra.append(get("OptiScaler", "Extra"))
|
||||
if extra:
|
||||
env["D4R_EXTRA_OPTISCALER_CONFIG"] = ";".join(extra)
|
||||
|
||||
if flag("Overlay", "MangoHud"):
|
||||
env["MANGOHUD"] = "1"
|
||||
if get("Overlay", "MangoHudConfig"):
|
||||
env["MANGOHUD_CONFIG"] = get("Overlay", "MangoHudConfig")
|
||||
|
||||
if flag("Debug", "Profile"):
|
||||
env["D4R_PROFILE"] = "1"
|
||||
if path_value("Debug", "LogDir"):
|
||||
env["D4R_GAME_LOG_DIR"] = path_value("Debug", "LogDir")
|
||||
|
||||
if ini.has_section("Env"):
|
||||
for key, value in ini.items("Env"):
|
||||
env[key] = value.strip()
|
||||
|
||||
final = {key: value for key, value in env.items() if key not in os.environ}
|
||||
if "--print" in argv:
|
||||
print(f"d4r config: {path}{'' if path.exists() else ' (missing: built-in defaults)'}")
|
||||
for key in sorted(env):
|
||||
note = "" if key in final else f" (environment wins: {os.environ[key]})"
|
||||
print(f" {key}={env[key]}{note}")
|
||||
return
|
||||
for key, value in sorted(final.items()):
|
||||
print(f"export {key}={shlex.quote(value)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
# One-command launch: runs the game configured in d4r.ini's [Launch] section with all d4r settings
|
||||
# from the same file (see config/d4r.ini.default). Extra arguments go to the game.
|
||||
# D4R_CONFIG=/path/to/other.ini scripts/d4r_play.sh
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
mapfile -t LAUNCH < <(python3 "$ROOT/scripts/d4r_config.py" --launch) || exit 2
|
||||
[[ ${#LAUNCH[@]} -eq 3 ]] || exit 2
|
||||
exec "$ROOT/scripts/run_game_d4r_optiscaler_proton.sh" "${LAUNCH[0]}" "${LAUNCH[1]}" "${LAUNCH[2]}" "$@"
|
||||
@@ -0,0 +1,40 @@
|
||||
# Source this to export the environment a Proton process needs to run
|
||||
# official DLSS on AMD through d4r (ZLUDA + Wine CUDA bridge + NGX shim).
|
||||
# Paths can be overridden before sourcing.
|
||||
D4R_RUNTIME_DIR="${D4R_RUNTIME_DIR:-$HOME/.local/share/d4r-dlss}"
|
||||
D4R_ZLUDA_DIR="${D4R_ZLUDA_DIR:-$HOME/.cache/d4r-zluda-current}"
|
||||
# ROCm user-space runtime: a private copy under ~/.cache if present, otherwise the system /opt/rocm
|
||||
if [[ -z "${D4R_ROCM_DIR:-}" ]]; then
|
||||
D4R_ROCM_DIR="$HOME/.cache/d4r-rocm-deps/root/opt/rocm"
|
||||
[[ -d "$D4R_ROCM_DIR" ]] || D4R_ROCM_DIR=/opt/rocm
|
||||
fi
|
||||
d4r_winpath() { printf 'Z:%s' "$(printf '%s' "$1" | tr '/' '\\')"; }
|
||||
|
||||
# dxvk-nvapi on a non-NVIDIA driver, reporting Ada so NGX uploads the FP8
|
||||
# network weights that match the sm_89 PTX ZLUDA compiles.
|
||||
export PROTON_ENABLE_NVAPI=1
|
||||
export DXVK_NVAPI_ALLOW_OTHER_DRIVERS=1
|
||||
export DXVK_NVAPI_GPU_ARCH=AD100
|
||||
# ZLUDA and the ROCm user-cache runtime.
|
||||
export LD_LIBRARY_PATH="$D4R_ZLUDA_DIR:$D4R_ROCM_DIR/lib:$D4R_ROCM_DIR/lib/llvm/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
export D4R_ZLUDA_LIBCUDA="$D4R_ZLUDA_DIR/libcuda.so"
|
||||
# Kernels without PTX launch bounds (DLSS's convolutions) are compiled for at
|
||||
# most 256-thread blocks, as CUDA itself allows 255 registers for them; the
|
||||
# default 1024 bound caps gfx11 waves at 96 VGPRs and makes them spill.
|
||||
export D4R_ZLUDA_IMPLICIT_MAX_BLOCK="${D4R_ZLUDA_IMPLICIT_MAX_BLOCK:-256}"
|
||||
# f16 tensor-core MMAs (DLSS's convolutions) run on gfx11/gfx12 WMMA instead of a
|
||||
# dot-product fallback (patches/zluda/0003 and 0007); 0 selects the fallback.
|
||||
export D4R_ZLUDA_WMMA="${D4R_ZLUDA_WMMA:-1}"
|
||||
# DLSS frames in flight. The presented upscaled image is this many game frames
|
||||
# old at most: 3 = highest frame rate (Townfall ~86 fps, age 2-3), 2 = age 2
|
||||
# (~75 fps), 1 = age 1, the lowest possible with the CPU staging path (~53 fps).
|
||||
export D4R_SHIM_MAX_IN_FLIGHT="${D4R_SHIM_MAX_IN_FLIGHT:-3}"
|
||||
# 1 keeps DLSS inputs and output in VRAM (vkd3d-proton Vulkan interop, no CPU
|
||||
# staging): Townfall ~141 fps at age 2-3 with 3 in flight, ~113 fps at age 1
|
||||
# with D4R_SHIM_MAX_IN_FLIGHT=1. Needs vkd3d-proton; falls back automatically.
|
||||
export D4R_SHIM_VRAM_INTEROP="${D4R_SHIM_VRAM_INTEROP:-0}"
|
||||
# Shim configuration (Windows paths inside the prefix).
|
||||
export D4R_NVCUDA_BRIDGE="$(d4r_winpath "$D4R_RUNTIME_DIR/bin/nvcuda.dll")"
|
||||
export D4R_NGX_CORE="${D4R_NGX_CORE:-$(d4r_winpath "$D4R_RUNTIME_DIR/ngx/_nvngx.dll")}"
|
||||
export D4R_NGX_FEATURE_DIR="$(d4r_winpath "$D4R_RUNTIME_DIR/dlss")"
|
||||
export D4R_SHIM_LOG="${D4R_SHIM_LOG:-$(d4r_winpath "$D4R_RUNTIME_DIR/d4r_nvngx.log")}"
|
||||
@@ -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)"
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Stages the pieces a Proton game needs under D4R_RUNTIME_DIR
|
||||
# (default ~/.local/share/d4r-dlss):
|
||||
# bin/nvcuda.dll Wine CUDA bridge (ELF builtin, preloaded by path by the shim)
|
||||
# bin/d4r_nvngx.dll D3D12-to-CUDA NGX core shim (OptiScaler NvngxPath target)
|
||||
# ngx/_nvngx.dll official NGX core
|
||||
# dlss/nvngx_dlss.dll official DLSS SR feature DLL (also copied into bin/)
|
||||
# usage: install_d4r_runtime.sh PATH_TO_NGX_CORE_DLL PATH_TO_NVNGX_DLSS_DLL
|
||||
if [[ $# -ne 2 ]]; then
|
||||
printf 'usage: %s PATH_TO_NGX_CORE_DLL PATH_TO_NVNGX_DLSS_DLL\n' "$0" >&2
|
||||
exit 2
|
||||
fi
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
RUNTIME="${D4R_RUNTIME_DIR:-$HOME/.local/share/d4r-dlss}"
|
||||
"$ROOT/scripts/build_d4r_nvngx_shim.sh"
|
||||
"$ROOT/scripts/build_wine_nvcuda_bridge.sh"
|
||||
mkdir -p "$RUNTIME/bin" "$RUNTIME/ngx" "$RUNTIME/dlss"
|
||||
cp -f "$ROOT/build/wine-nvcuda/x86_64-unix/nvcuda.dll.so" "$RUNTIME/bin/nvcuda.dll"
|
||||
cp -f "$ROOT/build/d4r_nvngx.dll" "$RUNTIME/bin/d4r_nvngx.dll"
|
||||
copy_unless_same() { [[ "$(realpath "$1")" == "$(realpath -m "$2")" ]] || cp -f "$1" "$2"; }
|
||||
copy_unless_same "$1" "$RUNTIME/ngx/_nvngx.dll"
|
||||
copy_unless_same "$2" "$RUNTIME/dlss/nvngx_dlss.dll"
|
||||
# The NGX core's CUDA init ignores the feature search paths it is given and
|
||||
# scans the directory of the module that calls it, i.e. the shim's.
|
||||
cp -f "$RUNTIME/dlss/nvngx_dlss.dll" "$RUNTIME/bin/nvngx_dlss.dll"
|
||||
sha256sum "$RUNTIME"/ngx/_nvngx.dll "$RUNTIME"/dlss/nvngx_dlss.dll
|
||||
printf 'Installed d4r runtime in %s\n' "$RUNTIME"
|
||||
@@ -0,0 +1,252 @@
|
||||
#!/usr/bin/env bash
|
||||
# Builds the drag-in release: a zip whose contents are extracted into the folder that holds a game's
|
||||
# main .exe, like an OptiScaler release. packaging/D4R_README.txt describes the result.
|
||||
#
|
||||
# usage: scripts/package_release.sh [OUT_DIR] (default: dist/)
|
||||
#
|
||||
# Inputs (environment):
|
||||
# D4R_OPTISCALER OptiScaler release archive (.7z or .zip) or extracted folder (required; 0.9.4 tested)
|
||||
# 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
|
||||
# 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_RUNTIME ROCm runtime bundled as d4r/rocm (scripts/fetch_rocm_runtime.sh)
|
||||
# default ~/.cache/d4r-rocm-runtime
|
||||
# D4R_MAX_GLIBC highest GLIBC ABI the Linux libraries may require default 2.41
|
||||
# D4R_GPU_ARCHS GPU targets to build kernels for default gfx1100 gfx1101 gfx1102 gfx1103 gfx1200 gfx1201
|
||||
# (RDNA4 targets also get a <target>-fp8 folder: the kernels for d4r.ini NativeFp8 = on)
|
||||
# D4R_OPTISCALER_LICENSE OptiScaler's LICENSE (GPL-3.0) text; default: the system's SPDX copy
|
||||
# D4R_VKD3D_SRC vkd3d-proton source checkout, for its license files default ~/.cache/d4r-vkd3d-proton
|
||||
# D4R_ZLUDA_SRC ZLUDA source checkout, for its license files default: D4R_ZLUDA_DIR's ../d4r-zluda-upstream
|
||||
# SOURCE_DATE_EPOCH timestamp given to every packaged file default: the last commit's
|
||||
# D4R_BUILD_INFO optional build-toolchain/provenance text to include in d4r/source/
|
||||
# D4R_PACKAGE_KERNEL_JOBS independent target builds at once (default 1)
|
||||
# The zip also contains NVIDIA's files and the texture kernels built from NVIDIA's PTX; redistributing
|
||||
# those is up to whoever publishes it (they are not covered by d4r's license):
|
||||
# D4R_BUNDLE_DLSS nvngx_dlss.dll to include D4R_BUNDLE_NGX _nvngx.dll to include
|
||||
# D4R_BUNDLE_TEX directory with texture-kernel code objects (kernels/build.sh tex), one subdirectory
|
||||
# per target folder (gfx1101, gfx1201, gfx1201-fp8, ...), or a flat gfx1101 directory for
|
||||
# older builds
|
||||
# Accuracy texture sets go in accuracy/<target>/, built with D4R_PREFER_ACCURACY=1.
|
||||
# D4R_ZLUDA_EMIT d4r_emit from the patched ZLUDA build; required in full builds when an accuracy
|
||||
# texture set or L's unfolded texture variants are not supplied in D4R_BUNDLE_TEX.
|
||||
# D4R_BUNDLE_NVIDIA=0 leaves them out (d4r-VERSION-nonvidia.zip; users then add the two DLLs themselves).
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
OUT="$(realpath -m "${1:-$ROOT/dist}")"
|
||||
VERSION="$(cat "$ROOT/packaging/VERSION")"
|
||||
VARIANT=full
|
||||
[[ "${D4R_BUNDLE_NVIDIA:-1}" == 0 ]] && VARIANT=clean
|
||||
NAME="d4r-$VERSION"
|
||||
[[ "$VARIANT" == clean ]] && NAME="$NAME-nonvidia"
|
||||
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}"
|
||||
: "${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; }
|
||||
done
|
||||
|
||||
# D4R_SKIP_BUILD=1 packages the shim and bridge already in build/ (e.g. the binaries that were tested)
|
||||
if [[ "${D4R_SKIP_BUILD:-0}" != 1 ]]; then
|
||||
"$ROOT/scripts/build_d4r_nvngx_shim.sh" >/dev/null
|
||||
"$ROOT/scripts/build_wine_nvcuda_bridge.sh" >/dev/null
|
||||
fi
|
||||
|
||||
rm -rf "$STAGE"
|
||||
mkdir -p "$STAGE/d4r/zluda" "$STAGE/d4r/rocm" "$STAGE/d4r/ngx" "$STAGE/d4r/kernels" "$STAGE/d4r/licenses" "$STAGE/d4r/source"
|
||||
TMP="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP"' EXIT
|
||||
|
||||
# OptiScaler, as dxgi.dll with its INI preconfigured for d4r
|
||||
if [[ -d "$D4R_OPTISCALER" ]]; then
|
||||
OPTI="$D4R_OPTISCALER"
|
||||
else
|
||||
OPTI="$TMP/optiscaler"
|
||||
mkdir -p "$OPTI"
|
||||
case "$D4R_OPTISCALER" in
|
||||
*.7z) 7z x -y -o"$OPTI" "$D4R_OPTISCALER" >/dev/null ;;
|
||||
*.zip) unzip -q -o "$D4R_OPTISCALER" -d "$OPTI" ;;
|
||||
*) echo "D4R_OPTISCALER must be a .7z, a .zip or a folder" >&2; exit 2 ;;
|
||||
esac
|
||||
fi
|
||||
cp "$OPTI/OptiScaler.dll" "$STAGE/dxgi.dll"
|
||||
python3 - "$OPTI/OptiScaler.ini" "$ROOT/packaging/optiscaler.settings" "$STAGE/OptiScaler.ini" <<'PY'
|
||||
import re, sys
|
||||
lines = open(sys.argv[1], encoding="utf-8", newline="").read().split("\n") # keeps the file's line endings
|
||||
for setting in open(sys.argv[2], encoding="utf-8"):
|
||||
setting = setting.strip()
|
||||
if not setting or setting.startswith("#"):
|
||||
continue
|
||||
path, value = setting.split("=", 1)
|
||||
section, key = path.split(".", 1)
|
||||
start = next((i for i, l in enumerate(lines) if l.strip() == f"[{section}]"), None)
|
||||
if start is None:
|
||||
sys.exit(f"OptiScaler.ini has no [{section}]")
|
||||
end = next((i for i in range(start + 1, len(lines)) if lines[i].startswith("[")), len(lines))
|
||||
for i in range(start + 1, end):
|
||||
if re.match(rf"{re.escape(key)}\s*=", lines[i]):
|
||||
lines[i] = f"{key}={value}" + ("\r" if lines[i].endswith("\r") else "")
|
||||
break
|
||||
else:
|
||||
sys.exit(f"OptiScaler.ini has no {key} in [{section}]")
|
||||
open(sys.argv[3], "w", encoding="utf-8", newline="").write("\n".join(lines))
|
||||
PY
|
||||
|
||||
# d4r-patched vkd3d-proton: same-frame DLSS results
|
||||
cp "$VKD3D/d3d12.dll" "$VKD3D/d3d12core.dll" "$STAGE/"
|
||||
|
||||
# d4r itself
|
||||
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 "$ZLUDA/libnvcuda.so" "$STAGE/d4r/zluda/libcuda.so"
|
||||
cp -r "$ROCM_RUNTIME/lib" "$STAGE/d4r/rocm/lib"
|
||||
"$ROOT/scripts/check_glibc_compat.sh" "${D4R_MAX_GLIBC:-2.41}" \
|
||||
"$STAGE/d4r/nvcuda.dll" "$STAGE/d4r/zluda/libcuda.so" "$STAGE/d4r/rocm/lib/"*
|
||||
cp "$ROOT/packaging/d4r.ini" "$STAGE/d4r/d4r.ini"
|
||||
cp "$ROOT/packaging/d4r-check.sh" "$STAGE/d4r/d4r-check.sh"
|
||||
if [[ "$VARIANT" == full ]]; then
|
||||
: "${D4R_BUNDLE_DLSS:?set D4R_BUNDLE_DLSS}" "${D4R_BUNDLE_NGX:?set D4R_BUNDLE_NGX}" "${D4R_BUNDLE_TEX:?set D4R_BUNDLE_TEX}"
|
||||
cp "$D4R_BUNDLE_DLSS" "$STAGE/d4r/nvngx_dlss.dll"
|
||||
cp "$D4R_BUNDLE_NGX" "$STAGE/d4r/ngx/_nvngx.dll"
|
||||
else
|
||||
printf 'Put NVIDIA'"'"'s NGX runtime, _nvngx.dll, in this folder (see D4R_README.txt).\r\n' > "$STAGE/d4r/ngx/README.txt"
|
||||
fi
|
||||
IFS=: read -r -a DLLS <<< "$D4R_DLSS_DLLS"
|
||||
# one folder per target; RDNA4 targets also get <target>-fp8 (native FP8 WMMA, d4r.ini NativeFp8), which the
|
||||
# bridge serves instead when that setting is on
|
||||
folders=()
|
||||
for arch in $ARCHS; do
|
||||
folders+=("$arch")
|
||||
[[ "$arch" == gfx12* ]] && folders+=("$arch-fp8")
|
||||
done
|
||||
# Older texture bundles predate L. Complete them from the user's DLSS DLL rather
|
||||
# than silently shipping only M's folded variants under the new L support claim.
|
||||
ensure_l_textures() {
|
||||
local dir="$1" accuracy="$2" missing=0 mv range v name
|
||||
local names=(rrlite_dec0_4x4)
|
||||
for mv in mvhi mvlo; do
|
||||
for range in hdr ldr; do
|
||||
names+=("rrlite_enc0_4x4_${mv}_${range}")
|
||||
for v in 3_1 3_2; do names+=("rrlite_post_${v}_${mv}_${range}"); done
|
||||
done
|
||||
done
|
||||
for name in "${names[@]}"; do [[ -f "$dir/$name.hsaco" ]] || missing=1; done
|
||||
if [[ "$missing" == 1 ]]; then
|
||||
: "${D4R_ZLUDA_EMIT:?set D4R_ZLUDA_EMIT or supply the unfolded L texture variants in D4R_BUNDLE_TEX}"
|
||||
D4R_PREFER_ACCURACY="$accuracy" D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" \
|
||||
D4R_DLSS_DLL="$D4R_BUNDLE_DLSS" "$ROOT/kernels/build.sh" l "$dir" >/dev/null
|
||||
fi
|
||||
}
|
||||
build_target() {
|
||||
local folder="$1" arch fp8 tex_dir accurate accurate_tex f
|
||||
arch="${folder%-fp8}"
|
||||
fp8=0
|
||||
[[ "$folder" == *-fp8 ]] && fp8=1
|
||||
D4R_PREFER_ACCURACY=0 D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" "$ROOT/kernels/build.sh" k "$STAGE/d4r/kernels/$folder" >/dev/null
|
||||
D4R_PREFER_ACCURACY=0 D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" "$ROOT/kernels/build.sh" m "$STAGE/d4r/kernels/$folder" >/dev/null
|
||||
rm -f "$STAGE/d4r/kernels/$folder"/*.resolution.txt # empty LTO notes from clang's -save-temps
|
||||
if [[ "$VARIANT" == full ]]; then
|
||||
tex_dir=""
|
||||
if [[ -d "$D4R_BUNDLE_TEX/$folder" ]]; then
|
||||
tex_dir="$D4R_BUNDLE_TEX/$folder"
|
||||
elif [[ "$folder" == gfx1101 ]]; then
|
||||
tex_dir="$D4R_BUNDLE_TEX" # older, flat gfx1101 texture-kernel builds
|
||||
fi
|
||||
if [[ -n "$tex_dir" ]]; then
|
||||
for f in "$tex_dir"/*.hsaco; do # texture kernels only; the layers above are built from source
|
||||
[[ -f "$f" ]] || continue
|
||||
[[ -e "$STAGE/d4r/kernels/$folder/$(basename "$f")" ]] || cp "$f" "$STAGE/d4r/kernels/$folder/"
|
||||
done
|
||||
fi
|
||||
fi
|
||||
[[ "$VARIANT" != full ]] || ensure_l_textures "$STAGE/d4r/kernels/$folder" 0
|
||||
python3 "$ROOT/kernels/tools/kernel_manifest.py" "$STAGE/d4r/kernels/$folder" "${DLLS[@]}"
|
||||
|
||||
# Every release target also gets conservative network and texture variants. Do not copy fast
|
||||
# texture objects into this set: their compiler policy is fixed inside the binary.
|
||||
accurate="$STAGE/d4r/kernels/accuracy/$folder"
|
||||
D4R_PREFER_ACCURACY=1 D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" "$ROOT/kernels/build.sh" k "$accurate" >/dev/null
|
||||
D4R_PREFER_ACCURACY=1 D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" "$ROOT/kernels/build.sh" m "$accurate" >/dev/null
|
||||
if [[ "$VARIANT" == full ]]; then
|
||||
accurate_tex="$D4R_BUNDLE_TEX/accuracy/$folder"
|
||||
if [[ -f "$accurate_tex/d4r-accuracy.txt" && "$(cat "$accurate_tex/d4r-accuracy.txt")" == 1 ]]; then
|
||||
for f in "$accurate_tex"/*.hsaco; do
|
||||
[[ -f "$f" ]] || continue
|
||||
[[ -e "$accurate/$(basename "$f")" ]] || cp "$f" "$accurate/"
|
||||
done
|
||||
else
|
||||
: "${D4R_ZLUDA_EMIT:?set D4R_ZLUDA_EMIT or supply D4R_BUNDLE_TEX/accuracy/$folder}"
|
||||
D4R_PREFER_ACCURACY=1 D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" \
|
||||
D4R_DLSS_DLL="$D4R_BUNDLE_DLSS" "$ROOT/kernels/build.sh" tex "$accurate" >/dev/null
|
||||
fi
|
||||
fi
|
||||
if [[ "$VARIANT" == full ]]; then
|
||||
ensure_l_textures "$accurate" 1
|
||||
for f in "$STAGE/d4r/kernels/$folder"/*.hsaco; do
|
||||
[[ -f "$accurate/$(basename "$f")" ]] || {
|
||||
echo "missing accuracy variant of $(basename "$f") for $folder" >&2; exit 2;
|
||||
}
|
||||
done
|
||||
fi
|
||||
rm -f "$accurate"/*.resolution.txt
|
||||
python3 "$ROOT/kernels/tools/kernel_manifest.py" "$accurate" "${DLLS[@]}"
|
||||
}
|
||||
kernel_jobs="${D4R_PACKAGE_KERNEL_JOBS:-1}"
|
||||
[[ "$kernel_jobs" =~ ^[1-9][0-9]*$ ]] || { echo "D4R_PACKAGE_KERNEL_JOBS must be positive" >&2; exit 2; }
|
||||
pending=()
|
||||
kernel_failed=0
|
||||
for folder in "${folders[@]}"; do
|
||||
build_target "$folder" &
|
||||
pending+=("$!")
|
||||
if (( ${#pending[@]} >= kernel_jobs )); then
|
||||
wait "${pending[0]}" || kernel_failed=1
|
||||
pending=("${pending[@]:1}")
|
||||
fi
|
||||
done
|
||||
for pid in "${pending[@]}"; do wait "$pid" || kernel_failed=1; done
|
||||
[[ "$kernel_failed" == 0 ]] || { echo "native target build failed; no release ZIP created" >&2; exit 1; }
|
||||
|
||||
# licenses and sources
|
||||
ZLUDA_SRC="${D4R_ZLUDA_SRC:-$(dirname "$ZLUDA")/d4r-zluda-upstream}"
|
||||
VKD3D_SRC="${D4R_VKD3D_SRC:-$HOME/.cache/d4r-vkd3d-proton}"
|
||||
L="$STAGE/d4r/licenses"
|
||||
cp "$ROOT/LICENSE" "$L/d4r-LICENSE.txt"
|
||||
cp "$ROOT/NOTICE" "$L/d4r-NOTICE.txt"
|
||||
cp "$ZLUDA_SRC/LICENSE-APACHE" "$L/ZLUDA-LICENSE-APACHE.txt"
|
||||
cp "$ZLUDA_SRC/LICENSE-MIT" "$L/ZLUDA-LICENSE-MIT.txt"
|
||||
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/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"
|
||||
for f in "$ROCM_RUNTIME"/licenses/*; do cp "$f" "$L/ROCm-$(basename "$f")"; done
|
||||
mkdir -p "$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)"
|
||||
VKD3D_COMMIT="$(git -C "$VKD3D_SRC" rev-parse HEAD 2>/dev/null || echo unknown)"
|
||||
# "@clean " / "@full " lines belong to one variant only
|
||||
variant() { sed -n -e "s/^@$VARIANT //" -e '/^@[a-z]* /d' -e p; }
|
||||
file_version() { [[ -f "$1" ]] && strings -el "$1" | grep -A1 '^FileVersion$' | sed -n 2p | tr ',' '.' | tr -d ' '; }
|
||||
sed -e "s/@VERSION@/$VERSION/g" -e "s/@ZLUDA_COMMIT@/$ZLUDA_COMMIT/g" -e "s/@VKD3D_COMMIT@/$VKD3D_COMMIT/g" \
|
||||
-e "s/@DLSS_VERSION@/$(file_version "$STAGE/d4r/nvngx_dlss.dll")/g" -e "s/@NGX_VERSION@/$(file_version "$STAGE/d4r/ngx/_nvngx.dll")/g" \
|
||||
"$ROOT/packaging/SOURCES.txt" | variant > "$STAGE/d4r/source/SOURCES.txt"
|
||||
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"
|
||||
|
||||
# One timestamp for every file (SOURCE_DATE_EPOCH, default the last commit): ZLUDA's kernel cache is keyed on
|
||||
# its library's size and mtime, so every extraction of the zip shares one cache.
|
||||
EPOCH="${SOURCE_DATE_EPOCH:-$(git -C "$ROOT" log -1 --format=%ct)}"
|
||||
find "$STAGE" -exec touch -h -d "@$EPOCH" {} +
|
||||
# The zip holds the folder's contents, so it can be extracted straight into the game folder.
|
||||
rm -f "$OUT/$NAME.zip"
|
||||
(cd "$STAGE" && find . -type f | LC_ALL=C sort | sed 's|^\./||' | zip -q -X -9 "$OUT/$NAME.zip" -@)
|
||||
(cd "$OUT" && sha256sum "$NAME.zip" > "$NAME.zip.sha256")
|
||||
printf 'Built %s (%s)\n' "$OUT/$NAME.zip" "$(du -h "$OUT/$NAME.zip" | cut -f1)"
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# usage: run_bench_dlss_kernel.sh FATBIN REPLAY_DIR [ITERATIONS [OUTPUT_PREFIX]]
|
||||
# Replays a launch captured with D4R_CUDA_REPLAY_DUMP_DIR (wine_nvcuda_bridge.c).
|
||||
if [[ $# -lt 2 ]]; then
|
||||
printf 'usage: %s FATBIN REPLAY_DIR [ITERATIONS [OUTPUT_PREFIX]]\n' "$0" >&2
|
||||
exit 2
|
||||
fi
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ZLUDA_ROOT="${ZLUDA_ROOT:-}"
|
||||
ROCM_ROOT="${ROCM_ROOT:-}"
|
||||
if [[ -z "$ZLUDA_ROOT" || -z "$ROCM_ROOT" || ! -f "$ZLUDA_ROOT/libcuda.so" || ! -f "$ROCM_ROOT/lib/libamdhip64.so" ]]; then
|
||||
printf 'Set ZLUDA_ROOT and ROCM_ROOT to the ZLUDA and HIP runtimes\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
[[ -x "$ROOT/build/bench_dlss_kernel" && "$ROOT/build/bench_dlss_kernel" -nt "$ROOT/tools/bench_dlss_kernel.cpp" ]] ||
|
||||
"$ROOT/scripts/build_bench_dlss_kernel.sh" >/dev/null
|
||||
LD_LIBRARY_PATH="$ZLUDA_ROOT:$ROCM_ROOT/lib:$ROCM_ROOT/lib/llvm/lib:${LD_LIBRARY_PATH:-}" \
|
||||
"$ROOT/build/bench_dlss_kernel" "$@"
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Runs the D3D12 harness against d4r_nvngx.dll in an isolated Wine prefix.
|
||||
# usage: run_d3d12_dlss_harness.sh PATH_TO_NGX_CORE_DLL PATH_TO_NVNGX_DLSS_DLL OUTPUT_RAW [FRAMES [IN_W IN_H OUT_W OUT_H]]
|
||||
if [[ $# -lt 3 ]]; then
|
||||
printf 'usage: %s PATH_TO_NGX_CORE_DLL PATH_TO_NVNGX_DLSS_DLL OUTPUT_RAW [FRAMES]\n' "$0" >&2
|
||||
exit 2
|
||||
fi
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CORE_PATH="$(realpath "$1")"
|
||||
FEATURE_DLL_PATH="$(realpath "$2")"
|
||||
OUTPUT_PATH="$(realpath -m "$3")"
|
||||
FRAMES="${4:-6}"
|
||||
SIZES=("${@:5}")
|
||||
WINE_BIN="${WINE:-wine}"
|
||||
WINEPREFIX="${D4R_WINEPREFIX:-${TMPDIR:-/tmp}/d4r-wineprefix}"
|
||||
APP_DIR="${D4R_HARNESS_APPDIR:-${TMPDIR:-/tmp}/d4r-d3d12-harness-app}"
|
||||
ZLUDA_ROOT="${ZLUDA_ROOT:-}"
|
||||
ROCM_ROOT="${ROCM_ROOT:-}"
|
||||
if [[ ! -f "$ZLUDA_ROOT/libcuda.so" || ! -f "$ROCM_ROOT/lib/libamdhip64.so" ]]; then
|
||||
printf 'Set ZLUDA_ROOT and ROCM_ROOT to the ZLUDA and HIP runtimes\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
mkdir -p "$APP_DIR" "$WINEPREFIX/drive_c/windows/system32"
|
||||
"$ROOT/scripts/build_d3d12_dlss_harness.sh"
|
||||
"$ROOT/scripts/build_wine_nvcuda_bridge.sh"
|
||||
cp -f "$ROOT/build/wine-nvcuda/x86_64-windows/nvcuda.dll" "$WINEPREFIX/drive_c/windows/system32/nvcuda.dll"
|
||||
cp -f "$ROOT/build/d3d12_dlss_harness.exe" "$ROOT/build/d4r_nvngx.dll" "$APP_DIR/"
|
||||
cp -f "$FEATURE_DLL_PATH" "$APP_DIR/nvngx_dlss.dll"
|
||||
winpath() { WINEPREFIX="$WINEPREFIX" winepath -w "$1"; }
|
||||
WINE_OVERRIDES="${WINEDLLOVERRIDES:-}"
|
||||
for override in 'dxgi=n,b' 'd3d12=n,b' 'd3d12core=n,b' 'nvcuda=b'; do
|
||||
dll_name="${override%%=*}"
|
||||
if [[ ";$WINE_OVERRIDES;" != *";$dll_name="* ]]; then
|
||||
WINE_OVERRIDES="${WINE_OVERRIDES:+$WINE_OVERRIDES;}$override"
|
||||
fi
|
||||
done
|
||||
LD_LIBRARY_PATH="$ZLUDA_ROOT:$ROCM_ROOT/lib:$ROCM_ROOT/lib/llvm/lib:${LD_LIBRARY_PATH:-}" \
|
||||
WINEDLLPATH="$ROOT/build/wine-nvcuda/x86_64-windows:$ROOT/build/wine-nvcuda/x86_64-unix${WINEDLLPATH:+:$WINEDLLPATH}" \
|
||||
WINEDLLOVERRIDES="$WINE_OVERRIDES" WINEPREFIX="$WINEPREFIX" WINEDEBUG="${D4R_WINEDEBUG:--all}" \
|
||||
D4R_NGX_CORE="$(winpath "$CORE_PATH")" D4R_NGX_FEATURE_DIR="$(winpath "$APP_DIR")" \
|
||||
D4R_SHIM_LOG="$(winpath "$APP_DIR/d4r_nvngx.log")" \
|
||||
"$WINE_BIN" "$(winpath "$APP_DIR/d3d12_dlss_harness.exe")" "$(winpath "$APP_DIR/d4r_nvngx.dll")" \
|
||||
"$(winpath "$OUTPUT_PATH")" "$FRAMES" "${SIZES[@]}"
|
||||
printf '\nShim log (%s):\n' "$APP_DIR/d4r_nvngx.log"
|
||||
cat "$APP_DIR/d4r_nvngx.log"
|
||||
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
umask 077
|
||||
|
||||
# Runs the synthetic D3D12 harness through a locally supplied OptiScaler
|
||||
# dxgi.dll and the d4r shim. All proxy files and the NVAPI identity bridge are
|
||||
# staged in disposable /tmp paths; the Proton installation is not modified.
|
||||
# usage: run_d3d12_dlss_harness_optiscaler_proton.sh PROTON_DIR OPTISCALER_DLL OUTPUT_RAW [FRAMES [IN_W IN_H OUT_W OUT_H]]
|
||||
if [[ $# -lt 3 ]]; then
|
||||
printf 'usage: %s PROTON_DIR OPTISCALER_DLL OUTPUT_RAW [FRAMES [IN_W IN_H OUT_W OUT_H]]\n' "$0" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PROTON_DIR="$(realpath "$1")"
|
||||
OPTISCALER_DLL="$(realpath "$2")"
|
||||
OUTPUT_PATH="$(realpath -m "$3")"
|
||||
shift 3
|
||||
source "$ROOT/scripts/d4r_proton_env.sh"
|
||||
|
||||
for path in "$D4R_RUNTIME_DIR/bin/d4r_nvngx.dll" "$D4R_RUNTIME_DIR/bin/nvcuda.dll" \
|
||||
"$D4R_RUNTIME_DIR/bin/nvngx_dlss.dll" "$D4R_RUNTIME_DIR/ngx/_nvngx.dll"; do
|
||||
if [[ ! -f "$path" ]]; then
|
||||
printf 'Missing staged d4r runtime file: %s\n' "$path" >&2
|
||||
exit 2
|
||||
fi
|
||||
done
|
||||
|
||||
# DLSS Enabler has been found installed under the name OptiScaler.dll on this
|
||||
# machine. Reject it here: its proxy rejects dxgi/NGX harness naming and can
|
||||
# open a modal error dialog during LoadLibrary.
|
||||
EXPORT_HEADER="$(x86_64-w64-mingw32-objdump -p "$OPTISCALER_DLL" | sed -n '/The Export Tables/,+6p')"
|
||||
if [[ "$EXPORT_HEADER" != *OptiScaler.dll* ]]; then
|
||||
printf 'Expected a real OptiScaler DLL (PE export name OptiScaler.dll): %s\n' "$OPTISCALER_DLL" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
"$ROOT/scripts/build_d4r_nvapi_identity.sh" >/dev/null
|
||||
APP_DIR="${D4R_OPTISCALER_APP_DIR:-${TMPDIR:-/tmp}/d4r-optiscaler-harness-${UID}-$(date +%s%N)}"
|
||||
mkdir -p "$APP_DIR"
|
||||
LAUNCHER_DIR="$(mktemp -d "${TMPDIR:-/tmp}/d4r-optiscaler-proton.XXXXXXXX")"
|
||||
COMPAT_DIR="${D4R_OPTISCALER_COMPAT_DIR:-${TMPDIR:-/tmp}/d4r-optiscaler-compat-$UID}"
|
||||
mkdir -p "$COMPAT_DIR"
|
||||
|
||||
cp -f "$OPTISCALER_DLL" "$APP_DIR/dxgi.dll"
|
||||
INPUT_HOOK="${D4R_OPTISCALER_ENABLE_DLSS_INPUTS:-false}"
|
||||
ORIGINAL_HOOK="${D4R_OPTISCALER_HOOK_ORIGINAL_NVNGX_ONLY:-$INPUT_HOOK}"
|
||||
if [[ "$INPUT_HOOK" != true && "$INPUT_HOOK" != false ]] ||
|
||||
[[ "$ORIGINAL_HOOK" != true && "$ORIGINAL_HOOK" != false ]]; then
|
||||
printf 'OptiScaler hook settings must be true or false\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
# D4R_OPTISCALER_STAGE_CORE=0 keeps the official core in the runtime directory,
|
||||
# as in a game install; the shim then loads it below OptiScaler's hooks.
|
||||
STAGE_CORE="${D4R_OPTISCALER_STAGE_CORE:-1}"
|
||||
if [[ "$INPUT_HOOK" == true && "$STAGE_CORE" == 1 ]]; then
|
||||
if [[ "$ORIGINAL_HOOK" != true || "$APP_DIR" != "${APP_DIR,,}" ]]; then
|
||||
printf 'The DLSS input hook requires HookOriginalNvngxOnly=true and a lowercase app path\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
# OptiScaler hooks game-side nvngx loads. Keep the official core's required
|
||||
# filename inside the harness EXE directory and pass a lowercase z: path;
|
||||
# its hook then leaves this internal core load alone.
|
||||
cp -f "$D4R_RUNTIME_DIR/ngx/_nvngx.dll" "$APP_DIR/_nvngx.dll"
|
||||
cp -f "$D4R_RUNTIME_DIR/dlss/nvngx_dlss.dll" "$APP_DIR/nvngx_dlss.dll"
|
||||
core_windows_path="$(d4r_winpath "$APP_DIR/_nvngx.dll")"
|
||||
export D4R_NGX_CORE="z:${core_windows_path#Z:}"
|
||||
fi
|
||||
cat > "$APP_DIR/OptiScaler.ini" <<EOF
|
||||
[Upscalers]
|
||||
Dx12Upscaler=dlss
|
||||
[FrameGen]
|
||||
Enabled=false
|
||||
FGInput=nofg
|
||||
FGOutput=nofg
|
||||
[DLSS]
|
||||
Enabled=true
|
||||
[Inputs]
|
||||
EnableDlssInputs=$INPUT_HOOK
|
||||
[Hooks]
|
||||
HookOriginalNvngxOnly=$ORIGINAL_HOOK
|
||||
[FSR]
|
||||
FsrAgilitySDKUpgrade=false
|
||||
[Libraries]
|
||||
NvngxPath=$(d4r_winpath "$D4R_RUNTIME_DIR/bin/d4r_nvngx.dll")
|
||||
NvngxDlssPath=$(d4r_winpath "$D4R_RUNTIME_DIR/bin")
|
||||
[NvApi]
|
||||
OverrideNvapiDll=false
|
||||
[Spoofing]
|
||||
Dxgi=false
|
||||
[Plugins]
|
||||
LoadAsiPlugins=false
|
||||
[Log]
|
||||
LogToFile=true
|
||||
LogLevel=1
|
||||
EOF
|
||||
|
||||
# Proton refreshes nvapi64.dll in the prefix on every run. Make a temporary
|
||||
# launcher that copies our identity bridge instead. Its other files are links
|
||||
# to the unmodified Proton installation.
|
||||
cp -f "$PROTON_DIR/proton" "$PROTON_DIR/filelock.py" "$PROTON_DIR/version" "$LAUNCHER_DIR/"
|
||||
ln -s "$PROTON_DIR/files" "$LAUNCHER_DIR/files"
|
||||
ln -s "$PROTON_DIR/protonfixes" "$LAUNCHER_DIR/protonfixes"
|
||||
LAUNCHER="$LAUNCHER_DIR/proton" python3 - <<'PY'
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
path = Path(os.environ["LAUNCHER"])
|
||||
source = path.read_text()
|
||||
old = 'try_copy(g_proton.arch_pe_dir("wine/nvapi", False) + "nvapi64.dll", "drive_c/windows/system32",'
|
||||
new = 'try_copy(os.environ.get("D4R_NVAPI_IDENTITY_DLL", g_proton.arch_pe_dir("wine/nvapi", False) + "nvapi64.dll"), "drive_c/windows/system32",'
|
||||
if source.count(old) != 1:
|
||||
raise SystemExit("Proton NVAPI copy point changed; cannot stage identity bridge")
|
||||
path.write_text(source.replace(old, new))
|
||||
PY
|
||||
chmod +x "$LAUNCHER_DIR/proton"
|
||||
|
||||
export STEAM_COMPAT_CLIENT_INSTALL_PATH="${STEAM_COMPAT_CLIENT_INSTALL_PATH:-$HOME/.local/share/Steam}"
|
||||
export STEAM_COMPAT_DATA_PATH="$COMPAT_DIR"
|
||||
PROTON_ENABLE_NVAPI=1 WINEDEBUG=-all "$PROTON_DIR/proton" run cmd /c exit 0 >/dev/null
|
||||
NVAPI_REAL="$PROTON_DIR/files/lib/wine/nvapi/x86_64-windows/nvapi64.dll"
|
||||
if [[ ! -f "$NVAPI_REAL" ]]; then
|
||||
printf 'Cannot find Proton DXVK-NVAPI at %s\n' "$NVAPI_REAL" >&2
|
||||
exit 2
|
||||
fi
|
||||
cp -f "$NVAPI_REAL" "$COMPAT_DIR/pfx/drive_c/windows/system32/d4r_nvapi64_real.dll"
|
||||
|
||||
export D4R_NVAPI_IDENTITY_DLL="$ROOT/build/nvapi64.dll"
|
||||
export D4R_HARNESS_APP_DIR="$APP_DIR"
|
||||
export D4R_HARNESS_NGX_DLL="$APP_DIR/dxgi.dll"
|
||||
export D4R_HARNESS_LOAD_NGX_EARLY=1
|
||||
export D4R_HARNESS_TRACE="$(d4r_winpath "$APP_DIR/harness.trace")"
|
||||
export D4R_SHIM_LOG="$(d4r_winpath "$APP_DIR/d4r_nvngx.log")"
|
||||
export D4R_PROTON_COMPAT_DATA="$COMPAT_DIR"
|
||||
export DXVK_CONFIG="${DXVK_CONFIG:-dxgi.customVendorId = 10de}"
|
||||
|
||||
"$ROOT/scripts/run_d3d12_dlss_harness_proton.sh" "$LAUNCHER_DIR" "$OUTPUT_PATH" "$@"
|
||||
|
||||
if [[ ! -s "$OUTPUT_PATH" || ! -f "$APP_DIR/OptiScaler.log" || ! -f "$APP_DIR/d4r_nvngx.log" ]] ||
|
||||
! rg -q 'DLSSFeatureDx12::Init _CreateFeature result: NVSDK_NGX_Result_Success' "$APP_DIR/OptiScaler.log" ||
|
||||
! rg -q 'NVSDK_NGX_CUDA_CreateFeature -> 0x00000001' "$APP_DIR/d4r_nvngx.log" ||
|
||||
! rg -q 'frame [0-9]+ done:' "$APP_DIR/d4r_nvngx.log"; then
|
||||
printf 'OptiScaler did not complete the official DLSS path; inspect %s\n' "$APP_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf 'OptiScaler DLSS harness output: %s\n' "$OUTPUT_PATH"
|
||||
printf 'OptiScaler, shim, and stage logs: %s\n' "$APP_DIR"
|
||||
sha256sum "$OUTPUT_PATH"
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Runs the D3D12 harness through a Proton build's own launcher script, so the
|
||||
# prefix, DXVK/vkd3d-proton/dxvk-nvapi and DLL overrides match a Steam game.
|
||||
# usage: run_d3d12_dlss_harness_proton.sh PROTON_DIR OUTPUT_RAW [FRAMES [IN_W IN_H OUT_W OUT_H]]
|
||||
if [[ $# -lt 2 ]]; then
|
||||
printf 'usage: %s PROTON_DIR OUTPUT_RAW [FRAMES [IN_W IN_H OUT_W OUT_H]]\n' "$0" >&2
|
||||
exit 2
|
||||
fi
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PROTON_DIR="$(realpath "$1")"
|
||||
OUTPUT_PATH="$(realpath -m "$2")"
|
||||
shift 2
|
||||
source "$ROOT/scripts/d4r_proton_env.sh"
|
||||
if [[ "${D4R_HARNESS_SKIP_BUILD:-0}" != 1 ]]; then
|
||||
"$ROOT/scripts/build_d3d12_dlss_harness.sh" >/dev/null
|
||||
fi
|
||||
HARNESS_APP_DIR="${D4R_HARNESS_APP_DIR:-$ROOT/build}"
|
||||
mkdir -p "$HARNESS_APP_DIR"
|
||||
HARNESS_EXE="$HARNESS_APP_DIR/d3d12_dlss_harness.exe"
|
||||
if [[ "$(realpath -m "$HARNESS_EXE")" != "$(realpath -m "$ROOT/build/d3d12_dlss_harness.exe")" ]]; then
|
||||
cp -f "$ROOT/build/d3d12_dlss_harness.exe" "$HARNESS_EXE"
|
||||
fi
|
||||
export STEAM_COMPAT_CLIENT_INSTALL_PATH="${STEAM_COMPAT_CLIENT_INSTALL_PATH:-$HOME/.local/share/Steam}"
|
||||
export STEAM_COMPAT_DATA_PATH="${D4R_PROTON_COMPAT_DATA:-${TMPDIR:-/tmp}/d4r-proton-harness-compat}"
|
||||
mkdir -p "$STEAM_COMPAT_DATA_PATH"
|
||||
# D4R_HARNESS_NGX_DLL selects the NGX DLL the harness drives (default: the
|
||||
# shim; point it at OptiScaler installed as nvngx.dll to test OptiScaler).
|
||||
NGX_DLL="${D4R_HARNESS_NGX_DLL:-$D4R_RUNTIME_DIR/bin/d4r_nvngx.dll}"
|
||||
"$PROTON_DIR/proton" run "$HARNESS_EXE" "$(d4r_winpath "$NGX_DLL")" \
|
||||
"$(d4r_winpath "$OUTPUT_PATH")" "$@"
|
||||
printf '\nShim log:\n'
|
||||
if [[ -f "$D4R_RUNTIME_DIR/d4r_nvngx.log" ]]; then
|
||||
tail -n 20 "$D4R_RUNTIME_DIR/d4r_nvngx.log"
|
||||
fi
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Zero-copy D3D12 <-> HIP sharing probe under Proton (see the .cpp).
|
||||
# usage: run_d3d12_hip_interop_probe.sh PROTON_DIR REPORT_FILE [WIDTH HEIGHT]
|
||||
if [[ $# -lt 2 ]]; then
|
||||
printf 'usage: %s PROTON_DIR REPORT_FILE [WIDTH HEIGHT]\n' "$0" >&2
|
||||
exit 2
|
||||
fi
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PROTON_DIR="$(realpath "$1")"
|
||||
REPORT="$(realpath -m "$2")"
|
||||
shift 2
|
||||
source "$ROOT/scripts/d4r_proton_env.sh"
|
||||
"$ROOT/scripts/build_d3d12_hip_interop_probe.sh" >/dev/null
|
||||
export STEAM_COMPAT_CLIENT_INSTALL_PATH="${STEAM_COMPAT_CLIENT_INSTALL_PATH:-$HOME/.local/share/Steam}"
|
||||
export STEAM_COMPAT_DATA_PATH="${D4R_PROTON_COMPAT_DATA:-${TMPDIR:-/tmp}/d4r-proton-harness-compat}"
|
||||
mkdir -p "$STEAM_COMPAT_DATA_PATH"
|
||||
"$PROTON_DIR/proton" run "$ROOT/build/d3d12_hip_interop_probe.exe" \
|
||||
"$(d4r_winpath "$D4R_RUNTIME_DIR/bin/nvcuda.dll")" "$(d4r_winpath "$REPORT")" "$@" || status=$?
|
||||
cat "$REPORT"
|
||||
exit "${status:-0}"
|
||||
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Launches a Windows game through a GE-Proton build's integrated OptiScaler
|
||||
# (PROTON_USE_OPTISCALER) with OptiScaler's DLSS backend routed into the d4r
|
||||
# shim, i.e. official NVIDIA DLSS executed through the Wine CUDA bridge and
|
||||
# ZLUDA on the AMD GPU.
|
||||
#
|
||||
# usage: run_game_d4r_optiscaler_proton.sh PROTON_DIR COMPAT_DATA_DIR GAME_EXE [ARGS...]
|
||||
#
|
||||
# The Proton installation and game directory are not modified. The prefix's
|
||||
# system32/umu/OptiScaler.ini (rewritten by GE-Proton from
|
||||
# PROTON_OPTISCALER_CONFIG) is restored when the game exits, and the real
|
||||
# DXVK-NVAPI is placed next to the identity bridge as d4r_nvapi64_real.dll.
|
||||
# With D4R_VKD3D_DIR (a directory holding d3d12.dll and d3d12core.dll, e.g. the
|
||||
# d4r-patched vkd3d-proton), those replace Proton's vkd3d-proton in the prefix
|
||||
# for this launch; the prefix's previous copies are backed up to the log
|
||||
# directory and restored when the game exits.
|
||||
# With D4R_UE_ENGINE_INI (the game's Saved/Config/Windows/Engine.ini) and
|
||||
# D4R_UE_CVARS ("r.A=0;r.B=1"), those console variables are added to its
|
||||
# [SystemSettings] for this launch only: an existing file is backed up to the
|
||||
# log directory and restored on exit, a new one is removed again. Unreal games
|
||||
# apply tonemapper sharpening and chromatic fringe after the upscaler; e.g.
|
||||
# Townfall's r.Tonemapper.Sharpen and r.SceneColorFringeQuality turn DLSS's
|
||||
# clean thin lines into haloed, stippled and beaded ones.
|
||||
# Logs go to D4R_GAME_LOG_DIR (default: a timestamped capture directory).
|
||||
if [[ $# -lt 3 ]]; then
|
||||
printf 'usage: %s PROTON_DIR COMPAT_DATA_DIR GAME_EXE [ARGS...]\n' "$0" >&2
|
||||
exit 2
|
||||
fi
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PROTON_DIR="$(realpath "$1")"
|
||||
COMPAT_DIR="$(realpath "$2")"
|
||||
GAME_EXE="$(realpath "$3")"
|
||||
shift 3
|
||||
# User settings from d4r.ini (config/d4r.ini.default documents them); explicit environment wins.
|
||||
# D4R_NO_CONFIG=1 ignores the file (automated tests); D4R_CONFIG picks another file.
|
||||
if [[ "${D4R_NO_CONFIG:-0}" != 1 ]]; then
|
||||
python3 "$ROOT/scripts/d4r_config.py" --print >&2 || true
|
||||
D4R_CONFIG_EXPORTS="$(python3 "$ROOT/scripts/d4r_config.py")" || exit 2
|
||||
eval "$D4R_CONFIG_EXPORTS"
|
||||
fi
|
||||
source "$ROOT/scripts/d4r_proton_env.sh"
|
||||
|
||||
PREFIX="$COMPAT_DIR/pfx"
|
||||
UMU="$PREFIX/drive_c/windows/system32/umu"
|
||||
LOG_DIR="${D4R_GAME_LOG_DIR:-$HOME/.cache/d4r-dlss-captures/game-$(basename "$GAME_EXE" .exe)-$(date +%Y%m%d-%H%M%S)}"
|
||||
mkdir -p "$LOG_DIR"
|
||||
[[ -d "$PREFIX" ]] || { printf 'No Proton prefix at %s\n' "$PREFIX" >&2; exit 2; }
|
||||
# A second launch would join the running wineserver, and whichever run exits
|
||||
# first would restore its OptiScaler.ini backup underneath the other.
|
||||
SERVER_DIR="/tmp/.wine-$(id -u)/server-$(stat -c '%D' "$PREFIX" | sed 's/^0*//')-$(printf '%x' "$(stat -c '%i' "$PREFIX")")"
|
||||
if [[ -S "$SERVER_DIR/socket" ]] && fuser "$SERVER_DIR/socket" >/dev/null 2>&1; then
|
||||
printf 'A wineserver is already running for %s; close that game first\n' "$PREFIX" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
"$ROOT/scripts/build_d4r_nvapi_identity.sh" >/dev/null
|
||||
NVAPI_REAL="$PROTON_DIR/files/lib/wine/nvapi/x86_64-windows/nvapi64.dll"
|
||||
[[ -f "$NVAPI_REAL" ]] || { printf 'Cannot find DXVK-NVAPI at %s\n' "$NVAPI_REAL" >&2; exit 2; }
|
||||
cp -f "$NVAPI_REAL" "$PREFIX/drive_c/windows/system32/d4r_nvapi64_real.dll"
|
||||
|
||||
# Temporary launcher: identical to the Proton build except that the NVAPI copy
|
||||
# installs the identity bridge (so OptiScaler enables its DLSS backend).
|
||||
LAUNCHER_DIR="$(mktemp -d "${TMPDIR:-/tmp}/d4r-game-proton.XXXXXXXX")"
|
||||
for entry in "$PROTON_DIR"/*; do
|
||||
[[ "$(basename "$entry")" == proton ]] || ln -s "$entry" "$LAUNCHER_DIR/$(basename "$entry")"
|
||||
done
|
||||
cp -f "$PROTON_DIR/proton" "$LAUNCHER_DIR/proton"
|
||||
LAUNCHER="$LAUNCHER_DIR/proton" python3 - <<'PY'
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
path = Path(os.environ["LAUNCHER"])
|
||||
source = path.read_text()
|
||||
variants = [
|
||||
'g_proton.arch_pe_dir("wine/nvapi", False) + "nvapi64.dll"',
|
||||
'g_proton.arch_pe_dir("wine/" + nvapi_path, False) + "nvapi64.dll"',
|
||||
]
|
||||
for old in variants:
|
||||
target = f'try_copy({old}, "drive_c/windows/system32",'
|
||||
if source.count(target) == 1:
|
||||
new = f'try_copy(os.environ.get("D4R_NVAPI_IDENTITY_DLL", {old}), "drive_c/windows/system32",'
|
||||
path.write_text(source.replace(target, new))
|
||||
break
|
||||
else:
|
||||
raise SystemExit("Proton NVAPI copy point not found; cannot stage identity bridge")
|
||||
if os.environ.get("D4R_VKD3D_DIR"):
|
||||
source = path.read_text()
|
||||
old = 'try_copy(g_proton.arch_pe_dir("wine/vkd3d-proton", False) + f + ".dll", "drive_c/windows/system32",'
|
||||
new = ('try_copy(os.path.join(os.environ["D4R_VKD3D_DIR"], f + ".dll"), "drive_c/windows/system32",')
|
||||
if source.count(old) != 1:
|
||||
raise SystemExit("Proton vkd3d-proton copy point not found; cannot stage D4R_VKD3D_DIR")
|
||||
path.write_text(source.replace(old, new))
|
||||
PY
|
||||
|
||||
# GE-Proton rewrites OptiScaler.ini from PROTON_OPTISCALER_CONFIG; keep the
|
||||
# user's configuration and put it back afterwards.
|
||||
INI="$UMU/OptiScaler.ini"
|
||||
[[ -f "$INI" ]] && cp -f "$INI" "$LOG_DIR/OptiScaler.ini.before"
|
||||
VKD3D_BACKUP="$LOG_DIR/vkd3d-prefix-backup"
|
||||
if [[ -n "${D4R_VKD3D_DIR:-}" ]]; then
|
||||
export D4R_VKD3D_DIR="$(realpath "$D4R_VKD3D_DIR")"
|
||||
for dll in d3d12.dll d3d12core.dll; do
|
||||
[[ -f "$D4R_VKD3D_DIR/$dll" ]] || { printf 'D4R_VKD3D_DIR lacks %s\n' "$dll" >&2; exit 2; }
|
||||
done
|
||||
mkdir -p "$VKD3D_BACKUP"
|
||||
for dll in d3d12.dll d3d12core.dll; do
|
||||
[[ -f "$PREFIX/drive_c/windows/system32/$dll" ]] && cp -f "$PREFIX/drive_c/windows/system32/$dll" "$VKD3D_BACKUP/$dll"
|
||||
done
|
||||
fi
|
||||
ENGINE_INI="${D4R_UE_ENGINE_INI:-}"
|
||||
ENGINE_INI_STATE=""
|
||||
restore_prefix() {
|
||||
[[ -f "$LOG_DIR/OptiScaler.ini.before" ]] && cp -f "$LOG_DIR/OptiScaler.ini.before" "$INI"
|
||||
case "$ENGINE_INI_STATE" in
|
||||
restore) cp -f "$LOG_DIR/Engine.ini.before" "$ENGINE_INI" ;;
|
||||
remove) rm -f "$ENGINE_INI" ;;
|
||||
esac
|
||||
if [[ -n "${D4R_VKD3D_DIR:-}" ]]; then
|
||||
for dll in d3d12.dll d3d12core.dll; do
|
||||
[[ -f "$VKD3D_BACKUP/$dll" ]] && cp -f "$VKD3D_BACKUP/$dll" "$PREFIX/drive_c/windows/system32/$dll"
|
||||
done
|
||||
fi
|
||||
rm -rf "$LAUNCHER_DIR"
|
||||
}
|
||||
trap restore_prefix EXIT
|
||||
if [[ -n "$ENGINE_INI" && -n "${D4R_UE_CVARS:-}" ]]; then
|
||||
if [[ -f "$ENGINE_INI" ]]; then
|
||||
cp -f "$ENGINE_INI" "$LOG_DIR/Engine.ini.before"
|
||||
ENGINE_INI_STATE=restore
|
||||
else
|
||||
mkdir -p "$(dirname "$ENGINE_INI")"
|
||||
ENGINE_INI_STATE=remove
|
||||
fi
|
||||
{ printf '\n[SystemSettings]\n'; tr ';' '\n' <<<"$D4R_UE_CVARS"; printf '\n'; } >> "$ENGINE_INI"
|
||||
fi
|
||||
|
||||
OPTI_CONFIG="Upscalers.Dx12Upscaler=dlss;DLSS.Enabled=true"
|
||||
OPTI_CONFIG+=";Libraries.NvngxPath=$(d4r_winpath "$D4R_RUNTIME_DIR/bin/d4r_nvngx.dll")"
|
||||
OPTI_CONFIG+=";Libraries.NvngxDlssPath=$(d4r_winpath "$D4R_RUNTIME_DIR/bin")"
|
||||
# Game-side DLSS inputs stay hooked; only NGX loads outside the game directory
|
||||
# are redirected, which lets the shim request the official core by an
|
||||
# exe-relative path.
|
||||
OPTI_CONFIG+=";Inputs.EnableDlssInputs=true;Hooks.HookOriginalNvngxOnly=true"
|
||||
OPTI_CONFIG+=";NvApi.OverrideNvapiDll=false;FrameGen.Enabled=false;FrameGen.FGInput=nofg;FrameGen.FGOutput=nofg"
|
||||
OPTI_CONFIG+=";Log.LogToFile=true;Log.LogLevel=${D4R_OPTISCALER_LOG_LEVEL:-2}"
|
||||
# Detailed FPS overlay with frame-time graph (the user's own INI default too).
|
||||
OPTI_CONFIG+=";Menu.ShowFps=${D4R_OPTISCALER_SHOW_FPS:-true};Menu.FpsOverlayType=${D4R_OPTISCALER_FPS_OVERLAY_TYPE:-3}"
|
||||
OPTI_CONFIG+="${D4R_EXTRA_OPTISCALER_CONFIG:+;$D4R_EXTRA_OPTISCALER_CONFIG}"
|
||||
|
||||
export PROTON_USE_OPTISCALER=1
|
||||
export PROTON_OPTISCALER_CONFIG="$OPTI_CONFIG"
|
||||
export D4R_NVAPI_IDENTITY_DLL="$ROOT/build/nvapi64.dll"
|
||||
export DXVK_CONFIG="${DXVK_CONFIG:-dxgi.customVendorId = 10de}"
|
||||
export D4R_SHIM_LOG="$(d4r_winpath "$LOG_DIR/d4r_nvngx.log")"
|
||||
export STEAM_COMPAT_CLIENT_INSTALL_PATH="${STEAM_COMPAT_CLIENT_INSTALL_PATH:-$HOME/.local/share/Steam}"
|
||||
export STEAM_COMPAT_DATA_PATH="$COMPAT_DIR"
|
||||
export PROTON_LOG=1 PROTON_LOG_DIR="$LOG_DIR"
|
||||
export WINEDEBUG="${WINEDEBUG:--all}"
|
||||
# Games load dozens of modules per launch; do not capture them unless asked.
|
||||
export D4R_CUDA_CAPTURE="${D4R_CUDA_CAPTURE:-0}"
|
||||
|
||||
printf 'd4r game launch: %s\nlogs: %s\n' "$GAME_EXE" "$LOG_DIR"
|
||||
status=0
|
||||
(cd "$(dirname "$GAME_EXE")" && "$LAUNCHER_DIR/proton" waitforexitandrun "$GAME_EXE" "$@") \
|
||||
>"$LOG_DIR/launcher.log" 2>&1 || status=$?
|
||||
cp -f "$UMU/OptiScaler.log" "$LOG_DIR/OptiScaler.log" 2>/dev/null || true
|
||||
printf 'game exited with status %s\n' "$status"
|
||||
exit "$status"
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ROCM_ROOT="${ROCM_ROOT:-/opt/rocm}"
|
||||
|
||||
"$ROOT/scripts/build_hip_vk_interop_probe.sh"
|
||||
LD_LIBRARY_PATH="$ROCM_ROOT/lib:$ROCM_ROOT/lib/llvm/lib:${LD_LIBRARY_PATH:-}" \
|
||||
"$ROOT/build/hip_vk_interop_probe"
|
||||
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -ne 3 ]]; then
|
||||
printf 'usage: %s PATH_TO_NGX_CORE_DLL PATH_TO_NVNGX_DLSS_DLL WRITABLE_APPDATA_DIRECTORY\n' "$0" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CORE_PATH="$(realpath "$1")"
|
||||
FEATURE_DLL_PATH="$(realpath "$2")"
|
||||
DATA_PATH="$(realpath -m "$3")"
|
||||
WINE_BIN="${WINE:-wine}"
|
||||
WINEPREFIX="${D4R_WINEPREFIX:-${TMPDIR:-/tmp}/d4r-wineprefix}"
|
||||
APP_DIR="${D4R_NGX_CUDA_APPDIR:-${TMPDIR:-/tmp}/d4r-ngxcuda-app}"
|
||||
CAPTURE_DIR="${D4R_CUDA_CAPTURE_DIR:-${TMPDIR:-/tmp}/d4r-dlss-cuda-modules}"
|
||||
ZLUDA_ROOT="${ZLUDA_ROOT:-}"
|
||||
ROCM_ROOT="${ROCM_ROOT:-}"
|
||||
|
||||
TMP_ROOT="$(realpath -m "${TMPDIR:-/tmp}")"
|
||||
WINEPREFIX="$(realpath -m "$WINEPREFIX")"
|
||||
APP_DIR="$(realpath -m "$APP_DIR")"
|
||||
DATA_PATH="$(realpath -m "$DATA_PATH")"
|
||||
CAPTURE_DIR="$(realpath -m "$CAPTURE_DIR")"
|
||||
case "$WINEPREFIX/" in
|
||||
"$TMP_ROOT/"*) ;;
|
||||
*)
|
||||
printf 'D4R_WINEPREFIX must be an isolated prefix under %s; refusing to modify a non-temporary prefix\n' "$TMP_ROOT" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
for isolated_path in "$APP_DIR" "$DATA_PATH" "$CAPTURE_DIR"; do
|
||||
case "$isolated_path/" in
|
||||
"$TMP_ROOT/"*) ;;
|
||||
*)
|
||||
printf 'App directory, appdata, and CUDA captures must stay under %s; refusing to write outside temporary storage\n' "$TMP_ROOT" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ! -f "$ZLUDA_ROOT/libcuda.so" || ! -f "$ROCM_ROOT/lib/libamdhip64.so" ]]; then
|
||||
printf 'Set ZLUDA_ROOT and ROCM_ROOT to the temporary or installed ZLUDA/HIP runtimes\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
mkdir -p "$WINEPREFIX/drive_c/windows/system32" "$DATA_PATH" "$APP_DIR" "$CAPTURE_DIR"
|
||||
"$ROOT/scripts/build_ngx_cuda_probe.sh"
|
||||
"$ROOT/scripts/build_wine_nvcuda_bridge.sh"
|
||||
cp -f "$ROOT/build/wine-nvcuda/x86_64-windows/nvcuda.dll" "$WINEPREFIX/drive_c/windows/system32/nvcuda.dll"
|
||||
APP_EXE_PATH="$APP_DIR/ngx_cuda_probe.exe"
|
||||
APP_FEATURE_DLL_PATH="$APP_DIR/nvngx_dlss.dll"
|
||||
cp -f "$ROOT/build/ngx_cuda_probe.exe" "$APP_EXE_PATH"
|
||||
cp -f "$FEATURE_DLL_PATH" "$APP_FEATURE_DLL_PATH"
|
||||
CORE_WIN_PATH="$(WINEPREFIX="$WINEPREFIX" winepath -w "$CORE_PATH")"
|
||||
FEATURE_DLL_WIN_PATH="$(WINEPREFIX="$WINEPREFIX" winepath -w "$APP_FEATURE_DLL_PATH")"
|
||||
DATA_WIN_PATH="$(WINEPREFIX="$WINEPREFIX" winepath -w "$DATA_PATH")"
|
||||
APP_EXE_WIN_PATH="$(WINEPREFIX="$WINEPREFIX" winepath -w "$APP_EXE_PATH")"
|
||||
WINE_OVERRIDES="${WINEDLLOVERRIDES:-}"
|
||||
for override in 'dxgi=n,b' 'd3d12=n,b' 'd3d12core=n,b' 'nvcuda=b'; do
|
||||
dll_name="${override%%=*}"
|
||||
if [[ ";$WINE_OVERRIDES;" != *";$dll_name="* ]]; then
|
||||
WINE_OVERRIDES="${WINE_OVERRIDES:+$WINE_OVERRIDES;}$override"
|
||||
fi
|
||||
done
|
||||
LD_LIBRARY_PATH="$ZLUDA_ROOT:$ROCM_ROOT/lib:$ROCM_ROOT/lib/llvm/lib:${LD_LIBRARY_PATH:-}" \
|
||||
WINEDLLPATH="$ROOT/build/wine-nvcuda/x86_64-windows:$ROOT/build/wine-nvcuda/x86_64-unix${WINEDLLPATH:+:$WINEDLLPATH}" \
|
||||
WINEDLLOVERRIDES="$WINE_OVERRIDES" WINEPREFIX="$WINEPREFIX" \
|
||||
WINEDEBUG="${D4R_WINEDEBUG:--all}" \
|
||||
D4R_CUDA_CAPTURE_DIR="$CAPTURE_DIR" \
|
||||
"$WINE_BIN" "$APP_EXE_WIN_PATH" "$CORE_WIN_PATH" "$FEATURE_DLL_WIN_PATH" "$DATA_WIN_PATH"
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -ne 3 ]]; then
|
||||
printf 'usage: %s PATH_TO_NGX_CORE_DLL PATH_TO_NVNGX_DLSS_DLL WRITABLE_APPDATA_DIRECTORY\n' "$0" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CORE_PATH="$(realpath "$1")"
|
||||
FEATURE_DLL_PATH="$(realpath "$2")"
|
||||
DATA_PATH="$(realpath -m "$3")"
|
||||
WINE_BIN="${WINE:-wine}"
|
||||
WINEPREFIX="${D4R_WINEPREFIX:-${TMPDIR:-/tmp}/d4r-wineprefix}"
|
||||
|
||||
mkdir -p "$WINEPREFIX" "$DATA_PATH"
|
||||
"$ROOT/scripts/build_ngx_d3d12_probe.sh"
|
||||
CORE_WIN_PATH="$(WINEPREFIX="$WINEPREFIX" winepath -w "$CORE_PATH")"
|
||||
FEATURE_DLL_WIN_PATH="$(WINEPREFIX="$WINEPREFIX" winepath -w "$FEATURE_DLL_PATH")"
|
||||
DATA_WIN_PATH="$(WINEPREFIX="$WINEPREFIX" winepath -w "$DATA_PATH")"
|
||||
WINEPREFIX="$WINEPREFIX" WINEDEBUG="${D4R_WINEDEBUG:--all}" \
|
||||
"$WINE_BIN" "$ROOT/build/ngx_d3d12_probe.exe" "$CORE_WIN_PATH" "$FEATURE_DLL_WIN_PATH" "$DATA_WIN_PATH"
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -ne 1 ]]; then
|
||||
printf 'usage: %s PATH_TO_NVNGX_DLSS_DLL\n' "$0" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
DLL_PATH="$(realpath "$1")"
|
||||
WINE_BIN="${WINE:-wine}"
|
||||
WINEPREFIX="${D4R_WINEPREFIX:-${TMPDIR:-/tmp}/d4r-wineprefix}"
|
||||
|
||||
"$ROOT/scripts/build_ngx_load_probe.sh"
|
||||
mkdir -p "$WINEPREFIX"
|
||||
DLL_WIN_PATH="$(WINEPREFIX="$WINEPREFIX" winepath -w "$DLL_PATH")"
|
||||
WINEPREFIX="$WINEPREFIX" WINEDEBUG="${D4R_WINEDEBUG:--all}" \
|
||||
"$WINE_BIN" "$ROOT/build/ngx_load_probe.exe" "$DLL_WIN_PATH"
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -ne 3 ]]; then
|
||||
printf 'usage: %s PATH_TO_NGX_CORE_DLL PATH_TO_NVNGX_DLSS_DLL WRITABLE_APPDATA_DIRECTORY\n' "$0" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CORE_PATH="$(realpath "$1")"
|
||||
FEATURE_DLL_PATH="$(realpath "$2")"
|
||||
DATA_PATH="$(realpath -m "$3")"
|
||||
WINE_BIN="${WINE:-wine}"
|
||||
WINEPREFIX="${D4R_WINEPREFIX:-${TMPDIR:-/tmp}/d4r-wineprefix}"
|
||||
|
||||
mkdir -p "$WINEPREFIX" "$DATA_PATH"
|
||||
"$ROOT/scripts/build_ngx_vulkan_probe.sh"
|
||||
CORE_WIN_PATH="$(WINEPREFIX="$WINEPREFIX" winepath -w "$CORE_PATH")"
|
||||
FEATURE_DLL_WIN_PATH="$(WINEPREFIX="$WINEPREFIX" winepath -w "$FEATURE_DLL_PATH")"
|
||||
DATA_WIN_PATH="$(WINEPREFIX="$WINEPREFIX" winepath -w "$DATA_PATH")"
|
||||
WINEPREFIX="$WINEPREFIX" WINEDEBUG="${D4R_WINEDEBUG:--all}" \
|
||||
"$WINE_BIN" "$ROOT/build/ngx_vulkan_probe.exe" "$CORE_WIN_PATH" "$FEATURE_DLL_WIN_PATH" "$DATA_WIN_PATH"
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ZLUDA_ROOT="${ZLUDA_ROOT:-}"
|
||||
ROCM_ROOT="${ROCM_ROOT:-}"
|
||||
if [[ -z "$ZLUDA_ROOT" || -z "$ROCM_ROOT" || ! -f "$ZLUDA_ROOT/libcuda.so" || ! -f "$ROCM_ROOT/lib/libamdhip64.so" ]]; then
|
||||
printf 'Set ZLUDA_ROOT and ROCM_ROOT to the ZLUDA and HIP runtimes\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
"$ROOT/scripts/build_replay_dlss_fp8_ops.sh"
|
||||
LD_LIBRARY_PATH="$ZLUDA_ROOT:$ROCM_ROOT/lib:$ROCM_ROOT/lib/llvm/lib:${LD_LIBRARY_PATH:-}" \
|
||||
"$ROOT/build/replay_dlss_fp8_ops"
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -ne 1 ]]; then
|
||||
printf 'usage: %s PATH_TO_LOCALLY_CAPTURED_DLSS_HISTOGRAM_FATBIN\n' "$0" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ZLUDA_ROOT="${ZLUDA_ROOT:-}"
|
||||
ROCM_ROOT="${ROCM_ROOT:-}"
|
||||
if [[ -z "$ZLUDA_ROOT" || -z "$ROCM_ROOT" || ! -f "$ZLUDA_ROOT/libcuda.so" || ! -f "$ROCM_ROOT/lib/libamdhip64.so" ]]; then
|
||||
printf 'Set ZLUDA_ROOT and ROCM_ROOT to the ZLUDA and HIP runtimes\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
"$ROOT/scripts/build_replay_dlss_histogram.sh"
|
||||
LD_LIBRARY_PATH="$ZLUDA_ROOT:$ROCM_ROOT/lib:$ROCM_ROOT/lib/llvm/lib:${LD_LIBRARY_PATH:-}" \
|
||||
"$ROOT/build/replay_dlss_histogram" "$1"
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -lt 1 || $# -gt 2 ]]; then
|
||||
printf 'usage: %s PATH_TO_LOCALLY_CAPTURED_DLSS_FATBIN [KERNEL_NAME]\n' "$0" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ZLUDA_ROOT="${ZLUDA_ROOT:-}"
|
||||
ROCM_ROOT="${ROCM_ROOT:-}"
|
||||
if [[ -z "$ZLUDA_ROOT" || -z "$ROCM_ROOT" || ! -f "$ZLUDA_ROOT/libcuda.so" || ! -f "$ROCM_ROOT/lib/libamdhip64.so" ]]; then
|
||||
printf 'Set ZLUDA_ROOT and ROCM_ROOT to the ZLUDA and HIP runtimes\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
"$ROOT/scripts/build_replay_dlss_kernel.sh"
|
||||
LD_LIBRARY_PATH="$ZLUDA_ROOT:$ROCM_ROOT/lib:$ROCM_ROOT/lib/llvm/lib:${LD_LIBRARY_PATH:-}" \
|
||||
"$ROOT/build/replay_dlss_kernel" "$@"
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ZLUDA_ROOT="${ZLUDA_ROOT:-}"
|
||||
ROCM_ROOT="${ROCM_ROOT:-}"
|
||||
if [[ -z "$ZLUDA_ROOT" || -z "$ROCM_ROOT" || ! -f "$ZLUDA_ROOT/libcuda.so" || ! -f "$ROCM_ROOT/lib/libamdhip64.so" ]]; then
|
||||
printf 'Set ZLUDA_ROOT and ROCM_ROOT to the ZLUDA and HIP runtimes\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
"$ROOT/scripts/build_replay_dlss_surface_half_store.sh"
|
||||
LD_LIBRARY_PATH="$ZLUDA_ROOT:$ROCM_ROOT/lib:$ROCM_ROOT/lib/llvm/lib:${LD_LIBRARY_PATH:-}" \
|
||||
"$ROOT/build/replay_dlss_surface_half_store"
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ZLUDA_ROOT="${ZLUDA_ROOT:-}"
|
||||
ROCM_ROOT="${ROCM_ROOT:-}"
|
||||
if [[ -z "$ZLUDA_ROOT" || -z "$ROCM_ROOT" || ! -f "$ZLUDA_ROOT/libcuda.so" || ! -f "$ROCM_ROOT/lib/libamdhip64.so" ]]; then
|
||||
printf 'Set ZLUDA_ROOT and ROCM_ROOT to the ZLUDA and HIP runtimes\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
"$ROOT/scripts/build_replay_dlss_surface_store.sh"
|
||||
LD_LIBRARY_PATH="$ZLUDA_ROOT:$ROCM_ROOT/lib:$ROCM_ROOT/lib/llvm/lib:${LD_LIBRARY_PATH:-}" \
|
||||
"$ROOT/build/replay_dlss_surface_store"
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ZLUDA_ROOT="${ZLUDA_ROOT:-}"
|
||||
ROCM_ROOT="${ROCM_ROOT:-}"
|
||||
if [[ -z "$ZLUDA_ROOT" || -z "$ROCM_ROOT" || ! -f "$ZLUDA_ROOT/libcuda.so" || ! -f "$ROCM_ROOT/lib/libamdhip64.so" ]]; then
|
||||
printf 'Set ZLUDA_ROOT and ROCM_ROOT to the ZLUDA and HIP runtimes\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
"$ROOT/scripts/build_replay_dlss_texture_sample.sh"
|
||||
LD_LIBRARY_PATH="$ZLUDA_ROOT:$ROCM_ROOT/lib:$ROCM_ROOT/lib/llvm/lib:${LD_LIBRARY_PATH:-}" \
|
||||
"$ROOT/build/replay_dlss_texture_sample"
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verify all original M11 source pack bytes remain in this reorganized tree."""
|
||||
import csv, hashlib
|
||||
from pathlib import Path
|
||||
root=Path(__file__).resolve().parent.parent
|
||||
manifest=root/'docs/archive/m11-source-snapshot/MIGRATION_MAP.tsv'
|
||||
bad=[]
|
||||
with manifest.open(newline='',encoding='utf-8') as f:
|
||||
for row in csv.DictReader(f,delimiter='\t'):
|
||||
path=root/row['new_path']
|
||||
if not path.is_file():
|
||||
bad.append('MISSING '+row['new_path']);continue
|
||||
sha=hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
if sha!=row['sha256'] or path.stat().st_size!=int(row['bytes']):
|
||||
bad.append('MISMATCH '+row['new_path'])
|
||||
if bad:
|
||||
print('\n'.join(bad[:50]));raise SystemExit(f'FAILED: {len(bad)} mismatches')
|
||||
print('OK: source file bytes and paths match MIGRATION_MAP.tsv')
|
||||
@@ -0,0 +1,10 @@
|
||||
# M11 historical Windows scripts
|
||||
|
||||
The source archive's native-Windows M11 scripts are under
|
||||
[`../../windows/m11/tools/`](../../windows/m11/tools/) and its shim build scripts
|
||||
under [`../../windows/m11/src/`](../../windows/m11/src/).
|
||||
|
||||
They deliberately retain their original **internal** layout because many derive
|
||||
paths from `$PSScriptRoot`. This pointer does not imply they are interchangeable
|
||||
with the mainline `scripts/windows/` build and release tooling. See
|
||||
[`../../docs/windows-m11.md`](../../docs/windows-m11.md).
|
||||
@@ -0,0 +1,17 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$Module,
|
||||
[Parameter(Mandatory=$true)][string]$Candidate,
|
||||
[Parameter(Mandatory=$true)][string]$FixtureDirectory,
|
||||
[string]$HipRoot, [string]$Probe, [string]$OutputDirectory,
|
||||
[ValidateRange(1,10000)][int]$Pairs=32,
|
||||
[ValidateRange(1,256)][int[]]$Batches=@(1,64),
|
||||
[ValidateSet('events','dispatch')][string[]]$Timings=@('events','dispatch'))
|
||||
$ErrorActionPreference='Stop'
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$Probe) { $Probe=Join-Path $repo 'build/windows-rdna4-therock/d4r_native_replay_probe.exe' }
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/native-benchmark-'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
& python.exe (Join-Path $repo 'tools/windows/benchmark_native_replay.py') --probe $Probe --hip-root $HipRoot `
|
||||
--module $Module --candidate $Candidate --fixture-dir $FixtureDirectory --output-dir $OutputDirectory `
|
||||
--pairs $Pairs --batches @Batches --timings @Timings
|
||||
if ($LASTEXITCODE) { throw "Native benchmark failed. Logs: $OutputDirectory" }
|
||||
@@ -0,0 +1,28 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$AdlxRoot, [string]$BuildDirectory, [string]$Compiler)
|
||||
$ErrorActionPreference='Stop'
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
$commit='32b5a740d42295c5dfe9026b9f52683da0f3af91'
|
||||
if (!$AdlxRoot) { $AdlxRoot=Join-Path $repo 'external/ADLX' }
|
||||
if (!$BuildDirectory) { $BuildDirectory=Join-Path $repo 'build/gpu-telemetry' }
|
||||
if (!$Compiler) { $Compiler=Join-Path $repo '.tools/llvm-mingw-20260922-ucrt-x86_64/bin/x86_64-w64-mingw32-clang.exe' }
|
||||
if (!(Test-Path -LiteralPath $AdlxRoot)) {
|
||||
& git clone --no-checkout https://github.com/GPUOpen-LibrariesAndSDKs/ADLX.git $AdlxRoot
|
||||
if ($LASTEXITCODE) { throw 'ADLX SDK download failed.' }
|
||||
& git -C $AdlxRoot checkout --detach $commit
|
||||
if ($LASTEXITCODE) { throw 'ADLX SDK checkout failed.' }
|
||||
}
|
||||
if ((& git -C $AdlxRoot rev-parse HEAD).Trim() -ne $commit -or (& git -C $AdlxRoot status --porcelain)) {
|
||||
throw "Expected clean ADLX SDK $commit; use a separate AdlxRoot."
|
||||
}
|
||||
if (!(Test-Path -LiteralPath $Compiler)) { throw 'Set Compiler to an x64 LLVM-MinGW clang.exe.' }
|
||||
New-Item -ItemType Directory -Force $BuildDirectory | Out-Null
|
||||
$exe=Join-Path $BuildDirectory 'd4r_gpu_telemetry.exe'
|
||||
& $Compiler -std=c11 -O2 -Wall -Wextra -Wno-unknown-pragmas -DWIN32_LEAN_AND_MEAN -D_M_AMD64 `
|
||||
-isystem "$AdlxRoot/SDK/Include" (Join-Path $repo 'tools/windows/gpu_telemetry.c') -o $exe
|
||||
if ($LASTEXITCODE) { throw "GPU telemetry compile failed: $LASTEXITCODE" }
|
||||
@{sdkCommit=$commit; sdkLicense=(Join-Path $AdlxRoot 'ADLX SDK License Agreement.pdf');
|
||||
executable=$exe; compiler=$Compiler; sha256=(Get-FileHash -LiteralPath $exe -Algorithm SHA256).Hash} |
|
||||
ConvertTo-Json | Set-Content -LiteralPath (Join-Path $BuildDirectory 'build-info.json') -Encoding UTF8
|
||||
Write-Host "Built local read-only telemetry: $exe"
|
||||
# The SDK, driver DLL and SDK-dependent binary are not added to public packages.
|
||||
@@ -0,0 +1,47 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$HipRoot = $env:HIP_PATH,
|
||||
[ValidateSet('gfx1100','gfx1101','gfx1102','gfx1103','gfx1150','gfx1151','gfx1152','gfx1153','gfx1154','gfx1200','gfx1201')]
|
||||
[string]$GpuArch = 'gfx1101',
|
||||
[int]$Parallel = [Math]::Max(1, [Environment]::ProcessorCount - 1)
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$HipRoot) { throw 'Pass -HipRoot or set HIP_PATH to the Windows HIP SDK root.' }
|
||||
$HipRoot = [IO.Path]::GetFullPath($HipRoot)
|
||||
if (!(Test-Path -LiteralPath (Join-Path $HipRoot 'include/hip/hip_runtime_api.h'))) {
|
||||
throw "HIP headers not found under $HipRoot"
|
||||
}
|
||||
& (Join-Path $PSScriptRoot 'setup-native-engine.ps1')
|
||||
if ($LASTEXITCODE) { throw "Native engine dependency setup failed: $LASTEXITCODE" }
|
||||
|
||||
$tools = Join-Path $repo '.tools'
|
||||
$cmake = Join-Path $tools 'python/cmake/data/bin/cmake.exe'
|
||||
$ninja = Join-Path $tools 'python/ninja/data/bin/ninja.exe'
|
||||
if (!(Test-Path -LiteralPath $cmake)) { $cmake = Join-Path $tools 'python/bin/cmake.exe' }
|
||||
if (!(Test-Path -LiteralPath $ninja)) { $ninja = Join-Path $tools 'python/bin/ninja.exe' }
|
||||
if (!(Test-Path -LiteralPath $cmake) -or !(Test-Path -LiteralPath $ninja)) {
|
||||
throw 'Pinned CMake/Ninja missing. Run scripts/windows/setup-windows-tools.ps1 first.'
|
||||
}
|
||||
$toolchain = Join-Path $tools 'llvm-mingw-20260922-ucrt-x86_64/bin'
|
||||
$cCompiler = Join-Path $toolchain 'x86_64-w64-mingw32-clang.exe'
|
||||
$cxxCompiler = Join-Path $toolchain 'x86_64-w64-mingw32-clang++.exe'
|
||||
if (!(Test-Path -LiteralPath $cCompiler) -or !(Test-Path -LiteralPath $cxxCompiler)) {
|
||||
throw 'Pinned llvm-mingw compiler missing. Run scripts/windows/setup-windows-tools.ps1 first.'
|
||||
}
|
||||
$oldPath = $env:PATH
|
||||
$env:PATH = "$toolchain;$oldPath"
|
||||
try {
|
||||
$build = Join-Path $repo "build/native-engine-windows-$GpuArch"
|
||||
$headers = Join-Path $tools 'Vulkan-Headers-v1.4.341'
|
||||
$volk = Join-Path $tools 'volk'
|
||||
& $cmake --fresh -S $repo -B $build -G Ninja "-DCMAKE_MAKE_PROGRAM=$ninja" `
|
||||
"-DCMAKE_C_COMPILER=$cCompiler" "-DCMAKE_CXX_COMPILER=$cxxCompiler" `
|
||||
"-DD4R_HIP_ROOT=$HipRoot" "-DD4R_GPU_ARCH=$GpuArch" `
|
||||
'-DD4R_BUILD_NATIVE_ENGINE=ON' "-DD4R_VULKAN_HEADERS_ROOT=$headers" "-DD4R_VOLK_ROOT=$volk" `
|
||||
"-DD4R_MINHOOK_ROOT=$(Join-Path $repo 'external/MinHook')"
|
||||
if ($LASTEXITCODE) { throw "Native engine configure failed: $LASTEXITCODE" }
|
||||
& $cmake --build $build --target d4r_engine_k d4r_engine_m d4r_engine_rr --parallel $Parallel
|
||||
if ($LASTEXITCODE) { throw "Native engine build failed: $LASTEXITCODE" }
|
||||
} finally { $env:PATH = $oldPath }
|
||||
Write-Host "Built native runners in ${build}: d4r-k.exe, d4r-m.exe, d4r-rr.exe."
|
||||
@@ -0,0 +1,68 @@
|
||||
[CmdletBinding()]
|
||||
param([ValidateSet('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0')]
|
||||
[string]$Layer='enc1', [ValidateSet('source','cu','wgp')][string]$Mode='source',
|
||||
[ValidateSet('baseline','load','store','both')][string]$NonTemporal='baseline',
|
||||
[ValidateSet(0,1,2,4)][int]$PosTiles=0, [switch]$ShareValues, [switch]$PrivateValues,
|
||||
[switch]$PackedAccumulator,
|
||||
[string]$HipRoot, [string]$OutputDirectory)
|
||||
$ErrorActionPreference='Stop'
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if ($ShareValues -and $PrivateValues) { throw 'Choose shared or private V storage.' }
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo "build/native-k-experiment-$Layer-$Mode-$NonTemporal-pos$PosTiles-share$([int][bool]$ShareValues)-private$([int][bool]$PrivateValues)-packed$([int][bool]$PackedAccumulator)" }
|
||||
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||
foreach ($baseline in @('build/native-k-gfx1201','build/windows-rdna4-therock','dist/windows-rdna4-command-list')) {
|
||||
if ($OutputDirectory.StartsWith([IO.Path]::GetFullPath((Join-Path $repo $baseline)), [StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw 'Keep experimental objects outside the validated baseline/package.'
|
||||
}
|
||||
}
|
||||
$compiler=Join-Path $HipRoot 'lib/llvm/bin/clang++.exe'
|
||||
if (!(Test-Path -LiteralPath $compiler)) { $compiler=Join-Path $HipRoot 'bin/clang++.exe' }
|
||||
$name="dltss_pwin_${Layer}_layer"
|
||||
$source=Join-Path $repo "kernels/k/$name.hip"
|
||||
$flags=@()
|
||||
if ((Get-Content -LiteralPath $source -Raw) -match '// d4r-build-flags: ([^\r\n]+)') { $flags=$Matches[1].Trim().Split(' ') }
|
||||
if ($Mode -ne 'source') {
|
||||
$flags=@($flags | Where-Object { $_ -ne '-mcumode' -and $_ -ne '-mno-cumode' })
|
||||
$flags+=if ($Mode -eq 'cu') { '-mcumode' } else { '-mno-cumode' }
|
||||
}
|
||||
if ($NonTemporal -in @('load','both')) { $flags+='-DPWIN_NT_LOAD' }
|
||||
if ($NonTemporal -in @('store','both')) { $flags+='-DPWIN_NT_STORE' }
|
||||
if ($PosTiles) {
|
||||
if ($Layer -notin @('enc0','enc1','dec0')) { throw 'Token tiling is audited for position-only layers only.' }
|
||||
$flags+=if ($Layer -eq 'enc1') { "-DD4R_K_ENC1_POS_MT=$PosTiles" } else { "-DD4R_K_POS_MT=$PosTiles" }
|
||||
}
|
||||
if ($ShareValues) {
|
||||
if ($Layer -notin @('enc0','dec0') -and !($Layer -eq 'enc1' -and $PosTiles)) {
|
||||
throw 'ShareValues requires a position-only template.'
|
||||
}
|
||||
$flags+='-DPOS_VSHARE'
|
||||
}
|
||||
if ($PackedAccumulator) { $flags+='-DD4R_K_PACKED_ACC' }
|
||||
if ($PrivateValues) { $flags+='-DD4R_K_PRIVATE_V' }
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$object=Join-Path $OutputDirectory "$name.hsaco"
|
||||
$arguments=@('-x','hip','-std=c++17','--offload-arch=gfx1201','--offload-device-only',
|
||||
'--no-gpu-bundle-output','-mno-wavefrontsize64','-nogpuinc','-nogpulib','-O3',
|
||||
'-DD4R_DEVICE_ONLY_MINIMAL','-DD4R_K_FP16_BASELINE')+$flags+@($source,'-o',$object)
|
||||
$ErrorActionPreference='Continue'
|
||||
& $compiler @arguments > (Join-Path $OutputDirectory "$name.build.stdout.log") 2> (Join-Path $OutputDirectory "$name.build.stderr.log")
|
||||
$ErrorActionPreference='Stop'
|
||||
if ($LASTEXITCODE) { throw "Experimental K compile failed: $Layer" }
|
||||
& (Join-Path (Split-Path $compiler) 'llvm-objdump.exe') --disassemble --mcpu=gfx1201 $object `
|
||||
> (Join-Path $OutputDirectory "$name.isa.txt")
|
||||
if ($LASTEXITCODE) { throw 'Native object ISA extraction failed.' }
|
||||
& (Join-Path (Split-Path $compiler) 'llvm-readobj.exe') --notes $object `
|
||||
> (Join-Path $OutputDirectory "$name.metadata.txt")
|
||||
if ($LASTEXITCODE) { throw 'Native object metadata extraction failed.' }
|
||||
@{layer=$Layer; architecture='gfx1201'; experimental=$true; numericalValidation='pending';
|
||||
mode=$Mode; nonTemporal=$NonTemporal; tokenTilesPerWave=$PosTiles; shareValues=[bool]$ShareValues;
|
||||
privateValues=[bool]$PrivateValues;
|
||||
packedAccumulator=[bool]$PackedAccumulator;
|
||||
compiler=$compiler; arguments=$arguments;
|
||||
sourceCommit=(git -C $repo rev-parse HEAD); workingTreeDirty=[bool](& git -C $repo status --porcelain);
|
||||
compilerVersion=(& $compiler --version | Out-String).Trim();
|
||||
sha256=(Get-FileHash -LiteralPath $object -Algorithm SHA256).Hash} |
|
||||
ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $OutputDirectory "$name.build-info.json") -Encoding UTF8
|
||||
Write-Host "Compiled gfx1201 K experiment: $Layer mode=$Mode nonTemporal=$NonTemporal posTiles=$PosTiles shareValues=$([bool]$ShareValues). Validate before installing."
|
||||
# No override manifest or game installation is produced by this script.
|
||||
@@ -0,0 +1,40 @@
|
||||
[CmdletBinding()]
|
||||
param([ValidateSet('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0')]
|
||||
[string]$Layer='enc1', [string]$HipRoot, [string]$OutputDirectory)
|
||||
$ErrorActionPreference='Stop'
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo 'build/native-k-f16-wmma-experimental' }
|
||||
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||
if ($OutputDirectory -eq [IO.Path]::GetFullPath((Join-Path $repo 'build/native-k-gfx1201'))) {
|
||||
throw 'Keep the validated baseline separate from this experimental build.'
|
||||
}
|
||||
$compiler=Join-Path $HipRoot 'lib/llvm/bin/clang++.exe'
|
||||
if (!(Test-Path -LiteralPath $compiler)) { $compiler=Join-Path $HipRoot 'bin/clang++.exe' }
|
||||
if (!(Test-Path -LiteralPath $compiler)) { throw "HIP compiler missing under $HipRoot" }
|
||||
$name="dltss_pwin_${Layer}_layer"
|
||||
$source=Join-Path $repo "kernels/k/$name.hip"
|
||||
$flags=@()
|
||||
if ((Get-Content -LiteralPath $source -Raw) -match '// d4r-build-flags: ([^\r\n]+)') { $flags=$Matches[1].Trim().Split(' ') }
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$object=Join-Path $OutputDirectory "$name.hsaco"
|
||||
$ErrorActionPreference='Continue'
|
||||
& $compiler -x hip -std=c++17 --offload-arch=gfx1201 --offload-device-only --no-gpu-bundle-output `
|
||||
-mno-wavefrontsize64 -nogpuinc -nogpulib -O3 -DD4R_DEVICE_ONLY_MINIMAL `
|
||||
-DD4R_K_FP16_BASELINE -DD4R_K_F16_WMMA @flags $source -o $object `
|
||||
> (Join-Path $OutputDirectory "$name.build.stdout.log") 2> (Join-Path $OutputDirectory "$name.build.stderr.log")
|
||||
$ErrorActionPreference='Stop'
|
||||
if ($LASTEXITCODE) { throw "Experimental K FP16 WMMA compile failed: $Layer" }
|
||||
$isa=Join-Path $OutputDirectory "$name.isa.txt"
|
||||
& (Join-Path (Split-Path $compiler) 'llvm-objdump.exe') --disassemble --mcpu=gfx1201 $object > $isa
|
||||
if ($LASTEXITCODE -or !(Select-String -LiteralPath $isa -Pattern 'v_wmma_f16_16x16x16_f16')) {
|
||||
throw 'Packed FP16 WMMA is absent from ISA; no alternative instruction is accepted.'
|
||||
}
|
||||
& (Join-Path (Split-Path $compiler) 'llvm-readobj.exe') --notes $object `
|
||||
> (Join-Path $OutputDirectory "$name.metadata.txt")
|
||||
if ($LASTEXITCODE) { throw 'Native object metadata extraction failed.' }
|
||||
@{layer=$Layer; architecture='gfx1201'; experimental=$true; numericalValidation='pending';
|
||||
compiler=$compiler; sha256=(Get-FileHash -LiteralPath $object -Algorithm SHA256).Hash} |
|
||||
ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory "$name.build-info.json") -Encoding UTF8
|
||||
Write-Host "Compiled experimental gfx1201 packed FP16 WMMA: $Layer. Validate before selecting another layer."
|
||||
# This script does not generate an override manifest or install into the game.
|
||||
@@ -0,0 +1,60 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$DlssDll, [string]$HipRoot, [string]$OutputDirectory, [switch]$HardwarePacking, [switch]$TokenLanes)
|
||||
$ErrorActionPreference='Stop'
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo $(if ($HardwarePacking) { 'build/native-m-fp8-hardware-pack' } else { 'build/native-m-fp8-experimental' }) }
|
||||
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||
if ($OutputDirectory -eq [IO.Path]::GetFullPath((Join-Path $repo 'build/native-m-gfx1201'))) {
|
||||
throw 'Keep the validated FP16 baseline separate from the experimental FP8 build.'
|
||||
}
|
||||
$DlssDll=(Get-Item -LiteralPath $DlssDll -ErrorAction Stop).FullName
|
||||
$compiler=Join-Path $HipRoot 'lib/llvm/bin/clang++.exe'
|
||||
if (!(Test-Path -LiteralPath $compiler)) { $compiler=Join-Path $HipRoot 'bin/clang++.exe' }
|
||||
if (!(Test-Path -LiteralPath $compiler)) { throw "HIP compiler missing under $HipRoot" }
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$disassembler=Join-Path (Split-Path $compiler) 'llvm-objdump.exe'
|
||||
if (!(Test-Path -LiteralPath $disassembler)) { throw 'HIP llvm-objdump is required to confirm native FP8 instructions.' }
|
||||
foreach ($source in Get-ChildItem -LiteralPath (Join-Path $repo 'kernels/m') -Filter '*.hip' | Sort-Object Name) {
|
||||
$flags=@()
|
||||
if ((Get-Content -LiteralPath $source.FullName -Raw) -match '// d4r-build-flags: ([^\r\n]+)') { $flags=$Matches[1].Trim().Split(' ') }
|
||||
if ($HardwarePacking) { $flags+='-DD4R_FP8_HW_PACK' }
|
||||
if ($TokenLanes) { $flags+='-DSWIN_TOKEN_LANES' }
|
||||
$object=Join-Path $OutputDirectory ($source.BaseName+'.hsaco')
|
||||
$ErrorActionPreference='Continue'
|
||||
& $compiler -x hip -std=c++17 --offload-arch=gfx1201 --offload-device-only --no-gpu-bundle-output `
|
||||
-mno-wavefrontsize64 -nogpuinc -nogpulib -O3 -DD4R_DEVICE_ONLY_MINIMAL `
|
||||
-DSWIN_EXACT -DSWIN_EXACT_PV -DD4R_FP8_WMMA @flags $source.FullName `
|
||||
-o $object > (Join-Path $OutputDirectory ($source.BaseName+'.build.stdout.log')) `
|
||||
2> (Join-Path $OutputDirectory ($source.BaseName+'.build.stderr.log'))
|
||||
$ErrorActionPreference='Stop'
|
||||
if ($LASTEXITCODE) { throw "Experimental FP8 compile failed: $($source.Name)" }
|
||||
$isa=Join-Path $OutputDirectory ($source.BaseName+'.isa.txt')
|
||||
& $disassembler --disassemble --mcpu=gfx1201 $object > $isa
|
||||
if ($LASTEXITCODE -or !(Select-String -LiteralPath $isa -Pattern 'v_wmma_f32_16x16x16_fp8_fp8')) {
|
||||
throw "Native FP8 WMMA is absent from $($source.Name); no FP16 fallback is accepted."
|
||||
}
|
||||
if ($HardwarePacking -and !(Select-String -LiteralPath $isa -Pattern 'v_cvt_pk_fp8_f32')) { throw 'Native FP8 operand packing is absent from ISA.' }
|
||||
Write-Host "Compiled gfx1201 FP8 WMMA: $($source.BaseName)"
|
||||
}
|
||||
if ($HardwarePacking) {
|
||||
$probeDirectory=Join-Path $OutputDirectory 'packing-probe'
|
||||
New-Item -ItemType Directory -Force $probeDirectory | Out-Null
|
||||
& $compiler -x hip -std=c++17 --offload-arch=gfx1201 --offload-device-only --no-gpu-bundle-output `
|
||||
-mno-wavefrontsize64 -nogpuinc -nogpulib -O3 -DD4R_DEVICE_ONLY_MINIMAL `
|
||||
-DD4R_FP8_WMMA -DD4R_FP8_HW_PACK -DSWIN_EXACT -DSWIN_EXACT_PV `
|
||||
(Join-Path $repo 'tools/windows/fp8_pack_probe.hip') -o (Join-Path $probeDirectory 'fp8_pack_probe.hsaco')
|
||||
if ($LASTEXITCODE) { throw 'Native hardware packing diagnostic compile failed.' }
|
||||
}
|
||||
& python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $OutputDirectory $DlssDll
|
||||
if ($LASTEXITCODE) { throw 'Experimental FP8 manifest generation failed.' }
|
||||
@{architecture='gfx1201'; family='M'; experimental=$true; nativeFp8=$true; accuracy=$true;
|
||||
numerics='SWIN_EXACT; SWIN_EXACT_PV; FP8 WMMA for e4m3 GEMMs; exact FP16 attention';
|
||||
validation='not validated; do not install before layer/full-network checks'; tokenLanes=[bool]$TokenLanes;
|
||||
compiler=$compiler; compilerVersion=(& $compiler --version | Select-Object -First 1);
|
||||
sourceCommit=(& git -C $repo rev-parse HEAD).Trim(); nativeFp8IsaVerified=$true; hardwarePacking=[bool]$HardwarePacking;
|
||||
sourceWorkingTreeDirty=[bool](& git -C $repo status --porcelain);
|
||||
dlssSha256=(Get-FileHash -LiteralPath $DlssDll -Algorithm SHA256).Hash;
|
||||
modules=@(Get-ChildItem -LiteralPath $OutputDirectory -Filter '*.hsaco' | Get-FileHash -Algorithm SHA256)} |
|
||||
ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'build-info.json') -Encoding UTF8
|
||||
Write-Host "Experimental native FP8 M objects: $OutputDirectory (baseline unchanged)"
|
||||
@@ -0,0 +1,59 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$DlssdDll,
|
||||
[ValidateSet('D','E')][string]$Preset='D',
|
||||
[string]$OutputDirectory,
|
||||
[string]$GitBash='C:\Program Files\Git\bin\bash.exe'
|
||||
)
|
||||
$ErrorActionPreference='Stop'
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
$DlssdDll=(Get-Item -LiteralPath $DlssdDll -ErrorAction Stop).FullName
|
||||
$GitBash=(Get-Item -LiteralPath $GitBash -ErrorAction Stop).FullName
|
||||
$expectedHash='4bc7ea5fcb2f32cf86bc2cb072e8d2860914a0be511cbdcb2fc206c1d9d83b80'
|
||||
$hash=(Get-FileHash -LiteralPath $DlssdDll -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
if($hash -ne $expectedHash){throw "Unsupported nvngx_dlssd.dll SHA256 $hash; expected NVIDIA DLSS 310.9.1 ($expectedHash)."}
|
||||
if(!$OutputDirectory){$OutputDirectory=Join-Path $repo ("build/native-rr-$($Preset.ToLower())-windows-gfx1101")}
|
||||
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||
$buildRoot=[IO.Path]::GetFullPath((Join-Path $repo 'build'))
|
||||
if(!$OutputDirectory.StartsWith($buildRoot+'\',[StringComparison]::OrdinalIgnoreCase)){
|
||||
throw "RR model output must stay under the ignored build directory: $OutputDirectory"
|
||||
}
|
||||
if(Test-Path -LiteralPath $OutputDirectory){throw "Use a new RR model output directory: $OutputDirectory"}
|
||||
$glslang=Join-Path $repo '.tools/glslang-16.6.0/bin'
|
||||
$pythonHome=Split-Path (Get-Command python.exe -ErrorAction Stop).Source
|
||||
$pythonShim=Join-Path $repo '.tools/rr-build-bin'
|
||||
New-Item -ItemType Directory -Force $pythonShim | Out-Null
|
||||
$python3=Join-Path $pythonShim 'python3.exe'
|
||||
Copy-Item -LiteralPath (Get-Command python.exe).Source -Destination $python3 -Force
|
||||
$pythonVendor=Join-Path $repo '.tools/python/vendor'
|
||||
$cygpath=Join-Path (Split-Path (Split-Path $GitBash -Parent) -Parent) 'usr/bin/cygpath.exe'
|
||||
if(!(Test-Path -LiteralPath $cygpath)){throw "Git for Windows cygpath missing: $cygpath"}
|
||||
foreach($tool in @($python3,(Join-Path $glslang 'glslangValidator.exe'))){if(!(Test-Path -LiteralPath $tool)){throw "RR build tool missing: $tool"}}
|
||||
$unixRepo=(& $cygpath -u $repo).Trim()
|
||||
$unixDll=(& $cygpath -u $DlssdDll).Trim()
|
||||
$unixOut=(& $cygpath -u $OutputDirectory).Trim()
|
||||
if(!$unixRepo -or !$unixDll -or !$unixOut){throw 'Git Bash path conversion failed.'}
|
||||
$oldPath=$env:PATH
|
||||
$oldPythonPath=$env:PYTHONPATH
|
||||
$env:PATH="$pythonShim;$pythonHome;$glslang;$(Split-Path $GitBash -Parent);$oldPath"
|
||||
$env:PYTHONPATH=$pythonVendor
|
||||
$env:D4R_RR_GFX11='1'
|
||||
$env:D4R_RR_RELAXED='0'
|
||||
$log=Join-Path $buildRoot ("native-rr-$($Preset.ToLower())-windows-build.log")
|
||||
try{
|
||||
& $python3 -c 'import numpy; print("numpy", numpy.__version__)'
|
||||
if($LASTEXITCODE){throw 'Pinned NumPy could not load in the Windows RR build environment.'}
|
||||
$command="cd '$unixRepo' && D4R_RR_GFX11=1 D4R_RR_RELAXED=0 bash engine/rr/build_native_model.sh --dll '$unixDll' '$Preset' '$unixOut'"
|
||||
$previousPreference=$ErrorActionPreference
|
||||
try{
|
||||
$ErrorActionPreference='Continue'
|
||||
& $GitBash -lc $command *> $log
|
||||
$bashExit=$LASTEXITCODE
|
||||
}finally{$ErrorActionPreference=$previousPreference}
|
||||
if($bashExit){throw "RR model build failed with exit $bashExit; see $log"}
|
||||
foreach($required in @('rr.txt','model.json','enc0_m.spv','dec0_m.spv','hkpn.spv','enc0.args','hkpn.args')){
|
||||
if(!(Test-Path -LiteralPath (Join-Path $OutputDirectory $required))){throw "RR build incomplete; missing $required"}
|
||||
}
|
||||
Write-Host "Built local preset $Preset native RR model: $OutputDirectory"
|
||||
Write-Host "DLL SHA256: $hash. Build log: $log"
|
||||
}finally{$env:PATH=$oldPath;$env:PYTHONPATH=$oldPythonPath}
|
||||
@@ -0,0 +1,19 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$DlssDll,
|
||||
[string]$Kernel = 'hiluma_engine_output_depthinv_mvlo_hdr_max_v2_rel',
|
||||
[string]$HipRoot = 'C:\Program Files\AMD\ROCm\7.2',
|
||||
[string]$ZludaRoot,
|
||||
[ValidateSet('cu','wgp')][string]$ShaderMode='cu',
|
||||
[string]$GpuArch='gfx1201',
|
||||
[string]$OutputDirectory
|
||||
)
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
[void](Get-D4RGpuTargetInfo $GpuArch)
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' }
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo "build/private-textures-$GpuArch" }
|
||||
& python (Join-Path $repo 'tools/windows/build_native_texture.py') --dlss-dll $DlssDll --hip-root $HipRoot `
|
||||
--zluda-root $ZludaRoot --kernel $Kernel --output-directory $OutputDirectory --shader-mode $ShaderMode --gpu-arch $GpuArch
|
||||
if ($LASTEXITCODE) { throw "Native texture build failed ($LASTEXITCODE); inspect the output work directory." }
|
||||
@@ -0,0 +1,93 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$SourceRoot,
|
||||
[string]$BuildToolsRoot,
|
||||
[string]$InstallRoot,
|
||||
[int]$Jobs = 6
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$SourceRoot) { $SourceRoot = Join-Path $repo 'external/OptiScaler' }
|
||||
if (!$InstallRoot) { $InstallRoot = Join-Path $repo 'dist/optiscaler-windows-d4r' }
|
||||
$SourceRoot = [IO.Path]::GetFullPath($SourceRoot)
|
||||
$InstallRoot = [IO.Path]::GetFullPath($InstallRoot)
|
||||
$base = '45a2001303ddff632e279f77aef85ceede5832cb'
|
||||
$patches = @(Get-ChildItem -LiteralPath (Join-Path $repo 'patches/optiscaler') -Filter '*.patch' | Sort-Object Name)
|
||||
if (!$patches.Count) { throw 'OptiScaler source patches are missing.' }
|
||||
$previousPath = $env:PATH
|
||||
try {
|
||||
# Git for Windows' submodule helper needs its POSIX utility directory.
|
||||
$gitExe = (Get-Command git -ErrorAction Stop).Source
|
||||
$gitInstall = [IO.Path]::GetFullPath((Join-Path (Split-Path $gitExe) '..'))
|
||||
$gitUtilities = Join-Path $gitInstall 'usr/bin'
|
||||
if (Test-Path -LiteralPath $gitUtilities) { $env:PATH = "$gitUtilities;$env:PATH" }
|
||||
if (!(Test-Path -LiteralPath (Join-Path $SourceRoot '.git'))) {
|
||||
& git clone --filter=blob:none --no-checkout https://github.com/optiscaler/OptiScaler.git $SourceRoot
|
||||
if ($LASTEXITCODE) { throw 'OptiScaler clone failed.' }
|
||||
& git -C $SourceRoot checkout --detach $base
|
||||
if ($LASTEXITCODE) { throw 'Pinned OptiScaler checkout failed.' }
|
||||
}
|
||||
& git -C $SourceRoot merge-base --is-ancestor $base HEAD
|
||||
if ($LASTEXITCODE) { throw "OptiScaler must be based on audited commit $base" }
|
||||
foreach ($patch in $patches) {
|
||||
$ErrorActionPreference = 'Continue'
|
||||
& git -C $SourceRoot apply --reverse --check $patch.FullName 2>$null
|
||||
$alreadyApplied = $LASTEXITCODE -eq 0
|
||||
$ErrorActionPreference = 'Stop'
|
||||
if (!$alreadyApplied) {
|
||||
& git -C $SourceRoot apply --check $patch.FullName
|
||||
if ($LASTEXITCODE) { throw "Source patch conflicts: $($patch.Name); existing changes retained." }
|
||||
& git -C $SourceRoot apply $patch.FullName
|
||||
if ($LASTEXITCODE) { throw "Source patch failed: $($patch.Name)" }
|
||||
}
|
||||
}
|
||||
# Verify existing checkouts directly. A complete local checkout does not
|
||||
# need Git's shell-based submodule helper (or a network connection).
|
||||
$submodulePaths=@(& git -C $SourceRoot config --file .gitmodules --get-regexp '\.path$' | ForEach-Object { ($_ -split ' ',2)[1] })
|
||||
$submodules=@()
|
||||
foreach ($path in $submodulePaths) {
|
||||
$tree=(& git -C $SourceRoot ls-tree HEAD -- $path) -split '\s+',4
|
||||
if ($tree.Count -lt 4 -or $tree[0] -ne '160000') { throw "Missing pinned submodule: $path" }
|
||||
$checkout=Join-Path $SourceRoot $path
|
||||
$actual=if (Test-Path -LiteralPath (Join-Path $checkout '.git')) { (& git -C $checkout rev-parse HEAD).Trim() } else { '' }
|
||||
if ($actual -ne $tree[2]) {
|
||||
& git -C $SourceRoot submodule update --init --depth 1 -- $path
|
||||
if ($LASTEXITCODE) { throw "Pinned OptiScaler submodule failed: $path" }
|
||||
$actual=(& git -C $checkout rev-parse HEAD).Trim()
|
||||
if ($actual -ne $tree[2]) { throw "Wrong submodule revision: $path" }
|
||||
}
|
||||
$submodules+=(' '+$actual+' '+$path)
|
||||
}
|
||||
if (!$BuildToolsRoot -and (Test-Path -LiteralPath (Join-Path $repo '.tools/vs2022/MSBuild/Current/Bin/MSBuild.exe'))) {
|
||||
$BuildToolsRoot = Join-Path $repo '.tools/vs2022'
|
||||
}
|
||||
if (!$BuildToolsRoot) {
|
||||
$vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/Installer/vswhere.exe'
|
||||
if (Test-Path -LiteralPath $vswhere) {
|
||||
$BuildToolsRoot = & $vswhere -latest -products '*' -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
|
||||
}
|
||||
}
|
||||
if (!$BuildToolsRoot) { throw 'MSVC v143 Build Tools and Windows SDK 10.0.26100 are required; see patches/optiscaler/README.md.' }
|
||||
$msbuild = Join-Path $BuildToolsRoot 'MSBuild/Current/Bin/MSBuild.exe'
|
||||
if (!(Test-Path -LiteralPath $msbuild)) { throw "MSBuild missing: $msbuild" }
|
||||
New-Item -ItemType Directory -Force $InstallRoot | Out-Null
|
||||
$log = Join-Path $InstallRoot 'build.log'
|
||||
$ErrorActionPreference = 'Continue'
|
||||
& $msbuild (Join-Path $SourceRoot 'OptiScaler.sln') "/m:$Jobs" /p:Configuration=Release /p:Platform=x64 `
|
||||
/p:PostBuildEventUseInBuild=false /verbosity:quiet "/flp:logfile=$log;verbosity=normal"
|
||||
$ErrorActionPreference = 'Stop'
|
||||
if ($LASTEXITCODE) { throw "OptiScaler build failed ($LASTEXITCODE); see $log" }
|
||||
foreach ($file in @('OptiScaler.dll', 'OptiScaler.pdb')) {
|
||||
Copy-Item -LiteralPath (Join-Path $SourceRoot "x64/Release/$file") -Destination $InstallRoot -Force
|
||||
}
|
||||
Copy-Item -LiteralPath (Join-Path $SourceRoot 'LICENSE') -Destination (Join-Path $InstallRoot 'GPL-3.0.txt') -Force
|
||||
foreach ($patch in $patches) { Copy-Item -LiteralPath $patch.FullName -Destination $InstallRoot -Force }
|
||||
$metadata = @{base=$base; sourceCommit=(& git -C $SourceRoot rev-parse HEAD).Trim();
|
||||
sourceDirty=[bool](& git -C $SourceRoot status --porcelain); backendAbi=1;
|
||||
buildTools=$BuildToolsRoot; msbuildVersion=(& $msbuild -version -nologo | Select-Object -Last 1);
|
||||
patches=@($patches | ForEach-Object { @{name=$_.Name; sha256=(Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash} });
|
||||
dllSha256=(Get-FileHash -LiteralPath (Join-Path $InstallRoot 'OptiScaler.dll') -Algorithm SHA256).Hash;
|
||||
submodules=$submodules}
|
||||
$metadata | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $InstallRoot 'build-info.json') -Encoding UTF8
|
||||
Write-Host "Built OptiScaler with external Windows d4r backend: $InstallRoot"
|
||||
} finally { $env:PATH = $previousPath }
|
||||
@@ -0,0 +1,44 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string[]]$GpuArchitectures,
|
||||
[ValidateSet('stable','therock')][string]$RuntimeProfile='therock',
|
||||
[string]$ZludaRoot, [string]$DlssDll,
|
||||
[switch]$PackageGames,
|
||||
[string]$BuildRoot, [string]$OutputRoot, [string]$ArchiveDirectory
|
||||
)
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$GpuArchitectures) { $GpuArchitectures=@(Get-D4RGpuTargets | ForEach-Object { $_.architecture }) }
|
||||
foreach ($arch in $GpuArchitectures) { [void](Get-D4RGpuTargetInfo $arch) }
|
||||
if (@($GpuArchitectures | Select-Object -Unique).Count -ne $GpuArchitectures.Count) { throw 'Duplicate GPU target.' }
|
||||
if (!$BuildRoot) { $BuildRoot=Join-Path $repo 'build/windows-gpu-coverage' }
|
||||
if (!$OutputRoot) { $OutputRoot=Join-Path $repo 'dist/windows-gpu-coverage' }
|
||||
if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' }
|
||||
if ($ArchiveDirectory -and !$PackageGames) { throw 'ArchiveDirectory requires PackageGames.' }
|
||||
if ($PackageGames -and (!$DlssDll -or ![IO.Path]::IsPathRooted($DlssDll) -or !(Test-Path -LiteralPath $DlssDll))) {
|
||||
throw 'PackageGames requires an absolute path to your local nvngx_dlss.dll; it will not be redistributed.'
|
||||
}
|
||||
New-Item -ItemType Directory -Force $BuildRoot | Out-Null
|
||||
if ($ArchiveDirectory) { New-Item -ItemType Directory -Force $ArchiveDirectory | Out-Null }
|
||||
$results=[Collections.Generic.List[object]]::new()
|
||||
foreach ($arch in $GpuArchitectures) {
|
||||
$build="$BuildRoot-$arch"; $diag="$OutputRoot-$arch"
|
||||
$log=Join-Path $BuildRoot "$arch-build.log"
|
||||
Write-Host "Building $arch (compile/package only; no GPU workloads). Log: $log"
|
||||
& (Join-Path $PSScriptRoot 'build-windows-rdna4.ps1') -RuntimeProfile $RuntimeProfile -GpuArch $arch `
|
||||
-ZludaRoot $ZludaRoot -BuildDirectory $build -InstallDirectory $diag *> $log
|
||||
$row=[ordered]@{architecture=$arch; compiled=$true; hardwareTestExecuted=$false; buildDirectory=$build; diagnosticRoot=$diag; log=$log}
|
||||
if ($PackageGames) {
|
||||
$k="$BuildRoot-native-k-$arch"; $m="$BuildRoot-native-m-$arch"; $game="$OutputRoot-game-$arch"
|
||||
& (Join-Path $PSScriptRoot 'stage-native-k.ps1') -DlssDll $DlssDll -PackageRoot $diag -GpuArch $arch -OutputDirectory $k
|
||||
& (Join-Path $PSScriptRoot 'stage-native-m.ps1') -DlssDll $DlssDll -PackageRoot $diag -GpuArch $arch -OutputDirectory $m
|
||||
$packageArgs=@{DiagnosticRoot=$diag; GpuArch=$arch; PackageRoot=$game; ZludaRoot=$ZludaRoot; KernelKRoot=$k; KernelMRoot=$m}
|
||||
if ($ArchiveDirectory) { $packageArgs.ArchivePath=Join-Path $ArchiveDirectory "d4r-windows-$arch.zip" }
|
||||
& (Join-Path $PSScriptRoot 'package-windows-game.ps1') @packageArgs
|
||||
$row.gamePackage=$game
|
||||
}
|
||||
$results.Add($row)
|
||||
$results | ConvertTo-Json -Depth 5 | Set-Content (Join-Path $BuildRoot 'matrix-build.json') -Encoding UTF8
|
||||
Write-Host "Compiled $arch; hardware validation remains separate."
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$HipRoot = $env:HIP_PATH,
|
||||
[ValidateSet('stable', 'therock')][string]$RuntimeProfile = 'stable',
|
||||
[string]$GpuArch = 'gfx1201',
|
||||
[string]$ZludaRoot,
|
||||
[string]$ToolchainRoot,
|
||||
[string]$BuildDirectory,
|
||||
[string]$InstallDirectory
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
[void](Get-D4RGpuTargetInfo $GpuArch)
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if ($RuntimeProfile -eq 'therock') {
|
||||
if (!$PSBoundParameters.ContainsKey('HipRoot')) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$BuildDirectory) { $BuildDirectory = Join-Path $repo 'build/windows-rdna4-therock' }
|
||||
if (!$InstallDirectory) { $InstallDirectory = Join-Path $repo 'dist/windows-rdna4-therock' }
|
||||
}
|
||||
if (!$HipRoot) { $HipRoot = 'C:\Program Files\AMD\ROCm\7.2' }
|
||||
if (!$BuildDirectory) { $BuildDirectory = Join-Path $repo 'build/windows-rdna4' }
|
||||
if (!$InstallDirectory) { $InstallDirectory = Join-Path $repo 'dist/windows-rdna4-diagnostics' }
|
||||
if ($GpuArch -ne 'gfx1201') {
|
||||
if (!$PSBoundParameters.ContainsKey('BuildDirectory')) { $BuildDirectory += "-$GpuArch" }
|
||||
if (!$PSBoundParameters.ContainsKey('InstallDirectory')) { $InstallDirectory += "-$GpuArch" }
|
||||
}
|
||||
$HipRoot = [IO.Path]::GetFullPath($HipRoot).TrimEnd('\', '/')
|
||||
$BuildDirectory = [IO.Path]::GetFullPath($BuildDirectory).TrimEnd('\', '/')
|
||||
$InstallDirectory = [IO.Path]::GetFullPath($InstallDirectory).TrimEnd('\', '/')
|
||||
if (!$ZludaRoot -and (Test-Path (Join-Path $repo '.tools/zluda/zluda/nvcuda.dll'))) {
|
||||
$ZludaRoot = Join-Path $repo '.tools/zluda/zluda'
|
||||
}
|
||||
if (!$ToolchainRoot -and (Test-Path (Join-Path $repo '.tools/llvm-mingw-20260922-ucrt-x86_64'))) {
|
||||
$ToolchainRoot = Join-Path $repo '.tools/llvm-mingw-20260922-ucrt-x86_64'
|
||||
}
|
||||
$cmakeCommand = Get-Command cmake -ErrorAction SilentlyContinue
|
||||
$cmake = if ($cmakeCommand) { $cmakeCommand.Source } else { Join-Path $repo '.tools/python/cmake/data/bin/cmake.exe' }
|
||||
$ninjaCommand = Get-Command ninja -ErrorAction SilentlyContinue
|
||||
$ninja = if ($ninjaCommand) { $ninjaCommand.Source } else { Join-Path $repo '.tools/python/ninja/data/bin/ninja.exe' }
|
||||
if (!(Test-Path $ninja)) { $ninja = Join-Path $repo '.tools/python/bin/ninja.exe' }
|
||||
if (!(Test-Path $cmake) -or !(Test-Path $ninja)) {
|
||||
throw 'CMake >=3.24 and Ninja required. See docs/windows-rdna4-port.md for local tool setup.'
|
||||
}
|
||||
New-Item -ItemType Directory -Force $BuildDirectory | Out-Null
|
||||
$minhook = Join-Path $repo 'external/MinHook'
|
||||
if (!(Test-Path (Join-Path $minhook 'include/MinHook.h'))) {
|
||||
& git clone --depth 1 --branch v1.3.4 https://github.com/TsudaKageyu/minhook.git $minhook
|
||||
if ($LASTEXITCODE) { throw 'MinHook 1.3.4 source download failed.' }
|
||||
}
|
||||
$minhookCommit = (& git -C $minhook rev-parse HEAD).Trim()
|
||||
if ($minhookCommit -ne 'c3fcafdc10146beb5919319d0683e44e3c30d537') {
|
||||
throw "Expected audited MinHook 1.3.4 source, found $minhookCommit"
|
||||
}
|
||||
$originalPath = $env:PATH
|
||||
try {
|
||||
$configure = @('--fresh', '-S', $repo, '-B', $BuildDirectory, '-G', 'Ninja', '-DCMAKE_BUILD_TYPE=RelWithDebInfo',
|
||||
"-DCMAKE_MAKE_PROGRAM=$ninja", "-DD4R_HIP_ROOT=$HipRoot", "-DD4R_GPU_ARCH=$GpuArch", "-DCMAKE_INSTALL_PREFIX=$InstallDirectory")
|
||||
if ($ToolchainRoot) {
|
||||
$compiler = Join-Path $ToolchainRoot 'bin/x86_64-w64-mingw32-clang++.exe'
|
||||
if (!(Test-Path $compiler)) { throw "Compiler missing: $compiler" }
|
||||
$env:PATH = "$(Join-Path $ToolchainRoot 'bin');$originalPath"
|
||||
$configure += "-DCMAKE_CXX_COMPILER=$compiler"
|
||||
$configure += "-DCMAKE_C_COMPILER=$(Join-Path $ToolchainRoot 'bin/x86_64-w64-mingw32-clang.exe')"
|
||||
}
|
||||
if ($ZludaRoot) { $configure += "-DD4R_ZLUDA_ROOT=$ZludaRoot" }
|
||||
$ErrorActionPreference = 'Continue'
|
||||
& $cmake @configure 2>&1 | Tee-Object (Join-Path $BuildDirectory 'configure.log')
|
||||
$ErrorActionPreference = 'Stop'
|
||||
if ($LASTEXITCODE) { throw "CMake configure exit=$LASTEXITCODE" }
|
||||
$ErrorActionPreference = 'Continue'
|
||||
& $cmake --build $BuildDirectory --parallel 4 2>&1 | Tee-Object (Join-Path $BuildDirectory 'build.log')
|
||||
$ErrorActionPreference = 'Stop'
|
||||
if ($LASTEXITCODE) { throw "Build exit=$LASTEXITCODE" }
|
||||
$ErrorActionPreference = 'Continue'
|
||||
& $cmake --install $BuildDirectory 2>&1 | Tee-Object (Join-Path $BuildDirectory 'install.log')
|
||||
$ErrorActionPreference = 'Stop'
|
||||
if ($LASTEXITCODE) { throw "Install exit=$LASTEXITCODE" }
|
||||
Write-Host "Built diagnostics: $InstallDirectory"
|
||||
} finally { $env:PATH = $originalPath }
|
||||
@@ -0,0 +1,70 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$SourceRoot, [string]$HipRoot = 'C:\Program Files\AMD\ROCm\7.2', [string]$WorkRoot)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$SourceRoot) { $SourceRoot = Join-Path $repo 'external/ZLUDA' }
|
||||
if (!$WorkRoot) { $WorkRoot = Join-Path $repo 'build/zluda-helpers-windows' }
|
||||
$toolchain = Join-Path $repo '.tools/llvm-mingw-20260922-ucrt-x86_64'
|
||||
$compiler = Join-Path $HipRoot 'bin/clang++.exe'
|
||||
$dis = Join-Path $HipRoot 'bin/llvm-dis.exe'
|
||||
$assembler = Join-Path $HipRoot 'bin/llvm-as.exe'
|
||||
$ocml = Join-Path $HipRoot 'amdgcn/bitcode/ocml.bc'
|
||||
foreach ($file in @($compiler,$dis,$assembler,$ocml)) {
|
||||
if (!(Test-Path $file)) { throw "HIP helper dependency missing: $file" }
|
||||
}
|
||||
New-Item -ItemType Directory -Force $WorkRoot | Out-Null
|
||||
$configInclude = Join-Path $WorkRoot 'libcxx-config'
|
||||
New-Item -ItemType Directory -Force $configInclude | Out-Null
|
||||
Copy-Item -LiteralPath (Join-Path $toolchain 'include/c++/v1/__config_site') -Destination $configInclude -Force
|
||||
Copy-Item -LiteralPath (Join-Path $SourceRoot 'ext/llvm-project/libcxx/vendor/llvm/default_assertion_handler.in') `
|
||||
-Destination (Join-Path $configInclude '__assertion_handler') -Force
|
||||
# HIP 7.2's long shuffle overloads assume every non-MSVC compiler has LP64.
|
||||
# Windows GNU also has 32-bit long. Correct that public header in a private
|
||||
# mirror; the installed SDK remains untouched and the GPU target is unchanged.
|
||||
$hipInclude = Join-Path $WorkRoot 'hip-include'
|
||||
New-Item -ItemType Directory -Force $hipInclude | Out-Null
|
||||
Copy-Item -LiteralPath (Join-Path $HipRoot 'include/hip') -Destination $hipInclude -Recurse -Force
|
||||
$warpHeader = Join-Path $hipInclude 'hip/amd_detail/amd_warp_functions.h'
|
||||
$warpSource = [IO.File]::ReadAllText($warpHeader)
|
||||
$warpSource = $warpSource.Replace('#ifndef _MSC_VER', '#if __SIZEOF_LONG__ == 8')
|
||||
[IO.File]::WriteAllText($warpHeader,$warpSource,[Text.UTF8Encoding]::new($false))
|
||||
$variants = @(
|
||||
@{Suffix=''; Flags=@()},
|
||||
@{Suffix='_constrained'; Flags=@('-ffp-model=strict','-ffp-exception-behavior=ignore')},
|
||||
@{Suffix='_w64'; Flags=@('-mwavefrontsize64','-DD4R_WAVE64')},
|
||||
@{Suffix='_constrained_w64'; Flags=@('-ffp-model=strict','-ffp-exception-behavior=ignore','-mwavefrontsize64','-DD4R_WAVE64')}
|
||||
)
|
||||
foreach ($variant in $variants) {
|
||||
$name = 'zluda_ptx_impl' + $variant.Suffix
|
||||
$raw = Join-Path $WorkRoot "$name.raw.bc"
|
||||
$ir = Join-Path $WorkRoot "$name.ll"
|
||||
$output = Join-Path $SourceRoot "ptx/lib/$name.bc"
|
||||
$arguments = @('-x','hip','-std=c++20','--target=x86_64-w64-windows-gnu','-stdlib=libc++',
|
||||
'--offload-arch=gfx1030','--offload-device-only','-emit-llvm','-c','-nogpulib','-O3',
|
||||
'-mno-wavefrontsize64','-DHIP_ENABLE_WARP_SYNC_BUILTINS',
|
||||
'-Xclang','-fdenormal-fp-math=dynamic',
|
||||
'-isystem',(Join-Path $SourceRoot 'ext/llvm-project/libcxx/include'),
|
||||
'-isystem',$configInclude,
|
||||
'-isystem',(Join-Path $toolchain 'include'),
|
||||
'-I',$hipInclude,
|
||||
'-Xclang','-mlink-bitcode-file','-Xclang',$ocml)
|
||||
$arguments += $variant.Flags
|
||||
$arguments += @('-o',$raw,(Join-Path $SourceRoot 'ptx/lib/zluda_ptx_impl.cpp'))
|
||||
$ErrorActionPreference = 'Continue'
|
||||
& $compiler @arguments 2>&1 | Tee-Object (Join-Path $WorkRoot "$name.compile.log")
|
||||
$ErrorActionPreference = 'Stop'
|
||||
if ($LASTEXITCODE) { throw "PTX helper compile failed: $name ($LASTEXITCODE)" }
|
||||
& $dis $raw -o $ir
|
||||
if ($LASTEXITCODE) { throw "Disassemble failed: $raw" }
|
||||
$clean = [Collections.Generic.List[string]]::new()
|
||||
foreach ($line in [IO.File]::ReadLines($ir)) {
|
||||
if ($line -match '@llvm.used|wchar_size|llvm.module.flags|__hip_cuid') { continue }
|
||||
$line = $line -replace 'optnone','' -replace 'define hidden','define linkonce_odr'
|
||||
$line = $line -replace '"target-cpu"="gfx1030"','' -replace '"target-features"="[^"]+"',''
|
||||
$clean.Add($line)
|
||||
}
|
||||
[IO.File]::WriteAllLines($ir,$clean,[Text.UTF8Encoding]::new($false))
|
||||
& $assembler $ir -o $output
|
||||
if ($LASTEXITCODE) { throw "Assemble failed: $ir" }
|
||||
Write-Host "Built $output ($((Get-Item $output).Length) bytes)"
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$SourceRoot, [string]$BuildRoot, [int]$Jobs = 4)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$SourceRoot) { $SourceRoot = Join-Path $repo 'external/ZLUDA' }
|
||||
if (!$BuildRoot) { $BuildRoot = Join-Path $repo 'build/zluda-llvm-windows-gnu' }
|
||||
$toolchain = Join-Path $repo '.tools/llvm-mingw-20260922-ucrt-x86_64'
|
||||
$cmake = Join-Path $repo '.tools/python/cmake/data/bin/cmake.exe'
|
||||
$ninja = Join-Path $repo '.tools/python/bin/ninja.exe'
|
||||
if (!(Test-Path $ninja)) { $ninja = Join-Path $repo '.tools/python/ninja/data/bin/ninja.exe' }
|
||||
if (!(Test-Path (Join-Path $SourceRoot 'ext/llvm-project/llvm/CMakeLists.txt'))) {
|
||||
throw 'Run git submodule update --init --depth 1 ext/llvm-project in the ZLUDA checkout.'
|
||||
}
|
||||
New-Item -ItemType Directory -Force $BuildRoot | Out-Null
|
||||
& (Join-Path $PSScriptRoot 'prepare-zluda-llvm-source.ps1') -LlvmSourceRoot (Join-Path $SourceRoot 'ext/llvm-project')
|
||||
$oldPath = $env:PATH
|
||||
try {
|
||||
$env:PATH = "$(Join-Path $toolchain 'bin');$oldPath"
|
||||
$arguments = @('-S', (Join-Path $SourceRoot 'ext/llvm-project/llvm'), '-B', $BuildRoot,
|
||||
'-G', 'Ninja', "-DCMAKE_MAKE_PROGRAM=$ninja", '-DCMAKE_BUILD_TYPE=Release',
|
||||
"-DCMAKE_C_COMPILER=$(Join-Path $toolchain 'bin/x86_64-w64-mingw32-clang.exe')",
|
||||
"-DCMAKE_CXX_COMPILER=$(Join-Path $toolchain 'bin/x86_64-w64-mingw32-clang++.exe')",
|
||||
'-DLLVM_TARGETS_TO_BUILD=AMDGPU', '-DLLVM_ENABLE_PROJECTS=llvm;lld',
|
||||
'-DLLVM_ENABLE_WARNINGS=OFF', '-DLLVM_ENABLE_TERMINFO=OFF', '-DLLVM_ENABLE_LIBXML2=OFF',
|
||||
'-DLLVM_ENABLE_LIBEDIT=OFF', '-DLLVM_ENABLE_LIBPFM=OFF', '-DLLVM_ENABLE_ZLIB=OFF',
|
||||
'-DLLVM_ENABLE_ZSTD=OFF', '-DLLVM_INCLUDE_BENCHMARKS=OFF', '-DLLVM_INCLUDE_EXAMPLES=OFF',
|
||||
'-DLLVM_INCLUDE_TESTS=OFF', '-DLLVM_BUILD_TESTS=OFF', '-DLLVM_BUILD_TOOLS=OFF',
|
||||
'-DLLVM_OPTIMIZED_TABLEGEN=ON', '-DLLVM_PARALLEL_LINK_JOBS=1')
|
||||
$ErrorActionPreference = 'Continue'
|
||||
& $cmake @arguments 2>&1 | Tee-Object (Join-Path $BuildRoot 'configure.log')
|
||||
$ErrorActionPreference = 'Stop'
|
||||
if ($LASTEXITCODE) { throw "LLVM configure failed ($LASTEXITCODE)" }
|
||||
$ErrorActionPreference = 'Continue'
|
||||
& $cmake --build $BuildRoot --parallel $Jobs --target llvm-config llvm-libraries lldCommon lldELF `
|
||||
2>&1 | Tee-Object (Join-Path $BuildRoot 'build.log')
|
||||
$ErrorActionPreference = 'Stop'
|
||||
if ($LASTEXITCODE) { throw "LLVM build failed ($LASTEXITCODE)" }
|
||||
& (Join-Path $BuildRoot 'bin/llvm-config.exe') --version
|
||||
if ($LASTEXITCODE) { throw 'llvm-config verification failed' }
|
||||
@{sourceBase=(& git -C (Join-Path $SourceRoot 'ext/llvm-project') rev-parse HEAD).Trim(); fullSourceDeltaVerified=$true;
|
||||
sourcePatches=@(Get-ChildItem (Join-Path $repo 'patches/zluda-llvm') -Filter *.patch | ForEach-Object {
|
||||
@{name=$_.Name; sha256=(Get-FileHash -LiteralPath $_.FullName).Hash}})} |
|
||||
ConvertTo-Json -Depth 5 | Set-Content (Join-Path $BuildRoot 'd4r-llvm-build-info.json') -Encoding UTF8
|
||||
Write-Host "Native Windows LLVM: $BuildRoot"
|
||||
} finally { $env:PATH = $oldPath }
|
||||
@@ -0,0 +1,96 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$SourceRoot, [string]$LlvmBuildRoot, [string]$InstallRoot, [switch]$FetchOnly, [switch]$CacheTestsOnly, [switch]$CompilerTestsOnly, [int]$Jobs = 4)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$SourceRoot) { $SourceRoot = Join-Path $repo 'external/ZLUDA' }
|
||||
if (!$LlvmBuildRoot) { $LlvmBuildRoot = Join-Path $repo 'build/zluda-llvm-windows-gnu' }
|
||||
if (!$InstallRoot) { $InstallRoot = Join-Path $repo 'dist/zluda-windows-final' }
|
||||
$rust = Join-Path $repo '.tools/rust-1.98.1-gnu'
|
||||
$toolchain = Join-Path $repo '.tools/llvm-mingw-20260922-ucrt-x86_64'
|
||||
$target = Join-Path $repo 'build/zluda-rust-windows-gnu'
|
||||
New-Item -ItemType Directory -Force $target,$InstallRoot | Out-Null
|
||||
$settings = @{
|
||||
PATH="$(Join-Path $rust 'bin');$(Join-Path $toolchain 'bin');$(Join-Path $repo '.tools/python/cmake/data/bin');$(Join-Path $repo '.tools/python/bin');C:\Program Files\Git\usr\bin;$env:PATH"
|
||||
CARGO_HOME=(Join-Path $repo '.tools/cargo-home')
|
||||
CARGO_TARGET_DIR=$target
|
||||
CARGO_BUILD_JOBS="$Jobs"
|
||||
RUSTC=(Join-Path $rust 'bin/rustc.exe')
|
||||
CC=(Join-Path $toolchain 'bin/x86_64-w64-mingw32-clang.exe')
|
||||
CXX=(Join-Path $toolchain 'bin/x86_64-w64-mingw32-clang++.exe')
|
||||
AR=(Join-Path $toolchain 'bin/llvm-ar.exe')
|
||||
CXXSTDLIB='c++'
|
||||
LIBRARY_PATH=(Join-Path $rust 'lib/rustlib/x86_64-pc-windows-gnu/lib/self-contained')
|
||||
CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER=(Join-Path $toolchain 'bin/x86_64-w64-mingw32-clang.exe')
|
||||
CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUSTFLAGS='-C link-arg=-static'
|
||||
ZLUDA_LLVM_BUILD_DIR=$LlvmBuildRoot
|
||||
}
|
||||
$previous = @{}
|
||||
foreach ($name in $settings.Keys) {
|
||||
$previous[$name] = [Environment]::GetEnvironmentVariable($name,'Process')
|
||||
[Environment]::SetEnvironmentVariable($name,$settings[$name],'Process')
|
||||
}
|
||||
Push-Location $SourceRoot
|
||||
try {
|
||||
$cargo = Join-Path $rust 'bin/cargo.exe'
|
||||
if (!(Test-Path $cargo)) { throw 'Run setup-rust-toolchain.ps1 first.' }
|
||||
if ($CompilerTestsOnly) {
|
||||
$ErrorActionPreference = 'Continue'
|
||||
& $cargo test --locked --release --target x86_64-pc-windows-gnu -p llvm_zluda shader_mode_tests `
|
||||
2>&1 | Tee-Object (Join-Path $target 'compiler-tests.log')
|
||||
$ErrorActionPreference = 'Stop'
|
||||
if ($LASTEXITCODE) { throw "ZLUDA compiler regression tests failed ($LASTEXITCODE)" }
|
||||
return
|
||||
}
|
||||
if ($CacheTestsOnly) {
|
||||
$ErrorActionPreference = 'Continue'
|
||||
& $cargo test --locked --release --target x86_64-pc-windows-gnu -p zluda_cache `
|
||||
2>&1 | Tee-Object (Join-Path $target 'cache-tests.log')
|
||||
$ErrorActionPreference = 'Stop'
|
||||
if ($LASTEXITCODE) { throw "ZLUDA cache regression tests failed ($LASTEXITCODE)" }
|
||||
return
|
||||
}
|
||||
if ($FetchOnly) {
|
||||
$ErrorActionPreference = 'Continue'
|
||||
& $cargo fetch --locked --target x86_64-pc-windows-gnu 2>&1 | Tee-Object (Join-Path $target 'fetch.log')
|
||||
$ErrorActionPreference = 'Stop'
|
||||
if ($LASTEXITCODE) { throw "Cargo fetch failed ($LASTEXITCODE)" }
|
||||
return
|
||||
}
|
||||
if (!(Test-Path (Join-Path $LlvmBuildRoot 'bin/llvm-config.exe'))) { throw 'Run build-zluda-llvm.ps1 first.' }
|
||||
foreach ($variant in @('','_constrained','_w64','_constrained_w64')) {
|
||||
if (!(Test-Path "ptx/lib/zluda_ptx_impl$variant.bc")) { throw 'Run build-zluda-helpers.ps1 first.' }
|
||||
}
|
||||
$ErrorActionPreference = 'Continue'
|
||||
& $cargo build --locked --release --target x86_64-pc-windows-gnu -p zluda -p nvapi -p zluda_trace `
|
||||
2>&1 | Tee-Object (Join-Path $target 'build.log')
|
||||
$ErrorActionPreference = 'Stop'
|
||||
if ($LASTEXITCODE) { throw "ZLUDA build failed ($LASTEXITCODE)" }
|
||||
$ErrorActionPreference = 'Continue'
|
||||
& $cargo build --locked --release --target x86_64-pc-windows-gnu -p ptx --example d4r_emit `
|
||||
2>&1 | Tee-Object (Join-Path $target 'emit-build.log')
|
||||
$ErrorActionPreference = 'Stop'
|
||||
if ($LASTEXITCODE) { throw "d4r_emit build failed ($LASTEXITCODE)" }
|
||||
$release = Join-Path $target 'x86_64-pc-windows-gnu/release'
|
||||
Copy-Item -LiteralPath (Join-Path $release 'nvcuda.dll') -Destination $InstallRoot -Force
|
||||
Copy-Item -LiteralPath (Join-Path $release 'nvapi64.dll') -Destination $InstallRoot -Force
|
||||
# The C++ dependencies select libc++ dynamically even when Rust requests
|
||||
# static GNU support libraries. Keep these portable runtime DLLs local.
|
||||
foreach ($runtime in @('libc++.dll', 'libunwind.dll')) {
|
||||
Copy-Item -LiteralPath (Join-Path $toolchain "bin/$runtime") -Destination $InstallRoot -Force
|
||||
}
|
||||
New-Item -ItemType Directory -Force (Join-Path $InstallRoot 'trace') | Out-Null
|
||||
Copy-Item -LiteralPath (Join-Path $release 'zluda_trace.dll') -Destination (Join-Path $InstallRoot 'trace/nvcuda.dll') -Force
|
||||
Copy-Item -LiteralPath (Join-Path $release 'examples/d4r_emit.exe') -Destination $InstallRoot -Force
|
||||
@{base='ee2f25a180099fa42f36b2346732e1f2470a03ad'; target='x86_64-pc-windows-gnu';
|
||||
sourceCommit=(& git rev-parse HEAD); workingTreeDirty=[bool](& git status --porcelain);
|
||||
rust='1.98.1'; llvm=(& (Join-Path $LlvmBuildRoot 'bin/llvm-config.exe') --version);
|
||||
llvmSource=$(if (Test-Path (Join-Path $LlvmBuildRoot 'd4r-llvm-build-info.json')) {
|
||||
Get-Content (Join-Path $LlvmBuildRoot 'd4r-llvm-build-info.json') -Raw | ConvertFrom-Json
|
||||
} else { $null });
|
||||
files=@(Get-ChildItem -LiteralPath $InstallRoot -Filter '*.dll' | Get-FileHash -Algorithm SHA256)} |
|
||||
ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $InstallRoot 'build-info.json') -Encoding UTF8
|
||||
Write-Host "Native Windows d4r ZLUDA runtime: $InstallRoot"
|
||||
} finally {
|
||||
Pop-Location
|
||||
foreach ($name in $previous.Keys) { [Environment]::SetEnvironmentVariable($name,$previous[$name],'Process') }
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][int]$GameProcessId, [Parameter(Mandatory=$true)][string]$OutputPath)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
if (!('D4RWindowCapture' -as [type])) {
|
||||
Add-Type @'
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
public static class D4RWindowCapture {
|
||||
public delegate bool Callback(IntPtr window, IntPtr data);
|
||||
[StructLayout(LayoutKind.Sequential)] public struct Rect { public int Left, Top, Right, Bottom; }
|
||||
[DllImport("user32.dll")] public static extern bool EnumWindows(Callback callback, IntPtr data);
|
||||
[DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr window, out uint process);
|
||||
[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr window, out Rect rect);
|
||||
[DllImport("user32.dll",CharSet=CharSet.Unicode)] public static extern int GetWindowText(IntPtr window, StringBuilder text, int count);
|
||||
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr window);
|
||||
[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
|
||||
[DllImport("user32.dll")] public static extern IntPtr SetThreadDpiAwarenessContext(IntPtr context);
|
||||
}
|
||||
'@
|
||||
}
|
||||
$windows = [Collections.Generic.List[object]]::new()
|
||||
$previousDpi=[D4RWindowCapture]::SetThreadDpiAwarenessContext([IntPtr](-4))
|
||||
try {
|
||||
$callback = [D4RWindowCapture+Callback]{ param($window,$data)
|
||||
[uint32]$owner = 0
|
||||
[void][D4RWindowCapture]::GetWindowThreadProcessId($window,[ref]$owner)
|
||||
if ($owner -eq $GameProcessId) {
|
||||
$rect = [D4RWindowCapture+Rect]::new()
|
||||
[void][D4RWindowCapture]::GetWindowRect($window,[ref]$rect)
|
||||
$title = [Text.StringBuilder]::new(512)
|
||||
[void][D4RWindowCapture]::GetWindowText($window,$title,512)
|
||||
$windows.Add([pscustomobject]@{handle=$window; title=$title.ToString(); visible=[D4RWindowCapture]::IsWindowVisible($window); rect=$rect;
|
||||
area=($rect.Right-$rect.Left)*($rect.Bottom-$rect.Top)})
|
||||
}
|
||||
return $true
|
||||
}
|
||||
[void][D4RWindowCapture]::EnumWindows($callback,[IntPtr]::Zero)
|
||||
$windows | Select-Object handle,title,visible,area | Format-Table
|
||||
$chosen = $windows | Where-Object { $_.visible -and $_.area -gt 4096 } | Sort-Object area -Descending | Select-Object -First 1
|
||||
if (!$chosen) { throw "No visible game window for PID $GameProcessId" }
|
||||
if ([D4RWindowCapture]::GetForegroundWindow() -ne $chosen.handle) {
|
||||
throw 'Game window is not foreground; capture refused to avoid including unrelated overlapping windows.'
|
||||
}
|
||||
$rect = $chosen.rect
|
||||
$bitmap = [Drawing.Bitmap]::new($rect.Right-$rect.Left,$rect.Bottom-$rect.Top)
|
||||
$graphics = [Drawing.Graphics]::FromImage($bitmap)
|
||||
try {
|
||||
$graphics.CopyFromScreen($rect.Left,$rect.Top,0,0,$bitmap.Size)
|
||||
$bitmap.Save([IO.Path]::GetFullPath($OutputPath),[Drawing.Imaging.ImageFormat]::Png)
|
||||
} finally {
|
||||
$graphics.Dispose(); $bitmap.Dispose()
|
||||
}
|
||||
Write-Host "Captured game window: $OutputPath"
|
||||
} finally {
|
||||
if ($previousDpi -ne [IntPtr]::Zero) { [void][D4RWindowCapture]::SetThreadDpiAwarenessContext($previousDpi) }
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$SourceRoot)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$SourceRoot) { $SourceRoot = Join-Path $repo 'external/ZLUDA' }
|
||||
$base = 'ee2f25a180099fa42f36b2346732e1f2470a03ad'
|
||||
$relative = 'build/zluda-patch-check-' + [Guid]::NewGuid().ToString('N')
|
||||
$snapshot = Join-Path $repo $relative
|
||||
New-Item -ItemType Directory -Force $snapshot | Out-Null
|
||||
$archive = "$snapshot.tar"
|
||||
& git -C $SourceRoot archive --format=tar "--output=$archive" $base
|
||||
if ($LASTEXITCODE) { throw 'Could not archive the pinned ZLUDA base.' }
|
||||
& tar.exe -xf $archive -C $snapshot
|
||||
if ($LASTEXITCODE) { throw 'Could not extract the ZLUDA patch-check snapshot.' }
|
||||
Push-Location $repo
|
||||
try {
|
||||
foreach ($patch in (Get-ChildItem -LiteralPath (Join-Path $repo 'patches/zluda') -Filter '*.patch' | Sort-Object Name)) {
|
||||
& git apply "--directory=$relative" --check $patch.FullName
|
||||
if ($LASTEXITCODE) { throw "Fresh-source patch check failed: $($patch.Name)" }
|
||||
& git apply "--directory=$relative" --whitespace=nowarn $patch.FullName
|
||||
if ($LASTEXITCODE) { throw "Fresh-source patch apply failed: $($patch.Name)" }
|
||||
Write-Host "PASS patch $($patch.Name)"
|
||||
}
|
||||
Write-Host "Pinned-source patch snapshot: $snapshot"
|
||||
} finally { Pop-Location }
|
||||
@@ -0,0 +1,271 @@
|
||||
# Checks a d4r install on Windows. It only reads files and prints what it finds.
|
||||
# Run it from the game folder (the one holding the game's .exe and d4r\), or give that folder:
|
||||
# powershell -ExecutionPolicy Bypass -File d4r\d4r-check.ps1 [GAME_FOLDER]
|
||||
# Add -Report to also write one ZIP (logs, this output, GPU and driver) to send with a bug report.
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$GameFolder,
|
||||
[switch]$Report,
|
||||
[string]$InventoryFile, # tests: a gpu-inventory.json to use instead of running the GPU query
|
||||
[string]$ReportDirectory # tests: where -Report writes its ZIP (default: your Desktop)
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
if (!$GameFolder) {
|
||||
$GameFolder = if ((Split-Path $PSScriptRoot -Leaf) -eq 'd4r') { Split-Path $PSScriptRoot -Parent } else { (Get-Location).Path }
|
||||
}
|
||||
$game = [IO.Path]::GetFullPath($GameFolder).TrimEnd('\')
|
||||
$d4r = Join-Path $game 'd4r'
|
||||
$problems = 0
|
||||
$lines = [Collections.Generic.List[string]]::new()
|
||||
function Say([string]$text) { $lines.Add($text); Write-Host $text }
|
||||
function Ok([string]$text) { Say (' ok ' + $text) }
|
||||
function Bad([string]$text) { Say (' MISSING ' + $text); $script:problems++ }
|
||||
function Note([string]$text) { Say (' note ' + $text) }
|
||||
function Hash([string]$path) { (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash }
|
||||
function Version([string]$path) {
|
||||
try { $v = (Get-Item -LiteralPath $path).VersionInfo; if ($v.FileVersion) { return ($v.FileVersion -replace ',\s*', '.') } } catch {}
|
||||
return 'unknown'
|
||||
}
|
||||
function Read-TextFile([string]$path) { [IO.File]::ReadAllText($path) }
|
||||
|
||||
Say "d4r install in $game"
|
||||
if (!(Test-Path -LiteralPath $d4r -PathType Container)) {
|
||||
Say 'no d4r folder here; run this from the folder with the game .exe (extract the d4r ZIP there first)'
|
||||
exit 1
|
||||
}
|
||||
if (@(Get-ChildItem -LiteralPath $game -Filter '*.exe' -File -ErrorAction SilentlyContinue).Count) { Ok 'game executable next to d4r\' }
|
||||
else { Note 'no .exe next to d4r\ (the d4r files must sit in the folder of the game''s main .exe; for Unreal Engine games that is <game>\<Project>\Binaries\Win64)' }
|
||||
|
||||
# --- the install's own file list ---------------------------------------------------------------
|
||||
$manifest = $null
|
||||
$manifestPath = Join-Path $d4r 'package.json'
|
||||
if (Test-Path -LiteralPath $manifestPath) {
|
||||
try { $manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json } catch { $manifest = $null }
|
||||
}
|
||||
if (!$manifest) { Bad 'd4r\package.json is missing or damaged (extract the d4r ZIP again)' }
|
||||
|
||||
# --- files --------------------------------------------------------------------------------------
|
||||
$required = @('dxgi.dll', 'OptiScaler.ini', 'd4r\_nvngx.dll', 'd4r\d4r.ini', 'd4r\dll-pins.txt',
|
||||
'd4r\hip\bin\amdhip64_7.dll', 'd4r\zluda\nvcuda.dll', 'd4r\zluda\nvapi64.dll', 'd4r\nvapi\backend\nvapi64.dll')
|
||||
foreach ($file in $required) {
|
||||
if (Test-Path -LiteralPath (Join-Path $game $file) -PathType Leaf) { Ok $file }
|
||||
else { Bad "$file (extract the d4r ZIP again over the game folder)" }
|
||||
}
|
||||
if ($manifest) {
|
||||
$edited = @('OptiScaler.ini', 'd4r/d4r.ini') # players are meant to edit these
|
||||
$damaged = 0
|
||||
foreach ($entry in $manifest.files) {
|
||||
$path = Join-Path $game ($entry.path -replace '/', '\')
|
||||
if (!(Test-Path -LiteralPath $path -PathType Leaf)) {
|
||||
if ($required -notcontains ($entry.path -replace '/', '\')) { Bad "$($entry.path) (extract the d4r ZIP again)" }
|
||||
continue
|
||||
}
|
||||
if ((Hash $path) -ne $entry.sha256) {
|
||||
if ($entry.path -eq 'dxgi.dll') { Note 'dxgi.dll is not the one that came with d4r (another OptiScaler or ReShade?). Move it away and extract the d4r ZIP again' }
|
||||
elseif ($edited -contains $entry.path) { Note "$($entry.path) has been edited (fine if you did it)" }
|
||||
else { Bad "$($entry.path) differs from the release (damaged or replaced; extract the d4r ZIP again)"; $damaged++ }
|
||||
}
|
||||
}
|
||||
if (!$damaged) { Ok 'every other d4r file matches the release' }
|
||||
}
|
||||
|
||||
# --- OptiScaler.ini ---------------------------------------------------------------------------
|
||||
$optiIni = Join-Path $game 'OptiScaler.ini'
|
||||
if (Test-Path -LiteralPath $optiIni) {
|
||||
$text = Read-TextFile $optiIni
|
||||
if ($text -notmatch '(?im)^\s*AllowExternalBackend\s*=\s*true') { Bad 'OptiScaler.ini: [DLSS] AllowExternalBackend must be true (this is not the d4r OptiScaler.ini)' }
|
||||
if ($text -notmatch '(?im)^\s*Dx12Upscaler\s*=\s*dlss') { Note 'OptiScaler.ini: [Upscalers] Dx12Upscaler is not dlss; the game will not use d4r' }
|
||||
$m = [regex]::Match($text, '(?im)^\s*NvngxPath\s*=\s*(\S+)')
|
||||
if (!$m.Success) { Bad 'OptiScaler.ini: [Libraries] NvngxPath is missing (it must name the d4r folder)' }
|
||||
else {
|
||||
$folder = $m.Groups[1].Value
|
||||
$resolved = if ([IO.Path]::IsPathRooted($folder)) { $folder } else { Join-Path $game $folder }
|
||||
if (Test-Path -LiteralPath (Join-Path $resolved '_nvngx.dll')) { Ok "OptiScaler.ini NvngxPath = $folder" }
|
||||
else { Bad "OptiScaler.ini NvngxPath = $folder does not hold _nvngx.dll (use NvngxPath=d4r, or the full path of the d4r folder)" }
|
||||
}
|
||||
}
|
||||
|
||||
# --- NVIDIA DLLs (never shipped; the exact versions come from dll-pins.txt) -----------------------
|
||||
$pins = @()
|
||||
$pinsPath = Join-Path $d4r 'dll-pins.txt'
|
||||
if (Test-Path -LiteralPath $pinsPath) {
|
||||
foreach ($line in Get-Content -LiteralPath $pinsPath) {
|
||||
$line = $line.Trim()
|
||||
if (!$line -or $line.StartsWith('#')) { continue }
|
||||
$f = $line -split '\s+'
|
||||
if ($f.Count -ge 4 -and $f[1].Length -eq 64) { $pins += [pscustomobject]@{ kind = $f[0]; sha = $f[1].ToUpperInvariant(); version = $f[2]; status = $f[3] } }
|
||||
}
|
||||
}
|
||||
function Check-Nvidia([string]$kind, [string]$relative, [string]$label, [string]$source) {
|
||||
$path = Join-Path $game $relative
|
||||
$wanted = @($pins | Where-Object { $_.kind -eq $kind } | ForEach-Object { $_.version }) -join ' or '
|
||||
if (!$wanted) { Note "$label cannot be checked (d4r\dll-pins.txt is missing or damaged)"; return }
|
||||
if (!(Test-Path -LiteralPath $path -PathType Leaf)) { Bad "${relative}: copy NVIDIA's $label here (version $wanted; $source)"; return }
|
||||
$hash = Hash $path
|
||||
$pin = @($pins | Where-Object { $_.kind -eq $kind -and $_.sha -eq $hash }) | Select-Object -First 1
|
||||
if (!$pin) { Bad "$relative is version $(Version $path), which d4r does not accept. d4r on Windows needs exactly $wanted ($source)"; return }
|
||||
if ($pin.status -eq 'validated') { Ok "$relative ($label $($pin.version), tested on Windows)" }
|
||||
else { Ok "$relative ($label $($pin.version))"; Note "$label $($pin.version) is accepted but has had less Windows testing" }
|
||||
}
|
||||
Check-Nvidia 'dlss' 'd4r\dlss\nvngx_dlss.dll' 'nvngx_dlss.dll' 'many games ship one in their folder; see WINDOWS_README.txt'
|
||||
Check-Nvidia 'ngx' 'd4r\ngx\_nvngx.dll' '_nvngx.dll' 'it is part of NVIDIA''s display driver package; see WINDOWS_README.txt'
|
||||
|
||||
# --- GPU and the build for it --------------------------------------------------------------------
|
||||
$registryPath = Join-Path $d4r 'gpu-targets.json'
|
||||
$registry = @()
|
||||
if (Test-Path -LiteralPath $registryPath) { $registry = @((Get-Content -LiteralPath $registryPath -Raw | ConvertFrom-Json).targets) }
|
||||
$inventory = $null
|
||||
if ($InventoryFile) { $inventory = Get-Content -LiteralPath $InventoryFile -Raw | ConvertFrom-Json }
|
||||
elseif (Test-Path -LiteralPath (Join-Path $d4r 'd4r_gpu_inventory.exe')) {
|
||||
$work = Join-Path ([IO.Path]::GetTempPath()) ('d4r-check-' + [Guid]::NewGuid().ToString('N'))
|
||||
New-Item -ItemType Directory -Path $work | Out-Null
|
||||
try {
|
||||
$info = New-Object Diagnostics.ProcessStartInfo
|
||||
$info.FileName = Join-Path $d4r 'd4r_gpu_inventory.exe'
|
||||
$info.Arguments = '--hip-root "' + (Join-Path $d4r 'hip') + '" --output-dir "' + $work + '"'
|
||||
$info.UseShellExecute = $false; $info.CreateNoWindow = $true
|
||||
$info.RedirectStandardOutput = $true; $info.RedirectStandardError = $true
|
||||
$info.EnvironmentVariables['D4R_DIAG_DIR'] = $work
|
||||
$process = New-Object Diagnostics.Process; $process.StartInfo = $info
|
||||
[void]$process.Start()
|
||||
$null = $process.StandardOutput.ReadToEndAsync(); $null = $process.StandardError.ReadToEndAsync()
|
||||
if (!$process.WaitForExit(30000)) { $process.Kill(); $process.WaitForExit() }
|
||||
if (Test-Path -LiteralPath (Join-Path $work 'gpu-inventory.json')) { $inventory = Get-Content -LiteralPath (Join-Path $work 'gpu-inventory.json') -Raw | ConvertFrom-Json }
|
||||
} catch {} finally {
|
||||
$process.Dispose()
|
||||
Get-ChildItem -LiteralPath $work -File -ErrorAction SilentlyContinue | ForEach-Object { Remove-Item -LiteralPath $_.FullName }
|
||||
Remove-Item -LiteralPath $work -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
$arch = $null
|
||||
if (!$inventory) { Note 'could not ask the AMD runtime about your GPU (d4r_gpu_inventory.exe is missing, or the AMD graphics driver is not installed or is too old)' }
|
||||
else {
|
||||
$supported = @($registry | ForEach-Object { $_.architecture })
|
||||
$devices = @($inventory.devices | Where-Object { $_.wavefrontSize -eq 32 -and ($supported -contains $_.architecture -or !$supported.Count) })
|
||||
if (!$devices.Count) {
|
||||
$seen = @($inventory.devices | ForEach-Object { "$($_.name) ($($_.architecture))" }) -join ', '
|
||||
Bad ("no supported RDNA3/RDNA4 GPU found by the AMD runtime. It sees: " + $(if ($seen) { $seen } else { 'nothing' }) + '. Update the AMD Adrenalin driver; RDNA2 and older are not supported')
|
||||
} else {
|
||||
foreach ($device in $devices) {
|
||||
$a = $device.architecture
|
||||
$missing = @()
|
||||
foreach ($rel in @("d4r\_nvngx_$a.dll", "d4r\pixel_convert_$a.hsaco", "d4r\nvapi\$a\nvapi64.dll", "d4r\native\$a\d4r-kernels.txt")) {
|
||||
if (!(Test-Path -LiteralPath (Join-Path $game $rel))) { $missing += $rel }
|
||||
}
|
||||
$native = @(Get-ChildItem -LiteralPath (Join-Path $d4r "native\$a") -Filter '*.hsaco' -ErrorAction SilentlyContinue).Count
|
||||
if ($missing.Count) { Bad "GPU $($device.name) ($a): this release has no complete build for it (missing: $($missing -join ', '))" }
|
||||
else {
|
||||
Ok "GPU $($device.name) ($a): build present, $native native kernels"
|
||||
if (!$arch) { $arch = $a }
|
||||
}
|
||||
if ($a -ne 'gfx1201') { Note "only the RX 9070 XT (gfx1201) has been tested on real hardware; $a is compile-tested" }
|
||||
}
|
||||
}
|
||||
if (@($inventory.devices | Where-Object { $_.wavefrontSize -eq 32 }).Count -gt 1) { Note 'several GPUs found; d4r uses the one the game renders on' }
|
||||
}
|
||||
if ($arch -and $registry.Count) {
|
||||
$info = @($registry | Where-Object { $_.architecture -eq $arch }) | Select-Object -First 1
|
||||
$bad = 0
|
||||
$objects = @(Get-Item -LiteralPath (Join-Path $d4r "pixel_convert_$arch.hsaco") -ErrorAction SilentlyContinue) +
|
||||
@(Get-ChildItem -LiteralPath (Join-Path $d4r "native\$arch") -Filter '*.hsaco' -ErrorAction SilentlyContinue)
|
||||
foreach ($object in $objects) {
|
||||
$stream = [IO.File]::OpenRead($object.FullName)
|
||||
try { $header = New-Object byte[] 64; [void]$stream.Read($header, 0, 64) } finally { $stream.Dispose() }
|
||||
if ($header[0] -ne 127 -or $header[1] -ne 69 -or $header[2] -ne 76 -or $header[3] -ne 70 -or $header[48] -ne $info.elfMachine) {
|
||||
Bad "$($object.Name) is not built for $arch (do not rename or mix files from other GPUs; extract the d4r ZIP again)"; $bad++
|
||||
}
|
||||
}
|
||||
if ($objects.Count -and !$bad) { Ok "all $($objects.Count) GPU code objects are built for $arch" }
|
||||
}
|
||||
|
||||
# --- Linux / Wine leftovers ------------------------------------------------------------------------
|
||||
$proxyFound = $false
|
||||
foreach ($name in @('d3d12.dll', 'd3d12core.dll')) {
|
||||
$proxy = Join-Path $game $name
|
||||
if (Test-Path -LiteralPath $proxy -PathType Leaf) {
|
||||
$item = Get-Item -LiteralPath $proxy
|
||||
if ($item.Length -le 64MB -and [Text.Encoding]::ASCII.GetString([IO.File]::ReadAllBytes($proxy)) -match '(?i)vkd3d|wined3d') {
|
||||
Bad "$name in the game folder is vkd3d-proton/Wine (left over from a Linux install?). Move it out; d4r on Windows uses native D3D12"
|
||||
$proxyFound = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$proxyFound) { Ok 'no vkd3d/Wine d3d12.dll in the game folder' }
|
||||
|
||||
# --- d4r.ini ---------------------------------------------------------------------------------------
|
||||
$iniPath = Join-Path $d4r 'd4r.ini'
|
||||
$model = 'K'
|
||||
if (Test-Path -LiteralPath $iniPath) {
|
||||
$section = ''; $iniProblems = 0
|
||||
$known = @{ 'dlss.model' = 1; 'dlss.showwatermark' = 1; 'interop.asyncinterop' = 1; 'debug.validateoutput' = 1; 'debug.log' = 1; 'paths.cachedir' = 1; 'kernels.nativefp8' = 1 }
|
||||
$boolean = @('dlss.showwatermark', 'interop.asyncinterop', 'debug.validateoutput', 'kernels.nativefp8')
|
||||
foreach ($raw in (Read-TextFile $iniPath).TrimStart([char]0xFEFF) -split "`n") {
|
||||
$line = $raw.Trim()
|
||||
if (!$line -or $line.StartsWith(';') -or $line.StartsWith('#')) { continue }
|
||||
if ($line.StartsWith('[')) { $section = $line.Trim('[', ']').Trim(); continue }
|
||||
$eq = $line.IndexOf('='); if ($eq -lt 0) { continue }
|
||||
$key = $line.Substring(0, $eq).Trim()
|
||||
$value = ($line.Substring($eq + 1) -replace '\s[;#].*$', '').Trim()
|
||||
$name = ("$section.$key").ToLowerInvariant()
|
||||
$v = $value.ToLowerInvariant()
|
||||
if (!$known.ContainsKey($name)) { Note "d4r.ini: [$section] $key is not a Windows setting; ignored"; $iniProblems++ }
|
||||
elseif ($name -eq 'dlss.model') {
|
||||
if (@('', 'auto', 'k', 'dlss4', '11') -contains $v) { $model = 'K' }
|
||||
elseif (@('m', 'dlss4.5', '13') -contains $v) { $model = 'M' }
|
||||
else { Note "d4r.ini: [DLSS] Model '$value' is not available on Windows (use K or M); K will be used"; $iniProblems++ }
|
||||
}
|
||||
elseif ($boolean -contains $name) {
|
||||
if (@('', 'auto', '1', 'true', 'yes', 'on', '0', 'false', 'no', 'off') -notcontains $v) { Note "d4r.ini: [$section] $key must be true or false, not '$value'; the default will be used"; $iniProblems++ }
|
||||
}
|
||||
elseif ($name -eq 'debug.log' -and @('', 'auto', 'normal', 'verbose') -notcontains $v) { Note "d4r.ini: [Debug] Log must be normal or verbose, not '$value'; normal will be used"; $iniProblems++ }
|
||||
}
|
||||
if (!$iniProblems) { Ok "d4r.ini is valid (model $model)" }
|
||||
}
|
||||
|
||||
# --- what the last game start said -----------------------------------------------------------------
|
||||
$log = Join-Path $d4r 'd4r_nvngx.log'
|
||||
if (Test-Path -LiteralPath $log) {
|
||||
$messages = @(Get-Content -LiteralPath $log | Where-Object { $_ -match '^d4r: ' })
|
||||
$failed = @($messages | Where-Object { $_ -match 'DLSS disabled' })
|
||||
if ($failed.Count) { foreach ($m in $failed | Select-Object -First 3) { Bad ('last game start: ' + $m.Substring(5)) } }
|
||||
elseif ($messages.Count) { Ok ('last game start: ' + $messages[0].Substring(5)) }
|
||||
else { Note 'd4r\d4r_nvngx.log has no d4r summary line yet (the game may not have chosen DLSS)' }
|
||||
foreach ($m in $messages | Where-Object { $_ -notmatch 'DLSS disabled|player mode' } | Select-Object -First 5) { Note ('last game start: ' + $m.Substring(5)) }
|
||||
} else { Note 'no d4r\d4r_nvngx.log yet: start the game once and choose DLSS (or FSR/XeSS) in its graphics settings' }
|
||||
if (Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.Path -and $_.Path.StartsWith($game + '\', [StringComparison]::OrdinalIgnoreCase) -and $_.ProcessName -notmatch '^(powershell|pwsh)$' }) {
|
||||
Note 'the game is running; its log is rewritten at every start'
|
||||
}
|
||||
|
||||
Say ''
|
||||
if ($problems -eq 0) { Say 'Everything d4r needs is in place.' } else { Say "$problems thing(s) to fix above." }
|
||||
|
||||
# --- bug-report ZIP ------------------------------------------------------------------------------
|
||||
if ($Report) {
|
||||
$stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
|
||||
$destination = if ($ReportDirectory) { $ReportDirectory } else { [Environment]::GetFolderPath('Desktop') }
|
||||
$staging = Join-Path ([IO.Path]::GetTempPath()) ("d4r-report-$stamp")
|
||||
New-Item -ItemType Directory -Force -Path $staging | Out-Null
|
||||
try {
|
||||
$lines | Set-Content -LiteralPath (Join-Path $staging 'check.txt') -Encoding UTF8
|
||||
foreach ($file in @('d4r\d4r_nvngx.log', 'd4r\d4r.ini', 'OptiScaler.ini', 'OptiScaler.log')) {
|
||||
$source = Join-Path $game $file
|
||||
if (Test-Path -LiteralPath $source) { Copy-Item -LiteralPath $source -Destination (Join-Path $staging (Split-Path $file -Leaf)) }
|
||||
}
|
||||
Get-ChildItem -LiteralPath $game -Filter 'OptiScaler*.log' -File | ForEach-Object { Copy-Item -LiteralPath $_.FullName -Destination $staging -Force }
|
||||
try { Get-CimInstance Win32_VideoController | Select-Object Name, DriverVersion, DriverDate | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $staging 'driver.json') -Encoding UTF8 } catch {}
|
||||
if ($inventory) { $inventory | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $staging 'gpu-inventory.json') -Encoding UTF8 }
|
||||
Get-ChildItem -LiteralPath $game -Recurse -File -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.FullName.StartsWith($d4r + '\') -or $_.Name -in @('dxgi.dll', 'OptiScaler.ini') } |
|
||||
ForEach-Object { '{0} {1,12} {2}' -f (Hash $_.FullName), $_.Length, $_.FullName.Substring($game.Length + 1) } |
|
||||
Set-Content -LiteralPath (Join-Path $staging 'files.sha256.txt') -Encoding UTF8
|
||||
$zip = Join-Path $destination "d4r-report-$stamp.zip"
|
||||
Compress-Archive -Path (Join-Path $staging '*') -DestinationPath $zip -Force
|
||||
Write-Host ''
|
||||
Write-Host "Bug report written: $zip" -ForegroundColor Cyan
|
||||
Write-Host 'Also tell us: GPU, game, K or M, and what the image looked like. The logs can contain local folder names; look before posting publicly.'
|
||||
} finally {
|
||||
Get-ChildItem -LiteralPath $staging -File -ErrorAction SilentlyContinue | ForEach-Object { Remove-Item -LiteralPath $_.FullName }
|
||||
Remove-Item -LiteralPath $staging -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
if ($problems) { exit 1 }
|
||||
@@ -0,0 +1,32 @@
|
||||
# Parse completed diagnostic records. Empty streams are valid, including games
|
||||
# that refuse a debugger before initializing the upscaler.
|
||||
function Get-D4RGameSummary {
|
||||
param([AllowEmptyString()][string]$Stdout='', [AllowEmptyString()][string]$Stderr='',
|
||||
[AllowEmptyString()][string]$Debugger='', [int]$Preset=11, [double]$RunSeconds=0,
|
||||
[bool]$ValidateOutput=$false, $GameCrash=$null)
|
||||
$gameExit=[regex]::Match($Debugger, 'EXIT code=0x([0-9a-f]+)')
|
||||
$exitCode=if ($gameExit.Success) { '0x'+$gameExit.Groups[1].Value } else { 'diagnostic_timeout' }
|
||||
$frames=[regex]::Matches($Stdout, '(?m)^D4R_FRAME cpu_frame_copies=([0-9]+) frame_age=([0-9]+) interop_ngx_ms=([0-9.]+)\r?$')
|
||||
$checks=[regex]::Matches($Stdout, '(?m)^D4R_OUTPUT_VALIDATION elements=([0-9]+) nan=([0-9]+) inf=([0-9]+) diagnostics_cpu_bytes=8\r?$')
|
||||
$launches=[regex]::Matches($Stderr, '\[d4r-launch\] kernel="([^"]+)" backend=(native|translated)')
|
||||
$kernels=@($launches | ForEach-Object { $_.Groups[1].Value+' '+$_.Groups[2].Value } | Group-Object | ForEach-Object {
|
||||
$parts=$_.Name.Split(' '); @{kernel=$parts[0]; backend=$parts[1]; launches=$_.Count}
|
||||
})
|
||||
$capabilities=@([regex]::Matches($Stdout, '(?m)^D4R_CAPABILITIES available=(-?[0-9]+) feature_init=(0x[0-9a-f]+) needs_driver=(-?[0-9]+)\r?$') |
|
||||
ForEach-Object { $_.Groups[2].Value } | Select-Object -Unique)
|
||||
$session=if ($frames.Count) { 'completed-dlss-frames' }
|
||||
elseif ($capabilities -contains '0xbad00004') { 'ngx-feature-not-found' }
|
||||
elseif ($capabilities.Count -and $capabilities -ne '0x00000001') { 'ngx-capability-failure' }
|
||||
elseif ($exitCode -eq '0x0') { 'process-exited-without-dlss-frames' }
|
||||
elseif ($exitCode -eq 'diagnostic_timeout') { 'diagnostic-timeout-without-dlss-frames' }
|
||||
else { 'process-failed-before-dlss-frames' }
|
||||
return [ordered]@{exitCode=$exitCode; session=$session; featureInitResults=$capabilities;
|
||||
runSeconds=$RunSeconds; preset=$Preset; gameCrash=$GameCrash;
|
||||
nativeLaunches=([regex]::Matches($Stderr, '\[d4r-launch\].*backend=native')).Count;
|
||||
completedFrames=$frames.Count; outputValidationRequested=$ValidateOutput;
|
||||
outputGpuChecks=$checks.Count; kernels=$kernels;
|
||||
nonfiniteOutputs=@($checks | Where-Object { $_.Groups[2].Value -ne '0' -or $_.Groups[3].Value -ne '0' }).Count;
|
||||
previousFrameOutputs=@($frames | Where-Object { $_.Groups[2].Value -ne '0' }).Count;
|
||||
cpuImageCopyFrames=@($frames | Where-Object { $_.Groups[1].Value -ne '0' }).Count;
|
||||
failures=([regex]::Matches($Stderr, 'D4R_[A-Z0-9_]*FAILURE\b')).Count}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
# Read-only inventory before touching game files. It allocates no GPU memory
|
||||
# and executes no kernels; successful discovery is not hardware validation.
|
||||
function Invoke-D4RGpuPreflight([string]$PackageRoot, [string]$GameDirectory, [string]$Target, [string]$OutputDirectory) {
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$result=[ordered]@{passed=$false; target=$Target; gpuWorkExecuted=$false; failure=$null}
|
||||
try {
|
||||
$proxy=Join-Path $GameDirectory 'd3d12.dll'
|
||||
if (Test-Path -LiteralPath $proxy) {
|
||||
$item=Get-Item -LiteralPath $proxy
|
||||
$result.localD3D12=@{path=$proxy; sha256=(Get-FileHash -LiteralPath $proxy).Hash; product=$item.VersionInfo.ProductName}
|
||||
# Identify known Vulkan/Wine proxies by their own strings. Do not
|
||||
# load, remove or replace a third-party game DLL.
|
||||
if ($item.Length -le 64MB -and [Text.Encoding]::ASCII.GetString([IO.File]::ReadAllBytes($proxy)) -match '(?i)vkd3d|wined3d') {
|
||||
throw 'Local d3d12.dll identifies as vkd3d/Wine. Restore the game native D3D12 installation before using this Windows backend.'
|
||||
}
|
||||
}
|
||||
$info=[Diagnostics.ProcessStartInfo]::new()
|
||||
$info.FileName=Join-Path $PackageRoot 'd4r/d4r_gpu_inventory.exe'
|
||||
$hip=Join-Path $PackageRoot 'd4r/hip'
|
||||
foreach ($value in @($hip,$OutputDirectory)) { if ($value.Contains('"')) { throw 'Unsupported quote in inventory path.' } }
|
||||
$info.Arguments='--hip-root "' + $hip + '" --output-dir "' + $OutputDirectory + '"'
|
||||
$info.UseShellExecute=$false; $info.CreateNoWindow=$true
|
||||
$info.EnvironmentVariables['D4R_DIAG_DIR']=$OutputDirectory
|
||||
$info.RedirectStandardOutput=$true; $info.RedirectStandardError=$true
|
||||
$process=[Diagnostics.Process]::new(); $process.StartInfo=$info
|
||||
try {
|
||||
if (!$process.Start()) { throw 'Cannot start GPU inventory.' }
|
||||
$stdout=$process.StandardOutput.ReadToEndAsync(); $stderr=$process.StandardError.ReadToEndAsync()
|
||||
$timedOut=!$process.WaitForExit(30000)
|
||||
if ($timedOut) { $process.Kill(); $process.WaitForExit() }
|
||||
$stdout.GetAwaiter().GetResult() | Set-Content (Join-Path $OutputDirectory 'gpu-inventory.stdout.log') -Encoding UTF8
|
||||
$stderr.GetAwaiter().GetResult() | Set-Content (Join-Path $OutputDirectory 'gpu-inventory.stderr.log') -Encoding UTF8
|
||||
$result.exitCode=$process.ExitCode
|
||||
$result.exceptionOrExitHex='0x{0:X8}' -f [BitConverter]::ToUInt32([BitConverter]::GetBytes([int]$process.ExitCode),0)
|
||||
if ($timedOut) { $result.timedOut=$true; throw 'HIP inventory timed out after 30 seconds.' }
|
||||
if ($process.ExitCode) { throw "HIP inventory failed (exit $($process.ExitCode))." }
|
||||
} finally { $process.Dispose() }
|
||||
$inventory=Get-Content (Join-Path $OutputDirectory 'gpu-inventory.json') -Raw | ConvertFrom-Json
|
||||
$result.inventory=$inventory
|
||||
if ($inventory.compiledTarget -ne $Target) { throw "Inventory executable targets $($inventory.compiledTarget); package requires $Target." }
|
||||
$matching=@($inventory.devices | Where-Object { $_.architecture -eq $Target -and $_.wavefrontSize -eq 32 })
|
||||
if (!$matching.Count) {
|
||||
$actual=@($inventory.devices | ForEach-Object { "$($_.architecture) ($($_.name))" }) -join ', '
|
||||
throw "GPU target mismatch: detected $actual; package requires $Target. Use the package for the actual HIP architecture; do not rename code objects."
|
||||
}
|
||||
$result.passed=$true
|
||||
Write-Host "D4R_PREFLIGHT target=$Target devices=$($matching.Count) gpu_work_executed=0"
|
||||
} catch {
|
||||
$result.failure=$_.Exception.Message
|
||||
$result | ConvertTo-Json -Depth 8 | Set-Content (Join-Path $OutputDirectory 'preflight.json') -Encoding UTF8
|
||||
try { Get-CimInstance Win32_VideoController | Select-Object Name,DriverVersion,DriverDate | ConvertTo-Json |
|
||||
Set-Content (Join-Path $OutputDirectory 'driver.json') -Encoding UTF8 } catch {}
|
||||
$bundle=$OutputDirectory+'.zip'
|
||||
Compress-Archive -Path (Join-Path $OutputDirectory '*') -DestinationPath $bundle -Force
|
||||
throw "$($result.failure) Diagnostic bundle: $bundle"
|
||||
}
|
||||
$result | ConvertTo-Json -Depth 8 | Set-Content (Join-Path $OutputDirectory 'preflight.json') -Encoding UTF8
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
# Shared by the build, diagnostics, staging and game package scripts.
|
||||
function Get-D4RGpuTargets {
|
||||
$registry=Join-Path $PSScriptRoot 'gpu-targets.json'
|
||||
if (!(Test-Path -LiteralPath $registry)) { $registry=Join-Path $PSScriptRoot '../../tools/windows/gpu-targets.json' }
|
||||
$data=Get-Content -LiteralPath $registry -Raw | ConvertFrom-Json
|
||||
if ($data.schema -ne 1) { throw 'Unsupported GPU target registry schema.' }
|
||||
return $data.targets
|
||||
}
|
||||
function Get-D4RGpuTargetInfo([string]$Target) {
|
||||
$info=@(Get-D4RGpuTargets | Where-Object { $_.architecture -eq $Target })
|
||||
if ($info.Count -ne 1) { throw "Unsupported GPU target: $Target" }
|
||||
return $info[0]
|
||||
}
|
||||
function Get-D4RGpuTarget([string]$Root, [string]$Requested) {
|
||||
$configured=$null
|
||||
$targetFile=Join-Path $Root 'gpu-target.json'
|
||||
if (Test-Path -LiteralPath $targetFile) {
|
||||
$configured=(Get-Content -LiteralPath $targetFile -Raw | ConvertFrom-Json).architecture
|
||||
[void](Get-D4RGpuTargetInfo $configured)
|
||||
}
|
||||
$target=if ($Requested) { $Requested } elseif ($configured) { $configured } else { 'gfx1201' }
|
||||
[void](Get-D4RGpuTargetInfo $target)
|
||||
if (!$configured -and $target -ne 'gfx1201') { throw "A $target build requires gpu-target.json; rebuild with -GpuArch $target." }
|
||||
if ($configured -and $target -ne $configured) { throw "Requested $target differs from package target $configured" }
|
||||
return $target
|
||||
}
|
||||
function Assert-D4RCodeObjectTarget([string]$Path, [string]$Target) {
|
||||
$info=Get-D4RGpuTargetInfo $Target
|
||||
$reader=[IO.BinaryReader]::new([IO.File]::OpenRead($Path))
|
||||
try { $header=$reader.ReadBytes(64) } finally { $reader.Dispose() }
|
||||
if ($header.Length -ne 64 -or $header[0] -ne 127 -or $header[1] -ne 69 -or $header[2] -ne 76 -or $header[3] -ne 70 -or
|
||||
$header[4] -ne 2 -or $header[5] -ne 1 -or $header[18] -ne 224 -or $header[19] -ne 0) {
|
||||
throw "Invalid AMDGPU ELF64 code object: $Path"
|
||||
}
|
||||
# LLVM AMDGPU e_flags machine IDs; inspect, never patch or spoof them.
|
||||
$expected=$info.elfMachine
|
||||
if ($header[48] -ne $expected) { throw "Code object does not target ${Target}: $Path (ELF machine=$($header[48]))" }
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$PackageRoot = $PSScriptRoot,
|
||||
[string]$CacheRoot = (Join-Path $env:LOCALAPPDATA 'd4r/cache'),
|
||||
[string]$BackupRoot = (Join-Path $env:LOCALAPPDATA 'd4r/backups'),
|
||||
[switch]$Yes
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$source = [IO.Path]::GetFullPath($PackageRoot)
|
||||
$runtime = if (Test-Path -LiteralPath (Join-Path $source 'd4r/d4r.ini')) {
|
||||
Join-Path $source 'd4r'
|
||||
} elseif (Test-Path -LiteralPath (Join-Path $source 'd4r.ini')) {
|
||||
$source
|
||||
} else {
|
||||
throw "No d4r runtime found under $source. Extract the Windows release first."
|
||||
}
|
||||
$runtime = [IO.Path]::GetFullPath($runtime)
|
||||
$CacheRoot = [IO.Path]::GetFullPath($CacheRoot)
|
||||
$BackupRoot = [IO.Path]::GetFullPath($BackupRoot)
|
||||
$gameRoot = if ($runtime -eq $source) { Split-Path -Parent $source } else { $source }
|
||||
$gameRoot = [IO.Path]::GetFullPath($gameRoot)
|
||||
$backupRoot = Join-Path $BackupRoot ('dlss-install-' + [DateTime]::UtcNow.ToString('yyyyMMdd-HHmmss') + '-' + [Guid]::NewGuid().ToString('N'))
|
||||
foreach ($path in @($CacheRoot, $BackupRoot)) {
|
||||
if ($path.Equals($gameRoot, [StringComparison]::OrdinalIgnoreCase) -or
|
||||
$path.StartsWith($gameRoot.TrimEnd('\') + '\', [StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "Cache and backups must stay outside the game directory: $path"
|
||||
}
|
||||
}
|
||||
|
||||
$dlssUrl = 'https://raw.githubusercontent.com/NVIDIA/DLSS/374959484e79a640feaba44c93ac8cfb0a03f5b5/lib/Windows_x86_64/rel/nvngx_dlss.dll'
|
||||
$dlssHash = '3975567b8943c53acce397f2b72380092f84f162d00b0d2c7d08a1025c563983'
|
||||
$dlssdUrl = 'https://raw.githubusercontent.com/NVIDIA/DLSS/374959484e79a640feaba44c93ac8cfb0a03f5b5/lib/Windows_x86_64/rel/nvngx_dlssd.dll'
|
||||
$dlssdHash = '4bc7ea5fcb2f32cf86bc2cb072e8d2860914a0be511cbdcb2fc206c1d9d83b80'
|
||||
$driverUrl = 'https://us.download.nvidia.com/Windows/617.14/617.14-desktop-win10-win11-64bit-international-dch-whql.exe'
|
||||
$driverHash = '63d3da5ed52547bf3fd86204bb9ea1455947b24e2aa2519f359126f9a6efe13f'
|
||||
$ngxHash = '66767018c36b3bab46398dade3adf173daa3730fda75965689ea848c9bc4e79b'
|
||||
$sevenZipUrl = 'https://github.com/ip7z/7zip/releases/download/26.04/7zr.exe'
|
||||
$sevenZipHash = '256feca8e274e5da655e2a284fabafd9f554365eb164862089dacd4e8276d282'
|
||||
|
||||
function Get-Sha256([string]$Path) {
|
||||
$sha = [Security.Cryptography.SHA256]::Create()
|
||||
$stream = [IO.File]::OpenRead($Path)
|
||||
try { return [BitConverter]::ToString($sha.ComputeHash($stream)).Replace('-', '').ToLowerInvariant() }
|
||||
finally { $stream.Dispose(); $sha.Dispose() }
|
||||
}
|
||||
|
||||
$pinPath = Join-Path $runtime 'dll-pins.txt'
|
||||
if (!(Test-Path -LiteralPath $pinPath -PathType Leaf)) { throw "Missing $pinPath. Use a current d4r Windows release." }
|
||||
$pins = Get-Content -LiteralPath $pinPath -Raw
|
||||
foreach ($required in @(@{kind='dlss'; hash=$dlssHash}, @{kind='dlssd'; hash=$dlssdHash}, @{kind='ngx'; hash=$ngxHash})) {
|
||||
if ($pins -notmatch ('(?im)^\s*' + [regex]::Escape($required.kind) + '\s+' + [regex]::Escape($required.hash) + '\s+\S+\s+\S+\s*$')) {
|
||||
throw "This release does not recognize NVIDIA $($required.kind) hash $($required.hash). Update the d4r package first."
|
||||
}
|
||||
}
|
||||
|
||||
if (!$Yes) {
|
||||
$answer = Read-Host 'Download DLSS 310.9.1, Ray Reconstruction 310.9.1, and NVIDIA NGX core from driver 617.14 (~945 MiB)? [Y/n]'
|
||||
if ($answer -match '^(?i)n') { Write-Host 'No files changed.'; exit 0 }
|
||||
}
|
||||
|
||||
$tempRoot = [IO.Path]::GetFullPath([IO.Path]::GetTempPath())
|
||||
$work = Join-Path $tempRoot ('d4r-dlss-install-' + [Guid]::NewGuid().ToString('N'))
|
||||
$work = [IO.Path]::GetFullPath($work)
|
||||
if (!$work.StartsWith($tempRoot.TrimEnd('\') + '\', [StringComparison]::OrdinalIgnoreCase) -or
|
||||
(Split-Path -Leaf $work) -notmatch '^d4r-dlss-install-[a-f0-9]{32}$') {
|
||||
throw "Temporary work path escaped the expected temp folder: $work"
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $work | Out-Null
|
||||
$cacheNvidia = Join-Path $CacheRoot 'nvidia'
|
||||
$cacheTools = Join-Path $CacheRoot 'tools'
|
||||
New-Item -ItemType Directory -Force -Path $cacheNvidia,$cacheTools | Out-Null
|
||||
|
||||
function Get-VerifiedFile([string]$Name, [string]$Url, [string]$ExpectedHash, [string]$CacheDirectory, [string]$WorkDirectory) {
|
||||
$cached = Join-Path $CacheDirectory $Name
|
||||
if (Test-Path -LiteralPath $cached -PathType Leaf) {
|
||||
$hash = Get-Sha256 $cached
|
||||
if ($hash -eq $ExpectedHash) { return $cached }
|
||||
$quarantine = "$cached.bad-$([DateTime]::UtcNow.ToString('yyyyMMdd-HHmmss'))"
|
||||
Move-Item -LiteralPath $cached -Destination $quarantine
|
||||
Write-Warning "Moved invalid cache file to $quarantine"
|
||||
}
|
||||
$download = Join-Path $WorkDirectory $Name
|
||||
Write-Host "Downloading $Name from its pinned official source..."
|
||||
Invoke-WebRequest -UseBasicParsing -Uri $Url -OutFile $download
|
||||
$hash = Get-Sha256 $download
|
||||
if ($hash -ne $ExpectedHash) { throw "SHA256 mismatch for $Name; no runtime files changed." }
|
||||
$staged = "$cached.tmp-$([Guid]::NewGuid().ToString('N'))"
|
||||
Copy-Item -LiteralPath $download -Destination $staged
|
||||
Move-Item -LiteralPath $staged -Destination $cached
|
||||
return $cached
|
||||
}
|
||||
|
||||
$changed = [Collections.Generic.List[object]]::new()
|
||||
try {
|
||||
$dlssCached = Get-VerifiedFile 'nvngx_dlss.dll' $dlssUrl $dlssHash $cacheNvidia $work
|
||||
$dlssdCached = Get-VerifiedFile 'nvngx_dlssd.dll' $dlssdUrl $dlssdHash $cacheNvidia $work
|
||||
$ngxCache = Join-Path $cacheNvidia '_nvngx.dll'
|
||||
$validCoreCache = (Test-Path -LiteralPath $ngxCache -PathType Leaf) -and
|
||||
(Get-Sha256 $ngxCache) -eq $ngxHash
|
||||
if ($validCoreCache -and (Get-Item -LiteralPath $ngxCache).VersionInfo.FileVersion -ne '32.0.16.1714') {
|
||||
$validCoreCache = $false
|
||||
}
|
||||
if (!$validCoreCache) {
|
||||
if (Test-Path -LiteralPath $ngxCache -PathType Leaf) {
|
||||
$bad = "$ngxCache.bad-$([DateTime]::UtcNow.ToString('yyyyMMdd-HHmmss'))"
|
||||
Move-Item -LiteralPath $ngxCache -Destination $bad
|
||||
Write-Warning "Moved invalid cache file to $bad"
|
||||
}
|
||||
$driverPath = Join-Path $work 'nvidia-driver-617.14.exe'
|
||||
Write-Host 'Downloading NVIDIA driver 617.14 (~945 MiB); installer will not run.'
|
||||
Invoke-WebRequest -UseBasicParsing -Uri $driverUrl -OutFile $driverPath
|
||||
$driverActual = Get-Sha256 $driverPath
|
||||
if ($driverActual -ne $driverHash) { throw 'NVIDIA driver archive SHA256 mismatch; no runtime files changed.' }
|
||||
$sevenZip = Get-VerifiedFile '7zr-26.04.exe' $sevenZipUrl $sevenZipHash $cacheTools $work
|
||||
$extract = Join-Path $work 'ngx'
|
||||
New-Item -ItemType Directory -Force -Path $extract | Out-Null
|
||||
& $sevenZip e -y "-o$extract" $driverPath 'Display.Driver/_nvngx.dll' | Out-Host
|
||||
if ($LASTEXITCODE -ne 0) { throw "7-Zip extraction failed: exit=$LASTEXITCODE" }
|
||||
$extractedNgx = Join-Path $extract '_nvngx.dll'
|
||||
if (!(Test-Path -LiteralPath $extractedNgx -PathType Leaf)) { throw 'NVIDIA driver archive has no Display.Driver/_nvngx.dll.' }
|
||||
$actual = Get-Sha256 $extractedNgx
|
||||
if ($actual -ne $ngxHash) { throw "Extracted NGX core SHA256 mismatch: $actual" }
|
||||
$version = (Get-Item -LiteralPath $extractedNgx).VersionInfo.FileVersion
|
||||
if ($version -ne '32.0.16.1714') { throw "Unexpected NGX core version $version; expected 32.0.16.1714." }
|
||||
$stagedNgx = "$ngxCache.tmp-$([Guid]::NewGuid().ToString('N'))"
|
||||
Copy-Item -LiteralPath $extractedNgx -Destination $stagedNgx
|
||||
Move-Item -LiteralPath $stagedNgx -Destination $ngxCache
|
||||
}
|
||||
|
||||
$assets = @(
|
||||
@{ source=$dlssCached; relative='dlss/nvngx_dlss.dll'; hash=$dlssHash },
|
||||
@{ source=$dlssdCached; relative='rr/nvngx_dlssd.dll'; hash=$dlssdHash },
|
||||
@{ source=$ngxCache; relative='ngx/_nvngx.dll'; hash=$ngxHash }
|
||||
)
|
||||
$originals = [Collections.Generic.List[object]]::new()
|
||||
foreach ($asset in $assets) {
|
||||
$destination = Join-Path $runtime $asset.relative
|
||||
if (Test-Path -LiteralPath $destination -PathType Container) { throw "DLL destination is a directory: $destination" }
|
||||
$present = Test-Path -LiteralPath $destination -PathType Leaf
|
||||
$previousHash = if ($present) { Get-Sha256 $destination } else { $null }
|
||||
if ($previousHash -eq $asset.hash) { continue }
|
||||
$backup = $null
|
||||
if ($present) {
|
||||
$backup = Join-Path $backupRoot $asset.relative
|
||||
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $backup) | Out-Null
|
||||
Copy-Item -LiteralPath $destination -Destination $backup
|
||||
$backupHash = Get-Sha256 $backup
|
||||
if ($backupHash -ne $previousHash) { throw "Backup hash mismatch for $destination; no runtime files changed." }
|
||||
}
|
||||
$originals.Add(@{path=$destination; existed=$present; sha256=$previousHash; backup=$backup; installHash=$asset.hash})
|
||||
}
|
||||
if ($originals.Count) {
|
||||
New-Item -ItemType Directory -Force -Path $backupRoot | Out-Null
|
||||
$changed = $originals
|
||||
@{ createdUtc=[DateTime]::UtcNow.ToString('o'); files=$originals } |
|
||||
ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $backupRoot 'backup-manifest.json') -Encoding UTF8
|
||||
foreach ($entry in $originals) {
|
||||
$asset = $assets | Where-Object { (Join-Path $runtime $_.relative) -eq $entry.path } | Select-Object -First 1
|
||||
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $entry.path) | Out-Null
|
||||
$stillPresent = Test-Path -LiteralPath $entry.path -PathType Leaf
|
||||
if ($entry.existed) {
|
||||
if (!$stillPresent -or (Get-Sha256 $entry.path) -ne $entry.sha256) {
|
||||
throw "Destination changed after backup; refusing to replace $($entry.path)"
|
||||
}
|
||||
} elseif (Test-Path -LiteralPath $entry.path) {
|
||||
throw "Destination appeared after validation; refusing to replace $($entry.path)"
|
||||
}
|
||||
$staged = "$($entry.path).install-$([Guid]::NewGuid().ToString('N'))"
|
||||
Copy-Item -LiteralPath $asset.source -Destination $staged
|
||||
Move-Item -LiteralPath $staged -Destination $entry.path -Force
|
||||
}
|
||||
Write-Host "Backups and original hashes: $backupRoot"
|
||||
} else {
|
||||
Write-Host 'All three NVIDIA DLLs already match their pinned hashes.'
|
||||
}
|
||||
Write-Host 'Installed NVIDIA DLSS 310.9.1, Ray Reconstruction 310.9.1, and NGX 32.0.16.1714.'
|
||||
Write-Host 'Files remain local under the applicable NVIDIA licenses; no binaries are bundled into d4r releases.'
|
||||
Write-Host 'This installer does not build local K/M/L or Ray Reconstruction model data.'
|
||||
} catch {
|
||||
for ($i = $changed.Count - 1; $i -ge 0; $i--) {
|
||||
$entry = $changed[$i]
|
||||
if (!(Test-Path -LiteralPath $entry.path)) { continue }
|
||||
if ($entry.existed -and $entry.backup) {
|
||||
$currentHash = Get-Sha256 $entry.path
|
||||
if ($currentHash -ne $entry.installHash -and $currentHash -ne $entry.sha256) {
|
||||
Write-Warning "Rollback left a file changed by another process: $($entry.path)"
|
||||
continue
|
||||
}
|
||||
$rollback = "$($entry.path).rollback-$([Guid]::NewGuid().ToString('N'))"
|
||||
Copy-Item -LiteralPath $entry.backup -Destination $rollback
|
||||
Move-Item -LiteralPath $rollback -Destination $entry.path -Force
|
||||
} elseif ((Get-Sha256 $entry.path) -eq $entry.installHash) {
|
||||
Remove-Item -LiteralPath $entry.path
|
||||
}
|
||||
}
|
||||
throw
|
||||
} finally {
|
||||
$resolvedWork = [IO.Path]::GetFullPath($work)
|
||||
if (!$resolvedWork.StartsWith($tempRoot.TrimEnd('\') + '\', [StringComparison]::OrdinalIgnoreCase) -or
|
||||
(Split-Path -Leaf $resolvedWork) -notmatch '^d4r-dlss-install-[a-f0-9]{32}$') {
|
||||
throw "Refusing to remove a path outside the installer temp folder: $resolvedWork"
|
||||
}
|
||||
Remove-Item -LiteralPath $resolvedWork -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$PackageRoot, [string]$DiagnosticRoot, [string]$OptiScalerRoot,
|
||||
[string]$ZludaRoot, [string]$HipRoot, [string]$KernelKRoot, [string]$KernelMRoot,
|
||||
[string]$GpuArch,
|
||||
[string]$ArchivePath
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
$defaults = @{PackageRoot='dist/windows-rdna4-game'; DiagnosticRoot='dist/windows-rdna4-command-list';
|
||||
OptiScalerRoot='dist/optiscaler-windows-d4r'; ZludaRoot='dist/zluda-windows-final';
|
||||
HipRoot='.tools/therock-10.2.0a20260929/_rocm_sdk_core'}
|
||||
foreach ($name in $defaults.Keys) {
|
||||
if (!(Get-Variable -Name $name -ValueOnly)) { Set-Variable -Name $name -Value (Join-Path $repo $defaults[$name]) }
|
||||
}
|
||||
$GpuArch=Get-D4RGpuTarget $DiagnosticRoot $GpuArch
|
||||
if ($GpuArch -ne 'gfx1201' -and !$PSBoundParameters.ContainsKey('PackageRoot')) { $PackageRoot += "-$GpuArch" }
|
||||
if (!$KernelKRoot) { $KernelKRoot=Join-Path $repo "build/native-k-$GpuArch" }
|
||||
if (!$KernelMRoot) { $KernelMRoot=Join-Path $repo "build/native-m-$GpuArch" }
|
||||
foreach ($directory in @($KernelKRoot,$KernelMRoot)) {
|
||||
$info=Join-Path $directory 'build-info.json'
|
||||
if ((Test-Path -LiteralPath $info) -and (Get-Content -LiteralPath $info -Raw | ConvertFrom-Json).architecture -ne $GpuArch) {
|
||||
throw "Native kernel directory does not target ${GpuArch}: $directory"
|
||||
}
|
||||
}
|
||||
$PackageRoot = [IO.Path]::GetFullPath($PackageRoot)
|
||||
if (Test-Path -LiteralPath (Join-Path $PackageRoot 'gpu-target.json')) {
|
||||
[void](Get-D4RGpuTarget $PackageRoot $GpuArch)
|
||||
}
|
||||
$sourceCommit=(& git -C $repo rev-parse HEAD).Trim()
|
||||
$sourceDirty=[bool](& git -C $repo status --porcelain)
|
||||
if ($ArchivePath -and $sourceDirty) { throw 'Commit source changes before creating the distributable archive.' }
|
||||
New-Item -ItemType Directory -Force $PackageRoot | Out-Null
|
||||
$stagedFiles = [Collections.Generic.List[string]]::new()
|
||||
function Stage([string]$source, [string]$relative) {
|
||||
if (!(Test-Path -LiteralPath $source -PathType Leaf)) { throw "Required file missing: $source" }
|
||||
if ($source.EndsWith('.hsaco')) { Assert-D4RCodeObjectTarget $source $GpuArch }
|
||||
$destination = Join-Path $PackageRoot $relative
|
||||
New-Item -ItemType Directory -Force (Split-Path $destination) | Out-Null
|
||||
Copy-Item -LiteralPath $source -Destination $destination -Force
|
||||
$stagedFiles.Add($relative)
|
||||
}
|
||||
Stage (Join-Path $OptiScalerRoot 'OptiScaler.dll') 'OptiScaler.dll'
|
||||
Stage (Join-Path $DiagnosticRoot 'bin/d4r_nvngx.dll') 'd4r/_nvngx.dll'
|
||||
Stage (Join-Path $DiagnosticRoot "bin/pixel_convert_${GpuArch}.hsaco") "d4r/pixel_convert_${GpuArch}.hsaco"
|
||||
Stage (Join-Path $DiagnosticRoot 'nvapi-compat/nvapi64.dll') 'd4r/nvapi/nvapi64.dll'
|
||||
Stage (Join-Path $DiagnosticRoot 'nvapi-compat/nvapi64.dll') 'd4r/zluda/nvapi64.dll'
|
||||
Stage (Join-Path $DiagnosticRoot 'bin/d4r_debug_launcher.exe') 'd4r/d4r_debug_launcher.exe'
|
||||
Stage (Join-Path $DiagnosticRoot 'bin/d4r_gpu_inventory.exe') 'd4r/d4r_gpu_inventory.exe'
|
||||
foreach ($file in @('nvcuda.dll','libc++.dll','libunwind.dll')) { Stage (Join-Path $ZludaRoot $file) "d4r/zluda/$file" }
|
||||
Stage (Join-Path $ZludaRoot 'nvapi64.dll') 'd4r/nvapi/backend/nvapi64.dll'
|
||||
foreach ($file in @('amdhip64_7.dll','amd_comgr.dll','hiprtc0717.dll','hiprtc-builtins0717.dll','rocm_kpack.dll','AMD.ROCM.Comgr.MANIFEST','.hipVersion')) {
|
||||
Stage (Join-Path $HipRoot "bin/$file") "d4r/hip/bin/$file"
|
||||
}
|
||||
$entries = @{}
|
||||
foreach ($directory in @($KernelKRoot, $KernelMRoot)) {
|
||||
foreach ($line in Get-Content -LiteralPath (Join-Path $directory 'd4r-kernels.txt')) {
|
||||
if (!$line.Trim() -or $line.StartsWith('#')) { continue }
|
||||
if ($line -notmatch '^([A-Za-z0-9_]+) ([0-9a-f]{16})$') { throw "Invalid native manifest entry: $line" }
|
||||
if ($entries.ContainsKey($Matches[1])) { throw "Duplicate native kernel: $($Matches[1])" }
|
||||
$entries[$Matches[1]] = $line
|
||||
Stage (Join-Path $directory ($Matches[1] + '.hsaco')) ('d4r/native/' + $Matches[1] + '.hsaco')
|
||||
}
|
||||
}
|
||||
if ($entries.Count -ne 16) { throw "Expected 11 K and 5 M native objects; found $($entries.Count)" }
|
||||
@("# Audited DLSS 310.9.1: K + M $GpuArch native kernels", '# Generated by package-windows-game.ps1') + @($entries.Values | Sort-Object) |
|
||||
Set-Content -LiteralPath (Join-Path $PackageRoot 'd4r/native/d4r-kernels.txt') -Encoding ASCII
|
||||
$stagedFiles.Add('d4r/native/d4r-kernels.txt')
|
||||
Stage (Join-Path $repo 'scripts/windows/windows-game.ps1') 'windows-game.ps1'
|
||||
Stage (Join-Path $repo 'scripts/windows/game-summary.ps1') 'game-summary.ps1'
|
||||
Stage (Join-Path $repo 'scripts/windows/gpu-target.ps1') 'gpu-target.ps1'
|
||||
Stage (Join-Path $repo 'scripts/windows/gpu-preflight.ps1') 'gpu-preflight.ps1'
|
||||
Stage (Join-Path $repo 'tools/windows/gpu-targets.json') 'gpu-targets.json'
|
||||
if (Test-Path -LiteralPath (Join-Path $DiagnosticRoot 'gpu-target.json')) {
|
||||
Stage (Join-Path $DiagnosticRoot 'gpu-target.json') 'gpu-target.json'
|
||||
} else {
|
||||
# Builds predating target metadata were gfx1201 only. The resolver rejects
|
||||
# any attempt to label such a build gfx1200.
|
||||
@{schema=1; architecture=$GpuArch; wavefrontSize=32; validation='Legacy gfx1201 build; run the GPU diagnostics.'} |
|
||||
ConvertTo-Json | Set-Content -LiteralPath (Join-Path $PackageRoot 'gpu-target.json') -Encoding UTF8
|
||||
$stagedFiles.Add('gpu-target.json')
|
||||
}
|
||||
Stage (Join-Path $repo 'scripts/windows/capture-game-window.ps1') 'capture-game-window.ps1'
|
||||
Stage (Join-Path $repo 'scripts/windows/send-game-key.ps1') 'send-game-key.ps1'
|
||||
Stage (Join-Path $repo 'docs/windows-game.md') 'README.md'
|
||||
Stage (Join-Path $repo 'docs/windows-performance.md') 'windows-performance.md'
|
||||
Stage (Join-Path $repo 'docs/windows-gpu-support.md') 'windows-gpu-support.md'
|
||||
Stage (Join-Path $repo 'docs/windows-rdna4-port.md') 'windows-rdna4-port.md'
|
||||
Stage (Join-Path $repo 'packaging/windows/d4r.ini') 'd4r/d4r.ini'
|
||||
Stage (Join-Path $repo 'tools/windows/dll-pins.txt') 'd4r/dll-pins.txt'
|
||||
Stage (Join-Path $repo 'LICENSE') 'licenses/d4r.txt'
|
||||
Stage (Join-Path $OptiScalerRoot 'GPL-3.0.txt') 'licenses/OptiScaler.txt'
|
||||
Stage (Join-Path $repo 'external/ZLUDA/LICENSE-MIT') 'licenses/ZLUDA-MIT.txt'
|
||||
Stage (Join-Path $repo 'external/ZLUDA/LICENSE-APACHE') 'licenses/ZLUDA-APACHE.txt'
|
||||
Stage (Join-Path $repo 'external/ZLUDA/ext/llvm-project/LICENSE.TXT') 'licenses/LLVM.txt'
|
||||
Stage (Join-Path $repo 'external/MinHook/LICENSE.txt') 'licenses/MinHook.txt'
|
||||
Stage (Join-Path $HipRoot 'share/doc/amd_comgr/LICENSE.txt') 'licenses/amd_comgr.txt'
|
||||
Stage (Join-Path $repo 'external/clr/LICENSE.md') 'licenses/HIP-CLR.txt'
|
||||
foreach ($patch in Get-ChildItem -LiteralPath (Join-Path $repo 'patches/optiscaler') -Filter '*.patch') { Stage $patch.FullName ('source-patches/optiscaler/' + $patch.Name) }
|
||||
foreach ($patch in Get-ChildItem -LiteralPath (Join-Path $repo 'patches/zluda') -Filter '*.patch') { Stage $patch.FullName ('source-patches/zluda/' + $patch.Name) }
|
||||
foreach ($patch in Get-ChildItem -LiteralPath (Join-Path $repo 'patches/zluda-llvm') -Filter '*.patch') { Stage $patch.FullName ('source-patches/zluda-llvm/' + $patch.Name) }
|
||||
$sourceArchive=Join-Path $repo 'build/d4r-windows-rdna4-source.zip'
|
||||
New-Item -ItemType Directory -Force (Split-Path $sourceArchive) | Out-Null
|
||||
& git -C $repo archive --format=zip --output $sourceArchive $sourceCommit
|
||||
if ($LASTEXITCODE) { throw 'Cannot archive the committed d4r source.' }
|
||||
Stage $sourceArchive 'source/d4r-windows-rdna4-source.zip'
|
||||
$mBuildInfoPath=Join-Path $KernelMRoot 'build-info.json'
|
||||
$nativeFp8M=$false; $tokenLanesM=$false
|
||||
if (Test-Path -LiteralPath $mBuildInfoPath) {
|
||||
$mBuildInfo=Get-Content -LiteralPath $mBuildInfoPath -Raw | ConvertFrom-Json
|
||||
if ($mBuildInfo.architecture -ne $GpuArch -or $mBuildInfo.family -ne 'M') { throw "M build metadata does not match ${GpuArch}: $mBuildInfoPath" }
|
||||
$nativeFp8M=[bool]$mBuildInfo.nativeFp8
|
||||
$tokenLanesM=[bool]$mBuildInfo.tokenLanes
|
||||
}
|
||||
if ($nativeFp8M) {
|
||||
if (!$tokenLanesM -or $GpuArch -notmatch '^gfx12') { throw 'Native FP8 M packages require the gfx12 token-lane build.' }
|
||||
$marker=Join-Path $PackageRoot 'd4r/native/d4r-native-fp8.txt'
|
||||
New-Item -ItemType Directory -Force (Split-Path $marker) | Out-Null
|
||||
@('D4R_NATIVE_FP8 1',"architecture=$GpuArch",'family=M','tokenLanes=true') |
|
||||
Set-Content -LiteralPath $marker -Encoding ASCII
|
||||
$stagedFiles.Add('d4r/native/d4r-native-fp8.txt')
|
||||
}
|
||||
$metadata = [ordered]@{
|
||||
architecture=$GpuArch; hardwareValidation=$(if ($GpuArch -eq 'gfx1201') { 'RX9070XT tested; other SKUs unverified' } else { 'compile-only; hardware unverified' }); nativeKernels=16; dlssSha256='3975567b8943c53acce397f2b72380092f84f162d00b0d2c7d08a1025c563983';
|
||||
nativeFp8M=$nativeFp8M; tokenLanesM=$tokenLanesM;
|
||||
d4rCommit=$sourceCommit;
|
||||
d4rWorkingTreeDirty=$sourceDirty;
|
||||
optiScaler=(Get-Content -LiteralPath (Join-Path $OptiScalerRoot 'build-info.json') -Raw | ConvertFrom-Json);
|
||||
zludaBuild=(Get-Content -LiteralPath (Join-Path $ZludaRoot 'build-info.json') -Raw | ConvertFrom-Json);
|
||||
hipRuntime='TheRock 10.2.0a20260929 / HIP 7.17.26386';
|
||||
files=@($stagedFiles | ForEach-Object { Get-Item -LiteralPath (Join-Path $PackageRoot $_) } | ForEach-Object {
|
||||
@{path=$_.FullName.Substring($PackageRoot.Length+1); sha256=(Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash; version=$_.VersionInfo.FileVersion}
|
||||
})
|
||||
}
|
||||
$metadata | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath (Join-Path $PackageRoot 'package.json') -Encoding UTF8
|
||||
if (Get-ChildItem -LiteralPath $PackageRoot -Recurse -File | Where-Object { $_.Name -eq 'nvngx_dlss.dll' -or ($_.Name -eq '_nvngx.dll' -and $_.DirectoryName -ne (Join-Path $PackageRoot 'd4r')) }) {
|
||||
throw 'Proprietary NVIDIA binaries must not be in the package.'
|
||||
}
|
||||
Write-Host "Windows game development package: $PackageRoot (NVIDIA DLLs excluded)"
|
||||
if ($ArchivePath) {
|
||||
$ArchivePath=[IO.Path]::GetFullPath($ArchivePath)
|
||||
if ($ArchivePath.StartsWith($PackageRoot+'\',[StringComparison]::OrdinalIgnoreCase)) { throw 'Place the ZIP outside the package directory.' }
|
||||
Add-Type -AssemblyName System.IO.Compression,System.IO.Compression.FileSystem
|
||||
New-Item -ItemType Directory -Force (Split-Path $ArchivePath) | Out-Null
|
||||
$stream=[IO.File]::Open($ArchivePath,[IO.FileMode]::Create,[IO.FileAccess]::ReadWrite,[IO.FileShare]::None)
|
||||
$archive=$null
|
||||
try {
|
||||
$archive=[IO.Compression.ZipArchive]::new($stream,[IO.Compression.ZipArchiveMode]::Create)
|
||||
# Never archive caches, results, private texture outputs or a local DLL
|
||||
# merely because it happens to be somewhere under PackageRoot.
|
||||
foreach ($relative in @($stagedFiles)+@('package.json')) {
|
||||
[void][IO.Compression.ZipFileExtensions]::CreateEntryFromFile($archive,
|
||||
(Join-Path $PackageRoot $relative),$relative.Replace('\','/'),[IO.Compression.CompressionLevel]::Optimal)
|
||||
}
|
||||
} finally { if ($archive) { $archive.Dispose() }; $stream.Dispose() }
|
||||
Write-Host "Public package archive: $ArchivePath"
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$GamePackagePrefix, [Parameter(Mandatory=$true)][string]$PackageRoot,
|
||||
[string]$ArchivePath,
|
||||
[string]$NvidiaDirectory, [string]$NvidiaLicensePath
|
||||
)
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$GamePackagePrefix) { $GamePackagePrefix=Join-Path $repo 'dist/windows-gpu-coverage-game' }
|
||||
$PackageRoot=[IO.Path]::GetFullPath($PackageRoot)
|
||||
if (Test-Path -LiteralPath $PackageRoot) { throw 'Use a new output directory to avoid mixing releases.' }
|
||||
$commit=(& git -C $repo rev-parse HEAD).Trim()
|
||||
if ($ArchivePath -and [bool](& git -C $repo status --porcelain)) { throw 'Commit source changes before creating a public ZIP.' }
|
||||
New-Item -ItemType Directory -Force $PackageRoot | Out-Null
|
||||
$files=[Collections.Generic.List[object]]::new()
|
||||
function Stage([string]$Source,[string]$Relative) {
|
||||
$destination=Join-Path $PackageRoot $Relative
|
||||
New-Item -ItemType Directory -Force (Split-Path $destination) | Out-Null
|
||||
Copy-Item -LiteralPath $Source -Destination $destination
|
||||
$item=Get-Item -LiteralPath $destination
|
||||
$entry=@{path=$Relative.Replace('\','/'); sha256=(Get-FileHash -LiteralPath $destination -Algorithm SHA256).Hash; version=$item.VersionInfo.FileVersion}
|
||||
$files.Add($entry)
|
||||
return $entry
|
||||
}
|
||||
$targets=[ordered]@{}; $common=@(); $baseline=$null
|
||||
foreach ($gpu in Get-D4RGpuTargets) {
|
||||
$arch=$gpu.architecture; $source="$GamePackagePrefix-$arch"
|
||||
$metadata=Get-Content -LiteralPath (Join-Path $source 'package.json') -Raw | ConvertFrom-Json
|
||||
if ($metadata.architecture -ne $arch -or $metadata.d4rWorkingTreeDirty) { throw "Uncommitted or wrong-target input: $source" }
|
||||
$targetFiles=@()
|
||||
foreach ($file in $metadata.files) {
|
||||
$relative=$file.path.Replace('\','/')
|
||||
if ((Get-FileHash -LiteralPath (Join-Path $source $file.path) -Algorithm SHA256).Hash -ne $file.sha256) { throw "Input file changed: $source/$relative" }
|
||||
if ($relative.EndsWith('.hsaco')) { Assert-D4RCodeObjectTarget (Join-Path $source $file.path) $arch }
|
||||
if ($relative -eq 'gpu-target.json' -or $relative -eq 'd4r/_nvngx.dll' -or $relative -eq 'd4r/d4r_gpu_inventory.exe' -or $relative -eq 'd4r/d4r_debug_launcher.exe' -or $relative -eq 'd4r/nvapi/nvapi64.dll' -or
|
||||
$relative.StartsWith('d4r/native/') -or $relative -like 'd4r/pixel_convert_*.hsaco') {
|
||||
$entry=Stage (Join-Path $source $file.path) "files/targets/$arch/$relative"
|
||||
$targetFiles+=@{path=$relative; sha256=$entry.sha256; version=$entry.version}
|
||||
} elseif ($relative -eq 'OptiScaler.dll' -or $relative.StartsWith('d4r/') -or $relative.StartsWith('licenses/')) {
|
||||
$publicPath=if ($relative.StartsWith('licenses/')) { $relative } else { "files/common/$relative" }
|
||||
if (!$baseline) {
|
||||
$entry=Stage (Join-Path $source $file.path) $publicPath
|
||||
if (!$relative.StartsWith('licenses/')) { $common+=@{path=$relative; sha256=$entry.sha256; version=$entry.version} }
|
||||
} elseif ((Get-FileHash -LiteralPath (Join-Path $PackageRoot $publicPath) -Algorithm SHA256).Hash -ne $file.sha256) {
|
||||
throw "Shared runtime differs in $arch : $relative"
|
||||
}
|
||||
}
|
||||
}
|
||||
if (@($targetFiles | Where-Object { $_.path.EndsWith('.hsaco') }).Count -ne 17) { throw "Expected 17 code objects for $arch" }
|
||||
$targets[$arch]=@{binarySourceCommit=$metadata.d4rCommit; hardwareValidation=$metadata.hardwareValidation;
|
||||
nativeFp8M=[bool]$metadata.nativeFp8M; tokenLanesM=[bool]$metadata.tokenLanesM;
|
||||
dlssSha256=$metadata.dlssSha256; files=$targetFiles}
|
||||
if (!$baseline) { $baseline=$metadata }
|
||||
if ($metadata.dlssSha256 -ne $baseline.dlssSha256 -or $metadata.zludaBuild.sourceCommit -ne $baseline.zludaBuild.sourceCommit) { throw 'Input packages do not use the same validated dependencies.' }
|
||||
}
|
||||
foreach ($name in @('windows-game.ps1','game-summary.ps1','gpu-target.ps1','gpu-preflight.ps1')) {
|
||||
$entry=Stage (Join-Path $PSScriptRoot $name) "files/common/$name"
|
||||
$common+=@{path=$name; sha256=$entry.sha256; version=$entry.version}
|
||||
}
|
||||
$entry=Stage (Join-Path $repo 'tools/windows/gpu-targets.json') 'files/common/gpu-targets.json'
|
||||
$common+=@{path='gpu-targets.json'; sha256=$entry.sha256; version=$entry.version}
|
||||
[void](Stage (Join-Path $PSScriptRoot 'quick-test.ps1') 'files/quick-test.ps1')
|
||||
[void](Stage (Join-Path $repo 'docs/windows-quick-test.txt') 'READ-ME-FIRST.txt')
|
||||
foreach ($launch in @(@{name='START-E.cmd'; arguments='-Preset 5'},@{name='START-K.cmd'; arguments='-Preset 11'},
|
||||
@{name='START-L.cmd'; arguments='-Preset 12'},@{name='START-M.cmd'; arguments='-Preset 13'},
|
||||
@{name='RESTORE-GAME.cmd'; arguments='-Action restore'})) {
|
||||
$path=Join-Path $PackageRoot $launch.name
|
||||
@('@echo off','cd /d "%~dp0"',('"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -STA -ExecutionPolicy Bypass -File "%~dp0files\quick-test.ps1" '+$launch.arguments),'pause') |
|
||||
Set-Content -LiteralPath $path -Encoding ASCII
|
||||
$files.Add(@{path=$launch.name; sha256=(Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash; version=$null})
|
||||
}
|
||||
$ngxCoreSha256='66767018C36B3BAB46398DADE3ADF173DAA3730FDA75965689EA848C9BC4E79B'
|
||||
$bundledNvidia=$null
|
||||
if ($NvidiaDirectory) {
|
||||
# Opt-in for local testing or a distributor with appropriate permission.
|
||||
# A matching hash establishes binary identity, not redistribution rights.
|
||||
if (!$NvidiaLicensePath -or !(Test-Path -LiteralPath $NvidiaLicensePath -PathType Leaf)) {
|
||||
throw 'Bundled NVIDIA files require -NvidiaLicensePath with their applicable license. This option does not establish redistribution permission.'
|
||||
}
|
||||
$core=Join-Path $NvidiaDirectory '_nvngx.dll'
|
||||
$dlss=Join-Path $NvidiaDirectory 'nvngx_dlss.dll'
|
||||
if ((Get-FileHash -LiteralPath $core -Algorithm SHA256).Hash -ne $ngxCoreSha256 -or
|
||||
(Get-FileHash -LiteralPath $dlss -Algorithm SHA256).Hash -ne $baseline.dlssSha256) {
|
||||
throw 'Bundled NVIDIA files must match the validated original NGX and DLSS hashes.'
|
||||
}
|
||||
[void](Stage $core 'files/nvidia/_nvngx.dll')
|
||||
[void](Stage $dlss 'files/nvidia/nvngx_dlss.dll')
|
||||
[void](Stage $NvidiaLicensePath 'licenses/NVIDIA-RTX.txt')
|
||||
$bundledNvidia=@{ngxCore='nvidia/_nvngx.dll'; dlssDll='nvidia/nvngx_dlss.dll'; license='licenses/NVIDIA-RTX.txt'}
|
||||
} elseif ($NvidiaLicensePath) { throw '-NvidiaLicensePath requires -NvidiaDirectory.' }
|
||||
$metadata=[ordered]@{schema=1; packageCommit=$commit; binarySourceCommit=$baseline.d4rCommit;
|
||||
ngxCoreSha256=$ngxCoreSha256; bundledNvidia=$bundledNvidia;
|
||||
dependencies=@{optiScalerSourceCommit=$baseline.optiScaler.sourceCommit; zludaSourceCommit=$baseline.zludaBuild.sourceCommit;
|
||||
llvmSource=$baseline.zludaBuild.llvmSource; hipRuntime=$baseline.hipRuntime};
|
||||
commonFiles=$common; targets=$targets; files=$files}
|
||||
$metadata | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath (Join-Path $PackageRoot 'files/package.json') -Encoding UTF8
|
||||
if ($ArchivePath) {
|
||||
$ArchivePath=[IO.Path]::GetFullPath($ArchivePath)
|
||||
if ($ArchivePath.StartsWith($PackageRoot+'\',[StringComparison]::OrdinalIgnoreCase)) { throw 'Place the ZIP outside the output directory.' }
|
||||
New-Item -ItemType Directory -Force (Split-Path $ArchivePath) | Out-Null
|
||||
Add-Type -AssemblyName System.IO.Compression,System.IO.Compression.FileSystem
|
||||
$stream=[IO.File]::Open($ArchivePath,[IO.FileMode]::Create,[IO.FileAccess]::ReadWrite,[IO.FileShare]::None)
|
||||
$archive=$null
|
||||
try {
|
||||
$archive=[IO.Compression.ZipArchive]::new($stream,[IO.Compression.ZipArchiveMode]::Create)
|
||||
foreach ($relative in @($files.path)+@('files/package.json')) {
|
||||
[void][IO.Compression.ZipFileExtensions]::CreateEntryFromFile($archive,(Join-Path $PackageRoot $relative),$relative,[IO.Compression.CompressionLevel]::Optimal)
|
||||
}
|
||||
} finally { if ($archive) { $archive.Dispose() }; $stream.Dispose() }
|
||||
}
|
||||
$nvidiaMode=if ($bundledNvidia) { 'bundled NVIDIA files; distributor must have appropriate permission' } else { 'user-supplied NVIDIA files' }
|
||||
Write-Host "Quick-test package: $PackageRoot (11 targets, automatic GPU selection, $nvidiaMode)"
|
||||
@@ -0,0 +1,108 @@
|
||||
# Builds the Windows drag-in release: extract it into the game folder, copy
|
||||
# the two NVIDIA DLLs, edit d4r\d4r.ini. Like the Linux release, no installer
|
||||
# or launcher is involved. Inputs are the per-target game packages that
|
||||
# package-windows-game.ps1 produces (the same inputs as the quick test).
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$GamePackagePrefix,
|
||||
[Parameter(Mandatory=$true)][string]$LoaderDll,
|
||||
[Parameter(Mandatory=$true)][string]$PackageRoot,
|
||||
[string[]]$Architectures, # default: every target in gpu-targets.json; a subset makes a smaller release
|
||||
[string]$ArchivePath
|
||||
)
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$GamePackagePrefix) { $GamePackagePrefix=Join-Path $repo 'dist/windows-gpu-coverage-game' }
|
||||
$PackageRoot=[IO.Path]::GetFullPath($PackageRoot)
|
||||
if (Test-Path -LiteralPath $PackageRoot) { throw 'Use a new output directory to avoid mixing releases.' }
|
||||
$git=Get-Command git -ErrorAction SilentlyContinue
|
||||
$commit=if ($git) { (& git -C $repo rev-parse HEAD).Trim() } else { $null }
|
||||
if ($ArchivePath -and (!$git -or [bool](& git -C $repo status --porcelain))) { throw 'A public ZIP needs git and committed sources.' }
|
||||
New-Item -ItemType Directory -Force $PackageRoot | Out-Null
|
||||
$files=[Collections.Generic.List[object]]::new()
|
||||
function Stage([string]$Source,[string]$Relative) {
|
||||
$destination=Join-Path $PackageRoot $Relative
|
||||
if (Test-Path -LiteralPath $destination) { throw "Duplicate release path: $Relative" }
|
||||
New-Item -ItemType Directory -Force (Split-Path $destination) | Out-Null
|
||||
Copy-Item -LiteralPath $Source -Destination $destination
|
||||
$item=Get-Item -LiteralPath $destination
|
||||
$files.Add(@{path=$Relative.Replace('\','/'); sha256=(Get-FileHash -LiteralPath $destination -Algorithm SHA256).Hash; version=$item.VersionInfo.FileVersion})
|
||||
}
|
||||
$targets=[ordered]@{}; $baseline=$null; $commonStaged=$false; $inventoryStaged=$false
|
||||
$selected=@(Get-D4RGpuTargets | Where-Object { !$Architectures -or $Architectures -contains $_.architecture })
|
||||
if (!$selected.Count -or ($Architectures -and $selected.Count -ne @($Architectures | Select-Object -Unique).Count)) { throw "Unknown GPU target in -Architectures: $($Architectures -join ', ')" }
|
||||
foreach ($gpu in $selected) {
|
||||
$arch=$gpu.architecture; $source="$GamePackagePrefix-$arch"
|
||||
$metadata=Get-Content -LiteralPath (Join-Path $source 'package.json') -Raw | ConvertFrom-Json
|
||||
if ($metadata.architecture -ne $arch -or $metadata.d4rWorkingTreeDirty) { throw "Uncommitted or wrong-target input: $source" }
|
||||
if (!$baseline) { $baseline=$metadata }
|
||||
if ($metadata.dlssSha256 -ne $baseline.dlssSha256 -or $metadata.zludaBuild.sourceCommit -ne $baseline.zludaBuild.sourceCommit) { throw 'Input packages do not use the same validated dependencies.' }
|
||||
$objects=0
|
||||
foreach ($file in $metadata.files) {
|
||||
$relative=$file.path.Replace('\','/'); $path=Join-Path $source $file.path
|
||||
if ((Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash -ne $file.sha256) { throw "Input file changed: $source/$relative" }
|
||||
if ($relative.EndsWith('.hsaco')) { Assert-D4RCodeObjectTarget $path $arch; $objects++ }
|
||||
# Per-target files get target-specific names so one folder serves every GPU.
|
||||
# One GPU-query tool serves every target: it reports each device's real architecture.
|
||||
if ($relative -eq 'd4r/d4r_gpu_inventory.exe') { if (!$inventoryStaged) { Stage $path 'd4r/d4r_gpu_inventory.exe'; $inventoryStaged=$true } }
|
||||
elseif ($relative -eq 'd4r/_nvngx.dll') { Stage $path "d4r/_nvngx_$arch.dll" }
|
||||
elseif ($relative -eq 'd4r/nvapi/nvapi64.dll') { Stage $path "d4r/nvapi/$arch/nvapi64.dll" }
|
||||
elseif ($relative -like 'd4r/pixel_convert_*.hsaco') { Stage $path $relative }
|
||||
elseif ($relative.StartsWith('d4r/native/')) { Stage $path ("d4r/native/$arch/" + $relative.Substring('d4r/native/'.Length)) }
|
||||
elseif ($relative -eq 'd4r/nvapi/backend/nvapi64.dll' -or $relative -eq 'OptiScaler.dll' -or $relative.StartsWith('licenses/') -or
|
||||
($relative.StartsWith('d4r/hip/') -or $relative.StartsWith('d4r/zluda/'))) {
|
||||
$public=if ($relative -eq 'OptiScaler.dll') { 'dxgi.dll' } elseif ($relative.StartsWith('licenses/')) { "d4r/$relative" } else { $relative }
|
||||
if (!$commonStaged) { Stage $path $public }
|
||||
elseif ((Get-FileHash -LiteralPath (Join-Path $PackageRoot $public) -Algorithm SHA256).Hash -ne $file.sha256) { throw "Shared runtime differs in $arch : $relative" }
|
||||
}
|
||||
# Installer-only files (launcher, PowerShell installer, docs) are not part of a drag-in install.
|
||||
}
|
||||
if ($objects -ne 17) { throw "Expected 17 code objects for $arch" }
|
||||
$commonStaged=$true
|
||||
$targets[$arch]=@{binarySourceCommit=$metadata.d4rCommit; hardwareValidation=$metadata.hardwareValidation;
|
||||
nativeFp8M=[bool]$metadata.nativeFp8M; tokenLanesM=[bool]$metadata.tokenLanesM}
|
||||
}
|
||||
$loader=(Get-Item -LiteralPath $LoaderDll).FullName
|
||||
if ([IO.Path]::GetFileName($loader) -ne 'd4r_nvngx_loader.dll') { throw 'Pass the d4r_nvngx_loader.dll built by CMake.' }
|
||||
Stage $loader 'd4r/_nvngx.dll'
|
||||
Stage (Join-Path $repo 'packaging/windows/OptiScaler.ini') 'OptiScaler.ini'
|
||||
Stage (Join-Path $repo 'packaging/windows/d4r.ini') 'd4r/d4r.ini'
|
||||
Stage (Join-Path $repo 'tools/windows/dll-pins.txt') 'd4r/dll-pins.txt'
|
||||
Stage (Join-Path $repo 'tools/windows/gpu-targets.json') 'd4r/gpu-targets.json'
|
||||
if (!$inventoryStaged) { throw 'No d4r/d4r_gpu_inventory.exe in the inputs; d4r-check.ps1 needs it to query the GPU.' }
|
||||
foreach ($optional in @(@{source='packaging/windows/WINDOWS_README.txt'; path='WINDOWS_README.txt'},
|
||||
@{source='packaging/windows/install.bat'; path='install.bat'},
|
||||
@{source='scripts/windows/install-dlss.ps1'; path='install-dlss.ps1'},
|
||||
@{source='scripts/windows/d4r-check.ps1'; path='d4r/d4r-check.ps1'})) {
|
||||
$path=Join-Path $repo $optional.source
|
||||
if (Test-Path -LiteralPath $path) { Stage $path $optional.path }
|
||||
}
|
||||
# Shows players where the NGX core goes; a ZIP cannot carry an empty folder.
|
||||
$placeholder=Join-Path $PackageRoot 'd4r/ngx/PUT _nvngx.dll HERE.txt'
|
||||
New-Item -ItemType Directory -Force (Split-Path $placeholder) | Out-Null
|
||||
"Copy NVIDIA's _nvngx.dll into this folder. See WINDOWS_README.txt." | Set-Content -LiteralPath $placeholder -Encoding ASCII
|
||||
$files.Add(@{path='d4r/ngx/PUT _nvngx.dll HERE.txt'; sha256=(Get-FileHash -LiteralPath $placeholder -Algorithm SHA256).Hash; version=$null})
|
||||
if (Get-ChildItem -LiteralPath $PackageRoot -Recurse -File | Where-Object { $_.Name -eq 'nvngx_dlss.dll' -or ($_.Name -eq '_nvngx.dll' -and $_.DirectoryName -ne (Join-Path $PackageRoot 'd4r')) }) {
|
||||
throw 'Proprietary NVIDIA binaries must not be in the release.'
|
||||
}
|
||||
# Read by d4r-check.ps1: every shipped file's hash, so a damaged or mixed install is reported.
|
||||
$manifest=[ordered]@{schema=1; kind='windows-drag-in'; packageCommit=$commit; binarySourceCommit=$baseline.d4rCommit;
|
||||
dependencies=@{optiScalerSourceCommit=$baseline.optiScaler.sourceCommit; zludaSourceCommit=$baseline.zludaBuild.sourceCommit; hipRuntime=$baseline.hipRuntime};
|
||||
targets=$targets; files=$files}
|
||||
$manifest | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Join-Path $PackageRoot 'd4r/package.json') -Encoding UTF8
|
||||
if ($ArchivePath) {
|
||||
$ArchivePath=[IO.Path]::GetFullPath($ArchivePath)
|
||||
if ($ArchivePath.StartsWith($PackageRoot+'\',[StringComparison]::OrdinalIgnoreCase)) { throw 'Place the ZIP outside the output directory.' }
|
||||
New-Item -ItemType Directory -Force (Split-Path $ArchivePath) | Out-Null
|
||||
Add-Type -AssemblyName System.IO.Compression,System.IO.Compression.FileSystem
|
||||
$stream=[IO.File]::Open($ArchivePath,[IO.FileMode]::Create,[IO.FileAccess]::ReadWrite,[IO.FileShare]::None)
|
||||
$archive=$null
|
||||
try {
|
||||
$archive=[IO.Compression.ZipArchive]::new($stream,[IO.Compression.ZipArchiveMode]::Create)
|
||||
foreach ($relative in @($files.path)+@('d4r/package.json')) {
|
||||
[void][IO.Compression.ZipFileExtensions]::CreateEntryFromFile($archive,(Join-Path $PackageRoot $relative),$relative,[IO.Compression.CompressionLevel]::Optimal)
|
||||
}
|
||||
} finally { if ($archive) { $archive.Dispose() }; $stream.Dispose() }
|
||||
}
|
||||
Write-Host "Windows drag-in release: $PackageRoot ($($targets.Count) GPU target(s): $($targets.Keys -join ', '); no NVIDIA DLLs)"
|
||||
@@ -0,0 +1,34 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$LlvmSourceRoot)
|
||||
$ErrorActionPreference='Stop'
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
$expected='ff4dc1f7c9e1c64d4d69e40f4ed30c2280a96dfd'
|
||||
$actual=(& git -C $LlvmSourceRoot rev-parse HEAD).Trim()
|
||||
if ($LASTEXITCODE -or $actual -ne $expected) { throw "Expected audited ZLUDA LLVM source $expected, found $actual; review the patch against this revision." }
|
||||
$patches=@(Get-ChildItem (Join-Path $repo 'patches/zluda-llvm') -Filter *.patch | Sort-Object Name)
|
||||
foreach ($patch in $patches) {
|
||||
$oldPreference=$ErrorActionPreference
|
||||
try {
|
||||
$ErrorActionPreference='Continue'
|
||||
& git -C $LlvmSourceRoot apply --reverse --check $patch.FullName 2>$null
|
||||
$applied=($LASTEXITCODE -eq 0)
|
||||
} finally { $ErrorActionPreference=$oldPreference }
|
||||
if (!$applied) {
|
||||
& git -C $LlvmSourceRoot apply --check $patch.FullName
|
||||
if ($LASTEXITCODE) { throw "LLVM source patch conflicts: $($patch.Name)" }
|
||||
& git -C $LlvmSourceRoot apply $patch.FullName
|
||||
if ($LASTEXITCODE) { throw "LLVM source patch failed: $($patch.Name)" }
|
||||
}
|
||||
Write-Host "LLVM source patch present: $($patch.Name)"
|
||||
}
|
||||
# The pinned backport is the entire intended LLVM delta.
|
||||
if ($patches.Count -ne 1) { throw 'Review combined LLVM source provenance before adding more patches.' }
|
||||
$audit=Join-Path $repo ('build/llvm-source-audit-'+[Guid]::NewGuid().ToString('N')+'.patch')
|
||||
New-Item -ItemType Directory -Force (Split-Path $audit) | Out-Null
|
||||
try {
|
||||
& git -C $LlvmSourceRoot diff --binary --output=$audit
|
||||
if ($LASTEXITCODE) { throw 'Cannot audit LLVM source diff.' }
|
||||
$actualDiff=[IO.File]::ReadAllText($audit).Replace("`r`n","`n").TrimEnd()
|
||||
$expectedDiff=[IO.File]::ReadAllText($patches[0].FullName).Replace("`r`n","`n").TrimEnd()
|
||||
if ($actualDiff -cne $expectedDiff) { throw 'LLVM source contains changes outside the exported audited patch.' }
|
||||
} finally { if (Test-Path -LiteralPath $audit) { Remove-Item -LiteralPath $audit } }
|
||||
@@ -0,0 +1,25 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$SourceRoot)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$SourceRoot) { $SourceRoot = Join-Path $repo 'external/ZLUDA' }
|
||||
$SourceRoot = [IO.Path]::GetFullPath($SourceRoot)
|
||||
$base = 'ee2f25a180099fa42f36b2346732e1f2470a03ad'
|
||||
if (!(Test-Path (Join-Path $SourceRoot 'Cargo.toml'))) { throw 'Clone upstream ZLUDA into external/ZLUDA first.' }
|
||||
Push-Location $SourceRoot
|
||||
try {
|
||||
$revision = (& git rev-parse HEAD).Trim()
|
||||
if ($revision -ne $base) { throw "Expected ZLUDA $base, found $revision; review patches against the new revision." }
|
||||
$dirty = @(& git status --porcelain --untracked-files=no --ignore-submodules=all)
|
||||
if ($dirty.Count) { throw 'ZLUDA source has tracked modifications. Keep this prepared checkout; do not reapply patches.' }
|
||||
& git switch -c windows-rdna4
|
||||
if ($LASTEXITCODE) { throw 'Could not create the local ZLUDA branch.' }
|
||||
foreach ($patch in (Get-ChildItem -LiteralPath (Join-Path $repo 'patches/zluda') -Filter '*.patch' | Sort-Object Name)) {
|
||||
& git apply --check $patch.FullName
|
||||
if ($LASTEXITCODE) { throw "Patch check failed: $($patch.Name)" }
|
||||
& git apply --whitespace=nowarn $patch.FullName
|
||||
if ($LASTEXITCODE) { throw "Patch failed: $($patch.Name)" }
|
||||
Write-Host "Applied $($patch.Name)"
|
||||
}
|
||||
Write-Host "Prepared Windows ZLUDA source at $SourceRoot (base $base)"
|
||||
} finally { Pop-Location }
|
||||
@@ -0,0 +1,44 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[ValidateSet(5,11,12,13)][int]$Preset = 11,
|
||||
[string]$ZludaRoot, [string]$PackageRoot, [string]$OutputDirectory,
|
||||
[string]$NativeKernelDirectory,
|
||||
[int]$Iterations = 4,
|
||||
[switch]$Translated, [switch]$NativeFp8
|
||||
)
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' }
|
||||
if (!$PackageRoot) { $PackageRoot=Join-Path $repo 'dist/windows-rdna4-command-list' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/profile-' + $Preset + '-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
$nativeDirectory=if ($NativeFp8) {
|
||||
if ($GpuArch -notmatch '^gfx12' -or $Preset -notin @(12,13)) { throw '-NativeFp8 requires gfx12 and preset L/M.' }
|
||||
if (!$NativeKernelDirectory) { $NativeKernelDirectory=Join-Path $repo 'build/native-m-fp8-token-lane-hwpack' }
|
||||
$info=Get-Content -LiteralPath (Join-Path $NativeKernelDirectory 'build-info.json') -Raw | ConvertFrom-Json
|
||||
if ($info.architecture -ne $GpuArch -or !$info.nativeFp8 -or !$info.tokenLanes) { throw 'NativeFp8 needs the matching gfx12 token-lane M kernel bundle.' }
|
||||
[IO.Path]::GetFullPath($NativeKernelDirectory)
|
||||
} elseif ($Translated -or $Preset -eq 5) { $null }
|
||||
elseif ($Preset -eq 11) { Join-Path $repo "build/native-k-$GpuArch" }
|
||||
else { Join-Path $repo "build/native-m-$GpuArch" }
|
||||
$settings=@{D4R_ZLUDA_PROFILE='1'; D4R_PROFILE_STAGES='1'; D4R_VALIDATE_OUTPUT='1';
|
||||
D4R_ZLUDA_PROFILE_DEFERRED=$null; D4R_ZLUDA_PROFILE_ALLOW_LEGACY=$null; D4R_ZLUDA_PROFILE_EVERY=$null;
|
||||
D4R_ZLUDA_NATIVE_DIR=$nativeDirectory}
|
||||
$settings['D4R_ZLUDA_WMMA_FP8_NATIVE']=if ($NativeFp8) { '1' } else { $null }
|
||||
$old=@{}
|
||||
try {
|
||||
foreach ($key in $settings.Keys) { $old[$key]=[Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') }
|
||||
$arguments=@{RuntimeProfile='therock'; ZludaRoot=$ZludaRoot; PackageRoot=$PackageRoot; OutputDirectory=$OutputDirectory;
|
||||
NgxCore=(Join-Path $repo '_nvngx.dll'); DlssDll=(Join-Path $repo 'nvngx_dlss.dll'); NgxMode='d3d12';
|
||||
NgxAbi='project-legacy'; Preset=$Preset; NgxOnly=$true; RequireNativeNetwork=(!$Translated -and $Preset -ne 5); NativeFp8=$NativeFp8; CommandListBackend=$true;
|
||||
CaptureExceptions=$true; EarlyIndirectProbe=$true; PixelProfile='depth-stencil'; BarrierMode='inherited-legacy';
|
||||
Iterations=$Iterations; TimeoutSeconds=300; OptiScalerDll=(Join-Path $repo 'dist/optiscaler-windows-d4r/OptiScaler.dll')}
|
||||
& (Join-Path $PSScriptRoot 'test-windows-rdna4.ps1') @arguments
|
||||
if ($LASTEXITCODE) { throw 'Profiling workload failed; see diagnostic bundle.' }
|
||||
$python=(Get-Command python.exe -ErrorAction Stop).Source
|
||||
& $python (Join-Path $repo 'tools/windows/profile_report.py') $OutputDirectory --output (Join-Path $OutputDirectory 'profile.json')
|
||||
if ($LASTEXITCODE) { throw 'No usable profile records.' }
|
||||
} finally {
|
||||
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[ValidateSet('run','install','restore')][string]$Action='run',
|
||||
[ValidateSet(5,11,12,13)][int]$Preset=11,
|
||||
[string]$GameExe, [string]$NgxCore, [string]$DlssDll,
|
||||
[switch]$NativeFp8, [switch]$CaptureExceptions,
|
||||
[ValidateRange(30,600)][int]$RunSeconds=180
|
||||
)
|
||||
$ErrorActionPreference='Stop'
|
||||
$root=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
||||
$work=Join-Path $root 'work'
|
||||
$settingsPath=Join-Path $work 'settings.json'
|
||||
New-Item -ItemType Directory -Force $work | Out-Null
|
||||
$saved=$null
|
||||
if (Test-Path -LiteralPath $settingsPath) {
|
||||
try { $saved=Get-Content -LiteralPath $settingsPath -Raw | ConvertFrom-Json }
|
||||
catch { Write-Host 'Saved choices could not be read. Select the files again.' }
|
||||
}
|
||||
function Select-LocalFile([string]$Title, [string]$FileName, [string]$Previous) {
|
||||
if ($Previous -and (Test-Path -LiteralPath $Previous -PathType Leaf)) { return (Get-Item -LiteralPath $Previous).FullName }
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
$dialog=New-Object Windows.Forms.OpenFileDialog
|
||||
try {
|
||||
$dialog.Title=$Title; $dialog.FileName=$FileName
|
||||
$dialog.Filter=if ($FileName -eq '*.exe') { 'Game executable (*.exe)|*.exe' } else { "$FileName|$FileName" }
|
||||
if ($dialog.ShowDialog() -ne [Windows.Forms.DialogResult]::OK) { throw 'File selection cancelled. No test was launched.' }
|
||||
return $dialog.FileName
|
||||
} finally { $dialog.Dispose() }
|
||||
}
|
||||
function Copy-Payload([string]$Source, [string]$Destination, [string]$Relative) {
|
||||
$path=[IO.Path]::GetFullPath((Join-Path $Destination $Relative))
|
||||
if (!$path.StartsWith($Destination+'\',[StringComparison]::OrdinalIgnoreCase)) { throw 'Invalid package path.' }
|
||||
New-Item -ItemType Directory -Force (Split-Path $path) | Out-Null
|
||||
Copy-Item -LiteralPath (Join-Path $Source $Relative) -Destination $path -Force
|
||||
}
|
||||
function Get-BundledNvidiaFile([string]$Relative) {
|
||||
$directory=Join-Path $PSScriptRoot 'nvidia'
|
||||
$path=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot $Relative))
|
||||
if (!$path.StartsWith($directory+'\',[StringComparison]::OrdinalIgnoreCase) -or
|
||||
!(Test-Path -LiteralPath $path -PathType Leaf)) { throw 'Invalid bundled NVIDIA file. Extract the complete package again.' }
|
||||
return $path
|
||||
}
|
||||
if ($Action -eq 'restore') {
|
||||
$GameExe=Select-LocalFile 'Select the SAME game executable used for the test' '*.exe' $(if ($GameExe) { $GameExe } elseif ($saved) { $saved.game })
|
||||
& (Join-Path $PSScriptRoot 'common/windows-game.ps1') -Action restore -GameExe $GameExe
|
||||
Write-Host 'RESTORED. Launch the game normally again.' -ForegroundColor Green
|
||||
return
|
||||
}
|
||||
$output=Join-Path $root ('results/test-'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff'))
|
||||
New-Item -ItemType Directory -Force $output | Out-Null
|
||||
$status=[ordered]@{action=$Action; preset=$Preset; status='starting'; hardwareValidationCompleted=$false}
|
||||
$selectedPackage=$null
|
||||
try {
|
||||
Write-Host 'd4r Windows quick test - keep this window open until you EXIT the game.'
|
||||
Write-Host 'Checking package and detecting the GPU...'
|
||||
$manifest=Get-Content -LiteralPath (Join-Path $PSScriptRoot 'package.json') -Raw | ConvertFrom-Json
|
||||
foreach ($file in $manifest.files) {
|
||||
$path=[IO.Path]::GetFullPath((Join-Path $root $file.path))
|
||||
if (!$path.StartsWith($root+'\',[StringComparison]::OrdinalIgnoreCase) -or
|
||||
(Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash -ne $file.sha256) { throw "Package is incomplete or changed. Extract the ZIP again. File: $($file.path)" }
|
||||
}
|
||||
$status.packageCommit=$manifest.packageCommit
|
||||
$inventoryDir=Join-Path $output 'gpu'
|
||||
New-Item -ItemType Directory -Force $inventoryDir | Out-Null
|
||||
$info=New-Object Diagnostics.ProcessStartInfo
|
||||
$info.FileName=Join-Path $PSScriptRoot 'targets/gfx1201/d4r/d4r_gpu_inventory.exe'
|
||||
$info.Arguments='--hip-root "'+(Join-Path $PSScriptRoot 'common/d4r/hip')+'" --output-dir "'+$inventoryDir+'"'
|
||||
$info.UseShellExecute=$false; $info.CreateNoWindow=$true
|
||||
$info.EnvironmentVariables['D4R_DIAG_DIR']=$inventoryDir
|
||||
$info.RedirectStandardOutput=$true; $info.RedirectStandardError=$true
|
||||
$process=New-Object Diagnostics.Process; $process.StartInfo=$info
|
||||
try {
|
||||
if (!$process.Start()) { throw 'GPU detection could not start.' }
|
||||
$stdout=$process.StandardOutput.ReadToEndAsync(); $stderr=$process.StandardError.ReadToEndAsync()
|
||||
$timedOut=!$process.WaitForExit(30000)
|
||||
if ($timedOut) { $process.Kill(); $process.WaitForExit() }
|
||||
$stdout.GetAwaiter().GetResult() | Set-Content (Join-Path $inventoryDir 'stdout.log') -Encoding UTF8
|
||||
$stderr.GetAwaiter().GetResult() | Set-Content (Join-Path $inventoryDir 'stderr.log') -Encoding UTF8
|
||||
$status.inventoryExit='0x{0:X8}' -f [BitConverter]::ToUInt32([BitConverter]::GetBytes([int]$process.ExitCode),0)
|
||||
if ($timedOut -or $process.ExitCode) { throw 'HIP could not detect your GPU. The diagnostic ZIP contains the details.' }
|
||||
} finally { $process.Dispose() }
|
||||
$inventory=Get-Content -LiteralPath (Join-Path $inventoryDir 'gpu-inventory.json') -Raw | ConvertFrom-Json
|
||||
$devices=@($inventory.devices | Where-Object { $_.buildEligible -and $_.wavefrontSize -eq 32 -and $manifest.targets.PSObject.Properties.Name -contains $_.architecture } | Sort-Object vramBytes -Descending)
|
||||
if (!$devices.Count) { throw 'No supported RDNA3/RDNA4 HIP device was detected. See the GPU inventory in the diagnostic ZIP.' }
|
||||
$device=$devices[0]
|
||||
if (@($devices.architecture | Select-Object -Unique).Count -gt 1) {
|
||||
for ($i=0; $i -lt $devices.Count; $i++) { Write-Host "$($i+1): $($devices[$i].name) ($($devices[$i].architecture))" }
|
||||
$choice=Read-Host 'Select the GPU used by your game (press Enter for 1)'
|
||||
if ($choice) {
|
||||
$index=0
|
||||
if (![int]::TryParse($choice,[ref]$index) -or $index -lt 1 -or $index -gt $devices.Count) { throw 'Invalid GPU selection.' }
|
||||
$device=$devices[$index-1]
|
||||
}
|
||||
}
|
||||
$arch=$device.architecture; $target=$manifest.targets.$arch
|
||||
if ($NativeFp8 -and ($arch -notmatch '^gfx12' -or !$target.nativeFp8M -or $Preset -notin @(12,13))) {
|
||||
throw '-NativeFp8 needs a gfx12 package containing the experimental native-FP8 M kernels and preset L or M.'
|
||||
}
|
||||
$status.gpu=$device; $status.binarySourceCommit=$target.binarySourceCommit
|
||||
$status.nativeFp8=[bool]$NativeFp8; $status.tokenLanesM=[bool]$target.tokenLanesM
|
||||
Write-Host "Detected: $($device.name) / $arch" -ForegroundColor Cyan
|
||||
Write-Host 'Experimental build. This run is a correctness test, not an FPS benchmark.'
|
||||
$previousGame=if ($saved) { $saved.game } else { $null }
|
||||
if (!$GameExe -and $previousGame -and (Test-Path -LiteralPath $previousGame)) {
|
||||
Write-Host "Remembered game: $previousGame"
|
||||
$reuse=Read-Host 'Press Enter to use this game, or type C to choose another (restore the old game first)'
|
||||
if ($reuse -match '^(?i)c$') { $previousGame=$null }
|
||||
elseif ($reuse) { throw 'Invalid choice. Press Enter or type C on the next run.' }
|
||||
}
|
||||
$GameExe=Select-LocalFile 'Select the actual D3D12 game .exe (not Steam or a launcher)' '*.exe' $(if ($GameExe) { $GameExe } else { $previousGame })
|
||||
$bundledCore=$null; $bundledDlss=$null
|
||||
if ($manifest.bundledNvidia) {
|
||||
$bundledCore=Get-BundledNvidiaFile $manifest.bundledNvidia.ngxCore
|
||||
$bundledDlss=Get-BundledNvidiaFile $manifest.bundledNvidia.dlssDll
|
||||
}
|
||||
$status.nvidiaDllSource=@{core=$(if ($NgxCore) { 'explicit' } elseif ($bundledCore) { 'bundled' } else { 'user-supplied' });
|
||||
dlss=$(if ($DlssDll) { 'explicit' } elseif ($bundledDlss) { 'bundled' } else { 'user-supplied' })}
|
||||
if ($bundledCore -and !$NgxCore -and !$DlssDll) { Write-Host 'Using the validated NVIDIA DLL pair included by this package distributor.' }
|
||||
$NgxCore=Select-LocalFile 'Select your ORIGINAL NVIDIA _nvngx.dll (32.0.16.1714)' '_nvngx.dll' $(if ($NgxCore) { $NgxCore } elseif ($bundledCore) { $bundledCore } elseif ($saved) { $saved.ngxCore })
|
||||
$DlssDll=Select-LocalFile 'Select your NVIDIA nvngx_dlss.dll (310.9.1)' 'nvngx_dlss.dll' $(if ($DlssDll) { $DlssDll } elseif ($bundledDlss) { $bundledDlss } elseif ($saved) { $saved.dlssDll })
|
||||
$status.game=$GameExe
|
||||
$status.localDlls=@($NgxCore,$DlssDll | ForEach-Object { $item=Get-Item -LiteralPath $_; @{path=$item.FullName; version=$item.VersionInfo.FileVersion; sha256=(Get-FileHash -LiteralPath $_ -Algorithm SHA256).Hash} })
|
||||
if ((Get-FileHash -LiteralPath $NgxCore -Algorithm SHA256).Hash -ne $manifest.ngxCoreSha256) {
|
||||
throw 'Wrong _nvngx.dll. This test needs the original NVIDIA 32.0.16.1714 DLL, not the d4r shim. Select the correct file on the next run.'
|
||||
}
|
||||
if ((Get-FileHash -LiteralPath $DlssDll -Algorithm SHA256).Hash -ne $target.dlssSha256) {
|
||||
throw 'Wrong nvngx_dlss.dll. This test needs exactly NVIDIA DLSS 310.9.1. Select the correct file on the next run.'
|
||||
}
|
||||
if ($GameExe.StartsWith($root+'\',[StringComparison]::OrdinalIgnoreCase)) { throw 'Select the installed GAME executable, outside this extracted package.' }
|
||||
# Save only validated choices. Wrong DLLs are never remembered.
|
||||
@{game=$GameExe; ngxCore=$NgxCore; dlssDll=$DlssDll; architecture=$arch} | ConvertTo-Json | Set-Content -LiteralPath $settingsPath -Encoding UTF8
|
||||
$selectedPackage=Join-Path $work "package-$arch"
|
||||
New-Item -ItemType Directory -Force $selectedPackage | Out-Null
|
||||
$selectedFiles=@()
|
||||
foreach ($file in $manifest.commonFiles) {
|
||||
Copy-Payload (Join-Path $PSScriptRoot 'common') $selectedPackage $file.path
|
||||
$selectedFiles+=$file
|
||||
}
|
||||
foreach ($file in $target.files) {
|
||||
Copy-Payload (Join-Path $PSScriptRoot "targets/$arch") $selectedPackage $file.path
|
||||
$selectedFiles+=$file
|
||||
}
|
||||
@{architecture=$arch; d4rCommit=$target.binarySourceCommit; packageCommit=$manifest.packageCommit;
|
||||
dlssSha256=$target.dlssSha256; hardwareValidation=$target.hardwareValidation;
|
||||
nativeFp8M=[bool]$target.nativeFp8M; tokenLanesM=[bool]$target.tokenLanesM; files=$selectedFiles} |
|
||||
ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Join-Path $selectedPackage 'package.json') -Encoding UTF8
|
||||
Write-Host 'Original game files will be backed up. RESTORE-GAME.cmd reverses the installation.'
|
||||
Write-Host 'In the game: select DLSS if available; otherwise select FSR/XeSS to let OptiScaler intercept it.'
|
||||
Write-Host 'The first launch compiles shaders and can take several minutes. Then play for 30 seconds and EXIT the game normally.'
|
||||
& (Join-Path $selectedPackage 'windows-game.ps1') -Action $Action -GameExe $GameExe -NgxCore $NgxCore -DlssDll $DlssDll `
|
||||
-Preset $Preset -RunSeconds $RunSeconds -NativeFp8:$NativeFp8 -ValidateOutput -CaptureExceptions:$CaptureExceptions -OutputDirectory $output
|
||||
if ($Action -eq 'install') { $status.status='installed-only' }
|
||||
else {
|
||||
$summary=Get-Content -LiteralPath (Join-Path $output 'summary.json') -Raw | ConvertFrom-Json
|
||||
$status.gameSummary=$summary
|
||||
$required=Get-Content -LiteralPath (Join-Path $selectedPackage 'd4r/native/d4r-kernels.txt') | Where-Object {
|
||||
if ($Preset -eq 11) { $_ -match '^dltss_pwin_' } elseif ($Preset -in @(12,13)) { $_ -match '^rrlite_' }
|
||||
} | ForEach-Object { $_.Split(' ')[0] }
|
||||
$hits=@($summary.kernels | Where-Object { $_.backend -eq 'native' -and $_.launches -gt 0 } | ForEach-Object { $_.kernel })
|
||||
$status.missingNativeKernels=@($required | Where-Object { $hits -notcontains $_ })
|
||||
$status.translatedNativeKernels=@($summary.kernels | Where-Object { $_.backend -eq 'translated' -and $required -contains $_.kernel } | ForEach-Object { $_.kernel })
|
||||
$requiredNativePath=($Preset -eq 5) -or ($summary.nativeLaunches -gt 0)
|
||||
$expectedNativeCount=if ($Preset -eq 5) { 0 } elseif ($Preset -eq 11) { 11 } else { 5 }
|
||||
$passed=$summary.exitCode -eq '0x0' -and $summary.completedFrames -gt 0 -and $requiredNativePath -and
|
||||
$summary.outputGpuChecks -gt 0 -and $summary.failures -eq 0 -and $summary.nonfiniteOutputs -eq 0 -and
|
||||
$summary.previousFrameOutputs -eq 0 -and $summary.cpuImageCopyFrames -eq 0 -and
|
||||
@($required).Count -eq $expectedNativeCount -and
|
||||
$status.missingNativeKernels.Count -eq 0 -and $status.translatedNativeKernels.Count -eq 0
|
||||
$status.hardwareValidationCompleted=[bool]$passed
|
||||
$status.status=if ($passed) { 'backend-checks-passed-image-still-needs-user-review' }
|
||||
elseif ($summary.completedFrames -eq 0 -and $summary.exitCode -eq '0x0' -and $summary.failures -eq 0) { 'inconclusive-process-exited-without-dlss-frames' }
|
||||
else { 'backend-checks-failed-or-no-dlss-frames' }
|
||||
$status.session=$summary.session
|
||||
if ($status.status -eq 'inconclusive-process-exited-without-dlss-frames') {
|
||||
Write-Host 'The launched process exited without a DLSS frame. A Steam/launcher restart is not tracked by this test. Keep Steam open and select the actual game executable. Send the ZIP if it restarts again.' -ForegroundColor Yellow
|
||||
}
|
||||
Write-Host $(if ($passed) { 'Backend checks PASSED. Please also report whether the image looked correct.' } else { 'Test did not pass. Send the diagnostic ZIP; do not guess from the FPS.' }) -ForegroundColor $(if ($passed) { 'Green' } else { 'Yellow' })
|
||||
}
|
||||
} catch {
|
||||
$status.status='failed'; $status.error=$_.Exception.Message
|
||||
$_ | Out-String | Set-Content -LiteralPath (Join-Path $output 'error.txt') -Encoding UTF8
|
||||
Write-Host "TEST STOPPED: $($status.error)" -ForegroundColor Red
|
||||
} finally {
|
||||
if ($selectedPackage) {
|
||||
$preflights=Join-Path $selectedPackage 'results'
|
||||
$latest=Get-ChildItem -LiteralPath $preflights -Directory -ErrorAction SilentlyContinue | Sort-Object Name -Descending | Select-Object -First 1
|
||||
if ($latest) { Copy-Item -LiteralPath $latest.FullName -Destination (Join-Path $output 'preflight') -Recurse -Force }
|
||||
}
|
||||
try { Get-CimInstance Win32_VideoController | Select-Object Name,DriverVersion,DriverDate | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $output 'driver.json') -Encoding UTF8 } catch {}
|
||||
$status | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath (Join-Path $output 'quick-test.json') -Encoding UTF8
|
||||
Compress-Archive -LiteralPath $output -DestinationPath ($output+'.zip') -Force
|
||||
Write-Host "SEND THIS ONE FILE: $output.zip" -ForegroundColor Cyan
|
||||
Write-Host 'Also tell us: GPU model, game name, K or M, and whether the image was correct.'
|
||||
Write-Host 'Close the game before running RESTORE-GAME.cmd.'
|
||||
}
|
||||
if ($status.status -eq 'failed' -or $status.status -eq 'backend-checks-failed-or-no-dlss-frames' -or $status.status.StartsWith('inconclusive-')) { exit 1 }
|
||||
@@ -0,0 +1,32 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][int]$GameProcessId,
|
||||
[ValidateSet('Enter','Escape','Up','Down','Tab','Space')][string]$Key='Enter')
|
||||
$ErrorActionPreference='Stop'
|
||||
$process=Get-Process -Id $GameProcessId -ErrorAction Stop
|
||||
if ($process.ProcessName -eq 'GhostOfTsushima') {
|
||||
$expected='C:\Program Files (x86)\Steam\steamapps\common\Ghost of Tsushima DIRECTOR''S CUT\GhostOfTsushima.exe'
|
||||
if ($process.Path -ine $expected) { throw 'Ghost of Tsushima process path does not match the authorized Steam installation.' }
|
||||
} elseif ($process.ProcessName -ne 'SHProto-Win64-Shipping') {
|
||||
throw 'This helper only accepts the authorized Ghost of Tsushima or Silent Hill 2 diagnostic process.'
|
||||
}
|
||||
if (!('D4RGameKeys' -as [type])) {
|
||||
Add-Type @'
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
public static class D4RGameKeys {
|
||||
[DllImport("user32.dll")] public static extern bool PostMessage(IntPtr window, uint message, IntPtr key, IntPtr flags);
|
||||
[DllImport("user32.dll")] public static extern uint MapVirtualKey(uint code, uint kind);
|
||||
[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
|
||||
}
|
||||
'@
|
||||
}
|
||||
$window=$process.MainWindowHandle
|
||||
if ($window -eq [IntPtr]::Zero) { throw 'Game window missing on this desktop.' }
|
||||
if ([D4RGameKeys]::GetForegroundWindow() -ne $window) { throw 'Target game is not foreground; key was not sent.' }
|
||||
$codes=@{Enter=13; Escape=27; Up=38; Down=40; Tab=9; Space=32}
|
||||
$code=$codes[$Key]; $scan=[D4RGameKeys]::MapVirtualKey($code,0)
|
||||
$flags=[long](1 -bor ($scan -shl 16))
|
||||
if ($Key -in @('Up','Down')) { $flags=$flags -bor 0x01000000 }
|
||||
if (![D4RGameKeys]::PostMessage($window,0x100,[IntPtr]$code,[IntPtr]$flags)) { throw 'Game key-down post failed.' }
|
||||
if (![D4RGameKeys]::PostMessage($window,0x101,[IntPtr]$code,[IntPtr]($flags -bor 0xc0000000L))) { throw 'Game key-up post failed.' }
|
||||
Write-Host "Sent $Key only to diagnostic game PID $GameProcessId."
|
||||
@@ -0,0 +1,50 @@
|
||||
[CmdletBinding()]
|
||||
param()
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
$tools = Join-Path $repo '.tools'
|
||||
$downloads = Join-Path $tools 'downloads'
|
||||
New-Item -ItemType Directory -Force -Path $downloads | Out-Null
|
||||
|
||||
function Ensure-PinnedGitCheckout([string]$Path, [string]$Url, [string]$Commit) {
|
||||
if (!(Test-Path -LiteralPath $Path)) {
|
||||
git clone --no-checkout $Url $Path
|
||||
if ($LASTEXITCODE) { throw "Git clone failed: $Url" }
|
||||
}
|
||||
git -C $Path fetch --depth 1 origin $Commit
|
||||
if ($LASTEXITCODE) { throw "Pinned dependency fetch failed: $Url@$Commit" }
|
||||
git -C $Path checkout --detach $Commit
|
||||
if ($LASTEXITCODE) { throw "Pinned dependency checkout failed: $Url@$Commit" }
|
||||
$actual = (git -C $Path rev-parse HEAD).Trim()
|
||||
if ($actual -ne $Commit) { throw "Dependency revision mismatch: $Path ($actual)" }
|
||||
}
|
||||
|
||||
$headers = Join-Path $tools 'Vulkan-Headers-v1.4.341'
|
||||
Ensure-PinnedGitCheckout $headers 'https://github.com/KhronosGroup/Vulkan-Headers.git' 'b5c8f996196ba4aa6d8f97e52b5d3b6e70f7e4e2'
|
||||
$volk = Join-Path $tools 'volk'
|
||||
Ensure-PinnedGitCheckout $volk 'https://github.com/zeux/volk.git' '0dc3ce00bf98b9f0b6fe708ca0f7eb74e2830173'
|
||||
|
||||
$glslangDirectory = Join-Path $tools 'glslang-16.6.0'
|
||||
$validator = Join-Path $glslangDirectory 'bin/glslangValidator.exe'
|
||||
$archive = Join-Path $downloads 'glslang-16.6.0-windows-x86_64-release.zip'
|
||||
$expectedHash = '82bf434e69b9bb4829de7e2b4bc2c5e7a7861e53d66cf75e5cc70f5f694a8d9b'
|
||||
if (!(Test-Path -LiteralPath $validator)) {
|
||||
if (!(Test-Path -LiteralPath $archive)) {
|
||||
Invoke-WebRequest -UseBasicParsing `
|
||||
-Uri 'https://github.com/KhronosGroup/glslang/releases/download/16.6.0/glslang-16.6.0-windows-x86_64-release.zip' `
|
||||
-OutFile $archive
|
||||
}
|
||||
$actualHash = (Get-FileHash -LiteralPath $archive -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
if ($actualHash -ne $expectedHash) { throw "glslang archive SHA256 mismatch: $actualHash" }
|
||||
Expand-Archive -LiteralPath $archive -DestinationPath $glslangDirectory -Force
|
||||
$releaseBinary = Join-Path $glslangDirectory 'bin/glslang.exe'
|
||||
if (!(Test-Path -LiteralPath $releaseBinary)) { throw "glslang archive layout changed: $releaseBinary" }
|
||||
Copy-Item -LiteralPath $releaseBinary -Destination $validator
|
||||
}
|
||||
if (!(Test-Path -LiteralPath $headers -PathType Container) -or
|
||||
!(Test-Path -LiteralPath (Join-Path $headers 'include/vulkan/vulkan.h')) -or
|
||||
!(Test-Path -LiteralPath (Join-Path $volk 'volk.c')) -or
|
||||
!(Test-Path -LiteralPath $validator)) {
|
||||
throw 'Native engine dependencies are incomplete.'
|
||||
}
|
||||
Write-Host "Native engine dependencies ready: Vulkan-Headers $((git -C $headers rev-parse --short HEAD).Trim()), Volk $((git -C $volk rev-parse --short HEAD).Trim()), glslang 16.6.0."
|
||||
@@ -0,0 +1,48 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$ToolRoot, [string]$DownloadRoot)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$ToolRoot) { $ToolRoot = Join-Path $repo '.tools/rust-1.98.1-gnu' }
|
||||
if (!$DownloadRoot) { $DownloadRoot = Join-Path $repo '.tools/downloads' }
|
||||
$ToolRoot = [IO.Path]::GetFullPath($ToolRoot)
|
||||
$DownloadRoot = [IO.Path]::GetFullPath($DownloadRoot)
|
||||
New-Item -ItemType Directory -Force $ToolRoot,$DownloadRoot | Out-Null
|
||||
# Pinned official Rust 2026-09-03 manifest. The toolchain is local to this
|
||||
# workspace; rustup, PATH changes and machine-wide installation are unnecessary.
|
||||
$components = @(
|
||||
@{Name='rustc'; Hash='f0e8e33973771acc4d2f87891b19d4f3f2d3827e2e7848c091d7c070bd63479c'},
|
||||
@{Name='cargo'; Hash='4bd77f16bd2a26db6eacf9320414d3a792d9998cb5e9ac122280ba56126f2a44'},
|
||||
@{Name='rust-std'; Hash='5bb599a541fcb9c0edc00e512570f60d2262623f1e2a19a44cce3a7a97208788'},
|
||||
@{Name='rust-mingw'; Hash='75d898804789c12ca969365f0a86d85c3bca1fdb070f0be615ca513a986b2674'}
|
||||
)
|
||||
foreach ($component in $components) {
|
||||
$package = "$($component.Name)-1.98.1-x86_64-pc-windows-gnu"
|
||||
$archive = Join-Path $DownloadRoot "$package.tar.xz"
|
||||
if (!(Test-Path $archive) -or (Get-FileHash -LiteralPath $archive -Algorithm SHA256).Hash -ne $component.Hash) {
|
||||
Write-Host "Downloading official Rust component $($component.Name)"
|
||||
& curl.exe --fail --location --silent --show-error --connect-timeout 15 --max-time 300 --retry 2 `
|
||||
"https://static.rust-lang.org/dist/2026-09-03/$package.tar.xz" --output $archive
|
||||
if ($LASTEXITCODE) { throw "Download failed for $package (exit=$LASTEXITCODE)" }
|
||||
}
|
||||
if ((Get-FileHash -LiteralPath $archive -Algorithm SHA256).Hash -ne $component.Hash) {
|
||||
throw "Rust component SHA256 mismatch: $archive"
|
||||
}
|
||||
$extract = Join-Path $DownloadRoot $package
|
||||
if (!(Test-Path (Join-Path $extract 'components'))) {
|
||||
& tar.exe -xf $archive -C $DownloadRoot
|
||||
if ($LASTEXITCODE) { throw "Extract failed: $archive" }
|
||||
}
|
||||
foreach ($entry in (Get-Content -LiteralPath (Join-Path $extract 'components'))) {
|
||||
$payload = Join-Path $extract $entry
|
||||
foreach ($directory in @('bin','lib','share','etc')) {
|
||||
$source = Join-Path $payload $directory
|
||||
if (Test-Path $source) { Copy-Item -LiteralPath $source -Destination $ToolRoot -Recurse -Force }
|
||||
}
|
||||
}
|
||||
Write-Host "Verified and installed $package"
|
||||
}
|
||||
& (Join-Path $ToolRoot 'bin/rustc.exe') --version
|
||||
if ($LASTEXITCODE) { throw 'Rust compiler failed to start' }
|
||||
& (Join-Path $ToolRoot 'bin/cargo.exe') --version
|
||||
if ($LASTEXITCODE) { throw 'Cargo failed to start' }
|
||||
Write-Host "Local Rust toolchain: $ToolRoot"
|
||||
@@ -0,0 +1,48 @@
|
||||
[CmdletBinding()]
|
||||
param([ValidateSet('stable', 'therock')][string]$RuntimeProfile = 'stable')
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
$localTools = Join-Path $repo '.tools'
|
||||
$downloadDirectory = Join-Path $localTools 'downloads'
|
||||
New-Item -ItemType Directory -Force $downloadDirectory | Out-Null
|
||||
function Get-VerifiedArchive([string]$Url, [string]$Name, [string]$Hash, [string]$Destination, [string]$Marker) {
|
||||
if (Test-Path -LiteralPath $Marker) { Write-Host "Already present: $Marker"; return }
|
||||
$archive = Join-Path $downloadDirectory $Name
|
||||
if (!(Test-Path -LiteralPath $archive)) {
|
||||
Invoke-WebRequest -UseBasicParsing -Uri $Url -OutFile $archive
|
||||
}
|
||||
if ((Get-FileHash -LiteralPath $archive -Algorithm SHA256).Hash -ne $Hash) {
|
||||
throw "SHA256 mismatch: $archive (remove this download and retry)"
|
||||
}
|
||||
if ([IO.Path]::GetExtension($archive) -eq '.whl') {
|
||||
python -c 'import sys,zipfile; zipfile.ZipFile(sys.argv[1]).extractall(sys.argv[2])' $archive $Destination
|
||||
if ($LASTEXITCODE) { throw "Wheel extraction exit=$LASTEXITCODE" }
|
||||
} else { Expand-Archive -LiteralPath $archive -DestinationPath $Destination -Force }
|
||||
if (!(Test-Path -LiteralPath $Marker)) { throw "Archive layout changed: $Marker" }
|
||||
}
|
||||
Get-VerifiedArchive 'https://github.com/mstorsjo/llvm-mingw/releases/download/20260922/llvm-mingw-20260922-ucrt-x86_64.zip' `
|
||||
'llvm-mingw.zip' 'e3ad77d117a4bea19a7a3b333341824d79a5a371004a10e25b8504e7b3047666' $localTools `
|
||||
(Join-Path $localTools 'llvm-mingw-20260922-ucrt-x86_64/bin/x86_64-w64-mingw32-clang++.exe')
|
||||
Get-VerifiedArchive 'https://github.com/vosen/ZLUDA/releases/download/v7-preview.11/zluda-windows-ee2f25a.zip' `
|
||||
'zluda.zip' '7788c1ed43385e62f0cc5f4d1aead24c92b671c02f1d685f63182540dea16d74' (Join-Path $localTools 'zluda') `
|
||||
(Join-Path $localTools 'zluda/zluda/nvcuda.dll')
|
||||
if ($RuntimeProfile -eq 'therock') {
|
||||
Get-VerifiedArchive 'https://nightly.repo.amd.com/rocm/core/whl-next/rocm-sdk-core/rocm_sdk_core-10.2.0a20260929-py3-none-win_amd64.whl' `
|
||||
'rocm_sdk_core-10.2.0a20260929-py3-none-win_amd64.whl' `
|
||||
'9623b97ca511eaa176905075a504393eca6fde594222af10b55e4fb1edfe5a25' `
|
||||
(Join-Path $localTools 'therock-10.2.0a20260929') `
|
||||
(Join-Path $localTools 'therock-10.2.0a20260929/_rocm_sdk_core/bin/amdhip64_7.dll')
|
||||
}
|
||||
if (!(Test-Path (Join-Path $localTools 'python/cmake/data/bin/cmake.exe')) -or
|
||||
!(Test-Path (Join-Path $localTools 'python/bin/ninja.exe'))) {
|
||||
python -m pip install --target (Join-Path $localTools 'python') cmake==3.31.6 ninja==1.11.1.4
|
||||
if ($LASTEXITCODE) { throw "pip install exit=$LASTEXITCODE" }
|
||||
}
|
||||
if (!(Test-Path (Join-Path $localTools 'python/vendor/numpy/__init__.py'))) {
|
||||
python -m pip install --disable-pip-version-check --only-binary=:all: `
|
||||
--target (Join-Path $localTools 'python/vendor') numpy==2.4.6
|
||||
if ($LASTEXITCODE) { throw "NumPy reference install exit=$LASTEXITCODE" }
|
||||
}
|
||||
& (Join-Path $PSScriptRoot 'setup-native-engine.ps1')
|
||||
if ($LASTEXITCODE) { throw "Native engine dependency setup exit=$LASTEXITCODE" }
|
||||
Write-Host 'Tools ready. Run scripts/windows/build-windows-rdna4.ps1 or scripts/windows/build-native-engine.ps1.'
|
||||
@@ -0,0 +1,30 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$DlssDll, [string]$PackageRoot, [string]$OutputDirectory, [string]$GpuArch)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $GpuArch
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo "build/native-k-$GpuArch" }
|
||||
if (![IO.Path]::IsPathRooted($DlssDll) -or !(Test-Path -LiteralPath $DlssDll)) {
|
||||
throw 'Supply the absolute path to your local nvngx_dlss.dll.'
|
||||
}
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
foreach ($object in Get-ChildItem -LiteralPath $OutputDirectory -Filter '*.hsaco') {
|
||||
Assert-D4RCodeObjectTarget $object.FullName $GpuArch
|
||||
}
|
||||
$layers = @('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0')
|
||||
foreach ($layer in $layers) {
|
||||
$name = "dltss_pwin_${layer}_layer"
|
||||
$source = Join-Path $PackageRoot "experimental/k/${name}_${GpuArch}.hsaco"
|
||||
if (!(Test-Path -LiteralPath $source)) { throw "Native K module missing: $source" }
|
||||
Assert-D4RCodeObjectTarget $source $GpuArch
|
||||
Copy-Item -LiteralPath $source -Destination (Join-Path $OutputDirectory "$name.hsaco") -Force
|
||||
}
|
||||
& python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $OutputDirectory $DlssDll
|
||||
if ($LASTEXITCODE) { throw "Native manifest generation failed ($LASTEXITCODE)" }
|
||||
@{architecture=$GpuArch; family='K'; dlssSha256=(Get-FileHash -LiteralPath $DlssDll -Algorithm SHA256).Hash;
|
||||
validation='staged only; GPU execution and DLSS capture/reference validation required';
|
||||
modules=@(Get-ChildItem -LiteralPath $OutputDirectory -Filter '*.hsaco' | Get-FileHash -Algorithm SHA256)} |
|
||||
ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'build-info.json') -Encoding UTF8
|
||||
Write-Host "Experimental native K directory: $OutputDirectory"
|
||||
@@ -0,0 +1,30 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$DlssDll, [string]$PackageRoot, [string]$OutputDirectory, [string]$GpuArch)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $GpuArch
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo "build/native-m-$GpuArch" }
|
||||
if (![IO.Path]::IsPathRooted($DlssDll) -or !(Test-Path -LiteralPath $DlssDll)) {
|
||||
throw 'Supply the absolute path to your local nvngx_dlss.dll.'
|
||||
}
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
foreach ($object in Get-ChildItem -LiteralPath $OutputDirectory -Filter '*.hsaco') {
|
||||
Assert-D4RCodeObjectTarget $object.FullName $GpuArch
|
||||
}
|
||||
foreach ($layer in @('enc1','enc2','enc3_tube','dec2','dec1')) {
|
||||
$name = "rrlite_${layer}_4x4"
|
||||
$source = Join-Path $PackageRoot "experimental/m/${name}_${GpuArch}.hsaco"
|
||||
if (!(Test-Path -LiteralPath $source)) { throw "Native M module missing: $source" }
|
||||
Assert-D4RCodeObjectTarget $source $GpuArch
|
||||
Copy-Item -LiteralPath $source -Destination (Join-Path $OutputDirectory "$name.hsaco") -Force
|
||||
}
|
||||
& python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $OutputDirectory $DlssDll
|
||||
if ($LASTEXITCODE) { throw "Native manifest generation failed ($LASTEXITCODE)" }
|
||||
@{architecture=$GpuArch; family='M'; numerics='SWIN_EXACT; SWIN_EXACT_PV; FP16 widening; native FP8 disabled';
|
||||
dlssSha256=(Get-FileHash -LiteralPath $DlssDll -Algorithm SHA256).Hash;
|
||||
validation='staged only; GPU execution and DLSS capture/reference validation required';
|
||||
modules=@(Get-ChildItem -LiteralPath $OutputDirectory -Filter '*.hsaco' | Get-FileHash -Algorithm SHA256)} |
|
||||
ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'build-info.json') -Encoding UTF8
|
||||
Write-Host "Experimental native M directory: $OutputDirectory"
|
||||
@@ -0,0 +1,84 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$NgxCore, [string]$DlssDll, [string]$NativeRoot,
|
||||
[string]$ZludaRoot, [string]$PackageRoot,
|
||||
[string]$Resolution = '3840x2160',
|
||||
[string]$InputResolution,
|
||||
[switch]$ProfileGpuBoundary,
|
||||
[switch]$ProfileKernelsDeferred,
|
||||
[switch]$BatchInputCopies,
|
||||
[ValidateSet('baseline','packed','unorm','depth-stencil')][string]$PixelProfile = 'depth-stencil',
|
||||
[string]$OutputDirectory
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$NgxCore) { $NgxCore = Join-Path $repo '_nvngx.dll' }
|
||||
if (!$DlssDll) { $DlssDll = Join-Path $repo 'nvngx_dlss.dll' }
|
||||
if (!$ZludaRoot) { $ZludaRoot = Join-Path $repo 'dist/zluda-windows-api-profile' }
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-command-list' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$NativeRoot) { $NativeRoot = Join-Path $repo "build/native-k-$GpuArch" }
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/async-k-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
if ($Resolution -notmatch '^([0-9]+)x([0-9]+)$') { throw 'Resolution must be WIDTHxHEIGHT' }
|
||||
$width=$Matches[1]; $height=$Matches[2]
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$settings = @{D4R_DIAG_BURST='1'; D4R_DIAG_RECREATE=$null; D4R_ASYNC_INTEROP=$null;
|
||||
D4R_BATCH_INPUT_COPIES=$null;
|
||||
D4R_ZLUDA_NATIVE_DIR=$NativeRoot; D4R_QUIET_API='1'; D4R_VALIDATE_OUTPUT='1';
|
||||
D4R_PROFILE_STAGES='1'; D4R_ZLUDA_PROFILE=$null; D4R_ZLUDA_PROFILE_API=$null;
|
||||
D4R_ZLUDA_PROFILE_DEFERRED=$(if ($ProfileKernelsDeferred) { '1' } else { $null });
|
||||
D4R_ZLUDA_PROFILE_ALLOW_LEGACY=$(if ($ProfileKernelsDeferred) { '1' } else { $null });
|
||||
D4R_ZLUDA_PROFILE_EVERY=$(if ($ProfileKernelsDeferred) { '1' } else { $null });
|
||||
D4R_PROFILE_GPU_BOUNDARY=$(if ($ProfileGpuBoundary) { '1' } else { $null });
|
||||
ZLUDA_CACHE_DIR=(Join-Path $repo 'build/zluda-cache-windows');
|
||||
PYTHONPATH=(Join-Path $repo '.tools/python/vendor')}
|
||||
$old=@{}
|
||||
try {
|
||||
foreach ($key in $settings.Keys) {
|
||||
$old[$key]=[Environment]::GetEnvironmentVariable($key,'Process')
|
||||
[Environment]::SetEnvironmentVariable($key,$settings[$key],'Process')
|
||||
}
|
||||
foreach ($mode in @('burst','recreate')) {
|
||||
[Environment]::SetEnvironmentVariable('D4R_DIAG_RECREATE', $(if ($mode -eq 'recreate') { '1' } else { $null }), 'Process')
|
||||
foreach ($variant in @('sync','async')) {
|
||||
[Environment]::SetEnvironmentVariable('D4R_ASYNC_INTEROP', $(if ($variant -eq 'async') { '1' } else { $null }), 'Process')
|
||||
[Environment]::SetEnvironmentVariable('D4R_BATCH_INPUT_COPIES', $(if ($BatchInputCopies -and $variant -eq 'async') { '1' } else { $null }), 'Process')
|
||||
$output=Join-Path $OutputDirectory "$variant-$mode"
|
||||
$arguments=@{RuntimeProfile='therock'; ZludaRoot=$ZludaRoot; PackageRoot=$PackageRoot;
|
||||
NgxCore=$NgxCore; DlssDll=$DlssDll; NgxOnly=$true; NgxMode='d3d12'; NgxAbi='project-legacy';
|
||||
Preset=11; NgxCreateFlags=11; NgxOutputResolution=$Resolution; RequireNativeNetwork=$true;
|
||||
NgxInputResolution=$InputResolution;
|
||||
CommandListBackend=$true; EarlyIndirectProbe=$true; PixelProfile=$PixelProfile;
|
||||
BarrierMode='inherited-legacy'; Iterations=3; CaptureExceptions=$true; TimeoutSeconds=600;
|
||||
OptiScalerDll=(Join-Path $repo 'dist/optiscaler-windows-d4r/OptiScaler.dll'); OutputDirectory=$output}
|
||||
& (Join-Path $PSScriptRoot 'test-windows-rdna4.ps1') @arguments
|
||||
if ($LASTEXITCODE) { throw "Failed $variant-$mode; logs: $output" }
|
||||
if ($ProfileKernelsDeferred) {
|
||||
$stderr=Get-Content -LiteralPath (Join-Path $output 'd3d12-evaluate-preset-11.stderr.log') -Raw
|
||||
if ($stderr -match '(?m)^D4R_KERNEL_PROFILE .*serializing=1') { throw 'Unexpected serializing kernel profiler.' }
|
||||
foreach ($layer in @('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0')) {
|
||||
$pattern='(?m)^D4R_KERNEL_PROFILE(?:_INVALID)? kernel="dltss_pwin_' + $layer + '_layer" backend=native phase=main '
|
||||
$events=[regex]::Matches($stderr,$pattern)
|
||||
if ($events.Count -ne 3) { throw "Expected three collected $layer events for $variant-$mode, found $($events.Count)." }
|
||||
}
|
||||
}
|
||||
if ($ProfileGpuBoundary -and $variant -eq 'async') {
|
||||
$stdout=Get-Content -LiteralPath (Join-Path $output 'd3d12-evaluate-preset-11.stdout.log') -Raw
|
||||
$timings=[regex]::Matches($stdout, '(?m)^D4R_GPU_BOUNDARY [^\r\n]*diagnostics_cpu_bytes=32 serializing=0\r?$')
|
||||
if ($timings.Count -ne 3) { throw "Expected three completed GPU timing records for $variant-$mode, found $($timings.Count)." }
|
||||
}
|
||||
}
|
||||
& python.exe (Join-Path $repo 'tools/windows/frame_compare.py') --reference (Join-Path $OutputDirectory "sync-$mode") --actual (Join-Path $OutputDirectory "async-$mode") --width $width --height $height --exact |
|
||||
Tee-Object -FilePath (Join-Path $OutputDirectory "$mode-comparison.log")
|
||||
if ($LASTEXITCODE) { throw "Async $mode output mismatch" }
|
||||
}
|
||||
@{passed=$true; architecture=$GpuArch; preset=11; resolution=$Resolution;
|
||||
inputResolution=$InputResolution;
|
||||
modes=@('burst','recreate'); framesPerMode=3; exactRgb=$true; gpuBoundaryTiming=[bool]$ProfileGpuBoundary;
|
||||
deferredKernelTiming=[bool]$ProfileKernelsDeferred; batchInputs=[bool]$BatchInputCopies; pixelProfile=$PixelProfile} |
|
||||
ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory 'validation.json') -Encoding UTF8
|
||||
Write-Host "PASS asynchronous K burst/recreate: $OutputDirectory"
|
||||
} finally {
|
||||
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
param(
|
||||
[int]$Iterations = 32,
|
||||
[switch]$NoHooks,
|
||||
[switch]$SmallSeed,
|
||||
[switch]$DebugLayer,
|
||||
[switch]$Warp,
|
||||
[switch]$NoHip,
|
||||
[ValidateSet('basic','indirect','indirect-root-reset','initial-pso')][string]$Mode = 'indirect',
|
||||
[string]$OutputDirectory = 'test-results/d3d12-command-development'
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repo = (Resolve-Path "$PSScriptRoot/../..").Path
|
||||
Push-Location $repo
|
||||
$names = @('D4R_COMMAND_PROBE_NO_HOOKS','D4R_COMMAND_PROBE_SMALL_SEED','D4R_COMMAND_DEBUG_SDK','D4R_DIAG_DIR','D4R_COMMAND_PROBE_WARP','D4R_COMMAND_PROBE_NO_HIP')
|
||||
$saved = @{}
|
||||
foreach ($name in $names) { $saved[$name] = [Environment]::GetEnvironmentVariable($name,'Process') }
|
||||
try {
|
||||
if ($Iterations -lt 1 -or $Iterations -gt 1024) { throw 'Iterations must be 1..1024' }
|
||||
$cmake = "$repo/.tools/python/cmake/data/bin/cmake.exe"
|
||||
$build = "$repo/build/windows-rdna4-therock"
|
||||
$hip = "$repo/.tools/therock-10.2.0a20260929/_rocm_sdk_core"
|
||||
$output = [IO.Path]::GetFullPath((Join-Path $repo $OutputDirectory))
|
||||
New-Item -ItemType Directory -Force -Path $output | Out-Null
|
||||
$target = 'd4r_d3d12_command_probe'
|
||||
if ($DebugLayer) {
|
||||
$sdk = "$repo/.tools/agility-1.619.5"
|
||||
if (!(Test-Path "$sdk/build/native/bin/x64/d3d12SDKLayers.dll")) { throw 'Unpack Microsoft.Direct3D.D3D12 1.619.5 into .tools/agility-1.619.5 for this optional debug probe' }
|
||||
& $cmake -S . -B $build "-DD4R_AGILITY_ROOT=$sdk" > "$output/configure.log"
|
||||
if ($LASTEXITCODE) { throw 'Debug probe configuration failed' }
|
||||
$target = 'd4r_d3d12_command_debug_probe'
|
||||
}
|
||||
& $cmake --build $build --target d4r_nvngx_windows $target > "$output/build.log"
|
||||
if ($LASTEXITCODE) { throw "Build failed: $output/build.log" }
|
||||
[Environment]::SetEnvironmentVariable($names[0], $(if ($NoHooks) { '1' } else { $null }), 'Process')
|
||||
[Environment]::SetEnvironmentVariable($names[1], $(if ($SmallSeed) { '1' } else { $null }), 'Process')
|
||||
[Environment]::SetEnvironmentVariable($names[2], $(if ($DebugLayer) { '1' } else { $null }), 'Process')
|
||||
[Environment]::SetEnvironmentVariable($names[3], $output, 'Process')
|
||||
[Environment]::SetEnvironmentVariable($names[4], $(if ($Warp) { '1' } else { $null }), 'Process')
|
||||
[Environment]::SetEnvironmentVariable($names[5], $(if ($NoHip -or $Warp) { '1' } else { $null }), 'Process')
|
||||
# PowerShell 5 treats native stderr as ErrorRecord: retain it without
|
||||
# converting it into a terminating exception, then check the process code.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
& "$build/$target.exe" --hip-root $hip --module "$build/d4r_nvngx.dll" --interop-mode $Mode --iterations $Iterations > "$output/stdout.log" 2> "$output/stderr.log"
|
||||
$code = $LASTEXITCODE
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Get-Content "$output/stdout.log", "$output/stderr.log" | Select-String 'D3D12_VALIDATION|DRED|DEVICE_REMOVED|PROBE_GPU|DIFFERENT|FAIL|PASS|installed'
|
||||
Write-Host "D3D12 command diagnostic logs: $output"
|
||||
if ($code) { throw "D3D12 probe exit=$code" }
|
||||
} finally {
|
||||
foreach ($name in $names) { [Environment]::SetEnvironmentVariable($name,$saved[$name],'Process') }
|
||||
Pop-Location
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
# d4r-check.ps1 against fixture installs. No GPU, no NVIDIA binaries: the packaging test's
|
||||
# synthetic release is the install, a fake inventory file stands in for the GPU query, and
|
||||
# fixture text files stand in for the NVIDIA DLLs (pinned in a fixture dll-pins.txt).
|
||||
[CmdletBinding()]
|
||||
param([string]$OutputDirectory)
|
||||
$ErrorActionPreference='Stop'
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/d4r-check/'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
& (Join-Path $PSScriptRoot 'test-windows-release-package.ps1') -OutputDirectory (Join-Path $OutputDirectory 'package') | Out-Null
|
||||
$release=Join-Path $OutputDirectory 'package/release'
|
||||
$check=Join-Path $repo 'scripts/windows/d4r-check.ps1'
|
||||
$powershell="$env:SystemRoot/System32/WindowsPowerShell/v1.0/powershell.exe"
|
||||
$results=@()
|
||||
function Sha([string]$path) { (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash }
|
||||
function Set-ManifestHash([string]$game,[string]$relative) {
|
||||
$path=Join-Path $game 'd4r/package.json'
|
||||
$manifest=Get-Content -LiteralPath $path -Raw | ConvertFrom-Json
|
||||
foreach ($entry in $manifest.files) { if ($entry.path -eq $relative) { $entry.sha256=Sha (Join-Path $game ($relative -replace '/','\')) } }
|
||||
$manifest | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $path -Encoding UTF8
|
||||
}
|
||||
function New-Install([string]$name) {
|
||||
$game=Join-Path $OutputDirectory "cases/$name"
|
||||
New-Item -ItemType Directory -Force (Split-Path $game) | Out-Null
|
||||
Copy-Item -LiteralPath $release -Destination $game -Recurse
|
||||
Set-Content -LiteralPath (Join-Path $game 'Game.exe') 'fixture' -Encoding ASCII
|
||||
New-Item -ItemType Directory -Force (Join-Path $game 'd4r/dlss') | Out-Null
|
||||
Set-Content -LiteralPath (Join-Path $game 'd4r/dlss/nvngx_dlss.dll') 'fixture dlss' -Encoding ASCII
|
||||
Set-Content -LiteralPath (Join-Path $game 'd4r/ngx/_nvngx.dll') 'fixture core' -Encoding ASCII
|
||||
$unrelated=('B'*64)
|
||||
@('# fixture pins',
|
||||
"dlss $(Sha (Join-Path $game 'd4r/dlss/nvngx_dlss.dll')) 310.9.1 validated",
|
||||
"dlss $unrelated 310.7.0 unvalidated",
|
||||
"ngx $(Sha (Join-Path $game 'd4r/ngx/_nvngx.dll')) 32.0.16.1714 validated") |
|
||||
Set-Content -LiteralPath (Join-Path $game 'd4r/dll-pins.txt') -Encoding ASCII
|
||||
Set-ManifestHash $game 'd4r/dll-pins.txt'
|
||||
return $game
|
||||
}
|
||||
function Write-Inventory([string]$name,[string]$arch,[string]$gpu='AMD Radeon RX 7700 XT') {
|
||||
$path=Join-Path $OutputDirectory "inventory-$name.json"
|
||||
@{devices=@(@{name=$gpu; architecture=$arch; wavefrontSize=32})} | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $path -Encoding UTF8
|
||||
return $path
|
||||
}
|
||||
function Run-Check([string]$game,[string]$inventory,[string[]]$More=@()) {
|
||||
$ErrorActionPreference='Continue' # native stderr must not become a terminating error
|
||||
$text=& $powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $game 'd4r/d4r-check.ps1') -GameFolder $game -InventoryFile $inventory @More 2>&1 | Out-String
|
||||
return @{exit=$LASTEXITCODE; text=$text}
|
||||
}
|
||||
function Expect([string]$case,$run,[int]$exit,[string[]]$patterns) {
|
||||
if ($run.exit -ne $exit) { throw "$case : exit $($run.exit), expected $exit`n$($run.text)" }
|
||||
foreach ($pattern in $patterns) { if ($run.text -notmatch $pattern) { throw "$case : output lacks /$pattern/`n$($run.text)" } }
|
||||
$script:results+=@{case=$case; passed=$true; gpuKernelsExecuted=$false}
|
||||
}
|
||||
|
||||
# 1. Healthy install.
|
||||
$game=New-Install 'healthy'; $inv=Write-Inventory 'a' 'gfx1101'
|
||||
Expect 'healthy-install' (Run-Check $game $inv) 0 @('ok\s+d4r\\dlss\\nvngx_dlss\.dll \(nvngx_dlss\.dll 310\.9\.1, tested on Windows\)','ok\s+d4r\\ngx\\_nvngx\.dll',
|
||||
'ok\s+GPU AMD Radeon RX 7700 XT \(gfx1101\): build present, 16 native kernels','ok\s+all 17 GPU code objects are built for gfx1101',
|
||||
'ok\s+no vkd3d/Wine d3d12\.dll','ok\s+d4r\.ini is valid \(model K\)','compile-tested','Everything d4r needs is in place')
|
||||
|
||||
# 2. NVIDIA DLL problems, each with the fix.
|
||||
$game=New-Install 'dll-missing'; Remove-Item -LiteralPath (Join-Path $game 'd4r/dlss/nvngx_dlss.dll')
|
||||
Expect 'dll-missing' (Run-Check $game $inv) 1 @("MISSING\s+d4r\\dlss\\nvngx_dlss\.dll: copy NVIDIA's nvngx_dlss\.dll here \(version 310\.9\.1 or 310\.7\.0","1 thing\(s\) to fix")
|
||||
$game=New-Install 'dll-wrong'; Set-Content -LiteralPath (Join-Path $game 'd4r/dlss/nvngx_dlss.dll') 'some other version' -Encoding ASCII
|
||||
Expect 'dll-wrong-version' (Run-Check $game $inv) 1 @('MISSING\s+d4r\\dlss\\nvngx_dlss\.dll is version \S+, which d4r does not accept\. d4r on Windows needs exactly 310\.9\.1 or 310\.7\.0')
|
||||
$game=New-Install 'dll-less-tested'; Set-Content -LiteralPath (Join-Path $game 'd4r/dlss/nvngx_dlss.dll') 'fixture dlss 310.7' -Encoding ASCII
|
||||
@('dlss '+(Sha (Join-Path $game 'd4r/dlss/nvngx_dlss.dll'))+' 310.7.0 unvalidated','ngx '+(Sha (Join-Path $game 'd4r/ngx/_nvngx.dll'))+' 32.0.16.1714 validated') | Set-Content -LiteralPath (Join-Path $game 'd4r/dll-pins.txt') -Encoding ASCII
|
||||
Set-ManifestHash $game 'd4r/dll-pins.txt'
|
||||
Expect 'dll-less-tested-accepted' (Run-Check $game $inv) 0 @('ok\s+d4r\\dlss\\nvngx_dlss\.dll \(nvngx_dlss\.dll 310\.7\.0\)','note\s+nvngx_dlss\.dll 310\.7\.0 is accepted but has had less Windows testing')
|
||||
|
||||
# 3. Leftovers, edited files, a replaced dxgi.dll, and a broken OptiScaler.ini.
|
||||
$game=New-Install 'vkd3d'; Set-Content -LiteralPath (Join-Path $game 'd3d12.dll') 'vkd3d-proton fixture' -Encoding ASCII
|
||||
Expect 'vkd3d-proxy' (Run-Check $game $inv) 1 @('MISSING\s+d3d12\.dll in the game folder is vkd3d-proton/Wine')
|
||||
$game=New-Install 'dxgi-replaced'; Set-Content -LiteralPath (Join-Path $game 'dxgi.dll') 'another dxgi' -Encoding ASCII
|
||||
Expect 'foreign-dxgi' (Run-Check $game $inv) 0 @('note\s+dxgi\.dll is not the one that came with d4r')
|
||||
$game=New-Install 'opti-path'; (Get-Content -LiteralPath (Join-Path $game 'OptiScaler.ini')) -replace '^NvngxPath=.*$','NvngxPath=nowhere' | Set-Content -LiteralPath (Join-Path $game 'OptiScaler.ini') -Encoding ASCII
|
||||
Expect 'optiscaler-ini-path' (Run-Check $game $inv) 1 @('MISSING\s+OptiScaler\.ini NvngxPath = nowhere does not hold _nvngx\.dll')
|
||||
|
||||
# 4. d4r.ini mistakes are notes with what will happen instead.
|
||||
$game=New-Install 'ini'; Set-Content -LiteralPath (Join-Path $game 'd4r/d4r.ini') "[DLSS]`nModel = E`nShowWatermark = sometimes`n[Latency]`nFrameAge = 1`n[Debug]`nLog = loud`n" -Encoding ASCII
|
||||
Expect 'ini-mistakes' (Run-Check $game $inv) 0 @("note\s+d4r\.ini: \[DLSS\] Model 'E' is not available on Windows",'must be true or false, not ''sometimes''','\[Latency\] FrameAge is not a Windows setting','Log must be normal or verbose')
|
||||
|
||||
# 5. GPU cases.
|
||||
$game=New-Install 'gpu-no-build'; Remove-Item -LiteralPath (Join-Path $game 'd4r/_nvngx_gfx1103.dll')
|
||||
Expect 'gpu-without-build' (Run-Check $game (Write-Inventory 'b' 'gfx1103' 'AMD Radeon 780M')) 1 @('MISSING\s+GPU AMD Radeon 780M \(gfx1103\): this release has no complete build for it \(missing: d4r\\_nvngx_gfx1103\.dll\)')
|
||||
$game=New-Install 'gpu-rdna2'
|
||||
Expect 'gpu-unsupported' (Run-Check $game (Write-Inventory 'c' 'gfx1030' 'AMD Radeon RX 6800')) 1 @('MISSING\s+no supported RDNA3/RDNA4 GPU found by the AMD runtime\. It sees: AMD Radeon RX 6800 \(gfx1030\)')
|
||||
$game=New-Install 'gpu-wrong-object'
|
||||
$other=@(Get-ChildItem -LiteralPath (Join-Path $game 'd4r/native/gfx1100') -Filter '*.hsaco')[0].FullName
|
||||
Copy-Item -LiteralPath $other -Destination (Join-Path $game 'd4r/native/gfx1101/kernel3.hsaco') -Force
|
||||
Expect 'code-object-for-another-gpu' (Run-Check $game $inv) 1 @('MISSING\s+kernel3\.hsaco is not built for gfx1101','differs from the release')
|
||||
|
||||
# 6. What the last game start said, and the bug-report ZIP.
|
||||
$game=New-Install 'log-failure'
|
||||
Set-Content -LiteralPath (Join-Path $game 'd4r/d4r_nvngx.log') "d4r: DLSS disabled: Wrong nvngx_dlss.dll: you have version 1.2.3.4" -Encoding ASCII
|
||||
Expect 'log-reports-failure' (Run-Check $game $inv) 1 @('MISSING\s+last game start: DLSS disabled: Wrong nvngx_dlss\.dll')
|
||||
$game=New-Install 'log-ok'
|
||||
Set-Content -LiteralPath (Join-Path $game 'd4r/d4r_nvngx.log') "d4r: player mode, model=K validate=0 async=0 log=normal, architecture=gfx1101" -Encoding ASCII
|
||||
Expect 'log-reports-ok' (Run-Check $game $inv) 0 @('ok\s+last game start: player mode, model=K')
|
||||
$game=New-Install 'report'; $reportDir=Join-Path $OutputDirectory 'reports'; New-Item -ItemType Directory -Force $reportDir | Out-Null
|
||||
Set-Content -LiteralPath (Join-Path $game 'd4r/d4r_nvngx.log') "d4r: DLSS disabled: test failure" -Encoding ASCII
|
||||
$run=Run-Check $game $inv @('-Report','-ReportDirectory',$reportDir)
|
||||
$zip=@(Get-ChildItem -LiteralPath $reportDir -Filter 'd4r-report-*.zip')
|
||||
if ($zip.Count -ne 1) { throw "Expected one report ZIP.`n$($run.text)" }
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
$archive=[IO.Compression.ZipFile]::OpenRead($zip[0].FullName)
|
||||
try { $names=@($archive.Entries | ForEach-Object { $_.Name }) } finally { $archive.Dispose() }
|
||||
foreach ($required in @('check.txt','d4r_nvngx.log','d4r.ini','OptiScaler.ini','files.sha256.txt','gpu-inventory.json')) { if ($names -notcontains $required) { throw "Report lacks $required (has: $($names -join ', '))" } }
|
||||
if ($names -contains 'nvngx_dlss.dll' -or $names -contains '_nvngx.dll') { throw 'The report must never contain NVIDIA DLLs.' }
|
||||
if ($run.text -notmatch 'Bug report written') { throw 'Report location was not printed.' }
|
||||
$results+=@{case='bug-report-zip'; passed=$true; entries=$names}
|
||||
|
||||
# 7. No d4r folder at all.
|
||||
$empty=Join-Path $OutputDirectory 'cases/empty'; New-Item -ItemType Directory -Force $empty | Out-Null
|
||||
$ErrorActionPreference='Continue'
|
||||
$text=& $powershell -NoProfile -ExecutionPolicy Bypass -File $check -GameFolder $empty 2>&1 | Out-String
|
||||
$exitEmpty=$LASTEXITCODE; $ErrorActionPreference='Stop'
|
||||
if ($exitEmpty -ne 1 -or $text -notmatch 'no d4r folder here') { throw "A folder without d4r must be reported.`n$text" }
|
||||
$results+=@{case='no-d4r-folder'; passed=$true}
|
||||
|
||||
$results | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Write-Host "PASS D4R_CHECK cases=$($results.Count) Report: $OutputDirectory"
|
||||
@@ -0,0 +1,103 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$ZludaRoot, [string]$PackageRoot, [string]$HipRoot, [string]$OutputDirectory)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$ZludaRoot) { $ZludaRoot = Join-Path $repo 'dist/zluda-windows-deferred-profile' }
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-command-list' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/deferred-profile-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$settings = @{
|
||||
D4R_ZLUDA_PROFILE=$null; D4R_ZLUDA_PROFILE_DEFERRED='1'; D4R_ZLUDA_PROFILE_EVERY='1';
|
||||
D4R_ZLUDA_PROFILE_ALLOW_LEGACY=$null;
|
||||
D4R_DIAG_NONBLOCKING_STREAM='1'; D4R_DIAG_QUEUE_PTX=$null;
|
||||
D4R_DIAG_DIR=$OutputDirectory;
|
||||
ZLUDA_CACHE_DIR=(Join-Path $repo 'build/zluda-cache-windows');
|
||||
PATH=((Join-Path $HipRoot 'bin') + ';' + $ZludaRoot + ';' + $env:PATH)
|
||||
}
|
||||
$old=@{}; $cases=@()
|
||||
function RunProbe([string[]]$Arguments, [string]$Stdout, [string]$Stderr) {
|
||||
$info=[Diagnostics.ProcessStartInfo]::new()
|
||||
$info.FileName=Join-Path $PackageRoot 'bin/d4r_cuda_driver_probe.exe'
|
||||
$info.Arguments=$Arguments -join ' '
|
||||
$info.UseShellExecute=$false; $info.CreateNoWindow=$true
|
||||
$info.RedirectStandardOutput=$true; $info.RedirectStandardError=$true
|
||||
$p=[Diagnostics.Process]::new(); $p.StartInfo=$info
|
||||
$outFile=$null; $errFile=$null; $outTask=$null; $errTask=$null
|
||||
try {
|
||||
if (!$p.Start()) { throw 'Could not launch the CUDA diagnostic.' }
|
||||
$outFile=[IO.File]::Open($Stdout,[IO.FileMode]::Create,[IO.FileAccess]::Write,[IO.FileShare]::Read)
|
||||
$errFile=[IO.File]::Open($Stderr,[IO.FileMode]::Create,[IO.FileAccess]::Write,[IO.FileShare]::Read)
|
||||
$outTask=$p.StandardOutput.BaseStream.CopyToAsync($outFile)
|
||||
$errTask=$p.StandardError.BaseStream.CopyToAsync($errFile)
|
||||
$timer=[Diagnostics.Stopwatch]::StartNew()
|
||||
while (!$p.WaitForExit(1000)) {
|
||||
if ($timer.Elapsed.TotalSeconds -ge 120) { throw "CUDA diagnostic timed out; logs: $OutputDirectory" }
|
||||
}
|
||||
$p.WaitForExit()
|
||||
[void]$outTask.GetAwaiter().GetResult(); [void]$errTask.GetAwaiter().GetResult()
|
||||
return $p.ExitCode
|
||||
} finally {
|
||||
if (!$p.HasExited) { $p.Kill(); $p.WaitForExit() }
|
||||
if ($outTask) { try { [void]$outTask.GetAwaiter().GetResult() } catch {} }
|
||||
if ($errTask) { try { [void]$errTask.GetAwaiter().GetResult() } catch {} }
|
||||
if ($outFile) { $outFile.Dispose() }; if ($errFile) { $errFile.Dispose() }
|
||||
$p.Dispose()
|
||||
}
|
||||
}
|
||||
try {
|
||||
foreach ($key in $settings.Keys) {
|
||||
$old[$key]=[Environment]::GetEnvironmentVariable($key,'Process')
|
||||
[Environment]::SetEnvironmentVariable($key,$settings[$key],'Process')
|
||||
}
|
||||
foreach ($context in @('primary','created')) {
|
||||
foreach ($queued in @($false,$true)) {
|
||||
$name="$context-$(if ($queued) { 'queued' } else { 'single' })"
|
||||
[Environment]::SetEnvironmentVariable('D4R_DIAG_QUEUE_PTX', $(if ($queued) { '1' } else { $null }), 'Process')
|
||||
$iterations=129
|
||||
$stdout=Join-Path $OutputDirectory "$name.stdout.log"
|
||||
$stderr=Join-Path $OutputDirectory "$name.stderr.log"
|
||||
# Native Windows argument parsing: quote absolute paths explicitly.
|
||||
$arguments=@('--hip-root', ('"' + $HipRoot + '"'), '--cuda-dll', ('"' + (Join-Path $ZludaRoot 'nvcuda.dll') + '"'),
|
||||
'--context', $context, '--iterations', "$iterations")
|
||||
$exitCode=RunProbe $arguments $stdout $stderr
|
||||
if ($exitCode) { throw "$name failed ($exitCode); logs: $OutputDirectory" }
|
||||
$out=Get-Content -LiteralPath $stdout -Raw
|
||||
$err=Get-Content -LiteralPath $stderr -Raw
|
||||
if ($out -notmatch "PASS CUDA architecture=$GpuArch" -or $out -notmatch 'guard_verified=1') { throw "$name did not verify PTX output" }
|
||||
$records=[regex]::Matches($err, '(?m)^D4R_KERNEL_PROFILE kernel="d4r_ptx_pattern" [^\r\n]*serializing=0 deferred=1 [^\r\n]+')
|
||||
$invalid=[regex]::Matches($err, '(?m)^D4R_KERNEL_PROFILE_INVALID kernel="d4r_ptx_pattern" [^\r\n]*reason=invalid_elapsed_time serializing=0 deferred=1 [^\r\n]+')
|
||||
if ($records.Count + $invalid.Count -ne $iterations -or $err -match 'serializing=1|D4R_KERNEL_PROFILE_SKIPPED') {
|
||||
throw "$name expected $iterations collected deferred records, found $($records.Count) valid / $($invalid.Count) invalid"
|
||||
}
|
||||
foreach ($record in $records) {
|
||||
if ($record.Value -notmatch 'gpu_ms=([0-9.]+)') {
|
||||
throw "$name contains an invalid GPU time"
|
||||
}
|
||||
$gpuMs=[double]::Parse($Matches[1], [Globalization.CultureInfo]::InvariantCulture)
|
||||
if ([double]::IsNaN($gpuMs) -or [double]::IsInfinity($gpuMs)) { throw "$name contains an invalid GPU time" }
|
||||
}
|
||||
$cases += @{context=$context; queued=$queued; launches=$iterations; collected=($records.Count+$invalid.Count);
|
||||
validTimings=$records.Count; invalidTimings=$invalid.Count; exitCode=$exitCode}
|
||||
Write-Host "PASS ${name}: $($records.Count) valid / $($invalid.Count) invalid deferred timings; exact PTX output and guards"
|
||||
}
|
||||
}
|
||||
# Legacy default-stream timing markers would add stream dependencies.
|
||||
[Environment]::SetEnvironmentVariable('D4R_DIAG_NONBLOCKING_STREAM',$null,'Process')
|
||||
[Environment]::SetEnvironmentVariable('D4R_DIAG_QUEUE_PTX',$null,'Process')
|
||||
$stdout=Join-Path $OutputDirectory 'default-stream.stdout.log'; $stderr=Join-Path $OutputDirectory 'default-stream.stderr.log'
|
||||
$arguments=@('--hip-root', ('"' + $HipRoot + '"'), '--cuda-dll', ('"' + (Join-Path $ZludaRoot 'nvcuda.dll') + '"'), '--iterations', '3')
|
||||
$exitCode=RunProbe $arguments $stdout $stderr
|
||||
$out=Get-Content -LiteralPath $stdout -Raw; $err=Get-Content -LiteralPath $stderr -Raw
|
||||
if ($exitCode -or $out -notmatch "PASS CUDA architecture=$GpuArch" -or
|
||||
$err -notmatch 'reason=legacy_default_stream' -or $err -match '(?m)^D4R_KERNEL_PROFILE ') {
|
||||
throw 'Default-stream diagnostics did not safely skip timing events.'
|
||||
}
|
||||
@{passed=$true; architecture=$GpuArch; cases=$cases; defaultStreamSkipped=$true; addedCompletionWaits=0} |
|
||||
ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'validation.json') -Encoding UTF8
|
||||
Write-Host "PASS deferred kernel profiling: $OutputDirectory"
|
||||
} finally {
|
||||
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$LogRoot, [string]$OutputDirectory)
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'game-summary.ps1')
|
||||
$cases=@(
|
||||
@{name='empty-streams-debugger-refused'; preset=11; stdout=''; stderr=''; debugger='EXIT code=0xdeadc0de'; session='process-failed-before-dlss-frames'},
|
||||
@{name='launcher-exit'; preset=11; stdout=''; stderr=''; debugger='EXIT code=0x0'; session='process-exited-without-dlss-frames'},
|
||||
@{name='feature-not-found'; preset=11; stdout='D4R_CAPABILITIES available=0 feature_init=0xbad00004 needs_driver=0'; stderr='D4R_WINDOWS_FAILURE unavailable'; debugger='EXIT code=0xc0000409'; session='ngx-feature-not-found'},
|
||||
@{name='complete-frame-k'; preset=11; native=1; kernel='dltss_pwin_enc0_layer'; stdout="D4R_FRAME cpu_frame_copies=0 frame_age=0 interop_ngx_ms=12.4`nD4R_OUTPUT_VALIDATION elements=3 nan=0 inf=0 diagnostics_cpu_bytes=8";
|
||||
stderr='[d4r-launch] kernel="dltss_pwin_enc0_layer" backend=native'; debugger='EXIT code=0x0'; session='completed-dlss-frames'},
|
||||
@{name='complete-frame-e'; preset=5; native=0; kernel=$null; stdout="D4R_FRAME cpu_frame_copies=0 frame_age=0 interop_ngx_ms=14.2`nD4R_OUTPUT_VALIDATION elements=3 nan=0 inf=0 diagnostics_cpu_bytes=8";
|
||||
stderr=''; debugger='EXIT code=0x0'; session='completed-dlss-frames'},
|
||||
@{name='complete-frame-l'; preset=12; native=1; kernel='rrlite_enc1_4x4'; stdout="D4R_FRAME cpu_frame_copies=0 frame_age=0 interop_ngx_ms=18.4`nD4R_OUTPUT_VALIDATION elements=3 nan=0 inf=0 diagnostics_cpu_bytes=8";
|
||||
stderr='[d4r-launch] kernel="rrlite_enc1_4x4" backend=native'; debugger='EXIT code=0x0'; session='completed-dlss-frames'},
|
||||
@{name='truncated-frame'; preset=13; stdout='D4R_FRAME cpu_frame_copies=0 frame_age=0 interop_ngx_ms='; stderr=''; debugger=''; session='diagnostic-timeout-without-dlss-frames'}
|
||||
)
|
||||
$results=@()
|
||||
foreach ($case in $cases) {
|
||||
$summary=Get-D4RGameSummary -Stdout $case.stdout -Stderr $case.stderr -Debugger $case.debugger -Preset $case.preset
|
||||
if ($summary.session -ne $case.session) { throw "Wrong session classification: $($case.name)" }
|
||||
if ($case.name -like 'complete-frame-*') {
|
||||
$kernelCount=if ($case.kernel) { @($summary.kernels | Where-Object kernel -eq $case.kernel).Count } else { 0 }
|
||||
if ($summary.completedFrames -ne 1 -or $summary.outputGpuChecks -ne 1 -or $summary.nativeLaunches -ne $case.native -or
|
||||
$kernelCount -ne $case.native -or $summary.preset -ne $case.preset -or
|
||||
$summary.nonfiniteOutputs -or $summary.previousFrameOutputs -or $summary.cpuImageCopyFrames) { throw 'Complete frame counters are wrong.' }
|
||||
} elseif ($summary.completedFrames -ne 0) { throw 'A failed or incomplete run was counted as a frame.' }
|
||||
$results+=@{case=$case.name; passed=$true; summary=$summary}
|
||||
}
|
||||
if ($LogRoot) {
|
||||
foreach ($entry in @{ 'acbf-k'='process-failed-before-dlss-frames'; 'cyberpunk-k'='ngx-feature-not-found';
|
||||
'cyberpunk-m'='ngx-feature-not-found'; 'sm2-k'='process-exited-without-dlss-frames' }.GetEnumerator()) {
|
||||
$dir=Get-ChildItem -LiteralPath (Join-Path $LogRoot $entry.Key) -Directory | Select-Object -First 1
|
||||
$summary=Get-D4RGameSummary -Stdout ([IO.File]::ReadAllText((Join-Path $dir.FullName 'd4r.stdout.log'))) `
|
||||
-Stderr ([IO.File]::ReadAllText((Join-Path $dir.FullName 'd4r.stderr.log'))) `
|
||||
-Debugger ([IO.File]::ReadAllText((Join-Path $dir.FullName 'debugger.log')))
|
||||
if ($summary.session -ne $entry.Value -or $summary.completedFrames -ne 0 -or $summary.nativeLaunches -ne 0) { throw "Unexpected classification for actual report $($entry.Key)" }
|
||||
$results+=@{case='issue-10-'+$entry.Key; passed=$true; summary=$summary}
|
||||
}
|
||||
}
|
||||
if ($OutputDirectory) {
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$results | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary-parser.json') -Encoding UTF8
|
||||
}
|
||||
Write-Host "PASS GAME_SUMMARY cases=$($results.Count)"
|
||||
@@ -0,0 +1,23 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$PackageRoot, [Parameter(Mandatory=$true)][string]$CodeObject)
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$target=Get-D4RGpuTarget $PackageRoot $null
|
||||
$other=if ($target -eq 'gfx1201') { 'gfx1200' } else { 'gfx1201' }
|
||||
function Expect-Rejection([scriptblock]$Action) {
|
||||
$rejected=$false
|
||||
try { & $Action | Out-Null } catch { $rejected=$true }
|
||||
if (!$rejected) { throw 'GPU target contract accepted incompatible input.' }
|
||||
}
|
||||
if ((Get-D4RGpuTarget $PackageRoot $target) -ne $target) { throw 'Incorrect package target.' }
|
||||
Assert-D4RCodeObjectTarget $CodeObject $target
|
||||
Expect-Rejection { Get-D4RGpuTarget $PackageRoot $other }
|
||||
Expect-Rejection { Get-D4RGpuTarget $PackageRoot 'gfx1250' }
|
||||
Expect-Rejection { Assert-D4RCodeObjectTarget $CodeObject $other }
|
||||
Expect-Rejection { Assert-D4RCodeObjectTarget (Join-Path $PackageRoot 'gpu-target.json') $target }
|
||||
# Old packages had no target metadata and were gfx1201 only.
|
||||
$legacy=Join-Path $PackageRoot '__no-target-metadata__'
|
||||
if (Test-Path -LiteralPath (Join-Path $legacy 'gpu-target.json')) { throw 'Unexpected legacy test fixture.' }
|
||||
if ((Get-D4RGpuTarget $legacy $null) -ne 'gfx1201') { throw 'Legacy target changed.' }
|
||||
Expect-Rejection { Get-D4RGpuTarget $legacy 'gfx1200' }
|
||||
Write-Host "PASS GPU_TARGET_SCRIPT target=$target rejected=5 hardware_executed=0"
|
||||
@@ -0,0 +1,54 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$CaptureDirectory, [string]$HipRoot,
|
||||
[string]$PackageRoot, [string]$OutputDirectory, [int]$Windows = 12,
|
||||
[Parameter(Mandatory=$true)][string]$DlssDll, [int]$PtxWindows = 2)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/k-real-reference-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) }
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$previousPython = $env:PYTHONPATH
|
||||
$previousDiag = $env:D4R_DIAG_DIR
|
||||
$env:PYTHONPATH = "$(Join-Path $repo '.tools/python/vendor');$previousPython"
|
||||
$summary = [ordered]@{utc=[DateTime]::UtcNow.ToString('o'); captureDirectory=$CaptureDirectory; layers=@(); passed=$false}
|
||||
try {
|
||||
foreach ($layer in @('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0')) {
|
||||
$name = "dltss_pwin_${layer}_layer"
|
||||
$captures = @(Get-ChildItem -LiteralPath $CaptureDirectory -Directory | Where-Object Name -Match ("^replay-[0-9]+-" + $name + '$'))
|
||||
if (!$captures.Count) { throw "Missing actual K capture: $name" }
|
||||
$capture = $captures | Sort-Object Name | Select-Object -First 1
|
||||
$folder = Join-Path $OutputDirectory $layer
|
||||
$output = Join-Path $folder 'output'
|
||||
New-Item -ItemType Directory -Force $folder | Out-Null
|
||||
$env:D4R_DIAG_DIR = $folder
|
||||
& (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --hip-root $HipRoot `
|
||||
--module (Join-Path $PackageRoot "experimental/k/${name}_${GpuArch}.hsaco") `
|
||||
--fixture-dir $capture.FullName --output-dir $output --iterations 1 `
|
||||
> (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "K $layer actual GPU replay failed: $folder" }
|
||||
& python (Join-Path $PackageRoot 'k_replay_validate.py') validate --layer $layer `
|
||||
--fixture-dir $capture.FullName --output-dir $output --windows $Windows --real-capture `
|
||||
> (Join-Path $folder 'reference.stdout.log') 2> (Join-Path $folder 'reference.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "K $layer mismatch; stopping before the next layer: $folder" }
|
||||
Get-Content -LiteralPath (Join-Path $folder 'reference.stdout.log') | Write-Host
|
||||
$ptxValidator = Join-Path $PackageRoot 'k_ptx_validate.py'
|
||||
if (!(Test-Path -LiteralPath $ptxValidator)) { $ptxValidator = Join-Path $repo 'tools/windows/k_ptx_validate.py' }
|
||||
& python -u $ptxValidator --layer $layer `
|
||||
--capture-dir $capture.FullName --native-dir $output --dlss-dll $DlssDll `
|
||||
--output-dir (Join-Path $folder 'private-ptx') --windows $PtxWindows `
|
||||
> (Join-Path $folder 'ptx-reference.stdout.log') 2> (Join-Path $folder 'ptx-reference.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "K $layer independent PTX mismatch; stopping: $folder" }
|
||||
Get-Content -LiteralPath (Join-Path $folder 'ptx-reference.stdout.log') | Write-Host
|
||||
$summary.layers += @{layer=$layer; passed=$true}
|
||||
}
|
||||
$summary.passed = $true
|
||||
} catch { $summary.error=$_.Exception.Message; Write-Warning $summary.error }
|
||||
finally {
|
||||
$env:PYTHONPATH = $previousPython; $env:D4R_DIAG_DIR = $previousDiag
|
||||
$summary | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Write-Host "Private real-weight replay results: $OutputDirectory (do not redistribute captures/allocations)"
|
||||
}
|
||||
if (!$summary.passed) { exit 1 }
|
||||
@@ -0,0 +1,55 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$HipRoot,
|
||||
[string]$PackageRoot,
|
||||
[string]$OutputDirectory,
|
||||
[string[]]$Layers = @('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0'),
|
||||
[int]$Iterations = 32
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/k-replay-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) }
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$previousPython = $env:PYTHONPATH
|
||||
$previousDiag = $env:D4R_DIAG_DIR
|
||||
$env:PYTHONPATH = "$(Join-Path $repo '.tools/python/vendor');$previousPython"
|
||||
$summary = [ordered]@{utc=[DateTime]::UtcNow.ToString('o'); hipRoot=$HipRoot; architecture=$GpuArch; layers=@(); passed=$false}
|
||||
try {
|
||||
if ($Iterations -lt 1 -or $Iterations -gt 10000) { throw 'Iterations must be 1..10000' }
|
||||
foreach ($layer in $Layers) {
|
||||
if ($layer -notmatch '^(enc[0-4]|dec[0-5])$') { throw "Invalid K layer $layer" }
|
||||
$folder = Join-Path $OutputDirectory $layer
|
||||
$fixture = Join-Path $folder 'fixture'
|
||||
$output = Join-Path $folder 'output'
|
||||
New-Item -ItemType Directory -Force $folder | Out-Null
|
||||
$env:D4R_DIAG_DIR = $folder
|
||||
& python (Join-Path $PackageRoot 'k_replay_validate.py') generate --layer $layer --fixture-dir $fixture `
|
||||
> (Join-Path $folder 'fixture.stdout.log') 2> (Join-Path $folder 'fixture.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "K $layer fixture generation failed; see $folder" }
|
||||
& (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --hip-root $HipRoot `
|
||||
--module (Join-Path $PackageRoot "experimental/k/dltss_pwin_${layer}_layer_${GpuArch}.hsaco") `
|
||||
--fixture-dir $fixture --output-dir $output --iterations $Iterations `
|
||||
> (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "K $layer GPU replay failed; see $folder" }
|
||||
& python (Join-Path $PackageRoot 'k_replay_validate.py') validate --layer $layer --fixture-dir $fixture --output-dir $output `
|
||||
> (Join-Path $folder 'reference.stdout.log') 2> (Join-Path $folder 'reference.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "K $layer reference mismatch; stopping before the next layer. See $folder" }
|
||||
Get-Content -LiteralPath (Join-Path $folder 'reference.stdout.log') | Write-Host
|
||||
$summary.layers += @{layer=$layer; passed=$true; iterations=$Iterations}
|
||||
}
|
||||
$summary.passed = $true
|
||||
} catch {
|
||||
$summary.error = $_.Exception.Message
|
||||
Write-Warning $summary.error
|
||||
} finally {
|
||||
$env:PYTHONPATH = $previousPython
|
||||
$env:D4R_DIAG_DIR = $previousDiag
|
||||
$summary | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Compress-Archive -LiteralPath $OutputDirectory -DestinationPath "$OutputDirectory.zip" -Force
|
||||
Write-Host "K replay bundle: $OutputDirectory.zip"
|
||||
}
|
||||
if (!$summary.passed) { exit 1 }
|
||||
@@ -0,0 +1,51 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$CaptureDirectory, [string]$HipRoot,
|
||||
[string]$PackageRoot, [string]$OutputDirectory, [int]$Blocks = 12,
|
||||
[Parameter(Mandatory=$true)][string]$DlssDll, [int]$PtxBlocks = 2)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/m-real-reference-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) }
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$previousPython=$env:PYTHONPATH; $previousDiag=$env:D4R_DIAG_DIR
|
||||
$env:PYTHONPATH = "$(Join-Path $repo '.tools/python/vendor');$previousPython"
|
||||
$summary = [ordered]@{utc=[DateTime]::UtcNow.ToString('o'); captureDirectory=$CaptureDirectory; layers=@(); passed=$false}
|
||||
try {
|
||||
foreach ($layer in @('enc1','enc2','enc3_tube','dec2','dec1')) {
|
||||
$name = "rrlite_${layer}_4x4"
|
||||
$captures = @(Get-ChildItem -LiteralPath $CaptureDirectory -Directory | Where-Object Name -Match ("^replay-[0-9]+-" + $name + '$'))
|
||||
if (!$captures.Count) { throw "Missing actual M capture: $name" }
|
||||
$capture = $captures | Sort-Object Name | Select-Object -First 1
|
||||
$folder = Join-Path $OutputDirectory $layer
|
||||
$output = Join-Path $folder 'output'
|
||||
New-Item -ItemType Directory -Force $folder | Out-Null
|
||||
$env:D4R_DIAG_DIR = $folder
|
||||
& (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --hip-root $HipRoot `
|
||||
--module (Join-Path $PackageRoot "experimental/m/${name}_${GpuArch}.hsaco") `
|
||||
--fixture-dir $capture.FullName --output-dir $output --iterations 1 `
|
||||
> (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "M $layer actual GPU replay failed: $folder" }
|
||||
& python (Join-Path $PackageRoot 'm_replay_validate.py') validate --layer $layer `
|
||||
--fixture-dir $capture.FullName --output-dir $output --blocks $Blocks --real-capture `
|
||||
> (Join-Path $folder 'reference.stdout.log') 2> (Join-Path $folder 'reference.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "M $layer mismatch; stopping before the next layer: $folder" }
|
||||
Get-Content -LiteralPath (Join-Path $folder 'reference.stdout.log') | Write-Host
|
||||
& python -u (Join-Path $PackageRoot 'm_ptx_validate.py') --layer $layer `
|
||||
--capture-dir $capture.FullName --native-dir $output --dlss-dll $DlssDll `
|
||||
--output-dir (Join-Path $folder 'private-ptx') --blocks $PtxBlocks `
|
||||
> (Join-Path $folder 'ptx-reference.stdout.log') 2> (Join-Path $folder 'ptx-reference.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "M $layer independent PTX mismatch; stopping: $folder" }
|
||||
Get-Content -LiteralPath (Join-Path $folder 'ptx-reference.stdout.log') | Write-Host
|
||||
$summary.layers += @{layer=$layer; passed=$true}
|
||||
}
|
||||
$summary.passed=$true
|
||||
} catch { $summary.error=$_.Exception.Message; Write-Warning $summary.error }
|
||||
finally {
|
||||
$env:PYTHONPATH=$previousPython; $env:D4R_DIAG_DIR=$previousDiag
|
||||
$summary | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Write-Host "Private real-weight replay results: $OutputDirectory (do not redistribute captures/allocations)"
|
||||
}
|
||||
if (!$summary.passed) { exit 1 }
|
||||
@@ -0,0 +1,47 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$HipRoot, [string]$PackageRoot, [string]$OutputDirectory,
|
||||
[string[]]$Layers = @('enc1','enc2','enc3_tube','dec2','dec1'),
|
||||
[ValidateRange(1,10000)][int]$Iterations = 32)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/m-replay-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) }
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$previousPython = $env:PYTHONPATH
|
||||
$previousDiag = $env:D4R_DIAG_DIR
|
||||
$env:PYTHONPATH = "$(Join-Path $repo '.tools/python/vendor');$previousPython"
|
||||
$summary = [ordered]@{utc=[DateTime]::UtcNow.ToString('o'); hipRoot=$HipRoot; architecture=$GpuArch; layers=@(); passed=$false}
|
||||
try {
|
||||
foreach ($layer in $Layers) {
|
||||
if ($layer -notmatch '^(enc[12]|enc3_tube|dec[12])$') { throw "Invalid M layer $layer" }
|
||||
$folder = Join-Path $OutputDirectory $layer
|
||||
$fixture = Join-Path $folder 'fixture'
|
||||
$output = Join-Path $folder 'output'
|
||||
New-Item -ItemType Directory -Force $folder | Out-Null
|
||||
$env:D4R_DIAG_DIR = $folder
|
||||
& python (Join-Path $PackageRoot 'm_replay_validate.py') generate --layer $layer --fixture-dir $fixture `
|
||||
> (Join-Path $folder 'fixture.stdout.log') 2> (Join-Path $folder 'fixture.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "M $layer fixture generation failed: $folder" }
|
||||
& (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --hip-root $HipRoot `
|
||||
--module (Join-Path $PackageRoot "experimental/m/rrlite_${layer}_4x4_${GpuArch}.hsaco") `
|
||||
--fixture-dir $fixture --output-dir $output --iterations $Iterations `
|
||||
> (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "M $layer GPU replay failed: $folder" }
|
||||
& python (Join-Path $PackageRoot 'm_replay_validate.py') validate --layer $layer --fixture-dir $fixture --output-dir $output `
|
||||
> (Join-Path $folder 'reference.stdout.log') 2> (Join-Path $folder 'reference.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "M $layer reference mismatch; stopping before the next layer: $folder" }
|
||||
Get-Content -LiteralPath (Join-Path $folder 'reference.stdout.log') | Write-Host
|
||||
$summary.layers += @{layer=$layer; passed=$true; iterations=$Iterations}
|
||||
}
|
||||
$summary.passed = $true
|
||||
} catch { $summary.error=$_.Exception.Message; Write-Warning $summary.error }
|
||||
finally {
|
||||
$env:PYTHONPATH=$previousPython; $env:D4R_DIAG_DIR=$previousDiag
|
||||
$summary | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Compress-Archive -LiteralPath $OutputDirectory -DestinationPath "$OutputDirectory.zip" -Force
|
||||
Write-Host "M replay bundle: $OutputDirectory.zip"
|
||||
}
|
||||
if (!$summary.passed) { exit 1 }
|
||||
@@ -0,0 +1,28 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$CaptureDirectory,
|
||||
[Parameter(Mandatory=$true)][string]$BaselineValidation,
|
||||
[string]$ModuleDirectory, [string]$HipRoot, [string]$OutputDirectory, [switch]$Benchmark)
|
||||
$ErrorActionPreference='Stop'
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$ModuleDirectory) { $ModuleDirectory=Join-Path $repo 'build/native-m-fp8-experimental' }
|
||||
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo 'test-results/m-native-fp8-replay' }
|
||||
$previousPython=$env:PYTHONPATH
|
||||
try {
|
||||
$env:PYTHONPATH="$(Join-Path $repo '.tools/python/vendor');$previousPython"
|
||||
$metadata=Get-Content -LiteralPath (Join-Path $ModuleDirectory 'build-info.json') -Raw | ConvertFrom-Json
|
||||
if ($metadata.hardwarePacking) {
|
||||
& python -u (Join-Path $repo 'tools/windows/fp8_pack_validate.py') `
|
||||
--probe (Join-Path $repo 'dist/windows-rdna4-command-list/bin/d4r_native_replay_probe.exe') `
|
||||
--module (Join-Path $ModuleDirectory 'packing-probe/fp8_pack_probe.hsaco') `
|
||||
--hip-root $HipRoot --output-dir (Join-Path $OutputDirectory 'packing-check')
|
||||
if ($LASTEXITCODE) { throw 'Hardware packing does not preserve every finite FP8 encoding; stopping before network replay.' }
|
||||
}
|
||||
$benchmarkArguments=@()
|
||||
if ($Benchmark) { $benchmarkArguments=@('--benchmark-control-dir',(Join-Path $repo 'build/native-m-gfx1201')) }
|
||||
& python -u (Join-Path $repo 'tools/windows/m_native_replay_compare.py') `
|
||||
--capture-dir $CaptureDirectory --module-dir $ModuleDirectory --hip-root $HipRoot `
|
||||
--probe (Join-Path $repo 'dist/windows-rdna4-command-list/bin/d4r_native_replay_probe.exe') `
|
||||
--baseline-validation $BaselineValidation --output-dir $OutputDirectory @benchmarkArguments
|
||||
if ($LASTEXITCODE) { throw "Experimental FP8 validation failed; baseline unchanged. Logs: $OutputDirectory" }
|
||||
} finally { $env:PYTHONPATH=$previousPython }
|
||||
@@ -0,0 +1,52 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$HipRoot, [string]$PackageRoot, [string]$ZludaRoot, [string]$OutputDirectory)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$ZludaRoot) { $ZludaRoot = Join-Path $repo 'dist/zluda-windows-final' }
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/native-identity-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) }
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$previous = @{D4R_ZLUDA_NATIVE_DIR=$env:D4R_ZLUDA_NATIVE_DIR; D4R_ZLUDA_VERBOSE=$env:D4R_ZLUDA_VERBOSE; D4R_DIAG_DIR=$env:D4R_DIAG_DIR}
|
||||
function Invoke-NativeProbe([string]$Name) {
|
||||
$info = New-Object Diagnostics.ProcessStartInfo
|
||||
$info.FileName = Join-Path $PackageRoot 'bin/d4r_cuda_driver_probe.exe'
|
||||
$info.Arguments = '--hip-root "' + $HipRoot + '" --cuda-dll "' + (Join-Path $ZludaRoot 'nvcuda.dll') + '" --iterations 32'
|
||||
$info.UseShellExecute=$false; $info.CreateNoWindow=$true; $info.RedirectStandardOutput=$true; $info.RedirectStandardError=$true
|
||||
$process = New-Object Diagnostics.Process
|
||||
$process.StartInfo=$info
|
||||
if (!$process.Start()) { throw 'Cannot start native override probe' }
|
||||
$stdout=$process.StandardOutput.ReadToEndAsync(); $stderr=$process.StandardError.ReadToEndAsync()
|
||||
if (!$process.WaitForExit(120000)) { $process.Kill(); $process.WaitForExit(); throw 'Native override test timed out' }
|
||||
[IO.File]::WriteAllText((Join-Path $OutputDirectory "$Name.stdout.log"),$stdout.GetAwaiter().GetResult())
|
||||
[IO.File]::WriteAllText((Join-Path $OutputDirectory "$Name.stderr.log"),$stderr.GetAwaiter().GetResult())
|
||||
$code=$process.ExitCode; $process.Dispose()
|
||||
return $code
|
||||
}
|
||||
try {
|
||||
$env:D4R_ZLUDA_VERBOSE = '1'; $env:D4R_DIAG_DIR = $OutputDirectory
|
||||
$bad = Join-Path $OutputDirectory 'wrong-identity'
|
||||
$good = Join-Path $OutputDirectory 'matching-identity'
|
||||
New-Item -ItemType Directory -Force $bad,$good | Out-Null
|
||||
foreach ($directory in @($bad,$good)) {
|
||||
Copy-Item -LiteralPath (Join-Path $PackageRoot "bin/probe_${GpuArch}.hsaco") -Destination (Join-Path $directory 'd4r_ptx_pattern.hsaco') -Force
|
||||
}
|
||||
'd4r_ptx_pattern 0000000000000000' | Set-Content -LiteralPath (Join-Path $bad 'd4r-kernels.txt') -Encoding ASCII
|
||||
$env:D4R_ZLUDA_NATIVE_DIR = $bad
|
||||
if (Invoke-NativeProbe 'wrong') { throw 'Wrong identity must use the correct translated fallback' }
|
||||
if (!(Select-String -LiteralPath (Join-Path $OutputDirectory 'wrong.stderr.log') -Pattern 'reason=ptx_identity.*fallback=translated' -Quiet)) {
|
||||
throw 'Wrong identity was not explicitly rejected'
|
||||
}
|
||||
$identity = Select-String -LiteralPath (Join-Path $OutputDirectory 'wrong.stdout.log') -Pattern 'fnv1a64=([0-9a-f]{16})'
|
||||
if (!$identity) { throw 'PTX probe did not report its identity' }
|
||||
"d4r_ptx_pattern $($identity.Matches[0].Groups[1].Value)" | Set-Content -LiteralPath (Join-Path $good 'd4r-kernels.txt') -Encoding ASCII
|
||||
$env:D4R_ZLUDA_NATIVE_DIR = $good
|
||||
if (Invoke-NativeProbe 'matching') { throw 'Matching native override produced incorrect output' }
|
||||
$hits = @(Select-String -LiteralPath (Join-Path $OutputDirectory 'matching.stderr.log') -Pattern '\[d4r-launch\].*backend=native')
|
||||
if ($hits.Count -ne 32) { throw "Expected 32 native launches, got $($hits.Count)" }
|
||||
Write-Host 'PASS NATIVE_IDENTITY wrong_hash=fallback matching_hash=native launches=32'
|
||||
} finally {
|
||||
foreach ($name in $previous.Keys) { [Environment]::SetEnvironmentVariable($name,$previous[$name],'Process') }
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$TextureRoot, [string]$ControlTextureRoot, [string]$CommonTextureRoot, [string]$ZludaRoot, [string]$PackageRoot,
|
||||
[string]$OutputDirectory, [int]$Iterations=8, [string]$OutputResolution='512x288', [string]$InputResolution,
|
||||
[switch]$CandidateFirst,
|
||||
[ValidateSet('output','input')][string]$KernelStage='output')
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$PackageRoot) { $PackageRoot=Join-Path $repo 'dist/windows-rdna4-command-list' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$TextureRoot) { $TextureRoot=Join-Path $repo "build/private-textures-$GpuArch" }
|
||||
if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' }
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo 'test-results/k-native-texture-reference' }
|
||||
if ($OutputResolution -notmatch '^([0-9]+)x([0-9]+)$') { throw 'OutputResolution must be WIDTHxHEIGHT.' }
|
||||
$outputWidth=[int]$Matches[1]; $outputHeight=[int]$Matches[2]
|
||||
$combined=Join-Path $OutputDirectory 'private-native'
|
||||
New-Item -ItemType Directory -Force $combined | Out-Null
|
||||
foreach ($directory in @((Join-Path $repo "build/native-k-$GpuArch"),$CommonTextureRoot,$TextureRoot) | Where-Object { $_ }) {
|
||||
Get-ChildItem -LiteralPath $directory -Filter '*.hsaco' | Copy-Item -Destination $combined -Force
|
||||
}
|
||||
& python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $combined (Join-Path $repo 'nvngx_dlss.dll')
|
||||
if ($LASTEXITCODE) { throw 'Combined private manifest failed.' }
|
||||
$controlNative=Join-Path $repo "build/native-k-$GpuArch"
|
||||
if ($ControlTextureRoot -or $CommonTextureRoot) {
|
||||
$controlNative=Join-Path $OutputDirectory 'control-private-native'
|
||||
New-Item -ItemType Directory -Force $controlNative | Out-Null
|
||||
foreach ($directory in @((Join-Path $repo "build/native-k-$GpuArch"),$CommonTextureRoot,$ControlTextureRoot) | Where-Object { $_ }) {
|
||||
Get-ChildItem -LiteralPath $directory -Filter '*.hsaco' | Copy-Item -Destination $controlNative -Force
|
||||
}
|
||||
& python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $controlNative (Join-Path $repo 'nvngx_dlss.dll')
|
||||
if ($LASTEXITCODE) { throw 'Control private manifest failed.' }
|
||||
}
|
||||
$settings=@{D4R_VALIDATE_OUTPUT='1'; D4R_PROFILE_STAGES='1'; D4R_ZLUDA_PROFILE='1'; D4R_QUIET_API='1';
|
||||
D4R_ZLUDA_WMMA='1'; D4R_ZLUDA_WMMA_FP8='1'; D4R_ZLUDA_WMMA_FP8_NATIVE='0';
|
||||
D4R_ZLUDA_WMMA_F16_REFERENCE='1'; D4R_ZLUDA_PROFILE_DEFERRED=$null;
|
||||
ZLUDA_CACHE_DIR=(Join-Path $repo 'build/zluda-cache-windows'); PYTHONPATH=(Join-Path $repo '.tools/python/vendor');
|
||||
D4R_ZLUDA_NATIVE_DIR=$null}
|
||||
$old=@{}
|
||||
$stagedObjects=@{}
|
||||
foreach ($stage in @(@{name='control'; root=$controlNative},@{name='candidate'; root=$combined})) {
|
||||
$stagedObjects[$stage.name]=@{}
|
||||
foreach ($object in Get-ChildItem -LiteralPath $stage.root -Filter '*.hsaco') {
|
||||
Assert-D4RCodeObjectTarget $object.FullName $GpuArch
|
||||
$stagedObjects[$stage.name][$object.Name]=(Get-FileHash -LiteralPath $object.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
}
|
||||
}
|
||||
try {
|
||||
foreach ($key in $settings.Keys) { $old[$key]=[Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') }
|
||||
foreach ($flags in @(0,11)) {
|
||||
$variant=if ($flags -eq 11) { 'depthinv_mvlo_hdr' } else { 'depthreg_mvhi_ldr' }
|
||||
$suffix=if ($KernelStage -eq 'output') { '_max_v2_rel' } else { '_v2_rel' }
|
||||
$kernel="hiluma_engine_${KernelStage}_${variant}${suffix}"
|
||||
$order=if ($CandidateFirst) { @('candidate','control') } else { @('control','candidate') }
|
||||
foreach ($mode in $order) {
|
||||
$env:D4R_ZLUDA_NATIVE_DIR=if ($mode -eq 'control') { $controlNative } else { $combined }
|
||||
$expectedBackend=if (Test-Path -LiteralPath (Join-Path $env:D4R_ZLUDA_NATIVE_DIR ($kernel + '.hsaco'))) { 'native' } else { 'translated' }
|
||||
if ($mode -eq 'candidate' -and $expectedBackend -ne 'native') { throw "Missing candidate object: $kernel" }
|
||||
$arguments=@{RuntimeProfile='therock'; ZludaRoot=$ZludaRoot;
|
||||
PackageRoot=$PackageRoot; NgxCore=(Join-Path $repo '_nvngx.dll');
|
||||
DlssDll=(Join-Path $repo 'nvngx_dlss.dll'); NgxMode='d3d12'; NgxAbi='project-legacy'; Preset=11;
|
||||
NgxCreateFlags=$flags; NgxOutputResolution=$OutputResolution; NgxOnly=$true; RequireNativeNetwork=$true; CommandListBackend=$true;
|
||||
NgxInputResolution=$InputResolution;
|
||||
PixelProfile='depth-stencil'; BarrierMode='inherited-legacy'; CaptureExceptions=$true; EarlyIndirectProbe=$true;
|
||||
Iterations=$Iterations; TimeoutSeconds=300; OptiScalerDll=(Join-Path $repo 'dist/optiscaler-windows-d4r/OptiScaler.dll');
|
||||
OutputDirectory=(Join-Path $OutputDirectory "$mode-$flags")}
|
||||
& (Join-Path $PSScriptRoot 'test-windows-rdna4.ps1') @arguments
|
||||
if ($LASTEXITCODE) { throw "Texture $mode flags=$flags workload failed." }
|
||||
& python (Join-Path $repo 'tools/windows/profile_report.py') $arguments.OutputDirectory --output (Join-Path $arguments.OutputDirectory 'profile.json')
|
||||
if ($LASTEXITCODE) { throw 'Texture profile report failed.' }
|
||||
$profile=Get-Content -LiteralPath (Join-Path $arguments.OutputDirectory 'profile.json') -Raw | ConvertFrom-Json
|
||||
$samples=@($profile.kernels | Where-Object { $_.kernel -eq $kernel -and $_.backend -eq $expectedBackend -and $_.phase -eq 'main' -and $_.serializing })
|
||||
if ($samples.Count -ne 1 -or $samples[0].gpuMs.samples -ne $Iterations) {
|
||||
throw "Missing valid serializing $KernelStage-kernel timings ($expectedBackend) for $mode flags=$flags; retain this run as correctness evidence only."
|
||||
}
|
||||
}
|
||||
$control=Join-Path $OutputDirectory "control-$flags"; $candidate=Join-Path $OutputDirectory "candidate-$flags"
|
||||
$stderr=Get-Content -LiteralPath (Join-Path $candidate 'd3d12-evaluate-preset-11.stderr.log') -Raw
|
||||
$hits=[regex]::Matches($stderr, ('\[d4r-launch\] kernel="' + $kernel + '" backend=native')).Count
|
||||
if ($hits -ne $Iterations) { throw "Texture override was not executed on every frame: $kernel hits=$hits" }
|
||||
& python (Join-Path $repo 'tools/windows/frame_compare.py') --reference $control --actual $candidate --width $outputWidth --height $outputHeight --exact
|
||||
if ($LASTEXITCODE) { throw "Texture flags=$flags full-frame RGB mismatch; preserve baseline." }
|
||||
}
|
||||
@{passed=$true; architecture=$GpuArch; framesPerVariant=$Iterations; outputResolution=$OutputResolution; createFlags=@(0,11); strictRgb=$true;
|
||||
inputResolution=$InputResolution;
|
||||
serializingProfile=$true; candidateFirst=[bool]$CandidateFirst; kernelStage=$KernelStage;
|
||||
controlTextureRoot=$ControlTextureRoot; commonTextureRoot=$CommonTextureRoot;
|
||||
stagedObjects=$stagedObjects;
|
||||
manifestSha256=(Get-FileHash -LiteralPath (Join-Path $TextureRoot 'd4r-kernels.txt') -Algorithm SHA256).Hash.ToLowerInvariant();
|
||||
privateNvidiaDerivedCode=$true; source=(Get-Content -LiteralPath (Join-Path $TextureRoot 'build-info.json') -Raw | ConvertFrom-Json)} |
|
||||
ConvertTo-Json -Depth 12 | Set-Content -LiteralPath (Join-Path $TextureRoot 'validation.json') -Encoding UTF8
|
||||
Write-Host "PASS: both private K $KernelStage variants executed and matched every RGB component. $OutputDirectory"
|
||||
} finally {
|
||||
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
# Reproduce numeric NGX feature discovery without launching or modifying a game.
|
||||
# The private NVIDIA DLL staging directory is deleted; reports contain logs only.
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$DiagnosticRoot,
|
||||
[Parameter(Mandatory=$true)][string]$HipRoot,
|
||||
[Parameter(Mandatory=$true)][string]$ZludaRoot,
|
||||
[Parameter(Mandatory=$true)][string]$NativeRoot,
|
||||
[Parameter(Mandatory=$true)][string]$OptiScalerDll,
|
||||
[Parameter(Mandatory=$true)][string]$NgxCore,
|
||||
[Parameter(Mandatory=$true)][string]$DlssDll,
|
||||
[Parameter(Mandatory=$true)][string]$OutputDirectory,
|
||||
[UInt64]$ApplicationId=100152211)
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
foreach ($name in @('DiagnosticRoot','HipRoot','ZludaRoot','NativeRoot','OptiScalerDll','NgxCore','DlssDll')) {
|
||||
Set-Variable -Name $name -Value ((Get-Item -LiteralPath (Get-Variable -Name $name -ValueOnly)).FullName)
|
||||
}
|
||||
$arch=Get-D4RGpuTarget $DiagnosticRoot
|
||||
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$runtime=Join-Path ([IO.Path]::GetTempPath()) ('d4r-feature-layout-'+[Guid]::NewGuid().ToString('N'))
|
||||
$settings=@{HIP_PATH=$HipRoot; PATH="$HipRoot\bin;$ZludaRoot;$env:PATH";
|
||||
D4R_NVAPI_BACKEND=(Join-Path $ZludaRoot 'nvapi64.dll'); ZLUDA_CUDA_LIB=(Join-Path $ZludaRoot 'nvcuda.dll');
|
||||
D4R_ZLUDA_NATIVE_DIR=$NativeRoot; D4R_ZLUDA_VERBOSE='1';
|
||||
D4R_ZLUDA_WMMA='1'; D4R_ZLUDA_WMMA_F16_REFERENCE='1'; D4R_ZLUDA_WMMA_FP8='1'; D4R_ZLUDA_WMMA_FP8_NATIVE='0';
|
||||
D4R_VALIDATE_OUTPUT='1'; D4R_DIAG_DIR=$null; ZLUDA_LOG_DIR=$null; D4R_FORMAT_MODULE=$null;
|
||||
D4R_ASYNC_INTEROP=$null; D4R_BATCH_INPUT_COPIES=$null; D4R_D3D12_COMMAND_BACKEND=$null}
|
||||
$old=@{}; $results=@()
|
||||
try {
|
||||
foreach ($key in $settings.Keys) { $old[$key]=[Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') }
|
||||
foreach ($dir in @('app','d4r','d4r/vendor','d4r/dlss')) { New-Item -ItemType Directory -Force (Join-Path $runtime $dir) | Out-Null }
|
||||
$exe=Join-Path $runtime 'app/d4r_d3d12_dlss_probe.exe'
|
||||
$shim=Join-Path $runtime 'd4r/_nvngx.dll'
|
||||
$core=Join-Path $runtime 'd4r/ngx/_nvngx.dll'
|
||||
$feature=Join-Path $runtime 'd4r/vendor/nvngx_dlss.dll'
|
||||
Copy-Item -LiteralPath (Join-Path $DiagnosticRoot 'bin/d4r_d3d12_dlss_probe.exe') -Destination $exe
|
||||
Copy-Item -LiteralPath (Join-Path $DiagnosticRoot 'bin/d4r_nvngx.dll') -Destination $shim
|
||||
Copy-Item -LiteralPath (Join-Path $DiagnosticRoot "bin/pixel_convert_${arch}.hsaco") -Destination (Split-Path $shim)
|
||||
Copy-Item -LiteralPath $NgxCore -Destination $core
|
||||
Copy-Item -LiteralPath $DlssDll -Destination $feature
|
||||
$opti=Join-Path $runtime 'app/OptiScaler.dll'
|
||||
Copy-Item -LiteralPath $OptiScalerDll -Destination $opti
|
||||
$cases=@(@{name='vendor-only-negative'; preset=11; frontend='d4r'; expectedExit=4},
|
||||
@{name='optiscaler-vendor-only-negative'; preset=11; frontend='optiscaler'; expectedExit=4},
|
||||
@{name='caller-dll-k'; preset=11; frontend='d4r'; expectedExit=0},
|
||||
@{name='optiscaler-caller-k'; preset=11; frontend='optiscaler'; expectedExit=0},
|
||||
@{name='optiscaler-caller-m'; preset=13; frontend='optiscaler'; expectedExit=0})
|
||||
foreach ($case in $cases) {
|
||||
$out=Join-Path $OutputDirectory $case.name
|
||||
New-Item -ItemType Directory -Force $out | Out-Null
|
||||
if ($case.expectedExit -eq 0) {
|
||||
$feature=Join-Path $runtime 'd4r/dlss/nvngx_dlss.dll'
|
||||
if (!(Test-Path -LiteralPath $feature)) { Copy-Item -LiteralPath $DlssDll -Destination $feature }
|
||||
}
|
||||
$env:D4R_DIAG_DIR=$out; $env:ZLUDA_LOG_DIR=Join-Path $out 'zluda-trace'
|
||||
$env:D4R_FORMAT_MODULE=Join-Path $runtime "d4r/pixel_convert_${arch}.hsaco"
|
||||
$module=if ($case.frontend -eq 'optiscaler') { $opti } else { $shim }
|
||||
if ($case.frontend -eq 'optiscaler') {
|
||||
@"
|
||||
[Upscalers]
|
||||
Dx12Upscaler=dlss
|
||||
[FrameGen]
|
||||
Enabled=false
|
||||
FGInput=nofg
|
||||
FGOutput=nofg
|
||||
[DLSS]
|
||||
Enabled=true
|
||||
AllowExternalBackend=true
|
||||
RenderPresetOverride=true
|
||||
RenderPresetForAll=$($case.preset)
|
||||
UseGenericAppIdWithDlss=false
|
||||
[Libraries]
|
||||
NvngxPath=$(Split-Path $shim)
|
||||
NvngxDlssPath=$(Split-Path $feature)
|
||||
NvngxFeaturePath=$(Split-Path $feature)
|
||||
[Hotfix]
|
||||
RestoreComputeSignature=false
|
||||
RestoreGraphicSignature=false
|
||||
ExtendedStateRestore=false
|
||||
[Log]
|
||||
LogToFile=true
|
||||
LogLevel=0
|
||||
[Menu]
|
||||
DisableSplash=true
|
||||
"@ | Set-Content -LiteralPath (Join-Path $runtime 'app/OptiScaler.ini') -Encoding UTF8
|
||||
}
|
||||
$arguments=@('--hip-root',$HipRoot,'--cuda-dll',(Join-Path $ZludaRoot 'nvcuda.dll'),
|
||||
'--module',$module,'--ngx-core',$core,'--dlss-dll',$feature,
|
||||
'--nvapi-dll',(Join-Path $DiagnosticRoot 'nvapi-compat/nvapi64.dll'),
|
||||
'--ngx-app-id',"$ApplicationId",'--ngx-abi','driver','--ngx-frontend',$case.frontend,
|
||||
'--interop-mode','command-list','--iterations','3','--preset',"$($case.preset)")
|
||||
$info=New-Object Diagnostics.ProcessStartInfo
|
||||
# Capture harness faults outside OptiScaler's loader hooks. Games use
|
||||
# the no-debug launcher by default; this developer reproducer is separate.
|
||||
$arguments=@('--output-directory',$out,'--',$exe)+$arguments
|
||||
$info.FileName=Join-Path $DiagnosticRoot 'bin/d4r_debug_launcher.exe'; $info.WorkingDirectory=Split-Path $exe
|
||||
$info.Arguments=(@($arguments | ForEach-Object { if ($_.Contains('"')) { throw 'Unsupported quote in test path.' }; '"'+$_+'"' }) -join ' ')
|
||||
$info.UseShellExecute=$false; $info.CreateNoWindow=$true; $info.RedirectStandardOutput=$true; $info.RedirectStandardError=$true
|
||||
foreach ($key in $settings.Keys) { if (![Environment]::GetEnvironmentVariable($key,'Process')) { $info.EnvironmentVariables.Remove($key) } }
|
||||
$process=New-Object Diagnostics.Process; $process.StartInfo=$info
|
||||
try {
|
||||
[void]$process.Start(); $stdout=$process.StandardOutput.ReadToEndAsync(); $stderr=$process.StandardError.ReadToEndAsync()
|
||||
if (!$process.WaitForExit(240000)) { $process.Kill(); $process.WaitForExit(); throw "NGX layout test timed out: $($case.name)" }
|
||||
$log=$stdout.GetAwaiter().GetResult(); $errorLog=$stderr.GetAwaiter().GetResult()
|
||||
[IO.File]::WriteAllText((Join-Path $out 'stdout.log'),$log)
|
||||
[IO.File]::WriteAllText((Join-Path $out 'stderr.log'),$errorLog)
|
||||
if ($process.ExitCode -ne $case.expectedExit) { throw "Unexpected exit $($process.ExitCode): $($case.name), see $out" }
|
||||
if ($case.expectedExit) {
|
||||
if ($log -notmatch 'D4R_CAPABILITIES available=0 feature_init=0xbad00004' -or $log -match 'D4R_FRAME ') { throw 'Old vendor-only layout did not reproduce FeatureNotFound.' }
|
||||
if ($case.frontend -eq 'optiscaler' -and $errorLog -notmatch 'FAIL D3D12_DLSS D3D12 Init NGX=') { throw 'OptiScaler swallowed the numeric backend initialization failure.' }
|
||||
} else {
|
||||
if ($log -notmatch 'D4R_CAPABILITIES available=1 feature_init=0x00000001 needs_driver=0' -or
|
||||
([regex]::Matches($log,'D3D12_OUTPUT preset=\d+ frame=\d+ finite=1')).Count -ne 3 -or
|
||||
([regex]::Matches($log,'D4R_OUTPUT_VALIDATION elements=\d+ nan=0 inf=0 diagnostics_cpu_bytes=8')).Count -ne 3 -or
|
||||
$log -notmatch 'PASS D3D12_DLSS .*fast_path_cpu_copies=0 frame_age=0' -or $errorLog -match 'D4R_WINDOWS_FAILURE') { throw 'Corrected feature layout did not complete finite zero-copy frames.' }
|
||||
$required=@(Get-Content -LiteralPath (Join-Path $NativeRoot 'd4r-kernels.txt') | Where-Object {
|
||||
if ($case.preset -eq 11) { $_ -match '^dltss_pwin_' } else { $_ -match '^rrlite_' }
|
||||
} | ForEach-Object { $_.Split(' ')[0] })
|
||||
if ($required.Count -ne $(if ($case.preset -eq 11) { 11 } else { 5 })) { throw 'Incomplete native manifest.' }
|
||||
foreach ($kernel in $required) {
|
||||
if ($errorLog -notmatch ('\[d4r-launch\] kernel="'+[regex]::Escape($kernel)+'" backend=native') -or
|
||||
$errorLog -match ('\[d4r-launch\] kernel="'+[regex]::Escape($kernel)+'" backend=translated')) { throw "Missing native network execution: $kernel" }
|
||||
}
|
||||
if ($case.frontend -eq 'd4r' -and $log -notmatch 'D3D12_REQUIREMENTS warp_supported=0 enumeration_success=1') { throw 'WARP adapter enumeration regression failed.' }
|
||||
}
|
||||
$results+=@{case=$case.name; passed=$true; expectedExit=$case.expectedExit; application=$ApplicationId; preset=$case.preset; architecture=$arch}
|
||||
Write-Host "PASS NGX_FEATURE_LAYOUT $($case.name)"
|
||||
} finally {
|
||||
if ($case.frontend -eq 'optiscaler') {
|
||||
Get-ChildItem -LiteralPath (Split-Path $exe) -Filter '*.log' -File | ForEach-Object {
|
||||
Copy-Item -LiteralPath $_.FullName -Destination $out -Force
|
||||
}
|
||||
}
|
||||
$process.Dispose()
|
||||
}
|
||||
}
|
||||
for ($frame=0; $frame -lt 3; $frame++) {
|
||||
$direct=[IO.File]::ReadAllBytes((Join-Path $OutputDirectory "caller-dll-k/output-$frame.rgba16f"))
|
||||
$frontend=[IO.File]::ReadAllBytes((Join-Path $OutputDirectory "optiscaler-caller-k/output-$frame.rgba16f"))
|
||||
if ($direct.Length -ne $frontend.Length -or $direct.Length -ne 512*288*8) { throw 'RGB comparison dimensions differ.' }
|
||||
for ($pixel=0; $pixel -lt $direct.Length; $pixel+=8) {
|
||||
if (([BitConverter]::ToUInt64($direct,$pixel) -band [UInt64]0xffffffffffff) -ne
|
||||
([BitConverter]::ToUInt64($frontend,$pixel) -band [UInt64]0xffffffffffff)) { throw "K frontend RGB mismatch: frame=$frame offset=$pixel" }
|
||||
}
|
||||
}
|
||||
$results+=@{case='k-direct-optiscaler-rgb'; passed=$true; frames=3; rgbByteExact=$true; maxAbsoluteError=0; maxRelativeError=0}
|
||||
Write-Host 'PASS NGX_FEATURE_LAYOUT k_direct_optiscaler_rgb exact=1 max_abs=0 max_rel=0'
|
||||
} finally {
|
||||
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
|
||||
$results | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'feature-layout.json') -Encoding UTF8
|
||||
$resolved=[IO.Path]::GetFullPath($runtime)
|
||||
$temp=[IO.Path]::GetFullPath([IO.Path]::GetTempPath()).TrimEnd('\')+'\'
|
||||
if (!$resolved.StartsWith($temp,[StringComparison]::OrdinalIgnoreCase) -or [IO.Path]::GetFileName($resolved) -notmatch '^d4r-feature-layout-[0-9a-f]{32}$') { throw 'Refusing cleanup outside private NGX staging.' }
|
||||
if (Test-Path -LiteralPath $resolved) {
|
||||
foreach ($attempt in 1..10) {
|
||||
try { Remove-Item -LiteralPath $resolved -Recurse -Force; break }
|
||||
catch { if ($attempt -eq 10) { throw }; Start-Sleep -Milliseconds 300 }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
# Drag-in player mode checks. Builds with the vendored llvm-mingw; needs no
|
||||
# NVIDIA DLLs and launches no GPU kernels. The loader case enumerates HIP
|
||||
# devices and is skipped (reported, not passed) without a supported AMD GPU.
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$CompilerRoot, [string]$HipRoot, [string]$OutputDirectory
|
||||
)
|
||||
$ErrorActionPreference='Stop'
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$CompilerRoot) { $CompilerRoot=Join-Path $repo '.tools/llvm-mingw-20260922-ucrt-x86_64' }
|
||||
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/player-config/'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$cxx=Join-Path $CompilerRoot 'bin/x86_64-w64-mingw32-clang++.exe'
|
||||
if (!(Test-Path -LiteralPath $cxx)) { throw "llvm-mingw not found: $cxx" }
|
||||
if (!(Test-Path -LiteralPath (Join-Path $HipRoot 'include/hip/hip_runtime_api.h'))) { throw "HIP headers not found under $HipRoot" }
|
||||
|
||||
# Same generated target table as CMakeLists.txt.
|
||||
$targets=(Get-Content -LiteralPath (Join-Path $repo 'tools/windows/gpu-targets.json') -Raw | ConvertFrom-Json).targets
|
||||
$generated=Join-Path $OutputDirectory 'generated'
|
||||
New-Item -ItemType Directory -Force $generated | Out-Null
|
||||
$entries=($targets | ForEach-Object { " GpuTarget{`"$($_.architecture)`", $($_.elfMachine), $($_.isaMajor), `"$($_.family)`"}," }) -join "`n"
|
||||
@('#pragma once','#include <array>','namespace d4r::diag {',
|
||||
'struct GpuTarget { const char* architecture; unsigned elf_machine; unsigned isa_major; const char* family; };',
|
||||
"inline constexpr std::array<GpuTarget, $($targets.Count)> gpu_targets = {{",$entries,'}};','}') |
|
||||
Set-Content -LiteralPath (Join-Path $generated 'gpu_targets.generated.h') -Encoding ASCII
|
||||
$common=@('-std=c++17','-O1','-static','-DWIN32_LEAN_AND_MEAN','-DNOMINMAX','-D__HIP_PLATFORM_AMD__','-DD4R_TARGET_ARCH="gfx1201"',
|
||||
'-I',$generated,'-I',(Join-Path $repo 'tools/windows'),'-I',(Join-Path $repo 'tools'),'-I',(Join-Path $HipRoot 'include'))
|
||||
# Windows PowerShell turns native stderr into terminating errors under Stop;
|
||||
# judge native tools by exit code and keep their output in the log.
|
||||
function Invoke-Native([string]$Log,[string]$Exe,[string[]]$Arguments) {
|
||||
$ErrorActionPreference='Continue'
|
||||
& $Exe @Arguments *> $Log
|
||||
return $LASTEXITCODE
|
||||
}
|
||||
function Build([string]$Name,[string[]]$Arguments) {
|
||||
$log=Join-Path $OutputDirectory "build-$Name.log"
|
||||
# PowerShell 5 / Legacy native passing strips embedded quotes. PowerShell
|
||||
# 7's Standard/Windows passing preserves them and must not double-escape.
|
||||
if ($PSVersionTable.PSVersion.Major -le 5 -or $PSNativeCommandArgumentPassing -eq 'Legacy') {
|
||||
$Arguments=@($Arguments | ForEach-Object { $_.Replace('"','\"') })
|
||||
}
|
||||
if (Invoke-Native $log $cxx $Arguments) { throw "Build failed: $Name; see $log" }
|
||||
}
|
||||
$results=@()
|
||||
|
||||
# 1. d4r.ini validation and DLL pin decisions (the shim's own header).
|
||||
$probe=Join-Path $OutputDirectory 'player_config_probe.exe'
|
||||
Build 'player-config' ($common+@('-municode',(Join-Path $repo 'tools/windows/player_config_probe.cpp'),'-o',$probe,'-lversion','-ldbghelp','-lbcrypt'))
|
||||
if (Invoke-Native (Join-Path $OutputDirectory 'player-config.log') $probe @((Join-Path $OutputDirectory 'pins-fixture'))) { throw "player_config_probe failed; see $(Join-Path $OutputDirectory 'player-config.log')" }
|
||||
$results+=@{case='d4r-ini-and-dll-pins'; passed=$true}
|
||||
|
||||
# 2. Loader: GPU-free marker, LUID-based target selection, forwarding, and a
|
||||
# clear failure when the folder has no build for the detected GPU.
|
||||
$folder=Join-Path $OutputDirectory 'game/d4r'
|
||||
New-Item -ItemType Directory -Force (Join-Path $folder 'hip/bin') | Out-Null
|
||||
Build 'loader' ($common+@('-shared',(Join-Path $repo 'tools/windows/d4r_nvngx_loader.cpp'),'-o',(Join-Path $folder '_nvngx.dll'),'-lversion','-ldbghelp'))
|
||||
$harness=Join-Path $OutputDirectory 'loader_selection_probe.exe'
|
||||
Build 'harness' @('-municode','-static',(Join-Path $repo 'tools/windows/loader_selection_probe.cpp'),'-o',$harness,'-ldxgi','-ldxguid')
|
||||
$stub=Join-Path $OutputDirectory 'stub.cpp'
|
||||
@('#define API extern "C" __declspec(dllexport)',
|
||||
'struct Req { unsigned FeatureSupported, MinHWArchitecture; char MinOSVersion[255]; };',
|
||||
'API unsigned NVSDK_NGX_D3D12_GetFeatureRequirements(void*, const void*, Req* r) { r->FeatureSupported = 0; r->MinHWArchitecture = 0x5101; return 1; }') |
|
||||
Set-Content -LiteralPath $stub -Encoding ASCII
|
||||
$stubDll=Join-Path $OutputDirectory 'stub.dll'
|
||||
Build 'stub' @('-shared','-static',$stub,'-o',$stubDll)
|
||||
foreach ($name in @('amdhip64_7.dll','amd_comgr.dll','rocm_kpack.dll')) {
|
||||
$source=Join-Path $HipRoot "bin/$name"
|
||||
if (Test-Path -LiteralPath $source) { Copy-Item -LiteralPath $source -Destination (Join-Path $folder 'hip/bin') }
|
||||
}
|
||||
$log=Join-Path $OutputDirectory 'loader-without-builds.log'
|
||||
[void](Invoke-Native $log $harness @((Join-Path $folder '_nvngx.dll')))
|
||||
$text=Get-Content -LiteralPath $log -Raw
|
||||
if ($text -notmatch 'MARKER version=1 hip_loaded=0') { throw "Discovery marker loaded HIP or failed; see $log" }
|
||||
if ($text -match 'supported=0') { throw "An adapter was reported supported without any target build; see $log" }
|
||||
$gpuLog=Join-Path $folder 'd4r_nvngx.log'
|
||||
$detected=$null
|
||||
if (Test-Path -LiteralPath $gpuLog) { $detected=[regex]::Match((Get-Content -LiteralPath $gpuLog -Raw),'no build for your GPU \((gfx[0-9a-f]+)\)').Groups[1].Value }
|
||||
if (!$detected) {
|
||||
$results+=@{case='loader-selection'; passed=$null; skipped='no supported AMD HIP device detected'}
|
||||
Write-Host 'SKIP loader selection: no supported AMD HIP device detected on this machine.' -ForegroundColor Yellow
|
||||
} else {
|
||||
if ((Get-Content -LiteralPath $gpuLog -Raw) -notmatch 'It supports: none \(the d4r folder is incomplete') { throw "Unclear unsupported-GPU message; see $gpuLog" }
|
||||
Copy-Item -LiteralPath $stubDll -Destination (Join-Path $folder "_nvngx_$detected.dll")
|
||||
$log=Join-Path $OutputDirectory 'loader-with-build.log'
|
||||
[void](Invoke-Native $log $harness @((Join-Path $folder '_nvngx.dll')))
|
||||
if ((Get-Content -LiteralPath $log -Raw) -notmatch 'vendor=0x1002 status=0x1 supported=0 marker=0x5101') { throw "AMD adapter was not forwarded to _nvngx_$detected.dll; see $log" }
|
||||
$results+=@{case='loader-selection'; passed=$true; architecture=$detected; gpuKernelsExecuted=$false}
|
||||
}
|
||||
$results | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Write-Host "PASS PLAYER_CONFIG cases=$(@($results | Where-Object { $_.passed }).Count) skipped=$(@($results | Where-Object { $_.skipped }).Count) Report: $OutputDirectory"
|
||||
@@ -0,0 +1,96 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$TextureRoot, [string]$ControlTextureRoot, [string]$CommonTextureRoot, [string]$ZludaRoot, [string]$PackageRoot,
|
||||
[string]$OutputDirectory, [int]$Iterations=8, [string]$OutputResolution='512x288', [string]$InputResolution,
|
||||
[switch]$CandidateFirst,
|
||||
[ValidateSet('output','input')][string]$KernelStage='output')
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$PackageRoot) { $PackageRoot=Join-Path $repo 'dist/windows-rdna4-command-list' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$TextureRoot) { $TextureRoot=Join-Path $repo "build/private-textures-$GpuArch" }
|
||||
if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' }
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo 'test-results/k-native-texture-reference' }
|
||||
if ($OutputResolution -notmatch '^([0-9]+)x([0-9]+)$') { throw 'OutputResolution must be WIDTHxHEIGHT.' }
|
||||
$outputWidth=[int]$Matches[1]; $outputHeight=[int]$Matches[2]
|
||||
$combined=Join-Path $OutputDirectory 'private-native'
|
||||
New-Item -ItemType Directory -Force $combined | Out-Null
|
||||
foreach ($directory in @((Join-Path $repo "build/native-k-$GpuArch"),$CommonTextureRoot,$TextureRoot) | Where-Object { $_ }) {
|
||||
Get-ChildItem -LiteralPath $directory -Filter '*.hsaco' | Copy-Item -Destination $combined -Force
|
||||
}
|
||||
& python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $combined (Join-Path $repo 'nvngx_dlss.dll')
|
||||
if ($LASTEXITCODE) { throw 'Combined private manifest failed.' }
|
||||
$controlNative=Join-Path $repo "build/native-k-$GpuArch"
|
||||
if ($ControlTextureRoot -or $CommonTextureRoot) {
|
||||
$controlNative=Join-Path $OutputDirectory 'control-private-native'
|
||||
New-Item -ItemType Directory -Force $controlNative | Out-Null
|
||||
foreach ($directory in @((Join-Path $repo "build/native-k-$GpuArch"),$CommonTextureRoot,$ControlTextureRoot) | Where-Object { $_ }) {
|
||||
Get-ChildItem -LiteralPath $directory -Filter '*.hsaco' | Copy-Item -Destination $controlNative -Force
|
||||
}
|
||||
& python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $controlNative (Join-Path $repo 'nvngx_dlss.dll')
|
||||
if ($LASTEXITCODE) { throw 'Control private manifest failed.' }
|
||||
}
|
||||
$settings=@{D4R_VALIDATE_OUTPUT='1'; D4R_PROFILE_STAGES='1'; D4R_ZLUDA_PROFILE='1'; D4R_QUIET_API='1';
|
||||
D4R_ZLUDA_WMMA='1'; D4R_ZLUDA_WMMA_FP8='1'; D4R_ZLUDA_WMMA_FP8_NATIVE='0';
|
||||
D4R_ZLUDA_WMMA_F16_REFERENCE='1'; D4R_ZLUDA_PROFILE_DEFERRED=$null;
|
||||
ZLUDA_CACHE_DIR=(Join-Path $repo 'build/zluda-cache-windows'); PYTHONPATH=(Join-Path $repo '.tools/python/vendor');
|
||||
D4R_ZLUDA_NATIVE_DIR=$null}
|
||||
$old=@{}
|
||||
$stagedObjects=@{}
|
||||
foreach ($stage in @(@{name='control'; root=$controlNative},@{name='candidate'; root=$combined})) {
|
||||
$stagedObjects[$stage.name]=@{}
|
||||
foreach ($object in Get-ChildItem -LiteralPath $stage.root -Filter '*.hsaco') {
|
||||
Assert-D4RCodeObjectTarget $object.FullName $GpuArch
|
||||
$stagedObjects[$stage.name][$object.Name]=(Get-FileHash -LiteralPath $object.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
}
|
||||
}
|
||||
try {
|
||||
foreach ($key in $settings.Keys) { $old[$key]=[Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') }
|
||||
foreach ($flags in @(11)) {
|
||||
$variant=if ($flags -eq 11) { 'depthinv_mvlo_hdr' } else { 'depthreg_mvhi_ldr' }
|
||||
$suffix=if ($KernelStage -eq 'output') { '_max_v2_rel' } else { '_v2_rel' }
|
||||
$kernel="hiluma_engine_${KernelStage}_${variant}${suffix}"
|
||||
$order=if ($CandidateFirst) { @('candidate','control') } else { @('control','candidate') }
|
||||
foreach ($mode in $order) {
|
||||
$env:D4R_ZLUDA_NATIVE_DIR=if ($mode -eq 'control') { $controlNative } else { $combined }
|
||||
$expectedBackend=if (Test-Path -LiteralPath (Join-Path $env:D4R_ZLUDA_NATIVE_DIR ($kernel + '.hsaco'))) { 'native' } else { 'translated' }
|
||||
if ($mode -eq 'candidate' -and $expectedBackend -ne 'native') { throw "Missing candidate object: $kernel" }
|
||||
$arguments=@{RuntimeProfile='therock'; ZludaRoot=$ZludaRoot;
|
||||
PackageRoot=$PackageRoot; NgxCore=(Join-Path $repo '_nvngx.dll');
|
||||
DlssDll=(Join-Path $repo 'nvngx_dlss.dll'); NgxMode='d3d12'; NgxAbi='project-legacy'; Preset=11;
|
||||
NgxCreateFlags=$flags; NgxOutputResolution=$OutputResolution; NgxOnly=$true; RequireNativeNetwork=$true; CommandListBackend=$true;
|
||||
NgxInputResolution=$InputResolution;
|
||||
PixelProfile='depth-stencil'; BarrierMode='inherited-legacy'; CaptureExceptions=$true; EarlyIndirectProbe=$true;
|
||||
Iterations=$Iterations; TimeoutSeconds=1200; OptiScalerDll=(Join-Path $repo 'dist/optiscaler-windows-d4r/OptiScaler.dll');
|
||||
OutputDirectory=(Join-Path $OutputDirectory "$mode-$flags")}
|
||||
& (Join-Path $PSScriptRoot 'test-windows-rdna4.ps1') @arguments
|
||||
if ($LASTEXITCODE) { throw "Texture $mode flags=$flags workload failed." }
|
||||
& python (Join-Path $repo 'tools/windows/profile_report.py') $arguments.OutputDirectory --output (Join-Path $arguments.OutputDirectory 'profile.json')
|
||||
if ($LASTEXITCODE) { throw 'Texture profile report failed.' }
|
||||
$profile=Get-Content -LiteralPath (Join-Path $arguments.OutputDirectory 'profile.json') -Raw | ConvertFrom-Json
|
||||
$samples=@($profile.kernels | Where-Object { $_.kernel -eq $kernel -and $_.backend -eq $expectedBackend -and $_.phase -eq 'main' -and $_.serializing })
|
||||
if ($samples.Count -ne 1 -or $samples[0].gpuMs.samples -ne $Iterations) {
|
||||
throw "Missing valid serializing $KernelStage-kernel timings ($expectedBackend) for $mode flags=$flags; retain this run as correctness evidence only."
|
||||
}
|
||||
}
|
||||
$control=Join-Path $OutputDirectory "control-$flags"; $candidate=Join-Path $OutputDirectory "candidate-$flags"
|
||||
$stderr=Get-Content -LiteralPath (Join-Path $candidate 'd3d12-evaluate-preset-11.stderr.log') -Raw
|
||||
$hits=[regex]::Matches($stderr, ('\[d4r-launch\] kernel="' + $kernel + '" backend=native')).Count
|
||||
if ($hits -ne $Iterations) { throw "Texture override was not executed on every frame: $kernel hits=$hits" }
|
||||
& python (Join-Path $repo 'tools/windows/frame_compare.py') --reference $control --actual $candidate --width $outputWidth --height $outputHeight --exact
|
||||
if ($LASTEXITCODE) { throw "Texture flags=$flags full-frame RGB mismatch; preserve baseline." }
|
||||
}
|
||||
@{passed=$true; architecture=$GpuArch; framesPerVariant=$Iterations; outputResolution=$OutputResolution; createFlags=@(0,11); strictRgb=$true;
|
||||
inputResolution=$InputResolution;
|
||||
serializingProfile=$true; candidateFirst=[bool]$CandidateFirst; kernelStage=$KernelStage;
|
||||
controlTextureRoot=$ControlTextureRoot; commonTextureRoot=$CommonTextureRoot;
|
||||
stagedObjects=$stagedObjects;
|
||||
manifestSha256=(Get-FileHash -LiteralPath (Join-Path $TextureRoot 'd4r-kernels.txt') -Algorithm SHA256).Hash.ToLowerInvariant();
|
||||
privateNvidiaDerivedCode=$true; source=(Get-Content -LiteralPath (Join-Path $TextureRoot 'build-info.json') -Raw | ConvertFrom-Json)} |
|
||||
ConvertTo-Json -Depth 12 | Set-Content -LiteralPath (Join-Path $TextureRoot 'validation.json') -Encoding UTF8
|
||||
Write-Host "PASS: both private K $KernelStage variants executed and matched every RGB component. $OutputDirectory"
|
||||
} finally {
|
||||
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
# Offline installer fixture. Uses locally supplied NVIDIA DLLs and never writes to a game folder.
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$NgxCore,
|
||||
[Parameter(Mandatory=$true)][string]$DlssDll,
|
||||
[Parameter(Mandatory=$true)][string]$DlssdDll,
|
||||
[string]$OutputDirectory
|
||||
)
|
||||
$ErrorActionPreference='Stop'
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/windows-dlss-installer/'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||
if (Test-Path -LiteralPath $OutputDirectory) { throw "Use a new output directory: $OutputDirectory" }
|
||||
$NgxCore=(Get-Item -LiteralPath $NgxCore -ErrorAction Stop).FullName
|
||||
$DlssDll=(Get-Item -LiteralPath $DlssDll -ErrorAction Stop).FullName
|
||||
$DlssdDll=(Get-Item -LiteralPath $DlssdDll -ErrorAction Stop).FullName
|
||||
$expected=@{
|
||||
'_nvngx.dll'='66767018c36b3bab46398dade3adf173daa3730fda75965689ea848c9bc4e79b'
|
||||
'nvngx_dlss.dll'='3975567b8943c53acce397f2b72380092f84f162d00b0d2c7d08a1025c563983'
|
||||
'nvngx_dlssd.dll'='4bc7ea5fcb2f32cf86bc2cb072e8d2860914a0be511cbdcb2fc206c1d9d83b80'
|
||||
}
|
||||
$inputs=@{'_nvngx.dll'=$NgxCore;'nvngx_dlss.dll'=$DlssDll;'nvngx_dlssd.dll'=$DlssdDll}
|
||||
foreach($name in $inputs.Keys) {
|
||||
if ((Get-FileHash -LiteralPath $inputs[$name] -Algorithm SHA256).Hash.ToLowerInvariant() -ne $expected[$name]) {
|
||||
throw "Wrong local NVIDIA DLL for installer fixture: $name"
|
||||
}
|
||||
}
|
||||
$package=Join-Path $OutputDirectory 'package'
|
||||
$runtime=Join-Path $package 'd4r'
|
||||
$cache=Join-Path $OutputDirectory 'cache'
|
||||
$backupRoot=Join-Path $OutputDirectory 'backups'
|
||||
$nvidiaCache=Join-Path $cache 'nvidia'
|
||||
New-Item -ItemType Directory -Force (Join-Path $runtime 'ngx'),(Join-Path $runtime 'dlss'),(Join-Path $runtime 'rr'),$nvidiaCache | Out-Null
|
||||
Copy-Item -LiteralPath (Join-Path $repo 'packaging/windows/d4r.ini') -Destination (Join-Path $runtime 'd4r.ini')
|
||||
Copy-Item -LiteralPath (Join-Path $repo 'tools/windows/dll-pins.txt') -Destination (Join-Path $runtime 'dll-pins.txt')
|
||||
$targets=@(
|
||||
@{source=$DlssDll; name='nvngx_dlss.dll'; relative='dlss/nvngx_dlss.dll'},
|
||||
@{source=$DlssdDll; name='nvngx_dlssd.dll'; relative='rr/nvngx_dlssd.dll'},
|
||||
@{source=$NgxCore; name='_nvngx.dll'; relative='ngx/_nvngx.dll'}
|
||||
)
|
||||
$originals=@{}
|
||||
foreach($target in $targets) {
|
||||
$path=Join-Path $runtime $target.relative
|
||||
$original="fixture original $($target.name)"
|
||||
Set-Content -LiteralPath $path $original -Encoding ASCII
|
||||
$originals[$target.relative]=(Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash
|
||||
Copy-Item -LiteralPath $target.source -Destination (Join-Path $nvidiaCache $target.name)
|
||||
}
|
||||
|
||||
$installer=Join-Path $PSScriptRoot 'install-dlss.ps1'
|
||||
$powershell=Join-Path $env:SystemRoot 'System32/WindowsPowerShell/v1.0/powershell.exe'
|
||||
function Invoke-Installer([string]$name) {
|
||||
$log=Join-Path $OutputDirectory "$name.log"
|
||||
& $powershell -NoProfile -ExecutionPolicy Bypass -File $installer -PackageRoot $package -CacheRoot $cache -BackupRoot $backupRoot -Yes *> $log
|
||||
if ($LASTEXITCODE) { throw "Installer fixture failed ($name); see $log" }
|
||||
return Get-Content -LiteralPath $log -Raw
|
||||
}
|
||||
$first=Invoke-Installer 'install'
|
||||
foreach($target in $targets) {
|
||||
$path=Join-Path $runtime $target.relative
|
||||
if ((Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash.ToLowerInvariant() -ne $expected[$target.name]) {
|
||||
throw "Installed hash mismatch: $($target.relative)"
|
||||
}
|
||||
}
|
||||
$manifests=@(Get-ChildItem -LiteralPath $backupRoot -Recurse -Filter 'backup-manifest.json' -File)
|
||||
if ($manifests.Count -ne 1) { throw "Expected one outside backup manifest, found $($manifests.Count)" }
|
||||
$manifest=Get-Content -LiteralPath $manifests[0].FullName -Raw | ConvertFrom-Json
|
||||
if (@($manifest.files).Count -ne 3) { throw 'Expected three backed-up originals.' }
|
||||
foreach($entry in $manifest.files) {
|
||||
if (!$entry.backup -or !(Test-Path -LiteralPath $entry.backup) -or $entry.sha256 -ne $originals[$entry.path.Substring($runtime.Length+1).Replace('\','/')]) {
|
||||
throw "Backup path or original hash mismatch: $($entry.path)"
|
||||
}
|
||||
if ((Get-FileHash -LiteralPath $entry.backup -Algorithm SHA256).Hash -ne $entry.sha256) { throw "Backup bytes changed: $($entry.backup)" }
|
||||
}
|
||||
$second=Invoke-Installer 'rerun'
|
||||
if ($second -notmatch 'All three NVIDIA DLLs already match their pinned hashes\.' -or
|
||||
@(Get-ChildItem -LiteralPath $backupRoot -Recurse -Filter 'backup-manifest.json' -File).Count -ne 1) {
|
||||
throw 'Installer rerun was not idempotent.'
|
||||
}
|
||||
@{passed=$true;files=3;outsideBackups=3;idempotent=$true;archiveDownloaded=$false;driverRun=$false} |
|
||||
ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Write-Host "PASS WINDOWS_DLSS_INSTALLER files=3 backups=3 idempotent=1 Report: $OutputDirectory"
|
||||
@@ -0,0 +1,145 @@
|
||||
# Isolated installer checks; never starts a real game or launches GPU kernels.
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$PackageRoot,
|
||||
[Parameter(Mandatory=$true)][string]$NgxCore,
|
||||
[Parameter(Mandatory=$true)][string]$DlssDll,
|
||||
[string]$OutputDirectory
|
||||
)
|
||||
$ErrorActionPreference='Stop'
|
||||
if ($PSVersionTable.PSVersion.Major -ge 6) {
|
||||
# The fixture uses the .NET Framework C# compiler; the shipped launcher
|
||||
# also runs Windows PowerShell 5. PowerShell 7 cannot emit an EXE via Add-Type.
|
||||
$arguments=@('-NoProfile','-STA','-ExecutionPolicy','Bypass','-File',$PSCommandPath,
|
||||
'-PackageRoot',$PackageRoot,'-NgxCore',$NgxCore,'-DlssDll',$DlssDll)
|
||||
if ($OutputDirectory) { $arguments+=@('-OutputDirectory',$OutputDirectory) }
|
||||
& "$env:SystemRoot/System32/WindowsPowerShell/v1.0/powershell.exe" @arguments
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
$PackageRoot=(Get-Item -LiteralPath $PackageRoot).FullName
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/windows-quick-test/'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
function Invoke-Child([string]$Name,[string[]]$Arguments,[int]$ExpectedExit=0) {
|
||||
$log=Join-Path $OutputDirectory "$Name.log"
|
||||
& "$env:SystemRoot/System32/WindowsPowerShell/v1.0/powershell.exe" -NoProfile -STA -ExecutionPolicy Bypass `
|
||||
-File (Join-Path $PackageRoot 'files/quick-test.ps1') @Arguments *> $log
|
||||
if ($LASTEXITCODE -ne $ExpectedExit) { throw "Unexpected exit $LASTEXITCODE for $Name; see $log" }
|
||||
}
|
||||
function Fixture([string]$Name) {
|
||||
$dir=Join-Path $OutputDirectory $Name
|
||||
New-Item -ItemType Directory -Force $dir | Out-Null
|
||||
$exe=Join-Path $dir 'Fixture.exe'
|
||||
Set-Content -LiteralPath $exe 'installer fixture; do not execute' -Encoding ASCII
|
||||
return $exe
|
||||
}
|
||||
$results=@()
|
||||
$exe=Fixture ('space GPU '+[char]0x03a9+' test')
|
||||
$game=Split-Path $exe
|
||||
$before=@{}
|
||||
foreach ($name in @('dxgi.dll','OptiScaler.ini','nvngx_dlss.dll')) {
|
||||
Set-Content -LiteralPath (Join-Path $game $name) "original $name" -Encoding ASCII
|
||||
$before[$name]=(Get-FileHash -LiteralPath (Join-Path $game $name)).Hash
|
||||
}
|
||||
Invoke-Child 'install' @('-Action','install','-GameExe',$exe,'-NgxCore',$NgxCore,'-DlssDll',$DlssDll)
|
||||
if ((Get-FileHash -LiteralPath (Join-Path $game 'd4r/dlss/nvngx_dlss.dll')).Hash -ne (Get-FileHash -LiteralPath $DlssDll).Hash) {
|
||||
throw 'Numeric NGX driver SR feature DLL was not staged in its isolated search directory.'
|
||||
}
|
||||
if ((Get-FileHash -LiteralPath (Join-Path $game 'd4r/ngx/_nvngx.dll')).Hash -ne (Get-FileHash -LiteralPath $NgxCore).Hash) {
|
||||
throw 'NGX core was not staged at the player-mode path d4r/ngx/_nvngx.dll.'
|
||||
}
|
||||
if (!(Test-Path -LiteralPath (Join-Path $game 'd4r/d4r.ini'))) {
|
||||
throw 'd4r.ini was not staged; Steam player mode cannot configure its runtime paths.'
|
||||
}
|
||||
if (!(Test-Path -LiteralPath (Join-Path $game 'd4r/dll-pins.txt'))) {
|
||||
throw 'dll-pins.txt was not staged; Steam player mode cannot verify its NVIDIA DLLs.'
|
||||
}
|
||||
if (!(Test-Path -LiteralPath (Join-Path $game 'd4r/nvapi/backend/nvapi64.dll'))) {
|
||||
throw 'Raw ZLUDA NVAPI backend is missing from d4r/nvapi/backend.'
|
||||
}
|
||||
if ((Get-FileHash -LiteralPath (Join-Path $game 'nvngx_dlss.dll')).Hash -ne $before['nvngx_dlss.dll']) { throw 'Original application DLSS DLL was changed.' }
|
||||
$saved=Get-Content -LiteralPath (Join-Path $PackageRoot 'work/settings.json') -Raw | ConvertFrom-Json
|
||||
if ($saved.game -ne $exe -or $saved.architecture -ne 'gfx1201') { throw 'GPU detection or remembered selection failed on this RX9070XT fixture.' }
|
||||
# Restore uses remembered game selection and needs neither HIP nor NVIDIA DLLs.
|
||||
Invoke-Child 'restore' @('-Action','restore')
|
||||
foreach ($name in $before.Keys) { if ((Get-FileHash -LiteralPath (Join-Path $game $name)).Hash -ne $before[$name]) { throw 'Original game bytes not restored.' } }
|
||||
if (Test-Path -LiteralPath (Join-Path $game 'd4r')) { throw 'Runtime directory remains after restore.' }
|
||||
$results+=@{case='auto-gfx1201-install-remember-restore'; passed=$true; gpuKernelsExecuted=$false}
|
||||
$manifest=Get-Content -LiteralPath (Join-Path $PackageRoot 'files/package.json') -Raw | ConvertFrom-Json
|
||||
if ($manifest.bundledNvidia) {
|
||||
# Bundled originals take precedence over stale remembered shim selections;
|
||||
# explicit overrides still go through the existing strict negative gates.
|
||||
$exe=Fixture 'bundled auto selection'
|
||||
$saved.ngxCore=Join-Path $PackageRoot 'files/targets/gfx1201/d4r/_nvngx.dll'
|
||||
$saved.dlssDll=$saved.ngxCore
|
||||
$saved | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $PackageRoot 'work/settings.json') -Encoding UTF8
|
||||
Invoke-Child 'bundled-install' @('-Action','install','-GameExe',$exe)
|
||||
$game=Split-Path $exe
|
||||
foreach ($pair in @(@{path='d4r/ngx/_nvngx.dll'; expected=$NgxCore},@{path='d4r/dlss/nvngx_dlss.dll'; expected=$DlssDll})) {
|
||||
if ((Get-FileHash -LiteralPath (Join-Path $game $pair.path)).Hash -ne (Get-FileHash -LiteralPath $pair.expected).Hash) { throw 'Bundled original DLL selection failed.' }
|
||||
}
|
||||
$report=Get-ChildItem -LiteralPath (Join-Path $PackageRoot 'results') -Directory | Sort-Object Name -Descending | Select-Object -First 1
|
||||
$status=Get-Content -LiteralPath (Join-Path $report.FullName 'quick-test.json') -Raw | ConvertFrom-Json
|
||||
if ($status.nvidiaDllSource.core -ne 'bundled' -or $status.nvidiaDllSource.dlss -ne 'bundled') { throw 'Bundled DLL provenance is missing.' }
|
||||
Invoke-Child 'bundled-restore' @('-Action','restore','-GameExe',$exe)
|
||||
if (Test-Path -LiteralPath (Join-Path $game 'd4r')) { throw 'Bundled runtime directory remains after restore.' }
|
||||
$results+=@{case='bundled-auto-selection-ignores-stale-choices'; passed=$true; gpuKernelsExecuted=$false}
|
||||
$core=Join-Path (Join-Path $PackageRoot 'files') $manifest.bundledNvidia.ngxCore
|
||||
$original=[IO.File]::ReadAllBytes($core)
|
||||
try {
|
||||
[IO.File]::AppendAllText($core,'tampered bundled fixture')
|
||||
$exe=Fixture 'tampered bundled core'
|
||||
Invoke-Child 'tampered-bundled-core' @('-Action','install','-GameExe',$exe) 1
|
||||
$game=Split-Path $exe
|
||||
if ((Test-Path -LiteralPath (Join-Path $game '.d4r-backup')) -or (Test-Path -LiteralPath (Join-Path $game 'dxgi.dll'))) { throw 'Tampered bundled DLL changed game files.' }
|
||||
$results+=@{case='tampered-bundled-core'; passed=$true; rejectedBeforeGameChanges=$true; gpuKernelsExecuted=$false}
|
||||
} finally { [IO.File]::WriteAllBytes($core,$original) }
|
||||
}
|
||||
# A harmless child refuses attached debuggers and produces an empty stderr.
|
||||
# The default launcher must allow it to exit and must report no DLSS session.
|
||||
$exe=Join-Path (Join-Path $OutputDirectory 'launcher-fixture') 'Fixture.exe'
|
||||
New-Item -ItemType Directory -Force (Split-Path $exe) | Out-Null
|
||||
Add-Type -TypeDefinition @'
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
public static class D4RLauncherFixture {
|
||||
[DllImport("kernel32.dll")] private static extern bool IsDebuggerPresent();
|
||||
public static int Main(string[] args) { return IsDebuggerPresent() ? 91 : 0; }
|
||||
}
|
||||
'@ -OutputAssembly $exe -OutputType ConsoleApplication
|
||||
Invoke-Child 'run-no-debug' @('-Action','run','-GameExe',$exe,'-NgxCore',$NgxCore,'-DlssDll',$DlssDll) 1
|
||||
$report=Get-ChildItem -LiteralPath (Join-Path $PackageRoot 'results') -Directory | Sort-Object Name -Descending | Select-Object -First 1
|
||||
$status=Get-Content -LiteralPath (Join-Path $report.FullName 'quick-test.json') -Raw | ConvertFrom-Json
|
||||
$environment=Get-Content -LiteralPath (Join-Path $report.FullName 'environment.json') -Raw | ConvertFrom-Json
|
||||
if ($environment.attachedDebugger -or $status.hardwareValidationCompleted -or
|
||||
$status.status -ne 'inconclusive-process-exited-without-dlss-frames' -or $status.gameSummary.exitCode -ne '0x0' -or
|
||||
(Get-Item -LiteralPath (Join-Path $report.FullName 'd4r.stderr.log')).Length -ne 0) {
|
||||
throw 'Default launch attaches a debugger, mishandles empty stderr or falsely validates a launcher exit.'
|
||||
}
|
||||
Invoke-Child 'restore-launcher-fixture' @('-Action','restore','-GameExe',$exe)
|
||||
$results+=@{case='run-no-debug-empty-stderr-no-false-pass'; passed=$true; gpuKernelsExecuted=$false}
|
||||
foreach ($case in @('wrong-core','wrong-dlss','vkd3d')) {
|
||||
$exe=Fixture $case; $game=Split-Path $exe
|
||||
$core=if ($case -eq 'wrong-core') { Join-Path $PackageRoot 'files/targets/gfx1201/d4r/_nvngx.dll' } else { $NgxCore }
|
||||
$dlss=if ($case -eq 'wrong-dlss') { $NgxCore } else { $DlssDll }
|
||||
if ($case -eq 'vkd3d') { Set-Content -LiteralPath (Join-Path $game 'd3d12.dll') 'vkd3d-proton fixture; do not load' -Encoding ASCII }
|
||||
Invoke-Child $case @('-Action','install','-GameExe',$exe,'-NgxCore',$core,'-DlssDll',$dlss) 1
|
||||
if ((Test-Path -LiteralPath (Join-Path $game '.d4r-backup')) -or (Test-Path -LiteralPath (Join-Path $game 'dxgi.dll'))) { throw "$case changed game files before rejection." }
|
||||
$message=Get-Content -LiteralPath (Join-Path $OutputDirectory "$case.log") -Raw
|
||||
$match=[regex]::Match($message,'SEND THIS ONE FILE: (.+\.zip)')
|
||||
if (!$match.Success -or !(Test-Path -LiteralPath $match.Groups[1].Value.Trim())) { throw "$case did not produce its single report ZIP." }
|
||||
$results+=@{case=$case; passed=$true; rejectedBeforeGameChanges=$true; gpuKernelsExecuted=$false}
|
||||
}
|
||||
$readme=Join-Path $PackageRoot 'READ-ME-FIRST.txt'
|
||||
$original=[IO.File]::ReadAllBytes($readme)
|
||||
try {
|
||||
Add-Content -LiteralPath $readme 'tampered fixture'
|
||||
$exe=Fixture 'tampered-package'
|
||||
Invoke-Child 'tampered-package' @('-Action','install','-GameExe',$exe,'-NgxCore',$NgxCore,'-DlssDll',$DlssDll) 1
|
||||
$game=Split-Path $exe
|
||||
if ((Test-Path -LiteralPath (Join-Path $game '.d4r-backup')) -or (Test-Path -LiteralPath (Join-Path $game 'dxgi.dll'))) { throw 'Tampered package changed game files.' }
|
||||
$results+=@{case='tampered-package'; passed=$true; rejectedBeforeGameChanges=$true; gpuKernelsExecuted=$false}
|
||||
} finally { [IO.File]::WriteAllBytes($readme,$original) }
|
||||
$results | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Write-Host "PASS QUICK_TEST_FIXTURES cases=$($results.Count) real_game_untouched=1 Report: $OutputDirectory"
|
||||
Loaded 100 of 430 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user