Skip to main content

long_screen/kernels/reveal/
blocks.rs

1//! Block-level selection on the world-aligned 16×16 grid (the compositor's quality blocks). What stays resident
2//! per canvas is a few hundred bytes per block, so a canvas of any size fits without paging; pixels are only
3//! ever copied from one decoded frame into the output.
4//!
5//! [`Consensus`] (pass 2): every whole-block view votes with its signature (the block's 8×8 grid of 2×2 cell
6//! means) in a tolerant Boyer–Moore majority within the best tier seen. One clean sighting beats any number of
7//! covered ones, a passing cursor is outvoted, and a tie goes to the later view (content that loads or fades
8//! settles in its final state). The signatures are also the reference of the second statistics pass.
9//!
10//! [`Output`] (pass 4): a block takes a clean, central view whose agreement is supported by local reference
11//! structure or a corroborated weak change. Near-edge views are held until a better observation arrives;
12//! a changing object takes a complete, corroborated moment where possible. Context is assigned after finding
13//! changing objects, so nearby independent objects do not become an unobservable giant component; a block with no clean agreeing view keeps the compositor's pixels unless a view
14//! of a better tier contradicts them.
15
16use super::locate::TierMap;
17use super::stats::CellRef;
18use super::{Footprint, Frame, BLOCK, CELL, TIER_CLEAN, TIER_VEIL};
19
20/// Cells per block side and per block.
21pub const SIDE: usize = BLOCK / CELL;
22pub const CELLS: usize = SIDE * SIDE;
23// `view` reads each cell as two pixels from each of two rows.
24const _: () = assert!(CELL == 2);
25const PIXELS: usize = BLOCK * BLOCK;
26
27#[derive(Clone, Copy)]
28pub struct BlockVote {
29    /// Best tier seen whole (-1 = never).
30    pub tier: i8,
31    pub clean_views: u16,
32    pub changed_views: u16,
33}
34
35/// The world-aligned block grid covering one canvas.
36#[derive(Clone, Copy, Debug)]
37pub struct Grid {
38    /// World block index of the grid's first block.
39    pub gx: i64,
40    pub gy: i64,
41    pub bw: usize,
42    pub bh: usize,
43}
44
45impl Grid {
46    /// Blocks covering the world rectangle (x, y, width, height).
47    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    /// World origin of block `b`.
63    #[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    /// Blocks whose whole area may lie in a footprint at `pose`: (bx0, by0, bx1, by1), half-open.
72    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
86/// Signature of the block at world (wx0, wy0) as seen in `frame` at `pose`, and its tier (worst cell). `None` when
87/// any pixel of the block is outside the footprint. The footprint mask and the tier map share the layer rectangle's
88/// origin, so both are read by row with one index each.
89fn 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    // Each 2×2 cell straight from its two rows: the same sums and rounding as accumulating pixel by pixel.
114    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    // The block's pixels fall in these tier cells (one more per axis when the block starts mid-cell).
130    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
140/// A cell whose luma is this far off is an outlier: a transient (a cursor tip), not codec noise. Luma is decoded at
141/// full resolution, so chroma subsampling, which shifts the colour of a sharp edge with the pose's parity, never
142/// makes an outlier.
143const 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
150/// (mean |Δ| per channel, |mean signed Δ| per channel, cells whose luma is off by more than [`OUTLIER`]).
151fn 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/// Two views agree when they differ by no more than codec noise on average, without a systematic shift (a
166/// dimming, a highlight), and in at most a couple of cells (codec ringing at a sharp edge).
167#[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/// Agreement without absolute outliers or unsupported structure in an observed reference neighbourhood.
173/// A weak transient on a smooth page must not become permanent just because it fits the absolute noise ceiling.
174#[inline]
175fn agrees_strictly(
176    d: (u32, u32, u32),
177    tau: i32,
178    observed: &BlockSignature,
179    reference: &impl CellRef,
180    world: (i64, i64),
181) -> bool {
182    if !agrees(d, tau) || d.2 != 0 {
183        return false;
184    }
185    // A small residual may be sampling phase at a genuine edge, but it cannot create
186    // structure on a smooth page. Retain the existing absolute outlier bound and use
187    // twice the mean-agreement noise floor where the reference has less contrast.
188    let noise = (tau / 4).max(2);
189    let quiet_limit = 2 * noise;
190    if quiet_limit >= OUTLIER {
191        return true;
192    }
193    let (cx, cy) = (
194        world.0.div_euclid(CELL as i64),
195        world.1.div_euclid(CELL as i64),
196    );
197    for (k, sample) in observed.iter().enumerate() {
198        let (x, y) = (cx + (k % SIDE) as i64, cy + (k / SIDE) as i64);
199        let Some(value) = reference.cell(x, y) else {
200            return false;
201        };
202        let value = cell_luma(value);
203        let residual = (cell_luma(*sample) - value).abs();
204        if residual <= quiet_limit {
205            continue;
206        }
207        let mut contrast = 0;
208        // Read the world reference across block boundaries. An unknown neighbour
209        // cannot prove flatness, so it leaves the existing absolute bound in force.
210        'neighbours: for yy in y - 1..=y + 1 {
211            for xx in x - 1..=x + 1 {
212                let Some(neighbour) = reference.cell(xx, yy) else {
213                    contrast = OUTLIER;
214                    break 'neighbours;
215                };
216                contrast = contrast.max((cell_luma(neighbour) - value).abs());
217            }
218        }
219        if residual > quiet_limit.max(contrast + noise) {
220            return false;
221        }
222    }
223    true
224}
225
226/// A whole clean view counts as a structural change (dynamic content) beyond this mean or shift.
227const CHANGED: u32 = 8;
228const SHIFTED: u32 = 3;
229
230/// Per canvas: consensus signatures and vote state. Each cell keeps its own majority, so a transient that touches
231/// only a few cells of an otherwise agreeing view (a cursor tip) is outvoted there.
232pub struct Consensus {
233    pub grid: Grid,
234    pub sig: Vec<[u8; 3]>,
235    count: Vec<i16>,
236    pub vote: Vec<BlockVote>,
237    /// Raw changes that also admit a small rigid phase interpretation.
238    phase_explained: Vec<u16>,
239    /// Upper bounds on observed viewport extents. Fitting does not prove co-visibility;
240    /// exceeding either bound proves that a whole-object moment is impossible.
241    viewport: (usize, usize),
242}
243
244impl Consensus {
245    pub fn new(grid: Grid) -> Self {
246        let n = grid.bw * grid.bh;
247        Self {
248            grid,
249            sig: vec![[0; 3]; n * CELLS],
250            count: vec![0; n * CELLS],
251            phase_explained: vec![0; n],
252            viewport: (0, 0),
253            vote: vec![
254                BlockVote {
255                    tier: -1,
256                    clean_views: 0,
257                    changed_views: 0
258                };
259                n
260            ],
261        }
262    }
263
264    /// One vote (a distinct pose, its last frame).
265    pub fn observe(
266        &mut self,
267        frame: &Frame<'_>,
268        footprint: &Footprint<'_>,
269        tiers: &TierMap<'_>,
270        pose: (i64, i64),
271    ) {
272        let width = ((footprint.x + footprint.width as i64).min(frame.width as i64)
273            - footprint.x.max(0))
274        .max(0) as usize;
275        let height = ((footprint.y + footprint.height as i64).min(frame.height as i64)
276            - footprint.y.max(0))
277        .max(0) as usize;
278        self.viewport.0 = self.viewport.0.max(width);
279        self.viewport.1 = self.viewport.1.max(height);
280        let (bx0, by0, bx1, by1) = self.grid.span(footprint, pose);
281        if bx1 <= bx0 || by1 <= by0 {
282            return;
283        }
284        let rows = by1 - by0;
285        let chunks = crate::kernels::pool::chunks_for(rows * (bx1 - bx0), 64);
286        let grid = self.grid;
287        let (sig_p, count_p, vote_p, phase_p) = (
288            crate::kernels::pool::SyncPtr(self.sig.as_mut_ptr()),
289            crate::kernels::pool::SyncPtr(self.count.as_mut_ptr()),
290            crate::kernels::pool::SyncPtr(self.vote.as_mut_ptr()),
291            crate::kernels::pool::SyncPtr(self.phase_explained.as_mut_ptr()),
292        );
293        crate::kernels::pool::par_for(chunks, |k| {
294            let mut sig = [[0u8; 3]; CELLS];
295            for by in by0 + crate::kernels::pool::split(rows, chunks, k)
296                ..by0 + crate::kernels::pool::split(rows, chunks, k + 1)
297            {
298                for bx in bx0..bx1 {
299                    let b = by * grid.bw + bx;
300                    let (wx0, wy0) = grid.origin(b);
301                    let Some(tier) = view(frame, footprint, tiers, pose, wx0, wy0, &mut sig) else {
302                        continue;
303                    };
304                    // SAFETY: chunk `k` owns block rows by0+split(k)..by0+split(k+1); each block's state is its own.
305                    let (cand, count, v) = unsafe {
306                        (
307                            std::slice::from_raw_parts_mut(sig_p.get().add(b * CELLS), CELLS),
308                            std::slice::from_raw_parts_mut(count_p.get().add(b * CELLS), CELLS),
309                            &mut *vote_p.get().add(b),
310                        )
311                    };
312                    if vote(cand, count, v, &sig, tier) {
313                        // SAFETY: this chunk owns the same block's phase counter as its vote.
314                        let explained = unsafe { &mut *phase_p.get().add(b) };
315                        *explained = explained.saturating_add(1);
316                    }
317                }
318            }
319        });
320    }
321}
322
323/// A sub-block rigid shift can be registration phase, not a changing object. Test one
324/// signature-cell displacement without changing source pixels or the world placement.
325/// The overlap must itself contain displacement evidence: cropping off a new edge mark
326/// and comparing two flat interiors does not demonstrate phase agreement.
327fn small_phase_agrees(a: &[[u8; 3]; CELLS], b: &[[u8; 3]]) -> bool {
328    for dy in -1isize..=1 {
329        for dx in -1isize..=1 {
330            if dx == 0 && dy == 0 {
331                continue;
332            }
333            let x0 = (-dx).max(0) as usize;
334            let y0 = (-dy).max(0) as usize;
335            let x1 = (SIDE as isize - dx).min(SIDE as isize) as usize;
336            let y1 = (SIDE as isize - dy).min(SIDE as isize) as usize;
337            let n = ((x1 - x0) * (y1 - y0) * 3) as u32;
338            let mut sum = 0u32;
339            let mut bias = 0i32;
340            let mut unshifted = 0u32;
341            'rows: for y in y0..y1 {
342                for x in x0..x1 {
343                    let p = a[y * SIDE + x];
344                    let q = b[((y as isize + dy) as usize) * SIDE + (x as isize + dx) as usize];
345                    for k in 0..3 {
346                        let d = p[k] as i32 - q[k] as i32;
347                        sum += d.unsigned_abs();
348                        bias += d;
349                        unshifted += (p[k] as i32 - b[y * SIDE + x][k] as i32).unsigned_abs();
350                    }
351                    if sum > CHANGED * n {
352                        break 'rows;
353                    }
354                }
355            }
356            if sum <= CHANGED * n && bias.unsigned_abs() <= SHIFTED * n && unshifted > CHANGED * n {
357                return true;
358            }
359        }
360    }
361    false
362}
363
364/// Whether a whole view of a block the consensus holds as static shows something else there: a luma outlier (a
365/// cursor tip, a status bubble), or what [`vote`] would count as a structural change that a small sampling phase
366/// does not explain. A component's moment that contradicts its static context has an object extending past the
367/// blocks the moment will write: a low-contrast card still growing when its outer rows left the viewport would
368/// otherwise be written cut along block boundaries, next to the consensus's view of the same rows. A structural
369/// difference the stationary run's previous frame shows as well (`settled`) is the context's settled state, not an
370/// object in motion: a scrollbar thumb present in most views but faded by the end of the recording.
371pub(super) fn contradicts(
372    sig: &[[u8; 3]; CELLS],
373    reference: &[[u8; 3]],
374    tau: i32,
375    settled: impl FnOnce() -> bool,
376) -> bool {
377    let d = compare(sig, reference);
378    if d.2 > 0 {
379        return true;
380    }
381    // Only a smooth reference proves a low-contrast object: on textured context (map imagery that refines as
382    // tiles load, a photo) a sub-outlier difference is as likely the same content resampled.
383    let (lo, hi) = reference.iter().fold((i32::MAX, i32::MIN), |(lo, hi), &c| {
384        let l = cell_luma(c);
385        (lo.min(l), hi.max(l))
386    });
387    hi - lo <= 2 * (tau / 4).max(2)
388        && (d.0 > CHANGED || d.1 > SHIFTED)
389        && !small_phase_agrees(sig, reference)
390        && !settled()
391}
392
393/// Whether the stationary run's previous frame shows block (wx0, wy0) as `sig` does, within codec noise.
394#[allow(clippy::too_many_arguments)]
395pub(super) fn settled(
396    sig: &[[u8; 3]; CELLS],
397    previous: Option<&Frame<'_>>,
398    footprint: &Footprint<'_>,
399    tiers: &TierMap<'_>,
400    pose: (i64, i64),
401    wx0: i64,
402    wy0: i64,
403    tau: i32,
404) -> bool {
405    let mut prior = [[0u8; 3]; CELLS];
406    previous
407        .and_then(|before| view(before, footprint, tiers, pose, wx0, wy0, &mut prior))
408        .is_some_and(|t| t >= TIER_VEIL && agrees(compare(sig, &prior), tau))
409}
410
411/// One whole-block view's vote.
412fn vote(
413    cand: &mut [[u8; 3]],
414    count: &mut [i16],
415    v: &mut BlockVote,
416    sig: &[[u8; 3]; CELLS],
417    tier: u8,
418) -> bool {
419    let mut phase_explained = false;
420    if tier == TIER_CLEAN {
421        if v.tier == TIER_CLEAN as i8 {
422            let d = compare(sig, cand);
423            if d.0 > CHANGED || d.1 > SHIFTED {
424                phase_explained = small_phase_agrees(sig, cand);
425                v.changed_views = v.changed_views.saturating_add(1);
426            }
427        }
428        v.clean_views = v.clean_views.saturating_add(1);
429    }
430    let t = tier as i8;
431    if t > v.tier {
432        cand.copy_from_slice(sig);
433        count.fill(1);
434        v.tier = t;
435    } else if t == v.tier {
436        // Majority per cell at the outlier threshold: codec-phase variants never flip a cell, so the signature stays
437        // one coherent view except where a transient was outvoted. A tie goes to the later view.
438        for k in 0..CELLS {
439            if (cell_luma(sig[k]) - cell_luma(cand[k])).abs() <= OUTLIER {
440                count[k] = count[k].saturating_add(1);
441            } else if count[k] <= 1 {
442                cand[k] = sig[k];
443                count[k] = 1;
444            } else {
445                count[k] -= 1;
446            }
447        }
448    }
449    phase_explained
450}
451
452impl CellRef for Consensus {
453    #[inline]
454    fn cell(&self, cx: i64, cy: i64) -> Option<[u8; 3]> {
455        let (wx, wy) = (cx * CELL as i64, cy * CELL as i64);
456        let (bx, by) = (
457            wx.div_euclid(BLOCK as i64) - self.grid.gx,
458            wy.div_euclid(BLOCK as i64) - self.grid.gy,
459        );
460        if bx < 0 || by < 0 || bx >= self.grid.bw as i64 || by >= self.grid.bh as i64 {
461            return None;
462        }
463        let b = by as usize * self.grid.bw + bx as usize;
464        if self.vote[b].tier < 0 {
465            return None;
466        }
467        let (ix, iy) = (
468            (wx.rem_euclid(BLOCK as i64) as usize) / CELL,
469            (wy.rem_euclid(BLOCK as i64) as usize) / CELL,
470        );
471        Some(self.sig[b * CELLS + iy * SIDE + ix])
472    }
473    fn cells(&self) -> (i64, i64, i64, i64) {
474        let s = SIDE as i64;
475        (
476            self.grid.gx * s,
477            self.grid.gy * s,
478            (self.grid.gx + self.grid.bw as i64) * s,
479            (self.grid.gy + self.grid.bh as i64) * s,
480        )
481    }
482}
483
484mod moments;
485
486type BlockSignature = [[u8; 3]; CELLS];
487
488/// Evidence order for a component's moment. Context must agree before a predicted cleanliness tier can win.
489#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
490pub struct MomentRank {
491    complete: bool,
492    untainted: bool,
493    tier: u8,
494    corroborated: bool,
495    interior: bool,
496    /// Frame index for a complete moment; covered block count for an incomplete one.
497    preference: usize,
498}
499
500/// A connected changing object with exclusively owned, observed context. Context rings never merge objects.
501pub struct Component {
502    pub bx: usize,
503    pub by: usize,
504    pub bw: usize,
505    pub bh: usize,
506    pub mask: Vec<bool>,
507    /// Blocks with change evidence in this hypothesis; the static margin validates the selected moment.
508    pub dynamic: Vec<bool>,
509    /// Best complete moment, or the widest partial observation when none is complete.
510    pub best: Option<MomentRank>,
511    /// Dynamic block signatures of the last complete observation, in mask traversal order.
512    last_complete: Vec<BlockSignature>,
513}
514
515/// Small local shifts are ambiguous: they can be registration phase or a genuinely
516/// moving widget. Preserve raw object coherence whenever its extent can fit a viewport.
517/// Only an intrinsically unobservable group needs the phase-partition fallback.
518pub fn components(c: &Consensus) -> Vec<Component> {
519    let changing =
520        |v: &BlockVote, changed: u16| changed >= 2 && changed as u32 * 4 >= v.clean_views as u32;
521    let raw: Vec<bool> = c
522        .vote
523        .iter()
524        .map(|v| changing(v, v.changed_views))
525        .collect();
526    let groups = partition_components(c, &raw, None);
527    if c.viewport.0 == 0 || c.viewport.1 == 0 {
528        return groups;
529    }
530    let structural: Vec<bool> = c
531        .vote
532        .iter()
533        .zip(&c.phase_explained)
534        .map(|(v, &phase)| changing(v, v.changed_views.saturating_sub(phase)))
535        .collect();
536    if raw == structural {
537        return groups;
538    }
539    let mut owners = vec![-1i32; raw.len()];
540    let mut split = vec![false; groups.len()];
541    for (id, group) in groups.iter().enumerate() {
542        let (mut left, mut top, mut right, mut bottom) = (group.bw, group.bh, 0, 0);
543        let mut ambiguous = false;
544        for y in 0..group.bh {
545            for x in 0..group.bw {
546                let k = y * group.bw + x;
547                if group.mask[k] {
548                    let b = (group.by + y) * c.grid.bw + group.bx + x;
549                    owners[b] = id as i32;
550                    if group.dynamic[k] {
551                        left = left.min(x);
552                        top = top.min(y);
553                        right = right.max(x + 1);
554                        bottom = bottom.max(y + 1);
555                        ambiguous |= !structural[b];
556                    }
557                }
558            }
559        }
560        // Context is support, not object extent. Even the outer changing blocks
561        // leave their exact changed-pixel positions uncertain by BLOCK-1 on each edge.
562        // Split only when the minimum possible changing span still exceeds a viewport.
563        let edge_uncertainty = 2 * (BLOCK - 1);
564        let min_width = ((right - left) * BLOCK).saturating_sub(edge_uncertainty);
565        let min_height = ((bottom - top) * BLOCK).saturating_sub(edge_uncertainty);
566        split[id] = ambiguous && (min_width > c.viewport.0 || min_height > c.viewport.1);
567    }
568    if !split.iter().any(|&v| v) {
569        return groups;
570    }
571    let fallback: Vec<bool> = structural
572        .iter()
573        .zip(&owners)
574        .map(|(&dynamic, &owner)| dynamic && owner >= 0 && split[owner as usize])
575        .collect();
576    let mut result: Vec<Component> = groups
577        .into_iter()
578        .enumerate()
579        .filter_map(|(id, group)| (!split[id]).then_some(group))
580        .collect();
581    result.extend(partition_components(c, &fallback, Some(&owners)));
582    result
583}
584
585/// Core-first components with unique context; a fallback cannot steal another raw group's margin.
586fn partition_components(
587    c: &Consensus,
588    dynamic: &[bool],
589    parents: Option<&[i32]>,
590) -> Vec<Component> {
591    let (bw, bh) = (c.grid.bw, c.grid.bh);
592    let mut labels = vec![0usize; bw * bh];
593    let mut count = 0;
594    for start in 0..labels.len() {
595        if !dynamic[start] || labels[start] != 0 {
596            continue;
597        }
598        count += 1;
599        labels[start] = count;
600        let mut stack = vec![start];
601        while let Some(i) = stack.pop() {
602            let (x, y) = (i % bw, i / bw);
603            for yy in y.saturating_sub(1)..=(y + 1).min(bh - 1) {
604                for xx in x.saturating_sub(1)..=(x + 1).min(bw - 1) {
605                    let j = yy * bw + xx;
606                    if dynamic[j] && labels[j] == 0 {
607                        labels[j] = count;
608                        stack.push(j);
609                    }
610                }
611            }
612        }
613    }
614    // Context validates an object; touching context rings do not make independent objects one time-varying state.
615    // Give each observed context block one owner so two moments cannot overwrite the same static boundary.
616    // A weak first-pass tier is still an observation: relearning may discover a cleaner view of it.
617    let mut owners = labels.clone();
618    for (i, &id) in labels.iter().enumerate() {
619        if id == 0 {
620            continue;
621        }
622        let (x, y) = (i % bw, i / bw);
623        for yy in y.saturating_sub(1)..=(y + 1).min(bh - 1) {
624            for xx in x.saturating_sub(1)..=(x + 1).min(bw - 1) {
625                let j = yy * bw + xx;
626                if labels[j] == 0
627                    && c.vote[j].tier >= 0
628                    && (owners[j] == 0 || id < owners[j])
629                    && parents.is_none_or(|parents| parents[i] == parents[j])
630                {
631                    owners[j] = id;
632                }
633            }
634        }
635    }
636    let mut members = vec![Vec::new(); count];
637    for (i, &id) in owners.iter().enumerate() {
638        if id > 0 {
639            members[id - 1].push(i);
640        }
641    }
642    members
643        .into_iter()
644        .map(|members| {
645            let x0 = members.iter().map(|&i| i % bw).min().unwrap();
646            let x1 = members.iter().map(|&i| i % bw).max().unwrap();
647            let y0 = members.iter().map(|&i| i / bw).min().unwrap();
648            let y1 = members.iter().map(|&i| i / bw).max().unwrap();
649            let (cw, ch) = (x1 - x0 + 1, y1 - y0 + 1);
650            let mut mask = vec![false; cw * ch];
651            let mut changing = vec![false; cw * ch];
652            for i in members {
653                let k = (i / bw - y0) * cw + i % bw - x0;
654                mask[k] = true;
655                changing[k] = dynamic[i];
656            }
657            Component {
658                bx: x0,
659                by: y0,
660                bw: cw,
661                bh: ch,
662                mask,
663                dynamic: changing,
664                best: None,
665                last_complete: Vec::new(),
666            }
667        })
668        .collect()
669}
670
671/// How a block's output was decided.
672#[derive(Clone, Copy, Debug, PartialEq, Eq)]
673#[repr(u8)]
674pub enum Kind {
675    /// Not decided: the compositor's pixels stand.
676    None = 0,
677    /// A central clean view supported by the consensus, with weak new structure corroborated by another clean observation.
678    Coherent = 1,
679    /// One frame for a dynamic component.
680    Component = 2,
681    /// Best-tier whole view held aside; written by [`Output::finish`] unless the compositor's block is as good.
682    Provisional = 3,
683    /// Never seen whole: per-pixel best-tier majority, written by [`Output::finish`] where a better tier
684    /// contradicts the compositor's pixel.
685    Fallback = 4,
686    /// The best clean, agreeing view short of [`Kind::Coherent`] (near the layer's edge, or agreeing only
687    /// loosely), held aside until a better one arrives or the pass ends.
688    Edge = 5,
689}
690
691/// Distance from the layer's edge (pixels) from which a clean agreeing view is final. Views nearer the edge are
692/// slightly degraded (rounded corners, container shadows, the codec at a fast-moving edge).
693pub const CENTRAL: i64 = 48;
694
695/// Copy of one block from a vote frame into the output: world block (gx, gy) from screen (sx, sy).
696#[derive(Clone, Copy, Debug, PartialEq, Eq)]
697pub struct BlockWrite {
698    /// Canvas slot (set by the run driver).
699    pub canvas: u32,
700    pub gx: i64,
701    pub gy: i64,
702    pub sx: i64,
703    pub sy: i64,
704    pub frame: u32,
705    pub kind: Kind,
706}
707
708/// A block written at the end: RGBA with alpha 0 on pixels that keep the compositor's value.
709/// [`Output::sources`]: the compositor's pixels stand.
710pub const UNTOUCHED: i32 = -1;
711/// [`Output::sources`]: per-pixel values that may come from several frames.
712pub const MIXED: i32 = -2;
713
714pub struct Deferred {
715    pub gx: i64,
716    pub gy: i64,
717    pub frame: u32,
718    pub kind: Kind,
719    pub rgba: Box<[u8; PIXELS * 4]>,
720}
721
722struct Fallback {
723    /// A prior whole, clean, locally flat observation; never an averaged output colour.
724    flat_witness: Option<[u8; 3]>,
725    cand: [[u8; 3]; PIXELS],
726    count: [i16; PIXELS],
727    tier: [i8; PIXELS],
728    /// The compositor's pixels (alpha 0 = uncovered) and the best tier of an observation showing them.
729    main: [[u8; 4]; PIXELS],
730    main_tier: [i8; PIXELS],
731}
732
733#[derive(Clone, Copy, Default)]
734enum WitnessAction {
735    #[default]
736    Keep,
737    Store,
738    Clear,
739}
740
741/// A whole-block view's verdict, decided in parallel and applied in block order.
742#[derive(Clone, Copy, Default)]
743struct Act {
744    corroborated_flat: bool,
745    witness: WitnessAction,
746    seen: bool,
747    tier: u8,
748    /// Spatial agreement, including a plausible sampling phase.
749    strict: bool,
750    /// Quiet reference agreement or corroboration by another actual observation.
751    settled: bool,
752    loose: bool,
753    inset: i16,
754    /// The view shows what the compositor already holds.
755    main: bool,
756}
757
758/// One partly visible view of a fallback block, per pixel: best tier, tolerant majority within it (a tie to the
759/// later view, whose value is written: the most settled one), and the best tier of any observation showing the
760/// compositor's own pixel.
761fn fallback_view(
762    fb: &mut Fallback,
763    frame: &Frame<'_>,
764    footprint: &Footprint<'_>,
765    tiers: &TierMap<'_>,
766    pose: (i64, i64),
767    origin: (i64, i64),
768    tau: i32,
769) {
770    let (w, near) = (frame.width, (tau / 2).max(2));
771    for j in 0..PIXELS {
772        let (sx, sy) = (
773            origin.0 + (j % BLOCK) as i64 - pose.0,
774            origin.1 + (j / BLOCK) as i64 - pose.1,
775        );
776        if sx < 0
777            || sy < 0
778            || sx >= w as i64
779            || sy >= frame.height as i64
780            || !footprint.contains(sx, sy)
781        {
782            continue;
783        }
784        let fi = (sy as usize * w + sx as usize) * 4;
785        let obs = [frame.rgba[fi], frame.rgba[fi + 1], frame.rgba[fi + 2]];
786        let t = tiers.at(sx, sy) as i8;
787        let m = fb.main[j];
788        if m[3] == 255 && (0..3).all(|k| (obs[k] as i32 - m[k] as i32).abs() <= near) {
789            fb.main_tier[j] = fb.main_tier[j].max(t);
790        }
791        if t > fb.tier[j] {
792            fb.cand[j] = obs;
793            fb.count[j] = 1;
794            fb.tier[j] = t;
795        } else if t == fb.tier[j] {
796            if (0..3).all(|k| (obs[k] as i32 - fb.cand[j][k] as i32).abs() <= tau) {
797                fb.cand[j] = obs;
798                fb.count[j] = fb.count[j].saturating_add(1);
799            } else if fb.count[j] <= 1 {
800                fb.cand[j] = obs;
801                fb.count[j] = 1;
802            } else {
803                fb.count[j] -= 1;
804            }
805        }
806    }
807}
808
809fn whole_block_is_clean(tiers: &TierMap<'_>, sx: i64, sy: i64) -> bool {
810    let (x0, y0) = (
811        (sx - tiers.x).div_euclid(CELL as i64),
812        (sy - tiers.y).div_euclid(CELL as i64),
813    );
814    let (x1, y1) = (
815        (sx + BLOCK as i64 - 1 - tiers.x).div_euclid(CELL as i64),
816        (sy + BLOCK as i64 - 1 - tiers.y).div_euclid(CELL as i64),
817    );
818    if x0 < 0 || y0 < 0 || x1 >= tiers.cw as i64 || y1 >= tiers.ch as i64 {
819        return false;
820    }
821    (y0..=y1).all(|y| {
822        tiers.tier[y as usize * tiers.cw + x0 as usize..=y as usize * tiers.cw + x1 as usize]
823            .iter()
824            .all(|&t| t == TIER_CLEAN)
825    })
826}
827
828/// A flat block alone may be a shifted fine line. Require its surrounding block ring to be flat and observed too.
829fn flat_neighborhood_sample(
830    frame: &Frame<'_>,
831    footprint: &Footprint<'_>,
832    sx: i64,
833    sy: i64,
834    tolerance: i32,
835) -> Option<[u8; 3]> {
836    let radius = BLOCK as i64;
837    let (left, top, right, bottom) = (sx - radius, sy - radius, sx + 2 * radius, sy + 2 * radius);
838    if left < 0 || top < 0 || right > frame.width as i64 || bottom > frame.height as i64 {
839        return None;
840    }
841    let mut low = [255u8; 3];
842    let mut high = [0u8; 3];
843    for y in top..bottom {
844        for x in left..right {
845            if !footprint.contains(x, y) {
846                return None;
847            }
848            let p = (y as usize * frame.width + x as usize) * 4;
849            for c in 0..3 {
850                low[c] = low[c].min(frame.rgba[p + c]);
851                high[c] = high[c].max(frame.rgba[p + c]);
852                if high[c] as i32 - low[c] as i32 > tolerance {
853                    return None;
854                }
855            }
856        }
857    }
858    let p = (sy as usize * frame.width + sx as usize) * 4;
859    Some([frame.rgba[p], frame.rgba[p + 1], frame.rgba[p + 2]])
860}
861
862/// Pass-4 state per canvas.
863pub struct Output {
864    acts: Vec<Act>,
865    pub kind: Vec<Kind>,
866    pub frame: Vec<i32>,
867    component: Vec<i32>,
868    provisional: Vec<i8>,
869    /// Rank of the held view: (tier, strictly agreeing, loosely agreeing, inset up to [`CENTRAL`]).
870    rank: Vec<(i8, bool, bool, i16)>,
871    held: Vec<Option<Box<[u8; PIXELS * 3]>>>,
872    /// Last unsupported weak change in a clean, central pose vote. Components use their own history.
873    weak_witness: Vec<Option<Box<BlockSignature>>>,
874    main_tier: Vec<i8>,
875    fallback: Vec<Option<Box<Fallback>>>,
876    pub components: Vec<Component>,
877}
878
879/// Bytes a consensus holds per block (signatures and counts of its cells, and its vote).
880pub const CONSENSUS_BLOCK_BYTES: usize = CELLS
881    * (std::mem::size_of::<[u8; 3]>() + std::mem::size_of::<i16>())
882    + std::mem::size_of::<BlockVote>()
883    + std::mem::size_of::<u16>();
884/// Bytes an output holds per block, counting a held view for every block (the most the write pass can hold); its
885/// per-pixel fallback state is [`FALLBACK_BYTES`] per block that needs it.
886pub const OUTPUT_BLOCK_BYTES: usize = std::mem::size_of::<Act>()
887    + std::mem::size_of::<Kind>()
888    + 2 * std::mem::size_of::<i32>()
889    + std::mem::size_of::<i8>()
890    + std::mem::size_of::<(i8, bool, bool, i16)>()
891    + std::mem::size_of::<Option<Box<[u8; PIXELS * 3]>>>()
892    + PIXELS * 3
893    + std::mem::size_of::<Option<Box<BlockSignature>>>()
894    // Component history and static weak-change witnesses own disjoint block sets.
895    + CELLS * std::mem::size_of::<[u8; 3]>()
896    + std::mem::size_of::<i8>()
897    + std::mem::size_of::<Option<Box<Fallback>>>();
898/// Bytes of one block's per-pixel fallback state.
899pub const FALLBACK_BYTES: usize = std::mem::size_of::<Fallback>();
900
901impl Consensus {
902    /// Blocks no clean view covered whole: an upper bound on the blocks [`Output::new`] gives fallback state (it also
903    /// spares those inside a component).
904    pub fn unseen_blocks(&self) -> usize {
905        self.vote.iter().filter(|v| v.clean_views == 0).count()
906    }
907}
908
909impl Output {
910    /// Blocks never seen whole and clean in pass 2 get per-pixel fallback state; their compositor pixels must be
911    /// supplied with [`Output::set_main`] before the first [`Output::emit`].
912    pub fn new(c: &Consensus, components: Vec<Component>) -> Self {
913        let n = c.grid.bw * c.grid.bh;
914        let mut component = vec![-1; n];
915        for (k, comp) in components.iter().enumerate() {
916            for yy in 0..comp.bh {
917                for xx in 0..comp.bw {
918                    if comp.mask[yy * comp.bw + xx] {
919                        component[(comp.by + yy) * c.grid.bw + comp.bx + xx] = k as i32;
920                    }
921                }
922            }
923        }
924        let fallback = c
925            .vote
926            .iter()
927            .zip(&component)
928            .map(|(v, &k)| {
929                (v.clean_views == 0 && k < 0).then(|| {
930                    Box::new(Fallback {
931                        flat_witness: None,
932                        cand: [[0; 3]; PIXELS],
933                        count: [0; PIXELS],
934                        tier: [-1; PIXELS],
935                        main: [[0; 4]; PIXELS],
936                        main_tier: [-1; PIXELS],
937                    })
938                })
939            })
940            .collect();
941        Self {
942            acts: vec![Act::default(); n],
943            kind: vec![Kind::None; n],
944            frame: vec![-1; n],
945            component,
946            provisional: vec![-1; n],
947            rank: vec![(-1, false, false, -1); n],
948            held: (0..n).map(|_| None).collect(),
949            weak_witness: (0..n).map(|_| None).collect(),
950            main_tier: vec![-1; n],
951            fallback,
952            components,
953        }
954    }
955
956    /// Blocks that need the compositor's pixels (fallback blocks), as indices into the grid.
957    pub fn fallback_blocks(&self) -> Vec<usize> {
958        self.fallback
959            .iter()
960            .enumerate()
961            .filter(|(_, f)| f.is_some())
962            .map(|(b, _)| b)
963            .collect()
964    }
965
966    /// The compositor's RGBA for fallback block `b` (alpha 255 = covered).
967    pub fn set_main(&mut self, b: usize, rgba: &[u8]) {
968        if let Some(fb) = self.fallback.get_mut(b).and_then(|f| f.as_mut()) {
969            for (j, px) in rgba.chunks_exact(4).take(PIXELS).enumerate() {
970                fb.main[j] = [px[0], px[1], px[2], px[3]];
971            }
972        }
973    }
974
975    /// One distinct-pose vote. `still_previous` is the immediately preceding frame only when the pose did not
976    /// change. It can confirm a final settled state that contributes just one vote. Writes always copy native pixels;
977    /// `main` is the compositor's reference as cell means.
978    #[allow(clippy::too_many_arguments)]
979    pub fn emit(
980        &mut self,
981        c: &Consensus,
982        frame: &Frame<'_>,
983        still_previous: Option<&Frame<'_>>,
984        footprint: &Footprint<'_>,
985        tiers: &TierMap<'_>,
986        pose: (i64, i64),
987        tau: i32,
988        frame_index: u32,
989        main: &(dyn CellRef + Sync),
990        writes: &mut Vec<BlockWrite>,
991    ) {
992        self.coobserve(
993            c,
994            &moments::View {
995                frame,
996                previous: still_previous,
997                footprint,
998                tiers,
999                pose,
1000                tau,
1001                index: frame_index,
1002            },
1003        );
1004        let grid = c.grid;
1005        let mut sig = [[0u8; 3]; CELLS];
1006        let block_write = |b: usize, kind: Kind| {
1007            let (wx0, wy0) = grid.origin(b);
1008            BlockWrite {
1009                canvas: 0,
1010                gx: grid.gx + (b % grid.bw) as i64,
1011                gy: grid.gy + (b / grid.bw) as i64,
1012                sx: wx0 - pose.0,
1013                sy: wy0 - pose.1,
1014                frame: frame_index,
1015                kind,
1016            }
1017        };
1018        // A complete moment outranks every partial cover. Within complete observations, use context, cleanliness,
1019        // temporal corroboration and visibility before recency. Without a complete view the widest partial cover
1020        // remains a best effort; it does not establish a complete epoch.
1021        for comp in self.components.iter_mut() {
1022            let (x, y) = grid.origin(comp.by * grid.bw + comp.bx);
1023            if x + (comp.bw * BLOCK) as i64 <= footprint.x + pose.0
1024                || y + (comp.bh * BLOCK) as i64 <= footprint.y + pose.1
1025                || x >= footprint.x + pose.0 + footprint.width as i64
1026                || y >= footprint.y + pose.1 + footprint.height as i64
1027            {
1028                continue;
1029            }
1030            let mut covered = Vec::new();
1031            let may_complete = x >= footprint.x + pose.0
1032                && y >= footprint.y + pose.1
1033                && x + (comp.bw * BLOCK) as i64 <= footprint.x + pose.0 + footprint.width as i64
1034                && y + (comp.bh * BLOCK) as i64 <= footprint.y + pose.1 + footprint.height as i64
1035                && x >= pose.0
1036                && y >= pose.1
1037                && x + (comp.bw * BLOCK) as i64 <= pose.0 + frame.width as i64
1038                && y + (comp.bh * BLOCK) as i64 <= pose.1 + frame.height as i64;
1039            let mut moment = Vec::with_capacity(if may_complete {
1040                comp.dynamic.iter().filter(|&&v| v).count()
1041            } else {
1042                0
1043            });
1044            let mut dynamic_index = 0;
1045            let mut compared = 0usize;
1046            let mut stable = true;
1047            let mut tier = TIER_CLEAN;
1048            let mut inset = i64::MAX;
1049            // A frame showing something in the component's static blocks that is not there in the consensus
1050            // carries a transient (a cursor passing over the widget's edge, or a status bubble over whole blocks)
1051            // or an object larger than the component's blocks: it is the component's moment only when no other
1052            // frame covers the component.
1053            let mut tainted = false;
1054            for yy in 0..comp.bh {
1055                for xx in 0..comp.bw {
1056                    let k = yy * comp.bw + xx;
1057                    if !comp.mask[k] {
1058                        continue;
1059                    }
1060                    let b = (comp.by + yy) * grid.bw + comp.bx + xx;
1061                    let (wx0, wy0) = grid.origin(b);
1062                    let previous_index = dynamic_index;
1063                    if comp.dynamic[k] {
1064                        dynamic_index += 1;
1065                    }
1066                    if let Some(t) = view(frame, footprint, tiers, pose, wx0, wy0, &mut sig) {
1067                        if t >= TIER_VEIL {
1068                            tier = tier.min(t);
1069                            // A cleaner relearned observation is not contradicted by a weaker first-pass reference.
1070                            if !comp.dynamic[k] && t as i8 <= c.vote[b].tier {
1071                                tainted |= contradicts(
1072                                    &sig,
1073                                    &c.sig[b * CELLS..(b + 1) * CELLS],
1074                                    tau,
1075                                    || {
1076                                        settled(
1077                                            &sig,
1078                                            still_previous,
1079                                            footprint,
1080                                            tiers,
1081                                            pose,
1082                                            wx0,
1083                                            wy0,
1084                                            tau,
1085                                        )
1086                                    },
1087                                );
1088                            }
1089                            if comp.dynamic[k] && may_complete {
1090                                let mut prior = [[0u8; 3]; CELLS];
1091                                let reference = if let Some(before) = still_previous {
1092                                    view(before, footprint, tiers, pose, wx0, wy0, &mut prior)
1093                                        .map(|_| &prior)
1094                                } else {
1095                                    comp.last_complete.get(previous_index)
1096                                };
1097                                if let Some(reference) = reference {
1098                                    let difference = compare(&sig, reference);
1099                                    compared += 1;
1100                                    stable &=
1101                                        difference.0 <= tau as u32 && difference.1 <= tau as u32;
1102                                }
1103                                moment.push(sig);
1104                            }
1105                            let (sx, sy) = (wx0 - pose.0, wy0 - pose.1);
1106                            inset = inset
1107                                .min(sx - footprint.x)
1108                                .min(sy - footprint.y)
1109                                .min(footprint.x + footprint.width as i64 - sx - BLOCK as i64)
1110                                .min(footprint.y + footprint.height as i64 - sy - BLOCK as i64);
1111                            covered.push(b);
1112                        }
1113                    }
1114                }
1115            }
1116            if covered.is_empty() {
1117                continue;
1118            }
1119            let blocks = comp.mask.iter().filter(|&&m| m).count();
1120            let eligible = covered.len() == blocks;
1121            let confirmed = eligible && compared == moment.len() && compared > 0 && stable;
1122            if eligible {
1123                comp.last_complete = moment;
1124            }
1125            // Registration phase can perturb a correct sample, so corroboration uses the source-noise tolerance.
1126            // A stationary run's previous frame confirms its final state without turning duplicate poses into votes.
1127            let score = if eligible {
1128                MomentRank {
1129                    complete: true,
1130                    untainted: !tainted,
1131                    tier,
1132                    corroborated: confirmed,
1133                    interior: confirmed && inset >= CENTRAL,
1134                    preference: frame_index as usize,
1135                }
1136            } else {
1137                MomentRank {
1138                    complete: false,
1139                    untainted: false,
1140                    tier: 0,
1141                    corroborated: false,
1142                    interior: false,
1143                    preference: covered.len(),
1144                }
1145            };
1146            if comp.best.is_some_and(|best| best > score) {
1147                continue;
1148            }
1149            comp.best = Some(score);
1150            for b in covered {
1151                self.kind[b] = Kind::Component;
1152                self.frame[b] = frame_index as i32;
1153                writes.push(block_write(b, Kind::Component));
1154            }
1155        }
1156        let (bx0, by0, bx1, by1) = grid.span(footprint, pose);
1157        let w = frame.width;
1158        // Fallback blocks can be partly in view, so they are visited over the footprint's whole block range.
1159        let (fx0, fy0, fx1, fy1) = {
1160            let b = BLOCK as i64;
1161            let (x0, y0) = (footprint.x + pose.0, footprint.y + pose.1);
1162            let (x1, y1) = (x0 + footprint.width as i64, y0 + footprint.height as i64);
1163            let clamp = |v: i64, n: usize| v.clamp(0, n as i64) as usize;
1164            (
1165                clamp(x0.div_euclid(b) - grid.gx, grid.bw),
1166                clamp(y0.div_euclid(b) - grid.gy, grid.bh),
1167                clamp((x1 + b - 1).div_euclid(b) - grid.gx, grid.bw),
1168                clamp((y1 + b - 1).div_euclid(b) - grid.gy, grid.bh),
1169            )
1170        };
1171        // The fallback slots hold boxes (not `Copy`), so their base travels as an address.
1172        let (kind_p, fallback_base) = (
1173            crate::kernels::pool::SyncPtr(self.kind.as_mut_ptr()),
1174            self.fallback.as_mut_ptr() as usize,
1175        );
1176        if fy1 > fy0 && fx1 > fx0 {
1177            let rows = fy1 - fy0;
1178            let chunks = crate::kernels::pool::chunks_for(rows * (fx1 - fx0), 64);
1179            crate::kernels::pool::par_for(chunks, |k| {
1180                for by in fy0 + crate::kernels::pool::split(rows, chunks, k)
1181                    ..fy0 + crate::kernels::pool::split(rows, chunks, k + 1)
1182                {
1183                    for bx in fx0..fx1 {
1184                        let b = by * grid.bw + bx;
1185                        // SAFETY: chunk `k` owns these block rows; each block's fallback state is its own.
1186                        let (slot, kind) = unsafe {
1187                            (
1188                                &mut *(fallback_base as *mut Option<Box<Fallback>>).add(b),
1189                                &mut *kind_p.get().add(b),
1190                            )
1191                        };
1192                        let Some(fb) = slot.as_mut() else { continue };
1193                        *kind = Kind::Fallback;
1194                        fallback_view(fb, frame, footprint, tiers, pose, grid.origin(b), tau);
1195                    }
1196                }
1197            });
1198        }
1199        if by1 <= by0 || bx1 <= bx0 {
1200            return;
1201        }
1202        // Decide every whole block in parallel (no allocation in the pool), then apply the decisions in block order.
1203        let rows = by1 - by0;
1204        let chunks = crate::kernels::pool::chunks_for(rows * (bx1 - bx0), 64);
1205        let acts = crate::kernels::pool::SyncPtr(self.acts.as_mut_ptr());
1206        let (component, kind, witnesses) = (&self.component, &self.kind, &self.weak_witness);
1207        crate::kernels::pool::par_for(chunks, |k| {
1208            let mut sig = [[0u8; 3]; CELLS];
1209            for by in by0 + crate::kernels::pool::split(rows, chunks, k)
1210                ..by0 + crate::kernels::pool::split(rows, chunks, k + 1)
1211            {
1212                for bx in bx0..bx1 {
1213                    let b = by * grid.bw + bx;
1214                    // SAFETY: chunk `k` owns these block rows' decision slots.
1215                    let act = unsafe { &mut *acts.get().add(b) };
1216                    *act = Act::default();
1217                    if component[b] >= 0 || kind[b] == Kind::Coherent {
1218                        continue;
1219                    }
1220                    let (wx0, wy0) = grid.origin(b);
1221                    // SAFETY: each pool chunk owns these block rows, as in fallback_view above.
1222                    let fb = unsafe { &mut *(fallback_base as *mut Option<Box<Fallback>>).add(b) };
1223                    if fb.is_some()
1224                        && (c.vote[b].tier >= TIER_CLEAN as i8
1225                            || !whole_block_is_clean(tiers, wx0 - pose.0, wy0 - pose.1))
1226                    {
1227                        continue;
1228                    }
1229                    let Some(tier) = view(frame, footprint, tiers, pose, wx0, wy0, &mut sig) else {
1230                        continue;
1231                    };
1232                    let d = compare(&sig, &c.sig[b * CELLS..(b + 1) * CELLS]);
1233                    let (sx0, sy0) = (wx0 - pose.0, wy0 - pose.1);
1234                    let inset = (sx0 - footprint.x)
1235                        .min(sy0 - footprint.y)
1236                        .min(footprint.x + footprint.width as i64 - sx0 - BLOCK as i64)
1237                        .min(footprint.y + footprint.height as i64 - sy0 - BLOCK as i64);
1238                    if let Some(fb) = fb.as_mut() {
1239                        if tier == TIER_CLEAN && inset >= CENTRAL {
1240                            let tolerance = (tau / 4).max(1);
1241                            if let Some(sample) =
1242                                flat_neighborhood_sample(frame, footprint, sx0, sy0, tolerance)
1243                            {
1244                                let consistent = |prior: [u8; 3]| {
1245                                    (0..3).all(|c| {
1246                                        (sample[c] as i32 - prior[c] as i32).abs() <= tolerance
1247                                    })
1248                                };
1249                                let prior = still_previous.and_then(|f| {
1250                                    flat_neighborhood_sample(f, footprint, sx0, sy0, tolerance)
1251                                });
1252                                act.seen = true;
1253                                act.corroborated_flat =
1254                                    prior.or(fb.flat_witness).is_some_and(consistent);
1255                                fb.flat_witness = Some(sample);
1256                            }
1257                        }
1258                        continue;
1259                    }
1260                    // Does this view show what the compositor already holds?
1261                    let (cx0, cy0) = (wx0.div_euclid(CELL as i64), wy0.div_euclid(CELL as i64));
1262                    let mut msig = [[0u8; 3]; CELLS];
1263                    let known = (0..CELLS).all(|k| {
1264                        match main.cell(cx0 + (k % SIDE) as i64, cy0 + (k / SIDE) as i64) {
1265                            Some(v) => {
1266                                msig[k] = v;
1267                                true
1268                            }
1269                            None => false,
1270                        }
1271                    });
1272                    let mut strict = agrees_strictly(d, tau, &sig, c, (wx0, wy0));
1273                    let quiet_limit = 2 * (tau / 4).max(2);
1274                    // Spatial phase support keeps a view eligible, but does not
1275                    // prove that a weak transient beside a real edge is permanent.
1276                    let mut settled = strict
1277                        && sig
1278                            .iter()
1279                            .zip(&c.sig[b * CELLS..(b + 1) * CELLS])
1280                            .all(|(a, b)| (cell_luma(*a) - cell_luma(*b)).abs() <= quiet_limit);
1281                    let mut witness = WitnessAction::Keep;
1282                    if tier == TIER_CLEAN {
1283                        witness = WitnessAction::Clear;
1284                        if !settled && inset >= CENTRAL && agrees(d, tau) && d.2 == 0 {
1285                            // The early reference may precede genuinely loaded, low-contrast content.
1286                            // Two clean views must agree with each other more closely than merely both
1287                            // resembling that reference; a moving weak mark cannot corroborate itself.
1288                            let quiet_limit = 2 * (tau / 4).max(2);
1289                            let mut previous_signature = [[0u8; 3]; CELLS];
1290                            // A still run's actual predecessor can confirm newly settled content without
1291                            // adding a duplicate-pose vote. It also prevents an old witness from confirming
1292                            // an isolated pulse that contradicts the immediately preceding image.
1293                            let previous = if let Some(before) = still_previous {
1294                                view(
1295                                    before,
1296                                    footprint,
1297                                    tiers,
1298                                    pose,
1299                                    wx0,
1300                                    wy0,
1301                                    &mut previous_signature,
1302                                )
1303                                .map(|_| &previous_signature)
1304                            } else {
1305                                witnesses[b].as_deref()
1306                            };
1307                            settled = previous.is_some_and(|previous| {
1308                                agrees(compare(&sig, previous), tau)
1309                                    && sig.iter().zip(previous).all(|(a, b)| {
1310                                        (cell_luma(*a) - cell_luma(*b)).abs() <= quiet_limit
1311                                    })
1312                            });
1313                            strict |= settled;
1314                            if !settled {
1315                                witness = WitnessAction::Store;
1316                            }
1317                        }
1318                    }
1319                    *act = Act {
1320                        corroborated_flat: false,
1321                        witness,
1322                        seen: true,
1323                        tier,
1324                        strict,
1325                        settled,
1326                        loose: agrees(d, tau),
1327                        inset: inset.min(CENTRAL) as i16,
1328                        main: known && agrees(compare(&sig, &msig), tau),
1329                    };
1330                }
1331            }
1332        });
1333        for by in by0..by1 {
1334            for bx in bx0..bx1 {
1335                let b = by * grid.bw + bx;
1336                let a = self.acts[b];
1337                if !a.seen {
1338                    continue;
1339                }
1340                match a.witness {
1341                    WitnessAction::Keep => {}
1342                    WitnessAction::Clear => self.weak_witness[b] = None,
1343                    WitnessAction::Store => {
1344                        // Only rare, whole clean views need a witness. Re-read their small signature here
1345                        // so allocation and release remain outside the parallel decision workers.
1346                        let (wx, wy) = grid.origin(b);
1347                        if view(frame, footprint, tiers, pose, wx, wy, &mut sig).is_some() {
1348                            if let Some(previous) = &mut self.weak_witness[b] {
1349                                **previous = sig;
1350                            } else {
1351                                self.weak_witness[b] = Some(Box::new(sig));
1352                            }
1353                        }
1354                    }
1355                }
1356                if self.fallback[b].is_some() {
1357                    if a.corroborated_flat {
1358                        self.kind[b] = Kind::Coherent;
1359                        self.frame[b] = frame_index as i32;
1360                        self.fallback[b] = None;
1361                        writes.push(block_write(b, Kind::Coherent));
1362                    }
1363                    continue;
1364                }
1365                if a.tier == TIER_CLEAN && a.settled && a.inset >= CENTRAL as i16 {
1366                    self.kind[b] = Kind::Coherent;
1367                    self.frame[b] = frame_index as i32;
1368                    self.held[b] = None;
1369                    writes.push(block_write(b, Kind::Coherent));
1370                    continue;
1371                }
1372                if a.main {
1373                    self.main_tier[b] = self.main_tier[b].max(a.tier as i8);
1374                }
1375                // Hold the best view so far; a later view wins a tie (the more settled state).
1376                let rank = (
1377                    a.tier as i8,
1378                    a.tier == TIER_CLEAN && a.strict,
1379                    a.tier == TIER_CLEAN && a.loose,
1380                    a.inset,
1381                );
1382                if rank >= self.rank[b] {
1383                    let held = self.held[b].get_or_insert_with(|| Box::new([0u8; PIXELS * 3]));
1384                    let (wx0, wy0) = grid.origin(b);
1385                    for j in 0..PIXELS {
1386                        let (sx, sy) = (
1387                            wx0 + (j % BLOCK) as i64 - pose.0,
1388                            wy0 + (j / BLOCK) as i64 - pose.1,
1389                        );
1390                        let fi = (sy as usize * w + sx as usize) * 4;
1391                        held[j * 3..j * 3 + 3].copy_from_slice(&frame.rgba[fi..fi + 3]);
1392                    }
1393                    self.rank[b] = rank;
1394                    self.kind[b] = if rank.2 {
1395                        Kind::Edge
1396                    } else {
1397                        Kind::Provisional
1398                    };
1399                    self.provisional[b] = a.tier as i8;
1400                    self.frame[b] = frame_index as i32;
1401                }
1402            }
1403        }
1404    }
1405
1406    /// Where each block's pixels now come from, given the blocks [`Output::finish`] returned: the frame a whole
1407    /// block was copied from, [`MIXED`] for per-pixel fallback values, [`UNTOUCHED`] where the compositor's pixels
1408    /// stand.
1409    pub fn sources(&self, c: &Consensus, deferred: &[Deferred]) -> Vec<i32> {
1410        let grid = c.grid;
1411        let mut out: Vec<i32> = self
1412            .kind
1413            .iter()
1414            .zip(&self.frame)
1415            .map(|(k, &f)| match k {
1416                Kind::Coherent | Kind::Component => f,
1417                _ => UNTOUCHED,
1418            })
1419            .collect();
1420        for d in deferred {
1421            let b = (d.gy - grid.gy) as usize * grid.bw + (d.gx - grid.gx) as usize;
1422            out[b] = if d.kind == Kind::Fallback {
1423                MIXED
1424            } else {
1425                d.frame as i32
1426            };
1427        }
1428        out
1429    }
1430
1431    /// The deferred blocks: a provisional view unless a view agreeing with the compositor's block was at least as
1432    /// clean, and fallback pixels where a better-tier observation contradicts the compositor's pixel.
1433    pub fn finish(&self, c: &Consensus, tau: i32) -> Vec<Deferred> {
1434        let grid = c.grid;
1435        let near = (tau / 2).max(2);
1436        let mut out = Vec::new();
1437        for b in 0..grid.bw * grid.bh {
1438            let (gx, gy) = (
1439                grid.gx + (b % grid.bw) as i64,
1440                grid.gy + (b / grid.bw) as i64,
1441            );
1442            match self.kind[b] {
1443                Kind::Edge => {
1444                    let Some(h) = &self.held[b] else { continue };
1445                    // Agreeing with a consensus only one clean view built is no evidence: near a layer edge in a
1446                    // fast scroll a block is seen whole once, and that view may carry an unmodelled overlay (a
1447                    // scrollbar thumb). It then replaces the compositor's block only when it is cleaner.
1448                    if c.vote[b].clean_views < 2 && self.main_tier[b] >= self.provisional[b] {
1449                        continue;
1450                    }
1451                    let mut rgba = Box::new([0u8; PIXELS * 4]);
1452                    for j in 0..PIXELS {
1453                        rgba[j * 4..j * 4 + 3].copy_from_slice(&h[j * 3..j * 3 + 3]);
1454                        rgba[j * 4 + 3] = 255;
1455                    }
1456                    out.push(Deferred {
1457                        gx,
1458                        gy,
1459                        frame: self.frame[b] as u32,
1460                        kind: Kind::Edge,
1461                        rgba,
1462                    });
1463                }
1464                Kind::Provisional => {
1465                    let Some(h) = &self.held[b] else { continue };
1466                    if self.main_tier[b] >= self.provisional[b] {
1467                        continue;
1468                    }
1469                    let mut rgba = Box::new([0u8; PIXELS * 4]);
1470                    for j in 0..PIXELS {
1471                        rgba[j * 4..j * 4 + 3].copy_from_slice(&h[j * 3..j * 3 + 3]);
1472                        rgba[j * 4 + 3] = 255;
1473                    }
1474                    out.push(Deferred {
1475                        gx,
1476                        gy,
1477                        frame: self.frame[b] as u32,
1478                        kind: Kind::Provisional,
1479                        rgba,
1480                    });
1481                }
1482                Kind::Fallback => {
1483                    let Some(fb) = &self.fallback[b] else {
1484                        continue;
1485                    };
1486                    let mut rgba = Box::new([0u8; PIXELS * 4]);
1487                    let mut any = false;
1488                    // The block is only touched when a better-tier observation contradicts the compositor somewhere
1489                    // in it; then pixels that say the same take the majority's settled value too, so the rewrite
1490                    // stays consistent, while the compositor's pixel stands wherever an observation at least as
1491                    // clean shows it and the majority says otherwise.
1492                    let same = |j: usize| {
1493                        let m = fb.main[j];
1494                        m[3] == 255
1495                            && (0..3).all(|k| (fb.cand[j][k] as i32 - m[k] as i32).abs() <= near)
1496                    };
1497                    if !(0..PIXELS)
1498                        .any(|j| fb.tier[j] >= 0 && fb.tier[j] > fb.main_tier[j] && !same(j))
1499                    {
1500                        continue;
1501                    }
1502                    for j in 0..PIXELS {
1503                        if fb.tier[j] < 0 || (fb.main_tier[j] >= fb.tier[j] && !same(j)) {
1504                            continue;
1505                        }
1506                        rgba[j * 4..j * 4 + 3].copy_from_slice(&fb.cand[j]);
1507                        rgba[j * 4 + 3] = 255;
1508                        any = true;
1509                    }
1510                    if any {
1511                        out.push(Deferred {
1512                            gx,
1513                            gy,
1514                            frame: 0,
1515                            kind: Kind::Fallback,
1516                            rgba,
1517                        });
1518                    }
1519                }
1520                _ => {}
1521            }
1522        }
1523        out
1524    }
1525}
1526
1527#[cfg(test)]
1528mod tests {
1529    use super::*;
1530    use crate::kernels::reveal::stats::CellMap;
1531    use crate::kernels::reveal::CanvasView;
1532
1533    fn solid_frame(width: usize, height: usize, value: u8) -> Vec<u8> {
1534        let mut rgba = vec![value; width * height * 4];
1535        for pixel in rgba.chunks_exact_mut(4) {
1536            pixel[3] = 255;
1537        }
1538        rgba
1539    }
1540
1541    const W: usize = 64;
1542    const H: usize = 64;
1543
1544    /// A white page with a dark rule in every block, a block at (16..32, 16..32) that shows a different picture in
1545    /// every frame (content that really changes), and in `transient` frames a dark bubble over the whole block at
1546    /// (0..16, 0..16).
1547    fn frame(k: usize, transient: bool) -> Vec<u8> {
1548        let mut rgba = vec![255u8; W * H * 4];
1549        for y in 0..H {
1550            for x in 0..W {
1551                let v = if (16..32).contains(&x) && (16..32).contains(&y) {
1552                    ((k * 60 + x * 3) % 256) as u8
1553                } else if transient && x < 16 && y < 16 {
1554                    40
1555                } else if y % 16 == 7 && x % 16 < 10 {
1556                    30
1557                } else {
1558                    250
1559                };
1560                rgba[(y * W + x) * 4..(y * W + x) * 4 + 3].fill(v);
1561            }
1562        }
1563        rgba
1564    }
1565
1566    /// Which frame each block of the component took, over `frames` voting in order (all whole, clean, at pose 0).
1567    fn moments(frames: &[Vec<u8>]) -> Vec<i32> {
1568        let mask = vec![1u8; W * H];
1569        let footprint = Footprint {
1570            x: 0,
1571            y: 0,
1572            width: W,
1573            height: H,
1574            mask: &mask,
1575        };
1576        let clean = vec![TIER_CLEAN; (W / CELL) * (H / CELL)];
1577        let tiers = TierMap {
1578            x: 0,
1579            y: 0,
1580            cw: W / CELL,
1581            ch: H / CELL,
1582            tier: &clean,
1583        };
1584        let mut c = Consensus::new(Grid::covering(0, 0, W, H));
1585        for f in frames {
1586            let f = Frame {
1587                rgba: f,
1588                width: W,
1589                height: H,
1590            };
1591            c.observe(&f, &footprint, &tiers, (0, 0));
1592        }
1593        let comps = components(&c);
1594        assert_eq!(comps.len(), 1, "one dynamic component");
1595        let mut out = Output::new(&c, comps);
1596        let coverage = vec![255u8; W * H / 8];
1597        let main = CellMap::from_canvas(&CanvasView {
1598            x: 0,
1599            y: 0,
1600            width: W,
1601            height: H,
1602            rgba: &frames[0],
1603            coverage: &coverage,
1604        });
1605        let mut writes = Vec::new();
1606        for (i, f) in frames.iter().enumerate() {
1607            let f = Frame {
1608                rgba: f,
1609                width: W,
1610                height: H,
1611            };
1612            out.emit(
1613                &c,
1614                &f,
1615                None,
1616                &footprint,
1617                &tiers,
1618                (0, 0),
1619                24,
1620                i as u32,
1621                &main,
1622                &mut writes,
1623            );
1624        }
1625        out.frame.clone()
1626    }
1627
1628    // A dynamic component takes one moment, the latest frame that covers it. Chrome's link-status bubble, shown
1629    // in 1 of 8 clean views of a sticky table of contents (owner_1.53.20), covered whole blocks of the component's
1630    // static margin in that latest frame, and the taint test only caught small blobs, so the bubble was copied in
1631    // over the page the compositor had. A frame whose static blocks disagree with what every other view shows is
1632    // not the component's moment while another frame covers it.
1633    #[test]
1634    fn a_dynamic_component_does_not_take_a_moment_with_a_whole_block_transient() {
1635        let frames: Vec<Vec<u8>> = (0..8).map(|k| frame(k, k == 7)).collect();
1636        let m = moments(&frames);
1637        assert_eq!(m[0], 6, "the bubble's block");
1638        assert_eq!(m[4 + 1], 6, "the dynamic block");
1639    }
1640
1641    // Content that changes (loading, expanding) settles in its final state: without a transient the component
1642    // still takes the latest frame (0-1's thumbnails, §十 揭示).
1643    #[test]
1644    fn a_dynamic_component_takes_its_latest_moment() {
1645        let frames: Vec<Vec<u8>> = (0..8).map(|k| frame(k, false)).collect();
1646        let m = moments(&frames);
1647        assert_eq!(m[4 + 1], 7);
1648        assert_eq!(m[0], 7);
1649    }
1650    #[test]
1651    fn small_rigid_phase_is_distinct_from_structural_change() {
1652        let pattern = |dx: usize| -> BlockSignature {
1653            std::array::from_fn(|k| {
1654                let x = k % SIDE + dx;
1655                let y = k / SIDE;
1656                [20 + ((x * 19 + y * 31) % 11) as u8 * 17; 3]
1657            })
1658        };
1659        let mut cand = pattern(0);
1660        let mut count = [1; CELLS];
1661        let mut v = BlockVote {
1662            tier: TIER_CLEAN as i8,
1663            clean_views: 1,
1664            changed_views: 0,
1665        };
1666        let mut explained = 0;
1667        for dx in [1, 0, 1, 0, 1, 0] {
1668            explained += vote(&mut cand, &mut count, &mut v, &pattern(dx), TIER_CLEAN) as u16;
1669        }
1670        assert_eq!(v.changed_views - explained, 0);
1671        let replacement = [[220; 3]; CELLS];
1672        vote(&mut cand, &mut count, &mut v, &replacement, TIER_CLEAN);
1673        assert_eq!(
1674            v.changed_views - explained,
1675            1,
1676            "a real replacement is not phase noise"
1677        );
1678    }
1679
1680    #[test]
1681    fn a_new_edge_mark_cannot_hide_in_the_uncompared_phase_strip() {
1682        for side in 0..4 {
1683            let mut cand = [[80; 3]; CELLS];
1684            let mut count = [1; CELLS];
1685            let mut v = BlockVote {
1686                tier: TIER_CLEAN as i8,
1687                clean_views: 1,
1688                changed_views: 0,
1689            };
1690            let mut observed = cand;
1691            for k in 0..SIDE {
1692                let cell = match side {
1693                    0 => k * SIDE,
1694                    1 => k * SIDE + SIDE - 1,
1695                    2 => k,
1696                    _ => (SIDE - 1) * SIDE + k,
1697                };
1698                observed[cell] = [230; 3];
1699            }
1700            assert!(!vote(&mut cand, &mut count, &mut v, &observed, TIER_CLEAN));
1701            assert_eq!(
1702                v.changed_views, 1,
1703                "a uniform overlap cannot establish a displacement or dismiss new edge content"
1704            );
1705        }
1706    }
1707
1708    #[test]
1709    fn phase_evidence_requires_a_small_rigid_displacement() {
1710        let pattern = |dx: i32, dy: i32| -> BlockSignature {
1711            std::array::from_fn(|k| {
1712                let x = (k % SIDE) as i32 + 8 + dx;
1713                let y = (k / SIDE) as i32 + 8 + dy;
1714                let seed =
1715                    (x as u32).wrapping_mul(0x9e3779b9) ^ (y as u32).wrapping_mul(0x85ebca6b);
1716                let value = (seed ^ (seed >> 13)).wrapping_mul(0xc2b2ae35);
1717                [(value >> 24) as u8; 3]
1718            })
1719        };
1720        let reference = pattern(0, 0);
1721        for dy in -1..=1 {
1722            for dx in -1..=1 {
1723                if dx != 0 || dy != 0 {
1724                    assert!(small_phase_agrees(&pattern(dx, dy), &reference));
1725                }
1726            }
1727        }
1728        assert!(!small_phase_agrees(&pattern(3, 0), &reference));
1729        let mut changed = pattern(1, 0);
1730        changed[3 * SIDE..5 * SIDE].fill([255; 3]);
1731        assert!(
1732            !small_phase_agrees(&changed, &reference),
1733            "phase cannot explain a changed interior"
1734        );
1735    }
1736
1737    // A real widget moves only two pixels. Separate initial viewports see different
1738    // epochs, so the consensus and compositor can contain a seam inside it. Local
1739    // phase explanations must not discard the later whole-widget observation.
1740    fn phase_ambiguous_widget(clipped_context: bool) {
1741        const WORLD: usize = 416;
1742        const SIZE: usize = 192;
1743        let world = |shift: usize| {
1744            let mut pixels = solid_frame(WORLD, SIZE, 240);
1745            for y in 64..128 {
1746                for x in 144 + shift..240 + shift {
1747                    let value = if (x - 144 - shift) % 8 < 4 { 80 } else { 180 };
1748                    pixels[(y * WORLD + x) * 4..(y * WORLD + x) * 4 + 3].fill(value);
1749                }
1750            }
1751            pixels
1752        };
1753        let epochs = [world(0), world(2), world(4)];
1754        let poses: Vec<(usize, usize)> = (0..8)
1755            .map(|x| (x, 0))
1756            .chain((0..8).map(|x| (if clipped_context { 176 + x } else { 192 + x }, 1)))
1757            .chain((0..3).map(|i| {
1758                if clipped_context {
1759                    (64 + i, 2)
1760                } else {
1761                    (96 + i, 1)
1762                }
1763            }))
1764            .collect();
1765        let frames: Vec<Vec<u8>> = poses
1766            .iter()
1767            .map(|&(x, epoch)| {
1768                let mut frame = vec![0; SIZE * SIZE * 4];
1769                for y in 0..SIZE {
1770                    frame[y * SIZE * 4..(y + 1) * SIZE * 4].copy_from_slice(
1771                        &epochs[epoch][(y * WORLD + x) * 4..(y * WORLD + x + SIZE) * 4],
1772                    );
1773                }
1774                frame
1775            })
1776            .collect();
1777        let mask = vec![1; SIZE * SIZE];
1778        let clean = vec![TIER_CLEAN; SIZE * SIZE / 4];
1779        let fp = Footprint {
1780            x: 0,
1781            y: 0,
1782            width: SIZE,
1783            height: SIZE,
1784            mask: &mask,
1785        };
1786        let tm = TierMap {
1787            x: 0,
1788            y: 0,
1789            cw: SIZE / 2,
1790            ch: SIZE / 2,
1791            tier: &clean,
1792        };
1793        let mut c = Consensus::new(Grid::covering(0, 0, WORLD, SIZE));
1794        for (i, &(x, _)) in poses.iter().enumerate() {
1795            c.observe(
1796                &Frame {
1797                    rgba: &frames[i],
1798                    width: SIZE,
1799                    height: SIZE,
1800                },
1801                &fp,
1802                &tm,
1803                (x as i64, 0),
1804            );
1805        }
1806        let mut result = epochs[0].clone();
1807        for y in 0..SIZE {
1808            result[(y * WORLD + 192) * 4..(y + 1) * WORLD * 4]
1809                .copy_from_slice(&epochs[1][(y * WORLD + 192) * 4..(y + 1) * WORLD * 4]);
1810        }
1811        let initial = result.clone();
1812        let coverage = vec![255; WORLD * SIZE / 8];
1813        let main = CellMap::from_canvas(&CanvasView {
1814            x: 0,
1815            y: 0,
1816            width: WORLD,
1817            height: SIZE,
1818            rgba: &initial,
1819            coverage: &coverage,
1820        });
1821        let mut out = Output::new(&c, components(&c));
1822        for b in out.fallback_blocks() {
1823            let (x, y) = c.grid.origin(b);
1824            let mut block = vec![0; PIXELS * 4];
1825            for row in 0..BLOCK {
1826                let from = ((y as usize + row) * WORLD + x as usize) * 4;
1827                block[row * BLOCK * 4..(row + 1) * BLOCK * 4]
1828                    .copy_from_slice(&initial[from..from + BLOCK * 4]);
1829            }
1830            out.set_main(b, &block);
1831        }
1832        for (i, &(x, _)) in poses.iter().enumerate() {
1833            let mut writes = Vec::new();
1834            out.emit(
1835                &c,
1836                &Frame {
1837                    rgba: &frames[i],
1838                    width: SIZE,
1839                    height: SIZE,
1840                },
1841                None,
1842                &fp,
1843                &tm,
1844                (x as i64, 0),
1845                24,
1846                i as u32,
1847                &main,
1848                &mut writes,
1849            );
1850            for w in writes {
1851                for row in 0..BLOCK {
1852                    let from = ((w.sy as usize + row) * SIZE + w.sx as usize) * 4;
1853                    let to = ((w.gy as usize * BLOCK + row) * WORLD + w.gx as usize * BLOCK) * 4;
1854                    result[to..to + BLOCK * 4].copy_from_slice(&frames[i][from..from + BLOCK * 4]);
1855                }
1856            }
1857        }
1858        for block in out.finish(&c, 24) {
1859            for row in 0..BLOCK {
1860                let to =
1861                    ((block.gy as usize * BLOCK + row) * WORLD + block.gx as usize * BLOCK) * 4;
1862                result[to..to + BLOCK * 4]
1863                    .copy_from_slice(&block.rgba[row * BLOCK * 4..(row + 1) * BLOCK * 4]);
1864            }
1865        }
1866        let mut wrong = 0;
1867        for y in 64..128 {
1868            for x in 144..242 {
1869                let p = (y * WORLD + x) * 4;
1870                wrong += (result[p..p + 3] != epochs[if clipped_context { 2 } else { 1 }][p..p + 3])
1871                    as usize;
1872            }
1873        }
1874        assert_eq!(
1875            wrong, 0,
1876            "a complete observed widget must not retain the seam between partial epochs"
1877        );
1878    }
1879
1880    #[test]
1881    fn a_complete_widget_moment_repairs_phase_ambiguous_partial_references() {
1882        phase_ambiguous_widget(false);
1883    }
1884    #[test]
1885    fn a_widget_can_stay_coherent_when_only_its_static_margin_is_clipped() {
1886        phase_ambiguous_widget(true);
1887    }
1888
1889    #[test]
1890    fn phase_partition_is_a_fallback_for_groups_larger_than_the_observed_viewport() {
1891        let mut c = Consensus::new(Grid::covering(0, 0, 320, 128));
1892        c.viewport = (192, 128);
1893        c.sig.fill([80; 3]);
1894        for v in &mut c.vote {
1895            v.tier = TIER_CLEAN as i8;
1896            v.clean_views = 8;
1897        }
1898        for x in 1..19 {
1899            let b = 3 * c.grid.bw + x;
1900            c.vote[b].changed_views = 8;
1901            if x != 4 && x != 14 {
1902                c.phase_explained[b] = 8;
1903            }
1904        }
1905        let pieces = components(&c);
1906        assert_eq!(
1907            pieces.len(),
1908            2,
1909            "the phase bridge cannot force two observable objects into an impossible whole"
1910        );
1911        assert!(pieces
1912            .iter()
1913            .all(|p| p.bw * BLOCK <= 192 && p.bh * BLOCK <= 128));
1914        // The same evidence in a larger viewport admits a coherent whole-object
1915        // interpretation. Unknown dimensions likewise provide no reason to split it.
1916        c.viewport = (320, 128);
1917        assert_eq!(components(&c).len(), 1);
1918        c.viewport = (0, 0);
1919        assert_eq!(components(&c).len(), 1);
1920    }
1921
1922    #[test]
1923    fn static_context_outside_a_viewport_does_not_make_a_fitting_object_unobservable() {
1924        let mut c = Consensus::new(Grid::covering(0, 0, 224, 128));
1925        c.viewport = (192, 128);
1926        c.sig.fill([80; 3]);
1927        for v in &mut c.vote {
1928            v.tier = TIER_CLEAN as i8;
1929            v.clean_views = 8;
1930        }
1931        for x in 1..13 {
1932            let b = 3 * c.grid.bw + x;
1933            c.vote[b].changed_views = 8;
1934            if x != 4 && x != 10 {
1935                c.phase_explained[b] = 8;
1936            }
1937        }
1938        let groups = components(&c);
1939        assert_eq!(
1940            groups.len(),
1941            1,
1942            "the changing core fits; its wider static margin is support, not object extent"
1943        );
1944        c.viewport = (176, 128);
1945        assert_eq!(
1946            components(&c).len(),
1947            1,
1948            "block quantization cannot prove the object exceeds this viewport"
1949        );
1950        c.viewport = (128, 128);
1951        assert_eq!(
1952            components(&c).len(),
1953            2,
1954            "a smaller viewport makes the changing extent provably unobservable"
1955        );
1956    }
1957
1958    #[test]
1959    fn a_partial_later_view_does_not_split_a_complete_component_moment() {
1960        // Twenty changing blocks have a complete old state. A later state loses just one
1961        // block to an overlay. It must not replace nineteen blocks of the old moment.
1962        let (width, height) = (80, 64);
1963        let mut c = Consensus::new(Grid::covering(0, 0, width, height));
1964        for v in &mut c.vote {
1965            v.tier = TIER_CLEAN as i8;
1966            v.clean_views = 4;
1967            v.changed_views = 4;
1968        }
1969        let mut out = Output::new(&c, components(&c));
1970        assert_eq!(out.components.len(), 1);
1971        let mask = vec![1; width * height];
1972        let fp = Footprint {
1973            x: 0,
1974            y: 0,
1975            width,
1976            height,
1977            mask: &mask,
1978        };
1979        let old = solid_frame(width, height, 80);
1980        let new = solid_frame(width, height, 180);
1981        let cov = vec![255; width * height / 8];
1982        let main = CellMap::from_canvas(&CanvasView {
1983            x: 0,
1984            y: 0,
1985            width,
1986            height,
1987            rgba: &old,
1988            coverage: &cov,
1989        });
1990        let mut tiers = vec![TIER_CLEAN; width * height / 4];
1991        let mut writes = Vec::new();
1992        for (i, rgba) in [&old, &new].into_iter().enumerate() {
1993            if i == 1 {
1994                for y in 0..8 {
1995                    tiers[y * (width / 2)..y * (width / 2) + 8].fill(0);
1996                }
1997            }
1998            let tm = TierMap {
1999                x: 0,
2000                y: 0,
2001                cw: width / 2,
2002                ch: height / 2,
2003                tier: &tiers,
2004            };
2005            out.emit(
2006                &c,
2007                &Frame {
2008                    rgba,
2009                    width,
2010                    height,
2011                },
2012                None,
2013                &fp,
2014                &tm,
2015                (0, 0),
2016                24,
2017                i as u32,
2018                &main,
2019                &mut writes,
2020            );
2021        }
2022        assert!(
2023            out.frame.iter().all(|&f| f == 0),
2024            "a component must retain one complete moment: {:?}",
2025            out.frame
2026        );
2027    }
2028    #[test]
2029    fn a_clean_component_view_outranks_a_more_central_veiled_view() {
2030        let size = 192;
2031        let c = Consensus::new(Grid::covering(0, 0, size, size));
2032        let part = Component {
2033            bx: 5,
2034            by: 5,
2035            bw: 1,
2036            bh: 1,
2037            mask: vec![true],
2038            dynamic: vec![true],
2039            best: None,
2040            last_complete: Vec::new(),
2041        };
2042        let mut out = Output::new(&c, vec![part]);
2043        let pixels = solid_frame(size, size, 80);
2044        let mask = vec![1; size * size];
2045        let fp = Footprint {
2046            x: 0,
2047            y: 0,
2048            width: size,
2049            height: size,
2050            mask: &mask,
2051        };
2052        let clear = vec![TIER_CLEAN; size * size / 4];
2053        let veil = vec![TIER_VEIL; size * size / 4];
2054        let veiled = TierMap {
2055            x: 0,
2056            y: 0,
2057            cw: size / 2,
2058            ch: size / 2,
2059            tier: &veil,
2060        };
2061        let tm = TierMap {
2062            x: 0,
2063            y: 0,
2064            cw: size / 2,
2065            ch: size / 2,
2066            tier: &clear,
2067        };
2068        let coverage = vec![255; size * size / 8];
2069        let main = CellMap::from_canvas(&CanvasView {
2070            x: 0,
2071            y: 0,
2072            width: size,
2073            height: size,
2074            rgba: &pixels,
2075            coverage: &coverage,
2076        });
2077        let frame = Frame {
2078            rgba: &pixels,
2079            width: size,
2080            height: size,
2081        };
2082        let mut writes = Vec::new();
2083        out.emit(
2084            &c,
2085            &frame,
2086            None,
2087            &fp,
2088            &veiled,
2089            (0, 0),
2090            24,
2091            0,
2092            &main,
2093            &mut writes,
2094        );
2095        out.emit(
2096            &c,
2097            &frame,
2098            None,
2099            &fp,
2100            &tm,
2101            (0, 72),
2102            24,
2103            1,
2104            &main,
2105            &mut writes,
2106        );
2107        let b = 5 * c.grid.bw + 5;
2108        assert_eq!(
2109            out.frame[b], 1,
2110            "a known clean view outranks a more central veil"
2111        );
2112        out.emit(
2113            &c,
2114            &frame,
2115            None,
2116            &fp,
2117            &tm,
2118            (0, 0),
2119            24,
2120            2,
2121            &main,
2122            &mut writes,
2123        );
2124        assert_eq!(
2125            out.frame[b], 2,
2126            "genuine later content in the interior must still update"
2127        );
2128    }
2129    #[test]
2130    fn component_moments_reject_a_single_pulse_but_accept_a_confirmed_update() {
2131        let size = 128;
2132        let c = Consensus::new(Grid::covering(0, 0, size, size));
2133        let part = Component {
2134            bx: 3,
2135            by: 3,
2136            bw: 1,
2137            bh: 1,
2138            mask: vec![true],
2139            dynamic: vec![true],
2140            best: None,
2141            last_complete: Vec::new(),
2142        };
2143        let mut out = Output::new(&c, vec![part]);
2144        let mask = vec![1; size * size];
2145        let fp = Footprint {
2146            x: 0,
2147            y: 0,
2148            width: size,
2149            height: size,
2150            mask: &mask,
2151        };
2152        let clean = vec![TIER_CLEAN; size * size / 4];
2153        let tm = TierMap {
2154            x: 0,
2155            y: 0,
2156            cw: size / 2,
2157            ch: size / 2,
2158            tier: &clean,
2159        };
2160        let initial = solid_frame(size, size, 80);
2161        let coverage = vec![255; size * size / 8];
2162        let main = CellMap::from_canvas(&CanvasView {
2163            x: 0,
2164            y: 0,
2165            width: size,
2166            height: size,
2167            rgba: &initial,
2168            coverage: &coverage,
2169        });
2170        let mut writes = Vec::new();
2171        for (i, v) in [80, 80, 200, 80, 80, 200, 200].into_iter().enumerate() {
2172            let rgba = solid_frame(size, size, v);
2173            let f = Frame {
2174                rgba: &rgba,
2175                width: size,
2176                height: size,
2177            };
2178            out.emit(
2179                &c,
2180                &f,
2181                None,
2182                &fp,
2183                &tm,
2184                (0, 0),
2185                24,
2186                i as u32,
2187                &main,
2188                &mut writes,
2189            );
2190            assert_eq!(
2191                out.frame[3 * c.grid.bw + 3],
2192                [0, 1, 1, 1, 4, 4, 6][i],
2193                "moment {i}: an uncorroborated pulse is not a new content version"
2194            );
2195        }
2196        let final_pixels = solid_frame(size, size, 120);
2197        let final_frame = Frame {
2198            rgba: &final_pixels,
2199            width: size,
2200            height: size,
2201        };
2202        out.emit(
2203            &c,
2204            &final_frame,
2205            Some(&final_frame),
2206            &fp,
2207            &tm,
2208            (0, 0),
2209            24,
2210            7,
2211            &main,
2212            &mut writes,
2213        );
2214        assert_eq!(out.frame[3*c.grid.bw+3],7,"the final stable state of a still run is corroborated without counting duplicate poses as votes");
2215    }
2216    #[test]
2217    fn equally_corroborated_moments_prefer_visibility_but_an_unconfirmed_interior_pulse_does_not() {
2218        let size = 192;
2219        let c = Consensus::new(Grid::covering(0, 0, size, size));
2220        let part = Component {
2221            bx: 5,
2222            by: 5,
2223            bw: 1,
2224            bh: 1,
2225            mask: vec![true],
2226            dynamic: vec![true],
2227            best: None,
2228            last_complete: Vec::new(),
2229        };
2230        let mut out = Output::new(&c, vec![part]);
2231        let mask = vec![1; size * size];
2232        let clean = vec![TIER_CLEAN; size * size / 4];
2233        let fp = Footprint {
2234            x: 0,
2235            y: 0,
2236            width: size,
2237            height: size,
2238            mask: &mask,
2239        };
2240        let tm = TierMap {
2241            x: 0,
2242            y: 0,
2243            cw: size / 2,
2244            ch: size / 2,
2245            tier: &clean,
2246        };
2247        let pixels = solid_frame(size, size, 80);
2248        let coverage = vec![255; size * size / 8];
2249        let main = CellMap::from_canvas(&CanvasView {
2250            x: 0,
2251            y: 0,
2252            width: size,
2253            height: size,
2254            rgba: &pixels,
2255            coverage: &coverage,
2256        });
2257        let frame = Frame {
2258            rgba: &pixels,
2259            width: size,
2260            height: size,
2261        };
2262        let mut writes = Vec::new();
2263        out.emit(
2264            &c,
2265            &frame,
2266            Some(&frame),
2267            &fp,
2268            &tm,
2269            (0, 0),
2270            24,
2271            0,
2272            &main,
2273            &mut writes,
2274        );
2275        let faded = solid_frame(size, size, 30);
2276        let edge = Frame {
2277            rgba: &faded,
2278            width: size,
2279            height: size,
2280        };
2281        out.emit(
2282            &c,
2283            &edge,
2284            Some(&edge),
2285            &fp,
2286            &tm,
2287            (0, 72),
2288            24,
2289            1,
2290            &main,
2291            &mut writes,
2292        );
2293        let b = 5 * c.grid.bw + 5;
2294        assert_eq!(
2295            out.frame[b], 0,
2296            "a stable border fade is still a worse view than a stable interior one"
2297        );
2298        let pulse = solid_frame(size, size, 200);
2299        let changed = Frame {
2300            rgba: &pulse,
2301            width: size,
2302            height: size,
2303        };
2304        out.emit(
2305            &c,
2306            &changed,
2307            None,
2308            &fp,
2309            &tm,
2310            (0, 0),
2311            24,
2312            2,
2313            &main,
2314            &mut writes,
2315        );
2316        assert_eq!(
2317            out.frame[b], 0,
2318            "position cannot make an uncorroborated pulse win"
2319        );
2320        out.emit(
2321            &c,
2322            &changed,
2323            Some(&changed),
2324            &fp,
2325            &tm,
2326            (0, 0),
2327            24,
2328            3,
2329            &main,
2330            &mut writes,
2331        );
2332        assert_eq!(
2333            out.frame[b], 3,
2334            "a corroborated newer interior state still replaces the old one"
2335        );
2336    }
2337    #[test]
2338    fn two_clean_flat_observations_can_replace_a_veiled_consensus_but_nearby_structure_cannot() {
2339        let size = 128;
2340        for nearby_line in [false, true] {
2341            let mut c = Consensus::new(Grid::covering(0, 0, size, size));
2342            c.sig.fill([140; 3]);
2343            for v in &mut c.vote {
2344                v.tier = TIER_VEIL as i8;
2345            }
2346            let mut out = Output::new(&c, components(&c));
2347            let target = 3 * c.grid.bw + 3;
2348            let mask = vec![1; size * size];
2349            let clean = vec![TIER_CLEAN; size * size / 4];
2350            let fp = Footprint {
2351                x: 0,
2352                y: 0,
2353                width: size,
2354                height: size,
2355                mask: &mask,
2356            };
2357            let tm = TierMap {
2358                x: 0,
2359                y: 0,
2360                cw: size / 2,
2361                ch: size / 2,
2362                tier: &clean,
2363            };
2364            let main_pixels = solid_frame(size, size, 140);
2365            let coverage = vec![255; size * size / 8];
2366            let main = CellMap::from_canvas(&CanvasView {
2367                x: 0,
2368                y: 0,
2369                width: size,
2370                height: size,
2371                rgba: &main_pixels,
2372                coverage: &coverage,
2373            });
2374            let mut writes = Vec::new();
2375            for (i, value) in [200, 80, 80].into_iter().enumerate() {
2376                let mut rgba = solid_frame(size, size, value);
2377                if nearby_line {
2378                    for y in 32..80 {
2379                        rgba[(y * size + 66) * 4..(y * size + 66) * 4 + 3].fill(0);
2380                    }
2381                }
2382                let f = Frame {
2383                    rgba: &rgba,
2384                    width: size,
2385                    height: size,
2386                };
2387                out.emit(
2388                    &c,
2389                    &f,
2390                    None,
2391                    &fp,
2392                    &tm,
2393                    (0, 0),
2394                    24,
2395                    i as u32,
2396                    &main,
2397                    &mut writes,
2398                );
2399                if i < 2 {
2400                    assert!(
2401                        !writes.iter().any(|w| w.gx == 3 && w.gy == 3),
2402                        "one observation cannot overturn a consensus"
2403                    );
2404                }
2405            }
2406            assert_eq!(writes.iter().any(|w|w.gx==3&&w.gy==3&&w.frame==2),!nearby_line,"a nearby line may have moved across the block boundary; do not treat it as a flat background");
2407            if !nearby_line {
2408                assert_eq!(out.kind[target], Kind::Coherent);
2409            }
2410        }
2411    }
2412    #[test]
2413    fn context_rings_do_not_merge_independent_changing_objects_or_claim_unseen_blocks() {
2414        let mut c = Consensus::new(Grid::covering(0, 0, 128, 64));
2415        for v in &mut c.vote {
2416            v.tier = TIER_CLEAN as i8;
2417            v.clean_views = 4;
2418        }
2419        for i in [10, 12] {
2420            c.vote[i].changed_views = 4;
2421        }
2422        let parts = components(&c);
2423        assert_eq!(
2424            parts.len(),
2425            2,
2426            "an unchanged separator is context, not evidence of one changing object"
2427        );
2428        let mut ownership = vec![0u8; c.vote.len()];
2429        for part in &parts {
2430            for y in 0..part.bh {
2431                for x in 0..part.bw {
2432                    if part.mask[y * part.bw + x] {
2433                        ownership[(part.by + y) * c.grid.bw + part.bx + x] += 1;
2434                    }
2435                }
2436            }
2437        }
2438        assert!(
2439            ownership.iter().all(|&n| n <= 1),
2440            "shared context must not be overwritten by two moments"
2441        );
2442        c.vote[11].changed_views = 4;
2443        assert_eq!(
2444            components(&c).len(),
2445            1,
2446            "truly touching dynamic blocks still form one object"
2447        );
2448        let mut c = Consensus::new(Grid::covering(0, 0, 64, 64));
2449        c.vote[0] = BlockVote {
2450            tier: TIER_CLEAN as i8,
2451            clean_views: 4,
2452            changed_views: 4,
2453        };
2454        let parts = components(&c);
2455        assert_eq!(
2456            parts[0].mask.iter().filter(|&&v| v).count(),
2457            1,
2458            "never observed context cannot make a complete moment impossible"
2459        );
2460    }
2461
2462    #[test]
2463    fn directly_contradicted_context_outranks_a_heuristic_clean_label() {
2464        let mask = vec![1; W * H];
2465        let coverage = vec![255; W * H / 8];
2466        let fp = Footprint {
2467            x: 0,
2468            y: 0,
2469            width: W,
2470            height: H,
2471            mask: &mask,
2472        };
2473        let clean = vec![TIER_CLEAN; W * H / 4];
2474        let veil = vec![TIER_VEIL; W * H / 4];
2475        let tm = TierMap {
2476            x: 0,
2477            y: 0,
2478            cw: W / 2,
2479            ch: H / 2,
2480            tier: &clean,
2481        };
2482        let vt = TierMap {
2483            x: 0,
2484            y: 0,
2485            cw: W / 2,
2486            ch: H / 2,
2487            tier: &veil,
2488        };
2489        let old = frame(0, false);
2490        let bad = frame(1, true);
2491        let old = Frame {
2492            rgba: &old,
2493            width: W,
2494            height: H,
2495        };
2496        let bad = Frame {
2497            rgba: &bad,
2498            width: W,
2499            height: H,
2500        };
2501        let mut c = Consensus::new(Grid::covering(0, 0, W, H));
2502        c.observe(&old, &fp, &tm, (0, 0));
2503        let part = Component {
2504            bx: 0,
2505            by: 0,
2506            bw: 3,
2507            bh: 3,
2508            mask: vec![true; 9],
2509            dynamic: (0..9).map(|i| i == 4).collect(),
2510            best: None,
2511            last_complete: Vec::new(),
2512        };
2513        let mut out = Output::new(&c, vec![part]);
2514        let main = CellMap::from_canvas(&CanvasView {
2515            x: 0,
2516            y: 0,
2517            width: W,
2518            height: H,
2519            rgba: old.rgba,
2520            coverage: &coverage,
2521        });
2522        let mut writes = Vec::new();
2523        out.emit(
2524            &c,
2525            &old,
2526            Some(&old),
2527            &fp,
2528            &vt,
2529            (0, 0),
2530            24,
2531            0,
2532            &main,
2533            &mut writes,
2534        );
2535        out.emit(
2536            &c,
2537            &bad,
2538            Some(&bad),
2539            &fp,
2540            &tm,
2541            (0, 0),
2542            24,
2543            1,
2544            &main,
2545            &mut writes,
2546        );
2547        assert_eq!(
2548            out.frame[5], 0,
2549            "a visible transient in unchanged context cannot be excused by a clean-tier prediction"
2550        );
2551    }
2552    #[test]
2553    fn observed_low_tier_context_remains_available_for_relearning() {
2554        let mut c = Consensus::new(Grid::covering(0, 0, 64, 64));
2555        for v in &mut c.vote {
2556            v.tier = 0;
2557        }
2558        c.vote[5] = BlockVote {
2559            tier: TIER_CLEAN as i8,
2560            clean_views: 4,
2561            changed_views: 4,
2562        };
2563        let parts = components(&c);
2564        assert_eq!(parts.len(), 1);
2565        assert_eq!(parts[0].mask.iter().filter(|&&v|v).count(),9,"observed context can become clean in the second model; only unseen context is unavailable");
2566    }
2567
2568    #[test]
2569    fn lower_tier_context_cannot_veto_a_cleaner_complete_observation() {
2570        let mask = vec![1; W * H];
2571        let coverage = vec![255; W * H / 8];
2572        let fp = Footprint {
2573            x: 0,
2574            y: 0,
2575            width: W,
2576            height: H,
2577            mask: &mask,
2578        };
2579        let clean = vec![TIER_CLEAN; W * H / 4];
2580        let veil = vec![TIER_VEIL; W * H / 4];
2581        let opaque = vec![0; W * H / 4];
2582        let tm = TierMap {
2583            x: 0,
2584            y: 0,
2585            cw: W / 2,
2586            ch: H / 2,
2587            tier: &clean,
2588        };
2589        let vt = TierMap {
2590            x: 0,
2591            y: 0,
2592            cw: W / 2,
2593            ch: H / 2,
2594            tier: &veil,
2595        };
2596        let ot = TierMap {
2597            x: 0,
2598            y: 0,
2599            cw: W / 2,
2600            ch: H / 2,
2601            tier: &opaque,
2602        };
2603        let old_pixels = frame(0, true);
2604        let clean_pixels = frame(1, false);
2605        let old = Frame {
2606            rgba: &old_pixels,
2607            width: W,
2608            height: H,
2609        };
2610        let later = Frame {
2611            rgba: &clean_pixels,
2612            width: W,
2613            height: H,
2614        };
2615        let mut c = Consensus::new(Grid::covering(0, 0, W, H));
2616        c.observe(&old, &fp, &ot, (0, 0));
2617        let part = Component {
2618            bx: 0,
2619            by: 0,
2620            bw: 3,
2621            bh: 3,
2622            mask: vec![true; 9],
2623            dynamic: (0..9).map(|i| i == 4).collect(),
2624            best: None,
2625            last_complete: Vec::new(),
2626        };
2627        let mut out = Output::new(&c, vec![part]);
2628        let main = CellMap::from_canvas(&CanvasView {
2629            x: 0,
2630            y: 0,
2631            width: W,
2632            height: H,
2633            rgba: old.rgba,
2634            coverage: &coverage,
2635        });
2636        let mut writes = Vec::new();
2637        out.emit(
2638            &c,
2639            &old,
2640            Some(&old),
2641            &fp,
2642            &vt,
2643            (0, 0),
2644            24,
2645            0,
2646            &main,
2647            &mut writes,
2648        );
2649        out.emit(
2650            &c,
2651            &later,
2652            Some(&later),
2653            &fp,
2654            &tm,
2655            (0, 0),
2656            24,
2657            1,
2658            &main,
2659            &mut writes,
2660        );
2661        assert_eq!(
2662            out.frame[5], 1,
2663            "an opaque first-pass reference cannot label a genuinely cleaner boundary transient"
2664        );
2665    }
2666    struct LocalReference(fn(i64, i64) -> [u8; 3]);
2667    impl CellRef for LocalReference {
2668        fn cell(&self, x: i64, y: i64) -> Option<[u8; 3]> {
2669            Some((self.0)(x, y))
2670        }
2671        fn cells(&self) -> (i64, i64, i64, i64) {
2672            (-1000, -1000, 1000, 1000)
2673        }
2674    }
2675    fn signature(reference: &impl CellRef) -> BlockSignature {
2676        std::array::from_fn(|k| {
2677            reference
2678                .cell((k % SIDE) as i64, (k / SIDE) as i64)
2679                .unwrap()
2680        })
2681    }
2682    #[test]
2683    fn strict_agreement_rejects_structure_absent_from_a_smooth_reference() {
2684        let reference = LocalReference(|_, _| [230; 3]);
2685        let expected = signature(&reference);
2686        let mut observed = expected;
2687        observed[3 * SIDE + 3] = [190; 3];
2688        let difference = compare(&observed, &expected);
2689        assert!(agrees(difference, 24));
2690        assert_eq!(
2691            difference.2, 0,
2692            "the legacy 48-level outlier threshold misses this mark"
2693        );
2694        assert!(
2695            !agrees_strictly(difference, 24, &observed, &reference, (0, 0)),
2696            "an isolated low-contrast mark is not codec phase on a flat page"
2697        );
2698    }
2699    #[test]
2700    fn strict_agreement_keeps_phase_changes_at_real_edges_including_block_boundaries() {
2701        for edge in [0, 4] {
2702            let reference = LocalReference(if edge == 0 {
2703                |x, y| [if x < 0 && y < 4 { 140 } else { 80 }; 3]
2704            } else {
2705                |x, y| [if x >= 4 && y < 4 { 140 } else { 80 }; 3]
2706            });
2707            let expected = signature(&reference);
2708            let mut observed = expected;
2709            // A one-native-pixel phase shift mixes the two sides in four 2x2 cells.
2710            let column = if edge == 0 { 0 } else { 3 };
2711            for y in 0..4 {
2712                observed[y * SIDE + column] = [110; 3];
2713            }
2714            let difference = compare(&observed, &expected);
2715            assert!(
2716                agrees_strictly(difference, 24, &observed, &reference, (0, 0)),
2717                "real contrast across a block boundary must remain available as phase evidence"
2718            );
2719        }
2720    }
2721    #[test]
2722    fn a_small_transient_cannot_freeze_an_otherwise_static_block() {
2723        let (width, height) = (160, 160);
2724        let mut clean = solid_frame(width, height, 230);
2725        for y in 0..height {
2726            for x in 0..width {
2727                let value = if (x / 8 + y / 8) % 2 == 0 { 230 } else { 248 };
2728                clean[(y * width + x) * 4..(y * width + x) * 4 + 3].fill(value);
2729            }
2730        }
2731        let mut transient = clean.clone();
2732        for y in 66..68 {
2733            for x in 66..68 {
2734                transient[(y * width + x) * 4..(y * width + x) * 4 + 3].fill(190);
2735            }
2736        }
2737        let mask = vec![1; width * height];
2738        let footprint = Footprint {
2739            x: 0,
2740            y: 0,
2741            width,
2742            height,
2743            mask: &mask,
2744        };
2745        let tier = vec![TIER_CLEAN; width * height / (CELL * CELL)];
2746        let tiers = TierMap {
2747            x: 0,
2748            y: 0,
2749            cw: width / CELL,
2750            ch: height / CELL,
2751            tier: &tier,
2752        };
2753        let clean_frame = Frame {
2754            rgba: &clean,
2755            width,
2756            height,
2757        };
2758        let mut consensus = Consensus::new(Grid::covering(0, 0, width, height));
2759        // The checker period is 16 pixels; these are distinct, consistent world views.
2760        for x in [0, 16, 32] {
2761            consensus.observe(&clean_frame, &footprint, &tiers, (x, 0));
2762        }
2763        let mut output = Output::new(&consensus, components(&consensus));
2764        let coverage = vec![255; width * height / 8];
2765        let main = CellMap::from_canvas(&CanvasView {
2766            x: 0,
2767            y: 0,
2768            width,
2769            height,
2770            rgba: &clean,
2771            coverage: &coverage,
2772        });
2773        let mut writes = Vec::new();
2774        output.emit(
2775            &consensus,
2776            &Frame {
2777                rgba: &transient,
2778                width,
2779                height,
2780            },
2781            None,
2782            &footprint,
2783            &tiers,
2784            (0, 0),
2785            24,
2786            0,
2787            &main,
2788            &mut writes,
2789        );
2790        output.emit(
2791            &consensus,
2792            &clean_frame,
2793            None,
2794            &footprint,
2795            &tiers,
2796            (16, 0),
2797            24,
2798            1,
2799            &main,
2800            &mut writes,
2801        );
2802        let block = 4 * consensus.grid.bw + 4;
2803        assert_eq!(
2804            output.frame[block], 1,
2805            "the later clean view must replace the small transient"
2806        );
2807        assert_eq!(output.kind[block], Kind::Coherent);
2808    }
2809    #[test]
2810    fn local_contrast_allows_decode_noise_on_top_of_a_phase_shift() {
2811        let reference = LocalReference(|x, y| [if x < 0 && y < 4 { 120 } else { 100 }; 3]);
2812        let expected = signature(&reference);
2813        let mut observed = expected;
2814        for y in 0..4 {
2815            observed[y * SIDE] = [124; 3];
2816        }
2817        let difference = compare(&observed, &expected);
2818        assert!(agrees_strictly(difference,24,&observed,&reference,(0,0)),"a real 20-level edge plus four levels of decode noise is still a valid phase observation");
2819    }
2820    #[derive(Clone, Copy, PartialEq, Eq)]
2821    enum WeakUpdate {
2822        Persistent,
2823        Moving,
2824        SettledStill,
2825        LastFramePulse,
2826    }
2827
2828    fn weak_update_source(mode: WeakUpdate) -> (i32, Kind) {
2829        let persistent = mode != WeakUpdate::Moving;
2830        let views = if mode == WeakUpdate::SettledStill {
2831            2
2832        } else {
2833            3
2834        };
2835        let (width, height) = (160, 160);
2836        let old = solid_frame(width, height, 230);
2837        let mut first = old.clone();
2838        let mut second = old.clone();
2839        for y in 66..68 {
2840            for x in 66..68 {
2841                first[(y * width + x) * 4..(y * width + x) * 4 + 3].fill(190);
2842            }
2843        }
2844        let second_x = if persistent { 50 } else { 54 };
2845        for y in 66..68 {
2846            for x in second_x..second_x + 2 {
2847                second[(y * width + x) * 4..(y * width + x) * 4 + 3].fill(190);
2848            }
2849        }
2850        let mask = vec![1; width * height];
2851        let footprint = Footprint {
2852            x: 0,
2853            y: 0,
2854            width,
2855            height,
2856            mask: &mask,
2857        };
2858        let tier = vec![TIER_CLEAN; width * height / (CELL * CELL)];
2859        let tiers = TierMap {
2860            x: 0,
2861            y: 0,
2862            cw: width / CELL,
2863            ch: height / CELL,
2864            tier: &tier,
2865        };
2866        let frames = [
2867            Frame {
2868                rgba: &old,
2869                width,
2870                height,
2871            },
2872            Frame {
2873                rgba: &first,
2874                width,
2875                height,
2876            },
2877            Frame {
2878                rgba: &second,
2879                width,
2880                height,
2881            },
2882        ];
2883        let poses = [(-80, 0), (0, 0), (16, 0)];
2884        let mut consensus = Consensus::new(Grid::covering(-80, 0, 256, height));
2885        for (frame, pose) in frames.iter().zip(poses).take(views) {
2886            consensus.observe(frame, &footprint, &tiers, pose);
2887        }
2888        let mut output = Output::new(&consensus, components(&consensus));
2889        let main_pixels = solid_frame(256, height, 230);
2890        let coverage = vec![255; 256 * height / 8];
2891        let main = CellMap::from_canvas(&CanvasView {
2892            x: -80,
2893            y: 0,
2894            width: 256,
2895            height,
2896            rgba: &main_pixels,
2897            coverage: &coverage,
2898        });
2899        let mut writes = Vec::new();
2900        for (index, (frame, pose)) in frames.iter().zip(poses).take(views).enumerate() {
2901            let before = match (mode, index) {
2902                (WeakUpdate::SettledStill, 1) => Some(&frames[1]),
2903                (WeakUpdate::LastFramePulse, 2) => Some(&frames[0]),
2904                _ => None,
2905            };
2906            output.emit(
2907                &consensus,
2908                frame,
2909                before,
2910                &footprint,
2911                &tiers,
2912                pose,
2913                24,
2914                index as u32,
2915                &main,
2916                &mut writes,
2917            );
2918        }
2919        let block = 4 * consensus.grid.bw + (4 - consensus.grid.gx) as usize;
2920        (output.frame[block], output.kind[block])
2921    }
2922    #[test]
2923    fn persistent_weak_content_can_replace_an_earlier_edge_view() {
2924        let (source, kind) = weak_update_source(WeakUpdate::Persistent);
2925        assert!(
2926            source == 1 || source == 2,
2927            "a newly loaded weak mark must not be lost merely because the early reference is blank"
2928        );
2929        assert_eq!(kind, Kind::Coherent);
2930    }
2931    #[test]
2932    fn different_weak_marks_do_not_corroborate_each_other() {
2933        let (source, kind) = weak_update_source(WeakUpdate::Moving);
2934        assert_eq!(
2935            source, 0,
2936            "two moving marks that merely both resemble the reference are not corroboration"
2937        );
2938        assert_ne!(kind, Kind::Coherent);
2939    }
2940    #[test]
2941    fn weak_content_can_settle_at_the_end_of_one_pose() {
2942        let (source, kind) = weak_update_source(WeakUpdate::SettledStill);
2943        assert_eq!(
2944            source, 1,
2945            "the actual previous still frame can corroborate freshly loaded content"
2946        );
2947        assert_eq!(kind, Kind::Coherent);
2948    }
2949    #[test]
2950    fn an_old_witness_cannot_confirm_a_new_pulse_at_the_end_of_a_still_run() {
2951        let (source, kind) = weak_update_source(WeakUpdate::LastFramePulse);
2952        assert_eq!(
2953            source, 0,
2954            "a contradictory immediate predecessor overrides the older witness"
2955        );
2956        assert_ne!(kind, Kind::Coherent);
2957    }
2958    // A weak transient can resemble sampling phase beside a real edge. Spatial
2959    // plausibility alone must not permanently lock it; genuine repeated phase
2960    // remains admissible after a second independent observation.
2961    #[test]
2962    fn phase_compatible_edges_need_temporal_evidence_before_becoming_permanent() {
2963        let size = 128;
2964        let edge = |at: usize| {
2965            let mut rgba = solid_frame(size, size, 190);
2966            for y in 0..size {
2967                for x in at..size {
2968                    rgba[(y * size + x) * 4..(y * size + x) * 4 + 3].fill(210);
2969                }
2970            }
2971            rgba
2972        };
2973        let base = edge(66);
2974        let coverage = vec![255; size * size / 8];
2975        let main = CellMap::from_canvas(&CanvasView {
2976            x: 0,
2977            y: 0,
2978            width: size,
2979            height: size,
2980            rgba: &base,
2981            coverage: &coverage,
2982        });
2983        let mask = vec![1; size * size];
2984        let clean = vec![TIER_CLEAN; size * size / 4];
2985        let fp = Footprint {
2986            x: 0,
2987            y: 0,
2988            width: size,
2989            height: size,
2990            mask: &mask,
2991        };
2992        let tiers = TierMap {
2993            x: 0,
2994            y: 0,
2995            cw: size / 2,
2996            ch: size / 2,
2997            tier: &clean,
2998        };
2999        let reference = Frame {
3000            rgba: &base,
3001            width: size,
3002            height: size,
3003        };
3004        for persistent in [false, true] {
3005            let mut consensus = Consensus::new(Grid::covering(0, 0, size, size));
3006            for _ in 0..4 {
3007                consensus.observe(&reference, &fp, &tiers, (0, 0));
3008            }
3009            let target = 4 * consensus.grid.bw + 4;
3010            let mut output = Output::new(&consensus, components(&consensus));
3011            let mut first = if persistent { edge(64) } else { base.clone() };
3012            if !persistent {
3013                for y in 66..78 {
3014                    for x in 64..66 {
3015                        first[(y * size + x) * 4..(y * size + x) * 4 + 3].fill(210);
3016                    }
3017                }
3018            }
3019            output.emit(
3020                &consensus,
3021                &Frame {
3022                    rgba: &first,
3023                    width: size,
3024                    height: size,
3025                },
3026                None,
3027                &fp,
3028                &tiers,
3029                (0, 0),
3030                24,
3031                0,
3032                &main,
3033                &mut Vec::new(),
3034            );
3035            assert_ne!(
3036                output.kind[target],
3037                Kind::Coherent,
3038                "a phase-sized protrusion is not yet independently confirmed"
3039            );
3040            if persistent {
3041                assert!(output.finish(&consensus,24).iter().any(|part|part.gx==4 && part.gy==4 && part.frame==0),
3042                    "a lone valid phase view stays available; deferring a lock must not discard its pixels");
3043            }
3044            let second = edge(if persistent { 63 } else { 65 });
3045            output.emit(
3046                &consensus,
3047                &Frame {
3048                    rgba: &second,
3049                    width: size,
3050                    height: size,
3051                },
3052                None,
3053                &fp,
3054                &tiers,
3055                (1, 0),
3056                24,
3057                1,
3058                &main,
3059                &mut Vec::new(),
3060            );
3061            assert_eq!(
3062                output.kind[target],
3063                Kind::Coherent,
3064                "a clean reference match or a corroborated phase is accepted"
3065            );
3066            assert_eq!(output.frame[target], 1);
3067        }
3068    }
3069
3070    // Failure cases for a component's static context: a low-contrast object overflowing the component must
3071    // count against the moment; a stationary run's settled context, codec noise, or a flat page must not;
3072    // a luma outlier always does.
3073    #[test]
3074    fn context_contradiction_cases() {
3075        let page = [[239u8, 235, 227]; CELLS];
3076        let card = [[250u8, 248, 244]; CELLS];
3077        let noisy = [[241u8, 236, 228]; CELLS];
3078        let mut cursor = page;
3079        cursor[9] = [0, 0, 0];
3080        assert!(!contradicts(&page, &page, 24, || false));
3081        assert!(!contradicts(&noisy, &page, 24, || false), "codec noise");
3082        assert!(
3083            contradicts(&card, &page, 24, || false),
3084            "a low-contrast card edge in a context block"
3085        );
3086        assert!(
3087            !contradicts(&card, &page, 24, || true),
3088            "the same difference confirmed by the stationary run's previous frame"
3089        );
3090        assert!(contradicts(&cursor, &page, 24, || true), "a luma outlier");
3091        let mut texture = [[0u8; 3]; CELLS];
3092        let mut refined = [[0u8; 3]; CELLS];
3093        for k in 0..CELLS {
3094            let v = if (k % SIDE + k / SIDE).is_multiple_of(2) {
3095                100
3096            } else {
3097                140
3098            };
3099            texture[k] = [v; 3];
3100            refined[k] = [v + 12; 3];
3101        }
3102        assert!(
3103            !contradicts(&refined, &texture, 24, || false),
3104            "a sub-outlier change of textured context (map imagery refining)"
3105        );
3106    }
3107}