use cubecl::prelude::*;
use super::helpers::{
BLK_STEP,
R,
RingFixture,
deterministic_texture,
make_client,
noisy_ring,
planted_ring,
};
use crate::collab::geometry::{fused_cubes_x, ref_count, refs_along};
use crate::collab::kernels::aggregate::{cross_frame_accum_scale, kaiser_window, weight_scale};
use crate::collab::kernels::fused::collab_fused;
use crate::collab::kernels::transforms::dct_noise_profile;
use crate::collab::{PATCH_AREA, PATCH_SIZE, STEP, needs_warp_uniform_search};
pub(super) const BLKSIZE: u32 = 16;
const REFINE: u32 = 2;
const K_MAX: u32 = 8;
const SPATIAL_RADIUS: u32 = 4;
struct Knobs {
c_min: f32,
k_max: u32,
sigma: f32,
lambda_ht: f32,
mismatch_scale: f32,
use_member_sigma: bool,
refine: u32,
noise_floor: f32,
blksize: u32,
}
impl Default for Knobs {
fn default() -> Self {
Knobs {
c_min: 0.05,
k_max: K_MAX,
sigma: 0.02,
lambda_ht: 2.7,
mismatch_scale: 1.0,
use_member_sigma: false,
refine: REFINE,
noise_floor: 0.0,
blksize: BLKSIZE,
}
}
}
struct FusedRun {
wsum: Vec<i32>,
group_weight: Vec<f32>,
pixels: usize,
}
impl FusedRun {
fn frame_weight_sum(&self, slot: u32) -> i64 {
let start = slot as usize * self.pixels;
self.wsum[start..start + self.pixels]
.iter()
.map(|&v| v as i64)
.sum()
}
fn total_weight(&self) -> i64 {
self.wsum.iter().map(|&v| v as i64).sum()
}
}
fn run_fused_over(fx: &RingFixture, k: Knobs) -> FusedRun {
let client = make_client();
let w = fx.width;
let h = fx.height;
let pixels = (w * h) as usize;
let frames = fx.ring.len() / pixels;
let refs = ref_count(w, h);
let refs_x = refs_along(w);
let profile = dct_noise_profile(0.0);
let ring_buf = client.create_from_slice(f32::as_bytes(&fx.ring));
let mv_buf = client.create_from_slice(i32::as_bytes(&fx.mv_field));
let conf_buf = client.create_from_slice(f32::as_bytes(&fx.confidence));
let slots_buf = client.create_from_slice(u32::as_bytes(&fx.neighbour_slots));
let sigma_buf = client.create_from_slice(f32::as_bytes(&[k.sigma]));
let profile_buf = client.create_from_slice(f32::as_bytes(&profile));
let kaiser_buf = client.create_from_slice(f32::as_bytes(&kaiser_window(0.0)));
let accum = client.create_from_slice(i32::as_bytes(&vec![0i32; pixels * frames]));
let wsum = client.create_from_slice(i32::as_bytes(&vec![0i32; pixels * frames]));
let group_weight = client.empty(refs * size_of::<f32>());
unsafe {
collab_fused::launch_unchecked::<R>(
&client,
CubeCount::new_2d(fused_cubes_x(w), refs_along(h)),
CubeDim::new_1d(64),
1usize,
ArrayArg::from_raw_parts(ring_buf, fx.ring.len()),
ArrayArg::from_raw_parts(mv_buf, fx.mv_field.len()),
ArrayArg::from_raw_parts(conf_buf, fx.confidence.len()),
ArrayArg::from_raw_parts(slots_buf, fx.neighbour_slots.len()),
ArrayArg::from_raw_parts(sigma_buf, 1),
ArrayArg::from_raw_parts(profile_buf, 8),
ArrayArg::from_raw_parts(kaiser_buf, PATCH_SIZE as usize),
ArrayArg::from_raw_parts(accum, pixels * frames),
ArrayArg::from_raw_parts(wsum.clone(), pixels * frames),
ArrayArg::from_raw_parts(group_weight.clone(), refs),
fx.centre_slot,
k.noise_floor,
k.c_min,
k.mismatch_scale * k.mismatch_scale,
k.lambda_ht,
weight_scale(k.sigma, &profile),
cross_frame_accum_scale(SPATIAL_RADIUS, fx.radius),
k.use_member_sigma,
needs_warp_uniform_search(&client),
fx.radius,
k.refine,
fx.mv_stride,
fx.conf_stride,
BLK_STEP,
k.blksize,
fx.blocks_x,
fx.blocks_y,
w,
h,
1u32,
k.k_max,
1u32,
SPATIAL_RADIUS,
refs_x,
);
}
let wsum_bytes = client.read_one(wsum).expect("wsum readback failed");
let weight_bytes = client
.read_one(group_weight)
.expect("group_weight readback failed");
FusedRun {
wsum: i32::from_bytes(&wsum_bytes)[..pixels * frames].to_vec(),
group_weight: f32::from_bytes(&weight_bytes)[..refs].to_vec(),
pixels,
}
}
#[test]
fn temporal_members_are_found_at_the_mv_prediction() {
let (w, h) = (96u32, 96u32);
let radius = 2u32;
let ref_pos = (64u32, 64u32);
let patch = deterministic_texture(7);
let predicted = planted_ring(w, h, radius, ref_pos, 3, &patch, 0.2, |_| 1.0);
let mut blind = planted_ring(w, h, radius, ref_pos, 3, &patch, 0.2, |_| 1.0);
blind.mv_field.fill(0);
let refs_x = refs_along(w);
let ref_idx = ((ref_pos.1 / STEP) * refs_x + (ref_pos.0 / STEP)) as usize;
let with_prediction = run_fused_over(&predicted, Knobs::default()).group_weight[ref_idx];
let without = run_fused_over(&blind, Knobs::default()).group_weight[ref_idx];
assert!(
with_prediction > without * 1.5,
"expected the group at {ref_pos:?} to agree far better when the motion field points at \
the planted copies, got weight {with_prediction} with the prediction and {without} \
with a zeroed field"
);
}
#[test]
fn low_confidence_neighbours_contribute_no_candidates() {
let (w, h) = (96u32, 96u32);
let radius = 2u32;
let ref_pos = (64u32, 64u32);
let patch = deterministic_texture(11);
let fx = planted_ring(w, h, radius, ref_pos, 3, &patch, 0.2, |k| {
if k > 0 { 0.0 } else { 1.0 }
});
let run = run_fused_over(&fx, Knobs::default());
for k in -(radius as i32)..=(radius as i32) {
let slot = (k + radius as i32) as u32;
let weight = run.frame_weight_sum(slot);
if k > 0 {
assert_eq!(
weight, 0,
"slot {slot} (k={k}) is gated by c_min, so it must receive no scatter at all"
);
} else {
assert!(
weight > 0,
"slot {slot} (k={k}) is ungated, so it must receive members"
);
}
}
}
#[test]
fn no_admission_gate_means_the_group_always_fills() {
let (w, h) = (64u32, 64u32);
let radius = 2u32;
let fx = noisy_ring(w, h, radius, 1.0);
let full = run_fused_over(
&fx,
Knobs {
lambda_ht: 1.0e6,
..Knobs::default()
},
);
let single = run_fused_over(
&fx,
Knobs {
k_max: 1,
lambda_ht: 1.0e6,
..Knobs::default()
},
);
let one = single.total_weight();
assert!(one > 0, "the k_max = 1 run deposited no weight at all");
assert_eq!(
full.total_weight(),
one * K_MAX as i64,
"expected every group to carry {K_MAX} members, so {K_MAX}x the weight the \
one-member run deposited"
);
}
#[test]
fn a_temporal_member_carries_its_own_match_distance_as_variance() {
use crate::collab::kernels::transforms::haar_variance_ladder;
let (w, h) = (96u32, 96u32);
let radius = 2u32;
let ref_pos = (64u32, 64u32);
let patch = deterministic_texture(5);
let sigma = 0.02f32;
let refs_x = refs_along(w);
let ref_idx = ((ref_pos.1 / STEP) * refs_x + (ref_pos.0 / STEP)) as usize;
for (d, scale) in [(0.0f32, 1.0f32), (0.05, 1.0), (0.05, 2.0), (0.1, 1.0)] {
let mut fx = planted_ring(w, h, radius, ref_pos, 3, &patch, 0.2, |_| 1.0);
let pixels = (w * h) as usize;
for slot in 0..(2 * radius + 1) {
if slot == fx.centre_slot {
continue;
}
let frame = &mut fx.ring[slot as usize * pixels..(slot as usize + 1) * pixels];
for v in frame.iter_mut() {
if *v > 0.5 {
*v += d;
}
}
}
let run = run_fused_over(
&fx,
Knobs {
sigma,
lambda_ht: 1.0e6,
mismatch_scale: scale,
use_member_sigma: true,
refine: 0,
..Knobs::default()
},
);
let base = sigma * sigma;
let mut v = [base; 8];
for m in v.iter_mut().take(5).skip(1) {
*m = base + d * d * scale * scale;
}
let expected = 1.0 / haar_variance_ladder(&v, 8)[0];
let got = run.group_weight[ref_idx];
assert!(
(got - expected).abs() <= expected * 1e-3,
"d={d} scale={scale}: expected group weight {expected}, got {got}"
);
}
}
#[test]
fn a_noise_floor_lowers_a_temporal_members_variance_by_the_expected_amount() {
use crate::collab::kernels::transforms::haar_variance_ladder;
let (w, h) = (96u32, 96u32);
let radius = 2u32;
let ref_pos = (64u32, 64u32);
let patch = deterministic_texture(5);
let sigma = 0.02f32;
let d = 0.1f32;
let refs_x = refs_along(w);
let ref_idx = ((ref_pos.1 / STEP) * refs_x + (ref_pos.0 / STEP)) as usize;
let mut fx = planted_ring(w, h, radius, ref_pos, 3, &patch, 0.2, |_| 1.0);
let pixels = (w * h) as usize;
for slot in 0..(2 * radius + 1) {
if slot == fx.centre_slot {
continue;
}
let frame = &mut fx.ring[slot as usize * pixels..(slot as usize + 1) * pixels];
for v in frame.iter_mut() {
if *v > 0.5 {
*v += d;
}
}
}
let raw_distance = 3.0 * PATCH_AREA as f32 * d * d;
let noise_floor = raw_distance / 2.0;
let run = run_fused_over(
&fx,
Knobs {
sigma,
lambda_ht: 1.0e6,
use_member_sigma: true,
refine: 0,
noise_floor,
..Knobs::default()
},
);
let base = sigma * sigma;
let excess = (raw_distance - noise_floor).max(0.0);
let member_variance = excess / (3.0 * PATCH_AREA as f32);
let mut v = [base; 8];
for m in v.iter_mut().take(5).skip(1) {
*m = base + member_variance;
}
let expected = 1.0 / haar_variance_ladder(&v, 8)[0];
let got = run.group_weight[ref_idx];
assert!(
(got - expected).abs() <= expected * 1e-3,
"noise_floor={noise_floor}: expected group weight {expected} (member variance \
{member_variance}), got {got}"
);
assert!(
member_variance < d * d,
"expected the floor to lower the member variance below the zero-floor value {}, got {}",
d * d,
member_variance
);
}
fn set_block_mv(fx: &mut RingFixture, t: u32, bx: u32, by: u32, mv: [i32; 2]) {
let block = by * fx.blocks_x + bx;
let base = (t * fx.mv_stride + block * 2) as usize;
fx.mv_field[base] = mv[0];
fx.mv_field[base + 1] = mv[1];
}
fn plant_in_slot(fx: &mut RingFixture, slot: u32, px: u32, py: u32, patch: &[f32; 64]) {
let pixels = (fx.width * fx.height) as usize;
let frame = &mut fx.ring[slot as usize * pixels..(slot as usize + 1) * pixels];
for row in 0..8u32 {
for col in 0..8u32 {
frame[((py + row) * fx.width + px + col) as usize] = patch[(row * 8 + col) as usize];
}
}
}
fn only_reachable_through(
fx: &mut RingFixture,
ref_pos: (u32, u32),
patch: &[f32; 64],
(bx, by): (u32, u32),
) {
let flat = [0.2f32; 64];
for t in 0..fx.neighbour_slots.len() as u32 {
let slot = fx.neighbour_slots[t as usize];
plant_in_slot(fx, slot, ref_pos.0, ref_pos.1, &flat);
plant_in_slot(fx, slot, ref_pos.0 + 20, ref_pos.1, patch);
set_block_mv(fx, t, 8, 8, [0, 0]);
set_block_mv(fx, t, bx, by, [20, 0]);
}
}
#[test]
fn a_covering_block_other_than_the_corner_finds_the_match() {
let (w, h) = (96u32, 96u32);
let radius = 2u32;
let ref_pos = (64u32, 64u32);
let patch = deterministic_texture(13);
let refs_x = refs_along(w);
let ref_idx = ((ref_pos.1 / STEP) * refs_x + (ref_pos.0 / STEP)) as usize;
let mut corner_only = planted_ring(w, h, radius, ref_pos, 0, &patch, 0.2, |_| 1.0);
only_reachable_through(&mut corner_only, ref_pos, &patch, (8, 8));
corner_only.mv_field.fill(0);
let without = run_fused_over(&corner_only, Knobs::default()).group_weight[ref_idx];
for block in [(7u32, 7u32), (8, 7), (7, 8)] {
let mut fx = planted_ring(w, h, radius, ref_pos, 0, &patch, 0.2, |_| 1.0);
only_reachable_through(&mut fx, ref_pos, &patch, block);
let with_covering = run_fused_over(&fx, Knobs::default()).group_weight[ref_idx];
assert!(
with_covering > without * 1.5,
"the copies are only reachable through block {block:?}'s vector, expected a far \
better group with it, got {with_covering} against {without}"
);
}
}
#[test]
fn overlapping_covering_rectangles_score_each_position_once() {
let (w, h) = (96u32, 96u32);
let radius = 1u32;
let ref_pos = (64u32, 64u32);
let patch = deterministic_texture(17);
let pixels = (w * h) as usize;
let knobs = || Knobs {
lambda_ht: 1.0e6,
..Knobs::default()
};
let build = |second_vector: Option<[i32; 2]>| {
let mut fx = planted_ring(w, h, radius, ref_pos, 0, &patch, 0.2, |_| 1.0);
for t in 0..2u32 {
let slot = fx.neighbour_slots[t as usize];
plant_in_slot(&mut fx, slot, ref_pos.0 + 20, ref_pos.1, &patch);
set_block_mv(&mut fx, t, 8, 8, [20, 0]);
if let Some(v) = second_vector {
set_block_mv(&mut fx, t, 7, 7, v);
}
}
fx
};
let one = run_fused_over(&build(None), knobs());
let two = run_fused_over(&build(Some([21, 0])), knobs());
let frames = one.wsum.len() / pixels;
let planted_centre =
|run: &FusedRun, s: usize| run.wsum[s * pixels + ((ref_pos.1 + 4) * w + ref_pos.0 + 20 + 4) as usize];
for s in 0..frames {
if s as u32 == 1 {
continue;
}
assert_eq!(
planted_centre(&two, s),
planted_centre(&one, s),
"slot {s}: the planted copy must carry the same weight whether one or two covering \
blocks reach it"
);
}
assert!(
planted_centre(&one, 0) > 0,
"the copy must be a member in the first place"
);
}
#[test]
fn a_block_size_equal_to_the_step_reads_only_the_corner_block() {
let (w, h) = (96u32, 96u32);
let radius = 2u32;
let ref_pos = (64u32, 64u32);
let patch = deterministic_texture(19);
let refs_x = refs_along(w);
let ref_idx = ((ref_pos.1 / STEP) * refs_x + (ref_pos.0 / STEP)) as usize;
let mut fx = planted_ring(w, h, radius, ref_pos, 0, &patch, 0.2, |_| 1.0);
only_reachable_through(&mut fx, ref_pos, &patch, (7, 7));
let covering = run_fused_over(&fx, Knobs::default()).group_weight[ref_idx];
let single = run_fused_over(
&fx,
Knobs {
blksize: BLK_STEP,
..Knobs::default()
},
)
.group_weight[ref_idx];
assert!(
covering > single * 1.5,
"at blksize == step the copy is unreachable, got {single} against {covering} with \
covering blocks"
);
}