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:
1 parent
247fe1092d
commit
a617c818de
62 files changed
+5894
-89
No files matched your search
@@ -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;
|
||||
}
|
||||
@@ -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())
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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, ®ion);
|
||||
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, ®ion, 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");
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = ⦥
|
||||
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);
|
||||
}
|
||||
};
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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();
|
||||
|
||||
@@ -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() +
|
||||
|
||||
@@ -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()
|
||||
@@ -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')
|
||||
|
||||
@@ -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, ®ion, 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, ©);
|
||||
vkCmdCopyImageToBuffer(cb, output, VK_IMAGE_LAYOUT_GENERAL, shared, 1, ©);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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())
|
||||
@@ -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}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user