#[allow(unused_imports)]
use alloc::vec;
#[allow(unused_imports)]
use alloc::vec::Vec;
#[allow(unused_imports)]
use rusty_h264_common::fmath::{F32Ext as _, F64Ext as _};
#[allow(unused_imports)]
use rusty_h264_common::once::OnceLock;
use core::cell::OnceCell;
pub(crate) fn mb_variance(sy: &[u8], cw: usize, mb_x: usize, mb_y: usize) -> i64 {
let base = mb_y * 16 * cw + mb_x * 16;
let (mut s, mut ss) = (0u32, 0u32);
for r in 0..16 {
let row = &sy[base + r * cw..base + r * cw + 16];
for &p in row {
let v = p as u32;
s += v;
ss += v * v;
}
}
ss as i64 - (s as i64) * (s as i64) / 256 }
fn b2_mgain(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> (f64, f64) {
const WIDE: isize = 8;
const STEP: isize = 4;
const TARGET: usize = 24;
let sad16 = |bx: usize, by: usize, rx: isize, ry: isize| -> Option<u32> {
if rx < 0 || ry < 0 || rx as usize + 16 > cw || ry as usize + 16 > ch {
return None;
}
let (rx, ry) = (rx as usize, ry as usize);
let mut s = 0u32;
for dy in 0..16 {
let a = &sy[(by + dy) * cw + bx..][..16];
let b = &ref_y[(ry + dy) * cw + rx..][..16];
s += a
.iter()
.zip(b)
.map(|(&p, &q)| p.abs_diff(q) as u32)
.sum::<u32>();
}
Some(s)
};
let (mbw, mbh) = (cw / 16, ch / 16);
if mbw < 6 || mbh < 6 {
return (0.0, 0.0);
}
let inner = (mbw - 4) * (mbh - 4);
let stride = (inner / TARGET).max(1);
let (mut acc, mut dc, mut n) = (0.0f64, 0.0f64, 0u32);
let w = mbw - 4;
let (mut rx, mut ry) = (0usize, 0usize);
let mut i = 0usize;
while i < inner {
let (mx, my) = (2 + rx, 2 + ry);
let (bx, by) = (mx * 16, my * 16);
if let Some(s0) = sad16(bx, by, bx as isize, by as isize) {
let (mut ms, mut mr) = (0u32, 0u32);
for dy in 0..16 {
ms += sy[(by + dy) * cw + bx..][..16]
.iter()
.map(|&v| v as u32)
.sum::<u32>();
mr += ref_y[(by + dy) * cw + bx..][..16]
.iter()
.map(|&v| v as u32)
.sum::<u32>();
}
dc += ms.abs_diff(mr) as f64 / (s0 + 1) as f64;
let mut best = s0;
let mut dy = -WIDE;
while dy <= WIDE {
let mut dx = -WIDE;
while dx <= WIDE {
if let Some(s) = sad16(bx, by, bx as isize + dx, by as isize + dy) {
best = best.min(s);
}
dx += STEP;
}
dy += STEP;
}
acc += (s0 - best) as f64 / (s0 + 1) as f64;
n += 1;
}
i += stride;
rx += stride;
while rx >= w {
rx -= w;
ry += 1;
}
}
if n == 0 {
(0.0, 0.0)
} else {
(acc / n as f64, dc / n as f64)
}
}
fn me_wide_headroom(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> f64 {
const LOCAL: isize = 2; const WIDE: isize = 24; const STEP: isize = 4; const TARGET: usize = 24; let sad16 = |bx: usize, by: usize, rx: isize, ry: isize| -> Option<u32> {
if rx < 0 || ry < 0 || rx as usize + 16 > cw || ry as usize + 16 > ch {
return None;
}
let (rx, ry) = (rx as usize, ry as usize);
let mut s = 0u32;
for dy in 0..16 {
let a = &sy[(by + dy) * cw + bx..][..16];
let b = &ref_y[(ry + dy) * cw + rx..][..16];
s += a
.iter()
.zip(b)
.map(|(&p, &q)| p.abs_diff(q) as u32)
.sum::<u32>();
}
Some(s)
};
let (mbw, mbh) = (cw / 16, ch / 16);
if mbw < 6 || mbh < 6 {
return 0.0;
}
let inner = (mbw - 4) * (mbh - 4);
let stride = (inner / TARGET).max(1);
let (mut acc, mut n) = (0.0f64, 0u32);
let w = mbw - 4;
let (mut rx, mut ry) = (0usize, 0usize);
let mut i = 0usize;
while i < inner {
let (mx, my) = (2 + rx, 2 + ry);
let (bx, by) = (mx * 16, my * 16);
let mut best_local = u32::MAX;
for dy in -LOCAL..=LOCAL {
for dx in -LOCAL..=LOCAL {
if let Some(s) = sad16(bx, by, bx as isize + dx, by as isize + dy) {
best_local = best_local.min(s);
}
}
}
let mut best_wide = best_local;
let mut dy = -WIDE;
while dy <= WIDE {
let mut dx = -WIDE;
while dx <= WIDE {
if let Some(s) = sad16(bx, by, bx as isize + dx, by as isize + dy) {
best_wide = best_wide.min(s);
}
dx += STEP;
}
dy += STEP;
}
if best_local > 0 {
acc += (best_local - best_wide) as f64 / best_local as f64;
n += 1;
}
i += stride;
rx += stride;
while rx >= w {
rx -= w;
ry += 1;
}
}
if n == 0 {
0.0
} else {
100.0 * acc / n as f64
}
}
fn global_mc_residual(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> f64 {
if cw < 48 || ch < 48 {
return f64::INFINITY;
}
let sad = |dx: isize, dy: isize| -> u64 {
let mut s = 0u64;
let mut y = 16;
while y < ch - 16 {
let cbase = (y * cw) as isize;
let rbase = (y as isize + dy) * cw as isize + dx;
let mut x = 16isize;
while x < (cw - 16) as isize {
let c = sy[(cbase + x) as usize] as i32;
let r = ref_y[(rbase + x) as usize] as i32;
s += (c - r).unsigned_abs() as u64;
x += 8;
}
y += 8;
}
s
};
let (mut best, mut bc) = ((0isize, 0isize), u64::MAX);
let mut dy = -12;
while dy <= 12 {
let mut dx = -12;
while dx <= 12 {
let c = sad(dx, dy);
if c < bc {
bc = c;
best = (dx, dy);
}
dx += 4;
}
dy += 4;
}
for dy in best.1 - 3..=best.1 + 3 {
for dx in best.0 - 3..=best.0 + 3 {
let c = sad(dx, dy);
if c < bc {
bc = c;
}
}
}
let nx = (16..cw - 16).step_by(8).count();
let ny = (16..ch - 16).step_by(8).count();
bc as f64 / (nx * ny).max(1) as f64
}
fn frame_median_mb_var(sy: &[u8], cw: usize, mb_w: usize, mb_h: usize) -> i64 {
let mut vs: Vec<i64> = Vec::with_capacity(mb_w * mb_h);
for my in 0..mb_h {
for mx in 0..mb_w {
let (mut sum, mut sq) = (0i64, 0i64);
for r in (0..16).step_by(2) {
let row = (my * 16 + r) * cw + mx * 16;
for c in (0..16).step_by(2) {
let v = sy[row + c] as i64;
sum += v;
sq += v * v;
}
}
let n = 64i64;
vs.push((sq - sum * sum / n) / n);
}
}
if vs.is_empty() {
return 0;
}
let mid = vs.len() / 2;
*vs.select_nth_unstable(mid).1
}
fn flat_hist(sy: &[u8], cw: usize, ch: usize) -> (f64, f64) {
let mut hist = [0u64; 256];
let (mut runs, mut px) = (0u64, 0u64);
let mut y = 0;
while y < ch {
let row = &sy[y * cw..y * cw + cw];
let mut prev = 0x100u32; for &p in row {
hist[p as usize] += 1;
if p as u32 != prev {
runs += 1;
prev = p as u32;
}
}
px += cw as u64;
y += 4;
}
let mut bins = hist;
bins.sort_unstable_by(|a, b| b.cmp(a));
let top16: u64 = bins[..16].iter().sum();
(
px as f64 / runs.max(1) as f64,
top16 as f64 / px.max(1) as f64,
)
}
fn grain_floor(sy: &[u8], cw: usize, ch: usize, ref_y: &[u8]) -> f64 {
const TARGET: usize = 48;
let (mbw, mbh) = (cw / 16, ch / 16);
if mbw < 6 || mbh < 6 {
return 0.0;
}
let inner = (mbw - 4) * (mbh - 4);
let stride = (inner / TARGET).max(1);
let mut floors: Vec<f64> = Vec::with_capacity(TARGET + 1);
let w = mbw - 4;
let (mut rx, mut ry) = (0usize, 0usize);
let mut i = 0usize;
while i < inner {
let (mx, my) = (2 + rx, 2 + ry);
let (bx, by) = (mx * 16, my * 16);
let mut s = 0u32;
for dy in 0..16 {
let a = &sy[(by + dy) * cw + bx..][..16];
let b = &ref_y[(by + dy) * cw + bx..][..16];
s += a
.iter()
.zip(b)
.map(|(&p, &q)| p.abs_diff(q) as u32)
.sum::<u32>();
}
floors.push(s as f64 / 256.0);
i += stride;
rx += stride;
while rx >= w {
rx -= w;
ry += 1;
}
}
if floors.is_empty() {
return 0.0;
}
floors.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
floors[floors.len() / 4]
}
pub(crate) struct FrameSignals<'a> {
sy: &'a [u8],
cw: usize,
ch: usize,
mb_w: usize,
mb_h: usize,
ref_y: Option<&'a [u8]>,
vars: OnceCell<Vec<i64>>,
vars_sorted: OnceCell<Vec<i64>>,
lv: OnceCell<(Vec<f64>, f64, f64)>, median_var: OnceCell<i64>,
gmc: OnceCell<f64>,
headroom: OnceCell<f64>,
mgain_dc: OnceCell<(f64, f64)>,
flat_hist: OnceCell<(f64, f64)>,
grain: OnceCell<f64>,
}
impl<'a> FrameSignals<'a> {
pub(crate) fn new(
sy: &'a [u8],
cw: usize,
mb_w: usize,
mb_h: usize,
ref_y: Option<&'a [u8]>,
) -> Self {
FrameSignals {
sy,
cw,
ch: mb_h * 16,
mb_w,
mb_h,
ref_y,
vars: OnceCell::new(),
vars_sorted: OnceCell::new(),
lv: OnceCell::new(),
median_var: OnceCell::new(),
gmc: OnceCell::new(),
headroom: OnceCell::new(),
mgain_dc: OnceCell::new(),
flat_hist: OnceCell::new(),
grain: OnceCell::new(),
}
}
pub(crate) fn has_ref(&self) -> bool {
self.ref_y.is_some()
}
pub(crate) fn n_mbs(&self) -> usize {
self.mb_w * self.mb_h
}
pub(crate) fn mb_vars(&self) -> &[i64] {
self.vars.get_or_init(|| {
(0..self.mb_h)
.flat_map(|my| (0..self.mb_w).map(move |mx| (mx, my)))
.map(|(mx, my)| mb_variance(self.sy, self.cw, mx, my))
.collect()
})
}
pub(crate) fn var_percentile_thresh(&self, q: f64) -> i64 {
let vars = self.vars_sorted.get_or_init(|| {
let mut v = self.mb_vars().to_vec();
v.sort_unstable();
v
});
let idx = (((1.0 - q) * vars.len() as f64) as usize).min(vars.len() - 1);
vars[idx]
}
pub(crate) fn log_vars(&self) -> &(Vec<f64>, f64, f64) {
self.lv.get_or_init(|| {
let poly = crate::fastmath::polytier_on();
let lv: Vec<f64> = self
.mb_vars()
.iter()
.map(|&v| {
if v == 0 {
0.0
} else if poly {
crate::fastmath::log2_poly((v + 1) as f64)
} else {
rusty_h264_common::fmath::log2((v + 1) as f64)
}
})
.collect();
let n = lv.len().max(1) as f64;
let mean = lv.iter().sum::<f64>() / n;
let std = rusty_h264_common::fmath::sqrt(
lv.iter()
.map(|&l| rusty_h264_common::fmath::powi(l - mean, 2))
.sum::<f64>()
/ n,
);
(lv, mean, std)
})
}
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) fn lv_spread(&self) -> f64 {
self.log_vars().2
}
pub(crate) fn median_var(&self) -> i64 {
*self
.median_var
.get_or_init(|| frame_median_mb_var(self.sy, self.cw, self.mb_w, self.mb_h))
}
pub(crate) fn gmc_residual(&self) -> f64 {
*self.gmc.get_or_init(|| match self.ref_y {
Some(r) => global_mc_residual(self.sy, self.cw, self.ch, r),
None => f64::INFINITY,
})
}
pub(crate) fn headroom(&self) -> f64 {
*self.headroom.get_or_init(|| match self.ref_y {
Some(r) => me_wide_headroom(self.sy, self.cw, self.ch, r),
None => 0.0,
})
}
pub(crate) fn mgain_dc(&self) -> (f64, f64) {
*self.mgain_dc.get_or_init(|| match self.ref_y {
Some(r) => b2_mgain(self.sy, self.cw, self.ch, r),
None => (0.0, 0.0),
})
}
pub(crate) fn is_screen(&self) -> bool {
self.flat_run() >= 4.0 && self.hist_top16() >= 0.5
}
pub(crate) fn flat_run(&self) -> f64 {
self.flat_hist_pair().0
}
pub(crate) fn hist_top16(&self) -> f64 {
self.flat_hist_pair().1
}
fn flat_hist_pair(&self) -> (f64, f64) {
*self
.flat_hist
.get_or_init(|| flat_hist(self.sy, self.cw, self.ch))
}
pub(crate) fn grain_signature(&self) -> bool {
self.has_ref()
&& self.median_var() < grain_var_max()
&& self.grain_floor() > grain_floor_min()
&& self.mgain_dc().0 < grain_mgain_max()
}
pub(crate) fn grain_floor(&self) -> f64 {
*self.grain.get_or_init(|| match self.ref_y {
Some(r) => grain_floor(self.sy, self.cw, self.ch, r),
None => 0.0,
})
}
}
fn grain_var_max() -> i64 {
rusty_h264_common::cached_knob!(i64, {
rusty_h264_common::knob("RFF_GRAIN_VARMAX")
.and_then(|v| v.parse().ok())
.unwrap_or(200)
})
}
fn grain_floor_min() -> f64 {
rusty_h264_common::cached_knob!(f64, env_f64("RFF_GRAIN_FLOORMIN").unwrap_or(5.0))
}
fn grain_mgain_max() -> f64 {
rusty_h264_common::cached_knob!(f64, env_f64("RFF_GRAIN_MGAINMAX").unwrap_or(0.1))
}
fn env_f64(k: &str) -> Option<f64> {
rusty_h264_common::knob(k).and_then(|v| v.parse().ok())
}
pub mod census {
#[allow(unused_imports)]
use alloc::{
boxed::Box,
format,
string::{String, ToString},
vec,
vec::Vec,
};
use rusty_h264_common::atomic::AtomicU64;
#[allow(unused_imports)]
use rusty_h264_common::once::OnceLock;
use core::sync::atomic::Ordering::Relaxed;
pub const N: usize = 9;
pub static NAMES: [&str; N] = [
"aq_grain_veto", "mbtree_grain", "mbtree_backoff", "sub8_grain", "sub8_split", "sub8_rd_revert", "intra_rd_flip", "shape_rd_flip", "mbtree_spread", ];
static FIRED: [AtomicU64; N] = [
AtomicU64::new(0),
AtomicU64::new(0),
AtomicU64::new(0),
AtomicU64::new(0),
AtomicU64::new(0),
AtomicU64::new(0),
AtomicU64::new(0),
AtomicU64::new(0),
AtomicU64::new(0),
];
static SEEN: [AtomicU64; N] = [
AtomicU64::new(0),
AtomicU64::new(0),
AtomicU64::new(0),
AtomicU64::new(0),
AtomicU64::new(0),
AtomicU64::new(0),
AtomicU64::new(0),
AtomicU64::new(0),
AtomicU64::new(0),
];
thread_local! {
static PENDING: core::cell::RefCell<Vec<(u8, bool)>> =
const { core::cell::RefCell::new(Vec::new()) };
}
static BY_T8: [[(AtomicU64, AtomicU64); N]; 2] = [
[const { (AtomicU64::new(0), AtomicU64::new(0)) }; N],
[const { (AtomicU64::new(0), AtomicU64::new(0)) }; N],
];
#[inline]
pub fn on() -> bool {
use core::sync::atomic::{AtomicU8, Ordering};
static ON: AtomicU8 = AtomicU8::new(0);
match ON.load(Ordering::Relaxed) {
1 => true,
2 => false,
_ => {
let on = rusty_h264_common::knob("RFF_GATE_CENSUS").is_some_and(|v| v != "0");
ON.store(if on { 1 } else { 2 }, Ordering::Relaxed);
on
}
}
}
pub fn commit_mb(t8: bool) {
if !on() {
return;
}
let b = &BY_T8[t8 as usize];
PENDING.with(|p| {
for (i, fired) in p.borrow_mut().drain(..) {
b[i as usize].1.fetch_add(1, Relaxed);
if fired {
b[i as usize].0.fetch_add(1, Relaxed);
}
}
});
}
pub fn snapshot_by_t8() -> [[(u64, u64); N]; 2] {
core::array::from_fn(|t| {
core::array::from_fn(|i| (BY_T8[t][i].0.load(Relaxed), BY_T8[t][i].1.load(Relaxed)))
})
}
#[inline]
pub fn bump(i: usize, fired: bool) {
if !on() {
return;
}
SEEN[i].fetch_add(1, Relaxed);
if fired {
FIRED[i].fetch_add(1, Relaxed);
}
PENDING.with(|p| p.borrow_mut().push((i as u8, fired)));
}
pub fn snapshot() -> [(u64, u64); N] {
core::array::from_fn(|i| (FIRED[i].load(Relaxed), SEEN[i].load(Relaxed)))
}
pub fn reset() {
PENDING.with(|p| p.borrow_mut().clear());
for t in 0..2 {
for i in 0..N {
BY_T8[t][i].0.store(0, Relaxed);
BY_T8[t][i].1.store(0, Relaxed);
}
}
for i in 0..N {
FIRED[i].store(0, Relaxed);
SEEN[i].store(0, Relaxed);
}
for w in WORK.iter() {
w.store(0, Relaxed);
}
}
pub const WN: usize = 4;
pub static WORK_NAMES: [&str; WN] = [
"best_part", "mb_plan", "mb_coded", "ref_search", ];
static WORK: [AtomicU64; WN] = [
AtomicU64::new(0),
AtomicU64::new(0),
AtomicU64::new(0),
AtomicU64::new(0),
];
#[inline]
pub fn work(i: usize) {
if !on() {
return;
}
WORK[i].fetch_add(1, Relaxed);
}
pub fn work_snapshot() -> [u64; WN] {
core::array::from_fn(|i| WORK[i].load(Relaxed))
}
pub const W_BEST_PART: usize = 0;
pub const W_MB_PLAN: usize = 1;
pub const W_MB_CODED: usize = 2;
pub const W_REF_SEARCH: usize = 3;
pub const AQ_GRAIN: usize = 0;
pub const MBTREE_GRAIN: usize = 1;
pub const MBTREE_BACKOFF: usize = 2;
pub const SUB8_GRAIN: usize = 3;
pub const SUB8_SPLIT: usize = 4;
pub const SUB8_RD_REVERT: usize = 5;
pub const INTRA_RD_FLIP: usize = 6;
pub const SHAPE_RD_FLIP: usize = 7;
pub const MBTREE_SPREAD_LATCH: usize = 8;
}
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) struct GateDecisions {
pub me_wide: bool,
pub sadfp: bool,
pub mv_smooth: bool,
pub do_splits: bool,
pub lme_scale: f64,
pub satd_thresh: i64,
}
impl Default for GateDecisions {
fn default() -> Self {
GateDecisions {
me_wide: false,
sadfp: false,
mv_smooth: false,
do_splits: true,
lme_scale: 1.0,
satd_thresh: i64::MAX,
}
}
}
#[cfg(not(feature = "std"))]
#[allow(dead_code)]
fn sink() -> &'static Option<()> {
static NONE: Option<()> = None;
&NONE
}
#[cfg(feature = "std")]
fn sink() -> &'static Option<std::sync::Mutex<std::fs::File>> {
#[cfg(feature = "std")]
use std::io::Write;
static S: rusty_h264_common::once::OnceLock<Option<std::sync::Mutex<std::fs::File>>> =
rusty_h264_common::once::OnceLock::new();
S.get_or_init(|| {
rusty_h264_common::knob("RFF_SIGNALS_CSV").and_then(|p| {
let mut f = std::fs::File::create(p).ok()?;
let _ = writeln!(
f,
"seq,slice,qp,mb_w,mb_h,mgain,dcfrac,headroom,gmc,median_var,lv_spread,\
flat_run,hist_top16,grain_floor,me_wide,sadfp,mv_smooth,do_splits,\
lme_scale,satd_thresh"
);
Some(std::sync::Mutex::new(f))
})
})
}
#[cfg_attr(not(feature = "std"), allow(unused_variables))]
pub(crate) fn harvest(sig: &FrameSignals, slice: char, qp: u8, d: &GateDecisions) {
#[cfg(feature = "std")]
use std::io::Write;
#[cfg(feature = "std")]
if let Some(m) = sink() {
static SEQ: rusty_h264_common::atomic::AtomicU64 =
rusty_h264_common::atomic::AtomicU64::new(0);
let seq = SEQ.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
let (mg, dc) = sig.mgain_dc();
if let Ok(mut f) = m.lock() {
let _ = writeln!(
f,
"{seq},{slice},{qp},{},{},{mg:.4},{dc:.4},{:.3},{:.3},{},{:.4},{:.3},{:.4},{:.3},{},{},{},{},{:.3},{}",
sig.mb_w,
sig.mb_h,
sig.headroom(),
sig.gmc_residual(),
sig.median_var(),
sig.lv_spread(),
sig.flat_run(),
sig.hist_top16(),
sig.grain_floor(),
d.me_wide as u8,
d.sadfp as u8,
d.mv_smooth as u8,
d.do_splits as u8,
d.lme_scale,
d.satd_thresh,
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(unused_imports)]
use alloc::{
boxed::Box,
format,
string::{String, ToString},
vec,
vec::Vec,
};
#[allow(unused_imports)]
use rusty_h264_common::once::OnceLock;
fn synth_pair(cw: usize, ch: usize) -> (Vec<u8>, Vec<u8>) {
let mut sy = vec![64u8; cw * ch];
let mut ry = vec![64u8; cw * ch];
for j in 16..ch {
for i in 0..cw {
let t = ((i * 7 + j * 13) % 97) as u8;
sy[j * cw + i] = 60 + t;
ry[j * cw + i] = 60 + t;
}
}
for j in 24..40.min(ch) {
for i in 0..16 {
let px = (220 - (i as i32) * 3 - (j as i32 % 16)) as u8;
if 20 + i < cw {
sy[j * cw + 20 + i] = px;
}
if 16 + i < cw {
ry[j * cw + 16 + i] = px;
}
}
}
(sy, ry)
}
fn h64(h: &mut u64, bits: u64) {
*h ^= bits;
*h = h.wrapping_mul(0x100_0000_01b3);
}
#[test]
fn signal_probes_golden() {
crate::fastmath::TEST_POLYTIER.with(|c| c.set(Some(false)));
assert_eq!(1f64.log2().to_bits(), 0f64.to_bits());
let mut golden = [(160usize, 112usize, 0u64), (224, 160, 0), (512, 480, 0)];
let rows = [
14809846845904276818u64,
2783330344417898965,
5253786124937756537,
];
for (g, r) in golden.iter_mut().zip(rows) {
g.2 = r;
}
for (cw, ch, want) in golden {
let (mb_w, mb_h) = (cw / 16, ch / 16);
let (sy, ry) = synth_pair(cw, ch);
let sig = FrameSignals::new(&sy, cw, mb_w, mb_h, Some(&ry));
assert!(
sig.mb_vars().contains(&0),
"{cw}x{ch}: no zero-variance MB"
);
let lvs = sig.log_vars();
for (l, &v) in lvs.0.iter().zip(sig.mb_vars()) {
if v == 0 {
assert_eq!(l.to_bits(), 0f64.to_bits(), "{cw}x{ch}: flat MB lv != +0.0");
}
}
let mut h = 0xcbf2_9ce4_8422_2325u64;
for l in &lvs.0 {
h64(&mut h, l.to_bits());
}
h64(&mut h, lvs.1.to_bits());
h64(&mut h, lvs.2.to_bits());
h64(&mut h, sig.headroom().to_bits());
let (mg, dc) = sig.mgain_dc();
h64(&mut h, mg.to_bits());
h64(&mut h, dc.to_bits());
h64(&mut h, sig.grain_floor().to_bits());
h64(&mut h, sig.median_var() as u64);
h64(&mut h, sig.gmc_residual().to_bits());
let (fr, ht) = (sig.flat_run(), sig.hist_top16());
h64(&mut h, fr.to_bits());
h64(&mut h, ht.to_bits());
eprintln!("[signal_probes_golden] {cw}x{ch}: {h}");
assert_eq!(h, want, "{cw}x{ch}: signal vector golden");
crate::fastmath::TEST_POLYTIER.with(|c| c.set(Some(true)));
let sigp = FrameSignals::new(&sy, cw, mb_w, mb_h, Some(&ry));
let lvp = sigp.log_vars();
for (i, (a, b)) in lvs.0.iter().zip(&lvp.0).enumerate() {
if a == &0.0 {
assert_eq!(b.to_bits(), 0f64.to_bits(), "{cw}x{ch} mb{i} flat");
} else {
assert!(
(a - b).abs() <= 1e-11 * a.abs().max(1.0),
"{cw}x{ch} mb{i}: {a} vs {b}"
);
}
}
crate::fastmath::TEST_POLYTIER.with(|c| c.set(Some(false)));
}
crate::fastmath::TEST_POLYTIER.with(|c| c.set(None));
}
}