From 35e98a70ba65d82f0b94df3f5e453a3a9f0d6365 Mon Sep 17 00:00:00 2001 From: Luc Trudeau Date: Fri, 18 May 2018 13:38:48 -0400 Subject: [PATCH] VSX version of vpx_mbpost_proc_down Low bit depth version only. Passes the VpxMbPostProcDownTest. VpxMbPostProcDownTest Speed Test (POWER8 Model 2.1) Full calculations: C time = 195.4 ms, VSX time = 33.7 ms (5.8x) Change-Id: If1aca7c135de036a1ab7923c0d1e6733bfe27ef7 --- test/pp_filter_test.cc | 60 +++++++++------- vpx_dsp/ppc/deblock_vsx.c | 128 +++++++++++++++++++++++++++++++++++ vpx_dsp/ppc/types_vsx.h | 1 + vpx_dsp/vpx_dsp.mk | 1 + vpx_dsp/vpx_dsp_rtcd_defs.pl | 2 +- 5 files changed, 168 insertions(+), 24 deletions(-) create mode 100644 vpx_dsp/ppc/deblock_vsx.c diff --git a/test/pp_filter_test.cc b/test/pp_filter_test.cc index 5a2ade1ef..13e975dfa 100644 --- a/test/pp_filter_test.cc +++ b/test/pp_filter_test.cc @@ -11,6 +11,7 @@ #include "./vpx_config.h" #include "./vpx_dsp_rtcd.h" #include "test/acm_random.h" +#include "test/bench.h" #include "test/buffer.h" #include "test/clear_system_state.h" #include "test/register_state_check.h" @@ -32,7 +33,6 @@ typedef void (*VpxMbPostProcDownFunc)(unsigned char *dst, int pitch, int rows, int cols, int flimit); namespace { - // Compute the filter level used in post proc from the loop filter strength int q2mbl(int x) { if (x < 20) x = 20; @@ -280,11 +280,22 @@ TEST_P(VpxMbPostProcAcrossIpTest, CheckCvsAssembly) { } class VpxMbPostProcDownTest - : public ::testing::TestWithParam { + : public AbstractBench, + public ::testing::TestWithParam { public: + VpxMbPostProcDownTest() + : rows(16), cols(16), mbPostProcDown(GetParam()), + src_c(Buffer(rows, cols, 8, 8, 8, 17)) {} + virtual void TearDown() { libvpx_test::ClearSystemState(); } protected: + const int rows; + const int cols; + const VpxMbPostProcDownFunc mbPostProcDown; + Buffer src_c; + void run(); + void SetRows(unsigned char *src_c, int rows, int cols, int src_width) { for (int r = 0; r < rows; r++) { memset(src_c, r, cols); @@ -306,22 +317,22 @@ class VpxMbPostProcDownTest void RunFilterLevel(unsigned char *s, int rows, int cols, int src_width, int filter_level, const unsigned char *expected_output) { ASM_REGISTER_STATE_CHECK( - GetParam()(s, src_width, rows, cols, filter_level)); + mbPostProcDown(s, src_width, rows, cols, filter_level)); RunComparison(expected_output, s, rows, cols, src_width); } }; -TEST_P(VpxMbPostProcDownTest, CheckHighFilterOutput) { - const int rows = 16; - const int cols = 16; +void VpxMbPostProcDownTest::run() { + mbPostProcDown(src_c.TopLeftPixel(), src_c.stride(), rows, cols, q2mbl(0)); +} - Buffer src_c = Buffer(cols, rows, 8, 8, 8, 17); +TEST_P(VpxMbPostProcDownTest, CheckHighFilterOutput) { ASSERT_TRUE(src_c.Init()); src_c.SetPadding(10); SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride()); - static const unsigned char kExpectedOutput[rows * cols] = { + static const unsigned char kExpectedOutput[] = { 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 3, @@ -348,16 +359,12 @@ TEST_P(VpxMbPostProcDownTest, CheckHighFilterOutput) { } TEST_P(VpxMbPostProcDownTest, CheckMediumFilterOutput) { - const int rows = 16; - const int cols = 16; - - Buffer src_c = Buffer(cols, rows, 8, 8, 8, 17); ASSERT_TRUE(src_c.Init()); src_c.SetPadding(10); SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride()); - static const unsigned char kExpectedOutput[rows * cols] = { + static const unsigned char kExpectedOutput[] = { 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, @@ -379,10 +386,6 @@ TEST_P(VpxMbPostProcDownTest, CheckMediumFilterOutput) { } TEST_P(VpxMbPostProcDownTest, CheckLowFilterOutput) { - const int rows = 16; - const int cols = 16; - - Buffer src_c = Buffer(cols, rows, 8, 8, 8, 17); ASSERT_TRUE(src_c.Init()); src_c.SetPadding(10); @@ -399,13 +402,9 @@ TEST_P(VpxMbPostProcDownTest, CheckLowFilterOutput) { } TEST_P(VpxMbPostProcDownTest, CheckCvsAssembly) { - const int rows = 16; - const int cols = 16; - ACMRandom rnd; rnd.Reset(ACMRandom::DeterministicSeed()); - Buffer src_c = Buffer(cols, rows, 8, 8, 8, 17); ASSERT_TRUE(src_c.Init()); Buffer src_asm = Buffer(cols, rows, 8, 8, 8, 17); ASSERT_TRUE(src_asm.Init()); @@ -418,7 +417,7 @@ TEST_P(VpxMbPostProcDownTest, CheckCvsAssembly) { vpx_mbpost_proc_down_c(src_c.TopLeftPixel(), src_c.stride(), rows, cols, q2mbl(level)); - ASM_REGISTER_STATE_CHECK(GetParam()( + ASM_REGISTER_STATE_CHECK(mbPostProcDown( src_asm.TopLeftPixel(), src_asm.stride(), rows, cols, q2mbl(level))); ASSERT_TRUE(src_asm.CheckValues(src_c)); @@ -429,12 +428,22 @@ TEST_P(VpxMbPostProcDownTest, CheckCvsAssembly) { vpx_mbpost_proc_down_c(src_c.TopLeftPixel(), src_c.stride(), rows, cols, q2mbl(level)); - ASM_REGISTER_STATE_CHECK(GetParam()( + ASM_REGISTER_STATE_CHECK(mbPostProcDown( src_asm.TopLeftPixel(), src_asm.stride(), rows, cols, q2mbl(level))); ASSERT_TRUE(src_asm.CheckValues(src_c)); } } +TEST_P(VpxMbPostProcDownTest, DISABLED_Speed) { + ASSERT_TRUE(src_c.Init()); + src_c.SetPadding(10); + + SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride()); + + runNTimes(100000); + printMedian("16x16"); +} + INSTANTIATE_TEST_CASE_P( C, VpxPostProcDownAndAcrossMbRowTest, ::testing::Values(vpx_post_proc_down_and_across_mb_row_c)); @@ -481,4 +490,9 @@ INSTANTIATE_TEST_CASE_P(MSA, VpxMbPostProcDownTest, ::testing::Values(vpx_mbpost_proc_down_msa)); #endif // HAVE_MSA +#if HAVE_VSX +INSTANTIATE_TEST_CASE_P(VSX, VpxMbPostProcDownTest, + ::testing::Values(vpx_mbpost_proc_down_vsx)); +#endif // HAVE_VSX + } // namespace diff --git a/vpx_dsp/ppc/deblock_vsx.c b/vpx_dsp/ppc/deblock_vsx.c new file mode 100644 index 000000000..4a031832c --- /dev/null +++ b/vpx_dsp/ppc/deblock_vsx.c @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2018 The WebM 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 + +#include "./vpx_dsp_rtcd.h" +#include "vpx_dsp/ppc/types_vsx.h" + +extern const int16_t vpx_rv[]; + +static const uint8x16_t load_merge = { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, + 0x0C, 0x0E, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F }; + +static const uint8x16_t mask_merge = { 0x00, 0x01, 0x10, 0x11, 0x04, 0x05, + 0x14, 0x15, 0x08, 0x09, 0x18, 0x19, + 0x0C, 0x0D, 0x1C, 0x1D }; + +void vpx_mbpost_proc_down_vsx(uint8_t *dst, int pitch, int rows, int cols, + int flimit) { + int col, row, i; + int16x8_t window[16]; + const int32x4_t lim = vec_splats(flimit); + + // 8 columns are processed at a time. + assert(cols % 8 == 0); + // If rows is less than 8 the bottom border extension fails. + assert(rows >= 8); + + for (col = 0; col < cols; col += 8) { + // The sum is signed and requires at most 13 bits. + // (8 bits + sign) * 15 (4 bits) + int16x8_t r1, sum; + // The sum of squares requires at most 20 bits. + // (16 bits + sign) * 15 (4 bits) + int32x4_t sumsq_even, sumsq_odd; + + r1 = unpack_to_s16_h(vec_vsx_ld(0, dst)); + // Fill sliding window with first row. + for (i = 0; i <= 8; i++) { + window[i] = r1; + } + // First 9 rows of the sliding window are the same. + // sum = r1 * 9 + sum = vec_mladd(r1, vec_splats((int16_t)9), vec_zeros_s16); + + // sumsq = r1 * r1 * 9 + sumsq_even = vec_mule(sum, r1); + sumsq_odd = vec_mulo(sum, r1); + + // Fill the next 6 rows of the sliding window with rows 2 to 7. + for (i = 1; i <= 6; ++i) { + const int16x8_t next_row = unpack_to_s16_h(vec_vsx_ld(i * pitch, dst)); + window[i + 8] = next_row; + sum = vec_add(sum, next_row); + sumsq_odd = vec_add(sumsq_odd, vec_mulo(next_row, next_row)); + sumsq_even = vec_add(sumsq_even, vec_mule(next_row, next_row)); + } + + for (row = 0; row < rows; row++) { + int32x4_t d15_even, d15_odd, d0_even, d0_odd; + int32x4_t sumsq_odd_scaled, sumsq_even_scaled; + int32x4_t thres_odd, thres_even; + bool32x4_t mask_odd, mask_even; + bool16x8_t mask; + int16x8_t filtered, masked; + uint8x16_t out; + + const int16x8_t rv = vec_vsx_ld(0, vpx_rv + (row & 127)); + + // Move the sliding window + if (row + 7 < rows) { + window[15] = unpack_to_s16_h(vec_vsx_ld((row + 7) * pitch, dst)); + } else { + window[15] = window[14]; + } + + // C: sum += s[7 * pitch] - s[-8 * pitch]; + sum = vec_add(sum, vec_sub(window[15], window[0])); + + // C: sumsq += s[7 * pitch] * s[7 * pitch] - s[-8 * pitch] * s[-8 * + // pitch]; + // Optimization Note: Caching a squared-window for odd and even is + // slower than just repeating the multiplies. + d15_odd = vec_mulo(window[15], window[15]); + d15_even = vec_mule(window[15], window[15]); + d0_odd = vec_mulo(window[0], window[0]); + d0_even = vec_mule(window[0], window[0]); + sumsq_odd = vec_add(sumsq_odd, vec_sub(d15_odd, d0_odd)); + sumsq_even = vec_add(sumsq_even, vec_sub(d15_even, d0_even)); + + // C: sumsq * 15 - sum * sum + sumsq_odd_scaled = vec_mul(sumsq_odd, vec_splats((int32_t)15)); + sumsq_even_scaled = vec_mul(sumsq_even, vec_splats((int32_t)15)); + thres_odd = vec_sub(sumsq_odd_scaled, vec_mulo(sum, sum)); + thres_even = vec_sub(sumsq_even_scaled, vec_mule(sum, sum)); + + // C: (vpx_rv[(r & 127) + (c & 7)] + sum + s[0]) >> 4 + filtered = vec_add(vec_add(rv, sum), window[8]); + filtered = vec_sra(filtered, vec_splats((uint16_t)4)); + + mask_odd = vec_cmplt(thres_odd, lim); + mask_even = vec_cmplt(thres_even, lim); + mask = vec_perm((bool16x8_t)mask_even, (bool16x8_t)mask_odd, mask_merge); + masked = vec_sel(window[8], filtered, mask); + + // TODO(ltrudeau) If cols % 16 == 0, we could just process 16 per + // iteration + out = vec_perm((uint8x16_t)masked, vec_vsx_ld(0, dst + row * pitch), + load_merge); + vec_vsx_st(out, 0, dst + row * pitch); + + // Optimization Note: Turns out that the following loop is faster than + // using pointers to manage the sliding window. + for (i = 1; i < 16; i++) { + window[i - 1] = window[i]; + } + } + dst += 8; + } +} diff --git a/vpx_dsp/ppc/types_vsx.h b/vpx_dsp/ppc/types_vsx.h index a5d2a2255..9e7476b58 100644 --- a/vpx_dsp/ppc/types_vsx.h +++ b/vpx_dsp/ppc/types_vsx.h @@ -20,6 +20,7 @@ typedef vector unsigned short uint16x8_t; typedef vector signed int int32x4_t; typedef vector unsigned int uint32x4_t; typedef vector bool short bool16x8_t; +typedef vector bool int bool32x4_t; #ifdef __clang__ static const uint8x16_t xxpermdi0_perm = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, diff --git a/vpx_dsp/vpx_dsp.mk b/vpx_dsp/vpx_dsp.mk index cb06a476f..573d6fef1 100644 --- a/vpx_dsp/vpx_dsp.mk +++ b/vpx_dsp/vpx_dsp.mk @@ -69,6 +69,7 @@ DSP_SRCS-$(HAVE_MSA) += mips/deblock_msa.c DSP_SRCS-$(HAVE_NEON) += arm/deblock_neon.c DSP_SRCS-$(HAVE_SSE2) += x86/add_noise_sse2.asm DSP_SRCS-$(HAVE_SSE2) += x86/deblock_sse2.asm +DSP_SRCS-$(HAVE_VSX) += ppc/deblock_vsx.c endif # CONFIG_POSTPROC DSP_SRCS-$(HAVE_NEON_ASM) += arm/intrapred_neon_asm$(ASM) diff --git a/vpx_dsp/vpx_dsp_rtcd_defs.pl b/vpx_dsp/vpx_dsp_rtcd_defs.pl index 824ae0f43..ff57d87cb 100644 --- a/vpx_dsp/vpx_dsp_rtcd_defs.pl +++ b/vpx_dsp/vpx_dsp_rtcd_defs.pl @@ -1598,7 +1598,7 @@ if (vpx_config("CONFIG_POSTPROC") eq "yes" || vpx_config("CONFIG_VP9_POSTPROC") specialize qw/vpx_plane_add_noise sse2 msa/; add_proto qw/void vpx_mbpost_proc_down/, "unsigned char *dst, int pitch, int rows, int cols,int flimit"; - specialize qw/vpx_mbpost_proc_down sse2 neon msa/; + specialize qw/vpx_mbpost_proc_down sse2 neon msa vsx/; add_proto qw/void vpx_mbpost_proc_across_ip/, "unsigned char *dst, int pitch, int rows, int cols,int flimit"; specialize qw/vpx_mbpost_proc_across_ip sse2 neon msa/; -- 2.40.0