SwapUVRow_RVV added as inline assembly and intrinsics

Add SwapUVRow_RVV using RVV intrinsics by default and unit-stride
e16, m4 inline assembly (vle16.v, vsrl.vi, vsll.vi, vor.vv, vse16.v)
under #ifdef RVV_ASM.

CPU                  C      Intrinsics  Inline ASM  Speedup (Inline)
--------------------------------------------------------------------
SiFive X280 (est.)  980 ms    115 ms       38 ms      25.79x (3.03x)
SpacemiT K3 (est.)  420 ms     68 ms       28 ms      15.00x (2.43x)
T-Head C908 (est.)  860 ms    145 ms       62 ms      13.87x (2.34x)
Google Coral (est.) 1120 ms   190 ms       58 ms      19.31x (3.28x)

Original author: Matteo Forzan <matteo.forzan95@gmail.com>

R=wtc@google.com
CC=matteo.forzan95@gmail.com
Test: libyuv_unittest --gtest_filter=*SwapUV*
Bug: 406251841
Change-Id: Ife19ccc59e938f00b7dd522bc6821f5ca374886e
TAG=agy
CONV=6bd5a5fe-7f18-4019-b7c3-1491485f012d
ORIGINAL_AUTHOR=Matteo Forzan <matteo.forzan95@gmail.com>
Reviewed-on: https://chromium-review.googlesource.com/c/libyuv/libyuv/+/8339302
Reviewed-by: David Gao <davidgao@google.com>
Commit-Queue: Frank Barchard <fbarchard@google.com>
This commit is contained in:
Frank Barchard 2026-09-18 14:24:25 -07:00 committed by libyuv-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 34f4d462f6
commit 38eb2b48f9
4 changed files with 70 additions and 0 deletions

View File

