xref: /aosp_15_r20/external/libvpx/vpx_dsp/x86/vpx_subpixel_8t_intrin_avx2.c (revision fb1b10ab9aebc7c7068eedab379b749d7e3900be)
1 /*
2  *  Copyright (c) 2010 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 <immintrin.h>
12 #include <stdio.h>
13 
14 #include "./vpx_dsp_rtcd.h"
15 #include "vpx_dsp/x86/convolve.h"
16 #include "vpx_dsp/x86/convolve_avx2.h"
17 #include "vpx_dsp/x86/convolve_sse2.h"
18 #include "vpx_dsp/x86/convolve_ssse3.h"
19 #include "vpx_ports/mem.h"
20 
21 // filters for 16_h8
22 DECLARE_ALIGNED(32, static const uint8_t,
23                 filt1_global_avx2[32]) = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5,
24                                            6, 6, 7, 7, 8, 0, 1, 1, 2, 2, 3,
25                                            3, 4, 4, 5, 5, 6, 6, 7, 7, 8 };
26 
27 DECLARE_ALIGNED(32, static const uint8_t,
28                 filt2_global_avx2[32]) = { 2, 3, 3, 4, 4,  5, 5, 6, 6, 7, 7,
29                                            8, 8, 9, 9, 10, 2, 3, 3, 4, 4, 5,
30                                            5, 6, 6, 7, 7,  8, 8, 9, 9, 10 };
31 
32 DECLARE_ALIGNED(32, static const uint8_t, filt3_global_avx2[32]) = {
33   4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12,
34   4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12
35 };
36 
37 DECLARE_ALIGNED(32, static const uint8_t, filt4_global_avx2[32]) = {
38   6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14,
39   6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14
40 };
41 
42 DECLARE_ALIGNED(32, static const uint8_t, filt_d4_global_avx2[64]) = {
43   0, 1, 2, 3,  1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, 0, 1, 2, 3,  1, 2,
44   3, 4, 2, 3,  4, 5, 3, 4, 5, 6, 4, 5, 6, 7, 5, 6, 7, 8, 6, 7,  8, 9,
45   7, 8, 9, 10, 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, 8, 9, 7, 8, 9, 10,
46 };
47 
48 #define CALC_CONVOLVE8_HORZ_ROW                                               \
49   srcReg = mm256_loadu2_si128(src_ptr - 3, src_ptr - 3 + src_pitch);          \
50   s1[0] = _mm256_shuffle_epi8(srcReg, filt[0]);                               \
51   s1[1] = _mm256_shuffle_epi8(srcReg, filt[1]);                               \
52   s1[2] = _mm256_shuffle_epi8(srcReg, filt[2]);                               \
53   s1[3] = _mm256_shuffle_epi8(srcReg, filt[3]);                               \
54   s1[0] = convolve8_16_avx2(s1, f1);                                          \
55   s1[0] = _mm256_packus_epi16(s1[0], s1[0]);                                  \
56   src_ptr += src_stride;                                                      \
57   _mm_storel_epi64((__m128i *)&output_ptr[0], _mm256_castsi256_si128(s1[0])); \
58   output_ptr += output_pitch;                                                 \
59   _mm_storel_epi64((__m128i *)&output_ptr[0],                                 \
60                    _mm256_extractf128_si256(s1[0], 1));                       \
61   output_ptr += output_pitch;
62 
vpx_filter_block1d16_h8_x_avx2(const uint8_t * src_ptr,ptrdiff_t src_pixels_per_line,uint8_t * output_ptr,ptrdiff_t output_pitch,uint32_t output_height,const int16_t * filter,const int avg)63 static INLINE void vpx_filter_block1d16_h8_x_avx2(
64     const uint8_t *src_ptr, ptrdiff_t src_pixels_per_line, uint8_t *output_ptr,
65     ptrdiff_t output_pitch, uint32_t output_height, const int16_t *filter,
66     const int avg) {
67   __m128i outReg1, outReg2;
68   __m256i outReg32b1, outReg32b2;
69   unsigned int i;
70   ptrdiff_t src_stride, dst_stride;
71   __m256i f[4], filt[4], s[4];
72 
73   shuffle_filter_avx2(filter, f);
74   filt[0] = _mm256_load_si256((__m256i const *)filt1_global_avx2);
75   filt[1] = _mm256_load_si256((__m256i const *)filt2_global_avx2);
76   filt[2] = _mm256_load_si256((__m256i const *)filt3_global_avx2);
77   filt[3] = _mm256_load_si256((__m256i const *)filt4_global_avx2);
78 
79   // multiple the size of the source and destination stride by two
80   src_stride = src_pixels_per_line << 1;
81   dst_stride = output_pitch << 1;
82   for (i = output_height; i > 1; i -= 2) {
83     __m256i srcReg;
84 
85     // load the 2 strides of source
86     srcReg = mm256_loadu2_si128(src_ptr - 3, src_ptr + src_pixels_per_line - 3);
87 
88     // filter the source buffer
89     s[0] = _mm256_shuffle_epi8(srcReg, filt[0]);
90     s[1] = _mm256_shuffle_epi8(srcReg, filt[1]);
91     s[2] = _mm256_shuffle_epi8(srcReg, filt[2]);
92     s[3] = _mm256_shuffle_epi8(srcReg, filt[3]);
93     outReg32b1 = convolve8_16_avx2(s, f);
94 
95     // reading 2 strides of the next 16 bytes
96     // (part of it was being read by earlier read)
97     srcReg = mm256_loadu2_si128(src_ptr + 5, src_ptr + src_pixels_per_line + 5);
98 
99     // filter the source buffer
100     s[0] = _mm256_shuffle_epi8(srcReg, filt[0]);
101     s[1] = _mm256_shuffle_epi8(srcReg, filt[1]);
102     s[2] = _mm256_shuffle_epi8(srcReg, filt[2]);
103     s[3] = _mm256_shuffle_epi8(srcReg, filt[3]);
104     outReg32b2 = convolve8_16_avx2(s, f);
105 
106     // shrink to 8 bit each 16 bits, the low and high 64-bits of each lane
107     // contain the first and second convolve result respectively
108     outReg32b1 = _mm256_packus_epi16(outReg32b1, outReg32b2);
109 
110     src_ptr += src_stride;
111 
112     if (avg) {
113       const __m256i outReg = mm256_loadu2_si128(
114           (__m128i *)output_ptr, (__m128i *)(output_ptr + output_pitch));
115       outReg32b1 = _mm256_avg_epu8(outReg32b1, outReg);
116     }
117     mm256_store2_si128((__m128i *)output_ptr,
118                        (__m128i *)(output_ptr + output_pitch), &outReg32b1);
119     output_ptr += dst_stride;
120   }
121 
122   // if the number of strides is odd.
123   // process only 16 bytes
124   if (i > 0) {
125     const __m128i srcReg1 = _mm_loadu_si128((const __m128i *)(src_ptr - 3));
126     const __m128i srcReg2 = _mm_loadu_si128((const __m128i *)(src_ptr + 5));
127     const __m256i srcReg =
128         _mm256_inserti128_si256(_mm256_castsi128_si256(srcReg1), srcReg2, 1);
129 
130     // filter the source buffer
131     s[0] = _mm256_shuffle_epi8(srcReg, filt[0]);
132     s[1] = _mm256_shuffle_epi8(srcReg, filt[1]);
133     s[2] = _mm256_shuffle_epi8(srcReg, filt[2]);
134     s[3] = _mm256_shuffle_epi8(srcReg, filt[3]);
135 
136     // The low and high 128-bits of each lane contain the first and second
137     // convolve result respectively
138     outReg32b1 = convolve8_16_avx2(s, f);
139     outReg1 = _mm256_castsi256_si128(outReg32b1);
140     outReg2 = _mm256_extractf128_si256(outReg32b1, 1);
141 
142     // shrink to 8 bit each 16 bits
143     outReg1 = _mm_packus_epi16(outReg1, outReg2);
144 
145     // average if necessary
146     if (avg) {
147       outReg1 = _mm_avg_epu8(outReg1, _mm_load_si128((__m128i *)output_ptr));
148     }
149 
150     // save 16 bytes
151     _mm_store_si128((__m128i *)output_ptr, outReg1);
152   }
153 }
154 
vpx_filter_block1d16_h8_avx2(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * output_ptr,ptrdiff_t dst_stride,uint32_t output_height,const int16_t * filter)155 static void vpx_filter_block1d16_h8_avx2(
156     const uint8_t *src_ptr, ptrdiff_t src_stride, uint8_t *output_ptr,
157     ptrdiff_t dst_stride, uint32_t output_height, const int16_t *filter) {
158   vpx_filter_block1d16_h8_x_avx2(src_ptr, src_stride, output_ptr, dst_stride,
159                                  output_height, filter, 0);
160 }
161 
vpx_filter_block1d16_h8_avg_avx2(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * output_ptr,ptrdiff_t dst_stride,uint32_t output_height,const int16_t * filter)162 static void vpx_filter_block1d16_h8_avg_avx2(
163     const uint8_t *src_ptr, ptrdiff_t src_stride, uint8_t *output_ptr,
164     ptrdiff_t dst_stride, uint32_t output_height, const int16_t *filter) {
165   vpx_filter_block1d16_h8_x_avx2(src_ptr, src_stride, output_ptr, dst_stride,
166                                  output_height, filter, 1);
167 }
168 
vpx_filter_block1d8_h8_avx2(const uint8_t * src_ptr,ptrdiff_t src_pitch,uint8_t * output_ptr,ptrdiff_t output_pitch,uint32_t output_height,const int16_t * filter)169 static void vpx_filter_block1d8_h8_avx2(
170     const uint8_t *src_ptr, ptrdiff_t src_pitch, uint8_t *output_ptr,
171     ptrdiff_t output_pitch, uint32_t output_height, const int16_t *filter) {
172   __m256i filt[4], f1[4], s1[4], srcReg;
173   __m128i f[4], s[4];
174   int y = output_height;
175 
176   // Multiply the size of the source stride by two
177   const ptrdiff_t src_stride = src_pitch << 1;
178 
179   shuffle_filter_avx2(filter, f1);
180   filt[0] = _mm256_load_si256((__m256i const *)filt1_global_avx2);
181   filt[1] = _mm256_load_si256((__m256i const *)filt2_global_avx2);
182   filt[2] = _mm256_load_si256((__m256i const *)filt3_global_avx2);
183   filt[3] = _mm256_load_si256((__m256i const *)filt4_global_avx2);
184 
185   // Process next 4 rows
186   while (y > 3) {
187     CALC_CONVOLVE8_HORZ_ROW
188     CALC_CONVOLVE8_HORZ_ROW
189     y -= 4;
190   }
191 
192   // If remaining, then process 2 rows at a time
193   while (y > 1) {
194     CALC_CONVOLVE8_HORZ_ROW
195     y -= 2;
196   }
197 
198   // For the remaining height.
199   if (y > 0) {
200     const __m128i src_reg_128 = _mm_loadu_si128((const __m128i *)(src_ptr - 3));
201 
202     f[0] = _mm256_castsi256_si128(f1[0]);
203     f[1] = _mm256_castsi256_si128(f1[1]);
204     f[2] = _mm256_castsi256_si128(f1[2]);
205     f[3] = _mm256_castsi256_si128(f1[3]);
206 
207     // filter the source buffer
208     s[0] = _mm_shuffle_epi8(src_reg_128, _mm256_castsi256_si128(filt[0]));
209     s[1] = _mm_shuffle_epi8(src_reg_128, _mm256_castsi256_si128(filt[1]));
210     s[2] = _mm_shuffle_epi8(src_reg_128, _mm256_castsi256_si128(filt[2]));
211     s[3] = _mm_shuffle_epi8(src_reg_128, _mm256_castsi256_si128(filt[3]));
212     s[0] = convolve8_8_ssse3(s, f);
213 
214     // Saturate 16bit value to 8bit.
215     s[0] = _mm_packus_epi16(s[0], s[0]);
216 
217     // Save only 8 bytes
218     _mm_storel_epi64((__m128i *)&output_ptr[0], s[0]);
219   }
220 }
221 
vpx_filter_block1d16_v8_x_avx2(const uint8_t * src_ptr,ptrdiff_t src_pitch,uint8_t * output_ptr,ptrdiff_t out_pitch,uint32_t output_height,const int16_t * filter,const int avg)222 static INLINE void vpx_filter_block1d16_v8_x_avx2(
223     const uint8_t *src_ptr, ptrdiff_t src_pitch, uint8_t *output_ptr,
224     ptrdiff_t out_pitch, uint32_t output_height, const int16_t *filter,
225     const int avg) {
226   __m256i srcRegHead1;
227   unsigned int i;
228   ptrdiff_t src_stride, dst_stride;
229   __m256i f[4], s1[4], s2[4];
230 
231   shuffle_filter_avx2(filter, f);
232 
233   // multiple the size of the source and destination stride by two
234   src_stride = src_pitch << 1;
235   dst_stride = out_pitch << 1;
236 
237   {
238     __m128i s[6];
239     __m256i s32b[6];
240 
241     // load 16 bytes 7 times in stride of src_pitch
242     s[0] = _mm_loadu_si128((const __m128i *)(src_ptr + 0 * src_pitch));
243     s[1] = _mm_loadu_si128((const __m128i *)(src_ptr + 1 * src_pitch));
244     s[2] = _mm_loadu_si128((const __m128i *)(src_ptr + 2 * src_pitch));
245     s[3] = _mm_loadu_si128((const __m128i *)(src_ptr + 3 * src_pitch));
246     s[4] = _mm_loadu_si128((const __m128i *)(src_ptr + 4 * src_pitch));
247     s[5] = _mm_loadu_si128((const __m128i *)(src_ptr + 5 * src_pitch));
248     srcRegHead1 = _mm256_castsi128_si256(
249         _mm_loadu_si128((const __m128i *)(src_ptr + 6 * src_pitch)));
250 
251     // have each consecutive loads on the same 256 register
252     s32b[0] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[0]), s[1], 1);
253     s32b[1] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[1]), s[2], 1);
254     s32b[2] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[2]), s[3], 1);
255     s32b[3] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[3]), s[4], 1);
256     s32b[4] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[4]), s[5], 1);
257     s32b[5] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[5]),
258                                       _mm256_castsi256_si128(srcRegHead1), 1);
259 
260     // merge every two consecutive registers except the last one
261     // the first lanes contain values for filtering odd rows (1,3,5...) and
262     // the second lanes contain values for filtering even rows (2,4,6...)
263     s1[0] = _mm256_unpacklo_epi8(s32b[0], s32b[1]);
264     s2[0] = _mm256_unpackhi_epi8(s32b[0], s32b[1]);
265     s1[1] = _mm256_unpacklo_epi8(s32b[2], s32b[3]);
266     s2[1] = _mm256_unpackhi_epi8(s32b[2], s32b[3]);
267     s1[2] = _mm256_unpacklo_epi8(s32b[4], s32b[5]);
268     s2[2] = _mm256_unpackhi_epi8(s32b[4], s32b[5]);
269   }
270 
271   // The output_height is always a multiple of two.
272   assert(!(output_height & 1));
273 
274   for (i = output_height; i > 1; i -= 2) {
275     __m256i srcRegHead2, srcRegHead3;
276 
277     // load the next 2 loads of 16 bytes and have every two
278     // consecutive loads in the same 256 bit register
279     srcRegHead2 = _mm256_castsi128_si256(
280         _mm_loadu_si128((const __m128i *)(src_ptr + 7 * src_pitch)));
281     srcRegHead1 = _mm256_inserti128_si256(
282         srcRegHead1, _mm256_castsi256_si128(srcRegHead2), 1);
283     srcRegHead3 = _mm256_castsi128_si256(
284         _mm_loadu_si128((const __m128i *)(src_ptr + 8 * src_pitch)));
285     srcRegHead2 = _mm256_inserti128_si256(
286         srcRegHead2, _mm256_castsi256_si128(srcRegHead3), 1);
287 
288     // merge the two new consecutive registers
289     // the first lane contain values for filtering odd rows (1,3,5...) and
290     // the second lane contain values for filtering even rows (2,4,6...)
291     s1[3] = _mm256_unpacklo_epi8(srcRegHead1, srcRegHead2);
292     s2[3] = _mm256_unpackhi_epi8(srcRegHead1, srcRegHead2);
293 
294     s1[0] = convolve8_16_avx2(s1, f);
295     s2[0] = convolve8_16_avx2(s2, f);
296 
297     // shrink to 8 bit each 16 bits, the low and high 64-bits of each lane
298     // contain the first and second convolve result respectively
299     s1[0] = _mm256_packus_epi16(s1[0], s2[0]);
300 
301     src_ptr += src_stride;
302 
303     // average if necessary
304     if (avg) {
305       const __m256i outReg = mm256_loadu2_si128(
306           (__m128i *)output_ptr, (__m128i *)(output_ptr + out_pitch));
307       s1[0] = _mm256_avg_epu8(s1[0], outReg);
308     }
309 
310     mm256_store2_si128((__m128i *)output_ptr,
311                        (__m128i *)(output_ptr + out_pitch), s1);
312 
313     output_ptr += dst_stride;
314 
315     // shift down by two rows
316     s1[0] = s1[1];
317     s2[0] = s2[1];
318     s1[1] = s1[2];
319     s2[1] = s2[2];
320     s1[2] = s1[3];
321     s2[2] = s2[3];
322     srcRegHead1 = srcRegHead3;
323   }
324 }
325 
vpx_filter_block1d16_v8_avx2(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,ptrdiff_t dst_stride,uint32_t height,const int16_t * filter)326 static void vpx_filter_block1d16_v8_avx2(const uint8_t *src_ptr,
327                                          ptrdiff_t src_stride, uint8_t *dst_ptr,
328                                          ptrdiff_t dst_stride, uint32_t height,
329                                          const int16_t *filter) {
330   vpx_filter_block1d16_v8_x_avx2(src_ptr, src_stride, dst_ptr, dst_stride,
331                                  height, filter, 0);
332 }
333 
vpx_filter_block1d16_v8_avg_avx2(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,ptrdiff_t dst_stride,uint32_t height,const int16_t * filter)334 static void vpx_filter_block1d16_v8_avg_avx2(
335     const uint8_t *src_ptr, ptrdiff_t src_stride, uint8_t *dst_ptr,
336     ptrdiff_t dst_stride, uint32_t height, const int16_t *filter) {
337   vpx_filter_block1d16_v8_x_avx2(src_ptr, src_stride, dst_ptr, dst_stride,
338                                  height, filter, 1);
339 }
340 
vpx_filter_block1d16_h4_avx2(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,ptrdiff_t dst_stride,uint32_t height,const int16_t * kernel)341 static void vpx_filter_block1d16_h4_avx2(const uint8_t *src_ptr,
342                                          ptrdiff_t src_stride, uint8_t *dst_ptr,
343                                          ptrdiff_t dst_stride, uint32_t height,
344                                          const int16_t *kernel) {
345   // We will cast the kernel from 16-bit words to 8-bit words, and then extract
346   // the middle four elements of the kernel into two registers in the form
347   // ... k[3] k[2] k[3] k[2]
348   // ... k[5] k[4] k[5] k[4]
349   // Then we shuffle the source into
350   // ... s[1] s[0] s[0] s[-1]
351   // ... s[3] s[2] s[2] s[1]
352   // Calling multiply and add gives us half of the sum. Calling add gives us
353   // first half of the output. Repeat again to get the second half of the
354   // output. Finally we shuffle again to combine the two outputs.
355   // Since avx2 allows us to use 256-bit buffer, we can do this two rows at a
356   // time.
357 
358   __m128i kernel_reg;  // Kernel
359   __m256i kernel_reg_256, kernel_reg_23,
360       kernel_reg_45;                             // Segments of the kernel used
361   const __m256i reg_32 = _mm256_set1_epi16(32);  // Used for rounding
362   const ptrdiff_t unrolled_src_stride = src_stride << 1;
363   const ptrdiff_t unrolled_dst_stride = dst_stride << 1;
364   int h;
365 
366   __m256i src_reg, src_reg_shift_0, src_reg_shift_2;
367   __m256i dst_first, dst_second;
368   __m256i tmp_0, tmp_1;
369   __m256i idx_shift_0 =
370       _mm256_setr_epi8(0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 0, 1, 1,
371                        2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8);
372   __m256i idx_shift_2 =
373       _mm256_setr_epi8(2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 2, 3, 3,
374                        4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10);
375 
376   // Start one pixel before as we need tap/2 - 1 = 1 sample from the past
377   src_ptr -= 1;
378 
379   // Load Kernel
380   kernel_reg = _mm_loadu_si128((const __m128i *)kernel);
381   kernel_reg = _mm_srai_epi16(kernel_reg, 1);
382   kernel_reg = _mm_packs_epi16(kernel_reg, kernel_reg);
383   kernel_reg_256 = _mm256_broadcastsi128_si256(kernel_reg);
384   kernel_reg_23 =
385       _mm256_shuffle_epi8(kernel_reg_256, _mm256_set1_epi16(0x0302u));
386   kernel_reg_45 =
387       _mm256_shuffle_epi8(kernel_reg_256, _mm256_set1_epi16(0x0504u));
388 
389   for (h = height; h >= 2; h -= 2) {
390     // Load the source
391     src_reg = mm256_loadu2_si128(src_ptr, src_ptr + src_stride);
392     src_reg_shift_0 = _mm256_shuffle_epi8(src_reg, idx_shift_0);
393     src_reg_shift_2 = _mm256_shuffle_epi8(src_reg, idx_shift_2);
394 
395     // Partial result for first half
396     tmp_0 = _mm256_maddubs_epi16(src_reg_shift_0, kernel_reg_23);
397     tmp_1 = _mm256_maddubs_epi16(src_reg_shift_2, kernel_reg_45);
398     dst_first = _mm256_adds_epi16(tmp_0, tmp_1);
399 
400     // Do again to get the second half of dst
401     // Load the source
402     src_reg = mm256_loadu2_si128(src_ptr + 8, src_ptr + src_stride + 8);
403     src_reg_shift_0 = _mm256_shuffle_epi8(src_reg, idx_shift_0);
404     src_reg_shift_2 = _mm256_shuffle_epi8(src_reg, idx_shift_2);
405 
406     // Partial result for second half
407     tmp_0 = _mm256_maddubs_epi16(src_reg_shift_0, kernel_reg_23);
408     tmp_1 = _mm256_maddubs_epi16(src_reg_shift_2, kernel_reg_45);
409     dst_second = _mm256_adds_epi16(tmp_0, tmp_1);
410 
411     // Round each result
412     dst_first = mm256_round_epi16(&dst_first, &reg_32, 6);
413     dst_second = mm256_round_epi16(&dst_second, &reg_32, 6);
414 
415     // Finally combine to get the final dst
416     dst_first = _mm256_packus_epi16(dst_first, dst_second);
417     mm256_store2_si128((__m128i *)dst_ptr, (__m128i *)(dst_ptr + dst_stride),
418                        &dst_first);
419 
420     src_ptr += unrolled_src_stride;
421     dst_ptr += unrolled_dst_stride;
422   }
423 
424   // Repeat for the last row if needed
425   if (h > 0) {
426     src_reg = _mm256_loadu_si256((const __m256i *)src_ptr);
427     // Reorder into 2 1 1 2
428     src_reg = _mm256_permute4x64_epi64(src_reg, 0x94);
429 
430     src_reg_shift_0 = _mm256_shuffle_epi8(src_reg, idx_shift_0);
431     src_reg_shift_2 = _mm256_shuffle_epi8(src_reg, idx_shift_2);
432 
433     tmp_0 = _mm256_maddubs_epi16(src_reg_shift_0, kernel_reg_23);
434     tmp_1 = _mm256_maddubs_epi16(src_reg_shift_2, kernel_reg_45);
435     dst_first = _mm256_adds_epi16(tmp_0, tmp_1);
436 
437     dst_first = mm256_round_epi16(&dst_first, &reg_32, 6);
438 
439     dst_first = _mm256_packus_epi16(dst_first, dst_first);
440     dst_first = _mm256_permute4x64_epi64(dst_first, 0x8);
441 
442     _mm_store_si128((__m128i *)dst_ptr, _mm256_castsi256_si128(dst_first));
443   }
444 }
445 
vpx_filter_block1d16_v4_avx2(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,ptrdiff_t dst_stride,uint32_t height,const int16_t * kernel)446 static void vpx_filter_block1d16_v4_avx2(const uint8_t *src_ptr,
447                                          ptrdiff_t src_stride, uint8_t *dst_ptr,
448                                          ptrdiff_t dst_stride, uint32_t height,
449                                          const int16_t *kernel) {
450   // We will load two rows of pixels as 8-bit words, rearrange them into the
451   // form
452   // ... s[1,0] s[0,0] s[0,0] s[-1,0]
453   // so that we can call multiply and add with the kernel partial output. Then
454   // we can call add with another row to get the output.
455 
456   // Register for source s[-1:3, :]
457   __m256i src_reg_1, src_reg_2, src_reg_3;
458   // Interleaved rows of the source. lo is first half, hi second
459   __m256i src_reg_m10, src_reg_01, src_reg_12, src_reg_23;
460   __m256i src_reg_m1001_lo, src_reg_m1001_hi, src_reg_1223_lo, src_reg_1223_hi;
461 
462   __m128i kernel_reg;  // Kernel
463   __m256i kernel_reg_256, kernel_reg_23,
464       kernel_reg_45;  // Segments of the kernel used
465 
466   // Result after multiply and add
467   __m256i res_reg_m1001_lo, res_reg_1223_lo, res_reg_m1001_hi, res_reg_1223_hi;
468   __m256i res_reg, res_reg_lo, res_reg_hi;
469 
470   const __m256i reg_32 = _mm256_set1_epi16(32);  // Used for rounding
471 
472   // We will compute the result two rows at a time
473   const ptrdiff_t src_stride_unrolled = src_stride << 1;
474   const ptrdiff_t dst_stride_unrolled = dst_stride << 1;
475   int h;
476 
477   // Load Kernel
478   kernel_reg = _mm_loadu_si128((const __m128i *)kernel);
479   kernel_reg = _mm_srai_epi16(kernel_reg, 1);
480   kernel_reg = _mm_packs_epi16(kernel_reg, kernel_reg);
481   kernel_reg_256 = _mm256_broadcastsi128_si256(kernel_reg);
482   kernel_reg_23 =
483       _mm256_shuffle_epi8(kernel_reg_256, _mm256_set1_epi16(0x0302u));
484   kernel_reg_45 =
485       _mm256_shuffle_epi8(kernel_reg_256, _mm256_set1_epi16(0x0504u));
486 
487   // Row -1 to row 0
488   src_reg_m10 = mm256_loadu2_si128((const __m128i *)src_ptr,
489                                    (const __m128i *)(src_ptr + src_stride));
490 
491   // Row 0 to row 1
492   src_reg_1 = _mm256_castsi128_si256(
493       _mm_loadu_si128((const __m128i *)(src_ptr + src_stride * 2)));
494   src_reg_01 = _mm256_permute2x128_si256(src_reg_m10, src_reg_1, 0x21);
495 
496   // First three rows
497   src_reg_m1001_lo = _mm256_unpacklo_epi8(src_reg_m10, src_reg_01);
498   src_reg_m1001_hi = _mm256_unpackhi_epi8(src_reg_m10, src_reg_01);
499 
500   for (h = height; h > 1; h -= 2) {
501     src_reg_2 = _mm256_castsi128_si256(
502         _mm_loadu_si128((const __m128i *)(src_ptr + src_stride * 3)));
503 
504     src_reg_12 = _mm256_inserti128_si256(src_reg_1,
505                                          _mm256_castsi256_si128(src_reg_2), 1);
506 
507     src_reg_3 = _mm256_castsi128_si256(
508         _mm_loadu_si128((const __m128i *)(src_ptr + src_stride * 4)));
509 
510     src_reg_23 = _mm256_inserti128_si256(src_reg_2,
511                                          _mm256_castsi256_si128(src_reg_3), 1);
512 
513     // Last three rows
514     src_reg_1223_lo = _mm256_unpacklo_epi8(src_reg_12, src_reg_23);
515     src_reg_1223_hi = _mm256_unpackhi_epi8(src_reg_12, src_reg_23);
516 
517     // Output from first half
518     res_reg_m1001_lo = _mm256_maddubs_epi16(src_reg_m1001_lo, kernel_reg_23);
519     res_reg_1223_lo = _mm256_maddubs_epi16(src_reg_1223_lo, kernel_reg_45);
520     res_reg_lo = _mm256_adds_epi16(res_reg_m1001_lo, res_reg_1223_lo);
521 
522     // Output from second half
523     res_reg_m1001_hi = _mm256_maddubs_epi16(src_reg_m1001_hi, kernel_reg_23);
524     res_reg_1223_hi = _mm256_maddubs_epi16(src_reg_1223_hi, kernel_reg_45);
525     res_reg_hi = _mm256_adds_epi16(res_reg_m1001_hi, res_reg_1223_hi);
526 
527     // Round the words
528     res_reg_lo = mm256_round_epi16(&res_reg_lo, &reg_32, 6);
529     res_reg_hi = mm256_round_epi16(&res_reg_hi, &reg_32, 6);
530 
531     // Combine to get the result
532     res_reg = _mm256_packus_epi16(res_reg_lo, res_reg_hi);
533 
534     // Save the result
535     mm256_store2_si128((__m128i *)dst_ptr, (__m128i *)(dst_ptr + dst_stride),
536                        &res_reg);
537 
538     // Update the source by two rows
539     src_ptr += src_stride_unrolled;
540     dst_ptr += dst_stride_unrolled;
541 
542     src_reg_m1001_lo = src_reg_1223_lo;
543     src_reg_m1001_hi = src_reg_1223_hi;
544     src_reg_1 = src_reg_3;
545   }
546 }
547 
vpx_filter_block1d8_h4_avx2(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,ptrdiff_t dst_stride,uint32_t height,const int16_t * kernel)548 static void vpx_filter_block1d8_h4_avx2(const uint8_t *src_ptr,
549                                         ptrdiff_t src_stride, uint8_t *dst_ptr,
550                                         ptrdiff_t dst_stride, uint32_t height,
551                                         const int16_t *kernel) {
552   // We will cast the kernel from 16-bit words to 8-bit words, and then extract
553   // the middle four elements of the kernel into two registers in the form
554   // ... k[3] k[2] k[3] k[2]
555   // ... k[5] k[4] k[5] k[4]
556   // Then we shuffle the source into
557   // ... s[1] s[0] s[0] s[-1]
558   // ... s[3] s[2] s[2] s[1]
559   // Calling multiply and add gives us half of the sum. Calling add gives us
560   // first half of the output. Repeat again to get the second half of the
561   // output. Finally we shuffle again to combine the two outputs.
562   // Since avx2 allows us to use 256-bit buffer, we can do this two rows at a
563   // time.
564 
565   __m128i kernel_reg_128;  // Kernel
566   __m256i kernel_reg, kernel_reg_23,
567       kernel_reg_45;                             // Segments of the kernel used
568   const __m256i reg_32 = _mm256_set1_epi16(32);  // Used for rounding
569   const ptrdiff_t unrolled_src_stride = src_stride << 1;
570   const ptrdiff_t unrolled_dst_stride = dst_stride << 1;
571   int h;
572 
573   __m256i idx_shift_0 =
574       _mm256_setr_epi8(0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 0, 1, 1,
575                        2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8);
576   __m256i idx_shift_2 =
577       _mm256_setr_epi8(2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 2, 3, 3,
578                        4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10);
579 
580   // Start one pixel before as we need tap/2 - 1 = 1 sample from the past
581   src_ptr -= 1;
582 
583   // Load Kernel
584   kernel_reg_128 = _mm_loadu_si128((const __m128i *)kernel);
585   kernel_reg_128 = _mm_srai_epi16(kernel_reg_128, 1);
586   kernel_reg_128 = _mm_packs_epi16(kernel_reg_128, kernel_reg_128);
587   kernel_reg = _mm256_broadcastsi128_si256(kernel_reg_128);
588   kernel_reg_23 = _mm256_shuffle_epi8(kernel_reg, _mm256_set1_epi16(0x0302u));
589   kernel_reg_45 = _mm256_shuffle_epi8(kernel_reg, _mm256_set1_epi16(0x0504u));
590 
591   for (h = height; h >= 2; h -= 2) {
592     // Load the source
593     const __m256i src_reg = mm256_loadu2_si128(src_ptr, src_ptr + src_stride);
594     __m256i dst_reg;
595     __m256i tmp_0, tmp_1;
596     const __m256i src_reg_shift_0 = _mm256_shuffle_epi8(src_reg, idx_shift_0);
597     const __m256i src_reg_shift_2 = _mm256_shuffle_epi8(src_reg, idx_shift_2);
598 
599     // Get the output
600     tmp_0 = _mm256_maddubs_epi16(src_reg_shift_0, kernel_reg_23);
601     tmp_1 = _mm256_maddubs_epi16(src_reg_shift_2, kernel_reg_45);
602     dst_reg = _mm256_adds_epi16(tmp_0, tmp_1);
603 
604     // Round the result
605     dst_reg = mm256_round_epi16(&dst_reg, &reg_32, 6);
606 
607     // Finally combine to get the final dst
608     dst_reg = _mm256_packus_epi16(dst_reg, dst_reg);
609     mm256_storeu2_epi64((__m128i *)dst_ptr, (__m128i *)(dst_ptr + dst_stride),
610                         &dst_reg);
611 
612     src_ptr += unrolled_src_stride;
613     dst_ptr += unrolled_dst_stride;
614   }
615 
616   // Repeat for the last row if needed
617   if (h > 0) {
618     const __m128i src_reg = _mm_loadu_si128((const __m128i *)src_ptr);
619     __m128i dst_reg;
620     const __m128i reg_32_128 = _mm_set1_epi16(32);  // Used for rounding
621     __m128i tmp_0, tmp_1;
622 
623     __m128i src_reg_shift_0 =
624         _mm_shuffle_epi8(src_reg, _mm256_castsi256_si128(idx_shift_0));
625     __m128i src_reg_shift_2 =
626         _mm_shuffle_epi8(src_reg, _mm256_castsi256_si128(idx_shift_2));
627 
628     tmp_0 = _mm_maddubs_epi16(src_reg_shift_0,
629                               _mm256_castsi256_si128(kernel_reg_23));
630     tmp_1 = _mm_maddubs_epi16(src_reg_shift_2,
631                               _mm256_castsi256_si128(kernel_reg_45));
632     dst_reg = _mm_adds_epi16(tmp_0, tmp_1);
633 
634     dst_reg = mm_round_epi16_sse2(&dst_reg, &reg_32_128, 6);
635 
636     dst_reg = _mm_packus_epi16(dst_reg, _mm_setzero_si128());
637 
638     _mm_storel_epi64((__m128i *)dst_ptr, dst_reg);
639   }
640 }
641 
vpx_filter_block1d8_v4_avx2(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,ptrdiff_t dst_stride,uint32_t height,const int16_t * kernel)642 static void vpx_filter_block1d8_v4_avx2(const uint8_t *src_ptr,
643                                         ptrdiff_t src_stride, uint8_t *dst_ptr,
644                                         ptrdiff_t dst_stride, uint32_t height,
645                                         const int16_t *kernel) {
646   // We will load two rows of pixels as 8-bit words, rearrange them into the
647   // form
648   // ... s[1,0] s[0,0] s[0,0] s[-1,0]
649   // so that we can call multiply and add with the kernel partial output. Then
650   // we can call add with another row to get the output.
651 
652   // Register for source s[-1:3, :]
653   __m256i src_reg_1, src_reg_2, src_reg_3;
654   // Interleaved rows of the source. lo is first half, hi second
655   __m256i src_reg_m10, src_reg_01, src_reg_12, src_reg_23;
656   __m256i src_reg_m1001, src_reg_1223;
657 
658   __m128i kernel_reg_128;  // Kernel
659   __m256i kernel_reg, kernel_reg_23,
660       kernel_reg_45;  // Segments of the kernel used
661 
662   // Result after multiply and add
663   __m256i res_reg_m1001, res_reg_1223;
664   __m256i res_reg;
665 
666   const __m256i reg_32 = _mm256_set1_epi16(32);  // Used for rounding
667 
668   // We will compute the result two rows at a time
669   const ptrdiff_t src_stride_unrolled = src_stride << 1;
670   const ptrdiff_t dst_stride_unrolled = dst_stride << 1;
671   int h;
672 
673   // Load Kernel
674   kernel_reg_128 = _mm_loadu_si128((const __m128i *)kernel);
675   kernel_reg_128 = _mm_srai_epi16(kernel_reg_128, 1);
676   kernel_reg_128 = _mm_packs_epi16(kernel_reg_128, kernel_reg_128);
677   kernel_reg = _mm256_broadcastsi128_si256(kernel_reg_128);
678   kernel_reg_23 = _mm256_shuffle_epi8(kernel_reg, _mm256_set1_epi16(0x0302u));
679   kernel_reg_45 = _mm256_shuffle_epi8(kernel_reg, _mm256_set1_epi16(0x0504u));
680 
681   // Row -1 to row 0
682   src_reg_m10 = mm256_loadu2_epi64((const __m128i *)src_ptr,
683                                    (const __m128i *)(src_ptr + src_stride));
684 
685   // Row 0 to row 1
686   src_reg_1 = _mm256_castsi128_si256(
687       _mm_loadu_si128((const __m128i *)(src_ptr + src_stride * 2)));
688   src_reg_01 = _mm256_permute2x128_si256(src_reg_m10, src_reg_1, 0x21);
689 
690   // First three rows
691   src_reg_m1001 = _mm256_unpacklo_epi8(src_reg_m10, src_reg_01);
692 
693   for (h = height; h > 1; h -= 2) {
694     src_reg_2 = _mm256_castsi128_si256(
695         _mm_loadl_epi64((const __m128i *)(src_ptr + src_stride * 3)));
696 
697     src_reg_12 = _mm256_inserti128_si256(src_reg_1,
698                                          _mm256_castsi256_si128(src_reg_2), 1);
699 
700     src_reg_3 = _mm256_castsi128_si256(
701         _mm_loadl_epi64((const __m128i *)(src_ptr + src_stride * 4)));
702 
703     src_reg_23 = _mm256_inserti128_si256(src_reg_2,
704                                          _mm256_castsi256_si128(src_reg_3), 1);
705 
706     // Last three rows
707     src_reg_1223 = _mm256_unpacklo_epi8(src_reg_12, src_reg_23);
708 
709     // Output
710     res_reg_m1001 = _mm256_maddubs_epi16(src_reg_m1001, kernel_reg_23);
711     res_reg_1223 = _mm256_maddubs_epi16(src_reg_1223, kernel_reg_45);
712     res_reg = _mm256_adds_epi16(res_reg_m1001, res_reg_1223);
713 
714     // Round the words
715     res_reg = mm256_round_epi16(&res_reg, &reg_32, 6);
716 
717     // Combine to get the result
718     res_reg = _mm256_packus_epi16(res_reg, res_reg);
719 
720     // Save the result
721     mm256_storeu2_epi64((__m128i *)dst_ptr, (__m128i *)(dst_ptr + dst_stride),
722                         &res_reg);
723 
724     // Update the source by two rows
725     src_ptr += src_stride_unrolled;
726     dst_ptr += dst_stride_unrolled;
727 
728     src_reg_m1001 = src_reg_1223;
729     src_reg_1 = src_reg_3;
730   }
731 }
732 
vpx_filter_block1d4_h4_avx2(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,ptrdiff_t dst_stride,uint32_t height,const int16_t * kernel)733 static void vpx_filter_block1d4_h4_avx2(const uint8_t *src_ptr,
734                                         ptrdiff_t src_stride, uint8_t *dst_ptr,
735                                         ptrdiff_t dst_stride, uint32_t height,
736                                         const int16_t *kernel) {
737   // We will cast the kernel from 16-bit words to 8-bit words, and then extract
738   // the middle four elements of the kernel into a single register in the form
739   // k[5:2] k[5:2] k[5:2] k[5:2]
740   // Then we shuffle the source into
741   // s[5:2] s[4:1] s[3:0] s[2:-1]
742   // Calling multiply and add gives us half of the sum next to each other.
743   // Calling horizontal add then gives us the output.
744   // Since avx2 has 256-bit register, we can do 2 rows at a time.
745 
746   __m128i kernel_reg_128;  // Kernel
747   __m256i kernel_reg;
748   const __m256i reg_32 = _mm256_set1_epi16(32);  // Used for rounding
749   int h;
750   const ptrdiff_t unrolled_src_stride = src_stride << 1;
751   const ptrdiff_t unrolled_dst_stride = dst_stride << 1;
752 
753   __m256i shuf_idx =
754       _mm256_setr_epi8(0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, 0, 1, 2,
755                        3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6);
756 
757   // Start one pixel before as we need tap/2 - 1 = 1 sample from the past
758   src_ptr -= 1;
759 
760   // Load Kernel
761   kernel_reg_128 = _mm_loadu_si128((const __m128i *)kernel);
762   kernel_reg_128 = _mm_srai_epi16(kernel_reg_128, 1);
763   kernel_reg_128 = _mm_packs_epi16(kernel_reg_128, kernel_reg_128);
764   kernel_reg = _mm256_broadcastsi128_si256(kernel_reg_128);
765   kernel_reg = _mm256_shuffle_epi8(kernel_reg, _mm256_set1_epi32(0x05040302u));
766 
767   for (h = height; h > 1; h -= 2) {
768     // Load the source
769     const __m256i src_reg = mm256_loadu2_epi64(
770         (const __m128i *)src_ptr, (const __m128i *)(src_ptr + src_stride));
771     const __m256i src_reg_shuf = _mm256_shuffle_epi8(src_reg, shuf_idx);
772 
773     // Get the result
774     __m256i dst = _mm256_maddubs_epi16(src_reg_shuf, kernel_reg);
775     dst = _mm256_hadds_epi16(dst, _mm256_setzero_si256());
776 
777     // Round result
778     dst = mm256_round_epi16(&dst, &reg_32, 6);
779 
780     // Pack to 8-bits
781     dst = _mm256_packus_epi16(dst, _mm256_setzero_si256());
782 
783     // Save
784     mm256_storeu2_epi32((__m128i *const)dst_ptr,
785                         (__m128i *const)(dst_ptr + dst_stride), &dst);
786 
787     src_ptr += unrolled_src_stride;
788     dst_ptr += unrolled_dst_stride;
789   }
790 
791   if (h > 0) {
792     // Load the source
793     const __m128i reg_32_128 = _mm_set1_epi16(32);  // Used for rounding
794     __m128i src_reg = _mm_loadl_epi64((const __m128i *)src_ptr);
795     __m128i src_reg_shuf =
796         _mm_shuffle_epi8(src_reg, _mm256_castsi256_si128(shuf_idx));
797 
798     // Get the result
799     __m128i dst =
800         _mm_maddubs_epi16(src_reg_shuf, _mm256_castsi256_si128(kernel_reg));
801     dst = _mm_hadds_epi16(dst, _mm_setzero_si128());
802 
803     // Round result
804     dst = mm_round_epi16_sse2(&dst, &reg_32_128, 6);
805 
806     // Pack to 8-bits
807     dst = _mm_packus_epi16(dst, _mm_setzero_si128());
808     *((int *)(dst_ptr)) = _mm_cvtsi128_si32(dst);
809   }
810 }
811 
vpx_filter_block1d4_v4_avx2(const uint8_t * src_ptr,ptrdiff_t src_stride,uint8_t * dst_ptr,ptrdiff_t dst_stride,uint32_t height,const int16_t * kernel)812 static void vpx_filter_block1d4_v4_avx2(const uint8_t *src_ptr,
813                                         ptrdiff_t src_stride, uint8_t *dst_ptr,
814                                         ptrdiff_t dst_stride, uint32_t height,
815                                         const int16_t *kernel) {
816   // We will load two rows of pixels as 8-bit words, rearrange them into the
817   // form
818   // ... s[3,0] s[2,0] s[1,0] s[0,0] s[2,0] s[1,0] s[0,0] s[-1,0]
819   // so that we can call multiply and add with the kernel to get partial output.
820   // Calling horizontal add then gives us the completely output
821 
822   // Register for source s[-1:3, :]
823   __m256i src_reg_1, src_reg_2, src_reg_3;
824   // Interleaved rows of the source. lo is first half, hi second
825   __m256i src_reg_m10, src_reg_01, src_reg_12, src_reg_23;
826   __m256i src_reg_m1001, src_reg_1223, src_reg_m1012_1023;
827 
828   __m128i kernel_reg_128;  // Kernel
829   __m256i kernel_reg;
830 
831   // Result after multiply and add
832   __m256i res_reg;
833 
834   const __m256i reg_32 = _mm256_set1_epi16(32);  // Used for rounding
835 
836   // We will compute the result two rows at a time
837   const ptrdiff_t src_stride_unrolled = src_stride << 1;
838   const ptrdiff_t dst_stride_unrolled = dst_stride << 1;
839   int h;
840 
841   // Load Kernel
842   kernel_reg_128 = _mm_loadu_si128((const __m128i *)kernel);
843   kernel_reg_128 = _mm_srai_epi16(kernel_reg_128, 1);
844   kernel_reg_128 = _mm_packs_epi16(kernel_reg_128, kernel_reg_128);
845   kernel_reg = _mm256_broadcastsi128_si256(kernel_reg_128);
846   kernel_reg = _mm256_shuffle_epi8(kernel_reg, _mm256_set1_epi32(0x05040302u));
847 
848   // Row -1 to row 0
849   src_reg_m10 = mm256_loadu2_si128((const __m128i *)src_ptr,
850                                    (const __m128i *)(src_ptr + src_stride));
851 
852   // Row 0 to row 1
853   src_reg_1 = _mm256_castsi128_si256(
854       _mm_loadu_si128((const __m128i *)(src_ptr + src_stride * 2)));
855   src_reg_01 = _mm256_permute2x128_si256(src_reg_m10, src_reg_1, 0x21);
856 
857   // First three rows
858   src_reg_m1001 = _mm256_unpacklo_epi8(src_reg_m10, src_reg_01);
859 
860   for (h = height; h > 1; h -= 2) {
861     src_reg_2 = _mm256_castsi128_si256(
862         _mm_loadl_epi64((const __m128i *)(src_ptr + src_stride * 3)));
863 
864     src_reg_12 = _mm256_inserti128_si256(src_reg_1,
865                                          _mm256_castsi256_si128(src_reg_2), 1);
866 
867     src_reg_3 = _mm256_castsi128_si256(
868         _mm_loadl_epi64((const __m128i *)(src_ptr + src_stride * 4)));
869 
870     src_reg_23 = _mm256_inserti128_si256(src_reg_2,
871                                          _mm256_castsi256_si128(src_reg_3), 1);
872 
873     // Last three rows
874     src_reg_1223 = _mm256_unpacklo_epi8(src_reg_12, src_reg_23);
875 
876     // Combine all the rows
877     src_reg_m1012_1023 = _mm256_unpacklo_epi16(src_reg_m1001, src_reg_1223);
878 
879     // Output
880     res_reg = _mm256_maddubs_epi16(src_reg_m1012_1023, kernel_reg);
881     res_reg = _mm256_hadds_epi16(res_reg, _mm256_setzero_si256());
882 
883     // Round the words
884     res_reg = mm256_round_epi16(&res_reg, &reg_32, 6);
885 
886     // Combine to get the result
887     res_reg = _mm256_packus_epi16(res_reg, res_reg);
888 
889     // Save the result
890     mm256_storeu2_epi32((__m128i *)dst_ptr, (__m128i *)(dst_ptr + dst_stride),
891                         &res_reg);
892 
893     // Update the source by two rows
894     src_ptr += src_stride_unrolled;
895     dst_ptr += dst_stride_unrolled;
896 
897     src_reg_m1001 = src_reg_1223;
898     src_reg_1 = src_reg_3;
899   }
900 }
901 
vpx_filter_block1d8_v8_avx2(const uint8_t * src_ptr,ptrdiff_t src_pitch,uint8_t * output_ptr,ptrdiff_t out_pitch,uint32_t output_height,const int16_t * filter)902 static void vpx_filter_block1d8_v8_avx2(
903     const uint8_t *src_ptr, ptrdiff_t src_pitch, uint8_t *output_ptr,
904     ptrdiff_t out_pitch, uint32_t output_height, const int16_t *filter) {
905   __m256i f[4], ss[4];
906   __m256i r[8];
907   __m128i s[9];
908 
909   unsigned int y = output_height;
910   // Multiply the size of the source stride by two
911   const ptrdiff_t src_stride = src_pitch << 1;
912 
913   // The output_height is always a multiple of two.
914   assert(!(output_height & 1));
915 
916   shuffle_filter_avx2(filter, f);
917   s[0] = _mm_loadl_epi64((const __m128i *)(src_ptr + 0 * src_pitch));
918   s[1] = _mm_loadl_epi64((const __m128i *)(src_ptr + 1 * src_pitch));
919   s[2] = _mm_loadl_epi64((const __m128i *)(src_ptr + 2 * src_pitch));
920   s[3] = _mm_loadl_epi64((const __m128i *)(src_ptr + 3 * src_pitch));
921   s[4] = _mm_loadl_epi64((const __m128i *)(src_ptr + 4 * src_pitch));
922   s[5] = _mm_loadl_epi64((const __m128i *)(src_ptr + 5 * src_pitch));
923   s[6] = _mm_loadl_epi64((const __m128i *)(src_ptr + 6 * src_pitch));
924 
925   // merge the result together
926   // r[0]:    0 0 0 0 0 0 0 0 r17 r16 r15 r14 r13 r12 r11 r10 | 0 0 0 0 0 0 0 0
927   // r07 r06 r05 r04 r03 r02 r01 r00
928   r[0] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[0]), s[1], 1);
929 
930   // r[1]:    0 0 0 0 0 0 0 0 r27 r26 r25 r24 r23 r22 r21 r20 | 0 0 0 0 0 0 0 0
931   // r17 r16 r15 r14 r13 r12 r11 r10
932   r[1] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[1]), s[2], 1);
933 
934   // r[2]:    0 0 0 0 0 0 0 0 r37 r36 r35 r34 r33 r32 r31 r30 | 0 0 0 0 0 0 0 0
935   // r27 r26 r25 r24 r23 r22 r21 r20
936   r[2] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[2]), s[3], 1);
937 
938   // r[3]:    0 0 0 0 0 0 0 0 r47 r46 r45 r44 r43 r42 r41 r40 | 0 0 0 0 0 0 0 0
939   // r37 r36 r35 r34 r33 r32 r31 r30
940   r[3] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[3]), s[4], 1);
941 
942   // r[4]:    0 0 0 0 0 0 0 0 r57 r56 r55 r54 r53 r52 r51 r50 | 0 0 0 0 0 0 0 0
943   // r47 r46 r45 r44 r43 r42 r41 r40
944   r[4] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[4]), s[5], 1);
945 
946   // r[5]:    0 0 0 0 0 0 0 0 r67 r66 r65 r64 r63 r62 r61 r60 | 0 0 0 0 0 0 0 0
947   // r57 r56 r55 r54 r53 r52 r51 r50
948   r[5] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[5]), s[6], 1);
949 
950   // Merge together
951   // ss[0]: |r27 r17|.......|r21 r11|r20 r10 || r17 r07|.....|r12 r02|r11
952   // r01|r10 r00|
953   ss[0] = _mm256_unpacklo_epi8(r[0], r[1]);
954 
955   // ss[0]: |r47 r37|.......|r41 r31|r40 r30 || r37 r27|.....|r32 r22|r31
956   // r21|r30 r20|
957   ss[1] = _mm256_unpacklo_epi8(r[2], r[3]);
958 
959   // ss[2]: |r67 r57|.......|r61 r51|r60 r50 || r57 r47|.....|r52 r42|r51
960   // r41|r50 r40|
961   ss[2] = _mm256_unpacklo_epi8(r[4], r[5]);
962 
963   // Process 2 rows at a time
964   do {
965     s[7] = _mm_loadl_epi64((const __m128i *)(src_ptr + 7 * src_pitch));
966     s[8] = _mm_loadl_epi64((const __m128i *)(src_ptr + 8 * src_pitch));
967 
968     // r[6]:    0 0 0 0 0 0 0 0 r77 r76 r75 r74 r73 r72 r71 r70 | 0 0 0 0 0 0 0
969     // 0 r67 r66 r65 r64 r63 r62 r61 r60
970     r[6] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[6]), s[7], 1);
971     // r[7]:    0 0 0 0 0 0 0 0 r87 r86 r85 r84 r83 r82 r81 r80 | 0 0 0 0 0 0 0
972     // 0 r77 r76 r75 r74 r73 r72 r71 r70
973     r[7] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[7]), s[8], 1);
974 
975     // ss[3] : | r87 r77 | .......| r81 r71 | r80 r70 || r77 r67 | .....| r72
976     // r62 | r71 r61|r70 r60|
977     ss[3] = _mm256_unpacklo_epi8(r[6], r[7]);
978     ss[0] = convolve8_16_avx2(ss, f);
979     ss[0] = _mm256_packus_epi16(ss[0], ss[0]);
980     src_ptr += src_stride;
981 
982     /* shift down two rows */
983     s[6] = s[8];
984     _mm_storel_epi64((__m128i *)&output_ptr[0], _mm256_castsi256_si128(ss[0]));
985     output_ptr += out_pitch;
986     _mm_storel_epi64((__m128i *)&output_ptr[0],
987                      _mm256_extractf128_si256(ss[0], 1));
988     output_ptr += out_pitch;
989     ss[0] = ss[1];
990     ss[1] = ss[2];
991     ss[2] = ss[3];
992     y -= 2;
993   } while (y > 1);
994 }
995 
vpx_filter_block1d4_h8_avx2(const uint8_t * src_ptr,ptrdiff_t src_pitch,uint8_t * output_ptr,ptrdiff_t output_pitch,uint32_t output_height,const int16_t * filter)996 static void vpx_filter_block1d4_h8_avx2(
997     const uint8_t *src_ptr, ptrdiff_t src_pitch, uint8_t *output_ptr,
998     ptrdiff_t output_pitch, uint32_t output_height, const int16_t *filter) {
999   __m128i filtersReg;
1000   __m256i addFilterReg64_256bit;
1001   unsigned int y = output_height;
1002 
1003   assert(output_height > 1);
1004 
1005   addFilterReg64_256bit = _mm256_set1_epi16(32);
1006 
1007   // f7 f6 f5 f4 f3 f2 f1 f0 (16 bit)
1008   filtersReg = _mm_loadu_si128((const __m128i *)filter);
1009 
1010   // converting the 16 bit (short) to 8 bit (byte) and have the same data
1011   // in both lanes of 128 bit register.
1012   // f7 f6 f5 f4 f3 f2 f1 f0 || f7 f6 f5 f4 f3 f2 f1 f0 (8 bit each)
1013   filtersReg = _mm_packs_epi16(filtersReg, filtersReg);
1014 
1015   {
1016     ptrdiff_t src_stride;
1017     __m256i filt1Reg, filt2Reg, firstFilters, secondFilters;
1018     // have the same data in both lanes of a 256 bit register
1019     // f7 f6 f5 f4 f3 f2 f1 f0 f7 f6 f5 f4 f3 f2 f1 f0 | f7 f6 f5 f4 f3 f2 f1 f0
1020     // f7 f6 f5 f4 f3 f2 f1 f0 (8bit each)
1021     const __m256i filtersReg32 = _mm256_broadcastsi128_si256(filtersReg);
1022 
1023     // duplicate only the first 32 bits
1024     // f3 f2 f1 f0|f3 f2 f1 f0|f3 f2 f1 f0|f3 f2 f1 f0 | f3 f2 f1 f0|f3 f2 f1
1025     // f0|f3 f2 f1 f0|f3 f2 f1 f0
1026     firstFilters = _mm256_shuffle_epi32(filtersReg32, 0);
1027     // duplicate only the second 32 bits
1028     // f7 f6 f5 f4|f7 f6 f5 f4|f7 f6 f5 f4|f7 f6 f5 f4 | f7 f6 f5 f4|f7 f6 f5
1029     // f4|f7 f6 f5 f4|f7 f6 f5 f4
1030     secondFilters = _mm256_shuffle_epi32(filtersReg32, 0x55);
1031 
1032     // s6 s5 s4 s3 s5 s4 s3 s2 s4 s3 s2 s1 s3 s2 s1 s0 | s6 s5 s4 s3 s5 s4 s3
1033     // s2 s4 s3 s2 s1 s3 s2 s1 s0
1034     filt1Reg = _mm256_load_si256((__m256i const *)filt_d4_global_avx2);
1035 
1036     // s10 s9 s8 s7 s9 s8 s7 s6 s8 s7 s6 s5 s7 s6 s5 s4 | s10 s9 s8 s7 s9 s8 s7
1037     // s6 s8 s7 s6 s5 s7 s6 s5 s4
1038     filt2Reg = _mm256_load_si256((__m256i const *)(filt_d4_global_avx2 + 32));
1039 
1040     // multiple the size of the source and destination stride by two
1041     src_stride = src_pitch << 1;
1042 
1043     do {
1044       __m256i srcRegFilt32b1_1, srcRegFilt32b2, srcReg32b1;
1045       // load the 2 strides of source
1046       // r115 r114 ...... r15 r14 r13 r12 r11 r10 | r015 r014 r013 ...... r07
1047       // r06 r05 r04 r03 r02 r01 r00
1048       srcReg32b1 = mm256_loadu2_si128(src_ptr - 3, src_ptr - 3 + src_pitch);
1049 
1050       // filter the source buffer
1051       // r16 r15 r14 r13 r15 r14 r13 r12 r14 r13 r12 r11 r13 r12 r11 r10 | r06
1052       // r05 r04 r03 r05 r04 r03 r02 r04 r03 r02 r01 r03 r02 r01 r00
1053       srcRegFilt32b1_1 = _mm256_shuffle_epi8(srcReg32b1, filt1Reg);
1054 
1055       // multiply 4 adjacent elements with the filter and add the result
1056       // ...|f3*r14+f2*r13|f1*r13+f0*r12|f3*r13+f2*r12|f1*r11+f0*r10||...
1057       // |f1*r03+f0*r02|f3*r04+f2*r03|f1*r02+f0*r01|f3*r03+f2*r02|f1*r01+f0*r00
1058       srcRegFilt32b1_1 = _mm256_maddubs_epi16(srcRegFilt32b1_1, firstFilters);
1059 
1060       // filter the source buffer
1061       // r110 r19 r18 r17|r19 r18 r17 r16|r18 r17 r16 r15|r17 r16 r15 r14||r010
1062       // r09 r08 r07|r09 r08 r07 r06|r08 r07 r06 r05|r07 r06 r05 r04
1063       srcRegFilt32b2 = _mm256_shuffle_epi8(srcReg32b1, filt2Reg);
1064 
1065       // multiply 4 adjacent elements with the filter and add the result
1066       // r010 r09 r08 r07|r9 r08 r07 r06|r08 r07 r06 r05|r07 r06 r05 r04||r010
1067       // r09 r08 r07|r9 r08 r07 r06|r08 r07 r06 r05|r07 r06 r05 r04
1068       srcRegFilt32b2 = _mm256_maddubs_epi16(srcRegFilt32b2, secondFilters);
1069 
1070       srcRegFilt32b1_1 =
1071           _mm256_add_epi16(srcRegFilt32b1_1, addFilterReg64_256bit);
1072       srcRegFilt32b1_1 = _mm256_adds_epi16(srcRegFilt32b1_1, srcRegFilt32b2);
1073 
1074       srcRegFilt32b1_1 =
1075           _mm256_hadds_epi16(srcRegFilt32b1_1, _mm256_setzero_si256());
1076 
1077       // 0 0 0 0 R13 R12 R11 R10 || 0 0 0 0 R03 R02 R01 R00 (16bit)
1078       srcRegFilt32b1_1 = _mm256_srai_epi16(srcRegFilt32b1_1, 7);
1079 
1080       // 8zeros 0 0 0 0 R13 R12 R11 R10 || 8zeros 0 0 0 0 R03 R02 R01 R00 (8bit)
1081       srcRegFilt32b1_1 =
1082           _mm256_packus_epi16(srcRegFilt32b1_1, _mm256_setzero_si256());
1083 
1084       src_ptr += src_stride;
1085       // save first row 4 values
1086       *((int *)&output_ptr[0]) =
1087           _mm_cvtsi128_si32(_mm256_castsi256_si128(srcRegFilt32b1_1));
1088       output_ptr += output_pitch;
1089 
1090       // save second row 4 values
1091       *((int *)&output_ptr[0]) =
1092           _mm_cvtsi128_si32(_mm256_extractf128_si256(srcRegFilt32b1_1, 1));
1093       output_ptr += output_pitch;
1094 
1095       y = y - 2;
1096     } while (y > 1);
1097 
1098     // For remaining height
1099     if (y > 0) {
1100       __m128i srcReg1, srcRegFilt1_1, addFilterReg64;
1101       __m128i srcRegFilt2;
1102 
1103       addFilterReg64 = _mm_set1_epi32((int)0x0400040u);
1104 
1105       srcReg1 = _mm_loadu_si128((const __m128i *)(src_ptr - 3));
1106 
1107       // filter the source buffer
1108       srcRegFilt1_1 =
1109           _mm_shuffle_epi8(srcReg1, _mm256_castsi256_si128(filt1Reg));
1110 
1111       // multiply 4 adjacent elements with the filter and add the result
1112       srcRegFilt1_1 = _mm_maddubs_epi16(srcRegFilt1_1,
1113                                         _mm256_castsi256_si128(firstFilters));
1114 
1115       // filter the source buffer
1116       srcRegFilt2 = _mm_shuffle_epi8(srcReg1, _mm256_castsi256_si128(filt2Reg));
1117 
1118       // multiply 4 adjacent elements with the filter and add the result
1119       srcRegFilt2 =
1120           _mm_maddubs_epi16(srcRegFilt2, _mm256_castsi256_si128(secondFilters));
1121 
1122       srcRegFilt1_1 = _mm_adds_epi16(srcRegFilt1_1, srcRegFilt2);
1123       srcRegFilt1_1 = _mm_hadds_epi16(srcRegFilt1_1, _mm_setzero_si128());
1124       // shift by 6 bit each 16 bit
1125       srcRegFilt1_1 = _mm_adds_epi16(srcRegFilt1_1, addFilterReg64);
1126       srcRegFilt1_1 = _mm_srai_epi16(srcRegFilt1_1, 7);
1127 
1128       // shrink to 8 bit each 16 bits, the first lane contain the first
1129       // convolve result and the second lane contain the second convolve result
1130       srcRegFilt1_1 = _mm_packus_epi16(srcRegFilt1_1, _mm_setzero_si128());
1131 
1132       // save 4 bytes
1133       *((int *)(output_ptr)) = _mm_cvtsi128_si32(srcRegFilt1_1);
1134     }
1135   }
1136 }
1137 
vpx_filter_block1d4_v8_avx2(const uint8_t * src_ptr,ptrdiff_t src_pitch,uint8_t * output_ptr,ptrdiff_t out_pitch,uint32_t output_height,const int16_t * filter)1138 static void vpx_filter_block1d4_v8_avx2(
1139     const uint8_t *src_ptr, ptrdiff_t src_pitch, uint8_t *output_ptr,
1140     ptrdiff_t out_pitch, uint32_t output_height, const int16_t *filter) {
1141   __m256i f[4], ss[4];
1142   __m256i r[9], rr[2];
1143   __m128i s[11];
1144 
1145   unsigned int y = output_height;
1146   // Multiply the size of the source stride by four
1147   const ptrdiff_t src_stride = src_pitch << 2;
1148   const ptrdiff_t out_stride = out_pitch << 2;
1149 
1150   // The output_height is always a multiple of two.
1151   assert(!(output_height & 0x01));
1152 
1153   shuffle_filter_avx2(filter, f);
1154 
1155   s[0] = _mm_loadl_epi64((const __m128i *)(src_ptr + 0 * src_pitch));
1156   s[1] = _mm_loadl_epi64((const __m128i *)(src_ptr + 1 * src_pitch));
1157   s[2] = _mm_loadl_epi64((const __m128i *)(src_ptr + 2 * src_pitch));
1158   s[3] = _mm_loadl_epi64((const __m128i *)(src_ptr + 3 * src_pitch));
1159   s[4] = _mm_loadl_epi64((const __m128i *)(src_ptr + 4 * src_pitch));
1160   s[5] = _mm_loadl_epi64((const __m128i *)(src_ptr + 5 * src_pitch));
1161   s[6] = _mm_loadl_epi64((const __m128i *)(src_ptr + 6 * src_pitch));
1162 
1163   r[0] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[0]), s[2], 1);
1164   r[1] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[1]), s[3], 1);
1165   r[2] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[2]), s[4], 1);
1166   r[3] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[3]), s[5], 1);
1167   r[4] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[4]), s[6], 1);
1168 
1169   // r37.....r24..r33..r31 r30 r23 r22 r21 r20|r17....r14 r07..r05 r04 r13 r12
1170   // r11 r10 r03 r02 r01 r00
1171   rr[0] = _mm256_unpacklo_epi32(r[0], r[1]);
1172 
1173   // r47.....r34..r43..r41 r40 r33 r32 r31 r30|r27....r24 r17..r15 r14 r23 r22
1174   // r21 r20 r13 r12 r11 r10
1175   rr[1] = _mm256_unpacklo_epi32(r[1], r[2]);
1176 
1177   // r43 r33....r40 r30|r33 r23....r30 r20||r23 r13....r20 r10|r13 r03....r10
1178   // r00|
1179   ss[0] = _mm256_unpacklo_epi8(rr[0], rr[1]);
1180 
1181   // r37.....r24..r33..r31 r30 r23 r22 r21 r20||r17....r14 r07..r05 r04 r13 r12
1182   // r11 r10 r03 r02 r01 r00
1183   rr[0] = _mm256_unpacklo_epi32(r[2], r[3]);
1184 
1185   // r47.....r34..r43..r41 r40 r33 r32 r31 r30|r27....r24 r17..r15 r14 r23 r22
1186   // r21 r20 r13 r12 r11 r10
1187   rr[1] = _mm256_unpacklo_epi32(r[3], r[4]);
1188 
1189   // r63 r53....r60 r50|r53 r43....r50 r40||r43 r33....r40 r30|r33 r23....r30
1190   // r20|
1191   ss[1] = _mm256_unpacklo_epi8(rr[0], rr[1]);
1192   // Process 4 rows at a time
1193   while (y >= 4) {
1194     s[7] = _mm_loadl_epi64((const __m128i *)(src_ptr + 7 * src_pitch));
1195     s[8] = _mm_loadl_epi64((const __m128i *)(src_ptr + 8 * src_pitch));
1196     s[9] = _mm_loadl_epi64((const __m128i *)(src_ptr + 9 * src_pitch));
1197     s[10] = _mm_loadl_epi64((const __m128i *)(src_ptr + 10 * src_pitch));
1198 
1199     r[5] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[5]), s[7], 1);
1200     r[6] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[6]), s[8], 1);
1201     rr[0] = _mm256_unpacklo_epi32(r[4], r[5]);
1202     rr[1] = _mm256_unpacklo_epi32(r[5], r[6]);
1203     ss[2] = _mm256_unpacklo_epi8(rr[0], rr[1]);
1204 
1205     r[7] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[7]), s[9], 1);
1206     r[8] = _mm256_inserti128_si256(_mm256_castsi128_si256(s[8]), s[10], 1);
1207     rr[0] = _mm256_unpacklo_epi32(r[6], r[7]);
1208     rr[1] = _mm256_unpacklo_epi32(r[7], r[8]);
1209     ss[3] = _mm256_unpacklo_epi8(rr[0], rr[1]);
1210 
1211     ss[0] = convolve8_16_avx2(ss, f);
1212 
1213     // r3 r2 r3 r2 r1 r0 r1 r0
1214     ss[0] = _mm256_packus_epi16(ss[0], ss[0]);
1215     src_ptr += src_stride;
1216 
1217     mm256_storeu2_epi32((__m128i *const)output_ptr,
1218                         (__m128i *const)(output_ptr + (2 * out_pitch)), ss);
1219 
1220     ss[0] = _mm256_srli_si256(ss[0], 4);
1221 
1222     mm256_storeu2_epi32((__m128i *const)(output_ptr + (1 * out_pitch)),
1223                         (__m128i *const)(output_ptr + (3 * out_pitch)), ss);
1224 
1225     output_ptr += out_stride;
1226 
1227     ss[0] = ss[2];
1228     ss[1] = ss[3];
1229 
1230     s[6] = s[10];
1231     s[5] = s[9];
1232 
1233     r[4] = r[8];
1234     y -= 4;
1235   }
1236 
1237   // Process 2 rows
1238   if (y == 2) {
1239     __m128i ss1[4], f1[4], r1[4];
1240 
1241     s[4] = _mm_loadl_epi64((const __m128i *)(src_ptr + 4 * src_pitch));
1242     s[7] = _mm_loadl_epi64((const __m128i *)(src_ptr + 7 * src_pitch));
1243     s[8] = _mm_loadl_epi64((const __m128i *)(src_ptr + 8 * src_pitch));
1244 
1245     f1[0] = _mm256_castsi256_si128(f[0]);
1246     f1[1] = _mm256_castsi256_si128(f[1]);
1247     f1[2] = _mm256_castsi256_si128(f[2]);
1248     f1[3] = _mm256_castsi256_si128(f[3]);
1249 
1250     r1[0] = _mm_unpacklo_epi32(s[4], s[5]);
1251     r1[1] = _mm_unpacklo_epi32(s[5], s[6]);
1252 
1253     // R7-6 xxxx .. . . x| r73 r72 r71 r70 r63 r62 r61 r60
1254     r1[2] = _mm_unpacklo_epi32(s[6], s[7]);
1255 
1256     // R8-7 xxxx .. . . x| r83 r82 r81 r80 r73 r72 r71 r70
1257     r1[3] = _mm_unpacklo_epi32(s[7], s[8]);
1258 
1259     // r23 r13....r20 r10|r13 r03....r10 r00
1260     ss1[0] = _mm256_castsi256_si128(ss[0]);
1261 
1262     // r43 r33....r40 r30|r33 r23....r30 r20
1263     ss1[1] = _mm256_castsi256_si128(ss[1]);
1264 
1265     // r63 r53....r60 r50|r53 r43....r50 r40
1266     ss1[2] = _mm_unpacklo_epi8(r1[0], r1[1]);
1267 
1268     // r83 r73....r80 r70|r73 r63....r70 r60
1269     ss1[3] = _mm_unpacklo_epi8(r1[2], r1[3]);
1270 
1271     ss1[0] = convolve8_8_ssse3(ss1, f1);
1272 
1273     // r1 r0 r1 r0
1274     ss1[0] = _mm_packus_epi16(ss1[0], ss1[0]);
1275 
1276     // Save first row 4 values
1277     *((int *)&output_ptr[0]) = _mm_cvtsi128_si32(ss1[0]);
1278     output_ptr += out_pitch;
1279 
1280     ss1[0] = _mm_srli_si128(ss1[0], 4);
1281     // Save second row 4 values
1282     *((int *)&output_ptr[0]) = _mm_cvtsi128_si32(ss1[0]);
1283   }
1284 }
1285 
1286 #if HAVE_AVX2 && HAVE_SSSE3
1287 #if VPX_ARCH_X86_64
1288 filter8_1dfunction vpx_filter_block1d8_v8_intrin_ssse3;
1289 filter8_1dfunction vpx_filter_block1d8_h8_intrin_ssse3;
1290 filter8_1dfunction vpx_filter_block1d4_h8_intrin_ssse3;
1291 #else   // VPX_ARCH_X86
1292 filter8_1dfunction vpx_filter_block1d8_v8_ssse3;
1293 filter8_1dfunction vpx_filter_block1d8_h8_ssse3;
1294 filter8_1dfunction vpx_filter_block1d4_h8_ssse3;
1295 #endif  // VPX_ARCH_X86_64
1296 filter8_1dfunction vpx_filter_block1d8_v8_avg_ssse3;
1297 filter8_1dfunction vpx_filter_block1d8_h8_avg_ssse3;
1298 filter8_1dfunction vpx_filter_block1d4_v8_avg_ssse3;
1299 filter8_1dfunction vpx_filter_block1d4_h8_avg_ssse3;
1300 #define vpx_filter_block1d8_v8_avg_avx2 vpx_filter_block1d8_v8_avg_ssse3
1301 #define vpx_filter_block1d8_h8_avg_avx2 vpx_filter_block1d8_h8_avg_ssse3
1302 #define vpx_filter_block1d4_v8_avg_avx2 vpx_filter_block1d4_v8_avg_ssse3
1303 #define vpx_filter_block1d4_h8_avg_avx2 vpx_filter_block1d4_h8_avg_ssse3
1304 filter8_1dfunction vpx_filter_block1d16_v2_ssse3;
1305 filter8_1dfunction vpx_filter_block1d16_h2_ssse3;
1306 filter8_1dfunction vpx_filter_block1d8_v2_ssse3;
1307 filter8_1dfunction vpx_filter_block1d8_h2_ssse3;
1308 filter8_1dfunction vpx_filter_block1d4_v2_ssse3;
1309 filter8_1dfunction vpx_filter_block1d4_h2_ssse3;
1310 #define vpx_filter_block1d16_v2_avx2 vpx_filter_block1d16_v2_ssse3
1311 #define vpx_filter_block1d16_h2_avx2 vpx_filter_block1d16_h2_ssse3
1312 #define vpx_filter_block1d8_v2_avx2 vpx_filter_block1d8_v2_ssse3
1313 #define vpx_filter_block1d8_h2_avx2 vpx_filter_block1d8_h2_ssse3
1314 #define vpx_filter_block1d4_v2_avx2 vpx_filter_block1d4_v2_ssse3
1315 #define vpx_filter_block1d4_h2_avx2 vpx_filter_block1d4_h2_ssse3
1316 filter8_1dfunction vpx_filter_block1d16_v2_avg_ssse3;
1317 filter8_1dfunction vpx_filter_block1d16_h2_avg_ssse3;
1318 filter8_1dfunction vpx_filter_block1d8_v2_avg_ssse3;
1319 filter8_1dfunction vpx_filter_block1d8_h2_avg_ssse3;
1320 filter8_1dfunction vpx_filter_block1d4_v2_avg_ssse3;
1321 filter8_1dfunction vpx_filter_block1d4_h2_avg_ssse3;
1322 #define vpx_filter_block1d16_v2_avg_avx2 vpx_filter_block1d16_v2_avg_ssse3
1323 #define vpx_filter_block1d16_h2_avg_avx2 vpx_filter_block1d16_h2_avg_ssse3
1324 #define vpx_filter_block1d8_v2_avg_avx2 vpx_filter_block1d8_v2_avg_ssse3
1325 #define vpx_filter_block1d8_h2_avg_avx2 vpx_filter_block1d8_h2_avg_ssse3
1326 #define vpx_filter_block1d4_v2_avg_avx2 vpx_filter_block1d4_v2_avg_ssse3
1327 #define vpx_filter_block1d4_h2_avg_avx2 vpx_filter_block1d4_h2_avg_ssse3
1328 
1329 #define vpx_filter_block1d16_v4_avg_avx2 vpx_filter_block1d16_v8_avg_avx2
1330 #define vpx_filter_block1d16_h4_avg_avx2 vpx_filter_block1d16_h8_avg_avx2
1331 #define vpx_filter_block1d8_v4_avg_avx2 vpx_filter_block1d8_v8_avg_avx2
1332 #define vpx_filter_block1d8_h4_avg_avx2 vpx_filter_block1d8_h8_avg_avx2
1333 #define vpx_filter_block1d4_v4_avg_avx2 vpx_filter_block1d4_v8_avg_avx2
1334 #define vpx_filter_block1d4_h4_avg_avx2 vpx_filter_block1d4_h8_avg_avx2
1335 // void vpx_convolve8_horiz_avx2(const uint8_t *src, ptrdiff_t src_stride,
1336 //                                uint8_t *dst, ptrdiff_t dst_stride,
1337 //                                const InterpKernel *filter, int x0_q4,
1338 //                                int32_t x_step_q4, int y0_q4, int y_step_q4,
1339 //                                int w, int h);
1340 // void vpx_convolve8_vert_avx2(const uint8_t *src, ptrdiff_t src_stride,
1341 //                               uint8_t *dst, ptrdiff_t dst_stride,
1342 //                               const InterpKernel *filter, int x0_q4,
1343 //                               int32_t x_step_q4, int y0_q4, int y_step_q4,
1344 //                               int w, int h);
1345 // void vpx_convolve8_avg_horiz_avx2(const uint8_t *src, ptrdiff_t src_stride,
1346 //                                    uint8_t *dst, ptrdiff_t dst_stride,
1347 //                                    const InterpKernel *filter, int x0_q4,
1348 //                                    int32_t x_step_q4, int y0_q4,
1349 //                                    int y_step_q4, int w, int h);
1350 // void vpx_convolve8_avg_vert_avx2(const uint8_t *src, ptrdiff_t src_stride,
1351 //                                   uint8_t *dst, ptrdiff_t dst_stride,
1352 //                                   const InterpKernel *filter, int x0_q4,
1353 //                                   int32_t x_step_q4, int y0_q4,
1354 //                                   int y_step_q4, int w, int h);
1355 FUN_CONV_1D(horiz, x0_q4, x_step_q4, h, src, , avx2, 0)
1356 FUN_CONV_1D(vert, y0_q4, y_step_q4, v, src - src_stride * (num_taps / 2 - 1), ,
1357             avx2, 0)
1358 FUN_CONV_1D(avg_horiz, x0_q4, x_step_q4, h, src, avg_, avx2, 1)
1359 FUN_CONV_1D(avg_vert, y0_q4, y_step_q4, v,
1360             src - src_stride * (num_taps / 2 - 1), avg_, avx2, 1)
1361 
1362 // void vpx_convolve8_avx2(const uint8_t *src, ptrdiff_t src_stride,
1363 //                          uint8_t *dst, ptrdiff_t dst_stride,
1364 //                          const InterpKernel *filter, int x0_q4,
1365 //                          int32_t x_step_q4, int y0_q4, int y_step_q4,
1366 //                          int w, int h);
1367 // void vpx_convolve8_avg_avx2(const uint8_t *src, ptrdiff_t src_stride,
1368 //                              uint8_t *dst, ptrdiff_t dst_stride,
1369 //                              const InterpKernel *filter, int x0_q4,
1370 //                              int32_t x_step_q4, int y0_q4, int y_step_q4,
1371 //                              int w, int h);
1372 FUN_CONV_2D(, avx2, 0)
1373 FUN_CONV_2D(avg_, avx2, 1)
1374 #endif  // HAVE_AX2 && HAVE_SSSE3
1375