feat: add d4r inference engines and Windows parity tooling
Add native K/M/RR inference engines, HIP kernels, replay and validation harnesses, and Windows DLSS setup and runtime support. Include GPU profiling tools, build and packaging scripts, platform integration patches, and implementation documentation.
This commit is contained in:
1 parent
25493b3198
commit
7c5d3c9157
430 files changed
+76191
No files matched your search
@@ -0,0 +1,10 @@
|
||||
# M11 historical Windows scripts
|
||||
|
||||
The source archive's native-Windows M11 scripts are under
|
||||
[`../../windows/m11/tools/`](../../windows/m11/tools/) and its shim build scripts
|
||||
under [`../../windows/m11/src/`](../../windows/m11/src/).
|
||||
|
||||
They deliberately retain their original **internal** layout because many derive
|
||||
paths from `$PSScriptRoot`. This pointer does not imply they are interchangeable
|
||||
with the mainline `scripts/windows/` build and release tooling. See
|
||||
[`../../docs/windows-m11.md`](../../docs/windows-m11.md).
|
||||
@@ -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,47 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$HipRoot = $env:HIP_PATH,
|
||||
[ValidateSet('gfx1100','gfx1101','gfx1102','gfx1103','gfx1150','gfx1151','gfx1152','gfx1153','gfx1154','gfx1200','gfx1201')]
|
||||
[string]$GpuArch = 'gfx1101',
|
||||
[int]$Parallel = [Math]::Max(1, [Environment]::ProcessorCount - 1)
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$HipRoot) { throw 'Pass -HipRoot or set HIP_PATH to the Windows HIP SDK root.' }
|
||||
$HipRoot = [IO.Path]::GetFullPath($HipRoot)
|
||||
if (!(Test-Path -LiteralPath (Join-Path $HipRoot 'include/hip/hip_runtime_api.h'))) {
|
||||
throw "HIP headers not found under $HipRoot"
|
||||
}
|
||||
& (Join-Path $PSScriptRoot 'setup-native-engine.ps1')
|
||||
if ($LASTEXITCODE) { throw "Native engine dependency setup failed: $LASTEXITCODE" }
|
||||
|
||||
$tools = Join-Path $repo '.tools'
|
||||
$cmake = Join-Path $tools 'python/cmake/data/bin/cmake.exe'
|
||||
$ninja = Join-Path $tools 'python/ninja/data/bin/ninja.exe'
|
||||
if (!(Test-Path -LiteralPath $cmake)) { $cmake = Join-Path $tools 'python/bin/cmake.exe' }
|
||||
if (!(Test-Path -LiteralPath $ninja)) { $ninja = Join-Path $tools 'python/bin/ninja.exe' }
|
||||
if (!(Test-Path -LiteralPath $cmake) -or !(Test-Path -LiteralPath $ninja)) {
|
||||
throw 'Pinned CMake/Ninja missing. Run scripts/windows/setup-windows-tools.ps1 first.'
|
||||
}
|
||||
$toolchain = Join-Path $tools 'llvm-mingw-20260922-ucrt-x86_64/bin'
|
||||
$cCompiler = Join-Path $toolchain 'x86_64-w64-mingw32-clang.exe'
|
||||
$cxxCompiler = Join-Path $toolchain 'x86_64-w64-mingw32-clang++.exe'
|
||||
if (!(Test-Path -LiteralPath $cCompiler) -or !(Test-Path -LiteralPath $cxxCompiler)) {
|
||||
throw 'Pinned llvm-mingw compiler missing. Run scripts/windows/setup-windows-tools.ps1 first.'
|
||||
}
|
||||
$oldPath = $env:PATH
|
||||
$env:PATH = "$toolchain;$oldPath"
|
||||
try {
|
||||
$build = Join-Path $repo "build/native-engine-windows-$GpuArch"
|
||||
$headers = Join-Path $tools 'Vulkan-Headers-v1.4.341'
|
||||
$volk = Join-Path $tools 'volk'
|
||||
& $cmake --fresh -S $repo -B $build -G Ninja "-DCMAKE_MAKE_PROGRAM=$ninja" `
|
||||
"-DCMAKE_C_COMPILER=$cCompiler" "-DCMAKE_CXX_COMPILER=$cxxCompiler" `
|
||||
"-DD4R_HIP_ROOT=$HipRoot" "-DD4R_GPU_ARCH=$GpuArch" `
|
||||
'-DD4R_BUILD_NATIVE_ENGINE=ON' "-DD4R_VULKAN_HEADERS_ROOT=$headers" "-DD4R_VOLK_ROOT=$volk" `
|
||||
"-DD4R_MINHOOK_ROOT=$(Join-Path $repo 'external/MinHook')"
|
||||
if ($LASTEXITCODE) { throw "Native engine configure failed: $LASTEXITCODE" }
|
||||
& $cmake --build $build --target d4r_engine_k d4r_engine_m d4r_engine_rr --parallel $Parallel
|
||||
if ($LASTEXITCODE) { throw "Native engine build failed: $LASTEXITCODE" }
|
||||
} finally { $env:PATH = $oldPath }
|
||||
Write-Host "Built native runners in ${build}: d4r-k.exe, d4r-m.exe, d4r-rr.exe."
|
||||
@@ -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,60 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$DlssDll, [string]$HipRoot, [string]$OutputDirectory, [switch]$HardwarePacking, [switch]$TokenLanes)
|
||||
$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' }
|
||||
if ($TokenLanes) { $flags+='-DSWIN_TOKEN_LANES' }
|
||||
$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'; tokenLanes=[bool]$TokenLanes;
|
||||
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,59 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$DlssdDll,
|
||||
[ValidateSet('D','E')][string]$Preset='D',
|
||||
[string]$OutputDirectory,
|
||||
[string]$GitBash='C:\Program Files\Git\bin\bash.exe'
|
||||
)
|
||||
$ErrorActionPreference='Stop'
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
$DlssdDll=(Get-Item -LiteralPath $DlssdDll -ErrorAction Stop).FullName
|
||||
$GitBash=(Get-Item -LiteralPath $GitBash -ErrorAction Stop).FullName
|
||||
$expectedHash='4bc7ea5fcb2f32cf86bc2cb072e8d2860914a0be511cbdcb2fc206c1d9d83b80'
|
||||
$hash=(Get-FileHash -LiteralPath $DlssdDll -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
if($hash -ne $expectedHash){throw "Unsupported nvngx_dlssd.dll SHA256 $hash; expected NVIDIA DLSS 310.9.1 ($expectedHash)."}
|
||||
if(!$OutputDirectory){$OutputDirectory=Join-Path $repo ("build/native-rr-$($Preset.ToLower())-windows-gfx1101")}
|
||||
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||
$buildRoot=[IO.Path]::GetFullPath((Join-Path $repo 'build'))
|
||||
if(!$OutputDirectory.StartsWith($buildRoot+'\',[StringComparison]::OrdinalIgnoreCase)){
|
||||
throw "RR model output must stay under the ignored build directory: $OutputDirectory"
|
||||
}
|
||||
if(Test-Path -LiteralPath $OutputDirectory){throw "Use a new RR model output directory: $OutputDirectory"}
|
||||
$glslang=Join-Path $repo '.tools/glslang-16.6.0/bin'
|
||||
$pythonHome=Split-Path (Get-Command python.exe -ErrorAction Stop).Source
|
||||
$pythonShim=Join-Path $repo '.tools/rr-build-bin'
|
||||
New-Item -ItemType Directory -Force $pythonShim | Out-Null
|
||||
$python3=Join-Path $pythonShim 'python3.exe'
|
||||
Copy-Item -LiteralPath (Get-Command python.exe).Source -Destination $python3 -Force
|
||||
$pythonVendor=Join-Path $repo '.tools/python/vendor'
|
||||
$cygpath=Join-Path (Split-Path (Split-Path $GitBash -Parent) -Parent) 'usr/bin/cygpath.exe'
|
||||
if(!(Test-Path -LiteralPath $cygpath)){throw "Git for Windows cygpath missing: $cygpath"}
|
||||
foreach($tool in @($python3,(Join-Path $glslang 'glslangValidator.exe'))){if(!(Test-Path -LiteralPath $tool)){throw "RR build tool missing: $tool"}}
|
||||
$unixRepo=(& $cygpath -u $repo).Trim()
|
||||
$unixDll=(& $cygpath -u $DlssdDll).Trim()
|
||||
$unixOut=(& $cygpath -u $OutputDirectory).Trim()
|
||||
if(!$unixRepo -or !$unixDll -or !$unixOut){throw 'Git Bash path conversion failed.'}
|
||||
$oldPath=$env:PATH
|
||||
$oldPythonPath=$env:PYTHONPATH
|
||||
$env:PATH="$pythonShim;$pythonHome;$glslang;$(Split-Path $GitBash -Parent);$oldPath"
|
||||
$env:PYTHONPATH=$pythonVendor
|
||||
$env:D4R_RR_GFX11='1'
|
||||
$env:D4R_RR_RELAXED='0'
|
||||
$log=Join-Path $buildRoot ("native-rr-$($Preset.ToLower())-windows-build.log")
|
||||
try{
|
||||
& $python3 -c 'import numpy; print("numpy", numpy.__version__)'
|
||||
if($LASTEXITCODE){throw 'Pinned NumPy could not load in the Windows RR build environment.'}
|
||||
$command="cd '$unixRepo' && D4R_RR_GFX11=1 D4R_RR_RELAXED=0 bash engine/rr/build_native_model.sh --dll '$unixDll' '$Preset' '$unixOut'"
|
||||
$previousPreference=$ErrorActionPreference
|
||||
try{
|
||||
$ErrorActionPreference='Continue'
|
||||
& $GitBash -lc $command *> $log
|
||||
$bashExit=$LASTEXITCODE
|
||||
}finally{$ErrorActionPreference=$previousPreference}
|
||||
if($bashExit){throw "RR model build failed with exit $bashExit; see $log"}
|
||||
foreach($required in @('rr.txt','model.json','enc0_m.spv','dec0_m.spv','hkpn.spv','enc0.args','hkpn.args')){
|
||||
if(!(Test-Path -LiteralPath (Join-Path $OutputDirectory $required))){throw "RR build incomplete; missing $required"}
|
||||
}
|
||||
Write-Host "Built local preset $Preset native RR model: $OutputDirectory"
|
||||
Write-Host "DLL SHA256: $hash. Build log: $log"
|
||||
}finally{$env:PATH=$oldPath;$env:PYTHONPATH=$oldPythonPath}
|
||||
@@ -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,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', 'd4r\nvapi\backend\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\dlss\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; 'kernels.nativefp8' = 1 }
|
||||
$boolean = @('dlss.showwatermark', 'interop.asyncinterop', 'debug.validateoutput', 'kernels.nativefp8')
|
||||
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,205 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$PackageRoot = $PSScriptRoot,
|
||||
[string]$CacheRoot = (Join-Path $env:LOCALAPPDATA 'd4r/cache'),
|
||||
[string]$BackupRoot = (Join-Path $env:LOCALAPPDATA 'd4r/backups'),
|
||||
[switch]$Yes
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$source = [IO.Path]::GetFullPath($PackageRoot)
|
||||
$runtime = if (Test-Path -LiteralPath (Join-Path $source 'd4r/d4r.ini')) {
|
||||
Join-Path $source 'd4r'
|
||||
} elseif (Test-Path -LiteralPath (Join-Path $source 'd4r.ini')) {
|
||||
$source
|
||||
} else {
|
||||
throw "No d4r runtime found under $source. Extract the Windows release first."
|
||||
}
|
||||
$runtime = [IO.Path]::GetFullPath($runtime)
|
||||
$CacheRoot = [IO.Path]::GetFullPath($CacheRoot)
|
||||
$BackupRoot = [IO.Path]::GetFullPath($BackupRoot)
|
||||
$gameRoot = if ($runtime -eq $source) { Split-Path -Parent $source } else { $source }
|
||||
$gameRoot = [IO.Path]::GetFullPath($gameRoot)
|
||||
$backupRoot = Join-Path $BackupRoot ('dlss-install-' + [DateTime]::UtcNow.ToString('yyyyMMdd-HHmmss') + '-' + [Guid]::NewGuid().ToString('N'))
|
||||
foreach ($path in @($CacheRoot, $BackupRoot)) {
|
||||
if ($path.Equals($gameRoot, [StringComparison]::OrdinalIgnoreCase) -or
|
||||
$path.StartsWith($gameRoot.TrimEnd('\') + '\', [StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "Cache and backups must stay outside the game directory: $path"
|
||||
}
|
||||
}
|
||||
|
||||
$dlssUrl = 'https://raw.githubusercontent.com/NVIDIA/DLSS/374959484e79a640feaba44c93ac8cfb0a03f5b5/lib/Windows_x86_64/rel/nvngx_dlss.dll'
|
||||
$dlssHash = '3975567b8943c53acce397f2b72380092f84f162d00b0d2c7d08a1025c563983'
|
||||
$dlssdUrl = 'https://raw.githubusercontent.com/NVIDIA/DLSS/374959484e79a640feaba44c93ac8cfb0a03f5b5/lib/Windows_x86_64/rel/nvngx_dlssd.dll'
|
||||
$dlssdHash = '4bc7ea5fcb2f32cf86bc2cb072e8d2860914a0be511cbdcb2fc206c1d9d83b80'
|
||||
$driverUrl = 'https://us.download.nvidia.com/Windows/617.14/617.14-desktop-win10-win11-64bit-international-dch-whql.exe'
|
||||
$driverHash = '63d3da5ed52547bf3fd86204bb9ea1455947b24e2aa2519f359126f9a6efe13f'
|
||||
$ngxHash = '66767018c36b3bab46398dade3adf173daa3730fda75965689ea848c9bc4e79b'
|
||||
$sevenZipUrl = 'https://github.com/ip7z/7zip/releases/download/26.04/7zr.exe'
|
||||
$sevenZipHash = '256feca8e274e5da655e2a284fabafd9f554365eb164862089dacd4e8276d282'
|
||||
|
||||
function Get-Sha256([string]$Path) {
|
||||
$sha = [Security.Cryptography.SHA256]::Create()
|
||||
$stream = [IO.File]::OpenRead($Path)
|
||||
try { return [BitConverter]::ToString($sha.ComputeHash($stream)).Replace('-', '').ToLowerInvariant() }
|
||||
finally { $stream.Dispose(); $sha.Dispose() }
|
||||
}
|
||||
|
||||
$pinPath = Join-Path $runtime 'dll-pins.txt'
|
||||
if (!(Test-Path -LiteralPath $pinPath -PathType Leaf)) { throw "Missing $pinPath. Use a current d4r Windows release." }
|
||||
$pins = Get-Content -LiteralPath $pinPath -Raw
|
||||
foreach ($required in @(@{kind='dlss'; hash=$dlssHash}, @{kind='dlssd'; hash=$dlssdHash}, @{kind='ngx'; hash=$ngxHash})) {
|
||||
if ($pins -notmatch ('(?im)^\s*' + [regex]::Escape($required.kind) + '\s+' + [regex]::Escape($required.hash) + '\s+\S+\s+\S+\s*$')) {
|
||||
throw "This release does not recognize NVIDIA $($required.kind) hash $($required.hash). Update the d4r package first."
|
||||
}
|
||||
}
|
||||
|
||||
if (!$Yes) {
|
||||
$answer = Read-Host 'Download DLSS 310.9.1, Ray Reconstruction 310.9.1, and NVIDIA NGX core from driver 617.14 (~945 MiB)? [Y/n]'
|
||||
if ($answer -match '^(?i)n') { Write-Host 'No files changed.'; exit 0 }
|
||||
}
|
||||
|
||||
$tempRoot = [IO.Path]::GetFullPath([IO.Path]::GetTempPath())
|
||||
$work = Join-Path $tempRoot ('d4r-dlss-install-' + [Guid]::NewGuid().ToString('N'))
|
||||
$work = [IO.Path]::GetFullPath($work)
|
||||
if (!$work.StartsWith($tempRoot.TrimEnd('\') + '\', [StringComparison]::OrdinalIgnoreCase) -or
|
||||
(Split-Path -Leaf $work) -notmatch '^d4r-dlss-install-[a-f0-9]{32}$') {
|
||||
throw "Temporary work path escaped the expected temp folder: $work"
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $work | Out-Null
|
||||
$cacheNvidia = Join-Path $CacheRoot 'nvidia'
|
||||
$cacheTools = Join-Path $CacheRoot 'tools'
|
||||
New-Item -ItemType Directory -Force -Path $cacheNvidia,$cacheTools | Out-Null
|
||||
|
||||
function Get-VerifiedFile([string]$Name, [string]$Url, [string]$ExpectedHash, [string]$CacheDirectory, [string]$WorkDirectory) {
|
||||
$cached = Join-Path $CacheDirectory $Name
|
||||
if (Test-Path -LiteralPath $cached -PathType Leaf) {
|
||||
$hash = Get-Sha256 $cached
|
||||
if ($hash -eq $ExpectedHash) { return $cached }
|
||||
$quarantine = "$cached.bad-$([DateTime]::UtcNow.ToString('yyyyMMdd-HHmmss'))"
|
||||
Move-Item -LiteralPath $cached -Destination $quarantine
|
||||
Write-Warning "Moved invalid cache file to $quarantine"
|
||||
}
|
||||
$download = Join-Path $WorkDirectory $Name
|
||||
Write-Host "Downloading $Name from its pinned official source..."
|
||||
Invoke-WebRequest -UseBasicParsing -Uri $Url -OutFile $download
|
||||
$hash = Get-Sha256 $download
|
||||
if ($hash -ne $ExpectedHash) { throw "SHA256 mismatch for $Name; no runtime files changed." }
|
||||
$staged = "$cached.tmp-$([Guid]::NewGuid().ToString('N'))"
|
||||
Copy-Item -LiteralPath $download -Destination $staged
|
||||
Move-Item -LiteralPath $staged -Destination $cached
|
||||
return $cached
|
||||
}
|
||||
|
||||
$changed = [Collections.Generic.List[object]]::new()
|
||||
try {
|
||||
$dlssCached = Get-VerifiedFile 'nvngx_dlss.dll' $dlssUrl $dlssHash $cacheNvidia $work
|
||||
$dlssdCached = Get-VerifiedFile 'nvngx_dlssd.dll' $dlssdUrl $dlssdHash $cacheNvidia $work
|
||||
$ngxCache = Join-Path $cacheNvidia '_nvngx.dll'
|
||||
$validCoreCache = (Test-Path -LiteralPath $ngxCache -PathType Leaf) -and
|
||||
(Get-Sha256 $ngxCache) -eq $ngxHash
|
||||
if ($validCoreCache -and (Get-Item -LiteralPath $ngxCache).VersionInfo.FileVersion -ne '32.0.16.1714') {
|
||||
$validCoreCache = $false
|
||||
}
|
||||
if (!$validCoreCache) {
|
||||
if (Test-Path -LiteralPath $ngxCache -PathType Leaf) {
|
||||
$bad = "$ngxCache.bad-$([DateTime]::UtcNow.ToString('yyyyMMdd-HHmmss'))"
|
||||
Move-Item -LiteralPath $ngxCache -Destination $bad
|
||||
Write-Warning "Moved invalid cache file to $bad"
|
||||
}
|
||||
$driverPath = Join-Path $work 'nvidia-driver-617.14.exe'
|
||||
Write-Host 'Downloading NVIDIA driver 617.14 (~945 MiB); installer will not run.'
|
||||
Invoke-WebRequest -UseBasicParsing -Uri $driverUrl -OutFile $driverPath
|
||||
$driverActual = Get-Sha256 $driverPath
|
||||
if ($driverActual -ne $driverHash) { throw 'NVIDIA driver archive SHA256 mismatch; no runtime files changed.' }
|
||||
$sevenZip = Get-VerifiedFile '7zr-26.04.exe' $sevenZipUrl $sevenZipHash $cacheTools $work
|
||||
$extract = Join-Path $work 'ngx'
|
||||
New-Item -ItemType Directory -Force -Path $extract | Out-Null
|
||||
& $sevenZip e -y "-o$extract" $driverPath 'Display.Driver/_nvngx.dll' | Out-Host
|
||||
if ($LASTEXITCODE -ne 0) { throw "7-Zip extraction failed: exit=$LASTEXITCODE" }
|
||||
$extractedNgx = Join-Path $extract '_nvngx.dll'
|
||||
if (!(Test-Path -LiteralPath $extractedNgx -PathType Leaf)) { throw 'NVIDIA driver archive has no Display.Driver/_nvngx.dll.' }
|
||||
$actual = Get-Sha256 $extractedNgx
|
||||
if ($actual -ne $ngxHash) { throw "Extracted NGX core SHA256 mismatch: $actual" }
|
||||
$version = (Get-Item -LiteralPath $extractedNgx).VersionInfo.FileVersion
|
||||
if ($version -ne '32.0.16.1714') { throw "Unexpected NGX core version $version; expected 32.0.16.1714." }
|
||||
$stagedNgx = "$ngxCache.tmp-$([Guid]::NewGuid().ToString('N'))"
|
||||
Copy-Item -LiteralPath $extractedNgx -Destination $stagedNgx
|
||||
Move-Item -LiteralPath $stagedNgx -Destination $ngxCache
|
||||
}
|
||||
|
||||
$assets = @(
|
||||
@{ source=$dlssCached; relative='dlss/nvngx_dlss.dll'; hash=$dlssHash },
|
||||
@{ source=$dlssdCached; relative='rr/nvngx_dlssd.dll'; hash=$dlssdHash },
|
||||
@{ source=$ngxCache; relative='ngx/_nvngx.dll'; hash=$ngxHash }
|
||||
)
|
||||
$originals = [Collections.Generic.List[object]]::new()
|
||||
foreach ($asset in $assets) {
|
||||
$destination = Join-Path $runtime $asset.relative
|
||||
if (Test-Path -LiteralPath $destination -PathType Container) { throw "DLL destination is a directory: $destination" }
|
||||
$present = Test-Path -LiteralPath $destination -PathType Leaf
|
||||
$previousHash = if ($present) { Get-Sha256 $destination } else { $null }
|
||||
if ($previousHash -eq $asset.hash) { continue }
|
||||
$backup = $null
|
||||
if ($present) {
|
||||
$backup = Join-Path $backupRoot $asset.relative
|
||||
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $backup) | Out-Null
|
||||
Copy-Item -LiteralPath $destination -Destination $backup
|
||||
$backupHash = Get-Sha256 $backup
|
||||
if ($backupHash -ne $previousHash) { throw "Backup hash mismatch for $destination; no runtime files changed." }
|
||||
}
|
||||
$originals.Add(@{path=$destination; existed=$present; sha256=$previousHash; backup=$backup; installHash=$asset.hash})
|
||||
}
|
||||
if ($originals.Count) {
|
||||
New-Item -ItemType Directory -Force -Path $backupRoot | Out-Null
|
||||
$changed = $originals
|
||||
@{ createdUtc=[DateTime]::UtcNow.ToString('o'); files=$originals } |
|
||||
ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $backupRoot 'backup-manifest.json') -Encoding UTF8
|
||||
foreach ($entry in $originals) {
|
||||
$asset = $assets | Where-Object { (Join-Path $runtime $_.relative) -eq $entry.path } | Select-Object -First 1
|
||||
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $entry.path) | Out-Null
|
||||
$stillPresent = Test-Path -LiteralPath $entry.path -PathType Leaf
|
||||
if ($entry.existed) {
|
||||
if (!$stillPresent -or (Get-Sha256 $entry.path) -ne $entry.sha256) {
|
||||
throw "Destination changed after backup; refusing to replace $($entry.path)"
|
||||
}
|
||||
} elseif (Test-Path -LiteralPath $entry.path) {
|
||||
throw "Destination appeared after validation; refusing to replace $($entry.path)"
|
||||
}
|
||||
$staged = "$($entry.path).install-$([Guid]::NewGuid().ToString('N'))"
|
||||
Copy-Item -LiteralPath $asset.source -Destination $staged
|
||||
Move-Item -LiteralPath $staged -Destination $entry.path -Force
|
||||
}
|
||||
Write-Host "Backups and original hashes: $backupRoot"
|
||||
} else {
|
||||
Write-Host 'All three NVIDIA DLLs already match their pinned hashes.'
|
||||
}
|
||||
Write-Host 'Installed NVIDIA DLSS 310.9.1, Ray Reconstruction 310.9.1, and NGX 32.0.16.1714.'
|
||||
Write-Host 'Files remain local under the applicable NVIDIA licenses; no binaries are bundled into d4r releases.'
|
||||
Write-Host 'This installer does not build local K/M/L or Ray Reconstruction model data.'
|
||||
} catch {
|
||||
for ($i = $changed.Count - 1; $i -ge 0; $i--) {
|
||||
$entry = $changed[$i]
|
||||
if (!(Test-Path -LiteralPath $entry.path)) { continue }
|
||||
if ($entry.existed -and $entry.backup) {
|
||||
$currentHash = Get-Sha256 $entry.path
|
||||
if ($currentHash -ne $entry.installHash -and $currentHash -ne $entry.sha256) {
|
||||
Write-Warning "Rollback left a file changed by another process: $($entry.path)"
|
||||
continue
|
||||
}
|
||||
$rollback = "$($entry.path).rollback-$([Guid]::NewGuid().ToString('N'))"
|
||||
Copy-Item -LiteralPath $entry.backup -Destination $rollback
|
||||
Move-Item -LiteralPath $rollback -Destination $entry.path -Force
|
||||
} elseif ((Get-Sha256 $entry.path) -eq $entry.installHash) {
|
||||
Remove-Item -LiteralPath $entry.path
|
||||
}
|
||||
}
|
||||
throw
|
||||
} finally {
|
||||
$resolvedWork = [IO.Path]::GetFullPath($work)
|
||||
if (!$resolvedWork.StartsWith($tempRoot.TrimEnd('\') + '\', [StringComparison]::OrdinalIgnoreCase) -or
|
||||
(Split-Path -Leaf $resolvedWork) -notmatch '^d4r-dlss-install-[a-f0-9]{32}$') {
|
||||
throw "Refusing to remove a path outside the installer temp folder: $resolvedWork"
|
||||
}
|
||||
Remove-Item -LiteralPath $resolvedWork -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
[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 'nvapi-compat/nvapi64.dll') 'd4r/zluda/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','libc++.dll','libunwind.dll')) { Stage (Join-Path $ZludaRoot $file) "d4r/zluda/$file" }
|
||||
Stage (Join-Path $ZludaRoot 'nvapi64.dll') 'd4r/nvapi/backend/nvapi64.dll'
|
||||
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 'packaging/windows/d4r.ini') 'd4r/d4r.ini'
|
||||
Stage (Join-Path $repo 'tools/windows/dll-pins.txt') 'd4r/dll-pins.txt'
|
||||
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'
|
||||
$mBuildInfoPath=Join-Path $KernelMRoot 'build-info.json'
|
||||
$nativeFp8M=$false; $tokenLanesM=$false
|
||||
if (Test-Path -LiteralPath $mBuildInfoPath) {
|
||||
$mBuildInfo=Get-Content -LiteralPath $mBuildInfoPath -Raw | ConvertFrom-Json
|
||||
if ($mBuildInfo.architecture -ne $GpuArch -or $mBuildInfo.family -ne 'M') { throw "M build metadata does not match ${GpuArch}: $mBuildInfoPath" }
|
||||
$nativeFp8M=[bool]$mBuildInfo.nativeFp8
|
||||
$tokenLanesM=[bool]$mBuildInfo.tokenLanes
|
||||
}
|
||||
if ($nativeFp8M) {
|
||||
if (!$tokenLanesM -or $GpuArch -notmatch '^gfx12') { throw 'Native FP8 M packages require the gfx12 token-lane build.' }
|
||||
$marker=Join-Path $PackageRoot 'd4r/native/d4r-native-fp8.txt'
|
||||
New-Item -ItemType Directory -Force (Split-Path $marker) | Out-Null
|
||||
@('D4R_NATIVE_FP8 1',"architecture=$GpuArch",'family=M','tokenLanes=true') |
|
||||
Set-Content -LiteralPath $marker -Encoding ASCII
|
||||
$stagedFiles.Add('d4r/native/d4r-native-fp8.txt')
|
||||
}
|
||||
$metadata = [ordered]@{
|
||||
architecture=$GpuArch; hardwareValidation=$(if ($GpuArch -eq 'gfx1201') { 'RX9070XT tested; other SKUs unverified' } else { 'compile-only; hardware unverified' }); nativeKernels=16; dlssSha256='3975567b8943c53acce397f2b72380092f84f162d00b0d2c7d08a1025c563983';
|
||||
nativeFp8M=$nativeFp8M; tokenLanesM=$tokenLanesM;
|
||||
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,113 @@
|
||||
[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;
|
||||
nativeFp8M=[bool]$metadata.nativeFp8M; tokenLanesM=[bool]$metadata.tokenLanesM;
|
||||
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-E.cmd'; arguments='-Preset 5'},@{name='START-K.cmd'; arguments='-Preset 11'},
|
||||
@{name='START-L.cmd'; arguments='-Preset 12'},@{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,108 @@
|
||||
# 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 'd4r/nvapi/backend/nvapi64.dll' -or $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;
|
||||
nativeFp8M=[bool]$metadata.nativeFp8M; tokenLanesM=[bool]$metadata.tokenLanesM}
|
||||
}
|
||||
$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='packaging/windows/install.bat'; path='install.bat'},
|
||||
@{source='scripts/windows/install-dlss.ps1'; path='install-dlss.ps1'},
|
||||
@{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 }
|
||||
@@ -0,0 +1,44 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[ValidateSet(5,11,12,13)][int]$Preset = 11,
|
||||
[string]$ZludaRoot, [string]$PackageRoot, [string]$OutputDirectory,
|
||||
[string]$NativeKernelDirectory,
|
||||
[int]$Iterations = 4,
|
||||
[switch]$Translated, [switch]$NativeFp8
|
||||
)
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' }
|
||||
if (!$PackageRoot) { $PackageRoot=Join-Path $repo 'dist/windows-rdna4-command-list' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/profile-' + $Preset + '-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
$nativeDirectory=if ($NativeFp8) {
|
||||
if ($GpuArch -notmatch '^gfx12' -or $Preset -notin @(12,13)) { throw '-NativeFp8 requires gfx12 and preset L/M.' }
|
||||
if (!$NativeKernelDirectory) { $NativeKernelDirectory=Join-Path $repo 'build/native-m-fp8-token-lane-hwpack' }
|
||||
$info=Get-Content -LiteralPath (Join-Path $NativeKernelDirectory 'build-info.json') -Raw | ConvertFrom-Json
|
||||
if ($info.architecture -ne $GpuArch -or !$info.nativeFp8 -or !$info.tokenLanes) { throw 'NativeFp8 needs the matching gfx12 token-lane M kernel bundle.' }
|
||||
[IO.Path]::GetFullPath($NativeKernelDirectory)
|
||||
} elseif ($Translated -or $Preset -eq 5) { $null }
|
||||
elseif ($Preset -eq 11) { Join-Path $repo "build/native-k-$GpuArch" }
|
||||
else { Join-Path $repo "build/native-m-$GpuArch" }
|
||||
$settings=@{D4R_ZLUDA_PROFILE='1'; D4R_PROFILE_STAGES='1'; D4R_VALIDATE_OUTPUT='1';
|
||||
D4R_ZLUDA_PROFILE_DEFERRED=$null; D4R_ZLUDA_PROFILE_ALLOW_LEGACY=$null; D4R_ZLUDA_PROFILE_EVERY=$null;
|
||||
D4R_ZLUDA_NATIVE_DIR=$nativeDirectory}
|
||||
$settings['D4R_ZLUDA_WMMA_FP8_NATIVE']=if ($NativeFp8) { '1' } else { $null }
|
||||
$old=@{}
|
||||
try {
|
||||
foreach ($key in $settings.Keys) { $old[$key]=[Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') }
|
||||
$arguments=@{RuntimeProfile='therock'; ZludaRoot=$ZludaRoot; PackageRoot=$PackageRoot; OutputDirectory=$OutputDirectory;
|
||||
NgxCore=(Join-Path $repo '_nvngx.dll'); DlssDll=(Join-Path $repo 'nvngx_dlss.dll'); NgxMode='d3d12';
|
||||
NgxAbi='project-legacy'; Preset=$Preset; NgxOnly=$true; RequireNativeNetwork=(!$Translated -and $Preset -ne 5); NativeFp8=$NativeFp8; CommandListBackend=$true;
|
||||
CaptureExceptions=$true; EarlyIndirectProbe=$true; PixelProfile='depth-stencil'; BarrierMode='inherited-legacy';
|
||||
Iterations=$Iterations; TimeoutSeconds=300; OptiScalerDll=(Join-Path $repo 'dist/optiscaler-windows-d4r/OptiScaler.dll')}
|
||||
& (Join-Path $PSScriptRoot 'test-windows-rdna4.ps1') @arguments
|
||||
if ($LASTEXITCODE) { throw 'Profiling workload failed; see diagnostic bundle.' }
|
||||
$python=(Get-Command python.exe -ErrorAction Stop).Source
|
||||
& $python (Join-Path $repo 'tools/windows/profile_report.py') $OutputDirectory --output (Join-Path $OutputDirectory 'profile.json')
|
||||
if ($LASTEXITCODE) { throw 'No usable profile records.' }
|
||||
} finally {
|
||||
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[ValidateSet('run','install','restore')][string]$Action='run',
|
||||
[ValidateSet(5,11,12,13)][int]$Preset=11,
|
||||
[string]$GameExe, [string]$NgxCore, [string]$DlssDll,
|
||||
[switch]$NativeFp8, [switch]$CaptureExceptions,
|
||||
[ValidateRange(30,600)][int]$RunSeconds=180
|
||||
)
|
||||
$ErrorActionPreference='Stop'
|
||||
$root=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
||||
$work=Join-Path $root 'work'
|
||||
$settingsPath=Join-Path $work 'settings.json'
|
||||
New-Item -ItemType Directory -Force $work | Out-Null
|
||||
$saved=$null
|
||||
if (Test-Path -LiteralPath $settingsPath) {
|
||||
try { $saved=Get-Content -LiteralPath $settingsPath -Raw | ConvertFrom-Json }
|
||||
catch { Write-Host 'Saved choices could not be read. Select the files again.' }
|
||||
}
|
||||
function Select-LocalFile([string]$Title, [string]$FileName, [string]$Previous) {
|
||||
if ($Previous -and (Test-Path -LiteralPath $Previous -PathType Leaf)) { return (Get-Item -LiteralPath $Previous).FullName }
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
$dialog=New-Object Windows.Forms.OpenFileDialog
|
||||
try {
|
||||
$dialog.Title=$Title; $dialog.FileName=$FileName
|
||||
$dialog.Filter=if ($FileName -eq '*.exe') { 'Game executable (*.exe)|*.exe' } else { "$FileName|$FileName" }
|
||||
if ($dialog.ShowDialog() -ne [Windows.Forms.DialogResult]::OK) { throw 'File selection cancelled. No test was launched.' }
|
||||
return $dialog.FileName
|
||||
} finally { $dialog.Dispose() }
|
||||
}
|
||||
function Copy-Payload([string]$Source, [string]$Destination, [string]$Relative) {
|
||||
$path=[IO.Path]::GetFullPath((Join-Path $Destination $Relative))
|
||||
if (!$path.StartsWith($Destination+'\',[StringComparison]::OrdinalIgnoreCase)) { throw 'Invalid package path.' }
|
||||
New-Item -ItemType Directory -Force (Split-Path $path) | Out-Null
|
||||
Copy-Item -LiteralPath (Join-Path $Source $Relative) -Destination $path -Force
|
||||
}
|
||||
function Get-BundledNvidiaFile([string]$Relative) {
|
||||
$directory=Join-Path $PSScriptRoot 'nvidia'
|
||||
$path=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot $Relative))
|
||||
if (!$path.StartsWith($directory+'\',[StringComparison]::OrdinalIgnoreCase) -or
|
||||
!(Test-Path -LiteralPath $path -PathType Leaf)) { throw 'Invalid bundled NVIDIA file. Extract the complete package again.' }
|
||||
return $path
|
||||
}
|
||||
if ($Action -eq 'restore') {
|
||||
$GameExe=Select-LocalFile 'Select the SAME game executable used for the test' '*.exe' $(if ($GameExe) { $GameExe } elseif ($saved) { $saved.game })
|
||||
& (Join-Path $PSScriptRoot 'common/windows-game.ps1') -Action restore -GameExe $GameExe
|
||||
Write-Host 'RESTORED. Launch the game normally again.' -ForegroundColor Green
|
||||
return
|
||||
}
|
||||
$output=Join-Path $root ('results/test-'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff'))
|
||||
New-Item -ItemType Directory -Force $output | Out-Null
|
||||
$status=[ordered]@{action=$Action; preset=$Preset; status='starting'; hardwareValidationCompleted=$false}
|
||||
$selectedPackage=$null
|
||||
try {
|
||||
Write-Host 'd4r Windows quick test - keep this window open until you EXIT the game.'
|
||||
Write-Host 'Checking package and detecting the GPU...'
|
||||
$manifest=Get-Content -LiteralPath (Join-Path $PSScriptRoot 'package.json') -Raw | ConvertFrom-Json
|
||||
foreach ($file in $manifest.files) {
|
||||
$path=[IO.Path]::GetFullPath((Join-Path $root $file.path))
|
||||
if (!$path.StartsWith($root+'\',[StringComparison]::OrdinalIgnoreCase) -or
|
||||
(Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash -ne $file.sha256) { throw "Package is incomplete or changed. Extract the ZIP again. File: $($file.path)" }
|
||||
}
|
||||
$status.packageCommit=$manifest.packageCommit
|
||||
$inventoryDir=Join-Path $output 'gpu'
|
||||
New-Item -ItemType Directory -Force $inventoryDir | Out-Null
|
||||
$info=New-Object Diagnostics.ProcessStartInfo
|
||||
$info.FileName=Join-Path $PSScriptRoot 'targets/gfx1201/d4r/d4r_gpu_inventory.exe'
|
||||
$info.Arguments='--hip-root "'+(Join-Path $PSScriptRoot 'common/d4r/hip')+'" --output-dir "'+$inventoryDir+'"'
|
||||
$info.UseShellExecute=$false; $info.CreateNoWindow=$true
|
||||
$info.EnvironmentVariables['D4R_DIAG_DIR']=$inventoryDir
|
||||
$info.RedirectStandardOutput=$true; $info.RedirectStandardError=$true
|
||||
$process=New-Object Diagnostics.Process; $process.StartInfo=$info
|
||||
try {
|
||||
if (!$process.Start()) { throw 'GPU detection could not start.' }
|
||||
$stdout=$process.StandardOutput.ReadToEndAsync(); $stderr=$process.StandardError.ReadToEndAsync()
|
||||
$timedOut=!$process.WaitForExit(30000)
|
||||
if ($timedOut) { $process.Kill(); $process.WaitForExit() }
|
||||
$stdout.GetAwaiter().GetResult() | Set-Content (Join-Path $inventoryDir 'stdout.log') -Encoding UTF8
|
||||
$stderr.GetAwaiter().GetResult() | Set-Content (Join-Path $inventoryDir 'stderr.log') -Encoding UTF8
|
||||
$status.inventoryExit='0x{0:X8}' -f [BitConverter]::ToUInt32([BitConverter]::GetBytes([int]$process.ExitCode),0)
|
||||
if ($timedOut -or $process.ExitCode) { throw 'HIP could not detect your GPU. The diagnostic ZIP contains the details.' }
|
||||
} finally { $process.Dispose() }
|
||||
$inventory=Get-Content -LiteralPath (Join-Path $inventoryDir 'gpu-inventory.json') -Raw | ConvertFrom-Json
|
||||
$devices=@($inventory.devices | Where-Object { $_.buildEligible -and $_.wavefrontSize -eq 32 -and $manifest.targets.PSObject.Properties.Name -contains $_.architecture } | Sort-Object vramBytes -Descending)
|
||||
if (!$devices.Count) { throw 'No supported RDNA3/RDNA4 HIP device was detected. See the GPU inventory in the diagnostic ZIP.' }
|
||||
$device=$devices[0]
|
||||
if (@($devices.architecture | Select-Object -Unique).Count -gt 1) {
|
||||
for ($i=0; $i -lt $devices.Count; $i++) { Write-Host "$($i+1): $($devices[$i].name) ($($devices[$i].architecture))" }
|
||||
$choice=Read-Host 'Select the GPU used by your game (press Enter for 1)'
|
||||
if ($choice) {
|
||||
$index=0
|
||||
if (![int]::TryParse($choice,[ref]$index) -or $index -lt 1 -or $index -gt $devices.Count) { throw 'Invalid GPU selection.' }
|
||||
$device=$devices[$index-1]
|
||||
}
|
||||
}
|
||||
$arch=$device.architecture; $target=$manifest.targets.$arch
|
||||
if ($NativeFp8 -and ($arch -notmatch '^gfx12' -or !$target.nativeFp8M -or $Preset -notin @(12,13))) {
|
||||
throw '-NativeFp8 needs a gfx12 package containing the experimental native-FP8 M kernels and preset L or M.'
|
||||
}
|
||||
$status.gpu=$device; $status.binarySourceCommit=$target.binarySourceCommit
|
||||
$status.nativeFp8=[bool]$NativeFp8; $status.tokenLanesM=[bool]$target.tokenLanesM
|
||||
Write-Host "Detected: $($device.name) / $arch" -ForegroundColor Cyan
|
||||
Write-Host 'Experimental build. This run is a correctness test, not an FPS benchmark.'
|
||||
$previousGame=if ($saved) { $saved.game } else { $null }
|
||||
if (!$GameExe -and $previousGame -and (Test-Path -LiteralPath $previousGame)) {
|
||||
Write-Host "Remembered game: $previousGame"
|
||||
$reuse=Read-Host 'Press Enter to use this game, or type C to choose another (restore the old game first)'
|
||||
if ($reuse -match '^(?i)c$') { $previousGame=$null }
|
||||
elseif ($reuse) { throw 'Invalid choice. Press Enter or type C on the next run.' }
|
||||
}
|
||||
$GameExe=Select-LocalFile 'Select the actual D3D12 game .exe (not Steam or a launcher)' '*.exe' $(if ($GameExe) { $GameExe } else { $previousGame })
|
||||
$bundledCore=$null; $bundledDlss=$null
|
||||
if ($manifest.bundledNvidia) {
|
||||
$bundledCore=Get-BundledNvidiaFile $manifest.bundledNvidia.ngxCore
|
||||
$bundledDlss=Get-BundledNvidiaFile $manifest.bundledNvidia.dlssDll
|
||||
}
|
||||
$status.nvidiaDllSource=@{core=$(if ($NgxCore) { 'explicit' } elseif ($bundledCore) { 'bundled' } else { 'user-supplied' });
|
||||
dlss=$(if ($DlssDll) { 'explicit' } elseif ($bundledDlss) { 'bundled' } else { 'user-supplied' })}
|
||||
if ($bundledCore -and !$NgxCore -and !$DlssDll) { Write-Host 'Using the validated NVIDIA DLL pair included by this package distributor.' }
|
||||
$NgxCore=Select-LocalFile 'Select your ORIGINAL NVIDIA _nvngx.dll (32.0.16.1714)' '_nvngx.dll' $(if ($NgxCore) { $NgxCore } elseif ($bundledCore) { $bundledCore } elseif ($saved) { $saved.ngxCore })
|
||||
$DlssDll=Select-LocalFile 'Select your NVIDIA nvngx_dlss.dll (310.9.1)' 'nvngx_dlss.dll' $(if ($DlssDll) { $DlssDll } elseif ($bundledDlss) { $bundledDlss } elseif ($saved) { $saved.dlssDll })
|
||||
$status.game=$GameExe
|
||||
$status.localDlls=@($NgxCore,$DlssDll | ForEach-Object { $item=Get-Item -LiteralPath $_; @{path=$item.FullName; version=$item.VersionInfo.FileVersion; sha256=(Get-FileHash -LiteralPath $_ -Algorithm SHA256).Hash} })
|
||||
if ((Get-FileHash -LiteralPath $NgxCore -Algorithm SHA256).Hash -ne $manifest.ngxCoreSha256) {
|
||||
throw 'Wrong _nvngx.dll. This test needs the original NVIDIA 32.0.16.1714 DLL, not the d4r shim. Select the correct file on the next run.'
|
||||
}
|
||||
if ((Get-FileHash -LiteralPath $DlssDll -Algorithm SHA256).Hash -ne $target.dlssSha256) {
|
||||
throw 'Wrong nvngx_dlss.dll. This test needs exactly NVIDIA DLSS 310.9.1. Select the correct file on the next run.'
|
||||
}
|
||||
if ($GameExe.StartsWith($root+'\',[StringComparison]::OrdinalIgnoreCase)) { throw 'Select the installed GAME executable, outside this extracted package.' }
|
||||
# Save only validated choices. Wrong DLLs are never remembered.
|
||||
@{game=$GameExe; ngxCore=$NgxCore; dlssDll=$DlssDll; architecture=$arch} | ConvertTo-Json | Set-Content -LiteralPath $settingsPath -Encoding UTF8
|
||||
$selectedPackage=Join-Path $work "package-$arch"
|
||||
New-Item -ItemType Directory -Force $selectedPackage | Out-Null
|
||||
$selectedFiles=@()
|
||||
foreach ($file in $manifest.commonFiles) {
|
||||
Copy-Payload (Join-Path $PSScriptRoot 'common') $selectedPackage $file.path
|
||||
$selectedFiles+=$file
|
||||
}
|
||||
foreach ($file in $target.files) {
|
||||
Copy-Payload (Join-Path $PSScriptRoot "targets/$arch") $selectedPackage $file.path
|
||||
$selectedFiles+=$file
|
||||
}
|
||||
@{architecture=$arch; d4rCommit=$target.binarySourceCommit; packageCommit=$manifest.packageCommit;
|
||||
dlssSha256=$target.dlssSha256; hardwareValidation=$target.hardwareValidation;
|
||||
nativeFp8M=[bool]$target.nativeFp8M; tokenLanesM=[bool]$target.tokenLanesM; files=$selectedFiles} |
|
||||
ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Join-Path $selectedPackage 'package.json') -Encoding UTF8
|
||||
Write-Host 'Original game files will be backed up. RESTORE-GAME.cmd reverses the installation.'
|
||||
Write-Host 'In the game: select DLSS if available; otherwise select FSR/XeSS to let OptiScaler intercept it.'
|
||||
Write-Host 'The first launch compiles shaders and can take several minutes. Then play for 30 seconds and EXIT the game normally.'
|
||||
& (Join-Path $selectedPackage 'windows-game.ps1') -Action $Action -GameExe $GameExe -NgxCore $NgxCore -DlssDll $DlssDll `
|
||||
-Preset $Preset -RunSeconds $RunSeconds -NativeFp8:$NativeFp8 -ValidateOutput -CaptureExceptions:$CaptureExceptions -OutputDirectory $output
|
||||
if ($Action -eq 'install') { $status.status='installed-only' }
|
||||
else {
|
||||
$summary=Get-Content -LiteralPath (Join-Path $output 'summary.json') -Raw | ConvertFrom-Json
|
||||
$status.gameSummary=$summary
|
||||
$required=Get-Content -LiteralPath (Join-Path $selectedPackage 'd4r/native/d4r-kernels.txt') | Where-Object {
|
||||
if ($Preset -eq 11) { $_ -match '^dltss_pwin_' } elseif ($Preset -in @(12,13)) { $_ -match '^rrlite_' }
|
||||
} | ForEach-Object { $_.Split(' ')[0] }
|
||||
$hits=@($summary.kernels | Where-Object { $_.backend -eq 'native' -and $_.launches -gt 0 } | ForEach-Object { $_.kernel })
|
||||
$status.missingNativeKernels=@($required | Where-Object { $hits -notcontains $_ })
|
||||
$status.translatedNativeKernels=@($summary.kernels | Where-Object { $_.backend -eq 'translated' -and $required -contains $_.kernel } | ForEach-Object { $_.kernel })
|
||||
$requiredNativePath=($Preset -eq 5) -or ($summary.nativeLaunches -gt 0)
|
||||
$expectedNativeCount=if ($Preset -eq 5) { 0 } elseif ($Preset -eq 11) { 11 } else { 5 }
|
||||
$passed=$summary.exitCode -eq '0x0' -and $summary.completedFrames -gt 0 -and $requiredNativePath -and
|
||||
$summary.outputGpuChecks -gt 0 -and $summary.failures -eq 0 -and $summary.nonfiniteOutputs -eq 0 -and
|
||||
$summary.previousFrameOutputs -eq 0 -and $summary.cpuImageCopyFrames -eq 0 -and
|
||||
@($required).Count -eq $expectedNativeCount -and
|
||||
$status.missingNativeKernels.Count -eq 0 -and $status.translatedNativeKernels.Count -eq 0
|
||||
$status.hardwareValidationCompleted=[bool]$passed
|
||||
$status.status=if ($passed) { 'backend-checks-passed-image-still-needs-user-review' }
|
||||
elseif ($summary.completedFrames -eq 0 -and $summary.exitCode -eq '0x0' -and $summary.failures -eq 0) { 'inconclusive-process-exited-without-dlss-frames' }
|
||||
else { 'backend-checks-failed-or-no-dlss-frames' }
|
||||
$status.session=$summary.session
|
||||
if ($status.status -eq 'inconclusive-process-exited-without-dlss-frames') {
|
||||
Write-Host 'The launched process exited without a DLSS frame. A Steam/launcher restart is not tracked by this test. Keep Steam open and select the actual game executable. Send the ZIP if it restarts again.' -ForegroundColor Yellow
|
||||
}
|
||||
Write-Host $(if ($passed) { 'Backend checks PASSED. Please also report whether the image looked correct.' } else { 'Test did not pass. Send the diagnostic ZIP; do not guess from the FPS.' }) -ForegroundColor $(if ($passed) { 'Green' } else { 'Yellow' })
|
||||
}
|
||||
} catch {
|
||||
$status.status='failed'; $status.error=$_.Exception.Message
|
||||
$_ | Out-String | Set-Content -LiteralPath (Join-Path $output 'error.txt') -Encoding UTF8
|
||||
Write-Host "TEST STOPPED: $($status.error)" -ForegroundColor Red
|
||||
} finally {
|
||||
if ($selectedPackage) {
|
||||
$preflights=Join-Path $selectedPackage 'results'
|
||||
$latest=Get-ChildItem -LiteralPath $preflights -Directory -ErrorAction SilentlyContinue | Sort-Object Name -Descending | Select-Object -First 1
|
||||
if ($latest) { Copy-Item -LiteralPath $latest.FullName -Destination (Join-Path $output 'preflight') -Recurse -Force }
|
||||
}
|
||||
try { Get-CimInstance Win32_VideoController | Select-Object Name,DriverVersion,DriverDate | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $output 'driver.json') -Encoding UTF8 } catch {}
|
||||
$status | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath (Join-Path $output 'quick-test.json') -Encoding UTF8
|
||||
Compress-Archive -LiteralPath $output -DestinationPath ($output+'.zip') -Force
|
||||
Write-Host "SEND THIS ONE FILE: $output.zip" -ForegroundColor Cyan
|
||||
Write-Host 'Also tell us: GPU model, game name, K or M, and whether the image was correct.'
|
||||
Write-Host 'Close the game before running RESTORE-GAME.cmd.'
|
||||
}
|
||||
if ($status.status -eq 'failed' -or $status.status -eq 'backend-checks-failed-or-no-dlss-frames' -or $status.status.StartsWith('inconclusive-')) { exit 1 }
|
||||
@@ -0,0 +1,32 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][int]$GameProcessId,
|
||||
[ValidateSet('Enter','Escape','Up','Down','Tab','Space')][string]$Key='Enter')
|
||||
$ErrorActionPreference='Stop'
|
||||
$process=Get-Process -Id $GameProcessId -ErrorAction Stop
|
||||
if ($process.ProcessName -eq 'GhostOfTsushima') {
|
||||
$expected='C:\Program Files (x86)\Steam\steamapps\common\Ghost of Tsushima DIRECTOR''S CUT\GhostOfTsushima.exe'
|
||||
if ($process.Path -ine $expected) { throw 'Ghost of Tsushima process path does not match the authorized Steam installation.' }
|
||||
} elseif ($process.ProcessName -ne 'SHProto-Win64-Shipping') {
|
||||
throw 'This helper only accepts the authorized Ghost of Tsushima or Silent Hill 2 diagnostic process.'
|
||||
}
|
||||
if (!('D4RGameKeys' -as [type])) {
|
||||
Add-Type @'
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
public static class D4RGameKeys {
|
||||
[DllImport("user32.dll")] public static extern bool PostMessage(IntPtr window, uint message, IntPtr key, IntPtr flags);
|
||||
[DllImport("user32.dll")] public static extern uint MapVirtualKey(uint code, uint kind);
|
||||
[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
|
||||
}
|
||||
'@
|
||||
}
|
||||
$window=$process.MainWindowHandle
|
||||
if ($window -eq [IntPtr]::Zero) { throw 'Game window missing on this desktop.' }
|
||||
if ([D4RGameKeys]::GetForegroundWindow() -ne $window) { throw 'Target game is not foreground; key was not sent.' }
|
||||
$codes=@{Enter=13; Escape=27; Up=38; Down=40; Tab=9; Space=32}
|
||||
$code=$codes[$Key]; $scan=[D4RGameKeys]::MapVirtualKey($code,0)
|
||||
$flags=[long](1 -bor ($scan -shl 16))
|
||||
if ($Key -in @('Up','Down')) { $flags=$flags -bor 0x01000000 }
|
||||
if (![D4RGameKeys]::PostMessage($window,0x100,[IntPtr]$code,[IntPtr]$flags)) { throw 'Game key-down post failed.' }
|
||||
if (![D4RGameKeys]::PostMessage($window,0x101,[IntPtr]$code,[IntPtr]($flags -bor 0xc0000000L))) { throw 'Game key-up post failed.' }
|
||||
Write-Host "Sent $Key only to diagnostic game PID $GameProcessId."
|
||||
@@ -0,0 +1,50 @@
|
||||
[CmdletBinding()]
|
||||
param()
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
$tools = Join-Path $repo '.tools'
|
||||
$downloads = Join-Path $tools 'downloads'
|
||||
New-Item -ItemType Directory -Force -Path $downloads | Out-Null
|
||||
|
||||
function Ensure-PinnedGitCheckout([string]$Path, [string]$Url, [string]$Commit) {
|
||||
if (!(Test-Path -LiteralPath $Path)) {
|
||||
git clone --no-checkout $Url $Path
|
||||
if ($LASTEXITCODE) { throw "Git clone failed: $Url" }
|
||||
}
|
||||
git -C $Path fetch --depth 1 origin $Commit
|
||||
if ($LASTEXITCODE) { throw "Pinned dependency fetch failed: $Url@$Commit" }
|
||||
git -C $Path checkout --detach $Commit
|
||||
if ($LASTEXITCODE) { throw "Pinned dependency checkout failed: $Url@$Commit" }
|
||||
$actual = (git -C $Path rev-parse HEAD).Trim()
|
||||
if ($actual -ne $Commit) { throw "Dependency revision mismatch: $Path ($actual)" }
|
||||
}
|
||||
|
||||
$headers = Join-Path $tools 'Vulkan-Headers-v1.4.341'
|
||||
Ensure-PinnedGitCheckout $headers 'https://github.com/KhronosGroup/Vulkan-Headers.git' 'b5c8f996196ba4aa6d8f97e52b5d3b6e70f7e4e2'
|
||||
$volk = Join-Path $tools 'volk'
|
||||
Ensure-PinnedGitCheckout $volk 'https://github.com/zeux/volk.git' '0dc3ce00bf98b9f0b6fe708ca0f7eb74e2830173'
|
||||
|
||||
$glslangDirectory = Join-Path $tools 'glslang-16.6.0'
|
||||
$validator = Join-Path $glslangDirectory 'bin/glslangValidator.exe'
|
||||
$archive = Join-Path $downloads 'glslang-16.6.0-windows-x86_64-release.zip'
|
||||
$expectedHash = '82bf434e69b9bb4829de7e2b4bc2c5e7a7861e53d66cf75e5cc70f5f694a8d9b'
|
||||
if (!(Test-Path -LiteralPath $validator)) {
|
||||
if (!(Test-Path -LiteralPath $archive)) {
|
||||
Invoke-WebRequest -UseBasicParsing `
|
||||
-Uri 'https://github.com/KhronosGroup/glslang/releases/download/16.6.0/glslang-16.6.0-windows-x86_64-release.zip' `
|
||||
-OutFile $archive
|
||||
}
|
||||
$actualHash = (Get-FileHash -LiteralPath $archive -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
if ($actualHash -ne $expectedHash) { throw "glslang archive SHA256 mismatch: $actualHash" }
|
||||
Expand-Archive -LiteralPath $archive -DestinationPath $glslangDirectory -Force
|
||||
$releaseBinary = Join-Path $glslangDirectory 'bin/glslang.exe'
|
||||
if (!(Test-Path -LiteralPath $releaseBinary)) { throw "glslang archive layout changed: $releaseBinary" }
|
||||
Copy-Item -LiteralPath $releaseBinary -Destination $validator
|
||||
}
|
||||
if (!(Test-Path -LiteralPath $headers -PathType Container) -or
|
||||
!(Test-Path -LiteralPath (Join-Path $headers 'include/vulkan/vulkan.h')) -or
|
||||
!(Test-Path -LiteralPath (Join-Path $volk 'volk.c')) -or
|
||||
!(Test-Path -LiteralPath $validator)) {
|
||||
throw 'Native engine dependencies are incomplete.'
|
||||
}
|
||||
Write-Host "Native engine dependencies ready: Vulkan-Headers $((git -C $headers rev-parse --short HEAD).Trim()), Volk $((git -C $volk rev-parse --short HEAD).Trim()), glslang 16.6.0."
|
||||
@@ -0,0 +1,48 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$ToolRoot, [string]$DownloadRoot)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$ToolRoot) { $ToolRoot = Join-Path $repo '.tools/rust-1.98.1-gnu' }
|
||||
if (!$DownloadRoot) { $DownloadRoot = Join-Path $repo '.tools/downloads' }
|
||||
$ToolRoot = [IO.Path]::GetFullPath($ToolRoot)
|
||||
$DownloadRoot = [IO.Path]::GetFullPath($DownloadRoot)
|
||||
New-Item -ItemType Directory -Force $ToolRoot,$DownloadRoot | Out-Null
|
||||
# Pinned official Rust 2026-09-03 manifest. The toolchain is local to this
|
||||
# workspace; rustup, PATH changes and machine-wide installation are unnecessary.
|
||||
$components = @(
|
||||
@{Name='rustc'; Hash='f0e8e33973771acc4d2f87891b19d4f3f2d3827e2e7848c091d7c070bd63479c'},
|
||||
@{Name='cargo'; Hash='4bd77f16bd2a26db6eacf9320414d3a792d9998cb5e9ac122280ba56126f2a44'},
|
||||
@{Name='rust-std'; Hash='5bb599a541fcb9c0edc00e512570f60d2262623f1e2a19a44cce3a7a97208788'},
|
||||
@{Name='rust-mingw'; Hash='75d898804789c12ca969365f0a86d85c3bca1fdb070f0be615ca513a986b2674'}
|
||||
)
|
||||
foreach ($component in $components) {
|
||||
$package = "$($component.Name)-1.98.1-x86_64-pc-windows-gnu"
|
||||
$archive = Join-Path $DownloadRoot "$package.tar.xz"
|
||||
if (!(Test-Path $archive) -or (Get-FileHash -LiteralPath $archive -Algorithm SHA256).Hash -ne $component.Hash) {
|
||||
Write-Host "Downloading official Rust component $($component.Name)"
|
||||
& curl.exe --fail --location --silent --show-error --connect-timeout 15 --max-time 300 --retry 2 `
|
||||
"https://static.rust-lang.org/dist/2026-09-03/$package.tar.xz" --output $archive
|
||||
if ($LASTEXITCODE) { throw "Download failed for $package (exit=$LASTEXITCODE)" }
|
||||
}
|
||||
if ((Get-FileHash -LiteralPath $archive -Algorithm SHA256).Hash -ne $component.Hash) {
|
||||
throw "Rust component SHA256 mismatch: $archive"
|
||||
}
|
||||
$extract = Join-Path $DownloadRoot $package
|
||||
if (!(Test-Path (Join-Path $extract 'components'))) {
|
||||
& tar.exe -xf $archive -C $DownloadRoot
|
||||
if ($LASTEXITCODE) { throw "Extract failed: $archive" }
|
||||
}
|
||||
foreach ($entry in (Get-Content -LiteralPath (Join-Path $extract 'components'))) {
|
||||
$payload = Join-Path $extract $entry
|
||||
foreach ($directory in @('bin','lib','share','etc')) {
|
||||
$source = Join-Path $payload $directory
|
||||
if (Test-Path $source) { Copy-Item -LiteralPath $source -Destination $ToolRoot -Recurse -Force }
|
||||
}
|
||||
}
|
||||
Write-Host "Verified and installed $package"
|
||||
}
|
||||
& (Join-Path $ToolRoot 'bin/rustc.exe') --version
|
||||
if ($LASTEXITCODE) { throw 'Rust compiler failed to start' }
|
||||
& (Join-Path $ToolRoot 'bin/cargo.exe') --version
|
||||
if ($LASTEXITCODE) { throw 'Cargo failed to start' }
|
||||
Write-Host "Local Rust toolchain: $ToolRoot"
|
||||
@@ -0,0 +1,48 @@
|
||||
[CmdletBinding()]
|
||||
param([ValidateSet('stable', 'therock')][string]$RuntimeProfile = 'stable')
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
$localTools = Join-Path $repo '.tools'
|
||||
$downloadDirectory = Join-Path $localTools 'downloads'
|
||||
New-Item -ItemType Directory -Force $downloadDirectory | Out-Null
|
||||
function Get-VerifiedArchive([string]$Url, [string]$Name, [string]$Hash, [string]$Destination, [string]$Marker) {
|
||||
if (Test-Path -LiteralPath $Marker) { Write-Host "Already present: $Marker"; return }
|
||||
$archive = Join-Path $downloadDirectory $Name
|
||||
if (!(Test-Path -LiteralPath $archive)) {
|
||||
Invoke-WebRequest -UseBasicParsing -Uri $Url -OutFile $archive
|
||||
}
|
||||
if ((Get-FileHash -LiteralPath $archive -Algorithm SHA256).Hash -ne $Hash) {
|
||||
throw "SHA256 mismatch: $archive (remove this download and retry)"
|
||||
}
|
||||
if ([IO.Path]::GetExtension($archive) -eq '.whl') {
|
||||
python -c 'import sys,zipfile; zipfile.ZipFile(sys.argv[1]).extractall(sys.argv[2])' $archive $Destination
|
||||
if ($LASTEXITCODE) { throw "Wheel extraction exit=$LASTEXITCODE" }
|
||||
} else { Expand-Archive -LiteralPath $archive -DestinationPath $Destination -Force }
|
||||
if (!(Test-Path -LiteralPath $Marker)) { throw "Archive layout changed: $Marker" }
|
||||
}
|
||||
Get-VerifiedArchive 'https://github.com/mstorsjo/llvm-mingw/releases/download/20260922/llvm-mingw-20260922-ucrt-x86_64.zip' `
|
||||
'llvm-mingw.zip' 'e3ad77d117a4bea19a7a3b333341824d79a5a371004a10e25b8504e7b3047666' $localTools `
|
||||
(Join-Path $localTools 'llvm-mingw-20260922-ucrt-x86_64/bin/x86_64-w64-mingw32-clang++.exe')
|
||||
Get-VerifiedArchive 'https://github.com/vosen/ZLUDA/releases/download/v7-preview.11/zluda-windows-ee2f25a.zip' `
|
||||
'zluda.zip' '7788c1ed43385e62f0cc5f4d1aead24c92b671c02f1d685f63182540dea16d74' (Join-Path $localTools 'zluda') `
|
||||
(Join-Path $localTools 'zluda/zluda/nvcuda.dll')
|
||||
if ($RuntimeProfile -eq 'therock') {
|
||||
Get-VerifiedArchive 'https://nightly.repo.amd.com/rocm/core/whl-next/rocm-sdk-core/rocm_sdk_core-10.2.0a20260929-py3-none-win_amd64.whl' `
|
||||
'rocm_sdk_core-10.2.0a20260929-py3-none-win_amd64.whl' `
|
||||
'9623b97ca511eaa176905075a504393eca6fde594222af10b55e4fb1edfe5a25' `
|
||||
(Join-Path $localTools 'therock-10.2.0a20260929') `
|
||||
(Join-Path $localTools 'therock-10.2.0a20260929/_rocm_sdk_core/bin/amdhip64_7.dll')
|
||||
}
|
||||
if (!(Test-Path (Join-Path $localTools 'python/cmake/data/bin/cmake.exe')) -or
|
||||
!(Test-Path (Join-Path $localTools 'python/bin/ninja.exe'))) {
|
||||
python -m pip install --target (Join-Path $localTools 'python') cmake==3.31.6 ninja==1.11.1.4
|
||||
if ($LASTEXITCODE) { throw "pip install exit=$LASTEXITCODE" }
|
||||
}
|
||||
if (!(Test-Path (Join-Path $localTools 'python/vendor/numpy/__init__.py'))) {
|
||||
python -m pip install --disable-pip-version-check --only-binary=:all: `
|
||||
--target (Join-Path $localTools 'python/vendor') numpy==2.4.6
|
||||
if ($LASTEXITCODE) { throw "NumPy reference install exit=$LASTEXITCODE" }
|
||||
}
|
||||
& (Join-Path $PSScriptRoot 'setup-native-engine.ps1')
|
||||
if ($LASTEXITCODE) { throw "Native engine dependency setup exit=$LASTEXITCODE" }
|
||||
Write-Host 'Tools ready. Run scripts/windows/build-windows-rdna4.ps1 or scripts/windows/build-native-engine.ps1.'
|
||||
@@ -0,0 +1,30 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$DlssDll, [string]$PackageRoot, [string]$OutputDirectory, [string]$GpuArch)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $GpuArch
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo "build/native-k-$GpuArch" }
|
||||
if (![IO.Path]::IsPathRooted($DlssDll) -or !(Test-Path -LiteralPath $DlssDll)) {
|
||||
throw 'Supply the absolute path to your local nvngx_dlss.dll.'
|
||||
}
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
foreach ($object in Get-ChildItem -LiteralPath $OutputDirectory -Filter '*.hsaco') {
|
||||
Assert-D4RCodeObjectTarget $object.FullName $GpuArch
|
||||
}
|
||||
$layers = @('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0')
|
||||
foreach ($layer in $layers) {
|
||||
$name = "dltss_pwin_${layer}_layer"
|
||||
$source = Join-Path $PackageRoot "experimental/k/${name}_${GpuArch}.hsaco"
|
||||
if (!(Test-Path -LiteralPath $source)) { throw "Native K module missing: $source" }
|
||||
Assert-D4RCodeObjectTarget $source $GpuArch
|
||||
Copy-Item -LiteralPath $source -Destination (Join-Path $OutputDirectory "$name.hsaco") -Force
|
||||
}
|
||||
& python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $OutputDirectory $DlssDll
|
||||
if ($LASTEXITCODE) { throw "Native manifest generation failed ($LASTEXITCODE)" }
|
||||
@{architecture=$GpuArch; family='K'; dlssSha256=(Get-FileHash -LiteralPath $DlssDll -Algorithm SHA256).Hash;
|
||||
validation='staged only; GPU execution and DLSS capture/reference validation required';
|
||||
modules=@(Get-ChildItem -LiteralPath $OutputDirectory -Filter '*.hsaco' | Get-FileHash -Algorithm SHA256)} |
|
||||
ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'build-info.json') -Encoding UTF8
|
||||
Write-Host "Experimental native K directory: $OutputDirectory"
|
||||
@@ -0,0 +1,30 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$DlssDll, [string]$PackageRoot, [string]$OutputDirectory, [string]$GpuArch)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $GpuArch
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo "build/native-m-$GpuArch" }
|
||||
if (![IO.Path]::IsPathRooted($DlssDll) -or !(Test-Path -LiteralPath $DlssDll)) {
|
||||
throw 'Supply the absolute path to your local nvngx_dlss.dll.'
|
||||
}
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
foreach ($object in Get-ChildItem -LiteralPath $OutputDirectory -Filter '*.hsaco') {
|
||||
Assert-D4RCodeObjectTarget $object.FullName $GpuArch
|
||||
}
|
||||
foreach ($layer in @('enc1','enc2','enc3_tube','dec2','dec1')) {
|
||||
$name = "rrlite_${layer}_4x4"
|
||||
$source = Join-Path $PackageRoot "experimental/m/${name}_${GpuArch}.hsaco"
|
||||
if (!(Test-Path -LiteralPath $source)) { throw "Native M module missing: $source" }
|
||||
Assert-D4RCodeObjectTarget $source $GpuArch
|
||||
Copy-Item -LiteralPath $source -Destination (Join-Path $OutputDirectory "$name.hsaco") -Force
|
||||
}
|
||||
& python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $OutputDirectory $DlssDll
|
||||
if ($LASTEXITCODE) { throw "Native manifest generation failed ($LASTEXITCODE)" }
|
||||
@{architecture=$GpuArch; family='M'; numerics='SWIN_EXACT; SWIN_EXACT_PV; FP16 widening; native FP8 disabled';
|
||||
dlssSha256=(Get-FileHash -LiteralPath $DlssDll -Algorithm SHA256).Hash;
|
||||
validation='staged only; GPU execution and DLSS capture/reference validation required';
|
||||
modules=@(Get-ChildItem -LiteralPath $OutputDirectory -Filter '*.hsaco' | Get-FileHash -Algorithm SHA256)} |
|
||||
ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'build-info.json') -Encoding UTF8
|
||||
Write-Host "Experimental native M directory: $OutputDirectory"
|
||||
@@ -0,0 +1,84 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$NgxCore, [string]$DlssDll, [string]$NativeRoot,
|
||||
[string]$ZludaRoot, [string]$PackageRoot,
|
||||
[string]$Resolution = '3840x2160',
|
||||
[string]$InputResolution,
|
||||
[switch]$ProfileGpuBoundary,
|
||||
[switch]$ProfileKernelsDeferred,
|
||||
[switch]$BatchInputCopies,
|
||||
[ValidateSet('baseline','packed','unorm','depth-stencil')][string]$PixelProfile = 'depth-stencil',
|
||||
[string]$OutputDirectory
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$NgxCore) { $NgxCore = Join-Path $repo '_nvngx.dll' }
|
||||
if (!$DlssDll) { $DlssDll = Join-Path $repo 'nvngx_dlss.dll' }
|
||||
if (!$ZludaRoot) { $ZludaRoot = Join-Path $repo 'dist/zluda-windows-api-profile' }
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-command-list' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$NativeRoot) { $NativeRoot = Join-Path $repo "build/native-k-$GpuArch" }
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/async-k-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
if ($Resolution -notmatch '^([0-9]+)x([0-9]+)$') { throw 'Resolution must be WIDTHxHEIGHT' }
|
||||
$width=$Matches[1]; $height=$Matches[2]
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$settings = @{D4R_DIAG_BURST='1'; D4R_DIAG_RECREATE=$null; D4R_ASYNC_INTEROP=$null;
|
||||
D4R_BATCH_INPUT_COPIES=$null;
|
||||
D4R_ZLUDA_NATIVE_DIR=$NativeRoot; D4R_QUIET_API='1'; D4R_VALIDATE_OUTPUT='1';
|
||||
D4R_PROFILE_STAGES='1'; D4R_ZLUDA_PROFILE=$null; D4R_ZLUDA_PROFILE_API=$null;
|
||||
D4R_ZLUDA_PROFILE_DEFERRED=$(if ($ProfileKernelsDeferred) { '1' } else { $null });
|
||||
D4R_ZLUDA_PROFILE_ALLOW_LEGACY=$(if ($ProfileKernelsDeferred) { '1' } else { $null });
|
||||
D4R_ZLUDA_PROFILE_EVERY=$(if ($ProfileKernelsDeferred) { '1' } else { $null });
|
||||
D4R_PROFILE_GPU_BOUNDARY=$(if ($ProfileGpuBoundary) { '1' } else { $null });
|
||||
ZLUDA_CACHE_DIR=(Join-Path $repo 'build/zluda-cache-windows');
|
||||
PYTHONPATH=(Join-Path $repo '.tools/python/vendor')}
|
||||
$old=@{}
|
||||
try {
|
||||
foreach ($key in $settings.Keys) {
|
||||
$old[$key]=[Environment]::GetEnvironmentVariable($key,'Process')
|
||||
[Environment]::SetEnvironmentVariable($key,$settings[$key],'Process')
|
||||
}
|
||||
foreach ($mode in @('burst','recreate')) {
|
||||
[Environment]::SetEnvironmentVariable('D4R_DIAG_RECREATE', $(if ($mode -eq 'recreate') { '1' } else { $null }), 'Process')
|
||||
foreach ($variant in @('sync','async')) {
|
||||
[Environment]::SetEnvironmentVariable('D4R_ASYNC_INTEROP', $(if ($variant -eq 'async') { '1' } else { $null }), 'Process')
|
||||
[Environment]::SetEnvironmentVariable('D4R_BATCH_INPUT_COPIES', $(if ($BatchInputCopies -and $variant -eq 'async') { '1' } else { $null }), 'Process')
|
||||
$output=Join-Path $OutputDirectory "$variant-$mode"
|
||||
$arguments=@{RuntimeProfile='therock'; ZludaRoot=$ZludaRoot; PackageRoot=$PackageRoot;
|
||||
NgxCore=$NgxCore; DlssDll=$DlssDll; NgxOnly=$true; NgxMode='d3d12'; NgxAbi='project-legacy';
|
||||
Preset=11; NgxCreateFlags=11; NgxOutputResolution=$Resolution; RequireNativeNetwork=$true;
|
||||
NgxInputResolution=$InputResolution;
|
||||
CommandListBackend=$true; EarlyIndirectProbe=$true; PixelProfile=$PixelProfile;
|
||||
BarrierMode='inherited-legacy'; Iterations=3; CaptureExceptions=$true; TimeoutSeconds=600;
|
||||
OptiScalerDll=(Join-Path $repo 'dist/optiscaler-windows-d4r/OptiScaler.dll'); OutputDirectory=$output}
|
||||
& (Join-Path $PSScriptRoot 'test-windows-rdna4.ps1') @arguments
|
||||
if ($LASTEXITCODE) { throw "Failed $variant-$mode; logs: $output" }
|
||||
if ($ProfileKernelsDeferred) {
|
||||
$stderr=Get-Content -LiteralPath (Join-Path $output 'd3d12-evaluate-preset-11.stderr.log') -Raw
|
||||
if ($stderr -match '(?m)^D4R_KERNEL_PROFILE .*serializing=1') { throw 'Unexpected serializing kernel profiler.' }
|
||||
foreach ($layer in @('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0')) {
|
||||
$pattern='(?m)^D4R_KERNEL_PROFILE(?:_INVALID)? kernel="dltss_pwin_' + $layer + '_layer" backend=native phase=main '
|
||||
$events=[regex]::Matches($stderr,$pattern)
|
||||
if ($events.Count -ne 3) { throw "Expected three collected $layer events for $variant-$mode, found $($events.Count)." }
|
||||
}
|
||||
}
|
||||
if ($ProfileGpuBoundary -and $variant -eq 'async') {
|
||||
$stdout=Get-Content -LiteralPath (Join-Path $output 'd3d12-evaluate-preset-11.stdout.log') -Raw
|
||||
$timings=[regex]::Matches($stdout, '(?m)^D4R_GPU_BOUNDARY [^\r\n]*diagnostics_cpu_bytes=32 serializing=0\r?$')
|
||||
if ($timings.Count -ne 3) { throw "Expected three completed GPU timing records for $variant-$mode, found $($timings.Count)." }
|
||||
}
|
||||
}
|
||||
& python.exe (Join-Path $repo 'tools/windows/frame_compare.py') --reference (Join-Path $OutputDirectory "sync-$mode") --actual (Join-Path $OutputDirectory "async-$mode") --width $width --height $height --exact |
|
||||
Tee-Object -FilePath (Join-Path $OutputDirectory "$mode-comparison.log")
|
||||
if ($LASTEXITCODE) { throw "Async $mode output mismatch" }
|
||||
}
|
||||
@{passed=$true; architecture=$GpuArch; preset=11; resolution=$Resolution;
|
||||
inputResolution=$InputResolution;
|
||||
modes=@('burst','recreate'); framesPerMode=3; exactRgb=$true; gpuBoundaryTiming=[bool]$ProfileGpuBoundary;
|
||||
deferredKernelTiming=[bool]$ProfileKernelsDeferred; batchInputs=[bool]$BatchInputCopies; pixelProfile=$PixelProfile} |
|
||||
ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory 'validation.json') -Encoding UTF8
|
||||
Write-Host "PASS asynchronous K burst/recreate: $OutputDirectory"
|
||||
} finally {
|
||||
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
param(
|
||||
[int]$Iterations = 32,
|
||||
[switch]$NoHooks,
|
||||
[switch]$SmallSeed,
|
||||
[switch]$DebugLayer,
|
||||
[switch]$Warp,
|
||||
[switch]$NoHip,
|
||||
[ValidateSet('basic','indirect','indirect-root-reset','initial-pso')][string]$Mode = 'indirect',
|
||||
[string]$OutputDirectory = 'test-results/d3d12-command-development'
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repo = (Resolve-Path "$PSScriptRoot/../..").Path
|
||||
Push-Location $repo
|
||||
$names = @('D4R_COMMAND_PROBE_NO_HOOKS','D4R_COMMAND_PROBE_SMALL_SEED','D4R_COMMAND_DEBUG_SDK','D4R_DIAG_DIR','D4R_COMMAND_PROBE_WARP','D4R_COMMAND_PROBE_NO_HIP')
|
||||
$saved = @{}
|
||||
foreach ($name in $names) { $saved[$name] = [Environment]::GetEnvironmentVariable($name,'Process') }
|
||||
try {
|
||||
if ($Iterations -lt 1 -or $Iterations -gt 1024) { throw 'Iterations must be 1..1024' }
|
||||
$cmake = "$repo/.tools/python/cmake/data/bin/cmake.exe"
|
||||
$build = "$repo/build/windows-rdna4-therock"
|
||||
$hip = "$repo/.tools/therock-10.2.0a20260929/_rocm_sdk_core"
|
||||
$output = [IO.Path]::GetFullPath((Join-Path $repo $OutputDirectory))
|
||||
New-Item -ItemType Directory -Force -Path $output | Out-Null
|
||||
$target = 'd4r_d3d12_command_probe'
|
||||
if ($DebugLayer) {
|
||||
$sdk = "$repo/.tools/agility-1.619.5"
|
||||
if (!(Test-Path "$sdk/build/native/bin/x64/d3d12SDKLayers.dll")) { throw 'Unpack Microsoft.Direct3D.D3D12 1.619.5 into .tools/agility-1.619.5 for this optional debug probe' }
|
||||
& $cmake -S . -B $build "-DD4R_AGILITY_ROOT=$sdk" > "$output/configure.log"
|
||||
if ($LASTEXITCODE) { throw 'Debug probe configuration failed' }
|
||||
$target = 'd4r_d3d12_command_debug_probe'
|
||||
}
|
||||
& $cmake --build $build --target d4r_nvngx_windows $target > "$output/build.log"
|
||||
if ($LASTEXITCODE) { throw "Build failed: $output/build.log" }
|
||||
[Environment]::SetEnvironmentVariable($names[0], $(if ($NoHooks) { '1' } else { $null }), 'Process')
|
||||
[Environment]::SetEnvironmentVariable($names[1], $(if ($SmallSeed) { '1' } else { $null }), 'Process')
|
||||
[Environment]::SetEnvironmentVariable($names[2], $(if ($DebugLayer) { '1' } else { $null }), 'Process')
|
||||
[Environment]::SetEnvironmentVariable($names[3], $output, 'Process')
|
||||
[Environment]::SetEnvironmentVariable($names[4], $(if ($Warp) { '1' } else { $null }), 'Process')
|
||||
[Environment]::SetEnvironmentVariable($names[5], $(if ($NoHip -or $Warp) { '1' } else { $null }), 'Process')
|
||||
# PowerShell 5 treats native stderr as ErrorRecord: retain it without
|
||||
# converting it into a terminating exception, then check the process code.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
& "$build/$target.exe" --hip-root $hip --module "$build/d4r_nvngx.dll" --interop-mode $Mode --iterations $Iterations > "$output/stdout.log" 2> "$output/stderr.log"
|
||||
$code = $LASTEXITCODE
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Get-Content "$output/stdout.log", "$output/stderr.log" | Select-String 'D3D12_VALIDATION|DRED|DEVICE_REMOVED|PROBE_GPU|DIFFERENT|FAIL|PASS|installed'
|
||||
Write-Host "D3D12 command diagnostic logs: $output"
|
||||
if ($code) { throw "D3D12 probe exit=$code" }
|
||||
} finally {
|
||||
foreach ($name in $names) { [Environment]::SetEnvironmentVariable($name,$saved[$name],'Process') }
|
||||
Pop-Location
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
# d4r-check.ps1 against fixture installs. No GPU, no NVIDIA binaries: the packaging test's
|
||||
# synthetic release is the install, a fake inventory file stands in for the GPU query, and
|
||||
# fixture text files stand in for the NVIDIA DLLs (pinned in a fixture dll-pins.txt).
|
||||
[CmdletBinding()]
|
||||
param([string]$OutputDirectory)
|
||||
$ErrorActionPreference='Stop'
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/d4r-check/'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
& (Join-Path $PSScriptRoot 'test-windows-release-package.ps1') -OutputDirectory (Join-Path $OutputDirectory 'package') | Out-Null
|
||||
$release=Join-Path $OutputDirectory 'package/release'
|
||||
$check=Join-Path $repo 'scripts/windows/d4r-check.ps1'
|
||||
$powershell="$env:SystemRoot/System32/WindowsPowerShell/v1.0/powershell.exe"
|
||||
$results=@()
|
||||
function Sha([string]$path) { (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash }
|
||||
function Set-ManifestHash([string]$game,[string]$relative) {
|
||||
$path=Join-Path $game 'd4r/package.json'
|
||||
$manifest=Get-Content -LiteralPath $path -Raw | ConvertFrom-Json
|
||||
foreach ($entry in $manifest.files) { if ($entry.path -eq $relative) { $entry.sha256=Sha (Join-Path $game ($relative -replace '/','\')) } }
|
||||
$manifest | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $path -Encoding UTF8
|
||||
}
|
||||
function New-Install([string]$name) {
|
||||
$game=Join-Path $OutputDirectory "cases/$name"
|
||||
New-Item -ItemType Directory -Force (Split-Path $game) | Out-Null
|
||||
Copy-Item -LiteralPath $release -Destination $game -Recurse
|
||||
Set-Content -LiteralPath (Join-Path $game 'Game.exe') 'fixture' -Encoding ASCII
|
||||
New-Item -ItemType Directory -Force (Join-Path $game 'd4r/dlss') | Out-Null
|
||||
Set-Content -LiteralPath (Join-Path $game 'd4r/dlss/nvngx_dlss.dll') 'fixture dlss' -Encoding ASCII
|
||||
Set-Content -LiteralPath (Join-Path $game 'd4r/ngx/_nvngx.dll') 'fixture core' -Encoding ASCII
|
||||
$unrelated=('B'*64)
|
||||
@('# fixture pins',
|
||||
"dlss $(Sha (Join-Path $game 'd4r/dlss/nvngx_dlss.dll')) 310.9.1 validated",
|
||||
"dlss $unrelated 310.7.0 unvalidated",
|
||||
"ngx $(Sha (Join-Path $game 'd4r/ngx/_nvngx.dll')) 32.0.16.1714 validated") |
|
||||
Set-Content -LiteralPath (Join-Path $game 'd4r/dll-pins.txt') -Encoding ASCII
|
||||
Set-ManifestHash $game 'd4r/dll-pins.txt'
|
||||
return $game
|
||||
}
|
||||
function Write-Inventory([string]$name,[string]$arch,[string]$gpu='AMD Radeon RX 7700 XT') {
|
||||
$path=Join-Path $OutputDirectory "inventory-$name.json"
|
||||
@{devices=@(@{name=$gpu; architecture=$arch; wavefrontSize=32})} | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $path -Encoding UTF8
|
||||
return $path
|
||||
}
|
||||
function Run-Check([string]$game,[string]$inventory,[string[]]$More=@()) {
|
||||
$ErrorActionPreference='Continue' # native stderr must not become a terminating error
|
||||
$text=& $powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $game 'd4r/d4r-check.ps1') -GameFolder $game -InventoryFile $inventory @More 2>&1 | Out-String
|
||||
return @{exit=$LASTEXITCODE; text=$text}
|
||||
}
|
||||
function Expect([string]$case,$run,[int]$exit,[string[]]$patterns) {
|
||||
if ($run.exit -ne $exit) { throw "$case : exit $($run.exit), expected $exit`n$($run.text)" }
|
||||
foreach ($pattern in $patterns) { if ($run.text -notmatch $pattern) { throw "$case : output lacks /$pattern/`n$($run.text)" } }
|
||||
$script:results+=@{case=$case; passed=$true; gpuKernelsExecuted=$false}
|
||||
}
|
||||
|
||||
# 1. Healthy install.
|
||||
$game=New-Install 'healthy'; $inv=Write-Inventory 'a' 'gfx1101'
|
||||
Expect 'healthy-install' (Run-Check $game $inv) 0 @('ok\s+d4r\\dlss\\nvngx_dlss\.dll \(nvngx_dlss\.dll 310\.9\.1, tested on Windows\)','ok\s+d4r\\ngx\\_nvngx\.dll',
|
||||
'ok\s+GPU AMD Radeon RX 7700 XT \(gfx1101\): build present, 16 native kernels','ok\s+all 17 GPU code objects are built for gfx1101',
|
||||
'ok\s+no vkd3d/Wine d3d12\.dll','ok\s+d4r\.ini is valid \(model K\)','compile-tested','Everything d4r needs is in place')
|
||||
|
||||
# 2. NVIDIA DLL problems, each with the fix.
|
||||
$game=New-Install 'dll-missing'; Remove-Item -LiteralPath (Join-Path $game 'd4r/dlss/nvngx_dlss.dll')
|
||||
Expect 'dll-missing' (Run-Check $game $inv) 1 @("MISSING\s+d4r\\dlss\\nvngx_dlss\.dll: copy NVIDIA's nvngx_dlss\.dll here \(version 310\.9\.1 or 310\.7\.0","1 thing\(s\) to fix")
|
||||
$game=New-Install 'dll-wrong'; Set-Content -LiteralPath (Join-Path $game 'd4r/dlss/nvngx_dlss.dll') 'some other version' -Encoding ASCII
|
||||
Expect 'dll-wrong-version' (Run-Check $game $inv) 1 @('MISSING\s+d4r\\dlss\\nvngx_dlss\.dll is version \S+, which d4r does not accept\. d4r on Windows needs exactly 310\.9\.1 or 310\.7\.0')
|
||||
$game=New-Install 'dll-less-tested'; Set-Content -LiteralPath (Join-Path $game 'd4r/dlss/nvngx_dlss.dll') 'fixture dlss 310.7' -Encoding ASCII
|
||||
@('dlss '+(Sha (Join-Path $game 'd4r/dlss/nvngx_dlss.dll'))+' 310.7.0 unvalidated','ngx '+(Sha (Join-Path $game 'd4r/ngx/_nvngx.dll'))+' 32.0.16.1714 validated') | Set-Content -LiteralPath (Join-Path $game 'd4r/dll-pins.txt') -Encoding ASCII
|
||||
Set-ManifestHash $game 'd4r/dll-pins.txt'
|
||||
Expect 'dll-less-tested-accepted' (Run-Check $game $inv) 0 @('ok\s+d4r\\dlss\\nvngx_dlss\.dll \(nvngx_dlss\.dll 310\.7\.0\)','note\s+nvngx_dlss\.dll 310\.7\.0 is accepted but has had less Windows testing')
|
||||
|
||||
# 3. Leftovers, edited files, a replaced dxgi.dll, and a broken OptiScaler.ini.
|
||||
$game=New-Install 'vkd3d'; Set-Content -LiteralPath (Join-Path $game 'd3d12.dll') 'vkd3d-proton fixture' -Encoding ASCII
|
||||
Expect 'vkd3d-proxy' (Run-Check $game $inv) 1 @('MISSING\s+d3d12\.dll in the game folder is vkd3d-proton/Wine')
|
||||
$game=New-Install 'dxgi-replaced'; Set-Content -LiteralPath (Join-Path $game 'dxgi.dll') 'another dxgi' -Encoding ASCII
|
||||
Expect 'foreign-dxgi' (Run-Check $game $inv) 0 @('note\s+dxgi\.dll is not the one that came with d4r')
|
||||
$game=New-Install 'opti-path'; (Get-Content -LiteralPath (Join-Path $game 'OptiScaler.ini')) -replace '^NvngxPath=.*$','NvngxPath=nowhere' | Set-Content -LiteralPath (Join-Path $game 'OptiScaler.ini') -Encoding ASCII
|
||||
Expect 'optiscaler-ini-path' (Run-Check $game $inv) 1 @('MISSING\s+OptiScaler\.ini NvngxPath = nowhere does not hold _nvngx\.dll')
|
||||
|
||||
# 4. d4r.ini mistakes are notes with what will happen instead.
|
||||
$game=New-Install 'ini'; Set-Content -LiteralPath (Join-Path $game 'd4r/d4r.ini') "[DLSS]`nModel = E`nShowWatermark = sometimes`n[Latency]`nFrameAge = 1`n[Debug]`nLog = loud`n" -Encoding ASCII
|
||||
Expect 'ini-mistakes' (Run-Check $game $inv) 0 @("note\s+d4r\.ini: \[DLSS\] Model 'E' is not available on Windows",'must be true or false, not ''sometimes''','\[Latency\] FrameAge is not a Windows setting','Log must be normal or verbose')
|
||||
|
||||
# 5. GPU cases.
|
||||
$game=New-Install 'gpu-no-build'; Remove-Item -LiteralPath (Join-Path $game 'd4r/_nvngx_gfx1103.dll')
|
||||
Expect 'gpu-without-build' (Run-Check $game (Write-Inventory 'b' 'gfx1103' 'AMD Radeon 780M')) 1 @('MISSING\s+GPU AMD Radeon 780M \(gfx1103\): this release has no complete build for it \(missing: d4r\\_nvngx_gfx1103\.dll\)')
|
||||
$game=New-Install 'gpu-rdna2'
|
||||
Expect 'gpu-unsupported' (Run-Check $game (Write-Inventory 'c' 'gfx1030' 'AMD Radeon RX 6800')) 1 @('MISSING\s+no supported RDNA3/RDNA4 GPU found by the AMD runtime\. It sees: AMD Radeon RX 6800 \(gfx1030\)')
|
||||
$game=New-Install 'gpu-wrong-object'
|
||||
$other=@(Get-ChildItem -LiteralPath (Join-Path $game 'd4r/native/gfx1100') -Filter '*.hsaco')[0].FullName
|
||||
Copy-Item -LiteralPath $other -Destination (Join-Path $game 'd4r/native/gfx1101/kernel3.hsaco') -Force
|
||||
Expect 'code-object-for-another-gpu' (Run-Check $game $inv) 1 @('MISSING\s+kernel3\.hsaco is not built for gfx1101','differs from the release')
|
||||
|
||||
# 6. What the last game start said, and the bug-report ZIP.
|
||||
$game=New-Install 'log-failure'
|
||||
Set-Content -LiteralPath (Join-Path $game 'd4r/d4r_nvngx.log') "d4r: DLSS disabled: Wrong nvngx_dlss.dll: you have version 1.2.3.4" -Encoding ASCII
|
||||
Expect 'log-reports-failure' (Run-Check $game $inv) 1 @('MISSING\s+last game start: DLSS disabled: Wrong nvngx_dlss\.dll')
|
||||
$game=New-Install 'log-ok'
|
||||
Set-Content -LiteralPath (Join-Path $game 'd4r/d4r_nvngx.log') "d4r: player mode, model=K validate=0 async=0 log=normal, architecture=gfx1101" -Encoding ASCII
|
||||
Expect 'log-reports-ok' (Run-Check $game $inv) 0 @('ok\s+last game start: player mode, model=K')
|
||||
$game=New-Install 'report'; $reportDir=Join-Path $OutputDirectory 'reports'; New-Item -ItemType Directory -Force $reportDir | Out-Null
|
||||
Set-Content -LiteralPath (Join-Path $game 'd4r/d4r_nvngx.log') "d4r: DLSS disabled: test failure" -Encoding ASCII
|
||||
$run=Run-Check $game $inv @('-Report','-ReportDirectory',$reportDir)
|
||||
$zip=@(Get-ChildItem -LiteralPath $reportDir -Filter 'd4r-report-*.zip')
|
||||
if ($zip.Count -ne 1) { throw "Expected one report ZIP.`n$($run.text)" }
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
$archive=[IO.Compression.ZipFile]::OpenRead($zip[0].FullName)
|
||||
try { $names=@($archive.Entries | ForEach-Object { $_.Name }) } finally { $archive.Dispose() }
|
||||
foreach ($required in @('check.txt','d4r_nvngx.log','d4r.ini','OptiScaler.ini','files.sha256.txt','gpu-inventory.json')) { if ($names -notcontains $required) { throw "Report lacks $required (has: $($names -join ', '))" } }
|
||||
if ($names -contains 'nvngx_dlss.dll' -or $names -contains '_nvngx.dll') { throw 'The report must never contain NVIDIA DLLs.' }
|
||||
if ($run.text -notmatch 'Bug report written') { throw 'Report location was not printed.' }
|
||||
$results+=@{case='bug-report-zip'; passed=$true; entries=$names}
|
||||
|
||||
# 7. No d4r folder at all.
|
||||
$empty=Join-Path $OutputDirectory 'cases/empty'; New-Item -ItemType Directory -Force $empty | Out-Null
|
||||
$ErrorActionPreference='Continue'
|
||||
$text=& $powershell -NoProfile -ExecutionPolicy Bypass -File $check -GameFolder $empty 2>&1 | Out-String
|
||||
$exitEmpty=$LASTEXITCODE; $ErrorActionPreference='Stop'
|
||||
if ($exitEmpty -ne 1 -or $text -notmatch 'no d4r folder here') { throw "A folder without d4r must be reported.`n$text" }
|
||||
$results+=@{case='no-d4r-folder'; passed=$true}
|
||||
|
||||
$results | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Write-Host "PASS D4R_CHECK cases=$($results.Count) Report: $OutputDirectory"
|
||||
@@ -0,0 +1,103 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$ZludaRoot, [string]$PackageRoot, [string]$HipRoot, [string]$OutputDirectory)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$ZludaRoot) { $ZludaRoot = Join-Path $repo 'dist/zluda-windows-deferred-profile' }
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-command-list' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/deferred-profile-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$settings = @{
|
||||
D4R_ZLUDA_PROFILE=$null; D4R_ZLUDA_PROFILE_DEFERRED='1'; D4R_ZLUDA_PROFILE_EVERY='1';
|
||||
D4R_ZLUDA_PROFILE_ALLOW_LEGACY=$null;
|
||||
D4R_DIAG_NONBLOCKING_STREAM='1'; D4R_DIAG_QUEUE_PTX=$null;
|
||||
D4R_DIAG_DIR=$OutputDirectory;
|
||||
ZLUDA_CACHE_DIR=(Join-Path $repo 'build/zluda-cache-windows');
|
||||
PATH=((Join-Path $HipRoot 'bin') + ';' + $ZludaRoot + ';' + $env:PATH)
|
||||
}
|
||||
$old=@{}; $cases=@()
|
||||
function RunProbe([string[]]$Arguments, [string]$Stdout, [string]$Stderr) {
|
||||
$info=[Diagnostics.ProcessStartInfo]::new()
|
||||
$info.FileName=Join-Path $PackageRoot 'bin/d4r_cuda_driver_probe.exe'
|
||||
$info.Arguments=$Arguments -join ' '
|
||||
$info.UseShellExecute=$false; $info.CreateNoWindow=$true
|
||||
$info.RedirectStandardOutput=$true; $info.RedirectStandardError=$true
|
||||
$p=[Diagnostics.Process]::new(); $p.StartInfo=$info
|
||||
$outFile=$null; $errFile=$null; $outTask=$null; $errTask=$null
|
||||
try {
|
||||
if (!$p.Start()) { throw 'Could not launch the CUDA diagnostic.' }
|
||||
$outFile=[IO.File]::Open($Stdout,[IO.FileMode]::Create,[IO.FileAccess]::Write,[IO.FileShare]::Read)
|
||||
$errFile=[IO.File]::Open($Stderr,[IO.FileMode]::Create,[IO.FileAccess]::Write,[IO.FileShare]::Read)
|
||||
$outTask=$p.StandardOutput.BaseStream.CopyToAsync($outFile)
|
||||
$errTask=$p.StandardError.BaseStream.CopyToAsync($errFile)
|
||||
$timer=[Diagnostics.Stopwatch]::StartNew()
|
||||
while (!$p.WaitForExit(1000)) {
|
||||
if ($timer.Elapsed.TotalSeconds -ge 120) { throw "CUDA diagnostic timed out; logs: $OutputDirectory" }
|
||||
}
|
||||
$p.WaitForExit()
|
||||
[void]$outTask.GetAwaiter().GetResult(); [void]$errTask.GetAwaiter().GetResult()
|
||||
return $p.ExitCode
|
||||
} finally {
|
||||
if (!$p.HasExited) { $p.Kill(); $p.WaitForExit() }
|
||||
if ($outTask) { try { [void]$outTask.GetAwaiter().GetResult() } catch {} }
|
||||
if ($errTask) { try { [void]$errTask.GetAwaiter().GetResult() } catch {} }
|
||||
if ($outFile) { $outFile.Dispose() }; if ($errFile) { $errFile.Dispose() }
|
||||
$p.Dispose()
|
||||
}
|
||||
}
|
||||
try {
|
||||
foreach ($key in $settings.Keys) {
|
||||
$old[$key]=[Environment]::GetEnvironmentVariable($key,'Process')
|
||||
[Environment]::SetEnvironmentVariable($key,$settings[$key],'Process')
|
||||
}
|
||||
foreach ($context in @('primary','created')) {
|
||||
foreach ($queued in @($false,$true)) {
|
||||
$name="$context-$(if ($queued) { 'queued' } else { 'single' })"
|
||||
[Environment]::SetEnvironmentVariable('D4R_DIAG_QUEUE_PTX', $(if ($queued) { '1' } else { $null }), 'Process')
|
||||
$iterations=129
|
||||
$stdout=Join-Path $OutputDirectory "$name.stdout.log"
|
||||
$stderr=Join-Path $OutputDirectory "$name.stderr.log"
|
||||
# Native Windows argument parsing: quote absolute paths explicitly.
|
||||
$arguments=@('--hip-root', ('"' + $HipRoot + '"'), '--cuda-dll', ('"' + (Join-Path $ZludaRoot 'nvcuda.dll') + '"'),
|
||||
'--context', $context, '--iterations', "$iterations")
|
||||
$exitCode=RunProbe $arguments $stdout $stderr
|
||||
if ($exitCode) { throw "$name failed ($exitCode); logs: $OutputDirectory" }
|
||||
$out=Get-Content -LiteralPath $stdout -Raw
|
||||
$err=Get-Content -LiteralPath $stderr -Raw
|
||||
if ($out -notmatch "PASS CUDA architecture=$GpuArch" -or $out -notmatch 'guard_verified=1') { throw "$name did not verify PTX output" }
|
||||
$records=[regex]::Matches($err, '(?m)^D4R_KERNEL_PROFILE kernel="d4r_ptx_pattern" [^\r\n]*serializing=0 deferred=1 [^\r\n]+')
|
||||
$invalid=[regex]::Matches($err, '(?m)^D4R_KERNEL_PROFILE_INVALID kernel="d4r_ptx_pattern" [^\r\n]*reason=invalid_elapsed_time serializing=0 deferred=1 [^\r\n]+')
|
||||
if ($records.Count + $invalid.Count -ne $iterations -or $err -match 'serializing=1|D4R_KERNEL_PROFILE_SKIPPED') {
|
||||
throw "$name expected $iterations collected deferred records, found $($records.Count) valid / $($invalid.Count) invalid"
|
||||
}
|
||||
foreach ($record in $records) {
|
||||
if ($record.Value -notmatch 'gpu_ms=([0-9.]+)') {
|
||||
throw "$name contains an invalid GPU time"
|
||||
}
|
||||
$gpuMs=[double]::Parse($Matches[1], [Globalization.CultureInfo]::InvariantCulture)
|
||||
if ([double]::IsNaN($gpuMs) -or [double]::IsInfinity($gpuMs)) { throw "$name contains an invalid GPU time" }
|
||||
}
|
||||
$cases += @{context=$context; queued=$queued; launches=$iterations; collected=($records.Count+$invalid.Count);
|
||||
validTimings=$records.Count; invalidTimings=$invalid.Count; exitCode=$exitCode}
|
||||
Write-Host "PASS ${name}: $($records.Count) valid / $($invalid.Count) invalid deferred timings; exact PTX output and guards"
|
||||
}
|
||||
}
|
||||
# Legacy default-stream timing markers would add stream dependencies.
|
||||
[Environment]::SetEnvironmentVariable('D4R_DIAG_NONBLOCKING_STREAM',$null,'Process')
|
||||
[Environment]::SetEnvironmentVariable('D4R_DIAG_QUEUE_PTX',$null,'Process')
|
||||
$stdout=Join-Path $OutputDirectory 'default-stream.stdout.log'; $stderr=Join-Path $OutputDirectory 'default-stream.stderr.log'
|
||||
$arguments=@('--hip-root', ('"' + $HipRoot + '"'), '--cuda-dll', ('"' + (Join-Path $ZludaRoot 'nvcuda.dll') + '"'), '--iterations', '3')
|
||||
$exitCode=RunProbe $arguments $stdout $stderr
|
||||
$out=Get-Content -LiteralPath $stdout -Raw; $err=Get-Content -LiteralPath $stderr -Raw
|
||||
if ($exitCode -or $out -notmatch "PASS CUDA architecture=$GpuArch" -or
|
||||
$err -notmatch 'reason=legacy_default_stream' -or $err -match '(?m)^D4R_KERNEL_PROFILE ') {
|
||||
throw 'Default-stream diagnostics did not safely skip timing events.'
|
||||
}
|
||||
@{passed=$true; architecture=$GpuArch; cases=$cases; defaultStreamSkipped=$true; addedCompletionWaits=0} |
|
||||
ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'validation.json') -Encoding UTF8
|
||||
Write-Host "PASS deferred kernel profiling: $OutputDirectory"
|
||||
} finally {
|
||||
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$LogRoot, [string]$OutputDirectory)
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'game-summary.ps1')
|
||||
$cases=@(
|
||||
@{name='empty-streams-debugger-refused'; preset=11; stdout=''; stderr=''; debugger='EXIT code=0xdeadc0de'; session='process-failed-before-dlss-frames'},
|
||||
@{name='launcher-exit'; preset=11; stdout=''; stderr=''; debugger='EXIT code=0x0'; session='process-exited-without-dlss-frames'},
|
||||
@{name='feature-not-found'; preset=11; stdout='D4R_CAPABILITIES available=0 feature_init=0xbad00004 needs_driver=0'; stderr='D4R_WINDOWS_FAILURE unavailable'; debugger='EXIT code=0xc0000409'; session='ngx-feature-not-found'},
|
||||
@{name='complete-frame-k'; preset=11; native=1; kernel='dltss_pwin_enc0_layer'; stdout="D4R_FRAME cpu_frame_copies=0 frame_age=0 interop_ngx_ms=12.4`nD4R_OUTPUT_VALIDATION elements=3 nan=0 inf=0 diagnostics_cpu_bytes=8";
|
||||
stderr='[d4r-launch] kernel="dltss_pwin_enc0_layer" backend=native'; debugger='EXIT code=0x0'; session='completed-dlss-frames'},
|
||||
@{name='complete-frame-e'; preset=5; native=0; kernel=$null; stdout="D4R_FRAME cpu_frame_copies=0 frame_age=0 interop_ngx_ms=14.2`nD4R_OUTPUT_VALIDATION elements=3 nan=0 inf=0 diagnostics_cpu_bytes=8";
|
||||
stderr=''; debugger='EXIT code=0x0'; session='completed-dlss-frames'},
|
||||
@{name='complete-frame-l'; preset=12; native=1; kernel='rrlite_enc1_4x4'; stdout="D4R_FRAME cpu_frame_copies=0 frame_age=0 interop_ngx_ms=18.4`nD4R_OUTPUT_VALIDATION elements=3 nan=0 inf=0 diagnostics_cpu_bytes=8";
|
||||
stderr='[d4r-launch] kernel="rrlite_enc1_4x4" backend=native'; debugger='EXIT code=0x0'; session='completed-dlss-frames'},
|
||||
@{name='truncated-frame'; preset=13; stdout='D4R_FRAME cpu_frame_copies=0 frame_age=0 interop_ngx_ms='; stderr=''; debugger=''; session='diagnostic-timeout-without-dlss-frames'}
|
||||
)
|
||||
$results=@()
|
||||
foreach ($case in $cases) {
|
||||
$summary=Get-D4RGameSummary -Stdout $case.stdout -Stderr $case.stderr -Debugger $case.debugger -Preset $case.preset
|
||||
if ($summary.session -ne $case.session) { throw "Wrong session classification: $($case.name)" }
|
||||
if ($case.name -like 'complete-frame-*') {
|
||||
$kernelCount=if ($case.kernel) { @($summary.kernels | Where-Object kernel -eq $case.kernel).Count } else { 0 }
|
||||
if ($summary.completedFrames -ne 1 -or $summary.outputGpuChecks -ne 1 -or $summary.nativeLaunches -ne $case.native -or
|
||||
$kernelCount -ne $case.native -or $summary.preset -ne $case.preset -or
|
||||
$summary.nonfiniteOutputs -or $summary.previousFrameOutputs -or $summary.cpuImageCopyFrames) { throw 'Complete frame counters are wrong.' }
|
||||
} elseif ($summary.completedFrames -ne 0) { throw 'A failed or incomplete run was counted as a frame.' }
|
||||
$results+=@{case=$case.name; passed=$true; summary=$summary}
|
||||
}
|
||||
if ($LogRoot) {
|
||||
foreach ($entry in @{ 'acbf-k'='process-failed-before-dlss-frames'; 'cyberpunk-k'='ngx-feature-not-found';
|
||||
'cyberpunk-m'='ngx-feature-not-found'; 'sm2-k'='process-exited-without-dlss-frames' }.GetEnumerator()) {
|
||||
$dir=Get-ChildItem -LiteralPath (Join-Path $LogRoot $entry.Key) -Directory | Select-Object -First 1
|
||||
$summary=Get-D4RGameSummary -Stdout ([IO.File]::ReadAllText((Join-Path $dir.FullName 'd4r.stdout.log'))) `
|
||||
-Stderr ([IO.File]::ReadAllText((Join-Path $dir.FullName 'd4r.stderr.log'))) `
|
||||
-Debugger ([IO.File]::ReadAllText((Join-Path $dir.FullName 'debugger.log')))
|
||||
if ($summary.session -ne $entry.Value -or $summary.completedFrames -ne 0 -or $summary.nativeLaunches -ne 0) { throw "Unexpected classification for actual report $($entry.Key)" }
|
||||
$results+=@{case='issue-10-'+$entry.Key; passed=$true; summary=$summary}
|
||||
}
|
||||
}
|
||||
if ($OutputDirectory) {
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$results | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary-parser.json') -Encoding UTF8
|
||||
}
|
||||
Write-Host "PASS GAME_SUMMARY cases=$($results.Count)"
|
||||
@@ -0,0 +1,23 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$PackageRoot, [Parameter(Mandatory=$true)][string]$CodeObject)
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$target=Get-D4RGpuTarget $PackageRoot $null
|
||||
$other=if ($target -eq 'gfx1201') { 'gfx1200' } else { 'gfx1201' }
|
||||
function Expect-Rejection([scriptblock]$Action) {
|
||||
$rejected=$false
|
||||
try { & $Action | Out-Null } catch { $rejected=$true }
|
||||
if (!$rejected) { throw 'GPU target contract accepted incompatible input.' }
|
||||
}
|
||||
if ((Get-D4RGpuTarget $PackageRoot $target) -ne $target) { throw 'Incorrect package target.' }
|
||||
Assert-D4RCodeObjectTarget $CodeObject $target
|
||||
Expect-Rejection { Get-D4RGpuTarget $PackageRoot $other }
|
||||
Expect-Rejection { Get-D4RGpuTarget $PackageRoot 'gfx1250' }
|
||||
Expect-Rejection { Assert-D4RCodeObjectTarget $CodeObject $other }
|
||||
Expect-Rejection { Assert-D4RCodeObjectTarget (Join-Path $PackageRoot 'gpu-target.json') $target }
|
||||
# Old packages had no target metadata and were gfx1201 only.
|
||||
$legacy=Join-Path $PackageRoot '__no-target-metadata__'
|
||||
if (Test-Path -LiteralPath (Join-Path $legacy 'gpu-target.json')) { throw 'Unexpected legacy test fixture.' }
|
||||
if ((Get-D4RGpuTarget $legacy $null) -ne 'gfx1201') { throw 'Legacy target changed.' }
|
||||
Expect-Rejection { Get-D4RGpuTarget $legacy 'gfx1200' }
|
||||
Write-Host "PASS GPU_TARGET_SCRIPT target=$target rejected=5 hardware_executed=0"
|
||||
@@ -0,0 +1,54 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$CaptureDirectory, [string]$HipRoot,
|
||||
[string]$PackageRoot, [string]$OutputDirectory, [int]$Windows = 12,
|
||||
[Parameter(Mandatory=$true)][string]$DlssDll, [int]$PtxWindows = 2)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/k-real-reference-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) }
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$previousPython = $env:PYTHONPATH
|
||||
$previousDiag = $env:D4R_DIAG_DIR
|
||||
$env:PYTHONPATH = "$(Join-Path $repo '.tools/python/vendor');$previousPython"
|
||||
$summary = [ordered]@{utc=[DateTime]::UtcNow.ToString('o'); captureDirectory=$CaptureDirectory; layers=@(); passed=$false}
|
||||
try {
|
||||
foreach ($layer in @('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0')) {
|
||||
$name = "dltss_pwin_${layer}_layer"
|
||||
$captures = @(Get-ChildItem -LiteralPath $CaptureDirectory -Directory | Where-Object Name -Match ("^replay-[0-9]+-" + $name + '$'))
|
||||
if (!$captures.Count) { throw "Missing actual K capture: $name" }
|
||||
$capture = $captures | Sort-Object Name | Select-Object -First 1
|
||||
$folder = Join-Path $OutputDirectory $layer
|
||||
$output = Join-Path $folder 'output'
|
||||
New-Item -ItemType Directory -Force $folder | Out-Null
|
||||
$env:D4R_DIAG_DIR = $folder
|
||||
& (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --hip-root $HipRoot `
|
||||
--module (Join-Path $PackageRoot "experimental/k/${name}_${GpuArch}.hsaco") `
|
||||
--fixture-dir $capture.FullName --output-dir $output --iterations 1 `
|
||||
> (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "K $layer actual GPU replay failed: $folder" }
|
||||
& python (Join-Path $PackageRoot 'k_replay_validate.py') validate --layer $layer `
|
||||
--fixture-dir $capture.FullName --output-dir $output --windows $Windows --real-capture `
|
||||
> (Join-Path $folder 'reference.stdout.log') 2> (Join-Path $folder 'reference.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "K $layer mismatch; stopping before the next layer: $folder" }
|
||||
Get-Content -LiteralPath (Join-Path $folder 'reference.stdout.log') | Write-Host
|
||||
$ptxValidator = Join-Path $PackageRoot 'k_ptx_validate.py'
|
||||
if (!(Test-Path -LiteralPath $ptxValidator)) { $ptxValidator = Join-Path $repo 'tools/windows/k_ptx_validate.py' }
|
||||
& python -u $ptxValidator --layer $layer `
|
||||
--capture-dir $capture.FullName --native-dir $output --dlss-dll $DlssDll `
|
||||
--output-dir (Join-Path $folder 'private-ptx') --windows $PtxWindows `
|
||||
> (Join-Path $folder 'ptx-reference.stdout.log') 2> (Join-Path $folder 'ptx-reference.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "K $layer independent PTX mismatch; stopping: $folder" }
|
||||
Get-Content -LiteralPath (Join-Path $folder 'ptx-reference.stdout.log') | Write-Host
|
||||
$summary.layers += @{layer=$layer; passed=$true}
|
||||
}
|
||||
$summary.passed = $true
|
||||
} catch { $summary.error=$_.Exception.Message; Write-Warning $summary.error }
|
||||
finally {
|
||||
$env:PYTHONPATH = $previousPython; $env:D4R_DIAG_DIR = $previousDiag
|
||||
$summary | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Write-Host "Private real-weight replay results: $OutputDirectory (do not redistribute captures/allocations)"
|
||||
}
|
||||
if (!$summary.passed) { exit 1 }
|
||||
@@ -0,0 +1,55 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$HipRoot,
|
||||
[string]$PackageRoot,
|
||||
[string]$OutputDirectory,
|
||||
[string[]]$Layers = @('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0'),
|
||||
[int]$Iterations = 32
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/k-replay-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) }
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$previousPython = $env:PYTHONPATH
|
||||
$previousDiag = $env:D4R_DIAG_DIR
|
||||
$env:PYTHONPATH = "$(Join-Path $repo '.tools/python/vendor');$previousPython"
|
||||
$summary = [ordered]@{utc=[DateTime]::UtcNow.ToString('o'); hipRoot=$HipRoot; architecture=$GpuArch; layers=@(); passed=$false}
|
||||
try {
|
||||
if ($Iterations -lt 1 -or $Iterations -gt 10000) { throw 'Iterations must be 1..10000' }
|
||||
foreach ($layer in $Layers) {
|
||||
if ($layer -notmatch '^(enc[0-4]|dec[0-5])$') { throw "Invalid K layer $layer" }
|
||||
$folder = Join-Path $OutputDirectory $layer
|
||||
$fixture = Join-Path $folder 'fixture'
|
||||
$output = Join-Path $folder 'output'
|
||||
New-Item -ItemType Directory -Force $folder | Out-Null
|
||||
$env:D4R_DIAG_DIR = $folder
|
||||
& python (Join-Path $PackageRoot 'k_replay_validate.py') generate --layer $layer --fixture-dir $fixture `
|
||||
> (Join-Path $folder 'fixture.stdout.log') 2> (Join-Path $folder 'fixture.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "K $layer fixture generation failed; see $folder" }
|
||||
& (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --hip-root $HipRoot `
|
||||
--module (Join-Path $PackageRoot "experimental/k/dltss_pwin_${layer}_layer_${GpuArch}.hsaco") `
|
||||
--fixture-dir $fixture --output-dir $output --iterations $Iterations `
|
||||
> (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "K $layer GPU replay failed; see $folder" }
|
||||
& python (Join-Path $PackageRoot 'k_replay_validate.py') validate --layer $layer --fixture-dir $fixture --output-dir $output `
|
||||
> (Join-Path $folder 'reference.stdout.log') 2> (Join-Path $folder 'reference.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "K $layer reference mismatch; stopping before the next layer. See $folder" }
|
||||
Get-Content -LiteralPath (Join-Path $folder 'reference.stdout.log') | Write-Host
|
||||
$summary.layers += @{layer=$layer; passed=$true; iterations=$Iterations}
|
||||
}
|
||||
$summary.passed = $true
|
||||
} catch {
|
||||
$summary.error = $_.Exception.Message
|
||||
Write-Warning $summary.error
|
||||
} finally {
|
||||
$env:PYTHONPATH = $previousPython
|
||||
$env:D4R_DIAG_DIR = $previousDiag
|
||||
$summary | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Compress-Archive -LiteralPath $OutputDirectory -DestinationPath "$OutputDirectory.zip" -Force
|
||||
Write-Host "K replay bundle: $OutputDirectory.zip"
|
||||
}
|
||||
if (!$summary.passed) { exit 1 }
|
||||
@@ -0,0 +1,51 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$CaptureDirectory, [string]$HipRoot,
|
||||
[string]$PackageRoot, [string]$OutputDirectory, [int]$Blocks = 12,
|
||||
[Parameter(Mandatory=$true)][string]$DlssDll, [int]$PtxBlocks = 2)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/m-real-reference-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) }
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$previousPython=$env:PYTHONPATH; $previousDiag=$env:D4R_DIAG_DIR
|
||||
$env:PYTHONPATH = "$(Join-Path $repo '.tools/python/vendor');$previousPython"
|
||||
$summary = [ordered]@{utc=[DateTime]::UtcNow.ToString('o'); captureDirectory=$CaptureDirectory; layers=@(); passed=$false}
|
||||
try {
|
||||
foreach ($layer in @('enc1','enc2','enc3_tube','dec2','dec1')) {
|
||||
$name = "rrlite_${layer}_4x4"
|
||||
$captures = @(Get-ChildItem -LiteralPath $CaptureDirectory -Directory | Where-Object Name -Match ("^replay-[0-9]+-" + $name + '$'))
|
||||
if (!$captures.Count) { throw "Missing actual M capture: $name" }
|
||||
$capture = $captures | Sort-Object Name | Select-Object -First 1
|
||||
$folder = Join-Path $OutputDirectory $layer
|
||||
$output = Join-Path $folder 'output'
|
||||
New-Item -ItemType Directory -Force $folder | Out-Null
|
||||
$env:D4R_DIAG_DIR = $folder
|
||||
& (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --hip-root $HipRoot `
|
||||
--module (Join-Path $PackageRoot "experimental/m/${name}_${GpuArch}.hsaco") `
|
||||
--fixture-dir $capture.FullName --output-dir $output --iterations 1 `
|
||||
> (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "M $layer actual GPU replay failed: $folder" }
|
||||
& python (Join-Path $PackageRoot 'm_replay_validate.py') validate --layer $layer `
|
||||
--fixture-dir $capture.FullName --output-dir $output --blocks $Blocks --real-capture `
|
||||
> (Join-Path $folder 'reference.stdout.log') 2> (Join-Path $folder 'reference.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "M $layer mismatch; stopping before the next layer: $folder" }
|
||||
Get-Content -LiteralPath (Join-Path $folder 'reference.stdout.log') | Write-Host
|
||||
& python -u (Join-Path $PackageRoot 'm_ptx_validate.py') --layer $layer `
|
||||
--capture-dir $capture.FullName --native-dir $output --dlss-dll $DlssDll `
|
||||
--output-dir (Join-Path $folder 'private-ptx') --blocks $PtxBlocks `
|
||||
> (Join-Path $folder 'ptx-reference.stdout.log') 2> (Join-Path $folder 'ptx-reference.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "M $layer independent PTX mismatch; stopping: $folder" }
|
||||
Get-Content -LiteralPath (Join-Path $folder 'ptx-reference.stdout.log') | Write-Host
|
||||
$summary.layers += @{layer=$layer; passed=$true}
|
||||
}
|
||||
$summary.passed=$true
|
||||
} catch { $summary.error=$_.Exception.Message; Write-Warning $summary.error }
|
||||
finally {
|
||||
$env:PYTHONPATH=$previousPython; $env:D4R_DIAG_DIR=$previousDiag
|
||||
$summary | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Write-Host "Private real-weight replay results: $OutputDirectory (do not redistribute captures/allocations)"
|
||||
}
|
||||
if (!$summary.passed) { exit 1 }
|
||||
@@ -0,0 +1,47 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$HipRoot, [string]$PackageRoot, [string]$OutputDirectory,
|
||||
[string[]]$Layers = @('enc1','enc2','enc3_tube','dec2','dec1'),
|
||||
[ValidateRange(1,10000)][int]$Iterations = 32)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/m-replay-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) }
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$previousPython = $env:PYTHONPATH
|
||||
$previousDiag = $env:D4R_DIAG_DIR
|
||||
$env:PYTHONPATH = "$(Join-Path $repo '.tools/python/vendor');$previousPython"
|
||||
$summary = [ordered]@{utc=[DateTime]::UtcNow.ToString('o'); hipRoot=$HipRoot; architecture=$GpuArch; layers=@(); passed=$false}
|
||||
try {
|
||||
foreach ($layer in $Layers) {
|
||||
if ($layer -notmatch '^(enc[12]|enc3_tube|dec[12])$') { throw "Invalid M layer $layer" }
|
||||
$folder = Join-Path $OutputDirectory $layer
|
||||
$fixture = Join-Path $folder 'fixture'
|
||||
$output = Join-Path $folder 'output'
|
||||
New-Item -ItemType Directory -Force $folder | Out-Null
|
||||
$env:D4R_DIAG_DIR = $folder
|
||||
& python (Join-Path $PackageRoot 'm_replay_validate.py') generate --layer $layer --fixture-dir $fixture `
|
||||
> (Join-Path $folder 'fixture.stdout.log') 2> (Join-Path $folder 'fixture.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "M $layer fixture generation failed: $folder" }
|
||||
& (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --hip-root $HipRoot `
|
||||
--module (Join-Path $PackageRoot "experimental/m/rrlite_${layer}_4x4_${GpuArch}.hsaco") `
|
||||
--fixture-dir $fixture --output-dir $output --iterations $Iterations `
|
||||
> (Join-Path $folder 'gpu.stdout.log') 2> (Join-Path $folder 'gpu.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "M $layer GPU replay failed: $folder" }
|
||||
& python (Join-Path $PackageRoot 'm_replay_validate.py') validate --layer $layer --fixture-dir $fixture --output-dir $output `
|
||||
> (Join-Path $folder 'reference.stdout.log') 2> (Join-Path $folder 'reference.stderr.log')
|
||||
if ($LASTEXITCODE) { throw "M $layer reference mismatch; stopping before the next layer: $folder" }
|
||||
Get-Content -LiteralPath (Join-Path $folder 'reference.stdout.log') | Write-Host
|
||||
$summary.layers += @{layer=$layer; passed=$true; iterations=$Iterations}
|
||||
}
|
||||
$summary.passed = $true
|
||||
} catch { $summary.error=$_.Exception.Message; Write-Warning $summary.error }
|
||||
finally {
|
||||
$env:PYTHONPATH=$previousPython; $env:D4R_DIAG_DIR=$previousDiag
|
||||
$summary | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Compress-Archive -LiteralPath $OutputDirectory -DestinationPath "$OutputDirectory.zip" -Force
|
||||
Write-Host "M replay bundle: $OutputDirectory.zip"
|
||||
}
|
||||
if (!$summary.passed) { exit 1 }
|
||||
@@ -0,0 +1,28 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$CaptureDirectory,
|
||||
[Parameter(Mandatory=$true)][string]$BaselineValidation,
|
||||
[string]$ModuleDirectory, [string]$HipRoot, [string]$OutputDirectory, [switch]$Benchmark)
|
||||
$ErrorActionPreference='Stop'
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$ModuleDirectory) { $ModuleDirectory=Join-Path $repo 'build/native-m-fp8-experimental' }
|
||||
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo 'test-results/m-native-fp8-replay' }
|
||||
$previousPython=$env:PYTHONPATH
|
||||
try {
|
||||
$env:PYTHONPATH="$(Join-Path $repo '.tools/python/vendor');$previousPython"
|
||||
$metadata=Get-Content -LiteralPath (Join-Path $ModuleDirectory 'build-info.json') -Raw | ConvertFrom-Json
|
||||
if ($metadata.hardwarePacking) {
|
||||
& python -u (Join-Path $repo 'tools/windows/fp8_pack_validate.py') `
|
||||
--probe (Join-Path $repo 'dist/windows-rdna4-command-list/bin/d4r_native_replay_probe.exe') `
|
||||
--module (Join-Path $ModuleDirectory 'packing-probe/fp8_pack_probe.hsaco') `
|
||||
--hip-root $HipRoot --output-dir (Join-Path $OutputDirectory 'packing-check')
|
||||
if ($LASTEXITCODE) { throw 'Hardware packing does not preserve every finite FP8 encoding; stopping before network replay.' }
|
||||
}
|
||||
$benchmarkArguments=@()
|
||||
if ($Benchmark) { $benchmarkArguments=@('--benchmark-control-dir',(Join-Path $repo 'build/native-m-gfx1201')) }
|
||||
& python -u (Join-Path $repo 'tools/windows/m_native_replay_compare.py') `
|
||||
--capture-dir $CaptureDirectory --module-dir $ModuleDirectory --hip-root $HipRoot `
|
||||
--probe (Join-Path $repo 'dist/windows-rdna4-command-list/bin/d4r_native_replay_probe.exe') `
|
||||
--baseline-validation $BaselineValidation --output-dir $OutputDirectory @benchmarkArguments
|
||||
if ($LASTEXITCODE) { throw "Experimental FP8 validation failed; baseline unchanged. Logs: $OutputDirectory" }
|
||||
} finally { $env:PYTHONPATH=$previousPython }
|
||||
@@ -0,0 +1,52 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$HipRoot, [string]$PackageRoot, [string]$ZludaRoot, [string]$OutputDirectory)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$HipRoot) { $HipRoot = Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$PackageRoot) { $PackageRoot = Join-Path $repo 'dist/windows-rdna4-therock' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$ZludaRoot) { $ZludaRoot = Join-Path $repo 'dist/zluda-windows-final' }
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $repo ('test-results/native-identity-' + (Get-Date -Format 'yyyyMMdd-HHmmss')) }
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$previous = @{D4R_ZLUDA_NATIVE_DIR=$env:D4R_ZLUDA_NATIVE_DIR; D4R_ZLUDA_VERBOSE=$env:D4R_ZLUDA_VERBOSE; D4R_DIAG_DIR=$env:D4R_DIAG_DIR}
|
||||
function Invoke-NativeProbe([string]$Name) {
|
||||
$info = New-Object Diagnostics.ProcessStartInfo
|
||||
$info.FileName = Join-Path $PackageRoot 'bin/d4r_cuda_driver_probe.exe'
|
||||
$info.Arguments = '--hip-root "' + $HipRoot + '" --cuda-dll "' + (Join-Path $ZludaRoot 'nvcuda.dll') + '" --iterations 32'
|
||||
$info.UseShellExecute=$false; $info.CreateNoWindow=$true; $info.RedirectStandardOutput=$true; $info.RedirectStandardError=$true
|
||||
$process = New-Object Diagnostics.Process
|
||||
$process.StartInfo=$info
|
||||
if (!$process.Start()) { throw 'Cannot start native override probe' }
|
||||
$stdout=$process.StandardOutput.ReadToEndAsync(); $stderr=$process.StandardError.ReadToEndAsync()
|
||||
if (!$process.WaitForExit(120000)) { $process.Kill(); $process.WaitForExit(); throw 'Native override test timed out' }
|
||||
[IO.File]::WriteAllText((Join-Path $OutputDirectory "$Name.stdout.log"),$stdout.GetAwaiter().GetResult())
|
||||
[IO.File]::WriteAllText((Join-Path $OutputDirectory "$Name.stderr.log"),$stderr.GetAwaiter().GetResult())
|
||||
$code=$process.ExitCode; $process.Dispose()
|
||||
return $code
|
||||
}
|
||||
try {
|
||||
$env:D4R_ZLUDA_VERBOSE = '1'; $env:D4R_DIAG_DIR = $OutputDirectory
|
||||
$bad = Join-Path $OutputDirectory 'wrong-identity'
|
||||
$good = Join-Path $OutputDirectory 'matching-identity'
|
||||
New-Item -ItemType Directory -Force $bad,$good | Out-Null
|
||||
foreach ($directory in @($bad,$good)) {
|
||||
Copy-Item -LiteralPath (Join-Path $PackageRoot "bin/probe_${GpuArch}.hsaco") -Destination (Join-Path $directory 'd4r_ptx_pattern.hsaco') -Force
|
||||
}
|
||||
'd4r_ptx_pattern 0000000000000000' | Set-Content -LiteralPath (Join-Path $bad 'd4r-kernels.txt') -Encoding ASCII
|
||||
$env:D4R_ZLUDA_NATIVE_DIR = $bad
|
||||
if (Invoke-NativeProbe 'wrong') { throw 'Wrong identity must use the correct translated fallback' }
|
||||
if (!(Select-String -LiteralPath (Join-Path $OutputDirectory 'wrong.stderr.log') -Pattern 'reason=ptx_identity.*fallback=translated' -Quiet)) {
|
||||
throw 'Wrong identity was not explicitly rejected'
|
||||
}
|
||||
$identity = Select-String -LiteralPath (Join-Path $OutputDirectory 'wrong.stdout.log') -Pattern 'fnv1a64=([0-9a-f]{16})'
|
||||
if (!$identity) { throw 'PTX probe did not report its identity' }
|
||||
"d4r_ptx_pattern $($identity.Matches[0].Groups[1].Value)" | Set-Content -LiteralPath (Join-Path $good 'd4r-kernels.txt') -Encoding ASCII
|
||||
$env:D4R_ZLUDA_NATIVE_DIR = $good
|
||||
if (Invoke-NativeProbe 'matching') { throw 'Matching native override produced incorrect output' }
|
||||
$hits = @(Select-String -LiteralPath (Join-Path $OutputDirectory 'matching.stderr.log') -Pattern '\[d4r-launch\].*backend=native')
|
||||
if ($hits.Count -ne 32) { throw "Expected 32 native launches, got $($hits.Count)" }
|
||||
Write-Host 'PASS NATIVE_IDENTITY wrong_hash=fallback matching_hash=native launches=32'
|
||||
} finally {
|
||||
foreach ($name in $previous.Keys) { [Environment]::SetEnvironmentVariable($name,$previous[$name],'Process') }
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$TextureRoot, [string]$ControlTextureRoot, [string]$CommonTextureRoot, [string]$ZludaRoot, [string]$PackageRoot,
|
||||
[string]$OutputDirectory, [int]$Iterations=8, [string]$OutputResolution='512x288', [string]$InputResolution,
|
||||
[switch]$CandidateFirst,
|
||||
[ValidateSet('output','input')][string]$KernelStage='output')
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$PackageRoot) { $PackageRoot=Join-Path $repo 'dist/windows-rdna4-command-list' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$TextureRoot) { $TextureRoot=Join-Path $repo "build/private-textures-$GpuArch" }
|
||||
if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' }
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo 'test-results/k-native-texture-reference' }
|
||||
if ($OutputResolution -notmatch '^([0-9]+)x([0-9]+)$') { throw 'OutputResolution must be WIDTHxHEIGHT.' }
|
||||
$outputWidth=[int]$Matches[1]; $outputHeight=[int]$Matches[2]
|
||||
$combined=Join-Path $OutputDirectory 'private-native'
|
||||
New-Item -ItemType Directory -Force $combined | Out-Null
|
||||
foreach ($directory in @((Join-Path $repo "build/native-k-$GpuArch"),$CommonTextureRoot,$TextureRoot) | Where-Object { $_ }) {
|
||||
Get-ChildItem -LiteralPath $directory -Filter '*.hsaco' | Copy-Item -Destination $combined -Force
|
||||
}
|
||||
& python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $combined (Join-Path $repo 'nvngx_dlss.dll')
|
||||
if ($LASTEXITCODE) { throw 'Combined private manifest failed.' }
|
||||
$controlNative=Join-Path $repo "build/native-k-$GpuArch"
|
||||
if ($ControlTextureRoot -or $CommonTextureRoot) {
|
||||
$controlNative=Join-Path $OutputDirectory 'control-private-native'
|
||||
New-Item -ItemType Directory -Force $controlNative | Out-Null
|
||||
foreach ($directory in @((Join-Path $repo "build/native-k-$GpuArch"),$CommonTextureRoot,$ControlTextureRoot) | Where-Object { $_ }) {
|
||||
Get-ChildItem -LiteralPath $directory -Filter '*.hsaco' | Copy-Item -Destination $controlNative -Force
|
||||
}
|
||||
& python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $controlNative (Join-Path $repo 'nvngx_dlss.dll')
|
||||
if ($LASTEXITCODE) { throw 'Control private manifest failed.' }
|
||||
}
|
||||
$settings=@{D4R_VALIDATE_OUTPUT='1'; D4R_PROFILE_STAGES='1'; D4R_ZLUDA_PROFILE='1'; D4R_QUIET_API='1';
|
||||
D4R_ZLUDA_WMMA='1'; D4R_ZLUDA_WMMA_FP8='1'; D4R_ZLUDA_WMMA_FP8_NATIVE='0';
|
||||
D4R_ZLUDA_WMMA_F16_REFERENCE='1'; D4R_ZLUDA_PROFILE_DEFERRED=$null;
|
||||
ZLUDA_CACHE_DIR=(Join-Path $repo 'build/zluda-cache-windows'); PYTHONPATH=(Join-Path $repo '.tools/python/vendor');
|
||||
D4R_ZLUDA_NATIVE_DIR=$null}
|
||||
$old=@{}
|
||||
$stagedObjects=@{}
|
||||
foreach ($stage in @(@{name='control'; root=$controlNative},@{name='candidate'; root=$combined})) {
|
||||
$stagedObjects[$stage.name]=@{}
|
||||
foreach ($object in Get-ChildItem -LiteralPath $stage.root -Filter '*.hsaco') {
|
||||
Assert-D4RCodeObjectTarget $object.FullName $GpuArch
|
||||
$stagedObjects[$stage.name][$object.Name]=(Get-FileHash -LiteralPath $object.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
}
|
||||
}
|
||||
try {
|
||||
foreach ($key in $settings.Keys) { $old[$key]=[Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') }
|
||||
foreach ($flags in @(0,11)) {
|
||||
$variant=if ($flags -eq 11) { 'depthinv_mvlo_hdr' } else { 'depthreg_mvhi_ldr' }
|
||||
$suffix=if ($KernelStage -eq 'output') { '_max_v2_rel' } else { '_v2_rel' }
|
||||
$kernel="hiluma_engine_${KernelStage}_${variant}${suffix}"
|
||||
$order=if ($CandidateFirst) { @('candidate','control') } else { @('control','candidate') }
|
||||
foreach ($mode in $order) {
|
||||
$env:D4R_ZLUDA_NATIVE_DIR=if ($mode -eq 'control') { $controlNative } else { $combined }
|
||||
$expectedBackend=if (Test-Path -LiteralPath (Join-Path $env:D4R_ZLUDA_NATIVE_DIR ($kernel + '.hsaco'))) { 'native' } else { 'translated' }
|
||||
if ($mode -eq 'candidate' -and $expectedBackend -ne 'native') { throw "Missing candidate object: $kernel" }
|
||||
$arguments=@{RuntimeProfile='therock'; ZludaRoot=$ZludaRoot;
|
||||
PackageRoot=$PackageRoot; NgxCore=(Join-Path $repo '_nvngx.dll');
|
||||
DlssDll=(Join-Path $repo 'nvngx_dlss.dll'); NgxMode='d3d12'; NgxAbi='project-legacy'; Preset=11;
|
||||
NgxCreateFlags=$flags; NgxOutputResolution=$OutputResolution; NgxOnly=$true; RequireNativeNetwork=$true; CommandListBackend=$true;
|
||||
NgxInputResolution=$InputResolution;
|
||||
PixelProfile='depth-stencil'; BarrierMode='inherited-legacy'; CaptureExceptions=$true; EarlyIndirectProbe=$true;
|
||||
Iterations=$Iterations; TimeoutSeconds=300; OptiScalerDll=(Join-Path $repo 'dist/optiscaler-windows-d4r/OptiScaler.dll');
|
||||
OutputDirectory=(Join-Path $OutputDirectory "$mode-$flags")}
|
||||
& (Join-Path $PSScriptRoot 'test-windows-rdna4.ps1') @arguments
|
||||
if ($LASTEXITCODE) { throw "Texture $mode flags=$flags workload failed." }
|
||||
& python (Join-Path $repo 'tools/windows/profile_report.py') $arguments.OutputDirectory --output (Join-Path $arguments.OutputDirectory 'profile.json')
|
||||
if ($LASTEXITCODE) { throw 'Texture profile report failed.' }
|
||||
$profile=Get-Content -LiteralPath (Join-Path $arguments.OutputDirectory 'profile.json') -Raw | ConvertFrom-Json
|
||||
$samples=@($profile.kernels | Where-Object { $_.kernel -eq $kernel -and $_.backend -eq $expectedBackend -and $_.phase -eq 'main' -and $_.serializing })
|
||||
if ($samples.Count -ne 1 -or $samples[0].gpuMs.samples -ne $Iterations) {
|
||||
throw "Missing valid serializing $KernelStage-kernel timings ($expectedBackend) for $mode flags=$flags; retain this run as correctness evidence only."
|
||||
}
|
||||
}
|
||||
$control=Join-Path $OutputDirectory "control-$flags"; $candidate=Join-Path $OutputDirectory "candidate-$flags"
|
||||
$stderr=Get-Content -LiteralPath (Join-Path $candidate 'd3d12-evaluate-preset-11.stderr.log') -Raw
|
||||
$hits=[regex]::Matches($stderr, ('\[d4r-launch\] kernel="' + $kernel + '" backend=native')).Count
|
||||
if ($hits -ne $Iterations) { throw "Texture override was not executed on every frame: $kernel hits=$hits" }
|
||||
& python (Join-Path $repo 'tools/windows/frame_compare.py') --reference $control --actual $candidate --width $outputWidth --height $outputHeight --exact
|
||||
if ($LASTEXITCODE) { throw "Texture flags=$flags full-frame RGB mismatch; preserve baseline." }
|
||||
}
|
||||
@{passed=$true; architecture=$GpuArch; framesPerVariant=$Iterations; outputResolution=$OutputResolution; createFlags=@(0,11); strictRgb=$true;
|
||||
inputResolution=$InputResolution;
|
||||
serializingProfile=$true; candidateFirst=[bool]$CandidateFirst; kernelStage=$KernelStage;
|
||||
controlTextureRoot=$ControlTextureRoot; commonTextureRoot=$CommonTextureRoot;
|
||||
stagedObjects=$stagedObjects;
|
||||
manifestSha256=(Get-FileHash -LiteralPath (Join-Path $TextureRoot 'd4r-kernels.txt') -Algorithm SHA256).Hash.ToLowerInvariant();
|
||||
privateNvidiaDerivedCode=$true; source=(Get-Content -LiteralPath (Join-Path $TextureRoot 'build-info.json') -Raw | ConvertFrom-Json)} |
|
||||
ConvertTo-Json -Depth 12 | Set-Content -LiteralPath (Join-Path $TextureRoot 'validation.json') -Encoding UTF8
|
||||
Write-Host "PASS: both private K $KernelStage variants executed and matched every RGB component. $OutputDirectory"
|
||||
} finally {
|
||||
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
# Reproduce numeric NGX feature discovery without launching or modifying a game.
|
||||
# The private NVIDIA DLL staging directory is deleted; reports contain logs only.
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true)][string]$DiagnosticRoot,
|
||||
[Parameter(Mandatory=$true)][string]$HipRoot,
|
||||
[Parameter(Mandatory=$true)][string]$ZludaRoot,
|
||||
[Parameter(Mandatory=$true)][string]$NativeRoot,
|
||||
[Parameter(Mandatory=$true)][string]$OptiScalerDll,
|
||||
[Parameter(Mandatory=$true)][string]$NgxCore,
|
||||
[Parameter(Mandatory=$true)][string]$DlssDll,
|
||||
[Parameter(Mandatory=$true)][string]$OutputDirectory,
|
||||
[UInt64]$ApplicationId=100152211)
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
foreach ($name in @('DiagnosticRoot','HipRoot','ZludaRoot','NativeRoot','OptiScalerDll','NgxCore','DlssDll')) {
|
||||
Set-Variable -Name $name -Value ((Get-Item -LiteralPath (Get-Variable -Name $name -ValueOnly)).FullName)
|
||||
}
|
||||
$arch=Get-D4RGpuTarget $DiagnosticRoot
|
||||
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$runtime=Join-Path ([IO.Path]::GetTempPath()) ('d4r-feature-layout-'+[Guid]::NewGuid().ToString('N'))
|
||||
$settings=@{HIP_PATH=$HipRoot; PATH="$HipRoot\bin;$ZludaRoot;$env:PATH";
|
||||
D4R_NVAPI_BACKEND=(Join-Path $ZludaRoot 'nvapi64.dll'); ZLUDA_CUDA_LIB=(Join-Path $ZludaRoot 'nvcuda.dll');
|
||||
D4R_ZLUDA_NATIVE_DIR=$NativeRoot; D4R_ZLUDA_VERBOSE='1';
|
||||
D4R_ZLUDA_WMMA='1'; D4R_ZLUDA_WMMA_F16_REFERENCE='1'; D4R_ZLUDA_WMMA_FP8='1'; D4R_ZLUDA_WMMA_FP8_NATIVE='0';
|
||||
D4R_VALIDATE_OUTPUT='1'; D4R_DIAG_DIR=$null; ZLUDA_LOG_DIR=$null; D4R_FORMAT_MODULE=$null;
|
||||
D4R_ASYNC_INTEROP=$null; D4R_BATCH_INPUT_COPIES=$null; D4R_D3D12_COMMAND_BACKEND=$null}
|
||||
$old=@{}; $results=@()
|
||||
try {
|
||||
foreach ($key in $settings.Keys) { $old[$key]=[Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') }
|
||||
foreach ($dir in @('app','d4r','d4r/vendor','d4r/dlss')) { New-Item -ItemType Directory -Force (Join-Path $runtime $dir) | Out-Null }
|
||||
$exe=Join-Path $runtime 'app/d4r_d3d12_dlss_probe.exe'
|
||||
$shim=Join-Path $runtime 'd4r/_nvngx.dll'
|
||||
$core=Join-Path $runtime 'd4r/ngx/_nvngx.dll'
|
||||
$feature=Join-Path $runtime 'd4r/vendor/nvngx_dlss.dll'
|
||||
Copy-Item -LiteralPath (Join-Path $DiagnosticRoot 'bin/d4r_d3d12_dlss_probe.exe') -Destination $exe
|
||||
Copy-Item -LiteralPath (Join-Path $DiagnosticRoot 'bin/d4r_nvngx.dll') -Destination $shim
|
||||
Copy-Item -LiteralPath (Join-Path $DiagnosticRoot "bin/pixel_convert_${arch}.hsaco") -Destination (Split-Path $shim)
|
||||
Copy-Item -LiteralPath $NgxCore -Destination $core
|
||||
Copy-Item -LiteralPath $DlssDll -Destination $feature
|
||||
$opti=Join-Path $runtime 'app/OptiScaler.dll'
|
||||
Copy-Item -LiteralPath $OptiScalerDll -Destination $opti
|
||||
$cases=@(@{name='vendor-only-negative'; preset=11; frontend='d4r'; expectedExit=4},
|
||||
@{name='optiscaler-vendor-only-negative'; preset=11; frontend='optiscaler'; expectedExit=4},
|
||||
@{name='caller-dll-k'; preset=11; frontend='d4r'; expectedExit=0},
|
||||
@{name='optiscaler-caller-k'; preset=11; frontend='optiscaler'; expectedExit=0},
|
||||
@{name='optiscaler-caller-m'; preset=13; frontend='optiscaler'; expectedExit=0})
|
||||
foreach ($case in $cases) {
|
||||
$out=Join-Path $OutputDirectory $case.name
|
||||
New-Item -ItemType Directory -Force $out | Out-Null
|
||||
if ($case.expectedExit -eq 0) {
|
||||
$feature=Join-Path $runtime 'd4r/dlss/nvngx_dlss.dll'
|
||||
if (!(Test-Path -LiteralPath $feature)) { Copy-Item -LiteralPath $DlssDll -Destination $feature }
|
||||
}
|
||||
$env:D4R_DIAG_DIR=$out; $env:ZLUDA_LOG_DIR=Join-Path $out 'zluda-trace'
|
||||
$env:D4R_FORMAT_MODULE=Join-Path $runtime "d4r/pixel_convert_${arch}.hsaco"
|
||||
$module=if ($case.frontend -eq 'optiscaler') { $opti } else { $shim }
|
||||
if ($case.frontend -eq 'optiscaler') {
|
||||
@"
|
||||
[Upscalers]
|
||||
Dx12Upscaler=dlss
|
||||
[FrameGen]
|
||||
Enabled=false
|
||||
FGInput=nofg
|
||||
FGOutput=nofg
|
||||
[DLSS]
|
||||
Enabled=true
|
||||
AllowExternalBackend=true
|
||||
RenderPresetOverride=true
|
||||
RenderPresetForAll=$($case.preset)
|
||||
UseGenericAppIdWithDlss=false
|
||||
[Libraries]
|
||||
NvngxPath=$(Split-Path $shim)
|
||||
NvngxDlssPath=$(Split-Path $feature)
|
||||
NvngxFeaturePath=$(Split-Path $feature)
|
||||
[Hotfix]
|
||||
RestoreComputeSignature=false
|
||||
RestoreGraphicSignature=false
|
||||
ExtendedStateRestore=false
|
||||
[Log]
|
||||
LogToFile=true
|
||||
LogLevel=0
|
||||
[Menu]
|
||||
DisableSplash=true
|
||||
"@ | Set-Content -LiteralPath (Join-Path $runtime 'app/OptiScaler.ini') -Encoding UTF8
|
||||
}
|
||||
$arguments=@('--hip-root',$HipRoot,'--cuda-dll',(Join-Path $ZludaRoot 'nvcuda.dll'),
|
||||
'--module',$module,'--ngx-core',$core,'--dlss-dll',$feature,
|
||||
'--nvapi-dll',(Join-Path $DiagnosticRoot 'nvapi-compat/nvapi64.dll'),
|
||||
'--ngx-app-id',"$ApplicationId",'--ngx-abi','driver','--ngx-frontend',$case.frontend,
|
||||
'--interop-mode','command-list','--iterations','3','--preset',"$($case.preset)")
|
||||
$info=New-Object Diagnostics.ProcessStartInfo
|
||||
# Capture harness faults outside OptiScaler's loader hooks. Games use
|
||||
# the no-debug launcher by default; this developer reproducer is separate.
|
||||
$arguments=@('--output-directory',$out,'--',$exe)+$arguments
|
||||
$info.FileName=Join-Path $DiagnosticRoot 'bin/d4r_debug_launcher.exe'; $info.WorkingDirectory=Split-Path $exe
|
||||
$info.Arguments=(@($arguments | ForEach-Object { if ($_.Contains('"')) { throw 'Unsupported quote in test path.' }; '"'+$_+'"' }) -join ' ')
|
||||
$info.UseShellExecute=$false; $info.CreateNoWindow=$true; $info.RedirectStandardOutput=$true; $info.RedirectStandardError=$true
|
||||
foreach ($key in $settings.Keys) { if (![Environment]::GetEnvironmentVariable($key,'Process')) { $info.EnvironmentVariables.Remove($key) } }
|
||||
$process=New-Object Diagnostics.Process; $process.StartInfo=$info
|
||||
try {
|
||||
[void]$process.Start(); $stdout=$process.StandardOutput.ReadToEndAsync(); $stderr=$process.StandardError.ReadToEndAsync()
|
||||
if (!$process.WaitForExit(240000)) { $process.Kill(); $process.WaitForExit(); throw "NGX layout test timed out: $($case.name)" }
|
||||
$log=$stdout.GetAwaiter().GetResult(); $errorLog=$stderr.GetAwaiter().GetResult()
|
||||
[IO.File]::WriteAllText((Join-Path $out 'stdout.log'),$log)
|
||||
[IO.File]::WriteAllText((Join-Path $out 'stderr.log'),$errorLog)
|
||||
if ($process.ExitCode -ne $case.expectedExit) { throw "Unexpected exit $($process.ExitCode): $($case.name), see $out" }
|
||||
if ($case.expectedExit) {
|
||||
if ($log -notmatch 'D4R_CAPABILITIES available=0 feature_init=0xbad00004' -or $log -match 'D4R_FRAME ') { throw 'Old vendor-only layout did not reproduce FeatureNotFound.' }
|
||||
if ($case.frontend -eq 'optiscaler' -and $errorLog -notmatch 'FAIL D3D12_DLSS D3D12 Init NGX=') { throw 'OptiScaler swallowed the numeric backend initialization failure.' }
|
||||
} else {
|
||||
if ($log -notmatch 'D4R_CAPABILITIES available=1 feature_init=0x00000001 needs_driver=0' -or
|
||||
([regex]::Matches($log,'D3D12_OUTPUT preset=\d+ frame=\d+ finite=1')).Count -ne 3 -or
|
||||
([regex]::Matches($log,'D4R_OUTPUT_VALIDATION elements=\d+ nan=0 inf=0 diagnostics_cpu_bytes=8')).Count -ne 3 -or
|
||||
$log -notmatch 'PASS D3D12_DLSS .*fast_path_cpu_copies=0 frame_age=0' -or $errorLog -match 'D4R_WINDOWS_FAILURE') { throw 'Corrected feature layout did not complete finite zero-copy frames.' }
|
||||
$required=@(Get-Content -LiteralPath (Join-Path $NativeRoot 'd4r-kernels.txt') | Where-Object {
|
||||
if ($case.preset -eq 11) { $_ -match '^dltss_pwin_' } else { $_ -match '^rrlite_' }
|
||||
} | ForEach-Object { $_.Split(' ')[0] })
|
||||
if ($required.Count -ne $(if ($case.preset -eq 11) { 11 } else { 5 })) { throw 'Incomplete native manifest.' }
|
||||
foreach ($kernel in $required) {
|
||||
if ($errorLog -notmatch ('\[d4r-launch\] kernel="'+[regex]::Escape($kernel)+'" backend=native') -or
|
||||
$errorLog -match ('\[d4r-launch\] kernel="'+[regex]::Escape($kernel)+'" backend=translated')) { throw "Missing native network execution: $kernel" }
|
||||
}
|
||||
if ($case.frontend -eq 'd4r' -and $log -notmatch 'D3D12_REQUIREMENTS warp_supported=0 enumeration_success=1') { throw 'WARP adapter enumeration regression failed.' }
|
||||
}
|
||||
$results+=@{case=$case.name; passed=$true; expectedExit=$case.expectedExit; application=$ApplicationId; preset=$case.preset; architecture=$arch}
|
||||
Write-Host "PASS NGX_FEATURE_LAYOUT $($case.name)"
|
||||
} finally {
|
||||
if ($case.frontend -eq 'optiscaler') {
|
||||
Get-ChildItem -LiteralPath (Split-Path $exe) -Filter '*.log' -File | ForEach-Object {
|
||||
Copy-Item -LiteralPath $_.FullName -Destination $out -Force
|
||||
}
|
||||
}
|
||||
$process.Dispose()
|
||||
}
|
||||
}
|
||||
for ($frame=0; $frame -lt 3; $frame++) {
|
||||
$direct=[IO.File]::ReadAllBytes((Join-Path $OutputDirectory "caller-dll-k/output-$frame.rgba16f"))
|
||||
$frontend=[IO.File]::ReadAllBytes((Join-Path $OutputDirectory "optiscaler-caller-k/output-$frame.rgba16f"))
|
||||
if ($direct.Length -ne $frontend.Length -or $direct.Length -ne 512*288*8) { throw 'RGB comparison dimensions differ.' }
|
||||
for ($pixel=0; $pixel -lt $direct.Length; $pixel+=8) {
|
||||
if (([BitConverter]::ToUInt64($direct,$pixel) -band [UInt64]0xffffffffffff) -ne
|
||||
([BitConverter]::ToUInt64($frontend,$pixel) -band [UInt64]0xffffffffffff)) { throw "K frontend RGB mismatch: frame=$frame offset=$pixel" }
|
||||
}
|
||||
}
|
||||
$results+=@{case='k-direct-optiscaler-rgb'; passed=$true; frames=3; rgbByteExact=$true; maxAbsoluteError=0; maxRelativeError=0}
|
||||
Write-Host 'PASS NGX_FEATURE_LAYOUT k_direct_optiscaler_rgb exact=1 max_abs=0 max_rel=0'
|
||||
} finally {
|
||||
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
|
||||
$results | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'feature-layout.json') -Encoding UTF8
|
||||
$resolved=[IO.Path]::GetFullPath($runtime)
|
||||
$temp=[IO.Path]::GetFullPath([IO.Path]::GetTempPath()).TrimEnd('\')+'\'
|
||||
if (!$resolved.StartsWith($temp,[StringComparison]::OrdinalIgnoreCase) -or [IO.Path]::GetFileName($resolved) -notmatch '^d4r-feature-layout-[0-9a-f]{32}$') { throw 'Refusing cleanup outside private NGX staging.' }
|
||||
if (Test-Path -LiteralPath $resolved) {
|
||||
foreach ($attempt in 1..10) {
|
||||
try { Remove-Item -LiteralPath $resolved -Recurse -Force; break }
|
||||
catch { if ($attempt -eq 10) { throw }; Start-Sleep -Milliseconds 300 }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
# Drag-in player mode checks. Builds with the vendored llvm-mingw; needs no
|
||||
# NVIDIA DLLs and launches no GPU kernels. The loader case enumerates HIP
|
||||
# devices and is skipped (reported, not passed) without a supported AMD GPU.
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$CompilerRoot, [string]$HipRoot, [string]$OutputDirectory
|
||||
)
|
||||
$ErrorActionPreference='Stop'
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$CompilerRoot) { $CompilerRoot=Join-Path $repo '.tools/llvm-mingw-20260922-ucrt-x86_64' }
|
||||
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/player-config/'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$cxx=Join-Path $CompilerRoot 'bin/x86_64-w64-mingw32-clang++.exe'
|
||||
if (!(Test-Path -LiteralPath $cxx)) { throw "llvm-mingw not found: $cxx" }
|
||||
if (!(Test-Path -LiteralPath (Join-Path $HipRoot 'include/hip/hip_runtime_api.h'))) { throw "HIP headers not found under $HipRoot" }
|
||||
|
||||
# Same generated target table as CMakeLists.txt.
|
||||
$targets=(Get-Content -LiteralPath (Join-Path $repo 'tools/windows/gpu-targets.json') -Raw | ConvertFrom-Json).targets
|
||||
$generated=Join-Path $OutputDirectory 'generated'
|
||||
New-Item -ItemType Directory -Force $generated | Out-Null
|
||||
$entries=($targets | ForEach-Object { " GpuTarget{`"$($_.architecture)`", $($_.elfMachine), $($_.isaMajor), `"$($_.family)`"}," }) -join "`n"
|
||||
@('#pragma once','#include <array>','namespace d4r::diag {',
|
||||
'struct GpuTarget { const char* architecture; unsigned elf_machine; unsigned isa_major; const char* family; };',
|
||||
"inline constexpr std::array<GpuTarget, $($targets.Count)> gpu_targets = {{",$entries,'}};','}') |
|
||||
Set-Content -LiteralPath (Join-Path $generated 'gpu_targets.generated.h') -Encoding ASCII
|
||||
$common=@('-std=c++17','-O1','-static','-DWIN32_LEAN_AND_MEAN','-DNOMINMAX','-D__HIP_PLATFORM_AMD__','-DD4R_TARGET_ARCH="gfx1201"',
|
||||
'-I',$generated,'-I',(Join-Path $repo 'tools/windows'),'-I',(Join-Path $repo 'tools'),'-I',(Join-Path $HipRoot 'include'))
|
||||
# Windows PowerShell turns native stderr into terminating errors under Stop;
|
||||
# judge native tools by exit code and keep their output in the log.
|
||||
function Invoke-Native([string]$Log,[string]$Exe,[string[]]$Arguments) {
|
||||
$ErrorActionPreference='Continue'
|
||||
& $Exe @Arguments *> $Log
|
||||
return $LASTEXITCODE
|
||||
}
|
||||
function Build([string]$Name,[string[]]$Arguments) {
|
||||
$log=Join-Path $OutputDirectory "build-$Name.log"
|
||||
# PowerShell 5 / Legacy native passing strips embedded quotes. PowerShell
|
||||
# 7's Standard/Windows passing preserves them and must not double-escape.
|
||||
if ($PSVersionTable.PSVersion.Major -le 5 -or $PSNativeCommandArgumentPassing -eq 'Legacy') {
|
||||
$Arguments=@($Arguments | ForEach-Object { $_.Replace('"','\"') })
|
||||
}
|
||||
if (Invoke-Native $log $cxx $Arguments) { throw "Build failed: $Name; see $log" }
|
||||
}
|
||||
$results=@()
|
||||
|
||||
# 1. d4r.ini validation and DLL pin decisions (the shim's own header).
|
||||
$probe=Join-Path $OutputDirectory 'player_config_probe.exe'
|
||||
Build 'player-config' ($common+@('-municode',(Join-Path $repo 'tools/windows/player_config_probe.cpp'),'-o',$probe,'-lversion','-ldbghelp','-lbcrypt'))
|
||||
if (Invoke-Native (Join-Path $OutputDirectory 'player-config.log') $probe @((Join-Path $OutputDirectory 'pins-fixture'))) { throw "player_config_probe failed; see $(Join-Path $OutputDirectory 'player-config.log')" }
|
||||
$results+=@{case='d4r-ini-and-dll-pins'; passed=$true}
|
||||
|
||||
# 2. Loader: GPU-free marker, LUID-based target selection, forwarding, and a
|
||||
# clear failure when the folder has no build for the detected GPU.
|
||||
$folder=Join-Path $OutputDirectory 'game/d4r'
|
||||
New-Item -ItemType Directory -Force (Join-Path $folder 'hip/bin') | Out-Null
|
||||
Build 'loader' ($common+@('-shared',(Join-Path $repo 'tools/windows/d4r_nvngx_loader.cpp'),'-o',(Join-Path $folder '_nvngx.dll'),'-lversion','-ldbghelp'))
|
||||
$harness=Join-Path $OutputDirectory 'loader_selection_probe.exe'
|
||||
Build 'harness' @('-municode','-static',(Join-Path $repo 'tools/windows/loader_selection_probe.cpp'),'-o',$harness,'-ldxgi','-ldxguid')
|
||||
$stub=Join-Path $OutputDirectory 'stub.cpp'
|
||||
@('#define API extern "C" __declspec(dllexport)',
|
||||
'struct Req { unsigned FeatureSupported, MinHWArchitecture; char MinOSVersion[255]; };',
|
||||
'API unsigned NVSDK_NGX_D3D12_GetFeatureRequirements(void*, const void*, Req* r) { r->FeatureSupported = 0; r->MinHWArchitecture = 0x5101; return 1; }') |
|
||||
Set-Content -LiteralPath $stub -Encoding ASCII
|
||||
$stubDll=Join-Path $OutputDirectory 'stub.dll'
|
||||
Build 'stub' @('-shared','-static',$stub,'-o',$stubDll)
|
||||
foreach ($name in @('amdhip64_7.dll','amd_comgr.dll','rocm_kpack.dll')) {
|
||||
$source=Join-Path $HipRoot "bin/$name"
|
||||
if (Test-Path -LiteralPath $source) { Copy-Item -LiteralPath $source -Destination (Join-Path $folder 'hip/bin') }
|
||||
}
|
||||
$log=Join-Path $OutputDirectory 'loader-without-builds.log'
|
||||
[void](Invoke-Native $log $harness @((Join-Path $folder '_nvngx.dll')))
|
||||
$text=Get-Content -LiteralPath $log -Raw
|
||||
if ($text -notmatch 'MARKER version=1 hip_loaded=0') { throw "Discovery marker loaded HIP or failed; see $log" }
|
||||
if ($text -match 'supported=0') { throw "An adapter was reported supported without any target build; see $log" }
|
||||
$gpuLog=Join-Path $folder 'd4r_nvngx.log'
|
||||
$detected=$null
|
||||
if (Test-Path -LiteralPath $gpuLog) { $detected=[regex]::Match((Get-Content -LiteralPath $gpuLog -Raw),'no build for your GPU \((gfx[0-9a-f]+)\)').Groups[1].Value }
|
||||
if (!$detected) {
|
||||
$results+=@{case='loader-selection'; passed=$null; skipped='no supported AMD HIP device detected'}
|
||||
Write-Host 'SKIP loader selection: no supported AMD HIP device detected on this machine.' -ForegroundColor Yellow
|
||||
} else {
|
||||
if ((Get-Content -LiteralPath $gpuLog -Raw) -notmatch 'It supports: none \(the d4r folder is incomplete') { throw "Unclear unsupported-GPU message; see $gpuLog" }
|
||||
Copy-Item -LiteralPath $stubDll -Destination (Join-Path $folder "_nvngx_$detected.dll")
|
||||
$log=Join-Path $OutputDirectory 'loader-with-build.log'
|
||||
[void](Invoke-Native $log $harness @((Join-Path $folder '_nvngx.dll')))
|
||||
if ((Get-Content -LiteralPath $log -Raw) -notmatch 'vendor=0x1002 status=0x1 supported=0 marker=0x5101') { throw "AMD adapter was not forwarded to _nvngx_$detected.dll; see $log" }
|
||||
$results+=@{case='loader-selection'; passed=$true; architecture=$detected; gpuKernelsExecuted=$false}
|
||||
}
|
||||
$results | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Write-Host "PASS PLAYER_CONFIG cases=$(@($results | Where-Object { $_.passed }).Count) skipped=$(@($results | Where-Object { $_.skipped }).Count) Report: $OutputDirectory"
|
||||
@@ -0,0 +1,96 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$TextureRoot, [string]$ControlTextureRoot, [string]$CommonTextureRoot, [string]$ZludaRoot, [string]$PackageRoot,
|
||||
[string]$OutputDirectory, [int]$Iterations=8, [string]$OutputResolution='512x288', [string]$InputResolution,
|
||||
[switch]$CandidateFirst,
|
||||
[ValidateSet('output','input')][string]$KernelStage='output')
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$PackageRoot) { $PackageRoot=Join-Path $repo 'dist/windows-rdna4-command-list' }
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $null
|
||||
if (!$TextureRoot) { $TextureRoot=Join-Path $repo "build/private-textures-$GpuArch" }
|
||||
if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' }
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo 'test-results/k-native-texture-reference' }
|
||||
if ($OutputResolution -notmatch '^([0-9]+)x([0-9]+)$') { throw 'OutputResolution must be WIDTHxHEIGHT.' }
|
||||
$outputWidth=[int]$Matches[1]; $outputHeight=[int]$Matches[2]
|
||||
$combined=Join-Path $OutputDirectory 'private-native'
|
||||
New-Item -ItemType Directory -Force $combined | Out-Null
|
||||
foreach ($directory in @((Join-Path $repo "build/native-k-$GpuArch"),$CommonTextureRoot,$TextureRoot) | Where-Object { $_ }) {
|
||||
Get-ChildItem -LiteralPath $directory -Filter '*.hsaco' | Copy-Item -Destination $combined -Force
|
||||
}
|
||||
& python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $combined (Join-Path $repo 'nvngx_dlss.dll')
|
||||
if ($LASTEXITCODE) { throw 'Combined private manifest failed.' }
|
||||
$controlNative=Join-Path $repo "build/native-k-$GpuArch"
|
||||
if ($ControlTextureRoot -or $CommonTextureRoot) {
|
||||
$controlNative=Join-Path $OutputDirectory 'control-private-native'
|
||||
New-Item -ItemType Directory -Force $controlNative | Out-Null
|
||||
foreach ($directory in @((Join-Path $repo "build/native-k-$GpuArch"),$CommonTextureRoot,$ControlTextureRoot) | Where-Object { $_ }) {
|
||||
Get-ChildItem -LiteralPath $directory -Filter '*.hsaco' | Copy-Item -Destination $controlNative -Force
|
||||
}
|
||||
& python (Join-Path $repo 'kernels/tools/kernel_manifest.py') $controlNative (Join-Path $repo 'nvngx_dlss.dll')
|
||||
if ($LASTEXITCODE) { throw 'Control private manifest failed.' }
|
||||
}
|
||||
$settings=@{D4R_VALIDATE_OUTPUT='1'; D4R_PROFILE_STAGES='1'; D4R_ZLUDA_PROFILE='1'; D4R_QUIET_API='1';
|
||||
D4R_ZLUDA_WMMA='1'; D4R_ZLUDA_WMMA_FP8='1'; D4R_ZLUDA_WMMA_FP8_NATIVE='0';
|
||||
D4R_ZLUDA_WMMA_F16_REFERENCE='1'; D4R_ZLUDA_PROFILE_DEFERRED=$null;
|
||||
ZLUDA_CACHE_DIR=(Join-Path $repo 'build/zluda-cache-windows'); PYTHONPATH=(Join-Path $repo '.tools/python/vendor');
|
||||
D4R_ZLUDA_NATIVE_DIR=$null}
|
||||
$old=@{}
|
||||
$stagedObjects=@{}
|
||||
foreach ($stage in @(@{name='control'; root=$controlNative},@{name='candidate'; root=$combined})) {
|
||||
$stagedObjects[$stage.name]=@{}
|
||||
foreach ($object in Get-ChildItem -LiteralPath $stage.root -Filter '*.hsaco') {
|
||||
Assert-D4RCodeObjectTarget $object.FullName $GpuArch
|
||||
$stagedObjects[$stage.name][$object.Name]=(Get-FileHash -LiteralPath $object.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
}
|
||||
}
|
||||
try {
|
||||
foreach ($key in $settings.Keys) { $old[$key]=[Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') }
|
||||
foreach ($flags in @(11)) {
|
||||
$variant=if ($flags -eq 11) { 'depthinv_mvlo_hdr' } else { 'depthreg_mvhi_ldr' }
|
||||
$suffix=if ($KernelStage -eq 'output') { '_max_v2_rel' } else { '_v2_rel' }
|
||||
$kernel="hiluma_engine_${KernelStage}_${variant}${suffix}"
|
||||
$order=if ($CandidateFirst) { @('candidate','control') } else { @('control','candidate') }
|
||||
foreach ($mode in $order) {
|
||||
$env:D4R_ZLUDA_NATIVE_DIR=if ($mode -eq 'control') { $controlNative } else { $combined }
|
||||
$expectedBackend=if (Test-Path -LiteralPath (Join-Path $env:D4R_ZLUDA_NATIVE_DIR ($kernel + '.hsaco'))) { 'native' } else { 'translated' }
|
||||
if ($mode -eq 'candidate' -and $expectedBackend -ne 'native') { throw "Missing candidate object: $kernel" }
|
||||
$arguments=@{RuntimeProfile='therock'; ZludaRoot=$ZludaRoot;
|
||||
PackageRoot=$PackageRoot; NgxCore=(Join-Path $repo '_nvngx.dll');
|
||||
DlssDll=(Join-Path $repo 'nvngx_dlss.dll'); NgxMode='d3d12'; NgxAbi='project-legacy'; Preset=11;
|
||||
NgxCreateFlags=$flags; NgxOutputResolution=$OutputResolution; NgxOnly=$true; RequireNativeNetwork=$true; CommandListBackend=$true;
|
||||
NgxInputResolution=$InputResolution;
|
||||
PixelProfile='depth-stencil'; BarrierMode='inherited-legacy'; CaptureExceptions=$true; EarlyIndirectProbe=$true;
|
||||
Iterations=$Iterations; TimeoutSeconds=1200; OptiScalerDll=(Join-Path $repo 'dist/optiscaler-windows-d4r/OptiScaler.dll');
|
||||
OutputDirectory=(Join-Path $OutputDirectory "$mode-$flags")}
|
||||
& (Join-Path $PSScriptRoot 'test-windows-rdna4.ps1') @arguments
|
||||
if ($LASTEXITCODE) { throw "Texture $mode flags=$flags workload failed." }
|
||||
& python (Join-Path $repo 'tools/windows/profile_report.py') $arguments.OutputDirectory --output (Join-Path $arguments.OutputDirectory 'profile.json')
|
||||
if ($LASTEXITCODE) { throw 'Texture profile report failed.' }
|
||||
$profile=Get-Content -LiteralPath (Join-Path $arguments.OutputDirectory 'profile.json') -Raw | ConvertFrom-Json
|
||||
$samples=@($profile.kernels | Where-Object { $_.kernel -eq $kernel -and $_.backend -eq $expectedBackend -and $_.phase -eq 'main' -and $_.serializing })
|
||||
if ($samples.Count -ne 1 -or $samples[0].gpuMs.samples -ne $Iterations) {
|
||||
throw "Missing valid serializing $KernelStage-kernel timings ($expectedBackend) for $mode flags=$flags; retain this run as correctness evidence only."
|
||||
}
|
||||
}
|
||||
$control=Join-Path $OutputDirectory "control-$flags"; $candidate=Join-Path $OutputDirectory "candidate-$flags"
|
||||
$stderr=Get-Content -LiteralPath (Join-Path $candidate 'd3d12-evaluate-preset-11.stderr.log') -Raw
|
||||
$hits=[regex]::Matches($stderr, ('\[d4r-launch\] kernel="' + $kernel + '" backend=native')).Count
|
||||
if ($hits -ne $Iterations) { throw "Texture override was not executed on every frame: $kernel hits=$hits" }
|
||||
& python (Join-Path $repo 'tools/windows/frame_compare.py') --reference $control --actual $candidate --width $outputWidth --height $outputHeight --exact
|
||||
if ($LASTEXITCODE) { throw "Texture flags=$flags full-frame RGB mismatch; preserve baseline." }
|
||||
}
|
||||
@{passed=$true; architecture=$GpuArch; framesPerVariant=$Iterations; outputResolution=$OutputResolution; createFlags=@(0,11); strictRgb=$true;
|
||||
inputResolution=$InputResolution;
|
||||
serializingProfile=$true; candidateFirst=[bool]$CandidateFirst; kernelStage=$KernelStage;
|
||||
controlTextureRoot=$ControlTextureRoot; commonTextureRoot=$CommonTextureRoot;
|
||||
stagedObjects=$stagedObjects;
|
||||
manifestSha256=(Get-FileHash -LiteralPath (Join-Path $TextureRoot 'd4r-kernels.txt') -Algorithm SHA256).Hash.ToLowerInvariant();
|
||||
privateNvidiaDerivedCode=$true; source=(Get-Content -LiteralPath (Join-Path $TextureRoot 'build-info.json') -Raw | ConvertFrom-Json)} |
|
||||
ConvertTo-Json -Depth 12 | Set-Content -LiteralPath (Join-Path $TextureRoot 'validation.json') -Encoding UTF8
|
||||
Write-Host "PASS: both private K $KernelStage variants executed and matched every RGB component. $OutputDirectory"
|
||||
} finally {
|
||||
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
# Offline installer fixture. Uses locally supplied NVIDIA DLLs and never writes to a game folder.
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$NgxCore,
|
||||
[Parameter(Mandatory=$true)][string]$DlssDll,
|
||||
[Parameter(Mandatory=$true)][string]$DlssdDll,
|
||||
[string]$OutputDirectory
|
||||
)
|
||||
$ErrorActionPreference='Stop'
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/windows-dlss-installer/'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||
if (Test-Path -LiteralPath $OutputDirectory) { throw "Use a new output directory: $OutputDirectory" }
|
||||
$NgxCore=(Get-Item -LiteralPath $NgxCore -ErrorAction Stop).FullName
|
||||
$DlssDll=(Get-Item -LiteralPath $DlssDll -ErrorAction Stop).FullName
|
||||
$DlssdDll=(Get-Item -LiteralPath $DlssdDll -ErrorAction Stop).FullName
|
||||
$expected=@{
|
||||
'_nvngx.dll'='66767018c36b3bab46398dade3adf173daa3730fda75965689ea848c9bc4e79b'
|
||||
'nvngx_dlss.dll'='3975567b8943c53acce397f2b72380092f84f162d00b0d2c7d08a1025c563983'
|
||||
'nvngx_dlssd.dll'='4bc7ea5fcb2f32cf86bc2cb072e8d2860914a0be511cbdcb2fc206c1d9d83b80'
|
||||
}
|
||||
$inputs=@{'_nvngx.dll'=$NgxCore;'nvngx_dlss.dll'=$DlssDll;'nvngx_dlssd.dll'=$DlssdDll}
|
||||
foreach($name in $inputs.Keys) {
|
||||
if ((Get-FileHash -LiteralPath $inputs[$name] -Algorithm SHA256).Hash.ToLowerInvariant() -ne $expected[$name]) {
|
||||
throw "Wrong local NVIDIA DLL for installer fixture: $name"
|
||||
}
|
||||
}
|
||||
$package=Join-Path $OutputDirectory 'package'
|
||||
$runtime=Join-Path $package 'd4r'
|
||||
$cache=Join-Path $OutputDirectory 'cache'
|
||||
$backupRoot=Join-Path $OutputDirectory 'backups'
|
||||
$nvidiaCache=Join-Path $cache 'nvidia'
|
||||
New-Item -ItemType Directory -Force (Join-Path $runtime 'ngx'),(Join-Path $runtime 'dlss'),(Join-Path $runtime 'rr'),$nvidiaCache | Out-Null
|
||||
Copy-Item -LiteralPath (Join-Path $repo 'packaging/windows/d4r.ini') -Destination (Join-Path $runtime 'd4r.ini')
|
||||
Copy-Item -LiteralPath (Join-Path $repo 'tools/windows/dll-pins.txt') -Destination (Join-Path $runtime 'dll-pins.txt')
|
||||
$targets=@(
|
||||
@{source=$DlssDll; name='nvngx_dlss.dll'; relative='dlss/nvngx_dlss.dll'},
|
||||
@{source=$DlssdDll; name='nvngx_dlssd.dll'; relative='rr/nvngx_dlssd.dll'},
|
||||
@{source=$NgxCore; name='_nvngx.dll'; relative='ngx/_nvngx.dll'}
|
||||
)
|
||||
$originals=@{}
|
||||
foreach($target in $targets) {
|
||||
$path=Join-Path $runtime $target.relative
|
||||
$original="fixture original $($target.name)"
|
||||
Set-Content -LiteralPath $path $original -Encoding ASCII
|
||||
$originals[$target.relative]=(Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash
|
||||
Copy-Item -LiteralPath $target.source -Destination (Join-Path $nvidiaCache $target.name)
|
||||
}
|
||||
|
||||
$installer=Join-Path $PSScriptRoot 'install-dlss.ps1'
|
||||
$powershell=Join-Path $env:SystemRoot 'System32/WindowsPowerShell/v1.0/powershell.exe'
|
||||
function Invoke-Installer([string]$name) {
|
||||
$log=Join-Path $OutputDirectory "$name.log"
|
||||
& $powershell -NoProfile -ExecutionPolicy Bypass -File $installer -PackageRoot $package -CacheRoot $cache -BackupRoot $backupRoot -Yes *> $log
|
||||
if ($LASTEXITCODE) { throw "Installer fixture failed ($name); see $log" }
|
||||
return Get-Content -LiteralPath $log -Raw
|
||||
}
|
||||
$first=Invoke-Installer 'install'
|
||||
foreach($target in $targets) {
|
||||
$path=Join-Path $runtime $target.relative
|
||||
if ((Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash.ToLowerInvariant() -ne $expected[$target.name]) {
|
||||
throw "Installed hash mismatch: $($target.relative)"
|
||||
}
|
||||
}
|
||||
$manifests=@(Get-ChildItem -LiteralPath $backupRoot -Recurse -Filter 'backup-manifest.json' -File)
|
||||
if ($manifests.Count -ne 1) { throw "Expected one outside backup manifest, found $($manifests.Count)" }
|
||||
$manifest=Get-Content -LiteralPath $manifests[0].FullName -Raw | ConvertFrom-Json
|
||||
if (@($manifest.files).Count -ne 3) { throw 'Expected three backed-up originals.' }
|
||||
foreach($entry in $manifest.files) {
|
||||
if (!$entry.backup -or !(Test-Path -LiteralPath $entry.backup) -or $entry.sha256 -ne $originals[$entry.path.Substring($runtime.Length+1).Replace('\','/')]) {
|
||||
throw "Backup path or original hash mismatch: $($entry.path)"
|
||||
}
|
||||
if ((Get-FileHash -LiteralPath $entry.backup -Algorithm SHA256).Hash -ne $entry.sha256) { throw "Backup bytes changed: $($entry.backup)" }
|
||||
}
|
||||
$second=Invoke-Installer 'rerun'
|
||||
if ($second -notmatch 'All three NVIDIA DLLs already match their pinned hashes\.' -or
|
||||
@(Get-ChildItem -LiteralPath $backupRoot -Recurse -Filter 'backup-manifest.json' -File).Count -ne 1) {
|
||||
throw 'Installer rerun was not idempotent.'
|
||||
}
|
||||
@{passed=$true;files=3;outsideBackups=3;idempotent=$true;archiveDownloaded=$false;driverRun=$false} |
|
||||
ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Write-Host "PASS WINDOWS_DLSS_INSTALLER files=3 backups=3 idempotent=1 Report: $OutputDirectory"
|
||||
@@ -0,0 +1,145 @@
|
||||
# Isolated installer checks; never starts a real game or launches GPU kernels.
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$PackageRoot,
|
||||
[Parameter(Mandatory=$true)][string]$NgxCore,
|
||||
[Parameter(Mandatory=$true)][string]$DlssDll,
|
||||
[string]$OutputDirectory
|
||||
)
|
||||
$ErrorActionPreference='Stop'
|
||||
if ($PSVersionTable.PSVersion.Major -ge 6) {
|
||||
# The fixture uses the .NET Framework C# compiler; the shipped launcher
|
||||
# also runs Windows PowerShell 5. PowerShell 7 cannot emit an EXE via Add-Type.
|
||||
$arguments=@('-NoProfile','-STA','-ExecutionPolicy','Bypass','-File',$PSCommandPath,
|
||||
'-PackageRoot',$PackageRoot,'-NgxCore',$NgxCore,'-DlssDll',$DlssDll)
|
||||
if ($OutputDirectory) { $arguments+=@('-OutputDirectory',$OutputDirectory) }
|
||||
& "$env:SystemRoot/System32/WindowsPowerShell/v1.0/powershell.exe" @arguments
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
$PackageRoot=(Get-Item -LiteralPath $PackageRoot).FullName
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/windows-quick-test/'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
function Invoke-Child([string]$Name,[string[]]$Arguments,[int]$ExpectedExit=0) {
|
||||
$log=Join-Path $OutputDirectory "$Name.log"
|
||||
& "$env:SystemRoot/System32/WindowsPowerShell/v1.0/powershell.exe" -NoProfile -STA -ExecutionPolicy Bypass `
|
||||
-File (Join-Path $PackageRoot 'files/quick-test.ps1') @Arguments *> $log
|
||||
if ($LASTEXITCODE -ne $ExpectedExit) { throw "Unexpected exit $LASTEXITCODE for $Name; see $log" }
|
||||
}
|
||||
function Fixture([string]$Name) {
|
||||
$dir=Join-Path $OutputDirectory $Name
|
||||
New-Item -ItemType Directory -Force $dir | Out-Null
|
||||
$exe=Join-Path $dir 'Fixture.exe'
|
||||
Set-Content -LiteralPath $exe 'installer fixture; do not execute' -Encoding ASCII
|
||||
return $exe
|
||||
}
|
||||
$results=@()
|
||||
$exe=Fixture ('space GPU '+[char]0x03a9+' test')
|
||||
$game=Split-Path $exe
|
||||
$before=@{}
|
||||
foreach ($name in @('dxgi.dll','OptiScaler.ini','nvngx_dlss.dll')) {
|
||||
Set-Content -LiteralPath (Join-Path $game $name) "original $name" -Encoding ASCII
|
||||
$before[$name]=(Get-FileHash -LiteralPath (Join-Path $game $name)).Hash
|
||||
}
|
||||
Invoke-Child 'install' @('-Action','install','-GameExe',$exe,'-NgxCore',$NgxCore,'-DlssDll',$DlssDll)
|
||||
if ((Get-FileHash -LiteralPath (Join-Path $game 'd4r/dlss/nvngx_dlss.dll')).Hash -ne (Get-FileHash -LiteralPath $DlssDll).Hash) {
|
||||
throw 'Numeric NGX driver SR feature DLL was not staged in its isolated search directory.'
|
||||
}
|
||||
if ((Get-FileHash -LiteralPath (Join-Path $game 'd4r/ngx/_nvngx.dll')).Hash -ne (Get-FileHash -LiteralPath $NgxCore).Hash) {
|
||||
throw 'NGX core was not staged at the player-mode path d4r/ngx/_nvngx.dll.'
|
||||
}
|
||||
if (!(Test-Path -LiteralPath (Join-Path $game 'd4r/d4r.ini'))) {
|
||||
throw 'd4r.ini was not staged; Steam player mode cannot configure its runtime paths.'
|
||||
}
|
||||
if (!(Test-Path -LiteralPath (Join-Path $game 'd4r/dll-pins.txt'))) {
|
||||
throw 'dll-pins.txt was not staged; Steam player mode cannot verify its NVIDIA DLLs.'
|
||||
}
|
||||
if (!(Test-Path -LiteralPath (Join-Path $game 'd4r/nvapi/backend/nvapi64.dll'))) {
|
||||
throw 'Raw ZLUDA NVAPI backend is missing from d4r/nvapi/backend.'
|
||||
}
|
||||
if ((Get-FileHash -LiteralPath (Join-Path $game 'nvngx_dlss.dll')).Hash -ne $before['nvngx_dlss.dll']) { throw 'Original application DLSS DLL was changed.' }
|
||||
$saved=Get-Content -LiteralPath (Join-Path $PackageRoot 'work/settings.json') -Raw | ConvertFrom-Json
|
||||
if ($saved.game -ne $exe -or $saved.architecture -ne 'gfx1201') { throw 'GPU detection or remembered selection failed on this RX9070XT fixture.' }
|
||||
# Restore uses remembered game selection and needs neither HIP nor NVIDIA DLLs.
|
||||
Invoke-Child 'restore' @('-Action','restore')
|
||||
foreach ($name in $before.Keys) { if ((Get-FileHash -LiteralPath (Join-Path $game $name)).Hash -ne $before[$name]) { throw 'Original game bytes not restored.' } }
|
||||
if (Test-Path -LiteralPath (Join-Path $game 'd4r')) { throw 'Runtime directory remains after restore.' }
|
||||
$results+=@{case='auto-gfx1201-install-remember-restore'; passed=$true; gpuKernelsExecuted=$false}
|
||||
$manifest=Get-Content -LiteralPath (Join-Path $PackageRoot 'files/package.json') -Raw | ConvertFrom-Json
|
||||
if ($manifest.bundledNvidia) {
|
||||
# Bundled originals take precedence over stale remembered shim selections;
|
||||
# explicit overrides still go through the existing strict negative gates.
|
||||
$exe=Fixture 'bundled auto selection'
|
||||
$saved.ngxCore=Join-Path $PackageRoot 'files/targets/gfx1201/d4r/_nvngx.dll'
|
||||
$saved.dlssDll=$saved.ngxCore
|
||||
$saved | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $PackageRoot 'work/settings.json') -Encoding UTF8
|
||||
Invoke-Child 'bundled-install' @('-Action','install','-GameExe',$exe)
|
||||
$game=Split-Path $exe
|
||||
foreach ($pair in @(@{path='d4r/ngx/_nvngx.dll'; expected=$NgxCore},@{path='d4r/dlss/nvngx_dlss.dll'; expected=$DlssDll})) {
|
||||
if ((Get-FileHash -LiteralPath (Join-Path $game $pair.path)).Hash -ne (Get-FileHash -LiteralPath $pair.expected).Hash) { throw 'Bundled original DLL selection failed.' }
|
||||
}
|
||||
$report=Get-ChildItem -LiteralPath (Join-Path $PackageRoot 'results') -Directory | Sort-Object Name -Descending | Select-Object -First 1
|
||||
$status=Get-Content -LiteralPath (Join-Path $report.FullName 'quick-test.json') -Raw | ConvertFrom-Json
|
||||
if ($status.nvidiaDllSource.core -ne 'bundled' -or $status.nvidiaDllSource.dlss -ne 'bundled') { throw 'Bundled DLL provenance is missing.' }
|
||||
Invoke-Child 'bundled-restore' @('-Action','restore','-GameExe',$exe)
|
||||
if (Test-Path -LiteralPath (Join-Path $game 'd4r')) { throw 'Bundled runtime directory remains after restore.' }
|
||||
$results+=@{case='bundled-auto-selection-ignores-stale-choices'; passed=$true; gpuKernelsExecuted=$false}
|
||||
$core=Join-Path (Join-Path $PackageRoot 'files') $manifest.bundledNvidia.ngxCore
|
||||
$original=[IO.File]::ReadAllBytes($core)
|
||||
try {
|
||||
[IO.File]::AppendAllText($core,'tampered bundled fixture')
|
||||
$exe=Fixture 'tampered bundled core'
|
||||
Invoke-Child 'tampered-bundled-core' @('-Action','install','-GameExe',$exe) 1
|
||||
$game=Split-Path $exe
|
||||
if ((Test-Path -LiteralPath (Join-Path $game '.d4r-backup')) -or (Test-Path -LiteralPath (Join-Path $game 'dxgi.dll'))) { throw 'Tampered bundled DLL changed game files.' }
|
||||
$results+=@{case='tampered-bundled-core'; passed=$true; rejectedBeforeGameChanges=$true; gpuKernelsExecuted=$false}
|
||||
} finally { [IO.File]::WriteAllBytes($core,$original) }
|
||||
}
|
||||
# A harmless child refuses attached debuggers and produces an empty stderr.
|
||||
# The default launcher must allow it to exit and must report no DLSS session.
|
||||
$exe=Join-Path (Join-Path $OutputDirectory 'launcher-fixture') 'Fixture.exe'
|
||||
New-Item -ItemType Directory -Force (Split-Path $exe) | Out-Null
|
||||
Add-Type -TypeDefinition @'
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
public static class D4RLauncherFixture {
|
||||
[DllImport("kernel32.dll")] private static extern bool IsDebuggerPresent();
|
||||
public static int Main(string[] args) { return IsDebuggerPresent() ? 91 : 0; }
|
||||
}
|
||||
'@ -OutputAssembly $exe -OutputType ConsoleApplication
|
||||
Invoke-Child 'run-no-debug' @('-Action','run','-GameExe',$exe,'-NgxCore',$NgxCore,'-DlssDll',$DlssDll) 1
|
||||
$report=Get-ChildItem -LiteralPath (Join-Path $PackageRoot 'results') -Directory | Sort-Object Name -Descending | Select-Object -First 1
|
||||
$status=Get-Content -LiteralPath (Join-Path $report.FullName 'quick-test.json') -Raw | ConvertFrom-Json
|
||||
$environment=Get-Content -LiteralPath (Join-Path $report.FullName 'environment.json') -Raw | ConvertFrom-Json
|
||||
if ($environment.attachedDebugger -or $status.hardwareValidationCompleted -or
|
||||
$status.status -ne 'inconclusive-process-exited-without-dlss-frames' -or $status.gameSummary.exitCode -ne '0x0' -or
|
||||
(Get-Item -LiteralPath (Join-Path $report.FullName 'd4r.stderr.log')).Length -ne 0) {
|
||||
throw 'Default launch attaches a debugger, mishandles empty stderr or falsely validates a launcher exit.'
|
||||
}
|
||||
Invoke-Child 'restore-launcher-fixture' @('-Action','restore','-GameExe',$exe)
|
||||
$results+=@{case='run-no-debug-empty-stderr-no-false-pass'; passed=$true; gpuKernelsExecuted=$false}
|
||||
foreach ($case in @('wrong-core','wrong-dlss','vkd3d')) {
|
||||
$exe=Fixture $case; $game=Split-Path $exe
|
||||
$core=if ($case -eq 'wrong-core') { Join-Path $PackageRoot 'files/targets/gfx1201/d4r/_nvngx.dll' } else { $NgxCore }
|
||||
$dlss=if ($case -eq 'wrong-dlss') { $NgxCore } else { $DlssDll }
|
||||
if ($case -eq 'vkd3d') { Set-Content -LiteralPath (Join-Path $game 'd3d12.dll') 'vkd3d-proton fixture; do not load' -Encoding ASCII }
|
||||
Invoke-Child $case @('-Action','install','-GameExe',$exe,'-NgxCore',$core,'-DlssDll',$dlss) 1
|
||||
if ((Test-Path -LiteralPath (Join-Path $game '.d4r-backup')) -or (Test-Path -LiteralPath (Join-Path $game 'dxgi.dll'))) { throw "$case changed game files before rejection." }
|
||||
$message=Get-Content -LiteralPath (Join-Path $OutputDirectory "$case.log") -Raw
|
||||
$match=[regex]::Match($message,'SEND THIS ONE FILE: (.+\.zip)')
|
||||
if (!$match.Success -or !(Test-Path -LiteralPath $match.Groups[1].Value.Trim())) { throw "$case did not produce its single report ZIP." }
|
||||
$results+=@{case=$case; passed=$true; rejectedBeforeGameChanges=$true; gpuKernelsExecuted=$false}
|
||||
}
|
||||
$readme=Join-Path $PackageRoot 'READ-ME-FIRST.txt'
|
||||
$original=[IO.File]::ReadAllBytes($readme)
|
||||
try {
|
||||
Add-Content -LiteralPath $readme 'tampered fixture'
|
||||
$exe=Fixture 'tampered-package'
|
||||
Invoke-Child 'tampered-package' @('-Action','install','-GameExe',$exe,'-NgxCore',$NgxCore,'-DlssDll',$DlssDll) 1
|
||||
$game=Split-Path $exe
|
||||
if ((Test-Path -LiteralPath (Join-Path $game '.d4r-backup')) -or (Test-Path -LiteralPath (Join-Path $game 'dxgi.dll'))) { throw 'Tampered package changed game files.' }
|
||||
$results+=@{case='tampered-package'; passed=$true; rejectedBeforeGameChanges=$true; gpuKernelsExecuted=$false}
|
||||
} finally { [IO.File]::WriteAllBytes($readme,$original) }
|
||||
$results | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Write-Host "PASS QUICK_TEST_FIXTURES cases=$($results.Count) real_game_untouched=1 Report: $OutputDirectory"
|
||||
@@ -0,0 +1,439 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$HipRoot = $env:HIP_PATH,
|
||||
[ValidateSet('stable', 'therock')][string]$RuntimeProfile = 'stable',
|
||||
[string]$ZludaRoot,
|
||||
[string]$PackageRoot,
|
||||
[string]$GpuArch,
|
||||
[string]$OutputDirectory,
|
||||
[string]$NgxCore = $env:D4R_NGX_CORE,
|
||||
[string]$DlssDll = $env:D4R_DLSS_DLL,
|
||||
[ValidateSet('init', 'evaluate', 'd3d12')][string]$NgxMode = 'init',
|
||||
[ValidateSet('driver','sdk','project','project-legacy')][string]$NgxAbi = 'driver',
|
||||
[string]$OptiScalerDll,
|
||||
[ValidateSet(5, 11, 12, 13)][int]$Preset = 11,
|
||||
[ValidateSet(0,11)][int]$NgxCreateFlags = 0,
|
||||
[string]$NgxOutputResolution = '512x288',
|
||||
[string]$NgxInputResolution,
|
||||
[switch]$NgxOnly,
|
||||
[switch]$Trace,
|
||||
[switch]$RequireNativeNetwork,
|
||||
[switch]$NativeFp8,
|
||||
[switch]$CommandListBackend,
|
||||
[switch]$CaptureExceptions,
|
||||
[switch]$EarlyIndirectProbe,
|
||||
[ValidateSet('baseline','packed','unorm','depth-stencil')][string]$PixelProfile = 'baseline',
|
||||
[ValidateSet('legacy','enhanced','inherited-legacy','inherited-enhanced')][string]$BarrierMode = 'legacy',
|
||||
[int]$Iterations = 32,
|
||||
[int]$TimeoutSeconds = 180
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repoRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if ($RuntimeProfile -eq 'therock') {
|
||||
# Works both from scripts/windows and the installed dist/<profile> directory.
|
||||
if (!$PSBoundParameters.ContainsKey('HipRoot')) { $HipRoot = Join-Path $repoRoot '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$PackageRoot) {
|
||||
if (Test-Path (Join-Path $PSScriptRoot 'bin/d4r_hip_gfx1201_probe.exe')) { $PackageRoot = $PSScriptRoot }
|
||||
else { $PackageRoot = Join-Path $repoRoot 'dist/windows-rdna4-therock' }
|
||||
}
|
||||
}
|
||||
if (!$PackageRoot) {
|
||||
if (Test-Path (Join-Path $PSScriptRoot 'bin/d4r_hip_gfx1201_probe.exe')) { $PackageRoot = $PSScriptRoot }
|
||||
else { $PackageRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../../dist/windows-rdna4-diagnostics')) }
|
||||
}
|
||||
$PackageRoot = [IO.Path]::GetFullPath($PackageRoot)
|
||||
$GpuArch=Get-D4RGpuTarget $PackageRoot $GpuArch
|
||||
if ($NativeFp8 -and ($GpuArch -notmatch '^gfx12' -or $Preset -notin @(12,13))) {
|
||||
throw '-NativeFp8 requires a gfx12 GPU target and preset L or M.'
|
||||
}
|
||||
if ($NativeFp8) {
|
||||
if (!$env:D4R_ZLUDA_NATIVE_DIR) { throw '-NativeFp8 requires D4R_ZLUDA_NATIVE_DIR for the matching native M bundle.' }
|
||||
$nativeInfoPath=Join-Path $env:D4R_ZLUDA_NATIVE_DIR 'build-info.json'
|
||||
if (Test-Path -LiteralPath $nativeInfoPath) {
|
||||
$nativeInfo=Get-Content -LiteralPath $nativeInfoPath -Raw | ConvertFrom-Json
|
||||
if ($nativeInfo.architecture -ne $GpuArch -or !$nativeInfo.nativeFp8 -or !$nativeInfo.tokenLanes) {
|
||||
throw '-NativeFp8 bundle metadata does not match this gfx12 token-lane test.'
|
||||
}
|
||||
} else {
|
||||
$marker=Join-Path $env:D4R_ZLUDA_NATIVE_DIR 'd4r-native-fp8.txt'
|
||||
$markerLines=if (Test-Path -LiteralPath $marker) { Get-Content -LiteralPath $marker } else { @() }
|
||||
if ($markerLines -notcontains 'D4R_NATIVE_FP8 1' -or $markerLines -notcontains "architecture=$GpuArch" -or
|
||||
$markerLines -notcontains 'family=M' -or $markerLines -notcontains 'tokenLanes=true') {
|
||||
throw '-NativeFp8 bundle lacks the matching native-FP8 marker.'
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$HipRoot) { $HipRoot = 'C:\Program Files\AMD\ROCm\7.2' }
|
||||
$HipRoot = [IO.Path]::GetFullPath($HipRoot)
|
||||
if (!$ZludaRoot) {
|
||||
$builtZluda = Join-Path $repoRoot 'dist/zluda-windows-final'
|
||||
$localZluda = [IO.Path]::GetFullPath((Join-Path $PackageRoot '../../.tools/zluda/zluda'))
|
||||
if (Test-Path (Join-Path $builtZluda 'build-info.json')) { $ZludaRoot = $builtZluda }
|
||||
elseif (Test-Path (Join-Path $localZluda 'nvcuda.dll')) { $ZludaRoot = $localZluda }
|
||||
else { throw 'Pass -ZludaRoot with the directory containing the Windows ZLUDA nvcuda.dll.' }
|
||||
}
|
||||
$ZludaRoot = [IO.Path]::GetFullPath($ZludaRoot)
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $PackageRoot ('results/' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
$OutputDirectory = [IO.Path]::GetFullPath($OutputDirectory)
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$originalPath = $env:PATH
|
||||
$originalHip = $env:HIP_PATH
|
||||
$originalDiag = $env:D4R_DIAG_DIR
|
||||
$originalLog = $env:ZLUDA_LOG_DIR
|
||||
$originalCuda = $env:ZLUDA_CUDA_LIB
|
||||
$originalNvapi = $env:D4R_NVAPI_BACKEND
|
||||
$originalPythonPath = $env:PYTHONPATH
|
||||
$originalVerbose = $env:D4R_ZLUDA_VERBOSE
|
||||
$originalCache = $env:ZLUDA_CACHE_DIR
|
||||
$originalCodegen = @{}
|
||||
foreach ($setting in @('D4R_ZLUDA_WMMA','D4R_ZLUDA_WMMA_FP8','D4R_ZLUDA_WMMA_FP8_NATIVE','D4R_ZLUDA_WMMA_F16_REFERENCE')) {
|
||||
$originalCodegen[$setting] = [Environment]::GetEnvironmentVariable($setting,'Process')
|
||||
}
|
||||
$exitStatus = 1
|
||||
$ngxRuntimeDirectory = $null
|
||||
$summary = [ordered]@{utc=[DateTime]::UtcNow.ToString('o'); profile=$RuntimeProfile; architecture=$GpuArch; hipRoot=$HipRoot; zludaRoot=$ZludaRoot; tests=@()}
|
||||
function Quote-Argument([string]$Value) {
|
||||
# All generated paths are absolute file/directory paths, with no trailing backslash.
|
||||
if ($Value.Contains('"')) { throw 'Double quotes are not allowed in arguments' }
|
||||
return '"' + $Value.TrimEnd('\') + '"'
|
||||
}
|
||||
function Invoke-Probe([string]$Name, [string]$Exe, [string[]]$Arguments) {
|
||||
if (!(Test-Path -LiteralPath $Exe)) { throw "Executable missing: $Exe" }
|
||||
if ($CaptureExceptions) {
|
||||
$Arguments = @('--output-directory', $OutputDirectory, '--', $Exe) + $Arguments
|
||||
$Exe = Join-Path $PackageRoot 'bin/d4r_debug_launcher.exe'
|
||||
if (!(Test-Path -LiteralPath $Exe)) { throw "Debugger launcher missing: $Exe" }
|
||||
}
|
||||
$stdout = Join-Path $OutputDirectory "$Name.stdout.log"
|
||||
$stderr = Join-Path $OutputDirectory "$Name.stderr.log"
|
||||
$argumentLine = ($Arguments | ForEach-Object { Quote-Argument $_ }) -join ' '
|
||||
$info = New-Object System.Diagnostics.ProcessStartInfo
|
||||
$info.FileName = $Exe
|
||||
$info.Arguments = $argumentLine
|
||||
$info.UseShellExecute = $false
|
||||
$info.CreateNoWindow = $true
|
||||
$info.RedirectStandardOutput = $true
|
||||
$info.RedirectStandardError = $true
|
||||
# Some PowerShell/.NET hosts restore a removed variable as an empty value.
|
||||
# These task switches/paths use an empty value to mean unset, whereas Rust
|
||||
# presence-based diagnostic flags would otherwise enable profiling.
|
||||
foreach ($key in @($info.EnvironmentVariables.Keys)) {
|
||||
if ($key.StartsWith('D4R_') -and [string]::IsNullOrWhiteSpace($info.EnvironmentVariables[$key])) {
|
||||
$info.EnvironmentVariables.Remove($key)
|
||||
}
|
||||
}
|
||||
# Disabled WGP has the ordinary CU semantics. Omit a disabled/empty switch
|
||||
# from child environments so older ZLUDA builds keep their existing cache.
|
||||
if ([Environment]::GetEnvironmentVariable('D4R_ZLUDA_WGP','Process') -ne '1') {
|
||||
$info.EnvironmentVariables.Remove('D4R_ZLUDA_WGP')
|
||||
}
|
||||
$process = New-Object System.Diagnostics.Process
|
||||
$process.StartInfo = $info
|
||||
if (!$process.Start()) { throw "Process start failed: $Exe" }
|
||||
# Consume both streams concurrently; a verbose JIT trace must not block its child.
|
||||
$outFile = [IO.File]::Open($stdout, [IO.FileMode]::Create, [IO.FileAccess]::Write, [IO.FileShare]::Read)
|
||||
$errFile = [IO.File]::Open($stderr, [IO.FileMode]::Create, [IO.FileAccess]::Write, [IO.FileShare]::Read)
|
||||
$outTask = $process.StandardOutput.BaseStream.CopyToAsync($outFile)
|
||||
$errTask = $process.StandardError.BaseStream.CopyToAsync($errFile)
|
||||
$finished = $process.WaitForExit($TimeoutSeconds * 1000)
|
||||
if (!$finished) { $process.Kill(); $process.WaitForExit() }
|
||||
$code = $process.ExitCode
|
||||
try {
|
||||
$null = $outTask.GetAwaiter().GetResult()
|
||||
$null = $errTask.GetAwaiter().GetResult()
|
||||
} finally { $outFile.Dispose(); $errFile.Dispose() }
|
||||
$process.Dispose()
|
||||
$codeHex = if ($null -eq $code) { 'unknown' } else {
|
||||
'0x{0:x8}' -f [BitConverter]::ToUInt32([BitConverter]::GetBytes([int]$code), 0)
|
||||
}
|
||||
$pass = $finished -and $code -eq 0 -and (Select-String -LiteralPath $stdout -Pattern '^PASS ' -Quiet)
|
||||
$summary.tests += [ordered]@{name=$Name; passed=$pass; timedOut=(!$finished); exitCode=$code; exceptionOrExitHex=$codeHex}
|
||||
Write-Host "$Name passed=$pass exit=$codeHex timeout=$(!$finished)"
|
||||
return $pass
|
||||
}
|
||||
try {
|
||||
if (!$env:ZLUDA_CACHE_DIR) { $env:ZLUDA_CACHE_DIR = Join-Path $repoRoot 'build/zluda-cache-windows' }
|
||||
$summary.zludaCacheDirectory = $env:ZLUDA_CACHE_DIR
|
||||
foreach ($setting in @('D4R_ZLUDA_WMMA','D4R_ZLUDA_WMMA_FP8','D4R_ZLUDA_WMMA_FP8_NATIVE','D4R_ZLUDA_WMMA_F16_REFERENCE')) {
|
||||
if ($setting -eq 'D4R_ZLUDA_WMMA_FP8_NATIVE' -and $NativeFp8) {
|
||||
[Environment]::SetEnvironmentVariable($setting, '1', 'Process')
|
||||
} elseif ([string]::IsNullOrWhiteSpace($originalCodegen[$setting])) {
|
||||
[Environment]::SetEnvironmentVariable($setting, $(if ($setting -eq 'D4R_ZLUDA_WMMA_FP8_NATIVE') { '0' } else { '1' }), 'Process')
|
||||
}
|
||||
}
|
||||
$summary.codegen = @{}
|
||||
foreach ($setting in $originalCodegen.Keys) { $summary.codegen[$setting] = [Environment]::GetEnvironmentVariable($setting,'Process') }
|
||||
Write-Host "ZLUDA codegen: WMMA=$env:D4R_ZLUDA_WMMA FP8 widening=$env:D4R_ZLUDA_WMMA_FP8 native FP8=$env:D4R_ZLUDA_WMMA_FP8_NATIVE f16 reference=$env:D4R_ZLUDA_WMMA_F16_REFERENCE"
|
||||
if ($NgxOnly -and (!$NgxCore -or !$DlssDll)) { throw '-NgxOnly requires both locally supplied NVIDIA DLL paths.' }
|
||||
if ($RequireNativeNetwork) {
|
||||
if ($NgxMode -notin @('evaluate','d3d12') -or $Preset -notin @(11,12,13) -or !$env:D4R_ZLUDA_NATIVE_DIR) {
|
||||
throw '-RequireNativeNetwork requires K/L/M Evaluate and D4R_ZLUDA_NATIVE_DIR; preset E has no native network kernels.'
|
||||
}
|
||||
$env:D4R_ZLUDA_VERBOSE = '1'
|
||||
}
|
||||
$summary.foundationDiagnosticsPerformed = !$NgxOnly
|
||||
if ($Iterations -lt 1 -or $Iterations -gt 10000) { throw 'Iterations must be 1..10000' }
|
||||
if ($TimeoutSeconds -lt 1) { throw 'TimeoutSeconds must be positive' }
|
||||
$env:HIP_PATH = $HipRoot
|
||||
$env:PATH = "$(Join-Path $HipRoot 'bin');$ZludaRoot;$originalPath"
|
||||
$env:D4R_DIAG_DIR = $OutputDirectory
|
||||
$env:ZLUDA_LOG_DIR = Join-Path $OutputDirectory 'zluda-trace'
|
||||
$env:ZLUDA_CUDA_LIB = Join-Path $ZludaRoot 'nvcuda.dll'
|
||||
$env:D4R_NVAPI_BACKEND = Join-Path $ZludaRoot 'nvapi64.dll'
|
||||
$localPythonVendor = Join-Path $repoRoot '.tools/python/vendor'
|
||||
if (Test-Path (Join-Path $localPythonVendor 'numpy/__init__.py')) {
|
||||
$env:PYTHONPATH = "$localPythonVendor;$originalPythonPath"
|
||||
}
|
||||
$comgr = Join-Path $HipRoot 'bin/amd_comgr_3.dll'
|
||||
if (!(Test-Path -LiteralPath $comgr)) { $comgr = Join-Path $HipRoot 'bin/amd_comgr.dll' }
|
||||
$files = @((Join-Path $HipRoot 'bin/amdhip64_7.dll'), $comgr,
|
||||
(Join-Path $ZludaRoot 'nvcuda.dll'), (Join-Path $PackageRoot "bin/probe_${GpuArch}.hsaco"),
|
||||
(Join-Path $PackageRoot "bin/wmma_${GpuArch}.hsaco"),
|
||||
(Join-Path $PackageRoot "experimental/k/dltss_pwin_enc1_layer_${GpuArch}.hsaco"),
|
||||
(Join-Path $PackageRoot "experimental/k/dltss_pwin_enc2_layer_${GpuArch}.hsaco"))
|
||||
if ($OptiScalerDll) {
|
||||
if ($NgxMode -ne 'd3d12' -or !$CommandListBackend) { throw '-OptiScalerDll requires D3D12 and -CommandListBackend' }
|
||||
if (![IO.Path]::IsPathRooted($OptiScalerDll)) { throw '-OptiScalerDll must be absolute' }
|
||||
$files += $OptiScalerDll
|
||||
}
|
||||
if ($NgxCore -or $DlssDll) {
|
||||
if (!$NgxCore -or !$DlssDll) { throw 'Supply both -NgxCore and -DlssDll absolute paths.' }
|
||||
if (![IO.Path]::IsPathRooted($NgxCore) -or ![IO.Path]::IsPathRooted($DlssDll)) {
|
||||
throw 'NVIDIA DLL paths must be absolute.'
|
||||
}
|
||||
$files += @($NgxCore, $DlssDll)
|
||||
}
|
||||
$summary.files = @($files | ForEach-Object {
|
||||
if (!(Test-Path -LiteralPath $_)) { throw "Required file missing: $_" }
|
||||
$file = Get-Item -LiteralPath $_
|
||||
[ordered]@{path=$file.FullName; version=$file.VersionInfo.FileVersion;
|
||||
sha256=(Get-FileHash -LiteralPath $_ -Algorithm SHA256).Hash}
|
||||
})
|
||||
try {
|
||||
$summary.displayDrivers = @(Get-CimInstance Win32_VideoController |
|
||||
Select-Object Name,DriverVersion,DriverDate,PNPDeviceID)
|
||||
$summary.os = Get-CimInstance Win32_OperatingSystem | Select-Object Caption,Version,BuildNumber
|
||||
} catch { $summary.inventoryError = $_.Exception.Message }
|
||||
$bin = Join-Path $PackageRoot 'bin'
|
||||
$hipOk = $true
|
||||
if (!$NgxOnly) { $hipOk = Invoke-Probe 'hip' (Join-Path $bin 'd4r_hip_gfx1201_probe.exe') @(
|
||||
'--hip-root', $HipRoot, '--module', (Join-Path $bin "probe_${GpuArch}.hsaco"), '--iterations', "$Iterations")
|
||||
}
|
||||
if ($hipOk) {
|
||||
if (!$NgxOnly) {
|
||||
$wmmaOk = Invoke-Probe 'gfx12-wmma' (Join-Path $bin 'd4r_gfx12_wmma_probe.exe') @(
|
||||
'--hip-root', $HipRoot, '--module', (Join-Path $bin "wmma_${GpuArch}.hsaco"), '--iterations', "$Iterations")
|
||||
if (!$wmmaOk) { throw 'gfx12 WMMA layout validation failed; refusing to mark K/M readiness.' }
|
||||
$pythonCommand = Get-Command python -ErrorAction SilentlyContinue
|
||||
if (!$pythonCommand) { throw 'Python 3.11+ with NumPy 2.4.6 is needed for K reference checks; run setup-windows-tools.ps1.' }
|
||||
foreach ($layer in @('enc1', 'enc2')) {
|
||||
$kFixture = Join-Path $OutputDirectory "k-$layer-fixture"
|
||||
$kModuleOk = Invoke-Probe "k-$layer-gpu" (Join-Path $bin 'd4r_k_module_probe.exe') @(
|
||||
'--hip-root', $HipRoot,
|
||||
'--module', (Join-Path $PackageRoot "experimental/k/dltss_pwin_${layer}_layer_${GpuArch}.hsaco"),
|
||||
'--kernel-name', $layer, '--fixture-dir', $kFixture, '--iterations', "$Iterations")
|
||||
if (!$kModuleOk) { throw "K $layer GPU execution failed; see the k-$layer-gpu logs." }
|
||||
$kReferenceOk = Invoke-Probe "k-$layer-reference" $pythonCommand.Source @(
|
||||
(Join-Path $PackageRoot 'k_layer_validate.py'), '--kernel-name', $layer, '--fixture-dir', $kFixture)
|
||||
if (!$kReferenceOk) { throw "K $layer output differs from pwin_model.py; see the k-$layer-reference logs." }
|
||||
}
|
||||
}
|
||||
$cudaOk = $true
|
||||
if (!$NgxOnly) {
|
||||
foreach ($context in @('primary', 'created')) {
|
||||
$ok = Invoke-Probe "cuda-$context" (Join-Path $bin 'd4r_cuda_driver_probe.exe') @(
|
||||
'--hip-root', $HipRoot, '--cuda-dll', (Join-Path $ZludaRoot 'nvcuda.dll'),
|
||||
'--context', $context, '--iterations', "$Iterations")
|
||||
$cudaOk = $cudaOk -and $ok
|
||||
# On failure collect the upstream trace automatically if available.
|
||||
if (!$ok -and (Test-Path (Join-Path $ZludaRoot 'trace/nvcuda.dll'))) {
|
||||
Invoke-Probe "cuda-$context-trace" (Join-Path $bin 'd4r_cuda_driver_probe.exe') @(
|
||||
'--hip-root', $HipRoot, '--cuda-dll', (Join-Path $ZludaRoot 'trace/nvcuda.dll'),
|
||||
'--context', $context, '--iterations', '1') | Out-Null
|
||||
}
|
||||
}
|
||||
if ($cudaOk -and ($NgxMode -eq 'evaluate' -or (Test-Path (Join-Path $ZludaRoot 'build-info.json')))) {
|
||||
$cudaOk = Invoke-Probe 'cuda-images' (Join-Path $bin 'd4r_cuda_image_probe.exe') @(
|
||||
'--hip-root', $HipRoot, '--cuda-dll', (Join-Path $ZludaRoot 'nvcuda.dll'), '--iterations', "$Iterations")
|
||||
if ($cudaOk) {
|
||||
$previousF16 = $env:D4R_ZLUDA_WMMA_F16_REFERENCE
|
||||
try {
|
||||
$env:D4R_ZLUDA_WMMA_F16_REFERENCE = '1'
|
||||
$cudaOk = Invoke-Probe 'cuda-f16-mma-reference' (Join-Path $bin 'd4r_cuda_mma_probe.exe') @(
|
||||
'--hip-root', $HipRoot, '--cuda-dll', (Join-Path $ZludaRoot 'nvcuda.dll'))
|
||||
} finally { $env:D4R_ZLUDA_WMMA_F16_REFERENCE = $previousF16 }
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($cudaOk) {
|
||||
$mapOk = $interopOk = $true
|
||||
if (!$NgxOnly) {
|
||||
$commandOk = Invoke-Probe 'd3d12-command-backend' (Join-Path $bin 'd4r_d3d12_command_probe.exe') @(
|
||||
'--hip-root', $HipRoot, '--module', (Join-Path $bin 'd4r_nvngx.dll'), '--interop-mode', 'indirect', '--iterations', "$Iterations")
|
||||
if (!$commandOk) { throw 'D3D12 command-list order/state/lifetime validation failed.' }
|
||||
$pixelsOk = Invoke-Probe 'd3d12-gpu-pixel-formats' (Join-Path $bin 'd4r_d3d12_pixel_probe.exe') @(
|
||||
'--hip-root', $HipRoot, '--module', (Join-Path $bin "pixel_convert_${GpuArch}.hsaco"), '--iterations', '2')
|
||||
if (!$pixelsOk) { throw 'D3D12 typed SRV/UAV format conversion validation failed.' }
|
||||
$mapOk = Invoke-Probe 'interop-map-lifetime' (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @(
|
||||
'--hip-root', $HipRoot, '--interop-mode', 'map', '--iterations', '64')
|
||||
$interopOk = Invoke-Probe 'interop-roundtrip' (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @(
|
||||
'--hip-root', $HipRoot, '--module', (Join-Path $bin "probe_${GpuArch}.hsaco"), '--iterations', "$Iterations")
|
||||
if (!$mapOk -or !$interopOk) {
|
||||
foreach ($mode in @('resource', 'import')) {
|
||||
Invoke-Probe "interop-$mode-lifetime" (Join-Path $bin 'd4r_d3d12_hip_interop_probe.exe') @(
|
||||
'--hip-root', $HipRoot, '--interop-mode', $mode, '--iterations', '32') | Out-Null
|
||||
}
|
||||
Invoke-Probe 'hip-stream-lifetime' (Join-Path $bin 'd4r_hip_stream_lifecycle_probe.exe') @(
|
||||
'--hip-root', $HipRoot, '--module', (Join-Path $bin "probe_${GpuArch}.hsaco"), '--iterations', '32') | Out-Null
|
||||
}
|
||||
}
|
||||
if ($mapOk -and $interopOk) { $exitStatus = 0 }
|
||||
if ($exitStatus -eq 0 -and $NgxCore) {
|
||||
# The driver Init ABI searches the executable directory. Keep the
|
||||
# supplied binaries in a private temporary runtime, outside dist/ZIP.
|
||||
$ngxRuntimeDirectory = Join-Path ([IO.Path]::GetTempPath()) ('d4r-ngx-' + [Guid]::NewGuid().ToString('N'))
|
||||
New-Item -ItemType Directory -Path $ngxRuntimeDirectory | Out-Null
|
||||
$ngxExe = Join-Path $ngxRuntimeDirectory 'd4r_ngx_cuda_init_probe.exe'
|
||||
$localCore = Join-Path $ngxRuntimeDirectory '_nvngx.dll'
|
||||
$localDlss = Join-Path $ngxRuntimeDirectory 'dlss/nvngx_dlss.dll'
|
||||
New-Item -ItemType Directory -Force (Split-Path -Parent $localDlss) | Out-Null
|
||||
$probeName = if ($NgxMode -eq 'd3d12') { 'd4r_d3d12_dlss_probe.exe' } else { 'd4r_ngx_cuda_init_probe.exe' }
|
||||
Copy-Item -LiteralPath (Join-Path $bin $probeName) -Destination $ngxExe
|
||||
Copy-Item -LiteralPath $NgxCore -Destination $localCore
|
||||
Copy-Item -LiteralPath $DlssDll -Destination $localDlss
|
||||
$selectedCuda = if ($Trace) { Join-Path $ZludaRoot 'trace/nvcuda.dll' } else { Join-Path $ZludaRoot 'nvcuda.dll' }
|
||||
if (!(Test-Path -LiteralPath $selectedCuda)) { throw "CUDA runtime missing: $selectedCuda" }
|
||||
$summary.traceRequested = [bool]$Trace
|
||||
$ngxArguments = @('--hip-root', $HipRoot, '--cuda-dll', $selectedCuda,
|
||||
'--ngx-core', $localCore, '--dlss-dll', $localDlss,
|
||||
'--nvapi-dll', (Join-Path $PackageRoot 'nvapi-compat/nvapi64.dll'),
|
||||
'--ngx-mode', $NgxMode, '--ngx-abi', $NgxAbi, '--preset', "$Preset", '--iterations', "$Iterations")
|
||||
if ($NgxMode -eq 'd3d12') {
|
||||
# The driver NGX core resolves feature DLLs relative to its
|
||||
# caller module as well as the executable. Keep the shim
|
||||
# alongside the private user-supplied DLLs for this test.
|
||||
$localShim = Join-Path $ngxRuntimeDirectory 'd4r_nvngx.dll'
|
||||
Copy-Item -LiteralPath (Join-Path $bin 'd4r_nvngx.dll') -Destination $localShim
|
||||
Copy-Item -LiteralPath (Join-Path $bin "pixel_convert_${GpuArch}.hsaco") -Destination $ngxRuntimeDirectory
|
||||
if ($OptiScalerDll) {
|
||||
$bridgeDirectory = Join-Path $ngxRuntimeDirectory 'd4r'
|
||||
New-Item -ItemType Directory -Path $bridgeDirectory | Out-Null
|
||||
Copy-Item -LiteralPath $localShim -Destination (Join-Path $bridgeDirectory '_nvngx.dll')
|
||||
Copy-Item -LiteralPath (Join-Path $bin "pixel_convert_${GpuArch}.hsaco") -Destination $bridgeDirectory
|
||||
$localShim = Join-Path $ngxRuntimeDirectory 'OptiScaler.dll'
|
||||
Copy-Item -LiteralPath $OptiScalerDll -Destination $localShim
|
||||
$ini = @"
|
||||
[Upscalers]
|
||||
Dx12Upscaler=dlss
|
||||
[FrameGen]
|
||||
Enabled=false
|
||||
FGInput=nofg
|
||||
FGOutput=nofg
|
||||
[DLSS]
|
||||
Enabled=true
|
||||
AllowExternalBackend=true
|
||||
RenderPresetOverride=true
|
||||
RenderPresetForAll=$Preset
|
||||
UseGenericAppIdWithDlss=false
|
||||
[Libraries]
|
||||
NvngxPath=$bridgeDirectory
|
||||
NvngxDlssPath=$(Join-Path $ngxRuntimeDirectory 'dlss')
|
||||
NvngxFeaturePath=$(Join-Path $ngxRuntimeDirectory 'dlss')
|
||||
[Hotfix]
|
||||
RestoreComputeSignature=false
|
||||
RestoreGraphicSignature=false
|
||||
ExtendedStateRestore=false
|
||||
[Log]
|
||||
LogToFile=true
|
||||
LogLevel=0
|
||||
[Menu]
|
||||
DisableSplash=true
|
||||
"@
|
||||
$ini | Set-Content -LiteralPath (Join-Path $ngxRuntimeDirectory 'OptiScaler.ini') -Encoding UTF8
|
||||
$summary.optiScaler = @{path=$OptiScalerDll; mode='standalone'; preset=$Preset}
|
||||
$ngxArguments += @('--ngx-frontend', 'optiscaler')
|
||||
}
|
||||
$ngxArguments += @('--module', $localShim, '--pixel-profile', $PixelProfile, '--barrier-mode', $BarrierMode,
|
||||
'--ngx-create-flags', "$NgxCreateFlags", '--ngx-output-resolution', $NgxOutputResolution)
|
||||
if ($NgxInputResolution) { $ngxArguments += @('--ngx-input-resolution', $NgxInputResolution) }
|
||||
if ($EarlyIndirectProbe) {
|
||||
if (!$CommandListBackend) { throw '-EarlyIndirectProbe requires -CommandListBackend' }
|
||||
$ngxArguments += @('--early-indirect', '1')
|
||||
}
|
||||
if ($CommandListBackend) { $ngxArguments += @('--interop-mode', 'command-list') }
|
||||
}
|
||||
$ngxName = if ($NgxMode -eq 'evaluate') { "ngx-evaluate-preset-$Preset" } elseif ($NgxMode -eq 'd3d12') { "d3d12-evaluate-preset-$Preset" } else { 'ngx-init' }
|
||||
$ngxOk = Invoke-Probe $ngxName $ngxExe $ngxArguments
|
||||
if ($OptiScalerDll) {
|
||||
Get-ChildItem -LiteralPath $ngxRuntimeDirectory -Filter '*.log' -File |
|
||||
ForEach-Object { Copy-Item -LiteralPath $_.FullName -Destination (Join-Path $OutputDirectory ('optiscaler-' + $_.Name)) }
|
||||
Copy-Item -LiteralPath (Join-Path $ngxRuntimeDirectory 'OptiScaler.ini') -Destination (Join-Path $OutputDirectory 'OptiScaler.ini')
|
||||
}
|
||||
if ($ngxOk -and $RequireNativeNetwork) {
|
||||
$hits = @()
|
||||
$nativeLayers = if ($Preset -eq 11) { @('enc0','enc1','enc2','enc3','enc4','dec5','dec4','dec3','dec2','dec1','dec0') }
|
||||
else { @('enc1','enc2','enc3_tube','dec2','dec1') }
|
||||
foreach ($layer in $nativeLayers) {
|
||||
$name = if ($Preset -eq 11) { "dltss_pwin_${layer}_layer" } else { "rrlite_${layer}_4x4" }
|
||||
$pattern = '\[d4r-launch\] kernel="' + $name + '" backend=native'
|
||||
$count = @(Select-String -LiteralPath (Join-Path $OutputDirectory "$ngxName.stderr.log") -Pattern $pattern).Count
|
||||
$hits += @{layer=$layer; nativeLaunches=$count; expectedFrames=$Iterations}
|
||||
if ($count -lt $Iterations) { $ngxOk=$false }
|
||||
}
|
||||
$summary.nativeTransformer = $hits
|
||||
$summary.nativeTransformerPassed = $ngxOk
|
||||
Write-Host "Native preset $Preset transformer launches validated=$ngxOk"
|
||||
}
|
||||
if (!$ngxOk) {
|
||||
$exitStatus = 1
|
||||
if (!$Trace -and (Test-Path (Join-Path $ZludaRoot 'trace/nvcuda.dll'))) {
|
||||
$traceArguments = @(
|
||||
'--hip-root', $HipRoot, '--cuda-dll', (Join-Path $ZludaRoot 'trace/nvcuda.dll'),
|
||||
'--ngx-core', $localCore, '--dlss-dll', $localDlss,
|
||||
'--nvapi-dll', (Join-Path $PackageRoot 'nvapi-compat/nvapi64.dll'),
|
||||
'--ngx-mode', $NgxMode, '--preset', "$Preset", '--iterations', '1')
|
||||
if ($NgxMode -eq 'd3d12') { $traceArguments += @('--module', $localShim) }
|
||||
Invoke-Probe "$ngxName-trace" $ngxExe $traceArguments | Out-Null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
$summary.error = $_.Exception.Message
|
||||
Write-Warning $summary.error
|
||||
} finally {
|
||||
if ($ngxRuntimeDirectory -and (Test-Path -LiteralPath $ngxRuntimeDirectory)) {
|
||||
$resolvedRuntime = [IO.Path]::GetFullPath($ngxRuntimeDirectory)
|
||||
$tempRoot = [IO.Path]::GetFullPath([IO.Path]::GetTempPath()).TrimEnd('\') + '\'
|
||||
if (!$resolvedRuntime.StartsWith($tempRoot, [StringComparison]::OrdinalIgnoreCase) -or
|
||||
[IO.Path]::GetFileName($resolvedRuntime) -notmatch '^d4r-ngx-[0-9a-f]{32}$') {
|
||||
throw 'Refusing to remove a path outside the private NGX temporary runtime.'
|
||||
}
|
||||
# A scanner can briefly hold the just-unloaded DLL open. Cleanup must
|
||||
# not skip environment restoration or discard the completed GPU logs.
|
||||
$cleanupError=$null
|
||||
foreach ($attempt in 1..10) {
|
||||
try { Remove-Item -LiteralPath $resolvedRuntime -Recurse -Force -ErrorAction Stop; $cleanupError=$null; break }
|
||||
catch { $cleanupError=$_.Exception.Message; if ($attempt -lt 10) { Start-Sleep -Milliseconds 300 } }
|
||||
}
|
||||
if ($cleanupError) { $summary.cleanupError=$cleanupError; $exitStatus=1; Write-Warning "Private runtime cleanup failed: $cleanupError" }
|
||||
}
|
||||
$summary.passed = $exitStatus -eq 0
|
||||
$summary | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
$env:PATH = $originalPath
|
||||
$env:HIP_PATH = $originalHip
|
||||
$env:D4R_DIAG_DIR = $originalDiag
|
||||
$env:ZLUDA_LOG_DIR = $originalLog
|
||||
$env:ZLUDA_CUDA_LIB = $originalCuda
|
||||
$env:D4R_NVAPI_BACKEND = $originalNvapi
|
||||
$env:PYTHONPATH = $originalPythonPath
|
||||
$env:D4R_ZLUDA_VERBOSE = $originalVerbose
|
||||
$env:ZLUDA_CACHE_DIR = $originalCache
|
||||
foreach ($setting in $originalCodegen.Keys) { [Environment]::SetEnvironmentVariable($setting, $originalCodegen[$setting], 'Process') }
|
||||
$archive = "$OutputDirectory.zip"
|
||||
Compress-Archive -LiteralPath $OutputDirectory -DestinationPath $archive -Force
|
||||
Write-Host "Diagnostic bundle: $archive"
|
||||
}
|
||||
exit $exitStatus
|
||||
@@ -0,0 +1,111 @@
|
||||
# Drag-in release packaging with synthetic per-target inputs. No GPU, no
|
||||
# NVIDIA DLLs, no game. Code objects carry real AMDGPU ELF target headers.
|
||||
[CmdletBinding()]
|
||||
param([string]$OutputDirectory)
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/windows-release-package/'+(Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$targets=@(Get-D4RGpuTargets)
|
||||
|
||||
function Write-CodeObject([string]$Path,[int]$Machine) {
|
||||
$header=New-Object byte[] 64
|
||||
$header[0]=127; $header[1]=69; $header[2]=76; $header[3]=70; $header[4]=2; $header[5]=1; $header[18]=224; $header[48]=$Machine
|
||||
New-Item -ItemType Directory -Force (Split-Path $Path) | Out-Null
|
||||
[IO.File]::WriteAllBytes($Path,$header)
|
||||
}
|
||||
function New-Inputs([string]$Prefix,[scriptblock]$Mutate) {
|
||||
foreach ($target in $targets) {
|
||||
$arch=$target.architecture; $root="$Prefix-$arch"
|
||||
$text=@{'OptiScaler.dll'='optiscaler'; 'd4r/_nvngx.dll'="shim $arch"; 'd4r/nvapi/nvapi64.dll'="nvapi $arch";
|
||||
'd4r/nvapi/backend/nvapi64.dll'='raw backend nvapi'; 'd4r/hip/bin/amdhip64_7.dll'='hip'; 'd4r/zluda/nvcuda.dll'='zluda'; 'd4r/zluda/nvapi64.dll'='compat nvapi'; 'licenses/d4r.txt'='license';
|
||||
'd4r/d4r_gpu_inventory.exe'='inventory'; 'd4r/d4r_debug_launcher.exe'='launcher'; 'windows-game.ps1'='installer';
|
||||
'd4r/native/d4r-kernels.txt'='manifest'}
|
||||
foreach ($entry in $text.GetEnumerator()) {
|
||||
$path=Join-Path $root $entry.Key; New-Item -ItemType Directory -Force (Split-Path $path) | Out-Null
|
||||
Set-Content -LiteralPath $path $entry.Value -Encoding ASCII
|
||||
}
|
||||
$objects=@("d4r/pixel_convert_$arch.hsaco")+@(1..16 | ForEach-Object { "d4r/native/kernel$_.hsaco" })
|
||||
foreach ($object in $objects) { Write-CodeObject (Join-Path $root $object) $target.elfMachine }
|
||||
if ($Mutate) { & $Mutate $root $arch }
|
||||
$files=@(Get-ChildItem -LiteralPath $root -Recurse -File | Where-Object { $_.Name -ne 'package.json' } | ForEach-Object {
|
||||
@{path=$_.FullName.Substring($root.Length+1).Replace('\','/'); sha256=(Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash}
|
||||
})
|
||||
@{architecture=$arch; d4rWorkingTreeDirty=$false; d4rCommit='fixture'; hardwareValidation='fixture';
|
||||
dlssSha256='3975567b8943c53acce397f2b72380092f84f162d00b0d2c7d08a1025c563983';
|
||||
zludaBuild=@{sourceCommit='fixture'}; optiScaler=@{sourceCommit='fixture'}; hipRuntime='fixture'; files=$files} |
|
||||
ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $root 'package.json') -Encoding UTF8
|
||||
}
|
||||
}
|
||||
function Invoke-Package([string]$Prefix,[string]$Root) {
|
||||
& (Join-Path $PSScriptRoot 'package-windows-release.ps1') -GamePackagePrefix $Prefix -LoaderDll $loader -PackageRoot $Root | Out-Null
|
||||
}
|
||||
$loader=Join-Path $OutputDirectory 'build/d4r_nvngx_loader.dll'
|
||||
New-Item -ItemType Directory -Force (Split-Path $loader) | Out-Null
|
||||
Set-Content -LiteralPath $loader 'loader fixture' -Encoding ASCII
|
||||
$results=@()
|
||||
|
||||
# 1. Layout of a good release.
|
||||
$prefix=Join-Path $OutputDirectory 'inputs/good/game'
|
||||
New-Inputs $prefix
|
||||
$release=Join-Path $OutputDirectory 'release'
|
||||
Invoke-Package $prefix $release
|
||||
foreach ($path in @('dxgi.dll','OptiScaler.ini','d4r/_nvngx.dll','d4r/d4r.ini','d4r/dll-pins.txt','d4r/package.json',
|
||||
'd4r/hip/bin/amdhip64_7.dll','d4r/zluda/nvcuda.dll','d4r/licenses/d4r.txt','d4r/ngx/PUT _nvngx.dll HERE.txt',
|
||||
'd4r/gpu-targets.json','d4r/d4r_gpu_inventory.exe','d4r/d4r-check.ps1','WINDOWS_README.txt')) {
|
||||
if (!(Test-Path -LiteralPath (Join-Path $release $path))) { throw "Release lacks $path" }
|
||||
}
|
||||
if ((Get-Content -LiteralPath (Join-Path $release 'd4r/_nvngx.dll') -Raw).Trim() -ne 'loader fixture') { throw 'd4r/_nvngx.dll is not the loader.' }
|
||||
foreach ($target in $targets) {
|
||||
$arch=$target.architecture
|
||||
if ((Get-Content -LiteralPath (Join-Path $release "d4r/_nvngx_$arch.dll") -Raw).Trim() -ne "shim $arch") { throw "Wrong shim for $arch" }
|
||||
if ((Get-Content -LiteralPath (Join-Path $release "d4r/nvapi/$arch/nvapi64.dll") -Raw).Trim() -ne "nvapi $arch") { throw "Wrong NVAPI for $arch" }
|
||||
Assert-D4RCodeObjectTarget (Join-Path $release "d4r/pixel_convert_$arch.hsaco") $arch
|
||||
$native=@(Get-ChildItem -LiteralPath (Join-Path $release "d4r/native/$arch") -Filter '*.hsaco')
|
||||
if ($native.Count -ne 16 -or !(Test-Path -LiteralPath (Join-Path $release "d4r/native/$arch/d4r-kernels.txt"))) { throw "Native folder for $arch is incomplete" }
|
||||
foreach ($object in $native) { Assert-D4RCodeObjectTarget $object.FullName $arch }
|
||||
}
|
||||
if ((Get-Content -LiteralPath (Join-Path $release 'd4r/nvapi/backend/nvapi64.dll') -Raw).Trim() -ne 'raw backend nvapi') { throw 'The raw ZLUDA NVAPI backend was not staged separately.' }
|
||||
if ((Get-Content -LiteralPath (Join-Path $release 'd4r/zluda/nvapi64.dll') -Raw).Trim() -ne 'compat nvapi') { throw 'The compatibility NVAPI is not colocated with nvcuda.dll.' }
|
||||
if (@(Get-ChildItem -LiteralPath $release -Recurse -Filter 'd4r_gpu_inventory.exe').Count -ne 1) { throw 'The GPU query tool must ship exactly once.' }
|
||||
foreach ($excluded in @('d4r/d4r_debug_launcher.exe','windows-game.ps1','d4r/native/kernel1.hsaco','d4r/nvapi/nvapi64.dll')) {
|
||||
if (Test-Path -LiteralPath (Join-Path $release $excluded)) { throw "Installer-only or untargeted file shipped: $excluded" }
|
||||
}
|
||||
$manifest=Get-Content -LiteralPath (Join-Path $release 'd4r/package.json') -Raw | ConvertFrom-Json
|
||||
foreach ($file in $manifest.files) {
|
||||
if ((Get-FileHash -LiteralPath (Join-Path $release $file.path) -Algorithm SHA256).Hash -ne $file.sha256) { throw "Manifest hash wrong: $($file.path)" }
|
||||
}
|
||||
$shipped=@(Get-ChildItem -LiteralPath $release -Recurse -File | ForEach-Object { $_.FullName.Substring($release.Length+1).Replace('\','/') } | Where-Object { $_ -ne 'd4r/package.json' })
|
||||
if (Compare-Object $shipped @($manifest.files.path)) { throw 'Manifest and shipped files differ.' }
|
||||
$results+=@{case='drag-in-layout'; passed=$true; targets=$targets.Count; files=$shipped.Count}
|
||||
|
||||
# 1b. A subset release carries only the requested targets.
|
||||
$subset=Join-Path $OutputDirectory 'release-subset'
|
||||
& (Join-Path $PSScriptRoot 'package-windows-release.ps1') -GamePackagePrefix $prefix -LoaderDll $loader -PackageRoot $subset -Architectures gfx1101,gfx1201 | Out-Null
|
||||
$kept=@(Get-ChildItem -LiteralPath (Join-Path $subset 'd4r') -Filter '_nvngx_gfx*.dll' | ForEach-Object { $_.Name })
|
||||
if (($kept | Sort-Object) -join ',' -ne '_nvngx_gfx1101.dll,_nvngx_gfx1201.dll') { throw "Subset shipped the wrong shims: $($kept -join ',')" }
|
||||
$rejected=$false
|
||||
try { & (Join-Path $PSScriptRoot 'package-windows-release.ps1') -GamePackagePrefix $prefix -LoaderDll $loader -PackageRoot (Join-Path $OutputDirectory 'release-unknown') -Architectures gfx9999 | Out-Null } catch { $rejected=$_.Exception.Message -match 'Unknown GPU target' }
|
||||
if (!$rejected) { throw 'An unknown architecture was not rejected.' }
|
||||
$results+=@{case='architecture-subset'; passed=$true; shipped=$kept}
|
||||
|
||||
# 2. A code object for another GPU in a target's input is rejected.
|
||||
$prefix=Join-Path $OutputDirectory 'inputs/mixed/game'
|
||||
New-Inputs $prefix { param($root,$arch) if ($arch -eq 'gfx1101') { Write-CodeObject (Join-Path $root 'd4r/native/kernel3.hsaco') 65 } }
|
||||
$rejected=$false
|
||||
try { Invoke-Package $prefix (Join-Path $OutputDirectory 'release-mixed') } catch { $rejected=$_.Exception.Message -match 'does not target gfx1101' }
|
||||
if (!$rejected) { throw 'A gfx1100 code object in the gfx1101 input was not rejected.' }
|
||||
$results+=@{case='mixed-target-rejected'; passed=$true}
|
||||
|
||||
# 3. Shared runtime files must be identical across targets.
|
||||
$prefix=Join-Path $OutputDirectory 'inputs/runtime/game'
|
||||
New-Inputs $prefix { param($root,$arch) if ($arch -eq 'gfx1200') { Set-Content -LiteralPath (Join-Path $root 'd4r/zluda/nvcuda.dll') 'other zluda' -Encoding ASCII } }
|
||||
$rejected=$false
|
||||
try { Invoke-Package $prefix (Join-Path $OutputDirectory 'release-runtime') } catch { $rejected=$_.Exception.Message -match 'Shared runtime differs' }
|
||||
if (!$rejected) { throw 'A differing shared runtime was not rejected.' }
|
||||
$results+=@{case='shared-runtime-mismatch-rejected'; passed=$true}
|
||||
|
||||
$results | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Write-Host "PASS WINDOWS_RELEASE_PACKAGE cases=$($results.Count) Report: $OutputDirectory"
|
||||
@@ -0,0 +1,24 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$HipRoot, [string]$PackageRoot, [string]$OutputDirectory)
|
||||
$ErrorActionPreference='Stop'
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$HipRoot) { $HipRoot=Join-Path $repo '.tools/therock-10.2.0a20260929/_rocm_sdk_core' }
|
||||
if (!$PackageRoot) { $PackageRoot=Join-Path $repo 'dist/windows-rdna4-command-list' }
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo ('test-results/wmma-f16-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
$compiler=Join-Path $HipRoot 'lib/llvm/bin/clang++.exe'
|
||||
if (!(Test-Path -LiteralPath $compiler)) { $compiler=Join-Path $HipRoot 'bin/clang++.exe' }
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$module=Join-Path $OutputDirectory 'wmma_f16_compare.hsaco'
|
||||
& $compiler -x hip -std=c++17 --offload-arch=gfx1201 --offload-device-only --no-gpu-bundle-output `
|
||||
-mno-wavefrontsize64 -nogpuinc -nogpulib -O3 (Join-Path $repo 'tools/windows/wmma_f16_compare.hip') -o $module
|
||||
if ($LASTEXITCODE) { throw 'WMMA arithmetic probe compilation failed.' }
|
||||
$previousPython=$env:PYTHONPATH
|
||||
try {
|
||||
$env:PYTHONPATH="$(Join-Path $repo '.tools/python/vendor');$previousPython"
|
||||
& python.exe (Join-Path $repo 'tools/windows/wmma_f16_compare.py') `
|
||||
--probe (Join-Path $PackageRoot 'bin/d4r_native_replay_probe.exe') --module $module `
|
||||
--hip-root $HipRoot --output-dir $OutputDirectory
|
||||
if ($LASTEXITCODE) { throw "WMMA arithmetic diagnostic failed; logs: $OutputDirectory" }
|
||||
} finally { $env:PYTHONPATH=$previousPython }
|
||||
# A successful diagnostic is not acceptance of native FP16 accumulators for K.
|
||||
# Read baselineEquivalent/gameOptimizationAccepted in validation.json.
|
||||
@@ -0,0 +1,67 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$ZludaRoot, [string[]]$GpuArchitectures, [string]$OutputDirectory)
|
||||
$ErrorActionPreference='Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
$repo=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
|
||||
if (!$ZludaRoot) { $ZludaRoot=Join-Path $repo 'dist/zluda-windows-final' }
|
||||
if (!$OutputDirectory) { $OutputDirectory=Join-Path $repo 'test-results/zluda-target-compilation' }
|
||||
if (!$GpuArchitectures) { $GpuArchitectures=@(Get-D4RGpuTargets | ForEach-Object { $_.architecture }) }
|
||||
foreach ($arch in $GpuArchitectures) { [void](Get-D4RGpuTargetInfo $arch) }
|
||||
$emitter=Join-Path $ZludaRoot 'd4r_emit.exe'
|
||||
if (!(Test-Path -LiteralPath $emitter)) { throw "Rebuild ZLUDA and its offline emitter: $emitter" }
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
$settings=@{PATH="$ZludaRoot;$env:PATH"; D4R_ZLUDA_WMMA='1'; D4R_ZLUDA_WMMA_FP8='1';
|
||||
D4R_ZLUDA_WMMA_F16_REFERENCE='1'; D4R_ZLUDA_WMMA_FP8_NATIVE='0'; D4R_ZLUDA_WMMA_LAYOUT=$null;
|
||||
D4R_ZLUDA_WAVE64=$null; D4R_ZLUDA_FAST_MATH=$null; D4R_ZLUDA_IGNORE_DENORMAL=$null;
|
||||
D4R_ZLUDA_WMMA_F32ACC=$null; D4R_ZLUDA_EXTRA_BC=$null; D4R_ZLUDA_WGP=$null}
|
||||
$old=@{}; $rows=[Collections.Generic.List[object]]::new()
|
||||
try {
|
||||
foreach ($key in $settings.Keys) {
|
||||
$old[$key]=[Environment]::GetEnvironmentVariable($key,'Process')
|
||||
if ($null -eq $settings[$key]) { Remove-Item -LiteralPath "Env:$key" -ErrorAction SilentlyContinue }
|
||||
else { [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') }
|
||||
}
|
||||
foreach ($fixture in @(@{name='pattern'; source='cuda_driver_probe.cpp'; reference='1'},
|
||||
@{name='f16-mma-reference'; source='cuda_mma_probe.cpp'; reference='1'},
|
||||
@{name='f16-mma-wmma'; source='cuda_mma_probe.cpp'; reference='0'})) {
|
||||
$env:D4R_ZLUDA_WMMA_F16_REFERENCE=$fixture.reference
|
||||
# Reuse the public synthetic PTX exercised by the hardware diagnostics.
|
||||
# Reading source does not execute a probe or load HIP/NVIDIA libraries.
|
||||
$match=[regex]::Match((Get-Content (Join-Path $repo ('tools/windows/'+$fixture.source)) -Raw),'(?s)R"PTX\((.*?)\)PTX"')
|
||||
if (!$match.Success) { throw 'Public PTX fixture not found.' }
|
||||
$ptx=Join-Path $OutputDirectory ($fixture.name+'.ptx')
|
||||
$match.Groups[1].Value | Set-Content -LiteralPath $ptx -Encoding ASCII
|
||||
foreach ($arch in $GpuArchitectures) {
|
||||
$output=Join-Path $OutputDirectory ($arch+'/'+$fixture.name)
|
||||
New-Item -ItemType Directory -Force $output | Out-Null
|
||||
$log=Join-Path $output 'compile.log'
|
||||
$oldPreference=$ErrorActionPreference
|
||||
try { $ErrorActionPreference='Continue'; & $emitter $ptx $output $arch *> $log; $code=$LASTEXITCODE }
|
||||
finally { $ErrorActionPreference=$oldPreference }
|
||||
if ($code) { throw "Offline ZLUDA compilation failed: $arch $($fixture.name), exit=$code; see $log" }
|
||||
Assert-D4RCodeObjectTarget (Join-Path $output 'module.hsaco') $arch
|
||||
$text=Get-Content -LiteralPath $log -Raw
|
||||
if ($text -match 'not a recognized processor|ignoring processor|unsupported processor') { throw "Target rejected by compiler: $log" }
|
||||
$asm=Get-Content (Join-Path $output 'asm.s') -Raw
|
||||
if ($asm -notmatch '\.amdhsa_wavefront_size32\s+1') { throw "Missing wave32 code generation: $output" }
|
||||
$hasWmma=($asm -match 'v_wmma_')
|
||||
if (($fixture.name -eq 'f16-mma-wmma' -or ($fixture.name -eq 'f16-mma-reference' -and $arch.StartsWith('gfx11'))) -and !$hasWmma) {
|
||||
throw "WMMA lowering missing: $output"
|
||||
}
|
||||
# gfx12 strict F16 accumulation intentionally uses the existing
|
||||
# double-precision reference helper. Do not label it native WMMA.
|
||||
if ($fixture.name -eq 'f16-mma-reference' -and $arch.StartsWith('gfx12') -and
|
||||
($hasWmma -or $asm -notmatch 'v_fma_f64')) { throw "Strict gfx12 reference lowering changed: $output" }
|
||||
$rows.Add(@{architecture=$arch; fixture=$fixture.name; compiled=$true; elfTargetCorrect=$true;
|
||||
nativeWmma=$hasWmma; hardwareExecuted=$false})
|
||||
Write-Host "PASS ZLUDA_OFFLINE target=$arch fixture=$($fixture.name) wave=32 hardware_executed=0"
|
||||
}
|
||||
}
|
||||
@{passed=$true; fixtures=@($rows); emitterSha256=(Get-FileHash -LiteralPath $emitter).Hash; gpuWorkExecuted=$false} |
|
||||
ConvertTo-Json -Depth 6 | Set-Content (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
} finally {
|
||||
foreach ($key in $old.Keys) {
|
||||
if ($null -eq $old[$key]) { Remove-Item -LiteralPath "Env:$key" -ErrorAction SilentlyContinue }
|
||||
else { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[ValidateSet('install','restore','run')][string]$Action = 'run',
|
||||
[Parameter(Mandatory=$true)][string]$GameExe,
|
||||
[string]$NgxCore, [string]$DlssDll,
|
||||
[ValidateSet(5,11,12,13)][int]$Preset = 11,
|
||||
[int]$RunSeconds = 0,
|
||||
[string]$DiagnosticResolution,
|
||||
[string]$CacheDirectory = $env:ZLUDA_CACHE_DIR,
|
||||
[string]$LocalTextureKernels,
|
||||
[switch]$ValidateOutput,
|
||||
[switch]$ProfileStages,
|
||||
[switch]$ProfileKernels,
|
||||
[switch]$ProfileKernelsDeferred,
|
||||
[switch]$ProfileLegacyStream,
|
||||
[ValidateRange(1,1000000)][int]$KernelProfileEvery = 17,
|
||||
[switch]$ProfileCommandHooks,
|
||||
[switch]$ProfileCudaApi,
|
||||
[switch]$ProfileGpuBoundary,
|
||||
[switch]$UncachedInteropLists,
|
||||
[switch]$AsyncInterop,
|
||||
[switch]$BatchInputCopies,
|
||||
[switch]$NativeFp8,
|
||||
[switch]$VerboseRuntime,
|
||||
[switch]$CaptureExceptions,
|
||||
[string[]]$GameArguments = @('-dx12'),
|
||||
[string]$OutputDirectory
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
|
||||
. (Join-Path $PSScriptRoot 'gpu-preflight.ps1')
|
||||
. (Join-Path $PSScriptRoot 'game-summary.ps1')
|
||||
if ($ProfileGpuBoundary -and !$AsyncInterop) { throw '-ProfileGpuBoundary requires -AsyncInterop.' }
|
||||
if ($ProfileKernels -and $ProfileKernelsDeferred) { throw 'Choose either serializing or deferred kernel profiling.' }
|
||||
if ($ProfileLegacyStream -and !$ProfileKernelsDeferred) { throw '-ProfileLegacyStream requires -ProfileKernelsDeferred.' }
|
||||
$package = [IO.Path]::GetFullPath($PSScriptRoot)
|
||||
$GameExe = (Get-Item -LiteralPath $GameExe -ErrorAction Stop).FullName
|
||||
$game = Split-Path $GameExe
|
||||
$backup = Join-Path $game '.d4r-backup'
|
||||
$manifestPath = Join-Path $backup 'manifest.json'
|
||||
function GamePath([string]$relative) {
|
||||
$path = [IO.Path]::GetFullPath((Join-Path $game $relative))
|
||||
if (!$path.StartsWith($game + '\', [StringComparison]::OrdinalIgnoreCase)) { throw "Path escapes game directory: $relative" }
|
||||
return $path
|
||||
}
|
||||
if (Get-Process -Name ([IO.Path]::GetFileNameWithoutExtension($GameExe)) -ErrorAction SilentlyContinue) { throw 'Close the game before installing, restoring or running this diagnostic.' }
|
||||
if ($Action -eq 'restore') {
|
||||
if (!(Test-Path -LiteralPath $manifestPath)) { throw 'No d4r installation manifest exists.' }
|
||||
$manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json
|
||||
foreach ($entry in $manifest.files) {
|
||||
$path = GamePath $entry.path
|
||||
if ((Test-Path -LiteralPath $path) -and (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash -ne $entry.installedHash) { throw "File changed since installation; preserved: $path" }
|
||||
}
|
||||
foreach ($entry in $manifest.files) {
|
||||
$path = GamePath $entry.path
|
||||
if ($entry.existed) { Copy-Item -LiteralPath (Join-Path $backup ('original/' + $entry.path)) -Destination $path -Force }
|
||||
elseif (Test-Path -LiteralPath $path) { Remove-Item -LiteralPath $path -Force }
|
||||
}
|
||||
# Remove only empty directories; preserve user files and diagnostic logs.
|
||||
if (Test-Path -LiteralPath (GamePath 'd4r')) {
|
||||
Get-ChildItem -LiteralPath (GamePath 'd4r') -Directory -Recurse | Sort-Object { $_.FullName.Length } -Descending | ForEach-Object {
|
||||
if (!(Get-ChildItem -LiteralPath $_.FullName -Force)) { Remove-Item -LiteralPath $_.FullName }
|
||||
}
|
||||
if (!(Get-ChildItem -LiteralPath (GamePath 'd4r') -Force)) { Remove-Item -LiteralPath (GamePath 'd4r') }
|
||||
}
|
||||
Move-Item -LiteralPath $manifestPath -Destination (Join-Path $backup ('restored-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff') + '.json'))
|
||||
Write-Host "Original game files restored. Backups retained: $backup"
|
||||
return
|
||||
}
|
||||
if (!$NgxCore -or !$DlssDll) { throw 'Pass both local NVIDIA DLL paths with -NgxCore and -DlssDll.' }
|
||||
$NgxCore = (Get-Item -LiteralPath $NgxCore).FullName
|
||||
$DlssDll = (Get-Item -LiteralPath $DlssDll).FullName
|
||||
$metadata = Get-Content -LiteralPath (Join-Path $package 'package.json') -Raw | ConvertFrom-Json
|
||||
$GpuArch=Get-D4RGpuTarget $package $metadata.architecture
|
||||
if ($NativeFp8 -and ($GpuArch -notmatch '^gfx12' -or $Preset -notin @(12,13) -or !$metadata.nativeFp8M)) {
|
||||
throw '-NativeFp8 needs a gfx12 package built with native-FP8 M kernels and preset L or M.'
|
||||
}
|
||||
foreach ($file in $metadata.files) {
|
||||
if ($file.path.EndsWith('.hsaco')) { Assert-D4RCodeObjectTarget (Join-Path $package $file.path) $GpuArch }
|
||||
if ((Get-FileHash -LiteralPath (Join-Path $package $file.path) -Algorithm SHA256).Hash -ne $file.sha256) { throw "Package file changed; rebuild package: $($file.path)" }
|
||||
}
|
||||
if ((Get-FileHash -LiteralPath $DlssDll -Algorithm SHA256).Hash -ne $metadata.dlssSha256) { throw 'DLSS DLL does not match the validated 310.9.1 native manifest. Rebuild and validate native manifests for this DLL first.' }
|
||||
Invoke-D4RGpuPreflight $package $game $GpuArch (Join-Path $package ('results/preflight-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')))
|
||||
$textureFiles=@(); $textureManifest=@()
|
||||
if ($LocalTextureKernels) {
|
||||
$LocalTextureKernels=(Get-Item -LiteralPath $LocalTextureKernels -ErrorAction Stop).FullName
|
||||
$validation=Get-Content -LiteralPath (Join-Path $LocalTextureKernels 'validation.json') -Raw | ConvertFrom-Json
|
||||
if (!$validation.passed -or !$validation.strictRgb -or $validation.architecture -ne $GpuArch -or
|
||||
$validation.source.dlssSha256 -ne $metadata.dlssSha256 -or !$validation.source.accuracy) {
|
||||
throw 'Local texture kernels must pass test-native-texture.ps1 with the exact packaged DLSS identity.'
|
||||
}
|
||||
foreach ($object in $validation.source.objects.PSObject.Properties) {
|
||||
if ($object.Name -notin @('hiluma_engine_output_depthinv_mvlo_hdr_max_v2_rel.hsaco','hiluma_engine_output_depthreg_mvhi_ldr_max_v2_rel.hsaco')) {
|
||||
throw "Unvalidated local texture variant: $($object.Name)"
|
||||
}
|
||||
$path=Join-Path $LocalTextureKernels $object.Name
|
||||
if ((Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash.ToLowerInvariant() -ne $object.Value) { throw 'Local texture object changed after validation.' }
|
||||
Assert-D4RCodeObjectTarget $path $GpuArch
|
||||
$textureFiles+=Get-Item -LiteralPath $path
|
||||
}
|
||||
if ($textureFiles.Count -ne 2) { throw 'Expected both validated local K output variants.' }
|
||||
# Use the manifest covered by validation, rather than a mutable extra file.
|
||||
foreach ($line in Get-Content -LiteralPath (Join-Path $LocalTextureKernels 'd4r-kernels.txt')) {
|
||||
if (!$line.Trim() -or $line.StartsWith('#')) { continue }
|
||||
if ($line -notmatch '^([A-Za-z0-9_]+) ([0-9a-f]{16})$' -or ($Matches[1]+'.hsaco') -notin @($textureFiles.Name)) { throw 'Invalid local texture manifest.' }
|
||||
$textureManifest+=$line
|
||||
}
|
||||
if ($textureManifest.Count -ne 2) { throw 'Expected two local texture manifest identities.' }
|
||||
if ((Get-FileHash -LiteralPath (Join-Path $LocalTextureKernels 'd4r-kernels.txt') -Algorithm SHA256).Hash.ToLowerInvariant() -ne $validation.manifestSha256) { throw 'Local texture manifest changed after validation.' }
|
||||
}
|
||||
$manifest = if (Test-Path -LiteralPath $manifestPath) { Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json } else { [pscustomobject]@{game=$GameExe; files=@()} }
|
||||
if ($manifest.game -ne $GameExe) { throw 'Backup manifest belongs to another executable.' }
|
||||
if (!(Test-Path -LiteralPath $manifestPath) -and (Test-Path -LiteralPath (GamePath 'd4r'))) { throw 'An existing d4r directory has no installation manifest; preserved.' }
|
||||
New-Item -ItemType Directory -Force $backup | Out-Null
|
||||
function InstallFile([string]$source, [string]$relative) {
|
||||
$relative=$relative.Replace('/','\')
|
||||
$path = GamePath $relative
|
||||
$entry = @($manifest.files | Where-Object { $_.path.Replace('/','\') -eq $relative }) | Select-Object -First 1
|
||||
if ($entry) {
|
||||
if ((Test-Path -LiteralPath $path) -and (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash -ne $entry.installedHash) { throw "Installed file changed; preserved: $path" }
|
||||
} else {
|
||||
$existed = Test-Path -LiteralPath $path
|
||||
if ($existed) {
|
||||
$original = Join-Path $backup ('original/' + $relative)
|
||||
New-Item -ItemType Directory -Force (Split-Path $original) | Out-Null
|
||||
Copy-Item -LiteralPath $path -Destination $original
|
||||
}
|
||||
$entry = [pscustomobject]@{path=$relative; existed=[bool]$existed; installedHash=''}
|
||||
$manifest.files = @($manifest.files) + @($entry)
|
||||
}
|
||||
New-Item -ItemType Directory -Force (Split-Path $path) | Out-Null
|
||||
Copy-Item -LiteralPath $source -Destination $path -Force
|
||||
$entry.installedHash = (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash
|
||||
$manifest | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath $manifestPath -Encoding UTF8
|
||||
}
|
||||
InstallFile (Join-Path $package 'OptiScaler.dll') 'dxgi.dll'
|
||||
foreach ($file in $metadata.files) {
|
||||
$relative=$file.path.Replace('/','\')
|
||||
if ($relative.StartsWith('d4r\',[StringComparison]::OrdinalIgnoreCase)) {
|
||||
InstallFile (Join-Path $package $relative) $relative
|
||||
}
|
||||
}
|
||||
if ($LocalTextureKernels) {
|
||||
foreach ($file in $textureFiles) { InstallFile $file.FullName ('d4r/native/' + $file.Name) }
|
||||
$combinedManifest=Join-Path $backup 'generated-native-manifest.txt'
|
||||
@(Get-Content -LiteralPath (Join-Path $package 'd4r/native/d4r-kernels.txt')) + $textureManifest |
|
||||
Set-Content -LiteralPath $combinedManifest -Encoding ASCII
|
||||
InstallFile $combinedManifest 'd4r/native/d4r-kernels.txt'
|
||||
}
|
||||
# Private local test copies only; these are never added to the public package.
|
||||
# Keep the core at the same path used by Steam player mode and install-dlss.ps1.
|
||||
InstallFile $NgxCore 'd4r/ngx/_nvngx.dll'
|
||||
# The numeric driver Init ABI searches the calling module's directory; it does
|
||||
# not accept FeatureCommonInfo. Keep the exact local feature DLL alongside the
|
||||
# d4r shim. Putting it only beside the core in vendor/ produces FeatureNotFound.
|
||||
InstallFile $DlssDll 'd4r/dlss/nvngx_dlss.dll'
|
||||
$bridge = GamePath 'd4r'
|
||||
$dlssFeature = GamePath 'd4r/dlss'
|
||||
$vendor = GamePath 'd4r/vendor'
|
||||
$ini = @"
|
||||
[Upscalers]
|
||||
Dx12Upscaler=dlss
|
||||
[FrameGen]
|
||||
Enabled=false
|
||||
FGInput=nofg
|
||||
FGOutput=nofg
|
||||
[DLSS]
|
||||
Enabled=true
|
||||
AllowExternalBackend=true
|
||||
RenderPresetOverride=true
|
||||
RenderPresetForAll=$Preset
|
||||
UseGenericAppIdWithDlss=false
|
||||
[Libraries]
|
||||
NvngxPath=$bridge
|
||||
NvngxDlssPath=$dlssFeature
|
||||
NvngxFeaturePath=$dlssFeature
|
||||
[Hotfix]
|
||||
RestoreComputeSignature=false
|
||||
RestoreGraphicSignature=false
|
||||
ExtendedStateRestore=false
|
||||
[Log]
|
||||
LogToFile=true
|
||||
LogLevel=$(if ($VerboseRuntime) { 0 } else { 2 })
|
||||
[Menu]
|
||||
DisableSplash=true
|
||||
"@
|
||||
$tempIni = Join-Path $backup 'generated-OptiScaler.ini'
|
||||
$ini | Set-Content -LiteralPath $tempIni -Encoding UTF8
|
||||
InstallFile $tempIni 'OptiScaler.ini'
|
||||
if ($Action -eq 'install') { Write-Host "Installed development backend for preset $Preset. Use -Action restore to revert."; return }
|
||||
if (!$OutputDirectory) { $OutputDirectory = Join-Path $package ('results/game-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
|
||||
$OutputDirectory = [IO.Path]::GetFullPath($OutputDirectory)
|
||||
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
|
||||
if (!$CacheDirectory) { $CacheDirectory = Join-Path $package 'cache' }
|
||||
$CacheDirectory = [IO.Path]::GetFullPath($CacheDirectory)
|
||||
$NgxDataDirectory = Join-Path $CacheDirectory 'ngx'
|
||||
New-Item -ItemType Directory -Force $NgxDataDirectory | Out-Null
|
||||
$settings = @{
|
||||
D4R_HIP_ROOT=(GamePath 'd4r/hip'); HIP_PATH=(GamePath 'd4r/hip');
|
||||
D4R_NVCUDA_DLL=(GamePath 'd4r/zluda/nvcuda.dll'); ZLUDA_CUDA_LIB=(GamePath 'd4r/zluda/nvcuda.dll');
|
||||
D4R_NVAPI_DLL=(GamePath 'd4r/zluda/nvapi64.dll'); D4R_NVAPI_BACKEND=(GamePath 'd4r/nvapi/backend/nvapi64.dll');
|
||||
D4R_NGX_CORE=(GamePath 'd4r/ngx/_nvngx.dll'); D4R_DLSS_DLL=(GamePath 'd4r/dlss/nvngx_dlss.dll'); D4R_NGX_DATA_DIR=$NgxDataDirectory;
|
||||
D4R_FORMAT_MODULE=(GamePath "d4r/pixel_convert_${GpuArch}.hsaco");
|
||||
D4R_ZLUDA_NATIVE_DIR=(GamePath 'd4r/native'); D4R_D3D12_COMMAND_BACKEND='1'; D4R_ZLUDA_VERBOSE='1';
|
||||
D4R_ZLUDA_WMMA='1'; D4R_ZLUDA_WMMA_FP8='1'; D4R_ZLUDA_WMMA_FP8_NATIVE=$(if ($NativeFp8) { '1' } else { '0' }); D4R_ZLUDA_WMMA_F16_REFERENCE='1';
|
||||
# Do not inherit relaxation flags from unrelated experiments. Unset and
|
||||
# false have the same numerical effect; unset reuses the validated cache.
|
||||
D4R_ZLUDA_IGNORE_DENORMAL=$null; D4R_ZLUDA_FAST_MATH=$null;
|
||||
D4R_ZLUDA_WMMA_F32ACC=$null; D4R_ZLUDA_WAVE64=$null;
|
||||
D4R_ZLUDA_WMMA_LAYOUT=$null;
|
||||
D4R_QUIET_API=$(if ($VerboseRuntime) { $null } else { '1' });
|
||||
D4R_VALIDATE_OUTPUT=$(if ($ValidateOutput) { '1' } else { $null });
|
||||
D4R_PROFILE_STAGES=$(if ($ProfileStages) { '1' } else { $null });
|
||||
D4R_ZLUDA_PROFILE=$(if ($ProfileKernels) { '1' } else { $null });
|
||||
D4R_ZLUDA_PROFILE_DEFERRED=$(if ($ProfileKernelsDeferred) { '1' } else { $null });
|
||||
D4R_ZLUDA_PROFILE_ALLOW_LEGACY=$(if ($ProfileLegacyStream) { '1' } else { $null });
|
||||
D4R_ZLUDA_PROFILE_EVERY=$(if ($ProfileKernelsDeferred) { "$KernelProfileEvery" } else { $null });
|
||||
D4R_PROFILE_COMMAND_HOOKS=$(if ($ProfileCommandHooks) { '1' } else { $null });
|
||||
D4R_ZLUDA_PROFILE_API=$(if ($ProfileCudaApi) { '1' } else { $null });
|
||||
D4R_PROFILE_GPU_BOUNDARY=$(if ($ProfileGpuBoundary) { '1' } else { $null });
|
||||
D4R_DISABLE_INTEROP_LIST_CACHE=$(if ($UncachedInteropLists) { '1' } else { $null });
|
||||
D4R_ASYNC_INTEROP=$(if ($AsyncInterop) { '1' } else { $null });
|
||||
D4R_BATCH_INPUT_COPIES=$(if ($BatchInputCopies) { '1' } else { $null });
|
||||
D4R_DIAG_DIR=$OutputDirectory; ZLUDA_LOG_DIR=(Join-Path $OutputDirectory 'zluda-trace');
|
||||
ZLUDA_CACHE_DIR=$CacheDirectory; PATH=((GamePath 'd4r/hip/bin') + ';' + (GamePath 'd4r/zluda') + ';' + $env:PATH)
|
||||
}
|
||||
$old = @{}; $process = $null; $originalSettings = @()
|
||||
$outFile = $null; $errFile = $null; $outTask = $null; $errTask = $null
|
||||
try {
|
||||
if ($DiagnosticResolution) {
|
||||
if ([IO.Path]::GetFileName($GameExe) -ne 'SHProto-Win64-Shipping.exe' -or $DiagnosticResolution -notmatch '^([0-9]{3,4})x([0-9]{3,4})$') { throw 'DiagnosticResolution currently supports Silent Hill 2 only; use e.g. 1280x720.' }
|
||||
$renderWidth=[int]$Matches[1]; $renderHeight=[int]$Matches[2]
|
||||
if ($renderWidth -gt 4096 -or $renderHeight -gt 2160) { throw 'Diagnostic resolution is too large.' }
|
||||
$userRoot=Join-Path $env:LOCALAPPDATA 'SilentHill2/Saved'
|
||||
foreach ($relative in @('Config/Windows/GameUserSettings.ini','SaveGames/GFXSettings.SHProto-Win64-Shipping.xml')) {
|
||||
$path=Join-Path $userRoot $relative
|
||||
$saved=Join-Path $OutputDirectory ('original-settings/' + $relative)
|
||||
New-Item -ItemType Directory -Force (Split-Path $saved) | Out-Null
|
||||
Copy-Item -LiteralPath $path -Destination $saved
|
||||
$originalSettings += @{path=$path; saved=$saved}
|
||||
if ($relative.EndsWith('.ini')) {
|
||||
$text=[IO.File]::ReadAllText($path)
|
||||
$text=$text -replace 'Resolution=\(X=[0-9]+,Y=[0-9]+\)', "Resolution=(X=$renderWidth,Y=$renderHeight)"
|
||||
$text=$text -replace 'ScreenMode=Borderless', 'ScreenMode=Windowed'
|
||||
$text=$text -replace '(?m)^ResolutionSizeX=[0-9]+', "ResolutionSizeX=$renderWidth"
|
||||
$text=$text -replace '(?m)^ResolutionSizeY=[0-9]+', "ResolutionSizeY=$renderHeight"
|
||||
$text=$text -replace '(?m)^FullscreenMode=[0-9]+', 'FullscreenMode=2'
|
||||
[IO.File]::WriteAllText($path,$text,[Text.UTF8Encoding]::new($false))
|
||||
} else {
|
||||
$xml=[xml](Get-Content -LiteralPath $path -Raw -Encoding Unicode)
|
||||
$xml.GSA_SDK.GAMESETTINGS.RESOLUTION.Width="$renderWidth"
|
||||
$xml.GSA_SDK.GAMESETTINGS.RESOLUTION.Height="$renderHeight"
|
||||
$xml.Save($path)
|
||||
}
|
||||
}
|
||||
Write-Host "Temporary diagnostic resolution: $DiagnosticResolution; original settings restore at exit."
|
||||
}
|
||||
foreach ($key in $settings.Keys) { $old[$key] = [Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') }
|
||||
@{preset=$Preset; game=$GameExe; package=$metadata; attachedDebugger=[bool]$CaptureExceptions;
|
||||
diagnostics=@{asyncInterop=[bool]$AsyncInterop; batchInputCopies=[bool]$BatchInputCopies; validateOutput=[bool]$ValidateOutput;
|
||||
profileGpuBoundary=[bool]$ProfileGpuBoundary; profileStages=[bool]$ProfileStages; profileCudaApi=[bool]$ProfileCudaApi;
|
||||
profileKernels=[bool]$ProfileKernels; profileKernelsDeferred=[bool]$ProfileKernelsDeferred;
|
||||
profileLegacyStream=[bool]$ProfileLegacyStream; kernelProfileEvery=$KernelProfileEvery};
|
||||
driver=@(Get-CimInstance Win32_VideoController | Select-Object Name,DriverVersion,DriverDate); arguments=$GameArguments} |
|
||||
ConvertTo-Json -Depth 12 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'environment.json') -Encoding UTF8
|
||||
function Quote([string]$value) { if ($value.Contains('"')) { throw 'A command argument contains an unsupported quote.' }; return '"' + $value + '"' }
|
||||
$arguments = @('--output-directory', (Quote $OutputDirectory))
|
||||
if (!$CaptureExceptions) { $arguments += '--no-debug' }
|
||||
$arguments += @('--', (Quote $GameExe)) + @($GameArguments | ForEach-Object { Quote $_ })
|
||||
# Drain raw bytes concurrently. PowerShell's line-oriented redirection can
|
||||
# throttle a verbose game and distort the very timings we are collecting.
|
||||
$info = [Diagnostics.ProcessStartInfo]::new()
|
||||
$info.FileName = GamePath 'd4r/d4r_debug_launcher.exe'
|
||||
$info.Arguments = $arguments -join ' '
|
||||
$info.WorkingDirectory = $game
|
||||
$info.UseShellExecute = $false; $info.CreateNoWindow = $true
|
||||
$info.RedirectStandardOutput = $true; $info.RedirectStandardError = $true
|
||||
# Empty task switches mean unset. Some PowerShell/.NET hosts preserve empty
|
||||
# values, which would enable Rust's presence-based diagnostic flags.
|
||||
foreach ($key in @($info.EnvironmentVariables.Keys)) {
|
||||
if ($key.StartsWith('D4R_') -and [string]::IsNullOrWhiteSpace($info.EnvironmentVariables[$key])) {
|
||||
$info.EnvironmentVariables.Remove($key)
|
||||
}
|
||||
}
|
||||
if ([Environment]::GetEnvironmentVariable('D4R_ZLUDA_WGP','Process') -ne '1') {
|
||||
$info.EnvironmentVariables.Remove('D4R_ZLUDA_WGP')
|
||||
}
|
||||
$process = [Diagnostics.Process]::new(); $process.StartInfo = $info
|
||||
if (!$process.Start()) { throw 'Game diagnostic launch failed.' }
|
||||
$outFile = [IO.File]::Open((Join-Path $OutputDirectory 'd4r.stdout.log'), [IO.FileMode]::Create, [IO.FileAccess]::Write, [IO.FileShare]::Read)
|
||||
$errFile = [IO.File]::Open((Join-Path $OutputDirectory 'd4r.stderr.log'), [IO.FileMode]::Create, [IO.FileAccess]::Write, [IO.FileShare]::Read)
|
||||
$outTask = $process.StandardOutput.BaseStream.CopyToAsync($outFile)
|
||||
$errTask = $process.StandardError.BaseStream.CopyToAsync($errFile)
|
||||
Write-Host "Game diagnostic started (preset $Preset). Logs: $OutputDirectory"
|
||||
$timer = [Diagnostics.Stopwatch]::StartNew()
|
||||
while (!$process.WaitForExit(1000)) {
|
||||
if ($RunSeconds -gt 0 -and $timer.Elapsed.TotalSeconds -ge $RunSeconds) { $process.Kill(); $process.WaitForExit(); break }
|
||||
}
|
||||
$process.WaitForExit()
|
||||
[void]$outTask.GetAwaiter().GetResult(); [void]$errTask.GetAwaiter().GetResult()
|
||||
$outFile.Dispose(); $outFile = $null; $errFile.Dispose(); $errFile = $null
|
||||
foreach ($file in Get-ChildItem -LiteralPath $game -Filter 'OptiScaler*.log' -File) { Copy-Item -LiteralPath $file.FullName -Destination $OutputDirectory -Force }
|
||||
$stderr = [IO.File]::ReadAllText((Join-Path $OutputDirectory 'd4r.stderr.log'))
|
||||
$stdout = [IO.File]::ReadAllText((Join-Path $OutputDirectory 'd4r.stdout.log'))
|
||||
$debugger = [IO.File]::ReadAllText((Join-Path $OutputDirectory 'debugger.log'))
|
||||
$gameExit = [regex]::Match($debugger, 'EXIT code=0x([0-9a-f]+)')
|
||||
$gameCrash = $null
|
||||
if ([IO.Path]::GetFileName($GameExe) -eq 'SHProto-Win64-Shipping.exe' -and $gameExit.Success -and $gameExit.Groups[1].Value -ne '0') {
|
||||
$crashRoot = Join-Path $env:LOCALAPPDATA 'SilentHill2/Saved/Crashes'
|
||||
$latest = Get-ChildItem -LiteralPath $crashRoot -Directory -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.LastWriteTime -ge (Get-Date).AddSeconds(-$timer.Elapsed.TotalSeconds-5) } |
|
||||
Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||
if ($latest) {
|
||||
Copy-Item -LiteralPath $latest.FullName -Destination (Join-Path $OutputDirectory 'ue-crash') -Recurse
|
||||
[xml]$context = Get-Content -LiteralPath (Join-Path $latest.FullName 'CrashContext.runtime-xml') -Raw
|
||||
$gameCrash = @{type=[string]$context.FGenericCrashContext.RuntimeProperties.CrashType;
|
||||
reason=[string]$context.FGenericCrashContext.RuntimeProperties.ErrorMessage}
|
||||
}
|
||||
}
|
||||
Get-D4RGameSummary -Stdout $stdout -Stderr $stderr -Debugger $debugger -Preset $Preset `
|
||||
-RunSeconds $timer.Elapsed.TotalSeconds -ValidateOutput ([bool]$ValidateOutput) -GameCrash $gameCrash |
|
||||
ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
|
||||
Compress-Archive -LiteralPath $OutputDirectory -DestinationPath ($OutputDirectory + '.zip') -Force
|
||||
Write-Host "Game diagnostic bundle: $OutputDirectory.zip"
|
||||
} finally {
|
||||
if ($process -and !$process.HasExited) { $process.Kill(); $process.WaitForExit() }
|
||||
if ($outTask) { try { [void]$outTask.GetAwaiter().GetResult() } catch {} }
|
||||
if ($errTask) { try { [void]$errTask.GetAwaiter().GetResult() } catch {} }
|
||||
if ($outFile) { $outFile.Dispose() }; if ($errFile) { $errFile.Dispose() }
|
||||
if ($process) { $process.Dispose() }
|
||||
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
|
||||
foreach ($entry in $originalSettings) { Copy-Item -LiteralPath $entry.saved -Destination $entry.path -Force }
|
||||
}
|
||||
Reference in new issue
Block a user