1 ; 2 ; Copyright (c) 2013 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 EXPORT |vpx_lpf_horizontal_8_neon| 12 EXPORT |vpx_lpf_vertical_8_neon| 13 ARM 14 15 AREA ||.text||, CODE, READONLY, ALIGN=2 16 17 ; Currently vpx only works on iterations 8 at a time. The vp8 loop filter 18 ; works on 16 iterations at a time. 19 ; TODO(fgalligan): See about removing the count code as this function is only 20 ; called with a count of 1. 21 ; 22 ; void vpx_lpf_horizontal_8_neon(uint8_t *s, int p, 23 ; const uint8_t *blimit, 24 ; const uint8_t *limit, 25 ; const uint8_t *thresh, 26 ; int count) 27 ; r0 uint8_t *s, 28 ; r1 int p, /* pitch */ 29 ; r2 const uint8_t *blimit, 30 ; r3 const uint8_t *limit, 31 ; sp const uint8_t *thresh, 32 ; sp+4 int count 33 |vpx_lpf_horizontal_8_neon| PROC 34 push {r4-r5, lr} 35 36 vld1.8 {d0[]}, [r2] ; duplicate *blimit 37 ldr r12, [sp, #16] ; load count 38 ldr r2, [sp, #12] ; load thresh 39 add r1, r1, r1 ; double pitch 40 41 cmp r12, #0 42 beq end_vpx_mblf_h_edge 43 44 vld1.8 {d1[]}, [r3] ; duplicate *limit 45 vld1.8 {d2[]}, [r2] ; duplicate *thresh 46 47 count_mblf_h_loop 48 sub r3, r0, r1, lsl #1 ; move src pointer down by 4 lines 49 add r2, r3, r1, lsr #1 ; set to 3 lines down 50 51 vld1.u8 {d3}, [r3@64], r1 ; p3 52 vld1.u8 {d4}, [r2@64], r1 ; p2 53 vld1.u8 {d5}, [r3@64], r1 ; p1 54 vld1.u8 {d6}, [r2@64], r1 ; p0 55 vld1.u8 {d7}, [r3@64], r1 ; q0 56 vld1.u8 {d16}, [r2@64], r1 ; q1 57 vld1.u8 {d17}, [r3@64] ; q2 58 vld1.u8 {d18}, [r2@64], r1 ; q3 59 60 sub r3, r3, r1, lsl #1 61 sub r2, r2, r1, lsl #2 62 63 bl vpx_mbloop_filter_neon 64 65 vst1.u8 {d0}, [r2@64], r1 ; store op2 66 vst1.u8 {d1}, [r3@64], r1 ; store op1 67 vst1.u8 {d2}, [r2@64], r1 ; store op0 68 vst1.u8 {d3}, [r3@64], r1 ; store oq0 69 vst1.u8 {d4}, [r2@64], r1 ; store oq1 70 vst1.u8 {d5}, [r3@64], r1 ; store oq2 71 72 add r0, r0, #8 73 subs r12, r12, #1 74 bne count_mblf_h_loop 75 76 end_vpx_mblf_h_edge 77 pop {r4-r5, pc} 78 79 ENDP ; |vpx_lpf_horizontal_8_neon| 80 81 ; void vpx_lpf_vertical_8_neon(uint8_t *s, 82 ; int pitch, 83 ; const uint8_t *blimit, 84 ; const uint8_t *limit, 85 ; const uint8_t *thresh, 86 ; int count) 87 ; 88 ; r0 uint8_t *s, 89 ; r1 int pitch, 90 ; r2 const uint8_t *blimit, 91 ; r3 const uint8_t *limit, 92 ; sp const uint8_t *thresh, 93 ; sp+4 int count 94 |vpx_lpf_vertical_8_neon| PROC 95 push {r4-r5, lr} 96 97 vld1.8 {d0[]}, [r2] ; duplicate *blimit 98 ldr r12, [sp, #16] ; load count 99 vld1.8 {d1[]}, [r3] ; duplicate *limit 100 101 ldr r3, [sp, #12] ; load thresh 102 sub r2, r0, #4 ; move s pointer down by 4 columns 103 cmp r12, #0 104 beq end_vpx_mblf_v_edge 105 106 vld1.8 {d2[]}, [r3] ; duplicate *thresh 107 108 count_mblf_v_loop 109 vld1.u8 {d3}, [r2], r1 ; load s data 110 vld1.u8 {d4}, [r2], r1 111 vld1.u8 {d5}, [r2], r1 112 vld1.u8 {d6}, [r2], r1 113 vld1.u8 {d7}, [r2], r1 114 vld1.u8 {d16}, [r2], r1 115 vld1.u8 {d17}, [r2], r1 116 vld1.u8 {d18}, [r2] 117 118 ;transpose to 8x16 matrix 119 vtrn.32 d3, d7 120 vtrn.32 d4, d16 121 vtrn.32 d5, d17 122 vtrn.32 d6, d18 123 124 vtrn.16 d3, d5 125 vtrn.16 d4, d6 126 vtrn.16 d7, d17 127 vtrn.16 d16, d18 128 129 vtrn.8 d3, d4 130 vtrn.8 d5, d6 131 vtrn.8 d7, d16 132 vtrn.8 d17, d18 133 134 sub r2, r0, #3 135 add r3, r0, #1 136 137 bl vpx_mbloop_filter_neon 138 139 ;store op2, op1, op0, oq0 140 vst4.8 {d0[0], d1[0], d2[0], d3[0]}, [r2], r1 141 vst4.8 {d0[1], d1[1], d2[1], d3[1]}, [r2], r1 142 vst4.8 {d0[2], d1[2], d2[2], d3[2]}, [r2], r1 143 vst4.8 {d0[3], d1[3], d2[3], d3[3]}, [r2], r1 144 vst4.8 {d0[4], d1[4], d2[4], d3[4]}, [r2], r1 145 vst4.8 {d0[5], d1[5], d2[5], d3[5]}, [r2], r1 146 vst4.8 {d0[6], d1[6], d2[6], d3[6]}, [r2], r1 147 vst4.8 {d0[7], d1[7], d2[7], d3[7]}, [r2] 148 149 ;store oq1, oq2 150 vst2.8 {d4[0], d5[0]}, [r3], r1 151 vst2.8 {d4[1], d5[1]}, [r3], r1 152 vst2.8 {d4[2], d5[2]}, [r3], r1 153 vst2.8 {d4[3], d5[3]}, [r3], r1 154 vst2.8 {d4[4], d5[4]}, [r3], r1 155 vst2.8 {d4[5], d5[5]}, [r3], r1 156 vst2.8 {d4[6], d5[6]}, [r3], r1 157 vst2.8 {d4[7], d5[7]}, [r3] 158 159 add r0, r0, r1, lsl #3 ; s += pitch * 8 160 subs r12, r12, #1 161 subne r2, r0, #4 ; move s pointer down by 4 columns 162 bne count_mblf_v_loop 163 164 end_vpx_mblf_v_edge 165 pop {r4-r5, pc} 166 ENDP ; |vpx_lpf_vertical_8_neon| 167 168 ; void vpx_mbloop_filter_neon(); 169 ; This is a helper function for the loopfilters. The invidual functions do the 170 ; necessary load, transpose (if necessary) and store. The function does not use 171 ; registers d8-d15. 172 ; 173 ; Inputs: 174 ; r0-r3, r12 PRESERVE 175 ; d0 blimit 176 ; d1 limit 177 ; d2 thresh 178 ; d3 p3 179 ; d4 p2 180 ; d5 p1 181 ; d6 p0 182 ; d7 q0 183 ; d16 q1 184 ; d17 q2 185 ; d18 q3 186 ; 187 ; Outputs: 188 ; d0 op2 189 ; d1 op1 190 ; d2 op0 191 ; d3 oq0 192 ; d4 oq1 193 ; d5 oq2 194 |vpx_mbloop_filter_neon| PROC 195 ; filter_mask 196 vabd.u8 d19, d3, d4 ; m1 = abs(p3 - p2) 197 vabd.u8 d20, d4, d5 ; m2 = abs(p2 - p1) 198 vabd.u8 d21, d5, d6 ; m3 = abs(p1 - p0) 199 vabd.u8 d22, d16, d7 ; m4 = abs(q1 - q0) 200 vabd.u8 d23, d17, d16 ; m5 = abs(q2 - q1) 201 vabd.u8 d24, d18, d17 ; m6 = abs(q3 - q2) 202 203 ; only compare the largest value to limit 204 vmax.u8 d19, d19, d20 ; m1 = max(m1, m2) 205 vmax.u8 d20, d21, d22 ; m2 = max(m3, m4) 206 207 vabd.u8 d25, d6, d4 ; m7 = abs(p0 - p2) 208 209 vmax.u8 d23, d23, d24 ; m3 = max(m5, m6) 210 211 vabd.u8 d26, d7, d17 ; m8 = abs(q0 - q2) 212 213 vmax.u8 d19, d19, d20 214 215 vabd.u8 d24, d6, d7 ; m9 = abs(p0 - q0) 216 vabd.u8 d27, d3, d6 ; m10 = abs(p3 - p0) 217 vabd.u8 d28, d18, d7 ; m11 = abs(q3 - q0) 218 219 vmax.u8 d19, d19, d23 220 221 vabd.u8 d23, d5, d16 ; a = abs(p1 - q1) 222 vqadd.u8 d24, d24, d24 ; b = abs(p0 - q0) * 2 223 224 ; abs () > limit 225 vcge.u8 d19, d1, d19 226 227 ; only compare the largest value to thresh 228 vmax.u8 d25, d25, d26 ; m4 = max(m7, m8) 229 vmax.u8 d26, d27, d28 ; m5 = max(m10, m11) 230 231 vshr.u8 d23, d23, #1 ; a = a / 2 232 233 vmax.u8 d25, d25, d26 ; m4 = max(m4, m5) 234 235 vqadd.u8 d24, d24, d23 ; a = b + a 236 237 vmax.u8 d20, d20, d25 ; m2 = max(m2, m4) 238 239 vmov.u8 d23, #1 240 vcge.u8 d24, d0, d24 ; a > blimit 241 242 vcgt.u8 d21, d21, d2 ; (abs(p1 - p0) > thresh)*-1 243 244 vcge.u8 d20, d23, d20 ; flat 245 246 vand d19, d19, d24 ; mask 247 248 vcgt.u8 d23, d22, d2 ; (abs(q1 - q0) > thresh)*-1 249 250 vand d20, d20, d19 ; flat & mask 251 252 vmov.u8 d22, #0x80 253 254 vorr d23, d21, d23 ; hev 255 256 ; This instruction will truncate the "flat & mask" masks down to 4 bits 257 ; each to fit into one 32 bit arm register. The values are stored in 258 ; q10.64[0]. 259 vshrn.u16 d30, q10, #4 260 vmov.u32 r4, d30[0] ; flat & mask 4bits 261 262 adds r5, r4, #1 ; Check for all 1's 263 264 ; If mask and flat are 1's for all vectors, then we only need to execute 265 ; the power branch for all vectors. 266 beq power_branch_only 267 268 cmp r4, #0 ; Check for 0, set flag for later 269 270 ; mbfilter() function 271 ; filter() function 272 ; convert to signed 273 veor d21, d7, d22 ; qs0 274 veor d24, d6, d22 ; ps0 275 veor d25, d5, d22 ; ps1 276 veor d26, d16, d22 ; qs1 277 278 vmov.u8 d27, #3 279 280 vsub.s8 d28, d21, d24 ; ( qs0 - ps0) 281 282 vqsub.s8 d29, d25, d26 ; filter = clamp(ps1-qs1) 283 284 vmull.s8 q15, d28, d27 ; 3 * ( qs0 - ps0) 285 286 vand d29, d29, d23 ; filter &= hev 287 288 vaddw.s8 q15, q15, d29 ; filter + 3 * (qs0 - ps0) 289 290 vmov.u8 d29, #4 291 292 ; filter = clamp(filter + 3 * ( qs0 - ps0)) 293 vqmovn.s16 d28, q15 294 295 vand d28, d28, d19 ; filter &= mask 296 297 vqadd.s8 d30, d28, d27 ; filter2 = clamp(filter+3) 298 vqadd.s8 d29, d28, d29 ; filter1 = clamp(filter+4) 299 vshr.s8 d30, d30, #3 ; filter2 >>= 3 300 vshr.s8 d29, d29, #3 ; filter1 >>= 3 301 302 vqadd.s8 d24, d24, d30 ; op0 = clamp(ps0 + filter2) 303 vqsub.s8 d21, d21, d29 ; oq0 = clamp(qs0 - filter1) 304 305 ; outer tap adjustments: ++filter1 >> 1 306 vrshr.s8 d29, d29, #1 307 vbic d29, d29, d23 ; filter &= ~hev 308 309 vqadd.s8 d25, d25, d29 ; op1 = clamp(ps1 + filter) 310 vqsub.s8 d26, d26, d29 ; oq1 = clamp(qs1 - filter) 311 312 ; If mask and flat are 0's for all vectors, then we only need to execute 313 ; the filter branch for all vectors. 314 beq filter_branch_only 315 316 ; If mask and flat are mixed then we must perform both branches and 317 ; combine the data. 318 veor d24, d24, d22 ; *f_op0 = u^0x80 319 veor d21, d21, d22 ; *f_oq0 = u^0x80 320 veor d25, d25, d22 ; *f_op1 = u^0x80 321 veor d26, d26, d22 ; *f_oq1 = u^0x80 322 323 ; At this point we have already executed the filter branch. The filter 324 ; branch does not set op2 or oq2, so use p2 and q2. Execute the power 325 ; branch and combine the data. 326 vmov.u8 d23, #2 327 vaddl.u8 q14, d6, d7 ; r_op2 = p0 + q0 328 vmlal.u8 q14, d3, d27 ; r_op2 += p3 * 3 329 vmlal.u8 q14, d4, d23 ; r_op2 += p2 * 2 330 331 vbif d0, d4, d20 ; op2 |= p2 & ~(flat & mask) 332 333 vaddw.u8 q14, d5 ; r_op2 += p1 334 335 vbif d1, d25, d20 ; op1 |= f_op1 & ~(flat & mask) 336 337 vqrshrn.u16 d30, q14, #3 ; r_op2 338 339 vsubw.u8 q14, d3 ; r_op1 = r_op2 - p3 340 vsubw.u8 q14, d4 ; r_op1 -= p2 341 vaddw.u8 q14, d5 ; r_op1 += p1 342 vaddw.u8 q14, d16 ; r_op1 += q1 343 344 vbif d2, d24, d20 ; op0 |= f_op0 & ~(flat & mask) 345 346 vqrshrn.u16 d31, q14, #3 ; r_op1 347 348 vsubw.u8 q14, d3 ; r_op0 = r_op1 - p3 349 vsubw.u8 q14, d5 ; r_op0 -= p1 350 vaddw.u8 q14, d6 ; r_op0 += p0 351 vaddw.u8 q14, d17 ; r_op0 += q2 352 353 vbit d0, d30, d20 ; op2 |= r_op2 & (flat & mask) 354 355 vqrshrn.u16 d23, q14, #3 ; r_op0 356 357 vsubw.u8 q14, d3 ; r_oq0 = r_op0 - p3 358 vsubw.u8 q14, d6 ; r_oq0 -= p0 359 vaddw.u8 q14, d7 ; r_oq0 += q0 360 361 vbit d1, d31, d20 ; op1 |= r_op1 & (flat & mask) 362 363 vaddw.u8 q14, d18 ; oq0 += q3 364 365 vbit d2, d23, d20 ; op0 |= r_op0 & (flat & mask) 366 367 vqrshrn.u16 d22, q14, #3 ; r_oq0 368 369 vsubw.u8 q14, d4 ; r_oq1 = r_oq0 - p2 370 vsubw.u8 q14, d7 ; r_oq1 -= q0 371 vaddw.u8 q14, d16 ; r_oq1 += q1 372 373 vbif d3, d21, d20 ; oq0 |= f_oq0 & ~(flat & mask) 374 375 vaddw.u8 q14, d18 ; r_oq1 += q3 376 377 vbif d4, d26, d20 ; oq1 |= f_oq1 & ~(flat & mask) 378 379 vqrshrn.u16 d6, q14, #3 ; r_oq1 380 381 vsubw.u8 q14, d5 ; r_oq2 = r_oq1 - p1 382 vsubw.u8 q14, d16 ; r_oq2 -= q1 383 vaddw.u8 q14, d17 ; r_oq2 += q2 384 vaddw.u8 q14, d18 ; r_oq2 += q3 385 386 vbif d5, d17, d20 ; oq2 |= q2 & ~(flat & mask) 387 388 vqrshrn.u16 d7, q14, #3 ; r_oq2 389 390 vbit d3, d22, d20 ; oq0 |= r_oq0 & (flat & mask) 391 vbit d4, d6, d20 ; oq1 |= r_oq1 & (flat & mask) 392 vbit d5, d7, d20 ; oq2 |= r_oq2 & (flat & mask) 393 394 bx lr 395 396 power_branch_only 397 vmov.u8 d27, #3 398 vmov.u8 d21, #2 399 vaddl.u8 q14, d6, d7 ; op2 = p0 + q0 400 vmlal.u8 q14, d3, d27 ; op2 += p3 * 3 401 vmlal.u8 q14, d4, d21 ; op2 += p2 * 2 402 vaddw.u8 q14, d5 ; op2 += p1 403 vqrshrn.u16 d0, q14, #3 ; op2 404 405 vsubw.u8 q14, d3 ; op1 = op2 - p3 406 vsubw.u8 q14, d4 ; op1 -= p2 407 vaddw.u8 q14, d5 ; op1 += p1 408 vaddw.u8 q14, d16 ; op1 += q1 409 vqrshrn.u16 d1, q14, #3 ; op1 410 411 vsubw.u8 q14, d3 ; op0 = op1 - p3 412 vsubw.u8 q14, d5 ; op0 -= p1 413 vaddw.u8 q14, d6 ; op0 += p0 414 vaddw.u8 q14, d17 ; op0 += q2 415 vqrshrn.u16 d2, q14, #3 ; op0 416 417 vsubw.u8 q14, d3 ; oq0 = op0 - p3 418 vsubw.u8 q14, d6 ; oq0 -= p0 419 vaddw.u8 q14, d7 ; oq0 += q0 420 vaddw.u8 q14, d18 ; oq0 += q3 421 vqrshrn.u16 d3, q14, #3 ; oq0 422 423 vsubw.u8 q14, d4 ; oq1 = oq0 - p2 424 vsubw.u8 q14, d7 ; oq1 -= q0 425 vaddw.u8 q14, d16 ; oq1 += q1 426 vaddw.u8 q14, d18 ; oq1 += q3 427 vqrshrn.u16 d4, q14, #3 ; oq1 428 429 vsubw.u8 q14, d5 ; oq2 = oq1 - p1 430 vsubw.u8 q14, d16 ; oq2 -= q1 431 vaddw.u8 q14, d17 ; oq2 += q2 432 vaddw.u8 q14, d18 ; oq2 += q3 433 vqrshrn.u16 d5, q14, #3 ; oq2 434 435 bx lr 436 437 filter_branch_only 438 ; TODO(fgalligan): See if we can rearange registers so we do not need to 439 ; do the 2 vswp. 440 vswp d0, d4 ; op2 441 vswp d5, d17 ; oq2 442 veor d2, d24, d22 ; *op0 = u^0x80 443 veor d3, d21, d22 ; *oq0 = u^0x80 444 veor d1, d25, d22 ; *op1 = u^0x80 445 veor d4, d26, d22 ; *oq1 = u^0x80 446 447 bx lr 448 449 ENDP ; |vpx_mbloop_filter_neon| 450 451 END 452