From 98de8e3a3edcbf31e23aaf127abe072a25fc1523 Mon Sep 17 00:00:00 2001 From: xdfnx-dev Date: Fri, 2 Oct 2026 16:17:49 +0300 Subject: [PATCH] Build native Windows packages for both RDNA4 targets Add explicit gfx1200/gfx1201 builds, runtime selection and per-target code objects. Guard staging, packaging and installation against mixed architectures; retain gfx1201 compatibility and strict K/M arithmetic. Adapt realdody's target configuration and MSVC binary16/BOOL portability fixes from ae530b0. Do not import its forced gfx12 layout on RDNA3. Add exhaustive binary16 and C++/PowerShell target contract regressions. Validation: 20 CTest gates on RX 9070 XT; four software-only gfx1200 gates; MSVC portability compile checks; exact 4K K async and M control outputs; isolated package install/restore and mixed-target rejection tests. No physical gfx1200 validation is claimed. Co-authored-by: realdody --- CMakeLists.txt | 67 ++++++--- docs/windows-game.md | 8 ++ docs/windows-gpu-support.md | 130 ++++++++++++++++++ docs/windows-rdna4-port.md | 16 ++- scripts/windows/build-native-texture.ps1 | 5 +- scripts/windows/build-windows-rdna4.ps1 | 7 +- scripts/windows/gpu-target.ps1 | 26 ++++ scripts/windows/package-windows-game.ps1 | 37 ++++- scripts/windows/profile-windows-rdna4.ps1 | 4 +- scripts/windows/stage-native-k.ps1 | 16 ++- scripts/windows/stage-native-m.ps1 | 16 ++- scripts/windows/test-async-k.ps1 | 6 +- .../windows/test-deferred-kernel-profile.ps1 | 8 +- scripts/windows/test-gpu-target-contract.ps1 | 23 ++++ scripts/windows/test-k-captures.ps1 | 4 +- scripts/windows/test-k-replay.ps1 | 6 +- scripts/windows/test-m-captures.ps1 | 4 +- scripts/windows/test-m-replay.ps1 | 6 +- scripts/windows/test-native-overrides.ps1 | 4 +- scripts/windows/test-native-texture.ps1 | 18 ++- scripts/windows/test-windows-rdna4.ps1 | 29 ++-- scripts/windows/windows-game.ps1 | 15 +- tools/windows/binary16.h | 51 +++++++ tools/windows/binary16_probe.cpp | 45 ++++++ tools/windows/build_native_texture.py | 24 +++- tools/windows/cuda_driver_probe.cpp | 4 +- tools/windows/cuda_image_probe.cpp | 4 +- tools/windows/cuda_mma_probe.cpp | 10 +- tools/windows/d3d12_command_hooks.generated.h | 4 +- tools/windows/d3d12_command_probe.cpp | 4 +- tools/windows/d3d12_dlss_probe.cpp | 6 +- tools/windows/d3d12_hip_interop_probe.cpp | 4 +- tools/windows/d3d12_pixel_probe.cpp | 4 +- tools/windows/d4r_nvngx_windows.cpp | 4 +- tools/windows/generate_d3d12_hooks.py | 5 +- tools/windows/gfx12_wmma_probe.cpp | 4 +- tools/windows/gpu-target.json.in | 6 + tools/windows/gpu_target.h | 41 ++++++ tools/windows/gpu_target_probe.cpp | 31 +++++ tools/windows/hip_api.h | 12 +- tools/windows/hip_gfx1201_probe.cpp | 6 +- tools/windows/hip_stream_lifecycle_probe.cpp | 2 +- tools/windows/k_layer_validate.py | 4 +- tools/windows/k_module_probe.cpp | 6 +- tools/windows/k_replay_validate.py | 2 +- tools/windows/m_replay_validate.py | 2 +- tools/windows/native_replay_probe.cpp | 6 +- tools/windows/ngx_cuda_init_probe.cpp | 4 +- tools/windows/ngx_windows_runtime.h | 6 +- tools/windows/nvapi_trace.cpp | 6 +- tools/windows/pixel_conversion.h | 1 + 51 files changed, 636 insertions(+), 127 deletions(-) create mode 100644 docs/windows-gpu-support.md create mode 100644 scripts/windows/gpu-target.ps1 create mode 100644 scripts/windows/test-gpu-target-contract.ps1 create mode 100644 tools/windows/binary16.h create mode 100644 tools/windows/binary16_probe.cpp create mode 100644 tools/windows/gpu-target.json.in create mode 100644 tools/windows/gpu_target.h create mode 100644 tools/windows/gpu_target_probe.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4049b66..1909c96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,25 +13,36 @@ if(NOT EXISTS "${D4R_HIP_ROOT}/include/hip/hip_runtime_api.h") message(FATAL_ERROR "Set D4R_HIP_ROOT to a Windows HIP SDK (headers and bin/clang++.exe required)") endif() find_program(D4R_HIP_CLANG NAMES clang++.exe PATHS "${D4R_HIP_ROOT}/bin" "${D4R_HIP_ROOT}/lib/llvm/bin" NO_DEFAULT_PATH REQUIRED) -set(probe_object "${CMAKE_CURRENT_BINARY_DIR}/probe_gfx1201.hsaco") +# Architecture parameterization adapted from realdody/d4r ae530b0. +# Keep explicit subtargets: no gfx11 substitution or untested future gfx12 ISA. +set(D4R_GPU_ARCH "gfx1201" CACHE STRING "Native Windows package GPU target") +set_property(CACHE D4R_GPU_ARCH PROPERTY STRINGS gfx1201 gfx1200) +set(d4r_supported_targets gfx1200 gfx1201) +if(NOT D4R_GPU_ARCH IN_LIST d4r_supported_targets) + message(FATAL_ERROR "D4R_GPU_ARCH must be gfx1200 or gfx1201") +endif() +add_compile_definitions(D4R_TARGET_ARCH="${D4R_GPU_ARCH}") +configure_file(tools/windows/gpu-target.json.in gpu-target.json @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/gpu-target.json" DESTINATION .) +set(probe_object "${CMAKE_CURRENT_BINARY_DIR}/probe_${D4R_GPU_ARCH}.hsaco") add_custom_command(OUTPUT "${probe_object}" - COMMAND "${D4R_HIP_CLANG}" -x hip --offload-arch=gfx1201 + COMMAND "${D4R_HIP_CLANG}" -x hip --offload-arch=${D4R_GPU_ARCH} --offload-device-only --no-gpu-bundle-output -mno-wavefrontsize64 -nogpuinc -nogpulib -O2 "${CMAKE_CURRENT_SOURCE_DIR}/tools/windows/probe_kernel.hip" -o "${probe_object}" DEPENDS tools/windows/probe_kernel.hip VERBATIM) add_custom_target(d4r_probe_code_object ALL DEPENDS "${probe_object}") -set(pixel_object "${CMAKE_CURRENT_BINARY_DIR}/pixel_convert_gfx1201.hsaco") +set(pixel_object "${CMAKE_CURRENT_BINARY_DIR}/pixel_convert_${D4R_GPU_ARCH}.hsaco") add_custom_command(OUTPUT "${pixel_object}" - COMMAND "${D4R_HIP_CLANG}" -x hip -std=c++17 --offload-arch=gfx1201 + COMMAND "${D4R_HIP_CLANG}" -x hip -std=c++17 --offload-arch=${D4R_GPU_ARCH} --offload-device-only --no-gpu-bundle-output -mno-wavefrontsize64 -nogpuinc -nogpulib -O2 "${CMAKE_CURRENT_SOURCE_DIR}/tools/windows/pixel_convert_gfx1201.hip" -o "${pixel_object}" DEPENDS tools/windows/pixel_convert_gfx1201.hip tools/windows/pixel_format.h kernels/common/hip_device_minimal.h VERBATIM) add_custom_target(d4r_pixel_code_object ALL DEPENDS "${pixel_object}") -set(wmma_object "${CMAKE_CURRENT_BINARY_DIR}/wmma_gfx1201.hsaco") +set(wmma_object "${CMAKE_CURRENT_BINARY_DIR}/wmma_${D4R_GPU_ARCH}.hsaco") add_custom_command(OUTPUT "${wmma_object}" - COMMAND "${D4R_HIP_CLANG}" -x hip --offload-arch=gfx1201 + COMMAND "${D4R_HIP_CLANG}" -x hip --offload-arch=${D4R_GPU_ARCH} --offload-device-only --no-gpu-bundle-output -mno-wavefrontsize64 -nogpuinc -nogpulib -O2 "${CMAKE_CURRENT_SOURCE_DIR}/tools/windows/wmma_gfx1201.hip" -o "${wmma_object}" @@ -49,7 +60,7 @@ foreach(family k m) file(GLOB native_headers CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/kernels/${family}/*.h") foreach(source IN LISTS native_sources) get_filename_component(kernel_name "${source}" NAME_WE) - set(native_object "${CMAKE_CURRENT_BINARY_DIR}/${kernel_name}_gfx1201.hsaco") + set(native_object "${CMAKE_CURRENT_BINARY_DIR}/${kernel_name}_${D4R_GPU_ARCH}.hsaco") set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${source}") file(READ "${source}" kernel_source) string(REGEX MATCH "// d4r-build-flags: ([^\r\n]+)" flags_line "${kernel_source}") @@ -65,7 +76,7 @@ foreach(family k m) list(APPEND native_flags ${source_flags}) endif() add_custom_command(OUTPUT "${native_object}" - COMMAND "${D4R_HIP_CLANG}" -x hip -std=c++17 --offload-arch=gfx1201 + COMMAND "${D4R_HIP_CLANG}" -x hip -std=c++17 --offload-arch=${D4R_GPU_ARCH} --offload-device-only --no-gpu-bundle-output -mno-wavefrontsize64 -nogpuinc -nogpulib -O3 -DD4R_DEVICE_ONLY_MINIMAL ${native_flags} "${source}" -o "${native_object}" @@ -75,7 +86,9 @@ foreach(family k m) install(FILES "${native_object}" DESTINATION "experimental/${family}") endforeach() endforeach() -add_custom_target(d4r_native_gfx1201_compile ALL DEPENDS ${native_objects}) +add_custom_target(d4r_native_compile ALL DEPENDS ${native_objects}) +# Retain the old manual build target for existing scripts. +add_custom_target(d4r_native_gfx1201_compile DEPENDS d4r_native_compile) foreach(probe hip_gfx1201_probe gfx12_wmma_probe k_module_probe native_replay_probe cuda_driver_probe cuda_mma_probe cuda_image_probe d3d12_command_probe d3d12_pixel_probe d3d12_hip_interop_probe hip_stream_lifecycle_probe ngx_cuda_init_probe d3d12_dlss_probe) add_executable(d4r_${probe} tools/windows/${probe}.cpp) target_compile_definitions(d4r_${probe} PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__) @@ -92,6 +105,16 @@ foreach(probe hip_gfx1201_probe gfx12_wmma_probe k_module_probe native_replay_pr endif() install(TARGETS d4r_${probe} RUNTIME DESTINATION bin) endforeach() +foreach(probe binary16_probe gpu_target_probe) + add_executable(d4r_${probe} tools/windows/${probe}.cpp) + if(MINGW) + target_link_options(d4r_${probe} PRIVATE -static) + elseif(MSVC) + set_property(TARGET d4r_${probe} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + endif() + install(TARGETS d4r_${probe} RUNTIME DESTINATION bin) +endforeach() +add_dependencies(d4r_gpu_target_probe d4r_probe_code_object d4r_pixel_code_object) set(D4R_MINHOOK_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/external/MinHook" CACHE PATH "MinHook 1.3.4 source checkout") if(NOT EXISTS "${D4R_MINHOOK_ROOT}/include/MinHook.h") message(FATAL_ERROR "Clone MinHook v1.3.4 into external/MinHook or set D4R_MINHOOK_ROOT") @@ -171,11 +194,13 @@ if(EXISTS "${D4R_AGILITY_ROOT}/build/native/bin/x64/d3d12SDKLayers.dll") endif() add_dependencies(d4r_hip_gfx1201_probe d4r_probe_code_object) add_dependencies(d4r_gfx12_wmma_probe d4r_wmma_code_object) -add_dependencies(d4r_k_module_probe d4r_native_gfx1201_compile) +add_dependencies(d4r_k_module_probe d4r_native_compile) install(FILES "${probe_object}" DESTINATION bin) install(FILES "${pixel_object}" DESTINATION bin) install(FILES "${wmma_object}" DESTINATION bin) install(FILES scripts/windows/test-windows-rdna4.ps1 DESTINATION .) +install(FILES scripts/windows/gpu-target.ps1 DESTINATION .) +install(FILES scripts/windows/test-gpu-target-contract.ps1 DESTINATION .) install(FILES scripts/windows/test-k-replay.ps1 DESTINATION .) install(FILES scripts/windows/test-k-captures.ps1 DESTINATION .) install(FILES scripts/windows/test-native-overrides.ps1 DESTINATION .) @@ -192,13 +217,23 @@ install(FILES kernels/tools/pwin_model.py kernels/tools/pwin_check.py kernels/to kernels/tools/extract_dlss_ptx.py kernels/tools/kernel_manifest.py DESTINATION reference) install(FILES kernels/tools/model_enc3.py kernels/tools/swin_model.py kernels/tools/swin_check.py DESTINATION reference) install(FILES docs/windows-rdna4-port.md DESTINATION docs) +install(FILES docs/windows-gpu-support.md DESTINATION docs) include(CTest) +add_test(NAME binary16_rounding COMMAND d4r_binary16_probe) +set_tests_properties(binary16_rounding PROPERTIES TIMEOUT 30 LABELS "software;numerics") +add_test(NAME gpu_target_contract COMMAND d4r_gpu_target_probe "${probe_object}" "${pixel_object}") +set_tests_properties(gpu_target_contract PROPERTIES TIMEOUT 30 LABELS "software;architecture") +find_program(D4R_POWERSHELL NAMES pwsh.exe powershell.exe REQUIRED) +add_test(NAME gpu_target_script_contract COMMAND "${D4R_POWERSHELL}" -NoProfile -File + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/windows/test-gpu-target-contract.ps1" + -PackageRoot "${CMAKE_CURRENT_BINARY_DIR}" -CodeObject "${probe_object}") +set_tests_properties(gpu_target_script_contract PROPERTIES TIMEOUT 30 LABELS "software;architecture") add_test(NAME d3d12_gpu_pixel_formats COMMAND d4r_d3d12_pixel_probe --hip-root "${D4R_HIP_ROOT}" --module "${pixel_object}" --iterations 2) -set_tests_properties(d3d12_gpu_pixel_formats PROPERTIES TIMEOUT 120 LABELS "hardware;gfx1201;formats") +set_tests_properties(d3d12_gpu_pixel_formats PROPERTIES TIMEOUT 120 LABELS "hardware;${D4R_GPU_ARCH};formats") add_test(NAME d3d12_gpu_enhanced_formats COMMAND d4r_d3d12_pixel_probe --hip-root "${D4R_HIP_ROOT}" --module "${pixel_object}" --interop-mode enhanced --iterations 2) -set_tests_properties(d3d12_gpu_enhanced_formats PROPERTIES TIMEOUT 120 LABELS "hardware;gfx1201;formats;enhanced_barriers") +set_tests_properties(d3d12_gpu_enhanced_formats PROPERTIES TIMEOUT 120 LABELS "hardware;${D4R_GPU_ARCH};formats;enhanced_barriers") add_test(NAME d3d12_command_backend COMMAND d4r_d3d12_command_probe --hip-root "${D4R_HIP_ROOT}" --module "$" --interop-mode indirect --iterations 32) set_tests_properties(d3d12_command_backend PROPERTIES TIMEOUT 120 LABELS "hardware;d3d12;command_lists;lifetime") @@ -211,16 +246,16 @@ set_tests_properties(d3d12_command_root_reset_warp PROPERTIES TIMEOUT 120 LABELS ENVIRONMENT "D4R_COMMAND_PROBE_WARP=1;D4R_COMMAND_PROBE_NO_HIP=1") add_test(NAME hip_gfx1201 COMMAND d4r_hip_gfx1201_probe --hip-root "${D4R_HIP_ROOT}" --module "${probe_object}") -set_tests_properties(hip_gfx1201 PROPERTIES TIMEOUT 90 LABELS "hardware;gfx1201") +set_tests_properties(hip_gfx1201 PROPERTIES TIMEOUT 90 LABELS "hardware;${D4R_GPU_ARCH}") add_test(NAME gfx12_wmma COMMAND d4r_gfx12_wmma_probe --hip-root "${D4R_HIP_ROOT}" --module "${wmma_object}" --iterations 16) -set_tests_properties(gfx12_wmma PROPERTIES TIMEOUT 90 LABELS "hardware;gfx1201;wmma") +set_tests_properties(gfx12_wmma PROPERTIES TIMEOUT 90 LABELS "hardware;${D4R_GPU_ARCH};wmma") foreach(layer enc1 enc2) add_test(NAME k_${layer}_execution COMMAND d4r_k_module_probe --hip-root "${D4R_HIP_ROOT}" - --module "${CMAKE_CURRENT_BINARY_DIR}/dltss_pwin_${layer}_layer_gfx1201.hsaco" + --module "${CMAKE_CURRENT_BINARY_DIR}/dltss_pwin_${layer}_layer_${D4R_GPU_ARCH}.hsaco" --kernel-name ${layer}) - set_tests_properties(k_${layer}_execution PROPERTIES TIMEOUT 90 LABELS "hardware;gfx1201;k_execution") + set_tests_properties(k_${layer}_execution PROPERTIES TIMEOUT 90 LABELS "hardware;${D4R_GPU_ARCH};k_execution") endforeach() add_test(NAME d3d12_hip_interop COMMAND d4r_d3d12_hip_interop_probe --hip-root "${D4R_HIP_ROOT}" --module "${probe_object}" --iterations 32) diff --git a/docs/windows-game.md b/docs/windows-game.md index 182f7b9..eb5d3bd 100644 --- a/docs/windows-game.md +++ b/docs/windows-game.md @@ -8,6 +8,14 @@ The user reports 49-51 FPS for K in the measured scene after the command-hook fix and locally built output-store optimization. This is a local development package; no NVIDIA proprietary DLL is included. +Builds also accept gfx1200 (for example RX 9060/9060 XT). This target is +**compile-tested only**; no physical gfx1200 validation has been performed. +RX 9070 and other gfx1201 cards also remain untested. Use a package built for +the GPU's actual HIP architecture: its `gpu-target.json`, host shim and ELF +code objects must agree. Installation/runtime reject mixed targets. See +[windows-gpu-support.md](windows-gpu-support.md) for separate-target build +commands, the fork review and the first hardware diagnostic gate. + Build the shim/diagnostics, corrected ZLUDA, K/M native objects and patched OptiScaler using the scripts in `scripts/windows`, then run: diff --git a/docs/windows-gpu-support.md b/docs/windows-gpu-support.md new file mode 100644 index 0000000..1ab4c9d --- /dev/null +++ b/docs/windows-gpu-support.md @@ -0,0 +1,130 @@ +# Windows GPU targets + +The native Windows build accepts **gfx1200** and **gfx1201**. It builds a +separate shim, diagnostics and code objects for each target. The default stays +gfx1201. GPU selection uses HIP's real `gcnArchName`, followed by D3D12/HIP +adapter LUID matching; it does not depend on a marketing name or overridden +DXGI vendor ID. + +| Target | Examples in LLVM's processor table | This project's validation | +| --- | --- | --- | +| gfx1201 | RX 9070, RX 9070 XT | RX 9070 XT: hardware, K/M and game tests. Other cards untested. | +| gfx1200 | RX 9060, RX 9060 XT | Host/device compilation and software tests only. No physical gfx1200 available. | + +Sources: [LLVM AMDGPU processors](https://llvm.org/docs/AMDGPUUsage.html#processors) +and [AMD's Windows compatibility matrix](https://rocm.docs.amd.com/projects/radeon-ryzen/en/latest/docs/compatibility/compatibilityrad/windows/windows_compatibility.html). +AMD's matrix is dependency information, not evidence that d4r's K/M or external +memory path works on an untested card. Other RDNA4 SKUs are eligible only when +HIP reports one of these exact targets and exposes the required Windows APIs. +Device-specific drivers, performance and memory capacity still need testing. +gfx1250/gfx1251 are a separate ISA group and are not enabled by this change. + +Do not rename a gfx1201 package to gfx1200. `gpu-target.json`, the compiled +host target and each ELF code-object target must agree. Runtime selection +rejects another architecture before launching kernels. Staging, packaging and +installation inspect the documented AMDGPU ELF machine ID and reject mixed +objects. They never rewrite a binary target or set `HSA_OVERRIDE_GFX_VERSION`. +Legacy packages without target metadata remain gfx1201 only. + +## Build and package + +Prepare the pinned dependencies as described in `windows-rdna4-port.md`. +Select a target explicitly; this example creates the experimental gfx1200 +package. Use gfx1201 for a 9070/9070 XT. Keep separate output directories when +building both targets. + +```powershell +$arch = 'gfx1200' +$diag = "$PWD\dist\windows-$arch-diagnostics" +.\scripts\windows\build-windows-rdna4.ps1 -RuntimeProfile therock -GpuArch $arch -ZludaRoot "$PWD\dist\zluda-windows-final" -BuildDirectory "$PWD\build\windows-$arch" -InstallDirectory $diag +.\scripts\windows\stage-native-k.ps1 -DlssDll "$PWD\nvngx_dlss.dll" -PackageRoot $diag +.\scripts\windows\stage-native-m.ps1 -DlssDll "$PWD\nvngx_dlss.dll" -PackageRoot $diag +.\scripts\windows\package-windows-game.ps1 -DiagnosticRoot $diag -GpuArch $arch -PackageRoot "$PWD\dist\windows-$arch-game" -ArchivePath "$PWD\dist\windows-$arch-game.zip" +``` + +ZLUDA, HIP and OptiScaler must already be built/prepared. The package contains +the strict K FP16 and M FP16-equivalent baseline; native FP8 stays disabled. +The architecture does not change arithmetic or the existing WMMA layout. +Private NVIDIA-derived texture overrides remain optional local experiments; +their builder accepts `-GpuArch`, and their validation must match the target +package before installation. Existing gfx1201 performance measurements are +not measurements of gfx1200. +The separate experimental FP8/F16 arithmetic and tuning recipes remain +gfx1201-specific; this change enables the conservative production baseline +on both build targets. + +Equivalent CMake selection is `-DD4R_GPU_ARCH=gfx1200`. Unknown targets fail +configuration. Some diagnostic executable names and source filenames retain +`gfx1201` for compatibility; the logged architecture and compiled code objects +use the selected target. + +## First hardware gate on another card + +From the repository root, with the matching diagnostic package above and the +pinned dependencies, run this single command: + +```powershell +.\scripts\windows\test-windows-rdna4.ps1 -RuntimeProfile therock -PackageRoot "$PWD\dist\windows-gfx1200-diagnostics" -ZludaRoot "$PWD\dist\zluda-windows-final" +``` + +Success requires exit 0, all selected tests passed in `summary.json`, the +correct actual GPU/target in stdout, exact HIP/PTX/WMMA/pixel-format checks and +stable interop lifetime. On failure, retain the single printed diagnostic ZIP; +it includes stdout/stderr, exit code, runtime/driver inventory and DLL identity. +No NVIDIA DLL is required for this first gate. Full K/M reference, temporal, +standalone DLSS and game validation are subsequent gates on that hardware; +passing the first gate alone does not close them. + +Without that GPU, run `ctest --test-dir build/windows-gfx1200 -L software +--output-on-failure`. These CPU/WARP checks do not execute gfx1200 kernels. + +## Fork review + +Reviewed [realdody's windows-rdna3 branch](https://github.com/realdody/d4r/tree/windows-rdna3) +at `ae530b0f53ed5c0bb87558dd7c20f619e014e4fd`, on 2026-10-02. Adapted: + +- Per-target build configuration and actual-target diagnostics, narrowed here + to the two documented RDNA4 subtargets, with package/ELF guards added. +- MSVC binary16 conversion in the synthetic CUDA MMA reference. An exhaustive + CPU test covers 63,490 finite/infinity encodings and 31,743 rounding + boundaries; LLVM also checks it against native `_Float16` conversion. +- `WINBOOL` to `BOOL` normalization in generated D3D12 hooks, so the generated + header works with the Microsoft SDK as well as MinGW headers. + +The fork's RDNA3 implementation forces `D4R_WMMA_LAYOUT=12` for gfx11. That +kernel-layout change, the raw gfx11 probe and the expanded gfx11 runtime +allowlist are not imported. Its reported RX 7800 XT/K result has not been +independently reproduced here; its M/translated-path limitations still require +separate checks. This change does not advertise native Windows RDNA3 readiness. +The existing upstream Linux/RDNA3 implementation is unchanged. Credit for +the adapted portability fixes: realdody . + +## Local results, 2026-10-02 + +- Both targets: complete MinGW host build and 19 device code objects using + TheRock `10.2.0a20260929`, HIP `7.17.26386`, Clang 24. The runtime, driver, + patched ZLUDA and OptiScaler versions are unchanged from the validated port. +- gfx1201/RX 9070 XT: 20/20 CTest gates, including 16 physical-GPU gates. +- gfx1200: 4/4 software gates; no gfx1200 kernel execution. +- MSVC v143 `14.44.35207`: binary16 CPU test passes; CUDA MMA probe and D3D12 + hook translation units compile. This is not a complete MSVC build claim. +- K: 12 finite 4K frames at 2259x1271 input, burst and feature recreation; + all six async RGB images exactly match their synchronous controls. +- M: eight finite 4K control/candidate frames at the same input dimensions; + all four new-build RGB images exactly match the prior validated build. + Both presets log every required native layer, zero CPU image copies and + current-frame output. +- Original gfx1201 objects remain byte-identical on disk. In fresh builds, + `.text` and `.rodata*` match the prior gfx1201 objects for all 19 kernels; + fresh ELF files have different overall hashes. gfx1200 and gfx1201 have + distinct correct ELF targets. Matching instruction sections are compile + evidence, not a substitute for testing gfx1200 hardware. +- A gfx1200 HIP probe on the physical gfx1201 fails cleanly with exit 4 before + any GPU allocation/kernel execution. This tests refusal, not gfx1200 support. +- Both target packages pass install/restore checks in isolated workspace + fixtures; no game is launched. Only manifest-listed runtime files install. + Packaging rejects a mismatched destination or native kernel directory before + writing files. Every packaged code object has the expected ELF target. + +Logs are local in `test-results/rdna4-targets-*` and +`test-results/rdna4-target-objects.json`; they contain no public NVIDIA DLLs. diff --git a/docs/windows-rdna4-port.md b/docs/windows-rdna4-port.md index 5361212..0d1a2c5 100644 --- a/docs/windows-rdna4-port.md +++ b/docs/windows-rdna4-port.md @@ -1,6 +1,7 @@ # Windows / RDNA4 port -Branch: `windows-rdna4`. Target: Windows 11 x64, RX 9070 XT, **gfx1201**. +Branch: `windows-rdna4`. Native Windows build targets: **gfx1200 / gfx1201**. +Validated physical hardware: Windows 11 x64, RX 9070 XT, **gfx1201**. Priority: correct K, correct M, native Windows, same-frame output, then speed. Upstream integration: [PR #11](https://github.com/countervolts/d4r/pull/11) is merged into `countervolts/d4r:windows` at `c522101`. @@ -17,6 +18,16 @@ explicit limits; the validated package keeps conservative arithmetic. ## Milestone and gates +2026-10-02 target coverage: gfx1200 and gfx1201 now have separate host/device +builds and guarded packages. Both compile; gfx1201 passes 20 CTest gates on +RX 9070 XT, and gfx1200 passes four software-only gates. No physical gfx1200 +test is available. K's new host build passes twelve finite 4K burst/recreation +frames with all six async RGB images exact. M's four new-build 4K RGB images +exactly match the previous build. The fork audit, adopted MSVC fixes, +per-target commands, validation limits and first hardware gate are maintained +in [windows-gpu-support.md](windows-gpu-support.md). RDNA3 runtime/layout changes +from the reviewed fork are not imported. K performance remains open. + **Current milestone: K performance remains open.** The latest unprofiled 4K Silent Hill 2 scene capture records 65.250 FPS. Functional K/M and the reproducible archive do not close this gate. Prioritize measured K kernel cost @@ -1496,7 +1507,8 @@ automatically on CUDA failure when `trace/nvcuda.dll` is available. The trace DLL forwards to the real DLL through `ZLUDA_CUDA_LIB`; explicit loading no longer bypasses the trace by accidentally passing the real DLL to the launcher. -The HIP module compiler uses the selected SDK, fixed `--offload-arch=gfx1201`, +The HIP module compiler uses the selected SDK, `--offload-arch=${D4R_GPU_ARCH}` +(gfx1201 by default, optionally gfx1200), wave32 and no host/device library dependency. Host code uses the installed HIP header ABI and loads that SDK's `amdhip64_7.dll` by absolute path. CUDA loads the provided ZLUDA DLL by absolute path, matches the adapter against HIP PCI identity, diff --git a/scripts/windows/build-native-texture.ps1 b/scripts/windows/build-native-texture.ps1 index 36c28bb..20ae134 100644 --- a/scripts/windows/build-native-texture.ps1 +++ b/scripts/windows/build-native-texture.ps1 @@ -5,12 +5,13 @@ param( [string]$HipRoot = 'C:\Program Files\AMD\ROCm\7.2', [string]$ZludaRoot, [ValidateSet('cu','wgp')][string]$ShaderMode='cu', + [ValidateSet('gfx1200','gfx1201')][string]$GpuArch='gfx1201', [string]$OutputDirectory ) $ErrorActionPreference='Stop' $repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' } -if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo 'build/private-textures-gfx1201' } +if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo "build/private-textures-$GpuArch" } & python (Join-Path $repo 'tools/windows/build_native_texture.py') --dlss-dll $DlssDll --hip-root $HipRoot ` - --zluda-root $ZludaRoot --kernel $Kernel --output-directory $OutputDirectory --shader-mode $ShaderMode + --zluda-root $ZludaRoot --kernel $Kernel --output-directory $OutputDirectory --shader-mode $ShaderMode --gpu-arch $GpuArch if ($LASTEXITCODE) { throw "Native texture build failed ($LASTEXITCODE); inspect the output work directory." } diff --git a/scripts/windows/build-windows-rdna4.ps1 b/scripts/windows/build-windows-rdna4.ps1 index 68debea..a60a752 100644 --- a/scripts/windows/build-windows-rdna4.ps1 +++ b/scripts/windows/build-windows-rdna4.ps1 @@ -2,6 +2,7 @@ param( [string]$HipRoot = $env:HIP_PATH, [ValidateSet('stable', 'therock')][string]$RuntimeProfile = 'stable', + [ValidateSet('gfx1200','gfx1201')][string]$GpuArch = 'gfx1201', [string]$ZludaRoot, [string]$ToolchainRoot, [string]$BuildDirectory, @@ -17,6 +18,10 @@ if ($RuntimeProfile -eq 'therock') { if (!$HipRoot) { $HipRoot = 'C:\Program Files\AMD\ROCm\7.2' } if (!$BuildDirectory) { $BuildDirectory = Join-Path $repo 'build/windows-rdna4' } if (!$InstallDirectory) { $InstallDirectory = Join-Path $repo 'dist/windows-rdna4-diagnostics' } +if ($GpuArch -ne 'gfx1201') { + if (!$PSBoundParameters.ContainsKey('BuildDirectory')) { $BuildDirectory += "-$GpuArch" } + if (!$PSBoundParameters.ContainsKey('InstallDirectory')) { $InstallDirectory += "-$GpuArch" } +} $HipRoot = [IO.Path]::GetFullPath($HipRoot).TrimEnd('\', '/') $BuildDirectory = [IO.Path]::GetFullPath($BuildDirectory).TrimEnd('\', '/') $InstallDirectory = [IO.Path]::GetFullPath($InstallDirectory).TrimEnd('\', '/') @@ -47,7 +52,7 @@ if ($minhookCommit -ne 'c3fcafdc10146beb5919319d0683e44e3c30d537') { $originalPath = $env:PATH try { $configure = @('--fresh', '-S', $repo, '-B', $BuildDirectory, '-G', 'Ninja', '-DCMAKE_BUILD_TYPE=RelWithDebInfo', - "-DCMAKE_MAKE_PROGRAM=$ninja", "-DD4R_HIP_ROOT=$HipRoot", "-DCMAKE_INSTALL_PREFIX=$InstallDirectory") + "-DCMAKE_MAKE_PROGRAM=$ninja", "-DD4R_HIP_ROOT=$HipRoot", "-DD4R_GPU_ARCH=$GpuArch", "-DCMAKE_INSTALL_PREFIX=$InstallDirectory") if ($ToolchainRoot) { $compiler = Join-Path $ToolchainRoot 'bin/x86_64-w64-mingw32-clang++.exe' if (!(Test-Path $compiler)) { throw "Compiler missing: $compiler" } diff --git a/scripts/windows/gpu-target.ps1 b/scripts/windows/gpu-target.ps1 new file mode 100644 index 0000000..e05ab38 --- /dev/null +++ b/scripts/windows/gpu-target.ps1 @@ -0,0 +1,26 @@ +# Shared by the build, diagnostics, staging and game package scripts. +function Get-D4RGpuTarget([string]$Root, [string]$Requested) { + $configured=$null + $targetFile=Join-Path $Root 'gpu-target.json' + if (Test-Path -LiteralPath $targetFile) { + $configured=(Get-Content -LiteralPath $targetFile -Raw | ConvertFrom-Json).architecture + if ($configured -notin @('gfx1200','gfx1201')) { throw "Unsupported package GPU target: $configured" } + } + $target=if ($Requested) { $Requested } elseif ($configured) { $configured } else { 'gfx1201' } + if ($target -notin @('gfx1200','gfx1201')) { throw "Unsupported GPU target: $target" } + if (!$configured -and $target -ne 'gfx1201') { throw 'A gfx1200 build requires gpu-target.json; rebuild with -GpuArch gfx1200.' } + if ($configured -and $target -ne $configured) { throw "Requested $target differs from package target $configured" } + return $target +} +function Assert-D4RCodeObjectTarget([string]$Path, [string]$Target) { + if ($Target -notin @('gfx1200','gfx1201')) { throw "Unsupported GPU target: $Target" } + $reader=[IO.BinaryReader]::new([IO.File]::OpenRead($Path)) + try { $header=$reader.ReadBytes(64) } finally { $reader.Dispose() } + if ($header.Length -ne 64 -or $header[0] -ne 127 -or $header[1] -ne 69 -or $header[2] -ne 76 -or $header[3] -ne 70 -or + $header[4] -ne 2 -or $header[5] -ne 1 -or $header[18] -ne 224 -or $header[19] -ne 0) { + throw "Invalid AMDGPU ELF64 code object: $Path" + } + # LLVM AMDGPU e_flags machine IDs; inspect, never patch or spoof them. + $expected=if ($Target -eq 'gfx1200') { 0x48 } else { 0x4e } + if ($header[48] -ne $expected) { throw "Code object does not target ${Target}: $Path (ELF machine=$($header[48]))" } +} diff --git a/scripts/windows/package-windows-game.ps1 b/scripts/windows/package-windows-game.ps1 index 14e0258..8c33dc7 100644 --- a/scripts/windows/package-windows-game.ps1 +++ b/scripts/windows/package-windows-game.ps1 @@ -2,18 +2,32 @@ param( [string]$PackageRoot, [string]$DiagnosticRoot, [string]$OptiScalerRoot, [string]$ZludaRoot, [string]$HipRoot, [string]$KernelKRoot, [string]$KernelMRoot, + [ValidateSet('gfx1200','gfx1201')][string]$GpuArch, [string]$ArchivePath ) $ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'gpu-target.ps1') $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $defaults = @{PackageRoot='dist/windows-rdna4-game'; DiagnosticRoot='dist/windows-rdna4-command-list'; OptiScalerRoot='dist/optiscaler-windows-d4r'; ZludaRoot='dist/zluda-windows-final'; - HipRoot='.tools/therock-10.2.0a20260929/_rocm_sdk_core'; - KernelKRoot='build/native-k-gfx1201'; KernelMRoot='build/native-m-gfx1201'} + HipRoot='.tools/therock-10.2.0a20260929/_rocm_sdk_core'} foreach ($name in $defaults.Keys) { if (!(Get-Variable -Name $name -ValueOnly)) { Set-Variable -Name $name -Value (Join-Path $repo $defaults[$name]) } } +$GpuArch=Get-D4RGpuTarget $DiagnosticRoot $GpuArch +if ($GpuArch -eq 'gfx1200' -and !$PSBoundParameters.ContainsKey('PackageRoot')) { $PackageRoot += '-gfx1200' } +if (!$KernelKRoot) { $KernelKRoot=Join-Path $repo "build/native-k-$GpuArch" } +if (!$KernelMRoot) { $KernelMRoot=Join-Path $repo "build/native-m-$GpuArch" } +foreach ($directory in @($KernelKRoot,$KernelMRoot)) { + $info=Join-Path $directory 'build-info.json' + if ((Test-Path -LiteralPath $info) -and (Get-Content -LiteralPath $info -Raw | ConvertFrom-Json).architecture -ne $GpuArch) { + throw "Native kernel directory does not target ${GpuArch}: $directory" + } +} $PackageRoot = [IO.Path]::GetFullPath($PackageRoot) +if (Test-Path -LiteralPath (Join-Path $PackageRoot 'gpu-target.json')) { + [void](Get-D4RGpuTarget $PackageRoot $GpuArch) +} $sourceCommit=(& git -C $repo rev-parse HEAD).Trim() $sourceDirty=[bool](& git -C $repo status --porcelain) if ($ArchivePath -and $sourceDirty) { throw 'Commit source changes before creating the distributable archive.' } @@ -21,6 +35,7 @@ New-Item -ItemType Directory -Force $PackageRoot | Out-Null $stagedFiles = [Collections.Generic.List[string]]::new() function Stage([string]$source, [string]$relative) { if (!(Test-Path -LiteralPath $source -PathType Leaf)) { throw "Required file missing: $source" } + if ($source.EndsWith('.hsaco')) { Assert-D4RCodeObjectTarget $source $GpuArch } $destination = Join-Path $PackageRoot $relative New-Item -ItemType Directory -Force (Split-Path $destination) | Out-Null Copy-Item -LiteralPath $source -Destination $destination -Force @@ -28,7 +43,7 @@ function Stage([string]$source, [string]$relative) { } Stage (Join-Path $OptiScalerRoot 'OptiScaler.dll') 'OptiScaler.dll' Stage (Join-Path $DiagnosticRoot 'bin/d4r_nvngx.dll') 'd4r/_nvngx.dll' -Stage (Join-Path $DiagnosticRoot 'bin/pixel_convert_gfx1201.hsaco') 'd4r/pixel_convert_gfx1201.hsaco' +Stage (Join-Path $DiagnosticRoot "bin/pixel_convert_${GpuArch}.hsaco") "d4r/pixel_convert_${GpuArch}.hsaco" Stage (Join-Path $DiagnosticRoot 'nvapi-compat/nvapi64.dll') 'd4r/nvapi/nvapi64.dll' Stage (Join-Path $DiagnosticRoot 'bin/d4r_debug_launcher.exe') 'd4r/d4r_debug_launcher.exe' foreach ($file in @('nvcuda.dll','nvapi64.dll','libc++.dll','libunwind.dll')) { Stage (Join-Path $ZludaRoot $file) "d4r/zluda/$file" } @@ -46,14 +61,26 @@ foreach ($directory in @($KernelKRoot, $KernelMRoot)) { } } if ($entries.Count -ne 16) { throw "Expected 11 K and 5 M native objects; found $($entries.Count)" } -@('# Audited DLSS 310.9.1: K + M gfx1201 native kernels', '# Generated by package-windows-game.ps1') + @($entries.Values | Sort-Object) | +@("# Audited DLSS 310.9.1: K + M $GpuArch native kernels", '# Generated by package-windows-game.ps1') + @($entries.Values | Sort-Object) | Set-Content -LiteralPath (Join-Path $PackageRoot 'd4r/native/d4r-kernels.txt') -Encoding ASCII $stagedFiles.Add('d4r/native/d4r-kernels.txt') Stage (Join-Path $repo 'scripts/windows/windows-game.ps1') 'windows-game.ps1' +Stage (Join-Path $repo 'scripts/windows/gpu-target.ps1') 'gpu-target.ps1' +if (Test-Path -LiteralPath (Join-Path $DiagnosticRoot 'gpu-target.json')) { + Stage (Join-Path $DiagnosticRoot 'gpu-target.json') 'gpu-target.json' +} else { + # Builds predating target metadata were gfx1201 only. The resolver rejects + # any attempt to label such a build gfx1200. + @{schema=1; architecture=$GpuArch; wavefrontSize=32; validation='Legacy gfx1201 build; run the GPU diagnostics.'} | + ConvertTo-Json | Set-Content -LiteralPath (Join-Path $PackageRoot 'gpu-target.json') -Encoding UTF8 + $stagedFiles.Add('gpu-target.json') +} Stage (Join-Path $repo 'scripts/windows/capture-game-window.ps1') 'capture-game-window.ps1' Stage (Join-Path $repo 'scripts/windows/send-game-key.ps1') 'send-game-key.ps1' Stage (Join-Path $repo 'docs/windows-game.md') 'README.md' Stage (Join-Path $repo 'docs/windows-performance.md') 'windows-performance.md' +Stage (Join-Path $repo 'docs/windows-gpu-support.md') 'windows-gpu-support.md' +Stage (Join-Path $repo 'docs/windows-rdna4-port.md') 'windows-rdna4-port.md' Stage (Join-Path $repo 'LICENSE') 'licenses/d4r.txt' Stage (Join-Path $OptiScalerRoot 'GPL-3.0.txt') 'licenses/OptiScaler.txt' Stage (Join-Path $repo 'external/ZLUDA/LICENSE-MIT') 'licenses/ZLUDA-MIT.txt' @@ -70,7 +97,7 @@ New-Item -ItemType Directory -Force (Split-Path $sourceArchive) | Out-Null if ($LASTEXITCODE) { throw 'Cannot archive the committed d4r source.' } Stage $sourceArchive 'source/d4r-windows-rdna4-source.zip' $metadata = [ordered]@{ - architecture='gfx1201'; nativeKernels=16; dlssSha256='3975567b8943c53acce397f2b72380092f84f162d00b0d2c7d08a1025c563983'; + architecture=$GpuArch; hardwareValidation=$(if ($GpuArch -eq 'gfx1201') { 'RX9070XT tested; other SKUs unverified' } else { 'compile-only; hardware unverified' }); nativeKernels=16; dlssSha256='3975567b8943c53acce397f2b72380092f84f162d00b0d2c7d08a1025c563983'; d4rCommit=$sourceCommit; d4rWorkingTreeDirty=$sourceDirty; optiScaler=(Get-Content -LiteralPath (Join-Path $OptiScalerRoot 'build-info.json') -Raw | ConvertFrom-Json); diff --git a/scripts/windows/profile-windows-rdna4.ps1 b/scripts/windows/profile-windows-rdna4.ps1 index 4ed3746..4e0817e 100644 --- a/scripts/windows/profile-windows-rdna4.ps1 +++ b/scripts/windows/profile-windows-rdna4.ps1 @@ -6,13 +6,15 @@ param( [switch]$Translated ) $ErrorActionPreference='Stop' +. (Join-Path $PSScriptRoot 'gpu-target.ps1') $repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' } if (!$PackageRoot) { $PackageRoot=Join-Path $repo 'dist/windows-rdna4-command-list' } +$GpuArch=Get-D4RGpuTarget $PackageRoot $null if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/profile-' + $Preset + '-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')) } $settings=@{D4R_ZLUDA_PROFILE='1'; D4R_PROFILE_STAGES='1'; D4R_VALIDATE_OUTPUT='1'; D4R_ZLUDA_PROFILE_DEFERRED=$null; D4R_ZLUDA_PROFILE_ALLOW_LEGACY=$null; D4R_ZLUDA_PROFILE_EVERY=$null; - D4R_ZLUDA_NATIVE_DIR=$(if ($Translated) { $null } elseif ($Preset -eq 11) { Join-Path $repo 'build/native-k-gfx1201' } else { Join-Path $repo 'build/native-m-gfx1201' })} + D4R_ZLUDA_NATIVE_DIR=$(if ($Translated) { $null } elseif ($Preset -eq 11) { Join-Path $repo "build/native-k-$GpuArch" } else { Join-Path $repo "build/native-m-$GpuArch" })} $old=@{} try { foreach ($key in $settings.Keys) { $old[$key]=[Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') } diff --git a/scripts/windows/stage-native-k.ps1 b/scripts/windows/stage-native-k.ps1 index 3f7dc7b..f470c50 100644 --- a/scripts/windows/stage-native-k.ps1 +++ b/scripts/windows/stage-native-k.ps1 @@ -1,24 +1,30 @@ [CmdletBinding()] -param([Parameter(Mandatory=$true)][string]$DlssDll, [string]$PackageRoot, [string]$OutputDirectory) +param([Parameter(Mandatory=$true)][string]$DlssDll, [string]$PackageRoot, [string]$OutputDirectory, [ValidateSet('gfx1200','gfx1201')][string]$GpuArch) $ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'gpu-target.ps1') $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' } -if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo 'build/native-k-gfx1201' } +$GpuArch=Get-D4RGpuTarget $PackageRoot $GpuArch +if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo "build/native-k-$GpuArch" } if (![IO.Path]::IsPathRooted($DlssDll) -or !(Test-Path -LiteralPath $DlssDll)) { throw 'Supply the absolute path to your local nvngx_dlss.dll.' } New-Item -ItemType Directory -Force $OutputDirectory | Out-Null +foreach ($object in Get-ChildItem -LiteralPath $OutputDirectory -Filter '*.hsaco') { + Assert-D4RCodeObjectTarget $object.FullName $GpuArch +} $layers = @('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0') foreach ($layer in $layers) { $name = "dltss_pwin_${layer}_layer" - $source = Join-Path $PackageRoot "experimental/k/${name}_gfx1201.hsaco" + $source = Join-Path $PackageRoot "experimental/k/${name}_${GpuArch}.hsaco" if (!(Test-Path -LiteralPath $source)) { throw "Native K module missing: $source" } + Assert-D4RCodeObjectTarget $source $GpuArch Copy-Item -LiteralPath $source -Destination (Join-Path $OutputDirectory "$name.hsaco") -Force } & python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $OutputDirectory $DlssDll if ($LASTEXITCODE) { throw "Native manifest generation failed ($LASTEXITCODE)" } -@{architecture='gfx1201'; family='K'; dlssSha256=(Get-FileHash -LiteralPath $DlssDll -Algorithm SHA256).Hash; - validation='synthetic layers passed; actual DLSS capture/reference validation required'; +@{architecture=$GpuArch; family='K'; dlssSha256=(Get-FileHash -LiteralPath $DlssDll -Algorithm SHA256).Hash; + validation='staged only; GPU execution and DLSS capture/reference validation required'; modules=@(Get-ChildItem -LiteralPath $OutputDirectory -Filter '*.hsaco' | Get-FileHash -Algorithm SHA256)} | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'build-info.json') -Encoding UTF8 Write-Host "Experimental native K directory: $OutputDirectory" diff --git a/scripts/windows/stage-native-m.ps1 b/scripts/windows/stage-native-m.ps1 index f348c28..c882b25 100644 --- a/scripts/windows/stage-native-m.ps1 +++ b/scripts/windows/stage-native-m.ps1 @@ -1,24 +1,30 @@ [CmdletBinding()] -param([Parameter(Mandatory=$true)][string]$DlssDll, [string]$PackageRoot, [string]$OutputDirectory) +param([Parameter(Mandatory=$true)][string]$DlssDll, [string]$PackageRoot, [string]$OutputDirectory, [ValidateSet('gfx1200','gfx1201')][string]$GpuArch) $ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'gpu-target.ps1') $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' } -if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo 'build/native-m-gfx1201' } +$GpuArch=Get-D4RGpuTarget $PackageRoot $GpuArch +if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo "build/native-m-$GpuArch" } if (![IO.Path]::IsPathRooted($DlssDll) -or !(Test-Path -LiteralPath $DlssDll)) { throw 'Supply the absolute path to your local nvngx_dlss.dll.' } New-Item -ItemType Directory -Force $OutputDirectory | Out-Null +foreach ($object in Get-ChildItem -LiteralPath $OutputDirectory -Filter '*.hsaco') { + Assert-D4RCodeObjectTarget $object.FullName $GpuArch +} foreach ($layer in @('enc1','enc2','enc3_tube','dec2','dec1')) { $name = "rrlite_${layer}_4x4" - $source = Join-Path $PackageRoot "experimental/m/${name}_gfx1201.hsaco" + $source = Join-Path $PackageRoot "experimental/m/${name}_${GpuArch}.hsaco" if (!(Test-Path -LiteralPath $source)) { throw "Native M module missing: $source" } + Assert-D4RCodeObjectTarget $source $GpuArch Copy-Item -LiteralPath $source -Destination (Join-Path $OutputDirectory "$name.hsaco") -Force } & python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $OutputDirectory $DlssDll if ($LASTEXITCODE) { throw "Native manifest generation failed ($LASTEXITCODE)" } -@{architecture='gfx1201'; family='M'; numerics='SWIN_EXACT; SWIN_EXACT_PV; FP16 widening; native FP8 disabled'; +@{architecture=$GpuArch; family='M'; numerics='SWIN_EXACT; SWIN_EXACT_PV; FP16 widening; native FP8 disabled'; dlssSha256=(Get-FileHash -LiteralPath $DlssDll -Algorithm SHA256).Hash; - validation='synthetic layers passed; actual DLSS capture/reference validation required'; + validation='staged only; GPU execution and DLSS capture/reference validation required'; modules=@(Get-ChildItem -LiteralPath $OutputDirectory -Filter '*.hsaco' | Get-FileHash -Algorithm SHA256)} | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'build-info.json') -Encoding UTF8 Write-Host "Experimental native M directory: $OutputDirectory" diff --git a/scripts/windows/test-async-k.ps1 b/scripts/windows/test-async-k.ps1 index 0ff58c3..8367f82 100644 --- a/scripts/windows/test-async-k.ps1 +++ b/scripts/windows/test-async-k.ps1 @@ -11,12 +11,14 @@ param( [string]$OutputDirectory ) $ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'gpu-target.ps1') $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) if (!$NgxCore) { $NgxCore = Join-Path $repo '_nvngx.dll' } if (!$DlssDll) { $DlssDll = Join-Path $repo 'nvngx_dlss.dll' } -if (!$NativeRoot) { $NativeRoot = Join-Path $repo 'build/native-k-gfx1201' } if (!$ZludaRoot) { $ZludaRoot = Join-Path $repo 'dist/zluda-windows-api-profile' } if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-command-list' } +$GpuArch=Get-D4RGpuTarget $PackageRoot $null +if (!$NativeRoot) { $NativeRoot = Join-Path $repo "build/native-k-$GpuArch" } if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/async-k-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')) } if ($Resolution -notmatch '^([0-9]+)x([0-9]+)$') { throw 'Resolution must be WIDTHxHEIGHT' } $width=$Matches[1]; $height=$Matches[2] @@ -71,7 +73,7 @@ try { Tee-Object -FilePath (Join-Path $OutputDirectory "$mode-comparison.log") if ($LASTEXITCODE) { throw "Async $mode output mismatch" } } - @{passed=$true; architecture='gfx1201'; preset=11; resolution=$Resolution; + @{passed=$true; architecture=$GpuArch; preset=11; resolution=$Resolution; inputResolution=$InputResolution; modes=@('burst','recreate'); framesPerMode=3; exactRgb=$true; gpuBoundaryTiming=[bool]$ProfileGpuBoundary; deferredKernelTiming=[bool]$ProfileKernelsDeferred; batchInputs=[bool]$BatchInputCopies; pixelProfile=$PixelProfile} | diff --git a/scripts/windows/test-deferred-kernel-profile.ps1 b/scripts/windows/test-deferred-kernel-profile.ps1 index b3a8aa6..47735b6 100644 --- a/scripts/windows/test-deferred-kernel-profile.ps1 +++ b/scripts/windows/test-deferred-kernel-profile.ps1 @@ -1,9 +1,11 @@ [CmdletBinding()] param([string]$ZludaRoot, [string]$PackageRoot, [string]$HipRoot, [string]$OutputDirectory) $ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'gpu-target.ps1') $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) if (!$ZludaRoot) { $ZludaRoot = Join-Path $repo 'dist/zluda-windows-deferred-profile' } if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-command-list' } +$GpuArch=Get-D4RGpuTarget $PackageRoot $null if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' } if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/deferred-profile-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')) } New-Item -ItemType Directory -Force $OutputDirectory | Out-Null @@ -64,7 +66,7 @@ try { if ($exitCode) { throw "$name failed ($exitCode); logs: $OutputDirectory" } $out=Get-Content -LiteralPath $stdout -Raw $err=Get-Content -LiteralPath $stderr -Raw - if ($out -notmatch 'PASS CUDA architecture=gfx1201' -or $out -notmatch 'guard_verified=1') { throw "$name did not verify PTX output" } + if ($out -notmatch "PASS CUDA architecture=$GpuArch" -or $out -notmatch 'guard_verified=1') { throw "$name did not verify PTX output" } $records=[regex]::Matches($err, '(?m)^D4R_KERNEL_PROFILE kernel="d4r_ptx_pattern" [^\r\n]*serializing=0 deferred=1 [^\r\n]+') $invalid=[regex]::Matches($err, '(?m)^D4R_KERNEL_PROFILE_INVALID kernel="d4r_ptx_pattern" [^\r\n]*reason=invalid_elapsed_time serializing=0 deferred=1 [^\r\n]+') if ($records.Count + $invalid.Count -ne $iterations -or $err -match 'serializing=1|D4R_KERNEL_PROFILE_SKIPPED') { @@ -89,11 +91,11 @@ try { $arguments=@('--hip-root', ('"' + $HipRoot + '"'), '--cuda-dll', ('"' + (Join-Path $ZludaRoot 'nvcuda.dll') + '"'), '--iterations', '3') $exitCode=RunProbe $arguments $stdout $stderr $out=Get-Content -LiteralPath $stdout -Raw; $err=Get-Content -LiteralPath $stderr -Raw - if ($exitCode -or $out -notmatch 'PASS CUDA architecture=gfx1201' -or + if ($exitCode -or $out -notmatch "PASS CUDA architecture=$GpuArch" -or $err -notmatch 'reason=legacy_default_stream' -or $err -match '(?m)^D4R_KERNEL_PROFILE ') { throw 'Default-stream diagnostics did not safely skip timing events.' } - @{passed=$true; architecture='gfx1201'; cases=$cases; defaultStreamSkipped=$true; addedCompletionWaits=0} | + @{passed=$true; architecture=$GpuArch; cases=$cases; defaultStreamSkipped=$true; addedCompletionWaits=0} | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'validation.json') -Encoding UTF8 Write-Host "PASS deferred kernel profiling: $OutputDirectory" } finally { diff --git a/scripts/windows/test-gpu-target-contract.ps1 b/scripts/windows/test-gpu-target-contract.ps1 new file mode 100644 index 0000000..78e73f3 --- /dev/null +++ b/scripts/windows/test-gpu-target-contract.ps1 @@ -0,0 +1,23 @@ +[CmdletBinding()] +param([Parameter(Mandatory=$true)][string]$PackageRoot, [Parameter(Mandatory=$true)][string]$CodeObject) +$ErrorActionPreference='Stop' +. (Join-Path $PSScriptRoot 'gpu-target.ps1') +$target=Get-D4RGpuTarget $PackageRoot $null +$other=if ($target -eq 'gfx1201') { 'gfx1200' } else { 'gfx1201' } +function Expect-Rejection([scriptblock]$Action) { + $rejected=$false + try { & $Action | Out-Null } catch { $rejected=$true } + if (!$rejected) { throw 'GPU target contract accepted incompatible input.' } +} +if ((Get-D4RGpuTarget $PackageRoot $target) -ne $target) { throw 'Incorrect package target.' } +Assert-D4RCodeObjectTarget $CodeObject $target +Expect-Rejection { Get-D4RGpuTarget $PackageRoot $other } +Expect-Rejection { Get-D4RGpuTarget $PackageRoot 'gfx1250' } +Expect-Rejection { Assert-D4RCodeObjectTarget $CodeObject $other } +Expect-Rejection { Assert-D4RCodeObjectTarget (Join-Path $PackageRoot 'gpu-target.json') $target } +# Old packages had no target metadata and were gfx1201 only. +$legacy=Join-Path $PackageRoot '__no-target-metadata__' +if (Test-Path -LiteralPath (Join-Path $legacy 'gpu-target.json')) { throw 'Unexpected legacy test fixture.' } +if ((Get-D4RGpuTarget $legacy $null) -ne 'gfx1201') { throw 'Legacy target changed.' } +Expect-Rejection { Get-D4RGpuTarget $legacy 'gfx1200' } +Write-Host "PASS GPU_TARGET_SCRIPT target=$target rejected=5 hardware_executed=0" diff --git a/scripts/windows/test-k-captures.ps1 b/scripts/windows/test-k-captures.ps1 index aa70b43..582b563 100644 --- a/scripts/windows/test-k-captures.ps1 +++ b/scripts/windows/test-k-captures.ps1 @@ -3,9 +3,11 @@ param([Parameter(Mandatory=$true)][string]$CaptureDirectory, [string]$HipRoot, [string]$PackageRoot, [string]$OutputDirectory, [int]$Windows = 12, [Parameter(Mandatory=$true)][string]$DlssDll, [int]$PtxWindows = 2) $ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'gpu-target.ps1') $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' } if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' } +$GpuArch=Get-D4RGpuTarget $PackageRoot $null if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/k-real-reference-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) } New-Item -ItemType Directory -Force $OutputDirectory | Out-Null $previousPython = $env:PYTHONPATH @@ -23,7 +25,7 @@ try { New-Item -ItemType Directory -Force $folder | Out-Null $env:D4R_DIAG_DIR = $folder & (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --hip-root $HipRoot ` - --module (Join-Path $PackageRoot "experimental/k/${name}_gfx1201.hsaco") ` + --module (Join-Path $PackageRoot "experimental/k/${name}_${GpuArch}.hsaco") ` --fixture-dir $capture.FullName --output-dir $output --iterations 1 ` > (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log') if ($LASTEXITCODE) { throw "K $layer actual GPU replay failed: $folder" } diff --git a/scripts/windows/test-k-replay.ps1 b/scripts/windows/test-k-replay.ps1 index 2fcfef6..9cf8d40 100644 --- a/scripts/windows/test-k-replay.ps1 +++ b/scripts/windows/test-k-replay.ps1 @@ -7,15 +7,17 @@ param( [int]$Iterations = 32 ) $ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'gpu-target.ps1') $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' } if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' } +$GpuArch=Get-D4RGpuTarget $PackageRoot $null if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/k-replay-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) } New-Item -ItemType Directory -Force $OutputDirectory | Out-Null $previousPython = $env:PYTHONPATH $previousDiag = $env:D4R_DIAG_DIR $env:PYTHONPATH = "$(Join-Path $repo '.tools/python/vendor');$previousPython" -$summary = [ordered]@{utc=[DateTime]::UtcNow.ToString('o'); hipRoot=$HipRoot; layers=@(); passed=$false} +$summary = [ordered]@{utc=[DateTime]::UtcNow.ToString('o'); hipRoot=$HipRoot; architecture=$GpuArch; layers=@(); passed=$false} try { if ($Iterations -lt 1 -or $Iterations -gt 10000) { throw 'Iterations must be 1..10000' } foreach ($layer in $Layers) { @@ -29,7 +31,7 @@ try { > (Join-Path $folder 'fixture.stdout.log') 2> (Join-Path $folder 'fixture.stderr.log') if ($LASTEXITCODE) { throw "K $layer fixture generation failed; see $folder" } & (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --hip-root $HipRoot ` - --module (Join-Path $PackageRoot "experimental/k/dltss_pwin_${layer}_layer_gfx1201.hsaco") ` + --module (Join-Path $PackageRoot "experimental/k/dltss_pwin_${layer}_layer_${GpuArch}.hsaco") ` --fixture-dir $fixture --output-dir $output --iterations $Iterations ` > (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log') if ($LASTEXITCODE) { throw "K $layer GPU replay failed; see $folder" } diff --git a/scripts/windows/test-m-captures.ps1 b/scripts/windows/test-m-captures.ps1 index 1396c7e..258226a 100644 --- a/scripts/windows/test-m-captures.ps1 +++ b/scripts/windows/test-m-captures.ps1 @@ -3,9 +3,11 @@ param([Parameter(Mandatory=$true)][string]$CaptureDirectory, [string]$HipRoot, [string]$PackageRoot, [string]$OutputDirectory, [int]$Blocks = 12, [Parameter(Mandatory=$true)][string]$DlssDll, [int]$PtxBlocks = 2) $ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'gpu-target.ps1') $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' } if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' } +$GpuArch=Get-D4RGpuTarget $PackageRoot $null if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/m-real-reference-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) } New-Item -ItemType Directory -Force $OutputDirectory | Out-Null $previousPython=$env:PYTHONPATH; $previousDiag=$env:D4R_DIAG_DIR @@ -22,7 +24,7 @@ try { New-Item -ItemType Directory -Force $folder | Out-Null $env:D4R_DIAG_DIR = $folder & (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --hip-root $HipRoot ` - --module (Join-Path $PackageRoot "experimental/m/${name}_gfx1201.hsaco") ` + --module (Join-Path $PackageRoot "experimental/m/${name}_${GpuArch}.hsaco") ` --fixture-dir $capture.FullName --output-dir $output --iterations 1 ` > (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log') if ($LASTEXITCODE) { throw "M $layer actual GPU replay failed: $folder" } diff --git a/scripts/windows/test-m-replay.ps1 b/scripts/windows/test-m-replay.ps1 index 9f9f0af..75a79f2 100644 --- a/scripts/windows/test-m-replay.ps1 +++ b/scripts/windows/test-m-replay.ps1 @@ -3,15 +3,17 @@ param([string]$HipRoot, [string]$PackageRoot, [string]$OutputDirectory, [string[]]$Layers = @('enc1','enc2','enc3_tube','dec2','dec1'), [ValidateRange(1,10000)][int]$Iterations = 32) $ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'gpu-target.ps1') $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' } if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' } +$GpuArch=Get-D4RGpuTarget $PackageRoot $null if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/m-replay-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) } New-Item -ItemType Directory -Force $OutputDirectory | Out-Null $previousPython = $env:PYTHONPATH $previousDiag = $env:D4R_DIAG_DIR $env:PYTHONPATH = "$(Join-Path $repo '.tools/python/vendor');$previousPython" -$summary = [ordered]@{utc=[DateTime]::UtcNow.ToString('o'); hipRoot=$HipRoot; layers=@(); passed=$false} +$summary = [ordered]@{utc=[DateTime]::UtcNow.ToString('o'); hipRoot=$HipRoot; architecture=$GpuArch; layers=@(); passed=$false} try { foreach ($layer in $Layers) { if ($layer -notmatch '^(enc[12]|enc3_tube|dec[12])$') { throw "Invalid M layer $layer" } @@ -24,7 +26,7 @@ try { > (Join-Path $folder 'fixture.stdout.log') 2> (Join-Path $folder 'fixture.stderr.log') if ($LASTEXITCODE) { throw "M $layer fixture generation failed: $folder" } & (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --hip-root $HipRoot ` - --module (Join-Path $PackageRoot "experimental/m/rrlite_${layer}_4x4_gfx1201.hsaco") ` + --module (Join-Path $PackageRoot "experimental/m/rrlite_${layer}_4x4_${GpuArch}.hsaco") ` --fixture-dir $fixture --output-dir $output --iterations $Iterations ` > (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log') if ($LASTEXITCODE) { throw "M $layer GPU replay failed: $folder" } diff --git a/scripts/windows/test-native-overrides.ps1 b/scripts/windows/test-native-overrides.ps1 index 5e8ca26..4d20289 100644 --- a/scripts/windows/test-native-overrides.ps1 +++ b/scripts/windows/test-native-overrides.ps1 @@ -1,9 +1,11 @@ [CmdletBinding()] param([string]$HipRoot, [string]$PackageRoot, [string]$ZludaRoot, [string]$OutputDirectory) $ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'gpu-target.ps1') $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' } if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' } +$GpuArch=Get-D4RGpuTarget $PackageRoot $null if (!$ZludaRoot) { $ZludaRoot = Join-Path $repo 'dist/zluda-windows-final' } if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/native-identity-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) } New-Item -ItemType Directory -Force $OutputDirectory | Out-Null @@ -29,7 +31,7 @@ try { $good = Join-Path $OutputDirectory 'matching-identity' New-Item -ItemType Directory -Force $bad,$good | Out-Null foreach ($directory in @($bad,$good)) { - Copy-Item -LiteralPath (Join-Path $PackageRoot 'bin/probe_gfx1201.hsaco') -Destination (Join-Path $directory 'd4r_ptx_pattern.hsaco') -Force + Copy-Item -LiteralPath (Join-Path $PackageRoot "bin/probe_${GpuArch}.hsaco") -Destination (Join-Path $directory 'd4r_ptx_pattern.hsaco') -Force } 'd4r_ptx_pattern 0000000000000000' | Set-Content -LiteralPath (Join-Path $bad 'd4r-kernels.txt') -Encoding ASCII $env:D4R_ZLUDA_NATIVE_DIR = $bad diff --git a/scripts/windows/test-native-texture.ps1 b/scripts/windows/test-native-texture.ps1 index ca64d65..df92b58 100644 --- a/scripts/windows/test-native-texture.ps1 +++ b/scripts/windows/test-native-texture.ps1 @@ -1,27 +1,30 @@ [CmdletBinding()] -param([string]$TextureRoot, [string]$ControlTextureRoot, [string]$CommonTextureRoot, [string]$ZludaRoot, +param([string]$TextureRoot, [string]$ControlTextureRoot, [string]$CommonTextureRoot, [string]$ZludaRoot, [string]$PackageRoot, [string]$OutputDirectory, [int]$Iterations=8, [string]$OutputResolution='512x288', [string]$InputResolution, [switch]$CandidateFirst, [ValidateSet('output','input')][string]$KernelStage='output') $ErrorActionPreference='Stop' +. (Join-Path $PSScriptRoot 'gpu-target.ps1') $repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) -if (!$TextureRoot) { $TextureRoot=Join-Path $repo 'build/private-textures-gfx1201' } +if (!$PackageRoot) { $PackageRoot=Join-Path $repo 'dist/windows-rdna4-command-list' } +$GpuArch=Get-D4RGpuTarget $PackageRoot $null +if (!$TextureRoot) { $TextureRoot=Join-Path $repo "build/private-textures-$GpuArch" } if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' } if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo 'test-results/k-native-texture-reference' } if ($OutputResolution -notmatch '^([0-9]+)x([0-9]+)$') { throw 'OutputResolution must be WIDTHxHEIGHT.' } $outputWidth=[int]$Matches[1]; $outputHeight=[int]$Matches[2] $combined=Join-Path $OutputDirectory 'private-native' New-Item -ItemType Directory -Force $combined | Out-Null -foreach ($directory in @((Join-Path $repo 'build/native-k-gfx1201'),$CommonTextureRoot,$TextureRoot) | Where-Object { $_ }) { +foreach ($directory in @((Join-Path $repo "build/native-k-$GpuArch"),$CommonTextureRoot,$TextureRoot) | Where-Object { $_ }) { Get-ChildItem -LiteralPath $directory -Filter '*.hsaco' | Copy-Item -Destination $combined -Force } & python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $combined (Join-Path $repo 'nvngx_dlss.dll') if ($LASTEXITCODE) { throw 'Combined private manifest failed.' } -$controlNative=Join-Path $repo 'build/native-k-gfx1201' +$controlNative=Join-Path $repo "build/native-k-$GpuArch" if ($ControlTextureRoot -or $CommonTextureRoot) { $controlNative=Join-Path $OutputDirectory 'control-private-native' New-Item -ItemType Directory -Force $controlNative | Out-Null - foreach ($directory in @((Join-Path $repo 'build/native-k-gfx1201'),$CommonTextureRoot,$ControlTextureRoot) | Where-Object { $_ }) { + foreach ($directory in @((Join-Path $repo "build/native-k-$GpuArch"),$CommonTextureRoot,$ControlTextureRoot) | Where-Object { $_ }) { Get-ChildItem -LiteralPath $directory -Filter '*.hsaco' | Copy-Item -Destination $controlNative -Force } & python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $controlNative (Join-Path $repo 'nvngx_dlss.dll') @@ -37,6 +40,7 @@ $stagedObjects=@{} foreach ($stage in @(@{name='control'; root=$controlNative},@{name='candidate'; root=$combined})) { $stagedObjects[$stage.name]=@{} foreach ($object in Get-ChildItem -LiteralPath $stage.root -Filter '*.hsaco') { + Assert-D4RCodeObjectTarget $object.FullName $GpuArch $stagedObjects[$stage.name][$object.Name]=(Get-FileHash -LiteralPath $object.FullName -Algorithm SHA256).Hash.ToLowerInvariant() } } @@ -52,7 +56,7 @@ try { $expectedBackend=if (Test-Path -LiteralPath (Join-Path $env:D4R_ZLUDA_NATIVE_DIR ($kernel + '.hsaco'))) { 'native' } else { 'translated' } if ($mode -eq 'candidate' -and $expectedBackend -ne 'native') { throw "Missing candidate object: $kernel" } $arguments=@{RuntimeProfile='therock'; ZludaRoot=$ZludaRoot; - PackageRoot=(Join-Path $repo 'dist/windows-rdna4-command-list'); NgxCore=(Join-Path $repo '_nvngx.dll'); + PackageRoot=$PackageRoot; NgxCore=(Join-Path $repo '_nvngx.dll'); DlssDll=(Join-Path $repo 'nvngx_dlss.dll'); NgxMode='d3d12'; NgxAbi='project-legacy'; Preset=11; NgxCreateFlags=$flags; NgxOutputResolution=$OutputResolution; NgxOnly=$true; RequireNativeNetwork=$true; CommandListBackend=$true; NgxInputResolution=$InputResolution; @@ -76,7 +80,7 @@ try { & python (Join-Path $repo 'tools/windows/frame_compare.py') --reference $control --actual $candidate --width $outputWidth --height $outputHeight --exact if ($LASTEXITCODE) { throw "Texture flags=$flags full-frame RGB mismatch; preserve baseline." } } - @{passed=$true; architecture='gfx1201'; framesPerVariant=$Iterations; outputResolution=$OutputResolution; createFlags=@(0,11); strictRgb=$true; + @{passed=$true; architecture=$GpuArch; framesPerVariant=$Iterations; outputResolution=$OutputResolution; createFlags=@(0,11); strictRgb=$true; inputResolution=$InputResolution; serializingProfile=$true; candidateFirst=[bool]$CandidateFirst; kernelStage=$KernelStage; controlTextureRoot=$ControlTextureRoot; commonTextureRoot=$CommonTextureRoot; diff --git a/scripts/windows/test-windows-rdna4.ps1 b/scripts/windows/test-windows-rdna4.ps1 index c343d50..6f71bfd 100644 --- a/scripts/windows/test-windows-rdna4.ps1 +++ b/scripts/windows/test-windows-rdna4.ps1 @@ -4,6 +4,7 @@ param( [ValidateSet('stable', 'therock')][string]$RuntimeProfile = 'stable', [string]$ZludaRoot, [string]$PackageRoot, + [ValidateSet('gfx1200','gfx1201')][string]$GpuArch, [string]$OutputDirectory, [string]$NgxCore = $env:D4R_NGX_CORE, [string]$DlssDll = $env:D4R_DLSS_DLL, @@ -26,6 +27,7 @@ param( [int]$TimeoutSeconds = 180 ) $ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'gpu-target.ps1') $repoRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) if ($RuntimeProfile -eq 'therock') { # Works both from scripts/windows and the installed dist/ directory. @@ -40,6 +42,7 @@ if (!$PackageRoot) { else { $PackageRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../../dist/windows-rdna4-diagnostics')) } } $PackageRoot = [IO.Path]::GetFullPath($PackageRoot) +$GpuArch=Get-D4RGpuTarget $PackageRoot $GpuArch if (!$HipRoot) { $HipRoot = 'C:\Program Files\AMD\ROCm\7.2' } $HipRoot = [IO.Path]::GetFullPath($HipRoot) if (!$ZludaRoot) { @@ -68,7 +71,7 @@ foreach ($setting in @('D4R_ZLUDA_WMMA','D4R_ZLUDA_WMMA_FP8','D4R_ZLUDA_WMMA_FP8 } $exitStatus = 1 $ngxRuntimeDirectory = $null -$summary = [ordered]@{utc=[DateTime]::UtcNow.ToString('o'); profile=$RuntimeProfile; hipRoot=$HipRoot; zludaRoot=$ZludaRoot; tests=@()} +$summary = [ordered]@{utc=[DateTime]::UtcNow.ToString('o'); profile=$RuntimeProfile; architecture=$GpuArch; hipRoot=$HipRoot; zludaRoot=$ZludaRoot; tests=@()} function Quote-Argument([string]$Value) { # All generated paths are absolute file/directory paths, with no trailing backslash. if ($Value.Contains('"')) { throw 'Double quotes are not allowed in arguments' } @@ -162,10 +165,10 @@ try { $comgr = Join-Path $HipRoot 'bin/amd_comgr_3.dll' if (!(Test-Path -LiteralPath $comgr)) { $comgr = Join-Path $HipRoot 'bin/amd_comgr.dll' } $files = @((Join-Path $HipRoot 'bin/amdhip64_7.dll'), $comgr, - (Join-Path $ZludaRoot 'nvcuda.dll'), (Join-Path $PackageRoot 'bin/probe_gfx1201.hsaco'), - (Join-Path $PackageRoot 'bin/wmma_gfx1201.hsaco'), - (Join-Path $PackageRoot 'experimental/k/dltss_pwin_enc1_layer_gfx1201.hsaco'), - (Join-Path $PackageRoot 'experimental/k/dltss_pwin_enc2_layer_gfx1201.hsaco')) + (Join-Path $ZludaRoot 'nvcuda.dll'), (Join-Path $PackageRoot "bin/probe_${GpuArch}.hsaco"), + (Join-Path $PackageRoot "bin/wmma_${GpuArch}.hsaco"), + (Join-Path $PackageRoot "experimental/k/dltss_pwin_enc1_layer_${GpuArch}.hsaco"), + (Join-Path $PackageRoot "experimental/k/dltss_pwin_enc2_layer_${GpuArch}.hsaco")) if ($OptiScalerDll) { if ($NgxMode -ne 'd3d12' -or !$CommandListBackend) { throw '-OptiScalerDll requires D3D12 and -CommandListBackend' } if (![IO.Path]::IsPathRooted($OptiScalerDll)) { throw '-OptiScalerDll must be absolute' } @@ -192,12 +195,12 @@ try { $bin = Join-Path $PackageRoot 'bin' $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_${GpuArch}.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") + '--hip-root', $HipRoot, '--module', (Join-Path $bin "wmma_${GpuArch}.hsaco"), '--iterations', "$Iterations") if (!$wmmaOk) { throw 'gfx12 WMMA layout validation failed; refusing to mark K/M readiness.' } $pythonCommand = Get-Command python -ErrorAction SilentlyContinue if (!$pythonCommand) { throw 'Python 3.11+ with NumPy 2.4.6 is needed for K reference checks; run setup-windows-tools.ps1.' } @@ -205,7 +208,7 @@ try { $kFixture = Join-Path $OutputDirectory "k-$layer-fixture" $kModuleOk = Invoke-Probe "k-$layer-gpu" (Join-Path $bin 'd4r_k_module_probe.exe') @( '--hip-root', $HipRoot, - '--module', (Join-Path $PackageRoot "experimental/k/dltss_pwin_${layer}_layer_gfx1201.hsaco"), + '--module', (Join-Path $PackageRoot "experimental/k/dltss_pwin_${layer}_layer_${GpuArch}.hsaco"), '--kernel-name', $layer, '--fixture-dir', $kFixture, '--iterations', "$Iterations") if (!$kModuleOk) { throw "K $layer GPU execution failed; see the k-$layer-gpu logs." } $kReferenceOk = Invoke-Probe "k-$layer-reference" $pythonCommand.Source @( @@ -247,19 +250,19 @@ try { '--hip-root', $HipRoot, '--module', (Join-Path $bin 'd4r_nvngx.dll'), '--interop-mode', 'indirect', '--iterations', "$Iterations") if (!$commandOk) { throw 'D3D12 command-list order/state/lifetime validation failed.' } $pixelsOk = Invoke-Probe 'd3d12-gpu-pixel-formats' (Join-Path $bin 'd4r_d3d12_pixel_probe.exe') @( - '--hip-root', $HipRoot, '--module', (Join-Path $bin 'pixel_convert_gfx1201.hsaco'), '--iterations', '2') + '--hip-root', $HipRoot, '--module', (Join-Path $bin "pixel_convert_${GpuArch}.hsaco"), '--iterations', '2') if (!$pixelsOk) { throw 'D3D12 typed SRV/UAV format conversion validation failed.' } $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') @( - '--hip-root', $HipRoot, '--module', (Join-Path $bin 'probe_gfx1201.hsaco'), '--iterations', "$Iterations") + '--hip-root', $HipRoot, '--module', (Join-Path $bin "probe_${GpuArch}.hsaco"), '--iterations', "$Iterations") if (!$mapOk -or !$interopOk) { foreach ($mode in @('resource', 'import')) { Invoke-Probe "interop-$mode-lifetime" (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @( '--hip-root', $HipRoot, '--interop-mode', $mode, '--iterations', '32') | Out-Null } 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_${GpuArch}.hsaco"), '--iterations', '32') | Out-Null } } if ($mapOk -and $interopOk) { $exitStatus = 0 } @@ -288,12 +291,12 @@ try { # alongside the private user-supplied DLLs for this test. $localShim = Join-Path $ngxRuntimeDirectory 'd4r_nvngx.dll' Copy-Item -LiteralPath (Join-Path $bin 'd4r_nvngx.dll') -Destination $localShim - Copy-Item -LiteralPath (Join-Path $bin 'pixel_convert_gfx1201.hsaco') -Destination $ngxRuntimeDirectory + Copy-Item -LiteralPath (Join-Path $bin "pixel_convert_${GpuArch}.hsaco") -Destination $ngxRuntimeDirectory if ($OptiScalerDll) { $bridgeDirectory = Join-Path $ngxRuntimeDirectory 'd4r' New-Item -ItemType Directory -Path $bridgeDirectory | Out-Null Copy-Item -LiteralPath $localShim -Destination (Join-Path $bridgeDirectory '_nvngx.dll') - Copy-Item -LiteralPath (Join-Path $bin 'pixel_convert_gfx1201.hsaco') -Destination $bridgeDirectory + Copy-Item -LiteralPath (Join-Path $bin "pixel_convert_${GpuArch}.hsaco") -Destination $bridgeDirectory $localShim = Join-Path $ngxRuntimeDirectory 'OptiScaler.dll' Copy-Item -LiteralPath $OptiScalerDll -Destination $localShim $ini = @" diff --git a/scripts/windows/windows-game.ps1 b/scripts/windows/windows-game.ps1 index 21d8b7a..38f83d4 100644 --- a/scripts/windows/windows-game.ps1 +++ b/scripts/windows/windows-game.ps1 @@ -26,6 +26,7 @@ param( [string]$OutputDirectory ) $ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'gpu-target.ps1') if ($ProfileGpuBoundary -and !$AsyncInterop) { throw '-ProfileGpuBoundary requires -AsyncInterop.' } if ($ProfileKernels -and $ProfileKernelsDeferred) { throw 'Choose either serializing or deferred kernel profiling.' } if ($ProfileLegacyStream -and !$ProfileKernelsDeferred) { throw '-ProfileLegacyStream requires -ProfileKernelsDeferred.' } @@ -67,7 +68,9 @@ if (!$NgxCore -or !$DlssDll) { throw 'Pass both local NVIDIA DLL paths with -Ngx $NgxCore = (Get-Item -LiteralPath $NgxCore).FullName $DlssDll = (Get-Item -LiteralPath $DlssDll).FullName $metadata = Get-Content -LiteralPath (Join-Path $package 'package.json') -Raw | ConvertFrom-Json +$GpuArch=Get-D4RGpuTarget $package $metadata.architecture foreach ($file in $metadata.files) { + if ($file.path.EndsWith('.hsaco')) { Assert-D4RCodeObjectTarget (Join-Path $package $file.path) $GpuArch } if ((Get-FileHash -LiteralPath (Join-Path $package $file.path) -Algorithm SHA256).Hash -ne $file.sha256) { throw "Package file changed; rebuild package: $($file.path)" } } if ((Get-FileHash -LiteralPath $DlssDll -Algorithm SHA256).Hash -ne $metadata.dlssSha256) { throw 'DLSS DLL does not match the validated 310.9.1 native manifest. Rebuild and validate native manifests for this DLL first.' } @@ -75,7 +78,7 @@ $textureFiles=@(); $textureManifest=@() if ($LocalTextureKernels) { $LocalTextureKernels=(Get-Item -LiteralPath $LocalTextureKernels -ErrorAction Stop).FullName $validation=Get-Content -LiteralPath (Join-Path $LocalTextureKernels 'validation.json') -Raw | ConvertFrom-Json - if (!$validation.passed -or !$validation.strictRgb -or $validation.architecture -ne 'gfx1201' -or + if (!$validation.passed -or !$validation.strictRgb -or $validation.architecture -ne $GpuArch -or $validation.source.dlssSha256 -ne $metadata.dlssSha256 -or !$validation.source.accuracy) { throw 'Local texture kernels must pass test-native-texture.ps1 with the exact packaged DLSS identity.' } @@ -85,6 +88,7 @@ if ($LocalTextureKernels) { } $path=Join-Path $LocalTextureKernels $object.Name if ((Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash.ToLowerInvariant() -ne $object.Value) { throw 'Local texture object changed after validation.' } + Assert-D4RCodeObjectTarget $path $GpuArch $textureFiles+=Get-Item -LiteralPath $path } if ($textureFiles.Count -ne 2) { throw 'Expected both validated local K output variants.' } @@ -123,7 +127,12 @@ function InstallFile([string]$source, [string]$relative) { $manifest | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath $manifestPath -Encoding UTF8 } InstallFile (Join-Path $package 'OptiScaler.dll') 'dxgi.dll' -foreach ($file in Get-ChildItem -LiteralPath (Join-Path $package 'd4r') -Recurse -File) { InstallFile $file.FullName $file.FullName.Substring($package.Length+1) } +foreach ($file in $metadata.files) { + $relative=$file.path.Replace('/','\') + if ($relative.StartsWith('d4r\',[StringComparison]::OrdinalIgnoreCase)) { + InstallFile (Join-Path $package $relative) $relative + } +} if ($LocalTextureKernels) { foreach ($file in $textureFiles) { InstallFile $file.FullName ('d4r/native/' + $file.Name) } $combinedManifest=Join-Path $backup 'generated-native-manifest.txt' @@ -177,7 +186,7 @@ $settings = @{ D4R_NVCUDA_DLL=(GamePath 'd4r/zluda/nvcuda.dll'); ZLUDA_CUDA_LIB=(GamePath 'd4r/zluda/nvcuda.dll'); D4R_NVAPI_DLL=(GamePath 'd4r/nvapi/nvapi64.dll'); D4R_NVAPI_BACKEND=(GamePath 'd4r/zluda/nvapi64.dll'); D4R_NGX_CORE=(GamePath 'd4r/vendor/_nvngx.dll'); D4R_DLSS_DLL=(GamePath 'd4r/vendor/nvngx_dlss.dll'); - D4R_FORMAT_MODULE=(GamePath 'd4r/pixel_convert_gfx1201.hsaco'); + D4R_FORMAT_MODULE=(GamePath "d4r/pixel_convert_${GpuArch}.hsaco"); D4R_ZLUDA_NATIVE_DIR=(GamePath 'd4r/native'); D4R_D3D12_COMMAND_BACKEND='1'; D4R_ZLUDA_VERBOSE='1'; D4R_ZLUDA_WMMA='1'; D4R_ZLUDA_WMMA_FP8='1'; D4R_ZLUDA_WMMA_FP8_NATIVE='0'; D4R_ZLUDA_WMMA_F16_REFERENCE='1'; # Do not inherit relaxation flags from unrelated experiments. Unset and diff --git a/tools/windows/binary16.h b/tools/windows/binary16.h new file mode 100644 index 0000000..2f7ef52 --- /dev/null +++ b/tools/windows/binary16.h @@ -0,0 +1,51 @@ +#pragma once +// Adapted from realdody/d4r ae530b0 (MIT), realdody . +#include +#include +#include +#include + +namespace d4r::diag::binary16 { +// MSVC has no _Float16 extension. Convert double<->IEEE binary16 explicitly with +// round-to-nearest-even, matching a native _Float16 cast. This probe compares the +// f16-accumulator MMA against a double sum rounded once to half, so the rounding +// must match IEEE 754 binary16 exactly. +inline uint16_t half_bits(double value) { + uint64_t bits; + std::memcpy(&bits, &value, sizeof(bits)); + const uint16_t sign = static_cast((bits >> 63) << 15); + int32_t exponent = static_cast((bits >> 52) & 0x7ff); + uint64_t mantissa = bits & 0x000fffffffffffffULL; + if (exponent == 0x7ff) return static_cast(sign | (mantissa ? 0x7e00 : 0x7c00)); + if (exponent == 0) return sign; // double subnormal rounds to half zero + int32_t unbiased = exponent - 1023; + if (unbiased > 15) return static_cast(sign | 0x7c00); + mantissa |= 0x0010000000000000ULL; // implicit leading one (53-bit significand) + const int shift = unbiased >= -14 ? 42 : 28 - unbiased; + uint64_t rounded = shift >= 64 ? 0 : (mantissa >> shift); + if (shift < 64) { + const uint64_t remainder = mantissa & ((1ULL << shift) - 1); + const uint64_t halfway = 1ULL << (shift - 1); + if (remainder > halfway || (remainder == halfway && (rounded & 1))) ++rounded; + } + if (unbiased >= -14) { + if (rounded >= 0x800) { // rounding overflowed into the exponent + rounded = 0x400; + if (++unbiased > 15) return static_cast(sign | 0x7c00); + } + return static_cast(sign | ((unbiased + 15) << 10) | static_cast(rounded & 0x3ff)); + } + if (rounded >= 0x400) return static_cast(sign | 0x0400); // smallest normal half + return static_cast(sign | static_cast(rounded)); +} +inline double half_value(uint16_t bits) { + const bool negative = (bits & 0x8000u) != 0; + const uint32_t exponent = (bits >> 10) & 0x1fu; + const uint32_t mantissa = bits & 0x3ffu; + double magnitude; + if (exponent == 0) magnitude = std::ldexp(static_cast(mantissa), -24); + else if (exponent == 0x1fu) magnitude = mantissa ? std::numeric_limits::quiet_NaN() : std::numeric_limits::infinity(); + else magnitude = std::ldexp(static_cast(mantissa) / 1024.0 + 1.0, static_cast(exponent) - 15); + return negative ? -magnitude : magnitude; +} +} diff --git a/tools/windows/binary16_probe.cpp b/tools/windows/binary16_probe.cpp new file mode 100644 index 0000000..92bc99b --- /dev/null +++ b/tools/windows/binary16_probe.cpp @@ -0,0 +1,45 @@ +#include "binary16.h" +#include +#include +#include + +static void check(double value, uint16_t expected) { + const auto actual = d4r::diag::binary16::half_bits(value); + if (actual != expected) throw std::runtime_error("binary16 rounding mismatch"); +} +int main() { + using namespace d4r::diag::binary16; + try { + unsigned encodings = 0, boundaries = 0; + for (unsigned bits = 0; bits < 65536; ++bits) { + if ((bits & 0x7c00) == 0x7c00 && (bits & 0x3ff)) continue; + check(half_value(uint16_t(bits)), uint16_t(bits)); + ++encodings; + } + for (unsigned bits = 0; bits < 0x7bff; ++bits) { + const double low = half_value(uint16_t(bits)), high = half_value(uint16_t(bits + 1)); + const double midpoint = (low + high) * 0.5; + const uint16_t tie = uint16_t(bits + (bits & 1)); + check(midpoint, tie); check(-midpoint, uint16_t(tie | 0x8000)); + check(std::nextafter(midpoint, low), uint16_t(bits)); + check(std::nextafter(midpoint, high), uint16_t(bits + 1)); +#if !defined(_MSC_VER) + for (double value : {midpoint, -midpoint, std::nextafter(midpoint, low), std::nextafter(midpoint, high)}) { + const _Float16 native = static_cast<_Float16>(value); + uint16_t expected; std::memcpy(&expected, &native, sizeof(expected)); + check(value, expected); + } +#endif + ++boundaries; + } + check(65519.0, 0x7bff); check(65520.0, 0x7c00); + check(-65520.0, 0xfc00); + check(std::numeric_limits::denorm_min(), 0); + check(-std::numeric_limits::denorm_min(), 0x8000); + check(std::numeric_limits::quiet_NaN(), 0x7e00); + std::printf("PASS BINARY16 encodings=%u rounding_boundaries=%u\n", encodings, boundaries); + return 0; + } catch (const std::exception& e) { + std::fprintf(stderr, "FAIL BINARY16 %s\n", e.what()); return 5; + } +} diff --git a/tools/windows/build_native_texture.py b/tools/windows/build_native_texture.py index 59918dc..67ff26c 100644 --- a/tools/windows/build_native_texture.py +++ b/tools/windows/build_native_texture.py @@ -1,4 +1,4 @@ -"""Build a private gfx1201 texture override from the user's local DLSS DLL. +"""Build a private RDNA4 texture override from the user's local DLSS DLL. The resulting PTX/code object contains NVIDIA code and must remain local. No Wine, shell script, GPU, CUDA SDK or system installation is required. @@ -15,6 +15,14 @@ import subprocess import sys +def require_code_object_target(path, target): + with path.open('rb') as file: + header = file.read(64) + if (len(header) != 64 or header[:6] != b'\x7fELF\x02\x01' or + header[18:20] != b'\xe0\x00' or header[48] != {'gfx1200': 0x48, 'gfx1201': 0x4e}[target]): + raise RuntimeError(f'Code object does not target {target}: {path}') + + def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--dlss-dll', type=pathlib.Path, required=True) @@ -22,6 +30,7 @@ def main(): parser.add_argument('--zluda-root', type=pathlib.Path, required=True) parser.add_argument('--kernel', required=True) parser.add_argument('--output-directory', type=pathlib.Path, required=True) + parser.add_argument('--gpu-arch', choices=['gfx1200', 'gfx1201'], default='gfx1201') parser.add_argument('--shader-mode', choices=['cu', 'wgp'], default='cu') args = parser.parse_args() if not re.fullmatch(r'hiluma_engine_(?:output_depth(?:inv|reg)_mv(?:hi|lo)_(?:hdr|ldr)(?:_max)?|' @@ -30,6 +39,8 @@ def main(): repo = pathlib.Path(__file__).resolve().parents[2] output = args.output_directory.resolve() output.mkdir(parents=True, exist_ok=True) + for existing in output.glob('*.hsaco'): + require_code_object_target(existing, args.gpu_arch) work = output / ('work-' + args.kernel) work.mkdir(exist_ok=True) hip = args.hip_root.resolve() @@ -70,7 +81,7 @@ def main(): raise RuntimeError('PTX recipe made no changes; refuse a no-op override') raw, ir, extra = work / 'raw.bc', work / 'raw.ll', work / 'extra.bc' run('compile-helper', [tools['clang++'], '-x', 'hip', '-std=c++20', '-nogpuinc', '-nogpulib', '-O3', - '-mno-wavefrontsize64', '--offload-device-only', '--offload-arch=gfx1201', + '-mno-wavefrontsize64', '--offload-device-only', '--offload-arch=' + args.gpu_arch, '-fgpu-rdc', '-emit-llvm', '-c', '-Xclang', '-fdenormal-fp-math=dynamic', '-DD4R_ACCURACY', '-DD4R_KERNEL_NAME=' + args.kernel, '-o', raw, repo / 'kernels/tex/sust_only.hip']) @@ -79,12 +90,12 @@ def main(): for line in ir.read_text().splitlines(): if re.search(r'@llvm.used|wchar_size|llvm.module.flags|__hip_cuid', line): continue - line = line.replace('optnone', '').replace('"target-cpu"="gfx1201"', '') + line = line.replace('optnone', '').replace(f'"target-cpu"="{args.gpu_arch}"', '') lines.append(re.sub(r'"target-features"="[^"]+"', '', line)) ir.write_text('\n'.join(lines) + '\n') run('assemble', [tools['llvm-as'], ir, '-o', extra]) env['D4R_ZLUDA_EXTRA_BC'] = str(extra) - run('emit', [emitter, edited, work / 'emitted', 'gfx1201']) + run('emit', [emitter, edited, work / 'emitted', args.gpu_arch]) optimized = (work / 'emitted/opt.ll').read_text() definition = re.search(r'^define amdgpu_kernel .*@' + re.escape(args.kernel) + r'\(.*#(\d+)\s*\{', optimized, re.M) if not definition: @@ -93,9 +104,10 @@ def main(): expected_mode = '-cumode' if args.shader_mode == 'wgp' else '+cumode' if not attributes or expected_mode not in attributes[1]: raise RuntimeError(f'Emitter did not honor {args.shader_mode} mode; build the corresponding ZLUDA patch first') + require_code_object_target(work / 'emitted/module.hsaco', args.gpu_arch) shutil.copyfile(work / 'emitted/module.hsaco', output / (args.kernel + '.hsaco')) run('manifest', [sys.executable, repo / 'kernels/tools/kernel_manifest.py', output, args.dlss_dll]) - metadata = dict(architecture='gfx1201', accuracy=True, nativeFP8=False, shaderMode=args.shader_mode, + metadata = dict(architecture=args.gpu_arch, accuracy=True, nativeFP8=False, shaderMode=args.shader_mode, lastBuiltKernel=args.kernel, lastRecipeRewrites=rewrite_counts, privateNvidiaDerivedCode=True, validation='not yet validated; do not install before comparison', dlssSha256=hashlib.sha256(args.dlss_dll.read_bytes()).hexdigest(), @@ -103,7 +115,7 @@ def main(): compiler=subprocess.check_output([str(tools['clang++']), '--version'], env=env).decode(errors='replace'), objects={p.name: hashlib.sha256(p.read_bytes()).hexdigest() for p in output.glob('*.hsaco')}) (output / 'build-info.json').write_text(json.dumps(metadata, indent=2) + '\n') - print(f'Built PRIVATE gfx1201 texture override: {output}; validation required before use') + print(f'Built PRIVATE {args.gpu_arch} texture override: {output}; validation required before use') if __name__ == '__main__': diff --git a/tools/windows/cuda_driver_probe.cpp b/tools/windows/cuda_driver_probe.cpp index 7153975..1c27191 100644 --- a/tools/windows/cuda_driver_probe.cpp +++ b/tools/windows/cuda_driver_probe.cpp @@ -46,7 +46,7 @@ int main(int argc, char** argv) // Require the real HIP architecture, then match the CUDA device by PCI identity. HipApi hip(args.hip_root); hipDeviceProp_t props{}; - hip.select_gfx1201(args.device, props); + hip.select_architecture(args.device, props); CudaApi api(args.cuda_dll); loaded_modules(); std::printf("STAGE cuInit\n"); @@ -130,7 +130,7 @@ int main(int argc, char** argv) if (primary) api.check(api.cuDevicePrimaryCtxRelease_v2(device), "cuDevicePrimaryCtxRelease_v2"); else api.check(api.cuCtxDestroy_v2(context), "cuCtxDestroy_v2"); cleanup.context = nullptr; - std::printf("PASS CUDA architecture=gfx1201 context=%s iterations=%u guard_verified=1 queued=%u\n", + std::printf("PASS CUDA architecture=%s context=%s iterations=%u guard_verified=1 queued=%u\n", d4r::diag::HipApi::target_arch(), args.context.c_str(), args.iterations, unsigned(queued)); return 0; } catch (const std::exception& e) { diff --git a/tools/windows/cuda_image_probe.cpp b/tools/windows/cuda_image_probe.cpp index f2ea4ce..8e246e0 100644 --- a/tools/windows/cuda_image_probe.cpp +++ b/tools/windows/cuda_image_probe.cpp @@ -8,7 +8,7 @@ int main(int argc, char** argv) { Args args(argc, argv); HipApi hip(args.hip_root); hipDeviceProp_t props{}; - hip.select_gfx1201(args.device, props); + hip.select_architecture(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"); @@ -64,7 +64,7 @@ int main(int argc, char** argv) { } } cuda.check(cuda.cuCtxSynchronize(), "cuCtxSynchronize"); - std::printf("PASS CUDA_IMAGES architecture=gfx1201 iterations=%u descriptor_and_storage=1 pitched_device_copies=1 fp16_fp32=1 async_array_copy=%u\n", + std::printf("PASS CUDA_IMAGES architecture=%s iterations=%u descriptor_and_storage=1 pitched_device_copies=1 fp16_fp32=1 async_array_copy=%u\n", d4r::diag::HipApi::target_arch(), args.iterations, unsigned(std::getenv("D4R_DIAG_ASYNC_ARRAY_COPY") != nullptr)); return 0; } catch (const std::exception& e) { diff --git a/tools/windows/cuda_mma_probe.cpp b/tools/windows/cuda_mma_probe.cpp index 79875bb..09f78ce 100644 --- a/tools/windows/cuda_mma_probe.cpp +++ b/tools/windows/cuda_mma_probe.cpp @@ -1,5 +1,6 @@ #include "hip_api.h" #include "cuda_api.h" +#include "binary16.h" #include #include #include @@ -60,6 +61,10 @@ static constexpr char source[] = R"PTX( } )PTX"; +#if defined(_MSC_VER) +using d4r::diag::binary16::half_bits; +using d4r::diag::binary16::half_value; +#else static uint16_t half_bits(double value) { _Float16 half = static_cast<_Float16>(value); uint16_t bits; @@ -71,6 +76,7 @@ static double half_value(uint16_t bits) { std::memcpy(&half, &bits, sizeof(bits)); return static_cast(half); } +#endif int main(int argc, char** argv) { using namespace d4r::diag; @@ -79,7 +85,7 @@ int main(int argc, char** argv) { Args args(argc, argv); HipApi hip(args.hip_root); hipDeviceProp_t props{}; - hip.select_gfx1201(args.device, props); + hip.select_architecture(args.device, props); CudaApi cuda(args.cuda_dll); cuda.check(cuda.cuInit(0), "cuInit"); CUdevice device = -1; @@ -155,7 +161,7 @@ int main(int argc, char** argv) { } std::printf("MMA_REFERENCE cases=%zu elements=%zu differing=%zu\n", cases, d.size(), mismatch); if (mismatch) return 5; - std::printf("PASS CUDA_F16_MMA architecture=gfx1201 cases=%zu exact_f16=1\n", cases); + std::printf("PASS CUDA_F16_MMA architecture=%s cases=%zu exact_f16=1\n", d4r::diag::HipApi::target_arch(), cases); return 0; } catch (const std::exception& error) { std::fprintf(stderr, "FAIL CUDA_F16_MMA %s\n", error.what()); diff --git a/tools/windows/d3d12_command_hooks.generated.h b/tools/windows/d3d12_command_hooks.generated.h index f9242ec..46227a1 100644 --- a/tools/windows/d3d12_command_hooks.generated.h +++ b/tools/windows/d3d12_command_hooks.generated.h @@ -360,9 +360,9 @@ static void STDMETHODCALLTYPE hook_SOSetTargets(ID3D12GraphicsCommandList* self, return original_SOSetTargets(access.target(), start_slot, view_count, views); } -using Fn_OMSetRenderTargets = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT render_target_descriptor_count, const D3D12_CPU_DESCRIPTOR_HANDLE *render_target_descriptors, WINBOOL single_descriptor_handle, const D3D12_CPU_DESCRIPTOR_HANDLE *depth_stencil_descriptor); +using Fn_OMSetRenderTargets = void(STDMETHODCALLTYPE*)(ID3D12GraphicsCommandList* self, UINT render_target_descriptor_count, const D3D12_CPU_DESCRIPTOR_HANDLE *render_target_descriptors, BOOL single_descriptor_handle, const D3D12_CPU_DESCRIPTOR_HANDLE *depth_stencil_descriptor); static Fn_OMSetRenderTargets original_OMSetRenderTargets = nullptr; -static void STDMETHODCALLTYPE hook_OMSetRenderTargets(ID3D12GraphicsCommandList* self, UINT render_target_descriptor_count, const D3D12_CPU_DESCRIPTOR_HANDLE *render_target_descriptors, WINBOOL single_descriptor_handle, const D3D12_CPU_DESCRIPTOR_HANDLE *depth_stencil_descriptor) { +static void STDMETHODCALLTYPE hook_OMSetRenderTargets(ID3D12GraphicsCommandList* self, UINT render_target_descriptor_count, const D3D12_CPU_DESCRIPTOR_HANDLE *render_target_descriptors, BOOL single_descriptor_handle, const D3D12_CPU_DESCRIPTOR_HANDLE *depth_stencil_descriptor) { Access access(self); auto saved_render_target_descriptors = copy_values(render_target_descriptors, (single_descriptor_handle ? 1 : render_target_descriptor_count)); auto saved_depth_stencil_descriptor = copy_values(depth_stencil_descriptor, 1); diff --git a/tools/windows/d3d12_command_probe.cpp b/tools/windows/d3d12_command_probe.cpp index 74c7aa9..07a5446 100644 --- a/tools/windows/d3d12_command_probe.cpp +++ b/tools/windows/d3d12_command_probe.cpp @@ -123,7 +123,7 @@ int main(int argc, char** argv) { std::unique_ptr hip; hipDeviceProp_t props{}; const bool no_hip = std::getenv("D4R_COMMAND_PROBE_NO_HIP") != nullptr; - if (!no_hip) { hip = std::make_unique(args.hip_root); hip->select_gfx1201(args.device, props); } + if (!no_hip) { hip = std::make_unique(args.hip_root); hip->select_architecture(args.device, props); } ComPtr factory; dx(CreateDXGIFactory1(IID_PPV_ARGS(factory.GetAddressOf())), "Probe DXGI"); ComPtr adapter; for (UINT i = 0; ; ++i) { @@ -295,7 +295,7 @@ int main(int argc, char** argv) { } const bool warp = std::getenv("D4R_COMMAND_PROBE_WARP") != nullptr; std::printf("PASS D3D12_COMMAND_BACKEND adapter=%s architecture=%s backend=%u iterations=%u batch_order=1 root_state=1 deep_copy=1 indirect=%u indirect_reset=%u reset_reuse=%u live_recordings=0 frame_age=0\n", - warp ? "WARP" : "AMD", warp ? "software" : no_hip ? "not_queried" : "gfx1201", !no_hooks, + warp ? "WARP" : "AMD", warp ? "software" : no_hip ? "not_queried" : HipApi::target_arch(), !no_hooks, args.iterations, indirect_enabled, root_indirect, no_hooks ? 0 : 2 * args.iterations); return 0; } catch (const std::exception& failure) { diff --git a/tools/windows/d3d12_dlss_probe.cpp b/tools/windows/d3d12_dlss_probe.cpp index 8d37b60..6fcb5de 100644 --- a/tools/windows/d3d12_dlss_probe.cpp +++ b/tools/windows/d3d12_dlss_probe.cpp @@ -30,7 +30,7 @@ int main(int argc, char** argv) { std::make_pair("D4R_NVCUDA_DLL", args.cuda_dll), std::make_pair("D4R_NVAPI_DLL", args.nvapi_dll), std::make_pair("D4R_NGX_CORE", args.ngx_core), std::make_pair("D4R_DLSS_DLL", args.dlss_dll)}) if (_putenv_s(pair.first, pair.second.c_str())) throw std::runtime_error("Cannot set runtime environment"); - HipApi hip(args.hip_root); hipDeviceProp_t props{}; hip.select_gfx1201(args.device, props); + HipApi hip(args.hip_root); hipDeviceProp_t props{}; hip.select_architecture(args.device, props); ComPtr factory; dx(CreateDXGIFactory1(IID_PPV_ARGS(factory.GetAddressOf())), "CreateDXGIFactory1"); ComPtr adapter; bool found = false; @@ -169,7 +169,7 @@ int main(int argc, char** argv) { if (!matches) throw std::runtime_error("Imported D3D12 VRAM to CUDA array mismatch on plane " + std::to_string(i)); timeline.drain(queue.Get()); } - std::printf("PASS D3D12_ARRAY_COPY architecture=gfx1201 planes=5 cpu_copies_between_apis=0\n"); + std::printf("PASS D3D12_ARRAY_COPY architecture=%s planes=5 cpu_copies_between_apis=0\n", d4r::diag::HipApi::target_arch()); return 0; } Library shim(wide(args.module)); @@ -398,7 +398,7 @@ int main(int argc, char** argv) { std::printf("D4R_BURST_VALIDATION frames=%zu cpu_readback_after_all_submissions=1\n", submitted.size()); } loaded_modules(); - std::printf("PASS D3D12_DLSS architecture=gfx1201 preset=%u frames=%u fast_path_cpu_copies=0 frame_age=0 enhanced=%u queue_integration=%s\n", + std::printf("PASS D3D12_DLSS architecture=%s preset=%u frames=%u fast_path_cpu_copies=0 frame_age=0 enhanced=%u queue_integration=%s\n", d4r::diag::HipApi::target_arch(), args.preset, args.iterations, enhanced, commandBackend ? "recorded_command_list" : "explicit_harness"); return 0; } catch (const std::exception& error) { std::fprintf(stderr, "FAIL D3D12_DLSS %s\n", error.what()); loaded_modules(); return 4; } diff --git a/tools/windows/d3d12_hip_interop_probe.cpp b/tools/windows/d3d12_hip_interop_probe.cpp index 14d289d..f2f312e 100644 --- a/tools/windows/d3d12_hip_interop_probe.cpp +++ b/tools/windows/d3d12_hip_interop_probe.cpp @@ -137,7 +137,7 @@ int main(int argc, char** argv) throw std::runtime_error("--stream-lifetime must be persistent or cycle"); HipApi hip(args.hip_root); hipDeviceProp_t props{}; - hip.select_gfx1201(args.device, props); + hip.select_architecture(args.device, props); ExternalApi external{hip}; ComPtr factory; dx(CreateDXGIFactory1(IID_PPV_ARGS(factory.GetAddressOf())), "CreateDXGIFactory1"); @@ -336,7 +336,7 @@ int main(int argc, char** argv) DWORD handles_after = 0; GetProcessHandleCount(GetCurrentProcess(), &handles_after); std::printf("HANDLES before=%lu after=%lu\n", handles_before, handles_after); if (final_cycle_handles > warmed_handles + 4) throw std::runtime_error("Handle count grew after first-cycle warmup"); - std::printf("PASS INTEROP architecture=gfx1201 iterations=%u texture=R32_UINT external_fence=1\n", args.iterations); + std::printf("PASS INTEROP architecture=%s iterations=%u texture=R32_UINT external_fence=1\n", d4r::diag::HipApi::target_arch(), args.iterations); return 0; } catch (const std::exception& e) { std::fprintf(stderr, "FAIL INTEROP %s\n", e.what()); diff --git a/tools/windows/d3d12_pixel_probe.cpp b/tools/windows/d3d12_pixel_probe.cpp index 6870999..0b40bab 100644 --- a/tools/windows/d3d12_pixel_probe.cpp +++ b/tools/windows/d3d12_pixel_probe.cpp @@ -170,7 +170,7 @@ std::vector read_reference(ID3D12Device* device, ID3D12CommandQueue* q int main(int argc, char** argv) { start(); try { - Args args(argc, argv); HipApi hip(args.hip_root); hipDeviceProp_t props{}; hip.select_gfx1201(args.device, props); + Args args(argc, argv); HipApi hip(args.hip_root); hipDeviceProp_t props{}; hip.select_architecture(args.device, props); ExternalApi external{hip}; PixelProgram program(hip, wide(args.module)); { PixelAllocation diagnostic(hip,3,1,8); @@ -259,6 +259,6 @@ int main(int argc, char** argv) { } ++verified; std::printf("PASS PIXEL_FORMAT name=%s plane=%u pixels=%u decode_max_abs=%.9g encode=%u\n", item.name,item.plane,count,maxAbs,item.plane==0); } - std::printf("PASS D3D12_GPU_FORMATS architecture=gfx1201 cases=%u iterations=%u width=%u height=%u enhanced=%u typed_srv_reference=1 typed_uav_reference=1\n", verified,args.iterations,width,height,enhanced); return 0; + std::printf("PASS D3D12_GPU_FORMATS architecture=%s cases=%u iterations=%u width=%u height=%u enhanced=%u typed_srv_reference=1 typed_uav_reference=1\n", d4r::diag::HipApi::target_arch(), verified,args.iterations,width,height,enhanced); return 0; } catch (const std::exception& error) { std::fprintf(stderr,"FAIL D3D12_GPU_FORMATS %s\n",error.what()); loaded_modules(); return 1; } } diff --git a/tools/windows/d4r_nvngx_windows.cpp b/tools/windows/d4r_nvngx_windows.cpp index 157fc3e..bf99083 100644 --- a/tools/windows/d4r_nvngx_windows.cpp +++ b/tools/windows/d4r_nvngx_windows.cpp @@ -87,10 +87,10 @@ API unsigned NVSDK_NGX_D3D12_GetFeatureRequirements(IDXGIAdapter* adapter, const DXGI_ADAPTER_DESC desc{}; d4r::win::dx(adapter->GetDesc(&desc), "NGX requirements adapter"); // OptiScaler can override the DXGI vendor ID. The physical HIP // architecture and full adapter LUID are the authoritative identity. - d4r::diag::HipApi hip(d4r::win::env_path("D4R_HIP_ROOT")); hipDeviceProp_t props{}; hip.select_gfx1201(-1,props); + d4r::diag::HipApi hip(d4r::win::env_path("D4R_HIP_ROOT")); hipDeviceProp_t props{}; hip.select_architecture(-1,props); requirements->FeatureSupported = std::memcmp(&desc.AdapterLuid,props.luid,sizeof(LUID)) ? 4u : 0u; std::strcpy(requirements->MinOSVersion,"10.0.22000.0"); - std::printf("D4R_REQUIREMENTS feature=%u support=%u architecture=gfx1201\n",info->FeatureID,requirements->FeatureSupported); + std::printf("D4R_REQUIREMENTS feature=%u support=%u architecture=%s\n",info->FeatureID,requirements->FeatureSupported,d4r::diag::HipApi::target_arch()); return 1u; }); } diff --git a/tools/windows/generate_d3d12_hooks.py b/tools/windows/generate_d3d12_hooks.py index 4d25db3..e356cf6 100644 --- a/tools/windows/generate_d3d12_hooks.py +++ b/tools/windows/generate_d3d12_hooks.py @@ -52,8 +52,9 @@ def generate(source): slot = 9 # IUnknown(3), ID3D12Object(4), DeviceChild(1), CommandList(1). for version, interface, methods in sorted(classes): for result, name, arguments in methods: - result = ' '.join(result.split()) - arguments = ' '.join(arguments.split()) + # MinGW WINBOOL and Microsoft SDK BOOL have the same ABI. + result = ' '.join(result.split()).replace('WINBOOL', 'BOOL') + arguments = ' '.join(arguments.split()).replace('WINBOOL', 'BOOL') declarations = [arg.strip() for arg in arguments.split(',')] if arguments else [] names = [] for declaration in declarations: diff --git a/tools/windows/gfx12_wmma_probe.cpp b/tools/windows/gfx12_wmma_probe.cpp index 9781344..db22c63 100644 --- a/tools/windows/gfx12_wmma_probe.cpp +++ b/tools/windows/gfx12_wmma_probe.cpp @@ -30,7 +30,7 @@ int main(int argc, char** argv) if (args.module.empty()) throw std::runtime_error("--module is required"); HipApi hip(args.hip_root); hipDeviceProp_t properties{}; - hip.select_gfx1201(args.device, properties); + hip.select_architecture(args.device, properties); hipModule_t module = nullptr; hip.check(hip.hipModuleLoad(&module, args.module.c_str()), "hipModuleLoad(WMMA)"); struct ModuleCleanup { @@ -120,7 +120,7 @@ int main(int argc, char** argv) } } } - std::printf("PASS GFX12_WMMA architecture=gfx1201 modes=raw,legacy_adapter,upstream_layout iterations=%u max_abs=%.9g max_rel=%.9g\n", + std::printf("PASS GFX12_WMMA architecture=%s modes=raw,legacy_adapter,upstream_layout iterations=%u max_abs=%.9g max_rel=%.9g\n", d4r::diag::HipApi::target_arch(), args.iterations, max_absolute_error, max_relative_error); return 0; } catch (const std::exception& error) { diff --git a/tools/windows/gpu-target.json.in b/tools/windows/gpu-target.json.in new file mode 100644 index 0000000..2757311 --- /dev/null +++ b/tools/windows/gpu-target.json.in @@ -0,0 +1,6 @@ +{ + "schema": 1, + "architecture": "@D4R_GPU_ARCH@", + "wavefrontSize": 32, + "validation": "Compilation is not hardware validation; run the GPU diagnostics." +} diff --git a/tools/windows/gpu_target.h b/tools/windows/gpu_target.h new file mode 100644 index 0000000..3539640 --- /dev/null +++ b/tools/windows/gpu_target.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include +#include +#include +#include + +#ifndef D4R_TARGET_ARCH +#define D4R_TARGET_ARCH "gfx1201" +#endif + +namespace d4r::diag { +inline bool supported_gpu_target(const std::string& target) { + return target == "gfx1200" || target == "gfx1201"; +} +inline std::string gpu_architecture(const std::string& name) { + return name.substr(0, name.find(':')); +} +inline void require_gpu_target(const std::string& actual, const std::string& expected) { + if (!supported_gpu_target(expected) || gpu_architecture(actual) != expected) + throw std::runtime_error("GPU architecture mismatch: detected " + actual + ", package requires " + expected); +} +inline std::string code_object_target(const std::filesystem::path& path) { + // AMDGPU ELF e_flags machine IDs from LLVM's documented code-object ABI. + // Never infer compatibility from a filename or rewrite the ELF target. + std::ifstream file(path, std::ios::binary); + std::array header{}; + if (!file.read(reinterpret_cast(header.data()), header.size()) || + header[0] != 0x7f || header[1] != 'E' || header[2] != 'L' || header[3] != 'F' || + header[4] != 2 || header[5] != 1 || header[18] != 0xe0 || header[19] != 0) + throw std::runtime_error("Invalid AMDGPU ELF64 code object: " + path.string()); + switch (header[48]) { + case 0x48: return "gfx1200"; + case 0x4e: return "gfx1201"; + default: throw std::runtime_error("Unsupported AMDGPU code-object target: " + path.string()); + } +} +inline void require_code_object_target(const std::filesystem::path& path, const std::string& expected) { + require_gpu_target(code_object_target(path), expected); +} +} diff --git a/tools/windows/gpu_target_probe.cpp b/tools/windows/gpu_target_probe.cpp new file mode 100644 index 0000000..ffcdf6c --- /dev/null +++ b/tools/windows/gpu_target_probe.cpp @@ -0,0 +1,31 @@ +#include "gpu_target.h" +#include + +int main(int argc, char** argv) { + using namespace d4r::diag; + try { + unsigned rejected = 0, objects = 0; + for (const char* target : {"gfx1200", "gfx1201"}) { + require_gpu_target(target, target); + require_gpu_target(std::string(target) + ":xnack-:sramecc-", target); + for (const char* invalid : {"gfx1101", "gfx12000", "gfx1202", "gfx1250", ""}) { + try { require_gpu_target(invalid, target); } + catch (const std::exception&) { ++rejected; continue; } + throw std::runtime_error("Invalid GPU target accepted"); + } + } + for (int i = 1; i < argc; ++i) { + require_code_object_target(argv[i], D4R_TARGET_ARCH); + const auto other = std::string(D4R_TARGET_ARCH) == "gfx1201" ? "gfx1200" : "gfx1201"; + try { require_code_object_target(argv[i], other); } + catch (const std::exception&) { ++rejected; ++objects; continue; } + throw std::runtime_error("Wrong code-object target accepted"); + } + if (!objects) throw std::runtime_error("Supply actual compiled code objects"); + std::printf("PASS GPU_TARGET_CONTRACT target=%s objects=%u rejected=%u hardware_executed=0\n", + D4R_TARGET_ARCH, objects, rejected); + return 0; + } catch (const std::exception& e) { + std::fprintf(stderr, "FAIL GPU_TARGET_CONTRACT %s\n", e.what()); return 5; + } +} diff --git a/tools/windows/hip_api.h b/tools/windows/hip_api.h index 4437e49..bc32088 100644 --- a/tools/windows/hip_api.h +++ b/tools/windows/hip_api.h @@ -1,5 +1,6 @@ #pragma once #include "diagnostic.h" +#include "gpu_target.h" #include namespace d4r::diag { @@ -40,7 +41,8 @@ struct HipApi { if (verbose || result != hipSuccess) std::printf("HIP %s -> %d (%s)\n", call, static_cast(result), hipGetErrorName(result)); if (result != hipSuccess) throw std::runtime_error(std::string(call) + ": " + hipGetErrorString(result)); } - int select_gfx1201(int requested, hipDeviceProp_t& selected) const { + static const char* target_arch() { return D4R_TARGET_ARCH; } + int select_architecture(int requested, hipDeviceProp_t& selected) const { // Architecture spoofing invalidates a gfx12 correctness test. for (const char* name : {"HSA_OVERRIDE_GFX_VERSION", "HSA_OVERRIDE_GFX_VERSION_0"}) if (std::getenv(name)) throw std::runtime_error(std::string("Unset architecture override ") + name); @@ -59,14 +61,16 @@ struct HipApi { i, props.name, props.gcnArchName, props.warpSize, props.totalGlobalMem, props.pciDomainID, props.pciBusID, props.pciDeviceID); const std::string arch = props.gcnArchName; - if (arch.substr(0, arch.find(':')) == "gfx1201" && (requested < 0 || requested == i) && found < 0) { + if (gpu_architecture(arch) == target_arch() && (requested < 0 || requested == i) && found < 0) { found = i; selected = props; } } - if (found < 0) throw std::runtime_error("No selected gfx1201 device; never substitute gfx110x"); + if (found < 0) throw std::runtime_error(std::string("No selected ") + target_arch() + " device; refusing architecture substitution"); + require_gpu_target(selected.gcnArchName, target_arch()); check(hipSetDevice(found), "hipSetDevice"); - std::printf("SELECTED HIP ordinal=%d architecture=gfx1201\n", found); + std::printf("SELECTED HIP ordinal=%d architecture=%s validation=%s\n", found, target_arch(), + std::string(target_arch()) == "gfx1201" ? "previously_tested_target" : "hardware_unverified_target"); return found; } }; diff --git a/tools/windows/hip_gfx1201_probe.cpp b/tools/windows/hip_gfx1201_probe.cpp index 3cd5b9c..2af106e 100644 --- a/tools/windows/hip_gfx1201_probe.cpp +++ b/tools/windows/hip_gfx1201_probe.cpp @@ -9,13 +9,13 @@ int main(int argc, char** argv) if (args.module.empty()) throw std::runtime_error("--module is required"); HipApi api(args.hip_root); hipDeviceProp_t props{}; - api.select_gfx1201(args.device, props); + api.select_architecture(args.device, props); loaded_modules(); size_t free = 0, total = 0; api.check(api.hipMemGetInfo(&free, &total), "hipMemGetInfo"); std::printf("VRAM free=%zu total=%zu\n", free, total); hipModule_t module = nullptr; - api.check(api.hipModuleLoad(&module, args.module.c_str()), "hipModuleLoad(gfx1201)"); + api.check(api.hipModuleLoad(&module, args.module.c_str()), "hipModuleLoad(target)"); struct ModuleCleanup { HipApi& api; hipModule_t m; ~ModuleCleanup() { if (m) (void)api.hipModuleUnload(m); } } cleanup{api, module}; hipFunction_t kernel = nullptr; api.check(api.hipModuleGetFunction(&kernel, module, "d4r_probe_pattern"), "hipModuleGetFunction"); @@ -41,7 +41,7 @@ int main(int argc, char** argv) allocation_cleanup.p = nullptr; api.check(api.hipModuleUnload(module), "hipModuleUnload"); cleanup.m = nullptr; - std::printf("PASS HIP architecture=gfx1201 iterations=%u guard_verified=1\n", args.iterations); + std::printf("PASS HIP architecture=%s iterations=%u guard_verified=1\n", d4r::diag::HipApi::target_arch(), args.iterations); return 0; } catch (const std::exception& e) { std::fprintf(stderr, "FAIL HIP %s\n", e.what()); diff --git a/tools/windows/hip_stream_lifecycle_probe.cpp b/tools/windows/hip_stream_lifecycle_probe.cpp index 1d14cb6..b6e59e8 100644 --- a/tools/windows/hip_stream_lifecycle_probe.cpp +++ b/tools/windows/hip_stream_lifecycle_probe.cpp @@ -9,7 +9,7 @@ int main(int argc, char** argv) Args args(argc, argv); HipApi hip(args.hip_root); hipDeviceProp_t props{}; - hip.select_gfx1201(args.device, props); + hip.select_architecture(args.device, props); hipModule_t module = nullptr; hipFunction_t kernel = nullptr; void* output = nullptr; diff --git a/tools/windows/k_layer_validate.py b/tools/windows/k_layer_validate.py index 2d51fbc..88d6c32 100644 --- a/tools/windows/k_layer_validate.py +++ b/tools/windows/k_layer_validate.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Compare real gfx1201 K enc1/enc2 launches with the existing numpy reference.""" +"""Compare RDNA4 K enc1/enc2 launches with the existing numpy reference.""" import argparse import math import pathlib @@ -73,7 +73,7 @@ def main(): # after each K stage has been compared on captured DLSS weights/activations. if full_abs > 0.001 or full_psnr < 60.0 or merged_abs > 0.001 or merged_psnr < 60.0: raise RuntimeError(f"K {args.kernel_name} numerical mismatch against pwin_model.py") - print(f"PASS K_REFERENCE architecture=gfx1201 kernel={args.kernel_name} " + print(f"PASS K_REFERENCE reference_layout=gfx12 kernel={args.kernel_name} " "nonzero_weights=1 transformer_executed=1 nan_inf=0") diff --git a/tools/windows/k_module_probe.cpp b/tools/windows/k_module_probe.cpp index b6d3012..cfade7c 100644 --- a/tools/windows/k_module_probe.cpp +++ b/tools/windows/k_module_probe.cpp @@ -42,7 +42,7 @@ int main(int argc, char** argv) if (args.module.empty()) throw std::runtime_error("--module is required"); HipApi hip(args.hip_root); hipDeviceProp_t properties{}; - hip.select_gfx1201(args.device, properties); + hip.select_architecture(args.device, properties); if (args.kernel_name != "enc1" && args.kernel_name != "enc2") throw std::runtime_error("--kernel-name must be enc1 or enc2"); const bool enc2 = args.kernel_name == "enc2"; @@ -124,7 +124,7 @@ int main(int argc, char** argv) return 5; } } - std::printf("PASS K_IDENTITY architecture=gfx1201 kernel=%s iterations=%u full_elements=%zu merged_elements=%zu transformer_executed=1 nonzero_weights=0\n", + std::printf("PASS K_IDENTITY architecture=%s kernel=%s iterations=%u full_elements=%zu merged_elements=%zu transformer_executed=1 nonzero_weights=0\n", d4r::diag::HipApi::target_arch(), args.kernel_name.c_str(), args.iterations, full_elements, merged_elements); // Exercise nonzero V projection, position-only attention and Wo GEMMs. @@ -213,7 +213,7 @@ int main(int argc, char** argv) save("full.bin", full.data(), full.size() * sizeof(uint16_t)); save("merged.bin", merged.data(), merged.size() * sizeof(uint16_t)); } - std::printf("PASS K_NONZERO architecture=gfx1201 kernel=%s changed_elements=%zu full_elements=%zu merged_elements=%zu fixture_saved=%d numerical_reference_pending=1\n", + std::printf("PASS K_NONZERO architecture=%s kernel=%s changed_elements=%zu full_elements=%zu merged_elements=%zu fixture_saved=%d numerical_reference_pending=1\n", d4r::diag::HipApi::target_arch(), args.kernel_name.c_str(), changed, full.size(), merged.size(), !args.fixture_dir.empty()); return 0; } catch (const std::exception& error) { diff --git a/tools/windows/k_replay_validate.py b/tools/windows/k_replay_validate.py index 348ff17..2b6563a 100644 --- a/tools/windows/k_replay_validate.py +++ b/tools/windows/k_replay_validate.py @@ -139,7 +139,7 @@ def validate(layer, directory, output, window_count=1, real_capture=False): if maximum > absolute_limit or psnr < 60: worst = int(difference.argmax()) raise RuntimeError(f'K mismatch at {worst}: reference={expected[worst]}, gpu={actual[worst]}') - print(f'PASS K_REPLAY_REFERENCE kernel={layer} architecture=gfx1201 nan_inf=0') + print(f'PASS K_REPLAY_REFERENCE kernel={layer} reference_layout=gfx12 nan_inf=0') if __name__ == '__main__': diff --git a/tools/windows/m_replay_validate.py b/tools/windows/m_replay_validate.py index adc54cd..991d786 100644 --- a/tools/windows/m_replay_validate.py +++ b/tools/windows/m_replay_validate.py @@ -153,7 +153,7 @@ def validate(layer, directory, output, block_count, real_capture, exact=False): maximum, peak, psnr, steps = metrics(expected, actual, f'M_REPLAY_REFERENCE kernel={layer}') if ((exact or not real_capture) and maximum != 0) or (real_capture and (steps.max() > 1 or psnr < 50)): raise RuntimeError(f'M mismatch: exact synthetic / <=1 FP8 step and >=50 dB real gate; peak={peak}') - print(f'PASS M_REPLAY_REFERENCE kernel={layer} blocks={len(pairs)} architecture=gfx1201 nan_inf=0') + print(f'PASS M_REPLAY_REFERENCE kernel={layer} blocks={len(pairs)} reference_layout=gfx12 nan_inf=0') if __name__ == '__main__': diff --git a/tools/windows/native_replay_probe.cpp b/tools/windows/native_replay_probe.cpp index aae1a38..ddc847d 100644 --- a/tools/windows/native_replay_probe.cpp +++ b/tools/windows/native_replay_probe.cpp @@ -37,7 +37,7 @@ int main(int argc, char** argv) { throw std::runtime_error("--module, --fixture-dir and --output-dir are required"); HipApi hip(args.hip_root); hipDeviceProp_t props{}; - hip.select_gfx1201(args.device, props); + hip.select_architecture(args.device, props); const std::filesystem::path input(wide(args.fixture_dir)), output(wide(args.output_dir)); auto parameters = read_file(input / L"args.bin"); if (parameters.empty() || parameters.size() > 4096) throw std::runtime_error("Invalid replay parameter size"); @@ -84,7 +84,7 @@ int main(int argc, char** argv) { std::memcpy(parameters.data() + offset, &pointer, 8); } hipModule_t module = nullptr; - hip.check(hip.hipModuleLoad(&module, args.module.c_str()), "hipModuleLoad(replay gfx1201)"); + hip.check(hip.hipModuleLoad(&module, args.module.c_str()), "hipModuleLoad(replay)"); struct Cleanup { HipApi& hip; hipModule_t module; ~Cleanup() { (void)hip.hipModuleUnload(module); } } cleanup{hip, module}; if (const char* debugBlock = std::getenv("D4R_SWIN_DEBUG_BLOCK")) { int coordinates[2]{}; @@ -240,7 +240,7 @@ int main(int argc, char** argv) { file.write(reinterpret_cast(bytes.data()), std::streamsize(bytes.size())); if (!file) throw std::runtime_error("Cannot save replay allocation"); } - std::printf("PASS NATIVE_REPLAY architecture=gfx1201 kernel=%s iterations=%u benchmark_pairs=%u numerical_reference=pending\n", + std::printf("PASS NATIVE_REPLAY architecture=%s kernel=%s iterations=%u benchmark_pairs=%u numerical_reference=pending\n", d4r::diag::HipApi::target_arch(), kernel.c_str(), args.benchmark_module.empty() ? args.iterations : 1, args.benchmark_module.empty() ? 0 : args.iterations); return 0; } catch (const std::exception& error) { diff --git a/tools/windows/ngx_cuda_init_probe.cpp b/tools/windows/ngx_cuda_init_probe.cpp index a5fffe5..da3d8bb 100644 --- a/tools/windows/ngx_cuda_init_probe.cpp +++ b/tools/windows/ngx_cuda_init_probe.cpp @@ -34,7 +34,7 @@ int main(int argc, char** argv) if (dlss_path.filename() != L"nvngx_dlss.dll") throw std::runtime_error("DLSS file must be named nvngx_dlss.dll"); HipApi hip(args.hip_root); hipDeviceProp_t props{}; - hip.select_gfx1201(args.device, props); + hip.select_architecture(args.device, props); SearchDirectory cuda_search(std::filesystem::path(wide(args.cuda_dll)).parent_path()); CudaApi cuda(args.cuda_dll); cuda.check(cuda.cuInit(0), "cuInit"); @@ -124,7 +124,7 @@ int main(int argc, char** argv) cuda.check(cuda.cuCtxSynchronize(), "cuCtxSynchronize"); cuda.check(cuda.cuCtxSetCurrent(nullptr), "cuCtxSetCurrent(NULL)"); cuda.check(cuda.cuDevicePrimaryCtxRelease_v2(device), "cuDevicePrimaryCtxRelease_v2"); context = nullptr; - std::printf("PASS NGX_INIT architecture=gfx1201 sr_available=1 transformer_validation=pending\n"); + std::printf("PASS NGX_INIT architecture=%s sr_available=1 transformer_validation=pending\n", d4r::diag::HipApi::target_arch()); return 0; } catch (const std::exception& e) { std::fprintf(stderr, "FAIL NGX_INIT %s\n", e.what()); diff --git a/tools/windows/ngx_windows_runtime.h b/tools/windows/ngx_windows_runtime.h index 22463f9..d6acced 100644 --- a/tools/windows/ngx_windows_runtime.h +++ b/tools/windows/ngx_windows_runtime.h @@ -42,7 +42,7 @@ inline std::filesystem::path pixel_module_path() { reinterpret_cast(&pixel_module_path), &module)) throw std::runtime_error("Locate GPU conversion module directory"); wchar_t path[32768]{}; const DWORD length = GetModuleFileNameW(module, path, 32768); if (!length || length == 32768) throw std::runtime_error("GPU conversion module path is invalid"); - return std::filesystem::path(path).parent_path() / L"pixel_convert_gfx1201.hsaco"; + return std::filesystem::path(path).parent_path() / diag::wide(std::string("pixel_convert_") + diag::HipApi::target_arch() + ".hsaco"); } struct Runtime { @@ -94,7 +94,7 @@ struct Runtime { if (!d3d) throw std::runtime_error("Null D3D12 device"); try { hipDeviceProp_t props{}; - hip.select_gfx1201(-1, props); + hip.select_architecture(-1, props); hip.verbose = cuda.verbose = !std::getenv("D4R_QUIET_API"); const auto luid = adapter_luid(d3d); if (std::memcmp(&luid, props.luid, sizeof(luid))) throw std::runtime_error("D3D12/HIP LUID mismatch"); @@ -155,7 +155,7 @@ struct Runtime { ngx_check(destroy(caps), "Destroy capability parameters"); if (available != 1 || initResult != 1 || needsDriver != 0) throw std::runtime_error("CUDA DLSS is unavailable after capability initialization"); - std::printf("D4R_RUNTIME platform=Windows architecture=gfx1201 cpu_frame_copies=0 frame_age=0\n"); + std::printf("D4R_RUNTIME platform=Windows architecture=%s cpu_frame_copies=0 frame_age=0\n", d4r::diag::HipApi::target_arch()); } catch (...) { cleanup(); throw; } } Runtime(const Runtime&) = delete; diff --git a/tools/windows/nvapi_trace.cpp b/tools/windows/nvapi_trace.cpp index 836f511..eb5763c 100644 --- a/tools/windows/nvapi_trace.cpp +++ b/tools/windows/nvapi_trace.cpp @@ -27,11 +27,11 @@ int __cdecl initialize() const char* root = std::getenv("HIP_PATH"); if (!root) return -5; auto runtime = std::make_unique(root); - device_ordinal = runtime->select_gfx1201(-1, properties); + device_ordinal = runtime->select_architecture(-1, properties); hip = std::move(runtime); runtime_ready.store(true, std::memory_order_release); } - std::printf("NVAPI_COMPAT initialize physical=gfx1201 CUDA_model_profile=AD100\n"); + std::printf("NVAPI_COMPAT initialize physical=%s CUDA_model_profile=AD100\n", properties.gcnArchName); return 0; } catch (const std::exception& e) { std::fprintf(stderr, "NVAPI_COMPAT init failed: %s\n", e.what()); return -1; } } @@ -48,7 +48,7 @@ int __cdecl architecture(void* handle, ArchInfo* info) if (!valid(handle) || !info) return -5; if (info->version != (sizeof(ArchInfo) | 1u << 16) && info->version != (sizeof(ArchInfo) | 2u << 16)) return -9; info->architecture = 0x190; info->implementation = 2; info->revision = 0x11; - std::printf("NVAPI_COMPAT GetArchInfo profile=AD102 physical=gfx1201\n"); + std::printf("NVAPI_COMPAT GetArchInfo profile=AD102 physical=%s\n", properties.gcnArchName); return 0; } int __cdecl adapter_id(void* handle, void* luid) diff --git a/tools/windows/pixel_conversion.h b/tools/windows/pixel_conversion.h index b32ada4..991694b 100644 --- a/tools/windows/pixel_conversion.h +++ b/tools/windows/pixel_conversion.h @@ -58,6 +58,7 @@ class PixelProgram { public: PixelProgram(diag::HipApi& hip, const std::filesystem::path& module) : hip_(hip) { try { + diag::require_code_object_target(module, diag::HipApi::target_arch()); hip.check(hip.hipModuleLoad(&module_, diag::utf8(module.c_str()).c_str()), "Load native GPU format conversion"); hip.check(hip.hipModuleGetFunction(&decode_, module_, "d4r_pixel_decode"), "Resolve GPU pixel decode"); hip.check(hip.hipModuleGetFunction(&encode_, module_, "d4r_pixel_encode"), "Resolve GPU pixel encode");