1 2 /* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10 #include "BenchTimer.h" 11 12 #if SK_SUPPORT_GPU 13 #include "GrContext.h" 14 #include "GrContextFactory.h" 15 #include "gl/GrGLDefines.h" 16 #include "GrRenderTarget.h" 17 #include "SkGpuDevice.h" 18 #else 19 class GrContext; 20 #endif // SK_SUPPORT_GPU 21 22 #include "SkBenchLogger.h" 23 #include "SkBenchmark.h" 24 #include "SkCanvas.h" 25 #include "SkCommandLineFlags.h" 26 #include "SkDeferredCanvas.h" 27 #include "SkDevice.h" 28 #include "SkColorPriv.h" 29 #include "SkGraphics.h" 30 #include "SkImageEncoder.h" 31 #include "SkNWayCanvas.h" 32 #include "SkPicture.h" 33 #include "SkString.h" 34 #include "SkTArray.h" 35 #include "TimerData.h" 36 37 enum benchModes { 38 kNormal_benchModes, 39 kDeferred_benchModes, 40 kDeferredSilent_benchModes, 41 kRecord_benchModes, 42 kPictureRecord_benchModes 43 }; 44 45 /////////////////////////////////////////////////////////////////////////////// 46 47 static void erase(SkBitmap& bm) { 48 if (bm.config() == SkBitmap::kA8_Config) { 49 bm.eraseColor(SK_ColorTRANSPARENT); 50 } else { 51 bm.eraseColor(SK_ColorWHITE); 52 } 53 } 54 55 #if 0 56 static bool equal(const SkBitmap& bm1, const SkBitmap& bm2) { 57 if (bm1.width() != bm2.width() || 58 bm1.height() != bm2.height() || 59 bm1.config() != bm2.config()) { 60 return false; 61 } 62 63 size_t pixelBytes = bm1.width() * bm1.bytesPerPixel(); 64 for (int y = 0; y < bm1.height(); y++) { 65 if (memcmp(bm1.getAddr(0, y), bm2.getAddr(0, y), pixelBytes)) { 66 return false; 67 } 68 } 69 return true; 70 } 71 #endif 72 73 class Iter { 74 public: 75 Iter(void* param) { 76 fBench = BenchRegistry::Head(); 77 fParam = param; 78 } 79 80 SkBenchmark* next() { 81 if (fBench) { 82 BenchRegistry::Factory f = fBench->factory(); 83 fBench = fBench->next(); 84 return f(fParam); 85 } 86 return NULL; 87 } 88 89 private: 90 const BenchRegistry* fBench; 91 void* fParam; 92 }; 93 94 class AutoPrePostDraw { 95 public: 96 AutoPrePostDraw(SkBenchmark* bench) : fBench(bench) { 97 fBench->preDraw(); 98 } 99 ~AutoPrePostDraw() { 100 fBench->postDraw(); 101 } 102 private: 103 SkBenchmark* fBench; 104 }; 105 106 static void make_filename(const char name[], SkString* path) { 107 path->set(name); 108 for (int i = 0; name[i]; i++) { 109 switch (name[i]) { 110 case '/': 111 case '\\': 112 case ' ': 113 case ':': 114 path->writable_str()[i] = '-'; 115 break; 116 default: 117 break; 118 } 119 } 120 } 121 122 static void saveFile(const char name[], const char config[], const char dir[], 123 const SkBitmap& bm) { 124 SkBitmap copy; 125 if (!bm.copyTo(©, SkBitmap::kARGB_8888_Config)) { 126 return; 127 } 128 129 if (bm.config() == SkBitmap::kA8_Config) { 130 // turn alpha into gray-scale 131 size_t size = copy.getSize() >> 2; 132 SkPMColor* p = copy.getAddr32(0, 0); 133 for (size_t i = 0; i < size; i++) { 134 int c = (*p >> SK_A32_SHIFT) & 0xFF; 135 c = 255 - c; 136 c |= (c << 24) | (c << 16) | (c << 8); 137 *p++ = c | (SK_A32_MASK << SK_A32_SHIFT); 138 } 139 } 140 141 SkString str; 142 make_filename(name, &str); 143 str.appendf("_%s.png", config); 144 str.prepend(dir); 145 ::remove(str.c_str()); 146 SkImageEncoder::EncodeFile(str.c_str(), copy, SkImageEncoder::kPNG_Type, 147 100); 148 } 149 150 static void performClip(SkCanvas* canvas, int w, int h) { 151 SkRect r; 152 153 r.set(SkIntToScalar(10), SkIntToScalar(10), 154 SkIntToScalar(w*2/3), SkIntToScalar(h*2/3)); 155 canvas->clipRect(r, SkRegion::kIntersect_Op); 156 157 r.set(SkIntToScalar(w/3), SkIntToScalar(h/3), 158 SkIntToScalar(w-10), SkIntToScalar(h-10)); 159 canvas->clipRect(r, SkRegion::kXOR_Op); 160 } 161 162 static void performRotate(SkCanvas* canvas, int w, int h) { 163 const SkScalar x = SkIntToScalar(w) / 2; 164 const SkScalar y = SkIntToScalar(h) / 2; 165 166 canvas->translate(x, y); 167 canvas->rotate(SkIntToScalar(35)); 168 canvas->translate(-x, -y); 169 } 170 171 static void performScale(SkCanvas* canvas, int w, int h) { 172 const SkScalar x = SkIntToScalar(w) / 2; 173 const SkScalar y = SkIntToScalar(h) / 2; 174 175 canvas->translate(x, y); 176 // just enough so we can't take the sprite case 177 canvas->scale(SK_Scalar1 * 99/100, SK_Scalar1 * 99/100); 178 canvas->translate(-x, -y); 179 } 180 181 static bool parse_bool_arg(char * const* argv, char* const* stop, bool* var) { 182 if (argv < stop) { 183 *var = atoi(*argv) != 0; 184 return true; 185 } 186 return false; 187 } 188 189 enum Backend { 190 kNonRendering_Backend, 191 kRaster_Backend, 192 kGPU_Backend, 193 kPDF_Backend, 194 }; 195 196 static SkDevice* make_device(SkBitmap::Config config, const SkIPoint& size, 197 Backend backend, int sampleCount, GrContext* context) { 198 SkDevice* device = NULL; 199 SkBitmap bitmap; 200 bitmap.setConfig(config, size.fX, size.fY); 201 202 switch (backend) { 203 case kRaster_Backend: 204 bitmap.allocPixels(); 205 erase(bitmap); 206 device = SkNEW_ARGS(SkDevice, (bitmap)); 207 break; 208 #if SK_SUPPORT_GPU 209 case kGPU_Backend: { 210 GrTextureDesc desc; 211 desc.fConfig = kSkia8888_GrPixelConfig; 212 desc.fFlags = kRenderTarget_GrTextureFlagBit; 213 desc.fWidth = size.fX; 214 desc.fHeight = size.fY; 215 desc.fSampleCnt = sampleCount; 216 SkAutoTUnref<GrTexture> texture(context->createUncachedTexture(desc, NULL, 0)); 217 if (!texture) { 218 return NULL; 219 } 220 device = SkNEW_ARGS(SkGpuDevice, (context, texture.get())); 221 break; 222 } 223 #endif 224 case kPDF_Backend: 225 default: 226 SkASSERT(!"unsupported"); 227 } 228 return device; 229 } 230 231 #if SK_SUPPORT_GPU 232 GrContextFactory gContextFactory; 233 typedef GrContextFactory::GLContextType GLContextType; 234 static const GLContextType kDontCareGLCtxType = GrContextFactory::kNative_GLContextType; 235 #else 236 typedef int GLContextType; 237 static const GLContextType kDontCareGLCtxType = 0; 238 #endif 239 240 static const struct { 241 SkBitmap::Config fConfig; 242 const char* fName; 243 int fSampleCnt; 244 Backend fBackend; 245 GLContextType fContextType; 246 bool fRunByDefault; 247 } gConfigs[] = { 248 { SkBitmap::kNo_Config, "NONRENDERING", 0, kNonRendering_Backend, kDontCareGLCtxType, true }, 249 { SkBitmap::kARGB_8888_Config, "8888", 0, kRaster_Backend, kDontCareGLCtxType, true }, 250 { SkBitmap::kRGB_565_Config, "565", 0, kRaster_Backend, kDontCareGLCtxType, true }, 251 #if SK_SUPPORT_GPU 252 { SkBitmap::kARGB_8888_Config, "GPU", 0, kGPU_Backend, GrContextFactory::kNative_GLContextType, true }, 253 { SkBitmap::kARGB_8888_Config, "MSAA4", 4, kGPU_Backend, GrContextFactory::kNative_GLContextType, false }, 254 { SkBitmap::kARGB_8888_Config, "MSAA16", 16, kGPU_Backend, GrContextFactory::kNative_GLContextType, false }, 255 #if SK_ANGLE 256 { SkBitmap::kARGB_8888_Config, "ANGLE", 0, kGPU_Backend, GrContextFactory::kANGLE_GLContextType, true }, 257 #endif // SK_ANGLE 258 #ifdef SK_DEBUG 259 { SkBitmap::kARGB_8888_Config, "Debug", 0, kGPU_Backend, GrContextFactory::kDebug_GLContextType, GR_DEBUG }, 260 #endif // SK_DEBUG 261 { SkBitmap::kARGB_8888_Config, "NULLGPU", 0, kGPU_Backend, GrContextFactory::kNull_GLContextType, true }, 262 #endif // SK_SUPPORT_GPU 263 }; 264 265 static int findConfig(const char config[]) { 266 for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) { 267 if (!strcmp(config, gConfigs[i].fName)) { 268 return i; 269 } 270 } 271 return -1; 272 } 273 274 static void help() { 275 SkString configsStr; 276 static const size_t kConfigCount = SK_ARRAY_COUNT(gConfigs); 277 for (size_t i = 0; i < kConfigCount; ++i) { 278 configsStr.appendf("%s%s", gConfigs[i].fName, ((i == kConfigCount - 1) ? "" : "|")); 279 } 280 281 SkDebugf("Usage: bench [-o outDir] [--repeat nr] [--logPerIter] " 282 "[--timers [wcgWC]*] [--rotate]\n" 283 " [--scale] [--clip] [--min] [--forceAA 1|0] [--forceFilter 1|0]\n" 284 " [--forceDither 1|0] [--forceBlend 1|0]" 285 #if SK_SUPPORT_GPU 286 " [--gpuCacheSize <bytes> <count>]" 287 #endif 288 "\n" 289 " [--strokeWidth width] [--match name]\n" 290 " [--mode normal|deferred|deferredSilent|record|picturerecord]\n" 291 " [--config "); 292 SkDebugf("%s]\n", configsStr.c_str()); 293 SkDebugf(" [-Dfoo bar] [--logFile filename] [-h|--help]"); 294 SkDebugf("\n\n"); 295 SkDebugf(" -o outDir : Image of each bench will be put in outDir.\n"); 296 SkDebugf(" --repeat nr : Each bench repeats for nr times.\n"); 297 SkDebugf(" --logPerIter : " 298 "Log each repeat timer instead of mean, default is disabled.\n"); 299 SkDebugf(" --timers [wcgWC]* : " 300 "Display wall, cpu, gpu, truncated wall or truncated cpu time for each bench.\n"); 301 SkDebugf(" --rotate : Rotate before each bench runs.\n"); 302 SkDebugf(" --scale : Scale before each bench runs.\n"); 303 SkDebugf(" --clip : Clip before each bench runs.\n"); 304 SkDebugf(" --min : Print the minimum times (instead of average).\n"); 305 SkDebugf(" --forceAA 1|0 : " 306 "Enable/disable anti-aliased, default is enabled.\n"); 307 SkDebugf(" --forceFilter 1|0 : " 308 "Enable/disable bitmap filtering, default is disabled.\n"); 309 SkDebugf(" --forceDither 1|0 : " 310 "Enable/disable dithering, default is disabled.\n"); 311 SkDebugf(" --forceBlend 1|0 : " 312 "Enable/disable dithering, default is disabled.\n"); 313 #if SK_SUPPORT_GPU 314 SkDebugf(" --gpuCacheSize <bytes> <count>: " 315 "limits gpu cache to bytes size or object count.\n"); 316 SkDebugf(" -1 for either value means use the default. 0 for either disables the cache.\n"); 317 #endif 318 SkDebugf(" --strokeWidth width : The width for path stroke.\n"); 319 SkDebugf(" --match [~][^]substring[$] [...] of test name to run.\n" 320 " Multiple matches may be separated by spaces.\n" 321 " ~ causes a matching test to always be skipped\n" 322 " ^ requires the start of the test to match\n" 323 " $ requires the end of the test to match\n" 324 " ^ and $ requires an exact match\n" 325 " If a test does not match any list entry,\n" 326 " it is skipped unless some list entry starts with ~\n"); 327 SkDebugf(" --mode normal|deferred|deferredSilent|record|picturerecord :\n" 328 " Run in the corresponding mode\n" 329 " normal, Use a normal canvas to draw to;\n" 330 " deferred, Use a deferrred canvas when drawing;\n" 331 " deferredSilent, deferred with silent playback;\n" 332 " record, Benchmark the time to record to an SkPicture;\n" 333 " picturerecord, Benchmark the time to do record from a \n" 334 " SkPicture to a SkPicture.\n"); 335 SkDebugf(" --logFile filename : destination for writing log output, in addition to stdout.\n"); 336 SkDebugf(" --config %s:\n", configsStr.c_str()); 337 SkDebugf(" Run bench in corresponding config mode.\n"); 338 SkDebugf(" -Dfoo bar : Add extra definition to bench.\n"); 339 SkDebugf(" -h|--help : Show this help message.\n"); 340 } 341 342 int tool_main(int argc, char** argv); 343 int tool_main(int argc, char** argv) { 344 #if SK_ENABLE_INST_COUNT 345 gPrintInstCount = true; 346 #endif 347 SkAutoGraphics ag; 348 349 SkTDict<const char*> defineDict(1024); 350 int repeatDraw = 1; 351 352 int forceAlpha = 0xFF; 353 bool forceAA = true; 354 bool forceFilter = false; 355 SkTriState::State forceDither = SkTriState::kDefault; 356 357 static const uint32_t kDefaultTimerTypes = TimerData::kCpu_Flag | TimerData::kGpu_Flag; 358 static const TimerData::Result kDefaultTimerResult = TimerData::kAvg_Result; 359 uint32_t timerTypes = kDefaultTimerTypes; 360 TimerData::Result timerResult = kDefaultTimerResult; 361 362 bool doScale = false; 363 bool doRotate = false; 364 bool doClip = false; 365 bool hasStrokeWidth = false; 366 367 #if SK_SUPPORT_GPU 368 struct { 369 int fBytes; 370 int fCount; 371 } gpuCacheSize = { -1, -1 }; // -1s mean use the default 372 #endif 373 374 float strokeWidth; 375 SkTDArray<const char*> fMatches; 376 benchModes benchMode = kNormal_benchModes; 377 SkString perIterTimeformat("%.2f"); 378 SkString normalTimeFormat("%6.2f"); 379 380 SkString outDir; 381 SkBitmap::Config outConfig = SkBitmap::kNo_Config; 382 const char* configName = ""; 383 Backend backend = kRaster_Backend; // for warning 384 int sampleCount = 0; 385 SkTDArray<int> configs; 386 bool userConfig = false; 387 388 SkBenchLogger logger; 389 390 char* const* stop = argv + argc; 391 for (++argv; argv < stop; ++argv) { 392 if (strcmp(*argv, "-o") == 0) { 393 argv++; 394 if (argv < stop && **argv) { 395 outDir.set(*argv); 396 if (outDir.c_str()[outDir.size() - 1] != '/') { 397 outDir.append("/"); 398 } 399 } 400 } else if (strcmp(*argv, "--repeat") == 0) { 401 argv++; 402 if (argv < stop) { 403 repeatDraw = atoi(*argv); 404 if (repeatDraw < 1) { 405 repeatDraw = 1; 406 } 407 } else { 408 logger.logError("missing arg for --repeat\n"); 409 help(); 410 return -1; 411 } 412 } else if (strcmp(*argv, "--logPerIter") == 0) { 413 timerResult = TimerData::kPerIter_Result; 414 } else if (strcmp(*argv, "--timers") == 0) { 415 argv++; 416 if (argv < stop) { 417 timerTypes = 0; 418 for (char* t = *argv; *t; ++t) { 419 switch (*t) { 420 case 'w': timerTypes |= TimerData::kWall_Flag; break; 421 case 'c': timerTypes |= TimerData::kCpu_Flag; break; 422 case 'W': timerTypes |= TimerData::kTruncatedWall_Flag; break; 423 case 'C': timerTypes |= TimerData::kTruncatedCpu_Flag; break; 424 case 'g': timerTypes |= TimerData::kGpu_Flag; break; 425 } 426 } 427 } else { 428 logger.logError("missing arg for --timers\n"); 429 help(); 430 return -1; 431 } 432 } else if (!strcmp(*argv, "--rotate")) { 433 doRotate = true; 434 } else if (!strcmp(*argv, "--scale")) { 435 doScale = true; 436 } else if (!strcmp(*argv, "--clip")) { 437 doClip = true; 438 } else if (!strcmp(*argv, "--min")) { 439 timerResult = TimerData::kMin_Result; 440 } else if (strcmp(*argv, "--forceAA") == 0) { 441 if (!parse_bool_arg(++argv, stop, &forceAA)) { 442 logger.logError("missing arg for --forceAA\n"); 443 help(); 444 return -1; 445 } 446 } else if (strcmp(*argv, "--forceFilter") == 0) { 447 if (!parse_bool_arg(++argv, stop, &forceFilter)) { 448 logger.logError("missing arg for --forceFilter\n"); 449 help(); 450 return -1; 451 } 452 } else if (strcmp(*argv, "--forceDither") == 0) { 453 bool tmp; 454 if (!parse_bool_arg(++argv, stop, &tmp)) { 455 logger.logError("missing arg for --forceDither\n"); 456 help(); 457 return -1; 458 } 459 forceDither = tmp ? SkTriState::kTrue : SkTriState::kFalse; 460 } else if (strcmp(*argv, "--forceBlend") == 0) { 461 bool wantAlpha = false; 462 if (!parse_bool_arg(++argv, stop, &wantAlpha)) { 463 logger.logError("missing arg for --forceBlend\n"); 464 help(); 465 return -1; 466 } 467 forceAlpha = wantAlpha ? 0x80 : 0xFF; 468 #if SK_SUPPORT_GPU 469 } else if (strcmp(*argv, "--gpuCacheSize") == 0) { 470 if (stop - argv > 2) { 471 gpuCacheSize.fBytes = atoi(*++argv); 472 gpuCacheSize.fCount = atoi(*++argv); 473 } else { 474 SkDebugf("missing arg for --gpuCacheSize\n"); 475 help(); 476 return -1; 477 } 478 #endif 479 } else if (strcmp(*argv, "--mode") == 0) { 480 argv++; 481 if (argv < stop) { 482 if (strcmp(*argv, "normal") == 0) { 483 benchMode = kNormal_benchModes; 484 } else if (strcmp(*argv, "deferred") == 0) { 485 benchMode = kDeferred_benchModes; 486 } else if (strcmp(*argv, "deferredSilent") == 0) { 487 benchMode = kDeferredSilent_benchModes; 488 } else if (strcmp(*argv, "record") == 0) { 489 benchMode = kRecord_benchModes; 490 } else if (strcmp(*argv, "picturerecord") == 0) { 491 benchMode = kPictureRecord_benchModes; 492 } else { 493 logger.logError("bad arg for --mode\n"); 494 help(); 495 return -1; 496 } 497 } else { 498 logger.logError("missing arg for --mode\n"); 499 help(); 500 return -1; 501 } 502 } else if (strcmp(*argv, "--strokeWidth") == 0) { 503 argv++; 504 if (argv < stop) { 505 const char *strokeWidthStr = *argv; 506 if (sscanf(strokeWidthStr, "%f", &strokeWidth) != 1) { 507 logger.logError("bad arg for --strokeWidth\n"); 508 help(); 509 return -1; 510 } 511 hasStrokeWidth = true; 512 } else { 513 logger.logError("missing arg for --strokeWidth\n"); 514 help(); 515 return -1; 516 } 517 } else if (strcmp(*argv, "--match") == 0) { 518 argv++; 519 while (argv < stop && (*argv)[0] != '-') { 520 *fMatches.append() = *argv++; 521 } 522 argv--; 523 if (!fMatches.count()) { 524 logger.logError("missing arg for --match\n"); 525 help(); 526 return -1; 527 } 528 } else if (strcmp(*argv, "--config") == 0) { 529 argv++; 530 if (argv < stop) { 531 int index = findConfig(*argv); 532 if (index >= 0) { 533 *configs.append() = index; 534 userConfig = true; 535 } else { 536 SkString str; 537 str.printf("unrecognized config %s\n", *argv); 538 logger.logError(str); 539 help(); 540 return -1; 541 } 542 } else { 543 logger.logError("missing arg for --config\n"); 544 help(); 545 return -1; 546 } 547 } else if (strcmp(*argv, "--logFile") == 0) { 548 argv++; 549 if (argv < stop) { 550 if (!logger.SetLogFile(*argv)) { 551 SkString str; 552 str.printf("Could not open %s for writing.", *argv); 553 logger.logError(str); 554 return -1; 555 } 556 } else { 557 logger.logError("missing arg for --logFile\n"); 558 help(); 559 return -1; 560 } 561 } else if (strlen(*argv) > 2 && strncmp(*argv, "-D", 2) == 0) { 562 argv++; 563 if (argv < stop) { 564 defineDict.set(argv[-1] + 2, *argv); 565 } else { 566 logger.logError("incomplete '-Dfoo bar' definition\n"); 567 help(); 568 return -1; 569 } 570 } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) { 571 help(); 572 return 0; 573 } else { 574 SkString str; 575 str.printf("unrecognized arg %s\n", *argv); 576 logger.logError(str); 577 help(); 578 return -1; 579 } 580 } 581 if ((benchMode == kRecord_benchModes || benchMode == kPictureRecord_benchModes) 582 && !outDir.isEmpty()) { 583 logger.logError("'--mode record' and '--mode picturerecord' are not" 584 " compatible with -o.\n"); 585 return -1; 586 } 587 if ((benchMode == kRecord_benchModes || benchMode == kPictureRecord_benchModes)) { 588 perIterTimeformat.set("%.4f"); 589 normalTimeFormat.set("%6.4f"); 590 } 591 if (!userConfig) { 592 // if no config is specified by user, add the default configs 593 for (unsigned int i = 0; i < SK_ARRAY_COUNT(gConfigs); ++i) { 594 if (gConfigs[i].fRunByDefault) { 595 *configs.append() = i; 596 } 597 } 598 } 599 if (kNormal_benchModes != benchMode) { 600 // Non-rendering configs only run in normal mode 601 for (int i = 0; i < configs.count(); ++i) { 602 int configIdx = configs[i]; 603 if (kNonRendering_Backend == gConfigs[configIdx].fBackend) { 604 configs.remove(i, 1); 605 --i; 606 } 607 } 608 } 609 610 #if SK_SUPPORT_GPU 611 for (int i = 0; i < configs.count(); ++i) { 612 int configIdx = configs[i]; 613 614 if (kGPU_Backend == gConfigs[configIdx].fBackend && gConfigs[configIdx].fSampleCnt > 0) { 615 GrContext* context = gContextFactory.get(gConfigs[configIdx].fContextType); 616 if (NULL == context) { 617 SkString error; 618 error.printf("Error creating GrContext for config %s. Config will be skipped.\n", 619 gConfigs[configIdx].fName); 620 logger.logError(error.c_str()); 621 configs.remove(i); 622 --i; 623 continue; 624 } 625 if (gConfigs[configIdx].fSampleCnt > context->getMaxSampleCount()){ 626 SkString error; 627 error.printf("Sample count (%d) for config %s is unsupported. " 628 "Config will be skipped.\n", 629 gConfigs[configIdx].fSampleCnt, gConfigs[configIdx].fName); 630 logger.logError(error.c_str()); 631 configs.remove(i); 632 --i; 633 continue; 634 } 635 } 636 } 637 #endif 638 639 // report our current settings 640 { 641 SkString str; 642 const char* deferredMode = benchMode == kDeferred_benchModes ? "yes" : 643 (benchMode == kDeferredSilent_benchModes ? "silent" : "no"); 644 str.printf("skia bench: alpha=0x%02X antialias=%d filter=%d " 645 "deferred=%s logperiter=%d", 646 forceAlpha, forceAA, forceFilter, deferredMode, 647 TimerData::kPerIter_Result == timerResult); 648 str.appendf(" rotate=%d scale=%d clip=%d min=%d", 649 doRotate, doScale, doClip, TimerData::kMin_Result == timerResult); 650 str.appendf(" record=%d picturerecord=%d", 651 benchMode == kRecord_benchModes, 652 benchMode == kPictureRecord_benchModes); 653 const char * ditherName; 654 switch (forceDither) { 655 case SkTriState::kDefault: ditherName = "default"; break; 656 case SkTriState::kTrue: ditherName = "true"; break; 657 case SkTriState::kFalse: ditherName = "false"; break; 658 default: ditherName = "<invalid>"; break; 659 } 660 str.appendf(" dither=%s", ditherName); 661 662 if (hasStrokeWidth) { 663 str.appendf(" strokeWidth=%f", strokeWidth); 664 } else { 665 str.append(" strokeWidth=none"); 666 } 667 668 #if defined(SK_SCALAR_IS_FLOAT) 669 str.append(" scalar=float"); 670 #elif defined(SK_SCALAR_IS_FIXED) 671 str.append(" scalar=fixed"); 672 #endif 673 674 #if defined(SK_BUILD_FOR_WIN32) 675 str.append(" system=WIN32"); 676 #elif defined(SK_BUILD_FOR_MAC) 677 str.append(" system=MAC"); 678 #elif defined(SK_BUILD_FOR_ANDROID) 679 str.append(" system=ANDROID"); 680 #elif defined(SK_BUILD_FOR_UNIX) 681 str.append(" system=UNIX"); 682 #else 683 str.append(" system=other"); 684 #endif 685 686 #if defined(SK_DEBUG) 687 str.append(" DEBUG"); 688 #endif 689 str.append("\n"); 690 logger.logProgress(str); 691 } 692 693 SkTArray<BenchTimer*> timers(SK_ARRAY_COUNT(gConfigs)); 694 for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); ++i) { 695 #if SK_SUPPORT_GPU 696 SkGLContextHelper* glCtx = NULL; 697 if (kGPU_Backend == gConfigs[i].fBackend) { 698 GrContext* context = gContextFactory.get(gConfigs[i].fContextType); 699 if (NULL != context) { 700 // Set the user specified cache limits if non-default. 701 size_t bytes; 702 int count; 703 context->getTextureCacheLimits(&count, &bytes); 704 if (-1 != gpuCacheSize.fBytes) { 705 bytes = static_cast<size_t>(gpuCacheSize.fBytes); 706 } 707 if (-1 != gpuCacheSize.fCount) { 708 count = gpuCacheSize.fCount; 709 } 710 context->setTextureCacheLimits(count, bytes); 711 } 712 glCtx = gContextFactory.getGLContext(gConfigs[i].fContextType); 713 } 714 timers.push_back(SkNEW_ARGS(BenchTimer, (glCtx))); 715 #else 716 timers.push_back(SkNEW(BenchTimer)); 717 #endif 718 } 719 720 Iter iter(&defineDict); 721 SkBenchmark* bench; 722 while ((bench = iter.next()) != NULL) { 723 SkAutoTUnref<SkBenchmark> benchUnref(bench); 724 725 SkIPoint dim = bench->getSize(); 726 if (dim.fX <= 0 || dim.fY <= 0) { 727 continue; 728 } 729 730 bench->setForceAlpha(forceAlpha); 731 bench->setForceAA(forceAA); 732 bench->setForceFilter(forceFilter); 733 bench->setDither(forceDither); 734 if (hasStrokeWidth) { 735 bench->setStrokeWidth(strokeWidth); 736 } 737 738 // only run benchmarks if their name contains matchStr 739 if (SkCommandLineFlags::ShouldSkip(fMatches, bench->getName())) { 740 continue; 741 } 742 743 bool loggedBenchStart = false; 744 745 AutoPrePostDraw appd(bench); 746 747 for (int x = 0; x < configs.count(); ++x) { 748 int configIndex = configs[x]; 749 750 bool setupFailed = false; 751 752 if (kNonRendering_Backend == gConfigs[configIndex].fBackend) { 753 if (bench->isRendering()) { 754 continue; 755 } 756 } else { 757 if (!bench->isRendering()) { 758 continue; 759 } 760 } 761 762 outConfig = gConfigs[configIndex].fConfig; 763 configName = gConfigs[configIndex].fName; 764 backend = gConfigs[configIndex].fBackend; 765 sampleCount = gConfigs[configIndex].fSampleCnt; 766 GrContext* context = NULL; 767 BenchTimer* timer = timers[configIndex]; 768 769 #if SK_SUPPORT_GPU 770 SkGLContextHelper* glContext = NULL; 771 if (kGPU_Backend == backend) { 772 context = gContextFactory.get(gConfigs[configIndex].fContextType); 773 if (NULL == context) { 774 continue; 775 } 776 glContext = gContextFactory.getGLContext(gConfigs[configIndex].fContextType); 777 } 778 #endif 779 SkDevice* device = NULL; 780 SkCanvas* canvas = NULL; 781 SkPicture pictureRecordFrom; 782 SkPicture pictureRecordTo; 783 784 if (kNonRendering_Backend != backend) { 785 device = make_device(outConfig, dim, backend, sampleCount, context); 786 if (NULL == device) { 787 SkString error; 788 error.printf("Device creation failure for config %s. Will skip.\n", configName); 789 logger.logError(error.c_str()); 790 setupFailed = true; 791 } else { 792 switch(benchMode) { 793 case kDeferredSilent_benchModes: 794 case kDeferred_benchModes: 795 canvas = SkDeferredCanvas::Create(device); 796 break; 797 case kRecord_benchModes: 798 canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY, 799 SkPicture::kUsePathBoundsForClip_RecordingFlag); 800 canvas->ref(); 801 break; 802 case kPictureRecord_benchModes: { 803 // This sets up picture-to-picture recording. 804 // The C++ drawing calls for the benchmark are recorded into 805 // pictureRecordFrom. As the benchmark, we will time how 806 // long it takes to playback pictureRecordFrom into 807 // pictureRecordTo. 808 SkCanvas* tempCanvas = pictureRecordFrom.beginRecording(dim.fX, dim.fY, 809 SkPicture::kUsePathBoundsForClip_RecordingFlag); 810 bench->draw(tempCanvas); 811 pictureRecordFrom.endRecording(); 812 canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY, 813 SkPicture::kUsePathBoundsForClip_RecordingFlag); 814 canvas->ref(); 815 break; 816 } 817 case kNormal_benchModes: 818 canvas = new SkCanvas(device); 819 break; 820 default: 821 SkASSERT(0); 822 } 823 device->unref(); 824 canvas->clear(SK_ColorWHITE); 825 } 826 } 827 SkAutoUnref canvasUnref(canvas); 828 if (!setupFailed) { 829 if (NULL != canvas) { 830 if (doClip) { 831 performClip(canvas, dim.fX, dim.fY); 832 } 833 if (doScale) { 834 performScale(canvas, dim.fX, dim.fY); 835 } 836 if (doRotate) { 837 performRotate(canvas, dim.fX, dim.fY); 838 } 839 } 840 841 if (!loggedBenchStart) { 842 loggedBenchStart = true; 843 SkString str; 844 str.printf("running bench [%d %d] %28s", dim.fX, dim.fY, bench->getName()); 845 logger.logProgress(str); 846 } 847 848 // warm up caches if needed 849 if (repeatDraw > 1 && NULL != canvas) { 850 #if SK_SUPPORT_GPU 851 // purge the GPU resources to reduce variance 852 if (NULL != context) { 853 context->freeGpuResources(); 854 } 855 #endif 856 SkAutoCanvasRestore acr(canvas, true); 857 if (benchMode == kPictureRecord_benchModes) { 858 pictureRecordFrom.draw(canvas); 859 } else { 860 bench->draw(canvas); 861 } 862 863 if (kDeferredSilent_benchModes == benchMode) { 864 static_cast<SkDeferredCanvas*>(canvas)->silentFlush(); 865 } else { 866 canvas->flush(); 867 } 868 #if SK_SUPPORT_GPU 869 if (NULL != context) { 870 context->flush(); 871 SK_GL(*glContext, Finish()); 872 } 873 #endif 874 } 875 876 // record timer values for each repeat, and their sum 877 TimerData timerData(repeatDraw); 878 for (int i = 0; i < repeatDraw; i++) { 879 if ((benchMode == kRecord_benchModes || benchMode == kPictureRecord_benchModes)) { 880 // This will clear the recorded commands so that they do not 881 // accumulate. 882 canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY, 883 SkPicture::kUsePathBoundsForClip_RecordingFlag); 884 } 885 886 timer->start(bench->getDurationScale()); 887 if (NULL != canvas) { 888 canvas->save(); 889 } 890 if (benchMode == kPictureRecord_benchModes) { 891 pictureRecordFrom.draw(canvas); 892 } else { 893 bench->draw(canvas); 894 } 895 896 if (kDeferredSilent_benchModes == benchMode) { 897 static_cast<SkDeferredCanvas*>(canvas)->silentFlush(); 898 } else if (NULL != canvas) { 899 canvas->flush(); 900 } 901 902 if (NULL != canvas) { 903 canvas->restore(); 904 } 905 906 // stop the truncated timer after the last canvas call but 907 // don't wait for all the GL calls to complete 908 timer->truncatedEnd(); 909 #if SK_SUPPORT_GPU 910 if (NULL != glContext) { 911 context->flush(); 912 SK_GL(*glContext, Finish()); 913 } 914 #endif 915 // stop the inclusive and gpu timers once all the GL calls 916 // have completed 917 timer->end(); 918 919 SkAssertResult(timerData.appendTimes(timer)); 920 921 } 922 if (repeatDraw > 1) { 923 const char* timeFormat; 924 if (TimerData::kPerIter_Result == timerResult) { 925 timeFormat = perIterTimeformat.c_str(); 926 } else { 927 timeFormat = normalTimeFormat.c_str(); 928 } 929 uint32_t filteredTimerTypes = timerTypes; 930 if (NULL == context) { 931 filteredTimerTypes &= ~TimerData::kGpu_Flag; 932 } 933 SkString result = timerData.getResult(timeFormat, 934 timerResult, 935 configName, 936 filteredTimerTypes); 937 logger.logProgress(result); 938 } 939 if (outDir.size() > 0 && kNonRendering_Backend != backend) { 940 saveFile(bench->getName(), configName, outDir.c_str(), 941 device->accessBitmap(false)); 942 } 943 } 944 } 945 if (loggedBenchStart) { 946 logger.logProgress(SkString("\n")); 947 } 948 } 949 #if SK_SUPPORT_GPU 950 #if GR_CACHE_STATS 951 for (int i = 0; i <= GrContextFactory::kLastGLContextType; ++i) { 952 GrContextFactory::GLContextType ctxType = (GrContextFactory::GLContextType)i; 953 GrContext* context = gContextFactory.get(ctxType); 954 if (NULL != context) { 955 SkDebugf("Cache Stats for %s context:\n", GrContextFactory::GLContextTypeName(ctxType)); 956 context->printCacheStats(); 957 SkDebugf("\n"); 958 } 959 } 960 #endif 961 // Destroy the GrContext before the inst tracking printing at main() exit occurs. 962 gContextFactory.destroyContexts(); 963 #endif 964 for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); ++i) { 965 SkDELETE(timers[i]); 966 } 967 968 return 0; 969 } 970 971 #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) 972 int main(int argc, char * const argv[]) { 973 return tool_main(argc, (char**) argv); 974 } 975 #endif 976