Engine: native presets K and M in the D3D12 shim, wider game coverage
A standalone Vulkan inference engine (engine/) for DLSS presets K and M that runs from weights extracted from the user's own nvngx_dlss.dll, with d4r's own input, network and reconstruction shaders or the native HIP network layers, recorded into the game's command list by the shim. Opt-in ([Engine] Enabled / D4R_ENGINE=1); anything uncovered falls back to the CUDA/ZLUDA path with the reason logged. Byte-identical to NGX's CUDA backend under the phase-capable reference (1706x960 -> 2560x1440, native HIP network, eleven frames, RX 9070 XT): - K render- and display-resolution motion vectors, HDR and LDR - K game-supplied exposure and automatic exposure with any pre-exposure and exposure scale (one residual: effective exposure exactly 2 or 4) - K and M with the DLSS indicator's inverted-axis parameters - M with any exposure settings (NGX ignores them for M) K packages need the current compile_k.py (marker D4RO0003). Also: HIP shared buffers are pooled across feature recreation (ROCm leaks destroyed imports), hipnet.bin must match the preset, M-only games do not load the K model, install_d4r_runtime.sh stages k, k-ldr, m, m-ldr with hip/, the activation table has a generator (gen_activation_m.py), and package_release.sh can bundle the engine's shaders (never its weights). Signed-off-by: woutermans <[email protected]>
This commit is contained in:
1 parent
16cfc1f063
commit
e0d1549a21
82 files changed
+13335
-96
No files matched your search
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Prepare a full-frame native K replay from a capture. Does not translate kernels.
|
||||
|
||||
prepare_k.py CAPTURE_DIR OUTPUT_DIR [--frames N] [--start FRAME]
|
||||
The model's reconstruction tables can be packaged using OUTPUT_DIR/lut.bin.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import struct
|
||||
from capture import INPUT, OUTPUT, ORDER, launches, input_params, output_params
|
||||
|
||||
|
||||
def quote(path):
|
||||
return json.dumps(str(Path(path).resolve()))
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('capture', type=Path)
|
||||
parser.add_argument('output', type=Path)
|
||||
parser.add_argument('--frames', type=int, default=0, help='0 = all captured frames')
|
||||
parser.add_argument('--start', type=int, default=0)
|
||||
a = parser.parse_args()
|
||||
inputs, outputs = launches(a.capture, INPUT), launches(a.capture, OUTPUT)
|
||||
if not inputs or not outputs or len(outputs) > len(inputs):
|
||||
raise ValueError('capture must contain complete K input/output frame pairs')
|
||||
pairs = list(zip(inputs, outputs))
|
||||
for i, (ip, op) in enumerate(pairs):
|
||||
if not ip.sequence < op.sequence or (i+1 < len(inputs) and op.sequence >= inputs[i+1].sequence):
|
||||
raise ValueError('capture has a missing or out-of-order input/output launch')
|
||||
if a.start < 0 or a.start >= len(pairs) or a.frames < 0:
|
||||
raise ValueError('invalid frame selection')
|
||||
end = len(pairs) if a.frames == 0 else min(len(pairs), a.start + a.frames)
|
||||
selected = pairs[a.start:end]
|
||||
# Unselected later frames may be incomplete (for example a capture stopped while writing them).
|
||||
for ip, op in selected:
|
||||
for resource in (*ip.resources.values(), *op.resources.values()): resource.validate()
|
||||
layer_launches = {name: launches(a.capture, 'dltss_pwin_'+name+'_layer') for name in ORDER}
|
||||
first_in, first_out = selected[0]
|
||||
geometry = []
|
||||
for name in ORDER:
|
||||
matches = [p for p in layer_launches[name] if first_in.sequence < p.sequence < first_out.sequence]
|
||||
if len(matches) != 1:
|
||||
raise ValueError(f'frame {a.start}: expected one {name} launch, found {len(matches)}')
|
||||
geometry.append((*matches[0].i2(0), *matches[0].i2(56)))
|
||||
final = first_out.resources[312]
|
||||
a.output.mkdir(parents=True, exist_ok=True)
|
||||
(a.output / 'shape.bin').write_bytes(struct.pack('<2I', final.width, final.height) + b''.join(struct.pack('<4i', *s) for s in geometry))
|
||||
# NGX's whole allocation: sixteen reconstruction tables, of which a launch uses the one for its upscaling ratio
|
||||
(a.output / 'lut.bin').write_bytes(first_out.allocation(280, 16 * 32768))
|
||||
lines = [f'shape {quote(a.output / "shape.bin")}',
|
||||
f'history {quote(first_in.resources[176].path)} {quote(first_out.resources[232].path)} {quote(first_in.resources[224].path)}']
|
||||
resource_ids = {}
|
||||
references = []
|
||||
for number, (ip, op) in enumerate(selected):
|
||||
if not ip.sequence < op.sequence or (number and ip.sequence <= selected[number-1][1].sequence):
|
||||
raise ValueError('capture frames are not in network order')
|
||||
if op.resources[312].width != final.width or op.resources[312].height != final.height:
|
||||
raise ValueError('resolution changes require another Engine')
|
||||
frame_geometry = []
|
||||
for name, expected in zip(ORDER, geometry):
|
||||
matches = [p for p in layer_launches[name] if ip.sequence < p.sequence < op.sequence]
|
||||
if len(matches) != 1 or matches[0].i2(0) != expected[:2]:
|
||||
raise ValueError(f'frame {number}: missing or changed {name} geometry')
|
||||
frame_geometry.append((*matches[0].i2(0), *matches[0].i2(56)))
|
||||
ids = []
|
||||
for off in (168, 184, 192):
|
||||
r = ip.resources[off]
|
||||
if r.path not in resource_ids:
|
||||
i = len(resource_ids); resource_ids[r.path] = i
|
||||
lines.append(f'image {i} {quote(r.path)} {r.width} {r.height} {r.channels} {r.bits}')
|
||||
ids.append(resource_ids[r.path])
|
||||
inp, outp = a.output / f'frame-{number}.input', a.output / f'frame-{number}.output'
|
||||
inp.write_bytes(input_params(ip)); outp.write_bytes(output_params(op))
|
||||
windows = a.output / f'frame-{number}.windows'
|
||||
windows.write_bytes(b''.join(struct.pack('<4i', *s) for s in frame_geometry))
|
||||
lines.append(f'frame {quote(inp)} {quote(outp)} {quote(windows)} ' + ' '.join(map(str, ids)))
|
||||
references.append({str(off): str(op.resources[off].path) for off in (288,296,304,312)})
|
||||
(a.output / 'case.txt').write_text('\n'.join(lines) + '\n')
|
||||
(a.output / 'references.json').write_text(json.dumps(references, indent=2) + '\n')
|
||||
print(f'Prepared {len(selected)} frames, {final.width}x{final.height}, token grid {geometry[0][0]}x{geometry[0][1]}')
|
||||
|
||||
|
||||
if __name__ == '__main__': main()
|
||||
Reference in new issue
Block a user