1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#include "Benchmark.h"
8#include "SkMatrix.h"
9#include "SkMatrixUtils.h"
10#include "SkRandom.h"
11#include "SkString.h"
12
13class MatrixBench : public Benchmark {
14    SkString    fName;
15public:
16    MatrixBench(const char name[])  {
17        fName.printf("matrix_%s", name);
18    }
19
20    bool isSuitableFor(Backend backend) override {
21        return backend == kNonRendering_Backend;
22    }
23
24    virtual void performTest() = 0;
25
26protected:
27    virtual int mulLoopCount() const { return 1; }
28
29    const char* onGetName() override {
30        return fName.c_str();
31    }
32
33    void onDraw(int loops, SkCanvas*) override {
34        for (int i = 0; i < loops; i++) {
35            this->performTest();
36        }
37    }
38
39private:
40    typedef Benchmark INHERITED;
41};
42
43
44class EqualsMatrixBench : public MatrixBench {
45public:
46    EqualsMatrixBench() : INHERITED("equals") {}
47protected:
48    void performTest() override {
49        SkMatrix m0, m1, m2;
50
51        m0.reset();
52        m1.reset();
53        m2.reset();
54
55        // xor into a volatile prevents these comparisons from being optimized away.
56        volatile bool junk = false;
57        junk ^= (m0 == m1);
58        junk ^= (m1 == m2);
59        junk ^= (m2 == m0);
60    }
61private:
62    typedef MatrixBench INHERITED;
63};
64
65class ScaleMatrixBench : public MatrixBench {
66public:
67    ScaleMatrixBench() : INHERITED("scale") {
68        fSX = fSY = 1.5f;
69        fM0.reset();
70        fM1.setScale(fSX, fSY);
71        fM2.setTranslate(fSX, fSY);
72    }
73protected:
74    void performTest() override {
75        SkMatrix m;
76        m = fM0; m.preScale(fSX, fSY);
77        m = fM1; m.preScale(fSX, fSY);
78        m = fM2; m.preScale(fSX, fSY);
79    }
80private:
81    SkMatrix fM0, fM1, fM2;
82    SkScalar fSX, fSY;
83    typedef MatrixBench INHERITED;
84};
85
86// having unknown values in our arrays can throw off the timing a lot, perhaps
87// handling NaN values is a lot slower. Anyway, this guy is just meant to put
88// reasonable values in our arrays.
89template <typename T> void init9(T array[9]) {
90    SkRandom rand;
91    for (int i = 0; i < 9; i++) {
92        array[i] = rand.nextSScalar1();
93    }
94}
95
96class GetTypeMatrixBench : public MatrixBench {
97public:
98    GetTypeMatrixBench()
99        : INHERITED("gettype") {
100        fArray[0] = (float) fRnd.nextS();
101        fArray[1] = (float) fRnd.nextS();
102        fArray[2] = (float) fRnd.nextS();
103        fArray[3] = (float) fRnd.nextS();
104        fArray[4] = (float) fRnd.nextS();
105        fArray[5] = (float) fRnd.nextS();
106        fArray[6] = (float) fRnd.nextS();
107        fArray[7] = (float) fRnd.nextS();
108        fArray[8] = (float) fRnd.nextS();
109    }
110protected:
111    // Putting random generation of the matrix inside performTest()
112    // would help us avoid anomalous runs, but takes up 25% or
113    // more of the function time.
114    void performTest() override {
115        fMatrix.setAll(fArray[0], fArray[1], fArray[2],
116                       fArray[3], fArray[4], fArray[5],
117                       fArray[6], fArray[7], fArray[8]);
118        // xoring into a volatile prevents the compiler from optimizing these away
119        volatile int junk = 0;
120        junk ^= (fMatrix.getType());
121        fMatrix.dirtyMatrixTypeCache();
122        junk ^= (fMatrix.getType());
123        fMatrix.dirtyMatrixTypeCache();
124        junk ^= (fMatrix.getType());
125        fMatrix.dirtyMatrixTypeCache();
126        junk ^= (fMatrix.getType());
127        fMatrix.dirtyMatrixTypeCache();
128        junk ^= (fMatrix.getType());
129        fMatrix.dirtyMatrixTypeCache();
130        junk ^= (fMatrix.getType());
131        fMatrix.dirtyMatrixTypeCache();
132        junk ^= (fMatrix.getType());
133        fMatrix.dirtyMatrixTypeCache();
134        junk ^= (fMatrix.getType());
135    }
136private:
137    SkMatrix fMatrix;
138    float fArray[9];
139    SkRandom fRnd;
140    typedef MatrixBench INHERITED;
141};
142
143class DecomposeMatrixBench : public MatrixBench {
144public:
145    DecomposeMatrixBench() : INHERITED("decompose") {}
146
147protected:
148    void onDelayedSetup() override {
149        for (int i = 0; i < 10; ++i) {
150            SkScalar rot0 = (fRandom.nextBool()) ? fRandom.nextRangeF(-180, 180) : 0.0f;
151            SkScalar sx = fRandom.nextRangeF(-3000.f, 3000.f);
152            SkScalar sy = (fRandom.nextBool()) ? fRandom.nextRangeF(-3000.f, 3000.f) : sx;
153            SkScalar rot1 = fRandom.nextRangeF(-180, 180);
154            fMatrix[i].setRotate(rot0);
155            fMatrix[i].postScale(sx, sy);
156            fMatrix[i].postRotate(rot1);
157        }
158    }
159    void performTest() override {
160        SkPoint rotation1, scale, rotation2;
161        for (int i = 0; i < 10; ++i) {
162            (void) SkDecomposeUpper2x2(fMatrix[i], &rotation1, &scale, &rotation2);
163        }
164    }
165private:
166    SkMatrix fMatrix[10];
167    SkRandom fRandom;
168    typedef MatrixBench INHERITED;
169};
170
171class InvertMapRectMatrixBench : public MatrixBench {
172public:
173    InvertMapRectMatrixBench(const char* name, int flags)
174        : INHERITED(name)
175        , fFlags(flags) {
176        fMatrix.reset();
177        fIteration = 0;
178        if (flags & kScale_Flag) {
179            fMatrix.postScale(1.5f, 2.5f);
180        }
181        if (flags & kTranslate_Flag) {
182            fMatrix.postTranslate(1.5f, 2.5f);
183        }
184        if (flags & kRotate_Flag) {
185            fMatrix.postRotate(45.0f);
186        }
187        if (flags & kPerspective_Flag) {
188            fMatrix.setPerspX(1.5f);
189            fMatrix.setPerspY(2.5f);
190        }
191        if (0 == (flags & kUncachedTypeMask_Flag)) {
192            fMatrix.getType();
193        }
194    }
195    enum Flag {
196        kScale_Flag             = 0x01,
197        kTranslate_Flag         = 0x02,
198        kRotate_Flag            = 0x04,
199        kPerspective_Flag       = 0x08,
200        kUncachedTypeMask_Flag  = 0x10,
201    };
202protected:
203    void performTest() override {
204        if (fFlags & kUncachedTypeMask_Flag) {
205            // This will invalidate the typemask without
206            // changing the matrix.
207            fMatrix.setPerspX(fMatrix.getPerspX());
208        }
209        SkMatrix inv;
210        bool invertible = fMatrix.invert(&inv);
211        SkASSERT(invertible);
212        SkRect transformedRect;
213        // an arbitrary, small, non-zero rect to transform
214        SkRect srcRect = SkRect::MakeWH(SkIntToScalar(10), SkIntToScalar(10));
215        if (invertible) {
216            inv.mapRect(&transformedRect, srcRect);
217        }
218    }
219private:
220    SkMatrix fMatrix;
221    int fFlags;
222    unsigned fIteration;
223    typedef MatrixBench INHERITED;
224};
225
226///////////////////////////////////////////////////////////////////////////////
227
228DEF_BENCH( return new EqualsMatrixBench(); )
229DEF_BENCH( return new ScaleMatrixBench(); )
230DEF_BENCH( return new GetTypeMatrixBench(); )
231DEF_BENCH( return new DecomposeMatrixBench(); )
232
233DEF_BENCH( return new InvertMapRectMatrixBench("invert_maprect_identity", 0); )
234
235DEF_BENCH(return new InvertMapRectMatrixBench(
236                                  "invert_maprect_rectstaysrect",
237                                  InvertMapRectMatrixBench::kScale_Flag |
238                                  InvertMapRectMatrixBench::kTranslate_Flag); )
239
240DEF_BENCH(return new InvertMapRectMatrixBench(
241                                  "invert_maprect_translate",
242                                  InvertMapRectMatrixBench::kTranslate_Flag); )
243
244DEF_BENCH(return new InvertMapRectMatrixBench(
245                                  "invert_maprect_nonpersp",
246                                  InvertMapRectMatrixBench::kScale_Flag |
247                                  InvertMapRectMatrixBench::kRotate_Flag |
248                                  InvertMapRectMatrixBench::kTranslate_Flag); )
249
250DEF_BENCH( return new InvertMapRectMatrixBench(
251                               "invert_maprect_persp",
252                               InvertMapRectMatrixBench::kPerspective_Flag); )
253
254DEF_BENCH( return new InvertMapRectMatrixBench(
255                           "invert_maprect_typemask_rectstaysrect",
256                           InvertMapRectMatrixBench::kUncachedTypeMask_Flag |
257                           InvertMapRectMatrixBench::kScale_Flag |
258                           InvertMapRectMatrixBench::kTranslate_Flag); )
259
260DEF_BENCH( return new InvertMapRectMatrixBench(
261                           "invert_maprect_typemask_nonpersp",
262                           InvertMapRectMatrixBench::kUncachedTypeMask_Flag |
263                           InvertMapRectMatrixBench::kScale_Flag |
264                           InvertMapRectMatrixBench::kRotate_Flag |
265                           InvertMapRectMatrixBench::kTranslate_Flag); )
266
267///////////////////////////////////////////////////////////////////////////////
268
269static SkMatrix make_trans() { return SkMatrix::MakeTrans(2, 3); }
270static SkMatrix make_scale() { SkMatrix m(make_trans()); m.postScale(1.5f, 0.5f); return m; }
271static SkMatrix make_afine() { SkMatrix m(make_trans()); m.postRotate(15); return m; }
272
273class MapPointsMatrixBench : public MatrixBench {
274protected:
275    SkMatrix fM;
276    enum {
277        N = 32
278    };
279    SkPoint fSrc[N], fDst[N];
280public:
281    MapPointsMatrixBench(const char name[], const SkMatrix& m)
282        : MatrixBench(name), fM(m)
283    {
284        SkRandom rand;
285        for (int i = 0; i < N; ++i) {
286            fSrc[i].set(rand.nextSScalar1(), rand.nextSScalar1());
287        }
288    }
289
290    void performTest() override {
291        for (int i = 0; i < 1000000; ++i) {
292            fM.mapPoints(fDst, fSrc, N);
293        }
294    }
295};
296DEF_BENCH( return new MapPointsMatrixBench("mappoints_identity", SkMatrix::I()); )
297DEF_BENCH( return new MapPointsMatrixBench("mappoints_trans", make_trans()); )
298DEF_BENCH( return new MapPointsMatrixBench("mappoints_scale", make_scale()); )
299DEF_BENCH( return new MapPointsMatrixBench("mappoints_affine", make_afine()); )
300
301///////////////////////////////////////////////////////////////////////////////
302
303class MapRectMatrixBench : public MatrixBench {
304    SkMatrix fM;
305    SkRect   fR;
306    bool     fScaleTrans;
307
308    enum { MEGA_LOOP = 1000 * 1000 };
309public:
310    MapRectMatrixBench(const char name[], bool scale_trans)
311        : MatrixBench(name), fScaleTrans(scale_trans)
312    {
313        fM.setScale(2, 3);
314        fM.postTranslate(1, 2);
315
316        fR.set(10, 10, 100, 200);
317    }
318
319    void performTest() override {
320        SkRect dst;
321        if (fScaleTrans) {
322            for (int i = 0; i < MEGA_LOOP; ++i) {
323                fM.mapRectScaleTranslate(&dst, fR);
324            }
325        } else {
326            for (int i = 0; i < MEGA_LOOP; ++i) {
327                fM.mapRect(&dst, fR);
328            }
329        }
330    }
331};
332DEF_BENCH( return new MapRectMatrixBench("maprect", false); )
333DEF_BENCH( return new MapRectMatrixBench("maprectscaletrans", true); )
334