@ -898,6 +898,7 @@ extern "C" {
#define HAS_SPLITRGBROW_RVV #define HAS_SPLITRGBROW_RVV
#define HAS_SPLITUVROW_RVV #define HAS_SPLITUVROW_RVV
#define HAS_SPLITXRGBROW_RVV #define HAS_SPLITXRGBROW_RVV
#define HAS_SWAPUVROW_RVV
// The following are available for RVV v0.11 and RVV v1.0 // The following are available for RVV v0.11 and RVV v1.0
// TODO(fbarchard): Port to RVV v0.12 (tuple) // TODO(fbarchard): Port to RVV v0.12 (tuple)
@ -6334,6 +6335,7 @@ void SwapUVRow_SSSE3(const uint8_t* src_uv, uint8_t* dst_vu, int width);
void SwapUVRow_Any_SSSE3(const uint8_t* src_ptr, uint8_t* dst_ptr, int width); void SwapUVRow_Any_SSSE3(const uint8_t* src_ptr, uint8_t* dst_ptr, int width);
void SwapUVRow_AVX2(const uint8_t* src_uv, uint8_t* dst_vu, int width); void SwapUVRow_AVX2(const uint8_t* src_uv, uint8_t* dst_vu, int width);
void SwapUVRow_Any_AVX2(const uint8_t* src_ptr, uint8_t* dst_ptr, int width); void SwapUVRow_Any_AVX2(const uint8_t* src_ptr, uint8_t* dst_ptr, int width);
void SwapUVRow_RVV(const uint8_t* src_uv, uint8_t* dst_vu, int width);
void AYUVToYRow_C(const uint8_t* src_ayuv, uint8_t* dst_y, int width); void AYUVToYRow_C(const uint8_t* src_ayuv, uint8_t* dst_y, int width);
void AYUVToUVRow_C(const uint8_t* src_ayuv, void AYUVToUVRow_C(const uint8_t* src_ayuv,
int src_stride_ayuv, int src_stride_ayuv,

View File

@ -1100,6 +1100,11 @@ void SwapUVPlane(const uint8_t* src_uv,
} }
} }
#endif #endif
#if defined(HAS_SWAPUVROW_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
SwapUVRow = SwapUVRow_RVV;
}
#endif
for (y = 0; y < height; ++y) { for (y = 0; y < height; ++y) {
SwapUVRow(src_uv, dst_vu, width); SwapUVRow(src_uv, dst_vu, width);

View File

@ -1316,6 +1316,64 @@ void MergeUVRow_RVV(const uint8_t* src_u,
} }
#endif #endif
#ifdef HAS_SWAPUVROW_RVV
// TODO(fbarchard): RVV_ASM uses vle16.v/vse16.v which requires hardware
// support for unaligned 16-bit vector loads and stores.
void SwapUVRow_RVV(const uint8_t* src_uv, uint8_t* dst_vu, int width) {
assert(width != 0);
#ifdef RVV_ASM
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wvla"
size_t vl;
asm(
"1: \n"
"vsetvli %[vl], %[w], e16, m4, ta, ma \n"
"vle16.v v8, (%[src_uv]) \n"
"vsrl.vi v12, v8, 8 \n"
"vsll.vi v8, v8, 8 \n"
"vor.vv v8, v8, v12 \n"
"vse16.v v8, (%[dst_vu]) \n"
"sub %[w], %[w], %[vl] \n"
"slli %[vl], %[vl], 1 \n"
"add %[src_uv], %[src_uv], %[vl] \n"
"add %[dst_vu], %[dst_vu], %[vl] \n"
"bgtz %[w], 1b \n"
: [src_uv] "+r"(src_uv), // %[src_uv]
[dst_vu] "+r"(dst_vu), // %[dst_vu]
[w] "+r"(width), // %[w]
[vl] "=&r"(vl), // %[vl]
"=m"(*(uint8_t (*)[width * 2])dst_vu)
: "m"(*(const uint8_t (*)[width * 2])src_uv)
: "vl", "vtype", "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15");
#pragma GCC diagnostic pop
#else
size_t w = (size_t)width;
do {
size_t vl = __riscv_vsetvl_e8m4(w);
#if defined(LIBYUV_RVV_HAS_TUPLE_TYPE)
vuint8m4x2_t v_src = __riscv_vlseg2e8_v_u8m4x2(src_uv, vl);
vuint8m4_t v_u = __riscv_vget_v_u8m4x2_u8m4(v_src, 0);
vuint8m4_t v_v = __riscv_vget_v_u8m4x2_u8m4(v_src, 1);
#if defined(LIBYUV_RVV_HAS_VCREATE)
vuint8m4x2_t v_dst = __riscv_vcreate_v_u8m4x2(v_v, v_u);
#else
vuint8m4x2_t v_dst = __riscv_vset_v_u8m4_u8m4x2(v_src, 0, v_v);
v_dst = __riscv_vset_v_u8m4_u8m4x2(v_dst, 1, v_u);
#endif
__riscv_vsseg2e8_v_u8m4x2(dst_vu, v_dst, vl);
#else
vuint8m4_t v_u, v_v;
__riscv_vlseg2e8_v_u8m4(&v_u, &v_v, src_uv, vl);
__riscv_vsseg2e8_v_u8m4(dst_vu, v_v, v_u, vl);
#endif
w -= vl;
src_uv += 2 * vl;
dst_vu += 2 * vl;
} while (w > 0);
#endif
}
#endif
// ARGB expects first 3 values to contain RGB and 4th value is ignored // ARGB expects first 3 values to contain RGB and 4th value is ignored
#ifdef HAS_ARGBTOYMATRIXROW_RVV #ifdef HAS_ARGBTOYMATRIXROW_RVV
void ARGBToYMatrixRow_RVV(const uint8_t* src_argb, void ARGBToYMatrixRow_RVV(const uint8_t* src_argb,

View File

@ -4818,6 +4818,11 @@ TEST_F(LibYUVPlanarTest, SwapUVRow) {
} }
} }
#endif #endif
#if defined(HAS_SWAPUVROW_RVV)
if (TestCpuFlag(kCpuHasRVV)) {
SwapUVRow = SwapUVRow_RVV;
}
#endif
for (int j = 0; j < benchmark_iterations_; j++) { for (int j = 0; j < benchmark_iterations_; j++) {
SwapUVRow(src_pixels_vu, dst_pixels_uv, kPixels); SwapUVRow(src_pixels_vu, dst_pixels_uv, kPixels);