Advance native Windows RR integration, kernel tooling and local packaging

Implement native NGX feature-13 RR through a LUID-matched D3D12/Vulkan
shared-memory and timeline-fence bridge. Add owned parameter snapshots,
GPU resource lifetime handling, inherited/enhanced barriers, optional
guides, sampled input formats and depth-plane support with stencil
preservation. Separate RR initialization from the SR CUDA runtime and
expose explicit backend/model selection through player configuration.

Add synthetic bridge, snapshot and NGX lifecycle harnesses covering
camera/history/reset, parameter mutation, delayed submission, early
release, capability gating and unsupported-input rejection.

Add exact gfx11 D/E model generation, verified caches, versioned local
deployment with configuration backups, and isolated portable model
tools with pinned Python, NumPy and glslang dependencies.

Extend Windows K benchmarking and private native-cache selection.
Fix LLVM 22 emitter/helper compatibility and preserve exact-output
validation for local output-kernel optimization.

Add native FSR RR provider diagnostics, local development package
refresh tooling, installer/checker updates, FidelityFX headers and
Windows parity documentation.

Keep NVIDIA binaries, derived models and private optimized kernels out
of distributable packages. Native RR evidence remains synthetic;
game validation, FSR RR dispatch, installer integration and Linux
performance parity remain incomplete.
This commit is contained in:
ayo committed 2026-10-16 12:53:26 -04:00
1 parent 3345fffcd3
commit 247fe1092d
60 files changed
+7168 -66

No files matched your search

