rr: add FSR Ray Regeneration, exact RDNA3 inference and motion experiments

Add an experimental FSR Ray Regeneration backend for D3D12 RR requests:
- Convert depth, motion, normals and albedos into AMD denoiser inputs.
- Split and recompose diffuse/specular signals, then run Super Sampling.
- Add stability/detail controls, demodulation guards and halo limiting.
- Handle AMD provider discovery under NVIDIA GPU spoofing.
- Add runtime probes, synthetic reference scenes and per-dispatch profiling.

Add exact gfx11 DLSS-RR inference using f16 cooperative matrices:
- Reproduce FP8 conversions, accumulation and camera reprojection arithmetic.
- Widen model weights and avoid requiring VK_EXT_shader_float8.
- Extend model building, replay tools and descriptor metadata.

Expand motion preprocessing and experimental AW2 native-motion support:
- Add tiled LDS dilation, padded allocation handling and thin-only sky fill.
- Order dilation with input waits and NGX on the selected stream.
- Add jitter-aligned native composition, hybrid routing and wire history repair.
- Add prepared masks, sparse repair and independent motion/repair controls.

Add optional fused R11G11B10 input/output conversion, sampled stage timings
and profile summarization. Extend configuration, packaging, documentation,
FidelityFX headers and CPU/GPU regression coverage.
This commit is contained in:
ayo committed 2026-10-16 04:55:25 -04:00
1 parent 17c1828f97
commit 34e6b0fe71
83 files changed
+11300 -195

No files matched your search

