| /* |
| * Copyright (c) 2018, Alliance for Open Media. All rights reserved |
| * |
| * This source code is subject to the terms of the BSD 2 Clause License and |
| * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License |
| * was not distributed with this source code in the LICENSE file, you can |
| * obtain it at www.aomedia.org/license/software. If the Alliance for Open |
| * Media Patent License 1.0 was not distributed with this source code in the |
| * PATENTS file, you can obtain it at www.aomedia.org/license/patent. |
| */ |
| |
| #include <arm_neon.h> |
| #include <assert.h> |
| |
| #include "config/aom_config.h" |
| #include "config/av1_rtcd.h" |
| |
| #include "aom_dsp/txfm_common.h" |
| #include "aom_dsp/arm/mem_neon.h" |
| #include "aom_dsp/arm/transpose_neon.h" |
| #include "aom_ports/mem.h" |
| #include "av1/common/common.h" |
| #include "av1/common/arm/convolve_neon.h" |
| |
| #define HORZ_FILTERING_CORE(t0, t1, t2, t3, t4, t5, t6, res) \ |
| res0 = vreinterpretq_s16_u16(vaddl_u8(t0, t1)); \ |
| res1 = vreinterpretq_s16_u16(vaddl_u8(t2, t3)); \ |
| res2 = vreinterpretq_s16_u16(vaddl_u8(t4, t5)); \ |
| res3 = vreinterpretq_s16_u16(vmovl_u8(t6)); \ |
| res = wiener_convolve8_horiz_8x8(res0, res1, res2, res3, filter_x_tmp, bd, \ |
| conv_params->round_0); |
| |
| #define PROCESS_ROW_FOR_VERTICAL_FILTER \ |
| __builtin_prefetch(dst_tmp_ptr + 0 * dst_stride); \ |
| \ |
| do { \ |
| s7 = vld1q_s16(s); \ |
| s += src_stride; \ |
| \ |
| t0 = wiener_convolve8_vert_4x8(s0, s1, s2, s3, s4, s5, s6, filter_y_tmp, \ |
| bd, conv_params->round_1); \ |
| vst1_u8(d, t0); \ |
| d += dst_stride; \ |
| \ |
| s0 = s1; \ |
| s1 = s2; \ |
| s2 = s3; \ |
| s3 = s4; \ |
| s4 = s5; \ |
| s5 = s6; \ |
| s6 = s7; \ |
| height--; \ |
| } while (height > 0); |
| |
| static INLINE void process_row_for_horz_filtering( |
| uint16_t *dst_ptr, int16_t *filter_x, const uint8_t *src_ptr, |
| ptrdiff_t src_stride, ptrdiff_t dst_stride, int round0_bits, int w, |
| int height, int bd) { |
| do { |
| __builtin_prefetch(src_ptr); |
| |
| uint8x8_t tt0 = vld1_u8(src_ptr); // a0 a1 a2 a3 a4 a5 a6 a7 |
| |
| __builtin_prefetch(dst_ptr); |
| |
| const uint8_t *ss = src_ptr + 8; |
| uint16_t *d_tmp = dst_ptr; |
| int width = w; |
| |
| do { |
| uint8x8_t tt7 = vld1_u8(ss); // a8 a9 a10 a11 a12 a13 a14 a15 |
| uint8x8_t ttemp_0 = tt0; |
| tt0 = tt7; |
| |
| uint8x8_t tt1 = vext_u8(ttemp_0, tt7, 1); // a1 a2 a3 a4 a5 a6 a7 a8 |
| uint8x8_t tt2 = vext_u8(ttemp_0, tt7, 2); // a2 a3 a4 a5 a6 a7 a8 a9 |
| uint8x8_t tt3 = vext_u8(ttemp_0, tt7, 3); // a3 a4 a5 a6 a7 a8 a9 a10 |
| uint8x8_t tt4 = vext_u8(ttemp_0, tt7, 4); // a4 a5 a6 a7 a8 a9 a10 a11 |
| uint8x8_t tt5 = vext_u8(ttemp_0, tt7, 5); // a5 a6 a7 a8 a9 a10 a11 a12 |
| uint8x8_t tt6 = vext_u8(ttemp_0, tt7, 6); // a6 a7 a8 a9 a10 a11 a12 a13 |
| tt7 = vext_u8(ttemp_0, tt7, 7); // a7 a8 a9 a10 a11 a12 a13 a14 |
| |
| int16x8_t ttt0 = vreinterpretq_s16_u16(vaddl_u8(ttemp_0, tt6)); |
| int16x8_t ttt1 = vreinterpretq_s16_u16(vaddl_u8(tt1, tt5)); |
| int16x8_t ttt2 = vreinterpretq_s16_u16(vaddl_u8(tt2, tt4)); |
| int16x8_t ttt3 = vreinterpretq_s16_u16(vmovl_u8(tt3)); |
| uint16x8_t dd0 = wiener_convolve8_horiz_8x8(ttt0, ttt1, ttt2, ttt3, |
| filter_x, bd, round0_bits); |
| |
| vst1q_u16(d_tmp, dd0); |
| |
| ss += 8; |
| d_tmp += 8; |
| width -= 8; |
| } while (width > 0); |
| |
| src_ptr += src_stride; |
| dst_ptr += dst_stride; |
| height--; |
| } while (height > 0); |
| } |
| |
| /* Wiener filter 2D |
| Apply horizontal filter and store in a temporary buffer. When applying |
| vertical filter, overwrite the original pixel values. |
| */ |
| void av1_wiener_convolve_add_src_neon(const uint8_t *src, ptrdiff_t src_stride, |
| uint8_t *dst, ptrdiff_t dst_stride, |
| const int16_t *filter_x, int x_step_q4, |
| const int16_t *filter_y, int y_step_q4, |
| int w, int h, |
| const ConvolveParams *conv_params) { |
| uint8_t *d; |
| const uint8_t *src_ptr, *s_tmp; |
| uint16_t *dst_ptr; |
| (void)x_step_q4; |
| (void)y_step_q4; |
| |
| int height; |
| const int bd = 8; |
| // Indicates the height needs to be processed during horizontal filtering. |
| const int intermediate_height = h + SUBPEL_TAPS - 1; |
| const int center_tap = ((SUBPEL_TAPS - 1) / 2); |
| int16_t filter_x_tmp[7], filter_y_tmp[7]; |
| |
| DECLARE_ALIGNED(16, uint16_t, |
| temp[(MAX_SB_SIZE + HORIZ_EXTRA_ROWS) * MAX_SB_SIZE]); |
| |
| assert(x_step_q4 == 16 && y_step_q4 == 16); |
| assert(!(w % 8)); |
| |
| assert(w <= MAX_SB_SIZE); |
| assert(h <= MAX_SB_SIZE); |
| |
| assert(filter_x[7] == 0); |
| assert(filter_y[7] == 0); |
| |
| /* assumption of horizontal filtering output will not exceed 15 bit. |
| ((bd) + 1 + FILTER_BITS - conv_params->round_0) <= 15 |
| 16 - conv_params->round_0 <= 15 -- (conv_params->round_0) >= 1 |
| */ |
| assert((conv_params->round_0) >= 1); |
| |
| memcpy(&filter_x_tmp[0], filter_x, sizeof(*filter_x) * FILTER_BITS); |
| memcpy(&filter_y_tmp[0], filter_y, sizeof(*filter_y) * FILTER_BITS); |
| |
| filter_x_tmp[3] += (1 << FILTER_BITS); |
| filter_y_tmp[3] += (1 << FILTER_BITS); |
| |
| s_tmp = src - center_tap * src_stride - center_tap; |
| dst_ptr = temp; |
| src_ptr = s_tmp; |
| height = intermediate_height; |
| |
| // For aarch_64. |
| #if defined(__aarch64__) |
| int processed_height = 0; |
| uint16_t *d_tmp; |
| int width, remaining_height; |
| // Start of horizontal filtering. |
| if (intermediate_height > 7) { |
| uint16x8_t res4, res5, res6, res7, res8, res9, res10, res11; |
| uint8x8_t t0, t1, t2, t3, t4, t5, t6, t7; |
| do { |
| const uint8_t *s; |
| |
| __builtin_prefetch(src_ptr + 0 * src_stride); |
| __builtin_prefetch(src_ptr + 1 * src_stride); |
| __builtin_prefetch(src_ptr + 2 * src_stride); |
| __builtin_prefetch(src_ptr + 3 * src_stride); |
| __builtin_prefetch(src_ptr + 4 * src_stride); |
| __builtin_prefetch(src_ptr + 5 * src_stride); |
| __builtin_prefetch(src_ptr + 6 * src_stride); |
| __builtin_prefetch(src_ptr + 7 * src_stride); |
| |
| load_u8_8x8(src_ptr, src_stride, &t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7); |
| transpose_u8_8x8(&t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7); |
| |
| s = src_ptr + 7; |
| d_tmp = dst_ptr; |
| width = w; |
| |
| __builtin_prefetch(dst_ptr + 0 * dst_stride); |
| __builtin_prefetch(dst_ptr + 1 * dst_stride); |
| __builtin_prefetch(dst_ptr + 2 * dst_stride); |
| __builtin_prefetch(dst_ptr + 3 * dst_stride); |
| __builtin_prefetch(dst_ptr + 4 * dst_stride); |
| __builtin_prefetch(dst_ptr + 5 * dst_stride); |
| __builtin_prefetch(dst_ptr + 6 * dst_stride); |
| __builtin_prefetch(dst_ptr + 7 * dst_stride); |
| |
| do { |
| int16x8_t res0, res1, res2, res3; |
| uint8x8_t t8, t9, t10, t11, t12, t13, t14; |
| load_u8_8x8(s, src_stride, &t7, &t8, &t9, &t10, &t11, &t12, &t13, &t14); |
| transpose_u8_8x8(&t7, &t8, &t9, &t10, &t11, &t12, &t13, &t14); |
| |
| HORZ_FILTERING_CORE(t0, t6, t1, t5, t2, t4, t3, res4) |
| HORZ_FILTERING_CORE(t1, t7, t2, t6, t3, t5, t4, res5) |
| HORZ_FILTERING_CORE(t2, t8, t3, t7, t4, t6, t5, res6) |
| HORZ_FILTERING_CORE(t3, t9, t4, t8, t5, t7, t6, res7) |
| HORZ_FILTERING_CORE(t4, t10, t5, t9, t6, t8, t7, res8) |
| HORZ_FILTERING_CORE(t5, t11, t6, t10, t7, t9, t8, res9) |
| HORZ_FILTERING_CORE(t6, t12, t7, t11, t8, t10, t9, res10) |
| HORZ_FILTERING_CORE(t7, t13, t8, t12, t9, t11, t10, res11) |
| |
| transpose_u16_8x8(&res4, &res5, &res6, &res7, &res8, &res9, &res10, |
| &res11); |
| store_u16_8x8(d_tmp, MAX_SB_SIZE, res4, res5, res6, res7, res8, res9, |
| res10, res11); |
| |
| t0 = t8; |
| t1 = t9; |
| t2 = t10; |
| t3 = t11; |
| t4 = t12; |
| t5 = t13; |
| t6 = t14; |
| s += 8; |
| d_tmp += 8; |
| width -= 8; |
| } while (width > 0); |
| src_ptr += 8 * src_stride; |
| dst_ptr += 8 * MAX_SB_SIZE; |
| height -= 8; |
| processed_height += 8; |
| } while (height > 7); |
| } |
| |
| // Process the remaining rows for horizontal filtering. |
| remaining_height = intermediate_height - processed_height; |
| if (remaining_height) |
| process_row_for_horz_filtering(dst_ptr, filter_x_tmp, src_ptr, src_stride, |
| MAX_SB_SIZE, conv_params->round_0, w, height, |
| bd); |
| |
| // Start of vertical filtering. |
| { |
| int16_t *src_tmp_ptr, *s; |
| uint8_t *dst_tmp_ptr; |
| height = h; |
| width = w; |
| src_tmp_ptr = (int16_t *)temp; |
| dst_tmp_ptr = dst; |
| src_stride = MAX_SB_SIZE; |
| |
| do { |
| int16x8_t s0, s1, s2, s3, s4, s5, s6, s7; |
| uint8x8_t t0; |
| s = src_tmp_ptr; |
| s0 = vld1q_s16(s); |
| s += src_stride; |
| s1 = vld1q_s16(s); |
| s += src_stride; |
| s2 = vld1q_s16(s); |
| s += src_stride; |
| s3 = vld1q_s16(s); |
| s += src_stride; |
| s4 = vld1q_s16(s); |
| s += src_stride; |
| s5 = vld1q_s16(s); |
| s += src_stride; |
| s6 = vld1q_s16(s); |
| s += src_stride; |
| d = dst_tmp_ptr; |
| height = h; |
| |
| do { |
| int16x8_t s8, s9, s10; |
| uint8x8_t t1, t2, t3; |
| __builtin_prefetch(dst_tmp_ptr + 0 * dst_stride); |
| __builtin_prefetch(dst_tmp_ptr + 1 * dst_stride); |
| __builtin_prefetch(dst_tmp_ptr + 2 * dst_stride); |
| __builtin_prefetch(dst_tmp_ptr + 3 * dst_stride); |
| |
| s7 = vld1q_s16(s); |
| s += src_stride; |
| s8 = vld1q_s16(s); |
| s += src_stride; |
| s9 = vld1q_s16(s); |
| s += src_stride; |
| s10 = vld1q_s16(s); |
| s += src_stride; |
| |
| t0 = wiener_convolve8_vert_4x8(s0, s1, s2, s3, s4, s5, s6, filter_y_tmp, |
| bd, conv_params->round_1); |
| t1 = wiener_convolve8_vert_4x8(s1, s2, s3, s4, s5, s6, s7, filter_y_tmp, |
| bd, conv_params->round_1); |
| t2 = wiener_convolve8_vert_4x8(s2, s3, s4, s5, s6, s7, s8, filter_y_tmp, |
| bd, conv_params->round_1); |
| t3 = wiener_convolve8_vert_4x8(s3, s4, s5, s6, s7, s8, s9, filter_y_tmp, |
| bd, conv_params->round_1); |
| |
| vst1_u8(d, t0); |
| d += dst_stride; |
| vst1_u8(d, t1); |
| d += dst_stride; |
| vst1_u8(d, t2); |
| d += dst_stride; |
| vst1_u8(d, t3); |
| d += dst_stride; |
| |
| s0 = s4; |
| s1 = s5; |
| s2 = s6; |
| s3 = s7; |
| s4 = s8; |
| s5 = s9; |
| s6 = s10; |
| height -= 4; |
| } while (height > 3); |
| |
| if (height) { |
| PROCESS_ROW_FOR_VERTICAL_FILTER |
| } |
| src_tmp_ptr += 8; |
| dst_tmp_ptr += 8; |
| w -= 8; |
| } while (w > 0); |
| } |
| #else |
| // Start of horizontal filtering. |
| process_row_for_horz_filtering(dst_ptr, filter_x_tmp, src_ptr, src_stride, |
| MAX_SB_SIZE, conv_params->round_0, w, height, |
| bd); |
| |
| // Start of vertical filtering. |
| { |
| int16_t *src_tmp_ptr, *s; |
| uint8_t *dst_tmp_ptr; |
| src_tmp_ptr = (int16_t *)temp; |
| dst_tmp_ptr = dst; |
| src_stride = MAX_SB_SIZE; |
| |
| do { |
| uint8x8_t t0; |
| int16x8_t s0, s1, s2, s3, s4, s5, s6, s7; |
| s = src_tmp_ptr; |
| s0 = vld1q_s16(s); |
| s += src_stride; |
| s1 = vld1q_s16(s); |
| s += src_stride; |
| s2 = vld1q_s16(s); |
| s += src_stride; |
| s3 = vld1q_s16(s); |
| s += src_stride; |
| s4 = vld1q_s16(s); |
| s += src_stride; |
| s5 = vld1q_s16(s); |
| s += src_stride; |
| s6 = vld1q_s16(s); |
| s += src_stride; |
| d = dst_tmp_ptr; |
| height = h; |
| PROCESS_ROW_FOR_VERTICAL_FILTER |
| |
| src_tmp_ptr += 8; |
| dst_tmp_ptr += 8; |
| |
| w -= 8; |
| } while (w > 0); |
| } |
| #endif |
| } |