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