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()
endforeach() endforeach()
add_custom_target(d4r_native_gfx1201_compile ALL DEPENDS ${native_objects}) 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) add_executable(d4r_${probe} tools/windows/${probe}.cpp)
target_compile_definitions(d4r_${probe} PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__) target_compile_definitions(d4r_${probe} PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__)
target_include_directories(d4r_${probe} PRIVATE "${D4R_HIP_ROOT}/include") 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) --context ${context} --iterations 32)
set_tests_properties(cuda_${context} PROPERTIES TIMEOUT 180 LABELS "hardware;zluda") set_tests_properties(cuda_${context} PROPERTIES TIMEOUT 180 LABELS "hardware;zluda")
endforeach() 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() 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 ## Milestone and gates
Current milestone: M1/M2/M4/M5 passed; NGX init + DLSS CUDA capability discovery Current milestone: M1/M2/M4/M5 passed; native NGX Create/Evaluate on the simple
passed with the user's DLLs. M6 is in progress: K `enc1` and `enc2` prep and 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. 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. 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 | | M0: branch, audit, Windows build and diagnostics | Initial implementation complete |
| M1: native HIP gfx1201 allocation, kernel, CPU verification | PASS, 32 iterations + guard verification | | 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 | | 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 | | 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 | | 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 | | 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. unvalidated until K and its full DLSS integration are correct.
* New ZLUDA patches `0006`/`0007` add k8 WMMA, prep slots, wave64 for MMA-free * 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 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. now built into native Windows ZLUDA. Integer PTX and resource descriptor
Our current integer PTX test uses the unpatched `v7-preview.11` binary. tests pass on hardware; complete K/M lowering remains to be validated.
* Upstream restored `kernels/tools/model_enc3.py`, resolving the missing M * Upstream restored `kernels/tools/model_enc3.py`, resolving the missing M
reference dependency found in the initial audit. New check/replay utilities reference dependency found in the initial audit. New check/replay utilities
cover K/M and texture tails. Texture artifacts still require local DLL/PTX 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 with explicit D3D12 queue submission ownership. Full K precedes M and native
FP8/performance work. No complete DLSS or OptiScaler game result is claimed. 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 ## Architecture and platform boundaries
Existing path: 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]$OutputDirectory,
[string]$NgxCore = $env:D4R_NGX_CORE, [string]$NgxCore = $env:D4R_NGX_CORE,
[string]$DlssDll = $env:D4R_DLSS_DLL, [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]$Iterations = 32,
[int]$TimeoutSeconds = 180 [int]$TimeoutSeconds = 180
) )
@@ -28,8 +31,10 @@ $PackageRoot = [IO.Path]::GetFullPath($PackageRoot)
if (!$HipRoot) { $HipRoot = 'C:\Program Files\AMD\ROCm\7.2' } if (!$HipRoot) { $HipRoot = 'C:\Program Files\AMD\ROCm\7.2' }
$HipRoot = [IO.Path]::GetFullPath($HipRoot) $HipRoot = [IO.Path]::GetFullPath($HipRoot)
if (!$ZludaRoot) { if (!$ZludaRoot) {
$builtZluda = Join-Path $repoRoot 'dist/zluda-windows-native'
$localZluda = [IO.Path]::GetFullPath((Join-Path $PackageRoot '../../.tools/zluda/zluda')) $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.' } else { throw 'Pass -ZludaRoot with the directory containing the Windows ZLUDA nvcuda.dll.' }
} }
$ZludaRoot = [IO.Path]::GetFullPath($ZludaRoot) $ZludaRoot = [IO.Path]::GetFullPath($ZludaRoot)
@@ -84,6 +89,8 @@ function Invoke-Probe([string]$Name, [string]$Exe, [string[]]$Arguments) {
return $pass return $pass
} }
try { 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 ($Iterations -lt 1 -or $Iterations -gt 10000) { throw 'Iterations must be 1..10000' }
if ($TimeoutSeconds -lt 1) { throw 'TimeoutSeconds must be positive' } if ($TimeoutSeconds -lt 1) { throw 'TimeoutSeconds must be positive' }
$env:HIP_PATH = $HipRoot $env:HIP_PATH = $HipRoot
@@ -122,9 +129,12 @@ try {
$summary.os = Get-CimInstance Win32_OperatingSystem | Select-Object Caption,Version,BuildNumber $summary.os = Get-CimInstance Win32_OperatingSystem | Select-Object Caption,Version,BuildNumber
} catch { $summary.inventoryError = $_.Exception.Message } } catch { $summary.inventoryError = $_.Exception.Message }
$bin = Join-Path $PackageRoot 'bin' $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") '--hip-root', $HipRoot, '--module', (Join-Path $bin 'probe_gfx1201.hsaco'), '--iterations', "$Iterations")
}
if ($hipOk) { if ($hipOk) {
if (!$NgxOnly) {
$wmmaOk = Invoke-Probe 'gfx12-wmma' (Join-Path $bin 'd4r_gfx12_wmma_probe.exe') @( $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") '--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.' } 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) (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." } if (!$kReferenceOk) { throw "K $layer output differs from pwin_model.py; see the k-$layer-reference logs." }
} }
}
$cudaOk = $true $cudaOk = $true
if (!$NgxOnly) {
foreach ($context in @('primary', 'created')) { foreach ($context in @('primary', 'created')) {
$ok = Invoke-Probe "cuda-$context" (Join-Path $bin 'd4r_cuda_driver_probe.exe') @( $ok = Invoke-Probe "cuda-$context" (Join-Path $bin 'd4r_cuda_driver_probe.exe') @(
'--hip-root', $HipRoot, '--cuda-dll', (Join-Path $ZludaRoot 'nvcuda.dll'), '--hip-root', $HipRoot, '--cuda-dll', (Join-Path $ZludaRoot 'nvcuda.dll'),
@@ -154,7 +166,14 @@ try {
'--context', $context, '--iterations', '1') | Out-Null '--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) { if ($cudaOk) {
$mapOk = $interopOk = $true
if (!$NgxOnly) {
$mapOk = Invoke-Probe 'interop-map-lifetime' (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @( $mapOk = Invoke-Probe 'interop-map-lifetime' (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @(
'--hip-root', $HipRoot, '--interop-mode', 'map', '--iterations', '64') '--hip-root', $HipRoot, '--interop-mode', 'map', '--iterations', '64')
$interopOk = Invoke-Probe 'interop-roundtrip' (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @( $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') @( 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 '--hip-root', $HipRoot, '--module', (Join-Path $bin 'probe_gfx1201.hsaco'), '--iterations', '32') | Out-Null
} }
}
if ($mapOk -and $interopOk) { $exitStatus = 0 } if ($mapOk -and $interopOk) { $exitStatus = 0 }
if ($exitStatus -eq 0 -and $NgxCore) { if ($exitStatus -eq 0 -and $NgxCore) {
# The driver Init ABI searches the executable directory. Keep the # The driver Init ABI searches the executable directory. Keep the
@@ -181,15 +201,18 @@ try {
Copy-Item -LiteralPath $DlssDll -Destination $localDlss Copy-Item -LiteralPath $DlssDll -Destination $localDlss
$ngxArguments = @('--hip-root', $HipRoot, '--cuda-dll', (Join-Path $ZludaRoot 'nvcuda.dll'), $ngxArguments = @('--hip-root', $HipRoot, '--cuda-dll', (Join-Path $ZludaRoot 'nvcuda.dll'),
'--ngx-core', $localCore, '--dlss-dll', $localDlss, '--ngx-core', $localCore, '--dlss-dll', $localDlss,
'--nvapi-dll', (Join-Path $PackageRoot 'nvapi-compat/nvapi64.dll')) '--nvapi-dll', (Join-Path $PackageRoot 'nvapi-compat/nvapi64.dll'),
$ngxOk = Invoke-Probe 'ngx-init' $ngxExe $ngxArguments '--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) { if (!$ngxOk) {
$exitStatus = 1 $exitStatus = 1
if (Test-Path (Join-Path $ZludaRoot 'trace/nvcuda.dll')) { 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'), '--hip-root', $HipRoot, '--cuda-dll', (Join-Path $ZludaRoot 'trace/nvcuda.dll'),
'--ngx-core', $localCore, '--dlss-dll', $localDlss, '--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 CUmodule = void*;
using CUfunction = void*; using CUfunction = void*;
struct CudaApi { struct CudaApi {
d4r::diag::SearchDirectory search;
d4r::diag::Library library; d4r::diag::Library library;
#define D4R_CU(name, ret, ...) using name##_fn = ret(WINAPI*)(__VA_ARGS__); name##_fn name = library.symbol<name##_fn>(#name) #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); D4R_CU(cuInit, int, unsigned);
@@ -33,7 +34,9 @@ struct CudaApi {
D4R_CU(cuGetErrorName, int, int, const char**); D4R_CU(cuGetErrorName, int, int, const char**);
D4R_CU(cuGetErrorString, int, int, const char**); D4R_CU(cuGetErrorString, int, int, const char**);
#undef D4R_CU #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) { void check(int result, const char* call) {
const char* name = "unknown", *message = "unknown"; const char* name = "unknown", *message = "unknown";
if (result) { cuGetErrorName(result, &name); cuGetErrorString(result, &message); } 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", std::fprintf(stderr, "EXCEPTION code=0x%08lx address=%p\n",
info->ExceptionRecord->ExceptionCode, info->ExceptionRecord->ExceptionAddress); 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(); loaded_modules();
wchar_t directory[32768]{}; wchar_t directory[32768]{};
const DWORD n = GetEnvironmentVariableW(L"D4R_DIAG_DIR", 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; 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() inline void start()
{ {
std::setvbuf(stdout, nullptr, _IONBF, 0); std::setvbuf(stdout, nullptr, _IONBF, 0);
std::setvbuf(stderr, nullptr, _IONBF, 0); std::setvbuf(stderr, nullptr, _IONBF, 0);
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
SetUnhandledExceptionFilter(unhandled); SetUnhandledExceptionFilter(unhandled);
AddVectoredExceptionHandler(1, first_chance);
std::printf("d4r native Windows diagnostic x64 pid=%lu\n", GetCurrentProcessId()); std::printf("d4r native Windows diagnostic x64 pid=%lu\n", GetCurrentProcessId());
} }
struct Args { struct Args {
@@ -89,6 +113,8 @@ struct Args {
std::string ngx_core, dlss_dll; std::string ngx_core, dlss_dll;
std::string nvapi_dll; std::string nvapi_dll;
std::string ngx_abi = "driver"; std::string ngx_abi = "driver";
std::string ngx_mode = "init";
unsigned preset = 11;
std::string fixture_dir; std::string fixture_dir;
std::string kernel_name = "enc1"; std::string kernel_name = "enc1";
unsigned iterations = 32; unsigned iterations = 32;
@@ -108,6 +134,13 @@ struct Args {
else if (key == "--dlss-dll") dlss_dll = value; else if (key == "--dlss-dll") dlss_dll = value;
else if (key == "--nvapi-dll") nvapi_dll = value; else if (key == "--nvapi-dll") nvapi_dll = value;
else if (key == "--ngx-abi") ngx_abi = 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 == "--fixture-dir") fixture_dir = value;
else if (key == "--kernel-name") kernel_name = value; else if (key == "--kernel-name") kernel_name = value;
else if (key == "--iterations") { 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 "hip_api.h"
#include "cuda_api.h" #include "cuda_api.h"
#include "ngx_cuda_evaluate.h"
// Same NGX C ABI and freestanding MSVC parameter accessors as the upstream shim. // Same NGX C ABI and freestanding MSVC parameter accessors as the upstream shim.
// NVIDIA SDK headers and proprietary binaries are not build dependencies. // NVIDIA SDK headers and proprietary binaries are not build dependencies.
@@ -110,6 +111,9 @@ int main(int argc, char** argv)
unsigned roundtrip = 0; unsigned roundtrip = 0;
check(d4r_ngx_get_uint(parameters, "Width", &roundtrip), "MSVC parameter ABI roundtrip"); check(d4r_ngx_get_uint(parameters, "Width", &roundtrip), "MSVC parameter ABI roundtrip");
if (roundtrip != 640) throw std::runtime_error("NGX parameter ABI mismatch"); 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(parameters), "DestroyParameters(allocated)"); parameters = nullptr;
check(destroy(caps), "DestroyParameters(capabilities)"); caps = nullptr; check(destroy(caps), "DestroyParameters(capabilities)"); caps = nullptr;
loaded_modules(); loaded_modules();