blob: 5fc9c032884d7d4fdfb33b1fb7a43ff0c50994e7 [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"
robertphillips@google.com631a59b2013-07-31 14:57:53 +000012#include "SkShader.h"
robertphillips@google.com6670ab92013-05-09 19:03:48 +000013#include "SkString.h"
14
robertphillips@google.com6670ab92013-05-09 19:03:48 +000015// This bench simulates the calls Skia sees from various HTML5 canvas
16// game bench marks
17class GameBench : public SkBenchmark {
18public:
19 enum Type {
skia.committer@gmail.com0f20a3f2013-05-10 07:01:04 +000020 kScale_Type,
21 kTranslate_Type,
robertphillips@google.com6670ab92013-05-09 19:03:48 +000022 kRotate_Type
23 };
24
robertphillips@google.comf865be32013-05-31 13:27:02 +000025 enum Clear {
26 kFull_Clear,
27 kPartial_Clear
28 };
29
skia.committer@gmail.com26da7f02013-06-01 07:01:39 +000030 GameBench(void* param, Type type, Clear clear,
robertphillips@google.com631a59b2013-07-31 14:57:53 +000031 bool aligned = false, bool useAtlas = false,
32 bool useDrawVertices = false)
robertphillips@google.com6670ab92013-05-09 19:03:48 +000033 : INHERITED(param)
34 , fType(type)
robertphillips@google.comf865be32013-05-31 13:27:02 +000035 , fClear(clear)
robertphillips@google.com347fd4e2013-05-15 14:18:32 +000036 , fAligned(aligned)
robertphillips@google.comf865be32013-05-31 13:27:02 +000037 , fUseAtlas(useAtlas)
robertphillips@google.com631a59b2013-07-31 14:57:53 +000038 , fUseDrawVertices(useDrawVertices)
robertphillips@google.com6670ab92013-05-09 19:03:48 +000039 , fName("game")
40 , fNumSaved(0)
41 , fInitialized(false) {
42
43 switch (fType) {
44 case kScale_Type:
45 fName.append("_scale");
46 break;
47 case kTranslate_Type:
48 fName.append("_trans");
49 break;
50 case kRotate_Type:
51 fName.append("_rot");
52 break;
53 };
54
robertphillips@google.com347fd4e2013-05-15 14:18:32 +000055 if (aligned) {
56 fName.append("_aligned");
57 }
58
robertphillips@google.comf865be32013-05-31 13:27:02 +000059 if (kPartial_Clear == clear) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +000060 fName.append("_partial");
61 } else {
62 fName.append("_full");
63 }
64
robertphillips@google.comf865be32013-05-31 13:27:02 +000065 if (useAtlas) {
66 fName.append("_atlas");
67 }
68
robertphillips@google.com631a59b2013-07-31 14:57:53 +000069 if (useDrawVertices) {
70 fName.append("_drawVerts");
71 }
72
robertphillips@google.com6670ab92013-05-09 19:03:48 +000073 // It's HTML 5 canvas, so always AA
74 fName.append("_aa");
75 }
76
77protected:
78 virtual const char* onGetName() SK_OVERRIDE {
robertphillips@google.comf865be32013-05-31 13:27:02 +000079 return fName.c_str();
robertphillips@google.com6670ab92013-05-09 19:03:48 +000080 }
81
82 virtual void onPreDraw() SK_OVERRIDE {
83 if (!fInitialized) {
84 this->makeCheckerboard();
robertphillips@google.comf865be32013-05-31 13:27:02 +000085 this->makeAtlas();
robertphillips@google.com6670ab92013-05-09 19:03:48 +000086 fInitialized = true;
87 }
88 }
89
90 virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000091 SkRandom scaleRand;
92 SkRandom transRand;
93 SkRandom rotRand;
robertphillips@google.com6670ab92013-05-09 19:03:48 +000094
robertphillips@google.comf865be32013-05-31 13:27:02 +000095 int width, height;
96 if (fUseAtlas) {
97 width = kAtlasCellWidth;
98 height = kAtlasCellHeight;
99 } else {
100 width = kCheckerboardWidth;
101 height = kCheckerboardHeight;
102 }
103
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000104 SkPaint clearPaint;
105 clearPaint.setColor(0xFF000000);
106 clearPaint.setAntiAlias(true);
107
108 SkISize size = canvas->getDeviceSize();
109
110 SkScalar maxTransX, maxTransY;
111
112 if (kScale_Type == fType) {
robertphillips@google.comf865be32013-05-31 13:27:02 +0000113 maxTransX = size.fWidth - (1.5f * width);
114 maxTransY = size.fHeight - (1.5f * height);
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000115 } else if (kTranslate_Type == fType) {
robertphillips@google.comf865be32013-05-31 13:27:02 +0000116 maxTransX = SkIntToScalar(size.fWidth - width);
117 maxTransY = SkIntToScalar(size.fHeight - height);
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000118 } else {
119 SkASSERT(kRotate_Type == fType);
120 // Yes, some rotations will be off the top and left sides
robertphillips@google.comf865be32013-05-31 13:27:02 +0000121 maxTransX = size.fWidth - SK_ScalarSqrt2 * height;
122 maxTransY = size.fHeight - SK_ScalarSqrt2 * height;
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000123 }
124
125 SkMatrix mat;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000126 SkRect dst = { 0, 0, SkIntToScalar(width), SkIntToScalar(height) };
127 SkRect clearRect = { -1.0f, -1.0f, width+1.0f, height+1.0f };
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000128 SkPoint verts[4] = { // for drawVertices path
129 { 0, 0 },
130 { 0, SkIntToScalar(height) },
131 { SkIntToScalar(width), SkIntToScalar(height) },
132 { SkIntToScalar(width), 0 }
133 };
134 uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 };
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000135
136 SkPaint p;
137 p.setColor(0xFF000000);
138 p.setFilterBitmap(true);
139
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000140 SkPaint p2; // for drawVertices path
141 p2.setColor(0xFF000000);
142 p2.setFilterBitmap(true);
skia.committer@gmail.com5d4b7732013-08-01 07:01:05 +0000143 p2.setShader(SkShader::CreateBitmapShader(fAtlas,
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000144 SkShader::kClamp_TileMode,
145 SkShader::kClamp_TileMode))->unref();
146
mtklein@google.comc2897432013-09-10 19:23:38 +0000147 for (int i = 0; i < this->getLoops(); ++i, ++fNumSaved) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000148 if (0 == i % kNumBeforeClear) {
robertphillips@google.comf865be32013-05-31 13:27:02 +0000149 if (kPartial_Clear == fClear) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000150 for (int j = 0; j < fNumSaved; ++j) {
151 canvas->setMatrix(SkMatrix::I());
152 mat.setTranslate(fSaved[j][0], fSaved[j][1]);
153
154 if (kScale_Type == fType) {
155 mat.preScale(fSaved[j][2], fSaved[j][2]);
156 } else if (kRotate_Type == fType) {
157 mat.preRotate(fSaved[j][2]);
158 }
159
160 canvas->concat(mat);
161 canvas->drawRect(clearRect, clearPaint);
162 }
163 } else {
164 canvas->clear(0xFF000000);
165 }
166
167 fNumSaved = 0;
168 }
169
170 SkASSERT(fNumSaved < kNumBeforeClear);
171
172 canvas->setMatrix(SkMatrix::I());
173
174 fSaved[fNumSaved][0] = transRand.nextRangeScalar(0.0f, maxTransX);
175 fSaved[fNumSaved][1] = transRand.nextRangeScalar(0.0f, maxTransY);
robertphillips@google.com347fd4e2013-05-15 14:18:32 +0000176 if (fAligned) {
177 // make the translations integer aligned
178 fSaved[fNumSaved][0] = SkScalarFloorToScalar(fSaved[fNumSaved][0]);
179 fSaved[fNumSaved][1] = SkScalarFloorToScalar(fSaved[fNumSaved][1]);
180 }
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000181
182 mat.setTranslate(fSaved[fNumSaved][0], fSaved[fNumSaved][1]);
183
184 if (kScale_Type == fType) {
185 fSaved[fNumSaved][2] = scaleRand.nextRangeScalar(0.5f, 1.5f);
186 mat.preScale(fSaved[fNumSaved][2], fSaved[fNumSaved][2]);
187 } else if (kRotate_Type == fType) {
188 fSaved[fNumSaved][2] = rotRand.nextRangeScalar(0.0f, 360.0f);
189 mat.preRotate(fSaved[fNumSaved][2]);
190 }
191
192 canvas->concat(mat);
skia.committer@gmail.com26da7f02013-06-01 07:01:39 +0000193 if (fUseAtlas) {
robertphillips@google.comf865be32013-05-31 13:27:02 +0000194 static int curCell = 0;
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000195 SkIRect src = fAtlasRects[curCell % (kNumAtlasedX)][curCell / (kNumAtlasedX)];
robertphillips@google.comf865be32013-05-31 13:27:02 +0000196 curCell = (curCell + 1) % (kNumAtlasedX*kNumAtlasedY);
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000197
198 if (fUseDrawVertices) {
skia.committer@gmail.com5d4b7732013-08-01 07:01:05 +0000199 SkPoint uvs[4] = {
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000200 { SkIntToScalar(src.fLeft), SkIntToScalar(src.fBottom) },
201 { SkIntToScalar(src.fLeft), SkIntToScalar(src.fTop) },
202 { SkIntToScalar(src.fRight), SkIntToScalar(src.fTop) },
203 { SkIntToScalar(src.fRight), SkIntToScalar(src.fBottom) },
204 };
205 canvas->drawVertices(SkCanvas::kTriangles_VertexMode,
skia.committer@gmail.com5d4b7732013-08-01 07:01:05 +0000206 4, verts, uvs, NULL, NULL,
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000207 indices, 6, p2);
208 } else {
robertphillips@google.com3c3c4622013-08-21 16:30:39 +0000209 canvas->drawBitmapRect(fAtlas, &src, dst, &p,
210 SkCanvas::kBleed_DrawBitmapRectFlag);
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000211 }
robertphillips@google.comf865be32013-05-31 13:27:02 +0000212 } else {
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000213 canvas->drawBitmapRect(fCheckerboard, NULL, dst, &p);
robertphillips@google.comf865be32013-05-31 13:27:02 +0000214 }
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000215 }
216 }
217
218private:
219 static const int kCheckerboardWidth = 64;
220 static const int kCheckerboardHeight = 128;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000221
222 static const int kAtlasCellWidth = 48;
223 static const int kAtlasCellHeight = 36;
224 static const int kNumAtlasedX = 5;
225 static const int kNumAtlasedY = 5;
226 static const int kAtlasSpacer = 2;
227 static const int kTotAtlasWidth = kNumAtlasedX * kAtlasCellWidth +
228 (kNumAtlasedX+1) * kAtlasSpacer;
229 static const int kTotAtlasHeight = kNumAtlasedY * kAtlasCellHeight +
230 (kNumAtlasedY+1) * kAtlasSpacer;
mtklein@google.comc2897432013-09-10 19:23:38 +0000231 static const int kNumBeforeClear = 100;
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000232
233 Type fType;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000234 Clear fClear;
robertphillips@google.com347fd4e2013-05-15 14:18:32 +0000235 bool fAligned;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000236 bool fUseAtlas;
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000237 bool fUseDrawVertices;
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000238 SkString fName;
239 int fNumSaved; // num draws stored in 'fSaved'
240 bool fInitialized;
241
242 // 0 & 1 are always x & y translate. 2 is either scale or rotate.
243 SkScalar fSaved[kNumBeforeClear][3];
robertphillips@google.comf865be32013-05-31 13:27:02 +0000244
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000245 SkBitmap fCheckerboard;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000246 SkBitmap fAtlas;
247 SkIRect fAtlasRects[kNumAtlasedX][kNumAtlasedY];
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000248
249 // Note: the resulting checker board has transparency
250 void makeCheckerboard() {
robertphillips@google.com6d58ad32013-05-09 19:08:57 +0000251 static int kCheckSize = 16;
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000252
253 fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config,
254 kCheckerboardWidth, kCheckerboardHeight);
255 fCheckerboard.allocPixels();
256 SkAutoLockPixels lock(fCheckerboard);
robertphillips@google.com6d58ad32013-05-09 19:08:57 +0000257 for (int y = 0; y < kCheckerboardHeight; ++y) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000258 int even = (y / kCheckSize) % 2;
259
260 SkPMColor* scanline = fCheckerboard.getAddr32(0, y);
261
robertphillips@google.com6d58ad32013-05-09 19:08:57 +0000262 for (int x = 0; x < kCheckerboardWidth; ++x) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000263 if (even == (x / kCheckSize) % 2) {
264 *scanline++ = 0xFFFF0000;
265 } else {
266 *scanline++ = 0x00000000;
267 }
268 }
269 }
270 }
271
robertphillips@google.comf865be32013-05-31 13:27:02 +0000272 // Note: the resulting atlas has transparency
273 void makeAtlas() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000274 SkRandom rand;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000275
276 SkColor colors[kNumAtlasedX][kNumAtlasedY];
277
278 for (int y = 0; y < kNumAtlasedY; ++y) {
279 for (int x = 0; x < kNumAtlasedX; ++x) {
280 colors[x][y] = rand.nextU() | 0xff000000;
281 fAtlasRects[x][y] = SkIRect::MakeXYWH(kAtlasSpacer + x * (kAtlasCellWidth + kAtlasSpacer),
282 kAtlasSpacer + y * (kAtlasCellHeight + kAtlasSpacer),
283 kAtlasCellWidth,
284 kAtlasCellHeight);
285 }
286 }
287
robertphillips@google.comf865be32013-05-31 13:27:02 +0000288 fAtlas.setConfig(SkBitmap::kARGB_8888_Config, kTotAtlasWidth, kTotAtlasHeight);
289 fAtlas.allocPixels();
290 SkAutoLockPixels lock(fAtlas);
291
292 for (int y = 0; y < kTotAtlasHeight; ++y) {
293 int colorY = y / (kAtlasCellHeight + kAtlasSpacer);
294 bool inColorY = (y % (kAtlasCellHeight + kAtlasSpacer)) >= kAtlasSpacer;
295
296 SkPMColor* scanline = fAtlas.getAddr32(0, y);
297
298 for (int x = 0; x < kTotAtlasWidth; ++x, ++scanline) {
299 int colorX = x / (kAtlasCellWidth + kAtlasSpacer);
300 bool inColorX = (x % (kAtlasCellWidth + kAtlasSpacer)) >= kAtlasSpacer;
301
302 if (inColorX && inColorY) {
303 SkASSERT(colorX < kNumAtlasedX && colorY < kNumAtlasedY);
304 *scanline = colors[colorX][colorY];
305 } else {
306 *scanline = 0x00000000;
307 }
308 }
309 }
310 }
311
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000312 typedef SkBenchmark INHERITED;
313};
314
315// Partial clear
skia.committer@gmail.com26da7f02013-06-01 07:01:39 +0000316DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kScale_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000317 GameBench::kPartial_Clear)); )
skia.committer@gmail.com26da7f02013-06-01 07:01:39 +0000318DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kTranslate_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000319 GameBench::kPartial_Clear)); )
skia.committer@gmail.com26da7f02013-06-01 07:01:39 +0000320DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kTranslate_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000321 GameBench::kPartial_Clear, true)); )
skia.committer@gmail.com26da7f02013-06-01 07:01:39 +0000322DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kRotate_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000323 GameBench::kPartial_Clear)); )
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000324
325// Full clear
skia.committer@gmail.com26da7f02013-06-01 07:01:39 +0000326DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kScale_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000327 GameBench::kFull_Clear)); )
skia.committer@gmail.com26da7f02013-06-01 07:01:39 +0000328DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kTranslate_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000329 GameBench::kFull_Clear)); )
skia.committer@gmail.com26da7f02013-06-01 07:01:39 +0000330DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kTranslate_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000331 GameBench::kFull_Clear, true)); )
skia.committer@gmail.com26da7f02013-06-01 07:01:39 +0000332DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kRotate_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000333 GameBench::kFull_Clear)); )
334
335// Atlased
skia.committer@gmail.com26da7f02013-06-01 07:01:39 +0000336DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kTranslate_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000337 GameBench::kFull_Clear, false, true)); )
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000338DEF_BENCH( return SkNEW_ARGS(GameBench, (p, GameBench::kTranslate_Type,
339 GameBench::kFull_Clear, false, true, true)); )