blob: 8022d16394446bdaf9fe6570716b2b94275f400a [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "bench/Benchmark.h"
9#include "include/core/SkBitmap.h"
10#include "include/core/SkCanvas.h"
Mike Reed069e4842021-01-24 11:39:58 -050011#include "include/core/SkImage.h"
Mike Reed46f5c5f2020-02-20 15:42:29 -050012#include "include/core/SkM44.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#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"
robertphillips@google.com6670ab92013-05-09 19:03:48 +000018
robertphillips@google.com6670ab92013-05-09 19:03:48 +000019// This bench simulates the calls Skia sees from various HTML5 canvas
20// game bench marks
tfarinaf168b862014-06-19 12:32:29 -070021class GameBench : public Benchmark {
robertphillips@google.com6670ab92013-05-09 19:03:48 +000022public:
23 enum Type {
skia.committer@gmail.com0f20a3f2013-05-10 07:01:04 +000024 kScale_Type,
25 kTranslate_Type,
robertphillips@google.com6670ab92013-05-09 19:03:48 +000026 kRotate_Type
27 };
28
robertphillips@google.comf865be32013-05-31 13:27:02 +000029 enum Clear {
30 kFull_Clear,
31 kPartial_Clear
32 };
33
mtklein@google.com410e6e82013-09-13 19:52:27 +000034 GameBench(Type type, Clear clear,
robertphillips@google.com631a59b2013-07-31 14:57:53 +000035 bool aligned = false, bool useAtlas = false,
36 bool useDrawVertices = false)
mtklein@google.com410e6e82013-09-13 19:52:27 +000037 : fType(type)
robertphillips@google.comf865be32013-05-31 13:27:02 +000038 , fClear(clear)
robertphillips@google.com347fd4e2013-05-15 14:18:32 +000039 , fAligned(aligned)
robertphillips@google.comf865be32013-05-31 13:27:02 +000040 , fUseAtlas(useAtlas)
robertphillips@google.com631a59b2013-07-31 14:57:53 +000041 , fUseDrawVertices(useDrawVertices)
robertphillips@google.com6670ab92013-05-09 19:03:48 +000042 , 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;
Brian Salomon23356442018-11-30 15:33:19 -050056 }
robertphillips@google.com6670ab92013-05-09 19:03:48 +000057
robertphillips@google.com347fd4e2013-05-15 14:18:32 +000058 if (aligned) {
59 fName.append("_aligned");
60 }
61
robertphillips@google.comf865be32013-05-31 13:27:02 +000062 if (kPartial_Clear == clear) {
robertphillips@google.com6670ab92013-05-09 19:03:48 +000063 fName.append("_partial");
64 } else {
65 fName.append("_full");
66 }
67
robertphillips@google.comf865be32013-05-31 13:27:02 +000068 if (useAtlas) {
69 fName.append("_atlas");
70 }
71
robertphillips@google.com631a59b2013-07-31 14:57:53 +000072 if (useDrawVertices) {
73 fName.append("_drawVerts");
74 }
75
robertphillips@google.com6670ab92013-05-09 19:03:48 +000076 // It's HTML 5 canvas, so always AA
77 fName.append("_aa");
78 }
79
80protected:
mtklein36352bf2015-03-25 18:17:31 -070081 const char* onGetName() override {
robertphillips@google.comf865be32013-05-31 13:27:02 +000082 return fName.c_str();
robertphillips@google.com6670ab92013-05-09 19:03:48 +000083 }
84
joshualitt8a6697a2015-09-30 12:11:07 -070085 void onDelayedSetup() override {
robertphillips@google.com6670ab92013-05-09 19:03:48 +000086 if (!fInitialized) {
87 this->makeCheckerboard();
robertphillips@google.comf865be32013-05-31 13:27:02 +000088 this->makeAtlas();
robertphillips@google.com6670ab92013-05-09 19:03:48 +000089 fInitialized = true;
90 }
91 }
92
mtkleina1ebeb22015-10-01 09:43:39 -070093 void onDraw(int loops, SkCanvas* canvas) override {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000094 SkRandom scaleRand;
95 SkRandom transRand;
96 SkRandom rotRand;
robertphillips@google.com6670ab92013-05-09 19:03:48 +000097
robertphillips@google.comf865be32013-05-31 13:27:02 +000098 int width, height;
99 if (fUseAtlas) {
100 width = kAtlasCellWidth;
101 height = kAtlasCellHeight;
102 } else {
103 width = kCheckerboardWidth;
104 height = kCheckerboardHeight;
105 }
106
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000107 SkPaint clearPaint;
108 clearPaint.setColor(0xFF000000);
109 clearPaint.setAntiAlias(true);
110
Mike Reed3661bc92017-02-22 13:21:42 -0500111 SkISize size = canvas->getBaseLayerSize();
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000112
113 SkScalar maxTransX, maxTransY;
114
115 if (kScale_Type == fType) {
robertphillips@google.comf865be32013-05-31 13:27:02 +0000116 maxTransX = size.fWidth - (1.5f * width);
117 maxTransY = size.fHeight - (1.5f * height);
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000118 } else if (kTranslate_Type == fType) {
robertphillips@google.comf865be32013-05-31 13:27:02 +0000119 maxTransX = SkIntToScalar(size.fWidth - width);
120 maxTransY = SkIntToScalar(size.fHeight - height);
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000121 } else {
122 SkASSERT(kRotate_Type == fType);
123 // Yes, some rotations will be off the top and left sides
robertphillips@google.comf865be32013-05-31 13:27:02 +0000124 maxTransX = size.fWidth - SK_ScalarSqrt2 * height;
125 maxTransY = size.fHeight - SK_ScalarSqrt2 * height;
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000126 }
127
128 SkMatrix mat;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000129 SkRect dst = { 0, 0, SkIntToScalar(width), SkIntToScalar(height) };
130 SkRect clearRect = { -1.0f, -1.0f, width+1.0f, height+1.0f };
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000131 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 };
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000138
139 SkPaint p;
140 p.setColor(0xFF000000);
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000141
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000142 SkPaint p2; // for drawVertices path
143 p2.setColor(0xFF000000);
Mike Reed069e4842021-01-24 11:39:58 -0500144 p2.setShader(fAtlas->makeShader(SkSamplingOptions(SkFilterMode::kLinear)));
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000145
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);
Mike Reed069e4842021-01-24 11:39:58 -0500194 SkRect src = SkRect::Make(
195 fAtlasRects[curCell % (kNumAtlasedX)][curCell / (kNumAtlasedX)]);
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000196
197 if (fUseDrawVertices) {
skia.committer@gmail.com5d4b7732013-08-01 07:01:05 +0000198 SkPoint uvs[4] = {
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000199 { 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 };
Mike Reed887cdf12017-04-03 11:11:09 -0400204 canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
205 4, verts, uvs, nullptr, 6, indices),
206 SkBlendMode::kModulate, p2);
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000207 } else {
Mike Reed069e4842021-01-24 11:39:58 -0500208 canvas->drawImageRect(fAtlas, src, dst, SkSamplingOptions(), &p,
reed84984ef2015-07-17 07:09:43 -0700209 SkCanvas::kFast_SrcRectConstraint);
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000210 }
robertphillips@google.comf865be32013-05-31 13:27:02 +0000211 } else {
Mike Reed069e4842021-01-24 11:39:58 -0500212 canvas->drawImageRect(fCheckerboard, dst, SkSamplingOptions(), &p);
robertphillips@google.comf865be32013-05-31 13:27:02 +0000213 }
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000214 }
215 }
216
217private:
218 static const int kCheckerboardWidth = 64;
219 static const int kCheckerboardHeight = 128;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000220
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;
mtklein@google.comc2897432013-09-10 19:23:38 +0000230 static const int kNumBeforeClear = 100;
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000231
232 Type fType;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000233 Clear fClear;
robertphillips@google.com347fd4e2013-05-15 14:18:32 +0000234 bool fAligned;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000235 bool fUseAtlas;
robertphillips@google.com631a59b2013-07-31 14:57:53 +0000236 bool fUseDrawVertices;
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000237 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];
robertphillips@google.comf865be32013-05-31 13:27:02 +0000243
Mike Reed069e4842021-01-24 11:39:58 -0500244 sk_sp<SkImage> fCheckerboard, fAtlas;
robertphillips@google.comf865be32013-05-31 13:27:02 +0000245 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
Mike Reed069e4842021-01-24 11:39:58 -0500251 SkBitmap bm;
252 bm.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight);
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
Mike Reed069e4842021-01-24 11:39:58 -0500256 SkPMColor* scanline = bm.getAddr32(0, y);
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000257
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 }
Mike Reed069e4842021-01-24 11:39:58 -0500266 fCheckerboard = bm.asImage();
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000267 }
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
Mike Reed069e4842021-01-24 11:39:58 -0500285 SkBitmap bm;
286 bm.allocN32Pixels(kTotAtlasWidth, kTotAtlasHeight);
robertphillips@google.comf865be32013-05-31 13:27:02 +0000287
288 for (int y = 0; y < kTotAtlasHeight; ++y) {
289 int colorY = y / (kAtlasCellHeight + kAtlasSpacer);
290 bool inColorY = (y % (kAtlasCellHeight + kAtlasSpacer)) >= kAtlasSpacer;
291
Mike Reed069e4842021-01-24 11:39:58 -0500292 SkPMColor* scanline = bm.getAddr32(0, y);
robertphillips@google.comf865be32013-05-31 13:27:02 +0000293
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 }
Mike Reed069e4842021-01-24 11:39:58 -0500306 fAtlas = bm.asImage();
robertphillips@google.comf865be32013-05-31 13:27:02 +0000307 }
308
John Stiles7571f9e2020-09-02 22:42:33 -0400309 using INHERITED = Benchmark;
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000310};
311
312// Partial clear
halcanary385fe4d2015-08-26 13:07:48 -0700313DEF_BENCH(return new GameBench(GameBench::kScale_Type, GameBench::kPartial_Clear);)
314DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kPartial_Clear);)
315DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kPartial_Clear, true);)
316DEF_BENCH(return new GameBench(GameBench::kRotate_Type, GameBench::kPartial_Clear);)
robertphillips@google.com6670ab92013-05-09 19:03:48 +0000317
318// Full clear
halcanary385fe4d2015-08-26 13:07:48 -0700319DEF_BENCH(return new GameBench(GameBench::kScale_Type, GameBench::kFull_Clear);)
320DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear);)
321DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear, true);)
322DEF_BENCH(return new GameBench(GameBench::kRotate_Type, GameBench::kFull_Clear);)
robertphillips@google.comf865be32013-05-31 13:27:02 +0000323
324// Atlased
halcanary385fe4d2015-08-26 13:07:48 -0700325DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear, false, true);)
326DEF_BENCH(return new GameBench(
327 GameBench::kTranslate_Type, GameBench::kFull_Clear, false, true, true);)
Mike Reed77cf4302019-12-30 10:21:29 -0500328
329
330class CanvasMatrixBench : public Benchmark {
331 SkString fName;
332public:
333 enum Type {
334 kTranslate_Type,
335 kScale_Type,
336 k2x3_Type,
337 k3x3_Type,
Mike Reed064c7f92020-01-08 17:33:04 -0500338 k4x4_Type,
Mike Reed77cf4302019-12-30 10:21:29 -0500339 };
340 Type fType;
341
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;
Mike Reed064c7f92020-01-08 17:33:04 -0500349 case k4x4_Type: fName.append("_4x4"); break;
Mike Reed77cf4302019-12-30 10:21:29 -0500350 }
351 }
352
353protected:
354 const char* onGetName() override {
355 return fName.c_str();
356 }
357
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 }
Mike Reedb26b4e72020-01-22 14:31:21 -0500364 SkM44 m4(m);
Mike Reed77cf4302019-12-30 10:21:29 -0500365
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;
Mike Reed3ef77dd2020-04-06 10:41:09 -0400374 case k4x4_Type: canvas->concat(m4); break;
Mike Reed77cf4302019-12-30 10:21:29 -0500375 }
376 }
377 canvas->restore();
378 }
379 }
380
381private:
John Stiles7571f9e2020-09-02 22:42:33 -0400382 using INHERITED = Benchmark;
Mike Reed77cf4302019-12-30 10:21:29 -0500383};
384
385DEF_BENCH(return new CanvasMatrixBench(CanvasMatrixBench::kTranslate_Type));
386DEF_BENCH(return new CanvasMatrixBench(CanvasMatrixBench::kScale_Type));
387DEF_BENCH(return new CanvasMatrixBench(CanvasMatrixBench::k2x3_Type));
388DEF_BENCH(return new CanvasMatrixBench(CanvasMatrixBench::k3x3_Type));
Mike Reed064c7f92020-01-08 17:33:04 -0500389DEF_BENCH(return new CanvasMatrixBench(CanvasMatrixBench::k4x4_Type));