xref: /aosp_15_r20/external/libgav1/src/dsp/x86/super_res_sse4.cc (revision 095378508e87ed692bf8dfeb34008b65b3735891)
1 // Copyright 2020 The libgav1 Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/dsp/super_res.h"
16 #include "src/utils/cpu.h"
17 
18 #if LIBGAV1_TARGETING_SSE4_1
19 
20 #include <smmintrin.h>
21 
22 #include "src/dsp/constants.h"
23 #include "src/dsp/dsp.h"
24 #include "src/dsp/x86/common_sse4.h"
25 #include "src/dsp/x86/transpose_sse4.h"
26 #include "src/utils/common.h"
27 #include "src/utils/constants.h"
28 
29 namespace libgav1 {
30 namespace dsp {
31 namespace low_bitdepth {
32 namespace {
33 
34 // Upscale_Filter as defined in AV1 Section 7.16
35 // Negative to make them fit in 8-bit.
36 alignas(16) const int8_t
37     kNegativeUpscaleFilter[kSuperResFilterShifts][kSuperResFilterTaps] = {
38         {0, 0, 0, -128, 0, 0, 0, 0},       {0, 0, 1, -128, -2, 1, 0, 0},
39         {0, -1, 3, -127, -4, 2, -1, 0},    {0, -1, 4, -127, -6, 3, -1, 0},
40         {0, -2, 6, -126, -8, 3, -1, 0},    {0, -2, 7, -125, -11, 4, -1, 0},
41         {1, -2, 8, -125, -13, 5, -2, 0},   {1, -3, 9, -124, -15, 6, -2, 0},
42         {1, -3, 10, -123, -18, 6, -2, 1},  {1, -3, 11, -122, -20, 7, -3, 1},
43         {1, -4, 12, -121, -22, 8, -3, 1},  {1, -4, 13, -120, -25, 9, -3, 1},
44         {1, -4, 14, -118, -28, 9, -3, 1},  {1, -4, 15, -117, -30, 10, -4, 1},
45         {1, -5, 16, -116, -32, 11, -4, 1}, {1, -5, 16, -114, -35, 12, -4, 1},
46         {1, -5, 17, -112, -38, 12, -4, 1}, {1, -5, 18, -111, -40, 13, -5, 1},
47         {1, -5, 18, -109, -43, 14, -5, 1}, {1, -6, 19, -107, -45, 14, -5, 1},
48         {1, -6, 19, -105, -48, 15, -5, 1}, {1, -6, 19, -103, -51, 16, -5, 1},
49         {1, -6, 20, -101, -53, 16, -6, 1}, {1, -6, 20, -99, -56, 17, -6, 1},
50         {1, -6, 20, -97, -58, 17, -6, 1},  {1, -6, 20, -95, -61, 18, -6, 1},
51         {2, -7, 20, -93, -64, 18, -6, 2},  {2, -7, 20, -91, -66, 19, -6, 1},
52         {2, -7, 20, -88, -69, 19, -6, 1},  {2, -7, 20, -86, -71, 19, -6, 1},
53         {2, -7, 20, -84, -74, 20, -7, 2},  {2, -7, 20, -81, -76, 20, -7, 1},
54         {2, -7, 20, -79, -79, 20, -7, 2},  {1, -7, 20, -76, -81, 20, -7, 2},
55         {2, -7, 20, -74, -84, 20, -7, 2},  {1, -6, 19, -71, -86, 20, -7, 2},
56         {1, -6, 19, -69, -88, 20, -7, 2},  {1, -6, 19, -66, -91, 20, -7, 2},
57         {2, -6, 18, -64, -93, 20, -7, 2},  {1, -6, 18, -61, -95, 20, -6, 1},
58         {1, -6, 17, -58, -97, 20, -6, 1},  {1, -6, 17, -56, -99, 20, -6, 1},
59         {1, -6, 16, -53, -101, 20, -6, 1}, {1, -5, 16, -51, -103, 19, -6, 1},
60         {1, -5, 15, -48, -105, 19, -6, 1}, {1, -5, 14, -45, -107, 19, -6, 1},
61         {1, -5, 14, -43, -109, 18, -5, 1}, {1, -5, 13, -40, -111, 18, -5, 1},
62         {1, -4, 12, -38, -112, 17, -5, 1}, {1, -4, 12, -35, -114, 16, -5, 1},
63         {1, -4, 11, -32, -116, 16, -5, 1}, {1, -4, 10, -30, -117, 15, -4, 1},
64         {1, -3, 9, -28, -118, 14, -4, 1},  {1, -3, 9, -25, -120, 13, -4, 1},
65         {1, -3, 8, -22, -121, 12, -4, 1},  {1, -3, 7, -20, -122, 11, -3, 1},
66         {1, -2, 6, -18, -123, 10, -3, 1},  {0, -2, 6, -15, -124, 9, -3, 1},
67         {0, -2, 5, -13, -125, 8, -2, 1},   {0, -1, 4, -11, -125, 7, -2, 0},
68         {0, -1, 3, -8, -126, 6, -2, 0},    {0, -1, 3, -6, -127, 4, -1, 0},
69         {0, -1, 2, -4, -127, 3, -1, 0},    {0, 0, 1, -2, -128, 1, 0, 0},
70 };
71 
SuperResCoefficients_SSE4_1(const int upscaled_width,const int initial_subpixel_x,const int step,void * const coefficients)72 void SuperResCoefficients_SSE4_1(const int upscaled_width,
73                                  const int initial_subpixel_x, const int step,
74                                  void* const coefficients) {
75   auto* dst = static_cast<uint8_t*>(coefficients);
76   int subpixel_x = initial_subpixel_x;
77   int x = RightShiftWithCeiling(upscaled_width, 4);
78   do {
79     for (int i = 0; i < 8; ++i, dst += 16) {
80       int remainder = subpixel_x & kSuperResScaleMask;
81       __m128i filter =
82           LoadLo8(kNegativeUpscaleFilter[remainder >> kSuperResExtraBits]);
83       subpixel_x += step;
84       remainder = subpixel_x & kSuperResScaleMask;
85       filter = LoadHi8(filter,
86                        kNegativeUpscaleFilter[remainder >> kSuperResExtraBits]);
87       subpixel_x += step;
88       StoreAligned16(dst, filter);
89     }
90   } while (--x != 0);
91 }
92 
SuperRes_SSE4_1(const void * LIBGAV1_RESTRICT const coefficients,void * LIBGAV1_RESTRICT const source,const ptrdiff_t source_stride,const int height,const int downscaled_width,const int upscaled_width,const int initial_subpixel_x,const int step,void * LIBGAV1_RESTRICT const dest,const ptrdiff_t dest_stride)93 void SuperRes_SSE4_1(const void* LIBGAV1_RESTRICT const coefficients,
94                      void* LIBGAV1_RESTRICT const source,
95                      const ptrdiff_t source_stride, const int height,
96                      const int downscaled_width, const int upscaled_width,
97                      const int initial_subpixel_x, const int step,
98                      void* LIBGAV1_RESTRICT const dest,
99                      const ptrdiff_t dest_stride) {
100   auto* src = static_cast<uint8_t*>(source) - DivideBy2(kSuperResFilterTaps);
101   auto* dst = static_cast<uint8_t*>(dest);
102   int y = height;
103   do {
104     const auto* filter = static_cast<const uint8_t*>(coefficients);
105     uint8_t* dst_ptr = dst;
106     ExtendLine<uint8_t>(src + DivideBy2(kSuperResFilterTaps), downscaled_width,
107                         kSuperResHorizontalBorder, kSuperResHorizontalBorder);
108     int subpixel_x = initial_subpixel_x;
109     // The below code calculates up to 15 extra upscaled pixels which will
110     // over-read up to 15 downscaled pixels in the end of each row.
111     // kSuperResHorizontalPadding protects this behavior from segmentation
112     // faults and threading issues.
113     int x = RightShiftWithCeiling(upscaled_width, 4);
114     do {
115       __m128i weighted_src[8];
116       for (int i = 0; i < 8; ++i, filter += 16) {
117         // TODO(b/178652672): Remove Msan loads when hadd bug is resolved.
118         // It's fine to write uninitialized bytes outside the frame, but the
119         // inside-frame pixels are incorrectly labeled uninitialized if
120         // uninitialized values go through the hadd intrinsics.
121         // |src| is offset 4 pixels to the left, and there are 4 extended border
122         // pixels, so a difference of 0 from |downscaled_width| indicates 8 good
123         // bytes. A difference of 1 indicates 7 good bytes.
124         const int msan_bytes_lo =
125             (subpixel_x >> kSuperResScaleBits) - downscaled_width;
126         __m128i s =
127             LoadLo8Msan(&src[subpixel_x >> kSuperResScaleBits], msan_bytes_lo);
128         subpixel_x += step;
129         const int msan_bytes_hi =
130             (subpixel_x >> kSuperResScaleBits) - downscaled_width;
131         s = LoadHi8Msan(s, &src[subpixel_x >> kSuperResScaleBits],
132                         msan_bytes_hi);
133         subpixel_x += step;
134         const __m128i f = LoadAligned16(filter);
135         weighted_src[i] = _mm_maddubs_epi16(s, f);
136       }
137 
138       __m128i a[4];
139       a[0] = _mm_hadd_epi16(weighted_src[0], weighted_src[1]);
140       a[1] = _mm_hadd_epi16(weighted_src[2], weighted_src[3]);
141       a[2] = _mm_hadd_epi16(weighted_src[4], weighted_src[5]);
142       a[3] = _mm_hadd_epi16(weighted_src[6], weighted_src[7]);
143       Transpose2x16_U16(a, a);
144       a[0] = _mm_adds_epi16(a[0], a[1]);
145       a[1] = _mm_adds_epi16(a[2], a[3]);
146       const __m128i rounding = _mm_set1_epi16(1 << (kFilterBits - 1));
147       a[0] = _mm_subs_epi16(rounding, a[0]);
148       a[1] = _mm_subs_epi16(rounding, a[1]);
149       a[0] = _mm_srai_epi16(a[0], kFilterBits);
150       a[1] = _mm_srai_epi16(a[1], kFilterBits);
151       StoreAligned16(dst_ptr, _mm_packus_epi16(a[0], a[1]));
152       dst_ptr += 16;
153     } while (--x != 0);
154     src += source_stride;
155     dst += dest_stride;
156   } while (--y != 0);
157 }
158 
Init8bpp()159 void Init8bpp() {
160   Dsp* dsp = dsp_internal::GetWritableDspTable(kBitdepth8);
161 #if DSP_ENABLED_8BPP_SSE4_1(SuperResCoefficients)
162   dsp->super_res_coefficients = SuperResCoefficients_SSE4_1;
163 #endif  // DSP_ENABLED_8BPP_SSE4_1(SuperResCoefficients)
164 #if DSP_ENABLED_8BPP_SSE4_1(SuperRes)
165   dsp->super_res = SuperRes_SSE4_1;
166 #endif  // DSP_ENABLED_8BPP_SSE4_1(SuperRes)
167 }
168 
169 }  // namespace
170 }  // namespace low_bitdepth
171 
172 //------------------------------------------------------------------------------
173 #if LIBGAV1_MAX_BITDEPTH >= 10
174 namespace high_bitdepth {
175 namespace {
176 
177 // Upscale_Filter as defined in AV1 Section 7.16
178 alignas(16) const int16_t
179     kUpscaleFilter[kSuperResFilterShifts][kSuperResFilterTaps] = {
180         {0, 0, 0, 128, 0, 0, 0, 0},        {0, 0, -1, 128, 2, -1, 0, 0},
181         {0, 1, -3, 127, 4, -2, 1, 0},      {0, 1, -4, 127, 6, -3, 1, 0},
182         {0, 2, -6, 126, 8, -3, 1, 0},      {0, 2, -7, 125, 11, -4, 1, 0},
183         {-1, 2, -8, 125, 13, -5, 2, 0},    {-1, 3, -9, 124, 15, -6, 2, 0},
184         {-1, 3, -10, 123, 18, -6, 2, -1},  {-1, 3, -11, 122, 20, -7, 3, -1},
185         {-1, 4, -12, 121, 22, -8, 3, -1},  {-1, 4, -13, 120, 25, -9, 3, -1},
186         {-1, 4, -14, 118, 28, -9, 3, -1},  {-1, 4, -15, 117, 30, -10, 4, -1},
187         {-1, 5, -16, 116, 32, -11, 4, -1}, {-1, 5, -16, 114, 35, -12, 4, -1},
188         {-1, 5, -17, 112, 38, -12, 4, -1}, {-1, 5, -18, 111, 40, -13, 5, -1},
189         {-1, 5, -18, 109, 43, -14, 5, -1}, {-1, 6, -19, 107, 45, -14, 5, -1},
190         {-1, 6, -19, 105, 48, -15, 5, -1}, {-1, 6, -19, 103, 51, -16, 5, -1},
191         {-1, 6, -20, 101, 53, -16, 6, -1}, {-1, 6, -20, 99, 56, -17, 6, -1},
192         {-1, 6, -20, 97, 58, -17, 6, -1},  {-1, 6, -20, 95, 61, -18, 6, -1},
193         {-2, 7, -20, 93, 64, -18, 6, -2},  {-2, 7, -20, 91, 66, -19, 6, -1},
194         {-2, 7, -20, 88, 69, -19, 6, -1},  {-2, 7, -20, 86, 71, -19, 6, -1},
195         {-2, 7, -20, 84, 74, -20, 7, -2},  {-2, 7, -20, 81, 76, -20, 7, -1},
196         {-2, 7, -20, 79, 79, -20, 7, -2},  {-1, 7, -20, 76, 81, -20, 7, -2},
197         {-2, 7, -20, 74, 84, -20, 7, -2},  {-1, 6, -19, 71, 86, -20, 7, -2},
198         {-1, 6, -19, 69, 88, -20, 7, -2},  {-1, 6, -19, 66, 91, -20, 7, -2},
199         {-2, 6, -18, 64, 93, -20, 7, -2},  {-1, 6, -18, 61, 95, -20, 6, -1},
200         {-1, 6, -17, 58, 97, -20, 6, -1},  {-1, 6, -17, 56, 99, -20, 6, -1},
201         {-1, 6, -16, 53, 101, -20, 6, -1}, {-1, 5, -16, 51, 103, -19, 6, -1},
202         {-1, 5, -15, 48, 105, -19, 6, -1}, {-1, 5, -14, 45, 107, -19, 6, -1},
203         {-1, 5, -14, 43, 109, -18, 5, -1}, {-1, 5, -13, 40, 111, -18, 5, -1},
204         {-1, 4, -12, 38, 112, -17, 5, -1}, {-1, 4, -12, 35, 114, -16, 5, -1},
205         {-1, 4, -11, 32, 116, -16, 5, -1}, {-1, 4, -10, 30, 117, -15, 4, -1},
206         {-1, 3, -9, 28, 118, -14, 4, -1},  {-1, 3, -9, 25, 120, -13, 4, -1},
207         {-1, 3, -8, 22, 121, -12, 4, -1},  {-1, 3, -7, 20, 122, -11, 3, -1},
208         {-1, 2, -6, 18, 123, -10, 3, -1},  {0, 2, -6, 15, 124, -9, 3, -1},
209         {0, 2, -5, 13, 125, -8, 2, -1},    {0, 1, -4, 11, 125, -7, 2, 0},
210         {0, 1, -3, 8, 126, -6, 2, 0},      {0, 1, -3, 6, 127, -4, 1, 0},
211         {0, 1, -2, 4, 127, -3, 1, 0},      {0, 0, -1, 2, 128, -1, 0, 0},
212 };
213 
SuperResCoefficients_SSE4_1(const int upscaled_width,const int initial_subpixel_x,const int step,void * const coefficients)214 void SuperResCoefficients_SSE4_1(const int upscaled_width,
215                                  const int initial_subpixel_x, const int step,
216                                  void* const coefficients) {
217   auto* dst = static_cast<uint16_t*>(coefficients);
218   int subpixel_x = initial_subpixel_x;
219   int x = RightShiftWithCeiling(upscaled_width, 3);
220   do {
221     for (int i = 0; i < 8; ++i, dst += 8) {
222       int remainder = subpixel_x & kSuperResScaleMask;
223       __m128i filter =
224           LoadAligned16(kUpscaleFilter[remainder >> kSuperResExtraBits]);
225       subpixel_x += step;
226       StoreAligned16(dst, filter);
227     }
228   } while (--x != 0);
229 }
230 
231 template <int bitdepth>
SuperRes_SSE4_1(const void * LIBGAV1_RESTRICT const coefficients,void * LIBGAV1_RESTRICT const source,const ptrdiff_t source_stride,const int height,const int downscaled_width,const int upscaled_width,const int initial_subpixel_x,const int step,void * LIBGAV1_RESTRICT const dest,const ptrdiff_t dest_stride)232 void SuperRes_SSE4_1(const void* LIBGAV1_RESTRICT const coefficients,
233                      void* LIBGAV1_RESTRICT const source,
234                      const ptrdiff_t source_stride, const int height,
235                      const int downscaled_width, const int upscaled_width,
236                      const int initial_subpixel_x, const int step,
237                      void* LIBGAV1_RESTRICT const dest,
238                      const ptrdiff_t dest_stride) {
239   auto* src = static_cast<uint16_t*>(source) - DivideBy2(kSuperResFilterTaps);
240   auto* dst = static_cast<uint16_t*>(dest);
241   int y = height;
242   do {
243     const auto* filter = static_cast<const uint16_t*>(coefficients);
244     uint16_t* dst_ptr = dst;
245     ExtendLine<uint16_t>(src + DivideBy2(kSuperResFilterTaps), downscaled_width,
246                          kSuperResHorizontalBorder, kSuperResHorizontalPadding);
247     int subpixel_x = initial_subpixel_x;
248     // The below code calculates up to 7 extra upscaled
249     // pixels which will over-read up to 7 downscaled pixels in the end of each
250     // row. kSuperResHorizontalPadding accounts for this.
251     int x = RightShiftWithCeiling(upscaled_width, 3);
252     do {
253       __m128i weighted_src[8];
254       for (int i = 0; i < 8; ++i, filter += 8) {
255         const __m128i s =
256             LoadUnaligned16(&src[subpixel_x >> kSuperResScaleBits]);
257         subpixel_x += step;
258         const __m128i f = LoadAligned16(filter);
259         weighted_src[i] = _mm_madd_epi16(s, f);
260       }
261 
262       __m128i a[4];
263       a[0] = _mm_hadd_epi32(weighted_src[0], weighted_src[1]);
264       a[1] = _mm_hadd_epi32(weighted_src[2], weighted_src[3]);
265       a[2] = _mm_hadd_epi32(weighted_src[4], weighted_src[5]);
266       a[3] = _mm_hadd_epi32(weighted_src[6], weighted_src[7]);
267 
268       a[0] = _mm_hadd_epi32(a[0], a[1]);
269       a[1] = _mm_hadd_epi32(a[2], a[3]);
270       a[0] = RightShiftWithRounding_S32(a[0], kFilterBits);
271       a[1] = RightShiftWithRounding_S32(a[1], kFilterBits);
272 
273       // Clip the values at (1 << bd) - 1
274       const __m128i clipped_16 = _mm_min_epi16(
275           _mm_packus_epi32(a[0], a[1]), _mm_set1_epi16((1 << bitdepth) - 1));
276       StoreAligned16(dst_ptr, clipped_16);
277       dst_ptr += 8;
278     } while (--x != 0);
279     src += source_stride;
280     dst += dest_stride;
281   } while (--y != 0);
282 }
283 
Init10bpp()284 void Init10bpp() {
285   Dsp* dsp = dsp_internal::GetWritableDspTable(kBitdepth10);
286   assert(dsp != nullptr);
287   static_cast<void>(dsp);
288 #if DSP_ENABLED_10BPP_SSE4_1(SuperResCoefficients)
289   dsp->super_res_coefficients = SuperResCoefficients_SSE4_1;
290 #else
291   static_cast<void>(SuperResCoefficients_SSE4_1);
292 #endif
293 #if DSP_ENABLED_10BPP_SSE4_1(SuperRes)
294   dsp->super_res = SuperRes_SSE4_1<10>;
295 #else
296   static_cast<void>(SuperRes_SSE4_1);
297 #endif
298 }
299 
300 }  // namespace
301 }  // namespace high_bitdepth
302 #endif  // LIBGAV1_MAX_BITDEPTH >= 10
303 
SuperResInit_SSE4_1()304 void SuperResInit_SSE4_1() {
305   low_bitdepth::Init8bpp();
306 #if LIBGAV1_MAX_BITDEPTH >= 10
307   high_bitdepth::Init10bpp();
308 #endif
309 }
310 
311 }  // namespace dsp
312 }  // namespace libgav1
313 
314 #else   // !LIBGAV1_TARGETING_SSE4_1
315 
316 namespace libgav1 {
317 namespace dsp {
318 
SuperResInit_SSE4_1()319 void SuperResInit_SSE4_1() {}
320 
321 }  // namespace dsp
322 }  // namespace libgav1
323 #endif  // LIBGAV1_TARGETING_SSE4_1
324