1 /* NEON optimized code (C) COPYRIGHT 2009 Motorola 2 * 3 * Use of this source code is governed by a BSD-style license that can be 4 * found in the LICENSE file. 5 */ 6 7 /* 8 * Modifications done in-house at Motorola 9 * 10 * this is a clone of SkBitmapProcState_matrix.h 11 * and has been tuned to work with the NEON unit. 12 * 13 * Still going back and forth between whether this approach 14 * (clone the entire SkBitmapProcState_matrix.h file or 15 * if I should put just the modified routines in here and 16 * then use a construct like #define DONT_DO_THIS_FUNCTION or 17 * something like that... 18 * 19 * This is for the ClampX_ClampY instance 20 * 21 */ 22 23 24 #include <arm_neon.h> 25 26 /* 27 * This has been modified on the knowledge that (at the time) 28 * we had the following macro definitions in the parent file 29 * 30 * #define MAKENAME(suffix) ClampX_ClampY ## suffix 31 * #define TILEX_PROCF(fx, max) SkClampMax((fx) >> 16, max) 32 * #define TILEY_PROCF(fy, max) SkClampMax((fy) >> 16, max) 33 * #define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF) 34 * #define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF) 35 * #define CHECK_FOR_DECAL 36 */ 37 38 /* SkClampMax(val,max) -- bound to 0..max */ 39 40 #define SCALE_NOFILTER_NAME MAKENAME(_nofilter_scale) 41 #define SCALE_FILTER_NAME MAKENAME(_filter_scale) 42 #define AFFINE_NOFILTER_NAME MAKENAME(_nofilter_affine) 43 #define AFFINE_FILTER_NAME MAKENAME(_filter_affine) 44 #define PERSP_NOFILTER_NAME MAKENAME(_nofilter_persp) 45 #define PERSP_FILTER_NAME MAKENAME(_filter_persp) 46 47 #define PACK_FILTER_X_NAME MAKENAME(_pack_filter_x) 48 #define PACK_FILTER_Y_NAME MAKENAME(_pack_filter_y) 49 50 #ifndef PREAMBLE 51 #define PREAMBLE(state) 52 #define PREAMBLE_PARAM_X 53 #define PREAMBLE_PARAM_Y 54 #define PREAMBLE_ARG_X 55 #define PREAMBLE_ARG_Y 56 #endif 57 58 static void SCALE_NOFILTER_NAME(const SkBitmapProcState& s, 59 uint32_t xy[], int count, int x, int y) { 60 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | 61 SkMatrix::kScale_Mask)) == 0); 62 63 PREAMBLE(s); 64 // we store y, x, x, x, x, x 65 66 const unsigned maxX = s.fBitmap->width() - 1; 67 SkFixed fx; 68 { 69 SkPoint pt; 70 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, 71 SkIntToScalar(y) + SK_ScalarHalf, &pt); 72 fx = SkScalarToFixed(pt.fY); 73 const unsigned maxY = s.fBitmap->height() - 1; 74 *xy++ = TILEY_PROCF(fx, maxY); 75 fx = SkScalarToFixed(pt.fX); 76 } 77 78 if (0 == maxX) { 79 // all of the following X values must be 0 80 memset(xy, 0, count * sizeof(uint16_t)); 81 return; 82 } 83 84 const SkFixed dx = s.fInvSx; 85 86 #ifdef CHECK_FOR_DECAL 87 // test if we don't need to apply the tile proc 88 if ((unsigned)(fx >> 16) <= maxX && 89 (unsigned)((fx + dx * (count - 1)) >> 16) <= maxX) { 90 decal_nofilter_scale_neon(xy, fx, dx, count); 91 return; 92 } 93 #endif 94 95 int i; 96 97 /* very much like done in decal_nofilter, but with 98 * an extra clamping function applied. 99 * TILEX_PROCF(fx,max) SkClampMax((fx)>>16, max) 100 */ 101 if (count >= 8) { 102 /* SkFixed is 16.16 fixed point */ 103 SkFixed dx2 = dx+dx; 104 SkFixed dx4 = dx2+dx2; 105 SkFixed dx8 = dx4+dx4; 106 107 /* now build fx/fx+dx/fx+2dx/fx+3dx */ 108 SkFixed fx1, fx2, fx3; 109 int32x4_t lbase, hbase; 110 int16_t *dst16 = (int16_t *)xy; 111 112 fx1 = fx+dx; 113 fx2 = fx1+dx; 114 fx3 = fx2+dx; 115 116 /* build my template(s) */ 117 /* avoid the 'lbase unitialized' warning */ 118 lbase = vdupq_n_s32(fx); 119 lbase = vsetq_lane_s32(fx1, lbase, 1); 120 lbase = vsetq_lane_s32(fx2, lbase, 2); 121 lbase = vsetq_lane_s32(fx3, lbase, 3); 122 123 hbase = vaddq_s32(lbase, vdupq_n_s32(dx4)); 124 125 /* store & bump */ 126 do { 127 int32x4_t lout; 128 int32x4_t hout; 129 int16x8_t hi16; 130 131 /* get the hi 16s of all those 32s */ 132 lout = lbase; 133 hout = hbase; 134 /* this sets up all lout's then all hout's in hout */ 135 asm ("vuzpq.16 %q0, %q1" : "+w" (lout), "+w" (hout)); 136 hi16 = vreinterpretq_s16_s32(hout); 137 138 /* clamp & output */ 139 hi16 = vmaxq_s16(hi16, vdupq_n_s16(0)); 140 hi16 = vminq_s16(hi16, vdupq_n_s16(maxX)); 141 vst1q_s16(dst16, hi16); 142 143 /* but preserving base & on to the next */ 144 lbase = vaddq_s32 (lbase, vdupq_n_s32(dx8)); 145 hbase = vaddq_s32 (hbase, vdupq_n_s32(dx8)); 146 dst16 += 8; 147 count -= 8; 148 fx += dx8; 149 } while (count >= 8); 150 xy = (uint32_t *) dst16; 151 } 152 153 uint16_t* xx = (uint16_t*)xy; 154 for (i = count; i > 0; --i) { 155 *xx++ = TILEX_PROCF(fx, maxX); fx += dx; 156 } 157 } 158 159 // note: we could special-case on a matrix which is skewed in X but not Y. 160 // this would require a more general setup thatn SCALE does, but could use 161 // SCALE's inner loop that only looks at dx 162 163 static void AFFINE_NOFILTER_NAME(const SkBitmapProcState& s, 164 uint32_t xy[], int count, int x, int y) { 165 SkASSERT(s.fInvType & SkMatrix::kAffine_Mask); 166 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | 167 SkMatrix::kScale_Mask | 168 SkMatrix::kAffine_Mask)) == 0); 169 170 PREAMBLE(s); 171 SkPoint srcPt; 172 s.fInvProc(s.fInvMatrix, 173 SkIntToScalar(x) + SK_ScalarHalf, 174 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 175 176 SkFixed fx = SkScalarToFixed(srcPt.fX); 177 SkFixed fy = SkScalarToFixed(srcPt.fY); 178 SkFixed dx = s.fInvSx; 179 SkFixed dy = s.fInvKy; 180 int maxX = s.fBitmap->width() - 1; 181 int maxY = s.fBitmap->height() - 1; 182 183 /* NEON lets us do an 8x unrolling */ 184 if (count >= 8) { 185 /* SkFixed is 16.16 fixed point */ 186 SkFixed dx4 = dx * 4; 187 SkFixed dy4 = dy * 4; 188 SkFixed dx8 = dx * 8; 189 SkFixed dy8 = dy * 8; 190 191 int32x4_t xbase, ybase; 192 int32x4_t x2base, y2base; 193 int16_t *dst16 = (int16_t *) xy; 194 195 /* my sets of maxx/maxy for clamping */ 196 int32_t maxpair = (maxX&0xffff) | ((maxY&0xffff)<<16); 197 int16x8_t maxXY = vreinterpretq_s16_s32(vdupq_n_s32(maxpair)); 198 199 /* now build fx/fx+dx/fx+2dx/fx+3dx */ 200 /* avoid the 'xbase unitialized' warning...*/ 201 xbase = vdupq_n_s32(fx); 202 xbase = vsetq_lane_s32(fx+dx, xbase, 1); 203 xbase = vsetq_lane_s32(fx+dx+dx, xbase, 2); 204 xbase = vsetq_lane_s32(fx+dx+dx+dx, xbase, 3); 205 206 /* same for fy */ 207 /* avoid the 'ybase unitialized' warning...*/ 208 ybase = vdupq_n_s32(fy); 209 ybase = vsetq_lane_s32(fy+dy, ybase, 1); 210 ybase = vsetq_lane_s32(fy+dy+dy, ybase, 2); 211 ybase = vsetq_lane_s32(fy+dy+dy+dy, ybase, 3); 212 213 x2base = vaddq_s32(xbase, vdupq_n_s32(dx4)); 214 y2base = vaddq_s32(ybase, vdupq_n_s32(dy4)); 215 216 /* store & bump */ 217 do { 218 int32x4_t xout, yout; 219 int32x4_t x2out, y2out; 220 int16x8_t hi16, hi16_2; 221 222 xout = xbase; 223 yout = ybase; 224 225 /* overlay y's low16 with hi16 from x */ 226 /* so we properly shifted xyxyxyxy */ 227 yout = vsriq_n_s32(yout, xout, 16); 228 hi16 = vreinterpretq_s16_s32 (yout); 229 230 /* do the clamping; both guys get 0's */ 231 hi16 = vmaxq_s16 (hi16, vdupq_n_s16(0)); 232 hi16 = vminq_s16 (hi16, maxXY); 233 234 vst1q_s16 (dst16, hi16); 235 236 /* and for the other 4 pieces of this iteration */ 237 x2out = x2base; 238 y2out = y2base; 239 240 /* overlay y's low16 with hi16 from x */ 241 /* so we properly shifted xyxyxyxy */ 242 y2out = vsriq_n_s32(y2out, x2out, 16); 243 hi16_2 = vreinterpretq_s16_s32 (y2out); 244 245 /* do the clamping; both guys get 0's */ 246 hi16_2 = vmaxq_s16 (hi16_2, vdupq_n_s16(0)); 247 hi16_2 = vminq_s16 (hi16_2, maxXY); 248 249 /* RBE: gcc regenerates dst16+8 all the time instead 250 * of folding it into an addressing mode. *sigh* */ 251 vst1q_s16 (dst16+8, hi16_2); 252 253 /* moving base and on to the next */ 254 xbase = vaddq_s32 (xbase, vdupq_n_s32 (dx8)); 255 ybase = vaddq_s32 (ybase, vdupq_n_s32 (dy8)); 256 x2base = vaddq_s32 (x2base, vdupq_n_s32 (dx8)); 257 y2base = vaddq_s32 (y2base, vdupq_n_s32 (dy8)); 258 259 dst16 += 16; /* 8x32 aka 16x16 */ 260 count -= 8; 261 fx += dx8; 262 fy += dy8; 263 } while (count >= 8); 264 xy = (uint32_t *) dst16; 265 } 266 267 for (int i = count; i > 0; --i) { 268 *xy++ = (TILEY_PROCF(fy, maxY) << 16) | TILEX_PROCF(fx, maxX); 269 fx += dx; fy += dy; 270 } 271 } 272 273 #undef DEBUG_PERSP_NOFILTER 274 275 static void PERSP_NOFILTER_NAME(const SkBitmapProcState& s, 276 uint32_t* SK_RESTRICT xy, 277 int count, int x, int y) { 278 SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask); 279 280 PREAMBLE(s); 281 /* max{X,Y} are int here, but later shown/assumed to fit in 16 bits */ 282 int maxX = s.fBitmap->width() - 1; 283 int maxY = s.fBitmap->height() - 1; 284 285 SkPerspIter iter(s.fInvMatrix, 286 SkIntToScalar(x) + SK_ScalarHalf, 287 SkIntToScalar(y) + SK_ScalarHalf, count); 288 289 while ((count = iter.next()) != 0) { 290 const SkFixed* SK_RESTRICT srcXY = iter.getXY(); 291 292 #if defined(DEBUG_PERSP_NOFILTER) 293 /* debugging stuff */ 294 const SkFixed *end_srcXY = srcXY + (count*2); 295 uint32_t *end_xy = xy + (count); 296 const SkFixed *base_srcXY = srcXY; 297 uint32_t *base_xy = xy; 298 int base_count = count; 299 #endif 300 301 #if 1 302 // 2009/9/30: crashes in ApiDemos - Views - Animation - 3D Transition 303 // 2009/10/9: reworked to avoid illegal (but allowed by gas) insn 304 305 /* srcXY is a batch of 32 bit numbers X0,Y0,X1,Y1... 306 * but we immediately discard the low 16 bits... 307 * so what we're going to do is vld4, which will give us 308 * xlo,xhi,ylo,yhi distribution and we can ignore the 'lo' 309 * parts.... 310 */ 311 if (count >= 8) { 312 int16_t *mysrc = (int16_t *) srcXY; 313 int16_t *mydst = (int16_t *) xy; 314 int16x4_t maxX4 = vdup_n_s16((int16_t)maxX); 315 int16x4_t maxY4 = vdup_n_s16((int16_t)maxY); 316 int16x4_t zero4 = vdup_n_s16(0); 317 318 /* The constructs with local blocks for register assignments 319 * and asm() instructions is to make keep any hard register 320 * assignments to as small a scope as possible. and to avoid 321 * burning call-preserved hard registers on the vld/vst 322 * instructions. 323 */ 324 325 do { 326 int16x4_t xhi, yhi; 327 int16x4_t x2hi, y2hi; 328 329 /* vld4 does the de-interleaving for us */ 330 { 331 register int16x4_t t_xlo asm("d0"); 332 register int16x4_t t_xhi asm("d1"); 333 register int16x4_t t_ylo asm("d2"); 334 register int16x4_t t_yhi asm("d3"); 335 336 asm ("vld4.16 {d0-d3},[%4] /* xlo=%P0 xhi=%P1 ylo=%P2 yhi=%P3 */" 337 : "=w" (t_xlo), "=w" (t_xhi), "=w" (t_ylo), "=w" (t_yhi) 338 : "r" (mysrc) 339 ); 340 xhi = t_xhi; 341 yhi = t_yhi; 342 } 343 344 /* clamp X>>16 (aka xhi) to 0..maxX */ 345 xhi = vmax_s16(xhi, zero4); /* now 0.. */ 346 xhi = vmin_s16(xhi, maxX4); /* now 0..maxX */ 347 348 /* clamp Y>>16 (aka yhi) to 0..maxY */ 349 yhi = vmax_s16(yhi, zero4); /* now 0.. */ 350 yhi = vmin_s16(yhi, maxY4); /* now 0..maxY */ 351 352 /* deal with the second set of numbers */ 353 { 354 register int16x4_t t_xlo asm("d4"); 355 register int16x4_t t_xhi asm("d5"); 356 register int16x4_t t_ylo asm("d6"); 357 register int16x4_t t_yhi asm("d7"); 358 359 /* offset == 256 bits == 32 bytes == 8 longs == 16 shorts */ 360 asm ("vld4.16 {d4-d7},[%4] /* xlo=%P0 xhi=%P1 ylo=%P2 yhi=%P3 */" 361 : "=w" (t_xlo), "=w" (t_xhi), "=w" (t_ylo), "=w" (t_yhi) 362 : "r" (mysrc+16) 363 ); 364 x2hi = t_xhi; 365 y2hi = t_yhi; 366 } 367 368 /* clamp the second 4 here */ 369 370 if (0) { extern void rbe(void); rbe(); } 371 372 /* clamp X>>16 (aka xhi) to 0..maxX */ 373 x2hi = vmax_s16(x2hi, zero4); /* now 0.. */ 374 x2hi = vmin_s16(x2hi, maxX4); /* now 0..maxX */ 375 376 /* clamp Y>>16 (aka yhi) to 0..maxY */ 377 y2hi = vmax_s16(y2hi, zero4); /* now 0.. */ 378 y2hi = vmin_s16(y2hi, maxY4); /* now 0..maxY */ 379 380 /* we're storing as {x,y}s: x is [0], y is [1] */ 381 /* we'll use vst2 to make this happen */ 382 383 { 384 register int16x4_t out_x asm("d16") = xhi; 385 register int16x4_t out_y asm("d17") = yhi; 386 387 asm ("vst2.16 {d16-d17},[%2] /* xlo=%P0 xhi=%P1 */" 388 : 389 : "w" (out_x), "w" (out_y), "r" (mydst) 390 ); 391 } 392 { 393 register int16x4_t out_x asm("d18") = x2hi; 394 register int16x4_t out_y asm("d19") = y2hi; 395 396 asm ("vst2.16 {d18-d19},[%2] /* xlo=%P0 xhi=%P1 */" 397 : 398 : "w" (out_x), "w" (out_y), "r" (mydst+8) 399 ); 400 } 401 402 /* XXX: gcc isn't interleaving these with the NEON ops 403 * but i think that all the scoreboarding works out */ 404 count -= 8; /* 8 iterations */ 405 mysrc += 32; /* 16 longs, aka 32 shorts */ 406 mydst += 16; /* 16 shorts, aka 8 longs */ 407 } while (count >= 8); 408 /* get xy and srcXY fixed up */ 409 srcXY = (const SkFixed *) mysrc; 410 xy = (uint32_t *) mydst; 411 } 412 #endif 413 414 while (--count >= 0) { 415 *xy++ = (TILEY_PROCF(srcXY[1], maxY) << 16) | 416 TILEX_PROCF(srcXY[0], maxX); 417 srcXY += 2; 418 } 419 420 #if defined(DEBUG_PERSP_NOFILTER) 421 /* for checking our NEON-produced results against vanilla code */ 422 { 423 int bad = (-1); 424 for (int i = 0; i < base_count; i++) { 425 uint32_t val; 426 val = (TILEY_PROCF (base_srcXY[i * 2 + 1], maxY) << 16) | 427 TILEX_PROCF (base_srcXY[i * 2 + 0], maxX); 428 429 if (val != base_xy[i]) { 430 bad = i; 431 break; 432 } 433 } 434 if (bad >= 0) { 435 SkDebugf("clamp-nofilter-persp failed piece %d\n", bad); 436 SkDebugf(" maxX %08x maxY %08x\n", maxX, maxY); 437 bad -= (bad & 0x7); /* align */ 438 for (int i = bad; i < bad + 8; i++) { 439 uint32_t val; 440 val = (TILEY_PROCF (base_srcXY[i * 2 + 1], maxY) << 16) | 441 TILEX_PROCF (base_srcXY[i * 2 + 0], maxX); 442 443 SkDebugf("%d: got %08x want %08x srcXY[0] %08x srcXY[1] %08x\n", 444 i, base_xy[i], val, base_srcXY[i * 2 + 0], 445 base_srcXY[i * 2 + 1]); 446 } 447 SkDebugf ("---\n"); 448 } 449 450 if (end_xy != xy) { 451 SkDebugf("xy ended at %08x, should be %08x\n", xy, end_xy); 452 } 453 if (end_srcXY != srcXY) { 454 SkDebugf("srcXY ended at %08x, should be %08x\n", srcXY, 455 end_srcXY); 456 } 457 } 458 #endif 459 } 460 } 461 462 #undef DEBUG_PERSP_NOFILTER 463 464 ////////////////////////////////////////////////////////////////////////////// 465 466 static inline uint32_t PACK_FILTER_Y_NAME(SkFixed f, unsigned max, 467 SkFixed one PREAMBLE_PARAM_Y) { 468 unsigned i = TILEY_PROCF(f, max); 469 i = (i << 4) | TILEY_LOW_BITS(f, max); 470 return (i << 14) | (TILEY_PROCF((f + one), max)); 471 } 472 473 static inline uint32_t PACK_FILTER_X_NAME(SkFixed f, unsigned max, 474 SkFixed one PREAMBLE_PARAM_X) { 475 unsigned i = TILEX_PROCF(f, max); 476 i = (i << 4) | TILEX_LOW_BITS(f, max); 477 return (i << 14) | (TILEX_PROCF((f + one), max)); 478 } 479 480 static void SCALE_FILTER_NAME(const SkBitmapProcState& s, 481 uint32_t xy[], int count, int x, int y) { 482 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | 483 SkMatrix::kScale_Mask)) == 0); 484 SkASSERT(s.fInvKy == 0); 485 486 PREAMBLE(s); 487 488 const unsigned maxX = s.fBitmap->width() - 1; 489 const SkFixed one = s.fFilterOneX; 490 const SkFixed dx = s.fInvSx; 491 SkFixed fx; 492 493 { 494 SkPoint pt; 495 s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, 496 SkIntToScalar(y) + SK_ScalarHalf, &pt); 497 const SkFixed fy = SkScalarToFixed(pt.fY) - (s.fFilterOneY >> 1); 498 const unsigned maxY = s.fBitmap->height() - 1; 499 // compute our two Y values up front 500 *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y); 501 // now initialize fx 502 fx = SkScalarToFixed(pt.fX) - (one >> 1); 503 } 504 505 #ifdef CHECK_FOR_DECAL 506 // test if we don't need to apply the tile proc 507 if (dx > 0 && 508 (unsigned)(fx >> 16) <= maxX && 509 (unsigned)((fx + dx * (count - 1)) >> 16) < maxX) { 510 decal_filter_scale_neon(xy, fx, dx, count); 511 } else 512 #endif 513 514 if (count >= 4) { 515 int32x4_t wide_one, wide_fx, wide_fx1, wide_i, wide_lo; 516 #if 0 517 /* verification hooks -- see below */ 518 SkFixed debug_fx = fx; 519 int count_done = 0; 520 #endif 521 522 wide_fx = vdupq_n_s32(fx); 523 wide_fx = vsetq_lane_s32(fx+dx, wide_fx, 1); 524 wide_fx = vsetq_lane_s32(fx+dx+dx, wide_fx, 2); 525 wide_fx = vsetq_lane_s32(fx+dx+dx+dx, wide_fx, 3); 526 527 wide_one = vdupq_n_s32(one); 528 529 while (count >= 4) { 530 /* original expands to: 531 * unsigned i = SkClampMax((f) >> 16, max); 532 * i = (i << 4) | (((f) >> 12) & 0xF); 533 * return (i << 14) | (SkClampMax(((f + one)) >> 16, max)); 534 */ 535 536 /* i = SkClampMax(f>>16, maxX) */ 537 wide_i = vmaxq_s32(vshrq_n_s32(wide_fx,16), vdupq_n_s32(0)); 538 wide_i = vminq_s32(wide_i, vdupq_n_s32(maxX)); 539 540 /* i<<4 | TILEX_LOW_BITS(fx) */ 541 wide_lo = vshrq_n_s32(wide_fx, 12); 542 wide_i = vsliq_n_s32(wide_lo, wide_i, 4); 543 544 /* i<<14 */ 545 wide_i = vshlq_n_s32(wide_i, 14); 546 547 /* SkClampMax(((f + one)) >> 16, max) */ 548 wide_fx1 = vaddq_s32(wide_fx, wide_one); 549 wide_fx1 = vmaxq_s32(vshrq_n_s32(wide_fx1,16), vdupq_n_s32(0)); 550 wide_fx1 = vminq_s32(wide_fx1, vdupq_n_s32(maxX)); 551 552 /* final combination */ 553 wide_i = vorrq_s32(wide_i, wide_fx1); 554 555 vst1q_u32(xy, vreinterpretq_u32_s32(wide_i)); 556 557 #if 0 558 /* having a verification hook is a good idea */ 559 /* use debug_fx, debug_fx+dx, etc. */ 560 561 for (int i=0;i<4;i++) { 562 uint32_t want = PACK_FILTER_X_NAME(debug_fx, maxX, one PREAMBLE_ARG_X); 563 if (xy[i] != want) 564 { 565 /* print a nastygram */ 566 SkDebugf("clamp-filter-scale fails\n"); 567 SkDebugf("got %08x want %08x\n", xy[i], want); 568 SkDebugf("fx %08x debug_fx %08x dx %08x done %d\n", 569 fx, debug_fx, dx, count_done); 570 SkDebugf(" maxX %08x one %08x\n", maxX, one); 571 572 } 573 debug_fx += dx; 574 count_done++; 575 } 576 #endif 577 wide_fx += vdupq_n_s32(dx+dx+dx+dx); 578 fx += dx+dx+dx+dx; 579 xy += 4; 580 count -= 4; 581 } 582 } 583 584 while (--count >= 0) { 585 *xy++ = PACK_FILTER_X_NAME(fx, maxX, one PREAMBLE_ARG_X); 586 fx += dx; 587 } 588 } 589 590 static void AFFINE_FILTER_NAME(const SkBitmapProcState& s, 591 uint32_t xy[], int count, int x, int y) { 592 SkASSERT(s.fInvType & SkMatrix::kAffine_Mask); 593 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | 594 SkMatrix::kScale_Mask | 595 SkMatrix::kAffine_Mask)) == 0); 596 597 PREAMBLE(s); 598 SkPoint srcPt; 599 s.fInvProc(s.fInvMatrix, 600 SkIntToScalar(x) + SK_ScalarHalf, 601 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 602 603 SkFixed oneX = s.fFilterOneX; 604 SkFixed oneY = s.fFilterOneY; 605 SkFixed fx = SkScalarToFixed(srcPt.fX) - (oneX >> 1); 606 SkFixed fy = SkScalarToFixed(srcPt.fY) - (oneY >> 1); 607 SkFixed dx = s.fInvSx; 608 SkFixed dy = s.fInvKy; 609 unsigned maxX = s.fBitmap->width() - 1; 610 unsigned maxY = s.fBitmap->height() - 1; 611 612 if (count >= 4) { 613 int32x4_t wide_i, wide_lo; 614 int32x4_t wide_fx, wide_onex, wide_fx1; 615 int32x4_t wide_fy, wide_oney, wide_fy1; 616 617 #undef AFFINE_DEBUG 618 #if defined(AFFINE_DEBUG) 619 SkFixed fyp = fy; 620 SkFixed fxp = fx; 621 uint32_t *xyp = xy; 622 int count_done = 0; 623 #endif 624 625 wide_fx = vdupq_n_s32(fx); 626 wide_fx = vsetq_lane_s32(fx+dx, wide_fx, 1); 627 wide_fx = vsetq_lane_s32(fx+dx+dx, wide_fx, 2); 628 wide_fx = vsetq_lane_s32(fx+dx+dx+dx, wide_fx, 3); 629 630 wide_fy = vdupq_n_s32(fy); 631 wide_fy = vsetq_lane_s32(fy+dy, wide_fy, 1); 632 wide_fy = vsetq_lane_s32(fy+dy+dy, wide_fy, 2); 633 wide_fy = vsetq_lane_s32(fy+dy+dy+dy, wide_fy, 3); 634 635 wide_onex = vdupq_n_s32(oneX); 636 wide_oney = vdupq_n_s32(oneY); 637 638 while (count >= 4) { 639 int32x4_t wide_x; 640 int32x4_t wide_y; 641 642 /* do the X side, then the Y side, then interleave them */ 643 644 /* original expands to: 645 * unsigned i = SkClampMax((f) >> 16, max); 646 * i = (i << 4) | (((f) >> 12) & 0xF); 647 * return (i << 14) | (SkClampMax(((f + one)) >> 16, max)); 648 */ 649 650 /* i = SkClampMax(f>>16, maxX) */ 651 wide_i = vmaxq_s32(vshrq_n_s32(wide_fx,16), vdupq_n_s32(0)); 652 wide_i = vminq_s32(wide_i, vdupq_n_s32(maxX)); 653 654 /* i<<4 | TILEX_LOW_BITS(fx) */ 655 wide_lo = vshrq_n_s32(wide_fx, 12); 656 wide_i = vsliq_n_s32(wide_lo, wide_i, 4); 657 658 /* i<<14 */ 659 wide_i = vshlq_n_s32(wide_i, 14); 660 661 /* SkClampMax(((f + one)) >> 16, max) */ 662 wide_fx1 = vaddq_s32(wide_fx, wide_onex); 663 wide_fx1 = vmaxq_s32(vshrq_n_s32(wide_fx1,16), vdupq_n_s32(0)); 664 wide_fx1 = vminq_s32(wide_fx1, vdupq_n_s32(maxX)); 665 666 /* final combination */ 667 wide_x = vorrq_s32(wide_i, wide_fx1); 668 669 /* And now the Y side */ 670 671 /* i = SkClampMax(f>>16, maxX) */ 672 wide_i = vmaxq_s32(vshrq_n_s32(wide_fy,16), vdupq_n_s32(0)); 673 wide_i = vminq_s32(wide_i, vdupq_n_s32(maxY)); 674 675 /* i<<4 | TILEX_LOW_BITS(fx) */ 676 wide_lo = vshrq_n_s32(wide_fy, 12); 677 wide_i = vsliq_n_s32(wide_lo, wide_i, 4); 678 679 /* i<<14 */ 680 wide_i = vshlq_n_s32(wide_i, 14); 681 682 /* SkClampMax(((f + one)) >> 16, max) */ 683 wide_fy1 = vaddq_s32(wide_fy, wide_oney); 684 wide_fy1 = vmaxq_s32(vshrq_n_s32(wide_fy1,16), vdupq_n_s32(0)); 685 wide_fy1 = vminq_s32(wide_fy1, vdupq_n_s32(maxY)); 686 687 /* final combination */ 688 wide_y = vorrq_s32(wide_i, wide_fy1); 689 690 /* interleave as YXYXYXYX as part of the storing */ 691 { 692 /* vst2.32 needs side-by-side registers */ 693 register int32x4_t t_x asm("q1"); 694 register int32x4_t t_y asm("q0"); 695 696 t_x = wide_x; t_y = wide_y; 697 asm ("vst2.32 {q0-q1},[%2] /* y=%q0 x=%q1 */" 698 : 699 : "w" (t_y), "w" (t_x), "r" (xy) 700 ); 701 } 702 703 #if defined(AFFINE_DEBUG) 704 /* make sure we're good here -- check the 4 we just output */ 705 for (int i = 0; i<4;i++) { 706 uint32_t val; 707 val = PACK_FILTER_Y_NAME(fyp, maxY, oneY PREAMBLE_ARG_Y); 708 if (val != xy[i*2+0]) { 709 /* print a nastygram */ 710 SkDebugf("clamp-filter-affine fails\n"); 711 SkDebugf("[bad-y] got %08x want %08x\n", xy[i*2+0], val); 712 SkDebugf("fy %08x fxp %08x fyp %08x dx %08x dy %08x done %d\n", 713 fy, fxp, fyp, dx, dy, count_done); 714 SkDebugf(" maxY %08x oneY %08x\n", maxY, oneY); 715 } 716 val = PACK_FILTER_X_NAME(fxp, maxX, oneX PREAMBLE_ARG_X); 717 if (val != xy[i*2+1]) { 718 /* print a nastygram */ 719 SkDebugf("clamp-filter-affine fails\n"); 720 SkDebugf("[bad-x] got %08x want %08x\n", xy[i*2+1], val); 721 SkDebugf("fx %08x fxp %08x fyp %08x dx %08x dy %08x done %d\n", 722 fx, fxp, fyp, dx, dy, count_done); 723 SkDebugf(" maxX %08x one %08x\n", maxX, oneX); 724 } 725 fyp += dy; 726 fxp += dx; 727 count_done++; 728 } 729 #endif 730 731 wide_fx += vdupq_n_s32(dx+dx+dx+dx); 732 fx += dx+dx+dx+dx; 733 wide_fy += vdupq_n_s32(dy+dy+dy+dy); 734 fy += dy+dy+dy+dy; 735 xy += 8; /* 4 x's, 4 y's */ 736 count -= 4; 737 } 738 } 739 740 while (--count >= 0) { 741 /* NB: writing Y/X */ 742 *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y); 743 fy += dy; 744 *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneX PREAMBLE_ARG_X); 745 fx += dx; 746 } 747 } 748 749 static void PERSP_FILTER_NAME(const SkBitmapProcState& s, 750 uint32_t* SK_RESTRICT xy, int count, 751 int x, int y) { 752 SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask); 753 754 PREAMBLE(s); 755 unsigned maxX = s.fBitmap->width() - 1; 756 unsigned maxY = s.fBitmap->height() - 1; 757 SkFixed oneX = s.fFilterOneX; 758 SkFixed oneY = s.fFilterOneY; 759 760 SkPerspIter iter(s.fInvMatrix, 761 SkIntToScalar(x) + SK_ScalarHalf, 762 SkIntToScalar(y) + SK_ScalarHalf, count); 763 764 while ((count = iter.next()) != 0) { 765 const SkFixed* SK_RESTRICT srcXY = iter.getXY(); 766 767 if (count >= 4) { 768 int32x4_t wide_i, wide_lo; 769 int32x4_t wide_fx1; 770 int32x4_t wide_fy1; 771 int32x4_t wide_x, wide_y; 772 773 while (count >= 4) { 774 /* RBE: it's good, but: 775 * -- we spill a constant that could be easily regnerated 776 * [perhaps tweak gcc's NEON constant costs?] 777 */ 778 779 /* load src: x-y-x-y-x-y-x-y */ 780 { 781 register int32x4_t q0 asm ("q0"); 782 register int32x4_t q1 asm ("q1"); 783 asm ("vld2.32 {q0-q1},[%2] /* x=%q0 y=%q1 */" 784 : "=w" (q0), "=w" (q1) 785 : "r" (srcXY)); 786 wide_x = q0; wide_y = q1; 787 } 788 789 /* do the X side, then the Y side, then interleave them */ 790 791 wide_x = vsubq_s32(wide_x, vdupq_n_s32 (oneX>>1)); 792 793 /* original expands to: 794 * unsigned i = SkClampMax((f) >> 16, max); 795 * i = (i << 4) | (((f) >> 12) & 0xF); 796 * return (i << 14) | (SkClampMax(((f + one)) >> 16, max)); 797 */ 798 799 /* i = SkClampMax(f>>16, maxX) */ 800 wide_i = vmaxq_s32 (vshrq_n_s32 (wide_x, 16), vdupq_n_s32 (0)); 801 wide_i = vminq_s32 (wide_i, vdupq_n_s32 (maxX)); 802 803 /* i<<4 | TILEX_LOW_BITS(fx) */ 804 wide_lo = vshrq_n_s32 (wide_x, 12); 805 wide_i = vsliq_n_s32 (wide_lo, wide_i, 4); 806 807 /* i<<14 */ 808 wide_i = vshlq_n_s32 (wide_i, 14); 809 810 /* SkClampMax(((f + one)) >> 16, max) */ 811 wide_fx1 = vaddq_s32 (wide_x, vdupq_n_s32(oneX)); 812 wide_fx1 = vmaxq_s32 (vshrq_n_s32 (wide_fx1, 16), vdupq_n_s32 (0)); 813 wide_fx1 = vminq_s32 (wide_fx1, vdupq_n_s32 (maxX)); 814 815 /* final combination */ 816 wide_x = vorrq_s32 (wide_i, wide_fx1); 817 818 819 /* And now the Y side */ 820 821 wide_y = vsubq_s32(wide_y, vdupq_n_s32 (oneY>>1)); 822 823 /* i = SkClampMax(f>>16, maxX) */ 824 wide_i = vmaxq_s32 (vshrq_n_s32 (wide_y, 16), vdupq_n_s32 (0)); 825 wide_i = vminq_s32 (wide_i, vdupq_n_s32 (maxY)); 826 827 /* i<<4 | TILEX_LOW_BITS(fx) */ 828 wide_lo = vshrq_n_s32 (wide_y, 12); 829 wide_i = vsliq_n_s32 (wide_lo, wide_i, 4); 830 831 /* i<<14 */ 832 wide_i = vshlq_n_s32 (wide_i, 14); 833 834 /* SkClampMax(((f + one)) >> 16, max) */ 835 836 /* wide_fy1_1 and wide_fy1_2 are just temporary variables to 837 * work-around an ICE in debug */ 838 int32x4_t wide_fy1_1 = vaddq_s32 (wide_y, vdupq_n_s32(oneY)); 839 int32x4_t wide_fy1_2 = vmaxq_s32 (vshrq_n_s32 (wide_fy1_1, 16), 840 vdupq_n_s32 (0)); 841 wide_fy1 = vminq_s32 (wide_fy1_2, vdupq_n_s32 (maxY)); 842 843 /* final combination */ 844 wide_y = vorrq_s32 (wide_i, wide_fy1); 845 846 /* switch them around; have to do it this way to get them 847 * in the proper registers to match our instruction */ 848 849 /* iteration bookkeeping, ahead of the asm() for scheduling */ 850 srcXY += 2*4; 851 count -= 4; 852 853 /* store interleaved as y-x-y-x-y-x-y-x (NB != read order) */ 854 { 855 register int32x4_t q0 asm ("q0") = wide_y; 856 register int32x4_t q1 asm ("q1") = wide_x; 857 858 asm ("vst2.32 {q0-q1},[%2] /* y=%q0 x=%q1 */" 859 : 860 : "w" (q0), "w" (q1), "r" (xy)); 861 } 862 863 /* on to the next iteration */ 864 /* count, srcXY are handled above */ 865 xy += 2*4; 866 } 867 } 868 869 /* was do-while; NEON code invalidates original count>0 assumption */ 870 while (--count >= 0) { 871 /* NB: we read x/y, we write y/x */ 872 *xy++ = PACK_FILTER_Y_NAME(srcXY[1] - (oneY >> 1), maxY, 873 oneY PREAMBLE_ARG_Y); 874 *xy++ = PACK_FILTER_X_NAME(srcXY[0] - (oneX >> 1), maxX, 875 oneX PREAMBLE_ARG_X); 876 srcXY += 2; 877 } 878 } 879 } 880 881 const SkBitmapProcState::MatrixProc MAKENAME(_Procs)[] = { 882 SCALE_NOFILTER_NAME, 883 SCALE_FILTER_NAME, 884 AFFINE_NOFILTER_NAME, 885 AFFINE_FILTER_NAME, 886 PERSP_NOFILTER_NAME, 887 PERSP_FILTER_NAME 888 }; 889 890 #undef MAKENAME 891 #undef TILEX_PROCF 892 #undef TILEY_PROCF 893 #ifdef CHECK_FOR_DECAL 894 #undef CHECK_FOR_DECAL 895 #endif 896 897 #undef SCALE_NOFILTER_NAME 898 #undef SCALE_FILTER_NAME 899 #undef AFFINE_NOFILTER_NAME 900 #undef AFFINE_FILTER_NAME 901 #undef PERSP_NOFILTER_NAME 902 #undef PERSP_FILTER_NAME 903 904 #undef PREAMBLE 905 #undef PREAMBLE_PARAM_X 906 #undef PREAMBLE_PARAM_Y 907 #undef PREAMBLE_ARG_X 908 #undef PREAMBLE_ARG_Y 909 910 #undef TILEX_LOW_BITS 911 #undef TILEY_LOW_BITS 912