engine/rr: native Ray Reconstruction stages with capture/replay and shim integration
- engine/rr: d4r's own shaders for the denoiser frame (exposure, six encoders,
five decoders, hkpn output stage) at NGX layouts, plus PTX->GLSL/HIP packing
scripts, reference implementations and weight extraction
- runtime.h/runtime_rr.inc, game_rr.{h,cpp}: GameDenoiser wired into the engine
build (d4r-rr replay binary) and the D3D12 shim's feature-13 path
- shim: answer GetFeatureRequirements from d4r's own state only (the old
cross-binary CUDA handoff crashed OptiScaler's scan); drop the core's
CUDA GetFeatureRequirements import
- shim/harness: exact guide capture/replay of a frame's RR inputs
- kernels/rr: expanded enc0/engine prototypes; kernels/tools: ptxsim rewrite
plus rrsim and an engine test; vulkan: ptx2glsl FP8/cooperative-matrix
support, kvk inout buffers and KVK_DISABLE_OPTIMIZATION
- bridge: report the surface array in cuSurfObjectCreate traces
- packaging: RR settings and the ICU launch overrides in D4R_README
This commit is contained in:
1 parent
30472ae195
commit
50fa65d87b
67 files changed
+11926
-504
No files matched your search
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Pack encoder 1's extracted weights (engine/rr/enc1_ref.py --extract) for engine/rr/enc1_m.comp.
|
||||
|
||||
usage: enc1_pack.py WEIGHTS.npz OUT.bin OUT.h
|
||||
|
||||
The shader runs every product transposed (D^T = W^T X^T: weights are the A operand, activations the B operand), so a
|
||||
result's rows are output channels and its columns tokens, and the result is the next product's B operand in place.
|
||||
The rows come out in the order of W's columns, so every weight's columns are permuted here into the k order NVIDIA's
|
||||
next product contracts them in (engine/rr/enc1_ref.py: q8(x)[:, inter(n)]), and the f16 vectors that act on those
|
||||
rows likewise. Keys and queries are token slots: slot s is token inter(96)[s] (the prologue's key list), so the
|
||||
attention bias is stored transposed in slot order.
|
||||
|
||||
A tiles W (K x N) -> W^T 16x16 tiles [mt][kt], row-major (m, k): 256 * (mt * K/16 + kt) bytes
|
||||
B tiles the V projection runs X Wv (A = tokens): tiles [kt][nt], row-major (k, n)
|
||||
f16 vectors as is; the attention bias as 96 x 64 [key slot][query slot]
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from enc1_ref import inter # noqa: E402
|
||||
|
||||
I32, I64, I96 = inter(32), inter(64), inter(96)
|
||||
|
||||
|
||||
def a_tiles(w):
|
||||
k, n = w.shape
|
||||
return np.ascontiguousarray(w.T).reshape(n // 16, 16, k // 16, 16).transpose(0, 2, 1, 3).reshape(-1)
|
||||
|
||||
|
||||
def b_tiles(w):
|
||||
k, n = w.shape
|
||||
return w.reshape(k // 16, 16, n // 16, 16).transpose(0, 2, 1, 3).reshape(-1)
|
||||
|
||||
|
||||
CUBIC_MAX = 0x3873 # binary16 bits of 0.55615234375, the clamp of the scaled score
|
||||
|
||||
|
||||
def act_table():
|
||||
"""the MLP's hidden activation and its FP8 conversion, q8(act(h)), for every binary16 h, indexed by its bits: one
|
||||
byte load in place of the six binary16 operations and the conversion (the shaders' ACTQ)"""
|
||||
from enc1_ref import act
|
||||
from rrmath import q8
|
||||
h = np.arange(65536, dtype=np.uint32).astype(np.uint16).view(np.float16)
|
||||
with np.errstate(all='ignore'):
|
||||
return np.asarray(q8(act(h).astype(np.float64)), np.uint8)
|
||||
|
||||
|
||||
def cubic_table():
|
||||
"""the cubic softmax numerator's bits (enc1_ref.softmax_cubic) for every binary16 score, indexed by its bits (the
|
||||
scaling and the clamp included; NaN scores take NVIDIA's min / max, which return the other operand)"""
|
||||
x = np.arange(65536, dtype=np.uint32).astype(np.uint16).view(np.float16)
|
||||
with np.errstate(invalid='ignore', over='ignore'):
|
||||
m = (x * np.float16(0.017181396484375)).astype(np.float16)
|
||||
lim = np.float16(0.55615234375)
|
||||
m = np.where(np.isnan(m), lim, np.clip(m, -lim, lim)).astype(np.float16)
|
||||
|
||||
def fma16(a, b, c):
|
||||
return (a.astype(np.float64) * b.astype(np.float64) + c.astype(np.float64)).astype(np.float16)
|
||||
t = fma16(m, -m, np.full_like(m, np.float16(0.92724609375)))
|
||||
u = fma16(m, t, np.full_like(m, np.float16(1.375)))
|
||||
return ((u.view(np.uint16).astype(np.uint32) << 5) & 0x7FE0).astype(np.uint16)
|
||||
|
||||
|
||||
def main():
|
||||
src, out_bin, out_h = sys.argv[1:4]
|
||||
w = dict(np.load(src))
|
||||
e = {}
|
||||
for h in range(2):
|
||||
e[f'wk{h}'] = ('A', np.vstack([w[f'wk{h}a'], w[f'wk{h}b']])[:, I32])
|
||||
e[f'wv{h}'] = ('B', np.vstack([w[f'wv{h}a'], w[f'wv{h}b']])[:, I32])
|
||||
e[f'wo{h}'] = ('A', w[f'wo{h}'][:, I64])
|
||||
e[f'bias{h}'] = ('H', w[f'bias{h}'][np.ix_(I64, I96)].T)
|
||||
for b in range(8):
|
||||
e[f'w1_{b}'] = ('A', w[f'w1_{b}'][:, I32])
|
||||
e[f'b1_{b}'] = ('H', w[f'b1_{b}'][I32])
|
||||
e[f'w2_{b}'] = ('A', w[f'w2_{b}'][:, I64])
|
||||
e['wm'], e['bm'] = ('A', w['wm'][:, I96]), ('H', w['bm'][I96])
|
||||
e['wh'], e['bh'] = ('A', w['wh']), ('H', w['bh'])
|
||||
e['gain0'] = ('H', w['gain0'])
|
||||
for k in ('table7', 'gain14', 'table16'):
|
||||
e[k] = ('H', w[k][I64])
|
||||
e['cubic'] = ('U', cubic_table())
|
||||
blob, defines = bytearray(), []
|
||||
for name, (kind, a) in e.items():
|
||||
while len(blob) % 256:
|
||||
blob.append(0)
|
||||
off = len(blob)
|
||||
if kind == 'A':
|
||||
blob += a_tiles(a).tobytes()
|
||||
elif kind == 'B':
|
||||
blob += b_tiles(a).tobytes()
|
||||
elif kind == 'U':
|
||||
blob += np.ascontiguousarray(a, np.uint16).tobytes()
|
||||
else:
|
||||
blob += np.ascontiguousarray(a, np.float16).tobytes()
|
||||
defines.append(f'#define W_{name.upper()} {off}u // {kind} {a.shape}')
|
||||
open(out_bin, 'wb').write(bytes(blob))
|
||||
open(out_h, 'w').write('// generated by engine/rr/enc1_pack.py\n' + '\n'.join(defines) + '\n')
|
||||
print(f'{len(blob)} bytes, {len(e)} entries')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in new issue
Block a user