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