blob: ae0cb7ed1bb8e891ec1a4f60464a1d251ffce49d [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"
Mike Reed75ae4212018-01-23 11:24:08 -05009#include "SkBitmap.h"
robertphillips@google.com6670ab92013-05-09 19:03:48 +000010#include "SkCanvas.h"
11#include "SkPaint.h"
12#include "SkRandom.h"
robertphillips@google.com631a59b2013-07-31 14:57:53 +000013#include "SkShader.h"
robertphillips@google.com6670ab92013-05-09 19:03:48 +000014#include "SkString.h"
Mike Reed887cdf12017-04-03 11:11:09 -040015#include "SkVertices.h"
robertphillips@google.com6670ab92013-05-09 19:03:48 +000016
robertphillips@google.com6670ab92013-05-09 19:03:48 +000017// This bench simulates the calls Skia sees from various HTML5 canvas
18// game bench marks
tfarinaf168b862014-06-19 12:32:29 -070019class GameBench : public Benchmark {
robertphillips@google.com6670ab92013-05-09 19:03:48 +000020public:
21 enum Type {
skia.committer@gmail.com0f20a3f2013-05-10 07:01:04 +000022 kScale_Type,
23 kTranslate_Type,
robertphillips@google.com6670ab92013-05-09 19:03:48 +000024 kRotate_Type
25 };
26
robertphillips@google.comf865be32013-05-31 13:27:02 +000027 enum Clear {
28 kFull_Clear,
29 kPartial_Clear
30 };
31
mtklein@google.com410e6e82013-09-13 19:52:27 +000032 GameBench(Type type, Clear clear,
robertphillips@google.com631a59b2013-07-31 14:57:53 +000033 bool aligned = false, bool useAtlas = false,
34 bool useDrawVertices = false)
mtklein@google.com410e6e82013-09-13 19:52:27 +000035 : fType(type)
robertphillips@google.comf865be32013-05-31 13:27:02 +000036 , fClear(clear)
robertphillips@google.com347fd4e2013-05-15 14:18:32 +000037 , fAligned(aligned)
robertphillips@google.comf865be32013-05-31 13:27:02 +000038 , fUseAtlas(useAtlas)
robertphillips@google.com631a59b2013-07-31 14:57:53 +000039 , fUseDrawVertices(useDrawVertices)
robertphillips@google.com6670ab92013-05-09 19:03:48 +000040 , fName("game")
41 , fNumSaved(0)
42 , fInitialized(false) {
43
44 switch (fType) {
45 case kScale_Type:
46 fName.append("_scale");
47 break;
48 case kTranslate_Type:
49 fName.append("_trans");
50 break;
51 case kRotate_Type:
52 fName.append("_rot");
53 break;
54 };
55
robertphillips@google.com347fd4e2013-05-15 14:18:32 +000056 if (aligned) {
57 fName.append("_aligned");
58 }
59
robertphillips@google.comf865be32013-05-31 13:27:02 +000060 if (kPartial_Clear == clear) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +000061 fName.append("_partial");
62 } else {
63 fName.append("_full");
64 }
65
robertphillips@google.comf865be32013-05-31 13:27:02 +000066 if (useAtlas) {
67 fName.append("_atlas");
68 }
69
robertphillips@google.com631a59b2013-07-31 14:57:53 +000070 if (useDrawVertices) {
71 fName.append("_drawVerts");
72 }
73
robertphillips@google.com6670ab92013-05-09 19:03:48 +000074 // It's HTML 5 canvas, so always AA
75 fName.append("_aa");
76 }
77
78protected:
mtklein36352bf2015-03-25 18:17:31 -070079 const char* onGetName() override {
robertphillips@google.comf865be32013-05-31 13:27:02 +000080 return fName.c_str();
robertphillips@google.com6670ab92013-05-09 19:03:48 +000081 }
82
joshualitt8a6697a2015-09-30 12:11:07 -070083 void onDelayedSetup() override {
robertphillips@google.com6670ab92013-05-09 19:03:48 +000084 if (!fInitialized) {
85 this->makeCheckerboard();
robertphillips@google.comf865be32013-05-31 13:27:02 +000086 this->makeAtlas();
robertphillips@google.com6670ab92013-05-09 19:03:48 +000087 fInitialized = true;
88 }
89 }
90
mtkleina1ebeb22015-10-01 09:43:39 -070091 void onDraw(int loops, SkCanvas* canvas) override {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000092 SkRandom scaleRand;
93 SkRandom transRand;
94 SkRandom rotRand;
robertphillips@google.com6670ab92013-05-09 19:03:48 +000095
robertphillips@google.comf865be32013-05-31 13:27:02 +000096 int width, height;
97 if (fUseAtlas) {
98 width = kAtlasCellWidth;
99 height = kAtlasCellHeight;
100 } else {
101 width = kCheckerboardWidth;
102 height = kCheckerboardHeight;
103 }
104
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000105 SkPaint clearPaint;
106 clearPaint.setColor(0xFF000000);
107 clearPaint.setAntiAlias(true);
108
Mike Reed3661bc92017-02-22 13:21:42 -0500109 SkISize size = canvas->getBaseLayerSize();
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000110
111 SkScalar maxTransX, maxTransY;
112
113 if (kScale_Type == fType) {
robertphillips@google.comf865be32013-05-31 13:27:02 +0000114 maxTransX = size.fWidth - (1.5f * width);
115 maxTransY = size.fHeight - (1.5f * height);
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000116 } else if (kTranslate_Type == fType) {
robertphillips@google.comf865be32013-05-31 13:27:02 +0000117 maxTransX = SkIntToScalar(size.fWidth - width);
118 maxTransY = SkIntToScalar(size.fHeight - height);
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000119 } else {
120 SkASSERT(kRotate_Type == fType);
121 // Yes, some rotations will be off the top and left sides
robertphillips@google.comf865be32013-05-31 13:27:02 +0000122 maxTransX = size.fWidth - SK_ScalarSqrt2 * height;
123 maxTransY = size.fHeight - SK_ScalarSqrt2 * height;
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000124 }
125
126 SkMatrix mat;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000127 SkRect dst = { 0, 0, SkIntToScalar(width), SkIntToScalar(height) };
128 SkRect clearRect = { -1.0f, -1.0f, width+1.0f, height+1.0f };
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000129 SkPoint verts[4] = { // for drawVertices path
130 { 0, 0 },
131 { 0, SkIntToScalar(height) },
132 { SkIntToScalar(width), SkIntToScalar(height) },
133 { SkIntToScalar(width), 0 }
134 };
135 uint16_t indices[6] = { 0, 1, 2, 0, 2, 3 };
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000136
137 SkPaint p;
138 p.setColor(0xFF000000);
reed93a12152015-03-16 10:08:34 -0700139 p.setFilterQuality(kLow_SkFilterQuality);
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000140
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000141 SkPaint p2; // for drawVertices path
142 p2.setColor(0xFF000000);
reed93a12152015-03-16 10:08:34 -0700143 p2.setFilterQuality(kLow_SkFilterQuality);
reedc6f28f72016-03-14 12:22:10 -0700144 p2.setShader(SkShader::MakeBitmapShader(fAtlas,
145 SkShader::kClamp_TileMode,
146 SkShader::kClamp_TileMode));
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000147
commit-bot@chromium.org33614712013-12-03 18:17:16 +0000148 for (int i = 0; i < loops; ++i, ++fNumSaved) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000149 if (0 == i % kNumBeforeClear) {
robertphillips@google.comf865be32013-05-31 13:27:02 +0000150 if (kPartial_Clear == fClear) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000151 for (int j = 0; j < fNumSaved; ++j) {
152 canvas->setMatrix(SkMatrix::I());
153 mat.setTranslate(fSaved[j][0], fSaved[j][1]);
154
155 if (kScale_Type == fType) {
156 mat.preScale(fSaved[j][2], fSaved[j][2]);
157 } else if (kRotate_Type == fType) {
158 mat.preRotate(fSaved[j][2]);
159 }
160
161 canvas->concat(mat);
162 canvas->drawRect(clearRect, clearPaint);
163 }
164 } else {
165 canvas->clear(0xFF000000);
166 }
167
168 fNumSaved = 0;
169 }
170
171 SkASSERT(fNumSaved < kNumBeforeClear);
172
173 canvas->setMatrix(SkMatrix::I());
174
175 fSaved[fNumSaved][0] = transRand.nextRangeScalar(0.0f, maxTransX);
176 fSaved[fNumSaved][1] = transRand.nextRangeScalar(0.0f, maxTransY);
robertphillips@google.com347fd4e2013-05-15 14:18:32 +0000177 if (fAligned) {
178 // make the translations integer aligned
179 fSaved[fNumSaved][0] = SkScalarFloorToScalar(fSaved[fNumSaved][0]);
180 fSaved[fNumSaved][1] = SkScalarFloorToScalar(fSaved[fNumSaved][1]);
181 }
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000182
183 mat.setTranslate(fSaved[fNumSaved][0], fSaved[fNumSaved][1]);
184
185 if (kScale_Type == fType) {
186 fSaved[fNumSaved][2] = scaleRand.nextRangeScalar(0.5f, 1.5f);
187 mat.preScale(fSaved[fNumSaved][2], fSaved[fNumSaved][2]);
188 } else if (kRotate_Type == fType) {
189 fSaved[fNumSaved][2] = rotRand.nextRangeScalar(0.0f, 360.0f);
190 mat.preRotate(fSaved[fNumSaved][2]);
191 }
192
193 canvas->concat(mat);
skia.committer@gmail.com26da7f02013-06-01 07:01:39 +0000194 if (fUseAtlas) {
commit-bot@chromium.org44e3f712014-05-07 17:12:55 +0000195 const int curCell = i % (kNumAtlasedX * kNumAtlasedY);
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000196 SkIRect src = fAtlasRects[curCell % (kNumAtlasedX)][curCell / (kNumAtlasedX)];
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 };
Mike Reed887cdf12017-04-03 11:11:09 -0400205 canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
206 4, verts, uvs, nullptr, 6, indices),
207 SkBlendMode::kModulate, p2);
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000208 } else {
reed84984ef2015-07-17 07:09:43 -0700209 canvas->drawBitmapRect(fAtlas, src, dst, &p,
210 SkCanvas::kFast_SrcRectConstraint);
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000211 }
robertphillips@google.comf865be32013-05-31 13:27:02 +0000212 } else {
reed84984ef2015-07-17 07:09:43 -0700213 canvas->drawBitmapRect(fCheckerboard, 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
reed6c225732014-06-09 19:52:07 -0700253 fCheckerboard.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight);
robertphillips@google.com6d58ad32013-05-09 19:08:57 +0000254 for (int y = 0; y < kCheckerboardHeight; ++y) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000255 int even = (y / kCheckSize) % 2;
256
257 SkPMColor* scanline = fCheckerboard.getAddr32(0, y);
258
robertphillips@google.com6d58ad32013-05-09 19:08:57 +0000259 for (int x = 0; x < kCheckerboardWidth; ++x) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000260 if (even == (x / kCheckSize) % 2) {
261 *scanline++ = 0xFFFF0000;
262 } else {
263 *scanline++ = 0x00000000;
264 }
265 }
266 }
267 }
268
robertphillips@google.comf865be32013-05-31 13:27:02 +0000269 // Note: the resulting atlas has transparency
270 void makeAtlas() {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000271 SkRandom rand;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000272
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
reed6c225732014-06-09 19:52:07 -0700285 fAtlas.allocN32Pixels(kTotAtlasWidth, kTotAtlasHeight);
robertphillips@google.comf865be32013-05-31 13:27:02 +0000286
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
halcanary385fe4d2015-08-26 13:07:48 -0700311DEF_BENCH(return new GameBench(GameBench::kScale_Type, GameBench::kPartial_Clear);)
312DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kPartial_Clear);)
313DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kPartial_Clear, true);)
314DEF_BENCH(return new GameBench(GameBench::kRotate_Type, GameBench::kPartial_Clear);)
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000315
316// Full clear
halcanary385fe4d2015-08-26 13:07:48 -0700317DEF_BENCH(return new GameBench(GameBench::kScale_Type, GameBench::kFull_Clear);)
318DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear);)
319DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear, true);)
320DEF_BENCH(return new GameBench(GameBench::kRotate_Type, GameBench::kFull_Clear);)
robertphillips@google.comf865be32013-05-31 13:27:02 +0000321
322// Atlased
halcanary385fe4d2015-08-26 13:07:48 -0700323DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear, false, true);)
324DEF_BENCH(return new GameBench(
325 GameBench::kTranslate_Type, GameBench::kFull_Clear, false, true, true);)