use rusty_h264_common::inter::mc_luma;
use std::time::Instant;
const W: usize = 352;
const H: usize = 288;
fn make_ref() -> Vec<u8> {
let mut v = vec![0u8; W * H];
let mut s: u32 = 0x1234_5678;
for p in v.iter_mut() {
s = s.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
*p = (s >> 24) as u8;
}
v
}
fn arm(reference: &[u8], bw: usize, bh: usize, fx: i32, fy: i32, iters: usize) -> f64 {
let mut out = [0u8; 256];
let mut acc = 0u64;
let t = Instant::now();
for i in 0..iters {
let x0 = 8 + (i * 7) % (W - bw - 24);
let y0 = 8 + (i * 13) % (H - bh - 24);
let mvx = ((x0 as i32) << 2) - ((x0 as i32) << 2) + fx;
let mvy = ((y0 as i32) << 2) - ((y0 as i32) << 2) + fy;
mc_luma(reference, W, H, x0, y0, bw, bh, mvx, mvy, &mut out);
acc = acc.wrapping_add(out[0] as u64);
}
let e = t.elapsed().as_secs_f64();
std::hint::black_box(acc);
e * 1e9 / iters as f64
}
fn main() {
let reference = make_ref();
let iters: usize = std::env::var("MC_ITERS").ok().and_then(|s| s.parse().ok()).unwrap_or(200_000);
let rounds: usize = std::env::var("MC_ROUNDS").ok().and_then(|s| s.parse().ok()).unwrap_or(9);
let arms: Vec<(String, usize, usize, i32, i32)> = {
let sizes = [(16usize, 16usize), (8, 8), (4, 4)];
let phases = [("fullpel", 0, 0), ("halfH", 2, 0), ("halfHV", 2, 2), ("qpel", 1, 1)];
let mut v = Vec::new();
for &(bw, bh) in &sizes {
for &(pn, fx, fy) in &phases {
v.push((format!("{bw}x{bh} {pn}"), bw, bh, fx, fy));
}
}
v
};
let mut samples: Vec<Vec<f64>> = vec![Vec::new(); arms.len()];
for r in 0..rounds {
let order: Vec<usize> = if r % 2 == 0 {
(0..arms.len()).collect()
} else {
(0..arms.len()).rev().collect()
};
for &i in &order {
let (_, bw, bh, fx, fy) = &arms[i];
samples[i].push(arm(&reference, *bw, *bh, *fx, *fy, iters));
}
}
println!("mc_luma ceiling probe — {iters} calls/arm, {rounds} interleaved rounds\n");
println!("{:<18} {:>10} {:>12} {:>10}", "arm", "ns/call", "ns/pixel", "px");
println!("{}", "-".repeat(54));
let mut per_call: Vec<(String, f64, f64)> = Vec::new();
for (i, (label, bw, bh, ..)) in arms.iter().enumerate() {
let mut s = samples[i].clone();
s.sort_by(|a, b| a.partial_cmp(b).unwrap());
let med = s[s.len() / 2];
let px = (bw * bh) as f64;
println!("{:<18} {:>10.1} {:>12.3} {:>10.0}", label, med, med / px, px);
per_call.push((label.clone(), med, med / px));
if label.ends_with("qpel") {
println!();
}
}
let get = |name: &str| per_call.iter().find(|(l, ..)| l == name).map(|(_, c, _)| *c).unwrap_or(0.0);
println!("\n--- verdict ---");
for phase in ["halfH", "halfHV", "qpel"] {
let c16 = get(&format!("16x16 {phase}"));
let c4 = get(&format!("4x4 {phase}"));
if c16 > 0.0 && c4 > 0.0 {
println!(
"{phase:<8} 4x4 costs {:.0}% of 16x16 per CALL despite 6.25% of the pixels \
-> fixed overhead {:.0}% of a 4x4 call",
100.0 * c4 / c16,
100.0 * (c4 - c16 / 16.0).max(0.0) / c4
);
}
}
}