// Copyright 2025 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // AVX2 variant of methods for lossless encoder // // Author: Vincent Rabaud (vrabaud@google.com)
// Note we are adding uint32_t's as *signed* int32's (using _mm256_add_epi32). // But that's ok since the histogram values are less than 1<<28 (max picture // size). staticvoid AddVector_AVX2(const uint32_t* WEBP_RESTRICT a, const uint32_t* WEBP_RESTRICT b,
uint32_t* WEBP_RESTRICT out, int size) { int i = 0; int aligned_size = size & ~31; // Size is, at minimum, NUM_DISTANCE_CODES (40) and may be as large as // NUM_LITERAL_CODES (256) + NUM_LENGTH_CODES (24) + (0 or a non-zero power of // 2). See the usage in VP8LHistogramAdd().
assert(size >= 32);
assert(size % 2 == 0);
staticvoid AddVectorEq_AVX2(const uint32_t* WEBP_RESTRICT a,
uint32_t* WEBP_RESTRICT out, int size) { int i = 0; int aligned_size = size & ~31; // Size is, at minimum, NUM_DISTANCE_CODES (40) and may be as large as // NUM_LITERAL_CODES (256) + NUM_LENGTH_CODES (24) + (0 or a non-zero power of // 2). See the usage in VP8LHistogramAdd().
assert(size >= 32);
assert(size % 2 == 0);
staticint VectorMismatch_AVX2(const uint32_t* const array1, const uint32_t* const array2, int length) { int match_len;
if (length >= 24) {
__m256i A0 = _mm256_loadu_si256((const __m256i*)&array1[0]);
__m256i A1 = _mm256_loadu_si256((const __m256i*)&array2[0]);
match_len = 0; do { // Loop unrolling and early load both provide a speedup of 10% for the // current function. Also, max_limit can be MAX_LENGTH=4096 at most. const __m256i cmpA = _mm256_cmpeq_epi32(A0, A1); const __m256i B0 =
_mm256_loadu_si256((const __m256i*)&array1[match_len + 8]); const __m256i B1 =
_mm256_loadu_si256((const __m256i*)&array2[match_len + 8]); if ((uint32_t)_mm256_movemask_epi8(cmpA) != 0xffffffff) break;
match_len += 8;
// Predictor0: ARGB_BLACK. staticvoid PredictorSub0_AVX2(const uint32_t* in, const uint32_t* upper, int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; const __m256i black = _mm256_set1_epi32((int)ARGB_BLACK); for (i = 0; i + 8 <= num_pixels; i += 8) { const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); const __m256i res = _mm256_sub_epi8(src, black);
_mm256_storeu_si256((__m256i*)&out[i], res);
} if (i != num_pixels) {
VP8LPredictorsSub_SSE[0](in + i, NULL, num_pixels - i, out + i);
}
(void)upper;
}
#define GENERATE_PREDICTOR_1(X, IN) \ staticvoid PredictorSub##X##_AVX2( \ const uint32_t* const in, const uint32_t* const upper, int num_pixels, \
uint32_t* WEBP_RESTRICT const out) { \ int i; \ for (i = 0; i + 8 <= num_pixels; i += 8) { \ const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); \ const __m256i pred = _mm256_loadu_si256((const __m256i*)&(IN)); \ const __m256i res = _mm256_sub_epi8(src, pred); \
_mm256_storeu_si256((__m256i*)&out[i], res); \
} \ if (i != num_pixels) { \
VP8LPredictorsSub_SSE[(X)](in + i, WEBP_OFFSET_PTR(upper, i), \
num_pixels - i, out + i); \
} \
}
// Predictor10: avg(avg(L,TL), avg(T, TR)). staticvoid PredictorSub10_AVX2(const uint32_t* in, const uint32_t* upper, int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; for (i = 0; i + 8 <= num_pixels; i += 8) { const __m256i L = _mm256_loadu_si256((const __m256i*)&in[i - 1]); const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); const __m256i TL = _mm256_loadu_si256((const __m256i*)&upper[i - 1]); const __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); const __m256i TR = _mm256_loadu_si256((const __m256i*)&upper[i + 1]);
__m256i avgTTR, avgLTL, avg, res;
Average2_m256i(&T, &TR, &avgTTR);
Average2_m256i(&L, &TL, &avgLTL);
Average2_m256i(&avgTTR, &avgLTL, &avg);
res = _mm256_sub_epi8(src, avg);
_mm256_storeu_si256((__m256i*)&out[i], res);
} if (i != num_pixels) {
VP8LPredictorsSub_SSE[10](in + i, upper + i, num_pixels - i, out + i);
}
}
// Predictor11: select. staticvoid GetSumAbsDiff32_AVX2(const __m256i* const A, const __m256i* const B,
__m256i* const out) { // We can unpack with any value on the upper 32 bits, provided it's the same // on both operands (to that their sum of abs diff is zero). Here we use *A. const __m256i A_lo = _mm256_unpacklo_epi32(*A, *A); const __m256i B_lo = _mm256_unpacklo_epi32(*B, *A); const __m256i A_hi = _mm256_unpackhi_epi32(*A, *A); const __m256i B_hi = _mm256_unpackhi_epi32(*B, *A); const __m256i s_lo = _mm256_sad_epu8(A_lo, B_lo); const __m256i s_hi = _mm256_sad_epu8(A_hi, B_hi);
*out = _mm256_packs_epi32(s_lo, s_hi);
}
staticvoid PredictorSub11_AVX2(const uint32_t* in, const uint32_t* upper, int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; for (i = 0; i + 8 <= num_pixels; i += 8) { const __m256i L = _mm256_loadu_si256((const __m256i*)&in[i - 1]); const __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); const __m256i TL = _mm256_loadu_si256((const __m256i*)&upper[i - 1]); const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]);
__m256i pa, pb;
GetSumAbsDiff32_AVX2(&T, &TL, &pa); // pa = sum |T-TL|
GetSumAbsDiff32_AVX2(&L, &TL, &pb); // pb = sum |L-TL|
{ const __m256i mask = _mm256_cmpgt_epi32(pb, pa); const __m256i A = _mm256_and_si256(mask, L); const __m256i B = _mm256_andnot_si256(mask, T); const __m256i pred = _mm256_or_si256(A, B); // pred = (L > T)? L : T const __m256i res = _mm256_sub_epi8(src, pred);
_mm256_storeu_si256((__m256i*)&out[i], res);
}
} if (i != num_pixels) {
VP8LPredictorsSub_SSE[11](in + i, upper + i, num_pixels - i, out + i);
}
}
// Predictor12: ClampedSubSubtractFull. staticvoid PredictorSub12_AVX2(const uint32_t* in, const uint32_t* upper, int num_pixels, uint32_t* WEBP_RESTRICT out) { int i; const __m256i zero = _mm256_setzero_si256(); for (i = 0; i + 8 <= num_pixels; i += 8) { const __m256i src = _mm256_loadu_si256((const __m256i*)&in[i]); const __m256i L = _mm256_loadu_si256((const __m256i*)&in[i - 1]); const __m256i L_lo = _mm256_unpacklo_epi8(L, zero); const __m256i L_hi = _mm256_unpackhi_epi8(L, zero); const __m256i T = _mm256_loadu_si256((const __m256i*)&upper[i]); const __m256i T_lo = _mm256_unpacklo_epi8(T, zero); const __m256i T_hi = _mm256_unpackhi_epi8(T, zero); const __m256i TL = _mm256_loadu_si256((const __m256i*)&upper[i - 1]); const __m256i TL_lo = _mm256_unpacklo_epi8(TL, zero); const __m256i TL_hi = _mm256_unpackhi_epi8(TL, zero); const __m256i diff_lo = _mm256_sub_epi16(T_lo, TL_lo); const __m256i diff_hi = _mm256_sub_epi16(T_hi, TL_hi); const __m256i pred_lo = _mm256_add_epi16(L_lo, diff_lo); const __m256i pred_hi = _mm256_add_epi16(L_hi, diff_hi); const __m256i pred = _mm256_packus_epi16(pred_lo, pred_hi); const __m256i res = _mm256_sub_epi8(src, pred);
_mm256_storeu_si256((__m256i*)&out[i], res);
} if (i != num_pixels) {
VP8LPredictorsSub_SSE[12](in + i, upper + i, num_pixels - i, out + i);
}
}
const __m256i pred = _mm256_packus_epi16(A4_lo, A4_hi); const __m256i res = _mm256_sub_epi8(src, pred);
_mm256_storeu_si256((__m256i*)&out[i], res);
} if (i != num_pixels) {
VP8LPredictorsSub_SSE[13](in + i, upper + i, num_pixels - i, out + i);
}
}
//------------------------------------------------------------------------------ // Entry point
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.