+11
View File
@@ -174,6 +174,17 @@ int main(int argc, char** argv) {
self.config("[Interop]\nMotionVectorDilation = 2\n",
{"D4R_MOTION_DILATION": "0"}).stdout)
def test_thin_sky_opt_in_and_environment_precedence(self):
for value, expected in (("false","0"),("true","1"),("auto","0")):
self.assertIn(f"export D4R_MOTION_THIN_SKY={expected}",
self.config(f"[Interop]\nMotionDilationThinSky = {value}\n").stdout)
self.config("[Interop]\nMotionDilationThinSky = maybe\n", expected=2)
self.assertNotIn("export D4R_MOTION_THIN_SKY",
self.config("[Interop]\nMotionDilationThinSky = true\n",
{"D4R_MOTION_THIN_SKY":"0"}).stdout)
for file in ("config/d4r.ini.default", "packaging/d4r.ini"):
self.assertRegex((ROOT/file).read_text(), r"(?m)^MotionDilationThinSky\s*=\s*false$")
def test_shipped_configs_default_off(self):
for file in ("config/d4r.ini.default", "packaging/d4r.ini"):
self.assertRegex((ROOT / file).read_text(), r"(?m)^PreferAccuracy\s*=\s*false$")
+3 -1
View File
@@ -185,11 +185,13 @@ int wmain(int argc, wchar_t** argv) {
(runtime / 'ngx').mkdir(parents=True)
for name in ('nvngx.dll', 'd4r.ini'):
(runtime / name).touch()
dlss, ngx = b'verified DLSS fixture', b'verified NGX fixture'
dlss, dlssd, ngx = b'verified DLSS fixture', b'verified DLSS RR fixture', b'verified NGX fixture'
(runtime / 'nvngx_dlss.dll').write_bytes(dlss)
(runtime / 'nvngx_dlssd.dll').write_bytes(dlssd)
(runtime / 'ngx/_nvngx.dll').write_bytes(ngx)
script = (ROOT / 'install.sh').read_text()
replacements = {'DLSS_SHA256': hashlib.sha256(dlss).hexdigest(),
'DLSSD_SHA256': hashlib.sha256(dlssd).hexdigest(),
'NGX_SHA256': hashlib.sha256(ngx).hexdigest()}
script = '\n'.join(key + '=' + replacements[key] if key in replacements else line
for line in script.splitlines() for key in [line.split('=', 1)[0]]) + '\n'
+110
View File
@@ -0,0 +1,110 @@
"""Exercise the actual optional output recorder's lifetime and fallback paths."""
from pathlib import Path
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
class FusedVramOutputTests(unittest.TestCase):
def test_output_lifetime_and_fallback(self):
shim=(ROOT/'tools/d4r_nvngx_shim.cpp').read_text()
start=shim.index('static bool record_feature_vram_output(')
end=shim.index('// --- Split frames',start)
input_start=shim.index('static bool ensure_fused_color(')
input_end=shim.index('// Records the input copies',input_start)
program=r'''
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <vector>
using VkImage=uint64_t;using VkImageView=uint64_t;using VkBuffer=uint64_t;
using VkCommandBuffer=uint64_t;using D3D12_RESOURCE_STATES=int;
constexpr int VK_SUCCESS=0,VK_NULL_HANDLE=0,VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO=1,
VK_STRUCTURE_TYPE_MEMORY_BARRIER=2,VK_IMAGE_VIEW_TYPE_2D=3,VK_FORMAT_B10G11R11_UFLOAT_PACK32=4,
VK_IMAGE_ASPECT_COLOR_BIT=1,VK_ACCESS_MEMORY_WRITE_BIT=1,VK_ACCESS_SHADER_READ_BIT=2,
VK_ACCESS_SHADER_WRITE_BIT=4,VK_ACCESS_MEMORY_READ_BIT=8,VK_PIPELINE_STAGE_ALL_COMMANDS_BIT=1,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT=2,DXGI_FORMAT_R11G11B10_FLOAT=26,
D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS=4,D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE=8,
DXGI_FORMAT_R8G8B8A8_UNORM=28,VK_FORMAT_R8G8B8A8_UNORM=5,D3D12_RESOURCE_STATE_UNORDERED_ACCESS=8,
D3D12_RESOURCE_STATE_COPY_DEST=16;
#define SUCCEEDED(value) ((value)>=0)
struct Range {int aspect,baseMip,levels,baseLayer,layers;};
struct VkImageViewCreateInfo {int sType;VkImage image=0;int viewType=0,format=0;Range subresourceRange={};};
struct VkMemoryBarrier {int sType;void* next;int src,dst;};
struct Desc {int Format=26,Flags=4;};
struct ID3D12Resource {Desc desc;int refs=0;Desc GetDesc(){return desc;}void AddRef(){++refs;}};
struct ID3D12GraphicsCommandList {};
struct Set {int pool=0;};
struct FusedColorInput {VkImage image=0;VkImageView view=0;VkBuffer buffer=0;Set set;ID3D12Resource* owner=nullptr;uint32_t inputMode=5;};
struct VramBuffer {VkBuffer buffer=1;size_t bytes=800;};
struct VramCopy {bool convert=true;unsigned width=10,height=10;VkImage image=10;};
struct Feature {bool fusedOutputAllowed=true,recordOutputFused=false,fusedColorAllowed=true;
struct {VramBuffer vram[1];struct {size_t size(){return 800;}} host[1];} inputs[3];
FusedColorInput fusedColor[3];std::vector<FusedColorInput> retiredFusedColor;struct {VramBuffer vram;} outputs[4];FusedColorInput fusedOutput[4];std::vector<FusedColorInput> retiredFusedOutput;int outputConversion=0;};
static unsigned enabled=1;
static bool g_fusedColorReady=true,viewOk=true,setOk=true,beginOk=true,endOk=true;
static int views=0,destroyed=0,frees=0,records=0,legacy=0;
static unsigned env_uint(const char*,unsigned){return enabled;}
static void logf(const char*,...) {}
static std::vector<int> transitions;
static void transition(ID3D12GraphicsCommandList*,ID3D12Resource*,int,int after){transitions.push_back(after);}
static bool record_vram_output(ID3D12GraphicsCommandList*,const VramBuffer&,const VramCopy&,int){++legacy;return true;}
struct Interop {int BeginVkCommandBufferInterop(ID3D12GraphicsCommandList*,VkCommandBuffer* c){*c=1;return beginOk?0:-1;}int EndVkCommandBufferInterop(ID3D12GraphicsCommandList*){return endOk?0:-1;}} interop;
static struct {
int device=0;Interop* interop=&::interop;
int createView(int,const VkImageViewCreateInfo*,void*,VkImageView* out){++views;if(viewOk)*out=5;return viewOk?0:-1;}
void destroyView(int,VkImageView,void*){++destroyed;}
void barrier(VkCommandBuffer,int,int,int,int,const VkMemoryBarrier*,int,void*,int,void*){}
} g_vk;
static struct {
bool set(Set& s,VkImageView,int,VkBuffer,size_t,bool){s.pool=1;return setOk;}
void free(Set& s){++frees;s={};}
void record(VkCommandBuffer,const Set&,unsigned,unsigned){++records;}
} g_fusedColorStage;
''' + shim[input_start:input_end] + shim[start:end] + r'''
int main() {
ID3D12GraphicsCommandList list;ID3D12Resource output,changed;VramCopy copy;Feature f;
enabled=false;assert(record_feature_vram_output(f,0,&list,&output,8,copy));assert(legacy==1 && views==0 && records==0);
enabled=true;assert(record_feature_vram_output(f,0,&list,&output,8,copy));assert(output.refs==1 && views==1 && records==1 && legacy==1);
assert(record_feature_vram_output(f,0,&list,&output,8,copy));assert(output.refs==1 && views==1 && records==2);
assert(record_feature_vram_output(f,0,&list,&changed,8,copy));assert(changed.refs==1 && output.refs==1 && f.retiredFusedOutput.size()==1);
Feature missingUav;ID3D12Resource noUav;noUav.desc.Flags=0;int old=legacy;
assert(record_feature_vram_output(missingUav,0,&list,&noUav,8,copy));assert(legacy==old+1 && !noUav.refs);
Feature small;small.outputs[0].vram.bytes=799;old=legacy;
assert(record_feature_vram_output(small,0,&list,&output,8,copy));assert(legacy==old+1);
Feature packed;copy.convert=false;old=legacy;
assert(record_feature_vram_output(packed,0,&list,&output,8,copy));assert(legacy==old+1);copy.convert=true;
Feature noView;viewOk=false;old=legacy;
assert(record_feature_vram_output(noView,0,&list,&output,8,copy));assert(legacy==old+1 && !noView.fusedOutputAllowed);viewOk=true;
Feature noSet;setOk=false;int released=destroyed;old=legacy;
assert(record_feature_vram_output(noSet,0,&list,&output,8,copy));assert(legacy==old+1 && destroyed==released+1 && !noSet.fusedOutputAllowed);setOk=true;
Feature noBegin;beginOk=false;old=legacy;int oldRecords=records;
assert(record_feature_vram_output(noBegin,0,&list,&output,8,copy));assert(legacy==old+1 && records==oldRecords && !noBegin.fusedOutputAllowed);beginOk=true;
Feature noEnd;endOk=false;old=legacy;
assert(record_feature_vram_output(noEnd,0,&list,&output,8,copy));assert(legacy==old+1 && !noEnd.fusedOutputAllowed);endOk=true;
Feature bounded;bounded.retiredFusedOutput.resize(64);old=legacy;int oldViews=views;
assert(record_feature_vram_output(bounded,0,&list,&output,8,copy));assert(legacy==old+1 && views==oldViews && !bounded.fusedOutputAllowed);
enabled=2;Feature alternating;old=legacy;
assert(record_feature_vram_output(alternating,0,&list,&output,8,copy,0));assert(legacy==old+1 && !alternating.recordOutputFused);
assert(record_feature_vram_output(alternating,0,&list,&output,8,copy,300));assert(legacy==old+1 && alternating.recordOutputFused);
assert(record_feature_vram_output(alternating,0,&list,&output,8,copy,600));assert(legacy==old+2 && !alternating.recordOutputFused);
enabled=1;Feature input;ID3D12Resource r11;
assert(ensure_fused_color(input,0,&r11,copy,0));assert(input.fusedColor[0].inputMode==1 && r11.refs==1);
int inputViews=views;assert(ensure_fused_color(input,0,&r11,copy,0));assert(views==inputViews && r11.refs==1);
enabled=2;assert(!ensure_fused_color(input,0,&r11,copy,300));assert(ensure_fused_color(input,0,&r11,copy,600));
assert(!ensure_fused_color(input,0,&r11,copy,1200));assert(views==inputViews);
enabled=1;ID3D12Resource rgba8;rgba8.desc.Format=28;Feature rgbaInput;
assert(ensure_fused_color(rgbaInput,0,&rgba8,copy,0));assert(rgbaInput.fusedColor[0].inputMode==5);
ID3D12Resource denied;denied.desc.Flags=8;assert(!ensure_fused_color(input,0,&denied,copy));
Feature limitedInput;limitedInput.retiredFusedColor.resize(64);
assert(!ensure_fused_color(limitedInput,0,&r11,copy) && !limitedInput.fusedColorAllowed);
assert(transitions.back()==8); // every route restores caller's output state
}
'''
with tempfile.TemporaryDirectory() as temp:
runner=Path(temp)/'fused-output'
subprocess.run(['c++','-std=c++17','-Wall','-Wextra','-Werror','-Wno-missing-field-initializers',
'-x','c++','-','-o',str(runner)],input=program,text=True,check=True,capture_output=True)
subprocess.run([str(runner)],check=True,timeout=5)
+13
View File
@@ -12,6 +12,7 @@ class MotionGeometryTests(unittest.TestCase):
source = r'''
#include "d4r_motion_dilation.h"
#include <cassert>
#include <initializer_list>
int main() {
D4rMotionDilationParams p = {};
p.render_width = 854; p.render_height = 480;
@@ -21,6 +22,18 @@ int main() {
assert(d4r_motion_dilation_geometry(&low, 0xb, 2560, 1440));
assert(low.motion_width == 854 && low.motion_height == 480);
assert(low.motion_pitch == 4096 && low.jitter_x == 0 && low.jitter_y == 0);
for (unsigned radius : {1u, 2u}) {
low.radius = radius;
assert(d4r_motion_dilation_lds_eligible(&low));
auto fallback = low; fallback.jitter_x = 0.25f;
assert(!d4r_motion_dilation_lds_eligible(&fallback));
fallback = low; ++fallback.motion_height;
assert(!d4r_motion_dilation_lds_eligible(&fallback));
fallback = low; ++fallback.motion_width;
assert(!d4r_motion_dilation_lds_eligible(&fallback));
}
low.radius = 0; assert(!d4r_motion_dilation_lds_eligible(&low));
low.radius = 3; assert(!d4r_motion_dilation_lds_eligible(&low));
low = p;
assert(d4r_motion_dilation_geometry(&low, 0x4b, 2560, 1440));
low = p; low.motion_width = 853;
+149
View File
@@ -121,3 +121,152 @@ class MotionKernelGpuTests(unittest.TestCase):
_, 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))
@unittest.skipUnless(os.environ.get('D4R_RUN_GPU_TESTS') == '1', 'opt-in GPU test')
class MotionLdsKernelGpuTests(unittest.TestCase):
def run_kernel(self, name, width, height, stride, allocation_height, depth, motion, radius, reverse):
runner = os.environ['D4R_DUMP_RUNNER']
kernel = ROOT / 'build/wine-nvcuda/generated' / (os.environ.get('D4R_GPU_ARCH', 'gfx1101') + '.hsaco')
# Guard the tail beyond the allocation too. Generic output is pre-copied,
# matching the bridge's original full-plane copy followed by dilation.
guard = [0xA5A5A5A5] * 32
original = motion + guard
arrays = [struct.pack(f'<{len(depth)}f', *depth),
struct.pack(f'<{len(original)}I', *original),
struct.pack(f'<{len(original)}I', *(original if name == 'd4r_motion_dilate' else [0xA5A5A5A5]*len(original)))]
addresses = (0x100000000, 0x200000000, 0x300000000)
args = struct.pack('<5Q4I2f2I', *addresses, stride*4, stride*4,
width, height, width, height, 0., 0., radius, reverse)
if name.startswith('d4r_motion_dilate_lds'):
args += struct.pack('<I', allocation_height)
grid = f'{(stride+15)//16} {(allocation_height+15)//16} 1 256 1 1 0'
else:
grid = f'{(width*height+127)//128} 1 1 128 1 1 0'
with tempfile.TemporaryDirectory() as t:
directory = Path(t); output = directory/'out'; output.mkdir()
(directory/'args.bin').write_bytes(args)
manifest = [f'kernel {name}', f'launch {grid}', f'args {len(args)}']
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), name, str(directory), str(output), '1'],
check=True, capture_output=True, timeout=30)
for i in (0, 1):
self.assertEqual((output/f'alloc-{i}.bin').read_bytes(), arrays[i], 'input mutated')
result = (output/'alloc-2.bin').read_bytes()
self.assertEqual(result[-128:], struct.pack('<32I', *guard), 'tail guard overwritten')
values = struct.unpack(f'<{len(original)}I', result)
for y in range(allocation_height):
for x in range(stride):
if x >= width or y >= height:
self.assertEqual(values[y*stride+x], motion[y*stride+x], 'padding changed')
return result
def test_image_edges_block_edges_and_lexicographic_sky_ties(self):
width, height, stride, allocation_height = 35, 33, 48, 36
# Equal-distance sources straddle x=16/y=16 tile boundaries. Include
# corners, circle boundary, and same-x/different-y tie candidates.
source_sets = [
[(0, 0), (34, 32), (13, 16), (19, 16), (8, 31)],
[(0, 0), (34, 32), (16, 13), (16, 19), (8, 31)],
]
for sources in source_sets:
for reverse in (False, True):
for radius in (1, 2):
with self.subTest(reverse=reverse, radius=radius, sources=sources):
depth = [float('nan')] * (stride*allocation_height)
motion = list(range(stride*allocation_height))
for y in range(height):
for x in range(width):
depth[y*stride+x] = 0. if reverse else 1.
for x, y in sources:
depth[y*stride+x] = .9 if reverse else .1
output = self.run_kernel('d4r_motion_dilate_lds', width, height, stride, allocation_height,
depth, motion, radius, reverse)
values = struct.unpack(f'<{len(output)//4}I', output)
for y in range(height):
for x in range(width):
nearby = sorted(((x-sx)**2+(y-sy)**2, sx, sy) for sx, sy in sources)
expected = motion[y*stride+x]
if nearby[0][0] <= 64:
_, sx, sy = nearby[0]
expected = motion[sy*stride+sx]
self.assertEqual(values[y*stride+x], expected, (x, y))
def test_randomized_bit_exact_generic_equivalence(self):
import random
rng = random.Random(0xD4A)
for width, height in ((1, 1), (2, 3), (15, 17), (17, 31), (33, 19), (65, 37)):
stride, allocation_height = ((width+7)//8+1)*8, height+3
for scene in ('empty', 'sparse', 'dense', 'special'):
forward = [float('nan')] * (stride*allocation_height)
for y in range(height):
for x in range(width):
d = 1.
if scene == 'dense' or (scene == 'sparse' and rng.random() < .12):
d = rng.choice((.1, .5, .9, .99))
elif scene == 'special':
d = rng.choice((0., 1., 1.e-7, 1.-1.e-7, float('nan'), float('inf'), -float('inf'), .98, .99999994))
forward[y*stride+x] = d
motion = [rng.getrandbits(32) for _ in forward]
for reverse in (False, True):
depth = [1.-d for d in forward] if reverse else forward
for radius in (1, 2):
with self.subTest(size=(width, height), scene=scene, reverse=reverse, radius=radius):
old = self.run_kernel('d4r_motion_dilate', width, height, stride, allocation_height,
depth, motion, radius, reverse)
new = self.run_kernel('d4r_motion_dilate_lds', width, height, stride, allocation_height,
depth, motion, radius, reverse)
self.assertEqual(old, new, 'LDS must have zero mismatched bytes')
def test_thin_sky_gate_matches_global_run_reference(self):
# Wide roof plus a long pole, horizontal wire, diagonal and a short tip.
# Moving the shapes across x/y=16 exercises the extra morphology halo.
for shift in (0, 3):
width, height, stride, ah = 47, 39, 56, 42
foreground = {(x, y) for y in range(24, 39) for x in range(20+shift, 47)}
foreground |= {(8+shift, y) for y in range(2, 29)}
foreground |= {(x, 8+shift) for x in range(2, 38)}
foreground |= {(x, x//2+12) for x in range(2, 20)}
def short_run(x, y, dx, dy):
length = 1
for sign in (-1, 1):
step = 1
while (x+sign*step*dx, y+sign*step*dy) in foreground:
length += 1
step += 1
return length <= 3
eligible = [(x, y) for x, y in foreground
if short_run(x,y,1,0) or short_run(x,y,0,1)]
self.assertIn((8+shift, 18), eligible, 'long pole must survive')
self.assertIn((18, 8+shift), eligible, 'horizontal wire must survive')
self.assertNotIn((30+shift, 28), eligible, 'broad roof must be rejected')
for reverse in (False, True):
for radius in (1, 2):
with self.subTest(shift=shift, reverse=reverse, radius=radius):
depth = [float('nan')]*(stride*ah)
motion = list(range(stride*ah))
for y in range(height):
for x in range(width):
depth[y*stride+x] = (.9 if reverse else .1) if (x,y) in foreground else (0. if reverse else 1.)
out = self.run_kernel('d4r_motion_dilate_lds_thin', width, height, stride, ah,
depth, motion, radius, reverse)
values = struct.unpack(f'<{len(out)//4}I',out)
for y in range(height):
for x in range(width):
bx, by = x, y
# Opaque dilation is deliberately retained even where sky fill is gated.
if (x,y) not in foreground:
found = False
for dy in range(-radius,radius+1):
for dx in range(-radius,radius+1):
nx, ny = max(0,min(width-1,x+dx)), max(0,min(height-1,y+dy))
if not found and (nx,ny) in foreground:
bx, by = nx, ny
found = True
near = sorted(((x-sx)**2+(y-sy)**2,sx,sy) for sx,sy in eligible)
if near and near[0][0] <= 64:
_, bx, by = near[0]
self.assertEqual(values[y*stride+x], motion[by*stride+bx], (x,y))
+75
View File
@@ -0,0 +1,75 @@
"""Dilation must not enter GPU input waits while lazy resources can synchronize."""
from pathlib import Path
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
class MotionSubmissionTests(unittest.TestCase):
def test_gpu_wait_requires_a_warm_compatible_lds_slot(self):
source = (ROOT / 'tools/d4r_nvngx_shim.cpp').read_text()
begin = source.index('static bool motion_dilation_parameters(')
end = source.index('static CudaObject prepare_dilated_motion(', begin)
program = r'''
#include "d4r_motion_dilation.h"
#include <cassert>
#include <cmath>
#include <cstddef>
using CudaObject = uint64_t;
static unsigned radius = 1;
static unsigned env_uint(const char*, unsigned) { return radius; }
struct HostPlane { unsigned width=20, height=20; size_t rowBytes=128; size_t size() const { return rowBytes*height; } };
struct InputSlot {
HostPlane host[3];
struct { uint64_t device=1; } vram[3];
uint64_t dilatedMotion=3, dilatedTexture=4;
size_t dilatedBytes=2560, dilatedPitch=128;
unsigned dilatedWidth=20, dilatedHeight=20;
bool dilationEnqueued=false;
};
struct Feature { unsigned flags=2, outWidth=40, outHeight=40; bool linearInputs=true; };
struct FrameParams {
unsigned renderWidth=17, renderHeight=19;
float jitterX=.25f, jitterY=-.25f;
bool vram=true;
unsigned colorBaseX=0, colorBaseY=0, depthBaseX=0, depthBaseY=0,
mvBaseX=0, mvBaseY=0, outputBaseX=0, outputBaseY=0;
};
struct { struct { void* dilateMotion=(void*)1; void* dilateMotionPadded=(void*)1; } cu; } g;
''' + source[begin:end] + r'''
int main() {
Feature f; InputSlot slot; FrameParams p;
assert(!motion_dilation_gpu_wait_ready(f,slot,p));
slot.dilationEnqueued=true;
assert(motion_dilation_gpu_wait_ready(f,slot,p));
radius=2; assert(motion_dilation_gpu_wait_ready(f,slot,p));
auto changed=slot; changed.host[2].rowBytes+=4;
assert(!motion_dilation_gpu_wait_ready(f,changed,p));
changed=slot; ++changed.host[2].width;
assert(!motion_dilation_gpu_wait_ready(f,changed,p));
changed=slot; ++changed.host[2].height;
assert(!motion_dilation_gpu_wait_ready(f,changed,p));
changed=slot; changed.dilatedTexture=0;
assert(!motion_dilation_gpu_wait_ready(f,changed,p));
changed=slot; changed.dilatedBytes=0;
assert(!motion_dilation_gpu_wait_ready(f,changed,p));
g.cu.dilateMotionPadded=nullptr;
assert(!motion_dilation_gpu_wait_ready(f,slot,p));
g.cu.dilateMotionPadded=(void*)1;
p.depthBaseX=1; assert(!motion_dilation_gpu_wait_ready(f,slot,p));
p.depthBaseX=0; f.flags=6; assert(!motion_dilation_gpu_wait_ready(f,slot,p));
f.flags=0; f.outWidth=20; f.outHeight=20;
assert(!motion_dilation_gpu_wait_ready(f,slot,p)); // mapped display-res fallback
f.flags=2; p.renderWidth=21;
assert(!motion_dilation_gpu_wait_ready(f,slot,p)); // invalid allocation
radius=0; assert(motion_dilation_gpu_wait_ready(f,slot,p)); // dilation off
}
'''
with tempfile.TemporaryDirectory() as temporary:
runner = Path(temporary)/'submission'
subprocess.run(['c++', '-std=c++17', '-Wall', '-Wextra', '-Werror', '-I', str(ROOT/'tools'),
'-x', 'c++', '-', '-o', str(runner)], input=program, text=True,
check=True, capture_output=True)
subprocess.run([str(runner)], check=True, timeout=5)
+87
View File
@@ -0,0 +1,87 @@
"""Jitter-aligned native 3x3 motion without the extended halo."""
import math
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 NativeAlignedGpuTests(unittest.TestCase):
def test_jitter_nearest_ties_partial_tiles_and_padding(self):
runner = os.environ['D4R_DUMP_RUNNER']
kernel = Path(os.environ.get('D4R_NATIVE_TEST_KERNEL', str(ROOT/'build/wine-nvcuda/generated'/
(os.environ.get('D4R_GPU_ARCH', 'gfx1101')+'.hsaco'))))
w, h, rw, rh = 33, 35, 11, 12
dp, gp, op = w+3, w+5, w+7
sentinel = 0xa5a5a5a5
f32 = lambda x: struct.unpack('<f', struct.pack('<f', x))[0]
depth = [f32(.1+((x*3+y*7)%5)*.1) for y in range(h) for x in range(w)]
geometry = [struct.unpack('<I', struct.pack('<2e', .001*x, .001*y))[0] for y in range(h) for x in range(w)]
for jx, jy in ((0., 0.), (.25, -.25), (-.5, .5), (1., -1.)):
with self.subTest(jitter=(jx, jy)), tempfile.TemporaryDirectory() as tmp:
d = Path(tmp); out = d/'out'; out.mkdir()
padded_d = [float('nan')]*(dp*h)
padded_g = [sentinel]*(gp*h)
expected = [sentinel]*(op*h)
for y in range(h):
for x in range(w):
padded_d[y*dp+x] = depth[y*w+x]
padded_g[y*gp+x] = geometry[y*w+x]
cx = min(w-1, max(0, math.floor(f32(f32(x+.5)+f32(f32(jx*w)/rw)))))
cy = min(h-1, max(0, math.floor(f32(f32(y+.5)+f32(f32(jy*h)/rh)))))
best, bx, by = depth[cy*w+cx], cx, cy
for dy in (-1, 0, 1):
for dx in (-1, 0, 1):
nx, ny = cx+dx, cy+dy
if 0 <= nx < w and 0 <= ny < h and depth[ny*w+nx] < best:
best, bx, by = depth[ny*w+nx], nx, ny
expected[y*op+x] = geometry[by*w+bx]
arrays = [struct.pack('<'+'f'*len(padded_d), *padded_d),
struct.pack('<'+'I'*len(padded_g), *padded_g), bytes(1136), bytes(64),
struct.pack('<'+'I'*(op*h), *([sentinel]*(op*h)))]
addresses = [(i+1)*0x100000000 for i in range(5)]
args = struct.pack('<8Q4I2f', *addresses, dp*4, gp*4, op*4, w, h, rw, rh, jx, jy)
(d/'args.bin').write_bytes(args)
manifest = ['kernel d4r_native_motion_compose_aligned', 'launch 3 3 1 256 1 1 0', 'args 88']
for i, data in enumerate(arrays):
(d/f'alloc-{i}.bin').write_bytes(data)
manifest += [f'alloc {i} {hex(addresses[i])} {len(data)}', f'pointer {i*8} {i} 0']
(d/'manifest.txt').write_text('\n'.join(manifest)+'\n')
subprocess.run([runner, str(kernel), 'd4r_native_motion_compose_aligned', str(d), str(out), '1'],
check=True, capture_output=True)
for i in range(4):
self.assertEqual((out/f'alloc-{i}.bin').read_bytes(), arrays[i])
self.assertEqual((out/'alloc-4.bin').read_bytes(), struct.pack('<'+'I'*len(expected), *expected))
def test_hybrid_preserves_game_vectors_away_from_silhouettes(self):
runner = os.environ['D4R_DUMP_RUNNER']
kernel = ROOT/'build/wine-nvcuda/generated'/(os.environ.get('D4R_GPU_ARCH', 'gfx1101')+'.hsaco')
w,h,rw,rh=33,35,11,12
pitch=(w+3)*4; gp=(rw+3)*4; sentinel=0xa5a5a5a5
game=[struct.unpack('<I',struct.pack('<2e',.001*x,.001*y))[0] for y in range(rh) for x in range(rw)]
padded_game=[sentinel]*((rw+3)*rh)
for y in range(rh): padded_game[y*(rw+3):y*(rw+3)+rw]=game[y*rw:(y+1)*rw]
for depth in (1.,.5):
with self.subTest(depth=depth),tempfile.TemporaryDirectory() as tmp:
d=Path(tmp);out=d/'out';out.mkdir()
arrays=[struct.pack('<f',depth)*(w+3)*h,struct.pack('<I',0x3e003e00)*(w+3)*h,bytes(1136),bytes(64),
struct.pack('<I',sentinel)*(w+3)*h,struct.pack('<'+'I'*len(padded_game),*padded_game)]
addresses=[(i+1)*0x100000000 for i in range(6)]
args=struct.pack('<8Q4I2f2Q',*addresses[:5],pitch,pitch,pitch,w,h,rw,rh,.25,-.25,addresses[5],gp)
(d/'args.bin').write_bytes(args)
manifest=['kernel d4r_native_motion_compose_hybrid','launch 3 3 1 256 1 1 0','args 104']
for i,data in enumerate(arrays):
(d/f'alloc-{i}.bin').write_bytes(data)
manifest += [f'alloc {i} {hex(addresses[i])} {len(data)}',f'pointer {88 if i==5 else i*8} {i} 0']
(d/'manifest.txt').write_text('\n'.join(manifest)+'\n')
subprocess.run([runner,str(kernel),'d4r_native_motion_compose_hybrid',str(d),str(out),'1'],check=True,capture_output=True)
for i in (0,1,2,3,5): self.assertEqual((out/f'alloc-{i}.bin').read_bytes(),arrays[i])
expected=[sentinel]*((w+3)*h)
for y in range(h):
for x in range(w): expected[y*(w+3)+x]=game[int((y+.5)*rh/h)*rw+int((x+.5)*rw/w)]
self.assertEqual((out/'alloc-4.bin').read_bytes(),struct.pack('<'+'I'*len(expected),*expected))
+65
View File
@@ -0,0 +1,65 @@
"""Native compositor ABI and rejection of unsafe buffer contracts."""
from pathlib import Path
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
class NativeMotionTests(unittest.TestCase):
def test_buffer_ranges_and_pitches(self):
source = r'''
#include "d4r_native_motion.h"
#include <assert.h>
#include <string.h>
int main(void) {
D4rNativeMotionParams p; memset(&p,0,sizeof(p));
p.width=2560; p.height=1440; p.render_width=853; p.render_height=480;
p.depth=0x10000000; p.geometry=0x20000000; p.scene=0x30000000;
p.matrix=0x40000000; p.output=0x50000000;
p.depth_pitch=10240; p.geometry_pitch=10244; p.output_pitch=10248;
assert(sizeof(p)==80 && d4r_native_motion_parameters_valid(&p));
assert(!d4r_native_motion_parameters_valid(0));
D4rNativeMotionParams q=p;
q.output=q.geometry+4; assert(!d4r_native_motion_parameters_valid(&q));
q=p; q.output=q.scene+16; assert(!d4r_native_motion_parameters_valid(&q));
q=p; q.output=q.matrix-16; assert(!d4r_native_motion_parameters_valid(&q));
q=p; q.depth=UINT64_MAX-16; assert(!d4r_native_motion_parameters_valid(&q));
q=p; q.output_pitch=UINT64_MAX-3; assert(!d4r_native_motion_parameters_valid(&q));
q=p; q.geometry_pitch=10239; assert(!d4r_native_motion_parameters_valid(&q));
q=p; q.depth_pitch=10241; assert(!d4r_native_motion_parameters_valid(&q));
q=p; q.render_width=2561; assert(!d4r_native_motion_parameters_valid(&q));
q=p; q.height=0; assert(!d4r_native_motion_parameters_valid(&q));
q=p; q.scene=0; assert(!d4r_native_motion_parameters_valid(&q));
q=p; q.matrix+=2; assert(!d4r_native_motion_parameters_valid(&q));
q=p; q.height=1; q.render_height=1; q.output_pitch=10240; assert(d4r_native_motion_parameters_valid(&q));
/* Adjacent, disjoint ranges are valid; output padding is not written. */
q=p; q.output=q.geometry+(q.height-1)*q.geometry_pitch+q.width*4;
assert(d4r_native_motion_parameters_valid(&q));
D4rNativeMotionHybridParams hybrid={{p,0,0},0x60000000,853*4};
assert(sizeof(hybrid)==104 && d4r_native_motion_hybrid_parameters_valid(&hybrid));
D4rNativeMotionHybridParams bad=hybrid;bad.game_motion=bad.native.base.output;
assert(!d4r_native_motion_hybrid_parameters_valid(&bad));
bad=hybrid;bad.game_motion_pitch=853*4-1;assert(!d4r_native_motion_hybrid_parameters_valid(&bad));
bad=hybrid;bad.native.jitter_x=2;assert(!d4r_native_motion_hybrid_parameters_valid(&bad));
bad=hybrid;bad.game_motion=UINT64_MAX-16;assert(!d4r_native_motion_hybrid_parameters_valid(&bad));
bad=hybrid;bad.game_motion_pitch=UINT64_MAX-3;assert(!d4r_native_motion_hybrid_parameters_valid(&bad));
D4rNativeMotionPreparedParams prep={{p,0,0},0x70000000,0x80000000,1,0};
assert(sizeof(prep)==112 && d4r_native_motion_prepared_parameters_valid(&prep));
D4rNativeMotionPreparedParams no=prep;no.thin_mask=0;assert(!d4r_native_motion_prepared_parameters_valid(&no));
no=prep;no.wire_axes=no.thin_mask;assert(!d4r_native_motion_prepared_parameters_valid(&no));
no=prep;no.wire_axes=no.native.base.output;assert(!d4r_native_motion_prepared_parameters_valid(&no));
no=prep;no.thin_mask=UINT64_MAX-3;assert(!d4r_native_motion_prepared_parameters_valid(&no));
no=prep;no.wire_flags=4;assert(!d4r_native_motion_prepared_parameters_valid(&no));
no=prep;no.reserved=1;assert(!d4r_native_motion_prepared_parameters_valid(&no));
return 0;
}
'''
with tempfile.TemporaryDirectory() as tmp:
runner = Path(tmp) / "native-motion"
for compiler, language, standard in [("cc", "c", "c11"), ("c++", "c++", "c++17")]:
subprocess.run([compiler, "-x", language, "-std=" + standard, "-Wall", "-Wextra", "-Werror",
"-I", str(ROOT / "tools"), "-o", str(runner), "-"],
input=source, text=True, check=True, capture_output=True)
subprocess.run([str(runner)], check=True, timeout=5)
+81
View File
@@ -0,0 +1,81 @@
"""Execute the shim's motion routing with native readiness and NGX failures."""
from pathlib import Path
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
class NativeMotionRoutingTests(unittest.TestCase):
def test_independent_repair_and_ngx_motion(self):
source = (ROOT / 'tools/d4r_nvngx_shim.cpp').read_text()
begin = source.index(' CudaObject nativeMotion = 0;')
end = source.index(' // Ray Reconstruction: the auxiliary', begin)
block = source[begin:end]
begin = source.index(' const bool wireHistoryRequested=nativeMotion')
end = source.index(';', begin) + 1
wire = source[begin:end]
program = r'''
#include <atomic>
#include <cassert>
#include <cstdint>
#include <cstring>
using CudaObject = uint64_t;
static unsigned substitute=1, repair=1;
static bool available=true, matched=true;
static CudaObject selected=0;
static unsigned env_uint(const char* name, unsigned) {
return std::strcmp(name,"D4R_NATIVE_MV_SUBSTITUTE")==0 ? substitute : repair;
}
static void logf(const char*, ...) {}
struct Feature {
unsigned flags=0x43, cudaFlags=0x43, outWidth=2560, outHeight=1440;
void* cudaHandle=(void*)1;
};
struct Slot { bool nativeLive=true; };
struct Params { unsigned renderWidth=853,renderHeight=480; float mvScaleX=853,mvScaleY=480; };
static CudaObject compose_native_live(Feature&, Slot&, const Params&, unsigned, void*) { return available ? 42 : 0; }
static bool set_native_motion_flags(Feature& f, Params&, bool native, unsigned) {
if(matched) f.cudaFlags=native ? f.flags & ~2u : f.flags;
return matched;
}
static CudaObject prepare_dilated_motion(Feature&, Slot&, const Params&) { return 7; }
struct { struct { bool stabilizeThinWires=true; } cu; } g;
static void run(bool nativeLive, bool hasField, unsigned useNative, unsigned useRepair,
bool match, unsigned initialFlags, bool expectedOk,
CudaObject expectedMotion, bool expectedRepair) {
Feature state; auto* feature=&state; Slot slot; Params params;
slot.nativeLive=nativeLive; available=hasField; substitute=useNative;repair=useRepair;matched=match;
state.cudaFlags=initialFlags;
bool ok=true; unsigned frame=1; void* stages=nullptr;
''' + block + wire + r'''
selected=liveMotion ? liveMotion : dilatedMotion;
assert(ok==expectedOk);
assert(selected==expectedMotion);
assert(wireHistoryRequested==expectedRepair);
if(liveMotion) {
assert(params.mvScaleX==2560 && params.mvScaleY==1440);
assert(state.cudaFlags==0x41);
} else {
assert(params.mvScaleX==853 && params.mvScaleY==480);
if(ok) assert(state.cudaFlags==0x43);
}
}
int main() {
run(true,true,1,1,true,0x43,true,42,true); // old combined path
run(true,true,1,0,true,0x43,true,42,false); // native only
run(true,true,0,1,true,0x43,true,7,true); // game MVs + repair
run(true,true,0,0,true,0x43,true,7,false); // game MVs, no repair
run(false,false,1,1,true,0x43,true,7,false); // clean baseline
run(true,false,0,1,true,0x43,true,7,false); // unavailable native inputs
run(true,true,0,1,true,0x41,true,7,true); // restore low-res NGX flags
run(true,true,0,1,false,0x41,false,0,true); // failed restore must not evaluate
run(true,true,1,1,false,0x43,true,7,false); // preserve old raw-output fallback
}
'''
with tempfile.TemporaryDirectory() as temp:
runner = Path(temp) / 'routing'
subprocess.run(['c++', '-std=c++17', '-Wall', '-Wextra', '-Werror', '-x', 'c++', '-',
'-o', str(runner)], input=program, text=True, check=True, capture_output=True)
subprocess.run([str(runner)], check=True, timeout=5)
+36 -3
View File
@@ -91,7 +91,7 @@ class OptiScalerConfigTests(unittest.TestCase):
inputs.mkdir()
(repo / 'scripts').mkdir(parents=True)
for name in ('package_release.sh', 'configure_optiscaler.py', 'check_glibc_compat.sh',
'check_redistributable.py', 'private_d3d12_core.py'):
'check_redistributable.py', 'private_d3d12_core.py', 'stage_engine_kit.sh'):
shutil.copy2(ROOT / 'scripts' / name, repo / 'scripts' / name)
shutil.copytree(ROOT / 'packaging', repo / 'packaging')
for name in ('LICENSE', 'NOTICE'):
@@ -113,6 +113,18 @@ class OptiScalerConfigTests(unittest.TestCase):
path.write_bytes(data)
return path
# Exercise the actual installer-kit stager with representative source files.
# No model builder, shader compiler or GPU fixture is executed here.
for name in ('engine/compile_k.py', 'engine/test_k.py', 'engine/test_m.py',
'engine/rr/test_layer.py', 'vulkan/ptx2glsl.py'):
destination = repo / name
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(ROOT / name, destination)
fixture(repo / 'engine/weights.bin', b'must not enter the model kit')
fixture(repo / 'engine/test_unrelated.py', b'must not enter the model kit')
fixture(repo / 'build/d3d12_dlss_harness.exe')
validator = fixture(inputs / 'glslang/bin/glslangValidator', b'packaging-only compiler fixture')
# A real minimal ELF lets the package's existing GLIBC checker run.
elf = inputs / 'library.so'
subprocess.run(['cc', '-shared', '-x', 'c', '-o', str(elf), '-'],
@@ -153,7 +165,8 @@ class OptiScalerConfigTests(unittest.TestCase):
D4R_ZLUDA_DIR=str(inputs / 'zluda'), D4R_VKD3D_DIR=str(inputs / 'vkd3d'),
D4R_ROCM_RUNTIME=str(inputs / 'rocm'), D4R_OPTISCALER_LICENSE=str(license_path),
D4R_ZLUDA_SRC=str(inputs / 'source'), D4R_VKD3D_SRC=str(inputs / 'source'),
D4R_BUNDLE_DLSS=str(dlss), D4R_BUNDLE_NGX=str(core), D4R_BUNDLE_TEX=str(inputs / 'tex'))
D4R_BUNDLE_DLSS=str(dlss), D4R_BUNDLE_NGX=str(core), D4R_BUNDLE_TEX=str(inputs / 'tex'),
D4R_GLSLANG=str(inputs / 'glslang'), D4R_GLSLANG_LICENSE=str(license_path))
version = (repo / 'packaging/VERSION').read_text().strip()
configs = []
# A compiled engine model: only d4r's shaders and marker may reach a zip.
@@ -186,12 +199,32 @@ class OptiScalerConfigTests(unittest.TestCase):
self.assertEqual(archive.read('d4r.asi'), archive.read('d4r/nvngx.dll'))
self.assertIn('d4r/d4r-setup.exe', archive.namelist())
self.assertEqual('d4r/install.sh' in archive.namelist(), bundled == 'installer')
self.assertFalse(any(n.endswith('.py') for n in archive.namelist()))
if bundled != 'installer':
self.assertFalse(any(n.endswith('.py') for n in archive.namelist()))
self.assertIn(b'ASI add-on', archive.read('d4r/README.txt'))
self.assertEqual({n.split('/')[0] for n in archive.namelist()}, {'d4r.asi', 'd4r'})
self.assertIn('d4r/d3d12.dll', archive.namelist())
self.assertIn('d4r/d4r12core.dll', archive.namelist())
self.assertNotIn('d4r/d3d12core.dll', archive.namelist())
kit = 'd4r/tools/models/'
self.assertEqual('d4r/tools/build-engine-models.sh' in archive.namelist(), bundled == 'installer')
if bundled == 'installer':
self.assertEqual(archive.read('d4r/tools/build-engine-models.sh'),
(ROOT / 'packaging/build_engine_models.sh').read_bytes())
for executable in ('d4r/tools/build-engine-models.sh', kit + 'bin/glslangValidator'):
self.assertTrue((archive.getinfo(executable).external_attr >> 16) & 0o111)
for source in ('engine/compile_k.py', 'engine/test_k.py', 'engine/test_m.py',
'engine/rr/test_layer.py', 'vulkan/ptx2glsl.py'):
self.assertEqual(archive.read(kit + source), (ROOT / source).read_bytes())
self.assertEqual(archive.read(kit + 'bin/glslangValidator'), validator.read_bytes())
self.assertEqual(archive.read('d4r/licenses/glslang-LICENSE.txt'), license_path.read_bytes())
self.assertIn(kit + 'bin/d3d12_dlss_harness.exe', archive.namelist())
self.assertFalse(any(n.endswith('.py') and not n.startswith(kit)
for n in archive.namelist()))
self.assertNotIn(kit + 'engine/weights.bin', archive.namelist())
self.assertNotIn(kit + 'engine/test_unrelated.py', archive.namelist())
else:
self.assertFalse(any(n.startswith(kit) for n in archive.namelist()))
self.assertIn('d4r/nvngx.dll', archive.namelist())
self.assertNotIn('nvngx.dll', archive.namelist())
self.assertNotIn('d4r/_nvngx.dll', archive.namelist())
+21
View File
@@ -0,0 +1,21 @@
"""R11G11B10 storage conversion must match the original blit for all half inputs."""
import os
from pathlib import Path
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 OutputConversionGpuTests(unittest.TestCase):
def test_fused_r11_matches_blit(self):
with tempfile.TemporaryDirectory() as temp:
runner=Path(temp)/'conversion'
subprocess.run(['c++','-std=c++17','-O2','-Wall','-Wextra','-Werror',
'-Wno-missing-field-initializers','-I'+str(ROOT/'tools'),
'-I'+str(ROOT/'build/vulkan-staging'),
str(ROOT/'tools/vulkan_output_conversion_probe.cpp'),'-lvulkan','-o',str(runner)],
check=True,capture_output=True,text=True)
subprocess.run([str(runner)],check=True,capture_output=True,text=True,timeout=30)
+83
View File
@@ -0,0 +1,83 @@
"""Prepared geometry keeps rendered output and readable temporal state exact."""
import os
from pathlib import Path
import random
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 PreparedWireGpuTests(unittest.TestCase):
def test_prepared_matches_reference_with_poisoned_invalid_history(self):
runner = os.environ['D4R_DUMP_RUNNER']
kernel = ROOT/'build/wine-nvcuda/generated'/(os.environ.get('D4R_GPU_ARCH', 'gfx1101')+'.hsaco')
w,h,rw,rh=65,67,21,22
pitch,dp,mp,cp,idp=w*8+16,w*4+12,w*4+20,rw*8+16,rw*4+12
pixels=w*h
rng=random.Random(746)
rgba=lambda: struct.pack('<4e',rng.uniform(.05,1),rng.uniform(.05,1),rng.uniform(.05,1),.5)
current=b''.join(b''.join(rgba() for _ in range(w))+b'\xa5'*16 for _ in range(h))
source=b''.join(struct.pack('<4e',.125,.125,.125,.75)*rw+b'\xa5'*16 for _ in range(rh))
previous=bytearray(b''.join(struct.pack('<4e',.125,.125,.125,.75)*w+b'\xa5'*16 for _ in range(h)))
ages=bytes(rng.choice((0,3,4,15,16,255)) for _ in range(pixels))
previous_depth=bytearray(struct.pack('<f',.5)*pixels)
for i,age in enumerate(ages):
if age==255:
y,x=divmod(i,w)
previous[y*pitch+x*8:y*pitch+x*8+8]=struct.pack('<4H',0x7e00,0x7e00,0x7e00,0x7e00)
previous_depth[i*4:i*4+4]=struct.pack('<f',float('nan'))
for flags in (0,1,2,3):
for jitter in ((0.,0.),(.25,-.25),(-1.,1.)):
for pattern in ('sky','flat','wire'):
with self.subTest(flags=flags,jitter=jitter,pattern=pattern), tempfile.TemporaryDirectory() as tmp:
d=Path(tmp);out=d/'out';out.mkdir()
z=[1. if pattern=='sky' else .5 if pattern=='flat' or x==32 else 1.
for y in range(h) for x in range(w)]
if pattern=='wire':z[10*w+31]=float('nan')
depth=b''.join(struct.pack('<'+'f'*w,*z[y*w:(y+1)*w])+b'\xa5'*12 for y in range(h))
arrays=[current,source,depth,b''.join(bytes(w*4)+b'\xa5'*20 for _ in range(h)),
bytes(previous),ages,bytes(previous_depth),b'\xa5'*(pitch*h),
b'\xa5'*(pixels+64),b'\xa5'*(pixels*4+64),
b''.join(struct.pack('<f',.5)*rw+b'\xa5'*12 for _ in range(rh)),
b'\xa5'*(pitch*h),b'\xa5'*(pixels+64),
b'\xa5'*(((w+31)//32)*h*4+64),b'\xa5'*(pixels+64),
b'\xa5'*(mp*h),bytes(1136),bytes(64)]
addresses=[(i+1)*0x100000000 for i in range(len(arrays))]
def reset():
for i,data in enumerate(arrays):(d/f'alloc-{i}.bin').write_bytes(data)
def launch(name,args,pointers,grid):
(d/'args.bin').write_bytes(args)
manifest=['kernel '+name,'launch '+' '.join(map(str,(*grid,1,256,1,1,0))),f'args {len(args)}']
for i,data in enumerate(arrays):manifest.append(f'alloc {i} {hex(addresses[i])} {len(data)}')
manifest += [f'pointer {offset} {allocation} 0' for offset,allocation in pointers]
(d/'manifest.txt').write_text('\n'.join(manifest)+'\n')
subprocess.run([runner,str(kernel),name,str(d),str(out),'1'],check=True,capture_output=True)
for i in range(len(arrays)):(d/f'alloc-{i}.bin').write_bytes((out/f'alloc-{i}.bin').read_bytes())
args=struct.pack('<17Q5I2fI',*addresses[:12],cp,dp,mp,pitch,idp,w,h,rw,rh,1,*jitter,flags)
base_pointers=[(i*8,i) for i in range(12)]
reset();launch('d4r_wire_history',args,base_pointers,((w+15)//16,(h+15)//16))
expected={i:(out/f'alloc-{i}.bin').read_bytes() for i in (7,8,9,11)}
reset()
native=struct.pack('<8Q4I2f2Q2I',addresses[2],addresses[3],addresses[16],addresses[17],addresses[15],
dp,mp,mp,w,h,rw,rh,*jitter,addresses[13],addresses[14],flags,0)
native_pointers=[(0,2),(8,3),(16,16),(24,17),(32,15),(88,13),(96,14)]
launch('d4r_native_motion_prepare',native,native_pointers,((w+31)//32,(h+31)//32))
prepared=args+struct.pack('<2Q',addresses[12],addresses[14])
launch('d4r_wire_history_preclassified',prepared,base_pointers+[(168,12),(176,14)],((w+15)//16,(h+15)//16))
launch('d4r_wire_history_apply',prepared[:176],base_pointers+[(168,12)],((w+15)//16,(h+15)//16))
actual={i:(out/f'alloc-{i}.bin').read_bytes() for i in range(len(arrays))}
self.assertEqual(actual[0],expected[7])
self.assertEqual(actual[8],expected[8])
for i in (1,2,3,4,5,6,10,16,17):self.assertEqual(actual[i],arrays[i])
for i in range(pixels):
if expected[8][i]!=255:
y,x=divmod(i,w);offset=y*pitch+x*8
self.assertEqual(actual[11][offset:offset+8],expected[11][offset:offset+8])
self.assertEqual(actual[9][i*4:i*4+4],expected[9][i*4:i*4+4])
for y in range(h):self.assertEqual(actual[11][y*pitch+w*8:(y+1)*pitch],b'\xa5'*16)
for i,start in ((8,pixels),(9,pixels*4),(12,pixels),(13,((w+31)//32)*h*4),(14,pixels)):
self.assertEqual(actual[i][start:],b'\xa5'*64)
+48
View File
@@ -0,0 +1,48 @@
"""Keep startup, feature instances and resolution changes out of shared medians."""
import importlib.util
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[1]
spec = importlib.util.spec_from_file_location('profile_summary', ROOT/'scripts/summarize_d4r_profile.py')
summary = importlib.util.module_from_spec(spec)
spec.loader.exec_module(summary)
class ProfileSummaryTests(unittest.TestCase):
def test_warm_groups_and_unavailable_timings(self):
def sample(frame, render='853x480', reset=0, gpu='2.2'):
return (f'D4R_PROFILE frame={frame} render={render} output=2560x1440 '
f'preset=11 quality=3 flags=0x43 input_wait=cpu reset={reset} '
f'vk_total=2.8 dlss_gpu={gpu} native_motion_gpu=-1 wire_repair_gpu=-1')
create='NVSDK_NGX_D3D12_CreateFeature(feature=1)'
log='\n'.join((create, sample(1), sample(300, reset=1), sample(600),
sample(900, gpu='nan'), sample(1200, render='1706x960'),
create, sample(300)))
rows=summary.summarize(log)
self.assertEqual(len(rows), 3)
self.assertEqual([r['samples'] for r in rows], [2, 1, 1])
self.assertEqual([r['feature_instance'] for r in rows], [1, 1, 2])
self.assertEqual(rows[0]['timings_ms']['dlss_gpu']['n'], 1)
self.assertIsNone(rows[0]['timings_ms']['wire_repair_gpu'])
self.assertEqual(rows[0]['timings_ms']['vk_total']['median'], 2.8)
def test_routing_changes_get_separate_groups(self):
base = ('D4R_PROFILE frame=300 render=853x480 output=2560x1440 '
'preset=11 quality=3 flags=0x43 input_wait=cpu reset=0 '
'feature=29184 native_field=1 wire_requested=1 gpu_completion=0 ')
rows = summary.summarize(base+'native_substitute=0\n'+base+'native_substitute=1')
self.assertEqual(len(rows), 2)
self.assertEqual([r['native_substitute'] for r in rows], ['0', '1'])
def test_scene_window_and_actual_output_mode(self):
base = ('D4R_PROFILE frame=600 render=853x480 output=2560x1440 '
'preset=11 quality=3 flags=0x43 input_wait=cpu reset=0 ')
log = ('[ 10.000] '+base+'vk_total=8 vk_output_mode=transfer\n'
'[ 20.000] '+base+'vk_total=3 vk_output_mode=compute\n'
'[ 21.000] '+base+'vk_total=4 vk_output_mode=transfer\n'
'[ 30.000] '+base+'vk_total=9 vk_output_mode=compute')
rows = summary.summarize(log, since_clock=19, until_clock=22)
self.assertEqual(len(rows), 2)
self.assertEqual([r['vk_output_mode'] for r in rows], ['compute', 'transfer'])
self.assertEqual([r['timings_ms']['vk_total']['median'] for r in rows], [3, 4])
+61 -1
View File
@@ -7,7 +7,8 @@ import unittest
ROOT = Path(__file__).resolve().parents[1]
CONFIG = ROOT / "scripts" / "d4r_config.py"
RR_VARIABLES = ("D4R_RR_ENABLE", "D4R_RR_PRESET")
RR_VARIABLES = ("D4R_RR_ENABLE", "D4R_RR_PRESET", "D4R_RR_BACKEND", "D4R_FSR_RR_DLL", "D4R_FSR_RR_STABILITY_BIAS",
"D4R_FSR_RR_DETAIL")
def run_config(text, **overrides):
@@ -68,5 +69,64 @@ class RayReconstructionConfigTests(unittest.TestCase):
self.assertNotIn("D4R_RR_PRESET", decided)
class RayReconstructionBackendTests(unittest.TestCase):
"""[RayReconstruction] Backend picks the denoiser; it never changes the DLSS presets."""
def test_default_leaves_dlss_in_place(self):
code, decided = run_config("[RayReconstruction]\nEnable = true\n")
self.assertEqual(code, 0)
self.assertNotIn("D4R_RR_BACKEND", decided) # the shim's default is dlss
def test_backends_are_exported_and_independent_of_presets(self):
for backend in ("dlss", "fsr", "FSR"):
with self.subTest(backend=backend):
code, decided = run_config(f"[RayReconstruction]\nBackend = {backend}\nModel = E\n[DLSS]\nModel = K\n")
self.assertEqual(code, 0)
self.assertEqual(decided["D4R_RR_BACKEND"], backend.lower())
self.assertEqual(decided["D4R_RR_PRESET"], "5")
self.assertEqual(decided["D4R_DLSS_PRESET"], "11")
def test_unknown_backend_is_rejected(self):
for value in ("amd", "fsr4", "native"):
with self.subTest(value=value):
code, decided = run_config(f"[RayReconstruction]\nBackend = {value}\n")
self.assertNotEqual(code, 0)
self.assertNotIn("D4R_RR_BACKEND", decided)
def test_denoiser_path_and_environment_precedence(self):
code, decided = run_config("[RayReconstruction]\nBackend = fsr\nFsrDenoiser = /opt/amd/denoiser.dll\n")
self.assertEqual(code, 0)
self.assertEqual(decided["D4R_FSR_RR_DLL"], "/opt/amd/denoiser.dll")
code, decided = run_config("[RayReconstruction]\nBackend = fsr\n", D4R_RR_BACKEND="dlss")
self.assertEqual(code, 0)
self.assertNotIn("D4R_RR_BACKEND", decided)
def test_templates_keep_dlss_as_the_default(self):
import configparser
for name in ("config/d4r.ini.default", "packaging/d4r.ini"):
ini = configparser.ConfigParser(inline_comment_prefixes=(";",))
ini.read(ROOT / name)
self.assertEqual(ini.get("RayReconstruction", "Backend"), "dlss", name)
self.assertEqual(ini.get("RayReconstruction", "FsrDenoiser"), "auto", name)
self.assertEqual(ini.get("RayReconstruction", "FsrStabilityBias"), "auto", name)
self.assertEqual(ini.get("RayReconstruction", "FsrDetail"), "0", name)
def test_fsr_tuning_keys(self):
code, decided = run_config("[RayReconstruction]\nBackend = fsr\nFsrStabilityBias = auto\nFsrDetail = 0.25\n")
self.assertEqual(code, 0)
self.assertNotIn("D4R_FSR_RR_STABILITY_BIAS", decided) # auto: AMD's own default
self.assertEqual(decided["D4R_FSR_RR_DETAIL"], "0.25")
code, decided = run_config("[RayReconstruction]\nFsrStabilityBias = 0\n")
self.assertEqual(code, 0)
self.assertEqual(decided["D4R_FSR_RR_STABILITY_BIAS"], "0")
for text in ("FsrStabilityBias = -1", "FsrStabilityBias = low", "FsrDetail = 1.5", "FsrDetail = x"):
with self.subTest(text=text):
code, _ = run_config(f"[RayReconstruction]\n{text}\n")
self.assertNotEqual(code, 0)
code, decided = run_config("[RayReconstruction]\nFsrStabilityBias = 0\n", D4R_FSR_RR_STABILITY_BIAS="1")
self.assertEqual(code, 0)
self.assertNotIn("D4R_FSR_RR_STABILITY_BIAS", decided)
if __name__ == "__main__":
unittest.main()
+46
View File
@@ -0,0 +1,46 @@
"""CPU camera transforms: inversion and relative camera motion before RR dispatch."""
from pathlib import Path
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
class RrCameraTests(unittest.TestCase):
def test_inverse_and_relative_camera_translation(self):
source = r'''
#include "engine/rr_camera.h"
#include <cassert>
#include <cmath>
using namespace d4r::rr_camera;
int main() {
const Matrix identity{1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
// A dense, asymmetric matrix catches row/column and cofactor-sign mistakes.
const Matrix dense{2,1,3,4, 0,3,2,1, 1,0,4,2, 2,3,0,5};
const Matrix product = multiply(dense, inverse(dense));
for (int i = 0; i < 16; ++i) assert(std::abs(product[i] - identity[i]) < 2e-6f);
Matrix current = identity, previous = identity;
current[12] = 3; previous[12] = 2;
const auto camera = transforms(current, identity, previous, identity, 4, 2);
Matrix expectedPrevious = identity; expectedPrevious[12] = -1;
const Matrix pixels{2,0,0,0, 0,-1,0,0, 0,0,1,0, 2,1,0,1};
const Matrix expectedPixels = multiply(expectedPrevious, pixels);
for (int i = 0; i < 16; ++i) {
assert(camera.previousClip[i] == expectedPrevious[i]);
assert(camera.inverseClip[i] == identity[i]);
assert(camera.previousPixels[i] == expectedPixels[i]);
}
const Matrix inversePixelProduct = multiply(pixels, camera.inversePixels);
for (int i = 0; i < 16; ++i) assert(inversePixelProduct[i] == identity[i]);
// Reset is represented by passing the current camera as the previous camera.
const auto reset = transforms(current, identity, current, identity, 4, 2);
for (int i = 0; i < 16; ++i) assert(reset.previousClip[i] == identity[i]);
}
'''
with tempfile.TemporaryDirectory() as temporary:
runner = Path(temporary) / 'camera'
subprocess.run(['c++', '-O2', '-std=c++17', '-Wall', '-Wextra', '-Werror',
'-I', str(ROOT), '-x', 'c++', '-', '-o', str(runner)],
input=source, text=True, check=True, capture_output=True)
subprocess.run([str(runner)], check=True, timeout=10)
+34 -4
View File
@@ -1,4 +1,4 @@
"""Engine opt-in, preset-selection behavior and polling configuration precedence."""
"""Engine defaults, preset-selection behavior and polling configuration precedence."""
import configparser
import os
from pathlib import Path
@@ -10,17 +10,47 @@ ROOT = Path(__file__).resolve().parents[1]
class ShimConfigTests(unittest.TestCase):
def test_engine_is_opt_in_and_configured_by_ini(self):
def test_profile_log_interval_validation_and_environment_precedence(self):
with tempfile.TemporaryDirectory() as temporary:
ini = Path(temporary) / 'd4r.ini'
env = {key: value for key, value in os.environ.items() if not key.startswith('D4R_')}
cmd = ['python3', str(ROOT / 'scripts/d4r_config.py'), '--config', str(ini)]
for value, expected in ((None, None), ('auto', None), ('1', '1'), ('60', '60'),
('060', '60'), ('120', '120'), ('4294967295', '4294967295')):
ini.write_text('[Debug]\nProfile = true\n' +
(f'ProfileLogEvery = {value}\n' if value is not None else ''))
result = subprocess.run(cmd, env=env, text=True, capture_output=True, check=True)
self.assertIn('export D4R_PROFILE=1\n', result.stdout)
if expected is None:
self.assertNotIn('export D4R_PROFILE_LOG_EVERY=', result.stdout)
else:
self.assertIn(f'export D4R_PROFILE_LOG_EVERY={expected}\n', result.stdout)
ini.write_text('[Debug]\nProfileLogEvery = 60\n')
result = subprocess.run(cmd, env=dict(env, D4R_PROFILE_LOG_EVERY='120'),
text=True, capture_output=True, check=True)
self.assertNotIn('export D4R_PROFILE_LOG_EVERY=', result.stdout)
for value in ('0', '-1', '0.5', 'invalid', '4294967296'):
ini.write_text(f'[Debug]\nProfileLogEvery = {value}\n')
result = subprocess.run(cmd, env=env, text=True, capture_output=True)
self.assertEqual(result.returncode, 2, value)
for name in ('packaging/d4r.ini', 'config/d4r.ini.default'):
ini = configparser.ConfigParser(inline_comment_prefixes=(';',))
ini.read(ROOT / name)
self.assertFalse(ini.getboolean('Engine', 'Enabled'), name)
self.assertEqual(ini.getint('Debug', 'ProfileLogEvery'), 60, name)
def test_engine_defaults_and_ini_overrides(self):
for name, expected in (('packaging/d4r.ini', 'auto'), ('config/d4r.ini.default', 'false')):
ini = configparser.ConfigParser(inline_comment_prefixes=(';',))
ini.read(ROOT / name)
self.assertEqual(ini.get('Engine', 'Enabled'), expected, name)
self.assertEqual(ini.get('Engine', 'ModelDir'), 'auto', name)
with tempfile.TemporaryDirectory() as temporary:
ini = Path(temporary) / 'd4r.ini'
env = {key: value for key, value in os.environ.items() if not key.startswith('D4R_')}
cmd = ['python3', str(ROOT / 'scripts/d4r_config.py'), '--config', str(ini)]
for text, enabled, model in (('', '0', None), ('[Engine]\nEnabled = true\n', '1', None),
for text, enabled, model in (('', '0', None), ('[Engine]\nEnabled = auto\n', '0', None),
('[Engine]\nEnabled = false\n', '0', None),
('[Engine]\nEnabled = true\n', '1', None),
('[Engine]\nEnabled = true\nModelDir = /models/k\n', '1', '/models/k')):
ini.write_text(text)
result = subprocess.run(cmd, env=env, text=True, capture_output=True, check=True)
+42
View File
@@ -0,0 +1,42 @@
"""Conversion labels must belong to the older Vulkan query slot, not this frame."""
from pathlib import Path
import subprocess
import tempfile
import unittest
ROOT=Path(__file__).resolve().parents[1]
class VramProfileTimingTests(unittest.TestCase):
def test_older_slot_mode_labels(self):
source=(ROOT/'tools/d4r_nvngx_shim.cpp').read_text()
start=source.index('static void read_profile_timestamps(Feature& feature, uint32_t frame, FrameTiming& timing)\n{')
end=source.index('\n}\n',start)+3
program=r'''
#include <cassert>
#include <cstdint>
constexpr unsigned kOutputSlots=4;
struct FrameTiming {double vkInput=-1,vkDlssWait=-1,vkOutput=-1,vkTotal=-1;int vkOutputFused=-1,vkInputFused=-1;};
struct Feature {uint64_t values[16]={};volatile uint64_t* timestampValues=values;double timestampTicksPerMs=10;
uint8_t timestampOutputFused[4]={},timestampInputFused[4]={};bool recordOutputFused=true,recordInputFused=true;};
''' + source[start:end] + r'''
int main() {
Feature f;FrameTiming old;
f.values[0]=10;f.values[1]=20;f.values[2]=50;f.values[3]=60;
f.timestampOutputFused[0]=0;f.timestampInputFused[0]=1;
read_profile_timestamps(f,300,old); // frame 296 used transfer output, despite current mode switching on
assert(old.vkOutputFused==0 && old.vkInputFused==1 && old.vkTotal==5);
assert(!f.recordOutputFused && !f.recordInputFused && f.values[0]==0);
f.values[0]=70;f.values[1]=80;f.values[2]=110;f.values[3]=120;
f.timestampOutputFused[0]=1;f.timestampInputFused[0]=0;FrameTiming next;
read_profile_timestamps(f,304,next);
assert(next.vkOutputFused==1 && next.vkInputFused==0);
FrameTiming unavailable;read_profile_timestamps(f,308,unavailable);
assert(unavailable.vkOutputFused==-1 && unavailable.vkInputFused==-1 && unavailable.vkTotal==-1);
}
'''
with tempfile.TemporaryDirectory() as temp:
runner=Path(temp)/'timing'
subprocess.run(['c++','-std=c++17','-Wall','-Wextra','-Werror','-x','c++','-',
'-o',str(runner)],input=program,text=True,check=True,capture_output=True)
subprocess.run([str(runner)],check=True,timeout=5)
+64
View File
@@ -0,0 +1,64 @@
"""Fractional wire coverage must preserve a different foreground surface."""
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 WireBackgroundGpuTests(unittest.TestCase):
def test_sky_wire_does_not_spill_onto_other_geometry(self):
runner=os.environ['D4R_DUMP_RUNNER']
kernel=Path(os.environ.get('D4R_WIRE_TEST_KERNEL',str(ROOT/'build/wine-nvcuda/generated'/'gfx1101.hsaco')))
w,h=65,67
pitch,dp,mp=w*8+16,w*4+12,w*4+20
pixels=w*h
rgba=struct.pack('<4e',.5,.5,.5,.5)
observed=struct.pack('<4e',.125,.125,.125,.75)
current=b''.join(rgba*w+b'\xa5'*16 for _ in range(h))
color=b''.join(observed*w+b'\xa5'*16 for _ in range(h))
depth=b''.join(struct.pack('<'+'f'*w,*[
.5 if y==32 else .9 if x<=31 and y<=31 else 1. for x in range(w)])+b'\xa5'*12 for y in range(h))
motion=b''.join(bytes(w*4)+b'\xa5'*20 for _ in range(h))
input_depth=b''.join(struct.pack('<f',.5)*w+b'\xa5'*12 for _ in range(h))
for sparse in (False,True):
with self.subTest(sparse=sparse), tempfile.TemporaryDirectory() as temp:
directory=Path(temp);out=directory/'out';out.mkdir()
arrays=[current,color,depth,motion,color,b'\xff'*pixels,struct.pack('<f',1.)*pixels,
b'\xa5'*(pitch*h),b'\xa5'*(pixels+64),b'\xa5'*(pixels*4+64),
input_depth,b'\xa5'*(pitch*h)]
if sparse:arrays.append(b'\xa5'*(pixels+64))
addresses=[(i+1)*0x100000000 for i in range(len(arrays))]
args=struct.pack('<17Q5I2fI',*addresses[:12],pitch,dp,mp,pitch,dp,w,h,w,h,0,.25,.25,3)
if sparse:args+=struct.pack('<Q',addresses[12])
(directory/'args.bin').write_bytes(args)
manifest=['kernel '+('d4r_wire_history_sparse' if sparse else 'd4r_wire_history'),
'launch 5 5 1 256 1 1 0',f'args {len(args)}']
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 {168 if i==12 else i*8} {i} 0']
(directory/'manifest.txt').write_text('\n'.join(manifest)+'\n')
name='d4r_wire_history_sparse' if sparse else 'd4r_wire_history'
subprocess.run([runner,str(kernel),name,str(directory),str(out),'1'],check=True,capture_output=True)
if sparse:
for i in range(len(arrays)):(directory/f'alloc-{i}.bin').write_bytes((out/f'alloc-{i}.bin').read_bytes())
manifest[0]='kernel d4r_wire_history_apply'
(directory/'manifest.txt').write_text('\n'.join(manifest)+'\n')
subprocess.run([runner,str(kernel),'d4r_wire_history_apply',str(directory),str(out),'1'],check=True,capture_output=True)
result=(out/f'alloc-{0 if sparse else 7}.bin').read_bytes()
# The .0625 wire corner overlaps a different, thick foreground object.
self.assertEqual(result[31*pitch+31*8:31*pitch+32*8],rgba)
self.assertEqual((out/'alloc-8.bin').read_bytes()[31*w+31],255)
# The neighboring actual wire remains repairable; alpha stays untouched.
wire=result[32*pitch+32*8:32*pitch+33*8]
self.assertNotEqual(wire,rgba)
self.assertEqual(wire[6:],rgba[6:])
for i in ((1,2,3,4,5,6,10) if sparse else (0,1,2,3,4,5,6,10)):
self.assertEqual((out/f'alloc-{i}.bin').read_bytes(),arrays[i])
for y in range(h):self.assertEqual(result[y*pitch+w*8:(y+1)*pitch],b'\xa5'*16)
for i,end in ((8,pixels),(9,pixels*4)):
self.assertEqual((out/f'alloc-{i}.bin').read_bytes()[end:],b'\xa5'*64)
+54
View File
@@ -0,0 +1,54 @@
"""Sparse wire publication rejects aliased scratch, inputs and overflowing ranges."""
from pathlib import Path
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
class WireHistoryTests(unittest.TestCase):
def test_sparse_buffer_contract(self):
source = r'''
#include "d4r_wire_history.h"
#include <assert.h>
#include <string.h>
int main(void) {
D4rWireHistorySparseParams p; memset(&p,0,sizeof(p));
D4rWireHistoryParams* b=&p.base;
b->width=65;b->height=67;b->render_width=21;b->render_height=22;
b->current=0x10000000;b->color=0x20000000;b->native_depth=0x30000000;
b->motion=0x40000000;b->previous=0x50000000;b->previous_age=0x60000000;
b->previous_depth=0x70000000;b->output=0x80000000;b->output_age=0x90000000;
b->output_depth=0xa0000000;b->input_depth=0xb0000000;b->output_history=0xc0000000;
b->color_pitch=21*8;b->depth_pitch=b->motion_pitch=65*4;
b->output_pitch=65*8;b->input_depth_pitch=21*4;p.patch_mask=0xd0000000;
assert(sizeof(p)==176 && d4r_wire_history_sparse_parameters_valid(&p));
assert(!d4r_wire_history_sparse_parameters_valid(0));
D4rWireHistorySparseParams q=p;q.patch_mask=0;assert(!d4r_wire_history_sparse_parameters_valid(&q));
q=p;q.patch_mask=UINT64_MAX-16;assert(!d4r_wire_history_sparse_parameters_valid(&q));
q=p;q.patch_mask=q.base.current+8;assert(!d4r_wire_history_sparse_parameters_valid(&q));
q=p;q.patch_mask=q.base.previous_age+1;assert(!d4r_wire_history_sparse_parameters_valid(&q));
q=p;q.base.current=q.base.color;assert(!d4r_wire_history_sparse_parameters_valid(&q));
q=p;q.base.output=q.base.native_depth+4;assert(!d4r_wire_history_sparse_parameters_valid(&q));
q=p;q.base.current=q.base.output_history+8;assert(!d4r_wire_history_sparse_parameters_valid(&q));
q=p;q.base.output_pitch=UINT64_MAX-7;assert(!d4r_wire_history_sparse_parameters_valid(&q));
q=p;q.base.current=UINT64_MAX-1024;assert(!d4r_wire_history_sparse_parameters_valid(&q));
q=p;q.patch_mask=q.base.current+66*q.base.output_pitch+65*8;
assert(d4r_wire_history_sparse_parameters_valid(&q));
D4rWireHistoryPreparedParams prepared={p,0xe0000000};
assert(sizeof(prepared)==184 && d4r_wire_history_prepared_parameters_valid(&prepared));
D4rWireHistoryPreparedParams no=prepared;no.axes=0;assert(!d4r_wire_history_prepared_parameters_valid(&no));
no=prepared;no.axes=no.sparse.base.current;assert(!d4r_wire_history_prepared_parameters_valid(&no));
no=prepared;no.axes=no.sparse.patch_mask;assert(!d4r_wire_history_prepared_parameters_valid(&no));
no=prepared;no.axes=UINT64_MAX-16;assert(!d4r_wire_history_prepared_parameters_valid(&no));
return 0;
}
'''
with tempfile.TemporaryDirectory() as tmp:
runner = Path(tmp)/'wire-contract'
for compiler, language, standard in (('cc', 'c', 'c11'), ('c++', 'c++', 'c++17')):
subprocess.run([compiler, '-x', language, '-std='+standard, '-Wall', '-Wextra', '-Werror',
'-I', str(ROOT/'tools'), '-o', str(runner), '-'],
input=source, text=True, check=True, capture_output=True)
subprocess.run([str(runner)], check=True)
+88
View File
@@ -0,0 +1,88 @@
"""Opt-in GPU checks for wire color, history eligibility, padding and immutable inputs."""
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 WireHistoryGpuTests(unittest.TestCase):
def test_wire_source_history_reset_and_expiry(self):
runner = os.environ['D4R_DUMP_RUNNER']
kernel = Path(os.environ.get('D4R_WIRE_TEST_KERNEL', str(ROOT / 'build/wine-nvcuda/generated' /
(os.environ.get('D4R_GPU_ARCH', 'gfx1101') + '.hsaco'))))
w, h = 65, 67 # partial workgroups on both axes
pitch, dp, mp = w*8+16, w*4+12, w*4+20
pixels = w*h
rgba = struct.pack('<4e', .5, .5, .5, .5)
observed = struct.pack('<4e', .125, .125, .125, .75)
repaired = struct.pack('<4e', .125, .125, .125, .5)
current = b''.join(rgba*w + b'\xa5'*16 for _ in range(h))
color = b''.join(observed*w + b'\xa5'*16 for _ in range(h))
native = b''.join(struct.pack('<'+'f'*w, *[.5 if x == 32 else 1. for x in range(w)]) +
b'\xa5'*12 for _ in range(h))
motion = b''.join(b'\0'*(w*4) + b'\xa5'*20 for _ in range(h))
for sky_only in (False, True):
for conservative in (False, True):
for sparse in (False, True):
for mode in ('source', 'history', 'reset', 'expired', 'disoccluded', 'missing', 'neighbor', 'far', 'backdrop'):
for jitter in (0, -1, 1):
with self.subTest(mode=mode, jitter=jitter, conservative=conservative, sparse=sparse, sky_only=sky_only), tempfile.TemporaryDirectory() as tmp:
directory = Path(tmp); output = directory/'out'; output.mkdir()
source = mode in ('source', 'backdrop')
history = mode in ('history', 'expired', 'disoccluded', 'neighbor')
z = .999988 if mode == 'far' else .5
near = b''.join(struct.pack('<'+'f'*w, *[z if x == 32 else .9 if mode == 'backdrop' else 1. for x in range(w)]) + b'\xa5'*12 for _ in range(h))
previous = color
if mode == 'neighbor':
previous = b''.join(b''.join(observed if x == 32-jitter else struct.pack('<4e', .01, .01, .01, .75) for x in range(w)) + b'\xa5'*16 for _ in range(h))
input_depth = b''.join(struct.pack('<f', z+1e-6 if mode == 'far' else .5 if source else 1.)*w + b'\xa5'*12 for _ in range(h))
arrays = [current, color, near, motion, previous,
bytes([(4 if conservative else 16) if mode == 'expired' else (3 if conservative else 15)])*pixels,
struct.pack('<f', 0. if mode == 'disoccluded' else .5)*pixels,
b'\xa5'*(pitch*h), b'\xa5'*(pixels+64), b'\xa5'*(pixels*4+64),
input_depth, b'\xa5'*(pitch*h)]
if sparse: arrays.append(b'\xa5'*(pixels+64))
addresses = [(i+1)*0x100000000 for i in range(len(arrays))]
args = struct.pack('<17Q5I2fI', *addresses[:12], pitch, dp, mp, pitch, dp,
w, h, w, h, int(history), jitter, -jitter, int(conservative) | (2 if sky_only else 0))
if sparse: args += struct.pack('<Q', addresses[12])
(directory/'args.bin').write_bytes(args)
manifest = ['kernel '+('d4r_wire_history_sparse' if sparse else 'd4r_wire_history'), 'launch 5 5 1 256 1 1 0', f'args {len(args)}']
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 {168 if i == 12 else i*8} {i} 0']
(directory/'manifest.txt').write_text('\n'.join(manifest)+'\n')
if sparse:
subprocess.run([runner, str(kernel), 'd4r_wire_history_sparse', str(directory), str(output), '1'], check=True, capture_output=True)
for i in range(len(arrays)):
(directory/f'alloc-{i}.bin').write_bytes((output/f'alloc-{i}.bin').read_bytes())
manifest[0]='kernel d4r_wire_history_apply'
(directory/'manifest.txt').write_text('\n'.join(manifest)+'\n')
subprocess.run([runner, str(kernel), 'd4r_wire_history_apply', str(directory), str(output), '1'], check=True, capture_output=True)
else:
subprocess.run([runner, str(kernel), 'd4r_wire_history', str(directory), str(output), '1'], check=True, capture_output=True)
for i in ((1, 2, 3, 4, 5, 6, 10) if sparse else (0, 1, 2, 3, 4, 5, 6, 10)):
self.assertEqual((output/f'alloc-{i}.bin').read_bytes(), arrays[i])
expected = bytearray(current)
cache = bytearray(current)
age = bytearray(b'\xff'*pixels + b'\xa5'*64)
depth = bytearray(struct.pack('<f', 1.)*pixels + b'\xa5'*64)
for y in range(16, h-16):
if mode == 'backdrop' and sky_only: continue
x = 32-jitter
offset, index = y*pitch+x*8, y*w+x
depth[index*4:index*4+4] = struct.pack('<f', z)
if mode in ('source', 'history', 'neighbor', 'backdrop') or (mode == 'far' and not conservative):
value = .01 if mode == 'neighbor' and not conservative else .125
expected[offset:offset+8] = struct.pack('<4e', value, value, value, .5)
cache[offset:offset+8] = struct.pack('<4e', value, value, value, .75)
age[index] = 0 if source or mode == 'far' else 4 if conservative else 16
self.assertEqual((output/f'alloc-{0 if sparse else 7}.bin').read_bytes(), expected)
self.assertEqual((output/'alloc-8.bin').read_bytes(), age)
self.assertEqual((output/'alloc-9.bin').read_bytes(), depth)
self.assertEqual((output/'alloc-11.bin').read_bytes(), cache)