#![allow(non_snake_case)]
#[path = "hpel.rs"]
mod hpel;
#[path = "mectx.rs"]
mod mectx;
#[path = "satd_avg.rs"]
pub(crate) mod satd_avg;
pub use hpel::hpel_fused;
pub use mectx::MeCtx;
pub use satd_avg::{sad_x4, satd_avg, satd_avg_x4, satd_x4, satd_x4p, x4_shape};
extern "C" {
fn WelsSampleSatd4x4_sse2(p1: *const u8, s1: i32, p2: *const u8, s2: i32) -> i32;
fn WelsSampleSatd8x8_sse2(p1: *const u8, s1: i32, p2: *const u8, s2: i32) -> i32;
fn WelsSampleSatd16x8_sse2(p1: *const u8, s1: i32, p2: *const u8, s2: i32) -> i32;
fn WelsSampleSatd8x16_sse2(p1: *const u8, s1: i32, p2: *const u8, s2: i32) -> i32;
fn WelsSampleSatd16x16_sse2(p1: *const u8, s1: i32, p2: *const u8, s2: i32) -> i32;
fn WelsSampleSad16x16_sse2(p1: *const u8, s1: i32, p2: *const u8, s2: i32) -> i32;
fn WelsSampleSad16x8_sse2(p1: *const u8, s1: i32, p2: *const u8, s2: i32) -> i32;
fn WelsSampleSad8x16_sse2(p1: *const u8, s1: i32, p2: *const u8, s2: i32) -> i32;
fn WelsQuantFour4x4_sse2(p_dct: *mut i16, p_ff: *const i16, p_mf: *const i16);
fn WelsI16x16LumaPredV_sse2(pred: *mut u8, refp: *const u8, stride: i32);
fn WelsI16x16LumaPredH_sse2(pred: *mut u8, refp: *const u8, stride: i32);
fn WelsI16x16LumaPredDc_sse2(pred: *mut u8, refp: *const u8, stride: i32);
fn WelsI16x16LumaPredPlane_sse2(pred: *mut u8, refp: *const u8, stride: i32);
fn WelsIChromaPredV_sse2(pred: *mut u8, refp: *const u8, stride: i32);
fn WelsIChromaPredPlane_sse2(pred: *mut u8, refp: *const u8, stride: i32);
fn WelsDctFourT4_sse2(p_dct: *mut i16, p1: *const u8, s1: i32, p2: *const u8, s2: i32);
fn WelsIDctFourT4Rec_sse2(
p_rec: *mut u8,
stride: i32,
p_pred: *const u8,
pred_stride: i32,
p_dct: *const i16,
);
fn WelsDctFourT4_avx2(p_dct: *mut i16, p1: *const u8, s1: i32, p2: *const u8, s2: i32);
fn WelsIDctFourT4Rec_avx2(
p_rec: *mut u8,
stride: i32,
p_pred: *const u8,
pred_stride: i32,
p_dct: *const i16,
);
fn WelsQuantFour4x4_avx2(p_dct: *mut i16, p_ff: *const i16, p_mf: *const i16);
fn WelsSampleSatd8x8_avx2(p1: *const u8, s1: i32, p2: *const u8, s2: i32) -> i32;
fn WelsSampleSatd16x8_avx2(p1: *const u8, s1: i32, p2: *const u8, s2: i32) -> i32;
fn WelsSampleSatd8x16_avx2(p1: *const u8, s1: i32, p2: *const u8, s2: i32) -> i32;
fn WelsSampleSatd16x16_avx2(p1: *const u8, s1: i32, p2: *const u8, s2: i32) -> i32;
}
#[inline]
fn abl_db_kernel() -> bool {
static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
*ON.get_or_init(|| std::env::var_os("RFF_ABL_DBKERNEL").is_some())
}
#[inline]
fn has_avx2() -> bool {
use std::sync::OnceLock;
static C: OnceLock<bool> = OnceLock::new();
*C.get_or_init(|| std::is_x86_feature_detected!("avx2"))
}
macro_rules! satd_wrapper {
($name:ident, $sse2:ident, $avx2:ident, $w:expr, $h:expr) => {
#[doc = concat!("SATD of a ", stringify!($w), "×", stringify!($h),
" block pair via openh264's Hadamard kernel (AVX2 when available, else SSE2 — \
bit-identical). Equal to the sum of the constituent 4×4 SATDs.")]
#[inline]
pub fn $name(pix1: &[u8], stride1: usize, pix2: &[u8], stride2: usize) -> i32 {
assert!(pix1.len() >= ($h - 1) * stride1 + $w && pix2.len() >= ($h - 1) * stride2 + $w);
unsafe {
if has_avx2() {
$avx2(pix1.as_ptr(), stride1 as i32, pix2.as_ptr(), stride2 as i32)
} else {
$sse2(pix1.as_ptr(), stride1 as i32, pix2.as_ptr(), stride2 as i32)
}
}
}
};
}
satd_wrapper!(satd_8x8, WelsSampleSatd8x8_sse2, WelsSampleSatd8x8_avx2, 8, 8);
satd_wrapper!(satd_16x8, WelsSampleSatd16x8_sse2, WelsSampleSatd16x8_avx2, 16, 8);
satd_wrapper!(satd_8x16, WelsSampleSatd8x16_sse2, WelsSampleSatd8x16_avx2, 8, 16);
satd_wrapper!(satd_16x16, WelsSampleSatd16x16_sse2, WelsSampleSatd16x16_avx2, 16, 16);
#[inline]
pub fn chroma8x8_pred(mode: u8, pred: &mut [u8], rec: &[u8], base: usize, stride: usize) {
assert!(pred.len() >= 64 && pred.as_ptr() as usize % 16 == 0);
assert!(base >= stride + 1 && base + 7 * stride <= rec.len());
let s = stride as i32;
unsafe {
let p = pred.as_mut_ptr();
let r = rec.as_ptr().add(base);
match mode {
2 => WelsIChromaPredV_sse2(p, r, s),
_ => WelsIChromaPredPlane_sse2(p, r, s),
}
}
}
#[inline]
pub fn i16x16_luma_pred(mode: u8, pred: &mut [u8], rec: &[u8], base: usize, stride: usize) {
assert!(pred.len() >= 256 && pred.as_ptr() as usize % 16 == 0);
assert!(base >= stride + 1 && base + 15 * stride <= rec.len());
let s = stride as i32;
unsafe {
let p = pred.as_mut_ptr();
let r = rec.as_ptr().add(base);
match mode {
0 => WelsI16x16LumaPredV_sse2(p, r, s),
1 => WelsI16x16LumaPredH_sse2(p, r, s),
2 => WelsI16x16LumaPredDc_sse2(p, r, s),
_ => WelsI16x16LumaPredPlane_sse2(p, r, s),
}
}
}
#[inline]
pub fn quant_four_4x4(dct: &mut [i16], ff: &[i16; 8], mf: &[i16; 8]) {
assert!(dct.len() >= 64);
#[repr(align(16))]
struct A([i16; 8]);
let (ffa, mfa) = (A(*ff), A(*mf));
unsafe {
if has_avx2() {
WelsQuantFour4x4_avx2(dct.as_mut_ptr(), ffa.0.as_ptr(), mfa.0.as_ptr())
} else {
WelsQuantFour4x4_sse2(dct.as_mut_ptr(), ffa.0.as_ptr(), mfa.0.as_ptr())
}
}
}
#[inline]
#[inline]
fn abl_recon() -> bool {
use std::sync::atomic::{AtomicU8, Ordering};
static ON: AtomicU8 = AtomicU8::new(0);
match ON.load(Ordering::Relaxed) {
1 => true,
2 => false,
_ => {
let on = std::env::var_os("RFF_ABL_RECON").is_some_and(|v| v != "0");
ON.store(if on { 1 } else { 2 }, Ordering::Relaxed);
on
}
}
}
pub fn idct_four_t4_rec(
rec: &mut [u8],
stride_rec: usize,
pred: &[u8],
stride_pred: usize,
dct: &[i16],
) {
assert!(dct.len() >= 64);
assert!(rec.len() >= 7 * stride_rec + 8);
assert!(pred.len() >= 7 * stride_pred + 8);
if abl_recon() {
for r in 0..8 {
rec[r * stride_rec..r * stride_rec + 8]
.copy_from_slice(&pred[r * stride_pred..r * stride_pred + 8]);
}
return;
}
unsafe {
if has_avx2() {
WelsIDctFourT4Rec_avx2(
rec.as_mut_ptr(),
stride_rec as i32,
pred.as_ptr(),
stride_pred as i32,
dct.as_ptr(),
);
} else {
WelsIDctFourT4Rec_sse2(
rec.as_mut_ptr(),
stride_rec as i32,
pred.as_ptr(),
stride_pred as i32,
dct.as_ptr(),
);
}
}
}
#[inline]
pub fn dct_four_t4(dct: &mut [i16], src: &[u8], stride_src: usize, pred: &[u8], stride_pred: usize) {
assert!(dct.len() >= 64);
assert!(src.len() >= 7 * stride_src + 8);
assert!(pred.len() >= 7 * stride_pred + 8);
unsafe {
if has_avx2() {
WelsDctFourT4_avx2(
dct.as_mut_ptr(),
src.as_ptr(),
stride_src as i32,
pred.as_ptr(),
stride_pred as i32,
);
} else {
WelsDctFourT4_sse2(
dct.as_mut_ptr(),
src.as_ptr(),
stride_src as i32,
pred.as_ptr(),
stride_pred as i32,
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{mc_chroma_w4, mc_chroma_w8};
use crate::{sad_16x16, sad_16x8, sad_8x16, satd_16x16, satd_16x8, satd_4x4, satd_8x16, satd_8x8};
fn satd_ref(a: &[u8], sa: usize, b: &[u8], sb: usize) -> i32 {
let mut m = [[0i32; 4]; 4];
for i in 0..4 {
for j in 0..4 {
m[i][j] = a[i * sa + j] as i32 - b[i * sb + j] as i32;
}
}
for row in m.iter_mut() {
let (s0, s1, s2, s3) =
(row[0] + row[2], row[1] + row[3], row[0] - row[2], row[1] - row[3]);
*row = [s0 + s1, s2 + s3, s2 - s3, s0 - s1];
}
let mut sum = 0i32;
for j in 0..4 {
let (s0, s1, s2, s3) =
(m[0][j] + m[2][j], m[1][j] + m[3][j], m[0][j] - m[2][j], m[1][j] - m[3][j]);
let (c0, c1, c2, c3) = (s0 + s1, s2 + s3, s2 - s3, s0 - s1);
sum += c0.abs() + c1.abs() + c2.abs() + c3.abs();
}
(sum + 1) >> 1
}
#[test]
fn mc_chroma_w8_matches_scalar() {
let mut tile = [0u8; 9 * 9];
let mut s = 0x12345u32;
for v in tile.iter_mut() {
s = s.wrapping_mul(1103515245).wrapping_add(12345);
*v = (s >> 16) as u8;
}
for fy in 0..8i32 {
for fx in 0..8i32 {
let (wa, wb, wc, wd) =
((8 - fx) * (8 - fy), fx * (8 - fy), (8 - fx) * fy, fx * fy);
let abcd = [wa as u8, wb as u8, wc as u8, wd as u8];
let mut got = [0u8; 64];
mc_chroma_w8(&tile, 9, &mut got, 8, &abcd, 8);
let mut want = [0u8; 64];
for r in 0..8 {
for c in 0..8 {
let p = r * 9 + c;
let v = wa * tile[p] as i32
+ wb * tile[p + 1] as i32
+ wc * tile[p + 9] as i32
+ wd * tile[p + 9 + 1] as i32;
want[r * 8 + c] = ((v + 32) >> 6) as u8;
}
}
assert_eq!(got, want, "fx={fx} fy={fy}");
}
}
}
#[test]
fn mc_chroma_w4_matches_scalar() {
let mut tile = [0u8; 5 * 9];
let mut s = 0xbeef1u32;
for v in tile.iter_mut() {
s = s.wrapping_mul(1103515245).wrapping_add(12345);
*v = (s >> 16) as u8;
}
for h in [2usize, 4, 8] {
for fy in 0..8i32 {
for fx in 0..8i32 {
let (wa, wb, wc, wd) =
((8 - fx) * (8 - fy), fx * (8 - fy), (8 - fx) * fy, fx * fy);
let abcd = [wa as u8, wb as u8, wc as u8, wd as u8];
let mut got = [0u8; 32];
mc_chroma_w4(&tile, 5, &mut got[..h * 4], 4, &abcd, h);
let mut want = [0u8; 32];
for r in 0..h {
for c in 0..4 {
let p = r * 5 + c;
let v = wa * tile[p] as i32
+ wb * tile[p + 1] as i32
+ wc * tile[p + 5] as i32
+ wd * tile[p + 5 + 1] as i32;
want[r * 4 + c] = ((v + 32) >> 6) as u8;
}
}
assert_eq!(got[..h * 4], want[..h * 4], "h={h} fx={fx} fy={fy}");
}
}
}
}
fn forward_core(b: &[i32; 16]) -> [i32; 16] {
let f = |x0: i32, x1: i32, x2: i32, x3: i32| {
let (t0, t1, t2, t3) = (x0 + x3, x1 + x2, x1 - x2, x0 - x3);
(t0 + t1, 2 * t3 + t2, t0 - t1, t3 - 2 * t2)
};
let mut m = *b;
for r in 0..4 {
let (a, c, d, e) = f(m[r * 4], m[r * 4 + 1], m[r * 4 + 2], m[r * 4 + 3]);
m[r * 4] = a;
m[r * 4 + 1] = c;
m[r * 4 + 2] = d;
m[r * 4 + 3] = e;
}
for c in 0..4 {
let (a, b2, d, e) = f(m[c], m[4 + c], m[8 + c], m[12 + c]);
m[c] = a;
m[4 + c] = b2;
m[8 + c] = d;
m[12 + c] = e;
}
m
}
#[test]
fn dct_four_t4_matches_forward_core() {
for seed in 0..128usize {
let mut src = [0u8; 64];
let mut pred = [0u8; 64];
for y in 0..8 {
for x in 0..8 {
src[y * 8 + x] = ((y * 31 + x * 17 + seed * 7) & 0xff) as u8;
pred[y * 8 + x] = ((y * 13 + x * 41 + seed * 5 + 9) & 0xff) as u8;
}
}
let mut dct = [0i16; 64];
dct_four_t4(&mut dct, &src, 8, &pred, 8);
for (k, (ox, oy)) in [(0, 0), (4, 0), (0, 4), (4, 4)].iter().enumerate() {
let mut res = [0i32; 16];
for dy in 0..4 {
for dx in 0..4 {
res[dy * 4 + dx] =
src[(oy + dy) * 8 + ox + dx] as i32 - pred[(oy + dy) * 8 + ox + dx] as i32;
}
}
let want = forward_core(&res);
for i in 0..16 {
assert_eq!(
dct[k * 16 + i] as i32,
want[i],
"seed {seed} block {k} coeff {i}"
);
}
}
}
}
fn idct_rec_block(pred: &[i32; 16], dct: &[i32; 16]) -> [u8; 16] {
let mut t = [0i32; 16];
for i in 0..4 {
let d = &dct[i * 4..i * 4 + 4];
let (su, de) = (d[0] + d[2], d[0] - d[2]);
let (sd, dd) = (d[1] + (d[3] >> 1), (d[1] >> 1) - d[3]);
t[i * 4] = su + sd;
t[i * 4 + 1] = de + dd;
t[i * 4 + 2] = de - dd;
t[i * 4 + 3] = su - sd;
}
let mut out = [0u8; 16];
for i in 0..4 {
let (sl, dl) = (t[i] + t[8 + i], t[i] - t[8 + i]);
let (dr, sr) = ((t[4 + i] >> 1) - t[12 + i], t[4 + i] + (t[12 + i] >> 1));
let r = [sl + sr, dl + dr, dl - dr, sl - sr];
for k in 0..4 {
out[k * 4 + i] = (pred[k * 4 + i] + ((r[k] + 32) >> 6)).clamp(0, 255) as u8;
}
}
out
}
#[repr(align(16))]
struct Align16<T>(T);
#[test]
fn idct_four_t4_rec_matches_scalar() {
for seed in 0..128usize {
let mut pred = [0u8; 64];
let mut dctw = Align16([0i16; 64]);
for i in 0..64 {
pred[i] = ((i * 7 + seed * 3) & 0xff) as u8;
dctw.0[i] = (((i as i32 * 53 + seed as i32 * 29) % 4096) - 2048) as i16;
}
let dct = &dctw.0;
let mut rec = [0u8; 64];
idct_four_t4_rec(&mut rec, 8, &pred, 8, dct);
for (k, (ox, oy)) in [(0, 0), (4, 0), (0, 4), (4, 4)].iter().enumerate() {
let mut pb = [0i32; 16];
let mut db = [0i32; 16];
for dy in 0..4 {
for dx in 0..4 {
pb[dy * 4 + dx] = pred[(oy + dy) * 8 + ox + dx] as i32;
db[dy * 4 + dx] = dct[k * 16 + dy * 4 + dx] as i32;
}
}
let want = idct_rec_block(&pb, &db);
for dy in 0..4 {
for dx in 0..4 {
assert_eq!(
rec[(oy + dy) * 8 + ox + dx],
want[dy * 4 + dx],
"seed {seed} block {k} ({dx},{dy})"
);
}
}
}
}
}
fn sad_ref(a: &[u8], sa: usize, b: &[u8], sb: usize, w: usize, h: usize) -> i32 {
let mut s = 0i32;
for i in 0..h {
for j in 0..w {
s += (a[i * sa + j] as i32 - b[i * sb + j] as i32).abs();
}
}
s
}
fn satd_region_ref(a: &[u8], sa: usize, b: &[u8], sb: usize, w: usize, h: usize) -> i32 {
let mut s = 0i32;
let mut by = 0;
while by < h {
let mut bx = 0;
while bx < w {
s += satd_ref(&a[by * sa + bx..], sa, &b[by * sb + bx..], sb);
bx += 4;
}
by += 4;
}
s
}
#[test]
fn sad_satd_family_matches_reference() {
let (sa, sb) = (16usize, 16usize);
let mut aw = Align16([0u8; 16 * 16]);
let mut bw = Align16([0u8; 16 * 16]);
for seed in 0..96usize {
for i in 0..16 {
for j in 0..16 {
aw.0[i * sa + j] = ((i * 37 + j * 101 + seed * 3) & 0xff) as u8;
bw.0[i * sb + j] = ((i * 53 + j * 17 + seed * 29 + 7) & 0xff) as u8;
}
}
let (a, b): (&[u8], &[u8]) = (&aw.0, &bw.0);
assert_eq!(sad_16x16(a, sa, b, sb), sad_ref(&a, sa, &b, sb, 16, 16), "sad16x16 {seed}");
assert_eq!(sad_16x8(a, sa, b, sb), sad_ref(&a, sa, &b, sb, 16, 8), "sad16x8 {seed}");
assert_eq!(sad_8x16(a, sa, b, sb), sad_ref(&a, sa, &b, sb, 8, 16), "sad8x16 {seed}");
assert_eq!(satd_8x8(a, sa, b, sb), satd_region_ref(&a, sa, &b, sb, 8, 8), "satd8x8 {seed}");
assert_eq!(satd_16x8(a, sa, b, sb), satd_region_ref(&a, sa, &b, sb, 16, 8), "satd16x8 {seed}");
assert_eq!(satd_8x16(a, sa, b, sb), satd_region_ref(&a, sa, &b, sb, 8, 16), "satd8x16 {seed}");
assert_eq!(satd_16x16(a, sa, b, sb), satd_region_ref(&a, sa, &b, sb, 16, 16), "satd16x16 {seed}");
}
}
#[test]
fn quant_four_matches_openh264_c() {
#[repr(align(16))]
struct A16i([i16; 64]);
let ff: [i16; 8] = [80, 85, 80, 85, 90, 95, 90, 95];
let mf: [i16; 8] = [410, 420, 410, 420, 430, 440, 430, 440];
for seed in 0..64i32 {
let mut input = [0i16; 64];
for (k, v) in input.iter_mut().enumerate() {
*v = (((k as i32 * 37 + seed * 53) % 2000) - 1000) as i16;
}
let mut dctw = A16i(input);
quant_four_4x4(&mut dctw.0, &ff, &mf);
for blk in 0..4 {
for row in 0..4 {
for col in 0..4 {
let idx = blk * 16 + row * 4 + col;
let pos = (row & 1) * 4 + col;
let c = input[idx] as i32;
let lvl = ((c.abs() + ff[pos] as i32) * mf[pos] as i32) >> 16;
let want = (if c < 0 { -lvl } else { lvl }) as i16;
assert_eq!(dctw.0[idx], want, "seed {seed} blk {blk} ({row},{col})");
}
}
}
}
}
#[test]
fn satd_matches_openh264_c_reference() {
for seed in 0..256u32 {
let mut a = [0u8; 64];
let mut b = [0u8; 64];
for i in 0..4 {
for j in 0..4 {
let s = seed as usize;
a[i * 16 + j] = ((i * 37 + j * 101 + s * 3) & 0xff) as u8;
b[i * 16 + j] = ((i * 53 + j * 17 + s * 29 + 7) & 0xff) as u8;
}
}
let got = satd_4x4(&a, 16, &b, 16);
let want = satd_ref(&a, 16, &b, 16);
assert_eq!(got, want, "seed {seed}: asm {got} != openh264-C ref {want}");
}
}
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
unsafe fn bs_motion_masks_avx2(
mvx: &[i16; 16],
mvy: &[i16; 16],
ref_id: &[i32; 16],
no_ref: i32,
) -> (u16, u16) {
use std::arch::x86_64::*;
let vx = _mm256_loadu_si256(mvx.as_ptr() as *const __m256i);
let vy = _mm256_loadu_si256(mvy.as_ptr() as *const __m256i);
let r0 = _mm256_loadu_si256(ref_id.as_ptr() as *const __m256i);
let r1 = _mm256_loadu_si256(ref_id.as_ptr().add(8) as *const __m256i);
let four = _mm256_set1_epi16(4);
let nr = _mm256_set1_epi32(no_ref);
let far = |a: __m256i, b: __m256i| -> __m256i {
let d = _mm256_abs_epi16(_mm256_sub_epi16(a, b));
_mm256_cmpgt_epi16(d, _mm256_sub_epi16(four, _mm256_set1_epi16(1)))
};
let vxl = _mm256_bslli_epi128(vx, 2);
let vyl = _mm256_bslli_epi128(vy, 2);
let farl = _mm256_or_si256(far(vx, vxl), far(vy, vyl));
let r0l = _mm256_bslli_epi128(r0, 4);
let r1l = _mm256_bslli_epi128(r1, 4);
let neq0 = _mm256_xor_si256(_mm256_cmpeq_epi32(r0, r0l), _mm256_set1_epi32(-1));
let neq1 = _mm256_xor_si256(_mm256_cmpeq_epi32(r1, r1l), _mm256_set1_epi32(-1));
let live0 = _mm256_xor_si256(_mm256_cmpeq_epi32(r0, nr), _mm256_set1_epi32(-1));
let live1 = _mm256_xor_si256(_mm256_cmpeq_epi32(r1, nr), _mm256_set1_epi32(-1));
let pack = |a: __m256i, b: __m256i| {
_mm256_permute4x64_epi64(_mm256_packs_epi32(a, b), 0b11_01_10_00)
};
let left = _mm256_or_si256(
pack(neq0, neq1),
_mm256_and_si256(pack(live0, live1), farl),
);
let shift4_i16 = |v: __m256i| {
let lo = _mm256_permute2x128_si256(v, v, 0x08); _mm256_alignr_epi8(v, lo, 8)
};
let vxu = shift4_i16(vx);
let vyu = shift4_i16(vy);
let faru = _mm256_or_si256(far(vx, vxu), far(vy, vyu));
let r0u = _mm256_permute2x128_si256(r0, r0, 0x08); let r1u = _mm256_permute2x128_si256(r0, r1, 0x21); let uneq0 = _mm256_xor_si256(_mm256_cmpeq_epi32(r0, r0u), _mm256_set1_epi32(-1));
let uneq1 = _mm256_xor_si256(_mm256_cmpeq_epi32(r1, r1u), _mm256_set1_epi32(-1));
let up = _mm256_or_si256(
pack(uneq0, uneq1),
_mm256_and_si256(pack(live0, live1), faru),
);
let bits = |v: __m256i| -> u16 {
let m = _mm256_movemask_epi8(v) as u32;
let mut out = 0u16;
let mut k = 0;
while k < 16 {
out |= (((m >> (k * 2)) & 1) as u16) << k;
k += 1;
}
out
};
(bits(left) & 0xEEEE, bits(up) & 0xFFF0)
}
#[cfg(target_arch = "x86_64")]
pub fn bs_motion_masks(
mvx: &[i16; 16],
mvy: &[i16; 16],
ref_id: &[i32; 16],
no_ref: i32,
) -> Option<(u16, u16)> {
if std::is_x86_feature_detected!("avx2") {
Some(unsafe { bs_motion_masks_avx2(mvx, mvy, ref_id, no_ref) })
} else {
None
}
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
unsafe fn bs_motion_masks_two_list_avx2(
mvx: &[i16; 16],
mvy: &[i16; 16],
ref0: &[i32; 16],
mvx1: &[i16; 16],
mvy1: &[i16; 16],
ref1: &[i32; 16],
no_ref: i32,
) -> (u16, u16) {
use std::arch::x86_64::*;
let x0 = _mm256_loadu_si256(mvx.as_ptr() as *const __m256i);
let y0 = _mm256_loadu_si256(mvy.as_ptr() as *const __m256i);
let x1 = _mm256_loadu_si256(mvx1.as_ptr() as *const __m256i);
let y1 = _mm256_loadu_si256(mvy1.as_ptr() as *const __m256i);
let r0lo = _mm256_loadu_si256(ref0.as_ptr() as *const __m256i);
let r0hi = _mm256_loadu_si256(ref0.as_ptr().add(8) as *const __m256i);
let r1lo = _mm256_loadu_si256(ref1.as_ptr() as *const __m256i);
let r1hi = _mm256_loadu_si256(ref1.as_ptr().add(8) as *const __m256i);
let nr = _mm256_set1_epi32(no_ref);
let ones = _mm256_set1_epi32(-1);
let pack = |a: __m256i, b: __m256i| {
_mm256_permute4x64_epi64(_mm256_packs_epi32(a, b), 0b11_01_10_00)
};
let live0 = pack(
_mm256_xor_si256(_mm256_cmpeq_epi32(r0lo, nr), ones),
_mm256_xor_si256(_mm256_cmpeq_epi32(r0hi, nr), ones),
);
let live1 = pack(
_mm256_xor_si256(_mm256_cmpeq_epi32(r1lo, nr), ones),
_mm256_xor_si256(_mm256_cmpeq_epi32(r1hi, nr), ones),
);
let x0 = _mm256_and_si256(x0, live0);
let y0 = _mm256_and_si256(y0, live0);
let x1 = _mm256_and_si256(x1, live1);
let y1 = _mm256_and_si256(y1, live1);
let three = _mm256_set1_epi16(3);
let far = |a: __m256i, b: __m256i| -> __m256i {
let d = _mm256_abs_epi16(_mm256_sub_epi16(a, b));
_mm256_cmpgt_epi16(d, three)
};
let bits = |v: __m256i| -> u16 {
let m = _mm256_movemask_epi8(v) as u32;
let mut out = 0u16;
for k in 0..16 {
out |= (((m >> (2 * k)) & 1) as u16) << k;
}
out
};
let differs = |x0s: __m256i,
y0s: __m256i,
x1s: __m256i,
y1s: __m256i,
e0: __m256i,
e1: __m256i,
c0: __m256i,
c1: __m256i|
-> __m256i {
let far_s = _mm256_or_si256(
_mm256_or_si256(far(x0, x0s), far(y0, y0s)),
_mm256_or_si256(far(x1, x1s), far(y1, y1s)),
);
let far_x = _mm256_or_si256(
_mm256_or_si256(far(x0, x1s), far(y0, y1s)),
_mm256_or_si256(far(x1, x0s), far(y1, y0s)),
);
let ok_s = _mm256_andnot_si256(far_s, _mm256_and_si256(e0, e1));
let ok_x = _mm256_andnot_si256(far_x, _mm256_and_si256(c0, c1));
_mm256_xor_si256(_mm256_or_si256(ok_s, ok_x), _mm256_set1_epi16(-1))
};
let sh16 = |v: __m256i| _mm256_bslli_epi128(v, 2);
let sh32 = |v: __m256i| _mm256_bslli_epi128(v, 4);
let e0l = pack(
_mm256_cmpeq_epi32(r0lo, sh32(r0lo)),
_mm256_cmpeq_epi32(r0hi, sh32(r0hi)),
);
let e1l = pack(
_mm256_cmpeq_epi32(r1lo, sh32(r1lo)),
_mm256_cmpeq_epi32(r1hi, sh32(r1hi)),
);
let c0l = pack(
_mm256_cmpeq_epi32(r0lo, sh32(r1lo)),
_mm256_cmpeq_epi32(r0hi, sh32(r1hi)),
);
let c1l = pack(
_mm256_cmpeq_epi32(r1lo, sh32(r0lo)),
_mm256_cmpeq_epi32(r1hi, sh32(r0hi)),
);
let left = differs(sh16(x0), sh16(y0), sh16(x1), sh16(y1), e0l, e1l, c0l, c1l);
let shu16 = |v: __m256i| {
let lo = _mm256_permute2x128_si256(v, v, 0x08);
_mm256_alignr_epi8(v, lo, 8)
};
let shu32 = |lo: __m256i, hi: __m256i| -> (__m256i, __m256i) {
(
_mm256_permute2x128_si256(lo, lo, 0x08),
_mm256_permute2x128_si256(lo, hi, 0x21),
)
};
let (r0ulo, r0uhi) = shu32(r0lo, r0hi);
let (r1ulo, r1uhi) = shu32(r1lo, r1hi);
let e0u = pack(_mm256_cmpeq_epi32(r0lo, r0ulo), _mm256_cmpeq_epi32(r0hi, r0uhi));
let e1u = pack(_mm256_cmpeq_epi32(r1lo, r1ulo), _mm256_cmpeq_epi32(r1hi, r1uhi));
let c0u = pack(_mm256_cmpeq_epi32(r0lo, r1ulo), _mm256_cmpeq_epi32(r0hi, r1uhi));
let c1u = pack(_mm256_cmpeq_epi32(r1lo, r0ulo), _mm256_cmpeq_epi32(r1hi, r0uhi));
let up = differs(shu16(x0), shu16(y0), shu16(x1), shu16(y1), e0u, e1u, c0u, c1u);
(bits(left) & 0xEEEE, bits(up) & 0xFFF0)
}
#[cfg(target_arch = "x86_64")]
pub fn bs_motion_masks_two_list(
mvx: &[i16; 16],
mvy: &[i16; 16],
ref0: &[i32; 16],
mvx1: &[i16; 16],
mvy1: &[i16; 16],
ref1: &[i32; 16],
no_ref: i32,
) -> Option<(u16, u16)> {
if !std::arch::is_x86_feature_detected!("avx2") {
return None;
}
Some(unsafe { bs_motion_masks_two_list_avx2(mvx, mvy, ref0, mvx1, mvy1, ref1, no_ref) })
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
unsafe fn mb_uniform_avx2(
mvx: &[i16; 16],
mvy: &[i16; 16],
ref_id: &[i32; 16],
mvx1: &[i16; 16],
mvy1: &[i16; 16],
ref1: &[i32; 16],
) -> bool {
use std::arch::x86_64::*;
let eq16 = |v: &[i16; 16]| -> __m256i {
let a = _mm256_loadu_si256(v.as_ptr() as *const __m256i);
_mm256_cmpeq_epi16(a, _mm256_set1_epi16(v[0]))
};
let eq32 = |v: &[i32; 16]| -> __m256i {
let b = _mm256_set1_epi32(v[0]);
let lo = _mm256_cmpeq_epi32(_mm256_loadu_si256(v.as_ptr() as *const __m256i), b);
let hi = _mm256_cmpeq_epi32(_mm256_loadu_si256(v.as_ptr().add(8) as *const __m256i), b);
_mm256_permute4x64_epi64(_mm256_packs_epi32(lo, hi), 0b11_01_10_00)
};
let all = _mm256_and_si256(
_mm256_and_si256(_mm256_and_si256(eq16(mvx), eq16(mvy)), eq32(ref_id)),
_mm256_and_si256(_mm256_and_si256(eq16(mvx1), eq16(mvy1)), eq32(ref1)),
);
_mm256_movemask_epi8(all) == -1
}
#[cfg(target_arch = "x86_64")]
#[allow(clippy::too_many_arguments)]
pub fn mb_uniform(
mvx: &[i16; 16],
mvy: &[i16; 16],
ref_id: &[i32; 16],
mvx1: &[i16; 16],
mvy1: &[i16; 16],
ref1: &[i32; 16],
) -> Option<bool> {
if std::is_x86_feature_detected!("avx2") {
Some(unsafe { mb_uniform_avx2(mvx, mvy, ref_id, mvx1, mvy1, ref1) })
} else {
None
}
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
unsafe fn dequant_4x4_avx2(out: &mut [i32; 16], levels: &[i32; 16], ls: &[i32; 16], qp: u8) {
use std::arch::x86_64::*;
let l0 = _mm256_loadu_si256(levels.as_ptr() as *const __m256i);
let l1 = _mm256_loadu_si256(levels.as_ptr().add(8) as *const __m256i);
let s0 = _mm256_loadu_si256(ls.as_ptr() as *const __m256i);
let s1 = _mm256_loadu_si256(ls.as_ptr().add(8) as *const __m256i);
let p0 = _mm256_mullo_epi32(l0, s0);
let p1 = _mm256_mullo_epi32(l1, s1);
let shift = (qp / 6) as i32;
let (r0, r1) = if qp >= 24 {
let c = _mm_cvtsi32_si128(shift - 4);
(_mm256_sll_epi32(p0, c), _mm256_sll_epi32(p1, c))
} else {
let add = _mm256_set1_epi32(1 << (3 - shift));
let c = _mm_cvtsi32_si128(4 - shift);
(
_mm256_sra_epi32(_mm256_add_epi32(p0, add), c),
_mm256_sra_epi32(_mm256_add_epi32(p1, add), c),
)
};
_mm256_storeu_si256(out.as_mut_ptr() as *mut __m256i, r0);
_mm256_storeu_si256(out.as_mut_ptr().add(8) as *mut __m256i, r1);
}
#[cfg(target_arch = "x86_64")]
pub fn dequant_4x4(out: &mut [i32; 16], levels: &[i32; 16], ls: &[i32; 16], qp: u8) -> bool {
if std::is_x86_feature_detected!("avx2") {
unsafe { dequant_4x4_avx2(out, levels, ls, qp) };
true
} else {
false
}
}