use super::locate::TierMap;
use super::stats::CellRef;
use super::{Footprint, Frame, BLOCK, CELL, TIER_CLEAN, TIER_VEIL};
pub const SIDE: usize = BLOCK / CELL;
pub const CELLS: usize = SIDE * SIDE;
const _: () = assert!(CELL == 2);
const PIXELS: usize = BLOCK * BLOCK;
#[derive(Clone, Copy)]
pub struct BlockVote {
pub tier: i8,
pub clean_views: u16,
pub changed_views: u16,
}
#[derive(Clone, Copy, Debug)]
pub struct Grid {
pub gx: i64,
pub gy: i64,
pub bw: usize,
pub bh: usize,
}
impl Grid {
pub fn covering(x: i64, y: i64, width: usize, height: usize) -> Self {
let b = BLOCK as i64;
let (gx, gy) = (x.div_euclid(b), y.div_euclid(b));
let (gx1, gy1) = (
(x + width as i64 + b - 1).div_euclid(b),
(y + height as i64 + b - 1).div_euclid(b),
);
Self {
gx,
gy,
bw: (gx1 - gx).max(0) as usize,
bh: (gy1 - gy).max(0) as usize,
}
}
#[inline]
pub fn origin(&self, b: usize) -> (i64, i64) {
(
(self.gx + (b % self.bw) as i64) * BLOCK as i64,
(self.gy + (b / self.bw) as i64) * BLOCK as i64,
)
}
fn span(&self, footprint: &Footprint<'_>, pose: (i64, i64)) -> (usize, usize, usize, usize) {
let b = BLOCK as i64;
let (x0, y0) = (footprint.x + pose.0, footprint.y + pose.1);
let (x1, y1) = (x0 + footprint.width as i64, y0 + footprint.height as i64);
let clamp = |v: i64, n: usize| v.clamp(0, n as i64) as usize;
(
clamp((x0 + b - 1).div_euclid(b) - self.gx, self.bw),
clamp((y0 + b - 1).div_euclid(b) - self.gy, self.bh),
clamp(x1.div_euclid(b) - self.gx, self.bw),
clamp(y1.div_euclid(b) - self.gy, self.bh),
)
}
}
fn view(
frame: &Frame<'_>,
footprint: &Footprint<'_>,
tiers: &TierMap<'_>,
pose: (i64, i64),
wx0: i64,
wy0: i64,
out: &mut [[u8; 3]; CELLS],
) -> Option<u8> {
let (sx0, sy0) = (wx0 - pose.0, wy0 - pose.1);
let (lx0, ly0) = (sx0 - footprint.x, sy0 - footprint.y);
let b = BLOCK as i64;
if sx0 < 0 || sy0 < 0 || sx0 + b > frame.width as i64 || sy0 + b > frame.height as i64 {
return None;
}
if lx0 < 0 || ly0 < 0 || lx0 + b > footprint.width as i64 || ly0 + b > footprint.height as i64 {
return None;
}
debug_assert_eq!((tiers.x, tiers.y), (footprint.x, footprint.y));
let (w, fw) = (frame.width, footprint.width);
let (lx0, ly0, sx0, sy0) = (lx0 as usize, ly0 as usize, sx0 as usize, sy0 as usize);
if (0..BLOCK).any(|yy| footprint.mask[(ly0 + yy) * fw + lx0..][..BLOCK].contains(&0)) {
return None;
}
let q = (CELL * CELL) as u32;
for (cy, cells) in out.chunks_exact_mut(SIDE).enumerate() {
let top = &frame.rgba[((sy0 + cy * CELL) * w + sx0) * 4..][..BLOCK * 4];
let bottom = &frame.rgba[((sy0 + cy * CELL + 1) * w + sx0) * 4..][..BLOCK * 4];
for ((cell, t), b) in cells
.iter_mut()
.zip(top.chunks_exact(8))
.zip(bottom.chunks_exact(8))
{
let mean = |c: usize| {
((t[c] as u32 + t[c + 4] as u32 + b[c] as u32 + b[c + 4] as u32 + q / 2) / q) as u8
};
*cell = [mean(0), mean(1), mean(2)];
}
}
let mut worst = TIER_CLEAN;
for ty in ly0 / CELL..=(ly0 + BLOCK - 1) / CELL {
let row = &tiers.tier[ty * tiers.cw..(ty + 1) * tiers.cw];
for &t in &row[lx0 / CELL..=(lx0 + BLOCK - 1) / CELL] {
worst = worst.min(t);
}
}
Some(worst)
}
const OUTLIER: i32 = 48;
#[inline]
fn cell_luma(c: [u8; 3]) -> i32 {
super::luma(c[0], c[1], c[2]) as i32
}
fn compare(a: &[[u8; 3]], b: &[[u8; 3]]) -> (u32, u32, u32) {
let (mut sum, mut bias, mut off) = (0u32, 0i32, 0u32);
for (p, q) in a.iter().zip(b) {
for c in 0..3 {
let d = p[c] as i32 - q[c] as i32;
sum += d.unsigned_abs();
bias += d;
}
off += ((cell_luma(*p) - cell_luma(*q)).abs() > OUTLIER) as u32;
}
let n = (a.len() * 3) as u32;
(sum / n, bias.unsigned_abs() / n, off)
}
#[inline]
fn agrees(d: (u32, u32, u32), tau: i32) -> bool {
d.0 <= (tau as u32 / 4).max(2) && d.1 <= 2 && d.2 <= 2
}
#[inline]
fn agrees_strictly(d: (u32, u32, u32), tau: i32) -> bool {
agrees(d, tau) && d.2 == 0
}
const CHANGED: u32 = 8;
const SHIFTED: u32 = 3;
pub struct Consensus {
pub grid: Grid,
pub sig: Vec<[u8; 3]>,
count: Vec<i16>,
pub vote: Vec<BlockVote>,
}
impl Consensus {
pub fn new(grid: Grid) -> Self {
let n = grid.bw * grid.bh;
Self {
grid,
sig: vec![[0; 3]; n * CELLS],
count: vec![0; n * CELLS],
vote: vec![
BlockVote {
tier: -1,
clean_views: 0,
changed_views: 0
};
n
],
}
}
pub fn observe(
&mut self,
frame: &Frame<'_>,
footprint: &Footprint<'_>,
tiers: &TierMap<'_>,
pose: (i64, i64),
) {
let (bx0, by0, bx1, by1) = self.grid.span(footprint, pose);
if bx1 <= bx0 || by1 <= by0 {
return;
}
let rows = by1 - by0;
let chunks = crate::kernels::pool::chunks_for(rows * (bx1 - bx0), 64);
let grid = self.grid;
let (sig_p, count_p, vote_p) = (
crate::kernels::pool::SyncPtr(self.sig.as_mut_ptr()),
crate::kernels::pool::SyncPtr(self.count.as_mut_ptr()),
crate::kernels::pool::SyncPtr(self.vote.as_mut_ptr()),
);
crate::kernels::pool::par_for(chunks, |k| {
let mut sig = [[0u8; 3]; CELLS];
for by in by0 + crate::kernels::pool::split(rows, chunks, k)
..by0 + crate::kernels::pool::split(rows, chunks, k + 1)
{
for bx in bx0..bx1 {
let b = by * grid.bw + bx;
let (wx0, wy0) = grid.origin(b);
let Some(tier) = view(frame, footprint, tiers, pose, wx0, wy0, &mut sig) else {
continue;
};
let (cand, count, v) = unsafe {
(
std::slice::from_raw_parts_mut(sig_p.get().add(b * CELLS), CELLS),
std::slice::from_raw_parts_mut(count_p.get().add(b * CELLS), CELLS),
&mut *vote_p.get().add(b),
)
};
vote(cand, count, v, &sig, tier);
}
}
});
}
}
fn vote(
cand: &mut [[u8; 3]],
count: &mut [i16],
v: &mut BlockVote,
sig: &[[u8; 3]; CELLS],
tier: u8,
) {
if tier == TIER_CLEAN {
if v.tier == TIER_CLEAN as i8 {
let d = compare(sig, cand);
if d.0 > CHANGED || d.1 > SHIFTED {
v.changed_views = v.changed_views.saturating_add(1);
}
}
v.clean_views = v.clean_views.saturating_add(1);
}
let t = tier as i8;
if t > v.tier {
cand.copy_from_slice(sig);
count.fill(1);
v.tier = t;
} else if t == v.tier {
for k in 0..CELLS {
if (cell_luma(sig[k]) - cell_luma(cand[k])).abs() <= OUTLIER {
count[k] = count[k].saturating_add(1);
} else if count[k] <= 1 {
cand[k] = sig[k];
count[k] = 1;
} else {
count[k] -= 1;
}
}
}
}
impl CellRef for Consensus {
#[inline]
fn cell(&self, cx: i64, cy: i64) -> Option<[u8; 3]> {
let (wx, wy) = (cx * CELL as i64, cy * CELL as i64);
let (bx, by) = (
wx.div_euclid(BLOCK as i64) - self.grid.gx,
wy.div_euclid(BLOCK as i64) - self.grid.gy,
);
if bx < 0 || by < 0 || bx >= self.grid.bw as i64 || by >= self.grid.bh as i64 {
return None;
}
let b = by as usize * self.grid.bw + bx as usize;
if self.vote[b].tier < 0 {
return None;
}
let (ix, iy) = (
(wx.rem_euclid(BLOCK as i64) as usize) / CELL,
(wy.rem_euclid(BLOCK as i64) as usize) / CELL,
);
Some(self.sig[b * CELLS + iy * SIDE + ix])
}
fn cells(&self) -> (i64, i64, i64, i64) {
let s = SIDE as i64;
(
self.grid.gx * s,
self.grid.gy * s,
(self.grid.gx + self.grid.bw as i64) * s,
(self.grid.gy + self.grid.bh as i64) * s,
)
}
}
pub struct Component {
pub bx: usize,
pub by: usize,
pub bw: usize,
pub bh: usize,
pub mask: Vec<bool>,
pub dynamic: Vec<bool>,
pub best: Option<(usize, usize, usize)>,
}
pub fn components(c: &Consensus) -> Vec<Component> {
let (bw, bh) = (c.grid.bw, c.grid.bh);
let dynamic: Vec<bool> = c
.vote
.iter()
.map(|v| v.changed_views >= 2 && v.changed_views as u32 * 4 >= v.clean_views as u32)
.collect();
let mut grown = vec![false; bw * bh];
for y in 0..bh {
for x in 0..bw {
if dynamic[y * bw + x] {
for yy in y.saturating_sub(1)..=(y + 1).min(bh - 1) {
for xx in x.saturating_sub(1)..=(x + 1).min(bw - 1) {
grown[yy * bw + xx] = true;
}
}
}
}
}
let mut label = vec![0usize; bw * bh];
let mut out = Vec::new();
for s in 0..bw * bh {
if !grown[s] || label[s] != 0 {
continue;
}
let id = out.len() + 1;
let mut stack = vec![s];
label[s] = id;
let mut members = Vec::new();
while let Some(i) = stack.pop() {
members.push(i);
let (x, y) = (i % bw, i / bw);
for yy in y.saturating_sub(1)..=(y + 1).min(bh - 1) {
for xx in x.saturating_sub(1)..=(x + 1).min(bw - 1) {
let j = yy * bw + xx;
if grown[j] && label[j] == 0 {
label[j] = id;
stack.push(j);
}
}
}
}
let x0 = members.iter().map(|&i| i % bw).min().unwrap();
let x1 = members.iter().map(|&i| i % bw).max().unwrap();
let y0 = members.iter().map(|&i| i / bw).min().unwrap();
let y1 = members.iter().map(|&i| i / bw).max().unwrap();
let (cw, ch) = (x1 - x0 + 1, y1 - y0 + 1);
let mut mask = vec![false; cw * ch];
let mut changing = vec![false; cw * ch];
for &i in &members {
let k = (i / bw - y0) * cw + (i % bw - x0);
mask[k] = true;
changing[k] = dynamic[i];
}
out.push(Component {
bx: x0,
by: y0,
bw: cw,
bh: ch,
mask,
dynamic: changing,
best: None,
});
}
out
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum Kind {
None = 0,
Coherent = 1,
Component = 2,
Provisional = 3,
Fallback = 4,
Edge = 5,
}
pub const CENTRAL: i64 = 48;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BlockWrite {
pub canvas: u32,
pub gx: i64,
pub gy: i64,
pub sx: i64,
pub sy: i64,
pub frame: u32,
pub kind: Kind,
}
pub const UNTOUCHED: i32 = -1;
pub const MIXED: i32 = -2;
pub struct Deferred {
pub gx: i64,
pub gy: i64,
pub frame: u32,
pub kind: Kind,
pub rgba: Box<[u8; PIXELS * 4]>,
}
struct Fallback {
cand: [[u8; 3]; PIXELS],
count: [i16; PIXELS],
tier: [i8; PIXELS],
main: [[u8; 4]; PIXELS],
main_tier: [i8; PIXELS],
}
#[derive(Clone, Copy, Default)]
struct Act {
seen: bool,
tier: u8,
strict: bool,
loose: bool,
inset: i16,
main: bool,
}
fn fallback_view(
fb: &mut Fallback,
frame: &Frame<'_>,
footprint: &Footprint<'_>,
tiers: &TierMap<'_>,
pose: (i64, i64),
origin: (i64, i64),
tau: i32,
) {
let (w, near) = (frame.width, (tau / 2).max(2));
for j in 0..PIXELS {
let (sx, sy) = (
origin.0 + (j % BLOCK) as i64 - pose.0,
origin.1 + (j / BLOCK) as i64 - pose.1,
);
if sx < 0
|| sy < 0
|| sx >= w as i64
|| sy >= frame.height as i64
|| !footprint.contains(sx, sy)
{
continue;
}
let fi = (sy as usize * w + sx as usize) * 4;
let obs = [frame.rgba[fi], frame.rgba[fi + 1], frame.rgba[fi + 2]];
let t = tiers.at(sx, sy) as i8;
let m = fb.main[j];
if m[3] == 255 && (0..3).all(|k| (obs[k] as i32 - m[k] as i32).abs() <= near) {
fb.main_tier[j] = fb.main_tier[j].max(t);
}
if t > fb.tier[j] {
fb.cand[j] = obs;
fb.count[j] = 1;
fb.tier[j] = t;
} else if t == fb.tier[j] {
if (0..3).all(|k| (obs[k] as i32 - fb.cand[j][k] as i32).abs() <= tau) {
fb.cand[j] = obs;
fb.count[j] = fb.count[j].saturating_add(1);
} else if fb.count[j] <= 1 {
fb.cand[j] = obs;
fb.count[j] = 1;
} else {
fb.count[j] -= 1;
}
}
}
}
pub struct Output {
acts: Vec<Act>,
pub kind: Vec<Kind>,
pub frame: Vec<i32>,
component: Vec<i32>,
provisional: Vec<i8>,
rank: Vec<(i8, bool, bool, i16)>,
held: Vec<Option<Box<[u8; PIXELS * 3]>>>,
main_tier: Vec<i8>,
fallback: Vec<Option<Box<Fallback>>>,
pub components: Vec<Component>,
}
pub const CONSENSUS_BLOCK_BYTES: usize = CELLS
* (std::mem::size_of::<[u8; 3]>() + std::mem::size_of::<i16>())
+ std::mem::size_of::<BlockVote>();
pub const OUTPUT_BLOCK_BYTES: usize = std::mem::size_of::<Act>()
+ std::mem::size_of::<Kind>()
+ 2 * std::mem::size_of::<i32>()
+ std::mem::size_of::<i8>()
+ std::mem::size_of::<(i8, bool, bool, i16)>()
+ std::mem::size_of::<Option<Box<[u8; PIXELS * 3]>>>()
+ PIXELS * 3
+ std::mem::size_of::<i8>()
+ std::mem::size_of::<Option<Box<Fallback>>>();
pub const FALLBACK_BYTES: usize = std::mem::size_of::<Fallback>();
impl Consensus {
pub fn unseen_blocks(&self) -> usize {
self.vote.iter().filter(|v| v.clean_views == 0).count()
}
}
impl Output {
pub fn new(c: &Consensus, components: Vec<Component>) -> Self {
let n = c.grid.bw * c.grid.bh;
let mut component = vec![-1; n];
for (k, comp) in components.iter().enumerate() {
for yy in 0..comp.bh {
for xx in 0..comp.bw {
if comp.mask[yy * comp.bw + xx] {
component[(comp.by + yy) * c.grid.bw + comp.bx + xx] = k as i32;
}
}
}
}
let fallback = c
.vote
.iter()
.zip(&component)
.map(|(v, &k)| {
(v.clean_views == 0 && k < 0).then(|| {
Box::new(Fallback {
cand: [[0; 3]; PIXELS],
count: [0; PIXELS],
tier: [-1; PIXELS],
main: [[0; 4]; PIXELS],
main_tier: [-1; PIXELS],
})
})
})
.collect();
Self {
acts: vec![Act::default(); n],
kind: vec![Kind::None; n],
frame: vec![-1; n],
component,
provisional: vec![-1; n],
rank: vec![(-1, false, false, -1); n],
held: (0..n).map(|_| None).collect(),
main_tier: vec![-1; n],
fallback,
components,
}
}
pub fn fallback_blocks(&self) -> Vec<usize> {
self.fallback
.iter()
.enumerate()
.filter(|(_, f)| f.is_some())
.map(|(b, _)| b)
.collect()
}
pub fn set_main(&mut self, b: usize, rgba: &[u8]) {
if let Some(fb) = self.fallback.get_mut(b).and_then(|f| f.as_mut()) {
for (j, px) in rgba.chunks_exact(4).take(PIXELS).enumerate() {
fb.main[j] = [px[0], px[1], px[2], px[3]];
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn emit(
&mut self,
c: &Consensus,
frame: &Frame<'_>,
footprint: &Footprint<'_>,
tiers: &TierMap<'_>,
pose: (i64, i64),
tau: i32,
frame_index: u32,
main: &(dyn CellRef + Sync),
writes: &mut Vec<BlockWrite>,
) {
let grid = c.grid;
let mut sig = [[0u8; 3]; CELLS];
let block_write = |b: usize, kind: Kind| {
let (wx0, wy0) = grid.origin(b);
BlockWrite {
canvas: 0,
gx: grid.gx + (b % grid.bw) as i64,
gy: grid.gy + (b / grid.bw) as i64,
sx: wx0 - pose.0,
sy: wy0 - pose.1,
frame: frame_index,
kind,
}
};
for comp in self.components.iter_mut() {
let mut covered = Vec::new();
let mut tainted = false;
for yy in 0..comp.bh {
for xx in 0..comp.bw {
let k = yy * comp.bw + xx;
if !comp.mask[k] {
continue;
}
let b = (comp.by + yy) * grid.bw + comp.bx + xx;
let (wx0, wy0) = grid.origin(b);
if let Some(t) = view(frame, footprint, tiers, pose, wx0, wy0, &mut sig) {
if t >= TIER_VEIL {
if !comp.dynamic[k] {
let off = compare(&sig, &c.sig[b * CELLS..(b + 1) * CELLS]).2;
tainted |= (1..=CELLS as u32 * 3 / 8).contains(&off);
}
covered.push(b);
}
}
}
}
if covered.is_empty() {
continue;
}
let blocks = comp.mask.iter().filter(|&&m| m).count();
let eligible = covered.len() * 10 >= blocks * 9;
let score = if eligible {
(1, !tainted as usize, frame_index as usize)
} else {
(0, 0, covered.len())
};
if comp.best.is_some_and(|best| best > score) {
continue;
}
comp.best = Some(score);
for b in covered {
self.kind[b] = Kind::Component;
self.frame[b] = frame_index as i32;
writes.push(block_write(b, Kind::Component));
}
}
let (bx0, by0, bx1, by1) = grid.span(footprint, pose);
let w = frame.width;
let (fx0, fy0, fx1, fy1) = {
let b = BLOCK as i64;
let (x0, y0) = (footprint.x + pose.0, footprint.y + pose.1);
let (x1, y1) = (x0 + footprint.width as i64, y0 + footprint.height as i64);
let clamp = |v: i64, n: usize| v.clamp(0, n as i64) as usize;
(
clamp(x0.div_euclid(b) - grid.gx, grid.bw),
clamp(y0.div_euclid(b) - grid.gy, grid.bh),
clamp((x1 + b - 1).div_euclid(b) - grid.gx, grid.bw),
clamp((y1 + b - 1).div_euclid(b) - grid.gy, grid.bh),
)
};
let (kind_p, fallback_base) = (
crate::kernels::pool::SyncPtr(self.kind.as_mut_ptr()),
self.fallback.as_mut_ptr() as usize,
);
if fy1 > fy0 && fx1 > fx0 {
let rows = fy1 - fy0;
let chunks = crate::kernels::pool::chunks_for(rows * (fx1 - fx0), 64);
crate::kernels::pool::par_for(chunks, |k| {
for by in fy0 + crate::kernels::pool::split(rows, chunks, k)
..fy0 + crate::kernels::pool::split(rows, chunks, k + 1)
{
for bx in fx0..fx1 {
let b = by * grid.bw + bx;
let (slot, kind) = unsafe {
(
&mut *(fallback_base as *mut Option<Box<Fallback>>).add(b),
&mut *kind_p.get().add(b),
)
};
let Some(fb) = slot.as_mut() else { continue };
*kind = Kind::Fallback;
fallback_view(fb, frame, footprint, tiers, pose, grid.origin(b), tau);
}
}
});
}
if by1 <= by0 || bx1 <= bx0 {
return;
}
let rows = by1 - by0;
let chunks = crate::kernels::pool::chunks_for(rows * (bx1 - bx0), 64);
let acts = crate::kernels::pool::SyncPtr(self.acts.as_mut_ptr());
let (component, fallback, kind) = (&self.component, &self.fallback, &self.kind);
crate::kernels::pool::par_for(chunks, |k| {
let mut sig = [[0u8; 3]; CELLS];
for by in by0 + crate::kernels::pool::split(rows, chunks, k)
..by0 + crate::kernels::pool::split(rows, chunks, k + 1)
{
for bx in bx0..bx1 {
let b = by * grid.bw + bx;
let act = unsafe { &mut *acts.get().add(b) };
*act = Act::default();
if component[b] >= 0 || fallback[b].is_some() || kind[b] == Kind::Coherent {
continue;
}
let (wx0, wy0) = grid.origin(b);
let Some(tier) = view(frame, footprint, tiers, pose, wx0, wy0, &mut sig) else {
continue;
};
let d = compare(&sig, &c.sig[b * CELLS..(b + 1) * CELLS]);
let (sx0, sy0) = (wx0 - pose.0, wy0 - pose.1);
let inset = (sx0 - footprint.x)
.min(sy0 - footprint.y)
.min(footprint.x + footprint.width as i64 - sx0 - BLOCK as i64)
.min(footprint.y + footprint.height as i64 - sy0 - BLOCK as i64);
let (cx0, cy0) = (wx0.div_euclid(CELL as i64), wy0.div_euclid(CELL as i64));
let mut msig = [[0u8; 3]; CELLS];
let known = (0..CELLS).all(|k| {
match main.cell(cx0 + (k % SIDE) as i64, cy0 + (k / SIDE) as i64) {
Some(v) => {
msig[k] = v;
true
}
None => false,
}
});
*act = Act {
seen: true,
tier,
strict: agrees_strictly(d, tau),
loose: agrees(d, tau),
inset: inset.min(CENTRAL) as i16,
main: known && agrees(compare(&sig, &msig), tau),
};
}
}
});
for by in by0..by1 {
for bx in bx0..bx1 {
let b = by * grid.bw + bx;
let a = self.acts[b];
if !a.seen {
continue;
}
if a.tier == TIER_CLEAN && a.strict && a.inset >= CENTRAL as i16 {
self.kind[b] = Kind::Coherent;
self.frame[b] = frame_index as i32;
self.held[b] = None;
writes.push(block_write(b, Kind::Coherent));
continue;
}
if a.main {
self.main_tier[b] = self.main_tier[b].max(a.tier as i8);
}
let rank = (
a.tier as i8,
a.tier == TIER_CLEAN && a.strict,
a.tier == TIER_CLEAN && a.loose,
a.inset,
);
if rank >= self.rank[b] {
let held = self.held[b].get_or_insert_with(|| Box::new([0u8; PIXELS * 3]));
let (wx0, wy0) = grid.origin(b);
for j in 0..PIXELS {
let (sx, sy) = (
wx0 + (j % BLOCK) as i64 - pose.0,
wy0 + (j / BLOCK) as i64 - pose.1,
);
let fi = (sy as usize * w + sx as usize) * 4;
held[j * 3..j * 3 + 3].copy_from_slice(&frame.rgba[fi..fi + 3]);
}
self.rank[b] = rank;
self.kind[b] = if rank.2 {
Kind::Edge
} else {
Kind::Provisional
};
self.provisional[b] = a.tier as i8;
self.frame[b] = frame_index as i32;
}
}
}
}
pub fn sources(&self, c: &Consensus, deferred: &[Deferred]) -> Vec<i32> {
let grid = c.grid;
let mut out: Vec<i32> = self
.kind
.iter()
.zip(&self.frame)
.map(|(k, &f)| match k {
Kind::Coherent | Kind::Component => f,
_ => UNTOUCHED,
})
.collect();
for d in deferred {
let b = (d.gy - grid.gy) as usize * grid.bw + (d.gx - grid.gx) as usize;
out[b] = if d.kind == Kind::Fallback {
MIXED
} else {
d.frame as i32
};
}
out
}
pub fn finish(&self, c: &Consensus, tau: i32) -> Vec<Deferred> {
let grid = c.grid;
let near = (tau / 2).max(2);
let mut out = Vec::new();
for b in 0..grid.bw * grid.bh {
let (gx, gy) = (
grid.gx + (b % grid.bw) as i64,
grid.gy + (b / grid.bw) as i64,
);
match self.kind[b] {
Kind::Edge => {
let Some(h) = &self.held[b] else { continue };
if c.vote[b].clean_views < 2 && self.main_tier[b] >= self.provisional[b] {
continue;
}
let mut rgba = Box::new([0u8; PIXELS * 4]);
for j in 0..PIXELS {
rgba[j * 4..j * 4 + 3].copy_from_slice(&h[j * 3..j * 3 + 3]);
rgba[j * 4 + 3] = 255;
}
out.push(Deferred {
gx,
gy,
frame: self.frame[b] as u32,
kind: Kind::Edge,
rgba,
});
}
Kind::Provisional => {
let Some(h) = &self.held[b] else { continue };
if self.main_tier[b] >= self.provisional[b] {
continue;
}
let mut rgba = Box::new([0u8; PIXELS * 4]);
for j in 0..PIXELS {
rgba[j * 4..j * 4 + 3].copy_from_slice(&h[j * 3..j * 3 + 3]);
rgba[j * 4 + 3] = 255;
}
out.push(Deferred {
gx,
gy,
frame: self.frame[b] as u32,
kind: Kind::Provisional,
rgba,
});
}
Kind::Fallback => {
let Some(fb) = &self.fallback[b] else {
continue;
};
let mut rgba = Box::new([0u8; PIXELS * 4]);
let mut any = false;
let same = |j: usize| {
let m = fb.main[j];
m[3] == 255
&& (0..3).all(|k| (fb.cand[j][k] as i32 - m[k] as i32).abs() <= near)
};
if !(0..PIXELS)
.any(|j| fb.tier[j] >= 0 && fb.tier[j] > fb.main_tier[j] && !same(j))
{
continue;
}
for j in 0..PIXELS {
if fb.tier[j] < 0 || (fb.main_tier[j] >= fb.tier[j] && !same(j)) {
continue;
}
rgba[j * 4..j * 4 + 3].copy_from_slice(&fb.cand[j]);
rgba[j * 4 + 3] = 255;
any = true;
}
if any {
out.push(Deferred {
gx,
gy,
frame: 0,
kind: Kind::Fallback,
rgba,
});
}
}
_ => {}
}
}
out
}
}