zoomvtools 2.0.0

Video motion vector analysis utilities in pure Rust
Documentation
#[cfg(all(target_arch = "x86_64", feature = "simd"))]
mod avx2;
#[cfg(all(target_arch = "x86_64", feature = "simd"))]
mod avx512;
mod rust;

#[cfg(test)]
mod tests;

use std::{mem::size_of, num::NonZeroUsize};

use crate::util::Pixel;

/// Function pointer for SAD (Sum of Absolute Differences) with block size baked in.
pub type SadFn = unsafe fn(
    src: *const u8,
    src_pitch: NonZeroUsize,
    ref_: *const u8,
    ref_pitch: NonZeroUsize,
) -> u64;

/// Selects the appropriate SAD function for the given block size.
///
/// # Panics
/// Panics if the block size is not supported.
#[must_use]
#[inline]
pub fn select_sad<T: Pixel>(width: NonZeroUsize, height: NonZeroUsize) -> SadFn {
    #[cfg(all(target_arch = "x86_64", feature = "simd"))]
    if cpudetect::x86_64::is_znver5_compatible() {
        match (size_of::<T>(), width.get(), height.get()) {
            // PERF: Equivalent to AVX2 on test machine
            (1, 2, 2) => return avx512::get_sad_u8::<2, 2>,
            (1, 2, 4) => return avx512::get_sad_u8::<2, 4>,
            // PERF: Equivalent to AVX2 on test machine
            (1, 4, 2) => return avx512::get_sad_u8::<4, 2>,
            (1, 4, 4) => return avx512::get_sad_u8::<4, 4>,
            (1, 4, 8) => return avx512::get_sad_u8::<4, 8>,
            // PERF: Equivalent to AVX2 on test machine
            (1, 8, 1) => return avx512::get_sad_u8::<8, 1>,
            (1, 8, 2) => return avx512::get_sad_u8::<8, 2>,
            (1, 8, 4) => return avx512::get_sad_u8::<8, 4>,
            (1, 8, 8) => return avx512::get_sad_u8::<8, 8>,
            (1, 8, 16) => return avx512::get_sad_u8::<8, 16>,
            // PERF: Equivalent to AVX2 on test machine
            (1, 16, 1) => return avx512::get_sad_u8::<16, 1>,
            (1, 16, 2) => return avx512::get_sad_u8::<16, 2>,
            (1, 16, 4) => return avx512::get_sad_u8::<16, 4>,
            (1, 16, 8) => return avx512::get_sad_u8::<16, 8>,
            (1, 16, 16) => return avx512::get_sad_u8::<16, 16>,
            (1, 16, 32) => return avx512::get_sad_u8::<16, 32>,
            // PERF: Equivalent to AVX2 on test machine
            (1, 32, 8) => return avx512::get_sad_u8::<32, 8>,
            (1, 32, 16) => return avx512::get_sad_u8::<32, 16>,
            (1, 32, 32) => return avx512::get_sad_u8::<32, 32>,
            (1, 32, 64) => return avx512::get_sad_u8::<32, 64>,
            // PERF: Equivalent to AVX2 on test machine
            (1, 64, 16) => return avx512::get_sad_u8::<64, 16>,
            (1, 64, 32) => return avx512::get_sad_u8::<64, 32>,
            (1, 64, 64) => return avx512::get_sad_u8::<64, 64>,
            (1, 64, 128) => return avx512::get_sad_u8::<64, 128>,
            // PERF: Equivalent to AVX2 on test machine
            (1, 128, 32) => return avx512::get_sad_u8::<128, 32>,
            (1, 128, 64) => return avx512::get_sad_u8::<128, 64>,
            (1, 128, 128) => return avx512::get_sad_u8::<128, 128>,
            // PERF: Equivalent to AVX2 on test machine
            (2, 2, 2) => return avx512::get_sad_u16::<2, 2>,
            (2, 2, 4) => return avx512::get_sad_u16::<2, 4>,
            // PERF: Equivalent to AVX2 on test machine
            (2, 4, 2) => return avx512::get_sad_u16::<4, 2>,
            (2, 4, 4) => return avx512::get_sad_u16::<4, 4>,
            (2, 4, 8) => return avx512::get_sad_u16::<4, 8>,
            // PERF: Equivalent to AVX2 on test machine
            (2, 8, 1) => return avx512::get_sad_u16::<8, 1>,
            (2, 8, 2) => return avx512::get_sad_u16::<8, 2>,
            (2, 8, 4) => return avx512::get_sad_u16::<8, 4>,
            (2, 8, 8) => return avx512::get_sad_u16::<8, 8>,
            (2, 8, 16) => return avx512::get_sad_u16::<8, 16>,
            // PERF: 10% faster than AVX2 on test machine
            (2, 16, 1) => return avx512::get_sad_u16::<16, 1>,
            (2, 16, 2) => return avx512::get_sad_u16::<16, 2>,
            (2, 16, 4) => return avx512::get_sad_u16::<16, 4>,
            (2, 16, 8) => return avx512::get_sad_u16::<16, 8>,
            (2, 16, 16) => return avx512::get_sad_u16::<16, 16>,
            (2, 16, 32) => return avx512::get_sad_u16::<16, 32>,
            // PERF: 20% faster than AVX2 on test machine
            (2, 32, 8) => return avx512::get_sad_u16::<32, 8>,
            (2, 32, 16) => return avx512::get_sad_u16::<32, 16>,
            (2, 32, 32) => return avx512::get_sad_u16::<32, 32>,
            (2, 32, 64) => return avx512::get_sad_u16::<32, 64>,
            // PERF: 40% faster than AVX2 on test machine
            (2, 64, 16) => return avx512::get_sad_u16::<64, 16>,
            (2, 64, 32) => return avx512::get_sad_u16::<64, 32>,
            (2, 64, 64) => return avx512::get_sad_u16::<64, 64>,
            (2, 64, 128) => return avx512::get_sad_u16::<64, 128>,
            // PERF: 20% faster than AVX2 on test machine
            (2, 128, 32) => return avx512::get_sad_u16::<128, 32>,
            (2, 128, 64) => return avx512::get_sad_u16::<128, 64>,
            (2, 128, 128) => return avx512::get_sad_u16::<128, 128>,
            _ => {}
        };
    }

    #[cfg(all(target_arch = "x86_64", feature = "simd"))]
    if cpudetect::x86_64::is_x86_64_v4_compatible() {
        match (size_of::<T>(), width.get(), height.get()) {
            // TODO: 5% slower than scalar
            #[cfg(feature = "experimental")]
            (1, 2, 2) => return avx512::get_sad_u8::<2, 2>,
            #[cfg(feature = "experimental")]
            (1, 2, 4) => return avx512::get_sad_u8::<2, 4>,
            // PERF: Equivalent to AVX2 on test machine
            (1, 4, 2) => return avx512::get_sad_u8::<4, 2>,
            (1, 4, 4) => return avx512::get_sad_u8::<4, 4>,
            (1, 4, 8) => return avx512::get_sad_u8::<4, 8>,
            // PERF: Equivalent to AVX2 on test machine
            (1, 8, 1) => return avx512::get_sad_u8::<8, 1>,
            (1, 8, 2) => return avx512::get_sad_u8::<8, 2>,
            (1, 8, 4) => return avx512::get_sad_u8::<8, 4>,
            (1, 8, 8) => return avx512::get_sad_u8::<8, 8>,
            (1, 8, 16) => return avx512::get_sad_u8::<8, 16>,
            // PERF: Equivalent to AVX2 on test machine
            (1, 16, 1) => return avx512::get_sad_u8::<16, 1>,
            (1, 16, 2) => return avx512::get_sad_u8::<16, 2>,
            (1, 16, 4) => return avx512::get_sad_u8::<16, 4>,
            (1, 16, 8) => return avx512::get_sad_u8::<16, 8>,
            (1, 16, 16) => return avx512::get_sad_u8::<16, 16>,
            (1, 16, 32) => return avx512::get_sad_u8::<16, 32>,
            // TODO: 2% slower than AVX2 on test machine
            #[cfg(feature = "experimental")]
            (1, 32, 8) => return avx512::get_sad_u8::<32, 8>,
            #[cfg(feature = "experimental")]
            (1, 32, 16) => return avx512::get_sad_u8::<32, 16>,
            #[cfg(feature = "experimental")]
            (1, 32, 32) => return avx512::get_sad_u8::<32, 32>,
            #[cfg(feature = "experimental")]
            (1, 32, 64) => return avx512::get_sad_u8::<32, 64>,
            // TODO: 10% slower than AVX2 on test machine
            #[cfg(feature = "experimental")]
            (1, 64, 16) => return avx512::get_sad_u8::<64, 16>,
            #[cfg(feature = "experimental")]
            (1, 64, 32) => return avx512::get_sad_u8::<64, 32>,
            #[cfg(feature = "experimental")]
            (1, 64, 64) => return avx512::get_sad_u8::<64, 64>,
            #[cfg(feature = "experimental")]
            (1, 64, 128) => return avx512::get_sad_u8::<64, 128>,
            // TODO: 2% slower than AVX2 on test machine
            #[cfg(feature = "experimental")]
            (1, 128, 32) => return avx512::get_sad_u8::<128, 32>,
            #[cfg(feature = "experimental")]
            (1, 128, 64) => return avx512::get_sad_u8::<128, 64>,
            #[cfg(feature = "experimental")]
            (1, 128, 128) => return avx512::get_sad_u8::<128, 128>,
            // TODO: 2% slower than scalar
            #[cfg(feature = "experimental")]
            (2, 2, 2) => return avx512::get_sad_u16::<2, 2>,
            #[cfg(feature = "experimental")]
            (2, 2, 4) => return avx512::get_sad_u16::<2, 4>,
            // PERF: Equivalent to AVX2 on test machine
            (2, 4, 2) => return avx512::get_sad_u16::<4, 2>,
            (2, 4, 4) => return avx512::get_sad_u16::<4, 4>,
            (2, 4, 8) => return avx512::get_sad_u16::<4, 8>,
            // PERF: Equivalent to AVX2 on test machine
            (2, 8, 1) => return avx512::get_sad_u16::<8, 1>,
            (2, 8, 2) => return avx512::get_sad_u16::<8, 2>,
            (2, 8, 4) => return avx512::get_sad_u16::<8, 4>,
            (2, 8, 8) => return avx512::get_sad_u16::<8, 8>,
            (2, 8, 16) => return avx512::get_sad_u16::<8, 16>,
            // TODO: 5% slower than AVX2 on test machine
            #[cfg(feature = "experimental")]
            (2, 16, 1) => return avx512::get_sad_u16::<16, 1>,
            #[cfg(feature = "experimental")]
            (2, 16, 2) => return avx512::get_sad_u16::<16, 2>,
            #[cfg(feature = "experimental")]
            (2, 16, 4) => return avx512::get_sad_u16::<16, 4>,
            #[cfg(feature = "experimental")]
            (2, 16, 8) => return avx512::get_sad_u16::<16, 8>,
            #[cfg(feature = "experimental")]
            (2, 16, 16) => return avx512::get_sad_u16::<16, 16>,
            #[cfg(feature = "experimental")]
            (2, 16, 32) => return avx512::get_sad_u16::<16, 32>,
            // PERF: 3-5% faster than AVX2 on test machine
            (2, 32, 8) => return avx512::get_sad_u16::<32, 8>,
            (2, 32, 16) => return avx512::get_sad_u16::<32, 16>,
            (2, 32, 32) => return avx512::get_sad_u16::<32, 32>,
            (2, 32, 64) => return avx512::get_sad_u16::<32, 64>,
            // PERF: Equivalent to AVX2 on test machine
            (2, 64, 16) => return avx512::get_sad_u16::<64, 16>,
            (2, 64, 32) => return avx512::get_sad_u16::<64, 32>,
            (2, 64, 64) => return avx512::get_sad_u16::<64, 64>,
            (2, 64, 128) => return avx512::get_sad_u16::<64, 128>,
            // PERF: 5% faster than AVX2 on test machine
            (2, 128, 32) => return avx512::get_sad_u16::<128, 32>,
            (2, 128, 64) => return avx512::get_sad_u16::<128, 64>,
            (2, 128, 128) => return avx512::get_sad_u16::<128, 128>,
            _ => {}
        };
    }

    #[cfg(all(target_arch = "x86_64", feature = "simd"))]
    if cpudetect::x86_64::is_x86_64_v3_compatible() {
        match (size_of::<T>(), width.get(), height.get()) {
            // TODO: 5% slower than scalar
            #[cfg(feature = "experimental")]
            (1, 2, 2) => return avx2::get_sad_u8::<2, 2>,
            #[cfg(feature = "experimental")]
            (1, 2, 4) => return avx2::get_sad_u8::<2, 4>,
            // PERF: 2% faster than scalar on test machine
            (1, 4, 2) => return avx2::get_sad_u8::<4, 2>,
            (1, 4, 4) => return avx2::get_sad_u8::<4, 4>,
            (1, 4, 8) => return avx2::get_sad_u8::<4, 8>,
            // PERF: 75% faster than scalar on test machine
            (1, 8, 1) => return avx2::get_sad_u8::<8, 1>,
            (1, 8, 2) => return avx2::get_sad_u8::<8, 2>,
            (1, 8, 4) => return avx2::get_sad_u8::<8, 4>,
            (1, 8, 8) => return avx2::get_sad_u8::<8, 8>,
            (1, 8, 16) => return avx2::get_sad_u8::<8, 16>,
            // PERF: 85% faster than scalar on test machine
            (1, 16, 1) => return avx2::get_sad_u8::<16, 1>,
            (1, 16, 2) => return avx2::get_sad_u8::<16, 2>,
            (1, 16, 4) => return avx2::get_sad_u8::<16, 4>,
            (1, 16, 8) => return avx2::get_sad_u8::<16, 8>,
            (1, 16, 16) => return avx2::get_sad_u8::<16, 16>,
            (1, 16, 32) => return avx2::get_sad_u8::<16, 32>,
            // PERF: 85% faster than scalar on test machine
            (1, 32, 8) => return avx2::get_sad_u8::<32, 8>,
            (1, 32, 16) => return avx2::get_sad_u8::<32, 16>,
            (1, 32, 32) => return avx2::get_sad_u8::<32, 32>,
            (1, 32, 64) => return avx2::get_sad_u8::<32, 64>,
            // PERF: 50-90% faster than scalar on test machine
            (1, 64, 16) => return avx2::get_sad_u8::<64, 16>,
            (1, 64, 32) => return avx2::get_sad_u8::<64, 32>,
            (1, 64, 64) => return avx2::get_sad_u8::<64, 64>,
            (1, 64, 128) => return avx2::get_sad_u8::<64, 128>,
            // PERF: 50-90% faster than scalar on test machine
            (1, 128, 32) => return avx2::get_sad_u8::<128, 32>,
            (1, 128, 64) => return avx2::get_sad_u8::<128, 64>,
            (1, 128, 128) => return avx2::get_sad_u8::<128, 128>,
            // TODO: 2% slower than scalar
            #[cfg(feature = "experimental")]
            (2, 2, 2) => return avx2::get_sad_u16::<2, 2>,
            #[cfg(feature = "experimental")]
            (2, 2, 4) => return avx2::get_sad_u16::<2, 4>,
            // PERF: 10% faster than scalar on test machine
            (2, 4, 2) => return avx2::get_sad_u16::<4, 2>,
            (2, 4, 4) => return avx2::get_sad_u16::<4, 4>,
            (2, 4, 8) => return avx2::get_sad_u16::<4, 8>,
            // PERF: 75% faster than scalar on test machine
            (2, 8, 1) => return avx2::get_sad_u16::<8, 1>,
            (2, 8, 2) => return avx2::get_sad_u16::<8, 2>,
            (2, 8, 4) => return avx2::get_sad_u16::<8, 4>,
            (2, 8, 8) => return avx2::get_sad_u16::<8, 8>,
            (2, 8, 16) => return avx2::get_sad_u16::<8, 16>,
            // PERF: 75% faster than scalar on test machine
            (2, 16, 1) => return avx2::get_sad_u16::<16, 1>,
            (2, 16, 2) => return avx2::get_sad_u16::<16, 2>,
            (2, 16, 4) => return avx2::get_sad_u16::<16, 4>,
            (2, 16, 8) => return avx2::get_sad_u16::<16, 8>,
            (2, 16, 16) => return avx2::get_sad_u16::<16, 16>,
            (2, 16, 32) => return avx2::get_sad_u16::<16, 32>,
            // PERF: 70% faster than scalar on test machine
            (2, 32, 8) => return avx2::get_sad_u16::<32, 8>,
            (2, 32, 16) => return avx2::get_sad_u16::<32, 16>,
            (2, 32, 32) => return avx2::get_sad_u16::<32, 32>,
            (2, 32, 64) => return avx2::get_sad_u16::<32, 64>,
            // PERF: 40-70% faster than scalar on test machine
            (2, 64, 16) => return avx2::get_sad_u16::<64, 16>,
            (2, 64, 32) => return avx2::get_sad_u16::<64, 32>,
            (2, 64, 64) => return avx2::get_sad_u16::<64, 64>,
            (2, 64, 128) => return avx2::get_sad_u16::<64, 128>,
            // PERF: 40-70% faster than scalar on test machine
            (2, 128, 32) => return avx2::get_sad_u16::<128, 32>,
            (2, 128, 64) => return avx2::get_sad_u16::<128, 64>,
            (2, 128, 128) => return avx2::get_sad_u16::<128, 128>,
            _ => {}
        };
    }

    match (size_of::<T>(), width.get(), height.get()) {
        (1, 2, 2) => rust::get_sad_u8::<2, 2>,
        (1, 2, 4) => rust::get_sad_u8::<2, 4>,
        (1, 4, 2) => rust::get_sad_u8::<4, 2>,
        (1, 4, 4) => rust::get_sad_u8::<4, 4>,
        (1, 4, 8) => rust::get_sad_u8::<4, 8>,
        (1, 8, 1) => rust::get_sad_u8::<8, 1>,
        (1, 8, 2) => rust::get_sad_u8::<8, 2>,
        (1, 8, 4) => rust::get_sad_u8::<8, 4>,
        (1, 8, 8) => rust::get_sad_u8::<8, 8>,
        (1, 8, 16) => rust::get_sad_u8::<8, 16>,
        (1, 16, 1) => rust::get_sad_u8::<16, 1>,
        (1, 16, 2) => rust::get_sad_u8::<16, 2>,
        (1, 16, 4) => rust::get_sad_u8::<16, 4>,
        (1, 16, 8) => rust::get_sad_u8::<16, 8>,
        (1, 16, 16) => rust::get_sad_u8::<16, 16>,
        (1, 16, 32) => rust::get_sad_u8::<16, 32>,
        (1, 32, 8) => rust::get_sad_u8::<32, 8>,
        (1, 32, 16) => rust::get_sad_u8::<32, 16>,
        (1, 32, 32) => rust::get_sad_u8::<32, 32>,
        (1, 32, 64) => rust::get_sad_u8::<32, 64>,
        (1, 64, 16) => rust::get_sad_u8::<64, 16>,
        (1, 64, 32) => rust::get_sad_u8::<64, 32>,
        (1, 64, 64) => rust::get_sad_u8::<64, 64>,
        (1, 64, 128) => rust::get_sad_u8::<64, 128>,
        (1, 128, 32) => rust::get_sad_u8::<128, 32>,
        (1, 128, 64) => rust::get_sad_u8::<128, 64>,
        (1, 128, 128) => rust::get_sad_u8::<128, 128>,
        (2, 2, 2) => rust::get_sad_u16::<2, 2>,
        (2, 2, 4) => rust::get_sad_u16::<2, 4>,
        (2, 4, 2) => rust::get_sad_u16::<4, 2>,
        (2, 4, 4) => rust::get_sad_u16::<4, 4>,
        (2, 4, 8) => rust::get_sad_u16::<4, 8>,
        (2, 8, 1) => rust::get_sad_u16::<8, 1>,
        (2, 8, 2) => rust::get_sad_u16::<8, 2>,
        (2, 8, 4) => rust::get_sad_u16::<8, 4>,
        (2, 8, 8) => rust::get_sad_u16::<8, 8>,
        (2, 8, 16) => rust::get_sad_u16::<8, 16>,
        (2, 16, 1) => rust::get_sad_u16::<16, 1>,
        (2, 16, 2) => rust::get_sad_u16::<16, 2>,
        (2, 16, 4) => rust::get_sad_u16::<16, 4>,
        (2, 16, 8) => rust::get_sad_u16::<16, 8>,
        (2, 16, 16) => rust::get_sad_u16::<16, 16>,
        (2, 16, 32) => rust::get_sad_u16::<16, 32>,
        (2, 32, 8) => rust::get_sad_u16::<32, 8>,
        (2, 32, 16) => rust::get_sad_u16::<32, 16>,
        (2, 32, 32) => rust::get_sad_u16::<32, 32>,
        (2, 32, 64) => rust::get_sad_u16::<32, 64>,
        (2, 64, 16) => rust::get_sad_u16::<64, 16>,
        (2, 64, 32) => rust::get_sad_u16::<64, 32>,
        (2, 64, 64) => rust::get_sad_u16::<64, 64>,
        (2, 64, 128) => rust::get_sad_u16::<64, 128>,
        (2, 128, 32) => rust::get_sad_u16::<128, 32>,
        (2, 128, 64) => rust::get_sad_u16::<128, 64>,
        (2, 128, 128) => rust::get_sad_u16::<128, 128>,
        _ => panic!("unsupported block size for SAD: {}x{}", width, height),
    }
}