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