• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 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 "SkCanvas.h"
10 #include "SkPaint.h"
11 #include "SkRandom.h"
12 #include "SkShader.h"
13 #include "SkString.h"
14 
15 // This bench simulates the calls Skia sees from various HTML5 canvas
16 // game bench marks
17 class GameBench : public Benchmark {
18 public:
19     enum Type {
20         kScale_Type,
21         kTranslate_Type,
22         kRotate_Type
23     };
24 
25     enum Clear {
26         kFull_Clear,
27         kPartial_Clear
28     };
29 
GameBench(Type type,Clear clear,bool aligned=false,bool useAtlas=false,bool useDrawVertices=false)30     GameBench(Type type, Clear clear,
31               bool aligned = false, bool useAtlas = false,
32               bool useDrawVertices = false)
33         : fType(type)
34         , fClear(clear)
35         , fAligned(aligned)
36         , fUseAtlas(useAtlas)
37         , fUseDrawVertices(useDrawVertices)
38         , fName("game")
39         , fNumSaved(0)
40         , fInitialized(false) {
41 
42         switch (fType) {
43         case kScale_Type:
44             fName.append("_scale");
45             break;
46         case kTranslate_Type:
47             fName.append("_trans");
48             break;
49         case kRotate_Type:
50             fName.append("_rot");
51             break;
52         };
53 
54         if (aligned) {
55             fName.append("_aligned");
56         }
57 
58         if (kPartial_Clear == clear) {
59             fName.append("_partial");
60         } else {
61             fName.append("_full");
62         }
63 
64         if (useAtlas) {
65             fName.append("_atlas");
66         }
67 
68         if (useDrawVertices) {
69             fName.append("_drawVerts");
70         }
71 
72         // It's HTML 5 canvas, so always AA
73         fName.append("_aa");
74     }
75 
76 protected:
onGetName()77     const char* onGetName() override {
78         return fName.c_str();
79     }
80 
onDelayedSetup()81     void onDelayedSetup() override {
82         if (!fInitialized) {
83             this->makeCheckerboard();
84             this->makeAtlas();
85             fInitialized = true;
86         }
87     }
88 
onDraw(int loops,SkCanvas * canvas)89     void onDraw(int loops, SkCanvas* canvas) override {
90         SkRandom scaleRand;
91         SkRandom transRand;
92         SkRandom rotRand;
93 
94         int width, height;
95         if (fUseAtlas) {
96             width = kAtlasCellWidth;
97             height = kAtlasCellHeight;
98         } else {
99             width = kCheckerboardWidth;
100             height = kCheckerboardHeight;
101         }
102 
103         SkPaint clearPaint;
104         clearPaint.setColor(0xFF000000);
105         clearPaint.setAntiAlias(true);
106 
107         SkISize size = canvas->getDeviceSize();
108 
109         SkScalar maxTransX, maxTransY;
110 
111         if (kScale_Type == fType) {
112             maxTransX = size.fWidth  - (1.5f * width);
113             maxTransY = size.fHeight - (1.5f * height);
114         } else if (kTranslate_Type == fType) {
115             maxTransX = SkIntToScalar(size.fWidth  - width);
116             maxTransY = SkIntToScalar(size.fHeight - height);
117         } else {
118             SkASSERT(kRotate_Type == fType);
119             // Yes, some rotations will be off the top and left sides
120             maxTransX = size.fWidth  - SK_ScalarSqrt2 * height;
121             maxTransY = size.fHeight - SK_ScalarSqrt2 * height;
122         }
123 
124         SkMatrix mat;
125         SkRect dst = { 0, 0, SkIntToScalar(width), SkIntToScalar(height) };
126         SkRect clearRect = { -1.0f, -1.0f, width+1.0f, height+1.0f };
127         SkPoint verts[4] = { // for drawVertices path
128             { 0, 0 },
129             { 0, SkIntToScalar(height) },
130             { SkIntToScalar(width), SkIntToScalar(height) },
131             { SkIntToScalar(width), 0 }
132         };
133         uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 };
134 
135         SkPaint p;
136         p.setColor(0xFF000000);
137         p.setFilterQuality(kLow_SkFilterQuality);
138 
139         SkPaint p2;         // for drawVertices path
140         p2.setColor(0xFF000000);
141         p2.setFilterQuality(kLow_SkFilterQuality);
142         p2.setShader(SkShader::CreateBitmapShader(fAtlas,
143                                                   SkShader::kClamp_TileMode,
144                                                   SkShader::kClamp_TileMode))->unref();
145 
146         for (int i = 0; i < loops; ++i, ++fNumSaved) {
147             if (0 == i % kNumBeforeClear) {
148                 if (kPartial_Clear == fClear) {
149                     for (int j = 0; j < fNumSaved; ++j) {
150                         canvas->setMatrix(SkMatrix::I());
151                         mat.setTranslate(fSaved[j][0], fSaved[j][1]);
152 
153                         if (kScale_Type == fType) {
154                             mat.preScale(fSaved[j][2], fSaved[j][2]);
155                         } else if (kRotate_Type == fType) {
156                             mat.preRotate(fSaved[j][2]);
157                         }
158 
159                         canvas->concat(mat);
160                         canvas->drawRect(clearRect, clearPaint);
161                     }
162                 } else {
163                     canvas->clear(0xFF000000);
164                 }
165 
166                 fNumSaved = 0;
167             }
168 
169             SkASSERT(fNumSaved < kNumBeforeClear);
170 
171             canvas->setMatrix(SkMatrix::I());
172 
173             fSaved[fNumSaved][0] = transRand.nextRangeScalar(0.0f, maxTransX);
174             fSaved[fNumSaved][1] = transRand.nextRangeScalar(0.0f, maxTransY);
175             if (fAligned) {
176                 // make the translations integer aligned
177                 fSaved[fNumSaved][0] = SkScalarFloorToScalar(fSaved[fNumSaved][0]);
178                 fSaved[fNumSaved][1] = SkScalarFloorToScalar(fSaved[fNumSaved][1]);
179             }
180 
181             mat.setTranslate(fSaved[fNumSaved][0], fSaved[fNumSaved][1]);
182 
183             if (kScale_Type == fType) {
184                 fSaved[fNumSaved][2] = scaleRand.nextRangeScalar(0.5f, 1.5f);
185                 mat.preScale(fSaved[fNumSaved][2], fSaved[fNumSaved][2]);
186             } else if (kRotate_Type == fType) {
187                 fSaved[fNumSaved][2] = rotRand.nextRangeScalar(0.0f, 360.0f);
188                 mat.preRotate(fSaved[fNumSaved][2]);
189             }
190 
191             canvas->concat(mat);
192             if (fUseAtlas) {
193                 const int curCell = i % (kNumAtlasedX * kNumAtlasedY);
194                 SkIRect src = fAtlasRects[curCell % (kNumAtlasedX)][curCell / (kNumAtlasedX)];
195 
196                 if (fUseDrawVertices) {
197                     SkPoint uvs[4] = {
198                         { SkIntToScalar(src.fLeft),  SkIntToScalar(src.fBottom) },
199                         { SkIntToScalar(src.fLeft),  SkIntToScalar(src.fTop) },
200                         { SkIntToScalar(src.fRight), SkIntToScalar(src.fTop) },
201                         { SkIntToScalar(src.fRight), SkIntToScalar(src.fBottom) },
202                     };
203                     canvas->drawVertices(SkCanvas::kTriangles_VertexMode,
204                                          4, verts, uvs, nullptr, nullptr,
205                                          indices, 6, p2);
206                 } else {
207                     canvas->drawBitmapRect(fAtlas, src, dst, &p,
208                                            SkCanvas::kFast_SrcRectConstraint);
209                 }
210             } else {
211                 canvas->drawBitmapRect(fCheckerboard, dst, &p);
212             }
213         }
214     }
215 
216 private:
217     static const int kCheckerboardWidth = 64;
218     static const int kCheckerboardHeight = 128;
219 
220     static const int kAtlasCellWidth = 48;
221     static const int kAtlasCellHeight = 36;
222     static const int kNumAtlasedX = 5;
223     static const int kNumAtlasedY = 5;
224     static const int kAtlasSpacer = 2;
225     static const int kTotAtlasWidth  = kNumAtlasedX * kAtlasCellWidth +
226                                        (kNumAtlasedX+1) * kAtlasSpacer;
227     static const int kTotAtlasHeight = kNumAtlasedY * kAtlasCellHeight +
228                                        (kNumAtlasedY+1) * kAtlasSpacer;
229     static const int kNumBeforeClear = 100;
230 
231     Type     fType;
232     Clear    fClear;
233     bool     fAligned;
234     bool     fUseAtlas;
235     bool     fUseDrawVertices;
236     SkString fName;
237     int      fNumSaved; // num draws stored in 'fSaved'
238     bool     fInitialized;
239 
240     // 0 & 1 are always x & y translate. 2 is either scale or rotate.
241     SkScalar fSaved[kNumBeforeClear][3];
242 
243     SkBitmap fCheckerboard;
244     SkBitmap fAtlas;
245     SkIRect  fAtlasRects[kNumAtlasedX][kNumAtlasedY];
246 
247     // Note: the resulting checker board has transparency
makeCheckerboard()248     void makeCheckerboard() {
249         static int kCheckSize = 16;
250 
251         fCheckerboard.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight);
252         SkAutoLockPixels lock(fCheckerboard);
253         for (int y = 0; y < kCheckerboardHeight; ++y) {
254             int even = (y / kCheckSize) % 2;
255 
256             SkPMColor* scanline = fCheckerboard.getAddr32(0, y);
257 
258             for (int x = 0; x < kCheckerboardWidth; ++x) {
259                 if (even == (x / kCheckSize) % 2) {
260                     *scanline++ = 0xFFFF0000;
261                 } else {
262                     *scanline++ = 0x00000000;
263                 }
264             }
265         }
266     }
267 
268     // Note: the resulting atlas has transparency
makeAtlas()269     void makeAtlas() {
270         SkRandom rand;
271 
272         SkColor colors[kNumAtlasedX][kNumAtlasedY];
273 
274         for (int y = 0; y < kNumAtlasedY; ++y) {
275             for (int x = 0; x < kNumAtlasedX; ++x) {
276                 colors[x][y] = rand.nextU() | 0xff000000;
277                 fAtlasRects[x][y] = SkIRect::MakeXYWH(kAtlasSpacer + x * (kAtlasCellWidth + kAtlasSpacer),
278                                                       kAtlasSpacer + y * (kAtlasCellHeight + kAtlasSpacer),
279                                                       kAtlasCellWidth,
280                                                       kAtlasCellHeight);
281             }
282         }
283 
284         fAtlas.allocN32Pixels(kTotAtlasWidth, kTotAtlasHeight);
285         SkAutoLockPixels lock(fAtlas);
286 
287         for (int y = 0; y < kTotAtlasHeight; ++y) {
288             int colorY = y / (kAtlasCellHeight + kAtlasSpacer);
289             bool inColorY = (y % (kAtlasCellHeight + kAtlasSpacer)) >= kAtlasSpacer;
290 
291             SkPMColor* scanline = fAtlas.getAddr32(0, y);
292 
293             for (int x = 0; x < kTotAtlasWidth; ++x, ++scanline) {
294                 int colorX = x / (kAtlasCellWidth + kAtlasSpacer);
295                 bool inColorX = (x % (kAtlasCellWidth + kAtlasSpacer)) >= kAtlasSpacer;
296 
297                 if (inColorX && inColorY) {
298                     SkASSERT(colorX < kNumAtlasedX && colorY < kNumAtlasedY);
299                     *scanline = colors[colorX][colorY];
300                 } else {
301                     *scanline = 0x00000000;
302                 }
303             }
304         }
305     }
306 
307     typedef Benchmark INHERITED;
308 };
309 
310 // Partial clear
311 DEF_BENCH(return new GameBench(GameBench::kScale_Type, GameBench::kPartial_Clear);)
312 DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kPartial_Clear);)
313 DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kPartial_Clear, true);)
314 DEF_BENCH(return new GameBench(GameBench::kRotate_Type, GameBench::kPartial_Clear);)
315 
316 // Full clear
317 DEF_BENCH(return new GameBench(GameBench::kScale_Type, GameBench::kFull_Clear);)
318 DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear);)
319 DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear, true);)
320 DEF_BENCH(return new GameBench(GameBench::kRotate_Type, GameBench::kFull_Clear);)
321 
322 // Atlased
323 DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear, false, true);)
324 DEF_BENCH(return new GameBench(
325                          GameBench::kTranslate_Type, GameBench::kFull_Clear, false, true, true);)
326