pub mod avx2_color;
pub mod avx2_color_encode;
pub mod avx2_fdct;
pub mod avx2_idct;
pub mod avx2_merged;
pub mod avx2_upsample;
pub mod color;
pub mod idct;
pub mod upsample;
use crate::simd::{EncoderSimdRoutines, QuantDivisors, SimdRoutines};
pub fn routines() -> SimdRoutines {
if is_x86_feature_detected!("avx2") {
return SimdRoutines {
idct_islow: avx2_idct::avx2_idct_islow,
ycbcr_to_rgb_row: avx2_color::avx2_ycbcr_to_rgb_row,
fancy_upsample_h2v1: avx2_upsample::avx2_fancy_upsample_h2v1,
};
}
if is_x86_feature_detected!("sse2") {
return SimdRoutines {
idct_islow: idct::sse2_idct_islow,
ycbcr_to_rgb_row: color::sse2_ycbcr_to_rgb_row,
fancy_upsample_h2v1: upsample::sse2_fancy_upsample_h2v1,
};
}
crate::simd::scalar::routines()
}
pub fn encoder_routines() -> EncoderSimdRoutines {
if is_x86_feature_detected!("avx2") {
return EncoderSimdRoutines {
rgb_to_ycbcr_row: avx2_color_encode::avx2_rgb_to_ycbcr_row,
fdct_quantize: avx2_fdct_quantize,
};
}
crate::simd::scalar::encoder_routines()
}
fn avx2_fdct_quantize(input: &mut [i16; 64], quant: &QuantDivisors, output: &mut [i16; 64]) {
avx2_fdct::avx2_fdct_islow(input);
unsafe { avx2_quantize_zigzag(input, quant, output) }
}
#[target_feature(enable = "avx2")]
pub(crate) unsafe fn avx2_extract_fdct_quantize(
plane_ptr: *const u8,
stride: usize,
quant: &QuantDivisors,
output: &mut [i16; 64],
) {
use core::arch::x86_64::*;
let zeros: __m128i = _mm_setzero_si128();
let level_shift: __m128i = _mm_set1_epi16(128);
macro_rules! load_row {
($row:expr) => {{
let ptr: *const u8 = plane_ptr.add(stride * $row);
let pixels: __m128i = _mm_loadl_epi64(ptr as *const __m128i);
_mm_sub_epi16(_mm_unpacklo_epi8(pixels, zeros), level_shift)
}};
}
let r0: __m128i = load_row!(0);
let r1: __m128i = load_row!(1);
let r2: __m128i = load_row!(2);
let r3: __m128i = load_row!(3);
let r4: __m128i = load_row!(4);
let r5: __m128i = load_row!(5);
let r6: __m128i = load_row!(6);
let r7: __m128i = load_row!(7);
let ymm01: __m256i = _mm256_inserti128_si256(_mm256_castsi128_si256(r0), r1, 1);
let ymm23: __m256i = _mm256_inserti128_si256(_mm256_castsi128_si256(r2), r3, 1);
let ymm45: __m256i = _mm256_inserti128_si256(_mm256_castsi128_si256(r4), r5, 1);
let ymm67: __m256i = _mm256_inserti128_si256(_mm256_castsi128_si256(r6), r7, 1);
let (out0, out1, out2, out3) = avx2_fdct::avx2_fdct_core(ymm01, ymm23, ymm45, ymm67);
let mut dct_buf = [0i16; 64];
_mm256_storeu_si256(dct_buf.as_mut_ptr() as *mut __m256i, out0);
_mm256_storeu_si256(dct_buf.as_mut_ptr().add(16) as *mut __m256i, out1);
_mm256_storeu_si256(dct_buf.as_mut_ptr().add(32) as *mut __m256i, out2);
_mm256_storeu_si256(dct_buf.as_mut_ptr().add(48) as *mut __m256i, out3);
avx2_quantize_zigzag(&dct_buf, quant, output);
}
#[target_feature(enable = "avx2")]
pub(crate) unsafe fn avx2_downsample_h2v2_fdct_quantize(
plane_ptr: *const u8,
stride: usize,
quant: &QuantDivisors,
output: &mut [i16; 64],
) {
use core::arch::x86_64::*;
let ones: __m128i = _mm_set1_epi8(1);
let bias: __m128i = _mm_set1_epi16(2); let level_shift: __m128i = _mm_set1_epi16(128);
macro_rules! downsample_row_pair {
($row:expr) => {{
let sy: usize = $row * 2;
let r0: __m128i = _mm_loadu_si128(plane_ptr.add(sy * stride) as *const __m128i);
let r1: __m128i = _mm_loadu_si128(plane_ptr.add((sy + 1) * stride) as *const __m128i);
let sum0: __m128i = _mm_maddubs_epi16(r0, ones);
let sum1: __m128i = _mm_maddubs_epi16(r1, ones);
let total: __m128i = _mm_add_epi16(_mm_add_epi16(sum0, sum1), bias);
let avg: __m128i = _mm_srai_epi16::<2>(total);
_mm_sub_epi16(avg, level_shift)
}};
}
let d0: __m128i = downsample_row_pair!(0);
let d1: __m128i = downsample_row_pair!(1);
let d2: __m128i = downsample_row_pair!(2);
let d3: __m128i = downsample_row_pair!(3);
let d4: __m128i = downsample_row_pair!(4);
let d5: __m128i = downsample_row_pair!(5);
let d6: __m128i = downsample_row_pair!(6);
let d7: __m128i = downsample_row_pair!(7);
let ymm01: __m256i = _mm256_inserti128_si256(_mm256_castsi128_si256(d0), d1, 1);
let ymm23: __m256i = _mm256_inserti128_si256(_mm256_castsi128_si256(d2), d3, 1);
let ymm45: __m256i = _mm256_inserti128_si256(_mm256_castsi128_si256(d4), d5, 1);
let ymm67: __m256i = _mm256_inserti128_si256(_mm256_castsi128_si256(d6), d7, 1);
let (out0, out1, out2, out3) = avx2_fdct::avx2_fdct_core(ymm01, ymm23, ymm45, ymm67);
let mut dct_buf = [0i16; 64];
_mm256_storeu_si256(dct_buf.as_mut_ptr() as *mut __m256i, out0);
_mm256_storeu_si256(dct_buf.as_mut_ptr().add(16) as *mut __m256i, out1);
_mm256_storeu_si256(dct_buf.as_mut_ptr().add(32) as *mut __m256i, out2);
_mm256_storeu_si256(dct_buf.as_mut_ptr().add(48) as *mut __m256i, out3);
avx2_quantize_zigzag(&dct_buf, quant, output);
}
#[target_feature(enable = "avx2")]
unsafe fn avx2_quantize_zigzag(coeffs: &[i16; 64], quant: &QuantDivisors, output: &mut [i16; 64]) {
use core::arch::x86_64::*;
let zigzag = &crate::encode::tables::ZIGZAG_ORDER;
for i in (0..64).step_by(16) {
let mut coeff_buf = [0i16; 16];
for j in 0..16 {
coeff_buf[j] = *coeffs.get_unchecked(zigzag[i + j]);
}
let c = _mm256_loadu_si256(coeff_buf.as_ptr() as *const __m256i);
let d = _mm256_loadu_si256(quant.divisors_zigzag.as_ptr().add(i) as *const __m256i);
let r = _mm256_loadu_si256(quant.reciprocals_zigzag.as_ptr().add(i) as *const __m256i);
let sign = _mm256_srai_epi16::<15>(c);
let abs_c = _mm256_abs_epi16(c);
let half_d = _mm256_srli_epi16::<1>(d);
let rounded = _mm256_add_epi16(abs_c, half_d);
let quantized = _mm256_mulhi_epu16(rounded, r);
let result = _mm256_sub_epi16(_mm256_xor_si256(quantized, sign), sign);
_mm256_storeu_si256(output.as_mut_ptr().add(i) as *mut __m256i, result);
}
}