use core::arch::wasm32::*;
use super::SadFn;
#[inline(always)]
unsafe fn d16(x: v128, y: v128) -> v128 {
i32x4_dot_i16x8(i16x8_abs(i16x8_sub(x, y)), i16x8_splat(1))
}
#[inline(always)]
unsafe fn d8(x: v128, y: v128) -> v128 {
u16x8_extadd_pairwise_u8x16(v128_or(u8x16_sub_sat(x, y), u8x16_sub_sat(y, x)))
}
#[inline(always)]
unsafe fn q(p: *const u8) -> u64 {
p.cast::<u64>().read_unaligned()
}
#[inline(always)]
unsafe fn d(p: *const u8) -> u32 {
p.cast::<u32>().read_unaligned()
}
#[inline(always)]
unsafe fn sum32(v: v128) -> u32 {
u32x4_extract_lane::<0>(v) + u32x4_extract_lane::<1>(v) + u32x4_extract_lane::<2>(v) + u32x4_extract_lane::<3>(v)
}
pub unsafe fn sad_u16<const W: usize, const H: usize>(a: *const u16, sa: usize, b: *const u16, sb: usize) -> u32 {
let (a, b, sa, sb) = (a.cast::<u8>(), b.cast::<u8>(), 2 * sa, 2 * sb);
let mut acc = i32x4_splat(0);
if W == 4 {
for r in (0..H).step_by(2) {
let (x, y) = (u64x2(q(a.add(r * sa)), q(a.add((r + 1) * sa))), u64x2(q(b.add(r * sb)), q(b.add((r + 1) * sb))));
acc = i32x4_add(acc, d16(x, y));
}
} else {
for r in 0..H {
for c in (0..W).step_by(8) {
acc = i32x4_add(acc, d16(v128_load(a.add(r * sa + 2 * c).cast()), v128_load(b.add(r * sb + 2 * c).cast())));
}
}
}
sum32(acc)
}
pub unsafe fn sad_u8<const W: usize, const H: usize>(a: *const u8, sa: usize, b: *const u8, sb: usize) -> u32 {
let mut acc = i32x4_splat(0);
for r0 in (0..H).step_by(16) {
let mut s = i16x8_splat(0);
if W == 4 {
for r in (r0..r0 + 16).take_while(|&r| r < H).step_by(4) {
let row = |p: *const u8, s: usize| u32x4(d(p), d(p.add(s)), d(p.add(2 * s)), d(p.add(3 * s)));
s = i16x8_add(s, d8(row(a.add(r * sa), sa), row(b.add(r * sb), sb)));
}
} else if W == 8 {
for r in (r0..r0 + 16).take_while(|&r| r < H).step_by(2) {
let (x, y) = (u64x2(q(a.add(r * sa)), q(a.add((r + 1) * sa))), u64x2(q(b.add(r * sb)), q(b.add((r + 1) * sb))));
s = i16x8_add(s, d8(x, y));
}
} else {
for r in (r0..r0 + 16).take_while(|&r| r < H) {
for c in (0..W).step_by(16) {
s = i16x8_add(s, d8(v128_load(a.add(r * sa + c).cast()), v128_load(b.add(r * sb + c).cast())));
}
}
}
acc = i32x4_add(acc, u32x4_extadd_pairwise_u16x8(s));
}
sum32(acc)
}
pub unsafe fn pair_u16(a: *const u16, sa: usize, b: *const u16, sb: usize) -> u64 {
let (mut a, mut b, sa, sb) = (a.cast::<u8>(), b.cast::<u8>(), 2 * sa, 2 * sb);
let (mut l, mut r) = (i32x4_splat(0), i32x4_splat(0));
for _ in 0..8 {
l = i32x4_add(l, d16(v128_load(a.cast()), v128_load(b.cast())));
r = i32x4_add(r, d16(v128_load(a.add(16).cast()), v128_load(b.add(16).cast())));
(a, b) = (a.add(sa), b.add(sb));
}
u64::from(sum32(l)) | u64::from(sum32(r)) << 32
}
pub unsafe fn pair_u8(mut a: *const u8, sa: usize, mut b: *const u8, sb: usize) -> u64 {
let mut s = i16x8_splat(0);
for _ in 0..8 {
s = i16x8_add(s, d8(v128_load(a.cast()), v128_load(b.cast())));
(a, b) = (a.add(sa), b.add(sb));
}
let v = u32x4_extadd_pairwise_u16x8(s);
u64::from(u32x4_extract_lane::<0>(v) + u32x4_extract_lane::<1>(v)) | u64::from(u32x4_extract_lane::<2>(v) + u32x4_extract_lane::<3>(v)) << 32
}
pub(super) static SAD_U16: [SadFn<u16>; 25] = sizes!(sad_u16);
pub(super) static SAD_U8: [SadFn<u8>; 25] = sizes!(sad_u8);