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    }
20
21    virtual void performTest() = 0;
22
23protected:
24    virtual int mulLoopCount() const { return 1; }
25
26    virtual const char* onGetName() {
27        return fName.c_str();
28    }
29
30    virtual void onDraw(SkCanvas* canvas) {
31        int n = SkBENCHLOOP(N * this->mulLoopCount());
32        for (int i = 0; i < n; i++) {
33            this->performTest();
34        }
35    }
36
37private:
38    typedef SkBenchmark INHERITED;
39};
40
41// we want to stop the compiler from eliminating code that it thinks is a no-op
42// so we have a non-static global we increment, hoping that will convince the
43// compiler to execute everything
44int gMatrixBench_NonStaticGlobal;
45
46#define always_do(pred)                     \
47    do {                                    \
48        if (pred) {                         \
49            ++gMatrixBench_NonStaticGlobal; \
50        }                                   \
51    } while (0)
52
53class EqualsMatrixBench : public MatrixBench {
54public:
55    EqualsMatrixBench(void* param) : INHERITED(param, "equals") {}
56protected:
57    virtual void performTest() {
58        SkMatrix m0, m1, m2;
59
60        m0.reset();
61        m1.reset();
62        m2.reset();
63        always_do(m0 == m1);
64        always_do(m1 == m2);
65        always_do(m2 == m0);
66    }
67private:
68    typedef MatrixBench INHERITED;
69};
70
71class ScaleMatrixBench : public MatrixBench {
72public:
73    ScaleMatrixBench(void* param) : INHERITED(param, "scale") {
74        fSX = fSY = SkFloatToScalar(1.5f);
75        fM0.reset();
76        fM1.setScale(fSX, fSY);
77        fM2.setTranslate(fSX, fSY);
78    }
79protected:
80    virtual void performTest() {
81        SkMatrix m;
82        m = fM0; m.preScale(fSX, fSY);
83        m = fM1; m.preScale(fSX, fSY);
84        m = fM2; m.preScale(fSX, fSY);
85    }
86private:
87    SkMatrix fM0, fM1, fM2;
88    SkScalar fSX, fSY;
89    typedef MatrixBench INHERITED;
90};
91
92// having unknown values in our arrays can throw off the timing a lot, perhaps
93// handling NaN values is a lot slower. Anyway, this guy is just meant to put
94// reasonable values in our arrays.
95template <typename T> void init9(T array[9]) {
96    SkRandom rand;
97    for (int i = 0; i < 9; i++) {
98        array[i] = rand.nextSScalar1();
99    }
100}
101
102// Test the performance of setConcat() non-perspective case:
103// using floating point precision only.
104class FloatConcatMatrixBench : public MatrixBench {
105public:
106    FloatConcatMatrixBench(void* p) : INHERITED(p, "concat_floatfloat") {
107        init9(mya);
108        init9(myb);
109        init9(myr);
110    }
111protected:
112    virtual int mulLoopCount() const { return 4; }
113
114    static inline void muladdmul(float a, float b, float c, float d,
115                                   float* result) {
116      *result = a * b + c * d;
117    }
118    virtual void performTest() {
119        const float* a = mya;
120        const float* b = myb;
121        float* r = myr;
122        muladdmul(a[0], b[0], a[1], b[3], &r[0]);
123        muladdmul(a[0], b[1], a[1], b[4], &r[1]);
124        muladdmul(a[0], b[2], a[1], b[5], &r[2]);
125        r[2] += a[2];
126        muladdmul(a[3], b[0], a[4], b[3], &r[3]);
127        muladdmul(a[3], b[1], a[4], b[4], &r[4]);
128        muladdmul(a[3], b[2], a[4], b[5], &r[5]);
129        r[5] += a[5];
130        r[6] = r[7] = 0.0f;
131        r[8] = 1.0f;
132    }
133private:
134    float mya [9];
135    float myb [9];
136    float myr [9];
137    typedef MatrixBench INHERITED;
138};
139
140static inline float SkDoubleToFloat(double x) {
141    return static_cast<float>(x);
142}
143
144// Test the performance of setConcat() non-perspective case:
145// using floating point precision but casting up to float for
146// intermediate results during computations.
147class FloatDoubleConcatMatrixBench : public MatrixBench {
148public:
149    FloatDoubleConcatMatrixBench(void* p) : INHERITED(p, "concat_floatdouble") {
150        init9(mya);
151        init9(myb);
152        init9(myr);
153    }
154protected:
155    virtual int mulLoopCount() const { return 4; }
156
157    static inline void muladdmul(float a, float b, float c, float d,
158                                   float* result) {
159      *result = SkDoubleToFloat((double)a * b + (double)c * d);
160    }
161    virtual void performTest() {
162        const float* a = mya;
163        const float* b = myb;
164        float* r = myr;
165        muladdmul(a[0], b[0], a[1], b[3], &r[0]);
166        muladdmul(a[0], b[1], a[1], b[4], &r[1]);
167        muladdmul(a[0], b[2], a[1], b[5], &r[2]);
168        r[2] += a[2];
169        muladdmul(a[3], b[0], a[4], b[3], &r[3]);
170        muladdmul(a[3], b[1], a[4], b[4], &r[4]);
171        muladdmul(a[3], b[2], a[4], b[5], &r[5]);
172        r[5] += a[5];
173        r[6] = r[7] = 0.0f;
174        r[8] = 1.0f;
175    }
176private:
177    float mya [9];
178    float myb [9];
179    float myr [9];
180    typedef MatrixBench INHERITED;
181};
182
183// Test the performance of setConcat() non-perspective case:
184// using double precision only.
185class DoubleConcatMatrixBench : public MatrixBench {
186public:
187    DoubleConcatMatrixBench(void* p) : INHERITED(p, "concat_double") {
188        init9(mya);
189        init9(myb);
190        init9(myr);
191    }
192protected:
193    virtual int mulLoopCount() const { return 4; }
194
195    static inline void muladdmul(double a, double b, double c, double d,
196                                   double* result) {
197      *result = a * b + c * d;
198    }
199    virtual void performTest() {
200        const double* a = mya;
201        const double* b = myb;
202        double* r = myr;
203        muladdmul(a[0], b[0], a[1], b[3], &r[0]);
204        muladdmul(a[0], b[1], a[1], b[4], &r[1]);
205        muladdmul(a[0], b[2], a[1], b[5], &r[2]);
206        r[2] += a[2];
207        muladdmul(a[3], b[0], a[4], b[3], &r[3]);
208        muladdmul(a[3], b[1], a[4], b[4], &r[4]);
209        muladdmul(a[3], b[2], a[4], b[5], &r[5]);
210        r[5] += a[5];
211        r[6] = r[7] = 0.0;
212        r[8] = 1.0;
213    }
214private:
215    double mya [9];
216    double myb [9];
217    double myr [9];
218    typedef MatrixBench INHERITED;
219};
220
221class GetTypeMatrixBench : public MatrixBench {
222public:
223    GetTypeMatrixBench(void* param)
224        : INHERITED(param, "gettype") {
225        fArray[0] = (float) fRnd.nextS();
226        fArray[1] = (float) fRnd.nextS();
227        fArray[2] = (float) fRnd.nextS();
228        fArray[3] = (float) fRnd.nextS();
229        fArray[4] = (float) fRnd.nextS();
230        fArray[5] = (float) fRnd.nextS();
231        fArray[6] = (float) fRnd.nextS();
232        fArray[7] = (float) fRnd.nextS();
233        fArray[8] = (float) fRnd.nextS();
234    }
235protected:
236    // Putting random generation of the matrix inside performTest()
237    // would help us avoid anomalous runs, but takes up 25% or
238    // more of the function time.
239    virtual void performTest() {
240        fMatrix.setAll(fArray[0], fArray[1], fArray[2],
241                       fArray[3], fArray[4], fArray[5],
242                       fArray[6], fArray[7], fArray[8]);
243        always_do(fMatrix.getType());
244        fMatrix.dirtyMatrixTypeCache();
245        always_do(fMatrix.getType());
246        fMatrix.dirtyMatrixTypeCache();
247        always_do(fMatrix.getType());
248        fMatrix.dirtyMatrixTypeCache();
249        always_do(fMatrix.getType());
250        fMatrix.dirtyMatrixTypeCache();
251        always_do(fMatrix.getType());
252        fMatrix.dirtyMatrixTypeCache();
253        always_do(fMatrix.getType());
254        fMatrix.dirtyMatrixTypeCache();
255        always_do(fMatrix.getType());
256        fMatrix.dirtyMatrixTypeCache();
257        always_do(fMatrix.getType());
258    }
259private:
260    SkMatrix fMatrix;
261    float fArray[9];
262    SkRandom fRnd;
263    typedef MatrixBench INHERITED;
264};
265
266#ifdef SK_SCALAR_IS_FLOAT
267class ScaleTransMixedMatrixBench : public MatrixBench {
268 public:
269    ScaleTransMixedMatrixBench(void* p) : INHERITED(p, "scaletrans_mixed"), fCount (16) {
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 < SkBENCHLOOP(fCount); 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 = SkBENCHLOOP(fCount);
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    SkMatrix fMatrix;
299    SkPoint fSrc [16];
300    SkPoint fDst [16];
301    int fCount;
302    SkRandom fRandom;
303    typedef MatrixBench INHERITED;
304};
305
306class ScaleTransDoubleMatrixBench : public MatrixBench {
307 public:
308    ScaleTransDoubleMatrixBench(void* p) : INHERITED(p, "scaletrans_double"), fCount (16) {
309        init9(fMatrix);
310        int i;
311        for (i = 0; i < SkBENCHLOOP(fCount); i++) {
312            fSrc[i].fX = fRandom.nextSScalar1();
313            fSrc[i].fY = fRandom.nextSScalar1();
314            fDst[i].fX = fRandom.nextSScalar1();
315            fDst[i].fY = fRandom.nextSScalar1();
316        }
317    }
318 protected:
319    virtual void performTest() {
320        SkPoint* dst = fDst;
321        const SkPoint* src = fSrc;
322        int count = SkBENCHLOOP(fCount);
323        // As doubles, on Z600 Linux systems this is 2.5x as expensive as mixed mode
324        float mx = (float) fMatrix[SkMatrix::kMScaleX];
325        float my = (float) fMatrix[SkMatrix::kMScaleY];
326        float tx = (float) fMatrix[SkMatrix::kMTransX];
327        float ty = (float) fMatrix[SkMatrix::kMTransY];
328        do {
329            dst->fY = src->fY * my + ty;
330            dst->fX = src->fX * mx + tx;
331            src += 1;
332            dst += 1;
333        } while (--count);
334    }
335 private:
336    double fMatrix [9];
337    SkPoint fSrc [16];
338    SkPoint fDst [16];
339    int fCount;
340    SkRandom fRandom;
341    typedef MatrixBench INHERITED;
342};
343#endif
344
345
346
347
348
349static SkBenchmark* M0(void* p) { return new EqualsMatrixBench(p); }
350static SkBenchmark* M1(void* p) { return new ScaleMatrixBench(p); }
351static SkBenchmark* M2(void* p) { return new FloatConcatMatrixBench(p); }
352static SkBenchmark* M3(void* p) { return new FloatDoubleConcatMatrixBench(p); }
353static SkBenchmark* M4(void* p) { return new DoubleConcatMatrixBench(p); }
354static SkBenchmark* M5(void* p) { return new GetTypeMatrixBench(p); }
355
356static BenchRegistry gReg0(M0);
357static BenchRegistry gReg1(M1);
358static BenchRegistry gReg2(M2);
359static BenchRegistry gReg3(M3);
360static BenchRegistry gReg4(M4);
361static BenchRegistry gReg5(M5);
362
363#ifdef SK_SCALAR_IS_FLOAT
364static SkBenchmark* FlM0(void* p) { return new ScaleTransMixedMatrixBench(p); }
365static SkBenchmark* FlM1(void* p) { return new ScaleTransDoubleMatrixBench(p); }
366static BenchRegistry gFlReg5(FlM0);
367static BenchRegistry gFlReg6(FlM1);
368#endif
369