[libyuv] Add 100% SIMD coverage across all compare functions

Implement SIMD kernels across all 6 compare row functions
(ARGBDetectRow, HammingDistance, HashDjb2, Ssim4x4Row, SsimWindowRow,
and SumSquareError) so that ARM (32-bit and 64-bit NEON), x86 (AVX2 and
AVX-512), RISC-V (RVV), Hexagon (HVX), WebAssembly (WASMSIMD and
RelaxedSIMD), and LoongArch (LSX and LASX) each reach 6 / 6 (100%)
coverage:

* HammingDistance:
  - Add HammingDistance_AVX512VNNI in compare_gcc.cc and compare_win.cc
    using vpshufb nibble popcount lookup tables accumulated into 32-bit
    counters via vpdpbusd with k1 masked tail loads (1.39x faster than
    HammingDistance_AVX512BW).
  - Update HammingDistance_WASMSIMD in compare_wasm.cc to accumulate
    per-byte popcounts into 32-bit counters using
    wasm_i32x4_relaxed_dot_i8x16_i7x16_add when __wasm_relaxed_simd__ is
    enabled.
  - Add HammingDistance_RVV, HammingDistance_LSX, and
    HammingDistance_LASX.

* SumSquareError:
  - Add SumSquareError_RVV (vwsubu.vv + vwmacc.vv), SumSquareError_LSX,
    and SumSquareError_LASX (xvabsd.bu + xvmulwev/wod.h.bu).

* HashDjb2:
  - Add 32-bit ARM HashDjb2_NEON in compare_neon.cc, HashDjb2_RVV (with
    arbitrary tail length support), HashDjb2_LSX, and HashDjb2_LASX.

* SsimWindowRow:
  - Add SsimWindowRow_NEON (32-bit and 64-bit), SsimWindowRow_WASMSIMD,
    SsimWindowRow_HVX, SsimWindowRow_LSX, and SsimWindowRow_LASX.

* ARGBDetectRow:
  - Add ARGBDetectRow_NEON (32-bit and 64-bit), ARGBDetectRow_RVV,
    ARGBDetectRow_WASMSIMD, ARGBDetectRow_HVX, ARGBDetectRow_LSX, and
    ARGBDetectRow_LASX, and add 1..7 pixel SIMD tail handling to
    ARGBDetectRow_AVX2 in compare_win.cc.

* Remainder & Test Coverage:
  - Handle tail counts/widths in HashDjb2, ComputeHammingDistance,
    ComputeSumSquareError, and CalcFrameSsim via padded SIMD buffers
    when a SIMD kernel is active, using zero-numerator padding in
    SsimWindowRow_Rem to avoid float cancellation.
  - Add TestARGBDetectAnyWidth, TestCompareAnyCount, and
    TestSsimAnyWidth in compare_test.cc.

Compare SIMD ISA per *_C:
------------------------------------------------------------------------
C Function              arm     x86        riscv  hexagon wasm    loong
------------------------------------------------------------------------
ARGBDetectRow_C         NEON    AVX2       RVV    HVX     SIMD    LASX
HammingDistance_C       DOTPROD AVX512VNNI RVV    HVX     RELAXED LASX
HashDjb2_C              NEON    AVX512BW   RVV    HVX     SIMD    LASX
Ssim4x4Row_C            SME     AVX512BW   RVV    HVX     SIMD    LASX
SsimWindowRow_C         SME     AVX512BW   RVV    HVX     SIMD    LASX
SumSquareError_C        DOTPROD AVX512VNNI RVV    HVX     SIMD    LASX
------------------------------------------------------------------------
SIMD (6 compare)        6 100%  6 100%     6 100% 6 100%  6 100%  6 100%
------------------------------------------------------------------------

Test: libyuv_unittest --gtest_filter=*Compare*
Bug: none
Change-Id: I72a026ca5d9b3da373e32c01396dddba8c494089
Reviewed-on: https://chromium-review.googlesource.com/c/libyuv/libyuv/+/8692272
Reviewed-by: richard winterton <rrwinterton@gmail.com>
This commit is contained in:
Frank Barchard 2026-10-30 13:17:42 -07:00
parent 59fb9e18a8
commit bb310b4e7e
12 changed files with 1688 additions and 91 deletions

View File

