Windows: DLSS watermark off, check script, 310.7.0 pair, shorter docs

- Set Disable.Watermark on the game path (the Linux shim already does);
  [DLSS] ShowWatermark = true keeps NVIDIA's stamp.

- Always write d4r\d4r_nvngx.log in player mode. It was skipped when the
  launcher gave the game a valid stderr pipe, e.g. Steam.

- Accept NVIDIA's 310.7.0 + _nvngx 32.0.15.9636 as "less tested" pins.
  Native kernels are still gated by PTX identity, so a different DLL
  falls back to translated kernels.

- Add d4r-check.ps1 (ok/MISSING/note lines with a fix for each; -Report
  writes a bug-report ZIP) with 15 fixture tests.

- package-windows-release.ps1: -Architectures subset, ships the check
  script, GPU registry and one GPU-query tool.

- Add a shorter WINDOWS_README.txt; trim README's Windows section and
  merge in the RX 9060 XT / distributor-DLL notes.

- Diagnostic probe accepts preset 12 (L).

Tested on RX 7700 XT without a game: loader and shim start-up, K and M
synthetic NGX frames with both DLL pairs (finite output, native kernels
used). Not tested in a game.
This commit is contained in:
ayo committed 2026-10-03 04:58:05 -04:00
1 parent df415ec828
commit 774efac524
12 files changed
+517 -68

No files matched your search