+140
View File
@@ -0,0 +1,140 @@
"""Compare K input batching using identical synthetic frames and alternating runs.
Uses an existing private runtime; never installs into a game. CPU interop timing
includes synchronization and NGX execution, and is not a GPU network-only time.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import re
import statistics
import subprocess
def main():
parser = argparse.ArgumentParser(description=__doc__)
for key in ('probe', 'shim', 'runtime', 'core', 'dlss', 'output'):
parser.add_argument('--' + key, type=lambda s: Path(s).resolve(), required=True)
parser.add_argument('--resolution', default='1920x1080')
parser.add_argument('--input-resolution', help='defaults to nominal Quality (two-thirds per axis)')
parser.add_argument('--frames', type=int, default=16)
parser.add_argument('--profile-stages', action='store_true',
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('--profile-kernels', action='store_true',
help='diagnostic deferred GPU events, allowing legacy-stream dependencies; timings are instrumented')
args = parser.parse_args()
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):
parser.error('resolution must be WIDTHxHEIGHT')
ow, oh = map(int, args.resolution.split('x'))
if not args.input_resolution:
args.input_resolution = f'{ow * 2 // 3}x{oh * 2 // 3}'
if not re.fullmatch(r'[1-9][0-9]*x[1-9][0-9]*', args.input_resolution):
parser.error('input-resolution must be WIDTHxHEIGHT')
build_root = Path(__file__).resolve().parents[2] / 'build'
if not args.output.is_relative_to(build_root.resolve()):
parser.error('output must stay under the repository ignored build directory')
args.output.mkdir(parents=True, exist_ok=False)
report = {'passed': False, 'resolution': args.resolution, 'warmupFrames': 4,
'inputResolution': args.input_resolution,
'stageProfiling': args.profile_stages,
'kernelProfiling': args.profile_kernels,
'comparison': 'native kernels' if args.candidate_native else 'input batching',
'timing': 'CPU interop including NGX and synchronization', 'runs': []}
runtime = args.runtime
native = runtime / 'native'
if (native / 'gfx1101').is_dir():
native = native / 'gfx1101'
nvapi = runtime / 'nvapi/nvapi64.dll'
if not nvapi.is_file():
nvapi = runtime / 'nvapi/gfx1101/nvapi64.dll'
report['artifacts'] = {str(p): hashlib.sha256(p.read_bytes()).hexdigest()
for p in (args.probe, args.shim, args.core, args.dlss)}
report['nativeArtifacts'] = {p.name: hashlib.sha256(p.read_bytes()).hexdigest()
for p in sorted(native.glob('*')) if p.is_file()}
if args.candidate_native:
report['candidateNativeArtifacts'] = {p.name: hashlib.sha256(p.read_bytes()).hexdigest()
for p in sorted(args.candidate_native.glob('*')) if p.is_file()}
env = os.environ.copy()
# Keep profiling and arithmetic settings independent of the caller's shell.
for key in list(env):
if key.startswith('D4R_') or key in ('ZLUDA_LOG_DIR', 'ZLUDA_CACHE_DIR', 'ZLUDA_CUDA_LIB'):
del env[key]
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'),
D4R_NGX_DATA_DIR=str(args.output / 'ngx-data'))
env['PATH'] = str(runtime / 'hip/bin') + os.pathsep + str(runtime / 'zluda') + os.pathsep + env['PATH']
if args.profile_stages:
env['D4R_PROFILE_STAGES'] = '1'
if args.profile_kernels:
env.update(D4R_ZLUDA_PROFILE_DEFERRED='1', D4R_ZLUDA_PROFILE_ALLOW_LEGACY='1',
D4R_ZLUDA_PROFILE_EVERY='1')
try:
reference = None
for index, batched in enumerate((False, True, True, False)):
label = ('native-candidate' if args.candidate_native else 'batch') if batched else 'control'
output = args.output / f'{index}-{label}'
output.mkdir()
env['D4R_DIAG_DIR'] = str(output)
if batched and not args.candidate_native:
env['D4R_BATCH_INPUT_COPIES'] = '1'
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'),
'--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)]
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)
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}')
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:
raise RuntimeError(f'run {index}: requested dimensions/interop mode not applied')
if len(re.findall(r'D3D12_OUTPUT preset=11 frame=\d+ finite=1 ', text)) != args.frames:
raise RuntimeError(f'run {index}: missing finite-output checks')
times = [float(x) for x in re.findall(r'D4R_FRAME .*?interop_ngx_ms=([0-9.]+)', text)]
if len(times) != args.frames:
raise RuntimeError(f'run {index}: incomplete frame timing')
hashes = {p.name: hashlib.sha256(p.read_bytes()).hexdigest() for p in output.glob('output-*.rgba16f')}
if len(hashes) != args.frames:
raise RuntimeError(f'run {index}: incomplete output readback')
if reference is None:
reference = hashes
elif hashes != reference:
raise RuntimeError(f'run {index}: output differs from control')
report['runs'].append({'batch': batched and not bool(args.candidate_native),
'candidate': batched, 'allFrameMs': times,
'medianMs': statistics.median(times[4:]), 'outputHashes': hashes})
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:]]
key = ('candidateMedianMs' if args.candidate_native else 'batchMedianMs') if mode else 'controlMedianMs'
report[key] = statistics.median(values)
report['passed'] = True
except Exception as error:
report['error'] = str(error)
print(str(error), flush=True)
finally:
(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())
+20 -3
View File
@@ -14,6 +14,7 @@ import shutil
import subprocess
import sys
from gpu_targets import TARGETS
from llvm22_ir import compatible_ir
def require_code_object_target(path, target):
@@ -39,6 +40,8 @@ def main():
parser.error('this private experimental build recipe supports K v2 input/output variants only')
repo = pathlib.Path(__file__).resolve().parents[2]
output = args.output_directory.resolve()
if not output.is_relative_to((repo / 'build').resolve()):
parser.error('private texture outputs must stay under the repository ignored build directory')
output.mkdir(parents=True, exist_ok=True)
for existing in output.glob('*.hsaco'):
require_code_object_target(existing, args.gpu_arch)
@@ -46,8 +49,16 @@ def main():
work.mkdir(exist_ok=True)
hip = args.hip_root.resolve()
zluda = args.zluda_root.resolve()
tools = {name: hip / 'bin' / (name + '.exe') for name in ('clang++', 'llvm-dis', 'llvm-as')}
tools = {}
for name in ('clang++', 'llvm-dis', 'llvm-as'):
candidates = [hip / folder / (name + '.exe') for folder in ('lib/llvm/bin', 'bin')]
tools[name] = next((path for path in candidates if path.is_file()), candidates[0])
emitter = zluda / 'd4r_emit.exe'
# The emitter's LLVM reader must consume the helper bitcode. A newer HIP
# compiler can emit textual IR, but its bitcode attributes may be unreadable.
emitter_assembler = zluda / 'llvm-as.exe'
if emitter_assembler.is_file():
tools['llvm-as'] = emitter_assembler
for path in [args.dlss_dll, emitter, *tools.values()]:
if not path.is_file():
parser.error(f'missing dependency: {path}')
@@ -63,7 +74,8 @@ def main():
def run(label, command):
with (work / (label + '.log')).open('wb') as log:
result = subprocess.run([str(arg) for arg in command], env=env, stdout=log, stderr=subprocess.STDOUT)
result = subprocess.run([str(arg) for arg in command], env=env, stdout=log,
stderr=subprocess.STDOUT, timeout=600)
if result.returncode:
print((work / (label + '.log')).read_text(errors='replace')[-8000:], file=sys.stderr)
raise RuntimeError(f'{label} failed ({result.returncode}); see {work}')
@@ -89,11 +101,16 @@ def main():
run('disassemble', [tools['llvm-dis'], raw, '-o', ir])
lines = []
for line in ir.read_text().splitlines():
# Helpers are linked directly into the translated module. Their ThinLTO
# index becomes stale when the unused HIP/LLVM globals below are removed.
if re.match(r'^\^\d+\s*=', line):
continue
if re.search(r'@llvm.used|wchar_size|llvm.module.flags|__hip_cuid', line):
continue
line = line.replace('optnone', '').replace(f'"target-cpu"="{args.gpu_arch}"', '')
lines.append(re.sub(r'"target-features"="[^"]+"', '', line))
ir.write_text('\n'.join(lines) + '\n')
ir.write_text(compatible_ir('\n'.join(lines) + '\n') if emitter_assembler.is_file()
else '\n'.join(lines) + '\n')
run('assemble', [tools['llvm-as'], ir, '-o', extra])
env['D4R_ZLUDA_EXTRA_BC'] = str(extra)
run('emit', [emitter, edited, work / 'emitted', args.gpu_arch])
+155
View File
@@ -0,0 +1,155 @@
"""Build exact gfx11 RR D/E models natively on Windows, without Git Bash.
Derived weights/shaders remain under ignored build/. Nothing is installed into
a game or bundled. Uses the same packers and compiler recipe as the Linux shell.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import re
import shutil
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('--dll', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--preset', choices=('D', 'E'), default='D')
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('derived models must stay under ignored build/')
if sha(args.dll) != '4bc7ea5fcb2f32cf86bc2cb072e8d2860914a0be511cbdcb2fc206c1d9d83b80':
parser.error('requires pinned NVIDIA RR 310.9.1 DLL')
if sha(args.glslang) != '9643709c02c83e26773f96b6a87f197ab2e84f78be0879ad8fb3619418b5d9ad':
parser.error('requires pinned glslang 16.6.0 executable')
env = os.environ.copy()
for key in tuple(env):
if key.startswith('D4R_RR_'):
del env[key]
env.update(D4R_RR_GFX11='1', D4R_RR_RELAXED='0', PYTHONPATH=str(repo / '.tools/python/vendor'))
env['PATH'] = str(args.glslang.parent) + os.pathsep + env['PATH']
numpy_version = subprocess.check_output([sys.executable, '-c', 'import numpy; print(numpy.__version__)'], env=env, text=True, timeout=30).strip()
sources = {str(Path(__file__).relative_to(repo)).replace('\\', '/'): sha(Path(__file__))}
for directory in ('engine', 'vulkan', 'kernels/tools'):
for path in sorted((repo / directory).rglob('*')):
if path.is_file() and path.suffix in ('.py', '.comp', '.glsl', '.h', '.inc', '.sh', '.txt'):
sources[path.relative_to(repo).as_posix()] = sha(path)
identity = {'schema': 1, 'preset': args.preset, 'architecture': 'gfx1101', 'arithmetic': 'exact-gfx11',
'dll': sha(args.dll), 'glslang': sha(args.glslang), 'python': sha(Path(sys.executable)),
'numpy': numpy_version, 'sources': sources}
stamp = args.output / 'windows-native-python-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('RR cache changed; use a fresh output directory')
print('RR_NATIVE_CACHE_HIT preset=' + args.preset)
return
args.output.mkdir(parents=True)
out = args.output
work = out / 'native-work'
work.mkdir()
rr = repo / 'engine/rr'
serial = 0
def run(command, label):
nonlocal serial
serial += 1
print(f'RR_BUILD step={serial} {label}', flush=True)
log = work / f'{serial:02}-{label}.log'
with log.open('wb') as stream:
process = subprocess.Popen([str(p) for p in command], cwd=repo, env=env, stdout=stream, stderr=subprocess.STDOUT)
try:
code = process.wait(timeout=600)
except subprocess.TimeoutExpired:
if os.name == 'nt':
subprocess.run(['taskkill', '/PID', str(process.pid), '/T', '/F'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=30)
else:
process.kill()
process.wait(timeout=30)
raise RuntimeError('RR build timed out; see ' + str(log))
if code:
raise RuntimeError(f'RR build failed ({code}); see {log}')
return log.read_text(errors='replace').strip()
def python(script, *arguments, label):
return run([sys.executable, script, *arguments], label)
python(repo / 'kernels/tools/extract_dlss_ptx.py', args.dll, work / 'ptx', label='extract')
python(repo / 'engine/compile_rr.py', '--dll', args.dll, '--preset', args.preset, out,
'--ptx-dir', work / 'ptx', '--network-bindings-only', label='bindings')
python(rr / 'dll_weights.py', args.dll, work / 'dll', '--model', '0' if args.preset == 'D' else '1', label='weights')
manifest = (out / 'rr.txt').read_text().replace(f'preset {4 if args.preset == "D" else 5}\n',
f'preset {4 if args.preset == "D" else 5}\narithmetic gfx11\n')
defines = ['-DRR_EXACT_CONVERT', '-DRR_GFX11'] + (['-DRR_PRESET_D'] if args.preset == 'D' else [])
def native(stage, shader, include):
nonlocal manifest
weights = out / f'weights-{stage}m.bin'
wide = int(python(rr / 'widen_weights.py', weights, label='widen-' + stage).splitlines()[-1])
run([args.glslang, '--target-env', 'vulkan1.3', '-V', *defines, f'-DW_WIDE={wide}u',
'-I' + str(include), '-I' + str(rr), '-o', out / f'{stage}_m.spv', shader], 'compile-' + stage)
python(rr / 'spv_exact_convert.py', out / f'{stage}_m.spv', label='exact-' + stage)
pattern = rf'^stage {stage} {stage}\.spv 32 weights-{stage}\.bin (.*)$'
def replacement(match):
tail = match[1].removesuffix(' noopt')
return f'stage {stage} {stage}_m.spv 32 weights-{stage}m.bin {tail}'
manifest, count = re.subn(pattern, replacement, manifest, flags=re.MULTILINE)
if count != 1:
raise RuntimeError('Missing/duplicate native stage: ' + stage)
for stage in ('enc1', 'enc2', 'enc3', 'enc4', 'enc5', 'dec4', 'dec3', 'dec2', 'dec1'):
directory = work / stage
directory.mkdir()
shutil.copyfile(work / 'dll' / f'{stage}.npz', directory / 'weights.npz')
python(rr / 'swin_pack.py', stage, directory / 'weights.npz', out / f'weights-{stage}m.bin', directory / 'swin_stage.h', label='pack-' + stage)
native(stage, rr / 'swin_m.comp', directory)
for stage in ('dec0', 'enc0'):
directory = work / stage
directory.mkdir()
shutil.copyfile(work / 'dll' / f'{stage}.npz', directory / 'weights.npz')
if stage == 'dec0':
python(rr / 'dec0_pack.py', directory / 'weights.npz', out / 'weights-dec0m.bin', directory / 'dec0_m_w.h', label='pack-dec0')
native(stage, rr / 'dec0_m.comp', directory)
else:
base = 0 if args.preset == 'D' else 4096
python(rr / 'enc0_pack.py', directory / 'weights.npz', directory / 'enc0_m.bin', directory / 'enc0_m_w.h', str(base), label='pack-enc0')
noise = b'' if not base else (work / 'dll/noise.bin').read_bytes()
if len(noise) != base:
raise RuntimeError('Invalid E noise table')
(out / 'weights-enc0m.bin').write_bytes(noise + (directory / 'enc0_m.bin').read_bytes())
python(rr / 'gen_enc0_m.py', out / 'enc0.generated.comp', directory / 'enc0_m.comp', label='gather-enc0')
native(stage, directory / 'enc0_m.comp', directory)
metadata = json.loads((out / 'model.json').read_text())
if metadata['stages']['hkpn'].get('shader') == 'generated from the DLL':
manifest = manifest.replace('stage hkpn hkpn.spv 32 ', 'stage hkpn hkpn.spv 64 ')
for stage in ('enc0', 'enc1', 'enc2', 'enc3', 'enc4', 'enc5', 'dec4', 'dec3', 'dec2', 'dec1', 'dec0'):
path = out / f'weights-{stage}.bin'
if path.exists():
path.unlink()
(out / 'rr.txt').write_text(manifest, newline='\n')
stages = [line.split() for line in manifest.splitlines() if line.startswith('stage ')]
if len(stages) != 12 or 'arithmetic gfx11' not in manifest:
raise RuntimeError('Incomplete native manifest')
for fields in stages:
if not (out / fields[2]).is_file() or not (out / fields[4]).is_file():
raise RuntimeError('Missing stage product')
textures = [] if fields[5] == '-' else list(map(int, fields[5].split(',')))
surfaces = [] if fields[6] == '-' else [[int(s.split(':')[0]), s.split(':')[1]] for s in fields[6].split(',')]
(out / fields[2]).with_suffix('.json').write_text(json.dumps({'textures': textures, 'surfaces': surfaces}) + '\n')
stamp.write_text(json.dumps({'identity': identity, 'products': products()}, indent=2) + '\n')
print('RR_NATIVE_MODEL_BUILT preset=' + args.preset + ' output=' + str(out))
if __name__ == '__main__':
main()
+1 -1
View File
@@ -29,7 +29,7 @@ int main(int argc, char** argv) {
for (const auto& pair : {std::make_pair("D4R_HIP_ROOT", args.hip_root),
std::make_pair("D4R_NVCUDA_DLL", args.cuda_dll), std::make_pair("D4R_NVAPI_DLL", args.nvapi_dll),
std::make_pair("D4R_NGX_CORE", args.ngx_core), std::make_pair("D4R_DLSS_DLL", args.dlss_dll)})
if (_putenv_s(pair.first, pair.second.c_str())) throw std::runtime_error("Cannot set runtime environment");
if (_putenv_s(pair.first, args.player_mode ? "" : pair.second.c_str())) throw std::runtime_error("Cannot set runtime environment");
HipApi hip(args.hip_root); hipDeviceProp_t props{}; hip.select_architecture(args.device, props);
ComPtr<IDXGIFactory4> factory; dx(CreateDXGIFactory1(IID_PPV_ARGS(factory.GetAddressOf())), "CreateDXGIFactory1");
ComPtr<IDXGIAdapter1> adapter;
+180 -6
View File
@@ -1,6 +1,10 @@
// Native Windows backend. The Linux/Wine implementation remains separate.
#include "ngx_windows_runtime.h"
#include "ngx_runtime_initialization.h"
#include "player_config.h"
#if defined(D4R_NATIVE_RR_BACKEND)
#include "rr_native_backend.h"
#endif
#include <dxgi.h>
#include <unordered_map>
#include <functional>
@@ -8,9 +12,102 @@
namespace {
std::mutex apiMutex;
std::shared_ptr<d4r::win::Runtime> runtime;
std::shared_ptr<d4r::win::RuntimeInitialization> initialization;
bool deferredSr = false;
void ensure_sr_runtime() {
if (!runtime) {
if (!initialization) throw std::runtime_error("NGX is not initialized");
runtime = initialization->create();
}
}
void initialize(ID3D12Device* device, unsigned long long app, const wchar_t* data, unsigned sdk,
const d4r::ngx::FeatureCommonInfo* common, const d4r::ngx::ProjectIdentity* project = nullptr) {
if (initialization) {
if (initialization->device.Get() != device) throw std::runtime_error("NGX already initialized on another device");
return;
}
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";
#endif
// Default SR startup remains eager. Native RR owns its Vulkan execution
// independently; SR initializes only when its capabilities/feature are used.
if (!native) runtime = candidate->create();
initialization = std::move(candidate); deferredSr = native;
}
struct Handle { unsigned Id; };
unsigned nextId = 1;
std::unordered_map<Handle*, std::shared_ptr<d4r::win::Feature>> features;
#if defined(D4R_NATIVE_RR_BACKEND)
struct RrFeature {
d4r::win::rr::Creation creation;
d4r::win::rr::Backend backend;
RrFeature(ID3D12Device* device, void* parameters, const std::string& directory)
: creation(parameters), backend(device, creation, directory) {}
};
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) != "native")
throw std::runtime_error("Native RR requires D4R_RR_BACKEND=native");
return d4r::win::env_path("D4R_RR_MODEL_DIR");
}
bool rr_ready(ID3D12Device* device) {
try {
const auto directory = rr_directory();
void* p = d4r_ngx_parameters_create();
if (!p) return false;
struct Cleanup { void* p; ~Cleanup() { d4r_ngx_parameters_destroy(p); } } cleanup{p};
for (const char* name : {"Width", "Height", "OutWidth", "OutHeight"}) d4r_ngx_set_uint(p, name, 64);
d4r_ngx_set_uint(p, "DLSS.Feature.Create.Flags", 3);
d4r::win::rr::Creation creation(p);
// Readiness checks the actual configured model's pipeline/weight load,
// adapter features and packet/fence import. No synthetic frame runs.
d4r::win::rr::Backend candidate(device, creation, directory);
return true;
} catch (const std::exception& error) {
if (std::getenv("D4R_RR_BACKEND")) std::fprintf(stderr, "D4R_RR_UNAVAILABLE %s\n", error.what());
return false;
}
}
struct RrSnapshot {
d4r::win::rr::Snapshot frame;
std::array<d4r::win::ResourceAccess, d4r::win::rr::Snapshot::Count> access;
std::array<bool, d4r::win::rr::Snapshot::Count> explicitState{};
RrSnapshot(const d4r::win::rr::Creation& creation, void* p, ID3D12GraphicsCommandList* list)
: frame(creation, p) {
const char* names[] = {"D4R.Color.State", "D4R.Motion.State", "D4R.Depth.State", "D4R.Normals.State",
"D4R.Roughness.State", "D4R.Diffuse.State", "D4R.Specular.State", "D4R.Output.State", "D4R.Exposure.State",
"D4R.HitDistance.State", "D4R.Disocclusion.State", "D4R.Subsurface.State", "D4R.BeforeParticles.State"};
for (size_t i = 0; i < access.size(); ++i) {
if (!frame.resources[i]) {
if (i < 8) throw std::runtime_error("Missing required NGX RR guide/output");
continue;
}
const auto fallback = d4r::win::ngx_resource_access(p, names[i], i == 7 ?
D3D12_RESOURCE_STATE_UNORDERED_ACCESS : D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
unsigned declared = 0;
explicitState[i] = d4r_ngx_get_uint(p, names[i], &declared) == 1;
access[i] = list ? d4r::win::commands::resource_access(list, frame.resources[i].Get(), fallback) :
d4r::win::commands::submitted_resource_access(frame.resources[i].Get(), fallback);
if (access[i].pending_split) throw std::runtime_error("NGX RR texture has an unfinished split barrier");
}
if (frame.settings.roughnessPacked) {
access[4] = access[3]; explicitState[4] = explicitState[3];
}
}
void resolve() {
for (size_t i = 0; i < access.size(); ++i) if (access[i].inherited) {
access[i] = d4r::win::commands::submitted_resource_access(frame.resources[i].Get(), access[i]);
// Explicit caller state remains valid when no prior submitted list
// supplied a tracked transition for this resource.
if (access[i].inherited && explicitState[i]) access[i].inherited = false;
if (access[i].pending_split || access[i].inherited) throw std::runtime_error("NGX RR inherited texture state unresolved");
}
}
};
#endif
std::unordered_map<void*, bool> parameters;
constexpr unsigned failure = 0xBAD00002, invalid = 0xBAD00005;
unsigned call(const std::function<unsigned()>& fn) noexcept {
@@ -27,7 +124,7 @@ unsigned call(const std::function<unsigned()>& fn) noexcept {
}
}
unsigned allocate(void** out, bool caps) {
if (!runtime || !out) return invalid;
if (!initialization || !out) return invalid;
void* value = d4r_ngx_parameters_create();
if (!value) return failure;
*out = nullptr;
@@ -36,6 +133,15 @@ unsigned allocate(void** out, bool caps) {
~LocalParameters() { if (p) d4r_ngx_parameters_destroy(p); }
} owned{value};
if (caps) {
try { ensure_sr_runtime(); }
catch (const std::exception& error) {
if (!deferredSr) throw;
std::fprintf(stderr, "D4R_SR_UNAVAILABLE %s\n", error.what());
d4r_ngx_set_int(value, "SuperSampling.Available", 0);
d4r_ngx_set_int(value, "SuperSampling.NeedsUpdatedDriver", 0);
d4r_ngx_set_int(value, "SuperSampling.FeatureInitResult", int(failure));
}
if (runtime) {
std::lock_guard<std::mutex> lock(runtime->mutex);
runtime->current(); void* official = nullptr;
d4r::win::ngx_check(runtime->capabilities(&official), "CUDA capabilities");
@@ -48,6 +154,14 @@ unsigned allocate(void** out, bool caps) {
void* callback = nullptr;
if (d4r_ngx_get_void(official, name, &callback) == 1) d4r_ngx_set_void(value, name, callback);
}
}
bool rrAvailable = false;
#if defined(D4R_NATIVE_RR_BACKEND)
rrAvailable = rr_ready(initialization->device.Get());
#endif
d4r_ngx_set_int(value, "SuperSamplingDenoising.Available", rrAvailable ? 1 : 0);
d4r_ngx_set_int(value, "SuperSamplingDenoising.NeedsUpdatedDriver", 0);
d4r_ngx_set_int(value, "SuperSamplingDenoising.FeatureInitResult", rrAvailable ? 1 : int(0xBAD00001u));
}
parameters.emplace(value, true); *out = value; owned.p = nullptr;
return 1;
@@ -61,7 +175,7 @@ API unsigned NVSDK_NGX_D3D12_Init_Ext(unsigned long long app, const wchar_t* dat
std::lock_guard<std::mutex> lock(apiMutex);
return call([&] {
d4r::win::player::ensure_environment();
if (!runtime) runtime = std::make_shared<d4r::win::Runtime>(device, app, data, sdk, static_cast<const d4r::ngx::FeatureCommonInfo*>(info));
initialize(device, app, data, sdk, static_cast<const d4r::ngx::FeatureCommonInfo*>(info));
if (const char* backend = std::getenv("D4R_D3D12_COMMAND_BACKEND"); backend && std::string(backend) == "1")
d4r::win::commands::install(device);
return 1u;
@@ -76,7 +190,7 @@ API unsigned NVSDK_NGX_D3D12_Init_ProjectID(const char* project, int engine, con
if (!project || !*project) return invalid;
d4r::win::player::ensure_environment();
d4r::ngx::ProjectIdentity identity{project, engine, version ? version : ""};
if (!runtime) runtime = std::make_shared<d4r::win::Runtime>(device, 0, data, sdk, static_cast<const d4r::ngx::FeatureCommonInfo*>(info), &identity);
initialize(device, 0, data, sdk, static_cast<const d4r::ngx::FeatureCommonInfo*>(info), &identity);
if (const char* backend = std::getenv("D4R_D3D12_COMMAND_BACKEND"); backend && std::string(backend) == "1") d4r::win::commands::install(device);
return 1u;
});
@@ -86,7 +200,19 @@ API unsigned NVSDK_NGX_D3D12_GetFeatureRequirements(IDXGIAdapter* adapter, const
if (!adapter || !info || !requirements) return invalid;
return call([&] {
*requirements = {}; requirements->FeatureSupported = 16; // Unsupported feature.
if (info->FeatureID != 1) return 1u;
if (info->FeatureID != 1) {
#if defined(D4R_NATIVE_RR_BACKEND)
if (info->FeatureID == 13) {
d4r::win::player::ensure_environment();
d4r::win::ComPtr<ID3D12Device> device;
if (SUCCEEDED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&device))) && rr_ready(device.Get())) {
requirements->FeatureSupported = 0;
std::strcpy(requirements->MinOSVersion, "10.0.22000.0");
}
}
#endif
return 1u;
}
d4r::win::player::ensure_environment();
DXGI_ADAPTER_DESC desc{}; d4r::win::dx(adapter->GetDesc(&desc), "NGX requirements adapter");
// OptiScaler can override the DXGI vendor ID. The physical HIP
@@ -119,7 +245,18 @@ API unsigned NVSDK_NGX_D3D12_GetScratchBufferSize(unsigned, const void*, size_t*
API unsigned NVSDK_NGX_D3D12_CreateFeature(ID3D12GraphicsCommandList*, unsigned feature, void* p, Handle** out) {
std::lock_guard<std::mutex> lock(apiMutex);
return call([&] {
if (!runtime || feature != 1 || !p || !out) return invalid;
if (!initialization || !p || !out) return invalid;
*out = nullptr;
#if defined(D4R_NATIVE_RR_BACKEND)
if (feature == 13) {
auto value = std::make_shared<RrFeature>(initialization->device.Get(), p, rr_directory());
d4r::win::commands::install(initialization->device.Get());
auto handle = std::make_unique<Handle>(); handle->Id = nextId++;
rrFeatures.emplace(handle.get(), std::move(value)); *out = handle.release(); return 1u;
}
#endif
if (feature != 1) return invalid;
ensure_sr_runtime();
d4r::win::player::apply_preset(p);
auto value = std::make_shared<d4r::win::Feature>(runtime, p);
auto handle = std::make_unique<Handle>(); handle->Id = nextId++;
@@ -127,12 +264,37 @@ API unsigned NVSDK_NGX_D3D12_CreateFeature(ID3D12GraphicsCommandList*, unsigned
});
}
API unsigned d4r_D3D12_EvaluateAtBoundary(ID3D12CommandQueue* queue, Handle* handle, void* p) {
#if defined(D4R_NATIVE_RR_BACKEND)
std::shared_ptr<RrFeature> rr;
{ std::lock_guard<std::mutex> lock(apiMutex);
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;
});
#endif
std::shared_ptr<d4r::win::Feature> feature;
{ std::lock_guard<std::mutex> lock(apiMutex);
auto it = features.find(handle); if (it == features.end() || !p) return invalid; feature = it->second; }
return call([&] { feature->evaluate_boundary(queue, p); return 1u; });
}
API unsigned NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCommandList* list, const Handle* handle, void* p, void*) {
#if defined(D4R_NATIVE_RR_BACKEND)
std::shared_ptr<RrFeature> rr;
{ std::lock_guard<std::mutex> lock(apiMutex);
auto it = rrFeatures.find(const_cast<Handle*>(handle)); if (it != rrFeatures.end()) rr = it->second; }
if (rr) return call([&] {
if (!p || !list) return invalid;
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);
d4r::win::commands::record_boundary(list, [rr, snapshot](ID3D12CommandQueue* queue) {
snapshot->resolve(); rr->backend.evaluate(queue, snapshot->frame, snapshot->access);
});
return 1u;
});
#endif
std::shared_ptr<d4r::win::Feature> feature;
{ std::lock_guard<std::mutex> lock(apiMutex);
auto it = features.find(const_cast<Handle*>(handle));
@@ -194,6 +356,13 @@ API unsigned NVSDK_NGX_D3D12_EvaluateFeature_C(ID3D12GraphicsCommandList* list,
return NVSDK_NGX_D3D12_EvaluateFeature(list, handle, p, callback);
}
API unsigned NVSDK_NGX_D3D12_ReleaseFeature(Handle* handle) {
#if defined(D4R_NATIVE_RR_BACKEND)
std::shared_ptr<RrFeature> rr;
{ 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; });
#endif
std::shared_ptr<d4r::win::Feature> feature;
{ std::lock_guard<std::mutex> lock(apiMutex);
auto it = features.find(handle); if (it == features.end()) return invalid;
@@ -206,10 +375,15 @@ API unsigned NVSDK_NGX_D3D12_Shutdown() {
std::lock_guard<std::mutex> lock(apiMutex);
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) delete pair.first;
rrFeatures.clear();
#endif
for (auto& pair : features) delete pair.first;
features.clear();
for (auto& pair : parameters) d4r_ngx_parameters_destroy(pair.first);
parameters.clear(); runtime.reset(); return 1u;
parameters.clear(); runtime.reset(); initialization.reset(); deferredSr = false; return 1u;
});
}
API unsigned NVSDK_NGX_D3D12_Shutdown1(ID3D12Device*) { return NVSDK_NGX_D3D12_Shutdown(); }
+135
View File
@@ -0,0 +1,135 @@
"""Verify and deploy a locally built RR model; optionally select it in d4r.ini.
Existing models are preserved. Configuration changes receive an external backup.
No NVIDIA DLL, compiler, driver, package archive or system setting is changed.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import re
import shutil
import uuid
def sha(data):
return hashlib.sha256(data).hexdigest()
def select_native(data, model):
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, pending = [], False, False, set()
values = {'backend': 'native', 'modeldir': model}
def flush():
if pending and result and not result[-1].endswith(('\n', '\r')):
result.append(newline)
for key in ('backend', 'modeldir'):
if key in pending:
result.append(('Backend' if key == 'backend' else 'ModelDir') + ' = ' + values[key] + newline)
pending.clear()
for line in text.splitlines(keepends=True):
stripped = line.strip()
if stripped.startswith('['):
if in_rr:
flush()
in_rr = stripped.split(']', 1)[0][1:].strip().casefold() == 'rayreconstruction'
if in_rr:
found = True
pending = set(values)
if in_rr and '=' in line and not stripped.startswith((';', '#')):
key = line.split('=', 1)[0].strip().casefold()
if key in values:
prefix, body = line.split('=', 1)
ending = '\r\n' if body.endswith('\r\n') else '\n' if body.endswith('\n') else ''
body = body.removesuffix(ending) if ending else body
comment = re.search(r'\s[;#]', body)
suffix = body[comment.start():] if comment else ''
result.append(prefix + '= ' + values[key] + suffix + ending)
pending.discard(key)
continue
result.append(line)
if in_rr:
flush()
if not found:
if result and not result[-1].endswith('\n'):
result.append(newline)
result.extend((newline, '[RayReconstruction]' + newline, 'Backend = native' + newline,
'ModelDir = ' + model + newline))
return (b'\xef\xbb\xbf' if bom else b'') + ''.join(result).encode('utf-8')
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--model', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--runtime', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--enable', action='store_true')
parser.add_argument('--backup-root', type=lambda p: Path(p).resolve(),
default=Path(os.environ.get('LOCALAPPDATA', str(Path.home()))) / 'd4r/backups')
args = parser.parse_args()
config = args.runtime / 'd4r.ini'
if not config.is_file():
parser.error('--runtime must contain d4r.ini')
if args.enable and args.backup_root.resolve().is_relative_to(args.runtime.parent):
parser.error('configuration backups must stay outside the game/runtime parent directory')
cache_data = (args.model / 'windows-native-python-cache.json').read_bytes()
cache = json.loads(cache_data)
identity = cache['identity']
if identity.get('preset') not in ('D', 'E') or identity.get('architecture') != 'gfx1101' or identity.get('arithmetic') != 'exact-gfx11':
raise RuntimeError('Unsupported local RR cache identity')
products = cache['products']
if not {'rr.txt', 'enc0.args', 'hkpn.args'}.issubset(products):
raise RuntimeError('RR cache omits required manifest/templates')
for name, expected in products.items():
if Path(name).name != name or name in ('.', '..') or '\\' in name or ':' in name:
raise RuntimeError('Invalid model product path: ' + name)
if sha((args.model / name).read_bytes()) != expected:
raise RuntimeError('Model product changed: ' + name)
manifest = (args.model / 'rr.txt').read_text()
stages = [line.split() for line in manifest.splitlines() if line.startswith('stage ')]
expected_stages = {'enc0', 'enc1', 'enc2', 'enc3', 'enc4', 'enc5', 'dec4', 'dec3', 'dec2', 'dec1', 'dec0', 'hkpn'}
if (not manifest.startswith('D4RR0001\n') or
f'preset {4 if identity["preset"] == "D" else 5}' not in manifest.splitlines() or
len(stages) != 12 or {fields[1] for fields in stages} != expected_stages or 'arithmetic gfx11' not in manifest):
raise RuntimeError('Incomplete RR model manifest')
for fields in stages:
if fields[2] not in products or fields[4] not in products:
raise RuntimeError('Stage product not covered by cache')
name = 'rr-' + identity['preset'].lower() + '-' + sha(cache_data)[:12]
destination = args.runtime / 'engine' / name
if not destination.resolve().is_relative_to(args.runtime):
raise RuntimeError('RR destination must stay within the selected runtime')
reused = destination.exists()
if not reused:
destination.mkdir(parents=True)
for product in products:
shutil.copy2(args.model / product, destination / product)
(destination / 'd4r-private-rr.json').write_text(json.dumps({'privateLocalModel': True, 'cacheSha256': sha(cache_data), 'products': products}, indent=2) + '\n')
for product, expected in products.items():
if sha((destination / product).read_bytes()) != expected:
raise RuntimeError('Existing/deployed RR model differs; preserving it: ' + str(destination))
backup = None
if args.enable:
original = config.read_bytes()
updated = select_native(original, destination.relative_to(args.runtime).as_posix())
if updated != original:
args.backup_root.mkdir(parents=True, exist_ok=True)
backup = args.backup_root / ('rr-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 deployment; preserved current configuration')
os.replace(temporary, config)
print(json.dumps({'deployed': str(destination), 'preset': identity['preset'], 'productsVerified': len(products),
'reused': reused, 'enabled': args.enable, 'configBackup': str(backup) if backup else None,
'privateLocalModel': True, 'published': False}, indent=2))
if __name__ == '__main__':
main()
+2
View File
@@ -119,6 +119,7 @@ struct Args {
std::string pixel_profile = "baseline";
std::string barrier_mode = "legacy";
bool early_indirect = false;
bool player_mode = false;
std::string ngx_core, dlss_dll;
std::string nvapi_dll;
std::string ngx_abi = "driver";
@@ -160,6 +161,7 @@ struct Args {
barrier_mode = value;
}
else if (key == "--early-indirect") early_indirect = value == "1";
else if (key == "--player-mode") player_mode = 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;
+39
View File
@@ -0,0 +1,39 @@
#pragma once
#include <windows.h>
#include <d3d12.h>
#include <dxgi1_4.h>
#include <cstdio>
#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)
{
LUID luid{};
device->GetAdapterLuid(&luid);
IDXGIFactory4* factory = nullptr;
if (FAILED(CreateDXGIFactory2(0, IID_PPV_ARGS(&factory)))) {
detail = "native Windows: DXGI factory query failed";
return false;
}
IDXGIAdapter1* adapter = nullptr;
const HRESULT result = factory->EnumAdapterByLuid(luid, IID_PPV_ARGS(&adapter));
factory->Release();
if (FAILED(result)) {
detail = "native Windows: device adapter query failed";
return false;
}
DXGI_ADAPTER_DESC1 desc{};
const HRESULT described = adapter->GetDesc1(&desc);
adapter->Release();
if (FAILED(described)) {
detail = "native Windows: adapter description failed";
return false;
}
char text[160];
std::snprintf(text, sizeof(text), "native Windows DXGI %04x:%04x; installed D3D12 driver path",
desc.VendorId, desc.DeviceId);
detail = text;
return true;
}
+64
View File
@@ -0,0 +1,64 @@
"""Adapt textual HIP helper IR to the pinned LLVM 22 reader without relaxing FP."""
import argparse
from pathlib import Path
import re
import struct
def float_hex(match):
bits = int(match[1], 16)
sign, exponent, fraction = bits >> 31, (bits >> 23) & 255, bits & 0x7fffff
if exponent:
wide_exponent = 2047 if exponent == 255 else exponent + 896
wide_fraction = fraction << 29
elif fraction:
leading = fraction.bit_length() - 1
wide_exponent = leading - 149 + 1023
wide_fraction = (fraction - (1 << leading)) << (52 - leading)
else:
wide_exponent = wide_fraction = 0
# LLVM's legacy float spelling uses the exact widening's binary64 bits.
# Integer operations preserve signed zero and signaling/quiet NaN payloads.
return f'0x{((sign << 63) | (wide_exponent << 52) | wide_fraction):016X}'
def compatible_ir(text):
text = re.sub(r'\bf0x([0-9A-Fa-f]{16})\b', r'0x\1', text)
text = re.sub(r'\bf0x([0-9A-Fa-f]{4})\b', r'0xH\1', text)
text = re.sub(r'\bf0x([0-9A-Fa-f]{8})\b', float_hex, text)
text = text.replace('denormal_fpenv(dynamic)', '"denormal-fp-math"="dynamic,dynamic"')
# This newer optimization promise is absent in LLVM 22. Dropping a promise
# weakens what the optimizer may assume; it does not change instructions.
text = re.sub(r'\bnocreateundeforpoison\b', '', text)
text = re.sub(r'\bmemory\([^)]*\btarget_mem\b[^)]*\)', '', text)
text = re.sub(r'\b(uitofp|sitofp|fptoui|fptosi)\s+(?:(?:nnan|ninf|nsz|arcp|contract|afn|reassoc|fast)\s+)+',
r'\1 ', text)
# LLVM 24 permits short decimal floats that the older reader rejects.
# Restrict conversion to unquoted literals whose nearest IR type is float/half;
# double literals and string attributes retain their original spelling.
result = []
decimal = re.compile(r'(?<![\w.])[-+]?\d+\.\d+(?:[eE][-+]?\d+)?(?![\w.])')
for line in text.splitlines():
pieces = re.split(r'("(?:[^"\\]|\\.)*")', line)
for index in range(0, len(pieces), 2):
piece = pieces[index]
def convert(match):
types = re.findall(r'\b(float|double|half|bfloat|fp128|i\d+)\b', piece[:match.start()])
if not types or types[-1] not in ('float', 'half'):
return match[0]
if types[-1] == 'half':
bits = struct.unpack('>H', struct.pack('>e', float(match[0])))[0]
return f'0xH{bits:04X}'
bits = struct.unpack('>I', struct.pack('>f', float(match[0])))[0]
return float_hex(re.match(r'(.*)', f'{bits:08X}'))
pieces[index] = decimal.sub(convert, piece)
result.append(''.join(pieces))
return '\n'.join(result) + '\n'
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('input', type=Path)
parser.add_argument('output', type=Path)
args = parser.parse_args()
args.output.write_text(compatible_ir(args.input.read_text()))
+1
View File
@@ -1,5 +1,6 @@
#pragma once
#include <cstdint>
#include <initializer_list>
struct ID3D12Resource;
extern "C" {
void d4r_ngx_set_ull(void*, const char*, unsigned long long);
@@ -0,0 +1,42 @@
#pragma once
#include "ngx_windows_runtime.h"
namespace d4r::win {
// Own the complete Init contract when native RR defers the independent SR
// runtime. The caller may mutate or free its strings/path list after Init.
struct RuntimeInitialization {
ComPtr<ID3D12Device> device;
unsigned long long application;
unsigned sdk;
std::wstring data;
ngx::FeatureCommonInfo common{};
std::vector<std::wstring> paths;
std::vector<const wchar_t*> pathPointers;
ngx::ProjectIdentity project;
bool hasCommon = false, hasProject = false;
RuntimeInitialization(ID3D12Device* d3d, unsigned long long app, const wchar_t* directory, unsigned version,
const ngx::FeatureCommonInfo* info, const ngx::ProjectIdentity* identity)
: device(d3d), application(app), sdk(version), data(directory && *directory ? directory : std::filesystem::current_path().wstring()) {
if (!d3d) throw std::runtime_error("Null NGX initialization device");
if (identity) { project = *identity; hasProject = true; }
if (info) {
if (info->PathListInfo.Length > 1024 || (info->PathListInfo.Length && !info->PathListInfo.Path))
throw std::runtime_error("Invalid NGX initialization path list");
hasCommon = true;
// Runtime consumes only path and logging information. InternalData
// is opaque and must not become a deferred borrowed pointer.
if (sdk >= 0x14) common.LoggingInfo = info->LoggingInfo;
for (unsigned i = 0; i < info->PathListInfo.Length; ++i)
if (info->PathListInfo.Path[i]) paths.emplace_back(info->PathListInfo.Path[i]);
for (const auto& path : paths) pathPointers.push_back(path.c_str());
common.PathListInfo = {pathPointers.data(), unsigned(pathPointers.size())};
}
}
RuntimeInitialization(const RuntimeInitialization&) = delete;
RuntimeInitialization& operator=(const RuntimeInitialization&) = delete;
std::shared_ptr<Runtime> create() const {
return std::make_shared<Runtime>(device.Get(), application, data.c_str(), sdk,
hasCommon ? &common : nullptr, hasProject ? &project : nullptr);
}
};
}
+45 -8
View File
@@ -26,6 +26,9 @@ struct Settings {
bool nativeFp8 = false;
bool asyncInterop = false, validateOutput = false, verbose = false, showWatermark = false;
std::wstring cacheDir;
std::wstring nativeDir;
std::string rrBackend = "off";
std::wstring rrModelDir;
};
struct State {
bool active = false;
@@ -122,6 +125,17 @@ inline Settings read_settings(const std::vector<IniEntry>& ini, std::vector<std:
notes.push_back("d4r.ini: [Debug] Log must be normal or verbose, not '" + entry.value + "'; using normal");
} else if (name == "paths.cachedir") {
if (!entry.value.empty() && lower(entry.value) != "auto") settings.cacheDir = diag::wide(entry.value);
} else if (name == "paths.nativedir") {
if (!entry.value.empty() && lower(entry.value) != "auto") settings.nativeDir = diag::wide(entry.value);
} else if (name == "rayreconstruction.backend") {
const auto value = lower(entry.value);
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");
} else { settings.rrBackend = "off"; notes.push_back("d4r.ini: [RayReconstruction] Backend must be off, native or fsr; using off"); }
} 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");
}
return settings;
@@ -192,14 +206,31 @@ inline void verify_dll(const std::filesystem::path& dir, const std::vector<Pin>&
// The installer used to check these before touching the game; a drag-in
// install checks them at startup instead.
inline std::filesystem::path native_code_object_dir(const std::filesystem::path& dir) {
inline std::filesystem::path native_code_object_dir(const std::filesystem::path& dir, const Settings* settings = nullptr) {
if (settings && !settings->nativeDir.empty()) {
auto root = std::filesystem::path(settings->nativeDir);
if (root.is_relative()) root = dir / root;
const auto arch = root / diag::wide(diag::HipApi::target_arch());
return std::filesystem::is_directory(arch) ? arch : root;
}
const auto arch = dir / L"native" / diag::wide(diag::HipApi::target_arch());
return std::filesystem::is_directory(arch) ? arch : dir / L"native";
}
inline void verify_code_objects(const std::filesystem::path& dir) {
inline void verify_code_objects(const std::filesystem::path& dir, const Settings* settings = nullptr) {
const std::string arch = diag::HipApi::target_arch();
std::vector<std::filesystem::path> objects{dir / (L"pixel_convert_" + diag::wide(arch) + L".hsaco")};
const auto native = native_code_object_dir(dir);
const auto native = native_code_object_dir(dir, settings);
if (settings && !settings->nativeDir.empty() &&
(!std::filesystem::is_directory(native) || !std::filesystem::is_regular_file(native / L"d4r-kernels.txt")))
throw std::runtime_error("[Paths] NativeDir must contain the complete native kernel set and d4r-kernels.txt");
if (settings && !settings->nativeDir.empty()) {
for (const char* name : {"dltss_pwin_enc0_layer", "dltss_pwin_enc1_layer", "dltss_pwin_enc2_layer",
"dltss_pwin_enc3_layer", "dltss_pwin_enc4_layer", "dltss_pwin_dec5_layer", "dltss_pwin_dec4_layer",
"dltss_pwin_dec3_layer", "dltss_pwin_dec2_layer", "dltss_pwin_dec1_layer", "dltss_pwin_dec0_layer",
"rrlite_enc1_4x4", "rrlite_enc2_4x4", "rrlite_enc3_tube_4x4", "rrlite_dec2_4x4", "rrlite_dec1_4x4"})
if (!std::filesystem::is_regular_file(native / (std::string(name) + ".hsaco")))
throw std::runtime_error(std::string("[Paths] NativeDir is incomplete; missing ") + name);
}
if (!std::filesystem::is_directory(native)) throw std::runtime_error("d4r\\native (or d4r\\native\\" + arch + ") is missing; extract the d4r ZIP again");
for (const auto& entry : std::filesystem::directory_iterator(native))
if (entry.path().extension() == L".hsaco") objects.push_back(entry.path());
@@ -212,9 +243,9 @@ inline void verify_code_objects(const std::filesystem::path& dir) {
}
}
inline void verify_native_fp8_bundle(const std::filesystem::path& dir) {
inline void verify_native_fp8_bundle(const std::filesystem::path& dir, const Settings* settings = nullptr) {
const auto arch = diag::HipApi::target_arch();
const auto marker = native_code_object_dir(dir) / L"d4r-native-fp8.txt";
const auto marker = native_code_object_dir(dir, settings) / L"d4r-native-fp8.txt";
if (!std::filesystem::is_regular_file(marker))
throw std::runtime_error("[Kernels] NativeFp8=true needs a matching gfx12 token-lane M bundle; rebuild with -TokenLanes and restart.");
const auto contents = read_text(marker);
@@ -260,6 +291,10 @@ inline void redirect(FILE* crt, DWORD stream, const std::filesystem::path& path)
SetStdHandle(stream, reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(crt))));
}
inline std::filesystem::path rr_model_directory(const std::filesystem::path& dir, const Settings& settings) {
const std::filesystem::path model = settings.rrModelDir.empty() ? std::filesystem::path(L"engine/rr-d") : std::filesystem::path(settings.rrModelDir);
return std::filesystem::absolute(model.is_relative() ? dir / model : model).lexically_normal();
}
inline void apply(const std::filesystem::path& dir, const Settings& settings) {
std::filesystem::path cache = settings.cacheDir;
if (cache.empty()) {
@@ -276,6 +311,8 @@ inline void apply(const std::filesystem::path& dir, const Settings& settings) {
const auto compatNvapi = compat_nvapi_path(dir);
// Same codegen and paths as windows-game.ps1; only diagnostics differ.
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_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())},
@@ -283,7 +320,7 @@ inline void apply(const std::filesystem::path& dir, const Settings& settings) {
{L"D4R_NGX_CORE", p(L"ngx\\_nvngx.dll")}, {L"D4R_DLSS_DLL", p(L"dlss\\nvngx_dlss.dll")},
{L"D4R_NGX_DATA_DIR", std::optional<std::wstring>(ngxData.wstring())},
{L"D4R_FORMAT_MODULE", p(L"pixel_convert_" + diag::wide(diag::HipApi::target_arch()) + L".hsaco")},
{L"D4R_ZLUDA_NATIVE_DIR", std::optional<std::wstring>(native_code_object_dir(dir).wstring())},{L"D4R_D3D12_COMMAND_BACKEND", on},
{L"D4R_ZLUDA_NATIVE_DIR", std::optional<std::wstring>(native_code_object_dir(dir, &settings).wstring())},{L"D4R_D3D12_COMMAND_BACKEND", on},
{L"D4R_ZLUDA_WMMA", on}, {L"D4R_ZLUDA_WMMA_FP8", on},
{L"D4R_ZLUDA_WMMA_FP8_NATIVE", settings.nativeFp8 ? on : off},
{L"D4R_ZLUDA_WMMA_F16_REFERENCE", on},
@@ -328,8 +365,8 @@ inline void ensure_environment() {
verify_dll(dir, pins, "dlss", L"dlss\\nvngx_dlss.dll", "nvngx_dlss.dll", current.notes);
verify_dll(dir, pins, "ngx", L"ngx\\_nvngx.dll", "_nvngx.dll", current.notes);
reject_translation_layer();
if (settings.nativeFp8) verify_native_fp8_bundle(dir);
verify_code_objects(dir);
if (settings.nativeFp8) verify_native_fp8_bundle(dir, &settings);
verify_code_objects(dir, &settings);
apply(dir, settings);
current.active = true; current.preset = settings.preset;
const char* model = settings.preset == 5 ? "E" : settings.preset == 12 ? "L" : settings.preset == 13 ? "M" : "K";
+22
View File
@@ -60,6 +60,28 @@ int wmain(int argc, wchar_t** argv) {
expect(!s.showWatermark && has_note(notes, "ShowWatermark must be true or false"), "invalid ShowWatermark falls back to hidden with a note");
s = settings("[Paths]\nCacheDir = D:\\d4r-cache\n", notes);
expect(s.cacheDir == L"D:\\d4r-cache" && notes.empty(), "CacheDir path");
s = settings("[Paths]\nNativeDir = private-native\n", notes);
expect(s.nativeDir == L"private-native" && notes.empty(), "NativeDir path");
expect(player::native_code_object_dir(dir, &s) == dir / L"private-native", "relative NativeDir resolves beside d4r.ini");
expect(!error_of([&] { player::verify_code_objects(dir, &s); }).empty(), "incomplete private native directory rejected");
s.cacheDir = (dir / L"cache").wstring();
player::apply(dir, s);
expect(std::wstring(_wgetenv(L"D4R_ZLUDA_NATIVE_DIR")) == (dir / L"private-native").wstring(),
"player environment selects configured private native directory");
s = settings("[Paths]\nNativeDir = auto\n", notes);
expect(s.nativeDir.empty() && notes.empty(), "NativeDir auto preserves packaged kernels");
s = settings("[RayReconstruction]\nBackend = NATIVE\nModelDir = local-rr\n", notes);
expect(s.rrBackend == "native" && s.rrModelDir == L"local-rr" && notes.empty(), "native RR settings parse independently of SR");
expect(player::rr_model_directory(dir, s) == std::filesystem::absolute(dir / L"local-rr").lexically_normal(), "relative RR model resolves beside d4r.ini");
s.cacheDir = (dir / L"cache").wstring(); player::apply(dir, s);
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");
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");
std::filesystem::create_directories(dir / L"nvapi");
{ std::ofstream(dir / L"nvapi" / L"nvapi64.dll", std::ios::binary) << "generic nvapi fixture"; }
+125
View File
@@ -0,0 +1,125 @@
"""Prepare a private portable RR model-tool bundle from pinned dependencies.
Downloads/extracts tools locally. Does not install Python, use pip, modify PATH,
change policy, build a model, or install into a game.
"""
import argparse
import hashlib
import json
from pathlib import Path, PurePosixPath
import shutil
import subprocess
import urllib.request
import uuid
import zipfile
DEPENDENCIES = {
'python-3.12.10-embed-amd64.zip': (
'https://www.python.org/ftp/python/3.12.10/python-3.12.10-embed-amd64.zip',
'4acbed6dd1c744b0376e3b1cf57ce906f9dc9e95e68824584c8099a63025a3c3'),
'numpy-2.4.6-cp312-cp312-win_amd64.whl': (
'https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl',
'd8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751'),
'glslang-16.6.0-windows-x86_64-release.zip': (
'https://github.com/KhronosGroup/glslang/releases/download/16.6.0/glslang-16.6.0-windows-x86_64-release.zip',
'82bf434e69b9bb4829de7e2b4bc2c5e7a7861e53d66cf75e5cc70f5f694a8d9b'),
'glslang-16.6.0-LICENSE.txt': (
'https://raw.githubusercontent.com/KhronosGroup/glslang/16.6.0/LICENSE.txt',
'17e70c676e1521ff3e4686f04a2053d93a7e28a33be8de7ec37ab0ff72feb677'),
}
def sha(path):
with path.open('rb') as stream:
return hashlib.file_digest(stream, 'sha256').hexdigest()
def extract(archive, destination):
with zipfile.ZipFile(archive) as source:
for entry in source.infolist():
name = PurePosixPath(entry.filename)
if name.is_absolute() or '..' in name.parts or '\\' in entry.filename or ':' in entry.filename:
raise RuntimeError('Unsafe dependency archive path')
if entry.is_dir():
continue
target = destination.joinpath(*name.parts)
target.parent.mkdir(parents=True, exist_ok=True)
with source.open(entry) as data, target.open('xb') as out:
shutil.copyfileobj(data, out)
def main():
repo = Path(__file__).resolve().parents[2]
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--output', required=True, type=lambda p: Path(p).resolve())
parser.add_argument('--archive', type=lambda p: Path(p).resolve(), help='optional clean portable-tools ZIP, before any model build')
parser.add_argument('--download-cache', type=lambda p: Path(p).resolve(), default=repo / '.tools/downloads')
args = parser.parse_args()
if args.output.exists() or not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('use a fresh output directory under ignored build/')
if args.archive and (args.archive.exists() or not args.archive.is_relative_to((repo / 'build').resolve()) or args.archive.is_relative_to(args.output)):
parser.error('use a fresh archive under build/, outside the bundle directory')
args.download_cache.mkdir(parents=True, exist_ok=True)
for name, (url, expected) in DEPENDENCIES.items():
path = args.download_cache / name
if not path.exists():
print('RR_TOOLS_DOWNLOAD ' + name, flush=True)
temporary = path.with_name(name + '.part-' + uuid.uuid4().hex)
with urllib.request.urlopen(url, timeout=60) as response, temporary.open('xb') as out:
shutil.copyfileobj(response, out)
if sha(temporary) != expected:
raise RuntimeError('Dependency download hash mismatch: ' + name)
temporary.rename(path)
if sha(path) != expected:
raise RuntimeError('Cached dependency changed: ' + name)
python = args.output / 'python'
recipe = args.output / 'recipe'
extract(args.download_cache / 'python-3.12.10-embed-amd64.zip', python)
extract(args.download_cache / 'numpy-2.4.6-cp312-cp312-win_amd64.whl', recipe / '.tools/python/vendor')
glslang = recipe / '.tools/glslang-16.6.0'
extract(args.download_cache / 'glslang-16.6.0-windows-x86_64-release.zip', glslang)
shutil.copy2(glslang / 'bin/glslang.exe', glslang / 'bin/glslangValidator.exe')
if sha(glslang / 'bin/glslangValidator.exe') != '9643709c02c83e26773f96b6a87f197ab2e84f78be0879ad8fb3619418b5d9ad':
raise RuntimeError('Extracted glslang identity mismatch')
files = subprocess.check_output(['git', 'ls-files', '-z', 'engine', 'vulkan', 'kernels/tools'], cwd=repo).decode().split('\0')
suffixes = {'.py', '.comp', '.glsl', '.h', '.inc', '.sh'}
sources = [repo / name for name in files if name and Path(name).suffix in suffixes]
sources += [repo / 'engine/rr/native_kernels.txt', repo / 'tools/windows/build_rr_model.py', repo / 'tools/windows/deploy_rr_model.py', repo / 'LICENSE']
for source in sources:
target = recipe / source.relative_to(repo)
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, target)
# Keep isolated imports: no user site-packages or PYTHONPATH dependence.
paths = ['python312.zip', '.', '../recipe', '../recipe/engine', '../recipe/engine/rr',
'../recipe/kernels/tools', '../recipe/vulkan', '../recipe/tools/windows', '../recipe/.tools/python/vendor']
(python / 'python312._pth').write_text('\n'.join(paths) + '\n')
licenses = args.output / 'licenses'
licenses.mkdir()
shutil.copy2(args.download_cache / 'glslang-16.6.0-LICENSE.txt', licenses / 'glslang-LICENSE.txt')
shutil.copy2(python / 'LICENSE.txt', licenses / 'Python-LICENSE.txt')
for source in (recipe / '.tools/python/vendor/numpy-2.4.6.dist-info').glob('licenses/*'):
if source.is_file():
shutil.copy2(source, licenses / ('numpy-' + source.name))
manifest = {'kind': 'local-rr-model-tools', 'dependencies': {name: {'url': url, 'sha256': digest}
for name, (url, digest) in DEPENDENCIES.items()}, 'sourceWorkingTree': 'local modified; file hashes authoritative',
'files': {path.relative_to(args.output).as_posix(): sha(path) for path in sorted(args.output.rglob('*')) if path.is_file()},
'modelsBundled': False, 'nvidiaBinariesBundled': False}
(args.output / 'rr-tools.json').write_text(json.dumps(manifest, indent=2) + '\n')
if args.archive:
args.archive.parent.mkdir(parents=True, exist_ok=True)
# Package only the recorded pristine tool inputs. Never sweep a used
# bundle's model-build directory into a distributable archive.
with zipfile.ZipFile(args.archive, 'x', zipfile.ZIP_DEFLATED, compresslevel=6) as archive:
for name in (*manifest['files'], 'rr-tools.json'):
archive.write(args.output / name, name)
with zipfile.ZipFile(args.archive) as archive:
for name, expected in manifest['files'].items():
if hashlib.sha256(archive.read(name)).hexdigest() != expected:
raise RuntimeError('Portable-tools ZIP hash mismatch: ' + name)
print('RR_TOOLS_ARCHIVE sha256=' + sha(args.archive) + ' bytes=' + str(args.archive.stat().st_size))
print('RR_TOOLS_READY ' + str(args.output))
if __name__ == '__main__':
main()
+93
View File
@@ -0,0 +1,93 @@
"""Refresh the verified gfx1101 development ZIP. Never creates a public release."""
import argparse
import hashlib
import json
from pathlib import Path, PurePosixPath
import shutil
import subprocess
import zipfile
def sha(path):
with path.open('rb') as stream:
return hashlib.file_digest(stream, 'sha256').hexdigest()
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())
args = parser.parse_args()
repo = Path(__file__).resolve().parents[2]
build = (repo / 'build').resolve()
for path in (args.output, args.archive):
if not path.is_relative_to(build) or path.exists():
parser.error('use fresh output/archive paths under ignored build/')
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')
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'}:
raise RuntimeError('Unexpected base package identity')
records = {entry['path']: entry for entry in manifest['files']}
names = [entry.filename for entry in base.infolist() if not entry.is_dir()]
if len(names) != len(set(names)) or set(names) != set(records) | {'d4r/package.json'}:
raise RuntimeError('Base 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 ZIP path: ' + name)
data = base.read(name)
if name in records and hashlib.sha256(data).hexdigest().upper() != records[name]['sha256'].upper():
raise RuntimeError('Base ZIP hash mismatch: ' + name)
destination = args.output.joinpath(*parts.parts)
destination.parent.mkdir(parents=True, exist_ok=True)
destination.write_bytes(data)
overlays = {
'd4r/_nvngx_gfx1101.dll': args.shim,
'd4r/d4r.ini': repo / 'packaging/windows/d4r.ini',
'WINDOWS_README.txt': repo / 'packaging/windows/WINDOWS_README.txt',
'd4r/d4r-check.ps1': repo / 'scripts/windows/d4r-check.ps1',
'install.bat': repo / 'packaging/windows/install.bat',
'install-dlss.ps1': repo / 'scripts/windows/install-dlss.ps1',
'd4r/dll-pins.txt': repo / 'tools/windows/dll-pins.txt',
}
for name, source in overlays.items():
if name not in records:
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'}
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))
manifest.update(localDevelopment=True, packageWorkingTreeDirty=True, binarySourceCommit=None,
packageCommit=subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=repo, text=True).strip(),
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'})
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')
args.archive.parent.mkdir(parents=True, exist_ok=True)
with zipfile.ZipFile(args.archive, 'x', zipfile.ZIP_DEFLATED, compresslevel=6) as archive:
for path in sorted(args.output.rglob('*')):
if path.is_file():
archive.write(path, path.relative_to(args.output).as_posix())
with zipfile.ZipFile(args.archive) as archive:
for name, entry in records.items():
if hashlib.sha256(archive.read(name)).hexdigest().upper() != entry['sha256'].upper():
raise RuntimeError('Refreshed ZIP hash mismatch: ' + name)
report = {'zip': str(args.archive), 'sha256': sha(args.archive), 'bytes': args.archive.stat().st_size,
'allHashesVerified': True, 'privateOverrideBundled': False, 'rrModelBundled': False,
'sourceCommitCreated': False, 'published': False, 'baseZipSha256': base_hash}
args.archive.with_suffix('.validation.json').write_text(json.dumps(report, indent=2) + '\n')
print(json.dumps(report, indent=2))
if __name__ == '__main__':
main()
+326
View File
@@ -0,0 +1,326 @@
#pragma once
#include <d3d12.h>
#include <wrl/client.h>
#include "../../engine/runtime.h"
#include "binary16.h"
#include <array>
#include <filesystem>
#include <fstream>
#include <memory>
#include <limits>
#include <algorithm>
#include <set>
#include <cstdio>
#include <cstring>
#include <string>
// Canonical-input replay through the native D3D12/Vulkan shared packet. The
// packet is GPU memory at the API boundary; CPU access is fixture upload/QA only.
namespace rr_bridge {
inline void check(VkResult r, const char* operation) {
if (r != VK_SUCCESS) throw std::runtime_error(std::string(operation) + " VkResult=" + std::to_string(r));
}
inline std::vector<char> read(const std::filesystem::path& path) {
std::ifstream f(path, std::ios::binary | std::ios::ate);
if (!f) throw std::runtime_error("Cannot read RR fixture: " + path.string());
std::vector<char> result(size_t(f.tellg())); f.seekg(0); f.read(result.data(), std::streamsize(result.size()));
if (!f) throw std::runtime_error("Incomplete RR fixture read");
return result;
}
struct Frame { uint32_t id; d4r::RFrameSettings settings; std::array<bool, 4> optional{}; };
struct Plane {
const char* name; VkFormat format; uint32_t bytes;
VkDeviceSize offset = 0, pitch = 0;
bool enabled = true;
};
struct Layout {
uint32_t rw = 0, rh = 0, ow = 0, oh = 0;
std::array<Plane, 11> planes{{
{"color", VK_FORMAT_R16G16B16A16_SFLOAT, 8}, {"motion", VK_FORMAT_R16G16_SFLOAT, 4},
{"depth", VK_FORMAT_R32_SFLOAT, 4}, {"normals", VK_FORMAT_R16G16B16A16_SFLOAT, 8},
{"roughness", VK_FORMAT_R16G16B16A16_SFLOAT, 8}, {"diffuse", VK_FORMAT_R8G8B8A8_UNORM, 4},
{"specular", VK_FORMAT_R8G8B8A8_UNORM, 4},
{"hitdistance", VK_FORMAT_R32_SFLOAT, 4, 0, 0, false},
{"disocclusion", VK_FORMAT_R16_SFLOAT, 2, 0, 0, false},
{"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;
std::filesystem::path inputs, output, reference;
std::vector<Frame> frames;
// Runtime layout construction performs no fixture I/O. Both callers use
// the same aligned packet contract for native texture transfers.
Layout(uint32_t renderWidth, uint32_t renderHeight, uint32_t outputWidth, uint32_t outputHeight)
: rw(renderWidth), rh(renderHeight), ow(outputWidth), oh(outputHeight) {
if (!rw || !rh || !ow || !oh || rw > 16384 || rh > 16384 || ow > 16384 || oh > 16384)
throw std::runtime_error("Invalid RR packet geometry");
initializePacket();
}
void initializePacket() {
bytes = 0;
for (auto& plane : planes) {
bytes = (bytes + 511) & ~VkDeviceSize(511); plane.offset = bytes;
plane.pitch = (VkDeviceSize(rw) * plane.bytes + 255) & ~VkDeviceSize(255);
bytes += plane.pitch * rh;
}
outputOffset = (bytes + 511) & ~VkDeviceSize(511);
outputPitch = (VkDeviceSize(ow) * 8 + 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)
throw std::runtime_error("Invalid bounded RR replay geometry");
while (f >> token) {
Frame frame{}; int reset, mode;
auto& s = frame.settings;
if (token != "frame" || !(f >> frame.id >> s.jitter[0] >> s.jitter[1] >> s.motionScale[0] >>
s.motionScale[1] >> s.preExposure >> reset >> mode)) throw std::runtime_error("Invalid RR frame header");
for (auto& value : s.worldToView) if (!(f >> value)) throw std::runtime_error("Invalid RR view matrix");
for (auto& value : s.viewToClip) if (!(f >> value)) throw std::runtime_error("Invalid RR projection matrix");
s.reset = reset != 0; s.depthInverted = (mode & 1) != 0; s.hardwareDepth = (mode & 2) != 0;
s.roughnessPacked = (mode & 4) != 0;
if (!std::isfinite(s.preExposure) || s.preExposure <= 0 ||
!std::isfinite(s.jitter[0]) || !std::isfinite(s.jitter[1]) ||
!std::isfinite(s.motionScale[0]) || !std::isfinite(s.motionScale[1]))
throw std::runtime_error("Nonfinite or invalid RR frame settings");
for (float value : s.worldToView) if (!std::isfinite(value)) throw std::runtime_error("Nonfinite RR view matrix");
for (float value : s.viewToClip) if (!std::isfinite(value)) throw std::runtime_error("Nonfinite RR projection matrix");
frames.push_back(frame);
if (frames.size() > 32) throw std::runtime_error("RR replay is limited to 32 frames");
}
if (frames.empty()) throw std::runtime_error("RR fixture has no frames");
std::set<uint32_t> ids;
for (auto& frame : frames) {
if (!ids.insert(frame.id).second) throw std::runtime_error("Duplicate RR frame ID");
for (size_t i = 7; i < planes.size(); ++i) {
frame.optional[i - 7] = std::filesystem::exists(inputs / (std::to_string(frame.id) + "-" + planes[i].name + ".bin"));
planes[i].enabled = planes[i].enabled || frame.optional[i - 7];
}
}
initializePacket();
std::filesystem::create_directories(output);
for (const auto& frame : frames)
if (std::filesystem::exists(output / (std::to_string(frame.id) + "-out.bin")))
throw std::runtime_error("Use a fresh RR output directory");
}
void upload(void* destination, unsigned frame) const {
std::memset(destination, 0, size_t(bytes));
for (const auto& p : planes) {
if (!p.enabled) continue;
const auto index = size_t(&p - planes.data());
if (index >= 7 && !frames.at(frame).optional[index - 7]) continue;
auto data = read(inputs / (std::to_string(frames.at(frame).id) + "-" + p.name + ".bin"));
if (data.size() != size_t(rw) * rh * p.bytes) throw std::runtime_error("RR input size mismatch");
for (uint32_t y = 0; y < rh; ++y)
std::memcpy(static_cast<char*>(destination) + p.offset + y * p.pitch,
data.data() + size_t(y) * rw * p.bytes, size_t(rw) * p.bytes);
}
}
void validate(const void* source, unsigned frame) const {
std::vector<char> packed(size_t(ow) * oh * 8);
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);
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);
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");
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",
frames[frame].id, !reference.empty(), ow, oh);
}
};
struct D3D12Frames {
const Layout& layout;
std::array<Microsoft::WRL::ComPtr<ID3D12Resource>, 11> inputs;
Microsoft::WRL::ComPtr<ID3D12Resource> output;
static void checkD3D(HRESULT r) { if (FAILED(r)) throw std::runtime_error("RR D3D12 texture operation failed: " + std::to_string(r)); }
static DXGI_FORMAT format(VkFormat f) {
switch (f) {
case VK_FORMAT_R16G16B16A16_SFLOAT: return DXGI_FORMAT_R16G16B16A16_FLOAT;
case VK_FORMAT_R16G16_SFLOAT: return DXGI_FORMAT_R16G16_FLOAT;
case VK_FORMAT_R32_SFLOAT: return DXGI_FORMAT_R32_FLOAT;
case VK_FORMAT_R8G8B8A8_UNORM: return DXGI_FORMAT_R8G8B8A8_UNORM;
case VK_FORMAT_R32G32B32A32_SFLOAT: return DXGI_FORMAT_R32G32B32A32_FLOAT;
case VK_FORMAT_R32G32_SFLOAT: return DXGI_FORMAT_R32G32_FLOAT;
case VK_FORMAT_R16_SFLOAT: return DXGI_FORMAT_R16_FLOAT;
case VK_FORMAT_R8_UNORM: return DXGI_FORMAT_R8_UNORM;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32: return DXGI_FORMAT_R10G10B10A2_UNORM;
default: throw std::runtime_error("Unsupported canonical RR D3D12 format");
}
}
static void barrier(ID3D12GraphicsCommandList* cb, ID3D12Resource* image, D3D12_RESOURCE_STATES from, D3D12_RESOURCE_STATES to) {
D3D12_RESOURCE_BARRIER b{}; b.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
b.Transition = {image, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, from, to}; cb->ResourceBarrier(1, &b);
}
static D3D12_TEXTURE_COPY_LOCATION placed(ID3D12Resource* resource, VkDeviceSize offset, VkDeviceSize pitch,
DXGI_FORMAT f, uint32_t width, uint32_t height) {
D3D12_TEXTURE_COPY_LOCATION result{}; result.pResource = resource;
result.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; result.PlacedFootprint.Offset = offset;
result.PlacedFootprint.Footprint = {f, width, height, 1, UINT(pitch)}; return result;
}
static D3D12_TEXTURE_COPY_LOCATION texture(ID3D12Resource* resource) {
D3D12_TEXTURE_COPY_LOCATION result{}; result.pResource = resource;
result.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; return result;
}
D3D12Frames(const Layout& l, ID3D12Device* device, DXGI_FORMAT depthFormat = DXGI_FORMAT_UNKNOWN) : layout(l) {
auto create = [&](DXGI_FORMAT f, uint32_t width, uint32_t height, bool isOutput, ID3D12Resource** resource) {
D3D12_HEAP_PROPERTIES heap{}; heap.Type = D3D12_HEAP_TYPE_DEFAULT;
D3D12_RESOURCE_DESC desc{}; desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; desc.Format = f;
desc.Width = width; desc.Height = height; desc.DepthOrArraySize = desc.MipLevels = 1; desc.SampleDesc.Count = 1;
if (isOutput) desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
if (f == DXGI_FORMAT_D32_FLOAT || f == DXGI_FORMAT_R32G8X24_TYPELESS || f == DXGI_FORMAT_D32_FLOAT_S8X24_UINT)
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
checkD3D(device->CreateCommittedResource(&heap, D3D12_HEAP_FLAG_NONE, &desc,
isOutput ? D3D12_RESOURCE_STATE_UNORDERED_ACCESS : D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
nullptr, IID_PPV_ARGS(resource)));
};
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());
}
void recordInputs(ID3D12GraphicsCommandList* cb, ID3D12Resource* upload, ID3D12Resource* shared, uint32_t optionalMask = 15) {
for (size_t i = 0; i < inputs.size(); ++i) {
if (!inputs[i]) continue;
if (i >= 7 && !(optionalMask & (1u << (i - 7)))) continue;
auto* input = inputs[i].Get(); const auto& plane = layout.planes[i];
auto copyFormat = format(plane.format);
if (i == 2) {
Microsoft::WRL::ComPtr<ID3D12Device> device; checkD3D(input->GetDevice(IID_PPV_ARGS(&device)));
D3D12_RESOURCE_DESC desc{};
#if defined(__MINGW32__)
input->GetDesc(&desc);
#else
desc = input->GetDesc();
#endif
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint{};
device->GetCopyableFootprints(&desc, 0, 1, 0, &footprint, nullptr, nullptr, nullptr);
copyFormat = footprint.Footprint.Format;
}
auto src = placed(upload, plane.offset, plane.pitch, copyFormat, layout.rw, layout.rh);
auto image = texture(input);
barrier(cb, input, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_COPY_DEST);
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);
barrier(cb, input, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
}
}
void recordOutput(ID3D12GraphicsCommandList* cb, ID3D12Resource* shared, ID3D12Resource* readback) {
auto src = placed(shared, layout.outputOffset, layout.outputPitch, DXGI_FORMAT_R16G16B16A16_FLOAT, layout.ow, layout.oh);
auto image = texture(output.Get());
barrier(cb, output.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
cb->CopyTextureRegion(&image, 0, 0, 0, &src, nullptr);
barrier(cb, output.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);
auto dst = placed(readback, layout.outputOffset, layout.outputPitch, DXGI_FORMAT_R16G16B16A16_FLOAT, layout.ow, layout.oh);
cb->CopyTextureRegion(&dst, 0, 0, 0, &image, nullptr);
barrier(cb, output.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
}
};
struct Image {
VkFormat format = VK_FORMAT_UNDEFINED; bool initialized = false;
VkDevice device; VkImage image = VK_NULL_HANDLE; VkDeviceMemory memory = VK_NULL_HANDLE; VkImageView view = VK_NULL_HANDLE;
~Image() { if (view) vkDestroyImageView(device, view, nullptr); if (image) vkDestroyImage(device, image, nullptr); if (memory) vkFreeMemory(device, memory, nullptr); }
};
struct Replay {
const Layout& layout;
VkPhysicalDevice physical; VkDevice device;
std::array<std::unique_ptr<Image>, 11> images;
std::array<std::vector<std::unique_ptr<Image>>, 11> alternatives;
std::unique_ptr<d4r::RModel> model;
std::unique_ptr<d4r::REngine> engine;
bool first = true;
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");
const std::string manifest(data.begin(), data.end());
if (manifest.rfind("D4RR0001", 0) != 0 || manifest.find("arithmetic gfx11") == std::string::npos)
throw std::runtime_error("This canonical RR bridge gate requires a D/E gfx11 model");
for (size_t i = 0; i < images.size(); ++i) if (l.planes[i].enabled) configure(i, l.planes[i].format);
model = std::make_unique<d4r::RModel>(physical, device, directory);
engine = std::make_unique<d4r::REngine>(*model, l.rw, l.rh, l.ow, l.oh);
}
// 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) {
if (images.at(i) && images[i]->format == format) return;
for (auto& previous : alternatives[i]) if (previous->format == format) {
images[i].swap(previous); return;
}
VkFormatProperties properties{}; vkGetPhysicalDeviceFormatProperties(physical, format, &properties);
const auto required = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT;
if ((properties.optimalTilingFeatures & required) != required)
throw std::runtime_error("RR input format lacks Vulkan sampled/transfer support");
VkPhysicalDeviceMemoryProperties memory; vkGetPhysicalDeviceMemoryProperties(physical, &memory);
auto image = std::make_unique<Image>(); image->device = device; image->format = format;
VkImageCreateInfo ci{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; ci.imageType = VK_IMAGE_TYPE_2D;
ci.format = format; ci.extent = {layout.rw, layout.rh, 1}; ci.mipLevels = ci.arrayLayers = 1;
ci.samples = VK_SAMPLE_COUNT_1_BIT; ci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
check(vkCreateImage(device, &ci, nullptr, &image->image), "RR input image");
VkMemoryRequirements req; vkGetImageMemoryRequirements(device, image->image, &req);
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 RR image 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 image memory");
check(vkBindImageMemory(device, image->image, image->memory, 0), "Bind RR image");
VkImageViewCreateInfo vi{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; vi.image = image->image;
vi.viewType = VK_IMAGE_VIEW_TYPE_2D; vi.format = ci.format; vi.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
check(vkCreateImageView(device, &vi, nullptr, &image->view), "RR input view");
if (images[i]) alternatives[i].push_back(std::move(images[i]));
images[i] = std::move(image);
}
void record(VkCommandBuffer cb, VkBuffer shared, unsigned frame) {
uint32_t mask = 0;
for (unsigned i = 0; i < 4; ++i) if (layout.frames.at(frame).optional[i]) mask |= 1u << i;
record(cb, shared, layout.frames.at(frame).settings, mask);
}
// Own settings at the NGX submission boundary; never retain game pointers.
void record(VkCommandBuffer cb, VkBuffer shared, const d4r::RFrameSettings& settings, uint32_t optionalMask = 15) {
for (size_t i = 0; i < images.size(); ++i) {
if (!layout.planes[i].enabled) continue;
if (i >= 7 && !(optionalMask & (1u << (i - 7)))) continue;
VkImageMemoryBarrier b{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; b.image = images[i]->image;
b.oldLayout = !images[i]->initialized ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_GENERAL; b.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
b.srcQueueFamilyIndex = b.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
b.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
b.srcAccessMask = !images[i]->initialized ? 0 : VK_ACCESS_SHADER_READ_BIT; b.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &b);
const auto& plane = layout.planes[i];
VkBufferImageCopy copy{}; copy.bufferOffset = plane.offset; copy.bufferRowLength = uint32_t(plane.pitch / plane.bytes);
copy.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; copy.imageExtent = {layout.rw, layout.rh, 1};
vkCmdCopyBufferToImage(cb, shared, images[i]->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy);
images[i]->initialized = true;
b.oldLayout = b.newLayout; b.newLayout = VK_IMAGE_LAYOUT_GENERAL;
b.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; b.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &b);
}
d4r::RFrameImages inputs{}; inputs.color = images[0]->view; inputs.motion = images[1]->view; inputs.depth = images[2]->view;
inputs.normals = images[3]->view; inputs.roughness = images[4]->view; inputs.diffuseAlbedo = images[5]->view; inputs.specularAlbedo = images[6]->view;
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; }
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);
copy.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; copy.imageExtent = {layout.ow, layout.oh, 1};
vkCmdCopyImageToBuffer(cb, engine->outputImage(), VK_IMAGE_LAYOUT_GENERAL, shared, 1, &copy);
}
};
}
+79
View File
@@ -0,0 +1,79 @@
// Native D3D12/Vulkan prerequisite query for the RR engine adapter. No dispatch.
#include <windows.h>
#include <d3d12.h>
#include <dxgi1_4.h>
#include <volk.h>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
int main() {
IDXGIFactory4* factory = nullptr;
if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&factory)))) return 1;
IDXGIAdapter1* selected = nullptr;
DXGI_ADAPTER_DESC1 adapter{};
for (UINT i = 0;; ++i) {
IDXGIAdapter1* next = nullptr;
if (factory->EnumAdapters1(i, &next) == DXGI_ERROR_NOT_FOUND) break;
if (!next) break;
DXGI_ADAPTER_DESC1 desc{};
next->GetDesc1(&desc);
if (desc.VendorId == 0x1002 && !(desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)) {
selected = next; adapter = desc; break;
}
next->Release();
}
factory->Release();
if (!selected) { std::puts("FAIL no native AMD D3D12 adapter"); return 1; }
ID3D12Device* device = nullptr;
const HRESULT created = D3D12CreateDevice(selected, D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&device));
selected->Release();
if (FAILED(created)) { std::puts("FAIL D3D12 device creation"); return 1; }
device->Release();
if (volkInitialize() != VK_SUCCESS) return 1;
VkApplicationInfo app{VK_STRUCTURE_TYPE_APPLICATION_INFO}; app.apiVersion = VK_API_VERSION_1_3;
VkInstanceCreateInfo info{VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO}; info.pApplicationInfo = &app;
VkInstance instance = VK_NULL_HANDLE;
if (vkCreateInstance(&info, nullptr, &instance) != VK_SUCCESS) return 1;
volkLoadInstance(instance);
uint32_t count = 0;
vkEnumeratePhysicalDevices(instance, &count, nullptr);
std::vector<VkPhysicalDevice> devices(count);
vkEnumeratePhysicalDevices(instance, &count, devices.data());
bool ready = false;
for (auto physical : devices) {
VkPhysicalDeviceIDProperties id{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES};
VkPhysicalDeviceProperties2 properties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, &id};
vkGetPhysicalDeviceProperties2(physical, &properties);
if (!id.deviceLUIDValid || std::memcmp(id.deviceLUID, &adapter.AdapterLuid, VK_LUID_SIZE)) continue;
uint32_t extensionCount = 0;
vkEnumerateDeviceExtensionProperties(physical, nullptr, &extensionCount, nullptr);
std::vector<VkExtensionProperties> extensions(extensionCount);
vkEnumerateDeviceExtensionProperties(physical, nullptr, &extensionCount, extensions.data());
auto has = [&](const char* name) {
return std::any_of(extensions.begin(), extensions.end(), [&](const auto& e) { return !std::strcmp(name, e.extensionName); });
};
VkPhysicalDeviceExternalBufferInfo buffer{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO};
buffer.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
buffer.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT;
VkExternalBufferProperties memory{VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES};
vkGetPhysicalDeviceExternalBufferProperties(physical, &buffer, &memory);
VkPhysicalDeviceExternalSemaphoreInfo semaphore{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO};
semaphore.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT;
VkExternalSemaphoreProperties fence{VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES};
vkGetPhysicalDeviceExternalSemaphoreProperties(physical, &semaphore, &fence);
const bool memoryImport = (memory.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT) != 0;
const bool fenceImport = (fence.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT) != 0;
const bool memoryExtension = has("VK_KHR_external_memory_win32");
const bool fenceExtension = has("VK_KHR_external_semaphore_win32");
std::printf("RR_INTEROP device=%s luid_match=1 memory_win32=%u d3d12_buffer_import=%u dedicated_only=%u semaphore_win32=%u d3d12_fence_import=%u\n",
properties.properties.deviceName, memoryExtension, memoryImport,
unsigned((memory.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT) != 0),
fenceExtension, fenceImport);
ready = memoryExtension && memoryImport && fenceExtension && fenceImport;
}
vkDestroyInstance(instance, nullptr);
std::puts(ready ? "PASS RR_INTEROP_CAPABILITIES import_or_dispatch_executed=0" : "FAIL RR_INTEROP_CAPABILITIES");
return ready ? 0 : 1;
}
+307
View File
@@ -0,0 +1,307 @@
// D3D12 -> Vulkan -> D3D12 shared-buffer/fence gate for native RR integration.
// Synthetic transfers and optional local RR model replay. No NGX, CUDA, or game.
#define WIDL_EXPLICIT_AGGREGATE_RETURNS
#include <windows.h>
#include <d3d12.h>
#include <dxgi1_4.h>
#include <wrl/client.h>
#include <volk.h>
#include <vulkan/vulkan_win32.h>
#include <cstdio>
#include <cstring>
#include <stdexcept>
#include <string>
#include <vector>
#include "rr_bridge_replay.h"
#include "rr_native_packet.h"
using Microsoft::WRL::ComPtr;
namespace {
void dx(HRESULT value, const char* operation) {
if (FAILED(value)) throw std::runtime_error(std::string(operation) + " HRESULT=" + std::to_string(value));
}
void vk(VkResult value, const char* operation) {
if (value != VK_SUCCESS) throw std::runtime_error(std::string(operation) + " VkResult=" + std::to_string(value));
}
struct Handle { HANDLE value = nullptr; ~Handle() { if (value) CloseHandle(value); } };
ComPtr<ID3D12Resource> buffer(ID3D12Device* device, UINT64 bytes, D3D12_HEAP_TYPE type,
D3D12_HEAP_FLAGS flags, D3D12_RESOURCE_STATES state) {
D3D12_HEAP_PROPERTIES heap{}; heap.Type = type;
D3D12_RESOURCE_DESC desc{}; desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Width = bytes; desc.Height = desc.DepthOrArraySize = desc.MipLevels = 1;
desc.SampleDesc.Count = 1; desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
if (type == D3D12_HEAP_TYPE_DEFAULT) desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
ComPtr<ID3D12Resource> result;
dx(device->CreateCommittedResource(&heap, flags, &desc, state, nullptr, IID_PPV_ARGS(&result)), "Create buffer");
return result;
}
void transition(ID3D12GraphicsCommandList* list, ID3D12Resource* resource,
D3D12_RESOURCE_STATES from, D3D12_RESOURCE_STATES to) {
D3D12_RESOURCE_BARRIER b{}; b.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
b.Transition = {resource, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, from, to};
list->ResourceBarrier(1, &b);
}
struct Vulkan {
std::unique_ptr<rr_bridge::NativeDevice> native;
std::unique_ptr<rr_bridge::NativePacket> packet;
VkDevice device = VK_NULL_HANDLE;
VkBuffer shared = VK_NULL_HANDLE, readback = VK_NULL_HANDLE;
VkDeviceMemory host = VK_NULL_HANDLE;
VkImage image = VK_NULL_HANDLE;
VkDeviceMemory imageMemory = VK_NULL_HANDLE;
VkSemaphore semaphore = VK_NULL_HANDLE;
VkCommandPool pool = VK_NULL_HANDLE;
VkFence fence = VK_NULL_HANDLE;
bool submitted = false;
std::unique_ptr<rr_bridge::Replay> replay;
~Vulkan() {
// The outer process timeout also bounds a stalled driver call. Never
// destroy resources that a failed/timed-out submission might still use.
if (submitted) { (void)replay.release(); (void)packet.release(); (void)native.release(); return; }
replay.reset();
if (device) {
if (fence) vkDestroyFence(device, fence, nullptr);
if (pool) vkDestroyCommandPool(device, pool, nullptr);
if (readback) vkDestroyBuffer(device, readback, nullptr);
if (image) vkDestroyImage(device, image, nullptr);
if (host) vkFreeMemory(device, host, nullptr);
if (imageMemory) vkFreeMemory(device, imageMemory, nullptr);
}
}
};
}
int main(int argc, char** argv) try {
std::setvbuf(stdout, nullptr, _IONBF, 0);
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
UINT64 bytes = 1u << 20;
unsigned iterations = 8;
uint32_t imageWidth = 0, imageHeight = 0;
std::string rrModel, rrInputs, rrOutput, rrReference;
for (int i = 1; i < argc; i += 2) {
if (i + 1 == argc) throw std::runtime_error("Missing argument value");
const std::string key = argv[i];
size_t end = 0;
const std::string text = argv[i + 1];
if (key == "--rr-model") { rrModel = text; continue; }
if (key == "--rr-inputs") { rrInputs = text; continue; }
if (key == "--rr-output") { rrOutput = text; continue; }
if (key == "--rr-reference") { rrReference = text; continue; }
if (text.empty() || text[0] == '-') throw std::runtime_error("Expected positive argument");
const auto value = std::stoull(text, &end);
if (end != text.size()) throw std::runtime_error("Invalid numeric argument");
if (key == "--bytes") bytes = value;
else if (key == "--iterations" && value <= 32) iterations = unsigned(value);
else if (key == "--image-width" && value <= 8192) imageWidth = uint32_t(value);
else if (key == "--image-height" && value <= 8192) imageHeight = uint32_t(value);
else throw std::runtime_error("Unknown or out-of-range argument");
}
std::unique_ptr<rr_bridge::Layout> rrLayout;
if (!rrModel.empty()) {
if (rrInputs.empty() || rrOutput.empty() || imageWidth || imageHeight) throw std::runtime_error("RR replay needs model/inputs/output and no image-clear mode");
rrLayout = std::make_unique<rr_bridge::Layout>(rrInputs, rrOutput, rrReference);
bytes = rrLayout->bytes; iterations = unsigned(rrLayout->frames.size());
} else if (!rrInputs.empty() || !rrOutput.empty() || !rrReference.empty()) throw std::runtime_error("Missing RR model");
if (!bytes || bytes % 4 || bytes > (64u << 20) || !iterations)
throw std::runtime_error("Need a 4-byte-aligned buffer (4 bytes..64MiB) and 1..32 iterations");
const UINT64 rowPitch = (UINT64(imageWidth) * 8 + 255) & ~UINT64(255);
const bool imageMode = imageWidth && imageHeight;
if (bool(imageWidth) != bool(imageHeight) || (imageMode && bytes != rowPitch * imageHeight))
throw std::runtime_error("Image dimensions must match the 256-byte-padded RGBA16F buffer size");
ComPtr<IDXGIFactory4> factory; dx(CreateDXGIFactory1(IID_PPV_ARGS(&factory)), "DXGI factory");
ComPtr<IDXGIAdapter1> adapter;
DXGI_ADAPTER_DESC1 description{};
bool selected = false;
for (UINT i = 0; factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND; ++i) {
dx(adapter->GetDesc1(&description), "DXGI adapter description");
if (description.VendorId == 0x1002 && !(description.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)) { selected = true; break; }
adapter.Reset();
}
if (!selected) throw std::runtime_error("No AMD D3D12 adapter");
ComPtr<ID3D12Device> d3d; dx(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&d3d)), "D3D12 device");
ComPtr<ID3D12CommandQueue> queue;
D3D12_COMMAND_QUEUE_DESC q{}; q.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
dx(d3d->CreateCommandQueue(&q, IID_PPV_ARGS(&queue)), "D3D12 queue");
auto shared = buffer(d3d.Get(), bytes, D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_FLAG_SHARED, D3D12_RESOURCE_STATE_COMMON);
auto upload = buffer(d3d.Get(), bytes, D3D12_HEAP_TYPE_UPLOAD, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_GENERIC_READ);
auto readback = buffer(d3d.Get(), bytes, D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
std::unique_ptr<rr_bridge::D3D12Frames> d3dFrames;
if (rrLayout) d3dFrames = std::make_unique<rr_bridge::D3D12Frames>(*rrLayout, d3d.Get());
ComPtr<ID3D12Fence> d3dFence; dx(d3d->CreateFence(0, D3D12_FENCE_FLAG_SHARED, IID_PPV_ARGS(&d3dFence)), "Shared D3D12 fence");
Handle memoryHandle, fenceHandle, event;
dx(d3d->CreateSharedHandle(shared.Get(), nullptr, GENERIC_ALL, nullptr, &memoryHandle.value), "Shared buffer handle");
dx(d3d->CreateSharedHandle(d3dFence.Get(), nullptr, GENERIC_ALL, nullptr, &fenceHandle.value), "Shared fence handle");
event.value = CreateEventW(nullptr, FALSE, FALSE, nullptr);
if (!event.value) throw std::runtime_error("Fence event creation failed");
Vulkan v;
v.native = std::make_unique<rr_bridge::NativeDevice>();
v.native->initialize(description.AdapterLuid, bool(rrLayout));
v.device = v.native->device;
const auto physical = v.native->physical;
const auto family = v.native->family;
const auto vkQueue = v.native->queue;
if (rrLayout) v.replay = std::make_unique<rr_bridge::Replay>(*rrLayout, physical, v.device, rrModel);
v.packet = std::make_unique<rr_bridge::NativePacket>(*v.native);
v.packet->initialize(bytes, memoryHandle.value, fenceHandle.value);
v.shared = v.packet->buffer; v.semaphore = v.packet->semaphore;
VkPhysicalDeviceMemoryProperties memory{}; vkGetPhysicalDeviceMemoryProperties(physical, &memory);
auto memoryType = [&](uint32_t bits, VkMemoryPropertyFlags flags) {
for (uint32_t i = 0; i < memory.memoryTypeCount; ++i)
if ((bits & (1u << i)) && (memory.memoryTypes[i].propertyFlags & flags) == flags) return i;
throw std::runtime_error("No compatible Vulkan memory type");
};
VkBufferCreateInfo bi{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; bi.size = bytes;
bi.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
VkMemoryRequirements req{};
VkMemoryAllocateInfo ai{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
vk(vkCreateBuffer(v.device, &bi, nullptr, &v.readback), "Vulkan readback buffer");
vkGetBufferMemoryRequirements(v.device, v.readback, &req);
ai.pNext = nullptr; ai.allocationSize = req.size;
ai.memoryTypeIndex = memoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
vk(vkAllocateMemory(v.device, &ai, nullptr, &v.host), "Vulkan readback memory");
vk(vkBindBufferMemory(v.device, v.readback, v.host, 0), "Bind readback");
if (imageMode) {
VkImageCreateInfo image{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
image.imageType = VK_IMAGE_TYPE_2D; image.format = VK_FORMAT_R16G16B16A16_SFLOAT;
image.extent = {imageWidth, imageHeight, 1}; image.mipLevels = image.arrayLayers = 1;
image.samples = VK_SAMPLE_COUNT_1_BIT;
image.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT;
vk(vkCreateImage(v.device, &image, nullptr, &v.image), "Canonical RR image");
vkGetImageMemoryRequirements(v.device, v.image, &req);
ai.allocationSize = req.size; ai.memoryTypeIndex = memoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
vk(vkAllocateMemory(v.device, &ai, nullptr, &v.imageMemory), "Canonical RR image memory");
vk(vkBindImageMemory(v.device, v.image, v.imageMemory, 0), "Bind canonical image");
}
VkCommandPoolCreateInfo pci{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO}; pci.queueFamilyIndex = family;
vk(vkCreateCommandPool(v.device, &pci, nullptr, &v.pool), "Vulkan command pool");
VkCommandBufferAllocateInfo cai{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO}; cai.commandPool = v.pool; cai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cai.commandBufferCount = 1;
VkCommandBuffer cb; vk(vkAllocateCommandBuffers(v.device, &cai, &cb), "Vulkan command buffer");
VkFenceCreateInfo fci{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO}; vk(vkCreateFence(v.device, &fci, nullptr, &v.fence), "Vulkan completion fence");
std::puts("RR_INTEROP_IMPORTED memory=dedicated_d3d12_resource semaphore=d3d12_timeline_fence luid_match=1");
ComPtr<ID3D12CommandAllocator> producerAllocator, consumerAllocator;
ComPtr<ID3D12GraphicsCommandList> producer, consumer;
dx(d3d->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&producerAllocator)), "Producer allocator");
dx(d3d->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&consumerAllocator)), "Consumer allocator");
dx(d3d->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, producerAllocator.Get(), nullptr, IID_PPV_ARGS(&producer)), "Producer list");
dx(d3d->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, consumerAllocator.Get(), nullptr, IID_PPV_ARGS(&consumer)), "Consumer list");
dx(producer->Close(), "Initial producer close"); dx(consumer->Close(), "Initial consumer close");
auto pattern = [](unsigned iteration, uint32_t word) { return (word * 2654435761u) ^ (0x13579bdfu + iteration * 97u); };
for (unsigned iteration = 0; iteration < iterations; ++iteration) {
uint32_t* input = nullptr; D3D12_RANGE noRead{};
dx(upload->Map(0, &noRead, reinterpret_cast<void**>(&input)), "Upload map");
if (rrLayout) rrLayout->upload(input, iteration);
else for (uint32_t word = 0; word < bytes / 4; ++word) input[word] = pattern(iteration, word);
upload->Unmap(0, nullptr);
dx(producerAllocator->Reset(), "Producer allocator reset"); dx(producer->Reset(producerAllocator.Get(), nullptr), "Producer reset");
transition(producer.Get(), shared.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST);
if (d3dFrames) d3dFrames->recordInputs(producer.Get(), upload.Get(), shared.Get());
else producer->CopyBufferRegion(shared.Get(), 0, upload.Get(), 0, bytes);
transition(producer.Get(), shared.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COMMON);
dx(producer->Close(), "Producer close"); ID3D12CommandList* p[] = {producer.Get()}; queue->ExecuteCommandLists(1, p);
const uint64_t ready = iteration * 3 + 1, done = ready + 1, complete = ready + 2;
dx(queue->Signal(d3dFence.Get(), ready), "D3D12 producer signal");
vk(vkResetCommandPool(v.device, v.pool, 0), "Vulkan pool reset");
VkCommandBufferBeginInfo begin{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO}; vk(vkBeginCommandBuffer(cb, &begin), "Vulkan begin");
VkBufferMemoryBarrier acquire{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER}; acquire.buffer = v.shared; acquire.size = VK_WHOLE_SIZE;
acquire.srcQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL; acquire.dstQueueFamilyIndex = family; acquire.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
if (rrLayout) acquire.dstAccessMask |= VK_ACCESS_TRANSFER_WRITE_BIT;
vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1, &acquire, 0, nullptr);
VkBufferImageCopy imageRegion{};
imageRegion.bufferRowLength = uint32_t(rowPitch / 8);
imageRegion.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
imageRegion.imageExtent = {imageWidth, imageHeight, 1};
auto imageBarrier = [&](VkImageLayout from, VkImageLayout to, VkAccessFlags source, VkAccessFlags destination) {
VkImageMemoryBarrier b{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
b.image = v.image; b.oldLayout = from; b.newLayout = to;
b.srcQueueFamilyIndex = b.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
b.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; b.srcAccessMask = source; b.dstAccessMask = destination;
vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &b);
};
if (rrLayout) {
v.replay->record(cb, v.shared, iteration);
} else if (imageMode) {
vkCmdFillBuffer(cb, v.readback, 0, bytes, 0xdeadbeefu);
VkBufferMemoryBarrier fill{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER}; fill.buffer = v.readback; fill.size = VK_WHOLE_SIZE;
fill.srcQueueFamilyIndex = fill.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
fill.srcAccessMask = fill.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1, &fill, 0, nullptr);
imageBarrier(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT);
vkCmdCopyBufferToImage(cb, v.shared, v.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageRegion);
imageBarrier(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
vkCmdCopyImageToBuffer(cb, v.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, v.readback, 1, &imageRegion);
imageBarrier(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT);
VkClearColorValue clear{}; clear.float32[0] = iteration % 2 ? .5f : .25f;
clear.float32[1] = .5f; clear.float32[2] = .75f; clear.float32[3] = 1.f;
VkImageSubresourceRange range{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
vkCmdClearColorImage(cb, v.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear, 1, &range);
imageBarrier(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
} else {
VkBufferCopy region{0, 0, bytes}; vkCmdCopyBuffer(cb, v.shared, v.readback, 1, &region);
}
VkBufferMemoryBarrier afterRead{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER}; afterRead.buffer = v.shared; afterRead.size = VK_WHOLE_SIZE;
afterRead.srcQueueFamilyIndex = afterRead.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
afterRead.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; afterRead.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
if (!rrLayout) vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1, &afterRead, 0, nullptr);
const uint32_t output = 0xa5a50000u | iteration;
if (!rrLayout) vkCmdFillBuffer(cb, v.shared, 0, bytes, output);
if (imageMode) {
afterRead.srcAccessMask = afterRead.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1, &afterRead, 0, nullptr);
vkCmdCopyImageToBuffer(cb, v.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, v.shared, 1, &imageRegion);
}
VkBufferMemoryBarrier release = acquire; release.srcQueueFamilyIndex = family; release.dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL;
release.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; release.dstAccessMask = 0;
vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 1, &release, 0, nullptr);
VkBufferMemoryBarrier hostRead{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER}; hostRead.buffer = v.readback; hostRead.size = VK_WHOLE_SIZE;
hostRead.srcQueueFamilyIndex = hostRead.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
hostRead.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; hostRead.dstAccessMask = VK_ACCESS_HOST_READ_BIT;
vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0, 0, nullptr, 1, &hostRead, 0, nullptr);
vk(vkEndCommandBuffer(cb), "Vulkan end");
VkTimelineSemaphoreSubmitInfo values{VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO};
values.waitSemaphoreValueCount = values.signalSemaphoreValueCount = 1; values.pWaitSemaphoreValues = &ready; values.pSignalSemaphoreValues = &done;
VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
VkSubmitInfo submit{VK_STRUCTURE_TYPE_SUBMIT_INFO, &values}; submit.waitSemaphoreCount = submit.signalSemaphoreCount = 1;
submit.pWaitSemaphores = submit.pSignalSemaphores = &v.semaphore; submit.pWaitDstStageMask = &waitStage; submit.commandBufferCount = 1; submit.pCommandBuffers = &cb;
vk(vkResetFences(v.device, 1, &v.fence), "Vulkan fence reset");
vk(vkQueueSubmit(vkQueue, 1, &submit, v.fence), "Vulkan shared-buffer submission"); v.submitted = true;
dx(queue->Wait(d3dFence.Get(), done), "D3D12 wait for Vulkan");
dx(consumerAllocator->Reset(), "Consumer allocator reset"); dx(consumer->Reset(consumerAllocator.Get(), nullptr), "Consumer reset");
transition(consumer.Get(), shared.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_SOURCE);
if (d3dFrames) d3dFrames->recordOutput(consumer.Get(), shared.Get(), readback.Get());
else consumer->CopyBufferRegion(readback.Get(), 0, shared.Get(), 0, bytes);
transition(consumer.Get(), shared.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COMMON);
dx(consumer->Close(), "Consumer close"); ID3D12CommandList* c[] = {consumer.Get()}; queue->ExecuteCommandLists(1, c);
dx(queue->Signal(d3dFence.Get(), complete), "D3D12 consumer signal");
dx(d3dFence->SetEventOnCompletion(complete, event.value), "Completion event");
if (WaitForSingleObject(event.value, 5000) != WAIT_OBJECT_0) throw std::runtime_error("D3D12/Vulkan handoff timeout");
vk(vkWaitForFences(v.device, 1, &v.fence, VK_TRUE, 5000000000ull), "Vulkan completion wait"); v.submitted = false;
if (rrLayout) {
void* data = nullptr; D3D12_RANGE range{0, SIZE_T(bytes)};
dx(readback->Map(0, &range, &data), "RR D3D12 output map");
try { rrLayout->validate(data, iteration); } catch (...) { D3D12_RANGE empty{}; readback->Unmap(0, &empty); throw; }
D3D12_RANGE empty{}; readback->Unmap(0, &empty);
continue;
}
uint32_t* first = nullptr; vk(vkMapMemory(v.device, v.host, 0, bytes, 0, reinterpret_cast<void**>(&first)), "Vulkan readback map");
auto padding = [&](uint32_t word) { return imageMode && word % (rowPitch / 4) >= imageWidth * 2; };
size_t mismatches = 0;
for (uint32_t word = 0; word < bytes / 4; ++word)
mismatches += first[word] != (padding(word) ? 0xdeadbeefu : pattern(iteration, word));
vkUnmapMemory(v.device, v.host);
uint32_t* second = nullptr; D3D12_RANGE readRange{0, SIZE_T(bytes)};
dx(readback->Map(0, &readRange, reinterpret_cast<void**>(&second)), "D3D12 readback map");
for (uint32_t word = 0; word < bytes / 4; ++word) {
const uint32_t imageWord = word % 2 ? 0x3c003a00u : (iteration % 2 ? 0x38003800u : 0x38003400u);
const uint32_t expected = imageMode && !padding(word) ? imageWord : output;
mismatches += second[word] != expected;
}
D3D12_RANGE noWrite{}; readback->Unmap(0, &noWrite);
if (mismatches) throw std::runtime_error("Shared-buffer contents differ: " + std::to_string(mismatches));
std::printf("RR_INTEROP_ITERATION index=%u bytes=%llu exact=1 producer_value=%llu vulkan_value=%llu consumer_value=%llu\n",
iteration, static_cast<unsigned long long>(bytes), static_cast<unsigned long long>(ready), static_cast<unsigned long long>(done), static_cast<unsigned long long>(complete));
}
if (rrLayout) std::printf("PASS RR_BRIDGED_INFERENCE frames=%u guides=7 d3d12_textures=1 gpu_fence_handoff=1 standalone_reference=%u\n", iterations, !rrReference.empty());
else std::printf("PASS RR_INTEROP_TRANSFER iterations=%u d3d12_to_vulkan_exact=1 vulkan_to_d3d12_exact=1 gpu_fence_handoff=1 canonical_image=%u inference_executed=0\n", iterations, imageMode);
return 0;
} catch (const std::exception& error) { std::fprintf(stderr, "FAIL RR_INTEROP_TRANSFER %s\n", error.what()); return 1; }
+291
View File
@@ -0,0 +1,291 @@
#include <volk.h>
#include "rr_native_backend.h"
#include "rr_native_packet.h"
#include "rr_bridge_replay.h"
#include "d3d12_command_hooks.h"
#include <mutex>
namespace d4r::win::rr {
namespace {
// The engine uses Volk's global dispatch. Serialize device creation, recording,
// submission and destruction; reload the owning dispatch before each operation.
std::mutex dispatchMutex;
using Packet = rr_bridge::NativePacket;
using Device = rr_bridge::NativeDevice;
size_t resource_index(size_t plane) { return plane < 7 ? plane : plane + 2; }
rr_bridge::Plane input_format(DXGI_FORMAT format, size_t plane) {
// Match Linux's direct sampled-image route. These formats preserve the
// stored texel values; no CPU conversion or reinterpretation is involved.
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 native 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 RR depth-plane footprint");
return footprint.Footprint.Format;
}
}
struct Backend::Impl {
Creation creation;
rr_bridge::Layout layout;
ComPtr<ID3D12Device> d3d;
ComPtr<ID3D12CommandQueue> queue;
ComPtr<ID3D12Resource> shared;
ComPtr<ID3D12Fence> fence;
ComPtr<ID3D12CommandAllocator> producerAllocator, consumerAllocator;
ComPtr<ID3D12GraphicsCommandList> producer, consumer;
Handle event;
std::unique_ptr<Device> native;
std::unique_ptr<Packet> packet;
std::unique_ptr<rr_bridge::Replay> replay;
VkCommandPool pool = VK_NULL_HANDLE;
VkCommandBuffer command = VK_NULL_HANDLE;
VkFence completion = VK_NULL_HANDLE;
uint64_t sequence = 0;
bool pending = false, failed = false;
std::unique_ptr<Snapshot> retained;
Impl(ID3D12Device* device, const Creation& c)
: creation(c), layout(c.width, c.height, c.outWidth, c.outHeight), d3d(device) {
// Reserve each guide's largest supported texel size once. Format
// changes can then preserve the packet, model and temporal history.
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.bytes = layout.outputOffset + layout.outputPitch * layout.oh;
}
void dispatch() {
if (native && native->instance) volkLoadInstance(native->instance);
if (native && native->device) volkLoadDevice(native->device);
}
~Impl() {
dispatch();
replay.reset();
if (native && native->device) {
if (completion) vkDestroyFence(native->device, completion, nullptr);
if (pool) vkDestroyCommandPool(native->device, pool, nullptr);
}
packet.reset(); native.reset();
}
void initialize(const std::string& directory) {
if (!d3d) throw std::runtime_error("Null native RR D3D12 device");
// Match the Linux RR adapter's contract: HDR and render-resolution,
// unjittered motion. D/E already perform their own exposure reduction.
// NVIDIA/DLSS include/nvsdk_ngx_defs.h defines these public flag bits.
if ((creation.flags & 3u) != 3u)
throw std::runtime_error("Native RR requires HDR and render-resolution motion vectors");
if (creation.flags & ~(3u | 8u | 64u))
throw std::runtime_error("Native RR creation flags require an unimplemented conversion");
if (creation.quality < 0 || creation.quality > 5) throw std::runtime_error("Unsupported RR quality mode");
native = std::make_unique<Device>(); native->initialize(adapter_luid(d3d.Get()), true);
replay = std::make_unique<rr_bridge::Replay>(layout, native->physical, native->device, directory);
const unsigned indices[] = {3, 2, 1, 4, 5, 0};
const auto requested = creation.presets[indices[creation.quality]];
if (requested && requested != replay->model->preset()) throw std::runtime_error("Configured RR model differs from requested preset");
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_SHARED, IID_PPV_ARGS(&fence)), "RR shared fence");
Handle memoryHandle, fenceHandle;
dx(d3d->CreateSharedHandle(shared.Get(), nullptr, GENERIC_ALL, nullptr, &memoryHandle.value), "RR packet handle");
dx(d3d->CreateSharedHandle(fence.Get(), nullptr, GENERIC_ALL, nullptr, &fenceHandle.value), "RR fence handle");
packet = std::make_unique<Packet>(*native); packet->initialize(layout.bytes, memoryHandle.value, fenceHandle.value);
event.value = CreateEventW(nullptr, FALSE, FALSE, nullptr);
if (!event.value) throw std::runtime_error("RR completion event creation failed");
auto createList = [&](auto& allocator, auto& list) {
dx(d3d->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&allocator)), "RR allocator");
dx(d3d->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, allocator.Get(), nullptr, IID_PPV_ARGS(&list)), "RR command list");
dx(list->Close(), "RR initial close");
};
createList(producerAllocator, producer); createList(consumerAllocator, consumer);
VkCommandPoolCreateInfo pci{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO}; pci.queueFamilyIndex = native->family;
Device::check(vkCreateCommandPool(native->device, &pci, nullptr, &pool), "RR command pool");
VkCommandBufferAllocateInfo cai{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};
cai.commandPool = pool; cai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cai.commandBufferCount = 1;
Device::check(vkAllocateCommandBuffers(native->device, &cai, &command), "RR command buffer");
VkFenceCreateInfo fci{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
Device::check(vkCreateFence(native->device, &fci, nullptr, &completion), "RR Vulkan completion fence");
}
void validate(ID3D12CommandQueue* q, const Snapshot& frame, const std::array<ResourceAccess, Snapshot::Count>& access) {
if (failed) throw std::runtime_error("Native 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 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.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");
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 native RR texture");
ComPtr<ID3D12Device> resourceOwner; dx(resource->GetDevice(IID_PPV_ARGS(&resourceOwner)), "RR texture device");
if (resourceOwner.Get() != d3d.Get()) throw std::runtime_error("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);
// 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))
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 ||
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");
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 evaluate(ID3D12CommandQueue* q, const Snapshot& frame, const std::array<ResourceAccess, Snapshot::Count>& access) {
validate(q, frame, access); dispatch();
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);
replay->configure(i, format.format);
layout.planes[i].format = format.format; layout.planes[i].bytes = format.bytes;
}
// Recording mutates engine upload/history bookkeeping. Any later error
// requires recreation, even if no GPU submission has started yet.
failed = true;
commands::InternalScope internal;
dx(producerAllocator->Reset(), "RR producer allocator reset"); dx(producer->Reset(producerAllocator.Get(), nullptr), "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(), "RR producer close");
dx(consumerAllocator->Reset(), "RR consumer allocator reset"); dx(consumer->Reset(consumerAllocator.Get(), nullptr), "RR consumer reset");
transition(consumer.Get(), shared.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_SOURCE);
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);
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(), "RR consumer close");
Device::check(vkResetCommandPool(native->device, pool, 0), "RR Vulkan pool reset");
VkCommandBufferBeginInfo begin{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
Device::check(vkBeginCommandBuffer(command, &begin), "RR Vulkan begin");
VkBufferMemoryBarrier acquire{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER}; acquire.buffer = packet->buffer; acquire.size = VK_WHOLE_SIZE;
acquire.srcQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL; acquire.dstQueueFamilyIndex = native->family;
acquire.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
vkCmdPipelineBarrier(command, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1, &acquire, 0, nullptr);
replay->record(command, packet->buffer, frame.settings);
VkBufferMemoryBarrier release = acquire; release.srcQueueFamilyIndex = native->family; release.dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL;
release.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; release.dstAccessMask = 0;
vkCmdPipelineBarrier(command, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 1, &release, 0, nullptr);
Device::check(vkEndCommandBuffer(command), "RR Vulkan end");
const uint64_t ready = sequence + 1, done = sequence + 2, complete = sequence + 3;
VkTimelineSemaphoreSubmitInfo values{VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO};
values.waitSemaphoreValueCount = values.signalSemaphoreValueCount = 1; values.pWaitSemaphoreValues = &ready; values.pSignalSemaphoreValues = &done;
VkPipelineStageFlags stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
VkSubmitInfo submit{VK_STRUCTURE_TYPE_SUBMIT_INFO, &values}; submit.waitSemaphoreCount = submit.signalSemaphoreCount = 1;
submit.pWaitSemaphores = submit.pSignalSemaphores = &packet->semaphore; submit.pWaitDstStageMask = &stage;
submit.commandBufferCount = 1; submit.pCommandBuffers = &command;
Device::check(vkResetFences(native->device, 1, &completion), "RR completion reset");
retained = std::make_unique<Snapshot>(frame); queue = q; pending = true;
try {
ID3D12CommandList* inputs[] = {producer.Get()}; q->ExecuteCommandLists(1, inputs);
dx(q->Signal(fence.Get(), ready), "RR input ready signal");
Device::check(vkQueueSubmit(native->queue, 1, &submit, completion), "RR model submission");
dx(q->Wait(fence.Get(), done), "RR output queue wait");
ID3D12CommandList* outputs[] = {consumer.Get()}; q->ExecuteCommandLists(1, outputs);
dx(q->Signal(fence.Get(), complete), "RR completion signal");
dx(fence->SetEventOnCompletion(complete, event.value), "RR completion event");
if (WaitForSingleObject(event.value, 5000) != WAIT_OBJECT_0) throw std::runtime_error("RR D3D12 completion timeout");
Device::check(vkWaitForFences(native->device, 1, &completion, VK_TRUE, 5000000000ull), "RR Vulkan completion wait");
if (fence->GetCompletedValue() == UINT64_MAX) throw std::runtime_error("RR D3D12 device removed");
pending = false; failed = false; retained.reset(); queue.Reset(); sequence = complete;
} catch (...) { failed = true; throw; }
}
};
Backend::Backend(ID3D12Device* device, const Creation& creation, const std::string& directory) {
std::lock_guard<std::mutex> lock(dispatchMutex);
auto candidate = std::make_unique<Impl>(device, creation);
candidate->initialize(directory); impl = std::move(candidate);
}
Backend::~Backend() {
std::lock_guard<std::mutex> lock(dispatchMutex);
if (impl && impl->pending) {
std::fprintf(stderr, "D4R_RR_QUARANTINE incomplete GPU work; retaining backend and guide resources\n");
(void)impl.release();
} else impl.reset();
}
void Backend::evaluate(ID3D12CommandQueue* queue, const Snapshot& frame, const std::array<ResourceAccess, Snapshot::Count>& access) {
std::lock_guard<std::mutex> lock(dispatchMutex);
impl->evaluate(queue, frame, access);
}
void Backend::validate_frame(const Snapshot& frame) {
std::lock_guard<std::mutex> lock(dispatchMutex);
impl->validate_frame(frame);
}
void Backend::drain() {
std::lock_guard<std::mutex> lock(dispatchMutex);
if (impl->pending || impl->failed) throw std::runtime_error("RR backend cannot retire failed GPU work");
}
}
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include "d3d12_external.h"
#include "rr_ngx_snapshot.h"
#include <memory>
namespace d4r::win::rr {
// Native RR execution on borrowed game resources. No fixture files, synthetic
// guide creation, child process, NVIDIA runtime, or CPU tensor transfer.
class Backend {
struct Impl;
std::unique_ptr<Impl> impl;
public:
Backend(ID3D12Device* device, const Creation& creation, const std::string& modelDirectory);
~Backend();
Backend(const Backend&) = delete;
Backend& operator=(const Backend&) = delete;
void validate_frame(const Snapshot& frame);
void evaluate(ID3D12CommandQueue* queue, const Snapshot& frame, const std::array<ResourceAccess, Snapshot::Count>& access);
void drain();
};
}
+78
View File
@@ -0,0 +1,78 @@
#pragma once
#include <windows.h>
#include <volk.h>
#include <cstring>
#include <stdexcept>
#include <string>
#include <vector>
namespace rr_bridge {
// Owns only Vulkan objects. The caller supplies the actual D3D12 adapter LUID;
// production callers must obtain it from the borrowed game's device.
struct NativeDevice {
VkInstance instance = VK_NULL_HANDLE;
VkPhysicalDevice physical = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE;
VkQueue queue = VK_NULL_HANDLE;
uint32_t family = 0;
NativeDevice() = default;
NativeDevice(const NativeDevice&) = delete;
NativeDevice& operator=(const NativeDevice&) = delete;
static void check(VkResult result, const char* operation) {
if (result != VK_SUCCESS)
throw std::runtime_error(std::string(operation) + " VkResult=" + std::to_string(result));
}
void initialize(const LUID& luid, bool inference) {
if (instance) throw std::runtime_error("RR Vulkan device already initialized");
check(volkInitialize(), "Vulkan loader");
VkApplicationInfo app{VK_STRUCTURE_TYPE_APPLICATION_INFO}; app.apiVersion = VK_API_VERSION_1_3;
VkInstanceCreateInfo ici{VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO}; ici.pApplicationInfo = &app;
check(vkCreateInstance(&ici, nullptr, &instance), "Vulkan instance"); volkLoadInstance(instance);
uint32_t count = 0;
check(vkEnumeratePhysicalDevices(instance, &count, nullptr), "Vulkan device count");
std::vector<VkPhysicalDevice> devices(count);
check(vkEnumeratePhysicalDevices(instance, &count, devices.data()), "Vulkan devices");
for (auto candidate : devices) {
VkPhysicalDeviceIDProperties id{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES};
VkPhysicalDeviceProperties2 properties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, &id};
vkGetPhysicalDeviceProperties2(candidate, &properties);
if (id.deviceLUIDValid && !std::memcmp(id.deviceLUID, &luid, VK_LUID_SIZE)) physical = candidate;
}
if (!physical) throw std::runtime_error("D3D12/Vulkan adapter LUID mismatch");
uint32_t countFamilies = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physical, &countFamilies, nullptr);
std::vector<VkQueueFamilyProperties> properties(countFamilies);
vkGetPhysicalDeviceQueueFamilyProperties(physical, &countFamilies, properties.data());
family = countFamilies;
for (uint32_t i = 0; i < countFamilies; ++i)
if (properties[i].queueFlags & VK_QUEUE_COMPUTE_BIT) { family = i; break; }
if (family == countFamilies) throw std::runtime_error("No Vulkan compute queue");
VkPhysicalDeviceCooperativeMatrixFeaturesKHR cm{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_KHR};
VkPhysicalDeviceVulkan13Features v13{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, &cm};
VkPhysicalDeviceVulkan12Features v12{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, &v13};
VkPhysicalDeviceVulkan11Features v11{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES, &v12};
VkPhysicalDeviceFeatures2 features{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, &v11};
vkGetPhysicalDeviceFeatures2(physical, &features);
if (!v12.timelineSemaphore) throw std::runtime_error("Timeline semaphore unsupported");
if (inference && (!cm.cooperativeMatrix || !v12.shaderFloat16 || !v11.storageBuffer16BitAccess || !v13.subgroupSizeControl))
throw std::runtime_error("Missing RR cooperative-matrix/fp16/subgroup feature");
if (!inference) v13.pNext = nullptr;
float priority = 1;
VkDeviceQueueCreateInfo qi{VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO};
qi.queueFamilyIndex = family; qi.queueCount = 1; qi.pQueuePriorities = &priority;
std::vector<const char*> extensions{"VK_KHR_external_memory_win32", "VK_KHR_external_semaphore_win32"};
if (inference) extensions.push_back(VK_KHR_COOPERATIVE_MATRIX_EXTENSION_NAME);
VkDeviceCreateInfo di{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, &features};
di.queueCreateInfoCount = 1; di.pQueueCreateInfos = &qi;
di.enabledExtensionCount = uint32_t(extensions.size()); di.ppEnabledExtensionNames = extensions.data();
check(vkCreateDevice(physical, &di, nullptr, &device), "Vulkan device"); volkLoadDevice(device);
vkGetDeviceQueue(device, family, 0, &queue);
}
// Callers must retire submitted work before destroying this owner. A failed
// submission may instead quarantine the owner and its dependent resources.
~NativeDevice() {
if (device) vkDestroyDevice(device, nullptr);
if (instance) vkDestroyInstance(instance, nullptr);
}
};
}
+65
View File
@@ -0,0 +1,65 @@
#pragma once
#include "rr_native_device.h"
#include <vulkan/vulkan_win32.h>
namespace rr_bridge {
// Imports the caller's D3D12 shared packet and fence. Exported Win32 handles
// remain caller-owned; Vulkan keeps its own references after successful import.
struct NativePacket {
NativeDevice& native;
VkBuffer buffer = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
VkSemaphore semaphore = VK_NULL_HANDLE;
explicit NativePacket(NativeDevice& owner) : native(owner) {}
NativePacket(const NativePacket&) = delete;
NativePacket& operator=(const NativePacket&) = delete;
void initialize(VkDeviceSize bytes, HANDLE resource, HANDLE fence) {
if (buffer || !bytes || !resource || !fence)
throw std::runtime_error("Invalid RR shared packet import");
auto memoryProperties = reinterpret_cast<PFN_vkGetMemoryWin32HandlePropertiesKHR>(
vkGetDeviceProcAddr(native.device, "vkGetMemoryWin32HandlePropertiesKHR"));
auto importSemaphore = reinterpret_cast<PFN_vkImportSemaphoreWin32HandleKHR>(
vkGetDeviceProcAddr(native.device, "vkImportSemaphoreWin32HandleKHR"));
if (!memoryProperties || !importSemaphore) throw std::runtime_error("Missing Win32 import entry points");
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 = bytes;
bi.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
NativeDevice::check(vkCreateBuffer(native.device, &bi, nullptr, &buffer), "External Vulkan buffer");
VkMemoryRequirements req; vkGetBufferMemoryRequirements(native.device, buffer, &req);
VkMemoryWin32HandlePropertiesKHR handleProperties{VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHR};
NativeDevice::check(memoryProperties(native.device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT,
resource, &handleProperties), "D3D12 resource memory types");
VkPhysicalDeviceMemoryProperties properties{};
vkGetPhysicalDeviceMemoryProperties(native.physical, &properties);
const uint32_t bits = req.memoryTypeBits & handleProperties.memoryTypeBits;
uint32_t type = properties.memoryTypeCount;
for (uint32_t i = 0; i < properties.memoryTypeCount; ++i)
if ((bits & (1u << i)) && (properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
type = i; break;
}
if (type == properties.memoryTypeCount) throw std::runtime_error("No compatible RR packet memory type");
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 = buffer;
VkMemoryAllocateInfo ai{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, &dedicated};
ai.allocationSize = req.size; ai.memoryTypeIndex = type;
NativeDevice::check(vkAllocateMemory(native.device, &ai, nullptr, &memory), "Dedicated D3D12 memory import");
NativeDevice::check(vkBindBufferMemory(native.device, buffer, memory, 0), "Bind imported buffer");
VkSemaphoreTypeCreateInfo timeline{VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO};
timeline.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;
VkSemaphoreCreateInfo sci{VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, &timeline};
NativeDevice::check(vkCreateSemaphore(native.device, &sci, nullptr, &semaphore), "Vulkan timeline semaphore");
VkImportSemaphoreWin32HandleInfoKHR si{VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR};
si.semaphore = semaphore; si.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT; si.handle = fence;
NativeDevice::check(importSemaphore(native.device, &si), "D3D12 fence import");
}
// The owner must retire GPU submissions before destroying an import.
~NativePacket() {
if (semaphore) vkDestroySemaphore(native.device, semaphore, nullptr);
if (buffer) vkDestroyBuffer(native.device, buffer, nullptr);
if (memory) vkFreeMemory(native.device, memory, nullptr);
}
};
}
+347
View File
@@ -0,0 +1,347 @@
// Synthetic NGX feature-13 lifecycle and delayed-submission test. No game access.
#define WIDL_EXPLICIT_AGGREGATE_RETURNS
#include <volk.h>
#include "d3d12_external.h"
#include "rr_bridge_replay.h"
#include "ngx_parameters.h"
#include "ngx_public_abi.h"
#include <dxgi1_4.h>
using namespace d4r::win;
int main(int argc, char** argv) try {
std::setvbuf(stdout, nullptr, _IONBF, 0);
if (argc != 6 && argc != 7) throw std::runtime_error("Usage: rr_ngx_probe SHIM INPUTS REFERENCE OUTPUT MODEL [contracts]");
const std::string mode = argc == 7 ? argv[6] : "";
const bool contracts = mode.find("contracts") != std::string::npos;
const bool inherited = mode.find("inherited") != std::string::npos;
const bool enhanced = mode.find("enhanced") != std::string::npos;
const bool split = mode.find("split") != std::string::npos;
const bool unavailable = mode.find("unavailable") != std::string::npos;
const bool withoutSr = mode.find("without-sr") != std::string::npos;
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;
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;
else if (mode.find("depth-d32") != std::string::npos) depthFormat = DXGI_FORMAT_D32_FLOAT;
else if (mode.find("depth-r32-typeless") != std::string::npos) depthFormat = DXGI_FORMAT_R32_TYPELESS;
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]);
const auto canonical = layout;
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;
}
layout.planes[1].format = VK_FORMAT_R32G32_SFLOAT; layout.planes[1].bytes = 8;
layout.planes[4].format = VK_FORMAT_R16_SFLOAT; layout.planes[4].bytes = 2;
layout.initializePacket();
}
const auto floatLayout = layout;
auto uploadFrame = [&](void* destination, unsigned index) {
if (!floatGuides) { layout.upload(destination, index); return; }
// CPU conversion belongs only to fixture initialization. The production
// backend transfers and samples the actual D3D12 formats on the GPU.
std::memset(destination, 0, size_t(layout.bytes));
for (size_t i = 0; i < layout.planes.size(); ++i) {
const auto& plane = layout.planes[i];
if (!plane.enabled || (i >= 7 && !layout.frames[index].optional[i - 7])) continue;
auto data = rr_bridge::read(layout.inputs / (std::to_string(layout.frames[index].id) + "-" + plane.name + ".bin"));
if (data.size() != size_t(layout.rw) * layout.rh * canonical.planes[i].bytes)
throw std::runtime_error("Invalid canonical input for float-guide fixture");
for (uint32_t y = 0; y < layout.rh; ++y) for (uint32_t x = 0; x < layout.rw; ++x) {
const auto* source = data.data() + (size_t(y) * layout.rw + x) * canonical.planes[i].bytes;
auto* dest = static_cast<char*>(destination) + plane.offset + y * plane.pitch + x * plane.bytes;
if ((i == 0 || i == 1 || i == 3) && plane.bytes != canonical.planes[i].bytes) {
for (uint32_t c = 0; c < plane.bytes / 4; ++c) {
uint16_t half; std::memcpy(&half, source + c * 2, 2);
const float value = float(d4r::diag::binary16::half_value(half)); std::memcpy(dest + c * 4, &value, 4);
}
} else std::memcpy(dest, source, plane.bytes);
}
}
};
ComPtr<IDXGIFactory4> factory; dx(CreateDXGIFactory1(IID_PPV_ARGS(&factory)), "NGX RR factory");
ComPtr<IDXGIAdapter1> adapter;
for (UINT i = 0; ; ++i) {
if (factory->EnumAdapters1(i, &adapter) == DXGI_ERROR_NOT_FOUND) throw std::runtime_error("No AMD adapter");
DXGI_ADAPTER_DESC1 desc{}; dx(adapter->GetDesc1(&desc), "NGX RR adapter");
if (desc.VendorId == 0x1002 && !(desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)) break;
adapter.Reset();
}
ComPtr<ID3D12Device> device; dx(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&device)), "NGX RR device");
if (enhanced) {
D3D12_FEATURE_DATA_D3D12_OPTIONS12 support{};
dx(device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS12, &support, sizeof(support)), "RR enhanced barrier support");
if (!support.EnhancedBarriersSupported) throw std::runtime_error("RR enhanced barriers unavailable");
}
auto enhancedAccess = [](bool output) {
ResourceAccess access; access.enhanced = true;
access.layout = output ? D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS : D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE;
access.access = output ? D3D12_BARRIER_ACCESS_UNORDERED_ACCESS : D3D12_BARRIER_ACCESS_SHADER_RESOURCE;
return access;
};
auto switchBarrierModel = [&](ID3D12GraphicsCommandList* list, ID3D12Resource* texture, bool output, bool toEnhanced) {
// Microsoft requires COMMON at each legacy/enhanced API boundary.
// https://microsoft.github.io/DirectX-Specs/d3d/D3D12EnhancedBarriers.html
const auto legacy = output ? D3D12_RESOURCE_STATE_UNORDERED_ACCESS : D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
if (toEnhanced) transition(list, texture, legacy, D3D12_RESOURCE_STATE_COMMON);
ComPtr<ID3D12GraphicsCommandList7> newer; dx(list->QueryInterface(IID_PPV_ARGS(&newer)), "RR enhanced fixture list");
const auto access = enhancedAccess(output);
D3D12_TEXTURE_BARRIER barrier{}; barrier.pResource = texture;
barrier.LayoutBefore = toEnhanced ? D3D12_BARRIER_LAYOUT_COMMON : access.layout;
barrier.LayoutAfter = toEnhanced ? access.layout : D3D12_BARRIER_LAYOUT_COMMON;
barrier.AccessBefore = toEnhanced ? D3D12_BARRIER_ACCESS_COMMON : access.access;
barrier.AccessAfter = toEnhanced ? access.access : D3D12_BARRIER_ACCESS_COMMON;
barrier.SyncBefore = barrier.SyncAfter = D3D12_BARRIER_SYNC_ALL;
D3D12_BARRIER_GROUP group{}; group.Type = D3D12_BARRIER_TYPE_TEXTURE; group.NumBarriers = 1; group.pTextureBarriers = &barrier;
newer->Barrier(1, &group);
if (!toEnhanced) transition(list, texture, D3D12_RESOURCE_STATE_COMMON, legacy);
};
ComPtr<ID3D12CommandQueue> queue; D3D12_COMMAND_QUEUE_DESC q{}; q.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
dx(device->CreateCommandQueue(&q, IID_PPV_ARGS(&queue)), "NGX RR queue");
d4r::diag::Library shim(d4r::diag::wide(std::filesystem::absolute(argv[1]).string()));
auto init = shim.symbol<unsigned(*)(const char*, int, const char*, const wchar_t*, ID3D12Device*, unsigned, const void*)>("NVSDK_NGX_D3D12_Init_ProjectID");
auto allocate = shim.symbol<unsigned(*)(void**)>("NVSDK_NGX_D3D12_AllocateParameters");
auto destroy = shim.symbol<unsigned(*)(void*)>("NVSDK_NGX_D3D12_DestroyParameters");
auto create = shim.symbol<unsigned(*)(ID3D12GraphicsCommandList*, unsigned, void*, void**)>("NVSDK_NGX_D3D12_CreateFeature");
auto evaluate = shim.symbol<unsigned(*)(ID3D12GraphicsCommandList*, const void*, void*, void*)>("NVSDK_NGX_D3D12_EvaluateFeature");
auto release = shim.symbol<unsigned(*)(void*)>("NVSDK_NGX_D3D12_ReleaseFeature");
auto shutdown = shim.symbol<unsigned(*)()>("NVSDK_NGX_D3D12_Shutdown");
auto check = [](unsigned r, const char* operation) { if (r != 1) throw std::runtime_error(std::string(operation) + " NGX=" + std::to_string(r)); };
auto require = [](bool condition, const char* operation) { if (!condition) throw std::runtime_error(operation); };
if (contracts || unavailable) {
auto requirements = shim.symbol<unsigned(*)(IDXGIAdapter*, const d4r::ngx::FeatureDiscoveryInfo*, d4r::ngx::FeatureRequirement*)>("NVSDK_NGX_D3D12_GetFeatureRequirements");
d4r::ngx::FeatureDiscoveryInfo discovery{}; discovery.FeatureID = 13;
d4r::ngx::FeatureRequirement result{};
check(requirements(adapter.Get(), &discovery, &result), "RR requirements");
require((result.FeatureSupported == 0) != unavailable, "RR requirement readiness disagrees with configured backend");
ComPtr<IDXGIAdapter> warp; dx(factory->EnumWarpAdapter(IID_PPV_ARGS(&warp)), "RR WARP adapter");
check(requirements(warp.Get(), &discovery, &result), "RR WARP requirements");
require(result.FeatureSupported != 0, "RR advertised on unsupported WARP adapter");
discovery.FeatureID = 14;
check(requirements(adapter.Get(), &discovery, &result), "Unknown feature requirements");
require(result.FeatureSupported != 0, "Unknown NGX feature advertised");
}
std::string projectId = "24480451-f00d-face-1304-0308dabad187", projectVersion = "1.0";
std::wstring featurePath = std::filesystem::absolute(argv[2]).wstring();
const wchar_t* featurePaths[] = {featurePath.c_str()};
d4r::ngx::FeatureCommonInfo common{}; common.PathListInfo = {featurePaths, 1};
check(init(projectId.c_str(), 0, projectVersion.c_str(), L"", device.Get(), 0x15, &common), "RR Init");
// Native RR defers SR startup. Its Init snapshot must survive caller-owned
// project/path strings and path-list descriptors changing before SR use.
projectId.assign("mutated-project"); projectVersion.assign("mutated-version");
featurePath.assign(L"mutated-feature-path"); featurePaths[0] = nullptr;
common.PathListInfo = {}; common.LoggingInfo.DisableOtherLoggingSinks = true;
if (contracts || unavailable) {
auto capabilities = shim.symbol<unsigned(*)(void**)>("NVSDK_NGX_D3D12_GetCapabilityParameters");
void* caps = nullptr; check(capabilities(&caps), "RR capabilities");
require(d4r::ngx::int_value(caps, "SuperSamplingDenoising.Available") == (unavailable ? 0 : 1) &&
(d4r::ngx::int_value(caps, "SuperSamplingDenoising.FeatureInitResult") == 1) != unavailable, "RR capability readiness failed");
require(d4r::ngx::int_value(caps, "SuperSampling.Available") == (withoutSr ? 0 : 1), "SR capability disagrees with dependency readiness");
check(destroy(caps), "RR destroy capabilities");
}
void* p = nullptr; check(allocate(&p), "RR Allocate");
d4r_ngx_set_uint(p, "Width", layout.rw); d4r_ngx_set_uint(p, "Height", layout.rh);
d4r_ngx_set_uint(p, "OutWidth", layout.ow); d4r_ngx_set_uint(p, "OutHeight", layout.oh);
d4r_ngx_set_int(p, "PerfQualityValue", 2);
const unsigned creationFlags = 3u | (layout.frames.front().settings.depthInverted ? 8u : 0u) | (autoExposure ? 64u : 0u);
d4r_ngx_set_uint(p, "DLSS.Feature.Create.Flags", creationFlags);
std::printf("RR_NGX_CREATION flags=%u\n", creationFlags);
if (unavailable) {
void* rejected = reinterpret_cast<void*>(uintptr_t(1));
require(create(nullptr, 13, p, &rejected) != 1 && !rejected, "Unavailable RR backend accepted creation");
check(destroy(p), "Unavailable RR destroy parameters");
check(shutdown(), "Unavailable RR shutdown");
std::puts("PASS RR_NGX_UNAVAILABLE capabilities=0 requirements=unsupported creation=rejected sr_available=1");
return 0;
}
if (contracts) {
void* rejected = reinterpret_cast<void*>(uintptr_t(1));
require(create(nullptr, 14, p, &rejected) != 1 && !rejected, "Unknown feature creation accepted");
d4r_ngx_set_uint(p, "Width", 0); rejected = reinterpret_cast<void*>(uintptr_t(1));
require(create(nullptr, 13, p, &rejected) != 1 && !rejected, "Invalid RR creation accepted");
d4r_ngx_set_uint(p, "Width", layout.rw);
for (unsigned flags : {0u, 1u, 2u, 7u, 131u}) {
d4r_ngx_set_uint(p, "DLSS.Feature.Create.Flags", flags); rejected = reinterpret_cast<void*>(uintptr_t(1));
require(create(nullptr, 13, p, &rejected) != 1 && !rejected, "Unsupported RR creation flags accepted");
}
d4r_ngx_set_uint(p, "DLSS.Feature.Create.Flags", creationFlags);
}
void* feature = nullptr; check(create(nullptr, 13, p, &feature), "RR 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;
if (stencil) {
const auto desc = resource_desc(textures->inputs[2].Get()); UINT rows = 0; UINT64 rowBytes = 0;
device->GetCopyableFootprints(&desc, 1, 1, 0, &stencilFootprint, &rows, &rowBytes, &stencilBytes);
require(rows == layout.rh && rowBytes == layout.rw && stencilBytes && stencilBytes != UINT64_MAX, "Unexpected stencil-plane footprint");
stencilUpload = make_buffer(device.Get(), stencilBytes, D3D12_HEAP_TYPE_UPLOAD, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_GENERIC_READ);
stencilReadback = make_buffer(device.Get(), stencilBytes, D3D12_HEAP_TYPE_READBACK, D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
}
auto stencilTransition = [](ID3D12GraphicsCommandList* list, ID3D12Resource* image, D3D12_RESOURCE_STATES from, D3D12_RESOURCE_STATES to) {
D3D12_RESOURCE_BARRIER barrier{}; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Transition = {image, 1, from, to}; list->ResourceBarrier(1, &barrier);
};
auto stencilCopy = [&](ID3D12GraphicsCommandList* list, ID3D12Resource* image, bool upload) {
D3D12_TEXTURE_COPY_LOCATION buffer{}; buffer.pResource = upload ? stencilUpload.Get() : stencilReadback.Get();
buffer.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; buffer.PlacedFootprint = stencilFootprint;
auto texture = rr_bridge::D3D12Frames::texture(image); texture.SubresourceIndex = 1;
const auto copy = upload ? D3D12_RESOURCE_STATE_COPY_DEST : D3D12_RESOURCE_STATE_COPY_SOURCE;
stencilTransition(list, image, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, copy);
list->CopyTextureRegion(upload ? &texture : &buffer, 0, 0, 0, upload ? &buffer : &texture, nullptr);
stencilTransition(list, image, copy, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
};
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<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 (switchGuides) {
layout = index % 2 ? floatLayout : canonical;
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;
dx(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&allocator)), "NGX RR fixture allocator");
dx(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, allocator.Get(), nullptr, IID_PPV_ARGS(&list)), "NGX RR fixture list");
if (stencil) {
dx(stencilUpload->Map(0, &noRead, &mapped), "RR stencil upload map");
std::memset(mapped, 0, size_t(stencilBytes));
for (uint32_t y = 0; y < layout.rh; ++y) for (uint32_t x = 0; x < layout.rw; ++x)
static_cast<uint8_t*>(mapped)[stencilFootprint.Offset + y * stencilFootprint.Footprint.RowPitch + x] = uint8_t(x * 13 + y * 17 + index * 29);
stencilUpload->Unmap(0, nullptr); stencilCopy(list.Get(), textures->inputs[2].Get(), true);
}
uint32_t optionalMask = 0;
for (unsigned i = 0; i < 4; ++i) if (layout.frames[index].optional[i]) optionalMask |= 1u << i;
textures->recordInputs(list.Get(), upload.Get(), scratch.Get(), optionalMask);
if (enhanced) {
for (size_t i = 0; i < textures->inputs.size(); ++i)
if (textures->inputs[i] && (i < 7 || (optionalMask & (1u << (i - 7)))))
switchBarrierModel(list.Get(), textures->inputs[i].Get(), false, true);
switchBarrierModel(list.Get(), textures->output.Get(), true, true);
}
ComPtr<ID3D12CommandAllocator> prefixAllocator;
ComPtr<ID3D12GraphicsCommandList> prefix;
if (inherited) {
// The preceding list publishes every resource state, including an
// output which has not otherwise been used by a fixture upload.
if (!enhanced) {
transition(list.Get(), textures->output.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COMMON);
transition(list.Get(), textures->output.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
}
dx(list->Close(), "NGX RR prefix close"); prefix = list; prefixAllocator = allocator;
list.Reset(); allocator.Reset();
dx(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&allocator)), "NGX RR inherited allocator");
dx(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, allocator.Get(), nullptr, IID_PPV_ARGS(&list)), "NGX RR inherited list");
}
const char* names[] = {"Color", "MotionVectors", "Depth", "GBuffer.Normals", "GBuffer.Roughness", "DLSS.Input.DiffuseAlbedo", "DLSS.Input.SpecularAlbedo"};
for (size_t i = 0; i < 7; ++i) d4r_ngx_set_d3d12_resource(p, names[i], textures->inputs[i].Get());
d4r_ngx_set_d3d12_resource(p, "Output", textures->output.Get());
const char* optionalNames[] = {"DLSSD.SpecularHitDistance", "DLSS.DisocclusionMask", "DLSSD.ScreenSpaceSubsurfaceScatteringGuide", "DLSSD.ColorBeforeParticles"};
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_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);
d4r_ngx_set_uint(p, "Reset", settings.reset); d4r_ngx_set_uint(p, "DLSS.Use.HW.Depth", settings.hardwareDepth);
d4r_ngx_set_uint(p, "DLSS.Roughness.Mode", settings.roughnessPacked);
d4r_ngx_set_void(p, "WorldToViewMatrix", settings.worldToView); d4r_ngx_set_void(p, "ViewToClipMatrix", settings.viewToClip);
const char* states[] = {"D4R.Color.State", "D4R.Motion.State", "D4R.Depth.State", "D4R.Normals.State", "D4R.Roughness.State", "D4R.Diffuse.State", "D4R.Specular.State"};
if (!inherited) {
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 (contracts && index == 0) {
auto rejects = [&](const char* name) { require(evaluate(list.Get(), feature, p, nullptr) != 1, name); };
if (depthFormat == DXGI_FORMAT_D32_FLOAT || stencil) {
d4r_ngx_set_uint(p, "DLSS.Input.Depth.Subrect.Base.X", 1); rejects("Cropped depth-stencil accepted");
d4r_ngx_set_uint(p, "DLSS.Input.Depth.Subrect.Base.X", 0);
std::puts("RR_NGX_DEPTH_SUBRECT cropped_rejected=1");
}
d4r_ngx_set_void(p, "ViewToClipMatrix", nullptr); rejects("Missing RR matrix accepted");
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", layout.rw);
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");
}
if (split && index == 0) {
D3D12_RESOURCE_BARRIER barrier{}; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Transition = {textures->inputs[0].Get(), 0, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_COPY_SOURCE};
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY; list->ResourceBarrier(1, &barrier);
require(evaluate(list.Get(), feature, p, nullptr) != 1, "NGX RR accepted unfinished split barrier");
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_END_ONLY; list->ResourceBarrier(1, &barrier);
transition(list.Get(), textures->inputs[0].Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
std::puts("RR_NGX_SPLIT unfinished_rejected=1 paired_before_submit=1");
}
check(evaluate(list.Get(), feature, p, nullptr), "RR 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);
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);
list->CopyTextureRegion(&dest, 0, 0, 0, &source, nullptr);
if (enhanced) {
texture_copy_barrier(list.Get(), output.Get(), enhancedAccess(true), false, true);
for (size_t i = 0; i < textures->inputs.size(); ++i)
if (textures->inputs[i] && (i < 7 || (optionalMask & (1u << (i - 7)))))
switchBarrierModel(list.Get(), textures->inputs[i].Get(), false, false);
switchBarrierModel(list.Get(), output.Get(), true, false);
} else transition(list.Get(), output.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
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;
check(destroy(p), "RR Destroy parameters before submission"); p = nullptr;
textures.reset(); output.Reset();
}
dx(list->Close(), "NGX RR fixture close");
ID3D12CommandList* lists[] = {prefix ? prefix.Get() : list.Get(), list.Get()};
queue->ExecuteCommandLists(inherited ? 2 : 1, lists);
dx(queue->Signal(fence.Get(), index + 1), "NGX RR fixture signal");
dx(fence->SetEventOnCompletion(index + 1, event.value), "NGX RR fixture completion");
if (WaitForSingleObject(event.value, 10000) != WAIT_OBJECT_0) throw std::runtime_error("NGX RR fixture timeout");
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 (stencil) {
D3D12_RANGE stencilRange{0, SIZE_T(stencilBytes)};
dx(stencilReadback->Map(0, &stencilRange, &mapped), "RR stencil verification map");
bool exact = true;
for (uint32_t y = 0; y < layout.rh; ++y) for (uint32_t x = 0; x < layout.rw; ++x)
exact &= static_cast<const uint8_t*>(mapped)[stencilFootprint.Offset + y * stencilFootprint.Footprint.RowPitch + x] == uint8_t(x * 13 + y * 17 + index * 29);
stencilReadback->Unmap(0, nullptr); require(exact, "RR bridge changed stencil values");
}
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);
}
check(shutdown(), "RR Shutdown");
if (contracts) std::puts("PASS RR_NGX_CONTRACTS capabilities=1 requirements=1 warp_rejected=1 invalid_inputs_rejected=1");
if (inherited) std::puts("PASS RR_NGX_INHERITED prefix_list=1 caller_states=0");
if (enhanced) std::puts("PASS RR_NGX_ENHANCED tracked_access=1 restored=1");
if (split) std::puts("PASS RR_NGX_SPLIT unfinished_rejected=1");
if (withoutSr) std::puts("PASS RR_NGX_WITHOUT_SR rr_executed=1 sr_available=0");
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 (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;
} catch (const std::exception& error) { std::fprintf(stderr, "FAIL RR_NGX %s\n", error.what()); return 1; }
+122
View File
@@ -0,0 +1,122 @@
#pragma once
#include "ngx_parameters.h"
#include "../../engine/runtime.h"
#include <d3d12.h>
#include <wrl/client.h>
#include <array>
#include <cmath>
#include <cstring>
#include <stdexcept>
#include <string>
namespace d4r::win::rr {
inline unsigned number(void* parameters, const char* name, unsigned fallback = 0) {
unsigned value = 0;
if (d4r_ngx_get_uint(parameters, name, &value) == 1) return value;
int signedValue = 0;
if (d4r_ngx_get_int(parameters, name, &signedValue) == 1 && signedValue >= 0) return unsigned(signedValue);
return fallback;
}
struct Creation {
unsigned width = 0, height = 0, outWidth = 0, outHeight = 0;
unsigned flags = 0, outputSubrects = 0, hardwareDepth = 0, roughnessMode = 0;
unsigned depthType = 0, normalsType = 0, roughnessType = 0, diffuseType = 0, specularType = 0;
int quality = 0;
std::array<unsigned, 6> presets{};
explicit Creation(void* parameters) {
if (!parameters) throw std::runtime_error("Null RR creation parameters");
width = number(parameters, "Width"); height = number(parameters, "Height");
outWidth = number(parameters, "OutWidth"); outHeight = number(parameters, "OutHeight");
if (!width || !height || !outWidth || !outHeight || width > 16384 || height > 16384 || outWidth > 16384 || outHeight > 16384)
throw std::runtime_error("Invalid RR creation geometry");
flags = number(parameters, "DLSS.Feature.Create.Flags");
outputSubrects = number(parameters, "DLSS.Enable.Output.Subrects");
hardwareDepth = number(parameters, "DLSS.Use.HW.Depth");
roughnessMode = number(parameters, "DLSS.Roughness.Mode");
depthType = number(parameters, "DLSS.Depth.Type"); normalsType = number(parameters, "DLSS.Normals.Type");
roughnessType = number(parameters, "DLSS.Roughness.Type"); diffuseType = number(parameters, "DLSS.Diffuse.Type");
specularType = number(parameters, "DLSS.Specular.Type");
quality = ngx::int_value(parameters, "PerfQualityValue");
const char* names[] = {"RayReconstruction.Hint.Render.Preset.DLAA", "RayReconstruction.Hint.Render.Preset.Quality",
"RayReconstruction.Hint.Render.Preset.Balanced", "RayReconstruction.Hint.Render.Preset.Performance",
"RayReconstruction.Hint.Render.Preset.UltraPerformance", "RayReconstruction.Hint.Render.Preset.UltraQuality"};
for (size_t i = 0; i < presets.size(); ++i) presets[i] = number(parameters, names[i]);
}
};
// All data that may outlive EvaluateFeature is owned here. No matrix pointer
// or resource pointer depends on the caller's parameter object's lifetime.
struct Snapshot {
enum Plane : size_t { Color, Motion, Depth, Normals, Roughness, Diffuse, Specular, Output,
Exposure, HitDistance, Disocclusion, Subsurface, BeforeParticles, Count };
std::array<Microsoft::WRL::ComPtr<ID3D12Resource>, Count> resources;
std::array<std::array<unsigned, 2>, Count> origins{};
RFrameSettings settings;
unsigned renderWidth = 0, renderHeight = 0;
unsigned depthType = 0, normalsType = 0, roughnessType = 0, diffuseType = 0, specularType = 0;
float exposureScale = 1;
static ID3D12Resource* resource(void* parameters, const char* name, const char* alias = nullptr) {
ID3D12Resource* value = nullptr;
(void)d4r_ngx_get_d3d12_resource(parameters, name, &value);
if (!value && alias) (void)d4r_ngx_get_d3d12_resource(parameters, alias, &value);
return value;
}
Snapshot(const Creation& creation, void* parameters) {
if (!parameters) throw std::runtime_error("Null RR frame parameters");
const char* names[] = {"Color", "MotionVectors", "Depth", "GBuffer.Normals", "GBuffer.Roughness",
"DLSS.Input.DiffuseAlbedo", "DLSS.Input.SpecularAlbedo", "Output", "ExposureTexture",
"DLSSD.SpecularHitDistance", "DLSS.DisocclusionMask", "DLSSD.ScreenSpaceSubsurfaceScatteringGuide", "DLSSD.ColorBeforeParticles"};
for (size_t i = 0; i < Count; ++i)
resources[i] = resource(parameters, names[i], i == Diffuse ? "GBuffer.DiffuseAlbedo" : i == Specular ? "GBuffer.SpecularAlbedo" : nullptr);
renderWidth = number(parameters, "DLSS.Render.Subrect.Dimensions.Width", creation.width);
renderHeight = number(parameters, "DLSS.Render.Subrect.Dimensions.Height", creation.height);
if (!renderWidth) renderWidth = creation.width;
if (!renderHeight) renderHeight = creation.height;
const char* prefixes[] = {"DLSS.Input.Color", "DLSS.Input.MV", "DLSS.Input.Depth", "DLSS.Input.Normals",
"DLSS.Input.Roughness", "DLSS.Input.DiffuseAlbedo", "DLSS.Input.SpecularAlbedo", "DLSS.Output"};
for (size_t i = 0; i < 8; ++i) {
origins[i][0] = number(parameters, (std::string(prefixes[i]) + ".Subrect.Base.X").c_str());
origins[i][1] = number(parameters, (std::string(prefixes[i]) + ".Subrect.Base.Y").c_str());
}
const char* optionalPrefixes[] = {"DLSSD.SpecularHitDistance", "DLSS.DisocclusionMask",
"DLSSD.ScreenSpaceSubsurfaceScatteringGuide", "DLSSD.ColorBeforeParticles"};
for (size_t i = HitDistance; i < Count; ++i) {
origins[i][0] = number(parameters, (std::string(optionalPrefixes[i - HitDistance]) + ".Subrect.Base.X").c_str());
origins[i][1] = number(parameters, (std::string(optionalPrefixes[i - HitDistance]) + ".Subrect.Base.Y").c_str());
}
settings.jitter[0] = ngx::float_value(parameters, "Jitter.Offset.X");
settings.jitter[1] = ngx::float_value(parameters, "Jitter.Offset.Y");
settings.motionScale[0] = ngx::float_value(parameters, "MV.Scale.X", 1);
settings.motionScale[1] = ngx::float_value(parameters, "MV.Scale.Y", 1);
settings.preExposure = ngx::float_value(parameters, "DLSS.Pre.Exposure", 1);
settings.frameTimeMs = ngx::float_value(parameters, "FrameTimeDeltaInMsec", 16.666667f);
exposureScale = ngx::float_value(parameters, "DLSS.Exposure.Scale", 1);
settings.reset = number(parameters, "Reset") != 0;
settings.depthInverted = (creation.flags & 8) != 0;
settings.hardwareDepth = number(parameters, "DLSS.Use.HW.Depth", creation.hardwareDepth) != 0;
// D/E consume a bound roughness texture even in packed mode. Only the
// absent-texture case uses normals.w; preset F has a different contract.
settings.roughnessPacked = number(parameters, "DLSS.Roughness.Mode", creation.roughnessMode) == 1 && !resources[Roughness];
if (settings.roughnessPacked) { resources[Roughness] = resources[Normals]; origins[Roughness] = origins[Normals]; }
depthType = number(parameters, "DLSS.Depth.Type", creation.depthType);
normalsType = number(parameters, "DLSS.Normals.Type", creation.normalsType);
roughnessType = number(parameters, "DLSS.Roughness.Type", creation.roughnessType);
diffuseType = number(parameters, "DLSS.Diffuse.Type", creation.diffuseType);
specularType = number(parameters, "DLSS.Specular.Type", creation.specularType);
const char* matrixNames[] = {"WorldToViewMatrix", "ViewToClipMatrix"};
float* matrices[] = {settings.worldToView, settings.viewToClip};
for (unsigned i = 0; i < 2; ++i) {
void* source = nullptr;
if (d4r_ngx_get_void(parameters, matrixNames[i], &source) != 1 || !source)
throw std::runtime_error(std::string("Missing RR matrix: ") + matrixNames[i]);
std::memcpy(matrices[i], source, sizeof(float) * 16);
for (unsigned n = 0; n < 16; ++n)
if (!std::isfinite(matrices[i][n])) throw std::runtime_error("Nonfinite RR matrix");
}
for (float value : {settings.jitter[0], settings.jitter[1], settings.motionScale[0], settings.motionScale[1],
settings.preExposure, settings.frameTimeMs, exposureScale})
if (!std::isfinite(value)) throw std::runtime_error("Nonfinite RR frame scalar");
if (settings.preExposure <= 0 || exposureScale <= 0 || settings.frameTimeMs < 0)
throw std::runtime_error("Invalid RR exposure or frame time");
}
};
}
+88
View File
@@ -0,0 +1,88 @@
// CPU-only checks of the NGX RR snapshot contract. No model or GPU dispatch.
#include <volk.h>
#include "rr_ngx_snapshot.h"
#include <cstdio>
#include <limits>
namespace {
struct Parameters {
void* value = d4r_ngx_parameters_create();
Parameters() { if (!value) throw std::runtime_error("Parameter allocation failed"); }
~Parameters() { if (value) d4r_ngx_parameters_destroy(value); }
};
void require(bool condition, const char* message) {
if (!condition) throw std::runtime_error(message);
}
template<class F> void rejects(F operation, const char* message) {
try { operation(); } catch (const std::runtime_error&) { return; }
throw std::runtime_error(message);
}
}
int main() try {
using namespace d4r::win::rr;
Parameters parameters;
void* p = parameters.value;
// RR callers can publish dimensions as signed NGX integers.
d4r_ngx_set_int(p, "Width", 64); d4r_ngx_set_uint(p, "Height", 48);
d4r_ngx_set_uint(p, "OutWidth", 128); d4r_ngx_set_uint(p, "OutHeight", 96);
d4r_ngx_set_int(p, "DLSS.Feature.Create.Flags", 8);
d4r_ngx_set_int(p, "DLSS.Use.HW.Depth", 1);
d4r_ngx_set_int(p, "DLSS.Roughness.Mode", 1);
d4r_ngx_set_uint(p, "RayReconstruction.Hint.Render.Preset.Quality", 4);
Creation creation(p);
require(creation.width == 64 && creation.presets[1] == 4, "RR creation values lost");
float view[16]{}, clip[16]{};
for (unsigned i = 0; i < 16; ++i) { view[i] = float(i + 1); clip[i] = float(i + 32); }
d4r_ngx_set_void(p, "WorldToViewMatrix", view); d4r_ngx_set_void(p, "ViewToClipMatrix", clip);
d4r_ngx_set_float(p, "Jitter.Offset.X", .25f);
d4r_ngx_set_float(p, "DLSS.Pre.Exposure", 2.f);
d4r_ngx_set_uint(p, "DLSS.Input.Normals.Subrect.Base.X", 7);
d4r_ngx_set_uint(p, "DLSSD.SpecularHitDistance.Subrect.Base.X", 5);
d4r_ngx_set_uint(p, "DLSSD.ColorBeforeParticles.Subrect.Base.Y", 9);
d4r_ngx_set_uint(p, "DLSS.Use.HW.Depth", 0);
Snapshot frame(creation, p);
view[0] = -91; clip[15] = -93;
d4r_ngx_set_float(p, "DLSS.Pre.Exposure", 3.f);
d4r_ngx_set_uint(p, "Width", 777);
d4r_ngx_set_uint(p, "RayReconstruction.Hint.Render.Preset.Quality", 5);
require(frame.settings.worldToView[0] == 1 && frame.settings.viewToClip[15] == 47, "RR matrices were not copied");
require(frame.settings.preExposure == 2 && frame.settings.jitter[0] == .25f, "RR scalars were not copied");
require(creation.width == 64 && creation.presets[1] == 4, "RR creation depends on mutable parameters");
require(frame.settings.depthInverted && !frame.settings.hardwareDepth, "RR frame override lost");
require(frame.renderWidth == 64 && frame.renderHeight == 48, "RR render dimensions lost creation fallback");
require(frame.settings.roughnessPacked && frame.origins[Snapshot::Roughness][0] == 7, "RR packed roughness origin lost");
require(frame.origins[Snapshot::HitDistance][0] == 5 && frame.origins[Snapshot::BeforeParticles][1] == 9,
"RR optional guide origins lost");
auto copied = frame;
require(copied.settings.worldToView[0] == 1, "Copied RR snapshot lost matrix ownership");
{
Parameters frameParameters;
d4r_ngx_set_void(frameParameters.value, "WorldToViewMatrix", view);
d4r_ngx_set_void(frameParameters.value, "ViewToClipMatrix", clip);
Snapshot baseline(creation, frameParameters.value);
require(baseline.settings.hardwareDepth && baseline.settings.roughnessPacked,
"RR frame settings lost creation baseline");
require(baseline.settings.preExposure == 1 && baseline.settings.motionScale[0] == 1,
"RR frame scalar defaults incorrect");
}
view[3] = std::numeric_limits<float>::infinity();
rejects([&] { Snapshot bad(creation, p); }, "Nonfinite RR matrix accepted");
view[3] = 4;
d4r_ngx_set_float(p, "DLSS.Pre.Exposure", 0);
rejects([&] { Snapshot bad(creation, p); }, "Zero RR pre-exposure accepted");
d4r_ngx_set_float(p, "DLSS.Pre.Exposure", 1);
d4r_ngx_set_float(p, "Jitter.Offset.Y", std::numeric_limits<float>::quiet_NaN());
rejects([&] { Snapshot bad(creation, p); }, "Nonfinite RR jitter accepted");
d4r_ngx_set_float(p, "Jitter.Offset.Y", 0);
d4r_ngx_set_void(p, "ViewToClipMatrix", nullptr);
rejects([&] { Snapshot bad(creation, p); }, "Missing RR matrix accepted");
d4r_ngx_set_uint(p, "Width", 0);
rejects([&] { Creation bad(p); }, "Zero RR creation width accepted");
// The completed snapshot must still be valid after the parameter object dies.
d4r_ngx_parameters_destroy(parameters.value); parameters.value = nullptr;
require(copied.settings.viewToClip[15] == 47 && copied.settings.preExposure == 2, "RR snapshot retained parameter storage");
std::puts("PASS RR_SNAPSHOT cpu_only=1 matrices_owned=1 scalars_owned=1 creation_fallback=1 invalid_rejected=1");
return 0;
} catch (const std::exception& error) {
std::fprintf(stderr, "FAIL RR_SNAPSHOT %s\n", error.what()); return 1;
}
+179
View File
@@ -0,0 +1,179 @@
"""Run the synthetic production NGX feature-13 harness in a bounded process."""
import argparse
import hashlib
import json
import os
import shutil
from pathlib import Path
import subprocess
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--contracts', action='store_true', help='also check capability readiness and negative contracts')
parser.add_argument('--inherited', action='store_true', help='inherit access from a preceding list in the same submission batch')
parser.add_argument('--enhanced', action='store_true', help='exercise enhanced texture access and restoration')
parser.add_argument('--split', action='store_true', help='reject a legacy split barrier, then complete the pair and evaluate')
parser.add_argument('--unavailable', choices=('disabled', 'missing-model', 'fsr'), help='test unavailable RR configuration without inference')
parser.add_argument('--without-sr', action='store_true', help='remove SR dependency configuration and require native RR execution')
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('--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')
for name in ('probe', 'shim', 'runtime', 'core', 'dlss', 'model', 'inputs', 'reference', 'output'):
parser.add_argument('--' + name, required=True, type=lambda value: Path(value).resolve())
args = parser.parse_args()
if args.use_existing_ini and not args.player_config:
parser.error('--use-existing-ini requires --player-config')
if args.player_config and (args.without_sr or args.unavailable or not args.contracts):
parser.error('--player-config requires --contracts, configured SR dependencies and native RR')
if args.enhanced and args.split:
parser.error('--split currently covers legacy barriers; use a separate --enhanced run')
if args.without_sr and (args.unavailable or not args.contracts):
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')
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)
env = {key: value for key, value in os.environ.items()
if not key.startswith('D4R_') and key not in ('ZLUDA_CUDA_LIB', 'ZLUDA_LOG_DIR', 'ZLUDA_CACHE_DIR')}
runtime = args.runtime
native = runtime / 'native'
if (native / 'gfx1101').is_dir():
native = native / 'gfx1101'
env.update(HIP_PATH=str(runtime / 'hip'), D4R_HIP_ROOT=str(runtime / 'hip'), D4R_NVCUDA_DLL=str(runtime / 'zluda/nvcuda.dll'),
ZLUDA_CUDA_LIB=str(runtime / 'zluda/nvcuda.dll'), D4R_DLSS_DLL=str(args.dlss),
D4R_NGX_CORE=str(args.core), D4R_NVAPI_DLL=str(runtime / 'nvapi/nvapi64.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_QUIET_API='1',
D4R_ZLUDA_WMMA='1', D4R_ZLUDA_WMMA_F16_REFERENCE='1',
D4R_NGX_DATA_DIR=str(args.output / 'ngx-data'), D4R_RR_BACKEND='native', D4R_RR_MODEL_DIR=str(args.model))
if args.unavailable == 'disabled':
env.pop('D4R_RR_BACKEND')
elif args.unavailable == 'missing-model':
missing = args.output / 'missing-model'
env['D4R_RR_MODEL_DIR'] = str(missing)
elif args.unavailable == 'fsr':
env['D4R_RR_BACKEND'] = 'fsr'
if args.without_sr:
for key in ('HIP_PATH', 'D4R_HIP_ROOT', 'D4R_NVCUDA_DLL', 'ZLUDA_CUDA_LIB', 'D4R_DLSS_DLL',
'D4R_NGX_CORE', 'D4R_NVAPI_DLL', 'D4R_NVAPI_BACKEND', 'D4R_FORMAT_MODULE', 'D4R_ZLUDA_NATIVE_DIR'):
env.pop(key, None)
else:
env['PATH'] = str(runtime / 'hip/bin') + os.pathsep + str(runtime / 'zluda') + os.pathsep + env['PATH']
command = [str(args.probe), str(args.shim), str(args.inputs), str(args.reference),
str(args.output / 'frames'), str(args.model)]
if args.player_config:
staged = args.output / 'player'
shutil.copytree(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
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, target)
if not args.use_existing_ini:
config = (Path(__file__).resolve().parents[2] / 'packaging/windows/d4r.ini').read_text()
config = config.replace('Backend = off', 'Backend = native').replace('ModelDir = auto', 'ModelDir = ' + str(args.model))
(staged / 'd4r.ini').write_text(config)
env = {key: value for key, value in os.environ.items()
if not key.startswith('D4R_') and key not in ('HIP_PATH', 'ZLUDA_CUDA_LIB', 'ZLUDA_LOG_DIR', 'ZLUDA_CACHE_DIR')}
command[1] = str(staged / 'd4r_nvngx.dll')
mode = '-'.join(name for name in ('contracts', 'inherited', 'enhanced', 'split', 'unavailable') if getattr(args, name))
if args.without_sr:
mode += '-without-sr'
if args.float_guides:
mode += '-float-guides'
if args.switch_guides:
mode += '-switch-guides'
if args.auto_exposure:
mode += '-auto-exposure'
if args.depth_format != 'r32':
mode += '-depth-' + args.depth_format
if mode:
command.append(mode)
report = {'passed': False, 'gameAccessed': False, 'qualityReference': False,
'artifacts': {str(path): hashlib.sha256(path.read_bytes()).hexdigest()
for path in (args.probe, args.shim, args.core, args.dlss)}}
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=180)
report['exitCode'] = result.returncode
text = (args.output / 'stdout.log').read_text(errors='replace')
if args.unavailable:
if result.returncode or 'PASS RR_NGX_UNAVAILABLE' not in text:
raise RuntimeError('Unavailable NGX RR checks failed; inspect ' + str(args.output))
report.update(passed=True, unavailable=args.unavailable, rrAvailable=False, srAvailable=True,
creationRejected=True, frames=0)
print('PASS NGX RR unavailable=' + args.unavailable + ' sr_available=1')
return
if result.returncode or 'PASS RR_NGX_LIFECYCLE' not in text:
raise RuntimeError('NGX RR harness failed; inspect ' + str(args.output))
if args.contracts and 'PASS RR_NGX_CONTRACTS' not in text:
raise RuntimeError('NGX RR contract checks incomplete')
if args.inherited and 'PASS RR_NGX_INHERITED' not in text:
raise RuntimeError('NGX RR inherited access checks incomplete')
for name in ('enhanced', 'split'):
if getattr(args, name) and 'PASS RR_NGX_' + name.upper() not in text:
raise RuntimeError('NGX RR ' + name + ' access checks incomplete')
if args.without_sr and ('PASS RR_NGX_WITHOUT_SR' not in text or 'D4R_RUNTIME platform=Windows' in text):
raise RuntimeError('NGX RR did not prove execution without initialized SR runtime')
if args.float_guides and 'PASS RR_NGX_FLOAT_GUIDES' not in text:
raise RuntimeError('NGX RR alternate float guide checks incomplete')
if args.switch_guides and 'PASS RR_NGX_SWITCH_GUIDES' not in text:
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')
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"))}'
if marker not in text:
raise RuntimeError('NGX RR depth-format/stencil checks incomplete')
if args.contracts and args.depth_format.startswith('d32') and 'RR_NGX_DEPTH_SUBRECT cropped_rejected=1' not in text:
raise RuntimeError('NGX RR cropped depth-stencil rejection missing')
expected = [line.split()[1] + '-out.bin' for line in (args.inputs / 'frames.txt').read_text().splitlines()
if line.startswith('frame ')]
outputs = sorted(path.name for path in (args.output / 'frames').glob('*-out.bin'))
if outputs != sorted(expected):
raise RuntimeError('Incomplete NGX RR output set')
for name in expected:
if (args.output / 'frames' / name).read_bytes() != (args.reference / name).read_bytes():
raise RuntimeError('NGX RR reference mismatch: ' + name)
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}
masks = [sum(1 << index for index, name in enumerate(optional_names)
if (args.inputs / (frame.removesuffix('-out.bin') + '-' + name + '.bin')).is_file())
for frame in expected]
frame_lines = [line.split() for line in (args.inputs / 'frames.txt').read_text().splitlines() if line.startswith('frame ')]
for index, mask in enumerate(masks):
depth_mode = int(frame_lines[index][8])
marker = (f'RR_NGX_FRAME index={index} snapshot_mutated=1 exact=1 optional_mask={mask}'
f' hardware_depth={int(bool(depth_mode & 2))} depth_inverted={int(bool(depth_mode & 1))}')
if marker not in text:
raise RuntimeError('Incomplete NGX optional-guide/depth setting checks')
report.update(passed=True, frames=len(expected), standaloneExact=True, contracts=args.contracts, inherited=args.inherited,
enhanced=args.enhanced, split=args.split,
withoutSr=args.without_sr,
floatGuides=args.float_guides,
switchGuides=args.switch_guides,
creationFlags=int(next(line.split('flags=')[1] for line in text.splitlines()
if line.startswith('RR_NGX_CREATION flags='))),
optionalGuideFrames=optional_frames, optionalMasks=masks,
depthFormat=args.depth_format, stencilPreserved=args.depth_format.startswith('d32s8'),
hardwareDepthFrames=sum(bool(int(line[8]) & 2) for line in frame_lines),
playerConfig=args.player_config,
preservedPlayerIni=args.use_existing_ini,
invertedDepthFrames=sum(bool(int(line[8]) & 1) for line in frame_lines),
snapshotMutation=True, releaseBeforeSubmit=True, parameterDestroyBeforeSubmit=True)
print('PASS NGX RR frames=' + str(len(expected)) + ' standalone_exact=1')
finally:
(args.output / 'summary.json').write_text(json.dumps(report, indent=2) + '\n')
if __name__ == '__main__':
main()