1 2 /* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 #include "Benchmark.h" 9 #include "SkMatrix.h" 10 #include "SkMatrixUtils.h" 11 #include "SkRandom.h" 12 #include "SkString.h" 13 14 class MatrixBench : public Benchmark { 15 SkString fName; 16 public: 17 MatrixBench(const char name[]) { 18 fName.printf("matrix_%s", name); 19 } 20 21 virtual bool isSuitableFor(Backend backend) SK_OVERRIDE { 22 return backend == kNonRendering_Backend; 23 } 24 25 virtual void performTest() = 0; 26 27 protected: 28 virtual int mulLoopCount() const { return 1; } 29 30 virtual const char* onGetName() { 31 return fName.c_str(); 32 } 33 34 virtual void onDraw(const int loops, SkCanvas*) { 35 for (int i = 0; i < loops; i++) { 36 this->performTest(); 37 } 38 } 39 40 private: 41 typedef Benchmark INHERITED; 42 }; 43 44 45 class EqualsMatrixBench : public MatrixBench { 46 public: 47 EqualsMatrixBench() : INHERITED("equals") {} 48 protected: 49 virtual void performTest() { 50 SkMatrix m0, m1, m2; 51 52 m0.reset(); 53 m1.reset(); 54 m2.reset(); 55 56 // xor into a volatile prevents these comparisons from being optimized away. 57 volatile bool junk = false; 58 junk ^= (m0 == m1); 59 junk ^= (m1 == m2); 60 junk ^= (m2 == m0); 61 } 62 private: 63 typedef MatrixBench INHERITED; 64 }; 65 66 class ScaleMatrixBench : public MatrixBench { 67 public: 68 ScaleMatrixBench() : INHERITED("scale") { 69 fSX = fSY = 1.5f; 70 fM0.reset(); 71 fM1.setScale(fSX, fSY); 72 fM2.setTranslate(fSX, fSY); 73 } 74 protected: 75 virtual void performTest() { 76 SkMatrix m; 77 m = fM0; m.preScale(fSX, fSY); 78 m = fM1; m.preScale(fSX, fSY); 79 m = fM2; m.preScale(fSX, fSY); 80 } 81 private: 82 SkMatrix fM0, fM1, fM2; 83 SkScalar fSX, fSY; 84 typedef MatrixBench INHERITED; 85 }; 86 87 // having unknown values in our arrays can throw off the timing a lot, perhaps 88 // handling NaN values is a lot slower. Anyway, this guy is just meant to put 89 // reasonable values in our arrays. 90 template <typename T> void init9(T array[9]) { 91 SkRandom rand; 92 for (int i = 0; i < 9; i++) { 93 array[i] = rand.nextSScalar1(); 94 } 95 } 96 97 // Test the performance of setConcat() non-perspective case: 98 // using floating point precision only. 99 class FloatConcatMatrixBench : public MatrixBench { 100 public: 101 FloatConcatMatrixBench() : INHERITED("concat_floatfloat") { 102 init9(mya); 103 init9(myb); 104 init9(myr); 105 } 106 protected: 107 virtual int mulLoopCount() const { return 4; } 108 109 static inline void muladdmul(float a, float b, float c, float d, 110 float* result) { 111 *result = a * b + c * d; 112 } 113 virtual void performTest() { 114 const float* a = mya; 115 const float* b = myb; 116 float* r = myr; 117 muladdmul(a[0], b[0], a[1], b[3], &r[0]); 118 muladdmul(a[0], b[1], a[1], b[4], &r[1]); 119 muladdmul(a[0], b[2], a[1], b[5], &r[2]); 120 r[2] += a[2]; 121 muladdmul(a[3], b[0], a[4], b[3], &r[3]); 122 muladdmul(a[3], b[1], a[4], b[4], &r[4]); 123 muladdmul(a[3], b[2], a[4], b[5], &r[5]); 124 r[5] += a[5]; 125 r[6] = r[7] = 0.0f; 126 r[8] = 1.0f; 127 } 128 private: 129 float mya [9]; 130 float myb [9]; 131 float myr [9]; 132 typedef MatrixBench INHERITED; 133 }; 134 135 static inline float SkDoubleToFloat(double x) { 136 return static_cast<float>(x); 137 } 138 139 // Test the performance of setConcat() non-perspective case: 140 // using floating point precision but casting up to float for 141 // intermediate results during computations. 142 class FloatDoubleConcatMatrixBench : public MatrixBench { 143 public: 144 FloatDoubleConcatMatrixBench() : INHERITED("concat_floatdouble") { 145 init9(mya); 146 init9(myb); 147 init9(myr); 148 } 149 protected: 150 virtual int mulLoopCount() const { return 4; } 151 152 static inline void muladdmul(float a, float b, float c, float d, 153 float* result) { 154 *result = SkDoubleToFloat((double)a * b + (double)c * d); 155 } 156 virtual void performTest() { 157 const float* a = mya; 158 const float* b = myb; 159 float* r = myr; 160 muladdmul(a[0], b[0], a[1], b[3], &r[0]); 161 muladdmul(a[0], b[1], a[1], b[4], &r[1]); 162 muladdmul(a[0], b[2], a[1], b[5], &r[2]); 163 r[2] += a[2]; 164 muladdmul(a[3], b[0], a[4], b[3], &r[3]); 165 muladdmul(a[3], b[1], a[4], b[4], &r[4]); 166 muladdmul(a[3], b[2], a[4], b[5], &r[5]); 167 r[5] += a[5]; 168 r[6] = r[7] = 0.0f; 169 r[8] = 1.0f; 170 } 171 private: 172 float mya [9]; 173 float myb [9]; 174 float myr [9]; 175 typedef MatrixBench INHERITED; 176 }; 177 178 // Test the performance of setConcat() non-perspective case: 179 // using double precision only. 180 class DoubleConcatMatrixBench : public MatrixBench { 181 public: 182 DoubleConcatMatrixBench() : INHERITED("concat_double") { 183 init9(mya); 184 init9(myb); 185 init9(myr); 186 } 187 protected: 188 virtual int mulLoopCount() const { return 4; } 189 190 static inline void muladdmul(double a, double b, double c, double d, 191 double* result) { 192 *result = a * b + c * d; 193 } 194 virtual void performTest() { 195 const double* a = mya; 196 const double* b = myb; 197 double* r = myr; 198 muladdmul(a[0], b[0], a[1], b[3], &r[0]); 199 muladdmul(a[0], b[1], a[1], b[4], &r[1]); 200 muladdmul(a[0], b[2], a[1], b[5], &r[2]); 201 r[2] += a[2]; 202 muladdmul(a[3], b[0], a[4], b[3], &r[3]); 203 muladdmul(a[3], b[1], a[4], b[4], &r[4]); 204 muladdmul(a[3], b[2], a[4], b[5], &r[5]); 205 r[5] += a[5]; 206 r[6] = r[7] = 0.0; 207 r[8] = 1.0; 208 } 209 private: 210 double mya [9]; 211 double myb [9]; 212 double myr [9]; 213 typedef MatrixBench INHERITED; 214 }; 215 216 class GetTypeMatrixBench : public MatrixBench { 217 public: 218 GetTypeMatrixBench() 219 : INHERITED("gettype") { 220 fArray[0] = (float) fRnd.nextS(); 221 fArray[1] = (float) fRnd.nextS(); 222 fArray[2] = (float) fRnd.nextS(); 223 fArray[3] = (float) fRnd.nextS(); 224 fArray[4] = (float) fRnd.nextS(); 225 fArray[5] = (float) fRnd.nextS(); 226 fArray[6] = (float) fRnd.nextS(); 227 fArray[7] = (float) fRnd.nextS(); 228 fArray[8] = (float) fRnd.nextS(); 229 } 230 protected: 231 // Putting random generation of the matrix inside performTest() 232 // would help us avoid anomalous runs, but takes up 25% or 233 // more of the function time. 234 virtual void performTest() { 235 fMatrix.setAll(fArray[0], fArray[1], fArray[2], 236 fArray[3], fArray[4], fArray[5], 237 fArray[6], fArray[7], fArray[8]); 238 // xoring into a volatile prevents the compiler from optimizing these away 239 volatile int junk = 0; 240 junk ^= (fMatrix.getType()); 241 fMatrix.dirtyMatrixTypeCache(); 242 junk ^= (fMatrix.getType()); 243 fMatrix.dirtyMatrixTypeCache(); 244 junk ^= (fMatrix.getType()); 245 fMatrix.dirtyMatrixTypeCache(); 246 junk ^= (fMatrix.getType()); 247 fMatrix.dirtyMatrixTypeCache(); 248 junk ^= (fMatrix.getType()); 249 fMatrix.dirtyMatrixTypeCache(); 250 junk ^= (fMatrix.getType()); 251 fMatrix.dirtyMatrixTypeCache(); 252 junk ^= (fMatrix.getType()); 253 fMatrix.dirtyMatrixTypeCache(); 254 junk ^= (fMatrix.getType()); 255 } 256 private: 257 SkMatrix fMatrix; 258 float fArray[9]; 259 SkRandom fRnd; 260 typedef MatrixBench INHERITED; 261 }; 262 263 class ScaleTransMixedMatrixBench : public MatrixBench { 264 public: 265 ScaleTransMixedMatrixBench() : INHERITED("scaletrans_mixed") { 266 fMatrix.setAll(fRandom.nextSScalar1(), fRandom.nextSScalar1(), fRandom.nextSScalar1(), 267 fRandom.nextSScalar1(), fRandom.nextSScalar1(), fRandom.nextSScalar1(), 268 fRandom.nextSScalar1(), fRandom.nextSScalar1(), fRandom.nextSScalar1()); 269 int i; 270 for (i = 0; i < kCount; i++) { 271 fSrc[i].fX = fRandom.nextSScalar1(); 272 fSrc[i].fY = fRandom.nextSScalar1(); 273 fDst[i].fX = fRandom.nextSScalar1(); 274 fDst[i].fY = fRandom.nextSScalar1(); 275 } 276 } 277 protected: 278 virtual void performTest() { 279 SkPoint* dst = fDst; 280 const SkPoint* src = fSrc; 281 int count = kCount; 282 float mx = fMatrix[SkMatrix::kMScaleX]; 283 float my = fMatrix[SkMatrix::kMScaleY]; 284 float tx = fMatrix[SkMatrix::kMTransX]; 285 float ty = fMatrix[SkMatrix::kMTransY]; 286 do { 287 dst->fY = SkScalarMulAdd(src->fY, my, ty); 288 dst->fX = SkScalarMulAdd(src->fX, mx, tx); 289 src += 1; 290 dst += 1; 291 } while (--count); 292 } 293 private: 294 enum { 295 kCount = 16 296 }; 297 SkMatrix fMatrix; 298 SkPoint fSrc [kCount]; 299 SkPoint fDst [kCount]; 300 SkRandom fRandom; 301 typedef MatrixBench INHERITED; 302 }; 303 304 class ScaleTransDoubleMatrixBench : public MatrixBench { 305 public: 306 ScaleTransDoubleMatrixBench() : INHERITED("scaletrans_double") { 307 init9(fMatrix); 308 int i; 309 for (i = 0; i < kCount; i++) { 310 fSrc[i].fX = fRandom.nextSScalar1(); 311 fSrc[i].fY = fRandom.nextSScalar1(); 312 fDst[i].fX = fRandom.nextSScalar1(); 313 fDst[i].fY = fRandom.nextSScalar1(); 314 } 315 } 316 protected: 317 virtual void performTest() { 318 SkPoint* dst = fDst; 319 const SkPoint* src = fSrc; 320 int count = kCount; 321 // As doubles, on Z600 Linux systems this is 2.5x as expensive as mixed mode 322 float mx = (float) fMatrix[SkMatrix::kMScaleX]; 323 float my = (float) fMatrix[SkMatrix::kMScaleY]; 324 float tx = (float) fMatrix[SkMatrix::kMTransX]; 325 float ty = (float) fMatrix[SkMatrix::kMTransY]; 326 do { 327 dst->fY = src->fY * my + ty; 328 dst->fX = src->fX * mx + tx; 329 src += 1; 330 dst += 1; 331 } while (--count); 332 } 333 private: 334 enum { 335 kCount = 16 336 }; 337 double fMatrix [9]; 338 SkPoint fSrc [kCount]; 339 SkPoint fDst [kCount]; 340 SkRandom fRandom; 341 typedef MatrixBench INHERITED; 342 }; 343 344 class DecomposeMatrixBench : public MatrixBench { 345 public: 346 DecomposeMatrixBench() : INHERITED("decompose") {} 347 348 protected: 349 virtual void onPreDraw() { 350 for (int i = 0; i < 10; ++i) { 351 SkScalar rot0 = (fRandom.nextBool()) ? fRandom.nextRangeF(-180, 180) : 0.0f; 352 SkScalar sx = fRandom.nextRangeF(-3000.f, 3000.f); 353 SkScalar sy = (fRandom.nextBool()) ? fRandom.nextRangeF(-3000.f, 3000.f) : sx; 354 SkScalar rot1 = fRandom.nextRangeF(-180, 180); 355 fMatrix[i].setRotate(rot0); 356 fMatrix[i].postScale(sx, sy); 357 fMatrix[i].postRotate(rot1); 358 } 359 } 360 virtual void performTest() { 361 SkPoint rotation1, scale, rotation2; 362 for (int i = 0; i < 10; ++i) { 363 (void) SkDecomposeUpper2x2(fMatrix[i], &rotation1, &scale, &rotation2); 364 } 365 } 366 private: 367 SkMatrix fMatrix[10]; 368 SkRandom fRandom; 369 typedef MatrixBench INHERITED; 370 }; 371 372 class InvertMapRectMatrixBench : public MatrixBench { 373 public: 374 InvertMapRectMatrixBench(const char* name, int flags) 375 : INHERITED(name) 376 , fFlags(flags) { 377 fMatrix.reset(); 378 fIteration = 0; 379 if (flags & kScale_Flag) { 380 fMatrix.postScale(1.5f, 2.5f); 381 } 382 if (flags & kTranslate_Flag) { 383 fMatrix.postTranslate(1.5f, 2.5f); 384 } 385 if (flags & kRotate_Flag) { 386 fMatrix.postRotate(45.0f); 387 } 388 if (flags & kPerspective_Flag) { 389 fMatrix.setPerspX(1.5f); 390 fMatrix.setPerspY(2.5f); 391 } 392 if (0 == (flags & kUncachedTypeMask_Flag)) { 393 fMatrix.getType(); 394 } 395 } 396 enum Flag { 397 kScale_Flag = 0x01, 398 kTranslate_Flag = 0x02, 399 kRotate_Flag = 0x04, 400 kPerspective_Flag = 0x08, 401 kUncachedTypeMask_Flag = 0x10, 402 }; 403 protected: 404 virtual void performTest() { 405 if (fFlags & kUncachedTypeMask_Flag) { 406 // This will invalidate the typemask without 407 // changing the matrix. 408 fMatrix.setPerspX(fMatrix.getPerspX()); 409 } 410 SkMatrix inv; 411 bool invertible = fMatrix.invert(&inv); 412 SkASSERT(invertible); 413 SkRect transformedRect; 414 // an arbitrary, small, non-zero rect to transform 415 SkRect srcRect = SkRect::MakeWH(SkIntToScalar(10), SkIntToScalar(10)); 416 if (invertible) { 417 inv.mapRect(&transformedRect, srcRect); 418 } 419 } 420 private: 421 SkMatrix fMatrix; 422 int fFlags; 423 unsigned fIteration; 424 typedef MatrixBench INHERITED; 425 }; 426 427 /////////////////////////////////////////////////////////////////////////////// 428 429 DEF_BENCH( return new EqualsMatrixBench(); ) 430 DEF_BENCH( return new ScaleMatrixBench(); ) 431 DEF_BENCH( return new FloatConcatMatrixBench(); ) 432 DEF_BENCH( return new FloatDoubleConcatMatrixBench(); ) 433 DEF_BENCH( return new DoubleConcatMatrixBench(); ) 434 DEF_BENCH( return new GetTypeMatrixBench(); ) 435 DEF_BENCH( return new DecomposeMatrixBench(); ) 436 437 DEF_BENCH( return new InvertMapRectMatrixBench("invert_maprect_identity", 0); ) 438 439 DEF_BENCH(return new InvertMapRectMatrixBench( 440 "invert_maprect_rectstaysrect", 441 InvertMapRectMatrixBench::kScale_Flag | 442 InvertMapRectMatrixBench::kTranslate_Flag); ) 443 444 DEF_BENCH(return new InvertMapRectMatrixBench( 445 "invert_maprect_translate", 446 InvertMapRectMatrixBench::kTranslate_Flag); ) 447 448 DEF_BENCH(return new InvertMapRectMatrixBench( 449 "invert_maprect_nonpersp", 450 InvertMapRectMatrixBench::kScale_Flag | 451 InvertMapRectMatrixBench::kRotate_Flag | 452 InvertMapRectMatrixBench::kTranslate_Flag); ) 453 454 DEF_BENCH( return new InvertMapRectMatrixBench( 455 "invert_maprect_persp", 456 InvertMapRectMatrixBench::kPerspective_Flag); ) 457 458 DEF_BENCH( return new InvertMapRectMatrixBench( 459 "invert_maprect_typemask_rectstaysrect", 460 InvertMapRectMatrixBench::kUncachedTypeMask_Flag | 461 InvertMapRectMatrixBench::kScale_Flag | 462 InvertMapRectMatrixBench::kTranslate_Flag); ) 463 464 DEF_BENCH( return new InvertMapRectMatrixBench( 465 "invert_maprect_typemask_nonpersp", 466 InvertMapRectMatrixBench::kUncachedTypeMask_Flag | 467 InvertMapRectMatrixBench::kScale_Flag | 468 InvertMapRectMatrixBench::kRotate_Flag | 469 InvertMapRectMatrixBench::kTranslate_Flag); ) 470 471 DEF_BENCH( return new ScaleTransMixedMatrixBench(); ) 472 DEF_BENCH( return new ScaleTransDoubleMatrixBench(); ) 473