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