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 "SkBenchmark.h" 9 #include "SkCanvas.h" 10 #include "SkPaint.h" 11 #include "SkRandom.h" 12 #include "SkShader.h" 13 #include "SkString.h" 14 15 // This bench simulates the calls Skia sees from various HTML5 canvas 16 // game bench marks 17 class GameBench : public SkBenchmark { 18 public: 19 enum Type { 20 kScale_Type, 21 kTranslate_Type, 22 kRotate_Type 23 }; 24 25 enum Clear { 26 kFull_Clear, 27 kPartial_Clear 28 }; 29 GameBench(Type type,Clear clear,bool aligned=false,bool useAtlas=false,bool useDrawVertices=false)30 GameBench(Type type, Clear clear, 31 bool aligned = false, bool useAtlas = false, 32 bool useDrawVertices = false) 33 : fType(type) 34 , fClear(clear) 35 , fAligned(aligned) 36 , fUseAtlas(useAtlas) 37 , fUseDrawVertices(useDrawVertices) 38 , 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 54 if (aligned) { 55 fName.append("_aligned"); 56 } 57 58 if (kPartial_Clear == clear) { 59 fName.append("_partial"); 60 } else { 61 fName.append("_full"); 62 } 63 64 if (useAtlas) { 65 fName.append("_atlas"); 66 } 67 68 if (useDrawVertices) { 69 fName.append("_drawVerts"); 70 } 71 72 // It's HTML 5 canvas, so always AA 73 fName.append("_aa"); 74 } 75 76 protected: onGetName()77 virtual const char* onGetName() SK_OVERRIDE { 78 return fName.c_str(); 79 } 80 onPreDraw()81 virtual void onPreDraw() SK_OVERRIDE { 82 if (!fInitialized) { 83 this->makeCheckerboard(); 84 this->makeAtlas(); 85 fInitialized = true; 86 } 87 } 88 onDraw(const int loops,SkCanvas * canvas)89 virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE { 90 SkRandom scaleRand; 91 SkRandom transRand; 92 SkRandom rotRand; 93 94 int width, height; 95 if (fUseAtlas) { 96 width = kAtlasCellWidth; 97 height = kAtlasCellHeight; 98 } else { 99 width = kCheckerboardWidth; 100 height = kCheckerboardHeight; 101 } 102 103 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) { 112 maxTransX = size.fWidth - (1.5f * width); 113 maxTransY = size.fHeight - (1.5f * height); 114 } else if (kTranslate_Type == fType) { 115 maxTransX = SkIntToScalar(size.fWidth - width); 116 maxTransY = SkIntToScalar(size.fHeight - height); 117 } else { 118 SkASSERT(kRotate_Type == fType); 119 // Yes, some rotations will be off the top and left sides 120 maxTransX = size.fWidth - SK_ScalarSqrt2 * height; 121 maxTransY = size.fHeight - SK_ScalarSqrt2 * height; 122 } 123 124 SkMatrix mat; 125 SkRect dst = { 0, 0, SkIntToScalar(width), SkIntToScalar(height) }; 126 SkRect clearRect = { -1.0f, -1.0f, width+1.0f, height+1.0f }; 127 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 }; 134 135 SkPaint p; 136 p.setColor(0xFF000000); 137 p.setFilterLevel(SkPaint::kLow_FilterLevel); 138 139 SkPaint p2; // for drawVertices path 140 p2.setColor(0xFF000000); 141 p2.setFilterLevel(SkPaint::kLow_FilterLevel); 142 p2.setShader(SkShader::CreateBitmapShader(fAtlas, 143 SkShader::kClamp_TileMode, 144 SkShader::kClamp_TileMode))->unref(); 145 146 for (int i = 0; i < loops; ++i, ++fNumSaved) { 147 if (0 == i % kNumBeforeClear) { 148 if (kPartial_Clear == fClear) { 149 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); 175 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 } 180 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); 192 if (fUseAtlas) { 193 static int curCell = 0; 194 SkIRect src = fAtlasRects[curCell % (kNumAtlasedX)][curCell / (kNumAtlasedX)]; 195 curCell = (curCell + 1) % (kNumAtlasedX*kNumAtlasedY); 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(SkCanvas::kTriangles_VertexMode, 205 4, verts, uvs, NULL, NULL, 206 indices, 6, p2); 207 } else { 208 canvas->drawBitmapRect(fAtlas, &src, dst, &p, 209 SkCanvas::kBleed_DrawBitmapRectFlag); 210 } 211 } else { 212 canvas->drawBitmapRect(fCheckerboard, NULL, 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 makeCheckerboard()249 void makeCheckerboard() { 250 static int kCheckSize = 16; 251 252 fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config, 253 kCheckerboardWidth, kCheckerboardHeight); 254 fCheckerboard.allocPixels(); 255 SkAutoLockPixels lock(fCheckerboard); 256 for (int y = 0; y < kCheckerboardHeight; ++y) { 257 int even = (y / kCheckSize) % 2; 258 259 SkPMColor* scanline = fCheckerboard.getAddr32(0, y); 260 261 for (int x = 0; x < kCheckerboardWidth; ++x) { 262 if (even == (x / kCheckSize) % 2) { 263 *scanline++ = 0xFFFF0000; 264 } else { 265 *scanline++ = 0x00000000; 266 } 267 } 268 } 269 } 270 271 // Note: the resulting atlas has transparency makeAtlas()272 void makeAtlas() { 273 SkRandom rand; 274 275 SkColor colors[kNumAtlasedX][kNumAtlasedY]; 276 277 for (int y = 0; y < kNumAtlasedY; ++y) { 278 for (int x = 0; x < kNumAtlasedX; ++x) { 279 colors[x][y] = rand.nextU() | 0xff000000; 280 fAtlasRects[x][y] = SkIRect::MakeXYWH(kAtlasSpacer + x * (kAtlasCellWidth + kAtlasSpacer), 281 kAtlasSpacer + y * (kAtlasCellHeight + kAtlasSpacer), 282 kAtlasCellWidth, 283 kAtlasCellHeight); 284 } 285 } 286 287 fAtlas.setConfig(SkBitmap::kARGB_8888_Config, kTotAtlasWidth, kTotAtlasHeight); 288 fAtlas.allocPixels(); 289 SkAutoLockPixels lock(fAtlas); 290 291 for (int y = 0; y < kTotAtlasHeight; ++y) { 292 int colorY = y / (kAtlasCellHeight + kAtlasSpacer); 293 bool inColorY = (y % (kAtlasCellHeight + kAtlasSpacer)) >= kAtlasSpacer; 294 295 SkPMColor* scanline = fAtlas.getAddr32(0, y); 296 297 for (int x = 0; x < kTotAtlasWidth; ++x, ++scanline) { 298 int colorX = x / (kAtlasCellWidth + kAtlasSpacer); 299 bool inColorX = (x % (kAtlasCellWidth + kAtlasSpacer)) >= kAtlasSpacer; 300 301 if (inColorX && inColorY) { 302 SkASSERT(colorX < kNumAtlasedX && colorY < kNumAtlasedY); 303 *scanline = colors[colorX][colorY]; 304 } else { 305 *scanline = 0x00000000; 306 } 307 } 308 } 309 } 310 311 typedef SkBenchmark INHERITED; 312 }; 313 314 // Partial clear 315 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kScale_Type, 316 GameBench::kPartial_Clear)); ) 317 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type, 318 GameBench::kPartial_Clear)); ) 319 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type, 320 GameBench::kPartial_Clear, true)); ) 321 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kRotate_Type, 322 GameBench::kPartial_Clear)); ) 323 324 // Full clear 325 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kScale_Type, 326 GameBench::kFull_Clear)); ) 327 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type, 328 GameBench::kFull_Clear)); ) 329 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type, 330 GameBench::kFull_Clear, true)); ) 331 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kRotate_Type, 332 GameBench::kFull_Clear)); ) 333 334 // Atlased 335 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type, 336 GameBench::kFull_Clear, false, true)); ) 337 DEF_BENCH( return SkNEW_ARGS(GameBench, (GameBench::kTranslate_Type, 338 GameBench::kFull_Clear, false, true, true)); ) 339