diff --git a/include/libyuv/row.h b/include/libyuv/row.h index 98e332319..c7079fdb3 100644 --- a/include/libyuv/row.h +++ b/include/libyuv/row.h @@ -898,6 +898,7 @@ extern "C" { #define HAS_SPLITRGBROW_RVV #define HAS_SPLITUVROW_RVV #define HAS_SPLITXRGBROW_RVV +#define HAS_SWAPUVROW_RVV // The following are available for RVV v0.11 and RVV v1.0 // 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_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_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 AYUVToUVRow_C(const uint8_t* src_ayuv, int src_stride_ayuv, diff --git a/source/planar_functions.cc b/source/planar_functions.cc index f03c8fa84..a64bb6a99 100644 --- a/source/planar_functions.cc +++ b/source/planar_functions.cc @@ -1100,6 +1100,11 @@ void SwapUVPlane(const uint8_t* src_uv, } } #endif +#if defined(HAS_SWAPUVROW_RVV) + if (TestCpuFlag(kCpuHasRVV)) { + SwapUVRow = SwapUVRow_RVV; + } +#endif for (y = 0; y < height; ++y) { SwapUVRow(src_uv, dst_vu, width); diff --git a/source/row_rvv.cc b/source/row_rvv.cc index 9cc87ed28..78fa46c35 100644 --- a/source/row_rvv.cc +++ b/source/row_rvv.cc @@ -1316,6 +1316,64 @@ void MergeUVRow_RVV(const uint8_t* src_u, } #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 #ifdef HAS_ARGBTOYMATRIXROW_RVV void ARGBToYMatrixRow_RVV(const uint8_t* src_argb, diff --git a/unit_test/planar_test.cc b/unit_test/planar_test.cc index 92463fc42..0aecafdc8 100644 --- a/unit_test/planar_test.cc +++ b/unit_test/planar_test.cc @@ -4818,6 +4818,11 @@ TEST_F(LibYUVPlanarTest, SwapUVRow) { } } #endif +#if defined(HAS_SWAPUVROW_RVV) + if (TestCpuFlag(kCpuHasRVV)) { + SwapUVRow = SwapUVRow_RVV; + } +#endif for (int j = 0; j < benchmark_iterations_; j++) { SwapUVRow(src_pixels_vu, dst_pixels_uv, kPixels);