blob: c37f46df3028aebd7b5f5153988a99311890464d [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
tfarinaf168b862014-06-19 12:32:29 -07008#include "Benchmark.h"
robertphillips@google.com6670ab92013-05-09 19:03:48 +00009#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
tfarinaf168b862014-06-19 12:32:29 -070017class GameBench : public Benchmark {
robertphillips@google.com6670ab92013-05-09 19:03:48 +000018public:
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
mtklein@google.com410e6e82013-09-13 19:52:27 +000030 GameBench(Type type, Clear clear,
robertphillips@google.com631a59b2013-07-31 14:57:53 +000031 bool aligned = false, bool useAtlas = false,
32 bool useDrawVertices = false)
mtklein@google.com410e6e82013-09-13 19:52:27 +000033 : fType(type)
robertphillips@google.comf865be32013-05-31 13:27:02 +000034 , fClear(clear)
robertphillips@google.com347fd4e2013-05-15 14:18:32 +000035 , fAligned(aligned)
robertphillips@google.comf865be32013-05-31 13:27:02 +000036 , fUseAtlas(useAtlas)
robertphillips@google.com631a59b2013-07-31 14:57:53 +000037 , fUseDrawVertices(useDrawVertices)
robertphillips@google.com6670ab92013-05-09 19:03:48 +000038 , 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
robertphillips@google.com347fd4e2013-05-15 14:18:32 +000054 if (aligned) {
55 fName.append("_aligned");
56 }
57
robertphillips@google.comf865be32013-05-31 13:27:02 +000058 if (kPartial_Clear == clear) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +000059 fName.append("_partial");
60 } else {
61 fName.append("_full");
62 }
63
robertphillips@google.comf865be32013-05-31 13:27:02 +000064 if (useAtlas) {
65 fName.append("_atlas");
66 }
67
robertphillips@google.com631a59b2013-07-31 14:57:53 +000068 if (useDrawVertices) {
69 fName.append("_drawVerts");
70 }
71
robertphillips@google.com6670ab92013-05-09 19:03:48 +000072 // It's HTML 5 canvas, so always AA
73 fName.append("_aa");
74 }
75
76protected:
77 virtual const char* onGetName() SK_OVERRIDE {
robertphillips@google.comf865be32013-05-31 13:27:02 +000078 return fName.c_str();
robertphillips@google.com6670ab92013-05-09 19:03:48 +000079 }
80
81 virtual void onPreDraw() SK_OVERRIDE {
82 if (!fInitialized) {
83 this->makeCheckerboard();
robertphillips@google.comf865be32013-05-31 13:27:02 +000084 this->makeAtlas();
robertphillips@google.com6670ab92013-05-09 19:03:48 +000085 fInitialized = true;
86 }
87 }
88
commit-bot@chromium.org33614712013-12-03 18:17:16 +000089 virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000090 SkRandom scaleRand;
91 SkRandom transRand;
92 SkRandom rotRand;
robertphillips@google.com6670ab92013-05-09 19:03:48 +000093
robertphillips@google.comf865be32013-05-31 13:27:02 +000094 int width, height;
95 if (fUseAtlas) {
96 width = kAtlasCellWidth;
97 height = kAtlasCellHeight;
98 } else {
99 width = kCheckerboardWidth;
100 height = kCheckerboardHeight;
101 }
102
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000103 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) {
robertphillips@google.comf865be32013-05-31 13:27:02 +0000112 maxTransX = size.fWidth - (1.5f * width);
113 maxTransY = size.fHeight - (1.5f * height);
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000114 } else if (kTranslate_Type == fType) {
robertphillips@google.comf865be32013-05-31 13:27:02 +0000115 maxTransX = SkIntToScalar(size.fWidth - width);
116 maxTransY = SkIntToScalar(size.fHeight - height);
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000117 } else {
118 SkASSERT(kRotate_Type == fType);
119 // Yes, some rotations will be off the top and left sides
robertphillips@google.comf865be32013-05-31 13:27:02 +0000120 maxTransX = size.fWidth - SK_ScalarSqrt2 * height;
121 maxTransY = size.fHeight - SK_ScalarSqrt2 * height;
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000122 }
123
124 SkMatrix mat;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000125 SkRect dst = { 0, 0, SkIntToScalar(width), SkIntToScalar(height) };
126 SkRect clearRect = { -1.0f, -1.0f, width+1.0f, height+1.0f };
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000127 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 };
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000134
135 SkPaint p;
136 p.setColor(0xFF000000);
reed@google.com44699382013-10-31 17:28:30 +0000137 p.setFilterLevel(SkPaint::kLow_FilterLevel);
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000138
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000139 SkPaint p2; // for drawVertices path
140 p2.setColor(0xFF000000);
reed@google.com44699382013-10-31 17:28:30 +0000141 p2.setFilterLevel(SkPaint::kLow_FilterLevel);
skia.committer@gmail.com5d4b7732013-08-01 07:01:05 +0000142 p2.setShader(SkShader::CreateBitmapShader(fAtlas,
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000143 SkShader::kClamp_TileMode,
144 SkShader::kClamp_TileMode))->unref();
145
commit-bot@chromium.org33614712013-12-03 18:17:16 +0000146 for (int i = 0; i < loops; ++i, ++fNumSaved) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000147 if (0 == i % kNumBeforeClear) {
robertphillips@google.comf865be32013-05-31 13:27:02 +0000148 if (kPartial_Clear == fClear) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000149 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);
robertphillips@google.com347fd4e2013-05-15 14:18:32 +0000175 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 }
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000180
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);
skia.committer@gmail.com26da7f02013-06-01 07:01:39 +0000192 if (fUseAtlas) {
commit-bot@chromium.org44e3f712014-05-07 17:12:55 +0000193 const int curCell = i % (kNumAtlasedX * kNumAtlasedY);
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000194 SkIRect src = fAtlasRects[curCell % (kNumAtlasedX)][curCell / (kNumAtlasedX)];
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000195
196 if (fUseDrawVertices) {
skia.committer@gmail.com5d4b7732013-08-01 07:01:05 +0000197 SkPoint uvs[4] = {
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000198 { 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,
skia.committer@gmail.com5d4b7732013-08-01 07:01:05 +0000204 4, verts, uvs, NULL, NULL,
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000205 indices, 6, p2);
206 } else {
robertphillips@google.com3c3c4622013-08-21 16:30:39 +0000207 canvas->drawBitmapRect(fAtlas, &src, dst, &p,
208 SkCanvas::kBleed_DrawBitmapRectFlag);
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000209 }
robertphillips@google.comf865be32013-05-31 13:27:02 +0000210 } else {
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000211 canvas->drawBitmapRect(fCheckerboard, NULL, dst, &p);
robertphillips@google.comf865be32013-05-31 13:27:02 +0000212 }
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000213 }
214 }
215
216private:
217 static const int kCheckerboardWidth = 64;
218 static const int kCheckerboardHeight = 128;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000219
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;
mtklein@google.comc2897432013-09-10 19:23:38 +0000229 static const int kNumBeforeClear = 100;
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000230
231 Type fType;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000232 Clear fClear;
robertphillips@google.com347fd4e2013-05-15 14:18:32 +0000233 bool fAligned;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000234 bool fUseAtlas;
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000235 bool fUseDrawVertices;
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000236 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];
robertphillips@google.comf865be32013-05-31 13:27:02 +0000242
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000243 SkBitmap fCheckerboard;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000244 SkBitmap fAtlas;
245 SkIRect fAtlasRects[kNumAtlasedX][kNumAtlasedY];
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000246
247 // Note: the resulting checker board has transparency
248 void makeCheckerboard() {
robertphillips@google.com6d58ad32013-05-09 19:08:57 +0000249 static int kCheckSize = 16;
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000250
reed6c225732014-06-09 19:52:07 -0700251 fCheckerboard.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight);
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000252 SkAutoLockPixels lock(fCheckerboard);
robertphillips@google.com6d58ad32013-05-09 19:08:57 +0000253 for (int y = 0; y < kCheckerboardHeight; ++y) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000254 int even = (y / kCheckSize) % 2;
255
256 SkPMColor* scanline = fCheckerboard.getAddr32(0, y);
257
robertphillips@google.com6d58ad32013-05-09 19:08:57 +0000258 for (int x = 0; x < kCheckerboardWidth; ++x) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000259 if (even == (x / kCheckSize) % 2) {
260 *scanline++ = 0xFFFF0000;
261 } else {
262 *scanline++ = 0x00000000;
263 }
264 }
265 }
266 }
267
robertphillips@google.comf865be32013-05-31 13:27:02 +0000268 // Note: the resulting atlas has transparency
269 void makeAtlas() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000270 SkRandom rand;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000271
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
reed6c225732014-06-09 19:52:07 -0700284 fAtlas.allocN32Pixels(kTotAtlasWidth, kTotAtlasHeight);
robertphillips@google.comf865be32013-05-31 13:27:02 +0000285 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
tfarinaf168b862014-06-19 12:32:29 -0700307 typedef Benchmark INHERITED;
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000308};
309
310// Partial clear
mtklein@google.com410e6e82013-09-13 19:52:27 +0000311DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kScale_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000312 GameBench::kPartial_Clear)); )
mtklein@google.com410e6e82013-09-13 19:52:27 +0000313DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000314 GameBench::kPartial_Clear)); )
mtklein@google.com410e6e82013-09-13 19:52:27 +0000315DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000316 GameBench::kPartial_Clear, true)); )
mtklein@google.com410e6e82013-09-13 19:52:27 +0000317DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kRotate_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000318 GameBench::kPartial_Clear)); )
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000319
320// Full clear
mtklein@google.com410e6e82013-09-13 19:52:27 +0000321DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kScale_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000322 GameBench::kFull_Clear)); )
mtklein@google.com410e6e82013-09-13 19:52:27 +0000323DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000324 GameBench::kFull_Clear)); )
mtklein@google.com410e6e82013-09-13 19:52:27 +0000325DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000326 GameBench::kFull_Clear, true)); )
mtklein@google.com410e6e82013-09-13 19:52:27 +0000327DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kRotate_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000328 GameBench::kFull_Clear)); )
329
330// Atlased
mtklein@google.com410e6e82013-09-13 19:52:27 +0000331DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
robertphillips@google.comf865be32013-05-31 13:27:02 +0000332 GameBench::kFull_Clear, false, true)); )
mtklein@google.com410e6e82013-09-13 19:52:27 +0000333DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type,
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000334 GameBench::kFull_Clear, false, true, true)); )