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