mirror of
https://github.com/webmproject/libwebp.git
synced 2024-11-19 20:08:28 +01:00
neon-implementation for rescaler code
It's better to stay with a 32b fixed-point precision overall, otherwise the C-version on ARM gets *slower*. Actually, gcc ARM compiler optimizes some instructions pretty well when WEBP_RESCALER_FIX is exactly 32, even in C. Change-Id: I0eea97f7db5947470f5af355dee098eca81e178d
This commit is contained in:
parent
6dfa5e3e58
commit
b4e731cd93
@ -62,6 +62,7 @@ dsp_dec_srcs := \
|
|||||||
src/dsp/rescaler.c \
|
src/dsp/rescaler.c \
|
||||||
src/dsp/rescaler_mips32.c \
|
src/dsp/rescaler_mips32.c \
|
||||||
src/dsp/rescaler_mips_dsp_r2.c \
|
src/dsp/rescaler_mips_dsp_r2.c \
|
||||||
|
src/dsp/rescaler_neon.$(NEON) \
|
||||||
src/dsp/rescaler_sse2.c \
|
src/dsp/rescaler_sse2.c \
|
||||||
src/dsp/upsampling.c \
|
src/dsp/upsampling.c \
|
||||||
src/dsp/upsampling_mips_dsp_r2.c \
|
src/dsp/upsampling_mips_dsp_r2.c \
|
||||||
|
@ -207,6 +207,7 @@ DSP_DEC_OBJS = \
|
|||||||
$(DIROBJ)\dsp\rescaler.obj \
|
$(DIROBJ)\dsp\rescaler.obj \
|
||||||
$(DIROBJ)\dsp\rescaler_mips32.obj \
|
$(DIROBJ)\dsp\rescaler_mips32.obj \
|
||||||
$(DIROBJ)\dsp\rescaler_mips_dsp_r2.obj \
|
$(DIROBJ)\dsp\rescaler_mips_dsp_r2.obj \
|
||||||
|
$(DIROBJ)\dsp\rescaler_neon.obj \
|
||||||
$(DIROBJ)\dsp\rescaler_sse2.obj \
|
$(DIROBJ)\dsp\rescaler_sse2.obj \
|
||||||
$(DIROBJ)\dsp\upsampling.obj \
|
$(DIROBJ)\dsp\upsampling.obj \
|
||||||
$(DIROBJ)\dsp\upsampling_mips_dsp_r2.obj \
|
$(DIROBJ)\dsp\upsampling_mips_dsp_r2.obj \
|
||||||
|
@ -149,6 +149,7 @@ DSP_DEC_OBJS = \
|
|||||||
src/dsp/rescaler.o \
|
src/dsp/rescaler.o \
|
||||||
src/dsp/rescaler_mips32.o \
|
src/dsp/rescaler_mips32.o \
|
||||||
src/dsp/rescaler_mips_dsp_r2.o \
|
src/dsp/rescaler_mips_dsp_r2.o \
|
||||||
|
src/dsp/rescaler_neon.o \
|
||||||
src/dsp/rescaler_sse2.o \
|
src/dsp/rescaler_sse2.o \
|
||||||
src/dsp/upsampling.o \
|
src/dsp/upsampling.o \
|
||||||
src/dsp/upsampling_mips_dsp_r2.o \
|
src/dsp/upsampling_mips_dsp_r2.o \
|
||||||
|
@ -30,6 +30,7 @@ COMMON_SOURCES += neon.h
|
|||||||
COMMON_SOURCES += rescaler.c
|
COMMON_SOURCES += rescaler.c
|
||||||
COMMON_SOURCES += rescaler_mips32.c
|
COMMON_SOURCES += rescaler_mips32.c
|
||||||
COMMON_SOURCES += rescaler_mips_dsp_r2.c
|
COMMON_SOURCES += rescaler_mips_dsp_r2.c
|
||||||
|
COMMON_SOURCES += rescaler_neon.c
|
||||||
COMMON_SOURCES += upsampling.c
|
COMMON_SOURCES += upsampling.c
|
||||||
COMMON_SOURCES += upsampling_mips_dsp_r2.c
|
COMMON_SOURCES += upsampling_mips_dsp_r2.c
|
||||||
COMMON_SOURCES += upsampling_neon.c
|
COMMON_SOURCES += upsampling_neon.c
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// Rescaling functions
|
// Rescaling functions
|
||||||
|
//
|
||||||
|
// Author: Skal (pascal.massimino@gmail.com)
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
@ -197,6 +199,7 @@ WebPRescalerExportRowFunc WebPRescalerExportRowShrink;
|
|||||||
extern void WebPRescalerDspInitSSE2(void);
|
extern void WebPRescalerDspInitSSE2(void);
|
||||||
extern void WebPRescalerDspInitMIPS32(void);
|
extern void WebPRescalerDspInitMIPS32(void);
|
||||||
extern void WebPRescalerDspInitMIPSdspR2(void);
|
extern void WebPRescalerDspInitMIPSdspR2(void);
|
||||||
|
extern void WebPRescalerDspInitNEON(void);
|
||||||
|
|
||||||
static volatile VP8CPUInfo rescaler_last_cpuinfo_used =
|
static volatile VP8CPUInfo rescaler_last_cpuinfo_used =
|
||||||
(VP8CPUInfo)&rescaler_last_cpuinfo_used;
|
(VP8CPUInfo)&rescaler_last_cpuinfo_used;
|
||||||
@ -215,6 +218,11 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInit(void) {
|
|||||||
WebPRescalerDspInitSSE2();
|
WebPRescalerDspInitSSE2();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(WEBP_USE_NEON)
|
||||||
|
if (VP8GetCPUInfo(kNEON)) {
|
||||||
|
WebPRescalerDspInitNEON();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#if defined(WEBP_USE_MIPS32)
|
#if defined(WEBP_USE_MIPS32)
|
||||||
if (VP8GetCPUInfo(kMIPS32)) {
|
if (VP8GetCPUInfo(kMIPS32)) {
|
||||||
WebPRescalerDspInitMIPS32();
|
WebPRescalerDspInitMIPS32();
|
||||||
|
186
src/dsp/rescaler_neon.c
Normal file
186
src/dsp/rescaler_neon.c
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
// Copyright 2015 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// NEON version of rescaling functions
|
||||||
|
//
|
||||||
|
// Author: Skal (pascal.massimino@gmail.com)
|
||||||
|
|
||||||
|
#include "./dsp.h"
|
||||||
|
|
||||||
|
#if defined(WEBP_USE_NEON)
|
||||||
|
|
||||||
|
#include <arm_neon.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "./neon.h"
|
||||||
|
#include "../utils/rescaler.h"
|
||||||
|
|
||||||
|
#define ROUNDER (WEBP_RESCALER_ONE >> 1)
|
||||||
|
#define MULT_FIX_C(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
|
||||||
|
|
||||||
|
#define LOAD_32x4(SRC, DST) const uint32x4_t DST = vld1q_u32((SRC))
|
||||||
|
#define LOAD_32x8(SRC, DST0, DST1) \
|
||||||
|
LOAD_32x4(SRC + 0, DST0); \
|
||||||
|
LOAD_32x4(SRC + 4, DST1)
|
||||||
|
|
||||||
|
#define STORE_32x8(SRC0, SRC1, DST) do { \
|
||||||
|
vst1q_u32((DST) + 0, SRC0); \
|
||||||
|
vst1q_u32((DST) + 4, SRC1); \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
#if (WEBP_RESCALER_RFIX == 32)
|
||||||
|
#define MAKE_HALF_CST(C) vdupq_n_s32((int32_t)((C) >> 1))
|
||||||
|
#define MULT_FIX(A, B) /* note: B is actualy scale>>1. See MAKE_HALF_CST */ \
|
||||||
|
vreinterpretq_u32_s32(vqrdmulhq_s32(vreinterpretq_s32_u32((A)), (B)))
|
||||||
|
#else
|
||||||
|
#error "MULT_FIX/WEBP_RESCALER_RFIX need some more work"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static uint32x4_t Interpolate(const rescaler_t* const frow,
|
||||||
|
const rescaler_t* const irow,
|
||||||
|
uint32_t A, uint32_t B) {
|
||||||
|
LOAD_32x4(frow, A0);
|
||||||
|
LOAD_32x4(irow, B0);
|
||||||
|
const uint64x2_t C0 = vmull_n_u32(vget_low_u32(A0), A);
|
||||||
|
const uint64x2_t C1 = vmull_n_u32(vget_high_u32(A0), A);
|
||||||
|
const uint64x2_t D0 = vmlal_n_u32(C0, vget_low_u32(B0), B);
|
||||||
|
const uint64x2_t D1 = vmlal_n_u32(C1, vget_high_u32(B0), B);
|
||||||
|
const uint32x4_t E = vcombine_u32(
|
||||||
|
vrshrn_n_u64(D0, WEBP_RESCALER_RFIX),
|
||||||
|
vrshrn_n_u64(D1, WEBP_RESCALER_RFIX));
|
||||||
|
return E;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RescalerExportRowExpand(WebPRescaler* const wrk) {
|
||||||
|
int x_out;
|
||||||
|
uint8_t* const dst = wrk->dst;
|
||||||
|
rescaler_t* const irow = wrk->irow;
|
||||||
|
const int x_out_max = wrk->dst_width * wrk->num_channels;
|
||||||
|
const int max_span = x_out_max & ~7;
|
||||||
|
const rescaler_t* const frow = wrk->frow;
|
||||||
|
const uint32_t fy_scale = wrk->fy_scale;
|
||||||
|
const int32x4_t fy_scale_half = MAKE_HALF_CST(fy_scale);
|
||||||
|
assert(!WebPRescalerOutputDone(wrk));
|
||||||
|
assert(wrk->y_accum <= 0);
|
||||||
|
assert(wrk->y_expand);
|
||||||
|
assert(wrk->y_sub != 0);
|
||||||
|
if (wrk->y_accum == 0) {
|
||||||
|
for (x_out = 0; x_out < max_span; x_out += 8) {
|
||||||
|
LOAD_32x4(frow + x_out + 0, A0);
|
||||||
|
LOAD_32x4(frow + x_out + 4, A1);
|
||||||
|
const uint32x4_t B0 = MULT_FIX(A0, fy_scale_half);
|
||||||
|
const uint32x4_t B1 = MULT_FIX(A1, fy_scale_half);
|
||||||
|
const uint16x4_t C0 = vmovn_u32(B0);
|
||||||
|
const uint16x4_t C1 = vmovn_u32(B1);
|
||||||
|
const uint8x8_t D = vmovn_u16(vcombine_u16(C0, C1));
|
||||||
|
vst1_u8(dst + x_out, D);
|
||||||
|
}
|
||||||
|
for (; x_out < x_out_max; ++x_out) {
|
||||||
|
const uint32_t J = frow[x_out];
|
||||||
|
const int v = (int)MULT_FIX_C(J, fy_scale);
|
||||||
|
assert(v >= 0 && v <= 255);
|
||||||
|
dst[x_out] = v;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
|
||||||
|
const uint32_t A = (uint32_t)(WEBP_RESCALER_ONE - B);
|
||||||
|
for (x_out = 0; x_out < max_span; x_out += 8) {
|
||||||
|
const uint32x4_t C0 =
|
||||||
|
Interpolate(frow + x_out + 0, irow + x_out + 0, A, B);
|
||||||
|
const uint32x4_t C1 =
|
||||||
|
Interpolate(frow + x_out + 4, irow + x_out + 4, A, B);
|
||||||
|
const uint32x4_t D0 = MULT_FIX(C0, fy_scale_half);
|
||||||
|
const uint32x4_t D1 = MULT_FIX(C1, fy_scale_half);
|
||||||
|
const uint16x4_t E0 = vmovn_u32(D0);
|
||||||
|
const uint16x4_t E1 = vmovn_u32(D1);
|
||||||
|
const uint8x8_t F = vmovn_u16(vcombine_u16(E0, E1));
|
||||||
|
vst1_u8(dst + x_out, F);
|
||||||
|
}
|
||||||
|
for (; x_out < x_out_max; ++x_out) {
|
||||||
|
const uint64_t I = (uint64_t)A * frow[x_out]
|
||||||
|
+ (uint64_t)B * irow[x_out];
|
||||||
|
const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
|
||||||
|
const int v = (int)MULT_FIX_C(J, fy_scale);
|
||||||
|
assert(v >= 0 && v <= 255);
|
||||||
|
dst[x_out] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RescalerExportRowShrink(WebPRescaler* const wrk) {
|
||||||
|
int x_out;
|
||||||
|
uint8_t* const dst = wrk->dst;
|
||||||
|
rescaler_t* const irow = wrk->irow;
|
||||||
|
const int x_out_max = wrk->dst_width * wrk->num_channels;
|
||||||
|
const int max_span = x_out_max & ~7;
|
||||||
|
const rescaler_t* const frow = wrk->frow;
|
||||||
|
const uint32_t yscale = wrk->fy_scale * (-wrk->y_accum);
|
||||||
|
const uint32_t fxy_scale = wrk->fxy_scale;
|
||||||
|
const uint32x4_t zero = vdupq_n_u32(0);
|
||||||
|
const int32x4_t yscale_half = MAKE_HALF_CST(yscale);
|
||||||
|
const int32x4_t fxy_scale_half = MAKE_HALF_CST(fxy_scale);
|
||||||
|
assert(!WebPRescalerOutputDone(wrk));
|
||||||
|
assert(wrk->y_accum <= 0);
|
||||||
|
assert(!wrk->y_expand);
|
||||||
|
if (yscale) {
|
||||||
|
for (x_out = 0; x_out < max_span; x_out += 8) {
|
||||||
|
LOAD_32x8(frow + x_out, in0, in1);
|
||||||
|
LOAD_32x8(irow + x_out, in2, in3);
|
||||||
|
const uint32x4_t A0 = MULT_FIX(in0, yscale_half);
|
||||||
|
const uint32x4_t A1 = MULT_FIX(in1, yscale_half);
|
||||||
|
const uint32x4_t B0 = vqsubq_u32(in2, A0);
|
||||||
|
const uint32x4_t B1 = vqsubq_u32(in3, A1);
|
||||||
|
const uint32x4_t C0 = MULT_FIX(B0, fxy_scale_half);
|
||||||
|
const uint32x4_t C1 = MULT_FIX(B1, fxy_scale_half);
|
||||||
|
const uint16x4_t D0 = vmovn_u32(C0);
|
||||||
|
const uint16x4_t D1 = vmovn_u32(C1);
|
||||||
|
const uint8x8_t E = vmovn_u16(vcombine_u16(D0, D1));
|
||||||
|
vst1_u8(dst + x_out, E);
|
||||||
|
STORE_32x8(A0, A1, irow + x_out);
|
||||||
|
}
|
||||||
|
for (; x_out < x_out_max; ++x_out) {
|
||||||
|
const uint32_t frac = (uint32_t)MULT_FIX_C(frow[x_out], yscale);
|
||||||
|
const int v = (int)MULT_FIX_C(irow[x_out] - frac, wrk->fxy_scale);
|
||||||
|
assert(v >= 0 && v <= 255);
|
||||||
|
dst[x_out] = v;
|
||||||
|
irow[x_out] = frac; // new fractional start
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (x_out = 0; x_out < max_span; x_out += 8) {
|
||||||
|
LOAD_32x8(irow + x_out, in0, in1);
|
||||||
|
const uint32x4_t A0 = MULT_FIX(in0, fxy_scale_half);
|
||||||
|
const uint32x4_t A1 = MULT_FIX(in1, fxy_scale_half);
|
||||||
|
const uint16x4_t B0 = vmovn_u32(A0);
|
||||||
|
const uint16x4_t B1 = vmovn_u32(A1);
|
||||||
|
const uint8x8_t C = vmovn_u16(vcombine_u16(B0, B1));
|
||||||
|
vst1_u8(dst + x_out, C);
|
||||||
|
STORE_32x8(zero, zero, irow + x_out);
|
||||||
|
}
|
||||||
|
for (; x_out < x_out_max; ++x_out) {
|
||||||
|
const int v = (int)MULT_FIX_C(irow[x_out], fxy_scale);
|
||||||
|
assert(v >= 0 && v <= 255);
|
||||||
|
dst[x_out] = v;
|
||||||
|
irow[x_out] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
extern void WebPRescalerDspInitNEON(void);
|
||||||
|
|
||||||
|
WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInitNEON(void) {
|
||||||
|
WebPRescalerExportRowExpand = RescalerExportRowExpand;
|
||||||
|
WebPRescalerExportRowShrink = RescalerExportRowShrink;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // !WEBP_USE_NEON
|
||||||
|
|
||||||
|
WEBP_DSP_INIT_STUB(WebPRescalerDspInitNEON)
|
||||||
|
|
||||||
|
#endif // WEBP_USE_NEON
|
@ -70,8 +70,15 @@ static WEBP_INLINE void ProcessRow(const __m128i* const A0,
|
|||||||
const __m128i C3 = _mm_add_epi64(B3, rounder);
|
const __m128i C3 = _mm_add_epi64(B3, rounder);
|
||||||
const __m128i D0 = _mm_srli_epi64(C0, WEBP_RESCALER_RFIX);
|
const __m128i D0 = _mm_srli_epi64(C0, WEBP_RESCALER_RFIX);
|
||||||
const __m128i D1 = _mm_srli_epi64(C1, WEBP_RESCALER_RFIX);
|
const __m128i D1 = _mm_srli_epi64(C1, WEBP_RESCALER_RFIX);
|
||||||
|
#if (WEBP_RESCALER_FIX < 32)
|
||||||
|
const __m128i D2 =
|
||||||
|
_mm_and_si128(_mm_slli_epi64(C2, 32 - WEBP_RESCALER_RFIX), mask);
|
||||||
|
const __m128i D3 =
|
||||||
|
_mm_and_si128(_mm_slli_epi64(C3, 32 - WEBP_RESCALER_RFIX), mask);
|
||||||
|
#else
|
||||||
const __m128i D2 = _mm_and_si128(C2, mask);
|
const __m128i D2 = _mm_and_si128(C2, mask);
|
||||||
const __m128i D3 = _mm_and_si128(C3, mask);
|
const __m128i D3 = _mm_and_si128(C3, mask);
|
||||||
|
#endif
|
||||||
const __m128i E0 = _mm_or_si128(D0, D2);
|
const __m128i E0 = _mm_or_si128(D0, D2);
|
||||||
const __m128i E1 = _mm_or_si128(D1, D3);
|
const __m128i E1 = _mm_or_si128(D1, D3);
|
||||||
const __m128i F = _mm_packs_epi32(E0, E1);
|
const __m128i F = _mm_packs_epi32(E0, E1);
|
||||||
|
Loading…
Reference in New Issue
Block a user