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
14class MatrixBench : public Benchmark {
15    SkString    fName;
16public:
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
27protected:
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
40private:
41    typedef Benchmark INHERITED;
42};
43
44
45class EqualsMatrixBench : public MatrixBench {
46public:
47    EqualsMatrixBench() : INHERITED("equals") {}
48protected:
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    }
62private:
63    typedef MatrixBench INHERITED;
64};
65
66class ScaleMatrixBench : public MatrixBench {
67public:
68    ScaleMatrixBench() : INHERITED("scale") {
69        fSX = fSY = 1.5f;
70        fM0.reset();
71        fM1.setScale(fSX, fSY);
72        fM2.setTranslate(fSX, fSY);
73    }
74protected:
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    }
81private:
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.
90template <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.
99class FloatConcatMatrixBench : public MatrixBench {
100public:
101    FloatConcatMatrixBench() : INHERITED("concat_floatfloat") {
102        init9(mya);
103        init9(myb);
104        init9(myr);
105    }
106protected:
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    }
128private:
129    float mya [9];
130    float myb [9];
131    float myr [9];
132    typedef MatrixBench INHERITED;
133};
134
135static 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.
142class FloatDoubleConcatMatrixBench : public MatrixBench {
143public:
144    FloatDoubleConcatMatrixBench() : INHERITED("concat_floatdouble") {
145        init9(mya);
146        init9(myb);
147        init9(myr);
148    }
149protected:
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    }
171private:
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.
180class DoubleConcatMatrixBench : public MatrixBench {
181public:
182    DoubleConcatMatrixBench() : INHERITED("concat_double") {
183        init9(mya);
184        init9(myb);
185        init9(myr);
186    }
187protected:
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    }
209private:
210    double mya [9];
211    double myb [9];
212    double myr [9];
213    typedef MatrixBench INHERITED;
214};
215
216class GetTypeMatrixBench : public MatrixBench {
217public:
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    }
230protected:
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    }
256private:
257    SkMatrix fMatrix;
258    float fArray[9];
259    SkRandom fRnd;
260    typedef MatrixBench INHERITED;
261};
262
263class 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
304class 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
344class DecomposeMatrixBench : public MatrixBench {
345public:
346    DecomposeMatrixBench() : INHERITED("decompose") {}
347
348protected:
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    }
366private:
367    SkMatrix fMatrix[10];
368    SkRandom fRandom;
369    typedef MatrixBench INHERITED;
370};
371
372class InvertMapRectMatrixBench : public MatrixBench {
373public:
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    };
403protected:
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    }
420private:
421    SkMatrix fMatrix;
422    int fFlags;
423    unsigned fIteration;
424    typedef MatrixBench INHERITED;
425};
426
427///////////////////////////////////////////////////////////////////////////////
428
429DEF_BENCH( return new EqualsMatrixBench(); )
430DEF_BENCH( return new ScaleMatrixBench(); )
431DEF_BENCH( return new FloatConcatMatrixBench(); )
432DEF_BENCH( return new FloatDoubleConcatMatrixBench(); )
433DEF_BENCH( return new DoubleConcatMatrixBench(); )
434DEF_BENCH( return new GetTypeMatrixBench(); )
435DEF_BENCH( return new DecomposeMatrixBench(); )
436
437DEF_BENCH( return new InvertMapRectMatrixBench("invert_maprect_identity", 0); )
438
439DEF_BENCH(return new InvertMapRectMatrixBench(
440                                  "invert_maprect_rectstaysrect",
441                                  InvertMapRectMatrixBench::kScale_Flag |
442                                  InvertMapRectMatrixBench::kTranslate_Flag); )
443
444DEF_BENCH(return new InvertMapRectMatrixBench(
445                                  "invert_maprect_translate",
446                                  InvertMapRectMatrixBench::kTranslate_Flag); )
447
448DEF_BENCH(return new InvertMapRectMatrixBench(
449                                  "invert_maprect_nonpersp",
450                                  InvertMapRectMatrixBench::kScale_Flag |
451                                  InvertMapRectMatrixBench::kRotate_Flag |
452                                  InvertMapRectMatrixBench::kTranslate_Flag); )
453
454DEF_BENCH( return new InvertMapRectMatrixBench(
455                               "invert_maprect_persp",
456                               InvertMapRectMatrixBench::kPerspective_Flag); )
457
458DEF_BENCH( return new InvertMapRectMatrixBench(
459                           "invert_maprect_typemask_rectstaysrect",
460                           InvertMapRectMatrixBench::kUncachedTypeMask_Flag |
461                           InvertMapRectMatrixBench::kScale_Flag |
462                           InvertMapRectMatrixBench::kTranslate_Flag); )
463
464DEF_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
471DEF_BENCH( return new ScaleTransMixedMatrixBench(); )
472DEF_BENCH( return new ScaleTransDoubleMatrixBench(); )
473