Files
d4r/engine/rr/spv_float_controls.py
woutermans 50fa65d87b 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
2026-10-12 12:47:53 +02:00

72 lines
2.6 KiB
Python

#!/usr/bin/env python3
"""Give a compute SPIR-V module PTX's floating-point environment: RoundingModeRTE for 16- and 32-bit results and
DenormPreserve for 16-bit values (SPV_KHR_float_controls, core in SPIR-V 1.4).
usage: spv_float_controls.py IN.spv [OUT.spv]
Without these execution modes the rounding of float conversions is the driver's choice, where every cvt.rn of
NVIDIA's kernels rounds to nearest even. glslang keeps only one execution mode per mode id, so the modes are inserted
into the compiled module instead.
"""
import struct
import sys
OP_EXTENSION, OP_CAPABILITY, OP_ENTRY_POINT, OP_EXECUTION_MODE = 10, 17, 15, 16
CAP_DENORM_PRESERVE, CAP_ROUNDING_RTE = 4464, 4467
MODES = ((4462, 16), (4462, 32), (4459, 16)) # RoundingModeRTE 16/32, DenormPreserve 16
EXTENSION = 'SPV_KHR_float_controls'
def instructions(words):
i = 5
while i < len(words):
count = words[i] >> 16
yield words[i] & 0xffff, words[i:i + count]
i += count
def string_words(text):
data = text.encode() + b'\0'
data += b'\0' * (-len(data) % 4)
return list(struct.unpack(f'<{len(data) // 4}I', data))
def patch(words):
insts = list(instructions(words))
caps = {w[1] for op, w in insts if op == OP_CAPABILITY}
has_ext = any(op == OP_EXTENSION and EXTENSION.encode() in struct.pack(f'<{len(w) - 1}I', *w[1:]) for op, w in insts)
entry = next(w[2] for op, w in insts if op == OP_ENTRY_POINT)
modes = {(w[2], w[3]) for op, w in insts if op == OP_EXECUTION_MODE and w[1] == entry and len(w) > 3}
last_cap = max(i for i, (op, _) in enumerate(insts) if op == OP_CAPABILITY)
first_mode = min(i for i, (op, _) in enumerate(insts) if op == OP_EXECUTION_MODE)
out = list(words[:5])
for i, (op, w) in enumerate(insts):
if i == first_mode:
for mode, width in MODES:
if (mode, width) not in modes:
out += [4 << 16 | OP_EXECUTION_MODE, entry, mode, width]
out += w
if i == last_cap:
for cap in (CAP_DENORM_PRESERVE, CAP_ROUNDING_RTE):
if cap not in caps:
out += [2 << 16 | OP_CAPABILITY, cap]
if not has_ext:
name = string_words(EXTENSION)
out += [(1 + len(name)) << 16 | OP_EXTENSION, *name]
return out
def main():
src = sys.argv[1]
dst = sys.argv[2] if len(sys.argv) > 2 else src
data = open(src, 'rb').read()
words = list(struct.unpack(f'<{len(data) // 4}I', data))
if words[0] != 0x07230203:
sys.exit(f'{src}: not SPIR-V')
out = patch(words)
open(dst, 'wb').write(struct.pack(f'<{len(out)}I', *out))
if __name__ == '__main__':
main()