network natively on HIP with Vulkan->HIP shared memory, extend K kernels to
gfx11 (RDNA3), and add dependency/packaging/test tooling around them.
Windows: FSR RR backend, native HIP K runner, gfx11 K kernels, tooling
FSR Ray Regeneration (experimental)
- Add fsr_rr_backend.{h,cpp}, fsr_rr_passes.h and the fsr_rr/ prepare and
compose compute shaders. They run AMD's FidelityFX denoiser
(amd_fidelityfx_denoiser_dx12.dll, SDK 2.3, not bundled) on a private
vkd3d-proton device.
- Add the amdxc64 shim (tools/windows/amdxc64_shim) and two vkd3d-proton
patches for libcxx includes. AMD's amdxc64.dll is never loaded.
- d4r_nvngx_windows: RrFeature now selects between the native DLSS RR
backend and the FSR backend (D4R_RR_BACKEND=fsr). After the denoiser it
runs an internal Super Sampling stage (feature 1) to upscale. If SR is
unavailable and output != render size, the frame fails; it is never
returned noisy.
- d4r.ini / player_config: new [RayReconstruction] FsrDenoiser,
FsrStabilityBias (auto or >= 0) and FsrDetail (0-1), with validation, and
matching D4R_FSR_* environment variables.
- Add the FSR RR fixture, output-check and ngx test scripts, and a shader
compile script.
Native HIP K runner (Windows)
- CMake: add d4r_engine_k_hip (d4r-k-hip) plus separate volk/engine copies,
because VK_USE_PLATFORM_WIN32_KHR changes volk's entry points.
- Share Vulkan memory with HIP via Win32 opaque handles on Windows
(VK_KHR_external_memory_win32) and via fds on Linux. Handle types are now
intptr_t, and the platform choice is in D4R_EXT_MEMORY_* macros.
- HipNetwork: add debugRepeatHashes and debugNodeMicros, exposed as
D4R_HIP_REPEAT_CHECK, D4R_HIP_REPEAT_ALL and D4R_HIP_NODE_PROFILE in
replay.cpp.
- replay.cpp: display-resolution-motion frames (FrameFlagDisplayMotion) no
longer require near2rnd == out2rnd. Both values must still be in (0, 1].
K kernels on gfx11
- Allow position-token and enc1 tiling on __GFX11__ as well as gfx12.
- Add an opt-in ds_bpermute cross-half exchange
(D4R_WINDOWS_GFX11_DS_EXCHANGE, enabled for K on Windows gfx11). M keeps
its LDS path.
- Fix a block-barrier race in the position layer. Without POS_VSHARE a fast
wave could overwrite its hb rows while a slower wave still read them for a
later head, giving rare wrong results on gfx1101 (enc0 outputs differing
between identical launches).
Packaging and install
- install.bat, d4r.ini, WINDOWS_README: FSR option, and
install_dependencies.py to install pinned NVIDIA dependencies with staged,
hash-verified replacement and rollback.
- Update the release package, local package refresh and test scripts.
- Add the InteropBatchInputCopies option (default false). Batching is faster
on some synthetic runs, but player temporal exactness is unresolved.
Tooling and tests
- K model: capture_k_weights, build_k_model, compare_k_outputs,
benchmark_k_hip, build_capture_runtime, test_k_model and the
zluda-k-output-capture patch. Captured weights and models are private
local assets and must not enter release archives.
- Synthetic probes: test_m_ngx, test_sr_dynamic,
prepare_rr_dynamic_fixture and the d3d12/rr_ngx probe updates.
- Add tests/windows/test_install_dependencies.py.
Docs
- Update the windows-fsr-rr, k-performance, local-package, rr-interop and
rr-model-cache docs. Add windows-parity-handoff.md and
windows-parity-continuation-prompt.md.
303 lines
20 KiB
PowerShell
303 lines
20 KiB
PowerShell
# 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 -eq 'gfx1101') { Note 'RX 7700 XT has passed native Windows synthetic checks and limited game-frame tests; broad game validation remains incomplete' }
|
|
elseif ($a -ne 'gfx1201') { Note "$a is compile-tested; no native Windows hardware validation is recorded for this target" }
|
|
}
|
|
}
|
|
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; 'interop.batchinputcopies' = 1; 'debug.validateoutput' = 1; 'debug.log' = 1; 'paths.cachedir' = 1; 'paths.nativedir' = 1; 'kernels.nativefp8' = 1; 'rayreconstruction.backend' = 1; 'rayreconstruction.modeldir' = 1 }
|
|
$boolean = @('dlss.showwatermark', 'interop.asyncinterop', 'interop.batchinputcopies', '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' }
|
|
elseif (@('e', '5') -contains $v) { $model = 'E' }
|
|
elseif (@('l', '12') -contains $v) { $model = 'L' }
|
|
else { Note "d4r.ini: [DLSS] Model '$value' is not available on Windows (use E, K, L 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 'rayreconstruction.backend') {
|
|
if ($v -notin @('', 'auto', 'off', 'native', 'fsr')) { Note 'd4r.ini: [RayReconstruction] Backend must be off, native or fsr; off will be used'; $iniProblems++ }
|
|
elseif ($v -eq 'fsr') { Note 'Native Windows FSR RR is unavailable; RR requests will be rejected' }
|
|
}
|
|
elseif ($name -eq 'paths.nativedir' -and $v -notin @('', 'auto')) {
|
|
$privateNative=if([IO.Path]::IsPathRooted($value)){$value}else{Join-Path $d4r $value}
|
|
if($arch -and (Test-Path -LiteralPath (Join-Path $privateNative $arch))){$privateNative=Join-Path $privateNative $arch}
|
|
$privateObjects=@(Get-ChildItem -LiteralPath $privateNative -Filter '*.hsaco' -ErrorAction SilentlyContinue)
|
|
if(!$privateObjects.Count -or !(Test-Path -LiteralPath (Join-Path $privateNative 'd4r-kernels.txt'))){
|
|
Bad '[Paths] NativeDir must contain the complete native kernel set and d4r-kernels.txt';$iniProblems++
|
|
}else{
|
|
foreach($requiredNative in @(Get-ChildItem -LiteralPath (Join-Path $d4r "native\$arch") -Filter '*.hsaco' -ErrorAction SilentlyContinue)){
|
|
if(!(Test-Path -LiteralPath (Join-Path $privateNative $requiredNative.Name))){
|
|
Bad "NativeDir is incomplete; missing $($requiredNative.Name)";$iniProblems++
|
|
}
|
|
}
|
|
$privateTarget=@($registry | Where-Object architecture -eq $arch) | Select-Object -First 1
|
|
foreach($object in $privateObjects){
|
|
$stream=[IO.File]::OpenRead($object.FullName)
|
|
try{$header=New-Object byte[] 64;$read=$stream.Read($header,0,64)}finally{$stream.Dispose()}
|
|
if($privateTarget -and ($read -ne 64 -or $header[0] -ne 127 -or $header[1] -ne 69 -or
|
|
$header[2] -ne 76 -or $header[3] -ne 70 -or $header[48] -ne $privateTarget.elfMachine)){
|
|
Bad "NativeDir $($object.Name) is not built for $arch";$iniProblems++
|
|
}
|
|
}
|
|
if(!$iniProblems){Ok "private native kernel directory: $privateNative"}
|
|
}
|
|
}
|
|
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 }
|