blob: ba7f76550ee59c5805b6b5bb051bfb903676edac [file] [log] [blame]
robertphillips@google.com6670ab92013-05-09 19:03:48 +00001/*
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 "SkBenchmark.h"
9#include "SkCanvas.h"
10#include "SkPaint.h"
11#include "SkRandom.h"
12#include "SkString.h"
13
robertphillips@google.com6670ab92013-05-09 19:03:48 +000014// This bench simulates the calls Skia sees from various HTML5 canvas
15// game bench marks
16class GameBench : public SkBenchmark {
17public:
18 enum Type {
skia.committer@gmail.com0f20a3f2013-05-10 07:01:04 +000019 kScale_Type,
20 kTranslate_Type,
robertphillips@google.com6670ab92013-05-09 19:03:48 +000021 kRotate_Type
22 };
23
robertphillips@google.comf865be32013-05-31 13:27:02 +000024 enum Clear {
25 kFull_Clear,
26 kPartial_Clear
27 };
28
29 GameBench(void* param, Type type, Clear clear,
30 bool aligned = false, bool useAtlas = false)
robertphillips@google.com6670ab92013-05-09 19:03:48 +000031 : INHERITED(param)
32 , fType(type)
robertphillips@google.comf865be32013-05-31 13:27:02 +000033 , fClear(clear)
robertphillips@google.com347fd4e2013-05-15 14:18:32 +000034 , fAligned(aligned)
robertphillips@google.comf865be32013-05-31 13:27:02 +000035 , fUseAtlas(useAtlas)
robertphillips@google.com6670ab92013-05-09 19:03:48 +000036 , fName("game")
37 , fNumSaved(0)
38 , fInitialized(false) {
39
40 switch (fType) {
41 case kScale_Type:
42 fName.append("_scale");
43 break;
44 case kTranslate_Type:
45 fName.append("_trans");
46 break;
47 case kRotate_Type:
48 fName.append("_rot");
49 break;
50 };
51
robertphillips@google.com347fd4e2013-05-15 14:18:32 +000052 if (aligned) {
53 fName.append("_aligned");
54 }
55
robertphillips@google.comf865be32013-05-31 13:27:02 +000056 if (kPartial_Clear == clear) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +000057 fName.append("_partial");
58 } else {
59 fName.append("_full");
60 }
61
robertphillips@google.comf865be32013-05-31 13:27:02 +000062 if (useAtlas) {
63 fName.append("_atlas");
64 }
65
robertphillips@google.com6670ab92013-05-09 19:03:48 +000066 // It's HTML 5 canvas, so always AA
67 fName.append("_aa");
68 }
69
70protected:
71 virtual const char* onGetName() SK_OVERRIDE {
robertphillips@google.comf865be32013-05-31 13:27:02 +000072 return fName.c_str();
robertphillips@google.com6670ab92013-05-09 19:03:48 +000073 }
74
75 virtual void onPreDraw() SK_OVERRIDE {
76 if (!fInitialized) {
77 this->makeCheckerboard();
robertphillips@google.comf865be32013-05-31 13:27:02 +000078 this->makeAtlas();
robertphillips@google.com6670ab92013-05-09 19:03:48 +000079 fInitialized = true;
80 }
81 }
82
83 virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
84 static SkMWCRandom scaleRand;
85 static SkMWCRandom transRand;
86 static SkMWCRandom rotRand;
87
robertphillips@google.comf865be32013-05-31 13:27:02 +000088 int width, height;
89 if (fUseAtlas) {
90 width = kAtlasCellWidth;
91 height = kAtlasCellHeight;
92 } else {
93 width = kCheckerboardWidth;
94 height = kCheckerboardHeight;
95 }
96
robertphillips@google.com6670ab92013-05-09 19:03:48 +000097 SkPaint clearPaint;
98 clearPaint.setColor(0xFF000000);
99 clearPaint.setAntiAlias(true);
100
101 SkISize size = canvas->getDeviceSize();
102
103 SkScalar maxTransX, maxTransY;
104
105 if (kScale_Type == fType) {
robertphillips@google.comf865be32013-05-31 13:27:02 +0000106 maxTransX = size.fWidth - (1.5f * width);
107 maxTransY = size.fHeight - (1.5f * height);
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000108 } else if (kTranslate_Type == fType) {
robertphillips@google.comf865be32013-05-31 13:27:02 +0000109 maxTransX = SkIntToScalar(size.fWidth - width);
110 maxTransY = SkIntToScalar(size.fHeight - height);
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000111 } else {
112 SkASSERT(kRotate_Type == fType);
113 // Yes, some rotations will be off the top and left sides
robertphillips@google.comf865be32013-05-31 13:27:02 +0000114 maxTransX = size.fWidth - SK_ScalarSqrt2 * height;
115 maxTransY = size.fHeight - SK_ScalarSqrt2 * height;
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000116 }
117
118 SkMatrix mat;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000119 SkIRect src = { 0, 0, width, height };
120 SkRect dst = { 0, 0, SkIntToScalar(width), SkIntToScalar(height) };
121 SkRect clearRect = { -1.0f, -1.0f, width+1.0f, height+1.0f };
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000122
123 SkPaint p;
124 p.setColor(0xFF000000);
125 p.setFilterBitmap(true);
126
127 for (int i = 0; i < kNumRects; ++i, ++fNumSaved) {
128
129 if (0 == i % kNumBeforeClear) {
robertphillips@google.comf865be32013-05-31 13:27:02 +0000130 if (kPartial_Clear == fClear) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000131 for (int j = 0; j < fNumSaved; ++j) {
132 canvas->setMatrix(SkMatrix::I());
133 mat.setTranslate(fSaved[j][0], fSaved[j][1]);
134
135 if (kScale_Type == fType) {
136 mat.preScale(fSaved[j][2], fSaved[j][2]);
137 } else if (kRotate_Type == fType) {
138 mat.preRotate(fSaved[j][2]);
139 }
140
141 canvas->concat(mat);
142 canvas->drawRect(clearRect, clearPaint);
143 }
144 } else {
145 canvas->clear(0xFF000000);
146 }
147
148 fNumSaved = 0;
149 }
150
151 SkASSERT(fNumSaved < kNumBeforeClear);
152
153 canvas->setMatrix(SkMatrix::I());
154
155 fSaved[fNumSaved][0] = transRand.nextRangeScalar(0.0f, maxTransX);
156 fSaved[fNumSaved][1] = transRand.nextRangeScalar(0.0f, maxTransY);
robertphillips@google.com347fd4e2013-05-15 14:18:32 +0000157 if (fAligned) {
158 // make the translations integer aligned
159 fSaved[fNumSaved][0] = SkScalarFloorToScalar(fSaved[fNumSaved][0]);
160 fSaved[fNumSaved][1] = SkScalarFloorToScalar(fSaved[fNumSaved][1]);
161 }
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000162
163 mat.setTranslate(fSaved[fNumSaved][0], fSaved[fNumSaved][1]);
164
165 if (kScale_Type == fType) {
166 fSaved[fNumSaved][2] = scaleRand.nextRangeScalar(0.5f, 1.5f);
167 mat.preScale(fSaved[fNumSaved][2], fSaved[fNumSaved][2]);
168 } else if (kRotate_Type == fType) {
169 fSaved[fNumSaved][2] = rotRand.nextRangeScalar(0.0f, 360.0f);
170 mat.preRotate(fSaved[fNumSaved][2]);
171 }
172
173 canvas->concat(mat);
robertphillips@google.comf865be32013-05-31 13:27:02 +0000174 if (fUseAtlas) {
175 static int curCell = 0;
176 src = fAtlasRects[curCell % (kNumAtlasedX)][curCell / (kNumAtlasedX)];
177 curCell = (curCell + 1) % (kNumAtlasedX*kNumAtlasedY);
178 canvas->drawBitmapRect(fAtlas, &src, dst, &p);
179 } else {
180 canvas->drawBitmapRect(fCheckerboard, &src, dst, &p);
181 }
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000182 }
183 }
184
185private:
186 static const int kCheckerboardWidth = 64;
187 static const int kCheckerboardHeight = 128;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000188
189 static const int kAtlasCellWidth = 48;
190 static const int kAtlasCellHeight = 36;
191 static const int kNumAtlasedX = 5;
192 static const int kNumAtlasedY = 5;
193 static const int kAtlasSpacer = 2;
194 static const int kTotAtlasWidth = kNumAtlasedX * kAtlasCellWidth +
195 (kNumAtlasedX+1) * kAtlasSpacer;
196 static const int kTotAtlasHeight = kNumAtlasedY * kAtlasCellHeight +
197 (kNumAtlasedY+1) * kAtlasSpacer;
198
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000199#ifdef SK_DEBUG
200 static const int kNumRects = 100;
201 static const int kNumBeforeClear = 10;
202#else
203 static const int kNumRects = 5000;
204 static const int kNumBeforeClear = 300;
205#endif
206
207
208 Type fType;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000209 Clear fClear;
robertphillips@google.com347fd4e2013-05-15 14:18:32 +0000210 bool fAligned;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000211 bool fUseAtlas;
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000212 SkString fName;
213 int fNumSaved; // num draws stored in 'fSaved'
214 bool fInitialized;
215
216 // 0 & 1 are always x & y translate. 2 is either scale or rotate.
217 SkScalar fSaved[kNumBeforeClear][3];
robertphillips@google.comf865be32013-05-31 13:27:02 +0000218
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000219 SkBitmap fCheckerboard;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000220 SkBitmap fAtlas;
221 SkIRect fAtlasRects[kNumAtlasedX][kNumAtlasedY];
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000222
223 // Note: the resulting checker board has transparency
224 void makeCheckerboard() {
robertphillips@google.com6d58ad32013-05-09 19:08:57 +0000225 static int kCheckSize = 16;
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000226
227 fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config,
228 kCheckerboardWidth, kCheckerboardHeight);
229 fCheckerboard.allocPixels();
230 SkAutoLockPixels lock(fCheckerboard);
robertphillips@google.com6d58ad32013-05-09 19:08:57 +0000231 for (int y = 0; y < kCheckerboardHeight; ++y) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000232 int even = (y / kCheckSize) % 2;
233
234 SkPMColor* scanline = fCheckerboard.getAddr32(0, y);
235
robertphillips@google.com6d58ad32013-05-09 19:08:57 +0000236 for (int x = 0; x < kCheckerboardWidth; ++x) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000237 if (even == (x / kCheckSize) % 2) {
238 *scanline++ = 0xFFFF0000;
239 } else {
240 *scanline++ = 0x00000000;
241 }
242 }
243 }
244 }
245
robertphillips@google.comf865be32013-05-31 13:27:02 +0000246 // Note: the resulting atlas has transparency
247 void makeAtlas() {
248 SkMWCRandom rand;
249
250 SkColor colors[kNumAtlasedX][kNumAtlasedY];
251
252 for (int y = 0; y < kNumAtlasedY; ++y) {
253 for (int x = 0; x < kNumAtlasedX; ++x) {
254 colors[x][y] = rand.nextU() | 0xff000000;
255 fAtlasRects[x][y] = SkIRect::MakeXYWH(kAtlasSpacer + x * (kAtlasCellWidth + kAtlasSpacer),
256 kAtlasSpacer + y * (kAtlasCellHeight + kAtlasSpacer),
257 kAtlasCellWidth,
258 kAtlasCellHeight);
259 }
260 }
261
262
263 fAtlas.setConfig(SkBitmap::kARGB_8888_Config, kTotAtlasWidth, kTotAtlasHeight);
264 fAtlas.allocPixels();
265 SkAutoLockPixels lock(fAtlas);
266
267 for (int y = 0; y < kTotAtlasHeight; ++y) {
268 int colorY = y / (kAtlasCellHeight + kAtlasSpacer);
269 bool inColorY = (y % (kAtlasCellHeight + kAtlasSpacer)) >= kAtlasSpacer;
270
271 SkPMColor* scanline = fAtlas.getAddr32(0, y);
272
273 for (int x = 0; x < kTotAtlasWidth; ++x, ++scanline) {
274 int colorX = x / (kAtlasCellWidth + kAtlasSpacer);
275 bool inColorX = (x % (kAtlasCellWidth + kAtlasSpacer)) >= kAtlasSpacer;
276
277 if (inColorX && inColorY) {
278 SkASSERT(colorX < kNumAtlasedX && colorY < kNumAtlasedY);
279 *scanline = colors[colorX][colorY];
280 } else {
281 *scanline = 0x00000000;
282 }
283 }
284 }
285 }
286
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000287 typedef SkBenchmark INHERITED;
288};
289
290// Partial clear
robertphillips@google.comf865be32013-05-31 13:27:02 +0000291DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kScale_Type,
292 GameBench::kPartial_Clear)); )
293DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kTranslate_Type,
294 GameBench::kPartial_Clear)); )
295DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kTranslate_Type,
296 GameBench::kPartial_Clear, true)); )
297DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kRotate_Type,
298 GameBench::kPartial_Clear)); )
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000299
300// Full clear
robertphillips@google.comf865be32013-05-31 13:27:02 +0000301DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kScale_Type,
302 GameBench::kFull_Clear)); )
303DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kTranslate_Type,
304 GameBench::kFull_Clear)); )
305DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kTranslate_Type,
306 GameBench::kFull_Clear, true)); )
307DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kRotate_Type,
308 GameBench::kFull_Clear)); )
309
310// Atlased
311DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kTranslate_Type,
312 GameBench::kFull_Clear, false, true)); )