Files
d4r/tools/windows/test_k_model.py
T
ayo a617c818de TL;DR: Add an experimental FSR Ray Regeneration backend on Windows, run the K
network natively on HIP with Vulkan->HIP shared memory, extend K kernels to
gfx11 (RDNA3), and add dependency/packaging/test tooling around them.

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

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

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

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

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

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

Docs
- Update the windows-fsr-rr, k-performance, local-package, rr-interop and
  rr-model-cache docs. Add windows-parity-handoff.md and
  windows-parity-continuation-prompt.md.
2026-10-17 01:43:06 -04:00

79 lines
3.8 KiB
Python

"""Run a bounded synthetic Vulkan K model smoke. No NGX, CUDA or game inference.
Authentic captured weights do not make the generated scene a quality reference.
Reports GPU dispatch validity separately from model provenance and game parity.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import struct
import subprocess
import sys
def main():
repo = Path(__file__).resolve().parents[2]
parser = argparse.ArgumentParser(description=__doc__)
for name in ('runner', 'model', 'output'):
parser.add_argument('--' + name, required=True, type=lambda p: Path(p).resolve())
args = parser.parse_args()
if args.output.exists() or not args.output.is_relative_to((repo / 'build').resolve()):
parser.error('use fresh output under ignored build/')
cache = json.loads((args.model / 'windows-k-model-cache.json').read_text())
for name, expected in cache['products'].items():
if Path(name).name != name or hashlib.sha256((args.model / name).read_bytes()).hexdigest() != expected:
raise RuntimeError('Compiled K model product changed')
args.output.mkdir(parents=True)
rw = rh = 128
ow = oh = 256
color, depth = bytearray(), bytearray()
for y in range(rh):
for x in range(rw):
u, v = x / (rw - 1), y / (rh - 1)
color += struct.pack('<4e', .1 + .7 * u, .1 + .6 * v, .2 + .3 * u * v, 1)
depth += struct.pack('<f', .5)
(args.output / 'color.bin').write_bytes(color)
(args.output / 'depth.bin').write_bytes(depth)
(args.output / 'motion.bin').write_bytes(bytes(rw * rh * 4))
lines = [f'output {ow} {oh}']
for index, (name, channels, bits) in enumerate((('color', 4, 16), ('motion', 2, 16), ('depth', 1, 32))):
lines.append(f'image {index} {json.dumps(str(args.output / (name + ".bin")))} {rw} {rh} {channels} {bits}')
for frame in range(4):
lines.append(f'frame_auto 0 1 2 1 1 1 {int(frame == 0)} 0 0 0 0 0 0 1.37')
case = args.output / 'case.txt'
case.write_text('\n'.join(lines) + '\n')
env = {key:value for key,value in os.environ.items() if not key.startswith('D4R_')}
report = dict(passed=False, synthetic=True, authenticCapturedWeights=True, qualityReference=False,
gameAccessed=False, cudaNgxInference=False, model=str(args.model),
runnerSha256=hashlib.sha256(args.runner.read_bytes()).hexdigest())
try:
with (args.output / 'stdout.log').open('wb') as out, (args.output / 'stderr.log').open('wb') as err:
result = subprocess.run([str(args.runner), str(args.model), str(case), str(args.output / 'frames')],
env=env, stdout=out, stderr=err, timeout=180)
if result.returncode:
raise RuntimeError('Native K runner failed; inspect logs')
sys.path.insert(0, str(repo / '.tools/python/vendor'))
import numpy as np
outputs = sorted((args.output / 'frames').glob('frame-*.312.bin'))
if len(outputs) != 4:
raise RuntimeError('Incomplete native K output set')
for path in outputs:
values = np.fromfile(path, '<f2')
if values.size != ow * oh * 4 or not np.isfinite(values).all() or float(np.ptp(values.astype('f4').reshape(-1, 4)[:, :3])) < 1e-5:
raise RuntimeError('Invalid native K output: ' + path.name)
report.update(passed=True, frames=4, finiteNonuniform=True,
outputs={p.name:hashlib.sha256(p.read_bytes()).hexdigest() for p in outputs},
gpuTimeParity=False, gameValidated=False)
print(json.dumps(report, indent=2))
except Exception as error:
report['error'] = str(error)
raise
finally:
(args.output / 'validation.json').write_text(json.dumps(report, indent=2) + '\n')
if __name__ == '__main__':
main()