mirror of
https://chromium.googlesource.com/libyuv/libyuv
synced 2025-12-06 16:56:55 +08:00
Neon version of ScaleRowDown34.
Review URL: http://webrtc-codereview.appspot.com/250003 git-svn-id: http://libyuv.googlecode.com/svn/trunk@44 16f28f9a-4ce2-e073-06de-1de4eb20be90
This commit is contained in:
parent
82ca395828
commit
f626bea05f
130
source/scale.cc
130
source/scale.cc
@ -168,6 +168,124 @@ static void ScaleRowDown4Int_NEON(const uint8* src_ptr, int src_stride,
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define HAS_SCALEROWDOWN34_NEON
|
||||||
|
// Down scale from 4 to 3 pixels. Use the neon multilane read/write
|
||||||
|
// to load up the every 4th pixel into a 4 different registers.
|
||||||
|
// Point samples 32 pixels to 24 pixels.
|
||||||
|
static void ScaleRowDown34_NEON(const uint8* src_ptr, int /* src_stride */,
|
||||||
|
uint8* dst_ptr, int dst_width) {
|
||||||
|
__asm__ volatile
|
||||||
|
(
|
||||||
|
"1: \n"
|
||||||
|
"vld4.u8 {d0, d1, d2, d3}, [%0]! \n" // src line 0
|
||||||
|
"vmov d2, d3 \n" // order needs to be d0, d1, d2
|
||||||
|
"vst3.u8 {d0, d1, d2}, [%1]! \n"
|
||||||
|
"subs %2, #24 \n"
|
||||||
|
"bhi 1b \n"
|
||||||
|
: "+r"(src_ptr), // %0
|
||||||
|
"+r"(dst_ptr), // %1
|
||||||
|
"+r"(dst_width) // %2
|
||||||
|
:
|
||||||
|
: "d0", "d1", "d2", "d3", "memory", "cc"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ScaleRowDown34_0_Int_NEON(const uint8* src_ptr, int src_stride,
|
||||||
|
uint8* dst_ptr, int dst_width) {
|
||||||
|
__asm__ volatile
|
||||||
|
(
|
||||||
|
"vmov.u8 d16, #3 \n"
|
||||||
|
"add %3, %0 \n"
|
||||||
|
"1: \n"
|
||||||
|
"vld4.u8 {d0, d1, d2, d3}, [%0]! \n" // src line 0
|
||||||
|
"vld4.u8 {d4, d5, d6, d7}, [%3]! \n" // src line 1
|
||||||
|
|
||||||
|
// filter src line 0 with src line 1
|
||||||
|
// expand chars to shorts to allow for room
|
||||||
|
// when adding lines together
|
||||||
|
"vmovl.u8 q4, d4 \n"
|
||||||
|
"vmovl.u8 q5, d5 \n"
|
||||||
|
"vmovl.u8 q6, d6 \n"
|
||||||
|
"vmovl.u8 q7, d7 \n"
|
||||||
|
|
||||||
|
// 3 * line_0 + line_1
|
||||||
|
"vmlal.u8 q4, d0, d16 \n"
|
||||||
|
"vmlal.u8 q5, d1, d16 \n"
|
||||||
|
"vmlal.u8 q6, d2, d16 \n"
|
||||||
|
"vmlal.u8 q7, d3, d16 \n"
|
||||||
|
|
||||||
|
// (3 * line_0 + line_1) >> 2
|
||||||
|
"vqrshrn.u16 d0, q4, #2 \n"
|
||||||
|
"vqrshrn.u16 d1, q5, #2 \n"
|
||||||
|
"vqrshrn.u16 d2, q6, #2 \n"
|
||||||
|
"vqrshrn.u16 d3, q7, #2 \n"
|
||||||
|
|
||||||
|
// a0 = (src[0] * 3 + s[1] * 1) >> 2
|
||||||
|
"vmovl.u8 q4, d1 \n"
|
||||||
|
"vmlal.u8 q4, d0, d16 \n"
|
||||||
|
"vqrshrn.u16 d0, q4, #2 \n"
|
||||||
|
|
||||||
|
// a1 = (src[1] * 1 + s[2] * 1) >> 1
|
||||||
|
"vrhadd.u8 d1, d1, d2 \n"
|
||||||
|
|
||||||
|
// a2 = (src[2] * 1 + s[3] * 3) >> 2
|
||||||
|
"vmovl.u8 q4, d2 \n"
|
||||||
|
"vmlal.u8 q4, d3, d16 \n"
|
||||||
|
"vqrshrn.u16 d2, q4, #2 \n"
|
||||||
|
|
||||||
|
"vst3.u8 {d0, d1, d2}, [%1]! \n"
|
||||||
|
|
||||||
|
"subs %2, #24 \n"
|
||||||
|
"bhi 1b \n"
|
||||||
|
: "+r"(src_ptr), // %0
|
||||||
|
"+r"(dst_ptr), // %1
|
||||||
|
"+r"(dst_width), // %2
|
||||||
|
"+r"(src_stride) // %3
|
||||||
|
:
|
||||||
|
: "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "d17", "memory", "cc"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ScaleRowDown34_1_Int_NEON(const uint8* src_ptr, int src_stride,
|
||||||
|
uint8* dst_ptr, int dst_width) {
|
||||||
|
__asm__ volatile
|
||||||
|
(
|
||||||
|
"vmov.u8 d10, #3 \n"
|
||||||
|
"add %3, %0 \n"
|
||||||
|
"1: \n"
|
||||||
|
"vld4.u8 {d0, d1, d2, d3}, [%0]! \n" // src line 0
|
||||||
|
"vld4.u8 {d4, d5, d6, d7}, [%3]! \n" // src line 1
|
||||||
|
|
||||||
|
// average src line 0 with src line 1
|
||||||
|
"vrhadd.u8 q0, q0, q2 \n"
|
||||||
|
"vrhadd.u8 q1, q1, q3 \n"
|
||||||
|
|
||||||
|
// a0 = (src[0] * 3 + s[1] * 1) >> 2
|
||||||
|
"vmovl.u8 q3, d1 \n"
|
||||||
|
"vmlal.u8 q3, d0, d10 \n"
|
||||||
|
"vqrshrn.u16 d0, q3, #2 \n"
|
||||||
|
|
||||||
|
// a1 = (src[1] * 1 + s[2] * 1) >> 1
|
||||||
|
"vrhadd.u8 d1, d1, d2 \n"
|
||||||
|
|
||||||
|
// a2 = (src[2] * 1 + s[3] * 3) >> 2
|
||||||
|
"vmovl.u8 q3, d2 \n"
|
||||||
|
"vmlal.u8 q3, d3, d10 \n"
|
||||||
|
"vqrshrn.u16 d2, q3, #2 \n"
|
||||||
|
|
||||||
|
"vst3.u8 {d0, d1, d2}, [%1]! \n"
|
||||||
|
|
||||||
|
"subs %2, #24 \n"
|
||||||
|
"bhi 1b \n"
|
||||||
|
: "+r"(src_ptr), // %0
|
||||||
|
"+r"(dst_ptr), // %1
|
||||||
|
"+r"(dst_width), // %2
|
||||||
|
"+r"(src_stride) // %3
|
||||||
|
:
|
||||||
|
: "r4", "q0", "q1", "q2", "q3", "d10", "memory", "cc"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SSE2 downscalers with interpolation.
|
* SSE2 downscalers with interpolation.
|
||||||
*
|
*
|
||||||
@ -2857,6 +2975,18 @@ static void ScalePlaneDown34(int src_width, int src_height,
|
|||||||
uint8* dst_ptr, int dst_width);
|
uint8* dst_ptr, int dst_width);
|
||||||
void (*ScaleRowDown34_1)(const uint8* src_ptr, int src_stride,
|
void (*ScaleRowDown34_1)(const uint8* src_ptr, int src_stride,
|
||||||
uint8* dst_ptr, int dst_width);
|
uint8* dst_ptr, int dst_width);
|
||||||
|
#if defined(HAS_SCALEROWDOWN34_NEON)
|
||||||
|
if (libyuv::TestCpuFlag(libyuv::kCpuHasNEON) &&
|
||||||
|
(dst_width % 24 == 0) && (dst_stride % 8 == 0)) {
|
||||||
|
if (!filtering) {
|
||||||
|
ScaleRowDown34_0 = ScaleRowDown34_NEON;
|
||||||
|
ScaleRowDown34_1 = ScaleRowDown34_NEON;
|
||||||
|
} else {
|
||||||
|
ScaleRowDown34_0 = ScaleRowDown34_0_Int_NEON;
|
||||||
|
ScaleRowDown34_1 = ScaleRowDown34_1_Int_NEON;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
#if defined(HAS_SCALEROWDOWN34_SSSE3)
|
#if defined(HAS_SCALEROWDOWN34_SSSE3)
|
||||||
if (libyuv::TestCpuFlag(libyuv::kCpuHasSSSE3) &&
|
if (libyuv::TestCpuFlag(libyuv::kCpuHasSSSE3) &&
|
||||||
(dst_width % 24 == 0) && (src_stride % 16 == 0) &&
|
(dst_width % 24 == 0) && (src_stride % 16 == 0) &&
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
* be found in the AUTHORS file in the root of the source tree.
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "libyuv/cpu_id.h"
|
||||||
#include "libyuv/scale.h"
|
#include "libyuv/scale.h"
|
||||||
#include "unit_test.h"
|
#include "unit_test.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -157,3 +158,129 @@ TEST_F(libyuvTest, ScaleDownBy4) {
|
|||||||
|
|
||||||
EXPECT_EQ(0, err);
|
EXPECT_EQ(0, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(libyuvTest, ScaleDownBy34) {
|
||||||
|
int b = 128;
|
||||||
|
int src_width = 1280;
|
||||||
|
int src_height = 720;
|
||||||
|
int src_width_uv = (src_width + 1) >> 1;
|
||||||
|
int src_height_uv = (src_height + 1) >> 1;
|
||||||
|
|
||||||
|
int src_y_plane_size = (src_width + (2 * b)) * (src_height + (2 * b));
|
||||||
|
int src_uv_plane_size = (src_width_uv + (2 * b)) * (src_height_uv + (2 * b));
|
||||||
|
|
||||||
|
int src_stride_y = 2 * b + src_width;
|
||||||
|
int src_stride_uv = 2 * b + src_width_uv;
|
||||||
|
|
||||||
|
align_buffer_16(src_y, src_y_plane_size)
|
||||||
|
align_buffer_16(src_u, src_uv_plane_size)
|
||||||
|
align_buffer_16(src_v, src_uv_plane_size)
|
||||||
|
|
||||||
|
int dst_width = (src_width*3) >> 2;
|
||||||
|
int dst_height = (src_height*3) >> 2;
|
||||||
|
|
||||||
|
int dst_width_uv = (dst_width + 1) >> 1;
|
||||||
|
int dst_height_uv = (dst_height + 1) >> 1;
|
||||||
|
|
||||||
|
int dst_y_plane_size = (dst_width + (2 * b)) * (dst_height + (2 * b));
|
||||||
|
int dst_uv_plane_size = (dst_width_uv + (2 * b)) * (dst_height_uv + (2 * b));
|
||||||
|
|
||||||
|
int dst_stride_y = 2 * b + dst_width;
|
||||||
|
int dst_stride_uv = 2 * b + dst_width_uv;
|
||||||
|
|
||||||
|
srandom(time(NULL));
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (i = b; i < (src_height + b); ++i) {
|
||||||
|
for (j = b; j < (src_width + b); ++j) {
|
||||||
|
src_y[(i * src_stride_y) + j] = (random() & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = b; i < (src_height_uv + b); ++i) {
|
||||||
|
for (j = b; j < (src_width_uv + b); ++j) {
|
||||||
|
src_u[(i * src_stride_uv) + j] = (random() & 0xff);
|
||||||
|
src_v[(i * src_stride_uv) + j] = (random() & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int f;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
// currently three filter modes, defined as FilterMode in scale.h
|
||||||
|
for (f = 0; f < 3; ++f) {
|
||||||
|
int max_diff = 0;
|
||||||
|
align_buffer_16(dst_y_c, dst_y_plane_size)
|
||||||
|
align_buffer_16(dst_u_c, dst_uv_plane_size)
|
||||||
|
align_buffer_16(dst_v_c, dst_uv_plane_size)
|
||||||
|
align_buffer_16(dst_y_opt, dst_y_plane_size)
|
||||||
|
align_buffer_16(dst_u_opt, dst_uv_plane_size)
|
||||||
|
align_buffer_16(dst_v_opt, dst_uv_plane_size)
|
||||||
|
|
||||||
|
libyuv::MaskCpuFlagsForTest(0);
|
||||||
|
I420Scale(src_y + (src_stride_y * b) + b, src_stride_y,
|
||||||
|
src_u + (src_stride_uv * b) + b, src_stride_uv,
|
||||||
|
src_v + (src_stride_uv * b) + b, src_stride_uv,
|
||||||
|
src_width, src_height,
|
||||||
|
dst_y_c + (dst_stride_y * b) + b, dst_stride_y,
|
||||||
|
dst_u_c + (dst_stride_uv * b) + b, dst_stride_uv,
|
||||||
|
dst_v_c + (dst_stride_uv * b) + b, dst_stride_uv,
|
||||||
|
dst_width, dst_height,
|
||||||
|
static_cast<FilterMode>(f));
|
||||||
|
|
||||||
|
libyuv::MaskCpuFlagsForTest(-1);
|
||||||
|
I420Scale(src_y + (src_stride_y * b) + b, src_stride_y,
|
||||||
|
src_u + (src_stride_uv * b) + b, src_stride_uv,
|
||||||
|
src_v + (src_stride_uv * b) + b, src_stride_uv,
|
||||||
|
src_width, src_height,
|
||||||
|
dst_y_opt + (dst_stride_y * b) + b, dst_stride_y,
|
||||||
|
dst_u_opt + (dst_stride_uv * b) + b, dst_stride_uv,
|
||||||
|
dst_v_opt + (dst_stride_uv * b) + b, dst_stride_uv,
|
||||||
|
dst_width, dst_height,
|
||||||
|
static_cast<FilterMode>(f));
|
||||||
|
|
||||||
|
// C version may be a little off from the optimized. Order of
|
||||||
|
// operations may introduce rounding somewhere. So do a difference
|
||||||
|
// of the buffers and look to see that the max difference isn't
|
||||||
|
// over 2.
|
||||||
|
for (i = b; i < (dst_height + b); ++i) {
|
||||||
|
for (j = b; j < (dst_width + b); ++j) {
|
||||||
|
int abs_diff = abs(dst_y_c[(i * dst_stride_y) + j] -
|
||||||
|
dst_y_opt[(i * dst_stride_y) + j]);
|
||||||
|
if (abs_diff > max_diff)
|
||||||
|
max_diff = abs_diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = b; i < (dst_height_uv + b); ++i) {
|
||||||
|
for (j = b; j < (dst_width_uv + b); ++j) {
|
||||||
|
int abs_diff = abs(dst_u_c[(i * dst_stride_uv) + j] -
|
||||||
|
dst_u_opt[(i * dst_stride_uv) + j]);
|
||||||
|
if (abs_diff > max_diff)
|
||||||
|
max_diff = abs_diff;
|
||||||
|
abs_diff = abs(dst_v_c[(i * dst_stride_uv) + j] -
|
||||||
|
dst_v_opt[(i * dst_stride_uv) + j]);
|
||||||
|
if (abs_diff > max_diff)
|
||||||
|
max_diff = abs_diff;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_diff > 2)
|
||||||
|
err++;
|
||||||
|
|
||||||
|
free_aligned_buffer_16(dst_y_c)
|
||||||
|
free_aligned_buffer_16(dst_u_c)
|
||||||
|
free_aligned_buffer_16(dst_v_c)
|
||||||
|
free_aligned_buffer_16(dst_y_opt)
|
||||||
|
free_aligned_buffer_16(dst_u_opt)
|
||||||
|
free_aligned_buffer_16(dst_v_opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
free_aligned_buffer_16(src_y)
|
||||||
|
free_aligned_buffer_16(src_u)
|
||||||
|
free_aligned_buffer_16(src_v)
|
||||||
|
|
||||||
|
EXPECT_EQ(0, err);
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user