Build patched Windows ZLUDA and execute native NGX DLSS diagnostics

This commit is contained in:
Codex committed 2026-09-30 16:35:30 +03:00
1 parent 64472e5a61
commit 3c28a82e34
17 files changed
+1114 -13

No files matched your search

+6 -1
View File
@@ -59,7 +59,7 @@ foreach(family k m)
endforeach()
endforeach()
add_custom_target(d4r_native_gfx1201_compile ALL DEPENDS ${native_objects})
foreach(probe hip_gfx1201_probe gfx12_wmma_probe k_module_probe cuda_driver_probe d3d12_hip_interop_probe hip_stream_lifecycle_probe ngx_cuda_init_probe)
foreach(probe hip_gfx1201_probe gfx12_wmma_probe k_module_probe cuda_driver_probe cuda_image_probe d3d12_hip_interop_probe hip_stream_lifecycle_probe ngx_cuda_init_probe)
add_executable(d4r_${probe} tools/windows/${probe}.cpp)
target_compile_definitions(d4r_${probe} PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__)
target_include_directories(d4r_${probe} PRIVATE "${D4R_HIP_ROOT}/include")
@@ -146,4 +146,9 @@ if(EXISTS "${D4R_ZLUDA_ROOT}/nvcuda.dll")
--context ${context} --iterations 32)
set_tests_properties(cuda_${context} PROPERTIES TIMEOUT 180 LABELS "hardware;zluda")
endforeach()
if(EXISTS "${D4R_ZLUDA_ROOT}/build-info.json")
add_test(NAME cuda_images COMMAND d4r_cuda_image_probe
--hip-root "${D4R_HIP_ROOT}" --cuda-dll "${D4R_ZLUDA_ROOT}/nvcuda.dll" --iterations 32)
set_tests_properties(cuda_images PROPERTIES TIMEOUT 90 LABELS "hardware;zluda;descriptors")
endif()
endif()
+94 -5
View File
@@ -6,8 +6,8 @@ This is a work log for an implementation in progress, not a claim of DLSS suppor
## Milestone and gates
Current milestone: M1/M2/M4/M5 passed; NGX init + DLSS CUDA capability discovery
passed with the user's DLLs. M6 is in progress: K `enc1` and `enc2` prep and
Current milestone: M1/M2/M4/M5 passed; native NGX Create/Evaluate on the simple
requested E path passes with the user's DLLs. M6 is in progress: K `enc1` and `enc2` prep and
transformers run on gfx1201 and pass synthetic nonzero numpy reference checks.
Do not integrate NGX until the integer PTX workload is stable on the real GPU.
@@ -16,7 +16,7 @@ Do not integrate NGX until the integer PTX workload is stable on the real GPU.
| M0: branch, audit, Windows build and diagnostics | Initial implementation complete |
| M1: native HIP gfx1201 allocation, kernel, CPU verification | PASS, 32 iterations + guard verification |
| M2: Windows ZLUDA integer PTX, primary and created contexts | PASS, 32 iterations each + guard verification |
| M3: Windows NGX initialization and simple DLSS path | Init + SR capabilities PASS; Evaluate/transformer pending |
| M3: Windows NGX initialization and simple DLSS path | Init, SR capabilities, Create/Evaluate PASS; four finite synthetic frames; K/M pending |
| M4: D3D12 / HIP external memory and fence round trip | PASS with TheRock; stable 7.2 has mapped-view leak |
| M5: independently validated gfx12 WMMA backend | PASS: raw + legacy adapter + upstream layout, max abs/relative error 0 |
| M6: K layers, full transformer and image validation | Partial: `enc1`/`enc2` execute and match nonzero references; other layers/full network pending |
@@ -68,8 +68,8 @@ and its `enc1`/`enc2` objects were checked on this RX 9070 XT against our fixtur
unvalidated until K and its full DLSS integration are correct.
* New ZLUDA patches `0006`/`0007` add k8 WMMA, prep slots, wave64 for MMA-free
kernels, gfx12 helpers and FP8 selection/cache identity. These patches are
present in our branch but have not been built or validated in Windows ZLUDA.
Our current integer PTX test uses the unpatched `v7-preview.11` binary.
now built into native Windows ZLUDA. Integer PTX and resource descriptor
tests pass on hardware; complete K/M lowering remains to be validated.
* Upstream restored `kernels/tools/model_enc3.py`, resolving the missing M
reference dependency found in the initial audit. New check/replay utilities
cover K/M and texture tails. Texture artifacts still require local DLL/PTX
@@ -121,6 +121,95 @@ build the d4r-patched Windows ZLUDA and implement same-frame Windows NGX Evaluat
with explicit D3D12 queue submission ownership. Full K precedes M and native
FP8/performance work. No complete DLSS or OptiScaler game result is claimed.
## Native Windows ZLUDA build and Evaluate work (2026-09-30)
The local fork now builds natively under Windows, including d4r patches
`0002`–`0007`. New `0008-native-windows-build.patch` preserves MSVC/Linux builds
and adds LLVM-MinGW support: prebuilt LLVM selection, GNU delay-load flags,
Windows LLVM system-library propagation, configurable HiGHS C++ library,
public OCKL assertion reporting, and optional TaskDialog lookup. It does not
modify NVIDIA binaries. The SDK's `long` shuffle overloads need an LLP64 fix
in a private header mirror; `build-zluda-helpers.ps1` applies it without touching
the installed HIP SDK. Helper bitcode is rebuilt in all four wave/FP variants.
Pinned additional build dependencies:
* Portable official Rust/Cargo `1.98.1`, `x86_64-pc-windows-gnu`, release
`2026-09-03`. `setup-rust-toolchain.ps1` verifies SHA256 of rustc, Cargo,
rust-std and rust-mingw archives and installs only under `.tools`.
* ZLUDA LLVM submodule `ff4dc1f7c9e1c64d4d69e40f4ed30c2280a96dfd`,
`llvm-config` reports `22.0.0git`. AMDGPU/LLVM/LLD are built with LLVM-MinGW
`20260922`; no Linux build host or runtime is used.
* HiGHS submodule `364c83a51e44ba6c27def9c8fc1a49b1daf5ad5c`.
* The LLVM IR helper producer is stable HIP 7.2/LLVM 21. The final target is
selected by the translator from HIP `gfx1201`; generic helper generation
strips its temporary target attributes, as the existing Linux builder does.
* `dist/zluda-windows-native` contains the built CUDA/NVAPI/trace DLLs,
`d4r_emit.exe`, local open-source libc++/libunwind DLLs and build hashes.
This directory contains no NVIDIA DLLs, extracted PTX or weights.
Build sequence after preparing the pinned ZLUDA source/submodules:
```powershell
powershell -NoProfile -File scripts/windows/setup-rust-toolchain.ps1
powershell -NoProfile -File scripts/windows/prepare-zluda-source.ps1
powershell -NoProfile -File scripts/windows/build-zluda-llvm.ps1
powershell -NoProfile -File scripts/windows/build-zluda-helpers.ps1
powershell -NoProfile -File scripts/windows/build-zluda-windows.ps1
```
`prepare-zluda-source.ps1` refuses to reapply patches over a modified checkout.
For an already prepared checkout, rebuild only the affected components.
Configure/fetch/build logs remain under `build/zluda-*`.
The NGX probe now accepts `--ngx-mode evaluate`, `--preset 5|11|13` and a frame
count. Its synthetic CUDA-array path is a **debug harness** with host uploads
and final readback, not the game's fast path. It initializes output to NaN,
checks all RGBA components for finite values, checks RGB variance and saves
unmodified `.rgba16f` plus BMP previews. A successful Evaluate alone is logged
as `network_validation=pending`. The diagnostic exception handler also saves
first-chance fault details/minidumps before NGX's own final filter takes over.
The first stock-ZLUDA E test failed during CreateFeature (`0xc0000005`);
trace recorded missing `cuda_histogram_kernel`/`cuda_dldn_engine_histogram_kernel`
with unrecognized `tex.base.2d.v4.f32.s32`. This is a captured failure, not a
successful DLSS result. Bundle: `test-results/ngx-evaluate-e-stock.zip`.
The first custom build then exposed missing libc++ deployment and an eager
Common Controls v6 import; both are corrected. `0009-resource-descriptor-queries`
implements texture and 2D/3D array queries. Texture descriptors are tracked
from creation and removed on destruction: probing a surface as a texture
returns an API error instead of dereferencing HIP host metadata. This also
preserves CUDA flags/reserved bytes, which HIP's query does not fully initialize.
Hardware evidence with the patched runtime:
* Full runner `native-zluda-descriptors-milestone`: **12/12 PASS**, with 32 PTX
iterations per context, 32 CUDA image iterations and unchanged HIP/K/interop
checks. Package/default DLL selection prefers the locally built runtime.
* `cuda-images-kind.stdout.log`: 32 iterations of 1/2/4-channel arrays,
texture/surface descriptor and storage round trips, plus wrong-kind errors.
* `ngx-evaluate-e-kindfix`: requested preset E Create/Evaluate **PASS** for one synthetic
frame, finite RGBA, nonconstant output saved in the private result bundle.
This establishes the first native Windows DLSS CUDA execution path. K/M
transformer validation and D3D12 game integration are still pending.
* `ngx-evaluate-e-four-frames`: four consecutive finite synthetic frames,
reset only on frame 0, saved RGBA16F/BMP outputs. CTest after self-contained
CUDA dependency lookup: **9/9 PASS**. Fresh pinned-source application of all
eight patches passes `check-zluda-patches.ps1`.
* First-chance capture successfully saved two minidumps for the preceding
surface-as-texture HIP crash; `ngx-evaluate-e-arraydesc.zip` preserves it.
Example focused diagnostic (after the full foundational checks), using the
user's local DLLs:
```powershell
powershell -NoProfile -File scripts/windows/test-windows-rdna4.ps1 -RuntimeProfile therock -NgxOnly -NgxMode evaluate -Preset 5 -Iterations 4 -NgxCore C:\Users\Administrator\d4r\_nvngx.dll -DlssDll C:\Users\Administrator\d4r\nvngx_dlss.dll
```
`-NgxOnly` is for iterative NGX debugging; its summary explicitly says that
foundational checks were skipped. Private trace bundles can contain extracted
NVIDIA PTX/weights and must not be included in public release packages.
## Architecture and platform boundaries
Existing path:
@@ -0,0 +1,203 @@
Windows LLVM-MinGW build, portable helper assertions and optional diagnostic UI.
Apply after upstream d4r patches 0002 through 0007, on ZLUDA ee2f25a.
diff --git a/ext/highs-sys/build.rs b/ext/highs-sys/build.rs
--- a/ext/highs-sys/build.rs
+++ b/ext/highs-sys/build.rs
@@ -66,11 +66,12 @@
let linux = target.contains("linux");
let mingw = target.contains("pc-windows-gnu");
if apple {
println!("cargo:rustc-link-lib=dylib=c++");
} else if linux || mingw {
- println!("cargo:rustc-link-lib=dylib=stdc++");
+ let cxx_library = env::var("CXXSTDLIB").unwrap_or_else(|_| "stdc++".to_owned());
+ println!("cargo:rustc-link-lib=dylib={cxx_library}");
}
println!("cargo:rerun-if-changed=../HiGHS/highs/interfaces/highs_c_api.h");
true
}
diff --git a/ext/hip_runtime-sys/build.rs b/ext/hip_runtime-sys/build.rs
--- a/ext/hip_runtime-sys/build.rs
+++ b/ext/hip_runtime-sys/build.rs
@@ -1,8 +1,12 @@
fn main() {
if cfg!(windows) {
- println!("cargo:rustc-link-arg=/DELAYLOAD:amdhip64_7.dll");
+ if std::env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("gnu") {
+ println!("cargo:rustc-link-arg=-Wl,--delayload=amdhip64_7.dll");
+ } else {
+ println!("cargo:rustc-link-arg=/DELAYLOAD:amdhip64_7.dll");
+ }
} else {
println!("cargo:rustc-link-lib=dylib=amdhip64");
println!("cargo:rustc-link-search=native=/opt/rocm/lib/");
}
}
diff --git a/llvm_zluda/build.rs b/llvm_zluda/build.rs
--- a/llvm_zluda/build.rs
+++ b/llvm_zluda/build.rs
@@ -19,10 +19,18 @@
fn main() {
let bc_path1 = "src/device-libs/ockl.bc";
println!("cargo:rerun-if-changed={}", bc_path1);
check_lfs_file(bc_path1);
+
+ println!("cargo:rerun-if-env-changed=ZLUDA_LLVM_BUILD_DIR");
+ if let Some(directory) = env::var_os("ZLUDA_LLVM_BUILD_DIR") {
+ let llvm_build_path = PathBuf::from(directory);
+ println!("cargo:build-path={}", llvm_build_path.display());
+ link_prebuilt_llvm(&llvm_build_path);
+ return;
+ }
let mut cmake = Config::new(r"../ext/llvm-project/llvm");
try_use_sccache(&mut cmake);
try_use_ninja(&mut cmake);
cmake
@@ -57,16 +65,31 @@
cmake.build();
}
let cmake_profile = cmake.get_profile();
let llvm_build_path = get_llvm_build_path(&llvm_dir, cmake_profile);
println!("cargo:build-path={}", llvm_build_path.display());
+ link_prebuilt_llvm(&llvm_build_path);
+}
+
+fn link_prebuilt_llvm(llvm_build_path: &PathBuf) {
let (cxxflags, ldflags, libdir, lib_names, system_libs) =
llvm_config(&llvm_build_path.join("bin/llvm-config")).unwrap();
println!("cargo:rustc-link-arg={ldflags}");
println!("cargo:rustc-link-search=native={libdir}");
for lib in system_libs.split_ascii_whitespace() {
- println!("cargo:rustc-link-arg={lib}");
+ if let Some(name) = lib.strip_prefix("-l") {
+ println!("cargo:rustc-link-lib={name}");
+ } else {
+ println!("cargo:rustc-link-arg={lib}");
+ }
+ }
+ // llvm-config omits system libraries on Windows. Propagate these through
+ // this rlib to its final cdylib consumer (link-arg alone cannot do that).
+ if env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") {
+ for library in ["psapi", "shell32", "ole32", "uuid", "advapi32", "ws2_32", "ntdll"] {
+ println!("cargo:rustc-link-lib={library}");
+ }
}
link_lld_components();
link_llvm_components(lib_names);
compile_cxx_lib(cxxflags, &llvm_build_path);
}
diff --git a/zluda/build.rs b/zluda/build.rs
--- a/zluda/build.rs
+++ b/zluda/build.rs
@@ -1,11 +1,15 @@
use vergen_gix::{Emitter, Gix};
fn main() {
if cfg!(windows) {
- println!("cargo:rustc-link-arg=delayimp.lib");
- println!("cargo:rustc-link-arg=/DELAYLOAD:amdhip64_7.dll");
+ if std::env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("gnu") {
+ println!("cargo:rustc-link-arg=-Wl,--delayload=amdhip64_7.dll");
+ } else {
+ println!("cargo:rustc-link-arg=delayimp.lib");
+ println!("cargo:rustc-link-arg=/DELAYLOAD:amdhip64_7.dll");
+ }
let dll_path = "bin/nvcudart_hybrid64.dll";
println!("cargo:rerun-if-changed={}", dll_path);
check_lfs_file(dll_path);
}
let git = Gix::builder().sha(false).build();
diff --git a/zluda_windows/src/lib.rs b/zluda_windows/src/lib.rs
--- a/zluda_windows/src/lib.rs
+++ b/zluda_windows/src/lib.rs
@@ -11,27 +11,27 @@
use uuid::uuid;
use widestring::{u16str, U16Str, U16String};
use windows::{
core::{w, HRESULT, PCWSTR},
Win32::{
- Foundation::{HANDLE, HINSTANCE, HMODULE, HWND, LPARAM, S_OK, WPARAM},
+ Foundation::{FreeLibrary, HANDLE, HINSTANCE, HMODULE, HWND, LPARAM, S_OK, WPARAM},
System::{
JobObjects::{
AssignProcessToJobObject, JobObjectExtendedLimitInformation,
SetInformationJobObject, JOBOBJECT_EXTENDED_LIMIT_INFORMATION,
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
},
LibraryLoader::{
- GetModuleFileNameA, GetModuleFileNameW, GetModuleHandleExW, LoadLibraryExW,
+ GetModuleFileNameA, GetModuleFileNameW, GetModuleHandleExW, GetProcAddress, LoadLibraryExW,
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS,
- LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR,
+ LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR, LOAD_LIBRARY_SEARCH_SYSTEM32,
},
},
UI::{
Controls::{
- TaskDialogIndirect, TASKDIALOGCONFIG, TASKDIALOGCONFIG_0, TASKDIALOGCONFIG_1,
+ TASKDIALOGCONFIG, TASKDIALOGCONFIG_0, TASKDIALOGCONFIG_1,
TASKDIALOG_NOTIFICATIONS, TDCBF_CLOSE_BUTTON, TDF_ENABLE_HYPERLINKS,
TDN_HYPERLINK_CLICKED, TD_ERROR_ICON,
},
WindowsAndMessaging::IDCLOSE,
},
@@ -487,11 +487,23 @@
pszFooter: PCWSTR::null(),
pfCallback: Some(task_dialog_callback),
lpCallbackData: 0,
cxWidth: 0,
};
- TaskDialogIndirect(&config, None, None, None).ok();
+ // A CUDA DLL may be loaded by an executable with no Common Controls
+ // v6 activation manifest. Do not make its diagnostic UI an eager DLL
+ // import: comctl32 v5 has no TaskDialogIndirect export.
+ if let Ok(controls) = LoadLibraryExW(w!("comctl32.dll"), None, LOAD_LIBRARY_SEARCH_SYSTEM32) {
+ if let Some(raw) = GetProcAddress(controls, windows::core::s!("TaskDialogIndirect")) {
+ let show: unsafe extern "system" fn(*const TASKDIALOGCONFIG, *mut i32, *mut i32, *mut i32) -> HRESULT =
+ mem::transmute(raw);
+ let _ = show(&config, ptr::null_mut(), ptr::null_mut(), ptr::null_mut());
+ } else {
+ eprintln!("ZLUDA failed to load {}; set HIP_PATH to the selected HIP SDK", redirect_names[0]);
+ }
+ let _ = FreeLibrary(controls);
+ }
}
result
}
unsafe extern "system" fn task_dialog_callback(
diff --git a/ptx/lib/zluda_ptx_impl.cpp b/ptx/lib/zluda_ptx_impl.cpp
--- a/ptx/lib/zluda_ptx_impl.cpp
+++ b/ptx/lib/zluda_ptx_impl.cpp
@@ -405,11 +405,26 @@
uint32_t line,
uint64_t function,
uint64_t char_size)
{
(void)char_size;
+#if defined(_WIN32) || defined(_WIN64)
+ // HIP on Windows only supplies the wide _wassert entry point. Keep the
+ // CUDA assertion's ASCII diagnostics and trap through public OCKL APIs.
+ auto output = __ockl_fprintf_stderr_begin();
+ const char* parts[] = {"CUDA assertion: ", (const char*)message, " at ", (const char*)file,
+ " in ", (const char*)function, "\n"};
+ for (const char* part : parts) {
+ const char* end = part;
+ while (*end) ++end;
+ output = __ockl_fprintf_append_string_n(output, part, end - part, 0);
+ }
+ __ockl_fprintf_append_args(output, 1, line, 0, 0, 0, 0, 0, 0, 1);
+ __builtin_trap();
+#else
[[clang::noinline]] __assert_fail((const char *)message, (const char *)file, line, (const char *)function);
+#endif
}
// * Smallest denormal is 1.4 × 10^-45
// * Smallest normal is ~1.175494351 × 10^(-38)
// * Now, 1.175494351×10^-38 / 1.4 × 10^-45 = 8396388 + 31/140
@@ -0,0 +1,124 @@
Implement the CUDA texture resource descriptor query required by NGX Evaluate.
Regression: tools/windows/cuda_image_probe.cpp in the d4r Windows port.
diff --git a/zluda/src/lib.rs b/zluda/src/lib.rs
--- a/zluda/src/lib.rs
+++ b/zluda/src/lib.rs
@@ -67,12 +67,14 @@
cuda_macros::cuda_function_declarations!(
unimplemented,
implemented
<= [
cuArray3DCreate_v2,
+ cuArray3DGetDescriptor_v2,
cuArrayCreate_v2,
cuArrayDestroy,
+ cuArrayGetDescriptor_v2,
cuCtxCreate_v2,
cuCtxCreate_v4,
cuCtxDestroy_v2,
cuCtxGetApiVersion,
cuCtxGetCurrent,
@@ -203,10 +205,11 @@
cuSurfObjectCreate,
cuSurfObjectDestroy,
cuSurfObjectGetResourceDesc,
cuTexObjectCreate,
cuTexObjectDestroy,
+ cuTexObjectGetResourceDesc,
cuTexRefSetAddress_v2,
cuTexRefSetAddressMode,
cuTexRefSetArray,
cuTexRefSetFilterMode,
cuTexRefSetFlags,
diff --git a/zluda/src/impl/tex.rs b/zluda/src/impl/tex.rs
--- a/zluda/src/impl/tex.rs
+++ b/zluda/src/impl/tex.rs
@@ -1,19 +1,58 @@
use crate::r#impl::hipfix;
use hip_runtime_sys::*;
+use std::collections::HashMap;
+use std::sync::{Mutex, OnceLock};
+
+fn object_resources() -> &'static Mutex<HashMap<usize, Vec<u8>>> {
+ static RESOURCES: OnceLock<Mutex<HashMap<usize, Vec<u8>>>> = OnceLock::new();
+ RESOURCES.get_or_init(|| Mutex::new(HashMap::new()))
+}
pub(crate) unsafe fn object_create(
p_tex_object: *mut hipTextureObject_t,
p_res_desc: *const HIP_RESOURCE_DESC,
p_tex_desc: *const HIP_TEXTURE_DESC,
p_res_view_desc: *const HIP_RESOURCE_VIEW_DESC,
) -> hipError_t {
- hipTexObjectCreate(p_tex_object, p_res_desc, p_tex_desc, p_res_view_desc)
+ let output = p_tex_object.as_mut().ok_or(hipErrorCode_t::InvalidValue)?;
+ let descriptor = p_res_desc.as_ref().ok_or(hipErrorCode_t::InvalidValue)?;
+ hipTexObjectCreate(output, descriptor, p_tex_desc, p_res_view_desc)?;
+ let bytes = std::slice::from_raw_parts(p_res_desc.cast::<u8>(), std::mem::size_of::<HIP_RESOURCE_DESC>()).to_vec();
+ match object_resources().lock() {
+ Ok(mut resources) => {
+ resources.insert(*output as usize, bytes);
+ Ok(())
+ }
+ Err(_) => {
+ let _ = hipDestroyTextureObject(*output);
+ *output = std::ptr::null_mut();
+ Err(hipErrorCode_t::Unknown)
+ }
+ }
}
pub(crate) unsafe fn object_destroy(tex_object: hipTextureObject_t) -> hipError_t {
- hipDestroyTextureObject(tex_object)
+ let mut resources = object_resources().lock().map_err(|_| hipErrorCode_t::Unknown)?;
+ if !resources.contains_key(&(tex_object as usize)) { return Err(hipErrorCode_t::InvalidValue); }
+ hipDestroyTextureObject(tex_object)?;
+ resources.remove(&(tex_object as usize));
+ Ok(())
+}
+
+pub(crate) unsafe fn object_get_resource_desc(
+ descriptor: &mut HIP_RESOURCE_DESC,
+ tex_object: hipTextureObject_t,
+) -> hipError_t {
+ // NGX probes surface handles as textures before trying the surface API.
+ // Passing that foreign handle to HIP dereferences invalid host metadata.
+ // Return a CUDA error for unknown handles and preserve the complete CUDA
+ // descriptor (HIP's query leaves the flags/reserved bytes unspecified).
+ let resources = object_resources().lock().map_err(|_| hipErrorCode_t::Unknown)?;
+ let bytes = resources.get(&(tex_object as usize)).ok_or(hipErrorCode_t::InvalidValue)?;
+ std::ptr::copy_nonoverlapping(bytes.as_ptr(), (descriptor as *mut HIP_RESOURCE_DESC).cast::<u8>(), bytes.len());
+ Ok(())
}
pub(crate) unsafe fn ref_set_array(
texref: *mut textureReference,
array: hipArray_t,
diff --git a/zluda/src/impl/array.rs b/zluda/src/impl/array.rs
--- a/zluda/src/impl/array.rs
+++ b/zluda/src/impl/array.rs
@@ -70,5 +70,23 @@
handle: *mut hipArray_t,
desc: *const HIP_ARRAY3D_DESCRIPTOR,
) -> hipError_t {
hipArray3DCreate(handle, desc)
}
+
+pub(crate) unsafe fn get_descriptor_v2(
+ output: &mut HIP_ARRAY_DESCRIPTOR,
+ array: hipArray_t,
+) -> hipError_t {
+ hipArrayGetDescriptor(output, array)?;
+ if let Some(format) = original_format(array)? {
+ output.Format = hipArray_Format(format);
+ }
+ Ok(())
+}
+
+pub(crate) unsafe fn _3d_get_descriptor_v2(
+ output: &mut HIP_ARRAY3D_DESCRIPTOR,
+ array: hipArray_t,
+) -> hipError_t {
+ hipArray3DGetDescriptor(output, array)
+}
+70
View File
@@ -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)"
}
+40
View File
@@ -0,0 +1,40 @@
[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
$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' }
Write-Host "Native Windows LLVM: $BuildRoot"
} finally { $env:PATH = $oldPath }
+76
View File
@@ -0,0 +1,76 @@
[CmdletBinding()]
param([string]$SourceRoot, [string]$LlvmBuildRoot, [string]$InstallRoot, [switch]$FetchOnly, [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-native' }
$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 ($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';
rust='1.98.1'; llvm=(& (Join-Path $LlvmBuildRoot 'bin/llvm-config.exe') --version);
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') }
}
+25
View File
@@ -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 }
+25
View File
@@ -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 }
+48
View File
@@ -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"
+29 -6
View File
@@ -7,6 +7,9 @@ param(
[string]$OutputDirectory,
[string]$NgxCore = $env:D4R_NGX_CORE,
[string]$DlssDll = $env:D4R_DLSS_DLL,
[ValidateSet('init', 'evaluate')][string]$NgxMode = 'init',
[ValidateSet(5, 11, 13)][int]$Preset = 11,
[switch]$NgxOnly,
[int]$Iterations = 32,
[int]$TimeoutSeconds = 180
)
@@ -28,8 +31,10 @@ $PackageRoot = [IO.Path]::GetFullPath($PackageRoot)
if (!$HipRoot) { $HipRoot = 'C:\Program Files\AMD\ROCm\7.2' }
$HipRoot = [IO.Path]::GetFullPath($HipRoot)
if (!$ZludaRoot) {
$builtZluda = Join-Path $repoRoot 'dist/zluda-windows-native'
$localZluda = [IO.Path]::GetFullPath((Join-Path $PackageRoot '../../.tools/zluda/zluda'))
if (Test-Path (Join-Path $localZluda 'nvcuda.dll')) { $ZludaRoot = $localZluda }
if (Test-Path (Join-Path $builtZluda 'build-info.json')) { $ZludaRoot = $builtZluda }
elseif (Test-Path (Join-Path $localZluda 'nvcuda.dll')) { $ZludaRoot = $localZluda }
else { throw 'Pass -ZludaRoot with the directory containing the Windows ZLUDA nvcuda.dll.' }
}
$ZludaRoot = [IO.Path]::GetFullPath($ZludaRoot)
@@ -84,6 +89,8 @@ function Invoke-Probe([string]$Name, [string]$Exe, [string[]]$Arguments) {
return $pass
}
try {
if ($NgxOnly -and (!$NgxCore -or !$DlssDll)) { throw '-NgxOnly requires both locally supplied NVIDIA DLL paths.' }
$summary.foundationDiagnosticsPerformed = !$NgxOnly
if ($Iterations -lt 1 -or $Iterations -gt 10000) { throw 'Iterations must be 1..10000' }
if ($TimeoutSeconds -lt 1) { throw 'TimeoutSeconds must be positive' }
$env:HIP_PATH = $HipRoot
@@ -122,9 +129,12 @@ try {
$summary.os = Get-CimInstance Win32_OperatingSystem | Select-Object Caption,Version,BuildNumber
} catch { $summary.inventoryError = $_.Exception.Message }
$bin = Join-Path $PackageRoot 'bin'
$hipOk = Invoke-Probe 'hip' (Join-Path $bin 'd4r_hip_gfx1201_probe.exe') @(
$hipOk = $true
if (!$NgxOnly) { $hipOk = Invoke-Probe 'hip' (Join-Path $bin 'd4r_hip_gfx1201_probe.exe') @(
'--hip-root', $HipRoot, '--module', (Join-Path $bin 'probe_gfx1201.hsaco'), '--iterations', "$Iterations")
}
if ($hipOk) {
if (!$NgxOnly) {
$wmmaOk = Invoke-Probe 'gfx12-wmma' (Join-Path $bin 'd4r_gfx12_wmma_probe.exe') @(
'--hip-root', $HipRoot, '--module', (Join-Path $bin 'wmma_gfx1201.hsaco'), '--iterations', "$Iterations")
if (!$wmmaOk) { throw 'gfx12 WMMA layout validation failed; refusing to mark K/M readiness.' }
@@ -141,7 +151,9 @@ try {
(Join-Path $PackageRoot 'k_layer_validate.py'), '--kernel-name', $layer, '--fixture-dir', $kFixture)
if (!$kReferenceOk) { throw "K $layer output differs from pwin_model.py; see the k-$layer-reference logs." }
}
}
$cudaOk = $true
if (!$NgxOnly) {
foreach ($context in @('primary', 'created')) {
$ok = Invoke-Probe "cuda-$context" (Join-Path $bin 'd4r_cuda_driver_probe.exe') @(
'--hip-root', $HipRoot, '--cuda-dll', (Join-Path $ZludaRoot 'nvcuda.dll'),
@@ -154,7 +166,14 @@ try {
'--context', $context, '--iterations', '1') | Out-Null
}
}
if ($cudaOk -and ($NgxMode -eq 'evaluate' -or (Test-Path (Join-Path $ZludaRoot 'build-info.json')))) {
$cudaOk = Invoke-Probe 'cuda-images' (Join-Path $bin 'd4r_cuda_image_probe.exe') @(
'--hip-root', $HipRoot, '--cuda-dll', (Join-Path $ZludaRoot 'nvcuda.dll'), '--iterations', "$Iterations")
}
}
if ($cudaOk) {
$mapOk = $interopOk = $true
if (!$NgxOnly) {
$mapOk = Invoke-Probe 'interop-map-lifetime' (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @(
'--hip-root', $HipRoot, '--interop-mode', 'map', '--iterations', '64')
$interopOk = Invoke-Probe 'interop-roundtrip' (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @(
@@ -167,6 +186,7 @@ try {
Invoke-Probe 'hip-stream-lifetime' (Join-Path $bin 'd4r_hip_stream_lifecycle_probe.exe') @(
'--hip-root', $HipRoot, '--module', (Join-Path $bin 'probe_gfx1201.hsaco'), '--iterations', '32') | Out-Null
}
}
if ($mapOk -and $interopOk) { $exitStatus = 0 }
if ($exitStatus -eq 0 -and $NgxCore) {
# The driver Init ABI searches the executable directory. Keep the
@@ -181,15 +201,18 @@ try {
Copy-Item -LiteralPath $DlssDll -Destination $localDlss
$ngxArguments = @('--hip-root', $HipRoot, '--cuda-dll', (Join-Path $ZludaRoot 'nvcuda.dll'),
'--ngx-core', $localCore, '--dlss-dll', $localDlss,
'--nvapi-dll', (Join-Path $PackageRoot 'nvapi-compat/nvapi64.dll'))
$ngxOk = Invoke-Probe 'ngx-init' $ngxExe $ngxArguments
'--nvapi-dll', (Join-Path $PackageRoot 'nvapi-compat/nvapi64.dll'),
'--ngx-mode', $NgxMode, '--preset', "$Preset", '--iterations', "$Iterations")
$ngxName = if ($NgxMode -eq 'evaluate') { "ngx-evaluate-preset-$Preset" } else { 'ngx-init' }
$ngxOk = Invoke-Probe $ngxName $ngxExe $ngxArguments
if (!$ngxOk) {
$exitStatus = 1
if (Test-Path (Join-Path $ZludaRoot 'trace/nvcuda.dll')) {
Invoke-Probe 'ngx-init-trace' $ngxExe @(
Invoke-Probe "$ngxName-trace" $ngxExe @(
'--hip-root', $HipRoot, '--cuda-dll', (Join-Path $ZludaRoot 'trace/nvcuda.dll'),
'--ngx-core', $localCore, '--dlss-dll', $localDlss,
'--nvapi-dll', (Join-Path $PackageRoot 'nvapi-compat/nvapi64.dll')) | Out-Null
'--nvapi-dll', (Join-Path $PackageRoot 'nvapi-compat/nvapi64.dll'),
'--ngx-mode', $NgxMode, '--preset', "$Preset", '--iterations', '1') | Out-Null
}
}
}
+4 -1
View File
@@ -7,6 +7,7 @@ using CUcontext = void*;
using CUmodule = void*;
using CUfunction = void*;
struct CudaApi {
d4r::diag::SearchDirectory search;
d4r::diag::Library library;
#define D4R_CU(name, ret, ...) using name##_fn = ret(WINAPI*)(__VA_ARGS__); name##_fn name = library.symbol<name##_fn>(#name)
D4R_CU(cuInit, int, unsigned);
@@ -33,7 +34,9 @@ struct CudaApi {
D4R_CU(cuGetErrorName, int, int, const char**);
D4R_CU(cuGetErrorString, int, int, const char**);
#undef D4R_CU
explicit CudaApi(const std::string& path) : library(d4r::diag::wide(path)) {}
explicit CudaApi(const std::string& path) :
search(std::filesystem::path(d4r::diag::wide(path)).parent_path()),
library(d4r::diag::wide(path)) {}
void check(int result, const char* call) {
const char* name = "unknown", *message = "unknown";
if (result) { cuGetErrorName(result, &name); cuGetErrorString(result, &message); }
+135
View File
@@ -0,0 +1,135 @@
#pragma once
#include "cuda_api.h"
namespace d4r::cuda {
// Public CUDA Driver API layouts, kept independent of NVIDIA SDK headers.
using Array = void*;
using Texture = uint64_t;
struct ArrayDescriptor { size_t width, height; uint32_t format, channels; };
struct Array3DDescriptor {
size_t width, height, depth;
uint32_t format, channels, flags;
};
struct Copy2D {
size_t srcX, srcY;
uint32_t srcType, srcAlignment;
const void* srcHost;
CUdeviceptr srcDevice;
Array srcArray;
size_t srcPitch, dstX, dstY;
uint32_t dstType, dstAlignment;
void* dstHost;
CUdeviceptr dstDevice;
Array dstArray;
size_t dstPitch, widthBytes, height;
};
struct ResourceDescriptor {
uint32_t type, alignment;
union { Array array; int reserved[32]; } resource;
uint32_t flags, reserved;
};
struct TextureDescriptor {
uint32_t addressMode[3], filterMode, flags, maxAnisotropy, mipmapFilterMode;
float mipmapBias, minMipmapClamp, maxMipmapClamp, borderColor[4];
int reserved[12];
};
static_assert(sizeof(Array3DDescriptor) == 40);
static_assert(sizeof(ArrayDescriptor) == 24);
static_assert(sizeof(Copy2D) == 128);
static_assert(sizeof(ResourceDescriptor) == 144);
static_assert(sizeof(TextureDescriptor) == 104);
struct ImageApi {
CudaApi& cuda;
#define D4R_IMAGE(name, ...) using name##_fn = int(WINAPI*)(__VA_ARGS__); name##_fn name = cuda.library.symbol<name##_fn>(#name)
D4R_IMAGE(cuArray3DCreate_v2, Array*, const Array3DDescriptor*);
D4R_IMAGE(cuArrayGetDescriptor_v2, ArrayDescriptor*, Array);
D4R_IMAGE(cuArray3DGetDescriptor_v2, Array3DDescriptor*, Array);
D4R_IMAGE(cuArrayDestroy, Array);
D4R_IMAGE(cuMemcpy2D_v2, const Copy2D*);
D4R_IMAGE(cuTexObjectCreate, Texture*, const ResourceDescriptor*, const TextureDescriptor*, const void*);
D4R_IMAGE(cuTexObjectDestroy, Texture);
D4R_IMAGE(cuTexObjectGetResourceDesc, ResourceDescriptor*, Texture);
D4R_IMAGE(cuSurfObjectCreate, Texture*, const ResourceDescriptor*);
D4R_IMAGE(cuSurfObjectDestroy, Texture);
D4R_IMAGE(cuSurfObjectGetResourceDesc, ResourceDescriptor*, Texture);
#undef D4R_IMAGE
explicit ImageApi(CudaApi& api) : cuda(api) {}
};
class Image {
ImageApi& api_;
void release() noexcept {
if (object) {
const int result = surface_ ? api_.cuSurfObjectDestroy(object) : api_.cuTexObjectDestroy(object);
if (result) std::fprintf(stderr, "IMAGE_DESTROY object result=%d\n", result);
object = 0;
}
if (array_) {
const int result = api_.cuArrayDestroy(array_);
if (result) std::fprintf(stderr, "IMAGE_DESTROY array result=%d\n", result);
array_ = nullptr;
}
}
Array array_ = nullptr;
bool surface_;
size_t rowBytes_, height_;
public:
Texture object = 0;
Image(ImageApi& api, unsigned width, unsigned height, uint32_t format,
unsigned channels, bool surface, unsigned filter = 0)
: api_(api), surface_(surface), rowBytes_(size_t(width) * channels * (format == 16 ? 2 : 4)), height_(height) {
try {
const Array3DDescriptor descriptor{width, height, 0, format, channels, surface ? 2u : 0u};
api_.cuda.check(api_.cuArray3DCreate_v2(&array_, &descriptor), "cuArray3DCreate_v2");
ResourceDescriptor resource{};
resource.resource.array = array_;
if (surface) api_.cuda.check(api_.cuSurfObjectCreate(&object, &resource), "cuSurfObjectCreate");
else {
TextureDescriptor texture{};
texture.addressMode[0] = texture.addressMode[1] = 1; // clamp
texture.filterMode = filter;
texture.flags = 2; // normalized coordinates
api_.cuda.check(api_.cuTexObjectCreate(&object, &resource, &texture, nullptr), "cuTexObjectCreate");
}
ResourceDescriptor roundtrip{};
api_.cuda.check(surface ? api_.cuSurfObjectGetResourceDesc(&roundtrip, object) :
api_.cuTexObjectGetResourceDesc(&roundtrip, object), "CUDA object resource descriptor roundtrip");
if (roundtrip.type != 0 || roundtrip.resource.array != array_ || roundtrip.flags != 0)
throw std::runtime_error("CUDA object descriptor does not match its array");
ArrayDescriptor twoD{};
Array3DDescriptor threeD{};
api_.cuda.check(api_.cuArrayGetDescriptor_v2(&twoD, array_), "cuArrayGetDescriptor_v2");
api_.cuda.check(api_.cuArray3DGetDescriptor_v2(&threeD, array_), "cuArray3DGetDescriptor_v2");
if (twoD.width != width || twoD.height != height || twoD.format != format || twoD.channels != channels ||
threeD.width != width || threeD.height != height || threeD.depth != 0 || threeD.format != format ||
threeD.channels != channels || threeD.flags != descriptor.flags)
throw std::runtime_error("CUDA array descriptor geometry or format mismatch");
} catch (...) { release(); throw; }
}
Image(const Image&) = delete;
Image& operator=(const Image&) = delete;
~Image() { release(); }
void verify_wrong_object_kind() {
ResourceDescriptor wrong{};
const int result = surface_ ? api_.cuTexObjectGetResourceDesc(&wrong, object) :
api_.cuSurfObjectGetResourceDesc(&wrong, object);
if (!result) throw std::runtime_error("Wrong CUDA object kind was accepted as a valid resource");
std::printf("CUDA wrong-kind descriptor safely rejected result=%d\n", result);
}
void upload(const void* source) {
Copy2D copy{};
copy.srcType = 1; copy.srcHost = source; copy.srcPitch = rowBytes_;
copy.dstType = 3; copy.dstArray = array_;
copy.widthBytes = rowBytes_; copy.height = height_;
api_.cuda.check(api_.cuMemcpy2D_v2(&copy), "cuMemcpy2D_v2(host to diagnostic array)");
}
void download(void* destination) {
Copy2D copy{};
copy.srcType = 3; copy.srcArray = array_;
copy.dstType = 1; copy.dstHost = destination; copy.dstPitch = rowBytes_;
copy.widthBytes = rowBytes_; copy.height = height_;
api_.cuda.check(api_.cuMemcpy2D_v2(&copy), "cuMemcpy2D_v2(diagnostic array to host)");
}
};
}
+51
View File
@@ -0,0 +1,51 @@
#include "hip_api.h"
#include "cuda_image_api.h"
int main(int argc, char** argv) {
using namespace d4r::diag;
start();
try {
Args args(argc, argv);
HipApi hip(args.hip_root);
hipDeviceProp_t props{};
hip.select_gfx1201(args.device, props);
SearchDirectory search(std::filesystem::path(wide(args.cuda_dll)).parent_path());
CudaApi cuda(args.cuda_dll);
cuda.check(cuda.cuInit(0), "cuInit");
CUdevice device = -1;
int count = 0;
cuda.check(cuda.cuDeviceGetCount(&count), "cuDeviceGetCount");
for (int ordinal = 0; ordinal < count; ++ordinal) {
CUdevice candidate = -1;
int bus = -1, pciDevice = -1, domain = -1;
cuda.check(cuda.cuDeviceGet(&candidate, ordinal), "cuDeviceGet");
cuda.check(cuda.cuDeviceGetAttribute(&bus, 33, candidate), "cuDeviceGetAttribute(PCI_BUS_ID)");
cuda.check(cuda.cuDeviceGetAttribute(&pciDevice, 34, candidate), "cuDeviceGetAttribute(PCI_DEVICE_ID)");
cuda.check(cuda.cuDeviceGetAttribute(&domain, 50, candidate), "cuDeviceGetAttribute(PCI_DOMAIN_ID)");
if (bus == props.pciBusID && pciDevice == props.pciDeviceID && domain == props.pciDomainID) device = candidate;
}
if (device < 0) throw std::runtime_error("CUDA image probe HIP/CUDA PCI identity mismatch");
CUcontext context = nullptr;
cuda.check(cuda.cuCtxCreate_v2(&context, 0, device), "cuCtxCreate_v2");
struct Cleanup { CudaApi& api; CUcontext context; ~Cleanup() { (void)api.cuCtxDestroy_v2(context); } } cleanup{cuda, context};
d4r::cuda::ImageApi images(cuda);
for (unsigned iteration = 0; iteration < args.iterations; ++iteration) {
for (unsigned channels : {1u, 2u, 4u}) for (bool surface : {false, true}) {
d4r::cuda::Image image(images, 17, 9, 16, channels, surface);
image.verify_wrong_object_kind();
std::vector<uint16_t> input(17 * 9 * channels), output(input.size());
for (size_t i = 0; i < input.size(); ++i) input[i] = uint16_t((i + iteration) % 8 * 0x100);
image.upload(input.data());
image.download(output.data());
if (input != output) throw std::runtime_error("CUDA array storage roundtrip mismatch");
}
}
cuda.check(cuda.cuCtxSynchronize(), "cuCtxSynchronize");
std::printf("PASS CUDA_IMAGES architecture=gfx1201 iterations=%u descriptor_and_storage=1\n", args.iterations);
return 0;
} catch (const std::exception& e) {
std::fprintf(stderr, "FAIL CUDA_IMAGES %s\n", e.what());
loaded_modules();
return 4;
}
}
+33
View File
@@ -57,6 +57,18 @@ inline LONG WINAPI unhandled(EXCEPTION_POINTERS* info)
{
std::fprintf(stderr, "EXCEPTION code=0x%08lx address=%p\n",
info->ExceptionRecord->ExceptionCode, info->ExceptionRecord->ExceptionAddress);
if (info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION &&
info->ExceptionRecord->NumberParameters >= 2)
std::fprintf(stderr, "EXCEPTION_ACCESS operation=%llu target=0x%llx\n",
static_cast<unsigned long long>(info->ExceptionRecord->ExceptionInformation[0]),
static_cast<unsigned long long>(info->ExceptionRecord->ExceptionInformation[1]));
std::fprintf(stderr, "CONTEXT rip=0x%llx rsp=0x%llx rcx=0x%llx rdx=0x%llx r8=0x%llx r9=0x%llx\n",
static_cast<unsigned long long>(info->ContextRecord->Rip),
static_cast<unsigned long long>(info->ContextRecord->Rsp),
static_cast<unsigned long long>(info->ContextRecord->Rcx),
static_cast<unsigned long long>(info->ContextRecord->Rdx),
static_cast<unsigned long long>(info->ContextRecord->R8),
static_cast<unsigned long long>(info->ContextRecord->R9));
loaded_modules();
wchar_t directory[32768]{};
const DWORD n = GetEnvironmentVariableW(L"D4R_DIAG_DIR", directory, 32768);
@@ -74,12 +86,24 @@ inline LONG WINAPI unhandled(EXCEPTION_POINTERS* info)
}
return EXCEPTION_EXECUTE_HANDLER;
}
inline LONG WINAPI first_chance(EXCEPTION_POINTERS* info)
{
// NGX installs its own final exception filter. Capture a diagnostic dump
// before that filter can replace ours; do not swallow or retry the fault.
const auto code = info->ExceptionRecord->ExceptionCode;
if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_ILLEGAL_INSTRUCTION) {
static LONG captured = 0;
if (InterlockedCompareExchange(&captured, 1, 0) == 0) (void)unhandled(info);
}
return EXCEPTION_CONTINUE_SEARCH;
}
inline void start()
{
std::setvbuf(stdout, nullptr, _IONBF, 0);
std::setvbuf(stderr, nullptr, _IONBF, 0);
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
SetUnhandledExceptionFilter(unhandled);
AddVectoredExceptionHandler(1, first_chance);
std::printf("d4r native Windows diagnostic x64 pid=%lu\n", GetCurrentProcessId());
}
struct Args {
@@ -89,6 +113,8 @@ struct Args {
std::string ngx_core, dlss_dll;
std::string nvapi_dll;
std::string ngx_abi = "driver";
std::string ngx_mode = "init";
unsigned preset = 11;
std::string fixture_dir;
std::string kernel_name = "enc1";
unsigned iterations = 32;
@@ -108,6 +134,13 @@ struct Args {
else if (key == "--dlss-dll") dlss_dll = value;
else if (key == "--nvapi-dll") nvapi_dll = value;
else if (key == "--ngx-abi") ngx_abi = value;
else if (key == "--ngx-mode") ngx_mode = value;
else if (key == "--preset") {
size_t end = 0;
preset = static_cast<unsigned>(std::stoul(value, &end));
if (end != value.size() || (preset != 5 && preset != 11 && preset != 13))
throw std::runtime_error("preset must be 5 (E), 11 (K) or 13 (M)");
}
else if (key == "--fixture-dir") fixture_dir = value;
else if (key == "--kernel-name") kernel_name = value;
else if (key == "--iterations") {
+147
View File
@@ -0,0 +1,147 @@
#pragma once
#include "cuda_image_api.h"
#include <cmath>
#include <chrono>
#include <fstream>
extern "C" void d4r_ngx_set_uint(void*, const char*, unsigned);
extern "C" void d4r_ngx_set_int(void*, const char*, int);
extern "C" void d4r_ngx_set_float(void*, const char*, float);
extern "C" void d4r_ngx_set_ull(void*, const char*, unsigned long long);
extern "C" void d4r_ngx_set_void(void*, const char*, void*);
namespace d4r::diag {
inline float half_value(uint16_t bits) {
const unsigned exponent = (bits >> 10) & 31, mantissa = bits & 1023;
const float sign = bits & 0x8000 ? -1.0f : 1.0f;
if (exponent == 31) return mantissa ? NAN : sign * INFINITY;
if (!exponent) return sign * std::ldexp(float(mantissa), -24);
return sign * std::ldexp(float(1024 + mantissa), int(exponent) - 25);
}
inline void save_output(const std::filesystem::path& directory, const std::vector<uint16_t>& output,
unsigned width, unsigned height, unsigned frame) {
const auto stem = "output-" + std::to_string(frame);
std::ofstream raw(directory / (stem + ".rgba16f"), std::ios::binary);
raw.write(reinterpret_cast<const char*>(output.data()), std::streamsize(output.size() * 2));
// BMP is only a diagnostic preview; the raw file preserves unmodified HDR values.
const unsigned stride = (width * 3 + 3) & ~3u;
std::vector<unsigned char> bytes(54 + size_t(stride) * height, 0);
auto u32 = [&](size_t offset, uint32_t value) {
for (int j = 0; j < 4; ++j) bytes[offset + j] = static_cast<unsigned char>(value >> (8 * j));
};
bytes[0] = 'B'; bytes[1] = 'M'; u32(2, static_cast<uint32_t>(bytes.size())); u32(10, 54);
u32(14, 40); u32(18, width); u32(22, height); bytes[26] = 1; bytes[28] = 24;
for (unsigned y = 0; y < height; ++y) for (unsigned x = 0; x < width; ++x)
for (unsigned c = 0; c < 3; ++c) {
const float value = half_value(output[(size_t(y) * width + x) * 4 + c]);
bytes[54 + size_t(height - y - 1) * stride + x * 3 + (2 - c)] =
static_cast<unsigned char>(std::clamp(value, 0.0f, 1.0f) * 255.0f + 0.5f);
}
std::ofstream bmp(directory / (stem + ".bmp"), std::ios::binary);
bmp.write(reinterpret_cast<const char*>(bytes.data()), std::streamsize(bytes.size()));
if (!raw || !bmp) throw std::runtime_error("Saving DLSS output failed");
}
inline void run_synthetic_dlss(CudaApi& cuda, Library& core, void* parameters,
unsigned preset, unsigned frames, const std::filesystem::path& directory) {
// CPU upload/readback is confined to this synthetic diagnostic. The game
// backend uses external D3D12 memory and GPU copies instead.
constexpr unsigned width = 256, height = 144, outWidth = 512, outHeight = 288;
using Create = unsigned(*)(unsigned, void*, void**);
using Evaluate = unsigned(*)(void*, void*, void*);
using Release = unsigned(*)(void*);
const auto create = core.symbol<Create>("NVSDK_NGX_CUDA_CreateFeature");
const auto evaluate = core.symbol<Evaluate>("NVSDK_NGX_CUDA_EvaluateFeature");
const auto release = core.symbol<Release>("NVSDK_NGX_CUDA_ReleaseFeature");
auto ngx_check = [](unsigned result, const char* operation) {
std::printf("NGX %s -> 0x%08x\n", operation, result);
if (result != 1) throw std::runtime_error(std::string(operation) + " result=" + std::to_string(result));
};
d4r_ngx_set_uint(parameters, "Width", width);
d4r_ngx_set_uint(parameters, "Height", height);
d4r_ngx_set_uint(parameters, "OutWidth", outWidth);
d4r_ngx_set_uint(parameters, "OutHeight", outHeight);
d4r_ngx_set_int(parameters, "PerfQualityValue", 2);
d4r_ngx_set_int(parameters, "DLSS.Feature.Create.Flags", 0);
d4r_ngx_set_int(parameters, "DLSS.Enable.Output.Subrects", 0);
d4r_ngx_set_uint(parameters, "CreationNodeMask", 1);
d4r_ngx_set_uint(parameters, "VisibilityNodeMask", 1);
for (const char* name : {"DLSS.Hint.Render.Preset.DLAA", "DLSS.Hint.Render.Preset.Quality",
"DLSS.Hint.Render.Preset.Balanced", "DLSS.Hint.Render.Preset.Performance",
"DLSS.Hint.Render.Preset.UltraPerformance", "DLSS.Hint.Render.Preset.UltraQuality"})
d4r_ngx_set_uint(parameters, name, preset);
CUdeviceptr scratch = 0;
cuda.check(cuda.cuMemAlloc_v2(&scratch, 64ull * 1024 * 1024), "cuMemAlloc_v2(NGX scratch)");
struct ScratchCleanup { CudaApi& api; CUdeviceptr pointer; ~ScratchCleanup() { (void)api.cuMemFree_v2(pointer); } } sc{cuda, scratch};
d4r_ngx_set_void(parameters, "Scratch", reinterpret_cast<void*>(uintptr_t(scratch)));
d4r_ngx_set_ull(parameters, "Scratch.SizeInBytes", 64ull * 1024 * 1024);
void* handle = nullptr;
std::printf("STAGE NGX_CREATE preset=%u dimensions=%ux%u->%ux%u\n", preset, width, height, outWidth, outHeight);
ngx_check(create(1, parameters, &handle), "NVSDK_NGX_CUDA_CreateFeature");
if (!handle) throw std::runtime_error("NGX CreateFeature returned a null handle");
struct FeatureCleanup { Release release; void*& handle; ~FeatureCleanup() { if (handle) (void)release(handle); } } fc{release, handle};
cuda::ImageApi images(cuda);
cuda::Image color(images, width, height, 16, 4, false, 1);
cuda::Image depth(images, width, height, 32, 1, false);
cuda::Image motion(images, width, height, 16, 2, false);
cuda::Image exposure(images, 1, 1, 32, 1, false);
cuda::Image output(images, outWidth, outHeight, 16, 4, true);
std::vector<uint16_t> input(size_t(width) * height * 4), zeroMotion(size_t(width) * height * 2, 0);
std::vector<float> inputDepth(size_t(width) * height, 0.5f);
for (unsigned y = 0; y < height; ++y) for (unsigned x = 0; x < width; ++x) {
const size_t index = (size_t(y) * width + x) * 4;
input[index] = ((x / 16 + y / 16) & 1) ? 0x3a00 : 0x3400; // .75 / .25
input[index + 1] = (x & 32) ? 0x3800 : 0x3400;
input[index + 2] = (y & 32) ? 0x3a00 : 0x3800;
input[index + 3] = 0x3c00;
}
const float one = 1.0f;
color.upload(input.data()); depth.upload(inputDepth.data()); motion.upload(zeroMotion.data()); exposure.upload(&one);
d4r_ngx_set_void(parameters, "Color", &color.object);
d4r_ngx_set_void(parameters, "Depth", &depth.object);
d4r_ngx_set_void(parameters, "MotionVectors", &motion.object);
d4r_ngx_set_void(parameters, "ExposureTexture", &exposure.object);
d4r_ngx_set_void(parameters, "Output", &output.object);
for (const char* name : {"TransparencyMask", "DLSS.Input.Bias.Current.Color.Mask"})
d4r_ngx_set_void(parameters, name, nullptr);
for (const char* name : {"Jitter.Offset.X", "Jitter.Offset.Y", "Sharpness"})
d4r_ngx_set_float(parameters, name, 0.0f);
for (const char* name : {"MV.Scale.X", "MV.Scale.Y", "DLSS.Pre.Exposure", "DLSS.Exposure.Scale"})
d4r_ngx_set_float(parameters, name, 1.0f);
d4r_ngx_set_float(parameters, "FrameTimeDeltaInMsec", 16.666667f);
d4r_ngx_set_int(parameters, "Disable.Watermark", 1);
d4r_ngx_set_uint(parameters, "DLSS.Render.Subrect.Dimensions.Width", width);
d4r_ngx_set_uint(parameters, "DLSS.Render.Subrect.Dimensions.Height", height);
for (const char* name : {"DLSS.Input.Color.Subrect.Base.X", "DLSS.Input.Color.Subrect.Base.Y",
"DLSS.Input.Depth.Subrect.Base.X", "DLSS.Input.Depth.Subrect.Base.Y", "DLSS.Input.MV.Subrect.Base.X",
"DLSS.Input.MV.Subrect.Base.Y", "DLSS.Output.Subrect.Base.X", "DLSS.Output.Subrect.Base.Y"})
d4r_ngx_set_uint(parameters, name, 0);
std::vector<uint16_t> pixels(size_t(outWidth) * outHeight * 4, 0x7e00);
for (unsigned frame = 0; frame < frames; ++frame) {
std::fill(pixels.begin(), pixels.end(), 0x7e00); // unwritten output is an explicit failure
output.upload(pixels.data());
d4r_ngx_set_int(parameters, "Reset", frame == 0 ? 1 : 0);
std::printf("STAGE NGX_EVALUATE preset=%u frame=%u\n", preset, frame);
const auto begin = std::chrono::steady_clock::now();
ngx_check(evaluate(handle, parameters, nullptr), "NVSDK_NGX_CUDA_EvaluateFeature");
cuda.check(cuda.cuCtxSynchronize(), "cuCtxSynchronize(NGX Evaluate)");
const double milliseconds = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - begin).count();
output.download(pixels.data());
size_t nonfinite = 0; double sum = 0, square = 0;
for (size_t i = 0; i < pixels.size(); ++i) {
const float value = half_value(pixels[i]);
if (!std::isfinite(value)) ++nonfinite;
if ((i & 3) < 3) { sum += value; square += double(value) * value; }
}
const double count = outWidth * outHeight * 3;
const double variance = square / count - (sum / count) * (sum / count);
std::printf("NGX_OUTPUT preset=%u frame=%u nonfinite=%zu mean=%.9g variance=%.9g evaluate_ms=%.3f\n",
preset, frame, nonfinite, sum / count, variance, milliseconds);
if (nonfinite || !std::isfinite(variance) || variance < 1e-6)
throw std::runtime_error("DLSS output has NaN/Inf, unwritten pixels or no synthetic pattern");
save_output(directory, pixels, outWidth, outHeight, frame);
}
ngx_check(release(handle), "NVSDK_NGX_CUDA_ReleaseFeature"); handle = nullptr;
std::printf("PASS NGX_EVALUATE preset=%u frames=%u finite=1 network_validation=pending\n", preset, frames);
}
}
+4
View File
@@ -1,5 +1,6 @@
#include "hip_api.h"
#include "cuda_api.h"
#include "ngx_cuda_evaluate.h"
// Same NGX C ABI and freestanding MSVC parameter accessors as the upstream shim.
// NVIDIA SDK headers and proprietary binaries are not build dependencies.
@@ -110,6 +111,9 @@ int main(int argc, char** argv)
unsigned roundtrip = 0;
check(d4r_ngx_get_uint(parameters, "Width", &roundtrip), "MSVC parameter ABI roundtrip");
if (roundtrip != 640) throw std::runtime_error("NGX parameter ABI mismatch");
if (args.ngx_mode == "evaluate")
run_synthetic_dlss(cuda, core, parameters, args.preset, args.iterations, data_path);
else if (args.ngx_mode != "init") throw std::runtime_error("--ngx-mode must be init or evaluate");
check(destroy(parameters), "DestroyParameters(allocated)"); parameters = nullptr;
check(destroy(caps), "DestroyParameters(capabilities)"); caps = nullptr;
loaded_modules();