Fix game startup resource lifetime and D32S8 depth-plane interop

This commit is contained in:
Codex committed 2026-10-01 17:27:06 +03:00
1 parent 19d859e43d
commit 5d33bddcd0
17 files changed
+539 -11

No files matched your search

+57
View File
@@ -0,0 +1,57 @@
# Native Windows RDNA4 game development package
Validated hardware: Windows 11 x64 / Radeon RX 9070 XT / gfx1201. Native K and
M transformer kernels pass standalone D3D12 + OptiScaler checks. Actual game
compatibility and performance are still being validated. This is a local
development package; no NVIDIA proprietary DLL is included.
Build the shim/diagnostics, corrected ZLUDA, K/M native objects and patched
OptiScaler using the scripts in `scripts/windows`, then run:
```powershell
.\scripts\windows\package-windows-game.ps1
```
The package pins TheRock 10.2.0a20260929 because its Windows external-memory
mapping lifetime passes the repeated import/release test. Stable HIP 7.2 has
a reproduced mapped-view leak; see `docs/windows-rdna4-port.md` for the source
fix, exact dependency versions, validation and complete build commands.
Run a D3D12 game without anti-cheat using your locally supplied NVIDIA DLLs:
```powershell
.\dist\windows-rdna4-game\windows-game.ps1 -GameExe "D:\Games\SILENT HILL 2\SHProto\Binaries\Win64\SHProto-Win64-Shipping.exe" -NgxCore "C:\Users\Administrator\d4r\_nvngx.dll" -DlssDll "C:\Users\Administrator\d4r\nvngx_dlss.dll" -Preset 11
```
Use `-Preset 13` for M. The validated manifest requires DLSS 310.9.1 with the
recorded SHA256; other feature DLLs require regenerating and validating the
native manifest. Select an upscaler supported by OptiScaler in the game.
The script installs `dxgi.dll`, an explicit DLSS configuration and a private
`d4r` runtime directory, and captures stdout/stderr, an external debugger log,
crash dump, loaded DLL paths, driver information and OptiScaler logs. A local
ZIP bundle is printed at exit. `-RunSeconds 120` bounds a diagnostic run;
the normal default allows you to play until you close the game.
For the Silent Hill 2 startup black-screen investigation, add
`-DiagnosticResolution 1280x720`. This backs up its two graphics configuration
files, temporarily selects a window at that resolution, and restores the
original bytes after the test. It does not change progression saves. First-use
PTX compilation can be slow; `-CacheDirectory <existing-ZLUDA-cache>` reuses
modules with matching GPU, codegen switches and ZLUDA binary fingerprint.
Original files are backed up before replacement. Restore with:
```powershell
.\dist\windows-rdna4-game\windows-game.ps1 -Action restore -GameExe "D:\Games\SILENT HILL 2\SHProto\Binaries\Win64\SHProto-Win64-Shipping.exe"
```
Restore refuses to overwrite files changed since installation. Backups and
diagnostic results are retained. The script does not change progression saves,
system DLLs, driver settings or security settings. All runtime work is native
Windows. Colour, depth, motion, exposure and output cross APIs through VRAM
and shared D3D12 fences; the backend waits for the current frame and never
uses a previous-frame fallback.
OptiScaler source modifications are GPL-3.0 and exported under
`source-patches/optiscaler`. ZLUDA source modifications are exported under
`source-patches/zluda`. Dependency license texts and build hashes are included.
+49 -2
View File
@@ -8,6 +8,53 @@ An actual game session remains unverified.
## Milestone and gates ## Milestone and gates
First actual game runs (2026-10-01): Silent Hill 2 1.2, XeSS input -> OptiScaler
DLSS K. `silent-hill2-k-first.zip` found the old Project ID wrapper initializing
CUDA NGX with app 4919 instead of the caller's identity, and back-buffer
tracking references blocking `ResizeBuffers` (DXGI_ERROR_INVALID_CALL).
OptiScaler patch `0003` preserves identity; resource-state records now follow
the public D3D12 caller-owned resource lifetime instead of retaining barriers'
resources. A composition swap-chain regression resizes while its submitted,
unreset command list remains alive; it passes on Radeon and WARP, along with
all 16 CTest gates (`game-resize-ctest.log`). K/M legacy Project ID standalone
frames have bit-exact RGB. The invalid-core test propagates the error from
legacy Init rather than pretending initialization succeeded.
`silent-hill2-k-project-resize.zip` successfully initializes CUDA DLSS and
presents thousands of startup frames with no d4r failures. It has **zero
completed DLSS frames** and is not a game acceptance pass. The next interactive
run reaches native K feature creation at 2259x1271 -> 3840x2160 after Enter;
the user reports a black screen during this phase. Capture and isolate this
before claiming successful in-game execution.
`silent-hill2-k-720p.zip` localizes the flashing “upscaler failed to run”
message to the actual depth resource: DXGI `R32G8X24_TYPELESS` (19). The
backend previously rejected it before executing CUDA Evaluate. Implemented
its D3D12 depth copy plane as R32; it is not an interleaved 64-bit texel.
The [public planar depth/stencil specification](https://github.com/microsoft/DirectX-Specs/blob/master/d3d/PlanarDepthStencilDDISpec.md)
defines this plane mapping. `GetCopyableFootprints` is checked against R32
and width * 4 bytes; copies/barriers affect subresource 0 only. All 4403 depth
pixels match an independent D3D12 typed SRV exactly in each of two legacy and
two enhanced runs. Full build / CTest 16/16 pass
(`d32-stencil-full-ctest.log`). The NGX runner supports
`-PixelProfile depth-stencil` for full-network validation.
`optiscaler-k-depth-stencil.zip` and `optiscaler-m-depth-stencil.zip` both
pass four full frames, with all 44/40 native transformer launches and bit-exact
RGB against the respective non-stencil depth baselines.
The local game package is `dist/windows-rdna4-game`, produced by
`scripts/windows/package-windows-game.ps1`. Its native manifest combines all
11 K + 5 M objects; NVIDIA binaries are excluded. `windows-game.ps1` backs up
replaced files, verifies package hashes and the local DLSS identity, sets the
native runtime environment, and captures stdout/stderr, external debugger,
OptiScaler, exception and version/driver logs. Install/restore regression
restores original DLL/config sentinel bytes exactly. For Silent Hill 2,
`-DiagnosticResolution 1280x720` backs up and restores its two graphics config
files; this avoids modifying progression saves. Original settings after the
stopped failing run have an identical SHA256. `-CacheDirectory` can reuse an
already validated local JIT cache. Development package usage is documented in
`docs/windows-game.md`; in-game success is still a gate.
Startup coverage (2026-10-01): OptiScaler patch `0002` installs d4r at device Startup coverage (2026-10-01): OptiScaler patch `0002` installs d4r at device
discovery, before NGX initialization. `optiscaler-k-preinit-signature.zip` and discovery, before NGX initialization. `optiscaler-k-preinit-signature.zip` and
`optiscaler-m-preinit-signature.zip` create a dispatch-only command signature `optiscaler-m-preinit-signature.zip` create a dispatch-only command signature
@@ -149,8 +196,8 @@ copy counts remain zero. Add `-BarrierMode enhanced` to the packed-run command
below to reproduce. The explicit harness can also supply layout metadata. below to reproduce. The explicit harness can also supply layout metadata.
Tracking now also publishes explicit ordinary texture state/layout metadata Tracking now also publishes explicit ordinary texture state/layout metadata
in the actual queue submission order. Each split segment retains its own in the actual queue submission order. Each split segment stores its own
resource-state snapshot and COM references. An Evaluate input with no barrier resource-state snapshot. An Evaluate input with no barrier
in its own list resolves the most recently submitted state at the callback, in its own list resolves the most recently submitted state at the callback,
after preceding lists in the batch. The metadata belongs to the resource, with after preceding lists in the batch. The metadata belongs to the resource, with
no global pointer cache or ownership cycle. Legacy buffer/simultaneous-access no global pointer cache or ownership cycle. Legacy buffer/simultaneous-access
@@ -0,0 +1,37 @@
From 09488c47855aadbf85f68ab8c72c0f3ac5f915e9 Mon Sep 17 00:00:00 2001
From: Codex <codex@localhost>
Date: Thu, 1 Oct 2026 17:01:32 +0300
Subject: [PATCH] Preserve legacy ProjectID initialization in the external
backend
---
OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
index 89c9ba8..1f723f1 100644
--- a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
+++ b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp
@@ -313,6 +313,8 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_Init_ProjectID(const char* InProj
if (result == NVSDK_NGX_Result_Success)
NVNGXProxy::SetDx12Inited(true);
+ else if (State::Instance().dlssExternalBackend)
+ return result;
}
}
@@ -343,6 +345,10 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_Init_with_ProjectID(
{
LOG_FUNC();
+ if (State::Instance().dlssExternalBackend)
+ return NVSDK_NGX_D3D12_Init_ProjectID(InProjectId, InEngineType, InEngineVersion,
+ InApplicationDataPath, InDevice, InSDKVersion, InFeatureInfo);
+
LOG_INFO("InProjectId: {0}", InProjectId);
LOG_INFO("InEngineType: {0}", (int) InEngineType);
LOG_INFO("InEngineVersion: {0}", InEngineVersion);
--
2.55.0.windows.5
+5
View File
@@ -21,6 +21,11 @@ and before OptiScaler's own method hooks. Command signatures created during
game startup therefore retain their public argument descriptions for replay. game startup therefore retain their public argument descriptions for replay.
The standalone runner's `-EarlyIndirectProbe` exercises this startup order. The standalone runner's `-EarlyIndirectProbe` exercises this startup order.
`0003` preserves the Project ID and engine identity through OptiScaler's
older `Init_with_ProjectID` entry point used by its XeSS/FSR inputs. It also
propagates an explicit external backend initialization failure. Reproduce
with the D3D12 runner's `-NgxAbi project-legacy`.
Build with Microsoft Build Tools 2022, v143 and Windows SDK 10.0.26100: Build with Microsoft Build Tools 2022, v143 and Windows SDK 10.0.26100:
```powershell ```powershell
+46
View File
@@ -0,0 +1,46 @@
[CmdletBinding()]
param([Parameter(Mandatory=$true)][int]$GameProcessId, [Parameter(Mandatory=$true)][string]$OutputPath)
$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.Drawing
if (!('D4RWindowCapture' -as [type])) {
Add-Type @'
using System;
using System.Runtime.InteropServices;
using System.Text;
public static class D4RWindowCapture {
public delegate bool Callback(IntPtr window, IntPtr data);
[StructLayout(LayoutKind.Sequential)] public struct Rect { public int Left, Top, Right, Bottom; }
[DllImport("user32.dll")] public static extern bool EnumWindows(Callback callback, IntPtr data);
[DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr window, out uint process);
[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr window, out Rect rect);
[DllImport("user32.dll",CharSet=CharSet.Unicode)] public static extern int GetWindowText(IntPtr window, StringBuilder text, int count);
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr window);
}
'@
}
$windows = [Collections.Generic.List[object]]::new()
$callback = [D4RWindowCapture+Callback]{ param($window,$data)
[uint32]$owner = 0
[void][D4RWindowCapture]::GetWindowThreadProcessId($window,[ref]$owner)
if ($owner -eq $GameProcessId) {
$rect = [D4RWindowCapture+Rect]::new()
[void][D4RWindowCapture]::GetWindowRect($window,[ref]$rect)
$title = [Text.StringBuilder]::new(512)
[void][D4RWindowCapture]::GetWindowText($window,$title,512)
$windows.Add([pscustomobject]@{handle=$window; title=$title.ToString(); visible=[D4RWindowCapture]::IsWindowVisible($window); rect=$rect;
area=($rect.Right-$rect.Left)*($rect.Bottom-$rect.Top)})
}
return $true
}
[void][D4RWindowCapture]::EnumWindows($callback,[IntPtr]::Zero)
$windows | Select-Object handle,title,visible,area | Format-Table
$chosen = $windows | Where-Object { $_.visible -and $_.area -gt 4096 } | Sort-Object area -Descending | Select-Object -First 1
if (!$chosen) { throw "No visible game window for PID $GameProcessId" }
$rect = $chosen.rect
$bitmap = [Drawing.Bitmap]::new($rect.Right-$rect.Left,$rect.Bottom-$rect.Top)
$graphics = [Drawing.Graphics]::FromImage($bitmap)
try {
$graphics.CopyFromScreen($rect.Left,$rect.Top,0,0,$bitmap.Size)
$bitmap.Save([IO.Path]::GetFullPath($OutputPath),[Drawing.Imaging.ImageFormat]::Png)
} finally { $graphics.Dispose(); $bitmap.Dispose() }
Write-Host "Captured game window: $OutputPath"
+76
View File
@@ -0,0 +1,76 @@
[CmdletBinding()]
param(
[string]$PackageRoot, [string]$DiagnosticRoot, [string]$OptiScalerRoot,
[string]$ZludaRoot, [string]$HipRoot, [string]$KernelKRoot, [string]$KernelMRoot
)
$ErrorActionPreference = 'Stop'
$repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..'))
$defaults = @{PackageRoot='dist/windows-rdna4-game'; DiagnosticRoot='dist/windows-rdna4-command-list';
OptiScalerRoot='dist/optiscaler-windows-d4r'; ZludaRoot='dist/zluda-windows-f16-reference';
HipRoot='.tools/therock-10.2.0a20260929/_rocm_sdk_core';
KernelKRoot='build/native-k-gfx1201'; KernelMRoot='build/native-m-gfx1201'}
foreach ($name in $defaults.Keys) {
if (!(Get-Variable -Name $name -ValueOnly)) { Set-Variable -Name $name -Value (Join-Path $repo $defaults[$name]) }
}
$PackageRoot = [IO.Path]::GetFullPath($PackageRoot)
New-Item -ItemType Directory -Force $PackageRoot | Out-Null
$stagedFiles = [Collections.Generic.List[string]]::new()
function Stage([string]$source, [string]$relative) {
if (!(Test-Path -LiteralPath $source -PathType Leaf)) { throw "Required file missing: $source" }
$destination = Join-Path $PackageRoot $relative
New-Item -ItemType Directory -Force (Split-Path $destination) | Out-Null
Copy-Item -LiteralPath $source -Destination $destination -Force
$stagedFiles.Add($relative)
}
Stage (Join-Path $OptiScalerRoot 'OptiScaler.dll') 'OptiScaler.dll'
Stage (Join-Path $DiagnosticRoot 'bin/d4r_nvngx.dll') 'd4r/_nvngx.dll'
Stage (Join-Path $DiagnosticRoot 'bin/pixel_convert_gfx1201.hsaco') 'd4r/pixel_convert_gfx1201.hsaco'
Stage (Join-Path $DiagnosticRoot 'nvapi-compat/nvapi64.dll') 'd4r/nvapi/nvapi64.dll'
Stage (Join-Path $DiagnosticRoot 'bin/d4r_debug_launcher.exe') 'd4r/d4r_debug_launcher.exe'
foreach ($file in @('nvcuda.dll','nvapi64.dll','libc++.dll','libunwind.dll')) { Stage (Join-Path $ZludaRoot $file) "d4r/zluda/$file" }
foreach ($file in @('amdhip64_7.dll','amd_comgr.dll','hiprtc0717.dll','hiprtc-builtins0717.dll','rocm_kpack.dll','AMD.ROCM.Comgr.MANIFEST','.hipVersion')) {
Stage (Join-Path $HipRoot "bin/$file") "d4r/hip/bin/$file"
}
$entries = @{}
foreach ($directory in @($KernelKRoot, $KernelMRoot)) {
foreach ($line in Get-Content -LiteralPath (Join-Path $directory 'd4r-kernels.txt')) {
if (!$line.Trim() -or $line.StartsWith('#')) { continue }
if ($line -notmatch '^([A-Za-z0-9_]+) ([0-9a-f]{16})$') { throw "Invalid native manifest entry: $line" }
if ($entries.ContainsKey($Matches[1])) { throw "Duplicate native kernel: $($Matches[1])" }
$entries[$Matches[1]] = $line
Stage (Join-Path $directory ($Matches[1] + '.hsaco')) ('d4r/native/' + $Matches[1] + '.hsaco')
}
}
if ($entries.Count -ne 16) { throw "Expected 11 K and 5 M native objects; found $($entries.Count)" }
@('# Audited DLSS 310.9.1: K + M gfx1201 native kernels', '# Generated by package-windows-game.ps1') + @($entries.Values | Sort-Object) |
Set-Content -LiteralPath (Join-Path $PackageRoot 'd4r/native/d4r-kernels.txt') -Encoding ASCII
$stagedFiles.Add('d4r/native/d4r-kernels.txt')
Stage (Join-Path $repo 'scripts/windows/windows-game.ps1') 'windows-game.ps1'
Stage (Join-Path $repo 'scripts/windows/capture-game-window.ps1') 'capture-game-window.ps1'
Stage (Join-Path $repo 'scripts/windows/send-game-key.ps1') 'send-game-key.ps1'
Stage (Join-Path $repo 'docs/windows-game.md') 'README.md'
Stage (Join-Path $repo 'LICENSE') 'licenses/d4r.txt'
Stage (Join-Path $OptiScalerRoot 'GPL-3.0.txt') 'licenses/OptiScaler.txt'
Stage (Join-Path $repo 'external/ZLUDA/LICENSE-MIT') 'licenses/ZLUDA-MIT.txt'
Stage (Join-Path $repo 'external/ZLUDA/LICENSE-APACHE') 'licenses/ZLUDA-APACHE.txt'
Stage (Join-Path $repo 'external/ZLUDA/ext/llvm-project/LICENSE.TXT') 'licenses/LLVM.txt'
Stage (Join-Path $repo 'external/MinHook/LICENSE.txt') 'licenses/MinHook.txt'
Stage (Join-Path $HipRoot 'share/doc/amd_comgr/LICENSE.txt') 'licenses/amd_comgr.txt'
Stage (Join-Path $repo 'external/clr/LICENSE.md') 'licenses/HIP-CLR.txt'
foreach ($patch in Get-ChildItem -LiteralPath (Join-Path $repo 'patches/optiscaler') -Filter '*.patch') { Stage $patch.FullName ('source-patches/optiscaler/' + $patch.Name) }
foreach ($patch in Get-ChildItem -LiteralPath (Join-Path $repo 'patches/zluda') -Filter '*.patch') { Stage $patch.FullName ('source-patches/zluda/' + $patch.Name) }
$metadata = [ordered]@{
architecture='gfx1201'; nativeKernels=16; dlssSha256='3975567b8943c53acce397f2b72380092f84f162d00b0d2c7d08a1025c563983';
d4rCommit=(& git -C $repo rev-parse HEAD).Trim();
optiScaler=(Get-Content -LiteralPath (Join-Path $OptiScalerRoot 'build-info.json') -Raw | ConvertFrom-Json);
zludaCommit=(& git -C (Join-Path $repo 'external/ZLUDA') rev-parse HEAD).Trim();
hipRuntime='TheRock 10.2.0a20260929 / HIP 7.17.26386';
files=@($stagedFiles | ForEach-Object { Get-Item -LiteralPath (Join-Path $PackageRoot $_) } | ForEach-Object {
@{path=$_.FullName.Substring($PackageRoot.Length+1); sha256=(Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash; version=$_.VersionInfo.FileVersion}
})
}
$metadata | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath (Join-Path $PackageRoot 'package.json') -Encoding UTF8
if (Get-ChildItem -LiteralPath $PackageRoot -Recurse -File | Where-Object { $_.Name -eq 'nvngx_dlss.dll' -or ($_.Name -eq '_nvngx.dll' -and $_.DirectoryName -ne (Join-Path $PackageRoot 'd4r')) }) {
throw 'Proprietary NVIDIA binaries must not be in the package.'
}
Write-Host "Windows game development package: $PackageRoot (NVIDIA DLLs excluded)"
+25
View File
@@ -0,0 +1,25 @@
[CmdletBinding()]
param([Parameter(Mandatory=$true)][int]$GameProcessId,
[ValidateSet('Enter','Escape','Up','Down','Tab')][string]$Key='Enter')
$ErrorActionPreference='Stop'
$process=Get-Process -Id $GameProcessId -ErrorAction Stop
if ($process.ProcessName -ne 'SHProto-Win64-Shipping') { throw 'This helper is scoped to the authorized Silent Hill 2 diagnostic.' }
if (!('D4RGameKeys' -as [type])) {
Add-Type @'
using System;
using System.Runtime.InteropServices;
public static class D4RGameKeys {
[DllImport("user32.dll")] public static extern bool PostMessage(IntPtr window, uint message, IntPtr key, IntPtr flags);
[DllImport("user32.dll")] public static extern uint MapVirtualKey(uint code, uint kind);
}
'@
}
$window=$process.MainWindowHandle
if ($window -eq [IntPtr]::Zero) { throw 'Game window missing on this desktop.' }
$codes=@{Enter=13; Escape=27; Up=38; Down=40; Tab=9}
$code=$codes[$Key]; $scan=[D4RGameKeys]::MapVirtualKey($code,0)
$flags=[long](1 -bor ($scan -shl 16))
if ($Key -in @('Up','Down')) { $flags=$flags -bor 0x01000000 }
if (![D4RGameKeys]::PostMessage($window,0x100,[IntPtr]$code,[IntPtr]$flags)) { throw 'Game key-down post failed.' }
if (![D4RGameKeys]::PostMessage($window,0x101,[IntPtr]$code,[IntPtr]($flags -bor 0xc0000000L))) { throw 'Game key-up post failed.' }
Write-Host "Sent $Key only to diagnostic game PID $GameProcessId."
+2 -2
View File
@@ -8,7 +8,7 @@ param(
[string]$NgxCore = $env:D4R_NGX_CORE, [string]$NgxCore = $env:D4R_NGX_CORE,
[string]$DlssDll = $env:D4R_DLSS_DLL, [string]$DlssDll = $env:D4R_DLSS_DLL,
[ValidateSet('init', 'evaluate', 'd3d12')][string]$NgxMode = 'init', [ValidateSet('init', 'evaluate', 'd3d12')][string]$NgxMode = 'init',
[ValidateSet('driver','sdk','project')][string]$NgxAbi = 'driver', [ValidateSet('driver','sdk','project','project-legacy')][string]$NgxAbi = 'driver',
[string]$OptiScalerDll, [string]$OptiScalerDll,
[ValidateSet(5, 11, 13)][int]$Preset = 11, [ValidateSet(5, 11, 13)][int]$Preset = 11,
[switch]$NgxOnly, [switch]$NgxOnly,
@@ -17,7 +17,7 @@ param(
[switch]$CommandListBackend, [switch]$CommandListBackend,
[switch]$CaptureExceptions, [switch]$CaptureExceptions,
[switch]$EarlyIndirectProbe, [switch]$EarlyIndirectProbe,
[ValidateSet('baseline','packed','unorm')][string]$PixelProfile = 'baseline', [ValidateSet('baseline','packed','unorm','depth-stencil')][string]$PixelProfile = 'baseline',
[ValidateSet('legacy','enhanced','inherited-legacy','inherited-enhanced')][string]$BarrierMode = 'legacy', [ValidateSet('legacy','enhanced','inherited-legacy','inherited-enhanced')][string]$BarrierMode = 'legacy',
[int]$Iterations = 32, [int]$Iterations = 32,
[int]$TimeoutSeconds = 180 [int]$TimeoutSeconds = 180
+193
View File
@@ -0,0 +1,193 @@
[CmdletBinding()]
param(
[ValidateSet('install','restore','run')][string]$Action = 'run',
[Parameter(Mandatory=$true)][string]$GameExe,
[string]$NgxCore, [string]$DlssDll,
[ValidateSet(11,13)][int]$Preset = 11,
[int]$RunSeconds = 0,
[string]$DiagnosticResolution,
[string]$CacheDirectory = $env:ZLUDA_CACHE_DIR,
[string[]]$GameArguments = @('-dx12','-windowed','-ResX=1280','-ResY=720'),
[string]$OutputDirectory
)
$ErrorActionPreference = 'Stop'
$package = [IO.Path]::GetFullPath($PSScriptRoot)
$GameExe = (Get-Item -LiteralPath $GameExe -ErrorAction Stop).FullName
$game = Split-Path $GameExe
$backup = Join-Path $game '.d4r-backup'
$manifestPath = Join-Path $backup 'manifest.json'
function GamePath([string]$relative) {
$path = [IO.Path]::GetFullPath((Join-Path $game $relative))
if (!$path.StartsWith($game + '\', [StringComparison]::OrdinalIgnoreCase)) { throw "Path escapes game directory: $relative" }
return $path
}
if (Get-Process -Name ([IO.Path]::GetFileNameWithoutExtension($GameExe)) -ErrorAction SilentlyContinue) { throw 'Close the game before installing, restoring or running this diagnostic.' }
if ($Action -eq 'restore') {
if (!(Test-Path -LiteralPath $manifestPath)) { throw 'No d4r installation manifest exists.' }
$manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json
foreach ($entry in $manifest.files) {
$path = GamePath $entry.path
if ((Test-Path -LiteralPath $path) -and (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash -ne $entry.installedHash) { throw "File changed since installation; preserved: $path" }
}
foreach ($entry in $manifest.files) {
$path = GamePath $entry.path
if ($entry.existed) { Copy-Item -LiteralPath (Join-Path $backup ('original/' + $entry.path)) -Destination $path -Force }
elseif (Test-Path -LiteralPath $path) { Remove-Item -LiteralPath $path -Force }
}
# Remove only empty directories; preserve user files and diagnostic logs.
if (Test-Path -LiteralPath (GamePath 'd4r')) {
Get-ChildItem -LiteralPath (GamePath 'd4r') -Directory -Recurse | Sort-Object { $_.FullName.Length } -Descending | ForEach-Object {
if (!(Get-ChildItem -LiteralPath $_.FullName -Force)) { Remove-Item -LiteralPath $_.FullName }
}
if (!(Get-ChildItem -LiteralPath (GamePath 'd4r') -Force)) { Remove-Item -LiteralPath (GamePath 'd4r') }
}
Move-Item -LiteralPath $manifestPath -Destination (Join-Path $backup ('restored-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff') + '.json'))
Write-Host "Original game files restored. Backups retained: $backup"
return
}
if (!$NgxCore -or !$DlssDll) { throw 'Pass both local NVIDIA DLL paths with -NgxCore and -DlssDll.' }
$NgxCore = (Get-Item -LiteralPath $NgxCore).FullName
$DlssDll = (Get-Item -LiteralPath $DlssDll).FullName
$metadata = Get-Content -LiteralPath (Join-Path $package 'package.json') -Raw | ConvertFrom-Json
foreach ($file in $metadata.files) {
if ((Get-FileHash -LiteralPath (Join-Path $package $file.path) -Algorithm SHA256).Hash -ne $file.sha256) { throw "Package file changed; rebuild package: $($file.path)" }
}
if ((Get-FileHash -LiteralPath $DlssDll -Algorithm SHA256).Hash -ne $metadata.dlssSha256) { throw 'DLSS DLL does not match the validated 310.9.1 native manifest. Rebuild and validate native manifests for this DLL first.' }
$manifest = if (Test-Path -LiteralPath $manifestPath) { Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json } else { [pscustomobject]@{game=$GameExe; files=@()} }
if ($manifest.game -ne $GameExe) { throw 'Backup manifest belongs to another executable.' }
if (!(Test-Path -LiteralPath $manifestPath) -and (Test-Path -LiteralPath (GamePath 'd4r'))) { throw 'An existing d4r directory has no installation manifest; preserved.' }
New-Item -ItemType Directory -Force $backup | Out-Null
function InstallFile([string]$source, [string]$relative) {
$path = GamePath $relative
$entry = @($manifest.files | Where-Object path -eq $relative) | Select-Object -First 1
if ($entry) {
if ((Test-Path -LiteralPath $path) -and (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash -ne $entry.installedHash) { throw "Installed file changed; preserved: $path" }
} else {
$existed = Test-Path -LiteralPath $path
if ($existed) {
$original = Join-Path $backup ('original/' + $relative)
New-Item -ItemType Directory -Force (Split-Path $original) | Out-Null
Copy-Item -LiteralPath $path -Destination $original
}
$entry = [pscustomobject]@{path=$relative; existed=[bool]$existed; installedHash=''}
$manifest.files = @($manifest.files) + @($entry)
}
New-Item -ItemType Directory -Force (Split-Path $path) | Out-Null
Copy-Item -LiteralPath $source -Destination $path -Force
$entry.installedHash = (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash
$manifest | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath $manifestPath -Encoding UTF8
}
InstallFile (Join-Path $package 'OptiScaler.dll') 'dxgi.dll'
foreach ($file in Get-ChildItem -LiteralPath (Join-Path $package 'd4r') -Recurse -File) { InstallFile $file.FullName $file.FullName.Substring($package.Length+1) }
# Private local test copies only; these are never added to the public package.
InstallFile $NgxCore 'd4r/vendor/_nvngx.dll'
InstallFile $DlssDll 'd4r/vendor/nvngx_dlss.dll'
$bridge = GamePath 'd4r'
$vendor = GamePath 'd4r/vendor'
$ini = @"
[Upscalers]
Dx12Upscaler=dlss
[FrameGen]
Enabled=false
FGInput=nofg
FGOutput=nofg
[DLSS]
Enabled=true
AllowExternalBackend=true
RenderPresetOverride=true
RenderPresetForAll=$Preset
UseGenericAppIdWithDlss=false
[Libraries]
NvngxPath=$bridge
NvngxDlssPath=$vendor\nvngx_dlss.dll
NvngxFeaturePath=$vendor
[Hotfix]
RestoreComputeSignature=false
RestoreGraphicSignature=false
ExtendedStateRestore=false
[Log]
LogToFile=true
LogLevel=0
[Menu]
DisableSplash=true
"@
$tempIni = Join-Path $backup 'generated-OptiScaler.ini'
$ini | Set-Content -LiteralPath $tempIni -Encoding UTF8
InstallFile $tempIni 'OptiScaler.ini'
if ($Action -eq 'install') { Write-Host "Installed development backend for preset $Preset. Use -Action restore to revert."; return }
if (!$OutputDirectory) { $OutputDirectory = Join-Path $package ('results/game-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff')) }
$OutputDirectory = [IO.Path]::GetFullPath($OutputDirectory)
New-Item -ItemType Directory -Force $OutputDirectory | Out-Null
if (!$CacheDirectory) { $CacheDirectory = Join-Path $package 'cache' }
$CacheDirectory = [IO.Path]::GetFullPath($CacheDirectory)
$settings = @{
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_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_FORMAT_MODULE=(GamePath 'd4r/pixel_convert_gfx1201.hsaco');
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_DIAG_DIR=$OutputDirectory; ZLUDA_LOG_DIR=(Join-Path $OutputDirectory 'zluda-trace');
ZLUDA_CACHE_DIR=$CacheDirectory; PATH=((GamePath 'd4r/hip/bin') + ';' + (GamePath 'd4r/zluda') + ';' + $env:PATH)
}
$old = @{}; $process = $null; $originalSettings = @()
try {
if ($DiagnosticResolution) {
if ([IO.Path]::GetFileName($GameExe) -ne 'SHProto-Win64-Shipping.exe' -or $DiagnosticResolution -notmatch '^([0-9]{3,4})x([0-9]{3,4})$') { throw 'DiagnosticResolution currently supports Silent Hill 2 only; use e.g. 1280x720.' }
$renderWidth=[int]$Matches[1]; $renderHeight=[int]$Matches[2]
if ($renderWidth -gt 4096 -or $renderHeight -gt 2160) { throw 'Diagnostic resolution is too large.' }
$userRoot=Join-Path $env:LOCALAPPDATA 'SilentHill2/Saved'
foreach ($relative in @('Config/Windows/GameUserSettings.ini','SaveGames/GFXSettings.SHProto-Win64-Shipping.xml')) {
$path=Join-Path $userRoot $relative
$saved=Join-Path $OutputDirectory ('original-settings/' + $relative)
New-Item -ItemType Directory -Force (Split-Path $saved) | Out-Null
Copy-Item -LiteralPath $path -Destination $saved
$originalSettings += @{path=$path; saved=$saved}
if ($relative.EndsWith('.ini')) {
$text=[IO.File]::ReadAllText($path)
$text=$text -replace 'Resolution=\(X=[0-9]+,Y=[0-9]+\)', "Resolution=(X=$renderWidth,Y=$renderHeight)"
$text=$text -replace 'ScreenMode=Borderless', 'ScreenMode=Windowed'
$text=$text -replace '(?m)^ResolutionSizeX=[0-9]+', "ResolutionSizeX=$renderWidth"
$text=$text -replace '(?m)^ResolutionSizeY=[0-9]+', "ResolutionSizeY=$renderHeight"
$text=$text -replace '(?m)^FullscreenMode=[0-9]+', 'FullscreenMode=2'
[IO.File]::WriteAllText($path,$text,[Text.UTF8Encoding]::new($false))
} else {
$xml=[xml](Get-Content -LiteralPath $path -Raw -Encoding Unicode)
$xml.GSA_SDK.GAMESETTINGS.RESOLUTION.Width="$renderWidth"
$xml.GSA_SDK.GAMESETTINGS.RESOLUTION.Height="$renderHeight"
$xml.Save($path)
}
}
Write-Host "Temporary diagnostic resolution: $DiagnosticResolution; original settings restore at exit."
}
foreach ($key in $settings.Keys) { $old[$key] = [Environment]::GetEnvironmentVariable($key,'Process'); [Environment]::SetEnvironmentVariable($key,$settings[$key],'Process') }
@{preset=$Preset; game=$GameExe; package=$metadata; driver=@(Get-CimInstance Win32_VideoController | Select-Object Name,DriverVersion,DriverDate); arguments=$GameArguments} |
ConvertTo-Json -Depth 12 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'environment.json') -Encoding UTF8
function Quote([string]$value) { if ($value.Contains('"')) { throw 'A command argument contains an unsupported quote.' }; return '"' + $value + '"' }
$arguments = @('--output-directory', (Quote $OutputDirectory), '--', (Quote $GameExe)) + @($GameArguments | ForEach-Object { Quote $_ })
$process = Start-Process -FilePath (GamePath 'd4r/d4r_debug_launcher.exe') -ArgumentList $arguments -WorkingDirectory $game -WindowStyle Hidden -PassThru `
-RedirectStandardOutput (Join-Path $OutputDirectory 'd4r.stdout.log') -RedirectStandardError (Join-Path $OutputDirectory 'd4r.stderr.log')
Write-Host "Game diagnostic started (preset $Preset). Logs: $OutputDirectory"
$timer = [Diagnostics.Stopwatch]::StartNew()
while (!$process.WaitForExit(1000)) {
if ($RunSeconds -gt 0 -and $timer.Elapsed.TotalSeconds -ge $RunSeconds) { $process.Kill(); $process.WaitForExit(); break }
}
$process.WaitForExit()
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
$stdout = Get-Content -LiteralPath (Join-Path $OutputDirectory 'd4r.stdout.log') -Raw
$debugger = Get-Content -LiteralPath (Join-Path $OutputDirectory 'debugger.log') -Raw
$gameExit = [regex]::Match($debugger, 'EXIT code=0x([0-9a-f]+)')
@{exitCode=$(if ($gameExit.Success) { '0x' + $gameExit.Groups[1].Value } else { 'diagnostic_timeout' }); runSeconds=$timer.Elapsed.TotalSeconds; preset=$Preset;
nativeLaunches=([regex]::Matches($stderr, '\[d4r-launch\].*backend=native')).Count;
completedFrames=([regex]::Matches($stdout, 'D4R_FRAME ')).Count;
failures=([regex]::Matches($stderr, 'D4R_WINDOWS_FAILURE|D4R_COMMAND_FAILURE')).Count} |
ConvertTo-Json | Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding UTF8
Compress-Archive -LiteralPath $OutputDirectory -DestinationPath ($OutputDirectory + '.zip') -Force
Write-Host "Game diagnostic bundle: $OutputDirectory.zip"
} finally {
if ($process -and !$process.HasExited) { $process.Kill(); $process.WaitForExit() }
foreach ($key in $old.Keys) { [Environment]::SetEnvironmentVariable($key,$old[$key],'Process') }
foreach ($entry in $originalSettings) { Copy-Item -LiteralPath $entry.saved -Destination $entry.path -Force }
}
+6 -5
View File
@@ -16,7 +16,9 @@ bool installed = false;
struct Event { UINT metadata; std::vector<uint8_t> data; }; struct Event { UINT metadata; std::vector<uint8_t> data; };
using State = std::function<void(ID3D12GraphicsCommandList*)>; using State = std::function<void(ID3D12GraphicsCommandList*)>;
struct TrackedResource { struct TrackedResource {
ComPtr<ID3D12Resource> owner; // D3D12 command lists do not retain resource references. The caller keeps
// them alive through submission/completion. An extra tracking reference
// would keep swap-chain buffers alive and make ResizeBuffers fail.
ResourceAccess state; ResourceAccess state;
}; };
using ResourceStates = std::unordered_map<ID3D12Resource*, TrackedResource>; using ResourceStates = std::unordered_map<ID3D12Resource*, TrackedResource>;
@@ -26,9 +28,9 @@ void publish_states(const ResourceStates& states) {
// Buffers/simultaneous-access textures have automatic legacy state // Buffers/simultaneous-access textures have automatic legacy state
// decay at ExecuteCommandLists completion. Only ordinary textures // decay at ExecuteCommandLists completion. Only ordinary textures
// retain these explicit transitions across submission boundaries. // retain these explicit transitions across submission boundaries.
const auto desc = resource_desc(entry.second.owner.Get()); const auto desc = resource_desc(entry.first);
if (desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER || (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS)) continue; if (desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER || (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS)) continue;
dx(entry.second.owner->SetPrivateData(submitted_access_tag, sizeof(ResourceAccess), &entry.second.state), "Publish submitted texture access"); dx(entry.first->SetPrivateData(submitted_access_tag, sizeof(ResourceAccess), &entry.second.state), "Publish submitted texture access");
} }
} }
struct Segment { struct Segment {
@@ -206,7 +208,6 @@ void STDMETHODCALLTYPE hook_ResourceBarrier(ID3D12GraphicsCommandList* self, UIN
if (barriers[i].Type == D3D12_RESOURCE_BARRIER_TYPE_TRANSITION && if (barriers[i].Type == D3D12_RESOURCE_BARRIER_TYPE_TRANSITION &&
(barriers[i].Transition.Subresource == 0 || barriers[i].Transition.Subresource == D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES)) { (barriers[i].Transition.Subresource == 0 || barriers[i].Transition.Subresource == D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES)) {
auto& tracked = access.recording->resource_states[barriers[i].Transition.pResource]; auto& tracked = access.recording->resource_states[barriers[i].Transition.pResource];
tracked.owner = retain(barriers[i].Transition.pResource);
auto& state = tracked.state; auto& state = tracked.state;
state = ResourceAccess(barriers[i].Transition.StateAfter); state = ResourceAccess(barriers[i].Transition.StateAfter);
state.pending_split = barriers[i].Flags == D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY; state.pending_split = barriers[i].Flags == D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY;
@@ -221,7 +222,7 @@ void STDMETHODCALLTYPE hook_Barrier(ID3D12GraphicsCommandList* self, UINT32 barr
const bool containsZero = range.NumMipLevels == 0 ? (range.IndexOrFirstMipLevel == 0 || range.IndexOrFirstMipLevel == UINT32_MAX) : const bool containsZero = range.NumMipLevels == 0 ? (range.IndexOrFirstMipLevel == 0 || range.IndexOrFirstMipLevel == UINT32_MAX) :
range.IndexOrFirstMipLevel == 0 && range.FirstArraySlice == 0 && range.FirstPlane == 0; range.IndexOrFirstMipLevel == 0 && range.FirstArraySlice == 0 && range.FirstPlane == 0;
if (containsZero) { if (containsZero) {
auto& tracked = access.recording->resource_states[barrier.pResource]; tracked.owner = retain(barrier.pResource); auto& tracked = access.recording->resource_states[barrier.pResource];
auto& state = tracked.state; state.enhanced = true; state.inherited = false; auto& state = tracked.state; state.enhanced = true; state.inherited = false;
state.layout = barrier.LayoutAfter; state.access = barrier.AccessAfter; state.sync = barrier.SyncAfter; state.layout = barrier.LayoutAfter; state.access = barrier.AccessAfter; state.sync = barrier.SyncAfter;
state.pending_split = barrier.SyncAfter == D3D12_BARRIER_SYNC_SPLIT; state.pending_split = barrier.SyncAfter == D3D12_BARRIER_SYNC_SPLIT;
+17
View File
@@ -161,6 +161,23 @@ int main(int argc, char** argv) {
using Live = unsigned long long(*)(); using Live = unsigned long long(*)();
const bool no_hooks = std::getenv("D4R_COMMAND_PROBE_NO_HOOKS") != nullptr; const bool no_hooks = std::getenv("D4R_COMMAND_PROBE_NO_HOOKS") != nullptr;
if (!no_hooks && shim.symbol<Install>("d4r_D3D12_InstallCommandBackend")(device.Get()) != 1) throw std::runtime_error("Command backend installation failed"); if (!no_hooks && shim.symbol<Install>("d4r_D3D12_InstallCommandBackend")(device.Get()) != 1) throw std::runtime_error("Command backend installation failed");
{
ComPtr<IDXGIFactory2> newerFactory; dx(factory->QueryInterface(IID_PPV_ARGS(newerFactory.GetAddressOf())), "Resize regression factory");
DXGI_SWAP_CHAIN_DESC1 desc{}; desc.Width = desc.Height = 64;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; desc.SampleDesc.Count = 1;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; desc.BufferCount = 2;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
ComPtr<IDXGISwapChain1> chain;
dx(newerFactory->CreateSwapChainForComposition(queue.Get(), &desc, nullptr, chain.GetAddressOf()), "Resize regression swap chain");
ComPtr<ID3D12Resource> backBuffer; dx(chain->GetBuffer(0, IID_PPV_ARGS(backBuffer.GetAddressOf())), "Resize regression back buffer");
NativeList recorded(device.Get());
transition(recorded.list.Get(), backBuffer.Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
transition(recorded.list.Get(), backBuffer.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
recorded.submit(device.Get(), queue.Get()); backBuffer.Reset();
// The submitted list remains alive and unreset during resize.
dx(chain->ResizeBuffers(2, 96, 64, DXGI_FORMAT_UNKNOWN, 0), "ResizeBuffers with retained submitted list");
std::printf("D3D12_RESIZE_REGRESSION submitted_list_alive=1 old_back_buffer_released=1\n");
}
auto record = shim.symbol<Record>("d4r_D3D12_RecordDiagnosticBoundary"); auto record = shim.symbol<Record>("d4r_D3D12_RecordDiagnosticBoundary");
auto live = shim.symbol<Live>("d4r_D3D12_LiveRecordings"); auto live = shim.symbol<Live>("d4r_D3D12_LiveRecordings");
const bool root_indirect = args.interop_mode == "indirect-root-reset"; const bool root_indirect = args.interop_mode == "indirect-root-reset";
+5 -1
View File
@@ -86,6 +86,7 @@ int main(int argc, char** argv) {
i == 2 ? DXGI_FORMAT_R32G32B32A32_FLOAT : i == 4 ? DXGI_FORMAT_R16_FLOAT : DXGI_FORMAT_R32_FLOAT; i == 2 ? DXGI_FORMAT_R32G32B32A32_FLOAT : i == 4 ? DXGI_FORMAT_R16_FLOAT : DXGI_FORMAT_R32_FLOAT;
if (args.pixel_profile == "unorm") desc.Format = i == 3 ? DXGI_FORMAT_B8G8R8A8_UNORM : if (args.pixel_profile == "unorm") desc.Format = i == 3 ? DXGI_FORMAT_B8G8R8A8_UNORM :
i == 2 ? DXGI_FORMAT_R16G16_SNORM : i == 4 ? DXGI_FORMAT_R32G32B32A32_FLOAT : desc.Format; i == 2 ? DXGI_FORMAT_R16G16_SNORM : i == 4 ? DXGI_FORMAT_R32G32B32A32_FLOAT : desc.Format;
if (args.pixel_profile == "depth-stencil" && i == 1) { desc.Format = DXGI_FORMAT_R32G8X24_TYPELESS; desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; }
if (i == 3) desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; if (i == 3) desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
D3D12_HEAP_PROPERTIES heap{}; heap.Type = D3D12_HEAP_TYPE_DEFAULT; D3D12_HEAP_PROPERTIES heap{}; heap.Type = D3D12_HEAP_TYPE_DEFAULT;
heap.CreationNodeMask = heap.VisibleNodeMask = 1; heap.CreationNodeMask = heap.VisibleNodeMask = 1;
@@ -214,7 +215,10 @@ 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_abi == "project") { if (args.ngx_abi == "project-legacy") {
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");
} 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")(241534723ull, directory.c_str(), device.Get(), 0x15, nullptr), "D3D12 Init");
+7
View File
@@ -115,6 +115,13 @@ public:
SharedPlane(ExternalApi& api, ID3D12Device* device, const D3D12_RESOURCE_DESC& texture) : api_(api) { SharedPlane(ExternalApi& api, ID3D12Device* device, const D3D12_RESOURCE_DESC& texture) : api_(api) {
UINT rows = 0; UINT64 rowBytes = 0; UINT rows = 0; UINT64 rowBytes = 0;
device->GetCopyableFootprints(&texture, 0, 1, 0, &footprint, &rows, &rowBytes, &bytes); device->GetCopyableFootprints(&texture, 0, 1, 0, &footprint, &rows, &rowBytes, &bytes);
if (texture.Format == DXGI_FORMAT_R32G8X24_TYPELESS || texture.Format == DXGI_FORMAT_D32_FLOAT_S8X24_UINT || texture.Format == DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS) {
// D3D12 exposes D32S8 as separate depth/stencil copy planes. Only
// subresource 0 (R32 depth) belongs to NGX; never copy interleaved
// 64-bit texels or touch the stencil plane.
if ((footprint.Footprint.Format != DXGI_FORMAT_R32_FLOAT && footprint.Footprint.Format != DXGI_FORMAT_R32_TYPELESS) || rowBytes != texture.Width * 4)
throw std::runtime_error("Unexpected D32S8 depth-plane copy footprint");
}
if (!bytes || bytes == UINT64_MAX || !footprint.Footprint.RowPitch) if (!bytes || bytes == UINT64_MAX || !footprint.Footprint.RowPitch)
throw std::runtime_error("Invalid shared texture footprint"); throw std::runtime_error("Invalid shared texture footprint");
buffer = make_buffer(device, bytes, D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_FLAG_SHARED, D3D12_RESOURCE_STATE_COMMON); buffer = make_buffer(device, bytes, D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_FLAG_SHARED, D3D12_RESOURCE_STATE_COMMON);
+3
View File
@@ -25,6 +25,7 @@ const Case cases[] = {
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, 1, "depth-r32f"}, {DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, 1, "depth-r32f"},
{DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_UNORM, 1, "depth-r16unorm"}, {DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_UNORM, 1, "depth-r16unorm"},
{DXGI_FORMAT_R24G8_TYPELESS, DXGI_FORMAT_R24_UNORM_X8_TYPELESS, 1, "depth24"}, {DXGI_FORMAT_R24G8_TYPELESS, DXGI_FORMAT_R24_UNORM_X8_TYPELESS, 1, "depth24"},
{DXGI_FORMAT_R32G8X24_TYPELESS, DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, 1, "depth32-stencil8-plane0"},
{DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_FLOAT, 4, "exposure-r16f"}, {DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_FLOAT, 4, "exposure-r16f"},
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, 4, "exposure-r32f"}, {DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, 4, "exposure-r32f"},
{DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, 4, "exposure-rgba16f"}, {DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_FLOAT, 4, "exposure-rgba16f"},
@@ -46,6 +47,7 @@ ComPtr<ID3D12Resource> make_texture(ID3D12Device* device, DXGI_FORMAT fmt, bool
D3D12_RESOURCE_DESC desc{}; desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; D3D12_RESOURCE_DESC desc{}; desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
desc.Width = width; desc.Height = height; desc.DepthOrArraySize = desc.MipLevels = desc.SampleDesc.Count = 1; desc.Width = width; desc.Height = height; desc.DepthOrArraySize = desc.MipLevels = desc.SampleDesc.Count = 1;
desc.Format = fmt; desc.Flags = uav ? D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS : D3D12_RESOURCE_FLAG_NONE; desc.Format = fmt; desc.Flags = uav ? D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS : D3D12_RESOURCE_FLAG_NONE;
if (fmt == DXGI_FORMAT_R32G8X24_TYPELESS) desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
D3D12_HEAP_PROPERTIES heap{}; heap.Type = D3D12_HEAP_TYPE_DEFAULT; heap.CreationNodeMask = heap.VisibleNodeMask = 1; D3D12_HEAP_PROPERTIES heap{}; heap.Type = D3D12_HEAP_TYPE_DEFAULT; heap.CreationNodeMask = heap.VisibleNodeMask = 1;
ComPtr<ID3D12Resource> result; ComPtr<ID3D12Resource> result;
dx(device->CreateCommittedResource(&heap, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COMMON, dx(device->CreateCommittedResource(&heap, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COMMON,
@@ -182,6 +184,7 @@ int main(int argc, char** argv) {
for (unsigned iteration = 0; iteration < args.iterations; ++iteration) for (const auto& item : cases) { for (unsigned iteration = 0; iteration < args.iterations; ++iteration) for (const auto& item : cases) {
PixelSpec spec = pixel_spec(item.plane, item.resource); if (!pixel_supported(item.plane, spec.storage)) throw std::runtime_error("Unsupported fixture plane"); PixelSpec spec = pixel_spec(item.plane, item.resource); if (!pixel_supported(item.plane, spec.storage)) throw std::runtime_error("Unsupported fixture plane");
auto input = make_texture(device.Get(), item.resource); SharedPlane shared(external, device.Get(), resource_desc(input.Get())); auto input = make_texture(device.Get(), item.resource); SharedPlane shared(external, device.Get(), resource_desc(input.Get()));
std::printf("PIXEL_FOOTPRINT format=%s plane0_format=%u pitch=%u storage_bytes=%u\n", item.name, shared.footprint.Footprint.Format, shared.footprint.Footprint.RowPitch, spec.bytes);
auto upload = make_buffer(device.Get(), shared.bytes, D3D12_HEAP_TYPE_UPLOAD, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_GENERIC_READ); auto upload = make_buffer(device.Get(), shared.bytes, D3D12_HEAP_TYPE_UPLOAD, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_GENERIC_READ);
void* data = nullptr; D3D12_RANGE noRead{}; dx(upload->Map(0, &noRead, &data), "Pixel fixture upload"); void* data = nullptr; D3D12_RANGE noRead{}; dx(upload->Map(0, &noRead, &data), "Pixel fixture upload");
std::memset(data, 0x5a, size_t(shared.bytes)); for (unsigned y = 0; y < height; ++y) fixture(static_cast<uint8_t*>(data) + y * shared.footprint.Footprint.RowPitch, y, spec); upload->Unmap(0, nullptr); std::memset(data, 0x5a, size_t(shared.bytes)); for (unsigned y = 0; y < height; ++y) fixture(static_cast<uint8_t*>(data) + y * shared.footprint.Footprint.RowPitch, y, spec); upload->Unmap(0, nullptr);
+7
View File
@@ -13,6 +13,13 @@ std::unordered_map<Handle*, std::shared_ptr<d4r::win::Feature>> features;
std::unordered_map<void*, bool> parameters; std::unordered_map<void*, bool> parameters;
constexpr unsigned failure = 0xBAD00002, invalid = 0xBAD00005; constexpr unsigned failure = 0xBAD00002, invalid = 0xBAD00005;
unsigned call(const std::function<unsigned()>& fn) noexcept { unsigned call(const std::function<unsigned()>& fn) noexcept {
static std::once_flag diagnosticOutput;
std::call_once(diagnosticOutput, [] {
if (std::getenv("D4R_DIAG_DIR")) {
std::setvbuf(stdout, nullptr, _IONBF, 0);
std::setvbuf(stderr, nullptr, _IONBF, 0);
}
});
try { return fn(); } try { return fn(); }
catch (const std::exception& error) { catch (const std::exception& error) {
std::fprintf(stderr, "D4R_WINDOWS_FAILURE %s\n", error.what()); return failure; std::fprintf(stderr, "D4R_WINDOWS_FAILURE %s\n", error.what()); return failure;
+1 -1
View File
@@ -142,7 +142,7 @@ struct Args {
else if (key == "--stream-lifetime") stream_lifetime = value; else if (key == "--stream-lifetime") stream_lifetime = value;
else if (key == "--interop-mode") interop_mode = value; else if (key == "--interop-mode") interop_mode = value;
else if (key == "--pixel-profile") { else if (key == "--pixel-profile") {
if (value != "baseline" && value != "packed" && value != "unorm") throw std::runtime_error("pixel-profile must be baseline, packed or unorm"); if (value != "baseline" && value != "packed" && value != "unorm" && value != "depth-stencil") throw std::runtime_error("Invalid pixel-profile");
pixel_profile = value; pixel_profile = value;
} }
else if (key == "--barrier-mode") { else if (key == "--barrier-mode") {
+3
View File
@@ -26,6 +26,9 @@ inline PixelSpec pixel_spec(unsigned plane, DXGI_FORMAT format) {
case DXGI_FORMAT_R16G16_UNORM: return {rg16unorm, 4, false}; case DXGI_FORMAT_R16G16_UNORM: return {rg16unorm, 4, false};
case DXGI_FORMAT_R32_FLOAT: case DXGI_FORMAT_R32_TYPELESS: case DXGI_FORMAT_D32_FLOAT: case DXGI_FORMAT_R32_FLOAT: case DXGI_FORMAT_R32_TYPELESS: case DXGI_FORMAT_D32_FLOAT:
return {r32f, 4, plane == depth || plane == exposure}; return {r32f, 4, plane == depth || plane == exposure};
case DXGI_FORMAT_R32G8X24_TYPELESS: case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS:
if (plane != depth) throw std::runtime_error("Depth/stencil resources are supported only for the depth input");
return {r32f, 4, true}; // D3D12 subresource 0 is the R32 depth plane.
case DXGI_FORMAT_R16_FLOAT: return {r16f, 2, false}; case DXGI_FORMAT_R16_FLOAT: return {r16f, 2, false};
case DXGI_FORMAT_D24_UNORM_S8_UINT: case DXGI_FORMAT_R24G8_TYPELESS: case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: case DXGI_FORMAT_D24_UNORM_S8_UINT: case DXGI_FORMAT_R24G8_TYPELESS: case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:
return {depth24, 4, false}; return {depth24, 4, false};