• 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 "bench/Benchmark.h"
9  #include "include/core/SkBitmap.h"
10  #include "include/core/SkCanvas.h"
11  #include "include/core/SkImage.h"
12  #include "include/core/SkM44.h"
13  #include "include/core/SkPaint.h"
14  #include "include/core/SkShader.h"
15  #include "include/core/SkString.h"
16  #include "include/core/SkVertices.h"
17  #include "include/utils/SkRandom.h"
18  
19  // This bench simulates the calls Skia sees from various HTML5 canvas
20  // game bench marks
21  class GameBench : public Benchmark {
22  public:
23      enum Type {
24          kScale_Type,
25          kTranslate_Type,
26          kRotate_Type
27      };
28  
29      enum Clear {
30          kFull_Clear,
31          kPartial_Clear
32      };
33  
GameBench(Type type,Clear clear,bool aligned=false,bool useAtlas=false,bool useDrawVertices=false)34      GameBench(Type type, Clear clear,
35                bool aligned = false, bool useAtlas = false,
36                bool useDrawVertices = false)
37          : fType(type)
38          , fClear(clear)
39          , fAligned(aligned)
40          , fUseAtlas(useAtlas)
41          , fUseDrawVertices(useDrawVertices)
42          , fName("game")
43          , fNumSaved(0)
44          , fInitialized(false) {
45  
46          switch (fType) {
47          case kScale_Type:
48              fName.append("_scale");
49              break;
50          case kTranslate_Type:
51              fName.append("_trans");
52              break;
53          case kRotate_Type:
54              fName.append("_rot");
55              break;
56          }
57  
58          if (aligned) {
59              fName.append("_aligned");
60          }
61  
62          if (kPartial_Clear == clear) {
63              fName.append("_partial");
64          } else {
65              fName.append("_full");
66          }
67  
68          if (useAtlas) {
69              fName.append("_atlas");
70          }
71  
72          if (useDrawVertices) {
73              fName.append("_drawVerts");
74          }
75  
76          // It's HTML 5 canvas, so always AA
77          fName.append("_aa");
78      }
79  
80  protected:
onGetName()81      const char* onGetName() override {
82          return fName.c_str();
83      }
84  
onDelayedSetup()85      void onDelayedSetup() override {
86          if (!fInitialized) {
87              this->makeCheckerboard();
88              this->makeAtlas();
89              fInitialized = true;
90          }
91      }
92  
onDraw(int loops,SkCanvas * canvas)93      void onDraw(int loops, SkCanvas* canvas) override {
94          SkRandom scaleRand;
95          SkRandom transRand;
96          SkRandom rotRand;
97  
98          int width, height;
99          if (fUseAtlas) {
100              width = kAtlasCellWidth;
101              height = kAtlasCellHeight;
102          } else {
103              width = kCheckerboardWidth;
104              height = kCheckerboardHeight;
105          }
106  
107          SkPaint clearPaint;
108          clearPaint.setColor(0xFF000000);
109          clearPaint.setAntiAlias(true);
110  
111          SkISize size = canvas->getBaseLayerSize();
112  
113          SkScalar maxTransX, maxTransY;
114  
115          if (kScale_Type == fType) {
116              maxTransX = size.fWidth  - (1.5f * width);
117              maxTransY = size.fHeight - (1.5f * height);
118          } else if (kTranslate_Type == fType) {
119              maxTransX = SkIntToScalar(size.fWidth  - width);
120              maxTransY = SkIntToScalar(size.fHeight - height);
121          } else {
122              SkASSERT(kRotate_Type == fType);
123              // Yes, some rotations will be off the top and left sides
124              maxTransX = size.fWidth  - SK_ScalarSqrt2 * height;
125              maxTransY = size.fHeight - SK_ScalarSqrt2 * height;
126          }
127  
128          SkMatrix mat;
129          SkRect dst = { 0, 0, SkIntToScalar(width), SkIntToScalar(height) };
130          SkRect clearRect = { -1.0f, -1.0f, width+1.0f, height+1.0f };
131          SkPoint verts[4] = { // for drawVertices path
132              { 0, 0 },
133              { 0, SkIntToScalar(height) },
134              { SkIntToScalar(width), SkIntToScalar(height) },
135              { SkIntToScalar(width), 0 }
136          };
137          uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 };
138  
139          SkPaint p;
140          p.setColor(0xFF000000);
141  
142          SkPaint p2;         // for drawVertices path
143          p2.setColor(0xFF000000);
144          p2.setShader(fAtlas->makeShader(SkSamplingOptions(SkFilterMode::kLinear)));
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                  SkRect src = SkRect::Make(
195                                fAtlasRects[curCell % (kNumAtlasedX)][curCell / (kNumAtlasedX)]);
196  
197                  if (fUseDrawVertices) {
198                      SkPoint uvs[4] = {
199                          { SkIntToScalar(src.fLeft),  SkIntToScalar(src.fBottom) },
200                          { SkIntToScalar(src.fLeft),  SkIntToScalar(src.fTop) },
201                          { SkIntToScalar(src.fRight), SkIntToScalar(src.fTop) },
202                          { SkIntToScalar(src.fRight), SkIntToScalar(src.fBottom) },
203                      };
204                      canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
205                                                                4, verts, uvs, nullptr, 6, indices),
206                                           SkBlendMode::kModulate, p2);
207                  } else {
208                      canvas->drawImageRect(fAtlas, src, dst, SkSamplingOptions(), &p,
209                                             SkCanvas::kFast_SrcRectConstraint);
210                  }
211              } else {
212                  canvas->drawImageRect(fCheckerboard, dst, SkSamplingOptions(), &p);
213              }
214          }
215      }
216  
217  private:
218      static const int kCheckerboardWidth = 64;
219      static const int kCheckerboardHeight = 128;
220  
221      static const int kAtlasCellWidth = 48;
222      static const int kAtlasCellHeight = 36;
223      static const int kNumAtlasedX = 5;
224      static const int kNumAtlasedY = 5;
225      static const int kAtlasSpacer = 2;
226      static const int kTotAtlasWidth  = kNumAtlasedX * kAtlasCellWidth +
227                                         (kNumAtlasedX+1) * kAtlasSpacer;
228      static const int kTotAtlasHeight = kNumAtlasedY * kAtlasCellHeight +
229                                         (kNumAtlasedY+1) * kAtlasSpacer;
230      static const int kNumBeforeClear = 100;
231  
232      Type     fType;
233      Clear    fClear;
234      bool     fAligned;
235      bool     fUseAtlas;
236      bool     fUseDrawVertices;
237      SkString fName;
238      int      fNumSaved; // num draws stored in 'fSaved'
239      bool     fInitialized;
240  
241      // 0 & 1 are always x & y translate. 2 is either scale or rotate.
242      SkScalar fSaved[kNumBeforeClear][3];
243  
244      sk_sp<SkImage> fCheckerboard, 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          SkBitmap bm;
252          bm.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight);
253          for (int y = 0; y < kCheckerboardHeight; ++y) {
254              int even = (y / kCheckSize) % 2;
255  
256              SkPMColor* scanline = bm.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          fCheckerboard = bm.asImage();
267      }
268  
269      // Note: the resulting atlas has transparency
makeAtlas()270      void makeAtlas() {
271          SkRandom rand;
272  
273          SkColor colors[kNumAtlasedX][kNumAtlasedY];
274  
275          for (int y = 0; y < kNumAtlasedY; ++y) {
276              for (int x = 0; x < kNumAtlasedX; ++x) {
277                  colors[x][y] = rand.nextU() | 0xff000000;
278                  fAtlasRects[x][y] = SkIRect::MakeXYWH(kAtlasSpacer + x * (kAtlasCellWidth + kAtlasSpacer),
279                                                        kAtlasSpacer + y * (kAtlasCellHeight + kAtlasSpacer),
280                                                        kAtlasCellWidth,
281                                                        kAtlasCellHeight);
282              }
283          }
284  
285          SkBitmap bm;
286          bm.allocN32Pixels(kTotAtlasWidth, kTotAtlasHeight);
287  
288          for (int y = 0; y < kTotAtlasHeight; ++y) {
289              int colorY = y / (kAtlasCellHeight + kAtlasSpacer);
290              bool inColorY = (y % (kAtlasCellHeight + kAtlasSpacer)) >= kAtlasSpacer;
291  
292              SkPMColor* scanline = bm.getAddr32(0, y);
293  
294              for (int x = 0; x < kTotAtlasWidth; ++x, ++scanline) {
295                  int colorX = x / (kAtlasCellWidth + kAtlasSpacer);
296                  bool inColorX = (x % (kAtlasCellWidth + kAtlasSpacer)) >= kAtlasSpacer;
297  
298                  if (inColorX && inColorY) {
299                      SkASSERT(colorX < kNumAtlasedX && colorY < kNumAtlasedY);
300                      *scanline = colors[colorX][colorY];
301                  } else {
302                      *scanline = 0x00000000;
303                  }
304              }
305          }
306          fAtlas = bm.asImage();
307      }
308  
309      using INHERITED = Benchmark;
310  };
311  
312  // Partial clear
313  DEF_BENCH(return new GameBench(GameBench::kScale_Type, GameBench::kPartial_Clear);)
314  DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kPartial_Clear);)
315  DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kPartial_Clear, true);)
316  DEF_BENCH(return new GameBench(GameBench::kRotate_Type, GameBench::kPartial_Clear);)
317  
318  // Full clear
319  DEF_BENCH(return new GameBench(GameBench::kScale_Type, GameBench::kFull_Clear);)
320  DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear);)
321  DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear, true);)
322  DEF_BENCH(return new GameBench(GameBench::kRotate_Type, GameBench::kFull_Clear);)
323  
324  // Atlased
325  DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear, false, true);)
326  DEF_BENCH(return new GameBench(
327                           GameBench::kTranslate_Type, GameBench::kFull_Clear, false, true, true);)
328  
329  
330  class CanvasMatrixBench : public Benchmark {
331      SkString fName;
332  public:
333      enum Type {
334          kTranslate_Type,
335          kScale_Type,
336          k2x3_Type,
337          k3x3_Type,
338          k4x4_Type,
339      };
340      Type fType;
341  
CanvasMatrixBench(Type t)342      CanvasMatrixBench(Type t) : fType(t) {
343          fName.set("canvas_matrix");
344          switch (fType) {
345              case kTranslate_Type: fName.append("_trans"); break;
346              case kScale_Type:     fName.append("_scale"); break;
347              case k2x3_Type:       fName.append("_2x3"); break;
348              case k3x3_Type:       fName.append("_3x3"); break;
349              case k4x4_Type:       fName.append("_4x4"); break;
350          }
351      }
352  
353  protected:
onGetName()354      const char* onGetName() override {
355          return fName.c_str();
356      }
357  
onDraw(int loops,SkCanvas * canvas)358      void onDraw(int loops, SkCanvas* canvas) override {
359          SkMatrix m;
360          m.setRotate(1);
361          if (fType == k3x3_Type) {
362              m[7] = 0.0001f;
363          }
364          SkM44 m4(m);
365  
366          for (int i = 0; i < loops; ++i) {
367              canvas->save();
368              for (int j = 0; j < 10000; ++j) {
369                  switch (fType) {
370                      case kTranslate_Type: canvas->translate(0.0001f, 0.0001f); break;
371                      case kScale_Type:     canvas->scale(1.0001f, 0.9999f); break;
372                      case k2x3_Type:       canvas->concat(m); break;
373                      case k3x3_Type:       canvas->concat(m); break;
374                      case k4x4_Type:       canvas->concat(m4); break;
375                  }
376              }
377              canvas->restore();
378          }
379      }
380  
381  private:
382      using INHERITED = Benchmark;
383  };
384  
385  DEF_BENCH(return new CanvasMatrixBench(CanvasMatrixBench::kTranslate_Type));
386  DEF_BENCH(return new CanvasMatrixBench(CanvasMatrixBench::kScale_Type));
387  DEF_BENCH(return new CanvasMatrixBench(CanvasMatrixBench::k2x3_Type));
388  DEF_BENCH(return new CanvasMatrixBench(CanvasMatrixBench::k3x3_Type));
389  DEF_BENCH(return new CanvasMatrixBench(CanvasMatrixBench::k4x4_Type));
390