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