#[cfg(target_arch = "x86")]
use std::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
#[inline]
#[target_feature(enable = "sse4.1")]
fn hsum(v: __m128) -> f32 {
let shuf = _mm_movehdup_ps(v); let sums = _mm_add_ps(v, 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 = "sse4.1")]
pub(crate) fn sse_and_rate_sse(
coeff: &[f32],
inv_matrix: &[f32],
q_scaled: f32,
width: usize,
height: usize,
half: usize,
cx: usize,
cy: usize,
_rate_log2_lut: &crate::enc_ac_strategy::RateLog2Lut,
thr: &[f32; 4],
) -> (f32, usize, f32) {
let n = width * height;
assert!(coeff.len() >= n && inv_matrix.len() >= n);
debug_assert!(width.is_multiple_of(4) && half.is_multiple_of(4));
let qs = _mm_set1_ps(q_scaled);
let mut sse_acc = _mm_setzero_ps();
let mut mag_acc = _mm_setzero_ps();
let mut nzeros = 0usize;
let zero = _mm_setzero_ps();
let sign_mask = _mm_set1_ps(-0.0);
let lane_ids = _mm_setr_epi32(0, 1, 2, 3);
let izero = _mm_setzero_si128();
let all_active_i = _mm_cmpeq_epi32(izero, izero);
const RND: i32 = _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC;
for (y, (coeffs, inv_matrix)) in coeff
.chunks_exact(width)
.zip(inv_matrix.chunks_exact(width))
.take(height)
.enumerate()
{
let yfix = if y >= height / 2 { 2 } else { 0 };
let thr_lo = thr[yfix];
let thr_hi = thr[yfix + 1];
for (x0, (coeff4, inv4)) in coeffs
.as_chunks::<4>()
.0
.iter()
.zip(inv_matrix.as_chunks::<4>().0.iter())
.enumerate()
{
let x = x0 * 4;
let threshold = if x >= half { thr_hi } else { thr_lo };
let thrv = _mm_set1_ps(threshold);
let cv = unsafe { _mm_loadu_ps(coeff4.as_ptr()) };
let mv = unsafe { _mm_loadu_ps(inv4.as_ptr()) };
let a = _mm_mul_ps(_mm_mul_ps(mv, qs), cv);
let absa = _mm_andnot_ps(sign_mask, a);
let keep = _mm_cmpge_ps(absa, thrv);
let rounded = _mm_round_ps::<RND>(a);
let q = _mm_and_ps(rounded, keep);
let d = _mm_sub_ps(a, q);
let d2 = _mm_mul_ps(d, d);
let active_i = if y < cy && x < cx {
let lane_x = _mm_add_epi32(_mm_set1_epi32(x as i32), lane_ids);
let cxv = _mm_set1_epi32(cx as i32);
let llf = _mm_cmpgt_epi32(cxv, lane_x);
_mm_andnot_si128(llf, all_active_i)
} else {
all_active_i
};
let active = _mm_castsi128_ps(active_i);
let d2 = _mm_and_ps(d2, active);
sse_acc = _mm_add_ps(sse_acc, d2);
let absq = _mm_andnot_ps(sign_mask, q);
let nz = _mm_cmpgt_ps(absq, zero);
let rate_mask = _mm_and_ps(nz, active);
nzeros += _mm_movemask_ps(rate_mask).count_ones() as usize;
let ratev = sse_log2p1_f32(absq);
mag_acc = _mm_add_ps(mag_acc, _mm_and_ps(ratev, rate_mask));
}
}
(hsum(sse_acc), nzeros, hsum(mag_acc))
}
#[inline]
#[target_feature(enable = "sse4.1")]
fn sse_log2p1_f32(x: __m128) -> __m128 {
let y = _mm_add_ps(x, _mm_set1_ps(1.0));
let bits = _mm_castps_si128(y);
let exp_u = _mm_srli_epi32(bits, 23);
let exp_i = _mm_sub_epi32(exp_u, _mm_set1_epi32(127));
let e = _mm_cvtepi32_ps(exp_i);
let mant_bits = _mm_or_si128(
_mm_and_si128(bits, _mm_set1_epi32(0x007f_ffff)),
_mm_set1_epi32(0x3f80_0000),
);
let m = _mm_castsi128_ps(mant_bits);
let one = _mm_set1_ps(1.0);
let s = _mm_div_ps(_mm_sub_ps(m, one), _mm_add_ps(m, one));
let t = _mm_sub_ps(m, one);
let c0 = _mm_set1_ps(1.4426934719085693359375);
let c1 = _mm_set1_ps(-0.721179187297821044921875);
let c2 = _mm_set1_ps(0.477900087833404541015625);
let c3 = _mm_set1_ps(-0.340080082416534423828125);
let c4 = _mm_set1_ps(0.21719777584075927734375);
let c5 = _mm_set1_ps(-9.749893844127655029296875e-2);
let c6 = _mm_set1_ps(2.096841670572757720947265625e-2);
let mut p = c6;
p = _mm_add_ps(c5, _mm_mul_ps(t, p));
p = _mm_add_ps(c4, _mm_mul_ps(t, p));
p = _mm_add_ps(c3, _mm_mul_ps(t, p));
p = _mm_add_ps(c2, _mm_mul_ps(t, p));
p = _mm_add_ps(c1, _mm_mul_ps(t, p));
p = _mm_add_ps(c0, _mm_mul_ps(t, p));
let log2_m = _mm_mul_ps(s, p);
_mm_add_ps(e, log2_m)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_log2p1() {
if !std::is_x86_feature_detected!("sse4.1") {
return;
}
unsafe {
for i in 0..5000 {
let q1 = ((i as f32 / 5000.) + 1.).log2();
let q2 = f32::from_bits(_mm_extract_ps::<0>(sse_log2p1_f32(_mm_set1_ps(
i as f32 / 5000.,
))) as u32);
assert!((q1 - q2).abs() < 1e-5, "q1 {} q2 {}", q1, q2);
}
}
}
}