1 /*
2  * Copyright 2015 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 
8 #include "Benchmark.h"
9 #include "SkPMFloat.h"
10 
11 // Used to prevent the compiler from optimizing away the whole loop.
12 volatile uint32_t blackhole = 0;
13 
14 // Not a great random number generator, but it's very fast.
15 // The code we're measuring is quite fast, so low overhead is essential.
lcg_rand(uint32_t * seed)16 static uint32_t lcg_rand(uint32_t* seed) {
17     *seed *= 1664525;
18     *seed += 1013904223;
19     return *seed;
20 }
21 
22 // I'm having better luck getting these to constant-propagate away as template parameters.
23 template <bool kClamp, bool kWide>
24 struct PMFloatGetSetBench : public Benchmark {
PMFloatGetSetBenchPMFloatGetSetBench25     PMFloatGetSetBench() {}
26 
onGetNamePMFloatGetSetBench27     const char* onGetName() override {
28         switch (kClamp << 1 | kWide) {
29             case 0: return "SkPMFloat_get_1x";
30             case 1: return "SkPMFloat_get_4x";
31             case 2: return "SkPMFloat_clamp_1x";
32             case 3: return "SkPMFloat_clamp_4x";
33         }
34         SkFAIL("unreachable");
35         return "oh bother";
36     }
isSuitableForPMFloatGetSetBench37     bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; }
38 
onDrawPMFloatGetSetBench39     void onDraw(const int loops, SkCanvas* canvas) override {
40         // Unlike blackhole, junk can and probably will be a register.
41         uint32_t junk = 0;
42         uint32_t seed = 0;
43         for (int i = 0; i < loops; i++) {
44             SkPMColor colors[4];
45         #ifdef SK_DEBUG
46             for (int i = 0; i < 4; i++) {
47                 // Our SkASSERTs will remind us that it's technically required that we premultiply.
48                 colors[i] = SkPreMultiplyColor(lcg_rand(&seed));
49             }
50         #else
51             // But it's a lot faster not to, and this code won't really mind the non-PM colors.
52             (void)lcg_rand(&seed);
53             colors[0] = seed + 0;
54             colors[1] = seed + 1;
55             colors[2] = seed + 2;
56             colors[3] = seed + 3;
57         #endif
58 
59             SkPMFloat fa,fb,fc,fd;
60             if (kWide) {
61                 SkPMFloat::From4PMColors(colors, &fa, &fb, &fc, &fd);
62             } else {
63                 fa = SkPMFloat::FromPMColor(colors[0]);
64                 fb = SkPMFloat::FromPMColor(colors[1]);
65                 fc = SkPMFloat::FromPMColor(colors[2]);
66                 fd = SkPMFloat::FromPMColor(colors[3]);
67             }
68 
69             SkPMColor back[4];
70             switch (kClamp << 1 | kWide) {
71                 case 0: {
72                     back[0] = fa.round();
73                     back[1] = fb.round();
74                     back[2] = fc.round();
75                     back[3] = fd.round();
76                 } break;
77                 case 1: SkPMFloat::RoundTo4PMColors(fa, fb, fc, fd, back); break;
78                 case 2: {
79                     back[0] = fa.roundClamp();
80                     back[1] = fb.roundClamp();
81                     back[2] = fc.roundClamp();
82                     back[3] = fd.roundClamp();
83                 } break;
84                 case 3: SkPMFloat::RoundClampTo4PMColors(fa, fb, fc, fd, back); break;
85             }
86             for (int i = 0; i < 4; i++) {
87                 junk ^= back[i];
88             }
89         }
90         blackhole ^= junk;
91     }
92 };
93 
94 // Extra () help DEF_BENCH not get confused by the comma inside the <>.
95 DEF_BENCH(return (new PMFloatGetSetBench< true,  true>);)
96 DEF_BENCH(return (new PMFloatGetSetBench<false,  true>);)
97 DEF_BENCH(return (new PMFloatGetSetBench< true, false>);)
98 DEF_BENCH(return (new PMFloatGetSetBench<false, false>);)
99 
100 struct PMFloatGradientBench : public Benchmark {
onGetNamePMFloatGradientBench101     const char* onGetName() override { return "PMFloat_gradient"; }
isSuitableForPMFloatGradientBench102     bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; }
103 
104     SkPMColor fDevice[100];
onDrawPMFloatGradientBench105     void onDraw(const int loops, SkCanvas*) override {
106         Sk4f c0 = SkPMFloat::FromARGB(255, 255, 0, 0),
107              c1 = SkPMFloat::FromARGB(255, 0, 0, 255),
108              dc = c1 - c0,
109              fx(0.1f),
110              dx(0.002f),
111              dcdx(dc*dx),
112              dcdx4(dcdx+dcdx+dcdx+dcdx);
113 
114         for (int n = 0; n < loops; n++) {
115             Sk4f a = c0 + dc*fx + Sk4f(0.5f),  // The +0.5f lets us call trunc() instead of get().
116                  b = a + dcdx,
117                  c = b + dcdx,
118                  d = c + dcdx;
119             for (size_t i = 0; i < SK_ARRAY_COUNT(fDevice); i += 4) {
120                 fDevice[i+0] = SkPMFloat(a).trunc();
121                 fDevice[i+1] = SkPMFloat(b).trunc();
122                 fDevice[i+2] = SkPMFloat(c).trunc();
123                 fDevice[i+3] = SkPMFloat(d).trunc();
124                 a += dcdx4;
125                 b += dcdx4;
126                 c += dcdx4;
127                 d += dcdx4;
128             }
129         }
130     }
131 };
132 
133 DEF_BENCH(return new PMFloatGradientBench;)
134