Files
d4r/tests/test_motion_kernel_gpu.py
T
ayo 7c5d3c9157 feat: add d4r inference engines and Windows parity tooling
Add native K/M/RR inference engines, HIP kernels, replay and validation
harnesses, and Windows DLSS setup and runtime support. Include GPU
profiling tools, build and packaging scripts, platform integration
patches, and implementation documentation.
2026-10-16 08:54:47 -04:00

124 lines
8.7 KiB
Python

"""Optional GPU regression: independently specified thin-foreground velocity coverage.
D4R_RUN_GPU_TESTS=1 D4R_DUMP_RUNNER=/path/to/dump_runner python3 -m unittest discover -s tests -v
Build the embedded kernels first with scripts/build_d4r_motion_kernels.sh.
"""
import os
from pathlib import Path
import struct
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
@unittest.skipUnless(os.environ.get('D4R_RUN_GPU_TESTS') == '1', 'opt-in GPU test')
class MotionKernelGpuTests(unittest.TestCase):
def test_low_resolution_opaque_edges_and_padded_rows(self):
runner = os.environ['D4R_DUMP_RUNNER']
kernel = ROOT / 'build/wine-nvcuda/generated' / (os.environ.get('D4R_GPU_ARCH', 'gfx1101') + '.hsaco')
width, height, stride = 5, 3, 8
sentinel = 0xA5A5A5A5
for reversed_depth in (False, True):
for radius in (1, 2):
with self.subTest(reversed_depth=reversed_depth, radius=radius), tempfile.TemporaryDirectory() as t:
directory = Path(t); output = directory / 'out'; output.mkdir()
depth = [0.1 if reversed_depth else 0.9] * (stride*height)
depth[stride+2] = 0.9 if reversed_depth else 0.1
motion = list(range(stride*height))
arrays = [struct.pack('<24f', *depth), struct.pack('<24I', *motion), struct.pack('<24I', *([sentinel]*24))]
addresses = (0x100000000, 0x200000000, 0x300000000)
args = struct.pack('<5Q4I2f2I', *addresses, stride*4, stride*4,
width, height, width, height, 0.0, 0.0, radius, reversed_depth)
(directory/'args.bin').write_bytes(args)
manifest = ['kernel d4r_motion_dilate', 'launch 1 1 1 128 1 1 0', 'args 72']
for i, data in enumerate(arrays):
(directory/f'alloc-{i}.bin').write_bytes(data)
manifest += [f'alloc {i} {hex(addresses[i])} {len(data)}', f'pointer {i*8} {i} 0']
(directory/'manifest.txt').write_text('\n'.join(manifest)+'\n')
subprocess.run([runner, str(kernel), 'd4r_motion_dilate', str(directory), str(output), '1'], check=True, capture_output=True)
self.assertEqual((output/'alloc-0.bin').read_bytes(), arrays[0])
self.assertEqual((output/'alloc-1.bin').read_bytes(), arrays[1])
expected = [sentinel] * 24
for y in range(height):
for x in range(width):
expected[y*stride+x] = motion[stride+2] if abs(x-2) <= radius and abs(y-1) <= radius else motion[y*stride+x]
self.assertEqual((output/'alloc-2.bin').read_bytes(), struct.pack('<24I', *expected))
def test_foreground_coverage_padding_and_input_immutability(self):
runner = os.environ['D4R_DUMP_RUNNER']
kernel = ROOT / 'build/wine-nvcuda/generated' / (os.environ.get('D4R_GPU_ARCH', 'gfx1101') + '.hsaco')
sentinel = 0xA5A5A5A5
for reversed_depth in (False, True):
for radius in (1, 2):
for jitter in ((0.0, 0.0), (0.25, -0.25)):
with self.subTest(reversed_depth=reversed_depth, radius=radius, jitter=jitter), tempfile.TemporaryDirectory() as t:
directory = Path(t); output = directory / 'out'; output.mkdir()
depth = [1000.0] * 16
for y in range(2):
for x in range(3):
depth[y*8+x] = 0.1 if reversed_depth else 0.9
depth[1] = 0.9 if reversed_depth else 0.1
motion = list(range(96))
arrays = [struct.pack('<16f', *depth), struct.pack('<96I', *motion), struct.pack('<96I', *([sentinel]*96))]
addresses = (0x100000000, 0x200000000, 0x300000000)
args = struct.pack('<5Q4I2f2I', *addresses, 32, 64, 3, 2, 9, 6, *jitter, radius, reversed_depth)
(directory/'args.bin').write_bytes(args)
manifest = ['kernel d4r_motion_dilate', 'launch 1 1 1 128 1 1 0', 'args 72']
for i, data in enumerate(arrays):
(directory/f'alloc-{i}.bin').write_bytes(data)
manifest += [f'alloc {i} {hex(addresses[i])} {len(data)}', f'pointer {i*8} {i} 0']
(directory/'manifest.txt').write_text('\n'.join(manifest)+'\n')
subprocess.run([runner, str(kernel), 'd4r_motion_dilate', str(directory), str(output), '1'], check=True, capture_output=True)
self.assertEqual((output/'alloc-0.bin').read_bytes(), arrays[0])
self.assertEqual((output/'alloc-1.bin').read_bytes(), arrays[1])
expected = [sentinel] * 96
# Known single foreground cell: all neighbouring background follows its velocity.
# Foreground itself preserves its original per-pixel velocity; row padding is untouched.
for y in range(6):
for x in range(9):
inside = (3 <= x <= 5 and y <= 2) if jitter == (0.0, 0.0) else (2 <= x <= 4 and y <= 3)
expected[y*16+x] = motion[y*16+x] if inside else motion[1*16+4] if jitter == (0.0, 0.0) else motion[2*16+3]
self.assertEqual((output/'alloc-2.bin').read_bytes(), struct.pack('<96I', *expected))
def test_automatic_sky_fill_circle_nearest_ties_and_empty_sky(self):
runner = os.environ['D4R_DUMP_RUNNER']
kernel = ROOT / 'build/wine-nvcuda/generated' / (os.environ.get('D4R_GPU_ARCH', 'gfx1101') + '.hsaco')
width, height, stride = 23, 23, 32
sentinel = 0xA5A5A5A5
for reversed_depth in (False, True):
for radius in (1, 2):
for empty, near_far in ((False, False), (True, False), (False, True)):
with self.subTest(reversed_depth=reversed_depth, radius=radius, empty=empty, near_far=near_far), tempfile.TemporaryDirectory() as t:
directory = Path(t); output = directory / 'out'; output.mkdir()
depth = [1000.0] * (stride*height)
for y in range(height):
for x in range(width):
depth[y*stride+x] = (1.e-7 if near_far else 0.0) if reversed_depth else 1.0
sources = [] if empty else [(8, 11), (14, 11)]
for x, y in sources:
depth[y*stride+x] = (1.5e-7 if near_far else 0.9) if reversed_depth else (0.99 if near_far else 0.1)
motion = list(range(stride*height))
arrays = [struct.pack(f'<{len(depth)}f', *depth), struct.pack(f'<{len(motion)}I', *motion), struct.pack(f'<{len(motion)}I', *([sentinel]*len(motion)))]
addresses = (0x100000000, 0x200000000, 0x300000000)
args = struct.pack('<5Q4I2f2I', *addresses, stride*4, stride*4, width, height, width, height, 0.0, 0.0, radius, reversed_depth)
(directory/'args.bin').write_bytes(args)
manifest = ['kernel d4r_motion_dilate', 'launch 5 1 1 128 1 1 0', 'args 72']
for i, data in enumerate(arrays):
(directory/f'alloc-{i}.bin').write_bytes(data)
manifest += [f'alloc {i} {hex(addresses[i])} {len(data)}', f'pointer {i*8} {i} 0']
(directory/'manifest.txt').write_text('\n'.join(manifest)+'\n')
subprocess.run([runner, str(kernel), 'd4r_motion_dilate', str(directory), str(output), '1'], check=True, capture_output=True)
self.assertEqual((output/'alloc-0.bin').read_bytes(), arrays[0])
self.assertEqual((output/'alloc-1.bin').read_bytes(), arrays[1])
expected = [sentinel] * len(motion)
for y in range(height):
for x in range(width):
candidates = sorted(((x-sx)**2+(y-sy)**2, sx, sy) for sx, sy in sources)
expected[y*stride+x] = motion[y*stride+x]
if candidates and candidates[0][0] <= 64:
_, sx, sy = candidates[0]
expected[y*stride+x] = motion[sy*stride+sx]
self.assertEqual((output/'alloc-2.bin').read_bytes(), struct.pack(f'<{len(expected)}I', *expected))