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