benchmain.cpp revision 78d0379dcc777b4bc4965cff0c2b3fc44ccaef56
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#if SK_SUPPORT_GPU 10#include "GrContext.h" 11#include "GrContextFactory.h" 12#include "GrRenderTarget.h" 13#include "SkGpuDevice.h" 14#include "gl/GrGLDefines.h" 15#else 16class GrContext; 17#endif // SK_SUPPORT_GPU 18 19#include "BenchTimer.h" 20#include "SkBenchLogger.h" 21#include "SkBenchmark.h" 22#include "SkBitmapDevice.h" 23#include "SkCanvas.h" 24#include "SkColorPriv.h" 25#include "SkCommandLineFlags.h" 26#include "SkDeferredCanvas.h" 27#include "SkGraphics.h" 28#include "SkImageEncoder.h" 29#include "SkOSFile.h" 30#include "SkPicture.h" 31#include "SkString.h" 32 33enum BenchMode { 34 kNormal_BenchMode, 35 kDeferred_BenchMode, 36 kDeferredSilent_BenchMode, 37 kRecord_BenchMode, 38 kPictureRecord_BenchMode 39}; 40const char* BenchMode_Name[] = { "normal", "deferred", "deferredSilent", "record", "picturerecord" }; 41 42/////////////////////////////////////////////////////////////////////////////// 43 44static void erase(SkBitmap& bm) { 45 if (bm.config() == SkBitmap::kA8_Config) { 46 bm.eraseColor(SK_ColorTRANSPARENT); 47 } else { 48 bm.eraseColor(SK_ColorWHITE); 49 } 50} 51 52class Iter { 53public: 54 Iter() : fBench(BenchRegistry::Head()) {} 55 56 SkBenchmark* next() { 57 if (fBench) { 58 BenchRegistry::Factory f = fBench->factory(); 59 fBench = fBench->next(); 60 return f(NULL); 61 } 62 return NULL; 63 } 64 65private: 66 const BenchRegistry* fBench; 67}; 68 69class AutoPrePostDraw { 70public: 71 AutoPrePostDraw(SkBenchmark* bench) : fBench(bench) { 72 fBench->preDraw(); 73 } 74 ~AutoPrePostDraw() { 75 fBench->postDraw(); 76 } 77private: 78 SkBenchmark* fBench; 79}; 80 81static void make_filename(const char name[], SkString* path) { 82 path->set(name); 83 for (int i = 0; name[i]; i++) { 84 switch (name[i]) { 85 case '/': 86 case '\\': 87 case ' ': 88 case ':': 89 path->writable_str()[i] = '-'; 90 break; 91 default: 92 break; 93 } 94 } 95} 96 97static void saveFile(const char name[], const char config[], const char dir[], 98 const SkBitmap& bm) { 99 SkBitmap copy; 100 if (!bm.copyTo(©, SkBitmap::kARGB_8888_Config)) { 101 return; 102 } 103 104 if (bm.config() == SkBitmap::kA8_Config) { 105 // turn alpha into gray-scale 106 size_t size = copy.getSize() >> 2; 107 SkPMColor* p = copy.getAddr32(0, 0); 108 for (size_t i = 0; i < size; i++) { 109 int c = (*p >> SK_A32_SHIFT) & 0xFF; 110 c = 255 - c; 111 c |= (c << 24) | (c << 16) | (c << 8); 112 *p++ = c | (SK_A32_MASK << SK_A32_SHIFT); 113 } 114 } 115 116 SkString filename; 117 make_filename(name, &filename); 118 filename.appendf("_%s.png", config); 119 SkString path = SkOSPath::SkPathJoin(dir, filename.c_str()); 120 ::remove(path.c_str()); 121 SkImageEncoder::EncodeFile(path.c_str(), copy, SkImageEncoder::kPNG_Type, 100); 122} 123 124static void performClip(SkCanvas* canvas, int w, int h) { 125 SkRect r; 126 127 r.set(SkIntToScalar(10), SkIntToScalar(10), 128 SkIntToScalar(w*2/3), SkIntToScalar(h*2/3)); 129 canvas->clipRect(r, SkRegion::kIntersect_Op); 130 131 r.set(SkIntToScalar(w/3), SkIntToScalar(h/3), 132 SkIntToScalar(w-10), SkIntToScalar(h-10)); 133 canvas->clipRect(r, SkRegion::kXOR_Op); 134} 135 136static void performRotate(SkCanvas* canvas, int w, int h) { 137 const SkScalar x = SkIntToScalar(w) / 2; 138 const SkScalar y = SkIntToScalar(h) / 2; 139 140 canvas->translate(x, y); 141 canvas->rotate(SkIntToScalar(35)); 142 canvas->translate(-x, -y); 143} 144 145static void performScale(SkCanvas* canvas, int w, int h) { 146 const SkScalar x = SkIntToScalar(w) / 2; 147 const SkScalar y = SkIntToScalar(h) / 2; 148 149 canvas->translate(x, y); 150 // just enough so we can't take the sprite case 151 canvas->scale(SK_Scalar1 * 99/100, SK_Scalar1 * 99/100); 152 canvas->translate(-x, -y); 153} 154 155enum Backend { 156 kNonRendering_Backend, 157 kRaster_Backend, 158 kGPU_Backend, 159 kPDF_Backend, 160}; 161 162static SkBaseDevice* make_device(SkBitmap::Config config, const SkIPoint& size, 163 Backend backend, int sampleCount, GrContext* context) { 164 SkBaseDevice* device = NULL; 165 SkBitmap bitmap; 166 bitmap.setConfig(config, size.fX, size.fY); 167 168 switch (backend) { 169 case kRaster_Backend: 170 bitmap.allocPixels(); 171 erase(bitmap); 172 device = SkNEW_ARGS(SkBitmapDevice, (bitmap)); 173 break; 174#if SK_SUPPORT_GPU 175 case kGPU_Backend: { 176 GrTextureDesc desc; 177 desc.fConfig = kSkia8888_GrPixelConfig; 178 desc.fFlags = kRenderTarget_GrTextureFlagBit; 179 desc.fWidth = size.fX; 180 desc.fHeight = size.fY; 181 desc.fSampleCnt = sampleCount; 182 SkAutoTUnref<GrTexture> texture(context->createUncachedTexture(desc, NULL, 0)); 183 if (!texture) { 184 return NULL; 185 } 186 device = SkNEW_ARGS(SkGpuDevice, (context, texture.get())); 187 break; 188 } 189#endif 190 case kPDF_Backend: 191 default: 192 SkDEBUGFAIL("unsupported"); 193 } 194 return device; 195} 196 197#if SK_SUPPORT_GPU 198GrContextFactory gContextFactory; 199typedef GrContextFactory::GLContextType GLContextType; 200static const GLContextType kNative = GrContextFactory::kNative_GLContextType; 201#if SK_ANGLE 202static const GLContextType kANGLE = GrContextFactory::kANGLE_GLContextType; 203#else 204static const GLContextType kANGLE = kNative; 205#endif 206static const GLContextType kDebug = GrContextFactory::kDebug_GLContextType; 207static const GLContextType kNull = GrContextFactory::kNull_GLContextType; 208#else 209typedef int GLContextType; 210static const GLContextType kNative = 0, kANGLE = 0, kDebug = 0, kNull = 0; 211#endif 212 213#ifdef SK_DEBUG 214static const bool kIsDebug = true; 215#else 216static const bool kIsDebug = false; 217#endif 218 219static const struct Config { 220 SkBitmap::Config config; 221 const char* name; 222 int sampleCount; 223 Backend backend; 224 GLContextType contextType; 225 bool runByDefault; 226} gConfigs[] = { 227 { SkBitmap::kNo_Config, "NONRENDERING", 0, kNonRendering_Backend, kNative, true}, 228 { SkBitmap::kARGB_8888_Config, "8888", 0, kRaster_Backend, kNative, true}, 229 { SkBitmap::kRGB_565_Config, "565", 0, kRaster_Backend, kNative, true}, 230#if SK_SUPPORT_GPU 231 { SkBitmap::kARGB_8888_Config, "GPU", 0, kGPU_Backend, kNative, true}, 232 { SkBitmap::kARGB_8888_Config, "MSAA4", 4, kGPU_Backend, kNative, false}, 233 { SkBitmap::kARGB_8888_Config, "MSAA16", 16, kGPU_Backend, kNative, false}, 234#if SK_ANGLE 235 { SkBitmap::kARGB_8888_Config, "ANGLE", 0, kGPU_Backend, kANGLE, true}, 236#endif // SK_ANGLE 237 { SkBitmap::kARGB_8888_Config, "Debug", 0, kGPU_Backend, kDebug, kIsDebug}, 238 { SkBitmap::kARGB_8888_Config, "NULLGPU", 0, kGPU_Backend, kNull, true}, 239#endif // SK_SUPPORT_GPU 240}; 241 242DEFINE_string(outDir, "", "If given, image of each bench will be put in outDir."); 243DEFINE_string(timers, "cg", "Timers to display. " 244 "Options: w(all) W(all, truncated) c(pu) C(pu, truncated) g(pu)"); 245 246DEFINE_bool(rotate, false, "Rotate canvas before bench run?"); 247DEFINE_bool(scale, false, "Scale canvas before bench run?"); 248DEFINE_bool(clip, false, "Clip canvas before bench run?"); 249 250DEFINE_bool(forceAA, true, "Force anti-aliasing?"); 251DEFINE_bool(forceFilter, false, "Force bitmap filtering?"); 252DEFINE_string(forceDither, "default", "Force dithering: true, false, or default?"); 253DEFINE_bool(forceBlend, false, "Force alpha blending?"); 254 255DEFINE_int32(gpuCacheBytes, -1, "GPU cache size limit in bytes. 0 to disable cache."); 256DEFINE_int32(gpuCacheCount, -1, "GPU cache size limit in object count. 0 to disable cache."); 257 258DEFINE_string(match, "", "[~][^]substring[$] [...] of test name to run.\n" 259 "Multiple matches may be separated by spaces.\n" 260 "~ causes a matching test to always be skipped\n" 261 "^ requires the start of the test to match\n" 262 "$ requires the end of the test to match\n" 263 "^ and $ requires an exact match\n" 264 "If a test does not match any list entry,\n" 265 "it is skipped unless some list entry starts with ~\n"); 266DEFINE_string(mode, "normal", 267 "normal: draw to a normal canvas;\n" 268 "deferred: draw to a deferred canvas;\n" 269 "deferredSilent: deferred with silent playback;\n" 270 "record: draw to an SkPicture;\n" 271 "picturerecord: draw from an SkPicture to an SkPicture.\n"); 272DEFINE_string(config, "", "Run configs given. If empty, runs the defaults set in gConfigs."); 273DEFINE_string(logFile, "", "Also write stdout here."); 274DEFINE_int32(benchMs, 20, "Target time in ms to run each benchmark config."); 275DEFINE_string(timeFormat, "%9.2f", "Format to print results, in milliseconds per 1000 loops."); 276 277int tool_main(int argc, char** argv); 278int tool_main(int argc, char** argv) { 279#if SK_ENABLE_INST_COUNT 280 gPrintInstCount = true; 281#endif 282 SkAutoGraphics ag; 283 SkCommandLineFlags::Parse(argc, argv); 284 285 // First, parse some flags. 286 287 SkBenchLogger logger; 288 if (FLAGS_logFile.count()) { 289 logger.SetLogFile(FLAGS_logFile[0]); 290 } 291 292 const uint8_t alpha = FLAGS_forceBlend ? 0x80 : 0xFF; 293 SkTriState::State dither = SkTriState::kDefault; 294 for (size_t i = 0; i < 3; i++) { 295 if (strcmp(SkTriState::Name[i], FLAGS_forceDither[0]) == 0) { 296 dither = static_cast<SkTriState::State>(i); 297 } 298 } 299 300 BenchMode benchMode = kNormal_BenchMode; 301 for (size_t i = 0; i < SK_ARRAY_COUNT(BenchMode_Name); i++) { 302 if (strcmp(FLAGS_mode[0], BenchMode_Name[i]) == 0) { 303 benchMode = static_cast<BenchMode>(i); 304 } 305 } 306 307 SkTDArray<int> configs; 308 // Try user-given configs first. 309 for (int i = 0; i < FLAGS_config.count(); i++) { 310 for (size_t j = 0; j < SK_ARRAY_COUNT(gConfigs); j++) { 311 if (0 == strcmp(FLAGS_config[i], gConfigs[j].name)) { 312 *configs.append() = j; 313 } 314 } 315 } 316 // If there weren't any, fill in with defaults. 317 if (configs.count() == 0) { 318 for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); ++i) { 319 if (gConfigs[i].runByDefault) { 320 *configs.append() = i; 321 } 322 } 323 } 324 // Filter out things we can't run. 325 if (kNormal_BenchMode != benchMode) { 326 // Non-rendering configs only run in normal mode 327 for (int i = 0; i < configs.count(); ++i) { 328 const Config& config = gConfigs[configs[i]]; 329 if (kNonRendering_Backend == config.backend) { 330 configs.remove(i, 1); 331 --i; 332 } 333 } 334 } 335#if SK_SUPPORT_GPU 336 for (int i = 0; i < configs.count(); ++i) { 337 const Config& config = gConfigs[configs[i]]; 338 339 if (kGPU_Backend == config.backend) { 340 GrContext* context = gContextFactory.get(config.contextType); 341 if (NULL == context) { 342 SkString error; 343 error.printf("Error creating GrContext for config %s. Config will be skipped.\n", 344 config.name); 345 logger.logError(error); 346 configs.remove(i); 347 --i; 348 continue; 349 } 350 if (config.sampleCount > context->getMaxSampleCount()){ 351 SkString error; 352 error.printf("Sample count (%d) for config %s is unsupported. " 353 "Config will be skipped.\n", 354 config.sampleCount, config.name); 355 logger.logError(error); 356 configs.remove(i); 357 --i; 358 continue; 359 } 360 } 361 } 362#endif 363 364 // All flags should be parsed now. Report our settings. 365 if (kIsDebug) { 366 logger.logError("bench was built in Debug mode, so we're going to hide the times." 367 " It's for your own good!\n"); 368 } 369 SkString str("skia bench:"); 370 str.appendf(" mode=%s", FLAGS_mode[0]); 371 str.appendf(" alpha=0x%02X antialias=%d filter=%d dither=%s", 372 alpha, FLAGS_forceAA, FLAGS_forceFilter, SkTriState::Name[dither]); 373 str.appendf(" rotate=%d scale=%d clip=%d", FLAGS_rotate, FLAGS_scale, FLAGS_clip); 374 375#if defined(SK_SCALAR_IS_FIXED) 376 str.append(" scalar=fixed"); 377#else 378 str.append(" scalar=float"); 379#endif 380 381#if defined(SK_BUILD_FOR_WIN32) 382 str.append(" system=WIN32"); 383#elif defined(SK_BUILD_FOR_MAC) 384 str.append(" system=MAC"); 385#elif defined(SK_BUILD_FOR_ANDROID) 386 str.append(" system=ANDROID"); 387#elif defined(SK_BUILD_FOR_UNIX) 388 str.append(" system=UNIX"); 389#else 390 str.append(" system=other"); 391#endif 392 393#if defined(SK_DEBUG) 394 str.append(" DEBUG"); 395#endif 396 str.append("\n"); 397 logger.logProgress(str); 398 399 400 // Set texture cache limits if non-default. 401 for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); ++i) { 402#if SK_SUPPORT_GPU 403 const Config& config = gConfigs[i]; 404 if (kGPU_Backend != config.backend) { 405 continue; 406 } 407 GrContext* context = gContextFactory.get(config.contextType); 408 if (NULL == context) { 409 continue; 410 } 411 412 size_t bytes; 413 int count; 414 context->getTextureCacheLimits(&count, &bytes); 415 if (-1 != FLAGS_gpuCacheBytes) { 416 bytes = static_cast<size_t>(FLAGS_gpuCacheBytes); 417 } 418 if (-1 != FLAGS_gpuCacheCount) { 419 count = FLAGS_gpuCacheCount; 420 } 421 context->setTextureCacheLimits(count, bytes); 422#endif 423 } 424 425 // Find the longest name of the benches we're going to run to make the output pretty. 426 Iter names; 427 SkBenchmark* bench; 428 int longestName = 0; 429 while ((bench = names.next()) != NULL) { 430 SkAutoTUnref<SkBenchmark> benchUnref(bench); 431 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getName())) { 432 continue; 433 } 434 const int length = strlen(bench->getName()); 435 longestName = length > longestName ? length : longestName; 436 } 437 438 // Run each bench in each configuration it supports and we asked for. 439 Iter iter; 440 while ((bench = iter.next()) != NULL) { 441 SkAutoTUnref<SkBenchmark> benchUnref(bench); 442 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getName())) { 443 continue; 444 } 445 446 bench->setForceAlpha(alpha); 447 bench->setForceAA(FLAGS_forceAA); 448 bench->setForceFilter(FLAGS_forceFilter); 449 bench->setDither(dither); 450 AutoPrePostDraw appd(bench); 451 452 bool loggedBenchName = false; 453 for (int i = 0; i < configs.count(); ++i) { 454 const int configIndex = configs[i]; 455 const Config& config = gConfigs[configIndex]; 456 457 if ((kNonRendering_Backend == config.backend) == bench->isRendering()) { 458 continue; 459 } 460 461 GrContext* context = NULL; 462#if SK_SUPPORT_GPU 463 SkGLContextHelper* glContext = NULL; 464 if (kGPU_Backend == config.backend) { 465 context = gContextFactory.get(config.contextType); 466 if (NULL == context) { 467 continue; 468 } 469 glContext = gContextFactory.getGLContext(config.contextType); 470 } 471#endif 472 SkAutoTUnref<SkBaseDevice> device; 473 SkAutoTUnref<SkCanvas> canvas; 474 SkPicture recordFrom, recordTo; 475 const SkIPoint dim = bench->getSize(); 476 477 const SkPicture::RecordingFlags kRecordFlags = 478 SkPicture::kUsePathBoundsForClip_RecordingFlag; 479 480 if (kNonRendering_Backend != config.backend) { 481 device.reset(make_device(config.config, 482 dim, 483 config.backend, 484 config.sampleCount, 485 context)); 486 if (!device.get()) { 487 SkString error; 488 error.printf("Device creation failure for config %s. Will skip.\n", config.name); 489 logger.logError(error); 490 continue; 491 } 492 493 switch(benchMode) { 494 case kDeferredSilent_BenchMode: 495 case kDeferred_BenchMode: 496 canvas.reset(SkDeferredCanvas::Create(device.get())); 497 break; 498 case kRecord_BenchMode: 499 canvas.reset(SkRef(recordTo.beginRecording(dim.fX, dim.fY, kRecordFlags))); 500 break; 501 case kPictureRecord_BenchMode: 502 bench->draw(recordFrom.beginRecording(dim.fX, dim.fY, kRecordFlags)); 503 recordFrom.endRecording(); 504 canvas.reset(SkRef(recordTo.beginRecording(dim.fX, dim.fY, kRecordFlags))); 505 break; 506 case kNormal_BenchMode: 507 canvas.reset(new SkCanvas(device.get())); 508 break; 509 default: 510 SkASSERT(false); 511 } 512 } 513 514 if (NULL != canvas) { 515 canvas->clear(SK_ColorWHITE); 516 if (FLAGS_clip) { performClip(canvas, dim.fX, dim.fY); } 517 if (FLAGS_scale) { performScale(canvas, dim.fX, dim.fY); } 518 if (FLAGS_rotate) { performRotate(canvas, dim.fX, dim.fY); } 519 } 520 521 if (!loggedBenchName) { 522 loggedBenchName = true; 523 SkString str; 524 str.printf("running bench [%3d %3d] %*s ", 525 dim.fX, dim.fY, longestName, bench->getName()); 526 logger.logProgress(str); 527 } 528 529#if SK_SUPPORT_GPU 530 SkGLContextHelper* contextHelper = NULL; 531 if (kGPU_Backend == config.backend) { 532 contextHelper = gContextFactory.getGLContext(config.contextType); 533 } 534 BenchTimer timer(contextHelper); 535#else 536 BenchTimer timer; 537#endif 538 539 bench->setLoops(0); 540 do { 541 // Ramp up 1 -> 4 -> 16 -> ... -> ~1 billion. 542 const int loops = bench->getLoops(); 543 if (loops >= (1<<30)) { 544 // If you find it takes more than a billion loops to get up to 20ms of runtime, 545 // you've got a computer clocked at several THz or have a broken benchmark. ;) 546 // "1B ought to be enough for anybody." 547 SkString str; 548 str.printf("Can't ramp %s to %dms.\n", bench->getName(), FLAGS_benchMs); 549 logger.logError(str); 550 break; 551 } 552 bench->setLoops(loops == 0 ? 1 : loops * 4); 553 554 if ((benchMode == kRecord_BenchMode || benchMode == kPictureRecord_BenchMode)) { 555 // Clear the recorded commands so that they do not accumulate. 556 canvas.reset(recordTo.beginRecording(dim.fX, dim.fY, kRecordFlags)); 557 } 558 559 timer.start(); 560 if (NULL != canvas) { 561 canvas->save(); 562 } 563 if (benchMode == kPictureRecord_BenchMode) { 564 recordFrom.draw(canvas); 565 } else { 566 bench->draw(canvas); 567 } 568 569 if (kDeferredSilent_BenchMode == benchMode) { 570 static_cast<SkDeferredCanvas*>(canvas.get())->silentFlush(); 571 } else if (NULL != canvas) { 572 canvas->flush(); 573 } 574 575 if (NULL != canvas) { 576 canvas->restore(); 577 } 578 579 580 // Stop truncated timers before GL calls complete, and stop the full timers after. 581 timer.truncatedEnd(); 582#if SK_SUPPORT_GPU 583 if (NULL != glContext) { 584 context->flush(); 585 SK_GL(*glContext, Finish()); 586 } 587#endif 588 timer.end(); 589 } while (!kIsDebug && timer.fWall < FLAGS_benchMs); // One loop only in debug mode. 590 591 if (FLAGS_outDir.count() && kNonRendering_Backend != config.backend) { 592 saveFile(bench->getName(), 593 config.name, 594 FLAGS_outDir[0], 595 device->accessBitmap(false)); 596 } 597 598 if (kIsDebug) { 599 // Let's not mislead ourselves by looking at Debug build bench times! 600 continue; 601 } 602 603 // Normalize to ms per 1000 iterations. 604 const double normalize = 1000.0 / bench->getLoops(); 605 const struct { char shortName; const char* longName; double ms; } times[] = { 606 {'w', "msecs", normalize * timer.fWall}, 607 {'W', "Wmsecs", normalize * timer.fTruncatedWall}, 608 {'c', "cmsecs", normalize * timer.fCpu}, 609 {'C', "Cmsecs", normalize * timer.fTruncatedCpu}, 610 {'g', "gmsecs", normalize * timer.fGpu}, 611 }; 612 613 SkString result; 614 result.appendf(" %s:", config.name); 615 for (size_t i = 0; i < SK_ARRAY_COUNT(times); i++) { 616 if (strchr(FLAGS_timers[0], times[i].shortName) && times[i].ms > 0) { 617 result.appendf(" %s = ", times[i].longName); 618 result.appendf(FLAGS_timeFormat[0], times[i].ms); 619 } 620 } 621 logger.logProgress(result); 622 } 623 if (loggedBenchName) { 624 logger.logProgress("\n"); 625 } 626 } 627#if SK_SUPPORT_GPU 628 gContextFactory.destroyContexts(); 629#endif 630 return 0; 631} 632 633#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) 634int main(int argc, char * const argv[]) { 635 return tool_main(argc, (char**) argv); 636} 637#endif 638