#![allow(clippy::unwrap_used, reason = "allow in test files")]
#![allow(clippy::undocumented_unsafe_blocks, reason = "allow in test files")]
#![allow(clippy::indexing_slicing, reason = "allow in test files")]
#[cfg(all(target_arch = "x86_64", feature = "avx2"))]
use std::mem::size_of;
use std::num::NonZeroUsize;
use crate::{params::Subpel, resize::SimpleResize};
fn nz(value: usize) -> NonZeroUsize {
NonZeroUsize::new(value).expect("test value must be non-zero")
}
fn build_resizer(
dest_width: usize,
dest_height: usize,
src_width: usize,
src_height: usize,
limit_width: usize,
limit_height: usize,
pel: Subpel,
) -> SimpleResize {
SimpleResize::new(
nz(dest_width),
nz(dest_height),
nz(src_width),
nz(src_height),
nz(limit_width),
nz(limit_height),
pel,
)
}
fn build_rust_resizer(
dest_width: usize,
dest_height: usize,
src_width: usize,
src_height: usize,
limit_width: usize,
limit_height: usize,
pel: Subpel,
) -> SimpleResize {
#[allow(unused_mut, reason = "only mutated in AVX2 test builds")]
let mut resizer = build_resizer(
dest_width,
dest_height,
src_width,
src_height,
limit_width,
limit_height,
pel,
);
#[cfg(all(target_arch = "x86_64", feature = "avx2"))]
if crate::util::has_avx2() {
for weight in &mut resizer.horizontal_weights {
*weight >>= 16;
}
}
resizer
}
fn build_u8_src(width: usize, height: usize, stride: usize) -> Vec<u8> {
let mut src = vec![0_u8; stride * height];
for y in 0..height {
for x in 0..width {
src[y * stride + x] = ((x * 29 + y * 17 + (x * y) * 7 + 13) & 0xff) as u8;
}
}
src
}
fn build_i16_src(width: usize, height: usize, stride: usize, pel: i16) -> Vec<i16> {
let mut src = vec![0_i16; stride * height];
for y in 0..height {
for x in 0..width {
src[y * stride + x] = (x as i16 * pel) - (y as i16 * (pel / 2 + 1));
}
}
src
}
#[test]
fn resize_u8_to_vec_matches_resize_u8_and_zeroes_stride_padding() {
let resizer = build_rust_resizer(5, 3, 4, 2, 5, 3, Subpel::Full);
let src_stride = 6;
let dest_stride = 8;
let src = build_u8_src(4, 2, src_stride);
let mut expected = vec![0xaa_u8; dest_stride * 3];
resizer.resize_u8(&mut expected, nz(dest_stride), &src, nz(src_stride), false);
let actual = resizer.resize_u8_to_vec(nz(dest_stride), &src, nz(src_stride), false);
for y in 0..3 {
let row = y * dest_stride;
assert_eq!(&actual[row..row + 5], &expected[row..row + 5]);
assert_eq!(&actual[row + 5..row + dest_stride], &[0, 0, 0]);
}
}
#[test]
fn resize_i16_to_vec_matches_resize_i16_and_zeroes_stride_padding() {
let resizer = build_rust_resizer(5, 4, 4, 3, 5, 4, Subpel::Half);
let src_stride = 7;
let dest_stride = 9;
let src = build_i16_src(4, 3, src_stride, 2);
let mut expected = vec![i16::MIN; dest_stride * 4];
resizer.resize_i16(&mut expected, nz(dest_stride), &src, nz(src_stride), true);
let actual = resizer.resize_i16_to_vec(nz(dest_stride), &src, nz(src_stride), true);
for y in 0..4 {
let row = y * dest_stride;
assert_eq!(&actual[row..row + 5], &expected[row..row + 5]);
assert_eq!(&actual[row + 5..row + dest_stride], &[0, 0, 0, 0]);
}
}
#[cfg(all(target_arch = "x86_64", feature = "avx2"))]
#[test]
fn simple_resize_u8_avx2_matches_rust_varied_shapes() {
let cases = [
(8, 4, 4, 3, false),
(12, 7, 8, 5, false),
(17, 9, 8, 6, false),
(19, 11, 9, 5, true),
(31, 13, 16, 9, true),
];
for &(dest_width, dest_height, src_width, src_height, horizontal_vectors) in &cases {
for pel in [Subpel::Full, Subpel::Half, Subpel::Quarter] {
let src_stride = src_width + 5;
let dest_stride = dest_width + 7;
let rust_resizer = build_rust_resizer(
dest_width,
dest_height,
src_width,
src_height,
dest_width.saturating_sub(1).max(1),
dest_height.saturating_sub(1).max(1),
pel,
);
let avx_resizer = build_resizer(
dest_width,
dest_height,
src_width,
src_height,
dest_width.saturating_sub(1).max(1),
dest_height.saturating_sub(1).max(1),
pel,
);
let src = build_u8_src(src_width, src_height, src_stride);
let mut rust_dest = vec![0_u8; dest_stride * dest_height];
let mut avx_dest = rust_dest.clone();
unsafe {
super::rust::simple_resize::<u8>(
&rust_resizer,
rust_dest.as_mut_ptr().cast(),
nz(dest_stride),
src.as_ptr().cast(),
nz(src_stride),
horizontal_vectors,
);
super::avx2::simple_resize_u8(
&avx_resizer,
avx_dest.as_mut_ptr().cast(),
nz(dest_stride),
src.as_ptr().cast(),
nz(src_stride),
horizontal_vectors,
);
}
assert_eq!(
avx_dest, rust_dest,
"u8 mismatch dest={dest_width}x{dest_height} src={src_width}x{src_height} pel={pel:?} horizontal_vectors={horizontal_vectors}"
);
}
}
}
#[cfg(all(target_arch = "x86_64", feature = "avx2"))]
#[test]
fn simple_resize_i16_avx2_matches_rust_varied_shapes() {
let cases = [
(8, 4, 8, 3, 5, 4, false),
(9, 6, 8, 4, 7, 5, false),
(11, 8, 8, 4, 5, 6, true),
(17, 9, 8, 6, 12, 7, true),
(23, 10, 11, 7, 9, 8, true),
];
for &(
dest_width,
dest_height,
src_width,
src_height,
limit_width,
limit_height,
horizontal_vectors,
) in &cases
{
for pel in [Subpel::Full, Subpel::Half, Subpel::Quarter] {
let src_stride = src_width + 6;
let dest_stride = dest_width + 5;
let pel_value = i16::from(u8::from(pel));
let rust_resizer = build_rust_resizer(
dest_width,
dest_height,
src_width,
src_height,
limit_width,
limit_height,
pel,
);
let avx_resizer = build_resizer(
dest_width,
dest_height,
src_width,
src_height,
limit_width,
limit_height,
pel,
);
let src = build_i16_src(src_width, src_height, src_stride, pel_value);
let mut rust_dest = vec![i16::MIN; dest_stride * dest_height];
let mut avx_dest = rust_dest.clone();
unsafe {
super::rust::simple_resize::<i16>(
&rust_resizer,
rust_dest.as_mut_ptr().cast(),
nz(dest_stride * size_of::<i16>()),
src.as_ptr().cast(),
nz(src_stride * size_of::<i16>()),
horizontal_vectors,
);
super::avx2::simple_resize_i16(
&avx_resizer,
avx_dest.as_mut_ptr().cast(),
nz(dest_stride * size_of::<i16>()),
src.as_ptr().cast(),
nz(src_stride * size_of::<i16>()),
horizontal_vectors,
);
}
assert_eq!(
avx_dest, rust_dest,
"i16 mismatch dest={dest_width}x{dest_height} src={src_width}x{src_height} limit={limit_width}x{limit_height} pel={pel:?} horizontal_vectors={horizontal_vectors}"
);
}
}
}