[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" }