@ -42,6 +42,7 @@ extern "C" {
#define HAS_ARGBDETECTROW_AVX2
#if defined(__x86_64__) || defined(_M_X64)
#define HAS_HAMMINGDISTANCE_AVX512BW
#define HAS_HAMMINGDISTANCE_AVX512VNNI
#define HAS_SUMSQUAREERROR_AVX512BW
#define HAS_SUMSQUAREERROR_AVX512VNNI
#define HAS_HASHDJB2_AVX512BW
@ -74,6 +75,7 @@ extern "C" {
#endif
#if defined(__x86_64__) && defined(CLANG_HAS_AVX512)
#define HAS_HAMMINGDISTANCE_AVX512BW
#define HAS_HAMMINGDISTANCE_AVX512VNNI
#define HAS_SUMSQUAREERROR_AVX512BW
#define HAS_SUMSQUAREERROR_AVX512VNNI
#define HAS_HASHDJB2_AVX512BW
@ -87,13 +89,14 @@ extern "C" {
(defined(__ARM_NEON__) || defined(LIBYUV_NEON) || defined(__aarch64__))
#define HAS_HAMMINGDISTANCE_NEON
#define HAS_SUMSQUAREERROR_NEON
#define HAS_HASHDJB2_NEON
#define HAS_SSIM4X4ROW_NEON
#define HAS_SSIMWINDOWROW_NEON
#define HAS_ARGBDETECTROW_NEON
#endif
// The following are available for AArch64 Neon:
#if !defined(LIBYUV_DISABLE_NEON) && defined(__aarch64__)
#define HAS_HASHDJB2_NEON
#define HAS_HAMMINGDISTANCE_NEON_DOTPROD
#define HAS_SUMSQUAREERROR_NEON_DOTPROD
#define HAS_SSIM4X4ROW_NEON_DOTPROD
@ -115,20 +118,34 @@ extern "C" {
// The following are available for RISC-V Vector (RVV):
#if !defined(LIBYUV_DISABLE_RVV) && \
(defined(__riscv_vector) || defined(LIBYUV_RVV))
#define HAS_HAMMINGDISTANCE_RVV
#define HAS_SUMSQUAREERROR_RVV
#define HAS_HASHDJB2_RVV
#define HAS_SSIM4X4ROW_RVV
#if defined(__riscv_f) || defined(__riscv_zve32f)
#define HAS_SSIMWINDOWROW_RVV
#endif
#define HAS_ARGBDETECTROW_RVV
#endif
// The following are available for LoongArch LSX:
#if !defined(LIBYUV_DISABLE_LSX) && defined(__loongarch_sx)
#define HAS_HAMMINGDISTANCE_LSX
#define HAS_SUMSQUAREERROR_LSX
#define HAS_HASHDJB2_LSX
#define HAS_SSIM4X4ROW_LSX
#define HAS_SSIMWINDOWROW_LSX
#define HAS_ARGBDETECTROW_LSX
#endif
// The following are available for LoongArch LASX:
#if !defined(LIBYUV_DISABLE_LASX) && defined(__loongarch_asx)
#define HAS_HAMMINGDISTANCE_LASX
#define HAS_SUMSQUAREERROR_LASX
#define HAS_HASHDJB2_LASX
#define HAS_SSIM4X4ROW_LASX
#define HAS_SSIMWINDOWROW_LASX
#define HAS_ARGBDETECTROW_LASX
#endif
// The following are available for WebAssembly SIMD128:
@ -137,6 +154,8 @@ extern "C" {
#define HAS_SUMSQUAREERROR_WASMSIMD
#define HAS_HASHDJB2_WASMSIMD
#define HAS_SSIM4X4ROW_WASMSIMD
#define HAS_SSIMWINDOWROW_WASMSIMD
#define HAS_ARGBDETECTROW_WASMSIMD
#endif
// The following are available for Hexagon HVX:
@ -145,6 +164,8 @@ extern "C" {
#define HAS_SUMSQUAREERROR_HVX
#define HAS_HASHDJB2_HVX
#define HAS_SSIM4X4ROW_HVX
#define HAS_SSIMWINDOWROW_HVX
#define HAS_ARGBDETECTROW_HVX
#endif
uint32_t HammingDistance_C(const uint8_t* src_a,
@ -162,12 +183,24 @@ uint32_t HammingDistance_AVX2(const uint8_t* src_a,
uint32_t HammingDistance_AVX512BW(const uint8_t* src_a,
const uint8_t* src_b,
int count);
uint32_t HammingDistance_AVX512VNNI(const uint8_t* src_a,
const uint8_t* src_b,
int count);
uint32_t HammingDistance_NEON(const uint8_t* src_a,
const uint8_t* src_b,
int count);
uint32_t HammingDistance_NEON_DotProd(const uint8_t* src_a,
const uint8_t* src_b,
int count);
uint32_t HammingDistance_RVV(const uint8_t* src_a,
const uint8_t* src_b,
int count);
uint32_t HammingDistance_LSX(const uint8_t* src_a,
const uint8_t* src_b,
int count);
uint32_t HammingDistance_LASX(const uint8_t* src_a,
const uint8_t* src_b,
int count);
uint32_t HammingDistance_WASMSIMD(const uint8_t* src_a,
const uint8_t* src_b,
int count);
@ -195,6 +228,15 @@ uint32_t SumSquareError_NEON(const uint8_t* src_a,
uint32_t SumSquareError_NEON_DotProd(const uint8_t* src_a,
const uint8_t* src_b,
int count);
uint32_t SumSquareError_RVV(const uint8_t* src_a,
const uint8_t* src_b,
int count);
uint32_t SumSquareError_LSX(const uint8_t* src_a,
const uint8_t* src_b,
int count);
uint32_t SumSquareError_LASX(const uint8_t* src_a,
const uint8_t* src_b,
int count);
uint32_t SumSquareError_WASMSIMD(const uint8_t* src_a,
const uint8_t* src_b,
int count);
@ -207,6 +249,9 @@ uint32_t HashDjb2_SSE41(const uint8_t* src, int count, uint32_t seed);
uint32_t HashDjb2_AVX2(const uint8_t* src, int count, uint32_t seed);
uint32_t HashDjb2_AVX512BW(const uint8_t* src, int count, uint32_t seed);
uint32_t HashDjb2_NEON(const uint8_t* src, int count, uint32_t seed);
uint32_t HashDjb2_RVV(const uint8_t* src, int count, uint32_t seed);
uint32_t HashDjb2_LSX(const uint8_t* src, int count, uint32_t seed);
uint32_t HashDjb2_LASX(const uint8_t* src, int count, uint32_t seed);
uint32_t HashDjb2_WASMSIMD(const uint8_t* src, int count, uint32_t seed);
uint32_t HashDjb2_HVX(const uint8_t* src, int count, uint32_t seed);
@ -300,6 +345,9 @@ float SsimWindowRow_AVX2(const uint32_t* row0,
float SsimWindowRow_AVX512BW(const uint32_t* row0,
const uint32_t* row1,
int num_windows);
float SsimWindowRow_NEON(const uint32_t* row0,
const uint32_t* row1,
int num_windows);
float SsimWindowRow_SVE2(const uint32_t* row0,
const uint32_t* row1,
int num_windows);
@ -309,9 +357,27 @@ float SsimWindowRow_SME(const uint32_t* row0,
float SsimWindowRow_RVV(const uint32_t* row0,
const uint32_t* row1,
int num_windows);
float SsimWindowRow_LSX(const uint32_t* row0,
const uint32_t* row1,
int num_windows);
float SsimWindowRow_LASX(const uint32_t* row0,
const uint32_t* row1,
int num_windows);
float SsimWindowRow_WASMSIMD(const uint32_t* row0,
const uint32_t* row1,
int num_windows);
float SsimWindowRow_HVX(const uint32_t* row0,
const uint32_t* row1,
int num_windows);
uint32_t ARGBDetectRow_C(const uint8_t* argb, int width);
uint32_t ARGBDetectRow_AVX2(const uint8_t* argb, int width);
uint32_t ARGBDetectRow_NEON(const uint8_t* argb, int width);
uint32_t ARGBDetectRow_RVV(const uint8_t* argb, int width);
uint32_t ARGBDetectRow_LSX(const uint8_t* argb, int width);
uint32_t ARGBDetectRow_LASX(const uint8_t* argb, int width);
uint32_t ARGBDetectRow_WASMSIMD(const uint8_t* argb, int width);
uint32_t ARGBDetectRow_HVX(const uint8_t* argb, int width);
#ifdef __cplusplus
} // extern "C"

View File

@ -13,6 +13,7 @@
#include <float.h>
#include <limits.h>
#include <math.h>
#include <string.h>
#ifdef _OPENMP
#include <omp.h>
#endif
@ -57,6 +58,22 @@ uint32_t HashDjb2(const uint8_t* src, uint64_t count, uint32_t seed) {
simd_size = 1; // Any count.
}
#endif
#if defined(HAS_HASHDJB2_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
HashDjb2_SSE = HashDjb2_RVV;
simd_size = 1; // Any count.
}
#endif
#if defined(HAS_HASHDJB2_LSX)
if (TestCpuFlag(kCpuHasLSX)) {
HashDjb2_SSE = HashDjb2_LSX;
}
#endif
#if defined(HAS_HASHDJB2_LASX)
if (TestCpuFlag(kCpuHasLASX)) {
HashDjb2_SSE = HashDjb2_LASX;
}
#endif
#if defined(HAS_HASHDJB2_WASMSIMD)
if (TestCpuFlag(kCpuHasWASMSIMD)) {
HashDjb2_SSE = HashDjb2_WASMSIMD;
@ -82,7 +99,19 @@ uint32_t HashDjb2(const uint8_t* src, uint64_t count, uint32_t seed) {
}
remainder = (int)count & (simd_size - 1);
if (remainder) {
seed = HashDjb2_C(src, remainder, seed);
if (HashDjb2_SSE == HashDjb2_C) {
seed = HashDjb2_C(src, remainder, seed);
} else {
SIMD_ALIGNED(uint8_t buf[32]);
uint32_t pow33 = 1;
int i;
memset(buf, 0, sizeof(buf));
for (i = 0; i < remainder; ++i) {
pow33 *= 33;
}
memcpy(buf + simd_size - remainder, src, remainder);
seed = seed * pow33 + HashDjb2_SSE(buf, simd_size, 0);
}
}
return seed;
}
@ -111,6 +140,36 @@ uint32_t ARGBDetect(const uint8_t* argb,
if (TestCpuFlag(kCpuHasAVX2)) {
ARGBDetectRow = ARGBDetectRow_AVX2;
}
#endif
#if defined(HAS_ARGBDETECTROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
ARGBDetectRow = ARGBDetectRow_NEON;
}
#endif
#if defined(HAS_ARGBDETECTROW_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
ARGBDetectRow = ARGBDetectRow_RVV;
}
#endif
#if defined(HAS_ARGBDETECTROW_LSX)
if (TestCpuFlag(kCpuHasLSX)) {
ARGBDetectRow = ARGBDetectRow_LSX;
}
#endif
#if defined(HAS_ARGBDETECTROW_LASX)
if (TestCpuFlag(kCpuHasLASX)) {
ARGBDetectRow = ARGBDetectRow_LASX;
}
#endif
#if defined(HAS_ARGBDETECTROW_WASMSIMD)
if (TestCpuFlag(kCpuHasWASMSIMD)) {
ARGBDetectRow = ARGBDetectRow_WASMSIMD;
}
#endif
#if defined(HAS_ARGBDETECTROW_HVX)
if (TestCpuFlag(kCpuHasHVX)) {
ARGBDetectRow = ARGBDetectRow_HVX;
}
#endif
for (h = 0; h < height && fourcc == 0; ++h) {
fourcc = ARGBDetectRow(argb, width);
@ -164,6 +223,28 @@ uint64_t ComputeHammingDistance(const uint8_t* src_a,
simd_size = 1; // Any count.
}
#endif
#if defined(HAS_HAMMINGDISTANCE_AVX512VNNI)
if (TestCpuFlag(kCpuHasAVX512VNNI)) {
HammingDistance = HammingDistance_AVX512VNNI;
simd_size = 1; // Any count.
}
#endif
#if defined(HAS_HAMMINGDISTANCE_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
HammingDistance = HammingDistance_RVV;
simd_size = 1; // Any count.
}
#endif
#if defined(HAS_HAMMINGDISTANCE_LSX)
if (TestCpuFlag(kCpuHasLSX)) {
HammingDistance = HammingDistance_LSX;
}
#endif
#if defined(HAS_HAMMINGDISTANCE_LASX)
if (TestCpuFlag(kCpuHasLASX)) {
HammingDistance = HammingDistance_LASX;
}
#endif
#if defined(HAS_HAMMINGDISTANCE_WASMSIMD)
if (TestCpuFlag(kCpuHasWASMSIMD)) {
HammingDistance = HammingDistance_WASMSIMD;
@ -192,7 +273,17 @@ uint64_t ComputeHammingDistance(const uint8_t* src_a,
}
remainder = count & (simd_size - 1);
if (remainder) {
diff += HammingDistance_C(src_a, src_b, remainder);
if (HammingDistance == HammingDistance_C) {
diff += HammingDistance_C(src_a, src_b, remainder);
} else {
SIMD_ALIGNED(uint8_t buf_a[64]);
SIMD_ALIGNED(uint8_t buf_b[64]);
memset(buf_a, 0, sizeof(buf_a));
memset(buf_b, 0, sizeof(buf_b));
memcpy(buf_a, src_a, remainder);
memcpy(buf_b, src_b, remainder);
diff += HammingDistance(buf_a, buf_b, simd_size);
}
}
return diff;
}
@ -246,6 +337,22 @@ uint64_t ComputeSumSquareError(const uint8_t* src_a,
simd_size = 1; // Any count.
}
#endif
#if defined(HAS_SUMSQUAREERROR_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
SumSquareError = SumSquareError_RVV;
simd_size = 1; // Any count.
}
#endif
#if defined(HAS_SUMSQUAREERROR_LSX)
if (TestCpuFlag(kCpuHasLSX)) {
SumSquareError = SumSquareError_LSX;
}
#endif
#if defined(HAS_SUMSQUAREERROR_LASX)
if (TestCpuFlag(kCpuHasLASX)) {
SumSquareError = SumSquareError_LASX;
}
#endif
#if defined(HAS_SUMSQUAREERROR_WASMSIMD)
if (TestCpuFlag(kCpuHasWASMSIMD)) {
SumSquareError = SumSquareError_WASMSIMD;
@ -273,7 +380,17 @@ uint64_t ComputeSumSquareError(const uint8_t* src_a,
}
remainder = count & (simd_size - 1);
if (remainder) {
sse += SumSquareError_C(src_a, src_b, remainder);
if (SumSquareError == SumSquareError_C) {
sse += SumSquareError_C(src_a, src_b, remainder);
} else {
SIMD_ALIGNED(uint8_t buf_a[32]);
SIMD_ALIGNED(uint8_t buf_b[32]);
memset(buf_a, 0, sizeof(buf_a));
memset(buf_b, 0, sizeof(buf_b));
memcpy(buf_a, src_a, remainder);
memcpy(buf_b, src_b, remainder);
sse += SumSquareError(buf_a, buf_b, simd_size);
}
}
return sse;
}
@ -400,6 +517,78 @@ static float Ssim8x8(const uint8_t* src_a,
}
}
static void Ssim4x4Row_Rem(void (*Ssim4x4Row)(const uint8_t* src_a,
int stride_a,
const uint8_t* src_b,
int stride_b,
uint32_t* dst,
int width),
int simd_size,
const uint8_t* src_a,
int stride_a,
const uint8_t* src_b,
int stride_b,
uint32_t* dst,
int rem_width) {
if (Ssim4x4Row == Ssim4x4Row_C) {
Ssim4x4Row_C(src_a, stride_a, src_b, stride_b, dst, rem_width);
return;
}
SIMD_ALIGNED(uint8_t buf_a[32 * 4]);
SIMD_ALIGNED(uint8_t buf_b[32 * 4]);
SIMD_ALIGNED(uint32_t buf_dst[32]);
memset(buf_a, 0, sizeof(buf_a));
memset(buf_b, 0, sizeof(buf_b));
int r;
for (r = 0; r < 4; ++r) {
memcpy(buf_a + r * 32, src_a + (ptrdiff_t)r * stride_a, rem_width);
memcpy(buf_b + r * 32, src_b + (ptrdiff_t)r * stride_b, rem_width);
}
Ssim4x4Row(buf_a, 32, buf_b, 32, buf_dst, simd_size);
memcpy(dst, buf_dst, (size_t)rem_width * sizeof(uint32_t));
}
static float SsimWindowRow_Rem(float (*SsimWindowRow)(const uint32_t* row0,
const uint32_t* row1,
int num_windows),
int win_simd_size,
const uint32_t* row0,
const uint32_t* row1,
int rem_windows) {
if (SsimWindowRow == SsimWindowRow_C) {
return SsimWindowRow_C(row0, row1, rem_windows);
}
SIMD_ALIGNED(uint32_t win0[9 * 4]);
SIMD_ALIGNED(uint32_t win1[9 * 4]);
memset(win0, 0, sizeof(win0));
memset(win1, 0, sizeof(win1));
// Set odd tiles (1, 3, 5, 7) in win1 so each unused window k >= 1 has
// sum_a = 4439, sum_b = -3, sum_sq = 307887, giving n1 = 2*4439*(-3) + c1 = 0
// and SSIM = 0.0f (avoiding float precision loss from adding 1.0f per lane).
int k;
for (k = 1; k <= win_simd_size; k += 2) {
win1[k * 4 + 0] = 4439u;
win1[k * 4 + 1] = (uint32_t)-3;
win1[k * 4 + 2] = 307887u;
}
float total = 0.0f;
int w;
for (w = 0; w < rem_windows; ++w) {
const uint32_t* r0 = row0 + w * 4;
const uint32_t* r1 = row1 + w * 4;
win0[0] = r0[0] + r0[4];
win0[1] = r0[1] + r0[5];
win0[2] = r0[2] + r0[6];
win0[3] = r0[3] + r0[7];
win1[0] = r1[0] + r1[4] - 4439u;
win1[1] = r1[1] + r1[5] - (uint32_t)-3;
win1[2] = r1[2] + r1[6] - 307887u;
win1[3] = r1[3] + r1[7];
total += SsimWindowRow(win0, win1, win_simd_size);
}
return total;
}
// We are using a 8x8 moving window with starting location of each 8x8 window
// on the 4x4 pixel grid. Such arrangement allows the windows to overlap
// block boundaries to penalize blocking artifacts.
@ -472,6 +661,12 @@ double CalcFrameSsim(const uint8_t* src_a,
simd_size = 16;
}
#endif
#if defined(HAS_SSIMWINDOWROW_NEON)
if (TestCpuFlag(kCpuHasNEON)) {
SsimWindowRow = SsimWindowRow_NEON;
win_simd_size = 4;
}
#endif
#if defined(HAS_SSIM4X4ROW_SVE2)
if (TestCpuFlag(kCpuHasSVE2)) {
Ssim4x4Row = Ssim4x4Row_SVE2;
@ -514,24 +709,48 @@ double CalcFrameSsim(const uint8_t* src_a,
simd_size = 16;
}
#endif
#if defined(HAS_SSIMWINDOWROW_LSX)
if (TestCpuFlag(kCpuHasLSX)) {
SsimWindowRow = SsimWindowRow_LSX;
win_simd_size = 1;
}
#endif
#if defined(HAS_SSIM4X4ROW_LASX)
if (TestCpuFlag(kCpuHasLASX)) {
Ssim4x4Row = Ssim4x4Row_LASX;
simd_size = 32;
}
#endif
#if defined(HAS_SSIMWINDOWROW_LASX)
if (TestCpuFlag(kCpuHasLASX)) {
SsimWindowRow = SsimWindowRow_LASX;
win_simd_size = 1;
}
#endif
#if defined(HAS_SSIM4X4ROW_WASMSIMD)
if (TestCpuFlag(kCpuHasWASMSIMD)) {
Ssim4x4Row = Ssim4x4Row_WASMSIMD;
simd_size = 16;
}
#endif
#if defined(HAS_SSIMWINDOWROW_WASMSIMD)
if (TestCpuFlag(kCpuHasWASMSIMD)) {
SsimWindowRow = SsimWindowRow_WASMSIMD;
win_simd_size = 4;
}
#endif
#if defined(HAS_SSIM4X4ROW_HVX)
if (TestCpuFlag(kCpuHasHVX)) {
Ssim4x4Row = Ssim4x4Row_HVX;
simd_size = 4;
}
#endif
#if defined(HAS_SSIMWINDOWROW_HVX)
if (TestCpuFlag(kCpuHasHVX)) {
SsimWindowRow = SsimWindowRow_HVX;
win_simd_size = 1;
}
#endif
const int num_windows = (width - 8 + 3) >> 2;
const int num_tiles = num_windows + 1;
@ -562,8 +781,9 @@ double CalcFrameSsim(const uint8_t* src_a,
Ssim4x4Row(a, stride_a, b, stride_b, row0, main_width);
}
if (rem_width > 0) {
Ssim4x4Row_C(a + main_width, stride_a, b + main_width, stride_b,
row0 + main_width, rem_width);
Ssim4x4Row_Rem(Ssim4x4Row, simd_size, a + main_width, stride_a,
b + main_width, stride_b, row0 + main_width,
rem_width);
}
}
a += (ptrdiff_t)stride_a * 4;
@ -572,15 +792,17 @@ double CalcFrameSsim(const uint8_t* src_a,
Ssim4x4Row(a, stride_a, b, stride_b, row1, main_width);
}
if (rem_width > 0) {
Ssim4x4Row_C(a + main_width, stride_a, b + main_width, stride_b,
row1 + main_width, rem_width);
Ssim4x4Row_Rem(Ssim4x4Row, simd_size, a + main_width, stride_a,
b + main_width, stride_b, row1 + main_width,
rem_width);
}
if (main_windows > 0) {
ssim_total += SsimWindowRow(row0, row1, main_windows);
}
if (rem_windows > 0) {
ssim_total += SsimWindowRow_C(row0 + main_windows * 4,
row1 + main_windows * 4, rem_windows);
ssim_total += SsimWindowRow_Rem(SsimWindowRow, win_simd_size,
row0 + main_windows * 4,
row1 + main_windows * 4, rem_windows);
}
samples += num_windows;
uint32_t* tmp = row0;

View File

@ -375,9 +375,11 @@ uint32_t HashDjb2_SSE41(const uint8_t* src, int count, uint32_t seed) {
"xmm7");
return hash;
}
#if defined(HAS_HAMMINGDISTANCE_AVX512BW) || \
defined(HAS_SUMSQUAREERROR_AVX512BW) || defined(HAS_HASHDJB2_AVX512BW) || \
defined(HAS_SSIM4X4ROW_AVX512BW)
#if defined(HAS_HAMMINGDISTANCE_AVX512BW) || \
defined(HAS_HAMMINGDISTANCE_AVX512VNNI) || \
defined(HAS_SUMSQUAREERROR_AVX512BW) || \
defined(HAS_SUMSQUAREERROR_AVX512VNNI) || \
defined(HAS_HASHDJB2_AVX512BW) || defined(HAS_SSIM4X4ROW_AVX512BW)
// Mask of the low n bytes, for n = 0 to 63.
static uint64_t LowBytesMask64(int n) {
return n ? ~0ULL >> (64 - n) : 0ULL;
@ -447,11 +449,81 @@ uint32_t HammingDistance_AVX512BW(const uint8_t* src_a,
: [src_a] "+r"(src_a), [src_b] "+r"(src_b), [count] "+r"(count),
[diff] "=r"(diff)
: [nibble] "m"(kNibbleMask), [bitcount] "m"(kBitCount), [mask] "m"(mask)
: "cc", "memory", "k1", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5");
: "memory", "cc", "k1", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5");
return diff;
}
#endif // HAS_HAMMINGDISTANCE_AVX512BW
#ifdef HAS_HAMMINGDISTANCE_AVX512VNNI
// Process 64 bytes per loop and the remainder of 1 to 63 bytes with masked
// loads that read zeros, accumulating byte popcounts with vpdpbusd.
uint32_t HammingDistance_AVX512VNNI(const uint8_t* src_a,
const uint8_t* src_b,
int count) {
uint32_t diff;
const uint64_t mask = LowBytesMask64(count & 63);
asm volatile(
"vbroadcasti32x4 %[nibble],%%zmm2 \n"
"vbroadcasti32x4 %[bitcount],%%zmm3 \n"
"vpxord %%zmm0,%%zmm0,%%zmm0 \n"
"vpternlogd $0xff,%%zmm1,%%zmm1,%%zmm1 \n"
"vpsrlw $0xf,%%zmm1,%%zmm1 \n"
"vpshufb %%zmm0,%%zmm1,%%zmm1 \n"
"kmovq %[mask],%%k1 \n"
"sub %[src_a],%[src_b] \n"
"sub $0x40,%[count] \n"
"jl 2f \n"
LABELALIGN
"1: \n"
"vmovdqu8 (%[src_a]),%%zmm4 \n"
"vpxorq (%[src_a],%[src_b]),%%zmm4,%%zmm4 \n"
"add $0x40,%[src_a] \n"
"vpandq %%zmm2,%%zmm4,%%zmm5 \n"
"vpsrlw $0x4,%%zmm4,%%zmm4 \n"
"vpandq %%zmm2,%%zmm4,%%zmm4 \n"
"vpshufb %%zmm5,%%zmm3,%%zmm5 \n"
"vpshufb %%zmm4,%%zmm3,%%zmm4 \n"
"vpaddb %%zmm5,%%zmm4,%%zmm4 \n"
"vpdpbusd %%zmm4,%%zmm1,%%zmm0 \n"
"sub $0x40,%[count] \n"
"jge 1b \n"
// Remainder: 1 to 63 bytes, masked loads.
"2: \n"
"add $0x40,%[count] \n"
"je 3f \n"
"vmovdqu8 (%[src_a]),%%zmm4%{%%k1%}%{z%} \n"
"vmovdqu8 (%[src_a],%[src_b]),%%zmm5%{%%k1%}%{z%} \n"
"vpxorq %%zmm5,%%zmm4,%%zmm4 \n"
"vpandq %%zmm2,%%zmm4,%%zmm5 \n"
"vpsrlw $0x4,%%zmm4,%%zmm4 \n"
"vpandq %%zmm2,%%zmm4,%%zmm4 \n"
"vpshufb %%zmm5,%%zmm3,%%zmm5 \n"
"vpshufb %%zmm4,%%zmm3,%%zmm4 \n"
"vpaddb %%zmm5,%%zmm4,%%zmm4 \n"
"vpdpbusd %%zmm4,%%zmm1,%%zmm0 \n"
// Add 16 dwords.
"3: \n"
"vextracti64x4 $1,%%zmm0,%%ymm1 \n"
"vpaddd %%ymm1,%%ymm0,%%ymm0 \n"
"vextracti128 $1,%%ymm0,%%xmm1 \n"
"vpaddd %%xmm1,%%xmm0,%%xmm0 \n"
"vpshufd $0xee,%%xmm0,%%xmm1 \n"
"vpaddd %%xmm1,%%xmm0,%%xmm0 \n"
"vpshufd $0x1,%%xmm0,%%xmm1 \n"
"vpaddd %%xmm1,%%xmm0,%%xmm0 \n"
"vmovd %%xmm0,%[diff] \n"
"vzeroupper \n"
: [src_a] "+r"(src_a), [src_b] "+r"(src_b), [count] "+r"(count),
[diff] "=r"(diff)
: [nibble] "m"(kNibbleMask), [bitcount] "m"(kBitCount), [mask] "m"(mask)
: "memory", "cc", "k1", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5");
return diff;
}
#endif // HAS_HAMMINGDISTANCE_AVX512VNNI
#ifdef HAS_SUMSQUAREERROR_AVX512BW
// Process 64 bytes per loop and the remainder of 1 to 63 bytes with masked
// loads that read zeros, so any count is supported.
@ -1454,8 +1526,7 @@ uint32_t ARGBDetectRow_AVX2(const uint8_t* argb, int width) {
asm volatile(
"vpcmpeqd %%ymm1,%%ymm1,%%ymm1 \n"
"sub $0x8,%[width] \n"
"jl 2f \n"
LABELALIGN
"jl 2f \n" LABELALIGN
"1: \n"
"vpcmpeqb (%[argb]),%%ymm1,%%ymm0 \n"
"vpmovmskb %%ymm0,%[diff] \n"
@ -1467,8 +1538,7 @@ uint32_t ARGBDetectRow_AVX2(const uint8_t* argb, int width) {
"jge 1b \n"
"2: \n"
"add $0x8,%[width] \n"
"jle 99f \n"
LABELALIGN
"jle 99f \n" LABELALIGN
"3: \n"
"vmovd (%[argb]),%%xmm0 \n"
"add $0x4,%[argb] \n"

View File

@ -9,6 +9,7 @@
*/
#include "libyuv/compare_row.h"
#include "libyuv/video_common.h"
#if !defined(LIBYUV_DISABLE_HVX) && defined(__HVX__)
@ -282,6 +283,96 @@ void Ssim4x4Row_HVX(const uint8_t* src_a,
#undef SSIM_4X4_HVX
#endif // defined(HAS_SSIM4X4ROW_HVX)
#if defined(HAS_SSIMWINDOWROW_HVX)
float SsimWindowRow_HVX(const uint32_t* row0,
const uint32_t* row1,
int num_windows) {
const HVX_Vector vc1 = Q6_V_vsplat_R(26634);
const HVX_Vector vc2 = Q6_V_vsplat_R(239708);
float total = 0.0f;
while (num_windows > 0) {
int count = num_windows < 8 ? num_windows : 8;
HVX_Vector t0, t1;
if (num_windows >= 8) {
t0 = Q6_Vw_vadd_VwVw(vloadu(row0), vloadu(row1));
t1 = Q6_Vw_vadd_VwVw(vloadu(row0 + 4), vloadu(row1 + 4));
} else {
int bytes0 = count * 16;
int bytes1 = (count + 1) * 16;
t0 = Q6_Vw_vadd_VwVw(vloadu_partial_zero(row0, bytes0),
vloadu_partial_zero(row1, bytes0));
t1 = Q6_Vw_vadd_VwVw(vloadu_partial_zero(row0 + 4, bytes1 - 16),
vloadu_partial_zero(row1 + 4, bytes1 - 16));
}
HVX_Vector w = Q6_Vw_vadd_VwVw(t0, t1);
HVX_Vector w_b = Q6_V_vror_VR(w, 4);
HVX_Vector w_sq = Q6_V_vror_VR(w, 8);
HVX_Vector w_axb = Q6_V_vror_VR(w, 12);
HVX_Vector ab2 = Q6_Vw_vasl_VwR(Q6_Vw_vmpyie_VwVuh(w, w_b), 1);
HVX_Vector a2b2 =
Q6_Vw_vmpyieacc_VwVwVuh(Q6_Vw_vmpyie_VwVuh(w, w), w_b, w_b);
HVX_Vector vn1 = Q6_Vw_vadd_VwVw(ab2, vc1);
HVX_Vector vn2 =
Q6_Vw_vadd_VwVw(Q6_Vw_vsub_VwVw(Q6_Vw_vasl_VwR(w_axb, 7), ab2), vc2);
HVX_Vector vd1 = Q6_Vw_vadd_VwVw(a2b2, vc1);
HVX_Vector vd2 =
Q6_Vw_vadd_VwVw(Q6_Vw_vsub_VwVw(Q6_Vw_vasl_VwR(w_sq, 6), a2b2), vc2);
int32_t n1[32] __attribute__((aligned(128)));
int32_t n2[32] __attribute__((aligned(128)));
int32_t d1[32] __attribute__((aligned(128)));
int32_t d2[32] __attribute__((aligned(128)));
vstoreu(n1, vn1);
vstoreu(n2, vn2);
vstoreu(d1, vd1);
vstoreu(d2, vd2);
int i;
for (i = 0; i < count; ++i) {
float num = (float)n1[i * 4] * (float)n2[i * 4];
float den = (float)d1[i * 4] * (float)d2[i * 4];
total += num / den;
}
row0 += 32;
row1 += 32;
num_windows -= 8;
}
return total;
}
#endif // defined(HAS_SSIMWINDOWROW_HVX)
#if defined(HAS_ARGBDETECTROW_HVX)
uint32_t ARGBDetectRow_HVX(const uint8_t* argb, int width) {
const HVX_Vector mask_rgb = Q6_V_vsplat_R(0x00ffff00);
const HVX_Vector ones = Q6_Vb_vsplat_R(-1);
const HVX_Vector zero = Q6_V_vzero();
while (width > 0) {
HVX_Vector v;
if (width >= 32) {
v = vloadu(argb);
} else {
int bytes = width * 4;
v = Q6_V_vmux_QVV(Q6_Q_vsetq_R(bytes), vloadu_partial_zero(argb, bytes),
ones);
}
v = Q6_V_vor_VV(v, mask_rgb);
HVX_VectorPred eq = Q6_Q_vcmp_eq_VbVb(v, ones);
HVX_Vector diff_v = Q6_V_vmux_QVV(eq, zero, ones);
if (horizontal_sum_u32(Q6_Vuw_vrmpy_VubRub(diff_v, 0x01010101)) != 0) {
uint32_t diff[32] __attribute__((aligned(128)));
vstoreu(diff, diff_v);
int i;
for (i = 0; i < 32; ++i) {
if (diff[i] != 0) {
return (diff[i] & 0xffu) ? FOURCC_BGRA : FOURCC_ARGB;
}
}
}
argb += 128;
width -= 32;
}
return 0;
}
#endif // defined(HAS_ARGBDETECTROW_HVX)
#ifdef __cplusplus
} // extern "C"
} // namespace libyuv

View File

@ -10,6 +10,7 @@
#include "libyuv/compare_row.h"
#include "libyuv/row.h"
#include "libyuv/video_common.h"
#ifdef __cplusplus
namespace libyuv {
@ -155,6 +156,176 @@ void Ssim4x4Row_NEON(const uint8_t* src_a,
}
#undef SSIM_ROW_NEON32
static const uvec32 kDjb2Multiplicands[] = {
{0x0c3525e1, // 33^15
0xa3476dc1, // 33^14
0x3b4039a1, // 33^13
0x4f5f0981}, // 33^12
{0x30f35d61, // 33^11
0x855cb541, // 33^10
0x040a9121, // 33^9
0x747c7101}, // 33^8
{0xec41d4e1, // 33^7
0x4cfa3cc1, // 33^6
0x025528a1, // 33^5
0x00121881}, // 33^4
{0x00008c61, // 33^3
0x00000441, // 33^2
0x00000021, // 33^1
0x00000001}, // 33^0
};
uint32_t HashDjb2_NEON(const uint8_t* src, int count, uint32_t seed) {
uint32_t hash = seed;
const uint32_t c16 = 0x92d9e201; // 33^16
const void* muls0 = &kDjb2Multiplicands[0];
const void* muls1 = &kDjb2Multiplicands[2];
uint32_t tmp;
asm volatile(
"vld1.32 {d16, d17, d18, d19}, [%[muls0]] \n"
"vld1.32 {d20, d21, d22, d23}, [%[muls1]] \n"
"1: \n"
"vld1.8 {d0, d1}, [%[src]]! \n"
"subs %[count], %[count], #16 \n"
"vmovl.u8 q1, d0 \n"
"vmovl.u8 q2, d1 \n"
"vmovl.u16 q0, d2 \n"
"vmovl.u16 q1, d3 \n"
"vmovl.u16 q3, d5 \n"
"vmovl.u16 q2, d4 \n"
"vmul.i32 q0, q0, q8 \n"
"vmla.i32 q0, q1, q9 \n"
"vmla.i32 q0, q2, q10 \n"
"vmla.i32 q0, q3, q11 \n"
"vpadd.i32 d0, d0, d1 \n"
"vpadd.i32 d0, d0, d0 \n"
"vmov.32 %[tmp], d0[0] \n"
"mla %[hash], %[hash], %[c16], %[tmp] \n"
"bgt 1b \n"
:
[src] "+r"(src), [hash] "+r"(hash), [count] "+r"(count), [tmp] "=&r"(tmp)
: [muls0] "r"(muls0), [muls1] "r"(muls1), [c16] "r"(c16)
: "memory", "cc", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11");
return hash;
}
float SsimWindowRow_NEON(const uint32_t* row0,
const uint32_t* row1,
int num_windows) {
const uint32_t* row0_next = row0 + 4;
const uint32_t* row1_next = row1 + 4;
float total;
asm volatile(
"vmov.i32 q12, #0 \n"
"vdup.32 q13, %[c1] \n"
"vdup.32 q14, %[c2] \n"
"1: \n"
"vld4.32 {d0, d2, d4, d6}, [%[row0]]! \n"
"vld4.32 {d1, d3, d5, d7}, [%[row0]]! \n"
"vld4.32 {d16, d18, d20, d22}, [%[row0_next]]! \n"
"vld4.32 {d17, d19, d21, d23}, [%[row0_next]]! \n"
"vadd.i32 q0, q0, q8 \n"
"vadd.i32 q1, q1, q9 \n"
"vadd.i32 q2, q2, q10 \n"
"vadd.i32 q3, q3, q11 \n"
"vld4.32 {d16, d18, d20, d22}, [%[row1]]! \n"
"vld4.32 {d17, d19, d21, d23}, [%[row1]]! \n"
"vadd.i32 q0, q0, q8 \n"
"vadd.i32 q1, q1, q9 \n"
"vadd.i32 q2, q2, q10 \n"
"vadd.i32 q3, q3, q11 \n"
"vld4.32 {d16, d18, d20, d22}, [%[row1_next]]! \n"
"vld4.32 {d17, d19, d21, d23}, [%[row1_next]]! \n"
"vadd.i32 q0, q0, q8 \n"
"vadd.i32 q1, q1, q9 \n"
"vadd.i32 q2, q2, q10 \n"
"vadd.i32 q3, q3, q11 \n"
"vmul.i32 q8, q0, q1 \n"
"vshl.i32 q8, q8, #1 \n"
"vmul.i32 q9, q0, q0 \n"
"vmla.i32 q9, q1, q1 \n"
"vshl.i32 q3, q3, #7 \n"
"vsub.i32 q3, q3, q8 \n"
"vadd.i32 q8, q8, q13 \n"
"vadd.i32 q3, q3, q14 \n"
"vshl.i32 q2, q2, #6 \n"
"vsub.i32 q2, q2, q9 \n"
"vadd.i32 q9, q9, q13 \n"
"vadd.i32 q2, q2, q14 \n"
"vcvt.f32.s32 q8, q8 \n"
"vcvt.f32.s32 q3, q3 \n"
"vcvt.f32.s32 q9, q9 \n"
"vcvt.f32.s32 q2, q2 \n"
"vmul.f32 q3, q8, q3 \n"
"vmul.f32 q2, q9, q2 \n"
"vdiv.f32 s12, s12, s8 \n"
"vdiv.f32 s13, s13, s9 \n"
"vdiv.f32 s14, s14, s10 \n"
"vdiv.f32 s15, s15, s11 \n"
"subs %[num_windows], %[num_windows], #4 \n"
"vadd.f32 q12, q12, q3 \n"
"bgt 1b \n"
"vpadd.f32 d0, d24, d25 \n"
"vpadd.f32 d0, d0, d0 \n"
"vmov.f32 %[total], s0 \n"
: [row0] "+r"(row0), [row0_next] "+r"(row0_next), [row1] "+r"(row1),
[row1_next] "+r"(row1_next), [num_windows] "+r"(num_windows),
[total] "=r"(total)
: [c1] "r"(26634), [c2] "r"(239708)
: "memory", "cc", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12",
"q13", "q14");
return total;
}
uint32_t ARGBDetectRow_NEON(const uint8_t* argb, int width) {
uint32_t diff_lo = 0;
uint32_t diff_hi = 0;
uint32_t tmp;
asm volatile(
"vmov.i32 q2, #0x00ff0000 \n"
"vorr.i32 q2, #0x0000ff00 \n"
"vmov.i8 q3, #0xff \n"
"subs %[width], %[width], #4 \n"
"blt 2f \n"
"1: \n"
"vld1.8 {q0}, [%[argb]]! \n"
"vorr q0, q0, q2 \n"
"vceq.i8 q0, q0, q3 \n"
"vmvn q0, q0 \n"
"vshrn.i16 d0, q0, #4 \n"
"vmov %[diff_lo], %[diff_hi], d0 \n"
"orrs %[tmp], %[diff_lo], %[diff_hi] \n"
"bne 99f \n"
"subs %[width], %[width], #4 \n"
"bge 1b \n"
"2: \n"
"adds %[width], %[width], #4 \n"
"ble 99f \n"
"3: \n"
"vld1.32 {d0[0]}, [%[argb]]! \n"
"vorr d0, d0, d4 \n"
"vceq.i8 d0, d0, d6 \n"
"vmvn d0, d0 \n"
"vshrn.i16 d0, q0, #4 \n"
"vmov.u16 %[diff_lo], d0[0] \n"
"cmp %[diff_lo], #0 \n"
"bne 99f \n"
"subs %[width], %[width], #1 \n"
"bgt 3b \n"
"99: \n"
: [argb] "+r"(argb), [width] "+r"(width), [diff_lo] "+r"(diff_lo),
[diff_hi] "+r"(diff_hi), [tmp] "=&r"(tmp)
:
: "memory", "cc", "q0", "q2", "q3");
if (diff_lo) {
return (__builtin_ctz(diff_lo) & 12) == 0 ? FOURCC_BGRA : FOURCC_ARGB;
}
if (diff_hi) {
return (__builtin_ctz(diff_hi) & 12) == 0 ? FOURCC_BGRA : FOURCC_ARGB;
}
return 0;
}
#endif // defined(__ARM_NEON__) && !defined(__aarch64__)
#ifdef __cplusplus

View File

@ -10,6 +10,7 @@
#include "libyuv/compare_row.h"
#include "libyuv/row.h"
#include "libyuv/video_common.h"
#ifdef __cplusplus
namespace libyuv {
@ -349,6 +350,107 @@ void Ssim4x4Row_NEON_DotProd(const uint8_t* src_a,
"v19");
}
float SsimWindowRow_NEON(const uint32_t* row0,
const uint32_t* row1,
int num_windows) {
const uint32_t* row0_next = row0 + 4;
const uint32_t* row1_next = row1 + 4;
float total;
asm volatile(
"movi v24.4s, #0 \n"
"dup v25.4s, %w[c1] \n"
"dup v26.4s, %w[c2] \n"
"1: \n"
"ld4 {v0.4s, v1.4s, v2.4s, v3.4s}, [%[row0]], #64 \n"
"ld4 {v4.4s, v5.4s, v6.4s, v7.4s}, [%[row0_next]], #64 \n"
"ld4 {v16.4s, v17.4s, v18.4s, v19.4s}, [%[row1]], #64 \n"
"ld4 {v20.4s, v21.4s, v22.4s, v23.4s}, [%[row1_next]], #64 \n"
"add v0.4s, v0.4s, v4.4s \n"
"add v1.4s, v1.4s, v5.4s \n"
"add v2.4s, v2.4s, v6.4s \n"
"add v3.4s, v3.4s, v7.4s \n"
"add v16.4s, v16.4s, v20.4s \n"
"add v17.4s, v17.4s, v21.4s \n"
"add v18.4s, v18.4s, v22.4s \n"
"add v19.4s, v19.4s, v23.4s \n"
"add v0.4s, v0.4s, v16.4s \n"
"add v1.4s, v1.4s, v17.4s \n"
"add v2.4s, v2.4s, v18.4s \n"
"add v3.4s, v3.4s, v19.4s \n"
"mul v4.4s, v0.4s, v1.4s \n"
"shl v4.4s, v4.4s, #1 \n"
"mul v5.4s, v0.4s, v0.4s \n"
"mla v5.4s, v1.4s, v1.4s \n"
"shl v3.4s, v3.4s, #7 \n"
"sub v3.4s, v3.4s, v4.4s \n"
"add v4.4s, v4.4s, v25.4s \n"
"add v3.4s, v3.4s, v26.4s \n"
"shl v2.4s, v2.4s, #6 \n"
"sub v2.4s, v2.4s, v5.4s \n"
"add v5.4s, v5.4s, v25.4s \n"
"add v2.4s, v2.4s, v26.4s \n"
"scvtf v4.4s, v4.4s \n"
"scvtf v3.4s, v3.4s \n"
"scvtf v5.4s, v5.4s \n"
"scvtf v2.4s, v2.4s \n"
"fmul v4.4s, v4.4s, v3.4s \n"
"fmul v5.4s, v5.4s, v2.4s \n"
"fdiv v4.4s, v4.4s, v5.4s \n"
"subs %w[num_windows], %w[num_windows], #4 \n"
"fadd v24.4s, v24.4s, v4.4s \n"
"b.gt 1b \n"
"faddp v24.4s, v24.4s, v24.4s \n"
"faddp %s[total], v24.2s \n"
: [row0] "+r"(row0), [row0_next] "+r"(row0_next), [row1] "+r"(row1),
[row1_next] "+r"(row1_next), [num_windows] "+r"(num_windows),
[total] "=w"(total)
: [c1] "r"(26634), [c2] "r"(239708)
: "memory", "cc", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v16",
"v17", "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26");
return total;
}
uint32_t ARGBDetectRow_NEON(const uint8_t* argb, int width) {
uint64_t diff = 0;
asm volatile(
"movi v2.2d, #0x00ffff0000ffff00 \n"
"movi v3.16b, #0xff \n"
"subs %w[width], %w[width], #4 \n"
"b.lt 2f \n"
"1: \n"
"ldr q0, [%[argb]], #16 \n"
"orr v0.16b, v0.16b, v2.16b \n"
"cmeq v0.16b, v0.16b, v3.16b \n"
"mvn v0.16b, v0.16b \n"
"shrn v0.8b, v0.8h, #4 \n"
"fmov %x[diff], d0 \n"
"cbnz %x[diff], 99f \n"
"subs %w[width], %w[width], #4 \n"
"b.ge 1b \n"
"2: \n"
"adds %w[width], %w[width], #4 \n"
"b.le 99f \n"
"3: \n"
"ldr s0, [%[argb]], #4 \n"
"orr v0.8b, v0.8b, v2.8b \n"
"cmeq v0.8b, v0.8b, v3.8b \n"
"mvn v0.8b, v0.8b \n"
"shrn v0.8b, v0.8h, #4 \n"
"fmov %x[diff], d0 \n"
"and %x[diff], %x[diff], #0xffff \n"
"cbnz %x[diff], 99f \n"
"subs %w[width], %w[width], #1 \n"
"b.gt 3b \n"
"99: \n"
: [argb] "+r"(argb), [width] "+r"(width), [diff] "+r"(diff)
:
: "memory", "cc", "v0", "v2", "v3");
if (diff) {
return (__builtin_ctzll(diff) & 12) == 0 ? FOURCC_BGRA : FOURCC_ARGB;
}
return 0;
}
#endif // !defined(LIBYUV_DISABLE_NEON) && defined(__aarch64__)
#ifdef __cplusplus

View File

@ -10,6 +10,7 @@
#include "libyuv/compare_row.h"
#include "libyuv/row.h"
#include "libyuv/video_common.h"
#if !defined(LIBYUV_DISABLE_WASM) && defined(__wasm_simd128__)
#include <wasm_simd128.h>
@ -27,27 +28,37 @@ static inline uint32_t SumLanes_WASMSIMD(v128_t sum) {
}
#if defined(HAS_HAMMINGDISTANCE_WASMSIMD)
// Bit counts of 64 bytes are added as bytes, then pairs of bytes are added
// to 16 bit sums, which hold up to 64 per 64 bytes, so count can be up to
// 65535. Count is a multiple of 64.
// Bit counts of 64 bytes are added as bytes, then accumulated into 32 bit
// lanes using relaxed i8x16 dot product when available or pairwise widening
// adds. Count is a multiple of 64.
uint32_t HammingDistance_WASMSIMD(const uint8_t* src_a,
const uint8_t* src_b,
int count) {
v128_t sum = wasm_i16x8_splat(0);
v128_t sum = wasm_i32x4_splat(0);
#ifdef __wasm_relaxed_simd__
const v128_t ones = wasm_i8x16_splat(1);
#endif
while (count > 0) {
v128_t c = wasm_i8x16_splat(0);
int i;
for (i = 0; i < 64; i += 16) {
c = wasm_i8x16_add(
c, wasm_i8x16_popcnt(wasm_v128_xor(wasm_v128_load(src_a + i),
wasm_v128_load(src_b + i))));
}
sum = wasm_i16x8_add(sum, wasm_u16x8_extadd_pairwise_u8x16(c));
v128_t c0 = wasm_i8x16_popcnt(
wasm_v128_xor(wasm_v128_load(src_a), wasm_v128_load(src_b)));
v128_t c1 = wasm_i8x16_popcnt(
wasm_v128_xor(wasm_v128_load(src_a + 16), wasm_v128_load(src_b + 16)));
v128_t c2 = wasm_i8x16_popcnt(
wasm_v128_xor(wasm_v128_load(src_a + 32), wasm_v128_load(src_b + 32)));
v128_t c3 = wasm_i8x16_popcnt(
wasm_v128_xor(wasm_v128_load(src_a + 48), wasm_v128_load(src_b + 48)));
v128_t c = wasm_i8x16_add(wasm_i8x16_add(c0, c1), wasm_i8x16_add(c2, c3));
#ifdef __wasm_relaxed_simd__
sum = wasm_i32x4_relaxed_dot_i8x16_i7x16_add(c, ones, sum);
#else
sum = wasm_i32x4_add(sum, wasm_u32x4_extadd_pairwise_u16x8(
wasm_u16x8_extadd_pairwise_u8x16(c)));
#endif
src_a += 64;
src_b += 64;
count -= 64;
}
return SumLanes_WASMSIMD(wasm_u32x4_extadd_pairwise_u16x8(sum));
return SumLanes_WASMSIMD(sum);
}
#endif // HAS_HAMMINGDISTANCE_WASMSIMD
@ -241,6 +252,91 @@ void Ssim4x4Row_WASMSIMD(const uint8_t* src_a,
}
#endif // HAS_SSIM4X4ROW_WASMSIMD
#if defined(HAS_SSIMWINDOWROW_WASMSIMD)
float SsimWindowRow_WASMSIMD(const uint32_t* row0,
const uint32_t* row1,
int num_windows) {
const v128_t vc1 = wasm_i32x4_splat(26634);
const v128_t vc2 = wasm_i32x4_splat(239708);
v128_t total = wasm_f32x4_splat(0.0f);
v128_t t0 = wasm_i32x4_add(wasm_v128_load(row0), wasm_v128_load(row1));
int w;
for (w = 0; w < num_windows; w += 4) {
v128_t t1 =
wasm_i32x4_add(wasm_v128_load(row0 + 4), wasm_v128_load(row1 + 4));
v128_t t2 =
wasm_i32x4_add(wasm_v128_load(row0 + 8), wasm_v128_load(row1 + 8));
v128_t t3 =
wasm_i32x4_add(wasm_v128_load(row0 + 12), wasm_v128_load(row1 + 12));
v128_t t4 =
wasm_i32x4_add(wasm_v128_load(row0 + 16), wasm_v128_load(row1 + 16));
row0 += 16;
row1 += 16;
v128_t w0 = wasm_i32x4_add(t0, t1);
v128_t w1 = wasm_i32x4_add(t1, t2);
v128_t w2 = wasm_i32x4_add(t2, t3);
v128_t w3 = wasm_i32x4_add(t3, t4);
t0 = t4;
v128_t u0 = wasm_i32x4_shuffle(w0, w1, 0, 4, 1, 5);
v128_t u1 = wasm_i32x4_shuffle(w0, w1, 2, 6, 3, 7);
v128_t u2 = wasm_i32x4_shuffle(w2, w3, 0, 4, 1, 5);
v128_t u3 = wasm_i32x4_shuffle(w2, w3, 2, 6, 3, 7);
v128_t sum_a = wasm_i64x2_shuffle(u0, u2, 0, 2);
v128_t sum_b = wasm_i64x2_shuffle(u0, u2, 1, 3);
v128_t sum_sq = wasm_i64x2_shuffle(u1, u3, 0, 2);
v128_t sum_axb = wasm_i64x2_shuffle(u1, u3, 1, 3);
v128_t ab2 = wasm_i32x4_shl(wasm_i32x4_dot_i16x8(sum_a, sum_b), 1);
v128_t a2b2 = wasm_i32x4_add(wasm_i32x4_dot_i16x8(sum_a, sum_a),
wasm_i32x4_dot_i16x8(sum_b, sum_b));
v128_t n1 = wasm_i32x4_add(ab2, vc1);
v128_t n2 =
wasm_i32x4_add(wasm_i32x4_sub(wasm_i32x4_shl(sum_axb, 7), ab2), vc2);
v128_t d1 = wasm_i32x4_add(a2b2, vc1);
v128_t d2 =
wasm_i32x4_add(wasm_i32x4_sub(wasm_i32x4_shl(sum_sq, 6), a2b2), vc2);
v128_t num = wasm_f32x4_mul(wasm_f32x4_convert_i32x4(n1),
wasm_f32x4_convert_i32x4(n2));
v128_t den = wasm_f32x4_mul(wasm_f32x4_convert_i32x4(d1),
wasm_f32x4_convert_i32x4(d2));
total = wasm_f32x4_add(total, wasm_f32x4_div(num, den));
}
total = wasm_f32x4_add(total, wasm_i64x2_shuffle(total, total, 1, 0));
total = wasm_f32x4_add(total, wasm_i32x4_shuffle(total, total, 1, 0, 3, 2));
return wasm_f32x4_extract_lane(total, 0);
}
#endif // HAS_SSIMWINDOWROW_WASMSIMD
#if defined(HAS_ARGBDETECTROW_WASMSIMD)
uint32_t ARGBDetectRow_WASMSIMD(const uint8_t* argb, int width) {
const v128_t mask_rgb = wasm_i32x4_splat(0x00ffff00);
const v128_t ones = wasm_i8x16_splat(-1);
while (width >= 4) {
v128_t v = wasm_v128_or(wasm_v128_load(argb), mask_rgb);
uint32_t diff =
(~(uint32_t)wasm_i8x16_bitmask(wasm_i8x16_eq(v, ones))) & 0xffffu;
if (diff) {
return (__builtin_ctz(diff) & 3) == 0 ? FOURCC_BGRA : FOURCC_ARGB;
}
argb += 16;
width -= 4;
}
while (width > 0) {
v128_t v = wasm_v128_or(wasm_v128_load32_zero(argb), mask_rgb);
uint32_t diff =
(~(uint32_t)wasm_i8x16_bitmask(wasm_i8x16_eq(v, ones))) & 0x9u;
if (diff) {
return (diff & 1u) ? FOURCC_BGRA : FOURCC_ARGB;
}
argb += 4;
width -= 1;
}
return 0;
}
#endif // HAS_ARGBDETECTROW_WASMSIMD
#ifdef __cplusplus
} // extern "C"
} // namespace libyuv

View File

@ -75,8 +75,11 @@ uint32_t HammingDistance_SSE42(const uint8_t* src_a,
}
#endif // HAS_HAMMINGDISTANCE_SSE42
#if defined(HAS_HAMMINGDISTANCE_AVX512BW) || \
defined(HAS_SUMSQUAREERROR_AVX512BW) || defined(HAS_HASHDJB2_AVX512BW)
#if defined(HAS_HAMMINGDISTANCE_AVX512BW) || \
defined(HAS_HAMMINGDISTANCE_AVX512VNNI) || \
defined(HAS_SUMSQUAREERROR_AVX512BW) || \
defined(HAS_SUMSQUAREERROR_AVX512VNNI) || \
defined(HAS_HASHDJB2_AVX512BW) || defined(HAS_SSIM4X4ROW_AVX512BW)
// Mask of the low n bytes, for n = 0 to 64.
static __mmask64 LowBytesMask64(int n) {
return (__mmask64)(n >= 64 ? ~0ULL : (1ULL << n) - 1);
@ -161,6 +164,46 @@ uint32_t HammingDistance_AVX512BW(const uint8_t* src_a,
}
#endif // HAS_HAMMINGDISTANCE_AVX512BW
#if defined(HAS_HAMMINGDISTANCE_AVX512VNNI)
// Bit count of 64 bytes accumulated into 16 dwords with vpdpbusd.
LIBYUV_TARGET_AVX512VNNI
static __m512i BitCount64_AVX512VNNI(__m512i x, __m512i sum) {
const __m512i kNibbleMask = _mm512_set1_epi8(15);
const __m512i kOnes = _mm512_set1_epi8(1);
const __m512i kBitCount = _mm512_broadcast_i32x4(
_mm_setr_epi8(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4));
__m512i lo = _mm512_shuffle_epi8(kBitCount, _mm512_and_si512(x, kNibbleMask));
__m512i hi = _mm512_shuffle_epi8(
kBitCount, _mm512_and_si512(_mm512_srli_epi16(x, 4), kNibbleMask));
return _mm512_dpbusd_epi32(sum, kOnes, _mm512_add_epi8(lo, hi));
}
// Process 64 bytes per loop and the remainder of 1 to 63 bytes with masked
// loads that read zeros, so any count is supported.
LIBYUV_TARGET_AVX512VNNI
uint32_t HammingDistance_AVX512VNNI(const uint8_t* src_a,
const uint8_t* src_b,
int count) {
__m512i sum = _mm512_setzero_si512();
while (count >= 64) {
sum = BitCount64_AVX512VNNI(
_mm512_xor_si512(_mm512_loadu_si512(src_a), _mm512_loadu_si512(src_b)),
sum);
src_a += 64;
src_b += 64;
count -= 64;
}
if (count > 0) {
const __mmask64 k = LowBytesMask64(count);
sum = BitCount64_AVX512VNNI(
_mm512_xor_si512(_mm512_maskz_loadu_epi8(k, src_a),
_mm512_maskz_loadu_epi8(k, src_b)),
sum);
}
return (uint32_t)_mm512_reduce_add_epi32(sum);
}
#endif // HAS_HAMMINGDISTANCE_AVX512VNNI
#if defined(HAS_SUMSQUAREERROR_SSE2)
// Count is a multiple of 16.
LIBYUV_TARGET_SSE2
@ -262,8 +305,7 @@ uint32_t SumSquareError_AVX512VNNI(const uint8_t* src_a,
while (count >= 64) {
__m512i a = _mm512_loadu_si512(src_a);
__m512i b = _mm512_loadu_si512(src_b);
__m512i d =
_mm512_or_si512(_mm512_subs_epu8(a, b), _mm512_subs_epu8(b, a));
__m512i d = _mm512_or_si512(_mm512_subs_epu8(a, b), _mm512_subs_epu8(b, a));
__m512i lo = _mm512_unpacklo_epi8(d, zero);
__m512i hi = _mm512_unpackhi_epi8(d, zero);
sum0 = _mm512_dpwssd_epi32(sum0, lo, lo);
@ -276,8 +318,7 @@ uint32_t SumSquareError_AVX512VNNI(const uint8_t* src_a,
const __mmask64 k = LowBytesMask64(count);
__m512i a = _mm512_maskz_loadu_epi8(k, src_a);
__m512i b = _mm512_maskz_loadu_epi8(k, src_b);
__m512i d =
_mm512_or_si512(_mm512_subs_epu8(a, b), _mm512_subs_epu8(b, a));
__m512i d = _mm512_or_si512(_mm512_subs_epu8(a, b), _mm512_subs_epu8(b, a));
__m512i lo = _mm512_unpacklo_epi8(d, zero);
__m512i hi = _mm512_unpackhi_epi8(d, zero);
sum0 = _mm512_dpwssd_epi32(sum0, lo, lo);
@ -961,7 +1002,18 @@ uint32_t ARGBDetectRow_AVX2(const uint8_t* argb, int width) {
_mm256_zeroupper();
}
if (width > 0) {
return ARGBDetectRow_C(argb, width);
const __m128i mask_rgb = _mm_set1_epi32(0x00ffff00);
const __m128i ones = _mm_set1_epi8(-1);
do {
__m128i v = _mm_or_si128(_mm_loadu_si32(argb), mask_rgb);
uint32_t diff =
(~(uint32_t)_mm_movemask_epi8(_mm_cmpeq_epi8(v, ones))) & 0x9u;
if (diff) {
return (diff & 1u) ? FOURCC_BGRA : FOURCC_ARGB;
}
argb += 4;
width -= 1;
} while (width > 0);
}
return 0;
}

View File

@ -10,6 +10,7 @@
#include "libyuv/compare_row.h"
#include "libyuv/row.h"
#include "libyuv/video_common.h"
// Note: every register written by an inline asm block (vector, general
// purpose, mask/predicate, "cc" for flags and "memory" for stores) must be
@ -3289,14 +3290,266 @@ void Ssim4x4Row_LASX(const uint8_t* src_a,
"addi.d %[dst], %[dst], 128\n\t"
"addi.d %[cnt], %[cnt], -32\n\t"
"bnez %[cnt], 1b\n\t"
: [sa] "+r"(src_a), [sb] "+r"(src_b), [dst] "+r"(dst),
[cnt] "+r"(width), [pa] "=&r"(pa), [pb] "=&r"(pb), [r] "=&r"(r)
: [sa] "+r"(src_a), [sb] "+r"(src_b), [dst] "+r"(dst), [cnt] "+r"(width),
[pa] "=&r"(pa), [pb] "=&r"(pb), [r] "=&r"(r)
: [sta] "r"((ptrdiff_t)stride_a), [stb] "r"((ptrdiff_t)stride_b)
: "memory", "$xr0", "$xr1", "$xr2", "$xr3", "$xr4", "$xr5", "$xr6",
"$xr7", "$xr8", "$xr9", "$xr10", "$xr11");
}
#endif // HAS_SSIM4X4ROW_LASX
#ifdef HAS_SSIMWINDOWROW_LASX
float SsimWindowRow_LASX(const uint32_t* row0,
const uint32_t* row1,
int num_windows) {
const uint32_t* row0_next = row0 + 4;
const uint32_t* row1_next = row1 + 4;
float total;
uint32_t c1 = 26634;
uint32_t c2 = 239708;
uint32_t c0 = 0;
asm volatile(
"xvreplgr2vr.w $xr6, %[c0]\n\t"
"xvreplgr2vr.w $xr4, %[c1]\n\t"
"xvreplgr2vr.w $xr5, %[c2]\n\t"
"addi.d %[cnt], %[cnt], -2\n\t"
"bltz %[cnt], 2f\n\t"
"1:\n\t"
"xvld $xr0, %[r0], 0\n\t"
"xvld $xr1, %[r0n], 0\n\t"
"xvld $xr2, %[r1], 0\n\t"
"xvld $xr3, %[r1n], 0\n\t"
"xvadd.w $xr0, $xr0, $xr1\n\t"
"xvadd.w $xr2, $xr2, $xr3\n\t"
"xvadd.w $xr0, $xr0, $xr2\n\t"
"xvshuf4i.w $xr1, $xr0, 0x00\n\t"
"xvshuf4i.w $xr2, $xr0, 0x55\n\t"
"xvshuf4i.w $xr3, $xr0, 0xaa\n\t"
"xvshuf4i.w $xr0, $xr0, 0xff\n\t"
"xvmul.w $xr7, $xr1, $xr2\n\t"
"xvslli.w $xr7, $xr7, 1\n\t"
"xvmul.w $xr1, $xr1, $xr1\n\t"
"xvmadd.w $xr1, $xr2, $xr2\n\t"
"xvslli.w $xr0, $xr0, 7\n\t"
"xvsub.w $xr0, $xr0, $xr7\n\t"
"xvadd.w $xr7, $xr7, $xr4\n\t"
"xvadd.w $xr0, $xr0, $xr5\n\t"
"xvslli.w $xr3, $xr3, 6\n\t"
"xvsub.w $xr3, $xr3, $xr1\n\t"
"xvadd.w $xr1, $xr1, $xr4\n\t"
"xvadd.w $xr3, $xr3, $xr5\n\t"
"xvffint.s.w $xr7, $xr7\n\t"
"xvffint.s.w $xr0, $xr0\n\t"
"xvffint.s.w $xr1, $xr1\n\t"
"xvffint.s.w $xr3, $xr3\n\t"
"xvfmul.s $xr7, $xr7, $xr0\n\t"
"xvfmul.s $xr1, $xr1, $xr3\n\t"
"xvfdiv.s $xr7, $xr7, $xr1\n\t"
"xvfadd.s $xr6, $xr6, $xr7\n\t"
"addi.d %[r0], %[r0], 32\n\t"
"addi.d %[r0n], %[r0n], 32\n\t"
"addi.d %[r1], %[r1], 32\n\t"
"addi.d %[r1n], %[r1n], 32\n\t"
"addi.d %[cnt], %[cnt], -2\n\t"
"bgez %[cnt], 1b\n\t"
"xvpermi.q $xr0, $xr6, 0x01\n\t"
"vfadd.s $vr6, $vr6, $vr0\n\t"
"2:\n\t"
"addi.d %[cnt], %[cnt], 2\n\t"
"beqz %[cnt], 3f\n\t"
"vld $vr0, %[r0], 0\n\t"
"vld $vr1, %[r0n], 0\n\t"
"vld $vr2, %[r1], 0\n\t"
"vld $vr3, %[r1n], 0\n\t"
"vadd.w $vr0, $vr0, $vr1\n\t"
"vadd.w $vr2, $vr2, $vr3\n\t"
"vadd.w $vr0, $vr0, $vr2\n\t"
"vshuf4i.w $vr1, $vr0, 0x00\n\t"
"vshuf4i.w $vr2, $vr0, 0x55\n\t"
"vshuf4i.w $vr3, $vr0, 0xaa\n\t"
"vshuf4i.w $vr0, $vr0, 0xff\n\t"
"vmul.w $vr7, $vr1, $vr2\n\t"
"vslli.w $vr7, $vr7, 1\n\t"
"vmul.w $vr1, $vr1, $vr1\n\t"
"vmadd.w $vr1, $vr2, $vr2\n\t"
"vslli.w $vr0, $vr0, 7\n\t"
"vsub.w $vr0, $vr0, $vr7\n\t"
"vadd.w $vr7, $vr7, $vr4\n\t"
"vadd.w $vr0, $vr0, $vr5\n\t"
"vslli.w $vr3, $vr3, 6\n\t"
"vsub.w $vr3, $vr3, $vr1\n\t"
"vadd.w $vr1, $vr1, $vr4\n\t"
"vadd.w $vr3, $vr3, $vr5\n\t"
"vffint.s.w $vr7, $vr7\n\t"
"vffint.s.w $vr0, $vr0\n\t"
"vffint.s.w $vr1, $vr1\n\t"
"vffint.s.w $vr3, $vr3\n\t"
"vfmul.s $vr7, $vr7, $vr0\n\t"
"vfmul.s $vr1, $vr1, $vr3\n\t"
"vfdiv.s $vr7, $vr7, $vr1\n\t"
"vfadd.s $vr6, $vr6, $vr7\n\t"
"3:\n\t"
"vstelm.w $vr6, %[tot], 0, 0\n\t"
: [r0] "+r"(row0), [r0n] "+r"(row0_next), [r1] "+r"(row1),
[r1n] "+r"(row1_next), [cnt] "+r"(num_windows)
: [c0] "r"(c0), [c1] "r"(c1), [c2] "r"(c2), [tot] "r"(&total)
: "memory", "$xr0", "$xr1", "$xr2", "$xr3", "$xr4", "$xr5", "$xr6",
"$xr7");
return total;
}
#endif // HAS_SSIMWINDOWROW_LASX
#ifdef HAS_ARGBDETECTROW_LASX
uint32_t ARGBDetectRow_LASX(const uint8_t* argb, int width) {
uint32_t diff = 0;
uint32_t tmp;
uint32_t rgb_mask = 0x00ffff00u;
asm volatile(
"xvreplgr2vr.w $xr2, %[rgb_mask]\n\t"
"xvldi $xr3, -1\n\t"
"addi.d %[w], %[w], -8\n\t"
"bltz %[w], 2f\n\t"
"1:\n\t"
"xvld $xr0, %[argb], 0\n\t"
"addi.d %[argb], %[argb], 32\n\t"
"xvor.v $xr0, $xr0, $xr2\n\t"
"xvseq.b $xr0, $xr0, $xr3\n\t"
"xvmskltz.b $xr0, $xr0\n\t"
"xvpickve2gr.wu %[diff], $xr0, 0\n\t"
"xvpickve2gr.wu %[tmp], $xr0, 4\n\t"
"bstrins.w %[diff], %[tmp], 31, 16\n\t"
"nor %[diff], %[diff], $zero\n\t"
"bnez %[diff], 4f\n\t"
"addi.d %[w], %[w], -8\n\t"
"bgez %[w], 1b\n\t"
"2:\n\t"
"addi.d %[w], %[w], 8\n\t"
"beqz %[w], 4f\n\t"
"3:\n\t"
"vldrepl.w $vr0, %[argb], 0\n\t"
"addi.d %[argb], %[argb], 4\n\t"
"vor.v $vr0, $vr0, $vr2\n\t"
"vseq.b $vr0, $vr0, $vr3\n\t"
"vmskltz.b $vr0, $vr0\n\t"
"vpickve2gr.hu %[diff], $vr0, 0\n\t"
"nor %[diff], %[diff], $zero\n\t"
"bstrpick.w %[diff], %[diff], 15, 0\n\t"
"bnez %[diff], 4f\n\t"
"addi.d %[w], %[w], -1\n\t"
"bnez %[w], 3b\n\t"
"4:\n\t"
: [argb] "+r"(argb), [w] "+r"(width), [diff] "+r"(diff), [tmp] "=&r"(tmp)
: [rgb_mask] "r"(rgb_mask)
: "memory", "$xr0", "$xr2", "$xr3");
if (diff) {
return (__builtin_ctz(diff) & 3) == 0 ? FOURCC_BGRA : FOURCC_ARGB;
}
return 0;
}
#endif // HAS_ARGBDETECTROW_LASX
#ifdef HAS_HAMMINGDISTANCE_LASX
uint32_t HammingDistance_LASX(const uint8_t* src_a,
const uint8_t* src_b,
int count) {
uint32_t diff;
asm volatile(
"xvxor.v $xr4, $xr4, $xr4\n\t"
"1:\n\t"
"xvld $xr0, %[sa], 0\n\t"
"xvld $xr1, %[sb], 0\n\t"
"xvxor.v $xr0, $xr0, $xr1\n\t"
"xvpcnt.b $xr0, $xr0\n\t"
"xvhaddw.hu.bu $xr0, $xr0, $xr0\n\t"
"xvhaddw.wu.hu $xr0, $xr0, $xr0\n\t"
"xvadd.w $xr4, $xr4, $xr0\n\t"
"addi.d %[sa], %[sa], 32\n\t"
"addi.d %[sb], %[sb], 32\n\t"
"addi.d %[cnt], %[cnt], -32\n\t"
"bnez %[cnt], 1b\n\t"
"xvhaddw.du.wu $xr4, $xr4, $xr4\n\t"
"xvhaddw.qu.du $xr4, $xr4, $xr4\n\t"
"xvpermi.q $xr0, $xr4, 0x01\n\t"
"vadd.w $vr4, $vr4, $vr0\n\t"
"vpickve2gr.wu %[diff], $vr4, 0\n\t"
: [sa] "+r"(src_a), [sb] "+r"(src_b), [cnt] "+r"(count), [diff] "=r"(diff)
:
: "memory", "$xr0", "$xr1", "$xr4");
return diff;
}
#endif // HAS_HAMMINGDISTANCE_LASX
#ifdef HAS_SUMSQUAREERROR_LASX
uint32_t SumSquareError_LASX(const uint8_t* src_a,
const uint8_t* src_b,
int count) {
uint32_t sse;
asm volatile(
"xvxor.v $xr4, $xr4, $xr4\n\t"
"1:\n\t"
"xvld $xr0, %[sa], 0\n\t"
"xvld $xr1, %[sb], 0\n\t"
"xvabsd.bu $xr0, $xr0, $xr1\n\t"
"xvmulwev.h.bu $xr2, $xr0, $xr0\n\t"
"xvmulwod.h.bu $xr3, $xr0, $xr0\n\t"
"xvhaddw.wu.hu $xr2, $xr2, $xr2\n\t"
"xvhaddw.wu.hu $xr3, $xr3, $xr3\n\t"
"xvadd.w $xr4, $xr4, $xr2\n\t"
"xvadd.w $xr4, $xr4, $xr3\n\t"
"addi.d %[sa], %[sa], 32\n\t"
"addi.d %[sb], %[sb], 32\n\t"
"addi.d %[cnt], %[cnt], -32\n\t"
"bnez %[cnt], 1b\n\t"
"xvhaddw.du.wu $xr4, $xr4, $xr4\n\t"
"xvhaddw.qu.du $xr4, $xr4, $xr4\n\t"
"xvpermi.q $xr0, $xr4, 0x01\n\t"
"vadd.w $vr4, $vr4, $vr0\n\t"
"vpickve2gr.wu %[sse], $vr4, 0\n\t"
: [sa] "+r"(src_a), [sb] "+r"(src_b), [cnt] "+r"(count), [sse] "=r"(sse)
:
: "memory", "$xr0", "$xr1", "$xr2", "$xr3", "$xr4");
return sse;
}
#endif // HAS_SUMSQUAREERROR_LASX
#ifdef HAS_HASHDJB2_LASX
static const uint32_t kDjb2Muls_LASX[16] = {
0x0c3525e1, 0xa3476dc1, 0x3b4039a1, 0x4f5f0981, 0x30f35d61, 0x855cb541,
0x040a9121, 0x747c7101, 0xec41d4e1, 0x4cfa3cc1, 0x025528a1, 0x00121881,
0x00008c61, 0x00000441, 0x00000021, 0x00000001,
};
uint32_t HashDjb2_LASX(const uint8_t* src, int count, uint32_t seed) {
uint32_t hash = seed;
uint32_t tmp;
uint32_t c16 = 0x92d9e201u;
asm volatile(
"xvld $xr4, %[muls], 0\n\t"
"xvld $xr5, %[muls], 32\n\t"
"1:\n\t"
"vld $vr0, %[src], 0\n\t"
"vbsrl.v $vr1, $vr0, 8\n\t"
"vext2xv.wu.bu $xr0, $xr0\n\t"
"vext2xv.wu.bu $xr1, $xr1\n\t"
"xvmul.w $xr0, $xr0, $xr4\n\t"
"xvmadd.w $xr0, $xr1, $xr5\n\t"
"xvhaddw.du.wu $xr0, $xr0, $xr0\n\t"
"xvhaddw.qu.du $xr0, $xr0, $xr0\n\t"
"xvpermi.q $xr1, $xr0, 0x01\n\t"
"vadd.w $vr0, $vr0, $vr1\n\t"
"vpickve2gr.wu %[tmp], $vr0, 0\n\t"
"mul.w %[hash], %[hash], %[c16]\n\t"
"add.w %[hash], %[hash], %[tmp]\n\t"
"addi.d %[src], %[src], 16\n\t"
"addi.d %[cnt], %[cnt], -16\n\t"
"bnez %[cnt], 1b\n\t"
: [src] "+r"(src), [cnt] "+r"(count), [hash] "+r"(hash), [tmp] "=&r"(tmp)
: [muls] "r"(kDjb2Muls_LASX), [c16] "r"(c16)
: "memory", "$xr0", "$xr1", "$xr4", "$xr5");
return hash;
}
#endif // HAS_HASHDJB2_LASX
#ifdef __cplusplus
} // extern "C"
} // namespace libyuv

View File

@ -10,6 +10,7 @@
#include "libyuv/compare_row.h"
#include "libyuv/row.h"
#include "libyuv/video_common.h"
// Note: every register written by an inline asm block (vector, general
// purpose, mask/predicate, "cc" for flags and "memory" for stores) must be
@ -2882,14 +2883,227 @@ void Ssim4x4Row_LSX(const uint8_t* src_a,
"addi.d %[dst], %[dst], 64\n\t"
"addi.d %[cnt], %[cnt], -16\n\t"
"bnez %[cnt], 1b\n\t"
: [sa] "+r"(src_a), [sb] "+r"(src_b), [dst] "+r"(dst),
[cnt] "+r"(width), [pa] "=&r"(pa), [pb] "=&r"(pb), [r] "=&r"(r)
: [sa] "+r"(src_a), [sb] "+r"(src_b), [dst] "+r"(dst), [cnt] "+r"(width),
[pa] "=&r"(pa), [pb] "=&r"(pb), [r] "=&r"(r)
: [sta] "r"((ptrdiff_t)stride_a), [stb] "r"((ptrdiff_t)stride_b)
: "memory", "$vr0", "$vr1", "$vr2", "$vr3", "$vr4", "$vr5", "$vr6",
"$vr7", "$vr8", "$vr9", "$vr10", "$vr11");
}
#endif // HAS_SSIM4X4ROW_LSX
#ifdef HAS_SSIMWINDOWROW_LSX
float SsimWindowRow_LSX(const uint32_t* row0,
const uint32_t* row1,
int num_windows) {
const uint32_t* row0_next = row0 + 4;
const uint32_t* row1_next = row1 + 4;
float total;
uint32_t c1 = 26634;
uint32_t c2 = 239708;
uint32_t c0 = 0;
asm volatile(
"vreplgr2vr.w $vr6, %[c0]\n\t"
"vreplgr2vr.w $vr4, %[c1]\n\t"
"vreplgr2vr.w $vr5, %[c2]\n\t"
"1:\n\t"
"vld $vr0, %[r0], 0\n\t"
"vld $vr1, %[r0n], 0\n\t"
"vld $vr2, %[r1], 0\n\t"
"vld $vr3, %[r1n], 0\n\t"
"vadd.w $vr0, $vr0, $vr1\n\t"
"vadd.w $vr2, $vr2, $vr3\n\t"
"vadd.w $vr0, $vr0, $vr2\n\t"
"vshuf4i.w $vr1, $vr0, 0x00\n\t"
"vshuf4i.w $vr2, $vr0, 0x55\n\t"
"vshuf4i.w $vr3, $vr0, 0xaa\n\t"
"vshuf4i.w $vr0, $vr0, 0xff\n\t"
"vmul.w $vr7, $vr1, $vr2\n\t"
"vslli.w $vr7, $vr7, 1\n\t"
"vmul.w $vr1, $vr1, $vr1\n\t"
"vmadd.w $vr1, $vr2, $vr2\n\t"
"vslli.w $vr0, $vr0, 7\n\t"
"vsub.w $vr0, $vr0, $vr7\n\t"
"vadd.w $vr7, $vr7, $vr4\n\t"
"vadd.w $vr0, $vr0, $vr5\n\t"
"vslli.w $vr3, $vr3, 6\n\t"
"vsub.w $vr3, $vr3, $vr1\n\t"
"vadd.w $vr1, $vr1, $vr4\n\t"
"vadd.w $vr3, $vr3, $vr5\n\t"
"vffint.s.w $vr7, $vr7\n\t"
"vffint.s.w $vr0, $vr0\n\t"
"vffint.s.w $vr1, $vr1\n\t"
"vffint.s.w $vr3, $vr3\n\t"
"vfmul.s $vr7, $vr7, $vr0\n\t"
"vfmul.s $vr1, $vr1, $vr3\n\t"
"vfdiv.s $vr7, $vr7, $vr1\n\t"
"vfadd.s $vr6, $vr6, $vr7\n\t"
"addi.d %[r0], %[r0], 16\n\t"
"addi.d %[r0n], %[r0n], 16\n\t"
"addi.d %[r1], %[r1], 16\n\t"
"addi.d %[r1n], %[r1n], 16\n\t"
"addi.d %[cnt], %[cnt], -1\n\t"
"bnez %[cnt], 1b\n\t"
"vstelm.w $vr6, %[tot], 0, 0\n\t"
: [r0] "+r"(row0), [r0n] "+r"(row0_next), [r1] "+r"(row1),
[r1n] "+r"(row1_next), [cnt] "+r"(num_windows)
: [c0] "r"(c0), [c1] "r"(c1), [c2] "r"(c2), [tot] "r"(&total)
: "memory", "$vr0", "$vr1", "$vr2", "$vr3", "$vr4", "$vr5", "$vr6",
"$vr7");
return total;
}
#endif // HAS_SSIMWINDOWROW_LSX
#ifdef HAS_ARGBDETECTROW_LSX
uint32_t ARGBDetectRow_LSX(const uint8_t* argb, int width) {
uint32_t diff = 0;
uint32_t rgb_mask = 0x00ffff00u;
asm volatile(
"vreplgr2vr.w $vr2, %[rgb_mask]\n\t"
"vldi $vr3, -1\n\t"
"addi.d %[w], %[w], -4\n\t"
"bltz %[w], 2f\n\t"
"1:\n\t"
"vld $vr0, %[argb], 0\n\t"
"addi.d %[argb], %[argb], 16\n\t"
"vor.v $vr0, $vr0, $vr2\n\t"
"vseq.b $vr0, $vr0, $vr3\n\t"
"vmskltz.b $vr0, $vr0\n\t"
"vpickve2gr.hu %[diff], $vr0, 0\n\t"
"nor %[diff], %[diff], $zero\n\t"
"bstrpick.w %[diff], %[diff], 15, 0\n\t"
"bnez %[diff], 4f\n\t"
"addi.d %[w], %[w], -4\n\t"
"bgez %[w], 1b\n\t"
"2:\n\t"
"addi.d %[w], %[w], 4\n\t"
"beqz %[w], 4f\n\t"
"3:\n\t"
"vldrepl.w $vr0, %[argb], 0\n\t"
"addi.d %[argb], %[argb], 4\n\t"
"vor.v $vr0, $vr0, $vr2\n\t"
"vseq.b $vr0, $vr0, $vr3\n\t"
"vmskltz.b $vr0, $vr0\n\t"
"vpickve2gr.hu %[diff], $vr0, 0\n\t"
"nor %[diff], %[diff], $zero\n\t"
"bstrpick.w %[diff], %[diff], 15, 0\n\t"
"bnez %[diff], 4f\n\t"
"addi.d %[w], %[w], -1\n\t"
"bnez %[w], 3b\n\t"
"4:\n\t"
: [argb] "+r"(argb), [w] "+r"(width), [diff] "+r"(diff)
: [rgb_mask] "r"(rgb_mask)
: "memory", "$vr0", "$vr2", "$vr3");
if (diff) {
return (__builtin_ctz(diff) & 3) == 0 ? FOURCC_BGRA : FOURCC_ARGB;
}
return 0;
}
#endif // HAS_ARGBDETECTROW_LSX
#ifdef HAS_HAMMINGDISTANCE_LSX
uint32_t HammingDistance_LSX(const uint8_t* src_a,
const uint8_t* src_b,
int count) {
uint32_t diff;
asm volatile(
"vxor.v $vr4, $vr4, $vr4\n\t"
"1:\n\t"
"vld $vr0, %[sa], 0\n\t"
"vld $vr1, %[sb], 0\n\t"
"vxor.v $vr0, $vr0, $vr1\n\t"
"vpcnt.b $vr0, $vr0\n\t"
"vhaddw.hu.bu $vr0, $vr0, $vr0\n\t"
"vhaddw.wu.hu $vr0, $vr0, $vr0\n\t"
"vadd.w $vr4, $vr4, $vr0\n\t"
"addi.d %[sa], %[sa], 16\n\t"
"addi.d %[sb], %[sb], 16\n\t"
"addi.d %[cnt], %[cnt], -16\n\t"
"bnez %[cnt], 1b\n\t"
"vhaddw.du.wu $vr4, $vr4, $vr4\n\t"
"vhaddw.qu.du $vr4, $vr4, $vr4\n\t"
"vpickve2gr.wu %[diff], $vr4, 0\n\t"
: [sa] "+r"(src_a), [sb] "+r"(src_b), [cnt] "+r"(count), [diff] "=r"(diff)
:
: "memory", "$vr0", "$vr1", "$vr4");
return diff;
}
#endif // HAS_HAMMINGDISTANCE_LSX
#ifdef HAS_SUMSQUAREERROR_LSX
uint32_t SumSquareError_LSX(const uint8_t* src_a,
const uint8_t* src_b,
int count) {
uint32_t sse;
asm volatile(
"vxor.v $vr4, $vr4, $vr4\n\t"
"1:\n\t"
"vld $vr0, %[sa], 0\n\t"
"vld $vr1, %[sb], 0\n\t"
"vabsd.bu $vr0, $vr0, $vr1\n\t"
"vmulwev.h.bu $vr2, $vr0, $vr0\n\t"
"vmulwod.h.bu $vr3, $vr0, $vr0\n\t"
"vhaddw.wu.hu $vr2, $vr2, $vr2\n\t"
"vhaddw.wu.hu $vr3, $vr3, $vr3\n\t"
"vadd.w $vr4, $vr4, $vr2\n\t"
"vadd.w $vr4, $vr4, $vr3\n\t"
"addi.d %[sa], %[sa], 16\n\t"
"addi.d %[sb], %[sb], 16\n\t"
"addi.d %[cnt], %[cnt], -16\n\t"
"bnez %[cnt], 1b\n\t"
"vhaddw.du.wu $vr4, $vr4, $vr4\n\t"
"vhaddw.qu.du $vr4, $vr4, $vr4\n\t"
"vpickve2gr.wu %[sse], $vr4, 0\n\t"
: [sa] "+r"(src_a), [sb] "+r"(src_b), [cnt] "+r"(count), [sse] "=r"(sse)
:
: "memory", "$vr0", "$vr1", "$vr2", "$vr3", "$vr4");
return sse;
}
#endif // HAS_SUMSQUAREERROR_LSX
#ifdef HAS_HASHDJB2_LSX
static const uint32_t kDjb2Muls_LSX[16] = {
0x0c3525e1, 0xa3476dc1, 0x3b4039a1, 0x4f5f0981, 0x30f35d61, 0x855cb541,
0x040a9121, 0x747c7101, 0xec41d4e1, 0x4cfa3cc1, 0x025528a1, 0x00121881,
0x00008c61, 0x00000441, 0x00000021, 0x00000001,
};
uint32_t HashDjb2_LSX(const uint8_t* src, int count, uint32_t seed) {
uint32_t hash = seed;
uint32_t tmp;
uint32_t c16 = 0x92d9e201u;
asm volatile(
"vld $vr4, %[muls], 0\n\t"
"vld $vr5, %[muls], 16\n\t"
"vld $vr6, %[muls], 32\n\t"
"vld $vr7, %[muls], 48\n\t"
"1:\n\t"
"vld $vr0, %[src], 0\n\t"
"vsllwil.hu.bu $vr1, $vr0, 0\n\t"
"vexth.hu.bu $vr2, $vr0\n\t"
"vsllwil.wu.hu $vr0, $vr1, 0\n\t"
"vexth.wu.hu $vr1, $vr1\n\t"
"vsllwil.wu.hu $vr3, $vr2, 0\n\t"
"vexth.wu.hu $vr2, $vr2\n\t"
"vmul.w $vr0, $vr0, $vr4\n\t"
"vmadd.w $vr0, $vr1, $vr5\n\t"
"vmadd.w $vr0, $vr3, $vr6\n\t"
"vmadd.w $vr0, $vr2, $vr7\n\t"
"vhaddw.du.wu $vr0, $vr0, $vr0\n\t"
"vhaddw.qu.du $vr0, $vr0, $vr0\n\t"
"vpickve2gr.wu %[tmp], $vr0, 0\n\t"
"mul.w %[hash], %[hash], %[c16]\n\t"
"add.w %[hash], %[hash], %[tmp]\n\t"
"addi.d %[src], %[src], 16\n\t"
"addi.d %[cnt], %[cnt], -16\n\t"
"bnez %[cnt], 1b\n\t"
: [src] "+r"(src), [cnt] "+r"(count), [hash] "+r"(hash), [tmp] "=&r"(tmp)
: [muls] "r"(kDjb2Muls_LSX), [c16] "r"(c16)
: "memory", "$vr0", "$vr1", "$vr2", "$vr3", "$vr4", "$vr5", "$vr6",
"$vr7");
return hash;
}
#endif // HAS_HASHDJB2_LSX
#ifdef __cplusplus
} // extern "C"
} // namespace libyuv

View File

@ -17,6 +17,7 @@
#include "libyuv/compare_row.h"
#include "libyuv/row.h"
#include "libyuv/video_common.h"
// Note: every register written by an inline asm block (vector, general
// purpose, mask/predicate, "cc" for flags and "memory" for stores) must be
@ -3015,7 +3016,7 @@ float SsimWindowRow_RVV(const uint32_t* row0,
[total] "=f"(total) // %[total]
: [c1] "r"(26634), // %[c1]
[c2] "r"(239708) // %[c2]
: RVV_VL_CLOBBER "vtype", "memory", "v0", "v1", "v4", "v8", "v9", "v10",
: "memory", RVV_VL_CLOBBER "vtype", "v0", "v1", "v4", "v8", "v9", "v10",
"v11", "v12", "v13", "v14", "v15", "v16", "v17", "v18", "v19", "v20",
"v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30",
"v31");
@ -3023,6 +3024,173 @@ float SsimWindowRow_RVV(const uint32_t* row0,
}
#endif // HAS_SSIMWINDOWROW_RVV
#ifdef HAS_ARGBDETECTROW_RVV
uint32_t ARGBDetectRow_RVV(const uint8_t* argb, int width) {
size_t vl;
size_t byte_vl;
long idx = -1;
asm volatile(
"1: \n"
"vsetvli %[vl], %[w], e32, m8, ta, ma \n"
"slli %[byte_vl], %[vl], 2 \n"
"vsetvli zero, %[byte_vl], e8, m8, ta, ma \n"
"vle8.v v8, (%[argb]) \n"
"vsetvli zero, %[vl], e32, m8, ta, ma \n"
"vor.vx v8, v8, %[mask_rgb] \n"
"sub %[w], %[w], %[vl] \n"
"add %[argb], %[argb], %[byte_vl] \n"
"vsetvli zero, %[byte_vl], e8, m8, ta, ma \n"
"vmsne.vi v0, v8, -1 \n"
"vfirst.m %[idx], v0 \n"
"bgez %[idx], 2f \n"
"bgtz %[w], 1b \n"
"2: \n"
: [argb] "+r"(argb), [w] "+r"(width), [vl] "=&r"(vl),
[byte_vl] "=&r"(byte_vl), [idx] "+r"(idx)
: [mask_rgb] "r"(0x00ffff00u)
: "memory", RVV_VL_CLOBBER "vtype", "v0", "v8", "v9", "v10", "v11", "v12",
"v13", "v14", "v15");
if (idx >= 0) {
return (idx & 3) == 0 ? FOURCC_BGRA : FOURCC_ARGB;
}
return 0;
}
#endif // HAS_ARGBDETECTROW_RVV
#ifdef HAS_SUMSQUAREERROR_RVV
uint32_t SumSquareError_RVV(const uint8_t* src_a,
const uint8_t* src_b,
int count) {
size_t vl;
uint32_t sse;
asm volatile(
"vsetvli %[vl], zero, e32, m8, ta, ma \n"
"vmv.v.i v24, 0 \n"
"vmv.s.x v0, zero \n"
"1: \n"
"vsetvli %[vl], %[count], e8, m2, tu, ma \n"
"vle8.v v8, (%[src_a]) \n"
"vle8.v v10, (%[src_b]) \n"
"vwsubu.vv v12, v8, v10 \n"
"sub %[count], %[count], %[vl] \n"
"add %[src_a], %[src_a], %[vl] \n"
"add %[src_b], %[src_b], %[vl] \n"
"vsetvli zero, %[vl], e16, m4, tu, ma \n"
"vwmacc.vv v24, v12, v12 \n"
"bgtz %[count], 1b \n"
"vsetvli %[vl], zero, e32, m8, ta, ma \n"
"vredsum.vs v0, v24, v0 \n"
"vmv.x.s %[sse], v0 \n"
: [src_a] "+r"(src_a), [src_b] "+r"(src_b), [count] "+r"(count),
[vl] "=&r"(vl), [sse] "=r"(sse)
:
: "memory", RVV_VL_CLOBBER "vtype", "v0", "v8", "v9", "v10", "v11", "v12",
"v13", "v14", "v15", "v24", "v25", "v26", "v27", "v28", "v29", "v30",
"v31");
return sse;
}
#endif // HAS_SUMSQUAREERROR_RVV
#ifdef HAS_HAMMINGDISTANCE_RVV
static const uint8_t kNibblePopcnt_RVV[16] = {0, 1, 1, 2, 1, 2, 2, 3,
1, 2, 2, 3, 2, 3, 3, 4};
uint32_t HammingDistance_RVV(const uint8_t* src_a,
const uint8_t* src_b,
int count) {
size_t vl;
uint32_t diff;
asm volatile(
"vsetivli zero, 16, e8, m1, ta, ma \n"
"vle8.v v4, (%[lut]) \n"
"vsetvli %[vl], zero, e32, m4, ta, ma \n"
"vmv.v.i v24, 0 \n"
"vsetvli %[vl], zero, e32, m1, ta, ma \n"
"vmv.s.x v0, zero \n"
"1: \n"
"vsetvli %[vl], %[count], e8, m1, tu, ma \n"
"vle8.v v8, (%[src_a]) \n"
"vle8.v v9, (%[src_b]) \n"
"vxor.vv v8, v8, v9 \n"
"vand.vi v10, v8, 15 \n"
"vsrl.vi v8, v8, 4 \n"
"vrgather.vv v11, v4, v10 \n"
"vrgather.vv v12, v4, v8 \n"
"vwaddu.vv v16, v11, v12 \n"
"sub %[count], %[count], %[vl] \n"
"add %[src_a], %[src_a], %[vl] \n"
"add %[src_b], %[src_b], %[vl] \n"
"vsetvli zero, %[vl], e16, m2, tu, ma \n"
"vwaddu.wv v24, v24, v16 \n"
"bgtz %[count], 1b \n"
"vsetvli %[vl], zero, e32, m4, ta, ma \n"
"vredsum.vs v0, v24, v0 \n"
"vsetvli %[vl], zero, e32, m1, ta, ma \n"
"vmv.x.s %[diff], v0 \n"
: [src_a] "+r"(src_a), [src_b] "+r"(src_b), [count] "+r"(count),
[vl] "=&r"(vl), [diff] "=r"(diff)
: [lut] "r"(kNibblePopcnt_RVV)
: "memory", RVV_VL_CLOBBER "vtype", "v0", "v4", "v8", "v9", "v10", "v11",
"v12", "v16", "v17", "v24", "v25", "v26", "v27");
return diff;
}
#endif // HAS_HAMMINGDISTANCE_RVV
#ifdef HAS_HASHDJB2_RVV
static const uint32_t kDjb2Muls_RVV[17] = {
0x92d9e201, // 33 ^ 16
0x0c3525e1, 0xa3476dc1, 0x3b4039a1, 0x4f5f0981, 0x30f35d61, 0x855cb541,
0x040a9121, 0x747c7101, 0xec41d4e1, 0x4cfa3cc1, 0x025528a1, 0x00121881,
0x00008c61, 0x00000441, 0x00000021, 0x00000001,
};
uint32_t HashDjb2_RVV(const uint8_t* src, int count, uint32_t seed) {
uint32_t hash = seed;
uint32_t tmp;
const uint32_t c16 = 0x92d9e201u;
const int rem = count & 15;
const uint32_t* tail_muls = &kDjb2Muls_RVV[17 - rem];
const uint32_t pow_n = kDjb2Muls_RVV[16 - rem];
asm volatile(
"vsetivli zero, 16, e32, m4, ta, ma \n"
"vmv.s.x v4, zero \n"
"addi %[count], %[count], -16 \n"
"bltz %[count], 2f \n"
"vle32.v v16, (%[muls16]) \n"
"1: \n"
"vle8.v v8, (%[src]) \n"
"vzext.vf4 v12, v8 \n"
"vmul.vv v12, v12, v16 \n"
"vredsum.vs v0, v12, v4 \n"
"vmv.x.s %[tmp], v0 \n"
"mul %[hash], %[hash], %[c16] \n"
"add %[hash], %[hash], %[tmp] \n"
"addi %[src], %[src], 16 \n"
"addi %[count], %[count], -16 \n"
"bgez %[count], 1b \n"
"2: \n"
"addi %[count], %[count], 16 \n"
"beqz %[count], 3f \n"
"vsetvli zero, %[count], e32, m4, ta, ma \n"
"vle32.v v16, (%[tail_muls]) \n"
"vle8.v v8, (%[src]) \n"
"vzext.vf4 v12, v8 \n"
"vmul.vv v12, v12, v16 \n"
"vredsum.vs v0, v12, v4 \n"
"vmv.x.s %[tmp], v0 \n"
"mul %[hash], %[hash], %[pow_n] \n"
"add %[hash], %[hash], %[tmp] \n"
"3: \n"
:
[src] "+r"(src), [count] "+r"(count), [hash] "+r"(hash), [tmp] "=&r"(tmp)
: [muls16] "r"(kDjb2Muls_RVV + 1), [c16] "r"(c16),
[tail_muls] "r"(tail_muls), [pow_n] "r"(pow_n)
: "memory", RVV_VL_CLOBBER "vtype", "v0", "v4", "v8", "v12", "v13", "v14",
"v15", "v16", "v17", "v18", "v19");
return hash;
}
#endif // HAS_HASHDJB2_RVV
#ifdef __cplusplus
} // extern "C"
} // namespace libyuv

View File

@ -287,32 +287,66 @@ TEST_F(LibYUVCompareTest, BenchmarkHammingDistance_Opt) {
benchmark_iterations_ *
((benchmark_width_ * benchmark_height_ + kMaxWidth - 1) / kMaxWidth);
for (int i = 0; i < count; ++i) {
#if defined(HAS_HAMMINGDISTANCE_NEON)
h1 = HammingDistance_NEON(src_a, src_b, kMaxWidth);
#elif defined(HAS_HAMMINGDISTANCE_AVX2)
int has_avx2 = TestCpuFlag(kCpuHasAVX2);
if (has_avx2) {
h1 = HammingDistance_AVX2(src_a, src_b, kMaxWidth);
#if defined(HAS_HAMMINGDISTANCE_NEON_DOTPROD)
if (TestCpuFlag(kCpuHasNeonDotProd)) {
h1 = HammingDistance_NEON_DotProd(src_a, src_b, kMaxWidth);
} else {
int has_sse42 = TestCpuFlag(kCpuHasSSE42);
if (has_sse42) {
h1 = HammingDistance_SSE42(src_a, src_b, kMaxWidth);
} else {
int has_ssse3 = TestCpuFlag(kCpuHasSSSE3);
if (has_ssse3) {
h1 = HammingDistance_SSSE3(src_a, src_b, kMaxWidth);
} else {
h1 = HammingDistance_C(src_a, src_b, kMaxWidth);
}
}
h1 = HammingDistance_NEON(src_a, src_b, kMaxWidth);
}
#elif defined(HAS_HAMMINGDISTANCE_NEON)
h1 = HammingDistance_NEON(src_a, src_b, kMaxWidth);
#elif defined(HAS_HAMMINGDISTANCE_AVX512VNNI)
if (TestCpuFlag(kCpuHasAVX512VNNI)) {
h1 = HammingDistance_AVX512VNNI(src_a, src_b, kMaxWidth);
} else if (TestCpuFlag(kCpuHasAVX512BW)) {
h1 = HammingDistance_AVX512BW(src_a, src_b, kMaxWidth);
} else if (TestCpuFlag(kCpuHasAVX2)) {
h1 = HammingDistance_AVX2(src_a, src_b, kMaxWidth);
#if defined(HAS_HAMMINGDISTANCE_SSE42)
} else if (TestCpuFlag(kCpuHasSSE42)) {
h1 = HammingDistance_SSE42(src_a, src_b, kMaxWidth);
#endif
#if defined(HAS_HAMMINGDISTANCE_SSSE3)
} else if (TestCpuFlag(kCpuHasSSSE3)) {
h1 = HammingDistance_SSSE3(src_a, src_b, kMaxWidth);
#endif
} else {
h1 = HammingDistance_C(src_a, src_b, kMaxWidth);
}
#elif defined(HAS_HAMMINGDISTANCE_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
h1 = HammingDistance_AVX2(src_a, src_b, kMaxWidth);
#if defined(HAS_HAMMINGDISTANCE_SSE42)
} else if (TestCpuFlag(kCpuHasSSE42)) {
h1 = HammingDistance_SSE42(src_a, src_b, kMaxWidth);
#endif
#if defined(HAS_HAMMINGDISTANCE_SSSE3)
} else if (TestCpuFlag(kCpuHasSSSE3)) {
h1 = HammingDistance_SSSE3(src_a, src_b, kMaxWidth);
#endif
} else {
h1 = HammingDistance_C(src_a, src_b, kMaxWidth);
}
#elif defined(HAS_HAMMINGDISTANCE_SSE42)
int has_sse42 = TestCpuFlag(kCpuHasSSE42);
if (has_sse42) {
if (TestCpuFlag(kCpuHasSSE42)) {
h1 = HammingDistance_SSE42(src_a, src_b, kMaxWidth);
} else {
h1 = HammingDistance_C(src_a, src_b, kMaxWidth);
}
#elif defined(HAS_HAMMINGDISTANCE_RVV)
h1 = HammingDistance_RVV(src_a, src_b, kMaxWidth);
#elif defined(HAS_HAMMINGDISTANCE_LASX)
if (TestCpuFlag(kCpuHasLASX)) {
h1 = HammingDistance_LASX(src_a, src_b, kMaxWidth);
} else {
h1 = HammingDistance_LSX(src_a, src_b, kMaxWidth);
}
#elif defined(HAS_HAMMINGDISTANCE_LSX)
h1 = HammingDistance_LSX(src_a, src_b, kMaxWidth);
#elif defined(HAS_HAMMINGDISTANCE_WASMSIMD)
h1 = HammingDistance_WASMSIMD(src_a, src_b, kMaxWidth);
#elif defined(HAS_HAMMINGDISTANCE_HVX)
h1 = HammingDistance_HVX(src_a, src_b, kMaxWidth);
#else
h1 = HammingDistance_C(src_a, src_b, kMaxWidth);
#endif
@ -408,32 +442,66 @@ TEST_F(LibYUVCompareTest, TestHammingDistance_Opt) {
ASSERT_EQ(kMaxWidth * 8ULL, h0);
for (int i = 0; i < benchmark_iterations_; ++i) {
#if defined(HAS_HAMMINGDISTANCE_NEON)
h1 = HammingDistance_NEON(src_a, src_b, kMaxWidth);
#elif defined(HAS_HAMMINGDISTANCE_AVX2)
int has_avx2 = TestCpuFlag(kCpuHasAVX2);
if (has_avx2) {
h1 = HammingDistance_AVX2(src_a, src_b, kMaxWidth);
#if defined(HAS_HAMMINGDISTANCE_NEON_DOTPROD)
if (TestCpuFlag(kCpuHasNeonDotProd)) {
h1 = HammingDistance_NEON_DotProd(src_a, src_b, kMaxWidth);
} else {
int has_sse42 = TestCpuFlag(kCpuHasSSE42);
if (has_sse42) {
h1 = HammingDistance_SSE42(src_a, src_b, kMaxWidth);
} else {
int has_ssse3 = TestCpuFlag(kCpuHasSSSE3);
if (has_ssse3) {
h1 = HammingDistance_SSSE3(src_a, src_b, kMaxWidth);
} else {
h1 = HammingDistance_C(src_a, src_b, kMaxWidth);
}
}
h1 = HammingDistance_NEON(src_a, src_b, kMaxWidth);
}
#elif defined(HAS_HAMMINGDISTANCE_NEON)
h1 = HammingDistance_NEON(src_a, src_b, kMaxWidth);
#elif defined(HAS_HAMMINGDISTANCE_AVX512VNNI)
if (TestCpuFlag(kCpuHasAVX512VNNI)) {
h1 = HammingDistance_AVX512VNNI(src_a, src_b, kMaxWidth);
} else if (TestCpuFlag(kCpuHasAVX512BW)) {
h1 = HammingDistance_AVX512BW(src_a, src_b, kMaxWidth);
} else if (TestCpuFlag(kCpuHasAVX2)) {
h1 = HammingDistance_AVX2(src_a, src_b, kMaxWidth);
#if defined(HAS_HAMMINGDISTANCE_SSE42)
} else if (TestCpuFlag(kCpuHasSSE42)) {
h1 = HammingDistance_SSE42(src_a, src_b, kMaxWidth);
#endif
#if defined(HAS_HAMMINGDISTANCE_SSSE3)
} else if (TestCpuFlag(kCpuHasSSSE3)) {
h1 = HammingDistance_SSSE3(src_a, src_b, kMaxWidth);
#endif
} else {
h1 = HammingDistance_C(src_a, src_b, kMaxWidth);
}
#elif defined(HAS_HAMMINGDISTANCE_AVX2)
if (TestCpuFlag(kCpuHasAVX2)) {
h1 = HammingDistance_AVX2(src_a, src_b, kMaxWidth);
#if defined(HAS_HAMMINGDISTANCE_SSE42)
} else if (TestCpuFlag(kCpuHasSSE42)) {
h1 = HammingDistance_SSE42(src_a, src_b, kMaxWidth);
#endif
#if defined(HAS_HAMMINGDISTANCE_SSSE3)
} else if (TestCpuFlag(kCpuHasSSSE3)) {
h1 = HammingDistance_SSSE3(src_a, src_b, kMaxWidth);
#endif
} else {
h1 = HammingDistance_C(src_a, src_b, kMaxWidth);
}
#elif defined(HAS_HAMMINGDISTANCE_SSE42)
int has_sse42 = TestCpuFlag(kCpuHasSSE42);
if (has_sse42) {
if (TestCpuFlag(kCpuHasSSE42)) {
h1 = HammingDistance_SSE42(src_a, src_b, kMaxWidth);
} else {
h1 = HammingDistance_C(src_a, src_b, kMaxWidth);
}
#elif defined(HAS_HAMMINGDISTANCE_RVV)
h1 = HammingDistance_RVV(src_a, src_b, kMaxWidth);
#elif defined(HAS_HAMMINGDISTANCE_LASX)
if (TestCpuFlag(kCpuHasLASX)) {
h1 = HammingDistance_LASX(src_a, src_b, kMaxWidth);
} else {
h1 = HammingDistance_LSX(src_a, src_b, kMaxWidth);
}
#elif defined(HAS_HAMMINGDISTANCE_LSX)
h1 = HammingDistance_LSX(src_a, src_b, kMaxWidth);
#elif defined(HAS_HAMMINGDISTANCE_WASMSIMD)
h1 = HammingDistance_WASMSIMD(src_a, src_b, kMaxWidth);
#elif defined(HAS_HAMMINGDISTANCE_HVX)
h1 = HammingDistance_HVX(src_a, src_b, kMaxWidth);
#else
h1 = HammingDistance_C(src_a, src_b, kMaxWidth);
#endif
@ -560,6 +628,14 @@ TEST_F(LibYUVCompareTest, TestCompareAnyCount) {
MemRandomize(src_a, kMaxCount + 1);
MemRandomize(src_b, kMaxCount + 1);
const int kCpuMasks[] = {
benchmark_cpu_info_,
benchmark_cpu_info_ & ~kCpuHasAVX512VNNI,
benchmark_cpu_info_ & ~(kCpuHasAVX512BW | kCpuHasAVX512VNNI),
benchmark_cpu_info_ &
~(kCpuHasAVX2 | kCpuHasAVX512BW | kCpuHasAVX512VNNI),
};
for (int count = 0; count <= kMaxCount; ++count) {
for (int offset = 0; offset < 2; ++offset) {
const uint8_t* a = src_a + kMaxCount + 1 - count - offset;
@ -568,13 +644,19 @@ TEST_F(LibYUVCompareTest, TestCompareAnyCount) {
uint32_t c_hash = HashDjb2(a, count, 5381);
uint64_t c_diff = ComputeHammingDistance(a, b, count);
uint64_t c_err = ComputeSumSquareError(a, b, count);
for (size_t m = 0; m < sizeof(kCpuMasks) / sizeof(kCpuMasks[0]); ++m) {
MaskCpuFlags(kCpuMasks[m]);
uint32_t opt_hash = HashDjb2(a, count, 5381);
uint64_t opt_diff = ComputeHammingDistance(a, b, count);
uint64_t opt_err = ComputeSumSquareError(a, b, count);
EXPECT_EQ(c_hash, opt_hash)
<< "count " << count << " offset " << offset << " mask " << m;
EXPECT_EQ(c_diff, opt_diff)
<< "count " << count << " offset " << offset << " mask " << m;
EXPECT_EQ(c_err, opt_err)
<< "count " << count << " offset " << offset << " mask " << m;
}
MaskCpuFlags(benchmark_cpu_info_);
uint32_t opt_hash = HashDjb2(a, count, 5381);
uint64_t opt_diff = ComputeHammingDistance(a, b, count);
uint64_t opt_err = ComputeSumSquareError(a, b, count);
EXPECT_EQ(c_hash, opt_hash) << "count " << count << " offset " << offset;
EXPECT_EQ(c_diff, opt_diff) << "count " << count << " offset " << offset;
EXPECT_EQ(c_err, opt_err) << "count " << count << " offset " << offset;
}
}
@ -772,6 +854,13 @@ TEST_F(LibYUVCompareTest, TestSsimAnyWidth) {
src_b[i] = (i * 151 + 43) & 0xff;
}
const int kCpuMasks[] = {
benchmark_cpu_info_,
benchmark_cpu_info_ & ~(kCpuHasAVX512BW | kCpuHasAVX512VNNI),
benchmark_cpu_info_ &
~(kCpuHasAVX2 | kCpuHasAVX512BW | kCpuHasAVX512VNNI),
};
for (int width = 9; width <= kMaxWidth; ++width) {
for (int offset = 0; offset < 2; ++offset) {
const int size = width * kHeight;
@ -779,10 +868,13 @@ TEST_F(LibYUVCompareTest, TestSsimAnyWidth) {
const uint8_t* b = src_b + kPlaneSize - size - offset;
MaskCpuFlags(disable_cpu_flags_);
double c_ssim = CalcFrameSsim(a, width, b, width, width, kHeight);
for (size_t m = 0; m < sizeof(kCpuMasks) / sizeof(kCpuMasks[0]); ++m) {
MaskCpuFlags(kCpuMasks[m]);
double opt_ssim = CalcFrameSsim(a, width, b, width, width, kHeight);
EXPECT_NEAR(c_ssim, opt_ssim, 1e-5)
<< "width " << width << " offset " << offset << " mask " << m;
}
MaskCpuFlags(benchmark_cpu_info_);
double opt_ssim = CalcFrameSsim(a, width, b, width, width, kHeight);
EXPECT_NEAR(c_ssim, opt_ssim, 1e-5)
<< "width " << width << " offset " << offset;
}
}