use core::arch::aarch64::*;
use crate::reconstruct::{
add_residual_into_scalar, add_residual_into_scalar16, can_reconstruct_full_block, sample_max,
};
#[inline]
fn supported_n(n: usize) -> bool {
matches!(n, 2 | 4 | 8 | 16 | 32)
}
#[inline]
#[target_feature(enable = "neon")]
fn load_u16x2(src: &[u16]) -> uint16x8_t {
debug_assert!(src.len() >= 2);
unsafe {
let lane = vld1q_lane_u32::<0>(src.as_ptr().cast(), vdupq_n_u32(0));
vreinterpretq_u16_u32(lane)
}
}
#[inline]
#[target_feature(enable = "neon")]
fn load_u16x4(src: &[u16]) -> uint16x8_t {
debug_assert!(src.len() >= 4);
unsafe { vcombine_u16(vld1_u16(src.as_ptr()), vdup_n_u16(0)) }
}
#[inline]
#[target_feature(enable = "neon")]
fn load_u16x8(src: &[u16]) -> uint16x8_t {
debug_assert!(src.len() >= 8);
unsafe { vld1q_u16(src.as_ptr()) }
}
#[inline]
#[target_feature(enable = "neon")]
fn load_i32x2(src: &[i32]) -> int32x4_t {
debug_assert!(src.len() >= 2);
unsafe { vcombine_s32(vld1_s32(src.as_ptr()), vdup_n_s32(0)) }
}
#[inline]
#[target_feature(enable = "neon")]
fn load_i32x4(src: &[i32]) -> int32x4_t {
debug_assert!(src.len() >= 4);
unsafe { vld1q_s32(src.as_ptr()) }
}
#[inline]
#[target_feature(enable = "neon")]
fn store_u16x2(dst: &mut [u16], v: uint16x8_t) {
debug_assert!(dst.len() >= 2);
unsafe {
vst1q_lane_u32::<0>(dst.as_mut_ptr().cast(), vreinterpretq_u32_u16(v));
}
}
#[inline]
#[target_feature(enable = "neon")]
fn store_u16x4(dst: &mut [u16], v: uint16x8_t) {
debug_assert!(dst.len() >= 4);
unsafe { vst1_u16(dst.as_mut_ptr(), vget_low_u16(v)) }
}
#[inline]
#[target_feature(enable = "neon")]
fn store_u16x8(dst: &mut [u16], v: uint16x8_t) {
debug_assert!(dst.len() >= 8);
unsafe { vst1q_u16(dst.as_mut_ptr(), v) }
}
#[inline]
#[target_feature(enable = "neon")]
fn widen_lo_u16x4(v: uint16x8_t) -> int32x4_t {
vreinterpretq_s32_u32(vmovl_u16(vget_low_u16(v)))
}
#[inline]
#[target_feature(enable = "neon")]
fn widen_hi_u16x4(v: uint16x8_t) -> int32x4_t {
vreinterpretq_s32_u32(vmovl_u16(vget_high_u16(v)))
}
#[inline]
#[target_feature(enable = "neon")]
fn pack_u16x8(lo: int32x4_t, hi: int32x4_t) -> uint16x8_t {
vcombine_u16(
vqmovn_u32(vreinterpretq_u32_s32(lo)),
vqmovn_u32(vreinterpretq_u32_s32(hi)),
)
}
#[inline]
#[target_feature(enable = "neon")]
fn add_clip4_neon(pred: int32x4_t, res: int32x4_t, zero: int32x4_t, max: int32x4_t) -> int32x4_t {
let sum = vaddq_s32(pred, res);
vminq_s32(vmaxq_s32(sum, zero), max)
}
#[inline]
#[target_feature(enable = "neon")]
fn add_clip8_neon(pred: uint16x8_t, res: &[i32], zero: int32x4_t, max: int32x4_t) -> uint16x8_t {
let lo = add_clip4_neon(widen_lo_u16x4(pred), load_i32x4(res), zero, max);
let hi = add_clip4_neon(widen_hi_u16x4(pred), load_i32x4(&res[4..]), zero, max);
pack_u16x8(lo, hi)
}
#[inline]
#[target_feature(enable = "neon")]
fn add_clip_row_neon(dst: &mut [u16], pred: &[u16], res: &[i32], n: usize, max: int32x4_t) {
let zero = vdupq_n_s32(0);
if n == 2 {
let pred = widen_lo_u16x4(load_u16x2(pred));
let out = add_clip4_neon(pred, load_i32x2(res), zero, max);
store_u16x2(dst, pack_u16x8(out, zero));
return;
}
if n == 4 {
let pred = widen_lo_u16x4(load_u16x4(pred));
let out = add_clip4_neon(pred, load_i32x4(res), zero, max);
store_u16x4(dst, pack_u16x8(out, zero));
return;
}
let mut x = 0usize;
while x < n {
let pred = load_u16x8(&pred[x..]);
let out = add_clip8_neon(pred, &res[x..], zero, max);
store_u16x8(&mut dst[x..], out);
x += 8;
}
}
#[inline]
#[target_feature(enable = "neon")]
fn add_residual_into_neon_impl(
dst: &mut [u16],
stride: usize,
pred: &[u16],
res: &[i32],
n: usize,
bit_depth: u8,
) {
debug_assert!(supported_n(n));
let Some(n2) = n.checked_mul(n) else {
return;
};
let Some(pred) = pred.get(..n2) else {
return;
};
let Some(res) = res.get(..n2) else {
return;
};
let max = vdupq_n_s32(sample_max(bit_depth));
for y in 0..n {
let row_off = y * n;
let dst_off = y * stride;
let Some(dst_row) = dst.get_mut(dst_off..dst_off.saturating_add(n)) else {
break;
};
let Some(pred_row) = pred.get(row_off..row_off + n) else {
break;
};
let Some(res_row) = res.get(row_off..row_off + n) else {
break;
};
add_clip_row_neon(dst_row, pred_row, res_row, n, max);
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn add_residual_into_neon(
dst: &mut [u16],
stride: usize,
pred: &[u16],
res: &[i32],
n: usize,
valid_w: usize,
valid_h: usize,
bit_depth: u8,
) {
if !supported_n(n)
|| !can_reconstruct_full_block(dst, stride, pred, res, n, valid_w, valid_h, bit_depth)
{
add_residual_into_scalar(dst, stride, pred, res, n, valid_w, valid_h, bit_depth);
return;
}
unsafe { add_residual_into_neon_impl(dst, stride, pred, res, n, bit_depth) }
}
#[inline]
#[target_feature(enable = "neon")]
fn load_i16x2(src: &[i16]) -> int16x4_t {
debug_assert!(src.len() >= 2);
unsafe {
let v = vld1_lane_s16::<0>(src.as_ptr(), vdup_n_s16(0));
vld1_lane_s16::<1>(src.as_ptr().add(1), v)
}
}
#[inline]
#[target_feature(enable = "neon")]
fn add_clip_i16q(pred: int16x8_t, res: int16x8_t, zero: int16x8_t, max: int16x8_t) -> int16x8_t {
vminq_s16(vmaxq_s16(vqaddq_s16(pred, res), zero), max)
}
#[inline]
#[target_feature(enable = "neon")]
fn add_clip_i16(pred: int16x4_t, res: int16x4_t, max: int16x4_t) -> int16x4_t {
vmin_s16(vmax_s16(vqadd_s16(pred, res), vdup_n_s16(0)), max)
}
#[inline]
#[target_feature(enable = "neon")]
fn add_clip_row_neon_16(dst: &mut [u16], pred: &[u16], res: &[i16], n: usize, max: int16x8_t) {
let zero = vdupq_n_s16(0);
if n == 2 {
let p = vget_low_s16(vreinterpretq_s16_u16(load_u16x2(pred)));
let s = add_clip_i16(p, load_i16x2(res), vget_low_s16(max));
store_u16x2(dst, vreinterpretq_u16_s16(vcombine_s16(s, vdup_n_s16(0))));
return;
}
if n == 4 {
let p = vget_low_s16(vreinterpretq_s16_u16(load_u16x4(pred)));
let s = add_clip_i16(p, unsafe { vld1_s16(res.as_ptr()) }, vget_low_s16(max));
unsafe { vst1_u16(dst.as_mut_ptr(), vreinterpret_u16_s16(s)) };
return;
}
let mut x = 0usize;
while x < n {
let p = vreinterpretq_s16_u16(load_u16x8(&pred[x..]));
let r = unsafe { vld1q_s16(res[x..].as_ptr()) };
let s = add_clip_i16q(p, r, zero, max);
store_u16x8(&mut dst[x..], vreinterpretq_u16_s16(s));
x += 8;
}
}
#[inline]
#[target_feature(enable = "neon")]
fn add_residual_into_neon_impl_16(
dst: &mut [u16],
stride: usize,
pred: &[u16],
res: &[i16],
n: usize,
bit_depth: u8,
) {
debug_assert!(supported_n(n));
let Some(n2) = n.checked_mul(n) else {
return;
};
let Some(pred) = pred.get(..n2) else {
return;
};
let Some(res) = res.get(..n2) else {
return;
};
let max = vdupq_n_s16(sample_max(bit_depth) as i16);
for y in 0..n {
let row_off = y * n;
let dst_off = y * stride;
let Some(dst_row) = dst.get_mut(dst_off..dst_off.saturating_add(n)) else {
break;
};
let (Some(pred_row), Some(res_row)) = (
pred.get(row_off..row_off + n),
res.get(row_off..row_off + n),
) else {
break;
};
add_clip_row_neon_16(dst_row, pred_row, res_row, n, max);
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn add_residual_into_neon16(
dst: &mut [u16],
stride: usize,
pred: &[u16],
res: &[i16],
n: usize,
valid_w: usize,
valid_h: usize,
bit_depth: u8,
) {
if !supported_n(n)
|| sample_max(bit_depth) > 32767
|| !can_reconstruct_full_block(dst, stride, pred, res, n, valid_w, valid_h, bit_depth)
{
add_residual_into_scalar16(dst, stride, pred, res, n, valid_w, valid_h, bit_depth);
return;
}
unsafe { add_residual_into_neon_impl_16(dst, stride, pred, res, n, bit_depth) }
}