21 Commits
Author SHA1 Message Date
ayo f0c881985b Change d4r license to GPL-3.0-only 2026-10-08 17:17:44 -04:00
ayo 774efac524 Windows: DLSS watermark off, check script, 310.7.0 pair, shorter docs
- Set Disable.Watermark on the game path (the Linux shim already does);
  [DLSS] ShowWatermark = true keeps NVIDIA's stamp.

- Always write d4r\d4r_nvngx.log in player mode. It was skipped when the
  launcher gave the game a valid stderr pipe, e.g. Steam.

- Accept NVIDIA's 310.7.0 + _nvngx 32.0.15.9636 as "less tested" pins.
  Native kernels are still gated by PTX identity, so a different DLL
  falls back to translated kernels.

- Add d4r-check.ps1 (ok/MISSING/note lines with a fix for each; -Report
  writes a bug-report ZIP) with 15 fixture tests.

- package-windows-release.ps1: -Architectures subset, ships the check
  script, GPU registry and one GPU-query tool.

- Add a shorter WINDOWS_README.txt; trim README's Windows section and
  merge in the RX 9060 XT / distributor-DLL notes.

- Diagnostic probe accepts preset 12 (L).

Tested on RX 7700 XT without a game: loader and shim start-up, K and M
synthetic NGX frames with both DLL pairs (finite output, native kernels
used). Not tested in a game.
2026-10-03 04:58:05 -04:00
xdfnx-dev df415ec828 Support distributor DLL packages and validate Windows player mode 2026-10-03 11:28:21 +03:00
ayo 65ab1460a8 Windows: drag-in install with a per-game d4r.ini
Players extract one ZIP into the game folder, copy their two NVIDIA
DLLs, and start the game from Steam, Epic or a shortcut. No launcher
script is needed; settings live in d4r\d4r.ini.

- Shim player mode: when the launcher has not set D4R_HIP_ROOT and
  d4r.ini sits beside the shim, derive the backend environment from it.
  Codegen flags stay at the Windows-validated values. NVIDIA DLLs must
  match an exact SHA256 in dll-pins.txt, with "you have X, you need Y"
  errors. Rejects a vkd3d/Wine d3d12.dll and code objects built for
  another GPU. [DLSS] Model accepts K or M and is applied by rewriting
  the render preset hints.

- d4r.ini exposes only options that exist on Windows: Model, AsyncInterop,
  ValidateOutput, Log, CacheDir. Invalid values fall back with a note in
  d4r_nvngx.log.

- d4r_nvngx_loader: one _nvngx.dll for all GPUs. It matches the game's
  adapter by LUID and loads _nvngx_<arch>.dll from the same folder.

- OptiScaler patch 0006: relative NvngxPath/NvngxDlssPath/
  NvngxFeaturePath resolve against OptiScaler's folder.

- package-windows-release.ps1 builds the release from the per-target
  game packages and refuses mixed targets or differing shared runtimes.

- Tests: test-player-config.ps1 (ini and DLL-pin decisions, loader
  selection on the local GPU) and test-windows-release-package.ps1.
2026-10-03 03:10:19 -04:00
ayo 7ab56d42fc Keep patch files LF on Windows checkouts
Patches are applied with git apply, which fails on CRLF. With core.autocrlf=true Git also reported every .patch file as modified.
2026-10-03 03:06:48 -04:00
xdfnx-dev 6da1e79f9b Record Cyberpunk K and M 4K gameplay validation 2026-10-03 00:16:18 +03:00
xdfnx-dev bea15d3754 Record issue 10 regression results and use the Windows fixture host 2026-10-02 23:36:02 +03:00
xdfnx-dev 17121ccbf6 Fix Windows numeric NGX feature discovery and quick-test launches 2026-10-02 23:36:02 +03:00
xdfnx-dev 21ed0a8c88 Add automatic native Windows GPU quick-test packaging 2026-10-02 18:19:15 +03:00
xdfnx-dev 3d5f42865f Backport gfx1154 support into ZLUDA's Windows LLVM compiler
Adapt official LLVM 7a0829e41228 to the pinned LLVM 22 parser and ELF structures. Apply and audit the complete source delta before building, export its provenance, and package the source patch. Add 33 offline public PTX compilation checks across all eleven GPU targets without hardware execution. Preserve strict arithmetic and document RX9070XT K/M regressions.
2026-10-02 17:45:41 +03:00
xdfnx-dev b6b6d91ed9 Add experimental Windows GPU coverage to the upstream README 2026-10-02 17:11:08 +03:00
xdfnx-devandrealdody 3fa963fd1b Enable experimental native Windows RDNA3 and RDNA4 targets
Share explicit target metadata across build/runtime/package guards, preserve native gfx11 WMMA with a Windows LDS lane exchange, select HIP devices by D3D12 LUID, and reject wrong-target/vkd3d installations before changing game files. Review all issue #10 logs and document compile-only coverage plus RX9070XT regressions.

Co-authored-by: realdody <[email protected]>
2026-10-02 17:10:42 +03:00
xdfnx-devandrealdody 98de8e3a3e 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 <[email protected]>
2026-10-02 16:18:18 +03:00
xdfnx-dev 8f7058d2ba Add exact input-kernel experiments and preserve translated controls 2026-10-02 15:34:22 +03:00
xdfnx-dev 4f008b3ed1 Record bounded K game results for shared V and private WGP outputs 2026-10-02 15:21:34 +03:00
xdfnx-dev 96650ed29d Add private WGP output validation and record measured K experiments 2026-10-02 15:03:32 +03:00
xdfnx-dev a2a4a7e765 Share gfx12 K enc0 V tiles while preserving strict FP16 arithmetic 2026-10-02 15:03:32 +03:00
xdfnx-dev 28832eef6c Guard paired GPU replay timings and normalize Windows diagnostic environments 2026-10-02 15:03:32 +03:00
xdfnx-dev bf4923aa4c Reproduce gfx12 FP16 accumulator differences and preserve K baseline 2026-10-02 13:03:19 +03:00
xdfnx-dev 9dece89599 Record controlled K timings and add read-only AMD GPU telemetry 2026-10-02 12:39:14 +03:00
xdfnx-dev 973eb5ecd2 Batch Windows DLSS GPU inputs and add deferred kernel diagnostics 2026-10-02 12:06:15 +03:00
125 changed files with 6845 additions and 339 deletions

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
# Patches are applied with `git apply`, which fails on CRLF. Keep them LF even
# where core.autocrlf=true (Windows checkouts).
*.patch text eol=lf
+94 -22
View File
@@ -13,30 +13,62 @@ 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)") message(FATAL_ERROR "Set D4R_HIP_ROOT to a Windows HIP SDK (headers and bin/clang++.exe required)")
endif() endif()
find_program(D4R_HIP_CLANG NAMES clang++.exe PATHS "${D4R_HIP_ROOT}/bin" "${D4R_HIP_ROOT}/lib/llvm/bin" NO_DEFAULT_PATH REQUIRED) 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.
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/tools/windows/gpu-targets.json" d4r_target_json)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS tools/windows/gpu-targets.json)
string(JSON d4r_target_count LENGTH "${d4r_target_json}" targets)
math(EXPR d4r_target_last "${d4r_target_count} - 1")
set(d4r_supported_targets)
set(d4r_target_cpp_entries "")
foreach(index RANGE ${d4r_target_last})
foreach(field architecture elfMachine isaMajor family)
string(JSON target_${field} GET "${d4r_target_json}" targets ${index} ${field})
endforeach()
list(APPEND d4r_supported_targets "${target_architecture}")
string(APPEND d4r_target_cpp_entries " GpuTarget{\"${target_architecture}\", ${target_elfMachine}, ${target_isaMajor}, \"${target_family}\"},\n")
endforeach()
configure_file(tools/windows/gpu_targets.generated.h.in gpu_targets.generated.h @ONLY)
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
set(D4R_GPU_ARCH "gfx1201" CACHE STRING "Native Windows package GPU target")
set_property(CACHE D4R_GPU_ARCH PROPERTY STRINGS ${d4r_supported_targets})
if(NOT D4R_GPU_ARCH IN_LIST d4r_supported_targets)
message(FATAL_ERROR "D4R_GPU_ARCH must be one of: ${d4r_supported_targets}")
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 .)
install(FILES tools/windows/gpu-targets.json DESTINATION .)
set(d4r_device_flags)
if(D4R_GPU_ARCH MATCHES "^gfx11")
# Windows-only workaround informed by realdody/d4r 1897b4a. Keep the real
# gfx11 layout; do not route RDNA3 through gfx12 fragments.
list(APPEND d4r_device_flags -DD4R_WINDOWS_GFX11_LANE_EXCHANGE)
endif()
set(probe_object "${CMAKE_CURRENT_BINARY_DIR}/probe_${D4R_GPU_ARCH}.hsaco")
add_custom_command(OUTPUT "${probe_object}" 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 --offload-device-only --no-gpu-bundle-output -mno-wavefrontsize64
-nogpuinc -nogpulib -O2 "${CMAKE_CURRENT_SOURCE_DIR}/tools/windows/probe_kernel.hip" -nogpuinc -nogpulib -O2 "${CMAKE_CURRENT_SOURCE_DIR}/tools/windows/probe_kernel.hip"
-o "${probe_object}" -o "${probe_object}"
DEPENDS tools/windows/probe_kernel.hip VERBATIM) DEPENDS tools/windows/probe_kernel.hip VERBATIM)
add_custom_target(d4r_probe_code_object ALL DEPENDS "${probe_object}") 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}" 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 --offload-device-only --no-gpu-bundle-output -mno-wavefrontsize64
-nogpuinc -nogpulib -O2 "${CMAKE_CURRENT_SOURCE_DIR}/tools/windows/pixel_convert_gfx1201.hip" -nogpuinc -nogpulib -O2 "${CMAKE_CURRENT_SOURCE_DIR}/tools/windows/pixel_convert_gfx1201.hip"
-o "${pixel_object}" -o "${pixel_object}"
DEPENDS tools/windows/pixel_convert_gfx1201.hip tools/windows/pixel_format.h kernels/common/hip_device_minimal.h VERBATIM) 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}") 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}" 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 --offload-device-only --no-gpu-bundle-output -mno-wavefrontsize64
-nogpuinc -nogpulib -O2 "${CMAKE_CURRENT_SOURCE_DIR}/tools/windows/wmma_gfx1201.hip" -nogpuinc -nogpulib -O2 ${d4r_device_flags} "${CMAKE_CURRENT_SOURCE_DIR}/tools/windows/wmma_gfx1201.hip"
-o "${wmma_object}" -o "${wmma_object}"
DEPENDS tools/windows/wmma_gfx1201.hip kernels/common/wmma_backend.h DEPENDS tools/windows/wmma_gfx1201.hip kernels/common/wmma_backend.h
kernels/common/wmma_layout.h VERBATIM) kernels/common/wmma_layout.h kernels/common/wave32_exchange.h VERBATIM)
add_custom_target(d4r_wmma_code_object ALL DEPENDS "${wmma_object}") add_custom_target(d4r_wmma_code_object ALL DEPENDS "${wmma_object}")
# K/M objects stay in experimental/, outside the ZLUDA native override # K/M objects stay in experimental/, outside the ZLUDA native override
# directory, until each layer and then the complete network pass replay. # directory, until each layer and then the complete network pass replay.
@@ -49,7 +81,7 @@ foreach(family k m)
file(GLOB native_headers CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/kernels/${family}/*.h") file(GLOB native_headers CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/kernels/${family}/*.h")
foreach(source IN LISTS native_sources) foreach(source IN LISTS native_sources)
get_filename_component(kernel_name "${source}" NAME_WE) 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}") set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${source}")
file(READ "${source}" kernel_source) file(READ "${source}" kernel_source)
string(REGEX MATCH "// d4r-build-flags: ([^\r\n]+)" flags_line "${kernel_source}") string(REGEX MATCH "// d4r-build-flags: ([^\r\n]+)" flags_line "${kernel_source}")
@@ -65,18 +97,20 @@ foreach(family k m)
list(APPEND native_flags ${source_flags}) list(APPEND native_flags ${source_flags})
endif() endif()
add_custom_command(OUTPUT "${native_object}" 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 --offload-device-only --no-gpu-bundle-output -mno-wavefrontsize64
-nogpuinc -nogpulib -O3 -DD4R_DEVICE_ONLY_MINIMAL ${native_flags} -nogpuinc -nogpulib -O3 -DD4R_DEVICE_ONLY_MINIMAL ${d4r_device_flags} ${native_flags}
"${source}" -o "${native_object}" "${source}" -o "${native_object}"
DEPENDS "${source}" ${native_headers} kernels/common/wmma_layout.h DEPENDS "${source}" ${native_headers} kernels/common/wmma_layout.h kernels/common/wave32_exchange.h
kernels/common/hip_device_minimal.h VERBATIM) kernels/common/hip_device_minimal.h VERBATIM)
list(APPEND native_objects "${native_object}") list(APPEND native_objects "${native_object}")
install(FILES "${native_object}" DESTINATION "experimental/${family}") install(FILES "${native_object}" DESTINATION "experimental/${family}")
endforeach() endforeach()
endforeach() endforeach()
add_custom_target(d4r_native_gfx1201_compile ALL DEPENDS ${native_objects}) add_custom_target(d4r_native_compile ALL DEPENDS ${native_objects})
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) # Retain the old manual build target for existing scripts.
add_custom_target(d4r_native_gfx1201_compile DEPENDS d4r_native_compile)
foreach(probe gpu_inventory 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) add_executable(d4r_${probe} tools/windows/${probe}.cpp)
target_compile_definitions(d4r_${probe} PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__) target_compile_definitions(d4r_${probe} PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__)
target_include_directories(d4r_${probe} PRIVATE "${D4R_HIP_ROOT}/include") target_include_directories(d4r_${probe} PRIVATE "${D4R_HIP_ROOT}/include")
@@ -92,6 +126,16 @@ foreach(probe hip_gfx1201_probe gfx12_wmma_probe k_module_probe native_replay_pr
endif() endif()
install(TARGETS d4r_${probe} RUNTIME DESTINATION bin) install(TARGETS d4r_${probe} RUNTIME DESTINATION bin)
endforeach() 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$<$<CONFIG:Debug>: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") 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") 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") message(FATAL_ERROR "Clone MinHook v1.3.4 into external/MinHook or set D4R_MINHOOK_ROOT")
@@ -102,12 +146,23 @@ add_dependencies(d4r_nvngx_windows d4r_pixel_code_object)
set_target_properties(d4r_nvngx_windows PROPERTIES PREFIX "" OUTPUT_NAME "d4r_nvngx") set_target_properties(d4r_nvngx_windows PROPERTIES PREFIX "" OUTPUT_NAME "d4r_nvngx")
target_compile_definitions(d4r_nvngx_windows PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__) target_compile_definitions(d4r_nvngx_windows PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__)
target_include_directories(d4r_nvngx_windows PRIVATE "${D4R_HIP_ROOT}/include") target_include_directories(d4r_nvngx_windows PRIVATE "${D4R_HIP_ROOT}/include")
target_link_libraries(d4r_nvngx_windows PRIVATE version dbghelp d3d12 dxguid minhook) target_link_libraries(d4r_nvngx_windows PRIVATE version dbghelp d3d12 dxguid minhook bcrypt)
install(FILES "${D4R_MINHOOK_ROOT}/LICENSE.txt" DESTINATION licenses RENAME MinHook.txt) install(FILES "${D4R_MINHOOK_ROOT}/LICENSE.txt" DESTINATION licenses RENAME MinHook.txt)
if(MINGW) if(MINGW)
target_link_options(d4r_nvngx_windows PRIVATE -static) target_link_options(d4r_nvngx_windows PRIVATE -static)
endif() endif()
install(TARGETS d4r_nvngx_windows RUNTIME DESTINATION bin) install(TARGETS d4r_nvngx_windows RUNTIME DESTINATION bin)
# Target-independent _nvngx.dll of drag-in releases: loads _nvngx_<arch>.dll
# for the game's actual HIP architecture. Its own D4R_TARGET_ARCH is unused.
add_library(d4r_nvngx_loader SHARED tools/windows/d4r_nvngx_loader.cpp)
set_target_properties(d4r_nvngx_loader PROPERTIES PREFIX "" OUTPUT_NAME "d4r_nvngx_loader")
target_compile_definitions(d4r_nvngx_loader PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__)
target_include_directories(d4r_nvngx_loader PRIVATE "${D4R_HIP_ROOT}/include")
target_link_libraries(d4r_nvngx_loader PRIVATE version dbghelp)
if(MINGW)
target_link_options(d4r_nvngx_loader PRIVATE -static)
endif()
install(TARGETS d4r_nvngx_loader RUNTIME DESTINATION bin)
if(MSVC) if(MSVC)
target_sources(d4r_ngx_cuda_init_probe PRIVATE tools/d4r_ngx_param_msvc.cpp tools/d4r_ngx_param_host.cpp) target_sources(d4r_ngx_cuda_init_probe PRIVATE tools/d4r_ngx_param_msvc.cpp tools/d4r_ngx_param_host.cpp)
target_sources(d4r_d3d12_dlss_probe PRIVATE tools/d4r_ngx_param_msvc.cpp tools/d4r_ngx_param_host.cpp) target_sources(d4r_d3d12_dlss_probe PRIVATE tools/d4r_ngx_param_msvc.cpp tools/d4r_ngx_param_host.cpp)
@@ -171,11 +226,14 @@ if(EXISTS "${D4R_AGILITY_ROOT}/build/native/bin/x64/d3d12SDKLayers.dll")
endif() endif()
add_dependencies(d4r_hip_gfx1201_probe d4r_probe_code_object) add_dependencies(d4r_hip_gfx1201_probe d4r_probe_code_object)
add_dependencies(d4r_gfx12_wmma_probe d4r_wmma_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 "${probe_object}" DESTINATION bin)
install(FILES "${pixel_object}" DESTINATION bin) install(FILES "${pixel_object}" DESTINATION bin)
install(FILES "${wmma_object}" DESTINATION bin) install(FILES "${wmma_object}" DESTINATION bin)
install(FILES scripts/windows/test-windows-rdna4.ps1 DESTINATION .) install(FILES scripts/windows/test-windows-rdna4.ps1 DESTINATION .)
install(FILES scripts/windows/gpu-target.ps1 DESTINATION .)
install(FILES scripts/windows/gpu-preflight.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-replay.ps1 DESTINATION .)
install(FILES scripts/windows/test-k-captures.ps1 DESTINATION .) install(FILES scripts/windows/test-k-captures.ps1 DESTINATION .)
install(FILES scripts/windows/test-native-overrides.ps1 DESTINATION .) install(FILES scripts/windows/test-native-overrides.ps1 DESTINATION .)
@@ -192,13 +250,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) 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 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-rdna4-port.md DESTINATION docs)
install(FILES docs/windows-gpu-support.md DESTINATION docs)
include(CTest) 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 add_test(NAME d3d12_gpu_pixel_formats COMMAND d4r_d3d12_pixel_probe
--hip-root "${D4R_HIP_ROOT}" --module "${pixel_object}" --iterations 2) --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 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) --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 add_test(NAME d3d12_command_backend COMMAND d4r_d3d12_command_probe
--hip-root "${D4R_HIP_ROOT}" --module "$<TARGET_FILE:d4r_nvngx_windows>" --interop-mode indirect --iterations 32) --hip-root "${D4R_HIP_ROOT}" --module "$<TARGET_FILE:d4r_nvngx_windows>" --interop-mode indirect --iterations 32)
set_tests_properties(d3d12_command_backend PROPERTIES TIMEOUT 120 LABELS "hardware;d3d12;command_lists;lifetime") set_tests_properties(d3d12_command_backend PROPERTIES TIMEOUT 120 LABELS "hardware;d3d12;command_lists;lifetime")
@@ -211,16 +279,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") ENVIRONMENT "D4R_COMMAND_PROBE_WARP=1;D4R_COMMAND_PROBE_NO_HIP=1")
add_test(NAME hip_gfx1201 COMMAND d4r_hip_gfx1201_probe add_test(NAME hip_gfx1201 COMMAND d4r_hip_gfx1201_probe
--hip-root "${D4R_HIP_ROOT}" --module "${probe_object}") --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 add_test(NAME gfx12_wmma COMMAND d4r_gfx12_wmma_probe
--hip-root "${D4R_HIP_ROOT}" --module "${wmma_object}" --iterations 16) --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) foreach(layer enc1 enc2)
add_test(NAME k_${layer}_execution COMMAND d4r_k_module_probe add_test(NAME k_${layer}_execution COMMAND d4r_k_module_probe
--hip-root "${D4R_HIP_ROOT}" --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}) --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() endforeach()
add_test(NAME d3d12_hip_interop COMMAND d4r_d3d12_hip_interop_probe add_test(NAME d3d12_hip_interop COMMAND d4r_d3d12_hip_interop_probe
--hip-root "${D4R_HIP_ROOT}" --module "${probe_object}" --iterations 32) --hip-root "${D4R_HIP_ROOT}" --module "${probe_object}" --iterations 32)
@@ -245,6 +313,10 @@ if(EXISTS "${D4R_ZLUDA_ROOT}/nvcuda.dll")
add_test(NAME cuda_images COMMAND d4r_cuda_image_probe add_test(NAME cuda_images COMMAND d4r_cuda_image_probe
--hip-root "${D4R_HIP_ROOT}" --cuda-dll "${D4R_ZLUDA_ROOT}/nvcuda.dll" --iterations 32) --hip-root "${D4R_HIP_ROOT}" --cuda-dll "${D4R_ZLUDA_ROOT}/nvcuda.dll" --iterations 32)
set_tests_properties(cuda_images PROPERTIES TIMEOUT 90 LABELS "hardware;zluda;descriptors") set_tests_properties(cuda_images PROPERTIES TIMEOUT 90 LABELS "hardware;zluda;descriptors")
add_test(NAME cuda_images_async COMMAND d4r_cuda_image_probe
--hip-root "${D4R_HIP_ROOT}" --cuda-dll "${D4R_ZLUDA_ROOT}/nvcuda.dll" --iterations 32)
set_tests_properties(cuda_images_async PROPERTIES TIMEOUT 90 LABELS "hardware;zluda;descriptors;async"
ENVIRONMENT "D4R_DIAG_ASYNC_ARRAY_COPY=1")
add_test(NAME d3d12_cuda_images COMMAND d4r_d3d12_dlss_probe add_test(NAME d3d12_cuda_images COMMAND d4r_d3d12_dlss_probe
--hip-root "${D4R_HIP_ROOT}" --cuda-dll "${D4R_ZLUDA_ROOT}/nvcuda.dll" --interop-mode images --iterations 1) --hip-root "${D4R_HIP_ROOT}" --cuda-dll "${D4R_ZLUDA_ROOT}/nvcuda.dll" --interop-mode images --iterations 1)
set_tests_properties(d3d12_cuda_images PROPERTIES TIMEOUT 90 LABELS "hardware;interop;zluda;descriptors") set_tests_properties(d3d12_cuda_images PROPERTIES TIMEOUT 90 LABELS "hardware;interop;zluda;descriptors")
+642 -41
View File
@@ -1,73 +1,674 @@
Apache License GNU GENERAL PUBLIC LICENSE
Version 2.0, January 2004 Version 3, 29 June 2007
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
1. Definitions. Preamble
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. The GNU General Public License is a free, copyleft license for
software and other kinds of works.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. The precise terms and conditions for copying, distribution and
modification follow.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. TERMS AND CONDITIONS
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 0. Definitions.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: "This License" refers to version 3 of the GNU General Public License.
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and "The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. A "covered work" means either the unmodified Program or a work based
on the Program.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 1. Source Code.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
END OF TERMS AND CONDITIONS The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
APPENDIX: How to apply the Apache License to your work. The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
Copyright [yyyy] [name of copyright owner] The Corresponding Source for a work in source code form is that
same work.
Licensed under the Apache License, Version 2.0 (the "License"); 2. Basic Permissions.
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
Unless required by applicable law or agreed to in writing, software You may make, run and propagate covered works that you do not
distributed under the License is distributed on an "AS IS" BASIS, convey, without conditions so long as your license otherwise remains
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. in force. You may convey covered works to others for the sole purpose
See the License for the specific language governing permissions and of having them make modifications exclusively for you, or provide you
limitations under the License. with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
+2 -1
View File
@@ -1,7 +1,8 @@
d4r d4r
Copyright 2026 countervolts Copyright 2026 countervolts
This product is licensed under the Apache License, Version 2.0 (see LICENSE), except for the files This product is licensed under the GNU General Public License, version 3 only
(GPL-3.0-only; see LICENSE), except for the files
listed below. listed below.
patches/zluda/*.patch patches/zluda/*.patch
+6 -16
View File
@@ -8,21 +8,11 @@ Preset L is NVIDIA's DLSS 4.5 model for Ultra Performance, especially at 4K. Set
**Proof of concept:** d4r shows that DLSS can run on an AMD GPU, but it is not really that practical for everyday use yet. It has been tested on one GPU in a handful of games and depends on unreleased patches to ZLUDA and vkd3d-proton. **Proof of concept:** d4r shows that DLSS can run on an AMD GPU, but it is not really that practical for everyday use yet. It has been tested on one GPU in a handful of games and depends on unreleased patches to ZLUDA and vkd3d-proton.
**Native Windows development:** an experimental D3D12 backend runs K and M **Windows (experimental):** d4r also runs natively on Windows 11, in D3D12 games, with no Wine or Proton. It supports the DLSS 4 (K) and DLSS 4.5 (M) models. Installing works like the Linux release: extract the ZIP into the game folder, copy in two NVIDIA files, and edit `d4r\d4r.ini`. See [the Windows guide](packaging/windows/WINDOWS_README.txt); `d4r\d4r-check.ps1` checks an install and writes a bug-report ZIP.
transformer networks on Windows 11 / RX 9070 XT (gfx1201), using patched
ZLUDA/HIP and VRAM buffers/shared fences directly. Inputs and output belong to Only the Radeon RX 9070 XT (gfx1201) has been tested on real hardware here. Its backend checks pass for K and M in Silent Hill 2 and Cyberpunk 2077 at 4K; those runs are correctness checks, not FPS benchmarks, and Windows performance is still under development. Community logs from an RX 9060 XT (gfx1200) show native K/M execution and finite output in Cyberpunk and Dawnwalker, but texture/LOD and motion artifacts are still reported, so that is backend coverage and not completed image-quality validation. The other targets (RDNA3 `gfx1100..gfx1103`, RDNA3.5 `gfx1150..gfx1154`) are compile-tested only. Windows preset E and L are not available. NVIDIA DLLs are supplied by you and are never part of the repository; the quick launcher can use a distributor-supplied DLL pair, but that packaging support does not establish redistribution permission.
the current frame; this path does not use Wine, Proton or CPU image staging.
The Windows backend, command-list hooks and diagnostics live in `tools/windows`. More: [Windows GPU coverage](docs/windows-gpu-support.md), [measurements](docs/windows-performance.md), [build and validation notes](docs/windows-rdna4-port.md), and the [diagnostic launcher](docs/windows-game.md) and [quick test](docs/windows-quick-test.txt) used for bug reports.
All sixteen Windows hardware gates pass, and queued-frame/feature-recreation K
outputs match synchronous controls exactly. A five-minute K/4K Silent Hill 2
run completes 16941 finite frames without backend errors. Performance remains
under development; asynchronous interop is opt-in and requires one queue.
M retains the FP16-equivalent baseline; native FP8 has not established a
full-network speedup. Windows preset L is not validated. See
[Windows build and validation](docs/windows-rdna4-port.md),
[OptiScaler installation](docs/windows-game.md) and
[Windows measurements](docs/windows-performance.md). NVIDIA DLLs are supplied
locally and excluded from packages.
See [supported games](SUPPORTED_GAMES.md) for the tested games and DLSS models. See [supported games](SUPPORTED_GAMES.md) for the tested games and DLSS models.
@@ -170,6 +160,6 @@ Gameplay recordings of each DLSS model at the Quality, Performance and Ultra Per
## License ## License
Apache License 2.0 (see [LICENSE](LICENSE)). The patches in `patches/` are offered under the licenses of the projects they modify: ZLUDA (Apache-2.0 or MIT) and vkd3d-proton (LGPL-2.1). See [NOTICE](NOTICE). GNU General Public License, version 3 only (GPL-3.0-only; see [LICENSE](LICENSE)). The patches in `patches/` are offered under the licenses of the projects they modify: ZLUDA (Apache-2.0 or MIT) and vkd3d-proton (LGPL-2.1). See [NOTICE](NOTICE).
To contact me my discord is `._ayo`. To contact me my discord is `._ayo`.
+32 -4
View File
@@ -1,13 +1,40 @@
# Native Windows RDNA4 game development package # Native Windows RDNA3 / RDNA4 game development package
Validated hardware: Windows 11 x64 / Radeon RX 9070 XT / gfx1201. Native K and Validated hardware: Windows 11 x64 / Radeon RX 9070 XT / gfx1201. Native K and
M transformer kernels pass standalone D3D12 + OptiScaler checks. Silent Hill 2 M transformer kernels pass standalone D3D12 + OptiScaler checks. Silent Hill 2
K now renders real menu frames and M renders a saved gameplay level with K now renders real menu frames and M renders a saved gameplay level with
GPU NaN/Inf checks. Both K and M pass 3840x2160 output on this machine. GPU NaN/Inf checks. Both K and M pass 3840x2160 output on this machine.
The user reports 49-51 FPS for K in the measured scene after the command-hook Cyberpunk 2077 2.31 also reaches saved gameplay with K and M at 3840x2160:
fix and locally built output-store optimization. This is a local all required native transformer layers execute and output checks pass.
The [port notes](windows-rdna4-port.md) record session counts and limitations.
Performance measurements and their controls are recorded in
[windows-performance.md](windows-performance.md). This is a
development package; no NVIDIA proprietary DLL is included. development package; no NVIDIA proprietary DLL is included.
Experimental builds also accept gfx1100..gfx1103 (RDNA3), gfx1150..gfx1154
(RDNA3.5) and gfx1200 (for example RX 9060/9060 XT). These targets are
**compile-tested only**; no physical validation here 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.
Before changing game files, the runner inventories HIP devices without
executing kernels. It rejects another architecture's package and a local
D3D12 proxy identifying itself as vkd3d/Wine, producing one diagnostic ZIP.
RX 9060 XT needs gfx1200: the old gfx1201-only package fails before DLSS
initialization on it. Use native D3D12. On an untested GPU, start with
synchronous K and output diagnostics. The ordinary launcher does not attach
a debugger. `-CaptureExceptions` is an explicit developer option; games with
anti-debug protection can refuse it.
Numeric CUDA driver NGX initialization searches for `nvngx_dlss.dll` beside
the calling d4r shim. The installer stages your local feature DLL in `d4r/`
and the original NVIDIA core in `d4r/vendor/`. `Libraries.NvngxDlssPath` is
the feature directory. Keeping the feature DLL only in `vendor/` causes
`BAD00004` (FeatureNotFound) in numeric-ID games such as Cyberpunk.
Build the shim/diagnostics, corrected ZLUDA, K/M native objects and patched Build the shim/diagnostics, corrected ZLUDA, K/M native objects and patched
OptiScaler using the scripts in `scripts/windows`, then run: OptiScaler using the scripts in `scripts/windows`, then run:
@@ -112,4 +139,5 @@ uses a previous-frame fallback.
OptiScaler source modifications are GPL-3.0 and exported under OptiScaler source modifications are GPL-3.0 and exported under
`source-patches/optiscaler`. ZLUDA source modifications are exported under `source-patches/optiscaler`. ZLUDA source modifications are exported under
`source-patches/zluda`. Dependency license texts and build hashes are included. `source-patches/zluda`; the LLVM gfx1154 backport is under
`source-patches/zluda-llvm`. Dependency license texts and build hashes are included.
+189
View File
@@ -0,0 +1,189 @@
# Experimental Windows RDNA3 / RDNA4 coverage
The shim, diagnostics, native K/M kernels and game installer accept the
targets below, with one package per actual ISA. **Only RX 9070 XT has physical
validation here.** Community RX 9060 XT / gfx1200 logs now show K/M backend
execution in Cyberpunk and Dawnwalker with all required native layers and
finite output. Visual issues remain reported and are not resolved by those
checks. All other targets are experimental and compile-tested; their DLSS,
interop and performance remain unverified.
| Family | Target | Examples / scope |
| --- | --- | --- |
| RDNA3 | gfx1100 | RX 7900 family; same-target workstation GPUs |
| RDNA3 | gfx1101 | RX 7700 / 7700 XT / 7800 XT; same-target mobile/workstation GPUs |
| RDNA3 | gfx1102 | RX 7600 / 7600 XT; same-target mobile/workstation GPUs |
| RDNA3 | gfx1103 | RDNA3 integrated graphics |
| RDNA3.5 | gfx1150, gfx1151, gfx1152, gfx1153, gfx1154 | Corresponding integrated graphics targets |
| RDNA4 | gfx1200 | RX 9060 / 9060 XT; same-target GPUs |
| RDNA4 | gfx1201 | RX 9070 / 9070 XT / 9070 GRE; same-target GPUs |
Names and IDs follow [LLVM processors](https://llvm.org/docs/AMDGPUUsage.html#processors)
and [ELF ABI](https://llvm.org/docs/AMDGPUUsage.html#amdgpu-elf-header-e-flags).
`tools/windows/gpu-targets.json` supplies CMake-generated C++, PowerShell and
the private texture builder. Selection uses HIP's real `gcnArchName` and the
D3D12 adapter LUID, including systems with several GPUs sharing a target.
Marketing names do not control eligibility.
All listed RDNA3/RDNA3.5/RDNA4 targets with existing gfx11/gfx12 WMMA contracts
are included. TBD RDNA4m gfx1170..gfx1172 and gfx12.5 gfx1250/gfx1251 are
different ISA groups and excluded. Unknown future targets need explicit review.
Check [AMD's Windows matrix](https://rocm.docs.amd.com/projects/radeon-ryzen/en/latest/docs/compatibility/compatibilityrad/windows/windows_compatibility.html)
for driver/runtime eligibility. Compiler support cannot supply missing Windows
driver support or external-memory APIs, especially on APUs.
## Installation and diagnostics
Use the actual target's archive: **gfx1200 for RX 9060 XT**, for example.
Metadata, host target and every ELF target must agree. Runtime/scripts reject
mixed targets and never rename targets, rewrite ELF IDs or set
`HSA_OVERRIDE_GFX_VERSION`. Legacy metadata-free packages remain gfx1201 only.
Before changing game files, `windows-game.ps1` now runs a read-only HIP
inventory and checks the target and wave32. Inventory allocates no GPU buffers
and runs no kernels; success is discovery, not validation. Failure prints one
ZIP containing stdout/stderr, exit code, runtime/DLL and driver details. A local
`d3d12.dll` identifying itself as vkd3d/Wine is rejected before install. Restore
the game's native D3D12 installation; the script never loads or removes that
proxy itself. Begin synchronously on an untested GPU:
```powershell
.\windows-game.ps1 -GameExe "D:\Games\Example\Game.exe" -NgxCore "C:\LocalDLLs\_nvngx.dll" -DlssDll "C:\LocalDLLs\nvngx_dlss.dll" -Preset 11 -ValidateOutput -CaptureExceptions
```
The local DLLs must match the documented NGX version and DLSS 310.9.1 SHA256
in `package.json`. NVIDIA DLLs are never distributed. Retain the printed ZIP
on failure. Async K and performance are subsequent gates; gfx1201-specific
FP8, arithmetic and tuning experiments remain separate.
## Issue #10 review, 2026-10-02
Reviewed [comment 5952822115](https://github.com/countervolts/d4r/issues/10#issuecomment-5952822115),
including all 32 attached text logs from eight attempts. HIP discovers
**RX 9060 XT / gfx1200** successfully. Every attempt fails with the old shim's
**"No selected gfx1201 device"** rejection; OptiScaler reports `support=false`
/ `BAD00002`. None reaches `D4R_RUNTIME` initialization or native transformer
execution. These logs do not demonstrate a K/M kernel crash. Use the gfx1200
package instead of overriding architecture. Three attempts also load
vkd3d-proton 3.1.0 from a local D3D12 proxy, outside this Windows backend.
The new packages/preflight address the identified failures. They do not prove
those games work on RX 9060 XT; a new native-D3D12 run on that GPU is required.
Raw community logs and personal directory paths stay local.
## Community retest, 2026-10-03
[Comment 5962444244](https://github.com/countervolts/d4r/issues/10#issuecomment-5962444244)
uses the corrected `52d9872` package / `3d62b98` binaries on RX 9060 XT
(`gfx1200`, 16 GB). Cyberpunk K/M complete 4,365 / 1,751 session frames;
Dawnwalker K/M complete 6,713 / 1,690. Every frame has a finite-output GPU
check, all required native layers execute and all four sessions exit normally.
The logs record zero backend failures, previous-frame outputs and CPU image
copies. This is community backend coverage, not a reference-image comparison:
the tester reports texture/LOD and motion-related artifacts. All four runs use
the HDR network input/output path, unlike the local Cyberpunk LDR validation.
Spider-Man 2 crashes with `0xc0000005` before any completed DLSS frame;
its nonstandard launch is not evidence of a transformer kernel failure.
Assassin's Creed exits with zero frames and Streamline logs repeated internal
minidumps; its clean process exit is still inconclusive. Their causes remain
open without a stack trace. The Witcher 3 log in
[comment 5962808629](https://github.com/countervolts/d4r/issues/10#issuecomment-5962808629)
selects the gfx1200 d4r shim instead of the original NGX core (its SHA256
matches the shipped shim exactly), and the installer correctly refuses
before modifying the game. It does not establish a Witcher runtime failure.
## Build
Prepare dependencies following `windows-rdna4-port.md`. Default: gfx1201.
Select another target explicitly and keep separate directories:
```powershell
$arch = 'gfx1101' # for example RX 7800 XT
$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"
```
Rebuild diagnostics for the new inventory executable. The package keeps strict
K FP16 and M FP16-equivalent baselines; native M FP8 remains disabled. CMake:
`-DD4R_GPU_ARCH=gfx1101`. Some source/executable names retain gfx1201/gfx12 for
compatibility; actual targets follow the selected architecture.
One command compiles all eleven targets without GPU workloads:
```powershell
.\scripts\windows\build-windows-gpu-matrix.ps1 -ZludaRoot "$PWD\dist\zluda-windows-final"
```
Add `-PackageGames -DlssDll "$PWD\nvngx_dlss.dll" -ArchiveDirectory "$PWD\dist\windows-gpu-archives"`
to stage/archive all packages after committing source and preparing OptiScaler.
Use `-GpuArchitectures gfx1100,gfx1101,gfx1102,gfx1200,gfx1201` for a subset.
Per-target logs and `matrix-build.json` distinguish compilation from execution.
The supplied DLL generates manifests locally and is never copied into archives.
Rebuild LLVM/ZLUDA when updating from an older package: the old LLVM 22 backend
does not recognize gfx1154 even though Clang 24 compiles native objects for it.
`build-zluda-llvm.ps1` now applies the source backport in `patches/zluda-llvm`
automatically; relink with `build-zluda-windows.ps1` afterward. Compiler base
and patch hashes are recorded in the ZLUDA build metadata and included in game
packages. The expected patched LLVM submodule appears modified in its parent
checkout; its complete source delta is exported, not a binary target override.
For a compilation gate requiring neither a GPU nor NVIDIA DLLs, run:
```powershell
.\scripts\windows\test-zluda-target-compilation.ps1 -ZludaRoot "$PWD\dist\zluda-windows-final"
```
All 33 public PTX compilations pass for the eleven targets: Driver API pattern,
FP16 MMA strict reference and explicit WMMA modes, with correct ELF targets
and wave32. The gfx12 strict F16 reference intentionally retains its existing
double-precision helper; the separate WMMA mode checks code generation only.
It does not enable relaxed arithmetic in game packages or validate numerical
results on other GPUs. The original gfx1154 compiler abort is preserved locally
in `build/llvm-gfx1154/before.log`; the regression results are in
`test-results/windows-gpu-coverage/zluda-offline`.
## Fork adaptation
Reviewed [realdody's branch](https://github.com/realdody/d4r/tree/windows-rdna3)
at `ae530b0f53ed5c0bb87558dd7c20f619e014e4fd` and the newer
[`1897b4a` correction](https://github.com/realdody/d4r/commit/1897b4afa79832bd879234cfb37e7c1468db5cde).
Earlier MSVC binary16 and generated `BOOL` fixes remain. The earlier forced
gfx12 fragment layout is not used.
The newer fork reports Windows gfx1101 `permlanex16` returning its own lane,
duplicating even K values into odd slots. This port adapts that finding into
`kernels/common/wave32_exchange.h`: a Windows/gfx11-only per-wave LDS exchange,
with native gfx11 layout, generic x/y/z indexing, 2 KiB scratch (512 threads
maximum), volatile accesses and wave fences/barriers protecting scratch reuse.
It covers K/M fragment conversions and other K half-wave exchanges. gfx12 and
Linux keep the original instruction path. The raw gfx11 probe uses its actual
intrinsic/layout, without a forced gfx12 shim.
The fork reports RX 7800 XT enc1/enc2 identity/nonzero/reference checks. Those
are **the author's results**, not validation of this adaptation or full K/M
here. LDS exchange, full network, translated CUDA, accuracy, interop and speed
still need physical gfx11 testing. Credit: realdody <dodobozicek@gmail.com>.
## Results and next hardware gate
All eleven targets compile the complete MinGW host build and 19 device objects
with TheRock `10.2.0a20260929`, HIP `7.17.26386`, Clang 24. Software target
contracts inspect actual ELF objects without other-target GPU launches.
RX 9070 XT passes all 20 CTest gates after these changes. K/M full-frame and
package checks are recorded in `windows-rdna4-port.md`. Local logs are in
`build/windows-gpu-coverage` and `test-results/windows-gpu-coverage`.
The first hardware gate on another GPU needs no NVIDIA DLL:
```powershell
.\scripts\windows\test-windows-rdna4.ps1 -RuntimeProfile therock -PackageRoot "$PWD\dist\windows-gfx1101-diagnostics" -ZludaRoot "$PWD\dist\zluda-windows-final"
```
Success requires exit 0, correct actual target, exact HIP/PTX/WMMA/pixel checks
and stable interop lifetime. Failure prints one ZIP. Full K/M reference,
temporal, DLSS and game validation remain subsequent gates.
+463 -1
View File
@@ -4,7 +4,7 @@ Hardware: RX 9070 XT / gfx1201, Windows 11, display driver 32.0.31041.1004.
Runtime: TheRock 10.2.0a20260929, HIP 7.17.26386; ZLUDA b0161a4 with Runtime: TheRock 10.2.0a20260929, HIP 7.17.26386; ZLUDA b0161a4 with
`D4R_ZLUDA_WMMA=1`, FP8 widening, native FP8 disabled, f16 reference rounding. `D4R_ZLUDA_WMMA=1`, FP8 widening, native FP8 disabled, f16 reference rounding.
The optional profiler uses HIP events on the actual launch stream, including The original serializing profiler uses HIP events on the actual launch stream, including
separate measurements of one-time weight preparation. It synchronizes each separate measurements of one-time weight preparation. It synchronizes each
sampled launch and changes scheduling: these numbers are not application FPS. sampled launch and changes scheduling: these numbers are not application FPS.
HIP occupancy predictions are not measured hardware occupancy. The native HIP occupancy predictions are not measured hardware occupancy. The native
@@ -192,6 +192,468 @@ without backend errors; ordinary recording intervals later return to about
16 ms. A stable foreground-scene capture is needed before optimizing from 16 ms. A stable foreground-scene capture is needed before optimizing from
the ETW metrics. Raw files: `test-results/silent-hill2-k-presentmon-4k`. the ETW metrics. Raw files: `test-results/silent-hill2-k-presentmon-4k`.
The following 30-second capture starts after the user confirms the same
foreground scene is ready. All 1902 presents use Hardware Independent Flip
and SyncInterval 0; input/output resolution remains 2259x1271 -> 3840x2160.
The mean present interval is 15.730 ms (**63.574 FPS**). PresentMon estimates
mean GPU busy/wait at 14.504/1.110 ms, GPU time at 15.614 ms and CPU wait at
0.052 ms. These frame-attributed metrics do not equal a hardware utilization
sensor and do not establish a physical CPU limit. FSR4 has not been captured
in this same scene, so its previously reported 80+ FPS remains a user reading.
Raw files: `test-results/silent-hill2-k-steady-profile-4k`.
The containing ten-minute diagnostic completes 36591 K frames, with zero
backend failures, CPU image copies, previous-frame outputs or recorded game
crashes. It deliberately stops at its time bound. Output scans and serializing
kernel events are off. Whole-run median boundary timestamps measure input
copies 0.063 ms, external span 5.399 ms and output copies 0.212 ms. The external
span includes HIP execution and host/driver scheduling, not only transformer
arithmetic. These medians cover the whole run; the PresentMon numbers above
come from the explicitly selected 30-second scene.
### Deferred kernel events and batched inputs
The new optional `-ProfileKernelsDeferred` samples every seventeenth launch
by default (`-KernelProfileEvery`). Its thread-local pool is bounded to 64
event pairs. It queries ready events before selected launches and after the
application's existing context synchronization, reusing a pair only after
reading its old timestamps. It adds no explicit event/stream/device completion
wait; timestamps, queries and logging still add overhead. Stream capture and
legacy default-stream launches are skipped by default. `-ProfileLegacyStream`
allows the latter explicitly, retaining their legacy cross-stream ordering.
This opt-in is necessary to profile this DLSS K binary, whose measured launches
use the legacy default stream. `completion_ms` now denotes collection lag for
deferred records. The report separates serializing/deferred records and retains
invalid timestamps and skipped samples outside kernel timing statistics.
HIP 7.17.26386 reports some negative elapsed times in the tiny queued PTX
diagnostic, including intervals exceeding one microsecond. These are logged as
`D4R_KERNEL_PROFILE_INVALID`; they are not clamped or used as valid GPU samples.
The cause is not established, and nonnegative samples alone cannot prove a
runtime's timestamp accuracy. The diagnostic verifies all 516 PTX launches
across primary/created contexts and individual/queued submission, checks final
device output and guards, accounts for every collected event and verifies safe
default-stream skipping. Results: `test-results/zluda-deferred-profile-final-ptx`.
The same diagnostic with stable HIP SDK 7.2 also verifies all 516 outputs and
reproduces negative intervals (`test-results/zluda-deferred-profile-stable-ptx`).
A native K dec4 sample is negative too; this is not confined to tiny PTX kernels.
Do not infer hardware/kernel utilization or a speedup from these event samples.
HIP event semantics: [AMD event management](https://rocm.docs.amd.com/projects/HIP/en/latest/reference/hip_runtime_api/modules/event_management.html).
The opt-in `-BatchInputCopies` (`D4R_BATCH_INPUT_COPIES=1`) enqueues native
pixel decoding and device-to-array input copies on the shared HIP/ZLUDA legacy
default stream. One all-stream completion remains before NGX can use any input
on its own streams. The independent D3D12 input-fence check, NGX completion and
same-frame output dependency remain. Output transfers retain their existing
completion. Every image stays in VRAM. The ZLUDA implementation adds
`cuMemcpy2DAsync_v2` using `hipMemcpyParam2DAsync`; format-80 array emulation
is explicitly rejected because its synchronous implementation uses temporary
host storage. The Windows path supplies canonical FP16/FP32 arrays instead.
This experiment remains disabled by default. Correctness checks pass, but the
controlled game comparison below shows only a small frame-time difference.
```powershell
.\scripts\windows\test-deferred-kernel-profile.ps1
.\scripts\windows\test-async-k.ps1 -BatchInputCopies -ProfileGpuBoundary -ZludaRoot "$PWD\dist\zluda-windows-deferred-profile"
```
The rebuilt Windows shim and new ZLUDA pass all seventeen CTest gates,
including the added asynchronous pitched FP16/FP32 array-copy test and
padding checks. Completed boundary timestamps are also read before reuse if
the copy fence advances between the first completion scan and slot selection;
this prevents diagnostic records being overwritten without adding a wait.
The depth/stencil and packed-format burst/recreation gates pass 24 full K/4K
frames. All twelve candidate RGB outputs match fresh unbatched synchronous
controls exactly, with finite RGBA. The former also enables deferred legacy
events; invalid timestamps remain separate. These short correctness fixtures
include initialization and do not establish steady-state FPS or a copy-stage
speedup. Raw results: `test-results/k-batch-deferred-4k` and
`test-results/k-batch-packed-4k`. ZLUDA candidate source is `67127dd`, isolated
in `dist/zluda-windows-deferred-profile`; patches 0016/0017 and all preceding
patches apply to the clean pinned base. The public prerelease is unchanged.
After committing/rebuilding ZLUDA with a clean worktree, the twelve-frame
four-way K/4K gate passes again with exact candidate RGB and finite RGBA
(`test-results/k-batch-committed-source-4k`). This warms the committed runtime's
JIT cache before the game comparison.
The following user-confirmed 30-second foreground-scene capture contains 1925
presents at **64.265 FPS**, versus the previous unbatched run's 63.574 FPS.
Both use 2259x1271 -> 3840x2160, Independent Flip and SyncInterval 0, without
kernel-event profiling, output scans or attached debuggers. Mean present time
is 15.561 versus 15.730 ms, a 0.169 ms difference. This single pair does not
establish repeatability or a large performance improvement. The containing
ten-minute candidate run completes 35673 K frames with zero backend failures,
CPU image copies, previous-frame outputs or recorded game crashes. Whole-run
median input preparation drops from 0.482 to 0.332 ms; median external span
is 5.438 versus 5.399 ms. Whole-run stages are not the selected PresentMon
window. No GPU utilization conclusion is drawn from ETW estimates. Results:
`test-results/silent-hill2-k-batch-inputs-4k`. The K performance gate stays open.
### AMD driver telemetry
A separate read-only diagnostic polls the installed driver's ADLX base C
interfaces for device-wide usage, core/VRAM clocks, board power and temperatures.
It adds no work to game queues and changes no tuning setting. Build it locally
using the official SDK pinned at `32b5a740d42295c5dfe9026b9f52683da0f3af91`:
```powershell
.\scripts\windows\build-gpu-telemetry.ps1
.\scripts\windows\capture-presentmon.ps1 -Seconds 30 -GpuTelemetry
```
The SDK retains its own AMD license; SDK headers, implementations, driver DLLs
and the SDK-dependent executable are excluded from public game packages.
The probe is independently written and uses the documented C ABI, including
with LLVM-MinGW. It loads `amdadlx64.dll` only from Windows System32 and
releases acquired interfaces before ADLX termination/unload. GPU selection
must match exactly one device (`-GpuName` selects a different name substring).
The process exits at its bound or target process exit. The capture retains
CSV, raw stderr, exit/timeout, driver DLL version/hash and SDK build identity.
No optional telemetry failure is silently converted to a valid measurement.
CSV records independent host UTC/QPC timestamps and the driver's raw sample
timestamp. On this installed ADLX 1.5.0.124, the latter is uptime despite the
SDK description of epoch time; do not interpret it as UTC. Unsupported metrics
are blank, zero utilization/clock remains valid, and the report uses each
driver timestamp once. Device-wide usage is distinct from PresentMon's
per-process frame attribution and is not measured WMMA occupancy. ADLX API:
[AMD GPU metrics sample](https://gpuopen.com/manuals/adlx/adlx-c__perf_g_p_u_metrics/).
The initial idle-device smoke test captures six valid polls, selects the real
RX 9070 XT and terminates ADLX successfully. Board power/core clock/usage
and temperatures are available; `GPUPower` is unsupported and stays absent
from the report. The first poll costs roughly 10 ms for initialization;
subsequent polls are about 0.08 ms at 500 ms intervals. This idle smoke test
does not establish game clocks, utilization or an overhead-free benchmark.
Results: `test-results/gpu-telemetry-smoke`.
The subsequent combined-capture smoke test collects 246 presents and ten
ADLX polls and exits cleanly (`test-results/gpu-telemetry-capture-smoke`).
It includes startup/scene changes and is excluded from FPS comparisons.
After the user confirms the foreground comparison scene, the 30-second run
with detailed d4r stages/boundary timestamps disabled records **64.661 FPS**
over 1935 presents. ADLX's sixty polls report mean usage **93.183%**, core
clock **3124.667 MHz**, board power **316.717 W**, temperature 57 C and hotspot
82.9 C. Mean PresentMon GPU busy/wait is 14.551/0.800 ms. All presents retain
Independent Flip / SyncInterval 0. This records high device-wide driver usage
in this run; the user identified the earlier 63% reading as Task Manager.
It is not the same measurement as ADLX device-wide usage. Do not infer a physical CPU
limit, isolated shader occupancy or a FSR4 speed comparison. Query overhead
is median 0.094 ms per 500 ms poll, with a roughly 12 ms first query. Results:
`test-results/silent-hill2-k-telemetry-unprofiled-4k`. The game stability
summary completes 37187 K frames with zero backend failures, CPU image copies,
previous-frame outputs or recorded crashes, stopping at its ten-minute bound.
Remaining K work now prioritizes transformer/output-kernel cost; frame
scheduling remains measured.
### Rejected packed FP16 accumulator experiment
An isolated `D4R_K_F16_WMMA` enc1 build uses gfx12's documented packed FP16
C/D intrinsic, retaining the current operand/accumulator lane mapping. It
removes most conversion instructions (1066 static F16/F32 conversions become
two) and reduces main-kernel VGPRs from 218 to 201; LDS remains 24576 bytes,
with zero spills. These are compiler/ISA counts, not GPU time or occupancy.
The candidate is rejected before moving to another layer: its real replay
changes 1187603 half codes in the output allocation, max absolute difference
0.0390625 from the validated baseline. All values are finite. Against the
current twelve-window NumPy reference, PSNR drops from baseline 93.7527 dB
to 69.2377 dB; max absolute error rises from 0.0078125 to 0.0390625.
The original comparison incorrectly selected a pre-normalization-fix saved
output (78.7702 dB); a fresh replay matches `k-real-strict-dual-reference/enc1`
exactly and supplies the corrected baseline here. The rejection still holds.
Two independent PTX windows give 70.4022/70.08 dB. These pass the existing
coarse reference thresholds but do not satisfy this optimization's baseline
preservation gate. No game installation or performance claim follows.
A public 64-matrix primitive reproducer isolates the difference to direct
instruction arithmetic. The packed intrinsic and the layout adapter agree
bit-for-bit. Direct FP16 output differs from FP32 WMMA rounded once to FP16
even after one K16 step (1674 of 4096 elements, max absolute 0.00390625).
After eight steps, 2874 elements differ, max absolute 0.0625. This rules out
the adapter packing as the source of this fixture's difference; the hardware
internal accumulation/rounding cause is not established. Production K keeps
its existing FP32-instruction/FP16-rounding baseline and the experimental flag
is off in every normal build. Results: `test-results/k-f16-wmma-enc1` and
`test-results/wmma-f16-arithmetic-compare-fixed`.
The finished `test-wmma-f16.ps1` repeats the same 64-case result. Rebuilding
every ordinary K/M object leaves all sixteen byte-identical; all seventeen
CTest gates pass and enc1 still compiles for gfx1101 (compile-only). Logs:
`test-results/wmma-f16-script-check` and `test-results/k-f16-baseline-ctest.log`.
```powershell
.\scripts\windows\test-wmma-f16.ps1
.\scripts\windows\build-native-k-f16-wmma.ps1 -Layer enc1
```
The first command compiles and runs the public arithmetic diagnostic without
NVIDIA inputs. Its success means the reproducer executed correctly, not that
the two instruction paths are equivalent; JSON retains `baselineEquivalent`
and `gameOptimizationAccepted`. The second compiles the rejected experiment
in a separate directory and generates no override manifest. Intrinsic API:
[Clang gfx12 FP16 WMMA](https://clang.llvm.org/docs/AMDGPUBuiltinReference.html#builtin-amdgcn-wmma-f16-16x16x16-f16-w32-gfx12).
### Controlled native replay timings
`native_replay_probe` now supports `--benchmark-timing events|dispatch` and
`--benchmark-batch 1..256`. Dispatch mode uses the documented
`hipExtModuleLaunchKernel` start/stop events, with work-item dimensions derived
from each module's launch metadata and flags zero. It does not change wave
order or arithmetic. Control/candidate can have different native block/grid
metadata; each launch uses its own geometry. The default remains event markers.
Each AB/BA sample restores captured inputs outside timing, submits a series,
waits for its end event, and logs both the event interval and independent host
launch-to-completion interval divided by launch count. These are isolated
diagnostics: batch intervals include dispatch/queue gaps and are not exclusive
instruction execution times, frame latency, or game FPS. Zero, negative,
nonfinite or event intervals exceeding the enclosing host interval are retained
as `D4R_REPLAY_PROFILE_INVALID` and excluded. Reports keep timing sources,
batch counts and log files separate, retaining host statistics and invalid
samples; no partially valid pair is accepted by the benchmark runner.
The new runner first executes fresh ordinary control/candidate replays and
compares every allocation's SHA256 before timing. It stops at any mismatch,
then checks the control output again after every benchmark. A successful
timing run does not replace the independent NumPy/PTX correctness gates.
Captured weights/IR and GPU outputs remain private.
```powershell
.\scripts\windows\benchmark-native-replay.ps1 -Module "$PWD\build\native-k-gfx1201\dltss_pwin_enc1_layer.hsaco" -Candidate "$PWD\build\native-k-gfx1201\dltss_pwin_enc1_layer.hsaco" -FixtureDirectory "$PWD\test-results\ngx-k-fp16-fenced-capture\replay\replay-000001-dltss_pwin_enc1_layer"
```
The same-module enc1 control has all exact allocations and 32 valid pairs per
mode. Median per-launch times at the captured 512x288 workload are 0.054850 ms
with separate single-launch events and 0.089700 ms with single-launch dispatch
events. At batch 64 they converge to 0.048213/0.048017 ms. Thus dispatch events
alone do not remove timing overhead; use the batch/host cross-check. All eleven
K same-module controls pass sixteen pairs at batch 64 in both modes, including
dec4. This does not fix or explain the previously recorded negative ZLUDA
timestamps. Results: `test-results/k-replay-timing-methods` and
`test-results/k-all-layers-batch-profile`.
One-frame standalone K/4K HDR captures provide actual enc0/enc1/dec0 inputs.
Each harness run uses the eleven validated native layers and private output
tail, passes the finite-output scan and completes without network fallback. At this
synthetic resolution enc1 uses a 61x35 grid; this workload is distinct from
the game's 2259x1271 input. Experiments retain F32 WMMA instructions and strict
FP16 rounding. `build-native-k-experiment.ps1` builds separate objects and no
override manifest; no experimental object is installed by the game runner.
CU/WGP and non-temporal load/store/both on enc1 preserve every allocation but
do not improve its paired 4K timings. Position-only token tiling also preserves
outputs: enc1 with two tiles and shared V spills four VGPRs and is about 9%
slower; one-tile shared V has no measured gain. The two-tile shared-V enc0 run
initially has a roughly 4% lower median paired ratio amid scheduling variance.
A repeat with 64 AB/BA pairs at batch 64 gives control/candidate medians
0.297060/0.282559 ms (events) and 0.299988/0.285011 ms (dispatch), with median
paired ratios 0.950863/0.952240. A 512x288 replay independently gives ratios
0.950396/0.952582. Both tested dec0 shared-V variants are slower or indistinguishable.
Enc0 now selects shared V for the gfx12 strict baseline, preserving the existing
two-token tiles, F32 WMMA and FP16 rounding. Gfx11 and arithmetic experiments
retain their prior selection. `D4R_K_PRIVATE_V` restores the previous enc0 for
comparison; the build helper exposes `-PrivateValues`. The exact CMake-produced
object also passes a fresh 4K replay and 32 paired batch-64 samples, with ratios
0.955418/0.948494. Its ELF hash differs from the experimental object; `.text`
and `.rodata` match exactly, and numerical validation is repeated on the actual
object. The other fifteen ordinary K/M objects remain byte-identical.
Gfx1101 enc0/enc1 compile-only controls retain identical code and constant
sections to their original sources; no RDNA3 hardware execution is claimed.
Full K controls/candidates complete sixteen 4K frames, and twelve more with
the game's 2259x1271 input and 3840x2160 output. Every compared RGB half code
matches, all RGBA scans are finite. Results: `test-results/k-enc0-shared-full-4k`,
`k-enc0-shared-full-game-input`, `k-4k-enc0-pos2-shared-repeat`,
`k-enc0-pos2-shared-small` and `k-enc0-default-shared-4k`. The saving is about
0.015 ms for enc0 in the isolated 4K replay, not 5% application FPS.
The separate `D4R_K_PACKED_ACC` experiment stores already-rounded accumulators
as half vectors between steps, widening for each unchanged F32 WMMA and
rounding its F32 output back to half. The ordinary `acc8v` alias remains F32.
Enc0/enc1/dec0 4K replays match every allocation exactly. Enc1 main VGPRs fall
218 -> 195, LDS remains 24576 bytes and spills stay zero, but paired GPU time
does not improve; enc0 is about 4% slower and dec0 about 1% slower. Combining
packed storage with enc1 two-tile shared V is about 13% slower. The flag remains
off. Smaller register counts alone are not evidence of an optimization.
Raw results are under `test-results/k-4k-*-packed-acc` and
`test-results/k-4k-enc1-pos2-shared-packed`.
A direct recompilation of the private output tail's optimized LLVM IR with
TheRock Clang 24 fails in instruction selection with `unsupported library
call operation` (driver exit 1, backend exit 70, exception 0xE0000046). The generated crash
reproducer/log are retained locally; strict FP is not removed to work around
this. The working LLVM 22 output object remains selected. The HDR tail's
metadata is wave32, 133 VGPRs, 6912 LDS bytes and no spills; LDR uses 128 VGPRs
with the same LDS and no spills. These are static resource counts, not actual
occupancy. Compiler scheduling hints are documented in the
[LLVM AMDGPU backend guide](https://llvm.org/docs/AMDGPUUsage.html#llvm-ir-attributes).
### Private output-tail WGP experiment
ZLUDA source `fb0adc8714f57647320f0601533ec50331e378b0`, patch 0018, adds
opt-in `D4R_ZLUDA_WGP=1`. It changes only CU/WGP placement for gfx11/gfx12,
preserving wave size and arithmetic. The ordinary runtime stays in CU mode.
The offline texture builder accepts `-ShaderMode wgp` and verifies `-cumode`
on the emitted kernel before accepting the object. The compiled output-tail
objects carry this mode independently of the runtime's environment variable.
These NVIDIA-derived objects remain private; the public package excludes them.
The matched K/4K comparison uses identical public network objects and the
existing validated CU output kernels as control. Both HDR and LDR complete
eight frames per variant; repeating with candidate-first order completes
sixteen frames per variant. All 96 control/candidate frames are finite, and
all 48 candidate RGB outputs exactly match their fresh controls. Every frame
has a native output-tail hit and a valid serializing event measurement.
| Output-tail median, ms | CU, first run | WGP, first run | CU, reverse order | WGP, reverse order |
| --- | ---: | ---: | ---: | ---: |
| LDR | 1.864650 | 1.703100 | 1.840260 | 1.687500 |
| HDR | 2.012301 | 1.883395 | 2.016020 | 1.869250 |
These are separate-process serializing diagnostic measurements, not paired
resident-module timings or game FPS. Unchanged network kernels also vary
between processes. The repeat indicates a candidate worth testing in the
game; it does not establish a frame-rate improvement. Results:
`test-results/k-output-wgp-reference-4k-clean-env` and
`k-output-wgp-reference-4k-reverse`.
The game-sized WGP async burst/recreate gate completes twelve frames, all
finite; all six async RGB outputs match fresh sync controls exactly, with
no CPU image copy or aged output (`k-output-wgp-async-game-input`).
The exact CMake shared-V enc0 and WGP output tails together also pass twelve
game-sized async/burst/recreation frames, with all six async RGB images exact
(`k-enc0-shared-wgp-async-game-input`). Default selection of private output
objects remains CU until a game comparison establishes the candidate's effect.
### Combined K game check
The private WGP outputs are rebuilt from clean ZLUDA source `fb0adc8`; both
object hashes match the earlier validated experimental objects. A final
2259x1271 -> 3840x2160 comparison completes twelve finite control/candidate
frames, all six candidate RGB outputs exact, with all expected native hits and
valid timings (`k-output-wgp-clean-source-final-4k`). This supplies clean build
provenance without changing the tested binary identities.
The subsequent Silent Hill 2 game check combines the exact CMake shared-V enc0
and those private WGP outputs. Package source is clean `68b551b`; the runtime
remains the validated `67127dd` ZLUDA build with its warm cache, while the
offline output objects carry WGP placement. DLSS is the locally provided
310.9.1.0 DLL. Async interop and batched GPU inputs are enabled; debugger,
serializing/deferred kernel profiling, detailed stage/boundary timestamps and
output scans are disabled.
After the user confirms the stationary foreground comparison scene, thirty
seconds capture 1954 presents: **65.250 FPS**, mean/median/p95 frame intervals
15.326/15.318/16.306 ms. Every present uses Hardware Independent Flip with
SyncInterval 0. PresentMon mean GPU busy/wait is 14.403/0.807 ms. Sixty unique
ADLX polls record **93.500%** mean device usage, **3118.400 MHz** core,
2505 MHz VRAM and **318.817 W** board power, with 57 C temperature and 82.9 C
hotspot. Median telemetry query time is 0.083 ms per 500 ms poll.
The preceding accepted scene capture was 64.661 FPS: the new result is only
0.589 FPS higher, about 0.91%, with mean frame interval 0.139 ms shorter.
This is one historical control and one candidate capture, without a fresh
repeated game A/B. It does not establish a reproducible gain or isolate enc0
from output placement. WGP remains an optional experiment, and the overall
K performance gate stays open.
The full bounded run completes **35664 K frames** and **427968 native
launches**, including feature recreation and color-format reimports. There
are zero backend errors, CPU image copies, previous-frame outputs or recorded
crashes. `diagnostic_timeout` denotes the intentional ten-minute stop.
Numerical game scans are off, so the game's zero reported nonfinite count is
not a NaN/Inf validation result; use the separate finite/exact-frame gates.
Raw capture, environment, package identities and summary remain in
`test-results/silent-hill2-k-vshare-wgp-4k` and its diagnostic ZIP.
```powershell
.\scripts\windows\build-zluda-windows.ps1 -InstallRoot "$PWD\dist\zluda-windows-wgp-experiment"
.\scripts\windows\build-native-texture.ps1 -DlssDll "$PWD\nvngx_dlss.dll" -ZludaRoot "$PWD\dist\zluda-windows-wgp-experiment" -ShaderMode wgp -OutputDirectory "$PWD\build\private-textures-wgp-gfx1201"
.\scripts\windows\build-native-texture.ps1 -DlssDll "$PWD\nvngx_dlss.dll" -ZludaRoot "$PWD\dist\zluda-windows-wgp-experiment" -ShaderMode wgp -Kernel hiluma_engine_output_depthreg_mvhi_ldr_max_v2_rel -OutputDirectory "$PWD\build\private-textures-wgp-gfx1201"
.\scripts\windows\test-native-texture.ps1 -TextureRoot "$PWD\build\private-textures-wgp-gfx1201" -ControlTextureRoot "$PWD\build\private-textures-gfx1201" -ZludaRoot "$PWD\dist\zluda-windows-deferred-profile" -OutputResolution 3840x2160
```
The compiler placement unit passes, all seventeen hardware/software CTest gates
pass on the clean source build with ordinary CU defaults, and the six ZLUDA
gates also pass with WGP explicitly enabled. All seventeen patches (0002..0018)
apply to the clean pinned base `ee2f25a`. The numerical benchmark's rejected
F16 candidate stops after its two ordinary reference replays with differing
output and no timing samples (`k-replay-rejected-f16-gate-fixed`).
### Diagnostic environment and input dimensions
Some PowerShell/.NET hosts preserve removed process variables as empty strings.
Presence-based Rust diagnostic flags then select deferred profiling, while
empty codegen values can disable WMMA. The probe/game child environments now
omit empty `D4R_` keys; the probe restores codegen defaults for null or empty
values. Disabled WGP is also omitted so older runtimes keep their warm cache.
The texture test requires valid timing records instead of silently accepting
a profiling-free comparison. The initial candidate attempt times out during
NGX initialization with no accepted frame; later pre-fix comparisons have
correct RGB but no accepted GPU timings. Their logs remain in
`k-output-wgp-reference-small`, `k-output-wgp-reference-small-fixed` and
`k-output-wgp-reference-4k-fixed`; they are excluded from the table above.
The D3D12 harness now supports `--ngx-input-resolution WIDTHxHEIGHT`, exposed
as `-NgxInputResolution` in the probe runner and `-InputResolution` in the
texture/async gates. Output defaults and limits remain unchanged. This allows
the game's non-aligned input dimensions without inventing a larger output.
An initial attempt using 4518x2542 output is rejected by the harness's existing
range guard before GPU initialization; it is not a GPU or enc0 failure.
### Rejected K input-rounding overrides
The remaining translated K input is about 0.34-0.40 ms in the earlier
game-sized standalone profile. The private builder now exposes the existing
surface-store/rounding recipe for both tested input variants. These inputs
have **zero surface stores**: the actual change replaces eight round-half-away
idioms with the existing recipe, avoiding constrained arithmetic lowering.
It is not a native surface-store saving. Build metadata records the recipe's
counts and refuses a no-op override.
CU and WGP input objects are built from the same clean `fb0adc8` ZLUDA source.
Both retain accuracy mode and wave32; native FP8 remains disabled. The full
pipeline control and candidate share all thirteen other code objects,
including the same private WGP output tails. Only the two input overrides
are added. Four frames per variant at 512x288, then eight per variant at
2259x1271 -> 3840x2160 in CU mode, then eight in WGP mode with candidate-first
order complete **80 finite control/candidate frames**. All forty candidate
RGB images match exactly. Every expected input launch and valid serializing
timing is checked; raw logs and module SHA256 values are retained.
| Input median, ms | Translated, CU test | Native CU | Translated, WGP test | Native WGP |
| --- | ---: | ---: | ---: | ---: |
| LDR / regular depth / high-resolution MV | 0.350050 | 0.359425 | 0.351000 | 0.358800 |
| HDR / inverted depth / low-resolution MV | 0.512300 | 0.556350 | 0.412650 | 0.552100 |
These are separate-process diagnostic measurements with eight samples each;
other unchanged kernels also vary between processes. Neither input candidate
shows a saving. Both are rejected for game installation and default packaging;
the validated game configuration keeps translated input. Results:
`test-results/k-input-native-stores-small` (initial filename; no stores were
replaced), `k-input-rounding-cu-game-input`,
`k-input-rounding-wgp-game-input-reverse` and
`k-input-rounding-experiment-summary.json`.
The comparison script's `-KernelStage input` selects the input hit/timing gate.
`-CommonTextureRoot` holds output objects identical in both processes; staged
object hashes are recorded. The ordinary output gate now accepts a translated
control when its object is absent, while still requiring a native candidate.
Rebuilding the two original WGP output objects preserves their SHA256 values;
the default translated-output comparison also passes four finite frames and
two exact candidate RGB images
(`k-output-recipe-translated-control-regression`). Input objects remain private
and are excluded by the game's existing output-only installation allowlist.
Reproduce the WGP experiment with locally built, validated output tails:
```powershell
foreach ($kernel in @('hiluma_engine_input_depthinv_mvlo_hdr_v2_rel', 'hiluma_engine_input_depthreg_mvhi_ldr_v2_rel')) {
.\scripts\windows\build-native-texture.ps1 -DlssDll "$PWD\nvngx_dlss.dll" -ZludaRoot "$PWD\dist\zluda-windows-wgp-experiment" -ShaderMode wgp -Kernel $kernel -OutputDirectory "$PWD\build\private-input-textures-wgp-gfx1201"
}
.\scripts\windows\test-native-texture.ps1 -KernelStage input -TextureRoot "$PWD\build\private-input-textures-wgp-gfx1201" -CommonTextureRoot "$PWD\build\private-textures-wgp-gfx1201" -ZludaRoot "$PWD\dist\zluda-windows-deferred-profile" -InputResolution 2259x1271 -OutputResolution 3840x2160 -Iterations 8 -CandidateFirst -OutputDirectory "$PWD\test-results\k-input-rounding-wgp-retest"
```
Further game coverage (HIP kernel profiling disabled, GPU output checks enabled): Further game coverage (HIP kernel profiling disabled, GPU output checks enabled):
M at 1920x1080 completes 4833 frames, all finite. K at 3840x2160 completes M at 1920x1080 completes 4833 frames, all finite. K at 3840x2160 completes
4685 frames, all finite. Neither has a backend failure, CPU image copy or an 4685 frames, all finite. Neither has a backend failure, CPU image copy or an
+62
View File
@@ -0,0 +1,62 @@
D4R WINDOWS QUICK TEST -- READ THIS FIRST
This is an EXPERIMENTAL Windows 11 D3D12 build for RDNA3 / RDNA3.5 / RDNA4.
Only RX 9070 XT has been tested here. Other cards may fail.
The package detects the real GPU automatically. Do not rename any DLLs.
1. EXTRACT THE WHOLE ZIP to a normal writable folder, e.g. Desktop\d4r-test.
Do not run it inside the ZIP. Do not copy files into the game manually.
Already installed an older test? Close the game and run RESTORE-GAME.cmd
from that old package first, then extract this version to a new folder.
2. CLOSE YOUR GAME. If your distributor included the validated NVIDIA DLL pair,
it is selected automatically; you only need to choose the game executable.
Otherwise have these TWO ORIGINAL NVIDIA files ready:
_nvngx.dll version 32.0.16.1714
nvngx_dlss.dll version 310.9.1
The xdfnx-dev public test ZIP does not include them. Other versions are rejected.
The small _nvngx.dll inside this package is d4r, NOT the original NVIDIA file.
3. DOUBLE-CLICK START-K.cmd. Pick the actual game .exe when asked.
If asked, pick your two NVIDIA DLLs. These choices are remembered for the next run.
Next time, press Enter for the same game, or C to choose another.
If Steam/Epic is needed, keep it open. Choose a D3D12 game without anti-cheat.
If a launcher opens, type C and choose the real game executable next time.
4. IN THE GAME, select DLSS if available, otherwise FSR/XeSS for OptiScaler.
Leave frame generation OFF. First launch can compile for several minutes.
Keep the console open. Play for 30 seconds, check the image, then EXIT THE GAME.
START-M.cmd tests DLSS 4.5 preset M; START-K.cmd tests DLSS 4 preset K.
5. SEND THE SINGLE ZIP shown as "SEND THIS ONE FILE" in the console.
It is also in the results folder. Tell us your GPU, game, K/M and image result:
"correct image", "black screen", "crash" or "visual corruption".
A passing backend log does not automatically prove the image is correct.
Logs can contain local paths. Review them before posting them publicly.
TO UNINSTALL: close the game and double-click RESTORE-GAME.cmd.
Original replaced files are backed up and restored; changed files are preserved.
Restore the previous game BEFORE choosing a different game for the next test.
Always launch tests through START-K/M.cmd, not the usual game shortcut.
IF SOMETHING FAILS:
- Wrong NVIDIA DLL: select the exact versions above on the next run.
- No supported GPU / HIP failure: send the ZIP; do not spoof the GPU architecture.
- vkd3d/Wine detected: use the game's original native Windows D3D12 files.
- Access denied: extract to a writable folder. Do not disable system security.
- Do not start a second test while the first game is running.
No HIP SDK installation is required; the isolated runtime is included.
This uses synchronous interop and output checks. No debugger is attached by default.
Exit codes and loaded DLLs are logged. A debugger is only an explicit developer option.
Your local DLSS DLL is staged beside the d4r shim automatically.
It is a correctness diagnostic, NOT an FPS benchmark or an optimized daily-use build.
Colour, depth, motion and output remain in VRAM in the runtime fast path.
Runtime files are in files/. Cache/settings are created in work/.
Licenses are in licenses/. Corresponding sources are a SEPARATE release download;
testers do not need to download or build them. NVIDIA DLLs, when supplied by a
distributor, keep their separate license and are not part of the GPL source archive.
Report: https://github.com/countervolts/d4r/issues/10
Source: https://github.com/xdfnx-dev/d4r
+396 -16
View File
@@ -1,27 +1,340 @@
# Windows / RDNA4 port # Windows / RDNA4 port
Branch: `windows-rdna4`. Target: Windows 11 x64, RX 9070 XT, **gfx1201**. Branch: `windows-rdna4`. Experimental Windows build targets: **gfx1100..gfx1103,
gfx1150..gfx1154, gfx1200 / gfx1201**.
Validated physical hardware: Windows 11 x64, RX 9070 XT, **gfx1201**.
Priority: correct K, correct M, native Windows, same-frame output, then speed. Priority: correct K, correct M, native Windows, same-frame output, then speed.
Upstream integration: [draft PR #11](https://github.com/countervolts/d4r/pull/11) Upstream integration: [PR #11](https://github.com/countervolts/d4r/pull/11)
targets `countervolts/d4r:windows` from `xdfnx-dev/d4r:upstream-windows`. is merged into `countervolts/d4r:windows` at `c522101`.
The integration branch retains upstream's README with a Windows status section; The integration branch retains upstream's README with a Windows status section;
the fork's `main` keeps the dedicated English Windows README. The PR is open, the fork's `main` keeps the dedicated English Windows README. The maintainer
mergeable and allows maintainer edits. K performance remains a separate open gate. invitation is accepted; GitHub confirms `xdfnx-dev` has push access and the
Windows branch is unprotected. Further validated Windows changes can go there
directly. K performance remains a separate open gate.
This file records current results followed by the dated development history. Standalone DLSS K/M This file records current results followed by the dated development history. Standalone DLSS K/M
works on the real Windows GPU, including the patched OptiScaler frontend. works on the real Windows GPU, including the patched OptiScaler frontend.
Silent Hill 2 now renders through the Windows backend; gameplay and output Silent Hill 2 and Cyberpunk 2077 now render through the Windows backend; gameplay and output
validation coverage are recorded below. Performance and game coverage have validation coverage are recorded below. Performance and game coverage have
explicit limits; the validated package keeps conservative arithmetic. explicit limits; the validated package keeps conservative arithmetic.
## Milestone and gates ## Milestone and gates
**Current milestone: K performance remains open.** The user reports 49–51 FPS 2026-10-03 upstream Windows refresh: imported ayo's `e7e5c44` (LF patch
and about 53% GPU utilization in 4K Silent Hill 2, compared with roughly 80+ attributes) and `54c201a` (drag-in installation / per-game `d4r.ini`) while
FPS / 100% GPU utilization with FSR4 on this system. Functional K/M and the preserving their authorship. The target-independent NGX loader selects the
reproducible archive do not close this gate. Prioritize K queue scheduling and shim using the game's D3D12/HIP LUID. Player mode derives paths and the
host synchronization; postpone further M/FP8 optimization. These user readings validated codegen settings from `d4r.ini`, pins NVIDIA DLL identities and
are not a controlled same-scene benchmark. Do not infer a hardware CPU limit exposes K/M, async interop, logging, validation and cache settings. OptiScaler
from utilization alone. patch `0006` resolves relative library directories. These changes do not
replace the transformer arithmetic or establish a performance improvement.
The new packaging fixture passes 3/3 cases. Player config/loader probes pass
2/2 in both PowerShell 5 and PowerShell 7, including real gfx1201 LUID
selection without executing GPU kernels. Found and fixed a PowerShell 7
argument-quoting regression in `test-player-config.ps1`: only Legacy/PS5
passing needs escaped embedded quotes. Both new DLL targets compile through
CMake/Ninja, and all 20 RX 9070 XT CTest gates pass (6.21 seconds).
Reports are in `test-results/upstream-player-54c201a-ps5/`,
`upstream-player-54c201a-ps7/` and `upstream-release-54c201a/`.
The drag-in installation has not yet received a real-game K/M test, and
OptiScaler patch `0006` is not in the previously published frontend binary.
The existing public quick-test archive is unchanged and continues to use the
game-tested installer layout. Do not label it as the new drag-in release.
2026-10-03 community retest ([comment 5962444244](https://github.com/countervolts/d4r/issues/10#issuecomment-5962444244)):
seven new diagnostic bundles are read without running their contents. On
RX 9060 XT / gfx1200, Cyberpunk K/M complete **4365 / 1751** frames and
Dawnwalker K/M complete **6713 / 1690**. Each frame has a finite-output GPU
check; all required native transformer layers execute, exit codes are zero,
and backend failures / previous-frame output / CPU image-copy counters are
zero. These sessions use HDR input/output helpers; the local Cyberpunk tests
below used LDR. This establishes additional community backend execution,
not numerical or visual equivalence. The tester reports texture/LOD and motion
artifacts, which remain an explicit correctness gate before performance work.
Do not claim that NaN/Inf scans establish image correctness.
Mean recorded `interop_ngx_ms` is 18.623 / 50.237 ms for Cyberpunk K/M and
8.126 / 32.632 ms for Dawnwalker K/M. These are CPU completion intervals in
the synchronous diagnostic, including scheduling and validation, not isolated
GPU kernel times or a controlled cross-game/preset benchmark. K performance
remains open. Extracted logs, original ZIP hashes and parsed reports stay local
under `build/issue-10-latest/`; personal user paths are not committed.
The new Spider-Man 2 log exits `0xc0000005` before a DLSS frame, without a
usable crash stack. Assassin's Creed's process exits zero with no DLSS frames,
but Streamline reports repeated internal minidumps before capability discovery;
it remains inconclusive. Neither report establishes a native WMMA failure.
[The Witcher 3 report](https://github.com/countervolts/d4r/issues/10#issuecomment-5962808629)
selects the gfx1200 d4r shim instead of the original NGX core; the submitted
SHA256 matches that shim exactly. It is rejected before installation, rather
than failing a GPU workload. Launcher/Streamline crash diagnosis remains open.
Distributor-supplied NVIDIA DLL support: quick-test packaging accepts optional
`-NvidiaDirectory <directory>` and `-NvidiaLicensePath <applicable-license>`.
It stages the exact validated originals under `files/nvidia/`, adds both DLLs
and the separate license to integrity metadata and selects them automatically.
Explicit developer overrides still have strict SHA256 checks; bundled files
take precedence over stale remembered file selections. Restore remains
independent of DLL/GPU selection. This option is for local testing or a
distributor with appropriate permission; matching hashes are not a grant of
redistribution rights. The current public xdfnx-dev release remains unchanged
and contains no NVIDIA runtime binaries or private NVIDIA-derived kernels.
The [current NVIDIA SDK license](https://github.com/NVIDIA/DLSS/blob/main/LICENSE.txt)
has distribution conditions and a supplement limiting DLSS/NGX development
to systems with NVIDIA GPUs; the Linux bundle's contents alone do not resolve
permission for this Windows package. A local-only bundled package with the
user-provided pair passes eight isolated installer/restore/negative fixtures,
including stale-choice recovery and altered bundled-core rejection before any
game write. The ordinary no-DLL package also passes all six existing fixtures;
reports are in `test-results/windows-bundled-fixtures/` and
`windows-unbundled-fixtures/`. Real games are untouched by these fixture tests.
2026-10-02 issue #10 follow-up ([comment 5958769640](https://github.com/countervolts/d4r/issues/10#issuecomment-5958769640)):
four new logs confirm that RX 9060 XT / gfx1200 HIP discovery succeeds. The
Cyberpunk K/M runs never launch the transformer: numeric CUDA NGX capabilities
report `available=0, feature_init=BAD00004` (FeatureNotFound). The unmatched
adapter during enumeration is not the main Radeon: its runtime LUID matches.
GetFeatureRequirements now returns success with AdapterUnsupported for Intel /
WARP / another nonmatching adapter, while runtime initialization still requires
the exact HIP architecture and D3D12 LUID.
The feature discovery failure is reproduced on RX 9070 XT without a game,
using Cyberpunk's unchanged numeric application ID `100152211`. Keeping the
feature DLL only beside the official core in `d4r/vendor/` fails; placing it
beside the calling shim in `d4r/` succeeds. Putting it beside the EXE alone is
insufficient when the call originates in the shim. The installer now stages
the local feature DLL beside the shim and sets `Libraries.NvngxDlssPath` to
the directory, as required by OptiScaler's discovery logic. Project identity,
DLL contents, numeric driver ABI and capability checks remain unchanged.
The standalone reproduction also exposed concurrent/reentrant OptiScaler
device hook installation: Detours error `0x10dd` clears a live trampoline and
execution jumps to null. Source patch `0004` serializes installation/removal
and guards recursion; `0005` forwards numeric NGX init failures instead of
reporting success. The exact patched frontend source is
`86b21ea` (base `45a2001`), with pinned submodules verified directly before build.
The quick launcher no longer attaches a debugger by default: Assassin's Creed
Black Flag Resynced refused the previous debugger launch (`DEADC0DE`). Empty
stdout/stderr now parse correctly. A clean early process exit with zero DLSS
frames is inconclusive, as in the SM2 log; an external Steam restart is not
tracked and must not be reported as successful hardware validation.
Validation on the available RX 9070 XT: all eleven target builds pass; CTest
20/20; summary parser 9/9 including all four submitted logs. The layout
regression passes two expected FeatureNotFound cases (direct + OptiScaler),
then three finite frames each for direct K, OptiScaler K and OptiScaler M.
Every required native layer executes; GPU NaN/Inf checks pass, CPU frame copies
and frame age are zero. Direct/OptiScaler K RGB matches bit-for-bit for all
three frames (max absolute/relative error 0). Reports are under
`test-results/issue-10-5958769640/`, including `ctest.log`,
`summary-parser.json`, `layout-final/` and `build-all-targets.json`.
All five exported OptiScaler patches apply to the pinned upstream sources and
reproduce its exact committed files (`optiScaler-patches.json`). Reproduce the
standalone layout/error/coverage/RGB checks with:
```powershell
.\scripts\windows\test-ngx-feature-layout.ps1 -DiagnosticRoot .\dist\windows-gpu-coverage-gfx1201 -HipRoot .\.tools\therock-10.2.0a20260929\_rocm_sdk_core -ZludaRoot .\dist\zluda-windows-gpu-coverage -NativeRoot .\dist\windows-gpu-coverage-game-gfx1201\d4r\native -OptiScalerDll .\dist\optiscaler-windows-d4r\OptiScaler.dll -NgxCore .\_nvngx.dll -DlssDll .\nvngx_dlss.dll -OutputDirectory .\test-results\ngx-feature-layout
```
The rebuilt compact installer passes six isolated Windows PowerShell 5
fixtures (`quick-fixtures-final/summary.json`): exact backup/restore with the
original game DLSS DLL preserved, automatic gfx1201 selection, a child that
refuses debugging with empty stderr, and four negative input/package gates.
The child exits normally with no debugger and is classified as inconclusive,
with hardwareValidationCompleted=false. No installed game is changed by the
fixtures. Run `test-windows-quick-test.ps1` with the package and the local DLL
paths to reproduce them; it selects Windows PowerShell 5 when called from 7.
The follow-up compact release is `windows-quick-test-20261002-issue10` in the
fork, with all eleven targets and a separate exact GPL corresponding-source
asset. Native network arithmetic and conservative scheduling are unchanged.
The published [issue #10 prerelease](https://github.com/xdfnx-dev/d4r/releases/tag/windows-quick-test-20261002-issue10)
contains the fixes above. GitHub reports all three assets uploaded; their
sizes and SHA256 digests match the locally checked archives. The runtime has
283 files and 187 correctly targeted native code objects; exact corresponding
GPL sources are a separate download. Package source is `52d9872`, host binaries
are from `3d62b98`, and the patched frontend is `86b21ea`.
2026-10-03 local Cyberpunk validation, after the user's explicit authorization
to launch and enter gameplay: Cyberpunk 2077 **2.31** at
`D:\Games\Cyberpunk 2077\bin\x64\Cyberpunk2077.exe` loads a saved Night City
street scene through OptiScaler and d4r for both K and M on RX 9070 XT.
The game's existing FSR4 Performance selection is intercepted by OptiScaler's
DLSS backend; native hit logs establish the actual K/M execution. Numeric
application ID `100152211` is preserved. Input is **1920x1080**, output is
**3840x2160**, ray tracing and frame generation are off. The fresh developer
package uses the released binaries, synchronous interop, verbose logging and
full GPU output validation, with no attached debugger or private kernels.
| Session | Completed frames / GPU output checks | Native launches | Required native layers |
| --- | --- | --- | --- |
| K / preset 11 | 13,145 / 13,145 | 144,595 | All 11, each once per frame |
| M / preset 13 | 5,268 / 5,268 | 52,680 | All 5; enc3 runs six times per frame |
Both sessions exit normally (`0x0`), initialize the feature successfully
(`0x00000001`) and record **zero NaN/Inf outputs, previous-frame outputs, CPU
image-copy frames or backend failures**. Counts include menu/loading frames
as well as actual gameplay; they are not gameplay-only counts. Expected
translated input/output helpers remain in K, and M retains translated
enc0/dec0/downsample/post kernels alongside its five native transformer layers.
The visible street scene and saved screenshots confirm an image is produced;
these checks do not establish visual equivalence to DLSS on a physical RTX.
Separate 30-second stationary gameplay captures record **54.447 FPS / 94.800%
mean AMD ADLX GPU usage for K**, and **25.717 FPS / 96.267% for M**. These are
synchronous correctness diagnostics with verbose/output-check overhead,
not optimization benchmarks. The two saves/camera positions differ slightly;
do not treat this as a controlled K-versus-M performance comparison or compare
it directly with the optimized Silent Hill 2 configuration below.
Reports, stdout/stderr, loaded DLL/driver information, screenshots and diagnostic
ZIPs are retained under `test-results/issue-10-5958769640/` in
`cyberpunk-k-sync/`, `cyberpunk-m-sync/` and their `*-capture/` directories.
`cyberpunk-validation.json` records the automated counter/native-hit acceptance
checks and `cyberpunk-restore.json` records restoration. The game is closed and
the original installation restored, including removal of the test `dxgi.dll`.
Its existing game DLSS DLL was preserved with SHA256
`AD3E9C07EE864E9702032459A59C6825166766C2CB75BD0318D5626595693BDB`.
Existing graphics settings and progression saves were not edited by the test.
The reporter's **RX 9060 XT / gfx1200** kernels and game image still require
their own hardware retest; local gfx1201 results do not validate that card.
2026-10-02 quick-test packaging: one universal ZIP shares the identical
OptiScaler/ZLUDA/HIP runtime and selects among all eleven previously compiled
host/kernel targets using read-only HIP discovery. START-K.cmd / START-M.cmd
use file pickers, remember validated game/DLL paths, back up replaced files,
run synchronous output diagnostics, and produce one report ZIP. The initial
release attached a debugger; the issue #10 follow-up above removes that default.
RESTORE-GAME.cmd works without HIP discovery or NVIDIA DLL selection. No
runtime arithmetic changes or additional hardware-support claims are made.
The short English instructions are in `windows-quick-test.txt`; developer
sources and exact patched GPL OptiScaler/submodule sources are a separate asset.
An isolated Windows PowerShell 5 fixture verifies automatic gfx1201 selection,
spaces/Unicode paths, remembered selection, exact install/restore, and rejection
of wrong NGX/DLSS DLLs, vkd3d and modified package files before game writes.
No real game is launched by these packaging checks. Reproduce packaging with
`package-windows-quick-test.ps1 -PackageRoot <new-dir> -ArchivePath <zip>` and
`tools/windows/package_quick_test_sources.py --output <source-zip>` after commit.
The quick-test build is a correctness diagnostic, not the async/private-texture
performance configuration; K optimization remains open.
2026-10-02 coverage: all eleven listed RDNA3 / RDNA3.5 / RDNA4 targets compile
with separate host/device objects and guarded packages. Only RX 9070 XT has
physical validation here; no other-target GPU workloads are executed. The
newest realdody correction informs a Windows-only gfx11 LDS half-wave exchange
preserving native gfx11 WMMA layout. gfx12 and Linux keep their original
instruction path. A shared registry drives build, runtime, ELF checks and
scripts. HIP selection matches the D3D12 LUID before selecting an ordinal,
including same-target multi-GPU systems.
Issue #10 comment 5952822115 contains 32 logs / eight attempts: all discover
RX 9060 XT / gfx1200 but reject the old gfx1201 package before NGX runtime
initialization. Three also load a vkd3d-proton D3D12 proxy. The installer now
catches these conditions before changing game files and prints one diagnostic
ZIP. The next community gate is the gfx1200 package on native D3D12; those logs
do not establish a native K/M crash. Coverage, sources, fork credit, commands
and hardware limits are in [windows-gpu-support.md](windows-gpu-support.md).
The rebuilt gfx1201 passes 20/20 CTest gates on RX 9070 XT. K completes twelve
finite 4K burst/recreation frames at 2259x1271 input, with all six async RGB
images exactly matching sync. M completes eight finite control/candidate 4K
frames, with all four candidate RGB images matching the prior build exactly.
Both log every required native layer, zero CPU frame copies and frame age 0.
All 209 compiled objects have the correct ELF target. For all nineteen gfx1201
objects, instruction/constant sections match the previous validated build.
All eleven game directories stage strict K/M baselines without NVIDIA DLLs.
An isolated gfx1201 install/restore fixture preserves original bytes; wrong
gfx1200-on-gfx1201 and dummy vkd3d-proxy fixtures refuse before game changes
and produce one diagnostic ZIP each. The real game installation is untouched.
Results: `test-results/windows-gpu-coverage`; build logs and the compile-only
matrix report: `build/windows-gpu-coverage`. No physical multi-GPU, APU or
other-target validation is claimed. K performance remains open.
The final translator gate exposed a separate dependency gap: pinned LLVM 22
rejects `gfx1154` and aborts (`0xC0000409`) on a public PTX pattern. A minimal
source backport of official LLVM `7a0829e41228` adds the processor's real
11.5.4 features, parser and ELF identity. It is exported under
`patches/zluda-llvm`, applied idempotently by the LLVM builder and included with
its base/hash in package metadata. LLVM and ZLUDA are rebuilt; all 33 offline
pattern/F16-reference/WMMA compilations pass across the eleven targets without
GPU execution. The existing strict gfx12 F16 reference lowering remains intact.
The rebuilt ZLUDA passes 20/20 RX 9070 XT CTest gates. Its twelve further K
4K burst/recreation frames are finite and all six async RGB images are exact;
three sync RGB images also exactly match the previous compiler's captures.
Four further M 4K frames are finite and match all four previous-compiler RGB
images exactly. Both require native transformer hits, zero CPU frame copies
and frame age 0. Results: `k-new-zluda`, `m-new-zluda` and compiler comparison
logs under `test-results/windows-gpu-coverage`. Other GPUs remain unverified.
**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
and retain queue/synchronization checks; postpone further M/FP8 optimization.
The user's approximately 80+ FPS with FSR4 is not a captured same-scene control.
The preceding unprofiled K/4K capture records **64.661 FPS** with direct AMD
ADLX mean usage **93.183%**, core clock **3124.667 MHz** and board power
**316.717 W**. The user identified the previous 63% reading as Task Manager,
which is distinct from ADLX's device-wide metric. This capture places GPU kernel cost
at the center of the next K investigation, while retaining queue measurements.
The shared-V enc0 + private WGP output candidate records **65.250 FPS** over
1954 presents in the user-confirmed stationary scene, at the same
2259x1271 -> 3840x2160 dimensions. ADLX mean usage is **93.500%**, core clock
**3118.400 MHz** and board power **318.817 W**. The 0.589 FPS historical
comparison is too small to establish a reproducible gain; a fresh repeated
game control remains necessary before selecting WGP by default. The bounded
ten-minute run completes **35664 K frames / 427968 native launches**, with
zero backend errors, CPU image copies, previous-frame outputs or recorded
crashes. Output scans are disabled in this unprofiled game run; standalone
finite/exact-image gates supply its numerical coverage. Results:
`test-results/silent-hill2-k-vshare-wgp-4k`. See
[windows-performance.md](windows-performance.md#combined-k-game-check).
Native replay benchmarks now compare fresh exact allocations before timing,
support event/dispatch profiling and paired batches, retain independent host
completion intervals, and reject invalid or incomplete timings. Eleven K
same-module controls pass both timing modes. Real K/4K enc0/enc1/dec0 captures
and CU/WGP, cache, token-tiling and packed-storage experiments are recorded in
[windows-performance.md](windows-performance.md#controlled-native-replay-timings).
Packed storage lowers enc1 VGPRs but does not speed it up. Enc0's shared-V
implementation preserves every tested output and gives about a 5% isolated
layer saving at both small and 4K workloads; the strict gfx12 baseline now
selects it. Other K layers, M and arithmetic remain unchanged. The previous direct-F16 experiment's
saved-control selection was corrected to the current strict-normalization
baseline; it remains rejected. K performance is still an open gate.
The private output-tail WGP candidate gives a lower event median in both
control-first and candidate-first K/4K tests, with exact RGB across 96 finite
control/candidate frames. This is a separate-process diagnostic result, not
game FPS. ZLUDA source `fb0adc8714f57647320f0601533ec50331e378b0` is rebuilt
cleanly in `dist/zluda-windows-wgp-experiment`; optional patch 0018 preserves
CU defaults and wave/arithmetic semantics. All seventeen CTest gates pass
with defaults, and all six ZLUDA gates also pass with WGP explicitly enabled.
All seventeen patches (0002..0018) apply to clean base `ee2f25a`.
Enc0 full-frame controls pass at 1920x1080 and the game's 2259x1271 input,
both outputting 3840x2160: 28 frames finite, all 14 candidate RGB images exact.
The exact CMake enc0 object passes a fresh replay/paired benchmark; the other
fifteen ordinary K/M objects retain their prior SHA256. The combined enc0 +
WGP-output async/burst/recreation gate passes twelve further game-sized frames
with all six async RGB images exact (`k-enc0-shared-wgp-async-game-input`).
`-InputResolution` exposes actual render dimensions without changing the
harness's output bounds. Empty task environment variables are removed from
probe/game children so disabled diagnostic flags stay disabled; GPU comparisons
now require actual valid timing samples. Commands, rejected experiments and
the initial diagnostic/cache failures are recorded in `windows-performance.md`.
A subsequent input-rounding CU/WGP experiment completes eighty finite frames
with all forty candidate RGB images exact, holding the other thirteen objects
identical. Neither input candidate improves standalone timing; the game keeps
translated input. The private recipe/test tools retain these reproducible
experiments, while the installer still accepts only the two validated output
variants. Rebuilt output objects retain their SHA256 values, and the ordinary
translated-output control comparison passes four further finite frames. See
[windows-performance.md](windows-performance.md#rejected-k-input-rounding-overrides).
The next diagnostics add `-ProfileCudaApi` (ZLUDA patch 0015): aggregate host The next diagnostics add `-ProfileCudaApi` (ZLUDA patch 0015): aggregate host
CUDA API durations without HIP-event waits. Boundary stages now separate CUDA API durations without HIP-event waits. Boundary stages now separate
@@ -82,7 +395,70 @@ measures median input/output copies at 0.061/0.208 ms and external span at
as HIP. PresentMon 2.6.0 capture/report tooling is added; its first trace as HIP. PresentMon 2.6.0 capture/report tooling is added; its first trace
contains changing presentation modes and is not a controlled capture of contains changing presentation modes and is not a controlled capture of
the user's 62 FPS scene. See `windows-performance.md` for exact metrics and the user's 62 FPS scene. See `windows-performance.md` for exact metrics and
the stable foreground-scene gate that remains open. the initial capture limitations. A subsequent user-confirmed foreground-scene
capture completes 1902 presents at 63.574 FPS, all Independent Flip / SyncInterval
0. PresentMon mean GPU busy/wait is 14.504/1.110 ms; this is not sensor
utilization. The containing ten-minute run completes 36591 K frames without
backend errors; median external span is 5.399 ms. Log bundle:
`test-results/silent-hill2-k-steady-profile-4k`.
The next candidate adds bounded, deferred HIP-event sampling and opt-in batched
GPU input conversion/array copies. It retains one all-stream input completion
before NGX and every D3D12/output dependency. All seventeen CTest gates pass,
including asynchronous pitched FP16/FP32 array copies and guard checks.
The deferred profiler's tiny PTX diagnostic verifies 516 launches and safe
default-stream skipping; negative HIP elapsed times are retained as invalid
samples, with cause unresolved. This DLSS K binary uses the legacy default
stream, which needs explicit opt-in for timestamp sampling. The depth/stencil
and packed-format burst/recreation gates pass 24 full K/4K frames: all twelve
candidate RGB images match their fresh unbatched synchronous controls exactly,
with finite RGBA. Results: `test-results/k-batch-deferred-4k` and
`test-results/k-batch-packed-4k`. The candidate remains
disabled by default; see `windows-performance.md` for semantics and commands.
Current candidate source is ZLUDA `67127dde599879ada022d6439383d682e09e0b36`
in `dist/zluda-windows-deferred-profile`, containing patches 0016/0017.
All sixteen patches (0002 through 0017) apply to clean pinned base `ee2f25a`.
The committed ZLUDA is rebuilt with a clean worktree and the four-way K/4K
gate is repeated: twelve frames pass, all six candidate RGB outputs match
their controls exactly, and all six async boundary timestamp records survive
slot reuse/release. Results: `test-results/k-batch-committed-source-4k`.
The same negative HIP event-timing issue reproduces with stable SDK 7.2 as
well as TheRock; PTX output/guards remain correct. It also affects a native K
dec4 timing sample, so valid-looking events alone are not a timing-accuracy
gate. No hardware utilization or kernel speedup is inferred from these samples.
The committed batched-input game run completes **35673 K/4K frames** without
backend errors, CPU image copies, previous-frame output or recorded crashes.
The user-confirmed 30-second PresentMon window records **64.265 FPS** (1925
presents), compared with the prior unbatched 63.574 FPS. Both keep Independent
Flip / SyncInterval 0 and 2259x1271 -> 3840x2160. This small single-pair
difference is not a repeatable speedup gate. Whole-run median input preparation
falls from 0.482 to 0.332 ms, while median external span is 5.438 versus
5.399 ms. Results: `test-results/silent-hill2-k-batch-inputs-4k`.
K performance remains open and batching remains opt-in. The next diagnostic
adds a separate read-only AMD ADLX usage/clock/power capture alongside
PresentMon. Its local RX 9070 XT smoke test passes, retaining unsupported
metrics as absent and preserving the driver's raw timestamp. See
`windows-performance.md` for build/capture commands and SDK packaging limits.
The combined capture smoke test passes, followed by the user-confirmed
1935-present/60-poll measurement recorded at the top of this document. Logs
and runtime/DLL hashes: `test-results/silent-hill2-k-telemetry-unprofiled-4k`.
Its ten-minute game summary completes **37187 K/4K frames**, zero backend
errors/CPU image copies/previous-frame outputs or recorded crashes.
The next isolated K enc1 experiment replaces FP32 WMMA + FP16 rounding with
gfx12's packed FP16 accumulator instruction. ISA conversions and VGPRs fall,
but the real output changes and reference PSNR falls. A 64-matrix public GPU
reproducer shows the direct instruction differs from FP32 + FP16 rounding
already at a single K16 step while the adapter is exact against the packed
instruction. The experiment is rejected at enc1; no subsequent layer or game
uses it. Baseline preservation takes priority over the instruction reduction.
`D4R_K_F16_WMMA` remains disabled in every normal build. Reproducer/results and
exact metrics are in `windows-performance.md`.
All sixteen ordinary K/M objects remain byte-identical after rebuilding with
the disabled experimental code. All seventeen CTest gates pass; gfx1101 enc1
compile-only compatibility and the ready-made public WMMA script also pass.
The public prerelease `windows-rdna4-dev-20261002-a346d76` is published to The public prerelease `windows-rdna4-dev-20261002-a346d76` is published to
`xdfnx-dev/d4r`, containing source commit `a346d76` and ZLUDA `a1c506f`. `xdfnx-dev/d4r`, containing source commit `a346d76` and ZLUDA `a1c506f`.
@@ -1059,7 +1435,10 @@ Pinned additional build dependencies:
rust-std and rust-mingw archives and installs only under `.tools`. rust-std and rust-mingw archives and installs only under `.tools`.
* ZLUDA LLVM submodule `ff4dc1f7c9e1c64d4d69e40f4ed30c2280a96dfd`, * ZLUDA LLVM submodule `ff4dc1f7c9e1c64d4d69e40f4ed30c2280a96dfd`,
`llvm-config` reports `22.0.0git`. AMDGPU/LLVM/LLD are built with LLVM-MinGW `llvm-config` reports `22.0.0git`. AMDGPU/LLVM/LLD are built with LLVM-MinGW
`20260922`; no Linux build host or runtime is used. `20260922`; no Linux build host or runtime is used. The Windows builder adds
`patches/zluda-llvm/0001-backport-gfx1154.patch`, adapted from official LLVM
`7a0829e41228513299c5108685b0bc127463c6a1`. Rebuild and relink ZLUDA when
updating; native Clang 24 target support alone does not cover translated PTX.
* HiGHS submodule `364c83a51e44ba6c27def9c8fc1a49b1daf5ad5c`. * HiGHS submodule `364c83a51e44ba6c27def9c8fc1a49b1daf5ad5c`.
* The LLVM IR helper producer is stable HIP 7.2/LLVM 21. The final target is * The LLVM IR helper producer is stable HIP 7.2/LLVM 21. The final target is
selected by the translator from HIP `gfx1201`; generic helper generation selected by the translator from HIP `gfx1201`; generic helper generation
@@ -1371,7 +1750,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 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. 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 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 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, provided ZLUDA DLL by absolute path, matches the adapter against HIP PCI identity,
+31
View File
@@ -0,0 +1,31 @@
#pragma once
#include <stdint.h>
// Windows gfx11 portability workaround informed by realdody/d4r 1897b4a.
// The reported permlanex16 failure duplicates the even K value into the odd
// slot. Retain the native gfx11 layout and exchange through per-wave LDS.
// Other platforms and gfx12 keep their original instruction sequence.
__attribute__((device, always_inline)) static inline uint32_t d4r_wave32_other_half(uint32_t value)
{
#if defined(D4R_WINDOWS_GFX11_LANE_EXCHANGE) && defined(__GFX11__)
__attribute__((shared)) volatile uint32_t scratch[512];
const uint32_t tid = __builtin_amdgcn_workitem_id_x() + __builtin_amdgcn_workgroup_size_x() *
(__builtin_amdgcn_workitem_id_y() + __builtin_amdgcn_workgroup_size_y() * __builtin_amdgcn_workitem_id_z());
// All native K/M launch bounds are <=512. Do not silently corrupt LDS if
// this helper is later reused by a larger workgroup.
if (tid >= 512) __builtin_trap();
scratch[tid] = value;
__builtin_amdgcn_fence(__ATOMIC_RELEASE, "wavefront");
__builtin_amdgcn_wave_barrier();
__builtin_amdgcn_fence(__ATOMIC_ACQUIRE, "wavefront");
const uint32_t result = scratch[tid ^ 16u];
// Protect scratch reuse by the next exchange in this wave. No block-wide
// rendezvous: different waves can reach GEMMs in a different order.
__builtin_amdgcn_fence(__ATOMIC_RELEASE, "wavefront");
__builtin_amdgcn_wave_barrier();
__builtin_amdgcn_fence(__ATOMIC_ACQUIRE, "wavefront");
return result;
#else
return __builtin_amdgcn_permlanex16(value, value, 0x76543210u, 0xfedcba98u, false, false);
#endif
}
+3 -3
View File
@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include "wave32_exchange.h"
// d4r's gfx11 interface: each lane supplies 16 FP16 K operands. The 8 FP32 // d4r's gfx11 interface: each lane supplies 16 FP16 K operands. The 8 FP32
// outputs have logical row 2*i + lane/16 and logical column lane%16. // outputs have logical row 2*i + lane/16 and logical column lane%16.
@@ -13,8 +14,7 @@ using d4r_wmma_f8 = float __attribute__((ext_vector_type(8)));
__attribute__((device, always_inline)) static inline float d4r_wmma_other_half(float value) __attribute__((device, always_inline)) static inline float d4r_wmma_other_half(float value)
{ {
const uint32_t bits = __builtin_bit_cast(uint32_t, value); const uint32_t bits = __builtin_bit_cast(uint32_t, value);
const uint32_t swapped = __builtin_amdgcn_permlanex16( const uint32_t swapped = d4r_wave32_other_half(bits);
bits, bits, 0x76543210u, 0xfedcba98u, false, false);
return __builtin_bit_cast(float, swapped); return __builtin_bit_cast(float, swapped);
} }
@@ -49,7 +49,7 @@ d4r_wmma_legacy_layout(d4r_wmma_h16 a, d4r_wmma_h16 b, d4r_wmma_f8 c)
result[i] = (row >> 3) == group ? d12[new_index] : remote_d[new_index]; result[i] = (row >> 3) == group ? d12[new_index] : remote_d[new_index];
} }
return result; return result;
#elif defined(__gfx1100__) || defined(__gfx1101__) || defined(__gfx1102__) || defined(__gfx1103__) #elif defined(__GFX11__)
return __builtin_amdgcn_wmma_f32_16x16x16_f16_w32(a, b, c); return __builtin_amdgcn_wmma_f32_16x16x16_f16_w32(a, b, c);
#else #else
#error "d4r WMMA backend requires a validated gfx11 or gfx12 target" #error "d4r WMMA backend requires a validated gfx11 or gfx12 target"
+16 -1
View File
@@ -18,6 +18,7 @@
// Only clang builtins are used (the texture tails compile with -nogpuinc -nogpulib). // Only clang builtins are used (the texture tails compile with -nogpuinc -nogpulib).
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include "wave32_exchange.h"
#ifndef D4R_WMMA_LAYOUT #ifndef D4R_WMMA_LAYOUT
#if defined(__GFX12__) #if defined(__GFX12__)
@@ -56,7 +57,7 @@ WM_FN uint32_t wm_half()
// value of the lane with the same index in the other half of the wave // value of the lane with the same index in the other half of the wave
WM_FN uint32_t wm_other_half(uint32_t v) WM_FN uint32_t wm_other_half(uint32_t v)
{ {
return __builtin_amdgcn_permlanex16(v, v, 0x76543210u, 0xfedcba98u, false, false); return d4r_wave32_other_half(v);
} }
WM_FN float wm_other_half_f(float v) WM_FN float wm_other_half_f(float v)
{ {
@@ -229,6 +230,20 @@ WM_FN wm_f8v wm_mma(const wm_op& a, const wm_op& b, wm_f8v c)
{ {
return __builtin_amdgcn_wmma_f32_16x16x16_f16_w32_gfx12(__builtin_bit_cast(wm_h8, a), __builtin_bit_cast(wm_h8, b), c); return __builtin_amdgcn_wmma_f32_16x16x16_f16_w32_gfx12(__builtin_bit_cast(wm_h8, a), __builtin_bit_cast(wm_h8, b), c);
} }
#ifdef D4R_K_F16_WMMA
// Rejected K experiment: direct packed F16 changes the arithmetic on gfx1201.
// The public WMMA reproducer and enc1 replay are in tools/windows and docs.
// Experimental gfx12 packed FP16 C/D. K's strict baseline enters each K16
// step with an already FP16-rounded accumulator. Keep the existing logical
// eight-row contract; only this instruction/register representation changes.
WM_FN wm_f8v wm_mma_f16_step(const wm_op& a, const wm_op& b, wm_f8v c)
{
const wm_h8 hc = __builtin_convertvector(c, wm_h8);
const wm_h8 hd = __builtin_amdgcn_wmma_f16_16x16x16_f16_w32_gfx12(
__builtin_bit_cast(wm_h8, a), __builtin_bit_cast(wm_h8, b), hc);
return __builtin_convertvector(hd, wm_f8v);
}
#endif
#else #else
WM_FN wm_f8v wm_mma11(const wm_u8v& a, const wm_u8v& b, wm_f8v c) WM_FN wm_f8v wm_mma11(const wm_u8v& a, const wm_u8v& b, wm_f8v c)
{ {
+6 -1
View File
@@ -1,5 +1,10 @@
// CU mode: the waves of a window share one L0 cache (1-6% faster here; slower in the wide layers) // CU mode: the waves of a window share one L0 cache (1-6% faster here; slower in the wide layers)
// d4r-build-flags: -mcumode // d4r-build-flags: -mcumode
#define PWIN_F32ACC #define PWIN_F32ACC
#ifndef D4R_K_POS_MT
#define D4R_K_POS_MT 2
#elif !defined(__GFX12__) || !defined(D4R_K_FP16_BASELINE)
#error "experimental position token tiling requires gfx12 strict K baseline"
#endif
#include "pwin_pos.h" #include "pwin_pos.h"
POS_DECODER(dltss_pwin_dec0_layer, 2, 32, 64, 40, 48, 2) POS_DECODER(dltss_pwin_dec0_layer, 2, 32, 64, 40, 48, D4R_K_POS_MT)
+15 -1
View File
@@ -1,5 +1,19 @@
// CU mode: the waves of a window share one L0 cache (1-6% faster here; slower in the wide layers) // CU mode: the waves of a window share one L0 cache (1-6% faster here; slower in the wide layers)
// d4r-build-flags: -mcumode // d4r-build-flags: -mcumode
#define PWIN_F32ACC #define PWIN_F32ACC
#ifndef D4R_K_POS_MT
#define D4R_K_POS_MT 2
#elif !defined(__GFX12__) || !defined(D4R_K_FP16_BASELINE)
#error "experimental position token tiling requires gfx12 strict K baseline"
#endif
// Reuse V across the window's waves. Paired gfx1201 replays retain every half
// code and reduce enc0 time by about 5%; gfx11 and arithmetic experiments keep
// their previous path. D4R_K_PRIVATE_V supplies the original A/B control.
#if defined(__GFX12__) && defined(D4R_K_FP16_BASELINE) && !defined(D4R_K_PRIVATE_V) && \
!defined(D4R_K_PACKED_ACC) && !defined(D4R_K_F16_WMMA)
#ifndef POS_VSHARE
#define POS_VSHARE
#endif
#endif
#include "pwin_pos.h" #include "pwin_pos.h"
POS_ENCODER(dltss_pwin_enc0_layer, 2, 32, 64, 16, 2) POS_ENCODER(dltss_pwin_enc0_layer, 2, 32, 64, 16, D4R_K_POS_MT)
+11
View File
@@ -1,5 +1,16 @@
// CU mode: the waves of a window share one L0 cache (1-6% faster here; slower in the wide layers) // CU mode: the waves of a window share one L0 cache (1-6% faster here; slower in the wide layers)
// d4r-build-flags: -mcumode // d4r-build-flags: -mcumode
#define PWIN_F32ACC #define PWIN_F32ACC
#ifdef D4R_K_ENC1_POS_MT
#if !defined(__GFX12__) || !defined(D4R_K_FP16_BASELINE)
#error "experimental enc1 token tiling requires gfx12 strict K baseline"
#endif
#if D4R_K_ENC1_POS_MT != 1 && D4R_K_ENC1_POS_MT != 2 && D4R_K_ENC1_POS_MT != 4
#error "enc1 token tiles per wave must be 1, 2 or 4"
#endif
#include "pwin_pos.h"
POS_ENCODER(dltss_pwin_enc1_layer, 2, 64, 64, 0, D4R_K_ENC1_POS_MT)
#else
#include "pwin_layer.h" #include "pwin_layer.h"
PWIN_ENCODER_POS(dltss_pwin_enc1_layer, 2, 64, 64, true) PWIN_ENCODER_POS(dltss_pwin_enc1_layer, 2, 64, 64, true)
#endif
+36 -5
View File
@@ -23,6 +23,16 @@ typedef _Float16 half_t;
typedef _Float16 hv2 __attribute__((ext_vector_type(2))); typedef _Float16 hv2 __attribute__((ext_vector_type(2)));
typedef _Float16 h16 __attribute__((ext_vector_type(16))); typedef _Float16 h16 __attribute__((ext_vector_type(16)));
typedef float f8v __attribute__((ext_vector_type(8))); typedef float f8v __attribute__((ext_vector_type(8)));
#ifdef D4R_K_PACKED_ACC
#if !defined(__GFX12__) || !defined(D4R_K_FP16_BASELINE) || defined(D4R_K_F16_WMMA)
#error "packed accumulator storage requires gfx12 strict F32 WMMA baseline"
#endif
// Keep already-rounded values packed between steps. Every matrix instruction
// still uses F32 C/D and rounds once to half afterwards, like the baseline.
typedef _Float16 acc8v __attribute__((ext_vector_type(8)));
#else
typedef f8v acc8v;
#endif
typedef uint32_t u8v __attribute__((ext_vector_type(8))); typedef uint32_t u8v __attribute__((ext_vector_type(8)));
typedef uint32_t u4v __attribute__((ext_vector_type(4))); typedef uint32_t u4v __attribute__((ext_vector_type(4)));
@@ -78,8 +88,18 @@ __device__ __forceinline__ half_t l2_sum(const half_t* row)
// one k16 step with the f16 accumulator of NVIDIA's f16 wmma (rounded after the step) // one k16 step with the f16 accumulator of NVIDIA's f16 wmma (rounded after the step)
// PWIN_F32ACC: keep the accumulator in f32 through the chain (rounded to f16 where the values are used) // PWIN_F32ACC: keep the accumulator in f32 through the chain (rounded to f16 where the values are used)
__device__ __forceinline__ f8v mma16(const op_t& a, const op_t& b, f8v c) __device__ __forceinline__ acc8v mma16(const op_t& a, const op_t& b, acc8v c)
{ {
#ifdef D4R_K_PACKED_ACC
const f8v fc = __builtin_convertvector(c, f8v);
const f8v d = wm_mma(a, b, fc);
return __builtin_convertvector(d, acc8v);
#elif defined(D4R_K_F16_WMMA)
#if !defined(__GFX12__) || D4R_WMMA_LAYOUT != 12 || !defined(D4R_K_FP16_BASELINE)
#error "experimental packed F16 WMMA requires gfx12 native layout and strict K baseline"
#endif
return wm_mma_f16_step(a, b, c);
#else
f8v d = wm_mma(a, b, c); f8v d = wm_mma(a, b, c);
#if !defined(PWIN_F32ACC) || defined(D4R_K_FP16_BASELINE) #if !defined(PWIN_F32ACC) || defined(D4R_K_FP16_BASELINE)
#pragma unroll #pragma unroll
@@ -87,16 +107,22 @@ __device__ __forceinline__ f8v mma16(const op_t& a, const op_t& b, f8v c)
d[i] = (float)(half_t)d[i]; d[i] = (float)(half_t)d[i];
#endif #endif
return d; return d;
#endif
} }
__device__ __forceinline__ f8v splat8(float v) __device__ __forceinline__ acc8v splat8(float v)
{ {
return (f8v){v, v, v, v, v, v, v, v}; #ifdef D4R_K_PACKED_ACC
const half_t h = (half_t)v;
return (acc8v){h, h, h, h, h, h, h, h};
#else
return (acc8v){v, v, v, v, v, v, v, v};
#endif
} }
__device__ __forceinline__ uint32_t other_half(uint32_t v) __device__ __forceinline__ uint32_t other_half(uint32_t v)
{ {
return __builtin_amdgcn_permlanex16(v, v, 0x76543210u, 0xfedcba98u, false, false); return wm_other_half(v);
} }
__device__ __forceinline__ uint32_t pack2(half_t lo, half_t hi) __device__ __forceinline__ uint32_t pack2(half_t lo, half_t hi)
@@ -120,9 +146,14 @@ __device__ __forceinline__ op_t operand_from_dt(const half_t own[8])
return wm_op_from_acc(own); return wm_op_from_acc(own);
} }
__device__ __forceinline__ op_t operand_from_f8(f8v d) __device__ __forceinline__ op_t operand_from_f8(acc8v d)
{ {
#ifdef D4R_K_PACKED_ACC
const half_t h[8] = {d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]};
return wm_op_from_acc(h);
#else
return wm_op_from_f8(d); return wm_op_from_f8(d);
#endif
} }
// element c (0..15) of a row of 16 halves (VALU code; not a WMMA operand) // element c (0..15) of a row of 16 halves (VALU code; not a WMMA operand)
+12 -12
View File
@@ -163,7 +163,7 @@ __device__ __forceinline__ half_t gelu(half_t x)
} }
// gelu of 8 accumulator values as 4 packed f16 pairs (the same f16 operations as gelu(), element-wise) // gelu of 8 accumulator values as 4 packed f16 pairs (the same f16 operations as gelu(), element-wise)
__device__ __forceinline__ void gelu8(const f8v& d, half_t g[8]) __device__ __forceinline__ void gelu8(const acc8v& d, half_t g[8])
{ {
const hv2 k1 = (hv2)(half_t)__builtin_bit_cast(float, 0x3ED306EBu), k2 = (hv2)(half_t)__builtin_bit_cast(float, 0x3DA60DD6u); const hv2 k1 = (hv2)(half_t)__builtin_bit_cast(float, 0x3ED306EBu), k2 = (hv2)(half_t)__builtin_bit_cast(float, 0x3DA60DD6u);
#pragma unroll #pragma unroll
@@ -283,7 +283,7 @@ __device__ void pwin_core(const PwinParams& p, const u8v* __restrict__ img, cons
block_sync(); // V of all 64 tokens is computed by every wave from hb block_sync(); // V of all 64 tokens is computed by every wave from hb
STAMP(2); STAMP(2);
// ---- attention, one head at a time; the output projection accumulates head by head // ---- attention, one head at a time; the output projection accumulates head by head
f8v acc[KT]; acc8v acc[KT];
#pragma unroll #pragma unroll
for (int nt = 0; nt < KT; ++nt) for (int nt = 0; nt < KT; ++nt)
{ {
@@ -310,7 +310,7 @@ __device__ void pwin_core(const PwinParams& p, const u8v* __restrict__ img, cons
#pragma unroll #pragma unroll
for (int nt = 0; nt < 2; ++nt) for (int nt = 0; nt < 2; ++nt)
{ {
f8v d = splat8(0.0f); acc8v d = splat8(0.0f);
for (int ks = 0; ks < KT; ++ks) for (int ks = 0; ks < KT; ++ks)
d = mma16(op_lds(&hb[16 * wv + m][16 * ks]), op_img(img, (L::T_QKV + ((h * 3 + 2) * KT + ks) * 2 + nt) * 16 + m), d); d = mma16(op_lds(&hb[16 * wv + m][16 * ks]), op_img(img, (L::T_QKV + ((h * 3 + 2) * KT + ks) * 2 + nt) * 16 + m), d);
op_img_store(&vsh[h][nt][0][0], wv * 16 + m, operand_from_f8(d)); op_img_store(&vsh[h][nt][0][0], wv * 16 + m, operand_from_f8(d));
@@ -333,7 +333,7 @@ __device__ void pwin_core(const PwinParams& p, const u8v* __restrict__ img, cons
{ {
op_t qop[2]; op_t qop[2];
// six independent chains (Q, K, V x 2 n-tiles), each over kt in order // six independent chains (Q, K, V x 2 n-tiles), each over kt in order
f8v dq[6]; acc8v dq[6];
#pragma unroll #pragma unroll
for (int u = 0; u < 6; ++u) for (int u = 0; u < 6; ++u)
dq[u] = splat8(0.0f); dq[u] = splat8(0.0f);
@@ -349,7 +349,7 @@ __device__ void pwin_core(const PwinParams& p, const u8v* __restrict__ img, cons
#pragma unroll #pragma unroll
for (int nt = 0; nt < 2; ++nt) for (int nt = 0; nt < 2; ++nt)
{ {
const f8v d = dq[2 * j + nt]; const acc8v d = dq[2 * j + nt];
if (j == 0) if (j == 0)
qop[nt] = operand_from_f8(d); qop[nt] = operand_from_f8(d);
else if (j == 1) else if (j == 1)
@@ -370,7 +370,7 @@ __device__ void pwin_core(const PwinParams& p, const u8v* __restrict__ img, cons
for (int kt = 0; kt < 4; ++kt) for (int kt = 0; kt < 4; ++kt)
{ {
const u4v bi = bias[((h * 4 + wv) * 4 + kt) * 32 + l]; const u4v bi = bias[((h * 4 + wv) * 4 + kt) * 32 + l];
f8v d; acc8v d;
#pragma unroll #pragma unroll
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
{ {
@@ -404,7 +404,7 @@ __device__ void pwin_core(const PwinParams& p, const u8v* __restrict__ img, cons
#pragma unroll #pragma unroll
for (int nt = 0; nt < 2; ++nt) for (int nt = 0; nt < 2; ++nt)
{ {
f8v d = splat8(0.0f); acc8v d = splat8(0.0f);
#pragma unroll #pragma unroll
for (int kt = 0; kt < 4; ++kt) for (int kt = 0; kt < 4; ++kt)
{ {
@@ -462,7 +462,7 @@ __device__ void pwin_core(const PwinParams& p, const u8v* __restrict__ img, cons
#pragma unroll #pragma unroll
for (int hn = 0; hn < 2; ++hn) for (int hn = 0; hn < 2; ++hn)
{ {
f8v d; acc8v d;
{ {
half_t vv_[8]; half_t vv_[8];
@@ -555,7 +555,7 @@ __device__ void pwin_encoder(const PwinParams& p0, const u8v* __restrict__ img,
#pragma unroll #pragma unroll
for (int nt = 0; nt < C / 16; ++nt) for (int nt = 0; nt < C / 16; ++nt)
{ {
f8v d; acc8v d;
{ {
half_t vv_[8]; half_t vv_[8];
dvec8(p0.w, 32 * nt, vv_); dvec8(p0.w, 32 * nt, vv_);
@@ -603,7 +603,7 @@ __device__ void pwin_encoder(const PwinParams& p0, const u8v* __restrict__ img,
const uint8_t* w = p.w; const uint8_t* w = p.w;
for (int vt = wv; vt < L::PMT; vt += 4) for (int vt = wv; vt < L::PMT; vt += 4)
{ {
f8v d; acc8v d;
{ {
half_t vv_[8]; half_t vv_[8];
@@ -753,7 +753,7 @@ __device__ void pwin_decoder(const PwinParams& p, const u8v* __restrict__ img, c
for (int nt = 0; nt < C / 16; ++nt) for (int nt = 0; nt < C / 16; ++nt)
{ {
const int gnt = wv * (C / 16) + nt; // expand output column tile const int gnt = wv * (C / 16) + nt; // expand output column tile
f8v d; acc8v d;
{ {
half_t vv_[8]; half_t vv_[8];
@@ -790,7 +790,7 @@ __device__ void pwin_decoder(const PwinParams& p, const u8v* __restrict__ img, c
#pragma unroll #pragma unroll
for (int nt = 0; nt < L::NOUTA / 16; ++nt) for (int nt = 0; nt < L::NOUTA / 16; ++nt)
{ {
f8v d; acc8v d;
{ {
half_t vv_[8]; half_t vv_[8];
+10 -9
View File
@@ -8,6 +8,7 @@ __device__ void pos_core(const PwinParams& p, const u8v* __restrict__ img, const
half_t (*act)[L::C], half_t (*hb)[L::C]) half_t (*act)[L::C], half_t (*hb)[L::C])
{ {
constexpr int C = L::C, KT = L::KT, H = L::H, NW = 4 / MT; constexpr int C = L::C, KT = L::KT, H = L::H, NW = 4 / MT;
static_assert(MT == 1 || MT == 2 || MT == 4, "position token tiles per wave");
static_assert(L::POS, "position-only attention layers"); static_assert(L::POS, "position-only attention layers");
const uint32_t l = lane_id(), m = l & 15, hf = l >> 4; const uint32_t l = lane_id(), m = l & 15, hf = l >> 4;
const int wv = threadIdx.z; const int wv = threadIdx.z;
@@ -37,7 +38,7 @@ __device__ void pos_core(const PwinParams& p, const u8v* __restrict__ img, const
STAMP(2); STAMP(2);
// ---- attention: O = P V per head (P from the table), output projection accumulated head by head // ---- attention: O = P V per head (P from the table), output projection accumulated head by head
f8v acc[MT][KT]; acc8v acc[MT][KT];
#pragma unroll #pragma unroll
for (int nt = 0; nt < KT; ++nt) for (int nt = 0; nt < KT; ++nt)
{ {
@@ -64,7 +65,7 @@ __device__ void pos_core(const PwinParams& p, const u8v* __restrict__ img, const
for (int mi = 0; mi < MT; ++mi) for (int mi = 0; mi < MT; ++mi)
{ {
const int kt = wv * MT + mi; const int kt = wv * MT + mi;
f8v d = splat8(0.0f); acc8v d = splat8(0.0f);
#pragma unroll #pragma unroll
for (int ks = 0; ks < KT; ++ks) for (int ks = 0; ks < KT; ++ks)
d = mma16(op_lds(&hb[16 * kt + m][16 * ks]), op_img(img, (L::T_QKV + ((h * 3 + 2) * KT + ks) * 2 + nt) * 16 + m), d); d = mma16(op_lds(&hb[16 * kt + m][16 * ks]), op_img(img, (L::T_QKV + ((h * 3 + 2) * KT + ks) * 2 + nt) * 16 + m), d);
@@ -82,7 +83,7 @@ __device__ void pos_core(const PwinParams& p, const u8v* __restrict__ img, const
#pragma unroll #pragma unroll
for (int kt = 0; kt < 4; ++kt) for (int kt = 0; kt < 4; ++kt)
{ {
f8v d = splat8(0.0f); acc8v d = splat8(0.0f);
#pragma unroll #pragma unroll
for (int ks = 0; ks < KT; ++ks) for (int ks = 0; ks < KT; ++ks)
d = mma16(op_lds(&hb[16 * kt + m][16 * ks]), op_img(img, (L::T_QKV + ((h * 3 + 2) * KT + ks) * 2 + nt) * 16 + m), d); d = mma16(op_lds(&hb[16 * kt + m][16 * ks]), op_img(img, (L::T_QKV + ((h * 3 + 2) * KT + ks) * 2 + nt) * 16 + m), d);
@@ -101,7 +102,7 @@ __device__ void pos_core(const PwinParams& p, const u8v* __restrict__ img, const
#pragma unroll #pragma unroll
for (int nt = 0; nt < 2; ++nt) for (int nt = 0; nt < 2; ++nt)
{ {
f8v d = splat8(0.0f); acc8v d = splat8(0.0f);
#pragma unroll #pragma unroll
for (int kt = 0; kt < 4; ++kt) for (int kt = 0; kt < 4; ++kt)
d = mma16(vtop[nt][kt], pop[kt], d); d = mma16(vtop[nt][kt], pop[kt], d);
@@ -156,7 +157,7 @@ __device__ void pos_core(const PwinParams& p, const u8v* __restrict__ img, const
{ {
half_t bb[8]; half_t bb[8];
dvec8(w, L::B1 + 64 * c + 32 * hn, bb); dvec8(w, L::B1 + 64 * c + 32 * hn, bb);
f8v d[MT]; acc8v d[MT];
#pragma unroll #pragma unroll
for (int mi = 0; mi < MT; ++mi) for (int mi = 0; mi < MT; ++mi)
#pragma unroll #pragma unroll
@@ -231,7 +232,7 @@ __device__ void pos_encoder(const PwinParams& p0, const u8v* __restrict__ img, c
{ {
half_t vv[8]; half_t vv[8];
dvec8(p0.w, 32 * nt, vv); dvec8(p0.w, 32 * nt, vv);
f8v d; acc8v d;
#pragma unroll #pragma unroll
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
d[i] = (float)vv[i]; d[i] = (float)vv[i];
@@ -278,7 +279,7 @@ __device__ void pos_encoder(const PwinParams& p0, const u8v* __restrict__ img, c
{ {
half_t vv[8]; half_t vv[8];
dvec8(p.w, L::PMB + 32 * vt, vv); dvec8(p.w, L::PMB + 32 * vt, vv);
f8v d; acc8v d;
#pragma unroll #pragma unroll
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
d[i] = (float)vv[i]; d[i] = (float)vv[i];
@@ -322,7 +323,7 @@ __device__ void pos_decoder(const PwinParams& p, const u8v* __restrict__ img, co
const int gnt = q * (C / 16) + nt; const int gnt = q * (C / 16) + nt;
half_t vv[8]; half_t vv[8];
dvec8(p.w, L::EXPB + 32 * gnt, vv); dvec8(p.w, L::EXPB + 32 * gnt, vv);
f8v d; acc8v d;
#pragma unroll #pragma unroll
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
d[i] = (float)vv[i]; d[i] = (float)vv[i];
@@ -353,7 +354,7 @@ __device__ void pos_decoder(const PwinParams& p, const u8v* __restrict__ img, co
{ {
half_t vv[8]; half_t vv[8];
dvec8(q.w, L::HB + 32 * nt, vv); dvec8(q.w, L::HB + 32 * nt, vv);
f8v d; acc8v d;
#pragma unroll #pragma unroll
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
d[i] = (float)vv[i]; d[i] = (float)vv[i];
+8 -8
View File
@@ -68,7 +68,7 @@ __device__ void wide_core(const PwinParams& p, const u8v* __restrict__ img, cons
block_sync(); // hb complete, R free block_sync(); // hb complete, R free
// ---- attention: NG heads per round; the output projection accumulates head by head // ---- attention: NG heads per round; the output projection accumulates head by head
f8v acc[NTL]; acc8v acc[NTL];
#pragma unroll #pragma unroll
for (int j = 0; j < NTL; ++j) for (int j = 0; j < NTL; ++j)
{ {
@@ -93,7 +93,7 @@ __device__ void wide_core(const PwinParams& p, const u8v* __restrict__ img, cons
if (h < H) if (h < H)
{ {
// six independent chains (Q, K, V x 2 n-tiles), each over kt in order // six independent chains (Q, K, V x 2 n-tiles), each over kt in order
f8v dq[6]; acc8v dq[6];
#pragma unroll #pragma unroll
for (int u = 0; u < 6; ++u) for (int u = 0; u < 6; ++u)
dq[u] = splat8(0.0f); dq[u] = splat8(0.0f);
@@ -109,7 +109,7 @@ __device__ void wide_core(const PwinParams& p, const u8v* __restrict__ img, cons
#pragma unroll #pragma unroll
for (int nt = 0; nt < 2; ++nt) for (int nt = 0; nt < 2; ++nt)
{ {
const f8v d = dq[2 * j + nt]; const acc8v d = dq[2 * j + nt];
if (j == 0) if (j == 0)
qop[nt] = operand_from_f8(d); qop[nt] = operand_from_f8(d);
else if (j == 1) else if (j == 1)
@@ -133,7 +133,7 @@ __device__ void wide_core(const PwinParams& p, const u8v* __restrict__ img, cons
for (int kt = 0; kt < 4; ++kt) for (int kt = 0; kt < 4; ++kt)
{ {
const u4v bi = bias[((h * 4 + t) * 4 + kt) * 32 + l]; const u4v bi = bias[((h * 4 + t) * 4 + kt) * 32 + l];
f8v d; acc8v d;
#pragma unroll #pragma unroll
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
{ {
@@ -166,7 +166,7 @@ __device__ void wide_core(const PwinParams& p, const u8v* __restrict__ img, cons
#pragma unroll #pragma unroll
for (int nt = 0; nt < 2; ++nt) for (int nt = 0; nt < 2; ++nt)
{ {
f8v d = splat8(0.0f); acc8v d = splat8(0.0f);
#pragma unroll #pragma unroll
for (int kt = 0; kt < 4; ++kt) for (int kt = 0; kt < 4; ++kt)
d = mma16(op_lds(&vt[g][16 * nt + m][16 * kt]), pop[kt], d); d = mma16(op_lds(&vt[g][16 * nt + m][16 * kt]), pop[kt], d);
@@ -233,7 +233,7 @@ __device__ void wide_core(const PwinParams& p, const u8v* __restrict__ img, cons
{ {
if (c >= L::NMLP) if (c >= L::NMLP)
break; break;
f8v d; acc8v d;
{ {
half_t vv[8]; half_t vv[8];
dvec8(w, L::B1 + 64 * c + 32 * hn, vv); dvec8(w, L::B1 + 64 * c + 32 * hn, vv);
@@ -329,7 +329,7 @@ __device__ void wide_encoder(const PwinParams& p, const u8v* __restrict__ img, c
const uint8_t* w = p.w; const uint8_t* w = p.w;
for (int vt = wv; vt < L::PMT; vt += 4 * NG) for (int vt = wv; vt < L::PMT; vt += 4 * NG)
{ {
f8v d; acc8v d;
{ {
half_t vv[8]; half_t vv[8];
dvec8(w, L::PMB + 32 * vt, vv); dvec8(w, L::PMB + 32 * vt, vv);
@@ -382,7 +382,7 @@ __device__ void wide_decoder(const PwinParams& p, const u8v* __restrict__ img, c
for (int nt = g; nt < C / 16; nt += NG) for (int nt = g; nt < C / 16; nt += NG)
{ {
const int gnt = q * (C / 16) + nt; const int gnt = q * (C / 16) + nt;
f8v d; acc8v d;
{ {
half_t vv[8]; half_t vv[8];
dvec8(p.w, L::EXPB + 32 * gnt, vv); dvec8(p.w, L::EXPB + 32 * gnt, vv);
+3 -2
View File
@@ -209,8 +209,9 @@ def replace_rz_round(lines):
ENC0_REF = "rrlite_enc0_4x4_mvhi_hdr_folded" ENC0_REF = "rrlite_enc0_4x4_mvhi_hdr_folded"
# the other flag combinations of the kernels whose only native part is the surface-store rewrite # Surface-store and round-idiom recipes for the other flag combinations.
SUST_ONLY_RE = re.compile(r"^(hiluma_engine_output_depth(inv|reg)_mv(hi|lo)_(hdr|ldr)(_max)?_v[12]_rel|" SUST_ONLY_RE = re.compile(r"^(hiluma_engine_output_depth(inv|reg)_mv(hi|lo)_(hdr|ldr)(_max)?_v[12]_rel|"
r"hiluma_engine_input_depth(inv|reg)_mv(hi|lo)_(hdr|ldr)_v2_rel|"
r"rrlite_post_3_[12]_mv(hi|lo)_(hdr|ldr)(_folded)?|" r"rrlite_post_3_[12]_mv(hi|lo)_(hdr|ldr)(_folded)?|"
r"rrlite_enc0_4x4_mv(hi|lo)_(hdr|ldr)|rrlite_dec0_4x4|" r"rrlite_enc0_4x4_mv(hi|lo)_(hdr|ldr)|rrlite_dec0_4x4|"
r"rrlite_downsample_kernel_(static|dynamic)_(hdr|ldr))$") r"rrlite_downsample_kernel_(static|dynamic)_(hdr|ldr))$")
@@ -277,7 +278,7 @@ def kernel_spec(name):
if ENC0_RE.match(name): if ENC0_RE.match(name):
return enc0_variant(name) return enc0_variant(name)
if SUST_ONLY_RE.match(name): if SUST_ONLY_RE.match(name):
return dict(file="", sust=True, extern="", rz_round=name.startswith("hiluma_engine_output")) return dict(file="", sust=True, extern="", rz_round=name.startswith("hiluma_engine_"))
sys.exit(f"make_ptx: no recipe for {name}") sys.exit(f"make_ptx: no recipe for {name}")
+1 -1
View File
@@ -128,7 +128,7 @@ cache in ~/.cache/d4r can be deleted too.
Licenses Licenses
-------- --------
d4r is Apache License 2.0. The release also contains ZLUDA (Apache 2.0 or MIT), vkd3d-proton d4r is GNU General Public License, version 3 only (GPL-3.0-only). The release also contains ZLUDA (Apache 2.0 or MIT), vkd3d-proton
(LGPL 2.1, patched) and OptiScaler 0.9.4 (GPL 3.0, unmodified). d4r/source/SOURCES.txt says where (LGPL 2.1, patched) and OptiScaler 0.9.4 (GPL 3.0, unmodified). d4r/source/SOURCES.txt says where
each file comes from, and d4r/licenses has the license texts. d4r is not affiliated with NVIDIA, each file comes from, and d4r/licenses has the license texts. d4r is not affiliated with NVIDIA,
AMD or the OptiScaler project. AMD or the OptiScaler project.
+1 -1
View File
@@ -58,6 +58,6 @@ The rebuilt runtime may compile a new kernel cache on first start. `sh d4r/d4r-c
## Licenses ## Licenses
d4r is Apache 2.0. ZLUDA, LLVM, vkd3d-proton, OptiScaler, and the bundled ROCm libraries retain their respective licenses in `d4r/licenses`; sources and patches are listed in `d4r/source`. NVIDIA's libraries and texture kernels compiled from its code are not covered by those licenses. d4r is not affiliated with NVIDIA or AMD. d4r is GNU General Public License, version 3 only (GPL-3.0-only). ZLUDA, LLVM, vkd3d-proton, OptiScaler, and the bundled ROCm libraries retain their respective licenses in `d4r/licenses`; sources and patches are listed in `d4r/source`. NVIDIA's libraries and texture kernels compiled from its code are not covered by those licenses. d4r is not affiliated with NVIDIA or AMD.
The attached `.sha256` file contains the ZIP's checksum. The attached `.sha256` file contains the ZIP's checksum.
+1 -1
View File
@@ -1,7 +1,7 @@
d4r @VERSION@: where every file in this release comes from d4r @VERSION@: where every file in this release comes from
============================================================ ============================================================
Built from source by d4r (Apache License 2.0, d4r/licenses/d4r-LICENSE.txt): Built from source by d4r (GPL-3.0-only, d4r/licenses/d4r-LICENSE.txt):
d4r/nvngx.dll NGX shim tools/d4r_nvngx_shim.cpp in the d4r repository d4r/nvngx.dll NGX shim tools/d4r_nvngx_shim.cpp in the d4r repository
d4r/nvcuda.dll Wine CUDA bridge tools/wine_nvcuda_bridge.c d4r/nvcuda.dll Wine CUDA bridge tools/wine_nvcuda_bridge.c
d4r/kernels/ native RDNA3/RDNA4 kernels for the DLSS 4 and 4.5 network layers, kernels/k and kernels/m d4r/kernels/ native RDNA3/RDNA4 kernels for the DLSS 4 and 4.5 network layers, kernels/k and kernels/m
+27
View File
@@ -0,0 +1,27 @@
; OptiScaler settings for the d4r Windows drag-in release. Values not listed keep OptiScaler's
; defaults. The DLSS model is chosen in d4r\d4r.ini, not here.
[Upscalers]
Dx12Upscaler=dlss
[FrameGen]
Enabled=false
FGInput=nofg
FGOutput=nofg
[DLSS]
Enabled=true
AllowExternalBackend=true
RenderPresetOverride=false
UseGenericAppIdWithDlss=false
[Libraries]
; Relative to this folder (needs d4r's OptiScaler with patch 0006).
NvngxPath=d4r
NvngxDlssPath=d4r
NvngxFeaturePath=d4r
[Hotfix]
RestoreComputeSignature=false
RestoreGraphicSignature=false
ExtendedStateRestore=false
[Log]
LogToFile=true
LogLevel=2
[Menu]
DisableSplash=true
+63
View File
@@ -0,0 +1,63 @@
d4r for Windows: NVIDIA DLSS on AMD Radeon RDNA3 and RDNA4
===========================================================
d4r runs NVIDIA's own DLSS (DLSS 4 by default) on a Radeon GPU in DirectX 12 games. OptiScaler
(included) catches the game's upscaler calls and hands them to d4r.
This is an early release. Only the Radeon RX 9070 XT has been tested on real hardware; every other
GPU in this release is compile-tested. SUPPORTED_GAMES.md in the source repository lists the
tested games. Expect problems.
What you need
-------------
- Windows 11, a Radeon RX 7000 or RX 9000 series GPU, a current AMD Adrenalin driver.
- A DirectX 12 game without anti-cheat (OptiScaler injects a DLL; it can get an account banned).
- Two files from NVIDIA, which this ZIP does not include:
nvngx_dlss.dll version 310.9.1 (tested) or 310.7.0. Many games ship one in their folder;
right-click > Properties > Details shows the version.
_nvngx.dll version 32.0.16.1714 (tested) or 32.0.15.9636. It is part of an NVIDIA display
driver package: extract the installer with 7-Zip and search for _nvngx.dll.
Install
-------
1. Extract this ZIP into the folder that holds the game's main .exe. For Unreal Engine games that
is <game>\<Project>\Binaries\Win64, next to <Project>-Win64-Shipping.exe.
If the folder already has a dxgi.dll or OptiScaler.ini (another OptiScaler install), or a
d3d12.dll from a Linux install, move those out first.
2. Copy nvngx_dlss.dll into the d4r folder and _nvngx.dll into d4r\ngx.
3. Optional: check the install. In the game folder, open PowerShell and run
powershell -ExecutionPolicy Bypass -File d4r\d4r-check.ps1
4. Start the game from Steam, Epic or a shortcut as usual. In its graphics settings choose DLSS
(or FSR 3.1/XeSS if DLSS is hidden; OptiScaler then feeds DLSS). Keep frame generation off.
Press Insert in game for OptiScaler's menu.
The first time DLSS starts, the game can freeze for several minutes while DLSS's GPU kernels are
compiled. They are cached in %LOCALAPPDATA%\d4r\cache, so later starts are quick.
Settings
--------
Edit d4r\d4r.ini (it is commented) and restart the game. Each game has its own copy.
- Model: K (DLSS 4, default) or M (DLSS 4.5, heavier). Select the quality mode in the game.
- AsyncInterop, ValidateOutput, Log: for testing and bug reports; the defaults are the tested setting.
OptiScaler's own settings (overlay, render ratios) are in OptiScaler.ini.
If something goes wrong
-----------------------
- Run d4r\d4r-check.ps1: it prints ok / MISSING / note lines with the fix for each problem.
- d4r\d4r_nvngx.log is rewritten at every launch. A line "d4r: DLSS disabled: ..." says why.
- DLSS missing in the menu and no d4r_nvngx.log: the files are not next to the game's main .exe.
- For a bug report run d4r\d4r-check.ps1 -Report and send the ZIP it puts on your Desktop,
with your GPU, the game, the model and what the image looked like. Logs can contain folder names.
- Not supported: frame generation, ray reconstruction, DirectX 11, Vulkan, models E and L.
Uninstall
---------
Delete dxgi.dll, OptiScaler.ini, OptiScaler.log (if present), WINDOWS_README.txt and the d4r folder
from the game folder. The cache in %LOCALAPPDATA%\d4r can be deleted too. Nothing else was changed.
Licenses
--------
d4r is GNU General Public License, version 3 only (GPL-3.0-only). This ZIP also contains ZLUDA (Apache 2.0 or MIT), OptiScaler (GPL 3.0,
patched; the patches are in the source repository), and AMD's HIP runtime. d4r\licenses has the
texts. d4r is not affiliated with NVIDIA, AMD or the OptiScaler project. NVIDIA's files are yours
to supply, under NVIDIA's terms.
+41
View File
@@ -0,0 +1,41 @@
; d4r settings for this game (Windows). This file lives in the game's d4r folder and applies to
; this game only. Edit it with Notepad, save, and restart the game. Mistakes are reported in
; d4r\d4r_nvngx.log and by d4r-check.ps1; an invalid value falls back to the default shown here.
;
; Only settings that exist and are checked on Windows are listed. The Linux settings for other
; models, kernels and interop are not available here.
[DLSS]
; DLSS model:
;
; Model | DLSS | Recommended modes | Notes
; ------+------+----------------------------+--------------------------------------
; K | 4 | DLAA / Quality / Balanced | Default; faster
; M | 4.5 | Performance | More demanding
;
; Select the quality mode (Quality, Performance, ...) in the game or the OptiScaler menu (Insert).
; Models E and L are not available on Windows.
Model = K
; NVIDIA's library stamps "DLSS SDK - DO NOT DISTRIBUTE" over the image; d4r turns that off.
; true shows it.
ShowWatermark = false
[Interop]
; Experimental: lets the game continue while DLSS runs on the GPU instead of waiting for it.
; Requires a game that uses one D3D12 command queue. Off is the tested setting. Whether it is
; faster depends on the game and has not been measured broadly; turn it off if the game crashes,
; stutters or shows a wrong image.
AsyncInterop = false
[Debug]
; Check every DLSS output for invalid values (NaN/Inf) on the GPU. Costs some speed; use it only
; when reporting a problem.
ValidateOutput = false
; normal: errors and a summary in d4r\d4r_nvngx.log. verbose: every API call and kernel launch
; (large log; use it only when asked for a bug report).
Log = normal
[Paths]
; Folder for the compiled DLSS kernels. auto = %LOCALAPPDATA%\d4r\cache. The first launch fills it
; and can take several minutes; later launches are quick.
CacheDir = auto
@@ -0,0 +1,34 @@
diff --git a/OptiScaler/hooks/D3D12_Hooks.cpp b/OptiScaler/hooks/D3D12_Hooks.cpp
index 3b757be..05fc194 100644
--- a/OptiScaler/hooks/D3D12_Hooks.cpp
+++ b/OptiScaler/hooks/D3D12_Hooks.cpp
@@ -2217,11 +2217,20 @@ static HRESULT hkD3D12GetInterface(REFCLSID rclsid, REFIID riid, void** ppvDebug
return result;
}
+// Device discovery and NGX initialization can overlap. Installing d4r's
+// command hooks can also create a device and re-enter this function. Never
+// overwrite live Detours trampolines from a second installation attempt.
+static std::recursive_mutex deviceHookMutex;
+static bool deviceHookInstalling = false;
static void HookToDevice(ID3D12Device* InDevice)
{
- if (o_CreateSampler != nullptr || InDevice == nullptr)
+ std::lock_guard<std::recursive_mutex> lock(deviceHookMutex);
+ if (o_CreateSampler != nullptr || InDevice == nullptr || deviceHookInstalling)
return;
+ deviceHookInstalling = true;
+ struct InstallationGuard { ~InstallationGuard() { deviceHookInstalling = false; } } installationGuard;
+
LOG_DEBUG("Dx12");
// Get the vtable pointer
@@ -2334,6 +2343,7 @@ static void HookToDevice(ID3D12Device* InDevice)
static void UnhookDevice()
{
+ std::lock_guard<std::recursive_mutex> lock(deviceHookMutex);
LOG_FUNC();
DetourTransactionBegin();
@@ -0,0 +1,22 @@
diff --git a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
index 1f723f1..ef5ec77 100644
--- a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
+++ b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
@@ -184,6 +184,8 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_Init_Ext(unsigned long long InApp
if (result == NVSDK_NGX_Result_Success)
NVNGXProxy::SetDx12Inited(true);
+ else if (State::Instance().dlssExternalBackend)
+ return result;
}
else
{
@@ -254,6 +256,8 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_Init(unsigned long long InApplica
if (result == NVSDK_NGX_Result_Success)
NVNGXProxy::SetDx12Inited(true);
+ else if (State::Instance().dlssExternalBackend)
+ return result;
}
}
@@ -0,0 +1,30 @@
Subject: [PATCH] Resolve relative d4r library paths against the OptiScaler folder
Lets a drag-in d4r install ship OptiScaler.ini with NvngxPath=d4r instead of
an absolute path that only an installer could know. Absolute paths behave
exactly as before.
---
diff --git a/OptiScaler/Config.cpp b/OptiScaler/Config.cpp
--- a/OptiScaler/Config.cpp
+++ b/OptiScaler/Config.cpp
@@ -787,9 +787,17 @@
{
MainDllPath.set_from_config(readWString("Libraries", "OptiDllPath"));
- NvngxPath.set_from_config(readWString("Libraries", "NvngxPath"));
- NVNGX_DLSS_Library.set_from_config(readWString("Libraries", "NvngxDlssPath"));
- DLSSFeaturePath.set_from_config(readWString("Libraries", "NvngxFeaturePath"));
+ // A relative library folder (d4r drag-in installs use "d4r") is resolved
+ // against this DLL's folder, as LogFileName is. Absolute paths are unchanged.
+ auto libraryPath = [](std::optional<std::wstring> value) -> std::optional<std::wstring>
+ {
+ if (!value.has_value() || value->empty() || std::filesystem::path(*value).is_absolute())
+ return value;
+ return (Util::DllPath().parent_path() / *value).lexically_normal().wstring();
+ };
+ NvngxPath.set_from_config(libraryPath(readWString("Libraries", "NvngxPath")));
+ NVNGX_DLSS_Library.set_from_config(libraryPath(readWString("Libraries", "NvngxDlssPath")));
+ DLSSFeaturePath.set_from_config(libraryPath(readWString("Libraries", "NvngxFeaturePath")));
NvapiDllPath.set_from_config(readWString("Libraries", "NvapiPath"));
FfxDx12Path.set_from_config(readWString("Libraries", "FfxDx12Path"));
+12
View File
@@ -1,6 +1,18 @@
# Explicit Windows d4r backend # Explicit Windows d4r backend
Base: OptiScaler `45a2001303ddff632e279f77aef85ceede5832cb` (2026-09-30). Base: OptiScaler `45a2001303ddff632e279f77aef85ceede5832cb` (2026-09-30).
`0004` serializes D3D12 device hook installation/removal and guards recursive
device discovery while the d4r command backend installs. The previous frontend
can start two installations, fail a Detours transaction with `0x10dd`, clear
live original-function pointers and call null. This was reproduced in the
numeric-ID standalone harness; no game was required.
`0005` propagates numeric Init/Init_Ext failures for the explicitly selected
external backend, matching the existing ProjectID behavior. FeatureNotFound
must fail initialization instead of being hidden until repeated CreateFeature
attempts. `Libraries.NvngxDlssPath` takes a directory, not the DLL filename;
the Windows installer and standalone runner now use that directory.
The modifications to OptiScaler are GPL-3.0, matching its upstream license. The modifications to OptiScaler are GPL-3.0, matching its upstream license.
No NVIDIA DLL is supplied or patched. No NVIDIA DLL is supplied or patched.
@@ -0,0 +1,128 @@
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index e619b186d..dd3e9d92b 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -856,7 +856,7 @@ enum : unsigned {
EF_AMDGPU_MACH_AMDGCN_GFX11_GENERIC = 0x054,
EF_AMDGPU_MACH_AMDGCN_GFX1152 = 0x055,
EF_AMDGPU_MACH_AMDGCN_RESERVED_0X56 = 0x056,
- EF_AMDGPU_MACH_AMDGCN_RESERVED_0X57 = 0x057,
+ EF_AMDGPU_MACH_AMDGCN_GFX1154 = 0x057,
EF_AMDGPU_MACH_AMDGCN_GFX1153 = 0x058,
EF_AMDGPU_MACH_AMDGCN_GFX12_GENERIC = 0x059,
EF_AMDGPU_MACH_AMDGCN_GFX1251 = 0x05a,
diff --git a/llvm/include/llvm/TargetParser/TargetParser.h b/llvm/include/llvm/TargetParser/TargetParser.h
index aad985926..c0aaa465b 100644
--- a/llvm/include/llvm/TargetParser/TargetParser.h
+++ b/llvm/include/llvm/TargetParser/TargetParser.h
@@ -109,6 +109,7 @@ enum GPUKind : uint32_t {
GK_GFX1151,
GK_GFX1152,
GK_GFX1153,
+ GK_GFX1154,
GK_GFX1200,
GK_GFX1201,
diff --git a/llvm/lib/Object/ELFObjectFile.cpp b/llvm/lib/Object/ELFObjectFile.cpp
index f9fda2346..70ddaae22 100644
--- a/llvm/lib/Object/ELFObjectFile.cpp
+++ b/llvm/lib/Object/ELFObjectFile.cpp
@@ -591,6 +591,8 @@ StringRef ELFObjectFileBase::getAMDGPUCPUName() const {
return "gfx1152";
case ELF::EF_AMDGPU_MACH_AMDGCN_GFX1153:
return "gfx1153";
+ case ELF::EF_AMDGPU_MACH_AMDGCN_GFX1154:
+ return "gfx1154";
// AMDGCN GFX12.
case ELF::EF_AMDGPU_MACH_AMDGCN_GFX1200:
diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp
index d9cce1eb7..7936876f4 100644
--- a/llvm/lib/ObjectYAML/ELFYAML.cpp
+++ b/llvm/lib/ObjectYAML/ELFYAML.cpp
@@ -632,6 +632,7 @@ void ScalarBitSetTraits<ELFYAML::ELF_EF>::bitset(IO &IO,
BCaseMask(EF_AMDGPU_MACH_AMDGCN_GFX1151, EF_AMDGPU_MACH);
BCaseMask(EF_AMDGPU_MACH_AMDGCN_GFX1152, EF_AMDGPU_MACH);
BCaseMask(EF_AMDGPU_MACH_AMDGCN_GFX1153, EF_AMDGPU_MACH);
+ BCaseMask(EF_AMDGPU_MACH_AMDGCN_GFX1154, EF_AMDGPU_MACH);
BCaseMask(EF_AMDGPU_MACH_AMDGCN_GFX1200, EF_AMDGPU_MACH);
BCaseMask(EF_AMDGPU_MACH_AMDGCN_GFX1201, EF_AMDGPU_MACH);
BCaseMask(EF_AMDGPU_MACH_AMDGCN_GFX1250, EF_AMDGPU_MACH);
diff --git a/llvm/lib/Target/AMDGPU/GCNProcessors.td b/llvm/lib/Target/AMDGPU/GCNProcessors.td
index c6fb31f9c..5d21894a3 100644
--- a/llvm/lib/Target/AMDGPU/GCNProcessors.td
+++ b/llvm/lib/Target/AMDGPU/GCNProcessors.td
@@ -304,7 +304,12 @@ def : ProcessorModel<"gfx1153", GFX11SpeedModel,
FeatureISAVersion11_5_3.Features
>;
-// [gfx1100, gfx1101, gfx1102, gfx1103, gfx1150, gfx1151, gfx1152, gfx1153]
+// Backported from LLVM 7a0829e41228: actual gfx1154 features and identity.
+def : ProcessorModel<"gfx1154", GFX11SpeedModel,
+ FeatureISAVersion11_5_Common.Features
+>;
+
+// [gfx1100, gfx1101, gfx1102, gfx1103, gfx1150, gfx1151, gfx1152, gfx1153, gfx1154]
def : ProcessorModel<"gfx11-generic", GFX11SpeedModel,
FeatureISAVersion11_Generic.Features
>;
diff --git a/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUTargetStreamer.cpp b/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUTargetStreamer.cpp
index 5a0857322..c506fc308 100644
--- a/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUTargetStreamer.cpp
+++ b/llvm/lib/Target/AMDGPU/MCTargetDesc/AMDGPUTargetStreamer.cpp
@@ -114,6 +114,7 @@ StringRef AMDGPUTargetStreamer::getArchNameFromElfMach(unsigned ElfMach) {
case ELF::EF_AMDGPU_MACH_AMDGCN_GFX1151: AK = GK_GFX1151; break;
case ELF::EF_AMDGPU_MACH_AMDGCN_GFX1152: AK = GK_GFX1152; break;
case ELF::EF_AMDGPU_MACH_AMDGCN_GFX1153: AK = GK_GFX1153; break;
+ case ELF::EF_AMDGPU_MACH_AMDGCN_GFX1154: AK = GK_GFX1154; break;
case ELF::EF_AMDGPU_MACH_AMDGCN_GFX1200: AK = GK_GFX1200; break;
case ELF::EF_AMDGPU_MACH_AMDGCN_GFX1201: AK = GK_GFX1201; break;
case ELF::EF_AMDGPU_MACH_AMDGCN_GFX1250: AK = GK_GFX1250; break;
@@ -201,6 +202,7 @@ unsigned AMDGPUTargetStreamer::getElfMach(StringRef GPU) {
case GK_GFX1151: return ELF::EF_AMDGPU_MACH_AMDGCN_GFX1151;
case GK_GFX1152: return ELF::EF_AMDGPU_MACH_AMDGCN_GFX1152;
case GK_GFX1153: return ELF::EF_AMDGPU_MACH_AMDGCN_GFX1153;
+ case GK_GFX1154: return ELF::EF_AMDGPU_MACH_AMDGCN_GFX1154;
case GK_GFX1200: return ELF::EF_AMDGPU_MACH_AMDGCN_GFX1200;
case GK_GFX1201: return ELF::EF_AMDGPU_MACH_AMDGCN_GFX1201;
case GK_GFX1250: return ELF::EF_AMDGPU_MACH_AMDGCN_GFX1250;
diff --git a/llvm/lib/TargetParser/TargetParser.cpp b/llvm/lib/TargetParser/TargetParser.cpp
index 34b09b14b..39acbb942 100644
--- a/llvm/lib/TargetParser/TargetParser.cpp
+++ b/llvm/lib/TargetParser/TargetParser.cpp
@@ -172,6 +172,7 @@ constexpr GPUInfo AMDGCNGPUs[] = {
{{"gfx1151"}, {"gfx1151"}, GK_GFX1151, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32|FEATURE_WAVE32|FEATURE_WGP},
{{"gfx1152"}, {"gfx1152"}, GK_GFX1152, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32|FEATURE_WAVE32|FEATURE_WGP},
{{"gfx1153"}, {"gfx1153"}, GK_GFX1153, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32|FEATURE_WAVE32|FEATURE_WGP},
+ {{"gfx1154"}, {"gfx1154"}, GK_GFX1154, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32|FEATURE_WAVE32|FEATURE_WGP},
{{"gfx1200"}, {"gfx1200"}, GK_GFX1200, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32|FEATURE_WAVE32|FEATURE_WGP},
{{"gfx1201"}, {"gfx1201"}, GK_GFX1201, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32|FEATURE_WAVE32|FEATURE_WGP},
{{"gfx1250"}, {"gfx1250"}, GK_GFX1250, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32|FEATURE_WAVE32},
@@ -328,6 +329,7 @@ AMDGPU::IsaVersion AMDGPU::getIsaVersion(StringRef GPU) {
case GK_GFX1151: return {11, 5, 1};
case GK_GFX1152: return {11, 5, 2};
case GK_GFX1153: return {11, 5, 3};
+ case GK_GFX1154: return {11, 5, 4};
case GK_GFX1200: return {12, 0, 0};
case GK_GFX1201: return {12, 0, 1};
case GK_GFX1250: return {12, 5, 0};
@@ -475,6 +477,7 @@ static void fillAMDGCNFeatureMap(StringRef GPU, const Triple &T,
Features["atomic-fmin-fmax-global-f32"] = true;
break;
case GK_GFX1153:
+ case GK_GFX1154:
case GK_GFX1152:
case GK_GFX1151:
case GK_GFX1150:
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index ab9331690..0ade2d412 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -1655,6 +1655,7 @@ const EnumEntry<unsigned> ElfHeaderMipsFlags[] = {
ENUM_ENT(EF_AMDGPU_MACH_AMDGCN_GFX1151, "gfx1151"), \
ENUM_ENT(EF_AMDGPU_MACH_AMDGCN_GFX1152, "gfx1152"), \
ENUM_ENT(EF_AMDGPU_MACH_AMDGCN_GFX1153, "gfx1153"), \
+ ENUM_ENT(EF_AMDGPU_MACH_AMDGCN_GFX1154, "gfx1154"), \
ENUM_ENT(EF_AMDGPU_MACH_AMDGCN_GFX1200, "gfx1200"), \
ENUM_ENT(EF_AMDGPU_MACH_AMDGCN_GFX1201, "gfx1201"), \
ENUM_ENT(EF_AMDGPU_MACH_AMDGCN_GFX1250, "gfx1250"), \
+24
View File
@@ -0,0 +1,24 @@
# LLVM source patches for the Windows compiler
Base: ZLUDA's LLVM submodule `ff4dc1f7c9e1c64d4d69e40f4ed30c2280a96dfd`
(LLVM 22). `build-zluda-llvm.ps1` applies these patches idempotently before
building. ZLUDA's parent source patches remain in `patches/zluda`.
`0001-backport-gfx1154.patch` adapts official LLVM
[`7a0829e41228`](https://github.com/llvm/llvm-project/commit/7a0829e41228513299c5108685b0bc127463c6a1)
to the older parser/ELF structures. It adds actual gfx1154 processor features,
ISA 11.5.4, parser identity and ELF machine 0x57. It does not map gfx1154 onto
another CPU, patch binary headers or alter existing processors' features.
LLVM's gfx1154 uses `FeatureISAVersion11_5_Common` and the GFX11 speed model.
LLVM code remains Apache-2.0 WITH LLVM-exception (see the dependency license).
The prior offline compiler aborts on a public PTX pattern for gfx1154 with
unrecognized-processor diagnostics and exit `0xC0000409`. After rebuilding
LLVM, rebuild/relink ZLUDA and its offline emitter; changing native Clang
objects alone cannot fix the translated PTX path. Run
`scripts/windows/test-zluda-target-compilation.ps1` to compile the public
Driver API pattern and FP16 MMA fixtures (strict/reference and WMMA modes)
for every target without GPU work. All 33 compilations pass with correct ELF
targets and wave32. Strict gfx12 F16 reference retains its prior FP64 helper;
WMMA mode here is a compilation check, not a default arithmetic change.
This checks code generation and ELF identity, not hardware execution/numerics.
@@ -0,0 +1,282 @@
From 26a43f7047ab933a4afb5ebdd26d5ba9e51e58a3 Mon Sep 17 00:00:00 2001
From: xdfnx-dev <[email protected]>
Date: Fri, 2 Oct 2026 11:50:45 +0300
Subject: [PATCH 16/17] Sample HIP kernel events without per-launch completion
waits
---
zluda/src/impl/context.rs | 4 +-
zluda/src/impl/d4r_profile.rs | 204 ++++++++++++++++++++++++++++------
zluda/src/impl/module.rs | 1 +
3 files changed, 171 insertions(+), 38 deletions(-)
diff --git a/zluda/src/impl/context.rs b/zluda/src/impl/context.rs
index 9ccb372..374d383 100644
--- a/zluda/src/impl/context.rs
+++ b/zluda/src/impl/context.rs
@@ -165,7 +165,7 @@ pub(crate) fn set_cache_config(config: CUfunc_cache) -> CUresult {
}
pub(crate) fn synchronize() -> hipError_t {
- unsafe { hipDeviceSynchronize() }
+ unsafe { hipDeviceSynchronize()?; super::d4r_profile::collect_completed() }
}
pub(crate) fn synchronize_v2(ctx: &Context) -> hipError_t {
@@ -177,7 +177,7 @@ pub(crate) fn synchronize_v2(ctx: &Context) -> hipError_t {
} else {
None
};
- unsafe { hipDeviceSynchronize() }
+ unsafe { hipDeviceSynchronize()?; super::d4r_profile::collect_completed() }
}
struct SetDeviceOnDrop(i32);
diff --git a/zluda/src/impl/d4r_profile.rs b/zluda/src/impl/d4r_profile.rs
index 33ba5f6..1b9d248 100644
--- a/zluda/src/impl/d4r_profile.rs
+++ b/zluda/src/impl/d4r_profile.rs
@@ -1,15 +1,29 @@
-//! Optional, deliberately serializing HIP-event diagnostics. Not a production
-//! scheduler or a claim about application FPS / measured hardware occupancy.
+//! Optional HIP-event diagnostics. Deferred mode never waits for its events,
+//! skips the legacy default stream and bounds outstanding events per thread.
+//! Timestamp insertion still adds overhead; this is not a production scheduler.
use hip_runtime_sys::*;
-use std::{ffi::CStr, ptr, time::Instant};
+use std::{cell::RefCell, ffi::{CStr, CString}, ptr, sync::OnceLock, time::Instant};
-pub(super) struct Launch {
- start: hipEvent_t,
- end: hipEvent_t,
- host: Instant,
+#[derive(Clone, Copy)]
+enum Mode { Off, Serial, Deferred(u64) }
+fn mode() -> Mode {
+ static MODE: OnceLock<Mode> = OnceLock::new();
+ *MODE.get_or_init(|| {
+ if std::env::var_os("D4R_ZLUDA_PROFILE_DEFERRED").is_some() {
+ let every = std::env::var("D4R_ZLUDA_PROFILE_EVERY").ok()
+ .and_then(|v| v.parse::<u64>().ok()).filter(|n| *n > 0).unwrap_or(17);
+ Mode::Deferred(every)
+ } else if std::env::var_os("D4R_ZLUDA_PROFILE").is_some() { Mode::Serial }
+ else { Mode::Off }
+ })
+}
+fn allow_legacy() -> bool {
+ static ALLOW: OnceLock<bool> = OnceLock::new();
+ *ALLOW.get_or_init(|| std::env::var_os("D4R_ZLUDA_PROFILE_ALLOW_LEGACY").is_some())
}
-impl Drop for Launch {
+struct Events { start: hipEvent_t, end: hipEvent_t }
+impl Drop for Events {
fn drop(&mut self) {
unsafe {
if !self.end.is_null() { let _ = hipEventDestroy(self.end); }
@@ -17,44 +31,162 @@ impl Drop for Launch {
}
}
}
+impl Events {
+ unsafe fn new() -> Result<Self, hipErrorCode_t> {
+ let mut result = Self { start: ptr::null_mut(), end: ptr::null_mut() };
+ hipEventCreateWithFlags(&mut result.start, 0)?;
+ hipEventCreateWithFlags(&mut result.end, 0)?;
+ Ok(result)
+ }
+}
+
+struct Description {
+ name: CString, backend: String, phase: String, grid: [u32;3], block: [u32;3],
+ shared: u32, lds: i32, regs: i32, private: i32, max_threads: i32, blocks_per_multiprocessor: i32,
+}
+impl Description {
+ unsafe fn new(function: hipFunction_t, name: &CStr, backend: &str, phase: &str,
+ grid: [u32;3], block: [u32;3], shared: u32) -> Result<Self, hipErrorCode_t> {
+ let mut d = Self { name: name.to_owned(), backend: backend.to_owned(), phase: phase.to_owned(),
+ grid, block, shared, lds: 0, regs: 0, private: 0, max_threads: 0, blocks_per_multiprocessor: 0 };
+ // Copy metadata while the function/module is alive. Collection never
+ // dereferences a function handle that the application may have unloaded.
+ hipFuncGetAttribute(&mut d.lds, hipFunction_attribute::HIP_FUNC_ATTRIBUTE_SHARED_SIZE_BYTES, function)?;
+ hipFuncGetAttribute(&mut d.regs, hipFunction_attribute::HIP_FUNC_ATTRIBUTE_NUM_REGS, function)?;
+ hipFuncGetAttribute(&mut d.private, hipFunction_attribute::HIP_FUNC_ATTRIBUTE_LOCAL_SIZE_BYTES, function)?;
+ hipFuncGetAttribute(&mut d.max_threads, hipFunction_attribute::HIP_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, function)?;
+ let threads = block.iter().try_fold(1u32, |a, b| a.checked_mul(*b))
+ .filter(|n| *n <= i32::MAX as u32).ok_or(hipErrorCode_t::InvalidValue)?;
+ hipModuleOccupancyMaxActiveBlocksPerMultiprocessor(&mut d.blocks_per_multiprocessor,
+ function, threads as i32, shared as usize)?;
+ Ok(d)
+ }
+ fn print(&self, gpu_ms: f32, enqueue_ms: f64, completion_ms: f64, serializing: bool, stream: usize) {
+ if !gpu_ms.is_finite() || gpu_ms < 0. {
+ eprintln!("D4R_KERNEL_PROFILE_INVALID kernel={:?} backend={} phase={} gpu_ms={gpu_ms:.6} reason=invalid_elapsed_time serializing={} deferred={} stream=0x{stream:x}",
+ self.name, self.backend, self.phase, u32::from(serializing), u32::from(!serializing));
+ return;
+ }
+ eprintln!("D4R_KERNEL_PROFILE kernel={:?} backend={} phase={} gpu_ms={gpu_ms:.6} enqueue_ms={enqueue_ms:.6} completion_ms={completion_ms:.6} grid={},{},{} block={},{},{} static_lds_bytes={} dynamic_lds_bytes={} regs={} private_bytes={} max_threads={} predicted_blocks_per_multiprocessor={} serializing={} deferred={} stream=0x{stream:x} legacy_stream={}",
+ self.name, self.backend, self.phase, self.grid[0], self.grid[1], self.grid[2],
+ self.block[0], self.block[1], self.block[2], self.lds, self.shared, self.regs,
+ self.private, self.max_threads, self.blocks_per_multiprocessor,
+ u32::from(serializing), u32::from(!serializing), u32::from(stream <= 1));
+ }
+}
+
+struct Pending { launch: Launch, description: Description, enqueue_ms: f64, stream: usize }
+const CAPACITY: usize = 64;
+struct Deferred {
+ calls: u64, skipped: u64, pending: Vec<Pending>, free: Vec<(i32, Events)>,
+}
+thread_local! {
+ static DEFERRED: RefCell<Deferred> = RefCell::new(Deferred {
+ calls: 0, skipped: 0, pending: Vec::new(), free: Vec::new(),
+ });
+}
+
+impl Deferred {
+ unsafe fn collect(&mut self, device: i32) -> hipError_t {
+ let mut index = 0;
+ while index < self.pending.len() {
+ let p = &self.pending[index];
+ if p.launch.device != device { index += 1; continue; }
+ match hipEventQuery(p.launch.events.end) {
+ Err(e) if e == hipErrorCode_t::NotReady => { index += 1; continue; }
+ result => result?,
+ }
+ let mut gpu_ms = 0.;
+ hipEventElapsedTime(&mut gpu_ms, p.launch.events.start, p.launch.events.end)?;
+ p.description.print(gpu_ms, p.enqueue_ms, p.launch.host.elapsed().as_secs_f64() * 1000., false, p.stream);
+ let p = self.pending.swap_remove(index);
+ // Re-record only after the old timestamps have been collected.
+ self.free.push((device, p.launch.events));
+ }
+ Ok(())
+ }
+}
+
+/// Called only after the application's existing context synchronization, or
+/// before a selected launch. No event/stream/device wait is added here.
+pub(super) unsafe fn collect_completed() -> hipError_t {
+ if !matches!(mode(), Mode::Deferred(_)) { return Ok(()); }
+ DEFERRED.with(|state| {
+ let mut state = state.borrow_mut();
+ if state.pending.is_empty() { return Ok(()); }
+ let mut device = 0; hipGetDevice(&mut device)?;
+ state.collect(device)
+ })
+}
+
+pub(super) struct Launch {
+ events: Events,
+ host: Instant,
+ device: i32,
+ deferred: bool,
+}
impl Launch {
pub(super) unsafe fn begin(stream: hipStream_t) -> Result<Option<Self>, hipErrorCode_t> {
- if std::env::var_os("D4R_ZLUDA_PROFILE").is_none() { return Ok(None); }
- let mut result = Self { start: ptr::null_mut(), end: ptr::null_mut(), host: Instant::now() };
- hipEventCreateWithFlags(&mut result.start, 0)?;
- hipEventCreateWithFlags(&mut result.end, 0)?;
- hipEventRecord(result.start, stream)?;
- result.host = Instant::now();
- Ok(Some(result))
+ let mode = mode();
+ if matches!(mode, Mode::Off) { return Ok(None); }
+ if let Mode::Deferred(every) = mode {
+ return DEFERRED.with(|state| {
+ let mut state = state.borrow_mut();
+ state.calls = state.calls.wrapping_add(1);
+ if (state.calls - 1) % every != 0 { return Ok(None); }
+ // HIP timing events on the legacy default stream can order
+ // other streams. Do not introduce that dependency in this mode.
+ if stream.0 as usize <= 1 && !allow_legacy() {
+ state.skipped += 1;
+ if state.skipped.is_power_of_two() {
+ eprintln!("D4R_KERNEL_PROFILE_SKIPPED reason=legacy_default_stream count={} serializing=0", state.skipped);
+ }
+ return Ok(None);
+ }
+ let mut device = 0; hipGetDevice(&mut device)?;
+ state.collect(device)?;
+ let mut capture = hipStreamCaptureStatus::hipStreamCaptureStatusNone;
+ hipStreamIsCapturing(stream, &mut capture)?;
+ if capture != hipStreamCaptureStatus::hipStreamCaptureStatusNone {
+ state.skipped += 1;
+ eprintln!("D4R_KERNEL_PROFILE_SKIPPED reason=stream_capture count={} serializing=0", state.skipped);
+ return Ok(None);
+ }
+ let events = if let Some(index) = state.free.iter().position(|(d, _)| *d == device) {
+ state.free.swap_remove(index).1
+ } else if state.pending.len() + state.free.len() < CAPACITY {
+ Events::new()?
+ } else {
+ state.skipped += 1;
+ eprintln!("D4R_KERNEL_PROFILE_SKIPPED reason=capacity count={} capacity={CAPACITY} serializing=0", state.skipped);
+ return Ok(None);
+ };
+ hipEventRecord(events.start, stream)?;
+ Ok(Some(Self { events, host: Instant::now(), device, deferred: true }))
+ });
+ }
+ let events = Events::new()?;
+ hipEventRecord(events.start, stream)?;
+ Ok(Some(Self { events, host: Instant::now(), device: -1, deferred: false }))
}
pub(super) unsafe fn finish(self, function: hipFunction_t, name: &CStr, backend: &str,
phase: &str, grid: [u32;3], block: [u32;3], shared: u32, stream: hipStream_t) -> hipError_t {
let enqueue_ms = self.host.elapsed().as_secs_f64() * 1000.;
- hipEventRecord(self.end, stream)?;
- hipEventSynchronize(self.end)?;
+ hipEventRecord(self.events.end, stream)?;
+ let description = Description::new(function, name, backend, phase, grid, block, shared)?;
+ if self.deferred {
+ DEFERRED.with(|state| state.borrow_mut().pending.push(Pending {
+ launch: self, description, enqueue_ms, stream: stream.0 as usize,
+ }));
+ return Ok(());
+ }
+ hipEventSynchronize(self.events.end)?;
let completion_ms = self.host.elapsed().as_secs_f64() * 1000.;
let mut gpu_ms = 0.;
- hipEventElapsedTime(&mut gpu_ms, self.start, self.end)?;
- // Module function handles use the Driver API attribute query. The
- // plural Runtime API expects a registered host function address.
- let mut lds = 0;
- let mut regs = 0;
- let mut private = 0;
- let mut max_threads = 0;
- hipFuncGetAttribute(&mut lds, hipFunction_attribute::HIP_FUNC_ATTRIBUTE_SHARED_SIZE_BYTES, function)?;
- hipFuncGetAttribute(&mut regs, hipFunction_attribute::HIP_FUNC_ATTRIBUTE_NUM_REGS, function)?;
- hipFuncGetAttribute(&mut private, hipFunction_attribute::HIP_FUNC_ATTRIBUTE_LOCAL_SIZE_BYTES, function)?;
- hipFuncGetAttribute(&mut max_threads, hipFunction_attribute::HIP_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, function)?;
- let threads = block.iter().try_fold(1u32, |a, b| a.checked_mul(*b))
- .filter(|n| *n <= i32::MAX as u32).ok_or(hipErrorCode_t::InvalidValue)?;
- let mut blocks_per_multiprocessor = 0;
- hipModuleOccupancyMaxActiveBlocksPerMultiprocessor(&mut blocks_per_multiprocessor,
- function, threads as i32, shared as usize)?;
- eprintln!("D4R_KERNEL_PROFILE kernel={name:?} backend={backend} phase={phase} gpu_ms={gpu_ms:.6} enqueue_ms={enqueue_ms:.6} completion_ms={completion_ms:.6} grid={},{},{} block={},{},{} static_lds_bytes={} dynamic_lds_bytes={shared} regs={} private_bytes={} max_threads={} predicted_blocks_per_multiprocessor={blocks_per_multiprocessor} serializing=1",
- grid[0], grid[1], grid[2], block[0], block[1], block[2], lds,
- regs, private, max_threads);
+ hipEventElapsedTime(&mut gpu_ms, self.events.start, self.events.end)?;
+ description.print(gpu_ms, enqueue_ms, completion_ms, true, stream.0 as usize);
Ok(())
}
}
diff --git a/zluda/src/impl/module.rs b/zluda/src/impl/module.rs
index 232f7d4..8d43abf 100644
--- a/zluda/src/impl/module.rs
+++ b/zluda/src/impl/module.rs
@@ -503,6 +503,7 @@ fn zluda_build_version() -> &'static str {
&& !matches!(
key.as_str(),
"D4R_ZLUDA_NATIVE_DIR" | "D4R_ZLUDA_DIR" | "D4R_ZLUDA_DUMP_DIR" | "D4R_ZLUDA_DUMP_PTX_DIR" | "D4R_ZLUDA_LIBCUDA" | "D4R_ZLUDA_VERBOSE" | "D4R_ZLUDA_PROFILE"
+ | "D4R_ZLUDA_PROFILE_API" | "D4R_ZLUDA_PROFILE_DEFERRED" | "D4R_ZLUDA_PROFILE_EVERY" | "D4R_ZLUDA_PROFILE_ALLOW_LEGACY"
| "D4R_ZLUDA_WMMA_FP8_NATIVE" // keyed by effect (ExtraCacheAttributes)
)
})
--
2.55.0.windows.5
@@ -0,0 +1,50 @@
From 67127dde599879ada022d6439383d682e09e0b36 Mon Sep 17 00:00:00 2001
From: xdfnx-dev <[email protected]>
Date: Fri, 2 Oct 2026 11:50:45 +0300
Subject: [PATCH 17/17] Implement asynchronous 2D array copies for Windows
input batching
---
zluda/src/impl/memory.rs | 13 +++++++++++++
zluda/src/lib.rs | 1 +
2 files changed, 14 insertions(+)
diff --git a/zluda/src/impl/memory.rs b/zluda/src/impl/memory.rs
index b724942..42facb3 100644
--- a/zluda/src/impl/memory.rs
+++ b/zluda/src/impl/memory.rs
@@ -469,6 +469,19 @@ pub(crate) unsafe fn copy_2d_v2(memcpy: hip_Memcpy2D) -> CUresult {
Ok(())
}
+pub(crate) unsafe fn copy_2d_async_v2(memcpy: hip_Memcpy2D, stream: hipStream_t) -> CUresult {
+ // The format-80 emulation in copy_2d_v2 uses temporary CPU storage.
+ // Reject it explicitly here: an asynchronous copy cannot outlive those
+ // temporaries, and the Windows fast path uses canonical float arrays.
+ let src_packed = memcpy.srcMemoryType == hipMemoryType::hipMemoryTypeArray
+ && array::original_format(memcpy.srcArray)? == Some(80);
+ let dst_packed = memcpy.dstMemoryType == hipMemoryType::hipMemoryTypeArray
+ && array::original_format(memcpy.dstArray)? == Some(80);
+ if src_packed || dst_packed { return Err(CUerror::NOT_SUPPORTED); }
+ hipMemcpyParam2DAsync(&memcpy, stream)?;
+ Ok(())
+}
+
pub(crate) unsafe fn copy_2d_unaligned_v2(memcpy: hip_Memcpy2D) -> CUresult {
hipDrvMemcpy2DUnaligned(&memcpy)?;
Ok(())
diff --git a/zluda/src/lib.rs b/zluda/src/lib.rs
index 31bfa42..939981e 100644
--- a/zluda/src/lib.rs
+++ b/zluda/src/lib.rs
@@ -150,6 +150,7 @@ cuda_macros::cuda_function_declarations!(
cuMemAlloc_v2,
cuMemAllocPitch_v2,
cuMemcpy2D_v2,
+ cuMemcpy2DAsync_v2,
cuMemcpy2DUnaligned_v2,
cuMemcpy3D_v2,
cuMemcpyAsync,
--
2.55.0.windows.5
@@ -0,0 +1,93 @@
From fb0adc8714f57647320f0601533ec50331e378b0 Mon Sep 17 00:00:00 2001
From: xdfnx-dev <[email protected]>
Date: Fri, 2 Oct 2026 14:40:56 +0300
Subject: [PATCH] Add opt-in RDNA CU/WGP placement for matched native
experiments
---
llvm_zluda/src/compile.rs | 42 ++++++++++++++++++++++++++++++++++-----
ptx/src/pass/llvm/emit.rs | 3 ++-
2 files changed, 39 insertions(+), 6 deletions(-)
diff --git a/llvm_zluda/src/compile.rs b/llvm_zluda/src/compile.rs
index 90c0034..6a2f452 100644
--- a/llvm_zluda/src/compile.rs
+++ b/llvm_zluda/src/compile.rs
@@ -24,6 +24,37 @@ const OCKL_MODULE: &[u8] = include_bytes!("device-libs/ockl.bc");
// https://llvm.org/docs/AMDGPUUsage.html#address-spaces
const CONSTANT_ADDRESS_SPACE: u32 = 4;
+/// Opt-in RDNA scheduling experiment; changes placement, not FP semantics.
+pub fn wgp_requested() -> bool {
+ std::env::var("D4R_ZLUDA_WGP").is_ok_and(|value| value.trim() == "1")
+}
+
+fn target_features(wave64: bool, wgp: bool) -> &'static CStr {
+ match (wave64, wgp) {
+ (false, false) => c"-wavefrontsize64,+cumode",
+ (true, false) => c"-wavefrontsize32,+wavefrontsize64,+cumode",
+ (false, true) => c"-wavefrontsize64,-cumode",
+ (true, true) => c"-wavefrontsize32,+wavefrontsize64,-cumode",
+ }
+}
+
+#[cfg(test)]
+mod shader_mode_tests {
+ use super::target_features;
+
+ #[test]
+ fn wgp_changes_only_cu_placement_in_both_wave_modes() {
+ for wave64 in [false, true] {
+ let cu = target_features(wave64, false).to_str().unwrap();
+ let wgp = target_features(wave64, true).to_str().unwrap();
+ assert_eq!(cu.replace("+cumode", "-cumode"), wgp);
+ assert_eq!(cu.contains("+wavefrontsize64"), wave64);
+ assert_eq!(wgp.contains("+wavefrontsize64"), wave64);
+ }
+ assert_eq!(target_features(false, false), c"-wavefrontsize64,+cumode");
+ }
+}
+
fn load_module(ctx: &Context, bc: &[u8], name: &std::ffi::CStr) -> Result<Module, String> {
let module = Module::try_from_bitcode(ctx, bc, Some(name))
.ok_or(("Failed to parse bitcode").to_string())?;
@@ -229,11 +260,12 @@ pub fn compile(
let cpu = std::ffi::CString::new(gcn_arch).map_err(|_| ("invalid gcn_arch").to_string())?;
// functions without their own target features (the linked helper libraries) take these, so under
// D4R_ZLUDA_WAVE64 they must be optimised as wave64 too (lane-id and ballot folding)
- let features = if std::env::var("D4R_ZLUDA_WAVE64").map_or(false, |v| v.trim() == "1") {
- c"-wavefrontsize32,+wavefrontsize64,+cumode"
- } else {
- c"-wavefrontsize64,+cumode"
- };
+ let wgp = wgp_requested();
+ if wgp && !gcn_arch.starts_with("gfx11") && !gcn_arch.starts_with("gfx12") {
+ return Err(format!("D4R_ZLUDA_WGP requires RDNA3/4, got {gcn_arch}"));
+ }
+ let features = target_features(
+ std::env::var("D4R_ZLUDA_WAVE64").is_ok_and(|v| v.trim() == "1"), wgp);
let mut target = unsafe { std::mem::zeroed() };
let mut err = std::ptr::null_mut();
diff --git a/ptx/src/pass/llvm/emit.rs b/ptx/src/pass/llvm/emit.rs
index 6af2495..8b67c93 100644
--- a/ptx/src/pass/llvm/emit.rs
+++ b/ptx/src/pass/llvm/emit.rs
@@ -397,12 +397,13 @@ impl<'a, 'input> ModuleEmitContext<'a, 'input> {
fn emit_target_features(&mut self, fn_: LLVMValueRef) {
let value = format!(
- "{},+cumode{}",
+ "{},{}{}",
if crate::pass::wave64_requested() {
"-wavefrontsize32,+wavefrontsize64"
} else {
"+wavefrontsize32,-wavefrontsize64"
},
+ if llvm_zluda::compile::wgp_requested() { "-cumode" } else { "+cumode" },
if cfg!(debug_assertions) {
",+precise-memory"
} else {
--
2.55.0.windows.5
@@ -0,0 +1,17 @@
[CmdletBinding()]
param([Parameter(Mandatory=$true)][string]$Module,
[Parameter(Mandatory=$true)][string]$Candidate,
[Parameter(Mandatory=$true)][string]$FixtureDirectory,
[string]$HipRoot, [string]$Probe, [string]$OutputDirectory,
[ValidateRange(1,10000)][int]$Pairs=32,
[ValidateRange(1,256)][int[]]$Batches=@(1,64),
[ValidateSet('events','dispatch')][string[]]$Timings=@('events','dispatch'))
$ErrorActionPreference='Stop'
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
if (!$Probe) { $Probe=Join-Path $repo 'build/windows-rdna4-therock/d4r_native_replay_probe.exe' }
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/native-benchmark-'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
& python.exe (Join-Path $repo 'tools/windows/benchmark_native_replay.py') --probe $Probe --hip-root $HipRoot `
--module $Module --candidate $Candidate --fixture-dir $FixtureDirectory --output-dir $OutputDirectory `
--pairs $Pairs --batches @Batches --timings @Timings
if ($LASTEXITCODE) { throw "Native benchmark failed. Logs: $OutputDirectory" }
+28
View File
@@ -0,0 +1,28 @@
[CmdletBinding()]
param([string]$AdlxRoot, [string]$BuildDirectory, [string]$Compiler)
$ErrorActionPreference='Stop'
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
$commit='32b5a740d42295c5dfe9026b9f52683da0f3af91'
if (!$AdlxRoot) { $AdlxRoot=Join-Path $repo 'external/ADLX' }
if (!$BuildDirectory) { $BuildDirectory=Join-Path $repo 'build/gpu-telemetry' }
if (!$Compiler) { $Compiler=Join-Path $repo '.tools/llvm-mingw-20260922-ucrt-x86_64/bin/x86_64-w64-mingw32-clang.exe' }
if (!(Test-Path -LiteralPath $AdlxRoot)) {
& git clone --no-checkout https://github.com/GPUOpen-LibrariesAndSDKs/ADLX.git $AdlxRoot
if ($LASTEXITCODE) { throw 'ADLX SDK download failed.' }
& git -C $AdlxRoot checkout --detach $commit
if ($LASTEXITCODE) { throw 'ADLX SDK checkout failed.' }
}
if ((& git -C $AdlxRoot rev-parse HEAD).Trim() -ne $commit -or (& git -C $AdlxRoot status --porcelain)) {
throw "Expected clean ADLX SDK $commit; use a separate AdlxRoot."
}
if (!(Test-Path -LiteralPath $Compiler)) { throw 'Set Compiler to an x64 LLVM-MinGW clang.exe.' }
New-Item -ItemType Directory -Force $BuildDirectory | Out-Null
$exe=Join-Path $BuildDirectory 'd4r_gpu_telemetry.exe'
& $Compiler -std=c11 -O2 -Wall -Wextra -Wno-unknown-pragmas -DWIN32_LEAN_AND_MEAN -D_M_AMD64 `
-isystem "$AdlxRoot/SDK/Include" (Join-Path $repo 'tools/windows/gpu_telemetry.c') -o $exe
if ($LASTEXITCODE) { throw "GPU telemetry compile failed: $LASTEXITCODE" }
@{sdkCommit=$commit; sdkLicense=(Join-Path $AdlxRoot 'ADLX SDK License Agreement.pdf');
executable=$exe; compiler=$Compiler; sha256=(Get-FileHash -LiteralPath $exe -Algorithm SHA256).Hash} |
ConvertTo-Json | Set-Content -LiteralPath (Join-Path $BuildDirectory 'build-info.json') -Encoding UTF8
Write-Host "Built local read-only telemetry: $exe"
# The SDK, driver DLL and SDK-dependent binary are not added to public packages.
@@ -0,0 +1,68 @@
[CmdletBinding()]
param([ValidateSet('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0')]
[string]$Layer='enc1', [ValidateSet('source','cu','wgp')][string]$Mode='source',
[ValidateSet('baseline','load','store','both')][string]$NonTemporal='baseline',
[ValidateSet(0,1,2,4)][int]$PosTiles=0, [switch]$ShareValues, [switch]$PrivateValues,
[switch]$PackedAccumulator,
[string]$HipRoot, [string]$OutputDirectory)
$ErrorActionPreference='Stop'
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
if ($ShareValues -and $PrivateValues) { throw 'Choose shared or private V storage.' }
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo "build/native-k-experiment-$Layer-$Mode-$NonTemporal-pos$PosTiles-share$([int][bool]$ShareValues)-private$([int][bool]$PrivateValues)-packed$([int][bool]$PackedAccumulator)" }
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
foreach ($baseline in @('build/native-k-gfx1201','build/windows-rdna4-therock','dist/windows-rdna4-command-list')) {
if ($OutputDirectory.StartsWith([IO.Path]::GetFullPath((Join-Path $repo $baseline)), [StringComparison]::OrdinalIgnoreCase)) {
throw 'Keep experimental objects outside the validated baseline/package.'
}
}
$compiler=Join-Path $HipRoot 'lib/llvm/bin/clang++.exe'
if (!(Test-Path -LiteralPath $compiler)) { $compiler=Join-Path $HipRoot 'bin/clang++.exe' }
$name="dltss_pwin_${Layer}_layer"
$source=Join-Path $repo "kernels/k/$name.hip"
$flags=@()
if ((Get-Content -LiteralPath $source -Raw) -match '// d4r-build-flags: ([^\r\n]+)') { $flags=$Matches[1].Trim().Split(' ') }
if ($Mode -ne 'source') {
$flags=@($flags | Where-Object { $_ -ne '-mcumode' -and $_ -ne '-mno-cumode' })
$flags+=if ($Mode -eq 'cu') { '-mcumode' } else { '-mno-cumode' }
}
if ($NonTemporal -in @('load','both')) { $flags+='-DPWIN_NT_LOAD' }
if ($NonTemporal -in @('store','both')) { $flags+='-DPWIN_NT_STORE' }
if ($PosTiles) {
if ($Layer -notin @('enc0','enc1','dec0')) { throw 'Token tiling is audited for position-only layers only.' }
$flags+=if ($Layer -eq 'enc1') { "-DD4R_K_ENC1_POS_MT=$PosTiles" } else { "-DD4R_K_POS_MT=$PosTiles" }
}
if ($ShareValues) {
if ($Layer -notin @('enc0','dec0') -and !($Layer -eq 'enc1' -and $PosTiles)) {
throw 'ShareValues requires a position-only template.'
}
$flags+='-DPOS_VSHARE'
}
if ($PackedAccumulator) { $flags+='-DD4R_K_PACKED_ACC' }
if ($PrivateValues) { $flags+='-DD4R_K_PRIVATE_V' }
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$object=Join-Path $OutputDirectory "$name.hsaco"
$arguments=@('-x','hip','-std=c++17','--offload-arch=gfx1201','--offload-device-only',
'--no-gpu-bundle-output','-mno-wavefrontsize64','-nogpuinc','-nogpulib','-O3',
'-DD4R_DEVICE_ONLY_MINIMAL','-DD4R_K_FP16_BASELINE')+$flags+@($source,'-o',$object)
$ErrorActionPreference='Continue'
& $compiler @arguments > (Join-Path $OutputDirectory "$name.build.stdout.log") 2> (Join-Path $OutputDirectory "$name.build.stderr.log")
$ErrorActionPreference='Stop'
if ($LASTEXITCODE) { throw "Experimental K compile failed: $Layer" }
& (Join-Path (Split-Path $compiler) 'llvm-objdump.exe') --disassemble --mcpu=gfx1201 $object `
> (Join-Path $OutputDirectory "$name.isa.txt")
if ($LASTEXITCODE) { throw 'Native object ISA extraction failed.' }
& (Join-Path (Split-Path $compiler) 'llvm-readobj.exe') --notes $object `
> (Join-Path $OutputDirectory "$name.metadata.txt")
if ($LASTEXITCODE) { throw 'Native object metadata extraction failed.' }
@{layer=$Layer; architecture='gfx1201'; experimental=$true; numericalValidation='pending';
mode=$Mode; nonTemporal=$NonTemporal; tokenTilesPerWave=$PosTiles; shareValues=[bool]$ShareValues;
privateValues=[bool]$PrivateValues;
packedAccumulator=[bool]$PackedAccumulator;
compiler=$compiler; arguments=$arguments;
sourceCommit=(git -C $repo rev-parse HEAD); workingTreeDirty=[bool](& git -C $repo status --porcelain);
compilerVersion=(& $compiler --version | Out-String).Trim();
sha256=(Get-FileHash -LiteralPath $object -Algorithm SHA256).Hash} |
ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $OutputDirectory "$name.build-info.json") -Encoding UTF8
Write-Host "Compiled gfx1201 K experiment: $Layer mode=$Mode nonTemporal=$NonTemporal posTiles=$PosTiles shareValues=$([bool]$ShareValues). Validate before installing."
# No override manifest or game installation is produced by this script.
@@ -0,0 +1,40 @@
[CmdletBinding()]
param([ValidateSet('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0')]
[string]$Layer='enc1', [string]$HipRoot, [string]$OutputDirectory)
$ErrorActionPreference='Stop'
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo 'build/native-k-f16-wmma-experimental' }
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
if ($OutputDirectory -eq [IO.Path]::GetFullPath((Join-Path $repo 'build/native-k-gfx1201'))) {
throw 'Keep the validated baseline separate from this experimental build.'
}
$compiler=Join-Path $HipRoot 'lib/llvm/bin/clang++.exe'
if (!(Test-Path -LiteralPath $compiler)) { $compiler=Join-Path $HipRoot 'bin/clang++.exe' }
if (!(Test-Path -LiteralPath $compiler)) { throw "HIP compiler missing under $HipRoot" }
$name="dltss_pwin_${Layer}_layer"
$source=Join-Path $repo "kernels/k/$name.hip"
$flags=@()
if ((Get-Content -LiteralPath $source -Raw) -match '// d4r-build-flags: ([^\r\n]+)') { $flags=$Matches[1].Trim().Split(' ') }
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$object=Join-Path $OutputDirectory "$name.hsaco"
$ErrorActionPreference='Continue'
& $compiler -x hip -std=c++17 --offload-arch=gfx1201 --offload-device-only --no-gpu-bundle-output `
-mno-wavefrontsize64 -nogpuinc -nogpulib -O3 -DD4R_DEVICE_ONLY_MINIMAL `
-DD4R_K_FP16_BASELINE -DD4R_K_F16_WMMA @flags $source -o $object `
> (Join-Path $OutputDirectory "$name.build.stdout.log") 2> (Join-Path $OutputDirectory "$name.build.stderr.log")
$ErrorActionPreference='Stop'
if ($LASTEXITCODE) { throw "Experimental K FP16 WMMA compile failed: $Layer" }
$isa=Join-Path $OutputDirectory "$name.isa.txt"
& (Join-Path (Split-Path $compiler) 'llvm-objdump.exe') --disassemble --mcpu=gfx1201 $object > $isa
if ($LASTEXITCODE -or !(Select-String -LiteralPath $isa -Pattern 'v_wmma_f16_16x16x16_f16')) {
throw 'Packed FP16 WMMA is absent from ISA; no alternative instruction is accepted.'
}
& (Join-Path (Split-Path $compiler) 'llvm-readobj.exe') --notes $object `
> (Join-Path $OutputDirectory "$name.metadata.txt")
if ($LASTEXITCODE) { throw 'Native object metadata extraction failed.' }
@{layer=$Layer; architecture='gfx1201'; experimental=$true; numericalValidation='pending';
compiler=$compiler; sha256=(Get-FileHash -LiteralPath $object -Algorithm SHA256).Hash} |
ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory "$name.build-info.json") -Encoding UTF8
Write-Host "Compiled experimental gfx1201 packed FP16 WMMA: $Layer. Validate before selecting another layer."
# This script does not generate an override manifest or install into the game.
+6 -2
View File
@@ -4,12 +4,16 @@ param(
[string]$Kernel = 'hiluma_engine_output_depthinv_mvlo_hdr_max_v2_rel', [string]$Kernel = 'hiluma_engine_output_depthinv_mvlo_hdr_max_v2_rel',
[string]$HipRoot = 'C:\Program Files\AMD\ROCm\7.2', [string]$HipRoot = 'C:\Program Files\AMD\ROCm\7.2',
[string]$ZludaRoot, [string]$ZludaRoot,
[ValidateSet('cu','wgp')][string]$ShaderMode='cu',
[string]$GpuArch='gfx1201',
[string]$OutputDirectory [string]$OutputDirectory
) )
$ErrorActionPreference='Stop' $ErrorActionPreference='Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
[void](Get-D4RGpuTargetInfo $GpuArch)
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' } 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 ` & python (Join-Path $repo 'tools/windows/build_native_texture.py') --dlss-dll $DlssDll --hip-root $HipRoot `
--zluda-root $ZludaRoot --kernel $Kernel --output-directory $OutputDirectory --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." } if ($LASTEXITCODE) { throw "Native texture build failed ($LASTEXITCODE); inspect the output work directory." }
+18 -3
View File
@@ -41,8 +41,23 @@ try {
if ($LASTEXITCODE) { throw "Source patch failed: $($patch.Name)" } if ($LASTEXITCODE) { throw "Source patch failed: $($patch.Name)" }
} }
} }
& git -C $SourceRoot submodule update --init --depth 1 # Verify existing checkouts directly. A complete local checkout does not
if ($LASTEXITCODE) { throw 'Pinned OptiScaler submodules failed.' } # need Git's shell-based submodule helper (or a network connection).
$submodulePaths=@(& git -C $SourceRoot config --file .gitmodules --get-regexp '\.path$' | ForEach-Object { ($_ -split ' ',2)[1] })
$submodules=@()
foreach ($path in $submodulePaths) {
$tree=(& git -C $SourceRoot ls-tree HEAD -- $path) -split '\s+',4
if ($tree.Count -lt 4 -or $tree[0] -ne '160000') { throw "Missing pinned submodule: $path" }
$checkout=Join-Path $SourceRoot $path
$actual=if (Test-Path -LiteralPath (Join-Path $checkout '.git')) { (& git -C $checkout rev-parse HEAD).Trim() } else { '' }
if ($actual -ne $tree[2]) {
& git -C $SourceRoot submodule update --init --depth 1 -- $path
if ($LASTEXITCODE) { throw "Pinned OptiScaler submodule failed: $path" }
$actual=(& git -C $checkout rev-parse HEAD).Trim()
if ($actual -ne $tree[2]) { throw "Wrong submodule revision: $path" }
}
$submodules+=(' '+$actual+' '+$path)
}
if (!$BuildToolsRoot -and (Test-Path -LiteralPath (Join-Path $repo '.tools/vs2022/MSBuild/Current/Bin/MSBuild.exe'))) { if (!$BuildToolsRoot -and (Test-Path -LiteralPath (Join-Path $repo '.tools/vs2022/MSBuild/Current/Bin/MSBuild.exe'))) {
$BuildToolsRoot = Join-Path $repo '.tools/vs2022' $BuildToolsRoot = Join-Path $repo '.tools/vs2022'
} }
@@ -72,7 +87,7 @@ try {
buildTools=$BuildToolsRoot; msbuildVersion=(& $msbuild -version -nologo | Select-Object -Last 1); buildTools=$BuildToolsRoot; msbuildVersion=(& $msbuild -version -nologo | Select-Object -Last 1);
patches=@($patches | ForEach-Object { @{name=$_.Name; sha256=(Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash} }); patches=@($patches | ForEach-Object { @{name=$_.Name; sha256=(Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash} });
dllSha256=(Get-FileHash -LiteralPath (Join-Path $InstallRoot 'OptiScaler.dll') -Algorithm SHA256).Hash; dllSha256=(Get-FileHash -LiteralPath (Join-Path $InstallRoot 'OptiScaler.dll') -Algorithm SHA256).Hash;
submodules=@(& git -C $SourceRoot submodule status)} submodules=$submodules}
$metadata | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $InstallRoot 'build-info.json') -Encoding UTF8 $metadata | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $InstallRoot 'build-info.json') -Encoding UTF8
Write-Host "Built OptiScaler with external Windows d4r backend: $InstallRoot" Write-Host "Built OptiScaler with external Windows d4r backend: $InstallRoot"
} finally { $env:PATH = $previousPath } } finally { $env:PATH = $previousPath }
@@ -0,0 +1,44 @@
[CmdletBinding()]
param(
[string[]]$GpuArchitectures,
[ValidateSet('stable','therock')][string]$RuntimeProfile='therock',
[string]$ZludaRoot, [string]$DlssDll,
[switch]$PackageGames,
[string]$BuildRoot, [string]$OutputRoot, [string]$ArchiveDirectory
)
$ErrorActionPreference='Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$GpuArchitectures) { $GpuArchitectures=@(Get-D4RGpuTargets | ForEach-Object { $_.architecture }) }
foreach ($arch in $GpuArchitectures) { [void](Get-D4RGpuTargetInfo $arch) }
if (@($GpuArchitectures | Select-Object -Unique).Count -ne $GpuArchitectures.Count) { throw 'Duplicate GPU target.' }
if (!$BuildRoot) { $BuildRoot=Join-Path $repo 'build/windows-gpu-coverage' }
if (!$OutputRoot) { $OutputRoot=Join-Path $repo 'dist/windows-gpu-coverage' }
if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' }
if ($ArchiveDirectory -and !$PackageGames) { throw 'ArchiveDirectory requires PackageGames.' }
if ($PackageGames -and (!$DlssDll -or ![IO.Path]::IsPathRooted($DlssDll) -or !(Test-Path -LiteralPath $DlssDll))) {
throw 'PackageGames requires an absolute path to your local nvngx_dlss.dll; it will not be redistributed.'
}
New-Item -ItemType Directory -Force $BuildRoot | Out-Null
if ($ArchiveDirectory) { New-Item -ItemType Directory -Force $ArchiveDirectory | Out-Null }
$results=[Collections.Generic.List[object]]::new()
foreach ($arch in $GpuArchitectures) {
$build="$BuildRoot-$arch"; $diag="$OutputRoot-$arch"
$log=Join-Path $BuildRoot "$arch-build.log"
Write-Host "Building $arch (compile/package only; no GPU workloads). Log: $log"
& (Join-Path $PSScriptRoot 'build-windows-rdna4.ps1') -RuntimeProfile $RuntimeProfile -GpuArch $arch `
-ZludaRoot $ZludaRoot -BuildDirectory $build -InstallDirectory $diag *> $log
$row=[ordered]@{architecture=$arch; compiled=$true; hardwareTestExecuted=$false; buildDirectory=$build; diagnosticRoot=$diag; log=$log}
if ($PackageGames) {
$k="$BuildRoot-native-k-$arch"; $m="$BuildRoot-native-m-$arch"; $game="$OutputRoot-game-$arch"
& (Join-Path $PSScriptRoot 'stage-native-k.ps1') -DlssDll $DlssDll -PackageRoot $diag -GpuArch $arch -OutputDirectory $k
& (Join-Path $PSScriptRoot 'stage-native-m.ps1') -DlssDll $DlssDll -PackageRoot $diag -GpuArch $arch -OutputDirectory $m
$packageArgs=@{DiagnosticRoot=$diag; GpuArch=$arch; PackageRoot=$game; ZludaRoot=$ZludaRoot; KernelKRoot=$k; KernelMRoot=$m}
if ($ArchiveDirectory) { $packageArgs.ArchivePath=Join-Path $ArchiveDirectory "d4r-windows-$arch.zip" }
& (Join-Path $PSScriptRoot 'package-windows-game.ps1') @packageArgs
$row.gamePackage=$game
}
$results.Add($row)
$results | ConvertTo-Json -Depth 5 | Set-Content (Join-Path $BuildRoot 'matrix-build.json') -Encoding UTF8
Write-Host "Compiled $arch; hardware validation remains separate."
}
+8 -1
View File
@@ -2,12 +2,15 @@
param( param(
[string]$HipRoot = $env:HIP_PATH, [string]$HipRoot = $env:HIP_PATH,
[ValidateSet('stable', 'therock')][string]$RuntimeProfile = 'stable', [ValidateSet('stable', 'therock')][string]$RuntimeProfile = 'stable',
[string]$GpuArch = 'gfx1201',
[string]$ZludaRoot, [string]$ZludaRoot,
[string]$ToolchainRoot, [string]$ToolchainRoot,
[string]$BuildDirectory, [string]$BuildDirectory,
[string]$InstallDirectory [string]$InstallDirectory
) )
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
[void](Get-D4RGpuTargetInfo $GpuArch)
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if ($RuntimeProfile -eq 'therock') { if ($RuntimeProfile -eq 'therock') {
if (!$PSBoundParameters.ContainsKey('HipRoot')) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' } if (!$PSBoundParameters.ContainsKey('HipRoot')) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
@@ -17,6 +20,10 @@ if ($RuntimeProfile -eq 'therock') {
if (!$HipRoot) { $HipRoot = 'C:\Program Files\AMD\ROCm\7.2' } if (!$HipRoot) { $HipRoot = 'C:\Program Files\AMD\ROCm\7.2' }
if (!$BuildDirectory) { $BuildDirectory = Join-Path $repo 'build/windows-rdna4' } if (!$BuildDirectory) { $BuildDirectory = Join-Path $repo 'build/windows-rdna4' }
if (!$InstallDirectory) { $InstallDirectory = Join-Path $repo 'dist/windows-rdna4-diagnostics' } 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('\', '/') $HipRoot = [IO.Path]::GetFullPath($HipRoot).TrimEnd('\', '/')
$BuildDirectory = [IO.Path]::GetFullPath($BuildDirectory).TrimEnd('\', '/') $BuildDirectory = [IO.Path]::GetFullPath($BuildDirectory).TrimEnd('\', '/')
$InstallDirectory = [IO.Path]::GetFullPath($InstallDirectory).TrimEnd('\', '/') $InstallDirectory = [IO.Path]::GetFullPath($InstallDirectory).TrimEnd('\', '/')
@@ -47,7 +54,7 @@ if ($minhookCommit -ne 'c3fcafdc10146beb5919319d0683e44e3c30d537') {
$originalPath = $env:PATH $originalPath = $env:PATH
try { try {
$configure = @('--fresh', '-S', $repo, '-B', $BuildDirectory, '-G', 'Ninja', '-DCMAKE_BUILD_TYPE=RelWithDebInfo', $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) { if ($ToolchainRoot) {
$compiler = Join-Path $ToolchainRoot 'bin/x86_64-w64-mingw32-clang++.exe' $compiler = Join-Path $ToolchainRoot 'bin/x86_64-w64-mingw32-clang++.exe'
if (!(Test-Path $compiler)) { throw "Compiler missing: $compiler" } if (!(Test-Path $compiler)) { throw "Compiler missing: $compiler" }
+5
View File
@@ -12,6 +12,7 @@ if (!(Test-Path (Join-Path $SourceRoot 'ext/llvm-project/llvm/CMakeLists.txt')))
throw 'Run git submodule update --init --depth 1 ext/llvm-project in the ZLUDA checkout.' throw 'Run git submodule update --init --depth 1 ext/llvm-project in the ZLUDA checkout.'
} }
New-Item -ItemType Directory -Force $BuildRoot | Out-Null New-Item -ItemType Directory -Force $BuildRoot | Out-Null
& (Join-Path $PSScriptRoot 'prepare-zluda-llvm-source.ps1') -LlvmSourceRoot (Join-Path $SourceRoot 'ext/llvm-project')
$oldPath = $env:PATH $oldPath = $env:PATH
try { try {
$env:PATH = "$(Join-Path $toolchain 'bin');$oldPath" $env:PATH = "$(Join-Path $toolchain 'bin');$oldPath"
@@ -36,5 +37,9 @@ try {
if ($LASTEXITCODE) { throw "LLVM build failed ($LASTEXITCODE)" } if ($LASTEXITCODE) { throw "LLVM build failed ($LASTEXITCODE)" }
& (Join-Path $BuildRoot 'bin/llvm-config.exe') --version & (Join-Path $BuildRoot 'bin/llvm-config.exe') --version
if ($LASTEXITCODE) { throw 'llvm-config verification failed' } if ($LASTEXITCODE) { throw 'llvm-config verification failed' }
@{sourceBase=(& git -C (Join-Path $SourceRoot 'ext/llvm-project') rev-parse HEAD).Trim(); fullSourceDeltaVerified=$true;
sourcePatches=@(Get-ChildItem (Join-Path $repo 'patches/zluda-llvm') -Filter *.patch | ForEach-Object {
@{name=$_.Name; sha256=(Get-FileHash -LiteralPath $_.FullName).Hash}})} |
ConvertTo-Json -Depth 5 | Set-Content (Join-Path $BuildRoot 'd4r-llvm-build-info.json') -Encoding UTF8
Write-Host "Native Windows LLVM: $BuildRoot" Write-Host "Native Windows LLVM: $BuildRoot"
} finally { $env:PATH = $oldPath } } finally { $env:PATH = $oldPath }
+12 -1
View File
@@ -1,5 +1,5 @@
[CmdletBinding()] [CmdletBinding()]
param([string]$SourceRoot, [string]$LlvmBuildRoot, [string]$InstallRoot, [switch]$FetchOnly, [switch]$CacheTestsOnly, [int]$Jobs = 4) param([string]$SourceRoot, [string]$LlvmBuildRoot, [string]$InstallRoot, [switch]$FetchOnly, [switch]$CacheTestsOnly, [switch]$CompilerTestsOnly, [int]$Jobs = 4)
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$SourceRoot) { $SourceRoot = Join-Path $repo 'external/ZLUDA' } if (!$SourceRoot) { $SourceRoot = Join-Path $repo 'external/ZLUDA' }
@@ -33,6 +33,14 @@ Push-Location $SourceRoot
try { try {
$cargo = Join-Path $rust 'bin/cargo.exe' $cargo = Join-Path $rust 'bin/cargo.exe'
if (!(Test-Path $cargo)) { throw 'Run setup-rust-toolchain.ps1 first.' } if (!(Test-Path $cargo)) { throw 'Run setup-rust-toolchain.ps1 first.' }
if ($CompilerTestsOnly) {
$ErrorActionPreference = 'Continue'
& $cargo test --locked --release --target x86_64-pc-windows-gnu -p llvm_zluda shader_mode_tests `
2>&1 | Tee-Object (Join-Path $target 'compiler-tests.log')
$ErrorActionPreference = 'Stop'
if ($LASTEXITCODE) { throw "ZLUDA compiler regression tests failed ($LASTEXITCODE)" }
return
}
if ($CacheTestsOnly) { if ($CacheTestsOnly) {
$ErrorActionPreference = 'Continue' $ErrorActionPreference = 'Continue'
& $cargo test --locked --release --target x86_64-pc-windows-gnu -p zluda_cache ` & $cargo test --locked --release --target x86_64-pc-windows-gnu -p zluda_cache `
@@ -76,6 +84,9 @@ try {
@{base='ee2f25a180099fa42f36b2346732e1f2470a03ad'; target='x86_64-pc-windows-gnu'; @{base='ee2f25a180099fa42f36b2346732e1f2470a03ad'; target='x86_64-pc-windows-gnu';
sourceCommit=(& git rev-parse HEAD); workingTreeDirty=[bool](& git status --porcelain); sourceCommit=(& git rev-parse HEAD); workingTreeDirty=[bool](& git status --porcelain);
rust='1.98.1'; llvm=(& (Join-Path $LlvmBuildRoot 'bin/llvm-config.exe') --version); rust='1.98.1'; llvm=(& (Join-Path $LlvmBuildRoot 'bin/llvm-config.exe') --version);
llvmSource=$(if (Test-Path (Join-Path $LlvmBuildRoot 'd4r-llvm-build-info.json')) {
Get-Content (Join-Path $LlvmBuildRoot 'd4r-llvm-build-info.json') -Raw | ConvertFrom-Json
} else { $null });
files=@(Get-ChildItem -LiteralPath $InstallRoot -Filter '*.dll' | Get-FileHash -Algorithm SHA256)} | files=@(Get-ChildItem -LiteralPath $InstallRoot -Filter '*.dll' | Get-FileHash -Algorithm SHA256)} |
ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $InstallRoot 'build-info.json') -Encoding UTF8 ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $InstallRoot 'build-info.json') -Encoding UTF8
Write-Host "Native Windows d4r ZLUDA runtime: $InstallRoot" Write-Host "Native Windows d4r ZLUDA runtime: $InstallRoot"
+56 -2
View File
@@ -5,6 +5,9 @@ param(
[ValidateRange(5,300)][int]$Seconds = 30, [ValidateRange(5,300)][int]$Seconds = 30,
[ValidateRange(0,300)][int]$DelaySeconds = 0, [ValidateRange(0,300)][int]$DelaySeconds = 0,
[string]$PresentMonPath, [string]$PresentMonPath,
[switch]$GpuTelemetry,
[string]$TelemetryPath,
[string]$GpuName = 'RX 9070 XT',
[string]$OutputDirectory [string]$OutputDirectory
) )
$ErrorActionPreference='Stop' $ErrorActionPreference='Stop'
@@ -32,16 +35,67 @@ $OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$csv=Join-Path $OutputDirectory 'presentmon.csv' $csv=Join-Path $OutputDirectory 'presentmon.csv'
if (Test-Path -LiteralPath $csv) { throw 'Preserve the existing capture; choose another OutputDirectory.' } if (Test-Path -LiteralPath $csv) { throw 'Preserve the existing capture; choose another OutputDirectory.' }
if ($TelemetryPath) { $GpuTelemetry=$true }
if ($GpuTelemetry) {
if (!$TelemetryPath) { $TelemetryPath=Join-Path $repo 'build/gpu-telemetry/d4r_gpu_telemetry.exe' }
$TelemetryPath=(Get-Item -LiteralPath $TelemetryPath -ErrorAction Stop).FullName
if ($GpuName.Contains('"')) { throw 'GpuName cannot contain a double quote.' }
foreach ($file in @('gpu-telemetry.csv','gpu-telemetry.stderr.log','gpu-telemetry-environment.json')) {
if (Test-Path -LiteralPath (Join-Path $OutputDirectory $file)) { throw "Preserve existing $file; choose another OutputDirectory." }
}
}
@{version='2.6.0'; executable=$PresentMonPath; sha256=(Get-FileHash -LiteralPath $PresentMonPath -Algorithm SHA256).Hash; @{version='2.6.0'; executable=$PresentMonPath; sha256=(Get-FileHash -LiteralPath $PresentMonPath -Algorithm SHA256).Hash;
targetProcessId=$target.Id; processName=$target.ProcessName; seconds=$Seconds; delaySeconds=$DelaySeconds; targetProcessId=$target.Id; processName=$target.ProcessName; seconds=$Seconds; delaySeconds=$DelaySeconds;
gpuTelemetryRequested=[bool]$GpuTelemetry;
hwsRegistry=$(Get-ItemPropertyValue -LiteralPath 'HKLM:/SYSTEM/CurrentControlSet/Control/GraphicsDrivers' -Name HwSchMode -ErrorAction SilentlyContinue); hwsRegistry=$(Get-ItemPropertyValue -LiteralPath 'HKLM:/SYSTEM/CurrentControlSet/Control/GraphicsDrivers' -Name HwSchMode -ErrorAction SilentlyContinue);
driver=@(Get-CimInstance Win32_VideoController | Select-Object Name,DriverVersion)} | driver=@(Get-CimInstance Win32_VideoController | Select-Object Name,DriverVersion)} |
ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'presentmon-environment.json') -Encoding UTF8 ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'presentmon-environment.json') -Encoding UTF8
# Unique ETW session: never stop or replace an existing user's trace. The CLI # Unique ETW session: never stop or replace an existing user's trace. The CLI
# terminates itself at the bound/process exit, without a service installation. # terminates itself at the bound/process exit, without a service installation.
$session='D4R_' + [Guid]::NewGuid().ToString('N') $session='D4R_' + [Guid]::NewGuid().ToString('N')
& $PresentMonPath --process_id $target.Id --timed $Seconds --delay $DelaySeconds --terminate_after_timed --terminate_on_proc_exit --session_name $session --no_console_stats --no_track_input --qpc_time_ms --output_file $csv 2>&1 | $telemetry=$null; $telemetryStarted=$false; $outStream=$null; $errStream=$null; $outTask=$null; $errTask=$null
try {
if ($GpuTelemetry) {
$info=[Diagnostics.ProcessStartInfo]::new()
$info.FileName=$TelemetryPath; $info.UseShellExecute=$false; $info.CreateNoWindow=$true
$info.RedirectStandardOutput=$true; $info.RedirectStandardError=$true
$info.Arguments="--seconds $Seconds --delay-ms $($DelaySeconds*1000) --interval-ms 500 --target-pid $($target.Id) --gpu-name `"$GpuName`""
$outStream=[IO.File]::Open((Join-Path $OutputDirectory 'gpu-telemetry.csv'),[IO.FileMode]::CreateNew,[IO.FileAccess]::Write,[IO.FileShare]::Read)
$errStream=[IO.File]::Open((Join-Path $OutputDirectory 'gpu-telemetry.stderr.log'),[IO.FileMode]::CreateNew,[IO.FileAccess]::Write,[IO.FileShare]::Read)
$telemetry=[Diagnostics.Process]::new(); $telemetry.StartInfo=$info
if (!$telemetry.Start()) { throw 'GPU telemetry process failed to start.' }
$telemetryStarted=$true
$outTask=$telemetry.StandardOutput.BaseStream.CopyToAsync($outStream)
$errTask=$telemetry.StandardError.BaseStream.CopyToAsync($errStream)
}
& $PresentMonPath --process_id $target.Id --timed $Seconds --delay $DelaySeconds --terminate_after_timed --terminate_on_proc_exit --session_name $session --no_console_stats --no_track_input --qpc_time_ms --output_file $csv 2>&1 |
Tee-Object -FilePath (Join-Path $OutputDirectory 'presentmon-console.log') Tee-Object -FilePath (Join-Path $OutputDirectory 'presentmon-console.log')
if ($LASTEXITCODE) { throw "PresentMon failed with exit code $LASTEXITCODE; see presentmon-console.log." } if ($LASTEXITCODE) { throw "PresentMon failed with exit code $LASTEXITCODE; see presentmon-console.log." }
} finally {
try { if ($telemetryStarted) {
$timedOut=$false
if (!$telemetry.HasExited -and !$telemetry.WaitForExit(30000)) { $timedOut=$true; $telemetry.Kill(); $telemetry.WaitForExit() }
if ($outTask) { [void]$outTask.GetAwaiter().GetResult() }
if ($errTask) { [void]$errTask.GetAwaiter().GetResult() }
$driverDll=Join-Path $env:WINDIR 'System32/amdadlx64.dll'
$driverInfo=$null
if (Test-Path -LiteralPath $driverDll) {
$driverInfo=@{path=$driverDll; version=(Get-Item -LiteralPath $driverDll).VersionInfo.FileVersion;
sha256=(Get-FileHash -LiteralPath $driverDll -Algorithm SHA256).Hash}
}
@{executable=$TelemetryPath; sha256=(Get-FileHash -LiteralPath $TelemetryPath -Algorithm SHA256).Hash;
exitCode=$telemetry.ExitCode; timeout=$timedOut; targetProcessId=$target.Id; gpuName=$GpuName;
intervalMs=500; driverDll=$driverInfo; readOnly=$true;
buildInfo=$(if (Test-Path -LiteralPath (Join-Path (Split-Path $TelemetryPath) 'build-info.json')) {
Get-Content -LiteralPath (Join-Path (Split-Path $TelemetryPath) 'build-info.json') -Raw | ConvertFrom-Json
})} |
ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'gpu-telemetry-environment.json') -Encoding UTF8
if ($telemetry.ExitCode -ne 0) { Write-Warning 'GPU telemetry failed; retain PresentMon capture and see gpu-telemetry.stderr.log.' }
} } finally {
if ($telemetry) { $telemetry.Dispose() }
if ($outStream) { $outStream.Dispose() }
if ($errStream) { $errStream.Dispose() }
}
}
if (!(Test-Path -LiteralPath $csv) -or (Get-Item -LiteralPath $csv).Length -eq 0) { throw 'PresentMon captured no frames.' } if (!(Test-Path -LiteralPath $csv) -or (Get-Item -LiteralPath $csv).Length -eq 0) { throw 'PresentMon captured no frames.' }
Write-Host "PresentMon capture: $csv" Write-Host "PresentMon capture: $csv"
+271
View File
@@ -0,0 +1,271 @@
# Checks a d4r install on Windows. It only reads files and prints what it finds.
# Run it from the game folder (the one holding the game's .exe and d4r\), or give that folder:
# powershell -ExecutionPolicy Bypass -File d4r\d4r-check.ps1 [GAME_FOLDER]
# Add -Report to also write one ZIP (logs, this output, GPU and driver) to send with a bug report.
[CmdletBinding()]
param(
[string]$GameFolder,
[switch]$Report,
[string]$InventoryFile, # tests: a gpu-inventory.json to use instead of running the GPU query
[string]$ReportDirectory # tests: where -Report writes its ZIP (default: your Desktop)
)
$ErrorActionPreference = 'Stop'
if (!$GameFolder) {
$GameFolder = if ((Split-Path $PSScriptRoot -Leaf) -eq 'd4r') { Split-Path $PSScriptRoot -Parent } else { (Get-Location).Path }
}
$game = [IO.Path]::GetFullPath($GameFolder).TrimEnd('\')
$d4r = Join-Path $game 'd4r'
$problems = 0
$lines = [Collections.Generic.List[string]]::new()
function Say([string]$text) { $lines.Add($text); Write-Host $text }
function Ok([string]$text) { Say (' ok ' + $text) }
function Bad([string]$text) { Say (' MISSING ' + $text); $script:problems++ }
function Note([string]$text) { Say (' note ' + $text) }
function Hash([string]$path) { (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash }
function Version([string]$path) {
try { $v = (Get-Item -LiteralPath $path).VersionInfo; if ($v.FileVersion) { return ($v.FileVersion -replace ',\s*', '.') } } catch {}
return 'unknown'
}
function Read-TextFile([string]$path) { [IO.File]::ReadAllText($path) }
Say "d4r install in $game"
if (!(Test-Path -LiteralPath $d4r -PathType Container)) {
Say 'no d4r folder here; run this from the folder with the game .exe (extract the d4r ZIP there first)'
exit 1
}
if (@(Get-ChildItem -LiteralPath $game -Filter '*.exe' -File -ErrorAction SilentlyContinue).Count) { Ok 'game executable next to d4r\' }
else { Note 'no .exe next to d4r\ (the d4r files must sit in the folder of the game''s main .exe; for Unreal Engine games that is <game>\<Project>\Binaries\Win64)' }
# --- the install's own file list ---------------------------------------------------------------
$manifest = $null
$manifestPath = Join-Path $d4r 'package.json'
if (Test-Path -LiteralPath $manifestPath) {
try { $manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json } catch { $manifest = $null }
}
if (!$manifest) { Bad 'd4r\package.json is missing or damaged (extract the d4r ZIP again)' }
# --- files --------------------------------------------------------------------------------------
$required = @('dxgi.dll', 'OptiScaler.ini', 'd4r\_nvngx.dll', 'd4r\d4r.ini', 'd4r\dll-pins.txt',
'd4r\hip\bin\amdhip64_7.dll', 'd4r\zluda\nvcuda.dll', 'd4r\zluda\nvapi64.dll')
foreach ($file in $required) {
if (Test-Path -LiteralPath (Join-Path $game $file) -PathType Leaf) { Ok $file }
else { Bad "$file (extract the d4r ZIP again over the game folder)" }
}
if ($manifest) {
$edited = @('OptiScaler.ini', 'd4r/d4r.ini') # players are meant to edit these
$damaged = 0
foreach ($entry in $manifest.files) {
$path = Join-Path $game ($entry.path -replace '/', '\')
if (!(Test-Path -LiteralPath $path -PathType Leaf)) {
if ($required -notcontains ($entry.path -replace '/', '\')) { Bad "$($entry.path) (extract the d4r ZIP again)" }
continue
}
if ((Hash $path) -ne $entry.sha256) {
if ($entry.path -eq 'dxgi.dll') { Note 'dxgi.dll is not the one that came with d4r (another OptiScaler or ReShade?). Move it away and extract the d4r ZIP again' }
elseif ($edited -contains $entry.path) { Note "$($entry.path) has been edited (fine if you did it)" }
else { Bad "$($entry.path) differs from the release (damaged or replaced; extract the d4r ZIP again)"; $damaged++ }
}
}
if (!$damaged) { Ok 'every other d4r file matches the release' }
}
# --- OptiScaler.ini ---------------------------------------------------------------------------
$optiIni = Join-Path $game 'OptiScaler.ini'
if (Test-Path -LiteralPath $optiIni) {
$text = Read-TextFile $optiIni
if ($text -notmatch '(?im)^\s*AllowExternalBackend\s*=\s*true') { Bad 'OptiScaler.ini: [DLSS] AllowExternalBackend must be true (this is not the d4r OptiScaler.ini)' }
if ($text -notmatch '(?im)^\s*Dx12Upscaler\s*=\s*dlss') { Note 'OptiScaler.ini: [Upscalers] Dx12Upscaler is not dlss; the game will not use d4r' }
$m = [regex]::Match($text, '(?im)^\s*NvngxPath\s*=\s*(\S+)')
if (!$m.Success) { Bad 'OptiScaler.ini: [Libraries] NvngxPath is missing (it must name the d4r folder)' }
else {
$folder = $m.Groups[1].Value
$resolved = if ([IO.Path]::IsPathRooted($folder)) { $folder } else { Join-Path $game $folder }
if (Test-Path -LiteralPath (Join-Path $resolved '_nvngx.dll')) { Ok "OptiScaler.ini NvngxPath = $folder" }
else { Bad "OptiScaler.ini NvngxPath = $folder does not hold _nvngx.dll (use NvngxPath=d4r, or the full path of the d4r folder)" }
}
}
# --- NVIDIA DLLs (never shipped; the exact versions come from dll-pins.txt) -----------------------
$pins = @()
$pinsPath = Join-Path $d4r 'dll-pins.txt'
if (Test-Path -LiteralPath $pinsPath) {
foreach ($line in Get-Content -LiteralPath $pinsPath) {
$line = $line.Trim()
if (!$line -or $line.StartsWith('#')) { continue }
$f = $line -split '\s+'
if ($f.Count -ge 4 -and $f[1].Length -eq 64) { $pins += [pscustomobject]@{ kind = $f[0]; sha = $f[1].ToUpperInvariant(); version = $f[2]; status = $f[3] } }
}
}
function Check-Nvidia([string]$kind, [string]$relative, [string]$label, [string]$source) {
$path = Join-Path $game $relative
$wanted = @($pins | Where-Object { $_.kind -eq $kind } | ForEach-Object { $_.version }) -join ' or '
if (!$wanted) { Note "$label cannot be checked (d4r\dll-pins.txt is missing or damaged)"; return }
if (!(Test-Path -LiteralPath $path -PathType Leaf)) { Bad "${relative}: copy NVIDIA's $label here (version $wanted; $source)"; return }
$hash = Hash $path
$pin = @($pins | Where-Object { $_.kind -eq $kind -and $_.sha -eq $hash }) | Select-Object -First 1
if (!$pin) { Bad "$relative is version $(Version $path), which d4r does not accept. d4r on Windows needs exactly $wanted ($source)"; return }
if ($pin.status -eq 'validated') { Ok "$relative ($label $($pin.version), tested on Windows)" }
else { Ok "$relative ($label $($pin.version))"; Note "$label $($pin.version) is accepted but has had less Windows testing" }
}
Check-Nvidia 'dlss' 'd4r\nvngx_dlss.dll' 'nvngx_dlss.dll' 'many games ship one in their folder; see WINDOWS_README.txt'
Check-Nvidia 'ngx' 'd4r\ngx\_nvngx.dll' '_nvngx.dll' 'it is part of NVIDIA''s display driver package; see WINDOWS_README.txt'
# --- GPU and the build for it --------------------------------------------------------------------
$registryPath = Join-Path $d4r 'gpu-targets.json'
$registry = @()
if (Test-Path -LiteralPath $registryPath) { $registry = @((Get-Content -LiteralPath $registryPath -Raw | ConvertFrom-Json).targets) }
$inventory = $null
if ($InventoryFile) { $inventory = Get-Content -LiteralPath $InventoryFile -Raw | ConvertFrom-Json }
elseif (Test-Path -LiteralPath (Join-Path $d4r 'd4r_gpu_inventory.exe')) {
$work = Join-Path ([IO.Path]::GetTempPath()) ('d4r-check-' + [Guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $work | Out-Null
try {
$info = New-Object Diagnostics.ProcessStartInfo
$info.FileName = Join-Path $d4r 'd4r_gpu_inventory.exe'
$info.Arguments = '--hip-root "' + (Join-Path $d4r 'hip') + '" --output-dir "' + $work + '"'
$info.UseShellExecute = $false; $info.CreateNoWindow = $true
$info.RedirectStandardOutput = $true; $info.RedirectStandardError = $true
$info.EnvironmentVariables['D4R_DIAG_DIR'] = $work
$process = New-Object Diagnostics.Process; $process.StartInfo = $info
[void]$process.Start()
$null = $process.StandardOutput.ReadToEndAsync(); $null = $process.StandardError.ReadToEndAsync()
if (!$process.WaitForExit(30000)) { $process.Kill(); $process.WaitForExit() }
if (Test-Path -LiteralPath (Join-Path $work 'gpu-inventory.json')) { $inventory = Get-Content -LiteralPath (Join-Path $work 'gpu-inventory.json') -Raw | ConvertFrom-Json }
} catch {} finally {
$process.Dispose()
Get-ChildItem -LiteralPath $work -File -ErrorAction SilentlyContinue | ForEach-Object { Remove-Item -LiteralPath $_.FullName }
Remove-Item -LiteralPath $work -ErrorAction SilentlyContinue
}
}
$arch = $null
if (!$inventory) { Note 'could not ask the AMD runtime about your GPU (d4r_gpu_inventory.exe is missing, or the AMD graphics driver is not installed or is too old)' }
else {
$supported = @($registry | ForEach-Object { $_.architecture })
$devices = @($inventory.devices | Where-Object { $_.wavefrontSize -eq 32 -and ($supported -contains $_.architecture -or !$supported.Count) })
if (!$devices.Count) {
$seen = @($inventory.devices | ForEach-Object { "$($_.name) ($($_.architecture))" }) -join ', '
Bad ("no supported RDNA3/RDNA4 GPU found by the AMD runtime. It sees: " + $(if ($seen) { $seen } else { 'nothing' }) + '. Update the AMD Adrenalin driver; RDNA2 and older are not supported')
} else {
foreach ($device in $devices) {
$a = $device.architecture
$missing = @()
foreach ($rel in @("d4r\_nvngx_$a.dll", "d4r\pixel_convert_$a.hsaco", "d4r\nvapi\$a\nvapi64.dll", "d4r\native\$a\d4r-kernels.txt")) {
if (!(Test-Path -LiteralPath (Join-Path $game $rel))) { $missing += $rel }
}
$native = @(Get-ChildItem -LiteralPath (Join-Path $d4r "native\$a") -Filter '*.hsaco' -ErrorAction SilentlyContinue).Count
if ($missing.Count) { Bad "GPU $($device.name) ($a): this release has no complete build for it (missing: $($missing -join ', '))" }
else {
Ok "GPU $($device.name) ($a): build present, $native native kernels"
if (!$arch) { $arch = $a }
}
if ($a -ne 'gfx1201') { Note "only the RX 9070 XT (gfx1201) has been tested on real hardware; $a is compile-tested" }
}
}
if (@($inventory.devices | Where-Object { $_.wavefrontSize -eq 32 }).Count -gt 1) { Note 'several GPUs found; d4r uses the one the game renders on' }
}
if ($arch -and $registry.Count) {
$info = @($registry | Where-Object { $_.architecture -eq $arch }) | Select-Object -First 1
$bad = 0
$objects = @(Get-Item -LiteralPath (Join-Path $d4r "pixel_convert_$arch.hsaco") -ErrorAction SilentlyContinue) +
@(Get-ChildItem -LiteralPath (Join-Path $d4r "native\$arch") -Filter '*.hsaco' -ErrorAction SilentlyContinue)
foreach ($object in $objects) {
$stream = [IO.File]::OpenRead($object.FullName)
try { $header = New-Object byte[] 64; [void]$stream.Read($header, 0, 64) } finally { $stream.Dispose() }
if ($header[0] -ne 127 -or $header[1] -ne 69 -or $header[2] -ne 76 -or $header[3] -ne 70 -or $header[48] -ne $info.elfMachine) {
Bad "$($object.Name) is not built for $arch (do not rename or mix files from other GPUs; extract the d4r ZIP again)"; $bad++
}
}
if ($objects.Count -and !$bad) { Ok "all $($objects.Count) GPU code objects are built for $arch" }
}
# --- Linux / Wine leftovers ------------------------------------------------------------------------
$proxyFound = $false
foreach ($name in @('d3d12.dll', 'd3d12core.dll')) {
$proxy = Join-Path $game $name
if (Test-Path -LiteralPath $proxy -PathType Leaf) {
$item = Get-Item -LiteralPath $proxy
if ($item.Length -le 64MB -and [Text.Encoding]::ASCII.GetString([IO.File]::ReadAllBytes($proxy)) -match '(?i)vkd3d|wined3d') {
Bad "$name in the game folder is vkd3d-proton/Wine (left over from a Linux install?). Move it out; d4r on Windows uses native D3D12"
$proxyFound = $true
}
}
}
if (!$proxyFound) { Ok 'no vkd3d/Wine d3d12.dll in the game folder' }
# --- d4r.ini ---------------------------------------------------------------------------------------
$iniPath = Join-Path $d4r 'd4r.ini'
$model = 'K'
if (Test-Path -LiteralPath $iniPath) {
$section = ''; $iniProblems = 0
$known = @{ 'dlss.model' = 1; 'dlss.showwatermark' = 1; 'interop.asyncinterop' = 1; 'debug.validateoutput' = 1; 'debug.log' = 1; 'paths.cachedir' = 1 }
$boolean = @('dlss.showwatermark', 'interop.asyncinterop', 'debug.validateoutput')
foreach ($raw in (Read-TextFile $iniPath).TrimStart([char]0xFEFF) -split "`n") {
$line = $raw.Trim()
if (!$line -or $line.StartsWith(';') -or $line.StartsWith('#')) { continue }
if ($line.StartsWith('[')) { $section = $line.Trim('[', ']').Trim(); continue }
$eq = $line.IndexOf('='); if ($eq -lt 0) { continue }
$key = $line.Substring(0, $eq).Trim()
$value = ($line.Substring($eq + 1) -replace '\s[;#].*$', '').Trim()
$name = ("$section.$key").ToLowerInvariant()
$v = $value.ToLowerInvariant()
if (!$known.ContainsKey($name)) { Note "d4r.ini: [$section] $key is not a Windows setting; ignored"; $iniProblems++ }
elseif ($name -eq 'dlss.model') {
if (@('', 'auto', 'k', 'dlss4', '11') -contains $v) { $model = 'K' }
elseif (@('m', 'dlss4.5', '13') -contains $v) { $model = 'M' }
else { Note "d4r.ini: [DLSS] Model '$value' is not available on Windows (use K or M); K will be used"; $iniProblems++ }
}
elseif ($boolean -contains $name) {
if (@('', 'auto', '1', 'true', 'yes', 'on', '0', 'false', 'no', 'off') -notcontains $v) { Note "d4r.ini: [$section] $key must be true or false, not '$value'; the default will be used"; $iniProblems++ }
}
elseif ($name -eq 'debug.log' -and @('', 'auto', 'normal', 'verbose') -notcontains $v) { Note "d4r.ini: [Debug] Log must be normal or verbose, not '$value'; normal will be used"; $iniProblems++ }
}
if (!$iniProblems) { Ok "d4r.ini is valid (model $model)" }
}
# --- what the last game start said -----------------------------------------------------------------
$log = Join-Path $d4r 'd4r_nvngx.log'
if (Test-Path -LiteralPath $log) {
$messages = @(Get-Content -LiteralPath $log | Where-Object { $_ -match '^d4r: ' })
$failed = @($messages | Where-Object { $_ -match 'DLSS disabled' })
if ($failed.Count) { foreach ($m in $failed | Select-Object -First 3) { Bad ('last game start: ' + $m.Substring(5)) } }
elseif ($messages.Count) { Ok ('last game start: ' + $messages[0].Substring(5)) }
else { Note 'd4r\d4r_nvngx.log has no d4r summary line yet (the game may not have chosen DLSS)' }
foreach ($m in $messages | Where-Object { $_ -notmatch 'DLSS disabled|player mode' } | Select-Object -First 5) { Note ('last game start: ' + $m.Substring(5)) }
} else { Note 'no d4r\d4r_nvngx.log yet: start the game once and choose DLSS (or FSR/XeSS) in its graphics settings' }
if (Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.Path -and $_.Path.StartsWith($game + '\', [StringComparison]::OrdinalIgnoreCase) -and $_.ProcessName -notmatch '^(powershell|pwsh)$' }) {
Note 'the game is running; its log is rewritten at every start'
}
Say ''
if ($problems -eq 0) { Say 'Everything d4r needs is in place.' } else { Say "$problems thing(s) to fix above." }
# --- bug-report ZIP ------------------------------------------------------------------------------
if ($Report) {
$stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
$destination = if ($ReportDirectory) { $ReportDirectory } else { [Environment]::GetFolderPath('Desktop') }
$staging = Join-Path ([IO.Path]::GetTempPath()) ("d4r-report-$stamp")
New-Item -ItemType Directory -Force -Path $staging | Out-Null
try {
$lines | Set-Content -LiteralPath (Join-Path $staging 'check.txt') -Encoding UTF8
foreach ($file in @('d4r\d4r_nvngx.log', 'd4r\d4r.ini', 'OptiScaler.ini', 'OptiScaler.log')) {
$source = Join-Path $game $file
if (Test-Path -LiteralPath $source) { Copy-Item -LiteralPath $source -Destination (Join-Path $staging (Split-Path $file -Leaf)) }
}
Get-ChildItem -LiteralPath $game -Filter 'OptiScaler*.log' -File | ForEach-Object { Copy-Item -LiteralPath $_.FullName -Destination $staging -Force }
try { Get-CimInstance Win32_VideoController | Select-Object Name, DriverVersion, DriverDate | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $staging 'driver.json') -Encoding UTF8 } catch {}
if ($inventory) { $inventory | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $staging 'gpu-inventory.json') -Encoding UTF8 }
Get-ChildItem -LiteralPath $game -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.FullName.StartsWith($d4r + '\') -or $_.Name -in @('dxgi.dll', 'OptiScaler.ini') } |
ForEach-Object { '{0} {1,12} {2}' -f (Hash $_.FullName), $_.Length, $_.FullName.Substring($game.Length + 1) } |
Set-Content -LiteralPath (Join-Path $staging 'files.sha256.txt') -Encoding UTF8
$zip = Join-Path $destination "d4r-report-$stamp.zip"
Compress-Archive -Path (Join-Path $staging '*') -DestinationPath $zip -Force
Write-Host ''
Write-Host "Bug report written: $zip" -ForegroundColor Cyan
Write-Host 'Also tell us: GPU, game, K or M, and what the image looked like. The logs can contain local folder names; look before posting publicly.'
} finally {
Get-ChildItem -LiteralPath $staging -File -ErrorAction SilentlyContinue | ForEach-Object { Remove-Item -LiteralPath $_.FullName }
Remove-Item -LiteralPath $staging -ErrorAction SilentlyContinue
}
}
if ($problems) { exit 1 }
+32
View File
@@ -0,0 +1,32 @@
# Parse completed diagnostic records. Empty streams are valid, including games
# that refuse a debugger before initializing the upscaler.
function Get-D4RGameSummary {
param([AllowEmptyString()][string]$Stdout='', [AllowEmptyString()][string]$Stderr='',
[AllowEmptyString()][string]$Debugger='', [int]$Preset=11, [double]$RunSeconds=0,
[bool]$ValidateOutput=$false, $GameCrash=$null)
$gameExit=[regex]::Match($Debugger, 'EXIT code=0x([0-9a-f]+)')
$exitCode=if ($gameExit.Success) { '0x'+$gameExit.Groups[1].Value } else { 'diagnostic_timeout' }
$frames=[regex]::Matches($Stdout, '(?m)^D4R_FRAME cpu_frame_copies=([0-9]+) frame_age=([0-9]+) interop_ngx_ms=([0-9.]+)\r?$')
$checks=[regex]::Matches($Stdout, '(?m)^D4R_OUTPUT_VALIDATION elements=([0-9]+) nan=([0-9]+) inf=([0-9]+) diagnostics_cpu_bytes=8\r?$')
$launches=[regex]::Matches($Stderr, '\[d4r-launch\] kernel="([^"]+)" backend=(native|translated)')
$kernels=@($launches | ForEach-Object { $_.Groups[1].Value+' '+$_.Groups[2].Value } | Group-Object | ForEach-Object {
$parts=$_.Name.Split(' '); @{kernel=$parts[0]; backend=$parts[1]; launches=$_.Count}
})
$capabilities=@([regex]::Matches($Stdout, '(?m)^D4R_CAPABILITIES available=(-?[0-9]+) feature_init=(0x[0-9a-f]+) needs_driver=(-?[0-9]+)\r?$') |
ForEach-Object { $_.Groups[2].Value } | Select-Object -Unique)
$session=if ($frames.Count) { 'completed-dlss-frames' }
elseif ($capabilities -contains '0xbad00004') { 'ngx-feature-not-found' }
elseif ($capabilities.Count -and $capabilities -ne '0x00000001') { 'ngx-capability-failure' }
elseif ($exitCode -eq '0x0') { 'process-exited-without-dlss-frames' }
elseif ($exitCode -eq 'diagnostic_timeout') { 'diagnostic-timeout-without-dlss-frames' }
else { 'process-failed-before-dlss-frames' }
return [ordered]@{exitCode=$exitCode; session=$session; featureInitResults=$capabilities;
runSeconds=$RunSeconds; preset=$Preset; gameCrash=$GameCrash;
nativeLaunches=([regex]::Matches($Stderr, '\[d4r-launch\].*backend=native')).Count;
completedFrames=$frames.Count; outputValidationRequested=$ValidateOutput;
outputGpuChecks=$checks.Count; kernels=$kernels;
nonfiniteOutputs=@($checks | Where-Object { $_.Groups[2].Value -ne '0' -or $_.Groups[3].Value -ne '0' }).Count;
previousFrameOutputs=@($frames | Where-Object { $_.Groups[2].Value -ne '0' }).Count;
cpuImageCopyFrames=@($frames | Where-Object { $_.Groups[1].Value -ne '0' }).Count;
failures=([regex]::Matches($Stderr, 'D4R_[A-Z0-9_]*FAILURE\b')).Count}
}
+58
View File
@@ -0,0 +1,58 @@
# Read-only inventory before touching game files. It allocates no GPU memory
# and executes no kernels; successful discovery is not hardware validation.
function Invoke-D4RGpuPreflight([string]$PackageRoot, [string]$GameDirectory, [string]$Target, [string]$OutputDirectory) {
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$result=[ordered]@{passed=$false; target=$Target; gpuWorkExecuted=$false; failure=$null}
try {
$proxy=Join-Path $GameDirectory 'd3d12.dll'
if (Test-Path -LiteralPath $proxy) {
$item=Get-Item -LiteralPath $proxy
$result.localD3D12=@{path=$proxy; sha256=(Get-FileHash -LiteralPath $proxy).Hash; product=$item.VersionInfo.ProductName}
# Identify known Vulkan/Wine proxies by their own strings. Do not
# load, remove or replace a third-party game DLL.
if ($item.Length -le 64MB -and [Text.Encoding]::ASCII.GetString([IO.File]::ReadAllBytes($proxy)) -match '(?i)vkd3d|wined3d') {
throw 'Local d3d12.dll identifies as vkd3d/Wine. Restore the game native D3D12 installation before using this Windows backend.'
}
}
$info=[Diagnostics.ProcessStartInfo]::new()
$info.FileName=Join-Path $PackageRoot 'd4r/d4r_gpu_inventory.exe'
$hip=Join-Path $PackageRoot 'd4r/hip'
foreach ($value in @($hip,$OutputDirectory)) { if ($value.Contains('"')) { throw 'Unsupported quote in inventory path.' } }
$info.Arguments='--hip-root "' + $hip + '" --output-dir "' + $OutputDirectory + '"'
$info.UseShellExecute=$false; $info.CreateNoWindow=$true
$info.EnvironmentVariables['D4R_DIAG_DIR']=$OutputDirectory
$info.RedirectStandardOutput=$true; $info.RedirectStandardError=$true
$process=[Diagnostics.Process]::new(); $process.StartInfo=$info
try {
if (!$process.Start()) { throw 'Cannot start GPU inventory.' }
$stdout=$process.StandardOutput.ReadToEndAsync(); $stderr=$process.StandardError.ReadToEndAsync()
$timedOut=!$process.WaitForExit(30000)
if ($timedOut) { $process.Kill(); $process.WaitForExit() }
$stdout.GetAwaiter().GetResult() | Set-Content (Join-Path $OutputDirectory 'gpu-inventory.stdout.log') -Encoding UTF8
$stderr.GetAwaiter().GetResult() | Set-Content (Join-Path $OutputDirectory 'gpu-inventory.stderr.log') -Encoding UTF8
$result.exitCode=$process.ExitCode
$result.exceptionOrExitHex='0x{0:X8}' -f [BitConverter]::ToUInt32([BitConverter]::GetBytes([int]$process.ExitCode),0)
if ($timedOut) { $result.timedOut=$true; throw 'HIP inventory timed out after 30 seconds.' }
if ($process.ExitCode) { throw "HIP inventory failed (exit $($process.ExitCode))." }
} finally { $process.Dispose() }
$inventory=Get-Content (Join-Path $OutputDirectory 'gpu-inventory.json') -Raw | ConvertFrom-Json
$result.inventory=$inventory
if ($inventory.compiledTarget -ne $Target) { throw "Inventory executable targets $($inventory.compiledTarget); package requires $Target." }
$matching=@($inventory.devices | Where-Object { $_.architecture -eq $Target -and $_.wavefrontSize -eq 32 })
if (!$matching.Count) {
$actual=@($inventory.devices | ForEach-Object { "$($_.architecture) ($($_.name))" }) -join ', '
throw "GPU target mismatch: detected $actual; package requires $Target. Use the package for the actual HIP architecture; do not rename code objects."
}
$result.passed=$true
Write-Host "D4R_PREFLIGHT target=$Target devices=$($matching.Count) gpu_work_executed=0"
} catch {
$result.failure=$_.Exception.Message
$result | ConvertTo-Json -Depth 8 | Set-Content (Join-Path $OutputDirectory 'preflight.json') -Encoding UTF8
try { Get-CimInstance Win32_VideoController | Select-Object Name,DriverVersion,DriverDate | ConvertTo-Json |
Set-Content (Join-Path $OutputDirectory 'driver.json') -Encoding UTF8 } catch {}
$bundle=$OutputDirectory+'.zip'
Compress-Archive -Path (Join-Path $OutputDirectory '*') -DestinationPath $bundle -Force
throw "$($result.failure) Diagnostic bundle: $bundle"
}
$result | ConvertTo-Json -Depth 8 | Set-Content (Join-Path $OutputDirectory 'preflight.json') -Encoding UTF8
}
+38
View File
@@ -0,0 +1,38 @@
# Shared by the build, diagnostics, staging and game package scripts.
function Get-D4RGpuTargets {
$registry=Join-Path $PSScriptRoot 'gpu-targets.json'
if (!(Test-Path -LiteralPath $registry)) { $registry=Join-Path $PSScriptRoot '../../tools/windows/gpu-targets.json' }
$data=Get-Content -LiteralPath $registry -Raw | ConvertFrom-Json
if ($data.schema -ne 1) { throw 'Unsupported GPU target registry schema.' }
return $data.targets
}
function Get-D4RGpuTargetInfo([string]$Target) {
$info=@(Get-D4RGpuTargets | Where-Object { $_.architecture -eq $Target })
if ($info.Count -ne 1) { throw "Unsupported GPU target: $Target" }
return $info[0]
}
function Get-D4RGpuTarget([string]$Root, [string]$Requested) {
$configured=$null
$targetFile=Join-Path $Root 'gpu-target.json'
if (Test-Path -LiteralPath $targetFile) {
$configured=(Get-Content -LiteralPath $targetFile -Raw | ConvertFrom-Json).architecture
[void](Get-D4RGpuTargetInfo $configured)
}
$target=if ($Requested) { $Requested } elseif ($configured) { $configured } else { 'gfx1201' }
[void](Get-D4RGpuTargetInfo $target)
if (!$configured -and $target -ne 'gfx1201') { throw "A $target build requires gpu-target.json; rebuild with -GpuArch $target." }
if ($configured -and $target -ne $configured) { throw "Requested $target differs from package target $configured" }
return $target
}
function Assert-D4RCodeObjectTarget([string]$Path, [string]$Target) {
$info=Get-D4RGpuTargetInfo $Target
$reader=[IO.BinaryReader]::new([IO.File]::OpenRead($Path))
try { $header=$reader.ReadBytes(64) } finally { $reader.Dispose() }
if ($header.Length -ne 64 -or $header[0] -ne 127 -or $header[1] -ne 69 -or $header[2] -ne 76 -or $header[3] -ne 70 -or
$header[4] -ne 2 -or $header[5] -ne 1 -or $header[18] -ne 224 -or $header[19] -ne 0) {
throw "Invalid AMDGPU ELF64 code object: $Path"
}
# LLVM AMDGPU e_flags machine IDs; inspect, never patch or spoof them.
$expected=$info.elfMachine
if ($header[48] -ne $expected) { throw "Code object does not target ${Target}: $Path (ELF machine=$($header[48]))" }
}
+37 -5
View File
@@ -2,18 +2,32 @@
param( param(
[string]$PackageRoot, [string]$DiagnosticRoot, [string]$OptiScalerRoot, [string]$PackageRoot, [string]$DiagnosticRoot, [string]$OptiScalerRoot,
[string]$ZludaRoot, [string]$HipRoot, [string]$KernelKRoot, [string]$KernelMRoot, [string]$ZludaRoot, [string]$HipRoot, [string]$KernelKRoot, [string]$KernelMRoot,
[string]$GpuArch,
[string]$ArchivePath [string]$ArchivePath
) )
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
$defaults = @{PackageRoot='dist/windows-rdna4-game'; DiagnosticRoot='dist/windows-rdna4-command-list'; $defaults = @{PackageRoot='dist/windows-rdna4-game'; DiagnosticRoot='dist/windows-rdna4-command-list';
OptiScalerRoot='dist/optiscaler-windows-d4r'; ZludaRoot='dist/zluda-windows-final'; OptiScalerRoot='dist/optiscaler-windows-d4r'; ZludaRoot='dist/zluda-windows-final';
HipRoot='.tools/therock-10.2.0a20260929/_rocm_sdk_core'; HipRoot='.tools/therock-10.2.0a20260929/_rocm_sdk_core'}
KernelKRoot='build/native-k-gfx1201'; KernelMRoot='build/native-m-gfx1201'}
foreach ($name in $defaults.Keys) { foreach ($name in $defaults.Keys) {
if (!(Get-Variable -Name $name -ValueOnly)) { Set-Variable -Name $name -Value (Join-Path $repo $defaults[$name]) } if (!(Get-Variable -Name $name -ValueOnly)) { Set-Variable -Name $name -Value (Join-Path $repo $defaults[$name]) }
} }
$GpuArch=Get-D4RGpuTarget $DiagnosticRoot $GpuArch
if ($GpuArch -ne 'gfx1201' -and !$PSBoundParameters.ContainsKey('PackageRoot')) { $PackageRoot += "-$GpuArch" }
if (!$KernelKRoot) { $KernelKRoot=Join-Path $repo "build/native-k-$GpuArch" }
if (!$KernelMRoot) { $KernelMRoot=Join-Path $repo "build/native-m-$GpuArch" }
foreach ($directory in @($KernelKRoot,$KernelMRoot)) {
$info=Join-Path $directory 'build-info.json'
if ((Test-Path -LiteralPath $info) -and (Get-Content -LiteralPath $info -Raw | ConvertFrom-Json).architecture -ne $GpuArch) {
throw "Native kernel directory does not target ${GpuArch}: $directory"
}
}
$PackageRoot = [IO.Path]::GetFullPath($PackageRoot) $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() $sourceCommit=(& git -C $repo rev-parse HEAD).Trim()
$sourceDirty=[bool](& git -C $repo status --porcelain) $sourceDirty=[bool](& git -C $repo status --porcelain)
if ($ArchivePath -and $sourceDirty) { throw 'Commit source changes before creating the distributable archive.' } 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() $stagedFiles = [Collections.Generic.List[string]]::new()
function Stage([string]$source, [string]$relative) { function Stage([string]$source, [string]$relative) {
if (!(Test-Path -LiteralPath $source -PathType Leaf)) { throw "Required file missing: $source" } 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 $destination = Join-Path $PackageRoot $relative
New-Item -ItemType Directory -Force (Split-Path $destination) | Out-Null New-Item -ItemType Directory -Force (Split-Path $destination) | Out-Null
Copy-Item -LiteralPath $source -Destination $destination -Force Copy-Item -LiteralPath $source -Destination $destination -Force
@@ -28,9 +43,10 @@ function Stage([string]$source, [string]$relative) {
} }
Stage (Join-Path $OptiScalerRoot 'OptiScaler.dll') 'OptiScaler.dll' Stage (Join-Path $OptiScalerRoot 'OptiScaler.dll') 'OptiScaler.dll'
Stage (Join-Path $DiagnosticRoot 'bin/d4r_nvngx.dll') 'd4r/_nvngx.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 'nvapi-compat/nvapi64.dll') 'd4r/nvapi/nvapi64.dll'
Stage (Join-Path $DiagnosticRoot 'bin/d4r_debug_launcher.exe') 'd4r/d4r_debug_launcher.exe' Stage (Join-Path $DiagnosticRoot 'bin/d4r_debug_launcher.exe') 'd4r/d4r_debug_launcher.exe'
Stage (Join-Path $DiagnosticRoot 'bin/d4r_gpu_inventory.exe') 'd4r/d4r_gpu_inventory.exe'
foreach ($file in @('nvcuda.dll','nvapi64.dll','libc++.dll','libunwind.dll')) { Stage (Join-Path $ZludaRoot $file) "d4r/zluda/$file" } foreach ($file in @('nvcuda.dll','nvapi64.dll','libc++.dll','libunwind.dll')) { Stage (Join-Path $ZludaRoot $file) "d4r/zluda/$file" }
foreach ($file in @('amdhip64_7.dll','amd_comgr.dll','hiprtc0717.dll','hiprtc-builtins0717.dll','rocm_kpack.dll','AMD.ROCM.Comgr.MANIFEST','.hipVersion')) { foreach ($file in @('amdhip64_7.dll','amd_comgr.dll','hiprtc0717.dll','hiprtc-builtins0717.dll','rocm_kpack.dll','AMD.ROCM.Comgr.MANIFEST','.hipVersion')) {
Stage (Join-Path $HipRoot "bin/$file") "d4r/hip/bin/$file" Stage (Join-Path $HipRoot "bin/$file") "d4r/hip/bin/$file"
@@ -46,14 +62,29 @@ foreach ($directory in @($KernelKRoot, $KernelMRoot)) {
} }
} }
if ($entries.Count -ne 16) { throw "Expected 11 K and 5 M native objects; found $($entries.Count)" } 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 Set-Content -LiteralPath (Join-Path $PackageRoot 'd4r/native/d4r-kernels.txt') -Encoding ASCII
$stagedFiles.Add('d4r/native/d4r-kernels.txt') $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/windows-game.ps1') 'windows-game.ps1'
Stage (Join-Path $repo 'scripts/windows/game-summary.ps1') 'game-summary.ps1'
Stage (Join-Path $repo 'scripts/windows/gpu-target.ps1') 'gpu-target.ps1'
Stage (Join-Path $repo 'scripts/windows/gpu-preflight.ps1') 'gpu-preflight.ps1'
Stage (Join-Path $repo 'tools/windows/gpu-targets.json') 'gpu-targets.json'
if (Test-Path -LiteralPath (Join-Path $DiagnosticRoot 'gpu-target.json')) {
Stage (Join-Path $DiagnosticRoot 'gpu-target.json') 'gpu-target.json'
} else {
# Builds predating target metadata were gfx1201 only. The resolver rejects
# any attempt to label such a build gfx1200.
@{schema=1; architecture=$GpuArch; wavefrontSize=32; validation='Legacy gfx1201 build; run the GPU diagnostics.'} |
ConvertTo-Json | Set-Content -LiteralPath (Join-Path $PackageRoot 'gpu-target.json') -Encoding UTF8
$stagedFiles.Add('gpu-target.json')
}
Stage (Join-Path $repo 'scripts/windows/capture-game-window.ps1') 'capture-game-window.ps1' Stage (Join-Path $repo 'scripts/windows/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 '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-game.md') 'README.md'
Stage (Join-Path $repo 'docs/windows-performance.md') 'windows-performance.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 $repo 'LICENSE') 'licenses/d4r.txt'
Stage (Join-Path $OptiScalerRoot 'GPL-3.0.txt') 'licenses/OptiScaler.txt' Stage (Join-Path $OptiScalerRoot 'GPL-3.0.txt') 'licenses/OptiScaler.txt'
Stage (Join-Path $repo 'external/ZLUDA/LICENSE-MIT') 'licenses/ZLUDA-MIT.txt' Stage (Join-Path $repo 'external/ZLUDA/LICENSE-MIT') 'licenses/ZLUDA-MIT.txt'
@@ -64,13 +95,14 @@ Stage (Join-Path $HipRoot 'share/doc/amd_comgr/LICENSE.txt') 'licenses/amd_comgr
Stage (Join-Path $repo 'external/clr/LICENSE.md') 'licenses/HIP-CLR.txt' Stage (Join-Path $repo 'external/clr/LICENSE.md') 'licenses/HIP-CLR.txt'
foreach ($patch in Get-ChildItem -LiteralPath (Join-Path $repo 'patches/optiscaler') -Filter '*.patch') { Stage $patch.FullName ('source-patches/optiscaler/' + $patch.Name) } foreach ($patch in Get-ChildItem -LiteralPath (Join-Path $repo 'patches/optiscaler') -Filter '*.patch') { Stage $patch.FullName ('source-patches/optiscaler/' + $patch.Name) }
foreach ($patch in Get-ChildItem -LiteralPath (Join-Path $repo 'patches/zluda') -Filter '*.patch') { Stage $patch.FullName ('source-patches/zluda/' + $patch.Name) } foreach ($patch in Get-ChildItem -LiteralPath (Join-Path $repo 'patches/zluda') -Filter '*.patch') { Stage $patch.FullName ('source-patches/zluda/' + $patch.Name) }
foreach ($patch in Get-ChildItem -LiteralPath (Join-Path $repo 'patches/zluda-llvm') -Filter '*.patch') { Stage $patch.FullName ('source-patches/zluda-llvm/' + $patch.Name) }
$sourceArchive=Join-Path $repo 'build/d4r-windows-rdna4-source.zip' $sourceArchive=Join-Path $repo 'build/d4r-windows-rdna4-source.zip'
New-Item -ItemType Directory -Force (Split-Path $sourceArchive) | Out-Null New-Item -ItemType Directory -Force (Split-Path $sourceArchive) | Out-Null
& git -C $repo archive --format=zip --output $sourceArchive $sourceCommit & git -C $repo archive --format=zip --output $sourceArchive $sourceCommit
if ($LASTEXITCODE) { throw 'Cannot archive the committed d4r source.' } if ($LASTEXITCODE) { throw 'Cannot archive the committed d4r source.' }
Stage $sourceArchive 'source/d4r-windows-rdna4-source.zip' Stage $sourceArchive 'source/d4r-windows-rdna4-source.zip'
$metadata = [ordered]@{ $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; d4rCommit=$sourceCommit;
d4rWorkingTreeDirty=$sourceDirty; d4rWorkingTreeDirty=$sourceDirty;
optiScaler=(Get-Content -LiteralPath (Join-Path $OptiScalerRoot 'build-info.json') -Raw | ConvertFrom-Json); optiScaler=(Get-Content -LiteralPath (Join-Path $OptiScalerRoot 'build-info.json') -Raw | ConvertFrom-Json);
@@ -0,0 +1,109 @@
[CmdletBinding()]
param(
[string]$GamePackagePrefix, [Parameter(Mandatory=$true)][string]$PackageRoot,
[string]$ArchivePath,
[string]$NvidiaDirectory, [string]$NvidiaLicensePath
)
$ErrorActionPreference='Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$GamePackagePrefix) { $GamePackagePrefix=Join-Path $repo 'dist/windows-gpu-coverage-game' }
$PackageRoot=[IO.Path]::GetFullPath($PackageRoot)
if (Test-Path -LiteralPath $PackageRoot) { throw 'Use a new output directory to avoid mixing releases.' }
$commit=(& git -C $repo rev-parse HEAD).Trim()
if ($ArchivePath -and [bool](& git -C $repo status --porcelain)) { throw 'Commit source changes before creating a public ZIP.' }
New-Item -ItemType Directory -Force $PackageRoot | Out-Null
$files=[Collections.Generic.List[object]]::new()
function Stage([string]$Source,[string]$Relative) {
$destination=Join-Path $PackageRoot $Relative
New-Item -ItemType Directory -Force (Split-Path $destination) | Out-Null
Copy-Item -LiteralPath $Source -Destination $destination
$item=Get-Item -LiteralPath $destination
$entry=@{path=$Relative.Replace('\','/'); sha256=(Get-FileHash -LiteralPath $destination -Algorithm SHA256).Hash; version=$item.VersionInfo.FileVersion}
$files.Add($entry)
return $entry
}
$targets=[ordered]@{}; $common=@(); $baseline=$null
foreach ($gpu in Get-D4RGpuTargets) {
$arch=$gpu.architecture; $source="$GamePackagePrefix-$arch"
$metadata=Get-Content -LiteralPath (Join-Path $source 'package.json') -Raw | ConvertFrom-Json
if ($metadata.architecture -ne $arch -or $metadata.d4rWorkingTreeDirty) { throw "Uncommitted or wrong-target input: $source" }
$targetFiles=@()
foreach ($file in $metadata.files) {
$relative=$file.path.Replace('\','/')
if ((Get-FileHash -LiteralPath (Join-Path $source $file.path) -Algorithm SHA256).Hash -ne $file.sha256) { throw "Input file changed: $source/$relative" }
if ($relative.EndsWith('.hsaco')) { Assert-D4RCodeObjectTarget (Join-Path $source $file.path) $arch }
if ($relative -eq 'gpu-target.json' -or $relative -eq 'd4r/_nvngx.dll' -or $relative -eq 'd4r/d4r_gpu_inventory.exe' -or $relative -eq 'd4r/d4r_debug_launcher.exe' -or $relative -eq 'd4r/nvapi/nvapi64.dll' -or
$relative.StartsWith('d4r/native/') -or $relative -like 'd4r/pixel_convert_*.hsaco') {
$entry=Stage (Join-Path $source $file.path) "files/targets/$arch/$relative"
$targetFiles+=@{path=$relative; sha256=$entry.sha256; version=$entry.version}
} elseif ($relative -eq 'OptiScaler.dll' -or $relative.StartsWith('d4r/') -or $relative.StartsWith('licenses/')) {
$publicPath=if ($relative.StartsWith('licenses/')) { $relative } else { "files/common/$relative" }
if (!$baseline) {
$entry=Stage (Join-Path $source $file.path) $publicPath
if (!$relative.StartsWith('licenses/')) { $common+=@{path=$relative; sha256=$entry.sha256; version=$entry.version} }
} elseif ((Get-FileHash -LiteralPath (Join-Path $PackageRoot $publicPath) -Algorithm SHA256).Hash -ne $file.sha256) {
throw "Shared runtime differs in $arch : $relative"
}
}
}
if (@($targetFiles | Where-Object { $_.path.EndsWith('.hsaco') }).Count -ne 17) { throw "Expected 17 code objects for $arch" }
$targets[$arch]=@{binarySourceCommit=$metadata.d4rCommit; hardwareValidation=$metadata.hardwareValidation; dlssSha256=$metadata.dlssSha256; files=$targetFiles}
if (!$baseline) { $baseline=$metadata }
if ($metadata.dlssSha256 -ne $baseline.dlssSha256 -or $metadata.zludaBuild.sourceCommit -ne $baseline.zludaBuild.sourceCommit) { throw 'Input packages do not use the same validated dependencies.' }
}
foreach ($name in @('windows-game.ps1','game-summary.ps1','gpu-target.ps1','gpu-preflight.ps1')) {
$entry=Stage (Join-Path $PSScriptRoot $name) "files/common/$name"
$common+=@{path=$name; sha256=$entry.sha256; version=$entry.version}
}
$entry=Stage (Join-Path $repo 'tools/windows/gpu-targets.json') 'files/common/gpu-targets.json'
$common+=@{path='gpu-targets.json'; sha256=$entry.sha256; version=$entry.version}
[void](Stage (Join-Path $PSScriptRoot 'quick-test.ps1') 'files/quick-test.ps1')
[void](Stage (Join-Path $repo 'docs/windows-quick-test.txt') 'READ-ME-FIRST.txt')
foreach ($launch in @(@{name='START-K.cmd'; arguments='-Preset 11'},@{name='START-M.cmd'; arguments='-Preset 13'},@{name='RESTORE-GAME.cmd'; arguments='-Action restore'})) {
$path=Join-Path $PackageRoot $launch.name
@('@echo off','cd /d "%~dp0"',('"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -STA -ExecutionPolicy Bypass -File "%~dp0files\quick-test.ps1" '+$launch.arguments),'pause') |
Set-Content -LiteralPath $path -Encoding ASCII
$files.Add(@{path=$launch.name; sha256=(Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash; version=$null})
}
$ngxCoreSha256='66767018C36B3BAB46398DADE3ADF173DAA3730FDA75965689EA848C9BC4E79B'
$bundledNvidia=$null
if ($NvidiaDirectory) {
# Opt-in for local testing or a distributor with appropriate permission.
# A matching hash establishes binary identity, not redistribution rights.
if (!$NvidiaLicensePath -or !(Test-Path -LiteralPath $NvidiaLicensePath -PathType Leaf)) {
throw 'Bundled NVIDIA files require -NvidiaLicensePath with their applicable license. This option does not establish redistribution permission.'
}
$core=Join-Path $NvidiaDirectory '_nvngx.dll'
$dlss=Join-Path $NvidiaDirectory 'nvngx_dlss.dll'
if ((Get-FileHash -LiteralPath $core -Algorithm SHA256).Hash -ne $ngxCoreSha256 -or
(Get-FileHash -LiteralPath $dlss -Algorithm SHA256).Hash -ne $baseline.dlssSha256) {
throw 'Bundled NVIDIA files must match the validated original NGX and DLSS hashes.'
}
[void](Stage $core 'files/nvidia/_nvngx.dll')
[void](Stage $dlss 'files/nvidia/nvngx_dlss.dll')
[void](Stage $NvidiaLicensePath 'licenses/NVIDIA-RTX.txt')
$bundledNvidia=@{ngxCore='nvidia/_nvngx.dll'; dlssDll='nvidia/nvngx_dlss.dll'; license='licenses/NVIDIA-RTX.txt'}
} elseif ($NvidiaLicensePath) { throw '-NvidiaLicensePath requires -NvidiaDirectory.' }
$metadata=[ordered]@{schema=1; packageCommit=$commit; binarySourceCommit=$baseline.d4rCommit;
ngxCoreSha256=$ngxCoreSha256; bundledNvidia=$bundledNvidia;
dependencies=@{optiScalerSourceCommit=$baseline.optiScaler.sourceCommit; zludaSourceCommit=$baseline.zludaBuild.sourceCommit;
llvmSource=$baseline.zludaBuild.llvmSource; hipRuntime=$baseline.hipRuntime};
commonFiles=$common; targets=$targets; files=$files}
$metadata | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath (Join-Path $PackageRoot 'files/package.json') -Encoding UTF8
if ($ArchivePath) {
$ArchivePath=[IO.Path]::GetFullPath($ArchivePath)
if ($ArchivePath.StartsWith($PackageRoot+'\',[StringComparison]::OrdinalIgnoreCase)) { throw 'Place the ZIP outside the output directory.' }
New-Item -ItemType Directory -Force (Split-Path $ArchivePath) | Out-Null
Add-Type -AssemblyName System.IO.Compression,System.IO.Compression.FileSystem
$stream=[IO.File]::Open($ArchivePath,[IO.FileMode]::Create,[IO.FileAccess]::ReadWrite,[IO.FileShare]::None)
$archive=$null
try {
$archive=[IO.Compression.ZipArchive]::new($stream,[IO.Compression.ZipArchiveMode]::Create)
foreach ($relative in @($files.path)+@('files/package.json')) {
[void][IO.Compression.ZipFileExtensions]::CreateEntryFromFile($archive,(Join-Path $PackageRoot $relative),$relative,[IO.Compression.CompressionLevel]::Optimal)
}
} finally { if ($archive) { $archive.Dispose() }; $stream.Dispose() }
}
$nvidiaMode=if ($bundledNvidia) { 'bundled NVIDIA files; distributor must have appropriate permission' } else { 'user-supplied NVIDIA files' }
Write-Host "Quick-test package: $PackageRoot (11 targets, automatic GPU selection, $nvidiaMode)"
+105
View File
@@ -0,0 +1,105 @@
# Builds the Windows drag-in release: extract it into the game folder, copy
# the two NVIDIA DLLs, edit d4r\d4r.ini. Like the Linux release, no installer
# or launcher is involved. Inputs are the per-target game packages that
# package-windows-game.ps1 produces (the same inputs as the quick test).
[CmdletBinding()]
param(
[string]$GamePackagePrefix,
[Parameter(Mandatory=$true)][string]$LoaderDll,
[Parameter(Mandatory=$true)][string]$PackageRoot,
[string[]]$Architectures, # default: every target in gpu-targets.json; a subset makes a smaller release
[string]$ArchivePath
)
$ErrorActionPreference='Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$GamePackagePrefix) { $GamePackagePrefix=Join-Path $repo 'dist/windows-gpu-coverage-game' }
$PackageRoot=[IO.Path]::GetFullPath($PackageRoot)
if (Test-Path -LiteralPath $PackageRoot) { throw 'Use a new output directory to avoid mixing releases.' }
$git=Get-Command git -ErrorAction SilentlyContinue
$commit=if ($git) { (& git -C $repo rev-parse HEAD).Trim() } else { $null }
if ($ArchivePath -and (!$git -or [bool](& git -C $repo status --porcelain))) { throw 'A public ZIP needs git and committed sources.' }
New-Item -ItemType Directory -Force $PackageRoot | Out-Null
$files=[Collections.Generic.List[object]]::new()
function Stage([string]$Source,[string]$Relative) {
$destination=Join-Path $PackageRoot $Relative
if (Test-Path -LiteralPath $destination) { throw "Duplicate release path: $Relative" }
New-Item -ItemType Directory -Force (Split-Path $destination) | Out-Null
Copy-Item -LiteralPath $Source -Destination $destination
$item=Get-Item -LiteralPath $destination
$files.Add(@{path=$Relative.Replace('\','/'); sha256=(Get-FileHash -LiteralPath $destination -Algorithm SHA256).Hash; version=$item.VersionInfo.FileVersion})
}
$targets=[ordered]@{}; $baseline=$null; $commonStaged=$false; $inventoryStaged=$false
$selected=@(Get-D4RGpuTargets | Where-Object { !$Architectures -or $Architectures -contains $_.architecture })
if (!$selected.Count -or ($Architectures -and $selected.Count -ne @($Architectures | Select-Object -Unique).Count)) { throw "Unknown GPU target in -Architectures: $($Architectures -join ', ')" }
foreach ($gpu in $selected) {
$arch=$gpu.architecture; $source="$GamePackagePrefix-$arch"
$metadata=Get-Content -LiteralPath (Join-Path $source 'package.json') -Raw | ConvertFrom-Json
if ($metadata.architecture -ne $arch -or $metadata.d4rWorkingTreeDirty) { throw "Uncommitted or wrong-target input: $source" }
if (!$baseline) { $baseline=$metadata }
if ($metadata.dlssSha256 -ne $baseline.dlssSha256 -or $metadata.zludaBuild.sourceCommit -ne $baseline.zludaBuild.sourceCommit) { throw 'Input packages do not use the same validated dependencies.' }
$objects=0
foreach ($file in $metadata.files) {
$relative=$file.path.Replace('\','/'); $path=Join-Path $source $file.path
if ((Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash -ne $file.sha256) { throw "Input file changed: $source/$relative" }
if ($relative.EndsWith('.hsaco')) { Assert-D4RCodeObjectTarget $path $arch; $objects++ }
# Per-target files get target-specific names so one folder serves every GPU.
# One GPU-query tool serves every target: it reports each device's real architecture.
if ($relative -eq 'd4r/d4r_gpu_inventory.exe') { if (!$inventoryStaged) { Stage $path 'd4r/d4r_gpu_inventory.exe'; $inventoryStaged=$true } }
elseif ($relative -eq 'd4r/_nvngx.dll') { Stage $path "d4r/_nvngx_$arch.dll" }
elseif ($relative -eq 'd4r/nvapi/nvapi64.dll') { Stage $path "d4r/nvapi/$arch/nvapi64.dll" }
elseif ($relative -like 'd4r/pixel_convert_*.hsaco') { Stage $path $relative }
elseif ($relative.StartsWith('d4r/native/')) { Stage $path ("d4r/native/$arch/" + $relative.Substring('d4r/native/'.Length)) }
elseif ($relative -eq 'OptiScaler.dll' -or $relative.StartsWith('licenses/') -or
($relative.StartsWith('d4r/hip/') -or $relative.StartsWith('d4r/zluda/'))) {
$public=if ($relative -eq 'OptiScaler.dll') { 'dxgi.dll' } elseif ($relative.StartsWith('licenses/')) { "d4r/$relative" } else { $relative }
if (!$commonStaged) { Stage $path $public }
elseif ((Get-FileHash -LiteralPath (Join-Path $PackageRoot $public) -Algorithm SHA256).Hash -ne $file.sha256) { throw "Shared runtime differs in $arch : $relative" }
}
# Installer-only files (launcher, PowerShell installer, docs) are not part of a drag-in install.
}
if ($objects -ne 17) { throw "Expected 17 code objects for $arch" }
$commonStaged=$true
$targets[$arch]=@{binarySourceCommit=$metadata.d4rCommit; hardwareValidation=$metadata.hardwareValidation}
}
$loader=(Get-Item -LiteralPath $LoaderDll).FullName
if ([IO.Path]::GetFileName($loader) -ne 'd4r_nvngx_loader.dll') { throw 'Pass the d4r_nvngx_loader.dll built by CMake.' }
Stage $loader 'd4r/_nvngx.dll'
Stage (Join-Path $repo 'packaging/windows/OptiScaler.ini') 'OptiScaler.ini'
Stage (Join-Path $repo 'packaging/windows/d4r.ini') 'd4r/d4r.ini'
Stage (Join-Path $repo 'tools/windows/dll-pins.txt') 'd4r/dll-pins.txt'
Stage (Join-Path $repo 'tools/windows/gpu-targets.json') 'd4r/gpu-targets.json'
if (!$inventoryStaged) { throw 'No d4r/d4r_gpu_inventory.exe in the inputs; d4r-check.ps1 needs it to query the GPU.' }
foreach ($optional in @(@{source='packaging/windows/WINDOWS_README.txt'; path='WINDOWS_README.txt'},
@{source='scripts/windows/d4r-check.ps1'; path='d4r/d4r-check.ps1'})) {
$path=Join-Path $repo $optional.source
if (Test-Path -LiteralPath $path) { Stage $path $optional.path }
}
# Shows players where the NGX core goes; a ZIP cannot carry an empty folder.
$placeholder=Join-Path $PackageRoot 'd4r/ngx/PUT _nvngx.dll HERE.txt'
New-Item -ItemType Directory -Force (Split-Path $placeholder) | Out-Null
"Copy NVIDIA's _nvngx.dll into this folder. See WINDOWS_README.txt." | Set-Content -LiteralPath $placeholder -Encoding ASCII
$files.Add(@{path='d4r/ngx/PUT _nvngx.dll HERE.txt'; sha256=(Get-FileHash -LiteralPath $placeholder -Algorithm SHA256).Hash; version=$null})
if (Get-ChildItem -LiteralPath $PackageRoot -Recurse -File | Where-Object { $_.Name -eq 'nvngx_dlss.dll' -or ($_.Name -eq '_nvngx.dll' -and $_.DirectoryName -ne (Join-Path $PackageRoot 'd4r')) }) {
throw 'Proprietary NVIDIA binaries must not be in the release.'
}
# Read by d4r-check.ps1: every shipped file's hash, so a damaged or mixed install is reported.
$manifest=[ordered]@{schema=1; kind='windows-drag-in'; packageCommit=$commit; binarySourceCommit=$baseline.d4rCommit;
dependencies=@{optiScalerSourceCommit=$baseline.optiScaler.sourceCommit; zludaSourceCommit=$baseline.zludaBuild.sourceCommit; hipRuntime=$baseline.hipRuntime};
targets=$targets; files=$files}
$manifest | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Join-Path $PackageRoot 'd4r/package.json') -Encoding UTF8
if ($ArchivePath) {
$ArchivePath=[IO.Path]::GetFullPath($ArchivePath)
if ($ArchivePath.StartsWith($PackageRoot+'\',[StringComparison]::OrdinalIgnoreCase)) { throw 'Place the ZIP outside the output directory.' }
New-Item -ItemType Directory -Force (Split-Path $ArchivePath) | Out-Null
Add-Type -AssemblyName System.IO.Compression,System.IO.Compression.FileSystem
$stream=[IO.File]::Open($ArchivePath,[IO.FileMode]::Create,[IO.FileAccess]::ReadWrite,[IO.FileShare]::None)
$archive=$null
try {
$archive=[IO.Compression.ZipArchive]::new($stream,[IO.Compression.ZipArchiveMode]::Create)
foreach ($relative in @($files.path)+@('d4r/package.json')) {
[void][IO.Compression.ZipFileExtensions]::CreateEntryFromFile($archive,(Join-Path $PackageRoot $relative),$relative,[IO.Compression.CompressionLevel]::Optimal)
}
} finally { if ($archive) { $archive.Dispose() }; $stream.Dispose() }
}
Write-Host "Windows drag-in release: $PackageRoot ($($targets.Count) GPU target(s): $($targets.Keys -join ', '); no NVIDIA DLLs)"
@@ -0,0 +1,34 @@
[CmdletBinding()]
param([Parameter(Mandatory=$true)][string]$LlvmSourceRoot)
$ErrorActionPreference='Stop'
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
$expected='ff4dc1f7c9e1c64d4d69e40f4ed30c2280a96dfd'
$actual=(& git -C $LlvmSourceRoot rev-parse HEAD).Trim()
if ($LASTEXITCODE -or $actual -ne $expected) { throw "Expected audited ZLUDA LLVM source $expected, found $actual; review the patch against this revision." }
$patches=@(Get-ChildItem (Join-Path $repo 'patches/zluda-llvm') -Filter *.patch | Sort-Object Name)
foreach ($patch in $patches) {
$oldPreference=$ErrorActionPreference
try {
$ErrorActionPreference='Continue'
& git -C $LlvmSourceRoot apply --reverse --check $patch.FullName 2>$null
$applied=($LASTEXITCODE -eq 0)
} finally { $ErrorActionPreference=$oldPreference }
if (!$applied) {
& git -C $LlvmSourceRoot apply --check $patch.FullName
if ($LASTEXITCODE) { throw "LLVM source patch conflicts: $($patch.Name)" }
& git -C $LlvmSourceRoot apply $patch.FullName
if ($LASTEXITCODE) { throw "LLVM source patch failed: $($patch.Name)" }
}
Write-Host "LLVM source patch present: $($patch.Name)"
}
# The pinned backport is the entire intended LLVM delta.
if ($patches.Count -ne 1) { throw 'Review combined LLVM source provenance before adding more patches.' }
$audit=Join-Path $repo ('build/llvm-source-audit-'+[Guid]::NewGuid().ToString('N')+'.patch')
New-Item -ItemType Directory -Force (Split-Path $audit) | Out-Null
try {
& git -C $LlvmSourceRoot diff --binary --output=$audit
if ($LASTEXITCODE) { throw 'Cannot audit LLVM source diff.' }
$actualDiff=[IO.File]::ReadAllText($audit).Replace("`r`n","`n").TrimEnd()
$expectedDiff=[IO.File]::ReadAllText($patches[0].FullName).Replace("`r`n","`n").TrimEnd()
if ($actualDiff -cne $expectedDiff) { throw 'LLVM source contains changes outside the exported audited patch.' }
} finally { if (Test-Path -LiteralPath $audit) { Remove-Item -LiteralPath $audit } }
+4 -1
View File
@@ -6,12 +6,15 @@ param(
[switch]$Translated [switch]$Translated
) )
$ErrorActionPreference='Stop' $ErrorActionPreference='Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' } if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' }
if (!$PackageRoot) { $PackageRoot=Join-Path $repo 'dist/windows-rdna4-command-list' } 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')) } 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'; $settings=@{D4R_ZLUDA_PROFILE='1'; D4R_PROFILE_STAGES='1'; D4R_VALIDATE_OUTPUT='1';
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_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-$GpuArch" } else { Join-Path $repo "build/native-m-$GpuArch" })}
$old=@{} $old=@{}
try { try {
foreach ($key in $settings.Keys) { $old[$key]=[Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') } foreach ($key in $settings.Keys) { $old[$key]=[Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') }
+188
View File
@@ -0,0 +1,188 @@
[CmdletBinding()]
param(
[ValidateSet('run','install','restore')][string]$Action='run',
[ValidateSet(11,13)][int]$Preset=11,
[string]$GameExe, [string]$NgxCore, [string]$DlssDll,
[switch]$CaptureExceptions
)
$ErrorActionPreference='Stop'
$root=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
$work=Join-Path $root 'work'
$settingsPath=Join-Path $work 'settings.json'
New-Item -ItemType Directory -Force $work | Out-Null
$saved=$null
if (Test-Path -LiteralPath $settingsPath) {
try { $saved=Get-Content -LiteralPath $settingsPath -Raw | ConvertFrom-Json }
catch { Write-Host 'Saved choices could not be read. Select the files again.' }
}
function Select-LocalFile([string]$Title, [string]$FileName, [string]$Previous) {
if ($Previous -and (Test-Path -LiteralPath $Previous -PathType Leaf)) { return (Get-Item -LiteralPath $Previous).FullName }
Add-Type -AssemblyName System.Windows.Forms
$dialog=New-Object Windows.Forms.OpenFileDialog
try {
$dialog.Title=$Title; $dialog.FileName=$FileName
$dialog.Filter=if ($FileName -eq '*.exe') { 'Game executable (*.exe)|*.exe' } else { "$FileName|$FileName" }
if ($dialog.ShowDialog() -ne [Windows.Forms.DialogResult]::OK) { throw 'File selection cancelled. No test was launched.' }
return $dialog.FileName
} finally { $dialog.Dispose() }
}
function Copy-Payload([string]$Source, [string]$Destination, [string]$Relative) {
$path=[IO.Path]::GetFullPath((Join-Path $Destination $Relative))
if (!$path.StartsWith($Destination+'\',[StringComparison]::OrdinalIgnoreCase)) { throw 'Invalid package path.' }
New-Item -ItemType Directory -Force (Split-Path $path) | Out-Null
Copy-Item -LiteralPath (Join-Path $Source $Relative) -Destination $path -Force
}
function Get-BundledNvidiaFile([string]$Relative) {
$directory=Join-Path $PSScriptRoot 'nvidia'
$path=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot $Relative))
if (!$path.StartsWith($directory+'\',[StringComparison]::OrdinalIgnoreCase) -or
!(Test-Path -LiteralPath $path -PathType Leaf)) { throw 'Invalid bundled NVIDIA file. Extract the complete package again.' }
return $path
}
if ($Action -eq 'restore') {
$GameExe=Select-LocalFile 'Select the SAME game executable used for the test' '*.exe' $(if ($GameExe) { $GameExe } elseif ($saved) { $saved.game })
& (Join-Path $PSScriptRoot 'common/windows-game.ps1') -Action restore -GameExe $GameExe
Write-Host 'RESTORED. Launch the game normally again.' -ForegroundColor Green
return
}
$output=Join-Path $root ('results/test-'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff'))
New-Item -ItemType Directory -Force $output | Out-Null
$status=[ordered]@{action=$Action; preset=$Preset; status='starting'; hardwareValidationCompleted=$false}
$selectedPackage=$null
try {
Write-Host 'd4r Windows quick test - keep this window open until you EXIT the game.'
Write-Host 'Checking package and detecting the GPU...'
$manifest=Get-Content -LiteralPath (Join-Path $PSScriptRoot 'package.json') -Raw | ConvertFrom-Json
foreach ($file in $manifest.files) {
$path=[IO.Path]::GetFullPath((Join-Path $root $file.path))
if (!$path.StartsWith($root+'\',[StringComparison]::OrdinalIgnoreCase) -or
(Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash -ne $file.sha256) { throw "Package is incomplete or changed. Extract the ZIP again. File: $($file.path)" }
}
$status.packageCommit=$manifest.packageCommit
$inventoryDir=Join-Path $output 'gpu'
New-Item -ItemType Directory -Force $inventoryDir | Out-Null
$info=New-Object Diagnostics.ProcessStartInfo
$info.FileName=Join-Path $PSScriptRoot 'targets/gfx1201/d4r/d4r_gpu_inventory.exe'
$info.Arguments='--hip-root "'+(Join-Path $PSScriptRoot 'common/d4r/hip')+'" --output-dir "'+$inventoryDir+'"'
$info.UseShellExecute=$false; $info.CreateNoWindow=$true
$info.EnvironmentVariables['D4R_DIAG_DIR']=$inventoryDir
$info.RedirectStandardOutput=$true; $info.RedirectStandardError=$true
$process=New-Object Diagnostics.Process; $process.StartInfo=$info
try {
if (!$process.Start()) { throw 'GPU detection could not start.' }
$stdout=$process.StandardOutput.ReadToEndAsync(); $stderr=$process.StandardError.ReadToEndAsync()
$timedOut=!$process.WaitForExit(30000)
if ($timedOut) { $process.Kill(); $process.WaitForExit() }
$stdout.GetAwaiter().GetResult() | Set-Content (Join-Path $inventoryDir 'stdout.log') -Encoding UTF8
$stderr.GetAwaiter().GetResult() | Set-Content (Join-Path $inventoryDir 'stderr.log') -Encoding UTF8
$status.inventoryExit='0x{0:X8}' -f [BitConverter]::ToUInt32([BitConverter]::GetBytes([int]$process.ExitCode),0)
if ($timedOut -or $process.ExitCode) { throw 'HIP could not detect your GPU. The diagnostic ZIP contains the details.' }
} finally { $process.Dispose() }
$inventory=Get-Content -LiteralPath (Join-Path $inventoryDir 'gpu-inventory.json') -Raw | ConvertFrom-Json
$devices=@($inventory.devices | Where-Object { $_.buildEligible -and $_.wavefrontSize -eq 32 -and $manifest.targets.PSObject.Properties.Name -contains $_.architecture } | Sort-Object vramBytes -Descending)
if (!$devices.Count) { throw 'No supported RDNA3/RDNA4 HIP device was detected. See the GPU inventory in the diagnostic ZIP.' }
$device=$devices[0]
if (@($devices.architecture | Select-Object -Unique).Count -gt 1) {
for ($i=0; $i -lt $devices.Count; $i++) { Write-Host "$($i+1): $($devices[$i].name) ($($devices[$i].architecture))" }
$choice=Read-Host 'Select the GPU used by your game (press Enter for 1)'
if ($choice) {
$index=0
if (![int]::TryParse($choice,[ref]$index) -or $index -lt 1 -or $index -gt $devices.Count) { throw 'Invalid GPU selection.' }
$device=$devices[$index-1]
}
}
$arch=$device.architecture; $target=$manifest.targets.$arch
$status.gpu=$device; $status.binarySourceCommit=$target.binarySourceCommit
Write-Host "Detected: $($device.name) / $arch" -ForegroundColor Cyan
Write-Host 'Experimental build. This run is a correctness test, not an FPS benchmark.'
$previousGame=if ($saved) { $saved.game } else { $null }
if (!$GameExe -and $previousGame -and (Test-Path -LiteralPath $previousGame)) {
Write-Host "Remembered game: $previousGame"
$reuse=Read-Host 'Press Enter to use this game, or type C to choose another (restore the old game first)'
if ($reuse -match '^(?i)c$') { $previousGame=$null }
elseif ($reuse) { throw 'Invalid choice. Press Enter or type C on the next run.' }
}
$GameExe=Select-LocalFile 'Select the actual D3D12 game .exe (not Steam or a launcher)' '*.exe' $(if ($GameExe) { $GameExe } else { $previousGame })
$bundledCore=$null; $bundledDlss=$null
if ($manifest.bundledNvidia) {
$bundledCore=Get-BundledNvidiaFile $manifest.bundledNvidia.ngxCore
$bundledDlss=Get-BundledNvidiaFile $manifest.bundledNvidia.dlssDll
}
$status.nvidiaDllSource=@{core=$(if ($NgxCore) { 'explicit' } elseif ($bundledCore) { 'bundled' } else { 'user-supplied' });
dlss=$(if ($DlssDll) { 'explicit' } elseif ($bundledDlss) { 'bundled' } else { 'user-supplied' })}
if ($bundledCore -and !$NgxCore -and !$DlssDll) { Write-Host 'Using the validated NVIDIA DLL pair included by this package distributor.' }
$NgxCore=Select-LocalFile 'Select your ORIGINAL NVIDIA _nvngx.dll (32.0.16.1714)' '_nvngx.dll' $(if ($NgxCore) { $NgxCore } elseif ($bundledCore) { $bundledCore } elseif ($saved) { $saved.ngxCore })
$DlssDll=Select-LocalFile 'Select your NVIDIA nvngx_dlss.dll (310.9.1)' 'nvngx_dlss.dll' $(if ($DlssDll) { $DlssDll } elseif ($bundledDlss) { $bundledDlss } elseif ($saved) { $saved.dlssDll })
$status.game=$GameExe
$status.localDlls=@($NgxCore,$DlssDll | ForEach-Object { $item=Get-Item -LiteralPath $_; @{path=$item.FullName; version=$item.VersionInfo.FileVersion; sha256=(Get-FileHash -LiteralPath $_ -Algorithm SHA256).Hash} })
if ((Get-FileHash -LiteralPath $NgxCore -Algorithm SHA256).Hash -ne $manifest.ngxCoreSha256) {
throw 'Wrong _nvngx.dll. This test needs the original NVIDIA 32.0.16.1714 DLL, not the d4r shim. Select the correct file on the next run.'
}
if ((Get-FileHash -LiteralPath $DlssDll -Algorithm SHA256).Hash -ne $target.dlssSha256) {
throw 'Wrong nvngx_dlss.dll. This test needs exactly NVIDIA DLSS 310.9.1. Select the correct file on the next run.'
}
if ($GameExe.StartsWith($root+'\',[StringComparison]::OrdinalIgnoreCase)) { throw 'Select the installed GAME executable, outside this extracted package.' }
# Save only validated choices. Wrong DLLs are never remembered.
@{game=$GameExe; ngxCore=$NgxCore; dlssDll=$DlssDll; architecture=$arch} | ConvertTo-Json | Set-Content -LiteralPath $settingsPath -Encoding UTF8
$selectedPackage=Join-Path $work "package-$arch"
New-Item -ItemType Directory -Force $selectedPackage | Out-Null
$selectedFiles=@()
foreach ($file in $manifest.commonFiles) {
Copy-Payload (Join-Path $PSScriptRoot 'common') $selectedPackage $file.path
$selectedFiles+=$file
}
foreach ($file in $target.files) {
Copy-Payload (Join-Path $PSScriptRoot "targets/$arch") $selectedPackage $file.path
$selectedFiles+=$file
}
@{architecture=$arch; d4rCommit=$target.binarySourceCommit; packageCommit=$manifest.packageCommit;
dlssSha256=$target.dlssSha256; hardwareValidation=$target.hardwareValidation; files=$selectedFiles} |
ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Join-Path $selectedPackage 'package.json') -Encoding UTF8
Write-Host 'Original game files will be backed up. RESTORE-GAME.cmd reverses the installation.'
Write-Host 'In the game: select DLSS if available; otherwise select FSR/XeSS to let OptiScaler intercept it.'
Write-Host 'The first launch compiles shaders and can take several minutes. Then play for 30 seconds and EXIT the game normally.'
& (Join-Path $selectedPackage 'windows-game.ps1') -Action $Action -GameExe $GameExe -NgxCore $NgxCore -DlssDll $DlssDll `
-Preset $Preset -ValidateOutput -CaptureExceptions:$CaptureExceptions -OutputDirectory $output
if ($Action -eq 'install') { $status.status='installed-only' }
else {
$summary=Get-Content -LiteralPath (Join-Path $output 'summary.json') -Raw | ConvertFrom-Json
$status.gameSummary=$summary
$required=Get-Content -LiteralPath (Join-Path $selectedPackage 'd4r/native/d4r-kernels.txt') | Where-Object {
if ($Preset -eq 11) { $_ -match '^dltss_pwin_' } else { $_ -match '^rrlite_' }
} | ForEach-Object { $_.Split(' ')[0] }
$hits=@($summary.kernels | Where-Object { $_.backend -eq 'native' -and $_.launches -gt 0 } | ForEach-Object { $_.kernel })
$status.missingNativeKernels=@($required | Where-Object { $hits -notcontains $_ })
$status.translatedNativeKernels=@($summary.kernels | Where-Object { $_.backend -eq 'translated' -and $required -contains $_.kernel } | ForEach-Object { $_.kernel })
$passed=$summary.exitCode -eq '0x0' -and $summary.completedFrames -gt 0 -and $summary.nativeLaunches -gt 0 -and
$summary.outputGpuChecks -gt 0 -and $summary.failures -eq 0 -and $summary.nonfiniteOutputs -eq 0 -and
$summary.previousFrameOutputs -eq 0 -and $summary.cpuImageCopyFrames -eq 0 -and
@($required).Count -eq $(if ($Preset -eq 11) { 11 } else { 5 }) -and
$status.missingNativeKernels.Count -eq 0 -and $status.translatedNativeKernels.Count -eq 0
$status.hardwareValidationCompleted=[bool]$passed
$status.status=if ($passed) { 'backend-checks-passed-image-still-needs-user-review' }
elseif ($summary.completedFrames -eq 0 -and $summary.exitCode -eq '0x0' -and $summary.failures -eq 0) { 'inconclusive-process-exited-without-dlss-frames' }
else { 'backend-checks-failed-or-no-dlss-frames' }
$status.session=$summary.session
if ($status.status -eq 'inconclusive-process-exited-without-dlss-frames') {
Write-Host 'The launched process exited without a DLSS frame. A Steam/launcher restart is not tracked by this test. Keep Steam open and select the actual game executable. Send the ZIP if it restarts again.' -ForegroundColor Yellow
}
Write-Host $(if ($passed) { 'Backend checks PASSED. Please also report whether the image looked correct.' } else { 'Test did not pass. Send the diagnostic ZIP; do not guess from the FPS.' }) -ForegroundColor $(if ($passed) { 'Green' } else { 'Yellow' })
}
} catch {
$status.status='failed'; $status.error=$_.Exception.Message
$_ | Out-String | Set-Content -LiteralPath (Join-Path $output 'error.txt') -Encoding UTF8
Write-Host "TEST STOPPED: $($status.error)" -ForegroundColor Red
} finally {
if ($selectedPackage) {
$preflights=Join-Path $selectedPackage 'results'
$latest=Get-ChildItem -LiteralPath $preflights -Directory -ErrorAction SilentlyContinue | Sort-Object Name -Descending | Select-Object -First 1
if ($latest) { Copy-Item -LiteralPath $latest.FullName -Destination (Join-Path $output 'preflight') -Recurse -Force }
}
try { Get-CimInstance Win32_VideoController | Select-Object Name,DriverVersion,DriverDate | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $output 'driver.json') -Encoding UTF8 } catch {}
$status | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath (Join-Path $output 'quick-test.json') -Encoding UTF8
Compress-Archive -LiteralPath $output -DestinationPath ($output+'.zip') -Force
Write-Host "SEND THIS ONE FILE: $output.zip" -ForegroundColor Cyan
Write-Host 'Also tell us: GPU model, game name, K or M, and whether the image was correct.'
Write-Host 'Close the game before running RESTORE-GAME.cmd.'
}
if ($status.status -eq 'failed' -or $status.status -eq 'backend-checks-failed-or-no-dlss-frames' -or $status.status.StartsWith('inconclusive-')) { exit 1 }
+11 -5
View File
@@ -1,24 +1,30 @@
[CmdletBinding()] [CmdletBinding()]
param([Parameter(Mandatory=$true)][string]$DlssDll, [string]$PackageRoot, [string]$OutputDirectory) param([Parameter(Mandatory=$true)][string]$DlssDll, [string]$PackageRoot, [string]$OutputDirectory, [string]$GpuArch)
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' } 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)) { if (![IO.Path]::IsPathRooted($DlssDll) -or !(Test-Path -LiteralPath $DlssDll)) {
throw 'Supply the absolute path to your local nvngx_dlss.dll.' throw 'Supply the absolute path to your local nvngx_dlss.dll.'
} }
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null 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') $layers = @('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0')
foreach ($layer in $layers) { foreach ($layer in $layers) {
$name = "dltss_pwin_${layer}_layer" $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" } 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 Copy-Item -LiteralPath $source -Destination (Join-Path $OutputDirectory "$name.hsaco") -Force
} }
& python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $OutputDirectory $DlssDll & python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $OutputDirectory $DlssDll
if ($LASTEXITCODE) { throw "Native manifest generation failed ($LASTEXITCODE)" } if ($LASTEXITCODE) { throw "Native manifest generation failed ($LASTEXITCODE)" }
@{architecture='gfx1201'; family='K'; dlssSha256=(Get-FileHash -LiteralPath $DlssDll -Algorithm SHA256).Hash; @{architecture=$GpuArch; family='K'; 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)} | 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 ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'build-info.json') -Encoding UTF8
Write-Host "Experimental native K directory: $OutputDirectory" Write-Host "Experimental native K directory: $OutputDirectory"
+11 -5
View File
@@ -1,24 +1,30 @@
[CmdletBinding()] [CmdletBinding()]
param([Parameter(Mandatory=$true)][string]$DlssDll, [string]$PackageRoot, [string]$OutputDirectory) param([Parameter(Mandatory=$true)][string]$DlssDll, [string]$PackageRoot, [string]$OutputDirectory, [string]$GpuArch)
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' } 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)) { if (![IO.Path]::IsPathRooted($DlssDll) -or !(Test-Path -LiteralPath $DlssDll)) {
throw 'Supply the absolute path to your local nvngx_dlss.dll.' throw 'Supply the absolute path to your local nvngx_dlss.dll.'
} }
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null 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')) { foreach ($layer in @('enc1','enc2','enc3_tube','dec2','dec1')) {
$name = "rrlite_${layer}_4x4" $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" } 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 Copy-Item -LiteralPath $source -Destination (Join-Path $OutputDirectory "$name.hsaco") -Force
} }
& python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $OutputDirectory $DlssDll & python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $OutputDirectory $DlssDll
if ($LASTEXITCODE) { throw "Native manifest generation failed ($LASTEXITCODE)" } 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; 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)} | 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 ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'build-info.json') -Encoding UTF8
Write-Host "Experimental native M directory: $OutputDirectory" Write-Host "Experimental native M directory: $OutputDirectory"
+27 -4
View File
@@ -3,23 +3,33 @@ param(
[string]$NgxCore, [string]$DlssDll, [string]$NativeRoot, [string]$NgxCore, [string]$DlssDll, [string]$NativeRoot,
[string]$ZludaRoot, [string]$PackageRoot, [string]$ZludaRoot, [string]$PackageRoot,
[string]$Resolution = '3840x2160', [string]$Resolution = '3840x2160',
[string]$InputResolution,
[switch]$ProfileGpuBoundary, [switch]$ProfileGpuBoundary,
[switch]$ProfileKernelsDeferred,
[switch]$BatchInputCopies,
[ValidateSet('baseline','packed','unorm','depth-stencil')][string]$PixelProfile = 'depth-stencil',
[string]$OutputDirectory [string]$OutputDirectory
) )
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$NgxCore) { $NgxCore = Join-Path $repo '_nvngx.dll' } if (!$NgxCore) { $NgxCore = Join-Path $repo '_nvngx.dll' }
if (!$DlssDll) { $DlssDll = Join-Path $repo 'nvngx_dlss.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 (!$ZludaRoot) { $ZludaRoot = Join-Path $repo 'dist/zluda-windows-api-profile' }
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-command-list' } 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 (!$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' } if ($Resolution -notmatch '^([0-9]+)x([0-9]+)$') { throw 'Resolution must be WIDTHxHEIGHT' }
$width=$Matches[1]; $height=$Matches[2] $width=$Matches[1]; $height=$Matches[2]
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$settings = @{D4R_DIAG_BURST='1'; D4R_DIAG_RECREATE=$null; D4R_ASYNC_INTEROP=$null; $settings = @{D4R_DIAG_BURST='1'; D4R_DIAG_RECREATE=$null; D4R_ASYNC_INTEROP=$null;
D4R_BATCH_INPUT_COPIES=$null;
D4R_ZLUDA_NATIVE_DIR=$NativeRoot; D4R_QUIET_API='1'; D4R_VALIDATE_OUTPUT='1'; D4R_ZLUDA_NATIVE_DIR=$NativeRoot; D4R_QUIET_API='1'; D4R_VALIDATE_OUTPUT='1';
D4R_PROFILE_STAGES='1'; D4R_ZLUDA_PROFILE=$null; D4R_ZLUDA_PROFILE_API=$null; D4R_PROFILE_STAGES='1'; D4R_ZLUDA_PROFILE=$null; D4R_ZLUDA_PROFILE_API=$null;
D4R_ZLUDA_PROFILE_DEFERRED=$(if ($ProfileKernelsDeferred) { '1' } else { $null });
D4R_ZLUDA_PROFILE_ALLOW_LEGACY=$(if ($ProfileKernelsDeferred) { '1' } else { $null });
D4R_ZLUDA_PROFILE_EVERY=$(if ($ProfileKernelsDeferred) { '1' } else { $null });
D4R_PROFILE_GPU_BOUNDARY=$(if ($ProfileGpuBoundary) { '1' } else { $null }); D4R_PROFILE_GPU_BOUNDARY=$(if ($ProfileGpuBoundary) { '1' } else { $null });
ZLUDA_CACHE_DIR=(Join-Path $repo 'build/zluda-cache-windows'); ZLUDA_CACHE_DIR=(Join-Path $repo 'build/zluda-cache-windows');
PYTHONPATH=(Join-Path $repo '.tools/python/vendor')} PYTHONPATH=(Join-Path $repo '.tools/python/vendor')}
@@ -33,15 +43,26 @@ try {
[Environment]::SetEnvironmentVariable('D4R_DIAG_RECREATE', $(if ($mode -eq 'recreate') { '1' } else { $null }), 'Process') [Environment]::SetEnvironmentVariable('D4R_DIAG_RECREATE', $(if ($mode -eq 'recreate') { '1' } else { $null }), 'Process')
foreach ($variant in @('sync','async')) { foreach ($variant in @('sync','async')) {
[Environment]::SetEnvironmentVariable('D4R_ASYNC_INTEROP', $(if ($variant -eq 'async') { '1' } else { $null }), 'Process') [Environment]::SetEnvironmentVariable('D4R_ASYNC_INTEROP', $(if ($variant -eq 'async') { '1' } else { $null }), 'Process')
[Environment]::SetEnvironmentVariable('D4R_BATCH_INPUT_COPIES', $(if ($BatchInputCopies -and $variant -eq 'async') { '1' } else { $null }), 'Process')
$output=Join-Path $OutputDirectory "$variant-$mode" $output=Join-Path $OutputDirectory "$variant-$mode"
$arguments=@{RuntimeProfile='therock'; ZludaRoot=$ZludaRoot; PackageRoot=$PackageRoot; $arguments=@{RuntimeProfile='therock'; ZludaRoot=$ZludaRoot; PackageRoot=$PackageRoot;
NgxCore=$NgxCore; DlssDll=$DlssDll; NgxOnly=$true; NgxMode='d3d12'; NgxAbi='project-legacy'; NgxCore=$NgxCore; DlssDll=$DlssDll; NgxOnly=$true; NgxMode='d3d12'; NgxAbi='project-legacy';
Preset=11; NgxCreateFlags=11; NgxOutputResolution=$Resolution; RequireNativeNetwork=$true; Preset=11; NgxCreateFlags=11; NgxOutputResolution=$Resolution; RequireNativeNetwork=$true;
CommandListBackend=$true; EarlyIndirectProbe=$true; PixelProfile='depth-stencil'; NgxInputResolution=$InputResolution;
CommandListBackend=$true; EarlyIndirectProbe=$true; PixelProfile=$PixelProfile;
BarrierMode='inherited-legacy'; Iterations=3; CaptureExceptions=$true; TimeoutSeconds=600; BarrierMode='inherited-legacy'; Iterations=3; CaptureExceptions=$true; TimeoutSeconds=600;
OptiScalerDll=(Join-Path $repo 'dist/optiscaler-windows-d4r/OptiScaler.dll'); OutputDirectory=$output} OptiScalerDll=(Join-Path $repo 'dist/optiscaler-windows-d4r/OptiScaler.dll'); OutputDirectory=$output}
& (Join-Path $PSScriptRoot 'test-windows-rdna4.ps1') @arguments & (Join-Path $PSScriptRoot 'test-windows-rdna4.ps1') @arguments
if ($LASTEXITCODE) { throw "Failed $variant-$mode; logs: $output" } if ($LASTEXITCODE) { throw "Failed $variant-$mode; logs: $output" }
if ($ProfileKernelsDeferred) {
$stderr=Get-Content -LiteralPath (Join-Path $output 'd3d12-evaluate-preset-11.stderr.log') -Raw
if ($stderr -match '(?m)^D4R_KERNEL_PROFILE .*serializing=1') { throw 'Unexpected serializing kernel profiler.' }
foreach ($layer in @('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0')) {
$pattern='(?m)^D4R_KERNEL_PROFILE(?:_INVALID)? kernel="dltss_pwin_' + $layer + '_layer" backend=native phase=main '
$events=[regex]::Matches($stderr,$pattern)
if ($events.Count -ne 3) { throw "Expected three collected $layer events for $variant-$mode, found $($events.Count)." }
}
}
if ($ProfileGpuBoundary -and $variant -eq 'async') { if ($ProfileGpuBoundary -and $variant -eq 'async') {
$stdout=Get-Content -LiteralPath (Join-Path $output 'd3d12-evaluate-preset-11.stdout.log') -Raw $stdout=Get-Content -LiteralPath (Join-Path $output 'd3d12-evaluate-preset-11.stdout.log') -Raw
$timings=[regex]::Matches($stdout, '(?m)^D4R_GPU_BOUNDARY [^\r\n]*diagnostics_cpu_bytes=32 serializing=0\r?$') $timings=[regex]::Matches($stdout, '(?m)^D4R_GPU_BOUNDARY [^\r\n]*diagnostics_cpu_bytes=32 serializing=0\r?$')
@@ -52,8 +73,10 @@ try {
Tee-Object -FilePath (Join-Path $OutputDirectory "$mode-comparison.log") Tee-Object -FilePath (Join-Path $OutputDirectory "$mode-comparison.log")
if ($LASTEXITCODE) { throw "Async $mode output mismatch" } if ($LASTEXITCODE) { throw "Async $mode output mismatch" }
} }
@{passed=$true; architecture='gfx1201'; preset=11; resolution=$Resolution; @{passed=$true; architecture=$GpuArch; preset=11; resolution=$Resolution;
modes=@('burst','recreate'); framesPerMode=3; exactRgb=$true; gpuBoundaryTiming=[bool]$ProfileGpuBoundary} | inputResolution=$InputResolution;
modes=@('burst','recreate'); framesPerMode=3; exactRgb=$true; gpuBoundaryTiming=[bool]$ProfileGpuBoundary;
deferredKernelTiming=[bool]$ProfileKernelsDeferred; batchInputs=[bool]$BatchInputCopies; pixelProfile=$PixelProfile} |
ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory 'validation.json') -Encoding UTF8 ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory 'validation.json') -Encoding UTF8
Write-Host "PASS asynchronous K burst/recreate: $OutputDirectory" Write-Host "PASS asynchronous K burst/recreate: $OutputDirectory"
} finally { } finally {
+122
View File
@@ -0,0 +1,122 @@
# d4r-check.ps1 against fixture installs. No GPU, no NVIDIA binaries: the packaging test's
# synthetic release is the install, a fake inventory file stands in for the GPU query, and
# fixture text files stand in for the NVIDIA DLLs (pinned in a fixture dll-pins.txt).
[CmdletBinding()]
param([string]$OutputDirectory)
$ErrorActionPreference='Stop'
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/d4r-check/'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
& (Join-Path $PSScriptRoot 'test-windows-release-package.ps1') -OutputDirectory (Join-Path $OutputDirectory 'package') | Out-Null
$release=Join-Path $OutputDirectory 'package/release'
$check=Join-Path $repo 'scripts/windows/d4r-check.ps1'
$powershell="$env:SystemRoot/System32/WindowsPowerShell/v1.0/powershell.exe"
$results=@()
function Sha([string]$path) { (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash }
function Set-ManifestHash([string]$game,[string]$relative) {
$path=Join-Path $game 'd4r/package.json'
$manifest=Get-Content -LiteralPath $path -Raw | ConvertFrom-Json
foreach ($entry in $manifest.files) { if ($entry.path -eq $relative) { $entry.sha256=Sha (Join-Path $game ($relative -replace '/','\')) } }
$manifest | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $path -Encoding UTF8
}
function New-Install([string]$name) {
$game=Join-Path $OutputDirectory "cases/$name"
New-Item -ItemType Directory -Force (Split-Path $game) | Out-Null
Copy-Item -LiteralPath $release -Destination $game -Recurse
Set-Content -LiteralPath (Join-Path $game 'Game.exe') 'fixture' -Encoding ASCII
Set-Content -LiteralPath (Join-Path $game 'd4r/nvngx_dlss.dll') 'fixture dlss' -Encoding ASCII
Set-Content -LiteralPath (Join-Path $game 'd4r/ngx/_nvngx.dll') 'fixture core' -Encoding ASCII
$unrelated=('B'*64)
@('# fixture pins',
"dlss $(Sha (Join-Path $game 'd4r/nvngx_dlss.dll')) 310.9.1 validated",
"dlss $unrelated 310.7.0 unvalidated",
"ngx $(Sha (Join-Path $game 'd4r/ngx/_nvngx.dll')) 32.0.16.1714 validated") |
Set-Content -LiteralPath (Join-Path $game 'd4r/dll-pins.txt') -Encoding ASCII
Set-ManifestHash $game 'd4r/dll-pins.txt'
return $game
}
function Write-Inventory([string]$name,[string]$arch,[string]$gpu='AMD Radeon RX 7700 XT') {
$path=Join-Path $OutputDirectory "inventory-$name.json"
@{devices=@(@{name=$gpu; architecture=$arch; wavefrontSize=32})} | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $path -Encoding UTF8
return $path
}
function Run-Check([string]$game,[string]$inventory,[string[]]$More=@()) {
$ErrorActionPreference='Continue' # native stderr must not become a terminating error
$text=& $powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $game 'd4r/d4r-check.ps1') -GameFolder $game -InventoryFile $inventory @More 2>&1 | Out-String
return @{exit=$LASTEXITCODE; text=$text}
}
function Expect([string]$case,$run,[int]$exit,[string[]]$patterns) {
if ($run.exit -ne $exit) { throw "$case : exit $($run.exit), expected $exit`n$($run.text)" }
foreach ($pattern in $patterns) { if ($run.text -notmatch $pattern) { throw "$case : output lacks /$pattern/`n$($run.text)" } }
$script:results+=@{case=$case; passed=$true; gpuKernelsExecuted=$false}
}
# 1. Healthy install.
$game=New-Install 'healthy'; $inv=Write-Inventory 'a' 'gfx1101'
Expect 'healthy-install' (Run-Check $game $inv) 0 @('ok\s+d4r\\nvngx_dlss\.dll \(nvngx_dlss\.dll 310\.9\.1, tested on Windows\)','ok\s+d4r\\ngx\\_nvngx\.dll',
'ok\s+GPU AMD Radeon RX 7700 XT \(gfx1101\): build present, 16 native kernels','ok\s+all 17 GPU code objects are built for gfx1101',
'ok\s+no vkd3d/Wine d3d12\.dll','ok\s+d4r\.ini is valid \(model K\)','compile-tested','Everything d4r needs is in place')
# 2. NVIDIA DLL problems, each with the fix.
$game=New-Install 'dll-missing'; Remove-Item -LiteralPath (Join-Path $game 'd4r/nvngx_dlss.dll')
Expect 'dll-missing' (Run-Check $game $inv) 1 @("MISSING\s+d4r\\nvngx_dlss\.dll: copy NVIDIA's nvngx_dlss\.dll here \(version 310\.9\.1 or 310\.7\.0","1 thing\(s\) to fix")
$game=New-Install 'dll-wrong'; Set-Content -LiteralPath (Join-Path $game 'd4r/nvngx_dlss.dll') 'some other version' -Encoding ASCII
Expect 'dll-wrong-version' (Run-Check $game $inv) 1 @('MISSING\s+d4r\\nvngx_dlss\.dll is version \S+, which d4r does not accept\. d4r on Windows needs exactly 310\.9\.1 or 310\.7\.0')
$game=New-Install 'dll-less-tested'; Set-Content -LiteralPath (Join-Path $game 'd4r/nvngx_dlss.dll') 'fixture dlss 310.7' -Encoding ASCII
@('dlss '+(Sha (Join-Path $game 'd4r/nvngx_dlss.dll'))+' 310.7.0 unvalidated','ngx '+(Sha (Join-Path $game 'd4r/ngx/_nvngx.dll'))+' 32.0.16.1714 validated') | Set-Content -LiteralPath (Join-Path $game 'd4r/dll-pins.txt') -Encoding ASCII
Set-ManifestHash $game 'd4r/dll-pins.txt'
Expect 'dll-less-tested-accepted' (Run-Check $game $inv) 0 @('ok\s+d4r\\nvngx_dlss\.dll \(nvngx_dlss\.dll 310\.7\.0\)','note\s+nvngx_dlss\.dll 310\.7\.0 is accepted but has had less Windows testing')
# 3. Leftovers, edited files, a replaced dxgi.dll, and a broken OptiScaler.ini.
$game=New-Install 'vkd3d'; Set-Content -LiteralPath (Join-Path $game 'd3d12.dll') 'vkd3d-proton fixture' -Encoding ASCII
Expect 'vkd3d-proxy' (Run-Check $game $inv) 1 @('MISSING\s+d3d12\.dll in the game folder is vkd3d-proton/Wine')
$game=New-Install 'dxgi-replaced'; Set-Content -LiteralPath (Join-Path $game 'dxgi.dll') 'another dxgi' -Encoding ASCII
Expect 'foreign-dxgi' (Run-Check $game $inv) 0 @('note\s+dxgi\.dll is not the one that came with d4r')
$game=New-Install 'opti-path'; (Get-Content -LiteralPath (Join-Path $game 'OptiScaler.ini')) -replace '^NvngxPath=.*$','NvngxPath=nowhere' | Set-Content -LiteralPath (Join-Path $game 'OptiScaler.ini') -Encoding ASCII
Expect 'optiscaler-ini-path' (Run-Check $game $inv) 1 @('MISSING\s+OptiScaler\.ini NvngxPath = nowhere does not hold _nvngx\.dll')
# 4. d4r.ini mistakes are notes with what will happen instead.
$game=New-Install 'ini'; Set-Content -LiteralPath (Join-Path $game 'd4r/d4r.ini') "[DLSS]`nModel = E`nShowWatermark = sometimes`n[Latency]`nFrameAge = 1`n[Debug]`nLog = loud`n" -Encoding ASCII
Expect 'ini-mistakes' (Run-Check $game $inv) 0 @("note\s+d4r\.ini: \[DLSS\] Model 'E' is not available on Windows",'must be true or false, not ''sometimes''','\[Latency\] FrameAge is not a Windows setting','Log must be normal or verbose')
# 5. GPU cases.
$game=New-Install 'gpu-no-build'; Remove-Item -LiteralPath (Join-Path $game 'd4r/_nvngx_gfx1103.dll')
Expect 'gpu-without-build' (Run-Check $game (Write-Inventory 'b' 'gfx1103' 'AMD Radeon 780M')) 1 @('MISSING\s+GPU AMD Radeon 780M \(gfx1103\): this release has no complete build for it \(missing: d4r\\_nvngx_gfx1103\.dll\)')
$game=New-Install 'gpu-rdna2'
Expect 'gpu-unsupported' (Run-Check $game (Write-Inventory 'c' 'gfx1030' 'AMD Radeon RX 6800')) 1 @('MISSING\s+no supported RDNA3/RDNA4 GPU found by the AMD runtime\. It sees: AMD Radeon RX 6800 \(gfx1030\)')
$game=New-Install 'gpu-wrong-object'
$other=@(Get-ChildItem -LiteralPath (Join-Path $game 'd4r/native/gfx1100') -Filter '*.hsaco')[0].FullName
Copy-Item -LiteralPath $other -Destination (Join-Path $game 'd4r/native/gfx1101/kernel3.hsaco') -Force
Expect 'code-object-for-another-gpu' (Run-Check $game $inv) 1 @('MISSING\s+kernel3\.hsaco is not built for gfx1101','differs from the release')
# 6. What the last game start said, and the bug-report ZIP.
$game=New-Install 'log-failure'
Set-Content -LiteralPath (Join-Path $game 'd4r/d4r_nvngx.log') "d4r: DLSS disabled: Wrong nvngx_dlss.dll: you have version 1.2.3.4" -Encoding ASCII
Expect 'log-reports-failure' (Run-Check $game $inv) 1 @('MISSING\s+last game start: DLSS disabled: Wrong nvngx_dlss\.dll')
$game=New-Install 'log-ok'
Set-Content -LiteralPath (Join-Path $game 'd4r/d4r_nvngx.log') "d4r: player mode, model=K validate=0 async=0 log=normal, architecture=gfx1101" -Encoding ASCII
Expect 'log-reports-ok' (Run-Check $game $inv) 0 @('ok\s+last game start: player mode, model=K')
$game=New-Install 'report'; $reportDir=Join-Path $OutputDirectory 'reports'; New-Item -ItemType Directory -Force $reportDir | Out-Null
Set-Content -LiteralPath (Join-Path $game 'd4r/d4r_nvngx.log') "d4r: DLSS disabled: test failure" -Encoding ASCII
$run=Run-Check $game $inv @('-Report','-ReportDirectory',$reportDir)
$zip=@(Get-ChildItem -LiteralPath $reportDir -Filter 'd4r-report-*.zip')
if ($zip.Count -ne 1) { throw "Expected one report ZIP.`n$($run.text)" }
Add-Type -AssemblyName System.IO.Compression.FileSystem
$archive=[IO.Compression.ZipFile]::OpenRead($zip[0].FullName)
try { $names=@($archive.Entries | ForEach-Object { $_.Name }) } finally { $archive.Dispose() }
foreach ($required in @('check.txt','d4r_nvngx.log','d4r.ini','OptiScaler.ini','files.sha256.txt','gpu-inventory.json')) { if ($names -notcontains $required) { throw "Report lacks $required (has: $($names -join ', '))" } }
if ($names -contains 'nvngx_dlss.dll' -or $names -contains '_nvngx.dll') { throw 'The report must never contain NVIDIA DLLs.' }
if ($run.text -notmatch 'Bug report written') { throw 'Report location was not printed.' }
$results+=@{case='bug-report-zip'; passed=$true; entries=$names}
# 7. No d4r folder at all.
$empty=Join-Path $OutputDirectory 'cases/empty'; New-Item -ItemType Directory -Force $empty | Out-Null
$ErrorActionPreference='Continue'
$text=& $powershell -NoProfile -ExecutionPolicy Bypass -File $check -GameFolder $empty 2>&1 | Out-String
$exitEmpty=$LASTEXITCODE; $ErrorActionPreference='Stop'
if ($exitEmpty -ne 1 -or $text -notmatch 'no d4r folder here') { throw "A folder without d4r must be reported.`n$text" }
$results+=@{case='no-d4r-folder'; passed=$true}
$results | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
Write-Host "PASS D4R_CHECK cases=$($results.Count) Report: $OutputDirectory"
@@ -0,0 +1,103 @@
[CmdletBinding()]
param([string]$ZludaRoot, [string]$PackageRoot, [string]$HipRoot, [string]$OutputDirectory)
$ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$ZludaRoot) { $ZludaRoot = Join-Path $repo 'dist/zluda-windows-deferred-profile' }
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-command-list' }
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/deferred-profile-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$settings = @{
D4R_ZLUDA_PROFILE=$null; D4R_ZLUDA_PROFILE_DEFERRED='1'; D4R_ZLUDA_PROFILE_EVERY='1';
D4R_ZLUDA_PROFILE_ALLOW_LEGACY=$null;
D4R_DIAG_NONBLOCKING_STREAM='1'; D4R_DIAG_QUEUE_PTX=$null;
D4R_DIAG_DIR=$OutputDirectory;
ZLUDA_CACHE_DIR=(Join-Path $repo 'build/zluda-cache-windows');
PATH=((Join-Path $HipRoot 'bin') + ';' + $ZludaRoot + ';' + $env:PATH)
}
$old=@{}; $cases=@()
function RunProbe([string[]]$Arguments, [string]$Stdout, [string]$Stderr) {
$info=[Diagnostics.ProcessStartInfo]::new()
$info.FileName=Join-Path $PackageRoot 'bin/d4r_cuda_driver_probe.exe'
$info.Arguments=$Arguments -join ' '
$info.UseShellExecute=$false; $info.CreateNoWindow=$true
$info.RedirectStandardOutput=$true; $info.RedirectStandardError=$true
$p=[Diagnostics.Process]::new(); $p.StartInfo=$info
$outFile=$null; $errFile=$null; $outTask=$null; $errTask=$null
try {
if (!$p.Start()) { throw 'Could not launch the CUDA diagnostic.' }
$outFile=[IO.File]::Open($Stdout,[IO.FileMode]::Create,[IO.FileAccess]::Write,[IO.FileShare]::Read)
$errFile=[IO.File]::Open($Stderr,[IO.FileMode]::Create,[IO.FileAccess]::Write,[IO.FileShare]::Read)
$outTask=$p.StandardOutput.BaseStream.CopyToAsync($outFile)
$errTask=$p.StandardError.BaseStream.CopyToAsync($errFile)
$timer=[Diagnostics.Stopwatch]::StartNew()
while (!$p.WaitForExit(1000)) {
if ($timer.Elapsed.TotalSeconds -ge 120) { throw "CUDA diagnostic timed out; logs: $OutputDirectory" }
}
$p.WaitForExit()
[void]$outTask.GetAwaiter().GetResult(); [void]$errTask.GetAwaiter().GetResult()
return $p.ExitCode
} finally {
if (!$p.HasExited) { $p.Kill(); $p.WaitForExit() }
if ($outTask) { try { [void]$outTask.GetAwaiter().GetResult() } catch {} }
if ($errTask) { try { [void]$errTask.GetAwaiter().GetResult() } catch {} }
if ($outFile) { $outFile.Dispose() }; if ($errFile) { $errFile.Dispose() }
$p.Dispose()
}
}
try {
foreach ($key in $settings.Keys) {
$old[$key]=[Environment]::GetEnvironmentVariable($key,'Process')
[Environment]::SetEnvironmentVariable($key,$settings[$key],'Process')
}
foreach ($context in @('primary','created')) {
foreach ($queued in @($false,$true)) {
$name="$context-$(if ($queued) { 'queued' } else { 'single' })"
[Environment]::SetEnvironmentVariable('D4R_DIAG_QUEUE_PTX', $(if ($queued) { '1' } else { $null }), 'Process')
$iterations=129
$stdout=Join-Path $OutputDirectory "$name.stdout.log"
$stderr=Join-Path $OutputDirectory "$name.stderr.log"
# Native Windows argument parsing: quote absolute paths explicitly.
$arguments=@('--hip-root', ('"' + $HipRoot + '"'), '--cuda-dll', ('"' + (Join-Path $ZludaRoot 'nvcuda.dll') + '"'),
'--context', $context, '--iterations', "$iterations")
$exitCode=RunProbe $arguments $stdout $stderr
if ($exitCode) { throw "$name failed ($exitCode); logs: $OutputDirectory" }
$out=Get-Content -LiteralPath $stdout -Raw
$err=Get-Content -LiteralPath $stderr -Raw
if ($out -notmatch "PASS CUDA architecture=$GpuArch" -or $out -notmatch 'guard_verified=1') { throw "$name did not verify PTX output" }
$records=[regex]::Matches($err, '(?m)^D4R_KERNEL_PROFILE kernel="d4r_ptx_pattern" [^\r\n]*serializing=0 deferred=1 [^\r\n]+')
$invalid=[regex]::Matches($err, '(?m)^D4R_KERNEL_PROFILE_INVALID kernel="d4r_ptx_pattern" [^\r\n]*reason=invalid_elapsed_time serializing=0 deferred=1 [^\r\n]+')
if ($records.Count + $invalid.Count -ne $iterations -or $err -match 'serializing=1|D4R_KERNEL_PROFILE_SKIPPED') {
throw "$name expected $iterations collected deferred records, found $($records.Count) valid / $($invalid.Count) invalid"
}
foreach ($record in $records) {
if ($record.Value -notmatch 'gpu_ms=([0-9.]+)') {
throw "$name contains an invalid GPU time"
}
$gpuMs=[double]::Parse($Matches[1], [Globalization.CultureInfo]::InvariantCulture)
if ([double]::IsNaN($gpuMs) -or [double]::IsInfinity($gpuMs)) { throw "$name contains an invalid GPU time" }
}
$cases += @{context=$context; queued=$queued; launches=$iterations; collected=($records.Count+$invalid.Count);
validTimings=$records.Count; invalidTimings=$invalid.Count; exitCode=$exitCode}
Write-Host "PASS ${name}: $($records.Count) valid / $($invalid.Count) invalid deferred timings; exact PTX output and guards"
}
}
# Legacy default-stream timing markers would add stream dependencies.
[Environment]::SetEnvironmentVariable('D4R_DIAG_NONBLOCKING_STREAM',$null,'Process')
[Environment]::SetEnvironmentVariable('D4R_DIAG_QUEUE_PTX',$null,'Process')
$stdout=Join-Path $OutputDirectory 'default-stream.stdout.log'; $stderr=Join-Path $OutputDirectory 'default-stream.stderr.log'
$arguments=@('--hip-root', ('"' + $HipRoot + '"'), '--cuda-dll', ('"' + (Join-Path $ZludaRoot 'nvcuda.dll') + '"'), '--iterations', '3')
$exitCode=RunProbe $arguments $stdout $stderr
$out=Get-Content -LiteralPath $stdout -Raw; $err=Get-Content -LiteralPath $stderr -Raw
if ($exitCode -or $out -notmatch "PASS CUDA architecture=$GpuArch" -or
$err -notmatch 'reason=legacy_default_stream' -or $err -match '(?m)^D4R_KERNEL_PROFILE ') {
throw 'Default-stream diagnostics did not safely skip timing events.'
}
@{passed=$true; architecture=$GpuArch; cases=$cases; defaultStreamSkipped=$true; addedCompletionWaits=0} |
ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'validation.json') -Encoding UTF8
Write-Host "PASS deferred kernel profiling: $OutputDirectory"
} finally {
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
}
+38
View File
@@ -0,0 +1,38 @@
[CmdletBinding()]
param([string]$LogRoot, [string]$OutputDirectory)
$ErrorActionPreference='Stop'
. (Join-Path $PSScriptRoot 'game-summary.ps1')
$cases=@(
@{name='empty-streams-debugger-refused'; stdout=''; stderr=''; debugger='EXIT code=0xdeadc0de'; session='process-failed-before-dlss-frames'},
@{name='launcher-exit'; stdout=''; stderr=''; debugger='EXIT code=0x0'; session='process-exited-without-dlss-frames'},
@{name='feature-not-found'; stdout='D4R_CAPABILITIES available=0 feature_init=0xbad00004 needs_driver=0'; stderr='D4R_WINDOWS_FAILURE unavailable'; debugger='EXIT code=0xc0000409'; session='ngx-feature-not-found'},
@{name='complete-frame'; stdout="D4R_FRAME cpu_frame_copies=0 frame_age=0 interop_ngx_ms=12.4`nD4R_OUTPUT_VALIDATION elements=3 nan=0 inf=0 diagnostics_cpu_bytes=8";
stderr='[d4r-launch] kernel="dltss_pwin_enc0_layer" backend=native'; debugger='EXIT code=0x0'; session='completed-dlss-frames'},
@{name='truncated-frame'; stdout='D4R_FRAME cpu_frame_copies=0 frame_age=0 interop_ngx_ms='; stderr=''; debugger=''; session='diagnostic-timeout-without-dlss-frames'}
)
$results=@()
foreach ($case in $cases) {
$summary=Get-D4RGameSummary -Stdout $case.stdout -Stderr $case.stderr -Debugger $case.debugger
if ($summary.session -ne $case.session) { throw "Wrong session classification: $($case.name)" }
if ($case.name -eq 'complete-frame') {
if ($summary.completedFrames -ne 1 -or $summary.outputGpuChecks -ne 1 -or $summary.nativeLaunches -ne 1 -or
$summary.nonfiniteOutputs -or $summary.previousFrameOutputs -or $summary.cpuImageCopyFrames) { throw 'Complete frame counters are wrong.' }
} elseif ($summary.completedFrames -ne 0) { throw 'A failed or incomplete run was counted as a frame.' }
$results+=@{case=$case.name; passed=$true; summary=$summary}
}
if ($LogRoot) {
foreach ($entry in @{ 'acbf-k'='process-failed-before-dlss-frames'; 'cyberpunk-k'='ngx-feature-not-found';
'cyberpunk-m'='ngx-feature-not-found'; 'sm2-k'='process-exited-without-dlss-frames' }.GetEnumerator()) {
$dir=Get-ChildItem -LiteralPath (Join-Path $LogRoot $entry.Key) -Directory | Select-Object -First 1
$summary=Get-D4RGameSummary -Stdout ([IO.File]::ReadAllText((Join-Path $dir.FullName 'd4r.stdout.log'))) `
-Stderr ([IO.File]::ReadAllText((Join-Path $dir.FullName 'd4r.stderr.log'))) `
-Debugger ([IO.File]::ReadAllText((Join-Path $dir.FullName 'debugger.log')))
if ($summary.session -ne $entry.Value -or $summary.completedFrames -ne 0 -or $summary.nativeLaunches -ne 0) { throw "Unexpected classification for actual report $($entry.Key)" }
$results+=@{case='issue-10-'+$entry.Key; passed=$true; summary=$summary}
}
}
if ($OutputDirectory) {
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$results | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary-parser.json') -Encoding UTF8
}
Write-Host "PASS GAME_SUMMARY cases=$($results.Count)"
@@ -0,0 +1,23 @@
[CmdletBinding()]
param([Parameter(Mandatory=$true)][string]$PackageRoot, [Parameter(Mandatory=$true)][string]$CodeObject)
$ErrorActionPreference='Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$target=Get-D4RGpuTarget $PackageRoot $null
$other=if ($target -eq 'gfx1201') { 'gfx1200' } else { 'gfx1201' }
function Expect-Rejection([scriptblock]$Action) {
$rejected=$false
try { & $Action | Out-Null } catch { $rejected=$true }
if (!$rejected) { throw 'GPU target contract accepted incompatible input.' }
}
if ((Get-D4RGpuTarget $PackageRoot $target) -ne $target) { throw 'Incorrect package target.' }
Assert-D4RCodeObjectTarget $CodeObject $target
Expect-Rejection { Get-D4RGpuTarget $PackageRoot $other }
Expect-Rejection { Get-D4RGpuTarget $PackageRoot 'gfx1250' }
Expect-Rejection { Assert-D4RCodeObjectTarget $CodeObject $other }
Expect-Rejection { Assert-D4RCodeObjectTarget (Join-Path $PackageRoot 'gpu-target.json') $target }
# Old packages had no target metadata and were gfx1201 only.
$legacy=Join-Path $PackageRoot '__no-target-metadata__'
if (Test-Path -LiteralPath (Join-Path $legacy 'gpu-target.json')) { throw 'Unexpected legacy test fixture.' }
if ((Get-D4RGpuTarget $legacy $null) -ne 'gfx1201') { throw 'Legacy target changed.' }
Expect-Rejection { Get-D4RGpuTarget $legacy 'gfx1200' }
Write-Host "PASS GPU_TARGET_SCRIPT target=$target rejected=5 hardware_executed=0"
+3 -1
View File
@@ -3,9 +3,11 @@ param([Parameter(Mandatory=$true)][string]$CaptureDirectory, [string]$HipRoot,
[string]$PackageRoot, [string]$OutputDirectory, [int]$Windows = 12, [string]$PackageRoot, [string]$OutputDirectory, [int]$Windows = 12,
[Parameter(Mandatory=$true)][string]$DlssDll, [int]$PtxWindows = 2) [Parameter(Mandatory=$true)][string]$DlssDll, [int]$PtxWindows = 2)
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' } if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' } 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')) } if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/k-real-reference-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) }
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$previousPython = $env:PYTHONPATH $previousPython = $env:PYTHONPATH
@@ -23,7 +25,7 @@ try {
New-Item -ItemType Directory -Force $folder | Out-Null New-Item -ItemType Directory -Force $folder | Out-Null
$env:D4R_DIAG_DIR = $folder $env:D4R_DIAG_DIR = $folder
& (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --hip-root $HipRoot ` & (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 ` --fixture-dir $capture.FullName --output-dir $output --iterations 1 `
> (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log') > (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log')
if ($LASTEXITCODE) { throw "K $layer actual GPU replay failed: $folder" } if ($LASTEXITCODE) { throw "K $layer actual GPU replay failed: $folder" }
+4 -2
View File
@@ -7,15 +7,17 @@ param(
[int]$Iterations = 32 [int]$Iterations = 32
) )
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' } if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' } 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')) } if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/k-replay-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) }
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$previousPython = $env:PYTHONPATH $previousPython = $env:PYTHONPATH
$previousDiag = $env:D4R_DIAG_DIR $previousDiag = $env:D4R_DIAG_DIR
$env:PYTHONPATH = "$(Join-Path $repo '.tools/python/vendor');$previousPython" $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 { try {
if ($Iterations -lt 1 -or $Iterations -gt 10000) { throw 'Iterations must be 1..10000' } if ($Iterations -lt 1 -or $Iterations -gt 10000) { throw 'Iterations must be 1..10000' }
foreach ($layer in $Layers) { foreach ($layer in $Layers) {
@@ -29,7 +31,7 @@ try {
> (Join-Path $folder 'fixture.stdout.log') 2> (Join-Path $folder 'fixture.stderr.log') > (Join-Path $folder 'fixture.stdout.log') 2> (Join-Path $folder 'fixture.stderr.log')
if ($LASTEXITCODE) { throw "K $layer fixture generation failed; see $folder" } if ($LASTEXITCODE) { throw "K $layer fixture generation failed; see $folder" }
& (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --hip-root $HipRoot ` & (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 ` --fixture-dir $fixture --output-dir $output --iterations $Iterations `
> (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log') > (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log')
if ($LASTEXITCODE) { throw "K $layer GPU replay failed; see $folder" } if ($LASTEXITCODE) { throw "K $layer GPU replay failed; see $folder" }
+3 -1
View File
@@ -3,9 +3,11 @@ param([Parameter(Mandatory=$true)][string]$CaptureDirectory, [string]$HipRoot,
[string]$PackageRoot, [string]$OutputDirectory, [int]$Blocks = 12, [string]$PackageRoot, [string]$OutputDirectory, [int]$Blocks = 12,
[Parameter(Mandatory=$true)][string]$DlssDll, [int]$PtxBlocks = 2) [Parameter(Mandatory=$true)][string]$DlssDll, [int]$PtxBlocks = 2)
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' } if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' } 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')) } if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/m-real-reference-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) }
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$previousPython=$env:PYTHONPATH; $previousDiag=$env:D4R_DIAG_DIR $previousPython=$env:PYTHONPATH; $previousDiag=$env:D4R_DIAG_DIR
@@ -22,7 +24,7 @@ try {
New-Item -ItemType Directory -Force $folder | Out-Null New-Item -ItemType Directory -Force $folder | Out-Null
$env:D4R_DIAG_DIR = $folder $env:D4R_DIAG_DIR = $folder
& (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --hip-root $HipRoot ` & (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 ` --fixture-dir $capture.FullName --output-dir $output --iterations 1 `
> (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log') > (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log')
if ($LASTEXITCODE) { throw "M $layer actual GPU replay failed: $folder" } if ($LASTEXITCODE) { throw "M $layer actual GPU replay failed: $folder" }
+4 -2
View File
@@ -3,15 +3,17 @@ param([string]$HipRoot, [string]$PackageRoot, [string]$OutputDirectory,
[string[]]$Layers = @('enc1','enc2','enc3_tube','dec2','dec1'), [string[]]$Layers = @('enc1','enc2','enc3_tube','dec2','dec1'),
[ValidateRange(1,10000)][int]$Iterations = 32) [ValidateRange(1,10000)][int]$Iterations = 32)
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' } if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' } 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')) } if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/m-replay-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) }
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$previousPython = $env:PYTHONPATH $previousPython = $env:PYTHONPATH
$previousDiag = $env:D4R_DIAG_DIR $previousDiag = $env:D4R_DIAG_DIR
$env:PYTHONPATH = "$(Join-Path $repo '.tools/python/vendor');$previousPython" $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 { try {
foreach ($layer in $Layers) { foreach ($layer in $Layers) {
if ($layer -notmatch '^(enc[12]|enc3_tube|dec[12])$') { throw "Invalid M layer $layer" } 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') > (Join-Path $folder 'fixture.stdout.log') 2> (Join-Path $folder 'fixture.stderr.log')
if ($LASTEXITCODE) { throw "M $layer fixture generation failed: $folder" } if ($LASTEXITCODE) { throw "M $layer fixture generation failed: $folder" }
& (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --hip-root $HipRoot ` & (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 ` --fixture-dir $fixture --output-dir $output --iterations $Iterations `
> (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log') > (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log')
if ($LASTEXITCODE) { throw "M $layer GPU replay failed: $folder" } if ($LASTEXITCODE) { throw "M $layer GPU replay failed: $folder" }
+3 -1
View File
@@ -1,9 +1,11 @@
[CmdletBinding()] [CmdletBinding()]
param([string]$HipRoot, [string]$PackageRoot, [string]$ZludaRoot, [string]$OutputDirectory) param([string]$HipRoot, [string]$PackageRoot, [string]$ZludaRoot, [string]$OutputDirectory)
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' } if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' } 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 (!$ZludaRoot) { $ZludaRoot = Join-Path $repo 'dist/zluda-windows-final' }
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/native-identity-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) } if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/native-identity-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) }
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
@@ -29,7 +31,7 @@ try {
$good = Join-Path $OutputDirectory 'matching-identity' $good = Join-Path $OutputDirectory 'matching-identity'
New-Item -ItemType Directory -Force $bad,$good | Out-Null New-Item -ItemType Directory -Force $bad,$good | Out-Null
foreach ($directory in @($bad,$good)) { 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 'd4r_ptx_pattern 0000000000000000' | Set-Content -LiteralPath (Join-Path $bad 'd4r-kernels.txt') -Encoding ASCII
$env:D4R_ZLUDA_NATIVE_DIR = $bad $env:D4R_ZLUDA_NATIVE_DIR = $bad
+52 -10
View File
@@ -1,31 +1,65 @@
[CmdletBinding()] [CmdletBinding()]
param([string]$TextureRoot, [string]$OutputDirectory, [int]$Iterations=8, [string]$OutputResolution='512x288') 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' $ErrorActionPreference='Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $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 (!$OutputDirectory) { $OutputDirectory=Join-Path $repo 'test-results/k-native-texture-reference' }
if ($OutputResolution -notmatch '^([0-9]+)x([0-9]+)$') { throw 'OutputResolution must be WIDTHxHEIGHT.' } if ($OutputResolution -notmatch '^([0-9]+)x([0-9]+)$') { throw 'OutputResolution must be WIDTHxHEIGHT.' }
$outputWidth=[int]$Matches[1]; $outputHeight=[int]$Matches[2] $outputWidth=[int]$Matches[1]; $outputHeight=[int]$Matches[2]
$combined=Join-Path $OutputDirectory 'private-native' $combined=Join-Path $OutputDirectory 'private-native'
New-Item -ItemType Directory -Force $combined | Out-Null New-Item -ItemType Directory -Force $combined | Out-Null
foreach ($directory in @((Join-Path $repo 'build/native-k-gfx1201'),$TextureRoot)) { 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 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') & python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $combined (Join-Path $repo 'nvngx_dlss.dll')
if ($LASTEXITCODE) { throw 'Combined private manifest failed.' } if ($LASTEXITCODE) { throw 'Combined private manifest failed.' }
$controlNative=Join-Path $repo "build/native-k-$GpuArch"
if ($ControlTextureRoot -or $CommonTextureRoot) {
$controlNative=Join-Path $OutputDirectory 'control-private-native'
New-Item -ItemType Directory -Force $controlNative | Out-Null
foreach ($directory in @((Join-Path $repo "build/native-k-$GpuArch"),$CommonTextureRoot,$ControlTextureRoot) | Where-Object { $_ }) {
Get-ChildItem -LiteralPath $directory -Filter '*.hsaco' | Copy-Item -Destination $controlNative -Force
}
& python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $controlNative (Join-Path $repo 'nvngx_dlss.dll')
if ($LASTEXITCODE) { throw 'Control private manifest failed.' }
}
$settings=@{D4R_VALIDATE_OUTPUT='1'; D4R_PROFILE_STAGES='1'; D4R_ZLUDA_PROFILE='1'; D4R_QUIET_API='1'; $settings=@{D4R_VALIDATE_OUTPUT='1'; D4R_PROFILE_STAGES='1'; D4R_ZLUDA_PROFILE='1'; D4R_QUIET_API='1';
D4R_ZLUDA_WMMA='1'; D4R_ZLUDA_WMMA_FP8='1'; D4R_ZLUDA_WMMA_FP8_NATIVE='0';
D4R_ZLUDA_WMMA_F16_REFERENCE='1'; D4R_ZLUDA_PROFILE_DEFERRED=$null;
ZLUDA_CACHE_DIR=(Join-Path $repo 'build/zluda-cache-windows'); PYTHONPATH=(Join-Path $repo '.tools/python/vendor'); ZLUDA_CACHE_DIR=(Join-Path $repo 'build/zluda-cache-windows'); PYTHONPATH=(Join-Path $repo '.tools/python/vendor');
D4R_ZLUDA_NATIVE_DIR=$null} D4R_ZLUDA_NATIVE_DIR=$null}
$old=@{} $old=@{}
$stagedObjects=@{}
foreach ($stage in @(@{name='control'; root=$controlNative},@{name='candidate'; root=$combined})) {
$stagedObjects[$stage.name]=@{}
foreach ($object in Get-ChildItem -LiteralPath $stage.root -Filter '*.hsaco') {
Assert-D4RCodeObjectTarget $object.FullName $GpuArch
$stagedObjects[$stage.name][$object.Name]=(Get-FileHash -LiteralPath $object.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
}
}
try { try {
foreach ($key in $settings.Keys) { $old[$key]=[Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') } foreach ($key in $settings.Keys) { $old[$key]=[Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') }
foreach ($flags in @(0,11)) { foreach ($flags in @(0,11)) {
foreach ($mode in @('control','candidate')) { $variant=if ($flags -eq 11) { 'depthinv_mvlo_hdr' } else { 'depthreg_mvhi_ldr' }
$env:D4R_ZLUDA_NATIVE_DIR=if ($mode -eq 'control') { Join-Path $repo 'build/native-k-gfx1201' } else { $combined } $suffix=if ($KernelStage -eq 'output') { '_max_v2_rel' } else { '_v2_rel' }
$arguments=@{RuntimeProfile='therock'; ZludaRoot=(Join-Path $repo 'dist/zluda-windows-final'); $kernel="hiluma_engine_${KernelStage}_${variant}${suffix}"
PackageRoot=(Join-Path $repo 'dist/windows-rdna4-command-list'); NgxCore=(Join-Path $repo '_nvngx.dll'); $order=if ($CandidateFirst) { @('candidate','control') } else { @('control','candidate') }
foreach ($mode in $order) {
$env:D4R_ZLUDA_NATIVE_DIR=if ($mode -eq 'control') { $controlNative } else { $combined }
$expectedBackend=if (Test-Path -LiteralPath (Join-Path $env:D4R_ZLUDA_NATIVE_DIR ($kernel + '.hsaco'))) { 'native' } else { 'translated' }
if ($mode -eq 'candidate' -and $expectedBackend -ne 'native') { throw "Missing candidate object: $kernel" }
$arguments=@{RuntimeProfile='therock'; ZludaRoot=$ZludaRoot;
PackageRoot=$PackageRoot; NgxCore=(Join-Path $repo '_nvngx.dll');
DlssDll=(Join-Path $repo 'nvngx_dlss.dll'); NgxMode='d3d12'; NgxAbi='project-legacy'; Preset=11; DlssDll=(Join-Path $repo 'nvngx_dlss.dll'); NgxMode='d3d12'; NgxAbi='project-legacy'; Preset=11;
NgxCreateFlags=$flags; NgxOutputResolution=$OutputResolution; NgxOnly=$true; RequireNativeNetwork=$true; CommandListBackend=$true; NgxCreateFlags=$flags; NgxOutputResolution=$OutputResolution; NgxOnly=$true; RequireNativeNetwork=$true; CommandListBackend=$true;
NgxInputResolution=$InputResolution;
PixelProfile='depth-stencil'; BarrierMode='inherited-legacy'; CaptureExceptions=$true; EarlyIndirectProbe=$true; PixelProfile='depth-stencil'; BarrierMode='inherited-legacy'; CaptureExceptions=$true; EarlyIndirectProbe=$true;
Iterations=$Iterations; TimeoutSeconds=300; OptiScalerDll=(Join-Path $repo 'dist/optiscaler-windows-d4r/OptiScaler.dll'); Iterations=$Iterations; TimeoutSeconds=300; OptiScalerDll=(Join-Path $repo 'dist/optiscaler-windows-d4r/OptiScaler.dll');
OutputDirectory=(Join-Path $OutputDirectory "$mode-$flags")} OutputDirectory=(Join-Path $OutputDirectory "$mode-$flags")}
@@ -33,20 +67,28 @@ try {
if ($LASTEXITCODE) { throw "Texture $mode flags=$flags workload failed." } if ($LASTEXITCODE) { throw "Texture $mode flags=$flags workload failed." }
& python (Join-Path $repo 'tools/windows/profile_report.py') $arguments.OutputDirectory --output (Join-Path $arguments.OutputDirectory 'profile.json') & python (Join-Path $repo 'tools/windows/profile_report.py') $arguments.OutputDirectory --output (Join-Path $arguments.OutputDirectory 'profile.json')
if ($LASTEXITCODE) { throw 'Texture profile report failed.' } if ($LASTEXITCODE) { throw 'Texture profile report failed.' }
$profile=Get-Content -LiteralPath (Join-Path $arguments.OutputDirectory 'profile.json') -Raw | ConvertFrom-Json
$samples=@($profile.kernels | Where-Object { $_.kernel -eq $kernel -and $_.backend -eq $expectedBackend -and $_.phase -eq 'main' -and $_.serializing })
if ($samples.Count -ne 1 -or $samples[0].gpuMs.samples -ne $Iterations) {
throw "Missing valid serializing $KernelStage-kernel timings ($expectedBackend) for $mode flags=$flags; retain this run as correctness evidence only."
}
} }
$control=Join-Path $OutputDirectory "control-$flags"; $candidate=Join-Path $OutputDirectory "candidate-$flags" $control=Join-Path $OutputDirectory "control-$flags"; $candidate=Join-Path $OutputDirectory "candidate-$flags"
$kernel=if ($flags -eq 11) { 'hiluma_engine_output_depthinv_mvlo_hdr_max_v2_rel' } else { 'hiluma_engine_output_depthreg_mvhi_ldr_max_v2_rel' }
$stderr=Get-Content -LiteralPath (Join-Path $candidate 'd3d12-evaluate-preset-11.stderr.log') -Raw $stderr=Get-Content -LiteralPath (Join-Path $candidate 'd3d12-evaluate-preset-11.stderr.log') -Raw
$hits=[regex]::Matches($stderr, ('\[d4r-launch\] kernel="' + $kernel + '" backend=native')).Count $hits=[regex]::Matches($stderr, ('\[d4r-launch\] kernel="' + $kernel + '" backend=native')).Count
if ($hits -ne $Iterations) { throw "Texture override was not executed on every frame: $kernel hits=$hits" } if ($hits -ne $Iterations) { throw "Texture override was not executed on every frame: $kernel hits=$hits" }
& python (Join-Path $repo 'tools/windows/frame_compare.py') --reference $control --actual $candidate --width $outputWidth --height $outputHeight --exact & 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." } 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;
stagedObjects=$stagedObjects;
manifestSha256=(Get-FileHash -LiteralPath (Join-Path $TextureRoot 'd4r-kernels.txt') -Algorithm SHA256).Hash.ToLowerInvariant(); manifestSha256=(Get-FileHash -LiteralPath (Join-Path $TextureRoot 'd4r-kernels.txt') -Algorithm SHA256).Hash.ToLowerInvariant();
privateNvidiaDerivedCode=$true; source=(Get-Content -LiteralPath (Join-Path $TextureRoot 'build-info.json') -Raw | ConvertFrom-Json)} | privateNvidiaDerivedCode=$true; source=(Get-Content -LiteralPath (Join-Path $TextureRoot 'build-info.json') -Raw | ConvertFrom-Json)} |
ConvertTo-Json -Depth 12 | Set-Content -LiteralPath (Join-Path $TextureRoot 'validation.json') -Encoding UTF8 ConvertTo-Json -Depth 12 | Set-Content -LiteralPath (Join-Path $TextureRoot 'validation.json') -Encoding UTF8
Write-Host "PASS: both private K output variants executed and matched every RGB component. $OutputDirectory" Write-Host "PASS: both private K $KernelStage variants executed and matched every RGB component. $OutputDirectory"
} finally { } finally {
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') } foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
} }
+160
View File
@@ -0,0 +1,160 @@
# Reproduce numeric NGX feature discovery without launching or modifying a game.
# The private NVIDIA DLL staging directory is deleted; reports contain logs only.
[CmdletBinding()]
param([Parameter(Mandatory=$true)][string]$DiagnosticRoot,
[Parameter(Mandatory=$true)][string]$HipRoot,
[Parameter(Mandatory=$true)][string]$ZludaRoot,
[Parameter(Mandatory=$true)][string]$NativeRoot,
[Parameter(Mandatory=$true)][string]$OptiScalerDll,
[Parameter(Mandatory=$true)][string]$NgxCore,
[Parameter(Mandatory=$true)][string]$DlssDll,
[Parameter(Mandatory=$true)][string]$OutputDirectory,
[UInt64]$ApplicationId=100152211)
$ErrorActionPreference='Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
foreach ($name in @('DiagnosticRoot','HipRoot','ZludaRoot','NativeRoot','OptiScalerDll','NgxCore','DlssDll')) {
Set-Variable -Name $name -Value ((Get-Item -LiteralPath (Get-Variable -Name $name -ValueOnly)).FullName)
}
$arch=Get-D4RGpuTarget $DiagnosticRoot
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$runtime=Join-Path ([IO.Path]::GetTempPath()) ('d4r-feature-layout-'+[Guid]::NewGuid().ToString('N'))
$settings=@{HIP_PATH=$HipRoot; PATH="$HipRoot\bin;$ZludaRoot;$env:PATH";
D4R_NVAPI_BACKEND=(Join-Path $ZludaRoot 'nvapi64.dll'); ZLUDA_CUDA_LIB=(Join-Path $ZludaRoot 'nvcuda.dll');
D4R_ZLUDA_NATIVE_DIR=$NativeRoot; D4R_ZLUDA_VERBOSE='1';
D4R_ZLUDA_WMMA='1'; D4R_ZLUDA_WMMA_F16_REFERENCE='1'; D4R_ZLUDA_WMMA_FP8='1'; D4R_ZLUDA_WMMA_FP8_NATIVE='0';
D4R_VALIDATE_OUTPUT='1'; D4R_DIAG_DIR=$null; ZLUDA_LOG_DIR=$null; D4R_FORMAT_MODULE=$null;
D4R_ASYNC_INTEROP=$null; D4R_BATCH_INPUT_COPIES=$null; D4R_D3D12_COMMAND_BACKEND=$null}
$old=@{}; $results=@()
try {
foreach ($key in $settings.Keys) { $old[$key]=[Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') }
foreach ($dir in @('app','d4r','d4r/vendor')) { New-Item -ItemType Directory -Force (Join-Path $runtime $dir) | Out-Null }
$exe=Join-Path $runtime 'app/d4r_d3d12_dlss_probe.exe'
$shim=Join-Path $runtime 'd4r/_nvngx.dll'
$core=Join-Path $runtime 'd4r/vendor/_nvngx.dll'
$feature=Join-Path $runtime 'd4r/vendor/nvngx_dlss.dll'
Copy-Item -LiteralPath (Join-Path $DiagnosticRoot 'bin/d4r_d3d12_dlss_probe.exe') -Destination $exe
Copy-Item -LiteralPath (Join-Path $DiagnosticRoot 'bin/d4r_nvngx.dll') -Destination $shim
Copy-Item -LiteralPath (Join-Path $DiagnosticRoot "bin/pixel_convert_${arch}.hsaco") -Destination (Split-Path $shim)
Copy-Item -LiteralPath $NgxCore -Destination $core
Copy-Item -LiteralPath $DlssDll -Destination $feature
$opti=Join-Path $runtime 'app/OptiScaler.dll'
Copy-Item -LiteralPath $OptiScalerDll -Destination $opti
$cases=@(@{name='vendor-only-negative'; preset=11; frontend='d4r'; expectedExit=4},
@{name='optiscaler-vendor-only-negative'; preset=11; frontend='optiscaler'; expectedExit=4},
@{name='caller-dll-k'; preset=11; frontend='d4r'; expectedExit=0},
@{name='optiscaler-caller-k'; preset=11; frontend='optiscaler'; expectedExit=0},
@{name='optiscaler-caller-m'; preset=13; frontend='optiscaler'; expectedExit=0})
foreach ($case in $cases) {
$out=Join-Path $OutputDirectory $case.name
New-Item -ItemType Directory -Force $out | Out-Null
if ($case.expectedExit -eq 0) {
$feature=Join-Path $runtime 'd4r/nvngx_dlss.dll'
if (!(Test-Path -LiteralPath $feature)) { Copy-Item -LiteralPath $DlssDll -Destination $feature }
}
$env:D4R_DIAG_DIR=$out; $env:ZLUDA_LOG_DIR=Join-Path $out 'zluda-trace'
$env:D4R_FORMAT_MODULE=Join-Path $runtime "d4r/pixel_convert_${arch}.hsaco"
$module=if ($case.frontend -eq 'optiscaler') { $opti } else { $shim }
if ($case.frontend -eq 'optiscaler') {
@"
[Upscalers]
Dx12Upscaler=dlss
[FrameGen]
Enabled=false
FGInput=nofg
FGOutput=nofg
[DLSS]
Enabled=true
AllowExternalBackend=true
RenderPresetOverride=true
RenderPresetForAll=$($case.preset)
UseGenericAppIdWithDlss=false
[Libraries]
NvngxPath=$(Split-Path $shim)
NvngxDlssPath=$(Split-Path $feature)
NvngxFeaturePath=$(Split-Path $feature)
[Hotfix]
RestoreComputeSignature=false
RestoreGraphicSignature=false
ExtendedStateRestore=false
[Log]
LogToFile=true
LogLevel=0
[Menu]
DisableSplash=true
"@ | Set-Content -LiteralPath (Join-Path $runtime 'app/OptiScaler.ini') -Encoding UTF8
}
$arguments=@('--hip-root',$HipRoot,'--cuda-dll',(Join-Path $ZludaRoot 'nvcuda.dll'),
'--module',$module,'--ngx-core',$core,'--dlss-dll',$feature,
'--nvapi-dll',(Join-Path $DiagnosticRoot 'nvapi-compat/nvapi64.dll'),
'--ngx-app-id',"$ApplicationId",'--ngx-abi','driver','--ngx-frontend',$case.frontend,
'--interop-mode','command-list','--iterations','3','--preset',"$($case.preset)")
$info=New-Object Diagnostics.ProcessStartInfo
# Capture harness faults outside OptiScaler's loader hooks. Games use
# the no-debug launcher by default; this developer reproducer is separate.
$arguments=@('--output-directory',$out,'--',$exe)+$arguments
$info.FileName=Join-Path $DiagnosticRoot 'bin/d4r_debug_launcher.exe'; $info.WorkingDirectory=Split-Path $exe
$info.Arguments=(@($arguments | ForEach-Object { if ($_.Contains('"')) { throw 'Unsupported quote in test path.' }; '"'+$_+'"' }) -join ' ')
$info.UseShellExecute=$false; $info.CreateNoWindow=$true; $info.RedirectStandardOutput=$true; $info.RedirectStandardError=$true
foreach ($key in $settings.Keys) { if (![Environment]::GetEnvironmentVariable($key,'Process')) { $info.EnvironmentVariables.Remove($key) } }
$process=New-Object Diagnostics.Process; $process.StartInfo=$info
try {
[void]$process.Start(); $stdout=$process.StandardOutput.ReadToEndAsync(); $stderr=$process.StandardError.ReadToEndAsync()
if (!$process.WaitForExit(240000)) { $process.Kill(); $process.WaitForExit(); throw "NGX layout test timed out: $($case.name)" }
$log=$stdout.GetAwaiter().GetResult(); $errorLog=$stderr.GetAwaiter().GetResult()
[IO.File]::WriteAllText((Join-Path $out 'stdout.log'),$log)
[IO.File]::WriteAllText((Join-Path $out 'stderr.log'),$errorLog)
if ($process.ExitCode -ne $case.expectedExit) { throw "Unexpected exit $($process.ExitCode): $($case.name), see $out" }
if ($case.expectedExit) {
if ($log -notmatch 'D4R_CAPABILITIES available=0 feature_init=0xbad00004' -or $log -match 'D4R_FRAME ') { throw 'Old vendor-only layout did not reproduce FeatureNotFound.' }
if ($case.frontend -eq 'optiscaler' -and $errorLog -notmatch 'FAIL D3D12_DLSS D3D12 Init NGX=') { throw 'OptiScaler swallowed the numeric backend initialization failure.' }
} else {
if ($log -notmatch 'D4R_CAPABILITIES available=1 feature_init=0x00000001 needs_driver=0' -or
([regex]::Matches($log,'D3D12_OUTPUT preset=\d+ frame=\d+ finite=1')).Count -ne 3 -or
([regex]::Matches($log,'D4R_OUTPUT_VALIDATION elements=\d+ nan=0 inf=0 diagnostics_cpu_bytes=8')).Count -ne 3 -or
$log -notmatch 'PASS D3D12_DLSS .*fast_path_cpu_copies=0 frame_age=0' -or $errorLog -match 'D4R_WINDOWS_FAILURE') { throw 'Corrected feature layout did not complete finite zero-copy frames.' }
$required=@(Get-Content -LiteralPath (Join-Path $NativeRoot 'd4r-kernels.txt') | Where-Object {
if ($case.preset -eq 11) { $_ -match '^dltss_pwin_' } else { $_ -match '^rrlite_' }
} | ForEach-Object { $_.Split(' ')[0] })
if ($required.Count -ne $(if ($case.preset -eq 11) { 11 } else { 5 })) { throw 'Incomplete native manifest.' }
foreach ($kernel in $required) {
if ($errorLog -notmatch ('\[d4r-launch\] kernel="'+[regex]::Escape($kernel)+'" backend=native') -or
$errorLog -match ('\[d4r-launch\] kernel="'+[regex]::Escape($kernel)+'" backend=translated')) { throw "Missing native network execution: $kernel" }
}
if ($case.frontend -eq 'd4r' -and $log -notmatch 'D3D12_REQUIREMENTS warp_supported=0 enumeration_success=1') { throw 'WARP adapter enumeration regression failed.' }
}
$results+=@{case=$case.name; passed=$true; expectedExit=$case.expectedExit; application=$ApplicationId; preset=$case.preset; architecture=$arch}
Write-Host "PASS NGX_FEATURE_LAYOUT $($case.name)"
} finally {
if ($case.frontend -eq 'optiscaler') {
Get-ChildItem -LiteralPath (Split-Path $exe) -Filter '*.log' -File | ForEach-Object {
Copy-Item -LiteralPath $_.FullName -Destination $out -Force
}
}
$process.Dispose()
}
}
for ($frame=0; $frame -lt 3; $frame++) {
$direct=[IO.File]::ReadAllBytes((Join-Path $OutputDirectory "caller-dll-k/output-$frame.rgba16f"))
$frontend=[IO.File]::ReadAllBytes((Join-Path $OutputDirectory "optiscaler-caller-k/output-$frame.rgba16f"))
if ($direct.Length -ne $frontend.Length -or $direct.Length -ne 512*288*8) { throw 'RGB comparison dimensions differ.' }
for ($pixel=0; $pixel -lt $direct.Length; $pixel+=8) {
if (([BitConverter]::ToUInt64($direct,$pixel) -band [UInt64]0xffffffffffff) -ne
([BitConverter]::ToUInt64($frontend,$pixel) -band [UInt64]0xffffffffffff)) { throw "K frontend RGB mismatch: frame=$frame offset=$pixel" }
}
}
$results+=@{case='k-direct-optiscaler-rgb'; passed=$true; frames=3; rgbByteExact=$true; maxAbsoluteError=0; maxRelativeError=0}
Write-Host 'PASS NGX_FEATURE_LAYOUT k_direct_optiscaler_rgb exact=1 max_abs=0 max_rel=0'
} finally {
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
$results | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'feature-layout.json') -Encoding UTF8
$resolved=[IO.Path]::GetFullPath($runtime)
$temp=[IO.Path]::GetFullPath([IO.Path]::GetTempPath()).TrimEnd('\')+'\'
if (!$resolved.StartsWith($temp,[StringComparison]::OrdinalIgnoreCase) -or [IO.Path]::GetFileName($resolved) -notmatch '^d4r-feature-layout-[0-9a-f]{32}$') { throw 'Refusing cleanup outside private NGX staging.' }
if (Test-Path -LiteralPath $resolved) {
foreach ($attempt in 1..10) {
try { Remove-Item -LiteralPath $resolved -Recurse -Force; break }
catch { if ($attempt -eq 10) { throw }; Start-Sleep -Milliseconds 300 }
}
}
}
+92
View File
@@ -0,0 +1,92 @@
# Drag-in player mode checks. Builds with the vendored llvm-mingw; needs no
# NVIDIA DLLs and launches no GPU kernels. The loader case enumerates HIP
# devices and is skipped (reported, not passed) without a supported AMD GPU.
[CmdletBinding()]
param(
[string]$CompilerRoot, [string]$HipRoot, [string]$OutputDirectory
)
$ErrorActionPreference='Stop'
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$CompilerRoot) { $CompilerRoot=Join-Path $repo '.tools/llvm-mingw-20260922-ucrt-x86_64' }
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/player-config/'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$cxx=Join-Path $CompilerRoot 'bin/x86_64-w64-mingw32-clang++.exe'
if (!(Test-Path -LiteralPath $cxx)) { throw "llvm-mingw not found: $cxx" }
if (!(Test-Path -LiteralPath (Join-Path $HipRoot 'include/hip/hip_runtime_api.h'))) { throw "HIP headers not found under $HipRoot" }
# Same generated target table as CMakeLists.txt.
$targets=(Get-Content -LiteralPath (Join-Path $repo 'tools/windows/gpu-targets.json') -Raw | ConvertFrom-Json).targets
$generated=Join-Path $OutputDirectory 'generated'
New-Item -ItemType Directory -Force $generated | Out-Null
$entries=($targets | ForEach-Object { " GpuTarget{`"$($_.architecture)`", $($_.elfMachine), $($_.isaMajor), `"$($_.family)`"}," }) -join "`n"
@('#pragma once','#include <array>','namespace d4r::diag {',
'struct GpuTarget { const char* architecture; unsigned elf_machine; unsigned isa_major; const char* family; };',
"inline constexpr std::array<GpuTarget, $($targets.Count)> gpu_targets = {{",$entries,'}};','}') |
Set-Content -LiteralPath (Join-Path $generated 'gpu_targets.generated.h') -Encoding ASCII
$common=@('-std=c++17','-O1','-static','-DWIN32_LEAN_AND_MEAN','-DNOMINMAX','-D__HIP_PLATFORM_AMD__','-DD4R_TARGET_ARCH="gfx1201"',
'-I',$generated,'-I',(Join-Path $repo 'tools/windows'),'-I',(Join-Path $repo 'tools'),'-I',(Join-Path $HipRoot 'include'))
# Windows PowerShell turns native stderr into terminating errors under Stop;
# judge native tools by exit code and keep their output in the log.
function Invoke-Native([string]$Log,[string]$Exe,[string[]]$Arguments) {
$ErrorActionPreference='Continue'
& $Exe @Arguments *> $Log
return $LASTEXITCODE
}
function Build([string]$Name,[string[]]$Arguments) {
$log=Join-Path $OutputDirectory "build-$Name.log"
# PowerShell 5 / Legacy native passing strips embedded quotes. PowerShell
# 7's Standard/Windows passing preserves them and must not double-escape.
if ($PSVersionTable.PSVersion.Major -le 5 -or $PSNativeCommandArgumentPassing -eq 'Legacy') {
$Arguments=@($Arguments | ForEach-Object { $_.Replace('"','\"') })
}
if (Invoke-Native $log $cxx $Arguments) { throw "Build failed: $Name; see $log" }
}
$results=@()
# 1. d4r.ini validation and DLL pin decisions (the shim's own header).
$probe=Join-Path $OutputDirectory 'player_config_probe.exe'
Build 'player-config' ($common+@('-municode',(Join-Path $repo 'tools/windows/player_config_probe.cpp'),'-o',$probe,'-lversion','-ldbghelp','-lbcrypt'))
if (Invoke-Native (Join-Path $OutputDirectory 'player-config.log') $probe @((Join-Path $OutputDirectory 'pins-fixture'))) { throw "player_config_probe failed; see $(Join-Path $OutputDirectory 'player-config.log')" }
$results+=@{case='d4r-ini-and-dll-pins'; passed=$true}
# 2. Loader: GPU-free marker, LUID-based target selection, forwarding, and a
# clear failure when the folder has no build for the detected GPU.
$folder=Join-Path $OutputDirectory 'game/d4r'
New-Item -ItemType Directory -Force (Join-Path $folder 'hip/bin') | Out-Null
Build 'loader' ($common+@('-shared',(Join-Path $repo 'tools/windows/d4r_nvngx_loader.cpp'),'-o',(Join-Path $folder '_nvngx.dll'),'-lversion','-ldbghelp'))
$harness=Join-Path $OutputDirectory 'loader_selection_probe.exe'
Build 'harness' @('-municode','-static',(Join-Path $repo 'tools/windows/loader_selection_probe.cpp'),'-o',$harness,'-ldxgi','-ldxguid')
$stub=Join-Path $OutputDirectory 'stub.cpp'
@('#define API extern "C" __declspec(dllexport)',
'struct Req { unsigned FeatureSupported, MinHWArchitecture; char MinOSVersion[255]; };',
'API unsigned NVSDK_NGX_D3D12_GetFeatureRequirements(void*, const void*, Req* r) { r->FeatureSupported = 0; r->MinHWArchitecture = 0x5101; return 1; }') |
Set-Content -LiteralPath $stub -Encoding ASCII
$stubDll=Join-Path $OutputDirectory 'stub.dll'
Build 'stub' @('-shared','-static',$stub,'-o',$stubDll)
foreach ($name in @('amdhip64_7.dll','amd_comgr.dll','rocm_kpack.dll')) {
$source=Join-Path $HipRoot "bin/$name"
if (Test-Path -LiteralPath $source) { Copy-Item -LiteralPath $source -Destination (Join-Path $folder 'hip/bin') }
}
$log=Join-Path $OutputDirectory 'loader-without-builds.log'
[void](Invoke-Native $log $harness @((Join-Path $folder '_nvngx.dll')))
$text=Get-Content -LiteralPath $log -Raw
if ($text -notmatch 'MARKER version=1 hip_loaded=0') { throw "Discovery marker loaded HIP or failed; see $log" }
if ($text -match 'supported=0') { throw "An adapter was reported supported without any target build; see $log" }
$gpuLog=Join-Path $folder 'd4r_nvngx.log'
$detected=$null
if (Test-Path -LiteralPath $gpuLog) { $detected=[regex]::Match((Get-Content -LiteralPath $gpuLog -Raw),'no build for your GPU \((gfx[0-9a-f]+)\)').Groups[1].Value }
if (!$detected) {
$results+=@{case='loader-selection'; passed=$null; skipped='no supported AMD HIP device detected'}
Write-Host 'SKIP loader selection: no supported AMD HIP device detected on this machine.' -ForegroundColor Yellow
} else {
if ((Get-Content -LiteralPath $gpuLog -Raw) -notmatch 'It supports: none \(the d4r folder is incomplete') { throw "Unclear unsupported-GPU message; see $gpuLog" }
Copy-Item -LiteralPath $stubDll -Destination (Join-Path $folder "_nvngx_$detected.dll")
$log=Join-Path $OutputDirectory 'loader-with-build.log'
[void](Invoke-Native $log $harness @((Join-Path $folder '_nvngx.dll')))
if ((Get-Content -LiteralPath $log -Raw) -notmatch 'vendor=0x1002 status=0x1 supported=0 marker=0x5101') { throw "AMD adapter was not forwarded to _nvngx_$detected.dll; see $log" }
$results+=@{case='loader-selection'; passed=$true; architecture=$detected; gpuKernelsExecuted=$false}
}
$results | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
Write-Host "PASS PLAYER_CONFIG cases=$(@($results | Where-Object { $_.passed }).Count) skipped=$(@($results | Where-Object { $_.skipped }).Count) Report: $OutputDirectory"
+133
View File
@@ -0,0 +1,133 @@
# Isolated installer checks; never starts a real game or launches GPU kernels.
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)][string]$PackageRoot,
[Parameter(Mandatory=$true)][string]$NgxCore,
[Parameter(Mandatory=$true)][string]$DlssDll,
[string]$OutputDirectory
)
$ErrorActionPreference='Stop'
if ($PSVersionTable.PSVersion.Major -ge 6) {
# The fixture uses the .NET Framework C# compiler; the shipped launcher
# also runs Windows PowerShell 5. PowerShell 7 cannot emit an EXE via Add-Type.
$arguments=@('-NoProfile','-STA','-ExecutionPolicy','Bypass','-File',$PSCommandPath,
'-PackageRoot',$PackageRoot,'-NgxCore',$NgxCore,'-DlssDll',$DlssDll)
if ($OutputDirectory) { $arguments+=@('-OutputDirectory',$OutputDirectory) }
& "$env:SystemRoot/System32/WindowsPowerShell/v1.0/powershell.exe" @arguments
exit $LASTEXITCODE
}
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
$PackageRoot=(Get-Item -LiteralPath $PackageRoot).FullName
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/windows-quick-test/'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
function Invoke-Child([string]$Name,[string[]]$Arguments,[int]$ExpectedExit=0) {
$log=Join-Path $OutputDirectory "$Name.log"
& "$env:SystemRoot/System32/WindowsPowerShell/v1.0/powershell.exe" -NoProfile -STA -ExecutionPolicy Bypass `
-File (Join-Path $PackageRoot 'files/quick-test.ps1') @Arguments *> $log
if ($LASTEXITCODE -ne $ExpectedExit) { throw "Unexpected exit $LASTEXITCODE for $Name; see $log" }
}
function Fixture([string]$Name) {
$dir=Join-Path $OutputDirectory $Name
New-Item -ItemType Directory -Force $dir | Out-Null
$exe=Join-Path $dir 'Fixture.exe'
Set-Content -LiteralPath $exe 'installer fixture; do not execute' -Encoding ASCII
return $exe
}
$results=@()
$exe=Fixture ('space GPU '+[char]0x03a9+' test')
$game=Split-Path $exe
$before=@{}
foreach ($name in @('dxgi.dll','OptiScaler.ini','nvngx_dlss.dll')) {
Set-Content -LiteralPath (Join-Path $game $name) "original $name" -Encoding ASCII
$before[$name]=(Get-FileHash -LiteralPath (Join-Path $game $name)).Hash
}
Invoke-Child 'install' @('-Action','install','-GameExe',$exe,'-NgxCore',$NgxCore,'-DlssDll',$DlssDll)
if ((Get-FileHash -LiteralPath (Join-Path $game 'd4r/nvngx_dlss.dll')).Hash -ne (Get-FileHash -LiteralPath $DlssDll).Hash) {
throw 'Numeric NGX driver feature DLL was not staged beside the shim.'
}
if ((Get-FileHash -LiteralPath (Join-Path $game 'nvngx_dlss.dll')).Hash -ne $before['nvngx_dlss.dll']) { throw 'Original application DLSS DLL was changed.' }
$saved=Get-Content -LiteralPath (Join-Path $PackageRoot 'work/settings.json') -Raw | ConvertFrom-Json
if ($saved.game -ne $exe -or $saved.architecture -ne 'gfx1201') { throw 'GPU detection or remembered selection failed on this RX9070XT fixture.' }
# Restore uses remembered game selection and needs neither HIP nor NVIDIA DLLs.
Invoke-Child 'restore' @('-Action','restore')
foreach ($name in $before.Keys) { if ((Get-FileHash -LiteralPath (Join-Path $game $name)).Hash -ne $before[$name]) { throw 'Original game bytes not restored.' } }
if (Test-Path -LiteralPath (Join-Path $game 'd4r')) { throw 'Runtime directory remains after restore.' }
$results+=@{case='auto-gfx1201-install-remember-restore'; passed=$true; gpuKernelsExecuted=$false}
$manifest=Get-Content -LiteralPath (Join-Path $PackageRoot 'files/package.json') -Raw | ConvertFrom-Json
if ($manifest.bundledNvidia) {
# Bundled originals take precedence over stale remembered shim selections;
# explicit overrides still go through the existing strict negative gates.
$exe=Fixture 'bundled auto selection'
$saved.ngxCore=Join-Path $PackageRoot 'files/targets/gfx1201/d4r/_nvngx.dll'
$saved.dlssDll=$saved.ngxCore
$saved | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $PackageRoot 'work/settings.json') -Encoding UTF8
Invoke-Child 'bundled-install' @('-Action','install','-GameExe',$exe)
$game=Split-Path $exe
foreach ($pair in @(@{path='d4r/vendor/_nvngx.dll'; expected=$NgxCore},@{path='d4r/nvngx_dlss.dll'; expected=$DlssDll})) {
if ((Get-FileHash -LiteralPath (Join-Path $game $pair.path)).Hash -ne (Get-FileHash -LiteralPath $pair.expected).Hash) { throw 'Bundled original DLL selection failed.' }
}
$report=Get-ChildItem -LiteralPath (Join-Path $PackageRoot 'results') -Directory | Sort-Object Name -Descending | Select-Object -First 1
$status=Get-Content -LiteralPath (Join-Path $report.FullName 'quick-test.json') -Raw | ConvertFrom-Json
if ($status.nvidiaDllSource.core -ne 'bundled' -or $status.nvidiaDllSource.dlss -ne 'bundled') { throw 'Bundled DLL provenance is missing.' }
Invoke-Child 'bundled-restore' @('-Action','restore','-GameExe',$exe)
if (Test-Path -LiteralPath (Join-Path $game 'd4r')) { throw 'Bundled runtime directory remains after restore.' }
$results+=@{case='bundled-auto-selection-ignores-stale-choices'; passed=$true; gpuKernelsExecuted=$false}
$core=Join-Path (Join-Path $PackageRoot 'files') $manifest.bundledNvidia.ngxCore
$original=[IO.File]::ReadAllBytes($core)
try {
[IO.File]::AppendAllText($core,'tampered bundled fixture')
$exe=Fixture 'tampered bundled core'
Invoke-Child 'tampered-bundled-core' @('-Action','install','-GameExe',$exe) 1
$game=Split-Path $exe
if ((Test-Path -LiteralPath (Join-Path $game '.d4r-backup')) -or (Test-Path -LiteralPath (Join-Path $game 'dxgi.dll'))) { throw 'Tampered bundled DLL changed game files.' }
$results+=@{case='tampered-bundled-core'; passed=$true; rejectedBeforeGameChanges=$true; gpuKernelsExecuted=$false}
} finally { [IO.File]::WriteAllBytes($core,$original) }
}
# A harmless child refuses attached debuggers and produces an empty stderr.
# The default launcher must allow it to exit and must report no DLSS session.
$exe=Join-Path (Join-Path $OutputDirectory 'launcher-fixture') 'Fixture.exe'
New-Item -ItemType Directory -Force (Split-Path $exe) | Out-Null
Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
public static class D4RLauncherFixture {
[DllImport("kernel32.dll")] private static extern bool IsDebuggerPresent();
public static int Main(string[] args) { return IsDebuggerPresent() ? 91 : 0; }
}
'@ -OutputAssembly $exe -OutputType ConsoleApplication
Invoke-Child 'run-no-debug' @('-Action','run','-GameExe',$exe,'-NgxCore',$NgxCore,'-DlssDll',$DlssDll) 1
$report=Get-ChildItem -LiteralPath (Join-Path $PackageRoot 'results') -Directory | Sort-Object Name -Descending | Select-Object -First 1
$status=Get-Content -LiteralPath (Join-Path $report.FullName 'quick-test.json') -Raw | ConvertFrom-Json
$environment=Get-Content -LiteralPath (Join-Path $report.FullName 'environment.json') -Raw | ConvertFrom-Json
if ($environment.attachedDebugger -or $status.hardwareValidationCompleted -or
$status.status -ne 'inconclusive-process-exited-without-dlss-frames' -or $status.gameSummary.exitCode -ne '0x0' -or
(Get-Item -LiteralPath (Join-Path $report.FullName 'd4r.stderr.log')).Length -ne 0) {
throw 'Default launch attaches a debugger, mishandles empty stderr or falsely validates a launcher exit.'
}
Invoke-Child 'restore-launcher-fixture' @('-Action','restore','-GameExe',$exe)
$results+=@{case='run-no-debug-empty-stderr-no-false-pass'; passed=$true; gpuKernelsExecuted=$false}
foreach ($case in @('wrong-core','wrong-dlss','vkd3d')) {
$exe=Fixture $case; $game=Split-Path $exe
$core=if ($case -eq 'wrong-core') { Join-Path $PackageRoot 'files/targets/gfx1201/d4r/_nvngx.dll' } else { $NgxCore }
$dlss=if ($case -eq 'wrong-dlss') { $NgxCore } else { $DlssDll }
if ($case -eq 'vkd3d') { Set-Content -LiteralPath (Join-Path $game 'd3d12.dll') 'vkd3d-proton fixture; do not load' -Encoding ASCII }
Invoke-Child $case @('-Action','install','-GameExe',$exe,'-NgxCore',$core,'-DlssDll',$dlss) 1
if ((Test-Path -LiteralPath (Join-Path $game '.d4r-backup')) -or (Test-Path -LiteralPath (Join-Path $game 'dxgi.dll'))) { throw "$case changed game files before rejection." }
$message=Get-Content -LiteralPath (Join-Path $OutputDirectory "$case.log") -Raw
$match=[regex]::Match($message,'SEND THIS ONE FILE: (.+\.zip)')
if (!$match.Success -or !(Test-Path -LiteralPath $match.Groups[1].Value.Trim())) { throw "$case did not produce its single report ZIP." }
$results+=@{case=$case; passed=$true; rejectedBeforeGameChanges=$true; gpuKernelsExecuted=$false}
}
$readme=Join-Path $PackageRoot 'READ-ME-FIRST.txt'
$original=[IO.File]::ReadAllBytes($readme)
try {
Add-Content -LiteralPath $readme 'tampered fixture'
$exe=Fixture 'tampered-package'
Invoke-Child 'tampered-package' @('-Action','install','-GameExe',$exe,'-NgxCore',$NgxCore,'-DlssDll',$DlssDll) 1
$game=Split-Path $exe
if ((Test-Path -LiteralPath (Join-Path $game '.d4r-backup')) -or (Test-Path -LiteralPath (Join-Path $game 'dxgi.dll'))) { throw 'Tampered package changed game files.' }
$results+=@{case='tampered-package'; passed=$true; rejectedBeforeGameChanges=$true; gpuKernelsExecuted=$false}
} finally { [IO.File]::WriteAllBytes($readme,$original) }
$results | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
Write-Host "PASS QUICK_TEST_FIXTURES cases=$($results.Count) real_game_untouched=1 Report: $OutputDirectory"
+33 -15
View File
@@ -4,6 +4,7 @@ param(
[ValidateSet('stable', 'therock')][string]$RuntimeProfile = 'stable', [ValidateSet('stable', 'therock')][string]$RuntimeProfile = 'stable',
[string]$ZludaRoot, [string]$ZludaRoot,
[string]$PackageRoot, [string]$PackageRoot,
[string]$GpuArch,
[string]$OutputDirectory, [string]$OutputDirectory,
[string]$NgxCore = $env:D4R_NGX_CORE, [string]$NgxCore = $env:D4R_NGX_CORE,
[string]$DlssDll = $env:D4R_DLSS_DLL, [string]$DlssDll = $env:D4R_DLSS_DLL,
@@ -13,6 +14,7 @@ param(
[ValidateSet(5, 11, 13)][int]$Preset = 11, [ValidateSet(5, 11, 13)][int]$Preset = 11,
[ValidateSet(0,11)][int]$NgxCreateFlags = 0, [ValidateSet(0,11)][int]$NgxCreateFlags = 0,
[string]$NgxOutputResolution = '512x288', [string]$NgxOutputResolution = '512x288',
[string]$NgxInputResolution,
[switch]$NgxOnly, [switch]$NgxOnly,
[switch]$Trace, [switch]$Trace,
[switch]$RequireNativeNetwork, [switch]$RequireNativeNetwork,
@@ -25,6 +27,7 @@ param(
[int]$TimeoutSeconds = 180 [int]$TimeoutSeconds = 180
) )
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repoRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $repoRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if ($RuntimeProfile -eq 'therock') { if ($RuntimeProfile -eq 'therock') {
# Works both from scripts/windows and the installed dist/<profile> directory. # Works both from scripts/windows and the installed dist/<profile> directory.
@@ -39,6 +42,7 @@ if (!$PackageRoot) {
else { $PackageRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../../dist/windows-rdna4-diagnostics')) } else { $PackageRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../../dist/windows-rdna4-diagnostics')) }
} }
$PackageRoot = [IO.Path]::GetFullPath($PackageRoot) $PackageRoot = [IO.Path]::GetFullPath($PackageRoot)
$GpuArch=Get-D4RGpuTarget $PackageRoot $GpuArch
if (!$HipRoot) { $HipRoot = 'C:\Program Files\AMD\ROCm\7.2' } if (!$HipRoot) { $HipRoot = 'C:\Program Files\AMD\ROCm\7.2' }
$HipRoot = [IO.Path]::GetFullPath($HipRoot) $HipRoot = [IO.Path]::GetFullPath($HipRoot)
if (!$ZludaRoot) { if (!$ZludaRoot) {
@@ -67,7 +71,7 @@ foreach ($setting in @('D4R_ZLUDA_WMMA','D4R_ZLUDA_WMMA_FP8','D4R_ZLUDA_WMMA_FP8
} }
$exitStatus = 1 $exitStatus = 1
$ngxRuntimeDirectory = $null $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) { function Quote-Argument([string]$Value) {
# All generated paths are absolute file/directory paths, with no trailing backslash. # All generated paths are absolute file/directory paths, with no trailing backslash.
if ($Value.Contains('"')) { throw 'Double quotes are not allowed in arguments' } if ($Value.Contains('"')) { throw 'Double quotes are not allowed in arguments' }
@@ -90,6 +94,19 @@ function Invoke-Probe([string]$Name, [string]$Exe, [string[]]$Arguments) {
$info.CreateNoWindow = $true $info.CreateNoWindow = $true
$info.RedirectStandardOutput = $true $info.RedirectStandardOutput = $true
$info.RedirectStandardError = $true $info.RedirectStandardError = $true
# Some PowerShell/.NET hosts restore a removed variable as an empty value.
# These task switches/paths use an empty value to mean unset, whereas Rust
# presence-based diagnostic flags would otherwise enable profiling.
foreach ($key in @($info.EnvironmentVariables.Keys)) {
if ($key.StartsWith('D4R_') -and [string]::IsNullOrWhiteSpace($info.EnvironmentVariables[$key])) {
$info.EnvironmentVariables.Remove($key)
}
}
# Disabled WGP has the ordinary CU semantics. Omit a disabled/empty switch
# from child environments so older ZLUDA builds keep their existing cache.
if ([Environment]::GetEnvironmentVariable('D4R_ZLUDA_WGP','Process') -ne '1') {
$info.EnvironmentVariables.Remove('D4R_ZLUDA_WGP')
}
$process = New-Object System.Diagnostics.Process $process = New-Object System.Diagnostics.Process
$process.StartInfo = $info $process.StartInfo = $info
if (!$process.Start()) { throw "Process start failed: $Exe" } if (!$process.Start()) { throw "Process start failed: $Exe" }
@@ -118,7 +135,7 @@ try {
if (!$env:ZLUDA_CACHE_DIR) { $env:ZLUDA_CACHE_DIR = Join-Path $repoRoot 'build/zluda-cache-windows' } if (!$env:ZLUDA_CACHE_DIR) { $env:ZLUDA_CACHE_DIR = Join-Path $repoRoot 'build/zluda-cache-windows' }
$summary.zludaCacheDirectory = $env:ZLUDA_CACHE_DIR $summary.zludaCacheDirectory = $env:ZLUDA_CACHE_DIR
foreach ($setting in @('D4R_ZLUDA_WMMA','D4R_ZLUDA_WMMA_FP8','D4R_ZLUDA_WMMA_FP8_NATIVE','D4R_ZLUDA_WMMA_F16_REFERENCE')) { foreach ($setting in @('D4R_ZLUDA_WMMA','D4R_ZLUDA_WMMA_FP8','D4R_ZLUDA_WMMA_FP8_NATIVE','D4R_ZLUDA_WMMA_F16_REFERENCE')) {
if ($null -eq $originalCodegen[$setting]) { if ([string]::IsNullOrWhiteSpace($originalCodegen[$setting])) {
[Environment]::SetEnvironmentVariable($setting, $(if ($setting -eq 'D4R_ZLUDA_WMMA_FP8_NATIVE') { '0' } else { '1' }), 'Process') [Environment]::SetEnvironmentVariable($setting, $(if ($setting -eq 'D4R_ZLUDA_WMMA_FP8_NATIVE') { '0' } else { '1' }), 'Process')
} }
} }
@@ -148,10 +165,10 @@ try {
$comgr = Join-Path $HipRoot 'bin/amd_comgr_3.dll' $comgr = Join-Path $HipRoot 'bin/amd_comgr_3.dll'
if (!(Test-Path -LiteralPath $comgr)) { $comgr = Join-Path $HipRoot 'bin/amd_comgr.dll' } if (!(Test-Path -LiteralPath $comgr)) { $comgr = Join-Path $HipRoot 'bin/amd_comgr.dll' }
$files = @((Join-Path $HipRoot 'bin/amdhip64_7.dll'), $comgr, $files = @((Join-Path $HipRoot 'bin/amdhip64_7.dll'), $comgr,
(Join-Path $ZludaRoot 'nvcuda.dll'), (Join-Path $PackageRoot 'bin/probe_gfx1201.hsaco'), (Join-Path $ZludaRoot 'nvcuda.dll'), (Join-Path $PackageRoot "bin/probe_${GpuArch}.hsaco"),
(Join-Path $PackageRoot 'bin/wmma_gfx1201.hsaco'), (Join-Path $PackageRoot "bin/wmma_${GpuArch}.hsaco"),
(Join-Path $PackageRoot 'experimental/k/dltss_pwin_enc1_layer_gfx1201.hsaco'), (Join-Path $PackageRoot "experimental/k/dltss_pwin_enc1_layer_${GpuArch}.hsaco"),
(Join-Path $PackageRoot 'experimental/k/dltss_pwin_enc2_layer_gfx1201.hsaco')) (Join-Path $PackageRoot "experimental/k/dltss_pwin_enc2_layer_${GpuArch}.hsaco"))
if ($OptiScalerDll) { if ($OptiScalerDll) {
if ($NgxMode -ne 'd3d12' -or !$CommandListBackend) { throw '-OptiScalerDll requires D3D12 and -CommandListBackend' } if ($NgxMode -ne 'd3d12' -or !$CommandListBackend) { throw '-OptiScalerDll requires D3D12 and -CommandListBackend' }
if (![IO.Path]::IsPathRooted($OptiScalerDll)) { throw '-OptiScalerDll must be absolute' } if (![IO.Path]::IsPathRooted($OptiScalerDll)) { throw '-OptiScalerDll must be absolute' }
@@ -178,12 +195,12 @@ try {
$bin = Join-Path $PackageRoot 'bin' $bin = Join-Path $PackageRoot 'bin'
$hipOk = $true $hipOk = $true
if (!$NgxOnly) { $hipOk = Invoke-Probe 'hip' (Join-Path $bin 'd4r_hip_gfx1201_probe.exe') @( 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 ($hipOk) {
if (!$NgxOnly) { if (!$NgxOnly) {
$wmmaOk = Invoke-Probe 'gfx12-wmma' (Join-Path $bin 'd4r_gfx12_wmma_probe.exe') @( $wmmaOk = Invoke-Probe 'gfx12-wmma' (Join-Path $bin 'd4r_gfx12_wmma_probe.exe') @(
'--hip-root', $HipRoot, '--module', (Join-Path $bin 'wmma_gfx1201.hsaco'), '--iterations', "$Iterations") '--hip-root', $HipRoot, '--module', (Join-Path $bin "wmma_${GpuArch}.hsaco"), '--iterations', "$Iterations")
if (!$wmmaOk) { throw 'gfx12 WMMA layout validation failed; refusing to mark K/M readiness.' } if (!$wmmaOk) { throw 'gfx12 WMMA layout validation failed; refusing to mark K/M readiness.' }
$pythonCommand = Get-Command python -ErrorAction SilentlyContinue $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.' } if (!$pythonCommand) { throw 'Python 3.11+ with NumPy 2.4.6 is needed for K reference checks; run setup-windows-tools.ps1.' }
@@ -191,7 +208,7 @@ try {
$kFixture = Join-Path $OutputDirectory "k-$layer-fixture" $kFixture = Join-Path $OutputDirectory "k-$layer-fixture"
$kModuleOk = Invoke-Probe "k-$layer-gpu" (Join-Path $bin 'd4r_k_module_probe.exe') @( $kModuleOk = Invoke-Probe "k-$layer-gpu" (Join-Path $bin 'd4r_k_module_probe.exe') @(
'--hip-root', $HipRoot, '--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") '--kernel-name', $layer, '--fixture-dir', $kFixture, '--iterations', "$Iterations")
if (!$kModuleOk) { throw "K $layer GPU execution failed; see the k-$layer-gpu logs." } if (!$kModuleOk) { throw "K $layer GPU execution failed; see the k-$layer-gpu logs." }
$kReferenceOk = Invoke-Probe "k-$layer-reference" $pythonCommand.Source @( $kReferenceOk = Invoke-Probe "k-$layer-reference" $pythonCommand.Source @(
@@ -233,19 +250,19 @@ try {
'--hip-root', $HipRoot, '--module', (Join-Path $bin 'd4r_nvngx.dll'), '--interop-mode', 'indirect', '--iterations', "$Iterations") '--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.' } 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') @( $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.' } 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') @( $mapOk = Invoke-Probe 'interop-map-lifetime' (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @(
'--hip-root', $HipRoot, '--interop-mode', 'map', '--iterations', '64') '--hip-root', $HipRoot, '--interop-mode', 'map', '--iterations', '64')
$interopOk = Invoke-Probe 'interop-roundtrip' (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @( $interopOk = Invoke-Probe 'interop-roundtrip' (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @(
'--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) { if (!$mapOk -or !$interopOk) {
foreach ($mode in @('resource', 'import')) { foreach ($mode in @('resource', 'import')) {
Invoke-Probe "interop-$mode-lifetime" (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @( Invoke-Probe "interop-$mode-lifetime" (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @(
'--hip-root', $HipRoot, '--interop-mode', $mode, '--iterations', '32') | Out-Null '--hip-root', $HipRoot, '--interop-mode', $mode, '--iterations', '32') | Out-Null
} }
Invoke-Probe 'hip-stream-lifetime' (Join-Path $bin 'd4r_hip_stream_lifecycle_probe.exe') @( Invoke-Probe 'hip-stream-lifetime' (Join-Path $bin 'd4r_hip_stream_lifecycle_probe.exe') @(
'--hip-root', $HipRoot, '--module', (Join-Path $bin 'probe_gfx1201.hsaco'), '--iterations', '32') | Out-Null '--hip-root', $HipRoot, '--module', (Join-Path $bin "probe_${GpuArch}.hsaco"), '--iterations', '32') | Out-Null
} }
} }
if ($mapOk -and $interopOk) { $exitStatus = 0 } if ($mapOk -and $interopOk) { $exitStatus = 0 }
@@ -274,12 +291,12 @@ try {
# alongside the private user-supplied DLLs for this test. # alongside the private user-supplied DLLs for this test.
$localShim = Join-Path $ngxRuntimeDirectory 'd4r_nvngx.dll' $localShim = Join-Path $ngxRuntimeDirectory 'd4r_nvngx.dll'
Copy-Item -LiteralPath (Join-Path $bin 'd4r_nvngx.dll') -Destination $localShim 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) { if ($OptiScalerDll) {
$bridgeDirectory = Join-Path $ngxRuntimeDirectory 'd4r' $bridgeDirectory = Join-Path $ngxRuntimeDirectory 'd4r'
New-Item -ItemType Directory -Path $bridgeDirectory | Out-Null New-Item -ItemType Directory -Path $bridgeDirectory | Out-Null
Copy-Item -LiteralPath $localShim -Destination (Join-Path $bridgeDirectory '_nvngx.dll') 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' $localShim = Join-Path $ngxRuntimeDirectory 'OptiScaler.dll'
Copy-Item -LiteralPath $OptiScalerDll -Destination $localShim Copy-Item -LiteralPath $OptiScalerDll -Destination $localShim
$ini = @" $ini = @"
@@ -297,7 +314,7 @@ RenderPresetForAll=$Preset
UseGenericAppIdWithDlss=false UseGenericAppIdWithDlss=false
[Libraries] [Libraries]
NvngxPath=$bridgeDirectory NvngxPath=$bridgeDirectory
NvngxDlssPath=$localDlss NvngxDlssPath=$ngxRuntimeDirectory
NvngxFeaturePath=$ngxRuntimeDirectory NvngxFeaturePath=$ngxRuntimeDirectory
[Hotfix] [Hotfix]
RestoreComputeSignature=false RestoreComputeSignature=false
@@ -315,6 +332,7 @@ DisableSplash=true
} }
$ngxArguments += @('--module', $localShim, '--pixel-profile', $PixelProfile, '--barrier-mode', $BarrierMode, $ngxArguments += @('--module', $localShim, '--pixel-profile', $PixelProfile, '--barrier-mode', $BarrierMode,
'--ngx-create-flags', "$NgxCreateFlags", '--ngx-output-resolution', $NgxOutputResolution) '--ngx-create-flags', "$NgxCreateFlags", '--ngx-output-resolution', $NgxOutputResolution)
if ($NgxInputResolution) { $ngxArguments += @('--ngx-input-resolution', $NgxInputResolution) }
if ($EarlyIndirectProbe) { if ($EarlyIndirectProbe) {
if (!$CommandListBackend) { throw '-EarlyIndirectProbe requires -CommandListBackend' } if (!$CommandListBackend) { throw '-EarlyIndirectProbe requires -CommandListBackend' }
$ngxArguments += @('--early-indirect', '1') $ngxArguments += @('--early-indirect', '1')
@@ -0,0 +1,109 @@
# Drag-in release packaging with synthetic per-target inputs. No GPU, no
# NVIDIA DLLs, no game. Code objects carry real AMDGPU ELF target headers.
[CmdletBinding()]
param([string]$OutputDirectory)
$ErrorActionPreference='Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/windows-release-package/'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$targets=@(Get-D4RGpuTargets)
function Write-CodeObject([string]$Path,[int]$Machine) {
$header=New-Object byte[] 64
$header[0]=127; $header[1]=69; $header[2]=76; $header[3]=70; $header[4]=2; $header[5]=1; $header[18]=224; $header[48]=$Machine
New-Item -ItemType Directory -Force (Split-Path $Path) | Out-Null
[IO.File]::WriteAllBytes($Path,$header)
}
function New-Inputs([string]$Prefix,[scriptblock]$Mutate) {
foreach ($target in $targets) {
$arch=$target.architecture; $root="$Prefix-$arch"
$text=@{'OptiScaler.dll'='optiscaler'; 'd4r/_nvngx.dll'="shim $arch"; 'd4r/nvapi/nvapi64.dll'="nvapi $arch";
'd4r/hip/bin/amdhip64_7.dll'='hip'; 'd4r/zluda/nvcuda.dll'='zluda'; 'd4r/zluda/nvapi64.dll'='zluda nvapi'; 'licenses/d4r.txt'='license';
'd4r/d4r_gpu_inventory.exe'='inventory'; 'd4r/d4r_debug_launcher.exe'='launcher'; 'windows-game.ps1'='installer';
'd4r/native/d4r-kernels.txt'='manifest'}
foreach ($entry in $text.GetEnumerator()) {
$path=Join-Path $root $entry.Key; New-Item -ItemType Directory -Force (Split-Path $path) | Out-Null
Set-Content -LiteralPath $path $entry.Value -Encoding ASCII
}
$objects=@("d4r/pixel_convert_$arch.hsaco")+@(1..16 | ForEach-Object { "d4r/native/kernel$_.hsaco" })
foreach ($object in $objects) { Write-CodeObject (Join-Path $root $object) $target.elfMachine }
if ($Mutate) { & $Mutate $root $arch }
$files=@(Get-ChildItem -LiteralPath $root -Recurse -File | Where-Object { $_.Name -ne 'package.json' } | ForEach-Object {
@{path=$_.FullName.Substring($root.Length+1).Replace('\','/'); sha256=(Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash}
})
@{architecture=$arch; d4rWorkingTreeDirty=$false; d4rCommit='fixture'; hardwareValidation='fixture';
dlssSha256='3975567b8943c53acce397f2b72380092f84f162d00b0d2c7d08a1025c563983';
zludaBuild=@{sourceCommit='fixture'}; optiScaler=@{sourceCommit='fixture'}; hipRuntime='fixture'; files=$files} |
ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $root 'package.json') -Encoding UTF8
}
}
function Invoke-Package([string]$Prefix,[string]$Root) {
& (Join-Path $PSScriptRoot 'package-windows-release.ps1') -GamePackagePrefix $Prefix -LoaderDll $loader -PackageRoot $Root | Out-Null
}
$loader=Join-Path $OutputDirectory 'build/d4r_nvngx_loader.dll'
New-Item -ItemType Directory -Force (Split-Path $loader) | Out-Null
Set-Content -LiteralPath $loader 'loader fixture' -Encoding ASCII
$results=@()
# 1. Layout of a good release.
$prefix=Join-Path $OutputDirectory 'inputs/good/game'
New-Inputs $prefix
$release=Join-Path $OutputDirectory 'release'
Invoke-Package $prefix $release
foreach ($path in @('dxgi.dll','OptiScaler.ini','d4r/_nvngx.dll','d4r/d4r.ini','d4r/dll-pins.txt','d4r/package.json',
'd4r/hip/bin/amdhip64_7.dll','d4r/zluda/nvcuda.dll','d4r/licenses/d4r.txt','d4r/ngx/PUT _nvngx.dll HERE.txt',
'd4r/gpu-targets.json','d4r/d4r_gpu_inventory.exe','d4r/d4r-check.ps1','WINDOWS_README.txt')) {
if (!(Test-Path -LiteralPath (Join-Path $release $path))) { throw "Release lacks $path" }
}
if ((Get-Content -LiteralPath (Join-Path $release 'd4r/_nvngx.dll') -Raw).Trim() -ne 'loader fixture') { throw 'd4r/_nvngx.dll is not the loader.' }
foreach ($target in $targets) {
$arch=$target.architecture
if ((Get-Content -LiteralPath (Join-Path $release "d4r/_nvngx_$arch.dll") -Raw).Trim() -ne "shim $arch") { throw "Wrong shim for $arch" }
if ((Get-Content -LiteralPath (Join-Path $release "d4r/nvapi/$arch/nvapi64.dll") -Raw).Trim() -ne "nvapi $arch") { throw "Wrong NVAPI for $arch" }
Assert-D4RCodeObjectTarget (Join-Path $release "d4r/pixel_convert_$arch.hsaco") $arch
$native=@(Get-ChildItem -LiteralPath (Join-Path $release "d4r/native/$arch") -Filter '*.hsaco')
if ($native.Count -ne 16 -or !(Test-Path -LiteralPath (Join-Path $release "d4r/native/$arch/d4r-kernels.txt"))) { throw "Native folder for $arch is incomplete" }
foreach ($object in $native) { Assert-D4RCodeObjectTarget $object.FullName $arch }
}
if (@(Get-ChildItem -LiteralPath $release -Recurse -Filter 'd4r_gpu_inventory.exe').Count -ne 1) { throw 'The GPU query tool must ship exactly once.' }
foreach ($excluded in @('d4r/d4r_debug_launcher.exe','windows-game.ps1','d4r/native/kernel1.hsaco','d4r/nvapi/nvapi64.dll')) {
if (Test-Path -LiteralPath (Join-Path $release $excluded)) { throw "Installer-only or untargeted file shipped: $excluded" }
}
$manifest=Get-Content -LiteralPath (Join-Path $release 'd4r/package.json') -Raw | ConvertFrom-Json
foreach ($file in $manifest.files) {
if ((Get-FileHash -LiteralPath (Join-Path $release $file.path) -Algorithm SHA256).Hash -ne $file.sha256) { throw "Manifest hash wrong: $($file.path)" }
}
$shipped=@(Get-ChildItem -LiteralPath $release -Recurse -File | ForEach-Object { $_.FullName.Substring($release.Length+1).Replace('\','/') } | Where-Object { $_ -ne 'd4r/package.json' })
if (Compare-Object $shipped @($manifest.files.path)) { throw 'Manifest and shipped files differ.' }
$results+=@{case='drag-in-layout'; passed=$true; targets=$targets.Count; files=$shipped.Count}
# 1b. A subset release carries only the requested targets.
$subset=Join-Path $OutputDirectory 'release-subset'
& (Join-Path $PSScriptRoot 'package-windows-release.ps1') -GamePackagePrefix $prefix -LoaderDll $loader -PackageRoot $subset -Architectures gfx1101,gfx1201 | Out-Null
$kept=@(Get-ChildItem -LiteralPath (Join-Path $subset 'd4r') -Filter '_nvngx_gfx*.dll' | ForEach-Object { $_.Name })
if (($kept | Sort-Object) -join ',' -ne '_nvngx_gfx1101.dll,_nvngx_gfx1201.dll') { throw "Subset shipped the wrong shims: $($kept -join ',')" }
$rejected=$false
try { & (Join-Path $PSScriptRoot 'package-windows-release.ps1') -GamePackagePrefix $prefix -LoaderDll $loader -PackageRoot (Join-Path $OutputDirectory 'release-unknown') -Architectures gfx9999 | Out-Null } catch { $rejected=$_.Exception.Message -match 'Unknown GPU target' }
if (!$rejected) { throw 'An unknown architecture was not rejected.' }
$results+=@{case='architecture-subset'; passed=$true; shipped=$kept}
# 2. A code object for another GPU in a target's input is rejected.
$prefix=Join-Path $OutputDirectory 'inputs/mixed/game'
New-Inputs $prefix { param($root,$arch) if ($arch -eq 'gfx1101') { Write-CodeObject (Join-Path $root 'd4r/native/kernel3.hsaco') 65 } }
$rejected=$false
try { Invoke-Package $prefix (Join-Path $OutputDirectory 'release-mixed') } catch { $rejected=$_.Exception.Message -match 'does not target gfx1101' }
if (!$rejected) { throw 'A gfx1100 code object in the gfx1101 input was not rejected.' }
$results+=@{case='mixed-target-rejected'; passed=$true}
# 3. Shared runtime files must be identical across targets.
$prefix=Join-Path $OutputDirectory 'inputs/runtime/game'
New-Inputs $prefix { param($root,$arch) if ($arch -eq 'gfx1200') { Set-Content -LiteralPath (Join-Path $root 'd4r/zluda/nvcuda.dll') 'other zluda' -Encoding ASCII } }
$rejected=$false
try { Invoke-Package $prefix (Join-Path $OutputDirectory 'release-runtime') } catch { $rejected=$_.Exception.Message -match 'Shared runtime differs' }
if (!$rejected) { throw 'A differing shared runtime was not rejected.' }
$results+=@{case='shared-runtime-mismatch-rejected'; passed=$true}
$results | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
Write-Host "PASS WINDOWS_RELEASE_PACKAGE cases=$($results.Count) Report: $OutputDirectory"
+24
View File
@@ -0,0 +1,24 @@
[CmdletBinding()]
param([string]$HipRoot, [string]$PackageRoot, [string]$OutputDirectory)
$ErrorActionPreference='Stop'
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
if (!$PackageRoot) { $PackageRoot=Join-Path $repo 'dist/windows-rdna4-command-list' }
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/wmma-f16-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
$compiler=Join-Path $HipRoot 'lib/llvm/bin/clang++.exe'
if (!(Test-Path -LiteralPath $compiler)) { $compiler=Join-Path $HipRoot 'bin/clang++.exe' }
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$module=Join-Path $OutputDirectory 'wmma_f16_compare.hsaco'
& $compiler -x hip -std=c++17 --offload-arch=gfx1201 --offload-device-only --no-gpu-bundle-output `
-mno-wavefrontsize64 -nogpuinc -nogpulib -O3 (Join-Path $repo 'tools/windows/wmma_f16_compare.hip') -o $module
if ($LASTEXITCODE) { throw 'WMMA arithmetic probe compilation failed.' }
$previousPython=$env:PYTHONPATH
try {
$env:PYTHONPATH="$(Join-Path $repo '.tools/python/vendor');$previousPython"
& python.exe (Join-Path $repo 'tools/windows/wmma_f16_compare.py') `
--probe (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --module $module `
--hip-root $HipRoot --output-dir $OutputDirectory
if ($LASTEXITCODE) { throw "WMMA arithmetic diagnostic failed; logs: $OutputDirectory" }
} finally { $env:PYTHONPATH=$previousPython }
# A successful diagnostic is not acceptance of native FP16 accumulators for K.
# Read baselineEquivalent/gameOptimizationAccepted in validation.json.
@@ -0,0 +1,67 @@
[CmdletBinding()]
param([string]$ZludaRoot, [string[]]$GpuArchitectures, [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-final' }
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo 'test-results/zluda-target-compilation' }
if (!$GpuArchitectures) { $GpuArchitectures=@(Get-D4RGpuTargets | ForEach-Object { $_.architecture }) }
foreach ($arch in $GpuArchitectures) { [void](Get-D4RGpuTargetInfo $arch) }
$emitter=Join-Path $ZludaRoot 'd4r_emit.exe'
if (!(Test-Path -LiteralPath $emitter)) { throw "Rebuild ZLUDA and its offline emitter: $emitter" }
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$settings=@{PATH="$ZludaRoot;$env:PATH"; D4R_ZLUDA_WMMA='1'; D4R_ZLUDA_WMMA_FP8='1';
D4R_ZLUDA_WMMA_F16_REFERENCE='1'; D4R_ZLUDA_WMMA_FP8_NATIVE='0'; D4R_ZLUDA_WMMA_LAYOUT=$null;
D4R_ZLUDA_WAVE64=$null; D4R_ZLUDA_FAST_MATH=$null; D4R_ZLUDA_IGNORE_DENORMAL=$null;
D4R_ZLUDA_WMMA_F32ACC=$null; D4R_ZLUDA_EXTRA_BC=$null; D4R_ZLUDA_WGP=$null}
$old=@{}; $rows=[Collections.Generic.List[object]]::new()
try {
foreach ($key in $settings.Keys) {
$old[$key]=[Environment]::GetEnvironmentVariable($key,'Process')
if ($null -eq $settings[$key]) { Remove-Item -LiteralPath "Env:$key" -ErrorAction SilentlyContinue }
else { [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') }
}
foreach ($fixture in @(@{name='pattern'; source='cuda_driver_probe.cpp'; reference='1'},
@{name='f16-mma-reference'; source='cuda_mma_probe.cpp'; reference='1'},
@{name='f16-mma-wmma'; source='cuda_mma_probe.cpp'; reference='0'})) {
$env:D4R_ZLUDA_WMMA_F16_REFERENCE=$fixture.reference
# Reuse the public synthetic PTX exercised by the hardware diagnostics.
# Reading source does not execute a probe or load HIP/NVIDIA libraries.
$match=[regex]::Match((Get-Content (Join-Path $repo ('tools/windows/'+$fixture.source)) -Raw),'(?s)R"PTX\((.*?)\)PTX"')
if (!$match.Success) { throw 'Public PTX fixture not found.' }
$ptx=Join-Path $OutputDirectory ($fixture.name+'.ptx')
$match.Groups[1].Value | Set-Content -LiteralPath $ptx -Encoding ASCII
foreach ($arch in $GpuArchitectures) {
$output=Join-Path $OutputDirectory ($arch+'/'+$fixture.name)
New-Item -ItemType Directory -Force $output | Out-Null
$log=Join-Path $output 'compile.log'
$oldPreference=$ErrorActionPreference
try { $ErrorActionPreference='Continue'; & $emitter $ptx $output $arch *> $log; $code=$LASTEXITCODE }
finally { $ErrorActionPreference=$oldPreference }
if ($code) { throw "Offline ZLUDA compilation failed: $arch $($fixture.name), exit=$code; see $log" }
Assert-D4RCodeObjectTarget (Join-Path $output 'module.hsaco') $arch
$text=Get-Content -LiteralPath $log -Raw
if ($text -match 'not a recognized processor|ignoring processor|unsupported processor') { throw "Target rejected by compiler: $log" }
$asm=Get-Content (Join-Path $output 'asm.s') -Raw
if ($asm -notmatch '\.amdhsa_wavefront_size32\s+1') { throw "Missing wave32 code generation: $output" }
$hasWmma=($asm -match 'v_wmma_')
if (($fixture.name -eq 'f16-mma-wmma' -or ($fixture.name -eq 'f16-mma-reference' -and $arch.StartsWith('gfx11'))) -and !$hasWmma) {
throw "WMMA lowering missing: $output"
}
# gfx12 strict F16 accumulation intentionally uses the existing
# double-precision reference helper. Do not label it native WMMA.
if ($fixture.name -eq 'f16-mma-reference' -and $arch.StartsWith('gfx12') -and
($hasWmma -or $asm -notmatch 'v_fma_f64')) { throw "Strict gfx12 reference lowering changed: $output" }
$rows.Add(@{architecture=$arch; fixture=$fixture.name; compiled=$true; elfTargetCorrect=$true;
nativeWmma=$hasWmma; hardwareExecuted=$false})
Write-Host "PASS ZLUDA_OFFLINE target=$arch fixture=$($fixture.name) wave=32 hardware_executed=0"
}
}
@{passed=$true; fixtures=@($rows); emitterSha256=(Get-FileHash -LiteralPath $emitter).Hash; gpuWorkExecuted=$false} |
ConvertTo-Json -Depth 6 | Set-Content (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
} finally {
foreach ($key in $old.Keys) {
if ($null -eq $old[$key]) { Remove-Item -LiteralPath "Env:$key" -ErrorAction SilentlyContinue }
else { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
}
}
+54 -26
View File
@@ -11,18 +11,27 @@ param(
[switch]$ValidateOutput, [switch]$ValidateOutput,
[switch]$ProfileStages, [switch]$ProfileStages,
[switch]$ProfileKernels, [switch]$ProfileKernels,
[switch]$ProfileKernelsDeferred,
[switch]$ProfileLegacyStream,
[ValidateRange(1,1000000)][int]$KernelProfileEvery = 17,
[switch]$ProfileCommandHooks, [switch]$ProfileCommandHooks,
[switch]$ProfileCudaApi, [switch]$ProfileCudaApi,
[switch]$ProfileGpuBoundary, [switch]$ProfileGpuBoundary,
[switch]$UncachedInteropLists, [switch]$UncachedInteropLists,
[switch]$AsyncInterop, [switch]$AsyncInterop,
[switch]$BatchInputCopies,
[switch]$VerboseRuntime, [switch]$VerboseRuntime,
[switch]$CaptureExceptions, [switch]$CaptureExceptions,
[string[]]$GameArguments = @('-dx12'), [string[]]$GameArguments = @('-dx12'),
[string]$OutputDirectory [string]$OutputDirectory
) )
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
. (Join-Path $PSScriptRoot 'gpu-preflight.ps1')
. (Join-Path $PSScriptRoot 'game-summary.ps1')
if ($ProfileGpuBoundary -and !$AsyncInterop) { throw '-ProfileGpuBoundary requires -AsyncInterop.' } 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.' }
$package = [IO.Path]::GetFullPath($PSScriptRoot) $package = [IO.Path]::GetFullPath($PSScriptRoot)
$GameExe = (Get-Item -LiteralPath $GameExe -ErrorAction Stop).FullName $GameExe = (Get-Item -LiteralPath $GameExe -ErrorAction Stop).FullName
$game = Split-Path $GameExe $game = Split-Path $GameExe
@@ -61,15 +70,18 @@ if (!$NgxCore -or !$DlssDll) { throw 'Pass both local NVIDIA DLL paths with -Ngx
$NgxCore = (Get-Item -LiteralPath $NgxCore).FullName $NgxCore = (Get-Item -LiteralPath $NgxCore).FullName
$DlssDll = (Get-Item -LiteralPath $DlssDll).FullName $DlssDll = (Get-Item -LiteralPath $DlssDll).FullName
$metadata = Get-Content -LiteralPath (Join-Path $package 'package.json') -Raw | ConvertFrom-Json $metadata = Get-Content -LiteralPath (Join-Path $package 'package.json') -Raw | ConvertFrom-Json
$GpuArch=Get-D4RGpuTarget $package $metadata.architecture
foreach ($file in $metadata.files) { 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 (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.' } 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.' }
Invoke-D4RGpuPreflight $package $game $GpuArch (Join-Path $package ('results/preflight-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')))
$textureFiles=@(); $textureManifest=@() $textureFiles=@(); $textureManifest=@()
if ($LocalTextureKernels) { if ($LocalTextureKernels) {
$LocalTextureKernels=(Get-Item -LiteralPath $LocalTextureKernels -ErrorAction Stop).FullName $LocalTextureKernels=(Get-Item -LiteralPath $LocalTextureKernels -ErrorAction Stop).FullName
$validation=Get-Content -LiteralPath (Join-Path $LocalTextureKernels 'validation.json') -Raw | ConvertFrom-Json $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) { $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.' throw 'Local texture kernels must pass test-native-texture.ps1 with the exact packaged DLSS identity.'
} }
@@ -79,6 +91,7 @@ if ($LocalTextureKernels) {
} }
$path=Join-Path $LocalTextureKernels $object.Name $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.' } 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 $textureFiles+=Get-Item -LiteralPath $path
} }
if ($textureFiles.Count -ne 2) { throw 'Expected both validated local K output variants.' } if ($textureFiles.Count -ne 2) { throw 'Expected both validated local K output variants.' }
@@ -117,7 +130,12 @@ function InstallFile([string]$source, [string]$relative) {
$manifest | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath $manifestPath -Encoding UTF8 $manifest | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath $manifestPath -Encoding UTF8
} }
InstallFile (Join-Path $package 'OptiScaler.dll') 'dxgi.dll' 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) { if ($LocalTextureKernels) {
foreach ($file in $textureFiles) { InstallFile $file.FullName ('d4r/native/' + $file.Name) } foreach ($file in $textureFiles) { InstallFile $file.FullName ('d4r/native/' + $file.Name) }
$combinedManifest=Join-Path $backup 'generated-native-manifest.txt' $combinedManifest=Join-Path $backup 'generated-native-manifest.txt'
@@ -127,7 +145,10 @@ if ($LocalTextureKernels) {
} }
# Private local test copies only; these are never added to the public package. # Private local test copies only; these are never added to the public package.
InstallFile $NgxCore 'd4r/vendor/_nvngx.dll' InstallFile $NgxCore 'd4r/vendor/_nvngx.dll'
InstallFile $DlssDll 'd4r/vendor/nvngx_dlss.dll' # The numeric driver Init ABI searches the calling module's directory; it does
# not accept FeatureCommonInfo. Keep the exact local feature DLL alongside the
# d4r shim. Putting it only beside the core in vendor/ produces FeatureNotFound.
InstallFile $DlssDll 'd4r/nvngx_dlss.dll'
$bridge = GamePath 'd4r' $bridge = GamePath 'd4r'
$vendor = GamePath 'd4r/vendor' $vendor = GamePath 'd4r/vendor'
$ini = @" $ini = @"
@@ -145,8 +166,8 @@ RenderPresetForAll=$Preset
UseGenericAppIdWithDlss=false UseGenericAppIdWithDlss=false
[Libraries] [Libraries]
NvngxPath=$bridge NvngxPath=$bridge
NvngxDlssPath=$vendor\nvngx_dlss.dll NvngxDlssPath=$bridge
NvngxFeaturePath=$vendor NvngxFeaturePath=$bridge
[Hotfix] [Hotfix]
RestoreComputeSignature=false RestoreComputeSignature=false
RestoreGraphicSignature=false RestoreGraphicSignature=false
@@ -170,23 +191,28 @@ $settings = @{
D4R_HIP_ROOT=(GamePath 'd4r/hip'); HIP_PATH=(GamePath 'd4r/hip'); D4R_HIP_ROOT=(GamePath 'd4r/hip'); HIP_PATH=(GamePath 'd4r/hip');
D4R_NVCUDA_DLL=(GamePath 'd4r/zluda/nvcuda.dll'); ZLUDA_CUDA_LIB=(GamePath 'd4r/zluda/nvcuda.dll'); 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_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_NGX_CORE=(GamePath 'd4r/vendor/_nvngx.dll'); D4R_DLSS_DLL=(GamePath 'd4r/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_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'; 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 # Do not inherit relaxation flags from unrelated experiments. Unset and
# false have the same numerical effect; unset reuses the validated cache. # false have the same numerical effect; unset reuses the validated cache.
D4R_ZLUDA_IGNORE_DENORMAL=$null; D4R_ZLUDA_FAST_MATH=$null; D4R_ZLUDA_IGNORE_DENORMAL=$null; D4R_ZLUDA_FAST_MATH=$null;
D4R_ZLUDA_WMMA_F32ACC=$null; D4R_ZLUDA_WAVE64=$null; D4R_ZLUDA_WMMA_F32ACC=$null; D4R_ZLUDA_WAVE64=$null;
D4R_ZLUDA_WMMA_LAYOUT=$null;
D4R_QUIET_API=$(if ($VerboseRuntime) { $null } else { '1' }); D4R_QUIET_API=$(if ($VerboseRuntime) { $null } else { '1' });
D4R_VALIDATE_OUTPUT=$(if ($ValidateOutput) { '1' } else { $null }); D4R_VALIDATE_OUTPUT=$(if ($ValidateOutput) { '1' } else { $null });
D4R_PROFILE_STAGES=$(if ($ProfileStages) { '1' } else { $null }); D4R_PROFILE_STAGES=$(if ($ProfileStages) { '1' } else { $null });
D4R_ZLUDA_PROFILE=$(if ($ProfileKernels) { '1' } else { $null }); D4R_ZLUDA_PROFILE=$(if ($ProfileKernels) { '1' } else { $null });
D4R_ZLUDA_PROFILE_DEFERRED=$(if ($ProfileKernelsDeferred) { '1' } else { $null });
D4R_ZLUDA_PROFILE_ALLOW_LEGACY=$(if ($ProfileLegacyStream) { '1' } else { $null });
D4R_ZLUDA_PROFILE_EVERY=$(if ($ProfileKernelsDeferred) { "$KernelProfileEvery" } else { $null });
D4R_PROFILE_COMMAND_HOOKS=$(if ($ProfileCommandHooks) { '1' } else { $null }); D4R_PROFILE_COMMAND_HOOKS=$(if ($ProfileCommandHooks) { '1' } else { $null });
D4R_ZLUDA_PROFILE_API=$(if ($ProfileCudaApi) { '1' } else { $null }); D4R_ZLUDA_PROFILE_API=$(if ($ProfileCudaApi) { '1' } else { $null });
D4R_PROFILE_GPU_BOUNDARY=$(if ($ProfileGpuBoundary) { '1' } else { $null }); D4R_PROFILE_GPU_BOUNDARY=$(if ($ProfileGpuBoundary) { '1' } else { $null });
D4R_DISABLE_INTEROP_LIST_CACHE=$(if ($UncachedInteropLists) { '1' } else { $null }); D4R_DISABLE_INTEROP_LIST_CACHE=$(if ($UncachedInteropLists) { '1' } else { $null });
D4R_ASYNC_INTEROP=$(if ($AsyncInterop) { '1' } else { $null }); D4R_ASYNC_INTEROP=$(if ($AsyncInterop) { '1' } else { $null });
D4R_BATCH_INPUT_COPIES=$(if ($BatchInputCopies) { '1' } else { $null });
D4R_DIAG_DIR=$OutputDirectory; ZLUDA_LOG_DIR=(Join-Path $OutputDirectory 'zluda-trace'); D4R_DIAG_DIR=$OutputDirectory; ZLUDA_LOG_DIR=(Join-Path $OutputDirectory 'zluda-trace');
ZLUDA_CACHE_DIR=$CacheDirectory; PATH=((GamePath 'd4r/hip/bin') + ';' + (GamePath 'd4r/zluda') + ';' + $env:PATH) ZLUDA_CACHE_DIR=$CacheDirectory; PATH=((GamePath 'd4r/hip/bin') + ';' + (GamePath 'd4r/zluda') + ';' + $env:PATH)
} }
@@ -222,7 +248,12 @@ try {
Write-Host "Temporary diagnostic resolution: $DiagnosticResolution; original settings restore at exit." Write-Host "Temporary diagnostic resolution: $DiagnosticResolution; original settings restore at exit."
} }
foreach ($key in $settings.Keys) { $old[$key] = [Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') } foreach ($key in $settings.Keys) { $old[$key] = [Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') }
@{preset=$Preset; game=$GameExe; package=$metadata; attachedDebugger=[bool]$CaptureExceptions; driver=@(Get-CimInstance Win32_VideoController | Select-Object Name,DriverVersion,DriverDate); arguments=$GameArguments} | @{preset=$Preset; game=$GameExe; package=$metadata; attachedDebugger=[bool]$CaptureExceptions;
diagnostics=@{asyncInterop=[bool]$AsyncInterop; batchInputCopies=[bool]$BatchInputCopies; validateOutput=[bool]$ValidateOutput;
profileGpuBoundary=[bool]$ProfileGpuBoundary; profileStages=[bool]$ProfileStages; profileCudaApi=[bool]$ProfileCudaApi;
profileKernels=[bool]$ProfileKernels; profileKernelsDeferred=[bool]$ProfileKernelsDeferred;
profileLegacyStream=[bool]$ProfileLegacyStream; kernelProfileEvery=$KernelProfileEvery};
driver=@(Get-CimInstance Win32_VideoController | Select-Object Name,DriverVersion,DriverDate); arguments=$GameArguments} |
ConvertTo-Json -Depth 12 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'environment.json') -Encoding UTF8 ConvertTo-Json -Depth 12 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'environment.json') -Encoding UTF8
function Quote([string]$value) { if ($value.Contains('"')) { throw 'A command argument contains an unsupported quote.' }; return '"' + $value + '"' } function Quote([string]$value) { if ($value.Contains('"')) { throw 'A command argument contains an unsupported quote.' }; return '"' + $value + '"' }
$arguments = @('--output-directory', (Quote $OutputDirectory)) $arguments = @('--output-directory', (Quote $OutputDirectory))
@@ -236,6 +267,16 @@ try {
$info.WorkingDirectory = $game $info.WorkingDirectory = $game
$info.UseShellExecute = $false; $info.CreateNoWindow = $true $info.UseShellExecute = $false; $info.CreateNoWindow = $true
$info.RedirectStandardOutput = $true; $info.RedirectStandardError = $true $info.RedirectStandardOutput = $true; $info.RedirectStandardError = $true
# Empty task switches mean unset. Some PowerShell/.NET hosts preserve empty
# values, which would enable Rust's presence-based diagnostic flags.
foreach ($key in @($info.EnvironmentVariables.Keys)) {
if ($key.StartsWith('D4R_') -and [string]::IsNullOrWhiteSpace($info.EnvironmentVariables[$key])) {
$info.EnvironmentVariables.Remove($key)
}
}
if ([Environment]::GetEnvironmentVariable('D4R_ZLUDA_WGP','Process') -ne '1') {
$info.EnvironmentVariables.Remove('D4R_ZLUDA_WGP')
}
$process = [Diagnostics.Process]::new(); $process.StartInfo = $info $process = [Diagnostics.Process]::new(); $process.StartInfo = $info
if (!$process.Start()) { throw 'Game diagnostic launch failed.' } if (!$process.Start()) { throw 'Game diagnostic launch failed.' }
$outFile = [IO.File]::Open((Join-Path $OutputDirectory 'd4r.stdout.log'), [IO.FileMode]::Create, [IO.FileAccess]::Write, [IO.FileShare]::Read) $outFile = [IO.File]::Open((Join-Path $OutputDirectory 'd4r.stdout.log'), [IO.FileMode]::Create, [IO.FileAccess]::Write, [IO.FileShare]::Read)
@@ -251,9 +292,9 @@ try {
[void]$outTask.GetAwaiter().GetResult(); [void]$errTask.GetAwaiter().GetResult() [void]$outTask.GetAwaiter().GetResult(); [void]$errTask.GetAwaiter().GetResult()
$outFile.Dispose(); $outFile = $null; $errFile.Dispose(); $errFile = $null $outFile.Dispose(); $outFile = $null; $errFile.Dispose(); $errFile = $null
foreach ($file in Get-ChildItem -LiteralPath $game -Filter 'OptiScaler*.log' -File) { Copy-Item -LiteralPath $file.FullName -Destination $OutputDirectory -Force } foreach ($file in Get-ChildItem -LiteralPath $game -Filter 'OptiScaler*.log' -File) { Copy-Item -LiteralPath $file.FullName -Destination $OutputDirectory -Force }
$stderr = Get-Content -LiteralPath (Join-Path $OutputDirectory 'd4r.stderr.log') -Raw $stderr = [IO.File]::ReadAllText((Join-Path $OutputDirectory 'd4r.stderr.log'))
$stdout = Get-Content -LiteralPath (Join-Path $OutputDirectory 'd4r.stdout.log') -Raw $stdout = [IO.File]::ReadAllText((Join-Path $OutputDirectory 'd4r.stdout.log'))
$debugger = Get-Content -LiteralPath (Join-Path $OutputDirectory 'debugger.log') -Raw $debugger = [IO.File]::ReadAllText((Join-Path $OutputDirectory 'debugger.log'))
$gameExit = [regex]::Match($debugger, 'EXIT code=0x([0-9a-f]+)') $gameExit = [regex]::Match($debugger, 'EXIT code=0x([0-9a-f]+)')
$gameCrash = $null $gameCrash = $null
if ([IO.Path]::GetFileName($GameExe) -eq 'SHProto-Win64-Shipping.exe' -and $gameExit.Success -and $gameExit.Groups[1].Value -ne '0') { if ([IO.Path]::GetFileName($GameExe) -eq 'SHProto-Win64-Shipping.exe' -and $gameExit.Success -and $gameExit.Groups[1].Value -ne '0') {
@@ -268,21 +309,8 @@ try {
reason=[string]$context.FGenericCrashContext.RuntimeProperties.ErrorMessage} reason=[string]$context.FGenericCrashContext.RuntimeProperties.ErrorMessage}
} }
} }
# A bounded stop can interrupt the last printf; count only complete records. Get-D4RGameSummary -Stdout $stdout -Stderr $stderr -Debugger $debugger -Preset $Preset `
$frames = [regex]::Matches($stdout, '(?m)^D4R_FRAME cpu_frame_copies=([0-9]+) frame_age=([0-9]+) interop_ngx_ms=([0-9.]+)\r?$') -RunSeconds $timer.Elapsed.TotalSeconds -ValidateOutput ([bool]$ValidateOutput) -GameCrash $gameCrash |
$checks = [regex]::Matches($stdout, '(?m)^D4R_OUTPUT_VALIDATION elements=([0-9]+) nan=([0-9]+) inf=([0-9]+) diagnostics_cpu_bytes=8\r?$')
$nonfinite = @($checks | Where-Object { $_.Groups[2].Value -ne '0' -or $_.Groups[3].Value -ne '0' }).Count
$launches = [regex]::Matches($stderr, '\[d4r-launch\] kernel="([^"]+)" backend=(native|translated)')
$kernels = @($launches | ForEach-Object { $_.Groups[1].Value + ' ' + $_.Groups[2].Value } | Group-Object | ForEach-Object {
$parts=$_.Name.Split(' '); @{kernel=$parts[0]; backend=$parts[1]; launches=$_.Count}
})
@{exitCode=$(if ($gameExit.Success) { '0x' + $gameExit.Groups[1].Value } else { 'diagnostic_timeout' }); runSeconds=$timer.Elapsed.TotalSeconds; preset=$Preset; gameCrash=$gameCrash;
nativeLaunches=([regex]::Matches($stderr, '\[d4r-launch\].*backend=native')).Count;
completedFrames=$frames.Count; outputValidationRequested=[bool]$ValidateOutput;
outputGpuChecks=$checks.Count; nonfiniteOutputs=$nonfinite; kernels=$kernels;
previousFrameOutputs=@($frames | Where-Object { $_.Groups[2].Value -ne '0' }).Count;
cpuImageCopyFrames=@($frames | Where-Object { $_.Groups[1].Value -ne '0' }).Count;
failures=([regex]::Matches($stderr, 'D4R_[A-Z0-9_]*FAILURE\b')).Count} |
ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8 ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
Compress-Archive -LiteralPath $OutputDirectory -DestinationPath ($OutputDirectory + '.zip') -Force Compress-Archive -LiteralPath $OutputDirectory -DestinationPath ($OutputDirectory + '.zip') -Force
Write-Host "Game diagnostic bundle: $OutputDirectory.zip" Write-Host "Game diagnostic bundle: $OutputDirectory.zip"
+104
View File
@@ -0,0 +1,104 @@
"""Compare resident native modules with exact replay and paired batch timings.
Fixtures can contain private weights; this utility never packages them.
"""
import argparse
import hashlib
import json
import os
import pathlib
import subprocess
import sys
from profile_report import report
def sha(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def run(args):
args.output_dir.mkdir(parents=True, exist_ok=False)
summary = dict(passed=False, numericalExact=False, fixture=str(args.fixture_dir),
module=str(args.module), candidate=str(args.candidate), probe=str(args.probe),
pairs=args.pairs, batches=args.batches,
timingSources=args.timings, executions=[])
environment = os.environ.copy()
environment['D4R_DIAG_DIR'] = str(args.output_dir)
environment['PATH'] = str(args.hip_root / 'bin') + os.pathsep + environment.get('PATH', '')
try:
summary.update(moduleSha256=sha(args.module), candidateSha256=sha(args.candidate),
probeSha256=sha(args.probe))
def execute(name, module, options=()):
command = [str(args.probe), '--hip-root', str(args.hip_root), '--module', str(module),
'--fixture-dir', str(args.fixture_dir), '--output-dir', str(args.output_dir / name),
'--iterations', '1', *options]
with (args.output_dir / (name + '.stdout.log')).open('wb') as out, \
(args.output_dir / (name + '.stderr.log')).open('wb') as err:
try:
completed = subprocess.run(command, stdout=out, stderr=err, env=environment, timeout=180)
except subprocess.TimeoutExpired:
summary['executions'].append(dict(name=name, command=command, timedOut=True))
raise
summary['executions'].append(dict(name=name, command=command, exitCode=completed.returncode))
if completed.returncode:
raise RuntimeError(f'{name} failed ({completed.returncode}); inspect preserved logs/dump')
return args.output_dir / name
control = execute('control-reference', args.module)
candidate = execute('candidate-reference', args.candidate)
reference = {p.name: sha(p) for p in control.glob('alloc-*.bin')}
if not reference:
raise RuntimeError('Replay produced no allocations')
def check(folder):
actual = {p.name: sha(p) for p in folder.glob('alloc-*.bin')}
if actual != reference:
changed = sorted(set(actual) ^ set(reference) |
{name for name in reference if actual.get(name) != reference[name]})
raise RuntimeError(f'{folder.name}: allocations differ from fresh control: {changed}')
# Stop before timing a candidate that changes the numerical result.
check(candidate)
summary['numericalExact'] = True
summary['allocationSha256'] = reference
for timing in args.timings:
for batch in args.batches:
folder = execute(f'{timing}-{batch}', args.module,
['--benchmark-module', str(args.candidate), '--benchmark-timing', timing,
'--benchmark-batch', str(batch), '--iterations', str(args.pairs)])
check(folder)
measured = report(args.output_dir)
summary['profile'] = measured
expected = len(args.timings) * len(args.batches)
if len(measured['replayPairs']) != expected or any(
row['controlGpuMs']['samples'] != args.pairs for row in measured['replayPairs']):
raise RuntimeError('Incomplete/invalid timing pairs; this run is not accepted for performance comparison')
summary['passed'] = True
for row in measured['replayPairs']:
print(f"PAIRED {row['kernel']} {row['timingSource']} batch={row['launchesPerSample']} "
f"control={row['controlGpuMs']['median']:.6f} candidate={row['candidateGpuMs']['median']:.6f} ms "
f"ratio={row['candidateToControl']['median']:.6f}")
print('PASS NATIVE_BENCHMARK exact_allocations=1 timings_valid=1 '
'isolated_replay=1 game_speedup=unmeasured')
except Exception as error:
summary['error'] = str(error)
print(f'FAIL NATIVE_BENCHMARK {error}', file=sys.stderr)
finally:
(args.output_dir / 'summary.json').write_text(json.dumps(summary, indent=2) + '\n', encoding='utf-8')
return 0 if summary['passed'] else 5
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__)
for name in ('probe', 'hip-root', 'module', 'candidate', 'fixture-dir', 'output-dir'):
parser.add_argument('--' + name, required=True, type=lambda s: pathlib.Path(s).resolve())
parser.add_argument('--pairs', type=int, default=32)
parser.add_argument('--batches', type=int, nargs='+', default=[1, 64])
parser.add_argument('--timings', choices=['events', 'dispatch'], nargs='+', default=['events', 'dispatch'])
args = parser.parse_args()
if not 1 <= args.pairs <= 10000 or any(not 1 <= batch <= 256 for batch in args.batches):
parser.error('pairs must be 1..10000 and batches 1..256')
if len(set(args.batches)) != len(args.batches) or len(set(args.timings)) != len(args.timings):
parser.error('duplicate batches/timing sources are not allowed')
raise SystemExit(run(args))
+51
View File
@@ -0,0 +1,51 @@
#pragma once
// Adapted from realdody/d4r ae530b0 (MIT), realdody <[email protected]>.
#include <cstdint>
#include <cstring>
#include <cmath>
#include <limits>
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<uint16_t>((bits >> 63) << 15);
int32_t exponent = static_cast<int32_t>((bits >> 52) & 0x7ff);
uint64_t mantissa = bits & 0x000fffffffffffffULL;
if (exponent == 0x7ff) return static_cast<uint16_t>(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<uint16_t>(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<uint16_t>(sign | 0x7c00);
}
return static_cast<uint16_t>(sign | ((unbiased + 15) << 10) | static_cast<uint16_t>(rounded & 0x3ff));
}
if (rounded >= 0x400) return static_cast<uint16_t>(sign | 0x0400); // smallest normal half
return static_cast<uint16_t>(sign | static_cast<uint16_t>(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<double>(mantissa), -24);
else if (exponent == 0x1fu) magnitude = mantissa ? std::numeric_limits<double>::quiet_NaN() : std::numeric_limits<double>::infinity();
else magnitude = std::ldexp(static_cast<double>(mantissa) / 1024.0 + 1.0, static_cast<int>(exponent) - 15);
return negative ? -magnitude : magnitude;
}
}
+45
View File
@@ -0,0 +1,45 @@
#include "binary16.h"
#include <cstdio>
#include <initializer_list>
#include <stdexcept>
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<double>::denorm_min(), 0);
check(-std::numeric_limits<double>::denorm_min(), 0x8000);
check(std::numeric_limits<double>::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;
}
}
+41 -8
View File
@@ -1,9 +1,10 @@
"""Build a private gfx1201 texture override from the user's local DLSS DLL. """Build a private target-specific texture override from the user's local DLSS DLL.
The resulting PTX/code object contains NVIDIA code and must remain local. The resulting PTX/code object contains NVIDIA code and must remain local.
No Wine, shell script, GPU, CUDA SDK or system installation is required. No Wine, shell script, GPU, CUDA SDK or system installation is required.
""" """
import argparse import argparse
import ast
import hashlib import hashlib
import json import json
import os import os
@@ -12,6 +13,15 @@ import re
import shutil import shutil
import subprocess import subprocess
import sys import sys
from gpu_targets import TARGETS
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] != TARGETS[target]['elfMachine']):
raise RuntimeError(f'Code object does not target {target}: {path}')
def main(): def main():
@@ -21,12 +31,17 @@ def main():
parser.add_argument('--zluda-root', type=pathlib.Path, required=True) parser.add_argument('--zluda-root', type=pathlib.Path, required=True)
parser.add_argument('--kernel', required=True) parser.add_argument('--kernel', required=True)
parser.add_argument('--output-directory', type=pathlib.Path, required=True) parser.add_argument('--output-directory', type=pathlib.Path, required=True)
parser.add_argument('--gpu-arch', choices=tuple(TARGETS), default='gfx1201')
parser.add_argument('--shader-mode', choices=['cu', 'wgp'], default='cu')
args = parser.parse_args() args = parser.parse_args()
if not re.fullmatch(r'hiluma_engine_output_depth(?:inv|reg)_mv(?:hi|lo)_(?:hdr|ldr)(?:_max)?_v2_rel', args.kernel): if not re.fullmatch(r'hiluma_engine_(?:output_depth(?:inv|reg)_mv(?:hi|lo)_(?:hdr|ldr)(?:_max)?|'
parser.error('this initial validated build recipe supports K v2 output variants only') r'input_depth(?:inv|reg)_mv(?:hi|lo)_(?:hdr|ldr))_v2_rel', args.kernel):
parser.error('this private experimental build recipe supports K v2 input/output variants only')
repo = pathlib.Path(__file__).resolve().parents[2] repo = pathlib.Path(__file__).resolve().parents[2]
output = args.output_directory.resolve() output = args.output_directory.resolve()
output.mkdir(parents=True, exist_ok=True) 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 = output / ('work-' + args.kernel)
work.mkdir(exist_ok=True) work.mkdir(exist_ok=True)
hip = args.hip_root.resolve() hip = args.hip_root.resolve()
@@ -39,6 +54,7 @@ def main():
env = os.environ.copy() env = os.environ.copy()
env.update(D4R_PREFER_ACCURACY='1', D4R_ZLUDA_WMMA='1', D4R_ZLUDA_WMMA_FP8='1', env.update(D4R_PREFER_ACCURACY='1', D4R_ZLUDA_WMMA='1', D4R_ZLUDA_WMMA_FP8='1',
D4R_ZLUDA_WMMA_FP8_NATIVE='0', D4R_ZLUDA_WMMA_F16_REFERENCE='1', D4R_ZLUDA_WMMA_FP8_NATIVE='0', D4R_ZLUDA_WMMA_F16_REFERENCE='1',
D4R_ZLUDA_WGP='1' if args.shader_mode == 'wgp' else '0',
D4R_ZLUDA_IMPLICIT_MAX_BLOCK='256') D4R_ZLUDA_IMPLICIT_MAX_BLOCK='256')
for name in ('D4R_ZLUDA_IGNORE_DENORMAL', 'D4R_ZLUDA_FAST_MATH', 'D4R_ZLUDA_WMMA_F32ACC', for name in ('D4R_ZLUDA_IGNORE_DENORMAL', 'D4R_ZLUDA_FAST_MATH', 'D4R_ZLUDA_WMMA_F32ACC',
'D4R_ZLUDA_WAVE64', 'D4R_ZLUDA_PROFILE'): 'D4R_ZLUDA_WAVE64', 'D4R_ZLUDA_PROFILE'):
@@ -57,9 +73,16 @@ def main():
env['D4R_DLSS_PTX_DIR'] = str(ptx) env['D4R_DLSS_PTX_DIR'] = str(ptx)
edited = work / (args.kernel + '.ptx') edited = work / (args.kernel + '.ptx')
run('make-ptx', [sys.executable, repo / 'kernels/tex/make_ptx.py', args.kernel, edited]) run('make-ptx', [sys.executable, repo / 'kernels/tex/make_ptx.py', args.kernel, edited])
rewrite_match = re.search(r'surface stores replaced: (\{[^\n]+\})',
(work / 'make-ptx.log').read_text(errors='replace'))
if not rewrite_match:
raise RuntimeError('PTX recipe did not report its changes')
rewrite_counts = ast.literal_eval(rewrite_match[1])
if not any(rewrite_counts.values()):
raise RuntimeError('PTX recipe made no changes; refuse a no-op override')
raw, ir, extra = work / 'raw.bc', work / 'raw.ll', work / 'extra.bc' 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', 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', '-fgpu-rdc', '-emit-llvm', '-c', '-Xclang', '-fdenormal-fp-math=dynamic',
'-DD4R_ACCURACY', '-DD4R_KERNEL_NAME=' + args.kernel, '-DD4R_ACCURACY', '-DD4R_KERNEL_NAME=' + args.kernel,
'-o', raw, repo / 'kernels/tex/sust_only.hip']) '-o', raw, repo / 'kernels/tex/sust_only.hip'])
@@ -68,22 +91,32 @@ def main():
for line in ir.read_text().splitlines(): for line in ir.read_text().splitlines():
if re.search(r'@llvm.used|wchar_size|llvm.module.flags|__hip_cuid', line): if re.search(r'@llvm.used|wchar_size|llvm.module.flags|__hip_cuid', line):
continue 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)) lines.append(re.sub(r'"target-features"="[^"]+"', '', line))
ir.write_text('\n'.join(lines) + '\n') ir.write_text('\n'.join(lines) + '\n')
run('assemble', [tools['llvm-as'], ir, '-o', extra]) run('assemble', [tools['llvm-as'], ir, '-o', extra])
env['D4R_ZLUDA_EXTRA_BC'] = str(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:
raise RuntimeError('Cannot verify shader mode: kernel attributes are missing')
attributes = re.search(r'^attributes #' + definition[1] + r' = \{([^\n]+)\}', optimized, re.M)
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')) shutil.copyfile(work / 'emitted/module.hsaco', output / (args.kernel + '.hsaco'))
run('manifest', [sys.executable, repo / 'kernels/tools/kernel_manifest.py', output, args.dlss_dll]) run('manifest', [sys.executable, repo / 'kernels/tools/kernel_manifest.py', output, args.dlss_dll])
metadata = dict(architecture='gfx1201', accuracy=True, nativeFP8=False, 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', privateNvidiaDerivedCode=True, validation='not yet validated; do not install before comparison',
dlssSha256=hashlib.sha256(args.dlss_dll.read_bytes()).hexdigest(), dlssSha256=hashlib.sha256(args.dlss_dll.read_bytes()).hexdigest(),
zludaBuild=json.loads((zluda / 'build-info.json').read_text(encoding='utf-8-sig')), zludaBuild=json.loads((zluda / 'build-info.json').read_text(encoding='utf-8-sig')),
compiler=subprocess.check_output([str(tools['clang++']), '--version'], env=env).decode(errors='replace'), 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')}) 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') (output / 'build-info.json').write_text(json.dumps(metadata, indent=2) + '\n')
print(f'Built PRIVATE gfx1201 output override: {output}; validation required before use') print(f'Built PRIVATE {args.gpu_arch} texture override: {output}; validation required before use')
if __name__ == '__main__': if __name__ == '__main__':
+25 -5
View File
@@ -46,7 +46,7 @@ int main(int argc, char** argv)
// Require the real HIP architecture, then match the CUDA device by PCI identity. // Require the real HIP architecture, then match the CUDA device by PCI identity.
HipApi hip(args.hip_root); HipApi hip(args.hip_root);
hipDeviceProp_t props{}; hipDeviceProp_t props{};
hip.select_gfx1201(args.device, props); hip.select_architecture(args.device, props);
CudaApi api(args.cuda_dll); CudaApi api(args.cuda_dll);
loaded_modules(); loaded_modules();
std::printf("STAGE cuInit\n"); std::printf("STAGE cuInit\n");
@@ -80,6 +80,21 @@ int main(int argc, char** argv)
if (primary) api.cuDevicePrimaryCtxRelease_v2(device); else api.cuCtxDestroy_v2(context); } } if (primary) api.cuDevicePrimaryCtxRelease_v2(device); else api.cuCtxDestroy_v2(context); } }
} cleanup{api, context, device, primary}; } cleanup{api, context, device, primary};
api.check(api.cuCtxSetCurrent(context), "cuCtxSetCurrent"); api.check(api.cuCtxSetCurrent(context), "cuCtxSetCurrent");
void* stream = nullptr;
using CreateStream = int(WINAPI*)(void**, unsigned);
using DestroyStream = int(WINAPI*)(void*);
DestroyStream destroyStream = nullptr;
if (std::getenv("D4R_DIAG_NONBLOCKING_STREAM")) {
const auto createStream = api.library.symbol<CreateStream>("cuStreamCreate");
destroyStream = api.library.symbol<DestroyStream>("cuStreamDestroy_v2");
api.check(createStream(&stream, 1), "cuStreamCreate(NON_BLOCKING)");
}
struct StreamCleanup {
void*& stream; DestroyStream destroy;
~StreamCleanup() { if (stream && destroy) destroy(stream); }
} streamCleanup{stream, destroyStream};
const bool queued = std::getenv("D4R_DIAG_QUEUE_PTX") != nullptr;
if (queued && !stream) throw std::runtime_error("Queued PTX diagnostic requires D4R_DIAG_NONBLOCKING_STREAM=1");
CUmodule module = nullptr; CUmodule module = nullptr;
std::printf("STAGE cuModuleLoadData (PTX JIT)\n"); std::printf("STAGE cuModuleLoadData (PTX JIT)\n");
api.check(api.cuModuleLoadData(&module, ptx), "cuModuleLoadData"); api.check(api.cuModuleLoadData(&module, ptx), "cuModuleLoadData");
@@ -93,25 +108,30 @@ int main(int argc, char** argv)
CUdeviceptr pointer = 0; CUdeviceptr pointer = 0;
api.check(api.cuMemAlloc_v2(&pointer, bytes), "cuMemAlloc_v2"); api.check(api.cuMemAlloc_v2(&pointer, bytes), "cuMemAlloc_v2");
struct MemCleanup { CudaApi& api; CUdeviceptr p; ~MemCleanup() { if (p) api.cuMemFree_v2(p); } } ac{api, pointer}; struct MemCleanup { CudaApi& api; CUdeviceptr p; ~MemCleanup() { if (p) api.cuMemFree_v2(p); } } ac{api, pointer};
if (queued) api.check(api.cuMemcpyHtoD_v2(pointer, result.data(), bytes), "cuMemcpyHtoD_v2(queued sentinel)");
for (unsigned i = 0; i < args.iterations; ++i) { for (unsigned i = 0; i < args.iterations; ++i) {
uint32_t n = nmax - i % 129u, seed = 0x12345678u + i * 31u; uint32_t n = queued ? nmax : nmax - i % 129u, seed = 0x12345678u + i * 31u;
if (!queued) {
std::fill(result.begin(), result.end(), 0xdeadbeefu); std::fill(result.begin(), result.end(), 0xdeadbeefu);
api.check(api.cuMemcpyHtoD_v2(pointer, result.data(), bytes), "cuMemcpyHtoD_v2(sentinel)"); api.check(api.cuMemcpyHtoD_v2(pointer, result.data(), bytes), "cuMemcpyHtoD_v2(sentinel)");
}
void* parameters[] = {&pointer, &n, &seed}; void* parameters[] = {&pointer, &n, &seed};
api.check(api.cuLaunchKernel(function, (n + 127u) / 128u, 1, 1, 128, 1, 1, api.check(api.cuLaunchKernel(function, (n + 127u) / 128u, 1, 1, 128, 1, 1,
0, nullptr, parameters, nullptr), "cuLaunchKernel"); 0, stream, parameters, nullptr), "cuLaunchKernel");
if (queued && i + 1 != args.iterations) continue;
api.check(api.cuCtxSynchronize(), "cuCtxSynchronize"); api.check(api.cuCtxSynchronize(), "cuCtxSynchronize");
api.check(api.cuMemcpyDtoH_v2(result.data(), pointer, bytes), "cuMemcpyDtoH_v2"); api.check(api.cuMemcpyDtoH_v2(result.data(), pointer, bytes), "cuMemcpyDtoH_v2");
if (!verify(result, n, seed)) return 5; if (!verify(result, n, seed)) return 5;
} }
api.check(api.cuMemFree_v2(pointer), "cuMemFree_v2"); ac.p = 0; api.check(api.cuMemFree_v2(pointer), "cuMemFree_v2"); ac.p = 0;
api.check(api.cuModuleUnload(module), "cuModuleUnload"); mc.m = nullptr; api.check(api.cuModuleUnload(module), "cuModuleUnload"); mc.m = nullptr;
if (stream) { api.check(destroyStream(stream), "cuStreamDestroy_v2"); stream = nullptr; }
api.check(api.cuCtxSetCurrent(nullptr), "cuCtxSetCurrent(NULL)"); api.check(api.cuCtxSetCurrent(nullptr), "cuCtxSetCurrent(NULL)");
if (primary) api.check(api.cuDevicePrimaryCtxRelease_v2(device), "cuDevicePrimaryCtxRelease_v2"); if (primary) api.check(api.cuDevicePrimaryCtxRelease_v2(device), "cuDevicePrimaryCtxRelease_v2");
else api.check(api.cuCtxDestroy_v2(context), "cuCtxDestroy_v2"); else api.check(api.cuCtxDestroy_v2(context), "cuCtxDestroy_v2");
cleanup.context = nullptr; cleanup.context = nullptr;
std::printf("PASS CUDA architecture=gfx1201 context=%s iterations=%u guard_verified=1\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); args.context.c_str(), args.iterations, unsigned(queued));
return 0; return 0;
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::fprintf(stderr, "FAIL CUDA %s\n", e.what()); std::fprintf(stderr, "FAIL CUDA %s\n", e.what());
+8 -2
View File
@@ -47,6 +47,7 @@ struct ImageApi {
D4R_IMAGE(cuArray3DGetDescriptor_v2, Array3DDescriptor*, Array); D4R_IMAGE(cuArray3DGetDescriptor_v2, Array3DDescriptor*, Array);
D4R_IMAGE(cuArrayDestroy, Array); D4R_IMAGE(cuArrayDestroy, Array);
D4R_IMAGE(cuMemcpy2D_v2, const Copy2D*); D4R_IMAGE(cuMemcpy2D_v2, const Copy2D*);
D4R_IMAGE(cuMemcpy2DAsync_v2, const Copy2D*, void*);
D4R_IMAGE(cuTexObjectCreate, Texture*, const ResourceDescriptor*, const TextureDescriptor*, const void*); D4R_IMAGE(cuTexObjectCreate, Texture*, const ResourceDescriptor*, const TextureDescriptor*, const void*);
D4R_IMAGE(cuTexObjectDestroy, Texture); D4R_IMAGE(cuTexObjectDestroy, Texture);
D4R_IMAGE(cuTexObjectGetResourceDesc, ResourceDescriptor*, Texture); D4R_IMAGE(cuTexObjectGetResourceDesc, ResourceDescriptor*, Texture);
@@ -131,13 +132,18 @@ public:
copy.widthBytes = rowBytes_; copy.height = height_; copy.widthBytes = rowBytes_; copy.height = height_;
api_.cuda.check(api_.cuMemcpy2D_v2(&copy), "cuMemcpy2D_v2(diagnostic array to host)"); api_.cuda.check(api_.cuMemcpy2D_v2(&copy), "cuMemcpy2D_v2(diagnostic array to host)");
} }
void upload_device(CUdeviceptr source, size_t pitch) { void upload_device(CUdeviceptr source, size_t pitch, bool deferred = false) {
if (pitch < rowBytes_) throw std::runtime_error("Device input pitch is smaller than its row"); if (pitch < rowBytes_) throw std::runtime_error("Device input pitch is smaller than its row");
Copy2D copy{}; Copy2D copy{};
copy.srcType = 2; copy.srcDevice = source; copy.srcPitch = pitch; copy.srcType = 2; copy.srcDevice = source; copy.srcPitch = pitch;
copy.dstType = 3; copy.dstArray = array_; copy.dstType = 3; copy.dstArray = array_;
copy.widthBytes = rowBytes_; copy.height = height_; copy.widthBytes = rowBytes_; copy.height = height_;
api_.cuda.check(api_.cuMemcpy2D_v2(&copy), "cuMemcpy2D_v2(external VRAM to array)"); if (deferred) {
// The Windows ZLUDA backend and HIP conversion use the same
// legacy default stream. Caller must complete the whole input
// batch before NGX may consume arrays on its own streams.
api_.cuda.check(api_.cuMemcpy2DAsync_v2(&copy, nullptr), "cuMemcpy2DAsync_v2(external VRAM to array)");
} else api_.cuda.check(api_.cuMemcpy2D_v2(&copy), "cuMemcpy2D_v2(external VRAM to array)");
} }
void download_device(CUdeviceptr destination, size_t pitch) { void download_device(CUdeviceptr destination, size_t pitch) {
if (pitch < rowBytes_) throw std::runtime_error("Device output pitch is smaller than its row"); if (pitch < rowBytes_) throw std::runtime_error("Device output pitch is smaller than its row");
+13 -9
View File
@@ -8,7 +8,7 @@ int main(int argc, char** argv) {
Args args(argc, argv); Args args(argc, argv);
HipApi hip(args.hip_root); HipApi hip(args.hip_root);
hipDeviceProp_t props{}; 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()); SearchDirectory search(std::filesystem::path(wide(args.cuda_dll)).parent_path());
CudaApi cuda(args.cuda_dll); CudaApi cuda(args.cuda_dll);
cuda.check(cuda.cuInit(0), "cuInit"); cuda.check(cuda.cuInit(0), "cuInit");
@@ -30,22 +30,25 @@ int main(int argc, char** argv) {
struct Cleanup { CudaApi& api; CUcontext context; ~Cleanup() { (void)api.cuCtxDestroy_v2(context); } } cleanup{cuda, context}; struct Cleanup { CudaApi& api; CUcontext context; ~Cleanup() { (void)api.cuCtxDestroy_v2(context); } } cleanup{cuda, context};
d4r::cuda::ImageApi images(cuda); d4r::cuda::ImageApi images(cuda);
for (unsigned iteration = 0; iteration < args.iterations; ++iteration) { for (unsigned iteration = 0; iteration < args.iterations; ++iteration) {
for (unsigned channels : {1u, 2u, 4u}) for (bool surface : {false, true}) { for (unsigned format : {16u, 32u}) for (unsigned channels : {1u, 2u, 4u}) for (bool surface : {false, true}) {
d4r::cuda::Image image(images, 17, 9, 16, channels, surface); const unsigned halvesPerPixel = channels * (format == 16 ? 1 : 2);
d4r::cuda::Image image(images, 17, 9, format, channels, surface);
image.verify_wrong_object_kind(); image.verify_wrong_object_kind();
std::vector<uint16_t> input(17 * 9 * channels), output(input.size()); std::vector<uint16_t> input(17 * 9 * halvesPerPixel), output(input.size());
for (size_t i = 0; i < input.size(); ++i) input[i] = uint16_t((i + iteration) % 8 * 0x100); for (size_t i = 0; i < input.size(); ++i) input[i] = uint16_t((i + iteration) % 8 * 0x100);
image.upload(input.data()); image.upload(input.data());
image.download(output.data()); image.download(output.data());
if (input != output) throw std::runtime_error("CUDA array storage roundtrip mismatch"); if (input != output) throw std::runtime_error("CUDA array storage roundtrip mismatch");
const size_t rowBytes = 17 * channels * sizeof(uint16_t), pitch = (rowBytes + 255) & ~size_t(255); const size_t rowBytes = 17 * halvesPerPixel * sizeof(uint16_t), pitch = (rowBytes + 255) & ~size_t(255);
CUdeviceptr devicePixels = 0; CUdeviceptr devicePixels = 0;
cuda.check(cuda.cuMemAlloc_v2(&devicePixels, pitch * 9), "cuMemAlloc_v2(pitched image regression)"); cuda.check(cuda.cuMemAlloc_v2(&devicePixels, pitch * 9), "cuMemAlloc_v2(pitched image regression)");
struct PixelsCleanup { CudaApi& cuda; CUdeviceptr pointer; ~PixelsCleanup() { (void)cuda.cuMemFree_v2(pointer); } } pixelsCleanup{cuda, devicePixels}; struct PixelsCleanup { CudaApi& cuda; CUdeviceptr pointer; ~PixelsCleanup() { (void)cuda.cuMemFree_v2(pointer); } } pixelsCleanup{cuda, devicePixels};
std::vector<uint8_t> padded(pitch * 9, 0xcd); std::vector<uint8_t> padded(pitch * 9, 0xcd);
for (unsigned y = 0; y < 9; ++y) std::memcpy(padded.data() + y * pitch, input.data() + y * 17 * channels, rowBytes); for (unsigned y = 0; y < 9; ++y) std::memcpy(padded.data() + y * pitch, input.data() + y * 17 * halvesPerPixel, rowBytes);
cuda.check(cuda.cuMemcpyHtoD_v2(devicePixels, padded.data(), padded.size()), "cuMemcpyHtoD_v2(pitched pattern)"); cuda.check(cuda.cuMemcpyHtoD_v2(devicePixels, padded.data(), padded.size()), "cuMemcpyHtoD_v2(pitched pattern)");
image.upload_device(devicePixels, pitch); const bool deferred = std::getenv("D4R_DIAG_ASYNC_ARRAY_COPY") != nullptr;
image.upload_device(devicePixels, pitch, deferred);
if (deferred) cuda.check(cuda.cuCtxSynchronize(), "cuCtxSynchronize(input batch regression)");
image.download(output.data()); image.download(output.data());
if (input != output) throw std::runtime_error("CUDA device-to-array pitched copy mismatch"); if (input != output) throw std::runtime_error("CUDA device-to-array pitched copy mismatch");
std::fill(padded.begin(), padded.end(), 0xee); std::fill(padded.begin(), padded.end(), 0xee);
@@ -53,7 +56,7 @@ int main(int argc, char** argv) {
image.download_device(devicePixels, pitch); image.download_device(devicePixels, pitch);
cuda.check(cuda.cuMemcpyDtoH_v2(padded.data(), devicePixels, padded.size()), "cuMemcpyDtoH_v2(device array output)"); cuda.check(cuda.cuMemcpyDtoH_v2(padded.data(), devicePixels, padded.size()), "cuMemcpyDtoH_v2(device array output)");
for (unsigned y = 0; y < 9; ++y) { for (unsigned y = 0; y < 9; ++y) {
if (std::memcmp(padded.data() + y * pitch, input.data() + y * 17 * channels, rowBytes)) if (std::memcmp(padded.data() + y * pitch, input.data() + y * 17 * halvesPerPixel, rowBytes))
throw std::runtime_error("CUDA array-to-device pitched copy mismatch"); throw std::runtime_error("CUDA array-to-device pitched copy mismatch");
if (!std::all_of(padded.begin() + y * pitch + rowBytes, padded.begin() + (y + 1) * pitch, [](uint8_t value) { return value == 0xee; })) if (!std::all_of(padded.begin() + y * pitch + rowBytes, padded.begin() + (y + 1) * pitch, [](uint8_t value) { return value == 0xee; }))
throw std::runtime_error("CUDA array-to-device copy overwrote pitch padding"); throw std::runtime_error("CUDA array-to-device copy overwrote pitch padding");
@@ -61,7 +64,8 @@ int main(int argc, char** argv) {
} }
} }
cuda.check(cuda.cuCtxSynchronize(), "cuCtxSynchronize"); cuda.check(cuda.cuCtxSynchronize(), "cuCtxSynchronize");
std::printf("PASS CUDA_IMAGES architecture=gfx1201 iterations=%u descriptor_and_storage=1 pitched_device_copies=1\n", args.iterations); 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; return 0;
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::fprintf(stderr, "FAIL CUDA_IMAGES %s\n", e.what()); std::fprintf(stderr, "FAIL CUDA_IMAGES %s\n", e.what());
+8 -2
View File
@@ -1,5 +1,6 @@
#include "hip_api.h" #include "hip_api.h"
#include "cuda_api.h" #include "cuda_api.h"
#include "binary16.h"
#include <array> #include <array>
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
@@ -60,6 +61,10 @@ static constexpr char source[] = R"PTX(
} }
)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) { static uint16_t half_bits(double value) {
_Float16 half = static_cast<_Float16>(value); _Float16 half = static_cast<_Float16>(value);
uint16_t bits; uint16_t bits;
@@ -71,6 +76,7 @@ static double half_value(uint16_t bits) {
std::memcpy(&half, &bits, sizeof(bits)); std::memcpy(&half, &bits, sizeof(bits));
return static_cast<double>(half); return static_cast<double>(half);
} }
#endif
int main(int argc, char** argv) { int main(int argc, char** argv) {
using namespace d4r::diag; using namespace d4r::diag;
@@ -79,7 +85,7 @@ int main(int argc, char** argv) {
Args args(argc, argv); Args args(argc, argv);
HipApi hip(args.hip_root); HipApi hip(args.hip_root);
hipDeviceProp_t props{}; hipDeviceProp_t props{};
hip.select_gfx1201(args.device, props); hip.select_architecture(args.device, props);
CudaApi cuda(args.cuda_dll); CudaApi cuda(args.cuda_dll);
cuda.check(cuda.cuInit(0), "cuInit"); cuda.check(cuda.cuInit(0), "cuInit");
CUdevice device = -1; 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); std::printf("MMA_REFERENCE cases=%zu elements=%zu differing=%zu\n", cases, d.size(), mismatch);
if (mismatch) return 5; 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; return 0;
} catch (const std::exception& error) { } catch (const std::exception& error) {
std::fprintf(stderr, "FAIL CUDA_F16_MMA %s\n", error.what()); std::fprintf(stderr, "FAIL CUDA_F16_MMA %s\n", error.what());
@@ -360,9 +360,9 @@ static void STDMETHODCALLTYPE hook_SOSetTargets(ID3D12GraphicsCommandList* self,
return original_SOSetTargets(access.target(), start_slot, view_count, views); 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 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); Access access(self);
auto saved_render_target_descriptors = copy_values(render_target_descriptors, (single_descriptor_handle ? 1 : render_target_descriptor_count)); 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); auto saved_depth_stencil_descriptor = copy_values(depth_stencil_descriptor, 1);
+2 -2
View File
@@ -123,7 +123,7 @@ int main(int argc, char** argv) {
std::unique_ptr<HipApi> hip; std::unique_ptr<HipApi> hip;
hipDeviceProp_t props{}; hipDeviceProp_t props{};
const bool no_hip = std::getenv("D4R_COMMAND_PROBE_NO_HIP") != nullptr; const bool no_hip = std::getenv("D4R_COMMAND_PROBE_NO_HIP") != nullptr;
if (!no_hip) { hip = std::make_unique<HipApi>(args.hip_root); hip->select_gfx1201(args.device, props); } if (!no_hip) { hip = std::make_unique<HipApi>(args.hip_root); hip->select_architecture(args.device, props); }
ComPtr<IDXGIFactory4> factory; dx(CreateDXGIFactory1(IID_PPV_ARGS(factory.GetAddressOf())), "Probe DXGI"); ComPtr<IDXGIFactory4> factory; dx(CreateDXGIFactory1(IID_PPV_ARGS(factory.GetAddressOf())), "Probe DXGI");
ComPtr<IDXGIAdapter1> adapter; ComPtr<IDXGIAdapter1> adapter;
for (UINT i = 0; ; ++i) { 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; 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", 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); args.iterations, indirect_enabled, root_indirect, no_hooks ? 0 : 2 * args.iterations);
return 0; return 0;
} catch (const std::exception& failure) { } catch (const std::exception& failure) {
+14 -5
View File
@@ -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_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)}) 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"); 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<IDXGIFactory4> factory; dx(CreateDXGIFactory1(IID_PPV_ARGS(factory.GetAddressOf())), "CreateDXGIFactory1"); ComPtr<IDXGIFactory4> factory; dx(CreateDXGIFactory1(IID_PPV_ARGS(factory.GetAddressOf())), "CreateDXGIFactory1");
ComPtr<IDXGIAdapter1> adapter; ComPtr<IDXGIAdapter1> adapter;
bool found = false; bool found = false;
@@ -74,7 +74,8 @@ int main(int argc, char** argv) {
dx(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(allocator.GetAddressOf())), "CreateCommandAllocator"); dx(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(allocator.GetAddressOf())), "CreateCommandAllocator");
dx(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, allocator.Get(), nullptr, IID_PPV_ARGS(list.GetAddressOf())), "CreateCommandList"); dx(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, allocator.Get(), nullptr, IID_PPV_ARGS(list.GetAddressOf())), "CreateCommandList");
const unsigned outWidth = args.output_width, outHeight = args.output_height; const unsigned outWidth = args.output_width, outHeight = args.output_height;
const unsigned width = outWidth / 2, height = outHeight / 2; const unsigned width = args.input_width ? args.input_width : outWidth / 2;
const unsigned height = args.input_height ? args.input_height : outHeight / 2;
std::printf("D3D12_NGX_DIMENSIONS input=%ux%u output=%ux%u\n", width, height, outWidth, outHeight); std::printf("D3D12_NGX_DIMENSIONS input=%ux%u output=%ux%u\n", width, height, outWidth, outHeight);
struct Texture { ComPtr<ID3D12Resource> image, upload; D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint{}; UINT64 bytes; } textures[5]; struct Texture { ComPtr<ID3D12Resource> image, upload; D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint{}; UINT64 bytes; } textures[5];
for (unsigned i = 0; i < 5; ++i) { for (unsigned i = 0; i < 5; ++i) {
@@ -168,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)); if (!matches) throw std::runtime_error("Imported D3D12 VRAM to CUDA array mismatch on plane " + std::to_string(i));
timeline.drain(queue.Get()); 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; return 0;
} }
Library shim(wide(args.module)); Library shim(wide(args.module));
@@ -217,13 +218,21 @@ int main(int argc, char** argv) {
using Requirements = unsigned(*)(IDXGIAdapter*,const d4r::ngx::FeatureDiscoveryInfo*,d4r::ngx::FeatureRequirement*); using Requirements = unsigned(*)(IDXGIAdapter*,const d4r::ngx::FeatureDiscoveryInfo*,d4r::ngx::FeatureRequirement*);
check(shim.symbol<Requirements>("NVSDK_NGX_D3D12_GetFeatureRequirements")(adapter.Get(),&discovery,&requirements),"D3D12 feature requirements"); check(shim.symbol<Requirements>("NVSDK_NGX_D3D12_GetFeatureRequirements")(adapter.Get(),&discovery,&requirements),"D3D12 feature requirements");
if (requirements.FeatureSupported != 0) throw std::runtime_error("D3D12 DLSS requirements unsupported"); if (requirements.FeatureSupported != 0) throw std::runtime_error("D3D12 DLSS requirements unsupported");
if (args.ngx_frontend != "optiscaler") {
ComPtr<IDXGIAdapter> warp;
dx(factory->EnumWarpAdapter(IID_PPV_ARGS(warp.GetAddressOf())), "Requirements WARP adapter");
d4r::ngx::FeatureRequirement unsupported{};
check(shim.symbol<Requirements>("NVSDK_NGX_D3D12_GetFeatureRequirements")(warp.Get(),&discovery,&unsupported),"WARP feature requirements");
if (unsupported.FeatureSupported != 4) throw std::runtime_error("WARP must report AdapterUnsupported without an initialization failure");
std::printf("D3D12_REQUIREMENTS warp_supported=0 enumeration_success=1\n");
}
if (args.ngx_abi == "project-legacy") { if (args.ngx_abi == "project-legacy") {
using ProjectInit = unsigned(*)(const char*,int,const char*,const wchar_t*,ID3D12Device*,const void*,unsigned); using ProjectInit = unsigned(*)(const char*,int,const char*,const wchar_t*,ID3D12Device*,const void*,unsigned);
check(shim.symbol<ProjectInit>("NVSDK_NGX_D3D12_Init_with_ProjectID")("24480451-f00d-face-1304-0308dabad187",0,"1.0",directory.c_str(),device.Get(),nullptr,0x15),"D3D12 legacy project Init"); check(shim.symbol<ProjectInit>("NVSDK_NGX_D3D12_Init_with_ProjectID")("24480451-f00d-face-1304-0308dabad187",0,"1.0",directory.c_str(),device.Get(),nullptr,0x15),"D3D12 legacy project Init");
} else if (args.ngx_abi == "project") { } else if (args.ngx_abi == "project") {
using ProjectInit = unsigned(*)(const char*,int,const char*,const wchar_t*,ID3D12Device*,unsigned,const void*); using ProjectInit = unsigned(*)(const char*,int,const char*,const wchar_t*,ID3D12Device*,unsigned,const void*);
check(shim.symbol<ProjectInit>("NVSDK_NGX_D3D12_Init_ProjectID")("24480451-f00d-face-1304-0308dabad187",0,"1.0",directory.c_str(),device.Get(),0x15,nullptr),"D3D12 project Init"); check(shim.symbol<ProjectInit>("NVSDK_NGX_D3D12_Init_ProjectID")("24480451-f00d-face-1304-0308dabad187",0,"1.0",directory.c_str(),device.Get(),0x15,nullptr),"D3D12 project Init");
} else check(shim.symbol<Init>("NVSDK_NGX_D3D12_Init_Ext")(241534723ull, directory.c_str(), device.Get(), 0x15, nullptr), "D3D12 Init"); } else check(shim.symbol<Init>("NVSDK_NGX_D3D12_Init_Ext")(args.ngx_app_id, directory.c_str(), device.Get(), 0x15, nullptr), "D3D12 Init");
auto shutdown = shim.symbol<Shutdown>("NVSDK_NGX_D3D12_Shutdown"); auto shutdown = shim.symbol<Shutdown>("NVSDK_NGX_D3D12_Shutdown");
struct ShutdownCleanup { Shutdown fn; ~ShutdownCleanup() { (void)fn(); } } shutdownCleanup{shutdown}; struct ShutdownCleanup { Shutdown fn; ~ShutdownCleanup() { (void)fn(); } } shutdownCleanup{shutdown};
void* parameters = nullptr; check(shim.symbol<Allocate>("NVSDK_NGX_D3D12_AllocateParameters")(&parameters), "AllocateParameters"); void* parameters = nullptr; check(shim.symbol<Allocate>("NVSDK_NGX_D3D12_AllocateParameters")(&parameters), "AllocateParameters");
@@ -397,7 +406,7 @@ int main(int argc, char** argv) {
std::printf("D4R_BURST_VALIDATION frames=%zu cpu_readback_after_all_submissions=1\n", submitted.size()); std::printf("D4R_BURST_VALIDATION frames=%zu cpu_readback_after_all_submissions=1\n", submitted.size());
} }
loaded_modules(); 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"); args.preset, args.iterations, enhanced, commandBackend ? "recorded_command_list" : "explicit_harness");
return 0; return 0;
} catch (const std::exception& error) { std::fprintf(stderr, "FAIL D3D12_DLSS %s\n", error.what()); loaded_modules(); return 4; } } catch (const std::exception& error) { std::fprintf(stderr, "FAIL D3D12_DLSS %s\n", error.what()); loaded_modules(); return 4; }
+2 -2
View File
@@ -137,7 +137,7 @@ int main(int argc, char** argv)
throw std::runtime_error("--stream-lifetime must be persistent or cycle"); throw std::runtime_error("--stream-lifetime must be persistent or cycle");
HipApi hip(args.hip_root); HipApi hip(args.hip_root);
hipDeviceProp_t props{}; hipDeviceProp_t props{};
hip.select_gfx1201(args.device, props); hip.select_architecture(args.device, props);
ExternalApi external{hip}; ExternalApi external{hip};
ComPtr<IDXGIFactory4> factory; ComPtr<IDXGIFactory4> factory;
dx(CreateDXGIFactory1(IID_PPV_ARGS(factory.GetAddressOf())), "CreateDXGIFactory1"); 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); DWORD handles_after = 0; GetProcessHandleCount(GetCurrentProcess(), &handles_after);
std::printf("HANDLES before=%lu after=%lu\n", handles_before, 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"); 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; return 0;
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::fprintf(stderr, "FAIL INTEROP %s\n", e.what()); std::fprintf(stderr, "FAIL INTEROP %s\n", e.what());
+2 -2
View File
@@ -170,7 +170,7 @@ std::vector<uint32_t> read_reference(ID3D12Device* device, ID3D12CommandQueue* q
int main(int argc, char** argv) { int main(int argc, char** argv) {
start(); start();
try { 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)); ExternalApi external{hip}; PixelProgram program(hip, wide(args.module));
{ {
PixelAllocation diagnostic(hip,3,1,8); 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); ++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; } } catch (const std::exception& error) { std::fprintf(stderr,"FAIL D3D12_GPU_FORMATS %s\n",error.what()); loaded_modules(); return 1; }
} }
+176
View File
@@ -0,0 +1,176 @@
// Drag-in releases ship one d4r folder for every supported GPU. This small
// _nvngx.dll is what OptiScaler loads: on first use it reads the real HIP
// architecture of the game's adapter (matched by LUID) and loads that
// target's shim, d4r\_nvngx_<arch>.dll, from the same folder. The per-target
// shim still enforces its own compiled target, so a mismatch cannot run.
// Keeping every file in one folder preserves NGX's search for
// nvngx_dlss.dll beside the calling module.
#include "d3d12_external.h"
#include "ngx_public_abi.h"
#include <dxgi.h>
#include <fstream>
#include <mutex>
namespace {
constexpr unsigned failure = 0xBAD00002, unsupported = 4;
std::mutex selectMutex;
HMODULE shim = nullptr;
bool selectionFailed = false;
std::filesystem::path module_directory() {
HMODULE module = nullptr;
GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(&module_directory), &module);
wchar_t path[32768]{}; GetModuleFileNameW(module, path, 32768);
return std::filesystem::path(path).parent_path();
}
// The shim never ran, so the loader writes the player's log itself.
// Fresh at each launch, then appended: OptiScaler can query several adapters.
void report(const std::string& message) {
static bool fresh = true;
std::ofstream(module_directory() / L"d4r_nvngx.log", fresh ? std::ios::trunc : std::ios::app)
<< "d4r: DLSS disabled: " << message << "\n";
fresh = false;
std::fprintf(stderr, "D4R_WINDOWS_FAILURE %s\n", message.c_str());
}
std::string supported_list(const std::filesystem::path& dir) {
std::string list;
for (const auto& target : d4r::diag::gpu_targets)
if (std::filesystem::exists(dir / (L"_nvngx_" + d4r::diag::wide(target.architecture) + L".dll")))
list += (list.empty() ? "" : ", ") + std::string(target.architecture);
return list.empty() ? "none (the d4r folder is incomplete; extract the ZIP again)" : list;
}
// Loads the matching shim once. A null LUID (calls made before NGX init)
// accepts the only HIP device with a shim in this folder.
HMODULE select(const LUID* luid) {
std::lock_guard<std::mutex> lock(selectMutex);
if (shim || selectionFailed) return shim;
const auto dir = module_directory();
try {
// Spoofed architectures invalidate the target guarantee; see hip_api.h.
for (const char* name : {"HSA_OVERRIDE_GFX_VERSION", "HSA_OVERRIDE_GFX_VERSION_0"})
if (std::getenv(name)) throw std::runtime_error(std::string("unset ") + name + "; d4r does not support architecture overrides");
d4r::diag::HipApi hip((dir / L"hip").string());
if (hip.hipInit(0) != hipSuccess) throw std::runtime_error("the AMD HIP runtime found no usable GPU; update the AMD graphics driver");
int count = 0; (void)hip.hipGetDeviceCount(&count);
std::string found, detected;
for (int i = 0; i < count; ++i) {
hipDeviceProp_t props{};
if (hip.hipGetDeviceProperties(&props, i) != hipSuccess) continue;
const auto arch = d4r::diag::gpu_architecture(props.gcnArchName);
detected += (detected.empty() ? "" : ", ") + arch + " (" + props.name + ")";
if (luid && std::memcmp(props.luid, luid, sizeof(*luid))) continue;
if (!luid && !std::filesystem::exists(dir / (L"_nvngx_" + d4r::diag::wide(arch) + L".dll"))) continue;
if (!found.empty() && found != arch) throw std::runtime_error("several GPUs with different architectures; start the game once DLSS selects one");
found = arch;
}
if (found.empty()) throw std::runtime_error("no supported AMD GPU for this game's adapter. Detected: " +
(detected.empty() ? std::string("none") : detected) + ". This release supports: " + supported_list(dir) + ".");
const auto path = dir / (L"_nvngx_" + d4r::diag::wide(found) + L".dll");
if (!std::filesystem::exists(path))
throw std::runtime_error("this release has no build for your GPU (" + found + "). It supports: " + supported_list(dir) + ".");
shim = LoadLibraryExW(path.c_str(), nullptr, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
if (!shim) {
selectionFailed = true; // A broken install will not fix itself during this session.
throw std::runtime_error("cannot load " + d4r::diag::utf8(path.c_str()) + " (Win32 error " + std::to_string(GetLastError()) + ")");
}
} catch (const std::exception& error) {
// An adapter without a matching GPU (an iGPU queried first) must not
// prevent selecting the discrete GPU on a later call.
report(error.what());
}
return shim;
}
template <typename Fn> Fn resolve(const char* name, const LUID* luid = nullptr) {
HMODULE module = select(luid);
return module ? reinterpret_cast<Fn>(reinterpret_cast<void*>(GetProcAddress(module, name))) : nullptr;
}
LUID device_luid(ID3D12Device* device) { return d4r::win::adapter_luid(device); }
}
#define API extern "C" __declspec(dllexport)
// Discovery marker for OptiScaler: no GPU or runtime initialization.
API unsigned d4r_WindowsBackendVersion() { return 1; }
API unsigned NVSDK_NGX_D3D12_Init_Ext(unsigned long long app, const wchar_t* data, ID3D12Device* device, unsigned sdk, const void* info) {
if (!device) return 0xBAD00005;
const LUID luid = device_luid(device);
auto fn = resolve<decltype(&NVSDK_NGX_D3D12_Init_Ext)>("NVSDK_NGX_D3D12_Init_Ext", &luid);
return fn ? fn(app, data, device, sdk, info) : failure;
}
API unsigned NVSDK_NGX_D3D12_Init(unsigned long long app, const wchar_t* data, ID3D12Device* device, const void* info, unsigned sdk) {
if (!device) return 0xBAD00005;
const LUID luid = device_luid(device);
auto fn = resolve<decltype(&NVSDK_NGX_D3D12_Init)>("NVSDK_NGX_D3D12_Init", &luid);
return fn ? fn(app, data, device, info, sdk) : failure;
}
API unsigned NVSDK_NGX_D3D12_Init_ProjectID(const char* project, int engine, const char* version, const wchar_t* data, ID3D12Device* device, unsigned sdk, const void* info) {
if (!device) return 0xBAD00005;
const LUID luid = device_luid(device);
auto fn = resolve<decltype(&NVSDK_NGX_D3D12_Init_ProjectID)>("NVSDK_NGX_D3D12_Init_ProjectID", &luid);
return fn ? fn(project, engine, version, data, device, sdk, info) : failure;
}
API unsigned NVSDK_NGX_D3D12_GetFeatureRequirements(IDXGIAdapter* adapter, const d4r::ngx::FeatureDiscoveryInfo* info,
d4r::ngx::FeatureRequirement* requirements) {
if (!adapter || !info || !requirements) return 0xBAD00005;
DXGI_ADAPTER_DESC desc{};
if (FAILED(adapter->GetDesc(&desc))) return failure;
auto fn = resolve<decltype(&NVSDK_NGX_D3D12_GetFeatureRequirements)>("NVSDK_NGX_D3D12_GetFeatureRequirements", &desc.AdapterLuid);
if (fn) return fn(adapter, info, requirements);
// Same answer as the shim for an adapter without a matching HIP device.
*requirements = {}; requirements->FeatureSupported = unsupported;
std::strcpy(requirements->MinOSVersion, "10.0.22000.0");
return 1;
}
API unsigned NVSDK_NGX_D3D12_AllocateParameters(void** p) {
auto fn = resolve<decltype(&NVSDK_NGX_D3D12_AllocateParameters)>("NVSDK_NGX_D3D12_AllocateParameters");
return fn ? fn(p) : failure;
}
API unsigned NVSDK_NGX_D3D12_GetCapabilityParameters(void** p) {
auto fn = resolve<decltype(&NVSDK_NGX_D3D12_GetCapabilityParameters)>("NVSDK_NGX_D3D12_GetCapabilityParameters");
return fn ? fn(p) : failure;
}
API unsigned NVSDK_NGX_D3D12_GetParameters(void** p) {
auto fn = resolve<decltype(&NVSDK_NGX_D3D12_GetParameters)>("NVSDK_NGX_D3D12_GetParameters");
return fn ? fn(p) : failure;
}
API unsigned NVSDK_NGX_D3D12_DestroyParameters(void* p) {
auto fn = resolve<decltype(&NVSDK_NGX_D3D12_DestroyParameters)>("NVSDK_NGX_D3D12_DestroyParameters");
return fn ? fn(p) : failure;
}
API unsigned NVSDK_NGX_D3D12_GetScratchBufferSize(unsigned feature, const void* p, size_t* size) {
auto fn = resolve<decltype(&NVSDK_NGX_D3D12_GetScratchBufferSize)>("NVSDK_NGX_D3D12_GetScratchBufferSize");
return fn ? fn(feature, p, size) : failure;
}
API unsigned NVSDK_NGX_D3D12_CreateFeature(ID3D12GraphicsCommandList* list, unsigned feature, void* p, void** out) {
auto fn = resolve<decltype(&NVSDK_NGX_D3D12_CreateFeature)>("NVSDK_NGX_D3D12_CreateFeature");
return fn ? fn(list, feature, p, out) : failure;
}
API unsigned d4r_D3D12_EvaluateAtBoundary(ID3D12CommandQueue* queue, void* handle, void* p) {
auto fn = resolve<decltype(&d4r_D3D12_EvaluateAtBoundary)>("d4r_D3D12_EvaluateAtBoundary");
return fn ? fn(queue, handle, p) : failure;
}
API unsigned NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCommandList* list, const void* handle, void* p, void* callback) {
auto fn = resolve<decltype(&NVSDK_NGX_D3D12_EvaluateFeature)>("NVSDK_NGX_D3D12_EvaluateFeature");
return fn ? fn(list, handle, p, callback) : failure;
}
API unsigned NVSDK_NGX_D3D12_EvaluateFeature_C(ID3D12GraphicsCommandList* list, const void* handle, void* p, void* callback) {
auto fn = resolve<decltype(&NVSDK_NGX_D3D12_EvaluateFeature_C)>("NVSDK_NGX_D3D12_EvaluateFeature_C");
return fn ? fn(list, handle, p, callback) : failure;
}
API unsigned NVSDK_NGX_D3D12_ReleaseFeature(void* handle) {
auto fn = resolve<decltype(&NVSDK_NGX_D3D12_ReleaseFeature)>("NVSDK_NGX_D3D12_ReleaseFeature");
return fn ? fn(handle) : failure;
}
API unsigned NVSDK_NGX_D3D12_Shutdown() {
// Nothing to shut down if no shim was ever selected.
{ std::lock_guard<std::mutex> lock(selectMutex); if (!shim) return 1; }
auto fn = resolve<decltype(&NVSDK_NGX_D3D12_Shutdown)>("NVSDK_NGX_D3D12_Shutdown");
return fn ? fn() : failure;
}
API unsigned NVSDK_NGX_D3D12_Shutdown1(ID3D12Device* device) {
{ std::lock_guard<std::mutex> lock(selectMutex); if (!shim) return 1; }
auto fn = resolve<decltype(&NVSDK_NGX_D3D12_Shutdown1)>("NVSDK_NGX_D3D12_Shutdown1");
return fn ? fn(device) : failure;
}
+17 -3
View File
@@ -1,5 +1,6 @@
// Native Windows backend. The Linux/Wine implementation remains separate. // Native Windows backend. The Linux/Wine implementation remains separate.
#include "ngx_windows_runtime.h" #include "ngx_windows_runtime.h"
#include "player_config.h"
#include <dxgi.h> #include <dxgi.h>
#include <unordered_map> #include <unordered_map>
#include <functional> #include <functional>
@@ -59,6 +60,7 @@ API unsigned d4r_WindowsBackendVersion() { return 1; }
API unsigned NVSDK_NGX_D3D12_Init_Ext(unsigned long long app, const wchar_t* data, ID3D12Device* device, unsigned sdk, const void* info) { API unsigned NVSDK_NGX_D3D12_Init_Ext(unsigned long long app, const wchar_t* data, ID3D12Device* device, unsigned sdk, const void* info) {
std::lock_guard<std::mutex> lock(apiMutex); std::lock_guard<std::mutex> lock(apiMutex);
return call([&] { return call([&] {
d4r::win::player::ensure_environment();
if (!runtime) runtime = std::make_shared<d4r::win::Runtime>(device, app, data, sdk, static_cast<const d4r::ngx::FeatureCommonInfo*>(info)); if (!runtime) runtime = std::make_shared<d4r::win::Runtime>(device, app, data, sdk, static_cast<const d4r::ngx::FeatureCommonInfo*>(info));
if (const char* backend = std::getenv("D4R_D3D12_COMMAND_BACKEND"); backend && std::string(backend) == "1") if (const char* backend = std::getenv("D4R_D3D12_COMMAND_BACKEND"); backend && std::string(backend) == "1")
d4r::win::commands::install(device); d4r::win::commands::install(device);
@@ -72,6 +74,7 @@ API unsigned NVSDK_NGX_D3D12_Init_ProjectID(const char* project, int engine, con
std::lock_guard<std::mutex> lock(apiMutex); std::lock_guard<std::mutex> lock(apiMutex);
return call([&] { return call([&] {
if (!project || !*project) return invalid; if (!project || !*project) return invalid;
d4r::win::player::ensure_environment();
d4r::ngx::ProjectIdentity identity{project, engine, version ? version : ""}; d4r::ngx::ProjectIdentity identity{project, engine, version ? version : ""};
if (!runtime) runtime = std::make_shared<d4r::win::Runtime>(device, 0, data, sdk, static_cast<const d4r::ngx::FeatureCommonInfo*>(info), &identity); if (!runtime) runtime = std::make_shared<d4r::win::Runtime>(device, 0, data, sdk, static_cast<const d4r::ngx::FeatureCommonInfo*>(info), &identity);
if (const char* backend = std::getenv("D4R_D3D12_COMMAND_BACKEND"); backend && std::string(backend) == "1") d4r::win::commands::install(device); if (const char* backend = std::getenv("D4R_D3D12_COMMAND_BACKEND"); backend && std::string(backend) == "1") d4r::win::commands::install(device);
@@ -84,13 +87,17 @@ API unsigned NVSDK_NGX_D3D12_GetFeatureRequirements(IDXGIAdapter* adapter, const
return call([&] { return call([&] {
*requirements = {}; requirements->FeatureSupported = 16; // Unsupported feature. *requirements = {}; requirements->FeatureSupported = 16; // Unsupported feature.
if (info->FeatureID != 1) return 1u; if (info->FeatureID != 1) return 1u;
d4r::win::player::ensure_environment();
DXGI_ADAPTER_DESC desc{}; d4r::win::dx(adapter->GetDesc(&desc), "NGX requirements adapter"); DXGI_ADAPTER_DESC desc{}; d4r::win::dx(adapter->GetDesc(&desc), "NGX requirements adapter");
// OptiScaler can override the DXGI vendor ID. The physical HIP // OptiScaler can override the DXGI vendor ID. The physical HIP
// architecture and full adapter LUID are the authoritative identity. // 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{};
requirements->FeatureSupported = std::memcmp(&desc.AdapterLuid,props.luid,sizeof(LUID)) ? 4u : 0u; const int ordinal = hip.select_architecture(-1,props,&desc.AdapterLuid,true);
requirements->FeatureSupported = ordinal < 0 ? 4u : 0u;
std::strcpy(requirements->MinOSVersion,"10.0.22000.0"); 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 adapter_luid=%08lx:%08lx hip_ordinal=%d\n",
info->FeatureID,requirements->FeatureSupported,d4r::diag::HipApi::target_arch(),
static_cast<unsigned long>(desc.AdapterLuid.HighPart),static_cast<unsigned long>(desc.AdapterLuid.LowPart),ordinal);
return 1u; return 1u;
}); });
} }
@@ -113,6 +120,7 @@ API unsigned NVSDK_NGX_D3D12_CreateFeature(ID3D12GraphicsCommandList*, unsigned
std::lock_guard<std::mutex> lock(apiMutex); std::lock_guard<std::mutex> lock(apiMutex);
return call([&] { return call([&] {
if (!runtime || feature != 1 || !p || !out) return invalid; if (!runtime || feature != 1 || !p || !out) return invalid;
d4r::win::player::apply_preset(p);
auto value = std::make_shared<d4r::win::Feature>(runtime, p); auto value = std::make_shared<d4r::win::Feature>(runtime, p);
auto handle = std::make_unique<Handle>(); handle->Id = nextId++; auto handle = std::make_unique<Handle>(); handle->Id = nextId++;
features.emplace(handle.get(), std::move(value)); *out = handle.release(); return 1u; features.emplace(handle.get(), std::move(value)); *out = handle.release(); return 1u;
@@ -138,7 +146,13 @@ API unsigned NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCommandList* list, co
}; };
auto snapshot = std::make_shared<Snapshot>(); auto snapshot = std::make_shared<Snapshot>();
if (!snapshot->parameters) return failure; if (!snapshot->parameters) return failure;
d4r::win::player::apply_preset(p);
d4r::ngx::copy_create(p, snapshot->parameters); d4r::ngx::copy_frame(p, snapshot->parameters); d4r::ngx::copy_create(p, snapshot->parameters); d4r::ngx::copy_frame(p, snapshot->parameters);
// NVIDIA's library stamps "DLSS SDK - DO NOT DISTRIBUTE" over its output unless the SDK's
// Disable.Watermark parameter is set (the Linux shim sets it too). The game's own value is
// not used. [DLSS] ShowWatermark = true (D4R_SHIM_WATERMARK=1) keeps the stamp.
static const bool watermark = [] { const char* v = std::getenv("D4R_SHIM_WATERMARK"); return v && v[0] == '1'; }();
d4r_ngx_set_int(snapshot->parameters, "Disable.Watermark", watermark ? 0 : 1);
const char* names[] = {"Color", "Depth", "MotionVectors", "Output", "ExposureTexture"}; const char* names[] = {"Color", "Depth", "MotionVectors", "Output", "ExposureTexture"};
const char* states[] = {"D4R.Color.State", "D4R.Depth.State", "D4R.Motion.State", "D4R.Output.State", "D4R.Exposure.State"}; const char* states[] = {"D4R.Color.State", "D4R.Depth.State", "D4R.Motion.State", "D4R.Output.State", "D4R.Exposure.State"};
for (unsigned i = 0; i < 5; ++i) { for (unsigned i = 0; i < 5; ++i) {
+37 -2
View File
@@ -122,13 +122,17 @@ struct Args {
std::string ngx_core, dlss_dll; std::string ngx_core, dlss_dll;
std::string nvapi_dll; std::string nvapi_dll;
std::string ngx_abi = "driver"; std::string ngx_abi = "driver";
unsigned long long ngx_app_id = 241534723ull;
std::string ngx_frontend = "d4r"; std::string ngx_frontend = "d4r";
std::string ngx_mode = "init"; std::string ngx_mode = "init";
unsigned preset = 11; unsigned preset = 11;
unsigned ngx_create_flags = 0; unsigned ngx_create_flags = 0;
unsigned output_width = 512, output_height = 288; unsigned output_width = 512, output_height = 288;
unsigned input_width = 0, input_height = 0;
std::string fixture_dir; std::string fixture_dir;
std::string benchmark_module; std::string benchmark_module;
std::string benchmark_timing = "events";
unsigned benchmark_batch = 1;
std::string output_dir; std::string output_dir;
std::string kernel_name = "enc1"; std::string kernel_name = "enc1";
unsigned iterations = 32; unsigned iterations = 32;
@@ -157,6 +161,12 @@ struct Args {
else if (key == "--dlss-dll") dlss_dll = value; else if (key == "--dlss-dll") dlss_dll = value;
else if (key == "--nvapi-dll") nvapi_dll = value; else if (key == "--nvapi-dll") nvapi_dll = value;
else if (key == "--ngx-abi") ngx_abi = value; else if (key == "--ngx-abi") ngx_abi = value;
else if (key == "--ngx-app-id") {
size_t end = 0;
if (value.empty() || value[0] == '-') throw std::runtime_error("Invalid NGX application ID");
ngx_app_id = std::stoull(value, &end);
if (end != value.size()) throw std::runtime_error("Invalid NGX application ID");
}
else if (key == "--ngx-frontend") { else if (key == "--ngx-frontend") {
if (value != "d4r" && value != "optiscaler") throw std::runtime_error("ngx-frontend must be d4r or optiscaler"); if (value != "d4r" && value != "optiscaler") throw std::runtime_error("ngx-frontend must be d4r or optiscaler");
ngx_frontend = value; ngx_frontend = value;
@@ -178,14 +188,37 @@ struct Args {
output_height < 128 || output_height > 2160 || output_width % 2 || output_height % 2) output_height < 128 || output_height > 2160 || output_width % 2 || output_height % 2)
throw std::runtime_error("Output resolution must be even and within 128x128..4096x2160"); throw std::runtime_error("Output resolution must be even and within 128x128..4096x2160");
} }
else if (key == "--ngx-input-resolution") {
const auto separator = value.find('x');
if (separator == std::string::npos) throw std::runtime_error("Input resolution must be WIDTHxHEIGHT");
size_t wend = 0, hend = 0;
const auto width = std::stoul(value.substr(0, separator), &wend);
const auto height = std::stoul(value.substr(separator + 1), &hend);
if (wend != separator || hend != value.size() - separator - 1 || width < 64 || width > 4096 ||
height < 64 || height > 2160)
throw std::runtime_error("Input resolution must be within 64x64..4096x2160");
input_width = static_cast<unsigned>(width); input_height = static_cast<unsigned>(height);
}
else if (key == "--preset") { else if (key == "--preset") {
size_t end = 0; size_t end = 0;
preset = static_cast<unsigned>(std::stoul(value, &end)); preset = static_cast<unsigned>(std::stoul(value, &end));
if (end != value.size() || (preset != 5 && preset != 11 && preset != 13)) if (end != value.size() || (preset != 5 && preset != 11 && preset != 12 && preset != 13))
throw std::runtime_error("preset must be 5 (E), 11 (K) or 13 (M)"); throw std::runtime_error("preset must be 5 (E), 11 (K), 12 (L) or 13 (M)");
} }
else if (key == "--fixture-dir") fixture_dir = value; else if (key == "--fixture-dir") fixture_dir = value;
else if (key == "--benchmark-module") benchmark_module = value; else if (key == "--benchmark-module") benchmark_module = value;
else if (key == "--benchmark-timing") {
if (value != "events" && value != "dispatch")
throw std::runtime_error("benchmark-timing must be events or dispatch");
benchmark_timing = value;
}
else if (key == "--benchmark-batch") {
size_t end = 0;
const auto parsed = std::stoul(value, &end);
if (end != value.size() || parsed < 1 || parsed > 256)
throw std::runtime_error("benchmark-batch must be 1..256");
benchmark_batch = static_cast<unsigned>(parsed);
}
else if (key == "--output-dir") output_dir = value; else if (key == "--output-dir") output_dir = value;
else if (key == "--kernel-name") kernel_name = value; else if (key == "--kernel-name") kernel_name = value;
else if (key == "--iterations") { else if (key == "--iterations") {
@@ -200,6 +233,8 @@ struct Args {
} else throw std::runtime_error("Unknown option " + key); } else throw std::runtime_error("Unknown option " + key);
} }
if (hip_root.empty()) throw std::runtime_error("--hip-root is required"); if (hip_root.empty()) throw std::runtime_error("--hip-root is required");
if (input_width > output_width || input_height > output_height)
throw std::runtime_error("Input resolution cannot exceed output resolution");
} }
}; };
class Library { class Library {
+14
View File
@@ -0,0 +1,14 @@
# NVIDIA DLLs the Windows backend accepts, by exact SHA256. NVIDIA DLLs are
# never shipped or committed; players copy their own.
# dlss = nvngx_dlss.dll (d4r\nvngx_dlss.dll)
# ngx = _nvngx.dll, NVIDIA's NGX core from a display driver (d4r\ngx\_nvngx.dll)
# status: validated = tested on Windows hardware by the project; anything else is
# accepted but reported as having had less testing in d4r\d4r_nvngx.log and d4r-check.ps1.
# Native kernels only replace DLSS code whose PTX identity matches; anything else
# falls back to ZLUDA's translated kernels, so an accepted DLL is never run with a
# wrong native kernel.
# kind sha256 version status
dlss 3975567B8943C53ACCE397F2B72380092F84F162D00B0D2C7D08A1025C563983 310.9.1 validated
dlss 1CDFDC957CB7FC9805500CA6793F607EF2D4DBD7C967FEAC70ACA9CAF382D0C0 310.7.0 unvalidated
ngx 66767018C36B3BAB46398DADE3ADF173DAA3730FDA75965689EA848C9BC4E79B 32.0.16.1714 validated
ngx 0765FB7FB2B8BE28EE1F0226679D7B2636A69CACCE5A23891984978F690D81A4 32.0.15.9636 unvalidated
+3 -2
View File
@@ -52,8 +52,9 @@ def generate(source):
slot = 9 # IUnknown(3), ID3D12Object(4), DeviceChild(1), CommandList(1). slot = 9 # IUnknown(3), ID3D12Object(4), DeviceChild(1), CommandList(1).
for version, interface, methods in sorted(classes): for version, interface, methods in sorted(classes):
for result, name, arguments in methods: for result, name, arguments in methods:
result = ' '.join(result.split()) # MinGW WINBOOL and Microsoft SDK BOOL have the same ABI.
arguments = ' '.join(arguments.split()) 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 [] declarations = [arg.strip() for arg in arguments.split(',')] if arguments else []
names = [] names = []
for declaration in declarations: for declaration in declarations:
+2 -2
View File
@@ -30,7 +30,7 @@ int main(int argc, char** argv)
if (args.module.empty()) throw std::runtime_error("--module is required"); if (args.module.empty()) throw std::runtime_error("--module is required");
HipApi hip(args.hip_root); HipApi hip(args.hip_root);
hipDeviceProp_t properties{}; hipDeviceProp_t properties{};
hip.select_gfx1201(args.device, properties); hip.select_architecture(args.device, properties);
hipModule_t module = nullptr; hipModule_t module = nullptr;
hip.check(hip.hipModuleLoad(&module, args.module.c_str()), "hipModuleLoad(WMMA)"); hip.check(hip.hipModuleLoad(&module, args.module.c_str()), "hipModuleLoad(WMMA)");
struct ModuleCleanup { 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); args.iterations, max_absolute_error, max_relative_error);
return 0; return 0;
} catch (const std::exception& error) { } catch (const std::exception& error) {
+6
View File
@@ -0,0 +1,6 @@
{
"schema": 1,
"architecture": "@D4R_GPU_ARCH@",
"wavefrontSize": 32,
"validation": "Compilation is not hardware validation; run the GPU diagnostics."
}
+16
View File
@@ -0,0 +1,16 @@
{
"schema": 1,
"targets": [
{"architecture":"gfx1100","elfMachine":65,"isaMajor":11,"family":"RDNA3"},
{"architecture":"gfx1101","elfMachine":70,"isaMajor":11,"family":"RDNA3"},
{"architecture":"gfx1102","elfMachine":71,"isaMajor":11,"family":"RDNA3"},
{"architecture":"gfx1103","elfMachine":68,"isaMajor":11,"family":"RDNA3"},
{"architecture":"gfx1150","elfMachine":67,"isaMajor":11,"family":"RDNA3.5"},
{"architecture":"gfx1151","elfMachine":74,"isaMajor":11,"family":"RDNA3.5"},
{"architecture":"gfx1152","elfMachine":85,"isaMajor":11,"family":"RDNA3.5"},
{"architecture":"gfx1153","elfMachine":88,"isaMajor":11,"family":"RDNA3.5"},
{"architecture":"gfx1154","elfMachine":87,"isaMajor":11,"family":"RDNA3.5"},
{"architecture":"gfx1200","elfMachine":72,"isaMajor":12,"family":"RDNA4"},
{"architecture":"gfx1201","elfMachine":78,"isaMajor":12,"family":"RDNA4"}
]
}
+57
View File
@@ -0,0 +1,57 @@
#include "hip_api.h"
#include <fstream>
#include <sstream>
#include <iomanip>
static std::string json_string(const std::string& value) {
std::ostringstream out; out << '"';
for (unsigned char c : value) {
if (c == '"' || c == '\\') out << '\\' << char(c);
else if (c < 32) out << "\\u" << std::hex << std::setw(4) << std::setfill('0') << unsigned(c);
else out << char(c);
}
out << '"'; return out.str();
}
int main(int argc, char** argv) {
using namespace d4r::diag;
start();
try {
Args args(argc, argv);
if (args.output_dir.empty()) throw std::runtime_error("--output-dir is required");
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);
HipApi hip(args.hip_root);
hip.check(hip.hipInit(0), "hipInit(inventory)");
int count=0, version=0, driver=0;
hip.check(hip.hipRuntimeGetVersion(&version), "hipRuntimeGetVersion");
hip.check(hip.hipDriverGetVersion(&driver), "hipDriverGetVersion");
std::printf("GPU_INVENTORY_RUNTIME runtime=%d driver_api=%d headers=%d.%d.%d sizeof(properties)=%zu\n",
version,driver,HIP_VERSION_MAJOR,HIP_VERSION_MINOR,HIP_VERSION_PATCH,sizeof(hipDeviceProp_t));
hip.check(hip.hipGetDeviceCount(&count), "hipGetDeviceCount");
std::ostringstream json;
json << "{\"schema\":1,\"runtimeVersion\":" << version << ",\"driverApiVersion\":" << driver << ",\"compiledTarget\":"
<< json_string(D4R_TARGET_ARCH) << ",\"gpuWorkExecuted\":false,\"devices\":[";
for (int i=0; i<count; ++i) {
hipDeviceProp_t p{}; hip.check(hip.hipGetDeviceProperties(&p,i), "hipGetDevicePropertiesR0600");
const auto arch=gpu_architecture(p.gcnArchName);
std::printf("GPU_INVENTORY ordinal=%d name=%s gcnArchName=%s wave=%d build_eligible=%d\n",
i,p.name,p.gcnArchName,p.warpSize,supported_gpu_target(arch));
if (i) json << ',';
json << "{\"ordinal\":" << i << ",\"name\":" << json_string(p.name)
<< ",\"gcnArchName\":" << json_string(p.gcnArchName) << ",\"architecture\":" << json_string(arch)
<< ",\"wavefrontSize\":" << p.warpSize << ",\"vramBytes\":" << p.totalGlobalMem
<< ",\"buildEligible\":" << (supported_gpu_target(arch) ? "true" : "false") << '}';
}
json << "]}\n";
std::filesystem::create_directories(wide(args.output_dir));
std::ofstream file(std::filesystem::path(wide(args.output_dir))/L"gpu-inventory.json",std::ios::binary);
file << json.str(); file.close();
if (!file) throw std::runtime_error("Cannot write GPU inventory");
if (!count) throw std::runtime_error("HIP reports no devices");
loaded_modules();
std::printf("PASS GPU_INVENTORY devices=%d gpu_work_executed=0\n",count);
return 0;
} catch (const std::exception& error) {
std::fprintf(stderr,"FAIL GPU_INVENTORY %s\n",error.what()); loaded_modules(); return 4;
}
}
Loaded 100 of 125 files, more files were not shown because too many files have changed in this diff. Show more