Fix Windows numeric NGX feature discovery and quick-test launches

This commit is contained in:
xdfnx-dev committed 2026-10-02 23:36:02 +03:00
1 parent 21ed0a8c88
commit 17121ccbf6
22 files changed
+473 -44

No files matched your search

+7 -1
View File
@@ -13,7 +13,7 @@ transformer networks on Windows 11 / RX 9070 XT (gfx1201), using patched
ZLUDA/HIP and VRAM buffers/shared fences directly. Inputs and output belong to 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 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`. The Windows backend, command-list hooks and diagnostics live in `tools/windows`.
All sixteen Windows hardware gates pass, and queued-frame/feature-recreation K 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 outputs match synchronous controls exactly. A five-minute K/4K Silent Hill 2
run completes 16941 finite frames without backend errors. Performance remains run completes 16941 finite frames without backend errors. Performance remains
under development; asynchronous interop is opt-in and requires one queue. under development; asynchronous interop is opt-in and requires one queue.
@@ -37,6 +37,12 @@ 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 diagnostic ZIP after exiting. RESTORE-GAME.cmd restores replaced files. Read
[the short English instructions](docs/windows-quick-test.txt). Corresponding [the short English instructions](docs/windows-quick-test.txt). Corresponding
sources are a separate release asset; the test requires no SDK installation. 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; other GPUs and the reported Cyberpunk game still need testing.
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.
+9 -1
View File
@@ -22,7 +22,15 @@ executing kernels. It rejects another architecture's package and a local
D3D12 proxy identifying itself as vkd3d/Wine, producing one diagnostic ZIP. D3D12 proxy identifying itself as vkd3d/Wine, producing one diagnostic ZIP.
RX 9060 XT needs gfx1200: the old gfx1201-only package fails before DLSS RX 9060 XT needs gfx1200: the old gfx1201-only package fails before DLSS
initialization on it. Use native D3D12. On an untested GPU, start with initialization on it. Use native D3D12. On an untested GPU, start with
synchronous K and output/exception diagnostics. synchronous K and output diagnostics. The ordinary launcher does not attach
a debugger. `-CaptureExceptions` is an explicit developer option; games with
anti-debug protection can refuse it.
Numeric CUDA driver NGX initialization searches for `nvngx_dlss.dll` beside
the calling d4r shim. The installer stages your local feature DLL in `d4r/`
and the original NVIDIA core in `d4r/vendor/`. `Libraries.NvngxDlssPath` is
the feature directory. Keeping the feature DLL only in `vendor/` causes
`BAD00004` (FeatureNotFound) in numeric-ID games such as Cyberpunk.
Build the shim/diagnostics, corrected ZLUDA, K/M native objects and patched Build the shim/diagnostics, corrected ZLUDA, K/M native objects and patched
OptiScaler using the scripts in `scripts/windows`, then run: OptiScaler using the scripts in `scripts/windows`, then run:
+3 -1
View File
@@ -43,7 +43,9 @@ IF SOMETHING FAILS:
- Do not start a second test while the first game is running. - Do not start a second test while the first game is running.
No HIP SDK installation is required; the isolated runtime is included. No HIP SDK installation is required; the isolated runtime is included.
This uses synchronous interop, output checks and crash capture by default. This uses synchronous interop and output checks. No debugger is attached by default.
Exit codes and loaded DLLs are logged. A debugger is only an explicit developer option.
Your local DLSS DLL is staged beside the d4r shim automatically.
It is a correctness diagnostic, NOT an FPS benchmark or an optimized daily-use build. It is a correctness diagnostic, NOT an FPS benchmark or an optimized daily-use build.
Colour, depth, motion and output remain in VRAM in the runtime fast path. Colour, depth, motion and output remain in VRAM in the runtime fast path.
+54 -1
View File
@@ -19,11 +19,64 @@ explicit limits; the validated package keeps conservative arithmetic.
## Milestone and gates ## Milestone and gates
2026-10-02 issue #10 follow-up ([comment 5958769640](https://github.com/countervolts/d4r/issues/10#issuecomment-5958769640)):
four new logs confirm that RX 9060 XT / gfx1200 HIP discovery succeeds. The
Cyberpunk K/M runs never launch the transformer: numeric CUDA NGX capabilities
report `available=0, feature_init=BAD00004` (FeatureNotFound). The unmatched
adapter during enumeration is not the main Radeon: its runtime LUID matches.
GetFeatureRequirements now returns success with AdapterUnsupported for Intel /
WARP / another nonmatching adapter, while runtime initialization still requires
the exact HIP architecture and D3D12 LUID.
The feature discovery failure is reproduced on RX 9070 XT without a game,
using Cyberpunk's unchanged numeric application ID `100152211`. Keeping the
feature DLL only beside the official core in `d4r/vendor/` fails; placing it
beside the calling shim in `d4r/` succeeds. Putting it beside the EXE alone is
insufficient when the call originates in the shim. The installer now stages
the local feature DLL beside the shim and sets `Libraries.NvngxDlssPath` to
the directory, as required by OptiScaler's discovery logic. Project identity,
DLL contents, numeric driver ABI and capability checks remain unchanged.
The standalone reproduction also exposed concurrent/reentrant OptiScaler
device hook installation: Detours error `0x10dd` clears a live trampoline and
execution jumps to null. Source patch `0004` serializes installation/removal
and guards recursion; `0005` forwards numeric NGX init failures instead of
reporting success. The exact patched frontend source is
`86b21ea` (base `45a2001`), with pinned submodules verified directly before build.
The quick launcher no longer attaches a debugger by default: Assassin's Creed
Black Flag Resynced refused the previous debugger launch (`DEADC0DE`). Empty
stdout/stderr now parse correctly. A clean early process exit with zero DLSS
frames is inconclusive, as in the SM2 log; an external Steam restart is not
tracked and must not be reported as successful hardware validation.
Validation on the available RX 9070 XT: all eleven target builds pass; CTest
20/20; summary parser 9/9 including all four submitted logs. The layout
regression passes two expected FeatureNotFound cases (direct + OptiScaler),
then three finite frames each for direct K, OptiScaler K and OptiScaler M.
Every required native layer executes; GPU NaN/Inf checks pass, CPU frame copies
and frame age are zero. Direct/OptiScaler K RGB matches bit-for-bit for all
three frames (max absolute/relative error 0). Reports are under
`test-results/issue-10-5958769640/`, including `ctest.log`,
`summary-parser.json`, `layout-final/` and `build-all-targets.json`.
All five exported OptiScaler patches apply to the pinned upstream sources and
reproduce its exact committed files (`optiScaler-patches.json`). Reproduce the
standalone layout/error/coverage/RGB checks with:
```powershell
.\scripts\windows\test-ngx-feature-layout.ps1 -DiagnosticRoot .\dist\windows-gpu-coverage-gfx1201 -HipRoot .\.tools\therock-10.2.0a20260929\_rocm_sdk_core -ZludaRoot .\dist\zluda-windows-gpu-coverage -NativeRoot .\dist\windows-gpu-coverage-game-gfx1201\d4r\native -OptiScalerDll .\dist\optiscaler-windows-d4r\OptiScaler.dll -NgxCore .\_nvngx.dll -DlssDll .\nvngx_dlss.dll -OutputDirectory .\test-results\ngx-feature-layout
```
These tests do not validate the reporter's gfx1200 kernels or game image.
The local Cyberpunk EXE is `D:\Games\Cyberpunk 2077\bin\x64\Cyberpunk2077.exe`;
it is located but is not launched or modified while the user is away.
2026-10-02 quick-test packaging: one universal ZIP shares the identical 2026-10-02 quick-test packaging: one universal ZIP shares the identical
OptiScaler/ZLUDA/HIP runtime and selects among all eleven previously compiled OptiScaler/ZLUDA/HIP runtime and selects among all eleven previously compiled
host/kernel targets using read-only HIP discovery. START-K.cmd / START-M.cmd host/kernel targets using read-only HIP discovery. START-K.cmd / START-M.cmd
use file pickers, remember validated game/DLL paths, back up replaced files, use file pickers, remember validated game/DLL paths, back up replaced files,
run synchronous output/crash diagnostics, and produce one report ZIP. run synchronous output diagnostics, and produce one report ZIP. The initial
release attached a debugger; the issue #10 follow-up above removes that default.
RESTORE-GAME.cmd works without HIP discovery or NVIDIA DLL selection. No RESTORE-GAME.cmd works without HIP discovery or NVIDIA DLL selection. No
runtime arithmetic changes or additional hardware-support claims are made. runtime arithmetic changes or additional hardware-support claims are made.
The short English instructions are in `windows-quick-test.txt`; developer The short English instructions are in `windows-quick-test.txt`; developer
@@ -0,0 +1,34 @@
diff --git a/OptiScaler/hooks/D3D12_Hooks.cpp b/OptiScaler/hooks/D3D12_Hooks.cpp
index 3b757be..05fc194 100644
--- a/OptiScaler/hooks/D3D12_Hooks.cpp
+++ b/OptiScaler/hooks/D3D12_Hooks.cpp
@@ -2217,11 +2217,20 @@ static HRESULT hkD3D12GetInterface(REFCLSID rclsid, REFIID riid, void** ppvDebug
return result;
}
+// Device discovery and NGX initialization can overlap. Installing d4r's
+// command hooks can also create a device and re-enter this function. Never
+// overwrite live Detours trampolines from a second installation attempt.
+static std::recursive_mutex deviceHookMutex;
+static bool deviceHookInstalling = false;
static void HookToDevice(ID3D12Device* InDevice)
{
- if (o_CreateSampler != nullptr || InDevice == nullptr)
+ std::lock_guard<std::recursive_mutex> lock(deviceHookMutex);
+ if (o_CreateSampler != nullptr || InDevice == nullptr || deviceHookInstalling)
return;
+ deviceHookInstalling = true;
+ struct InstallationGuard { ~InstallationGuard() { deviceHookInstalling = false; } } installationGuard;
+
LOG_DEBUG("Dx12");
// Get the vtable pointer
@@ -2334,6 +2343,7 @@ static void HookToDevice(ID3D12Device* InDevice)
static void UnhookDevice()
{
+ std::lock_guard<std::recursive_mutex> lock(deviceHookMutex);
LOG_FUNC();
DetourTransactionBegin();
@@ -0,0 +1,22 @@
diff --git a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
index 1f723f1..ef5ec77 100644
--- a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
+++ b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
@@ -184,6 +184,8 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_Init_Ext(unsigned long long InApp
if (result == NVSDK_NGX_Result_Success)
NVNGXProxy::SetDx12Inited(true);
+ else if (State::Instance().dlssExternalBackend)
+ return result;
}
else
{
@@ -254,6 +256,8 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_Init(unsigned long long InApplica
if (result == NVSDK_NGX_Result_Success)
NVNGXProxy::SetDx12Inited(true);
+ else if (State::Instance().dlssExternalBackend)
+ return result;
}
}
+12
View File
@@ -1,6 +1,18 @@
# Explicit Windows d4r backend # Explicit Windows d4r backend
Base: OptiScaler `45a2001303ddff632e279f77aef85ceede5832cb` (2026-09-30). Base: OptiScaler `45a2001303ddff632e279f77aef85ceede5832cb` (2026-09-30).
`0004` serializes D3D12 device hook installation/removal and guards recursive
device discovery while the d4r command backend installs. The previous frontend
can start two installations, fail a Detours transaction with `0x10dd`, clear
live original-function pointers and call null. This was reproduced in the
numeric-ID standalone harness; no game was required.
`0005` propagates numeric Init/Init_Ext failures for the explicitly selected
external backend, matching the existing ProjectID behavior. FeatureNotFound
must fail initialization instead of being hidden until repeated CreateFeature
attempts. `Libraries.NvngxDlssPath` takes a directory, not the DLL filename;
the Windows installer and standalone runner now use that directory.
The modifications to OptiScaler are GPL-3.0, matching its upstream license. The modifications to OptiScaler are GPL-3.0, matching its upstream license.
No NVIDIA DLL is supplied or patched. No NVIDIA DLL is supplied or patched.
+18 -3
View File
@@ -41,8 +41,23 @@ try {
if ($LASTEXITCODE) { throw "Source patch failed: $($patch.Name)" } if ($LASTEXITCODE) { throw "Source patch failed: $($patch.Name)" }
} }
} }
& git -C $SourceRoot submodule update --init --depth 1 # Verify existing checkouts directly. A complete local checkout does not
if ($LASTEXITCODE) { throw 'Pinned OptiScaler submodules failed.' } # need Git's shell-based submodule helper (or a network connection).
$submodulePaths=@(& git -C $SourceRoot config --file .gitmodules --get-regexp '\.path$' | ForEach-Object { ($_ -split ' ',2)[1] })
$submodules=@()
foreach ($path in $submodulePaths) {
$tree=(& git -C $SourceRoot ls-tree HEAD -- $path) -split '\s+',4
if ($tree.Count -lt 4 -or $tree[0] -ne '160000') { throw "Missing pinned submodule: $path" }
$checkout=Join-Path $SourceRoot $path
$actual=if (Test-Path -LiteralPath (Join-Path $checkout '.git')) { (& git -C $checkout rev-parse HEAD).Trim() } else { '' }
if ($actual -ne $tree[2]) {
& git -C $SourceRoot submodule update --init --depth 1 -- $path
if ($LASTEXITCODE) { throw "Pinned OptiScaler submodule failed: $path" }
$actual=(& git -C $checkout rev-parse HEAD).Trim()
if ($actual -ne $tree[2]) { throw "Wrong submodule revision: $path" }
}
$submodules+=(' '+$actual+' '+$path)
}
if (!$BuildToolsRoot -and (Test-Path -LiteralPath (Join-Path $repo '.tools/vs2022/MSBuild/Current/Bin/MSBuild.exe'))) { if (!$BuildToolsRoot -and (Test-Path -LiteralPath (Join-Path $repo '.tools/vs2022/MSBuild/Current/Bin/MSBuild.exe'))) {
$BuildToolsRoot = Join-Path $repo '.tools/vs2022' $BuildToolsRoot = Join-Path $repo '.tools/vs2022'
} }
@@ -72,7 +87,7 @@ try {
buildTools=$BuildToolsRoot; msbuildVersion=(& $msbuild -version -nologo | Select-Object -Last 1); buildTools=$BuildToolsRoot; msbuildVersion=(& $msbuild -version -nologo | Select-Object -Last 1);
patches=@($patches | ForEach-Object { @{name=$_.Name; sha256=(Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash} }); patches=@($patches | ForEach-Object { @{name=$_.Name; sha256=(Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash} });
dllSha256=(Get-FileHash -LiteralPath (Join-Path $InstallRoot 'OptiScaler.dll') -Algorithm SHA256).Hash; dllSha256=(Get-FileHash -LiteralPath (Join-Path $InstallRoot 'OptiScaler.dll') -Algorithm SHA256).Hash;
submodules=@(& git -C $SourceRoot submodule status)} submodules=$submodules}
$metadata | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $InstallRoot 'build-info.json') -Encoding UTF8 $metadata | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $InstallRoot 'build-info.json') -Encoding UTF8
Write-Host "Built OptiScaler with external Windows d4r backend: $InstallRoot" Write-Host "Built OptiScaler with external Windows d4r backend: $InstallRoot"
} finally { $env:PATH = $previousPath } } finally { $env:PATH = $previousPath }
+32
View File
@@ -0,0 +1,32 @@
# Parse completed diagnostic records. Empty streams are valid, including games
# that refuse a debugger before initializing the upscaler.
function Get-D4RGameSummary {
param([AllowEmptyString()][string]$Stdout='', [AllowEmptyString()][string]$Stderr='',
[AllowEmptyString()][string]$Debugger='', [int]$Preset=11, [double]$RunSeconds=0,
[bool]$ValidateOutput=$false, $GameCrash=$null)
$gameExit=[regex]::Match($Debugger, 'EXIT code=0x([0-9a-f]+)')
$exitCode=if ($gameExit.Success) { '0x'+$gameExit.Groups[1].Value } else { 'diagnostic_timeout' }
$frames=[regex]::Matches($Stdout, '(?m)^D4R_FRAME cpu_frame_copies=([0-9]+) frame_age=([0-9]+) interop_ngx_ms=([0-9.]+)\r?$')
$checks=[regex]::Matches($Stdout, '(?m)^D4R_OUTPUT_VALIDATION elements=([0-9]+) nan=([0-9]+) inf=([0-9]+) diagnostics_cpu_bytes=8\r?$')
$launches=[regex]::Matches($Stderr, '\[d4r-launch\] kernel="([^"]+)" backend=(native|translated)')
$kernels=@($launches | ForEach-Object { $_.Groups[1].Value+' '+$_.Groups[2].Value } | Group-Object | ForEach-Object {
$parts=$_.Name.Split(' '); @{kernel=$parts[0]; backend=$parts[1]; launches=$_.Count}
})
$capabilities=@([regex]::Matches($Stdout, '(?m)^D4R_CAPABILITIES available=(-?[0-9]+) feature_init=(0x[0-9a-f]+) needs_driver=(-?[0-9]+)\r?$') |
ForEach-Object { $_.Groups[2].Value } | Select-Object -Unique)
$session=if ($frames.Count) { 'completed-dlss-frames' }
elseif ($capabilities -contains '0xbad00004') { 'ngx-feature-not-found' }
elseif ($capabilities.Count -and $capabilities -ne '0x00000001') { 'ngx-capability-failure' }
elseif ($exitCode -eq '0x0') { 'process-exited-without-dlss-frames' }
elseif ($exitCode -eq 'diagnostic_timeout') { 'diagnostic-timeout-without-dlss-frames' }
else { 'process-failed-before-dlss-frames' }
return [ordered]@{exitCode=$exitCode; session=$session; featureInitResults=$capabilities;
runSeconds=$RunSeconds; preset=$Preset; gameCrash=$GameCrash;
nativeLaunches=([regex]::Matches($Stderr, '\[d4r-launch\].*backend=native')).Count;
completedFrames=$frames.Count; outputValidationRequested=$ValidateOutput;
outputGpuChecks=$checks.Count; kernels=$kernels;
nonfiniteOutputs=@($checks | Where-Object { $_.Groups[2].Value -ne '0' -or $_.Groups[3].Value -ne '0' }).Count;
previousFrameOutputs=@($frames | Where-Object { $_.Groups[2].Value -ne '0' }).Count;
cpuImageCopyFrames=@($frames | Where-Object { $_.Groups[1].Value -ne '0' }).Count;
failures=([regex]::Matches($Stderr, 'D4R_[A-Z0-9_]*FAILURE\b')).Count}
}
+1
View File
@@ -66,6 +66,7 @@ if ($entries.Count -ne 16) { throw "Expected 11 K and 5 M native objects; found
Set-Content -LiteralPath (Join-Path $PackageRoot 'd4r/native/d4r-kernels.txt') -Encoding ASCII Set-Content -LiteralPath (Join-Path $PackageRoot 'd4r/native/d4r-kernels.txt') -Encoding ASCII
$stagedFiles.Add('d4r/native/d4r-kernels.txt') $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/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-target.ps1') 'gpu-target.ps1'
Stage (Join-Path $repo 'scripts/windows/gpu-preflight.ps1') 'gpu-preflight.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' Stage (Join-Path $repo 'tools/windows/gpu-targets.json') 'gpu-targets.json'
@@ -51,7 +51,7 @@ foreach ($gpu in Get-D4RGpuTargets) {
if (!$baseline) { $baseline=$metadata } if (!$baseline) { $baseline=$metadata }
if ($metadata.dlssSha256 -ne $baseline.dlssSha256 -or $metadata.zludaBuild.sourceCommit -ne $baseline.zludaBuild.sourceCommit) { throw 'Input packages do not use the same validated dependencies.' } if ($metadata.dlssSha256 -ne $baseline.dlssSha256 -or $metadata.zludaBuild.sourceCommit -ne $baseline.zludaBuild.sourceCommit) { throw 'Input packages do not use the same validated dependencies.' }
} }
foreach ($name in @('windows-game.ps1','gpu-target.ps1','gpu-preflight.ps1')) { foreach ($name in @('windows-game.ps1','game-summary.ps1','gpu-target.ps1','gpu-preflight.ps1')) {
$entry=Stage (Join-Path $PSScriptRoot $name) "files/common/$name" $entry=Stage (Join-Path $PSScriptRoot $name) "files/common/$name"
$common+=@{path=$name; sha256=$entry.sha256; version=$entry.version} $common+=@{path=$name; sha256=$entry.sha256; version=$entry.version}
} }
+11 -4
View File
@@ -2,7 +2,8 @@
param( param(
[ValidateSet('run','install','restore')][string]$Action='run', [ValidateSet('run','install','restore')][string]$Action='run',
[ValidateSet(11,13)][int]$Preset=11, [ValidateSet(11,13)][int]$Preset=11,
[string]$GameExe, [string]$NgxCore, [string]$DlssDll [string]$GameExe, [string]$NgxCore, [string]$DlssDll,
[switch]$CaptureExceptions
) )
$ErrorActionPreference='Stop' $ErrorActionPreference='Stop'
$root=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..')) $root=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
@@ -126,7 +127,7 @@ try {
Write-Host 'In the game: select DLSS if available; otherwise select FSR/XeSS to let OptiScaler intercept it.' Write-Host 'In the game: select DLSS if available; otherwise select FSR/XeSS to let OptiScaler intercept it.'
Write-Host 'The first launch compiles shaders and can take several minutes. Then play for 30 seconds and EXIT the game normally.' Write-Host 'The first launch compiles shaders and can take several minutes. Then play for 30 seconds and EXIT the game normally.'
& (Join-Path $selectedPackage 'windows-game.ps1') -Action $Action -GameExe $GameExe -NgxCore $NgxCore -DlssDll $DlssDll ` & (Join-Path $selectedPackage 'windows-game.ps1') -Action $Action -GameExe $GameExe -NgxCore $NgxCore -DlssDll $DlssDll `
-Preset $Preset -ValidateOutput -CaptureExceptions -OutputDirectory $output -Preset $Preset -ValidateOutput -CaptureExceptions:$CaptureExceptions -OutputDirectory $output
if ($Action -eq 'install') { $status.status='installed-only' } if ($Action -eq 'install') { $status.status='installed-only' }
else { else {
$summary=Get-Content -LiteralPath (Join-Path $output 'summary.json') -Raw | ConvertFrom-Json $summary=Get-Content -LiteralPath (Join-Path $output 'summary.json') -Raw | ConvertFrom-Json
@@ -143,7 +144,13 @@ try {
@($required).Count -eq $(if ($Preset -eq 11) { 11 } else { 5 }) -and @($required).Count -eq $(if ($Preset -eq 11) { 11 } else { 5 }) -and
$status.missingNativeKernels.Count -eq 0 -and $status.translatedNativeKernels.Count -eq 0 $status.missingNativeKernels.Count -eq 0 -and $status.translatedNativeKernels.Count -eq 0
$status.hardwareValidationCompleted=[bool]$passed $status.hardwareValidationCompleted=[bool]$passed
$status.status=if ($passed) { 'backend-checks-passed-image-still-needs-user-review' } else { 'backend-checks-failed-or-no-dlss-frames' } $status.status=if ($passed) { 'backend-checks-passed-image-still-needs-user-review' }
elseif ($summary.completedFrames -eq 0 -and $summary.exitCode -eq '0x0' -and $summary.failures -eq 0) { 'inconclusive-process-exited-without-dlss-frames' }
else { 'backend-checks-failed-or-no-dlss-frames' }
$status.session=$summary.session
if ($status.status -eq 'inconclusive-process-exited-without-dlss-frames') {
Write-Host 'The launched process exited without a DLSS frame. A Steam/launcher restart is not tracked by this test. Keep Steam open and select the actual game executable. Send the ZIP if it restarts again.' -ForegroundColor Yellow
}
Write-Host $(if ($passed) { 'Backend checks PASSED. Please also report whether the image looked correct.' } else { 'Test did not pass. Send the diagnostic ZIP; do not guess from the FPS.' }) -ForegroundColor $(if ($passed) { 'Green' } else { 'Yellow' }) Write-Host $(if ($passed) { 'Backend checks PASSED. Please also report whether the image looked correct.' } else { 'Test did not pass. Send the diagnostic ZIP; do not guess from the FPS.' }) -ForegroundColor $(if ($passed) { 'Green' } else { 'Yellow' })
} }
} catch { } catch {
@@ -163,4 +170,4 @@ try {
Write-Host 'Also tell us: GPU model, game name, K or M, and whether the image was correct.' Write-Host 'Also tell us: GPU model, game name, K or M, and whether the image was correct.'
Write-Host 'Close the game before running RESTORE-GAME.cmd.' Write-Host 'Close the game before running RESTORE-GAME.cmd.'
} }
if ($status.status -eq 'failed' -or $status.status -eq 'backend-checks-failed-or-no-dlss-frames') { exit 1 } if ($status.status -eq 'failed' -or $status.status -eq 'backend-checks-failed-or-no-dlss-frames' -or $status.status.StartsWith('inconclusive-')) { exit 1 }
+38
View File
@@ -0,0 +1,38 @@
[CmdletBinding()]
param([string]$LogRoot, [string]$OutputDirectory)
$ErrorActionPreference='Stop'
. (Join-Path $PSScriptRoot 'game-summary.ps1')
$cases=@(
@{name='empty-streams-debugger-refused'; stdout=''; stderr=''; debugger='EXIT code=0xdeadc0de'; session='process-failed-before-dlss-frames'},
@{name='launcher-exit'; stdout=''; stderr=''; debugger='EXIT code=0x0'; session='process-exited-without-dlss-frames'},
@{name='feature-not-found'; stdout='D4R_CAPABILITIES available=0 feature_init=0xbad00004 needs_driver=0'; stderr='D4R_WINDOWS_FAILURE unavailable'; debugger='EXIT code=0xc0000409'; session='ngx-feature-not-found'},
@{name='complete-frame'; stdout="D4R_FRAME cpu_frame_copies=0 frame_age=0 interop_ngx_ms=12.4`nD4R_OUTPUT_VALIDATION elements=3 nan=0 inf=0 diagnostics_cpu_bytes=8";
stderr='[d4r-launch] kernel="dltss_pwin_enc0_layer" backend=native'; debugger='EXIT code=0x0'; session='completed-dlss-frames'},
@{name='truncated-frame'; stdout='D4R_FRAME cpu_frame_copies=0 frame_age=0 interop_ngx_ms='; stderr=''; debugger=''; session='diagnostic-timeout-without-dlss-frames'}
)
$results=@()
foreach ($case in $cases) {
$summary=Get-D4RGameSummary -Stdout $case.stdout -Stderr $case.stderr -Debugger $case.debugger
if ($summary.session -ne $case.session) { throw "Wrong session classification: $($case.name)" }
if ($case.name -eq 'complete-frame') {
if ($summary.completedFrames -ne 1 -or $summary.outputGpuChecks -ne 1 -or $summary.nativeLaunches -ne 1 -or
$summary.nonfiniteOutputs -or $summary.previousFrameOutputs -or $summary.cpuImageCopyFrames) { throw 'Complete frame counters are wrong.' }
} elseif ($summary.completedFrames -ne 0) { throw 'A failed or incomplete run was counted as a frame.' }
$results+=@{case=$case.name; passed=$true; summary=$summary}
}
if ($LogRoot) {
foreach ($entry in @{ 'acbf-k'='process-failed-before-dlss-frames'; 'cyberpunk-k'='ngx-feature-not-found';
'cyberpunk-m'='ngx-feature-not-found'; 'sm2-k'='process-exited-without-dlss-frames' }.GetEnumerator()) {
$dir=Get-ChildItem -LiteralPath (Join-Path $LogRoot $entry.Key) -Directory | Select-Object -First 1
$summary=Get-D4RGameSummary -Stdout ([IO.File]::ReadAllText((Join-Path $dir.FullName 'd4r.stdout.log'))) `
-Stderr ([IO.File]::ReadAllText((Join-Path $dir.FullName 'd4r.stderr.log'))) `
-Debugger ([IO.File]::ReadAllText((Join-Path $dir.FullName 'debugger.log')))
if ($summary.session -ne $entry.Value -or $summary.completedFrames -ne 0 -or $summary.nativeLaunches -ne 0) { throw "Unexpected classification for actual report $($entry.Key)" }
$results+=@{case='issue-10-'+$entry.Key; passed=$true; summary=$summary}
}
}
if ($OutputDirectory) {
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$results | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary-parser.json') -Encoding UTF8
}
Write-Host "PASS GAME_SUMMARY cases=$($results.Count)"
+160
View File
@@ -0,0 +1,160 @@
# Reproduce numeric NGX feature discovery without launching or modifying a game.
# The private NVIDIA DLL staging directory is deleted; reports contain logs only.
[CmdletBinding()]
param([Parameter(Mandatory=$true)][string]$DiagnosticRoot,
[Parameter(Mandatory=$true)][string]$HipRoot,
[Parameter(Mandatory=$true)][string]$ZludaRoot,
[Parameter(Mandatory=$true)][string]$NativeRoot,
[Parameter(Mandatory=$true)][string]$OptiScalerDll,
[Parameter(Mandatory=$true)][string]$NgxCore,
[Parameter(Mandatory=$true)][string]$DlssDll,
[Parameter(Mandatory=$true)][string]$OutputDirectory,
[UInt64]$ApplicationId=100152211)
$ErrorActionPreference='Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1')
foreach ($name in @('DiagnosticRoot','HipRoot','ZludaRoot','NativeRoot','OptiScalerDll','NgxCore','DlssDll')) {
Set-Variable -Name $name -Value ((Get-Item -LiteralPath (Get-Variable -Name $name -ValueOnly)).FullName)
}
$arch=Get-D4RGpuTarget $DiagnosticRoot
$OutputDirectory=[IO.Path]::GetFullPath($OutputDirectory)
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
$runtime=Join-Path ([IO.Path]::GetTempPath()) ('d4r-feature-layout-'+[Guid]::NewGuid().ToString('N'))
$settings=@{HIP_PATH=$HipRoot; PATH="$HipRoot\bin;$ZludaRoot;$env:PATH";
D4R_NVAPI_BACKEND=(Join-Path $ZludaRoot 'nvapi64.dll'); ZLUDA_CUDA_LIB=(Join-Path $ZludaRoot 'nvcuda.dll');
D4R_ZLUDA_NATIVE_DIR=$NativeRoot; D4R_ZLUDA_VERBOSE='1';
D4R_ZLUDA_WMMA='1'; D4R_ZLUDA_WMMA_F16_REFERENCE='1'; D4R_ZLUDA_WMMA_FP8='1'; D4R_ZLUDA_WMMA_FP8_NATIVE='0';
D4R_VALIDATE_OUTPUT='1'; D4R_DIAG_DIR=$null; ZLUDA_LOG_DIR=$null; D4R_FORMAT_MODULE=$null;
D4R_ASYNC_INTEROP=$null; D4R_BATCH_INPUT_COPIES=$null; D4R_D3D12_COMMAND_BACKEND=$null}
$old=@{}; $results=@()
try {
foreach ($key in $settings.Keys) { $old[$key]=[Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') }
foreach ($dir in @('app','d4r','d4r/vendor')) { New-Item -ItemType Directory -Force (Join-Path $runtime $dir) | Out-Null }
$exe=Join-Path $runtime 'app/d4r_d3d12_dlss_probe.exe'
$shim=Join-Path $runtime 'd4r/_nvngx.dll'
$core=Join-Path $runtime 'd4r/vendor/_nvngx.dll'
$feature=Join-Path $runtime 'd4r/vendor/nvngx_dlss.dll'
Copy-Item -LiteralPath (Join-Path $DiagnosticRoot 'bin/d4r_d3d12_dlss_probe.exe') -Destination $exe
Copy-Item -LiteralPath (Join-Path $DiagnosticRoot 'bin/d4r_nvngx.dll') -Destination $shim
Copy-Item -LiteralPath (Join-Path $DiagnosticRoot "bin/pixel_convert_${arch}.hsaco") -Destination (Split-Path $shim)
Copy-Item -LiteralPath $NgxCore -Destination $core
Copy-Item -LiteralPath $DlssDll -Destination $feature
$opti=Join-Path $runtime 'app/OptiScaler.dll'
Copy-Item -LiteralPath $OptiScalerDll -Destination $opti
$cases=@(@{name='vendor-only-negative'; preset=11; frontend='d4r'; expectedExit=4},
@{name='optiscaler-vendor-only-negative'; preset=11; frontend='optiscaler'; expectedExit=4},
@{name='caller-dll-k'; preset=11; frontend='d4r'; expectedExit=0},
@{name='optiscaler-caller-k'; preset=11; frontend='optiscaler'; expectedExit=0},
@{name='optiscaler-caller-m'; preset=13; frontend='optiscaler'; expectedExit=0})
foreach ($case in $cases) {
$out=Join-Path $OutputDirectory $case.name
New-Item -ItemType Directory -Force $out | Out-Null
if ($case.expectedExit -eq 0) {
$feature=Join-Path $runtime 'd4r/nvngx_dlss.dll'
if (!(Test-Path -LiteralPath $feature)) { Copy-Item -LiteralPath $DlssDll -Destination $feature }
}
$env:D4R_DIAG_DIR=$out; $env:ZLUDA_LOG_DIR=Join-Path $out 'zluda-trace'
$env:D4R_FORMAT_MODULE=Join-Path $runtime "d4r/pixel_convert_${arch}.hsaco"
$module=if ($case.frontend -eq 'optiscaler') { $opti } else { $shim }
if ($case.frontend -eq 'optiscaler') {
@"
[Upscalers]
Dx12Upscaler=dlss
[FrameGen]
Enabled=false
FGInput=nofg
FGOutput=nofg
[DLSS]
Enabled=true
AllowExternalBackend=true
RenderPresetOverride=true
RenderPresetForAll=$($case.preset)
UseGenericAppIdWithDlss=false
[Libraries]
NvngxPath=$(Split-Path $shim)
NvngxDlssPath=$(Split-Path $feature)
NvngxFeaturePath=$(Split-Path $feature)
[Hotfix]
RestoreComputeSignature=false
RestoreGraphicSignature=false
ExtendedStateRestore=false
[Log]
LogToFile=true
LogLevel=0
[Menu]
DisableSplash=true
"@ | Set-Content -LiteralPath (Join-Path $runtime 'app/OptiScaler.ini') -Encoding UTF8
}
$arguments=@('--hip-root',$HipRoot,'--cuda-dll',(Join-Path $ZludaRoot 'nvcuda.dll'),
'--module',$module,'--ngx-core',$core,'--dlss-dll',$feature,
'--nvapi-dll',(Join-Path $DiagnosticRoot 'nvapi-compat/nvapi64.dll'),
'--ngx-app-id',"$ApplicationId",'--ngx-abi','driver','--ngx-frontend',$case.frontend,
'--interop-mode','command-list','--iterations','3','--preset',"$($case.preset)")
$info=New-Object Diagnostics.ProcessStartInfo
# Capture harness faults outside OptiScaler's loader hooks. Games use
# the no-debug launcher by default; this developer reproducer is separate.
$arguments=@('--output-directory',$out,'--',$exe)+$arguments
$info.FileName=Join-Path $DiagnosticRoot 'bin/d4r_debug_launcher.exe'; $info.WorkingDirectory=Split-Path $exe
$info.Arguments=(@($arguments | ForEach-Object { if ($_.Contains('"')) { throw 'Unsupported quote in test path.' }; '"'+$_+'"' }) -join ' ')
$info.UseShellExecute=$false; $info.CreateNoWindow=$true; $info.RedirectStandardOutput=$true; $info.RedirectStandardError=$true
foreach ($key in $settings.Keys) { if (![Environment]::GetEnvironmentVariable($key,'Process')) { $info.EnvironmentVariables.Remove($key) } }
$process=New-Object Diagnostics.Process; $process.StartInfo=$info
try {
[void]$process.Start(); $stdout=$process.StandardOutput.ReadToEndAsync(); $stderr=$process.StandardError.ReadToEndAsync()
if (!$process.WaitForExit(240000)) { $process.Kill(); $process.WaitForExit(); throw "NGX layout test timed out: $($case.name)" }
$log=$stdout.GetAwaiter().GetResult(); $errorLog=$stderr.GetAwaiter().GetResult()
[IO.File]::WriteAllText((Join-Path $out 'stdout.log'),$log)
[IO.File]::WriteAllText((Join-Path $out 'stderr.log'),$errorLog)
if ($process.ExitCode -ne $case.expectedExit) { throw "Unexpected exit $($process.ExitCode): $($case.name), see $out" }
if ($case.expectedExit) {
if ($log -notmatch 'D4R_CAPABILITIES available=0 feature_init=0xbad00004' -or $log -match 'D4R_FRAME ') { throw 'Old vendor-only layout did not reproduce FeatureNotFound.' }
if ($case.frontend -eq 'optiscaler' -and $errorLog -notmatch 'FAIL D3D12_DLSS D3D12 Init NGX=') { throw 'OptiScaler swallowed the numeric backend initialization failure.' }
} else {
if ($log -notmatch 'D4R_CAPABILITIES available=1 feature_init=0x00000001 needs_driver=0' -or
([regex]::Matches($log,'D3D12_OUTPUT preset=\d+ frame=\d+ finite=1')).Count -ne 3 -or
([regex]::Matches($log,'D4R_OUTPUT_VALIDATION elements=\d+ nan=0 inf=0 diagnostics_cpu_bytes=8')).Count -ne 3 -or
$log -notmatch 'PASS D3D12_DLSS .*fast_path_cpu_copies=0 frame_age=0' -or $errorLog -match 'D4R_WINDOWS_FAILURE') { throw 'Corrected feature layout did not complete finite zero-copy frames.' }
$required=@(Get-Content -LiteralPath (Join-Path $NativeRoot 'd4r-kernels.txt') | Where-Object {
if ($case.preset -eq 11) { $_ -match '^dltss_pwin_' } else { $_ -match '^rrlite_' }
} | ForEach-Object { $_.Split(' ')[0] })
if ($required.Count -ne $(if ($case.preset -eq 11) { 11 } else { 5 })) { throw 'Incomplete native manifest.' }
foreach ($kernel in $required) {
if ($errorLog -notmatch ('\[d4r-launch\] kernel="'+[regex]::Escape($kernel)+'" backend=native') -or
$errorLog -match ('\[d4r-launch\] kernel="'+[regex]::Escape($kernel)+'" backend=translated')) { throw "Missing native network execution: $kernel" }
}
if ($case.frontend -eq 'd4r' -and $log -notmatch 'D3D12_REQUIREMENTS warp_supported=0 enumeration_success=1') { throw 'WARP adapter enumeration regression failed.' }
}
$results+=@{case=$case.name; passed=$true; expectedExit=$case.expectedExit; application=$ApplicationId; preset=$case.preset; architecture=$arch}
Write-Host "PASS NGX_FEATURE_LAYOUT $($case.name)"
} finally {
if ($case.frontend -eq 'optiscaler') {
Get-ChildItem -LiteralPath (Split-Path $exe) -Filter '*.log' -File | ForEach-Object {
Copy-Item -LiteralPath $_.FullName -Destination $out -Force
}
}
$process.Dispose()
}
}
for ($frame=0; $frame -lt 3; $frame++) {
$direct=[IO.File]::ReadAllBytes((Join-Path $OutputDirectory "caller-dll-k/output-$frame.rgba16f"))
$frontend=[IO.File]::ReadAllBytes((Join-Path $OutputDirectory "optiscaler-caller-k/output-$frame.rgba16f"))
if ($direct.Length -ne $frontend.Length -or $direct.Length -ne 512*288*8) { throw 'RGB comparison dimensions differ.' }
for ($pixel=0; $pixel -lt $direct.Length; $pixel+=8) {
if (([BitConverter]::ToUInt64($direct,$pixel) -band [UInt64]0xffffffffffff) -ne
([BitConverter]::ToUInt64($frontend,$pixel) -band [UInt64]0xffffffffffff)) { throw "K frontend RGB mismatch: frame=$frame offset=$pixel" }
}
}
$results+=@{case='k-direct-optiscaler-rgb'; passed=$true; frames=3; rgbByteExact=$true; maxAbsoluteError=0; maxRelativeError=0}
Write-Host 'PASS NGX_FEATURE_LAYOUT k_direct_optiscaler_rgb exact=1 max_abs=0 max_rel=0'
} finally {
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
$results | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'feature-layout.json') -Encoding UTF8
$resolved=[IO.Path]::GetFullPath($runtime)
$temp=[IO.Path]::GetFullPath([IO.Path]::GetTempPath()).TrimEnd('\')+'\'
if (!$resolved.StartsWith($temp,[StringComparison]::OrdinalIgnoreCase) -or [IO.Path]::GetFileName($resolved) -notmatch '^d4r-feature-layout-[0-9a-f]{32}$') { throw 'Refusing cleanup outside private NGX staging.' }
if (Test-Path -LiteralPath $resolved) {
foreach ($attempt in 1..10) {
try { Remove-Item -LiteralPath $resolved -Recurse -Force; break }
catch { if ($attempt -eq 10) { throw }; Start-Sleep -Milliseconds 300 }
}
}
}
+29 -2
View File
@@ -29,11 +29,15 @@ $results=@()
$exe=Fixture ('space GPU '+[char]0x03a9+' test') $exe=Fixture ('space GPU '+[char]0x03a9+' test')
$game=Split-Path $exe $game=Split-Path $exe
$before=@{} $before=@{}
foreach ($name in @('dxgi.dll','OptiScaler.ini')) { foreach ($name in @('dxgi.dll','OptiScaler.ini','nvngx_dlss.dll')) {
Set-Content -LiteralPath (Join-Path $game $name) "original $name" -Encoding ASCII Set-Content -LiteralPath (Join-Path $game $name) "original $name" -Encoding ASCII
$before[$name]=(Get-FileHash -LiteralPath (Join-Path $game $name)).Hash $before[$name]=(Get-FileHash -LiteralPath (Join-Path $game $name)).Hash
} }
Invoke-Child 'install' @('-Action','install','-GameExe',$exe,'-NgxCore',$NgxCore,'-DlssDll',$DlssDll) Invoke-Child 'install' @('-Action','install','-GameExe',$exe,'-NgxCore',$NgxCore,'-DlssDll',$DlssDll)
if ((Get-FileHash -LiteralPath (Join-Path $game 'd4r/nvngx_dlss.dll')).Hash -ne (Get-FileHash -LiteralPath $DlssDll).Hash) {
throw 'Numeric NGX driver feature DLL was not staged beside the shim.'
}
if ((Get-FileHash -LiteralPath (Join-Path $game 'nvngx_dlss.dll')).Hash -ne $before['nvngx_dlss.dll']) { throw 'Original application DLSS DLL was changed.' }
$saved=Get-Content -LiteralPath (Join-Path $PackageRoot 'work/settings.json') -Raw | ConvertFrom-Json $saved=Get-Content -LiteralPath (Join-Path $PackageRoot 'work/settings.json') -Raw | ConvertFrom-Json
if ($saved.game -ne $exe -or $saved.architecture -ne 'gfx1201') { throw 'GPU detection or remembered selection failed on this RX9070XT fixture.' } if ($saved.game -ne $exe -or $saved.architecture -ne 'gfx1201') { throw 'GPU detection or remembered selection failed on this RX9070XT fixture.' }
# Restore uses remembered game selection and needs neither HIP nor NVIDIA DLLs. # Restore uses remembered game selection and needs neither HIP nor NVIDIA DLLs.
@@ -41,6 +45,29 @@ Invoke-Child 'restore' @('-Action','restore')
foreach ($name in $before.Keys) { if ((Get-FileHash -LiteralPath (Join-Path $game $name)).Hash -ne $before[$name]) { throw 'Original game bytes not restored.' } } foreach ($name in $before.Keys) { if ((Get-FileHash -LiteralPath (Join-Path $game $name)).Hash -ne $before[$name]) { throw 'Original game bytes not restored.' } }
if (Test-Path -LiteralPath (Join-Path $game 'd4r')) { throw 'Runtime directory remains after restore.' } if (Test-Path -LiteralPath (Join-Path $game 'd4r')) { throw 'Runtime directory remains after restore.' }
$results+=@{case='auto-gfx1201-install-remember-restore'; passed=$true; gpuKernelsExecuted=$false} $results+=@{case='auto-gfx1201-install-remember-restore'; passed=$true; gpuKernelsExecuted=$false}
# A harmless child refuses attached debuggers and produces an empty stderr.
# The default launcher must allow it to exit and must report no DLSS session.
$exe=Join-Path (Join-Path $OutputDirectory 'launcher-fixture') 'Fixture.exe'
New-Item -ItemType Directory -Force (Split-Path $exe) | Out-Null
Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
public static class D4RLauncherFixture {
[DllImport("kernel32.dll")] private static extern bool IsDebuggerPresent();
public static int Main(string[] args) { return IsDebuggerPresent() ? 91 : 0; }
}
'@ -OutputAssembly $exe -OutputType ConsoleApplication
Invoke-Child 'run-no-debug' @('-Action','run','-GameExe',$exe,'-NgxCore',$NgxCore,'-DlssDll',$DlssDll) 1
$report=Get-ChildItem -LiteralPath (Join-Path $PackageRoot 'results') -Directory | Sort-Object Name -Descending | Select-Object -First 1
$status=Get-Content -LiteralPath (Join-Path $report.FullName 'quick-test.json') -Raw | ConvertFrom-Json
$environment=Get-Content -LiteralPath (Join-Path $report.FullName 'environment.json') -Raw | ConvertFrom-Json
if ($environment.attachedDebugger -or $status.hardwareValidationCompleted -or
$status.status -ne 'inconclusive-process-exited-without-dlss-frames' -or $status.gameSummary.exitCode -ne '0x0' -or
(Get-Item -LiteralPath (Join-Path $report.FullName 'd4r.stderr.log')).Length -ne 0) {
throw 'Default launch attaches a debugger, mishandles empty stderr or falsely validates a launcher exit.'
}
Invoke-Child 'restore-launcher-fixture' @('-Action','restore','-GameExe',$exe)
$results+=@{case='run-no-debug-empty-stderr-no-false-pass'; passed=$true; gpuKernelsExecuted=$false}
foreach ($case in @('wrong-core','wrong-dlss','vkd3d')) { foreach ($case in @('wrong-core','wrong-dlss','vkd3d')) {
$exe=Fixture $case; $game=Split-Path $exe $exe=Fixture $case; $game=Split-Path $exe
$core=if ($case -eq 'wrong-core') { Join-Path $PackageRoot 'files/targets/gfx1201/d4r/_nvngx.dll' } else { $NgxCore } $core=if ($case -eq 'wrong-core') { Join-Path $PackageRoot 'files/targets/gfx1201/d4r/_nvngx.dll' } else { $NgxCore }
@@ -64,4 +91,4 @@ try {
$results+=@{case='tampered-package'; passed=$true; rejectedBeforeGameChanges=$true; gpuKernelsExecuted=$false} $results+=@{case='tampered-package'; passed=$true; rejectedBeforeGameChanges=$true; gpuKernelsExecuted=$false}
} finally { [IO.File]::WriteAllBytes($readme,$original) } } finally { [IO.File]::WriteAllBytes($readme,$original) }
$results | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8 $results | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
Write-Host "PASS QUICK_TEST_FIXTURES positive=1 negative=4 real_game_untouched=1 Report: $OutputDirectory" Write-Host "PASS QUICK_TEST_FIXTURES cases=$($results.Count) real_game_untouched=1 Report: $OutputDirectory"
+1 -1
View File
@@ -314,7 +314,7 @@ RenderPresetForAll=$Preset
UseGenericAppIdWithDlss=false UseGenericAppIdWithDlss=false
[Libraries] [Libraries]
NvngxPath=$bridgeDirectory NvngxPath=$bridgeDirectory
NvngxDlssPath=$localDlss NvngxDlssPath=$ngxRuntimeDirectory
NvngxFeaturePath=$ngxRuntimeDirectory NvngxFeaturePath=$ngxRuntimeDirectory
[Hotfix] [Hotfix]
RestoreComputeSignature=false RestoreComputeSignature=false
+13 -22
View File
@@ -28,6 +28,7 @@ param(
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'gpu-target.ps1') . (Join-Path $PSScriptRoot 'gpu-target.ps1')
. (Join-Path $PSScriptRoot 'gpu-preflight.ps1') . (Join-Path $PSScriptRoot 'gpu-preflight.ps1')
. (Join-Path $PSScriptRoot 'game-summary.ps1')
if ($ProfileGpuBoundary -and !$AsyncInterop) { throw '-ProfileGpuBoundary requires -AsyncInterop.' } if ($ProfileGpuBoundary -and !$AsyncInterop) { throw '-ProfileGpuBoundary requires -AsyncInterop.' }
if ($ProfileKernels -and $ProfileKernelsDeferred) { throw 'Choose either serializing or deferred kernel profiling.' } if ($ProfileKernels -and $ProfileKernelsDeferred) { throw 'Choose either serializing or deferred kernel profiling.' }
if ($ProfileLegacyStream -and !$ProfileKernelsDeferred) { throw '-ProfileLegacyStream requires -ProfileKernelsDeferred.' } if ($ProfileLegacyStream -and !$ProfileKernelsDeferred) { throw '-ProfileLegacyStream requires -ProfileKernelsDeferred.' }
@@ -144,7 +145,10 @@ if ($LocalTextureKernels) {
} }
# Private local test copies only; these are never added to the public package. # Private local test copies only; these are never added to the public package.
InstallFile $NgxCore 'd4r/vendor/_nvngx.dll' InstallFile $NgxCore 'd4r/vendor/_nvngx.dll'
InstallFile $DlssDll 'd4r/vendor/nvngx_dlss.dll' # The numeric driver Init ABI searches the calling module's directory; it does
# not accept FeatureCommonInfo. Keep the exact local feature DLL alongside the
# d4r shim. Putting it only beside the core in vendor/ produces FeatureNotFound.
InstallFile $DlssDll 'd4r/nvngx_dlss.dll'
$bridge = GamePath 'd4r' $bridge = GamePath 'd4r'
$vendor = GamePath 'd4r/vendor' $vendor = GamePath 'd4r/vendor'
$ini = @" $ini = @"
@@ -162,8 +166,8 @@ RenderPresetForAll=$Preset
UseGenericAppIdWithDlss=false UseGenericAppIdWithDlss=false
[Libraries] [Libraries]
NvngxPath=$bridge NvngxPath=$bridge
NvngxDlssPath=$vendor\nvngx_dlss.dll NvngxDlssPath=$bridge
NvngxFeaturePath=$vendor NvngxFeaturePath=$bridge
[Hotfix] [Hotfix]
RestoreComputeSignature=false RestoreComputeSignature=false
RestoreGraphicSignature=false RestoreGraphicSignature=false
@@ -187,7 +191,7 @@ $settings = @{
D4R_HIP_ROOT=(GamePath 'd4r/hip'); HIP_PATH=(GamePath 'd4r/hip'); D4R_HIP_ROOT=(GamePath 'd4r/hip'); HIP_PATH=(GamePath 'd4r/hip');
D4R_NVCUDA_DLL=(GamePath 'd4r/zluda/nvcuda.dll'); ZLUDA_CUDA_LIB=(GamePath 'd4r/zluda/nvcuda.dll'); D4R_NVCUDA_DLL=(GamePath 'd4r/zluda/nvcuda.dll'); ZLUDA_CUDA_LIB=(GamePath 'd4r/zluda/nvcuda.dll');
D4R_NVAPI_DLL=(GamePath 'd4r/nvapi/nvapi64.dll'); D4R_NVAPI_BACKEND=(GamePath 'd4r/zluda/nvapi64.dll'); D4R_NVAPI_DLL=(GamePath 'd4r/nvapi/nvapi64.dll'); D4R_NVAPI_BACKEND=(GamePath 'd4r/zluda/nvapi64.dll');
D4R_NGX_CORE=(GamePath 'd4r/vendor/_nvngx.dll'); D4R_DLSS_DLL=(GamePath 'd4r/vendor/nvngx_dlss.dll'); D4R_NGX_CORE=(GamePath 'd4r/vendor/_nvngx.dll'); D4R_DLSS_DLL=(GamePath 'd4r/nvngx_dlss.dll');
D4R_FORMAT_MODULE=(GamePath "d4r/pixel_convert_${GpuArch}.hsaco"); D4R_FORMAT_MODULE=(GamePath "d4r/pixel_convert_${GpuArch}.hsaco");
D4R_ZLUDA_NATIVE_DIR=(GamePath 'd4r/native'); D4R_D3D12_COMMAND_BACKEND='1'; D4R_ZLUDA_VERBOSE='1'; D4R_ZLUDA_NATIVE_DIR=(GamePath 'd4r/native'); D4R_D3D12_COMMAND_BACKEND='1'; D4R_ZLUDA_VERBOSE='1';
D4R_ZLUDA_WMMA='1'; D4R_ZLUDA_WMMA_FP8='1'; D4R_ZLUDA_WMMA_FP8_NATIVE='0'; D4R_ZLUDA_WMMA_F16_REFERENCE='1'; D4R_ZLUDA_WMMA='1'; D4R_ZLUDA_WMMA_FP8='1'; D4R_ZLUDA_WMMA_FP8_NATIVE='0'; D4R_ZLUDA_WMMA_F16_REFERENCE='1';
@@ -288,9 +292,9 @@ try {
[void]$outTask.GetAwaiter().GetResult(); [void]$errTask.GetAwaiter().GetResult() [void]$outTask.GetAwaiter().GetResult(); [void]$errTask.GetAwaiter().GetResult()
$outFile.Dispose(); $outFile = $null; $errFile.Dispose(); $errFile = $null $outFile.Dispose(); $outFile = $null; $errFile.Dispose(); $errFile = $null
foreach ($file in Get-ChildItem -LiteralPath $game -Filter 'OptiScaler*.log' -File) { Copy-Item -LiteralPath $file.FullName -Destination $OutputDirectory -Force } foreach ($file in Get-ChildItem -LiteralPath $game -Filter 'OptiScaler*.log' -File) { Copy-Item -LiteralPath $file.FullName -Destination $OutputDirectory -Force }
$stderr = Get-Content -LiteralPath (Join-Path $OutputDirectory 'd4r.stderr.log') -Raw $stderr = [IO.File]::ReadAllText((Join-Path $OutputDirectory 'd4r.stderr.log'))
$stdout = Get-Content -LiteralPath (Join-Path $OutputDirectory 'd4r.stdout.log') -Raw $stdout = [IO.File]::ReadAllText((Join-Path $OutputDirectory 'd4r.stdout.log'))
$debugger = Get-Content -LiteralPath (Join-Path $OutputDirectory 'debugger.log') -Raw $debugger = [IO.File]::ReadAllText((Join-Path $OutputDirectory 'debugger.log'))
$gameExit = [regex]::Match($debugger, 'EXIT code=0x([0-9a-f]+)') $gameExit = [regex]::Match($debugger, 'EXIT code=0x([0-9a-f]+)')
$gameCrash = $null $gameCrash = $null
if ([IO.Path]::GetFileName($GameExe) -eq 'SHProto-Win64-Shipping.exe' -and $gameExit.Success -and $gameExit.Groups[1].Value -ne '0') { if ([IO.Path]::GetFileName($GameExe) -eq 'SHProto-Win64-Shipping.exe' -and $gameExit.Success -and $gameExit.Groups[1].Value -ne '0') {
@@ -305,21 +309,8 @@ try {
reason=[string]$context.FGenericCrashContext.RuntimeProperties.ErrorMessage} reason=[string]$context.FGenericCrashContext.RuntimeProperties.ErrorMessage}
} }
} }
# A bounded stop can interrupt the last printf; count only complete records. Get-D4RGameSummary -Stdout $stdout -Stderr $stderr -Debugger $debugger -Preset $Preset `
$frames = [regex]::Matches($stdout, '(?m)^D4R_FRAME cpu_frame_copies=([0-9]+) frame_age=([0-9]+) interop_ngx_ms=([0-9.]+)\r?$') -RunSeconds $timer.Elapsed.TotalSeconds -ValidateOutput ([bool]$ValidateOutput) -GameCrash $gameCrash |
$checks = [regex]::Matches($stdout, '(?m)^D4R_OUTPUT_VALIDATION elements=([0-9]+) nan=([0-9]+) inf=([0-9]+) diagnostics_cpu_bytes=8\r?$')
$nonfinite = @($checks | Where-Object { $_.Groups[2].Value -ne '0' -or $_.Groups[3].Value -ne '0' }).Count
$launches = [regex]::Matches($stderr, '\[d4r-launch\] kernel="([^"]+)" backend=(native|translated)')
$kernels = @($launches | ForEach-Object { $_.Groups[1].Value + ' ' + $_.Groups[2].Value } | Group-Object | ForEach-Object {
$parts=$_.Name.Split(' '); @{kernel=$parts[0]; backend=$parts[1]; launches=$_.Count}
})
@{exitCode=$(if ($gameExit.Success) { '0x' + $gameExit.Groups[1].Value } else { 'diagnostic_timeout' }); runSeconds=$timer.Elapsed.TotalSeconds; preset=$Preset; gameCrash=$gameCrash;
nativeLaunches=([regex]::Matches($stderr, '\[d4r-launch\].*backend=native')).Count;
completedFrames=$frames.Count; outputValidationRequested=[bool]$ValidateOutput;
outputGpuChecks=$checks.Count; nonfiniteOutputs=$nonfinite; kernels=$kernels;
previousFrameOutputs=@($frames | Where-Object { $_.Groups[2].Value -ne '0' }).Count;
cpuImageCopyFrames=@($frames | Where-Object { $_.Groups[1].Value -ne '0' }).Count;
failures=([regex]::Matches($stderr, 'D4R_[A-Z0-9_]*FAILURE\b')).Count} |
ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8 ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
Compress-Archive -LiteralPath $OutputDirectory -DestinationPath ($OutputDirectory + '.zip') -Force Compress-Archive -LiteralPath $OutputDirectory -DestinationPath ($OutputDirectory + '.zip') -Force
Write-Host "Game diagnostic bundle: $OutputDirectory.zip" Write-Host "Game diagnostic bundle: $OutputDirectory.zip"
+9 -1
View File
@@ -218,13 +218,21 @@ int main(int argc, char** argv) {
using Requirements = unsigned(*)(IDXGIAdapter*,const d4r::ngx::FeatureDiscoveryInfo*,d4r::ngx::FeatureRequirement*); using Requirements = unsigned(*)(IDXGIAdapter*,const d4r::ngx::FeatureDiscoveryInfo*,d4r::ngx::FeatureRequirement*);
check(shim.symbol<Requirements>("NVSDK_NGX_D3D12_GetFeatureRequirements")(adapter.Get(),&discovery,&requirements),"D3D12 feature requirements"); check(shim.symbol<Requirements>("NVSDK_NGX_D3D12_GetFeatureRequirements")(adapter.Get(),&discovery,&requirements),"D3D12 feature requirements");
if (requirements.FeatureSupported != 0) throw std::runtime_error("D3D12 DLSS requirements unsupported"); if (requirements.FeatureSupported != 0) throw std::runtime_error("D3D12 DLSS requirements unsupported");
if (args.ngx_frontend != "optiscaler") {
ComPtr<IDXGIAdapter> warp;
dx(factory->EnumWarpAdapter(IID_PPV_ARGS(warp.GetAddressOf())), "Requirements WARP adapter");
d4r::ngx::FeatureRequirement unsupported{};
check(shim.symbol<Requirements>("NVSDK_NGX_D3D12_GetFeatureRequirements")(warp.Get(),&discovery,&unsupported),"WARP feature requirements");
if (unsupported.FeatureSupported != 4) throw std::runtime_error("WARP must report AdapterUnsupported without an initialization failure");
std::printf("D3D12_REQUIREMENTS warp_supported=0 enumeration_success=1\n");
}
if (args.ngx_abi == "project-legacy") { if (args.ngx_abi == "project-legacy") {
using ProjectInit = unsigned(*)(const char*,int,const char*,const wchar_t*,ID3D12Device*,const void*,unsigned); using ProjectInit = unsigned(*)(const char*,int,const char*,const wchar_t*,ID3D12Device*,const void*,unsigned);
check(shim.symbol<ProjectInit>("NVSDK_NGX_D3D12_Init_with_ProjectID")("24480451-f00d-face-1304-0308dabad187",0,"1.0",directory.c_str(),device.Get(),nullptr,0x15),"D3D12 legacy project Init"); check(shim.symbol<ProjectInit>("NVSDK_NGX_D3D12_Init_with_ProjectID")("24480451-f00d-face-1304-0308dabad187",0,"1.0",directory.c_str(),device.Get(),nullptr,0x15),"D3D12 legacy project Init");
} else if (args.ngx_abi == "project") { } else if (args.ngx_abi == "project") {
using ProjectInit = unsigned(*)(const char*,int,const char*,const wchar_t*,ID3D12Device*,unsigned,const void*); using ProjectInit = unsigned(*)(const char*,int,const char*,const wchar_t*,ID3D12Device*,unsigned,const void*);
check(shim.symbol<ProjectInit>("NVSDK_NGX_D3D12_Init_ProjectID")("24480451-f00d-face-1304-0308dabad187",0,"1.0",directory.c_str(),device.Get(),0x15,nullptr),"D3D12 project Init"); check(shim.symbol<ProjectInit>("NVSDK_NGX_D3D12_Init_ProjectID")("24480451-f00d-face-1304-0308dabad187",0,"1.0",directory.c_str(),device.Get(),0x15,nullptr),"D3D12 project Init");
} else check(shim.symbol<Init>("NVSDK_NGX_D3D12_Init_Ext")(241534723ull, directory.c_str(), device.Get(), 0x15, nullptr), "D3D12 Init"); } else check(shim.symbol<Init>("NVSDK_NGX_D3D12_Init_Ext")(args.ngx_app_id, directory.c_str(), device.Get(), 0x15, nullptr), "D3D12 Init");
auto shutdown = shim.symbol<Shutdown>("NVSDK_NGX_D3D12_Shutdown"); auto shutdown = shim.symbol<Shutdown>("NVSDK_NGX_D3D12_Shutdown");
struct ShutdownCleanup { Shutdown fn; ~ShutdownCleanup() { (void)fn(); } } shutdownCleanup{shutdown}; struct ShutdownCleanup { Shutdown fn; ~ShutdownCleanup() { (void)fn(); } } shutdownCleanup{shutdown};
void* parameters = nullptr; check(shim.symbol<Allocate>("NVSDK_NGX_D3D12_AllocateParameters")(&parameters), "AllocateParameters"); void* parameters = nullptr; check(shim.symbol<Allocate>("NVSDK_NGX_D3D12_AllocateParameters")(&parameters), "AllocateParameters");
+6 -3
View File
@@ -87,10 +87,13 @@ API unsigned NVSDK_NGX_D3D12_GetFeatureRequirements(IDXGIAdapter* adapter, const
DXGI_ADAPTER_DESC desc{}; d4r::win::dx(adapter->GetDesc(&desc), "NGX requirements adapter"); DXGI_ADAPTER_DESC desc{}; d4r::win::dx(adapter->GetDesc(&desc), "NGX requirements adapter");
// OptiScaler can override the DXGI vendor ID. The physical HIP // OptiScaler can override the DXGI vendor ID. The physical HIP
// architecture and full adapter LUID are the authoritative identity. // architecture and full adapter LUID are the authoritative identity.
d4r::diag::HipApi hip(d4r::win::env_path("D4R_HIP_ROOT")); hipDeviceProp_t props{}; hip.select_architecture(-1,props,&desc.AdapterLuid); d4r::diag::HipApi hip(d4r::win::env_path("D4R_HIP_ROOT")); hipDeviceProp_t props{};
requirements->FeatureSupported = std::memcmp(&desc.AdapterLuid,props.luid,sizeof(LUID)) ? 4u : 0u; const int ordinal = hip.select_architecture(-1,props,&desc.AdapterLuid,true);
requirements->FeatureSupported = ordinal < 0 ? 4u : 0u;
std::strcpy(requirements->MinOSVersion,"10.0.22000.0"); std::strcpy(requirements->MinOSVersion,"10.0.22000.0");
std::printf("D4R_REQUIREMENTS feature=%u support=%u architecture=%s\n",info->FeatureID,requirements->FeatureSupported,d4r::diag::HipApi::target_arch()); std::printf("D4R_REQUIREMENTS feature=%u support=%u architecture=%s adapter_luid=%08lx:%08lx hip_ordinal=%d\n",
info->FeatureID,requirements->FeatureSupported,d4r::diag::HipApi::target_arch(),
static_cast<unsigned long>(desc.AdapterLuid.HighPart),static_cast<unsigned long>(desc.AdapterLuid.LowPart),ordinal);
return 1u; return 1u;
}); });
} }
+7
View File
@@ -122,6 +122,7 @@ struct Args {
std::string ngx_core, dlss_dll; std::string ngx_core, dlss_dll;
std::string nvapi_dll; std::string nvapi_dll;
std::string ngx_abi = "driver"; std::string ngx_abi = "driver";
unsigned long long ngx_app_id = 241534723ull;
std::string ngx_frontend = "d4r"; std::string ngx_frontend = "d4r";
std::string ngx_mode = "init"; std::string ngx_mode = "init";
unsigned preset = 11; unsigned preset = 11;
@@ -160,6 +161,12 @@ struct Args {
else if (key == "--dlss-dll") dlss_dll = value; else if (key == "--dlss-dll") dlss_dll = value;
else if (key == "--nvapi-dll") nvapi_dll = value; else if (key == "--nvapi-dll") nvapi_dll = value;
else if (key == "--ngx-abi") ngx_abi = value; else if (key == "--ngx-abi") ngx_abi = value;
else if (key == "--ngx-app-id") {
size_t end = 0;
if (value.empty() || value[0] == '-') throw std::runtime_error("Invalid NGX application ID");
ngx_app_id = std::stoull(value, &end);
if (end != value.size()) throw std::runtime_error("Invalid NGX application ID");
}
else if (key == "--ngx-frontend") { else if (key == "--ngx-frontend") {
if (value != "d4r" && value != "optiscaler") throw std::runtime_error("ngx-frontend must be d4r or optiscaler"); if (value != "d4r" && value != "optiscaler") throw std::runtime_error("ngx-frontend must be d4r or optiscaler");
ngx_frontend = value; ngx_frontend = value;
+3 -1
View File
@@ -42,7 +42,8 @@ struct HipApi {
if (result != hipSuccess) throw std::runtime_error(std::string(call) + ": " + hipGetErrorString(result)); if (result != hipSuccess) throw std::runtime_error(std::string(call) + ": " + hipGetErrorString(result));
} }
static const char* target_arch() { return D4R_TARGET_ARCH; } static const char* target_arch() { return D4R_TARGET_ARCH; }
int select_architecture(int requested, hipDeviceProp_t& selected, const void* required_luid = nullptr) const { int select_architecture(int requested, hipDeviceProp_t& selected, const void* required_luid = nullptr,
bool allow_no_match = false) const {
// Architecture spoofing invalidates a gfx12 correctness test. // Architecture spoofing invalidates a gfx12 correctness test.
for (const char* name : {"HSA_OVERRIDE_GFX_VERSION", "HSA_OVERRIDE_GFX_VERSION_0"}) for (const char* name : {"HSA_OVERRIDE_GFX_VERSION", "HSA_OVERRIDE_GFX_VERSION_0"})
if (std::getenv(name)) throw std::runtime_error(std::string("Unset architecture override ") + name); if (std::getenv(name)) throw std::runtime_error(std::string("Unset architecture override ") + name);
@@ -67,6 +68,7 @@ struct HipApi {
selected = props; selected = props;
} }
} }
if (found < 0 && allow_no_match) return -1; // Capability enumeration can include Intel/WARP adapters.
if (found < 0) throw std::runtime_error(std::string("No selected ") + target_arch() + if (found < 0) throw std::runtime_error(std::string("No selected ") + target_arch() +
" device for this adapter; use the package matching the actual HIP architecture (see GPU log)"); " device for this adapter; use the package matching the actual HIP architecture (see GPU log)");
require_gpu_target(selected.gcnArchName, target_arch()); require_gpu_target(selected.gcnArchName, target_arch());
+3 -2
View File
@@ -88,8 +88,9 @@ int main(int argc, char** argv)
common.LoggingInfo = {reinterpret_cast<void*>(&ngx_log), 2, false}; common.LoggingInfo = {reinterpret_cast<void*>(&ngx_log), 2, false};
loaded_modules(); loaded_modules();
std::printf("STAGE NVSDK_NGX_CUDA_Init sdk=0x15 core=%s dlss=%s\n", args.ngx_core.c_str(), args.dlss_dll.c_str()); std::printf("STAGE NVSDK_NGX_CUDA_Init sdk=0x15 core=%s dlss=%s\n", args.ngx_core.c_str(), args.dlss_dll.c_str());
if (args.ngx_abi == "driver") check(driver_init(241534723ull, data_path.c_str(), 0x15), "NVSDK_NGX_CUDA_Init(driver ABI)"); std::printf("NGX_APPLICATION id=%llu\n", args.ngx_app_id);
else if (args.ngx_abi == "sdk") check(init(241534723ull, data_path.c_str(), &common, 0x15), "NVSDK_NGX_CUDA_Init(SDK ABI)"); if (args.ngx_abi == "driver") check(driver_init(args.ngx_app_id, data_path.c_str(), 0x15), "NVSDK_NGX_CUDA_Init(driver ABI)");
else if (args.ngx_abi == "sdk") check(init(args.ngx_app_id, data_path.c_str(), &common, 0x15), "NVSDK_NGX_CUDA_Init(SDK ABI)");
else if (args.ngx_abi == "project") check(core.symbol<ProjectInit>("NVSDK_NGX_CUDA_Init_ProjectID")( else if (args.ngx_abi == "project") check(core.symbol<ProjectInit>("NVSDK_NGX_CUDA_Init_ProjectID")(
"24480451-f00d-face-1304-0308dabad187", 0, "1.0", data_path.c_str(), 0x15, &common), "NVSDK_NGX_CUDA_Init_ProjectID"); "24480451-f00d-face-1304-0308dabad187", 0, "1.0", data_path.c_str(), 0x15, &common), "NVSDK_NGX_CUDA_Init_ProjectID");
else throw std::runtime_error("--ngx-abi must be driver, sdk or project"); else throw std::runtime_error("--ngx-abi must be driver, sdk or project");