1 /* 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 "Benchmark.h" 9 #include "SkCanvas.h" 10 #include "SkPaint.h" 11 #include "SkRandom.h" 12 #include "SkShader.h" 13 #include "SkString.h" 14 #include "SkVertices.h" 15 16 // This bench simulates the calls Skia sees from various HTML5 canvas 17 // game bench marks 18 class GameBench : public Benchmark { 19 public: 20 enum Type { 21 kScale_Type, 22 kTranslate_Type, 23 kRotate_Type 24 }; 25 26 enum Clear { 27 kFull_Clear, 28 kPartial_Clear 29 }; 30 31 GameBench(Type type, Clear clear, 32 bool aligned = false, bool useAtlas = false, 33 bool useDrawVertices = false) 34 : fType(type) 35 , fClear(clear) 36 , fAligned(aligned) 37 , fUseAtlas(useAtlas) 38 , fUseDrawVertices(useDrawVertices) 39 , 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 55 if (aligned) { 56 fName.append("_aligned"); 57 } 58 59 if (kPartial_Clear == clear) { 60 fName.append("_partial"); 61 } else { 62 fName.append("_full"); 63 } 64 65 if (useAtlas) { 66 fName.append("_atlas"); 67 } 68 69 if (useDrawVertices) { 70 fName.append("_drawVerts"); 71 } 72 73 // It's HTML 5 canvas, so always AA 74 fName.append("_aa"); 75 } 76 77 protected: 78 const char* onGetName() override { 79 return fName.c_str(); 80 } 81 82 void onDelayedSetup() override { 83 if (!fInitialized) { 84 this->makeCheckerboard(); 85 this->makeAtlas(); 86 fInitialized = true; 87 } 88 } 89 90 void onDraw(int loops, SkCanvas* canvas) override { 91 SkRandom scaleRand; 92 SkRandom transRand; 93 SkRandom rotRand; 94 95 int width, height; 96 if (fUseAtlas) { 97 width = kAtlasCellWidth; 98 height = kAtlasCellHeight; 99 } else { 100 width = kCheckerboardWidth; 101 height = kCheckerboardHeight; 102 } 103 104 SkPaint clearPaint; 105 clearPaint.setColor(0xFF000000); 106 clearPaint.setAntiAlias(true); 107 108 SkISize size = canvas->getBaseLayerSize(); 109 110 SkScalar maxTransX, maxTransY; 111 112 if (kScale_Type == fType) { 113 maxTransX = size.fWidth - (1.5f * width); 114 maxTransY = size.fHeight - (1.5f * height); 115 } else if (kTranslate_Type == fType) { 116 maxTransX = SkIntToScalar(size.fWidth - width); 117 maxTransY = SkIntToScalar(size.fHeight - height); 118 } else { 119 SkASSERT(kRotate_Type == fType); 120 // Yes, some rotations will be off the top and left sides 121 maxTransX = size.fWidth - SK_ScalarSqrt2 * height; 122 maxTransY = size.fHeight - SK_ScalarSqrt2 * height; 123 } 124 125 SkMatrix mat; 126 SkRect dst = { 0, 0, SkIntToScalar(width), SkIntToScalar(height) }; 127 SkRect clearRect = { -1.0f, -1.0f, width+1.0f, height+1.0f }; 128 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 }; 135 136 SkPaint p; 137 p.setColor(0xFF000000); 138 p.setFilterQuality(kLow_SkFilterQuality); 139 140 SkPaint p2; // for drawVertices path 141 p2.setColor(0xFF000000); 142 p2.setFilterQuality(kLow_SkFilterQuality); 143 p2.setShader(SkShader::MakeBitmapShader(fAtlas, 144 SkShader::kClamp_TileMode, 145 SkShader::kClamp_TileMode)); 146 147 for (int i = 0; i < loops; ++i, ++fNumSaved) { 148 if (0 == i % kNumBeforeClear) { 149 if (kPartial_Clear == fClear) { 150 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); 176 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 } 181 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); 193 if (fUseAtlas) { 194 const int curCell = i % (kNumAtlasedX * kNumAtlasedY); 195 SkIRect src = fAtlasRects[curCell % (kNumAtlasedX)][curCell / (kNumAtlasedX)]; 196 197 if (fUseDrawVertices) { 198 SkPoint uvs[4] = { 199 { 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 }; 204 canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, 205 4, verts, uvs, nullptr, 6, indices), 206 SkBlendMode::kModulate, p2); 207 } else { 208 canvas->drawBitmapRect(fAtlas, src, dst, &p, 209 SkCanvas::kFast_SrcRectConstraint); 210 } 211 } else { 212 canvas->drawBitmapRect(fCheckerboard, dst, &p); 213 } 214 } 215 } 216 217 private: 218 static const int kCheckerboardWidth = 64; 219 static const int kCheckerboardHeight = 128; 220 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; 230 static const int kNumBeforeClear = 100; 231 232 Type fType; 233 Clear fClear; 234 bool fAligned; 235 bool fUseAtlas; 236 bool fUseDrawVertices; 237 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]; 243 244 SkBitmap fCheckerboard; 245 SkBitmap fAtlas; 246 SkIRect fAtlasRects[kNumAtlasedX][kNumAtlasedY]; 247 248 // Note: the resulting checker board has transparency 249 void makeCheckerboard() { 250 static int kCheckSize = 16; 251 252 fCheckerboard.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight); 253 for (int y = 0; y < kCheckerboardHeight; ++y) { 254 int even = (y / kCheckSize) % 2; 255 256 SkPMColor* scanline = fCheckerboard.getAddr32(0, y); 257 258 for (int x = 0; x < kCheckerboardWidth; ++x) { 259 if (even == (x / kCheckSize) % 2) { 260 *scanline++ = 0xFFFF0000; 261 } else { 262 *scanline++ = 0x00000000; 263 } 264 } 265 } 266 } 267 268 // Note: the resulting atlas has transparency 269 void makeAtlas() { 270 SkRandom rand; 271 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 284 fAtlas.allocN32Pixels(kTotAtlasWidth, kTotAtlasHeight); 285 286 for (int y = 0; y < kTotAtlasHeight; ++y) { 287 int colorY = y / (kAtlasCellHeight + kAtlasSpacer); 288 bool inColorY = (y % (kAtlasCellHeight + kAtlasSpacer)) >= kAtlasSpacer; 289 290 SkPMColor* scanline = fAtlas.getAddr32(0, y); 291 292 for (int x = 0; x < kTotAtlasWidth; ++x, ++scanline) { 293 int colorX = x / (kAtlasCellWidth + kAtlasSpacer); 294 bool inColorX = (x % (kAtlasCellWidth + kAtlasSpacer)) >= kAtlasSpacer; 295 296 if (inColorX && inColorY) { 297 SkASSERT(colorX < kNumAtlasedX && colorY < kNumAtlasedY); 298 *scanline = colors[colorX][colorY]; 299 } else { 300 *scanline = 0x00000000; 301 } 302 } 303 } 304 } 305 306 typedef Benchmark INHERITED; 307 }; 308 309 // Partial clear 310 DEF_BENCH(return new GameBench(GameBench::kScale_Type, GameBench::kPartial_Clear);) 311 DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kPartial_Clear);) 312 DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kPartial_Clear, true);) 313 DEF_BENCH(return new GameBench(GameBench::kRotate_Type, GameBench::kPartial_Clear);) 314 315 // Full clear 316 DEF_BENCH(return new GameBench(GameBench::kScale_Type, GameBench::kFull_Clear);) 317 DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear);) 318 DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear, true);) 319 DEF_BENCH(return new GameBench(GameBench::kRotate_Type, GameBench::kFull_Clear);) 320 321 // Atlased 322 DEF_BENCH(return new GameBench(GameBench::kTranslate_Type, GameBench::kFull_Clear, false, true);) 323 DEF_BENCH(return new GameBench( 324 GameBench::kTranslate_Type, GameBench::kFull_Clear, false, true, true);) 325