mirror of
https://chromium.googlesource.com/libyuv/libyuv
synced 2026-06-15 00:16:08 +08:00
I420ToRAW and I420ToRGB24 1 pass AVX2
Replaced the 2-pass conversion (I420 -> ARGB -> RGB24/RAW) with a
highly optimized 1-pass AVX2 implementation. This avoids intermediate
stack buffering and significantly reduces memory bandwidth.
Implemented `I422ToRGB24Row_AVX2` in:
- `row_gcc.cc`: Inline assembly for GCC/Clang.
- `row_win.cc`: C++ intrinsics for MSVC (also verified with Clang).
Optimized the width alignment requirement: changed from 32-pixel to
16-pixel alignment in `convert_argb.cc` and `row_any.cc`. This allows
the optimized AVX2 path to be used for more common video resolutions.
Performance results (1080p, 100 iterations):
- C Reference: ~18.5 ms
- AVX2 2-Pass (Baseline): ~412 us (~45x speedup)
- AVX2 1-Pass (GCC Assembly): ~411 us (~s45x speedup)
- AVX2 1-Pass (Intrinsics): ~365 us (~50x speedup, 11% faster than asm)
Test: libyuv_unittest --gunit_filter=*I420ToRGB24*
Test: libyuv_unittest --gunit_filter=*I420ToRAW*
Bug: 42280902
Change-Id: I07c0505c95410ea16a6218c858844791a11ef073
This commit is contained in:
parent
3bdb3b94ca
commit
27f765e53e
@ -1,6 +1,6 @@
|
||||
Name: libyuv
|
||||
URL: https://chromium.googlesource.com/libyuv/libyuv/
|
||||
Version: 1948
|
||||
Version: 1949
|
||||
Revision: DEPS
|
||||
License: BSD-3-Clause
|
||||
License File: LICENSE
|
||||
|
||||
@ -349,6 +349,7 @@ extern "C" {
|
||||
((defined(_MSC_VER) && !defined(__clang__)) || \
|
||||
defined(LIBYUV_ENABLE_ROWWIN))
|
||||
#define HAS_RAWTOARGBROW_AVX2
|
||||
#define HAS_I422TORGB24ROW_AVX2
|
||||
#define HAS_RGB24TOARGBROW_AVX2
|
||||
#define HAS_RGB565TOARGBROW_AVX2
|
||||
#define HAS_ARGB1555TOARGBROW_AVX2
|
||||
|
||||
@ -11,6 +11,6 @@
|
||||
#ifndef INCLUDE_LIBYUV_VERSION_H_
|
||||
#define INCLUDE_LIBYUV_VERSION_H_
|
||||
|
||||
#define LIBYUV_VERSION 1948
|
||||
#define LIBYUV_VERSION 1949
|
||||
|
||||
#endif // INCLUDE_LIBYUV_VERSION_H_
|
||||
|
||||
@ -5551,7 +5551,7 @@ int I420ToRGB24Matrix(const uint8_t* src_y,
|
||||
#if defined(HAS_I422TORGB24ROW_AVX2)
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
I422ToRGB24Row = I422ToRGB24Row_Any_AVX2;
|
||||
if (IS_ALIGNED(width, 32)) {
|
||||
if (IS_ALIGNED(width, 16)) {
|
||||
I422ToRGB24Row = I422ToRGB24Row_AVX2;
|
||||
}
|
||||
}
|
||||
@ -5772,7 +5772,7 @@ int I422ToRGB24Matrix(const uint8_t* src_y,
|
||||
#if defined(HAS_I422TORGB24ROW_AVX2)
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
I422ToRGB24Row = I422ToRGB24Row_Any_AVX2;
|
||||
if (IS_ALIGNED(width, 32)) {
|
||||
if (IS_ALIGNED(width, 16)) {
|
||||
I422ToRGB24Row = I422ToRGB24Row_AVX2;
|
||||
}
|
||||
}
|
||||
|
||||
@ -385,7 +385,7 @@ ANY31C(I444ToARGBRow_Any_SSSE3, I444ToARGBRow_SSSE3, 0, 0, 4, 7)
|
||||
ANY31C(I444ToRGB24Row_Any_SSSE3, I444ToRGB24Row_SSSE3, 0, 0, 3, 15)
|
||||
#endif
|
||||
#ifdef HAS_I422TORGB24ROW_AVX2
|
||||
ANY31C(I422ToRGB24Row_Any_AVX2, I422ToRGB24Row_AVX2, 1, 0, 3, 31)
|
||||
ANY31C(I422ToRGB24Row_Any_AVX2, I422ToRGB24Row_AVX2, 1, 0, 3, 15)
|
||||
#endif
|
||||
#ifdef HAS_I422TORGB24ROW_AVX512VBMI
|
||||
ANY31C(I422ToRGB24Row_Any_AVX512VBMI, I422ToRGB24Row_AVX512VBMI, 1, 0, 3, 31)
|
||||
|
||||
@ -4276,7 +4276,8 @@ void I422ToARGB4444Row_AVX2(const uint8_t* src_y,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAS_I422TOARGBROW_AVX2) && defined(HAS_ARGBTORGB24ROW_AVX2)
|
||||
#if defined(HAS_I422TOARGBROW_AVX2) && defined(HAS_ARGBTORGB24ROW_AVX2) && \
|
||||
!defined(HAS_I422TORGB24ROW_AVX2)
|
||||
void I422ToRGB24Row_AVX2(const uint8_t* src_y,
|
||||
const uint8_t* src_u,
|
||||
const uint8_t* src_v,
|
||||
|
||||
@ -3775,6 +3775,58 @@ void OMITFP I422ToARGBRow_AVX2(const uint8_t* y_buf,
|
||||
}
|
||||
#endif // HAS_I422TOARGBROW_AVX2
|
||||
|
||||
#if defined(HAS_I422TORGB24ROW_AVX2)
|
||||
// 16 pixels
|
||||
// 8 UV values upsampled to 16 UV, mixed with 16 Y producing 16 RGB24 (48 bytes).
|
||||
void OMITFP I422ToRGB24Row_AVX2(const uint8_t* y_buf,
|
||||
const uint8_t* u_buf,
|
||||
const uint8_t* v_buf,
|
||||
uint8_t* dst_rgb24,
|
||||
const struct YuvConstants* yuvconstants,
|
||||
int width) {
|
||||
asm volatile (
|
||||
YUVTORGB_SETUP_AVX2(yuvconstants)
|
||||
"vbroadcasti128 %[kShuffleMaskARGBToRGB24_0],%%ymm5 \n"
|
||||
"vbroadcasti128 %[kShuffleMaskARGBToRGB24],%%ymm6 \n"
|
||||
"sub %[u_buf],%[v_buf] \n"
|
||||
|
||||
LABELALIGN
|
||||
"1: \n"
|
||||
READYUV422_AVX2
|
||||
YUVTORGB_AVX2(yuvconstants)
|
||||
"vpunpcklbw %%ymm1,%%ymm0,%%ymm0 \n"
|
||||
"vpunpcklbw %%ymm2,%%ymm2,%%ymm2 \n"
|
||||
"vmovdqa %%ymm0,%%ymm1 \n"
|
||||
"vpunpcklwd %%ymm2,%%ymm0,%%ymm0 \n"
|
||||
"vpunpckhwd %%ymm2,%%ymm1,%%ymm1 \n"
|
||||
"vpshufb %%ymm5,%%ymm0,%%ymm0 \n"
|
||||
"vpshufb %%ymm6,%%ymm1,%%ymm1 \n"
|
||||
"vpalignr $0xc,%%ymm0,%%ymm1,%%ymm1 \n"
|
||||
"vextracti128 $1,%%ymm0,%%xmm2 \n"
|
||||
"vextracti128 $1,%%ymm1,%%xmm3 \n"
|
||||
"vmovq %%xmm0,(%[dst_rgb24]) \n"
|
||||
"vmovdqu %%xmm1,0x8(%[dst_rgb24]) \n"
|
||||
"vmovq %%xmm2,0x18(%[dst_rgb24]) \n"
|
||||
"vmovdqu %%xmm3,0x20(%[dst_rgb24]) \n"
|
||||
"lea 0x30(%[dst_rgb24]),%[dst_rgb24] \n"
|
||||
"sub $0x10,%[width] \n"
|
||||
"jg 1b \n"
|
||||
"vzeroupper \n"
|
||||
: [y_buf]"+r"(y_buf), // %[y_buf]
|
||||
[u_buf]"+r"(u_buf), // %[u_buf]
|
||||
[v_buf]"+r"(v_buf), // %[v_buf]
|
||||
[dst_rgb24]"+r"(dst_rgb24), // %[dst_rgb24]
|
||||
[width]"+rm"(width) // %[width]
|
||||
: [yuvconstants]"r"(yuvconstants), // %[yuvconstants]
|
||||
[kShuffleMaskARGBToRGB24_0]"m"(kShuffleMaskARGBToRGB24_0),
|
||||
[kShuffleMaskARGBToRGB24]"m"(kShuffleMaskARGBToRGB24)
|
||||
: "memory", "cc", YUVTORGB_REGS_AVX2
|
||||
"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6"
|
||||
);
|
||||
}
|
||||
#endif // HAS_I422TORGB24ROW_AVX2
|
||||
|
||||
|
||||
#if defined(HAS_I422TOARGBROW_AVX512BW)
|
||||
static const uint64_t kSplitQuadWords[8] = {0, 2, 2, 2, 1, 2, 2, 2};
|
||||
static const uint64_t kSplitDoubleQuadWords[8] = {0, 1, 4, 4, 2, 3, 4, 4};
|
||||
|
||||
@ -973,6 +973,101 @@ void ARGBShuffleRow_AVX512BW(const uint8_t* src_argb,
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_I422TORGB24ROW_AVX2
|
||||
LIBYUV_TARGET_AVX2
|
||||
void I422ToRGB24Row_AVX2(const uint8_t* src_y,
|
||||
const uint8_t* src_u,
|
||||
const uint8_t* src_v,
|
||||
uint8_t* dst_rgb24,
|
||||
const struct YuvConstants* yuvconstants,
|
||||
int width) {
|
||||
// Constants
|
||||
__m256i ymm_kUVToB = _mm256_loadu_si256((const __m256i*)yuvconstants->kUVToB);
|
||||
__m256i ymm_kUVToG = _mm256_loadu_si256((const __m256i*)yuvconstants->kUVToG);
|
||||
__m256i ymm_kUVToR = _mm256_loadu_si256((const __m256i*)yuvconstants->kUVToR);
|
||||
__m256i ymm_kYToRgb = _mm256_loadu_si256((const __m256i*)yuvconstants->kYToRgb);
|
||||
__m256i ymm_kYBiasToRgb = _mm256_loadu_si256((const __m256i*)yuvconstants->kYBiasToRgb);
|
||||
__m256i ymm_128 = _mm256_set1_epi8((char)0x80);
|
||||
|
||||
__m128i shuf0 = _mm_setr_epi8(0, 1, 2, 4, 5, 6, 8, 9, (char)0x80, (char)0x80, (char)0x80, (char)0x80, 10, 12, 13, 14);
|
||||
__m128i shuf1 = _mm_setr_epi8(0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, (char)0x80, (char)0x80, (char)0x80, (char)0x80);
|
||||
__m256i ymm_shuf0 = _mm256_broadcastsi128_si256(shuf0);
|
||||
__m256i ymm_shuf1 = _mm256_broadcastsi128_si256(shuf1);
|
||||
__m256i ymm_u_zero = _mm256_setzero_si256();
|
||||
|
||||
ptrdiff_t offset = src_v - src_u;
|
||||
|
||||
while (width >= 16) {
|
||||
// READYUV422_AVX2
|
||||
__m128i xmm_u = _mm_loadl_epi64((const __m128i*)src_u);
|
||||
__m128i xmm_v = _mm_loadl_epi64((const __m128i*)(src_u + offset));
|
||||
src_u += 8;
|
||||
|
||||
__m256i ymm3 = _mm256_insertf128_si256(ymm_u_zero, xmm_u, 0);
|
||||
__m256i ymm1 = _mm256_insertf128_si256(ymm_u_zero, xmm_v, 0);
|
||||
|
||||
ymm3 = _mm256_unpacklo_epi8(ymm3, ymm1);
|
||||
ymm3 = _mm256_permute4x64_epi64(ymm3, 0xd8);
|
||||
ymm3 = _mm256_unpacklo_epi16(ymm3, ymm3);
|
||||
|
||||
__m128i xmm_y = _mm_loadu_si128((const __m128i*)src_y);
|
||||
src_y += 16;
|
||||
__m256i ymm4 = _mm256_insertf128_si256(ymm_u_zero, xmm_y, 0);
|
||||
ymm4 = _mm256_permute4x64_epi64(ymm4, 0xd8);
|
||||
ymm4 = _mm256_unpacklo_epi8(ymm4, ymm4);
|
||||
|
||||
// YUVTORGB_AVX2
|
||||
ymm3 = _mm256_sub_epi8(ymm3, ymm_128);
|
||||
ymm4 = _mm256_mulhi_epu16(ymm4, ymm_kYToRgb);
|
||||
|
||||
__m256i ymm0 = _mm256_maddubs_epi16(ymm_kUVToB, ymm3);
|
||||
ymm1 = _mm256_maddubs_epi16(ymm_kUVToG, ymm3);
|
||||
__m256i ymm2 = _mm256_maddubs_epi16(ymm_kUVToR, ymm3);
|
||||
|
||||
ymm4 = _mm256_add_epi16(ymm4, ymm_kYBiasToRgb);
|
||||
|
||||
ymm0 = _mm256_adds_epi16(ymm0, ymm4);
|
||||
ymm1 = _mm256_subs_epi16(ymm4, ymm1);
|
||||
ymm2 = _mm256_adds_epi16(ymm2, ymm4);
|
||||
|
||||
ymm0 = _mm256_srai_epi16(ymm0, 6);
|
||||
ymm1 = _mm256_srai_epi16(ymm1, 6);
|
||||
ymm2 = _mm256_srai_epi16(ymm2, 6);
|
||||
|
||||
ymm0 = _mm256_packus_epi16(ymm0, ymm0);
|
||||
ymm1 = _mm256_packus_epi16(ymm1, ymm1);
|
||||
ymm2 = _mm256_packus_epi16(ymm2, ymm2);
|
||||
|
||||
// STORERGB24_AVX2
|
||||
__m256i ymm0_packed = _mm256_unpacklo_epi8(ymm0, ymm1);
|
||||
__m256i ymm2_packed = _mm256_unpacklo_epi8(ymm2, ymm2);
|
||||
__m256i ymm1_packed = ymm0_packed;
|
||||
|
||||
ymm0_packed = _mm256_unpacklo_epi16(ymm0_packed, ymm2_packed);
|
||||
ymm1_packed = _mm256_unpackhi_epi16(ymm1_packed, ymm2_packed);
|
||||
|
||||
ymm0_packed = _mm256_shuffle_epi8(ymm0_packed, ymm_shuf0);
|
||||
ymm1_packed = _mm256_shuffle_epi8(ymm1_packed, ymm_shuf1);
|
||||
|
||||
ymm1_packed = _mm256_alignr_epi8(ymm1_packed, ymm0_packed, 0xc);
|
||||
|
||||
__m128i xmm0_store = _mm256_castsi256_si128(ymm0_packed);
|
||||
__m128i xmm1_store = _mm256_castsi256_si128(ymm1_packed);
|
||||
__m128i xmm2_store = _mm256_extractf128_si256(ymm0_packed, 1);
|
||||
__m128i xmm3_store = _mm256_extractf128_si256(ymm1_packed, 1);
|
||||
|
||||
_mm_storel_epi64((__m128i*)dst_rgb24, xmm0_store);
|
||||
_mm_storeu_si128((__m128i*)(dst_rgb24 + 8), xmm1_store);
|
||||
_mm_storel_epi64((__m128i*)(dst_rgb24 + 24), xmm2_store);
|
||||
_mm_storeu_si128((__m128i*)(dst_rgb24 + 32), xmm3_store);
|
||||
|
||||
dst_rgb24 += 48;
|
||||
width -= 16;
|
||||
}
|
||||
_mm256_zeroupper();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace libyuv
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user