mirror of
https://chromium.googlesource.com/libyuv/libyuv
synced 2026-10-28 18:07:12 +08:00
[libyuv] Add WASM SIMD128 and Relaxed SIMD for RAWToRGB24Row
Implement RAWToRGB24Row_WASMSIMD and RAWToRGB24Row_Any_WASMSIMD in source/row_wasm.cc for RGB24ToRAW and RAWToRGB24 conversions: - Process 16 pixels (48 bytes) per loop iteration using three 128-bit loads, three byte-wise shuffles, and four 2-way blends. - Use wasm_i8x16_relaxed_swizzle when __wasm_relaxed_simd__ is enabled (compiling to a single vpshufb on x86 without bounds-checking overhead) and fall back to wasm_i8x16_swizzle for baseline SIMD128. - Add kCpuHasWASM (0x100) and kCpuHasWASMSIMD (0x101) CPU feature flags and enable -msimd128 in BUILD.bazel, BUILD.gn, and CMakeLists.txt for WebAssembly targets. Performance (RAWToRGB24_Opt, 1280x720, V8 d8): CPU WASM C SIMD128 Relaxed SIMD Spd Rlx Spd ------------------------------------------------------------------- AMD Zen 4 595.0 us 137.0 us 84.0 us 4.34x 7.08x AMD Zen 3 708.0 us 148.0 us 107.0 us 4.78x 6.62x AMD Zen 5 375.0 us 107.0 us 67.0 us 3.50x 5.60x ARM Neoverse-V2 638.0 us 118.0 us 116.0 us 5.41x 5.50x ARM Neoverse-N1 1046.0 us 294.0 us 294.0 us 3.56x 3.56x Intel Ice Lake 707.0 us 209.0 us 220.0 us 3.38x 3.21x Intel Haswell 1110.0 us 344.0 us 360.0 us 3.23x 3.08x Intel Emerald Rapids 519.0 us 207.0 us 208.0 us 2.51x 2.50x Intel Sapphire Rapids 551.0 us 220.0 us 255.0 us 2.50x 2.16x ARM Cortex-A53 14440.0 us 8660.0us 6920.0us 1.67x 2.09x Intel Granite Rapids 453.0 us 225.0 us 221.0 us 2.01x 2.05x Bug: 42271072 Test: libyuv_test --gunit_filter=*RAWToRGB24*:*RGB24ToRAW*:*Cpu* Change-Id: Id902668263a2ab70aa300eeeea18851490c917b5 Reviewed-on: https://chromium-review.googlesource.com/c/libyuv/libyuv/+/8515581 Reviewed-by: richard winterton <rrwinterton@gmail.com> Commit-Queue: Frank Barchard <fbarchard@google.com>
This commit is contained in:
parent
2b7ecc0aad
commit
eb8eda9c79
@ -69,6 +69,8 @@ ARCH_SUPPORT_COPTS = select({
|
||||
"@platforms//cpu:aarch64": SUPPORTS_NEON_COPTS + SUPPORTS_SVE_COPTS,
|
||||
"@platforms//cpu:riscv32": SUPPORTS_RVV_COPTS,
|
||||
"@platforms//cpu:riscv64": SUPPORTS_RVV_COPTS,
|
||||
"@platforms//cpu:wasm32": ["-msimd128"],
|
||||
"@platforms//cpu:wasm64": ["-msimd128"],
|
||||
"//conditions:default": [],
|
||||
})
|
||||
|
||||
|
||||
4
BUILD.gn
4
BUILD.gn
@ -48,6 +48,9 @@ config("libyuv_config") {
|
||||
if (!libyuv_use_lasx) {
|
||||
defines += [ "LIBYUV_DISABLE_LASX" ]
|
||||
}
|
||||
if (current_cpu == "wasm") {
|
||||
cflags_cc = [ "-msimd128" ]
|
||||
}
|
||||
}
|
||||
|
||||
# This target is built when no specific target is specified on the command line.
|
||||
@ -166,6 +169,7 @@ static_library("libyuv_internal") {
|
||||
"source/row_any.cc",
|
||||
"source/row_common.cc",
|
||||
"source/row_gcc.cc",
|
||||
"source/row_wasm.cc",
|
||||
"source/row_win.cc",
|
||||
"source/scale.cc",
|
||||
"source/scale_any.cc",
|
||||
|
||||
@ -46,6 +46,7 @@ set ( ly_common_source_files
|
||||
${ly_src_dir}/row_common.cc
|
||||
${ly_src_dir}/row_gcc.cc
|
||||
${ly_src_dir}/row_rvv.cc
|
||||
${ly_src_dir}/row_wasm.cc
|
||||
${ly_src_dir}/row_win.cc
|
||||
${ly_src_dir}/scale.cc
|
||||
${ly_src_dir}/scale_any.cc
|
||||
@ -174,6 +175,11 @@ int main(void) { return 0; }
|
||||
add_definitions(-DLIBYUV_DISABLE_RVV)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(arch_lowercase MATCHES "^wasm" OR EMSCRIPTEN)
|
||||
set(CMAKE_C_FLAGS "-msimd128 ${CMAKE_C_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "-msimd128 ${CMAKE_CXX_FLAGS}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(LOONGARCH64)
|
||||
|
||||
@ -69,6 +69,10 @@ static const int kCpuHasLOONGARCH = 0x20;
|
||||
static const int kCpuHasLSX = 0x100;
|
||||
static const int kCpuHasLASX = 0x200;
|
||||
|
||||
// These flags are only valid on WASM processors.
|
||||
static const int kCpuHasWASM = 0x40;
|
||||
static const int kCpuHasWASMSIMD = 0x100;
|
||||
|
||||
// Optional init function. TestCpuFlag does an auto-init.
|
||||
// Returns cpu_info flags.
|
||||
LIBYUV_API
|
||||
|
||||
@ -929,6 +929,10 @@ extern "C" {
|
||||
#define HAS_RGBATOARGBROW_RVV
|
||||
#endif
|
||||
|
||||
#if !defined(LIBYUV_DISABLE_WASM) && defined(__wasm_simd128__)
|
||||
#define HAS_RAWTORGB24ROW_WASMSIMD
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__CLR_VER) && !defined(__clang__)
|
||||
#if defined(VISUALC_HAS_AVX2)
|
||||
#define SIMD_ALIGNED(var) __declspec(align(32)) var
|
||||
@ -4031,6 +4035,9 @@ void RAWToRGB24Row_SVE2(const uint8_t* src_raw, uint8_t* dst_rgb24, int width);
|
||||
void RAWToRGB24Row_LSX(const uint8_t* src_raw, uint8_t* dst_rgb24, int width);
|
||||
void RAWToRGB24Row_LASX(const uint8_t* src_raw, uint8_t* dst_rgb24, int width);
|
||||
void RAWToRGB24Row_RVV(const uint8_t* src_raw, uint8_t* dst_rgb24, int width);
|
||||
void RAWToRGB24Row_WASMSIMD(const uint8_t* src_raw,
|
||||
uint8_t* dst_rgb24,
|
||||
int width);
|
||||
void RGB565ToARGBRow_NEON(const uint8_t* src_rgb565,
|
||||
uint8_t* dst_argb,
|
||||
int width);
|
||||
@ -4142,6 +4149,9 @@ void RAWToRGB24Row_Any_LSX(const uint8_t* src_ptr, uint8_t* dst_ptr, int width);
|
||||
void RAWToRGB24Row_Any_LASX(const uint8_t* src_ptr,
|
||||
uint8_t* dst_ptr,
|
||||
int width);
|
||||
void RAWToRGB24Row_Any_WASMSIMD(const uint8_t* src_ptr,
|
||||
uint8_t* dst_ptr,
|
||||
int width);
|
||||
void RGB565ToARGBRow_Any_NEON(const uint8_t* src_ptr,
|
||||
uint8_t* dst_ptr,
|
||||
int width);
|
||||
|
||||
@ -492,6 +492,12 @@ static SAFEBUFFERS int GetCpuFlags(void) {
|
||||
cpu_info = RiscvCpuCaps("/proc/cpuinfo");
|
||||
cpu_info |= kCpuHasRISCV;
|
||||
#endif // __riscv
|
||||
#if defined(__wasm__)
|
||||
cpu_info = kCpuHasWASM;
|
||||
#if !defined(LIBYUV_DISABLE_WASM) && defined(__wasm_simd128__)
|
||||
cpu_info |= kCpuHasWASMSIMD;
|
||||
#endif
|
||||
#endif // __wasm__
|
||||
cpu_info |= kCpuInitialized;
|
||||
return cpu_info;
|
||||
}
|
||||
|
||||
@ -3656,6 +3656,14 @@ int RAWToRGB24(const uint8_t* src_raw,
|
||||
RAWToRGB24Row = RAWToRGB24Row_RVV;
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_RAWTORGB24ROW_WASMSIMD)
|
||||
if (TestCpuFlag(kCpuHasWASMSIMD)) {
|
||||
RAWToRGB24Row = RAWToRGB24Row_Any_WASMSIMD;
|
||||
if (IS_ALIGNED(width, 16)) {
|
||||
RAWToRGB24Row = RAWToRGB24Row_WASMSIMD;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (y = 0; y < height; ++y) {
|
||||
RAWToRGB24Row(src_raw, dst_rgb24, width);
|
||||
|
||||
@ -1133,6 +1133,9 @@ ANY11(RAWToRGB24Row_Any_LSX, RAWToRGB24Row_LSX, 0, 3, 3, 15)
|
||||
#if defined(HAS_RAWTORGB24ROW_LASX)
|
||||
ANY11(RAWToRGB24Row_Any_LASX, RAWToRGB24Row_LASX, 0, 3, 3, 31)
|
||||
#endif
|
||||
#if defined(HAS_RAWTORGB24ROW_WASMSIMD)
|
||||
ANY11(RAWToRGB24Row_Any_WASMSIMD, RAWToRGB24Row_WASMSIMD, 0, 3, 3, 15)
|
||||
#endif
|
||||
|
||||
#ifdef HAS_UYVYTOYROW_AVX2
|
||||
ANY11(UYVYToYRow_Any_AVX2, UYVYToYRow_AVX2, 0, 2, 1, 31)
|
||||
|
||||
120
source/row_wasm.cc
Normal file
120
source/row_wasm.cc
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2026 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE 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.
|
||||
*/
|
||||
|
||||
#include "libyuv/row.h"
|
||||
|
||||
#if !defined(LIBYUV_DISABLE_WASM) && defined(__wasm_simd128__)
|
||||
#include <wasm_simd128.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libyuv {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef HAS_RAWTORGB24ROW_WASMSIMD
|
||||
#ifdef __wasm_relaxed_simd__
|
||||
#define LIBYUV_WASM_SWIZZLE wasm_i8x16_relaxed_swizzle
|
||||
#else
|
||||
#define LIBYUV_WASM_SWIZZLE wasm_i8x16_swizzle
|
||||
#endif
|
||||
|
||||
static const uint8_t kRawToRGB24SwizzleTable[96] = {
|
||||
2, 1, 0, 5, 4, 3, 8, 7, 6, 11, 10, 9, 14, 13, 12,
|
||||
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128, 1, 0, 128, 4, 3, 2, 7, 6, 5, 10, 9, 8, 13, 12,
|
||||
11, 128, 15, 128, 1, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128, 128, 2, 128, 128, 3, 2, 1, 6, 5, 4, 9, 8, 7, 12,
|
||||
11, 10, 15, 14, 13, 14, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128, 128, 128, 128, 128, 128};
|
||||
|
||||
void RAWToRGB24Row_WASMSIMD(const uint8_t* src_raw,
|
||||
uint8_t* dst_rgb24,
|
||||
int width) {
|
||||
const uint8_t* shuf = kRawToRGB24SwizzleTable;
|
||||
// Load shuffle masks into Wasm locals outside the loop so Binaryen's
|
||||
// precompute-propagate pass does not sink v128.const into the loop.
|
||||
asm("" : "+r"(shuf));
|
||||
v128_t kShuf0_0 = wasm_v128_load(shuf);
|
||||
v128_t kShuf0_1 = wasm_v128_load(shuf + 16);
|
||||
v128_t kShuf1_1 = wasm_v128_load(shuf + 32);
|
||||
v128_t kShuf1_02 = wasm_v128_load(shuf + 48);
|
||||
v128_t kShuf2_2 = wasm_v128_load(shuf + 64);
|
||||
v128_t kShuf2_1 = wasm_v128_load(shuf + 80);
|
||||
|
||||
while (width >= 32) {
|
||||
// Prevent LLVM LSR from splitting constant load/store offsets into separate
|
||||
// pointer induction variables.
|
||||
asm("" : "+r"(src_raw), "+r"(dst_rgb24));
|
||||
v128_t s0 = wasm_v128_load(src_raw);
|
||||
v128_t s1 = wasm_v128_load(src_raw + 16);
|
||||
v128_t s2 = wasm_v128_load(src_raw + 32);
|
||||
v128_t s3 = wasm_v128_load(src_raw + 48);
|
||||
v128_t s4 = wasm_v128_load(src_raw + 64);
|
||||
v128_t s5 = wasm_v128_load(src_raw + 80);
|
||||
|
||||
v128_t s02 = wasm_i8x16_shuffle(s0, s2, 14, 15, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 24, 25, 26, 27, 28, 29);
|
||||
v128_t s35 = wasm_i8x16_shuffle(s3, s5, 14, 15, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 24, 25, 26, 27, 28, 29);
|
||||
|
||||
v128_t d0 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s0, kShuf0_0),
|
||||
LIBYUV_WASM_SWIZZLE(s1, kShuf0_1));
|
||||
v128_t d1 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s1, kShuf1_1),
|
||||
LIBYUV_WASM_SWIZZLE(s02, kShuf1_02));
|
||||
v128_t d2 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s2, kShuf2_2),
|
||||
LIBYUV_WASM_SWIZZLE(s1, kShuf2_1));
|
||||
v128_t d3 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s3, kShuf0_0),
|
||||
LIBYUV_WASM_SWIZZLE(s4, kShuf0_1));
|
||||
v128_t d4 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s4, kShuf1_1),
|
||||
LIBYUV_WASM_SWIZZLE(s35, kShuf1_02));
|
||||
v128_t d5 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s5, kShuf2_2),
|
||||
LIBYUV_WASM_SWIZZLE(s4, kShuf2_1));
|
||||
|
||||
wasm_v128_store(dst_rgb24, d0);
|
||||
wasm_v128_store(dst_rgb24 + 16, d1);
|
||||
wasm_v128_store(dst_rgb24 + 32, d2);
|
||||
wasm_v128_store(dst_rgb24 + 48, d3);
|
||||
wasm_v128_store(dst_rgb24 + 64, d4);
|
||||
wasm_v128_store(dst_rgb24 + 80, d5);
|
||||
|
||||
src_raw += 96;
|
||||
dst_rgb24 += 96;
|
||||
width -= 32;
|
||||
}
|
||||
if (width > 0) {
|
||||
asm("" : "+r"(src_raw), "+r"(dst_rgb24));
|
||||
v128_t s0 = wasm_v128_load(src_raw);
|
||||
v128_t s1 = wasm_v128_load(src_raw + 16);
|
||||
v128_t s2 = wasm_v128_load(src_raw + 32);
|
||||
|
||||
v128_t s02 = wasm_i8x16_shuffle(s0, s2, 14, 15, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 24, 25, 26, 27, 28, 29);
|
||||
|
||||
v128_t d0 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s0, kShuf0_0),
|
||||
LIBYUV_WASM_SWIZZLE(s1, kShuf0_1));
|
||||
v128_t d1 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s1, kShuf1_1),
|
||||
LIBYUV_WASM_SWIZZLE(s02, kShuf1_02));
|
||||
v128_t d2 = wasm_v128_or(LIBYUV_WASM_SWIZZLE(s2, kShuf2_2),
|
||||
LIBYUV_WASM_SWIZZLE(s1, kShuf2_1));
|
||||
|
||||
wasm_v128_store(dst_rgb24, d0);
|
||||
wasm_v128_store(dst_rgb24 + 16, d1);
|
||||
wasm_v128_store(dst_rgb24 + 32, d2);
|
||||
}
|
||||
}
|
||||
#undef LIBYUV_WASM_SWIZZLE
|
||||
#endif // HAS_RAWTORGB24ROW_WASMSIMD
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace libyuv
|
||||
#endif
|
||||
|
||||
#endif // !defined(LIBYUV_DISABLE_WASM) && defined(__wasm_simd128__)
|
||||
@ -163,6 +163,15 @@ TEST_F(LibYUVBaseTest, TestCpuHas) {
|
||||
}
|
||||
#endif // defined(__loongarch__)
|
||||
|
||||
#if defined(__wasm__)
|
||||
int has_wasm = TestCpuFlag(kCpuHasWASM);
|
||||
if (has_wasm) {
|
||||
int has_wasm_simd = TestCpuFlag(kCpuHasWASMSIMD);
|
||||
printf("Has WASM 0x%x\n", has_wasm);
|
||||
printf("Has WASM SIMD 0x%x\n", has_wasm_simd);
|
||||
}
|
||||
#endif // defined(__wasm__)
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || \
|
||||
defined(_M_X64)
|
||||
int has_x86 = TestCpuFlag(kCpuHasX86);
|
||||
@ -288,6 +297,12 @@ TEST_F(LibYUVBaseTest, TestCompilerMacros) {
|
||||
#ifdef __loongarch__
|
||||
printf("__loongarch__ %d\n", __loongarch__);
|
||||
#endif
|
||||
#ifdef __wasm__
|
||||
printf("__wasm__ %d\n", __wasm__);
|
||||
#endif
|
||||
#ifdef __wasm_simd128__
|
||||
printf("__wasm_simd128__ %d\n", __wasm_simd128__);
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
printf("_WIN32 %d\n", _WIN32);
|
||||
#endif
|
||||
|
||||
@ -106,6 +106,11 @@ static int TestCpuEnv(int cpu_info) {
|
||||
cpu_info &= ~libyuv::kCpuHasRVV;
|
||||
}
|
||||
#endif
|
||||
#if defined(__wasm__)
|
||||
if (TestEnv("LIBYUV_DISABLE_WASM")) {
|
||||
cpu_info &= ~libyuv::kCpuHasWASMSIMD;
|
||||
}
|
||||
#endif
|
||||
#if !defined(__pnacl__) && !defined(__CLR_VER) && \
|
||||
(defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \
|
||||
defined(_M_IX86))
|
||||
|
||||
@ -136,6 +136,15 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
#endif // defined(__loongarch__)
|
||||
|
||||
#if defined(__wasm__)
|
||||
int has_wasm = TestCpuFlag(kCpuHasWASM);
|
||||
if (has_wasm) {
|
||||
int has_wasm_simd = TestCpuFlag(kCpuHasWASMSIMD);
|
||||
printf("Has WASM 0x%x\n", has_wasm);
|
||||
printf("Has WASM SIMD 0x%x\n", has_wasm_simd);
|
||||
}
|
||||
#endif // defined(__wasm__)
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || \
|
||||
defined(_M_X64)
|
||||
int has_x86 = TestCpuFlag(kCpuHasX86);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user