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