• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2017 The WebM project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include <arm_neon.h>
12 #include <assert.h>
13 #include <string.h>
14 
15 #include "./vpx_config.h"
16 #include "./vpx_dsp_rtcd.h"
17 #include "vpx/vpx_integer.h"
18 #include "vpx_dsp/arm/mem_neon.h"
19 #include "vpx_dsp/arm/transpose_neon.h"
20 #include "vpx_dsp/arm/vpx_convolve8_neon.h"
21 #include "vpx_ports/mem.h"
22 
scaledconvolve_horiz_w4(const uint8_t * src,const ptrdiff_t src_stride,uint8_t * dst,const ptrdiff_t dst_stride,const InterpKernel * const x_filters,const int x0_q4,const int x_step_q4,const int w,const int h)23 static INLINE void scaledconvolve_horiz_w4(
24     const uint8_t *src, const ptrdiff_t src_stride, uint8_t *dst,
25     const ptrdiff_t dst_stride, const InterpKernel *const x_filters,
26     const int x0_q4, const int x_step_q4, const int w, const int h) {
27   DECLARE_ALIGNED(16, uint8_t, temp[4 * 4]);
28   int x, y, z;
29 
30   src -= SUBPEL_TAPS / 2 - 1;
31 
32   y = h;
33   do {
34     int x_q4 = x0_q4;
35     x = 0;
36     do {
37       // process 4 src_x steps
38       for (z = 0; z < 4; ++z) {
39         const uint8_t *const src_x = &src[x_q4 >> SUBPEL_BITS];
40         if (x_q4 & SUBPEL_MASK) {
41           const int16x8_t filters = vld1q_s16(x_filters[x_q4 & SUBPEL_MASK]);
42           uint8x8_t s[8], d;
43           int16x8_t ss[4];
44           int16x4_t t[8], tt;
45 
46           load_u8_8x4(src_x, src_stride, &s[0], &s[1], &s[2], &s[3]);
47           transpose_u8_8x4(&s[0], &s[1], &s[2], &s[3]);
48 
49           ss[0] = vreinterpretq_s16_u16(vmovl_u8(s[0]));
50           ss[1] = vreinterpretq_s16_u16(vmovl_u8(s[1]));
51           ss[2] = vreinterpretq_s16_u16(vmovl_u8(s[2]));
52           ss[3] = vreinterpretq_s16_u16(vmovl_u8(s[3]));
53           t[0] = vget_low_s16(ss[0]);
54           t[1] = vget_low_s16(ss[1]);
55           t[2] = vget_low_s16(ss[2]);
56           t[3] = vget_low_s16(ss[3]);
57           t[4] = vget_high_s16(ss[0]);
58           t[5] = vget_high_s16(ss[1]);
59           t[6] = vget_high_s16(ss[2]);
60           t[7] = vget_high_s16(ss[3]);
61 
62           tt = convolve8_4(t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7],
63                            filters);
64           d = vqrshrun_n_s16(vcombine_s16(tt, tt), 7);
65           vst1_lane_u32((uint32_t *)&temp[4 * z], vreinterpret_u32_u8(d), 0);
66         } else {
67           int i;
68           for (i = 0; i < 4; ++i) {
69             temp[z * 4 + i] = src_x[i * src_stride + 3];
70           }
71         }
72         x_q4 += x_step_q4;
73       }
74 
75       // transpose the 4x4 filters values back to dst
76       {
77         const uint8x8x4_t d4 = vld4_u8(temp);
78         vst1_lane_u32((uint32_t *)&dst[x + 0 * dst_stride],
79                       vreinterpret_u32_u8(d4.val[0]), 0);
80         vst1_lane_u32((uint32_t *)&dst[x + 1 * dst_stride],
81                       vreinterpret_u32_u8(d4.val[1]), 0);
82         vst1_lane_u32((uint32_t *)&dst[x + 2 * dst_stride],
83                       vreinterpret_u32_u8(d4.val[2]), 0);
84         vst1_lane_u32((uint32_t *)&dst[x + 3 * dst_stride],
85                       vreinterpret_u32_u8(d4.val[3]), 0);
86       }
87       x += 4;
88     } while (x < w);
89 
90     src += src_stride * 4;
91     dst += dst_stride * 4;
92     y -= 4;
93   } while (y > 0);
94 }
95 
scaledconvolve_horiz_w8(const uint8_t * src,const ptrdiff_t src_stride,uint8_t * dst,const ptrdiff_t dst_stride,const InterpKernel * const x_filters,const int x0_q4,const int x_step_q4,const int w,const int h)96 static INLINE void scaledconvolve_horiz_w8(
97     const uint8_t *src, const ptrdiff_t src_stride, uint8_t *dst,
98     const ptrdiff_t dst_stride, const InterpKernel *const x_filters,
99     const int x0_q4, const int x_step_q4, const int w, const int h) {
100   DECLARE_ALIGNED(16, uint8_t, temp[8 * 8]);
101   int x, y, z;
102   src -= SUBPEL_TAPS / 2 - 1;
103 
104   // This function processes 8x8 areas. The intermediate height is not always
105   // a multiple of 8, so force it to be a multiple of 8 here.
106   y = (h + 7) & ~7;
107 
108   do {
109     int x_q4 = x0_q4;
110     x = 0;
111     do {
112       uint8x8_t d[8];
113       // process 8 src_x steps
114       for (z = 0; z < 8; ++z) {
115         const uint8_t *const src_x = &src[x_q4 >> SUBPEL_BITS];
116 
117         if (x_q4 & SUBPEL_MASK) {
118           const int16x8_t filters = vld1q_s16(x_filters[x_q4 & SUBPEL_MASK]);
119           uint8x8_t s[8];
120           load_u8_8x8(src_x, src_stride, &s[0], &s[1], &s[2], &s[3], &s[4],
121                       &s[5], &s[6], &s[7]);
122           transpose_u8_8x8(&s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6],
123                            &s[7]);
124           d[0] = scale_filter_8(s, filters);
125           vst1_u8(&temp[8 * z], d[0]);
126         } else {
127           int i;
128           for (i = 0; i < 8; ++i) {
129             temp[z * 8 + i] = src_x[i * src_stride + 3];
130           }
131         }
132         x_q4 += x_step_q4;
133       }
134 
135       // transpose the 8x8 filters values back to dst
136       load_u8_8x8(temp, 8, &d[0], &d[1], &d[2], &d[3], &d[4], &d[5], &d[6],
137                   &d[7]);
138       transpose_u8_8x8(&d[0], &d[1], &d[2], &d[3], &d[4], &d[5], &d[6], &d[7]);
139       vst1_u8(&dst[x + 0 * dst_stride], d[0]);
140       vst1_u8(&dst[x + 1 * dst_stride], d[1]);
141       vst1_u8(&dst[x + 2 * dst_stride], d[2]);
142       vst1_u8(&dst[x + 3 * dst_stride], d[3]);
143       vst1_u8(&dst[x + 4 * dst_stride], d[4]);
144       vst1_u8(&dst[x + 5 * dst_stride], d[5]);
145       vst1_u8(&dst[x + 6 * dst_stride], d[6]);
146       vst1_u8(&dst[x + 7 * dst_stride], d[7]);
147       x += 8;
148     } while (x < w);
149 
150     src += src_stride * 8;
151     dst += dst_stride * 8;
152   } while (y -= 8);
153 }
154 
scaledconvolve_vert_w4(const uint8_t * src,const ptrdiff_t src_stride,uint8_t * dst,const ptrdiff_t dst_stride,const InterpKernel * const y_filters,const int y0_q4,const int y_step_q4,const int w,const int h)155 static INLINE void scaledconvolve_vert_w4(
156     const uint8_t *src, const ptrdiff_t src_stride, uint8_t *dst,
157     const ptrdiff_t dst_stride, const InterpKernel *const y_filters,
158     const int y0_q4, const int y_step_q4, const int w, const int h) {
159   int y;
160   int y_q4 = y0_q4;
161 
162   src -= src_stride * (SUBPEL_TAPS / 2 - 1);
163   y = h;
164   do {
165     const unsigned char *src_y = &src[(y_q4 >> SUBPEL_BITS) * src_stride];
166 
167     if (y_q4 & SUBPEL_MASK) {
168       const int16x8_t filters = vld1q_s16(y_filters[y_q4 & SUBPEL_MASK]);
169       uint8x8_t s[8], d;
170       int16x4_t t[8], tt;
171 
172       load_u8_8x8(src_y, src_stride, &s[0], &s[1], &s[2], &s[3], &s[4], &s[5],
173                   &s[6], &s[7]);
174       t[0] = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(s[0])));
175       t[1] = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(s[1])));
176       t[2] = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(s[2])));
177       t[3] = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(s[3])));
178       t[4] = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(s[4])));
179       t[5] = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(s[5])));
180       t[6] = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(s[6])));
181       t[7] = vget_low_s16(vreinterpretq_s16_u16(vmovl_u8(s[7])));
182 
183       tt = convolve8_4(t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7], filters);
184       d = vqrshrun_n_s16(vcombine_s16(tt, tt), 7);
185       vst1_lane_u32((uint32_t *)dst, vreinterpret_u32_u8(d), 0);
186     } else {
187       memcpy(dst, &src_y[3 * src_stride], w);
188     }
189 
190     dst += dst_stride;
191     y_q4 += y_step_q4;
192   } while (--y);
193 }
194 
scaledconvolve_vert_w8(const uint8_t * src,const ptrdiff_t src_stride,uint8_t * dst,const ptrdiff_t dst_stride,const InterpKernel * const y_filters,const int y0_q4,const int y_step_q4,const int w,const int h)195 static INLINE void scaledconvolve_vert_w8(
196     const uint8_t *src, const ptrdiff_t src_stride, uint8_t *dst,
197     const ptrdiff_t dst_stride, const InterpKernel *const y_filters,
198     const int y0_q4, const int y_step_q4, const int w, const int h) {
199   int y;
200   int y_q4 = y0_q4;
201 
202   src -= src_stride * (SUBPEL_TAPS / 2 - 1);
203   y = h;
204   do {
205     const unsigned char *src_y = &src[(y_q4 >> SUBPEL_BITS) * src_stride];
206     if (y_q4 & SUBPEL_MASK) {
207       const int16x8_t filters = vld1q_s16(y_filters[y_q4 & SUBPEL_MASK]);
208       uint8x8_t s[8], d;
209       load_u8_8x8(src_y, src_stride, &s[0], &s[1], &s[2], &s[3], &s[4], &s[5],
210                   &s[6], &s[7]);
211       d = scale_filter_8(s, filters);
212       vst1_u8(dst, d);
213     } else {
214       memcpy(dst, &src_y[3 * src_stride], w);
215     }
216     dst += dst_stride;
217     y_q4 += y_step_q4;
218   } while (--y);
219 }
220 
scaledconvolve_vert_w16(const uint8_t * src,const ptrdiff_t src_stride,uint8_t * dst,const ptrdiff_t dst_stride,const InterpKernel * const y_filters,const int y0_q4,const int y_step_q4,const int w,const int h)221 static INLINE void scaledconvolve_vert_w16(
222     const uint8_t *src, const ptrdiff_t src_stride, uint8_t *dst,
223     const ptrdiff_t dst_stride, const InterpKernel *const y_filters,
224     const int y0_q4, const int y_step_q4, const int w, const int h) {
225   int x, y;
226   int y_q4 = y0_q4;
227 
228   src -= src_stride * (SUBPEL_TAPS / 2 - 1);
229   y = h;
230   do {
231     const unsigned char *src_y = &src[(y_q4 >> SUBPEL_BITS) * src_stride];
232     if (y_q4 & SUBPEL_MASK) {
233       x = 0;
234       do {
235         const int16x8_t filters = vld1q_s16(y_filters[y_q4 & SUBPEL_MASK]);
236         uint8x16_t ss[8];
237         uint8x8_t s[8], d[2];
238         load_u8_16x8(src_y, src_stride, &ss[0], &ss[1], &ss[2], &ss[3], &ss[4],
239                      &ss[5], &ss[6], &ss[7]);
240         s[0] = vget_low_u8(ss[0]);
241         s[1] = vget_low_u8(ss[1]);
242         s[2] = vget_low_u8(ss[2]);
243         s[3] = vget_low_u8(ss[3]);
244         s[4] = vget_low_u8(ss[4]);
245         s[5] = vget_low_u8(ss[5]);
246         s[6] = vget_low_u8(ss[6]);
247         s[7] = vget_low_u8(ss[7]);
248         d[0] = scale_filter_8(s, filters);
249 
250         s[0] = vget_high_u8(ss[0]);
251         s[1] = vget_high_u8(ss[1]);
252         s[2] = vget_high_u8(ss[2]);
253         s[3] = vget_high_u8(ss[3]);
254         s[4] = vget_high_u8(ss[4]);
255         s[5] = vget_high_u8(ss[5]);
256         s[6] = vget_high_u8(ss[6]);
257         s[7] = vget_high_u8(ss[7]);
258         d[1] = scale_filter_8(s, filters);
259         vst1q_u8(&dst[x], vcombine_u8(d[0], d[1]));
260         src_y += 16;
261         x += 16;
262       } while (x < w);
263     } else {
264       memcpy(dst, &src_y[3 * src_stride], w);
265     }
266     dst += dst_stride;
267     y_q4 += y_step_q4;
268   } while (--y);
269 }
270 
vpx_scaled_2d_neon(const uint8_t * src,ptrdiff_t src_stride,uint8_t * dst,ptrdiff_t dst_stride,const InterpKernel * filter,int x0_q4,int x_step_q4,int y0_q4,int y_step_q4,int w,int h)271 void vpx_scaled_2d_neon(const uint8_t *src, ptrdiff_t src_stride, uint8_t *dst,
272                         ptrdiff_t dst_stride, const InterpKernel *filter,
273                         int x0_q4, int x_step_q4, int y0_q4, int y_step_q4,
274                         int w, int h) {
275   // Note: Fixed size intermediate buffer, temp, places limits on parameters.
276   // 2d filtering proceeds in 2 steps:
277   //   (1) Interpolate horizontally into an intermediate buffer, temp.
278   //   (2) Interpolate temp vertically to derive the sub-pixel result.
279   // Deriving the maximum number of rows in the temp buffer (135):
280   // --Smallest scaling factor is x1/2 ==> y_step_q4 = 32 (Normative).
281   // --Largest block size is 64x64 pixels.
282   // --64 rows in the downscaled frame span a distance of (64 - 1) * 32 in the
283   //   original frame (in 1/16th pixel units).
284   // --Must round-up because block may be located at sub-pixel position.
285   // --Require an additional SUBPEL_TAPS rows for the 8-tap filter tails.
286   // --((64 - 1) * 32 + 15) >> 4 + 8 = 135.
287   // --Require an additional 8 rows for the horiz_w8 transpose tail.
288   // When calling in frame scaling function, the smallest scaling factor is x1/4
289   // ==> y_step_q4 = 64. Since w and h are at most 16, the temp buffer is still
290   // big enough.
291   DECLARE_ALIGNED(16, uint8_t, temp[(135 + 8) * 64]);
292   const int intermediate_height =
293       (((h - 1) * y_step_q4 + y0_q4) >> SUBPEL_BITS) + SUBPEL_TAPS;
294 
295   assert(w <= 64);
296   assert(h <= 64);
297   assert(y_step_q4 <= 32 || (y_step_q4 <= 64 && h <= 32));
298   assert(x_step_q4 <= 64);
299 
300   if (w >= 8) {
301     scaledconvolve_horiz_w8(src - src_stride * (SUBPEL_TAPS / 2 - 1),
302                             src_stride, temp, 64, filter, x0_q4, x_step_q4, w,
303                             intermediate_height);
304   } else {
305     scaledconvolve_horiz_w4(src - src_stride * (SUBPEL_TAPS / 2 - 1),
306                             src_stride, temp, 64, filter, x0_q4, x_step_q4, w,
307                             intermediate_height);
308   }
309 
310   if (w >= 16) {
311     scaledconvolve_vert_w16(temp + 64 * (SUBPEL_TAPS / 2 - 1), 64, dst,
312                             dst_stride, filter, y0_q4, y_step_q4, w, h);
313   } else if (w == 8) {
314     scaledconvolve_vert_w8(temp + 64 * (SUBPEL_TAPS / 2 - 1), 64, dst,
315                            dst_stride, filter, y0_q4, y_step_q4, w, h);
316   } else {
317     scaledconvolve_vert_w4(temp + 64 * (SUBPEL_TAPS / 2 - 1), 64, dst,
318                            dst_stride, filter, y0_q4, y_step_q4, w, h);
319   }
320 }
321