looprestoration_tmpl.c (13140B)
1 /* 2 * Copyright © 2019, VideoLAN and dav1d authors 3 * Copyright © 2019, Michail Alvanos 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright notice, this 10 * list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "src/ppc/dav1d_types.h" 29 #include "src/ppc/looprestoration.h" 30 31 #if BITDEPTH == 8 32 33 #define REST_UNIT_STRIDE (400) 34 35 static inline i32x4 iclip_vec(i32x4 v, const i32x4 minv, const i32x4 maxv) { 36 v = vec_max(minv, v); 37 v = vec_min(maxv, v); 38 return v; 39 } 40 41 #define APPLY_FILTER_H(v, f, ssum1, ssum2) do { \ 42 i16x8 ktmp_u16_high = (i16x8) u8h_to_u16(v); \ 43 i16x8 ktmp_u16_low = (i16x8) u8l_to_u16(v); \ 44 ssum1 = vec_madd(ktmp_u16_high, f, ssum1); \ 45 ssum2 = vec_madd(ktmp_u16_low, f, ssum2); \ 46 } while (0) 47 48 static void wiener_filter_h_vsx(int32_t *hor_ptr, 49 uint8_t *tmp_ptr, 50 const int16_t filterh[8], 51 const int w, const int h) 52 { 53 const i32x4 zerov = vec_splats(0); 54 const i32x4 seven_vec = vec_splats(7); 55 const i32x4 bitdepth_added_vec = vec_splats(1 << 14); 56 const i32x4 round_bits_vec = vec_splats(3); 57 const i32x4 rounding_off_vec = vec_splats(1<<2); 58 const i32x4 clip_limit_v = vec_splats((1 << 13) - 1); 59 60 i16x8 filterhvall = vec_vsx_ld(0, filterh); 61 i16x8 filterhv0 = vec_splat( filterhvall, 0); 62 i16x8 filterhv1 = vec_splat( filterhvall, 1); 63 i16x8 filterhv2 = vec_splat( filterhvall, 2); 64 i16x8 filterhv3 = vec_splat( filterhvall, 3); 65 i16x8 filterhv4 = vec_splat( filterhvall, 4); 66 i16x8 filterhv5 = vec_splat( filterhvall, 5); 67 i16x8 filterhv6 = vec_splat( filterhvall, 6); 68 69 for (int j = 0; j < h + 6; j++) { 70 for (int i = 0; i < w; i+=16) { 71 i32x4 sum1 = bitdepth_added_vec; 72 i32x4 sum2 = bitdepth_added_vec; 73 i32x4 sum3 = bitdepth_added_vec; 74 i32x4 sum4 = bitdepth_added_vec; 75 76 u8x16 tmp_v0 = vec_ld(0, &tmp_ptr[i]); 77 u8x16 tmp_v7 = vec_ld(0, &tmp_ptr[i+16]); 78 79 u8x16 tmp_v1 = vec_sld( tmp_v7, tmp_v0, 15); 80 u8x16 tmp_v2 = vec_sld( tmp_v7, tmp_v0, 14); 81 u8x16 tmp_v3 = vec_sld( tmp_v7, tmp_v0, 13); 82 u8x16 tmp_v4 = vec_sld( tmp_v7, tmp_v0, 12); 83 u8x16 tmp_v5 = vec_sld( tmp_v7, tmp_v0, 11); 84 u8x16 tmp_v6 = vec_sld( tmp_v7, tmp_v0, 10); 85 86 u16x8 tmp_u16_high = u8h_to_u16(tmp_v3); 87 u16x8 tmp_u16_low = u8l_to_u16(tmp_v3); 88 89 i32x4 tmp_expanded1 = i16h_to_i32(tmp_u16_high); 90 i32x4 tmp_expanded2 = i16l_to_i32(tmp_u16_high); 91 i32x4 tmp_expanded3 = i16h_to_i32(tmp_u16_low); 92 i32x4 tmp_expanded4 = i16l_to_i32(tmp_u16_low); 93 94 i16x8 ssum1 = (i16x8) zerov; 95 i16x8 ssum2 = (i16x8) zerov; 96 97 APPLY_FILTER_H(tmp_v0, filterhv0, ssum1, ssum2); 98 APPLY_FILTER_H(tmp_v1, filterhv1, ssum1, ssum2); 99 APPLY_FILTER_H(tmp_v2, filterhv2, ssum1, ssum2); 100 APPLY_FILTER_H(tmp_v3, filterhv3, ssum1, ssum2); 101 APPLY_FILTER_H(tmp_v4, filterhv4, ssum1, ssum2); 102 APPLY_FILTER_H(tmp_v5, filterhv5, ssum1, ssum2); 103 APPLY_FILTER_H(tmp_v6, filterhv6, ssum1, ssum2); 104 105 sum1 += i16h_to_i32(ssum1) + (tmp_expanded1 << seven_vec); 106 sum2 += i16l_to_i32(ssum1) + (tmp_expanded2 << seven_vec); 107 sum3 += i16h_to_i32(ssum2) + (tmp_expanded3 << seven_vec); 108 sum4 += i16l_to_i32(ssum2) + (tmp_expanded4 << seven_vec); 109 110 sum1 = (sum1 + rounding_off_vec) >> round_bits_vec; 111 sum2 = (sum2 + rounding_off_vec) >> round_bits_vec; 112 sum3 = (sum3 + rounding_off_vec) >> round_bits_vec; 113 sum4 = (sum4 + rounding_off_vec) >> round_bits_vec; 114 115 sum1 = iclip_vec(sum1, zerov, clip_limit_v); 116 sum2 = iclip_vec(sum2, zerov, clip_limit_v); 117 sum3 = iclip_vec(sum3, zerov, clip_limit_v); 118 sum4 = iclip_vec(sum4, zerov, clip_limit_v); 119 120 vec_st(sum1, 0, &hor_ptr[i]); 121 vec_st(sum2, 16, &hor_ptr[i]); 122 vec_st(sum3, 32, &hor_ptr[i]); 123 vec_st(sum4, 48, &hor_ptr[i]); 124 } 125 tmp_ptr += REST_UNIT_STRIDE; 126 hor_ptr += REST_UNIT_STRIDE; 127 } 128 } 129 130 static inline i16x8 iclip_u8_vec(i16x8 v) { 131 const i16x8 zerov = vec_splats((int16_t)0); 132 const i16x8 maxv = vec_splats((int16_t)255); 133 v = vec_max(zerov, v); 134 v = vec_min(maxv, v); 135 return v; 136 } 137 138 #define APPLY_FILTER_V(index, f) do { \ 139 i32x4 v1 = vec_ld( 0, &hor[(j + index) * REST_UNIT_STRIDE + i]); \ 140 i32x4 v2 = vec_ld(16, &hor[(j + index) * REST_UNIT_STRIDE + i]); \ 141 i32x4 v3 = vec_ld(32, &hor[(j + index) * REST_UNIT_STRIDE + i]); \ 142 i32x4 v4 = vec_ld(48, &hor[(j + index) * REST_UNIT_STRIDE + i]); \ 143 sum1 = sum1 + v1 * f; \ 144 sum2 = sum2 + v2 * f; \ 145 sum3 = sum3 + v3 * f; \ 146 sum4 = sum4 + v4 * f; \ 147 } while (0) 148 149 #define LOAD_AND_APPLY_FILTER_V(sumpixelv, hor) do { \ 150 i32x4 sum1 = round_vec; \ 151 i32x4 sum2 = round_vec; \ 152 i32x4 sum3 = round_vec; \ 153 i32x4 sum4 = round_vec; \ 154 APPLY_FILTER_V(0, filterv0); \ 155 APPLY_FILTER_V(1, filterv1); \ 156 APPLY_FILTER_V(2, filterv2); \ 157 APPLY_FILTER_V(3, filterv3); \ 158 APPLY_FILTER_V(4, filterv4); \ 159 APPLY_FILTER_V(5, filterv5); \ 160 APPLY_FILTER_V(6, filterv6); \ 161 sum1 = sum1 >> round_bits_vec; \ 162 sum2 = sum2 >> round_bits_vec; \ 163 sum3 = sum3 >> round_bits_vec; \ 164 sum4 = sum4 >> round_bits_vec; \ 165 i16x8 sum_short_packed_1 = (i16x8) vec_pack(sum1, sum2); \ 166 i16x8 sum_short_packed_2 = (i16x8) vec_pack(sum3, sum4); \ 167 sum_short_packed_1 = iclip_u8_vec(sum_short_packed_1); \ 168 sum_short_packed_2 = iclip_u8_vec(sum_short_packed_2); \ 169 sum_pixel = (u8x16) vec_pack(sum_short_packed_1, sum_short_packed_2); \ 170 } while (0) 171 172 static inline void wiener_filter_v_vsx(uint8_t *p, 173 const ptrdiff_t stride, 174 const int32_t *hor, 175 const int16_t filterv[8], 176 const int w, const int h) 177 { 178 const i32x4 round_bits_vec = vec_splats(11); 179 const i32x4 round_vec = vec_splats((1 << 10) - (1 << 18)); 180 181 i32x4 filterv0 = vec_splats((int32_t) filterv[0]); 182 i32x4 filterv1 = vec_splats((int32_t) filterv[1]); 183 i32x4 filterv2 = vec_splats((int32_t) filterv[2]); 184 i32x4 filterv3 = vec_splats((int32_t) filterv[3]); 185 i32x4 filterv4 = vec_splats((int32_t) filterv[4]); 186 i32x4 filterv5 = vec_splats((int32_t) filterv[5]); 187 i32x4 filterv6 = vec_splats((int32_t) filterv[6]); 188 189 for (int j = 0; j < h; j++) { 190 for (int i = 0; i <(w-w%16); i += 16) { 191 u8x16 sum_pixel; 192 LOAD_AND_APPLY_FILTER_V(sum_pixel, hor); 193 vec_vsx_st(sum_pixel, 0, &p[j * PXSTRIDE(stride) + i]); 194 } 195 // remaining loop 196 if (w & 0xf){ 197 int i=w-w%16; 198 ALIGN_STK_16(uint8_t, tmp_out, 16,); 199 u8x16 sum_pixel; 200 201 LOAD_AND_APPLY_FILTER_V(sum_pixel, hor); 202 vec_vsx_st(sum_pixel, 0, tmp_out); 203 204 for (int k=0; i<w; i++, k++) { 205 p[j * PXSTRIDE(stride) + i] = tmp_out[k]; 206 } 207 } 208 } 209 } 210 211 static inline void padding(uint8_t *dst, const uint8_t *p, 212 const ptrdiff_t stride, const uint8_t (*left)[4], 213 const uint8_t *lpf, int unit_w, const int stripe_h, 214 const enum LrEdgeFlags edges) 215 { 216 const int have_left = !!(edges & LR_HAVE_LEFT); 217 const int have_right = !!(edges & LR_HAVE_RIGHT); 218 219 // Copy more pixels if we don't have to pad them 220 unit_w += 3 * have_left + 3 * have_right; 221 uint8_t *dst_l = dst + 3 * !have_left; 222 p -= 3 * have_left; 223 lpf -= 3 * have_left; 224 225 if (edges & LR_HAVE_TOP) { 226 // Copy previous loop filtered rows 227 const uint8_t *const above_1 = lpf; 228 const uint8_t *const above_2 = above_1 + PXSTRIDE(stride); 229 pixel_copy(dst_l, above_1, unit_w); 230 pixel_copy(dst_l + REST_UNIT_STRIDE, above_1, unit_w); 231 pixel_copy(dst_l + 2 * REST_UNIT_STRIDE, above_2, unit_w); 232 } else { 233 // Pad with first row 234 pixel_copy(dst_l, p, unit_w); 235 pixel_copy(dst_l + REST_UNIT_STRIDE, p, unit_w); 236 pixel_copy(dst_l + 2 * REST_UNIT_STRIDE, p, unit_w); 237 if (have_left) { 238 pixel_copy(dst_l, &left[0][1], 3); 239 pixel_copy(dst_l + REST_UNIT_STRIDE, &left[0][1], 3); 240 pixel_copy(dst_l + 2 * REST_UNIT_STRIDE, &left[0][1], 3); 241 } 242 } 243 244 uint8_t *dst_tl = dst_l + 3 * REST_UNIT_STRIDE; 245 if (edges & LR_HAVE_BOTTOM) { 246 // Copy next loop filtered rows 247 const uint8_t *const below_1 = lpf + 6 * PXSTRIDE(stride); 248 const uint8_t *const below_2 = below_1 + PXSTRIDE(stride); 249 pixel_copy(dst_tl + stripe_h * REST_UNIT_STRIDE, below_1, unit_w); 250 pixel_copy(dst_tl + (stripe_h + 1) * REST_UNIT_STRIDE, below_2, unit_w); 251 pixel_copy(dst_tl + (stripe_h + 2) * REST_UNIT_STRIDE, below_2, unit_w); 252 } else { 253 // Pad with last row 254 const uint8_t *const src = p + (stripe_h - 1) * PXSTRIDE(stride); 255 pixel_copy(dst_tl + stripe_h * REST_UNIT_STRIDE, src, unit_w); 256 pixel_copy(dst_tl + (stripe_h + 1) * REST_UNIT_STRIDE, src, unit_w); 257 pixel_copy(dst_tl + (stripe_h + 2) * REST_UNIT_STRIDE, src, unit_w); 258 if (have_left) { 259 pixel_copy(dst_tl + stripe_h * REST_UNIT_STRIDE, &left[stripe_h - 1][1], 3); 260 pixel_copy(dst_tl + (stripe_h + 1) * REST_UNIT_STRIDE, &left[stripe_h - 1][1], 3); 261 pixel_copy(dst_tl + (stripe_h + 2) * REST_UNIT_STRIDE, &left[stripe_h - 1][1], 3); 262 } 263 } 264 265 // Inner UNIT_WxSTRIPE_H 266 for (int j = 0; j < stripe_h; j++) { 267 pixel_copy(dst_tl + 3 * have_left, p + 3 * have_left, unit_w - 3 * have_left); 268 dst_tl += REST_UNIT_STRIDE; 269 p += PXSTRIDE(stride); 270 } 271 272 if (!have_right) { 273 uint8_t *pad = dst_l + unit_w; 274 uint8_t *row_last = &dst_l[unit_w - 1]; 275 // Pad 3x(STRIPE_H+6) with last column 276 for (int j = 0; j < stripe_h + 6; j++) { 277 pixel_set(pad, *row_last, 3); 278 pad += REST_UNIT_STRIDE; 279 row_last += REST_UNIT_STRIDE; 280 } 281 } 282 283 if (!have_left) { 284 // Pad 3x(STRIPE_H+6) with first column 285 for (int j = 0; j < stripe_h + 6; j++) { 286 pixel_set(dst, *dst_l, 3); 287 dst += REST_UNIT_STRIDE; 288 dst_l += REST_UNIT_STRIDE; 289 } 290 } else { 291 dst += 3 * REST_UNIT_STRIDE; 292 for (int j = 0; j < stripe_h; j++) { 293 pixel_copy(dst, &left[j][1], 3); 294 dst += REST_UNIT_STRIDE; 295 } 296 } 297 } 298 299 // FIXME Could split into luma and chroma specific functions, 300 // (since first and last tops are always 0 for chroma) 301 // FIXME Could implement a version that requires less temporary memory 302 // (should be possible to implement with only 6 rows of temp storage) 303 void dav1d_wiener_filter_vsx(uint8_t *p, const ptrdiff_t stride, 304 const uint8_t (*const left)[4], 305 const uint8_t *lpf, 306 const int w, const int h, 307 const LooprestorationParams *const params, 308 const enum LrEdgeFlags edges) 309 { 310 const int16_t (*const filter)[8] = params->filter; 311 312 // Wiener filtering is applied to a maximum stripe height of 64 + 3 pixels 313 // of padding above and below 314 ALIGN_STK_16(uint8_t, tmp, 70 /*(64 + 3 + 3)*/ * REST_UNIT_STRIDE,); 315 padding(tmp, p, stride, left, lpf, w, h, edges); 316 ALIGN_STK_16(int32_t, hor, 70 /*(64 + 3 + 3)*/ * REST_UNIT_STRIDE + 64,); 317 318 wiener_filter_h_vsx(hor, tmp, filter[0], w, h); 319 wiener_filter_v_vsx(p, stride, hor, filter[1], w, h); 320 } 321 #endif