TL;DR: Add an experimental FSR Ray Regeneration backend on Windows, run the K

network natively on HIP with Vulkan->HIP shared memory, extend K kernels to
gfx11 (RDNA3), and add dependency/packaging/test tooling around them.

Windows: FSR RR backend, native HIP K runner, gfx11 K kernels, tooling

FSR Ray Regeneration (experimental)
- Add fsr_rr_backend.{h,cpp}, fsr_rr_passes.h and the fsr_rr/ prepare and
  compose compute shaders. They run AMD's FidelityFX denoiser
  (amd_fidelityfx_denoiser_dx12.dll, SDK 2.3, not bundled) on a private
  vkd3d-proton device.
- Add the amdxc64 shim (tools/windows/amdxc64_shim) and two vkd3d-proton
  patches for libcxx includes. AMD's amdxc64.dll is never loaded.
- d4r_nvngx_windows: RrFeature now selects between the native DLSS RR
  backend and the FSR backend (D4R_RR_BACKEND=fsr). After the denoiser it
  runs an internal Super Sampling stage (feature 1) to upscale. If SR is
  unavailable and output != render size, the frame fails; it is never
  returned noisy.
- d4r.ini / player_config: new [RayReconstruction] FsrDenoiser,
  FsrStabilityBias (auto or >= 0) and FsrDetail (0-1), with validation, and
  matching D4R_FSR_* environment variables.
- Add the FSR RR fixture, output-check and ngx test scripts, and a shader
  compile script.

Native HIP K runner (Windows)
- CMake: add d4r_engine_k_hip (d4r-k-hip) plus separate volk/engine copies,
  because VK_USE_PLATFORM_WIN32_KHR changes volk's entry points.
- Share Vulkan memory with HIP via Win32 opaque handles on Windows
  (VK_KHR_external_memory_win32) and via fds on Linux. Handle types are now
  intptr_t, and the platform choice is in D4R_EXT_MEMORY_* macros.
- HipNetwork: add debugRepeatHashes and debugNodeMicros, exposed as
  D4R_HIP_REPEAT_CHECK, D4R_HIP_REPEAT_ALL and D4R_HIP_NODE_PROFILE in
  replay.cpp.
- replay.cpp: display-resolution-motion frames (FrameFlagDisplayMotion) no
  longer require near2rnd == out2rnd. Both values must still be in (0, 1].

K kernels on gfx11
- Allow position-token and enc1 tiling on __GFX11__ as well as gfx12.
- Add an opt-in ds_bpermute cross-half exchange
  (D4R_WINDOWS_GFX11_DS_EXCHANGE, enabled for K on Windows gfx11). M keeps
  its LDS path.
- Fix a block-barrier race in the position layer. Without POS_VSHARE a fast
  wave could overwrite its hb rows while a slower wave still read them for a
  later head, giving rare wrong results on gfx1101 (enc0 outputs differing
  between identical launches).

Packaging and install
- install.bat, d4r.ini, WINDOWS_README: FSR option, and
  install_dependencies.py to install pinned NVIDIA dependencies with staged,
  hash-verified replacement and rollback.
- Update the release package, local package refresh and test scripts.
- Add the InteropBatchInputCopies option (default false). Batching is faster
  on some synthetic runs, but player temporal exactness is unresolved.

Tooling and tests
- K model: capture_k_weights, build_k_model, compare_k_outputs,
  benchmark_k_hip, build_capture_runtime, test_k_model and the
  zluda-k-output-capture patch. Captured weights and models are private
  local assets and must not enter release archives.
- Synthetic probes: test_m_ngx, test_sr_dynamic,
  prepare_rr_dynamic_fixture and the d3d12/rr_ngx probe updates.
- Add tests/windows/test_install_dependencies.py.

Docs
- Update the windows-fsr-rr, k-performance, local-package, rr-interop and
  rr-model-cache docs. Add windows-parity-handoff.md and
  windows-parity-continuation-prompt.md.
This commit is contained in:
ayo committed 2026-10-17 01:43:06 -04:00
1 parent 247fe1092d
commit a617c818de
62 files changed
+5894 -89

No files matched your search

+83
View File
@@ -0,0 +1,83 @@
// Experimental app-local amdxc64.dll for running AMD's FSR Ray Regeneration denoiser on a vkd3d-proton device
// (D3D12 over Vulkan) on native Windows RDNA3. AMD's real amdxc64.dll expects AMD's own ID3D12Device and must not
// see a vkd3d device. This shim only answers the capability queries the denoiser makes (recovered from the call
// sites in amd_fidelityfx_denoiser_dx12.dll 1.2.0.2740): it advertises the FP8 wave-matrix shape the denoiser's
// shaders use. The shaders themselves are executed by dxil-spirv's FP8 emulation inside vkd3d-proton, not by
// hardware FP8. It does not forward anything to AMD's driver.
#include <windows.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
static void trace(const char* what, const GUID* g) {
const char* path = getenv("D4R_AMDXC_LOG"); if (!path) return;
FILE* f = fopen(path, "a"); if (!f) return;
if (g) fprintf(f, "%s {%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", what, g->Data1, g->Data2, g->Data3, g->Data4[0], g->Data4[1], g->Data4[2], g->Data4[3], g->Data4[4], g->Data4[5], g->Data4[6], g->Data4[7]);
else fprintf(f, "%s\n", what);
fclose(f);
}
static const GUID IID_Factory = {0x014937ec, 0x9288, 0x446f, {0xa9, 0xac, 0xd7, 0x5a, 0x8e, 0x3a, 0x98, 0x4f}};
static const GUID IID_WaveMatrix = {0xf714e11a, 0xb54e, 0x4e0f, {0xab, 0xc5, 0xdf, 0x58, 0xb1, 0x81, 0x33, 0xd1}};
static const GUID IID_Intrinsics = {0xba019d53, 0xccab, 0x4cbd, {0xb5, 0x6a, 0x72, 0x30, 0xed, 0x43, 0x30, 0xad}};
static const GUID IID_DeviceInfo = {0xb58d6601, 0x7401, 0x4234, {0x81, 0x80, 0x6f, 0xeb, 0xfc, 0x0e, 0x48, 0x4c}};
typedef struct Obj { void** vtbl; LONG refs; } Obj;
static ULONG WINAPI AddRef_(void* self) { return (ULONG)InterlockedIncrement(&((Obj*)self)->refs); }
static ULONG WINAPI Release_(void* self) { return (ULONG)InterlockedDecrement(&((Obj*)self)->refs); } // statics: never freed
static HRESULT WINAPI QI_(void* self, const GUID* iid, void** out) { (void)iid; *out = self; AddRef_(self); return S_OK; }
static HRESULT WINAPI Unused_(void* self) { (void)self; return (HRESULT)0x80004001; } // E_NOTIMPL
static HRESULT WINAPI Ok_(void* self) { (void)self; return S_OK; }
static HRESULT WINAPI OkOpcode_(void* self, uint32_t opcode) { (void)self; (void)opcode; trace("EnableOpcode", NULL); return S_OK; }
// IID_WaveMatrix slot 16: (count in/out, properties); entry = M, N, K (u64), then 0xb, 0xb, 1, 1 (u32), flag (u8)
static HRESULT WINAPI WaveMatrixProperties_(void* self, uint64_t* count, uint8_t* props) {
(void)self; trace("WaveMatrixProperties", NULL);
if (*count < 1) { *count = 1; return S_OK; }
memset(props, 0, 0x30);
uint64_t shape[3] = {16, 16, 16}; memcpy(props, shape, sizeof(shape));
uint32_t types[4] = {0xb, 0xb, 1, 1}; memcpy(props + 0x18, types, sizeof(types));
props[0x28] = 0;
*count = 1;
return S_OK;
}
// IID_DeviceInfo slot 3: (request[0x50], size). Command 2 fills 0x40 nonzero bytes at +0x10.
static HRESULT WINAPI DeviceInfo_(void* self, uint8_t* request, uint64_t size) {
(void)self; trace("DeviceInfo", NULL);
if (size < 0x50) return (HRESULT)0x80070057;
uint32_t command; memcpy(&command, request, 4);
if (command != 2) return (HRESULT)0x80004001;
uint64_t info[8] = {0x1101, 1, 0x20, 0x2000, 64, 1, 1, 1}; // placeholders, see docs/windows-fsr-rr.md
memcpy(request + 0x10, info, sizeof(info));
return S_OK;
}
#define VT 24
static void* vt_factory[VT], *vt_wave[VT], *vt_intr[VT], *vt_info[VT];
static Obj factory = {vt_factory, 1}, wave = {vt_wave, 1}, intr = {vt_intr, 1}, info = {vt_info, 1};
static HRESULT WINAPI CreateInterface_(void* self, void* device, const GUID* iid, void** out) {
(void)self; (void)device; *out = NULL; trace("CreateInterface", iid);
Obj* o = NULL;
if (IsEqualGUID(iid, &IID_WaveMatrix)) o = &wave;
else if (IsEqualGUID(iid, &IID_Intrinsics)) o = &intr;
else if (IsEqualGUID(iid, &IID_DeviceInfo)) o = &info;
if (!o) return (HRESULT)0x80004002; // E_NOINTERFACE
AddRef_(o); *out = o; return S_OK;
}
static void init_tables(void) {
void** all[4] = {vt_factory, vt_wave, vt_intr, vt_info};
for (int t = 0; t < 4; ++t) {
all[t][0] = (void*)QI_; all[t][1] = (void*)AddRef_; all[t][2] = (void*)Release_;
for (int i = 3; i < VT; ++i) all[t][i] = (void*)Unused_;
}
vt_factory[3] = (void*)CreateInterface_;
vt_wave[16] = (void*)WaveMatrixProperties_;
vt_intr[3] = (void*)Ok_; vt_intr[4] = (void*)OkOpcode_; vt_intr[5] = (void*)Ok_;
vt_info[3] = (void*)DeviceInfo_;
}
__declspec(dllexport) HRESULT WINAPI AmdExtD3DCreateInterface(void* device, const GUID* iid, void** out) {
static volatile LONG ready = 0; (void)device; *out = NULL; trace("AmdExtD3DCreateInterface", iid);
if (!ready) { init_tables(); InterlockedExchange(&ready, 1); } // idempotent: concurrent init writes the same values
if (!IsEqualGUID(iid, &IID_Factory)) return (HRESULT)0x80004002; // E_NOINTERFACE
AddRef_(&factory); *out = &factory; return S_OK;
}
+107
View File
@@ -0,0 +1,107 @@
"""Compare strict HIP K candidates against a fixed model on synthetic temporal frames.
Each candidate overlays only named code objects in a private model copy. All five
frame products must match the baseline hashes exactly. Raw files created by this
run are hashed and deleted immediately after each process; existing evidence is
never removed. Reported frame timestamps include Vulkan front/back and CPU gaps.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import re
import shutil
import subprocess
import sys
repo = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(repo / '.tools/python/vendor'))
import numpy as np
def main():
parser = argparse.ArgumentParser(description=__doc__)
for name in ('runner', 'model', 'case', 'output'):
parser.add_argument('--' + name, required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--candidate', action='append', default=[], metavar='LABEL=OBJECT_DIRECTORY')
parser.add_argument('--repeats', type=int, default=25)
args = parser.parse_args()
if not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('output must stay under ignored build/')
args.output.mkdir(parents=True, exist_ok=False)
frame_count = sum(1 for line in args.case.read_text().splitlines()
if line.split() and line.split()[0] in ('frame', 'frame_auto'))
if not frame_count:
parser.error('case must contain explicit frame or frame_auto entries')
expected_products = {f'frame-{i}.{suffix}.bin' for i in range(frame_count)
for suffix in ('312', '288', '296', '304', 'head')}
env = {k: v for k, v in os.environ.items() if not k.startswith('D4R_')}
env['PATH'] = str(repo / '.tools/therock-10.2.0a20260929/_rocm_sdk_core/bin') + os.pathsep + env['PATH']
report = {'synthetic': True, 'gameAccessed': False, 'exactGate': 'all five frame products',
'timingScope': 'Vulkan front + HIP network + Vulkan back, including CPU synchronization gaps',
'runnerSha256': hashlib.sha256(args.runner.read_bytes()).hexdigest(),
'caseSha256': hashlib.sha256(args.case.read_bytes()).hexdigest(), 'runs': []}
reference = None
variants = [('baseline', None)] + [tuple(s.split('=', 1)) for s in args.candidate]
for label, source in variants:
if not re.fullmatch(r'[a-zA-Z0-9_-]+', label):
parser.error('candidate label must be letters, digits, underscores or hyphens')
output = args.output / label
output.mkdir()
model = args.model
row = {'label': label, 'objects': {}}
if source:
model = output / 'model'
shutil.copytree(args.model, model)
inputs = sorted(Path(source).resolve().glob('*.hsaco')) + sorted(Path(source).resolve().glob('*.spv'))
for obj in inputs:
destination = model / 'hip' / obj.name if obj.suffix == '.hsaco' else model / obj.name
if not destination.is_file():
raise RuntimeError('candidate must replace an existing object: ' + obj.name)
shutil.copy2(obj, destination)
row['objects'][obj.name] = hashlib.sha256(obj.read_bytes()).hexdigest()
if not row['objects']:
raise RuntimeError('candidate contains no objects')
manifest = json.loads((model / 'model.json').read_text())
for name, digest in row['objects'].items():
manifest['sha256'][name] = digest
manifest['experimentalOverlay'] = row['objects']
(model / 'model.json').write_text(json.dumps(manifest, indent=2) + '\n')
command = [str(args.runner), str(model), str(args.case), str(output / 'frames'), str(args.repeats), 'hip']
with (output / 'stdout.log').open('wb') as out, (output / 'stderr.log').open('wb') as err:
result = subprocess.run(command, env=env, stdout=out, stderr=err, timeout=240)
row['exitCode'] = result.returncode
hashes = {}
finite = True
for path in sorted((output / 'frames').glob('frame-*.bin')):
hashes[path.name] = hashlib.sha256(path.read_bytes()).hexdigest()
if path.name.endswith('.312.bin'):
values = np.fromfile(path, '<f2').astype('f4')
finite = finite and bool(np.isfinite(values).all() and np.var(values) > 1e-6)
if not path.resolve().is_relative_to(args.output):
raise RuntimeError('raw cleanup escaped the fresh output directory')
path.unlink()
row['hashes'] = hashes
row['finiteNonuniformOutput'] = finite
if reference is None:
reference = hashes
row['differentProducts'] = [k for k in sorted(set(hashes) | set(reference)) if hashes.get(k) != reference.get(k)]
row['exact'] = result.returncode == 0 and set(hashes) == expected_products and not row['differentProducts'] and finite
text = (output / 'stdout.log').read_text(errors='replace')
timing = re.search(r'full native frame: ([0-9.]+) us GPU median \(min ([0-9.]+)\)', text)
if timing:
row['measuredMedianUs'] = float(timing[1])
row['measuredMinUs'] = float(timing[2])
report['runs'].append(row)
(args.output / 'summary.json').write_text(json.dumps(report, indent=2) + '\n')
print(f'{label}: exact={row["exact"]} differing={len(row["differentProducts"])} median_us={row.get("measuredMedianUs")}', flush=True)
if result.returncode:
return 1
report['passed'] = all(row['exact'] for row in report['runs'])
(args.output / 'summary.json').write_text(json.dumps(report, indent=2) + '\n')
return 0 if report['passed'] else 1
if __name__ == '__main__':
raise SystemExit(main())
+47 -2
View File
@@ -11,6 +11,7 @@ from pathlib import Path
import re
import statistics
import subprocess
import shutil
def main():
@@ -24,9 +25,17 @@ def main():
help='diagnostic CPU stage logging; adds overhead to timings')
parser.add_argument('--candidate-native', type=lambda s: Path(s).resolve(),
help='compare an experimental native directory instead of input batching')
parser.add_argument('--control-native', type=lambda s: Path(s).resolve(),
help='use an explicit verified control kernel directory while retaining runtime dependencies')
parser.add_argument('--player-config', action='store_true', help='select batching/native paths through staged player INI')
parser.add_argument('--sequence', choices=('abba', 'controls', 'batches'), default='abba',
help='paired comparison, or repeated identical settings to diagnose baseline determinism')
parser.add_argument('--reset-every-frame', action='store_true', help='diagnose history dependence; not representative temporal performance')
parser.add_argument('--profile-kernels', action='store_true',
help='diagnostic deferred GPU events, allowing legacy-stream dependencies; timings are instrumented')
args = parser.parse_args()
if args.player_config and (args.profile_stages or args.profile_kernels):
parser.error('player mode clears diagnostic profiling flags; use a separate instrumented run')
if args.frames < 8 or args.frames > 120:
parser.error('frames must be 8..120')
if not re.fullmatch(r'[1-9][0-9]*x[1-9][0-9]*', args.resolution):
@@ -50,6 +59,21 @@ def main():
native = runtime / 'native'
if (native / 'gfx1101').is_dir():
native = native / 'gfx1101'
if args.control_native:
native = args.control_native
report['playerConfig'] = args.player_config
report['sequence'] = args.sequence
report['resetEveryFrame'] = args.reset_every_frame
module = args.shim
if args.player_config:
runtime = args.output / 'player'
shutil.copytree(args.runtime, runtime)
module = runtime / 'd4r_nvngx.dll'
shutil.copy2(args.shim, module)
for source, relative in ((args.core, 'ngx/_nvngx.dll'), (args.dlss, 'dlss/nvngx_dlss.dll')):
destination = runtime / relative
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, destination)
nvapi = runtime / 'nvapi/nvapi64.dll'
if not nvapi.is_file():
nvapi = runtime / 'nvapi/gfx1101/nvapi64.dll'
@@ -82,7 +106,8 @@ def main():
D4R_ZLUDA_PROFILE_EVERY='1')
try:
reference = None
for index, batched in enumerate((False, True, True, False)):
variants = {'abba': (False, True, True, False), 'controls': (False,) * 4, 'batches': (True,) * 4}
for index, batched in enumerate(variants[args.sequence]):
label = ('native-candidate' if args.candidate_native else 'batch') if batched else 'control'
output = args.output / f'{index}-{label}'
output.mkdir()
@@ -92,17 +117,32 @@ def main():
else:
env.pop('D4R_BATCH_INPUT_COPIES', None)
env['D4R_ZLUDA_NATIVE_DIR'] = str(args.candidate_native if batched and args.candidate_native else native)
command = [str(args.probe), '--module', str(args.shim), '--hip-root', str(runtime / 'hip'),
if args.player_config:
config = (Path(__file__).resolve().parents[2] / 'packaging/windows/d4r.ini').read_text()
config = re.sub(r'(?m)^BatchInputCopies\s*=\s*(?:true|false)\s*$',
'BatchInputCopies = ' + str(batched and not args.candidate_native).lower(), config)
selected_native = args.candidate_native if batched and args.candidate_native else native
config = config.replace('NativeDir = auto', 'NativeDir = ' + str(selected_native))
(runtime / 'd4r.ini').write_text(config)
command = [str(args.probe), '--module', str(module), '--hip-root', str(runtime / 'hip'),
'--cuda-dll', str(runtime / 'zluda/nvcuda.dll'), '--ngx-core', str(args.core),
'--dlss-dll', str(args.dlss), '--nvapi-dll', str(nvapi),
'--ngx-abi', 'project', '--interop-mode', 'command-list', '--preset', '11',
'--ngx-output-resolution', args.resolution,
'--ngx-input-resolution', args.input_resolution, '--iterations', str(args.frames)]
if args.player_config:
command += ['--player-mode', '1']
if args.reset_every_frame:
command += ['--reset-every-frame', '1']
with (output / 'stdout.log').open('wb') as out, (output / 'stderr.log').open('wb') as err:
result = subprocess.run(command, env=env, stdout=out, stderr=err, timeout=180)
if args.player_config and (runtime / 'd4r_nvngx.log').exists():
shutil.copy2(runtime / 'd4r_nvngx.log', output / 'player.log')
text = (output / 'stdout.log').read_text(errors='replace')
if result.returncode or 'PASS D3D12_DLSS' not in text:
raise RuntimeError(f'run {index} failed ({result.returncode}); inspect {output}')
if args.reset_every_frame and 'D3D12_RESET every_frame=1' not in text:
raise RuntimeError('Requested reset diagnostic was not applied')
iw, ih = map(int, args.input_resolution.split('x'))
expected_size = f'D3D12_NGX_DIMENSIONS input={iw}x{ih} output={ow}x{oh}'
if expected_size not in text or f'D4R_INTEROP async=0 batch_inputs={int(batched and not args.candidate_native)}' not in text:
@@ -118,6 +158,9 @@ def main():
if reference is None:
reference = hashes
elif hashes != reference:
report['mismatchRun'] = index
report['differentFrames'] = [name for name, digest in hashes.items() if reference.get(name) != digest]
report['mismatchedOutputHashes'] = hashes
raise RuntimeError(f'run {index}: output differs from control')
report['runs'].append({'batch': batched and not bool(args.candidate_native),
'candidate': batched, 'allFrameMs': times,
@@ -125,6 +168,8 @@ def main():
print(f'run={index} variant={label} warmed_cpu_interop_ms={statistics.median(times[4:]):.3f}', flush=True)
for mode in (False, True):
values = [t for row in report['runs'] if row['candidate'] == mode for t in row['allFrameMs'][4:]]
if not values:
continue
key = ('candidateMedianMs' if args.candidate_native else 'batchMedianMs') if mode else 'controlMedianMs'
report[key] = statistics.median(values)
report['passed'] = True
+95
View File
@@ -0,0 +1,95 @@
"""Build an isolated diagnostic ZLUDA runtime using the already-built LLVM tree.
Never replaces the delivered runtime or rebuilds LLVM. Output is local under
build/. The copied runtime is for audited synthetic captures, not game use.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import shutil
import subprocess
def sha(path):
with path.open('rb') as stream:
return hashlib.file_digest(stream, 'sha256').hexdigest()
def main():
repo = Path(__file__).resolve().parents[2]
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--runtime', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--output', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--jobs', type=int, default=4)
args = parser.parse_args()
if args.output.exists() or not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('use a fresh directory under ignored build/')
if args.output.is_relative_to(args.runtime) or not 1 <= args.jobs <= 8:
parser.error('output must be outside input runtime; jobs must be 1..8')
source = repo / 'external/ZLUDA'
if '"hiluma_engine_output_depthreg_mvhi_ldr_max_v2_rel" => Some(328)' not in (source / 'zluda/src/impl/d4r_replay.rs').read_text():
parser.error('apply tools/windows/zluda-k-output-capture.patch to the prepared Windows ZLUDA source first')
llvm = repo / 'build/zluda-llvm-windows-gnu'
rust = repo / '.tools/rust-1.98.1-gnu'
compiler = repo / '.tools/llvm-mingw-20260922-ucrt-x86_64'
target = repo / 'build/zluda-rust-windows-gnu'
for path in (llvm / 'bin/llvm-config.exe', rust / 'bin/cargo.exe', source / 'ptx/lib/zluda_ptx_impl.bc'):
if not path.is_file():
parser.error('prebuilt dependency missing: ' + str(path))
args.output.mkdir(parents=True)
artifact = target / 'x86_64-pc-windows-gnu/release/nvcuda.dll'
if artifact.exists():
shutil.copy2(artifact, args.output / 'previous-cargo-nvcuda.dll')
environment = os.environ.copy()
environment.update(CARGO_HOME=str(repo / '.tools/cargo-home'), CARGO_TARGET_DIR=str(target),
CARGO_BUILD_JOBS=str(args.jobs), RUSTC=str(rust / 'bin/rustc.exe'),
CC=str(compiler / 'bin/x86_64-w64-mingw32-clang.exe'),
CXX=str(compiler / 'bin/x86_64-w64-mingw32-clang++.exe'),
AR=str(compiler / 'bin/llvm-ar.exe'), CXXSTDLIB='c++',
LIBRARY_PATH=str(rust / 'lib/rustlib/x86_64-pc-windows-gnu/lib/self-contained'),
CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER=str(compiler / 'bin/x86_64-w64-mingw32-clang.exe'),
CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUSTFLAGS='-C link-arg=-static',
ZLUDA_LLVM_BUILD_DIR=str(llvm))
environment['PATH'] = os.pathsep.join(str(p) for p in (rust / 'bin', compiler / 'bin',
repo / '.tools/python/cmake/data/bin', repo / '.tools/python/bin', Path('C:/Program Files/Git/usr/bin'))) + os.pathsep + environment['PATH']
report = dict(passed=False, diagnosticOnly=True, llvmRebuilt=False, deliveredRuntimeChanged=False,
originalRuntimeSha256=sha(args.runtime / 'zluda/nvcuda.dll'),
captureSourceSha256=sha(source / 'zluda/src/impl/d4r_replay.rs'),
llvmConfigSha256=sha(llvm / 'bin/llvm-config.exe'))
try:
command = [str(rust / 'bin/cargo.exe'), 'build', '--offline', '--locked', '--release',
'--target', 'x86_64-pc-windows-gnu', '-p', 'zluda']
with (args.output / 'cargo.log').open('wb') as log:
process = subprocess.Popen(command, cwd=source, env=environment, stdout=log, stderr=subprocess.STDOUT)
try:
code = process.wait(timeout=1800)
except subprocess.TimeoutExpired:
subprocess.run(['taskkill', '/PID', str(process.pid), '/T', '/F'], check=False, timeout=30,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
process.wait(timeout=30)
raise RuntimeError('Capture runtime build timed out; process tree terminated')
if code:
raise subprocess.CalledProcessError(code, command)
runtime = args.output / 'runtime'
shutil.copytree(args.runtime, runtime)
shutil.copy2(artifact, runtime / 'zluda/nvcuda.dll')
for name in ('libc++.dll', 'libunwind.dll'):
shutil.copy2(compiler / 'bin' / name, runtime / 'zluda' / name)
if sha(args.runtime / 'zluda/nvcuda.dll') != report['originalRuntimeSha256']:
raise RuntimeError('Original runtime changed during build')
report.update(passed=True, runtime=str(runtime), runtimeSha256=sha(runtime / 'zluda/nvcuda.dll'),
sourceBase=subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=source, text=True).strip(),
sourceWorkingTree='local modified; capture source hash recorded, no clean source assertion',
gpuValidation=False, published=False)
print(json.dumps(report, indent=2))
except Exception as error:
report['error'] = str(error)
raise
finally:
(args.output / 'build.json').write_text(json.dumps(report, indent=2) + '\n')
if __name__ == '__main__':
main()
+146
View File
@@ -0,0 +1,146 @@
"""Build the experimental FSR Ray Regeneration payload for native Windows: vkd3d-proton + the amdxc64 shim.
Output (a fresh directory under ignored build/):
fsr/d4r_vkd3d_core.dll vkd3d-proton's d3d12core.dll, built for Windows with dxil-spirv's scalar FP8 fallback
(upstream PR 307) and extended emulation, renamed so it cannot collide with the system's
fsr/amdxc64.dll d4r's capability shim (tools/windows/amdxc64_shim); never forwards to AMD's driver
licenses/ vkd3d-proton (LGPL-2.1), dxil-spirv (MIT), dxbc-spirv licence texts
SOURCE.txt exact commits, patches and build flags: the LGPL source offer for the shipped binary
fsr-payload.json hashes of everything above
AMD's amd_fidelityfx_denoiser_dx12.dll is NOT part of the payload (installed hash-pinned by install.bat on request).
Needs network once (git clone of vkd3d-proton and its submodules), the vendored llvm-mingw, pinned glslang 16.6.0,
ninja, and meson (installed with pip --target into .tools/meson-pkg when missing).
"""
import argparse
import hashlib
import json
import os
import shutil
import subprocess
import sys
from pathlib import Path
repo = Path(__file__).resolve().parents[2]
VKD3D_URL = 'https://github.com/HansKristian-Work/vkd3d-proton.git'
VKD3D_COMMIT = '3dfc6f07d0953b1e8b41705275c2c59cc7374fc5'
DXIL_FSR_MERGE = 'f9f6294f3a6795e071bef3a64d2bee99e7c1b7bc' # dxil-spirv PR 307 merge (scalar FP8 fallback)
GLSLANG_SHA256 = '9643709c02c83e26773f96b6a87f197ab2e84f78be0879ad8fb3619418b5d9ad'
def sha(path):
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
def run(command, cwd=None, env=None, log=None):
print('+ ' + ' '.join(str(part) for part in command), flush=True)
with open(log, 'ab') if log else open(os.devnull, 'wb') as out:
result = subprocess.run([str(part) for part in command], cwd=cwd, env=env, stdout=out, stderr=subprocess.STDOUT)
if result.returncode:
raise RuntimeError(f'command failed ({result.returncode}): {command[0]} {command[1] if len(command) > 1 else ""}; see {log}')
def git(args, cwd, log, identity=False):
prefix = ['git', '-c', 'user.name=d4r', '-c', 'user.email=d4r@localhost'] if identity else ['git']
run(prefix + list(args), cwd=cwd, log=log)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--output', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--source', type=lambda p: Path(p).resolve(),
help='reuse an existing vkd3d-proton clone (must be a checkout of the pinned commit); default: fresh clone under --output/_work')
args = parser.parse_args()
if not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('output must stay under ignored build/')
args.output.mkdir(parents=True, exist_ok=False)
work = args.output / '_work'
work.mkdir()
log = work / 'build.log'
tools = repo / '.tools'
mingw = tools / 'llvm-mingw-20260922-ucrt-x86_64/bin'
glslang = tools / 'glslang-16.6.0/bin/glslangValidator.exe'
ninja = tools / 'python/bin/ninja.exe'
for required in (mingw / 'x86_64-w64-mingw32-clang.exe', glslang, ninja):
if not required.is_file():
raise RuntimeError('missing tool: ' + str(required))
if sha(glslang) != GLSLANG_SHA256:
raise RuntimeError('requires the pinned glslang 16.6.0')
meson_path = tools / 'meson-pkg'
if not (meson_path / 'mesonbuild').is_dir():
run([sys.executable, '-m', 'pip', 'install', '--target', meson_path, 'meson'], log=log)
source = args.source
if source is None:
source = work / 'vkd3d-proton'
git(['clone', VKD3D_URL, source], work, log)
git(['checkout', '-q', '-f', '--detach', VKD3D_COMMIT], source, log)
git(['submodule', 'update', '--init', '--recursive', '-q'], source, log)
if args.source is not None:
git(['submodule', 'update', '--force', '--recursive', '-q'], source, log)
# dxil-spirv: the accepted scalar FP8 fallback (PR 307), cherry-picked as the Linux build script does
dxil = source / 'subprojects/dxil-spirv'
git(['fetch', '-q', 'origin', DXIL_FSR_MERGE], dxil, log)
listing = subprocess.run(['git', 'rev-list', '--reverse', DXIL_FSR_MERGE + '^1..' + DXIL_FSR_MERGE + '^2'], cwd=dxil,
capture_output=True, text=True, check=True).stdout.split()
ancestor = subprocess.run(['git', 'merge-base', '--is-ancestor', DXIL_FSR_MERGE + '^2', 'HEAD'], cwd=dxil).returncode == 0
if not ancestor:
git(['cherry-pick', '-x'] + listing, dxil, log, identity=True)
# libc++ include fixes (GCC pulled these in transitively; libc++ does not)
for patch, where in (('0001-dxil-spirv-include-exception-for-libcxx.patch', dxil),
('0002-dxbc-spirv-include-new-for-libcxx.patch', dxil / 'subprojects/dxbc-spirv')):
full = repo / 'patches/vkd3d-proton-windows' / patch
check = subprocess.run(['git', 'apply', '--check', '--reverse', str(full)], cwd=where, capture_output=True)
if check.returncode: # not applied yet
git(['apply', full], where, log)
escaped = lambda path: str(path).replace('\\', '/')
cross = work / 'cross.txt'
cross.write_text('\n'.join([
'[binaries]',
f"c = '{escaped(mingw / 'x86_64-w64-mingw32-clang.exe')}'",
f"cpp = '{escaped(mingw / 'x86_64-w64-mingw32-clang++.exe')}'",
f"ar = '{escaped(mingw / 'x86_64-w64-mingw32-ar.exe')}'",
f"strip = '{escaped(mingw / 'x86_64-w64-mingw32-strip.exe')}'",
f"windres = '{escaped(mingw / 'x86_64-w64-mingw32-windres.exe')}'",
f"widl-mingw-tools-fallback = '{escaped(mingw / 'x86_64-w64-mingw32-widl.exe')}'",
'', '[properties]', 'needs_exe_wrapper = true', '',
'[host_machine]', "system = 'windows'", "cpu_family = 'x86_64'", "cpu = 'x86_64'", "endian = 'little'", '']))
env = dict(os.environ)
env['PATH'] = os.pathsep.join([str(mingw), str(glslang.parent), str(ninja.parent), env['PATH']])
env['PYTHONPATH'] = str(meson_path)
build = work / 'build'
mesonmain = [sys.executable, '-m', 'mesonbuild.mesonmain']
run(mesonmain + ['setup', '--cross-file', cross, '--buildtype', 'release', '-Denable_tests=false',
'-Denable_extended_emulation=true', build, source], cwd=source, env=env, log=log)
run(mesonmain + ['compile', '-C', build], cwd=source, env=env, log=log)
fsr = args.output / 'fsr'
fsr.mkdir()
core = build / 'libs/d3d12core/d3d12core.dll'
run([mingw / 'x86_64-w64-mingw32-strip.exe', '-o', fsr / 'd4r_vkd3d_core.dll', core], log=log)
run([mingw / 'x86_64-w64-mingw32-clang.exe', '-shared', '-O2', '-Wall', '-o', fsr / 'amdxc64.dll',
repo / 'tools/windows/amdxc64_shim/amdxc64_shim.c', '-Wl,--kill-at'], log=log)
licenses = args.output / 'licenses'
licenses.mkdir()
for src, name in ((source / 'LICENSE', 'vkd3d-proton-LGPL-2.1.txt'), (source / 'COPYING', 'vkd3d-proton-COPYING.txt'),
(dxil / 'LICENSE.MIT', 'dxil-spirv-MIT.txt'), (dxil / 'subprojects/dxbc-spirv/LICENSE', 'dxbc-spirv-LICENSE.txt')):
shutil.copy2(src, licenses / name)
heads = {name: subprocess.run(['git', 'rev-parse', 'HEAD'], cwd=path, capture_output=True, text=True, check=True).stdout.strip()
for name, path in (('vkd3d-proton', source), ('dxil-spirv', dxil), ('dxbc-spirv', dxil / 'subprojects/dxbc-spirv'))}
patches = {p.name: sha(p) for p in sorted((repo / 'patches/vkd3d-proton-windows').glob('*.patch'))}
(args.output / 'SOURCE.txt').write_text('\n'.join([
'fsr/d4r_vkd3d_core.dll is vkd3d-proton (LGPL-2.1) built for Windows by tools/windows/build_fsr_payload.py.',
'Corresponding source: ' + VKD3D_URL + ' at ' + VKD3D_COMMIT + ',',
'dxil-spirv with upstream PR 307 (merge ' + DXIL_FSR_MERGE + ') cherry-picked, and the patches in',
'patches/vkd3d-proton-windows/ of the d4r repository (libc++ include fixes). Build: meson cross build with',
'llvm-mingw clang, -Denable_extended_emulation=true, buildtype release; the DLL is stripped and renamed.',
'Built HEADs: ' + json.dumps(heads), 'Patches: ' + json.dumps(patches),
'fsr/amdxc64.dll is d4r original code (tools/windows/amdxc64_shim). It is not AMD software.', '']))
files = {str(p.relative_to(args.output)).replace('\\', '/'): sha(p) for p in sorted(args.output.rglob('*'))
if p.is_file() and '_work' not in p.relative_to(args.output).parts}
(args.output / 'fsr-payload.json').write_text(json.dumps(dict(
schema=1, kind='d4r-fsr-payload', amdDenoiserBundled=False, vkd3dProtonCommit=VKD3D_COMMIT, dxilSpirvFsrMerge=DXIL_FSR_MERGE,
builtHeads=heads, patches=patches, files=files), indent=2) + '\n')
print(json.dumps(files, indent=2))
print('FSR_PAYLOAD_BUILT ' + str(args.output))
if __name__ == '__main__':
main()
+91
View File
@@ -0,0 +1,91 @@
"""Compile a private native K LDR model from verified Windows synthetic captures.
Requires all 11 extracted layers and 16 captured reconstruction tables. Never
substitutes synthetic tables, installs into a game, or creates a release archive.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import subprocess
import sys
def sha(path):
with path.open('rb') as stream:
return hashlib.file_digest(stream, 'sha256').hexdigest()
def main():
repo = Path(__file__).resolve().parents[2]
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--capture', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--output', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--glslang', type=lambda p: Path(p).resolve(), default=repo / '.tools/glslang-16.6.0/bin/glslangValidator.exe')
parser.add_argument('--reuse', action='store_true')
args = parser.parse_args()
if not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('private model products must stay under ignored build/')
validation = json.loads((args.capture / 'validation.json').read_text())
if (validation.get('passed') is not True or validation.get('reconstructionLutCaptured') is not True
or validation.get('reconstructionTables') != 16 or validation.get('extractedLayers') != 11
or validation.get('numerics') != 'explicit-f16-gains'):
parser.error('requires verified complete 11-layer/16-table capture')
if set(validation['products']) != {'k.bin', 'k.json', 'lut.bin'}:
raise RuntimeError('Unexpected/missing capture products')
weights = args.capture / 'weights'
for name, expected in validation['products'].items():
if sha(weights / name) != expected:
raise RuntimeError('Capture product changed: ' + name)
if (weights / 'lut.bin').stat().st_size != 16 * 32768:
raise RuntimeError('Incomplete reconstruction tables')
if sha(args.glslang) != '9643709c02c83e26773f96b6a87f197ab2e84f78be0879ad8fb3619418b5d9ad':
raise RuntimeError('Requires pinned glslang 16.6.0')
inputs = ['engine/compile_k.py', 'engine/test_k.py', 'engine/layer.comp', 'engine/input_k.comp',
'engine/output_k.comp', 'engine/exposure_k.comp', 'engine/normalized_store.glsl']
identity = dict(schema=1, preset='K', variant='ldr', numerics='explicit-f16-gains',
captureProducts=validation['products'], glslang=sha(args.glslang),
python=sha(Path(sys.executable)), sources={name:sha(repo / name) for name in inputs},
builder=sha(Path(__file__)))
stamp = args.output / 'windows-k-model-cache.json'
def products():
return {p.name:sha(p) for p in sorted(args.output.iterdir()) if p.is_file() and p.name != stamp.name}
if args.output.exists():
if not args.reuse:
parser.error('use a fresh output directory or --reuse')
cached = json.loads(stamp.read_text())
if cached['identity'] != identity or cached['products'] != products():
raise RuntimeError('K model cache changed; preserve it and use a fresh output directory')
print('K_MODEL_CACHE_HIT')
return
env = {key:value for key,value in os.environ.items() if not key.startswith(('D4R_', 'K_DEFS_'))}
env['PYTHONPATH'] = os.pathsep.join(str(repo / name) for name in ('.tools/python/vendor', 'engine', 'kernels/tools'))
env['PATH'] = str(args.glslang.parent) + os.pathsep + env['PATH']
args.output.mkdir(parents=True)
command = [sys.executable, str(repo / 'engine/compile_k.py'), str(weights / 'k'),
str(weights / 'lut.bin'), str(args.output), '--ldr']
with (args.output / 'compile.log').open('wb') as log:
process = subprocess.Popen(command, env=env, stdout=log, stderr=subprocess.STDOUT)
try:
code = process.wait(timeout=600)
except subprocess.TimeoutExpired:
subprocess.run(['taskkill', '/PID', str(process.pid), '/T', '/F'], check=False, timeout=30,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
process.wait(timeout=30)
raise RuntimeError('K compilation timed out; process tree terminated')
if code:
raise RuntimeError('K compilation failed; inspect compile.log')
manifest = json.loads((args.output / 'model.json').read_text())
if manifest.get('numerics') != 'explicit-f16-gains' or manifest.get('variant') != 'ldr' or len(manifest['layers']) != 11:
raise RuntimeError('Incorrect compiled K model contract')
for name, expected in manifest['sha256'].items():
if Path(name).name != name or sha(args.output / name) != expected:
raise RuntimeError('Compiled model product failed verification')
stamp.write_text(json.dumps(dict(identity=identity, products=products(), privateLocalModel=True,
gpuValidated=False, gameValidated=False, published=False), indent=2) + '\n')
print('K_MODEL_BUILT private=1 gpu_validated=0 products=' + str(len(products())))
if __name__ == '__main__':
main()
+109
View File
@@ -0,0 +1,109 @@
"""Capture and extract private K weights through the existing synthetic Windows probe.
Uses the audited ZLUDA layer-capture hook, not a game or DLL patch. Generated
weights/captures are private local assets and must never enter release archives.
This does not yet capture the reconstruction LUT or build a full K model.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import subprocess
import sys
def sha(path):
with path.open('rb') as stream:
return hashlib.file_digest(stream, 'sha256').hexdigest()
def main():
repo = Path(__file__).resolve().parents[2]
parser = argparse.ArgumentParser(description=__doc__)
for name in ('probe', 'shim', 'runtime', 'core', 'dlss', 'native', 'output'):
parser.add_argument('--' + name, required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--output-resolution', default='512x288', help='NGX output WxH (default 512x288)')
parser.add_argument('--input-resolution', default='256x144', help='NGX render WxH (default 256x144)')
parser.add_argument('--include-lut', action='store_true', help='requires audited 328-byte output capture support in isolated runtime')
args = parser.parse_args()
if args.output.exists() or not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('use a fresh directory under ignored build/')
if sha(args.dlss) != '3975567b8943c53acce397f2b72380092f84f162d00b0d2c7d08a1025c563983':
parser.error('requires pinned SR 310.9.1')
if sha(args.core) != '66767018c36b3bab46398dade3adf173daa3730fda75965689ea848c9bc4e79b':
parser.error('requires pinned NGX core 617.14')
args.output.mkdir(parents=True)
capture = args.output / 'capture'
env = {key: value for key, value in os.environ.items()
if not key.startswith('D4R_') and key not in ('ZLUDA_LOG_DIR', 'ZLUDA_CACHE_DIR', 'ZLUDA_CUDA_LIB')}
runtime = args.runtime
nvapi = runtime / 'nvapi/gfx1101/nvapi64.dll'
if not nvapi.is_file():
nvapi = runtime / 'nvapi/nvapi64.dll'
env.update(HIP_PATH=str(runtime / 'hip'), ZLUDA_CUDA_LIB=str(runtime / 'zluda/nvcuda.dll'),
D4R_NVAPI_BACKEND=str(runtime / 'nvapi/backend/nvapi64.dll'),
D4R_FORMAT_MODULE=str(runtime / 'pixel_convert_gfx1101.hsaco'),
D4R_ZLUDA_NATIVE_DIR=str(args.native), D4R_ZLUDA_WMMA='1',
D4R_ZLUDA_WMMA_FP8='1', D4R_ZLUDA_WMMA_F16_REFERENCE='1', D4R_QUIET_API='1',
D4R_DIAG_DIR=str(args.output), D4R_NGX_DATA_DIR=str(args.output / 'ngx-data'),
D4R_CUDA_REPLAY_DUMP_DIR=str(capture), D4R_CUDA_REPLAY_DUMP_FILTER='dltss_pwin_',
D4R_CUDA_REPLAY_DUMP_LIMIT='1')
if args.include_lut:
env['D4R_CUDA_REPLAY_DUMP_FILTER'] = ''
env['PATH'] = str(runtime / 'hip/bin') + os.pathsep + str(runtime / 'zluda') + os.pathsep + env['PATH']
command = [str(args.probe), '--module', str(args.shim), '--hip-root', str(runtime / 'hip'),
'--cuda-dll', str(runtime / 'zluda/nvcuda.dll'), '--ngx-core', str(args.core),
'--dlss-dll', str(args.dlss), '--nvapi-dll', str(nvapi), '--ngx-abi', 'project',
'--interop-mode', 'command-list', '--preset', '11', '--ngx-output-resolution', args.output_resolution,
'--ngx-input-resolution', args.input_resolution, '--iterations', '1']
report = dict(passed=False, privateLocalWeights=True, gameAccessed=False, reconstructionLutCaptured=False,
artifacts={str(p):sha(p) for p in (args.probe, args.shim, args.core, args.dlss, runtime / 'zluda/nvcuda.dll')})
try:
with (args.output / 'probe.stdout.log').open('wb') as out, (args.output / 'probe.stderr.log').open('wb') as err:
result = subprocess.run(command, env=env, stdout=out, stderr=err, timeout=180)
if result.returncode or 'PASS D3D12_DLSS' not in (args.output / 'probe.stdout.log').read_text(errors='replace'):
raise RuntimeError('Synthetic capture probe failed; inspect logs')
expected = {'enc0', 'enc1', 'enc2', 'enc3', 'enc4', 'dec5', 'dec4', 'dec3', 'dec2', 'dec1', 'dec0'}
captures = list(capture.glob('replay-*-dltss_pwin_*_layer'))
found = {p.name.split('-dltss_pwin_')[1].removesuffix('_layer') for p in captures}
if found != expected or len(captures) != 11:
raise RuntimeError('Missing/duplicate audited K layer captures')
weights = args.output / 'weights'
weights.mkdir()
if args.include_lut:
outputs = list(capture.glob('replay-*-hiluma_engine_output_depthreg_mvhi_ldr_max_v2_rel'))
if len(outputs) != 1 or (outputs[0] / 'args.bin').stat().st_size != 328:
raise RuntimeError('Missing or invalid audited K output capture')
fields = [line.split() for line in (outputs[0] / 'manifest.txt').read_text().splitlines()]
pointers = [row for row in fields if row[0] == 'pointer' and row[1] == '280']
if len(pointers) != 1:
raise RuntimeError('Missing/duplicate reconstruction LUT pointer')
_, _, identifier, offset = pointers[0]
lut = (outputs[0] / ('alloc-' + identifier + '.bin')).read_bytes()
if len(lut) != 16 * 32768 or int(offset) % 32768 or not 0 <= int(offset) < len(lut) or not any(lut):
raise RuntimeError('Unexpected reconstruction-table allocation; refusing guessed extraction')
(weights / 'lut.bin').write_bytes(lut)
report.update(reconstructionLutCaptured=True, reconstructionTables=16, capturedLutIndex=int(offset) // 32768)
extraction_env = env.copy()
extraction_env['PYTHONPATH'] = os.pathsep.join(str(repo / path) for path in ('.tools/python/vendor', 'engine', 'kernels/tools'))
with (args.output / 'extract.log').open('wb') as out:
subprocess.run([sys.executable, str(repo / 'engine/extract_k.py'), str(capture), str(weights / 'k'),
'--unfold-gains'], env=extraction_env, stdout=out, stderr=subprocess.STDOUT, check=True, timeout=120)
index = json.loads((weights / 'k.json').read_text())
if set(index) != expected or any(layer['fold_gains'] for layer in index.values()):
raise RuntimeError('Incomplete/incorrect K weight extraction')
report.update(passed=True, layerCaptures=11, extractedLayers=11, numerics='explicit-f16-gains',
products={p.name:sha(p) for p in weights.iterdir() if p.is_file()},
weightBytes=(weights / 'k.bin').stat().st_size,
standaloneModelComplete=False, qualityReference=False, published=False)
print(json.dumps(report, indent=2))
except Exception as error:
report['error'] = str(error)
raise
finally:
(args.output / 'validation.json').write_text(json.dumps(report, indent=2) + '\n')
if __name__ == '__main__':
main()
+75
View File
@@ -0,0 +1,75 @@
"""Compare rr_ngx_probe outputs of the synthetic noisy scene against its noise-free answer, frame by frame.
The fixture's frames.txt first line is `size RW RH OW OH`; dynamic-extents.txt (when present) gives each frame's render
size. Per frame the reference is clean-<frame>.bin (clean.bin for fixtures from the first generator version) at output
size. When a frame's render size differs from the output size the baselines are the noisy input nearest-resampled to
output size and, if clean-render-<frame>.bin exists, the noise-free render-size image resampled the same way (the best a
render-resolution result could be).
"""
import argparse
import json
import sys
from pathlib import Path
repo = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(repo / '.tools/python/vendor'))
import numpy as np
def resampler(render_shape, shape):
if render_shape == shape:
return lambda a: a
ys = np.minimum((np.arange(shape[0]) * render_shape[0] / shape[0]).astype(int), render_shape[0] - 1)
xs = np.minimum((np.arange(shape[1]) * render_shape[1] / shape[1]).astype(int), render_shape[1] - 1)
return lambda a: a[ys][:, xs]
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--fixture', required=True, type=Path)
parser.add_argument('--output', required=True, type=Path)
parser.add_argument('--report', required=True, type=Path)
parser.add_argument('--ratio', type=float, default=0.5, help='tail RMSE must be below ratio * the input baseline')
parser.add_argument('--output-float32', action='store_true')
parser.add_argument('--switch-output', action='store_true')
args = parser.parse_args()
sizes = list(map(int, (args.fixture / 'frames.txt').read_text().split('\n')[0].split()[1:5]))
shape = (sizes[3], sizes[2], 4) # output size
n = len(list(args.fixture.glob('*-color.bin')))
extents_file = args.fixture / 'dynamic-extents.txt'
extents = [tuple(map(int, line.split())) for line in extents_file.read_text().split('\n') if line.strip()] if extents_file.exists() else None
sky = np.load(args.fixture / 'sky.npy')
body = ~sky
rows = []
for i in range(n):
fw, fh = extents[i] if extents else (sizes[0], sizes[1])
render_shape = (fh, fw, 4)
resample = resampler(render_shape, shape)
reference = args.fixture / f'clean-{i}.bin'
clean = np.fromfile(reference if reference.exists() else args.fixture / 'clean.bin', '<f2').astype('f8').reshape(shape)[..., :3]
float_output = args.output_float32 and (not args.switch_output or i % 2 == 1)
out = np.fromfile(args.output / f'{i}-out.bin', '<f4' if float_output else '<f2').astype('f8').reshape(shape)[..., :3]
noisy = resample(np.fromfile(args.fixture / f'{i}-color.bin', '<f2').astype('f8').reshape(render_shape)[..., :3])
rmse = lambda a: float(np.sqrt(np.mean((a[body] - clean[body]) ** 2)))
row = dict(frame=i, renderSize=[fw, fh], finite=bool(np.isfinite(out).all()), outputRmse=rmse(out), inputRmse=rmse(noisy),
skyMaxAbs=float(np.abs(out[sky] - clean[sky]).max()) if sky.any() else 0.0,
differsFromInput=bool(not np.array_equal(out, noisy)))
ideal = args.fixture / f'clean-render-{i}.bin'
if ideal.exists():
row['idealNearestRmse'] = rmse(resample(np.fromfile(ideal, '<f2').astype('f8').reshape(render_shape)[..., :3]))
rows.append(row)
tail = rows[-8:]
summary = dict(frames=n, creationRenderSize=sizes[:2], outputSize=sizes[2:], dynamicExtents=bool(extents),
allFinite=all(r['finite'] for r in rows), allDifferFromInput=all(r['differsFromInput'] for r in rows),
tailOutputRmse=float(np.mean([r['outputRmse'] for r in tail])), tailInputRmse=float(np.mean([r['inputRmse'] for r in tail])),
tailSkyMaxAbs=max(r['skyMaxAbs'] for r in tail), qualityReference=False, gameAccessed=False, synthetic=True)
if all('idealNearestRmse' in r for r in tail):
summary['idealNearestRmse'] = float(np.mean([r['idealNearestRmse'] for r in tail]))
summary['denoised'] = summary['tailOutputRmse'] < args.ratio * summary['tailInputRmse']
args.report.write_text(json.dumps(dict(summary=summary, frames=rows), indent=2) + '\n')
print(json.dumps(summary, indent=2))
sys.exit(0 if summary['allFinite'] and summary['allDifferFromInput'] and summary['denoised'] else 1)
if __name__ == '__main__':
main()
+51
View File
@@ -0,0 +1,51 @@
"""Inspect preserved synthetic K half-output differences without running a GPU."""
import argparse
import json
from pathlib import Path
import sys
def main():
repo = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(repo / '.tools/python/vendor'))
import numpy as np
parser = argparse.ArgumentParser(description=__doc__)
for name in ('reference', 'candidate', 'output'):
parser.add_argument('--' + name, required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--width', required=True, type=int)
args = parser.parse_args()
if args.output.exists() or not args.output.is_relative_to((repo / 'build').resolve()) or args.width <= 0:
parser.error('use a fresh JSON output under build/ and a positive width')
names = sorted(p.name for p in args.reference.glob('output-*.rgba16f'))
if not names or names != sorted(p.name for p in args.candidate.glob('output-*.rgba16f')):
raise RuntimeError('Output sets differ or are empty')
rows = []
for name in names:
a = np.fromfile(args.reference / name, '<f2')
b = np.fromfile(args.candidate / name, '<f2')
if a.size != b.size or a.size % (args.width * 4):
raise RuntimeError('Invalid output dimensions')
indices = np.flatnonzero(a.view('<u2') != b.view('<u2'))
pixels = indices // 4
finite = np.isfinite(a) & np.isfinite(b)
max_difference = float(np.max(np.abs(a[finite].astype('f4') - b[finite].astype('f4')))) if finite.any() else None
rows.append(dict(frame=name, changedComponents=int(indices.size),
channels=[int(np.count_nonzero(indices % 4 == channel)) for channel in range(4)],
maxAbsoluteDifference=max_difference,
nonfiniteReference=int(np.count_nonzero(~np.isfinite(a))),
nonfiniteCandidate=int(np.count_nonzero(~np.isfinite(b))),
finite=bool(np.isfinite(a).all() and np.isfinite(b).all()),
boundsXY=[int((pixels % args.width).min()), int((pixels // args.width).min()),
int((pixels % args.width).max()), int((pixels // args.width).max())] if indices.size else None))
report = dict(exact=all(row['changedComponents'] == 0 for row in rows), rows=rows,
changedFrames=sum(row['changedComponents'] > 0 for row in rows),
changedComponents=sum(row['changedComponents'] for row in rows),
maximumAbsoluteDifference=max((row['maxAbsoluteDifference'] for row in rows if row['maxAbsoluteDifference'] is not None), default=None),
gameAccessed=False)
with args.output.open('x') as stream:
json.dump(report, stream, indent=2, allow_nan=False)
print(json.dumps({key: value for key, value in report.items() if key != 'rows'}, indent=2))
if __name__ == '__main__':
main()
+21 -2
View File
@@ -13,6 +13,15 @@ int main(int argc, char** argv) {
start();
try {
Args args(argc, argv);
unsigned subrectWidth = 0, subrectHeight = 0;
if (const char* subrect = std::getenv("D4R_PROBE_RENDER_SUBRECT")) {
char extra = 0;
if (std::sscanf(subrect, "%ux%u%c", &subrectWidth, &subrectHeight, &extra) != 2 ||
subrectWidth < 64 || subrectHeight < 64 ||
subrectWidth > (args.input_width ? args.input_width : args.output_width / 2) ||
subrectHeight > (args.input_height ? args.input_height : args.output_height / 2))
throw std::runtime_error("D4R_PROBE_RENDER_SUBRECT must be WIDTHxHEIGHT within 64x64..creation size");
}
if (args.cuda_dll.empty() || (args.interop_mode != "images" && (args.module.empty() || args.ngx_core.empty() || args.dlss_dll.empty())))
throw std::runtime_error("--module shim DLL, --cuda-dll, --ngx-core and --dlss-dll required");
const char* diagDir = std::getenv("D4R_DIAG_DIR");
@@ -243,7 +252,8 @@ int main(int argc, char** argv) {
for (const auto& pair : {std::make_pair("Width", width), std::make_pair("Height", height),
std::make_pair("OutWidth", outWidth), std::make_pair("OutHeight", outHeight),
std::make_pair("CreationNodeMask", 1u), std::make_pair("VisibilityNodeMask", 1u)}) d4r_ngx_set_uint(parameters, pair.first, pair.second);
d4r_ngx_set_int(parameters, "PerfQualityValue", 2); d4r_ngx_set_int(parameters, "DLSS.Feature.Create.Flags", args.ngx_create_flags);
d4r_ngx_set_int(parameters, "PerfQualityValue", int(args.ngx_quality)); d4r_ngx_set_int(parameters, "DLSS.Feature.Create.Flags", args.ngx_create_flags);
std::printf("D3D12_NGX_QUALITY value=%u\n", args.ngx_quality);
std::printf("D3D12_NGX_CREATE_FLAGS value=%u\n", args.ngx_create_flags);
for (const char* name : {"DLSS.Hint.Render.Preset.DLAA", "DLSS.Hint.Render.Preset.Quality", "DLSS.Hint.Render.Preset.Balanced",
"DLSS.Hint.Render.Preset.Performance", "DLSS.Hint.Render.Preset.UltraPerformance", "DLSS.Hint.Render.Preset.UltraQuality"}) d4r_ngx_set_uint(parameters, name, args.preset);
@@ -342,6 +352,7 @@ int main(int argc, char** argv) {
std::printf("D3D12_OUTPUT preset=%u frame=%u finite=1 mean=%.9g variance=%.9g frame_age=0\n", args.preset, frame, sum/count, variance);
if (variance < 1e-6) throw std::runtime_error("D3D12 output lost the synthetic pattern");
};
std::printf("D3D12_RESET every_frame=%u\n", unsigned(args.reset_each_frame));
for (unsigned frame = 0; frame < args.iterations; ++frame) {
if (burst) {
dx(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(allocator.ReleaseAndGetAddressOf())), "Burst allocator");
@@ -349,7 +360,15 @@ int main(int argc, char** argv) {
dx(list->Close(), "Burst initial Close");
readback = make_buffer(device.Get(), textures[3].bytes, D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
}
d4r_ngx_set_int(parameters, "Reset", frame == 0 ? 1 : 0);
d4r_ngx_set_int(parameters, "Reset", args.reset_each_frame || frame == 0 ? 1 : 0);
// Diagnostic: D4R_PROBE_RENDER_SUBRECT=WxH evaluates with that render rectangle on frames >= 1 (the
// feature keeps its creation size), to exercise dynamic-resolution Super Sampling.
const unsigned renderWidth = subrectWidth && frame >= 1 ? subrectWidth : width;
const unsigned renderHeight = subrectHeight && frame >= 1 ? subrectHeight : height;
d4r_ngx_set_uint(parameters, "DLSS.Render.Subrect.Dimensions.Width", renderWidth);
d4r_ngx_set_uint(parameters, "DLSS.Render.Subrect.Dimensions.Height", renderHeight);
std::printf("D3D12_RENDER_SUBRECT frame=%u size=%ux%u reset=%u retained_resources=1\n",
frame, renderWidth, renderHeight, unsigned(args.reset_each_frame || frame == 0));
if (commandBackend) {
dx(allocator->Reset(), "Reset(game-style allocator)"); dx(list->Reset(allocator.Get(), nullptr), "Reset(game-style list)");
if (earlySignature) {
+125 -9
View File
@@ -5,6 +5,9 @@
#if defined(D4R_NATIVE_RR_BACKEND)
#include "rr_native_backend.h"
#endif
#if defined(D4R_FSR_RR_BACKEND)
#include "fsr_rr_backend.h"
#endif
#include <dxgi.h>
#include <unordered_map>
#include <functional>
@@ -29,7 +32,7 @@ void initialize(ID3D12Device* device, unsigned long long app, const wchar_t* dat
auto candidate = std::make_shared<d4r::win::RuntimeInitialization>(device, app, data, sdk, common, project);
bool native = false;
#if defined(D4R_NATIVE_RR_BACKEND)
const char* backend = std::getenv("D4R_RR_BACKEND"); native = backend && std::string(backend) == "native";
const char* backend = std::getenv("D4R_RR_BACKEND"); native = backend && (std::string(backend) == "native" || std::string(backend) == "fsr");
#endif
// Default SR startup remains eager. Native RR owns its Vulkan execution
// independently; SR initializes only when its capabilities/feature are used.
@@ -40,20 +43,133 @@ struct Handle { unsigned Id; };
unsigned nextId = 1;
std::unordered_map<Handle*, std::shared_ptr<d4r::win::Feature>> features;
#if defined(D4R_NATIVE_RR_BACKEND)
bool rr_is_fsr() { const char* backend = std::getenv("D4R_RR_BACKEND"); return backend && std::string(backend) == "fsr"; }
// One Ray Reconstruction feature served by the configured backend: native DLSS RR (D/E models) or FSR RR.
struct RrFeature {
d4r::win::rr::Creation creation;
d4r::win::rr::Backend backend;
std::unique_ptr<d4r::win::rr::Backend> native;
#if defined(D4R_FSR_RR_BACKEND)
std::unique_ptr<d4r::win::rr::FsrBackend> fsr;
// The Super Sampling stage after FSR's denoiser: an internal feature 1 at the game's render and output sizes,
// configured like any SR feature ([DLSS] Model applies). Absent only when SR cannot start and output == render.
std::shared_ptr<d4r::win::Feature> sr;
void* srParams = nullptr;
~RrFeature() { if (srParams) d4r_ngx_parameters_destroy(srParams); }
void start_super_sampling() {
try {
ensure_sr_runtime();
void* creationParams = d4r_ngx_parameters_create();
struct Guard { void* p; ~Guard() { d4r_ngx_parameters_destroy(p); } } guard{creationParams};
d4r_ngx_set_uint(creationParams, "Width", creation.width); d4r_ngx_set_uint(creationParams, "Height", creation.height);
d4r_ngx_set_uint(creationParams, "OutWidth", creation.outWidth); d4r_ngx_set_uint(creationParams, "OutHeight", creation.outHeight);
d4r_ngx_set_int(creationParams, "PerfQualityValue", creation.quality);
d4r_ngx_set_int(creationParams, "DLSS.Feature.Create.Flags", int(creation.flags));
d4r_ngx_set_int(creationParams, "DLSS.Enable.Output.Subrects", int(creation.outputSubrects));
d4r::win::player::apply_preset(creationParams);
sr = std::make_shared<d4r::win::Feature>(runtime, creationParams);
srParams = d4r_ngx_parameters_create();
} catch (const std::exception& error) {
std::fprintf(stderr, "D4R_FSR_RR_SR_UNAVAILABLE %s\n", error.what());
sr.reset();
if (creation.outWidth != creation.width || creation.outHeight != creation.height)
throw std::runtime_error(std::string("FSR RR needs the Super Sampling stage to upscale, which is unavailable: ") + error.what());
return; // output == render: the denoised colour goes straight to the output
}
fsr->set_super_sampling([this](ID3D12CommandQueue* queue, ID3D12Resource* denoised,
const d4r::win::rr::Snapshot& frame, const std::array<d4r::win::ResourceAccess, d4r::win::rr::Snapshot::Count>& access) {
using d4r::win::rr::Snapshot;
void* sp = srParams;
d4r_ngx_set_d3d12_resource(sp, "Color", denoised);
d4r_ngx_set_d3d12_resource(sp, "Depth", frame.resources[Snapshot::Depth].Get());
d4r_ngx_set_d3d12_resource(sp, "MotionVectors", frame.resources[Snapshot::Motion].Get());
d4r_ngx_set_d3d12_resource(sp, "Output", frame.resources[Snapshot::Output].Get());
d4r_ngx_set_d3d12_resource(sp, "ExposureTexture", frame.resources[Snapshot::Exposure].Get());
const auto& settings = frame.settings;
d4r_ngx_set_float(sp, "Jitter.Offset.X", settings.jitter[0]); d4r_ngx_set_float(sp, "Jitter.Offset.Y", settings.jitter[1]);
d4r_ngx_set_float(sp, "MV.Scale.X", settings.motionScale[0]); d4r_ngx_set_float(sp, "MV.Scale.Y", settings.motionScale[1]);
d4r_ngx_set_float(sp, "Sharpness", 0.0f);
d4r_ngx_set_float(sp, "DLSS.Pre.Exposure", settings.preExposure); d4r_ngx_set_float(sp, "DLSS.Exposure.Scale", frame.exposureScale);
d4r_ngx_set_float(sp, "FrameTimeDeltaInMsec", settings.frameTimeMs);
d4r_ngx_set_int(sp, "Reset", settings.reset ? 1 : 0);
d4r_ngx_set_uint(sp, "DLSS.Render.Subrect.Dimensions.Width", frame.renderWidth);
d4r_ngx_set_uint(sp, "DLSS.Render.Subrect.Dimensions.Height", frame.renderHeight);
d4r_ngx_set_uint(sp, "DLSS.Input.Color.Subrect.Base.X", 0); d4r_ngx_set_uint(sp, "DLSS.Input.Color.Subrect.Base.Y", 0);
d4r_ngx_set_uint(sp, "DLSS.Input.Depth.Subrect.Base.X", frame.origins[Snapshot::Depth][0]);
d4r_ngx_set_uint(sp, "DLSS.Input.Depth.Subrect.Base.Y", frame.origins[Snapshot::Depth][1]);
d4r_ngx_set_uint(sp, "DLSS.Input.MV.Subrect.Base.X", frame.origins[Snapshot::Motion][0]);
d4r_ngx_set_uint(sp, "DLSS.Input.MV.Subrect.Base.Y", frame.origins[Snapshot::Motion][1]);
d4r_ngx_set_uint(sp, "DLSS.Output.Subrect.Base.X", frame.origins[Snapshot::Output][0]);
d4r_ngx_set_uint(sp, "DLSS.Output.Subrect.Base.Y", frame.origins[Snapshot::Output][1]);
// resource states, as the shim's own feature-1 evaluation passes them
struct Plane { const char* state; d4r::win::ResourceAccess access; };
d4r::win::ResourceAccess denoisedAccess(D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
const Plane planes[] = {{"D4R.Color.State", denoisedAccess}, {"D4R.Depth.State", access[Snapshot::Depth]},
{"D4R.Motion.State", access[Snapshot::Motion]}, {"D4R.Output.State", access[Snapshot::Output]},
{"D4R.Exposure.State", access[Snapshot::Exposure]}};
for (const auto& plane : planes) {
const std::string prefix(plane.state);
d4r_ngx_set_uint(sp, plane.state, unsigned(plane.access.legacy));
d4r_ngx_set_uint(sp, (prefix + ".Enhanced").c_str(), plane.access.enhanced);
d4r_ngx_set_uint(sp, (prefix + ".Layout").c_str(), unsigned(plane.access.layout));
d4r_ngx_set_uint(sp, (prefix + ".Access").c_str(), unsigned(plane.access.access));
}
sr->evaluate_boundary(queue, sp);
}, sr->render_limits());
}
#endif
RrFeature(ID3D12Device* device, void* parameters, const std::string& directory)
: creation(parameters), backend(device, creation, directory) {}
: creation(parameters) {
#if defined(D4R_FSR_RR_BACKEND)
if (rr_is_fsr()) {
fsr = std::make_unique<d4r::win::rr::FsrBackend>(device, creation, d4r::win::rr::FsrConfig::from_environment());
start_super_sampling();
return;
}
#endif
native = std::make_unique<d4r::win::rr::Backend>(device, creation, directory);
}
void validate_frame(const d4r::win::rr::Snapshot& frame) {
#if defined(D4R_FSR_RR_BACKEND)
if (fsr) { fsr->validate_frame(frame); return; }
#endif
native->validate_frame(frame);
}
void evaluate(ID3D12CommandQueue* queue, const d4r::win::rr::Snapshot& frame,
const std::array<d4r::win::ResourceAccess, d4r::win::rr::Snapshot::Count>& access) {
#if defined(D4R_FSR_RR_BACKEND)
if (fsr) { fsr->evaluate(queue, frame, access); return; }
#endif
native->evaluate(queue, frame, access);
}
void drain() {
#if defined(D4R_FSR_RR_BACKEND)
if (fsr) { fsr->drain(); if (sr) sr->drain(); return; }
#endif
native->drain();
}
};
std::unordered_map<Handle*, std::shared_ptr<RrFeature>> rrFeatures;
std::string rr_directory() {
const char* backend = std::getenv("D4R_RR_BACKEND");
if (backend && std::string(backend) == "fsr") return {}; // FSR RR has no DLSS model directory
if (!backend || std::string(backend) != "native")
throw std::runtime_error("Native RR requires D4R_RR_BACKEND=native");
throw std::runtime_error("Native RR requires D4R_RR_BACKEND=native (or fsr)");
return d4r::win::env_path("D4R_RR_MODEL_DIR");
}
// FSR readiness is a cheap file check: building the backend loads vkd3d-proton and compiles AMD's shaders.
bool fsr_ready() {
#if defined(D4R_FSR_RR_BACKEND)
const auto config = d4r::win::rr::FsrConfig::from_environment();
auto present = [](const std::wstring& path) { return !path.empty() && GetFileAttributesW(path.c_str()) != INVALID_FILE_ATTRIBUTES; };
const bool ready = present(config.vkd3dCore) && present(config.denoiserDll) && present(config.amdxcShim);
if (!ready) std::fprintf(stderr, "D4R_RR_UNAVAILABLE FSR RR needs D4R_FSR_VKD3D_CORE, D4R_FSR_RR_DLL and D4R_FSR_AMDXC_SHIM to exist\n");
return ready;
#else
return false;
#endif
}
bool rr_ready(ID3D12Device* device) {
if (rr_is_fsr()) return fsr_ready();
try {
const auto directory = rr_directory();
void* p = d4r_ngx_parameters_create();
@@ -270,7 +386,7 @@ API unsigned d4r_D3D12_EvaluateAtBoundary(ID3D12CommandQueue* queue, Handle* han
auto it = rrFeatures.find(handle); if (it != rrFeatures.end()) rr = it->second; }
if (rr) return call([&] {
RrSnapshot snapshot(rr->creation, p, nullptr); snapshot.resolve();
rr->backend.evaluate(queue, snapshot.frame, snapshot.access); return 1u;
rr->evaluate(queue, snapshot.frame, snapshot.access); return 1u;
});
#endif
std::shared_ptr<d4r::win::Feature> feature;
@@ -288,9 +404,9 @@ API unsigned NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCommandList* list, co
auto snapshot = std::make_shared<RrSnapshot>(rr->creation, p, list);
// Reject unsupported contracts before splitting the logical command
// list. Submission still rechecks resources and resolved access.
rr->backend.validate_frame(snapshot->frame);
rr->validate_frame(snapshot->frame);
d4r::win::commands::record_boundary(list, [rr, snapshot](ID3D12CommandQueue* queue) {
snapshot->resolve(); rr->backend.evaluate(queue, snapshot->frame, snapshot->access);
snapshot->resolve(); rr->evaluate(queue, snapshot->frame, snapshot->access);
});
return 1u;
});
@@ -361,7 +477,7 @@ API unsigned NVSDK_NGX_D3D12_ReleaseFeature(Handle* handle) {
{ std::lock_guard<std::mutex> lock(apiMutex);
auto it = rrFeatures.find(handle);
if (it != rrFeatures.end()) { rr = std::move(it->second); rrFeatures.erase(it); delete handle; } }
if (rr) return call([&] { rr->backend.drain(); rr.reset(); return 1u; });
if (rr) return call([&] { rr->drain(); rr.reset(); return 1u; });
#endif
std::shared_ptr<d4r::win::Feature> feature;
{ std::lock_guard<std::mutex> lock(apiMutex);
@@ -376,7 +492,7 @@ API unsigned NVSDK_NGX_D3D12_Shutdown() {
return call([&] {
if (runtime && std::getenv("D4R_ASYNC_INTEROP")) runtime->drain_async_queue();
#if defined(D4R_NATIVE_RR_BACKEND)
for (auto& pair : rrFeatures) pair.second->backend.drain();
for (auto& pair : rrFeatures) pair.second->drain();
for (auto& pair : rrFeatures) delete pair.first;
rrFeatures.clear();
#endif
+9
View File
@@ -120,6 +120,7 @@ struct Args {
std::string barrier_mode = "legacy";
bool early_indirect = false;
bool player_mode = false;
bool reset_each_frame = false;
std::string ngx_core, dlss_dll;
std::string nvapi_dll;
std::string ngx_abi = "driver";
@@ -131,6 +132,7 @@ struct Args {
std::string ngx_mode = "init";
unsigned preset = 11;
unsigned ngx_create_flags = 0;
unsigned ngx_quality = 2;
unsigned output_width = 512, output_height = 288;
unsigned input_width = 0, input_height = 0;
std::string fixture_dir;
@@ -162,6 +164,7 @@ struct Args {
}
else if (key == "--early-indirect") early_indirect = value == "1";
else if (key == "--player-mode") player_mode = value == "1";
else if (key == "--reset-every-frame") reset_each_frame = value == "1";
else if (key == "--ngx-core") ngx_core = value;
else if (key == "--dlss-dll") dlss_dll = value;
else if (key == "--nvapi-dll") nvapi_dll = value;
@@ -190,6 +193,12 @@ struct Args {
if (end != value.size() || (ngx_create_flags != 0 && ngx_create_flags != 11))
throw std::runtime_error("ngx-create-flags must be 0 or 11 (HDR, low-resolution MV, inverted depth)");
}
else if (key == "--ngx-quality") {
size_t end = 0;
const auto parsed = std::stoul(value, &end);
if (end != value.size() || parsed > 5) throw std::runtime_error("ngx-quality must be 0..5");
ngx_quality = static_cast<unsigned>(parsed);
}
else if (key == "--ngx-output-resolution") {
const auto separator = value.find('x');
if (separator == std::string::npos) throw std::runtime_error("Output resolution must be WIDTHxHEIGHT");
+55
View File
@@ -0,0 +1,55 @@
#version 450
// FSR Ray Regeneration backend, stage 3: the denoised diffuse and specular signals remodulated by
// their albedos, plus the residual the denoiser never saw (particles, what demodulation could not
// carry, sky). The result is render-resolution colour for the Super Sampling stage.
layout(local_size_x = 8, local_size_y = 8) in;
layout(binding = 0, rgba16f) uniform readonly image2D denoisedDiffuse;
layout(binding = 1, rgba16f) uniform readonly image2D denoisedSpecular;
layout(binding = 2, rgba16f) uniform readonly image2D diffuseAlbedo;
layout(binding = 3, rgba16f) uniform readonly image2D specularAlbedo;
layout(binding = 4, rgba16f) uniform readonly image2D residual;
layout(binding = 5, rgba16f) uniform writeonly image2D composed;
layout(binding = 6, rgba16f) uniform readonly image2D noisyDiffuse;
layout(binding = 7, rgba16f) uniform readonly image2D noisySpecular;
layout(push_constant) uniform Constants
{
uvec2 renderSize;
float haloLimit; // > 0: each denoised lobe at most this multiple of the brightest noisy input of that lobe around it
uint unused;
} pc;
void main()
{
const ivec2 px = ivec2(gl_GlobalInvocationID.xy);
if (px.x >= int(pc.renderSize.x) || px.y >= int(pc.renderSize.y))
return;
const vec3 da = imageLoad(diffuseAlbedo, px).rgb, sa = imageLoad(specularAlbedo, px).rgb;
vec3 diffuse = imageLoad(denoisedDiffuse, px).rgb * da;
vec3 specular = imageLoad(denoisedSpecular, px).rgb * sa;
if (pc.haloLimit > 0.0)
{
// The denoiser can carry a thin object's large demodulated signal (dark albedo) across its edge, where this
// texel's albedo turns it into a bright fringe. A denoised value above everything around it in the noisy
// input is such a leak: capped, by luminance (the hue is kept).
const vec3 w = vec3(0.2126, 0.7152, 0.0722);
float diffuseMax = 0.0, specularMax = 0.0;
for (int dy = -1; dy <= 1; ++dy)
for (int dx = -1; dx <= 1; ++dx)
{
const ivec2 q = clamp(px + ivec2(dx, dy), ivec2(0), ivec2(pc.renderSize) - 1);
// what the lobe contributes there (the neighbour's own albedo): a leaked demodulated signal
// remodulated by this texel's albedo exceeds all of these
diffuseMax = max(diffuseMax, dot(imageLoad(noisyDiffuse, q).rgb * imageLoad(diffuseAlbedo, q).rgb, w));
specularMax = max(specularMax, dot(imageLoad(noisySpecular, q).rgb * imageLoad(specularAlbedo, q).rgb, w));
}
const float dl = dot(diffuse, w), sl = dot(specular, w);
if (dl > pc.haloLimit * diffuseMax)
diffuse *= pc.haloLimit * diffuseMax / dl;
if (sl > pc.haloLimit * specularMax)
specular *= pc.haloLimit * specularMax / sl;
}
const vec3 color = diffuse + specular + imageLoad(residual, px).rgb;
imageStore(composed, px, vec4(clamp(color, 0.0, 65000.0), 1.0));
}
+194
View File
@@ -0,0 +1,194 @@
#version 450
// FSR Ray Regeneration backend, stage 1: a game's DLSS Ray Reconstruction inputs -> the inputs of
// AMD's FidelityFX denoiser 1.2 (ffx_denoiser.h), at render resolution.
//
// The split of the game's single noisy colour into a diffuse and a specular signal follows the
// DLSS-RR -> FSR-RR adapter of Zach Hembree's OptiScaler branch ffx-denoise-experimental (GPL-3.0,
// 18ac686, FSRDInputConv.hlsl, "Mode 2") and burak113's continuation (5a6adb1): each lobe gets the
// colour in proportion to its albedo and is demodulated by it. That is an approximation - one
// colour cannot be split exactly - and what does not survive demodulation goes to the residual,
// which bypasses the denoiser. Unlike that reference this writes FSR-RR 1.2's contract: signed
// linear depth reconstructed from hardware or linear depth, motion as previous-minus-current UV,
// linear (non-gamma) albedo and the colour before particles as the denoised base.
layout(local_size_x = 8, local_size_y = 8) in;
layout(binding = 0) uniform sampler2D inColor;
layout(binding = 1) uniform sampler2D inDepth;
layout(binding = 2) uniform sampler2D inMotion;
layout(binding = 3) uniform sampler2D inNormals;
layout(binding = 4) uniform sampler2D inRoughness; // the normals again when roughness is packed
layout(binding = 5) uniform sampler2D inDiffuseAlbedo;
layout(binding = 6) uniform sampler2D inSpecularAlbedo;
layout(binding = 7) uniform sampler2D inSpecularHit; // bound to the normals when absent
layout(binding = 8) uniform sampler2D inDiffuseHit; // bound to the normals when absent
layout(binding = 9) uniform sampler2D inBeforeParticles; // bound to the colour when absent
layout(binding = 10, r32f) uniform writeonly image2D outLinearDepth;
layout(binding = 11, rgba16f) uniform writeonly image2D outMotion;
layout(binding = 12, rgba16f) uniform writeonly image2D outNormals;
layout(binding = 13, rgba16f) uniform writeonly image2D outDiffuseAlbedo;
layout(binding = 14, rgba16f) uniform writeonly image2D outSpecularAlbedo;
layout(binding = 15, rgba16f) uniform writeonly image2D outDiffuse;
layout(binding = 16, rgba16f) uniform writeonly image2D outSpecular;
layout(binding = 17, rgba16f) uniform writeonly image2D outResidual;
// Matrices are uploaded as the row-major, row-vector (v' = vM) float[16] NGX and FFX use. Read as a
// GLSL (column-major) mat4 that is the transpose, so M * v here is the row-vector product v * M.
layout(push_constant) uniform Constants
{
mat4 clipToView; // inverse(ViewToClipMatrix)
mat4 viewToPreviousView; // inverse(WorldToView) * previous WorldToView
vec4 renderSize; // width, height, 1 / width, 1 / height
vec4 motion; // xy: MV texel -> UV delta (MV.Scale / MV resolution); zw: MV texel per render pixel
uvec4 colorDepthBase; // colour subrect origin xy, depth subrect origin xy
uvec4 motionBaseFlags; // motion subrect origin xy, flags, unused
vec4 depthParams; // x: sign of view z in front of the camera, y: sky depth (hardware), z: far marker
vec4 detail; // x: share of the colour that bypasses the denoiser (0: none); y: smallest albedo a
// signal is divided by
} pc;
const uint kHardwareDepth = 1u;
const uint kRoughnessPacked = 2u;
const uint kSpecularHit = 4u;
const uint kSpecularHitInW = 8u; // DLSSD.SpecularRayDirectionHitDistance: the distance is .w
const uint kDiffuseHit = 16u;
const uint kDiffuseHitInW = 32u;
const uint kNormalsUnorm = 64u; // [0,1]-encoded normals (UNORM formats)
const uint kBeforeParticles = 128u;
const uint kInvalidNormalPassthrough = 256u;
const float kHalfMax = 65000.0;
vec3 safe(vec3 v)
{
// NaN fails both comparisons and becomes 0
return vec3(v.x >= 0.0 ? min(v.x, kHalfMax) : 0.0, v.y >= 0.0 ? min(v.y, kHalfMax) : 0.0,
v.z >= 0.0 ? min(v.z, kHalfMax) : 0.0);
}
// AMD's NormalToOctahedronUv (FidelityFX denoising.md 5.3.1)
vec2 octahedron(vec3 n)
{
n /= abs(n.x) + abs(n.y) + abs(n.z);
vec2 k = vec2(n.x >= 0.0 ? 1.0 : -1.0, n.y >= 0.0 ? 1.0 : -1.0);
vec2 xy = n.z < 0.0 ? (1.0 - abs(n.yx)) * k : n.xy;
return xy * 0.5 + 0.5;
}
void main()
{
const ivec2 px = ivec2(gl_GlobalInvocationID.xy);
if (px.x >= int(pc.renderSize.x) || px.y >= int(pc.renderSize.y))
return;
const uint flags = pc.motionBaseFlags.z;
const vec2 uv = (vec2(px) + 0.5) * pc.renderSize.zw;
const vec2 ndc = vec2(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0);
// Signed view-space position. Hardware depth goes through the inverse projection, which carries
// the game's handedness and (reversed) depth range; linear depth is the distance along the view
// axis, given its sign by the projection.
const float depth = texelFetch(inDepth, ivec2(pc.colorDepthBase.zw) + px, 0).r;
vec3 viewPos;
bool sky;
if ((flags & kHardwareDepth) != 0u)
{
const vec4 p = pc.clipToView * vec4(ndc, depth, 1.0);
viewPos = p.xyz / p.w;
sky = depth == pc.depthParams.y || !(abs(p.w) > 0.0);
}
else
{
const vec4 p = pc.clipToView * vec4(ndc, 0.5, 1.0);
const vec3 ray = p.xyz / p.w;
const float z = depth * pc.depthParams.x;
viewPos = ray * (z / ray.z);
sky = !(depth > 0.0) || depth >= pc.depthParams.z;
}
const vec3 color = safe(texelFetch(inColor, ivec2(pc.colorDepthBase.xy) + px, 0).rgb);
const vec3 base = (flags & kBeforeParticles) != 0u ? safe(texelFetch(inBeforeParticles, px, 0).rgb) : color;
// Particles (colour after minus before) stay out of the denoiser; signed, so smoke can darken.
const vec3 particles = color - base;
vec3 diffuseAlbedo = clamp(texelFetch(inDiffuseAlbedo, px, 0).rgb, 0.0, 1.0);
vec3 specularAlbedo = clamp(texelFetch(inSpecularAlbedo, px, 0).rgb, 0.0, 1.0);
// Energy above 1 is taken from the diffuse lobe first (reference: albedo overshoot clamp).
diffuseAlbedo = max(diffuseAlbedo - max(diffuseAlbedo + specularAlbedo - 1.0, 0.0), 0.0);
const float albedoSum = dot(diffuseAlbedo + specularAlbedo, vec3(1.0));
sky = sky || !(albedoSum > 3e-3) || any(isnan(viewPos)) || any(isinf(viewPos));
const vec4 normalTexel = texelFetch(inNormals, px, 0);
vec3 normal = (flags & kNormalsUnorm) != 0u ? normalTexel.xyz * 2.0 - 1.0 : normalTexel.xyz;
const float normalLength = dot(normal, normal);
// A zero/invalid normal is no surface, not an invented world-Z-facing mirror. Alan Wake 2
// supplies zero normals for its sky with finite FP16-max depth, so the far bound alone misses it.
if ((flags & kInvalidNormalPassthrough) != 0u)
sky = sky || !(normalLength > 1e-8) || isnan(normalLength) || isinf(normalLength);
if (sky)
{
// Outside linearDepthBounds: the denoiser passes these texels through, and the residual carries
// the whole colour.
imageStore(outLinearDepth, px, vec4(pc.depthParams.z * 10.0 * pc.depthParams.x));
imageStore(outMotion, px, vec4(0.0));
imageStore(outNormals, px, vec4(0.5, 0.5, 1.0, 0.0));
imageStore(outDiffuseAlbedo, px, vec4(0.0));
imageStore(outSpecularAlbedo, px, vec4(0.0));
imageStore(outDiffuse, px, vec4(0.0));
imageStore(outSpecular, px, vec4(0.0));
imageStore(outResidual, px, vec4(color, 0.0));
return;
}
imageStore(outLinearDepth, px, vec4(viewPos.z));
// 2.5D motion: DLSS motion vectors point from this pixel to where it was (previous - current), in
// the MV texture's pixels. The depth delta is the camera's: the previous view's z of this point
// (moving geometry is not reprojected; there is no previous depth for it).
const ivec2 mvTexel = ivec2(pc.motionBaseFlags.xy) + ivec2(vec2(px) * pc.motion.zw);
const vec2 mv = texelFetch(inMotion, mvTexel, 0).xy * pc.motion.xy;
const vec4 previous = pc.viewToPreviousView * vec4(viewPos, 1.0);
imageStore(outMotion, px, vec4(mv, previous.z / previous.w - viewPos.z, 0.0));
normal = dot(normal, normal) > 1e-8 ? normalize(normal) : vec3(0.0, 0.0, 1.0);
const float roughness = clamp((flags & kRoughnessPacked) != 0u ? normalTexel.w : texelFetch(inRoughness, px, 0).r,
0.0, 1.0);
// material type 0 (the default material) everywhere
imageStore(outNormals, px, vec4(octahedron(normal), roughness, 0.0));
imageStore(outDiffuseAlbedo, px, vec4(diffuseAlbedo, 0.0));
imageStore(outSpecularAlbedo, px, vec4(specularAlbedo, 0.0));
// Split by albedo and demodulate (reference "Mode 2"). Per channel, so a tinted metal keeps its tint.
const vec3 total = diffuseAlbedo + specularAlbedo;
const vec3 specularShare = specularAlbedo / max(total, vec3(1e-4));
const vec3 specularColor = base * specularShare;
const vec3 diffuseColor = base - specularColor;
// A dark albedo would turn light it did not shape (fog, sky light, the background at a thin object's edge) into
// a huge signal that the denoiser clips (fog lost) or spreads (bright fringes): no albedo below the floor
// divides a signal, and what the division cannot carry stays in the residual.
vec3 diffuse = safe(diffuseColor / max(diffuseAlbedo, vec3(pc.detail.y)));
vec3 specular = safe(specularColor / max(specularAlbedo, vec3(pc.detail.y)));
if (pc.detail.x > 0.0)
{
// Fine detail the denoiser treats as noise (hair, water ripples): part of the colour goes around it,
// through the residual.
const float denoised = 1.0 - clamp(pc.detail.x, 0.0, 1.0);
diffuse *= denoised;
specular *= denoised;
}
const vec3 remodulated = diffuse * diffuseAlbedo + specular * specularAlbedo;
// A: the ray hit distance for an indirect signal (ignored, but non-negative, for a direct one)
float specularHit = 0.0, diffuseHit = 0.0;
if ((flags & kSpecularHit) != 0u)
{
const vec4 hit = texelFetch(inSpecularHit, px, 0);
specularHit = clamp((flags & kSpecularHitInW) != 0u ? hit.w : hit.x, 0.0, kHalfMax);
}
if ((flags & kDiffuseHit) != 0u)
{
const vec4 hit = texelFetch(inDiffuseHit, px, 0);
diffuseHit = clamp((flags & kDiffuseHitInW) != 0u ? hit.w : hit.x, 0.0, kHalfMax);
}
imageStore(outDiffuse, px, vec4(diffuse, diffuseHit));
imageStore(outSpecular, px, vec4(specular, specularHit));
imageStore(outResidual, px, vec4(base - remodulated + particles, 0.0));
}
+729
View File
@@ -0,0 +1,729 @@
#pragma once
#include <cstdint>
// Generated by scripts/windows/compile-fsr-rr-shaders.py; do not edit.
static const uint32_t d4r_vk_fsr_rr_prepare[] = {
0x07230203,0x00010000,0x0008000b,0x000002d8,0x00000000,0x00020011,0x00000001,0x0006000b,
0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001,
0x0006000f,0x00000005,0x00000004,0x6e69616d,0x00000000,0x00000078,0x00060010,0x00000004,
0x00000011,0x00000008,0x00000008,0x00000001,0x00030003,0x00000002,0x000001c2,0x00040005,
0x00000004,0x6e69616d,0x00000000,0x00050005,0x0000000b,0x65666173,0x33667628,0x0000003b,
0x00030005,0x0000000a,0x00000076,0x00060005,0x00000010,0x6174636f,0x72646568,0x76286e6f,
0x003b3366,0x00030005,0x0000000f,0x0000006e,0x00030005,0x0000004d,0x0000006b,0x00030005,
0x00000059,0x00007978,0x00030005,0x00000075,0x00007870,0x00080005,0x00000078,0x475f6c67,
0x61626f6c,0x766e496c,0x7461636f,0x496e6f69,0x00000044,0x00050005,0x00000083,0x736e6f43,
0x746e6174,0x00000073,0x00060006,0x00000083,0x00000000,0x70696c63,0x69566f54,0x00007765,
0x00080006,0x00000083,0x00000001,0x77656976,0x72506f54,0x6f697665,0x69567375,0x00007765,
0x00060006,0x00000083,0x00000002,0x646e6572,0x69537265,0x0000657a,0x00050006,0x00000083,
0x00000003,0x69746f6d,0x00006e6f,0x00070006,0x00000083,0x00000004,0x6f6c6f63,0x70654472,
0x61426874,0x00006573,0x00070006,0x00000083,0x00000005,0x69746f6d,0x61426e6f,0x6c466573,
0x00736761,0x00060006,0x00000083,0x00000006,0x74706564,0x72615068,0x00736d61,0x00050006,
0x00000083,0x00000007,0x61746564,0x00006c69,0x00030005,0x00000085,0x00006370,0x00040005,
0x0000009a,0x67616c66,0x00000073,0x00030005,0x0000009f,0x00007675,0x00030005,0x000000a9,
0x0063646e,0x00040005,0x000000b4,0x74706564,0x00000068,0x00040005,0x000000b8,0x65446e69,
0x00687470,0x00030005,0x000000cc,0x00000070,0x00040005,0x000000d6,0x77656976,0x00736f50,
0x00030005,0x000000df,0x00796b73,0x00030005,0x000000ef,0x00000070,0x00030005,0x000000f7,
0x00796172,0x00030005,0x000000fe,0x0000007a,0x00040005,0x00000114,0x6f6c6f63,0x00000072,
0x00040005,0x00000115,0x6f436e69,0x00726f6c,0x00040005,0x0000011f,0x61726170,0x0000006d,
0x00040005,0x00000122,0x65736162,0x00000000,0x00070005,0x0000012a,0x65426e69,0x65726f66,
0x74726150,0x656c6369,0x00000073,0x00040005,0x0000012f,0x61726170,0x0000006d,0x00050005,
0x00000135,0x74726170,0x656c6369,0x00000073,0x00060005,0x00000139,0x66666964,0x41657375,
0x6465626c,0x0000006f,0x00060005,0x0000013a,0x69446e69,0x73756666,0x626c4165,0x006f6465,
0x00060005,0x00000143,0x63657073,0x72616c75,0x65626c41,0x00006f64,0x00070005,0x00000144,
0x70536e69,0x6c756365,0x6c417261,0x6f646562,0x00000000,0x00050005,0x00000158,0x65626c61,
0x75536f64,0x0000006d,0x00050005,0x00000176,0x6d726f6e,0x65546c61,0x006c6578,0x00050005,
0x00000177,0x6f4e6e69,0x6c616d72,0x00000073,0x00040005,0x0000017c,0x6d726f6e,0x00006c61,
0x00060005,0x0000018d,0x6d726f6e,0x654c6c61,0x6874676e,0x00000000,0x00060005,0x000001b1,
0x4c74756f,0x61656e69,0x70654472,0x00006874,0x00050005,0x000001be,0x4d74756f,0x6f69746f,
0x0000006e,0x00050005,0x000001c2,0x4e74756f,0x616d726f,0x0000736c,0x00070005,0x000001c6,
0x4474756f,0x75666669,0x6c416573,0x6f646562,0x00000000,0x00070005,0x000001c9,0x5374756f,
0x75636570,0x4172616c,0x6465626c,0x0000006f,0x00050005,0x000001cc,0x4474756f,0x75666669,
0x00006573,0x00050005,0x000001cf,0x5374756f,0x75636570,0x0072616c,0x00050005,0x000001d2,
0x5274756f,0x64697365,0x006c6175,0x00040005,0x000001e0,0x6554766d,0x006c6578,0x00030005,
0x000001ee,0x0000766d,0x00050005,0x000001ef,0x6f4d6e69,0x6e6f6974,0x00000000,0x00050005,
0x000001f9,0x76657270,0x73756f69,0x00000000,0x00050005,0x0000021d,0x67756f72,0x73656e68,
0x00000073,0x00050005,0x00000227,0x6f526e69,0x6e686775,0x00737365,0x00040005,0x00000231,
0x61726170,0x0000006d,0x00040005,0x00000246,0x61746f74,0x0000006c,0x00060005,0x0000024a,
0x63657073,0x72616c75,0x72616853,0x00000065,0x00060005,0x00000251,0x63657073,0x72616c75,
0x6f6c6f43,0x00000072,0x00060005,0x00000255,0x66666964,0x43657375,0x726f6c6f,0x00000000,
0x00040005,0x00000259,0x66666964,0x00657375,0x00040005,0x00000262,0x61726170,0x0000006d,
0x00050005,0x00000264,0x63657073,0x72616c75,0x00000000,0x00040005,0x0000026c,0x61726170,
0x0000006d,0x00050005,0x00000273,0x6f6e6564,0x64657369,0x00000000,0x00050005,0x0000027e,
0x6f6d6572,0x616c7564,0x00646574,0x00050005,0x00000286,0x63657073,0x72616c75,0x00746948,
0x00050005,0x00000287,0x66666964,0x48657375,0x00007469,0x00030005,0x0000028e,0x00746968,
0x00060005,0x0000028f,0x70536e69,0x6c756365,0x69487261,0x00000074,0x00030005,0x000002a8,
0x00746968,0x00060005,0x000002a9,0x69446e69,0x73756666,0x74694865,0x00000000,0x00040047,
0x00000078,0x0000000b,0x0000001c,0x00030047,0x00000083,0x00000002,0x00040048,0x00000083,
0x00000000,0x00000005,0x00050048,0x00000083,0x00000000,0x00000007,0x00000010,0x00050048,
0x00000083,0x00000000,0x00000023,0x00000000,0x00040048,0x00000083,0x00000001,0x00000005,
0x00050048,0x00000083,0x00000001,0x00000007,0x00000010,0x00050048,0x00000083,0x00000001,
0x00000023,0x00000040,0x00050048,0x00000083,0x00000002,0x00000023,0x00000080,0x00050048,
0x00000083,0x00000003,0x00000023,0x00000090,0x00050048,0x00000083,0x00000004,0x00000023,
0x000000a0,0x00050048,0x00000083,0x00000005,0x00000023,0x000000b0,0x00050048,0x00000083,
0x00000006,0x00000023,0x000000c0,0x00050048,0x00000083,0x00000007,0x00000023,0x000000d0,
0x00040047,0x000000b8,0x00000021,0x00000001,0x00040047,0x000000b8,0x00000022,0x00000000,
0x00040047,0x00000115,0x00000021,0x00000000,0x00040047,0x00000115,0x00000022,0x00000000,
0x00040047,0x0000012a,0x00000021,0x00000009,0x00040047,0x0000012a,0x00000022,0x00000000,
0x00040047,0x0000013a,0x00000021,0x00000005,0x00040047,0x0000013a,0x00000022,0x00000000,
0x00040047,0x00000144,0x00000021,0x00000006,0x00040047,0x00000144,0x00000022,0x00000000,
0x00040047,0x00000177,0x00000021,0x00000003,0x00040047,0x00000177,0x00000022,0x00000000,
0x00030047,0x000001b1,0x00000019,0x00040047,0x000001b1,0x00000021,0x0000000a,0x00040047,
0x000001b1,0x00000022,0x00000000,0x00030047,0x000001be,0x00000019,0x00040047,0x000001be,
0x00000021,0x0000000b,0x00040047,0x000001be,0x00000022,0x00000000,0x00030047,0x000001c2,
0x00000019,0x00040047,0x000001c2,0x00000021,0x0000000c,0x00040047,0x000001c2,0x00000022,
0x00000000,0x00030047,0x000001c6,0x00000019,0x00040047,0x000001c6,0x00000021,0x0000000d,
0x00040047,0x000001c6,0x00000022,0x00000000,0x00030047,0x000001c9,0x00000019,0x00040047,
0x000001c9,0x00000021,0x0000000e,0x00040047,0x000001c9,0x00000022,0x00000000,0x00030047,
0x000001cc,0x00000019,0x00040047,0x000001cc,0x00000021,0x0000000f,0x00040047,0x000001cc,
0x00000022,0x00000000,0x00030047,0x000001cf,0x00000019,0x00040047,0x000001cf,0x00000021,
0x00000010,0x00040047,0x000001cf,0x00000022,0x00000000,0x00030047,0x000001d2,0x00000019,
0x00040047,0x000001d2,0x00000021,0x00000011,0x00040047,0x000001d2,0x00000022,0x00000000,
0x00040047,0x000001ef,0x00000021,0x00000002,0x00040047,0x000001ef,0x00000022,0x00000000,
0x00040047,0x00000227,0x00000021,0x00000004,0x00040047,0x00000227,0x00000022,0x00000000,
0x00040047,0x0000028f,0x00000021,0x00000007,0x00040047,0x0000028f,0x00000022,0x00000000,
0x00040047,0x000002a9,0x00000021,0x00000008,0x00040047,0x000002a9,0x00000022,0x00000000,
0x00040047,0x000002d7,0x0000000b,0x00000019,0x00020013,0x00000002,0x00030021,0x00000003,
0x00000002,0x00030016,0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000003,
0x00040020,0x00000008,0x00000007,0x00000007,0x00040021,0x00000009,0x00000007,0x00000008,
0x00040017,0x0000000d,0x00000006,0x00000002,0x00040021,0x0000000e,0x0000000d,0x00000008,
0x00040015,0x00000012,0x00000020,0x00000000,0x0004002b,0x00000012,0x00000013,0x00000000,
0x00040020,0x00000014,0x00000007,0x00000006,0x0004002b,0x00000006,0x00000017,0x00000000,
0x00020014,0x00000018,0x0004002b,0x00000006,0x0000001f,0x477de800,0x0004002b,0x00000012,
0x00000023,0x00000001,0x0004002b,0x00000012,0x0000002f,0x00000002,0x00040020,0x0000004c,
0x00000007,0x0000000d,0x0004002b,0x00000006,0x00000051,0x3f800000,0x0004002b,0x00000006,
0x00000052,0xbf800000,0x0004002b,0x00000006,0x0000006c,0x3f000000,0x00040015,0x00000072,
0x00000020,0x00000001,0x00040017,0x00000073,0x00000072,0x00000002,0x00040020,0x00000074,
0x00000007,0x00000073,0x00040017,0x00000076,0x00000012,0x00000003,0x00040020,0x00000077,
0x00000001,0x00000076,0x0004003b,0x00000077,0x00000078,0x00000001,0x00040017,0x00000079,
0x00000012,0x00000002,0x00040020,0x0000007d,0x00000007,0x00000072,0x00040017,0x00000080,
0x00000006,0x00000004,0x00040018,0x00000081,0x00000080,0x00000004,0x00040017,0x00000082,
0x00000012,0x00000004,0x000a001e,0x00000083,0x00000081,0x00000081,0x00000080,0x00000080,
0x00000082,0x00000082,0x00000080,0x00000080,0x00040020,0x00000084,0x00000009,0x00000083,
0x0004003b,0x00000084,0x00000085,0x00000009,0x0004002b,0x00000072,0x00000086,0x00000002,
0x00040020,0x00000087,0x00000009,0x00000006,0x00040020,0x00000099,0x00000007,0x00000012,
0x0004002b,0x00000072,0x0000009b,0x00000005,0x00040020,0x0000009c,0x00000009,0x00000012,
0x00040020,0x000000a4,0x00000009,0x00000080,0x0004002b,0x00000006,0x000000ac,0x40000000,
0x00090019,0x000000b5,0x00000006,0x00000001,0x00000000,0x00000000,0x00000000,0x00000001,
0x00000000,0x0003001b,0x000000b6,0x000000b5,0x00040020,0x000000b7,0x00000000,0x000000b6,
0x0004003b,0x000000b7,0x000000b8,0x00000000,0x0004002b,0x00000072,0x000000ba,0x00000004,
0x00040020,0x000000bb,0x00000009,0x00000082,0x0004002b,0x00000072,0x000000c2,0x00000000,
0x00040020,0x000000cb,0x00000007,0x00000080,0x00040020,0x000000cd,0x00000009,0x00000081,
0x0004002b,0x00000012,0x000000d9,0x00000003,0x00040020,0x000000de,0x00000007,0x00000018,
0x0004002b,0x00000072,0x000000e1,0x00000006,0x0004003b,0x000000b7,0x00000115,0x00000000,
0x0004002b,0x00000012,0x00000124,0x00000080,0x0004003b,0x000000b7,0x0000012a,0x00000000,
0x0004003b,0x000000b7,0x0000013a,0x00000000,0x0004003b,0x000000b7,0x00000144,0x00000000,
0x0006002c,0x00000007,0x0000015c,0x00000051,0x00000051,0x00000051,0x0004002b,0x00000006,
0x00000163,0x3b449ba6,0x00040017,0x0000016b,0x00000018,0x00000003,0x0004003b,0x000000b7,
0x00000177,0x00000000,0x0004002b,0x00000012,0x0000017e,0x00000040,0x0004002b,0x00000012,
0x00000192,0x00000100,0x0004002b,0x00000006,0x0000019c,0x322bcc77,0x00090019,0x000001af,
0x00000006,0x00000001,0x00000000,0x00000000,0x00000000,0x00000002,0x00000003,0x00040020,
0x000001b0,0x00000000,0x000001af,0x0004003b,0x000001b0,0x000001b1,0x00000000,0x0004002b,
0x00000006,0x000001b6,0x41200000,0x00090019,0x000001bc,0x00000006,0x00000001,0x00000000,
0x00000000,0x00000000,0x00000002,0x00000002,0x00040020,0x000001bd,0x00000000,0x000001bc,
0x0004003b,0x000001bd,0x000001be,0x00000000,0x0007002c,0x00000080,0x000001c1,0x00000017,
0x00000017,0x00000017,0x00000017,0x0004003b,0x000001bd,0x000001c2,0x00000000,0x0007002c,
0x00000080,0x000001c5,0x0000006c,0x0000006c,0x00000051,0x00000017,0x0004003b,0x000001bd,
0x000001c6,0x00000000,0x0004003b,0x000001bd,0x000001c9,0x00000000,0x0004003b,0x000001bd,
0x000001cc,0x00000000,0x0004003b,0x000001bd,0x000001cf,0x00000000,0x0004003b,0x000001bd,
0x000001d2,0x00000000,0x0004002b,0x00000072,0x000001e7,0x00000003,0x0004003b,0x000000b7,
0x000001ef,0x00000000,0x0004002b,0x00000072,0x000001fa,0x00000001,0x0006002c,0x00000007,
0x0000021b,0x00000017,0x00000017,0x00000051,0x0004003b,0x000000b7,0x00000227,0x00000000,
0x0004002b,0x00000006,0x0000024d,0x38d1b717,0x0006002c,0x00000007,0x0000024e,0x0000024d,
0x0000024d,0x0000024d,0x0004002b,0x00000072,0x0000025c,0x00000007,0x0004002b,0x00000012,
0x00000289,0x00000004,0x0004003b,0x000000b7,0x0000028f,0x00000000,0x0004002b,0x00000012,
0x00000295,0x00000008,0x0004002b,0x00000012,0x000002a3,0x00000010,0x0004003b,0x000000b7,
0x000002a9,0x00000000,0x0004002b,0x00000012,0x000002af,0x00000020,0x0006002c,0x00000076,
0x000002d7,0x00000295,0x00000295,0x00000023,0x00050036,0x00000002,0x00000004,0x00000000,
0x00000003,0x000200f8,0x00000005,0x0004003b,0x00000074,0x00000075,0x00000007,0x0004003b,
0x00000099,0x0000009a,0x00000007,0x0004003b,0x0000004c,0x0000009f,0x00000007,0x0004003b,
0x0000004c,0x000000a9,0x00000007,0x0004003b,0x00000014,0x000000b4,0x00000007,0x0004003b,
0x000000cb,0x000000cc,0x00000007,0x0004003b,0x00000008,0x000000d6,0x00000007,0x0004003b,
0x000000de,0x000000df,0x00000007,0x0004003b,0x000000cb,0x000000ef,0x00000007,0x0004003b,
0x00000008,0x000000f7,0x00000007,0x0004003b,0x00000014,0x000000fe,0x00000007,0x0004003b,
0x00000008,0x00000114,0x00000007,0x0004003b,0x00000008,0x0000011f,0x00000007,0x0004003b,
0x00000008,0x00000122,0x00000007,0x0004003b,0x00000008,0x00000127,0x00000007,0x0004003b,
0x00000008,0x0000012f,0x00000007,0x0004003b,0x00000008,0x00000135,0x00000007,0x0004003b,
0x00000008,0x00000139,0x00000007,0x0004003b,0x00000008,0x00000143,0x00000007,0x0004003b,
0x00000014,0x00000158,0x00000007,0x0004003b,0x000000cb,0x00000176,0x00000007,0x0004003b,
0x00000008,0x0000017c,0x00000007,0x0004003b,0x00000008,0x00000181,0x00000007,0x0004003b,
0x00000014,0x0000018d,0x00000007,0x0004003b,0x00000074,0x000001e0,0x00000007,0x0004003b,
0x0000004c,0x000001ee,0x00000007,0x0004003b,0x000000cb,0x000001f9,0x00000007,0x0004003b,
0x00000008,0x00000215,0x00000007,0x0004003b,0x00000014,0x0000021d,0x00000007,0x0004003b,
0x00000014,0x00000221,0x00000007,0x0004003b,0x00000008,0x00000231,0x00000007,0x0004003b,
0x00000008,0x00000246,0x00000007,0x0004003b,0x00000008,0x0000024a,0x00000007,0x0004003b,
0x00000008,0x00000251,0x00000007,0x0004003b,0x00000008,0x00000255,0x00000007,0x0004003b,
0x00000008,0x00000259,0x00000007,0x0004003b,0x00000008,0x00000262,0x00000007,0x0004003b,
0x00000008,0x00000264,0x00000007,0x0004003b,0x00000008,0x0000026c,0x00000007,0x0004003b,
0x00000014,0x00000273,0x00000007,0x0004003b,0x00000008,0x0000027e,0x00000007,0x0004003b,
0x00000014,0x00000286,0x00000007,0x0004003b,0x00000014,0x00000287,0x00000007,0x0004003b,
0x000000cb,0x0000028e,0x00000007,0x0004003b,0x00000014,0x00000298,0x00000007,0x0004003b,
0x000000cb,0x000002a8,0x00000007,0x0004003b,0x00000014,0x000002b2,0x00000007,0x0004003d,
0x00000076,0x0000007a,0x00000078,0x0007004f,0x00000079,0x0000007b,0x0000007a,0x0000007a,
0x00000000,0x00000001,0x0004007c,0x00000073,0x0000007c,0x0000007b,0x0003003e,0x00000075,
0x0000007c,0x00050041,0x0000007d,0x0000007e,0x00000075,0x00000013,0x0004003d,0x00000072,
0x0000007f,0x0000007e,0x00060041,0x00000087,0x00000088,0x00000085,0x00000086,0x00000013,
0x0004003d,0x00000006,0x00000089,0x00000088,0x0004006e,0x00000072,0x0000008a,0x00000089,
0x000500af,0x00000018,0x0000008b,0x0000007f,0x0000008a,0x000400a8,0x00000018,0x0000008c,
0x0000008b,0x000300f7,0x0000008e,0x00000000,0x000400fa,0x0000008c,0x0000008d,0x0000008e,
0x000200f8,0x0000008d,0x00050041,0x0000007d,0x0000008f,0x00000075,0x00000023,0x0004003d,
0x00000072,0x00000090,0x0000008f,0x00060041,0x00000087,0x00000091,0x00000085,0x00000086,
0x00000023,0x0004003d,0x00000006,0x00000092,0x00000091,0x0004006e,0x00000072,0x00000093,
0x00000092,0x000500af,0x00000018,0x00000094,0x00000090,0x00000093,0x000200f9,0x0000008e,
0x000200f8,0x0000008e,0x000700f5,0x00000018,0x00000095,0x0000008b,0x00000005,0x00000094,
0x0000008d,0x000300f7,0x00000097,0x00000000,0x000400fa,0x00000095,0x00000096,0x00000097,
0x000200f8,0x00000096,0x000100fd,0x000200f8,0x00000097,0x00060041,0x0000009c,0x0000009d,
0x00000085,0x0000009b,0x0000002f,0x0004003d,0x00000012,0x0000009e,0x0000009d,0x0003003e,
0x0000009a,0x0000009e,0x0004003d,0x00000073,0x000000a0,0x00000075,0x0004006f,0x0000000d,
0x000000a1,0x000000a0,0x00050050,0x0000000d,0x000000a2,0x0000006c,0x0000006c,0x00050081,
0x0000000d,0x000000a3,0x000000a1,0x000000a2,0x00050041,0x000000a4,0x000000a5,0x00000085,
0x00000086,0x0004003d,0x00000080,0x000000a6,0x000000a5,0x0007004f,0x0000000d,0x000000a7,
0x000000a6,0x000000a6,0x00000002,0x00000003,0x00050085,0x0000000d,0x000000a8,0x000000a3,
0x000000a7,0x0003003e,0x0000009f,0x000000a8,0x00050041,0x00000014,0x000000aa,0x0000009f,
0x00000013,0x0004003d,0x00000006,0x000000ab,0x000000aa,0x00050085,0x00000006,0x000000ad,
0x000000ab,0x000000ac,0x00050083,0x00000006,0x000000ae,0x000000ad,0x00000051,0x00050041,
0x00000014,0x000000af,0x0000009f,0x00000023,0x0004003d,0x00000006,0x000000b0,0x000000af,
0x00050085,0x00000006,0x000000b1,0x000000b0,0x000000ac,0x00050083,0x00000006,0x000000b2,
0x00000051,0x000000b1,0x00050050,0x0000000d,0x000000b3,0x000000ae,0x000000b2,0x0003003e,
0x000000a9,0x000000b3,0x0004003d,0x000000b6,0x000000b9,0x000000b8,0x00050041,0x000000bb,
0x000000bc,0x00000085,0x000000ba,0x0004003d,0x00000082,0x000000bd,0x000000bc,0x0007004f,
0x00000079,0x000000be,0x000000bd,0x000000bd,0x00000002,0x00000003,0x0004007c,0x00000073,
0x000000bf,0x000000be,0x0004003d,0x00000073,0x000000c0,0x00000075,0x00050080,0x00000073,
0x000000c1,0x000000bf,0x000000c0,0x00040064,0x000000b5,0x000000c3,0x000000b9,0x0007005f,
0x00000080,0x000000c4,0x000000c3,0x000000c1,0x00000002,0x000000c2,0x00050051,0x00000006,
0x000000c5,0x000000c4,0x00000000,0x0003003e,0x000000b4,0x000000c5,0x0004003d,0x00000012,
0x000000c6,0x0000009a,0x000500c7,0x00000012,0x000000c7,0x000000c6,0x00000023,0x000500ab,
0x00000018,0x000000c8,0x000000c7,0x00000013,0x000300f7,0x000000ca,0x00000000,0x000400fa,
0x000000c8,0x000000c9,0x000000ee,0x000200f8,0x000000c9,0x00050041,0x000000cd,0x000000ce,
0x00000085,0x000000c2,0x0004003d,0x00000081,0x000000cf,0x000000ce,0x0004003d,0x0000000d,
0x000000d0,0x000000a9,0x0004003d,0x00000006,0x000000d1,0x000000b4,0x00050051,0x00000006,
0x000000d2,0x000000d0,0x00000000,0x00050051,0x00000006,0x000000d3,0x000000d0,0x00000001,
0x00070050,0x00000080,0x000000d4,0x000000d2,0x000000d3,0x000000d1,0x00000051,0x00050091,
0x00000080,0x000000d5,0x000000cf,0x000000d4,0x0003003e,0x000000cc,0x000000d5,0x0004003d,
0x00000080,0x000000d7,0x000000cc,0x0008004f,0x00000007,0x000000d8,0x000000d7,0x000000d7,
0x00000000,0x00000001,0x00000002,0x00050041,0x00000014,0x000000da,0x000000cc,0x000000d9,
0x0004003d,0x00000006,0x000000db,0x000000da,0x00060050,0x00000007,0x000000dc,0x000000db,
0x000000db,0x000000db,0x00050088,0x00000007,0x000000dd,0x000000d8,0x000000dc,0x0003003e,
0x000000d6,0x000000dd,0x0004003d,0x00000006,0x000000e0,0x000000b4,0x00060041,0x00000087,
0x000000e2,0x00000085,0x000000e1,0x00000023,0x0004003d,0x00000006,0x000000e3,0x000000e2,
0x000500b4,0x00000018,0x000000e4,0x000000e0,0x000000e3,0x000400a8,0x00000018,0x000000e5,
0x000000e4,0x000300f7,0x000000e7,0x00000000,0x000400fa,0x000000e5,0x000000e6,0x000000e7,
0x000200f8,0x000000e6,0x00050041,0x00000014,0x000000e8,0x000000cc,0x000000d9,0x0004003d,
0x00000006,0x000000e9,0x000000e8,0x0006000c,0x00000006,0x000000ea,0x00000001,0x00000004,
0x000000e9,0x000500ba,0x00000018,0x000000eb,0x000000ea,0x00000017,0x000400a8,0x00000018,
0x000000ec,0x000000eb,0x000200f9,0x000000e7,0x000200f8,0x000000e7,0x000700f5,0x00000018,
0x000000ed,0x000000e4,0x000000c9,0x000000ec,0x000000e6,0x0003003e,0x000000df,0x000000ed,
0x000200f9,0x000000ca,0x000200f8,0x000000ee,0x00050041,0x000000cd,0x000000f0,0x00000085,
0x000000c2,0x0004003d,0x00000081,0x000000f1,0x000000f0,0x0004003d,0x0000000d,0x000000f2,
0x000000a9,0x00050051,0x00000006,0x000000f3,0x000000f2,0x00000000,0x00050051,0x00000006,
0x000000f4,0x000000f2,0x00000001,0x00070050,0x00000080,0x000000f5,0x000000f3,0x000000f4,
0x0000006c,0x00000051,0x00050091,0x00000080,0x000000f6,0x000000f1,0x000000f5,0x0003003e,
0x000000ef,0x000000f6,0x0004003d,0x00000080,0x000000f8,0x000000ef,0x0008004f,0x00000007,
0x000000f9,0x000000f8,0x000000f8,0x00000000,0x00000001,0x00000002,0x00050041,0x00000014,
0x000000fa,0x000000ef,0x000000d9,0x0004003d,0x00000006,0x000000fb,0x000000fa,0x00060050,
0x00000007,0x000000fc,0x000000fb,0x000000fb,0x000000fb,0x00050088,0x00000007,0x000000fd,
0x000000f9,0x000000fc,0x0003003e,0x000000f7,0x000000fd,0x0004003d,0x00000006,0x000000ff,
0x000000b4,0x00060041,0x00000087,0x00000100,0x00000085,0x000000e1,0x00000013,0x0004003d,
0x00000006,0x00000101,0x00000100,0x00050085,0x00000006,0x00000102,0x000000ff,0x00000101,
0x0003003e,0x000000fe,0x00000102,0x0004003d,0x00000007,0x00000103,0x000000f7,0x0004003d,
0x00000006,0x00000104,0x000000fe,0x00050041,0x00000014,0x00000105,0x000000f7,0x0000002f,
0x0004003d,0x00000006,0x00000106,0x00000105,0x00050088,0x00000006,0x00000107,0x00000104,
0x00000106,0x0005008e,0x00000007,0x00000108,0x00000103,0x00000107,0x0003003e,0x000000d6,
0x00000108,0x0004003d,0x00000006,0x00000109,0x000000b4,0x000500ba,0x00000018,0x0000010a,
0x00000109,0x00000017,0x000400a8,0x00000018,0x0000010b,0x0000010a,0x000400a8,0x00000018,
0x0000010c,0x0000010b,0x000300f7,0x0000010e,0x00000000,0x000400fa,0x0000010c,0x0000010d,
0x0000010e,0x000200f8,0x0000010d,0x0004003d,0x00000006,0x0000010f,0x000000b4,0x00060041,
0x00000087,0x00000110,0x00000085,0x000000e1,0x0000002f,0x0004003d,0x00000006,0x00000111,
0x00000110,0x000500be,0x00000018,0x00000112,0x0000010f,0x00000111,0x000200f9,0x0000010e,
0x000200f8,0x0000010e,0x000700f5,0x00000018,0x00000113,0x0000010b,0x000000ee,0x00000112,
0x0000010d,0x0003003e,0x000000df,0x00000113,0x000200f9,0x000000ca,0x000200f8,0x000000ca,
0x0004003d,0x000000b6,0x00000116,0x00000115,0x00050041,0x000000bb,0x00000117,0x00000085,
0x000000ba,0x0004003d,0x00000082,0x00000118,0x00000117,0x0007004f,0x00000079,0x00000119,
0x00000118,0x00000118,0x00000000,0x00000001,0x0004007c,0x00000073,0x0000011a,0x00000119,
0x0004003d,0x00000073,0x0000011b,0x00000075,0x00050080,0x00000073,0x0000011c,0x0000011a,
0x0000011b,0x00040064,0x000000b5,0x0000011d,0x00000116,0x0007005f,0x00000080,0x0000011e,
0x0000011d,0x0000011c,0x00000002,0x000000c2,0x0008004f,0x00000007,0x00000120,0x0000011e,
0x0000011e,0x00000000,0x00000001,0x00000002,0x0003003e,0x0000011f,0x00000120,0x00050039,
0x00000007,0x00000121,0x0000000b,0x0000011f,0x0003003e,0x00000114,0x00000121,0x0004003d,
0x00000012,0x00000123,0x0000009a,0x000500c7,0x00000012,0x00000125,0x00000123,0x00000124,
0x000500ab,0x00000018,0x00000126,0x00000125,0x00000013,0x000300f7,0x00000129,0x00000000,
0x000400fa,0x00000126,0x00000128,0x00000132,0x000200f8,0x00000128,0x0004003d,0x000000b6,
0x0000012b,0x0000012a,0x0004003d,0x00000073,0x0000012c,0x00000075,0x00040064,0x000000b5,
0x0000012d,0x0000012b,0x0007005f,0x00000080,0x0000012e,0x0000012d,0x0000012c,0x00000002,
0x000000c2,0x0008004f,0x00000007,0x00000130,0x0000012e,0x0000012e,0x00000000,0x00000001,
0x00000002,0x0003003e,0x0000012f,0x00000130,0x00050039,0x00000007,0x00000131,0x0000000b,
0x0000012f,0x0003003e,0x00000127,0x00000131,0x000200f9,0x00000129,0x000200f8,0x00000132,
0x0004003d,0x00000007,0x00000133,0x00000114,0x0003003e,0x00000127,0x00000133,0x000200f9,
0x00000129,0x000200f8,0x00000129,0x0004003d,0x00000007,0x00000134,0x00000127,0x0003003e,
0x00000122,0x00000134,0x0004003d,0x00000007,0x00000136,0x00000114,0x0004003d,0x00000007,
0x00000137,0x00000122,0x00050083,0x00000007,0x00000138,0x00000136,0x00000137,0x0003003e,
0x00000135,0x00000138,0x0004003d,0x000000b6,0x0000013b,0x0000013a,0x0004003d,0x00000073,
0x0000013c,0x00000075,0x00040064,0x000000b5,0x0000013d,0x0000013b,0x0007005f,0x00000080,
0x0000013e,0x0000013d,0x0000013c,0x00000002,0x000000c2,0x0008004f,0x00000007,0x0000013f,
0x0000013e,0x0000013e,0x00000000,0x00000001,0x00000002,0x00060050,0x00000007,0x00000140,
0x00000017,0x00000017,0x00000017,0x00060050,0x00000007,0x00000141,0x00000051,0x00000051,
0x00000051,0x0008000c,0x00000007,0x00000142,0x00000001,0x0000002b,0x0000013f,0x00000140,
0x00000141,0x0003003e,0x00000139,0x00000142,0x0004003d,0x000000b6,0x00000145,0x00000144,
0x0004003d,0x00000073,0x00000146,0x00000075,0x00040064,0x000000b5,0x00000147,0x00000145,
0x0007005f,0x00000080,0x00000148,0x00000147,0x00000146,0x00000002,0x000000c2,0x0008004f,
0x00000007,0x00000149,0x00000148,0x00000148,0x00000000,0x00000001,0x00000002,0x00060050,
0x00000007,0x0000014a,0x00000017,0x00000017,0x00000017,0x00060050,0x00000007,0x0000014b,
0x00000051,0x00000051,0x00000051,0x0008000c,0x00000007,0x0000014c,0x00000001,0x0000002b,
0x00000149,0x0000014a,0x0000014b,0x0003003e,0x00000143,0x0000014c,0x0004003d,0x00000007,
0x0000014d,0x00000139,0x0004003d,0x00000007,0x0000014e,0x00000139,0x0004003d,0x00000007,
0x0000014f,0x00000143,0x00050081,0x00000007,0x00000150,0x0000014e,0x0000014f,0x00060050,
0x00000007,0x00000151,0x00000051,0x00000051,0x00000051,0x00050083,0x00000007,0x00000152,
0x00000150,0x00000151,0x00060050,0x00000007,0x00000153,0x00000017,0x00000017,0x00000017,
0x0007000c,0x00000007,0x00000154,0x00000001,0x00000028,0x00000152,0x00000153,0x00050083,
0x00000007,0x00000155,0x0000014d,0x00000154,0x00060050,0x00000007,0x00000156,0x00000017,
0x00000017,0x00000017,0x0007000c,0x00000007,0x00000157,0x00000001,0x00000028,0x00000155,
0x00000156,0x0003003e,0x00000139,0x00000157,0x0004003d,0x00000007,0x00000159,0x00000139,
0x0004003d,0x00000007,0x0000015a,0x00000143,0x00050081,0x00000007,0x0000015b,0x00000159,
0x0000015a,0x00050094,0x00000006,0x0000015d,0x0000015b,0x0000015c,0x0003003e,0x00000158,
0x0000015d,0x0004003d,0x00000018,0x0000015e,0x000000df,0x000400a8,0x00000018,0x0000015f,
0x0000015e,0x000300f7,0x00000161,0x00000000,0x000400fa,0x0000015f,0x00000160,0x00000161,
0x000200f8,0x00000160,0x0004003d,0x00000006,0x00000162,0x00000158,0x000500ba,0x00000018,
0x00000164,0x00000162,0x00000163,0x000400a8,0x00000018,0x00000165,0x00000164,0x000200f9,
0x00000161,0x000200f8,0x00000161,0x000700f5,0x00000018,0x00000166,0x0000015e,0x00000129,
0x00000165,0x00000160,0x000400a8,0x00000018,0x00000167,0x00000166,0x000300f7,0x00000169,
0x00000000,0x000400fa,0x00000167,0x00000168,0x00000169,0x000200f8,0x00000168,0x0004003d,
0x00000007,0x0000016a,0x000000d6,0x0004009c,0x0000016b,0x0000016c,0x0000016a,0x0004009a,
0x00000018,0x0000016d,0x0000016c,0x000200f9,0x00000169,0x000200f8,0x00000169,0x000700f5,
0x00000018,0x0000016e,0x00000166,0x00000161,0x0000016d,0x00000168,0x000400a8,0x00000018,
0x0000016f,0x0000016e,0x000300f7,0x00000171,0x00000000,0x000400fa,0x0000016f,0x00000170,
0x00000171,0x000200f8,0x00000170,0x0004003d,0x00000007,0x00000172,0x000000d6,0x0004009d,
0x0000016b,0x00000173,0x00000172,0x0004009a,0x00000018,0x00000174,0x00000173,0x000200f9,
0x00000171,0x000200f8,0x00000171,0x000700f5,0x00000018,0x00000175,0x0000016e,0x00000169,
0x00000174,0x00000170,0x0003003e,0x000000df,0x00000175,0x0004003d,0x000000b6,0x00000178,
0x00000177,0x0004003d,0x00000073,0x00000179,0x00000075,0x00040064,0x000000b5,0x0000017a,
0x00000178,0x0007005f,0x00000080,0x0000017b,0x0000017a,0x00000179,0x00000002,0x000000c2,
0x0003003e,0x00000176,0x0000017b,0x0004003d,0x00000012,0x0000017d,0x0000009a,0x000500c7,
0x00000012,0x0000017f,0x0000017d,0x0000017e,0x000500ab,0x00000018,0x00000180,0x0000017f,
0x00000013,0x000300f7,0x00000183,0x00000000,0x000400fa,0x00000180,0x00000182,0x00000189,
0x000200f8,0x00000182,0x0004003d,0x00000080,0x00000184,0x00000176,0x0008004f,0x00000007,
0x00000185,0x00000184,0x00000184,0x00000000,0x00000001,0x00000002,0x0005008e,0x00000007,
0x00000186,0x00000185,0x000000ac,0x00060050,0x00000007,0x00000187,0x00000051,0x00000051,
0x00000051,0x00050083,0x00000007,0x00000188,0x00000186,0x00000187,0x0003003e,0x00000181,
0x00000188,0x000200f9,0x00000183,0x000200f8,0x00000189,0x0004003d,0x00000080,0x0000018a,
0x00000176,0x0008004f,0x00000007,0x0000018b,0x0000018a,0x0000018a,0x00000000,0x00000001,
0x00000002,0x0003003e,0x00000181,0x0000018b,0x000200f9,0x00000183,0x000200f8,0x00000183,
0x0004003d,0x00000007,0x0000018c,0x00000181,0x0003003e,0x0000017c,0x0000018c,0x0004003d,
0x00000007,0x0000018e,0x0000017c,0x0004003d,0x00000007,0x0000018f,0x0000017c,0x00050094,
0x00000006,0x00000190,0x0000018e,0x0000018f,0x0003003e,0x0000018d,0x00000190,0x0004003d,
0x00000012,0x00000191,0x0000009a,0x000500c7,0x00000012,0x00000193,0x00000191,0x00000192,
0x000500ab,0x00000018,0x00000194,0x00000193,0x00000013,0x000300f7,0x00000196,0x00000000,
0x000400fa,0x00000194,0x00000195,0x00000196,0x000200f8,0x00000195,0x0004003d,0x00000018,
0x00000197,0x000000df,0x000400a8,0x00000018,0x00000198,0x00000197,0x000300f7,0x0000019a,
0x00000000,0x000400fa,0x00000198,0x00000199,0x0000019a,0x000200f8,0x00000199,0x0004003d,
0x00000006,0x0000019b,0x0000018d,0x000500ba,0x00000018,0x0000019d,0x0000019b,0x0000019c,
0x000400a8,0x00000018,0x0000019e,0x0000019d,0x000200f9,0x0000019a,0x000200f8,0x0000019a,
0x000700f5,0x00000018,0x0000019f,0x00000197,0x00000195,0x0000019e,0x00000199,0x000400a8,
0x00000018,0x000001a0,0x0000019f,0x000300f7,0x000001a2,0x00000000,0x000400fa,0x000001a0,
0x000001a1,0x000001a2,0x000200f8,0x000001a1,0x0004003d,0x00000006,0x000001a3,0x0000018d,
0x0004009c,0x00000018,0x000001a4,0x000001a3,0x000200f9,0x000001a2,0x000200f8,0x000001a2,
0x000700f5,0x00000018,0x000001a5,0x0000019f,0x0000019a,0x000001a4,0x000001a1,0x000400a8,
0x00000018,0x000001a6,0x000001a5,0x000300f7,0x000001a8,0x00000000,0x000400fa,0x000001a6,
0x000001a7,0x000001a8,0x000200f8,0x000001a7,0x0004003d,0x00000006,0x000001a9,0x0000018d,
0x0004009d,0x00000018,0x000001aa,0x000001a9,0x000200f9,0x000001a8,0x000200f8,0x000001a8,
0x000700f5,0x00000018,0x000001ab,0x000001a5,0x000001a2,0x000001aa,0x000001a7,0x0003003e,
0x000000df,0x000001ab,0x000200f9,0x00000196,0x000200f8,0x00000196,0x0004003d,0x00000018,
0x000001ac,0x000000df,0x000300f7,0x000001ae,0x00000000,0x000400fa,0x000001ac,0x000001ad,
0x000001ae,0x000200f8,0x000001ad,0x0004003d,0x000001af,0x000001b2,0x000001b1,0x0004003d,
0x00000073,0x000001b3,0x00000075,0x00060041,0x00000087,0x000001b4,0x00000085,0x000000e1,
0x0000002f,0x0004003d,0x00000006,0x000001b5,0x000001b4,0x00050085,0x00000006,0x000001b7,
0x000001b5,0x000001b6,0x00060041,0x00000087,0x000001b8,0x00000085,0x000000e1,0x00000013,
0x0004003d,0x00000006,0x000001b9,0x000001b8,0x00050085,0x00000006,0x000001ba,0x000001b7,
0x000001b9,0x00070050,0x00000080,0x000001bb,0x000001ba,0x000001ba,0x000001ba,0x000001ba,
0x00040063,0x000001b2,0x000001b3,0x000001bb,0x0004003d,0x000001bc,0x000001bf,0x000001be,
0x0004003d,0x00000073,0x000001c0,0x00000075,0x00040063,0x000001bf,0x000001c0,0x000001c1,
0x0004003d,0x000001bc,0x000001c3,0x000001c2,0x0004003d,0x00000073,0x000001c4,0x00000075,
0x00040063,0x000001c3,0x000001c4,0x000001c5,0x0004003d,0x000001bc,0x000001c7,0x000001c6,
0x0004003d,0x00000073,0x000001c8,0x00000075,0x00040063,0x000001c7,0x000001c8,0x000001c1,
0x0004003d,0x000001bc,0x000001ca,0x000001c9,0x0004003d,0x00000073,0x000001cb,0x00000075,
0x00040063,0x000001ca,0x000001cb,0x000001c1,0x0004003d,0x000001bc,0x000001cd,0x000001cc,
0x0004003d,0x00000073,0x000001ce,0x00000075,0x00040063,0x000001cd,0x000001ce,0x000001c1,
0x0004003d,0x000001bc,0x000001d0,0x000001cf,0x0004003d,0x00000073,0x000001d1,0x00000075,
0x00040063,0x000001d0,0x000001d1,0x000001c1,0x0004003d,0x000001bc,0x000001d3,0x000001d2,
0x0004003d,0x00000073,0x000001d4,0x00000075,0x0004003d,0x00000007,0x000001d5,0x00000114,
0x00050051,0x00000006,0x000001d6,0x000001d5,0x00000000,0x00050051,0x00000006,0x000001d7,
0x000001d5,0x00000001,0x00050051,0x00000006,0x000001d8,0x000001d5,0x00000002,0x00070050,
0x00000080,0x000001d9,0x000001d6,0x000001d7,0x000001d8,0x00000017,0x00040063,0x000001d3,
0x000001d4,0x000001d9,0x000100fd,0x000200f8,0x000001ae,0x0004003d,0x000001af,0x000001db,
0x000001b1,0x0004003d,0x00000073,0x000001dc,0x00000075,0x00050041,0x00000014,0x000001dd,
0x000000d6,0x0000002f,0x0004003d,0x00000006,0x000001de,0x000001dd,0x00070050,0x00000080,
0x000001df,0x000001de,0x000001de,0x000001de,0x000001de,0x00040063,0x000001db,0x000001dc,
0x000001df,0x00050041,0x000000bb,0x000001e1,0x00000085,0x0000009b,0x0004003d,0x00000082,
0x000001e2,0x000001e1,0x0007004f,0x00000079,0x000001e3,0x000001e2,0x000001e2,0x00000000,
0x00000001,0x0004007c,0x00000073,0x000001e4,0x000001e3,0x0004003d,0x00000073,0x000001e5,
0x00000075,0x0004006f,0x0000000d,0x000001e6,0x000001e5,0x00050041,0x000000a4,0x000001e8,
0x00000085,0x000001e7,0x0004003d,0x00000080,0x000001e9,0x000001e8,0x0007004f,0x0000000d,
0x000001ea,0x000001e9,0x000001e9,0x00000002,0x00000003,0x00050085,0x0000000d,0x000001eb,
0x000001e6,0x000001ea,0x0004006e,0x00000073,0x000001ec,0x000001eb,0x00050080,0x00000073,
0x000001ed,0x000001e4,0x000001ec,0x0003003e,0x000001e0,0x000001ed,0x0004003d,0x000000b6,
0x000001f0,0x000001ef,0x0004003d,0x00000073,0x000001f1,0x000001e0,0x00040064,0x000000b5,
0x000001f2,0x000001f0,0x0007005f,0x00000080,0x000001f3,0x000001f2,0x000001f1,0x00000002,
0x000000c2,0x0007004f,0x0000000d,0x000001f4,0x000001f3,0x000001f3,0x00000000,0x00000001,
0x00050041,0x000000a4,0x000001f5,0x00000085,0x000001e7,0x0004003d,0x00000080,0x000001f6,
0x000001f5,0x0007004f,0x0000000d,0x000001f7,0x000001f6,0x000001f6,0x00000000,0x00000001,
0x00050085,0x0000000d,0x000001f8,0x000001f4,0x000001f7,0x0003003e,0x000001ee,0x000001f8,
0x00050041,0x000000cd,0x000001fb,0x00000085,0x000001fa,0x0004003d,0x00000081,0x000001fc,
0x000001fb,0x0004003d,0x00000007,0x000001fd,0x000000d6,0x00050051,0x00000006,0x000001fe,
0x000001fd,0x00000000,0x00050051,0x00000006,0x000001ff,0x000001fd,0x00000001,0x00050051,
0x00000006,0x00000200,0x000001fd,0x00000002,0x00070050,0x00000080,0x00000201,0x000001fe,
0x000001ff,0x00000200,0x00000051,0x00050091,0x00000080,0x00000202,0x000001fc,0x00000201,
0x0003003e,0x000001f9,0x00000202,0x0004003d,0x000001bc,0x00000203,0x000001be,0x0004003d,
0x00000073,0x00000204,0x00000075,0x0004003d,0x0000000d,0x00000205,0x000001ee,0x00050041,
0x00000014,0x00000206,0x000001f9,0x0000002f,0x0004003d,0x00000006,0x00000207,0x00000206,
0x00050041,0x00000014,0x00000208,0x000001f9,0x000000d9,0x0004003d,0x00000006,0x00000209,
0x00000208,0x00050088,0x00000006,0x0000020a,0x00000207,0x00000209,0x00050041,0x00000014,
0x0000020b,0x000000d6,0x0000002f,0x0004003d,0x00000006,0x0000020c,0x0000020b,0x00050083,
0x00000006,0x0000020d,0x0000020a,0x0000020c,0x00050051,0x00000006,0x0000020e,0x00000205,
0x00000000,0x00050051,0x00000006,0x0000020f,0x00000205,0x00000001,0x00070050,0x00000080,
0x00000210,0x0000020e,0x0000020f,0x0000020d,0x00000017,0x00040063,0x00000203,0x00000204,
0x00000210,0x0004003d,0x00000007,0x00000211,0x0000017c,0x0004003d,0x00000007,0x00000212,
0x0000017c,0x00050094,0x00000006,0x00000213,0x00000211,0x00000212,0x000500ba,0x00000018,
0x00000214,0x00000213,0x0000019c,0x000300f7,0x00000217,0x00000000,0x000400fa,0x00000214,
0x00000216,0x0000021a,0x000200f8,0x00000216,0x0004003d,0x00000007,0x00000218,0x0000017c,
0x0006000c,0x00000007,0x00000219,0x00000001,0x00000045,0x00000218,0x0003003e,0x00000215,
0x00000219,0x000200f9,0x00000217,0x000200f8,0x0000021a,0x0003003e,0x00000215,0x0000021b,
0x000200f9,0x00000217,0x000200f8,0x00000217,0x0004003d,0x00000007,0x0000021c,0x00000215,
0x0003003e,0x0000017c,0x0000021c,0x0004003d,0x00000012,0x0000021e,0x0000009a,0x000500c7,
0x00000012,0x0000021f,0x0000021e,0x0000002f,0x000500ab,0x00000018,0x00000220,0x0000021f,
0x00000013,0x000300f7,0x00000223,0x00000000,0x000400fa,0x00000220,0x00000222,0x00000226,
0x000200f8,0x00000222,0x00050041,0x00000014,0x00000224,0x00000176,0x000000d9,0x0004003d,
0x00000006,0x00000225,0x00000224,0x0003003e,0x00000221,0x00000225,0x000200f9,0x00000223,
0x000200f8,0x00000226,0x0004003d,0x000000b6,0x00000228,0x00000227,0x0004003d,0x00000073,
0x00000229,0x00000075,0x00040064,0x000000b5,0x0000022a,0x00000228,0x0007005f,0x00000080,
0x0000022b,0x0000022a,0x00000229,0x00000002,0x000000c2,0x00050051,0x00000006,0x0000022c,
0x0000022b,0x00000000,0x0003003e,0x00000221,0x0000022c,0x000200f9,0x00000223,0x000200f8,
0x00000223,0x0004003d,0x00000006,0x0000022d,0x00000221,0x0008000c,0x00000006,0x0000022e,
0x00000001,0x0000002b,0x0000022d,0x00000017,0x00000051,0x0003003e,0x0000021d,0x0000022e,
0x0004003d,0x000001bc,0x0000022f,0x000001c2,0x0004003d,0x00000073,0x00000230,0x00000075,
0x0004003d,0x00000007,0x00000232,0x0000017c,0x0003003e,0x00000231,0x00000232,0x00050039,
0x0000000d,0x00000233,0x00000010,0x00000231,0x0004003d,0x00000006,0x00000234,0x0000021d,
0x00050051,0x00000006,0x00000235,0x00000233,0x00000000,0x00050051,0x00000006,0x00000236,
0x00000233,0x00000001,0x00070050,0x00000080,0x00000237,0x00000235,0x00000236,0x00000234,
0x00000017,0x00040063,0x0000022f,0x00000230,0x00000237,0x0004003d,0x000001bc,0x00000238,
0x000001c6,0x0004003d,0x00000073,0x00000239,0x00000075,0x0004003d,0x00000007,0x0000023a,
0x00000139,0x00050051,0x00000006,0x0000023b,0x0000023a,0x00000000,0x00050051,0x00000006,
0x0000023c,0x0000023a,0x00000001,0x00050051,0x00000006,0x0000023d,0x0000023a,0x00000002,
0x00070050,0x00000080,0x0000023e,0x0000023b,0x0000023c,0x0000023d,0x00000017,0x00040063,
0x00000238,0x00000239,0x0000023e,0x0004003d,0x000001bc,0x0000023f,0x000001c9,0x0004003d,
0x00000073,0x00000240,0x00000075,0x0004003d,0x00000007,0x00000241,0x00000143,0x00050051,
0x00000006,0x00000242,0x00000241,0x00000000,0x00050051,0x00000006,0x00000243,0x00000241,
0x00000001,0x00050051,0x00000006,0x00000244,0x00000241,0x00000002,0x00070050,0x00000080,
0x00000245,0x00000242,0x00000243,0x00000244,0x00000017,0x00040063,0x0000023f,0x00000240,
0x00000245,0x0004003d,0x00000007,0x00000247,0x00000139,0x0004003d,0x00000007,0x00000248,
0x00000143,0x00050081,0x00000007,0x00000249,0x00000247,0x00000248,0x0003003e,0x00000246,
0x00000249,0x0004003d,0x00000007,0x0000024b,0x00000143,0x0004003d,0x00000007,0x0000024c,
0x00000246,0x0007000c,0x00000007,0x0000024f,0x00000001,0x00000028,0x0000024c,0x0000024e,
0x00050088,0x00000007,0x00000250,0x0000024b,0x0000024f,0x0003003e,0x0000024a,0x00000250,
0x0004003d,0x00000007,0x00000252,0x00000122,0x0004003d,0x00000007,0x00000253,0x0000024a,
0x00050085,0x00000007,0x00000254,0x00000252,0x00000253,0x0003003e,0x00000251,0x00000254,
0x0004003d,0x00000007,0x00000256,0x00000122,0x0004003d,0x00000007,0x00000257,0x00000251,
0x00050083,0x00000007,0x00000258,0x00000256,0x00000257,0x0003003e,0x00000255,0x00000258,
0x0004003d,0x00000007,0x0000025a,0x00000255,0x0004003d,0x00000007,0x0000025b,0x00000139,
0x00060041,0x00000087,0x0000025d,0x00000085,0x0000025c,0x00000023,0x0004003d,0x00000006,
0x0000025e,0x0000025d,0x00060050,0x00000007,0x0000025f,0x0000025e,0x0000025e,0x0000025e,
0x0007000c,0x00000007,0x00000260,0x00000001,0x00000028,0x0000025b,0x0000025f,0x00050088,
0x00000007,0x00000261,0x0000025a,0x00000260,0x0003003e,0x00000262,0x00000261,0x00050039,
0x00000007,0x00000263,0x0000000b,0x00000262,0x0003003e,0x00000259,0x00000263,0x0004003d,
0x00000007,0x00000265,0x00000251,0x0004003d,0x00000007,0x00000266,0x00000143,0x00060041,
0x00000087,0x00000267,0x00000085,0x0000025c,0x00000023,0x0004003d,0x00000006,0x00000268,
0x00000267,0x00060050,0x00000007,0x00000269,0x00000268,0x00000268,0x00000268,0x0007000c,
0x00000007,0x0000026a,0x00000001,0x00000028,0x00000266,0x00000269,0x00050088,0x00000007,
0x0000026b,0x00000265,0x0000026a,0x0003003e,0x0000026c,0x0000026b,0x00050039,0x00000007,
0x0000026d,0x0000000b,0x0000026c,0x0003003e,0x00000264,0x0000026d,0x00060041,0x00000087,
0x0000026e,0x00000085,0x0000025c,0x00000013,0x0004003d,0x00000006,0x0000026f,0x0000026e,
0x000500ba,0x00000018,0x00000270,0x0000026f,0x00000017,0x000300f7,0x00000272,0x00000000,
0x000400fa,0x00000270,0x00000271,0x00000272,0x000200f8,0x00000271,0x00060041,0x00000087,
0x00000274,0x00000085,0x0000025c,0x00000013,0x0004003d,0x00000006,0x00000275,0x00000274,
0x0008000c,0x00000006,0x00000276,0x00000001,0x0000002b,0x00000275,0x00000017,0x00000051,
0x00050083,0x00000006,0x00000277,0x00000051,0x00000276,0x0003003e,0x00000273,0x00000277,
0x0004003d,0x00000006,0x00000278,0x00000273,0x0004003d,0x00000007,0x00000279,0x00000259,
0x0005008e,0x00000007,0x0000027a,0x00000279,0x00000278,0x0003003e,0x00000259,0x0000027a,
0x0004003d,0x00000006,0x0000027b,0x00000273,0x0004003d,0x00000007,0x0000027c,0x00000264,
0x0005008e,0x00000007,0x0000027d,0x0000027c,0x0000027b,0x0003003e,0x00000264,0x0000027d,
0x000200f9,0x00000272,0x000200f8,0x00000272,0x0004003d,0x00000007,0x0000027f,0x00000259,
0x0004003d,0x00000007,0x00000280,0x00000139,0x00050085,0x00000007,0x00000281,0x0000027f,
0x00000280,0x0004003d,0x00000007,0x00000282,0x00000264,0x0004003d,0x00000007,0x00000283,
0x00000143,0x00050085,0x00000007,0x00000284,0x00000282,0x00000283,0x00050081,0x00000007,
0x00000285,0x00000281,0x00000284,0x0003003e,0x0000027e,0x00000285,0x0003003e,0x00000286,
0x00000017,0x0003003e,0x00000287,0x00000017,0x0004003d,0x00000012,0x00000288,0x0000009a,
0x000500c7,0x00000012,0x0000028a,0x00000288,0x00000289,0x000500ab,0x00000018,0x0000028b,
0x0000028a,0x00000013,0x000300f7,0x0000028d,0x00000000,0x000400fa,0x0000028b,0x0000028c,
0x0000028d,0x000200f8,0x0000028c,0x0004003d,0x000000b6,0x00000290,0x0000028f,0x0004003d,
0x00000073,0x00000291,0x00000075,0x00040064,0x000000b5,0x00000292,0x00000290,0x0007005f,
0x00000080,0x00000293,0x00000292,0x00000291,0x00000002,0x000000c2,0x0003003e,0x0000028e,
0x00000293,0x0004003d,0x00000012,0x00000294,0x0000009a,0x000500c7,0x00000012,0x00000296,
0x00000294,0x00000295,0x000500ab,0x00000018,0x00000297,0x00000296,0x00000013,0x000300f7,
0x0000029a,0x00000000,0x000400fa,0x00000297,0x00000299,0x0000029d,0x000200f8,0x00000299,
0x00050041,0x00000014,0x0000029b,0x0000028e,0x000000d9,0x0004003d,0x00000006,0x0000029c,
0x0000029b,0x0003003e,0x00000298,0x0000029c,0x000200f9,0x0000029a,0x000200f8,0x0000029d,
0x00050041,0x00000014,0x0000029e,0x0000028e,0x00000013,0x0004003d,0x00000006,0x0000029f,
0x0000029e,0x0003003e,0x00000298,0x0000029f,0x000200f9,0x0000029a,0x000200f8,0x0000029a,
0x0004003d,0x00000006,0x000002a0,0x00000298,0x0008000c,0x00000006,0x000002a1,0x00000001,
0x0000002b,0x000002a0,0x00000017,0x0000001f,0x0003003e,0x00000286,0x000002a1,0x000200f9,
0x0000028d,0x000200f8,0x0000028d,0x0004003d,0x00000012,0x000002a2,0x0000009a,0x000500c7,
0x00000012,0x000002a4,0x000002a2,0x000002a3,0x000500ab,0x00000018,0x000002a5,0x000002a4,
0x00000013,0x000300f7,0x000002a7,0x00000000,0x000400fa,0x000002a5,0x000002a6,0x000002a7,
0x000200f8,0x000002a6,0x0004003d,0x000000b6,0x000002aa,0x000002a9,0x0004003d,0x00000073,
0x000002ab,0x00000075,0x00040064,0x000000b5,0x000002ac,0x000002aa,0x0007005f,0x00000080,
0x000002ad,0x000002ac,0x000002ab,0x00000002,0x000000c2,0x0003003e,0x000002a8,0x000002ad,
0x0004003d,0x00000012,0x000002ae,0x0000009a,0x000500c7,0x00000012,0x000002b0,0x000002ae,
0x000002af,0x000500ab,0x00000018,0x000002b1,0x000002b0,0x00000013,0x000300f7,0x000002b4,
0x00000000,0x000400fa,0x000002b1,0x000002b3,0x000002b7,0x000200f8,0x000002b3,0x00050041,
0x00000014,0x000002b5,0x000002a8,0x000000d9,0x0004003d,0x00000006,0x000002b6,0x000002b5,
0x0003003e,0x000002b2,0x000002b6,0x000200f9,0x000002b4,0x000200f8,0x000002b7,0x00050041,
0x00000014,0x000002b8,0x000002a8,0x00000013,0x0004003d,0x00000006,0x000002b9,0x000002b8,
0x0003003e,0x000002b2,0x000002b9,0x000200f9,0x000002b4,0x000200f8,0x000002b4,0x0004003d,
0x00000006,0x000002ba,0x000002b2,0x0008000c,0x00000006,0x000002bb,0x00000001,0x0000002b,
0x000002ba,0x00000017,0x0000001f,0x0003003e,0x00000287,0x000002bb,0x000200f9,0x000002a7,
0x000200f8,0x000002a7,0x0004003d,0x000001bc,0x000002bc,0x000001cc,0x0004003d,0x00000073,
0x000002bd,0x00000075,0x0004003d,0x00000007,0x000002be,0x00000259,0x0004003d,0x00000006,
0x000002bf,0x00000287,0x00050051,0x00000006,0x000002c0,0x000002be,0x00000000,0x00050051,
0x00000006,0x000002c1,0x000002be,0x00000001,0x00050051,0x00000006,0x000002c2,0x000002be,
0x00000002,0x00070050,0x00000080,0x000002c3,0x000002c0,0x000002c1,0x000002c2,0x000002bf,
0x00040063,0x000002bc,0x000002bd,0x000002c3,0x0004003d,0x000001bc,0x000002c4,0x000001cf,
0x0004003d,0x00000073,0x000002c5,0x00000075,0x0004003d,0x00000007,0x000002c6,0x00000264,
0x0004003d,0x00000006,0x000002c7,0x00000286,0x00050051,0x00000006,0x000002c8,0x000002c6,
0x00000000,0x00050051,0x00000006,0x000002c9,0x000002c6,0x00000001,0x00050051,0x00000006,
0x000002ca,0x000002c6,0x00000002,0x00070050,0x00000080,0x000002cb,0x000002c8,0x000002c9,
0x000002ca,0x000002c7,0x00040063,0x000002c4,0x000002c5,0x000002cb,0x0004003d,0x000001bc,
0x000002cc,0x000001d2,0x0004003d,0x00000073,0x000002cd,0x00000075,0x0004003d,0x00000007,
0x000002ce,0x00000122,0x0004003d,0x00000007,0x000002cf,0x0000027e,0x00050083,0x00000007,
0x000002d0,0x000002ce,0x000002cf,0x0004003d,0x00000007,0x000002d1,0x00000135,0x00050081,
0x00000007,0x000002d2,0x000002d0,0x000002d1,0x00050051,0x00000006,0x000002d3,0x000002d2,
0x00000000,0x00050051,0x00000006,0x000002d4,0x000002d2,0x00000001,0x00050051,0x00000006,
0x000002d5,0x000002d2,0x00000002,0x00070050,0x00000080,0x000002d6,0x000002d3,0x000002d4,
0x000002d5,0x00000017,0x00040063,0x000002cc,0x000002cd,0x000002d6,0x000100fd,0x00010038,
0x00050036,0x00000007,0x0000000b,0x00000000,0x00000009,0x00030037,0x00000008,0x0000000a,
0x000200f8,0x0000000c,0x0004003b,0x00000014,0x0000001a,0x00000007,0x0004003b,0x00000014,
0x00000027,0x00000007,0x0004003b,0x00000014,0x00000033,0x00000007,0x00050041,0x00000014,
0x00000015,0x0000000a,0x00000013,0x0004003d,0x00000006,0x00000016,0x00000015,0x000500be,
0x00000018,0x00000019,0x00000016,0x00000017,0x000300f7,0x0000001c,0x00000000,0x000400fa,
0x00000019,0x0000001b,0x00000021,0x000200f8,0x0000001b,0x00050041,0x00000014,0x0000001d,
0x0000000a,0x00000013,0x0004003d,0x00000006,0x0000001e,0x0000001d,0x0007000c,0x00000006,
0x00000020,0x00000001,0x00000025,0x0000001e,0x0000001f,0x0003003e,0x0000001a,0x00000020,
0x000200f9,0x0000001c,0x000200f8,0x00000021,0x0003003e,0x0000001a,0x00000017,0x000200f9,
0x0000001c,0x000200f8,0x0000001c,0x0004003d,0x00000006,0x00000022,0x0000001a,0x00050041,
0x00000014,0x00000024,0x0000000a,0x00000023,0x0004003d,0x00000006,0x00000025,0x00000024,
0x000500be,0x00000018,0x00000026,0x00000025,0x00000017,0x000300f7,0x00000029,0x00000000,
0x000400fa,0x00000026,0x00000028,0x0000002d,0x000200f8,0x00000028,0x00050041,0x00000014,
0x0000002a,0x0000000a,0x00000023,0x0004003d,0x00000006,0x0000002b,0x0000002a,0x0007000c,
0x00000006,0x0000002c,0x00000001,0x00000025,0x0000002b,0x0000001f,0x0003003e,0x00000027,
0x0000002c,0x000200f9,0x00000029,0x000200f8,0x0000002d,0x0003003e,0x00000027,0x00000017,
0x000200f9,0x00000029,0x000200f8,0x00000029,0x0004003d,0x00000006,0x0000002e,0x00000027,
0x00050041,0x00000014,0x00000030,0x0000000a,0x0000002f,0x0004003d,0x00000006,0x00000031,
0x00000030,0x000500be,0x00000018,0x00000032,0x00000031,0x00000017,0x000300f7,0x00000035,
0x00000000,0x000400fa,0x00000032,0x00000034,0x00000039,0x000200f8,0x00000034,0x00050041,
0x00000014,0x00000036,0x0000000a,0x0000002f,0x0004003d,0x00000006,0x00000037,0x00000036,
0x0007000c,0x00000006,0x00000038,0x00000001,0x00000025,0x00000037,0x0000001f,0x0003003e,
0x00000033,0x00000038,0x000200f9,0x00000035,0x000200f8,0x00000039,0x0003003e,0x00000033,
0x00000017,0x000200f9,0x00000035,0x000200f8,0x00000035,0x0004003d,0x00000006,0x0000003a,
0x00000033,0x00060050,0x00000007,0x0000003b,0x00000022,0x0000002e,0x0000003a,0x000200fe,
0x0000003b,0x00010038,0x00050036,0x0000000d,0x00000010,0x00000000,0x0000000e,0x00030037,
0x00000008,0x0000000f,0x000200f8,0x00000011,0x0004003b,0x0000004c,0x0000004d,0x00000007,
0x0004003b,0x0000004c,0x00000059,0x00000007,0x0004003b,0x0000004c,0x0000005d,0x00000007,
0x00050041,0x00000014,0x0000003e,0x0000000f,0x00000013,0x0004003d,0x00000006,0x0000003f,
0x0000003e,0x0006000c,0x00000006,0x00000040,0x00000001,0x00000004,0x0000003f,0x00050041,
0x00000014,0x00000041,0x0000000f,0x00000023,0x0004003d,0x00000006,0x00000042,0x00000041,
0x0006000c,0x00000006,0x00000043,0x00000001,0x00000004,0x00000042,0x00050081,0x00000006,
0x00000044,0x00000040,0x00000043,0x00050041,0x00000014,0x00000045,0x0000000f,0x0000002f,
0x0004003d,0x00000006,0x00000046,0x00000045,0x0006000c,0x00000006,0x00000047,0x00000001,
0x00000004,0x00000046,0x00050081,0x00000006,0x00000048,0x00000044,0x00000047,0x0004003d,
0x00000007,0x00000049,0x0000000f,0x00060050,0x00000007,0x0000004a,0x00000048,0x00000048,
0x00000048,0x00050088,0x00000007,0x0000004b,0x00000049,0x0000004a,0x0003003e,0x0000000f,
0x0000004b,0x00050041,0x00000014,0x0000004e,0x0000000f,0x00000013,0x0004003d,0x00000006,
0x0000004f,0x0000004e,0x000500be,0x00000018,0x00000050,0x0000004f,0x00000017,0x000600a9,
0x00000006,0x00000053,0x00000050,0x00000051,0x00000052,0x00050041,0x00000014,0x00000054,
0x0000000f,0x00000023,0x0004003d,0x00000006,0x00000055,0x00000054,0x000500be,0x00000018,
0x00000056,0x00000055,0x00000017,0x000600a9,0x00000006,0x00000057,0x00000056,0x00000051,
0x00000052,0x00050050,0x0000000d,0x00000058,0x00000053,0x00000057,0x0003003e,0x0000004d,
0x00000058,0x00050041,0x00000014,0x0000005a,0x0000000f,0x0000002f,0x0004003d,0x00000006,
0x0000005b,0x0000005a,0x000500b8,0x00000018,0x0000005c,0x0000005b,0x00000017,0x000300f7,
0x0000005f,0x00000000,0x000400fa,0x0000005c,0x0000005e,0x00000067,0x000200f8,0x0000005e,
0x0004003d,0x00000007,0x00000060,0x0000000f,0x0007004f,0x0000000d,0x00000061,0x00000060,
0x00000060,0x00000001,0x00000000,0x0006000c,0x0000000d,0x00000062,0x00000001,0x00000004,
0x00000061,0x00050050,0x0000000d,0x00000063,0x00000051,0x00000051,0x00050083,0x0000000d,
0x00000064,0x00000063,0x00000062,0x0004003d,0x0000000d,0x00000065,0x0000004d,0x00050085,
0x0000000d,0x00000066,0x00000064,0x00000065,0x0003003e,0x0000005d,0x00000066,0x000200f9,
0x0000005f,0x000200f8,0x00000067,0x0004003d,0x00000007,0x00000068,0x0000000f,0x0007004f,
0x0000000d,0x00000069,0x00000068,0x00000068,0x00000000,0x00000001,0x0003003e,0x0000005d,
0x00000069,0x000200f9,0x0000005f,0x000200f8,0x0000005f,0x0004003d,0x0000000d,0x0000006a,
0x0000005d,0x0003003e,0x00000059,0x0000006a,0x0004003d,0x0000000d,0x0000006b,0x00000059,
0x0005008e,0x0000000d,0x0000006d,0x0000006b,0x0000006c,0x00050050,0x0000000d,0x0000006e,
0x0000006c,0x0000006c,0x00050081,0x0000000d,0x0000006f,0x0000006d,0x0000006e,0x000200fe,
0x0000006f,0x00010038,
};
static const uint32_t d4r_vk_fsr_rr_compose[] = {
0x07230203,0x00010000,0x0008000b,0x000000db,0x00000000,0x00020011,0x00000001,0x0006000b,
0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001,
0x0006000f,0x00000005,0x00000004,0x6e69616d,0x00000000,0x0000000d,0x00060010,0x00000004,
0x00000011,0x00000008,0x00000008,0x00000001,0x00030003,0x00000002,0x000001c2,0x00040005,
0x00000004,0x6e69616d,0x00000000,0x00030005,0x00000009,0x00007870,0x00080005,0x0000000d,
0x475f6c67,0x61626f6c,0x766e496c,0x7461636f,0x496e6f69,0x00000044,0x00050005,0x00000018,
0x736e6f43,0x746e6174,0x00000073,0x00060006,0x00000018,0x00000000,0x646e6572,0x69537265,
0x0000657a,0x00060006,0x00000018,0x00000001,0x6f6c6168,0x696d694c,0x00000074,0x00050006,
0x00000018,0x00000002,0x73756e75,0x00006465,0x00030005,0x0000001a,0x00006370,0x00030005,
0x00000031,0x00006164,0x00060005,0x00000034,0x66666964,0x41657375,0x6465626c,0x0000006f,
0x00030005,0x0000003a,0x00006173,0x00060005,0x0000003b,0x63657073,0x72616c75,0x65626c41,
0x00006f64,0x00040005,0x00000040,0x66666964,0x00657375,0x00060005,0x00000041,0x6f6e6564,
0x64657369,0x66666944,0x00657375,0x00050005,0x00000048,0x63657073,0x72616c75,0x00000000,
0x00070005,0x00000049,0x6f6e6564,0x64657369,0x63657053,0x72616c75,0x00000000,0x00050005,
0x00000059,0x66666964,0x4d657375,0x00007861,0x00050005,0x0000005a,0x63657073,0x72616c75,
0x0078614d,0x00030005,0x0000005b,0x00007964,0x00030005,0x00000064,0x00007864,0x00030005,
0x0000006c,0x00000071,0x00060005,0x0000007b,0x73696f6e,0x66694479,0x65737566,0x00000000,
0x00060005,0x0000008c,0x73696f6e,0x65705379,0x616c7563,0x00000072,0x00030005,0x0000009c,
0x00006c64,0x00030005,0x0000009f,0x00006c73,0x00040005,0x000000c2,0x6f6c6f63,0x00000072,
0x00050005,0x000000c6,0x69736572,0x6c617564,0x00000000,0x00050005,0x000000cc,0x706d6f63,
0x6465736f,0x00000000,0x00040047,0x0000000d,0x0000000b,0x0000001c,0x00030047,0x00000018,
0x00000002,0x00050048,0x00000018,0x00000000,0x00000023,0x00000000,0x00050048,0x00000018,
0x00000001,0x00000023,0x00000008,0x00050048,0x00000018,0x00000002,0x00000023,0x0000000c,
0x00030047,0x00000034,0x00000018,0x00040047,0x00000034,0x00000021,0x00000002,0x00040047,
0x00000034,0x00000022,0x00000000,0x00030047,0x0000003b,0x00000018,0x00040047,0x0000003b,
0x00000021,0x00000003,0x00040047,0x0000003b,0x00000022,0x00000000,0x00030047,0x00000041,
0x00000018,0x00040047,0x00000041,0x00000021,0x00000000,0x00040047,0x00000041,0x00000022,
0x00000000,0x00030047,0x00000049,0x00000018,0x00040047,0x00000049,0x00000021,0x00000001,
0x00040047,0x00000049,0x00000022,0x00000000,0x00030047,0x0000007b,0x00000018,0x00040047,
0x0000007b,0x00000021,0x00000006,0x00040047,0x0000007b,0x00000022,0x00000000,0x00030047,
0x0000008c,0x00000018,0x00040047,0x0000008c,0x00000021,0x00000007,0x00040047,0x0000008c,
0x00000022,0x00000000,0x00030047,0x000000c6,0x00000018,0x00040047,0x000000c6,0x00000021,
0x00000004,0x00040047,0x000000c6,0x00000022,0x00000000,0x00030047,0x000000cc,0x00000019,
0x00040047,0x000000cc,0x00000021,0x00000005,0x00040047,0x000000cc,0x00000022,0x00000000,
0x00040047,0x000000da,0x0000000b,0x00000019,0x00020013,0x00000002,0x00030021,0x00000003,
0x00000002,0x00040015,0x00000006,0x00000020,0x00000001,0x00040017,0x00000007,0x00000006,
0x00000002,0x00040020,0x00000008,0x00000007,0x00000007,0x00040015,0x0000000a,0x00000020,
0x00000000,0x00040017,0x0000000b,0x0000000a,0x00000003,0x00040020,0x0000000c,0x00000001,
0x0000000b,0x0004003b,0x0000000c,0x0000000d,0x00000001,0x00040017,0x0000000e,0x0000000a,
0x00000002,0x00020014,0x00000012,0x0004002b,0x0000000a,0x00000013,0x00000000,0x00040020,
0x00000014,0x00000007,0x00000006,0x00030016,0x00000017,0x00000020,0x0005001e,0x00000018,
0x0000000e,0x00000017,0x0000000a,0x00040020,0x00000019,0x00000009,0x00000018,0x0004003b,
0x00000019,0x0000001a,0x00000009,0x0004002b,0x00000006,0x0000001b,0x00000000,0x00040020,
0x0000001c,0x00000009,0x0000000a,0x0004002b,0x0000000a,0x00000024,0x00000001,0x00040017,
0x0000002f,0x00000017,0x00000003,0x00040020,0x00000030,0x00000007,0x0000002f,0x00090019,
0x00000032,0x00000017,0x00000001,0x00000000,0x00000000,0x00000000,0x00000002,0x00000002,
0x00040020,0x00000033,0x00000000,0x00000032,0x0004003b,0x00000033,0x00000034,0x00000000,
0x00040017,0x00000037,0x00000017,0x00000004,0x0004003b,0x00000033,0x0000003b,0x00000000,
0x0004003b,0x00000033,0x00000041,0x00000000,0x0004003b,0x00000033,0x00000049,0x00000000,
0x0004002b,0x00000006,0x00000050,0x00000001,0x00040020,0x00000051,0x00000009,0x00000017,
0x0004002b,0x00000017,0x00000054,0x00000000,0x00040020,0x00000058,0x00000007,0x00000017,
0x0004002b,0x00000006,0x0000005c,0xffffffff,0x0005002c,0x00000007,0x00000072,0x0000001b,
0x0000001b,0x00040020,0x00000073,0x00000009,0x0000000e,0x0004003b,0x00000033,0x0000007b,
0x00000000,0x0004002b,0x00000017,0x00000085,0x3e59b3d0,0x0004002b,0x00000017,0x00000086,
0x3f371759,0x0004002b,0x00000017,0x00000087,0x3d93dd98,0x0006002c,0x0000002f,0x00000088,
0x00000085,0x00000086,0x00000087,0x0004003b,0x00000033,0x0000008c,0x00000000,0x0004003b,
0x00000033,0x000000c6,0x00000000,0x0004003b,0x00000033,0x000000cc,0x00000000,0x0004002b,
0x00000017,0x000000d0,0x477de800,0x0004002b,0x00000017,0x000000d4,0x3f800000,0x0004002b,
0x0000000a,0x000000d9,0x00000008,0x0006002c,0x0000000b,0x000000da,0x000000d9,0x000000d9,
0x00000024,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,
0x0004003b,0x00000008,0x00000009,0x00000007,0x0004003b,0x00000030,0x00000031,0x00000007,
0x0004003b,0x00000030,0x0000003a,0x00000007,0x0004003b,0x00000030,0x00000040,0x00000007,
0x0004003b,0x00000030,0x00000048,0x00000007,0x0004003b,0x00000058,0x00000059,0x00000007,
0x0004003b,0x00000058,0x0000005a,0x00000007,0x0004003b,0x00000014,0x0000005b,0x00000007,
0x0004003b,0x00000014,0x00000064,0x00000007,0x0004003b,0x00000008,0x0000006c,0x00000007,
0x0004003b,0x00000058,0x0000009c,0x00000007,0x0004003b,0x00000058,0x0000009f,0x00000007,
0x0004003b,0x00000030,0x000000c2,0x00000007,0x0004003d,0x0000000b,0x0000000f,0x0000000d,
0x0007004f,0x0000000e,0x00000010,0x0000000f,0x0000000f,0x00000000,0x00000001,0x0004007c,
0x00000007,0x00000011,0x00000010,0x0003003e,0x00000009,0x00000011,0x00050041,0x00000014,
0x00000015,0x00000009,0x00000013,0x0004003d,0x00000006,0x00000016,0x00000015,0x00060041,
0x0000001c,0x0000001d,0x0000001a,0x0000001b,0x00000013,0x0004003d,0x0000000a,0x0000001e,
0x0000001d,0x0004007c,0x00000006,0x0000001f,0x0000001e,0x000500af,0x00000012,0x00000020,
0x00000016,0x0000001f,0x000400a8,0x00000012,0x00000021,0x00000020,0x000300f7,0x00000023,
0x00000000,0x000400fa,0x00000021,0x00000022,0x00000023,0x000200f8,0x00000022,0x00050041,
0x00000014,0x00000025,0x00000009,0x00000024,0x0004003d,0x00000006,0x00000026,0x00000025,
0x00060041,0x0000001c,0x00000027,0x0000001a,0x0000001b,0x00000024,0x0004003d,0x0000000a,
0x00000028,0x00000027,0x0004007c,0x00000006,0x00000029,0x00000028,0x000500af,0x00000012,
0x0000002a,0x00000026,0x00000029,0x000200f9,0x00000023,0x000200f8,0x00000023,0x000700f5,
0x00000012,0x0000002b,0x00000020,0x00000005,0x0000002a,0x00000022,0x000300f7,0x0000002d,
0x00000000,0x000400fa,0x0000002b,0x0000002c,0x0000002d,0x000200f8,0x0000002c,0x000100fd,
0x000200f8,0x0000002d,0x0004003d,0x00000032,0x00000035,0x00000034,0x0004003d,0x00000007,
0x00000036,0x00000009,0x00050062,0x00000037,0x00000038,0x00000035,0x00000036,0x0008004f,
0x0000002f,0x00000039,0x00000038,0x00000038,0x00000000,0x00000001,0x00000002,0x0003003e,
0x00000031,0x00000039,0x0004003d,0x00000032,0x0000003c,0x0000003b,0x0004003d,0x00000007,
0x0000003d,0x00000009,0x00050062,0x00000037,0x0000003e,0x0000003c,0x0000003d,0x0008004f,
0x0000002f,0x0000003f,0x0000003e,0x0000003e,0x00000000,0x00000001,0x00000002,0x0003003e,
0x0000003a,0x0000003f,0x0004003d,0x00000032,0x00000042,0x00000041,0x0004003d,0x00000007,
0x00000043,0x00000009,0x00050062,0x00000037,0x00000044,0x00000042,0x00000043,0x0008004f,
0x0000002f,0x00000045,0x00000044,0x00000044,0x00000000,0x00000001,0x00000002,0x0004003d,
0x0000002f,0x00000046,0x00000031,0x00050085,0x0000002f,0x00000047,0x00000045,0x00000046,
0x0003003e,0x00000040,0x00000047,0x0004003d,0x00000032,0x0000004a,0x00000049,0x0004003d,
0x00000007,0x0000004b,0x00000009,0x00050062,0x00000037,0x0000004c,0x0000004a,0x0000004b,
0x0008004f,0x0000002f,0x0000004d,0x0000004c,0x0000004c,0x00000000,0x00000001,0x00000002,
0x0004003d,0x0000002f,0x0000004e,0x0000003a,0x00050085,0x0000002f,0x0000004f,0x0000004d,
0x0000004e,0x0003003e,0x00000048,0x0000004f,0x00050041,0x00000051,0x00000052,0x0000001a,
0x00000050,0x0004003d,0x00000017,0x00000053,0x00000052,0x000500ba,0x00000012,0x00000055,
0x00000053,0x00000054,0x000300f7,0x00000057,0x00000000,0x000400fa,0x00000055,0x00000056,
0x00000057,0x000200f8,0x00000056,0x0003003e,0x00000059,0x00000054,0x0003003e,0x0000005a,
0x00000054,0x0003003e,0x0000005b,0x0000005c,0x000200f9,0x0000005d,0x000200f8,0x0000005d,
0x000400f6,0x0000005f,0x00000060,0x00000000,0x000200f9,0x00000061,0x000200f8,0x00000061,
0x0004003d,0x00000006,0x00000062,0x0000005b,0x000500b3,0x00000012,0x00000063,0x00000062,
0x00000050,0x000400fa,0x00000063,0x0000005e,0x0000005f,0x000200f8,0x0000005e,0x0003003e,
0x00000064,0x0000005c,0x000200f9,0x00000065,0x000200f8,0x00000065,0x000400f6,0x00000067,
0x00000068,0x00000000,0x000200f9,0x00000069,0x000200f8,0x00000069,0x0004003d,0x00000006,
0x0000006a,0x00000064,0x000500b3,0x00000012,0x0000006b,0x0000006a,0x00000050,0x000400fa,
0x0000006b,0x00000066,0x00000067,0x000200f8,0x00000066,0x0004003d,0x00000007,0x0000006d,
0x00000009,0x0004003d,0x00000006,0x0000006e,0x00000064,0x0004003d,0x00000006,0x0000006f,
0x0000005b,0x00050050,0x00000007,0x00000070,0x0000006e,0x0000006f,0x00050080,0x00000007,
0x00000071,0x0000006d,0x00000070,0x00050041,0x00000073,0x00000074,0x0000001a,0x0000001b,
0x0004003d,0x0000000e,0x00000075,0x00000074,0x0004007c,0x00000007,0x00000076,0x00000075,
0x00050050,0x00000007,0x00000077,0x00000050,0x00000050,0x00050082,0x00000007,0x00000078,
0x00000076,0x00000077,0x0008000c,0x00000007,0x00000079,0x00000001,0x0000002d,0x00000071,
0x00000072,0x00000078,0x0003003e,0x0000006c,0x00000079,0x0004003d,0x00000017,0x0000007a,
0x00000059,0x0004003d,0x00000032,0x0000007c,0x0000007b,0x0004003d,0x00000007,0x0000007d,
0x0000006c,0x00050062,0x00000037,0x0000007e,0x0000007c,0x0000007d,0x0008004f,0x0000002f,
0x0000007f,0x0000007e,0x0000007e,0x00000000,0x00000001,0x00000002,0x0004003d,0x00000032,
0x00000080,0x00000034,0x0004003d,0x00000007,0x00000081,0x0000006c,0x00050062,0x00000037,
0x00000082,0x00000080,0x00000081,0x0008004f,0x0000002f,0x00000083,0x00000082,0x00000082,
0x00000000,0x00000001,0x00000002,0x00050085,0x0000002f,0x00000084,0x0000007f,0x00000083,
0x00050094,0x00000017,0x00000089,0x00000084,0x00000088,0x0007000c,0x00000017,0x0000008a,
0x00000001,0x00000028,0x0000007a,0x00000089,0x0003003e,0x00000059,0x0000008a,0x0004003d,
0x00000017,0x0000008b,0x0000005a,0x0004003d,0x00000032,0x0000008d,0x0000008c,0x0004003d,
0x00000007,0x0000008e,0x0000006c,0x00050062,0x00000037,0x0000008f,0x0000008d,0x0000008e,
0x0008004f,0x0000002f,0x00000090,0x0000008f,0x0000008f,0x00000000,0x00000001,0x00000002,
0x0004003d,0x00000032,0x00000091,0x0000003b,0x0004003d,0x00000007,0x00000092,0x0000006c,
0x00050062,0x00000037,0x00000093,0x00000091,0x00000092,0x0008004f,0x0000002f,0x00000094,
0x00000093,0x00000093,0x00000000,0x00000001,0x00000002,0x00050085,0x0000002f,0x00000095,
0x00000090,0x00000094,0x00050094,0x00000017,0x00000096,0x00000095,0x00000088,0x0007000c,
0x00000017,0x00000097,0x00000001,0x00000028,0x0000008b,0x00000096,0x0003003e,0x0000005a,
0x00000097,0x000200f9,0x00000068,0x000200f8,0x00000068,0x0004003d,0x00000006,0x00000098,
0x00000064,0x00050080,0x00000006,0x00000099,0x00000098,0x00000050,0x0003003e,0x00000064,
0x00000099,0x000200f9,0x00000065,0x000200f8,0x00000067,0x000200f9,0x00000060,0x000200f8,
0x00000060,0x0004003d,0x00000006,0x0000009a,0x0000005b,0x00050080,0x00000006,0x0000009b,
0x0000009a,0x00000050,0x0003003e,0x0000005b,0x0000009b,0x000200f9,0x0000005d,0x000200f8,
0x0000005f,0x0004003d,0x0000002f,0x0000009d,0x00000040,0x00050094,0x00000017,0x0000009e,
0x0000009d,0x00000088,0x0003003e,0x0000009c,0x0000009e,0x0004003d,0x0000002f,0x000000a0,
0x00000048,0x00050094,0x00000017,0x000000a1,0x000000a0,0x00000088,0x0003003e,0x0000009f,
0x000000a1,0x0004003d,0x00000017,0x000000a2,0x0000009c,0x00050041,0x00000051,0x000000a3,
0x0000001a,0x00000050,0x0004003d,0x00000017,0x000000a4,0x000000a3,0x0004003d,0x00000017,
0x000000a5,0x00000059,0x00050085,0x00000017,0x000000a6,0x000000a4,0x000000a5,0x000500ba,
0x00000012,0x000000a7,0x000000a2,0x000000a6,0x000300f7,0x000000a9,0x00000000,0x000400fa,
0x000000a7,0x000000a8,0x000000a9,0x000200f8,0x000000a8,0x00050041,0x00000051,0x000000aa,
0x0000001a,0x00000050,0x0004003d,0x00000017,0x000000ab,0x000000aa,0x0004003d,0x00000017,
0x000000ac,0x00000059,0x00050085,0x00000017,0x000000ad,0x000000ab,0x000000ac,0x0004003d,
0x00000017,0x000000ae,0x0000009c,0x00050088,0x00000017,0x000000af,0x000000ad,0x000000ae,
0x0004003d,0x0000002f,0x000000b0,0x00000040,0x0005008e,0x0000002f,0x000000b1,0x000000b0,
0x000000af,0x0003003e,0x00000040,0x000000b1,0x000200f9,0x000000a9,0x000200f8,0x000000a9,
0x0004003d,0x00000017,0x000000b2,0x0000009f,0x00050041,0x00000051,0x000000b3,0x0000001a,
0x00000050,0x0004003d,0x00000017,0x000000b4,0x000000b3,0x0004003d,0x00000017,0x000000b5,
0x0000005a,0x00050085,0x00000017,0x000000b6,0x000000b4,0x000000b5,0x000500ba,0x00000012,
0x000000b7,0x000000b2,0x000000b6,0x000300f7,0x000000b9,0x00000000,0x000400fa,0x000000b7,
0x000000b8,0x000000b9,0x000200f8,0x000000b8,0x00050041,0x00000051,0x000000ba,0x0000001a,
0x00000050,0x0004003d,0x00000017,0x000000bb,0x000000ba,0x0004003d,0x00000017,0x000000bc,
0x0000005a,0x00050085,0x00000017,0x000000bd,0x000000bb,0x000000bc,0x0004003d,0x00000017,
0x000000be,0x0000009f,0x00050088,0x00000017,0x000000bf,0x000000bd,0x000000be,0x0004003d,
0x0000002f,0x000000c0,0x00000048,0x0005008e,0x0000002f,0x000000c1,0x000000c0,0x000000bf,
0x0003003e,0x00000048,0x000000c1,0x000200f9,0x000000b9,0x000200f8,0x000000b9,0x000200f9,
0x00000057,0x000200f8,0x00000057,0x0004003d,0x0000002f,0x000000c3,0x00000040,0x0004003d,
0x0000002f,0x000000c4,0x00000048,0x00050081,0x0000002f,0x000000c5,0x000000c3,0x000000c4,
0x0004003d,0x00000032,0x000000c7,0x000000c6,0x0004003d,0x00000007,0x000000c8,0x00000009,
0x00050062,0x00000037,0x000000c9,0x000000c7,0x000000c8,0x0008004f,0x0000002f,0x000000ca,
0x000000c9,0x000000c9,0x00000000,0x00000001,0x00000002,0x00050081,0x0000002f,0x000000cb,
0x000000c5,0x000000ca,0x0003003e,0x000000c2,0x000000cb,0x0004003d,0x00000032,0x000000cd,
0x000000cc,0x0004003d,0x00000007,0x000000ce,0x00000009,0x0004003d,0x0000002f,0x000000cf,
0x000000c2,0x00060050,0x0000002f,0x000000d1,0x00000054,0x00000054,0x00000054,0x00060050,
0x0000002f,0x000000d2,0x000000d0,0x000000d0,0x000000d0,0x0008000c,0x0000002f,0x000000d3,
0x00000001,0x0000002b,0x000000cf,0x000000d1,0x000000d2,0x00050051,0x00000017,0x000000d5,
0x000000d3,0x00000000,0x00050051,0x00000017,0x000000d6,0x000000d3,0x00000001,0x00050051,
0x00000017,0x000000d7,0x000000d3,0x00000002,0x00070050,0x00000037,0x000000d8,0x000000d5,
0x000000d6,0x000000d7,0x000000d4,0x00040063,0x000000cd,0x000000ce,0x000000d8,0x000100fd,
0x00010038,
};
+931
View File
@@ -0,0 +1,931 @@
// FSR Ray Regeneration as the native Windows Ray Reconstruction backend (experimental). See fsr_rr_backend.h.
//
// Data path of one evaluation (every hand-off is CPU-synchronized; no vkd3d-proton patch is needed):
// game queue : guides -> shared packet (D3D12 copies, as the native DLSS backend), signal, CPU waits
// vkd3d queue: one command list on a private vkd3d-proton device:
// packet -> staging images, fsr_rr_prepare (Vulkan, recorded through vkd3d's interop),
// AMD ffxDispatch, fsr_rr_compose (Vulkan), composed image -> packet output; CPU waits
// game queue : packet output -> the game's output texture, signal, CPU waits
// The conversion and composition follow the Linux adapter (reference 34e6b0fe, tools/d4r_fsr_rr_backend.inl).
#include "fsr_rr_backend.h"
#include "../d4r_fsr_rr.h"
#include "../d4r_vkd3d_interop.h"
#include "fsr_rr_passes.h"
#include "d3d12_command_hooks.h"
#include <volk.h>
#include "rr_bridge_replay.h"
#include <vulkan/vulkan_win32.h>
#include <dxgi1_4.h>
#include <chrono>
#include <cstdlib>
#include <mutex>
#include <tuple>
#include <vector>
#include <string>
namespace d4r::win::rr {
namespace {
using Clock = std::chrono::steady_clock;
double since(Clock::time_point start) { return std::chrono::duration<double, std::milli>(Clock::now() - start).count(); }
bool log_enabled() { static const bool on = std::getenv("D4R_FSR_RR_LOG") != nullptr; return on; }
LONG WINAPI crash_report(EXCEPTION_POINTERS* info) {
const auto code = info->ExceptionRecord->ExceptionCode;
if (code != 0xC0000005 && code != 0xC000001D && code != 0xC0000094 && code != 0xC00000FD) return EXCEPTION_CONTINUE_SEARCH;
char name[MAX_PATH] = "?"; HMODULE module = nullptr;
const void* at = info->ExceptionRecord->ExceptionAddress;
if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, static_cast<const char*>(at), &module))
GetModuleFileNameA(module, name, MAX_PATH);
std::fprintf(stderr, "FSR_RR crash: code=%08lx at %p (%s +0x%llx) info0=%llu info1=%p\n", code, at, name,
(unsigned long long)(static_cast<const char*>(at) - reinterpret_cast<const char*>(module)),
(unsigned long long)info->ExceptionRecord->ExceptionInformation[0], (void*)info->ExceptionRecord->ExceptionInformation[1]);
std::fflush(stderr);
return EXCEPTION_CONTINUE_SEARCH;
}
// The denoiser finds amdxc64.dll with GetModuleHandle, so in a process whose game device already loaded AMD's real
// amdxc64.dll (it always has: AMD's D3D12 driver does), the name resolves to AMD's driver component, which crashes
// on a vkd3d device. The loaded copy of AMD's DLL gets its GetModuleHandleA/W imports redirected in memory so that
// the name resolves to d4r's shim; every other lookup is forwarded. The DLL on disk is never modified.
HMODULE g_shimModule = nullptr;
HMODULE WINAPI module_handle_a(LPCSTR name) {
if (name && g_shimModule && !_stricmp(name, "amdxc64.dll")) return g_shimModule;
return GetModuleHandleA(name);
}
HMODULE WINAPI module_handle_w(LPCWSTR name) {
if (name && g_shimModule && !_wcsicmp(name, L"amdxc64.dll")) return g_shimModule;
return GetModuleHandleW(name);
}
bool redirect_amdxc(HMODULE module, HMODULE shim) {
g_shimModule = shim;
auto* base = reinterpret_cast<unsigned char*>(module);
auto* dos = reinterpret_cast<IMAGE_DOS_HEADER*>(base);
auto* nt = reinterpret_cast<IMAGE_NT_HEADERS*>(base + dos->e_lfanew);
const auto& directory = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
if (!directory.VirtualAddress) return false;
unsigned patched = 0;
for (auto* descriptor = reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR*>(base + directory.VirtualAddress); descriptor->Name; ++descriptor) {
if (_stricmp(reinterpret_cast<const char*>(base + descriptor->Name), "KERNEL32.dll") != 0) continue;
auto* lookup = reinterpret_cast<IMAGE_THUNK_DATA*>(base + (descriptor->OriginalFirstThunk ? descriptor->OriginalFirstThunk : descriptor->FirstThunk));
auto* address = reinterpret_cast<void**>(base + descriptor->FirstThunk);
for (; lookup->u1.AddressOfData; ++lookup, ++address) {
if (lookup->u1.Ordinal & IMAGE_ORDINAL_FLAG) continue;
const char* function = reinterpret_cast<IMAGE_IMPORT_BY_NAME*>(base + lookup->u1.AddressOfData)->Name;
void* replacement = !std::strcmp(function, "GetModuleHandleA") ? reinterpret_cast<void*>(&module_handle_a)
: !std::strcmp(function, "GetModuleHandleW") ? reinterpret_cast<void*>(&module_handle_w) : nullptr;
if (!replacement) continue;
DWORD old = 0;
if (!VirtualProtect(address, sizeof(void*), PAGE_READWRITE, &old)) return false;
*address = replacement; ++patched;
VirtualProtect(address, sizeof(void*), old, &old);
}
}
return patched != 0;
}
void trace(const char* what) { if (log_enabled()) { std::fprintf(stderr, "FSR_RR step: %s\n", what); std::fflush(stderr); } }
std::wstring widen(const char* text) {
if (!text || !*text) return {};
const int n = MultiByteToWideChar(CP_UTF8, 0, text, -1, nullptr, 0);
std::wstring out(size_t(n > 0 ? n - 1 : 0), L'\0');
if (n > 1) MultiByteToWideChar(CP_UTF8, 0, text, -1, out.data(), n);
return out;
}
std::wstring env_wide(const char* name) { return widen(std::getenv(name)); }
std::string narrow(const std::wstring& text) {
std::string out(size_t(WideCharToMultiByte(CP_UTF8, 0, text.c_str(), -1, nullptr, 0, nullptr, nullptr)), '\0');
if (!out.empty()) { WideCharToMultiByte(CP_UTF8, 0, text.c_str(), -1, out.data(), int(out.size()), nullptr, nullptr); out.pop_back(); }
return out;
}
// The vkd3d-proton core interface (include/vkd3d_core_interface.idl). The IDL declares no base interface, so the
// vtable starts at CreateDevice: slot 0, not 3.
const GUID CLSID_Core = {0xed53efad, 0xda21, 0x4d96, {0xa1, 0xbc, 0xe7, 0x34, 0xe0, 0x78, 0x87, 0x9c}};
const GUID IID_CoreInterface = {0x18da885c, 0xa7b5, 0x464e, {0xa1, 0x21, 0xcc, 0xb7, 0x5d, 0x4d, 0xfc, 0x31}};
struct CoreInterface {
struct Vtbl {
HRESULT(STDMETHODCALLTYPE* createDevice)(CoreInterface*, IUnknown*, D3D_FEATURE_LEVEL, REFIID, void**);
};
const Vtbl* vtbl;
};
// Row-major, row-vector 4x4 helpers (the NGX/FFX convention), in double. From the Linux adapter.
bool invert(const float (&m)[16], double (&out)[16]) {
double a[16]; for (int i = 0; i < 16; ++i) a[i] = m[i];
double inv[16];
inv[0] = a[5] * a[10] * a[15] - a[5] * a[11] * a[14] - a[9] * a[6] * a[15] + a[9] * a[7] * a[14] + a[13] * a[6] * a[11] - a[13] * a[7] * a[10];
inv[4] = -a[4] * a[10] * a[15] + a[4] * a[11] * a[14] + a[8] * a[6] * a[15] - a[8] * a[7] * a[14] - a[12] * a[6] * a[11] + a[12] * a[7] * a[10];
inv[8] = a[4] * a[9] * a[15] - a[4] * a[11] * a[13] - a[8] * a[5] * a[15] + a[8] * a[7] * a[13] + a[12] * a[5] * a[11] - a[12] * a[7] * a[9];
inv[12] = -a[4] * a[9] * a[14] + a[4] * a[10] * a[13] + a[8] * a[5] * a[14] - a[8] * a[6] * a[13] - a[12] * a[5] * a[10] + a[12] * a[6] * a[9];
inv[1] = -a[1] * a[10] * a[15] + a[1] * a[11] * a[14] + a[9] * a[2] * a[15] - a[9] * a[3] * a[14] - a[13] * a[2] * a[11] + a[13] * a[3] * a[10];
inv[5] = a[0] * a[10] * a[15] - a[0] * a[11] * a[14] - a[8] * a[2] * a[15] + a[8] * a[3] * a[14] + a[12] * a[2] * a[11] - a[12] * a[3] * a[10];
inv[9] = -a[0] * a[9] * a[15] + a[0] * a[11] * a[13] + a[8] * a[1] * a[15] - a[8] * a[3] * a[13] - a[12] * a[1] * a[11] + a[12] * a[3] * a[9];
inv[13] = a[0] * a[9] * a[14] - a[0] * a[10] * a[13] - a[8] * a[1] * a[14] + a[8] * a[2] * a[13] + a[12] * a[1] * a[10] - a[12] * a[2] * a[9];
inv[2] = a[1] * a[6] * a[15] - a[1] * a[7] * a[14] - a[5] * a[2] * a[15] + a[5] * a[3] * a[14] + a[13] * a[2] * a[7] - a[13] * a[3] * a[6];
inv[6] = -a[0] * a[6] * a[15] + a[0] * a[7] * a[14] + a[4] * a[2] * a[15] - a[4] * a[3] * a[14] - a[12] * a[2] * a[7] + a[12] * a[3] * a[6];
inv[10] = a[0] * a[5] * a[15] - a[0] * a[7] * a[13] - a[4] * a[1] * a[15] + a[4] * a[3] * a[13] + a[12] * a[1] * a[7] - a[12] * a[3] * a[5];
inv[14] = -a[0] * a[5] * a[14] + a[0] * a[6] * a[13] + a[4] * a[1] * a[14] - a[4] * a[2] * a[13] - a[12] * a[1] * a[6] + a[12] * a[2] * a[5];
inv[3] = -a[1] * a[6] * a[11] + a[1] * a[7] * a[10] + a[5] * a[2] * a[11] - a[5] * a[3] * a[10] - a[9] * a[2] * a[7] + a[9] * a[3] * a[6];
inv[7] = a[0] * a[6] * a[11] - a[0] * a[7] * a[10] - a[4] * a[2] * a[11] + a[4] * a[3] * a[10] + a[8] * a[2] * a[7] - a[8] * a[3] * a[6];
inv[11] = -a[0] * a[5] * a[11] + a[0] * a[7] * a[9] + a[4] * a[1] * a[11] - a[4] * a[3] * a[9] - a[8] * a[1] * a[7] + a[8] * a[3] * a[5];
inv[15] = a[0] * a[5] * a[10] - a[0] * a[6] * a[9] - a[4] * a[1] * a[10] + a[4] * a[2] * a[9] + a[8] * a[1] * a[6] - a[8] * a[2] * a[5];
const double det = a[0] * inv[0] + a[1] * inv[4] + a[2] * inv[8] + a[3] * inv[12];
if (!std::isfinite(det) || std::fabs(det) < 1e-30) return false;
for (int i = 0; i < 16; ++i) { out[i] = inv[i] / det; if (!std::isfinite(out[i])) return false; }
return true;
}
void multiply(const double (&a)[16], const double (&b)[16], double (&out)[16]) {
for (int r = 0; r < 4; ++r) for (int c = 0; c < 4; ++c) {
double sum = 0; for (int k = 0; k < 4; ++k) sum += a[r * 4 + k] * b[k * 4 + c]; out[r * 4 + c] = sum;
}
}
bool unorm(VkFormat format) {
return format == VK_FORMAT_R8G8B8A8_UNORM || format == VK_FORMAT_A2B10G10R10_UNORM_PACK32;
}
size_t resource_index(size_t plane) { return plane < 7 ? plane : plane + 2; }
// Same sampled-input formats as the native backend: the texel values reach Vulkan unchanged.
rr_bridge::Plane input_format(DXGI_FORMAT format, size_t plane) {
if (plane == Snapshot::Motion) {
if (format == DXGI_FORMAT_R16G16_FLOAT) return {nullptr, VK_FORMAT_R16G16_SFLOAT, 4};
if (format == DXGI_FORMAT_R32G32_FLOAT) return {nullptr, VK_FORMAT_R32G32_SFLOAT, 8};
} else if (plane == Snapshot::Depth || plane == Snapshot::HitDistance || plane == Snapshot::Disocclusion || plane == Snapshot::Subsurface) {
if (plane == Snapshot::Depth && (format == DXGI_FORMAT_R32_TYPELESS || format == DXGI_FORMAT_D32_FLOAT ||
format == DXGI_FORMAT_R32G8X24_TYPELESS || format == DXGI_FORMAT_D32_FLOAT_S8X24_UINT || format == DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS))
return {nullptr, VK_FORMAT_R32_SFLOAT, 4};
if (format == DXGI_FORMAT_R32_FLOAT) return {nullptr, VK_FORMAT_R32_SFLOAT, 4};
if (format == DXGI_FORMAT_R16_FLOAT) return {nullptr, VK_FORMAT_R16_SFLOAT, 2};
if (plane != Snapshot::Depth && format == DXGI_FORMAT_R8_UNORM) return {nullptr, VK_FORMAT_R8_UNORM, 1};
} else {
if (format == DXGI_FORMAT_R16G16B16A16_FLOAT) return {nullptr, VK_FORMAT_R16G16B16A16_SFLOAT, 8};
if (format == DXGI_FORMAT_R32G32B32A32_FLOAT) return {nullptr, VK_FORMAT_R32G32B32A32_SFLOAT, 16};
if (format == DXGI_FORMAT_R8G8B8A8_UNORM) return {nullptr, VK_FORMAT_R8G8B8A8_UNORM, 4};
if (format == DXGI_FORMAT_R10G10B10A2_UNORM) return {nullptr, VK_FORMAT_A2B10G10R10_UNORM_PACK32, 4};
if (plane == Snapshot::Roughness) {
if (format == DXGI_FORMAT_R16_FLOAT) return {nullptr, VK_FORMAT_R16_SFLOAT, 2};
if (format == DXGI_FORMAT_R32_FLOAT) return {nullptr, VK_FORMAT_R32_SFLOAT, 4};
if (format == DXGI_FORMAT_R8_UNORM) return {nullptr, VK_FORMAT_R8_UNORM, 1};
}
}
throw std::runtime_error("Unsupported FSR RR sampled input format");
}
DXGI_FORMAT copy_format(ID3D12Device* device, ID3D12Resource* texture, size_t plane) {
const auto desc = resource_desc(texture);
if (plane != Snapshot::Depth) return desc.Format;
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint{}; UINT rows = 0; UINT64 rowBytes = 0, bytes = 0;
device->GetCopyableFootprints(&desc, 0, 1, 0, &footprint, &rows, &rowBytes, &bytes);
const auto format = input_format(desc.Format, plane);
if (!bytes || bytes == UINT64_MAX || rows != desc.Height || rowBytes != desc.Width * format.bytes ||
footprint.Footprint.Width != desc.Width || footprint.Footprint.Height != desc.Height)
throw std::runtime_error("Unsupported FSR RR depth-plane footprint");
return footprint.Footprint.Format;
}
// The Vulkan entry points used on vkd3d-proton's VkDevice. Loaded from the loader by instance/device, never through
// volk's globals, which belong to the native backend's own devices.
struct VkApi {
PFN_vkGetDeviceProcAddr deviceProc = nullptr;
PFN_vkGetPhysicalDeviceMemoryProperties memoryProperties = nullptr;
PFN_vkGetPhysicalDeviceFormatProperties formatProperties = nullptr;
PFN_vkCreateBuffer createBuffer = nullptr; PFN_vkDestroyBuffer destroyBuffer = nullptr;
PFN_vkGetBufferMemoryRequirements bufferRequirements = nullptr;
PFN_vkAllocateMemory allocate = nullptr; PFN_vkFreeMemory free = nullptr;
PFN_vkBindBufferMemory bindBuffer = nullptr;
PFN_vkCreateImage createImage = nullptr; PFN_vkDestroyImage destroyImage = nullptr;
PFN_vkGetImageMemoryRequirements imageRequirements = nullptr; PFN_vkBindImageMemory bindImage = nullptr;
PFN_vkCmdCopyBufferToImage copyBufferToImage = nullptr; PFN_vkCmdCopyImageToBuffer copyImageToBuffer = nullptr;
PFN_vkCmdBlitImage blitImage = nullptr;
PFN_vkCmdPipelineBarrier barrier = nullptr;
PFN_vkGetMemoryWin32HandlePropertiesKHR handleProperties = nullptr;
PFN_vkDeviceWaitIdle waitIdle = nullptr;
};
struct StagingImage {
VkImage image = VK_NULL_HANDLE; VkDeviceMemory memory = VK_NULL_HANDLE; VkImageView view = VK_NULL_HANDLE;
VkFormat format = VK_FORMAT_UNDEFINED;
};
struct Intermediate {
ComPtr<ID3D12Resource> resource; D3D12_RESOURCE_DESC desc{};
D3D12_RESOURCE_STATES state = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
VkImageView view = VK_NULL_HANDLE; VkImageLayout layout = VK_IMAGE_LAYOUT_GENERAL; VkImage image = VK_NULL_HANDLE;
};
enum Tex { LinearDepth, MotionT, NormalsT, DiffuseAlbedo, SpecularAlbedo, DiffuseT, SpecularT, DiffuseOut, SpecularOut, Residual, Composed, Widened, TexCount };
std::mutex hostMutex;
}
FsrConfig FsrConfig::from_environment() {
FsrConfig config;
config.vkd3dCore = env_wide("D4R_FSR_VKD3D_CORE");
config.denoiserDll = env_wide("D4R_FSR_RR_DLL");
config.amdxcShim = env_wide("D4R_FSR_AMDXC_SHIM");
config.cacheDir = env_wide("D4R_FSR_CACHE_DIR");
return config;
}
struct FsrBackend::Impl {
Creation creation;
rr_bridge::Layout layout;
ComPtr<ID3D12Device> d3d; // the game's device
ComPtr<ID3D12CommandQueue> queue;
ComPtr<ID3D12Resource> shared;
ComPtr<ID3D12Fence> fence;
ComPtr<ID3D12CommandAllocator> producerAllocator, consumerAllocator;
ComPtr<ID3D12GraphicsCommandList> producer, consumer;
Handle event;
uint64_t sequence = 0;
bool pending = false, failed = false;
// --- private vkd3d-proton side ---
HMODULE core = nullptr;
ComPtr<ID3D12Device> vdev;
ComPtr<ID3D12CommandQueue> vqueue;
ComPtr<ID3D12CommandAllocator> vallocator;
ComPtr<ID3D12GraphicsCommandList> vlist;
ComPtr<ID3D12Fence> vfence; Handle vevent; uint64_t vsequence = 0;
ComPtr<ID3D12DXVKInteropDevice1> interop;
VkInstance vkInstance = VK_NULL_HANDLE; VkPhysicalDevice vkPhysical = VK_NULL_HANDLE; VkDevice vk = VK_NULL_HANDLE;
VkQueue vkQueue = VK_NULL_HANDLE; uint32_t vkFamily = 0;
VkApi api;
D4rFsrRrPasses passes;
FsrRrRuntime runtime;
ffxContext context = nullptr; uint32_t signals = 0;
VkBuffer packetBuffer = VK_NULL_HANDLE; VkDeviceMemory packetMemory = VK_NULL_HANDLE;
std::array<StagingImage, 11> staging{};
std::array<Intermediate, TexCount> textures{};
float previousView[16] = {}; bool havePrevious = false;
uint32_t frameIndex = 0;
FsrConfig config;
HMODULE shimModule = nullptr;
FsrBackend::SuperSampling superSampling;
std::array<unsigned, 4> srRenderLimits{}; // min width/height, max width/height from the SR library
// D4R_FSR_RR_LOG: GPU timestamps of the vkd3d list (begin, after prepare, after denoise, end)
ComPtr<ID3D12QueryHeap> stamps; ComPtr<ID3D12Resource> stampReadback; UINT64 stampFrequency = 0;
void stamp(UINT index) { if (stamps) vlist->EndQuery(stamps.Get(), D3D12_QUERY_TYPE_TIMESTAMP, index); }
ComPtr<ID3D12Resource> denoised; // game-device copy of the denoised colour, the Super Sampling stage's input
D3D12_RESOURCE_STATES denoisedState = D3D12_RESOURCE_STATE_COMMON;
Impl(ID3D12Device* device, const Creation& c, const FsrConfig& cfg)
: creation(c), layout(c.width, c.height, c.outWidth, c.outHeight), d3d(device), config(cfg) {
// Reserve each guide's largest texel size once, as the native backend does, so format changes keep the packet.
VkDeviceSize offset = 0;
for (auto& plane : layout.planes) {
plane.offset = (offset + 511) & ~VkDeviceSize(511);
plane.pitch = (VkDeviceSize(layout.rw) * 16 + 255) & ~VkDeviceSize(255);
offset = plane.offset + plane.pitch * layout.rh;
}
layout.outputOffset = (offset + 511) & ~VkDeviceSize(511);
layout.outputPitch = (VkDeviceSize(layout.ow) * 16 + 255) & ~VkDeviceSize(255);
layout.bytes = layout.outputOffset + layout.outputPitch * layout.oh;
}
~Impl() {
if (vk && api.waitIdle) (void)api.waitIdle(vk);
if (vqueue && vfence) {
const uint64_t value = ++vsequence;
if (SUCCEEDED(vqueue->Signal(vfence.Get(), value)) && SUCCEEDED(vfence->SetEventOnCompletion(value, vevent.value)))
WaitForSingleObject(vevent.value, 10000);
}
if (context) runtime.destroyContext(&context, nullptr);
if (vk) {
for (auto& s : staging) destroy_staging(s);
for (auto& t : textures) if (t.view) passes.destroyView(vk, t.view, nullptr);
passes.destroy();
if (packetBuffer) api.destroyBuffer(vk, packetBuffer, nullptr);
if (packetMemory) api.free(vk, packetMemory, nullptr);
}
// vkd3d-proton's DLL stays loaded for the process: its device objects are released by their ComPtrs.
}
void destroy_staging(StagingImage& s) {
if (s.view) passes.destroyView(vk, s.view, nullptr);
if (s.image) api.destroyImage(vk, s.image, nullptr);
if (s.memory) api.free(vk, s.memory, nullptr);
s = StagingImage{};
}
uint32_t memory_type(uint32_t bits, VkMemoryPropertyFlags flags) {
VkPhysicalDeviceMemoryProperties properties{}; api.memoryProperties(vkPhysical, &properties);
for (uint32_t i = 0; i < properties.memoryTypeCount; ++i)
if ((bits & (1u << i)) && (properties.memoryTypes[i].propertyFlags & flags) == flags) return i;
throw std::runtime_error("No compatible FSR RR Vulkan memory type");
}
void check(VkResult r, const char* what) {
if (r != VK_SUCCESS) throw std::runtime_error(std::string(what) + " VkResult=" + std::to_string(r));
}
void initialize() {
if (!d3d) throw std::runtime_error("Null FSR RR D3D12 device");
if ((creation.flags & 3u) != 3u) throw std::runtime_error("FSR RR requires HDR and render-resolution motion vectors");
if (creation.flags & 4u) throw std::runtime_error("FSR RR does not convert jittered motion vectors");
if (creation.quality < 0 || creation.quality > 5) throw std::runtime_error("Unsupported RR quality mode");
if (config.vkd3dCore.empty() || config.denoiserDll.empty() || config.amdxcShim.empty())
throw std::runtime_error("FSR RR needs D4R_FSR_VKD3D_CORE, D4R_FSR_RR_DLL and D4R_FSR_AMDXC_SHIM");
// --- the game-device packet, fence and copy lists (as the native backend) ---
shared = make_buffer(d3d.Get(), layout.bytes, D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_FLAG_SHARED, D3D12_RESOURCE_STATE_COMMON);
dx(d3d->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)), "FSR RR fence");
event.value = CreateEventW(nullptr, FALSE, FALSE, nullptr);
vevent.value = CreateEventW(nullptr, FALSE, FALSE, nullptr);
if (!event.value || !vevent.value) throw std::runtime_error("FSR RR event creation failed");
auto createList = [&](ID3D12Device* device, auto& allocator, auto& list) {
dx(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&allocator)), "FSR RR allocator");
dx(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, allocator.Get(), nullptr, IID_PPV_ARGS(&list)), "FSR RR command list");
dx(list->Close(), "FSR RR initial close");
};
createList(d3d.Get(), producerAllocator, producer); createList(d3d.Get(), consumerAllocator, consumer);
Handle memoryHandle;
dx(d3d->CreateSharedHandle(shared.Get(), nullptr, GENERIC_ALL, nullptr, &memoryHandle.value), "FSR RR packet handle");
if (log_enabled()) AddVectoredExceptionHandler(1, crash_report);
trace("before start_vkd3d");
start_vkd3d();
trace("before import_packet");
import_packet(memoryHandle.value);
trace("before vkd3d list");
createList(vdev.Get(), vallocator, vlist);
trace("before create_intermediates");
create_intermediates();
trace("before create_context");
create_context();
}
void start_vkd3d() {
// vkd3d-proton's own environment: the dxil-spirv RDNA3 WMMA/FP8 emulation, and a cache for its shaders.
SetEnvironmentVariableA("DXIL_SPIRV_CONFIG", "wmma_rdna3_workaround");
if (!config.cacheDir.empty() && !std::getenv("VKD3D_SHADER_CACHE_PATH")) {
CreateDirectoryW(config.cacheDir.c_str(), nullptr);
SetEnvironmentVariableW(L"VKD3D_SHADER_CACHE_PATH", config.cacheDir.c_str());
}
// The denoiser consults amdxc64.dll only when it is already loaded: ours, never AMD's.
shimModule = LoadLibraryW(config.amdxcShim.c_str());
if (!shimModule) throw std::runtime_error("FSR RR amdxc64 shim could not be loaded");
core = LoadLibraryExW(config.vkd3dCore.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
if (!core) throw std::runtime_error("FSR RR vkd3d-proton core could not be loaded");
using GetInterface = HRESULT(WINAPI*)(REFCLSID, REFIID, void**);
auto getInterface = reinterpret_cast<GetInterface>(reinterpret_cast<void*>(GetProcAddress(core, "D3D12GetInterface")));
CoreInterface* iface = nullptr;
if (!getInterface || FAILED(getInterface(CLSID_Core, IID_CoreInterface, reinterpret_cast<void**>(&iface))) || !iface)
throw std::runtime_error("FSR RR vkd3d-proton core interface unavailable");
ComPtr<IDXGIFactory4> factory; dx(CreateDXGIFactory1(IID_PPV_ARGS(&factory)), "FSR RR DXGI factory");
ComPtr<IDXGIAdapter1> adapter; dx(factory->EnumAdapterByLuid(adapter_luid(d3d.Get()), IID_PPV_ARGS(&adapter)), "FSR RR adapter");
dx(iface->vtbl->createDevice(iface, adapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&vdev)), "FSR RR vkd3d device");
trace("vkd3d device created");
D3D12_COMMAND_QUEUE_DESC queueDesc{}; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
dx(vdev->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&vqueue)), "FSR RR vkd3d queue");
dx(vdev->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&vfence)), "FSR RR vkd3d fence");
if (log_enabled()) {
D3D12_QUERY_HEAP_DESC heap{}; heap.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP; heap.Count = 4;
if (SUCCEEDED(vdev->CreateQueryHeap(&heap, IID_PPV_ARGS(&stamps))) && SUCCEEDED(vqueue->GetTimestampFrequency(&stampFrequency)) && stampFrequency)
stampReadback = make_buffer(vdev.Get(), 4 * sizeof(UINT64), D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
else stamps.Reset();
}
trace("vkd3d queue and fence created");
dx(vdev->QueryInterface(IID_PPV_ARGS(&interop)), "FSR RR vkd3d Vulkan interop");
dx(interop->GetVulkanHandles(&vkInstance, &vkPhysical, &vk), "FSR RR Vulkan handles");
dx(interop->GetVulkanQueueInfo(vqueue.Get(), &vkQueue, &vkFamily), "FSR RR Vulkan queue info");
trace("interop and handles ok");
HMODULE vulkan = GetModuleHandleA("vulkan-1.dll"); if (!vulkan) vulkan = LoadLibraryA("vulkan-1.dll");
auto instanceProc = vulkan ? reinterpret_cast<PFN_vkGetInstanceProcAddr>(reinterpret_cast<void*>(GetProcAddress(vulkan, "vkGetInstanceProcAddr"))) : nullptr;
if (!instanceProc) throw std::runtime_error("FSR RR Vulkan loader unavailable");
api.deviceProc = reinterpret_cast<PFN_vkGetDeviceProcAddr>(instanceProc(vkInstance, "vkGetDeviceProcAddr"));
api.memoryProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceMemoryProperties>(instanceProc(vkInstance, "vkGetPhysicalDeviceMemoryProperties"));
api.formatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceFormatProperties>(instanceProc(vkInstance, "vkGetPhysicalDeviceFormatProperties"));
auto physicalProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(instanceProc(vkInstance, "vkGetPhysicalDeviceProperties"));
if (!api.deviceProc || !api.memoryProperties || !api.formatProperties || !physicalProperties) throw std::runtime_error("FSR RR Vulkan entry points missing");
#define D4R_VK(member, name) api.member = reinterpret_cast<decltype(api.member)>(api.deviceProc(vk, "vk" #name)); \
if (!api.member) throw std::runtime_error("FSR RR Vulkan entry point missing: vk" #name)
D4R_VK(createBuffer, CreateBuffer); D4R_VK(destroyBuffer, DestroyBuffer); D4R_VK(bufferRequirements, GetBufferMemoryRequirements);
D4R_VK(allocate, AllocateMemory); D4R_VK(free, FreeMemory); D4R_VK(bindBuffer, BindBufferMemory);
D4R_VK(createImage, CreateImage); D4R_VK(destroyImage, DestroyImage); D4R_VK(imageRequirements, GetImageMemoryRequirements);
D4R_VK(bindImage, BindImageMemory); D4R_VK(copyBufferToImage, CmdCopyBufferToImage); D4R_VK(copyImageToBuffer, CmdCopyImageToBuffer);
D4R_VK(blitImage, CmdBlitImage);
D4R_VK(barrier, CmdPipelineBarrier); D4R_VK(handleProperties, GetMemoryWin32HandlePropertiesKHR); D4R_VK(waitIdle, DeviceWaitIdle);
#undef D4R_VK
trace("vulkan entry points loaded");
VkPhysicalDeviceProperties limits{}; physicalProperties(vkPhysical, &limits);
if (!passes.init(vk, api.deviceProc, limits.limits.maxPushConstantsSize))
throw std::runtime_error("FSR RR conversion passes could not be created");
trace("passes created");
if (!runtime.load(config.denoiserDll)) throw std::runtime_error(std::string("FSR RR denoiser DLL: ") + fsr_rr_stage_name(runtime.stage));
if (!redirect_amdxc(runtime.module, shimModule)) throw std::runtime_error("FSR RR could not redirect the denoiser's amdxc64 lookup");
trace("amdxc64 lookup redirected to the d4r shim");
trace("before message callback");
runtime.set_message_callback([](uint32_t type, const wchar_t* message) {
std::fprintf(stderr, "FSR RR runtime %s: %ls\n", type == FFX_API_MESSAGE_TYPE_WARNING ? "warning" : "error", message);
}, FFX_API_CONFIGURE_GLOBALDEBUG_LEVEL_WARNINGS);
trace("denoiser dll loaded");
trace("before query_providers");
if (!runtime.query_providers(vdev.Get()))
throw std::runtime_error(std::string("FSR RR provider: ") + fsr_rr_stage_name(runtime.stage));
trace("providers queried");
if (log_enabled()) std::fprintf(stderr, "FSR_RR provider \"%s\" on vkd3d-proton\n", runtime.providerName.c_str());
}
void import_packet(HANDLE resource) {
VkExternalMemoryBufferCreateInfo external{VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO};
external.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT;
VkBufferCreateInfo bi{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, &external}; bi.size = layout.bytes;
bi.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
check(api.createBuffer(vk, &bi, nullptr, &packetBuffer), "FSR RR packet buffer");
VkMemoryRequirements req{}; api.bufferRequirements(vk, packetBuffer, &req);
VkMemoryWin32HandlePropertiesKHR handleProperties{VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHR};
check(api.handleProperties(vk, VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT, resource, &handleProperties), "FSR RR packet memory types");
const uint32_t type = memory_type(req.memoryTypeBits & handleProperties.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VkImportMemoryWin32HandleInfoKHR imported{VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR};
imported.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT; imported.handle = resource;
VkMemoryDedicatedAllocateInfo dedicated{VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, &imported};
dedicated.buffer = packetBuffer;
VkMemoryAllocateInfo ai{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, &dedicated};
ai.allocationSize = req.size; ai.memoryTypeIndex = type;
check(api.allocate(vk, &ai, nullptr, &packetMemory), "FSR RR packet import");
check(api.bindBuffer(vk, packetBuffer, packetMemory, 0), "FSR RR packet bind");
}
void create_intermediate(int i) {
auto& t = textures[i];
t.desc = {}; t.desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
t.desc.Width = creation.width; t.desc.Height = creation.height; t.desc.DepthOrArraySize = 1; t.desc.MipLevels = 1;
t.desc.Format = i == LinearDepth ? DXGI_FORMAT_R32_FLOAT : i == Widened ? DXGI_FORMAT_R32G32B32A32_FLOAT : DXGI_FORMAT_R16G16B16A16_FLOAT;
t.desc.SampleDesc.Count = 1; t.desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
D3D12_HEAP_PROPERTIES heap{}; heap.Type = D3D12_HEAP_TYPE_DEFAULT;
dx(vdev->CreateCommittedResource(&heap, D3D12_HEAP_FLAG_NONE, &t.desc, t.state, nullptr, IID_PPV_ARGS(&t.resource)), "FSR RR intermediate texture");
UINT64 handle = 0, offset = 0; VkFormat format = VK_FORMAT_UNDEFINED;
dx(interop->GetVulkanResourceInfo1(t.resource.Get(), &handle, &offset, &format), "FSR RR intermediate Vulkan info");
if (!handle) throw std::runtime_error("FSR RR intermediate has no Vulkan image");
t.image = reinterpret_cast<VkImage>(handle);
interop->GetVulkanImageLayout(t.resource.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, &t.layout);
VkImageViewCreateInfo info{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
info.image = t.image; info.viewType = VK_IMAGE_VIEW_TYPE_2D; info.format = format;
info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
check(passes.createView(vk, &info, nullptr, &t.view), "FSR RR intermediate view");
}
void create_intermediates() {
// The float output image is needed only for an FSR-only RGBA32F output.
for (int i = 0; i < Widened; ++i) create_intermediate(i);
}
// AMD's tweakable settings (FfxApiConfigureDenoiserKey 1-6). Defaults are logged; D4R_FSR_RR_KEY_<n>=<float>
// overrides key n (diagnostics) and D4R_FSR_RR_STABILITY_BIAS ([RayReconstruction] FsrStabilityBias) sets key 2.
// AMD's default stability bias of 1 weights history heavily, which blurs fine detail on some content.
void apply_denoiser_keys() {
static const char* const names[7] = {"", "cross bilateral normal strength", "stability bias", "max radiance",
"radiance clip std k", "gaussian kernel relaxation", "disocclusion threshold"};
std::string defaults;
for (uint64_t key = 1; key <= 6; ++key) {
float value = 0.0f;
ffxQueryDescDenoiserGetDefaultKeyValue query = {};
query.header.type = FFX_API_QUERY_DESC_TYPE_DENOISER_GET_DEFAULT_KEYVALUE;
query.key = key; query.count = 1; query.data = &value;
char text[96];
if (runtime.query(&context, &query.header) == FFX_API_RETURN_OK)
std::snprintf(text, sizeof(text), "%s%s %g", defaults.empty() ? "" : ", ", names[key], value);
else
std::snprintf(text, sizeof(text), "%s%s ?", defaults.empty() ? "" : ", ", names[key]);
defaults += text;
char variable[32];
std::snprintf(variable, sizeof(variable), "D4R_FSR_RR_KEY_%u", unsigned(key));
std::string override = std::getenv(variable) ? std::getenv(variable) : "";
if (override.empty() && key == 2 && std::getenv("D4R_FSR_RR_STABILITY_BIAS") && std::string(std::getenv("D4R_FSR_RR_STABILITY_BIAS")) != "auto")
override = std::getenv("D4R_FSR_RR_STABILITY_BIAS");
if (override.empty()) continue;
char* end = nullptr;
const float wanted = std::strtof(override.c_str(), &end);
if (end == override.c_str() || !std::isfinite(wanted)) {
std::fprintf(stderr, "FSR_RR: %s override '%s' is not a number; ignored\n", names[key], override.c_str());
continue;
}
ffxConfigureDescDenoiserKeyValue configure = {};
configure.header.type = FFX_API_CONFIGURE_DESC_TYPE_DENOISER_KEYVALUE;
configure.key = key; configure.count = 1; configure.data = &wanted;
const ffxReturnCode_t configured = runtime.configure(&context, &configure.header);
std::fprintf(stderr, "FSR_RR: %s set to %g (%s)\n", names[key], wanted, fsr_rr_return_name(configured));
}
if (log_enabled()) std::fprintf(stderr, "FSR_RR denoiser defaults: %s\n", defaults.c_str());
}
// [RayReconstruction] FsrDetail (D4R_FSR_RR_DETAIL, 0-1): the share of the colour that bypasses the denoiser through
// the residual; D4R_FSR_RR_DEMOD_FLOOR (default 0.1): the smallest albedo a signal is divided by; D4R_FSR_RR_HALO_LIMIT
// (default 1, 0 = off): caps each denoised lobe at that multiple of the brightest noisy input around the texel.
static float env_float(const char* name, float fallback, float low, float high) {
const char* text = std::getenv(name);
if (!text || !*text) return fallback;
char* end = nullptr; const float value = std::strtof(text, &end);
if (end == text || !std::isfinite(value) || value < low || value > high)
throw std::runtime_error(std::string(name) + " must be a number between " + std::to_string(low) + " and " + std::to_string(high));
return value;
}
void create_context() {
const auto start = Clock::now();
// Diffuse is a direct signal (the game's diffuse hit distance is not carried); specular is indirect only
// when the game passes a specular hit distance, which decides the context's signal set once.
signals = FFX_DENOISER_SIGNAL_DIRECT_DIFFUSE | FFX_DENOISER_SIGNAL_DIRECT_SPECULAR;
const ffxReturnCode_t created = fsr_rr_create_context(runtime, vdev.Get(), creation.width, creation.height, signals, 0, context);
if (created != FFX_API_RETURN_OK) throw std::runtime_error(std::string("FSR RR ffxCreateContext failed: ") + fsr_rr_return_name(created));
apply_denoiser_keys();
if (log_enabled()) std::fprintf(stderr, "FSR_RR context created in %.0f ms (%ux%u, signals 0x%x)\n", since(start), creation.width, creation.height, signals);
}
void validate_frame(const Snapshot& frame) {
if (failed) throw std::runtime_error("FSR RR backend is poisoned after failed submission");
if (frame.renderWidth < 64 || frame.renderHeight < 64 || frame.renderWidth > creation.width || frame.renderHeight > creation.height)
throw std::runtime_error("RR render extent must be at least 64 and within creation geometry");
// The exposure texture belongs to the Super Sampling stage (it receives it); the denoiser does not use it.
if (frame.resources[Snapshot::Exposure] && !superSampling) throw std::runtime_error("FSR RR exposure texture needs the Super Sampling stage");
// The old dynamic fixture shrank below NVIDIA's Quality minimum, triggering BAD00005 and a teardown fault.
// Query the SR library's actual range and reject unsupported sizes before recording GPU work.
if (superSampling && (frame.renderWidth < srRenderLimits[0] || frame.renderHeight < srRenderLimits[1] ||
frame.renderWidth > srRenderLimits[2] || frame.renderHeight > srRenderLimits[3]))
throw std::runtime_error("FSR RR render size is outside the Super Sampling dynamic render range");
if (frame.exposureScale != 1 || frame.depthType || frame.normalsType || frame.roughnessType || frame.diffuseType || frame.specularType)
throw std::runtime_error("FSR RR guide encoding or exposure scale requires conversion");
if (frame.resources[Snapshot::Disocclusion] || frame.resources[Snapshot::Subsurface])
throw std::runtime_error("FSR RR does not use the disocclusion or subsurface guides");
if (!superSampling && (frame.renderWidth != creation.outWidth || frame.renderHeight != creation.outHeight))
throw std::runtime_error("FSR RR upscaling needs a Super Sampling stage (none is installed) or output size == render size");
for (size_t i = 0; i < Snapshot::Count; ++i) {
if (i == Snapshot::Exposure) continue;
auto* resource = frame.resources[i].Get();
if (!resource && i >= Snapshot::HitDistance) continue;
if (!resource) throw std::runtime_error("Missing required FSR RR texture");
ComPtr<ID3D12Device> owner; dx(resource->GetDevice(IID_PPV_ARGS(&owner)), "FSR RR texture device");
if (owner.Get() != d3d.Get()) throw std::runtime_error("FSR RR texture belongs to another device");
const auto desc = resource_desc(resource);
if (i != Snapshot::Output) (void)input_format(desc.Format, i);
if (i == Snapshot::Depth) {
(void)copy_format(d3d.Get(), resource, i);
if ((desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) &&
(frame.origins[i][0] || frame.origins[i][1] || frame.renderWidth != desc.Width || frame.renderHeight != desc.Height))
throw std::runtime_error("FSR RR depth-stencil requires a full depth-plane rectangle");
}
const uint32_t width = i == Snapshot::Output ? layout.ow : frame.renderWidth;
const uint32_t height = i == Snapshot::Output ? layout.oh : frame.renderHeight;
if (desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D || (i == Snapshot::Output && !superSampling &&
desc.Format != DXGI_FORMAT_R16G16B16A16_FLOAT && desc.Format != DXGI_FORMAT_R32G32B32A32_FLOAT) ||
desc.SampleDesc.Count != 1 || desc.DepthOrArraySize != 1 ||
frame.origins[i][0] > desc.Width || width > desc.Width - frame.origins[i][0] ||
frame.origins[i][1] > desc.Height || height > desc.Height - frame.origins[i][1])
throw std::runtime_error("Unsupported FSR RR texture format or subrect (FSR-only output must be RGBA16F or RGBA32F)");
if (i == Snapshot::Output && !superSampling && desc.Format == DXGI_FORMAT_R32G32B32A32_FLOAT) {
VkFormatProperties source{}, destination{};
api.formatProperties(vkPhysical, VK_FORMAT_R16G16B16A16_SFLOAT, &source);
api.formatProperties(vkPhysical, VK_FORMAT_R32G32B32A32_SFLOAT, &destination);
if (!(source.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) ||
!(destination.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT))
throw std::runtime_error("FSR RR output widening requires half-to-float blit support");
}
if (i == Snapshot::Output && !(desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS))
throw std::runtime_error("RR output lacks UAV capability");
}
for (size_t i = 0; i < Snapshot::Count; ++i)
if (i != Snapshot::Output && frame.resources[i] == frame.resources[Snapshot::Output]) throw std::runtime_error("RR input/output alias is unsupported");
}
void ensure_staging(size_t plane) {
auto& s = staging[plane]; const VkFormat wanted = layout.planes[plane].format;
if (s.image && s.format == wanted) return;
destroy_staging(s);
VkImageCreateInfo ci{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; ci.imageType = VK_IMAGE_TYPE_2D; ci.format = wanted;
ci.extent = {creation.width, creation.height, 1}; ci.mipLevels = ci.arrayLayers = 1; ci.samples = VK_SAMPLE_COUNT_1_BIT;
ci.tiling = VK_IMAGE_TILING_OPTIMAL; ci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
ci.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
check(api.createImage(vk, &ci, nullptr, &s.image), "FSR RR staging image");
VkMemoryRequirements req{}; api.imageRequirements(vk, s.image, &req);
VkMemoryAllocateInfo ai{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; ai.allocationSize = req.size;
ai.memoryTypeIndex = memory_type(req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
check(api.allocate(vk, &ai, nullptr, &s.memory), "FSR RR staging memory");
check(api.bindImage(vk, s.image, s.memory, 0), "FSR RR staging bind");
VkImageViewCreateInfo vi{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; vi.image = s.image; vi.viewType = VK_IMAGE_VIEW_TYPE_2D;
vi.format = wanted; vi.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
check(passes.createView(vk, &vi, nullptr, &s.view), "FSR RR staging view");
s.format = wanted;
}
void wait_vkd3d() {
const uint64_t value = ++vsequence;
dx(vqueue->Signal(vfence.Get(), value), "FSR RR vkd3d signal");
dx(vfence->SetEventOnCompletion(value, vevent.value), "FSR RR vkd3d completion event");
if (WaitForSingleObject(vevent.value, 60000) != WAIT_OBJECT_0) throw std::runtime_error("FSR RR vkd3d completion timeout");
if (vfence->GetCompletedValue() == UINT64_MAX) throw std::runtime_error("FSR RR vkd3d device removed");
}
void wait_game(ID3D12CommandQueue* q, uint64_t value) {
dx(q->Signal(fence.Get(), value), "FSR RR game signal");
dx(fence->SetEventOnCompletion(value, event.value), "FSR RR game completion event");
if (WaitForSingleObject(event.value, 10000) != WAIT_OBJECT_0) throw std::runtime_error("FSR RR D3D12 completion timeout");
if (fence->GetCompletedValue() == UINT64_MAX) throw std::runtime_error("FSR RR D3D12 device removed");
}
void validate(ID3D12CommandQueue* q, const Snapshot& frame, const std::array<ResourceAccess, Snapshot::Count>& access) {
if (failed) throw std::runtime_error("FSR RR backend is poisoned after failed submission");
if (!q) throw std::runtime_error("Null RR submission queue");
D3D12_COMMAND_QUEUE_DESC queueDesc{};
#if defined(__MINGW32__)
q->GetDesc(&queueDesc);
#else
queueDesc = q->GetDesc();
#endif
if (queueDesc.Type != D3D12_COMMAND_LIST_TYPE_DIRECT) throw std::runtime_error("RR requires a direct queue");
ComPtr<ID3D12Device> owner; dx(q->GetDevice(IID_PPV_ARGS(&owner)), "RR queue device");
if (owner.Get() != d3d.Get()) throw std::runtime_error("RR queue belongs to another device");
for (size_t i = 0; i < access.size(); ++i)
if (frame.resources[i] && (access[i].pending_split || access[i].inherited)) throw std::runtime_error("Unresolved RR texture state");
validate_frame(frame);
}
void evaluate(ID3D12CommandQueue* q, const Snapshot& frame, const std::array<ResourceAccess, Snapshot::Count>& access) {
validate(q, frame, access);
failed = true;
const auto begin = Clock::now();
layout.rw = frame.renderWidth; layout.rh = frame.renderHeight;
for (size_t i = 0; i < layout.planes.size(); ++i) {
const auto resource = resource_index(i);
layout.planes[i].enabled = frame.resources[resource] != nullptr;
if (!layout.planes[i].enabled) continue;
const auto format = input_format(resource_desc(frame.resources[resource].Get()).Format, resource);
layout.planes[i].format = format.format; layout.planes[i].bytes = format.bytes;
}
const bool floatOutput = !superSampling && resource_desc(frame.resources[Snapshot::Output].Get()).Format == DXGI_FORMAT_R32G32B32A32_FLOAT;
if (floatOutput && !textures[Widened].resource) create_intermediate(Widened);
layout.outputBytes = floatOutput ? 16 : 8;
layout.outputFormat = floatOutput ? VK_FORMAT_R32G32B32A32_SFLOAT : VK_FORMAT_R16G16B16A16_SFLOAT;
const auto outputFormat = floatOutput ? DXGI_FORMAT_R32G32B32A32_FLOAT : DXGI_FORMAT_R16G16B16A16_FLOAT;
commands::InternalScope internal;
// --- 1. the game's guides into the shared packet ---
dx(producerAllocator->Reset(), "FSR RR producer allocator reset"); dx(producer->Reset(producerAllocator.Get(), nullptr), "FSR RR producer reset");
transition(producer.Get(), shared.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST);
for (size_t i = 0; i < layout.planes.size(); ++i) {
const auto resource = resource_index(i);
auto* texture = frame.resources[resource].Get(); const auto& plane = layout.planes[i];
if (!plane.enabled) continue;
texture_copy_barrier(producer.Get(), texture, access[resource], false, false);
auto source = rr_bridge::D3D12Frames::texture(texture);
auto dest = rr_bridge::D3D12Frames::placed(shared.Get(), plane.offset, plane.pitch, copy_format(d3d.Get(), texture, resource), layout.rw, layout.rh);
const auto x = frame.origins[resource][0], y = frame.origins[resource][1];
D3D12_BOX box{x, y, 0, x + layout.rw, y + layout.rh, 1};
const bool depthStencil = (resource_desc(texture).Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) != 0;
producer->CopyTextureRegion(&dest, 0, 0, 0, &source, depthStencil ? nullptr : &box);
texture_copy_barrier(producer.Get(), texture, access[resource], false, true);
}
transition(producer.Get(), shared.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COMMON);
dx(producer->Close(), "FSR RR producer close");
ID3D12CommandList* inputs[] = {producer.Get()}; q->ExecuteCommandLists(1, inputs);
pending = true; queue = q;
wait_game(q, ++sequence);
const double producedMs = since(begin);
// --- 2. conversion, denoising and composition on the vkd3d device ---
run_vkd3d(frame);
const double vkd3dMs = since(begin) - producedMs;
// --- 3. the packet's output into the game's output texture ---
dx(consumerAllocator->Reset(), "FSR RR consumer allocator reset"); dx(consumer->Reset(consumerAllocator.Get(), nullptr), "FSR RR consumer reset");
transition(consumer.Get(), shared.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_SOURCE);
auto* output = frame.resources[Snapshot::Output].Get();
if (superSampling) {
// the denoised colour at render size goes to a game-device texture; the Super Sampling stage upscales it
if (!denoised) {
D3D12_HEAP_PROPERTIES heap{}; heap.Type = D3D12_HEAP_TYPE_DEFAULT;
D3D12_RESOURCE_DESC desc{}; desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; desc.Width = creation.width; desc.Height = creation.height;
desc.DepthOrArraySize = desc.MipLevels = 1; desc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; desc.SampleDesc.Count = 1;
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
dx(d3d->CreateCommittedResource(&heap, D3D12_HEAP_FLAG_NONE, &desc, denoisedState, nullptr, IID_PPV_ARGS(&denoised)), "FSR RR denoised texture");
}
transition(consumer.Get(), denoised.Get(), denoisedState, D3D12_RESOURCE_STATE_COPY_DEST);
auto source = rr_bridge::D3D12Frames::placed(shared.Get(), layout.outputOffset, layout.outputPitch, outputFormat, layout.rw, layout.rh);
auto dest = rr_bridge::D3D12Frames::texture(denoised.Get());
consumer->CopyTextureRegion(&dest, 0, 0, 0, &source, nullptr);
denoisedState = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
transition(consumer.Get(), denoised.Get(), D3D12_RESOURCE_STATE_COPY_DEST, denoisedState);
} else {
texture_copy_barrier(consumer.Get(), output, access[Snapshot::Output], true, false);
auto source = rr_bridge::D3D12Frames::placed(shared.Get(), layout.outputOffset, layout.outputPitch, outputFormat, layout.ow, layout.oh);
auto dest = rr_bridge::D3D12Frames::texture(output);
consumer->CopyTextureRegion(&dest, frame.origins[Snapshot::Output][0], frame.origins[Snapshot::Output][1], 0, &source, nullptr);
texture_copy_barrier(consumer.Get(), output, access[Snapshot::Output], true, true);
}
transition(consumer.Get(), shared.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COMMON);
dx(consumer->Close(), "FSR RR consumer close");
ID3D12CommandList* outputs[] = {consumer.Get()}; q->ExecuteCommandLists(1, outputs);
wait_game(q, ++sequence);
if (superSampling) {
// the stage records/submits its own work on `q`; the denoised texture is in NON_PIXEL_SHADER_RESOURCE
const auto upscaleStart = Clock::now();
superSampling(q, denoised.Get(), frame, access);
if (log_enabled()) std::fprintf(stderr, "FSR_RR super sampling submitted in %.2f ms\n", since(upscaleStart));
}
pending = false; failed = false; queue.Reset();
if (log_enabled())
std::fprintf(stderr, "FSR_RR frame %u: %ux%u produce %.2f ms, vkd3d (convert+denoise+compose) %.2f ms, total %.2f ms\n",
frameIndex, layout.rw, layout.rh, producedMs, vkd3dMs, since(begin));
++frameIndex;
}
void run_vkd3d(const Snapshot& frame) {
const auto& s = frame.settings;
const uint32_t rw = layout.rw, rh = layout.rh;
// which game planes exist decides the context's specular signal once
const bool specularHit = layout.planes[7].enabled;
const uint32_t wanted = FFX_DENOISER_SIGNAL_DIRECT_DIFFUSE |
(specularHit ? FFX_DENOISER_SIGNAL_INDIRECT_SPECULAR : FFX_DENOISER_SIGNAL_DIRECT_SPECULAR);
if (wanted != signals) {
if (frameIndex == 0) {
runtime.destroyContext(&context, nullptr); context = nullptr; signals = wanted;
const ffxReturnCode_t created = fsr_rr_create_context(runtime, vdev.Get(), creation.width, creation.height, signals, 0, context);
if (created != FFX_API_RETURN_OK) throw std::runtime_error(std::string("FSR RR ffxCreateContext failed: ") + fsr_rr_return_name(created));
apply_denoiser_keys();
} else throw std::runtime_error("FSR RR: the game's hit-distance guide changed after the denoiser was created (recreate the feature)");
}
for (size_t plane : {0, 1, 2, 3, 4, 5, 6, 7, 10}) if (layout.planes[plane].enabled) ensure_staging(plane);
double clipToView[16], viewToWorld[16];
if (!invert(s.viewToClip, clipToView) || !invert(s.worldToView, viewToWorld)) throw std::runtime_error("FSR RR camera matrix is not invertible");
const bool reset = s.reset || !havePrevious;
double previousView[16], viewToPrevious[16];
for (int i = 0; i < 16; ++i) previousView[i] = reset ? s.worldToView[i] : this->previousView[i];
multiply(viewToWorld, previousView, viewToPrevious);
D4rFsrRrPrepareConstants c = {};
for (int i = 0; i < 16; ++i) { c.clipToView[i] = float(clipToView[i]); c.viewToPreviousView[i] = float(viewToPrevious[i]); }
c.renderSize[0] = float(rw); c.renderSize[1] = float(rh); c.renderSize[2] = 1.0f / rw; c.renderSize[3] = 1.0f / rh;
c.motion[0] = s.motionScale[0] / rw; c.motion[1] = s.motionScale[1] / rh; c.motion[2] = 1.0f; c.motion[3] = 1.0f;
uint32_t flags = 0;
if (s.hardwareDepth) flags |= kFsrRrHardwareDepth;
if (s.roughnessPacked) flags |= kFsrRrRoughnessPacked;
if (specularHit) flags |= kFsrRrSpecularHit;
if (unorm(layout.planes[3].format)) flags |= kFsrRrNormalsUnorm;
if (layout.planes[10].enabled) flags |= kFsrRrBeforeParticles;
flags |= kFsrRrInvalidNormalPassthrough;
c.motionBaseFlags[2] = flags;
const float viewZSign = s.viewToClip[11] < 0.0f ? -1.0f : 1.0f;
c.depthParams[0] = viewZSign; c.depthParams[1] = s.depthInverted ? 0.0f : 1.0f; c.depthParams[2] = 1.0e6f;
c.detail[0] = env_float("D4R_FSR_RR_DETAIL", 0.0f, 0.0f, 1.0f);
c.detail[1] = std::max(env_float("D4R_FSR_RR_DEMOD_FLOOR", 0.1f, 0.0f, 1.0f), 1e-3f);
dx(vallocator->Reset(), "FSR RR vkd3d allocator reset"); dx(vlist->Reset(vallocator.Get(), nullptr), "FSR RR vkd3d list reset");
stamp(0);
auto setState = [&](int index, D3D12_RESOURCE_STATES state) {
auto& t = textures[index];
if (t.resource) { transition(vlist.Get(), t.resource.Get(), t.state, state); t.state = state; }
};
for (int i = 0; i < TexCount; ++i) setState(i, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
VkCommandBuffer command = VK_NULL_HANDLE;
dx(interop->BeginVkCommandBufferInterop(vlist.Get(), &command), "FSR RR Vulkan interop begin");
// the packet comes in from the game's device
VkBufferMemoryBarrier acquire{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER}; acquire.buffer = packetBuffer; acquire.size = VK_WHOLE_SIZE;
acquire.srcQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL; acquire.dstQueueFamilyIndex = vkFamily;
acquire.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
api.barrier(command, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1, &acquire, 0, nullptr);
for (size_t plane : {0, 1, 2, 3, 4, 5, 6, 7, 10}) {
if (!layout.planes[plane].enabled) continue;
const auto& st = staging[plane];
VkImageMemoryBarrier toCopy{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; toCopy.image = st.image;
toCopy.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; toCopy.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
toCopy.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; toCopy.srcQueueFamilyIndex = toCopy.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
toCopy.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
api.barrier(command, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &toCopy);
VkBufferImageCopy region{}; region.bufferOffset = layout.planes[plane].offset;
region.bufferRowLength = uint32_t(layout.planes[plane].pitch / layout.planes[plane].bytes);
region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; region.imageExtent = {rw, rh, 1};
api.copyBufferToImage(command, packetBuffer, st.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
VkImageMemoryBarrier toRead = toCopy; toRead.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; toRead.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
toRead.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; toRead.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
api.barrier(command, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &toRead);
}
auto sampled = [&](size_t plane) { return D4rFsrRrImage{staging[plane].view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL}; };
const bool hasHit = layout.planes[7].enabled, hasParticles = layout.planes[10].enabled;
D4rFsrRrImage images[D4rFsrRrPasses::kPrepareInputs + D4rFsrRrPasses::kPrepareOutputs] = {
sampled(0), sampled(2), sampled(1), sampled(3), sampled(4), sampled(5), sampled(6),
hasHit ? sampled(7) : sampled(3), sampled(3), hasParticles ? sampled(10) : sampled(0)};
static const int targets[D4rFsrRrPasses::kPrepareOutputs] = {LinearDepth, MotionT, NormalsT, DiffuseAlbedo, SpecularAlbedo, DiffuseT, SpecularT, Residual};
for (uint32_t i = 0; i < D4rFsrRrPasses::kPrepareOutputs; ++i)
images[D4rFsrRrPasses::kPrepareInputs + i] = {textures[targets[i]].view, textures[targets[i]].layout};
passes.record_prepare(command, images, c, rw, rh);
dx(interop->EndVkCommandBufferInterop(vlist.Get()), "FSR RR Vulkan interop end");
stamp(1);
// --- AMD FSR Ray Regeneration (D3D12 on vkd3d-proton) ---
const D3D12_RESOURCE_STATES read = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
for (int index : {LinearDepth, MotionT, NormalsT, DiffuseAlbedo, SpecularAlbedo, DiffuseT, SpecularT}) setState(index, read);
auto ffx = [&](int index, uint32_t state) {
const auto& t = textures[index];
return fsr_rr_texture(t.resource.Get(), t.desc, fsr_rr_surface_format(t.desc.Format), state);
};
const uint32_t ffxRead = FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ;
ffxDispatchDescDenoiser desc = {};
desc.header.type = FFX_API_DISPATCH_DESC_TYPE_DENOISER;
desc.commandList = vlist.Get();
desc.linearDepth = ffx(LinearDepth, ffxRead); desc.motionVectors = ffx(MotionT, ffxRead); desc.normals = ffx(NormalsT, ffxRead);
desc.specularAlbedo = ffx(SpecularAlbedo, ffxRead); desc.diffuseAlbedo = ffx(DiffuseAlbedo, ffxRead);
desc.motionVectorScale = {1.0f, 1.0f, 1.0f};
desc.jitterOffsets = {s.jitter[0], s.jitter[1]};
const double position[3] = {viewToWorld[12], viewToWorld[13], viewToWorld[14]};
double previousPosition[3] = {position[0], position[1], position[2]};
if (!reset) {
double previousInverse[16];
if (invert(this->previousView, previousInverse)) previousPosition[0] = previousInverse[12], previousPosition[1] = previousInverse[13], previousPosition[2] = previousInverse[14];
}
desc.cameraPositionDelta = {float(previousPosition[0] - position[0]), float(previousPosition[1] - position[1]), float(previousPosition[2] - position[2])};
std::memcpy(&desc.view, s.worldToView, sizeof(desc.view)); std::memcpy(&desc.projection, s.viewToClip, sizeof(desc.projection));
desc.linearDepthBounds = {0.0f, 1.0e6f};
desc.renderSize = {rw, rh};
desc.frameIndex = frameIndex;
desc.flags = uint32_t(FFX_DENOISER_DISPATCH_NON_GAMMA_ALBEDO) | (reset ? uint32_t(FFX_DENOISER_DISPATCH_RESET) : 0u);
ffxDispatchDescDenoiserDirectDiffuse directDiffuse = {}; ffxDispatchDescDenoiserDirectSpecular directSpecular = {};
ffxDispatchDescDenoiserIndirectSpecular indirectSpecular = {};
directDiffuse.header.type = FFX_API_DISPATCH_DESC_TYPE_DENOISER_DIRECT_DIFFUSE;
FfxApiDenoiserSignal* diffuseSignal = &directDiffuse.signal;
FfxApiDenoiserSignal* specularSignal; ffxApiHeader* specularHeader;
if (specularHit) {
indirectSpecular.header.type = FFX_API_DISPATCH_DESC_TYPE_DENOISER_INDIRECT_SPECULAR; specularSignal = &indirectSpecular.signal; specularHeader = &indirectSpecular.header;
} else {
directSpecular.header.type = FFX_API_DISPATCH_DESC_TYPE_DENOISER_DIRECT_SPECULAR; specularSignal = &directSpecular.signal; specularHeader = &directSpecular.header;
}
diffuseSignal->input = ffx(DiffuseT, ffxRead); diffuseSignal->output = ffx(DiffuseOut, FFX_API_RESOURCE_STATE_UNORDERED_ACCESS);
specularSignal->input = ffx(SpecularT, ffxRead); specularSignal->output = ffx(SpecularOut, FFX_API_RESOURCE_STATE_UNORDERED_ACCESS);
desc.header.pNext = &directDiffuse.header; directDiffuse.header.pNext = specularHeader;
const ffxReturnCode_t dispatched = runtime.dispatch(&context, &desc.header);
if (dispatched != FFX_API_RETURN_OK) throw std::runtime_error(std::string("FSR RR ffxDispatch failed: ") + fsr_rr_return_name(dispatched));
D3D12_RESOURCE_BARRIER uav{}; uav.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; vlist->ResourceBarrier(1, &uav);
stamp(2);
// --- composition, then the composed image into the packet's output region ---
for (int i = 0; i < TexCount; ++i) setState(i, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
const D4rFsrRrImage compose[D4rFsrRrPasses::kComposeImages] = {
{textures[DiffuseOut].view, textures[DiffuseOut].layout}, {textures[SpecularOut].view, textures[SpecularOut].layout},
{textures[DiffuseAlbedo].view, textures[DiffuseAlbedo].layout}, {textures[SpecularAlbedo].view, textures[SpecularAlbedo].layout},
{textures[Residual].view, textures[Residual].layout}, {textures[Composed].view, textures[Composed].layout},
{textures[DiffuseT].view, textures[DiffuseT].layout}, {textures[SpecularT].view, textures[SpecularT].layout}};
dx(interop->BeginVkCommandBufferInterop(vlist.Get(), &command), "FSR RR Vulkan interop begin (compose)");
passes.record_compose(command, compose, rw, rh, env_float("D4R_FSR_RR_HALO_LIMIT", 1.0f, 0.0f, 1000.0f));
int copiedImage = Composed;
if (layout.outputBytes == 16) {
// Preserve the denoiser/composition's half arithmetic; widen only the completed output on the GPU.
VkImageBlit region{}; region.srcSubresource = region.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
region.srcOffsets[1] = region.dstOffsets[1] = {int32_t(rw), int32_t(rh), 1};
api.blitImage(command, textures[Composed].image, textures[Composed].layout,
textures[Widened].image, textures[Widened].layout, 1, &region, VK_FILTER_NEAREST);
passes.full_barrier(command);
copiedImage = Widened;
}
VkBufferImageCopy back{}; back.bufferOffset = layout.outputOffset; back.bufferRowLength = uint32_t(layout.outputPitch / layout.outputBytes);
back.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; back.imageExtent = {rw, rh, 1};
api.copyImageToBuffer(command, textures[copiedImage].image, textures[copiedImage].layout, packetBuffer, 1, &back);
VkBufferMemoryBarrier release = acquire; release.srcQueueFamilyIndex = vkFamily; release.dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL;
release.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; release.dstAccessMask = 0;
api.barrier(command, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 1, &release, 0, nullptr);
dx(interop->EndVkCommandBufferInterop(vlist.Get()), "FSR RR Vulkan interop end (compose)");
// Diagnostic (D4R_FSR_RR_DUMP_DIR, first D4R_FSR_RR_DUMP_FRAMES frames, default 3): raw copies of the converted
// linear depth and motion textures, to check the DLSS -> FFX guide conversion numerically.
stamp(3);
if (stamps) vlist->ResolveQueryData(stamps.Get(), D3D12_QUERY_TYPE_TIMESTAMP, 0, 4, stampReadback.Get(), 0);
const char* dumpDir = std::getenv("D4R_FSR_RR_DUMP_DIR");
const unsigned dumpFrames = std::getenv("D4R_FSR_RR_DUMP_FRAMES") ? unsigned(std::atoi(std::getenv("D4R_FSR_RR_DUMP_FRAMES"))) : 3;
struct Dump { int index; const char* name; ComPtr<ID3D12Resource> buffer; D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint; UINT rows; UINT bytesPerTexel; };
std::vector<Dump> dumps;
if (dumpDir && frameIndex < dumpFrames) {
for (auto [index, name, texel] : {std::tuple<int, const char*, UINT>{LinearDepth, "linear-depth", 4}, {MotionT, "motion", 8}, {NormalsT, "normals", 8}, {Composed, "composed", 8}}) {
Dump d{index, name, {}, {}, 0, texel};
UINT64 rowBytes = 0, total = 0; auto desc = textures[index].desc;
vdev->GetCopyableFootprints(&desc, 0, 1, 0, &d.footprint, &d.rows, &rowBytes, &total);
d.buffer = make_buffer(vdev.Get(), total, D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
transition(vlist.Get(), textures[index].resource.Get(), textures[index].state, D3D12_RESOURCE_STATE_COPY_SOURCE);
D3D12_TEXTURE_COPY_LOCATION destination{}; destination.pResource = d.buffer.Get();
destination.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; destination.PlacedFootprint = d.footprint;
D3D12_TEXTURE_COPY_LOCATION source{}; source.pResource = textures[index].resource.Get(); source.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
vlist->CopyTextureRegion(&destination, 0, 0, 0, &source, nullptr);
transition(vlist.Get(), textures[index].resource.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, textures[index].state);
dumps.push_back(std::move(d));
}
}
dx(vlist->Close(), "FSR RR vkd3d list close");
ID3D12CommandList* lists[] = {vlist.Get()}; vqueue->ExecuteCommandLists(1, lists);
wait_vkd3d();
if (stamps) {
void* mapped = nullptr; D3D12_RANGE range{0, 4 * sizeof(UINT64)};
if (SUCCEEDED(stampReadback->Map(0, &range, &mapped))) {
UINT64 t[4]; std::memcpy(t, mapped, sizeof(t)); stampReadback->Unmap(0, nullptr);
auto ms = [&](int a, int b) { return double(t[b] - t[a]) * 1000.0 / double(stampFrequency); };
std::fprintf(stderr, "FSR_RR gpu frame %u: prepare %.2f ms, denoise %.2f ms, compose+copy %.2f ms, total %.2f ms\n", frameIndex, ms(0, 1), ms(1, 2), ms(2, 3), ms(0, 3));
}
}
for (auto& d : dumps) {
void* mapped = nullptr; D3D12_RANGE range{0, size_t(d.footprint.Footprint.RowPitch) * d.rows};
dx(d.buffer->Map(0, &range, &mapped), "FSR RR dump map");
const std::string path = std::string(dumpDir) + "/frame" + std::to_string(frameIndex) + "-" + d.name + ".bin";
if (FILE* out = std::fopen(path.c_str(), "wb")) {
for (UINT row = 0; row < rw && row < d.rows; ++row) {} // (rows are written below; width = render width)
for (UINT row = 0; row < rh; ++row)
std::fwrite(static_cast<const char*>(mapped) + size_t(row) * d.footprint.Footprint.RowPitch, 1, size_t(rw) * d.bytesPerTexel, out);
std::fclose(out);
}
d.buffer->Unmap(0, nullptr);
}
std::memcpy(this->previousView, s.worldToView, sizeof(this->previousView));
havePrevious = true;
}
};
FsrBackend::FsrBackend(ID3D12Device* device, const Creation& creation, const FsrConfig& config) {
std::lock_guard<std::mutex> lock(hostMutex);
auto candidate = std::make_unique<Impl>(device, creation, config);
candidate->initialize(); impl = std::move(candidate);
}
FsrBackend::~FsrBackend() {
std::lock_guard<std::mutex> lock(hostMutex);
if (impl && impl->pending) {
std::fprintf(stderr, "D4R_FSR_RR_QUARANTINE incomplete GPU work; retaining backend and guide resources\n");
(void)impl.release();
} else impl.reset();
}
void FsrBackend::evaluate(ID3D12CommandQueue* queue, const Snapshot& frame, const std::array<ResourceAccess, Snapshot::Count>& access) {
std::lock_guard<std::mutex> lock(hostMutex); impl->evaluate(queue, frame, access);
}
void FsrBackend::set_super_sampling(SuperSampling stage, const std::array<unsigned, 4>& renderLimits) {
std::lock_guard<std::mutex> lock(hostMutex); impl->superSampling = std::move(stage); impl->srRenderLimits = renderLimits;
}
void FsrBackend::validate_frame(const Snapshot& frame) { std::lock_guard<std::mutex> lock(hostMutex); impl->validate_frame(frame); }
void FsrBackend::drain() {
std::lock_guard<std::mutex> lock(hostMutex);
if (impl->pending || impl->failed) throw std::runtime_error("FSR RR backend cannot retire failed GPU work");
}
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include "d3d12_external.h"
#include "rr_ngx_snapshot.h"
#include <functional>
#include <memory>
#include <string>
namespace d4r::win::rr {
// Where the experimental FSR Ray Regeneration backend finds its pieces. All paths are explicit: nothing is
// searched for, and AMD's amdxc64.dll is never loaded.
struct FsrConfig {
std::wstring vkd3dCore; // d4r's vkd3d-proton d3d12core.dll, renamed so it cannot collide with the system's
std::wstring denoiserDll; // AMD's amd_fidelityfx_denoiser_dx12.dll (FidelityFX SDK 2.3), unmodified
std::wstring amdxcShim; // d4r's amdxc64 shim (tools/windows/amdxc64_shim)
std::wstring cacheDir; // vkd3d-proton's shader cache: the denoiser's shaders are compiled once
static FsrConfig from_environment();
};
// FSR Ray Regeneration as the Ray Reconstruction backend on native Windows. The game's guides cross to a private
// vkd3d-proton device (D3D12 over Vulkan, dxil-spirv FP8 emulation) through the same shared packet the native
// DLSS backend uses; AMD's denoiser runs there. Evaluation is synchronous, as the native backend's is.
// The denoiser works at render resolution. When the output is larger (or a Super Sampling stage is wanted at all),
// the owner installs one with set_super_sampling(): the denoised colour is then copied into a texture on the game's
// device and handed to that callback together with the frame, which must record/submit the upscale into the game's
// output on `queue`. Without it an output larger than the render size is refused, never returned noisy.
class FsrBackend {
struct Impl;
std::unique_ptr<Impl> impl;
public:
FsrBackend(ID3D12Device* device, const Creation& creation, const FsrConfig& config);
~FsrBackend();
FsrBackend(const FsrBackend&) = delete;
FsrBackend& operator=(const FsrBackend&) = delete;
using SuperSampling = std::function<void(ID3D12CommandQueue*, ID3D12Resource* denoised, const Snapshot&,
const std::array<ResourceAccess, Snapshot::Count>&)>;
void set_super_sampling(SuperSampling stage, const std::array<unsigned, 4>& renderLimits);
void validate_frame(const Snapshot& frame);
void evaluate(ID3D12CommandQueue* queue, const Snapshot& frame, const std::array<ResourceAccess, Snapshot::Count>& access);
void drain();
};
}
+10
View File
@@ -4,12 +4,22 @@
#include <d3d12.h>
#include <dxgi1_4.h>
#include <cstdio>
#include <cstdlib>
#include <string>
// Native Windows uses the installed driver's normal D3D12 path. Do not import
// Proton's amdxc64 shim or Linux PCI/sysfs discovery into this process.
inline bool d4r_fsr_rr_driver_extension(ID3D12Device* device, std::string& detail)
{
// Experimental: the denoiser only consults amdxc64.dll when it is already loaded in the process. With
// D4R_FSR_AMDXC_SHIM=path, preload d4r's own shim (tools/windows/amdxc64_shim) for a vkd3d-proton device.
// AMD's real amdxc64.dll is never loaded by this helper.
if (const char* shim = std::getenv("D4R_FSR_AMDXC_SHIM")) {
if (!LoadLibraryA(shim)) {
detail = "D4R_FSR_AMDXC_SHIM could not be loaded";
return false;
}
}
LUID luid{};
device->GetAdapterLuid(&luid);
IDXGIFactory4* factory = nullptr;
+239
View File
@@ -0,0 +1,239 @@
// The FSR Ray Regeneration backend's own two compute passes (tools/windows/fsr_rr/fsr_rr_prepare.comp and
// fsr_rr_compose.comp), recorded into the game's D3D12 command list through vkd3d-proton's Vulkan
// interop. Descriptors are pushed (VK_KHR_push_descriptor, which vkd3d-proton requires), so nothing
// here outlives the command buffer that recorded it except the pipelines.
#pragma once
#include <vulkan/vulkan_core.h>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include "fsr_rr/fsr_rr_shaders_spv.h"
struct D4rFsrRrPrepareConstants
{
float clipToView[16];
float viewToPreviousView[16];
float renderSize[4];
float motion[4];
uint32_t colorDepthBase[4];
uint32_t motionBaseFlags[4];
float depthParams[4];
float detail[4];
};
static_assert(sizeof(D4rFsrRrPrepareConstants) == 224, "fsr_rr_prepare.comp push constants");
enum D4rFsrRrPrepareFlags : uint32_t
{
kFsrRrHardwareDepth = 1u,
kFsrRrRoughnessPacked = 2u,
kFsrRrSpecularHit = 4u,
kFsrRrSpecularHitInW = 8u,
kFsrRrDiffuseHit = 16u,
kFsrRrDiffuseHitInW = 32u,
kFsrRrNormalsUnorm = 64u,
kFsrRrBeforeParticles = 128u,
kFsrRrInvalidNormalPassthrough = 256u,
};
// One image a pass reads or writes: the view and the layout the image is in while the pass runs.
struct D4rFsrRrImage
{
VkImageView view = VK_NULL_HANDLE;
VkImageLayout layout = VK_IMAGE_LAYOUT_GENERAL;
};
struct D4rFsrRrPasses
{
static constexpr uint32_t kPrepareInputs = 10, kPrepareOutputs = 8, kComposeImages = 8;
VkDevice device = VK_NULL_HANDLE;
VkSampler sampler = VK_NULL_HANDLE;
VkDescriptorSetLayout prepareSet = VK_NULL_HANDLE, composeSet = VK_NULL_HANDLE;
VkPipelineLayout prepareLayout = VK_NULL_HANDLE, composeLayout = VK_NULL_HANDLE;
VkPipeline preparePipeline = VK_NULL_HANDLE, composePipeline = VK_NULL_HANDLE;
PFN_vkCreateSampler createSampler = nullptr;
PFN_vkDestroySampler destroySampler = nullptr;
PFN_vkCreateDescriptorSetLayout createSetLayout = nullptr;
PFN_vkDestroyDescriptorSetLayout destroySetLayout = nullptr;
PFN_vkCreatePipelineLayout createLayout = nullptr;
PFN_vkDestroyPipelineLayout destroyLayout = nullptr;
PFN_vkCreateShaderModule createShader = nullptr;
PFN_vkDestroyShaderModule destroyShader = nullptr;
PFN_vkCreateComputePipelines createPipelines = nullptr;
PFN_vkDestroyPipeline destroyPipeline = nullptr;
PFN_vkCmdBindPipeline bindPipeline = nullptr;
PFN_vkCmdPushConstants push = nullptr;
PFN_vkCmdPushDescriptorSetKHR pushDescriptors = nullptr;
PFN_vkCmdDispatch dispatch = nullptr;
PFN_vkCmdPipelineBarrier barrier = nullptr;
PFN_vkCreateImageView createView = nullptr;
PFN_vkDestroyImageView destroyView = nullptr;
bool pipeline(const uint32_t* code, size_t bytes, VkPipelineLayout layout, VkPipeline& result)
{
VkShaderModuleCreateInfo shader = {VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
shader.codeSize = bytes;
shader.pCode = code;
VkShaderModule module = VK_NULL_HANDLE;
if (createShader(device, &shader, nullptr, &module) != VK_SUCCESS)
return false;
VkComputePipelineCreateInfo info = {VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO};
info.stage = {VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO};
info.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
info.stage.module = module;
info.stage.pName = "main";
info.layout = layout;
const VkResult status = createPipelines(device, VK_NULL_HANDLE, 1, &info, nullptr, &result);
destroyShader(device, module, nullptr);
return status == VK_SUCCESS;
}
bool set_layout(const VkDescriptorType* types, uint32_t count, uint32_t pushBytes, VkDescriptorSetLayout& set,
VkPipelineLayout& layout)
{
VkDescriptorSetLayoutBinding bindings[kPrepareInputs + kPrepareOutputs] = {};
for (uint32_t i = 0; i < count; ++i)
{
bindings[i].binding = i;
bindings[i].descriptorType = types[i];
bindings[i].descriptorCount = 1;
bindings[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
bindings[i].pImmutableSamplers = types[i] == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ? &sampler : nullptr;
}
VkDescriptorSetLayoutCreateInfo info = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
info.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
info.bindingCount = count;
info.pBindings = bindings;
if (createSetLayout(device, &info, nullptr, &set) != VK_SUCCESS)
return false;
VkPushConstantRange range = {VK_SHADER_STAGE_COMPUTE_BIT, 0, pushBytes};
VkPipelineLayoutCreateInfo pipelineLayout = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
pipelineLayout.setLayoutCount = 1;
pipelineLayout.pSetLayouts = &set;
pipelineLayout.pushConstantRangeCount = 1;
pipelineLayout.pPushConstantRanges = &range;
return createLayout(device, &pipelineLayout, nullptr, &layout) == VK_SUCCESS;
}
// `maxPushConstants`: VkPhysicalDeviceLimits::maxPushConstantsSize (the prepare pass needs 224).
bool init(VkDevice d, PFN_vkGetDeviceProcAddr proc, uint32_t maxPushConstants)
{
device = d;
if (maxPushConstants < sizeof(D4rFsrRrPrepareConstants))
return false;
#define D4R_LOAD(member, name) \
member = reinterpret_cast<decltype(member)>(proc(d, "vk" #name)); \
if (!member) \
return false
D4R_LOAD(createSampler, CreateSampler);
D4R_LOAD(destroySampler, DestroySampler);
D4R_LOAD(createSetLayout, CreateDescriptorSetLayout);
D4R_LOAD(destroySetLayout, DestroyDescriptorSetLayout);
D4R_LOAD(createLayout, CreatePipelineLayout);
D4R_LOAD(destroyLayout, DestroyPipelineLayout);
D4R_LOAD(createShader, CreateShaderModule);
D4R_LOAD(destroyShader, DestroyShaderModule);
D4R_LOAD(createPipelines, CreateComputePipelines);
D4R_LOAD(destroyPipeline, DestroyPipeline);
D4R_LOAD(bindPipeline, CmdBindPipeline);
D4R_LOAD(push, CmdPushConstants);
D4R_LOAD(pushDescriptors, CmdPushDescriptorSetKHR);
D4R_LOAD(dispatch, CmdDispatch);
D4R_LOAD(barrier, CmdPipelineBarrier);
D4R_LOAD(createView, CreateImageView);
D4R_LOAD(destroyView, DestroyImageView);
#undef D4R_LOAD
VkSamplerCreateInfo nearest = {VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
nearest.magFilter = nearest.minFilter = VK_FILTER_NEAREST;
nearest.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
nearest.addressModeU = nearest.addressModeV = nearest.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
if (createSampler(d, &nearest, nullptr, &sampler) != VK_SUCCESS)
return false;
VkDescriptorType prepare[kPrepareInputs + kPrepareOutputs];
for (uint32_t i = 0; i < kPrepareInputs + kPrepareOutputs; ++i)
prepare[i] = i < kPrepareInputs ? VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER : VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
VkDescriptorType compose[kComposeImages];
for (VkDescriptorType& type : compose)
type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
return set_layout(prepare, kPrepareInputs + kPrepareOutputs, sizeof(D4rFsrRrPrepareConstants), prepareSet,
prepareLayout) &&
set_layout(compose, kComposeImages, 16, composeSet, composeLayout) &&
pipeline(d4r_vk_fsr_rr_prepare, sizeof(d4r_vk_fsr_rr_prepare), prepareLayout, preparePipeline) &&
pipeline(d4r_vk_fsr_rr_compose, sizeof(d4r_vk_fsr_rr_compose), composeLayout, composePipeline);
}
void destroy()
{
if (device == VK_NULL_HANDLE || destroyPipeline == nullptr)
return;
destroyPipeline(device, preparePipeline, nullptr);
destroyPipeline(device, composePipeline, nullptr);
destroyLayout(device, prepareLayout, nullptr);
destroyLayout(device, composeLayout, nullptr);
destroySetLayout(device, prepareSet, nullptr);
destroySetLayout(device, composeSet, nullptr);
destroySampler(device, sampler, nullptr);
*this = D4rFsrRrPasses{};
}
// Everything the D3D12 side wrote before is visible to the pass, and the pass's writes to what
// the D3D12 side records after.
void full_barrier(VkCommandBuffer cmd)
{
VkMemoryBarrier memory = {VK_STRUCTURE_TYPE_MEMORY_BARRIER};
memory.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
memory.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;
barrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 1, &memory, 0, nullptr,
0, nullptr);
}
void record(VkCommandBuffer cmd, VkPipeline pipeline, VkPipelineLayout layout, const D4rFsrRrImage* images,
uint32_t count, uint32_t sampledCount, const void* constants, uint32_t constantBytes, uint32_t width,
uint32_t height)
{
VkDescriptorImageInfo infos[kPrepareInputs + kPrepareOutputs] = {};
VkWriteDescriptorSet writes[kPrepareInputs + kPrepareOutputs] = {};
for (uint32_t i = 0; i < count; ++i)
{
infos[i].sampler = i < sampledCount ? sampler : VK_NULL_HANDLE;
infos[i].imageView = images[i].view;
infos[i].imageLayout = images[i].layout;
writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[i].dstBinding = i;
writes[i].descriptorCount = 1;
writes[i].descriptorType =
i < sampledCount ? VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER : VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
writes[i].pImageInfo = &infos[i];
}
bindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
pushDescriptors(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, layout, 0, count, writes);
push(cmd, layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, constantBytes, constants);
dispatch(cmd, (width + 7) / 8, (height + 7) / 8, 1);
}
// images: the 10 sampled inputs, then the 8 storage outputs (fsr_rr_prepare.comp's bindings)
void record_prepare(VkCommandBuffer cmd, const D4rFsrRrImage* images, const D4rFsrRrPrepareConstants& constants,
uint32_t width, uint32_t height)
{
full_barrier(cmd);
record(cmd, preparePipeline, prepareLayout, images, kPrepareInputs + kPrepareOutputs, kPrepareInputs,
&constants, sizeof(constants), width, height);
full_barrier(cmd);
}
// images: denoised diffuse, denoised specular, diffuse albedo, specular albedo, residual, composed, noisy diffuse,
// noisy specular. haloLimit: fsr_rr_compose.comp's limit (0: off)
void record_compose(VkCommandBuffer cmd, const D4rFsrRrImage* images, uint32_t width, uint32_t height,
float haloLimit = 0.0f)
{
uint32_t limitBits;
std::memcpy(&limitBits, &haloLimit, sizeof(limitBits));
const uint32_t constants[4] = {width, height, limitBits, 0};
full_barrier(cmd);
record(cmd, composePipeline, composeLayout, images, kComposeImages, 0, constants, sizeof(constants), width,
height);
full_barrier(cmd);
}
};
+351
View File
@@ -0,0 +1,351 @@
"""Install pinned local NVIDIA dependencies without PowerShell or driver installation.
All downloads and backups stay outside the game. Runtime replacements are staged,
hash verified and rolled back on failure. No model generation is implicit.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path, PurePosixPath
import re
import shutil
import subprocess
import tempfile
import urllib.request
import uuid
PINS = {
'dlss': ('nvngx_dlss.dll', 'dlss/nvngx_dlss.dll',
'https://raw.githubusercontent.com/NVIDIA/DLSS/374959484e79a640feaba44c93ac8cfb0a03f5b5/lib/Windows_x86_64/rel/nvngx_dlss.dll',
'3975567b8943c53acce397f2b72380092f84f162d00b0d2c7d08a1025c563983'),
'dlssd': ('nvngx_dlssd.dll', 'rr/nvngx_dlssd.dll',
'https://raw.githubusercontent.com/NVIDIA/DLSS/374959484e79a640feaba44c93ac8cfb0a03f5b5/lib/Windows_x86_64/rel/nvngx_dlssd.dll',
'4bc7ea5fcb2f32cf86bc2cb072e8d2860914a0be511cbdcb2fc206c1d9d83b80'),
'ngx': ('_nvngx.dll', 'ngx/_nvngx.dll', None,
'66767018c36b3bab46398dade3adf173daa3730fda75965689ea848c9bc4e79b'),
}
# Optional: AMD's FSR Ray Regeneration denoiser (FidelityFX SDK 2.3), only for [RayReconstruction] Backend = fsr.
# AMD's file, never bundled; the same pin as scripts/windows/setup-fsr-rr.ps1.
FSR_DENOISER = ('amd_fidelityfx_denoiser_dx12.dll', 'amd_fidelityfx_denoiser_dx12.dll',
'https://raw.githubusercontent.com/GPUOpen-LibrariesAndSDKs/FidelityFX-SDK/v2.3.0/Kits/FidelityFX/signedbin/amd_fidelityfx_denoiser_dx12.dll',
'48f1e5888ba6a0a3d59a98b9751e37c392b0f7b8c223d0082d5c1f40642879d3')
DRIVER = ('https://us.download.nvidia.com/Windows/617.14/617.14-desktop-win10-win11-64bit-international-dch-whql.exe',
'63d3da5ed52547bf3fd86204bb9ea1455947b24e2aa2519f359126f9a6efe13f')
SEVENZIP = ('https://github.com/ip7z/7zip/releases/download/26.04/7zr.exe',
'256feca8e274e5da655e2a284fabafd9f554365eb164862089dacd4e8276d282')
def sha(path):
with path.open('rb') as stream:
return hashlib.file_digest(stream, 'sha256').hexdigest()
def external(path, game):
if path.resolve().is_relative_to(game.resolve()):
raise RuntimeError('Cache and backups must stay outside game directory: ' + str(path))
def verified_download(path, url, expected, offline=False):
if path.exists():
if path.is_file() and sha(path) == expected:
return path
if offline:
raise RuntimeError('Invalid offline cache: ' + str(path))
path.rename(path.with_name(path.name + '.bad-' + uuid.uuid4().hex))
if offline:
raise RuntimeError('Missing offline cache: ' + str(path))
path.parent.mkdir(parents=True, exist_ok=True)
temporary = path.with_name(path.name + '.part-' + uuid.uuid4().hex)
try:
print('DOWNLOAD ' + path.name, flush=True)
with urllib.request.urlopen(url, timeout=60) as response, temporary.open('xb') as out:
shutil.copyfileobj(response, out)
if sha(temporary) != expected:
raise RuntimeError('Download SHA256 mismatch: ' + path.name)
os.replace(temporary, path)
finally:
temporary.unlink(missing_ok=True)
return path
def ini_selects_fsr(runtime):
"""True when d4r.ini sets [RayReconstruction] Backend = fsr (comments and case ignored)."""
section = ''
for line in (runtime / 'd4r.ini').read_text(encoding='utf-8-sig').splitlines():
line = line.split(';', 1)[0].split('#', 1)[0].strip()
if line.startswith('[') and line.endswith(']'):
section = line[1:-1].strip().lower()
elif section == 'rayreconstruction' and '=' in line:
key, value = line.split('=', 1)
if key.strip().lower() == 'backend' and value.strip().lower() == 'fsr':
return True
return False
def select_backend(data, backend):
"""Return d4r.ini bytes with [RayReconstruction] Backend set to `backend`; everything else is preserved."""
bom = data.startswith(b'\xef\xbb\xbf')
text = data.decode('utf-8-sig')
newline = '\r\n' if '\r\n' in text else '\n'
result, in_rr, found, done = [], False, False, False
for line in text.splitlines(keepends=True):
stripped = line.strip()
if stripped.startswith('['):
if in_rr and not done:
result.append('Backend = ' + backend + newline)
done = True
in_rr = stripped.split(']', 1)[0][1:].strip().casefold() == 'rayreconstruction'
found = found or in_rr
elif in_rr and '=' in line and not stripped.startswith((';', '#')) and line.split('=', 1)[0].strip().casefold() == 'backend':
prefix, body = line.split('=', 1)
ending = '\r\n' if body.endswith('\r\n') else '\n' if body.endswith('\n') else ''
body = body[:len(body) - len(ending)]
comment = re.search(r'\s[;#]', body)
result.append(prefix + '= ' + backend + (body[comment.start():] if comment else '') + ending)
done = True
continue
result.append(line)
if in_rr and not done:
if result and not result[-1].endswith(('\n', '\r')):
result.append(newline)
result.append('Backend = ' + backend + newline)
elif not found:
if result and not result[-1].endswith(('\n', '\r')):
result.append(newline)
result.extend((newline, '[RayReconstruction]' + newline, 'Backend = ' + backend + newline))
return (b'\xef\xbb\xbf' if bom else b'') + ''.join(result).encode('utf-8')
def enable_fsr(runtime, backup_root):
"""Select the FSR backend in d4r.ini with an external backup; idempotent."""
config = runtime / 'd4r.ini'
original = config.read_bytes()
updated = select_backend(original, 'fsr')
if updated == original:
return None
backup_root.mkdir(parents=True, exist_ok=True)
backup = backup_root / ('fsr-config-' + uuid.uuid4().hex + '.ini')
with backup.open('xb') as stream:
stream.write(original)
temporary = config.with_name('d4r.ini.' + uuid.uuid4().hex + '.tmp')
with temporary.open('xb') as stream:
stream.write(updated)
if config.read_bytes() != original:
temporary.unlink()
raise RuntimeError('d4r.ini changed during installation; preserved current configuration')
os.replace(temporary, config)
return backup
def collect(cache, offline, fsr=False):
assets = []
for kind, (name, relative, url, expected) in PINS.items():
cached = cache / 'nvidia' / name
if kind == 'ngx' and not (cached.is_file() and sha(cached) == expected):
if offline:
raise RuntimeError('Missing or invalid offline NGX 617.14 core')
driver = verified_download(cache / 'nvidia-driver-617.14.exe', *DRIVER)
extractor = verified_download(cache / 'tools/7zr-26.04.exe', *SEVENZIP)
with tempfile.TemporaryDirectory(prefix='d4r-ngx-') as work:
subprocess.run([str(extractor), 'e', '-y', '-o' + work, str(driver),
'Display.Driver/_nvngx.dll'], check=True, timeout=600)
core = Path(work) / name
if sha(core) != expected:
raise RuntimeError('Extracted NGX core SHA256 mismatch')
cached.parent.mkdir(parents=True, exist_ok=True)
if cached.exists():
cached.rename(cached.with_name(name + '.bad-' + uuid.uuid4().hex))
staged = cached.with_name(name + '.part-' + uuid.uuid4().hex)
try:
shutil.copy2(core, staged)
if sha(staged) != expected:
raise RuntimeError('Staged core SHA256 mismatch')
os.replace(staged, cached)
finally:
staged.unlink(missing_ok=True)
else:
verified_download(cached, url, expected, offline)
assets.append((cached, relative, expected))
if fsr:
name, relative, url, expected = FSR_DENOISER
assets.append((verified_download(cache / 'amd' / name, url, expected, offline), relative, expected))
return assets
def transaction(runtime, assets, backup_root):
"""Back up every replacement before first mutation; preserve concurrent edits."""
entries, applied, staged_files = [], [], []
backup = backup_root / ('dlss-install-' + uuid.uuid4().hex)
try:
for source, relative, expected in assets:
if Path(relative).is_absolute() or '..' in Path(relative).parts:
raise RuntimeError('Invalid runtime destination')
destination = runtime / relative
if not destination.resolve().is_relative_to(runtime.resolve()):
raise RuntimeError('Runtime destination escapes runtime')
if destination.exists() and not destination.is_file():
raise RuntimeError('DLL destination is not a file: ' + str(destination))
previous = sha(destination) if destination.exists() else None
if previous == expected:
continue
original = backup / relative
if previous is not None:
original.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(destination, original)
if sha(original) != previous:
raise RuntimeError('Backup SHA256 mismatch')
entries.append(dict(path=str(destination), sha256=previous, installHash=expected,
backup=str(original) if previous is not None else None))
destination.parent.mkdir(parents=True, exist_ok=True)
staged = destination.with_name(destination.name + '.install-' + uuid.uuid4().hex)
staged_files.append(staged)
shutil.copy2(source, staged)
if sha(staged) != expected:
raise RuntimeError('Staged DLL SHA256 mismatch')
if entries:
backup.mkdir(parents=True, exist_ok=True)
(backup / 'backup-manifest.json').write_text(json.dumps({'files': entries}, indent=2) + '\n')
for entry, staged in zip(entries, staged_files):
destination = Path(entry['path'])
current = sha(destination) if destination.exists() else None
if current != entry['sha256']:
raise RuntimeError('Destination changed after backup: ' + str(destination))
os.replace(staged, destination)
applied.append(entry)
if sha(destination) != entry['installHash']:
raise RuntimeError('Installed DLL SHA256 mismatch')
return {'changed': len(entries), 'backup': str(backup) if entries else None,
'files': {relative: sha(runtime / relative) for _, relative, _ in assets}}
except BaseException:
errors = []
for entry in reversed(applied):
destination = Path(entry['path'])
try:
if not destination.is_file() or sha(destination) != entry['installHash']:
errors.append('Concurrent edit preserved: ' + str(destination))
continue
if entry['backup']:
original = Path(entry['backup'])
if sha(original) != entry['sha256']:
raise RuntimeError('Rollback backup changed')
staged = destination.with_name(destination.name + '.rollback-' + uuid.uuid4().hex)
staged_files.append(staged)
shutil.copy2(original, staged)
os.replace(staged, destination)
else:
destination.unlink()
except Exception as error:
errors.append(str(error))
if errors:
print('ROLLBACK ATTENTION: ' + '; '.join(errors), flush=True)
raise
finally:
for staged in staged_files:
staged.unlink(missing_ok=True)
def prepare_model_tools(bundle, cache):
"""Copy only verified clean inputs; generated products never become inputs."""
data = (bundle / 'rr-tools.json').read_bytes()
manifest = json.loads(data)
if (manifest.get('kind') != 'local-rr-model-tools' or manifest.get('modelsBundled') is not False
or manifest.get('nvidiaBinariesBundled') is not False):
raise RuntimeError('Unsupported model-tool manifest')
target = cache / ('rr-tools-' + hashlib.sha256(data).hexdigest()[:16])
if not target.resolve().is_relative_to(cache.resolve()):
raise RuntimeError('Model-tool cache escapes cache root')
files = manifest['files']
required = {'python/python.exe', 'recipe/tools/windows/build_rr_model.py',
'recipe/tools/windows/deploy_rr_model.py', 'recipe/engine/rr/native_kernels.txt'}
if not required.issubset(files):
raise RuntimeError('Incomplete model-tool bundle')
for name, expected in files.items():
parts = PurePosixPath(name)
if parts.is_absolute() or '..' in parts.parts or '\\' in name or ':' in name:
raise RuntimeError('Unsafe model-tool path')
source = bundle.joinpath(*parts.parts)
if not source.resolve().is_relative_to(bundle.resolve()) or sha(source) != expected:
raise RuntimeError('Model-tool input changed: ' + name)
destination = target.joinpath(*parts.parts)
if not destination.resolve().is_relative_to(target.resolve()):
raise RuntimeError('Model-tool cache path escaped')
if destination.exists():
if sha(destination) != expected:
raise RuntimeError('Model-tool cache changed; preserved: ' + str(destination))
else:
destination.parent.mkdir(parents=True, exist_ok=True)
with source.open('rb') as src, destination.open('xb') as out:
shutil.copyfileobj(src, out)
if sha(destination) != expected:
raise RuntimeError('Model-tool copy SHA256 mismatch')
# A stale or used cache is never swept into a package. Build reuse validates
# generated products separately through the existing builder's identity.
return target
def install_model(runtime, cache, backup, preset, enable):
metadata = json.loads((runtime / 'package.json').read_text(encoding='utf-8-sig'))
if set(metadata['targets']) != {'gfx1101'}:
raise RuntimeError('Automatic RR build currently requires gfx1101-only package')
tools = prepare_model_tools(runtime / 'model-tools', cache)
python = tools / 'python/python.exe'
recipe = tools / 'recipe'
output = recipe / 'build' / ('rr-' + preset.lower())
# Each builder child has its own 600s deadline and kills a timed-out tree.
subprocess.run([str(python), str(recipe / 'tools/windows/build_rr_model.py'),
'--dll', str(runtime / 'rr/nvngx_dlssd.dll'), '--preset', preset,
'--output', str(output), '--reuse'], check=True)
command = [str(python), str(recipe / 'tools/windows/deploy_rr_model.py'),
'--model', str(output), '--runtime', str(runtime), '--backup-root', str(backup)]
if enable:
command.append('--enable')
subprocess.run(command, check=True, timeout=120)
def main():
local = Path(os.environ.get('LOCALAPPDATA', str(Path.home()))) / 'd4r'
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--package-root', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--cache-root', type=lambda p: Path(p).resolve(), default=local / 'cache')
parser.add_argument('--backup-root', type=lambda p: Path(p).resolve(), default=local / 'backups')
parser.add_argument('--offline', action='store_true')
parser.add_argument('--yes', action='store_true')
parser.add_argument('--rr-preset', choices=('D', 'E'), help='build and deploy optional exact gfx1101 RR model')
parser.add_argument('--enable-rr', action='store_true', help='select generated native RR model in d4r.ini')
parser.add_argument('--enable-fsr', action='store_true', help='install the FSR denoiser and select Backend = fsr in d4r.ini')
parser.add_argument('--install-fsr', action='store_true',
help="also install AMD's pinned FSR Ray Regeneration denoiser (automatic when d4r.ini selects Backend = fsr)")
args = parser.parse_args()
runtime = args.package_root / 'd4r' if (args.package_root / 'd4r/d4r.ini').is_file() else args.package_root
if not (runtime / 'd4r.ini').is_file():
parser.error('Extract Windows release first; runtime d4r.ini missing')
game = runtime.parent
external(args.cache_root, game)
external(args.backup_root, game)
if args.enable_rr and not args.rr_preset:
parser.error('--enable-rr requires --rr-preset D or E')
pins = (runtime / 'dll-pins.txt').read_text(encoding='utf-8-sig')
for kind, (_, _, _, expected) in PINS.items():
if not re.search(r'^\s*' + kind + r'\s+' + expected + r'\s+\S+\s+\S+\s*$', pins, re.M | re.I):
raise RuntimeError('Release does not recognize pinned ' + kind)
if args.enable_fsr and args.rr_preset:
parser.error('--enable-fsr and --rr-preset select different RR backends')
fsr = args.install_fsr or args.enable_fsr or ini_selects_fsr(runtime)
if fsr and not all((runtime / 'fsr' / name).is_file() for name in ('d4r_vkd3d_core.dll', 'amdxc64.dll')):
parser.error("FSR Ray Regeneration needs this release's d4r/fsr payload (d4r_vkd3d_core.dll, amdxc64.dll); extract the complete ZIP")
if not args.yes and input('Download pinned DLSS/RR 310.9.1 and NGX 617.14 (~945 MiB archive)? [Y/n] ').lower().startswith('n'):
return
result = transaction(runtime, collect(args.cache_root, args.offline, fsr), args.backup_root)
print(json.dumps(result, indent=2))
if args.enable_fsr:
backup = enable_fsr(runtime, args.backup_root)
print(json.dumps({'fsrBackendSelected': True, 'configBackup': str(backup) if backup else None}, indent=2))
if args.rr_preset:
print('DLL installation verified. Building optional private RR model; a model failure leaves verified DLLs installed.', flush=True)
install_model(runtime, args.cache_root, args.backup_root, args.rr_preset, args.enable_rr)
if __name__ == '__main__':
main()
+139
View File
@@ -0,0 +1,139 @@
"""Synthetic noisy DLSS-RR-style scene with a known noise-free answer, in the rr_ngx_probe fixture format.
Left-handed view space, linear depth, render size == output size or output larger. Surfaces: a wall (stripes fixed in
WORLD space), a disc (a sphere's normals) and a sky band. The colour is (diffuse + specular albedo) * smooth lighting
times per-frame mean-preserving log-normal noise; the sky is noise-free. The camera may translate along world x
(--camera-speed per frame): the guides then carry the exact motion vectors (previous minus current, render pixels) and
the view matrix, and clean-<frame>.bin is the noise-free output-size image of that frame. clean.bin is frame 0's.
--extents W1xH1,W2xH2,... makes the render size change per frame (cycling, each within --size): the guides are written
at that frame's size and dynamic-extents.txt lists them (the probe's "dynamic-extents" mode).
No game, no NVIDIA runtime. Output stays under ignored build/.
"""
import argparse
import math
import sys
from pathlib import Path
repo = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(repo / '.tools/python/vendor'))
import numpy as np
FOV_DEGREES = 60.0
def scene(width, height, cam_x=0.0, prev_cam_x=None):
"""Returns depth, normal, diffuse, specular, clean colour, sky mask and motion (render pixels, prev - current)."""
if prev_cam_x is None:
prev_cam_x = cam_x
y, x = np.mgrid[0:height, 0:width].astype('f8')
u, v = (x + 0.5) / width * 2 - 1, 1 - (y + 0.5) / height * 2
aspect = width / height
tangent = math.tan(math.radians(FOV_DEGREES) / 2)
# camera-space ray scale: x_view = u * aspect * tangent * z, y_view = v * tangent * z
z = np.full((height, width), 10.0)
normal = np.zeros((height, width, 3)); normal[..., 2] = -1.0
diffuse = np.empty((height, width, 3)); diffuse[:] = (0.60, 0.50, 0.40)
# the disc: a world-fixed sphere of radius 0.9 centred at world (0.9, -0.5, 7.5)
centre = np.array([0.9, -0.5, 7.5]); radius = 0.9
ray = np.stack([u * aspect * tangent, v * tangent, np.ones_like(u)], axis=-1)
origin = np.array([cam_x, 0.0, 0.0])
oc = origin - centre
b = np.einsum('ijk,k->ij', ray, oc); a = np.einsum('ijk,ijk->ij', ray, ray); c = float(oc @ oc - radius * radius)
disc_term = b * b - a * c
hit = disc_term > 0
t = np.where(hit, (-b - np.sqrt(np.maximum(disc_term, 0))) / a, 0)
point = origin + ray * t[..., None]
disc = hit & (t > 0)
z[disc] = (t * ray[..., 2])[disc] # view z = t (the ray's z component is 1)
normal[disc] = ((point - centre) / radius)[disc]
world_x = cam_x + u * aspect * tangent * 10.0
stripe = (np.floor(world_x / 0.45).astype(int) % 2) == 0
diffuse[stripe & ~disc] = (0.35, 0.45, 0.55)
diffuse[disc] = (0.80, 0.25, 0.20)
sky = v > 0.7
z[sky] = 1.0e8; normal[sky] = 0.0; diffuse[sky] = 0.0; disc = disc & ~sky
specular = np.full((height, width, 3), 0.04); specular[sky] = 0.0
# lighting is a function of the surface point (world x, screen-independent y), so it moves with the scene
surf_x = np.where(disc, point[..., 0], world_x)
light = (1.5 + 0.8 * np.sin(surf_x * 2.2) * np.cos(y * 360.0 / height / 41.0))[..., None] * np.array([1.0, 0.92, 0.8])
clean = (diffuse + specular) * light
clean[sky] = (0.35, 0.55, 0.95)
# motion: previous minus current screen position of the same surface point, in render pixels
motion = np.zeros((height, width, 2))
dx = cam_x - prev_cam_x
if dx != 0.0:
depth = np.where(sky, 1.0, z)
motion[..., 0] = dx / (depth * tangent * aspect) * (width / 2.0)
motion[sky] = 0.0
return z, normal, diffuse, specular, clean, sky, motion
def half_bytes(a):
return np.ascontiguousarray(a.astype('<f2')).tobytes()
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--output', required=True, type=Path)
parser.add_argument('--size', default='640x360', help='render size (the feature creation size)')
parser.add_argument('--out-size', help='output size (default: render size)')
parser.add_argument('--frames', type=int, default=24)
parser.add_argument('--noise', type=float, default=0.6, help='log-normal sigma of the per-frame colour noise')
parser.add_argument('--camera-speed', type=float, default=0.0, help='camera translation along world x per frame')
parser.add_argument('--extents', help='per-frame render sizes WxH,WxH,... cycling (each within --size, aspect ratio kept)')
parser.add_argument('--seed', type=int, default=7)
args = parser.parse_args()
if not args.output.resolve().is_relative_to((repo / 'build').resolve()):
parser.error('output must stay under ignored build/')
args.output.mkdir(parents=True, exist_ok=False)
width, height = map(int, args.size.split('x'))
out_width, out_height = map(int, (args.out_size or args.size).split('x'))
extents = [tuple(map(int, item.split('x'))) for item in args.extents.split(',')] if args.extents else [(width, height)]
for ew, eh in extents:
if ew < 64 or eh < 64 or ew > width or eh > height:
parser.error('every extent must be at least 64 and within --size')
rng = np.random.default_rng(args.seed)
f = 1.0 / math.tan(math.radians(FOV_DEGREES) / 2)
near, far = 0.1, 1000.0
lines = [f'size {width} {height} {out_width} {out_height}']
extent_lines = []
for i in range(args.frames):
fw, fh = extents[i % len(extents)]
extent_lines.append(f'{fw} {fh}')
aspect = fw / fh
proj = [f / aspect, 0, 0, 0, 0, f, 0, 0, 0, 0, far / (far - near), 1, 0, 0, -near * far / (far - near), 0]
cam = args.camera_speed * i
z, normal, diffuse, specular, clean_render, sky, motion = scene(fw, fh, cam, cam - args.camera_speed if i else cam)
if (out_width, out_height) == (fw, fh):
clean_out, sky_out = clean_render, sky
else:
clean_out, sky_out = scene(out_width, out_height, cam)[4:6]
noise = np.exp(args.noise * rng.standard_normal((fh, fw, 3)) - 0.5 * args.noise ** 2)
noise[sky] = 1.0
color = np.concatenate([clean_render * noise, np.ones((fh, fw, 1))], axis=-1)
rgba = lambda c3: np.concatenate([c3, np.zeros((fh, fw, 1))], axis=-1)
(args.output / f'{i}-color.bin').write_bytes(half_bytes(color))
(args.output / f'{i}-motion.bin').write_bytes(half_bytes(motion))
(args.output / f'{i}-depth.bin').write_bytes(np.ascontiguousarray(z.astype('<f4')).tobytes())
(args.output / f'{i}-normals.bin').write_bytes(half_bytes(rgba(normal)))
rough = np.zeros((fh, fw, 4)); rough[..., 0] = 0.5
(args.output / f'{i}-roughness.bin').write_bytes(half_bytes(rough))
alb = lambda a: np.concatenate([np.round(a * 255), np.full((fh, fw, 1), 255)], axis=-1).astype('u1').tobytes()
(args.output / f'{i}-diffuse.bin').write_bytes(alb(diffuse))
(args.output / f'{i}-specular.bin').write_bytes(alb(specular))
view = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -cam, 0, 0, 1] # row-vector world -> view: translate by -camera
lines.append('frame %d 0 0 1 1 1 %d 0 ' % (i, 1 if i == 0 else 0) + ' '.join(f'{v:.9g}' for v in view + proj))
(args.output / f'clean-{i}.bin').write_bytes(half_bytes(np.concatenate([clean_out, np.ones((out_height, out_width, 1))], axis=-1)))
if (out_width, out_height) != (fw, fh): # the best a render-resolution result could be, before upscaling
(args.output / f'clean-render-{i}.bin').write_bytes(half_bytes(np.concatenate([clean_render, np.ones((fh, fw, 1))], axis=-1)))
if i == 0:
(args.output / 'clean.bin').write_bytes((args.output / 'clean-0.bin').read_bytes())
np.save(args.output / 'sky.npy', sky_out)
(args.output / 'frames.txt').write_text('\n'.join(lines) + '\n')
if args.extents:
(args.output / 'dynamic-extents.txt').write_text('\n'.join(extent_lines) + '\n')
print('wrote', args.output)
if __name__ == '__main__':
main()
+29
View File
@@ -14,6 +14,7 @@
#include <atomic>
#include <memory>
#include <mutex>
#include <array>
namespace d4r::win {
inline std::string env_path(const char* name) {
@@ -312,6 +313,34 @@ public:
std::lock_guard<std::mutex> lock(rt_->mutex); cleanup();
}
Feature(const Feature&) = delete;
// Query the loaded SR library, using NVIDIA's optimal-settings callback contract.
// An unavailable/invalid query keeps the conservative creation-size-only range.
std::array<unsigned, 4> render_limits() {
std::lock_guard<std::mutex> lock(rt_->mutex);
rt_->current();
std::array<unsigned, 4> limits{width, height, width, height};
void* caps = nullptr;
if (rt_->capabilities(&caps) != 1 || !caps) return limits;
struct Guard { Runtime& rt; void* p; ~Guard() { (void)rt.destroy(p); } } guard{*rt_, caps};
void* callback = nullptr;
if (d4r_ngx_get_void(caps, "DLSSOptimalSettingsCallback", &callback) != 1 || !callback) return limits;
d4r_ngx_set_uint(caps, "Width", ngx::uint_value(parameters_, "OutWidth"));
d4r_ngx_set_uint(caps, "Height", ngx::uint_value(parameters_, "OutHeight"));
d4r_ngx_set_int(caps, "PerfQualityValue", ngx::int_value(parameters_, "PerfQualityValue"));
d4r_ngx_set_int(caps, "RTXValue", 0);
using Optimal = unsigned(*)(void*);
if (reinterpret_cast<Optimal>(callback)(caps) != 1) return limits;
const unsigned optimalWidth = ngx::uint_value(caps, "OutWidth");
const unsigned optimalHeight = ngx::uint_value(caps, "OutHeight");
const std::array<unsigned, 4> queried{
ngx::uint_value(caps, "DLSS.Get.Dynamic.Min.Render.Width", optimalWidth),
ngx::uint_value(caps, "DLSS.Get.Dynamic.Min.Render.Height", optimalHeight),
ngx::uint_value(caps, "DLSS.Get.Dynamic.Max.Render.Width", optimalWidth),
ngx::uint_value(caps, "DLSS.Get.Dynamic.Max.Render.Height", optimalHeight)};
if (queried[0] && queried[1] && queried[2] >= queried[0] && queried[3] >= queried[1]) limits = queried;
std::fprintf(stderr, "D4R_SR_RENDER_LIMITS min=%ux%u max=%ux%u\n", limits[0], limits[1], limits[2], limits[3]);
return limits;
}
void drain() {
if (!std::getenv("D4R_ASYNC_INTEROP")) return;
wait_worker();
+27 -2
View File
@@ -13,6 +13,8 @@
#include "hip_api.h"
#include "ngx_parameters.h"
#include <bcrypt.h>
#include <cmath>
#include <cstdlib>
#include <fstream>
#include <mutex>
#include <optional>
@@ -25,10 +27,14 @@ struct Settings {
unsigned preset = 11; // K
bool nativeFp8 = false;
bool asyncInterop = false, validateOutput = false, verbose = false, showWatermark = false;
bool batchInputCopies = false;
std::wstring cacheDir;
std::wstring nativeDir;
std::string rrBackend = "off";
std::wstring rrModelDir;
std::string fsrStabilityBias = "auto"; // [RayReconstruction] FsrStabilityBias: auto (AMD's default 1) or a number
std::string fsrDetail; // [RayReconstruction] FsrDetail: 0-1 share of the colour that bypasses the denoiser
std::wstring fsrDenoiser; // [RayReconstruction] FsrDenoiser: AMD's amd_fidelityfx_denoiser_dx12.dll (default: beside the shim)
};
struct State {
bool active = false;
@@ -117,6 +123,7 @@ inline Settings read_settings(const std::vector<IniEntry>& ini, std::vector<std:
} else if (name == "kernels.nativefp8") settings.nativeFp8 = flag(entry, false);
else if (name == "dlss.showwatermark") settings.showWatermark = flag(entry, false);
else if (name == "interop.asyncinterop") settings.asyncInterop = flag(entry, false);
else if (name == "interop.batchinputcopies") settings.batchInputCopies = flag(entry, false);
else if (name == "debug.validateoutput") settings.validateOutput = flag(entry, false);
else if (name == "debug.log") {
const auto value = lower(entry.value);
@@ -132,8 +139,18 @@ inline Settings read_settings(const std::vector<IniEntry>& ini, std::vector<std:
if (value.empty() || value == "auto" || value == "off") settings.rrBackend = "off";
else if (value == "native" || value == "fsr") {
settings.rrBackend = value;
if (value == "fsr") notes.push_back("d4r.ini: native Windows FSR RR is unavailable; RR requests will be rejected");
if (value == "fsr") notes.push_back("d4r.ini: native Windows FSR RR is experimental: it runs AMD's denoiser on a private vkd3d-proton device (d4r\\fsr) and needs AMD's amd_fidelityfx_denoiser_dx12.dll");
} else { settings.rrBackend = "off"; notes.push_back("d4r.ini: [RayReconstruction] Backend must be off, native or fsr; using off"); }
} else if (name == "rayreconstruction.fsrstabilitybias" || name == "rayreconstruction.fsrdetail") {
const bool bias = name == "rayreconstruction.fsrstabilitybias";
const auto value = lower(entry.value);
char* end = nullptr; const float number = std::strtof(value.c_str(), &end);
const bool numeric = !value.empty() && end == value.c_str() + value.size() && std::isfinite(number) && number >= 0.0f && (bias || number <= 1.0f);
if (bias && (value.empty() || value == "auto")) settings.fsrStabilityBias = "auto";
else if (numeric) (bias ? settings.fsrStabilityBias : settings.fsrDetail) = value;
else notes.push_back(std::string("d4r.ini: [RayReconstruction] ") + entry.key + " must be " + (bias ? "auto or a number >= 0" : "a number from 0 to 1") + ", not '" + entry.value + "'; using the default");
} else if (name == "rayreconstruction.fsrdenoiser") {
settings.fsrDenoiser = !entry.value.empty() && lower(entry.value) != "auto" ? diag::wide(entry.value) : L"";
} else if (name == "rayreconstruction.modeldir") {
settings.rrModelDir = !entry.value.empty() && lower(entry.value) != "auto" ? diag::wide(entry.value) : L"";
} else notes.push_back("d4r.ini: [" + entry.section + "] " + entry.key + " is not a Windows setting; ignored");
@@ -313,6 +330,13 @@ inline void apply(const std::filesystem::path& dir, const Settings& settings) {
const std::pair<const wchar_t*, std::optional<std::wstring>> values[] = {
{L"D4R_RR_BACKEND", settings.rrBackend == "off" ? unset : std::optional<std::wstring>(diag::wide(settings.rrBackend))},
{L"D4R_RR_MODEL_DIR", settings.rrBackend == "native" ? std::optional<std::wstring>(rr_model_directory(dir, settings).wstring()) : unset},
{L"D4R_FSR_VKD3D_CORE", settings.rrBackend == "fsr" ? p(L"fsr\\d4r_vkd3d_core.dll") : unset},
{L"D4R_FSR_AMDXC_SHIM", settings.rrBackend == "fsr" ? p(L"fsr\\amdxc64.dll") : unset},
{L"D4R_FSR_RR_DLL", settings.rrBackend == "fsr" ? (settings.fsrDenoiser.empty() ? p(L"amd_fidelityfx_denoiser_dx12.dll") :
std::optional<std::wstring>(std::filesystem::absolute(std::filesystem::path(settings.fsrDenoiser).is_relative() ? dir / settings.fsrDenoiser : std::filesystem::path(settings.fsrDenoiser)).lexically_normal().wstring())) : unset},
{L"D4R_FSR_RR_STABILITY_BIAS", settings.rrBackend == "fsr" && settings.fsrStabilityBias != "auto" ? std::optional<std::wstring>(diag::wide(settings.fsrStabilityBias)) : unset},
{L"D4R_FSR_RR_DETAIL", settings.rrBackend == "fsr" && !settings.fsrDetail.empty() ? std::optional<std::wstring>(diag::wide(settings.fsrDetail)) : unset},
{L"D4R_FSR_CACHE_DIR", settings.rrBackend == "fsr" ? std::optional<std::wstring>((cache / L"vkd3d").wstring()) : unset},
{L"D4R_HIP_ROOT", p(L"hip")}, {L"HIP_PATH", p(L"hip")},
{L"D4R_NVCUDA_DLL", p(L"zluda\\nvcuda.dll")}, {L"ZLUDA_CUDA_LIB", p(L"zluda\\nvcuda.dll")},
{L"D4R_NVAPI_DLL", std::optional<std::wstring>(compatNvapi.wstring())},
@@ -331,11 +355,12 @@ inline void apply(const std::filesystem::path& dir, const Settings& settings) {
{L"D4R_QUIET_API", settings.verbose ? unset : on},
{L"D4R_VALIDATE_OUTPUT", settings.validateOutput ? on : unset},
{L"D4R_ASYNC_INTEROP", settings.asyncInterop ? on : unset},
{L"D4R_BATCH_INPUT_COPIES", settings.batchInputCopies ? on : unset},
{L"D4R_SHIM_WATERMARK", settings.showWatermark ? on : unset},
{L"D4R_PROFILE_STAGES", unset}, {L"D4R_ZLUDA_PROFILE", unset}, {L"D4R_ZLUDA_PROFILE_DEFERRED", unset},
{L"D4R_ZLUDA_PROFILE_ALLOW_LEGACY", unset}, {L"D4R_ZLUDA_PROFILE_EVERY", unset},
{L"D4R_PROFILE_COMMAND_HOOKS", unset}, {L"D4R_ZLUDA_PROFILE_API", unset}, {L"D4R_PROFILE_GPU_BOUNDARY", unset},
{L"D4R_DISABLE_INTEROP_LIST_CACHE", unset}, {L"D4R_BATCH_INPUT_COPIES", unset}, {L"D4R_INTEROP_VERIFY", unset},
{L"D4R_DISABLE_INTEROP_LIST_CACHE", unset}, {L"D4R_INTEROP_VERIFY", unset},
{L"D4R_DIAG_DIR", unset}, {L"ZLUDA_LOG_DIR", unset},
{L"ZLUDA_CACHE_DIR", std::optional<std::wstring>(cache.wstring())},
{L"PATH", std::optional<std::wstring>((dir / L"hip\\bin").wstring() + L";" + (dir / L"zluda").wstring() +
+31 -2
View File
@@ -46,6 +46,16 @@ int wmain(int argc, wchar_t** argv) {
expect(s.preset == 12 && has_note(notes, "not validated"), "Model = 12 selects NGX preset 12 with limitation note");
s = settings("[Interop]\nAsyncInterop = yes\n[Debug]\nValidateOutput = on\nLog = verbose\n", notes);
expect(s.asyncInterop && s.validateOutput && s.verbose && notes.empty(), "boolean spellings and Log = verbose");
s = settings("[Interop]\nBatchInputCopies = yes\n", notes);
expect(s.batchInputCopies && notes.empty(), "input batching selected through player INI");
s.cacheDir = (dir / L"cache").wstring(); player::apply(dir, s);
expect(std::getenv("D4R_BATCH_INPUT_COPIES") != nullptr, "player environment enables input batching");
s = settings("[Interop]\nBatchInputCopies = invalid\n", notes);
expect(!s.batchInputCopies && has_note(notes, "BatchInputCopies must be true or false"), "invalid batching falls back to unbatched with a note");
s.cacheDir = (dir / L"cache").wstring(); player::apply(dir, s);
expect(std::getenv("D4R_BATCH_INPUT_COPIES") == nullptr, "player environment clears earlier input batching");
s = settings("", notes);
expect(!s.batchInputCopies && notes.empty(), "older INIs retain unbatched behavior");
s = settings("[Debug]\nValidateOutput = maybe\nLog = loud\n", notes);
expect(!s.validateOutput && !s.verbose && has_note(notes, "must be true or false, not 'maybe'") &&
has_note(notes, "must be normal or verbose"), "invalid values fall back with plain notes");
@@ -77,11 +87,30 @@ int wmain(int argc, wchar_t** argv) {
expect(std::string(std::getenv("D4R_RR_BACKEND")) == "native" &&
std::filesystem::path(_wgetenv(L"D4R_RR_MODEL_DIR")) == player::rr_model_directory(dir, s), "native RR environment selected without a launcher");
s = settings("[RayReconstruction]\nBackend = fsr\n", notes);
expect(s.rrBackend == "fsr" && has_note(notes, "FSR RR is unavailable"), "unavailable FSR selection remains explicit");
expect(s.rrBackend == "fsr" && has_note(notes, "FSR RR is experimental"), "FSR selection is reported as experimental");
s = settings("[RayReconstruction]\nBackend = fsr\nFsrDenoiser = amd\\denoiser.dll\n", notes);
s.cacheDir = (dir / L"cache").wstring(); player::apply(dir, s);
expect(std::string(std::getenv("D4R_RR_BACKEND")) == "fsr" &&
std::filesystem::path(_wgetenv(L"D4R_FSR_RR_DLL")) == std::filesystem::absolute(dir / L"amd" / L"denoiser.dll").lexically_normal() &&
std::filesystem::path(_wgetenv(L"D4R_FSR_VKD3D_CORE")) == dir / L"fsr" / L"d4r_vkd3d_core.dll" &&
std::filesystem::path(_wgetenv(L"D4R_FSR_AMDXC_SHIM")) == dir / L"fsr" / L"amdxc64.dll" &&
std::filesystem::path(_wgetenv(L"D4R_FSR_CACHE_DIR")) == dir / L"cache" / L"vkd3d" && !std::getenv("D4R_RR_MODEL_DIR"),
"FSR environment selected from d4r.ini without a launcher");
s = settings("[RayReconstruction]\nBackend = fsr\nFsrStabilityBias = 0\nFsrDetail = 0.25\n", notes);
s.cacheDir = (dir / L"cache").wstring(); player::apply(dir, s);
expect(notes.empty() || has_note(notes, "experimental"), "FSR tuning keys are recognized");
expect(std::string(std::getenv("D4R_FSR_RR_STABILITY_BIAS")) == "0" && std::string(std::getenv("D4R_FSR_RR_DETAIL")) == "0.25",
"FSR tuning reaches the environment");
s = settings("[RayReconstruction]\nBackend = fsr\nFsrStabilityBias = auto\nFsrDetail = 2\n", notes);
s.cacheDir = (dir / L"cache").wstring(); player::apply(dir, s);
expect(has_note(notes, "FsrDetail must be a number from 0 to 1") && !std::getenv("D4R_FSR_RR_STABILITY_BIAS") && !std::getenv("D4R_FSR_RR_DETAIL"),
"auto bias leaves AMD's default and an out-of-range detail is rejected");
s = settings("[RayReconstruction]\nBackend = typo\n", notes);
expect(s.rrBackend == "off" && has_note(notes, "Backend must be"), "invalid RR backend stays off");
s = settings("", notes); s.cacheDir = (dir / L"cache").wstring(); player::apply(dir, s);
expect(!std::getenv("D4R_RR_BACKEND") && !std::getenv("D4R_RR_MODEL_DIR"), "default RR off clears stale selection");
expect(!std::getenv("D4R_RR_BACKEND") && !std::getenv("D4R_RR_MODEL_DIR") && !std::getenv("D4R_FSR_RR_DLL") &&
!std::getenv("D4R_FSR_VKD3D_CORE") && !std::getenv("D4R_FSR_AMDXC_SHIM") && !std::getenv("D4R_FSR_CACHE_DIR") &&
!std::getenv("D4R_FSR_RR_STABILITY_BIAS") && !std::getenv("D4R_FSR_RR_DETAIL"), "default RR off clears stale selection");
std::filesystem::create_directories(dir / L"nvapi");
{ std::ofstream(dir / L"nvapi" / L"nvapi64.dll", std::ios::binary) << "generic nvapi fixture"; }
@@ -0,0 +1,72 @@
"""Build bounded synthetic dynamic-size RR references with the standalone engine.
Reference resets at each size change; combined NGX frames deliberately omit that
reset to verify the adapter's implicit history reset. This is engine consistency,
not an NVIDIA image-quality reference. No game files are read or written.
"""
import argparse
import json
import os
from pathlib import Path
import shutil
import struct
import subprocess
import sys
def main():
repo = Path(__file__).resolve().parents[2]
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--runner', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--model', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--output', required=True, type=lambda p: Path(p).resolve())
args = parser.parse_args()
if args.output.exists() or not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('use a fresh directory under ignored build/')
args.output.mkdir(parents=True)
inputs, reference = args.output / 'inputs', args.output / 'reference'
inputs.mkdir(); reference.mkdir()
lines, extents = ['size 96 96 192 192'], []
env = {key: value for key, value in os.environ.items() if not key.startswith('D4R_')}
env['D4R_RR_TIME_REPEAT'] = '1'
index = 0
for segment, (width, height) in enumerate(((96, 96), (64, 64), (96, 96), (72, 80))):
source, result = args.output / f'segment-{segment}', args.output / f'reference-{segment}'
subprocess.run([sys.executable, str(repo / 'scripts/windows/rr-synthetic-inputs.py'), str(source),
'--width', str(width), '--height', str(height), '--frames', '2',
'--output-width', '192', '--output-height', '192'], check=True, timeout=60)
# Exercise optional-view rebinding across reconfiguration and an absent
# guide frame. Fixture uploads and standalone replay use the same values.
for frame in (1,):
for name, texel in (('hitdistance', struct.pack('<f', 2.0)),
('disocclusion', struct.pack('<e', 0.0)),
('sss', struct.pack('<e', 0.125)),
('particles', struct.pack('<4e', 0.2, 0.3, 0.4, 1.0))):
(source / f'{frame}-{name}.bin').write_bytes(texel * (width * height))
result.mkdir()
with (args.output / f'segment-{segment}.log').open('wb') as log:
subprocess.run([str(args.runner), str(args.model), str(source), str(result)],
env=env, stdout=log, stderr=subprocess.STDOUT, check=True, timeout=180)
frames = (source / 'frames.txt').read_text().splitlines()[1:]
for local, line in enumerate(frames):
fields = line.split()
fields[1] = str(index)
if segment and local == 0:
fields[7] = '0' # omit caller reset on a size change
lines.append(' '.join(fields))
extents.append([width, height])
for path in source.glob(f'{local}-*.bin'):
shutil.copy2(path, inputs / (str(index) + path.name[len(str(local)):]))
shutil.copy2(result / f'{local}-out.bin', reference / f'{index}-out.bin')
index += 1
(inputs / 'frames.txt').write_text('\n'.join(lines) + '\n')
(inputs / 'dynamic-extents.txt').write_text(''.join(f'{w} {h}\n' for w, h in extents))
report = dict(kind='synthetic-dynamic-rr-engine-consistency', extents=extents,
frames=index, callerResetOnSizeChange=False, qualityReference=False,
model=str(args.model), runner=str(args.runner), gameAccessed=False)
(args.output / 'fixture.json').write_text(json.dumps(report, indent=2) + '\n')
print(json.dumps(report, indent=2))
if __name__ == '__main__':
main()
+83 -4
View File
@@ -17,6 +17,11 @@ def main():
parser = argparse.ArgumentParser(description=__doc__)
for name in ('base-zip', 'shim', 'output', 'archive'):
parser.add_argument('--' + name, required=True, type=lambda value: Path(value).resolve())
parser.add_argument('--rr-tools', required=True, type=lambda value: Path(value).resolve())
parser.add_argument('--native-fix', type=lambda value: Path(value).resolve(),
help='directory of rebuilt gfx1101 K layer .hsaco files that replace same-named packaged objects')
parser.add_argument('--fsr-payload', type=lambda value: Path(value).resolve(),
help='build_fsr_payload.py output to add as d4r/fsr (experimental FSR RR; AMD denoiser never bundled)')
args = parser.parse_args()
repo = Path(__file__).resolve().parents[2]
build = (repo / 'build').resolve()
@@ -26,8 +31,10 @@ def main():
if args.archive.is_relative_to(args.output):
parser.error('archive must be outside the package directory')
base_hash = sha(args.base_zip)
if base_hash != 'e93be288f4013616cc2229be0132b40395d5031d8733bfa63886ba75ce1389ca':
parser.error('base must be the verified local v3 ZIP')
# verified local ZIPs this refresher may start from: v3, and v8 (build/d4r-windows-gfx1101-local-v8.zip)
if base_hash not in ('e93be288f4013616cc2229be0132b40395d5031d8733bfa63886ba75ce1389ca',
'962530d1a914268cc6f9d2ec908185f8e4d9e5baafca6800d77ebc3281a647fe'):
parser.error('base must be a verified local ZIP (v3 or v8)')
with zipfile.ZipFile(args.base_zip) as base:
manifest = json.loads(base.read('d4r/package.json').decode('utf-8-sig'))
if not manifest.get('localDevelopment') or set(manifest['targets']) != {'gfx1101'}:
@@ -60,7 +67,76 @@ def main():
raise RuntimeError('Overlay is not part of the validated package layout: ' + name)
shutil.copy2(source, args.output / name)
records[name] = {'path': name, 'sha256': sha(source).upper(), 'version': None}
prohibited = {'nvngx_dlss.dll', 'nvngx_dlssd.dll'}
native_report = None
if args.native_fix:
# Replace named K layer code objects with rebuilt ones (e.g. the pwin_pos.h barrier fix). Each must already be part
# of the package's gfx1101 native folder and must be a gfx1101 AMDGPU ELF (e_machine 224, EF_AMDGPU_MACH byte 70).
replaced = {}
for source in sorted(args.native_fix.glob('*.hsaco')):
relative = 'd4r/native/gfx1101/' + source.name
data = source.read_bytes()
if relative not in records or data[:4] != b'\x7fELF' or data[4] != 2 or data[18] != 0xe0 or data[48] != 70:
raise RuntimeError('Native fix is not a replacement gfx1101 code object: ' + source.name)
shutil.copy2(source, args.output / relative)
replaced[relative] = {'before': records[relative]['sha256'], 'after': sha(source).upper()}
records[relative] = {'path': relative, 'sha256': sha(source).upper(), 'version': None}
if not replaced:
raise RuntimeError('--native-fix directory has no .hsaco files')
native_report = replaced
installer = repo / 'tools/windows/install_dependencies.py'
shutil.copy2(installer, args.output / 'install-dependencies.py')
records['install-dependencies.py'] = {'path': 'install-dependencies.py', 'sha256': sha(installer).upper(), 'version': None}
fsr_report = None
if args.fsr_payload:
# Experimental FSR RR: d4r-built vkd3d-proton core (LGPL, SOURCE.txt is the source offer) and d4r's amdxc64 shim.
payload = json.loads((args.fsr_payload / 'fsr-payload.json').read_text(encoding='utf-8'))
if payload.get('kind') != 'd4r-fsr-payload' or payload.get('amdDenoiserBundled') is not False:
raise RuntimeError('Unsupported FSR payload manifest')
additions = {}
for name, digest in payload['files'].items():
source = args.fsr_payload / name
if sha(source) != digest:
raise RuntimeError('FSR payload file changed: ' + name)
if name.startswith('fsr/'):
additions['d4r/' + name] = source
elif name.startswith('licenses/'):
additions['d4r/' + name] = source
elif name == 'SOURCE.txt':
additions['d4r/fsr/SOURCE.txt'] = source
for patch in sorted((repo / 'patches/vkd3d-proton-windows').glob('*.patch')):
additions['d4r/fsr/patches/' + patch.name] = patch
for relative, source in additions.items():
if relative in records:
raise RuntimeError('FSR payload path already in package: ' + relative)
destination = args.output / relative
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, destination)
records[relative] = {'path': relative, 'sha256': sha(source).upper(), 'version': None}
fsr_report = {'payloadManifestSha256': sha(args.fsr_payload / 'fsr-payload.json'), 'files': sorted(additions),
'vkd3dProtonCommit': payload['vkd3dProtonCommit'], 'amdDenoiserBundled': False}
# Consume the pristine archive, never an extracted/used tool directory.
if sha(args.rr_tools) != '297289a57a82142eb952479c99112b1c352a6753eb15bc6b2431bf686aed9b22':
raise RuntimeError('Requires verified clean portable-tools v2 ZIP')
with zipfile.ZipFile(args.rr_tools) as tools:
tool_manifest = json.loads(tools.read('rr-tools.json'))
expected_files = tool_manifest['files']
names = [entry.filename for entry in tools.infolist() if not entry.is_dir()]
if len(names) != len(set(names)) or set(names) != set(expected_files) | {'rr-tools.json'}:
raise RuntimeError('Portable-tools ZIP entries differ from manifest')
for name in names:
parts = PurePosixPath(name)
if parts.is_absolute() or '..' in parts.parts or '\\' in name or ':' in name:
raise RuntimeError('Unsafe portable-tools path')
data = tools.read(name)
digest = hashlib.sha256(data).hexdigest()
if name in expected_files and digest != expected_files[name]:
raise RuntimeError('Portable-tools hash mismatch: ' + name)
relative = 'd4r/model-tools/' + name
destination = args.output / relative
destination.parent.mkdir(parents=True, exist_ok=True)
destination.write_bytes(data)
records[relative] = {'path': relative, 'sha256': digest.upper(), 'version': None}
prohibited = {'nvngx_dlss.dll', 'nvngx_dlssd.dll', 'amd_fidelityfx_denoiser_dx12.dll'}
for path in args.output.rglob('*'):
if path.is_file() and (path.name.lower() in prohibited or path.suffix.lower() in ('.spv', '.ptx', '.cubin', '.weights')):
raise RuntimeError('Proprietary/model artifact cannot be bundled: ' + str(path))
@@ -69,7 +145,10 @@ def main():
files=list(records.values()),
localRefresh={'baseZipSha256': base_hash, 'binaryProvenance': 'local modified build; hashes authoritative',
'overlays': {name: sha(path) for name, path in overlays.items()},
'rr': 'experimental synthetic validation; disabled by default; models generated separately'})
'rrToolsZipSha256': sha(args.rr_tools),
'installerSha256': sha(installer),
'fsrPayload': fsr_report, 'nativeFix': native_report,
'rr': 'experimental synthetic validation; optional local D/E model generation; disabled by default'})
manifest['targets']['gfx1101']['binarySourceCommit'] = None
manifest['targets']['gfx1101']['hardwareValidation'] = 'SR prior limited evidence; native RR synthetic NGX lifecycle only; no game RR or performance parity'
(args.output / 'd4r/package.json').write_text(json.dumps(manifest, indent=2) + '\n')
+101 -15
View File
@@ -45,6 +45,8 @@ struct Layout {
{"sss", VK_FORMAT_R16_SFLOAT, 2, 0, 0, false},
{"particles", VK_FORMAT_R16G16B16A16_SFLOAT, 8, 0, 0, false}}};
VkDeviceSize bytes = 0, outputOffset = 0, outputPitch = 0;
VkFormat outputFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
uint32_t outputBytes = 8;
std::filesystem::path inputs, output, reference;
std::vector<Frame> frames;
// Runtime layout construction performs no fixture I/O. Both callers use
@@ -63,14 +65,14 @@ struct Layout {
bytes += plane.pitch * rh;
}
outputOffset = (bytes + 511) & ~VkDeviceSize(511);
outputPitch = (VkDeviceSize(ow) * 8 + 255) & ~VkDeviceSize(255);
outputPitch = (VkDeviceSize(ow) * outputBytes + 255) & ~VkDeviceSize(255);
bytes = outputOffset + outputPitch * oh;
}
Layout(const std::string& inputDir, const std::string& outputDir, const std::string& referenceDir)
: inputs(inputDir), output(outputDir), reference(referenceDir) {
std::ifstream f(inputs / "frames.txt"); std::string token;
if (!(f >> token >> rw >> rh >> ow >> oh) || token != "size" || rw < 32 || rh < 32 ||
rw > 1024 || rh > 1024 || !ow || !oh || ow > 2560 || oh > 1440)
rw > 2560 || rh > 1440 || !ow || !oh || ow > 2560 || oh > 1440)
throw std::runtime_error("Invalid bounded RR replay geometry");
while (f >> token) {
Frame frame{}; int reset, mode;
@@ -119,21 +121,34 @@ struct Layout {
}
}
void validate(const void* source, unsigned frame) const {
std::vector<char> packed(size_t(ow) * oh * 8);
std::vector<char> packed(size_t(ow) * oh * outputBytes);
for (uint32_t y = 0; y < oh; ++y)
std::memcpy(packed.data() + size_t(y) * ow * 8,
static_cast<const char*>(source) + outputOffset + y * outputPitch, size_t(ow) * 8);
std::memcpy(packed.data() + size_t(y) * ow * outputBytes,
static_cast<const char*>(source) + outputOffset + y * outputPitch, size_t(ow) * outputBytes);
double low = std::numeric_limits<double>::infinity(), high = -low;
for (size_t i = 0; i < packed.size() / 2; ++i) {
uint16_t bits; std::memcpy(&bits, packed.data() + i * 2, 2);
const double value = d4r::diag::binary16::half_value(bits);
for (size_t i = 0; i < size_t(ow) * oh * 4; ++i) {
double value;
if (outputBytes == 16) { float v; std::memcpy(&v, packed.data() + i * 4, 4); value = v; }
else { uint16_t bits; std::memcpy(&bits, packed.data() + i * 2, 2); value = d4r::diag::binary16::half_value(bits); }
if (!std::isfinite(value)) throw std::runtime_error("RR output contains NaN/Inf");
if (i % 4 != 3) { low = std::min(low, value); high = std::max(high, value); }
}
if (!(high - low > 1e-5)) throw std::runtime_error("RR output is uniform");
const auto name = std::to_string(frames.at(frame).id) + "-out.bin";
if (!reference.empty() && read(reference / name) != packed)
throw std::runtime_error("Bridged RR output differs from standalone replay");
if (!reference.empty()) {
auto expected = read(reference / name);
if (outputBytes == 16) {
if (expected.size() != size_t(ow) * oh * 8) throw std::runtime_error("Invalid half output reference size");
std::vector<char> widened(packed.size());
for (size_t i = 0; i < expected.size() / 2; ++i) {
uint16_t bits; std::memcpy(&bits, expected.data() + i * 2, 2);
const float value = float(d4r::diag::binary16::half_value(bits));
std::memcpy(widened.data() + i * 4, &value, 4);
}
expected = std::move(widened);
}
if (expected != packed) throw std::runtime_error("Bridged RR output differs from standalone replay");
}
std::ofstream file(output / name, std::ios::binary); file.write(packed.data(), std::streamsize(packed.size()));
if (!file) throw std::runtime_error("RR output write failed");
std::printf("RR_BRIDGED_FRAME id=%u finite=1 nonuniform=1 standalone_exact=%u output=%ux%u\n",
@@ -187,7 +202,7 @@ struct D3D12Frames {
};
for (size_t i = 0; i < inputs.size(); ++i) if (l.planes[i].enabled)
create(i == 2 && depthFormat != DXGI_FORMAT_UNKNOWN ? depthFormat : format(l.planes[i].format), l.rw, l.rh, false, inputs[i].GetAddressOf());
create(DXGI_FORMAT_R16G16B16A16_FLOAT, l.ow, l.oh, true, output.GetAddressOf());
create(format(l.outputFormat), l.ow, l.oh, true, output.GetAddressOf());
}
void recordInputs(ID3D12GraphicsCommandList* cb, ID3D12Resource* upload, ID3D12Resource* shared, uint32_t optionalMask = 15) {
for (size_t i = 0; i < inputs.size(); ++i) {
@@ -213,7 +228,18 @@ struct D3D12Frames {
cb->CopyTextureRegion(&image, 0, 0, 0, &src, nullptr);
barrier(cb, input, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);
auto dst = placed(shared, plane.offset, plane.pitch, copyFormat, layout.rw, layout.rh);
cb->CopyTextureRegion(&dst, 0, 0, 0, &image, nullptr);
// A dynamic subrect can occupy a creation-sized fixture texture.
// Only depth-stencil resources require whole-subresource copies.
Microsoft::WRL::ComPtr<ID3D12Device> owner; checkD3D(input->GetDevice(IID_PPV_ARGS(&owner)));
D3D12_RESOURCE_DESC description{};
#if defined(__MINGW32__)
input->GetDesc(&description);
#else
description = input->GetDesc();
#endif
D3D12_BOX box{0, 0, 0, layout.rw, layout.rh, 1};
cb->CopyTextureRegion(&dst, 0, 0, 0, &image,
(description.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) ? nullptr : &box);
barrier(cb, input, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
}
}
@@ -240,7 +266,9 @@ struct Replay {
std::array<std::vector<std::unique_ptr<Image>>, 11> alternatives;
std::unique_ptr<d4r::RModel> model;
std::unique_ptr<d4r::REngine> engine;
std::unique_ptr<Image> convertedOutput;
bool first = true;
bool modelUploaded = false;
Replay(const Layout& l, VkPhysicalDevice physical, VkDevice device, const std::string& directory)
: layout(l), physical(physical), device(device) {
const auto data = read(std::filesystem::path(directory) / "rr.txt");
@@ -251,6 +279,45 @@ struct Replay {
model = std::make_unique<d4r::RModel>(physical, device, directory);
engine = std::make_unique<d4r::REngine>(*model, l.rw, l.rh, l.ow, l.oh);
}
// Caller retires all submissions before changing layout extents. Pipelines
// and uploaded weights survive; descriptors/views and temporal state do not.
void resize() {
engine.reset();
for (auto& image : images) image.reset();
for (auto& cached : alternatives) cached.clear();
for (size_t i = 0; i < images.size(); ++i)
if (layout.planes[i].enabled) configure(i, layout.planes[i].format);
engine = std::make_unique<d4r::REngine>(*model, layout.rw, layout.rh, layout.ow, layout.oh);
first = true;
}
void configureOutput() {
if (layout.outputFormat == VK_FORMAT_R16G16B16A16_SFLOAT) return;
if (layout.outputFormat != VK_FORMAT_R32G32B32A32_SFLOAT)
throw std::runtime_error("Unsupported RR output conversion");
if (convertedOutput) return;
VkFormatProperties source{}, target{};
vkGetPhysicalDeviceFormatProperties(physical, VK_FORMAT_R16G16B16A16_SFLOAT, &source);
vkGetPhysicalDeviceFormatProperties(physical, layout.outputFormat, &target);
if (!(source.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) ||
(target.optimalTilingFeatures & (VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT)) !=
(VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT))
throw std::runtime_error("RR float32 output blit unsupported");
auto image = std::make_unique<Image>(); image->device = device; image->format = layout.outputFormat;
VkImageCreateInfo ci{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; ci.imageType = VK_IMAGE_TYPE_2D;
ci.format = image->format; ci.extent = {layout.ow, layout.oh, 1}; ci.mipLevels = ci.arrayLayers = 1;
ci.samples = VK_SAMPLE_COUNT_1_BIT; ci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
check(vkCreateImage(device, &ci, nullptr, &image->image), "RR converted output image");
VkMemoryRequirements req; vkGetImageMemoryRequirements(device, image->image, &req);
VkPhysicalDeviceMemoryProperties memory; vkGetPhysicalDeviceMemoryProperties(physical, &memory);
uint32_t type = memory.memoryTypeCount;
for (uint32_t n = 0; n < memory.memoryTypeCount; ++n)
if ((req.memoryTypeBits & (1u << n)) && (memory.memoryTypes[n].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { type = n; break; }
if (type == memory.memoryTypeCount) throw std::runtime_error("No converted RR output memory type");
VkMemoryAllocateInfo ai{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; ai.allocationSize = req.size; ai.memoryTypeIndex = type;
check(vkAllocateMemory(device, &ai, nullptr, &image->memory), "RR converted output memory");
check(vkBindImageMemory(device, image->image, image->memory, 0), "RR converted output bind");
convertedOutput = std::move(image);
}
// Cache views by format: REngine caches descriptors referencing old views,
// so they must remain alive until the engine is destroyed.
void configure(size_t i, VkFormat format) {
@@ -312,15 +379,34 @@ struct Replay {
VkImageView* optional[] = {&inputs.specularHitDistance, &inputs.disocclusionMask, &inputs.subsurfaceGuide, &inputs.colorBeforeParticles};
for (size_t i = 7; i < images.size(); ++i) if (layout.planes[i].enabled && (optionalMask & (1u << (i - 7)))) *optional[i - 7] = images[i]->view;
engine->setFrame(inputs, settings);
if (first) { model->recordUpload(cb); engine->recordInitialize(cb); first = false; }
if (first) {
if (!modelUploaded) { model->recordUpload(cb); modelUploaded = true; }
engine->recordInitialize(cb); first = false;
}
engine->recordFrame(cb);
VkImageMemoryBarrier b{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; b.image = engine->outputImage();
b.oldLayout = b.newLayout = VK_IMAGE_LAYOUT_GENERAL; b.srcQueueFamilyIndex = b.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
b.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; b.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; b.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &b);
VkBufferImageCopy copy{}; copy.bufferOffset = layout.outputOffset; copy.bufferRowLength = uint32_t(layout.outputPitch / 8);
VkImage output = engine->outputImage();
if (layout.outputFormat != VK_FORMAT_R16G16B16A16_SFLOAT) {
if (!convertedOutput) throw std::runtime_error("RR converted output was not configured");
VkImageMemoryBarrier target = b; target.image = convertedOutput->image;
target.oldLayout = convertedOutput->initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED;
target.newLayout = VK_IMAGE_LAYOUT_GENERAL;
target.srcAccessMask = convertedOutput->initialized ? VK_ACCESS_TRANSFER_READ_BIT : 0;
target.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &target);
VkImageBlit region{}; region.srcSubresource = region.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
region.srcOffsets[1] = region.dstOffsets[1] = {int32_t(layout.ow), int32_t(layout.oh), 1};
vkCmdBlitImage(cb, output, VK_IMAGE_LAYOUT_GENERAL, convertedOutput->image, VK_IMAGE_LAYOUT_GENERAL, 1, &region, VK_FILTER_NEAREST);
target.oldLayout = target.newLayout; target.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; target.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &target);
convertedOutput->initialized = true; output = convertedOutput->image;
}
VkBufferImageCopy copy{}; copy.bufferOffset = layout.outputOffset; copy.bufferRowLength = uint32_t(layout.outputPitch / layout.outputBytes);
copy.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; copy.imageExtent = {layout.ow, layout.oh, 1};
vkCmdCopyImageToBuffer(cb, engine->outputImage(), VK_IMAGE_LAYOUT_GENERAL, shared, 1, &copy);
vkCmdCopyImageToBuffer(cb, output, VK_IMAGE_LAYOUT_GENERAL, shared, 1, &copy);
}
};
}
+24 -7
View File
@@ -81,6 +81,7 @@ struct Backend::Impl {
offset = plane.offset + plane.pitch * layout.rh;
}
layout.outputOffset = (offset + 511) & ~VkDeviceSize(511);
layout.outputPitch = (VkDeviceSize(layout.ow) * 16 + 255) & ~VkDeviceSize(255);
layout.bytes = layout.outputOffset + layout.outputPitch * layout.oh;
}
void dispatch() {
@@ -151,8 +152,8 @@ struct Backend::Impl {
}
void validate_frame(const Snapshot& frame) {
if (failed) throw std::runtime_error("Native RR backend is poisoned after failed submission");
if (frame.renderWidth != layout.rw || frame.renderHeight != layout.rh)
throw std::runtime_error("Dynamic RR render extent requires engine reconfiguration");
if (frame.renderWidth < 64 || frame.renderHeight < 64 || frame.renderWidth > creation.width || frame.renderHeight > creation.height)
throw std::runtime_error("RR render extent must be at least 64 and within creation geometry");
if (frame.resources[Snapshot::Exposure]) throw std::runtime_error("RR exposure texture is not implemented");
if (frame.exposureScale != 1 || frame.depthType || frame.normalsType || frame.roughnessType || frame.diffuseType || frame.specularType)
throw std::runtime_error("RR guide encoding or exposure scale requires conversion");
@@ -170,12 +171,13 @@ struct Backend::Impl {
// D3D12 requires whole-subresource copies for depth-stencil.
// The copied subresource is depth plane 0, never stencil.
if ((desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) &&
(frame.origins[i][0] || frame.origins[i][1] || layout.rw != desc.Width || layout.rh != desc.Height))
(frame.origins[i][0] || frame.origins[i][1] || frame.renderWidth != desc.Width || frame.renderHeight != desc.Height))
throw std::runtime_error("RR depth-stencil requires a full depth-plane rectangle");
}
const uint32_t width = i == Snapshot::Output ? layout.ow : layout.rw;
const uint32_t height = i == Snapshot::Output ? layout.oh : layout.rh;
if (desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D || (i == Snapshot::Output && desc.Format != DXGI_FORMAT_R16G16B16A16_FLOAT) || desc.SampleDesc.Count != 1 || desc.DepthOrArraySize != 1 ||
const uint32_t width = i == Snapshot::Output ? layout.ow : frame.renderWidth;
const uint32_t height = i == Snapshot::Output ? layout.oh : frame.renderHeight;
if (desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D || (i == Snapshot::Output &&
desc.Format != DXGI_FORMAT_R16G16B16A16_FLOAT && desc.Format != DXGI_FORMAT_R32G32B32A32_FLOAT) || desc.SampleDesc.Count != 1 || desc.DepthOrArraySize != 1 ||
frame.origins[i][0] > desc.Width || width > desc.Width - frame.origins[i][0] ||
frame.origins[i][1] > desc.Height || height > desc.Height - frame.origins[i][1])
throw std::runtime_error("Unsupported native RR texture format or subrect");
@@ -187,6 +189,17 @@ struct Backend::Impl {
}
void evaluate(ID3D12CommandQueue* q, const Snapshot& frame, const std::array<ResourceAccess, Snapshot::Count>& access) {
validate(q, frame, access); dispatch();
// Any mutation below may invalidate descriptors or engine bookkeeping;
// a failed reconfiguration must never reuse a partially rebuilt engine.
failed = true;
if (frame.renderWidth != layout.rw || frame.renderHeight != layout.rh) {
if (pending) throw std::runtime_error("RR resize requires retired GPU work");
layout.rw = frame.renderWidth; layout.rh = frame.renderHeight;
// Keep creation-sized packet pitches/offsets and imported fence.
// Rebuild only size-dependent views/history, retaining model data.
replay->resize();
std::fprintf(stderr, "D4R_RR_RESIZE render=%ux%u history_reset=1 model_reused=1 packet_reused=1\n", layout.rw, layout.rh);
}
for (size_t i = 0; i < layout.planes.size(); ++i) {
const auto resource = resource_index(i);
layout.planes[i].enabled = frame.resources[resource] != nullptr;
@@ -195,6 +208,10 @@ struct Backend::Impl {
replay->configure(i, format.format);
layout.planes[i].format = format.format; layout.planes[i].bytes = format.bytes;
}
const auto outputFormat = resource_desc(frame.resources[Snapshot::Output].Get()).Format;
layout.outputBytes = outputFormat == DXGI_FORMAT_R32G32B32A32_FLOAT ? 16 : 8;
layout.outputFormat = layout.outputBytes == 16 ? VK_FORMAT_R32G32B32A32_SFLOAT : VK_FORMAT_R16G16B16A16_SFLOAT;
replay->configureOutput();
// Recording mutates engine upload/history bookkeeping. Any later error
// requires recreation, even if no GPU submission has started yet.
failed = true;
@@ -222,7 +239,7 @@ struct Backend::Impl {
auto* output = frame.resources[Snapshot::Output].Get();
texture_copy_barrier(consumer.Get(), output, access[Snapshot::Output], true, false);
auto source = rr_bridge::D3D12Frames::placed(shared.Get(), layout.outputOffset, layout.outputPitch,
DXGI_FORMAT_R16G16B16A16_FLOAT, layout.ow, layout.oh);
outputFormat, layout.ow, layout.oh);
auto dest = rr_bridge::D3D12Frames::texture(output);
consumer->CopyTextureRegion(&dest, frame.origins[Snapshot::Output][0], frame.origins[Snapshot::Output][1], 0, &source, nullptr);
texture_copy_barrier(consumer.Get(), output, access[Snapshot::Output], true, true);
+98 -3
View File
@@ -21,6 +21,13 @@ int main(int argc, char** argv) try {
const bool floatGuides = mode.find("float-guides") != std::string::npos;
const bool switchGuides = mode.find("switch-guides") != std::string::npos;
const bool autoExposure = mode.find("auto-exposure") != std::string::npos;
const bool dynamicExtents = mode.find("dynamic-extents") != std::string::npos;
const bool twoFeatures = mode.find("two-features") != std::string::npos;
const bool maxResources = mode.find("max-resources") != std::string::npos;
const bool outputFloat32 = mode.find("output-float32") != std::string::npos;
const bool switchOutput = mode.find("switch-output") != std::string::npos;
if (switchOutput && (!outputFloat32 || maxResources)) throw std::runtime_error("Output switching requires float32 mode and resized resources");
if (maxResources && !dynamicExtents) throw std::runtime_error("Creation-sized resources require dynamic fixture");
DXGI_FORMAT depthFormat = DXGI_FORMAT_UNKNOWN;
if (mode.find("depth-d32s8-typeless") != std::string::npos) depthFormat = DXGI_FORMAT_R32G8X24_TYPELESS;
else if (mode.find("depth-d32s8") != std::string::npos) depthFormat = DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
@@ -29,7 +36,25 @@ int main(int argc, char** argv) try {
const bool stencil = depthFormat == DXGI_FORMAT_R32G8X24_TYPELESS || depthFormat == DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
if (enhanced && split) throw std::runtime_error("The split fixture currently tests legacy split barriers only");
rr_bridge::Layout layout(argv[2], argv[4], argv[3]);
if (outputFloat32) {
layout.outputFormat = VK_FORMAT_R32G32B32A32_SFLOAT; layout.outputBytes = 16;
layout.initializePacket();
}
const auto canonical = layout;
std::vector<std::array<uint32_t, 2>> extents;
if (dynamicExtents) {
if (switchGuides || floatGuides || depthFormat != DXGI_FORMAT_UNKNOWN)
throw std::runtime_error("Dynamic fixture currently uses canonical guides and R32 depth");
std::ifstream sizes(layout.inputs / "dynamic-extents.txt");
uint32_t width, height;
while (sizes >> width >> height) {
if (width < 64 || height < 64 || width > layout.rw || height > layout.rh)
throw std::runtime_error("Invalid bounded dynamic fixture extent");
extents.push_back({width, height});
}
if (!sizes.eof() || extents.size() != layout.frames.size())
throw std::runtime_error("Dynamic fixture extent count differs from frames");
}
if (floatGuides) {
for (size_t i : {size_t(0), size_t(3)}) {
layout.planes[i].format = VK_FORMAT_R32G32B32A32_SFLOAT; layout.planes[i].bytes = 16;
@@ -170,6 +195,8 @@ int main(int argc, char** argv) try {
d4r_ngx_set_uint(p, "DLSS.Feature.Create.Flags", creationFlags);
}
void* feature = nullptr; check(create(nullptr, 13, p, &feature), "RR CreateFeature");
void* peerFeature = nullptr;
if (twoFeatures) check(create(nullptr, 13, p, &peerFeature), "RR peer CreateFeature");
auto textures = std::make_unique<rr_bridge::D3D12Frames>(layout, device.Get(), depthFormat);
ComPtr<ID3D12Resource> stencilUpload, stencilReadback;
D3D12_PLACED_SUBRESOURCE_FOOTPRINT stencilFootprint{}; UINT64 stencilBytes = 0;
@@ -196,14 +223,30 @@ int main(int argc, char** argv) try {
auto upload = make_buffer(device.Get(), layout.bytes, D3D12_HEAP_TYPE_UPLOAD, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_GENERIC_READ);
auto scratch = make_buffer(device.Get(), layout.bytes, D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
auto readback = make_buffer(device.Get(), layout.bytes, D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
ComPtr<ID3D12Resource> peerReadback;
if (twoFeatures) {
peerReadback = make_buffer(device.Get(), layout.bytes, D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
std::filesystem::create_directories(layout.output / "peer");
}
ComPtr<ID3D12Fence> fence; dx(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)), "NGX RR fixture fence");
Handle event; event.value = CreateEventW(nullptr, FALSE, FALSE, nullptr);
if (!event.value) throw std::runtime_error("NGX RR fixture event");
for (unsigned index = 0; index < layout.frames.size(); ++index) {
if (dynamicExtents) {
layout.rw = extents[index][0]; layout.rh = extents[index][1];
layout.initializePacket();
if (!maxResources) textures = std::make_unique<rr_bridge::D3D12Frames>(layout, device.Get(), depthFormat);
}
if (switchGuides) {
layout = index % 2 ? floatLayout : canonical;
textures = std::make_unique<rr_bridge::D3D12Frames>(layout, device.Get(), depthFormat);
}
if (switchOutput) {
layout.outputBytes = index % 2 ? 16 : 8;
layout.outputFormat = index % 2 ? VK_FORMAT_R32G32B32A32_SFLOAT : VK_FORMAT_R16G16B16A16_SFLOAT;
layout.initializePacket();
textures = std::make_unique<rr_bridge::D3D12Frames>(layout, device.Get(), depthFormat);
}
void* mapped = nullptr; D3D12_RANGE noRead{};
dx(upload->Map(0, &noRead, &mapped), "NGX RR upload map"); uploadFrame(mapped, index); upload->Unmap(0, nullptr);
ComPtr<ID3D12CommandAllocator> allocator; ComPtr<ID3D12GraphicsCommandList> list;
@@ -246,6 +289,8 @@ int main(int argc, char** argv) try {
for (size_t i = 0; i < 4; ++i)
d4r_ngx_set_d3d12_resource(p, optionalNames[i], layout.frames[index].optional[i] ? textures->inputs[i + 7].Get() : nullptr);
auto settings = layout.frames[index].settings;
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", layout.rw);
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Height", layout.rh);
d4r_ngx_set_float(p, "Jitter.Offset.X", settings.jitter[0]); d4r_ngx_set_float(p, "Jitter.Offset.Y", settings.jitter[1]);
d4r_ngx_set_float(p, "MV.Scale.X", settings.motionScale[0]); d4r_ngx_set_float(p, "MV.Scale.Y", settings.motionScale[1]);
d4r_ngx_set_float(p, "DLSS.Pre.Exposure", settings.preExposure);
@@ -257,6 +302,18 @@ int main(int argc, char** argv) try {
for (const auto* state : states) d4r_ngx_set_uint(p, state, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
d4r_ngx_set_uint(p, "D4R.Output.State", D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
}
if (mode.find("sr-bounds") != std::string::npos && index == 0) {
// This synthetic Quality fixture uses the pinned SR library's half-output minimum.
// Reject each axis below that minimum, then submit a valid frame on the same list.
require(layout.ow / 2 > 64 && layout.oh / 2 > 64, "SR bounds fixture is too small");
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", layout.ow / 2 - 1);
require(evaluate(list.Get(), feature, p, nullptr) != 1, "SR width below dynamic minimum accepted");
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", layout.rw);
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Height", layout.oh / 2 - 1);
require(evaluate(list.Get(), feature, p, nullptr) != 1, "SR height below dynamic minimum accepted");
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Height", layout.rh);
std::puts("RR_NGX_SR_BOUNDS width_below_min_rejected=1 height_below_min_rejected=1 command_list_preserved=1");
}
if (contracts && index == 0) {
auto rejects = [&](const char* name) { require(evaluate(list.Get(), feature, p, nullptr) != 1, name); };
if (depthFormat == DXGI_FORMAT_D32_FLOAT || stencil) {
@@ -268,15 +325,20 @@ int main(int argc, char** argv) try {
d4r_ngx_set_void(p, "ViewToClipMatrix", settings.viewToClip);
d4r_ngx_set_d3d12_resource(p, "DLSSD.SpecularHitDistance", textures->inputs[0].Get()); rejects("Invalid RR scalar guide format accepted");
d4r_ngx_set_d3d12_resource(p, "DLSSD.SpecularHitDistance", layout.frames[index].optional[0] ? textures->inputs[7].Get() : nullptr);
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", layout.rw + 1); rejects("Unsupported RR dynamic extent accepted");
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", canonical.rw + 1); rejects("RR extent exceeding creation size accepted");
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", layout.rw);
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Height", canonical.rh + 1); rejects("RR height exceeding creation size accepted");
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Height", layout.rh);
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", 63); rejects("RR width below engine minimum accepted");
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", layout.rw);
std::puts("RR_NGX_EXTENT_BOUNDS creation_max_rejected=1 engine_min_rejected=1 command_list_preserved=1");
d4r_ngx_set_uint(p, "DLSS.Depth.Type", 1); rejects("Unsupported RR encoding accepted");
d4r_ngx_set_uint(p, "DLSS.Depth.Type", 0);
d4r_ngx_set_uint(p, "DLSS.Input.Color.Subrect.Base.X", layout.rw); rejects("Invalid RR subrect accepted");
d4r_ngx_set_uint(p, "DLSS.Input.Color.Subrect.Base.X", 0);
d4r_ngx_set_d3d12_resource(p, "Output", textures->inputs[0].Get()); rejects("Aliased or invalid RR output accepted");
d4r_ngx_set_d3d12_resource(p, "Output", textures->output.Get());
std::puts("RR_NGX_NEGATIVE rejected=13 command_list_preserved=1");
std::puts("RR_NGX_NEGATIVE rejected=15 command_list_preserved=1");
}
if (split && index == 0) {
D3D12_RESOURCE_BARRIER barrier{}; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
@@ -288,16 +350,35 @@ int main(int argc, char** argv) try {
std::puts("RR_NGX_SPLIT unfinished_rejected=1 paired_before_submit=1");
}
check(evaluate(list.Get(), feature, p, nullptr), "RR EvaluateFeature");
if (twoFeatures) {
// Preserve first feature output before a second live Vulkan backend
// consumes the same borrowed inputs and publishes to the same UAV.
// Both evaluations are delayed boundaries in one game command list.
auto* output = textures->output.Get();
if (enhanced) texture_copy_barrier(list.Get(), output, enhancedAccess(true), false, false);
else transition(list.Get(), output, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
auto source = rr_bridge::D3D12Frames::texture(output);
auto destination = rr_bridge::D3D12Frames::placed(peerReadback.Get(), layout.outputOffset, layout.outputPitch,
rr_bridge::D3D12Frames::format(layout.outputFormat), layout.ow, layout.oh);
list->CopyTextureRegion(&destination, 0, 0, 0, &source, nullptr);
if (enhanced) texture_copy_barrier(list.Get(), output, enhancedAccess(true), false, true);
else transition(list.Get(), output, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
check(evaluate(list.Get(), peerFeature, p, nullptr), "RR peer EvaluateFeature");
}
// Both pointer payloads and parameter scalars change before submission.
settings.worldToView[0] = 999; settings.viewToClip[5] = 999;
d4r_ngx_set_float(p, "DLSS.Pre.Exposure", 999);
d4r_ngx_set_d3d12_resource(p, "Color", nullptr);
if (dynamicExtents) {
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Width", 1);
d4r_ngx_set_uint(p, "DLSS.Render.Subrect.Dimensions.Height", 1);
}
auto output = textures->output;
if (enhanced) texture_copy_barrier(list.Get(), output.Get(), enhancedAccess(true), false, false);
else transition(list.Get(), output.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
auto source = rr_bridge::D3D12Frames::texture(output.Get());
auto dest = rr_bridge::D3D12Frames::placed(readback.Get(), layout.outputOffset, layout.outputPitch,
DXGI_FORMAT_R16G16B16A16_FLOAT, layout.ow, layout.oh);
rr_bridge::D3D12Frames::format(layout.outputFormat), layout.ow, layout.oh);
list->CopyTextureRegion(&dest, 0, 0, 0, &source, nullptr);
if (enhanced) {
texture_copy_barrier(list.Get(), output.Get(), enhancedAccess(true), false, true);
@@ -309,6 +390,7 @@ int main(int argc, char** argv) try {
if (stencil) stencilCopy(list.Get(), textures->inputs[2].Get(), false);
if (index + 1 == layout.frames.size()) {
check(release(feature), "RR Release before submission"); feature = nullptr;
if (peerFeature) { check(release(peerFeature), "RR peer Release before submission"); peerFeature = nullptr; }
check(destroy(p), "RR Destroy parameters before submission"); p = nullptr;
textures.reset(); output.Reset();
}
@@ -321,6 +403,13 @@ int main(int argc, char** argv) try {
D3D12_RANGE range{0, SIZE_T(layout.bytes)}; dx(readback->Map(0, &range, &mapped), "NGX RR readback map");
try { layout.validate(mapped, index); } catch (...) { readback->Unmap(0, nullptr); throw; }
readback->Unmap(0, nullptr);
if (twoFeatures) {
dx(peerReadback->Map(0, &range, &mapped), "NGX RR first feature readback map");
auto peerLayout = layout; peerLayout.output /= "peer";
try { peerLayout.validate(mapped, index); } catch (...) { peerReadback->Unmap(0, nullptr); throw; }
peerReadback->Unmap(0, nullptr);
std::printf("RR_NGX_PEER_FRAME index=%u exact=1 both_features_live=1\n", index);
}
if (stencil) {
D3D12_RANGE stencilRange{0, SIZE_T(stencilBytes)};
dx(stencilReadback->Map(0, &stencilRange, &mapped), "RR stencil verification map");
@@ -331,6 +420,7 @@ int main(int argc, char** argv) try {
}
std::printf("RR_NGX_FRAME index=%u snapshot_mutated=1 exact=1 optional_mask=%u hardware_depth=%u depth_inverted=%u\n",
index, optionalMask, settings.hardwareDepth, settings.depthInverted);
if (dynamicExtents) std::printf("RR_NGX_EXTENT index=%u render=%ux%u exact=1\n", index, layout.rw, layout.rh);
}
check(shutdown(), "RR Shutdown");
if (contracts) std::puts("PASS RR_NGX_CONTRACTS capabilities=1 requirements=1 warp_rejected=1 invalid_inputs_rejected=1");
@@ -341,6 +431,11 @@ int main(int argc, char** argv) try {
if (floatGuides) std::puts("PASS RR_NGX_FLOAT_GUIDES color_rgba32=1 motion_rg32=1 normals_rgba32=1 roughness_r16=1");
if (switchGuides) std::puts("PASS RR_NGX_SWITCH_GUIDES alternate_each_frame=1 feature_recreated=0");
if (autoExposure) std::puts("PASS RR_NGX_FLAGS hdr=1 render_motion=1 auto_exposure=1");
if (dynamicExtents) std::puts("PASS RR_NGX_DYNAMIC_EXTENTS feature_recreated=0 snapshot_geometry_owned=1");
if (twoFeatures) std::puts("PASS RR_NGX_TWO_FEATURES delayed_boundaries=2 separate_vulkan_devices=1 release_both_before_submit=1");
if (maxResources) std::puts("PASS RR_NGX_MAX_RESOURCES creation_size_retained=1 dynamic_input_subrects=1");
if (outputFloat32) std::puts("PASS RR_NGX_OUTPUT_FLOAT32 gpu_conversion=1 half_reference_widening_exact=1");
if (switchOutput) std::puts("PASS RR_NGX_SWITCH_OUTPUT alternating_half_float=1 feature_recreated=0");
if (depthFormat != DXGI_FORMAT_UNKNOWN) std::printf("PASS RR_NGX_DEPTH format=%u stencil_preserved=%u\n", unsigned(depthFormat), stencil);
std::puts("PASS RR_NGX_LIFECYCLE feature=13 snapshot_mutation=1 release_before_submit=1 parameter_destroy_before_submit=1");
return 0;
+215
View File
@@ -0,0 +1,215 @@
"""Run the synthetic NGX feature-13 harness with the FSR Ray Regeneration backend selected only through d4r.ini.
Stages a private player folder from a packaged runtime, adds the experimental FSR payload (vkd3d-proton core, the
amdxc64 shim) and AMD's denoiser DLL, runs d4r_rr_ngx_probe with a synthetic noisy scene (make_fsr_rr_fixture.py),
and compares the output with the scene's noise-free answer. No game, no NVIDIA denoiser; Super Sampling runs when the
packaged runtime has its dependencies. Output stays under ignored build/.
"""
import argparse
import hashlib
import json
import os
import re
import shutil
import statistics
import subprocess
import sys
from pathlib import Path
repo = Path(__file__).resolve().parents[2]
def sha(path):
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
def main():
parser = argparse.ArgumentParser(description=__doc__)
for name in ('probe', 'shim', 'runtime', 'core', 'dlss', 'fsr-dir', 'denoiser', 'fixture', 'output'):
parser.add_argument('--' + name, required=True, type=lambda value: Path(value).resolve())
parser.add_argument('--timeout', type=int, default=1500)
parser.add_argument('--without-sr', action='store_true', help='omit SR/core dependencies and require the same-size FSR-only fallback')
parser.add_argument('--cache-dir', type=lambda value: Path(value).resolve(), help='isolated cache for shader/compiler experiments')
parser.add_argument('--vkd3d-config', help='diagnostic per-process vkd3d configuration')
parser.add_argument('--fp16-rte', action='store_true', help='experimental dxil-spirv payload: retain layout workaround but omit global FP16 RTZ')
parser.add_argument('--float-guides', action='store_true')
parser.add_argument('--switch-guides', action='store_true')
parser.add_argument('--output-float32', action='store_true')
parser.add_argument('--switch-output', action='store_true')
parser.add_argument('--depth-format', choices=('r32', 'd32', 'r32-typeless', 'd32s8', 'd32s8-typeless'), default='r32')
parser.add_argument('--reference-frames', type=lambda value: Path(value).resolve(), help='require exact components against canonical half-output frames, widening the reference for float32')
parser.add_argument('--verify-output-widening', action='store_true', help='FSR-only: dump each composed half image and require exact output widening in the same frame')
parser.add_argument('--max-resources', action='store_true', help='with --dynamic-extents: keep creation-sized textures and change only the render rectangle, as games do')
parser.add_argument('--dynamic-extents', action='store_true', help='run the probe in its dynamic-extents mode (the fixture has dynamic-extents.txt)')
parser.add_argument('--sr-bounds', action='store_true', help='Quality fixture: reject render width/height below the pinned SR dynamic minimum, then evaluate normally')
parser.add_argument('--ini-set', action='append', default=[], metavar='KEY=VALUE',
help='override a [RayReconstruction] key in the staged d4r.ini (repeatable), e.g. FsrStabilityBias=auto')
args = parser.parse_args()
if args.switch_output and not args.output_float32:
parser.error('--switch-output requires --output-float32')
if args.switch_guides and not args.float_guides:
parser.error('--switch-guides requires --float-guides')
if args.verify_output_widening and not args.without_sr:
parser.error('--verify-output-widening requires --without-sr')
build = (repo / 'build').resolve()
if not args.output.is_relative_to(build):
parser.error('output must stay under ignored build/')
args.output.mkdir(parents=True, exist_ok=False)
staged = args.output / 'player'
shutil.copytree(args.runtime, staged)
shutil.copy2(args.shim, staged / 'd4r_nvngx.dll')
for source, relative in ((args.core, 'ngx/_nvngx.dll'), (args.dlss, 'dlss/nvngx_dlss.dll')):
target = staged / relative
if args.without_sr:
if target.exists():
raise RuntimeError('--without-sr needs a runtime without installed SR/core DLLs')
continue
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, target)
fsr = staged / 'fsr'
fsr.mkdir(exist_ok=True)
for name in ('d4r_vkd3d_core.dll', 'amdxc64.dll'):
shutil.copy2(args.fsr_dir / name, fsr / name)
shutil.copy2(args.denoiser, staged / 'amd_fidelityfx_denoiser_dx12.dll')
config = (repo / 'packaging/windows/d4r.ini').read_text()
if 'Backend = off' not in config:
raise RuntimeError('packaging d4r.ini no longer has [RayReconstruction] Backend = off')
config = config.replace('Backend = off', 'Backend = fsr')
if args.cache_dir:
config = config.replace('CacheDir = auto', 'CacheDir = ' + str(args.cache_dir))
for item in args.ini_set:
key, value = item.split('=', 1)
pattern = re.compile(r'^' + re.escape(key) + r'\s*=.*$', re.M)
if not pattern.search(config):
raise RuntimeError('d4r.ini has no ' + key + ' line to override')
config = pattern.sub(lambda match: key + ' = ' + value, config, count=1)
(staged / 'd4r.ini').write_text(config)
env = {key: value for key, value in os.environ.items()
if not key.startswith(('D4R_', 'VKD3D_', 'DXIL_')) and key not in ('HIP_PATH', 'ZLUDA_CUDA_LIB', 'ZLUDA_LOG_DIR', 'ZLUDA_CACHE_DIR')}
env['D4R_FSR_RR_LOG'] = '1' # per-frame stage timings in stderr; selection itself comes only from d4r.ini
if args.vkd3d_config:
env['VKD3D_CONFIG'] = args.vkd3d_config
if args.fp16_rte:
env['D4R_DXIL_F16_RTE'] = '1'
if args.verify_output_widening:
dump = args.output / 'converted'
dump.mkdir()
env['D4R_FSR_RR_DUMP_DIR'] = str(dump)
env['D4R_FSR_RR_DUMP_FRAMES'] = str(len(list(args.fixture.glob('*-color.bin'))))
if args.without_sr:
# Player startup requires SR dependencies. Use the existing diagnostic environment
# contract to exercise the supported FSR-only fallback without changing player policy.
cache = (args.cache_dir or (Path(os.environ['LOCALAPPDATA']) / 'd4r/cache')) / 'vkd3d'
cache.mkdir(parents=True, exist_ok=True)
env.update(D4R_HIP_ROOT=str(staged / 'hip'), D4R_RR_BACKEND='fsr',
D4R_FSR_VKD3D_CORE=str(fsr / 'd4r_vkd3d_core.dll'),
D4R_FSR_AMDXC_SHIM=str(fsr / 'amdxc64.dll'),
D4R_FSR_RR_DLL=str(staged / 'amd_fidelityfx_denoiser_dx12.dll'),
D4R_FSR_CACHE_DIR=str(cache))
command = [str(args.probe), str(staged / 'd4r_nvngx.dll'), str(args.fixture), '', str(args.output / 'frames'), '']
modes = []
if args.dynamic_extents:
modes.append('dynamic-extents' + ('-max-resources' if args.max_resources else ''))
if args.sr_bounds:
modes.append('sr-bounds')
if args.float_guides:
modes.append('float-guides')
if args.switch_guides:
modes.append('switch-guides')
if args.output_float32:
modes.append('output-float32')
if args.switch_output:
modes.append('switch-output')
if args.depth_format != 'r32':
modes.append('depth-' + args.depth_format)
if modes:
command.append('-'.join(modes))
report = {'passed': False, 'gameAccessed': False, 'qualityReference': False, 'synthetic': True,
'selectionMode': 'diagnostic environment without SR' if args.without_sr else 'player INI',
'artifacts': {str(path): sha(path) for path in (args.probe, args.shim, args.core, args.dlss, args.denoiser,
args.fsr_dir / 'd4r_vkd3d_core.dll', args.fsr_dir / 'amdxc64.dll')}}
try:
with (args.output / 'stdout.log').open('wb') as out, (args.output / 'stderr.log').open('wb') as err:
result = subprocess.run(command, env=env, stdout=out, stderr=err, timeout=args.timeout)
report['exitCode'] = result.returncode
text = (args.output / 'stdout.log').read_text(errors='replace')
if result.returncode or 'PASS RR_NGX_LIFECYCLE' not in text:
raise RuntimeError('NGX RR harness failed; inspect ' + str(args.output))
if args.dynamic_extents:
expected = [tuple(map(int, line.split())) for line in (args.fixture / 'dynamic-extents.txt').read_text().splitlines() if line.strip()]
actual = [(int(w), int(h)) for w, h in re.findall(r'RR_NGX_EXTENT index=\d+ render=(\d+)x(\d+) exact=1', text)]
if actual != expected or 'PASS RR_NGX_DYNAMIC_EXTENTS' not in text:
raise RuntimeError('Dynamic render dimensions were not applied')
if args.max_resources and 'PASS RR_NGX_MAX_RESOURCES' not in text:
raise RuntimeError('Creation-sized resources were not retained')
report['renderExtents'] = actual
if args.sr_bounds:
if 'RR_NGX_SR_BOUNDS width_below_min_rejected=1 height_below_min_rejected=1 command_list_preserved=1' not in text:
raise RuntimeError('SR minimum bound rejections were not verified')
report['srMinimumRejectedBeforeSubmission'] = True
quality_command = [sys.executable, str(repo / 'tools/windows/check_fsr_rr_output.py'), '--fixture', str(args.fixture),
'--output', str(args.output / 'frames'), '--report', str(args.output / 'quality.json')]
if args.output_float32:
quality_command.append('--output-float32')
if args.switch_output:
quality_command.append('--switch-output')
quality = subprocess.run(quality_command,
capture_output=True, text=True)
report['quality'] = json.loads((args.output / 'quality.json').read_text())['summary']
if quality.returncode:
raise RuntimeError('FSR RR output quality check failed; inspect ' + str(args.output / 'quality.json'))
if args.reference_frames:
sys.path.insert(0, str(repo / '.tools/python/vendor'))
import numpy as np
comparisons = []
for index in range(len(list(args.fixture.glob('*-color.bin')))):
float_output = args.output_float32 and (not args.switch_output or index % 2 == 1)
actual = np.fromfile(args.output / 'frames' / f'{index}-out.bin', '<f4' if float_output else '<f2')
reference = np.fromfile(args.reference_frames / f'{index}-out.bin', '<f2').astype(actual.dtype)
comparisons.append(bool(np.array_equal(actual, reference)))
report['exactReferenceFrames'] = comparisons
if not all(comparisons):
raise RuntimeError('FSR format output differs from the canonical half reference')
if args.verify_output_widening:
sys.path.insert(0, str(repo / '.tools/python/vendor'))
import numpy as np
exact = []
for index in range(len(list(args.fixture.glob('*-color.bin')))):
float_output = args.output_float32 and (not args.switch_output or index % 2 == 1)
actual = np.fromfile(args.output / 'frames' / f'{index}-out.bin', '<f4' if float_output else '<f2')
composed = np.fromfile(args.output / 'converted' / f'frame{index}-composed.bin', '<f2').astype(actual.dtype)
exact.append(bool(np.array_equal(actual, composed)))
report['exactSameFrameOutputConversion'] = exact
if not all(exact):
raise RuntimeError('FSR output differs from the same-frame composed half image')
# player mode redirects the shim's stderr into its own log beside d4r_nvngx.dll
err_text = (args.output / 'stderr.log').read_text(errors='replace')
playerLog = staged / 'd4r_nvngx.log'
if playerLog.exists():
err_text += playerLog.read_text(errors='replace')
shutil.copy2(playerLog, args.output / 'd4r_nvngx.log')
report['superSamplingUsed'] = 'FSR_RR super sampling submitted' in err_text
if args.without_sr and report['superSamplingUsed']:
raise RuntimeError('SR ran in the FSR-only test')
report['superSamplingFrames'] = err_text.count('FSR_RR super sampling submitted')
expected_frames = len(list(args.fixture.glob('*-color.bin')))
if (args.dynamic_extents or args.sr_bounds) and report['superSamplingFrames'] != expected_frames:
raise RuntimeError('Dynamic SR fixture did not upscale every frame')
report['srRenderLimits'] = re.findall(r'D4R_SR_RENDER_LIMITS min=(\d+x\d+) max=(\d+x\d+)', err_text)
timings = re.findall(r'FSR_RR gpu frame \d+: prepare ([0-9.]+) ms, denoise ([0-9.]+) ms, compose\+copy ([0-9.]+) ms, total ([0-9.]+) ms', err_text)
if len(timings) > 4:
report['gpuTiming'] = {name: statistics.median(float(row[i]) for row in timings[4:])
for i, name in enumerate(('prepareMedianMs', 'denoiseMedianMs', 'composeCopyMedianMs', 'totalMedianMs'))}
report['gpuTiming']['warmupFrames'] = 4
report['gpuTiming']['measuredFrames'] = len(timings) - 4
report.update(passed=True)
print('PASS FSR_RR_NGX super_sampling=%d tail_rmse=%.4f input_rmse=%.4f ideal_nearest_rmse=%.4f' % (
report['superSamplingUsed'], report['quality']['tailOutputRmse'], report['quality']['tailInputRmse'], report['quality'].get('idealNearestRmse', float('nan'))))
finally:
(args.output / 'summary.json').write_text(json.dumps(report, indent=2) + '\n')
if not report['passed']:
sys.exit(1)
if __name__ == '__main__':
main()
+78
View File
@@ -0,0 +1,78 @@
"""Run a bounded synthetic Vulkan K model smoke. No NGX, CUDA or game inference.
Authentic captured weights do not make the generated scene a quality reference.
Reports GPU dispatch validity separately from model provenance and game parity.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import struct
import subprocess
import sys
def main():
repo = Path(__file__).resolve().parents[2]
parser = argparse.ArgumentParser(description=__doc__)
for name in ('runner', 'model', 'output'):
parser.add_argument('--' + name, required=True, type=lambda p: Path(p).resolve())
args = parser.parse_args()
if args.output.exists() or not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('use fresh output under ignored build/')
cache = json.loads((args.model / 'windows-k-model-cache.json').read_text())
for name, expected in cache['products'].items():
if Path(name).name != name or hashlib.sha256((args.model / name).read_bytes()).hexdigest() != expected:
raise RuntimeError('Compiled K model product changed')
args.output.mkdir(parents=True)
rw = rh = 128
ow = oh = 256
color, depth = bytearray(), bytearray()
for y in range(rh):
for x in range(rw):
u, v = x / (rw - 1), y / (rh - 1)
color += struct.pack('<4e', .1 + .7 * u, .1 + .6 * v, .2 + .3 * u * v, 1)
depth += struct.pack('<f', .5)
(args.output / 'color.bin').write_bytes(color)
(args.output / 'depth.bin').write_bytes(depth)
(args.output / 'motion.bin').write_bytes(bytes(rw * rh * 4))
lines = [f'output {ow} {oh}']
for index, (name, channels, bits) in enumerate((('color', 4, 16), ('motion', 2, 16), ('depth', 1, 32))):
lines.append(f'image {index} {json.dumps(str(args.output / (name + ".bin")))} {rw} {rh} {channels} {bits}')
for frame in range(4):
lines.append(f'frame_auto 0 1 2 1 1 1 {int(frame == 0)} 0 0 0 0 0 0 1.37')
case = args.output / 'case.txt'
case.write_text('\n'.join(lines) + '\n')
env = {key:value for key,value in os.environ.items() if not key.startswith('D4R_')}
report = dict(passed=False, synthetic=True, authenticCapturedWeights=True, qualityReference=False,
gameAccessed=False, cudaNgxInference=False, model=str(args.model),
runnerSha256=hashlib.sha256(args.runner.read_bytes()).hexdigest())
try:
with (args.output / 'stdout.log').open('wb') as out, (args.output / 'stderr.log').open('wb') as err:
result = subprocess.run([str(args.runner), str(args.model), str(case), str(args.output / 'frames')],
env=env, stdout=out, stderr=err, timeout=180)
if result.returncode:
raise RuntimeError('Native K runner failed; inspect logs')
sys.path.insert(0, str(repo / '.tools/python/vendor'))
import numpy as np
outputs = sorted((args.output / 'frames').glob('frame-*.312.bin'))
if len(outputs) != 4:
raise RuntimeError('Incomplete native K output set')
for path in outputs:
values = np.fromfile(path, '<f2')
if values.size != ow * oh * 4 or not np.isfinite(values).all() or float(np.ptp(values.astype('f4').reshape(-1, 4)[:, :3])) < 1e-5:
raise RuntimeError('Invalid native K output: ' + path.name)
report.update(passed=True, frames=4, finiteNonuniform=True,
outputs={p.name:hashlib.sha256(p.read_bytes()).hexdigest() for p in outputs},
gpuTimeParity=False, gameValidated=False)
print(json.dumps(report, indent=2))
except Exception as error:
report['error'] = str(error)
raise
finally:
(args.output / 'validation.json').write_text(json.dumps(report, indent=2) + '\n')
if __name__ == '__main__':
main()
+129
View File
@@ -0,0 +1,129 @@
"""Validate Windows SR preset M on synthetic D3D12 resources, never a game.
Tests packaged native M layers, a translated-PTX control on the same GPU, and
an identical native repeat. Finite/nonuniform output, actual layer selection,
and exact full-frame comparisons are separate gates. This is not NVIDIA
hardware/reference-quality validation or a standalone M engine test.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import re
import shutil
import subprocess
import sys
repo = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(repo / '.tools/python/vendor'))
import numpy as np
LAYERS = ('rrlite_enc1_4x4', 'rrlite_enc2_4x4', 'rrlite_enc3_tube_4x4', 'rrlite_dec2_4x4', 'rrlite_dec1_4x4')
def sha(path):
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
def main():
parser = argparse.ArgumentParser(description=__doc__)
for name in ('probe', 'shim', 'runtime', 'core', 'dlss', 'output'):
parser.add_argument('--' + name, required=True, type=lambda s: Path(s).resolve())
parser.add_argument('--resolution', default='512x288')
parser.add_argument('--frames', type=int, default=8)
parser.add_argument('--timeout', type=int, default=300)
parser.add_argument('--native-objects', type=lambda s: Path(s).resolve(), help='overlay all five candidate M objects in the staged player only')
args = parser.parse_args()
if not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('output must stay under ignored build/')
if not re.fullmatch(r'[1-9][0-9]*x[1-9][0-9]*', args.resolution) or not 4 <= args.frames <= 32:
parser.error('resolution must be WIDTHxHEIGHT; frames must be 4..32')
ow, oh = map(int, args.resolution.split('x'))
args.output.mkdir(parents=True, exist_ok=False)
player = args.output / 'player'
shutil.copytree(args.runtime, player)
module = player / 'd4r_nvngx.dll'
shutil.copy2(args.shim, module)
for source, relative in ((args.core, 'ngx/_nvngx.dll'), (args.dlss, 'dlss/nvngx_dlss.dll')):
target = player / relative
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, target)
config = (repo / 'packaging/windows/d4r.ini').read_text()
config, replaced = re.subn(r'^Model\s*=\s*K\s*$', 'Model = M', config, count=1, flags=re.M)
if replaced != 1:
raise RuntimeError('expected the default Model = K setting')
(player / 'd4r.ini').write_text(config)
native = player / 'native/gfx1101'
if args.native_objects:
for name in LAYERS:
shutil.copy2(args.native_objects / (name + '.hsaco'), native / (name + '.hsaco'))
manifest_path = native / 'd4r-kernels.txt'
manifest = manifest_path.read_text()
control_manifest = ''.join(line for line in manifest.splitlines(keepends=True)
if not any(line.startswith(name + ' ') for name in LAYERS))
env = {k: v for k, v in os.environ.items() if not k.startswith(('D4R_', 'ZLUDA_'))}
nvapi = player / 'nvapi/gfx1101/nvapi64.dll'
if not nvapi.is_file():
nvapi = player / 'nvapi/nvapi64.dll'
report = {'passed': False, 'synthetic': True, 'gameAccessed': False, 'qualityReference': False,
'preset': 13, 'model': 'M', 'quality': 'Performance', 'createFlags': 11,
'render': [ow // 2, oh // 2], 'output': [ow, oh], 'frames': args.frames,
'nativeFp8': False, 'comparison': 'same-RDNA3 translated PTX and native repeat',
'artifacts': {str(p): sha(p) for p in (args.probe, args.shim, args.core, args.dlss)},
'nativeObjects': {name: sha(native / (name + '.hsaco')) for name in LAYERS}, 'runs': []}
reference_hashes = None
try:
for label in ('native', 'translated', 'native-repeat'):
folder = args.output / label
folder.mkdir()
manifest_path.write_text(control_manifest if label == 'translated' else manifest)
child_env = dict(env, D4R_DIAG_DIR=str(folder))
command = [str(args.probe), '--module', str(module), '--hip-root', str(player / 'hip'),
'--cuda-dll', str(player / 'zluda/nvcuda.dll'), '--nvapi-dll', str(nvapi),
'--ngx-core', str(args.core), '--dlss-dll', str(args.dlss), '--ngx-abi', 'project',
'--interop-mode', 'command-list', '--player-mode', '1', '--preset', '13',
'--ngx-quality', '0', '--ngx-create-flags', '11', '--ngx-output-resolution', args.resolution,
'--ngx-input-resolution', f'{ow // 2}x{oh // 2}', '--iterations', str(args.frames)]
with (folder / 'stdout.log').open('wb') as out, (folder / 'stderr.log').open('wb') as err:
result = subprocess.run(command, env=child_env, stdout=out, stderr=err, timeout=args.timeout)
text = (folder / 'stdout.log').read_text(errors='replace')
logs = (folder / 'stderr.log').read_text(errors='replace')
player_log = player / 'd4r_nvngx.log'
if player_log.exists():
shutil.copy2(player_log, folder / 'player.log')
logs += player_log.read_text(errors='replace')
hashes = {p.name: sha(p) for p in sorted(folder.glob('output-*.rgba16f'))}
selected = {name: bool(re.search(r'\[d4r-native\] "' + re.escape(name) + r'" ->', logs)) for name in LAYERS}
row = {'label': label, 'exitCode': result.returncode, 'outputHashes': hashes,
'nativeLayersLoaded': selected, 'finiteFrames': len(re.findall(r'D3D12_OUTPUT preset=13 frame=\d+ finite=1 ', text)),
'playerSelectedM': 'model=M native_fp8=0' in logs,
'performanceSelected': 'D3D12_NGX_QUALITY value=0' in text}
row['executionPassed'] = (result.returncode == 0 and 'PASS D3D12_DLSS' in text
and len(hashes) == args.frames and row['finiteFrames'] == args.frames
and row['playerSelectedM'] and row['performanceSelected'])
row['selectionPassed'] = not any(selected.values()) if label == 'translated' else all(selected.values())
if reference_hashes is None:
reference_hashes = hashes
row['exactNativeControl'] = hashes == reference_hashes and len(hashes) == args.frames
row['differences'] = []
for name in hashes:
if hashes[name] != reference_hashes.get(name):
actual = np.fromfile(folder / name, '<f2').astype('f4')
baseline = np.fromfile(args.output / 'native' / name, '<f2').astype('f4')
row['differences'].append({'frame': name, 'components': int(np.count_nonzero(actual != baseline)),
'maxAbs': float(np.max(np.abs(actual - baseline)))})
report['runs'].append(row)
(args.output / 'summary.json').write_text(json.dumps(report, indent=2) + '\n')
print(f'{label}: execution={row["executionPassed"]} selection={row["selectionPassed"]} exact={row["exactNativeControl"]} frames={row["finiteFrames"]}', flush=True)
if not row['executionPassed']:
break
report['passed'] = len(report['runs']) == 3 and all(r['executionPassed'] and r['selectionPassed'] and r['exactNativeControl'] for r in report['runs'])
finally:
manifest_path.write_text(manifest)
(args.output / 'summary.json').write_text(json.dumps(report, indent=2) + '\n')
return 0 if report['passed'] else 1
if __name__ == '__main__':
raise SystemExit(main())
+75 -1
View File
@@ -6,6 +6,7 @@ import os
import shutil
from pathlib import Path
import subprocess
import struct
def main():
@@ -19,6 +20,11 @@ def main():
parser.add_argument('--float-guides', action='store_true', help='exercise equivalent float32 color/motion/normals and scalar roughness')
parser.add_argument('--switch-guides', action='store_true', help='alternate canonical and float guide formats without recreating feature')
parser.add_argument('--auto-exposure', action='store_true', help='also set the public NGX AutoExposure creation flag')
parser.add_argument('--dynamic-extents', action='store_true', help='read per-frame bounded render dimensions from dynamic-extents.txt')
parser.add_argument('--two-features', action='store_true', help='evaluate two live independent RR features on each game command list')
parser.add_argument('--max-resources', action='store_true', help='retain creation-sized textures while dynamic input rectangles change')
parser.add_argument('--output-float32', action='store_true', help='validate GPU half-to-float32 output conversion against widened standalone output')
parser.add_argument('--switch-output', action='store_true', help='alternate half/float32 output formats without recreating feature')
parser.add_argument('--depth-format', choices=('r32', 'r32-typeless', 'd32', 'd32s8', 'd32s8-typeless'), default='r32', help='exercise native depth-plane copy format')
parser.add_argument('--player-config', action='store_true', help='stage a private player folder and select RR only through d4r.ini')
parser.add_argument('--use-existing-ini', action='store_true', help='preserve the selected runtime INI when staging player validation')
@@ -35,6 +41,12 @@ def main():
parser.error('--without-sr requires --contracts and a configured native RR backend')
if args.switch_guides and not args.float_guides:
parser.error('--switch-guides requires --float-guides')
if args.dynamic_extents and (args.float_guides or args.switch_guides or args.depth_format != 'r32'):
parser.error('--dynamic-extents currently uses canonical guides and R32 depth')
if args.max_resources and not args.dynamic_extents:
parser.error('--max-resources requires --dynamic-extents')
if args.switch_output and (not args.output_float32 or args.max_resources):
parser.error('--switch-output requires --output-float32 and cannot retain max resources')
build = Path(__file__).resolve().parents[2] / 'build'
if not args.output.is_relative_to(build.resolve()):
parser.error('output must stay under ignored build/')
@@ -92,6 +104,16 @@ def main():
mode += '-switch-guides'
if args.auto_exposure:
mode += '-auto-exposure'
if args.dynamic_extents:
mode += '-dynamic-extents'
if args.two_features:
mode += '-two-features'
if args.max_resources:
mode += '-max-resources'
if args.output_float32:
mode += '-output-float32'
if args.switch_output:
mode += '-switch-output'
if args.depth_format != 'r32':
mode += '-depth-' + args.depth_format
if mode:
@@ -128,6 +150,32 @@ def main():
raise RuntimeError('NGX RR temporal guide-format switching checks incomplete')
if args.auto_exposure and 'PASS RR_NGX_FLAGS' not in text:
raise RuntimeError('NGX RR creation flag checks incomplete')
if args.dynamic_extents:
extents = [tuple(map(int, line.split())) for line in (args.inputs / 'dynamic-extents.txt').read_text().splitlines()]
if 'PASS RR_NGX_DYNAMIC_EXTENTS' not in text:
raise RuntimeError('NGX RR dynamic extent checks incomplete')
for index, (width, height) in enumerate(extents):
if f'RR_NGX_EXTENT index={index} render={width}x{height} exact=1' not in text:
raise RuntimeError('Incomplete NGX RR dynamic extent output')
creation = tuple(map(int, (args.inputs / 'frames.txt').read_text().splitlines()[0].split()[1:3]))
previous, expected_resizes = creation, []
for size in extents:
if size != previous:
expected_resizes.extend([f'D4R_RR_RESIZE render={size[0]}x{size[1]} history_reset=1 model_reused=1 packet_reused=1'] * (2 if args.two_features else 1))
previous = size
diagnostics = args.output / ('player/d4r_nvngx.log' if args.player_config else 'stderr.log')
actual_resizes = [line for line in diagnostics.read_text().splitlines() if line.startswith('D4R_RR_RESIZE ')]
if actual_resizes != expected_resizes:
raise RuntimeError('NGX RR resize operations differ from requested sequence')
if args.contracts and 'RR_NGX_EXTENT_BOUNDS' not in text:
raise RuntimeError('NGX RR extent bounds checks missing')
report['dynamicExtents'] = extents
report.update(geometryChanges=len(expected_resizes), modelReusedAcrossResize=True,
packetReusedAcrossResize=True, historyResetOnResize=True)
if args.max_resources:
if 'PASS RR_NGX_MAX_RESOURCES' not in text:
raise RuntimeError('NGX RR creation-sized dynamic input checks missing')
report['creationSizedInputResourcesRetained'] = True
depth_values = {'r32-typeless': 39, 'd32': 40, 'd32s8': 20, 'd32s8-typeless': 19}
if args.depth_format != 'r32':
marker = f'PASS RR_NGX_DEPTH format={depth_values[args.depth_format]} stencil_preserved={int(args.depth_format.startswith("d32s8"))}'
@@ -140,9 +188,35 @@ def main():
outputs = sorted(path.name for path in (args.output / 'frames').glob('*-out.bin'))
if outputs != sorted(expected):
raise RuntimeError('Incomplete NGX RR output set')
def reference_bytes(name):
data = (args.reference / name).read_bytes()
if args.output_float32 and (not args.switch_output or expected.index(name) % 2):
return b''.join(struct.pack('<f', value[0]) for value in struct.iter_unpack('<e', data))
return data
for name in expected:
if (args.output / 'frames' / name).read_bytes() != (args.reference / name).read_bytes():
if (args.output / 'frames' / name).read_bytes() != reference_bytes(name):
raise RuntimeError('NGX RR reference mismatch: ' + name)
if args.two_features:
if 'PASS RR_NGX_TWO_FEATURES' not in text:
raise RuntimeError('NGX RR two-feature lifecycle checks incomplete')
peer_outputs = sorted(path.name for path in (args.output / 'frames/peer').glob('*-out.bin'))
if peer_outputs != sorted(expected):
raise RuntimeError('Incomplete first-feature output set')
for index, name in enumerate(expected):
if (args.output / 'frames/peer' / name).read_bytes() != reference_bytes(name):
raise RuntimeError('NGX RR first-feature reference mismatch: ' + name)
if f'RR_NGX_PEER_FRAME index={index} exact=1 both_features_live=1' not in text:
raise RuntimeError('NGX RR first-feature validation incomplete')
report.update(liveFeatures=2, bothFeaturesStandaloneExact=True, separateVulkanDevices=True,
twoDelayedBoundariesPerCommandList=True, bothReleasedBeforeFinalSubmit=True)
if args.output_float32:
if 'PASS RR_NGX_OUTPUT_FLOAT32' not in text:
raise RuntimeError('NGX RR float32 output checks missing')
report.update(outputFormat='RGBA32F', gpuOutputConversion=True, widenedHalfReferenceExact=True)
if args.switch_output:
if 'PASS RR_NGX_SWITCH_OUTPUT' not in text:
raise RuntimeError('NGX RR temporal output format switching checks missing')
report.update(outputFormat='alternating RGBA16F/RGBA32F', outputFormatSwitchWithoutFeatureRecreation=True)
optional_names = ('hitdistance', 'disocclusion', 'sss', 'particles')
optional_frames = {name: sum((args.inputs / (frame.removesuffix('-out.bin') + '-' + name + '.bin')).is_file()
for frame in expected) for name in optional_names}
+92
View File
@@ -0,0 +1,92 @@
"""Probe plain Windows SR render subrects without a game or FSR denoiser.
Retains creation-sized resources. Separate child processes compare static,
smaller-render, and reset-on-every-frame cases. Output is diagnostic evidence,
not a quality or performance gate.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import re
import subprocess
def main():
parser = argparse.ArgumentParser(description=__doc__)
for name in ('probe', 'shim', 'runtime', 'core', 'dlss', 'output'):
parser.add_argument('--' + name, required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--creation-size', default='426x256')
parser.add_argument('--smaller-render-size', default='384x224')
args = parser.parse_args()
build = Path(__file__).resolve().parents[2] / 'build'
if not args.output.is_relative_to(build.resolve()):
parser.error('output must stay under ignored build/')
args.output.mkdir(parents=True, exist_ok=False)
runtime = args.runtime
native = runtime / 'native'
if (native / 'gfx1101').is_dir():
native /= 'gfx1101'
nvapi = runtime / 'nvapi/nvapi64.dll'
if not nvapi.is_file():
nvapi = runtime / 'nvapi/gfx1101/nvapi64.dll'
env = {k: v for k, v in os.environ.items()
if not k.startswith(('D4R_', 'ZLUDA_'))}
env.update(HIP_PATH=str(runtime / 'hip'),
ZLUDA_CUDA_LIB=str(runtime / 'zluda/nvcuda.dll'),
D4R_NVAPI_BACKEND=str(runtime / 'nvapi/backend/nvapi64.dll'),
D4R_FORMAT_MODULE=str(runtime / 'pixel_convert_gfx1101.hsaco'),
D4R_ZLUDA_NATIVE_DIR=str(native), D4R_ZLUDA_WMMA='1',
D4R_ZLUDA_WMMA_FP8='1', D4R_ZLUDA_WMMA_F16_REFERENCE='1',
D4R_QUIET_API='1',
ZLUDA_CACHE_DIR=str(Path(os.environ['LOCALAPPDATA']) / 'd4r/cache'))
env['PATH'] = str(runtime / 'hip/bin') + os.pathsep + str(runtime / 'zluda') + os.pathsep + env['PATH']
report = {'synthetic': True, 'gameAccessed': False, 'qualityReference': False,
'performanceEvidence': False, 'creationSize': args.creation_size,
'outputSize': [640, 384], 'smallerRenderSize': args.smaller_render_size,
'artifacts': {str(p): hashlib.sha256(p.read_bytes()).hexdigest()
for p in (args.probe, args.shim, args.core, args.dlss)},
'runs': []}
for flags in (0, 11):
for mode in ('static', 'smaller', 'smaller-reset'):
output = args.output / f'flags-{flags}-{mode}'
output.mkdir()
child_env = dict(env, D4R_DIAG_DIR=str(output), D4R_NGX_DATA_DIR=str(output / 'ngx-data'))
if mode != 'static':
child_env['D4R_PROBE_RENDER_SUBRECT'] = args.smaller_render_size
command = [str(args.probe), '--module', str(args.shim),
'--hip-root', str(runtime / 'hip'), '--cuda-dll', str(runtime / 'zluda/nvcuda.dll'),
'--ngx-core', str(args.core), '--dlss-dll', str(args.dlss), '--nvapi-dll', str(nvapi),
'--ngx-abi', 'project', '--interop-mode', 'command-list', '--preset', '11',
'--ngx-create-flags', str(flags), '--ngx-input-resolution', args.creation_size,
'--ngx-output-resolution', '640x384', '--iterations', '4']
if mode == 'smaller-reset':
command += ['--reset-every-frame', '1']
row = {'flags': flags, 'mode': mode, 'command': command}
with (output / 'stdout.log').open('wb') as out, (output / 'stderr.log').open('wb') as err:
try:
result = subprocess.run(command, env=child_env, stdout=out, stderr=err, timeout=180)
row['exitCode'] = result.returncode
except subprocess.TimeoutExpired:
row['timeout'] = True
text = (output / 'stdout.log').read_text(errors='replace')
row['finiteFrames'] = len(re.findall(r'D3D12_OUTPUT preset=11 frame=\d+ finite=1 ', text))
expected = [args.creation_size] + [args.creation_size if mode == 'static' else args.smaller_render_size] * 3
row['appliedRenderSizes'] = re.findall(r'D3D12_RENDER_SUBRECT frame=\d+ size=(\d+x\d+) ', text)
row['passed'] = (row.get('exitCode') == 0 and 'PASS D3D12_DLSS' in text
and row['finiteFrames'] == 4 and row['appliedRenderSizes'] == expected)
row['outputHashes'] = {p.name: hashlib.sha256(p.read_bytes()).hexdigest()
for p in sorted(output.glob('output-*.rgba16f'))}
report['runs'].append(row)
(args.output / 'summary.json').write_text(json.dumps(report, indent=2) + '\n')
print(f'flags={flags} mode={mode} exit={row.get("exitCode")} finite_frames={row["finiteFrames"]}', flush=True)
if row.get('timeout'):
return 1
report['passed'] = all(row['passed'] for row in report['runs'])
(args.output / 'summary.json').write_text(json.dumps(report, indent=2) + '\n')
return 0 if report['passed'] else 1
if __name__ == '__main__':
raise SystemExit(main())
@@ -0,0 +1,23 @@
# Base Windows capture-helper SHA256: 517e0b158fad0070c556d84ce62749159a4b1f1a7b4bee93632f62cc23b3ec0e
--- a/zluda/src/impl/d4r_replay.rs
+++ b/zluda/src/impl/d4r_replay.rs
@@ -15,6 +15,10 @@
"dltss_pwin_dec1_layer" | "dltss_pwin_dec0_layer" => Some(176),
"rrlite_enc1_4x4" | "rrlite_enc2_4x4" | "rrlite_dec2_4x4" | "rrlite_dec1_4x4" => Some(56),
"rrlite_enc3_tube_4x4" => Some(88),
+ // Audited SR 310.9.1 output variant: the reconstruction LUT pointer is
+ // at byte 280 of this exact 328-byte PTX argument block. Other texture
+ // kernels remain outside this capture hook's whitelist.
+ "hiluma_engine_output_depthreg_mvhi_ldr_max_v2_rel" => Some(328),
_ => None,
}
}
@@ -131,6 +135,8 @@
assert_eq!(super::parameter_size("rrlite_enc3_tube_4x4"), Some(88));
assert_eq!(super::parameter_size("dltss_pwin_enc0_layer_prep"), None);
assert_eq!(super::parameter_size("rrlite_enc3_tube_4x4_prep"), None);
+ assert_eq!(super::parameter_size("hiluma_engine_output_depthreg_mvhi_ldr_max_v2_rel"), Some(328));
+ assert_eq!(super::parameter_size("hiluma_engine_output_depthreg_mvhi_hdr_max_v2_rel"), None);
assert_eq!(super::parameter_size("dltss_pwin_unknown_layer"), None);
}
}