use std::arch::x86_64::*;
#[inline]
#[target_feature(enable = "avx")]
fn hsum256(v: __m256) -> f32 {
let lo = _mm256_castps256_ps128(v);
let hi = _mm256_extractf128_ps::<1>(v);
let s = _mm_add_ps(lo, hi);
let shuf = _mm_movehdup_ps(s);
let sums = _mm_add_ps(s, shuf);
let shuf2 = _mm_movehl_ps(shuf, sums);
let sums = _mm_add_ss(sums, shuf2);
_mm_cvtss_f32(sums)
}
#[allow(clippy::too_many_arguments)]
#[target_feature(enable = "avx2")]
pub(crate) fn sse_and_rate_avx2(
coeff: &[f32],
inv_matrix: &[f32],
q_scaled: f32,
width: usize,
height: usize,
half: usize,
cx: usize,
cy: usize,
thr: &[f32; 4],
) -> (f32, usize, f32) {
let n = width * height;
assert!(coeff.len() >= n && inv_matrix.len() >= n);
debug_assert!(width.is_multiple_of(8));
let qs = _mm256_set1_ps(q_scaled);
let sign = _mm256_set1_ps(-0.0);
let mut sse_acc = _mm256_setzero_ps();
let mut nzeros = 0usize;
let mut mag_bits = 0.0f32;
const RND: i32 = _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC;
let lut = crate::enc_ac_strategy::rate_log2_lut();
let mut qbuf = [0.0f32; 8];
for y in 0..height {
let yfix = if y >= height / 2 { 2 } else { 0 };
let thr_lo = thr[yfix];
let thr_hi = thr[yfix + 1];
let row = y * width;
let mut x = 0;
while x < width {
let thr_v = if x + 8 <= half {
_mm256_set1_ps(thr_lo)
} else if x >= half {
_mm256_set1_ps(thr_hi)
} else {
let mut t = [0.0f32; 8];
for (i, ti) in t.iter_mut().enumerate() {
*ti = if x + i >= half { thr_hi } else { thr_lo };
}
unsafe { _mm256_loadu_ps(t.as_ptr()) }
};
let idx = row + x;
let cv = unsafe { _mm256_loadu_ps(coeff[idx..idx + 8].as_ptr()) };
let mv = unsafe { _mm256_loadu_ps(inv_matrix[idx..idx + 8].as_ptr()) };
let a = _mm256_mul_ps(_mm256_mul_ps(mv, qs), cv);
let absa = _mm256_andnot_ps(sign, a);
let keep = _mm256_cmp_ps::<_CMP_GE_OQ>(absa, thr_v);
let rounded = _mm256_round_ps::<RND>(a);
let q = _mm256_and_ps(rounded, keep); let d = _mm256_sub_ps(a, q);
let mut d2 = _mm256_mul_ps(d, d);
if y < cy && x == 0 {
let mut lanemask = [1.0f32; 8];
for lm in lanemask.iter_mut().take(cx.min(8)) {
*lm = 0.0;
}
let lmv = unsafe { _mm256_loadu_ps(lanemask.as_ptr()) };
d2 = _mm256_mul_ps(d2, lmv);
}
sse_acc = _mm256_add_ps(sse_acc, d2);
unsafe {
_mm256_storeu_ps(qbuf.as_mut_ptr(), q);
}
for (lane, &qv) in qbuf.iter().enumerate() {
if y < cy && x == 0 && lane < cx {
continue; }
if qv != 0.0 {
nzeros += 1;
let qa = qv.abs();
let k = qa as usize;
mag_bits += if k < lut.len() {
lut[k]
} else {
(1.0 + qa).log2()
};
}
}
x += 8;
}
}
(hsum256(sse_acc), nzeros, mag_bits)
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(clippy::too_many_arguments)]
fn reference(
coeff: &[f32],
inv: &[f32],
qs: f32,
w: usize,
h: usize,
half: usize,
cx: usize,
cy: usize,
thr: &[f32; 4],
) -> (f32, usize, f32) {
let (mut sse, mut nz, mut mb) = (0.0f32, 0usize, 0.0f32);
for y in 0..h {
let yfix = if y >= h / 2 { 2 } else { 0 };
let (lo, hi) = (thr[yfix], thr[yfix + 1]);
for x in 0..w {
if x < cx && y < cy {
continue;
}
let idx = y * w + x;
let t = if x >= half { hi } else { lo };
let a = (inv[idx] * qs) * coeff[idx];
let q = if a.abs() >= t {
a.round_ties_even()
} else {
0.0
};
let d = a - q;
sse += d * d;
if q != 0.0 {
nz += 1;
mb += crate::enc_ac_strategy::rate_log2(q.abs());
}
}
}
(sse, nz, mb)
}
#[test]
fn test_sse_and_rate_avx2_vs_reference() {
if !std::is_x86_feature_detected!("avx") {
return;
}
let mut st: u64 = 0x5e5e_a11d_0f00_d00d;
let mut rnd = || {
st = st
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
((st >> 33) as u32 as f32) / (u32::MAX as f32)
};
for &(w, h, half, cx, cy) in &[
(8usize, 8usize, 4usize, 1usize, 1usize),
(16, 16, 8, 2, 2),
(32, 32, 16, 4, 4),
(16, 8, 8, 2, 1),
(8, 16, 4, 1, 2),
] {
for _ in 0..200 {
let n = w * h;
let coeff: Vec<f32> = (0..n).map(|_| (rnd() - 0.5) * 200.0).collect();
let inv: Vec<f32> = (0..n).map(|_| 0.001 + rnd() * 0.5).collect();
let qs = 0.5 + rnd() * 3.0;
let thr = [rnd() * 0.6, rnd() * 0.6, rnd() * 0.6, rnd() * 0.6];
let r = reference(&coeff, &inv, qs, w, h, half, cx, cy, &thr);
let a = unsafe { sse_and_rate_avx2(&coeff, &inv, qs, w, h, half, cx, cy, &thr) };
assert_eq!(a.1, r.1, "nzeros mismatch {w}x{h}");
assert_eq!(a.2, r.2, "mag_bits mismatch {w}x{h}");
let rel = if r.0.abs() > 1e-3 {
((a.0 - r.0) / r.0).abs()
} else {
(a.0 - r.0).abs()
};
assert!(rel < 1e-4, "sse rel diff {rel} too large {w}x{h}");
}
}
}
}