engine/rr: native preset F (DLSS 4.5 Ray Reconstruction) and the reference's fused roundings

- engine/compile_rr2.py, runtime_rr2.inc, engine/rr/rr2_m.comp, exposure_f.comp,
  gen_rr2_enc0.py, rr2_pack.py, rr2_ref.py: the rr2_* model (nvngx_dlssd.dll 310.9's
  third network) as d4r's own shaders - exposure, enc0 gather/block split, enc1-5 and
  dec4-0 through one matrix shader, translated output passes; weights packed from the
  DLL, argument blocks from the capture
- ptx2rr: match the driver's fused roundings - opaque-zero OR around cvt.rn.f16.f32
  against v_fma_mix, half-away-from-zero formatted stores, UNORM 10:10:10:2 as RGBA32F
  of q/1023, OCML tanhF with the AMDGPU exp lowering, binary16 fma and fetch roundings;
  --probe for register bisection against ZLUDA
- dec0_m/swin_m/enc0_*: RR_MIX build option (D4R_RR_MIX=1), one v_fma_mix rounding per
  32-deep step, ~5% faster, not bit-exact; default build stays exact
- runtime_rr.inc: RModel/REngine dispatch to the D4RR0002 model; replay_rr: RGBA32F and
  A2B10G10R1010 texel sizes, roughnessPacked bit, larger debug staging
- shim: engine\rr-f model slot, 310.9 version probe so preset F is only claimed when the
  CUDA path has it; d4r.ini / d4r_config.py Model = F; run_rr_engine_compare.sh D4R_RR_DLL
- kernels/rr/swin_native.h + rr_swin_native_test.cpp: HIP port of encoder 1 for the
  FP8-product and rounding cost measurements
- engine/rr/README: preset F's block, options, capture-by-capture results, timings and
  the open camera-matrix difference
This commit is contained in:
woutermans committed 2026-10-13 13:59:17 +02:00
1 parent 065fe2c2a1
commit 8eb7a94533
31 files changed
+3547 -128

No files matched your search