+3 -46
View File
@@ -8,54 +8,11 @@ Preset L is NVIDIA's DLSS 4.5 model for Ultra Performance, especially at 4K. Set
**Proof of concept:** d4r shows that DLSS can run on an AMD GPU, but it is not really that practical for everyday use yet. It has been tested on one GPU in a handful of games and depends on unreleased patches to ZLUDA and vkd3d-proton. **Proof of concept:** d4r shows that DLSS can run on an AMD GPU, but it is not really that practical for everyday use yet. It has been tested on one GPU in a handful of games and depends on unreleased patches to ZLUDA and vkd3d-proton.
**Native Windows development:** an experimental D3D12 backend runs K and M **Windows (experimental):** d4r also runs natively on Windows 11, in D3D12 games, with no Wine or Proton. It supports the DLSS 4 (K) and DLSS 4.5 (M) models. Installing works like the Linux release: extract the ZIP into the game folder, copy in two NVIDIA files, and edit `d4r\d4r.ini`. See [the Windows guide](packaging/windows/WINDOWS_README.txt); `d4r\d4r-check.ps1` checks an install and writes a bug-report ZIP.
transformer networks on Windows 11 / RX 9070 XT (gfx1201), using patched
ZLUDA/HIP and VRAM buffers/shared fences directly. Inputs and output belong to
the current frame; this path does not use Wine, Proton or CPU image staging.
The Windows backend, command-list hooks and diagnostics live in `tools/windows`.
All twenty Windows diagnostic gates pass, and queued-frame/feature-recreation K
outputs match synchronous controls exactly. A five-minute K/4K Silent Hill 2
run completes 16941 finite frames without backend errors. Performance remains
under development; asynchronous interop is opt-in and requires one queue.
M retains the FP16-equivalent baseline; native FP8 has not established a
full-network speedup. Windows preset L is not validated. See
[Windows build and validation](docs/windows-rdna4-port.md),
[OptiScaler installation](docs/windows-game.md) and
[Windows measurements](docs/windows-performance.md). NVIDIA DLLs are supplied
locally and excluded from packages.
Experimental Windows builds also cover RDNA3 `gfx1100..gfx1103`, RDNA3.5 Only the Radeon RX 9070 XT (gfx1201) has been tested on real hardware here. Its backend checks pass for K and M in Silent Hill 2 and Cyberpunk 2077 at 4K; those runs are correctness checks, not FPS benchmarks, and Windows performance is still under development. Community logs from an RX 9060 XT (gfx1200) show native K/M execution and finite output in Cyberpunk and Dawnwalker, but texture/LOD and motion artifacts are still reported, so that is backend coverage and not completed image-quality validation. The other targets (RDNA3 `gfx1100..gfx1103`, RDNA3.5 `gfx1150..gfx1154`) are compile-tested only. Windows preset E and L are not available. NVIDIA DLLs are supplied by you and are never part of the repository; the quick launcher can use a distributor-supplied DLL pair, but that packaging support does not establish redistribution permission.
`gfx1150..gfx1154` and RDNA4 `gfx1200/gfx1201`, with separate guarded packages.
Only RX 9070 XT has physical validation here; other targets are compile-tested.
Community RX 9060 XT / gfx1200 logs now show native K/M execution and finite
output in Cyberpunk and Dawnwalker. Visual issues are still reported, so this
is backend coverage rather than completed image-quality validation.
The installer checks the real HIP target before changing game files. See
[Windows GPU coverage and issue #10 diagnostics](docs/windows-gpu-support.md).
For a short Windows hardware test, the fork's More: [Windows GPU coverage](docs/windows-gpu-support.md), [measurements](docs/windows-performance.md), [build and validation notes](docs/windows-rdna4-port.md), and the [diagnostic launcher](docs/windows-game.md) and [quick test](docs/windows-quick-test.txt) used for bug reports.
[quick-test prereleases](https://github.com/xdfnx-dev/d4r/releases) provide one
ZIP that detects the GPU automatically. Extract it and double-click START-K.cmd
or START-M.cmd, select your game and local NVIDIA DLLs, then send the single
diagnostic ZIP after exiting. RESTORE-GAME.cmd restores replaced files. Read
[the short English instructions](docs/windows-quick-test.txt). Corresponding
sources are a separate release asset; the test requires no SDK installation.
The updated quick test launches without an attached debugger, stages the
locally supplied DLSS DLL beside the shim for numeric CUDA NGX initialization,
and reports a launcher exit with zero frames as inconclusive. Source fixes
also serialize OptiScaler device-hook installation and forward numeric init
errors. These fixes pass standalone direct/OptiScaler K and OptiScaler M on
RX 9070 XT. Cyberpunk 2077 2.31 also reaches saved 4K gameplay with K and M:
13,145 K session frames and 5,268 M frames pass GPU output checks, every
required native transformer layer executes, and no backend failures,
previous-frame outputs or CPU image copies occur. Sessions include menus and
loading; this synchronous diagnostic is not an FPS benchmark. Other GPUs,
including the reporter's RX 9060 XT / gfx1200, required further testing at that point.
The subsequent RX 9060 XT retest passes all required native-layer/output
checks in Cyberpunk and Dawnwalker; texture/LOD and motion artifacts remain
open. Distributor-supplied DLL pairs can now be selected automatically by the
quick launcher. The public fork ZIP still uses locally supplied NVIDIA files;
packaging support does not establish redistribution permission.
See [supported games](SUPPORTED_GAMES.md) for the tested games and DLSS models. See [supported games](SUPPORTED_GAMES.md) for the tested games and DLSS models.
+63
View File
@@ -0,0 +1,63 @@
d4r for Windows: NVIDIA DLSS on AMD Radeon RDNA3 and RDNA4
===========================================================
d4r runs NVIDIA's own DLSS (DLSS 4 by default) on a Radeon GPU in DirectX 12 games. OptiScaler
(included) catches the game's upscaler calls and hands them to d4r.
This is an early release. Only the Radeon RX 9070 XT has been tested on real hardware; every other
GPU in this release is compile-tested. SUPPORTED_GAMES.md in the source repository lists the
tested games. Expect problems.
What you need
-------------
- Windows 11, a Radeon RX 7000 or RX 9000 series GPU, a current AMD Adrenalin driver.
- A DirectX 12 game without anti-cheat (OptiScaler injects a DLL; it can get an account banned).
- Two files from NVIDIA, which this ZIP does not include:
nvngx_dlss.dll version 310.9.1 (tested) or 310.7.0. Many games ship one in their folder;
right-click > Properties > Details shows the version.
_nvngx.dll version 32.0.16.1714 (tested) or 32.0.15.9636. It is part of an NVIDIA display
driver package: extract the installer with 7-Zip and search for _nvngx.dll.
Install
-------
1. Extract this ZIP into the folder that holds the game's main .exe. For Unreal Engine games that
is <game>\<Project>\Binaries\Win64, next to <Project>-Win64-Shipping.exe.
If the folder already has a dxgi.dll or OptiScaler.ini (another OptiScaler install), or a
d3d12.dll from a Linux install, move those out first.
2. Copy nvngx_dlss.dll into the d4r folder and _nvngx.dll into d4r\ngx.
3. Optional: check the install. In the game folder, open PowerShell and run
powershell -ExecutionPolicy Bypass -File d4r\d4r-check.ps1
4. Start the game from Steam, Epic or a shortcut as usual. In its graphics settings choose DLSS
(or FSR 3.1/XeSS if DLSS is hidden; OptiScaler then feeds DLSS). Keep frame generation off.
Press Insert in game for OptiScaler's menu.
The first time DLSS starts, the game can freeze for several minutes while DLSS's GPU kernels are
compiled. They are cached in %LOCALAPPDATA%\d4r\cache, so later starts are quick.
Settings
--------
Edit d4r\d4r.ini (it is commented) and restart the game. Each game has its own copy.
- Model: K (DLSS 4, default) or M (DLSS 4.5, heavier). Select the quality mode in the game.
- AsyncInterop, ValidateOutput, Log: for testing and bug reports; the defaults are the tested setting.
OptiScaler's own settings (overlay, render ratios) are in OptiScaler.ini.
If something goes wrong
-----------------------
- Run d4r\d4r-check.ps1: it prints ok / MISSING / note lines with the fix for each problem.
- d4r\d4r_nvngx.log is rewritten at every launch. A line "d4r: DLSS disabled: ..." says why.
- DLSS missing in the menu and no d4r_nvngx.log: the files are not next to the game's main .exe.
- For a bug report run d4r\d4r-check.ps1 -Report and send the ZIP it puts on your Desktop,
with your GPU, the game, the model and what the image looked like. Logs can contain folder names.
- Not supported: frame generation, ray reconstruction, DirectX 11, Vulkan, models E and L.
Uninstall
---------
Delete dxgi.dll, OptiScaler.ini, OptiScaler.log (if present), WINDOWS_README.txt and the d4r folder
from the game folder. The cache in %LOCALAPPDATA%\d4r can be deleted too. Nothing else was changed.
Licenses
--------
d4r is Apache License 2.0. This ZIP also contains ZLUDA (Apache 2.0 or MIT), OptiScaler (GPL 3.0,
patched; the patches are in the source repository), and AMD's HIP runtime. d4r\licenses has the
texts. d4r is not affiliated with NVIDIA, AMD or the OptiScaler project. NVIDIA's files are yours
to supply, under NVIDIA's terms.
+3
View File
@@ -16,6 +16,9 @@
; Select the quality mode (Quality, Performance, ...) in the game or the OptiScaler menu (Insert). ; Select the quality mode (Quality, Performance, ...) in the game or the OptiScaler menu (Insert).
; Models E and L are not available on Windows. ; Models E and L are not available on Windows.
Model = K Model = K
; NVIDIA's library stamps "DLSS SDK - DO NOT DISTRIBUTE" over the image; d4r turns that off.
; true shows it.
ShowWatermark = false
[Interop] [Interop]
; Experimental: lets the game continue while DLSS runs on the GPU instead of waiting for it. ; Experimental: lets the game continue while DLSS runs on the GPU instead of waiting for it.
+271
View File
@@ -0,0 +1,271 @@
# Checks a d4r install on Windows. It only reads files and prints what it finds.
# Run it from the game folder (the one holding the game's .exe and d4r\), or give that folder:
# powershell -ExecutionPolicy Bypass -File d4r\d4r-check.ps1 [GAME_FOLDER]
# Add -Report to also write one ZIP (logs, this output, GPU and driver) to send with a bug report.
[CmdletBinding()]
param(
[string]$GameFolder,
[switch]$Report,
[string]$InventoryFile, # tests: a gpu-inventory.json to use instead of running the GPU query
[string]$ReportDirectory # tests: where -Report writes its ZIP (default: your Desktop)
)
$ErrorActionPreference = 'Stop'
if (!$GameFolder) {
$GameFolder = if ((Split-Path $PSScriptRoot -Leaf) -eq 'd4r') { Split-Path $PSScriptRoot -Parent } else { (Get-Location).Path }
}
$game = [IO.Path]::GetFullPath($GameFolder).TrimEnd('\')
$d4r = Join-Path $game 'd4r'
$problems = 0
$lines = [Collections.Generic.List[string]]::new()
function Say([string]$text) { $lines.Add($text); Write-Host $text }
function Ok([string]$text) { Say (' ok ' + $text) }
function Bad([string]$text) { Say (' MISSING ' + $text); $script:problems++ }
function Note([string]$text) { Say (' note ' + $text) }
function Hash([string]$path) { (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash }
function Version([string]$path) {
try { $v = (Get-Item -LiteralPath $path).VersionInfo; if ($v.FileVersion) { return ($v.FileVersion -replace ',\s*', '.') } } catch {}
return 'unknown'
}
function Read-TextFile([string]$path) { [IO.File]::ReadAllText($path) }
Say "d4r install in $game"
if (!(Test-Path -LiteralPath $d4r -PathType Container)) {
Say 'no d4r folder here; run this from the folder with the game .exe (extract the d4r ZIP there first)'
exit 1
}
if (@(Get-ChildItem -LiteralPath $game -Filter '*.exe' -File -ErrorAction SilentlyContinue).Count) { Ok 'game executable next to d4r\' }
else { Note 'no .exe next to d4r\ (the d4r files must sit in the folder of the game''s main .exe; for Unreal Engine games that is <game>\<Project>\Binaries\Win64)' }
# --- the install's own file list ---------------------------------------------------------------
$manifest = $null
$manifestPath = Join-Path $d4r 'package.json'
if (Test-Path -LiteralPath $manifestPath) {
try { $manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json } catch { $manifest = $null }
}
if (!$manifest) { Bad 'd4r\package.json is missing or damaged (extract the d4r ZIP again)' }
# --- files --------------------------------------------------------------------------------------
$required = @('dxgi.dll', 'OptiScaler.ini', 'd4r\_nvngx.dll', 'd4r\d4r.ini', 'd4r\dll-pins.txt',
'd4r\hip\bin\amdhip64_7.dll', 'd4r\zluda\nvcuda.dll', 'd4r\zluda\nvapi64.dll')
foreach ($file in $required) {
if (Test-Path -LiteralPath (Join-Path $game $file) -PathType Leaf) { Ok $file }
else { Bad "$file (extract the d4r ZIP again over the game folder)" }
}
if ($manifest) {
$edited = @('OptiScaler.ini', 'd4r/d4r.ini') # players are meant to edit these
$damaged = 0
foreach ($entry in $manifest.files) {
$path = Join-Path $game ($entry.path -replace '/', '\')
if (!(Test-Path -LiteralPath $path -PathType Leaf)) {
if ($required -notcontains ($entry.path -replace '/', '\')) { Bad "$($entry.path) (extract the d4r ZIP again)" }
continue
}
if ((Hash $path) -ne $entry.sha256) {
if ($entry.path -eq 'dxgi.dll') { Note 'dxgi.dll is not the one that came with d4r (another OptiScaler or ReShade?). Move it away and extract the d4r ZIP again' }
elseif ($edited -contains $entry.path) { Note "$($entry.path) has been edited (fine if you did it)" }
else { Bad "$($entry.path) differs from the release (damaged or replaced; extract the d4r ZIP again)"; $damaged++ }
}
}
if (!$damaged) { Ok 'every other d4r file matches the release' }
}
# --- OptiScaler.ini ---------------------------------------------------------------------------
$optiIni = Join-Path $game 'OptiScaler.ini'
if (Test-Path -LiteralPath $optiIni) {
$text = Read-TextFile $optiIni
if ($text -notmatch '(?im)^\s*AllowExternalBackend\s*=\s*true') { Bad 'OptiScaler.ini: [DLSS] AllowExternalBackend must be true (this is not the d4r OptiScaler.ini)' }
if ($text -notmatch '(?im)^\s*Dx12Upscaler\s*=\s*dlss') { Note 'OptiScaler.ini: [Upscalers] Dx12Upscaler is not dlss; the game will not use d4r' }
$m = [regex]::Match($text, '(?im)^\s*NvngxPath\s*=\s*(\S+)')
if (!$m.Success) { Bad 'OptiScaler.ini: [Libraries] NvngxPath is missing (it must name the d4r folder)' }
else {
$folder = $m.Groups[1].Value
$resolved = if ([IO.Path]::IsPathRooted($folder)) { $folder } else { Join-Path $game $folder }
if (Test-Path -LiteralPath (Join-Path $resolved '_nvngx.dll')) { Ok "OptiScaler.ini NvngxPath = $folder" }
else { Bad "OptiScaler.ini NvngxPath = $folder does not hold _nvngx.dll (use NvngxPath=d4r, or the full path of the d4r folder)" }
}
}
# --- NVIDIA DLLs (never shipped; the exact versions come from dll-pins.txt) -----------------------
$pins = @()
$pinsPath = Join-Path $d4r 'dll-pins.txt'
if (Test-Path -LiteralPath $pinsPath) {
foreach ($line in Get-Content -LiteralPath $pinsPath) {
$line = $line.Trim()
if (!$line -or $line.StartsWith('#')) { continue }
$f = $line -split '\s+'
if ($f.Count -ge 4 -and $f[1].Length -eq 64) { $pins += [pscustomobject]@{ kind = $f[0]; sha = $f[1].ToUpperInvariant(); version = $f[2]; status = $f[3] } }
}
}
function Check-Nvidia([string]$kind, [string]$relative, [string]$label, [string]$source) {
$path = Join-Path $game $relative
$wanted = @($pins | Where-Object { $_.kind -eq $kind } | ForEach-Object { $_.version }) -join ' or '
if (!$wanted) { Note "$label cannot be checked (d4r\dll-pins.txt is missing or damaged)"; return }
if (!(Test-Path -LiteralPath $path -PathType Leaf)) { Bad "${relative}: copy NVIDIA's $label here (version $wanted; $source)"; return }
$hash = Hash $path
$pin = @($pins | Where-Object { $_.kind -eq $kind -and $_.sha -eq $hash }) | Select-Object -First 1
if (!$pin) { Bad "$relative is version $(Version $path), which d4r does not accept. d4r on Windows needs exactly $wanted ($source)"; return }
if ($pin.status -eq 'validated') { Ok "$relative ($label $($pin.version), tested on Windows)" }
else { Ok "$relative ($label $($pin.version))"; Note "$label $($pin.version) is accepted but has had less Windows testing" }
}
Check-Nvidia 'dlss' 'd4r\nvngx_dlss.dll' 'nvngx_dlss.dll' 'many games ship one in their folder; see WINDOWS_README.txt'
Check-Nvidia 'ngx' 'd4r\ngx\_nvngx.dll' '_nvngx.dll' 'it is part of NVIDIA''s display driver package; see WINDOWS_README.txt'
# --- GPU and the build for it --------------------------------------------------------------------
$registryPath = Join-Path $d4r 'gpu-targets.json'
$registry = @()
if (Test-Path -LiteralPath $registryPath) { $registry = @((Get-Content -LiteralPath $registryPath -Raw | ConvertFrom-Json).targets) }
$inventory = $null
if ($InventoryFile) { $inventory = Get-Content -LiteralPath $InventoryFile -Raw | ConvertFrom-Json }
elseif (Test-Path -LiteralPath (Join-Path $d4r 'd4r_gpu_inventory.exe')) {
$work = Join-Path ([IO.Path]::GetTempPath()) ('d4r-check-' + [Guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $work | Out-Null
try {
$info = New-Object Diagnostics.ProcessStartInfo
$info.FileName = Join-Path $d4r 'd4r_gpu_inventory.exe'
$info.Arguments = '--hip-root "' + (Join-Path $d4r 'hip') + '" --output-dir "' + $work + '"'
$info.UseShellExecute = $false; $info.CreateNoWindow = $true
$info.RedirectStandardOutput = $true; $info.RedirectStandardError = $true
$info.EnvironmentVariables['D4R_DIAG_DIR'] = $work
$process = New-Object Diagnostics.Process; $process.StartInfo = $info
[void]$process.Start()
$null = $process.StandardOutput.ReadToEndAsync(); $null = $process.StandardError.ReadToEndAsync()
if (!$process.WaitForExit(30000)) { $process.Kill(); $process.WaitForExit() }
if (Test-Path -LiteralPath (Join-Path $work 'gpu-inventory.json')) { $inventory = Get-Content -LiteralPath (Join-Path $work 'gpu-inventory.json') -Raw | ConvertFrom-Json }
} catch {} finally {
$process.Dispose()
Get-ChildItem -LiteralPath $work -File -ErrorAction SilentlyContinue | ForEach-Object { Remove-Item -LiteralPath $_.FullName }
Remove-Item -LiteralPath $work -ErrorAction SilentlyContinue
}
}
$arch = $null
if (!$inventory) { Note 'could not ask the AMD runtime about your GPU (d4r_gpu_inventory.exe is missing, or the AMD graphics driver is not installed or is too old)' }
else {
$supported = @($registry | ForEach-Object { $_.architecture })
$devices = @($inventory.devices | Where-Object { $_.wavefrontSize -eq 32 -and ($supported -contains $_.architecture -or !$supported.Count) })
if (!$devices.Count) {
$seen = @($inventory.devices | ForEach-Object { "$($_.name) ($($_.architecture))" }) -join ', '
Bad ("no supported RDNA3/RDNA4 GPU found by the AMD runtime. It sees: " + $(if ($seen) { $seen } else { 'nothing' }) + '. Update the AMD Adrenalin driver; RDNA2 and older are not supported')
} else {
foreach ($device in $devices) {
$a = $device.architecture
$missing = @()
foreach ($rel in @("d4r\_nvngx_$a.dll", "d4r\pixel_convert_$a.hsaco", "d4r\nvapi\$a\nvapi64.dll", "d4r\native\$a\d4r-kernels.txt")) {
if (!(Test-Path -LiteralPath (Join-Path $game $rel))) { $missing += $rel }
}
$native = @(Get-ChildItem -LiteralPath (Join-Path $d4r "native\$a") -Filter '*.hsaco' -ErrorAction SilentlyContinue).Count
if ($missing.Count) { Bad "GPU $($device.name) ($a): this release has no complete build for it (missing: $($missing -join ', '))" }
else {
Ok "GPU $($device.name) ($a): build present, $native native kernels"
if (!$arch) { $arch = $a }
}
if ($a -ne 'gfx1201') { Note "only the RX 9070 XT (gfx1201) has been tested on real hardware; $a is compile-tested" }
}
}
if (@($inventory.devices | Where-Object { $_.wavefrontSize -eq 32 }).Count -gt 1) { Note 'several GPUs found; d4r uses the one the game renders on' }
}
if ($arch -and $registry.Count) {
$info = @($registry | Where-Object { $_.architecture -eq $arch }) | Select-Object -First 1
$bad = 0
$objects = @(Get-Item -LiteralPath (Join-Path $d4r "pixel_convert_$arch.hsaco") -ErrorAction SilentlyContinue) +
@(Get-ChildItem -LiteralPath (Join-Path $d4r "native\$arch") -Filter '*.hsaco' -ErrorAction SilentlyContinue)
foreach ($object in $objects) {
$stream = [IO.File]::OpenRead($object.FullName)
try { $header = New-Object byte[] 64; [void]$stream.Read($header, 0, 64) } finally { $stream.Dispose() }
if ($header[0] -ne 127 -or $header[1] -ne 69 -or $header[2] -ne 76 -or $header[3] -ne 70 -or $header[48] -ne $info.elfMachine) {
Bad "$($object.Name) is not built for $arch (do not rename or mix files from other GPUs; extract the d4r ZIP again)"; $bad++
}
}
if ($objects.Count -and !$bad) { Ok "all $($objects.Count) GPU code objects are built for $arch" }
}
# --- Linux / Wine leftovers ------------------------------------------------------------------------
$proxyFound = $false
foreach ($name in @('d3d12.dll', 'd3d12core.dll')) {
$proxy = Join-Path $game $name
if (Test-Path -LiteralPath $proxy -PathType Leaf) {
$item = Get-Item -LiteralPath $proxy
if ($item.Length -le 64MB -and [Text.Encoding]::ASCII.GetString([IO.File]::ReadAllBytes($proxy)) -match '(?i)vkd3d|wined3d') {
Bad "$name in the game folder is vkd3d-proton/Wine (left over from a Linux install?). Move it out; d4r on Windows uses native D3D12"
$proxyFound = $true
}
}
}
if (!$proxyFound) { Ok 'no vkd3d/Wine d3d12.dll in the game folder' }
# --- d4r.ini ---------------------------------------------------------------------------------------
$iniPath = Join-Path $d4r 'd4r.ini'
$model = 'K'
if (Test-Path -LiteralPath $iniPath) {
$section = ''; $iniProblems = 0
$known = @{ 'dlss.model' = 1; 'dlss.showwatermark' = 1; 'interop.asyncinterop' = 1; 'debug.validateoutput' = 1; 'debug.log' = 1; 'paths.cachedir' = 1 }
$boolean = @('dlss.showwatermark', 'interop.asyncinterop', 'debug.validateoutput')
foreach ($raw in (Read-TextFile $iniPath).TrimStart([char]0xFEFF) -split "`n") {
$line = $raw.Trim()
if (!$line -or $line.StartsWith(';') -or $line.StartsWith('#')) { continue }
if ($line.StartsWith('[')) { $section = $line.Trim('[', ']').Trim(); continue }
$eq = $line.IndexOf('='); if ($eq -lt 0) { continue }
$key = $line.Substring(0, $eq).Trim()
$value = ($line.Substring($eq + 1) -replace '\s[;#].*$', '').Trim()
$name = ("$section.$key").ToLowerInvariant()
$v = $value.ToLowerInvariant()
if (!$known.ContainsKey($name)) { Note "d4r.ini: [$section] $key is not a Windows setting; ignored"; $iniProblems++ }
elseif ($name -eq 'dlss.model') {
if (@('', 'auto', 'k', 'dlss4', '11') -contains $v) { $model = 'K' }
elseif (@('m', 'dlss4.5', '13') -contains $v) { $model = 'M' }
else { Note "d4r.ini: [DLSS] Model '$value' is not available on Windows (use K or M); K will be used"; $iniProblems++ }
}
elseif ($boolean -contains $name) {
if (@('', 'auto', '1', 'true', 'yes', 'on', '0', 'false', 'no', 'off') -notcontains $v) { Note "d4r.ini: [$section] $key must be true or false, not '$value'; the default will be used"; $iniProblems++ }
}
elseif ($name -eq 'debug.log' -and @('', 'auto', 'normal', 'verbose') -notcontains $v) { Note "d4r.ini: [Debug] Log must be normal or verbose, not '$value'; normal will be used"; $iniProblems++ }
}
if (!$iniProblems) { Ok "d4r.ini is valid (model $model)" }
}
# --- what the last game start said -----------------------------------------------------------------
$log = Join-Path $d4r 'd4r_nvngx.log'
if (Test-Path -LiteralPath $log) {
$messages = @(Get-Content -LiteralPath $log | Where-Object { $_ -match '^d4r: ' })
$failed = @($messages | Where-Object { $_ -match 'DLSS disabled' })
if ($failed.Count) { foreach ($m in $failed | Select-Object -First 3) { Bad ('last game start: ' + $m.Substring(5)) } }
elseif ($messages.Count) { Ok ('last game start: ' + $messages[0].Substring(5)) }
else { Note 'd4r\d4r_nvngx.log has no d4r summary line yet (the game may not have chosen DLSS)' }
foreach ($m in $messages | Where-Object { $_ -notmatch 'DLSS disabled|player mode' } | Select-Object -First 5) { Note ('last game start: ' + $m.Substring(5)) }
} else { Note 'no d4r\d4r_nvngx.log yet: start the game once and choose DLSS (or FSR/XeSS) in its graphics settings' }
if (Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.Path -and $_.Path.StartsWith($game + '\', [StringComparison]::OrdinalIgnoreCase) -and $_.ProcessName -notmatch '^(powershell|pwsh)$' }) {
Note 'the game is running; its log is rewritten at every start'
}
Say ''
if ($problems -eq 0) { Say 'Everything d4r needs is in place.' } else { Say "$problems thing(s) to fix above." }
# --- bug-report ZIP ------------------------------------------------------------------------------
if ($Report) {
$stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
$destination = if ($ReportDirectory) { $ReportDirectory } else { [Environment]::GetFolderPath('Desktop') }
$staging = Join-Path ([IO.Path]::GetTempPath()) ("d4r-report-$stamp")
New-Item -ItemType Directory -Force -Path $staging | Out-Null
try {
$lines | Set-Content -LiteralPath (Join-Path $staging 'check.txt') -Encoding UTF8
foreach ($file in @('d4r\d4r_nvngx.log', 'd4r\d4r.ini', 'OptiScaler.ini', 'OptiScaler.log')) {
$source = Join-Path $game $file
if (Test-Path -LiteralPath $source) { Copy-Item -LiteralPath $source -Destination (Join-Path $staging (Split-Path $file -Leaf)) }
}
Get-ChildItem -LiteralPath $game -Filter 'OptiScaler*.log' -File | ForEach-Object { Copy-Item -LiteralPath $_.FullName -Destination $staging -Force }
try { Get-CimInstance Win32_VideoController | Select-Object Name, DriverVersion, DriverDate | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $staging 'driver.json') -Encoding UTF8 } catch {}
if ($inventory) { $inventory | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $staging 'gpu-inventory.json') -Encoding UTF8 }
Get-ChildItem -LiteralPath $game -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.FullName.StartsWith($d4r + '\') -or $_.Name -in @('dxgi.dll', 'OptiScaler.ini') } |
ForEach-Object { '{0} {1,12} {2}' -f (Hash $_.FullName), $_.Length, $_.FullName.Substring($game.Length + 1) } |
Set-Content -LiteralPath (Join-Path $staging 'files.sha256.txt') -Encoding UTF8
$zip = Join-Path $destination "d4r-report-$stamp.zip"
Compress-Archive -Path (Join-Path $staging '*') -DestinationPath $zip -Force
Write-Host ''
Write-Host "Bug report written: $zip" -ForegroundColor Cyan
Write-Host 'Also tell us: GPU, game, K or M, and what the image looked like. The logs can contain local folder names; look before posting publicly.'
} finally {
Get-ChildItem -LiteralPath $staging -File -ErrorAction SilentlyContinue | ForEach-Object { Remove-Item -LiteralPath $_.FullName }
Remove-Item -LiteralPath $staging -ErrorAction SilentlyContinue
}
}
if ($problems) { exit 1 }
+12 -5
View File
@@ -7,6 +7,7 @@ param(
[string]$GamePackagePrefix, [string]$GamePackagePrefix,
[Parameter(Mandatory=$true)][string]$LoaderDll, [Parameter(Mandatory=$true)][string]$LoaderDll,
[Parameter(Mandatory=$true)][string]$PackageRoot, [Parameter(Mandatory=$true)][string]$PackageRoot,
[string[]]$Architectures, # default: every target in gpu-targets.json; a subset makes a smaller release
[string]$ArchivePath [string]$ArchivePath
) )
$ErrorActionPreference='Stop' $ErrorActionPreference='Stop'
@@ -28,8 +29,10 @@ function Stage([string]$Source,[string]$Relative) {
$item=Get-Item -LiteralPath $destination $item=Get-Item -LiteralPath $destination
$files.Add(@{path=$Relative.Replace('\','/'); sha256=(Get-FileHash -LiteralPath $destination -Algorithm SHA256).Hash; version=$item.VersionInfo.FileVersion}) $files.Add(@{path=$Relative.Replace('\','/'); sha256=(Get-FileHash -LiteralPath $destination -Algorithm SHA256).Hash; version=$item.VersionInfo.FileVersion})
} }
$targets=[ordered]@{}; $baseline=$null; $commonStaged=$false $targets=[ordered]@{}; $baseline=$null; $commonStaged=$false; $inventoryStaged=$false
foreach ($gpu in Get-D4RGpuTargets) { $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" $arch=$gpu.architecture; $source="$GamePackagePrefix-$arch"
$metadata=Get-Content -LiteralPath (Join-Path $source 'package.json') -Raw | ConvertFrom-Json $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 ($metadata.architecture -ne $arch -or $metadata.d4rWorkingTreeDirty) { throw "Uncommitted or wrong-target input: $source" }
@@ -41,7 +44,9 @@ foreach ($gpu in Get-D4RGpuTargets) {
if ((Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash -ne $file.sha256) { throw "Input file changed: $source/$relative" } 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++ } if ($relative.EndsWith('.hsaco')) { Assert-D4RCodeObjectTarget $path $arch; $objects++ }
# Per-target files get target-specific names so one folder serves every GPU. # Per-target files get target-specific names so one folder serves every GPU.
if ($relative -eq 'd4r/_nvngx.dll') { Stage $path "d4r/_nvngx_$arch.dll" } # 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 -eq 'd4r/nvapi/nvapi64.dll') { Stage $path "d4r/nvapi/$arch/nvapi64.dll" }
elseif ($relative -like 'd4r/pixel_convert_*.hsaco') { Stage $path $relative } 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.StartsWith('d4r/native/')) { Stage $path ("d4r/native/$arch/" + $relative.Substring('d4r/native/'.Length)) }
@@ -51,7 +56,7 @@ foreach ($gpu in Get-D4RGpuTargets) {
if (!$commonStaged) { Stage $path $public } 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" } 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, inventory, PowerShell, docs) are not part of a drag-in install. # 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" } if ($objects -ne 17) { throw "Expected 17 code objects for $arch" }
$commonStaged=$true $commonStaged=$true
@@ -63,6 +68,8 @@ Stage $loader 'd4r/_nvngx.dll'
Stage (Join-Path $repo 'packaging/windows/OptiScaler.ini') 'OptiScaler.ini' 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 '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/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'}, foreach ($optional in @(@{source='packaging/windows/WINDOWS_README.txt'; path='WINDOWS_README.txt'},
@{source='scripts/windows/d4r-check.ps1'; path='d4r/d4r-check.ps1'})) { @{source='scripts/windows/d4r-check.ps1'; path='d4r/d4r-check.ps1'})) {
$path=Join-Path $repo $optional.source $path=Join-Path $repo $optional.source
@@ -95,4 +102,4 @@ if ($ArchivePath) {
} }
} finally { if ($archive) { $archive.Dispose() }; $stream.Dispose() } } finally { if ($archive) { $archive.Dispose() }; $stream.Dispose() }
} }
Write-Host "Windows drag-in release: $PackageRoot ($($targets.Count) GPU targets, no NVIDIA DLLs)" Write-Host "Windows drag-in release: $PackageRoot ($($targets.Count) GPU target(s): $($targets.Keys -join ', '); no NVIDIA DLLs)"
+122
View File
@@ -0,0 +1,122 @@
# 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
Set-Content -LiteralPath (Join-Path $game 'd4r/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/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\\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/nvngx_dlss.dll')
Expect 'dll-missing' (Run-Check $game $inv) 1 @("MISSING\s+d4r\\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/nvngx_dlss.dll') 'some other version' -Encoding ASCII
Expect 'dll-wrong-version' (Run-Check $game $inv) 1 @('MISSING\s+d4r\\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/nvngx_dlss.dll') 'fixture dlss 310.7' -Encoding ASCII
@('dlss '+(Sha (Join-Path $game 'd4r/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\\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"
@@ -20,7 +20,7 @@ function New-Inputs([string]$Prefix,[scriptblock]$Mutate) {
foreach ($target in $targets) { foreach ($target in $targets) {
$arch=$target.architecture; $root="$Prefix-$arch" $arch=$target.architecture; $root="$Prefix-$arch"
$text=@{'OptiScaler.dll'='optiscaler'; 'd4r/_nvngx.dll'="shim $arch"; 'd4r/nvapi/nvapi64.dll'="nvapi $arch"; $text=@{'OptiScaler.dll'='optiscaler'; 'd4r/_nvngx.dll'="shim $arch"; 'd4r/nvapi/nvapi64.dll'="nvapi $arch";
'd4r/hip/bin/amdhip64_7.dll'='hip'; 'd4r/zluda/nvcuda.dll'='zluda'; 'licenses/d4r.txt'='license'; 'd4r/hip/bin/amdhip64_7.dll'='hip'; 'd4r/zluda/nvcuda.dll'='zluda'; 'd4r/zluda/nvapi64.dll'='zluda nvapi'; 'licenses/d4r.txt'='license';
'd4r/d4r_gpu_inventory.exe'='inventory'; 'd4r/d4r_debug_launcher.exe'='launcher'; 'windows-game.ps1'='installer'; 'd4r/d4r_gpu_inventory.exe'='inventory'; 'd4r/d4r_debug_launcher.exe'='launcher'; 'windows-game.ps1'='installer';
'd4r/native/d4r-kernels.txt'='manifest'} 'd4r/native/d4r-kernels.txt'='manifest'}
foreach ($entry in $text.GetEnumerator()) { foreach ($entry in $text.GetEnumerator()) {
@@ -53,7 +53,8 @@ New-Inputs $prefix
$release=Join-Path $OutputDirectory 'release' $release=Join-Path $OutputDirectory 'release'
Invoke-Package $prefix $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', 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/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 (!(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.' } if ((Get-Content -LiteralPath (Join-Path $release 'd4r/_nvngx.dll') -Raw).Trim() -ne 'loader fixture') { throw 'd4r/_nvngx.dll is not the loader.' }
@@ -66,7 +67,8 @@ foreach ($target in $targets) {
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" } 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 } foreach ($object in $native) { Assert-D4RCodeObjectTarget $object.FullName $arch }
} }
foreach ($excluded in @('d4r/d4r_gpu_inventory.exe','d4r/d4r_debug_launcher.exe','windows-game.ps1','d4r/native/kernel1.hsaco','d4r/nvapi/nvapi64.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" } 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 $manifest=Get-Content -LiteralPath (Join-Path $release 'd4r/package.json') -Raw | ConvertFrom-Json
@@ -77,6 +79,16 @@ $shipped=@(Get-ChildItem -LiteralPath $release -Recurse -File | ForEach-Object {
if (Compare-Object $shipped @($manifest.files.path)) { throw 'Manifest and shipped files differ.' } 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} $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. # 2. A code object for another GPU in a target's input is rejected.
$prefix=Join-Path $OutputDirectory 'inputs/mixed/game' $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 } } New-Inputs $prefix { param($root,$arch) if ($arch -eq 'gfx1101') { Write-CodeObject (Join-Path $root 'd4r/native/kernel3.hsaco') 65 } }
+5
View File
@@ -148,6 +148,11 @@ API unsigned NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCommandList* list, co
if (!snapshot->parameters) return failure; if (!snapshot->parameters) return failure;
d4r::win::player::apply_preset(p); d4r::win::player::apply_preset(p);
d4r::ngx::copy_create(p, snapshot->parameters); d4r::ngx::copy_frame(p, snapshot->parameters); d4r::ngx::copy_create(p, snapshot->parameters); d4r::ngx::copy_frame(p, snapshot->parameters);
// NVIDIA's library stamps "DLSS SDK - DO NOT DISTRIBUTE" over its output unless the SDK's
// Disable.Watermark parameter is set (the Linux shim sets it too). The game's own value is
// not used. [DLSS] ShowWatermark = true (D4R_SHIM_WATERMARK=1) keeps the stamp.
static const bool watermark = [] { const char* v = std::getenv("D4R_SHIM_WATERMARK"); return v && v[0] == '1'; }();
d4r_ngx_set_int(snapshot->parameters, "Disable.Watermark", watermark ? 0 : 1);
const char* names[] = {"Color", "Depth", "MotionVectors", "Output", "ExposureTexture"}; const char* names[] = {"Color", "Depth", "MotionVectors", "Output", "ExposureTexture"};
const char* states[] = {"D4R.Color.State", "D4R.Depth.State", "D4R.Motion.State", "D4R.Output.State", "D4R.Exposure.State"}; const char* states[] = {"D4R.Color.State", "D4R.Depth.State", "D4R.Motion.State", "D4R.Output.State", "D4R.Exposure.State"};
for (unsigned i = 0; i < 5; ++i) { for (unsigned i = 0; i < 5; ++i) {
+2 -2
View File
@@ -202,8 +202,8 @@ struct Args {
else if (key == "--preset") { else if (key == "--preset") {
size_t end = 0; size_t end = 0;
preset = static_cast<unsigned>(std::stoul(value, &end)); preset = static_cast<unsigned>(std::stoul(value, &end));
if (end != value.size() || (preset != 5 && preset != 11 && preset != 13)) if (end != value.size() || (preset != 5 && preset != 11 && preset != 12 && preset != 13))
throw std::runtime_error("preset must be 5 (E), 11 (K) or 13 (M)"); throw std::runtime_error("preset must be 5 (E), 11 (K), 12 (L) or 13 (M)");
} }
else if (key == "--fixture-dir") fixture_dir = value; else if (key == "--fixture-dir") fixture_dir = value;
else if (key == "--benchmark-module") benchmark_module = value; else if (key == "--benchmark-module") benchmark_module = value;
+7 -2
View File
@@ -2,8 +2,13 @@
# never shipped or committed; players copy their own. # never shipped or committed; players copy their own.
# dlss = nvngx_dlss.dll (d4r\nvngx_dlss.dll) # dlss = nvngx_dlss.dll (d4r\nvngx_dlss.dll)
# ngx = _nvngx.dll, NVIDIA's NGX core from a display driver (d4r\ngx\_nvngx.dll) # ngx = _nvngx.dll, NVIDIA's NGX core from a display driver (d4r\ngx\_nvngx.dll)
# status: validated = tested on Windows hardware; anything else is accepted # status: validated = tested on Windows hardware by the project; anything else is
# but reported as unvalidated in d4r\d4r_nvngx.log. # accepted but reported as having had less testing in d4r\d4r_nvngx.log and d4r-check.ps1.
# Native kernels only replace DLSS code whose PTX identity matches; anything else
# falls back to ZLUDA's translated kernels, so an accepted DLL is never run with a
# wrong native kernel.
# kind sha256 version status # kind sha256 version status
dlss 3975567B8943C53ACCE397F2B72380092F84F162D00B0D2C7D08A1025C563983 310.9.1 validated dlss 3975567B8943C53ACCE397F2B72380092F84F162D00B0D2C7D08A1025C563983 310.9.1 validated
dlss 1CDFDC957CB7FC9805500CA6793F607EF2D4DBD7C967FEAC70ACA9CAF382D0C0 310.7.0 unvalidated
ngx 66767018C36B3BAB46398DADE3ADF173DAA3730FDA75965689EA848C9BC4E79B 32.0.16.1714 validated ngx 66767018C36B3BAB46398DADE3ADF173DAA3730FDA75965689EA848C9BC4E79B 32.0.16.1714 validated
ngx 0765FB7FB2B8BE28EE1F0226679D7B2636A69CACCE5A23891984978F690D81A4 32.0.15.9636 unvalidated
+8 -10
View File
@@ -22,7 +22,7 @@ struct IniEntry { std::string section, key, value; };
struct Pin { std::string kind, sha256, version, status; }; struct Pin { std::string kind, sha256, version, status; };
struct Settings { struct Settings {
unsigned preset = 11; // K unsigned preset = 11; // K
bool asyncInterop = false, validateOutput = false, verbose = false; bool asyncInterop = false, validateOutput = false, verbose = false, showWatermark = false;
std::wstring cacheDir; std::wstring cacheDir;
}; };
struct State { struct State {
@@ -102,7 +102,8 @@ inline Settings read_settings(const std::vector<IniEntry>& ini, std::vector<std:
if (value.empty() || value == "AUTO" || value == "K" || value == "DLSS4" || value == "11") settings.preset = 11; if (value.empty() || value == "AUTO" || value == "K" || value == "DLSS4" || value == "11") settings.preset = 11;
else if (value == "M" || value == "DLSS4.5" || value == "13") settings.preset = 13; else if (value == "M" || value == "DLSS4.5" || value == "13") settings.preset = 13;
else notes.push_back("d4r.ini: [DLSS] Model '" + entry.value + "' is not available on Windows (use K or M); using K"); else notes.push_back("d4r.ini: [DLSS] Model '" + entry.value + "' is not available on Windows (use K or M); using K");
} else if (name == "interop.asyncinterop") settings.asyncInterop = flag(entry, false); } else if (name == "dlss.showwatermark") settings.showWatermark = flag(entry, false);
else if (name == "interop.asyncinterop") settings.asyncInterop = flag(entry, false);
else if (name == "debug.validateoutput") settings.validateOutput = flag(entry, false); else if (name == "debug.validateoutput") settings.validateOutput = flag(entry, false);
else if (name == "debug.log") { else if (name == "debug.log") {
const auto value = lower(entry.value); const auto value = lower(entry.value);
@@ -213,11 +214,9 @@ inline void set_variable(const wchar_t* name, const std::optional<std::wstring>&
SetEnvironmentVariableW(name, value ? value->c_str() : nullptr); SetEnvironmentVariableW(name, value ? value->c_str() : nullptr);
_wputenv_s(name, value ? value->c_str() : L""); _wputenv_s(name, value ? value->c_str() : L"");
} }
// Only when the game has no console/pipe: never steal a launcher's streams. // Player mode only runs when no launcher set up the environment, so no launcher is
inline bool unattached(DWORD stream) { // reading these streams. A game started from Steam can have a valid but unread pipe
const HANDLE current = GetStdHandle(stream); // as stderr; always writing to the log keeps errors visible.
return !current || current == INVALID_HANDLE_VALUE;
}
inline void redirect(FILE* crt, DWORD stream, const std::filesystem::path& path) { inline void redirect(FILE* crt, DWORD stream, const std::filesystem::path& path) {
FILE* reopened = nullptr; FILE* reopened = nullptr;
if (_wfreopen_s(&reopened, path.c_str(), L"a", crt) || !reopened) return; if (_wfreopen_s(&reopened, path.c_str(), L"a", crt) || !reopened) return;
@@ -253,6 +252,7 @@ inline void apply(const std::filesystem::path& dir, const Settings& settings) {
{L"D4R_QUIET_API", settings.verbose ? unset : on}, {L"D4R_QUIET_API", settings.verbose ? unset : on},
{L"D4R_VALIDATE_OUTPUT", settings.validateOutput ? on : unset}, {L"D4R_VALIDATE_OUTPUT", settings.validateOutput ? on : unset},
{L"D4R_ASYNC_INTEROP", settings.asyncInterop ? on : unset}, {L"D4R_ASYNC_INTEROP", settings.asyncInterop ? on : unset},
{L"D4R_SHIM_WATERMARK", settings.showWatermark ? on : unset},
{L"D4R_PROFILE_STAGES", unset}, {L"D4R_ZLUDA_PROFILE", unset}, {L"D4R_ZLUDA_PROFILE_DEFERRED", unset}, {L"D4R_PROFILE_STAGES", unset}, {L"D4R_ZLUDA_PROFILE", unset}, {L"D4R_ZLUDA_PROFILE_DEFERRED", unset},
{L"D4R_ZLUDA_PROFILE_ALLOW_LEGACY", unset}, {L"D4R_ZLUDA_PROFILE_EVERY", unset}, {L"D4R_ZLUDA_PROFILE_ALLOW_LEGACY", unset}, {L"D4R_ZLUDA_PROFILE_EVERY", unset},
{L"D4R_PROFILE_COMMAND_HOOKS", unset}, {L"D4R_ZLUDA_PROFILE_API", unset}, {L"D4R_PROFILE_GPU_BOUNDARY", unset}, {L"D4R_PROFILE_COMMAND_HOOKS", unset}, {L"D4R_ZLUDA_PROFILE_API", unset}, {L"D4R_PROFILE_GPU_BOUNDARY", unset},
@@ -278,12 +278,10 @@ inline void ensure_environment() {
if (!std::filesystem::exists(dir / L"d4r.ini")) return; // Developer layout: keep the original requirement. if (!std::filesystem::exists(dir / L"d4r.ini")) return; // Developer layout: keep the original requirement.
auto& current = state(); current.dir = dir; auto& current = state(); current.dir = dir;
const auto log = dir / L"d4r_nvngx.log"; const auto log = dir / L"d4r_nvngx.log";
if (unattached(STD_ERROR_HANDLE)) {
{ std::ofstream truncate(log, std::ios::trunc); } // Rewritten at every launch, as on Linux. { std::ofstream truncate(log, std::ios::trunc); } // Rewritten at every launch, as on Linux.
redirect(stderr, STD_ERROR_HANDLE, log); redirect(stderr, STD_ERROR_HANDLE, log);
}
const auto settings = read_settings(parse_ini(read_text(dir / L"d4r.ini")), current.notes); const auto settings = read_settings(parse_ini(read_text(dir / L"d4r.ini")), current.notes);
if (settings.verbose && unattached(STD_OUTPUT_HANDLE)) redirect(stdout, STD_OUTPUT_HANDLE, log); if (settings.verbose) redirect(stdout, STD_OUTPUT_HANDLE, log);
const auto pins = parse_pins(read_text(dir / L"dll-pins.txt")); const auto pins = parse_pins(read_text(dir / L"dll-pins.txt"));
verify_dll(dir, pins, "dlss", L"nvngx_dlss.dll", "nvngx_dlss.dll", current.notes); verify_dll(dir, pins, "dlss", L"nvngx_dlss.dll", "nvngx_dlss.dll", current.notes);
verify_dll(dir, pins, "ngx", L"ngx\\_nvngx.dll", "_nvngx.dll", current.notes); verify_dll(dir, pins, "ngx", L"ngx\\_nvngx.dll", "_nvngx.dll", current.notes);
+6
View File
@@ -46,6 +46,12 @@ int wmain(int argc, wchar_t** argv) {
s = settings("[Kernels]\nNativeFp8 = on\n[Latency]\nFrameAge = 1\n", notes); s = settings("[Kernels]\nNativeFp8 = on\n[Latency]\nFrameAge = 1\n", notes);
expect(has_note(notes, "[Kernels] NativeFp8 is not a Windows setting") && has_note(notes, "[Latency] FrameAge"), expect(has_note(notes, "[Kernels] NativeFp8 is not a Windows setting") && has_note(notes, "[Latency] FrameAge"),
"Linux-only settings are reported as ignored"); "Linux-only settings are reported as ignored");
s = settings("", notes);
expect(!s.showWatermark, "watermark is hidden by default");
s = settings("[DLSS]\nShowWatermark = true\n", notes);
expect(s.showWatermark && notes.empty(), "[DLSS] ShowWatermark = true keeps NVIDIA's stamp");
s = settings("[DLSS]\nShowWatermark = sometimes\n", notes);
expect(!s.showWatermark && has_note(notes, "ShowWatermark must be true or false"), "invalid ShowWatermark falls back to hidden with a note");
s = settings("[Paths]\nCacheDir = D:\\d4r-cache\n", notes); s = settings("[Paths]\nCacheDir = D:\\d4r-cache\n", notes);
expect(s.cacheDir == L"D:\\d4r-cache" && notes.empty(), "CacheDir path"); expect(s.cacheDir == L"D:\\d4r-cache" && notes.empty(), "CacheDir path");