#[rustfmt::skip]
const ALPHA: [i32; 52] = [
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
4,4,5,6,7,8,9,10,12,13,15,17,20,22,25,28,
32,36,40,45,50,56,63,71,80,90,101,113,127,144,162,182,203,226,255,255,
];
#[rustfmt::skip]
const BETA: [i32; 52] = [
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2,2,2,3,3,3,3,4,4,4,6,6,7,7,8,8,
9,9,10,10,11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18,
];
#[rustfmt::skip]
const TC0: [[i32; 3]; 52] = [
[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],
[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],
[0,0,0],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,1,1],[0,1,1],[1,1,1],
[1,1,1],[1,1,1],[1,1,1],[1,1,2],[1,1,2],[1,1,2],[1,1,2],[1,2,3],
[1,2,3],[2,2,3],[2,2,4],[2,3,4],[2,3,4],[3,3,5],[3,4,6],[3,4,6],
[4,5,7],[4,5,8],[4,6,9],[5,7,10],[6,8,11],[6,8,13],[7,10,14],[8,11,16],
[9,12,18],[10,13,20],[11,15,23],[13,17,25],
];
#[inline]
fn clip1(v: i32) -> u8 {
v.clamp(0, 255) as u8
}
#[inline]
fn clip3(lo: i32, hi: i32, v: i32) -> i32 {
v.clamp(lo, hi)
}
struct Line {
base: usize,
step: isize,
}
#[allow(clippy::too_many_arguments)]
fn filter_luma_line(plane: &mut [u8], line: &Line, bs: i32, alpha: i32, beta: i32, tc0: i32) {
let at = |i: isize| -> i32 {
plane[(line.base as isize + i * line.step) as usize] as i32
};
let (p0, p1, p2, p3) = (at(-1), at(-2), at(-3), at(-4));
let (q0, q1, q2, q3) = (at(0), at(1), at(2), at(3));
if (p0 - q0).abs() >= alpha || (p1 - p0).abs() >= beta || (q1 - q0).abs() >= beta {
return;
}
let set = |plane: &mut [u8], i: isize, v: u8| {
plane[(line.base as isize + i * line.step) as usize] = v;
};
let ap = (p2 - p0).abs();
let aq = (q2 - q0).abs();
if bs < 4 {
let tc = tc0 + (ap < beta) as i32 + (aq < beta) as i32;
let delta = clip3(-tc, tc, (((q0 - p0) << 2) + (p1 - q1) + 4) >> 3);
set(plane, -1, clip1(p0 + delta));
set(plane, 0, clip1(q0 - delta));
if ap < beta {
let d = clip3(-tc0, tc0, (p2 + ((p0 + q0 + 1) >> 1) - (p1 << 1)) >> 1);
set(plane, -2, clip1(p1 + d));
}
if aq < beta {
let d = clip3(-tc0, tc0, (q2 + ((p0 + q0 + 1) >> 1) - (q1 << 1)) >> 1);
set(plane, 1, clip1(q1 + d));
}
} else {
let strong = (p0 - q0).abs() < (alpha >> 2) + 2;
if strong && ap < beta {
set(plane, -1, clip1((p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3));
set(plane, -2, clip1((p2 + p1 + p0 + q0 + 2) >> 2));
set(plane, -3, clip1((2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3));
} else {
set(plane, -1, clip1((2 * p1 + p0 + q1 + 2) >> 2));
}
if strong && aq < beta {
set(plane, 0, clip1((q2 + 2 * q1 + 2 * q0 + 2 * p0 + p1 + 4) >> 3));
set(plane, 1, clip1((q2 + q1 + q0 + p0 + 2) >> 2));
set(plane, 2, clip1((2 * q3 + 3 * q2 + q1 + q0 + p0 + 4) >> 3));
} else {
set(plane, 0, clip1((2 * q1 + q0 + p1 + 2) >> 2));
}
}
}
fn filter_chroma_line(plane: &mut [u8], line: &Line, bs: i32, alpha: i32, beta: i32, tc0: i32) {
let at = |i: isize| -> i32 {
plane[(line.base as isize + i * line.step) as usize] as i32
};
let (p0, p1) = (at(-1), at(-2));
let (q0, q1) = (at(0), at(1));
if (p0 - q0).abs() >= alpha || (p1 - p0).abs() >= beta || (q1 - q0).abs() >= beta {
return;
}
let set = |plane: &mut [u8], i: isize, v: u8| {
plane[(line.base as isize + i * line.step) as usize] = v;
};
if bs < 4 {
let tc = tc0 + 1;
let delta = clip3(-tc, tc, (((q0 - p0) << 2) + (p1 - q1) + 4) >> 3);
set(plane, -1, clip1(p0 + delta));
set(plane, 0, clip1(q0 - delta));
} else {
set(plane, -1, clip1((2 * p1 + p0 + q1 + 2) >> 2));
set(plane, 0, clip1((2 * q1 + q0 + p1 + 2) >> 2));
}
}
pub struct BlockInfo<'a> {
pub inter: &'a [bool],
pub nnz: &'a [u8],
pub mv: &'a [(i32, i32)],
pub ref_id: &'a [i32],
pub mv1: &'a [(i32, i32)],
pub ref_id1: &'a [i32],
pub w4: usize,
pub t8x8: &'a [bool],
pub bs: &'a [MbBs],
pub kind: &'a [u8],
}
pub const MB_KIND_UNSET: u8 = 255;
pub const MB_KIND_INTRA: u8 = 0;
pub const MB_KIND_SKIP: u8 = 1;
pub const MB_KIND_INTER_UNIFORM: u8 = 2;
pub const MB_KIND_INTER: u8 = 3;
impl MbKind {
#[inline]
pub fn from_u8(v: u8) -> Option<Self> {
match v {
MB_KIND_INTRA => Some(MbKind::Intra),
MB_KIND_SKIP => Some(MbKind::Skip),
MB_KIND_INTER_UNIFORM => Some(MbKind::InterUniform),
MB_KIND_INTER => Some(MbKind::Inter),
_ => None,
}
}
}
#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)]
pub struct MbBs {
pub v: [[u8; 4]; 4],
pub h: [[u8; 4]; 4],
}
impl MbBs {
pub const UNSET: MbBs = MbBs { v: [[0xFF; 4]; 4], h: [[0xFF; 4]; 4] };
}
const NO_REF: i32 = i32::MIN;
#[derive(Clone, Copy, Default)]
struct Blk {
inter: bool,
nz: bool,
ref_id: i32,
mvx: i32,
mvy: i32,
ref1: i32,
mv1x: i32,
mv1y: i32,
}
impl Blk {
#[inline]
fn load(info: &BlockInfo, i: usize) -> Self {
#[cfg(feature = "profile")]
census::BLK_LOADS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let (mvx, mvy) = info.mv[i];
let (ref1, (mv1x, mv1y)) = if info.ref_id1.is_empty() {
(NO_REF, (0, 0))
} else {
(info.ref_id1[i], info.mv1[i])
};
Blk { inter: info.inter[i], nz: info.nnz[i] != 0, ref_id: info.ref_id[i], mvx, mvy, ref1, mv1x, mv1y }
}
#[inline]
fn same_motion(&self, o: &Blk) -> bool {
self.ref_id == o.ref_id
&& self.mvx == o.mvx
&& self.mvy == o.mvy
&& self.ref1 == o.ref1
&& self.mv1x == o.mv1x
&& self.mv1y == o.mv1y
}
}
#[inline]
fn bs1_tile(p: &Blk, q: &Blk) -> bool {
if (p.ref1 == NO_REF) & (q.ref1 == NO_REF) {
let far = ((p.mvx - q.mvx).abs() >= 4) | ((p.mvy - q.mvy).abs() >= 4);
return (p.ref_id != q.ref_id) | ((p.ref_id != NO_REF) & far);
}
let used = |b: &Blk| {
let mut v = [(0i32, (0i32, 0i32)); 2];
let mut n = 0usize;
if b.ref_id != NO_REF {
v[n] = (b.ref_id, (b.mvx, b.mvy));
n += 1;
}
if b.ref1 != NO_REF {
v[n] = (b.ref1, (b.mv1x, b.mv1y));
n += 1;
}
(v, n)
};
let (pv, pn) = used(p);
let (qv, qn) = used(q);
if pn != qn {
return true;
}
let far = |a: (i32, i32), b: (i32, i32)| (a.0 - b.0).abs() >= 4 || (a.1 - b.1).abs() >= 4;
match pn {
0 => false,
1 => pv[0].0 != qv[0].0 || far(pv[0].1, qv[0].1),
_ => {
let direct = !far(pv[0].1, qv[0].1) && !far(pv[1].1, qv[1].1);
let swap = !far(pv[0].1, qv[1].1) && !far(pv[1].1, qv[0].1);
if pv[0].0 == pv[1].0 {
qv[0].0 != pv[0].0 || qv[1].0 != pv[0].0 || !(direct || swap)
} else if pv[0].0 == qv[0].0 && pv[1].0 == qv[1].0 {
!direct
} else if pv[0].0 == qv[1].0 && pv[1].0 == qv[0].0 {
!swap
} else {
true
}
}
}
}
#[inline]
fn bs_tile(p: &Blk, q: &Blk, mb_edge: bool) -> i32 {
let intra = !(p.inter & q.inter);
let nz = p.nz | q.nz;
let moved = bs1_tile(p, q);
let intra_bs = if mb_edge { 4 } else { 3 };
let non_intra = if nz { 2 } else { moved as i32 };
if intra {
intra_bs
} else {
non_intra
}
}
#[inline]
fn bs_inter(p: &Blk, q: &Blk) -> i32 {
let nz = p.nz | q.nz;
let moved = bs1_tile(p, q);
if nz {
2
} else {
moved as i32
}
}
#[cfg(feature = "profile")]
pub mod census {
use std::sync::atomic::{AtomicU64, Ordering};
pub static INTRA: AtomicU64 = AtomicU64::new(0);
pub static SKIP: AtomicU64 = AtomicU64::new(0);
pub static UNIFORM: AtomicU64 = AtomicU64::new(0);
pub static INTER: AtomicU64 = AtomicU64::new(0);
pub static PRED_VISITS: AtomicU64 = AtomicU64::new(0);
pub static BLK_LOADS: AtomicU64 = AtomicU64::new(0);
pub static PACKED_MB: AtomicU64 = AtomicU64::new(0);
pub static MASKS_CALLS: AtomicU64 = AtomicU64::new(0);
pub static MASKS_KERNEL: AtomicU64 = AtomicU64::new(0);
pub static MASKS_L1: AtomicU64 = AtomicU64::new(0);
pub fn reset() {
for c in [
&INTRA, &SKIP, &UNIFORM, &INTER, &PRED_VISITS, &BLK_LOADS, &PACKED_MB,
&MASKS_CALLS, &MASKS_KERNEL, &MASKS_L1,
] {
c.store(0, Ordering::Relaxed);
}
}
pub fn dump() {
let (i, s, u, n) = (
INTRA.load(Ordering::Relaxed),
SKIP.load(Ordering::Relaxed),
UNIFORM.load(Ordering::Relaxed),
INTER.load(Ordering::Relaxed),
);
let tot = (i + s + u + n).max(1);
eprintln!("--- MB-kind census (deblock bS derivation) — {tot} macroblocks ---");
for (name, n_mb, blocks) in
[("Intra", i, 0u64), ("Skip", s, 9), ("InterUniform", u, 16), ("Inter", n, 24)]
{
eprintln!(
" {name:<13} {n_mb:>10} MB {:>5.1}% kind-aware gather: {blocks:>2} blocks vs 24 blind",
100.0 * n_mb as f64 / tot as f64
);
}
eprintln!(
" Blk::load GATHER loads: {} ({:.2} per MB)",
BLK_LOADS.load(Ordering::Relaxed),
BLK_LOADS.load(Ordering::Relaxed) as f64 / tot as f64
);
eprintln!(
" predicate-walk block VISITS: {} ({:.2} per MB)",
PRED_VISITS.load(Ordering::Relaxed),
PRED_VISITS.load(Ordering::Relaxed) as f64 / tot as f64
);
eprintln!(
" PACKED-path macroblocks: {}",
PACKED_MB.load(Ordering::Relaxed)
);
let mc = MASKS_CALLS.load(Ordering::Relaxed).max(1);
eprintln!(
" mask derivations: {} (AVX2 kernel {} = {:.1}%, scalar two-list {} = {:.1}%)",
MASKS_CALLS.load(Ordering::Relaxed),
MASKS_KERNEL.load(Ordering::Relaxed),
100.0 * MASKS_KERNEL.load(Ordering::Relaxed) as f64 / mc as f64,
MASKS_L1.load(Ordering::Relaxed),
100.0 * MASKS_L1.load(Ordering::Relaxed) as f64 / mc as f64,
);
let blind = 24.0 * tot as f64;
let aware = (0 * i + 9 * s + 16 * u + 24 * n) as f64;
eprintln!(
" => block loads {:.0} -> {:.0} ({:.1}% of the gather removed)",
blind,
aware,
100.0 * (1.0 - aware / blind)
);
}
}
#[inline]
fn derive_mb_bs(
tile: &Tile,
mb_x: usize,
mb_y: usize,
flat_inter: bool,
uniform_motion: bool,
mb_t8: bool,
bs_v: &mut [[i32; 4]; 4],
bs_h: &mut [[i32; 4]; 4],
) {
let cur_intra = !tile[1][1].inter;
#[cfg(feature = "profile")]
{
use std::sync::atomic::Ordering::Relaxed;
let c = if cur_intra {
&census::INTRA
} else if flat_inter {
&census::SKIP
} else if uniform_motion {
&census::UNIFORM
} else {
&census::INTER
};
c.fetch_add(1, Relaxed);
}
if mb_x > 0 {
bs_v[0] = if cur_intra || !tile[1][0].inter {
[4; 4]
} else {
std::array::from_fn(|seg| bs_inter(&tile[seg + 1][0], &tile[seg + 1][1]))
};
}
if mb_y > 0 {
bs_h[0] = if cur_intra || !tile[0][1].inter {
[4; 4]
} else {
std::array::from_fn(|seg| bs_inter(&tile[0][seg + 1], &tile[1][seg + 1]))
};
}
if flat_inter {
return; }
for be in 1..4usize {
if mb_t8 && (be == 1 || be == 3) {
continue;
}
if cur_intra {
bs_v[be] = [3; 4];
bs_h[be] = [3; 4];
} else if uniform_motion {
bs_v[be] = std::array::from_fn(|seg| {
2 * (tile[seg + 1][be].nz | tile[seg + 1][be + 1].nz) as i32
});
bs_h[be] = std::array::from_fn(|seg| {
2 * (tile[be][seg + 1].nz | tile[be + 1][seg + 1].nz) as i32
});
} else {
bs_v[be] = std::array::from_fn(|seg| bs_inter(&tile[seg + 1][be], &tile[seg + 1][be + 1]));
bs_h[be] = std::array::from_fn(|seg| bs_inter(&tile[be][seg + 1], &tile[be + 1][seg + 1]));
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum MbKind {
Intra,
Skip,
InterUniform,
Inter,
}
pub fn derive_mb_kind_into(
info: &BlockInfo,
mb_x: usize,
mb_y: usize,
kind: MbKind,
bs_v: &mut [[i32; 4]; 4],
bs_h: &mut [[i32; 4]; 4],
) {
let m = derive_mb_kind(info, mb_x, mb_y, kind);
for e in 0..4 {
for sg in 0..4 {
bs_v[e][sg] = m.v[e][sg] as i32;
bs_h[e][sg] = m.h[e][sg] as i32;
}
}
}
pub fn derive_mb_kind(info: &BlockInfo, mb_x: usize, mb_y: usize, kind: MbKind) -> MbBs {
let (bx0, by0) = (mb_x * 4, mb_y * 4);
let w4 = info.w4;
match kind {
MbKind::Intra => {
let mut m = MbBs::default();
if mb_x > 0 {
m.v[0] = [4; 4];
}
if mb_y > 0 {
m.h[0] = [4; 4];
}
for e in 1..4 {
m.v[e] = [3; 4];
m.h[e] = [3; 4];
}
m
}
MbKind::Skip => {
let mut m = MbBs::default();
let me = Blk::load(info, by0 * w4 + bx0); if mb_x > 0 {
m.v[0] = std::array::from_fn(|seg| {
let p = Blk::load(info, (by0 + seg) * w4 + bx0 - 1);
if p.inter { bs_inter(&p, &me) as u8 } else { 4 }
});
}
if mb_y > 0 {
m.h[0] = std::array::from_fn(|seg| {
let p = Blk::load(info, (by0 - 1) * w4 + bx0 + seg);
if p.inter { bs_inter(&p, &me) as u8 } else { 4 }
});
}
m
}
MbKind::InterUniform => {
let mut m = MbBs::default();
for e in 1..4usize {
m.v[e] = std::array::from_fn(|seg| {
let i = (by0 + seg) * w4 + bx0 + e;
2 * ((info.nnz[i] != 0) | (info.nnz[i - 1] != 0)) as u8
});
m.h[e] = std::array::from_fn(|seg| {
let i = (by0 + e) * w4 + bx0 + seg;
2 * ((info.nnz[i] != 0) | (info.nnz[i - w4] != 0)) as u8
});
}
if mb_x > 0 {
m.v[0] = std::array::from_fn(|seg| {
let qi = (by0 + seg) * w4 + bx0;
let p = Blk::load(info, qi - 1);
if p.inter { bs_inter(&p, &Blk::load(info, qi)) as u8 } else { 4 }
});
}
if mb_y > 0 {
m.h[0] = std::array::from_fn(|seg| {
let qi = by0 * w4 + bx0 + seg;
let p = Blk::load(info, qi - w4);
if p.inter { bs_inter(&p, &Blk::load(info, qi)) as u8 } else { 4 }
});
}
m
}
MbKind::Inter => derive_mb(info, mb_x, mb_y, false),
}
}
#[derive(Clone, Copy)]
#[repr(C, align(32))]
pub struct MbPack {
pub nnz_mask: u16,
pub inter: bool,
_pad: u8,
pub mvx: [i16; 16],
pub mvy: [i16; 16],
pub l1_used: u16,
pub mvx1: [i16; 16],
pub mvy1: [i16; 16],
pub ref1: [i32; 16],
pub ref_id: [i32; 16],
}
impl Default for MbPack {
fn default() -> Self {
Self {
nnz_mask: 0,
inter: false,
_pad: 0,
mvx: [0; 16],
mvy: [0; 16],
l1_used: 0,
mvx1: [0; 16],
mvy1: [0; 16],
ref1: [NO_REF; 16],
ref_id: [NO_REF; 16],
}
}
}
pub fn pack_frame(info: &BlockInfo, mb_w: usize, mb_h: usize) -> Option<Vec<MbPack>> {
let has1 = !info.ref_id1.is_empty();
let mut out = vec![MbPack::default(); mb_w * mb_h];
for mb_y in 0..mb_h {
for mb_x in 0..mb_w {
let rec = &mut out[mb_y * mb_w + mb_x];
let (bx0, by0) = (mb_x * 4, mb_y * 4);
rec.inter = info.inter[by0 * info.w4 + bx0];
for r in 0..4 {
let row = (by0 + r) * info.w4 + bx0;
for c in 0..4 {
let i = row + c;
let k = r * 4 + c;
if info.nnz[i] != 0 {
rec.nnz_mask |= 1 << k;
}
rec.mvx[k] = info.mv[i].0 as i16;
rec.mvy[k] = info.mv[i].1 as i16;
rec.ref_id[k] = info.ref_id[i];
if has1 {
rec.ref1[k] = info.ref_id1[i];
rec.mvx1[k] = info.mv1[i].0 as i16;
rec.mvy1[k] = info.mv1[i].1 as i16;
if info.ref_id1[i] != NO_REF {
rec.l1_used |= 1 << k;
}
}
}
}
}
}
Some(out)
}
#[inline]
pub fn mb_uniform_scalar(p: &MbPack) -> bool {
p.inter
&& (1..16).all(|k| {
p.ref_id[k] == p.ref_id[0]
&& p.mvx[k] == p.mvx[0]
&& p.mvy[k] == p.mvy[0]
&& p.ref1[k] == p.ref1[0]
&& p.mvx1[k] == p.mvx1[0]
&& p.mvy1[k] == p.mvy1[0]
})
}
#[inline]
pub fn mb_uniform(p: &MbPack) -> bool {
#[cfg(all(target_arch = "x86_64", feature = "asm"))]
if p.inter {
if let Some(u) =
rusty_h264_accel::mb_uniform(&p.mvx, &p.mvy, &p.ref_id, &p.mvx1, &p.mvy1, &p.ref1)
{
return u;
}
}
mb_uniform_scalar(p)
}
#[inline]
pub fn bs_motion_masks_scalar(p: &MbPack) -> (u16, u16) {
let differs = |a: usize, b: usize| -> bool { pk_differs(p, a, p, b) };
let (mut left, mut up) = (0u16, 0u16);
for k in 0..16usize {
if k % 4 != 0 && differs(k, k - 1) {
left |= 1 << k;
}
if k >= 4 && differs(k, k - 4) {
up |= 1 << k;
}
}
(left & 0xEEEE, up & 0xFFF0)
}
#[inline]
pub fn bs_motion_masks(p: &MbPack) -> (u16, u16) {
#[cfg(feature = "profile")]
{
use std::sync::atomic::Ordering::Relaxed;
census::MASKS_CALLS.fetch_add(1, Relaxed);
if p.l1_used == 0 {
census::MASKS_KERNEL.fetch_add(1, Relaxed);
} else {
census::MASKS_L1.fetch_add(1, Relaxed);
}
}
#[cfg(all(target_arch = "x86_64", feature = "asm"))]
if p.l1_used == 0 {
if let Some(m) = rusty_h264_accel::bs_motion_masks(&p.mvx, &p.mvy, &p.ref_id, NO_REF) {
return m;
}
}
bs_motion_masks_scalar(p)
}
#[inline]
fn pk_differs(p: &MbPack, pk: usize, q: &MbPack, qk: usize) -> bool {
let p_has1 = p.ref1[pk] != NO_REF;
let q_has1 = q.ref1[qk] != NO_REF;
if !p_has1 && !q_has1 {
let far = ((p.mvx[pk] - q.mvx[qk]).abs() >= 4) | ((p.mvy[pk] - q.mvy[qk]).abs() >= 4);
return (p.ref_id[pk] != q.ref_id[qk]) | ((p.ref_id[pk] != NO_REF) & far);
}
let used = |b: &MbPack, k: usize| {
let mut v = [(0i32, (0i32, 0i32)); 2];
let mut n = 0usize;
if b.ref_id[k] != NO_REF {
v[n] = (b.ref_id[k], (b.mvx[k] as i32, b.mvy[k] as i32));
n += 1;
}
if b.ref1[k] != NO_REF {
v[n] = (b.ref1[k], (b.mvx1[k] as i32, b.mvy1[k] as i32));
n += 1;
}
(v, n)
};
let (pv, pn) = used(p, pk);
let (qv, qn) = used(q, qk);
if pn != qn {
return true;
}
let far = |a: (i32, i32), b: (i32, i32)| (a.0 - b.0).abs() >= 4 || (a.1 - b.1).abs() >= 4;
match pn {
0 => false,
1 => pv[0].0 != qv[0].0 || far(pv[0].1, qv[0].1),
_ => {
let direct = !far(pv[0].1, qv[0].1) && !far(pv[1].1, qv[1].1);
let swap = !far(pv[0].1, qv[1].1) && !far(pv[1].1, qv[0].1);
if pv[0].0 == pv[1].0 {
qv[0].0 != pv[0].0 || qv[1].0 != pv[0].0 || !(direct || swap)
} else if pv[0].0 == qv[0].0 && pv[1].0 == qv[1].0 {
!direct
} else if pv[0].0 == qv[1].0 && pv[1].0 == qv[0].0 {
!swap
} else {
true
}
}
}
}
#[inline]
fn pk_nz(p: &MbPack, k: usize) -> bool {
(p.nnz_mask >> k) & 1 != 0
}
#[inline]
fn pk_bs_inter(p: &MbPack, pk: usize, q: &MbPack, qk: usize) -> i32 {
if pk_nz(p, pk) | pk_nz(q, qk) {
return 2;
}
let (pr, qr) = (p.ref_id[pk], q.ref_id[qk]);
let _ = (pr, qr);
pk_differs(p, pk, q, qk) as i32
}
pub fn derive_mb_packed(
packs: &[MbPack],
mb_w: usize,
mb_x: usize,
mb_y: usize,
mb_t8: bool,
bs_v: &mut [[i32; 4]; 4],
bs_h: &mut [[i32; 4]; 4],
) -> bool {
let cur = &packs[mb_y * mb_w + mb_x];
let cur_intra = !cur.inter;
let uniform = mb_uniform(cur);
let flat_inter = uniform && cur.nnz_mask == 0;
if mb_x > 0 {
let l = &packs[mb_y * mb_w + mb_x - 1];
bs_v[0] = if cur_intra || !l.inter {
[4; 4]
} else {
std::array::from_fn(|seg| pk_bs_inter(l, seg * 4 + 3, cur, seg * 4))
};
}
if mb_y > 0 {
let t = &packs[(mb_y - 1) * mb_w + mb_x];
bs_h[0] = if cur_intra || !t.inter {
[4; 4]
} else {
std::array::from_fn(|seg| pk_bs_inter(t, 12 + seg, cur, seg))
};
}
if flat_inter {
return true; }
let masks = if cur_intra || uniform { (0, 0) } else { bs_motion_masks(cur) };
for be in 1..4usize {
if mb_t8 && (be == 1 || be == 3) {
continue;
}
if cur_intra {
bs_v[be] = [3; 4];
bs_h[be] = [3; 4];
} else if uniform {
bs_v[be] = std::array::from_fn(|seg| {
2 * (pk_nz(cur, seg * 4 + be) | pk_nz(cur, seg * 4 + be - 1)) as i32
});
bs_h[be] = std::array::from_fn(|seg| {
2 * (pk_nz(cur, be * 4 + seg) | pk_nz(cur, (be - 1) * 4 + seg)) as i32
});
} else {
let (left, up) = masks;
bs_v[be] = std::array::from_fn(|seg| {
let k = seg * 4 + be;
if pk_nz(cur, k) | pk_nz(cur, k - 1) {
2
} else {
((left >> k) & 1) as i32
}
});
bs_h[be] = std::array::from_fn(|seg| {
let k = be * 4 + seg;
if pk_nz(cur, k) | pk_nz(cur, k - 4) {
2
} else {
((up >> k) & 1) as i32
}
});
}
}
false
}
#[inline]
fn scan_uniform_flat(tile: &Tile) -> (bool, bool) {
let b0 = &tile[1][1];
if !b0.inter {
return (false, false); }
let mut any_nz = false;
for r in 1..5 {
for c in 1..5 {
let b = &tile[r][c];
#[cfg(feature = "profile")]
census::PRED_VISITS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
if !b.inter || !b.same_motion(b0) {
return (false, false);
}
any_nz |= b.nz;
}
}
(true, !any_nz)
}
#[inline]
fn scan_two_pass(tile: &Tile) -> (bool, bool) {
let b0 = &tile[1][1];
let flat = b0.inter
&& (1..5).all(|r| {
(1..5).all(|c| {
let b = &tile[r][c];
#[cfg(feature = "profile")]
census::PRED_VISITS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
b.inter && !b.nz && b.same_motion(b0)
})
});
let uniform = b0.inter
&& (1..5).all(|r| {
(1..5).all(|c| {
#[cfg(feature = "profile")]
census::PRED_VISITS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
tile[r][c].inter && tile[r][c].same_motion(b0)
})
});
(uniform, flat)
}
static BS_TWOPASS: std::sync::atomic::AtomicU8 = std::sync::atomic::AtomicU8::new(0);
fn bs_twopass() -> bool {
use std::sync::atomic::Ordering;
match BS_TWOPASS.load(Ordering::Relaxed) {
1 => true,
2 => false,
_ => {
let on = std::env::var_os("RS_H264_BS_TWOPASS").is_some_and(|v| v != "0");
BS_TWOPASS.store(if on { 1 } else { 2 }, Ordering::Relaxed);
on
}
}
}
static NO_MBKIND: std::sync::atomic::AtomicU8 = std::sync::atomic::AtomicU8::new(0);
fn verify_kind() -> bool {
use std::sync::atomic::Ordering;
static V: std::sync::atomic::AtomicU8 = std::sync::atomic::AtomicU8::new(0);
match V.load(Ordering::Relaxed) {
1 => true,
2 => false,
_ => {
let on = std::env::var_os("RS_H264_VERIFY_MBKIND").is_some_and(|v| v != "0");
V.store(if on { 1 } else { 2 }, Ordering::Relaxed);
on
}
}
}
#[allow(clippy::too_many_arguments)]
fn verify_kind_matches_blind(
info: &BlockInfo,
mb_x: usize,
mb_y: usize,
mb_t8: bool,
kind: MbKind,
flat_inter: bool,
bs_v: &[[i32; 4]; 4],
bs_h: &[[i32; 4]; 4],
) {
let tile = gather_tile(info, mb_x, mb_y);
let (u, f) = scan_uniform_flat(&tile);
let (mut vv, mut vh) = ([[0i32; 4]; 4], [[0i32; 4]; 4]);
derive_mb_bs(&tile, mb_x, mb_y, f, u, mb_t8, &mut vv, &mut vh);
assert_eq!(
flat_inter, f,
"MB ({mb_x},{mb_y}) kind {kind:?}: flat_inter {flat_inter} but blind derived {f}"
);
for be in 0..4usize {
if be == 0 {
if mb_x > 0 {
assert_eq!(bs_v[0], vv[0], "MB ({mb_x},{mb_y}) kind {kind:?}: left MB edge");
}
if mb_y > 0 {
assert_eq!(bs_h[0], vh[0], "MB ({mb_x},{mb_y}) kind {kind:?}: top MB edge");
}
continue;
}
if flat_inter || (mb_t8 && (be == 1 || be == 3)) {
continue; }
assert_eq!(bs_v[be], vv[be], "MB ({mb_x},{mb_y}) kind {kind:?}: v internal edge {be}");
assert_eq!(bs_h[be], vh[be], "MB ({mb_x},{mb_y}) kind {kind:?}: h internal edge {be}");
}
}
fn verify_packed() -> bool {
use std::sync::atomic::Ordering;
static V: std::sync::atomic::AtomicU8 = std::sync::atomic::AtomicU8::new(0);
match V.load(Ordering::Relaxed) {
1 => true,
2 => false,
_ => {
let on = std::env::var_os("RS_H264_VERIFY_PACKED").is_some_and(|v| v != "0");
V.store(if on { 1 } else { 2 }, Ordering::Relaxed);
on
}
}
}
fn bs_packed_on() -> bool {
use std::sync::atomic::Ordering;
static ON: std::sync::atomic::AtomicU8 = std::sync::atomic::AtomicU8::new(0);
match ON.load(Ordering::Relaxed) {
1 => true,
2 => false,
_ => {
let off = std::env::var_os("RS_H264_BS_PACKED").is_some_and(|v| v == "0");
ON.store(if off { 2 } else { 1 }, Ordering::Relaxed);
!off
}
}
}
fn kind_gate_off() -> bool {
use std::sync::atomic::Ordering;
match NO_MBKIND.load(Ordering::Relaxed) {
1 => true,
2 => false,
_ => {
let off = std::env::var_os("RS_H264_NO_MBKIND").is_some_and(|v| v != "0");
NO_MBKIND.store(if off { 1 } else { 2 }, Ordering::Relaxed);
off
}
}
}
#[inline]
fn scan_predicates(tile: &Tile, two_pass: bool) -> (bool, bool) {
if two_pass {
scan_two_pass(tile)
} else {
scan_uniform_flat(tile)
}
}
pub fn derive_mb(info: &BlockInfo, mb_x: usize, mb_y: usize, mb_t8: bool) -> MbBs {
let tile = gather_tile(info, mb_x, mb_y);
let (uniform_motion, flat_inter) = scan_predicates(&tile, bs_twopass());
let (mut bs_v, mut bs_h) = ([[0i32; 4]; 4], [[0i32; 4]; 4]);
derive_mb_bs(&tile, mb_x, mb_y, flat_inter, uniform_motion, mb_t8, &mut bs_v, &mut bs_h);
let pack = |a: [[i32; 4]; 4]| a.map(|e| e.map(|x| x as u8));
MbBs { v: pack(bs_v), h: pack(bs_h) }
}
type Tile = [[Blk; 5]; 5];
fn gather_tile(info: &BlockInfo, mb_x: usize, mb_y: usize) -> Tile {
let mut t: Tile = Default::default();
let (bx0, by0) = (mb_x * 4, mb_y * 4);
for r in 0..4 {
let row = (by0 + r) * info.w4 + bx0;
for c in 0..4 {
t[r + 1][c + 1] = Blk::load(info, row + c);
}
}
if mb_x > 0 {
for r in 0..4 {
t[r + 1][0] = Blk::load(info, (by0 + r) * info.w4 + bx0 - 1);
}
}
if mb_y > 0 {
let row = (by0 - 1) * info.w4 + bx0;
for c in 0..4 {
t[0][c + 1] = Blk::load(info, row + c);
}
}
debug_assert!(
(1..5).all(|r| (1..5).all(|c| t[r][c].inter == t[1][1].inter)),
"macroblock ({mb_x},{mb_y}) mixes intra and inter 4x4 blocks"
);
t
}
static BS_MODE: std::sync::atomic::AtomicU8 = std::sync::atomic::AtomicU8::new(0);
fn branchless_bs() -> bool {
use std::sync::atomic::Ordering;
match BS_MODE.load(Ordering::Relaxed) {
1 => true,
2 => false,
_ => {
let branchy = std::env::var_os("RS_H264_DEBLOCK_BRANCHY").is_some_and(|v| v != "0");
BS_MODE.store(if branchy { 2 } else { 1 }, Ordering::Relaxed);
!branchy
}
}
}
fn deblock_tile() -> bool {
branchless_bs()
}
static BS_PRECOMP: std::sync::atomic::AtomicU8 = std::sync::atomic::AtomicU8::new(0);
pub fn precomputed_bs_enabled() -> bool {
BS_PRECOMP.load(std::sync::atomic::Ordering::Relaxed) != 0
}
#[doc(hidden)]
pub fn set_precomputed_bs(on: bool) {
BS_PRECOMP.store(on as u8, std::sync::atomic::Ordering::Relaxed);
}
#[doc(hidden)]
pub fn set_branchless_bs(on: bool) {
BS_MODE.store(if on { 1 } else { 2 }, std::sync::atomic::Ordering::Relaxed);
}
impl BlockInfo<'_> {
#[inline]
fn at(&self, bx: usize, by: usize) -> usize {
by * self.w4 + bx
}
#[inline]
fn bs(&self, p: usize, q: usize, mb_edge: bool) -> i32 {
if branchless_bs() {
self.bs_branchless(p, q, mb_edge)
} else {
self.bs_branchy(p, q, mb_edge)
}
}
fn bs_branchy(&self, p: usize, q: usize, mb_edge: bool) -> i32 {
if !self.inter[p] || !self.inter[q] {
if mb_edge {
4
} else {
3
}
} else if self.nnz[p] > 0 || self.nnz[q] > 0 {
2
} else if self.inter_bs1(p, q) {
1
} else {
0
}
}
#[inline]
fn bs_branchless(&self, p: usize, q: usize, mb_edge: bool) -> i32 {
let intra = !(self.inter[p] & self.inter[q]);
let nz = (self.nnz[p] | self.nnz[q]) != 0;
let moved = self.inter_bs1(p, q);
let intra_bs = if mb_edge { 4 } else { 3 };
let motion_bs = moved as i32; let non_intra = if nz { 2 } else { motion_bs };
if intra {
intra_bs
} else {
non_intra
}
}
fn inter_bs1(&self, p: usize, q: usize) -> bool {
if self.ref_id1.is_empty() {
let (rp, rq) = (self.ref_id[p], self.ref_id[q]);
let (a, b) = (self.mv[p], self.mv[q]);
let far = ((a.0 - b.0).abs() >= 4) | ((a.1 - b.1).abs() >= 4);
return (rp != rq) | ((rp != NO_REF) & far);
}
let used = |i: usize| {
let mut v = [(0i32, (0i32, 0i32)); 2];
let mut n = 0;
if self.ref_id[i] != NO_REF {
v[n] = (self.ref_id[i], self.mv[i]);
n += 1;
}
if !self.ref_id1.is_empty() && self.ref_id1[i] != NO_REF {
v[n] = (self.ref_id1[i], self.mv1[i]);
n += 1;
}
(v, n)
};
let (pv, pn) = used(p);
let (qv, qn) = used(q);
if pn != qn {
return true; }
let far = |a: (i32, i32), b: (i32, i32)| (a.0 - b.0).abs() >= 4 || (a.1 - b.1).abs() >= 4;
match pn {
0 => false,
1 => pv[0].0 != qv[0].0 || far(pv[0].1, qv[0].1),
_ => {
let direct = !far(pv[0].1, qv[0].1) && !far(pv[1].1, qv[1].1);
let swap = !far(pv[0].1, qv[1].1) && !far(pv[1].1, qv[0].1);
if pv[0].0 == pv[1].0 {
qv[0].0 != pv[0].0 || qv[1].0 != pv[0].0 || !(direct || swap)
} else if pv[0].0 == qv[0].0 && pv[1].0 == qv[1].0 {
!direct
} else if pv[0].0 == qv[1].0 && pv[1].0 == qv[0].0 {
!swap
} else {
true }
}
}
}
}
#[inline]
fn thresholds(qpav: i32, offset_a: i32, offset_b: i32) -> (i32, i32, [i32; 3]) {
let ia = (qpav + offset_a).clamp(0, 51) as usize;
let ib = (qpav + offset_b).clamp(0, 51) as usize;
(ALPHA[ia], BETA[ib], TC0[ia])
}
#[allow(clippy::too_many_arguments)]
pub fn filter_frame(
y: &mut [u8],
u: &mut [u8],
v: &mut [u8],
mb_w: usize,
mb_h: usize,
mb_qp: &[u8],
chroma_qp_offset: i32,
offset_a: i32,
offset_b: i32,
info: &BlockInfo,
) {
let _g = crate::prof::scope(crate::prof::Stage::Deblock);
let cw = mb_w * 16;
let ccw = mb_w * 8;
let qpy = |mx: usize, my: usize| mb_qp[my * mb_w + mx] as i32;
let qpc = |qpy_val: i32| {
crate::predict::chroma_qp((qpy_val + chroma_qp_offset).clamp(0, 51) as u8) as i32
};
let two_pass = bs_twopass();
let kind_off = kind_gate_off();
let verify_kinds = verify_kind();
let packs: Option<Vec<MbPack>> = if bs_packed_on() && info.bs.is_empty() && deblock_tile() {
pack_frame(info, mb_w, mb_h)
} else {
None
};
for mb_y in 0..mb_h {
for mb_x in 0..mb_w {
let mb_t8 = !info.t8x8.is_empty() && info.t8x8[mb_y * mb_w + mb_x];
let precomputed = !info.bs.is_empty();
let use_tile = !precomputed && deblock_tile();
let have_bs = precomputed || use_tile;
let fast_kind = if use_tile && !kind_off {
match info.kind.get(mb_y * mb_w + mb_x).copied().and_then(MbKind::from_u8) {
Some(k) if k != MbKind::Inter => Some(k),
_ => None,
}
} else {
None
};
let blind_tile = use_tile && fast_kind.is_none() && packs.is_none();
let mut bs_v = [[0i32; 4]; 4];
let mut bs_h = [[0i32; 4]; 4];
let packed_mb = packs.as_ref().filter(|_| use_tile && fast_kind.is_none());
let tile = if blind_tile { Some(gather_tile(info, mb_x, mb_y)) } else { None };
let mut uniform_motion = false;
let flat_inter = if precomputed {
false } else if let Some(k) = fast_kind {
match k {
MbKind::Skip => true,
MbKind::InterUniform => {
let (bx0, by0) = (mb_x * 4, mb_y * 4);
(0..4).all(|r| {
(0..4).all(|c| info.nnz[(by0 + r) * info.w4 + bx0 + c] == 0)
})
}
_ => false,
}
} else if let Some(pk) = packed_mb {
#[cfg(feature = "profile")]
census::PACKED_MB.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let pflat = derive_mb_packed(pk, mb_w, mb_x, mb_y, mb_t8, &mut bs_v, &mut bs_h);
if verify_packed() {
let t = gather_tile(info, mb_x, mb_y);
let (u, f) = scan_uniform_flat(&t);
let (mut tv, mut th) = ([[0i32; 4]; 4], [[0i32; 4]; 4]);
derive_mb_bs(&t, mb_x, mb_y, f, u, mb_t8, &mut tv, &mut th);
assert_eq!(pflat, f, "MB ({mb_x},{mb_y}) t8={mb_t8}: flat_inter");
assert_eq!(bs_v, tv, "MB ({mb_x},{mb_y}) t8={mb_t8} flat={f}: bs_v");
assert_eq!(bs_h, th, "MB ({mb_x},{mb_y}) t8={mb_t8} flat={f}: bs_h");
}
pflat
} else if blind_tile {
let (u, f) = scan_predicates(tile.as_ref().unwrap(), two_pass);
uniform_motion = u;
f
} else {
let b0 = info.at(mb_x * 4, mb_y * 4);
let mut ok = info.inter[b0];
if ok {
let (r0, m0) = (info.ref_id[b0], info.mv[b0]);
let has1 = !info.ref_id1.is_empty();
let (r10, m10) = if has1 { (info.ref_id1[b0], info.mv1[b0]) } else { (NO_REF, (0, 0)) };
'scan: for by in 0..4 {
for bx in 0..4 {
let i = info.at(mb_x * 4 + bx, mb_y * 4 + by);
if !info.inter[i]
|| info.nnz[i] != 0
|| info.ref_id[i] != r0
|| info.mv[i] != m0
|| (has1 && (info.ref_id1[i] != r10 || info.mv1[i] != m10))
{
ok = false;
break 'scan;
}
}
}
}
ok
};
if packed_mb.is_some() {
} else if precomputed {
let m = &info.bs[mb_y * mb_w + mb_x];
for e in 0..4 {
for sg in 0..4 {
bs_v[e][sg] = m.v[e][sg] as i32;
bs_h[e][sg] = m.h[e][sg] as i32;
}
}
} else if let Some(k) = fast_kind {
derive_mb_kind_into(info, mb_x, mb_y, k, &mut bs_v, &mut bs_h);
if verify_kinds {
verify_kind_matches_blind(
info, mb_x, mb_y, mb_t8, k, flat_inter, &bs_v, &bs_h,
);
}
} else if blind_tile {
derive_mb_bs(
tile.as_ref().unwrap(),
mb_x,
mb_y,
flat_inter,
uniform_motion,
mb_t8,
&mut bs_v,
&mut bs_h,
);
}
for be in 0..4usize {
if be == 0 && mb_x == 0 {
continue;
}
if flat_inter && be != 0 {
continue; }
if mb_t8 && (be == 1 || be == 3) {
continue;
}
let mb_edge = be == 0;
let mut bs4 = [0i32; 4];
if have_bs {
bs4 = bs_v[be];
} else {
let abx = mb_x * 4 + be;
for (seg, b) in bs4.iter_mut().enumerate() {
let aby = mb_y * 4 + seg;
*b = info.bs(info.at(abx - 1, aby), info.at(abx, aby), mb_edge);
}
}
if bs4.iter().all(|&b| b == 0) {
continue;
}
let qpav = if mb_edge {
(qpy(mb_x - 1, mb_y) + qpy(mb_x, mb_y) + 1) >> 1
} else {
qpy(mb_x, mb_y)
};
let (alpha_y, beta_y, tc0a) = thresholds(qpav, offset_a, offset_b);
let tc0_luma = |bs: i32| if (1..4).contains(&bs) { tc0a[bs as usize - 1] } else { 0 };
let x = mb_x * 16 + be * 4;
#[cfg(accel)]
{
let base = mb_y * 16 * cw + (x - 4); if bs4.iter().all(|&b| b == 4) {
rusty_h264_accel::deblock_luma_eq4_h(&mut y[base..], cw, alpha_y, beta_y);
} else {
let tc: [i8; 4] = std::array::from_fn(|i| {
if (1..4).contains(&bs4[i]) { tc0_luma(bs4[i]) as i8 } else { -1 }
});
rusty_h264_accel::deblock_luma_lt4_h(&mut y[base..], cw, alpha_y, beta_y, &tc);
}
}
#[cfg(not(accel))]
for (seg, &bs) in bs4.iter().enumerate() {
if bs == 0 {
continue;
}
let tc0 = tc0_luma(bs);
for row in 0..4 {
let yy = mb_y * 16 + seg * 4 + row;
let line = Line { base: yy * cw + x, step: 1 };
filter_luma_line(y, &line, bs, alpha_y, beta_y, tc0);
}
}
}
for be in 0..4usize {
if be == 0 && mb_y == 0 {
continue;
}
if flat_inter && be != 0 {
continue; }
if mb_t8 && (be == 1 || be == 3) {
continue;
}
let mb_edge = be == 0;
let mut bs4 = [0i32; 4];
if have_bs {
bs4 = bs_h[be];
} else {
let aby = mb_y * 4 + be;
for (seg, b) in bs4.iter_mut().enumerate() {
let abx = mb_x * 4 + seg;
*b = info.bs(info.at(abx, aby - 1), info.at(abx, aby), mb_edge);
}
}
if bs4.iter().all(|&b| b == 0) {
continue;
}
let qpav = if mb_edge {
(qpy(mb_x, mb_y - 1) + qpy(mb_x, mb_y) + 1) >> 1
} else {
qpy(mb_x, mb_y)
};
let (alpha_y, beta_y, tc0a) = thresholds(qpav, offset_a, offset_b);
let tc0_luma = |bs: i32| if (1..4).contains(&bs) { tc0a[bs as usize - 1] } else { 0 };
let yy = mb_y * 16 + be * 4;
#[cfg(accel)]
{
let base = (yy - 4) * cw + mb_x * 16; if bs4.iter().all(|&b| b == 4) {
rusty_h264_accel::deblock_luma_eq4_v(&mut y[base..], cw, alpha_y, beta_y);
} else {
let tc: [i8; 4] = std::array::from_fn(|i| {
if (1..4).contains(&bs4[i]) { tc0_luma(bs4[i]) as i8 } else { -1 }
});
rusty_h264_accel::deblock_luma_lt4_v(&mut y[base..], cw, alpha_y, beta_y, &tc);
}
}
#[cfg(not(accel))]
for (seg, &bs) in bs4.iter().enumerate() {
if bs == 0 {
continue;
}
let tc0 = tc0_luma(bs);
for col in 0..4 {
let x = mb_x * 16 + seg * 4 + col;
let line = Line { base: yy * cw + x, step: cw as isize };
filter_luma_line(y, &line, bs, alpha_y, beta_y, tc0);
}
}
}
#[cfg(accel)]
{
let tc0_of = |arr: [i32; 3], bs: i32| if (1..4).contains(&bs) { arr[bs as usize - 1] } else { 0 };
let chroma_thresholds = |mb_edge: bool, nx: usize, ny: usize| {
let cur = qpc(qpy(mb_x, mb_y));
let q = if mb_edge { (qpc(qpy(nx, ny)) + cur + 1) >> 1 } else { cur };
thresholds(q, offset_a, offset_b)
};
for cxe in [0usize, 4] {
if cxe == 0 && mb_x == 0 {
continue;
}
if flat_inter && cxe != 0 {
continue; }
let mb_edge = cxe == 0;
let x = mb_x * 8 + cxe;
let mut bs4 = [0i32; 4];
if have_bs {
bs4 = bs_v[cxe / 2]; } else {
let abx = mb_x * 4 + cxe / 2;
for (seg, b) in bs4.iter_mut().enumerate() {
let aby = mb_y * 4 + seg;
*b = info.bs(info.at(abx - 1, aby), info.at(abx, aby), mb_edge);
}
}
if bs4.iter().all(|&b| b == 0) {
continue;
}
let (alpha_c, beta_c, tc0c) =
chroma_thresholds(mb_edge, mb_x.wrapping_sub(1), mb_y);
let base = (mb_y * 8) * ccw + (x - 2); if bs4.iter().all(|&b| b == 4) {
rusty_h264_accel::deblock_chroma_eq4_h(&mut u[base..], &mut v[base..], ccw, alpha_c, beta_c);
} else {
let tc: [i8; 4] = std::array::from_fn(|i| {
if (1..4).contains(&bs4[i]) { tc0_of(tc0c, bs4[i]) as i8 + 1 } else { 0 }
});
rusty_h264_accel::deblock_chroma_lt4_h(&mut u[base..], &mut v[base..], ccw, alpha_c, beta_c, &tc);
}
}
for cye in [0usize, 4] {
if cye == 0 && mb_y == 0 {
continue;
}
if flat_inter && cye != 0 {
continue; }
let mb_edge = cye == 0;
let yy = mb_y * 8 + cye;
let mut bs4 = [0i32; 4];
if have_bs {
bs4 = bs_h[cye / 2]; } else {
let aby = mb_y * 4 + cye / 2;
for (seg, b) in bs4.iter_mut().enumerate() {
let abx = mb_x * 4 + seg;
*b = info.bs(info.at(abx, aby - 1), info.at(abx, aby), mb_edge);
}
}
if bs4.iter().all(|&b| b == 0) {
continue;
}
let (alpha_c, beta_c, tc0c) =
chroma_thresholds(mb_edge, mb_x, mb_y.wrapping_sub(1));
let base = (yy - 2) * ccw + mb_x * 8; if bs4.iter().all(|&b| b == 4) {
rusty_h264_accel::deblock_chroma_eq4_v(&mut u[base..], &mut v[base..], ccw, alpha_c, beta_c);
} else {
let tc: [i8; 4] = std::array::from_fn(|i| {
if (1..4).contains(&bs4[i]) { tc0_of(tc0c, bs4[i]) as i8 + 1 } else { 0 }
});
rusty_h264_accel::deblock_chroma_lt4_v(&mut u[base..], &mut v[base..], ccw, alpha_c, beta_c, &tc);
}
}
}
#[cfg(not(accel))]
{
let cur_qpc = qpc(qpy(mb_x, mb_y));
let (alpha_cv, beta_cv, tc0cv) = if mb_x > 0 {
thresholds((qpc(qpy(mb_x - 1, mb_y)) + cur_qpc + 1) >> 1, offset_a, offset_b)
} else {
(0, 0, [0; 3]) };
let (alpha_ch, beta_ch, tc0ch) = if mb_y > 0 {
thresholds((qpc(qpy(mb_x, mb_y - 1)) + cur_qpc + 1) >> 1, offset_a, offset_b)
} else {
(0, 0, [0; 3])
};
let (alpha_ci, beta_ci, tc0ci) = thresholds(cur_qpc, offset_a, offset_b);
let tc0_of = |arr: [i32; 3], bs: i32| if (1..4).contains(&bs) { arr[bs as usize - 1] } else { 0 };
for plane in [&mut *u, &mut *v] {
for cxe in [0usize, 4] {
if cxe == 0 && mb_x == 0 {
continue;
}
if flat_inter && cxe != 0 {
continue;
}
let mb_edge = cxe == 0;
let (alpha_c, beta_c, tc0c) =
if mb_edge { (alpha_cv, beta_cv, tc0cv) } else { (alpha_ci, beta_ci, tc0ci) };
let abx = mb_x * 4 + cxe / 2; let x = mb_x * 8 + cxe;
for row in 0..8 {
let aby = mb_y * 4 + (row * 2) / 4; let bs = info.bs(info.at(abx - 1, aby), info.at(abx, aby), mb_edge);
if bs == 0 {
continue;
}
let yy = mb_y * 8 + row;
let line = Line { base: yy * ccw + x, step: 1 };
filter_chroma_line(plane, &line, bs, alpha_c, beta_c, tc0_of(tc0c, bs));
}
}
for cye in [0usize, 4] {
if cye == 0 && mb_y == 0 {
continue;
}
if flat_inter && cye != 0 {
continue;
}
let mb_edge = cye == 0;
let (alpha_c, beta_c, tc0c) =
if mb_edge { (alpha_ch, beta_ch, tc0ch) } else { (alpha_ci, beta_ci, tc0ci) };
let aby = mb_y * 4 + cye / 2;
let yy = mb_y * 8 + cye;
for col in 0..8 {
let abx = mb_x * 4 + (col * 2) / 4;
let bs = info.bs(info.at(abx, aby - 1), info.at(abx, aby), mb_edge);
if bs == 0 {
continue;
}
let line = Line { base: yy * ccw + (mb_x * 8 + col), step: ccw as isize };
filter_chroma_line(plane, &line, bs, alpha_c, beta_c, tc0_of(tc0c, bs));
}
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bs_arms_agree() {
let (w4, h4) = (16usize, 16usize);
let n = w4 * h4;
let mut st = 0x9e3779b9u32;
let mut rnd = || {
st ^= st << 13;
st ^= st >> 17;
st ^= st << 5;
st
};
let mut inter = vec![false; n];
let mut nnz = vec![0u8; n];
let mut mv = vec![(0i32, 0i32); n];
let mut ref_id = vec![0i32; n];
for i in 0..n {
let r = rnd();
inter[i] = r & 3 != 0;
nnz[i] = if r & 0x30 != 0 { (r >> 8 & 15) as u8 } else { 0 };
mv[i] = (((r >> 12) & 15) as i32 - 8, ((r >> 16) & 15) as i32 - 8);
ref_id[i] = if inter[i] { ((r >> 20) & 3) as i32 } else { NO_REF };
}
let info = BlockInfo {
inter: &inter,
nnz: &nnz,
mv: &mv,
ref_id: &ref_id,
mv1: &[],
ref_id1: &[],
w4,
t8x8: &[],
bs: &[], kind: &[],
};
let mut checked = 0;
for q in 0..n {
for &p in &[q.saturating_sub(1), q.saturating_sub(w4)] {
for mb_edge in [false, true] {
assert_eq!(
info.bs_branchy(p, q, mb_edge),
info.bs_branchless(p, q, mb_edge),
"bS mismatch at p={p} q={q} mb_edge={mb_edge}"
);
checked += 1;
}
}
}
assert!(checked > 1000, "expected broad coverage, checked {checked}");
}
}
#[cfg(test)]
mod tile_tests {
use super::*;
#[test]
fn tile_matches_frame_indexing() {
let (mb_w, mb_h) = (5usize, 4usize);
let w4 = mb_w * 4;
let n = w4 * mb_h * 4;
let mut st = 0xdeadbeefu32;
let mut rnd = || {
st ^= st << 13;
st ^= st >> 17;
st ^= st << 5;
st
};
let mut inter = vec![false; n];
let mut nnz = vec![0u8; n];
let mut mv = vec![(0i32, 0i32); n];
let mut ref_id = vec![0i32; n];
for i in 0..n {
let r = rnd();
inter[i] = r & 3 != 0;
nnz[i] = if r & 0x30 != 0 { (r >> 8 & 15) as u8 } else { 0 };
mv[i] = (((r >> 12) & 15) as i32 - 8, ((r >> 16) & 15) as i32 - 8);
ref_id[i] = if inter[i] { ((r >> 20) & 3) as i32 } else { NO_REF };
}
let info = BlockInfo {
inter: &inter, nnz: &nnz, mv: &mv, ref_id: &ref_id,
mv1: &[], ref_id1: &[], w4, t8x8: &[], bs: &[], kind: &[],
};
let mut checked = 0;
for mb_y in 0..mb_h {
for mb_x in 0..mb_w {
let tile = gather_tile(&info, mb_x, mb_y);
for be in 0..4 {
let mb_edge = be == 0;
if mb_edge && mb_x == 0 {
continue;
}
let abx = mb_x * 4 + be;
for seg in 0..4 {
let aby = mb_y * 4 + seg;
assert_eq!(
info.bs(info.at(abx - 1, aby), info.at(abx, aby), mb_edge),
bs_tile(&tile[seg + 1][be], &tile[seg + 1][be + 1], mb_edge),
"luma V mb=({mb_x},{mb_y}) be={be} seg={seg}"
);
checked += 1;
}
}
for be in 0..4 {
let mb_edge = be == 0;
if mb_edge && mb_y == 0 {
continue;
}
let aby = mb_y * 4 + be;
for seg in 0..4 {
let abx = mb_x * 4 + seg;
assert_eq!(
info.bs(info.at(abx, aby - 1), info.at(abx, aby), mb_edge),
bs_tile(&tile[be][seg + 1], &tile[be + 1][seg + 1], mb_edge),
"luma H mb=({mb_x},{mb_y}) be={be} seg={seg}"
);
checked += 1;
}
}
for cxe in [0usize, 4] {
let mb_edge = cxe == 0;
if mb_edge && mb_x == 0 {
continue;
}
let abx = mb_x * 4 + cxe / 2;
for seg in 0..4 {
let aby = mb_y * 4 + seg;
assert_eq!(
info.bs(info.at(abx - 1, aby), info.at(abx, aby), mb_edge),
bs_tile(&tile[seg + 1][cxe / 2], &tile[seg + 1][cxe / 2 + 1], mb_edge),
"chroma V mb=({mb_x},{mb_y}) cxe={cxe} seg={seg}"
);
checked += 1;
}
}
for cye in [0usize, 4] {
let mb_edge = cye == 0;
if mb_edge && mb_y == 0 {
continue;
}
let aby = mb_y * 4 + cye / 2;
for seg in 0..4 {
let abx = mb_x * 4 + seg;
assert_eq!(
info.bs(info.at(abx, aby - 1), info.at(abx, aby), mb_edge),
bs_tile(&tile[cye / 2][seg + 1], &tile[cye / 2 + 1][seg + 1], mb_edge),
"chroma H mb=({mb_x},{mb_y}) cye={cye} seg={seg}"
);
checked += 1;
}
}
}
}
assert!(checked > 800, "coverage too low: {checked}");
}
}
#[cfg(test)]
mod chroma_bs_tests {
use super::*;
#[test]
fn chroma_bs_matches_luma() {
let (mb_w, mb_h) = (5usize, 4usize);
let w4 = mb_w * 4;
let n = w4 * mb_h * 4;
let mut st = 0x1badb002u32;
let mut rnd = || {
st ^= st << 13;
st ^= st >> 17;
st ^= st << 5;
st
};
let (mut inter, mut nnz) = (vec![false; n], vec![0u8; n]);
let (mut mv, mut ref_id) = (vec![(0i32, 0i32); n], vec![0i32; n]);
for i in 0..n {
let r = rnd();
inter[i] = r & 3 != 0;
nnz[i] = if r & 0x30 != 0 { (r >> 8 & 15) as u8 } else { 0 };
mv[i] = (((r >> 12) & 15) as i32 - 8, ((r >> 16) & 15) as i32 - 8);
ref_id[i] = if inter[i] { ((r >> 20) & 3) as i32 } else { NO_REF };
}
let info = BlockInfo {
inter: &inter, nnz: &nnz, mv: &mv, ref_id: &ref_id,
mv1: &[], ref_id1: &[], w4, t8x8: &[], bs: &[], kind: &[],
};
let mut checked = 0;
for mb_y in 0..mb_h {
for mb_x in 0..mb_w {
let tile = gather_tile(&info, mb_x, mb_y);
for (cxe, be) in [(0usize, 0usize), (4, 2)] {
let mb_edge = cxe == 0;
for seg in 0..4 {
assert_eq!(
bs_tile(&tile[seg + 1][cxe / 2], &tile[seg + 1][cxe / 2 + 1], mb_edge),
bs_tile(&tile[seg + 1][be], &tile[seg + 1][be + 1], mb_edge),
"V mb=({mb_x},{mb_y}) cxe={cxe} seg={seg}"
);
assert_eq!(
bs_tile(&tile[cxe / 2][seg + 1], &tile[cxe / 2 + 1][seg + 1], mb_edge),
bs_tile(&tile[be][seg + 1], &tile[be + 1][seg + 1], mb_edge),
"H mb=({mb_x},{mb_y}) cye={cxe} seg={seg}"
);
checked += 2;
}
}
}
}
assert!(checked > 300, "coverage too low: {checked}");
}
}
#[cfg(test)]
mod derive_tests {
use super::*;
#[test]
fn mb_uniform_simd_matches_scalar() {
let mut st = 0x51ed270bu32;
let mut rnd = || {
st ^= st << 13;
st ^= st >> 17;
st ^= st << 5;
st
};
for case in 0..6000 {
let mut p = MbPack::default();
p.inter = case % 7 != 0; for k in 0..16 {
p.ref_id[k] = 3;
p.mvx[k] = 7;
p.mvy[k] = -9;
p.ref1[k] = if case % 3 == 0 { NO_REF } else { 5 };
p.mvx1[k] = 2;
p.mvy1[k] = -4;
}
if case % 2 == 0 {
let k = 1 + (rnd() % 15) as usize;
match rnd() % 6 {
0 => p.ref_id[k] += 1,
1 => p.mvx[k] += 1,
2 => p.mvy[k] += 1,
3 => p.ref1[k] = p.ref1[k].wrapping_add(1),
4 => p.mvx1[k] += 1,
_ => p.mvy1[k] += 1,
}
}
assert_eq!(
mb_uniform(&p),
mb_uniform_scalar(&p),
"case {case}: uniform SIMD != scalar"
);
}
}
#[test]
fn bs_motion_masks_simd_matches_scalar() {
let mut st = 0x9e3779b9u32;
let mut rnd = || {
st ^= st << 13;
st ^= st >> 17;
st ^= st << 5;
st
};
for case in 0..4000 {
let mut p = MbPack::default();
p.inter = true;
for k in 0..16 {
let r = rnd();
p.ref_id[k] = match case % 4 {
0 => (r & 1) as i32, 1 => 7, 2 if r & 7 == 0 => NO_REF, _ => (r & 3) as i32,
};
p.mvx[k] = match case % 3 {
0 => ((r >> 3) & 7) as i16 - 4,
1 => ((r >> 3) & 0x7fff) as i16 - 16384,
_ => ((r >> 3) & 1) as i16 * 4,
};
p.mvy[k] = match case % 3 {
0 => ((r >> 9) & 7) as i16 - 4,
1 => ((r >> 9) & 0x7fff) as i16 - 16384,
_ => ((r >> 9) & 1) as i16 * 4,
};
}
let want = bs_motion_masks_scalar(&p);
let got = bs_motion_masks(&p);
assert_eq!(
got, want,
"case {case}: SIMD (left={:#06x} up={:#06x}) != scalar (left={:#06x} up={:#06x})",
got.0, got.1, want.0, want.1
);
}
}
#[test]
fn packed_matches_tile() {
let (mb_w, mb_h) = (6usize, 5usize);
let w4 = mb_w * 4;
let n = w4 * mb_h * 4;
let mut st = 0x1234abcdu32;
let mut rnd = || {
st ^= st << 13;
st ^= st >> 17;
st ^= st << 5;
st
};
let (mut inter, mut nnz) = (vec![false; n], vec![0u8; n]);
let (mut mv, mut ref_id) = (vec![(0i32, 0i32); n], vec![0i32; n]);
for my in 0..mb_h {
for mx in 0..mb_w {
let mb_inter = rnd() & 3 != 0;
let uniform_mb = rnd() & 1 == 0;
let (ur, umv) = ((rnd() & 1) as i32, ((rnd() & 7) as i32 - 4, (rnd() & 7) as i32 - 4));
let zero_coeffs = rnd() & 1 == 0;
for by in 0..4 {
for bx in 0..4 {
let i = (my * 4 + by) * w4 + mx * 4 + bx;
let r = rnd();
inter[i] = mb_inter;
nnz[i] = if uniform_mb && zero_coeffs {
0
} else if r & 0x30 != 0 {
(r >> 8 & 15) as u8
} else {
0
};
if mb_inter {
ref_id[i] = if uniform_mb { ur } else { (r >> 12 & 1) as i32 };
mv[i] = if uniform_mb {
umv
} else {
((r >> 16 & 15) as i32 - 8, (r >> 20 & 15) as i32 - 8)
};
} else {
ref_id[i] = NO_REF;
mv[i] = (0, 0);
}
}
}
}
}
let (mut mv1, mut ref_id1) = (vec![(0i32, 0i32); n], vec![NO_REF; n]);
for my in 0..mb_h {
for mx in 0..mb_w {
let mb_bi = rnd() & 1 == 0; for by in 0..4 {
for bx in 0..4 {
let i = (my * 4 + by) * w4 + mx * 4 + bx;
let r = rnd();
if inter[i] && mb_bi && r & 3 != 0 {
ref_id1[i] = (r >> 2 & 1) as i32;
mv1[i] = ((r >> 4 & 15) as i32 - 8, (r >> 8 & 15) as i32 - 8);
}
}
}
}
}
for (tag, m1, r1) in [
("single-list", &[][..], &[][..]),
("two-list", &mv1[..], &ref_id1[..]),
] {
let info = BlockInfo {
inter: &inter, nnz: &nnz, mv: &mv, ref_id: &ref_id,
mv1: m1, ref_id1: r1, w4, t8x8: &[], bs: &[], kind: &[],
};
let packs = pack_frame(&info, mb_w, mb_h).expect("frame packs");
let _ = tag;
let mut checked = 0usize;
for mb_y in 0..mb_h {
for mb_x in 0..mb_w {
let tile = gather_tile(&info, mb_x, mb_y);
let (uniform, flat) = scan_uniform_flat(&tile);
for &mb_t8 in &[false, true] {
let (mut tv, mut th) = ([[0i32; 4]; 4], [[0i32; 4]; 4]);
derive_mb_bs(&tile, mb_x, mb_y, flat, uniform, mb_t8, &mut tv, &mut th);
let (mut pv, mut ph) = ([[0i32; 4]; 4], [[0i32; 4]; 4]);
let pflat =
derive_mb_packed(&packs, mb_w, mb_x, mb_y, mb_t8, &mut pv, &mut ph);
assert_eq!(
pflat, flat,
"MB ({mb_x},{mb_y}) t8={mb_t8}: flat_inter packed={pflat} tile={flat}"
);
for be in 0..4usize {
if be == 0 {
if mb_x > 0 {
assert_eq!(pv[0], tv[0], "MB ({mb_x},{mb_y}) left MB edge");
}
if mb_y > 0 {
assert_eq!(ph[0], th[0], "MB ({mb_x},{mb_y}) top MB edge");
}
continue;
}
if flat || (mb_t8 && (be == 1 || be == 3)) {
continue;
}
assert_eq!(pv[be], tv[be], "MB ({mb_x},{mb_y}) t8={mb_t8} v edge {be}");
assert_eq!(ph[be], th[be], "MB ({mb_x},{mb_y}) t8={mb_t8} h edge {be}");
}
checked += 1;
}
}
}
assert_eq!(checked, mb_w * mb_h * 2, "every macroblock checked both t8 ways ({tag})");
}
}
#[test]
fn derive_matches_per_edge() {
let (mb_w, mb_h) = (6usize, 5usize);
let w4 = mb_w * 4;
let n = w4 * mb_h * 4;
let mut st = 0xfeedfaceu32;
let mut rnd = || {
st ^= st << 13;
st ^= st >> 17;
st ^= st << 5;
st
};
let (mut inter, mut nnz) = (vec![false; n], vec![0u8; n]);
let (mut mv, mut ref_id) = (vec![(0i32, 0i32); n], vec![0i32; n]);
for my in 0..mb_h {
for mx in 0..mb_w {
let mb_inter = rnd() & 3 != 0;
for by in 0..4 {
for bx in 0..4 {
let i = (my * 4 + by) * w4 + mx * 4 + bx;
let r = rnd();
inter[i] = mb_inter;
nnz[i] = if r & 0x30 != 0 { (r >> 8 & 15) as u8 } else { 0 };
mv[i] = (((r >> 12) & 15) as i32 - 8, ((r >> 16) & 15) as i32 - 8);
ref_id[i] = if mb_inter { ((r >> 20) & 3) as i32 } else { NO_REF };
}
}
}
}
let info = BlockInfo {
inter: &inter, nnz: &nnz, mv: &mv, ref_id: &ref_id,
mv1: &[], ref_id1: &[], w4, t8x8: &[], bs: &[], kind: &[],
};
for mb_y in 0..mb_h {
for mb_x in 0..mb_w {
let tile = gather_tile(&info, mb_x, mb_y);
assert_eq!(
scan_uniform_flat(&tile),
scan_two_pass(&tile),
"fused predicate walk disagrees with the two-scan oracle at ({mb_x},{mb_y})"
);
}
}
let mut checked = 0;
for mb_y in 0..mb_h {
for mb_x in 0..mb_w {
let tile = gather_tile(&info, mb_x, mb_y);
let b0 = &tile[1][1];
let flat = b0.inter
&& (1..5).all(|r| (1..5).all(|c| {
let b = &tile[r][c];
b.inter && !b.nz && b.same_motion(b0)
}));
let uniform = b0.inter
&& (1..5).all(|r| {
(1..5).all(|c| tile[r][c].inter && tile[r][c].same_motion(b0))
});
for &mb_t8 in &[false, true] {
let (mut bv, mut bh) = ([[0i32; 4]; 4], [[0i32; 4]; 4]);
derive_mb_bs(&tile, mb_x, mb_y, flat, uniform, mb_t8, &mut bv, &mut bh);
for be in 0..4usize {
let mb_edge = be == 0;
let skip_internal =
!mb_edge && (flat || (mb_t8 && (be == 1 || be == 3)));
for seg in 0..4 {
let want_v = if skip_internal || (mb_edge && mb_x == 0) {
0
} else {
bs_tile(&tile[seg + 1][be], &tile[seg + 1][be + 1], mb_edge)
};
let want_h = if skip_internal || (mb_edge && mb_y == 0) {
0
} else {
bs_tile(&tile[be][seg + 1], &tile[be + 1][seg + 1], mb_edge)
};
assert_eq!(bv[be][seg], want_v, "V mb=({mb_x},{mb_y}) be={be} seg={seg} t8={mb_t8}");
assert_eq!(bh[be][seg], want_h, "H mb=({mb_x},{mb_y}) be={be} seg={seg} t8={mb_t8}");
checked += 2;
}
}
}
}
}
assert!(checked > 1500, "coverage too low: {checked}");
}
}