+203
View File
@@ -0,0 +1,203 @@
#!/usr/bin/env python3
"""Build the engine's Ray Reconstruction model directory for preset F (DLSS 4.5, the rr2_* kernels of
nvngx_dlssd.dll 310.9) from a capture of the denoiser.
usage: compile_rr2.py CAPTURE_DIR OUT_DIR [--ptx-dir DIR] [--frame N] [--native NVNGX_DLSSD_DLL]
CAPTURE_DIR is a bridge capture of a few preset F frames (engine/rr/rr_capture.py, taken with
D4R_CUDA_REPLAY_DUMP_FILTER=rr2 D4R_CUDA_LAUNCH_STATS_FILTER=rr2 and preset hint 6). Writes:
<stage>.spv one shader per kernel, generated from its PTX by engine/rr/ptx2rr.py
weights-<stage>.bin the stage's weight image as NGX uploads it (argument 24); the output filter's is the constant
table of its PTX module; enc0-aux.bin is encoder 0's second table (argument 104)
<stage>.args the argument block of the capture's frame, pointers and handles cleared: the runtime keeps its
constants and fills what depends on the frame or the geometry (engine/runtime_rr2.inc)
exposure_f0.spv, exposure_f1.spv engine/rr/exposure_f.comp
rr.txt the manifest RModel reads ("D4RR0002")
With --native, the stages engine/rr/rr2_m.comp implements (enc1 ... enc5, dec4 ... dec1, dec0's first kernel) are that
shader with weights read from the DLL itself (engine/rr/rr2_pack.py) instead of the translation: bit-identical to
NVIDIA's kernels on the reference captures and about three times faster. D4R_RR_MIX=1 builds the one-rounding variant.
Everything but the exposure shader is derived from NVIDIA's DLL: it stays on the user's machine and is never packaged.
"""
import argparse
import json
import os
import subprocess
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE / 'rr'))
from rr_capture import Capture # noqa: E402
from test_layer import stage_params # noqa: E402
NETWORK = ['enc0', 'enc1', 'enc2', 'enc3', 'enc4', 'enc5'] + [f'dec{n}_{k}' for n in (4, 3, 2, 1, 0) for k in (0, 1)]
STAGES = NETWORK + ['post', 'down', 'pooled']
KERNEL = {s: f'rr2_{s}_kernel' for s in NETWORK[:6]}
KERNEL.update({f'dec{n}_{k}': f'rr2_dec{n}_kernel_{k}' for n in range(5) for k in (0, 1)})
KERNEL.update(post='rr2_post_kernel', down='rr2_downsample_kernel_static_hdr_no_transparency',
pooled='rr2_pooled_history2_kernel')
BLOCK = {'enc0': '32,1,1', 'enc1': '32,1,1', 'enc2': '32,1,4', 'enc3': '32,1,4', 'enc4': '32,1,8', 'enc5': '32,1,8',
'post': '16,16,1', 'down': '16,16,1', 'pooled': '16,16,1'}
for n, b in ((4, '32,1,8'), (3, '32,1,4'), (2, '32,1,4'), (1, '32,1,2'), (0, '32,1,1')):
BLOCK[f'dec{n}_0'] = BLOCK[f'dec{n}_1'] = b
# pointer arguments that are neither the weights (24) nor the plane arena: the exposure values (16 bytes), encoder
# 0's second table, and the output passes' progress counters (the runtime leaves those pointers null)
BUFFERS = {'enc0': [104, 256], 'post': [16, 256, 264], 'down': [72, 168, 176]}
# surfaces whose image format is not the store's own guess. The two pooled histories are UNORM 10:10:10:2 arrays for
# NVIDIA; the reference this engine is checked against (ZLUDA) backs them with RGBA32F, and so does the engine.
NATIVE = ['enc1', 'enc2', 'enc3', 'enc4', 'enc5', 'dec4_0', 'dec4_1', 'dec3_0', 'dec3_1', 'dec2_0', 'dec2_1', 'dec1_0',
'dec1_1', 'dec0_0']
# --particles: the downsample kernel with the particle layer
KERNEL['downp'] = 'rr2_downsample_kernel_static_hdr'
BLOCK['downp'] = '16,16,1'
BUFFERS['downp'] = [72, 168, 176]
# with --native: the translation of the per-pixel code around the native block (engine/rr/gen_rr2_enc0.py)
HYBRID = {'enc0': [], 'dec0_1': ['--dec0_1']}
# windows per workgroup of the hybrid stages (gen_rr2_enc0.py --group; D4R_RR2_GROUP to experiment)
GROUP = int(os.environ.get('D4R_RR2_GROUP', '1'))
# encoder 0's gather as a pass of its own (D4R_RR2_SPLIT=0: one shader, the gather on one wave of the block's four)
SPLIT = os.environ.get('D4R_RR2_SPLIT', '1') != '0'
# The pooled histories as RGBA32F holding q / 1023, as ZLUDA backs them (16 bytes a pixel). D4R_RR2_POOLED=rgb10_a2
# makes them real UNORM 10:10:10:2 images (0.1 ms faster at 4K), but the hardware's bilinear filter then rounds
# differently: identical at 3x and 2x scaling, 44 - 51 dB on temporal frames at 1.5x (capFQ, 2026-10-13)
POOLED = os.environ.get('D4R_RR2_POOLED', 'rgba32f')
SURFACES = {'dec0_1': {176: 'rgba8_snorm', 184: 'rgba8', 192: 'rgba8'}, 'post': {120: POOLED}, 'pooled': {8: POOLED}}
def find_module(ptx_dir, kernel):
for path in sorted(Path(ptx_dir).glob('*.ptx')):
if f'.entry {kernel}(' in path.read_text(errors='replace'):
return path
raise FileNotFoundError(f'no PTX module with the entry {kernel} in {ptx_dir}')
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument('capture')
ap.add_argument('output', type=Path)
ap.add_argument('--ptx-dir', default=os.path.expanduser('~/.cache/d4r-rr-corpus-f'))
ap.add_argument('--frame', type=int, default=2, help='which captured frame supplies the templates (1-based)')
ap.add_argument('--particles', metavar='CAPTURE_DIR',
help='a capture taken with DLSSD.ColorBeforeParticles: adds the downsample kernel NGX runs then (downp)')
ap.add_argument('--native', metavar='DLL', help='nvngx_dlssd.dll 310.9: build the native stages from its weights')
a = ap.parse_args()
cap = Capture(a.capture)
out = a.output
out.mkdir(parents=True, exist_ok=True)
capped = ['systemd-run', '--user', '--scope', '-q', '-p', 'MemoryMax=24G', '-p', 'MemorySwapMax=0']
lines = ['D4RR0002', 'preset 6']
pcap = Capture(a.particles) if a.particles else None
for stage in STAGES + (['downp'] if pcap else []):
kernel = KERNEL[stage]
if stage == 'downp':
cap = pcap
launches = [l for l in cap.launches if l.kernel == kernel]
per_frame = len(launches) // len([l for l in cap.launches if l.kernel == KERNEL['enc0']])
launch = launches[(a.frame - 1) * per_frame]
network = stage in NETWORK
if a.native and stage in NATIVE:
work = out / 'native-work' / stage
work.mkdir(parents=True, exist_ok=True)
weights = f'weights-{stage}m.bin'
subprocess.run([sys.executable, str(HERE / 'rr' / 'rr2_pack.py'), stage, a.native, str(out / weights),
str(work / 'rr2_stage.h')], check=True, stdout=subprocess.DEVNULL)
defs = ['-DRR_EXACT_CONVERT'] + (['-DRR_MIX'] if os.environ.get('D4R_RR_MIX') == '1' else [])
subprocess.run(capped + ['glslangValidator', '-V', '--target-env', 'vulkan1.3', *defs, f'-I{work}',
f'-I{HERE / "rr"}', str(HERE / 'rr' / 'rr2_m.comp'), '-o', str(out / f'{stage}_m.spv')],
check=True, stdout=subprocess.DEVNULL)
subprocess.run([sys.executable, str(HERE / 'rr' / 'spv_exact_convert.py'), str(out / f'{stage}_m.spv')],
check=True, stdout=subprocess.DEVNULL)
(out / f'{stage}.args').write_bytes(stage_params(launch, 24)[:len(launch.args)])
arena = launch.arena_alloc
unit = launch.allocs[arena][1] / 3152
ptrs = [f'{p.offset}:{p.delta / unit:g}' for p in launch.pointers if p.alloc == arena and p.offset != 24]
lines.append(f'stage {stage} {stage}_m.spv 32 {weights} {stage}.args - - - {",".join(ptrs)}')
print(f'{stage}: native (engine/rr/rr2_m.comp)')
continue
generated = out / f'{stage}.generated.comp'
cmd = [sys.executable, str(HERE / 'rr' / 'ptx2rr.py'), str(find_module(a.ptx_dir, kernel)), kernel, str(generated),
'--block', BLOCK[stage], '--weights-arg', '24' if network else '9999']
cmd += [f'--buffer={off}' for off in BUFFERS.get(stage, [])]
cmd += [f'--surface={off}={fmt}' for off, fmt in SURFACES.get(stage, {}).items()]
subprocess.run(cmd, check=True, stderr=subprocess.DEVNULL)
info = json.loads(generated.with_suffix('.json').read_text())
weights = '-'
hybrid = a.native and stage in HYBRID
if hybrid:
work = out / 'native-work' / stage
work.mkdir(parents=True, exist_ok=True)
weights = f'weights-{stage}m.bin'
subprocess.run([sys.executable, str(HERE / 'rr' / 'rr2_pack.py'), stage, a.native, str(out / weights),
str(work / 'rr2_stage.h')], check=True, stdout=subprocess.DEVNULL)
defs = ['-DRR_EXACT_CONVERT'] + (['-DRR_MIX'] if os.environ.get('D4R_RR_MIX') == '1' else [])
# encoder 0 in two passes (gen_rr2_enc0.py --split): its gather as the stage enc0g, whose record's address
# is a word appended to the argument block
split = stage == 'enc0' and SPLIT and GROUP == 1
how = ['--split', 'block', '--xf-word', str((len(launch.args) + 7) // 8 * 2)] if split else ['--group', str(GROUP)]
if split:
subprocess.run([sys.executable, str(HERE / 'rr' / 'gen_rr2_enc0.py'), str(find_module(a.ptx_dir, kernel)),
str(generated), str(work / 'enc0_g.comp'), '--split', 'gather', *how[2:]],
check=True, stdout=subprocess.DEVNULL)
subprocess.run(capped + ['glslangValidator', '-V', '--target-env', 'vulkan1.3', *defs, f'-I{work}',
f'-I{HERE / "rr"}', str(work / 'enc0_g.comp'), '-o', str(out / 'enc0g.spv')],
check=True, stdout=subprocess.DEVNULL)
subprocess.run([sys.executable, str(HERE / 'rr' / 'spv_exact_convert.py'), str(out / 'enc0g.spv')],
check=True, stdout=subprocess.DEVNULL)
record = int((work / 'enc0_g.record').read_text())
subprocess.run([sys.executable, str(HERE / 'rr' / 'gen_rr2_enc0.py'), str(find_module(a.ptx_dir, kernel)),
str(generated), str(work / f'{stage}_m.comp'), *HYBRID[stage], *how],
check=True, stdout=subprocess.DEVNULL)
subprocess.run(capped + ['glslangValidator', '-V', '--target-env', 'vulkan1.3', *defs, f'-I{work}',
f'-I{HERE / "rr"}', str(work / f'{stage}_m.comp'), '-o', str(out / f'{stage}.spv')],
check=True, stdout=subprocess.DEVNULL)
subprocess.run([sys.executable, str(HERE / 'rr' / 'spv_exact_convert.py'), str(out / f'{stage}.spv')],
check=True, stdout=subprocess.DEVNULL)
else:
subprocess.run(capped + ['glslangValidator', '-V', '--target-env', 'vulkan1.3', str(generated), '-o',
str(out / f'{stage}.spv')], check=True, stdout=subprocess.DEVNULL)
if hybrid:
pass
elif network:
weights = f'weights-{stage}.bin'
(out / weights).write_bytes(launch.weights_at(24))
elif generated.with_suffix('.globals.bin').exists():
weights = f'weights-{stage}.bin'
(out / weights).write_bytes(generated.with_suffix('.globals.bin').read_bytes())
if stage == 'enc0':
(out / 'enc0-aux.bin').write_bytes(launch.pre(launch.pointer(104).alloc))
args = stage_params(launch, 24 if network else 9999)[:len(launch.args)]
if hybrid and split:
args = args + bytes(-len(args) % 8) + bytes(8) # the gather record's address (the runtime's)
(out / f'{stage}.args').write_bytes(args)
# the stage's arena regions, in units of 1/3152 of NGX's arena (W0 * H0 / 32 bytes: the regions scale with it)
arena = launch.arena_alloc
unit = launch.allocs[arena][1] / 3152 if arena is not None else 1
ptrs = [f'{p.offset}:{p.delta / unit:g}' for p in launch.pointers
if p.alloc == arena and p.offset not in BUFFERS.get(stage, []) and not (network and p.offset == 24)]
if not network:
ptrs = [] # the output passes' regions are the runtime's own
tex = ','.join(map(str, info['textures'])) or '-'
surf = ','.join(f'{off}:{fmt}' for off, fmt in info['surfaces']) or '-'
buf = ','.join(map(str, info['buffers'])) or '-'
# the output passes have no 32-lane matrix products: 64-wide waves (their warp-level shuffles address the
# lane's own half, engine/rr/ptx2rr.py)
wave = 32 if network else 64
lines.append(f'stage {stage} {stage}.spv {wave} {weights} {stage}.args {tex} {surf} {buf} {",".join(ptrs) or "-"}' +
(f' x{GROUP}' if hybrid and GROUP > 1 else '')) # windows per workgroup (engine/rr/gen_rr2_enc0.py)
if hybrid and split: # the same block, bindings and regions; "rN": N words per window record
lines.append(f'stage enc0g enc0g.spv 32 - {stage}.args {tex} {surf} {buf} {",".join(ptrs) or "-"} r{record}')
print(f'{stage}: {kernel}, {len(launch.args)} argument bytes' + (' (native block)' if hybrid else ''))
for p in (0, 1):
subprocess.run(['glslangValidator', '-V', '--target-env', 'vulkan1.3', f'-DPASS={p}',
str(HERE / 'rr' / 'exposure_f.comp'), '-o', str(out / f'exposure_f{p}.spv')],
check=True, stdout=subprocess.DEVNULL)
# the driver folds float(float16_t(x)) away unless the conversions are marked (engine/rr/spv_exact_convert.py):
# the kernel truncates the exposure to binary16
subprocess.run([sys.executable, str(HERE / 'rr' / 'spv_exact_convert.py'), str(out / f'exposure_f{p}.spv')],
check=True, stdout=subprocess.DEVNULL)
(out / 'rr.txt').write_text('\n'.join(lines) + '\n')
print(f'Ray Reconstruction preset F model in {out}')
if __name__ == '__main__':
main()