0.1.5 shipped one d4r/zluda/nvapi64.dll (the gfx1101 build) for all targets, and compat_nvapi_path() preferred it over d4r/nvapi/<arch>/. Nothing on Windows ever set D4R_GPU_ARCH, so on any other GPU NVAPI failed with "No selected gfx1101 device", NGX CUDA init failed, and DLSS stayed unavailable (black frames, no OptiScaler upscaler section). - Select the NVAPI per architecture; never fall back to a shared DLL in a multi-target folder, and report a missing build by name. - The loader and runtime publish D4R_GPU_ARCH and D4R_GPU_LUID for the game's adapter, so NVAPI picks the right device with several GPUs. - nvapi64.dll exports the target it was built for; the runtime refuses a mismatch, and the packager and d4r-check read the same marker. - Release packaging no longer ships d4r/zluda/nvapi64.dll and rejects NVAPI builds for the wrong target, without a marker, or missing. - Move target resolution, device matching and loader choice into gpu_selection.h; regression tests cover all 11 targets, bad or missing D4R_GPU_ARCH, mixed installs, multi-GPU and missing files. - d4r-check: accept the FSR keys in the shipped d4r.ini.
160 lines
11 KiB
PowerShell
160 lines
11 KiB
PowerShell
[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"
|
|
Assert-D4RNvapiTarget (Join-Path $DiagnosticRoot 'nvapi-compat/nvapi64.dll') $GpuArch
|
|
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"
|
|
}
|