1use super::locate::TierMap;
17use super::stats::CellRef;
18use super::{Footprint, Frame, BLOCK, CELL, TIER_CLEAN, TIER_VEIL};
19
20pub const SIDE: usize = BLOCK / CELL;
22pub const CELLS: usize = SIDE * SIDE;
23const _: () = assert!(CELL == 2);
25const PIXELS: usize = BLOCK * BLOCK;
26
27#[derive(Clone, Copy)]
28pub struct BlockVote {
29 pub tier: i8,
31 pub clean_views: u16,
32 pub changed_views: u16,
33}
34
35#[derive(Clone, Copy, Debug)]
37pub struct Grid {
38 pub gx: i64,
40 pub gy: i64,
41 pub bw: usize,
42 pub bh: usize,
43}
44
45impl Grid {
46 pub fn covering(x: i64, y: i64, width: usize, height: usize) -> Self {
48 let b = BLOCK as i64;
49 let (gx, gy) = (x.div_euclid(b), y.div_euclid(b));
50 let (gx1, gy1) = (
51 (x + width as i64 + b - 1).div_euclid(b),
52 (y + height as i64 + b - 1).div_euclid(b),
53 );
54 Self {
55 gx,
56 gy,
57 bw: (gx1 - gx).max(0) as usize,
58 bh: (gy1 - gy).max(0) as usize,
59 }
60 }
61
62 #[inline]
64 pub fn origin(&self, b: usize) -> (i64, i64) {
65 (
66 (self.gx + (b % self.bw) as i64) * BLOCK as i64,
67 (self.gy + (b / self.bw) as i64) * BLOCK as i64,
68 )
69 }
70
71 fn span(&self, footprint: &Footprint<'_>, pose: (i64, i64)) -> (usize, usize, usize, usize) {
73 let b = BLOCK as i64;
74 let (x0, y0) = (footprint.x + pose.0, footprint.y + pose.1);
75 let (x1, y1) = (x0 + footprint.width as i64, y0 + footprint.height as i64);
76 let clamp = |v: i64, n: usize| v.clamp(0, n as i64) as usize;
77 (
78 clamp((x0 + b - 1).div_euclid(b) - self.gx, self.bw),
79 clamp((y0 + b - 1).div_euclid(b) - self.gy, self.bh),
80 clamp(x1.div_euclid(b) - self.gx, self.bw),
81 clamp(y1.div_euclid(b) - self.gy, self.bh),
82 )
83 }
84}
85
86fn view(
90 frame: &Frame<'_>,
91 footprint: &Footprint<'_>,
92 tiers: &TierMap<'_>,
93 pose: (i64, i64),
94 wx0: i64,
95 wy0: i64,
96 out: &mut [[u8; 3]; CELLS],
97) -> Option<u8> {
98 let (sx0, sy0) = (wx0 - pose.0, wy0 - pose.1);
99 let (lx0, ly0) = (sx0 - footprint.x, sy0 - footprint.y);
100 let b = BLOCK as i64;
101 if sx0 < 0 || sy0 < 0 || sx0 + b > frame.width as i64 || sy0 + b > frame.height as i64 {
102 return None;
103 }
104 if lx0 < 0 || ly0 < 0 || lx0 + b > footprint.width as i64 || ly0 + b > footprint.height as i64 {
105 return None;
106 }
107 debug_assert_eq!((tiers.x, tiers.y), (footprint.x, footprint.y));
108 let (w, fw) = (frame.width, footprint.width);
109 let (lx0, ly0, sx0, sy0) = (lx0 as usize, ly0 as usize, sx0 as usize, sy0 as usize);
110 if (0..BLOCK).any(|yy| footprint.mask[(ly0 + yy) * fw + lx0..][..BLOCK].contains(&0)) {
111 return None;
112 }
113 let q = (CELL * CELL) as u32;
115 for (cy, cells) in out.chunks_exact_mut(SIDE).enumerate() {
116 let top = &frame.rgba[((sy0 + cy * CELL) * w + sx0) * 4..][..BLOCK * 4];
117 let bottom = &frame.rgba[((sy0 + cy * CELL + 1) * w + sx0) * 4..][..BLOCK * 4];
118 for ((cell, t), b) in cells
119 .iter_mut()
120 .zip(top.chunks_exact(8))
121 .zip(bottom.chunks_exact(8))
122 {
123 let mean = |c: usize| {
124 ((t[c] as u32 + t[c + 4] as u32 + b[c] as u32 + b[c + 4] as u32 + q / 2) / q) as u8
125 };
126 *cell = [mean(0), mean(1), mean(2)];
127 }
128 }
129 let mut worst = TIER_CLEAN;
131 for ty in ly0 / CELL..=(ly0 + BLOCK - 1) / CELL {
132 let row = &tiers.tier[ty * tiers.cw..(ty + 1) * tiers.cw];
133 for &t in &row[lx0 / CELL..=(lx0 + BLOCK - 1) / CELL] {
134 worst = worst.min(t);
135 }
136 }
137 Some(worst)
138}
139
140const OUTLIER: i32 = 48;
144
145#[inline]
146fn cell_luma(c: [u8; 3]) -> i32 {
147 super::luma(c[0], c[1], c[2]) as i32
148}
149
150fn compare(a: &[[u8; 3]], b: &[[u8; 3]]) -> (u32, u32, u32) {
152 let (mut sum, mut bias, mut off) = (0u32, 0i32, 0u32);
153 for (p, q) in a.iter().zip(b) {
154 for c in 0..3 {
155 let d = p[c] as i32 - q[c] as i32;
156 sum += d.unsigned_abs();
157 bias += d;
158 }
159 off += ((cell_luma(*p) - cell_luma(*q)).abs() > OUTLIER) as u32;
160 }
161 let n = (a.len() * 3) as u32;
162 (sum / n, bias.unsigned_abs() / n, off)
163}
164
165#[inline]
168fn agrees(d: (u32, u32, u32), tau: i32) -> bool {
169 d.0 <= (tau as u32 / 4).max(2) && d.1 <= 2 && d.2 <= 2
170}
171
172#[inline]
175fn agrees_strictly(d: (u32, u32, u32), tau: i32) -> bool {
176 agrees(d, tau) && d.2 == 0
177}
178
179const CHANGED: u32 = 8;
181const SHIFTED: u32 = 3;
182
183pub struct Consensus {
186 pub grid: Grid,
187 pub sig: Vec<[u8; 3]>,
188 count: Vec<i16>,
189 pub vote: Vec<BlockVote>,
190}
191
192impl Consensus {
193 pub fn new(grid: Grid) -> Self {
194 let n = grid.bw * grid.bh;
195 Self {
196 grid,
197 sig: vec![[0; 3]; n * CELLS],
198 count: vec![0; n * CELLS],
199 vote: vec![
200 BlockVote {
201 tier: -1,
202 clean_views: 0,
203 changed_views: 0
204 };
205 n
206 ],
207 }
208 }
209
210 pub fn observe(
212 &mut self,
213 frame: &Frame<'_>,
214 footprint: &Footprint<'_>,
215 tiers: &TierMap<'_>,
216 pose: (i64, i64),
217 ) {
218 let (bx0, by0, bx1, by1) = self.grid.span(footprint, pose);
219 if bx1 <= bx0 || by1 <= by0 {
220 return;
221 }
222 let rows = by1 - by0;
223 let chunks = crate::kernels::pool::chunks_for(rows * (bx1 - bx0), 64);
224 let grid = self.grid;
225 let (sig_p, count_p, vote_p) = (
226 crate::kernels::pool::SyncPtr(self.sig.as_mut_ptr()),
227 crate::kernels::pool::SyncPtr(self.count.as_mut_ptr()),
228 crate::kernels::pool::SyncPtr(self.vote.as_mut_ptr()),
229 );
230 crate::kernels::pool::par_for(chunks, |k| {
231 let mut sig = [[0u8; 3]; CELLS];
232 for by in by0 + crate::kernels::pool::split(rows, chunks, k)
233 ..by0 + crate::kernels::pool::split(rows, chunks, k + 1)
234 {
235 for bx in bx0..bx1 {
236 let b = by * grid.bw + bx;
237 let (wx0, wy0) = grid.origin(b);
238 let Some(tier) = view(frame, footprint, tiers, pose, wx0, wy0, &mut sig) else {
239 continue;
240 };
241 let (cand, count, v) = unsafe {
243 (
244 std::slice::from_raw_parts_mut(sig_p.get().add(b * CELLS), CELLS),
245 std::slice::from_raw_parts_mut(count_p.get().add(b * CELLS), CELLS),
246 &mut *vote_p.get().add(b),
247 )
248 };
249 vote(cand, count, v, &sig, tier);
250 }
251 }
252 });
253 }
254}
255
256fn vote(
258 cand: &mut [[u8; 3]],
259 count: &mut [i16],
260 v: &mut BlockVote,
261 sig: &[[u8; 3]; CELLS],
262 tier: u8,
263) {
264 if tier == TIER_CLEAN {
265 if v.tier == TIER_CLEAN as i8 {
266 let d = compare(sig, cand);
267 if d.0 > CHANGED || d.1 > SHIFTED {
268 v.changed_views = v.changed_views.saturating_add(1);
269 }
270 }
271 v.clean_views = v.clean_views.saturating_add(1);
272 }
273 let t = tier as i8;
274 if t > v.tier {
275 cand.copy_from_slice(sig);
276 count.fill(1);
277 v.tier = t;
278 } else if t == v.tier {
279 for k in 0..CELLS {
282 if (cell_luma(sig[k]) - cell_luma(cand[k])).abs() <= OUTLIER {
283 count[k] = count[k].saturating_add(1);
284 } else if count[k] <= 1 {
285 cand[k] = sig[k];
286 count[k] = 1;
287 } else {
288 count[k] -= 1;
289 }
290 }
291 }
292}
293
294impl CellRef for Consensus {
295 #[inline]
296 fn cell(&self, cx: i64, cy: i64) -> Option<[u8; 3]> {
297 let (wx, wy) = (cx * CELL as i64, cy * CELL as i64);
298 let (bx, by) = (
299 wx.div_euclid(BLOCK as i64) - self.grid.gx,
300 wy.div_euclid(BLOCK as i64) - self.grid.gy,
301 );
302 if bx < 0 || by < 0 || bx >= self.grid.bw as i64 || by >= self.grid.bh as i64 {
303 return None;
304 }
305 let b = by as usize * self.grid.bw + bx as usize;
306 if self.vote[b].tier < 0 {
307 return None;
308 }
309 let (ix, iy) = (
310 (wx.rem_euclid(BLOCK as i64) as usize) / CELL,
311 (wy.rem_euclid(BLOCK as i64) as usize) / CELL,
312 );
313 Some(self.sig[b * CELLS + iy * SIDE + ix])
314 }
315 fn cells(&self) -> (i64, i64, i64, i64) {
316 let s = SIDE as i64;
317 (
318 self.grid.gx * s,
319 self.grid.gy * s,
320 (self.grid.gx + self.grid.bw as i64) * s,
321 (self.grid.gy + self.grid.bh as i64) * s,
322 )
323 }
324}
325
326pub struct Component {
328 pub bx: usize,
329 pub by: usize,
330 pub bw: usize,
331 pub bh: usize,
332 pub mask: Vec<bool>,
333 pub dynamic: Vec<bool>,
335 pub best: Option<(usize, usize, usize)>,
337}
338
339pub fn components(c: &Consensus) -> Vec<Component> {
341 let (bw, bh) = (c.grid.bw, c.grid.bh);
342 let dynamic: Vec<bool> = c
343 .vote
344 .iter()
345 .map(|v| v.changed_views >= 2 && v.changed_views as u32 * 4 >= v.clean_views as u32)
346 .collect();
347 let mut grown = vec![false; bw * bh];
348 for y in 0..bh {
349 for x in 0..bw {
350 if dynamic[y * bw + x] {
351 for yy in y.saturating_sub(1)..=(y + 1).min(bh - 1) {
352 for xx in x.saturating_sub(1)..=(x + 1).min(bw - 1) {
353 grown[yy * bw + xx] = true;
354 }
355 }
356 }
357 }
358 }
359 let mut label = vec![0usize; bw * bh];
360 let mut out = Vec::new();
361 for s in 0..bw * bh {
362 if !grown[s] || label[s] != 0 {
363 continue;
364 }
365 let id = out.len() + 1;
366 let mut stack = vec![s];
367 label[s] = id;
368 let mut members = Vec::new();
369 while let Some(i) = stack.pop() {
370 members.push(i);
371 let (x, y) = (i % bw, i / bw);
372 for yy in y.saturating_sub(1)..=(y + 1).min(bh - 1) {
373 for xx in x.saturating_sub(1)..=(x + 1).min(bw - 1) {
374 let j = yy * bw + xx;
375 if grown[j] && label[j] == 0 {
376 label[j] = id;
377 stack.push(j);
378 }
379 }
380 }
381 }
382 let x0 = members.iter().map(|&i| i % bw).min().unwrap();
383 let x1 = members.iter().map(|&i| i % bw).max().unwrap();
384 let y0 = members.iter().map(|&i| i / bw).min().unwrap();
385 let y1 = members.iter().map(|&i| i / bw).max().unwrap();
386 let (cw, ch) = (x1 - x0 + 1, y1 - y0 + 1);
387 let mut mask = vec![false; cw * ch];
388 let mut changing = vec![false; cw * ch];
389 for &i in &members {
390 let k = (i / bw - y0) * cw + (i % bw - x0);
391 mask[k] = true;
392 changing[k] = dynamic[i];
393 }
394 out.push(Component {
395 bx: x0,
396 by: y0,
397 bw: cw,
398 bh: ch,
399 mask,
400 dynamic: changing,
401 best: None,
402 });
403 }
404 out
405}
406
407#[derive(Clone, Copy, Debug, PartialEq, Eq)]
409#[repr(u8)]
410pub enum Kind {
411 None = 0,
413 Coherent = 1,
415 Component = 2,
417 Provisional = 3,
419 Fallback = 4,
422 Edge = 5,
425}
426
427pub const CENTRAL: i64 = 48;
430
431#[derive(Clone, Copy, Debug, PartialEq, Eq)]
433pub struct BlockWrite {
434 pub canvas: u32,
436 pub gx: i64,
437 pub gy: i64,
438 pub sx: i64,
439 pub sy: i64,
440 pub frame: u32,
441 pub kind: Kind,
442}
443
444pub const UNTOUCHED: i32 = -1;
447pub const MIXED: i32 = -2;
449
450pub struct Deferred {
451 pub gx: i64,
452 pub gy: i64,
453 pub frame: u32,
454 pub kind: Kind,
455 pub rgba: Box<[u8; PIXELS * 4]>,
456}
457
458struct Fallback {
459 cand: [[u8; 3]; PIXELS],
460 count: [i16; PIXELS],
461 tier: [i8; PIXELS],
462 main: [[u8; 4]; PIXELS],
464 main_tier: [i8; PIXELS],
465}
466
467#[derive(Clone, Copy, Default)]
469struct Act {
470 seen: bool,
471 tier: u8,
472 strict: bool,
473 loose: bool,
474 inset: i16,
475 main: bool,
477}
478
479fn fallback_view(
483 fb: &mut Fallback,
484 frame: &Frame<'_>,
485 footprint: &Footprint<'_>,
486 tiers: &TierMap<'_>,
487 pose: (i64, i64),
488 origin: (i64, i64),
489 tau: i32,
490) {
491 let (w, near) = (frame.width, (tau / 2).max(2));
492 for j in 0..PIXELS {
493 let (sx, sy) = (
494 origin.0 + (j % BLOCK) as i64 - pose.0,
495 origin.1 + (j / BLOCK) as i64 - pose.1,
496 );
497 if sx < 0
498 || sy < 0
499 || sx >= w as i64
500 || sy >= frame.height as i64
501 || !footprint.contains(sx, sy)
502 {
503 continue;
504 }
505 let fi = (sy as usize * w + sx as usize) * 4;
506 let obs = [frame.rgba[fi], frame.rgba[fi + 1], frame.rgba[fi + 2]];
507 let t = tiers.at(sx, sy) as i8;
508 let m = fb.main[j];
509 if m[3] == 255 && (0..3).all(|k| (obs[k] as i32 - m[k] as i32).abs() <= near) {
510 fb.main_tier[j] = fb.main_tier[j].max(t);
511 }
512 if t > fb.tier[j] {
513 fb.cand[j] = obs;
514 fb.count[j] = 1;
515 fb.tier[j] = t;
516 } else if t == fb.tier[j] {
517 if (0..3).all(|k| (obs[k] as i32 - fb.cand[j][k] as i32).abs() <= tau) {
518 fb.cand[j] = obs;
519 fb.count[j] = fb.count[j].saturating_add(1);
520 } else if fb.count[j] <= 1 {
521 fb.cand[j] = obs;
522 fb.count[j] = 1;
523 } else {
524 fb.count[j] -= 1;
525 }
526 }
527 }
528}
529
530pub struct Output {
532 acts: Vec<Act>,
533 pub kind: Vec<Kind>,
534 pub frame: Vec<i32>,
535 component: Vec<i32>,
536 provisional: Vec<i8>,
537 rank: Vec<(i8, bool, bool, i16)>,
539 held: Vec<Option<Box<[u8; PIXELS * 3]>>>,
540 main_tier: Vec<i8>,
541 fallback: Vec<Option<Box<Fallback>>>,
542 pub components: Vec<Component>,
543}
544
545pub const CONSENSUS_BLOCK_BYTES: usize = CELLS
547 * (std::mem::size_of::<[u8; 3]>() + std::mem::size_of::<i16>())
548 + std::mem::size_of::<BlockVote>();
549pub const OUTPUT_BLOCK_BYTES: usize = std::mem::size_of::<Act>()
552 + std::mem::size_of::<Kind>()
553 + 2 * std::mem::size_of::<i32>()
554 + std::mem::size_of::<i8>()
555 + std::mem::size_of::<(i8, bool, bool, i16)>()
556 + std::mem::size_of::<Option<Box<[u8; PIXELS * 3]>>>()
557 + PIXELS * 3
558 + std::mem::size_of::<i8>()
559 + std::mem::size_of::<Option<Box<Fallback>>>();
560pub const FALLBACK_BYTES: usize = std::mem::size_of::<Fallback>();
562
563impl Consensus {
564 pub fn unseen_blocks(&self) -> usize {
567 self.vote.iter().filter(|v| v.clean_views == 0).count()
568 }
569}
570
571impl Output {
572 pub fn new(c: &Consensus, components: Vec<Component>) -> Self {
575 let n = c.grid.bw * c.grid.bh;
576 let mut component = vec![-1; n];
577 for (k, comp) in components.iter().enumerate() {
578 for yy in 0..comp.bh {
579 for xx in 0..comp.bw {
580 if comp.mask[yy * comp.bw + xx] {
581 component[(comp.by + yy) * c.grid.bw + comp.bx + xx] = k as i32;
582 }
583 }
584 }
585 }
586 let fallback = c
587 .vote
588 .iter()
589 .zip(&component)
590 .map(|(v, &k)| {
591 (v.clean_views == 0 && k < 0).then(|| {
592 Box::new(Fallback {
593 cand: [[0; 3]; PIXELS],
594 count: [0; PIXELS],
595 tier: [-1; PIXELS],
596 main: [[0; 4]; PIXELS],
597 main_tier: [-1; PIXELS],
598 })
599 })
600 })
601 .collect();
602 Self {
603 acts: vec![Act::default(); n],
604 kind: vec![Kind::None; n],
605 frame: vec![-1; n],
606 component,
607 provisional: vec![-1; n],
608 rank: vec![(-1, false, false, -1); n],
609 held: (0..n).map(|_| None).collect(),
610 main_tier: vec![-1; n],
611 fallback,
612 components,
613 }
614 }
615
616 pub fn fallback_blocks(&self) -> Vec<usize> {
618 self.fallback
619 .iter()
620 .enumerate()
621 .filter(|(_, f)| f.is_some())
622 .map(|(b, _)| b)
623 .collect()
624 }
625
626 pub fn set_main(&mut self, b: usize, rgba: &[u8]) {
628 if let Some(fb) = self.fallback.get_mut(b).and_then(|f| f.as_mut()) {
629 for (j, px) in rgba.chunks_exact(4).take(PIXELS).enumerate() {
630 fb.main[j] = [px[0], px[1], px[2], px[3]];
631 }
632 }
633 }
634
635 #[allow(clippy::too_many_arguments)]
638 pub fn emit(
639 &mut self,
640 c: &Consensus,
641 frame: &Frame<'_>,
642 footprint: &Footprint<'_>,
643 tiers: &TierMap<'_>,
644 pose: (i64, i64),
645 tau: i32,
646 frame_index: u32,
647 main: &(dyn CellRef + Sync),
648 writes: &mut Vec<BlockWrite>,
649 ) {
650 let grid = c.grid;
651 let mut sig = [[0u8; 3]; CELLS];
652 let block_write = |b: usize, kind: Kind| {
653 let (wx0, wy0) = grid.origin(b);
654 BlockWrite {
655 canvas: 0,
656 gx: grid.gx + (b % grid.bw) as i64,
657 gy: grid.gy + (b / grid.bw) as i64,
658 sx: wx0 - pose.0,
659 sy: wy0 - pose.1,
660 frame: frame_index,
661 kind,
662 }
663 };
664 for comp in self.components.iter_mut() {
667 let mut covered = Vec::new();
668 let mut tainted = false;
672 for yy in 0..comp.bh {
673 for xx in 0..comp.bw {
674 let k = yy * comp.bw + xx;
675 if !comp.mask[k] {
676 continue;
677 }
678 let b = (comp.by + yy) * grid.bw + comp.bx + xx;
679 let (wx0, wy0) = grid.origin(b);
680 if let Some(t) = view(frame, footprint, tiers, pose, wx0, wy0, &mut sig) {
681 if t >= TIER_VEIL {
682 if !comp.dynamic[k] {
683 let off = compare(&sig, &c.sig[b * CELLS..(b + 1) * CELLS]).2;
684 tainted |= (1..=CELLS as u32 * 3 / 8).contains(&off);
685 }
686 covered.push(b);
687 }
688 }
689 }
690 }
691 if covered.is_empty() {
692 continue;
693 }
694 let blocks = comp.mask.iter().filter(|&&m| m).count();
695 let eligible = covered.len() * 10 >= blocks * 9;
696 let score = if eligible {
698 (1, !tainted as usize, frame_index as usize)
699 } else {
700 (0, 0, covered.len())
701 };
702 if comp.best.is_some_and(|best| best > score) {
703 continue;
704 }
705 comp.best = Some(score);
706 for b in covered {
707 self.kind[b] = Kind::Component;
708 self.frame[b] = frame_index as i32;
709 writes.push(block_write(b, Kind::Component));
710 }
711 }
712 let (bx0, by0, bx1, by1) = grid.span(footprint, pose);
713 let w = frame.width;
714 let (fx0, fy0, fx1, fy1) = {
716 let b = BLOCK as i64;
717 let (x0, y0) = (footprint.x + pose.0, footprint.y + pose.1);
718 let (x1, y1) = (x0 + footprint.width as i64, y0 + footprint.height as i64);
719 let clamp = |v: i64, n: usize| v.clamp(0, n as i64) as usize;
720 (
721 clamp(x0.div_euclid(b) - grid.gx, grid.bw),
722 clamp(y0.div_euclid(b) - grid.gy, grid.bh),
723 clamp((x1 + b - 1).div_euclid(b) - grid.gx, grid.bw),
724 clamp((y1 + b - 1).div_euclid(b) - grid.gy, grid.bh),
725 )
726 };
727 let (kind_p, fallback_base) = (
729 crate::kernels::pool::SyncPtr(self.kind.as_mut_ptr()),
730 self.fallback.as_mut_ptr() as usize,
731 );
732 if fy1 > fy0 && fx1 > fx0 {
733 let rows = fy1 - fy0;
734 let chunks = crate::kernels::pool::chunks_for(rows * (fx1 - fx0), 64);
735 crate::kernels::pool::par_for(chunks, |k| {
736 for by in fy0 + crate::kernels::pool::split(rows, chunks, k)
737 ..fy0 + crate::kernels::pool::split(rows, chunks, k + 1)
738 {
739 for bx in fx0..fx1 {
740 let b = by * grid.bw + bx;
741 let (slot, kind) = unsafe {
743 (
744 &mut *(fallback_base as *mut Option<Box<Fallback>>).add(b),
745 &mut *kind_p.get().add(b),
746 )
747 };
748 let Some(fb) = slot.as_mut() else { continue };
749 *kind = Kind::Fallback;
750 fallback_view(fb, frame, footprint, tiers, pose, grid.origin(b), tau);
751 }
752 }
753 });
754 }
755 if by1 <= by0 || bx1 <= bx0 {
756 return;
757 }
758 let rows = by1 - by0;
760 let chunks = crate::kernels::pool::chunks_for(rows * (bx1 - bx0), 64);
761 let acts = crate::kernels::pool::SyncPtr(self.acts.as_mut_ptr());
762 let (component, fallback, kind) = (&self.component, &self.fallback, &self.kind);
763 crate::kernels::pool::par_for(chunks, |k| {
764 let mut sig = [[0u8; 3]; CELLS];
765 for by in by0 + crate::kernels::pool::split(rows, chunks, k)
766 ..by0 + crate::kernels::pool::split(rows, chunks, k + 1)
767 {
768 for bx in bx0..bx1 {
769 let b = by * grid.bw + bx;
770 let act = unsafe { &mut *acts.get().add(b) };
772 *act = Act::default();
773 if component[b] >= 0 || fallback[b].is_some() || kind[b] == Kind::Coherent {
774 continue;
775 }
776 let (wx0, wy0) = grid.origin(b);
777 let Some(tier) = view(frame, footprint, tiers, pose, wx0, wy0, &mut sig) else {
778 continue;
779 };
780 let d = compare(&sig, &c.sig[b * CELLS..(b + 1) * CELLS]);
781 let (sx0, sy0) = (wx0 - pose.0, wy0 - pose.1);
782 let inset = (sx0 - footprint.x)
783 .min(sy0 - footprint.y)
784 .min(footprint.x + footprint.width as i64 - sx0 - BLOCK as i64)
785 .min(footprint.y + footprint.height as i64 - sy0 - BLOCK as i64);
786 let (cx0, cy0) = (wx0.div_euclid(CELL as i64), wy0.div_euclid(CELL as i64));
788 let mut msig = [[0u8; 3]; CELLS];
789 let known = (0..CELLS).all(|k| {
790 match main.cell(cx0 + (k % SIDE) as i64, cy0 + (k / SIDE) as i64) {
791 Some(v) => {
792 msig[k] = v;
793 true
794 }
795 None => false,
796 }
797 });
798 *act = Act {
799 seen: true,
800 tier,
801 strict: agrees_strictly(d, tau),
802 loose: agrees(d, tau),
803 inset: inset.min(CENTRAL) as i16,
804 main: known && agrees(compare(&sig, &msig), tau),
805 };
806 }
807 }
808 });
809 for by in by0..by1 {
810 for bx in bx0..bx1 {
811 let b = by * grid.bw + bx;
812 let a = self.acts[b];
813 if !a.seen {
814 continue;
815 }
816 if a.tier == TIER_CLEAN && a.strict && a.inset >= CENTRAL as i16 {
817 self.kind[b] = Kind::Coherent;
818 self.frame[b] = frame_index as i32;
819 self.held[b] = None;
820 writes.push(block_write(b, Kind::Coherent));
821 continue;
822 }
823 if a.main {
824 self.main_tier[b] = self.main_tier[b].max(a.tier as i8);
825 }
826 let rank = (
828 a.tier as i8,
829 a.tier == TIER_CLEAN && a.strict,
830 a.tier == TIER_CLEAN && a.loose,
831 a.inset,
832 );
833 if rank >= self.rank[b] {
834 let held = self.held[b].get_or_insert_with(|| Box::new([0u8; PIXELS * 3]));
835 let (wx0, wy0) = grid.origin(b);
836 for j in 0..PIXELS {
837 let (sx, sy) = (
838 wx0 + (j % BLOCK) as i64 - pose.0,
839 wy0 + (j / BLOCK) as i64 - pose.1,
840 );
841 let fi = (sy as usize * w + sx as usize) * 4;
842 held[j * 3..j * 3 + 3].copy_from_slice(&frame.rgba[fi..fi + 3]);
843 }
844 self.rank[b] = rank;
845 self.kind[b] = if rank.2 {
846 Kind::Edge
847 } else {
848 Kind::Provisional
849 };
850 self.provisional[b] = a.tier as i8;
851 self.frame[b] = frame_index as i32;
852 }
853 }
854 }
855 }
856
857 pub fn sources(&self, c: &Consensus, deferred: &[Deferred]) -> Vec<i32> {
861 let grid = c.grid;
862 let mut out: Vec<i32> = self
863 .kind
864 .iter()
865 .zip(&self.frame)
866 .map(|(k, &f)| match k {
867 Kind::Coherent | Kind::Component => f,
868 _ => UNTOUCHED,
869 })
870 .collect();
871 for d in deferred {
872 let b = (d.gy - grid.gy) as usize * grid.bw + (d.gx - grid.gx) as usize;
873 out[b] = if d.kind == Kind::Fallback {
874 MIXED
875 } else {
876 d.frame as i32
877 };
878 }
879 out
880 }
881
882 pub fn finish(&self, c: &Consensus, tau: i32) -> Vec<Deferred> {
885 let grid = c.grid;
886 let near = (tau / 2).max(2);
887 let mut out = Vec::new();
888 for b in 0..grid.bw * grid.bh {
889 let (gx, gy) = (
890 grid.gx + (b % grid.bw) as i64,
891 grid.gy + (b / grid.bw) as i64,
892 );
893 match self.kind[b] {
894 Kind::Edge => {
895 let Some(h) = &self.held[b] else { continue };
896 if c.vote[b].clean_views < 2 && self.main_tier[b] >= self.provisional[b] {
900 continue;
901 }
902 let mut rgba = Box::new([0u8; PIXELS * 4]);
903 for j in 0..PIXELS {
904 rgba[j * 4..j * 4 + 3].copy_from_slice(&h[j * 3..j * 3 + 3]);
905 rgba[j * 4 + 3] = 255;
906 }
907 out.push(Deferred {
908 gx,
909 gy,
910 frame: self.frame[b] as u32,
911 kind: Kind::Edge,
912 rgba,
913 });
914 }
915 Kind::Provisional => {
916 let Some(h) = &self.held[b] else { continue };
917 if self.main_tier[b] >= self.provisional[b] {
918 continue;
919 }
920 let mut rgba = Box::new([0u8; PIXELS * 4]);
921 for j in 0..PIXELS {
922 rgba[j * 4..j * 4 + 3].copy_from_slice(&h[j * 3..j * 3 + 3]);
923 rgba[j * 4 + 3] = 255;
924 }
925 out.push(Deferred {
926 gx,
927 gy,
928 frame: self.frame[b] as u32,
929 kind: Kind::Provisional,
930 rgba,
931 });
932 }
933 Kind::Fallback => {
934 let Some(fb) = &self.fallback[b] else {
935 continue;
936 };
937 let mut rgba = Box::new([0u8; PIXELS * 4]);
938 let mut any = false;
939 let same = |j: usize| {
944 let m = fb.main[j];
945 m[3] == 255
946 && (0..3).all(|k| (fb.cand[j][k] as i32 - m[k] as i32).abs() <= near)
947 };
948 if !(0..PIXELS)
949 .any(|j| fb.tier[j] >= 0 && fb.tier[j] > fb.main_tier[j] && !same(j))
950 {
951 continue;
952 }
953 for j in 0..PIXELS {
954 if fb.tier[j] < 0 || (fb.main_tier[j] >= fb.tier[j] && !same(j)) {
955 continue;
956 }
957 rgba[j * 4..j * 4 + 3].copy_from_slice(&fb.cand[j]);
958 rgba[j * 4 + 3] = 255;
959 any = true;
960 }
961 if any {
962 out.push(Deferred {
963 gx,
964 gy,
965 frame: 0,
966 kind: Kind::Fallback,
967 rgba,
968 });
969 }
970 }
971 _ => {}
972 }
973 }
974 out
975 }
976}