Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0c881985b | ||
|
|
774efac524 | ||
|
|
df415ec828 | ||
|
|
65ab1460a8 | ||
|
|
7ab56d42fc | ||
|
|
6da1e79f9b | ||
|
|
bea15d3754 | ||
|
|
17121ccbf6 | ||
|
|
21ed0a8c88 | ||
|
|
3d5f42865f | ||
|
|
b6b6d91ed9 | ||
|
|
3fa963fd1b | ||
|
|
98de8e3a3e | ||
|
|
8f7058d2ba | ||
|
|
4f008b3ed1 | ||
|
|
96650ed29d | ||
|
|
a2a4a7e765 | ||
|
|
28832eef6c | ||
|
|
bf4923aa4c | ||
|
|
9dece89599 | ||
|
|
973eb5ecd2 | ||
|
|
4f3b3a9fe6 | ||
|
|
77a5c008f9 | ||
|
|
42394953de | ||
|
|
7eaeb8c3db | ||
|
|
eba26c3a76 | ||
|
|
cf43794450 | ||
|
|
541c9838f7 | ||
|
|
caecad0897 | ||
|
|
0b471be024 | ||
|
|
f66cccaf28 | ||
|
|
df44d8aeae | ||
|
|
0c18df6253 | ||
|
|
74aad12938 | ||
|
|
73ee2e5ecc | ||
|
|
d556747bad | ||
|
|
40d9a55e10 | ||
|
|
74395b6aef | ||
|
|
5d33bddcd0 | ||
|
|
19d859e43d | ||
|
|
e4c1c87b66 | ||
|
|
e14b4cb54c | ||
|
|
27d6a1230a | ||
|
|
6ddf740877 | ||
|
|
e9eec6d4e0 | ||
|
|
312c6bb50d | ||
|
|
b645ef9096 | ||
|
|
63607298ea | ||
|
|
b8d801ae4c | ||
|
|
e15f6a73a6 | ||
|
|
2db93adb1a | ||
|
|
5592b7a921 | ||
|
|
a0c36a6d6f | ||
|
|
3c28a82e34 | ||
|
|
64472e5a61 | ||
|
|
2dea066d6a | ||
|
|
1b5d4c9db7 | ||
|
|
6760294f60 | ||
|
|
64669c2c36 | ||
|
|
239a644270 | ||
|
|
e95e061e81 |
No files matched your search
@@ -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
|
||||||
@@ -15,6 +15,9 @@
|
|||||||
/traces/
|
/traces/
|
||||||
/build/
|
/build/
|
||||||
/dist/
|
/dist/
|
||||||
|
/.tools/
|
||||||
|
/external/
|
||||||
|
/test-results/
|
||||||
*.trace
|
*.trace
|
||||||
*.dump
|
*.dump
|
||||||
*.log
|
*.log
|
||||||
|
|||||||
+324
@@ -0,0 +1,324 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.24)
|
||||||
|
project(d4r_windows_diagnostics LANGUAGES C CXX)
|
||||||
|
if(NOT WIN32 OR NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
message(FATAL_ERROR "These diagnostics require native Windows x64")
|
||||||
|
endif()
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(D4R_HIP_ROOT "$ENV{HIP_PATH}" CACHE PATH "One stable HIP SDK or TheRock installation")
|
||||||
|
if(NOT D4R_HIP_ROOT AND EXISTS "C:/Program Files/AMD/ROCm/7.2")
|
||||||
|
set(D4R_HIP_ROOT "C:/Program Files/AMD/ROCm/7.2")
|
||||||
|
endif()
|
||||||
|
if(NOT EXISTS "${D4R_HIP_ROOT}/include/hip/hip_runtime_api.h")
|
||||||
|
message(FATAL_ERROR "Set D4R_HIP_ROOT to a Windows HIP SDK (headers and bin/clang++.exe required)")
|
||||||
|
endif()
|
||||||
|
find_program(D4R_HIP_CLANG NAMES clang++.exe PATHS "${D4R_HIP_ROOT}/bin" "${D4R_HIP_ROOT}/lib/llvm/bin" NO_DEFAULT_PATH REQUIRED)
|
||||||
|
# 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}"
|
||||||
|
COMMAND "${D4R_HIP_CLANG}" -x hip --offload-arch=${D4R_GPU_ARCH}
|
||||||
|
--offload-device-only --no-gpu-bundle-output -mno-wavefrontsize64
|
||||||
|
-nogpuinc -nogpulib -O2 "${CMAKE_CURRENT_SOURCE_DIR}/tools/windows/probe_kernel.hip"
|
||||||
|
-o "${probe_object}"
|
||||||
|
DEPENDS tools/windows/probe_kernel.hip VERBATIM)
|
||||||
|
add_custom_target(d4r_probe_code_object ALL DEPENDS "${probe_object}")
|
||||||
|
set(pixel_object "${CMAKE_CURRENT_BINARY_DIR}/pixel_convert_${D4R_GPU_ARCH}.hsaco")
|
||||||
|
add_custom_command(OUTPUT "${pixel_object}"
|
||||||
|
COMMAND "${D4R_HIP_CLANG}" -x hip -std=c++17 --offload-arch=${D4R_GPU_ARCH}
|
||||||
|
--offload-device-only --no-gpu-bundle-output -mno-wavefrontsize64
|
||||||
|
-nogpuinc -nogpulib -O2 "${CMAKE_CURRENT_SOURCE_DIR}/tools/windows/pixel_convert_gfx1201.hip"
|
||||||
|
-o "${pixel_object}"
|
||||||
|
DEPENDS tools/windows/pixel_convert_gfx1201.hip tools/windows/pixel_format.h kernels/common/hip_device_minimal.h VERBATIM)
|
||||||
|
add_custom_target(d4r_pixel_code_object ALL DEPENDS "${pixel_object}")
|
||||||
|
set(wmma_object "${CMAKE_CURRENT_BINARY_DIR}/wmma_${D4R_GPU_ARCH}.hsaco")
|
||||||
|
add_custom_command(OUTPUT "${wmma_object}"
|
||||||
|
COMMAND "${D4R_HIP_CLANG}" -x hip --offload-arch=${D4R_GPU_ARCH}
|
||||||
|
--offload-device-only --no-gpu-bundle-output -mno-wavefrontsize64
|
||||||
|
-nogpuinc -nogpulib -O2 ${d4r_device_flags} "${CMAKE_CURRENT_SOURCE_DIR}/tools/windows/wmma_gfx1201.hip"
|
||||||
|
-o "${wmma_object}"
|
||||||
|
DEPENDS tools/windows/wmma_gfx1201.hip kernels/common/wmma_backend.h
|
||||||
|
kernels/common/wmma_layout.h kernels/common/wave32_exchange.h VERBATIM)
|
||||||
|
add_custom_target(d4r_wmma_code_object ALL DEPENDS "${wmma_object}")
|
||||||
|
# K/M objects stay in experimental/, outside the ZLUDA native override
|
||||||
|
# directory, until each layer and then the complete network pass replay.
|
||||||
|
# M uses the existing FP16 widening baseline; native FP8 is not enabled here.
|
||||||
|
set(native_objects)
|
||||||
|
option(D4R_K_FP16_BASELINE "Round every K16 accumulation step like NVIDIA f16 WMMA" ON)
|
||||||
|
option(D4R_M_FP16_BASELINE "Preserve M e4m3 quantisation and f16 accumulation per K32" ON)
|
||||||
|
foreach(family k m)
|
||||||
|
file(GLOB native_sources CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/kernels/${family}/*.hip")
|
||||||
|
file(GLOB native_headers CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/kernels/${family}/*.h")
|
||||||
|
foreach(source IN LISTS native_sources)
|
||||||
|
get_filename_component(kernel_name "${source}" NAME_WE)
|
||||||
|
set(native_object "${CMAKE_CURRENT_BINARY_DIR}/${kernel_name}_${D4R_GPU_ARCH}.hsaco")
|
||||||
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${source}")
|
||||||
|
file(READ "${source}" kernel_source)
|
||||||
|
string(REGEX MATCH "// d4r-build-flags: ([^\r\n]+)" flags_line "${kernel_source}")
|
||||||
|
set(native_flags)
|
||||||
|
if(family STREQUAL "k" AND D4R_K_FP16_BASELINE)
|
||||||
|
list(APPEND native_flags -DD4R_K_FP16_BASELINE)
|
||||||
|
endif()
|
||||||
|
if(family STREQUAL "m" AND D4R_M_FP16_BASELINE)
|
||||||
|
list(APPEND native_flags -DSWIN_EXACT -DSWIN_EXACT_PV)
|
||||||
|
endif()
|
||||||
|
if(flags_line)
|
||||||
|
separate_arguments(source_flags NATIVE_COMMAND "${CMAKE_MATCH_1}")
|
||||||
|
list(APPEND native_flags ${source_flags})
|
||||||
|
endif()
|
||||||
|
add_custom_command(OUTPUT "${native_object}"
|
||||||
|
COMMAND "${D4R_HIP_CLANG}" -x hip -std=c++17 --offload-arch=${D4R_GPU_ARCH}
|
||||||
|
--offload-device-only --no-gpu-bundle-output -mno-wavefrontsize64
|
||||||
|
-nogpuinc -nogpulib -O3 -DD4R_DEVICE_ONLY_MINIMAL ${d4r_device_flags} ${native_flags}
|
||||||
|
"${source}" -o "${native_object}"
|
||||||
|
DEPENDS "${source}" ${native_headers} kernels/common/wmma_layout.h kernels/common/wave32_exchange.h
|
||||||
|
kernels/common/hip_device_minimal.h VERBATIM)
|
||||||
|
list(APPEND native_objects "${native_object}")
|
||||||
|
install(FILES "${native_object}" DESTINATION "experimental/${family}")
|
||||||
|
endforeach()
|
||||||
|
endforeach()
|
||||||
|
add_custom_target(d4r_native_compile ALL DEPENDS ${native_objects})
|
||||||
|
# Retain the old manual build target for existing scripts.
|
||||||
|
add_custom_target(d4r_native_gfx1201_compile DEPENDS d4r_native_compile)
|
||||||
|
foreach(probe 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)
|
||||||
|
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_link_libraries(d4r_${probe} PRIVATE version dbghelp)
|
||||||
|
if(MINGW)
|
||||||
|
target_link_options(d4r_${probe} PRIVATE -static)
|
||||||
|
endif()
|
||||||
|
if(MSVC)
|
||||||
|
target_compile_options(d4r_${probe} PRIVATE /W4 /EHsc)
|
||||||
|
set_property(TARGET d4r_${probe} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||||
|
else()
|
||||||
|
target_compile_options(d4r_${probe} PRIVATE -Wall -Wextra)
|
||||||
|
endif()
|
||||||
|
install(TARGETS d4r_${probe} RUNTIME DESTINATION bin)
|
||||||
|
endforeach()
|
||||||
|
foreach(probe binary16_probe gpu_target_probe)
|
||||||
|
add_executable(d4r_${probe} tools/windows/${probe}.cpp)
|
||||||
|
if(MINGW)
|
||||||
|
target_link_options(d4r_${probe} PRIVATE -static)
|
||||||
|
elseif(MSVC)
|
||||||
|
set_property(TARGET d4r_${probe} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<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")
|
||||||
|
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")
|
||||||
|
endif()
|
||||||
|
add_subdirectory("${D4R_MINHOOK_ROOT}" "${CMAKE_CURRENT_BINARY_DIR}/minhook" EXCLUDE_FROM_ALL)
|
||||||
|
add_library(d4r_nvngx_windows SHARED tools/windows/d4r_nvngx_windows.cpp tools/windows/d3d12_command_hooks.cpp)
|
||||||
|
add_dependencies(d4r_nvngx_windows d4r_pixel_code_object)
|
||||||
|
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_include_directories(d4r_nvngx_windows PRIVATE "${D4R_HIP_ROOT}/include")
|
||||||
|
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)
|
||||||
|
if(MINGW)
|
||||||
|
target_link_options(d4r_nvngx_windows PRIVATE -static)
|
||||||
|
endif()
|
||||||
|
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)
|
||||||
|
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_nvngx_windows PRIVATE tools/d4r_ngx_param_msvc.cpp tools/d4r_ngx_param_host.cpp)
|
||||||
|
else()
|
||||||
|
find_program(D4R_CLANG_CL NAMES clang-cl.exe PATHS "${D4R_HIP_ROOT}/bin" "${D4R_HIP_ROOT}/lib/llvm/bin" NO_DEFAULT_PATH REQUIRED)
|
||||||
|
set(ngx_parameter_object "${CMAKE_CURRENT_BINARY_DIR}/ngx_parameter_msvc.obj")
|
||||||
|
add_custom_command(OUTPUT "${ngx_parameter_object}"
|
||||||
|
COMMAND "${D4R_CLANG_CL}" /c /O2 /Zl /GS- /GR- /EHs-c-
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/tools/d4r_ngx_param_msvc.cpp" "/Fo${ngx_parameter_object}"
|
||||||
|
DEPENDS tools/d4r_ngx_param_msvc.cpp VERBATIM)
|
||||||
|
set_source_files_properties("${ngx_parameter_object}" PROPERTIES GENERATED TRUE EXTERNAL_OBJECT TRUE)
|
||||||
|
target_sources(d4r_ngx_cuda_init_probe PRIVATE "${ngx_parameter_object}" tools/d4r_ngx_param_host.cpp)
|
||||||
|
target_sources(d4r_d3d12_dlss_probe PRIVATE "${ngx_parameter_object}" tools/d4r_ngx_param_host.cpp)
|
||||||
|
target_sources(d4r_nvngx_windows PRIVATE "${ngx_parameter_object}" tools/d4r_ngx_param_host.cpp)
|
||||||
|
endif()
|
||||||
|
add_library(d4r_nvapi_trace SHARED tools/windows/nvapi_trace.cpp)
|
||||||
|
set_target_properties(d4r_nvapi_trace PROPERTIES PREFIX "" OUTPUT_NAME "nvapi64"
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/nvapi-trace"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/nvapi-trace")
|
||||||
|
target_compile_definitions(d4r_nvapi_trace PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX)
|
||||||
|
target_link_libraries(d4r_nvapi_trace PRIVATE version dbghelp)
|
||||||
|
if(MINGW)
|
||||||
|
target_link_options(d4r_nvapi_trace PRIVATE -static)
|
||||||
|
endif()
|
||||||
|
install(TARGETS d4r_nvapi_trace RUNTIME DESTINATION nvapi-trace)
|
||||||
|
add_library(d4r_nvapi_compat SHARED tools/windows/nvapi_trace.cpp)
|
||||||
|
set_target_properties(d4r_nvapi_compat PROPERTIES PREFIX "" OUTPUT_NAME "nvapi64"
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/nvapi-compat"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/nvapi-compat")
|
||||||
|
target_compile_definitions(d4r_nvapi_compat PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__ D4R_NVAPI_COMPAT)
|
||||||
|
target_include_directories(d4r_nvapi_compat PRIVATE "${D4R_HIP_ROOT}/include")
|
||||||
|
target_link_libraries(d4r_nvapi_compat PRIVATE version dbghelp)
|
||||||
|
if(MINGW)
|
||||||
|
target_link_options(d4r_nvapi_compat PRIVATE -static)
|
||||||
|
endif()
|
||||||
|
install(TARGETS d4r_nvapi_compat RUNTIME DESTINATION nvapi-compat)
|
||||||
|
target_link_libraries(d4r_d3d12_hip_interop_probe PRIVATE d3d12 dxgi dxguid)
|
||||||
|
target_link_libraries(d4r_d3d12_dlss_probe PRIVATE d3d12 dxgi dxguid d3dcompiler)
|
||||||
|
add_executable(d4r_debug_launcher tools/windows/debug_launcher.cpp)
|
||||||
|
target_include_directories(d4r_debug_launcher PRIVATE tools/windows)
|
||||||
|
target_link_libraries(d4r_debug_launcher PRIVATE version dbghelp)
|
||||||
|
install(TARGETS d4r_debug_launcher RUNTIME DESTINATION bin)
|
||||||
|
target_link_libraries(d4r_d3d12_command_probe PRIVATE d3d12 dxgi dxguid)
|
||||||
|
target_link_libraries(d4r_d3d12_pixel_probe PRIVATE d3d12 dxgi dxguid)
|
||||||
|
add_dependencies(d4r_d3d12_pixel_probe d4r_pixel_code_object)
|
||||||
|
add_dependencies(d4r_d3d12_command_probe d4r_nvngx_windows)
|
||||||
|
set(D4R_AGILITY_ROOT "" CACHE PATH "Optional unpacked Microsoft.Direct3D.D3D12 1.619.5 debug SDK")
|
||||||
|
if(EXISTS "${D4R_AGILITY_ROOT}/build/native/bin/x64/d3d12SDKLayers.dll")
|
||||||
|
add_executable(d4r_d3d12_command_debug_probe EXCLUDE_FROM_ALL tools/windows/d3d12_command_probe.cpp)
|
||||||
|
target_compile_definitions(d4r_d3d12_command_debug_probe PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX __HIP_PLATFORM_AMD__ D4R_DIAGNOSTIC_AGILITY)
|
||||||
|
target_include_directories(d4r_d3d12_command_debug_probe PRIVATE "${D4R_HIP_ROOT}/include")
|
||||||
|
target_link_libraries(d4r_d3d12_command_debug_probe PRIVATE version dbghelp d3d12 dxgi dxguid)
|
||||||
|
if(MINGW)
|
||||||
|
target_link_options(d4r_d3d12_command_debug_probe PRIVATE -static)
|
||||||
|
endif()
|
||||||
|
foreach(dll D3D12Core.dll d3d12SDKLayers.dll)
|
||||||
|
configure_file("${D4R_AGILITY_ROOT}/build/native/bin/x64/${dll}" "${CMAKE_CURRENT_BINARY_DIR}/debug-d3d12/${dll}" COPYONLY)
|
||||||
|
endforeach()
|
||||||
|
configure_file("${D4R_AGILITY_ROOT}/LICENSE.txt" "${CMAKE_CURRENT_BINARY_DIR}/debug-d3d12/LICENSE.txt" COPYONLY)
|
||||||
|
endif()
|
||||||
|
add_dependencies(d4r_hip_gfx1201_probe d4r_probe_code_object)
|
||||||
|
add_dependencies(d4r_gfx12_wmma_probe d4r_wmma_code_object)
|
||||||
|
add_dependencies(d4r_k_module_probe d4r_native_compile)
|
||||||
|
install(FILES "${probe_object}" DESTINATION bin)
|
||||||
|
install(FILES "${pixel_object}" DESTINATION bin)
|
||||||
|
install(FILES "${wmma_object}" DESTINATION bin)
|
||||||
|
install(FILES scripts/windows/test-windows-rdna4.ps1 DESTINATION .)
|
||||||
|
install(FILES scripts/windows/gpu-target.ps1 DESTINATION .)
|
||||||
|
install(FILES scripts/windows/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-captures.ps1 DESTINATION .)
|
||||||
|
install(FILES scripts/windows/test-native-overrides.ps1 DESTINATION .)
|
||||||
|
install(FILES tools/windows/k_layer_validate.py DESTINATION .)
|
||||||
|
install(FILES tools/windows/k_replay_validate.py DESTINATION .)
|
||||||
|
install(FILES tools/windows/k_ptx_validate.py DESTINATION .)
|
||||||
|
install(FILES tools/windows/frame_compare.py DESTINATION .)
|
||||||
|
install(FILES tools/windows/m_replay_validate.py DESTINATION .)
|
||||||
|
install(FILES tools/windows/m_ptx_validate.py DESTINATION .)
|
||||||
|
install(FILES tools/windows/m_temporal_validate.py tools/windows/m_capture_compare.py DESTINATION .)
|
||||||
|
install(FILES scripts/windows/test-m-replay.ps1 DESTINATION .)
|
||||||
|
install(FILES scripts/windows/test-m-captures.ps1 DESTINATION .)
|
||||||
|
install(FILES kernels/tools/pwin_model.py kernels/tools/pwin_check.py kernels/tools/ptxsim.py
|
||||||
|
kernels/tools/extract_dlss_ptx.py kernels/tools/kernel_manifest.py DESTINATION reference)
|
||||||
|
install(FILES kernels/tools/model_enc3.py kernels/tools/swin_model.py kernels/tools/swin_check.py DESTINATION reference)
|
||||||
|
install(FILES docs/windows-rdna4-port.md DESTINATION docs)
|
||||||
|
install(FILES docs/windows-gpu-support.md DESTINATION docs)
|
||||||
|
include(CTest)
|
||||||
|
add_test(NAME binary16_rounding COMMAND d4r_binary16_probe)
|
||||||
|
set_tests_properties(binary16_rounding PROPERTIES TIMEOUT 30 LABELS "software;numerics")
|
||||||
|
add_test(NAME gpu_target_contract COMMAND d4r_gpu_target_probe "${probe_object}" "${pixel_object}")
|
||||||
|
set_tests_properties(gpu_target_contract PROPERTIES TIMEOUT 30 LABELS "software;architecture")
|
||||||
|
find_program(D4R_POWERSHELL NAMES pwsh.exe powershell.exe REQUIRED)
|
||||||
|
add_test(NAME gpu_target_script_contract COMMAND "${D4R_POWERSHELL}" -NoProfile -File
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/windows/test-gpu-target-contract.ps1"
|
||||||
|
-PackageRoot "${CMAKE_CURRENT_BINARY_DIR}" -CodeObject "${probe_object}")
|
||||||
|
set_tests_properties(gpu_target_script_contract PROPERTIES TIMEOUT 30 LABELS "software;architecture")
|
||||||
|
add_test(NAME d3d12_gpu_pixel_formats COMMAND d4r_d3d12_pixel_probe
|
||||||
|
--hip-root "${D4R_HIP_ROOT}" --module "${pixel_object}" --iterations 2)
|
||||||
|
set_tests_properties(d3d12_gpu_pixel_formats PROPERTIES TIMEOUT 120 LABELS "hardware;${D4R_GPU_ARCH};formats")
|
||||||
|
add_test(NAME d3d12_gpu_enhanced_formats COMMAND d4r_d3d12_pixel_probe
|
||||||
|
--hip-root "${D4R_HIP_ROOT}" --module "${pixel_object}" --interop-mode enhanced --iterations 2)
|
||||||
|
set_tests_properties(d3d12_gpu_enhanced_formats PROPERTIES TIMEOUT 120 LABELS "hardware;${D4R_GPU_ARCH};formats;enhanced_barriers")
|
||||||
|
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)
|
||||||
|
set_tests_properties(d3d12_command_backend PROPERTIES TIMEOUT 120 LABELS "hardware;d3d12;command_lists;lifetime")
|
||||||
|
add_test(NAME d3d12_command_initial_pso COMMAND d4r_d3d12_command_probe
|
||||||
|
--hip-root "${D4R_HIP_ROOT}" --module "$<TARGET_FILE:d4r_nvngx_windows>" --interop-mode initial-pso --iterations 32)
|
||||||
|
set_tests_properties(d3d12_command_initial_pso PROPERTIES TIMEOUT 120 LABELS "hardware;d3d12;command_lists;lifetime")
|
||||||
|
add_test(NAME d3d12_command_root_reset_warp COMMAND d4r_d3d12_command_probe
|
||||||
|
--hip-root "${D4R_HIP_ROOT}" --module "$<TARGET_FILE:d4r_nvngx_windows>" --interop-mode indirect-root-reset --iterations 32)
|
||||||
|
set_tests_properties(d3d12_command_root_reset_warp PROPERTIES TIMEOUT 120 LABELS "software;d3d12;command_lists;lifetime"
|
||||||
|
ENVIRONMENT "D4R_COMMAND_PROBE_WARP=1;D4R_COMMAND_PROBE_NO_HIP=1")
|
||||||
|
add_test(NAME hip_gfx1201 COMMAND d4r_hip_gfx1201_probe
|
||||||
|
--hip-root "${D4R_HIP_ROOT}" --module "${probe_object}")
|
||||||
|
set_tests_properties(hip_gfx1201 PROPERTIES TIMEOUT 90 LABELS "hardware;${D4R_GPU_ARCH}")
|
||||||
|
add_test(NAME gfx12_wmma COMMAND d4r_gfx12_wmma_probe
|
||||||
|
--hip-root "${D4R_HIP_ROOT}" --module "${wmma_object}" --iterations 16)
|
||||||
|
set_tests_properties(gfx12_wmma PROPERTIES TIMEOUT 90 LABELS "hardware;${D4R_GPU_ARCH};wmma")
|
||||||
|
foreach(layer enc1 enc2)
|
||||||
|
add_test(NAME k_${layer}_execution COMMAND d4r_k_module_probe
|
||||||
|
--hip-root "${D4R_HIP_ROOT}"
|
||||||
|
--module "${CMAKE_CURRENT_BINARY_DIR}/dltss_pwin_${layer}_layer_${D4R_GPU_ARCH}.hsaco"
|
||||||
|
--kernel-name ${layer})
|
||||||
|
set_tests_properties(k_${layer}_execution PROPERTIES TIMEOUT 90 LABELS "hardware;${D4R_GPU_ARCH};k_execution")
|
||||||
|
endforeach()
|
||||||
|
add_test(NAME d3d12_hip_interop COMMAND d4r_d3d12_hip_interop_probe
|
||||||
|
--hip-root "${D4R_HIP_ROOT}" --module "${probe_object}" --iterations 32)
|
||||||
|
set_tests_properties(d3d12_hip_interop PROPERTIES TIMEOUT 120 LABELS "hardware;interop")
|
||||||
|
add_test(NAME d3d12_hip_map_lifetime COMMAND d4r_d3d12_hip_interop_probe
|
||||||
|
--hip-root "${D4R_HIP_ROOT}" --interop-mode map --iterations 64)
|
||||||
|
set_tests_properties(d3d12_hip_map_lifetime PROPERTIES TIMEOUT 120 LABELS "hardware;interop;lifetime")
|
||||||
|
set(D4R_ZLUDA_ROOT "" CACHE PATH "Local Windows ZLUDA directory (nvcuda.dll)")
|
||||||
|
if(EXISTS "${D4R_ZLUDA_ROOT}/nvcuda.dll")
|
||||||
|
foreach(context primary created)
|
||||||
|
add_test(NAME cuda_${context} COMMAND d4r_cuda_driver_probe
|
||||||
|
--hip-root "${D4R_HIP_ROOT}" --cuda-dll "${D4R_ZLUDA_ROOT}/nvcuda.dll"
|
||||||
|
--context ${context} --iterations 32)
|
||||||
|
set_tests_properties(cuda_${context} PROPERTIES TIMEOUT 180 LABELS "hardware;zluda")
|
||||||
|
endforeach()
|
||||||
|
if(EXISTS "${D4R_ZLUDA_ROOT}/build-info.json")
|
||||||
|
add_test(NAME cuda_f16_mma_reference COMMAND d4r_cuda_mma_probe
|
||||||
|
--hip-root "${D4R_HIP_ROOT}" --cuda-dll "${D4R_ZLUDA_ROOT}/nvcuda.dll")
|
||||||
|
set_tests_properties(cuda_f16_mma_reference PROPERTIES TIMEOUT 120
|
||||||
|
LABELS "hardware;zluda;mma;reference"
|
||||||
|
ENVIRONMENT "D4R_ZLUDA_WMMA=1;D4R_ZLUDA_WMMA_F16_REFERENCE=1")
|
||||||
|
add_test(NAME cuda_images COMMAND d4r_cuda_image_probe
|
||||||
|
--hip-root "${D4R_HIP_ROOT}" --cuda-dll "${D4R_ZLUDA_ROOT}/nvcuda.dll" --iterations 32)
|
||||||
|
set_tests_properties(cuda_images PROPERTIES TIMEOUT 90 LABELS "hardware;zluda;descriptors")
|
||||||
|
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
|
||||||
|
--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")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
@@ -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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
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
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
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.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
you may not use this file except in compliance with the License.
|
Copyright (C) <year> <name of author>
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
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.
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
This program is distributed in the hope that it will be useful,
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
See the License for the specific language governing permissions and
|
GNU General Public License for more details.
|
||||||
limitations under the License.
|
|
||||||
|
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>.
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -2,10 +2,18 @@
|
|||||||
|
|
||||||
d4r runs NVIDIA's official DLSS Super Resolution library (`nvngx_dlss.dll`) in Windows games on AMD Radeon GPUs under Linux and Proton. The game asks for DLSS as usual; the DLSS network runs on the AMD GPU through [ZLUDA](https://github.com/vosen/ZLUDA) (CUDA on ROCm/HIP), with the heaviest DLSS kernels replaced by hand-written RDNA3 and RDNA4 code.
|
d4r runs NVIDIA's official DLSS Super Resolution library (`nvngx_dlss.dll`) in Windows games on AMD Radeon GPUs under Linux and Proton. The game asks for DLSS as usual; the DLSS network runs on the AMD GPU through [ZLUDA](https://github.com/vosen/ZLUDA) (CUDA on ROCm/HIP), with the heaviest DLSS kernels replaced by hand-written RDNA3 and RDNA4 code.
|
||||||
|
|
||||||
**Supported DLSS models:** DLSS 3 CNN (E), DLSS 4 transformer (K, default), and DLSS 4.5 transformer (M). DLSS 5 is PURPOSELY not supported.
|
**Supported DLSS models:** DLSS 3 CNN (E), DLSS 4 transformer (K, default), and DLSS 4.5 transformer (M, plus experimental L). DLSS 5 is PURPOSELY not supported.
|
||||||
|
|
||||||
|
Preset L is NVIDIA's DLSS 4.5 model for Ultra Performance, especially at 4K. Set `[DLSS] Model = L` in `d4r.ini` and select Ultra Performance in game or OptiScaler; selecting L alone does not change the input resolution. L shares M's native Swin layers and has its own texture-kernel build. Its unfolded input/output arithmetic remains translated, so performance and image quality need game testing; the M benchmarks below do not apply to L.
|
||||||
|
|
||||||
**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.
|
||||||
|
|
||||||
|
**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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
> **Not affiliated with NVIDIA or AMD.**
|
> **Not affiliated with NVIDIA or AMD.**
|
||||||
@@ -33,6 +41,8 @@ On the same GPU DLSS starts at a disadvantage: its networks were designed for NV
|
|||||||
|
|
||||||
- In some games using some models native upscaling can show visible artifacting.
|
- In some games using some models native upscaling can show visible artifacting.
|
||||||
|
|
||||||
|
For users who prioritize fidelity over speed, set `[Kernels] PreferAccuracy = true` in `d4r/d4r.ini` and restart the game. It defaults to `false`. This selects accuracy variants of every native kernel and restores conservative translation settings. It aims to match NVIDIA's arithmetic; 1:1 image quality against RTX DLSS is not yet proven. See [native kernel numerics](docs/native-kernels.md#numerics).
|
||||||
|
|
||||||
## GPU support
|
## GPU support
|
||||||
|
|
||||||
d4r builds for RDNA3 and RDNA4. A newly built release compiles native DLSS 4 and 4.5 network kernels for the targets below and selects the matching set at runtime. Only the RX 7700 XT has been tested by this project on a real GPU; an external video reports DLSS 4.5 running through d4r 0.1.2 on an RX 7900 XTX. RDNA4 runtime and performance remain unverified on hardware.
|
d4r builds for RDNA3 and RDNA4. A newly built release compiles native DLSS 4 and 4.5 network kernels for the targets below and selects the matching set at runtime. Only the RX 7700 XT has been tested by this project on a real GPU; an external video reports DLSS 4.5 running through d4r 0.1.2 on an RX 7900 XTX. RDNA4 runtime and performance remain unverified on hardware.
|
||||||
@@ -43,11 +53,11 @@ d4r builds for RDNA3 and RDNA4. A newly built release compiles native DLSS 4 and
|
|||||||
| RX 7900 GRE / XT / XTX, Radeon PRO W7800 / W7900 | gfx1100 | widened to f16 | RX 7900 XTX: [external video](https://www.youtube.com/watch?v=_GLjJ2Dn5pU) (DLSS 4.5); other cards untested |
|
| RX 7900 GRE / XT / XTX, Radeon PRO W7800 / W7900 | gfx1100 | widened to f16 | RX 7900 XTX: [external video](https://www.youtube.com/watch?v=_GLjJ2Dn5pU) (DLSS 4.5); other cards untested |
|
||||||
| RX 7600 / 7600 XT, RX 7650 GRE | gfx1102 | widened to f16 | untested |
|
| RX 7600 / 7600 XT, RX 7650 GRE | gfx1102 | widened to f16 | untested |
|
||||||
| RDNA3 integrated GPUs | gfx1103 | widened to f16 | untested |
|
| RDNA3 integrated GPUs | gfx1103 | widened to f16 | untested |
|
||||||
| RX 9070 XT / 9070 / 9070 GRE, Radeon AI PRO R9700 | gfx1201 | native FP8 (`NativeFp8`, default on) | emulator only |
|
| RX 9070 XT / 9070 / 9070 GRE, Radeon AI PRO R9700 | gfx1201 | native FP8 (`NativeFp8`, default on) | RX 9070 XT: [external video](https://www.youtube.com/watch?v=lZ0BLYqAtCs), other cards untested |
|
||||||
| RX 9060 XT / 9060 | gfx1200 | native FP8 (`NativeFp8`, default on) | compile only |
|
| RX 9060 XT / 9060 | gfx1200 | native FP8 (`NativeFp8`, default on) | compile only |
|
||||||
| RDNA2 and older | | | unsupported |
|
| RDNA2 and older | | | unsupported |
|
||||||
|
|
||||||
Native network kernels are built for all listed gfx11/gfx12 targets. Texture kernels are compiled for each target by `d4r_emit` when supplied to the package script; RDNA4 also has a `-fp8` variant. The bridge selects the KFD GPU with the most SIMDs, avoiding an integrated GPU when a discrete GPU is present; `D4R_GPU_ARCH` overrides that choice. Missing native kernels fall back to ZLUDA. Without the native network kernels, NVIDIA's translated DLSS 4 layers produce invalid values and DLSS discards the network's output, so K loses the transformer's image quality. Preset E does not use the native network kernels.
|
Native network kernels are built for all listed gfx11/gfx12 targets. Texture kernels are compiled for each target by `d4r_emit` when supplied to the package script; RDNA4 also has a `-fp8` variant. The bridge selects the KFD GPU with the most SIMDs, avoiding an integrated GPU when a discrete GPU is present; `D4R_GPU_ARCH` overrides that choice. Missing native kernels fall back to ZLUDA and can be much slower. Earlier translated K layers produced invalid values; preserving FP16 denormal handling fixes that failure in recorded captures, but RTX image-quality parity remains unverified. Preset E does not use the native network kernels.
|
||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
|
|
||||||
@@ -82,6 +92,8 @@ The release zip works like an OptiScaler release: its contents go into the folde
|
|||||||
- an `d4r` folder with the shim, the CUDA bridge, ZLUDA, the ROCm 7.2.4 runtime, the native kernels, this game's `d4r.ini`, and NVIDIA's `nvngx_dlss.dll` (310.7) and `_nvngx.dll`.
|
- an `d4r` folder with the shim, the CUDA bridge, ZLUDA, the ROCm 7.2.4 runtime, the native kernels, this game's `d4r.ini`, and NVIDIA's `nvngx_dlss.dll` (310.7) and `_nvngx.dll`.
|
||||||
2. In Steam, select GE-Proton 11 for the game and set these launch options: `PROTON_FORCE_NVAPI=1 DXVK_NVAPI_GPU_ARCH=AD100 %command%`.
|
2. In Steam, select GE-Proton 11 for the game and set these launch options: `PROTON_FORCE_NVAPI=1 DXVK_NVAPI_GPU_ARCH=AD100 %command%`.
|
||||||
|
|
||||||
|
When updating, replace `d3d12.dll` and `d3d12core.dll` together with `d4r/nvngx.dll`. The shim requires their matching resource-lifetime extension.
|
||||||
|
|
||||||
ROCm does not need to be installed: the zip includes its runtime (from AMD's Ubuntu 22.04 packages, which run under Steam's container runtime on any distribution). The zip's NVIDIA files and the kernels built from NVIDIA's code are not covered by this repository's license (see [NOTICE](NOTICE)). The Proton prefix and the system are not changed. [packaging/D4R_README.txt](packaging/D4R_README.txt) is the full guide that ships in the zip; `scripts/package_release.sh` builds the zip (see [docs/building.md](docs/building.md#7-package-a-release)).
|
ROCm does not need to be installed: the zip includes its runtime (from AMD's Ubuntu 22.04 packages, which run under Steam's container runtime on any distribution). The zip's NVIDIA files and the kernels built from NVIDIA's code are not covered by this repository's license (see [NOTICE](NOTICE)). The Proton prefix and the system are not changed. [packaging/D4R_README.txt](packaging/D4R_README.txt) is the full guide that ships in the zip; `scripts/package_release.sh` builds the zip (see [docs/building.md](docs/building.md#7-package-a-release)).
|
||||||
|
|
||||||
## Requirements (building from source)
|
## Requirements (building from source)
|
||||||
@@ -148,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`.
|
||||||
+38
-16
@@ -17,14 +17,20 @@ CompatDataDir =
|
|||||||
GameExe =
|
GameExe =
|
||||||
|
|
||||||
[DLSS]
|
[DLSS]
|
||||||
; Which DLSS network ("preset") to run. fps measured on an RX 7700 XT, Townfall 2560x1440, same street
|
; DLSS model recommendations:
|
||||||
; walk, for Quality / Balanced / Performance / Ultra Performance (FSR 4: 76 / 85 / 94 / 107):
|
;
|
||||||
; E (or CNN) = DLSS 3 CNN, the fastest at Quality..Performance 72 / 81 / 88 / 89
|
; Model | DLSS | Recommended modes | Notes
|
||||||
; K (or DLSS4) = DLSS 4 transformer, sharper; best at Ultra Perf 69 / 77 / 84 / 95
|
; ------+-------+---------------------------------+----------------------------------
|
||||||
; M (or DLSS4.5) = DLSS 4.5 transformer, best image, slowest 52 / 60 / 70 / 90
|
; K | 4 | DLAA / Quality / Balanced | Default; faster alternative to M/L
|
||||||
; F = the other CNN (same speed as E); A, B, C, D, G, J run the K network in this DLSS version;
|
; M | 4.5 | Performance | More demanding on RDNA3
|
||||||
; L (DLSS 4.5 Ultra Performance model) is not accelerated here and runs very slowly.
|
; L | 4.5 | Ultra Performance | Especially 4K; experimental
|
||||||
; auto = whatever the game/OptiScaler asks for
|
; E | 3 CNN | Quality / Balanced / Performance | Legacy faster fallback
|
||||||
|
; F | 3 CNN | DLAA / Ultra Performance | Deprecated; prefer K or L
|
||||||
|
;
|
||||||
|
; Select the upscaling mode separately in game/OptiScaler. These are recommendations;
|
||||||
|
; other modes are allowed. L requires the L texture build.
|
||||||
|
; Aliases: CNN = E, DLSS4 = K, DLSS4.5 = M. A/B/C/D/G/J use K in this DLL version.
|
||||||
|
; auto = use the game/OptiScaler choice. Benchmarks: docs/performance.md.
|
||||||
Model = K
|
Model = K
|
||||||
[Latency]
|
[Latency]
|
||||||
; How many frames old the upscaled image the game presents may be.
|
; How many frames old the upscaled image the game presents may be.
|
||||||
@@ -36,9 +42,20 @@ Model = K
|
|||||||
FrameAge = 0
|
FrameAge = 0
|
||||||
|
|
||||||
[Kernels]
|
[Kernels]
|
||||||
|
; Prefer NVIDIA's original arithmetic over speed for every native kernel and the translated fallback.
|
||||||
|
; Restores intermediate FP8 quantization, per-MMA f16 rounding and original denormal handling; keeps
|
||||||
|
; NGX's synchronizations. Selects accuracy variants in NativeKernelDir*/accuracy (or a marked accuracy
|
||||||
|
; directory directly). Missing accuracy kernels use translated PTX, never the fast set. Slower; this
|
||||||
|
; aims for fidelity, but RTX image parity is not proven. (D4R_PREFER_ACCURACY)
|
||||||
|
PreferAccuracy = false
|
||||||
|
; false: use original translated enc1/enc2 layers for L/M, retaining other native kernels.
|
||||||
|
; Reduces the native encoders' motion trails seen in Townfall; best paired with PreferAccuracy.
|
||||||
|
; Costs some GPU time. Manifest required for selective fallback; flat sets fall back entirely.
|
||||||
|
; (D4R_NATIVE_SWIN_ENCODERS)
|
||||||
|
NativeSwinEncoders = true
|
||||||
; Native RDNA3 / RDNA4 kernels for the DLSS 4 and 4.5 network layers (replace ZLUDA's translated code).
|
; Native RDNA3 / RDNA4 kernels for the DLSS 4 and 4.5 network layers (replace ZLUDA's translated code).
|
||||||
; fast = native kernels with relaxed numerics (visually identical to exact, ~15% faster)
|
; fast = native kernels with relaxed numerics (unless PreferAccuracy is true)
|
||||||
; exact = native kernels that reproduce NVIDIA's FP8/f16 rounding bit for bit
|
; exact = a separately built directory with conservative FP8/f16 rounding; not an RTX parity guarantee
|
||||||
; off = ZLUDA-translated kernels only
|
; off = ZLUDA-translated kernels only
|
||||||
; fast/exact need the code objects built by the native-kernel tools ([Paths] NativeKernelDir*).
|
; fast/exact need the code objects built by the native-kernel tools ([Paths] NativeKernelDir*).
|
||||||
NativeKernels = off
|
NativeKernels = off
|
||||||
@@ -54,6 +71,12 @@ IgnoreDenormals = true
|
|||||||
ImplicitMaxBlock = 256
|
ImplicitMaxBlock = 256
|
||||||
|
|
||||||
[Interop]
|
[Interop]
|
||||||
|
; Experimental foreground motion coverage for thin geometry. 0 = original vectors;
|
||||||
|
; 1/2 = opaque-surface dilation radius in render pixels; both automatically fill sky gaps
|
||||||
|
; from the nearest foreground within 8 render pixels. Needs render- or display-resolution, unjittered motion vectors
|
||||||
|
; and same-frame linear VRAM inputs; unsupported features retain original vectors.
|
||||||
|
; Default off. May affect disocclusion edges. (D4R_MOTION_DILATION)
|
||||||
|
MotionVectorDilation = 0
|
||||||
; Keep DLSS inputs and output in VRAM (vkd3d-proton Vulkan interop) instead of copying through
|
; Keep DLSS inputs and output in VRAM (vkd3d-proton Vulkan interop) instead of copying through
|
||||||
; system memory. Falls back automatically when the game's resources do not qualify.
|
; system memory. Falls back automatically when the game's resources do not qualify.
|
||||||
VramInterop = true
|
VramInterop = true
|
||||||
@@ -61,19 +84,18 @@ VramInterop = true
|
|||||||
InputSync = true
|
InputSync = true
|
||||||
; Faster hand-offs between the game and DLSS (same frame, same result). Need FrameAge = 0 and VRAM
|
; Faster hand-offs between the game and DLSS (same frame, same result). Need FrameAge = 0 and VRAM
|
||||||
; interop, and a runtime built from this checkout (scripts/install_d4r_runtime.sh).
|
; interop, and a runtime built from this checkout (scripts/install_d4r_runtime.sh).
|
||||||
; GpuWait: DLSS is queued ahead and starts on the GPU the moment the game's inputs are ready
|
; Input readiness is checked with CPU sleeps; recorded-but-discarded lists never queue GPU waits.
|
||||||
; LinearInputs: DLSS reads the game's inputs in place (no input copies; needs GpuWait)
|
; LinearInputs: DLSS reads the game's inputs in place once the input marker is ready
|
||||||
; DirectOutput: the result is written straight into the game-side buffer (preset K, and only while
|
; DirectOutput: the result is written straight into the game-side buffer (preset K, and only while
|
||||||
; NGX runs the native output kernel; drops NVIDIA's "DLSS SDK" watermark)
|
; NGX runs the native output kernel; drops NVIDIA's "DLSS SDK" watermark)
|
||||||
; ElideNgxSync: removes NGX's internal CPU waits between its GPU stages
|
; ElideNgxSync: removes NGX's internal CPU waits between its GPU stages
|
||||||
; EvalSync = false: no CPU wait between DLSS and the output copy
|
; EvalSync = false: no CPU wait between DLSS and the output copy
|
||||||
; MarkerPollUs: how often (microseconds) the CPU checks for the game's inputs without GpuWait
|
; MarkerPollUs: how often (microseconds) the CPU checks for the game's inputs
|
||||||
GpuWait = true
|
|
||||||
LinearInputs = true
|
LinearInputs = true
|
||||||
DirectOutput = true
|
DirectOutput = true
|
||||||
ElideNgxSync = true
|
ElideNgxSync = true
|
||||||
EvalSync = false
|
EvalSync = false
|
||||||
MarkerPollUs = 20
|
MarkerPollUs = 200
|
||||||
|
|
||||||
[Paths]
|
[Paths]
|
||||||
; auto = defaults of scripts/d4r_proton_env.sh
|
; auto = defaults of scripts/d4r_proton_env.sh
|
||||||
@@ -127,4 +149,4 @@ LogDir = auto
|
|||||||
[Env]
|
[Env]
|
||||||
; Anything else, passed through as environment variables (one per line):
|
; Anything else, passed through as environment variables (one per line):
|
||||||
; D4R_SHIM_SPLIT_TIMEOUT_MS = 200
|
; D4R_SHIM_SPLIT_TIMEOUT_MS = 200
|
||||||
; D4R_SHIM_BLOCKING_SYNC = 1 ; yield CPU while waiting for DLSS output
|
; D4R_SHIM_BLOCKING_SYNC = 0 ; A/B only: restore context waits instead of default event queries/sleeps
|
||||||
+13
-14
@@ -12,7 +12,7 @@ d4r keeps NVIDIA's DLSS library unmodified and gives it what it expects: an NGX
|
|||||||
| CUDA bridge `nvcuda.dll` | `tools/wine_nvcuda_bridge.c` | A Wine builtin whose Windows exports forward to ZLUDA's Linux `libcuda.so`, plus d4r helpers (Vulkan memory import, asynchronous 2D copies, GPU-side waits, output redirect, linear textures) |
|
| CUDA bridge `nvcuda.dll` | `tools/wine_nvcuda_bridge.c` | A Wine builtin whose Windows exports forward to ZLUDA's Linux `libcuda.so`, plus d4r helpers (Vulkan memory import, asynchronous 2D copies, GPU-side waits, output redirect, linear textures) |
|
||||||
| ZLUDA | `patches/zluda` | CUDA driver API on HIP; compiles NVIDIA's PTX to AMDGPU code; serves native kernels in place of PTX kernels |
|
| ZLUDA | `patches/zluda` | CUDA driver API on HIP; compiles NVIDIA's PTX to AMDGPU code; serves native kernels in place of PTX kernels |
|
||||||
| Native kernels | `kernels/` | RDNA3 implementations of the DLSS network layers and parts of texture kernels |
|
| Native kernels | `kernels/` | RDNA3 implementations of the DLSS network layers and parts of texture kernels |
|
||||||
| vkd3d-proton patch | `patches/vkd3d-proton` | Lets the shim split a D3D12 command list around the DLSS call |
|
| vkd3d-proton patch | `patches/vkd3d-proton` | Splits a D3D12 command list around DLSS and retains external resources through command allocator completion |
|
||||||
| NVAPI identity | `tools/d4r_nvapi_identity.c` | Reports an Ada (sm_89) GPU so NGX selects the network weights matching the PTX ZLUDA compiles |
|
| NVAPI identity | `tools/d4r_nvapi_identity.c` | Reports an Ada (sm_89) GPU so NGX selects the network weights matching the PTX ZLUDA compiles |
|
||||||
|
|
||||||
## One frame
|
## One frame
|
||||||
@@ -24,23 +24,22 @@ With VRAM interop and split frames (the defaults when the patched vkd3d-proton i
|
|||||||
- A frame marker.
|
- A frame marker.
|
||||||
- A split. vkd3d-proton submits everything the game records after the DLSS call separately, gated on a timeline semaphore reaching this frame's number.
|
- A split. vkd3d-proton submits everything the game records after the DLSS call separately, gated on a timeline semaphore reaching this frame's number.
|
||||||
- At the start of that second part, a copy of the DLSS result into the game's output texture. Linear R11G11B10, RGB10A2, and RGBA8/BGRA8 outputs are converted from RGBA16F on the GPU. sRGB formats continue through host staging to preserve their existing byte interpretation.
|
- At the start of that second part, a copy of the DLSS result into the game's output texture. Linear R11G11B10, RGB10A2, and RGBA8/BGRA8 outputs are converted from RGBA16F on the GPU. sRGB formats continue through host staging to preserve their existing byte interpretation.
|
||||||
2. **The CUDA worker queues DLSS.** A worker thread owns the CUDA context. For each frame it queues on the GPU, without waiting on the CPU:
|
2. **The CUDA worker queues DLSS.** The prep thread checks the GPU-written input marker with short CPU sleeps. It drops recorded-but-unsubmitted frames when they time out or their feature retires, before any DLSS work is queued. The CUDA worker then queues:
|
||||||
- a wait for the marker (`GpuWait`);
|
|
||||||
- the input copies into CUDA arrays, or none when NGX samples the buffers in place (`LinearInputs`);
|
- the input copies into CUDA arrays, or none when NGX samples the buffers in place (`LinearInputs`);
|
||||||
- NGX's evaluation (NGX's own CPU synchronisations are elided with `ElideNgxSync`);
|
- NGX's evaluation (NGX's own CPU synchronisations are elided with `ElideNgxSync`);
|
||||||
- the result, written straight into the output buffer by the native output kernel (`DirectOutput`, preset K) or copied there.
|
- the result, written straight into the output buffer by the native output kernel (`DirectOutput`, preset K) or copied there.
|
||||||
3. **Release.** When the GPU finishes, the worker signals the timeline semaphore and the rest of the game's frame runs, now with this frame's result.
|
3. **Release.** When the GPU finishes, the worker signals the timeline semaphore and the rest of the game's frame runs, now with this frame's result.
|
||||||
|
|
||||||
A watchdog releases any frame that never completes (for example a command list that is recorded but never executed), so the game cannot stall on DLSS.
|
The watchdog reports delayed output but never signals an unfinished producer as ready. Retiring features remain registered until their CPU/CUDA jobs drain. The lifetime-aware vkd3d extension retains their staging buffers, imported buffers, conversion images and split semaphore through command-allocator reset or destruction after GPU completion. Discarded recordings are covered too; cleanup no longer relies on a 50 ms delay.
|
||||||
|
|
||||||
Without the vkd3d-proton patch the shim shows the newest finished result instead (one or more frames old). Without VRAM interop, inputs and output are staged through host memory.
|
The shim requires the matching lifetime-aware d3d12.dll and d3d12core.dll, even for host staging. It rejects older runtimes rather than recording references whose lifetime it cannot protect. FrameAge > 0 shows the newest finished result; without VRAM interop, inputs and output are staged through host memory.
|
||||||
|
|
||||||
## Why each piece exists
|
## Why each piece exists
|
||||||
|
|
||||||
- **CUDA NGX instead of D3D12 NGX.** NVIDIA's D3D12 path needs NVIDIA's driver. The CUDA path only needs a CUDA driver API, which ZLUDA provides.
|
- **CUDA NGX instead of D3D12 NGX.** NVIDIA's D3D12 path needs NVIDIA's driver. The CUDA path only needs a CUDA driver API, which ZLUDA provides.
|
||||||
- **The Ada identity.** NGX picks network weights by GPU architecture. The weights have to match the PTX that ZLUDA compiles, which is sm_89 for DLSS 310.
|
- **The Ada identity.** NGX picks network weights by GPU architecture. The weights have to match the PTX that ZLUDA compiles, which is sm_89 for DLSS 310.
|
||||||
- **Native kernels.** ZLUDA translates NVIDIA's warp-level matrix code faithfully but slowly (register pressure, lane shuffles, per-instruction mode switches). In the DLSS 4 transformer, ZLUDA's translation also produced non-finite outputs, which the final kernel masks, so the network had no effect. The native layers fix both.
|
- **Native kernels.** Translating NVIDIA's warp-level matrix code is slow (register pressure, lane shuffles, per-instruction mode switches). Earlier translated K layers also produced non-finite outputs that the final kernel masked. Native layers avoid that path; the compiler now preserves FP16 denormal requirements too, which fixes the translated failure in recorded captures. Numerical and image-quality parity with RTX hardware remains unverified.
|
||||||
- **The GPU-side hand-offs.** With DLSS itself fast, most of the remaining gap to FSR 4 was the GPU idling while CPU threads waited on each other. Moving the waits onto the GPU raised GPU utilisation from about 95% to 99%.
|
- **Sleeping readiness checks.** Waiting for input submission before queueing DLSS avoids an uncancellable GPU wait. Explicit output event queries with short sleeps avoid HIP event/context waits that can consume a CPU core. Submillisecond waits use NtDelayExecution because MinGW std::sleep_for can truncate them to zero. Linear inputs and direct output remain available.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
@@ -51,8 +50,8 @@ Without the vkd3d-proton patch the shim shows the newest finished result instead
|
|||||||
| `[DLSS] Model` | `D4R_DLSS_PRESET` | DLSS network: `E` (CNN), `K` (DLSS 4), `M` (DLSS 4.5) |
|
| `[DLSS] Model` | `D4R_DLSS_PRESET` | DLSS network: `E` (CNN), `K` (DLSS 4), `M` (DLSS 4.5) |
|
||||||
| `[Latency] FrameAge` | `D4R_SHIM_SPLIT_FRAME`, `D4R_SHIM_MAX_IN_FLIGHT` | 0 = same-frame results; higher values pipeline frames |
|
| `[Latency] FrameAge` | `D4R_SHIM_SPLIT_FRAME`, `D4R_SHIM_MAX_IN_FLIGHT` | 0 = same-frame results; higher values pipeline frames |
|
||||||
| `[Kernels] NativeKernels` | `D4R_ZLUDA_NATIVE_DIR` | native kernel directory (`fast` / `exact` / `off`) |
|
| `[Kernels] NativeKernels` | `D4R_ZLUDA_NATIVE_DIR` | native kernel directory (`fast` / `exact` / `off`) |
|
||||||
|
| `[Kernels] PreferAccuracy` | `D4R_PREFER_ACCURACY` | default off; accuracy variants for every native family and conservative translation/synchronization settings |
|
||||||
| `[Interop] VramInterop` | `D4R_SHIM_VRAM_INTEROP` | keep inputs and output in VRAM |
|
| `[Interop] VramInterop` | `D4R_SHIM_VRAM_INTEROP` | keep inputs and output in VRAM |
|
||||||
| `[Interop] GpuWait` | `D4R_SHIM_GPU_WAIT` | GPU-side wait for the game's inputs |
|
|
||||||
| `[Interop] LinearInputs` | `D4R_SHIM_LINEAR_INPUTS` | NGX samples the input buffers in place |
|
| `[Interop] LinearInputs` | `D4R_SHIM_LINEAR_INPUTS` | NGX samples the input buffers in place |
|
||||||
| `[Interop] DirectOutput` | `D4R_SHIM_OUTPUT_DIRECT` | the output kernel writes the game-side buffer directly (preset K) |
|
| `[Interop] DirectOutput` | `D4R_SHIM_OUTPUT_DIRECT` | the output kernel writes the game-side buffer directly (preset K) |
|
||||||
| `[Interop] ElideNgxSync` | `D4R_ELIDE_NGX_SYNC` | skip NGX's internal CPU waits |
|
| `[Interop] ElideNgxSync` | `D4R_ELIDE_NGX_SYNC` | skip NGX's internal CPU waits |
|
||||||
@@ -89,11 +88,11 @@ The release zip (`scripts/package_release.sh`) is unpacked into the folder that
|
|||||||
|
|
||||||
The launcher writes each run's logs to `~/.cache/d4r-dlss-captures/game-<exe>-<time>/`. `d4r_nvngx.log` holds the shim's log. With `[Debug] Profile = true` it also records one `D4R_PROFILE` line per frame, with the stage timings.
|
The launcher writes each run's logs to `~/.cache/d4r-dlss-captures/game-<exe>-<time>/`. `d4r_nvngx.log` holds the shim's log. With `[Debug] Profile = true` it also records one `D4R_PROFILE` line per frame, with the stage timings.
|
||||||
|
|
||||||
For a CPU-use A/B check, set `D4R_SHIM_BLOCKING_SYNC = 1` under `[Env]` in the game's `d4r.ini`.
|
Output completion uses event queries with 200 us NtDelayExecution sleeps by default. `D4R_SHIM_BLOCKING_SYNC = 0`
|
||||||
The VRAM path then requests a blocking event wait for its null-stream output. If ZLUDA and HIP
|
under `[Env]` restores context synchronization for an A/B check. The profile records
|
||||||
honour the event flag, the waiting thread yields the CPU; an unavailable or failed event falls back
|
`output_sync_wall`, `output_sync_cpu`, and `output_sync_method=event_query_sleep` for the new path.
|
||||||
to the original context sync. This is opt-in because it changes the wait scope and may add wakeup
|
An unavailable or failed event falls back to context synchronization. The old `GpuWait` setting
|
||||||
latency. The profile reports `output_sync_wall`, `output_sync_cpu`, and `output_sync_blocking`;
|
is no longer used: input readiness is checked on the CPU before CUDA work is queued.
|
||||||
compare CPU use and frame rate in the same scene.
|
|
||||||
The `d2h` field on this path includes the output synchronization wait, even when direct output
|
The `d2h` field on this path includes the output synchronization wait, even when direct output
|
||||||
skips the array-to-buffer copy.
|
skips the array-to-buffer copy.
|
||||||
+30
-4
@@ -5,7 +5,7 @@ These steps produce the pieces the launcher needs: a patched ZLUDA, a patched vk
|
|||||||
| Component | Tested version |
|
| Component | Tested version |
|
||||||
|---|---|
|
|---|---|
|
||||||
| ZLUDA | `ee2f25a` (upstream), plus `patches/zluda/0002` through `0007` in order |
|
| ZLUDA | `ee2f25a` (upstream), plus `patches/zluda/0002` through `0007` in order |
|
||||||
| vkd3d-proton | `3dfc6f07` (the base GE-Proton11-3 ships), plus `patches/vkd3d-proton/0001` |
|
| vkd3d-proton | `3dfc6f07` (the base GE-Proton11-3 ships), plus `patches/vkd3d-proton/0001` and `0002` |
|
||||||
| ROCm | 7.2 (HIP runtime, clang, device libraries) |
|
| ROCm | 7.2 (HIP runtime, clang, device libraries) |
|
||||||
| Proton | GE-Proton11-3 (with its OptiScaler integration) |
|
| Proton | GE-Proton11-3 (with its OptiScaler integration) |
|
||||||
| DLSS | `nvngx_dlss.dll` 310.7.0 |
|
| DLSS | `nvngx_dlss.dll` 310.7.0 |
|
||||||
@@ -81,7 +81,9 @@ D4R_ZLUDA_EMIT=/path/to/zluda/target/release/examples/d4r_emit \
|
|||||||
kernels/build.sh all kernels/out/native
|
kernels/build.sh all kernels/out/native
|
||||||
```
|
```
|
||||||
|
|
||||||
`kernels/out/native` then holds one code object per replaced DLSS kernel; set `NativeKernelDirFast` in d4r.ini (or `D4R_ZLUDA_NATIVE_DIR`) to it. `kernels/build.sh k` or `m` builds only the network layers and needs neither the DLL nor ZLUDA. The texture kernels (`tex`) extract PTX from your DLL into `kernels/extracted/` and compile it offline for `D4R_GPU_ARCH` through `D4R_ZLUDA_EMIT`; that directory and the build output are git-ignored and must not be redistributed. On gfx12, build a second variant with `D4R_NATIVE_FP8=1` and place it in an `<arch>-fp8` folder.
|
`kernels/out/native` then holds one code object per replaced DLSS kernel; set `NativeKernelDirFast` in d4r.ini (or `D4R_ZLUDA_NATIVE_DIR`) to it. `kernels/build.sh k` or `m` builds only the network layers and needs neither the DLL nor ZLUDA. `kernels/build.sh l` builds L's shared network layers and unfolded texture variants; it needs the DLL and emitter. The texture kernels (`tex`, including L) extract PTX from your DLL into `kernels/extracted/` and compile it offline for `D4R_GPU_ARCH` through `D4R_ZLUDA_EMIT`; that directory and the build output are git-ignored and must not be redistributed. On gfx12, build a second variant with `D4R_NATIVE_FP8=1` and place it in an `<arch>-fp8` folder.
|
||||||
|
|
||||||
|
To add the accuracy option to a developer set, repeat the build with `D4R_PREFER_ACCURACY=1` and output `kernels/out/native/accuracy` (multi-target builds: `kernels/accuracy/<arch>` and `<arch>-fp8`). Use a separate empty directory; successfully built accuracy sets receive `d4r-accuracy.txt`. Enable `[Kernels] PreferAccuracy = true` and restart. The option defaults to false and never substitutes the fast set when accuracy binaries are absent.
|
||||||
|
|
||||||
## 6. Configure and play
|
## 6. Configure and play
|
||||||
|
|
||||||
@@ -101,18 +103,42 @@ D4R_OPTISCALER=/path/to/OptiScaler_0.9.4.7z \
|
|||||||
D4R_DLSS_DLLS=/path/to/310.7/nvngx_dlss.dll:/path/to/310.9/nvngx_dlss.dll \
|
D4R_DLSS_DLLS=/path/to/310.7/nvngx_dlss.dll:/path/to/310.9/nvngx_dlss.dll \
|
||||||
D4R_BUNDLE_DLSS=/path/to/nvngx_dlss.dll D4R_BUNDLE_NGX=/path/to/_nvngx.dll D4R_BUNDLE_TEX=kernels/out/native \
|
D4R_BUNDLE_DLSS=/path/to/nvngx_dlss.dll D4R_BUNDLE_NGX=/path/to/_nvngx.dll D4R_BUNDLE_TEX=kernels/out/native \
|
||||||
D4R_ZLUDA_DIR=~/.cache/d4r-zluda-current D4R_VKD3D_DIR=~/.cache/d4r-vkd3d-d4r D4R_ROCM_DIR=/opt/rocm \
|
D4R_ZLUDA_DIR=~/.cache/d4r-zluda-current D4R_VKD3D_DIR=~/.cache/d4r-vkd3d-d4r D4R_ROCM_DIR=/opt/rocm \
|
||||||
|
D4R_ZLUDA_EMIT=/path/to/zluda/target/release/examples/d4r_emit \
|
||||||
scripts/package_release.sh # -> dist/d4r-<version>.zip
|
scripts/package_release.sh # -> dist/d4r-<version>.zip
|
||||||
```
|
```
|
||||||
|
|
||||||
The script:
|
The script:
|
||||||
- builds the shim, the bridge and the network-layer kernels for gfx1100–gfx1103 and gfx1200–gfx1201 by default (`D4R_GPU_ARCHS` can select fewer targets); each gfx12 target also gets an `<arch>-fp8` variant;
|
- builds the shim, the bridge and both fast and accuracy network-layer kernels for gfx1100–gfx1103 and gfx1200–gfx1201 by default (`D4R_GPU_ARCHS` can select fewer targets); each gfx12 target also gets an `<arch>-fp8` variant;
|
||||||
- writes the kernel manifest from the DLLs you list (it records hashes of their PTX, nothing else);
|
- writes the kernel manifest from the DLLs you list (it records hashes of their PTX, nothing else);
|
||||||
- stages OptiScaler as `dxgi.dll` with the settings in `packaging/optiscaler.settings`;
|
- stages OptiScaler as `dxgi.dll` with the settings in `packaging/optiscaler.settings`;
|
||||||
- adds NVIDIA's two DLLs and any supplied texture kernels (built from NVIDIA's PTX by `kernels/build.sh tex`);
|
- adds NVIDIA's two DLLs and any supplied texture kernels (built from NVIDIA's PTX by `kernels/build.sh tex`); builds accuracy texture sets with `D4R_ZLUDA_EMIT` unless marked prebuilt sets are supplied in `D4R_BUNDLE_TEX/accuracy/<target>`;
|
||||||
- zips the result together with the ZLUDA and vkd3d-proton builds, the ROCm runtime (as `d4r/rocm`), `packaging/d4r.ini`, the licenses and the patches.
|
- zips the result together with the ZLUDA and vkd3d-proton builds, the ROCm runtime (as `d4r/rocm`), `packaging/d4r.ini`, the licenses and the patches.
|
||||||
|
|
||||||
The NVIDIA files are not covered by d4r's license; redistributing them is up to whoever publishes the zip. `D4R_BUNDLE_NVIDIA=0` builds `d4r-<version>-nonvidia.zip` without them and without the texture kernels. [architecture.md](architecture.md#portable-installs-the-release-zip) describes how the installed files work together.
|
The NVIDIA files are not covered by d4r's license; redistributing them is up to whoever publishes the zip. `D4R_BUNDLE_NVIDIA=0` builds `d4r-<version>-nonvidia.zip` without them and without the texture kernels. [architecture.md](architecture.md#portable-installs-the-release-zip) describes how the installed files work together.
|
||||||
|
|
||||||
|
### GLIBC 2.41 release builds
|
||||||
|
|
||||||
|
Build the pinned environment with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker build -f packaging/build/Dockerfile.glibc241 -t d4r-build:glibc241 packaging/build
|
||||||
|
```
|
||||||
|
|
||||||
|
Inside that image, `scripts/build_release_glibc241.sh` rebuilds the GPU device helpers, ZLUDA and its LLVM, `d4r_emit`, the NGX shim, Wine CUDA bridge, patched vkd3d-proton, and performance/accuracy texture sets for every release target. Packaging then builds both network sets, checks all bundled Linux libraries against 2.41, and records the toolchain in `d4r/source/BUILD_INFO.txt`.
|
||||||
|
|
||||||
|
Mount the repository at `/work`, ROCm's compiler/headers/device libraries at `/opt/rocm`, and an official Rust toolchain (1.98.0 used for 0.1.3) at `/opt/rust`, with `/opt/rust/bin` on `PATH`. Keep toolchain and third-party runtime inputs read-only. Supply the normal package inputs listed above and these build settings:
|
||||||
|
|
||||||
|
| Variable | Input |
|
||||||
|
|---|---|
|
||||||
|
| `D4R_RELEASE_BUILD_ROOT` | writable directory under `/work/build/` |
|
||||||
|
| `D4R_ZLUDA_SRC` | isolated checkout inside that directory, with both pinned LLVM and HiGHS submodules, the real OCKL LFS payload, and patches `0002`–`0007` applied |
|
||||||
|
| `D4R_VKD3D_SRC` | isolated vkd3d-proton checkout inside that directory, with its submodules and patches `0001` and `0002` applied |
|
||||||
|
| `CARGO_HOME` | writable build-local Cargo cache; prefetch the locked dependencies for an offline build |
|
||||||
|
| `D4R_ROCM_DIR` | `/opt/rocm` |
|
||||||
|
| `D4R_ROCM_LINK_STUBS` | optional directory for additional ROCm link libraries |
|
||||||
|
|
||||||
|
Run `bash scripts/build_release_glibc241.sh` inside the container. The script checks that the build system actually reports `glibc 2.41`, rejects external source-cache checkouts, builds with the locked Cargo dependencies offline, and runs the configuration tests before packaging. The ZIP and checksum go in `$D4R_RELEASE_BUILD_ROOT/dist/`; logs and `toolchain.txt` remain in the build directory. AMD's bundled ROCm runtime, OptiScaler and NVIDIA's libraries remain supplied binaries; the Linux ABI check covers the bundled ROCm libraries too.
|
||||||
|
|
||||||
`D4R_BUNDLE_TEX` accepts the older flat directory for gfx1101, or a directory with per-target subdirectories (`gfx1100/`–`gfx1103/`, `gfx1200/`, `gfx1201/`, plus `gfx1200-fp8/` and `gfx1201-fp8/`). Build each texture set with `D4R_GPU_ARCH` and `D4R_ZLUDA_EMIT`; the `-fp8` sets also need `D4R_NATIVE_FP8=1`. `d4r_emit` targets those GPUs offline. Missing texture kernels fall back to ZLUDA on that target; the network-layer kernels are still included. Only the RX 7700 XT has been tested on real hardware.
|
`D4R_BUNDLE_TEX` accepts the older flat directory for gfx1101, or a directory with per-target subdirectories (`gfx1100/`–`gfx1103/`, `gfx1200/`, `gfx1201/`, plus `gfx1200-fp8/` and `gfx1201-fp8/`). Build each texture set with `D4R_GPU_ARCH` and `D4R_ZLUDA_EMIT`; the `-fp8` sets also need `D4R_NATIVE_FP8=1`. `d4r_emit` targets those GPUs offline. Missing texture kernels fall back to ZLUDA on that target; the network-layer kernels are still included. Only the RX 7700 XT has been tested on real hardware.
|
||||||
|
|
||||||
## Checks
|
## Checks
|
||||||
|
|||||||
+26
-2
@@ -35,12 +35,18 @@ The release is built this way, with hashes from DLSS 310.7.0 and 310.9.1. The PT
|
|||||||
- `pwin_wide.h`: the deep layers, which have only 6–77 windows per frame. It uses 4·NG waves per window, split by token tile and channel group, so the GPU stays busy.
|
- `pwin_wide.h`: the deep layers, which have only 6–77 windows per frame. It uses 4·NG waves per window, split by token tile and channel group, so the GPU stays busy.
|
||||||
- **Weights:** prep kernels expand the weights into WMMA operand images once.
|
- **Weights:** prep kernels expand the weights into WMMA operand images once.
|
||||||
|
|
||||||
**DLSS 4.5, preset M (`kernels/m`, `rrlite_*`).**
|
**DLSS 4.5, presets L/M (`kernels/m`, `rrlite_*`).**
|
||||||
- **Network:** the Swin blocks of DLSS 4.5, whose weights are FP8. Their prep kernels expand the weights to f16 WMMA operands, once per weight set.
|
- **Network:** the Swin blocks of DLSS 4.5, whose weights are FP8. Their prep kernels expand the weights to f16 WMMA operands, once per weight set.
|
||||||
- **Template:** `swin_block.h` covers encoders, the tube-shaped enc3 and decoders.
|
- **Template:** `swin_block.h` covers encoders, the tube-shaped enc3 and decoders.
|
||||||
- **Output encoding:** the FP8 output is encoded two values at a time with packed 16-bit operations (`enc8x2`, exhaustively equal to the scalar encoder), and the 2×2 patch merge reads the rounded f16 values the codes decode to, from a row layout without LDS bank conflicts, instead of decoding the bytes again in every wave.
|
- **Output encoding:** the FP8 output is encoded two values at a time with packed 16-bit operations (`enc8x2`, exhaustively equal to the scalar encoder), and the 2×2 patch merge reads the rounded f16 values the codes decode to, from a row layout without LDS bank conflicts, instead of decoding the bytes again in every wave.
|
||||||
- **Weight loads:** enc1 forms each weight tile's address in scalar registers (`SWIN_SCALAR_BLOAD`); the 8-wave layers are faster without it.
|
- **Weight loads:** enc1 forms each weight tile's address in scalar registers (`SWIN_SCALAR_BLOAD`); the 8-wave layers are faster without it.
|
||||||
|
|
||||||
|
**Preset L texture variants.** L uses unfolded `rrlite_enc0_4x4_*`, `rrlite_dec0_4x4`, and `rrlite_post_3_*` kernels. The L build keeps their full neural arithmetic and replaces surface stores with the existing native format-conversion functions. M's folded enc0 tail and dec0 head cannot be used for these variants. All four input flag combinations and all eight post variants are built, along with the shared downsample kernels. `all` and `tex` include these variants; `l` builds the shared Swin layers plus only the L texture set. L remains experimental; these replacements do not establish RTX parity or game performance.
|
||||||
|
|
||||||
|
**Motion mitigation.** `[Kernels] NativeSwinEncoders = false` (`D4R_NATIVE_SWIN_ENCODERS=0`) bypasses only `rrlite_enc1_4x4` and `rrlite_enc2_4x4` in the native manifest. L/M then use the original translated PTX for those encoders, retaining native tube, decoder and texture kernels. Keep `PreferAccuracy = true` for the tested conservative path. It reduced the extra building-edge trails in a 64-frame moving Townfall capture at 853×480 → 2560×1440 on gfx1101; it does not recover detail absent from the low-resolution input or establish RTX parity. The default remains `true` for speed. Flat developer sets without manifests fall back entirely when this switch is off; add a manifest to keep selective acceleration.
|
||||||
|
|
||||||
|
Initial validation: a gfx1101 fast L build completed two Ultra Performance harness frames (192×108 → 576×324, DLSS 310.7). Its finite, nonzero RGBA16F output matched the original translated L texture kernels byte for byte with the same native Swin layers. A gfx1201 FP8 accuracy L set compiled successfully; RDNA4 hardware and 4K game output have not been tested. Full packaging rebuilds missing L variants when given an older texture bundle.
|
||||||
|
|
||||||
## RDNA4
|
## RDNA4
|
||||||
|
|
||||||
`kernels/common/wmma_layout.h` selects the WMMA operand and accumulator layout. gfx12 uses eight f16 values per lane in each K half; the accumulator rows are `i + 8·half`. The gfx11 path retains its original layout. `D4R_WMMA_LAYOUT=12` on gfx11 is a test shim: it exercises gfx12 indexing but executes gfx11 WMMA, so its replay output can be compared byte for byte with the existing gfx11 build.
|
`kernels/common/wmma_layout.h` selects the WMMA operand and accumulator layout. gfx12 uses eight f16 values per lane in each K half; the accumulator rows are `i + 8·half`. The gfx11 path retains its original layout. `D4R_WMMA_LAYOUT=12` on gfx11 is a test shim: it exercises gfx12 indexing but executes gfx11 WMMA, so its replay output can be compared byte for byte with the existing gfx11 build.
|
||||||
@@ -64,21 +70,31 @@ For ZLUDA-compiled code, compare rocjitsu runs with other rocjitsu runs: the emu
|
|||||||
|
|
||||||
## Numerics
|
## Numerics
|
||||||
|
|
||||||
NVIDIA's tensor-core code rounds its f16 accumulator after every 16-deep step. The native kernels either do the same (bit-exact against the reference models) or keep f32 accumulators through a chain (`PWIN_F32ACC`, `SWIN_F32ACC`). The f32 version is more accurate than NVIDIA's own rounding, and its replayed output differs from the exact one by about 66 dB PSNR, which is invisible.
|
The conservative kernels round their f16 accumulator after each MMA step (k16 for K, k32 for M). The fast kernels keep f32 accumulators through a chain (`PWIN_F32ACC`, `SWIN_F32ACC`); RDNA3 M also skips intermediate FP8 re-quantization (`SWIN_NO_Q8`). Wider arithmetic changes the network's intermediate values; it does not establish better image quality. Recorded fast-versus-conservative replays were close (about 66 dB PSNR), but that is not an RTX reference comparison or a guarantee for all scenes.
|
||||||
|
|
||||||
The installed K set uses f32 accumulation for enc0–enc2 and dec0–dec2 and exact rounding for the deep layers. The M set uses the fast numerics throughout.
|
The installed K set uses f32 accumulation for enc0–enc2 and dec0–dec2 and exact rounding for the deep layers. The M set uses the fast numerics throughout.
|
||||||
|
|
||||||
|
**`[Kernels] PreferAccuracy = true`** (default `false`, environment `D4R_PREFER_ACCURACY=1`) selects a separate accuracy set for all K/M network layers and all texture/output variants, on every supported target, including gfx12 FP8. K disables `PWIN_F32ACC`; M restores FP8 quantization and rounds each k32 accumulator to f16. Texture builds use wave32 and retain the original denormal requirements; the existing exact round-half-away rewrite is retained. Native texture heads/tails already round their accumulators per k32 step. The bridge also disables `IgnoreDenormals`, translated f32 accumulator shadows, wave64 translation and NGX synchronization elision before ZLUDA loads, including when native kernels are off or fail their PTX hash check. Other interop and latency settings remain independent.
|
||||||
|
|
||||||
|
The bridge selects `kernels/accuracy/<target>` (or `<target>-fp8`). Accuracy directories carry a `d4r-accuracy.txt` marker written only after a successful build. A marked flat developer directory also works. Missing accuracy variants fall back to translated PTX and are logged; the bridge never substitutes fast native binaries. Restart the game when changing the setting. The mode uses a separate `d4r-accuracy` JIT cache under the configured cache directory so older runtimes cannot reuse experimental fast-math results. The first accuracy run may compile new kernels.
|
||||||
|
|
||||||
|
This mode aims to preserve NVIDIA's arithmetic, not to promise bit-identical RTX output: matrix accumulation order differs across hardware, and the shim still canonicalizes input formats and does not forward the optional transparency/current-color-bias masks. RTX parity requires identical captured inputs, library version, preset and temporal sequence on NVIDIA hardware.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
kernels/build.sh k # DLSS 4 layers (ROCm clang only)
|
kernels/build.sh k # DLSS 4 layers (ROCm clang only)
|
||||||
kernels/build.sh m # DLSS 4.5 layers
|
kernels/build.sh m # DLSS 4.5 layers
|
||||||
|
kernels/build.sh l # L shared layers + unfolded textures (needs DLL and D4R_ZLUDA_EMIT)
|
||||||
kernels/build.sh tex # texture kernels (needs D4R_DLSS_DLL and D4R_ZLUDA_BUILD)
|
kernels/build.sh tex # texture kernels (needs D4R_DLSS_DLL and D4R_ZLUDA_BUILD)
|
||||||
kernels/build.sh all DIR # everything into DIR (default kernels/out/native)
|
kernels/build.sh all DIR # everything into DIR (default kernels/out/native)
|
||||||
|
D4R_PREFER_ACCURACY=1 kernels/build.sh all DIR/accuracy # conservative versions of every family
|
||||||
```
|
```
|
||||||
|
|
||||||
`D4R_GPU_ARCH` selects one target for this standalone build (default gfx1101). The release script builds the network layers for gfx1100–gfx1103 and gfx1200–gfx1201 by default and puts each set in its own directory. The network kernels need gfx11 or gfx12 WMMA. Texture-kernel builds use `D4R_ZLUDA_EMIT` to generate code objects for the selected target without that GPU. Set `D4R_NATIVE_FP8=1` on gfx12 for the matching FP8 M and texture variants.
|
`D4R_GPU_ARCH` selects one target for this standalone build (default gfx1101). The release script builds the network layers for gfx1100–gfx1103 and gfx1200–gfx1201 by default and puts each set in its own directory. The network kernels need gfx11 or gfx12 WMMA. Texture-kernel builds use `D4R_ZLUDA_EMIT` to generate code objects for the selected target without that GPU. Set `D4R_NATIVE_FP8=1` on gfx12 for the matching FP8 M and texture variants.
|
||||||
|
|
||||||
|
The release includes both fast and accuracy network sets. Full releases also include accuracy texture sets: supply them in `D4R_BUNDLE_TEX/accuracy/<target>` or set `D4R_ZLUDA_EMIT` so packaging builds them. Build accuracy sets into a separate directory; the builder refuses to mark a directory containing unmarked older binaries as accurate.
|
||||||
|
|
||||||
## Validating a kernel
|
## Validating a kernel
|
||||||
|
|
||||||
1. **Capture launches.** Set `D4R_CUDA_REPLAY_DUMP_DIR` (with `D4R_CUDA_REPLAY_DUMP_FILTER=<kernel name>`) while running the D3D12 harness or a game. The bridge then writes each matching launch as `manifest.txt`, `args.bin` and one `alloc-N.bin` per buffer.
|
1. **Capture launches.** Set `D4R_CUDA_REPLAY_DUMP_DIR` (with `D4R_CUDA_REPLAY_DUMP_FILTER=<kernel name>`) while running the D3D12 harness or a game. The bridge then writes each matching launch as `manifest.txt`, `args.bin` and one `alloc-N.bin` per buffer.
|
||||||
@@ -110,3 +126,11 @@ Median GPU time per frame at Quality (1706×960 → 2560×1440, RX 7700 XT), fro
|
|||||||
| **total** | **3.10** | **2.97** | **total** | **9.04** | **8.23** |
|
| **total** | **3.10** | **2.97** | **total** | **9.04** | **8.23** |
|
||||||
|
|
||||||
Runs of the same build vary by about ±3% per kernel. M additionally skips its output copy now (direct output through the downsample kernel), which is not a kernel and not in the table. Games whose flags differ from Townfall's (motion vectors at render resolution, LDR input, regular depth) gain more: those kernel variants used to run as plain ZLUDA compiles (K 3.6–4.6 ms → 2.8–3.0 ms, M −0.5 to −0.7 ms per frame).
|
Runs of the same build vary by about ±3% per kernel. M additionally skips its output copy now (direct output through the downsample kernel), which is not a kernel and not in the table. Games whose flags differ from Townfall's (motion vectors at render resolution, LDR input, regular depth) gain more: those kernel variants used to run as plain ZLUDA compiles (K 3.6–4.6 ms → 2.8–3.0 ms, M −0.5 to −0.7 ms per frame).
|
||||||
|
|
||||||
|
## Experimental thin-feature motion coverage
|
||||||
|
|
||||||
|
`[Interop] MotionVectorDilation = 1` or `2` (`D4R_MOTION_DILATION`) extends nearby foreground motion into pixels whose depth otherwise belongs to the background. The value selects the opaque-surface radius in render pixels. Both enabled values also fill sky gaps using the nearest foreground pixel within an eight-render-pixel circle (far depth 0 for reversed Z, 1 for normal Z). This is the wider sky fill from the comparison; it can strengthen trails in some frames and remains opt-in through `MotionVectorDilation`. This is a depth-guided approximation to better thin-feature motion coverage, not engine-side conservative rasterization. It improved the captured Townfall rooftop where the earlier native-encoder fallback did not resolve the artifact.
|
||||||
|
|
||||||
|
The d4r-owned GPU kernel preserves the original depth/motion buffers and writes a separate RG16F velocity texture. The worker waits for its completion before NGX evaluates. No per-frame CPU readback is needed. It requires same-frame linear VRAM inputs, full-resolution unjittered motion vectors and zero subrect bases. Unsupported features or unavailable kernels keep the original vectors. The default is `0` (off); larger radii can affect disocclusion and unrelated foreground edges. The method does not recreate geometry missing from every input frame or establish RTX parity.
|
||||||
|
|
||||||
|
`scripts/build_wine_nvcuda_bridge.sh` embeds the support kernels when `D4R_ROCM_DIR` points to ROCm clang. All six gfx110x/gfx120x targets compile; physical validation is currently gfx1101 only. A bridge built without the support kernels reports the feature unavailable rather than changing inputs. Optional GPU regressions cover forward/reversed depth, two radii, jitter, padded rows and preservation of input buffers; see `tests/test_motion_kernel_gpu.py`.
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
# Native Windows RDNA3 / RDNA4 game development package
|
||||||
|
|
||||||
|
Validated hardware: Windows 11 x64 / Radeon RX 9070 XT / gfx1201. Native K and
|
||||||
|
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
|
||||||
|
GPU NaN/Inf checks. Both K and M pass 3840x2160 output on this machine.
|
||||||
|
Cyberpunk 2077 2.31 also reaches saved gameplay with K and M at 3840x2160:
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
OptiScaler using the scripts in `scripts/windows`, then run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\scripts\windows\package-windows-game.ps1 -ArchivePath "$PWD\dist\windows-rdna4-game.zip"
|
||||||
|
```
|
||||||
|
|
||||||
|
The package pins TheRock 10.2.0a20260929 because its Windows external-memory
|
||||||
|
mapping lifetime passes the repeated import/release test. Stable HIP 7.2 has
|
||||||
|
a reproduced mapped-view leak; see `docs/windows-rdna4-port.md` for the source
|
||||||
|
fix, exact dependency versions, validation and complete build commands.
|
||||||
|
The ZIP contains only manifest-listed files, including a snapshot of committed
|
||||||
|
d4r source. Private NVIDIA-derived objects, caches and diagnostic captures are
|
||||||
|
excluded. Original FP8 M and the optional hardware-packing variant pass strict
|
||||||
|
replay/network tests but do not establish a full-network speedup, so the game
|
||||||
|
package retains its FP16-equivalent M baseline.
|
||||||
|
|
||||||
|
Run a D3D12 game without anti-cheat using your locally supplied NVIDIA DLLs:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\dist\windows-rdna4-game\windows-game.ps1 -GameExe "D:\Games\SILENT HILL 2\SHProto\Binaries\Win64\SHProto-Win64-Shipping.exe" -NgxCore "C:\Users\Administrator\d4r\_nvngx.dll" -DlssDll "C:\Users\Administrator\d4r\nvngx_dlss.dll" -Preset 11
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `-Preset 13` for M. The validated manifest requires DLSS 310.9.1 with the
|
||||||
|
recorded SHA256; other feature DLLs require regenerating and validating the
|
||||||
|
native manifest. Select an upscaler supported by OptiScaler in the game.
|
||||||
|
The script installs `dxgi.dll`, an explicit DLSS configuration and a private
|
||||||
|
`d4r` runtime directory, and captures stdout/stderr, exit code, loaded DLL paths,
|
||||||
|
driver information and OptiScaler logs. Ordinary runs use a process job without
|
||||||
|
an attached debugger. Add `-CaptureExceptions` to capture exception events and
|
||||||
|
minidumps when reproducing a crash. A local
|
||||||
|
ZIP bundle is printed at exit. `-RunSeconds 120` bounds a diagnostic run;
|
||||||
|
the normal default allows you to play until you close the game.
|
||||||
|
|
||||||
|
For the Silent Hill 2 startup black-screen investigation, add
|
||||||
|
`-DiagnosticResolution 1280x720`. This backs up its two graphics configuration
|
||||||
|
files, temporarily selects a window at that resolution, and restores the
|
||||||
|
original bytes after the test. It does not change progression saves. First-use
|
||||||
|
PTX compilation can be slow; `-CacheDirectory <existing-ZLUDA-cache>` reuses
|
||||||
|
modules with matching GPU, codegen switches and ZLUDA binary fingerprint.
|
||||||
|
|
||||||
|
Add `-ValidateOutput` to scan the entire RGBA16F DLSS output for NaN/Inf on
|
||||||
|
the GPU before returning it to D3D12. Only two diagnostic counters (8 bytes)
|
||||||
|
are read by the CPU; image data remains in VRAM. Validation failures are
|
||||||
|
reported explicitly. Disable this diagnostic when measuring production speed.
|
||||||
|
|
||||||
|
`-ProfileStages` records CPU completion time for seven interop/NGX stages,
|
||||||
|
command-list split/replay, submitted state publication and suffix completion.
|
||||||
|
The recording interval measures NGX calls on the recording thread, not Present.
|
||||||
|
`-ProfileKernels` enables synchronizing HIP-event kernel profiling and changes
|
||||||
|
frame scheduling; use it separately from a production FPS measurement.
|
||||||
|
`-ProfileCommandHooks` samples one in 64 D3D12 interceptions to distinguish
|
||||||
|
tracking/lock waits from driver calls. It is disabled in ordinary runs.
|
||||||
|
`-ProfileCudaApi` aggregates existing CUDA API host durations without adding
|
||||||
|
HIP events or synchronization. `-UncachedInteropLists` restores per-frame
|
||||||
|
copy-list allocation for comparison with the normal safely reused lists.
|
||||||
|
`-ProfileGpuBoundary` requires `-AsyncInterop` and measures D3D12 GPU intervals
|
||||||
|
around the input/output copies and external fence. It reads 32 timestamp bytes
|
||||||
|
only after existing completion, with no extra CPU wait. Its external interval
|
||||||
|
includes HIP and scheduling; it is not an isolated transformer kernel time.
|
||||||
|
The runner drains stdout/stderr concurrently as raw bytes; the earlier
|
||||||
|
line-oriented PowerShell collector imposed large per-frame delays. The optional
|
||||||
|
ZLUDA kernel profiler is for diagnostics only. Measurements and reproduction
|
||||||
|
commands are in [windows-performance.md](windows-performance.md).
|
||||||
|
|
||||||
|
`-AsyncInterop` enables the experimental K scheduling path. Submission queues
|
||||||
|
the current frame's input/output dependencies and returns while a CUDA worker
|
||||||
|
executes DLSS. Owned command memory is retained until GPU completion; feature
|
||||||
|
release and resource reconfiguration wait for pending consumers. It passes
|
||||||
|
the standalone queued-frame and Release/CreateFeature exact-output regression,
|
||||||
|
but remains opt-in while game stability and performance are checked. It
|
||||||
|
currently requires one D3D12 command queue. This switch is independent of
|
||||||
|
profiling and output scans; omit those diagnostics for a production FPS run.
|
||||||
|
|
||||||
|
Ordinary runs use OptiScaler info logging and suppress successful HIP/CUDA API
|
||||||
|
messages. Errors, GPU identity, imports, frame records and native/translated
|
||||||
|
kernel launches remain logged. `-VerboseRuntime` enables all API and frontend
|
||||||
|
trace messages when investigating a failure. Numerical relaxation flags are
|
||||||
|
cleared in the child process; FP16 rounding and subnormals stay enabled.
|
||||||
|
|
||||||
|
The optional K output-store optimization is built locally from your NVIDIA DLL
|
||||||
|
with `build-native-texture.ps1` and checked by `test-native-texture.ps1`;
|
||||||
|
commands and measurements are in [windows-performance.md](windows-performance.md).
|
||||||
|
Add `-LocalTextureKernels "C:\Users\Administrator\d4r\build\private-textures-gfx1201"`
|
||||||
|
to the game command to install it. The object/manifest hashes and DLSS identity
|
||||||
|
must match validation. These generated objects contain NVIDIA code, remain
|
||||||
|
private, and are excluded from the distributable package. Ordinary launches
|
||||||
|
inherit the game's resolution/window settings; the default argument is `-dx12`.
|
||||||
|
|
||||||
|
Original files are backed up before replacement. Restore with:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\dist\windows-rdna4-game\windows-game.ps1 -Action restore -GameExe "D:\Games\SILENT HILL 2\SHProto\Binaries\Win64\SHProto-Win64-Shipping.exe"
|
||||||
|
```
|
||||||
|
|
||||||
|
Restore refuses to overwrite files changed since installation. Backups and
|
||||||
|
diagnostic results are retained. The script does not change progression saves,
|
||||||
|
system DLLs, driver settings or security settings. All runtime work is native
|
||||||
|
Windows. Colour, depth, motion, exposure and output cross APIs through VRAM
|
||||||
|
and shared D3D12 fences; the backend waits for the current frame and never
|
||||||
|
uses a previous-frame fallback.
|
||||||
|
|
||||||
|
OptiScaler source modifications are GPL-3.0 and exported under
|
||||||
|
`source-patches/optiscaler`. ZLUDA source modifications are exported under
|
||||||
|
`source-patches/zluda`; the LLVM gfx1154 backport is under
|
||||||
|
`source-patches/zluda-llvm`. Dependency license texts and build hashes are included.
|
||||||
@@ -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.
|
||||||
@@ -0,0 +1,810 @@
|
|||||||
|
# Windows RDNA4 measurements
|
||||||
|
|
||||||
|
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
|
||||||
|
`D4R_ZLUDA_WMMA=1`, FP8 widening, native FP8 disabled, f16 reference rounding.
|
||||||
|
|
||||||
|
The original serializing profiler uses HIP events on the actual launch stream, including
|
||||||
|
separate measurements of one-time weight preparation. It synchronizes each
|
||||||
|
sampled launch and changes scheduling: these numbers are not application FPS.
|
||||||
|
HIP occupancy predictions are not measured hardware occupancy. The native
|
||||||
|
objects' actual wave32, LDS, VGPR/SGPR and spill counts are available in
|
||||||
|
`llvm-readobj --notes` and can be attached to the JSON report. Memory bandwidth
|
||||||
|
and WMMA utilization were not measured; no utilization is inferred from timing.
|
||||||
|
|
||||||
|
The identical four-frame standalone D3D12/OptiScaler workloads output 512x288.
|
||||||
|
Summing the measured main-kernel event times and dividing by four gives:
|
||||||
|
|
||||||
|
| Preset | Fully translated, ms/frame | Native FP16-equivalent, ms/frame |
|
||||||
|
| --- | ---: | ---: |
|
||||||
|
| K | 157.745441 | 0.684675 |
|
||||||
|
| M | 7.428538 | 1.566500 |
|
||||||
|
|
||||||
|
Both rows include translated input/output tails. Native overrides cover all
|
||||||
|
11 K layers and five M layers; M's tube layer executes six times per frame.
|
||||||
|
M native and translated RGB are bit-exact, with max absolute/relative error 0.
|
||||||
|
K native remains bit-exact against its independently validated native control;
|
||||||
|
fully translated K has the previously documented arithmetic differences.
|
||||||
|
All outputs are finite. These are reference-correct codegen settings, not
|
||||||
|
measurements of a relaxed fast-math translated configuration.
|
||||||
|
|
||||||
|
M's first profiled Evaluate includes about 0.9 seconds of host initialization;
|
||||||
|
the four-sample CPU mean must not be treated as steady-state latency. Its
|
||||||
|
median Evaluate host completion is 4.855 ms. GPU events and prep phases are
|
||||||
|
reported separately in the raw JSON.
|
||||||
|
|
||||||
|
The initial game runner used PowerShell line-oriented output redirection.
|
||||||
|
It heavily throttled verbose stdout/stderr. The runner now concurrently drains
|
||||||
|
raw byte streams using the same approach as the standalone diagnostics.
|
||||||
|
|
||||||
|
| Silent Hill 2 stage, CPU mean | Initial capture, ms | Raw-byte capture with HIP profiler, ms |
|
||||||
|
| --- | ---: | ---: |
|
||||||
|
| Input fence wait | 5.641 | 0.833 |
|
||||||
|
| Input conversion and CUDA array upload | 13.196 | 0.668 |
|
||||||
|
| NGX Evaluate host call | 25.715 | 3.350 |
|
||||||
|
| NGX all-stream completion | 1.846 | 0.033 |
|
||||||
|
| Output CUDA array download | 4.288 | 0.167 |
|
||||||
|
| Output conversion and shared fence signal | 1.500 | 0.033 |
|
||||||
|
|
||||||
|
The first run additionally scanned every output (10.142 ms in that throttled
|
||||||
|
capture); the kernel-profiling run disables that scan. Both use a temporary
|
||||||
|
1280x720 window and current-frame VRAM interop. These are separate menu/game
|
||||||
|
runs, not a controlled whole-game FPS comparison. No synchronization was
|
||||||
|
removed to obtain the improvement. The raw-byte profiling run completed
|
||||||
|
2345 K frames, with no d4r failure, CPU image copy or previous-frame output.
|
||||||
|
|
||||||
|
Reproduce native or translated measurements with locally supplied NVIDIA DLLs
|
||||||
|
at the repository root and the separately built profiling runtime:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\scripts\windows\profile-windows-rdna4.ps1 -Preset 11
|
||||||
|
.\scripts\windows\profile-windows-rdna4.ps1 -Preset 11 -Translated
|
||||||
|
.\scripts\windows\profile-windows-rdna4.ps1 -Preset 13
|
||||||
|
.\scripts\windows\profile-windows-rdna4.ps1 -Preset 13 -Translated
|
||||||
|
```
|
||||||
|
|
||||||
|
`profile_report.py <results-directory> --output profile.json` summarizes raw
|
||||||
|
CPU stages and every kernel's event time, launch geometry, registers, private
|
||||||
|
memory, LDS and predicted blocks per multiprocessor. Add
|
||||||
|
`--metadata-directory <directory-of-llvm-readobj-notes>` for native wave sizes
|
||||||
|
and compiler spill counts. Reports and proprietary workload inputs remain local.
|
||||||
|
|
||||||
|
Native FP8 remains a separate optional experiment; the package uses the
|
||||||
|
validated FP16-equivalent baseline. Its later checks and timings are below.
|
||||||
|
|
||||||
|
## K host synchronization and asynchronous submission
|
||||||
|
|
||||||
|
Current priority is K performance. The user's synchronous 4K reading is
|
||||||
|
49–51 FPS / 53% GPU utilization, versus approximately 80+ FPS / 100% with
|
||||||
|
FSR4. These readings do not identify a CPU hardware limit or provide a
|
||||||
|
controlled same-scene comparison. M/FP8 optimization is deferred.
|
||||||
|
|
||||||
|
ZLUDA candidate `a1c506f` adds `D4R_ZLUDA_PROFILE_API`, enabled by the game
|
||||||
|
runner's `-ProfileCudaApi`. It measures existing API host durations, aggregates
|
||||||
|
per thread and logs every two seconds without HIP events or additional waits.
|
||||||
|
The synchronous control shows five `cuCtxSynchronize` calls per frame with
|
||||||
|
about 3.9 ms aggregate host time. CUDA launch host overhead is approximately
|
||||||
|
0.46 ms per frame. Cached copy-list setup is only 0.011 ms median. These host
|
||||||
|
measurements include GPU waits and must not be added to GPU kernel times.
|
||||||
|
|
||||||
|
| K/4K command submission, CPU median | Synchronous | First async | Async without CUDA mutex |
|
||||||
|
| --- | ---: | ---: | ---: |
|
||||||
|
| Entire submission, ms | 10.512 | 2.676 | 0.181 |
|
||||||
|
| DLSS boundary, ms | 10.083 | 2.604 | 0.124 |
|
||||||
|
| Consumer suffix completion, ms | 0.350 | 0.005 | 0.004 |
|
||||||
|
|
||||||
|
The first async game run completes 9481 finite frames; the user reports
|
||||||
|
60 FPS / 63% GPU. The mutex-free run crashes during feature recreation after
|
||||||
|
366 finite frames, so its submission timing is not a successful gameplay
|
||||||
|
benchmark. The standalone burst/recreate reproducer subsequently identifies
|
||||||
|
caller-owned GPU backing freed after ReleaseFeature returns. Waiting for
|
||||||
|
pending D3D12 consumers in ReleaseFeature fixes the reproducer: all twelve
|
||||||
|
sync/async test frames pass, and corresponding RGB outputs are exact. Game
|
||||||
|
stability and FPS must be checked separately on the fixed build.
|
||||||
|
|
||||||
|
The fixed game retest on 2026-10-02 completes 16941 K/4K frames, all finite,
|
||||||
|
including feature recreation and color-format reimports, with zero backend
|
||||||
|
failures. The user reports 62 FPS / 63% GPU. Median CPU submission is
|
||||||
|
0.197 ms, NGX host completion on the worker is 4.400 ms and output validation
|
||||||
|
is 0.380 ms. The 10.162 ms median worker input wait includes queued game
|
||||||
|
rendering. These results establish a stable five-minute test of this fix,
|
||||||
|
not a conclusion about the remaining GPU idle time or FSR4 performance.
|
||||||
|
|
||||||
|
`-AsyncInterop` is opt-in. It queues input/output fences on the submission
|
||||||
|
thread, executes CUDA on a worker and retires owned command memory by GPU
|
||||||
|
completion. Three copy slots bound outstanding ownership. The queue consumes
|
||||||
|
the output of the same frame, with no CPU image copy. The worker's input wait
|
||||||
|
includes its lead behind submitted rendering and is not a CPU submission
|
||||||
|
stall. Neither that duration nor the NGX recording interval measures Present
|
||||||
|
latency. Reconfiguration waits for all consumers; ordinary submission does not
|
||||||
|
hold the CUDA mutex. The experimental runtime currently supports one queue.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\scripts\windows\test-async-k.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
The test generates fresh synchronous controls, submits three frames without
|
||||||
|
intermediate CPU completion, and repeats while releasing/recreating the DLSS
|
||||||
|
feature with work pending. Results include exact RGB comparisons, finite GPU
|
||||||
|
scans, stdout/stderr, exceptions and runtime provenance. To include the local
|
||||||
|
validated output-store optimization, pass its combined native directory with
|
||||||
|
`-NativeRoot`; proprietary objects remain local.
|
||||||
|
|
||||||
|
`-ProfileGpuBoundary` (requires `-AsyncInterop`) adds D3D12 timestamps before
|
||||||
|
and after the VRAM input/output copies. Resolved results are read only once
|
||||||
|
the copy slot's existing fence completes; 32 diagnostic bytes per frame are
|
||||||
|
read and no new completion wait is added. `external_span_ms` spans HIP and
|
||||||
|
host/driver scheduling across the external fence. `between_boundaries_ms`
|
||||||
|
also includes game rendering, and `input_interval_ms` is not Present timing.
|
||||||
|
These queries obey the queue's timestamp ordering and add GPU commands; they
|
||||||
|
are disabled for an ordinary FPS run. GPU clock behavior during idle can
|
||||||
|
affect intervals; no hardware occupancy is inferred. Timestamp semantics:
|
||||||
|
[Microsoft D3D12 timing](https://learn.microsoft.com/en-us/windows/win32/direct3d12/timing).
|
||||||
|
|
||||||
|
The timestamp-enabled 4K burst/recreation gate completes twelve frames, all
|
||||||
|
finite; all six async RGB outputs still match their fresh controls exactly.
|
||||||
|
Each async mode emits all three timing records, including the final frame
|
||||||
|
at feature release. Results: `test-results/k-async-gpu-boundary-4k`.
|
||||||
|
All sixteen CTest gates pass on the rebuilt shim. Reproduce with:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\scripts\windows\test-async-k.ps1 -ProfileGpuBoundary
|
||||||
|
```
|
||||||
|
|
||||||
|
The 2026-10-02 game timestamp run completes 9384 K/4K frames without backend
|
||||||
|
errors (`test-results/silent-hill2-k-gpu-boundary-4k`). Median GPU intervals:
|
||||||
|
input copies 0.061 ms, external span 5.586 ms, output copy 0.208 ms, entire
|
||||||
|
boundary 5.856 ms, between boundaries 9.512 ms. Median input interval is
|
||||||
|
15.383 ms. Output scans and serializing kernel events are off. This locates
|
||||||
|
the remaining time outside the small D3D12 copies; it does not prove whether
|
||||||
|
that time is game GPU work, CPU waits or context scheduling.
|
||||||
|
|
||||||
|
### Present and GPU-busy capture
|
||||||
|
|
||||||
|
`capture-presentmon.ps1` attaches the official PresentMon 2.6.0 x64 CLI to
|
||||||
|
one running game process. It verifies SHA256
|
||||||
|
`b2a706bc6ad475749e3b7e3409263aa1e6906d45bdcf993f6dbc0f660188f1af`,
|
||||||
|
uses a unique ETW session and stops at its time bound/process exit, without
|
||||||
|
replacing any existing trace or installing a service. It saves CSV, console
|
||||||
|
output, driver version, target PID and the HWS registry setting. The default
|
||||||
|
downloads the pinned CLI into `.tools` if needed. Run elevated when ETW access
|
||||||
|
requires it; no driver or security setting is changed.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\scripts\windows\capture-presentmon.ps1 -Seconds 30
|
||||||
|
python .\tools\windows\profile_report.py <capture-directory> --output profile.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Keep the game foreground and use the same scene/quality setting for comparison.
|
||||||
|
The report separates swapchains and preserves missing metrics. Present FPS
|
||||||
|
comes from present intervals, rather than NGX recording timestamps. GPU
|
||||||
|
busy/wait are ETW estimates, not a hardware sensor percentage; CPU busy is
|
||||||
|
frame work/wait attribution before Present, not physical CPU utilization.
|
||||||
|
HWS and cross-API context attribution can affect accuracy. Metric definitions:
|
||||||
|
[PresentMon console documentation](https://github.com/GameTechDev/PresentMon/blob/v2.6.0/README-ConsoleApplication.md).
|
||||||
|
|
||||||
|
The initial 60-second trace captures 2172 presents during a changing
|
||||||
|
Independent Flip / Composed Flip session, with mean present interval
|
||||||
|
27.593 ms (36.241 FPS), mean GPU busy 18.759 ms and GPU wait 2.303 ms.
|
||||||
|
This is not a controlled capture of the user's 62 FPS scene and is excluded
|
||||||
|
from speedup conclusions. Its containing game run completes 9627 K/4K frames
|
||||||
|
without backend errors; ordinary recording intervals later return to about
|
||||||
|
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 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):
|
||||||
|
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
|
||||||
|
aged-frame fallback. Their mean interop/NGX CPU stages total about 13.6 and
|
||||||
|
10.1 ms respectively; these are different scenes, not a K/M comparison.
|
||||||
|
|
||||||
|
The no-debugger K/4K run completes 5774 frames, with 5774 finite output scans
|
||||||
|
and 63514 native transformer launches. The user reports 38-42 FPS. Additional
|
||||||
|
CPU measurements exclude replay/publication as the main cause of low FPS:
|
||||||
|
|
||||||
|
| CPU measurement | Mean, ms | Median, ms |
|
||||||
|
| --- | ---: | ---: |
|
||||||
|
| Command split setup | 0.284 | 0.275 |
|
||||||
|
| State replay within split setup | 0.025 | 0.023 |
|
||||||
|
| Submitted texture state publication | 0.029 | 0.029 |
|
||||||
|
| Consumer suffix completion | 0.405 | 0.409 |
|
||||||
|
| NGX recording interval | 27.474 | 28.466 |
|
||||||
|
|
||||||
|
Recording intervals are between NGX recording calls on the same thread, not
|
||||||
|
DXGI Present times. The interval includes rendering and application scheduling.
|
||||||
|
A second K/4K run with the attached debugger completes 3567 frames without
|
||||||
|
failure, with median recording interval 30.091 ms. The runs include different
|
||||||
|
menu/game states; this does not establish a precise debugger FPS speedup.
|
||||||
|
Its sparse debugger events also rule out a per-kernel OutputDebugString storm.
|
||||||
|
An earlier control that stayed on the startup screen completed zero DLSS frames
|
||||||
|
and is excluded. Normal game launches now omit the attached debugger; crash
|
||||||
|
diagnostics remain available with `-CaptureExceptions`.
|
||||||
|
|
||||||
|
The 2961-frame 4K K profile identifies the translated output tail as its largest
|
||||||
|
kernel: 4.401670 ms mean GPU time, versus about 2.875 ms for all eleven native
|
||||||
|
transformer layers combined. Ported upstream's texture-store recipe to native
|
||||||
|
Windows. It retains accuracy mode, wave32, FP16 subnormals and f16 reference
|
||||||
|
accumulation; native FP8 remains disabled.
|
||||||
|
|
||||||
|
Both output variants execute on every checked frame and match the previous
|
||||||
|
RGB output bit-exactly: eight consecutive 512x288 frames per variant, then
|
||||||
|
three consecutive 3840x2160 frames per variant. All RGBA components are finite.
|
||||||
|
Identical synthetic inputs and public NGX flags give these 4K event times
|
||||||
|
(three samples each; host initialization and readback are outside the event):
|
||||||
|
|
||||||
|
| K output variant | Translated mean, ms | Native stores mean, ms |
|
||||||
|
| --- | ---: | ---: |
|
||||||
|
| LDR, regular depth, high-resolution MV | 2.907823 | 1.803233 |
|
||||||
|
| HDR, inverted depth, low-resolution MV | 2.978547 | 2.088347 |
|
||||||
|
|
||||||
|
A subsequent game run executes 6221 frames with the native HDR output tail,
|
||||||
|
zero backend failures and mean tail GPU time 1.801918 ms. The earlier game
|
||||||
|
profile's 4.401670 ms is from a different scene/scheduling state; use the
|
||||||
|
identical-input table for the controlled comparison, not an inferred FPS
|
||||||
|
multiplier. This profiled game run did not enable the NaN scan.
|
||||||
|
|
||||||
|
Reproduce the private texture build and full-frame comparisons:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\scripts\windows\build-native-texture.ps1 -DlssDll "$PWD\nvngx_dlss.dll"
|
||||||
|
.\scripts\windows\build-native-texture.ps1 -DlssDll "$PWD\nvngx_dlss.dll" -Kernel hiluma_engine_output_depthreg_mvhi_ldr_max_v2_rel
|
||||||
|
.\scripts\windows\test-native-texture.ps1 -OutputResolution 3840x2160 -Iterations 3
|
||||||
|
```
|
||||||
|
|
||||||
|
These objects contain code derived from the locally supplied NVIDIA PTX. They
|
||||||
|
stay in ignored local directories and are excluded from the public package.
|
||||||
|
The installer accepts them with `-LocalTextureKernels` only after validation,
|
||||||
|
checking the exact DLL identity, object and manifest hashes. M retains its
|
||||||
|
independently validated FP16-equivalent baseline.
|
||||||
|
|
||||||
|
The following no-debugger K/4K run completes 9982 frames and 9982 finite GPU
|
||||||
|
scans. Kernel event profiling is disabled; the validated private output tail
|
||||||
|
executes natively. The user reports 43-47 FPS. Mean NGX recording interval is
|
||||||
|
21.867 ms. No backend failure, CPU image copy or previous-frame output occurs.
|
||||||
|
|
||||||
|
Random one-in-64 hook samples then identify contention in the shared routing
|
||||||
|
table used by every intercepted D3D12 command. Per-thread weak lookup caches
|
||||||
|
now bypass that table on repeated calls to a list. Table mutations invalidate
|
||||||
|
cache generations; Reset/address reuse cannot route to a previous suffix, and
|
||||||
|
thread caches do not retain game resources. The per-list and submission locks
|
||||||
|
remain. A GPU regression resets and re-splits the exact same list with changed
|
||||||
|
root constants twice per iteration, checking both prefix and suffix results.
|
||||||
|
|
||||||
|
| Sampled command-hook access | Shared table | Per-thread weak cache |
|
||||||
|
| --- | ---: | ---: |
|
||||||
|
| Busiest recording thread, mean us/call | 0.203 | 0.066 |
|
||||||
|
| Estimated access + capture across threads, ms/s | 977.528 | 233.606 |
|
||||||
|
|
||||||
|
The latter estimate includes lock waits summed across concurrently running
|
||||||
|
threads, not CPU execution time or serial frame latency. The two game runs
|
||||||
|
have different thread populations (31 versus 29); there is no identical frame
|
||||||
|
trace. Generated forwarding methods have separate driver/capture markers;
|
||||||
|
special-case hooks contribute access samples but lack those body markers.
|
||||||
|
|
||||||
|
With the cache, 4770 complete K/4K frames and finite GPU scans pass, with zero
|
||||||
|
backend failures, CPU image copies or previous-frame output. Median NGX
|
||||||
|
recording interval is 19.473 ms. The user reports 49-51 FPS and 53% GPU usage
|
||||||
|
in the previously measured scene. These user readings are separate from the
|
||||||
|
sampled hook estimates and do not establish isolated WMMA utilization.
|
||||||
|
All sixteen CTest gates pass, including 64 same-object Reset/split checks in
|
||||||
|
each physical command-backend test and the software indirect-root regression.
|
||||||
|
Use `-ProfileCommandHooks -ProfileStages` to collect samples; the sampler is
|
||||||
|
disabled for ordinary play.
|
||||||
|
|
||||||
|
Native M FP8 was subsequently compiled and tested on this gfx1201. ISA confirms
|
||||||
|
`v_wmma_f32_16x16x16_fp8_fp8`, with packed e4m3 weight images; exact FP16
|
||||||
|
attention/PV remains. All logical outputs and merges in forty recorded launches
|
||||||
|
match the previously NumPy-validated FP16 baseline byte-for-byte. Full DLSS RGB
|
||||||
|
also matches exactly: four 512x288 LDR frames and four 3840x2160 HDR frames,
|
||||||
|
all RGBA finite. This establishes correctness for those fixtures, not a speedup.
|
||||||
|
|
||||||
|
The original software operand packing makes the FP8 network slower in the
|
||||||
|
four-frame 4K test: sum of five main-kernel event means (tube weighted six times)
|
||||||
|
is 27.789640 ms versus FP16's 23.550875 ms. A paired in-process replay confirms
|
||||||
|
the regression without relying solely on separate process timings. Both modules
|
||||||
|
and prepared weights remain resident, 512 launches warm the GPU, then sixty-four
|
||||||
|
AB/BA pairs time the same restored input using HIP events. Diagnostic CPU input
|
||||||
|
restoration is outside the timed event and is unrelated to game VRAM interop.
|
||||||
|
|
||||||
|
Added opt-in `D4R_FP8_HW_PACK`: native packed conversion of already quantised
|
||||||
|
operands replaces software exponent/subnormal reconstruction. It preserves all
|
||||||
|
254 finite OCP e4m3 encodings, including signed zero, in an exhaustive GPU check.
|
||||||
|
All forty recorded launches still match exactly. Four complete 4K HDR frames
|
||||||
|
also match FP16 RGB exactly with finite RGBA. ISA confirms both packed converts
|
||||||
|
and FP8 WMMA. The baseline build's sixteen objects remain byte-identical.
|
||||||
|
|
||||||
|
| Paired replay median, ms | FP16 (software-packing test) | FP8 software packing | FP16 (hardware-packing test) | FP8 hardware packing |
|
||||||
|
| --- | ---: | ---: | ---: | ---: |
|
||||||
|
| enc1 | 0.175450 | 0.217050 | 0.175100 | 0.177700 |
|
||||||
|
| enc2 | 0.141350 | 0.164550 | 0.132550 | 0.131550 |
|
||||||
|
| enc3 tube | 0.108000 | 0.112000 | 0.107600 | 0.099850 |
|
||||||
|
| dec2 | 0.129900 | 0.146000 | 0.143250 | 0.140850 |
|
||||||
|
| dec1 | 0.169650 | 0.194450 | 0.154250 | 0.156550 |
|
||||||
|
|
||||||
|
These are two paired tests on the captured 512x288 workload. Medians avoid large
|
||||||
|
isolated scheduling outliers; raw per-pair ratios are retained. They identify a
|
||||||
|
substantial cost in software packing but do not establish an overall FPS gain.
|
||||||
|
The separately run four-frame 4K hardware-packing network mean is 24.603463 ms;
|
||||||
|
it still does not beat the measured FP16 total. Default packaging retains FP16.
|
||||||
|
Hardware counters for bandwidth or WMMA utilization were not measured.
|
||||||
|
|
||||||
|
Reproduce the isolated build, encoding/replay checks and paired timings using
|
||||||
|
the existing private validated captures; no source editing is required:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\scripts\windows\build-native-m-fp8.ps1 -DlssDll "$PWD\nvngx_dlss.dll" -HardwarePacking
|
||||||
|
.\scripts\windows\test-native-m-fp8.ps1 -ModuleDirectory "$PWD\build\native-m-fp8-hardware-pack" -CaptureDirectory "$PWD\test-results\ngx-m-exact-pv-capture\captures" -BaselineValidation "$PWD\test-results\m-native-exact-pv-temporal-reference\summary.json" -Benchmark
|
||||||
|
```
|
||||||
|
|
||||||
|
Omit `-HardwarePacking` to compile the original FP8 experiment. The replay
|
||||||
|
validator stops at the first mismatch, retains GPU stdout/stderr/exit code and
|
||||||
|
hashes every compared logical output. `profile_report.py` accepts replay logs
|
||||||
|
and reports per-pair GPU time and candidate/control ratios. This experiment is
|
||||||
|
not installed by the game runner or substituted silently for the baseline.
|
||||||
|
The conversion builtin is listed in the official
|
||||||
|
[Clang AMDGPU builtin reference](https://clang.llvm.org/docs/AMDGPUBuiltinReference.html#builtin-amdgcn-cvt-pk-fp8-f32).
|
||||||
|
|
||||||
|
References: [HIP events](https://rocmdocs.amd.com/projects/HIP/en/develop/doxygen/html/group___event.html),
|
||||||
|
[HIP occupancy API](https://rocm.docs.amd.com/projects/HIP/en/latest/doxygen/html/group___occupancy.html).
|
||||||
@@ -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
|
||||||
File diff suppressed because it is too large.
Load diff
+42
-8
@@ -2,7 +2,7 @@
|
|||||||
# Builds the native replacements for DLSS kernels into one directory that ZLUDA serves them from
|
# Builds the native replacements for DLSS kernels into one directory that ZLUDA serves them from
|
||||||
# (D4R_ZLUDA_NATIVE_DIR, or NativeKernelDirFast in d4r.ini). See docs/native-kernels.md.
|
# (D4R_ZLUDA_NATIVE_DIR, or NativeKernelDirFast in d4r.ini). See docs/native-kernels.md.
|
||||||
#
|
#
|
||||||
# usage: kernels/build.sh [all|k|m|tex] [OUT_DIR] (default: all, kernels/out/native)
|
# usage: kernels/build.sh [all|k|l|m|tex] [OUT_DIR] (default: all, kernels/out/native)
|
||||||
#
|
#
|
||||||
# D4R_ROCM_DIR ROCm installation with clang and the HIP device libraries (default /opt/rocm)
|
# D4R_ROCM_DIR ROCm installation with clang and the HIP device libraries (default /opt/rocm)
|
||||||
# D4R_GPU_ARCH target GPU (default gfx1101): an RDNA3 (gfx110x) or RDNA4 (gfx120x) target; the kernels
|
# D4R_GPU_ARCH target GPU (default gfx1101): an RDNA3 (gfx110x) or RDNA4 (gfx120x) target; the kernels
|
||||||
@@ -10,15 +10,22 @@
|
|||||||
# D4R_NATIVE_FP8 1 (RDNA4 only): the variant for ZLUDA's native FP8 WMMA (d4r.ini [Kernels] NativeFp8):
|
# D4R_NATIVE_FP8 1 (RDNA4 only): the variant for ZLUDA's native FP8 WMMA (d4r.ini [Kernels] NativeFp8):
|
||||||
# M's layers on FP8 WMMA with NVIDIA's e4m3 activations, texture tails matching ZLUDA's
|
# M's layers on FP8 WMMA with NVIDIA's e4m3 activations, texture tails matching ZLUDA's
|
||||||
# D4R_ZLUDA_WMMA_FP8_NATIVE lowering. K is the same in both variants.
|
# D4R_ZLUDA_WMMA_FP8_NATIVE lowering. K is the same in both variants.
|
||||||
|
# D4R_PREFER_ACCURACY 1: preserve FP8 quantization, per-MMA f16 rounding and denormal handling;
|
||||||
|
# build a separate set (default output: kernels/out/native/accuracy).
|
||||||
# Texture kernels (tex) are NVIDIA's PTX with parts replaced, so they additionally need:
|
# Texture kernels (tex) are NVIDIA's PTX with parts replaced, so they additionally need:
|
||||||
# D4R_DLSS_DLL nvngx_dlss.dll 310.7 (its PTX is extracted into kernels/extracted/, never committed)
|
# D4R_DLSS_DLL nvngx_dlss.dll 310.7 (its PTX is extracted into kernels/extracted/, never committed)
|
||||||
# D4R_ZLUDA_EMIT ZLUDA's d4r_emit (patches/zluda applied; `cargo build --release -p ptx --example d4r_emit`),
|
# D4R_ZLUDA_EMIT ZLUDA's d4r_emit (patches/zluda applied; `cargo build --release -p ptx --example d4r_emit`),
|
||||||
# which compiles PTX offline for D4R_GPU_ARCH (no GPU of that kind needed)
|
# which compiles PTX offline for D4R_GPU_ARCH (no GPU of that kind needed)
|
||||||
|
# D4R_DLSS_PTX_DIR optional pre-extracted PTX directory for parallel per-target builds
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
WHAT="${1:-all}"
|
WHAT="${1:-all}"
|
||||||
OUT="$(realpath -m "${2:-$HERE/out/native}")"
|
ACCURACY="${D4R_PREFER_ACCURACY:-0}"
|
||||||
|
[[ "$ACCURACY" == 0 || "$ACCURACY" == 1 ]] || { echo "D4R_PREFER_ACCURACY must be 0 or 1" >&2; exit 2; }
|
||||||
|
DEFAULT_OUT="$HERE/out/native"
|
||||||
|
[[ "$ACCURACY" == 1 ]] && DEFAULT_OUT+="/accuracy"
|
||||||
|
OUT="$(realpath -m "${2:-$DEFAULT_OUT}")"
|
||||||
ROCM="${D4R_ROCM_DIR:-/opt/rocm}"
|
ROCM="${D4R_ROCM_DIR:-/opt/rocm}"
|
||||||
ARCH="${D4R_GPU_ARCH:-gfx1101}"
|
ARCH="${D4R_GPU_ARCH:-gfx1101}"
|
||||||
FP8="${D4R_NATIVE_FP8:-0}"
|
FP8="${D4R_NATIVE_FP8:-0}"
|
||||||
@@ -30,6 +37,14 @@ esac
|
|||||||
CLANG="$ROCM/lib/llvm/bin/clang++"
|
CLANG="$ROCM/lib/llvm/bin/clang++"
|
||||||
[[ -x "$CLANG" ]] || { echo "clang++ not found in $ROCM/lib/llvm/bin (set D4R_ROCM_DIR)" >&2; exit 2; }
|
[[ -x "$CLANG" ]] || { echo "clang++ not found in $ROCM/lib/llvm/bin (set D4R_ROCM_DIR)" >&2; exit 2; }
|
||||||
mkdir -p "$OUT"
|
mkdir -p "$OUT"
|
||||||
|
case "$WHAT" in all|k|l|m|tex) ;; *) echo "unknown kernel family: $WHAT" >&2; exit 2 ;; esac
|
||||||
|
# Never certify a directory containing older fast binaries as an accuracy set.
|
||||||
|
if [[ "$ACCURACY" == 1 ]] && compgen -G "$OUT/*.hsaco" >/dev/null &&
|
||||||
|
{ [[ ! -f "$OUT/d4r-accuracy.txt" ]] || [[ "$(cat "$OUT/d4r-accuracy.txt")" != 1 ]]; }; then
|
||||||
|
echo "accuracy kernels need an empty output directory or an existing accuracy set: $OUT" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
rm -f "$OUT/d4r-accuracy.txt"
|
||||||
|
|
||||||
# HIP source -> raw code object (the kernel name inside matches the DLSS kernel it replaces)
|
# HIP source -> raw code object (the kernel name inside matches the DLSS kernel it replaces)
|
||||||
build_hip() {
|
build_hip() {
|
||||||
@@ -39,6 +54,7 @@ build_hip() {
|
|||||||
tmp="$(mktemp -d)"
|
tmp="$(mktemp -d)"
|
||||||
# per-kernel compiler flags from a "// d4r-build-flags: ..." line in the source
|
# per-kernel compiler flags from a "// d4r-build-flags: ..." line in the source
|
||||||
flags="$(sed -n 's|^// d4r-build-flags: *||p' "$src")"
|
flags="$(sed -n 's|^// d4r-build-flags: *||p' "$src")"
|
||||||
|
[[ "$ACCURACY" == 1 ]] && extra+=" -DD4R_ACCURACY"
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
(cd "$tmp" && "$CLANG" -x hip --offload-arch="$ARCH" --offload-device-only -O3 $flags $extra -I"$ROCM/include" \
|
(cd "$tmp" && "$CLANG" -x hip --offload-arch="$ARCH" --offload-device-only -O3 $flags $extra -I"$ROCM/include" \
|
||||||
--rocm-path="$ROCM" --rocm-device-lib-path="$ROCM/amdgcn/bitcode" -I"$(dirname "$src")" \
|
--rocm-path="$ROCM" --rocm-device-lib-path="$ROCM/amdgcn/bitcode" -I"$(dirname "$src")" \
|
||||||
@@ -51,22 +67,28 @@ if [[ "$WHAT" == all || "$WHAT" == k ]]; then
|
|||||||
echo "== DLSS 4 (preset K) transformer layers"
|
echo "== DLSS 4 (preset K) transformer layers"
|
||||||
for src in "$HERE"/k/dltss_pwin_*.hip; do build_hip "$src"; done
|
for src in "$HERE"/k/dltss_pwin_*.hip; do build_hip "$src"; done
|
||||||
fi
|
fi
|
||||||
if [[ "$WHAT" == all || "$WHAT" == m ]]; then
|
if [[ "$WHAT" == all || "$WHAT" == m || "$WHAT" == l ]]; then
|
||||||
echo "== DLSS 4.5 (preset M) Swin layers"
|
echo "== DLSS 4.5 (presets L/M) shared Swin layers"
|
||||||
for src in "$HERE"/m/rrlite_*.hip; do build_hip "$src" "$([[ "$FP8" == 1 ]] && echo -DD4R_FP8_WMMA)"; done
|
for src in "$HERE"/m/rrlite_*.hip; do build_hip "$src" "$([[ "$FP8" == 1 ]] && echo -DD4R_FP8_WMMA)"; done
|
||||||
fi
|
fi
|
||||||
if [[ "$WHAT" == all || "$WHAT" == tex ]]; then
|
if [[ "$WHAT" == all || "$WHAT" == tex || "$WHAT" == l ]]; then
|
||||||
echo "== texture kernels (NVIDIA PTX with native parts)"
|
echo "== texture kernels (NVIDIA PTX with native parts)"
|
||||||
: "${D4R_DLSS_DLL:?set D4R_DLSS_DLL to nvngx_dlss.dll (310.7)}"
|
: "${D4R_DLSS_DLL:?set D4R_DLSS_DLL to nvngx_dlss.dll (310.7)}"
|
||||||
: "${D4R_ZLUDA_EMIT:?set D4R_ZLUDA_EMIT to d4r_emit from a ZLUDA build with patches/zluda}"
|
: "${D4R_ZLUDA_EMIT:?set D4R_ZLUDA_EMIT to d4r_emit from a ZLUDA build with patches/zluda}"
|
||||||
PTX_DIR="$HERE/extracted/ptx"
|
PTX_DIR="${D4R_DLSS_PTX_DIR:-$HERE/extracted/ptx}"
|
||||||
|
if [[ -z "${D4R_DLSS_PTX_DIR:-}" ]]; then
|
||||||
python3 "$HERE/tools/extract_dlss_ptx.py" "$D4R_DLSS_DLL" "$PTX_DIR"
|
python3 "$HERE/tools/extract_dlss_ptx.py" "$D4R_DLSS_DLL" "$PTX_DIR"
|
||||||
|
elif [[ ! -d "$PTX_DIR" ]]; then
|
||||||
|
echo "missing extracted PTX directory: $PTX_DIR" >&2; exit 2
|
||||||
|
fi
|
||||||
# kernel : source of its native part [: w64 = compiled as wave64 (two CUDA warps per wave; RDNA3 issues
|
# kernel : source of its native part [: w64 = compiled as wave64 (two CUDA warps per wave; RDNA3 issues
|
||||||
# FP32 work for all 64 lanes at once). Only for kernels without MMAs; measured faster, bit-identical:
|
# FP32 work for all 64 lanes at once). Only for kernels without MMAs; measured faster, bit-identical:
|
||||||
# post 0.89 -> 0.77 ms, hiluma output 0.905 -> 0.887 ms (downsample was slower as wave64).]
|
# post 0.89 -> 0.77 ms, hiluma output 0.905 -> 0.887 ms (downsample was slower as wave64).]
|
||||||
# Every flag combination DLSS selects (motion vectors hi/lo, HDR/LDR, depth inverted/regular, ...) gets
|
# Every flag combination DLSS selects (motion vectors hi/lo, HDR/LDR, depth inverted/regular, ...) gets
|
||||||
# the same treatment, so games other than the one measured run the native versions too.
|
# the same treatment, so games other than the one measured run the native versions too.
|
||||||
specs=(rrlite_dec0_4x4_folded:dec0_head)
|
specs=()
|
||||||
|
if [[ "$WHAT" != l ]]; then
|
||||||
|
specs+=(rrlite_dec0_4x4_folded:dec0_head)
|
||||||
for mv in mvhi mvlo; do
|
for mv in mvhi mvlo; do
|
||||||
for range in hdr ldr; do
|
for range in hdr ldr; do
|
||||||
specs+=("rrlite_enc0_4x4_${mv}_${range}_folded:enc0_tail")
|
specs+=("rrlite_enc0_4x4_${mv}_${range}_folded:enc0_tail")
|
||||||
@@ -79,13 +101,25 @@ if [[ "$WHAT" == all || "$WHAT" == tex ]]; then
|
|||||||
done
|
done
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
fi
|
||||||
|
# L's unfolded variants keep their complete neural arithmetic. The folded M
|
||||||
|
# GEMM heads/tails are incompatible; substitute only the surface stores.
|
||||||
|
specs+=(rrlite_dec0_4x4:sust_only)
|
||||||
|
for mv in mvhi mvlo; do
|
||||||
|
for range in hdr ldr; do
|
||||||
|
specs+=("rrlite_enc0_4x4_${mv}_${range}:sust_only")
|
||||||
|
for v in 3_1 3_2; do specs+=("rrlite_post_${v}_${mv}_${range}:sust_only:w64"); done
|
||||||
|
done
|
||||||
|
done
|
||||||
for mode in static dynamic; do
|
for mode in static dynamic; do
|
||||||
for range in hdr ldr; do specs+=("rrlite_downsample_kernel_${mode}_${range}:sust_only"); done
|
for range in hdr ldr; do specs+=("rrlite_downsample_kernel_${mode}_${range}:sust_only"); done
|
||||||
done
|
done
|
||||||
for spec in "${specs[@]}"; do
|
for spec in "${specs[@]}"; do
|
||||||
IFS=: read -r kernel src mode <<< "$spec"
|
IFS=: read -r kernel src mode <<< "$spec"
|
||||||
D4R_ZLUDA_WAVE64="$([[ "$mode" == w64 ]] && echo 1 || echo 0)" D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$ARCH" \
|
D4R_PREFER_ACCURACY="$ACCURACY" \
|
||||||
|
D4R_ZLUDA_WAVE64="$([[ "$mode" == w64 && "$ACCURACY" == 0 ]] && echo 1 || echo 0)" D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$ARCH" \
|
||||||
D4R_TEX_FP8="$FP8" D4R_DLSS_PTX_DIR="$PTX_DIR" "$HERE/tex/build_tex.sh" "$kernel" "$src" "$OUT"
|
D4R_TEX_FP8="$FP8" D4R_DLSS_PTX_DIR="$PTX_DIR" "$HERE/tex/build_tex.sh" "$kernel" "$src" "$OUT"
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
[[ "$ACCURACY" == 1 ]] && printf '1\n' > "$OUT/d4r-accuracy.txt"
|
||||||
echo "native kernels in $OUT"
|
echo "native kernels in $OUT"
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Device-only Windows HIP build shim. The diagnostic/kernel code objects are
|
||||||
|
// compiled with -nogpuinc/-nogpulib; no host HIP or C++ standard library is
|
||||||
|
// needed. Definitions below mirror public HIP qualifiers and LLVM AMDGPU
|
||||||
|
// work-item/grid builtins. The normal Linux HIP include path is untouched.
|
||||||
|
#define __device__ __attribute__((device))
|
||||||
|
#define __host__ __attribute__((host))
|
||||||
|
#define __global__ __attribute__((global))
|
||||||
|
#define __shared__ __attribute__((shared))
|
||||||
|
#define __constant__ __attribute__((constant))
|
||||||
|
#define __forceinline__ __attribute__((always_inline)) inline
|
||||||
|
#define __launch_bounds__(n) __attribute__((amdgpu_flat_work_group_size(1, n)))
|
||||||
|
|
||||||
|
struct d4r_device_dim3 { unsigned x, y, z; };
|
||||||
|
// Public HIP vector storage ABI used by M's packed global/LDS accesses.
|
||||||
|
struct alignas(8) uint2 { unsigned x, y; };
|
||||||
|
struct alignas(16) uint4 { unsigned x, y, z, w; };
|
||||||
|
static_assert(sizeof(uint2) == 8 && alignof(uint2) == 8, "HIP uint2 ABI");
|
||||||
|
static_assert(sizeof(uint4) == 16 && alignof(uint4) == 16, "HIP uint4 ABI");
|
||||||
|
__device__ __forceinline__ d4r_device_dim3 d4r_thread_idx() {
|
||||||
|
return {__builtin_amdgcn_workitem_id_x(), __builtin_amdgcn_workitem_id_y(),
|
||||||
|
__builtin_amdgcn_workitem_id_z()};
|
||||||
|
}
|
||||||
|
__device__ __forceinline__ d4r_device_dim3 d4r_block_idx() {
|
||||||
|
return {__builtin_amdgcn_workgroup_id_x(), __builtin_amdgcn_workgroup_id_y(),
|
||||||
|
__builtin_amdgcn_workgroup_id_z()};
|
||||||
|
}
|
||||||
|
__device__ __forceinline__ d4r_device_dim3 d4r_grid_dim() {
|
||||||
|
return {__builtin_amdgcn_grid_size_x() / __builtin_amdgcn_workgroup_size_x(),
|
||||||
|
__builtin_amdgcn_grid_size_y() / __builtin_amdgcn_workgroup_size_y(),
|
||||||
|
__builtin_amdgcn_grid_size_z() / __builtin_amdgcn_workgroup_size_z()};
|
||||||
|
}
|
||||||
|
#define threadIdx d4r_thread_idx()
|
||||||
|
#define blockIdx d4r_block_idx()
|
||||||
|
#define gridDim d4r_grid_dim()
|
||||||
|
__device__ __attribute__((convergent, always_inline)) inline void __syncthreads() {
|
||||||
|
// Match HIP __work_group_barrier: execution rendezvous alone does not
|
||||||
|
// publish LDS stores or prevent loads moving across the barrier.
|
||||||
|
__builtin_amdgcn_fence(__ATOMIC_RELEASE, "workgroup");
|
||||||
|
__builtin_amdgcn_s_barrier();
|
||||||
|
__builtin_amdgcn_fence(__ATOMIC_ACQUIRE, "workgroup");
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "wave32_exchange.h"
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// gfx12 consumes only 8 FP16 operands per lane and emits 8 consecutive rows
|
||||||
|
// per lane group. Keep the old interface for K/M/texture call sites until their
|
||||||
|
// surrounding fragments are ported and validated individually.
|
||||||
|
using d4r_wmma_h16 = _Float16 __attribute__((ext_vector_type(16)));
|
||||||
|
using d4r_wmma_h8 = _Float16 __attribute__((ext_vector_type(8)));
|
||||||
|
using d4r_wmma_f8 = float __attribute__((ext_vector_type(8)));
|
||||||
|
|
||||||
|
__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 swapped = d4r_wave32_other_half(bits);
|
||||||
|
return __builtin_bit_cast(float, swapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((device, always_inline)) static inline d4r_wmma_f8
|
||||||
|
d4r_wmma_legacy_layout(d4r_wmma_h16 a, d4r_wmma_h16 b, d4r_wmma_f8 c)
|
||||||
|
{
|
||||||
|
#if defined(__gfx1200__) || defined(__gfx1201__)
|
||||||
|
const uint32_t group = __builtin_amdgcn_mbcnt_lo(~0u, 0u) >> 4;
|
||||||
|
d4r_wmma_h8 a12, b12;
|
||||||
|
d4r_wmma_f8 c12;
|
||||||
|
d4r_wmma_f8 remote_c;
|
||||||
|
#pragma unroll
|
||||||
|
for (int i = 0; i < 8; ++i)
|
||||||
|
remote_c[i] = d4r_wmma_other_half(c[i]);
|
||||||
|
#pragma unroll
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
a12[i] = a[8u * group + i];
|
||||||
|
b12[i] = b[8u * group + i];
|
||||||
|
const uint32_t row = 8u * group + i;
|
||||||
|
const uint32_t old_index = row >> 1;
|
||||||
|
c12[i] = (row & 1u) == group ? c[old_index] : remote_c[old_index];
|
||||||
|
}
|
||||||
|
const d4r_wmma_f8 d12 = __builtin_amdgcn_wmma_f32_16x16x16_f16_w32_gfx12(a12, b12, c12);
|
||||||
|
d4r_wmma_f8 result, remote_d;
|
||||||
|
#pragma unroll
|
||||||
|
for (int i = 0; i < 8; ++i)
|
||||||
|
remote_d[i] = d4r_wmma_other_half(d12[i]);
|
||||||
|
#pragma unroll
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
const uint32_t row = 2u * i + group;
|
||||||
|
const uint32_t new_index = row & 7u;
|
||||||
|
result[i] = (row >> 3) == group ? d12[new_index] : remote_d[new_index];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
#elif defined(__GFX11__)
|
||||||
|
return __builtin_amdgcn_wmma_f32_16x16x16_f16_w32(a, b, c);
|
||||||
|
#else
|
||||||
|
#error "d4r WMMA backend requires a validated gfx11 or gfx12 target"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
+87
-6
@@ -7,15 +7,32 @@
|
|||||||
// output channel wm_acc_row(i) of the 16-channel tile ("D^T"); ../common/wmma_layout.h has the per-target
|
// output channel wm_acc_row(i) of the 16-channel tile ("D^T"); ../common/wmma_layout.h has the per-target
|
||||||
// register layouts (gfx11: channel 2i + (l >> 4); gfx12: channel i + 8 (l >> 4)).
|
// register layouts (gfx11: channel 2i + (l >> 4); gfx12: channel i + 8 (l >> 4)).
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#ifdef D4R_DEVICE_ONLY_MINIMAL
|
||||||
|
#include "../common/hip_device_minimal.h"
|
||||||
|
#else
|
||||||
#include <hip/hip_runtime.h>
|
#include <hip/hip_runtime.h>
|
||||||
|
#endif
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "../common/wmma_layout.h"
|
#include "../common/wmma_layout.h"
|
||||||
|
#if defined(D4R_ACCURACY) && defined(PWIN_F32ACC)
|
||||||
|
#undef PWIN_F32ACC
|
||||||
|
#endif
|
||||||
#pragma clang fp contract(off)
|
#pragma clang fp contract(off)
|
||||||
|
|
||||||
typedef _Float16 half_t;
|
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)));
|
||||||
|
|
||||||
@@ -26,27 +43,86 @@ __device__ __forceinline__ uint32_t lane_id()
|
|||||||
|
|
||||||
typedef wm_op op_t; // WMMA operand: u8v (gfx11) or u4v (gfx12)
|
typedef wm_op op_t; // WMMA operand: u8v (gfx11) or u4v (gfx12)
|
||||||
|
|
||||||
|
// The CUDA fragment reduction rounds products and every tree node to f16.
|
||||||
|
// A float sum of the squared halves changes rsqrt and can amplify differences
|
||||||
|
// through attention. The strict baseline preserves its channel grouping.
|
||||||
|
template <int C>
|
||||||
|
__device__ __forceinline__ half_t l2_sum(const half_t* row)
|
||||||
|
{
|
||||||
|
#ifdef D4R_K_FP16_BASELINE
|
||||||
|
static_assert(C % 32 == 0 && C <= 160, "audited K channel counts");
|
||||||
|
hv2 partial[4];
|
||||||
|
#pragma unroll
|
||||||
|
for (int t = 0; t < 4; ++t)
|
||||||
|
{
|
||||||
|
hv2 halves[2];
|
||||||
|
#pragma unroll
|
||||||
|
for (int parity = 0; parity < 2; ++parity)
|
||||||
|
{
|
||||||
|
hv2 pairs[C / 32];
|
||||||
|
#pragma unroll
|
||||||
|
for (int group = 0; group < C / 32; ++group)
|
||||||
|
{
|
||||||
|
const hv2 a = *(const hv2*)(row + 2 * t + 8 * parity + 32 * group);
|
||||||
|
const hv2 b = *(const hv2*)(row + 2 * t + 8 * parity + 32 * group + 16);
|
||||||
|
pairs[group] = (a * a) + (b * b);
|
||||||
|
}
|
||||||
|
hv2 value = pairs[0];
|
||||||
|
if constexpr (C >= 64) value = value + pairs[1];
|
||||||
|
if constexpr (C == 96) value = value + pairs[2];
|
||||||
|
if constexpr (C >= 128) value = value + (pairs[2] + pairs[3]);
|
||||||
|
if constexpr (C == 160) value = value + pairs[4];
|
||||||
|
halves[parity] = value;
|
||||||
|
}
|
||||||
|
partial[t] = halves[0] + halves[1];
|
||||||
|
}
|
||||||
|
const hv2 sum = (partial[0] + partial[1]) + (partial[2] + partial[3]);
|
||||||
|
return sum[0] + sum[1];
|
||||||
|
#else
|
||||||
|
float sum = 0;
|
||||||
|
#pragma unroll
|
||||||
|
for (int c = 0; c < C; ++c) sum += (float)(half_t)(row[c] * row[c]);
|
||||||
|
return (half_t)sum;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// 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);
|
||||||
#ifndef PWIN_F32ACC
|
#if !defined(PWIN_F32ACC) || defined(D4R_K_FP16_BASELINE)
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
for (int i = 0; i < 8; ++i)
|
for (int i = 0; i < 8; ++i)
|
||||||
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)
|
||||||
@@ -70,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)
|
||||||
|
|||||||
+13
-23
@@ -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
|
||||||
@@ -259,17 +259,7 @@ __device__ void pwin_core(const PwinParams& p, const u8v* __restrict__ img, cons
|
|||||||
|
|
||||||
STAMP(1);
|
STAMP(1);
|
||||||
// ---- L2 norm: h1 = x0 * (rsqrt(sum x0^2) * gamma1) -> hb
|
// ---- L2 norm: h1 = x0 * (rsqrt(sum x0^2) * gamma1) -> hb
|
||||||
float ss = 0.0f;
|
const half_t ss = l2_sum<C>(arow);
|
||||||
for (int kt = 0; kt < KT; ++kt)
|
|
||||||
{
|
|
||||||
const u8v xv = lds_row16(arow + 16 * kt);
|
|
||||||
#pragma unroll
|
|
||||||
for (int c = 0; c < 16; ++c)
|
|
||||||
{
|
|
||||||
const half_t v = op_get(xv, c);
|
|
||||||
ss += (float)(half_t)(v * v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const half_t r16 = (half_t)__builtin_amdgcn_rsqf((float)(half_t)ss);
|
const half_t r16 = (half_t)__builtin_amdgcn_rsqf((float)(half_t)ss);
|
||||||
for (int kt = (int)hf; kt < KT; kt += 2)
|
for (int kt = (int)hf; kt < KT; kt += 2)
|
||||||
{
|
{
|
||||||
@@ -293,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)
|
||||||
{
|
{
|
||||||
@@ -320,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));
|
||||||
@@ -343,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);
|
||||||
@@ -359,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)
|
||||||
@@ -380,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)
|
||||||
{
|
{
|
||||||
@@ -414,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)
|
||||||
{
|
{
|
||||||
@@ -472,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];
|
||||||
@@ -565,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_);
|
||||||
@@ -613,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];
|
||||||
@@ -763,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];
|
||||||
@@ -800,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];
|
||||||
|
|||||||
+11
-21
@@ -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;
|
||||||
@@ -19,18 +20,7 @@ __device__ void pos_core(const PwinParams& p, const u8v* __restrict__ img, const
|
|||||||
{
|
{
|
||||||
const half_t* arow = act[16 * (wv * MT + mi) + m];
|
const half_t* arow = act[16 * (wv * MT + mi) + m];
|
||||||
half_t* hrow = hb[16 * (wv * MT + mi) + m];
|
half_t* hrow = hb[16 * (wv * MT + mi) + m];
|
||||||
float ss = 0.0f;
|
const half_t ss = l2_sum<C>(arow);
|
||||||
#pragma unroll
|
|
||||||
for (int kt = 0; kt < KT; ++kt)
|
|
||||||
{
|
|
||||||
const u8v xv = lds_row16(arow + 16 * kt);
|
|
||||||
#pragma unroll
|
|
||||||
for (int c = 0; c < 16; ++c)
|
|
||||||
{
|
|
||||||
const half_t v = op_get(xv, c);
|
|
||||||
ss += (float)(half_t)(v * v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const half_t r16 = (half_t)__builtin_amdgcn_rsqf((float)(half_t)ss);
|
const half_t r16 = (half_t)__builtin_amdgcn_rsqf((float)(half_t)ss);
|
||||||
for (int kt = (int)hf; kt < KT; kt += 2)
|
for (int kt = (int)hf; kt < KT; kt += 2)
|
||||||
{
|
{
|
||||||
@@ -48,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)
|
||||||
{
|
{
|
||||||
@@ -75,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);
|
||||||
@@ -93,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);
|
||||||
@@ -112,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);
|
||||||
@@ -167,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
|
||||||
@@ -242,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];
|
||||||
@@ -289,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];
|
||||||
@@ -333,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];
|
||||||
@@ -364,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];
|
||||||
|
|||||||
+9
-19
@@ -38,17 +38,7 @@ __device__ void wide_core(const PwinParams& p, const u8v* __restrict__ img, cons
|
|||||||
half_t* hrow = hb[tok];
|
half_t* hrow = hb[tok];
|
||||||
|
|
||||||
// ---- L2 norm: h1 = x0 * (rsqrt(sum x0^2) * gamma1) -> hb (all groups of a token tile compute the sum)
|
// ---- L2 norm: h1 = x0 * (rsqrt(sum x0^2) * gamma1) -> hb (all groups of a token tile compute the sum)
|
||||||
float ss = 0.0f;
|
const half_t ss = l2_sum<C>(arow);
|
||||||
for (int kt = 0; kt < KT; ++kt)
|
|
||||||
{
|
|
||||||
const u8v xv = lds_row16(arow + 16 * kt);
|
|
||||||
#pragma unroll
|
|
||||||
for (int c = 0; c < 16; ++c)
|
|
||||||
{
|
|
||||||
const half_t v = op_get(xv, c);
|
|
||||||
ss += (float)(half_t)(v * v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const half_t r16 = (half_t)__builtin_amdgcn_rsqf((float)(half_t)ss);
|
const half_t r16 = (half_t)__builtin_amdgcn_rsqf((float)(half_t)ss);
|
||||||
for (int kt = 2 * g + (int)hf; kt < KT; kt += 2 * NG)
|
for (int kt = 2 * g + (int)hf; kt < KT; kt += 2 * NG)
|
||||||
{
|
{
|
||||||
@@ -78,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)
|
||||||
{
|
{
|
||||||
@@ -103,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);
|
||||||
@@ -119,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)
|
||||||
@@ -143,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)
|
||||||
{
|
{
|
||||||
@@ -176,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);
|
||||||
@@ -243,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);
|
||||||
@@ -339,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);
|
||||||
@@ -392,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);
|
||||||
|
|||||||
+72
-8
@@ -5,7 +5,24 @@
|
|||||||
// CIN patch-expand input channels (0 = none): decoder input = f16(q8(expand(low-res CIN)) + skip)
|
// CIN patch-expand input channels (0 = none): decoder input = f16(q8(expand(low-res CIN)) + skip)
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "swin_common.h"
|
#include "swin_common.h"
|
||||||
#include <type_traits>
|
#ifdef D4R_SWIN_DIAGNOSTICS
|
||||||
|
extern "C" {
|
||||||
|
__device__ int d4r_swin_debug_block[2] = {0, 4};
|
||||||
|
__device__ half_t d4r_swin_debug_values[4 * 64 * 128];
|
||||||
|
}
|
||||||
|
template <int C, int AST> __device__ __forceinline__ void swin_snapshot(
|
||||||
|
const half_t* activation, int stage, int bx, int by, int tid, bool pair_order) {
|
||||||
|
if (bx == d4r_swin_debug_block[0] && by == d4r_swin_debug_block[1] && tid < 64)
|
||||||
|
for (int c = 0; c < C; ++c)
|
||||||
|
d4r_swin_debug_values[(stage * 64 + tid) * 128 + c] =
|
||||||
|
activation[tid * AST + (pair_order ? gperm(c) : apos(c))];
|
||||||
|
__syncthreads();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Immediate lane masks need only a compile-time integer, not the host C++
|
||||||
|
// standard library (which is absent from device-only Windows HIP builds).
|
||||||
|
template <int Mask> struct SwinLaneMask { static constexpr int value = Mask; };
|
||||||
|
|
||||||
template <int C, int NW, int NPM, int CIN = 0> struct SwinLayout
|
template <int C, int NW, int NPM, int CIN = 0> struct SwinLayout
|
||||||
{
|
{
|
||||||
@@ -76,7 +93,12 @@ __device__ __forceinline__ void swin_block(const CommonParams& p, const TubePara
|
|||||||
static_assert(NW % NH == 0 && NW / NH <= 4, "waves per head");
|
static_assert(NW % NH == 0 && NW / NH <= 4, "waves per head");
|
||||||
constexpr int NPL = L::NPL, NT = L::NT, INNER = L::INNER;
|
constexpr int NPL = L::NPL, NT = L::NT, INNER = L::INNER;
|
||||||
constexpr int AST = (C > INNER ? C : INNER) + 8;
|
constexpr int AST = (C > INNER ? C : INNER) + 8;
|
||||||
constexpr int GST = 40, QST = 40, PST = 24;
|
constexpr int GST = 40, PST = 24;
|
||||||
|
#ifdef SWIN_EXACT_PV
|
||||||
|
constexpr int QST = 56;
|
||||||
|
#else
|
||||||
|
constexpr int QST = 40;
|
||||||
|
#endif
|
||||||
#ifndef SWIN_MLP_R
|
#ifndef SWIN_MLP_R
|
||||||
#define SWIN_MLP_R(NW) ((NW) / 2)
|
#define SWIN_MLP_R(NW) ((NW) / 2)
|
||||||
#endif
|
#endif
|
||||||
@@ -291,6 +313,9 @@ __device__ __forceinline__ void swin_block(const CommonParams& p, const TubePara
|
|||||||
__syncthreads();
|
__syncthreads();
|
||||||
|
|
||||||
// ------------------------------------------------ stage 2: Q/V projection + attention (wave = head)
|
// ------------------------------------------------ stage 2: Q/V projection + attention (wave = head)
|
||||||
|
#ifdef D4R_SWIN_DIAGNOSTICS
|
||||||
|
swin_snapshot<C, AST>(A, 0, bx, by, tid, false);
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
constexpr int MW = 4 / (NW / NH); // windows per wave
|
constexpr int MW = 4 / (NW / NH); // windows per wave
|
||||||
const int h = NW == NH ? wv : wv % NH, m0 = NW == NH ? 0 : MW * (wv / NH);
|
const int h = NW == NH ? wv : wv % NH, m0 = NW == NH ? 0 : MW * (wv / NH);
|
||||||
@@ -387,16 +412,23 @@ __device__ __forceinline__ void swin_block(const CommonParams& p, const TubePara
|
|||||||
auto xr = [](hv2 v, auto mask) {
|
auto xr = [](hv2 v, auto mask) {
|
||||||
return __builtin_bit_cast(hv2, xor_lane<decltype(mask)::value>(__builtin_bit_cast(uint32_t, v)));
|
return __builtin_bit_cast(hv2, xor_lane<decltype(mask)::value>(__builtin_bit_cast(uint32_t, v)));
|
||||||
};
|
};
|
||||||
const hv2 v1 = wg[k] + xr(wg[k], std::integral_constant<int, 8>{});
|
const hv2 v1 = wg[k] + xr(wg[k], SwinLaneMask<8>{});
|
||||||
const hv2 v2 = v1 + xr(v1, std::integral_constant<int, 2>{});
|
const hv2 v2 = v1 + xr(v1, SwinLaneMask<2>{});
|
||||||
const hv2 v3 = v2 + xr(v2, std::integral_constant<int, 4>{});
|
const hv2 v3 = v2 + xr(v2, SwinLaneMask<4>{});
|
||||||
const hv2 sum = v3 + xr(v3, std::integral_constant<int, 1>{});
|
const hv2 sum = v3 + xr(v3, SwinLaneMask<1>{});
|
||||||
const hv2 rinv = {f16(1.0f / (float)sum[0]), f16(1.0f / (float)sum[1])};
|
const hv2 rinv = {f16(1.0f / (float)sum[0]), f16(1.0f / (float)sum[1])};
|
||||||
const hv2 pw = wg[k] * rinv;
|
const hv2 pw = wg[k] * rinv;
|
||||||
Qs[mrow(2 * k) * PST + l16] = pw[0];
|
Qs[mrow(2 * k) * PST + l16] = pw[0];
|
||||||
Qs[mrow(2 * k + 1) * PST + l16] = pw[1];
|
Qs[mrow(2 * k + 1) * PST + l16] = pw[1];
|
||||||
}
|
}
|
||||||
wave_sync();
|
wave_sync();
|
||||||
|
#ifdef SWIN_EXACT_PV
|
||||||
|
half_t* Vs = Qs + 16 * PST;
|
||||||
|
for (int nt = 0; nt < 2; ++nt)
|
||||||
|
for (int i = 0; i < 8; ++i)
|
||||||
|
Vs[mrow(i) * 32 + 16 * nt + l16] = vp[m][nt][i / 2][i & 1];
|
||||||
|
wave_sync();
|
||||||
|
#endif
|
||||||
const sop pa = lds16(Qs + l16 * PST);
|
const sop pa = lds16(Qs + l16 * PST);
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
for (int nt = 0; nt < 2; ++nt)
|
for (int nt = 0; nt < 2; ++nt)
|
||||||
@@ -419,7 +451,19 @@ __device__ __forceinline__ void swin_block(const CommonParams& p, const TubePara
|
|||||||
}
|
}
|
||||||
const h16 vb = __builtin_bit_cast(h16, vw);
|
const h16 vb = __builtin_bit_cast(h16, vw);
|
||||||
#endif
|
#endif
|
||||||
const f8v o = wmma(pa, vb, splat(0.0f));
|
f8v o = wmma(pa, vb, splat(0.0f));
|
||||||
|
#ifdef SWIN_EXACT_PV
|
||||||
|
// FP16 P/V products need more than 24 significant bits in a
|
||||||
|
// cancellation-heavy dot. An f32 WMMA can round to the wrong
|
||||||
|
// FP16 neighbour at a midpoint, then flip the FP8 output code.
|
||||||
|
// This baseline sums the 16 FP16 products before one FP16 round.
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
double dot = 0.0;
|
||||||
|
for (int k = 0; k < 16; ++k)
|
||||||
|
dot += (double)Qs[mrow(i) * PST + k] * (double)Vs[k * 32 + 16 * nt + l16];
|
||||||
|
o[i] = (float)(half_t)dot;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
const int ocol = apos(32 * h + ginv(16 * nt + l16));
|
const int ocol = apos(32 * h + ginv(16 * nt + l16));
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
for (int i = 0; i < 8; i += 2)
|
for (int i = 0; i < 8; i += 2)
|
||||||
@@ -435,6 +479,9 @@ __device__ __forceinline__ void swin_block(const CommonParams& p, const TubePara
|
|||||||
__syncthreads();
|
__syncthreads();
|
||||||
|
|
||||||
// ------------------------------------------------ stage 3: output projection + residual
|
// ------------------------------------------------ stage 3: output projection + residual
|
||||||
|
#ifdef D4R_SWIN_DIAGNOSTICS
|
||||||
|
swin_snapshot<INNER, AST>(A, 3, bx, by, tid, false);
|
||||||
|
#endif
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
for (int kc = 0; kc < NH; ++kc)
|
for (int kc = 0; kc < NH; ++kc)
|
||||||
{
|
{
|
||||||
@@ -468,6 +515,11 @@ __device__ __forceinline__ void swin_block(const CommonParams& p, const TubePara
|
|||||||
if (tid < 64)
|
if (tid < 64)
|
||||||
{
|
{
|
||||||
// the row as 128-bit LDS loads: element 8 j + c of n8 tile j
|
// the row as 128-bit LDS loads: element 8 j + c of n8 tile j
|
||||||
|
#ifdef D4R_SWIN_DIAGNOSTICS
|
||||||
|
if (bx == d4r_swin_debug_block[0] && by == d4r_swin_debug_block[1])
|
||||||
|
for (int c = 0; c < C; ++c)
|
||||||
|
d4r_swin_debug_values[(64 + tid) * 128 + c] = A[tid * AST + gperm(c)];
|
||||||
|
#endif
|
||||||
h8 row[L::NT8];
|
h8 row[L::NT8];
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
for (int j = 0; j < L::NT8; ++j)
|
for (int j = 0; j < L::NT8; ++j)
|
||||||
@@ -526,6 +578,9 @@ __device__ __forceinline__ void swin_block(const CommonParams& p, const TubePara
|
|||||||
__syncthreads();
|
__syncthreads();
|
||||||
|
|
||||||
// ------------------------------------------------ stage 4: MLP, R chunks per round
|
// ------------------------------------------------ stage 4: MLP, R chunks per round
|
||||||
|
#ifdef D4R_SWIN_DIAGNOSTICS
|
||||||
|
swin_snapshot<C, AST>(A, 2, bx, by, tid, false);
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
constexpr int WPC = NW / R, MPW = 4 / WPC; // waves per chunk, fc1 m tiles per wave
|
constexpr int WPC = NW / R, MPW = 4 / WPC; // waves per chunk, fc1 m tiles per wave
|
||||||
const int cw = wv / WPC, mb = MPW * (wv % WPC);
|
const int cw = wv / WPC, mb = MPW * (wv % WPC);
|
||||||
@@ -715,7 +770,16 @@ __device__ __forceinline__ void swin_block(const CommonParams& p, const TubePara
|
|||||||
if (MX < 0 || MX >= W2 || MY < 0 || MY >= H2)
|
if (MX < 0 || MX >= W2 || MY < 0 || MY >= H2)
|
||||||
continue;
|
continue;
|
||||||
p.p48[((size_t)((ch >> 5) * H2 + MY) * W2 + MX) * 32 + (ch & 31)] =
|
p.p48[((size_t)((ch >> 5) * H2 + MY) * W2 + MX) * 32 + (ch & 31)] =
|
||||||
(uint8_t)enc8(y[j].get(i));
|
(uint8_t)enc8(
|
||||||
|
#ifdef SWIN_EXACT
|
||||||
|
// The audited 310.9 patch-merge path bounds the f16
|
||||||
|
// result to +/- 2*pi before e4m3 quantisation.
|
||||||
|
__builtin_elementwise_max(__builtin_elementwise_min(y[j].get(i), (half_t)6.283185307179586f),
|
||||||
|
(half_t)-6.283185307179586f)
|
||||||
|
#else
|
||||||
|
y[j].get(i)
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,16 @@
|
|||||||
// D4R_FP8_WMMA (gfx12 layout only): the GEMMs whose operands are both e4m3 values run as native FP8 WMMAs on
|
// D4R_FP8_WMMA (gfx12 layout only): the GEMMs whose operands are both e4m3 values run as native FP8 WMMAs on
|
||||||
// e4m3 bytes (weights prepared as bytes, activations encoded when loaded); P V stays f16.
|
// e4m3 bytes (weights prepared as bytes, activations encoded when loaded); P V stays f16.
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#ifdef D4R_DEVICE_ONLY_MINIMAL
|
||||||
|
#include "../common/hip_device_minimal.h"
|
||||||
|
#else
|
||||||
#include <hip/hip_runtime.h>
|
#include <hip/hip_runtime.h>
|
||||||
|
#endif
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "../common/wmma_layout.h"
|
#include "../common/wmma_layout.h"
|
||||||
|
#ifdef D4R_ACCURACY
|
||||||
|
#define SWIN_EXACT
|
||||||
|
#endif
|
||||||
#if defined(D4R_FP8_WMMA) && D4R_WMMA_LAYOUT != 12 && defined(__AMDGCN__)
|
#if defined(D4R_FP8_WMMA) && D4R_WMMA_LAYOUT != 12 && defined(__AMDGCN__)
|
||||||
#error "D4R_FP8_WMMA needs the gfx12 WMMA layout"
|
#error "D4R_FP8_WMMA needs the gfx12 WMMA layout"
|
||||||
#endif
|
#endif
|
||||||
@@ -266,11 +273,25 @@ __device__ __forceinline__ sop lds16(const half_t* p)
|
|||||||
// e4m3 bytes of an f16 operand whose values are e4m3 values (K order kept: byte j = element j)
|
// e4m3 bytes of an f16 operand whose values are e4m3 values (K order kept: byte j = element j)
|
||||||
__device__ __forceinline__ gop to_fp8(sop v)
|
__device__ __forceinline__ gop to_fp8(sop v)
|
||||||
{
|
{
|
||||||
|
#if defined(D4R_FP8_HW_PACK) && defined(__GFX12__)
|
||||||
|
// Inputs already contain exact e4m3 values. gfx12's native packed convert
|
||||||
|
// avoids the software subnormal/sign/exponent reconstruction below. Keep
|
||||||
|
// opt-in until all finite encodings and layer/network outputs pass replay.
|
||||||
|
gop result;
|
||||||
|
#pragma unroll
|
||||||
|
for (int word = 0; word < 2; ++word) {
|
||||||
|
int packed = __builtin_amdgcn_cvt_pk_fp8_f32((float)v[4 * word], (float)v[4 * word + 1], 0, false);
|
||||||
|
packed = __builtin_amdgcn_cvt_pk_fp8_f32((float)v[4 * word + 2], (float)v[4 * word + 3], packed, true);
|
||||||
|
result[word] = (uint32_t)packed;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
uint32_t c[4];
|
uint32_t c[4];
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
for (int j = 0; j < 4; ++j)
|
for (int j = 0; j < 4; ++j)
|
||||||
c[j] = codes8x2((hv2){v[2 * j], v[2 * j + 1]});
|
c[j] = codes8x2((hv2){v[2 * j], v[2 * j + 1]});
|
||||||
return (u2v){__builtin_amdgcn_perm(c[1], c[0], 0x06040200u), __builtin_amdgcn_perm(c[3], c[2], 0x06040200u)};
|
return (u2v){__builtin_amdgcn_perm(c[1], c[0], 0x06040200u), __builtin_amdgcn_perm(c[3], c[2], 0x06040200u)};
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
// f32 += A B over 16 K for e4m3 operands
|
// f32 += A B over 16 K for e4m3 operands
|
||||||
__device__ __forceinline__ f8v wmma8(gop a, gop b, f8v c)
|
__device__ __forceinline__ f8v wmma8(gop a, gop b, f8v c)
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
#include "../../tools/d4r_motion_dilation.h"
|
||||||
|
#pragma clang fp contract(off)
|
||||||
|
|
||||||
|
__attribute__((device)) static int clamp_index(int x, uint32_t limit)
|
||||||
|
{
|
||||||
|
return x < 0 ? 0 : x >= (int)limit ? (int)limit - 1 : x;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" __attribute__((global, amdgpu_flat_work_group_size(128, 128)))
|
||||||
|
void d4r_motion_dilate(D4rMotionDilationParams p)
|
||||||
|
{
|
||||||
|
const uint32_t id = __builtin_amdgcn_workgroup_id_x() * 128 + __builtin_amdgcn_workitem_id_x();
|
||||||
|
if ((uint64_t)id >= (uint64_t)p.motion_width * p.motion_height)
|
||||||
|
return;
|
||||||
|
const uint32_t x = id % p.motion_width, y = id / p.motion_width;
|
||||||
|
const int cx = clamp_index((int)__builtin_floorf((x + 0.5f) * p.render_width / p.motion_width + p.jitter_x), p.render_width);
|
||||||
|
const int cy = clamp_index((int)__builtin_floorf((y + 0.5f) * p.render_height / p.motion_height + p.jitter_y), p.render_height);
|
||||||
|
const uint8_t* depth = (const uint8_t*)p.depth;
|
||||||
|
const uint8_t* motion = (const uint8_t*)p.motion;
|
||||||
|
uint8_t* output = (uint8_t*)p.output;
|
||||||
|
const float current = *(const float*)(depth + (uint64_t)cy * p.depth_pitch + 4 * cx);
|
||||||
|
float best = current;
|
||||||
|
int bx = cx, by = cy;
|
||||||
|
for (int dy = -(int)p.radius; dy <= (int)p.radius; ++dy)
|
||||||
|
for (int dx = -(int)p.radius; dx <= (int)p.radius; ++dx)
|
||||||
|
{
|
||||||
|
const int nx = clamp_index(cx + dx, p.render_width), ny = clamp_index(cy + dy, p.render_height);
|
||||||
|
const float candidate = *(const float*)(depth + (uint64_t)ny * p.depth_pitch + 4 * nx);
|
||||||
|
if (p.reversed_depth ? candidate > best : candidate < best)
|
||||||
|
best = candidate, bx = nx, by = ny;
|
||||||
|
}
|
||||||
|
// Sky gaps need wider coverage than opaque edges. Select the closest
|
||||||
|
// foreground pixel within eight render pixels, rather than the closest
|
||||||
|
// depth (which can borrow a distant roof/chimney's velocity).
|
||||||
|
const bool sky = p.reversed_depth ? current <= 1.e-7f : current >= 1.0f - 1.e-7f;
|
||||||
|
bool sky_filled = false;
|
||||||
|
if (sky)
|
||||||
|
{
|
||||||
|
int best_distance = 65;
|
||||||
|
for (int dy = -8; dy <= 8; ++dy)
|
||||||
|
for (int dx = -8; dx <= 8; ++dx)
|
||||||
|
{
|
||||||
|
const int nx = cx + dx, ny = cy + dy;
|
||||||
|
const int distance = dx * dx + dy * dy;
|
||||||
|
if (nx < 0 || ny < 0 || nx >= (int)p.render_width || ny >= (int)p.render_height ||
|
||||||
|
distance > 64 || distance > best_distance)
|
||||||
|
continue;
|
||||||
|
const float candidate = *(const float*)(depth + (uint64_t)ny * p.depth_pitch + 4 * nx);
|
||||||
|
const bool foreground = p.reversed_depth ? candidate > 1.e-7f : candidate < 1.0f - 1.e-7f;
|
||||||
|
// Stable coordinate tie-break matches nearest-foreground fill.
|
||||||
|
if (foreground && (distance < best_distance || nx < bx || (nx == bx && ny < by)))
|
||||||
|
best_distance = distance, best = candidate, bx = nx, by = ny, sky_filled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint32_t value = *(const uint32_t*)(motion + (uint64_t)y * p.motion_pitch + 4 * x);
|
||||||
|
const bool nearer = p.reversed_depth ? best > current * 1.02f + 1.e-7f : best < current * 0.98f - 1.e-7f;
|
||||||
|
if (sky_filled || nearer)
|
||||||
|
{
|
||||||
|
const int mx = clamp_index((int)__builtin_floorf((bx + 0.5f - p.jitter_x) * p.motion_width / p.render_width), p.motion_width);
|
||||||
|
const int my = clamp_index((int)__builtin_floorf((by + 0.5f - p.jitter_y) * p.motion_height / p.render_height), p.motion_height);
|
||||||
|
value = *(const uint32_t*)(motion + (uint64_t)my * p.motion_pitch + 4 * mx);
|
||||||
|
}
|
||||||
|
*(uint32_t*)(output + (uint64_t)y * p.motion_pitch + 4 * x) = value;
|
||||||
|
}
|
||||||
@@ -11,12 +11,14 @@ D="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|||||||
R="${D4R_ROCM_DIR:-/opt/rocm}"
|
R="${D4R_ROCM_DIR:-/opt/rocm}"
|
||||||
ARCH="${D4R_GPU_ARCH:-gfx1101}"
|
ARCH="${D4R_GPU_ARCH:-gfx1101}"
|
||||||
FP8="${D4R_TEX_FP8:-0}"
|
FP8="${D4R_TEX_FP8:-0}"
|
||||||
|
ACCURACY="${D4R_PREFER_ACCURACY:-0}"
|
||||||
LLVM="$R/lib/llvm/bin"
|
LLVM="$R/lib/llvm/bin"
|
||||||
B="$(mktemp -d)"
|
B="$(mktemp -d)"
|
||||||
trap 'rm -rf "$B"' EXIT
|
trap 'rm -rf "$B"' EXIT
|
||||||
# D4R_TEX_CFLAGS: extra flags for SRC.hip (tests, e.g. -DD4R_WMMA_LAYOUT=12 for the gfx12-layout shim on gfx11)
|
# D4R_TEX_CFLAGS: extra flags for SRC.hip (tests, e.g. -DD4R_WMMA_LAYOUT=12 for the gfx12-layout shim on gfx11)
|
||||||
read -r -a FLAGS <<< "${D4R_TEX_CFLAGS:-}"
|
read -r -a FLAGS <<< "${D4R_TEX_CFLAGS:-}"
|
||||||
[[ "$FP8" == 1 ]] && FLAGS+=(-DD4R_TEX_FP8)
|
[[ "$FP8" == 1 ]] && FLAGS+=(-DD4R_TEX_FP8)
|
||||||
|
[[ "$ACCURACY" == 1 ]] && FLAGS+=(-DD4R_ACCURACY)
|
||||||
# SRC.hip -> bitcode without target attributes, so ZLUDA's own target settings apply after linking
|
# SRC.hip -> bitcode without target attributes, so ZLUDA's own target settings apply after linking
|
||||||
"$LLVM/clang++" -x hip -std=c++20 -nogpuinc -nogpulib -O3 -mno-wavefrontsize64 --offload-device-only \
|
"$LLVM/clang++" -x hip -std=c++20 -nogpuinc -nogpulib -O3 -mno-wavefrontsize64 --offload-device-only \
|
||||||
--offload-arch="$ARCH" -fgpu-rdc -emit-llvm -c -Xclang -fdenormal-fp-math=dynamic -DD4R_KERNEL_NAME="$K" \
|
--offload-arch="$ARCH" -fgpu-rdc -emit-llvm -c -Xclang -fdenormal-fp-math=dynamic -DD4R_KERNEL_NAME="$K" \
|
||||||
@@ -24,10 +26,12 @@ read -r -a FLAGS <<< "${D4R_TEX_CFLAGS:-}"
|
|||||||
"$LLVM/llvm-dis" "$B/raw.bc" -o "$B/raw.ll"
|
"$LLVM/llvm-dis" "$B/raw.bc" -o "$B/raw.ll"
|
||||||
sed -E -e '/@llvm.used/d' -e '/wchar_size/d' -e '/llvm.module.flags/d' -e '/__hip_cuid/d' -e 's/optnone//g' \
|
sed -E -e '/@llvm.used/d' -e '/wchar_size/d' -e '/llvm.module.flags/d' -e '/__hip_cuid/d' -e 's/optnone//g' \
|
||||||
-e "s/\"target-cpu\"=\"$ARCH\"//g" -e 's/"target-features"="[^"]+"//g' "$B/raw.ll" | "$LLVM/llvm-as" -o "$B/extra.bc" -
|
-e "s/\"target-cpu\"=\"$ARCH\"//g" -e 's/"target-features"="[^"]+"//g' "$B/raw.ll" | "$LLVM/llvm-as" -o "$B/extra.bc" -
|
||||||
python3 "$D/make_ptx.py" "$K" "$B/$K.ptx" >/dev/null
|
D4R_PREFER_ACCURACY="$ACCURACY" python3 "$D/make_ptx.py" "$K" "$B/$K.ptx" >/dev/null
|
||||||
# the code-generation switches of the runtime (d4r.ini [Kernels]), which ZLUDA's module cache keys on
|
# the code-generation switches of the runtime (d4r.ini [Kernels]), which ZLUDA's module cache keys on
|
||||||
env LD_LIBRARY_PATH="$R/lib" D4R_ZLUDA_EXTRA_BC="$B/extra.bc" D4R_ZLUDA_WMMA=1 D4R_ZLUDA_WMMA_FP8=1 \
|
env LD_LIBRARY_PATH="$R/lib" D4R_ZLUDA_EXTRA_BC="$B/extra.bc" D4R_ZLUDA_WMMA=1 D4R_ZLUDA_WMMA_FP8=1 \
|
||||||
D4R_ZLUDA_WMMA_FP8_NATIVE="$FP8" D4R_ZLUDA_IGNORE_DENORMAL=1 D4R_ZLUDA_IMPLICIT_MAX_BLOCK=256 \
|
D4R_ZLUDA_WMMA_FP8_NATIVE="$FP8" D4R_ZLUDA_IGNORE_DENORMAL="$((1 - ACCURACY))" \
|
||||||
D4R_ZLUDA_WAVE64="${D4R_ZLUDA_WAVE64:-0}" "$D4R_ZLUDA_EMIT" "$B/$K.ptx" "$B/dump" "$ARCH" >/dev/null
|
D4R_ZLUDA_WMMA_F32ACC=0 D4R_ZLUDA_FAST_MATH=0 D4R_ZLUDA_IMPLICIT_MAX_BLOCK=256 \
|
||||||
|
D4R_ZLUDA_WAVE64="$([[ "$ACCURACY" == 1 ]] && echo 0 || echo "${D4R_ZLUDA_WAVE64:-0}")" \
|
||||||
|
"$D4R_ZLUDA_EMIT" "$B/$K.ptx" "$B/dump" "$ARCH" >/dev/null
|
||||||
cp "$B/dump/module.hsaco" "$OUT/$K.hsaco"
|
cp "$B/dump/module.hsaco" "$OUT/$K.hsaco"
|
||||||
printf '%-52s %s\n' "$K" "$(grep -E '^\s+\.(vgpr_count|private_segment_fixed_size|wavefront_size):' "$B/dump/asm.s" | tr -s ' ' | paste -sd' ')"
|
printf '%-52s %s\n' "$K" "$(grep -E '^\s+\.(vgpr_count|private_segment_fixed_size|wavefront_size):' "$B/dump/asm.s" | tr -s ' ' | paste -sd' ')"
|
||||||
@@ -209,9 +209,12 @@ 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"rrlite_post_3_[12]_mv(hi|lo)_(hdr|ldr)_folded|rrlite_downsample_kernel_(static|dynamic)_(hdr|ldr))$")
|
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_enc0_4x4_mv(hi|lo)_(hdr|ldr)|rrlite_dec0_4x4|"
|
||||||
|
r"rrlite_downsample_kernel_(static|dynamic)_(hdr|ldr))$")
|
||||||
ENC0_RE = re.compile(r"^rrlite_enc0_4x4_mv(hi|lo)_(hdr|ldr)_folded$")
|
ENC0_RE = re.compile(r"^rrlite_enc0_4x4_mv(hi|lo)_(hdr|ldr)_folded$")
|
||||||
|
|
||||||
|
|
||||||
@@ -275,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}")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// Kernels that ZLUDA compiles in strict-FP mode (constrained intrinsics) cannot take inlined FP code from
|
// Kernels that ZLUDA compiles in strict-FP mode (constrained intrinsics) cannot take inlined FP code from
|
||||||
// here (the AMDGPU backend fails on some constrained operations): build them with -DD4R_SUST_OUTLINE,
|
// here (the AMDGPU backend fails on some constrained operations): build them with -DD4R_SUST_OUTLINE,
|
||||||
// which keeps the FP bodies of the surface stores in noinline leaf functions (no calls, no stack).
|
// which keeps the FP bodies of the surface stores in noinline leaf functions (no calls, no stack).
|
||||||
#ifdef D4R_SUST_OUTLINE
|
#if defined(D4R_SUST_OUTLINE) || defined(D4R_ACCURACY)
|
||||||
#define FP_BODY __attribute__((device, noinline)) static
|
#define FP_BODY __attribute__((device, noinline)) static
|
||||||
#else
|
#else
|
||||||
#define FP_BODY __attribute__((device, always_inline)) static inline
|
#define FP_BODY __attribute__((device, always_inline)) static inline
|
||||||
|
|||||||
+14
-8
@@ -96,7 +96,8 @@ def split_args(s):
|
|||||||
|
|
||||||
|
|
||||||
def parse_kernel(path, name):
|
def parse_kernel(path, name):
|
||||||
src = open(path).read()
|
with open(path) as file:
|
||||||
|
src = file.read()
|
||||||
i = src.index('.entry ' + name + '(')
|
i = src.index('.entry ' + name + '(')
|
||||||
j = src.find('\n.visible', i + 10)
|
j = src.find('\n.visible', i + 10)
|
||||||
j = len(src) if j < 0 else j
|
j = len(src) if j < 0 else j
|
||||||
@@ -340,12 +341,11 @@ class Block:
|
|||||||
steps = 0
|
steps = 0
|
||||||
while pc < len(stmts):
|
while pc < len(stmts):
|
||||||
st = stmts[pc]
|
st = stmts[pc]
|
||||||
for l in st.labels:
|
if pc in pending:
|
||||||
if l in pending:
|
active |= pending.pop(pc)
|
||||||
active |= pending.pop(l)
|
|
||||||
if not active.any():
|
if not active.any():
|
||||||
# jump to the nearest label someone waits at
|
# Reconverge at either a forward target or a loop fallthrough.
|
||||||
targets = sorted(self.labels[l] for l in pending)
|
targets = sorted(pending)
|
||||||
if not targets:
|
if not targets:
|
||||||
break
|
break
|
||||||
pc = targets[0]
|
pc = targets[0]
|
||||||
@@ -365,10 +365,16 @@ class Block:
|
|||||||
if not taking.any():
|
if not taking.any():
|
||||||
pc += 1
|
pc += 1
|
||||||
continue
|
continue
|
||||||
raise NotImplementedError('divergent backward branch at ' + st.text)
|
# Lanes finishing a loop wait at its fallthrough while the
|
||||||
|
# other lanes execute their remaining iterations. Their
|
||||||
|
# registers and memory writes stay masked until reconvergence.
|
||||||
|
pending[pc + 1] = pending.get(pc + 1, np.zeros(self.T, bool)) | (active & ~taking)
|
||||||
|
active = taking.copy()
|
||||||
|
pc = target
|
||||||
|
continue
|
||||||
active = active & ~taking
|
active = active & ~taking
|
||||||
if taking.any():
|
if taking.any():
|
||||||
pending[st.args[0]] = pending.get(st.args[0], np.zeros(self.T, bool)) | taking
|
pending[target] = pending.get(target, np.zeros(self.T, bool)) | taking
|
||||||
pc += 1
|
pc += 1
|
||||||
continue
|
continue
|
||||||
if mask.any() or st.op.startswith(('mma', 'shfl', 'movmatrix', 'bar')):
|
if mask.any() or st.op.startswith(('mma', 'shfl', 'movmatrix', 'bar')):
|
||||||
|
|||||||
@@ -6,6 +6,38 @@ import numpy as np
|
|||||||
|
|
||||||
F16, F32, F64 = np.float16, np.float32, np.float64
|
F16, F32, F64 = np.float16, np.float32, np.float64
|
||||||
|
|
||||||
|
# Legacy replay reports retain their original approximate norm by default.
|
||||||
|
# Windows strict K validation selects the PTX fragment's rounded reduction.
|
||||||
|
NORM_FRAGMENT_ORDER = False
|
||||||
|
|
||||||
|
|
||||||
|
def norm_rows(x):
|
||||||
|
if not NORM_FRAGMENT_ORDER:
|
||||||
|
return f16(np.sum(x.astype(F32).astype(F64) ** 2, axis=1))
|
||||||
|
tokens, channels = x.shape
|
||||||
|
assert channels in (32, 64, 96, 128, 160)
|
||||||
|
square = f16(x.astype(F64) ** 2)
|
||||||
|
partial = []
|
||||||
|
for lane in range(4):
|
||||||
|
halves = []
|
||||||
|
for parity in range(2):
|
||||||
|
pairs = [add16(square[:, 2 * lane + 8 * parity + 32 * g:2 * lane + 8 * parity + 32 * g + 2],
|
||||||
|
square[:, 2 * lane + 8 * parity + 32 * g + 16:2 * lane + 8 * parity + 32 * g + 18])
|
||||||
|
for g in range(channels // 32)]
|
||||||
|
value = pairs[0]
|
||||||
|
if channels >= 64:
|
||||||
|
value = add16(value, pairs[1])
|
||||||
|
if channels == 96:
|
||||||
|
value = add16(value, pairs[2])
|
||||||
|
if channels >= 128:
|
||||||
|
value = add16(value, add16(pairs[2], pairs[3]))
|
||||||
|
if channels == 160:
|
||||||
|
value = add16(value, pairs[4])
|
||||||
|
halves.append(value)
|
||||||
|
partial.append(add16(halves[0], halves[1]))
|
||||||
|
total = add16(add16(partial[0], partial[1]), add16(partial[2], partial[3]))
|
||||||
|
return add16(total[:, 0], total[:, 1])
|
||||||
|
|
||||||
|
|
||||||
class Dump:
|
class Dump:
|
||||||
def __init__(self, d):
|
def __init__(self, d):
|
||||||
@@ -181,7 +213,7 @@ def pwin_enc2(P, bx, by, pm_order=((0, 0), (0, 1), (1, 0), (1, 1)), b2_in_c0=Tru
|
|||||||
"""logical enc2 layer (H 2, C 64 -> merged 96) for one window; returns (y [64 x 64], merged [16 x 96])"""
|
"""logical enc2 layer (H 2, C 64 -> merged 96) for one window; returns (y [64 x 64], merged [16 x 96])"""
|
||||||
C, H, Dh = 64, 2, 32
|
C, H, Dh = 64, 2, 32
|
||||||
x0 = load_window(P, bx, by, C)
|
x0 = load_window(P, bx, by, C)
|
||||||
s = f16(np.sum(x0.astype(F32).astype(F64) ** 2, axis=1))
|
s = norm_rows(x0)
|
||||||
r = rsqrt16(s)
|
r = rsqrt16(s)
|
||||||
h1 = mul16(x0, mul16(r[:, None], vec(P, 0, C)[None, :]))
|
h1 = mul16(x0, mul16(r[:, None], vec(P, 0, C)[None, :]))
|
||||||
Os = []
|
Os = []
|
||||||
@@ -248,7 +280,7 @@ def swin_core(P, L, x0, dbg=None, posattn=False):
|
|||||||
"""norm -> attention -> Wo + residual -> MLP (+ residual): the block output y [64 x C]"""
|
"""norm -> attention -> Wo + residual -> MLP (+ residual): the block output y [64 x C]"""
|
||||||
C, H = L.C, L.H
|
C, H = L.C, L.H
|
||||||
NCH = C // 32
|
NCH = C // 32
|
||||||
s = f16(np.sum(x0.astype(F32).astype(F64) ** 2, axis=1))
|
s = norm_rows(x0)
|
||||||
r = rsqrt16(s)
|
r = rsqrt16(s)
|
||||||
h1 = mul16(x0, mul16(r[:, None], vec(P, L.G1, C)[None, :]))
|
h1 = mul16(x0, mul16(r[:, None], vec(P, L.G1, C)[None, :]))
|
||||||
Os = []
|
Os = []
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ import swin_model as sm # noqa: E402
|
|||||||
LAYERS = {'enc1': (64, 2, 96, 0), 'enc2': (96, 4, 128, 0), 'enc3_tube': (128, 4, 0, 0), 'dec2': (96, 4, 0, 128),
|
LAYERS = {'enc1': (64, 2, 96, 0), 'enc2': (96, 4, 128, 0), 'enc3_tube': (128, 4, 0, 0), 'dec2': (96, 4, 0, 128),
|
||||||
'dec1': (64, 2, 0, 96)}
|
'dec1': (64, 2, 0, 96)}
|
||||||
|
|
||||||
|
# Preserve the existing Linux oracle by default; Windows strict replay enables
|
||||||
|
# the audited 310.9 f16 patch-merge bound before final e4m3 quantisation.
|
||||||
|
CLAMP_PATCH_MERGE = False
|
||||||
|
|
||||||
|
|
||||||
def blocks(grid, n, seed=1):
|
def blocks(grid, n, seed=1):
|
||||||
gx, gy = grid[0], grid[1]
|
gx, gy = grid[0], grid[1]
|
||||||
@@ -47,7 +51,11 @@ def model_block(P, layer, bx, by):
|
|||||||
Wg = me.E4M3[P.wbuf[sm.woff_table(PM0 + g * 128 * C, 512, 64 * C, 4 * C, 32)]]
|
Wg = me.E4M3[P.wbuf[sm.woff_table(PM0 + g * 128 * C, 512, 64 * C, 4 * C, 32)]]
|
||||||
bias = P.f16vec(PM0 + 4 * C * NPM + 64 * g, 32)
|
bias = P.f16vec(PM0 + 4 * C * NPM + 64 * g, 32)
|
||||||
cols.append(me.mma_chain(A, Wg, C0=np.broadcast_to(bias, (16, 32)).copy()))
|
cols.append(me.mma_chain(A, Wg, C0=np.broadcast_to(bias, (16, 32)).copy()))
|
||||||
merged = me.q8(sm.pair_to_nat(np.concatenate(cols, axis=1)))
|
merged = sm.pair_to_nat(np.concatenate(cols, axis=1))
|
||||||
|
if CLAMP_PATCH_MERGE:
|
||||||
|
limit = me.f16(2 * np.pi)
|
||||||
|
merged = np.clip(merged, -limit, limit)
|
||||||
|
merged = me.q8(merged)
|
||||||
return out, merged
|
return out, merged
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ def layout(C, heads, pre=0):
|
|||||||
L['end'] = M0 + 66 * C + 64 + (64 * C + 64) * (NC - 1)
|
L['end'] = M0 + 66 * C + 64 + (64 * C + 64) * (NC - 1)
|
||||||
return L
|
return L
|
||||||
|
|
||||||
def swin_block(P, x0, C, heads, pre=0):
|
def swin_block(P, x0, C, heads, pre=0, stages=None):
|
||||||
"""x0: 64 x C natural (f16 values). Returns (x2 pair order, out natural fp8 values)."""
|
"""x0: 64 x C natural (f16 values). Returns (x2 pair order, out natural fp8 values)."""
|
||||||
fp8 = lambda T: E4M3[P.wbuf[T]]
|
fp8 = lambda T: E4M3[P.wbuf[T]]
|
||||||
L = layout(C, heads, pre)
|
L = layout(C, heads, pre)
|
||||||
@@ -116,6 +116,8 @@ def swin_block(P, x0, C, heads, pre=0):
|
|||||||
Wo[32 * s:32 * s + 32] = woff_table(L['head'][s] + 64 * C + 512, 0, 512, 32, C)
|
Wo[32 * s:32 * s + 32] = woff_table(L['head'][s] + 64 * C + 512, 0, 512, 32, C)
|
||||||
x1 = mma_chain(pair_to_nat(q8(O)), fp8(Wo), C0=f16(nat_to_pair(x0) + P.f16vec(L['bo'], C)[None, :]))
|
x1 = mma_chain(pair_to_nat(q8(O)), fp8(Wo), C0=f16(nat_to_pair(x0) + P.f16vec(L['bo'], C)[None, :]))
|
||||||
h2 = pair_to_nat(rms_from_sum(x1, tree_norm2(x1), P.f16vec(L['g2'], C)))
|
h2 = pair_to_nat(rms_from_sum(x1, tree_norm2(x1), P.f16vec(L['g2'], C)))
|
||||||
|
if stages is not None:
|
||||||
|
stages.update(h1=h1, x1=pair_to_nat(x1), h2=h2, O=pair_to_nat(q8(O)))
|
||||||
x2 = f16(x1 + P.f16vec(L['b2'], C)[None, :])
|
x2 = f16(x1 + P.f16vec(L['b2'], C)[None, :])
|
||||||
idx = gp(np.arange(32))
|
idx = gp(np.arange(32))
|
||||||
for c, B1 in enumerate(L['b1']):
|
for c, B1 in enumerate(L['b1']):
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ Install
|
|||||||
1. Extract everything in this zip into the folder that holds the game's main .exe. For Unreal
|
1. Extract everything in 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.
|
Engine games that is <game>/<Project>/Binaries/Win64/, next to <Project>-Win64-Shipping.exe.
|
||||||
You get dxgi.dll, OptiScaler.ini, d3d12.dll, d3d12core.dll, this file and an d4r folder.
|
You get dxgi.dll, OptiScaler.ini, d3d12.dll, d3d12core.dll, this file and an d4r folder.
|
||||||
|
When updating, replace both d3d12 DLLs with the shim: it requires their resource-lifetime extension.
|
||||||
If the folder already has a dxgi.dll or OptiScaler.ini (another OptiScaler install), move those
|
If the folder already has a dxgi.dll or OptiScaler.ini (another OptiScaler install), move those
|
||||||
out of the way first. Also remove PROTON_USE_OPTISCALER from the game's launch options if you
|
out of the way first. Also remove PROTON_USE_OPTISCALER from the game's launch options if you
|
||||||
used GE-Proton's built-in OptiScaler: d4r brings its own.
|
used GE-Proton's built-in OptiScaler: d4r brings its own.
|
||||||
@@ -58,11 +59,23 @@ compiled. They are cached in ~/.cache/d4r, so later starts are quick.
|
|||||||
|
|
||||||
Settings
|
Settings
|
||||||
--------
|
--------
|
||||||
|
- In d4r/d4r.ini, [Kernels] PreferAccuracy = true prioritizes fidelity over performance for every
|
||||||
|
native kernel and the translated fallback. It selects separately built accuracy kernels, restores
|
||||||
|
intermediate FP8 quantization and per-MMA f16 rounding, preserves denormal handling, and keeps
|
||||||
|
NGX's synchronization calls. It defaults to false. Restart the game after changing it; the first
|
||||||
|
start may compile a separate cache. Missing accuracy variants use translated kernels, never the
|
||||||
|
fast native set. The mode aims to match NVIDIA's output, but 1:1 RTX image quality is not proven.
|
||||||
- d4r/d4r.ini: d4r's settings for this game. The main one is the DLSS model:
|
- d4r/d4r.ini: d4r's settings for this game. The main one is the DLSS model:
|
||||||
K (DLSS 4, default), E (DLSS 3 CNN, a little faster) or M (DLSS 4.5, slowest).
|
K (DLSS 4, default), E (DLSS 3 CNN, a little faster), M (DLSS 4.5), or L (DLSS 4.5
|
||||||
|
for Ultra Performance, especially 4K; experimental). Set Model = L and select Ultra Performance
|
||||||
|
in game or OptiScaler. Choosing the model does not change the render resolution.
|
||||||
- On RDNA4, [Kernels] NativeFp8 is on by default. It selects native FP8 WMMA and the matching
|
- On RDNA4, [Kernels] NativeFp8 is on by default. It selects native FP8 WMMA and the matching
|
||||||
gfx12-fp8 kernel folder; set it to false to use f16 widening. The FP8 path has not been tested
|
gfx12-fp8 kernel folder; set it to false to use f16 widening. The FP8 path has not been tested
|
||||||
on a real RDNA4 GPU.
|
on a real RDNA4 GPU.
|
||||||
|
- If L/M shows extra trails in motion, try [Kernels] NativeSwinEncoders = false with
|
||||||
|
PreferAccuracy = true. This uses original translated enc1/enc2 layers while retaining other
|
||||||
|
native acceleration. It reduced trails in a captured Townfall sequence on RX 7700 XT and costs
|
||||||
|
some GPU time. Restart after changing it; other scenes and RDNA4 hardware still need testing.
|
||||||
- OptiScaler.ini: OptiScaler's settings, such as its fps overlay and the render resolution of each
|
- OptiScaler.ini: OptiScaler's settings, such as its fps overlay and the render resolution of each
|
||||||
quality mode. Press Insert in game for OptiScaler's menu.
|
quality mode. Press Insert in game for OptiScaler's menu.
|
||||||
|
|
||||||
@@ -115,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.
|
||||||
|
|||||||
+44
-54
@@ -1,73 +1,63 @@
|
|||||||
# d4r 0.1.1
|
# d4r 0.1.3
|
||||||
|
|
||||||
NVIDIA's own DLSS Super Resolution on AMD Radeon RX 7000 (RDNA3) GPUs, in DirectX 12 games under Proton. This release fixes a VRAM leak and two input-format problems, bundles the ROCm runtime, and adds a dozen tested games.
|
This release adds an optional accuracy mode and fixes the GLIBC compatibility failure, DLSS artifacts, and stale output-buffer redirects reported after 0.1.2. Accuracy mode is **off by default**.
|
||||||
|
|
||||||
## What's new
|
## Added
|
||||||
|
|
||||||
- **No more VRAM growth when changing the DLSS quality setting.** Each switch between Quality, Balanced, Performance and so on used to keep about 130 MB of VRAM until the game closed, because of a reference-counting bug in ROCm 7.2's external-memory mapping. d4r now reuses its shared buffers, and VRAM stays flat over repeated switches. It also no longer leaks a file handle per buffer.
|
- **Prefer accuracy over performance.** Set `[Kernels] PreferAccuracy = true` in `d4r/d4r.ini` and restart the game. The release includes separate accuracy variants of every native network and texture kernel for all supported RDNA3 and RDNA4 targets, including RDNA4's native FP8 variants.
|
||||||
- **Games that pass 4-channel motion vectors now work.** Motion vectors in `R16G16B16A16` formats (including `TYPELESS`, as Dying Light: The Beast uses) used to be rejected, which left the screen black. They are now converted on the GPU and stay on the fast VRAM path.
|
- K restores FP16 accumulator rounding after each matrix step. M also restores intermediate FP8 quantization.
|
||||||
- **Multi-channel exposure textures stay on the VRAM path.** Games that pass exposure as an RGBA float texture no longer fall back to the slower copy through system memory, which showed up as reflections lagging behind (Ready or Not).
|
- The accuracy path retains denormal handling, disables relaxed accumulation/fast-math switches, and keeps NGX's synchronization calls. It uses a separate compilation cache.
|
||||||
- **ROCm no longer needs to be installed.** The zip includes the ROCm 7.2.4 runtime (HIP, HSA, comgr) in `d4r/rocm`, from AMD's Ubuntu 22.04 packages; it runs under Steam's container runtime on any distribution. `RocmDir` in `d4r/d4r.ini` still selects another installation.
|
- Missing accuracy variants use translated NVIDIA kernels and are logged; the fast native set is never silently substituted.
|
||||||
|
- This aims to preserve NVIDIA's arithmetic. It is slower, its FPS cost has not been benchmarked, and **1:1 image quality against RTX DLSS is not proven**.
|
||||||
|
- **Reproducible GLIBC 2.41 build environment.** A pinned Debian build image and release-build script rebuild ZLUDA/LLVM, the Wine bridge, the Windows DLLs, and both kernel sets. The ZIP includes toolchain details in `d4r/source/BUILD_INFO.txt`.
|
||||||
|
|
||||||
## Tested games
|
## Fixed
|
||||||
|
|
||||||
Radeon RX 7700 XT, GE-Proton 11-3. All three DLSS models (E, K, M) run in each game:
|
- **GLIBC loader compatibility ([#6](https://github.com/countervolts/d4r/issues/6)).** The previous release required `GLIBC_2.44` from `libm.so.6`, preventing ZLUDA from loading on systems such as the reporter's Bazzite installation. This release is built in a GLIBC 2.41 environment; the package check rejects any bundled Linux library requiring a newer ABI.
|
||||||
|
- **Cyberpunk 2077 preset-K black artifacts ([#5](https://github.com/countervolts/d4r/issues/5)).** Preserve FP16/FP64 denormal requirements while limiting the performance optimization to FP32. The fix covers both translated kernels and the rebuilt native texture kernels. Captured-scene replays showed substantially fewer sky artifacts; this is not a guarantee that every scene is artifact-free.
|
||||||
|
- **Stale direct-output redirects after resource destruction ([#8](https://github.com/countervolts/d4r/issues/8)).** Reused CUDA array handles no longer inherit an earlier feature's output buffer. This addresses the quality-switch corruption reported with preset M in Spider-Man 2.
|
||||||
|
|
||||||
SILENT HILL Townfall, Ghost of Tsushima DIRECTOR'S CUT, Ready or Not, Clair Obscur: Expedition 33, Marvel's Spider-Man: Miles Morales, The Last of Us Part I, Control Ultimate Edition, Cyberpunk 2077, Alan Wake 2, SILENT HILL 2 (2024), Subnautica 2, Horizon Zero Dawn Remastered, Ratchet & Clank: Rift Apart, Dying Light: The Beast.
|
## Improved
|
||||||
|
|
||||||
Several need a small setup step: renaming a file, a launch option, or an `OptiScaler.ini` setting. [SUPPORTED_GAMES.md](https://github.com/countervolts/d4r/blob/main/SUPPORTED_GAMES.md) lists these, along with known image-quality issues per model.
|
- **Synchronization diagnostics and an optional blocking output wait.** `D4R_SHIM_BLOCKING_SYNC=1` yields the CPU while waiting for DLSS output, with a fallback to context synchronization. It is an opt-in mitigation for [#7](https://github.com/countervolts/d4r/issues/7); reduced CPU usage on the reporter's RDNA4 setup still needs confirmation.
|
||||||
|
- **More useful failure logs.** Rejected input/output formats and dimensions are now recorded even after startup, with repeated errors rate-limited. Output-wait CPU and wall time are reported separately, and GPU timing queries are deferred until asynchronous work finishes.
|
||||||
|
- **More conservative accuracy texture builds.** Strict surface conversions use separate helper functions, avoiding a compiler failure when the original denormal handling is retained.
|
||||||
|
- **Accuracy build safeguards.** The builder refuses to label a directory containing older fast binaries as an accuracy set. Configuration tests cover every release target, missing-set fallback, invalid markers, environment precedence, and the default-off behavior.
|
||||||
|
|
||||||
## Install
|
## Also included from 0.1.2
|
||||||
|
|
||||||
1. Extract `d4r-0.1.1.zip` into the folder that holds the game's main `.exe`. For Unreal Engine games that is `<Project>/Binaries/Win64`. Keep the zip's layout: `dxgi.dll`, `OptiScaler.ini`, `d3d12.dll` and `d3d12core.dll` go next to the `.exe`, beside the `d4r` folder.
|
These capabilities remain in the release; they are not newly validated hardware results:
|
||||||
2. In Steam, force GE-Proton 11 for the game, and set these launch options:
|
|
||||||
|
- Experimental RDNA4 builds for gfx1200/gfx1201, with `NativeFp8` enabled by default, alongside gfx1100–gfx1103 RDNA3 builds.
|
||||||
|
- Kernel selection uses the GPU with the most SIMDs when integrated and discrete GPUs coexist; `D4R_GPU_ARCH` overrides it.
|
||||||
|
- DLSS 3 matrix-kernel acceleration; M's cached weight preparation, packed FP8 encoding, improved patch-merge/weight loads, and direct output; K's CU-mode layers; wave64 performance texture variants.
|
||||||
|
- Native texture/output variants for the game's motion-vector resolution, HDR/LDR input and depth direction, compiled offline per GPU target.
|
||||||
|
- The bundled ROCm 7.2.4 runtime: no separate ROCm installation is required.
|
||||||
|
|
||||||
|
The 0.1.2 performance measurements remain historical results. They are not benchmarks of this rebuild or the new accuracy mode.
|
||||||
|
|
||||||
|
## Install or upgrade
|
||||||
|
|
||||||
|
1. Back up your `d4r/d4r.ini` and `OptiScaler.ini` if you customized them.
|
||||||
|
2. Extract `d4r-0.1.3.zip` into the folder holding the game's main `.exe` (`<Project>/Binaries/Win64` for Unreal Engine games), keeping the ZIP's layout.
|
||||||
|
3. Restore your settings if needed. Older INIs omit `PreferAccuracy`, which defaults to off; add it under `[Kernels]` to enable it.
|
||||||
|
4. Select GE-Proton 11 in Steam (tested integration: GE-Proton11-3), with:
|
||||||
```
|
```
|
||||||
PROTON_FORCE_NVAPI=1 DXVK_NVAPI_GPU_ARCH=AD100 %command%
|
PROTON_FORCE_NVAPI=1 DXVK_NVAPI_GPU_ARCH=AD100 %command%
|
||||||
```
|
```
|
||||||
3. Select DLSS in the game. The first start compiles DLSS's GPU kernels, which takes about a minute and a half; later starts use the cache in `~/.cache/d4r`.
|
5. Select DLSS in the game and restart after changing the accuracy option.
|
||||||
|
|
||||||
**Upgrading from 0.1.0:** extract over the old files, then restore your `d4r/d4r.ini` if you changed it. `D4R_README.txt` in the zip has the details, and `sh d4r/d4r-check.sh` checks an install.
|
The rebuilt runtime may compile a new kernel cache on first start. `sh d4r/d4r-check.sh` checks the install. [SUPPORTED_GAMES.md](https://github.com/countervolts/d4r/blob/main/SUPPORTED_GAMES.md) lists game-specific setup and known issues.
|
||||||
|
|
||||||
## Requirements
|
## Validation and limits
|
||||||
|
|
||||||
- An AMD Radeon RX 7000 series GPU. The fast kernels are built for gfx1101 (RX 7700 XT, RX 7800 XT, RX 7700). Other RDNA3 cards run DLSS without them:
|
- All bundled Linux ELF libraries are checked against the GLIBC 2.41 ABI limit; release loading is checked inside that environment.
|
||||||
- DLSS 3 (E) at full speed;
|
- The release contains performance and accuracy kernels for all eight target variants. Configuration tests and sampled CPU-emulated kernel/reference checks cover the new accuracy path; these do not establish full-game or RTX parity.
|
||||||
- DLSS 4.5 (M) at about half speed;
|
- RDNA4 remains experimental: emulator/compiler coverage does not prove hardware speed, image quality, or driver stability. The external RX 7900 XTX report is RDNA3, not RDNA4 validation.
|
||||||
- DLSS 4 (K) without its transformer, with a worse image.
|
- Super Resolution only: Frame Generation, Ray Reconstruction, and DLSS 5 Neural Rendering are unsupported. Keep Ray Reconstruction off.
|
||||||
- The amdgpu kernel driver (`/dev/kfd`).
|
|
||||||
- GE-Proton 11 (tested: GE-Proton11-3).
|
|
||||||
|
|
||||||
## Performance
|
|
||||||
|
|
||||||
Radeon RX 7700 XT, SILENT HILL Townfall at 2560×1440, average fps over the same 62-second walk:
|
|
||||||
|
|
||||||
| Mode | DLSS 3 CNN (E) | DLSS 4 (K) | DLSS 4.5 (M) | FSR 4 |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| Quality | 72 | 69 | 51 | 76 |
|
|
||||||
| Balanced | 80 | 76 | 59 | 84 |
|
|
||||||
| Performance | 88 | 82 | 69 | 94 |
|
|
||||||
| Ultra Performance | 89 | 93 | 90 | 107 |
|
|
||||||
|
|
||||||
Native 2560×1440 without upscaling (the game's TSR at 100%) runs at 49 fps. This release does not change the per-frame cost of DLSS.
|
|
||||||
|
|
||||||
## Known limitations
|
|
||||||
|
|
||||||
- Only DLSS Super Resolution in DirectX 12 games. There is no Frame Generation or Ray Reconstruction: keep Ray Reconstruction off in games that offer it.
|
|
||||||
- Use the game's DLSS setting where it exists. Feeding DLSS from FSR or XeSS inputs through OptiScaler can ghost, or show a black screen in some games.
|
|
||||||
- Saving from the OptiScaler overlay ("Save INI") resets d4r's `OptiScaler.ini` values to `auto`, including frame generation. Restore them afterwards.
|
|
||||||
- Do not use it in games with anti-cheat.
|
|
||||||
- DLSS 4.5 (M) is much slower than FSR 4 on RDNA3.
|
|
||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
|
||||||
d4r is Apache 2.0. The zip also contains:
|
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.
|
||||||
- ZLUDA (Apache 2.0 or MIT);
|
|
||||||
- vkd3d-proton (LGPL 2.1, patched; the patch is in `d4r/source`);
|
|
||||||
- the ROCm 7.2.4 runtime, unmodified: HIP and rocprofiler-register (MIT), ROCr (NCSA), comgr (Apache 2.0), and Ubuntu's libelf and libnuma (LGPL). The license texts are in `d4r/licenses`;
|
|
||||||
- OptiScaler 0.9.4 (GPL 3.0, unmodified; source at https://github.com/optiscaler/OptiScaler/tree/v0.9.4).
|
|
||||||
|
|
||||||
`d4r/source/SOURCES.txt` lists where every file comes from.
|
The attached `.sha256` file contains the ZIP's checksum.
|
||||||
|
|
||||||
NVIDIA's files in the zip, and the kernels built from NVIDIA's code, belong to NVIDIA and are not covered by any of these licenses. d4r is not affiliated with NVIDIA, AMD or the OptiScaler project.
|
|
||||||
|
|
||||||
**SHA-256** `d4r-0.1.1.zip`: `@SHA256@`
|
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
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 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
|
||||||
(d4r-kernels.txt lists hashes of the DLSS code each kernel replaces; it holds no NVIDIA code)
|
(d4r-kernels.txt lists hashes of the DLSS code each kernel replaces; it holds no NVIDIA code)
|
||||||
d4r/d4r.ini, d4r/d4r-check.sh, D4R_README.txt
|
d4r/d4r.ini, d4r/d4r-check.sh, D4R_README.txt
|
||||||
|
|
||||||
Third-party software, unmodified or patched by d4r:
|
Third-party software, unmodified or patched by d4r:
|
||||||
d4r/zluda/libcuda.so ZLUDA (Apache License 2.0 or MIT), https://github.com/vosen/ZLUDA
|
d4r/zluda/libcuda.so ZLUDA (Apache License 2.0 or MIT), https://github.com/vosen/ZLUDA
|
||||||
commit @ZLUDA_COMMIT@ with d4r/source/patches/zluda/0002 to 0005 applied.
|
commit @ZLUDA_COMMIT@ with d4r/source/patches/zluda/0002 to 0007 applied.
|
||||||
It contains LLVM (Apache License 2.0 with LLVM exceptions).
|
It contains LLVM (Apache License 2.0 with LLVM exceptions).
|
||||||
Licenses: d4r/licenses/ZLUDA-LICENSE-*.txt, d4r/licenses/LLVM-LICENSE.txt
|
Licenses: d4r/licenses/ZLUDA-LICENSE-*.txt, d4r/licenses/LLVM-LICENSE.txt
|
||||||
d3d12.dll, d3d12core.dll
|
d3d12.dll, d3d12core.dll
|
||||||
vkd3d-proton (GNU LGPL 2.1), https://github.com/HansKristian-Work/vkd3d-proton
|
vkd3d-proton (GNU LGPL 2.1), https://github.com/HansKristian-Work/vkd3d-proton
|
||||||
commit @VKD3D_COMMIT@ with d4r/source/patches/vkd3d-proton/0001 applied,
|
commit @VKD3D_COMMIT@ with d4r/source/patches/vkd3d-proton/0001 and 0002 applied,
|
||||||
built with its build-win64.txt cross file (meson --buildtype release).
|
built with its build-win64.txt cross file (meson --buildtype release).
|
||||||
Licenses: d4r/licenses/vkd3d-proton-*.txt
|
Licenses: d4r/licenses/vkd3d-proton-*.txt
|
||||||
d4r/rocm/lib/ ROCm 7.2.4 runtime, unmodified, from AMD's Ubuntu 22.04 packages
|
d4r/rocm/lib/ ROCm 7.2.4 runtime, unmodified, from AMD's Ubuntu 22.04 packages
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
0.1.2
|
0.1.3
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
FROM debian:trixie-slim@sha256:a99cfc517144bc59b1978475ec53b46ecabec7e43635402ee5b77cc54cd1b20a
|
||||||
|
|
||||||
|
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||||
|
build-essential clang cmake ninja-build pkg-config libssl-dev python3 python3-numpy \
|
||||||
|
git git-lfs wine64-tools libwine-dev gcc-mingw-w64-x86-64-posix \
|
||||||
|
g++-mingw-w64-x86-64-posix meson libvulkan-dev zip unzip ca-certificates \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# ROCm loads the system's AMD DRM library even for the CPU-only loader check.
|
||||||
|
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||||
|
libdrm-amdgpu1 libdrm2 mingw-w64-tools glslang-tools spirv-tools \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN ln -s /usr/lib/wine/winegcc /usr/local/bin/winegcc \
|
||||||
|
&& ln -s /usr/lib/wine/winebuild /usr/local/bin/winebuild \
|
||||||
|
&& ln -s /usr/bin/clang-19 /usr/local/bin/clang-cl \
|
||||||
|
&& useradd --uid 1000 --create-home builder
|
||||||
|
USER builder
|
||||||
|
WORKDIR /work
|
||||||
+28
-11
@@ -3,12 +3,19 @@
|
|||||||
; that config/d4r.ini.default in the source repository lists for it, e.g. in the game's launch options.
|
; that config/d4r.ini.default in the source repository lists for it, e.g. in the game's launch options.
|
||||||
|
|
||||||
[DLSS]
|
[DLSS]
|
||||||
; Which DLSS network runs:
|
; DLSS model recommendations:
|
||||||
; K DLSS 4 transformer (default)
|
;
|
||||||
; E DLSS 3 CNN: a little faster, softer image
|
; Model | DLSS | Recommended modes | Notes
|
||||||
; M DLSS 4.5 transformer: best image, much slower on RDNA3
|
; ------+-------+---------------------------------+----------------------------------
|
||||||
; game whatever the game (or OptiScaler's preset override) asks for
|
; K | 4 | DLAA / Quality / Balanced | Default; faster alternative to M/L
|
||||||
; Frame rates measured on an RX 7700 XT at 2560x1440, Quality mode: see D4R_README.txt.
|
; M | 4.5 | Performance | More demanding on RDNA3
|
||||||
|
; L | 4.5 | Ultra Performance | Especially 4K; experimental
|
||||||
|
; E | 3 CNN | Quality / Balanced / Performance | Legacy faster fallback
|
||||||
|
; F | 3 CNN | DLAA / Ultra Performance | Deprecated; prefer K or L
|
||||||
|
;
|
||||||
|
; Select the upscaling mode separately in game/OptiScaler. These are recommendations;
|
||||||
|
; other modes are allowed. L requires the L texture build.
|
||||||
|
; game = use the game/OptiScaler choice. Benchmarks: D4R_README.txt.
|
||||||
Model = K
|
Model = K
|
||||||
; NVIDIA's library marks the image "DLSS SDK - DO NOT DISTRIBUTE" in this setup; d4r turns that off.
|
; NVIDIA's library marks the image "DLSS SDK - DO NOT DISTRIBUTE" in this setup; d4r turns that off.
|
||||||
; true shows it.
|
; true shows it.
|
||||||
@@ -21,6 +28,13 @@ ShowWatermark = false
|
|||||||
FrameAge = 0
|
FrameAge = 0
|
||||||
|
|
||||||
[Kernels]
|
[Kernels]
|
||||||
|
; Prioritize fidelity over speed. Uses accuracy variants of all native kernels, preserves NVIDIA's
|
||||||
|
; intermediate FP8/f16 rounding and denormal handling, and keeps NGX's synchronization calls.
|
||||||
|
; Slower but aims to match RTX DLSS.
|
||||||
|
PreferAccuracy = false
|
||||||
|
; false reduces L/M motion trails by using original enc1/enc2 layers; other native kernels stay on.
|
||||||
|
; Best paired with PreferAccuracy = true; costs some GPU time. Restart after changing this.
|
||||||
|
NativeSwinEncoders = true
|
||||||
; Hand-written RDNA3 and RDNA4 kernels for the DLSS 4 and 4.5 networks (d4r\kernels). Each one is used
|
; Hand-written RDNA3 and RDNA4 kernels for the DLSS 4 and 4.5 networks (d4r\kernels). Each one is used
|
||||||
; only when your DLSS library's code for that kernel matches the code it was written for; otherwise
|
; only when your DLSS library's code for that kernel matches the code it was written for; otherwise
|
||||||
; that kernel is compiled from NVIDIA's code instead, which is slower but still correct.
|
; that kernel is compiled from NVIDIA's code instead, which is slower but still correct.
|
||||||
@@ -31,10 +45,13 @@ NativeKernels = on
|
|||||||
NativeFp8 = on
|
NativeFp8 = on
|
||||||
|
|
||||||
[Interop]
|
[Interop]
|
||||||
; These keep DLSS's inputs and output in video memory and move the waits between the game and DLSS
|
; Experimental thin-feature tracking: 0 = off; 1/2 = opaque-surface dilation radius.
|
||||||
; onto the GPU. Leave them on unless you are chasing a problem.
|
; Both enabled values fill sky gaps from the nearest foreground within 8 render pixels.
|
||||||
|
; Requires render- or display-resolution, unjittered motion vectors and same-frame linear VRAM inputs. Restart to change.
|
||||||
|
; Unsupported features keep original vectors; larger radii may affect disocclusion edges.
|
||||||
|
MotionVectorDilation = 0
|
||||||
|
; Keep inputs and output in video memory. Input and output readiness waits use CPU sleeps.
|
||||||
VramInterop = true
|
VramInterop = true
|
||||||
GpuWait = true
|
|
||||||
LinearInputs = true
|
LinearInputs = true
|
||||||
ElideNgxSync = true
|
ElideNgxSync = true
|
||||||
; Lets DLSS's last kernel write straight into the game's output buffer (DLSS 4 / K only). It takes
|
; Lets DLSS's last kernel write straight into the game's output buffer (DLSS 4 / K only). It takes
|
||||||
@@ -55,5 +72,5 @@ Profile = false
|
|||||||
|
|
||||||
[Env]
|
[Env]
|
||||||
; Extra environment variables for d4r, NAME = value.
|
; Extra environment variables for d4r, NAME = value.
|
||||||
; Use a blocking CUDA event while waiting for DLSS output.
|
; Sleeping event queries are the default; 0 restores context waits for an A/B comparison.
|
||||||
; D4R_SHIM_BLOCKING_SYNC = 1
|
; D4R_SHIM_BLOCKING_SYNC = 0
|
||||||
@@ -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
|
||||||
@@ -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.
|
||||||
@@ -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,328 @@
|
|||||||
|
From 33bbac250ac7e2ca4eff67e8817ada9dad578208 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Codex <codex@localhost>
|
||||||
|
Date: Thu, 1 Oct 2026 16:10:08 +0300
|
||||||
|
Subject: [PATCH] Support explicitly selected Windows d4r DLSS backend on
|
||||||
|
physical AMD adapters
|
||||||
|
|
||||||
|
---
|
||||||
|
OptiScaler/Config.cpp | 3 ++
|
||||||
|
OptiScaler/Config.h | 1 +
|
||||||
|
OptiScaler/OptiScaler.vcxproj | 4 +--
|
||||||
|
OptiScaler/State.h | 1 +
|
||||||
|
OptiScaler/Util.cpp | 12 +++++--
|
||||||
|
OptiScaler/dllmain.cpp | 33 +++++++++++++++++--
|
||||||
|
OptiScaler/hooks/LibraryLoad_Hooks.cpp | 17 ++++++++++
|
||||||
|
OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp | 17 ++++++++++
|
||||||
|
OptiScaler/misc/IdentifyGpu.cpp | 20 +++++++++++
|
||||||
|
OptiScaler/proxies/NVNGX_Proxy.h | 5 ++-
|
||||||
|
OptiScaler/upscalers/FeatureProvider_Dx12.cpp | 10 ++++++
|
||||||
|
11 files changed, 115 insertions(+), 8 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/OptiScaler/Config.cpp b/OptiScaler/Config.cpp
|
||||||
|
index d8c62d3..f444387 100644
|
||||||
|
--- a/OptiScaler/Config.cpp
|
||||||
|
+++ b/OptiScaler/Config.cpp
|
||||||
|
@@ -323,6 +323,7 @@ bool Config::Reload(std::filesystem::path iniPath)
|
||||||
|
{
|
||||||
|
// Don't enable again if set false because of no nvngx found
|
||||||
|
DLSSEnabled.set_from_config(readBool("DLSS", "Enabled"));
|
||||||
|
+ DLSSAllowExternalBackend.set_from_config(readBool("DLSS", "AllowExternalBackend"));
|
||||||
|
UseGenericAppIdWithDlss.set_from_config(readBool("DLSS", "UseGenericAppIdWithDlss"));
|
||||||
|
|
||||||
|
RenderPresetOverride.set_from_config(readBool("DLSS", "RenderPresetOverride"));
|
||||||
|
@@ -1168,6 +1169,8 @@ bool Config::SaveIni()
|
||||||
|
// DLSS
|
||||||
|
{
|
||||||
|
ini.SetValue("DLSS", "Enabled", GetBoolValue(Instance()->DLSSEnabled.value_for_config()).c_str());
|
||||||
|
+ ini.SetValue("DLSS", "AllowExternalBackend",
|
||||||
|
+ GetBoolValue(Instance()->DLSSAllowExternalBackend.value_for_config()).c_str());
|
||||||
|
ini.SetValue("DLSS", "RenderPresetOverride",
|
||||||
|
GetBoolValue(Instance()->RenderPresetOverride.value_for_config()).c_str());
|
||||||
|
ini.SetValue("DLSS", "RenderPresetForAll",
|
||||||
|
diff --git a/OptiScaler/Config.h b/OptiScaler/Config.h
|
||||||
|
index 4ab62a4..0caf8c8 100644
|
||||||
|
--- a/OptiScaler/Config.h
|
||||||
|
+++ b/OptiScaler/Config.h
|
||||||
|
@@ -275,6 +275,7 @@ class Config
|
||||||
|
|
||||||
|
// DLSS
|
||||||
|
CustomOptional<bool> DLSSEnabled { true };
|
||||||
|
+ CustomOptional<bool> DLSSAllowExternalBackend { false };
|
||||||
|
CustomOptional<bool> RenderPresetOverride { false };
|
||||||
|
CustomOptional<uint32_t> RenderPresetForAll { 0 };
|
||||||
|
CustomOptional<uint32_t> RenderPresetDLAA { 0 };
|
||||||
|
diff --git a/OptiScaler/OptiScaler.vcxproj b/OptiScaler/OptiScaler.vcxproj
|
||||||
|
index 24ca80b..dfbfc97 100644
|
||||||
|
--- a/OptiScaler/OptiScaler.vcxproj
|
||||||
|
+++ b/OptiScaler/OptiScaler.vcxproj
|
||||||
|
@@ -236,7 +236,7 @@
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
- <GenerateDebugInformation>false</GenerateDebugInformation>
|
||||||
|
+ <GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<EnableUAC>false</EnableUAC>
|
||||||
|
<AdditionalDependencies>winhttp.lib;WindowsApp.lib;Dbghelp.lib;dxgi.lib;d3d11.lib;d3d12.lib;vulkan-1.lib;dxguid.lib;freetype.lib;ffx_fsr2_api_x64.lib;ffx_fsr2_api_dx11_x64.lib;ffx_fsr2_api_dx12_x64.lib;ffx_fsr2_api_vk_x64.lib;ffx_fsr2_212_api_dx12_x64.lib;ffx_fsr2_212_api_vk_x64.lib;ffx_fsr2_212_api_x64.lib;d3dcompiler.lib;ffx_backend_dx11_x64.lib;ffx_fsr3_x64.lib;ffx_frameinterpolation_x64.lib;ffx_fsr3upscaler_x64.lib;ffx_opticalflow_x64.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
<ModuleDefinitionFile>Source.def</ModuleDefinitionFile>
|
||||||
|
@@ -873,4 +873,4 @@ copy NUL "$(SolutionDir)x64\Release\a\!! EXTRACT ALL FILES TO GAME FOLDER !!" /Y
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
-</Project>
|
||||||
|
\ No newline at end of file
|
||||||
|
+</Project>
|
||||||
|
diff --git a/OptiScaler/State.h b/OptiScaler/State.h
|
||||||
|
index 42164de..84f8263 100644
|
||||||
|
--- a/OptiScaler/State.h
|
||||||
|
+++ b/OptiScaler/State.h
|
||||||
|
@@ -265,6 +265,7 @@ class State
|
||||||
|
int dlssgDetectedInterpolationCount = 0;
|
||||||
|
|
||||||
|
// DLSS
|
||||||
|
+ bool dlssExternalBackend = false;
|
||||||
|
bool dlssPresetsOverriddenExternally = false;
|
||||||
|
bool dlssPresetsOverridenByOpti = false;
|
||||||
|
uint32_t dlssRenderPresetExternal = 0;
|
||||||
|
diff --git a/OptiScaler/Util.cpp b/OptiScaler/Util.cpp
|
||||||
|
index f7ac6fa..7367a91 100644
|
||||||
|
--- a/OptiScaler/Util.cpp
|
||||||
|
+++ b/OptiScaler/Util.cpp
|
||||||
|
@@ -486,8 +486,16 @@ bool Util::GetFileVersion(std::wstring dllPath, version_t* fileVersionOut, versi
|
||||||
|
|
||||||
|
bool Util::IsSubpath(const std::filesystem::path& path, const std::filesystem::path& base)
|
||||||
|
{
|
||||||
|
- auto rel = std::filesystem::relative(path, base);
|
||||||
|
- auto first = *rel.begin();
|
||||||
|
+ // Called by LdrLoadDll hooks: relative()/weakly_canonical() perform file
|
||||||
|
+ // queries, can re-enter the hooks, and throw when an optional directory or
|
||||||
|
+ // a bare DLL search name cannot be resolved. This is a lexical routing
|
||||||
|
+ // check, not a filesystem access control decision.
|
||||||
|
+ auto normalizedPath = std::filesystem::path(ToLower(path.lexically_normal().wstring()));
|
||||||
|
+ auto normalizedBase = std::filesystem::path(ToLower(base.lexically_normal().wstring()));
|
||||||
|
+ auto rel = normalizedPath.lexically_relative(normalizedBase);
|
||||||
|
+ if (rel.empty())
|
||||||
|
+ return false;
|
||||||
|
+ const auto first = *rel.begin();
|
||||||
|
return first != "." && first != "..";
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/OptiScaler/dllmain.cpp b/OptiScaler/dllmain.cpp
|
||||||
|
index cba14b1..b4b786e 100644
|
||||||
|
--- a/OptiScaler/dllmain.cpp
|
||||||
|
+++ b/OptiScaler/dllmain.cpp
|
||||||
|
@@ -948,7 +948,7 @@ static void CheckWorkingMode()
|
||||||
|
|
||||||
|
// NVAPI
|
||||||
|
// Doesn't seem to like GetModuleHandle for some reason, so call our load to make sure
|
||||||
|
- if (GetDllNameWModule(&nvapiNamesW) != nullptr)
|
||||||
|
+ if (!State::Instance().dlssExternalBackend && GetDllNameWModule(&nvapiNamesW) != nullptr)
|
||||||
|
{
|
||||||
|
// This hooks nvapi as well when possible
|
||||||
|
auto nvapi64 = LibraryLoadHooks::LoadNvApi();
|
||||||
|
@@ -1958,7 +1958,33 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
|
||||||
|
return nvidiaDetected;
|
||||||
|
};
|
||||||
|
|
||||||
|
- bool possibleNvidia = isNvidiaViaNvapi();
|
||||||
|
+ // Opt in only to a configured, versioned Windows d4r backend. The
|
||||||
|
+ // marker performs no HIP/NGX initialization under DLL_PROCESS_ATTACH.
|
||||||
|
+ // GPU support is still checked by its public NGX requirements API.
|
||||||
|
+ if (Config::Instance()->DLSSEnabled.value_or_default() &&
|
||||||
|
+ Config::Instance()->DLSSAllowExternalBackend.value_or_default() &&
|
||||||
|
+ Config::Instance()->NvngxPath.has_value())
|
||||||
|
+ {
|
||||||
|
+ auto backendPath = std::filesystem::path(Config::Instance()->NvngxPath.value()) / L"_nvngx.dll";
|
||||||
|
+ if (backendPath.is_absolute())
|
||||||
|
+ {
|
||||||
|
+ auto module = NtdllProxy::LoadLibraryExW_Ldr(backendPath.c_str(), nullptr,
|
||||||
|
+ LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
|
||||||
|
+ if (module)
|
||||||
|
+ {
|
||||||
|
+ using BackendVersion = unsigned (*)();
|
||||||
|
+ auto version = reinterpret_cast<BackendVersion>(
|
||||||
|
+ KernelBaseProxy::GetProcAddress_()(module, "d4r_WindowsBackendVersion"));
|
||||||
|
+ State::Instance().dlssExternalBackend = version && version() == 1;
|
||||||
|
+ NtdllProxy::FreeLibrary_Ldr(module);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ if (State::Instance().dlssExternalBackend)
|
||||||
|
+ spdlog::info("Validated external Windows d4r DLSS backend; retaining physical GPU identity");
|
||||||
|
+ else
|
||||||
|
+ spdlog::error("AllowExternalBackend requires an absolute NvngxPath containing Windows d4r ABI 1");
|
||||||
|
+ }
|
||||||
|
+ bool possibleNvidia = !State::Instance().dlssExternalBackend && isNvidiaViaNvapi();
|
||||||
|
|
||||||
|
spdlog::info("");
|
||||||
|
spdlog::info("Check for DLSS files");
|
||||||
|
@@ -1992,7 +2018,8 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
|
||||||
|
State::Instance().NVNGX_DLSSG_Path = Util::FindFilePath(exePath, "nvngx_dlssg.dll");
|
||||||
|
|
||||||
|
// Not 100% accurate for Nvidia cards without DLSS
|
||||||
|
- if (Config::Instance()->DLSSEnabled.value_or_default() && possibleNvidia)
|
||||||
|
+ if (Config::Instance()->DLSSEnabled.value_or_default() &&
|
||||||
|
+ (possibleNvidia || State::Instance().dlssExternalBackend))
|
||||||
|
{
|
||||||
|
if (State::Instance().NVNGX_DLSS_Path.has_value())
|
||||||
|
{
|
||||||
|
diff --git a/OptiScaler/hooks/LibraryLoad_Hooks.cpp b/OptiScaler/hooks/LibraryLoad_Hooks.cpp
|
||||||
|
index 90bdb89..108ea5b 100644
|
||||||
|
--- a/OptiScaler/hooks/LibraryLoad_Hooks.cpp
|
||||||
|
+++ b/OptiScaler/hooks/LibraryLoad_Hooks.cpp
|
||||||
|
@@ -46,6 +46,18 @@ HMODULE LibraryLoadHooks::LoadLibraryCheckW(std::wstring libName, LPCWSTR lpLibF
|
||||||
|
{
|
||||||
|
auto libNameA = wstring_to_string(libName);
|
||||||
|
|
||||||
|
+ // The external D3D12 shim itself uses a locally supplied CUDA NGX core.
|
||||||
|
+ // Keep that exact absolute path distinct from game-facing NGX interception.
|
||||||
|
+ if (State::Instance().dlssExternalBackend)
|
||||||
|
+ {
|
||||||
|
+ wchar_t corePath[32768] {};
|
||||||
|
+ const auto length = GetEnvironmentVariableW(L"D4R_NGX_CORE", corePath, 32768);
|
||||||
|
+ if (length && length < 32768 &&
|
||||||
|
+ Util::ToLower(std::filesystem::path(libName).lexically_normal().wstring()) ==
|
||||||
|
+ Util::ToLower(std::filesystem::path(corePath).lexically_normal().wstring()))
|
||||||
|
+ return nullptr;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
#ifdef LOG_LIB_OPERATIONS
|
||||||
|
LOG_TRACE("{}", libNameA);
|
||||||
|
#endif
|
||||||
|
@@ -132,6 +144,11 @@ HMODULE LibraryLoadHooks::LoadLibraryCheckW(std::wstring libName, LPCWSTR lpLibF
|
||||||
|
// NvApi64.dll
|
||||||
|
if (CheckDllNameW(&libName, &nvapiNamesW))
|
||||||
|
{
|
||||||
|
+ // An explicit external CUDA backend owns its NVAPI topology provider.
|
||||||
|
+ // Returning OptiScaler's fake-NVAPI would replace that provider and can
|
||||||
|
+ // recurse into DXGI while the NGX loader holds the loader lock.
|
||||||
|
+ if (State::Instance().dlssExternalBackend)
|
||||||
|
+ return nullptr; // Continue through the original loader.
|
||||||
|
LOG_INFO("{} call!", libNameA);
|
||||||
|
|
||||||
|
return LibraryLoadHooks::LoadNvApi();
|
||||||
|
diff --git a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
|
||||||
|
index 68e1b42..89c9ba8 100644
|
||||||
|
--- a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
|
||||||
|
+++ b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
|
||||||
|
@@ -687,6 +687,7 @@ static NVSDK_NGX_Result TryCreateOptiFeature(ID3D12GraphicsCommandList* InCmdLis
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign handle
|
||||||
|
+ const bool allocatedHandle = *OutHandle == nullptr;
|
||||||
|
if (*OutHandle == nullptr)
|
||||||
|
*OutHandle = new NVSDK_NGX_Handle { handleId };
|
||||||
|
else
|
||||||
|
@@ -705,6 +706,16 @@ static NVSDK_NGX_Result TryCreateOptiFeature(ID3D12GraphicsCommandList* InCmdLis
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
+ if (state.dlssExternalBackend && upscalerBackend == Upscaler::DLSS)
|
||||||
|
+ {
|
||||||
|
+ LOG_ERROR("External DLSS initialization failed; refusing FSR fallback");
|
||||||
|
+ if (shouldRestoreSigs)
|
||||||
|
+ D3D12Hooks::RestoreRoot(InCmdList);
|
||||||
|
+ D3D12Hooks::SetRootSignatureTracking(true);
|
||||||
|
+ Dx12Contexts.erase(handleId);
|
||||||
|
+ if (allocatedHandle) { delete *OutHandle; *OutHandle = nullptr; }
|
||||||
|
+ return NVSDK_NGX_Result_Fail;
|
||||||
|
+ }
|
||||||
|
LOG_ERROR("Feature '{}' initialization failed falling back to FSR 2.1.2", UpscalerDisplayName(upscalerBackend));
|
||||||
|
state.newBackend = Upscaler::FSR21;
|
||||||
|
state.changeBackend[handleId] = true;
|
||||||
|
@@ -1027,6 +1038,12 @@ static NVSDK_NGX_Result TryEvaluateOptiFeature(ID3D12GraphicsCommandList* InCmdL
|
||||||
|
// Fallback to FSR 2.1.2 if feature failed to initialize and user didn't explicitly request it
|
||||||
|
if (!feature->IsInited() && cfg.Dx12Upscaler.value_or_default() != Upscaler::FSR21)
|
||||||
|
{
|
||||||
|
+ if (state.dlssExternalBackend && cfg.Dx12Upscaler.value_or_default() == Upscaler::DLSS)
|
||||||
|
+ {
|
||||||
|
+ LOG_ERROR("External DLSS feature is not initialized; refusing FSR fallback");
|
||||||
|
+ D3D12Hooks::SetRootSignatureTracking(true);
|
||||||
|
+ return NVSDK_NGX_Result_Fail;
|
||||||
|
+ }
|
||||||
|
LOG_WARN("Feature '{}' failed to initialize. Falling back to FSR 2.1.2", feature->Name());
|
||||||
|
ImGui::InsertNotification({ ImGuiToastType::Warning, 10000, "Falling back to FSR 2.1.2" });
|
||||||
|
|
||||||
|
diff --git a/OptiScaler/misc/IdentifyGpu.cpp b/OptiScaler/misc/IdentifyGpu.cpp
|
||||||
|
index 3877f9f..6c05904 100644
|
||||||
|
--- a/OptiScaler/misc/IdentifyGpu.cpp
|
||||||
|
+++ b/OptiScaler/misc/IdentifyGpu.cpp
|
||||||
|
@@ -8,6 +8,7 @@
|
||||||
|
#include <proxies/D3d12_Proxy.h>
|
||||||
|
#include "nvapi/NvApiTypes.h"
|
||||||
|
#include <hooks/Amdxc64_Hooks.h>
|
||||||
|
+#include <proxies/NVNGX_Proxy.h>
|
||||||
|
|
||||||
|
using Microsoft::WRL::ComPtr;
|
||||||
|
|
||||||
|
@@ -187,6 +188,25 @@ std::vector<GpuInformation> IdentifyGpu::checkGpuInfo()
|
||||||
|
|
||||||
|
for (auto& gpuInfo : localCachedInfo)
|
||||||
|
{
|
||||||
|
+ if (State::Instance().dlssExternalBackend && !gpuInfo.softwareAdapter)
|
||||||
|
+ {
|
||||||
|
+ // Query the configured D3D12 backend outside DLL_PROCESS_ATTACH.
|
||||||
|
+ // Preserve vendor/device/architecture data obtained from DXGI.
|
||||||
|
+ auto requirements = NVNGXProxy::D3D12_GetFeatureRequirements();
|
||||||
|
+ ComPtr<IDXGIAdapter1> physicalAdapter;
|
||||||
|
+ if (requirements && SUCCEEDED(factory->EnumAdapterByLuid(gpuInfo.luid, IID_PPV_ARGS(&physicalAdapter))))
|
||||||
|
+ {
|
||||||
|
+ NVSDK_NGX_FeatureDiscoveryInfo discovery {};
|
||||||
|
+ discovery.SDKVersion = NVSDK_NGX_Version_API;
|
||||||
|
+ discovery.FeatureID = NVSDK_NGX_Feature_SuperSampling;
|
||||||
|
+ NVSDK_NGX_FeatureRequirement supported {};
|
||||||
|
+ const auto result = requirements(physicalAdapter.Get(), &discovery, &supported);
|
||||||
|
+ gpuInfo.dlssCapable = result == NVSDK_NGX_Result_Success &&
|
||||||
|
+ supported.FeatureSupported == NVSDK_NGX_FeatureSupportResult_Supported;
|
||||||
|
+ LOG_INFO("External D3D12 DLSS backend: {} support={} result={:X}",
|
||||||
|
+ gpuInfo.name, gpuInfo.dlssCapable, (unsigned) result);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
if (gpuInfo.vendorId == VendorId::Nvidia)
|
||||||
|
{
|
||||||
|
queryNvapi(gpuInfo);
|
||||||
|
diff --git a/OptiScaler/proxies/NVNGX_Proxy.h b/OptiScaler/proxies/NVNGX_Proxy.h
|
||||||
|
index f6c08e5..7892009 100644
|
||||||
|
--- a/OptiScaler/proxies/NVNGX_Proxy.h
|
||||||
|
+++ b/OptiScaler/proxies/NVNGX_Proxy.h
|
||||||
|
@@ -500,7 +500,10 @@ class NVNGXProxy
|
||||||
|
{
|
||||||
|
LOG_INFO("Getting nvngx method addresses");
|
||||||
|
|
||||||
|
- HookNgxApi(_module.dll);
|
||||||
|
+ // The external bridge reports physical adapter support itself.
|
||||||
|
+ // Do not overwrite its unsupported result with NVIDIA spoofing.
|
||||||
|
+ if (!State::Instance().dlssExternalBackend)
|
||||||
|
+ HookNgxApi(_module.dll);
|
||||||
|
|
||||||
|
_module.D3D11_Init =
|
||||||
|
(PFN_D3D11_Init) KernelBaseProxy::GetProcAddress_()(_module.dll, "NVSDK_NGX_D3D11_Init");
|
||||||
|
diff --git a/OptiScaler/upscalers/FeatureProvider_Dx12.cpp b/OptiScaler/upscalers/FeatureProvider_Dx12.cpp
|
||||||
|
index d738498..1191b29 100644
|
||||||
|
--- a/OptiScaler/upscalers/FeatureProvider_Dx12.cpp
|
||||||
|
+++ b/OptiScaler/upscalers/FeatureProvider_Dx12.cpp
|
||||||
|
@@ -42,6 +42,11 @@ bool FeatureProvider_Dx12::GetFeature(Upscaler upscaler, UINT handleId, NVSDK_NG
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Upscaler::DLSS:
|
||||||
|
+ if (state.dlssExternalBackend && (!primaryGpu.dlssCapable || !state.NVNGX_DLSS_Path.has_value()))
|
||||||
|
+ {
|
||||||
|
+ LOG_ERROR("Configured external D3D12 DLSS backend is unavailable; refusing FSR fallback");
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
if (primaryGpu.dlssCapable && state.NVNGX_DLSS_Path.has_value())
|
||||||
|
{
|
||||||
|
*feature = std::make_unique<DLSSFeatureDx12>(handleId, parameters);
|
||||||
|
@@ -77,6 +82,11 @@ bool FeatureProvider_Dx12::GetFeature(Upscaler upscaler, UINT handleId, NVSDK_NG
|
||||||
|
|
||||||
|
if (!loaded)
|
||||||
|
{
|
||||||
|
+ if (state.dlssExternalBackend && upscaler == Upscaler::DLSS)
|
||||||
|
+ {
|
||||||
|
+ LOG_ERROR("External D3D12 DLSS module failed to load; refusing FSR fallback");
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
// Fail after the constructor
|
||||||
|
ImGui::InsertNotification({ ImGuiToastType::Warning, 10000, "Falling back to FSR 2.1.2" });
|
||||||
|
*feature = std::make_unique<FSR2FeatureDx12_212>(handleId, parameters);
|
||||||
|
--
|
||||||
|
2.55.0.windows.5
|
||||||
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
From 7ed1f1d66b430ba35a197fbd9880cd6a76af8f06 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Codex <codex@localhost>
|
||||||
|
Date: Thu, 1 Oct 2026 16:45:01 +0300
|
||||||
|
Subject: [PATCH] Install the external d4r command backend before NGX
|
||||||
|
initialization
|
||||||
|
|
||||||
|
---
|
||||||
|
OptiScaler/hooks/D3D12_Hooks.cpp | 14 ++++++++++++++
|
||||||
|
1 file changed, 14 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/OptiScaler/hooks/D3D12_Hooks.cpp b/OptiScaler/hooks/D3D12_Hooks.cpp
|
||||||
|
index 29c0bec..3b757be 100644
|
||||||
|
--- a/OptiScaler/hooks/D3D12_Hooks.cpp
|
||||||
|
+++ b/OptiScaler/hooks/D3D12_Hooks.cpp
|
||||||
|
@@ -14,6 +14,7 @@
|
||||||
|
#include <proxies/IGDExt_Proxy.h>
|
||||||
|
#include <proxies/Streamline_Proxy.h>
|
||||||
|
#include <proxies/KernelBase_Proxy.h>
|
||||||
|
+#include <proxies/NVNGX_Proxy.h>
|
||||||
|
|
||||||
|
#include <detours/detours.h>
|
||||||
|
|
||||||
|
@@ -2230,6 +2231,19 @@ static void HookToDevice(ID3D12Device* InDevice)
|
||||||
|
if (Util::CheckForRealObject(__FUNCTION__, InDevice, (IUnknown**) &realDevice))
|
||||||
|
pVTable = *(PVOID**) realDevice;
|
||||||
|
|
||||||
|
+ if (State::Instance().dlssExternalBackend)
|
||||||
|
+ {
|
||||||
|
+ // Capture command signatures/resources created before the game's NGX
|
||||||
|
+ // Init. Install before adding OptiScaler's own method trampolines.
|
||||||
|
+ using InstallCommandBackend = unsigned (*)(ID3D12Device*);
|
||||||
|
+ auto install = reinterpret_cast<InstallCommandBackend>(KernelBaseProxy::GetProcAddress_()(
|
||||||
|
+ NVNGXProxy::NVNGXModule(), "d4r_D3D12_InstallCommandBackend"));
|
||||||
|
+ auto result = install ? install(realDevice ? realDevice : InDevice) : 0;
|
||||||
|
+ LOG_INFO("External d4r command backend installed at device discovery: result={:X}", result);
|
||||||
|
+ if (result != 1)
|
||||||
|
+ LOG_ERROR("External d4r early command tracking failed; DLSS initialization must be checked");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
// hudless
|
||||||
|
o_D3D12DeviceRelease = (PFN_Release) pVTable[2];
|
||||||
|
o_CreateSampler = (PFN_CreateSampler) pVTable[22];
|
||||||
|
--
|
||||||
|
2.55.0.windows.5
|
||||||
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
From 09488c47855aadbf85f68ab8c72c0f3ac5f915e9 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Codex <codex@localhost>
|
||||||
|
Date: Thu, 1 Oct 2026 17:01:32 +0300
|
||||||
|
Subject: [PATCH] Preserve legacy ProjectID initialization in the external
|
||||||
|
backend
|
||||||
|
|
||||||
|
---
|
||||||
|
OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp | 6 ++++++
|
||||||
|
1 file changed, 6 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
|
||||||
|
index 89c9ba8..1f723f1 100644
|
||||||
|
--- a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
|
||||||
|
+++ b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
|
||||||
|
@@ -313,6 +313,8 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_Init_ProjectID(const char* InProj
|
||||||
|
|
||||||
|
if (result == NVSDK_NGX_Result_Success)
|
||||||
|
NVNGXProxy::SetDx12Inited(true);
|
||||||
|
+ else if (State::Instance().dlssExternalBackend)
|
||||||
|
+ return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -343,6 +345,10 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_Init_with_ProjectID(
|
||||||
|
{
|
||||||
|
LOG_FUNC();
|
||||||
|
|
||||||
|
+ if (State::Instance().dlssExternalBackend)
|
||||||
|
+ return NVSDK_NGX_D3D12_Init_ProjectID(InProjectId, InEngineType, InEngineVersion,
|
||||||
|
+ InApplicationDataPath, InDevice, InSDKVersion, InFeatureInfo);
|
||||||
|
+
|
||||||
|
LOG_INFO("InProjectId: {0}", InProjectId);
|
||||||
|
LOG_INFO("InEngineType: {0}", (int) InEngineType);
|
||||||
|
LOG_INFO("InEngineVersion: {0}", InEngineVersion);
|
||||||
|
--
|
||||||
|
2.55.0.windows.5
|
||||||
|
|
||||||
@@ -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"));
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# Explicit Windows d4r backend
|
||||||
|
|
||||||
|
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.
|
||||||
|
No NVIDIA DLL is supplied or patched.
|
||||||
|
|
||||||
|
`0001` introduces `[DLSS] AllowExternalBackend=true`, default false. Set
|
||||||
|
`[Libraries] NvngxPath` to the absolute directory containing the Windows d4r
|
||||||
|
shim as `_nvngx.dll`. Discovery requires its GPU-free ABI-1 marker. Physical
|
||||||
|
adapter support is queried through the bridge's D3D12 requirements API after
|
||||||
|
DLL startup; vendor, LUID and HIP architecture are retained.
|
||||||
|
|
||||||
|
The configured `D4R_NGX_CORE` absolute path bypasses game-facing NGX DLL
|
||||||
|
redirection. The external backend loads its own NVAPI provider. An explicitly
|
||||||
|
selected external DLSS backend reports initialization/load errors instead of
|
||||||
|
silently switching to FSR. The patch fixes the upstream loader's filesystem
|
||||||
|
exception by making IsSubpath a lexical check, and retains a PDB for diagnosis.
|
||||||
|
|
||||||
|
`0002` installs the command backend at D3D12 device discovery, before NGX Init
|
||||||
|
and before OptiScaler's own method hooks. Command signatures created during
|
||||||
|
game startup therefore retain their public argument descriptions for replay.
|
||||||
|
The standalone runner's `-EarlyIndirectProbe` exercises this startup order.
|
||||||
|
|
||||||
|
`0003` preserves the Project ID and engine identity through OptiScaler's
|
||||||
|
older `Init_with_ProjectID` entry point used by its XeSS/FSR inputs. It also
|
||||||
|
propagates an explicit external backend initialization failure. Reproduce
|
||||||
|
with the D3D12 runner's `-NgxAbi project-legacy`.
|
||||||
|
|
||||||
|
Build with Microsoft Build Tools 2022, v143 and Windows SDK 10.0.26100:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\scripts\windows\build-optiscaler-windows.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
The script clones the pinned source if absent, checks/applies the patch,
|
||||||
|
initializes pinned submodules, invokes MSBuild and writes the DLL, PDB, license
|
||||||
|
and build metadata into `dist/optiscaler-windows-d4r`. Existing source is not
|
||||||
|
reset. The upstream packaging post-build event is disabled for this build.
|
||||||
|
The official bootstrapper is https://aka.ms/vs/17/release/vs_buildtools.exe;
|
||||||
|
install the `Microsoft.VisualStudio.Workload.VCTools`,
|
||||||
|
`Microsoft.VisualStudio.Component.VC.Tools.x86.x64` and
|
||||||
|
`Microsoft.VisualStudio.Component.Windows11SDK.26100` components. The audited
|
||||||
|
local build uses MSVC 14.44.35207 / Build Tools 17.14.
|
||||||
|
|
||||||
|
For standalone verification add
|
||||||
|
`-OptiScalerDll <absolute-dist-path>/OptiScaler.dll -CaptureExceptions`
|
||||||
|
to the documented K/M runner. Its private INI selects DLSS, disables frame
|
||||||
|
generation and keeps RestoreComputeSignature, RestoreGraphicSignature and
|
||||||
|
ExtendedStateRestore false. Use `-RequireNativeNetwork`; finite output alone
|
||||||
|
cannot distinguish DLSS from an unwanted FSR replacement.
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
From: d4r Windows port
|
||||||
|
Subject: [PATCH] Backport AMD external-memory view ownership fix to ROCm 7.2
|
||||||
|
|
||||||
|
Upstream fix: ROCm/clr 529f6b1641de436dafbb095d5438b0fbf773765d
|
||||||
|
(ROCm/rocm-systems #3528). Already present in current upstream.
|
||||||
|
The constructor owns reference 1. MemObjMap does not retain the view;
|
||||||
|
hipFree removes the entry and releases reference 1. The extra retain
|
||||||
|
leaks the view and its parent imported allocation.
|
||||||
|
|
||||||
|
Hardware regression: d4r_d3d12_hip_interop_probe --interop-mode map
|
||||||
|
--iterations 64. Windows SDK 7.2.60201 leaks one HANDLE / 256 KiB each
|
||||||
|
cycle; tested TheRock 10.2.0a20260929 (HIP 7.17.26386) stays bounded.
|
||||||
|
This patch is for rebuilding old runtime sources, never binary patching.
|
||||||
|
|
||||||
|
diff --git a/hipamd/src/hip_memory.cpp b/hipamd/src/hip_memory.cpp
|
||||||
|
--- a/hipamd/src/hip_memory.cpp
|
||||||
|
+++ b/hipamd/src/hip_memory.cpp
|
||||||
|
@@ -179,7 +179,6 @@ hipError_t hipExternalMemoryGetMappedBuffer(void** devPtr, hipExternalMemory_t extMem,
|
||||||
|
// Map the device memory to the user pointer
|
||||||
|
*devPtr = reinterpret_cast<void*>(devMem->virtualAddress());
|
||||||
|
amd::MemObjMap::AddMemObj(*devPtr, view);
|
||||||
|
- view->retain();
|
||||||
|
|
||||||
|
HIP_RETURN(hipSuccess);
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
diff --git a/include/vkd3d_device_vkd3d_ext.idl b/include/vkd3d_device_vkd3d_ext.idl
|
||||||
|
--- a/include/vkd3d_device_vkd3d_ext.idl
|
||||||
|
+++ b/include/vkd3d_device_vkd3d_ext.idl
|
||||||
|
@@ -120,6 +120,19 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
[
|
||||||
|
+ uuid(4f8c9462-8197-4dc1-a584-cf44d9706db2),
|
||||||
|
+ object,
|
||||||
|
+ local,
|
||||||
|
+ pointer_default(unique)
|
||||||
|
+]
|
||||||
|
+interface ID3D12DXVKInteropDeviceD4R2 : ID3D12DXVKInteropDeviceD4R
|
||||||
|
+{
|
||||||
|
+ /* Retain external resources until the allocator can safely reset or is
|
||||||
|
+ * destroyed. Pending submissions already retain the allocator. */
|
||||||
|
+ HRESULT RetainExternalResources(ID3D12CommandList *list, IUnknown *resources);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+[
|
||||||
|
uuid(f3112584-41f9-348d-a59b-00b7e1d285d6),
|
||||||
|
object,
|
||||||
|
local,
|
||||||
|
diff --git a/libs/vkd3d/command.c b/libs/vkd3d/command.c
|
||||||
|
--- a/libs/vkd3d/command.c
|
||||||
|
+++ b/libs/vkd3d/command.c
|
||||||
|
@@ -2791,6 +2791,24 @@
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+HRESULT d3d12_command_list_retain_external_resources(struct d3d12_command_list *list, IUnknown *resources)
|
||||||
|
+{
|
||||||
|
+ struct d3d12_command_allocator *allocator = list->allocator;
|
||||||
|
+ size_t i;
|
||||||
|
+
|
||||||
|
+ if (!list->is_recording || !allocator)
|
||||||
|
+ return E_INVALIDARG;
|
||||||
|
+ for (i = 0; i < allocator->external_resources_count; i++)
|
||||||
|
+ if (allocator->external_resources[i] == resources)
|
||||||
|
+ return S_OK;
|
||||||
|
+ if (!vkd3d_array_reserve((void **)&allocator->external_resources, &allocator->external_resources_size,
|
||||||
|
+ allocator->external_resources_count + 1, sizeof(*allocator->external_resources)))
|
||||||
|
+ return E_OUTOFMEMORY;
|
||||||
|
+ IUnknown_AddRef(resources);
|
||||||
|
+ allocator->external_resources[allocator->external_resources_count++] = resources;
|
||||||
|
+ return S_OK;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static bool d3d12_command_allocator_add_view(struct d3d12_command_allocator *allocator,
|
||||||
|
struct vkd3d_view *view)
|
||||||
|
{
|
||||||
|
@@ -2858,6 +2876,10 @@
|
||||||
|
struct d3d12_device *device = allocator->device;
|
||||||
|
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
|
||||||
|
unsigned int i;
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < allocator->external_resources_count; ++i)
|
||||||
|
+ IUnknown_Release(allocator->external_resources[i]);
|
||||||
|
+ allocator->external_resources_count = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < allocator->buffer_view_count; ++i)
|
||||||
|
{
|
||||||
|
@@ -2993,6 +3015,7 @@
|
||||||
|
d3d12_command_list_allocator_destroyed(allocator->current_command_list);
|
||||||
|
|
||||||
|
d3d12_command_allocator_free_resources(allocator);
|
||||||
|
+ vkd3d_free(allocator->external_resources);
|
||||||
|
vkd3d_free(allocator->buffer_views);
|
||||||
|
vkd3d_free(allocator->views);
|
||||||
|
vkd3d_free(allocator->pipelines);
|
||||||
|
diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c
|
||||||
|
--- a/libs/vkd3d/device.c
|
||||||
|
+++ b/libs/vkd3d/device.c
|
||||||
|
@@ -4658,7 +4658,8 @@
|
||||||
|
|| IsEqualGUID(riid, &IID_ID3D12DXVKInteropDevice1)
|
||||||
|
|| IsEqualGUID(riid, &IID_ID3D12DXVKInteropDevice2)
|
||||||
|
|| IsEqualGUID(riid, &IID_ID3D12DXVKInteropDevice3)
|
||||||
|
- || IsEqualGUID(riid, &IID_ID3D12DXVKInteropDeviceD4R))
|
||||||
|
+ || IsEqualGUID(riid, &IID_ID3D12DXVKInteropDeviceD4R)
|
||||||
|
+ || IsEqualGUID(riid, &IID_ID3D12DXVKInteropDeviceD4R2))
|
||||||
|
{
|
||||||
|
d3d12_dxvk_interop_device_AddRef(&device->ID3D12DXVKInteropDevice_iface);
|
||||||
|
*object = &device->ID3D12DXVKInteropDevice_iface;
|
||||||
|
@@ -11380,7 +11381,7 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
extern CONST_VTBL struct ID3D12DeviceExt5Vtbl d3d12_device_vkd3d_ext_vtbl;
|
||||||
|
-extern CONST_VTBL struct ID3D12DXVKInteropDeviceD4RVtbl d3d12_dxvk_interop_device_vtbl;
|
||||||
|
+extern CONST_VTBL struct ID3D12DXVKInteropDeviceD4R2Vtbl d3d12_dxvk_interop_device_vtbl;
|
||||||
|
extern CONST_VTBL struct ID3DLowLatencyDeviceVtbl d3d_low_latency_device_vtbl;
|
||||||
|
extern CONST_VTBL struct IAmdExtAntiLagApiVtbl d3d_amd_ext_anti_lag_vtbl;
|
||||||
|
|
||||||
|
diff --git a/libs/vkd3d/device_vkd3d_ext.c b/libs/vkd3d/device_vkd3d_ext.c
|
||||||
|
--- a/libs/vkd3d/device_vkd3d_ext.c
|
||||||
|
+++ b/libs/vkd3d/device_vkd3d_ext.c
|
||||||
|
@@ -1157,7 +1157,16 @@
|
||||||
|
return d3d12_command_list_split_for_external_wait(cmd_list, (VkSemaphore)vk_semaphore, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
-CONST_VTBL struct ID3D12DXVKInteropDeviceD4RVtbl d3d12_dxvk_interop_device_vtbl =
|
||||||
|
+static HRESULT STDMETHODCALLTYPE d3d12_dxvk_interop_device_RetainExternalResources(
|
||||||
|
+ d3d12_dxvk_interop_device_iface *iface, ID3D12CommandList *list, IUnknown *resources)
|
||||||
|
+{
|
||||||
|
+ struct d3d12_command_list *cmd_list = d3d12_command_list_from_iface(list);
|
||||||
|
+ if (!cmd_list || !resources)
|
||||||
|
+ return E_INVALIDARG;
|
||||||
|
+ return d3d12_command_list_retain_external_resources(cmd_list, resources);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+CONST_VTBL struct ID3D12DXVKInteropDeviceD4R2Vtbl d3d12_dxvk_interop_device_vtbl =
|
||||||
|
{
|
||||||
|
/* IUnknown methods */
|
||||||
|
d3d12_dxvk_interop_device_QueryInterface,
|
||||||
|
@@ -1192,6 +1201,9 @@
|
||||||
|
|
||||||
|
/* ID3D12DXVKInteropDeviceD4R methods */
|
||||||
|
d3d12_dxvk_interop_device_SplitCommandListForExternalWait,
|
||||||
|
+
|
||||||
|
+ /* ID3D12DXVKInteropDeviceD4R2 methods */
|
||||||
|
+ d3d12_dxvk_interop_device_RetainExternalResources,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct d3d12_device *d3d12_device_from_ID3DLowLatencyDevice(d3d_low_latency_device_iface *iface)
|
||||||
|
diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h
|
||||||
|
--- a/libs/vkd3d/vkd3d_private.h
|
||||||
|
+++ b/libs/vkd3d/vkd3d_private.h
|
||||||
|
@@ -2758,6 +2758,10 @@
|
||||||
|
LONG refcount;
|
||||||
|
LONG internal_refcount;
|
||||||
|
|
||||||
|
+ IUnknown **external_resources;
|
||||||
|
+ size_t external_resources_size;
|
||||||
|
+ size_t external_resources_count;
|
||||||
|
+
|
||||||
|
D3D12_COMMAND_LIST_TYPE type;
|
||||||
|
|
||||||
|
struct vkd3d_view **views;
|
||||||
|
@@ -5474,7 +5478,9 @@
|
||||||
|
HRESULT d3d12_command_list_split_for_external_wait(struct d3d12_command_list *list,
|
||||||
|
VkSemaphore semaphore, uint64_t value);
|
||||||
|
|
||||||
|
-typedef ID3D12DXVKInteropDeviceD4R d3d12_dxvk_interop_device_iface;
|
||||||
|
+HRESULT d3d12_command_list_retain_external_resources(struct d3d12_command_list *list, IUnknown *resources);
|
||||||
|
+
|
||||||
|
+typedef ID3D12DXVKInteropDeviceD4R2 d3d12_dxvk_interop_device_iface;
|
||||||
|
|
||||||
|
/* ID3DLowLatencyDevice */
|
||||||
|
typedef ID3DLowLatencyDevice d3d_low_latency_device_iface;
|
||||||
@@ -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"), \
|
||||||
@@ -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,203 @@
|
|||||||
|
Windows LLVM-MinGW build, portable helper assertions and optional diagnostic UI.
|
||||||
|
Apply after upstream d4r patches 0002 through 0007, on ZLUDA ee2f25a.
|
||||||
|
|
||||||
|
diff --git a/ext/highs-sys/build.rs b/ext/highs-sys/build.rs
|
||||||
|
--- a/ext/highs-sys/build.rs
|
||||||
|
+++ b/ext/highs-sys/build.rs
|
||||||
|
@@ -66,11 +66,12 @@
|
||||||
|
let linux = target.contains("linux");
|
||||||
|
let mingw = target.contains("pc-windows-gnu");
|
||||||
|
if apple {
|
||||||
|
println!("cargo:rustc-link-lib=dylib=c++");
|
||||||
|
} else if linux || mingw {
|
||||||
|
- println!("cargo:rustc-link-lib=dylib=stdc++");
|
||||||
|
+ let cxx_library = env::var("CXXSTDLIB").unwrap_or_else(|_| "stdc++".to_owned());
|
||||||
|
+ println!("cargo:rustc-link-lib=dylib={cxx_library}");
|
||||||
|
}
|
||||||
|
println!("cargo:rerun-if-changed=../HiGHS/highs/interfaces/highs_c_api.h");
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
diff --git a/ext/hip_runtime-sys/build.rs b/ext/hip_runtime-sys/build.rs
|
||||||
|
--- a/ext/hip_runtime-sys/build.rs
|
||||||
|
+++ b/ext/hip_runtime-sys/build.rs
|
||||||
|
@@ -1,8 +1,12 @@
|
||||||
|
fn main() {
|
||||||
|
if cfg!(windows) {
|
||||||
|
- println!("cargo:rustc-link-arg=/DELAYLOAD:amdhip64_7.dll");
|
||||||
|
+ if std::env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("gnu") {
|
||||||
|
+ println!("cargo:rustc-link-arg=-Wl,--delayload=amdhip64_7.dll");
|
||||||
|
+ } else {
|
||||||
|
+ println!("cargo:rustc-link-arg=/DELAYLOAD:amdhip64_7.dll");
|
||||||
|
+ }
|
||||||
|
} else {
|
||||||
|
println!("cargo:rustc-link-lib=dylib=amdhip64");
|
||||||
|
println!("cargo:rustc-link-search=native=/opt/rocm/lib/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diff --git a/llvm_zluda/build.rs b/llvm_zluda/build.rs
|
||||||
|
--- a/llvm_zluda/build.rs
|
||||||
|
+++ b/llvm_zluda/build.rs
|
||||||
|
@@ -19,10 +19,18 @@
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let bc_path1 = "src/device-libs/ockl.bc";
|
||||||
|
println!("cargo:rerun-if-changed={}", bc_path1);
|
||||||
|
check_lfs_file(bc_path1);
|
||||||
|
+
|
||||||
|
+ println!("cargo:rerun-if-env-changed=ZLUDA_LLVM_BUILD_DIR");
|
||||||
|
+ if let Some(directory) = env::var_os("ZLUDA_LLVM_BUILD_DIR") {
|
||||||
|
+ let llvm_build_path = PathBuf::from(directory);
|
||||||
|
+ println!("cargo:build-path={}", llvm_build_path.display());
|
||||||
|
+ link_prebuilt_llvm(&llvm_build_path);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
let mut cmake = Config::new(r"../ext/llvm-project/llvm");
|
||||||
|
try_use_sccache(&mut cmake);
|
||||||
|
try_use_ninja(&mut cmake);
|
||||||
|
cmake
|
||||||
|
@@ -57,16 +65,31 @@
|
||||||
|
cmake.build();
|
||||||
|
}
|
||||||
|
let cmake_profile = cmake.get_profile();
|
||||||
|
let llvm_build_path = get_llvm_build_path(&llvm_dir, cmake_profile);
|
||||||
|
println!("cargo:build-path={}", llvm_build_path.display());
|
||||||
|
+ link_prebuilt_llvm(&llvm_build_path);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+fn link_prebuilt_llvm(llvm_build_path: &PathBuf) {
|
||||||
|
let (cxxflags, ldflags, libdir, lib_names, system_libs) =
|
||||||
|
llvm_config(&llvm_build_path.join("bin/llvm-config")).unwrap();
|
||||||
|
println!("cargo:rustc-link-arg={ldflags}");
|
||||||
|
println!("cargo:rustc-link-search=native={libdir}");
|
||||||
|
for lib in system_libs.split_ascii_whitespace() {
|
||||||
|
- println!("cargo:rustc-link-arg={lib}");
|
||||||
|
+ if let Some(name) = lib.strip_prefix("-l") {
|
||||||
|
+ println!("cargo:rustc-link-lib={name}");
|
||||||
|
+ } else {
|
||||||
|
+ println!("cargo:rustc-link-arg={lib}");
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ // llvm-config omits system libraries on Windows. Propagate these through
|
||||||
|
+ // this rlib to its final cdylib consumer (link-arg alone cannot do that).
|
||||||
|
+ if env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") {
|
||||||
|
+ for library in ["psapi", "shell32", "ole32", "uuid", "advapi32", "ws2_32", "ntdll"] {
|
||||||
|
+ println!("cargo:rustc-link-lib={library}");
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
link_lld_components();
|
||||||
|
link_llvm_components(lib_names);
|
||||||
|
compile_cxx_lib(cxxflags, &llvm_build_path);
|
||||||
|
}
|
||||||
|
diff --git a/zluda/build.rs b/zluda/build.rs
|
||||||
|
--- a/zluda/build.rs
|
||||||
|
+++ b/zluda/build.rs
|
||||||
|
@@ -1,11 +1,15 @@
|
||||||
|
use vergen_gix::{Emitter, Gix};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if cfg!(windows) {
|
||||||
|
- println!("cargo:rustc-link-arg=delayimp.lib");
|
||||||
|
- println!("cargo:rustc-link-arg=/DELAYLOAD:amdhip64_7.dll");
|
||||||
|
+ if std::env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("gnu") {
|
||||||
|
+ println!("cargo:rustc-link-arg=-Wl,--delayload=amdhip64_7.dll");
|
||||||
|
+ } else {
|
||||||
|
+ println!("cargo:rustc-link-arg=delayimp.lib");
|
||||||
|
+ println!("cargo:rustc-link-arg=/DELAYLOAD:amdhip64_7.dll");
|
||||||
|
+ }
|
||||||
|
let dll_path = "bin/nvcudart_hybrid64.dll";
|
||||||
|
println!("cargo:rerun-if-changed={}", dll_path);
|
||||||
|
check_lfs_file(dll_path);
|
||||||
|
}
|
||||||
|
let git = Gix::builder().sha(false).build();
|
||||||
|
diff --git a/zluda_windows/src/lib.rs b/zluda_windows/src/lib.rs
|
||||||
|
--- a/zluda_windows/src/lib.rs
|
||||||
|
+++ b/zluda_windows/src/lib.rs
|
||||||
|
@@ -11,27 +11,27 @@
|
||||||
|
use uuid::uuid;
|
||||||
|
use widestring::{u16str, U16Str, U16String};
|
||||||
|
use windows::{
|
||||||
|
core::{w, HRESULT, PCWSTR},
|
||||||
|
Win32::{
|
||||||
|
- Foundation::{HANDLE, HINSTANCE, HMODULE, HWND, LPARAM, S_OK, WPARAM},
|
||||||
|
+ Foundation::{FreeLibrary, HANDLE, HINSTANCE, HMODULE, HWND, LPARAM, S_OK, WPARAM},
|
||||||
|
System::{
|
||||||
|
JobObjects::{
|
||||||
|
AssignProcessToJobObject, JobObjectExtendedLimitInformation,
|
||||||
|
SetInformationJobObject, JOBOBJECT_EXTENDED_LIMIT_INFORMATION,
|
||||||
|
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
|
||||||
|
},
|
||||||
|
LibraryLoader::{
|
||||||
|
- GetModuleFileNameA, GetModuleFileNameW, GetModuleHandleExW, LoadLibraryExW,
|
||||||
|
+ GetModuleFileNameA, GetModuleFileNameW, GetModuleHandleExW, GetProcAddress, LoadLibraryExW,
|
||||||
|
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
|
||||||
|
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS,
|
||||||
|
- LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR,
|
||||||
|
+ LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR, LOAD_LIBRARY_SEARCH_SYSTEM32,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UI::{
|
||||||
|
Controls::{
|
||||||
|
- TaskDialogIndirect, TASKDIALOGCONFIG, TASKDIALOGCONFIG_0, TASKDIALOGCONFIG_1,
|
||||||
|
+ TASKDIALOGCONFIG, TASKDIALOGCONFIG_0, TASKDIALOGCONFIG_1,
|
||||||
|
TASKDIALOG_NOTIFICATIONS, TDCBF_CLOSE_BUTTON, TDF_ENABLE_HYPERLINKS,
|
||||||
|
TDN_HYPERLINK_CLICKED, TD_ERROR_ICON,
|
||||||
|
},
|
||||||
|
WindowsAndMessaging::IDCLOSE,
|
||||||
|
},
|
||||||
|
@@ -487,11 +487,23 @@
|
||||||
|
pszFooter: PCWSTR::null(),
|
||||||
|
pfCallback: Some(task_dialog_callback),
|
||||||
|
lpCallbackData: 0,
|
||||||
|
cxWidth: 0,
|
||||||
|
};
|
||||||
|
- TaskDialogIndirect(&config, None, None, None).ok();
|
||||||
|
+ // A CUDA DLL may be loaded by an executable with no Common Controls
|
||||||
|
+ // v6 activation manifest. Do not make its diagnostic UI an eager DLL
|
||||||
|
+ // import: comctl32 v5 has no TaskDialogIndirect export.
|
||||||
|
+ if let Ok(controls) = LoadLibraryExW(w!("comctl32.dll"), None, LOAD_LIBRARY_SEARCH_SYSTEM32) {
|
||||||
|
+ if let Some(raw) = GetProcAddress(controls, windows::core::s!("TaskDialogIndirect")) {
|
||||||
|
+ let show: unsafe extern "system" fn(*const TASKDIALOGCONFIG, *mut i32, *mut i32, *mut i32) -> HRESULT =
|
||||||
|
+ mem::transmute(raw);
|
||||||
|
+ let _ = show(&config, ptr::null_mut(), ptr::null_mut(), ptr::null_mut());
|
||||||
|
+ } else {
|
||||||
|
+ eprintln!("ZLUDA failed to load {}; set HIP_PATH to the selected HIP SDK", redirect_names[0]);
|
||||||
|
+ }
|
||||||
|
+ let _ = FreeLibrary(controls);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn task_dialog_callback(
|
||||||
|
diff --git a/ptx/lib/zluda_ptx_impl.cpp b/ptx/lib/zluda_ptx_impl.cpp
|
||||||
|
--- a/ptx/lib/zluda_ptx_impl.cpp
|
||||||
|
+++ b/ptx/lib/zluda_ptx_impl.cpp
|
||||||
|
@@ -405,11 +405,26 @@
|
||||||
|
uint32_t line,
|
||||||
|
uint64_t function,
|
||||||
|
uint64_t char_size)
|
||||||
|
{
|
||||||
|
(void)char_size;
|
||||||
|
+#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
+ // HIP on Windows only supplies the wide _wassert entry point. Keep the
|
||||||
|
+ // CUDA assertion's ASCII diagnostics and trap through public OCKL APIs.
|
||||||
|
+ auto output = __ockl_fprintf_stderr_begin();
|
||||||
|
+ const char* parts[] = {"CUDA assertion: ", (const char*)message, " at ", (const char*)file,
|
||||||
|
+ " in ", (const char*)function, "\n"};
|
||||||
|
+ for (const char* part : parts) {
|
||||||
|
+ const char* end = part;
|
||||||
|
+ while (*end) ++end;
|
||||||
|
+ output = __ockl_fprintf_append_string_n(output, part, end - part, 0);
|
||||||
|
+ }
|
||||||
|
+ __ockl_fprintf_append_args(output, 1, line, 0, 0, 0, 0, 0, 0, 1);
|
||||||
|
+ __builtin_trap();
|
||||||
|
+#else
|
||||||
|
[[clang::noinline]] __assert_fail((const char *)message, (const char *)file, line, (const char *)function);
|
||||||
|
+#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// * Smallest denormal is 1.4 × 10^-45
|
||||||
|
// * Smallest normal is ~1.175494351 × 10^(-38)
|
||||||
|
// * Now, 1.175494351×10^-38 / 1.4 × 10^-45 = 8396388 + 31/140
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
Implement the CUDA texture resource descriptor query required by NGX Evaluate.
|
||||||
|
Regression: tools/windows/cuda_image_probe.cpp in the d4r Windows port.
|
||||||
|
|
||||||
|
diff --git a/zluda/src/lib.rs b/zluda/src/lib.rs
|
||||||
|
--- a/zluda/src/lib.rs
|
||||||
|
+++ b/zluda/src/lib.rs
|
||||||
|
@@ -67,12 +67,14 @@
|
||||||
|
cuda_macros::cuda_function_declarations!(
|
||||||
|
unimplemented,
|
||||||
|
implemented
|
||||||
|
<= [
|
||||||
|
cuArray3DCreate_v2,
|
||||||
|
+ cuArray3DGetDescriptor_v2,
|
||||||
|
cuArrayCreate_v2,
|
||||||
|
cuArrayDestroy,
|
||||||
|
+ cuArrayGetDescriptor_v2,
|
||||||
|
cuCtxCreate_v2,
|
||||||
|
cuCtxCreate_v4,
|
||||||
|
cuCtxDestroy_v2,
|
||||||
|
cuCtxGetApiVersion,
|
||||||
|
cuCtxGetCurrent,
|
||||||
|
@@ -203,10 +205,11 @@
|
||||||
|
cuSurfObjectCreate,
|
||||||
|
cuSurfObjectDestroy,
|
||||||
|
cuSurfObjectGetResourceDesc,
|
||||||
|
cuTexObjectCreate,
|
||||||
|
cuTexObjectDestroy,
|
||||||
|
+ cuTexObjectGetResourceDesc,
|
||||||
|
cuTexRefSetAddress_v2,
|
||||||
|
cuTexRefSetAddressMode,
|
||||||
|
cuTexRefSetArray,
|
||||||
|
cuTexRefSetFilterMode,
|
||||||
|
cuTexRefSetFlags,
|
||||||
|
diff --git a/zluda/src/impl/tex.rs b/zluda/src/impl/tex.rs
|
||||||
|
--- a/zluda/src/impl/tex.rs
|
||||||
|
+++ b/zluda/src/impl/tex.rs
|
||||||
|
@@ -1,19 +1,58 @@
|
||||||
|
use crate::r#impl::hipfix;
|
||||||
|
use hip_runtime_sys::*;
|
||||||
|
+use std::collections::HashMap;
|
||||||
|
+use std::sync::{Mutex, OnceLock};
|
||||||
|
+
|
||||||
|
+fn object_resources() -> &'static Mutex<HashMap<usize, Vec<u8>>> {
|
||||||
|
+ static RESOURCES: OnceLock<Mutex<HashMap<usize, Vec<u8>>>> = OnceLock::new();
|
||||||
|
+ RESOURCES.get_or_init(|| Mutex::new(HashMap::new()))
|
||||||
|
+}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn object_create(
|
||||||
|
p_tex_object: *mut hipTextureObject_t,
|
||||||
|
p_res_desc: *const HIP_RESOURCE_DESC,
|
||||||
|
p_tex_desc: *const HIP_TEXTURE_DESC,
|
||||||
|
p_res_view_desc: *const HIP_RESOURCE_VIEW_DESC,
|
||||||
|
) -> hipError_t {
|
||||||
|
- hipTexObjectCreate(p_tex_object, p_res_desc, p_tex_desc, p_res_view_desc)
|
||||||
|
+ let output = p_tex_object.as_mut().ok_or(hipErrorCode_t::InvalidValue)?;
|
||||||
|
+ let descriptor = p_res_desc.as_ref().ok_or(hipErrorCode_t::InvalidValue)?;
|
||||||
|
+ hipTexObjectCreate(output, descriptor, p_tex_desc, p_res_view_desc)?;
|
||||||
|
+ let bytes = std::slice::from_raw_parts(p_res_desc.cast::<u8>(), std::mem::size_of::<HIP_RESOURCE_DESC>()).to_vec();
|
||||||
|
+ match object_resources().lock() {
|
||||||
|
+ Ok(mut resources) => {
|
||||||
|
+ resources.insert(*output as usize, bytes);
|
||||||
|
+ Ok(())
|
||||||
|
+ }
|
||||||
|
+ Err(_) => {
|
||||||
|
+ let _ = hipDestroyTextureObject(*output);
|
||||||
|
+ *output = std::ptr::null_mut();
|
||||||
|
+ Err(hipErrorCode_t::Unknown)
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn object_destroy(tex_object: hipTextureObject_t) -> hipError_t {
|
||||||
|
- hipDestroyTextureObject(tex_object)
|
||||||
|
+ let mut resources = object_resources().lock().map_err(|_| hipErrorCode_t::Unknown)?;
|
||||||
|
+ if !resources.contains_key(&(tex_object as usize)) { return Err(hipErrorCode_t::InvalidValue); }
|
||||||
|
+ hipDestroyTextureObject(tex_object)?;
|
||||||
|
+ resources.remove(&(tex_object as usize));
|
||||||
|
+ Ok(())
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+pub(crate) unsafe fn object_get_resource_desc(
|
||||||
|
+ descriptor: &mut HIP_RESOURCE_DESC,
|
||||||
|
+ tex_object: hipTextureObject_t,
|
||||||
|
+) -> hipError_t {
|
||||||
|
+ // NGX probes surface handles as textures before trying the surface API.
|
||||||
|
+ // Passing that foreign handle to HIP dereferences invalid host metadata.
|
||||||
|
+ // Return a CUDA error for unknown handles and preserve the complete CUDA
|
||||||
|
+ // descriptor (HIP's query leaves the flags/reserved bytes unspecified).
|
||||||
|
+ let resources = object_resources().lock().map_err(|_| hipErrorCode_t::Unknown)?;
|
||||||
|
+ let bytes = resources.get(&(tex_object as usize)).ok_or(hipErrorCode_t::InvalidValue)?;
|
||||||
|
+ std::ptr::copy_nonoverlapping(bytes.as_ptr(), (descriptor as *mut HIP_RESOURCE_DESC).cast::<u8>(), bytes.len());
|
||||||
|
+ Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn ref_set_array(
|
||||||
|
texref: *mut textureReference,
|
||||||
|
array: hipArray_t,
|
||||||
|
diff --git a/zluda/src/impl/array.rs b/zluda/src/impl/array.rs
|
||||||
|
--- a/zluda/src/impl/array.rs
|
||||||
|
+++ b/zluda/src/impl/array.rs
|
||||||
|
@@ -70,5 +70,23 @@
|
||||||
|
handle: *mut hipArray_t,
|
||||||
|
desc: *const HIP_ARRAY3D_DESCRIPTOR,
|
||||||
|
) -> hipError_t {
|
||||||
|
hipArray3DCreate(handle, desc)
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+pub(crate) unsafe fn get_descriptor_v2(
|
||||||
|
+ output: &mut HIP_ARRAY_DESCRIPTOR,
|
||||||
|
+ array: hipArray_t,
|
||||||
|
+) -> hipError_t {
|
||||||
|
+ hipArrayGetDescriptor(output, array)?;
|
||||||
|
+ if let Some(format) = original_format(array)? {
|
||||||
|
+ output.Format = hipArray_Format(format);
|
||||||
|
+ }
|
||||||
|
+ Ok(())
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+pub(crate) unsafe fn _3d_get_descriptor_v2(
|
||||||
|
+ output: &mut HIP_ARRAY3D_DESCRIPTOR,
|
||||||
|
+ array: hipArray_t,
|
||||||
|
+) -> hipError_t {
|
||||||
|
+ hipArray3DGetDescriptor(output, array)
|
||||||
|
+}
|
||||||
@@ -0,0 +1,440 @@
|
|||||||
|
Subject: [PATCH] Guard and own native overrides; capture K replay on Windows
|
||||||
|
|
||||||
|
Native replacements require the upstream PTX identity manifest. Preserve native
|
||||||
|
module/image ownership, bypass redundant JIT for completely replaced modules,
|
||||||
|
include the loaded Windows DLL fingerprint in cache identity, and expose opt-in
|
||||||
|
private real-weight replay captures with explicit native/translated launch logs.
|
||||||
|
|
||||||
|
diff --git a/zluda/src/impl/d4r_replay.rs b/zluda/src/impl/d4r_replay.rs
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..875b2e3
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/zluda/src/impl/d4r_replay.rs
|
||||||
|
@@ -0,0 +1,109 @@
|
||||||
|
+//! Opt-in, synchronous diagnostic captures. Never enabled in the game fast path.
|
||||||
|
+//! Captures can contain proprietary network weights; keep them out of releases.
|
||||||
|
+use hip_runtime_sys::*;
|
||||||
|
+use std::{collections::HashMap, ffi::{c_void, CStr}, fs, path::PathBuf, sync::{Mutex, OnceLock}};
|
||||||
|
+
|
||||||
|
+struct Config { directory: PathBuf, filter: String, limit: u32 }
|
||||||
|
+static CONFIG: OnceLock<Option<Config>> = OnceLock::new();
|
||||||
|
+static COUNTS: OnceLock<Mutex<HashMap<String, u32>>> = OnceLock::new();
|
||||||
|
+
|
||||||
|
+pub(crate) struct Capture { directory: PathBuf, allocations: Vec<(usize, usize)> }
|
||||||
|
+
|
||||||
|
+fn io_error(error: impl std::fmt::Display) -> hipErrorCode_t {
|
||||||
|
+ eprintln!("[d4r-replay] capture failed: {error}");
|
||||||
|
+ hipErrorCode_t::r#Unknown
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl Capture {
|
||||||
|
+ fn save(&self, destination: &std::path::Path) -> hipError_t {
|
||||||
|
+ fs::create_dir_all(destination).map_err(io_error)?;
|
||||||
|
+ for (id, &(base, size)) in self.allocations.iter().enumerate() {
|
||||||
|
+ let mut bytes = vec![0u8; size];
|
||||||
|
+ unsafe { hipMemcpyDtoH(bytes.as_mut_ptr().cast(), hipDeviceptr_t(base as *mut c_void), size) }?;
|
||||||
|
+ fs::write(destination.join(format!("alloc-{id}.bin")), bytes).map_err(io_error)?;
|
||||||
|
+ }
|
||||||
|
+ Ok(())
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ pub(crate) fn finish(self, stream: hipStream_t) -> hipError_t {
|
||||||
|
+ unsafe { hipStreamSynchronize(stream) }?;
|
||||||
|
+ self.save(&self.directory.join("gpu-output"))?;
|
||||||
|
+ eprintln!("[d4r-replay] complete path={}", self.directory.display());
|
||||||
|
+ Ok(())
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+pub(crate) unsafe fn begin(
|
||||||
|
+ name: &CStr, stream: hipStream_t, parameters: *mut *mut c_void,
|
||||||
|
+ extra: *mut *mut c_void, launch: [u32; 7],
|
||||||
|
+) -> Result<Option<Capture>, hipErrorCode_t> {
|
||||||
|
+ let Some(config) = CONFIG.get_or_init(|| std::env::var_os("D4R_CUDA_REPLAY_DUMP_DIR").map(|directory| Config {
|
||||||
|
+ directory: directory.into(),
|
||||||
|
+ filter: std::env::var("D4R_CUDA_REPLAY_DUMP_FILTER").unwrap_or_default(),
|
||||||
|
+ limit: std::env::var("D4R_CUDA_REPLAY_DUMP_LIMIT").ok().and_then(|s| s.parse().ok()).unwrap_or(1),
|
||||||
|
+ })) else { return Ok(None) };
|
||||||
|
+ let name = name.to_str().map_err(io_error)?;
|
||||||
|
+ // Only the audited, single 176-byte K argument ABI is captured here.
|
||||||
|
+ if !name.starts_with("dltss_pwin_") || !name.ends_with("_layer") || !name.contains(&config.filter) {
|
||||||
|
+ return Ok(None);
|
||||||
|
+ }
|
||||||
|
+ let mut block = std::ptr::null::<u8>();
|
||||||
|
+ let mut bytes = None;
|
||||||
|
+ if !extra.is_null() {
|
||||||
|
+ for i in (0..6).step_by(2) {
|
||||||
|
+ match *extra.add(i) as usize {
|
||||||
|
+ 1 => block = (*extra.add(i + 1)).cast(),
|
||||||
|
+ 2 => bytes = Some(*(*extra.add(i + 1) as *const usize)),
|
||||||
|
+ 0 => break,
|
||||||
|
+ _ => return Err(hipErrorCode_t::r#InvalidValue),
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ } else if !parameters.is_null() {
|
||||||
|
+ block = (*parameters).cast();
|
||||||
|
+ bytes = Some(176);
|
||||||
|
+ }
|
||||||
|
+ if block.is_null() || bytes != Some(176) {
|
||||||
|
+ return Err(io_error(format!("unexpected K parameter ABI for {name}: {bytes:?}")));
|
||||||
|
+ }
|
||||||
|
+ let sequence = {
|
||||||
|
+ let mut counts = COUNTS.get_or_init(|| Mutex::new(HashMap::new())).lock().map_err(io_error)?;
|
||||||
|
+ let count = counts.entry(name.to_owned()).or_default();
|
||||||
|
+ if *count >= config.limit { return Ok(None); }
|
||||||
|
+ *count += 1;
|
||||||
|
+ *count
|
||||||
|
+ };
|
||||||
|
+ hipStreamSynchronize(stream)?;
|
||||||
|
+ let arguments = std::slice::from_raw_parts(block, 176);
|
||||||
|
+ let directory = config.directory.join(format!("replay-{sequence:06}-{name}"));
|
||||||
|
+ fs::create_dir_all(&directory).map_err(io_error)?;
|
||||||
|
+ fs::write(directory.join("args.bin"), arguments).map_err(io_error)?;
|
||||||
|
+ let mut manifest = format!("kernel {name}\nlaunch {} {} {} {} {} {} {}\nargs 176\n",
|
||||||
|
+ launch[0], launch[1], launch[2], launch[3], launch[4], launch[5], launch[6]);
|
||||||
|
+ let mut allocations = Vec::<(usize, usize)>::new();
|
||||||
|
+ for offset in (0..176).step_by(8) {
|
||||||
|
+ let pointer = u64::from_le_bytes(arguments[offset..offset + 8].try_into().unwrap()) as usize;
|
||||||
|
+ if pointer == 0 { continue; }
|
||||||
|
+ let mut base = hipDeviceptr_t(std::ptr::null_mut());
|
||||||
|
+ let mut size = 0;
|
||||||
|
+ if hipMemGetAddressRange(&mut base, &mut size, hipDeviceptr_t(pointer as *mut c_void)).is_err() { continue; }
|
||||||
|
+ let base = base.0 as usize;
|
||||||
|
+ if pointer < base || pointer - base >= size { continue; }
|
||||||
|
+ if size > 512 * 1024 * 1024 { return Err(io_error("allocation exceeds diagnostic 512 MiB limit")); }
|
||||||
|
+ let id = match allocations.iter().position(|&(b, _)| b == base) {
|
||||||
|
+ Some(id) => id,
|
||||||
|
+ None => {
|
||||||
|
+ let id = allocations.len();
|
||||||
|
+ allocations.push((base, size));
|
||||||
|
+ manifest.push_str(&format!("alloc {id} 0x{base:x} {size}\n"));
|
||||||
|
+ id
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ manifest.push_str(&format!("pointer {offset} {id} {}\n", pointer - base));
|
||||||
|
+ }
|
||||||
|
+ if allocations.is_empty() { return Err(io_error("no K allocation pointers resolved")); }
|
||||||
|
+ fs::write(directory.join("manifest.txt"), manifest).map_err(io_error)?;
|
||||||
|
+ let capture = Capture { directory, allocations };
|
||||||
|
+ capture.save(&capture.directory)?;
|
||||||
|
+ eprintln!("[d4r-replay] begin kernel={name} allocations={} path={}", capture.allocations.len(), capture.directory.display());
|
||||||
|
+ Ok(Some(capture))
|
||||||
|
+}
|
||||||
|
diff --git a/zluda/src/impl/function.rs b/zluda/src/impl/function.rs
|
||||||
|
index 033d9a0..7a7470a 100644
|
||||||
|
--- a/zluda/src/impl/function.rs
|
||||||
|
+++ b/zluda/src/impl/function.rs
|
||||||
|
@@ -7,6 +7,8 @@ use hip_runtime_sys::*;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
pub(crate) struct Function {
|
||||||
|
+ pub(crate) name: std::ffi::CString,
|
||||||
|
+ pub(crate) native_owner: Option<super::module::NativeModule>,
|
||||||
|
pub(crate) base: hipFunction_t,
|
||||||
|
pub(crate) sm_version: u32,
|
||||||
|
pub(crate) explicit_args_size_align: Option<Vec<FunctionArgInfo>>,
|
||||||
|
@@ -144,6 +146,8 @@ pub(crate) fn launch_kernel(
|
||||||
|
}
|
||||||
|
translated_extra_storage.as_mut_ptr()
|
||||||
|
};
|
||||||
|
+ let replay = unsafe { super::d4r_replay::begin(&f.name, stream, kernel_params, extra,
|
||||||
|
+ [grid_dim_x, grid_dim_y, grid_dim_z, block_dim_x, block_dim_y, block_dim_z, shared_mem_bytes]) }?;
|
||||||
|
// the parameter block: kernel_params[0] (a single struct parameter) or the extra buffer
|
||||||
|
let prep_key = if let (Some(_), Some(key_offset)) = (f.prep, f.prep_key) {
|
||||||
|
let mut block: *const u8 = std::ptr::null();
|
||||||
|
@@ -206,7 +210,11 @@ pub(crate) fn launch_kernel(
|
||||||
|
}
|
||||||
|
let block_dim_z = if f.block_z != 0 { f.block_z } else { block_dim_z };
|
||||||
|
let (grid_dim_x, grid_dim_y, grid_dim_z) = if f.grid_x != 0 { (f.grid_x, 1, 1) } else { (grid_dim_x, grid_dim_y, grid_dim_z) };
|
||||||
|
- unsafe {
|
||||||
|
+ if std::env::var_os("D4R_ZLUDA_VERBOSE").is_some() {
|
||||||
|
+ eprintln!("[d4r-launch] kernel={:?} backend={} grid={grid_dim_x},{grid_dim_y},{grid_dim_z} block={block_dim_x},{block_dim_y},{block_dim_z}",
|
||||||
|
+ f.name, if f.native_owner.is_some() { "native" } else { "translated" });
|
||||||
|
+ }
|
||||||
|
+ let result = unsafe {
|
||||||
|
hipModuleLaunchKernel(
|
||||||
|
f.base,
|
||||||
|
grid_dim_x,
|
||||||
|
@@ -220,7 +228,10 @@ pub(crate) fn launch_kernel(
|
||||||
|
kernel_params,
|
||||||
|
extra_for_hip,
|
||||||
|
)
|
||||||
|
- }
|
||||||
|
+ };
|
||||||
|
+ result?;
|
||||||
|
+ if let Some(replay) = replay { replay.finish(stream)?; }
|
||||||
|
+ Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn set_attribute(
|
||||||
|
diff --git a/zluda/src/impl/library.rs b/zluda/src/impl/library.rs
|
||||||
|
index 98be33e..1319792 100644
|
||||||
|
--- a/zluda/src/impl/library.rs
|
||||||
|
+++ b/zluda/src/impl/library.rs
|
||||||
|
@@ -21,8 +21,8 @@ impl Library {
|
||||||
|
let module_lock = self.modules.get(device).ok_or(CUerror::INVALID_DEVICE)?;
|
||||||
|
*module_lock.get_or_init(|| match self.data {
|
||||||
|
LibraryData::Lazy(lib) => {
|
||||||
|
- let (module, sm_version, meta32) = module::load_hip_module(lib)?;
|
||||||
|
- Ok(Module::new(module, sm_version, meta32).wrap())
|
||||||
|
+ let (module, sm_version, meta32, ptx_hash, native_image) = module::load_hip_module(lib)?;
|
||||||
|
+ Ok(Module::new(module, sm_version, meta32, ptx_hash, native_image).wrap())
|
||||||
|
}
|
||||||
|
LibraryData::Eager(()) => Err(CUerror::NOT_SUPPORTED),
|
||||||
|
})
|
||||||
|
diff --git a/zluda/src/impl/mod.rs b/zluda/src/impl/mod.rs
|
||||||
|
index 5329d32..14e0aee 100644
|
||||||
|
--- a/zluda/src/impl/mod.rs
|
||||||
|
+++ b/zluda/src/impl/mod.rs
|
||||||
|
@@ -5,6 +5,7 @@ pub(super) mod array;
|
||||||
|
pub(super) mod context;
|
||||||
|
pub(super) mod device;
|
||||||
|
pub(super) mod driver;
|
||||||
|
+pub(super) mod d4r_replay;
|
||||||
|
pub(super) mod event;
|
||||||
|
pub(super) mod function;
|
||||||
|
pub(super) mod graph;
|
||||||
|
diff --git a/zluda/src/impl/module.rs b/zluda/src/impl/module.rs
|
||||||
|
index 9f72f2f..5a79368 100644
|
||||||
|
--- a/zluda/src/impl/module.rs
|
||||||
|
+++ b/zluda/src/impl/module.rs
|
||||||
|
@@ -19,6 +19,8 @@ pub(crate) struct Module {
|
||||||
|
pub(crate) base: hipModule_t,
|
||||||
|
pub(crate) sm_version: u32,
|
||||||
|
pub(crate) bit32: Option<Metadata32Bit>,
|
||||||
|
+ ptx_hash: Option<u64>,
|
||||||
|
+ _native_base_image: Option<Vec<u8>>,
|
||||||
|
mutable: Mutex<ModuleMutable>,
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -33,12 +35,14 @@ impl ModuleMutable {
|
||||||
|
sm_version: u32,
|
||||||
|
name: CString,
|
||||||
|
explicit_args_size_align: Option<Vec<FunctionArgInfo>>,
|
||||||
|
+ ptx_hash: Option<u64>,
|
||||||
|
) -> Result<&Function, CUerror> {
|
||||||
|
Ok(match self.functions.entry(name) {
|
||||||
|
hash_map::Entry::Occupied(entry) => &*entry.into_mut(),
|
||||||
|
hash_map::Entry::Vacant(entry) => {
|
||||||
|
- let (func_handle, prep, block_z, grid_x, prep_key, prep_slots) = match native_override(entry.key()) {
|
||||||
|
- Some(native) => native,
|
||||||
|
+ let mut native_owner = None;
|
||||||
|
+ let (func_handle, prep, block_z, grid_x, prep_key, prep_slots) = match native_override(entry.key(), ptx_hash) {
|
||||||
|
+ Some((native, owner)) => { native_owner = Some(owner); native },
|
||||||
|
None => {
|
||||||
|
let mut func_handle = unsafe { std::mem::zeroed() };
|
||||||
|
unsafe { hipModuleGetFunction(&mut func_handle, module, entry.key().as_ptr()) }?;
|
||||||
|
@@ -46,6 +50,8 @@ impl ModuleMutable {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let func = Box::new(Function {
|
||||||
|
+ name: entry.key().clone(),
|
||||||
|
+ native_owner,
|
||||||
|
base: func_handle,
|
||||||
|
sm_version,
|
||||||
|
explicit_args_size_align,
|
||||||
|
@@ -69,19 +75,48 @@ impl ModuleMutable {
|
||||||
|
/// same stream with `d4r_prep_blocks` (a u32 global in the code object) blocks of 128 threads.
|
||||||
|
/// An optional u32 global `d4r_block_z` replaces the launch's block z dimension, and `d4r_grid_x`
|
||||||
|
/// (persistent kernels, which derive the original grid from their parameters) the grid.
|
||||||
|
+pub(crate) struct NativeModule {
|
||||||
|
+ module: hipModule_t,
|
||||||
|
+ _image: Vec<u8>,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl Drop for NativeModule {
|
||||||
|
+ fn drop(&mut self) {
|
||||||
|
+ if self.module.0.is_null() { return; }
|
||||||
|
+ if let Err(error) = unsafe { hipModuleUnload(self.module) } {
|
||||||
|
+ eprintln!("[d4r-native] unload failed: {error:?}");
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
fn native_override(
|
||||||
|
name: &CStr,
|
||||||
|
-) -> Option<(hipFunction_t, Option<(hipFunction_t, u32)>, u32, u32, Option<u32>, u32)> {
|
||||||
|
+ ptx_hash: Option<u64>,
|
||||||
|
+) -> Option<((hipFunction_t, Option<(hipFunction_t, u32)>, u32, u32, Option<u32>, u32), NativeModule)> {
|
||||||
|
let dir = std::env::var_os("D4R_ZLUDA_NATIVE_DIR")?;
|
||||||
|
+ let manifest = std::path::Path::new(&dir).join("d4r-kernels.txt");
|
||||||
|
+ let allowed = fs::read_to_string(&manifest).ok().is_some_and(|text| text.lines().any(|line| {
|
||||||
|
+ let mut fields = line.split_whitespace();
|
||||||
|
+ fields.next() == name.to_str().ok()
|
||||||
|
+ && fields.next().and_then(|s| u64::from_str_radix(s, 16).ok()) == ptx_hash
|
||||||
|
+ && ptx_hash.is_some()
|
||||||
|
+ }));
|
||||||
|
+ if !allowed {
|
||||||
|
+ eprintln!("[d4r-native] miss kernel={name:?} reason=ptx_identity hash={ptx_hash:016x?} fallback=translated");
|
||||||
|
+ return None;
|
||||||
|
+ }
|
||||||
|
let path = std::path::Path::new(&dir).join(format!("{}.hsaco", name.to_str().ok()?));
|
||||||
|
- let image = fs::read(&path).ok()?;
|
||||||
|
- // HIP may reference the image after loading; the override lives for the whole process.
|
||||||
|
- let image: &'static [u8] = Box::leak(image.into_boxed_slice());
|
||||||
|
+ let image = match fs::read(&path) {
|
||||||
|
+ Ok(image) => image,
|
||||||
|
+ Err(error) => { eprintln!("[d4r-native] miss kernel={name:?} reason=object_missing error={error} fallback=translated"); return None; }
|
||||||
|
+ };
|
||||||
|
let mut native_module = unsafe { mem::zeroed() };
|
||||||
|
if unsafe { hipModuleLoadData(&mut native_module, image.as_ptr().cast()) }.is_err() {
|
||||||
|
eprintln!("[d4r-native] failed to load {}", path.display());
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
+ // Keep the image and module owned by the CUfunction; release at module unload.
|
||||||
|
+ let owner = NativeModule { module: native_module, _image: image };
|
||||||
|
let mut func = unsafe { mem::zeroed() };
|
||||||
|
if unsafe { hipModuleGetFunction(&mut func, native_module, name.as_ptr()) }.is_err() {
|
||||||
|
eprintln!("[d4r-native] {} has no kernel {:?}", path.display(), name);
|
||||||
|
@@ -138,7 +173,7 @@ fn native_override(
|
||||||
|
prep_key,
|
||||||
|
prep_slots
|
||||||
|
);
|
||||||
|
- Some((func, prep, block_z, grid_x, prep_key, prep_slots))
|
||||||
|
+ Some(((func, prep, block_z, grid_x, prep_key, prep_slots), owner))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ZludaObject for Module {
|
||||||
|
@@ -229,7 +264,7 @@ pub(crate) struct Global32Bit {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Module {
|
||||||
|
- pub(crate) fn new(base: hipModule_t, sm_version: u32, bit32: Option<Metadata32Bit>) -> Self {
|
||||||
|
+ pub(crate) fn new(base: hipModule_t, sm_version: u32, bit32: Option<Metadata32Bit>, ptx_hash: Option<u64>, native_base_image: Option<Vec<u8>>) -> Self {
|
||||||
|
Self {
|
||||||
|
base,
|
||||||
|
sm_version,
|
||||||
|
@@ -237,6 +272,8 @@ impl Module {
|
||||||
|
functions: FxHashMap::default(),
|
||||||
|
}),
|
||||||
|
bit32,
|
||||||
|
+ ptx_hash,
|
||||||
|
+ _native_base_image: native_base_image,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -248,7 +285,7 @@ impl Module {
|
||||||
|
.map(Vec::clone)
|
||||||
|
});
|
||||||
|
mutable
|
||||||
|
- .get_function(self.base, self.sm_version, name.to_owned(), explicit_args)
|
||||||
|
+ .get_function(self.base, self.sm_version, name.to_owned(), explicit_args, self.ptx_hash)
|
||||||
|
.map(|f| unsafe { (f as *const Function).as_ref().unwrap() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -256,7 +293,7 @@ impl Module {
|
||||||
|
fn get_best_ptx_and_compile(
|
||||||
|
global_state: &GlobalState,
|
||||||
|
image: CodeLibraryRef<'_>,
|
||||||
|
-) -> Result<(hipModule_t, u32, Option<Metadata32Bit>), CUerror> {
|
||||||
|
+) -> Result<(hipModule_t, u32, Option<Metadata32Bit>, Option<u64>, Option<Vec<u8>>), CUerror> {
|
||||||
|
let mut ptx_modules = Vec::new();
|
||||||
|
unsafe {
|
||||||
|
CodeLibraryRef::iterate_modules(image, |_, module| match module {
|
||||||
|
@@ -280,7 +317,7 @@ fn get_best_ptx_and_compile(
|
||||||
|
let dump_dir = std::path::PathBuf::from(dump_dir);
|
||||||
|
let _ = fs::create_dir_all(&dump_dir);
|
||||||
|
for (index, source) in ptx_modules.iter().enumerate() {
|
||||||
|
- let stem = format!("module-{index:04}");
|
||||||
|
+ let stem = format!("module-{:016x}-{index:04}", ptx_identity(source));
|
||||||
|
let _ = fs::write(dump_dir.join(format!("{stem}.ptx")), source.as_bytes());
|
||||||
|
let diagnostic = match ptx_parser::parse_module_checked(source.as_ref()) {
|
||||||
|
Ok(ast) => format!(
|
||||||
|
@@ -345,6 +382,32 @@ fn get_best_ptx_and_compile(
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
+ let ptx_hash = text.map(|source| ptx_identity(source));
|
||||||
|
+ // A fully replaced, single-entry module needs no translated binary. LDS
|
||||||
|
+ // declarations belong to the replaced kernel; exported globals do not.
|
||||||
|
+ let only_kernel_and_lds = module.directives.iter().all(|directive| match directive {
|
||||||
|
+ ptx_parser::Directive::Method(_, method) => method.func_directive.name.is_kernel(),
|
||||||
|
+ ptx_parser::Directive::Variable(_, variable) => matches!(variable.info.state_space, ptx_parser::StateSpace::Shared),
|
||||||
|
+ });
|
||||||
|
+ let entries: Vec<_> = module.directives.iter().filter_map(|directive| match directive {
|
||||||
|
+ ptx_parser::Directive::Method(_, method) => Some(method), _ => None,
|
||||||
|
+ }).collect();
|
||||||
|
+ if module.address_size == 64 && only_kernel_and_lds && entries.len() == 1 {
|
||||||
|
+ let method = entries[0];
|
||||||
|
+ {
|
||||||
|
+ if method.func_directive.name.is_kernel() {
|
||||||
|
+ if let Ok(name) = CString::new(method.func_directive.name.text()) {
|
||||||
|
+ if let Some((_, mut owner)) = native_override(&name, ptx_hash) {
|
||||||
|
+ let base = owner.module;
|
||||||
|
+ owner.module = hipModule_t(std::ptr::null_mut());
|
||||||
|
+ let image = mem::take(&mut owner._image);
|
||||||
|
+ eprintln!("[d4r-native] module_direct kernel={name:?} translated_jit=skipped");
|
||||||
|
+ return Ok((base, module.sm_version, None, ptx_hash, Some(image)));
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
// TODO: get this information on initialization
|
||||||
|
let hip_properties = get_hip_properties()?;
|
||||||
|
let gcn_arch = get_gcn_arch(&hip_properties)?;
|
||||||
|
@@ -372,7 +435,12 @@ fn get_best_ptx_and_compile(
|
||||||
|
.or_else(|_| compile_and_cache(gcn_arch, attributes, module, &mut cache_with_key))?;
|
||||||
|
let mut hip_module = unsafe { mem::zeroed() };
|
||||||
|
unsafe { hipModuleLoadData(&mut hip_module, elf_module.as_ptr().cast()) }?;
|
||||||
|
- Ok((hip_module, sm_version, zluda32))
|
||||||
|
+ Ok((hip_module, sm_version, zluda32, ptx_hash, None))
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+fn ptx_identity(text: &str) -> u64 {
|
||||||
|
+ text.trim_end_matches('\0').bytes().fold(0xcbf29ce484222325u64, |hash, byte|
|
||||||
|
+ (hash ^ u64::from(byte)).wrapping_mul(0x100000001b3))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cow_bytes_to_str<'a>(data: Cow<'a, [u8]>) -> Option<Cow<'a, str>> {
|
||||||
|
@@ -384,7 +452,7 @@ fn cow_bytes_to_str<'a>(data: Cow<'a, [u8]>) -> Option<Cow<'a, str>> {
|
||||||
|
|
||||||
|
pub(crate) fn load_hip_module(
|
||||||
|
library: CodeLibraryRef,
|
||||||
|
-) -> Result<(hipModule_t, u32, Option<Metadata32Bit>), CUerror> {
|
||||||
|
+) -> Result<(hipModule_t, u32, Option<Metadata32Bit>, Option<u64>, Option<Vec<u8>>), CUerror> {
|
||||||
|
let global_state = driver::global_state()?;
|
||||||
|
get_best_ptx_and_compile(global_state, library)
|
||||||
|
}
|
||||||
|
@@ -434,7 +502,7 @@ fn zluda_build_version() -> &'static str {
|
||||||
|
key.starts_with("D4R_ZLUDA_")
|
||||||
|
&& !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_NATIVE_DIR" | "D4R_ZLUDA_DIR" | "D4R_ZLUDA_DUMP_DIR" | "D4R_ZLUDA_DUMP_PTX_DIR" | "D4R_ZLUDA_LIBCUDA" | "D4R_ZLUDA_VERBOSE"
|
||||||
|
| "D4R_ZLUDA_WMMA_FP8_NATIVE" // keyed by effect (ExtraCacheAttributes)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
@@ -469,7 +537,10 @@ fn loaded_library_fingerprint() -> Option<String> {
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn loaded_library_fingerprint() -> Option<String> {
|
||||||
|
- None
|
||||||
|
+ let path = zluda_windows::get_module_path_for_function(zluda_build_version as *const () as usize)?;
|
||||||
|
+ let metadata = fs::metadata(path).ok()?;
|
||||||
|
+ let timestamp = metadata.modified().ok()?.duration_since(std::time::UNIX_EPOCH).ok()?;
|
||||||
|
+ Some(format!("{:x}.{:x}.{:x}", metadata.len(), timestamp.as_secs(), timestamp.subsec_nanos()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_cache_key<'a, 'b>(
|
||||||
|
@@ -577,16 +648,16 @@ pub(crate) fn load(module: &mut CUmodule, fname: &CStr) -> CUresult {
|
||||||
|
image.push(0);
|
||||||
|
let library = unsafe { CodeLibraryRef::try_load(image.as_ptr() as *const std::ffi::c_void) }
|
||||||
|
.map_err(|_| CUerror::NO_BINARY_FOR_GPU)?;
|
||||||
|
- let (hip_module, sm_version, meta32) = load_hip_module(library)?;
|
||||||
|
- *module = Module::new(hip_module, sm_version, meta32).wrap();
|
||||||
|
+ let (hip_module, sm_version, meta32, ptx_hash, native_image) = load_hip_module(library)?;
|
||||||
|
+ *module = Module::new(hip_module, sm_version, meta32, ptx_hash, native_image).wrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn load_data(module: &mut CUmodule, image: &std::ffi::c_void) -> CUresult {
|
||||||
|
let library =
|
||||||
|
unsafe { CodeLibraryRef::try_load(image) }.map_err(|_| CUerror::NO_BINARY_FOR_GPU)?;
|
||||||
|
- let (hip_module, sm_version, meta32) = load_hip_module(library)?;
|
||||||
|
- *module = Module::new(hip_module, sm_version, meta32).wrap();
|
||||||
|
+ let (hip_module, sm_version, meta32, ptx_hash, native_image) = load_hip_module(library)?;
|
||||||
|
+ *module = Module::new(hip_module, sm_version, meta32, ptx_hash, native_image).wrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
diff --git a/zluda/src/impl/d4r_replay.rs b/zluda/src/impl/d4r_replay.rs
|
||||||
|
index 875b2e3..d491ae2 100644
|
||||||
|
--- a/zluda/src/impl/d4r_replay.rs
|
||||||
|
+++ b/zluda/src/impl/d4r_replay.rs
|
||||||
|
@@ -7,6 +7,18 @@ struct Config { directory: PathBuf, filter: String, limit: u32 }
|
||||||
|
static CONFIG: OnceLock<Option<Config>> = OnceLock::new();
|
||||||
|
static COUNTS: OnceLock<Mutex<HashMap<String, u32>>> = OnceLock::new();
|
||||||
|
|
||||||
|
+fn parameter_size(name: &str) -> Option<usize> {
|
||||||
|
+ match name {
|
||||||
|
+ "dltss_pwin_enc0_layer" | "dltss_pwin_enc1_layer" | "dltss_pwin_enc2_layer" |
|
||||||
|
+ "dltss_pwin_enc3_layer" | "dltss_pwin_enc4_layer" | "dltss_pwin_dec5_layer" |
|
||||||
|
+ "dltss_pwin_dec4_layer" | "dltss_pwin_dec3_layer" | "dltss_pwin_dec2_layer" |
|
||||||
|
+ "dltss_pwin_dec1_layer" | "dltss_pwin_dec0_layer" => Some(176),
|
||||||
|
+ "rrlite_enc1_4x4" | "rrlite_enc2_4x4" | "rrlite_dec2_4x4" | "rrlite_dec1_4x4" => Some(56),
|
||||||
|
+ "rrlite_enc3_tube_4x4" => Some(88),
|
||||||
|
+ _ => None,
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
pub(crate) struct Capture { directory: PathBuf, allocations: Vec<(usize, usize)> }
|
||||||
|
|
||||||
|
fn io_error(error: impl std::fmt::Display) -> hipErrorCode_t {
|
||||||
|
@@ -43,8 +55,10 @@ pub(crate) unsafe fn begin(
|
||||||
|
limit: std::env::var("D4R_CUDA_REPLAY_DUMP_LIMIT").ok().and_then(|s| s.parse().ok()).unwrap_or(1),
|
||||||
|
})) else { return Ok(None) };
|
||||||
|
let name = name.to_str().map_err(io_error)?;
|
||||||
|
- // Only the audited, single 176-byte K argument ABI is captured here.
|
||||||
|
- if !name.starts_with("dltss_pwin_") || !name.ends_with("_layer") || !name.contains(&config.filter) {
|
||||||
|
+ // Only audited single-by-value K/M argument blocks. Never infer the size
|
||||||
|
+ // from a prefix: reading a different kernel's parameters can overrun host memory.
|
||||||
|
+ let Some(parameter_bytes) = parameter_size(name) else { return Ok(None); };
|
||||||
|
+ if !name.contains(&config.filter) {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
let mut block = std::ptr::null::<u8>();
|
||||||
|
@@ -60,10 +74,10 @@ pub(crate) unsafe fn begin(
|
||||||
|
}
|
||||||
|
} else if !parameters.is_null() {
|
||||||
|
block = (*parameters).cast();
|
||||||
|
- bytes = Some(176);
|
||||||
|
+ bytes = Some(parameter_bytes);
|
||||||
|
}
|
||||||
|
- if block.is_null() || bytes != Some(176) {
|
||||||
|
- return Err(io_error(format!("unexpected K parameter ABI for {name}: {bytes:?}")));
|
||||||
|
+ if block.is_null() || bytes != Some(parameter_bytes) {
|
||||||
|
+ return Err(io_error(format!("unexpected parameter ABI for {name}: {bytes:?}, expected {parameter_bytes}")));
|
||||||
|
}
|
||||||
|
let sequence = {
|
||||||
|
let mut counts = COUNTS.get_or_init(|| Mutex::new(HashMap::new())).lock().map_err(io_error)?;
|
||||||
|
@@ -73,14 +87,14 @@ pub(crate) unsafe fn begin(
|
||||||
|
*count
|
||||||
|
};
|
||||||
|
hipStreamSynchronize(stream)?;
|
||||||
|
- let arguments = std::slice::from_raw_parts(block, 176);
|
||||||
|
+ let arguments = std::slice::from_raw_parts(block, parameter_bytes);
|
||||||
|
let directory = config.directory.join(format!("replay-{sequence:06}-{name}"));
|
||||||
|
fs::create_dir_all(&directory).map_err(io_error)?;
|
||||||
|
fs::write(directory.join("args.bin"), arguments).map_err(io_error)?;
|
||||||
|
- let mut manifest = format!("kernel {name}\nlaunch {} {} {} {} {} {} {}\nargs 176\n",
|
||||||
|
+ let mut manifest = format!("kernel {name}\nlaunch {} {} {} {} {} {} {}\nargs {parameter_bytes}\n",
|
||||||
|
launch[0], launch[1], launch[2], launch[3], launch[4], launch[5], launch[6]);
|
||||||
|
let mut allocations = Vec::<(usize, usize)>::new();
|
||||||
|
- for offset in (0..176).step_by(8) {
|
||||||
|
+ for offset in (0..parameter_bytes).step_by(8) {
|
||||||
|
let pointer = u64::from_le_bytes(arguments[offset..offset + 8].try_into().unwrap()) as usize;
|
||||||
|
if pointer == 0 { continue; }
|
||||||
|
let mut base = hipDeviceptr_t(std::ptr::null_mut());
|
||||||
|
@@ -100,10 +114,23 @@ pub(crate) unsafe fn begin(
|
||||||
|
};
|
||||||
|
manifest.push_str(&format!("pointer {offset} {id} {}\n", pointer - base));
|
||||||
|
}
|
||||||
|
- if allocations.is_empty() { return Err(io_error("no K allocation pointers resolved")); }
|
||||||
|
+ if allocations.is_empty() { return Err(io_error("no allocation pointers resolved")); }
|
||||||
|
fs::write(directory.join("manifest.txt"), manifest).map_err(io_error)?;
|
||||||
|
let capture = Capture { directory, allocations };
|
||||||
|
capture.save(&capture.directory)?;
|
||||||
|
eprintln!("[d4r-replay] begin kernel={name} allocations={} path={}", capture.allocations.len(), capture.directory.display());
|
||||||
|
Ok(Some(capture))
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+#[cfg(test)]
|
||||||
|
+mod tests {
|
||||||
|
+ #[test]
|
||||||
|
+ fn only_audited_argument_sizes() {
|
||||||
|
+ assert_eq!(super::parameter_size("dltss_pwin_enc0_layer"), Some(176));
|
||||||
|
+ assert_eq!(super::parameter_size("rrlite_dec2_4x4"), Some(56));
|
||||||
|
+ assert_eq!(super::parameter_size("rrlite_enc3_tube_4x4"), Some(88));
|
||||||
|
+ assert_eq!(super::parameter_size("dltss_pwin_enc0_layer_prep"), None);
|
||||||
|
+ assert_eq!(super::parameter_size("rrlite_enc3_tube_4x4_prep"), None);
|
||||||
|
+ assert_eq!(super::parameter_size("dltss_pwin_unknown_layer"), None);
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
diff --git a/zluda_cache/src/lib.rs b/zluda_cache/src/lib.rs
|
||||||
|
index acd262e..c3ac1be 100644
|
||||||
|
--- a/zluda_cache/src/lib.rs
|
||||||
|
+++ b/zluda_cache/src/lib.rs
|
||||||
|
@@ -22,18 +22,36 @@ pub struct ModuleCache(SqliteConnection);
|
||||||
|
|
||||||
|
impl ModuleCache {
|
||||||
|
pub fn create_cache_dir_and_get_path() -> Option<String> {
|
||||||
|
- let mut cache_dir = dirs::cache_dir()?;
|
||||||
|
- cache_dir.extend(["zluda", "ComputeCache"]);
|
||||||
|
+ let override_dir = std::env::var_os("ZLUDA_CACHE_DIR");
|
||||||
|
+ let cache_dir = match override_dir {
|
||||||
|
+ Some(path) => {
|
||||||
|
+ let path = std::path::PathBuf::from(path);
|
||||||
|
+ if !path.is_absolute() {
|
||||||
|
+ eprintln!("[zluda-cache] ZLUDA_CACHE_DIR must be absolute; cache disabled");
|
||||||
|
+ return None;
|
||||||
|
+ }
|
||||||
|
+ path
|
||||||
|
+ }
|
||||||
|
+ None => dirs::cache_dir()?.join("zluda").join("ComputeCache"),
|
||||||
|
+ };
|
||||||
|
+ Self::cache_path(&cache_dir)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ fn cache_path(cache_dir: &std::path::Path) -> Option<String> {
|
||||||
|
// We ensure that the cache directory exists
|
||||||
|
- std::fs::create_dir_all(&cache_dir).ok()?;
|
||||||
|
+ if let Err(error) = std::fs::create_dir_all(cache_dir) {
|
||||||
|
+ eprintln!("[zluda-cache] cannot create {}: {error}; cache disabled", cache_dir.display());
|
||||||
|
+ return None;
|
||||||
|
+ }
|
||||||
|
// No need to create the file, it will be created by SQLite on first access
|
||||||
|
// zluda.db might be in use by older versions of ZLUDA
|
||||||
|
- cache_dir.push("zluda2.db");
|
||||||
|
- Some(cache_dir.to_string_lossy().into())
|
||||||
|
+ Some(cache_dir.join("zluda2.db").to_string_lossy().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open(file_path: &str) -> Option<Self> {
|
||||||
|
- let mut conn = SqliteConnection::establish(file_path).ok()?;
|
||||||
|
+ let mut conn = SqliteConnection::establish(file_path).map_err(|error| {
|
||||||
|
+ eprintln!("[zluda-cache] cannot open {file_path}: {error}; cache disabled");
|
||||||
|
+ }).ok()?;
|
||||||
|
conn.batch_execute("PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL;")
|
||||||
|
.ok()?;
|
||||||
|
conn.run_pending_migrations(MIGRATIONS).ok()?;
|
||||||
|
@@ -86,6 +104,17 @@ mod tests {
|
||||||
|
use arrayvec::ArrayString;
|
||||||
|
use diesel::prelude::*;
|
||||||
|
|
||||||
|
+ #[test]
|
||||||
|
+ fn explicit_directory_creates_persistent_database() {
|
||||||
|
+ let directory = std::env::temp_dir().join(format!("zluda-cache-test-{}-{}", std::process::id(), ModuleCache::time_now()));
|
||||||
|
+ let path = ModuleCache::cache_path(&directory).unwrap();
|
||||||
|
+ assert_eq!(std::path::Path::new(&path), directory.join("zluda2.db"));
|
||||||
|
+ let cache = ModuleCache::open(&path).unwrap();
|
||||||
|
+ drop(cache);
|
||||||
|
+ assert!(std::path::Path::new(&path).is_file());
|
||||||
|
+ std::fs::remove_dir_all(directory).unwrap();
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
#[derive(Queryable, Selectable)]
|
||||||
|
#[diesel(table_name = crate::schema::modules)]
|
||||||
|
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
diff --git a/llvm_zluda/src/compile.rs b/llvm_zluda/src/compile.rs
|
||||||
|
index 5a54417..90c0034 100644
|
||||||
|
--- a/llvm_zluda/src/compile.rs
|
||||||
|
+++ b/llvm_zluda/src/compile.rs
|
||||||
|
@@ -158,6 +158,10 @@ fn wmma_rewrite_flags(gcn_arch: &str) -> Option<u32> {
|
||||||
|
let mut flags = if wmma_fp8_rewrite_requested() { 1 } else { 0 };
|
||||||
|
if family == 12 {
|
||||||
|
flags |= 2;
|
||||||
|
+ // Explicit diagnostic/correctness mode; retain upstream's fast default.
|
||||||
|
+ if std::env::var("D4R_ZLUDA_WMMA_F16_REFERENCE").is_ok_and(|value| value.trim() == "1") {
|
||||||
|
+ flags |= 8;
|
||||||
|
+ }
|
||||||
|
// native FP8 needs gfx12 hardware; the layout shim on gfx11 takes it only for tests (D4R_WMMA12_SHIM)
|
||||||
|
if flags & 1 != 0 && wmma_fp8_native_requested() {
|
||||||
|
flags |= 4;
|
||||||
|
diff --git a/llvm_zluda/src/lib.cpp b/llvm_zluda/src/lib.cpp
|
||||||
|
index 07238db..d262fc6 100644
|
||||||
|
--- a/llvm_zluda/src/lib.cpp
|
||||||
|
+++ b/llvm_zluda/src/lib.cpp
|
||||||
|
@@ -970,6 +970,7 @@ static unsigned widenMmaK8(Module *M)
|
||||||
|
// Flags bit 0: also lower m16n8k32 e4m3 MMAs (D4R_ZLUDA_WMMA_FP8=1).
|
||||||
|
// Flags bit 1: gfx12 (RDNA4) WMMA layout: the d4r_wmma12_* helpers.
|
||||||
|
// Flags bit 2 (with bit 1): e4m3 MMAs on native FP8 WMMA: the d4r_wmma12f8_* e4m3 helpers.
|
||||||
|
+// Flags bit 3 (with bit 1): f64 sum / one f16 round for f16 accumulator MMAs.
|
||||||
|
// The helper sets share one contract (see zluda_ptx_impl.cpp), so only their names change here.
|
||||||
|
unsigned LLVMZludaRewriteMmaF16Wmma(LLVMModuleRef ModuleRef, unsigned Flags)
|
||||||
|
{
|
||||||
|
@@ -999,14 +1000,15 @@ unsigned LLVMZludaRewriteMmaF16Wmma(LLVMModuleRef ModuleRef, unsigned Flags)
|
||||||
|
const std::string Arch = Gfx12 ? "__zluda_ptx_impl_d4r_wmma12_" : "__zluda_ptx_impl_d4r_wmma_";
|
||||||
|
const std::string E4m3 = NativeFp8 ? "__zluda_ptx_impl_d4r_wmma12f8_" : Arch;
|
||||||
|
const std::string Widen = NativeFp8 ? E4m3 : "__zluda_ptx_impl_d4r_wmma_";
|
||||||
|
+ const std::string F16 = Arch + (Gfx12 && (Flags & 8) != 0 ? "t_mma_f16_reference" : "t_mma_f16");
|
||||||
|
Helpers H;
|
||||||
|
H.AFromB = M->getOrInsertFunction((Arch + "t_a_from_b").c_str(), Attributes, V8, V2);
|
||||||
|
H.BFromA = M->getOrInsertFunction((Arch + "t_b_from_a").c_str(), Attributes, V8, V4);
|
||||||
|
- H.Mma = M->getOrInsertFunction((Arch + "t_mma_f16").c_str(), Attributes, V2, V8, V8, V2);
|
||||||
|
+ H.Mma = M->getOrInsertFunction(F16.c_str(), Attributes, V2, V8, V8, V2);
|
||||||
|
H.FromNv = M->getOrInsertFunction("__zluda_ptx_impl_d4r_wmma_t_from_nv", Attributes, V2, V2);
|
||||||
|
H.ToNv = M->getOrInsertFunction("__zluda_ptx_impl_d4r_wmma_t_to_nv", Attributes, V2, V2);
|
||||||
|
H.AFromBPair = M->getOrInsertFunction((Arch + "t_a_from_b_pair").c_str(), Attributes, V8, V2, V2);
|
||||||
|
- H.MmaPair = M->getOrInsertFunction((Arch + "t_mma_f16_pair").c_str(), Attributes, V4, V8, V8, V2, V2);
|
||||||
|
+ H.MmaPair = M->getOrInsertFunction((F16 + "_pair").c_str(), Attributes, V4, V8, V8, V2, V2);
|
||||||
|
{
|
||||||
|
Type *F = Type::getFloatTy(Context);
|
||||||
|
Type *V4F = FixedVectorType::get(F, 4);
|
||||||
|
diff --git a/ptx/lib/zluda_ptx_impl.cpp b/ptx/lib/zluda_ptx_impl.cpp
|
||||||
|
index 4ff25dd..36bd6b1 100644
|
||||||
|
--- a/ptx/lib/zluda_ptx_impl.cpp
|
||||||
|
+++ b/ptx/lib/zluda_ptx_impl.cpp
|
||||||
|
@@ -1801,6 +1801,78 @@ __device__ static wmma_float8 wmma12_f16(wmma_uint8 a, wmma_uint8 b, wmma_float8
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
+// Correctness baseline for f16-accumulator MMAs. f32 WMMA accumulation may
|
||||||
|
+// reach the other f16 neighbour around cancellation-heavy rounding midpoints.
|
||||||
|
+// Keep the existing gfx12 gathers/layout; sum their f16 operands in f64 and
|
||||||
|
+// round directly to f16 once, matching d4r's independent NumPy/PTX reference.
|
||||||
|
+// The e4m3 and f32-shadow paths continue using native WMMA.
|
||||||
|
+__device__ static double wmma12_half_value(uint32_t bits)
|
||||||
|
+{
|
||||||
|
+ const uint32_t magnitude = bits & 0x7fffu, exponent = magnitude >> 10;
|
||||||
|
+ const double value = exponent == 0 ? double(magnitude) * 0x1p-24 :
|
||||||
|
+ double(std::bit_cast<float>(exponent == 31 ? (0x7f800000u | ((magnitude & 1023u) << 13)) :
|
||||||
|
+ ((magnitude << 13) + 0x38000000u)));
|
||||||
|
+ return bits & 0x8000u ? -value : value;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// Preserve f16 subnormal result bits even when the target's default f16
|
||||||
|
+// arithmetic mode flushes them. No intermediate f32 rounding is involved.
|
||||||
|
+__device__ static uint32_t wmma12_half_round(double value)
|
||||||
|
+{
|
||||||
|
+ const uint64_t bits = std::bit_cast<uint64_t>(value);
|
||||||
|
+ const uint32_t sign = uint32_t(bits >> 48) & 0x8000u;
|
||||||
|
+ const uint32_t exponent = uint32_t((bits >> 52) & 2047u);
|
||||||
|
+ const uint64_t fraction = bits & 0xfffffffffffffu;
|
||||||
|
+ if (exponent == 2047u) return sign | (fraction ? 0x7e00u : 0x7c00u);
|
||||||
|
+ const int power = int(exponent) - 1023;
|
||||||
|
+ if (power < -25) return sign;
|
||||||
|
+ if (power > 15) return sign | 0x7c00u;
|
||||||
|
+ const unsigned shift = power < -14 ? unsigned(28 - power) : 42u;
|
||||||
|
+ const uint64_t significand = fraction | 0x10000000000000ull;
|
||||||
|
+ uint32_t code = uint32_t(significand >> shift);
|
||||||
|
+ const uint64_t tail = significand & ((uint64_t(1) << shift) - 1);
|
||||||
|
+ const uint64_t midpoint = uint64_t(1) << (shift - 1);
|
||||||
|
+ code += tail > midpoint || (tail == midpoint && (code & 1u));
|
||||||
|
+ if (power >= -14) code += uint32_t(power + 14) << 10;
|
||||||
|
+ return sign | code;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+__device__ static wmma_uint4 wmma12_reference_c(uint2::Native_vec_ c0, uint2::Native_vec_ c1, bool paired)
|
||||||
|
+{
|
||||||
|
+ const bool high = wmma12_half() != 0;
|
||||||
|
+ const uint2::Native_vec_ own = paired && high ? c1 : c0;
|
||||||
|
+ const uint2::Native_vec_ send = paired && !high ? c1 : c0;
|
||||||
|
+ const uint2::Native_vec_ other = {wmma12_other_half(send.x), wmma12_other_half(send.y)};
|
||||||
|
+ const uint2::Native_vec_ even = paired && high ? other : own;
|
||||||
|
+ const uint2::Native_vec_ odd = paired && high ? own : other;
|
||||||
|
+ return {(even.x & 0xffffu) | (odd.x << 16), (even.x >> 16) | (odd.x & 0xffff0000u),
|
||||||
|
+ (even.y & 0xffffu) | (odd.y << 16), (even.y >> 16) | (odd.y & 0xffff0000u)};
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+__device__ static wmma_uint4 wmma12_f16_reference(wmma_uint8 a, wmma_uint8 b, wmma_uint4 c)
|
||||||
|
+{
|
||||||
|
+ // x/y: packed even rows, z/w: packed odd rows, in this half-wave.
|
||||||
|
+ wmma_uint4 result = {};
|
||||||
|
+ const uint32_t half = wmma12_half();
|
||||||
|
+ for (uint32_t i = 0; i < 8; ++i)
|
||||||
|
+ {
|
||||||
|
+ const uint32_t row = i + 8 * half;
|
||||||
|
+ double sum = wmma12_half_value((c[i / 2] >> (16 * (i & 1))) & 0xffffu);
|
||||||
|
+ for (uint32_t khalf = 0; khalf < 2; ++khalf)
|
||||||
|
+ for (uint32_t j = 0; j < 4; ++j)
|
||||||
|
+ {
|
||||||
|
+ const uint32_t av = wmma_from_lane(row + 16 * khalf, a[j]);
|
||||||
|
+ const uint32_t other = wmma12_other_half(b[j]);
|
||||||
|
+ const uint32_t bv = half == khalf ? b[j] : other;
|
||||||
|
+ sum += wmma12_half_value(av & 0xffffu) * wmma12_half_value(bv & 0xffffu);
|
||||||
|
+ sum += wmma12_half_value(av >> 16) * wmma12_half_value(bv >> 16);
|
||||||
|
+ }
|
||||||
|
+ // Do not round through f32: that can reintroduce the midpoint error.
|
||||||
|
+ result[(i & 1) * 2 + i / 4] |= wmma12_half_round(sum) << (16 * ((i / 2) & 1));
|
||||||
|
+ }
|
||||||
|
+ return result;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
// f32 D' = A' B' + C' for operands whose dwords hold two e4m3 bytes in their low 16 bits (the d4r_wmma12f8
|
||||||
|
// e4m3 "widening" below keeps the bytes): true e4m3 values, no scaling
|
||||||
|
__device__ static wmma_float8 wmma12_e4m3(wmma_uint8 a, wmma_uint8 b, wmma_float8 c)
|
||||||
|
@@ -1959,6 +2031,22 @@ extern "C"
|
||||||
|
return wmma12_t_from_d_pair(wmma12_f16(a_t, b_t, wmma12_c_from_t_pair(c0_t, c1_t)));
|
||||||
|
}
|
||||||
|
|
||||||
|
+ uint2::Native_vec_ FUNC(d4r_wmma12_t_mma_f16_reference)(wmma_uint8 a_t, wmma_uint8 b_t, uint2::Native_vec_ c_t)
|
||||||
|
+ {
|
||||||
|
+ const wmma_uint4 rows = wmma12_f16_reference(a_t, b_t, wmma12_reference_c(c_t, c_t, false));
|
||||||
|
+ const uint2::Native_vec_ other = {wmma12_other_half(rows.z), wmma12_other_half(rows.w)};
|
||||||
|
+ return wmma12_half() ? other : uint2::Native_vec_{rows.x, rows.y};
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ uint4::Native_vec_ FUNC(d4r_wmma12_t_mma_f16_reference_pair)(wmma_uint8 a_t, wmma_uint8 b_t, uint2::Native_vec_ c0_t, uint2::Native_vec_ c1_t)
|
||||||
|
+ {
|
||||||
|
+ const wmma_uint4 rows = wmma12_f16_reference(a_t, b_t, wmma12_reference_c(c0_t, c1_t, true));
|
||||||
|
+ const bool high = wmma12_half() != 0;
|
||||||
|
+ const uint32_t r0 = wmma12_other_half(high ? rows.x : rows.z);
|
||||||
|
+ const uint32_t r1 = wmma12_other_half(high ? rows.y : rows.w);
|
||||||
|
+ return high ? uint4::Native_vec_{r0, r1, rows.z, rows.w} : uint4::Native_vec_{rows.x, rows.y, r0, r1};
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
wmma_float4 FUNC(d4r_wmma12_s_mma_f16)(wmma_uint8 a_t, wmma_uint8 b_t, wmma_float4 c, float up, float down)
|
||||||
|
{
|
||||||
|
return wmma12_s_from_d(wmma12_f16(a_t, b_t, wmma12_c_from_s(c * up))) * down;
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
From b0161a4166652eee86a8113a38d042b02403db6e Mon Sep 17 00:00:00 2001
|
||||||
|
From: Codex <codex@localhost>
|
||||||
|
Date: Thu, 1 Oct 2026 18:07:07 +0300
|
||||||
|
Subject: [PATCH] Add opt-in Windows HIP event and occupancy diagnostics
|
||||||
|
|
||||||
|
---
|
||||||
|
zluda/src/impl/d4r_profile.rs | 60 +++++++++++++++++++++++++++++++++++
|
||||||
|
zluda/src/impl/function.rs | 9 ++++++
|
||||||
|
zluda/src/impl/mod.rs | 1 +
|
||||||
|
zluda/src/impl/module.rs | 2 +-
|
||||||
|
4 files changed, 71 insertions(+), 1 deletion(-)
|
||||||
|
create mode 100644 zluda/src/impl/d4r_profile.rs
|
||||||
|
|
||||||
|
diff --git a/zluda/src/impl/d4r_profile.rs b/zluda/src/impl/d4r_profile.rs
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..33ba5f6
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/zluda/src/impl/d4r_profile.rs
|
||||||
|
@@ -0,0 +1,60 @@
|
||||||
|
+//! Optional, deliberately serializing HIP-event diagnostics. Not a production
|
||||||
|
+//! scheduler or a claim about application FPS / measured hardware occupancy.
|
||||||
|
+use hip_runtime_sys::*;
|
||||||
|
+use std::{ffi::CStr, ptr, time::Instant};
|
||||||
|
+
|
||||||
|
+pub(super) struct Launch {
|
||||||
|
+ start: hipEvent_t,
|
||||||
|
+ end: hipEvent_t,
|
||||||
|
+ host: Instant,
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+impl Drop for Launch {
|
||||||
|
+ fn drop(&mut self) {
|
||||||
|
+ unsafe {
|
||||||
|
+ if !self.end.is_null() { let _ = hipEventDestroy(self.end); }
|
||||||
|
+ if !self.start.is_null() { let _ = hipEventDestroy(self.start); }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+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))
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ 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)?;
|
||||||
|
+ 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);
|
||||||
|
+ Ok(())
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
diff --git a/zluda/src/impl/function.rs b/zluda/src/impl/function.rs
|
||||||
|
index 7a7470a..c456dfe 100644
|
||||||
|
--- a/zluda/src/impl/function.rs
|
||||||
|
+++ b/zluda/src/impl/function.rs
|
||||||
|
@@ -191,6 +191,7 @@ pub(crate) fn launch_kernel(
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let (Some((prep, blocks)), true) = (f.prep, run_prep) {
|
||||||
|
+ let profile = unsafe { super::d4r_profile::Launch::begin(stream) }?;
|
||||||
|
unsafe {
|
||||||
|
hipModuleLaunchKernel(
|
||||||
|
prep,
|
||||||
|
@@ -206,6 +207,9 @@ pub(crate) fn launch_kernel(
|
||||||
|
extra_for_hip,
|
||||||
|
)
|
||||||
|
}?;
|
||||||
|
+ if let Some(profile) = profile {
|
||||||
|
+ unsafe { profile.finish(prep, &f.name, "native", "prep", [blocks,1,1], [128,1,1], 0, stream) }?;
|
||||||
|
+ }
|
||||||
|
f.prep_last.store(prep_key, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
let block_dim_z = if f.block_z != 0 { f.block_z } else { block_dim_z };
|
||||||
|
@@ -214,6 +218,7 @@ pub(crate) fn launch_kernel(
|
||||||
|
eprintln!("[d4r-launch] kernel={:?} backend={} grid={grid_dim_x},{grid_dim_y},{grid_dim_z} block={block_dim_x},{block_dim_y},{block_dim_z}",
|
||||||
|
f.name, if f.native_owner.is_some() { "native" } else { "translated" });
|
||||||
|
}
|
||||||
|
+ let profile = unsafe { super::d4r_profile::Launch::begin(stream) }?;
|
||||||
|
let result = unsafe {
|
||||||
|
hipModuleLaunchKernel(
|
||||||
|
f.base,
|
||||||
|
@@ -230,6 +235,10 @@ pub(crate) fn launch_kernel(
|
||||||
|
)
|
||||||
|
};
|
||||||
|
result?;
|
||||||
|
+ if let Some(profile) = profile {
|
||||||
|
+ unsafe { profile.finish(f.base, &f.name, if f.native_owner.is_some() { "native" } else { "translated" },
|
||||||
|
+ "main", [grid_dim_x, grid_dim_y, grid_dim_z], [block_dim_x, block_dim_y, block_dim_z], shared_mem_bytes, stream) }?;
|
||||||
|
+ }
|
||||||
|
if let Some(replay) = replay { replay.finish(stream)?; }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
diff --git a/zluda/src/impl/mod.rs b/zluda/src/impl/mod.rs
|
||||||
|
index 14e0aee..380eb6e 100644
|
||||||
|
--- a/zluda/src/impl/mod.rs
|
||||||
|
+++ b/zluda/src/impl/mod.rs
|
||||||
|
@@ -6,6 +6,7 @@ pub(super) mod context;
|
||||||
|
pub(super) mod device;
|
||||||
|
pub(super) mod driver;
|
||||||
|
pub(super) mod d4r_replay;
|
||||||
|
+pub(super) mod d4r_profile;
|
||||||
|
pub(super) mod event;
|
||||||
|
pub(super) mod function;
|
||||||
|
pub(super) mod graph;
|
||||||
|
diff --git a/zluda/src/impl/module.rs b/zluda/src/impl/module.rs
|
||||||
|
index 5a79368..232f7d4 100644
|
||||||
|
--- a/zluda/src/impl/module.rs
|
||||||
|
+++ b/zluda/src/impl/module.rs
|
||||||
|
@@ -502,7 +502,7 @@ fn zluda_build_version() -> &'static str {
|
||||||
|
key.starts_with("D4R_ZLUDA_")
|
||||||
|
&& !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_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_WMMA_FP8_NATIVE" // keyed by effect (ExtraCacheAttributes)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
--
|
||||||
|
2.55.0.windows.5
|
||||||
|
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
From a1c506fc956cfc347c56c44311669700c0d500ac Mon Sep 17 00:00:00 2001
|
||||||
|
From: Codex <codex@localhost>
|
||||||
|
Date: Thu, 1 Oct 2026 22:16:19 +0300
|
||||||
|
Subject: [PATCH] Profile CUDA host API duration without serializing GPU
|
||||||
|
launches
|
||||||
|
|
||||||
|
---
|
||||||
|
zluda/src/d4r_api_profile.rs | 34 ++++++++++++++++++++++++++++++++++
|
||||||
|
zluda/src/lib.rs | 3 +++
|
||||||
|
2 files changed, 37 insertions(+)
|
||||||
|
create mode 100644 zluda/src/d4r_api_profile.rs
|
||||||
|
|
||||||
|
diff --git a/zluda/src/d4r_api_profile.rs b/zluda/src/d4r_api_profile.rs
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..ef2da1b
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/zluda/src/d4r_api_profile.rs
|
||||||
|
@@ -0,0 +1,34 @@
|
||||||
|
+//! Host API timings only: no HIP events, stream waits, or altered scheduling.
|
||||||
|
+use std::{cell::RefCell, collections::BTreeMap, sync::OnceLock, time::Instant};
|
||||||
|
+
|
||||||
|
+#[derive(Default)]
|
||||||
|
+struct Row { calls: u64, total_ms: f64, max_ms: f64 }
|
||||||
|
+struct Samples { since: Instant, rows: BTreeMap<&'static str, Row> }
|
||||||
|
+thread_local! {
|
||||||
|
+ static SAMPLES: RefCell<Samples> = RefCell::new(Samples { since: Instant::now(), rows: BTreeMap::new() });
|
||||||
|
+}
|
||||||
|
+pub(crate) struct Call { name: &'static str, start: Instant }
|
||||||
|
+impl Call {
|
||||||
|
+ pub(crate) fn begin(name: &'static str) -> Option<Self> {
|
||||||
|
+ static ENABLED: OnceLock<bool> = OnceLock::new();
|
||||||
|
+ if !*ENABLED.get_or_init(|| std::env::var_os("D4R_ZLUDA_PROFILE_API").is_some()) { return None; }
|
||||||
|
+ Some(Self { name, start: Instant::now() })
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+impl Drop for Call {
|
||||||
|
+ fn drop(&mut self) {
|
||||||
|
+ let ms = self.start.elapsed().as_secs_f64() * 1000.;
|
||||||
|
+ SAMPLES.with(|samples| {
|
||||||
|
+ let mut samples = samples.borrow_mut();
|
||||||
|
+ let row = samples.rows.entry(self.name).or_default();
|
||||||
|
+ row.calls += 1; row.total_ms += ms; row.max_ms = row.max_ms.max(ms);
|
||||||
|
+ let period_ms = samples.since.elapsed().as_secs_f64() * 1000.;
|
||||||
|
+ if period_ms < 2000. { return; }
|
||||||
|
+ for (name, row) in &samples.rows {
|
||||||
|
+ eprintln!("D4R_CUDA_API_PROFILE thread={:?} api={name} calls={} total_ms={:.6} max_ms={:.6} period_ms={period_ms:.6} serializing=0",
|
||||||
|
+ std::thread::current().id(), row.calls, row.total_ms, row.max_ms);
|
||||||
|
+ }
|
||||||
|
+ samples.rows.clear(); samples.since = Instant::now();
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
diff --git a/zluda/src/lib.rs b/zluda/src/lib.rs
|
||||||
|
index ecdeb3e..31bfa42 100644
|
||||||
|
--- a/zluda/src/lib.rs
|
||||||
|
+++ b/zluda/src/lib.rs
|
||||||
|
@@ -2,6 +2,7 @@ use cuda_types::cuda::CUerror;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
pub(crate) mod r#impl;
|
||||||
|
+mod d4r_api_profile;
|
||||||
|
#[cfg_attr(windows, path = "os_win.rs")]
|
||||||
|
#[cfg_attr(not(windows), path = "os_unix.rs")]
|
||||||
|
mod os;
|
||||||
|
@@ -40,6 +41,7 @@ macro_rules! implemented {
|
||||||
|
if !initialized() {
|
||||||
|
return Err(CUerror::DEINITIALIZED);
|
||||||
|
}
|
||||||
|
+ let _profile = d4r_api_profile::Call::begin(stringify!($fn_name));
|
||||||
|
cuda_macros::cuda_normalize_fn!( crate::r#impl::$fn_name ) ($(zluda_common::FromCuda::<_, CUerror>::from_cuda(&$arg_id)?),*)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@@ -57,6 +59,7 @@ macro_rules! implemented_in_function {
|
||||||
|
if !initialized() {
|
||||||
|
return Err(CUerror::DEINITIALIZED);
|
||||||
|
}
|
||||||
|
+ let _profile = d4r_api_profile::Call::begin(stringify!($fn_name));
|
||||||
|
cuda_macros::cuda_normalize_fn!( crate::r#impl::function::$fn_name ) ($(zluda_common::FromCuda::<_, CUerror>::from_cuda(&$arg_id)?),*)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.55.0.windows.5
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -5,7 +5,7 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|||||||
MINGW_CXX="${MINGW_CXX:-x86_64-w64-mingw32-g++}"
|
MINGW_CXX="${MINGW_CXX:-x86_64-w64-mingw32-g++}"
|
||||||
|
|
||||||
"$ROOT/scripts/build_d4r_nvngx_shim.sh"
|
"$ROOT/scripts/build_d4r_nvngx_shim.sh"
|
||||||
"$MINGW_CXX" -std=c++20 -O2 -Wall -Wextra -static -static-libgcc -static-libstdc++ \
|
"$MINGW_CXX" -std=c++20 -O2 -Wall -Wextra -I"$ROOT/build/vulkan-include" -static -static-libgcc -static-libstdc++ \
|
||||||
"$ROOT/tools/d3d12_dlss_harness.cpp" "$ROOT/tools/d4r_ngx_param_host.cpp" "$ROOT/build/d4r_ngx_param_msvc.obj" \
|
"$ROOT/tools/d3d12_dlss_harness.cpp" "$ROOT/tools/d4r_ngx_param_host.cpp" "$ROOT/build/d4r_ngx_param_msvc.obj" \
|
||||||
-o "$ROOT/build/d3d12_dlss_harness.exe"
|
-o "$ROOT/build/d3d12_dlss_harness.exe"
|
||||||
printf 'Built %s\n' "$ROOT/build/d3d12_dlss_harness.exe"
|
printf 'Built %s\n' "$ROOT/build/d3d12_dlss_harness.exe"
|
||||||
Executable
+30
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build d4r-owned motion processing kernels and embed them in the Wine bridge.
|
||||||
|
set -euo pipefail
|
||||||
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
OUT="${1:-$ROOT/build/wine-nvcuda/generated}"
|
||||||
|
ROCM="${D4R_ROCM_DIR:-/opt/rocm}"
|
||||||
|
CLANG="$ROCM/lib/llvm/bin/clang++"
|
||||||
|
[[ -x "$CLANG" ]] || { echo "motion kernels need ROCm clang (D4R_ROCM_DIR)" >&2; exit 2; }
|
||||||
|
mkdir -p "$OUT"
|
||||||
|
for arch in gfx1100 gfx1101 gfx1102 gfx1103 gfx1200 gfx1201; do
|
||||||
|
"$CLANG" -x hip -nogpuinc -nogpulib --offload-device-only --offload-arch="$arch" \
|
||||||
|
--no-gpu-bundle-output -O3 -mno-wavefrontsize64 -o "$OUT/$arch.hsaco" \
|
||||||
|
"$ROOT/kernels/support/motion_dilate.hip"
|
||||||
|
done
|
||||||
|
python3 - "$OUT" <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
root = Path(sys.argv[1])
|
||||||
|
lines = ['/* Generated from d4r-owned HIP source; do not edit. */', '#include <string.h>']
|
||||||
|
for p in sorted(root.glob('gfx*.hsaco')):
|
||||||
|
data = p.read_bytes()
|
||||||
|
lines.append(f'static const unsigned char motion_{p.stem}[] __attribute__((aligned(64))) = {{')
|
||||||
|
lines.extend(','.join(f'0x{x:02x}' for x in data[i:i+24]) + ',' for i in range(0,len(data),24))
|
||||||
|
lines.append('};')
|
||||||
|
lines.append('static const void* d4r_motion_image(const char* arch) {')
|
||||||
|
for p in sorted(root.glob('gfx*.hsaco')):
|
||||||
|
lines.append(f' if (strcmp(arch, "{p.stem}") == 0) return motion_{p.stem};')
|
||||||
|
lines.append(' return NULL;\n}')
|
||||||
|
(root/'d4r_motion_kernels.h').write_text('\n'.join(lines)+'\n')
|
||||||
|
PY
|
||||||
@@ -16,7 +16,7 @@ ln -sfn "$VULKAN_INCLUDE/vulkan" "$ROOT/build/vulkan-include/vulkan"
|
|||||||
ln -sfn "$VULKAN_INCLUDE/vk_video" "$ROOT/build/vulkan-include/vk_video"
|
ln -sfn "$VULKAN_INCLUDE/vk_video" "$ROOT/build/vulkan-include/vk_video"
|
||||||
PARAM_OBJECT="$ROOT/build/d4r_ngx_param_msvc.obj"
|
PARAM_OBJECT="$ROOT/build/d4r_ngx_param_msvc.obj"
|
||||||
"$CLANG_CL" --target=x86_64-pc-windows-msvc /nologo /std:c++20 /O2 /c /GS- /GR- /EHs-c- /Zl \
|
"$CLANG_CL" --target=x86_64-pc-windows-msvc /nologo /std:c++20 /O2 /c /GS- /GR- /EHs-c- /Zl \
|
||||||
"/Fo$PARAM_OBJECT" "$ROOT/tools/d4r_ngx_param_msvc.cpp"
|
"/Fo$PARAM_OBJECT" -- "$ROOT/tools/d4r_ngx_param_msvc.cpp"
|
||||||
"$MINGW_CXX" -std=c++20 -O2 -Wall -Wextra -Wno-missing-field-initializers -I"$ROOT/build/vulkan-include" -shared -static -static-libgcc -static-libstdc++ \
|
"$MINGW_CXX" -std=c++20 -O2 -Wall -Wextra -Wno-missing-field-initializers -I"$ROOT/build/vulkan-include" -shared -static -static-libgcc -static-libstdc++ \
|
||||||
"$ROOT/tools/d4r_nvngx_shim.cpp" "$ROOT/tools/d4r_ngx_param_host.cpp" "$PARAM_OBJECT" \
|
"$ROOT/tools/d4r_nvngx_shim.cpp" "$ROOT/tools/d4r_ngx_param_host.cpp" "$PARAM_OBJECT" \
|
||||||
-o "$ROOT/build/d4r_nvngx.dll"
|
-o "$ROOT/build/d4r_nvngx.dll"
|
||||||
|
|||||||
Executable
+111
@@ -0,0 +1,111 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Run inside packaging/build/Dockerfile.glibc241 with a recent official Rust toolchain and ROCm mounted.
|
||||||
|
# Supply isolated, patched ZLUDA/vkd3d checkouts and the usual package_release.sh inputs.
|
||||||
|
set -euo pipefail
|
||||||
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
: "${D4R_RELEASE_BUILD_ROOT:?set the build output directory}"
|
||||||
|
: "${D4R_ZLUDA_SRC:?set a build-local ZLUDA checkout with patches 0002-0007 applied}"
|
||||||
|
: "${D4R_VKD3D_SRC:?set a build-local vkd3d-proton checkout with patches 0001 and 0002 applied}"
|
||||||
|
: "${D4R_ROCM_DIR:?set ROCm with clang and device libraries}"
|
||||||
|
BUILD="$(realpath -m "$D4R_RELEASE_BUILD_ROOT")"
|
||||||
|
[[ "$(getconf GNU_LIBC_VERSION)" == 'glibc 2.41' ]] || {
|
||||||
|
echo "This release build requires GLIBC 2.41; use the pinned Debian build image" >&2; exit 2;
|
||||||
|
}
|
||||||
|
# Helper generation modifies its source checkout. Refuse the external source-cache directory.
|
||||||
|
case "$(realpath "$D4R_ZLUDA_SRC")" in "$BUILD"/*) ;; *) echo "ZLUDA source must be inside $BUILD" >&2; exit 2 ;; esac
|
||||||
|
case "$(realpath "$D4R_VKD3D_SRC")" in "$BUILD"/*) ;; *) echo "vkd3d source must be inside $BUILD" >&2; exit 2 ;; esac
|
||||||
|
mkdir -p "$BUILD/logs"
|
||||||
|
for source in "$D4R_ZLUDA_SRC/ext/llvm-project/llvm/CMakeLists.txt" "$D4R_ZLUDA_SRC/ext/HiGHS/CMakeLists.txt"; do
|
||||||
|
[[ -f "$source" ]] || { echo "missing pinned source submodule: $source" >&2; exit 2; }
|
||||||
|
done
|
||||||
|
export CARGO_TARGET_DIR="$BUILD/zluda-target"
|
||||||
|
export CARGO_BUILD_JOBS="${CARGO_BUILD_JOBS:-8}"
|
||||||
|
export CMAKE_BUILD_PARALLEL_LEVEL="$CARGO_BUILD_JOBS"
|
||||||
|
export LIBRARY_PATH="$D4R_ROCM_DIR/lib${D4R_ROCM_LINK_STUBS:+:$D4R_ROCM_LINK_STUBS}"
|
||||||
|
export LD_LIBRARY_PATH="$D4R_ROCM_DIR/lib"
|
||||||
|
export HIP_PATH="$D4R_ROCM_DIR"
|
||||||
|
export ROCM_PATH="$D4R_ROCM_DIR"
|
||||||
|
export HIP_CLANG_PATH="$D4R_ROCM_DIR/lib/llvm/bin"
|
||||||
|
{
|
||||||
|
getconf GNU_LIBC_VERSION
|
||||||
|
rustc --version
|
||||||
|
cargo --version
|
||||||
|
gcc --version | head -1
|
||||||
|
winegcc --version | head -1
|
||||||
|
"$D4R_ROCM_DIR/lib/llvm/bin/clang++" --version | head -1
|
||||||
|
} > "$BUILD/toolchain.txt"
|
||||||
|
|
||||||
|
ZLUDA_SOURCE_ROOT="$D4R_ZLUDA_SRC" ROCM_ROOT="$D4R_ROCM_DIR" \
|
||||||
|
D4R_ZLUDA_PTX_HELPER_WORK="$BUILD/helpers" "$ROOT/scripts/build_zluda_ptx_helpers.sh" \
|
||||||
|
> "$BUILD/logs/helpers.log" 2>&1
|
||||||
|
echo 'Device helpers rebuilt'
|
||||||
|
(cd "$D4R_ZLUDA_SRC" && cargo build --offline --locked --release -p zluda \
|
||||||
|
&& cargo build --offline --locked --release -p ptx --example d4r_emit) \
|
||||||
|
> "$BUILD/logs/zluda.log" 2>&1
|
||||||
|
export D4R_ZLUDA_DIR="$CARGO_TARGET_DIR/release"
|
||||||
|
export D4R_ZLUDA_EMIT="$D4R_ZLUDA_DIR/examples/d4r_emit"
|
||||||
|
"$ROOT/scripts/check_glibc_compat.sh" 2.41 "$D4R_ZLUDA_DIR/libnvcuda.so"
|
||||||
|
echo 'ZLUDA and the offline emitter rebuilt; GLIBC check passed'
|
||||||
|
# A minimal ROCm compiler extraction may omit lld. Use the matching LLVM built above so
|
||||||
|
# all GPU targets link with the same AMDGPU/ELF support, inside the compatible environment.
|
||||||
|
for llvm_build in "$CARGO_TARGET_DIR"/release/build/llvm_zluda-*/out/build; do
|
||||||
|
[[ -f "$llvm_build/build.ninja" ]] || continue
|
||||||
|
ninja -j "$CARGO_BUILD_JOBS" -C "$llvm_build" lld > "$BUILD/logs/lld.log" 2>&1
|
||||||
|
export PATH="$llvm_build/bin:$PATH"
|
||||||
|
break
|
||||||
|
done
|
||||||
|
command -v lld >/dev/null || { echo 'missing AMDGPU linker' >&2; exit 2; }
|
||||||
|
|
||||||
|
"$ROOT/scripts/build_d4r_nvngx_shim.sh" > "$BUILD/logs/shim.log" 2>&1
|
||||||
|
"$ROOT/scripts/build_wine_nvcuda_bridge.sh" > "$BUILD/logs/bridge.log" 2>&1
|
||||||
|
"$ROOT/scripts/check_glibc_compat.sh" 2.41 "$ROOT/build/wine-nvcuda/x86_64-unix/nvcuda.dll.so"
|
||||||
|
if [[ ! -f "$BUILD/vkd3d-build/build.ninja" ]]; then
|
||||||
|
meson setup --cross-file "$D4R_VKD3D_SRC/build-win64.txt" --buildtype release \
|
||||||
|
-Denable_tests=false "$BUILD/vkd3d-build" "$D4R_VKD3D_SRC" > "$BUILD/logs/vkd3d.log" 2>&1
|
||||||
|
fi
|
||||||
|
ninja -j "$CARGO_BUILD_JOBS" -C "$BUILD/vkd3d-build" >> "$BUILD/logs/vkd3d.log" 2>&1
|
||||||
|
export D4R_VKD3D_DIR="$BUILD/vkd3d"
|
||||||
|
mkdir -p "$D4R_VKD3D_DIR"
|
||||||
|
x86_64-w64-mingw32-strip -o "$D4R_VKD3D_DIR/d3d12.dll" "$BUILD/vkd3d-build/libs/d3d12/d3d12.dll"
|
||||||
|
x86_64-w64-mingw32-strip -o "$D4R_VKD3D_DIR/d3d12core.dll" "$BUILD/vkd3d-build/libs/d3d12core/d3d12core.dll"
|
||||||
|
echo 'Shim, bridge and vkd3d-proton rebuilt'
|
||||||
|
|
||||||
|
export D4R_BUNDLE_TEX="$BUILD/kernels"
|
||||||
|
export D4R_DLSS_PTX_DIR="$BUILD/ptx"
|
||||||
|
python3 "$ROOT/kernels/tools/extract_dlss_ptx.py" "$D4R_BUNDLE_DLSS" "$D4R_DLSS_PTX_DIR" > "$BUILD/logs/extract.log"
|
||||||
|
build_textures() {
|
||||||
|
local arch="$1" folder fp8 accuracy out
|
||||||
|
folders=("$arch")
|
||||||
|
[[ "$arch" == gfx12* ]] && folders+=("$arch-fp8")
|
||||||
|
for folder in "${folders[@]}"; do
|
||||||
|
fp8=0
|
||||||
|
[[ "$folder" == *-fp8 ]] && fp8=1
|
||||||
|
for accuracy in 0 1; do
|
||||||
|
out="$D4R_BUNDLE_TEX/$folder"
|
||||||
|
[[ "$accuracy" == 1 ]] && out="$D4R_BUNDLE_TEX/accuracy/$folder"
|
||||||
|
D4R_PREFER_ACCURACY="$accuracy" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" \
|
||||||
|
D4R_DLSS_DLL="$D4R_BUNDLE_DLSS" "$ROOT/kernels/build.sh" tex "$out" \
|
||||||
|
> "$BUILD/logs/tex-$folder-$accuracy.log" 2>&1
|
||||||
|
echo "Texture kernels rebuilt: $folder accuracy=$accuracy"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
}
|
||||||
|
pending=()
|
||||||
|
failed=0
|
||||||
|
for arch in ${D4R_GPU_ARCHS:-gfx1100 gfx1101 gfx1102 gfx1103 gfx1200 gfx1201}; do
|
||||||
|
build_textures "$arch" &
|
||||||
|
pending+=("$!")
|
||||||
|
if (( ${#pending[@]} >= 4 )); then
|
||||||
|
wait "${pending[0]}" || failed=1
|
||||||
|
pending=("${pending[@]:1}")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
for pid in "${pending[@]}"; do wait "$pid" || failed=1; done
|
||||||
|
[[ "$failed" == 0 ]] || { echo 'Texture target build failed' >&2; exit 1; }
|
||||||
|
python3 -m unittest discover -s "$ROOT/tests" -v > "$BUILD/logs/tests.log" 2>&1
|
||||||
|
export D4R_SKIP_BUILD=1
|
||||||
|
export D4R_MAX_GLIBC=2.41
|
||||||
|
export D4R_BUILD_INFO="$BUILD/toolchain.txt"
|
||||||
|
export D4R_PACKAGE_KERNEL_JOBS=4
|
||||||
|
"$ROOT/scripts/package_release.sh" "$BUILD/dist" > "$BUILD/logs/package.log" 2>&1
|
||||||
|
echo "Release packaged in $BUILD/dist"
|
||||||
@@ -7,7 +7,13 @@ WINEBUILD="${WINEBUILD:-winebuild}"
|
|||||||
BRIDGE_ROOT="$ROOT/build/wine-nvcuda"
|
BRIDGE_ROOT="$ROOT/build/wine-nvcuda"
|
||||||
|
|
||||||
mkdir -p "$BRIDGE_ROOT/x86_64-unix" "$BRIDGE_ROOT/x86_64-windows"
|
mkdir -p "$BRIDGE_ROOT/x86_64-unix" "$BRIDGE_ROOT/x86_64-windows"
|
||||||
|
MOTION_FLAGS=()
|
||||||
|
if [[ -x "${D4R_ROCM_DIR:-/opt/rocm}/lib/llvm/bin/clang++" ]]; then
|
||||||
|
"$ROOT/scripts/build_d4r_motion_kernels.sh" "$BRIDGE_ROOT/generated"
|
||||||
|
MOTION_FLAGS=(-DD4R_MOTION_KERNELS -I"$BRIDGE_ROOT/generated")
|
||||||
|
fi
|
||||||
"$WINEGCC" -m64 -shared -o "$BRIDGE_ROOT/x86_64-unix/nvcuda.dll" \
|
"$WINEGCC" -m64 -shared -o "$BRIDGE_ROOT/x86_64-unix/nvcuda.dll" \
|
||||||
|
"${MOTION_FLAGS[@]}" \
|
||||||
"$ROOT/tools/wine_nvcuda_bridge.c" \
|
"$ROOT/tools/wine_nvcuda_bridge.c" \
|
||||||
-Wb,--export="$ROOT/tools/wine_nvcuda_bridge.spec" -ldl -lpthread
|
-Wb,--export="$ROOT/tools/wine_nvcuda_bridge.spec" -ldl -lpthread
|
||||||
"$WINEBUILD" --dll --fake-module -E "$ROOT/tools/wine_nvcuda_bridge.spec" \
|
"$WINEBUILD" --dll --fake-module -E "$ROOT/tools/wine_nvcuda_bridge.spec" \
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ mkdir -p "$WORK_ROOT"
|
|||||||
SOURCE="$ZLUDA_SOURCE_ROOT/ptx/lib/zluda_ptx_impl.cpp"
|
SOURCE="$ZLUDA_SOURCE_ROOT/ptx/lib/zluda_ptx_impl.cpp"
|
||||||
PTX_LIB="$ZLUDA_SOURCE_ROOT/ptx/lib"
|
PTX_LIB="$ZLUDA_SOURCE_ROOT/ptx/lib"
|
||||||
COMMON_FLAGS=(-DHIP_ENABLE_WARP_SYNC_BUILTINS -std=c++20 -Xclang -fdenormal-fp-math=dynamic
|
COMMON_FLAGS=(-DHIP_ENABLE_WARP_SYNC_BUILTINS -std=c++20 -Xclang -fdenormal-fp-math=dynamic
|
||||||
|
--rocm-path="$ROCM_ROOT" -I"$ROCM_ROOT/include"
|
||||||
-Wall -Wextra -Wsign-compare -Wconversion -x hip "$SOURCE" -nogpulib -O3
|
-Wall -Wextra -Wsign-compare -Wconversion -x hip "$SOURCE" -nogpulib -O3
|
||||||
-mno-wavefrontsize64 --offload-device-only --offload-arch=gfx1030 -emit-llvm -c
|
-mno-wavefrontsize64 --offload-device-only --offload-arch=gfx1030 -emit-llvm -c
|
||||||
-Xclang -mlink-bitcode-file -Xclang "$OCML" "${EXTRA_FLAGS[@]}")
|
-Xclang -mlink-bitcode-file -Xclang "$OCML" "${EXTRA_FLAGS[@]}")
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ def main(argv):
|
|||||||
return
|
return
|
||||||
|
|
||||||
env = {}
|
env = {}
|
||||||
|
env["D4R_PREFER_ACCURACY"] = "1" if flag("Kernels", "PreferAccuracy") else "0"
|
||||||
|
|
||||||
model = get("DLSS", "Model")
|
model = get("DLSS", "Model")
|
||||||
if model is not None:
|
if model is not None:
|
||||||
@@ -132,18 +133,24 @@ def main(argv):
|
|||||||
# RDNA4's native FP8 WMMA (on unless set off; ZLUDA and the bridge ignore it on other GPUs)
|
# RDNA4's native FP8 WMMA (on unless set off; ZLUDA and the bridge ignore it on other GPUs)
|
||||||
value = flag("Kernels", "NativeFp8")
|
value = flag("Kernels", "NativeFp8")
|
||||||
env["D4R_ZLUDA_WMMA_FP8_NATIVE"] = "0" if value is False else "1"
|
env["D4R_ZLUDA_WMMA_FP8_NATIVE"] = "0" if value is False else "1"
|
||||||
|
value = flag("Kernels", "NativeSwinEncoders")
|
||||||
|
env["D4R_NATIVE_SWIN_ENCODERS"] = "0" if value is False else "1"
|
||||||
value = get("Kernels", "ImplicitMaxBlock")
|
value = get("Kernels", "ImplicitMaxBlock")
|
||||||
if value:
|
if value:
|
||||||
env["D4R_ZLUDA_IMPLICIT_MAX_BLOCK"] = value
|
env["D4R_ZLUDA_IMPLICIT_MAX_BLOCK"] = value
|
||||||
|
|
||||||
for key, var in (("VramInterop", "D4R_SHIM_VRAM_INTEROP"), ("InputSync", "D4R_SHIM_INPUT_SYNC"),
|
for key, var in (("VramInterop", "D4R_SHIM_VRAM_INTEROP"), ("InputSync", "D4R_SHIM_INPUT_SYNC"),
|
||||||
("GpuWait", "D4R_SHIM_GPU_WAIT"), ("LinearInputs", "D4R_SHIM_LINEAR_INPUTS"),
|
("LinearInputs", "D4R_SHIM_LINEAR_INPUTS"),
|
||||||
("DirectOutput", "D4R_SHIM_OUTPUT_DIRECT"), ("ElideNgxSync", "D4R_ELIDE_NGX_SYNC"),
|
("DirectOutput", "D4R_SHIM_OUTPUT_DIRECT"), ("ElideNgxSync", "D4R_ELIDE_NGX_SYNC"),
|
||||||
("EvalSync", "D4R_SHIM_EVAL_SYNC")):
|
("EvalSync", "D4R_SHIM_EVAL_SYNC")):
|
||||||
value = flag("Interop", key)
|
value = flag("Interop", key)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
env[var] = "1" if value else "0"
|
env[var] = "1" if value else "0"
|
||||||
value = get("Interop", "MarkerPollUs")
|
value = get("Interop", "MarkerPollUs")
|
||||||
|
dilation = get("Interop", "MotionVectorDilation")
|
||||||
|
if dilation is not None and dilation not in ("0", "1", "2"):
|
||||||
|
fail("[Interop] MotionVectorDilation must be 0, 1 or 2")
|
||||||
|
env["D4R_MOTION_DILATION"] = dilation or "0"
|
||||||
if value:
|
if value:
|
||||||
if not value.isdigit():
|
if not value.isdigit():
|
||||||
fail(f"[Interop] MarkerPollUs must be a number of microseconds, not {value!r}")
|
fail(f"[Interop] MarkerPollUs must be a number of microseconds, not {value!r}")
|
||||||
|
|||||||
@@ -20,12 +20,17 @@
|
|||||||
# D4R_VKD3D_SRC vkd3d-proton source checkout, for its license files default ~/.cache/d4r-vkd3d-proton
|
# D4R_VKD3D_SRC vkd3d-proton source checkout, for its license files default ~/.cache/d4r-vkd3d-proton
|
||||||
# D4R_ZLUDA_SRC ZLUDA source checkout, for its license files default: D4R_ZLUDA_DIR's ../d4r-zluda-upstream
|
# D4R_ZLUDA_SRC ZLUDA source checkout, for its license files default: D4R_ZLUDA_DIR's ../d4r-zluda-upstream
|
||||||
# SOURCE_DATE_EPOCH timestamp given to every packaged file default: the last commit's
|
# SOURCE_DATE_EPOCH timestamp given to every packaged file default: the last commit's
|
||||||
|
# D4R_BUILD_INFO optional build-toolchain/provenance text to include in d4r/source/
|
||||||
|
# D4R_PACKAGE_KERNEL_JOBS independent target builds at once (default 1)
|
||||||
# The zip also contains NVIDIA's files and the texture kernels built from NVIDIA's PTX; redistributing
|
# The zip also contains NVIDIA's files and the texture kernels built from NVIDIA's PTX; redistributing
|
||||||
# those is up to whoever publishes it (they are not covered by d4r's license):
|
# those is up to whoever publishes it (they are not covered by d4r's license):
|
||||||
# D4R_BUNDLE_DLSS nvngx_dlss.dll to include D4R_BUNDLE_NGX _nvngx.dll to include
|
# D4R_BUNDLE_DLSS nvngx_dlss.dll to include D4R_BUNDLE_NGX _nvngx.dll to include
|
||||||
# D4R_BUNDLE_TEX directory with texture-kernel code objects (kernels/build.sh tex), one subdirectory
|
# D4R_BUNDLE_TEX directory with texture-kernel code objects (kernels/build.sh tex), one subdirectory
|
||||||
# per target folder (gfx1101, gfx1201, gfx1201-fp8, ...), or a flat gfx1101 directory for
|
# per target folder (gfx1101, gfx1201, gfx1201-fp8, ...), or a flat gfx1101 directory for
|
||||||
# older builds
|
# older builds
|
||||||
|
# Accuracy texture sets go in accuracy/<target>/, built with D4R_PREFER_ACCURACY=1.
|
||||||
|
# D4R_ZLUDA_EMIT d4r_emit from the patched ZLUDA build; required in full builds when an accuracy
|
||||||
|
# texture set or L's unfolded texture variants are not supplied in D4R_BUNDLE_TEX.
|
||||||
# D4R_BUNDLE_NVIDIA=0 leaves them out (d4r-VERSION-nonvidia.zip; users then add the two DLLs themselves).
|
# D4R_BUNDLE_NVIDIA=0 leaves them out (d4r-VERSION-nonvidia.zip; users then add the two DLLs themselves).
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -121,12 +126,31 @@ for arch in $ARCHS; do
|
|||||||
folders+=("$arch")
|
folders+=("$arch")
|
||||||
[[ "$arch" == gfx12* ]] && folders+=("$arch-fp8")
|
[[ "$arch" == gfx12* ]] && folders+=("$arch-fp8")
|
||||||
done
|
done
|
||||||
for folder in "${folders[@]}"; do
|
# Older texture bundles predate L. Complete them from the user's DLSS DLL rather
|
||||||
|
# than silently shipping only M's folded variants under the new L support claim.
|
||||||
|
ensure_l_textures() {
|
||||||
|
local dir="$1" accuracy="$2" missing=0 mv range v name
|
||||||
|
local names=(rrlite_dec0_4x4)
|
||||||
|
for mv in mvhi mvlo; do
|
||||||
|
for range in hdr ldr; do
|
||||||
|
names+=("rrlite_enc0_4x4_${mv}_${range}")
|
||||||
|
for v in 3_1 3_2; do names+=("rrlite_post_${v}_${mv}_${range}"); done
|
||||||
|
done
|
||||||
|
done
|
||||||
|
for name in "${names[@]}"; do [[ -f "$dir/$name.hsaco" ]] || missing=1; done
|
||||||
|
if [[ "$missing" == 1 ]]; then
|
||||||
|
: "${D4R_ZLUDA_EMIT:?set D4R_ZLUDA_EMIT or supply the unfolded L texture variants in D4R_BUNDLE_TEX}"
|
||||||
|
D4R_PREFER_ACCURACY="$accuracy" D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" \
|
||||||
|
D4R_DLSS_DLL="$D4R_BUNDLE_DLSS" "$ROOT/kernels/build.sh" l "$dir" >/dev/null
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
build_target() {
|
||||||
|
local folder="$1" arch fp8 tex_dir accurate accurate_tex f
|
||||||
arch="${folder%-fp8}"
|
arch="${folder%-fp8}"
|
||||||
fp8=0
|
fp8=0
|
||||||
[[ "$folder" == *-fp8 ]] && fp8=1
|
[[ "$folder" == *-fp8 ]] && fp8=1
|
||||||
D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" "$ROOT/kernels/build.sh" k "$STAGE/d4r/kernels/$folder" >/dev/null
|
D4R_PREFER_ACCURACY=0 D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" "$ROOT/kernels/build.sh" k "$STAGE/d4r/kernels/$folder" >/dev/null
|
||||||
D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" "$ROOT/kernels/build.sh" m "$STAGE/d4r/kernels/$folder" >/dev/null
|
D4R_PREFER_ACCURACY=0 D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" "$ROOT/kernels/build.sh" m "$STAGE/d4r/kernels/$folder" >/dev/null
|
||||||
rm -f "$STAGE/d4r/kernels/$folder"/*.resolution.txt # empty LTO notes from clang's -save-temps
|
rm -f "$STAGE/d4r/kernels/$folder"/*.resolution.txt # empty LTO notes from clang's -save-temps
|
||||||
if [[ "$VARIANT" == full ]]; then
|
if [[ "$VARIANT" == full ]]; then
|
||||||
tex_dir=""
|
tex_dir=""
|
||||||
@@ -142,8 +166,52 @@ for folder in "${folders[@]}"; do
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
[[ "$VARIANT" != full ]] || ensure_l_textures "$STAGE/d4r/kernels/$folder" 0
|
||||||
python3 "$ROOT/kernels/tools/kernel_manifest.py" "$STAGE/d4r/kernels/$folder" "${DLLS[@]}"
|
python3 "$ROOT/kernels/tools/kernel_manifest.py" "$STAGE/d4r/kernels/$folder" "${DLLS[@]}"
|
||||||
|
|
||||||
|
# Every release target also gets conservative network and texture variants. Do not copy fast
|
||||||
|
# texture objects into this set: their compiler policy is fixed inside the binary.
|
||||||
|
accurate="$STAGE/d4r/kernels/accuracy/$folder"
|
||||||
|
D4R_PREFER_ACCURACY=1 D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" "$ROOT/kernels/build.sh" k "$accurate" >/dev/null
|
||||||
|
D4R_PREFER_ACCURACY=1 D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" "$ROOT/kernels/build.sh" m "$accurate" >/dev/null
|
||||||
|
if [[ "$VARIANT" == full ]]; then
|
||||||
|
accurate_tex="$D4R_BUNDLE_TEX/accuracy/$folder"
|
||||||
|
if [[ -f "$accurate_tex/d4r-accuracy.txt" && "$(cat "$accurate_tex/d4r-accuracy.txt")" == 1 ]]; then
|
||||||
|
for f in "$accurate_tex"/*.hsaco; do
|
||||||
|
[[ -f "$f" ]] || continue
|
||||||
|
[[ -e "$accurate/$(basename "$f")" ]] || cp "$f" "$accurate/"
|
||||||
done
|
done
|
||||||
|
else
|
||||||
|
: "${D4R_ZLUDA_EMIT:?set D4R_ZLUDA_EMIT or supply D4R_BUNDLE_TEX/accuracy/$folder}"
|
||||||
|
D4R_PREFER_ACCURACY=1 D4R_ROCM_DIR="$ROCM" D4R_GPU_ARCH="$arch" D4R_NATIVE_FP8="$fp8" \
|
||||||
|
D4R_DLSS_DLL="$D4R_BUNDLE_DLSS" "$ROOT/kernels/build.sh" tex "$accurate" >/dev/null
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [[ "$VARIANT" == full ]]; then
|
||||||
|
ensure_l_textures "$accurate" 1
|
||||||
|
for f in "$STAGE/d4r/kernels/$folder"/*.hsaco; do
|
||||||
|
[[ -f "$accurate/$(basename "$f")" ]] || {
|
||||||
|
echo "missing accuracy variant of $(basename "$f") for $folder" >&2; exit 2;
|
||||||
|
}
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
rm -f "$accurate"/*.resolution.txt
|
||||||
|
python3 "$ROOT/kernels/tools/kernel_manifest.py" "$accurate" "${DLLS[@]}"
|
||||||
|
}
|
||||||
|
kernel_jobs="${D4R_PACKAGE_KERNEL_JOBS:-1}"
|
||||||
|
[[ "$kernel_jobs" =~ ^[1-9][0-9]*$ ]] || { echo "D4R_PACKAGE_KERNEL_JOBS must be positive" >&2; exit 2; }
|
||||||
|
pending=()
|
||||||
|
kernel_failed=0
|
||||||
|
for folder in "${folders[@]}"; do
|
||||||
|
build_target "$folder" &
|
||||||
|
pending+=("$!")
|
||||||
|
if (( ${#pending[@]} >= kernel_jobs )); then
|
||||||
|
wait "${pending[0]}" || kernel_failed=1
|
||||||
|
pending=("${pending[@]:1}")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
for pid in "${pending[@]}"; do wait "$pid" || kernel_failed=1; done
|
||||||
|
[[ "$kernel_failed" == 0 ]] || { echo "native target build failed; no release ZIP created" >&2; exit 1; }
|
||||||
|
|
||||||
# licenses and sources
|
# licenses and sources
|
||||||
ZLUDA_SRC="${D4R_ZLUDA_SRC:-$(dirname "$ZLUDA")/d4r-zluda-upstream}"
|
ZLUDA_SRC="${D4R_ZLUDA_SRC:-$(dirname "$ZLUDA")/d4r-zluda-upstream}"
|
||||||
@@ -168,6 +236,9 @@ file_version() { [[ -f "$1" ]] && strings -el "$1" | grep -A1 '^FileVersion$' |
|
|||||||
sed -e "s/@VERSION@/$VERSION/g" -e "s/@ZLUDA_COMMIT@/$ZLUDA_COMMIT/g" -e "s/@VKD3D_COMMIT@/$VKD3D_COMMIT/g" \
|
sed -e "s/@VERSION@/$VERSION/g" -e "s/@ZLUDA_COMMIT@/$ZLUDA_COMMIT/g" -e "s/@VKD3D_COMMIT@/$VKD3D_COMMIT/g" \
|
||||||
-e "s/@DLSS_VERSION@/$(file_version "$STAGE/d4r/nvngx_dlss.dll")/g" -e "s/@NGX_VERSION@/$(file_version "$STAGE/d4r/ngx/_nvngx.dll")/g" \
|
-e "s/@DLSS_VERSION@/$(file_version "$STAGE/d4r/nvngx_dlss.dll")/g" -e "s/@NGX_VERSION@/$(file_version "$STAGE/d4r/ngx/_nvngx.dll")/g" \
|
||||||
"$ROOT/packaging/SOURCES.txt" | variant > "$STAGE/d4r/source/SOURCES.txt"
|
"$ROOT/packaging/SOURCES.txt" | variant > "$STAGE/d4r/source/SOURCES.txt"
|
||||||
|
if [[ -n "${D4R_BUILD_INFO:-}" ]]; then
|
||||||
|
cp "$D4R_BUILD_INFO" "$STAGE/d4r/source/BUILD_INFO.txt"
|
||||||
|
fi
|
||||||
sed -e "s/@VERSION@/$VERSION/g" "$ROOT/packaging/D4R_README.txt" | variant | sed 's/$/\r/' > "$STAGE/D4R_README.txt"
|
sed -e "s/@VERSION@/$VERSION/g" "$ROOT/packaging/D4R_README.txt" | variant | sed 's/$/\r/' > "$STAGE/D4R_README.txt"
|
||||||
|
|
||||||
# One timestamp for every file (SOURCE_DATE_EPOCH, default the last commit): ZLUDA's kernel cache is keyed on
|
# One timestamp for every file (SOURCE_DATE_EPOCH, default the last commit): ZLUDA's kernel cache is keyed on
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([Parameter(Mandatory=$true)][string]$Module,
|
||||||
|
[Parameter(Mandatory=$true)][string]$Candidate,
|
||||||
|
[Parameter(Mandatory=$true)][string]$FixtureDirectory,
|
||||||
|
[string]$HipRoot, [string]$Probe, [string]$OutputDirectory,
|
||||||
|
[ValidateRange(1,10000)][int]$Pairs=32,
|
||||||
|
[ValidateRange(1,256)][int[]]$Batches=@(1,64),
|
||||||
|
[ValidateSet('events','dispatch')][string[]]$Timings=@('events','dispatch'))
|
||||||
|
$ErrorActionPreference='Stop'
|
||||||
|
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||||
|
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||||
|
if (!$Probe) { $Probe=Join-Path $repo 'build/windows-rdna4-therock/d4r_native_replay_probe.exe' }
|
||||||
|
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/native-benchmark-'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||||
|
& python.exe (Join-Path $repo 'tools/windows/benchmark_native_replay.py') --probe $Probe --hip-root $HipRoot `
|
||||||
|
--module $Module --candidate $Candidate --fixture-dir $FixtureDirectory --output-dir $OutputDirectory `
|
||||||
|
--pairs $Pairs --batches @Batches --timings @Timings
|
||||||
|
if ($LASTEXITCODE) { throw "Native benchmark failed. Logs: $OutputDirectory" }
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([string]$AdlxRoot, [string]$BuildDirectory, [string]$Compiler)
|
||||||
|
$ErrorActionPreference='Stop'
|
||||||
|
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||||
|
$commit='32b5a740d42295c5dfe9026b9f52683da0f3af91'
|
||||||
|
if (!$AdlxRoot) { $AdlxRoot=Join-Path $repo 'external/ADLX' }
|
||||||
|
if (!$BuildDirectory) { $BuildDirectory=Join-Path $repo 'build/gpu-telemetry' }
|
||||||
|
if (!$Compiler) { $Compiler=Join-Path $repo '.tools/llvm-mingw-20260922-ucrt-x86_64/bin/x86_64-w64-mingw32-clang.exe' }
|
||||||
|
if (!(Test-Path -LiteralPath $AdlxRoot)) {
|
||||||
|
& git clone --no-checkout https://github.com/GPUOpen-LibrariesAndSDKs/ADLX.git $AdlxRoot
|
||||||
|
if ($LASTEXITCODE) { throw 'ADLX SDK download failed.' }
|
||||||
|
& git -C $AdlxRoot checkout --detach $commit
|
||||||
|
if ($LASTEXITCODE) { throw 'ADLX SDK checkout failed.' }
|
||||||
|
}
|
||||||
|
if ((& git -C $AdlxRoot rev-parse HEAD).Trim() -ne $commit -or (& git -C $AdlxRoot status --porcelain)) {
|
||||||
|
throw "Expected clean ADLX SDK $commit; use a separate AdlxRoot."
|
||||||
|
}
|
||||||
|
if (!(Test-Path -LiteralPath $Compiler)) { throw 'Set Compiler to an x64 LLVM-MinGW clang.exe.' }
|
||||||
|
New-Item -ItemType Directory -Force $BuildDirectory | Out-Null
|
||||||
|
$exe=Join-Path $BuildDirectory 'd4r_gpu_telemetry.exe'
|
||||||
|
& $Compiler -std=c11 -O2 -Wall -Wextra -Wno-unknown-pragmas -DWIN32_LEAN_AND_MEAN -D_M_AMD64 `
|
||||||
|
-isystem "$AdlxRoot/SDK/Include" (Join-Path $repo 'tools/windows/gpu_telemetry.c') -o $exe
|
||||||
|
if ($LASTEXITCODE) { throw "GPU telemetry compile failed: $LASTEXITCODE" }
|
||||||
|
@{sdkCommit=$commit; sdkLicense=(Join-Path $AdlxRoot 'ADLX SDK License Agreement.pdf');
|
||||||
|
executable=$exe; compiler=$Compiler; sha256=(Get-FileHash -LiteralPath $exe -Algorithm SHA256).Hash} |
|
||||||
|
ConvertTo-Json | Set-Content -LiteralPath (Join-Path $BuildDirectory 'build-info.json') -Encoding UTF8
|
||||||
|
Write-Host "Built local read-only telemetry: $exe"
|
||||||
|
# The SDK, driver DLL and SDK-dependent binary are not added to public packages.
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([ValidateSet('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0')]
|
||||||
|
[string]$Layer='enc1', [ValidateSet('source','cu','wgp')][string]$Mode='source',
|
||||||
|
[ValidateSet('baseline','load','store','both')][string]$NonTemporal='baseline',
|
||||||
|
[ValidateSet(0,1,2,4)][int]$PosTiles=0, [switch]$ShareValues, [switch]$PrivateValues,
|
||||||
|
[switch]$PackedAccumulator,
|
||||||
|
[string]$HipRoot, [string]$OutputDirectory)
|
||||||
|
$ErrorActionPreference='Stop'
|
||||||
|
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||||
|
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||||
|
if ($ShareValues -and $PrivateValues) { throw 'Choose shared or private V storage.' }
|
||||||
|
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo "build/native-k-experiment-$Layer-$Mode-$NonTemporal-pos$PosTiles-share$([int][bool]$ShareValues)-private$([int][bool]$PrivateValues)-packed$([int][bool]$PackedAccumulator)" }
|
||||||
|
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||||
|
foreach ($baseline in @('build/native-k-gfx1201','build/windows-rdna4-therock','dist/windows-rdna4-command-list')) {
|
||||||
|
if ($OutputDirectory.StartsWith([IO.Path]::GetFullPath((Join-Path $repo $baseline)), [StringComparison]::OrdinalIgnoreCase)) {
|
||||||
|
throw 'Keep experimental objects outside the validated baseline/package.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$compiler=Join-Path $HipRoot 'lib/llvm/bin/clang++.exe'
|
||||||
|
if (!(Test-Path -LiteralPath $compiler)) { $compiler=Join-Path $HipRoot 'bin/clang++.exe' }
|
||||||
|
$name="dltss_pwin_${Layer}_layer"
|
||||||
|
$source=Join-Path $repo "kernels/k/$name.hip"
|
||||||
|
$flags=@()
|
||||||
|
if ((Get-Content -LiteralPath $source -Raw) -match '// d4r-build-flags: ([^\r\n]+)') { $flags=$Matches[1].Trim().Split(' ') }
|
||||||
|
if ($Mode -ne 'source') {
|
||||||
|
$flags=@($flags | Where-Object { $_ -ne '-mcumode' -and $_ -ne '-mno-cumode' })
|
||||||
|
$flags+=if ($Mode -eq 'cu') { '-mcumode' } else { '-mno-cumode' }
|
||||||
|
}
|
||||||
|
if ($NonTemporal -in @('load','both')) { $flags+='-DPWIN_NT_LOAD' }
|
||||||
|
if ($NonTemporal -in @('store','both')) { $flags+='-DPWIN_NT_STORE' }
|
||||||
|
if ($PosTiles) {
|
||||||
|
if ($Layer -notin @('enc0','enc1','dec0')) { throw 'Token tiling is audited for position-only layers only.' }
|
||||||
|
$flags+=if ($Layer -eq 'enc1') { "-DD4R_K_ENC1_POS_MT=$PosTiles" } else { "-DD4R_K_POS_MT=$PosTiles" }
|
||||||
|
}
|
||||||
|
if ($ShareValues) {
|
||||||
|
if ($Layer -notin @('enc0','dec0') -and !($Layer -eq 'enc1' -and $PosTiles)) {
|
||||||
|
throw 'ShareValues requires a position-only template.'
|
||||||
|
}
|
||||||
|
$flags+='-DPOS_VSHARE'
|
||||||
|
}
|
||||||
|
if ($PackedAccumulator) { $flags+='-DD4R_K_PACKED_ACC' }
|
||||||
|
if ($PrivateValues) { $flags+='-DD4R_K_PRIVATE_V' }
|
||||||
|
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||||
|
$object=Join-Path $OutputDirectory "$name.hsaco"
|
||||||
|
$arguments=@('-x','hip','-std=c++17','--offload-arch=gfx1201','--offload-device-only',
|
||||||
|
'--no-gpu-bundle-output','-mno-wavefrontsize64','-nogpuinc','-nogpulib','-O3',
|
||||||
|
'-DD4R_DEVICE_ONLY_MINIMAL','-DD4R_K_FP16_BASELINE')+$flags+@($source,'-o',$object)
|
||||||
|
$ErrorActionPreference='Continue'
|
||||||
|
& $compiler @arguments > (Join-Path $OutputDirectory "$name.build.stdout.log") 2> (Join-Path $OutputDirectory "$name.build.stderr.log")
|
||||||
|
$ErrorActionPreference='Stop'
|
||||||
|
if ($LASTEXITCODE) { throw "Experimental K compile failed: $Layer" }
|
||||||
|
& (Join-Path (Split-Path $compiler) 'llvm-objdump.exe') --disassemble --mcpu=gfx1201 $object `
|
||||||
|
> (Join-Path $OutputDirectory "$name.isa.txt")
|
||||||
|
if ($LASTEXITCODE) { throw 'Native object ISA extraction failed.' }
|
||||||
|
& (Join-Path (Split-Path $compiler) 'llvm-readobj.exe') --notes $object `
|
||||||
|
> (Join-Path $OutputDirectory "$name.metadata.txt")
|
||||||
|
if ($LASTEXITCODE) { throw 'Native object metadata extraction failed.' }
|
||||||
|
@{layer=$Layer; architecture='gfx1201'; experimental=$true; numericalValidation='pending';
|
||||||
|
mode=$Mode; nonTemporal=$NonTemporal; tokenTilesPerWave=$PosTiles; shareValues=[bool]$ShareValues;
|
||||||
|
privateValues=[bool]$PrivateValues;
|
||||||
|
packedAccumulator=[bool]$PackedAccumulator;
|
||||||
|
compiler=$compiler; arguments=$arguments;
|
||||||
|
sourceCommit=(git -C $repo rev-parse HEAD); workingTreeDirty=[bool](& git -C $repo status --porcelain);
|
||||||
|
compilerVersion=(& $compiler --version | Out-String).Trim();
|
||||||
|
sha256=(Get-FileHash -LiteralPath $object -Algorithm SHA256).Hash} |
|
||||||
|
ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $OutputDirectory "$name.build-info.json") -Encoding UTF8
|
||||||
|
Write-Host "Compiled gfx1201 K experiment: $Layer mode=$Mode nonTemporal=$NonTemporal posTiles=$PosTiles shareValues=$([bool]$ShareValues). Validate before installing."
|
||||||
|
# No override manifest or game installation is produced by this script.
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([ValidateSet('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0')]
|
||||||
|
[string]$Layer='enc1', [string]$HipRoot, [string]$OutputDirectory)
|
||||||
|
$ErrorActionPreference='Stop'
|
||||||
|
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||||
|
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||||
|
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo 'build/native-k-f16-wmma-experimental' }
|
||||||
|
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||||
|
if ($OutputDirectory -eq [IO.Path]::GetFullPath((Join-Path $repo 'build/native-k-gfx1201'))) {
|
||||||
|
throw 'Keep the validated baseline separate from this experimental build.'
|
||||||
|
}
|
||||||
|
$compiler=Join-Path $HipRoot 'lib/llvm/bin/clang++.exe'
|
||||||
|
if (!(Test-Path -LiteralPath $compiler)) { $compiler=Join-Path $HipRoot 'bin/clang++.exe' }
|
||||||
|
if (!(Test-Path -LiteralPath $compiler)) { throw "HIP compiler missing under $HipRoot" }
|
||||||
|
$name="dltss_pwin_${Layer}_layer"
|
||||||
|
$source=Join-Path $repo "kernels/k/$name.hip"
|
||||||
|
$flags=@()
|
||||||
|
if ((Get-Content -LiteralPath $source -Raw) -match '// d4r-build-flags: ([^\r\n]+)') { $flags=$Matches[1].Trim().Split(' ') }
|
||||||
|
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||||
|
$object=Join-Path $OutputDirectory "$name.hsaco"
|
||||||
|
$ErrorActionPreference='Continue'
|
||||||
|
& $compiler -x hip -std=c++17 --offload-arch=gfx1201 --offload-device-only --no-gpu-bundle-output `
|
||||||
|
-mno-wavefrontsize64 -nogpuinc -nogpulib -O3 -DD4R_DEVICE_ONLY_MINIMAL `
|
||||||
|
-DD4R_K_FP16_BASELINE -DD4R_K_F16_WMMA @flags $source -o $object `
|
||||||
|
> (Join-Path $OutputDirectory "$name.build.stdout.log") 2> (Join-Path $OutputDirectory "$name.build.stderr.log")
|
||||||
|
$ErrorActionPreference='Stop'
|
||||||
|
if ($LASTEXITCODE) { throw "Experimental K FP16 WMMA compile failed: $Layer" }
|
||||||
|
$isa=Join-Path $OutputDirectory "$name.isa.txt"
|
||||||
|
& (Join-Path (Split-Path $compiler) 'llvm-objdump.exe') --disassemble --mcpu=gfx1201 $object > $isa
|
||||||
|
if ($LASTEXITCODE -or !(Select-String -LiteralPath $isa -Pattern 'v_wmma_f16_16x16x16_f16')) {
|
||||||
|
throw 'Packed FP16 WMMA is absent from ISA; no alternative instruction is accepted.'
|
||||||
|
}
|
||||||
|
& (Join-Path (Split-Path $compiler) 'llvm-readobj.exe') --notes $object `
|
||||||
|
> (Join-Path $OutputDirectory "$name.metadata.txt")
|
||||||
|
if ($LASTEXITCODE) { throw 'Native object metadata extraction failed.' }
|
||||||
|
@{layer=$Layer; architecture='gfx1201'; experimental=$true; numericalValidation='pending';
|
||||||
|
compiler=$compiler; sha256=(Get-FileHash -LiteralPath $object -Algorithm SHA256).Hash} |
|
||||||
|
ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory "$name.build-info.json") -Encoding UTF8
|
||||||
|
Write-Host "Compiled experimental gfx1201 packed FP16 WMMA: $Layer. Validate before selecting another layer."
|
||||||
|
# This script does not generate an override manifest or install into the game.
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([Parameter(Mandatory=$true)][string]$DlssDll, [string]$HipRoot, [string]$OutputDirectory, [switch]$HardwarePacking)
|
||||||
|
$ErrorActionPreference='Stop'
|
||||||
|
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||||
|
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||||
|
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo $(if ($HardwarePacking) { 'build/native-m-fp8-hardware-pack' } else { 'build/native-m-fp8-experimental' }) }
|
||||||
|
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||||
|
if ($OutputDirectory -eq [IO.Path]::GetFullPath((Join-Path $repo 'build/native-m-gfx1201'))) {
|
||||||
|
throw 'Keep the validated FP16 baseline separate from the experimental FP8 build.'
|
||||||
|
}
|
||||||
|
$DlssDll=(Get-Item -LiteralPath $DlssDll -ErrorAction Stop).FullName
|
||||||
|
$compiler=Join-Path $HipRoot 'lib/llvm/bin/clang++.exe'
|
||||||
|
if (!(Test-Path -LiteralPath $compiler)) { $compiler=Join-Path $HipRoot 'bin/clang++.exe' }
|
||||||
|
if (!(Test-Path -LiteralPath $compiler)) { throw "HIP compiler missing under $HipRoot" }
|
||||||
|
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||||
|
$disassembler=Join-Path (Split-Path $compiler) 'llvm-objdump.exe'
|
||||||
|
if (!(Test-Path -LiteralPath $disassembler)) { throw 'HIP llvm-objdump is required to confirm native FP8 instructions.' }
|
||||||
|
foreach ($source in Get-ChildItem -LiteralPath (Join-Path $repo 'kernels/m') -Filter '*.hip' | Sort-Object Name) {
|
||||||
|
$flags=@()
|
||||||
|
if ((Get-Content -LiteralPath $source.FullName -Raw) -match '// d4r-build-flags: ([^\r\n]+)') { $flags=$Matches[1].Trim().Split(' ') }
|
||||||
|
if ($HardwarePacking) { $flags+='-DD4R_FP8_HW_PACK' }
|
||||||
|
$object=Join-Path $OutputDirectory ($source.BaseName+'.hsaco')
|
||||||
|
$ErrorActionPreference='Continue'
|
||||||
|
& $compiler -x hip -std=c++17 --offload-arch=gfx1201 --offload-device-only --no-gpu-bundle-output `
|
||||||
|
-mno-wavefrontsize64 -nogpuinc -nogpulib -O3 -DD4R_DEVICE_ONLY_MINIMAL `
|
||||||
|
-DSWIN_EXACT -DSWIN_EXACT_PV -DD4R_FP8_WMMA @flags $source.FullName `
|
||||||
|
-o $object > (Join-Path $OutputDirectory ($source.BaseName+'.build.stdout.log')) `
|
||||||
|
2> (Join-Path $OutputDirectory ($source.BaseName+'.build.stderr.log'))
|
||||||
|
$ErrorActionPreference='Stop'
|
||||||
|
if ($LASTEXITCODE) { throw "Experimental FP8 compile failed: $($source.Name)" }
|
||||||
|
$isa=Join-Path $OutputDirectory ($source.BaseName+'.isa.txt')
|
||||||
|
& $disassembler --disassemble --mcpu=gfx1201 $object > $isa
|
||||||
|
if ($LASTEXITCODE -or !(Select-String -LiteralPath $isa -Pattern 'v_wmma_f32_16x16x16_fp8_fp8')) {
|
||||||
|
throw "Native FP8 WMMA is absent from $($source.Name); no FP16 fallback is accepted."
|
||||||
|
}
|
||||||
|
if ($HardwarePacking -and !(Select-String -LiteralPath $isa -Pattern 'v_cvt_pk_fp8_f32')) { throw 'Native FP8 operand packing is absent from ISA.' }
|
||||||
|
Write-Host "Compiled gfx1201 FP8 WMMA: $($source.BaseName)"
|
||||||
|
}
|
||||||
|
if ($HardwarePacking) {
|
||||||
|
$probeDirectory=Join-Path $OutputDirectory 'packing-probe'
|
||||||
|
New-Item -ItemType Directory -Force $probeDirectory | Out-Null
|
||||||
|
& $compiler -x hip -std=c++17 --offload-arch=gfx1201 --offload-device-only --no-gpu-bundle-output `
|
||||||
|
-mno-wavefrontsize64 -nogpuinc -nogpulib -O3 -DD4R_DEVICE_ONLY_MINIMAL `
|
||||||
|
-DD4R_FP8_WMMA -DD4R_FP8_HW_PACK -DSWIN_EXACT -DSWIN_EXACT_PV `
|
||||||
|
(Join-Path $repo 'tools/windows/fp8_pack_probe.hip') -o (Join-Path $probeDirectory 'fp8_pack_probe.hsaco')
|
||||||
|
if ($LASTEXITCODE) { throw 'Native hardware packing diagnostic compile failed.' }
|
||||||
|
}
|
||||||
|
& python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $OutputDirectory $DlssDll
|
||||||
|
if ($LASTEXITCODE) { throw 'Experimental FP8 manifest generation failed.' }
|
||||||
|
@{architecture='gfx1201'; family='M'; experimental=$true; nativeFp8=$true; accuracy=$true;
|
||||||
|
numerics='SWIN_EXACT; SWIN_EXACT_PV; FP8 WMMA for e4m3 GEMMs; exact FP16 attention';
|
||||||
|
validation='not validated; do not install before layer/full-network checks';
|
||||||
|
compiler=$compiler; compilerVersion=(& $compiler --version | Select-Object -First 1);
|
||||||
|
sourceCommit=(& git -C $repo rev-parse HEAD).Trim(); nativeFp8IsaVerified=$true; hardwarePacking=[bool]$HardwarePacking;
|
||||||
|
sourceWorkingTreeDirty=[bool](& git -C $repo status --porcelain);
|
||||||
|
dlssSha256=(Get-FileHash -LiteralPath $DlssDll -Algorithm SHA256).Hash;
|
||||||
|
modules=@(Get-ChildItem -LiteralPath $OutputDirectory -Filter '*.hsaco' | Get-FileHash -Algorithm SHA256)} |
|
||||||
|
ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'build-info.json') -Encoding UTF8
|
||||||
|
Write-Host "Experimental native FP8 M objects: $OutputDirectory (baseline unchanged)"
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$true)][string]$DlssDll,
|
||||||
|
[string]$Kernel = 'hiluma_engine_output_depthinv_mvlo_hdr_max_v2_rel',
|
||||||
|
[string]$HipRoot = 'C:\Program Files\AMD\ROCm\7.2',
|
||||||
|
[string]$ZludaRoot,
|
||||||
|
[ValidateSet('cu','wgp')][string]$ShaderMode='cu',
|
||||||
|
[string]$GpuArch='gfx1201',
|
||||||
|
[string]$OutputDirectory
|
||||||
|
)
|
||||||
|
$ErrorActionPreference='Stop'
|
||||||
|
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||||
|
[void](Get-D4RGpuTargetInfo $GpuArch)
|
||||||
|
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||||
|
if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' }
|
||||||
|
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo "build/private-textures-$GpuArch" }
|
||||||
|
& python (Join-Path $repo 'tools/windows/build_native_texture.py') --dlss-dll $DlssDll --hip-root $HipRoot `
|
||||||
|
--zluda-root $ZludaRoot --kernel $Kernel --output-directory $OutputDirectory --shader-mode $ShaderMode --gpu-arch $GpuArch
|
||||||
|
if ($LASTEXITCODE) { throw "Native texture build failed ($LASTEXITCODE); inspect the output work directory." }
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[string]$SourceRoot,
|
||||||
|
[string]$BuildToolsRoot,
|
||||||
|
[string]$InstallRoot,
|
||||||
|
[int]$Jobs = 6
|
||||||
|
)
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||||
|
if (!$SourceRoot) { $SourceRoot = Join-Path $repo 'external/OptiScaler' }
|
||||||
|
if (!$InstallRoot) { $InstallRoot = Join-Path $repo 'dist/optiscaler-windows-d4r' }
|
||||||
|
$SourceRoot = [IO.Path]::GetFullPath($SourceRoot)
|
||||||
|
$InstallRoot = [IO.Path]::GetFullPath($InstallRoot)
|
||||||
|
$base = '45a2001303ddff632e279f77aef85ceede5832cb'
|
||||||
|
$patches = @(Get-ChildItem -LiteralPath (Join-Path $repo 'patches/optiscaler') -Filter '*.patch' | Sort-Object Name)
|
||||||
|
if (!$patches.Count) { throw 'OptiScaler source patches are missing.' }
|
||||||
|
$previousPath = $env:PATH
|
||||||
|
try {
|
||||||
|
# Git for Windows' submodule helper needs its POSIX utility directory.
|
||||||
|
$gitExe = (Get-Command git -ErrorAction Stop).Source
|
||||||
|
$gitInstall = [IO.Path]::GetFullPath((Join-Path (Split-Path $gitExe) '..'))
|
||||||
|
$gitUtilities = Join-Path $gitInstall 'usr/bin'
|
||||||
|
if (Test-Path -LiteralPath $gitUtilities) { $env:PATH = "$gitUtilities;$env:PATH" }
|
||||||
|
if (!(Test-Path -LiteralPath (Join-Path $SourceRoot '.git'))) {
|
||||||
|
& git clone --filter=blob:none --no-checkout https://github.com/optiscaler/OptiScaler.git $SourceRoot
|
||||||
|
if ($LASTEXITCODE) { throw 'OptiScaler clone failed.' }
|
||||||
|
& git -C $SourceRoot checkout --detach $base
|
||||||
|
if ($LASTEXITCODE) { throw 'Pinned OptiScaler checkout failed.' }
|
||||||
|
}
|
||||||
|
& git -C $SourceRoot merge-base --is-ancestor $base HEAD
|
||||||
|
if ($LASTEXITCODE) { throw "OptiScaler must be based on audited commit $base" }
|
||||||
|
foreach ($patch in $patches) {
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
& git -C $SourceRoot apply --reverse --check $patch.FullName 2>$null
|
||||||
|
$alreadyApplied = $LASTEXITCODE -eq 0
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if (!$alreadyApplied) {
|
||||||
|
& git -C $SourceRoot apply --check $patch.FullName
|
||||||
|
if ($LASTEXITCODE) { throw "Source patch conflicts: $($patch.Name); existing changes retained." }
|
||||||
|
& git -C $SourceRoot apply $patch.FullName
|
||||||
|
if ($LASTEXITCODE) { throw "Source patch failed: $($patch.Name)" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# Verify existing checkouts directly. A complete local checkout does not
|
||||||
|
# need Git's shell-based submodule helper (or a network connection).
|
||||||
|
$submodulePaths=@(& git -C $SourceRoot config --file .gitmodules --get-regexp '\.path$' | ForEach-Object { ($_ -split ' ',2)[1] })
|
||||||
|
$submodules=@()
|
||||||
|
foreach ($path in $submodulePaths) {
|
||||||
|
$tree=(& git -C $SourceRoot ls-tree HEAD -- $path) -split '\s+',4
|
||||||
|
if ($tree.Count -lt 4 -or $tree[0] -ne '160000') { throw "Missing pinned submodule: $path" }
|
||||||
|
$checkout=Join-Path $SourceRoot $path
|
||||||
|
$actual=if (Test-Path -LiteralPath (Join-Path $checkout '.git')) { (& git -C $checkout rev-parse HEAD).Trim() } else { '' }
|
||||||
|
if ($actual -ne $tree[2]) {
|
||||||
|
& git -C $SourceRoot submodule update --init --depth 1 -- $path
|
||||||
|
if ($LASTEXITCODE) { throw "Pinned OptiScaler submodule failed: $path" }
|
||||||
|
$actual=(& git -C $checkout rev-parse HEAD).Trim()
|
||||||
|
if ($actual -ne $tree[2]) { throw "Wrong submodule revision: $path" }
|
||||||
|
}
|
||||||
|
$submodules+=(' '+$actual+' '+$path)
|
||||||
|
}
|
||||||
|
if (!$BuildToolsRoot -and (Test-Path -LiteralPath (Join-Path $repo '.tools/vs2022/MSBuild/Current/Bin/MSBuild.exe'))) {
|
||||||
|
$BuildToolsRoot = Join-Path $repo '.tools/vs2022'
|
||||||
|
}
|
||||||
|
if (!$BuildToolsRoot) {
|
||||||
|
$vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/Installer/vswhere.exe'
|
||||||
|
if (Test-Path -LiteralPath $vswhere) {
|
||||||
|
$BuildToolsRoot = & $vswhere -latest -products '*' -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$BuildToolsRoot) { throw 'MSVC v143 Build Tools and Windows SDK 10.0.26100 are required; see patches/optiscaler/README.md.' }
|
||||||
|
$msbuild = Join-Path $BuildToolsRoot 'MSBuild/Current/Bin/MSBuild.exe'
|
||||||
|
if (!(Test-Path -LiteralPath $msbuild)) { throw "MSBuild missing: $msbuild" }
|
||||||
|
New-Item -ItemType Directory -Force $InstallRoot | Out-Null
|
||||||
|
$log = Join-Path $InstallRoot 'build.log'
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
& $msbuild (Join-Path $SourceRoot 'OptiScaler.sln') "/m:$Jobs" /p:Configuration=Release /p:Platform=x64 `
|
||||||
|
/p:PostBuildEventUseInBuild=false /verbosity:quiet "/flp:logfile=$log;verbosity=normal"
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if ($LASTEXITCODE) { throw "OptiScaler build failed ($LASTEXITCODE); see $log" }
|
||||||
|
foreach ($file in @('OptiScaler.dll', 'OptiScaler.pdb')) {
|
||||||
|
Copy-Item -LiteralPath (Join-Path $SourceRoot "x64/Release/$file") -Destination $InstallRoot -Force
|
||||||
|
}
|
||||||
|
Copy-Item -LiteralPath (Join-Path $SourceRoot 'LICENSE') -Destination (Join-Path $InstallRoot 'GPL-3.0.txt') -Force
|
||||||
|
foreach ($patch in $patches) { Copy-Item -LiteralPath $patch.FullName -Destination $InstallRoot -Force }
|
||||||
|
$metadata = @{base=$base; sourceCommit=(& git -C $SourceRoot rev-parse HEAD).Trim();
|
||||||
|
sourceDirty=[bool](& git -C $SourceRoot status --porcelain); backendAbi=1;
|
||||||
|
buildTools=$BuildToolsRoot; msbuildVersion=(& $msbuild -version -nologo | Select-Object -Last 1);
|
||||||
|
patches=@($patches | ForEach-Object { @{name=$_.Name; sha256=(Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash} });
|
||||||
|
dllSha256=(Get-FileHash -LiteralPath (Join-Path $InstallRoot 'OptiScaler.dll') -Algorithm SHA256).Hash;
|
||||||
|
submodules=$submodules}
|
||||||
|
$metadata | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $InstallRoot 'build-info.json') -Encoding UTF8
|
||||||
|
Write-Host "Built OptiScaler with external Windows d4r backend: $InstallRoot"
|
||||||
|
} finally { $env:PATH = $previousPath }
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[string[]]$GpuArchitectures,
|
||||||
|
[ValidateSet('stable','therock')][string]$RuntimeProfile='therock',
|
||||||
|
[string]$ZludaRoot, [string]$DlssDll,
|
||||||
|
[switch]$PackageGames,
|
||||||
|
[string]$BuildRoot, [string]$OutputRoot, [string]$ArchiveDirectory
|
||||||
|
)
|
||||||
|
$ErrorActionPreference='Stop'
|
||||||
|
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||||
|
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||||
|
if (!$GpuArchitectures) { $GpuArchitectures=@(Get-D4RGpuTargets | ForEach-Object { $_.architecture }) }
|
||||||
|
foreach ($arch in $GpuArchitectures) { [void](Get-D4RGpuTargetInfo $arch) }
|
||||||
|
if (@($GpuArchitectures | Select-Object -Unique).Count -ne $GpuArchitectures.Count) { throw 'Duplicate GPU target.' }
|
||||||
|
if (!$BuildRoot) { $BuildRoot=Join-Path $repo 'build/windows-gpu-coverage' }
|
||||||
|
if (!$OutputRoot) { $OutputRoot=Join-Path $repo 'dist/windows-gpu-coverage' }
|
||||||
|
if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' }
|
||||||
|
if ($ArchiveDirectory -and !$PackageGames) { throw 'ArchiveDirectory requires PackageGames.' }
|
||||||
|
if ($PackageGames -and (!$DlssDll -or ![IO.Path]::IsPathRooted($DlssDll) -or !(Test-Path -LiteralPath $DlssDll))) {
|
||||||
|
throw 'PackageGames requires an absolute path to your local nvngx_dlss.dll; it will not be redistributed.'
|
||||||
|
}
|
||||||
|
New-Item -ItemType Directory -Force $BuildRoot | Out-Null
|
||||||
|
if ($ArchiveDirectory) { New-Item -ItemType Directory -Force $ArchiveDirectory | Out-Null }
|
||||||
|
$results=[Collections.Generic.List[object]]::new()
|
||||||
|
foreach ($arch in $GpuArchitectures) {
|
||||||
|
$build="$BuildRoot-$arch"; $diag="$OutputRoot-$arch"
|
||||||
|
$log=Join-Path $BuildRoot "$arch-build.log"
|
||||||
|
Write-Host "Building $arch (compile/package only; no GPU workloads). Log: $log"
|
||||||
|
& (Join-Path $PSScriptRoot 'build-windows-rdna4.ps1') -RuntimeProfile $RuntimeProfile -GpuArch $arch `
|
||||||
|
-ZludaRoot $ZludaRoot -BuildDirectory $build -InstallDirectory $diag *> $log
|
||||||
|
$row=[ordered]@{architecture=$arch; compiled=$true; hardwareTestExecuted=$false; buildDirectory=$build; diagnosticRoot=$diag; log=$log}
|
||||||
|
if ($PackageGames) {
|
||||||
|
$k="$BuildRoot-native-k-$arch"; $m="$BuildRoot-native-m-$arch"; $game="$OutputRoot-game-$arch"
|
||||||
|
& (Join-Path $PSScriptRoot 'stage-native-k.ps1') -DlssDll $DlssDll -PackageRoot $diag -GpuArch $arch -OutputDirectory $k
|
||||||
|
& (Join-Path $PSScriptRoot 'stage-native-m.ps1') -DlssDll $DlssDll -PackageRoot $diag -GpuArch $arch -OutputDirectory $m
|
||||||
|
$packageArgs=@{DiagnosticRoot=$diag; GpuArch=$arch; PackageRoot=$game; ZludaRoot=$ZludaRoot; KernelKRoot=$k; KernelMRoot=$m}
|
||||||
|
if ($ArchiveDirectory) { $packageArgs.ArchivePath=Join-Path $ArchiveDirectory "d4r-windows-$arch.zip" }
|
||||||
|
& (Join-Path $PSScriptRoot 'package-windows-game.ps1') @packageArgs
|
||||||
|
$row.gamePackage=$game
|
||||||
|
}
|
||||||
|
$results.Add($row)
|
||||||
|
$results | ConvertTo-Json -Depth 5 | Set-Content (Join-Path $BuildRoot 'matrix-build.json') -Encoding UTF8
|
||||||
|
Write-Host "Compiled $arch; hardware validation remains separate."
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[string]$HipRoot = $env:HIP_PATH,
|
||||||
|
[ValidateSet('stable', 'therock')][string]$RuntimeProfile = 'stable',
|
||||||
|
[string]$GpuArch = 'gfx1201',
|
||||||
|
[string]$ZludaRoot,
|
||||||
|
[string]$ToolchainRoot,
|
||||||
|
[string]$BuildDirectory,
|
||||||
|
[string]$InstallDirectory
|
||||||
|
)
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||||
|
[void](Get-D4RGpuTargetInfo $GpuArch)
|
||||||
|
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||||
|
if ($RuntimeProfile -eq 'therock') {
|
||||||
|
if (!$PSBoundParameters.ContainsKey('HipRoot')) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||||
|
if (!$BuildDirectory) { $BuildDirectory = Join-Path $repo 'build/windows-rdna4-therock' }
|
||||||
|
if (!$InstallDirectory) { $InstallDirectory = Join-Path $repo 'dist/windows-rdna4-therock' }
|
||||||
|
}
|
||||||
|
if (!$HipRoot) { $HipRoot = 'C:\Program Files\AMD\ROCm\7.2' }
|
||||||
|
if (!$BuildDirectory) { $BuildDirectory = Join-Path $repo 'build/windows-rdna4' }
|
||||||
|
if (!$InstallDirectory) { $InstallDirectory = Join-Path $repo 'dist/windows-rdna4-diagnostics' }
|
||||||
|
if ($GpuArch -ne 'gfx1201') {
|
||||||
|
if (!$PSBoundParameters.ContainsKey('BuildDirectory')) { $BuildDirectory += "-$GpuArch" }
|
||||||
|
if (!$PSBoundParameters.ContainsKey('InstallDirectory')) { $InstallDirectory += "-$GpuArch" }
|
||||||
|
}
|
||||||
|
$HipRoot = [IO.Path]::GetFullPath($HipRoot).TrimEnd('\', '/')
|
||||||
|
$BuildDirectory = [IO.Path]::GetFullPath($BuildDirectory).TrimEnd('\', '/')
|
||||||
|
$InstallDirectory = [IO.Path]::GetFullPath($InstallDirectory).TrimEnd('\', '/')
|
||||||
|
if (!$ZludaRoot -and (Test-Path (Join-Path $repo '.tools/zluda/zluda/nvcuda.dll'))) {
|
||||||
|
$ZludaRoot = Join-Path $repo '.tools/zluda/zluda'
|
||||||
|
}
|
||||||
|
if (!$ToolchainRoot -and (Test-Path (Join-Path $repo '.tools/llvm-mingw-20260922-ucrt-x86_64'))) {
|
||||||
|
$ToolchainRoot = Join-Path $repo '.tools/llvm-mingw-20260922-ucrt-x86_64'
|
||||||
|
}
|
||||||
|
$cmakeCommand = Get-Command cmake -ErrorAction SilentlyContinue
|
||||||
|
$cmake = if ($cmakeCommand) { $cmakeCommand.Source } else { Join-Path $repo '.tools/python/cmake/data/bin/cmake.exe' }
|
||||||
|
$ninjaCommand = Get-Command ninja -ErrorAction SilentlyContinue
|
||||||
|
$ninja = if ($ninjaCommand) { $ninjaCommand.Source } else { Join-Path $repo '.tools/python/ninja/data/bin/ninja.exe' }
|
||||||
|
if (!(Test-Path $ninja)) { $ninja = Join-Path $repo '.tools/python/bin/ninja.exe' }
|
||||||
|
if (!(Test-Path $cmake) -or !(Test-Path $ninja)) {
|
||||||
|
throw 'CMake >=3.24 and Ninja required. See docs/windows-rdna4-port.md for local tool setup.'
|
||||||
|
}
|
||||||
|
New-Item -ItemType Directory -Force $BuildDirectory | Out-Null
|
||||||
|
$minhook = Join-Path $repo 'external/MinHook'
|
||||||
|
if (!(Test-Path (Join-Path $minhook 'include/MinHook.h'))) {
|
||||||
|
& git clone --depth 1 --branch v1.3.4 https://github.com/TsudaKageyu/minhook.git $minhook
|
||||||
|
if ($LASTEXITCODE) { throw 'MinHook 1.3.4 source download failed.' }
|
||||||
|
}
|
||||||
|
$minhookCommit = (& git -C $minhook rev-parse HEAD).Trim()
|
||||||
|
if ($minhookCommit -ne 'c3fcafdc10146beb5919319d0683e44e3c30d537') {
|
||||||
|
throw "Expected audited MinHook 1.3.4 source, found $minhookCommit"
|
||||||
|
}
|
||||||
|
$originalPath = $env:PATH
|
||||||
|
try {
|
||||||
|
$configure = @('--fresh', '-S', $repo, '-B', $BuildDirectory, '-G', 'Ninja', '-DCMAKE_BUILD_TYPE=RelWithDebInfo',
|
||||||
|
"-DCMAKE_MAKE_PROGRAM=$ninja", "-DD4R_HIP_ROOT=$HipRoot", "-DD4R_GPU_ARCH=$GpuArch", "-DCMAKE_INSTALL_PREFIX=$InstallDirectory")
|
||||||
|
if ($ToolchainRoot) {
|
||||||
|
$compiler = Join-Path $ToolchainRoot 'bin/x86_64-w64-mingw32-clang++.exe'
|
||||||
|
if (!(Test-Path $compiler)) { throw "Compiler missing: $compiler" }
|
||||||
|
$env:PATH = "$(Join-Path $ToolchainRoot 'bin');$originalPath"
|
||||||
|
$configure += "-DCMAKE_CXX_COMPILER=$compiler"
|
||||||
|
$configure += "-DCMAKE_C_COMPILER=$(Join-Path $ToolchainRoot 'bin/x86_64-w64-mingw32-clang.exe')"
|
||||||
|
}
|
||||||
|
if ($ZludaRoot) { $configure += "-DD4R_ZLUDA_ROOT=$ZludaRoot" }
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
& $cmake @configure 2>&1 | Tee-Object (Join-Path $BuildDirectory 'configure.log')
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if ($LASTEXITCODE) { throw "CMake configure exit=$LASTEXITCODE" }
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
& $cmake --build $BuildDirectory --parallel 4 2>&1 | Tee-Object (Join-Path $BuildDirectory 'build.log')
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if ($LASTEXITCODE) { throw "Build exit=$LASTEXITCODE" }
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
& $cmake --install $BuildDirectory 2>&1 | Tee-Object (Join-Path $BuildDirectory 'install.log')
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if ($LASTEXITCODE) { throw "Install exit=$LASTEXITCODE" }
|
||||||
|
Write-Host "Built diagnostics: $InstallDirectory"
|
||||||
|
} finally { $env:PATH = $originalPath }
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([string]$SourceRoot, [string]$HipRoot = 'C:\Program Files\AMD\ROCm\7.2', [string]$WorkRoot)
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||||
|
if (!$SourceRoot) { $SourceRoot = Join-Path $repo 'external/ZLUDA' }
|
||||||
|
if (!$WorkRoot) { $WorkRoot = Join-Path $repo 'build/zluda-helpers-windows' }
|
||||||
|
$toolchain = Join-Path $repo '.tools/llvm-mingw-20260922-ucrt-x86_64'
|
||||||
|
$compiler = Join-Path $HipRoot 'bin/clang++.exe'
|
||||||
|
$dis = Join-Path $HipRoot 'bin/llvm-dis.exe'
|
||||||
|
$assembler = Join-Path $HipRoot 'bin/llvm-as.exe'
|
||||||
|
$ocml = Join-Path $HipRoot 'amdgcn/bitcode/ocml.bc'
|
||||||
|
foreach ($file in @($compiler,$dis,$assembler,$ocml)) {
|
||||||
|
if (!(Test-Path $file)) { throw "HIP helper dependency missing: $file" }
|
||||||
|
}
|
||||||
|
New-Item -ItemType Directory -Force $WorkRoot | Out-Null
|
||||||
|
$configInclude = Join-Path $WorkRoot 'libcxx-config'
|
||||||
|
New-Item -ItemType Directory -Force $configInclude | Out-Null
|
||||||
|
Copy-Item -LiteralPath (Join-Path $toolchain 'include/c++/v1/__config_site') -Destination $configInclude -Force
|
||||||
|
Copy-Item -LiteralPath (Join-Path $SourceRoot 'ext/llvm-project/libcxx/vendor/llvm/default_assertion_handler.in') `
|
||||||
|
-Destination (Join-Path $configInclude '__assertion_handler') -Force
|
||||||
|
# HIP 7.2's long shuffle overloads assume every non-MSVC compiler has LP64.
|
||||||
|
# Windows GNU also has 32-bit long. Correct that public header in a private
|
||||||
|
# mirror; the installed SDK remains untouched and the GPU target is unchanged.
|
||||||
|
$hipInclude = Join-Path $WorkRoot 'hip-include'
|
||||||
|
New-Item -ItemType Directory -Force $hipInclude | Out-Null
|
||||||
|
Copy-Item -LiteralPath (Join-Path $HipRoot 'include/hip') -Destination $hipInclude -Recurse -Force
|
||||||
|
$warpHeader = Join-Path $hipInclude 'hip/amd_detail/amd_warp_functions.h'
|
||||||
|
$warpSource = [IO.File]::ReadAllText($warpHeader)
|
||||||
|
$warpSource = $warpSource.Replace('#ifndef _MSC_VER', '#if __SIZEOF_LONG__ == 8')
|
||||||
|
[IO.File]::WriteAllText($warpHeader,$warpSource,[Text.UTF8Encoding]::new($false))
|
||||||
|
$variants = @(
|
||||||
|
@{Suffix=''; Flags=@()},
|
||||||
|
@{Suffix='_constrained'; Flags=@('-ffp-model=strict','-ffp-exception-behavior=ignore')},
|
||||||
|
@{Suffix='_w64'; Flags=@('-mwavefrontsize64','-DD4R_WAVE64')},
|
||||||
|
@{Suffix='_constrained_w64'; Flags=@('-ffp-model=strict','-ffp-exception-behavior=ignore','-mwavefrontsize64','-DD4R_WAVE64')}
|
||||||
|
)
|
||||||
|
foreach ($variant in $variants) {
|
||||||
|
$name = 'zluda_ptx_impl' + $variant.Suffix
|
||||||
|
$raw = Join-Path $WorkRoot "$name.raw.bc"
|
||||||
|
$ir = Join-Path $WorkRoot "$name.ll"
|
||||||
|
$output = Join-Path $SourceRoot "ptx/lib/$name.bc"
|
||||||
|
$arguments = @('-x','hip','-std=c++20','--target=x86_64-w64-windows-gnu','-stdlib=libc++',
|
||||||
|
'--offload-arch=gfx1030','--offload-device-only','-emit-llvm','-c','-nogpulib','-O3',
|
||||||
|
'-mno-wavefrontsize64','-DHIP_ENABLE_WARP_SYNC_BUILTINS',
|
||||||
|
'-Xclang','-fdenormal-fp-math=dynamic',
|
||||||
|
'-isystem',(Join-Path $SourceRoot 'ext/llvm-project/libcxx/include'),
|
||||||
|
'-isystem',$configInclude,
|
||||||
|
'-isystem',(Join-Path $toolchain 'include'),
|
||||||
|
'-I',$hipInclude,
|
||||||
|
'-Xclang','-mlink-bitcode-file','-Xclang',$ocml)
|
||||||
|
$arguments += $variant.Flags
|
||||||
|
$arguments += @('-o',$raw,(Join-Path $SourceRoot 'ptx/lib/zluda_ptx_impl.cpp'))
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
& $compiler @arguments 2>&1 | Tee-Object (Join-Path $WorkRoot "$name.compile.log")
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if ($LASTEXITCODE) { throw "PTX helper compile failed: $name ($LASTEXITCODE)" }
|
||||||
|
& $dis $raw -o $ir
|
||||||
|
if ($LASTEXITCODE) { throw "Disassemble failed: $raw" }
|
||||||
|
$clean = [Collections.Generic.List[string]]::new()
|
||||||
|
foreach ($line in [IO.File]::ReadLines($ir)) {
|
||||||
|
if ($line -match '@llvm.used|wchar_size|llvm.module.flags|__hip_cuid') { continue }
|
||||||
|
$line = $line -replace 'optnone','' -replace 'define hidden','define linkonce_odr'
|
||||||
|
$line = $line -replace '"target-cpu"="gfx1030"','' -replace '"target-features"="[^"]+"',''
|
||||||
|
$clean.Add($line)
|
||||||
|
}
|
||||||
|
[IO.File]::WriteAllLines($ir,$clean,[Text.UTF8Encoding]::new($false))
|
||||||
|
& $assembler $ir -o $output
|
||||||
|
if ($LASTEXITCODE) { throw "Assemble failed: $ir" }
|
||||||
|
Write-Host "Built $output ($((Get-Item $output).Length) bytes)"
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([string]$SourceRoot, [string]$BuildRoot, [int]$Jobs = 4)
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||||
|
if (!$SourceRoot) { $SourceRoot = Join-Path $repo 'external/ZLUDA' }
|
||||||
|
if (!$BuildRoot) { $BuildRoot = Join-Path $repo 'build/zluda-llvm-windows-gnu' }
|
||||||
|
$toolchain = Join-Path $repo '.tools/llvm-mingw-20260922-ucrt-x86_64'
|
||||||
|
$cmake = Join-Path $repo '.tools/python/cmake/data/bin/cmake.exe'
|
||||||
|
$ninja = Join-Path $repo '.tools/python/bin/ninja.exe'
|
||||||
|
if (!(Test-Path $ninja)) { $ninja = Join-Path $repo '.tools/python/ninja/data/bin/ninja.exe' }
|
||||||
|
if (!(Test-Path (Join-Path $SourceRoot 'ext/llvm-project/llvm/CMakeLists.txt'))) {
|
||||||
|
throw 'Run git submodule update --init --depth 1 ext/llvm-project in the ZLUDA checkout.'
|
||||||
|
}
|
||||||
|
New-Item -ItemType Directory -Force $BuildRoot | Out-Null
|
||||||
|
& (Join-Path $PSScriptRoot 'prepare-zluda-llvm-source.ps1') -LlvmSourceRoot (Join-Path $SourceRoot 'ext/llvm-project')
|
||||||
|
$oldPath = $env:PATH
|
||||||
|
try {
|
||||||
|
$env:PATH = "$(Join-Path $toolchain 'bin');$oldPath"
|
||||||
|
$arguments = @('-S', (Join-Path $SourceRoot 'ext/llvm-project/llvm'), '-B', $BuildRoot,
|
||||||
|
'-G', 'Ninja', "-DCMAKE_MAKE_PROGRAM=$ninja", '-DCMAKE_BUILD_TYPE=Release',
|
||||||
|
"-DCMAKE_C_COMPILER=$(Join-Path $toolchain 'bin/x86_64-w64-mingw32-clang.exe')",
|
||||||
|
"-DCMAKE_CXX_COMPILER=$(Join-Path $toolchain 'bin/x86_64-w64-mingw32-clang++.exe')",
|
||||||
|
'-DLLVM_TARGETS_TO_BUILD=AMDGPU', '-DLLVM_ENABLE_PROJECTS=llvm;lld',
|
||||||
|
'-DLLVM_ENABLE_WARNINGS=OFF', '-DLLVM_ENABLE_TERMINFO=OFF', '-DLLVM_ENABLE_LIBXML2=OFF',
|
||||||
|
'-DLLVM_ENABLE_LIBEDIT=OFF', '-DLLVM_ENABLE_LIBPFM=OFF', '-DLLVM_ENABLE_ZLIB=OFF',
|
||||||
|
'-DLLVM_ENABLE_ZSTD=OFF', '-DLLVM_INCLUDE_BENCHMARKS=OFF', '-DLLVM_INCLUDE_EXAMPLES=OFF',
|
||||||
|
'-DLLVM_INCLUDE_TESTS=OFF', '-DLLVM_BUILD_TESTS=OFF', '-DLLVM_BUILD_TOOLS=OFF',
|
||||||
|
'-DLLVM_OPTIMIZED_TABLEGEN=ON', '-DLLVM_PARALLEL_LINK_JOBS=1')
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
& $cmake @arguments 2>&1 | Tee-Object (Join-Path $BuildRoot 'configure.log')
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if ($LASTEXITCODE) { throw "LLVM configure failed ($LASTEXITCODE)" }
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
& $cmake --build $BuildRoot --parallel $Jobs --target llvm-config llvm-libraries lldCommon lldELF `
|
||||||
|
2>&1 | Tee-Object (Join-Path $BuildRoot 'build.log')
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if ($LASTEXITCODE) { throw "LLVM build failed ($LASTEXITCODE)" }
|
||||||
|
& (Join-Path $BuildRoot 'bin/llvm-config.exe') --version
|
||||||
|
if ($LASTEXITCODE) { throw 'llvm-config verification failed' }
|
||||||
|
@{sourceBase=(& git -C (Join-Path $SourceRoot 'ext/llvm-project') rev-parse HEAD).Trim(); fullSourceDeltaVerified=$true;
|
||||||
|
sourcePatches=@(Get-ChildItem (Join-Path $repo 'patches/zluda-llvm') -Filter *.patch | ForEach-Object {
|
||||||
|
@{name=$_.Name; sha256=(Get-FileHash -LiteralPath $_.FullName).Hash}})} |
|
||||||
|
ConvertTo-Json -Depth 5 | Set-Content (Join-Path $BuildRoot 'd4r-llvm-build-info.json') -Encoding UTF8
|
||||||
|
Write-Host "Native Windows LLVM: $BuildRoot"
|
||||||
|
} finally { $env:PATH = $oldPath }
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([string]$SourceRoot, [string]$LlvmBuildRoot, [string]$InstallRoot, [switch]$FetchOnly, [switch]$CacheTestsOnly, [switch]$CompilerTestsOnly, [int]$Jobs = 4)
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||||
|
if (!$SourceRoot) { $SourceRoot = Join-Path $repo 'external/ZLUDA' }
|
||||||
|
if (!$LlvmBuildRoot) { $LlvmBuildRoot = Join-Path $repo 'build/zluda-llvm-windows-gnu' }
|
||||||
|
if (!$InstallRoot) { $InstallRoot = Join-Path $repo 'dist/zluda-windows-final' }
|
||||||
|
$rust = Join-Path $repo '.tools/rust-1.98.1-gnu'
|
||||||
|
$toolchain = Join-Path $repo '.tools/llvm-mingw-20260922-ucrt-x86_64'
|
||||||
|
$target = Join-Path $repo 'build/zluda-rust-windows-gnu'
|
||||||
|
New-Item -ItemType Directory -Force $target,$InstallRoot | Out-Null
|
||||||
|
$settings = @{
|
||||||
|
PATH="$(Join-Path $rust 'bin');$(Join-Path $toolchain 'bin');$(Join-Path $repo '.tools/python/cmake/data/bin');$(Join-Path $repo '.tools/python/bin');C:\Program Files\Git\usr\bin;$env:PATH"
|
||||||
|
CARGO_HOME=(Join-Path $repo '.tools/cargo-home')
|
||||||
|
CARGO_TARGET_DIR=$target
|
||||||
|
CARGO_BUILD_JOBS="$Jobs"
|
||||||
|
RUSTC=(Join-Path $rust 'bin/rustc.exe')
|
||||||
|
CC=(Join-Path $toolchain 'bin/x86_64-w64-mingw32-clang.exe')
|
||||||
|
CXX=(Join-Path $toolchain 'bin/x86_64-w64-mingw32-clang++.exe')
|
||||||
|
AR=(Join-Path $toolchain 'bin/llvm-ar.exe')
|
||||||
|
CXXSTDLIB='c++'
|
||||||
|
LIBRARY_PATH=(Join-Path $rust 'lib/rustlib/x86_64-pc-windows-gnu/lib/self-contained')
|
||||||
|
CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER=(Join-Path $toolchain 'bin/x86_64-w64-mingw32-clang.exe')
|
||||||
|
CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUSTFLAGS='-C link-arg=-static'
|
||||||
|
ZLUDA_LLVM_BUILD_DIR=$LlvmBuildRoot
|
||||||
|
}
|
||||||
|
$previous = @{}
|
||||||
|
foreach ($name in $settings.Keys) {
|
||||||
|
$previous[$name] = [Environment]::GetEnvironmentVariable($name,'Process')
|
||||||
|
[Environment]::SetEnvironmentVariable($name,$settings[$name],'Process')
|
||||||
|
}
|
||||||
|
Push-Location $SourceRoot
|
||||||
|
try {
|
||||||
|
$cargo = Join-Path $rust 'bin/cargo.exe'
|
||||||
|
if (!(Test-Path $cargo)) { throw 'Run setup-rust-toolchain.ps1 first.' }
|
||||||
|
if ($CompilerTestsOnly) {
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
& $cargo test --locked --release --target x86_64-pc-windows-gnu -p llvm_zluda shader_mode_tests `
|
||||||
|
2>&1 | Tee-Object (Join-Path $target 'compiler-tests.log')
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if ($LASTEXITCODE) { throw "ZLUDA compiler regression tests failed ($LASTEXITCODE)" }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ($CacheTestsOnly) {
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
& $cargo test --locked --release --target x86_64-pc-windows-gnu -p zluda_cache `
|
||||||
|
2>&1 | Tee-Object (Join-Path $target 'cache-tests.log')
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if ($LASTEXITCODE) { throw "ZLUDA cache regression tests failed ($LASTEXITCODE)" }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ($FetchOnly) {
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
& $cargo fetch --locked --target x86_64-pc-windows-gnu 2>&1 | Tee-Object (Join-Path $target 'fetch.log')
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if ($LASTEXITCODE) { throw "Cargo fetch failed ($LASTEXITCODE)" }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!(Test-Path (Join-Path $LlvmBuildRoot 'bin/llvm-config.exe'))) { throw 'Run build-zluda-llvm.ps1 first.' }
|
||||||
|
foreach ($variant in @('','_constrained','_w64','_constrained_w64')) {
|
||||||
|
if (!(Test-Path "ptx/lib/zluda_ptx_impl$variant.bc")) { throw 'Run build-zluda-helpers.ps1 first.' }
|
||||||
|
}
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
& $cargo build --locked --release --target x86_64-pc-windows-gnu -p zluda -p nvapi -p zluda_trace `
|
||||||
|
2>&1 | Tee-Object (Join-Path $target 'build.log')
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if ($LASTEXITCODE) { throw "ZLUDA build failed ($LASTEXITCODE)" }
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
& $cargo build --locked --release --target x86_64-pc-windows-gnu -p ptx --example d4r_emit `
|
||||||
|
2>&1 | Tee-Object (Join-Path $target 'emit-build.log')
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if ($LASTEXITCODE) { throw "d4r_emit build failed ($LASTEXITCODE)" }
|
||||||
|
$release = Join-Path $target 'x86_64-pc-windows-gnu/release'
|
||||||
|
Copy-Item -LiteralPath (Join-Path $release 'nvcuda.dll') -Destination $InstallRoot -Force
|
||||||
|
Copy-Item -LiteralPath (Join-Path $release 'nvapi64.dll') -Destination $InstallRoot -Force
|
||||||
|
# The C++ dependencies select libc++ dynamically even when Rust requests
|
||||||
|
# static GNU support libraries. Keep these portable runtime DLLs local.
|
||||||
|
foreach ($runtime in @('libc++.dll', 'libunwind.dll')) {
|
||||||
|
Copy-Item -LiteralPath (Join-Path $toolchain "bin/$runtime") -Destination $InstallRoot -Force
|
||||||
|
}
|
||||||
|
New-Item -ItemType Directory -Force (Join-Path $InstallRoot 'trace') | Out-Null
|
||||||
|
Copy-Item -LiteralPath (Join-Path $release 'zluda_trace.dll') -Destination (Join-Path $InstallRoot 'trace/nvcuda.dll') -Force
|
||||||
|
Copy-Item -LiteralPath (Join-Path $release 'examples/d4r_emit.exe') -Destination $InstallRoot -Force
|
||||||
|
@{base='ee2f25a180099fa42f36b2346732e1f2470a03ad'; target='x86_64-pc-windows-gnu';
|
||||||
|
sourceCommit=(& git rev-parse HEAD); workingTreeDirty=[bool](& git status --porcelain);
|
||||||
|
rust='1.98.1'; llvm=(& (Join-Path $LlvmBuildRoot 'bin/llvm-config.exe') --version);
|
||||||
|
llvmSource=$(if (Test-Path (Join-Path $LlvmBuildRoot 'd4r-llvm-build-info.json')) {
|
||||||
|
Get-Content (Join-Path $LlvmBuildRoot 'd4r-llvm-build-info.json') -Raw | ConvertFrom-Json
|
||||||
|
} else { $null });
|
||||||
|
files=@(Get-ChildItem -LiteralPath $InstallRoot -Filter '*.dll' | Get-FileHash -Algorithm SHA256)} |
|
||||||
|
ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $InstallRoot 'build-info.json') -Encoding UTF8
|
||||||
|
Write-Host "Native Windows d4r ZLUDA runtime: $InstallRoot"
|
||||||
|
} finally {
|
||||||
|
Pop-Location
|
||||||
|
foreach ($name in $previous.Keys) { [Environment]::SetEnvironmentVariable($name,$previous[$name],'Process') }
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([Parameter(Mandatory=$true)][int]$GameProcessId, [Parameter(Mandatory=$true)][string]$OutputPath)
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
Add-Type -AssemblyName System.Drawing
|
||||||
|
if (!('D4RWindowCapture' -as [type])) {
|
||||||
|
Add-Type @'
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
public static class D4RWindowCapture {
|
||||||
|
public delegate bool Callback(IntPtr window, IntPtr data);
|
||||||
|
[StructLayout(LayoutKind.Sequential)] public struct Rect { public int Left, Top, Right, Bottom; }
|
||||||
|
[DllImport("user32.dll")] public static extern bool EnumWindows(Callback callback, IntPtr data);
|
||||||
|
[DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr window, out uint process);
|
||||||
|
[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr window, out Rect rect);
|
||||||
|
[DllImport("user32.dll",CharSet=CharSet.Unicode)] public static extern int GetWindowText(IntPtr window, StringBuilder text, int count);
|
||||||
|
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr window);
|
||||||
|
[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
|
||||||
|
[DllImport("user32.dll")] public static extern IntPtr SetThreadDpiAwarenessContext(IntPtr context);
|
||||||
|
}
|
||||||
|
'@
|
||||||
|
}
|
||||||
|
$windows = [Collections.Generic.List[object]]::new()
|
||||||
|
$previousDpi=[D4RWindowCapture]::SetThreadDpiAwarenessContext([IntPtr](-4))
|
||||||
|
try {
|
||||||
|
$callback = [D4RWindowCapture+Callback]{ param($window,$data)
|
||||||
|
[uint32]$owner = 0
|
||||||
|
[void][D4RWindowCapture]::GetWindowThreadProcessId($window,[ref]$owner)
|
||||||
|
if ($owner -eq $GameProcessId) {
|
||||||
|
$rect = [D4RWindowCapture+Rect]::new()
|
||||||
|
[void][D4RWindowCapture]::GetWindowRect($window,[ref]$rect)
|
||||||
|
$title = [Text.StringBuilder]::new(512)
|
||||||
|
[void][D4RWindowCapture]::GetWindowText($window,$title,512)
|
||||||
|
$windows.Add([pscustomobject]@{handle=$window; title=$title.ToString(); visible=[D4RWindowCapture]::IsWindowVisible($window); rect=$rect;
|
||||||
|
area=($rect.Right-$rect.Left)*($rect.Bottom-$rect.Top)})
|
||||||
|
}
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
[void][D4RWindowCapture]::EnumWindows($callback,[IntPtr]::Zero)
|
||||||
|
$windows | Select-Object handle,title,visible,area | Format-Table
|
||||||
|
$chosen = $windows | Where-Object { $_.visible -and $_.area -gt 4096 } | Sort-Object area -Descending | Select-Object -First 1
|
||||||
|
if (!$chosen) { throw "No visible game window for PID $GameProcessId" }
|
||||||
|
if ([D4RWindowCapture]::GetForegroundWindow() -ne $chosen.handle) {
|
||||||
|
throw 'Game window is not foreground; capture refused to avoid including unrelated overlapping windows.'
|
||||||
|
}
|
||||||
|
$rect = $chosen.rect
|
||||||
|
$bitmap = [Drawing.Bitmap]::new($rect.Right-$rect.Left,$rect.Bottom-$rect.Top)
|
||||||
|
$graphics = [Drawing.Graphics]::FromImage($bitmap)
|
||||||
|
try {
|
||||||
|
$graphics.CopyFromScreen($rect.Left,$rect.Top,0,0,$bitmap.Size)
|
||||||
|
$bitmap.Save([IO.Path]::GetFullPath($OutputPath),[Drawing.Imaging.ImageFormat]::Png)
|
||||||
|
} finally {
|
||||||
|
$graphics.Dispose(); $bitmap.Dispose()
|
||||||
|
}
|
||||||
|
Write-Host "Captured game window: $OutputPath"
|
||||||
|
} finally {
|
||||||
|
if ($previousDpi -ne [IntPtr]::Zero) { [void][D4RWindowCapture]::SetThreadDpiAwarenessContext($previousDpi) }
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[int]$TargetProcessId,
|
||||||
|
[string]$ProcessName = 'SHProto-Win64-Shipping',
|
||||||
|
[ValidateRange(5,300)][int]$Seconds = 30,
|
||||||
|
[ValidateRange(0,300)][int]$DelaySeconds = 0,
|
||||||
|
[string]$PresentMonPath,
|
||||||
|
[switch]$GpuTelemetry,
|
||||||
|
[string]$TelemetryPath,
|
||||||
|
[string]$GpuName = 'RX 9070 XT',
|
||||||
|
[string]$OutputDirectory
|
||||||
|
)
|
||||||
|
$ErrorActionPreference='Stop'
|
||||||
|
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||||
|
if (!$PresentMonPath) {
|
||||||
|
$PresentMonPath=Join-Path $repo '.tools/presentmon-2.6.0/PresentMon.exe'
|
||||||
|
if (!(Test-Path -LiteralPath $PresentMonPath)) {
|
||||||
|
New-Item -ItemType Directory -Force (Split-Path $PresentMonPath) | Out-Null
|
||||||
|
Invoke-WebRequest -UseBasicParsing -Uri 'https://github.com/GameTechDev/PresentMon/releases/download/v2.6.0/PresentMon-2.6.0-x64.exe' -OutFile $PresentMonPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/presentmon-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||||
|
$PresentMonPath=(Get-Item -LiteralPath $PresentMonPath -ErrorAction Stop).FullName
|
||||||
|
if ((Get-FileHash -LiteralPath $PresentMonPath -Algorithm SHA256).Hash.ToLowerInvariant() -ne
|
||||||
|
'b2a706bc6ad475749e3b7e3409263aa1e6906d45bdcf993f6dbc0f660188f1af') {
|
||||||
|
throw 'Expected the official PresentMon 2.6.0 x64 CLI. See docs/windows-performance.md.'
|
||||||
|
}
|
||||||
|
if ($TargetProcessId) { $target=Get-Process -Id $TargetProcessId -ErrorAction Stop }
|
||||||
|
else {
|
||||||
|
$targets=@(Get-Process -Name $ProcessName -ErrorAction Stop)
|
||||||
|
if ($targets.Count -ne 1) { throw 'Select one running game process using -TargetProcessId.' }
|
||||||
|
$target=$targets[0]
|
||||||
|
}
|
||||||
|
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||||
|
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||||
|
$csv=Join-Path $OutputDirectory 'presentmon.csv'
|
||||||
|
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;
|
||||||
|
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);
|
||||||
|
driver=@(Get-CimInstance Win32_VideoController | Select-Object Name,DriverVersion)} |
|
||||||
|
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
|
||||||
|
# terminates itself at the bound/process exit, without a service installation.
|
||||||
|
$session='D4R_' + [Guid]::NewGuid().ToString('N')
|
||||||
|
$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')
|
||||||
|
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.' }
|
||||||
|
Write-Host "PresentMon capture: $csv"
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([string]$SourceRoot)
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||||
|
if (!$SourceRoot) { $SourceRoot = Join-Path $repo 'external/ZLUDA' }
|
||||||
|
$base = 'ee2f25a180099fa42f36b2346732e1f2470a03ad'
|
||||||
|
$relative = 'build/zluda-patch-check-' + [Guid]::NewGuid().ToString('N')
|
||||||
|
$snapshot = Join-Path $repo $relative
|
||||||
|
New-Item -ItemType Directory -Force $snapshot | Out-Null
|
||||||
|
$archive = "$snapshot.tar"
|
||||||
|
& git -C $SourceRoot archive --format=tar "--output=$archive" $base
|
||||||
|
if ($LASTEXITCODE) { throw 'Could not archive the pinned ZLUDA base.' }
|
||||||
|
& tar.exe -xf $archive -C $snapshot
|
||||||
|
if ($LASTEXITCODE) { throw 'Could not extract the ZLUDA patch-check snapshot.' }
|
||||||
|
Push-Location $repo
|
||||||
|
try {
|
||||||
|
foreach ($patch in (Get-ChildItem -LiteralPath (Join-Path $repo 'patches/zluda') -Filter '*.patch' | Sort-Object Name)) {
|
||||||
|
& git apply "--directory=$relative" --check $patch.FullName
|
||||||
|
if ($LASTEXITCODE) { throw "Fresh-source patch check failed: $($patch.Name)" }
|
||||||
|
& git apply "--directory=$relative" --whitespace=nowarn $patch.FullName
|
||||||
|
if ($LASTEXITCODE) { throw "Fresh-source patch apply failed: $($patch.Name)" }
|
||||||
|
Write-Host "PASS patch $($patch.Name)"
|
||||||
|
}
|
||||||
|
Write-Host "Pinned-source patch snapshot: $snapshot"
|
||||||
|
} finally { Pop-Location }
|
||||||
@@ -0,0 +1,271 @@
|
|||||||
|
# Checks a d4r install on Windows. It only reads files and prints what it finds.
|
||||||
|
# Run it from the game folder (the one holding the game's .exe and d4r\), or give that folder:
|
||||||
|
# powershell -ExecutionPolicy Bypass -File d4r\d4r-check.ps1 [GAME_FOLDER]
|
||||||
|
# Add -Report to also write one ZIP (logs, this output, GPU and driver) to send with a bug report.
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[string]$GameFolder,
|
||||||
|
[switch]$Report,
|
||||||
|
[string]$InventoryFile, # tests: a gpu-inventory.json to use instead of running the GPU query
|
||||||
|
[string]$ReportDirectory # tests: where -Report writes its ZIP (default: your Desktop)
|
||||||
|
)
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
if (!$GameFolder) {
|
||||||
|
$GameFolder = if ((Split-Path $PSScriptRoot -Leaf) -eq 'd4r') { Split-Path $PSScriptRoot -Parent } else { (Get-Location).Path }
|
||||||
|
}
|
||||||
|
$game = [IO.Path]::GetFullPath($GameFolder).TrimEnd('\')
|
||||||
|
$d4r = Join-Path $game 'd4r'
|
||||||
|
$problems = 0
|
||||||
|
$lines = [Collections.Generic.List[string]]::new()
|
||||||
|
function Say([string]$text) { $lines.Add($text); Write-Host $text }
|
||||||
|
function Ok([string]$text) { Say (' ok ' + $text) }
|
||||||
|
function Bad([string]$text) { Say (' MISSING ' + $text); $script:problems++ }
|
||||||
|
function Note([string]$text) { Say (' note ' + $text) }
|
||||||
|
function Hash([string]$path) { (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash }
|
||||||
|
function Version([string]$path) {
|
||||||
|
try { $v = (Get-Item -LiteralPath $path).VersionInfo; if ($v.FileVersion) { return ($v.FileVersion -replace ',\s*', '.') } } catch {}
|
||||||
|
return 'unknown'
|
||||||
|
}
|
||||||
|
function Read-TextFile([string]$path) { [IO.File]::ReadAllText($path) }
|
||||||
|
|
||||||
|
Say "d4r install in $game"
|
||||||
|
if (!(Test-Path -LiteralPath $d4r -PathType Container)) {
|
||||||
|
Say 'no d4r folder here; run this from the folder with the game .exe (extract the d4r ZIP there first)'
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
if (@(Get-ChildItem -LiteralPath $game -Filter '*.exe' -File -ErrorAction SilentlyContinue).Count) { Ok 'game executable next to d4r\' }
|
||||||
|
else { Note 'no .exe next to d4r\ (the d4r files must sit in the folder of the game''s main .exe; for Unreal Engine games that is <game>\<Project>\Binaries\Win64)' }
|
||||||
|
|
||||||
|
# --- the install's own file list ---------------------------------------------------------------
|
||||||
|
$manifest = $null
|
||||||
|
$manifestPath = Join-Path $d4r 'package.json'
|
||||||
|
if (Test-Path -LiteralPath $manifestPath) {
|
||||||
|
try { $manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json } catch { $manifest = $null }
|
||||||
|
}
|
||||||
|
if (!$manifest) { Bad 'd4r\package.json is missing or damaged (extract the d4r ZIP again)' }
|
||||||
|
|
||||||
|
# --- files --------------------------------------------------------------------------------------
|
||||||
|
$required = @('dxgi.dll', 'OptiScaler.ini', 'd4r\_nvngx.dll', 'd4r\d4r.ini', 'd4r\dll-pins.txt',
|
||||||
|
'd4r\hip\bin\amdhip64_7.dll', 'd4r\zluda\nvcuda.dll', 'd4r\zluda\nvapi64.dll')
|
||||||
|
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 }
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Parse completed diagnostic records. Empty streams are valid, including games
|
||||||
|
# that refuse a debugger before initializing the upscaler.
|
||||||
|
function Get-D4RGameSummary {
|
||||||
|
param([AllowEmptyString()][string]$Stdout='', [AllowEmptyString()][string]$Stderr='',
|
||||||
|
[AllowEmptyString()][string]$Debugger='', [int]$Preset=11, [double]$RunSeconds=0,
|
||||||
|
[bool]$ValidateOutput=$false, $GameCrash=$null)
|
||||||
|
$gameExit=[regex]::Match($Debugger, 'EXIT code=0x([0-9a-f]+)')
|
||||||
|
$exitCode=if ($gameExit.Success) { '0x'+$gameExit.Groups[1].Value } else { 'diagnostic_timeout' }
|
||||||
|
$frames=[regex]::Matches($Stdout, '(?m)^D4R_FRAME cpu_frame_copies=([0-9]+) frame_age=([0-9]+) interop_ngx_ms=([0-9.]+)\r?$')
|
||||||
|
$checks=[regex]::Matches($Stdout, '(?m)^D4R_OUTPUT_VALIDATION elements=([0-9]+) nan=([0-9]+) inf=([0-9]+) diagnostics_cpu_bytes=8\r?$')
|
||||||
|
$launches=[regex]::Matches($Stderr, '\[d4r-launch\] kernel="([^"]+)" backend=(native|translated)')
|
||||||
|
$kernels=@($launches | ForEach-Object { $_.Groups[1].Value+' '+$_.Groups[2].Value } | Group-Object | ForEach-Object {
|
||||||
|
$parts=$_.Name.Split(' '); @{kernel=$parts[0]; backend=$parts[1]; launches=$_.Count}
|
||||||
|
})
|
||||||
|
$capabilities=@([regex]::Matches($Stdout, '(?m)^D4R_CAPABILITIES available=(-?[0-9]+) feature_init=(0x[0-9a-f]+) needs_driver=(-?[0-9]+)\r?$') |
|
||||||
|
ForEach-Object { $_.Groups[2].Value } | Select-Object -Unique)
|
||||||
|
$session=if ($frames.Count) { 'completed-dlss-frames' }
|
||||||
|
elseif ($capabilities -contains '0xbad00004') { 'ngx-feature-not-found' }
|
||||||
|
elseif ($capabilities.Count -and $capabilities -ne '0x00000001') { 'ngx-capability-failure' }
|
||||||
|
elseif ($exitCode -eq '0x0') { 'process-exited-without-dlss-frames' }
|
||||||
|
elseif ($exitCode -eq 'diagnostic_timeout') { 'diagnostic-timeout-without-dlss-frames' }
|
||||||
|
else { 'process-failed-before-dlss-frames' }
|
||||||
|
return [ordered]@{exitCode=$exitCode; session=$session; featureInitResults=$capabilities;
|
||||||
|
runSeconds=$RunSeconds; preset=$Preset; gameCrash=$GameCrash;
|
||||||
|
nativeLaunches=([regex]::Matches($Stderr, '\[d4r-launch\].*backend=native')).Count;
|
||||||
|
completedFrames=$frames.Count; outputValidationRequested=$ValidateOutput;
|
||||||
|
outputGpuChecks=$checks.Count; kernels=$kernels;
|
||||||
|
nonfiniteOutputs=@($checks | Where-Object { $_.Groups[2].Value -ne '0' -or $_.Groups[3].Value -ne '0' }).Count;
|
||||||
|
previousFrameOutputs=@($frames | Where-Object { $_.Groups[2].Value -ne '0' }).Count;
|
||||||
|
cpuImageCopyFrames=@($frames | Where-Object { $_.Groups[1].Value -ne '0' }).Count;
|
||||||
|
failures=([regex]::Matches($Stderr, 'D4R_[A-Z0-9_]*FAILURE\b')).Count}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
# Read-only inventory before touching game files. It allocates no GPU memory
|
||||||
|
# and executes no kernels; successful discovery is not hardware validation.
|
||||||
|
function Invoke-D4RGpuPreflight([string]$PackageRoot, [string]$GameDirectory, [string]$Target, [string]$OutputDirectory) {
|
||||||
|
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||||
|
$result=[ordered]@{passed=$false; target=$Target; gpuWorkExecuted=$false; failure=$null}
|
||||||
|
try {
|
||||||
|
$proxy=Join-Path $GameDirectory 'd3d12.dll'
|
||||||
|
if (Test-Path -LiteralPath $proxy) {
|
||||||
|
$item=Get-Item -LiteralPath $proxy
|
||||||
|
$result.localD3D12=@{path=$proxy; sha256=(Get-FileHash -LiteralPath $proxy).Hash; product=$item.VersionInfo.ProductName}
|
||||||
|
# Identify known Vulkan/Wine proxies by their own strings. Do not
|
||||||
|
# load, remove or replace a third-party game DLL.
|
||||||
|
if ($item.Length -le 64MB -and [Text.Encoding]::ASCII.GetString([IO.File]::ReadAllBytes($proxy)) -match '(?i)vkd3d|wined3d') {
|
||||||
|
throw 'Local d3d12.dll identifies as vkd3d/Wine. Restore the game native D3D12 installation before using this Windows backend.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$info=[Diagnostics.ProcessStartInfo]::new()
|
||||||
|
$info.FileName=Join-Path $PackageRoot 'd4r/d4r_gpu_inventory.exe'
|
||||||
|
$hip=Join-Path $PackageRoot 'd4r/hip'
|
||||||
|
foreach ($value in @($hip,$OutputDirectory)) { if ($value.Contains('"')) { throw 'Unsupported quote in inventory path.' } }
|
||||||
|
$info.Arguments='--hip-root "' + $hip + '" --output-dir "' + $OutputDirectory + '"'
|
||||||
|
$info.UseShellExecute=$false; $info.CreateNoWindow=$true
|
||||||
|
$info.EnvironmentVariables['D4R_DIAG_DIR']=$OutputDirectory
|
||||||
|
$info.RedirectStandardOutput=$true; $info.RedirectStandardError=$true
|
||||||
|
$process=[Diagnostics.Process]::new(); $process.StartInfo=$info
|
||||||
|
try {
|
||||||
|
if (!$process.Start()) { throw 'Cannot start GPU inventory.' }
|
||||||
|
$stdout=$process.StandardOutput.ReadToEndAsync(); $stderr=$process.StandardError.ReadToEndAsync()
|
||||||
|
$timedOut=!$process.WaitForExit(30000)
|
||||||
|
if ($timedOut) { $process.Kill(); $process.WaitForExit() }
|
||||||
|
$stdout.GetAwaiter().GetResult() | Set-Content (Join-Path $OutputDirectory 'gpu-inventory.stdout.log') -Encoding UTF8
|
||||||
|
$stderr.GetAwaiter().GetResult() | Set-Content (Join-Path $OutputDirectory 'gpu-inventory.stderr.log') -Encoding UTF8
|
||||||
|
$result.exitCode=$process.ExitCode
|
||||||
|
$result.exceptionOrExitHex='0x{0:X8}' -f [BitConverter]::ToUInt32([BitConverter]::GetBytes([int]$process.ExitCode),0)
|
||||||
|
if ($timedOut) { $result.timedOut=$true; throw 'HIP inventory timed out after 30 seconds.' }
|
||||||
|
if ($process.ExitCode) { throw "HIP inventory failed (exit $($process.ExitCode))." }
|
||||||
|
} finally { $process.Dispose() }
|
||||||
|
$inventory=Get-Content (Join-Path $OutputDirectory 'gpu-inventory.json') -Raw | ConvertFrom-Json
|
||||||
|
$result.inventory=$inventory
|
||||||
|
if ($inventory.compiledTarget -ne $Target) { throw "Inventory executable targets $($inventory.compiledTarget); package requires $Target." }
|
||||||
|
$matching=@($inventory.devices | Where-Object { $_.architecture -eq $Target -and $_.wavefrontSize -eq 32 })
|
||||||
|
if (!$matching.Count) {
|
||||||
|
$actual=@($inventory.devices | ForEach-Object { "$($_.architecture) ($($_.name))" }) -join ', '
|
||||||
|
throw "GPU target mismatch: detected $actual; package requires $Target. Use the package for the actual HIP architecture; do not rename code objects."
|
||||||
|
}
|
||||||
|
$result.passed=$true
|
||||||
|
Write-Host "D4R_PREFLIGHT target=$Target devices=$($matching.Count) gpu_work_executed=0"
|
||||||
|
} catch {
|
||||||
|
$result.failure=$_.Exception.Message
|
||||||
|
$result | ConvertTo-Json -Depth 8 | Set-Content (Join-Path $OutputDirectory 'preflight.json') -Encoding UTF8
|
||||||
|
try { Get-CimInstance Win32_VideoController | Select-Object Name,DriverVersion,DriverDate | ConvertTo-Json |
|
||||||
|
Set-Content (Join-Path $OutputDirectory 'driver.json') -Encoding UTF8 } catch {}
|
||||||
|
$bundle=$OutputDirectory+'.zip'
|
||||||
|
Compress-Archive -Path (Join-Path $OutputDirectory '*') -DestinationPath $bundle -Force
|
||||||
|
throw "$($result.failure) Diagnostic bundle: $bundle"
|
||||||
|
}
|
||||||
|
$result | ConvertTo-Json -Depth 8 | Set-Content (Join-Path $OutputDirectory 'preflight.json') -Encoding UTF8
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Shared by the build, diagnostics, staging and game package scripts.
|
||||||
|
function Get-D4RGpuTargets {
|
||||||
|
$registry=Join-Path $PSScriptRoot 'gpu-targets.json'
|
||||||
|
if (!(Test-Path -LiteralPath $registry)) { $registry=Join-Path $PSScriptRoot '../../tools/windows/gpu-targets.json' }
|
||||||
|
$data=Get-Content -LiteralPath $registry -Raw | ConvertFrom-Json
|
||||||
|
if ($data.schema -ne 1) { throw 'Unsupported GPU target registry schema.' }
|
||||||
|
return $data.targets
|
||||||
|
}
|
||||||
|
function Get-D4RGpuTargetInfo([string]$Target) {
|
||||||
|
$info=@(Get-D4RGpuTargets | Where-Object { $_.architecture -eq $Target })
|
||||||
|
if ($info.Count -ne 1) { throw "Unsupported GPU target: $Target" }
|
||||||
|
return $info[0]
|
||||||
|
}
|
||||||
|
function Get-D4RGpuTarget([string]$Root, [string]$Requested) {
|
||||||
|
$configured=$null
|
||||||
|
$targetFile=Join-Path $Root 'gpu-target.json'
|
||||||
|
if (Test-Path -LiteralPath $targetFile) {
|
||||||
|
$configured=(Get-Content -LiteralPath $targetFile -Raw | ConvertFrom-Json).architecture
|
||||||
|
[void](Get-D4RGpuTargetInfo $configured)
|
||||||
|
}
|
||||||
|
$target=if ($Requested) { $Requested } elseif ($configured) { $configured } else { 'gfx1201' }
|
||||||
|
[void](Get-D4RGpuTargetInfo $target)
|
||||||
|
if (!$configured -and $target -ne 'gfx1201') { throw "A $target build requires gpu-target.json; rebuild with -GpuArch $target." }
|
||||||
|
if ($configured -and $target -ne $configured) { throw "Requested $target differs from package target $configured" }
|
||||||
|
return $target
|
||||||
|
}
|
||||||
|
function Assert-D4RCodeObjectTarget([string]$Path, [string]$Target) {
|
||||||
|
$info=Get-D4RGpuTargetInfo $Target
|
||||||
|
$reader=[IO.BinaryReader]::new([IO.File]::OpenRead($Path))
|
||||||
|
try { $header=$reader.ReadBytes(64) } finally { $reader.Dispose() }
|
||||||
|
if ($header.Length -ne 64 -or $header[0] -ne 127 -or $header[1] -ne 69 -or $header[2] -ne 76 -or $header[3] -ne 70 -or
|
||||||
|
$header[4] -ne 2 -or $header[5] -ne 1 -or $header[18] -ne 224 -or $header[19] -ne 0) {
|
||||||
|
throw "Invalid AMDGPU ELF64 code object: $Path"
|
||||||
|
}
|
||||||
|
# LLVM AMDGPU e_flags machine IDs; inspect, never patch or spoof them.
|
||||||
|
$expected=$info.elfMachine
|
||||||
|
if ($header[48] -ne $expected) { throw "Code object does not target ${Target}: $Path (ELF machine=$($header[48]))" }
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[string]$PackageRoot, [string]$DiagnosticRoot, [string]$OptiScalerRoot,
|
||||||
|
[string]$ZludaRoot, [string]$HipRoot, [string]$KernelKRoot, [string]$KernelMRoot,
|
||||||
|
[string]$GpuArch,
|
||||||
|
[string]$ArchivePath
|
||||||
|
)
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||||
|
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||||
|
$defaults = @{PackageRoot='dist/windows-rdna4-game'; DiagnosticRoot='dist/windows-rdna4-command-list';
|
||||||
|
OptiScalerRoot='dist/optiscaler-windows-d4r'; ZludaRoot='dist/zluda-windows-final';
|
||||||
|
HipRoot='.tools/therock-10.2.0a20260929/_rocm_sdk_core'}
|
||||||
|
foreach ($name in $defaults.Keys) {
|
||||||
|
if (!(Get-Variable -Name $name -ValueOnly)) { Set-Variable -Name $name -Value (Join-Path $repo $defaults[$name]) }
|
||||||
|
}
|
||||||
|
$GpuArch=Get-D4RGpuTarget $DiagnosticRoot $GpuArch
|
||||||
|
if ($GpuArch -ne 'gfx1201' -and !$PSBoundParameters.ContainsKey('PackageRoot')) { $PackageRoot += "-$GpuArch" }
|
||||||
|
if (!$KernelKRoot) { $KernelKRoot=Join-Path $repo "build/native-k-$GpuArch" }
|
||||||
|
if (!$KernelMRoot) { $KernelMRoot=Join-Path $repo "build/native-m-$GpuArch" }
|
||||||
|
foreach ($directory in @($KernelKRoot,$KernelMRoot)) {
|
||||||
|
$info=Join-Path $directory 'build-info.json'
|
||||||
|
if ((Test-Path -LiteralPath $info) -and (Get-Content -LiteralPath $info -Raw | ConvertFrom-Json).architecture -ne $GpuArch) {
|
||||||
|
throw "Native kernel directory does not target ${GpuArch}: $directory"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$PackageRoot = [IO.Path]::GetFullPath($PackageRoot)
|
||||||
|
if (Test-Path -LiteralPath (Join-Path $PackageRoot 'gpu-target.json')) {
|
||||||
|
[void](Get-D4RGpuTarget $PackageRoot $GpuArch)
|
||||||
|
}
|
||||||
|
$sourceCommit=(& git -C $repo rev-parse HEAD).Trim()
|
||||||
|
$sourceDirty=[bool](& git -C $repo status --porcelain)
|
||||||
|
if ($ArchivePath -and $sourceDirty) { throw 'Commit source changes before creating the distributable archive.' }
|
||||||
|
New-Item -ItemType Directory -Force $PackageRoot | Out-Null
|
||||||
|
$stagedFiles = [Collections.Generic.List[string]]::new()
|
||||||
|
function Stage([string]$source, [string]$relative) {
|
||||||
|
if (!(Test-Path -LiteralPath $source -PathType Leaf)) { throw "Required file missing: $source" }
|
||||||
|
if ($source.EndsWith('.hsaco')) { Assert-D4RCodeObjectTarget $source $GpuArch }
|
||||||
|
$destination = Join-Path $PackageRoot $relative
|
||||||
|
New-Item -ItemType Directory -Force (Split-Path $destination) | Out-Null
|
||||||
|
Copy-Item -LiteralPath $source -Destination $destination -Force
|
||||||
|
$stagedFiles.Add($relative)
|
||||||
|
}
|
||||||
|
Stage (Join-Path $OptiScalerRoot 'OptiScaler.dll') 'OptiScaler.dll'
|
||||||
|
Stage (Join-Path $DiagnosticRoot 'bin/d4r_nvngx.dll') 'd4r/_nvngx.dll'
|
||||||
|
Stage (Join-Path $DiagnosticRoot "bin/pixel_convert_${GpuArch}.hsaco") "d4r/pixel_convert_${GpuArch}.hsaco"
|
||||||
|
Stage (Join-Path $DiagnosticRoot 'nvapi-compat/nvapi64.dll') 'd4r/nvapi/nvapi64.dll'
|
||||||
|
Stage (Join-Path $DiagnosticRoot '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 @('amdhip64_7.dll','amd_comgr.dll','hiprtc0717.dll','hiprtc-builtins0717.dll','rocm_kpack.dll','AMD.ROCM.Comgr.MANIFEST','.hipVersion')) {
|
||||||
|
Stage (Join-Path $HipRoot "bin/$file") "d4r/hip/bin/$file"
|
||||||
|
}
|
||||||
|
$entries = @{}
|
||||||
|
foreach ($directory in @($KernelKRoot, $KernelMRoot)) {
|
||||||
|
foreach ($line in Get-Content -LiteralPath (Join-Path $directory 'd4r-kernels.txt')) {
|
||||||
|
if (!$line.Trim() -or $line.StartsWith('#')) { continue }
|
||||||
|
if ($line -notmatch '^([A-Za-z0-9_]+) ([0-9a-f]{16})$') { throw "Invalid native manifest entry: $line" }
|
||||||
|
if ($entries.ContainsKey($Matches[1])) { throw "Duplicate native kernel: $($Matches[1])" }
|
||||||
|
$entries[$Matches[1]] = $line
|
||||||
|
Stage (Join-Path $directory ($Matches[1] + '.hsaco')) ('d4r/native/' + $Matches[1] + '.hsaco')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($entries.Count -ne 16) { throw "Expected 11 K and 5 M native objects; found $($entries.Count)" }
|
||||||
|
@("# Audited DLSS 310.9.1: K + M $GpuArch native kernels", '# Generated by package-windows-game.ps1') + @($entries.Values | Sort-Object) |
|
||||||
|
Set-Content -LiteralPath (Join-Path $PackageRoot 'd4r/native/d4r-kernels.txt') -Encoding ASCII
|
||||||
|
$stagedFiles.Add('d4r/native/d4r-kernels.txt')
|
||||||
|
Stage (Join-Path $repo 'scripts/windows/windows-game.ps1') 'windows-game.ps1'
|
||||||
|
Stage (Join-Path $repo 'scripts/windows/game-summary.ps1') 'game-summary.ps1'
|
||||||
|
Stage (Join-Path $repo 'scripts/windows/gpu-target.ps1') 'gpu-target.ps1'
|
||||||
|
Stage (Join-Path $repo 'scripts/windows/gpu-preflight.ps1') 'gpu-preflight.ps1'
|
||||||
|
Stage (Join-Path $repo 'tools/windows/gpu-targets.json') 'gpu-targets.json'
|
||||||
|
if (Test-Path -LiteralPath (Join-Path $DiagnosticRoot 'gpu-target.json')) {
|
||||||
|
Stage (Join-Path $DiagnosticRoot 'gpu-target.json') 'gpu-target.json'
|
||||||
|
} else {
|
||||||
|
# Builds predating target metadata were gfx1201 only. The resolver rejects
|
||||||
|
# any attempt to label such a build gfx1200.
|
||||||
|
@{schema=1; architecture=$GpuArch; wavefrontSize=32; validation='Legacy gfx1201 build; run the GPU diagnostics.'} |
|
||||||
|
ConvertTo-Json | Set-Content -LiteralPath (Join-Path $PackageRoot 'gpu-target.json') -Encoding UTF8
|
||||||
|
$stagedFiles.Add('gpu-target.json')
|
||||||
|
}
|
||||||
|
Stage (Join-Path $repo 'scripts/windows/capture-game-window.ps1') 'capture-game-window.ps1'
|
||||||
|
Stage (Join-Path $repo 'scripts/windows/send-game-key.ps1') 'send-game-key.ps1'
|
||||||
|
Stage (Join-Path $repo 'docs/windows-game.md') 'README.md'
|
||||||
|
Stage (Join-Path $repo 'docs/windows-performance.md') 'windows-performance.md'
|
||||||
|
Stage (Join-Path $repo 'docs/windows-gpu-support.md') 'windows-gpu-support.md'
|
||||||
|
Stage (Join-Path $repo 'docs/windows-rdna4-port.md') 'windows-rdna4-port.md'
|
||||||
|
Stage (Join-Path $repo 'LICENSE') 'licenses/d4r.txt'
|
||||||
|
Stage (Join-Path $OptiScalerRoot 'GPL-3.0.txt') 'licenses/OptiScaler.txt'
|
||||||
|
Stage (Join-Path $repo 'external/ZLUDA/LICENSE-MIT') 'licenses/ZLUDA-MIT.txt'
|
||||||
|
Stage (Join-Path $repo 'external/ZLUDA/LICENSE-APACHE') 'licenses/ZLUDA-APACHE.txt'
|
||||||
|
Stage (Join-Path $repo 'external/ZLUDA/ext/llvm-project/LICENSE.TXT') 'licenses/LLVM.txt'
|
||||||
|
Stage (Join-Path $repo 'external/MinHook/LICENSE.txt') 'licenses/MinHook.txt'
|
||||||
|
Stage (Join-Path $HipRoot 'share/doc/amd_comgr/LICENSE.txt') 'licenses/amd_comgr.txt'
|
||||||
|
Stage (Join-Path $repo 'external/clr/LICENSE.md') 'licenses/HIP-CLR.txt'
|
||||||
|
foreach ($patch in Get-ChildItem -LiteralPath (Join-Path $repo 'patches/optiscaler') -Filter '*.patch') { Stage $patch.FullName ('source-patches/optiscaler/' + $patch.Name) }
|
||||||
|
foreach ($patch in Get-ChildItem -LiteralPath (Join-Path $repo 'patches/zluda') -Filter '*.patch') { Stage $patch.FullName ('source-patches/zluda/' + $patch.Name) }
|
||||||
|
foreach ($patch in Get-ChildItem -LiteralPath (Join-Path $repo 'patches/zluda-llvm') -Filter '*.patch') { Stage $patch.FullName ('source-patches/zluda-llvm/' + $patch.Name) }
|
||||||
|
$sourceArchive=Join-Path $repo 'build/d4r-windows-rdna4-source.zip'
|
||||||
|
New-Item -ItemType Directory -Force (Split-Path $sourceArchive) | Out-Null
|
||||||
|
& git -C $repo archive --format=zip --output $sourceArchive $sourceCommit
|
||||||
|
if ($LASTEXITCODE) { throw 'Cannot archive the committed d4r source.' }
|
||||||
|
Stage $sourceArchive 'source/d4r-windows-rdna4-source.zip'
|
||||||
|
$metadata = [ordered]@{
|
||||||
|
architecture=$GpuArch; hardwareValidation=$(if ($GpuArch -eq 'gfx1201') { 'RX9070XT tested; other SKUs unverified' } else { 'compile-only; hardware unverified' }); nativeKernels=16; dlssSha256='3975567b8943c53acce397f2b72380092f84f162d00b0d2c7d08a1025c563983';
|
||||||
|
d4rCommit=$sourceCommit;
|
||||||
|
d4rWorkingTreeDirty=$sourceDirty;
|
||||||
|
optiScaler=(Get-Content -LiteralPath (Join-Path $OptiScalerRoot 'build-info.json') -Raw | ConvertFrom-Json);
|
||||||
|
zludaBuild=(Get-Content -LiteralPath (Join-Path $ZludaRoot 'build-info.json') -Raw | ConvertFrom-Json);
|
||||||
|
hipRuntime='TheRock 10.2.0a20260929 / HIP 7.17.26386';
|
||||||
|
files=@($stagedFiles | ForEach-Object { Get-Item -LiteralPath (Join-Path $PackageRoot $_) } | ForEach-Object {
|
||||||
|
@{path=$_.FullName.Substring($PackageRoot.Length+1); sha256=(Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash; version=$_.VersionInfo.FileVersion}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
$metadata | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath (Join-Path $PackageRoot 'package.json') -Encoding UTF8
|
||||||
|
if (Get-ChildItem -LiteralPath $PackageRoot -Recurse -File | Where-Object { $_.Name -eq 'nvngx_dlss.dll' -or ($_.Name -eq '_nvngx.dll' -and $_.DirectoryName -ne (Join-Path $PackageRoot 'd4r')) }) {
|
||||||
|
throw 'Proprietary NVIDIA binaries must not be in the package.'
|
||||||
|
}
|
||||||
|
Write-Host "Windows game development package: $PackageRoot (NVIDIA DLLs excluded)"
|
||||||
|
if ($ArchivePath) {
|
||||||
|
$ArchivePath=[IO.Path]::GetFullPath($ArchivePath)
|
||||||
|
if ($ArchivePath.StartsWith($PackageRoot+'\',[StringComparison]::OrdinalIgnoreCase)) { throw 'Place the ZIP outside the package directory.' }
|
||||||
|
Add-Type -AssemblyName System.IO.Compression,System.IO.Compression.FileSystem
|
||||||
|
New-Item -ItemType Directory -Force (Split-Path $ArchivePath) | Out-Null
|
||||||
|
$stream=[IO.File]::Open($ArchivePath,[IO.FileMode]::Create,[IO.FileAccess]::ReadWrite,[IO.FileShare]::None)
|
||||||
|
$archive=$null
|
||||||
|
try {
|
||||||
|
$archive=[IO.Compression.ZipArchive]::new($stream,[IO.Compression.ZipArchiveMode]::Create)
|
||||||
|
# Never archive caches, results, private texture outputs or a local DLL
|
||||||
|
# merely because it happens to be somewhere under PackageRoot.
|
||||||
|
foreach ($relative in @($stagedFiles)+@('package.json')) {
|
||||||
|
[void][IO.Compression.ZipFileExtensions]::CreateEntryFromFile($archive,
|
||||||
|
(Join-Path $PackageRoot $relative),$relative.Replace('\','/'),[IO.Compression.CompressionLevel]::Optimal)
|
||||||
|
}
|
||||||
|
} finally { if ($archive) { $archive.Dispose() }; $stream.Dispose() }
|
||||||
|
Write-Host "Public package archive: $ArchivePath"
|
||||||
|
}
|
||||||
@@ -0,0 +1,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)"
|
||||||
@@ -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 } }
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param([string]$SourceRoot)
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||||
|
if (!$SourceRoot) { $SourceRoot = Join-Path $repo 'external/ZLUDA' }
|
||||||
|
$SourceRoot = [IO.Path]::GetFullPath($SourceRoot)
|
||||||
|
$base = 'ee2f25a180099fa42f36b2346732e1f2470a03ad'
|
||||||
|
if (!(Test-Path (Join-Path $SourceRoot 'Cargo.toml'))) { throw 'Clone upstream ZLUDA into external/ZLUDA first.' }
|
||||||
|
Push-Location $SourceRoot
|
||||||
|
try {
|
||||||
|
$revision = (& git rev-parse HEAD).Trim()
|
||||||
|
if ($revision -ne $base) { throw "Expected ZLUDA $base, found $revision; review patches against the new revision." }
|
||||||
|
$dirty = @(& git status --porcelain --untracked-files=no --ignore-submodules=all)
|
||||||
|
if ($dirty.Count) { throw 'ZLUDA source has tracked modifications. Keep this prepared checkout; do not reapply patches.' }
|
||||||
|
& git switch -c windows-rdna4
|
||||||
|
if ($LASTEXITCODE) { throw 'Could not create the local ZLUDA branch.' }
|
||||||
|
foreach ($patch in (Get-ChildItem -LiteralPath (Join-Path $repo 'patches/zluda') -Filter '*.patch' | Sort-Object Name)) {
|
||||||
|
& git apply --check $patch.FullName
|
||||||
|
if ($LASTEXITCODE) { throw "Patch check failed: $($patch.Name)" }
|
||||||
|
& git apply --whitespace=nowarn $patch.FullName
|
||||||
|
if ($LASTEXITCODE) { throw "Patch failed: $($patch.Name)" }
|
||||||
|
Write-Host "Applied $($patch.Name)"
|
||||||
|
}
|
||||||
|
Write-Host "Prepared Windows ZLUDA source at $SourceRoot (base $base)"
|
||||||
|
} finally { Pop-Location }
|
||||||
Loaded 100 of 213 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user