use cubecl::prelude::*;
use super::aggregate::scatter_patch;
use super::group::{clamp_top_left, pack_pos_t, unpack_t};
use super::plane_ops::{group_base, plane_ssd_reduce8, shift_insert8, shift_insert8_gated, transpose8};
use super::transforms::{
RECIPROCAL_FLOOR,
dct8_reg_fwd,
dct8_reg_inv,
fill_dct8_basis,
haar_reg_fwd_level,
haar_reg_inv_level,
safe_reciprocal,
variance_reg_level,
};
use crate::collab::{MAX_K, MAX_TEMPORAL_RADIUS, PATCH_AREA, PATCH_SIZE, STEP};
use crate::nlmeans::kernels::helpers::{channel_scale, read_line};
const _: () = assert!(
2 * MAX_TEMPORAL_RADIUS < 64,
"pack_pos_t's 6-bit t field must hold every neighbour index collab_fused packs"
);
const _: () = assert!(
MAX_K == PATCH_SIZE && MAX_K == 8,
"collab_fused's per-lane group array and its three-level stack transform are written for \
MAX_K == PATCH_SIZE == 8"
);
pub(crate) const MEMBER_SIGMA2_CAP: f32 = 64.0;
#[cube]
fn covering_lo(p: u32, #[comptime] blksize: u32, #[comptime] step: u32) -> u32 {
let past = u32::max(p + PATCH_SIZE, blksize) - blksize;
past.div_ceil(step)
}
#[cfg(test)]
fn covering_lo_host(p: u32, blksize: u32, step: u32) -> u32 {
let past = u32::max(p + PATCH_SIZE, blksize) - blksize;
past.div_ceil(step)
}
#[cube]
fn candidate_distance<N: Size>(
ring: &Array<Vector<f32, N>>,
current: &Array<f32>,
x: u32,
y: u32,
slot: u32,
sub: u32,
scale: f32,
noise_floor: f32,
#[comptime] width: u32,
#[comptime] height: u32,
#[comptime] channels: u32,
) -> f32 {
let mut partial = 0.0f32;
#[unroll]
for r in 0..PATCH_SIZE {
let px = read_line(ring, x + sub, y + r, slot, width, height);
#[unroll]
for c in 0..channels {
let d = current[(r * channels + c) as usize] - px[c as usize];
partial += d * d;
}
}
plane_ssd_reduce8(partial) * scale - noise_floor
}
#[cube(launch_unchecked)]
#[expect(
clippy::too_many_arguments,
reason = "every argument is a buffer or comptime shape the kernel binds"
)]
#[expect(
clippy::collapsible_if,
reason = "the outer condition of the group-DC exception is comptime, so nesting elides the \
inner test in 63 of the 64 unrolled positions rather than emitting it and ANDing \
a constant false into it"
)]
pub fn collab_fused<N: Size>(
ring: &Array<Vector<f32, N>>,
mv_field: &Array<i32>,
confidence: &Array<f32>,
neighbour_slots: &Array<u32>,
sigma: &Array<f32>,
dct_profile: &Array<f32>,
kaiser: &Array<f32>,
accum: &mut Array<Atomic<i32>>,
wsum: &mut Array<Atomic<i32>>,
group_weight: &mut Array<f32>,
centre_slot: u32,
noise_floor: f32,
c_min: f32,
mismatch_scale2: f32,
lambda_ht: f32,
weight_scale: f32,
accum_scale: f32,
#[comptime] use_member_sigma: bool,
#[comptime] warp_uniform: bool,
#[comptime] radius: u32,
#[comptime] refine: u32,
#[comptime] mv_stride: u32,
#[comptime] conf_stride: u32,
#[comptime] blk_step: u32,
#[comptime] blksize: u32,
#[comptime] blocks_x: u32,
#[comptime] blocks_y: u32,
#[comptime] width: u32,
#[comptime] height: u32,
#[comptime] channels: u32,
#[comptime] k_max: u32,
#[comptime] stored_ch: u32,
#[comptime] spatial_radius: u32,
#[comptime] refs_x: u32,
) {
let tid = UNIT_POS_X;
let grp = tid / 8u32;
let sub = tid % 8u32;
let base = group_base();
let max_x = comptime!(width - PATCH_SIZE);
let max_y = comptime!(height - PATCH_SIZE);
let mut basis = SharedMemory::<f32>::new(PATCH_AREA as usize);
let mut tbuf = SharedMemory::<f32>::new(comptime!(8 * 65) as usize);
fill_dct8_basis(&mut basis, tid);
sync_cube();
let ref_x_index = CUBE_POS_X * 8u32 + grp;
let live = ref_x_index < refs_x;
let ref_x_clamped = ref_x_index.min(refs_x - 1u32);
let rx = (ref_x_clamped * STEP).min(max_x);
let ry = (CUBE_POS_Y * STEP).min(max_y);
let mut current = Array::<f32>::new(comptime!(PATCH_SIZE * channels) as usize);
#[unroll]
for r in 0..PATCH_SIZE {
let px = read_line(ring, rx + sub, ry + r, centre_slot, width, height);
#[unroll]
for c in 0..channels {
current[(r * channels + c) as usize] = px[c as usize];
}
}
let mut best_d = 3.0e38f32;
let mut best_pos = 0u32;
let scale = channel_scale(channels);
let bx_hi = (rx / blk_step).min(blocks_x - 1);
let by_hi = (ry / blk_step).min(blocks_y - 1);
let bx_lo = u32::min(covering_lo(rx, blksize, blk_step), bx_hi);
let by_lo = u32::min(covering_lo(ry, blksize, blk_step), by_hi);
let mut n_live = 0u32;
let s_left = clamp_top_left(rx as i32 - spatial_radius as i32, max_x);
let s_right = clamp_top_left(rx as i32 + spatial_radius as i32, max_x);
let s_top = clamp_top_left(ry as i32 - spatial_radius as i32, max_y);
let s_bot = clamp_top_left(ry as i32 + spatial_radius as i32, max_y);
n_live += (s_right - s_left + 1u32) * (s_bot - s_top + 1u32);
if warp_uniform {
let span = comptime!(2 * spatial_radius + 1);
for dy in 0..span {
for dx in 0..span {
let wanted_y = s_top + dy;
let wanted_x = s_left + dx;
let live_pos = wanted_x <= s_right && wanted_y <= s_bot;
let cx = u32::min(wanted_x, s_right);
let cy = u32::min(wanted_y, s_bot);
let scored = candidate_distance(
ring,
¤t,
cx,
cy,
centre_slot,
sub,
scale,
noise_floor,
width,
height,
channels,
);
let mut dist = select(live_pos, scored, 3.0e38f32);
if live_pos && cx == rx && cy == ry {
dist = -1.0e38f32;
}
shift_insert8(&mut best_d, &mut best_pos, dist, pack_pos_t(cx, cy, 0u32), sub);
}
}
} else {
let mut cy = s_top;
while cy <= s_bot {
let mut cx = s_left;
while cx <= s_right {
let mut dist = candidate_distance(
ring,
¤t,
cx,
cy,
centre_slot,
sub,
scale,
noise_floor,
width,
height,
channels,
);
if cx == rx && cy == ry {
dist = -1.0e38f32;
}
shift_insert8_gated(
&mut best_d,
&mut best_pos,
dist,
pack_pos_t(cx, cy, 0u32),
sub,
base,
);
cx += 1u32;
}
cy += 1u32;
}
}
let n_neighbours = comptime!(2 * radius);
let covers = comptime!(blksize.div_ceil(blk_step));
let max_rects = comptime!(covers * covers);
let mut t = 0u32;
while t < n_neighbours {
let slot = neighbour_slots[t as usize];
let packed_t = t + 1u32;
let mut seen_left = Array::<u32>::new(max_rects as usize);
let mut seen_right = Array::<u32>::new(max_rects as usize);
let mut seen_top = Array::<u32>::new(max_rects as usize);
let mut seen_bot = Array::<u32>::new(max_rects as usize);
#[unroll]
for s in 0..max_rects {
seen_left[s as usize] = 1u32;
seen_right[s as usize] = 0u32;
seen_top[s as usize] = 1u32;
seen_bot[s as usize] = 0u32;
}
#[unroll]
for iy in 0..covers {
#[unroll]
for ix in 0..covers {
let wanted_bx = bx_lo + ix;
let wanted_by = by_lo + iy;
let block_live = wanted_bx <= bx_hi && wanted_by <= by_hi;
if warp_uniform {
let cbx = u32::min(wanted_bx, bx_hi);
let cby = u32::min(wanted_by, by_hi);
let block = cby * blocks_x + cbx;
let conf = confidence[(t * conf_stride + block) as usize];
let block_scored = block_live && conf >= c_min;
let mv = (t * mv_stride + block * 2u32) as usize;
let px0 = rx as i32 + mv_field[mv];
let py0 = ry as i32 + mv_field[mv + 1];
let t_left = clamp_top_left(px0 - refine as i32, max_x);
let t_right = clamp_top_left(px0 + refine as i32, max_x);
let t_top = clamp_top_left(py0 - refine as i32, max_y);
let t_bot = clamp_top_left(py0 + refine as i32, max_y);
let span = comptime!(2 * refine + 1);
for dy in 0..span {
for dx in 0..span {
let wanted_y = t_top + dy;
let wanted_x = t_left + dx;
let in_rect = wanted_x <= t_right && wanted_y <= t_bot;
let nx = u32::min(wanted_x, t_right);
let ny = u32::min(wanted_y, t_bot);
let mut covered = false;
#[unroll]
for s in 0..max_rects {
if nx >= seen_left[s as usize]
&& nx <= seen_right[s as usize]
&& ny >= seen_top[s as usize]
&& ny <= seen_bot[s as usize]
{
covered = true;
}
}
let live_pos = block_scored && in_rect && !covered;
if live_pos {
n_live += 1u32;
}
let scored = candidate_distance(
ring,
¤t,
nx,
ny,
slot,
sub,
scale,
noise_floor,
width,
height,
channels,
);
shift_insert8(
&mut best_d,
&mut best_pos,
select(live_pos, scored, 3.0e38f32),
pack_pos_t(nx, ny, packed_t),
sub,
);
}
}
seen_left[(iy * covers + ix) as usize] = select(block_scored, t_left, 1u32);
seen_right[(iy * covers + ix) as usize] = select(block_scored, t_right, 0u32);
seen_top[(iy * covers + ix) as usize] = select(block_scored, t_top, 1u32);
seen_bot[(iy * covers + ix) as usize] = select(block_scored, t_bot, 0u32);
} else {
let cbx = wanted_bx;
let cby = wanted_by;
if block_live {
let block = cby * blocks_x + cbx;
let conf = confidence[(t * conf_stride + block) as usize];
if conf >= c_min {
let mv = (t * mv_stride + block * 2u32) as usize;
let px0 = rx as i32 + mv_field[mv];
let py0 = ry as i32 + mv_field[mv + 1];
let t_left = clamp_top_left(px0 - refine as i32, max_x);
let t_right = clamp_top_left(px0 + refine as i32, max_x);
let t_top = clamp_top_left(py0 - refine as i32, max_y);
let t_bot = clamp_top_left(py0 + refine as i32, max_y);
let mut ny = t_top;
while ny <= t_bot {
let mut nx = t_left;
while nx <= t_right {
let mut covered = false;
#[unroll]
for s in 0..max_rects {
if nx >= seen_left[s as usize]
&& nx <= seen_right[s as usize]
&& ny >= seen_top[s as usize]
&& ny <= seen_bot[s as usize]
{
covered = true;
}
}
if !covered {
n_live += 1u32;
let dist = candidate_distance(
ring,
¤t,
nx,
ny,
slot,
sub,
scale,
noise_floor,
width,
height,
channels,
);
shift_insert8_gated(
&mut best_d,
&mut best_pos,
dist,
pack_pos_t(nx, ny, packed_t),
sub,
base,
);
}
nx += 1u32;
}
ny += 1u32;
}
seen_left[(iy * covers + ix) as usize] = t_left;
seen_right[(iy * covers + ix) as usize] = t_right;
seen_top[(iy * covers + ix) as usize] = t_top;
seen_bot[(iy * covers + ix) as usize] = t_bot;
}
}
}
}
}
t += 1u32;
}
let ref_idx = CUBE_POS_Y * refs_x + ref_x_clamped;
let mut k_use = 1u32;
while k_use * 2u32 <= n_live && k_use * 2u32 <= k_max {
k_use *= 2u32;
}
let mut member_pos = Array::<u32>::new(MAX_K as usize);
let mut member_slot = Array::<u32>::new(MAX_K as usize);
let mut member_sig2 = Array::<f32>::new(MAX_K as usize);
#[unroll]
for m in 0..MAX_K {
let packed = plane_shuffle(best_pos, base + m);
let mt = unpack_t(packed);
let n = u32::max(mt, 1u32) - 1u32;
member_pos[m as usize] = packed;
member_slot[m as usize] = select(mt > 0u32, neighbour_slots[n as usize], centre_slot);
let mut sig2 = 0.0f32;
if use_member_sigma {
if warp_uniform {
let excess = f32::max(plane_shuffle(best_d, base + m), 0.0f32);
let mismatch = mismatch_scale2 * excess / comptime!(3 * PATCH_AREA) as f32;
sig2 = select(mt > 0u32, mismatch, 0.0f32);
} else {
if mt > 0u32 {
let excess = f32::max(plane_shuffle(best_d, base + m), 0.0f32);
sig2 = mismatch_scale2 * excess / comptime!(3 * PATCH_AREA) as f32;
}
}
}
member_sig2[m as usize] = sig2;
}
let prof_sub = dct_profile[sub as usize];
let mut gw = 0.0f32;
#[unroll]
for c in 0..channels {
let sigma_c = sigma[c as usize];
let base_sig2 = sigma_c * sigma_c;
let mut stack = Array::<f32>::new(PATCH_AREA as usize);
let mut v = Array::<f32>::new(MAX_K as usize);
#[unroll]
for m in 0..MAX_K {
let packed = member_pos[m as usize];
let mx = packed & 0x1FFFu32;
let my = (packed >> 13u32) & 0x1FFFu32;
let src_slot = member_slot[m as usize];
let extra = f32::min(member_sig2[m as usize], MEMBER_SIGMA2_CAP * base_sig2);
v[m as usize] = base_sig2 + extra;
#[unroll]
for r in 0..PATCH_SIZE {
let px = read_line(ring, mx + sub, my + r, src_slot, width, height);
stack[(m * PATCH_SIZE + r) as usize] = px[c as usize];
}
}
if k_use >= 8u32 {
variance_reg_level(&mut v, 8u32);
}
if k_use >= 4u32 {
variance_reg_level(&mut v, 4u32);
}
if k_use >= 2u32 {
variance_reg_level(&mut v, 2u32);
}
#[unroll]
for m in 0..MAX_K {
let mut line = Array::<f32>::new(PATCH_SIZE as usize);
#[unroll]
for i in 0..PATCH_SIZE {
line[i as usize] = stack[(m * PATCH_SIZE + i) as usize];
}
dct8_reg_fwd(&basis, &mut line);
transpose8(&mut tbuf, &mut line, sub, grp);
dct8_reg_fwd(&basis, &mut line);
#[unroll]
for i in 0..PATCH_SIZE {
stack[(m * PATCH_SIZE + i) as usize] = line[i as usize];
}
}
if k_use >= 8u32 {
haar_reg_fwd_level(&mut stack, 8u32);
}
if k_use >= 4u32 {
haar_reg_fwd_level(&mut stack, 4u32);
}
if k_use >= 2u32 {
haar_reg_fwd_level(&mut stack, 2u32);
}
let mut retained_v = 0.0f32;
#[unroll]
for i in 0..PATCH_SIZE {
let factor = dct_profile[i as usize] * prof_sub;
#[unroll]
for j in 0..MAX_K {
if j < k_use {
let vj = v[j as usize] * factor;
let slot = (j * PATCH_SIZE + i) as usize;
let mut keep = f32::abs(stack[slot]) >= lambda_ht * f32::sqrt(vj);
if comptime!(j == 0u32 && i == 0u32) {
if sub == 0u32 {
keep = true;
}
}
if keep {
retained_v += vj;
} else {
stack[slot] = 0.0f32;
}
}
}
}
if comptime!(c == 0u32) {
let sum = plane_ssd_reduce8(retained_v);
let w = safe_reciprocal(sum, RECIPROCAL_FLOOR);
if live && sub == 0u32 {
group_weight[ref_idx as usize] = w;
}
gw = w * weight_scale;
}
if k_use >= 2u32 {
haar_reg_inv_level(&mut stack, 2u32);
}
if k_use >= 4u32 {
haar_reg_inv_level(&mut stack, 4u32);
}
if k_use >= 8u32 {
haar_reg_inv_level(&mut stack, 8u32);
}
#[unroll]
for m in 0..MAX_K {
let mut line = Array::<f32>::new(PATCH_SIZE as usize);
#[unroll]
for i in 0..PATCH_SIZE {
line[i as usize] = stack[(m * PATCH_SIZE + i) as usize];
}
dct8_reg_inv(&basis, &mut line);
transpose8(&mut tbuf, &mut line, sub, grp);
dct8_reg_inv(&basis, &mut line);
#[unroll]
for i in 0..PATCH_SIZE {
stack[(m * PATCH_SIZE + i) as usize] = line[i as usize];
}
}
#[unroll]
for m in 0..MAX_K {
if live && m < k_use {
let packed = member_pos[m as usize];
let mx = packed & 0x1FFFu32;
let my = (packed >> 13u32) & 0x1FFFu32;
let dst_slot = member_slot[m as usize];
#[unroll]
for r in 0..PATCH_SIZE {
scatter_patch(
accum,
wsum,
kaiser,
stack[(m * PATCH_SIZE + r) as usize],
gw,
mx,
my,
r * PATCH_SIZE + sub,
comptime!(c == 0u32),
c,
width,
stored_ch,
dst_slot,
comptime!(width * height),
accum_scale,
);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::covering_lo_host;
fn covering_blocks_host(p: u32, blksize: u32, step: u32, blocks: u32) -> (u32, u32) {
let hi = (p / step).min(blocks - 1);
let lo = if p + super::PATCH_SIZE <= blksize {
0
} else {
(p + super::PATCH_SIZE - blksize).div_ceil(step)
};
(lo.min(hi), hi)
}
#[test]
fn covering_lo_matches_the_harness_across_a_range_of_geometries() {
for (blksize, overlap) in [(16u32, 8u32), (16, 12), (32, 24), (8, 4), (16, 0)] {
let step = blksize - overlap;
let blocks = 8u32;
for p in (0..blocks * step).step_by(3) {
let (expect_lo, hi) = covering_blocks_host(p, blksize, step, blocks);
let got_lo = covering_lo_host(p, blksize, step).min(hi);
assert_eq!(
got_lo, expect_lo,
"blksize={blksize} step={step} p={p}: covering_lo disagrees with the \
harness's covering_blocks"
);
}
}
}
}