|  | 
 | /* | 
 |  * Copyright 2011 Google Inc. | 
 |  * | 
 |  * Use of this source code is governed by a BSD-style license that can be | 
 |  * found in the LICENSE file. | 
 |  */ | 
 | #include "SkBenchmark.h" | 
 | #include "SkMatrix.h" | 
 | #include "SkMatrixUtils.h" | 
 | #include "SkRandom.h" | 
 | #include "SkString.h" | 
 |  | 
 | class MatrixBench : public SkBenchmark { | 
 |     SkString    fName; | 
 | public: | 
 |     MatrixBench(const char name[])  { | 
 |         fName.printf("matrix_%s", name); | 
 |     } | 
 |  | 
 |     virtual bool isSuitableFor(Backend backend) SK_OVERRIDE { | 
 |         return backend == kNonRendering_Backend; | 
 |     } | 
 |  | 
 |     virtual void performTest() = 0; | 
 |  | 
 | protected: | 
 |     virtual int mulLoopCount() const { return 1; } | 
 |  | 
 |     virtual const char* onGetName() { | 
 |         return fName.c_str(); | 
 |     } | 
 |  | 
 |     virtual void onDraw(const int loops, SkCanvas*) { | 
 |         for (int i = 0; i < loops; i++) { | 
 |             this->performTest(); | 
 |         } | 
 |     } | 
 |  | 
 | private: | 
 |     typedef SkBenchmark INHERITED; | 
 | }; | 
 |  | 
 | // we want to stop the compiler from eliminating code that it thinks is a no-op | 
 | // so we have a non-static global we increment, hoping that will convince the | 
 | // compiler to execute everything | 
 | int gMatrixBench_NonStaticGlobal; | 
 |  | 
 | #define always_do(pred)                     \ | 
 |     do {                                    \ | 
 |         if (pred) {                         \ | 
 |             ++gMatrixBench_NonStaticGlobal; \ | 
 |         }                                   \ | 
 |     } while (0) | 
 |  | 
 | class EqualsMatrixBench : public MatrixBench { | 
 | public: | 
 |     EqualsMatrixBench() : INHERITED("equals") {} | 
 | protected: | 
 |     virtual void performTest() { | 
 |         SkMatrix m0, m1, m2; | 
 |  | 
 |         m0.reset(); | 
 |         m1.reset(); | 
 |         m2.reset(); | 
 |         always_do(m0 == m1); | 
 |         always_do(m1 == m2); | 
 |         always_do(m2 == m0); | 
 |     } | 
 | private: | 
 |     typedef MatrixBench INHERITED; | 
 | }; | 
 |  | 
 | class ScaleMatrixBench : public MatrixBench { | 
 | public: | 
 |     ScaleMatrixBench() : INHERITED("scale") { | 
 |         fSX = fSY = 1.5f; | 
 |         fM0.reset(); | 
 |         fM1.setScale(fSX, fSY); | 
 |         fM2.setTranslate(fSX, fSY); | 
 |     } | 
 | protected: | 
 |     virtual void performTest() { | 
 |         SkMatrix m; | 
 |         m = fM0; m.preScale(fSX, fSY); | 
 |         m = fM1; m.preScale(fSX, fSY); | 
 |         m = fM2; m.preScale(fSX, fSY); | 
 |     } | 
 | private: | 
 |     SkMatrix fM0, fM1, fM2; | 
 |     SkScalar fSX, fSY; | 
 |     typedef MatrixBench INHERITED; | 
 | }; | 
 |  | 
 | // having unknown values in our arrays can throw off the timing a lot, perhaps | 
 | // handling NaN values is a lot slower. Anyway, this guy is just meant to put | 
 | // reasonable values in our arrays. | 
 | template <typename T> void init9(T array[9]) { | 
 |     SkRandom rand; | 
 |     for (int i = 0; i < 9; i++) { | 
 |         array[i] = rand.nextSScalar1(); | 
 |     } | 
 | } | 
 |  | 
 | // Test the performance of setConcat() non-perspective case: | 
 | // using floating point precision only. | 
 | class FloatConcatMatrixBench : public MatrixBench { | 
 | public: | 
 |     FloatConcatMatrixBench() : INHERITED("concat_floatfloat") { | 
 |         init9(mya); | 
 |         init9(myb); | 
 |         init9(myr); | 
 |     } | 
 | protected: | 
 |     virtual int mulLoopCount() const { return 4; } | 
 |  | 
 |     static inline void muladdmul(float a, float b, float c, float d, | 
 |                                    float* result) { | 
 |       *result = a * b + c * d; | 
 |     } | 
 |     virtual void performTest() { | 
 |         const float* a = mya; | 
 |         const float* b = myb; | 
 |         float* r = myr; | 
 |         muladdmul(a[0], b[0], a[1], b[3], &r[0]); | 
 |         muladdmul(a[0], b[1], a[1], b[4], &r[1]); | 
 |         muladdmul(a[0], b[2], a[1], b[5], &r[2]); | 
 |         r[2] += a[2]; | 
 |         muladdmul(a[3], b[0], a[4], b[3], &r[3]); | 
 |         muladdmul(a[3], b[1], a[4], b[4], &r[4]); | 
 |         muladdmul(a[3], b[2], a[4], b[5], &r[5]); | 
 |         r[5] += a[5]; | 
 |         r[6] = r[7] = 0.0f; | 
 |         r[8] = 1.0f; | 
 |     } | 
 | private: | 
 |     float mya [9]; | 
 |     float myb [9]; | 
 |     float myr [9]; | 
 |     typedef MatrixBench INHERITED; | 
 | }; | 
 |  | 
 | static inline float SkDoubleToFloat(double x) { | 
 |     return static_cast<float>(x); | 
 | } | 
 |  | 
 | // Test the performance of setConcat() non-perspective case: | 
 | // using floating point precision but casting up to float for | 
 | // intermediate results during computations. | 
 | class FloatDoubleConcatMatrixBench : public MatrixBench { | 
 | public: | 
 |     FloatDoubleConcatMatrixBench() : INHERITED("concat_floatdouble") { | 
 |         init9(mya); | 
 |         init9(myb); | 
 |         init9(myr); | 
 |     } | 
 | protected: | 
 |     virtual int mulLoopCount() const { return 4; } | 
 |  | 
 |     static inline void muladdmul(float a, float b, float c, float d, | 
 |                                    float* result) { | 
 |       *result = SkDoubleToFloat((double)a * b + (double)c * d); | 
 |     } | 
 |     virtual void performTest() { | 
 |         const float* a = mya; | 
 |         const float* b = myb; | 
 |         float* r = myr; | 
 |         muladdmul(a[0], b[0], a[1], b[3], &r[0]); | 
 |         muladdmul(a[0], b[1], a[1], b[4], &r[1]); | 
 |         muladdmul(a[0], b[2], a[1], b[5], &r[2]); | 
 |         r[2] += a[2]; | 
 |         muladdmul(a[3], b[0], a[4], b[3], &r[3]); | 
 |         muladdmul(a[3], b[1], a[4], b[4], &r[4]); | 
 |         muladdmul(a[3], b[2], a[4], b[5], &r[5]); | 
 |         r[5] += a[5]; | 
 |         r[6] = r[7] = 0.0f; | 
 |         r[8] = 1.0f; | 
 |     } | 
 | private: | 
 |     float mya [9]; | 
 |     float myb [9]; | 
 |     float myr [9]; | 
 |     typedef MatrixBench INHERITED; | 
 | }; | 
 |  | 
 | // Test the performance of setConcat() non-perspective case: | 
 | // using double precision only. | 
 | class DoubleConcatMatrixBench : public MatrixBench { | 
 | public: | 
 |     DoubleConcatMatrixBench() : INHERITED("concat_double") { | 
 |         init9(mya); | 
 |         init9(myb); | 
 |         init9(myr); | 
 |     } | 
 | protected: | 
 |     virtual int mulLoopCount() const { return 4; } | 
 |  | 
 |     static inline void muladdmul(double a, double b, double c, double d, | 
 |                                    double* result) { | 
 |       *result = a * b + c * d; | 
 |     } | 
 |     virtual void performTest() { | 
 |         const double* a = mya; | 
 |         const double* b = myb; | 
 |         double* r = myr; | 
 |         muladdmul(a[0], b[0], a[1], b[3], &r[0]); | 
 |         muladdmul(a[0], b[1], a[1], b[4], &r[1]); | 
 |         muladdmul(a[0], b[2], a[1], b[5], &r[2]); | 
 |         r[2] += a[2]; | 
 |         muladdmul(a[3], b[0], a[4], b[3], &r[3]); | 
 |         muladdmul(a[3], b[1], a[4], b[4], &r[4]); | 
 |         muladdmul(a[3], b[2], a[4], b[5], &r[5]); | 
 |         r[5] += a[5]; | 
 |         r[6] = r[7] = 0.0; | 
 |         r[8] = 1.0; | 
 |     } | 
 | private: | 
 |     double mya [9]; | 
 |     double myb [9]; | 
 |     double myr [9]; | 
 |     typedef MatrixBench INHERITED; | 
 | }; | 
 |  | 
 | class GetTypeMatrixBench : public MatrixBench { | 
 | public: | 
 |     GetTypeMatrixBench() | 
 |         : INHERITED("gettype") { | 
 |         fArray[0] = (float) fRnd.nextS(); | 
 |         fArray[1] = (float) fRnd.nextS(); | 
 |         fArray[2] = (float) fRnd.nextS(); | 
 |         fArray[3] = (float) fRnd.nextS(); | 
 |         fArray[4] = (float) fRnd.nextS(); | 
 |         fArray[5] = (float) fRnd.nextS(); | 
 |         fArray[6] = (float) fRnd.nextS(); | 
 |         fArray[7] = (float) fRnd.nextS(); | 
 |         fArray[8] = (float) fRnd.nextS(); | 
 |     } | 
 | protected: | 
 |     // Putting random generation of the matrix inside performTest() | 
 |     // would help us avoid anomalous runs, but takes up 25% or | 
 |     // more of the function time. | 
 |     virtual void performTest() { | 
 |         fMatrix.setAll(fArray[0], fArray[1], fArray[2], | 
 |                        fArray[3], fArray[4], fArray[5], | 
 |                        fArray[6], fArray[7], fArray[8]); | 
 |         always_do(fMatrix.getType()); | 
 |         fMatrix.dirtyMatrixTypeCache(); | 
 |         always_do(fMatrix.getType()); | 
 |         fMatrix.dirtyMatrixTypeCache(); | 
 |         always_do(fMatrix.getType()); | 
 |         fMatrix.dirtyMatrixTypeCache(); | 
 |         always_do(fMatrix.getType()); | 
 |         fMatrix.dirtyMatrixTypeCache(); | 
 |         always_do(fMatrix.getType()); | 
 |         fMatrix.dirtyMatrixTypeCache(); | 
 |         always_do(fMatrix.getType()); | 
 |         fMatrix.dirtyMatrixTypeCache(); | 
 |         always_do(fMatrix.getType()); | 
 |         fMatrix.dirtyMatrixTypeCache(); | 
 |         always_do(fMatrix.getType()); | 
 |     } | 
 | private: | 
 |     SkMatrix fMatrix; | 
 |     float fArray[9]; | 
 |     SkRandom fRnd; | 
 |     typedef MatrixBench INHERITED; | 
 | }; | 
 |  | 
 | class ScaleTransMixedMatrixBench : public MatrixBench { | 
 |  public: | 
 |     ScaleTransMixedMatrixBench() : INHERITED("scaletrans_mixed") { | 
 |         fMatrix.setAll(fRandom.nextSScalar1(), fRandom.nextSScalar1(), fRandom.nextSScalar1(), | 
 |                        fRandom.nextSScalar1(), fRandom.nextSScalar1(), fRandom.nextSScalar1(), | 
 |                        fRandom.nextSScalar1(), fRandom.nextSScalar1(), fRandom.nextSScalar1()); | 
 |         int i; | 
 |         for (i = 0; i < kCount; i++) { | 
 |             fSrc[i].fX = fRandom.nextSScalar1(); | 
 |             fSrc[i].fY = fRandom.nextSScalar1(); | 
 |             fDst[i].fX = fRandom.nextSScalar1(); | 
 |             fDst[i].fY = fRandom.nextSScalar1(); | 
 |         } | 
 |     } | 
 |  protected: | 
 |     virtual void performTest() { | 
 |         SkPoint* dst = fDst; | 
 |         const SkPoint* src = fSrc; | 
 |         int count = kCount; | 
 |         float mx = fMatrix[SkMatrix::kMScaleX]; | 
 |         float my = fMatrix[SkMatrix::kMScaleY]; | 
 |         float tx = fMatrix[SkMatrix::kMTransX]; | 
 |         float ty = fMatrix[SkMatrix::kMTransY]; | 
 |         do { | 
 |             dst->fY = SkScalarMulAdd(src->fY, my, ty); | 
 |             dst->fX = SkScalarMulAdd(src->fX, mx, tx); | 
 |             src += 1; | 
 |             dst += 1; | 
 |         } while (--count); | 
 |     } | 
 |  private: | 
 |     enum { | 
 |         kCount = 16 | 
 |     }; | 
 |     SkMatrix fMatrix; | 
 |     SkPoint fSrc [kCount]; | 
 |     SkPoint fDst [kCount]; | 
 |     SkRandom fRandom; | 
 |     typedef MatrixBench INHERITED; | 
 | }; | 
 |  | 
 | class ScaleTransDoubleMatrixBench : public MatrixBench { | 
 |  public: | 
 |     ScaleTransDoubleMatrixBench() : INHERITED("scaletrans_double") { | 
 |         init9(fMatrix); | 
 |         int i; | 
 |         for (i = 0; i < kCount; i++) { | 
 |             fSrc[i].fX = fRandom.nextSScalar1(); | 
 |             fSrc[i].fY = fRandom.nextSScalar1(); | 
 |             fDst[i].fX = fRandom.nextSScalar1(); | 
 |             fDst[i].fY = fRandom.nextSScalar1(); | 
 |         } | 
 |     } | 
 |  protected: | 
 |     virtual void performTest() { | 
 |         SkPoint* dst = fDst; | 
 |         const SkPoint* src = fSrc; | 
 |         int count = kCount; | 
 |         // As doubles, on Z600 Linux systems this is 2.5x as expensive as mixed mode | 
 |         float mx = (float) fMatrix[SkMatrix::kMScaleX]; | 
 |         float my = (float) fMatrix[SkMatrix::kMScaleY]; | 
 |         float tx = (float) fMatrix[SkMatrix::kMTransX]; | 
 |         float ty = (float) fMatrix[SkMatrix::kMTransY]; | 
 |         do { | 
 |             dst->fY = src->fY * my + ty; | 
 |             dst->fX = src->fX * mx + tx; | 
 |             src += 1; | 
 |             dst += 1; | 
 |         } while (--count); | 
 |     } | 
 |  private: | 
 |     enum { | 
 |         kCount = 16 | 
 |     }; | 
 |     double fMatrix [9]; | 
 |     SkPoint fSrc [kCount]; | 
 |     SkPoint fDst [kCount]; | 
 |     SkRandom fRandom; | 
 |     typedef MatrixBench INHERITED; | 
 | }; | 
 |  | 
 | class DecomposeMatrixBench : public MatrixBench { | 
 | public: | 
 |     DecomposeMatrixBench() : INHERITED("decompose") {} | 
 |  | 
 | protected: | 
 |     virtual void onPreDraw() { | 
 |         for (int i = 0; i < 10; ++i) { | 
 |             SkScalar rot0 = (fRandom.nextBool()) ? fRandom.nextRangeF(-180, 180) : 0.0f; | 
 |             SkScalar sx = fRandom.nextRangeF(-3000.f, 3000.f); | 
 |             SkScalar sy = (fRandom.nextBool()) ? fRandom.nextRangeF(-3000.f, 3000.f) : sx; | 
 |             SkScalar rot1 = fRandom.nextRangeF(-180, 180); | 
 |             fMatrix[i].setRotate(rot0); | 
 |             fMatrix[i].postScale(sx, sy); | 
 |             fMatrix[i].postRotate(rot1); | 
 |         } | 
 |     } | 
 |     virtual void performTest() { | 
 |         SkPoint rotation1, scale, rotation2; | 
 |         for (int i = 0; i < 10; ++i) { | 
 |             (void) SkDecomposeUpper2x2(fMatrix[i], &rotation1, &scale, &rotation2); | 
 |         } | 
 |     } | 
 | private: | 
 |     SkMatrix fMatrix[10]; | 
 |     SkRandom fRandom; | 
 |     typedef MatrixBench INHERITED; | 
 | }; | 
 |  | 
 | class InvertMapRectMatrixBench : public MatrixBench { | 
 | public: | 
 |     InvertMapRectMatrixBench(const char* name, int flags) | 
 |         : INHERITED(name) | 
 |         , fFlags(flags) { | 
 |         fMatrix.reset(); | 
 |         fIteration = 0; | 
 |         if (flags & kScale_Flag) { | 
 |             fMatrix.postScale(1.5f, 2.5f); | 
 |         } | 
 |         if (flags & kTranslate_Flag) { | 
 |             fMatrix.postTranslate(1.5f, 2.5f); | 
 |         } | 
 |         if (flags & kRotate_Flag) { | 
 |             fMatrix.postRotate(45.0f); | 
 |         } | 
 |         if (flags & kPerspective_Flag) { | 
 |             fMatrix.setPerspX(1.5f); | 
 |             fMatrix.setPerspY(2.5f); | 
 |         } | 
 |         if (0 == (flags & kUncachedTypeMask_Flag)) { | 
 |             fMatrix.getType(); | 
 |         } | 
 |     } | 
 |     enum Flag { | 
 |         kScale_Flag             = 0x01, | 
 |         kTranslate_Flag         = 0x02, | 
 |         kRotate_Flag            = 0x04, | 
 |         kPerspective_Flag       = 0x08, | 
 |         kUncachedTypeMask_Flag  = 0x10, | 
 |     }; | 
 | protected: | 
 |     virtual void performTest() { | 
 |         if (fFlags & kUncachedTypeMask_Flag) { | 
 |             // This will invalidate the typemask without | 
 |             // changing the matrix. | 
 |             fMatrix.setPerspX(fMatrix.getPerspX()); | 
 |         } | 
 |         SkMatrix inv; | 
 |         bool invertible = fMatrix.invert(&inv); | 
 |         SkASSERT(invertible); | 
 |         SkRect transformedRect; | 
 |         // an arbitrary, small, non-zero rect to transform | 
 |         SkRect srcRect = SkRect::MakeWH(SkIntToScalar(10), SkIntToScalar(10)); | 
 |         if (invertible) { | 
 |             inv.mapRect(&transformedRect, srcRect); | 
 |         } | 
 |     } | 
 | private: | 
 |     SkMatrix fMatrix; | 
 |     int fFlags; | 
 |     unsigned fIteration; | 
 |     typedef MatrixBench INHERITED; | 
 | }; | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | DEF_BENCH( return new EqualsMatrixBench(); ) | 
 | DEF_BENCH( return new ScaleMatrixBench(); ) | 
 | DEF_BENCH( return new FloatConcatMatrixBench(); ) | 
 | DEF_BENCH( return new FloatDoubleConcatMatrixBench(); ) | 
 | DEF_BENCH( return new DoubleConcatMatrixBench(); ) | 
 | DEF_BENCH( return new GetTypeMatrixBench(); ) | 
 | DEF_BENCH( return new DecomposeMatrixBench(); ) | 
 |  | 
 | DEF_BENCH( return new InvertMapRectMatrixBench("invert_maprect_identity", 0); ) | 
 |  | 
 | DEF_BENCH(return new InvertMapRectMatrixBench( | 
 |                                   "invert_maprect_rectstaysrect", | 
 |                                   InvertMapRectMatrixBench::kScale_Flag | | 
 |                                   InvertMapRectMatrixBench::kTranslate_Flag); ) | 
 |  | 
 | DEF_BENCH(return new InvertMapRectMatrixBench( | 
 |                                   "invert_maprect_translate", | 
 |                                   InvertMapRectMatrixBench::kTranslate_Flag); ) | 
 |  | 
 | DEF_BENCH(return new InvertMapRectMatrixBench( | 
 |                                   "invert_maprect_nonpersp", | 
 |                                   InvertMapRectMatrixBench::kScale_Flag | | 
 |                                   InvertMapRectMatrixBench::kRotate_Flag | | 
 |                                   InvertMapRectMatrixBench::kTranslate_Flag); ) | 
 |  | 
 | DEF_BENCH( return new InvertMapRectMatrixBench( | 
 |                                "invert_maprect_persp", | 
 |                                InvertMapRectMatrixBench::kPerspective_Flag); ) | 
 |  | 
 | DEF_BENCH( return new InvertMapRectMatrixBench( | 
 |                            "invert_maprect_typemask_rectstaysrect", | 
 |                            InvertMapRectMatrixBench::kUncachedTypeMask_Flag | | 
 |                            InvertMapRectMatrixBench::kScale_Flag | | 
 |                            InvertMapRectMatrixBench::kTranslate_Flag); ) | 
 |  | 
 | DEF_BENCH( return new InvertMapRectMatrixBench( | 
 |                            "invert_maprect_typemask_nonpersp", | 
 |                            InvertMapRectMatrixBench::kUncachedTypeMask_Flag | | 
 |                            InvertMapRectMatrixBench::kScale_Flag | | 
 |                            InvertMapRectMatrixBench::kRotate_Flag | | 
 |                            InvertMapRectMatrixBench::kTranslate_Flag); ) | 
 |  | 
 | DEF_BENCH( return new ScaleTransMixedMatrixBench(); ) | 
 | DEF_BENCH( return new ScaleTransDoubleMatrixBench(); ) |