1 /* 2 * Copyright 2014 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 <ctype.h> 9 10 #include "Benchmark.h" 11 #include "CrashHandler.h" 12 #include "GMBench.h" 13 #include "ProcStats.h" 14 #include "ResultsWriter.h" 15 #include "RecordingBench.h" 16 #include "SKPBench.h" 17 #include "Stats.h" 18 #include "Timer.h" 19 20 #include "SkBBHFactory.h" 21 #include "SkCanvas.h" 22 #include "SkCommonFlags.h" 23 #include "SkForceLinking.h" 24 #include "SkGraphics.h" 25 #include "SkOSFile.h" 26 #include "SkPictureRecorder.h" 27 #include "SkString.h" 28 #include "SkSurface.h" 29 30 #if SK_SUPPORT_GPU 31 #include "gl/GrGLDefines.h" 32 #include "GrContextFactory.h" 33 SkAutoTDelete<GrContextFactory> gGrFactory; 34 #endif 35 36 __SK_FORCE_IMAGE_DECODER_LINKING; 37 38 static const int kAutoTuneLoops = -1; 39 40 static const int kDefaultLoops = 41 #ifdef SK_DEBUG 42 1; 43 #else 44 kAutoTuneLoops; 45 #endif 46 47 static SkString loops_help_txt() { 48 SkString help; 49 help.printf("Number of times to run each bench. Set this to %d to auto-" 50 "tune for each bench. Timings are only reported when auto-tuning.", 51 kAutoTuneLoops); 52 return help; 53 } 54 55 DEFINE_int32(loops, kDefaultLoops, loops_help_txt().c_str()); 56 57 DEFINE_int32(samples, 10, "Number of samples to measure for each bench."); 58 DEFINE_int32(overheadLoops, 100000, "Loops to estimate timer overhead."); 59 DEFINE_double(overheadGoal, 0.0001, 60 "Loop until timer overhead is at most this fraction of our measurments."); 61 DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU."); 62 DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allows to lag."); 63 DEFINE_bool(gpuCompressAlphaMasks, false, "Compress masks generated from falling back to " 64 "software path rendering."); 65 66 DEFINE_string(outResultsFile, "", "If given, write results here as JSON."); 67 DEFINE_int32(maxCalibrationAttempts, 3, 68 "Try up to this many times to guess loops for a bench, or skip the bench."); 69 DEFINE_int32(maxLoops, 1000000, "Never run a bench more times than this."); 70 DEFINE_string(clip, "0,0,1000,1000", "Clip for SKPs."); 71 DEFINE_string(scales, "1.0", "Space-separated scales for SKPs."); 72 DEFINE_bool(bbh, true, "Build a BBH for SKPs?"); 73 74 static SkString humanize(double ms) { 75 if (FLAGS_verbose) return SkStringPrintf("%llu", (uint64_t)(ms*1e6)); 76 if (ms > 1e+3) return SkStringPrintf("%.3gs", ms/1e3); 77 if (ms < 1e-3) return SkStringPrintf("%.3gns", ms*1e6); 78 #ifdef SK_BUILD_FOR_WIN 79 if (ms < 1) return SkStringPrintf("%.3gus", ms*1e3); 80 #else 81 if (ms < 1) return SkStringPrintf("%.3gs", ms*1e3); 82 #endif 83 return SkStringPrintf("%.3gms", ms); 84 } 85 #define HUMANIZE(ms) humanize(ms).c_str() 86 87 static double time(int loops, Benchmark* bench, SkCanvas* canvas, SkGLContextHelper* gl) { 88 if (canvas) { 89 canvas->clear(SK_ColorWHITE); 90 } 91 WallTimer timer; 92 timer.start(); 93 if (bench) { 94 bench->draw(loops, canvas); 95 } 96 if (canvas) { 97 canvas->flush(); 98 } 99 #if SK_SUPPORT_GPU 100 if (gl) { 101 SK_GL(*gl, Flush()); 102 gl->swapBuffers(); 103 } 104 #endif 105 timer.end(); 106 return timer.fWall; 107 } 108 109 static double estimate_timer_overhead() { 110 double overhead = 0; 111 for (int i = 0; i < FLAGS_overheadLoops; i++) { 112 overhead += time(1, NULL, NULL, NULL); 113 } 114 return overhead / FLAGS_overheadLoops; 115 } 116 117 static int clamp_loops(int loops) { 118 if (loops < 1) { 119 SkDebugf("ERROR: clamping loops from %d to 1.\n", loops); 120 return 1; 121 } 122 if (loops > FLAGS_maxLoops) { 123 SkDebugf("WARNING: clamping loops from %d to FLAGS_maxLoops, %d.\n", loops, FLAGS_maxLoops); 124 return FLAGS_maxLoops; 125 } 126 return loops; 127 } 128 129 static bool write_canvas_png(SkCanvas* canvas, const SkString& filename) { 130 if (filename.isEmpty()) { 131 return false; 132 } 133 if (kUnknown_SkColorType == canvas->imageInfo().colorType()) { 134 return false; 135 } 136 SkBitmap bmp; 137 bmp.setInfo(canvas->imageInfo()); 138 if (!canvas->readPixels(&bmp, 0, 0)) { 139 SkDebugf("Can't read canvas pixels.\n"); 140 return false; 141 } 142 SkString dir = SkOSPath::Dirname(filename.c_str()); 143 if (!sk_mkdir(dir.c_str())) { 144 SkDebugf("Can't make dir %s.\n", dir.c_str()); 145 return false; 146 } 147 SkFILEWStream stream(filename.c_str()); 148 if (!stream.isValid()) { 149 SkDebugf("Can't write %s.\n", filename.c_str()); 150 return false; 151 } 152 if (!SkImageEncoder::EncodeStream(&stream, bmp, SkImageEncoder::kPNG_Type, 100)) { 153 SkDebugf("Can't encode a PNG.\n"); 154 return false; 155 } 156 return true; 157 } 158 159 static int kFailedLoops = -2; 160 static int cpu_bench(const double overhead, Benchmark* bench, SkCanvas* canvas, double* samples) { 161 // First figure out approximately how many loops of bench it takes to make overhead negligible. 162 double bench_plus_overhead = 0.0; 163 int round = 0; 164 if (kAutoTuneLoops == FLAGS_loops) { 165 while (bench_plus_overhead < overhead) { 166 if (round++ == FLAGS_maxCalibrationAttempts) { 167 SkDebugf("WARNING: Can't estimate loops for %s (%s vs. %s); skipping.\n", 168 bench->getUniqueName(), HUMANIZE(bench_plus_overhead), HUMANIZE(overhead)); 169 return kFailedLoops; 170 } 171 bench_plus_overhead = time(1, bench, canvas, NULL); 172 } 173 } 174 175 // Later we'll just start and stop the timer once but loop N times. 176 // We'll pick N to make timer overhead negligible: 177 // 178 // overhead 179 // ------------------------- < FLAGS_overheadGoal 180 // overhead + N * Bench Time 181 // 182 // where bench_plus_overhead overhead + Bench Time. 183 // 184 // Doing some math, we get: 185 // 186 // (overhead / FLAGS_overheadGoal) - overhead 187 // ------------------------------------------ < N 188 // bench_plus_overhead - overhead) 189 // 190 // Luckily, this also works well in practice. :) 191 int loops = FLAGS_loops; 192 if (kAutoTuneLoops == loops) { 193 const double numer = overhead / FLAGS_overheadGoal - overhead; 194 const double denom = bench_plus_overhead - overhead; 195 loops = (int)ceil(numer / denom); 196 } 197 loops = clamp_loops(loops); 198 199 for (int i = 0; i < FLAGS_samples; i++) { 200 samples[i] = time(loops, bench, canvas, NULL) / loops; 201 } 202 return loops; 203 } 204 205 #if SK_SUPPORT_GPU 206 static int gpu_bench(SkGLContextHelper* gl, 207 Benchmark* bench, 208 SkCanvas* canvas, 209 double* samples) { 210 gl->makeCurrent(); 211 // Make sure we're done with whatever came before. 212 SK_GL(*gl, Finish()); 213 214 // First, figure out how many loops it'll take to get a frame up to FLAGS_gpuMs. 215 int loops = FLAGS_loops; 216 if (kAutoTuneLoops == loops) { 217 loops = 1; 218 double elapsed = 0; 219 do { 220 loops *= 2; 221 // If the GPU lets frames lag at all, we need to make sure we're timing 222 // _this_ round, not still timing last round. We force this by looping 223 // more times than any reasonable GPU will allow frames to lag. 224 for (int i = 0; i < FLAGS_gpuFrameLag; i++) { 225 elapsed = time(loops, bench, canvas, gl); 226 } 227 } while (elapsed < FLAGS_gpuMs); 228 229 // We've overshot at least a little. Scale back linearly. 230 loops = (int)ceil(loops * FLAGS_gpuMs / elapsed); 231 232 // Might as well make sure we're not still timing our calibration. 233 SK_GL(*gl, Finish()); 234 } 235 loops = clamp_loops(loops); 236 237 // Pretty much the same deal as the calibration: do some warmup to make 238 // sure we're timing steady-state pipelined frames. 239 for (int i = 0; i < FLAGS_gpuFrameLag; i++) { 240 time(loops, bench, canvas, gl); 241 } 242 243 // Now, actually do the timing! 244 for (int i = 0; i < FLAGS_samples; i++) { 245 samples[i] = time(loops, bench, canvas, gl) / loops; 246 } 247 return loops; 248 } 249 #endif 250 251 static SkString to_lower(const char* str) { 252 SkString lower(str); 253 for (size_t i = 0; i < lower.size(); i++) { 254 lower[i] = tolower(lower[i]); 255 } 256 return lower; 257 } 258 259 struct Config { 260 const char* name; 261 Benchmark::Backend backend; 262 SkColorType color; 263 SkAlphaType alpha; 264 int samples; 265 #if SK_SUPPORT_GPU 266 GrContextFactory::GLContextType ctxType; 267 #else 268 int bogusInt; 269 #endif 270 }; 271 272 struct Target { 273 explicit Target(const Config& c) : config(c) {} 274 const Config config; 275 SkAutoTDelete<SkSurface> surface; 276 #if SK_SUPPORT_GPU 277 SkGLContextHelper* gl; 278 #endif 279 }; 280 281 static bool is_cpu_config_allowed(const char* name) { 282 for (int i = 0; i < FLAGS_config.count(); i++) { 283 if (to_lower(FLAGS_config[i]).equals(name)) { 284 return true; 285 } 286 } 287 return false; 288 } 289 290 #if SK_SUPPORT_GPU 291 static bool is_gpu_config_allowed(const char* name, GrContextFactory::GLContextType ctxType, 292 int sampleCnt) { 293 if (!is_cpu_config_allowed(name)) { 294 return false; 295 } 296 if (const GrContext* ctx = gGrFactory->get(ctxType)) { 297 return sampleCnt <= ctx->getMaxSampleCount(); 298 } 299 return false; 300 } 301 #endif 302 303 #if SK_SUPPORT_GPU 304 #define kBogusGLContextType GrContextFactory::kNative_GLContextType 305 #else 306 #define kBogusGLContextType 0 307 #endif 308 309 // Append all configs that are enabled and supported. 310 static void create_configs(SkTDArray<Config>* configs) { 311 #define CPU_CONFIG(name, backend, color, alpha) \ 312 if (is_cpu_config_allowed(#name)) { \ 313 Config config = { #name, Benchmark::backend, color, alpha, 0, kBogusGLContextType }; \ 314 configs->push(config); \ 315 } 316 317 if (FLAGS_cpu) { 318 CPU_CONFIG(nonrendering, kNonRendering_Backend, kUnknown_SkColorType, kUnpremul_SkAlphaType) 319 CPU_CONFIG(8888, kRaster_Backend, kN32_SkColorType, kPremul_SkAlphaType) 320 CPU_CONFIG(565, kRaster_Backend, kRGB_565_SkColorType, kOpaque_SkAlphaType) 321 } 322 323 #if SK_SUPPORT_GPU 324 #define GPU_CONFIG(name, ctxType, samples) \ 325 if (is_gpu_config_allowed(#name, GrContextFactory::ctxType, samples)) { \ 326 Config config = { \ 327 #name, \ 328 Benchmark::kGPU_Backend, \ 329 kN32_SkColorType, \ 330 kPremul_SkAlphaType, \ 331 samples, \ 332 GrContextFactory::ctxType }; \ 333 configs->push(config); \ 334 } 335 336 if (FLAGS_gpu) { 337 GPU_CONFIG(gpu, kNative_GLContextType, 0) 338 GPU_CONFIG(msaa4, kNative_GLContextType, 4) 339 GPU_CONFIG(msaa16, kNative_GLContextType, 16) 340 GPU_CONFIG(nvprmsaa4, kNVPR_GLContextType, 4) 341 GPU_CONFIG(nvprmsaa16, kNVPR_GLContextType, 16) 342 GPU_CONFIG(debug, kDebug_GLContextType, 0) 343 GPU_CONFIG(nullgpu, kNull_GLContextType, 0) 344 #ifdef SK_ANGLE 345 GPU_CONFIG(angle, kANGLE_GLContextType, 0) 346 #endif 347 } 348 #endif 349 } 350 351 // If bench is enabled for config, returns a Target* for it, otherwise NULL. 352 static Target* is_enabled(Benchmark* bench, const Config& config) { 353 if (!bench->isSuitableFor(config.backend)) { 354 return NULL; 355 } 356 357 SkImageInfo info = SkImageInfo::Make(bench->getSize().fX, bench->getSize().fY, 358 config.color, config.alpha); 359 360 Target* target = new Target(config); 361 362 if (Benchmark::kRaster_Backend == config.backend) { 363 target->surface.reset(SkSurface::NewRaster(info)); 364 } 365 #if SK_SUPPORT_GPU 366 else if (Benchmark::kGPU_Backend == config.backend) { 367 target->surface.reset(SkSurface::NewRenderTarget(gGrFactory->get(config.ctxType), info, 368 config.samples)); 369 target->gl = gGrFactory->getGLContext(config.ctxType); 370 } 371 #endif 372 373 if (Benchmark::kNonRendering_Backend != config.backend && !target->surface.get()) { 374 delete target; 375 return NULL; 376 } 377 return target; 378 } 379 380 // Creates targets for a benchmark and a set of configs. 381 static void create_targets(SkTDArray<Target*>* targets, Benchmark* b, 382 const SkTDArray<Config>& configs) { 383 for (int i = 0; i < configs.count(); ++i) { 384 if (Target* t = is_enabled(b, configs[i])) { 385 targets->push(t); 386 } 387 388 } 389 } 390 391 #if SK_SUPPORT_GPU 392 static void fill_gpu_options(ResultsWriter* log, SkGLContextHelper* ctx) { 393 const GrGLubyte* version; 394 SK_GL_RET(*ctx, version, GetString(GR_GL_VERSION)); 395 log->configOption("GL_VERSION", (const char*)(version)); 396 397 SK_GL_RET(*ctx, version, GetString(GR_GL_RENDERER)); 398 log->configOption("GL_RENDERER", (const char*) version); 399 400 SK_GL_RET(*ctx, version, GetString(GR_GL_VENDOR)); 401 log->configOption("GL_VENDOR", (const char*) version); 402 403 SK_GL_RET(*ctx, version, GetString(GR_GL_SHADING_LANGUAGE_VERSION)); 404 log->configOption("GL_SHADING_LANGUAGE_VERSION", (const char*) version); 405 } 406 #endif 407 408 class BenchmarkStream { 409 public: 410 BenchmarkStream() : fBenches(BenchRegistry::Head()) 411 , fGMs(skiagm::GMRegistry::Head()) 412 , fCurrentRecording(0) 413 , fCurrentScale(0) 414 , fCurrentSKP(0) { 415 for (int i = 0; i < FLAGS_skps.count(); i++) { 416 if (SkStrEndsWith(FLAGS_skps[i], ".skp")) { 417 fSKPs.push_back() = FLAGS_skps[i]; 418 } else { 419 SkOSFile::Iter it(FLAGS_skps[i], ".skp"); 420 SkString path; 421 while (it.next(&path)) { 422 fSKPs.push_back() = SkOSPath::Join(FLAGS_skps[0], path.c_str()); 423 } 424 } 425 } 426 427 if (4 != sscanf(FLAGS_clip[0], "%d,%d,%d,%d", 428 &fClip.fLeft, &fClip.fTop, &fClip.fRight, &fClip.fBottom)) { 429 SkDebugf("Can't parse %s from --clip as an SkIRect.\n", FLAGS_clip[0]); 430 exit(1); 431 } 432 433 for (int i = 0; i < FLAGS_scales.count(); i++) { 434 if (1 != sscanf(FLAGS_scales[i], "%f", &fScales.push_back())) { 435 SkDebugf("Can't parse %s from --scales as an SkScalar.\n", FLAGS_scales[i]); 436 exit(1); 437 } 438 } 439 } 440 441 static bool ReadPicture(const char* path, SkAutoTUnref<SkPicture>* pic) { 442 // Not strictly necessary, as it will be checked again later, 443 // but helps to avoid a lot of pointless work if we're going to skip it. 444 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path)) { 445 return false; 446 } 447 448 SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path)); 449 if (stream.get() == NULL) { 450 SkDebugf("Could not read %s.\n", path); 451 return false; 452 } 453 454 pic->reset(SkPicture::CreateFromStream(stream.get())); 455 if (pic->get() == NULL) { 456 SkDebugf("Could not read %s as an SkPicture.\n", path); 457 return false; 458 } 459 return true; 460 } 461 462 Benchmark* next() { 463 if (fBenches) { 464 Benchmark* bench = fBenches->factory()(NULL); 465 fBenches = fBenches->next(); 466 fSourceType = "bench"; 467 fBenchType = "micro"; 468 return bench; 469 } 470 471 while (fGMs) { 472 SkAutoTDelete<skiagm::GM> gm(fGMs->factory()(NULL)); 473 fGMs = fGMs->next(); 474 if (gm->getFlags() & skiagm::GM::kAsBench_Flag) { 475 fSourceType = "gm"; 476 fBenchType = "micro"; 477 return SkNEW_ARGS(GMBench, (gm.detach())); 478 } 479 } 480 481 // First add all .skps as RecordingBenches. 482 while (fCurrentRecording < fSKPs.count()) { 483 const SkString& path = fSKPs[fCurrentRecording++]; 484 SkAutoTUnref<SkPicture> pic; 485 if (!ReadPicture(path.c_str(), &pic)) { 486 continue; 487 } 488 SkString name = SkOSPath::Basename(path.c_str()); 489 fSourceType = "skp"; 490 fBenchType = "recording"; 491 return SkNEW_ARGS(RecordingBench, (name.c_str(), pic.get(), FLAGS_bbh)); 492 } 493 494 // Then once each for each scale as SKPBenches (playback). 495 while (fCurrentScale < fScales.count()) { 496 while (fCurrentSKP < fSKPs.count()) { 497 const SkString& path = fSKPs[fCurrentSKP++]; 498 SkAutoTUnref<SkPicture> pic; 499 if (!ReadPicture(path.c_str(), &pic)) { 500 continue; 501 } 502 if (FLAGS_bbh) { 503 // The SKP we read off disk doesn't have a BBH. Re-record so it grows one. 504 // Here we use an SkTileGrid with parameters optimized for FLAGS_clip. 505 const SkTileGridFactory::TileGridInfo info = { 506 SkISize::Make(fClip.width(), fClip.height()), // tile interval 507 SkISize::Make(0,0), // margin 508 SkIPoint::Make(fClip.left(), fClip.top()), // offset 509 }; 510 SkTileGridFactory factory(info); 511 SkPictureRecorder recorder; 512 pic->playback(recorder.beginRecording(pic->cullRect().width(), 513 pic->cullRect().height(), 514 &factory)); 515 pic.reset(recorder.endRecording()); 516 } 517 SkString name = SkOSPath::Basename(path.c_str()); 518 fSourceType = "skp"; 519 fBenchType = "playback"; 520 return SkNEW_ARGS(SKPBench, 521 (name.c_str(), pic.get(), fClip, fScales[fCurrentScale])); 522 } 523 fCurrentSKP = 0; 524 fCurrentScale++; 525 } 526 527 return NULL; 528 } 529 530 void fillCurrentOptions(ResultsWriter* log) const { 531 log->configOption("source_type", fSourceType); 532 log->configOption("bench_type", fBenchType); 533 if (0 == strcmp(fSourceType, "skp")) { 534 log->configOption("clip", 535 SkStringPrintf("%d %d %d %d", fClip.fLeft, fClip.fTop, 536 fClip.fRight, fClip.fBottom).c_str()); 537 log->configOption("scale", SkStringPrintf("%.2g", fScales[fCurrentScale]).c_str()); 538 } 539 } 540 541 private: 542 const BenchRegistry* fBenches; 543 const skiagm::GMRegistry* fGMs; 544 SkIRect fClip; 545 SkTArray<SkScalar> fScales; 546 SkTArray<SkString> fSKPs; 547 548 const char* fSourceType; // What we're benching: bench, GM, SKP, ... 549 const char* fBenchType; // How we bench it: micro, recording, playback, ... 550 int fCurrentRecording; 551 int fCurrentScale; 552 int fCurrentSKP; 553 }; 554 555 int nanobench_main(); 556 int nanobench_main() { 557 SetupCrashHandler(); 558 SkAutoGraphics ag; 559 560 #if SK_SUPPORT_GPU 561 GrContext::Options grContextOpts; 562 grContextOpts.fDrawPathToCompressedTexture = FLAGS_gpuCompressAlphaMasks; 563 gGrFactory.reset(SkNEW_ARGS(GrContextFactory, (grContextOpts))); 564 #endif 565 566 if (kAutoTuneLoops != FLAGS_loops) { 567 FLAGS_samples = 1; 568 FLAGS_gpuFrameLag = 0; 569 } 570 571 if (!FLAGS_writePath.isEmpty()) { 572 SkDebugf("Writing files to %s.\n", FLAGS_writePath[0]); 573 if (!sk_mkdir(FLAGS_writePath[0])) { 574 SkDebugf("Could not create %s. Files won't be written.\n", FLAGS_writePath[0]); 575 FLAGS_writePath.set(0, NULL); 576 } 577 } 578 579 SkAutoTDelete<ResultsWriter> log(SkNEW(ResultsWriter)); 580 if (!FLAGS_outResultsFile.isEmpty()) { 581 log.reset(SkNEW(NanoJSONResultsWriter(FLAGS_outResultsFile[0]))); 582 } 583 584 if (1 == FLAGS_properties.count() % 2) { 585 SkDebugf("ERROR: --properties must be passed with an even number of arguments.\n"); 586 return 1; 587 } 588 for (int i = 1; i < FLAGS_properties.count(); i += 2) { 589 log->property(FLAGS_properties[i-1], FLAGS_properties[i]); 590 } 591 592 if (1 == FLAGS_key.count() % 2) { 593 SkDebugf("ERROR: --key must be passed with an even number of arguments.\n"); 594 return 1; 595 } 596 for (int i = 1; i < FLAGS_key.count(); i += 2) { 597 log->key(FLAGS_key[i-1], FLAGS_key[i]); 598 } 599 600 const double overhead = estimate_timer_overhead(); 601 SkDebugf("Timer overhead: %s\n", HUMANIZE(overhead)); 602 603 SkAutoTMalloc<double> samples(FLAGS_samples); 604 605 if (kAutoTuneLoops != FLAGS_loops) { 606 SkDebugf("Fixed number of loops; times would only be misleading so we won't print them.\n"); 607 } else if (FLAGS_verbose) { 608 // No header. 609 } else if (FLAGS_quiet) { 610 SkDebugf("median\tbench\tconfig\n"); 611 } else { 612 SkDebugf("maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tconfig\tbench\n", 613 FLAGS_samples, "samples"); 614 } 615 616 SkTDArray<Config> configs; 617 create_configs(&configs); 618 619 BenchmarkStream benchStream; 620 while (Benchmark* b = benchStream.next()) { 621 SkAutoTDelete<Benchmark> bench(b); 622 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getUniqueName())) { 623 continue; 624 } 625 626 SkTDArray<Target*> targets; 627 create_targets(&targets, bench.get(), configs); 628 629 if (!targets.isEmpty()) { 630 log->bench(bench->getUniqueName(), bench->getSize().fX, bench->getSize().fY); 631 bench->preDraw(); 632 } 633 for (int j = 0; j < targets.count(); j++) { 634 SkCanvas* canvas = targets[j]->surface.get() ? targets[j]->surface->getCanvas() : NULL; 635 const char* config = targets[j]->config.name; 636 637 const int loops = 638 #if SK_SUPPORT_GPU 639 Benchmark::kGPU_Backend == targets[j]->config.backend 640 ? gpu_bench(targets[j]->gl, bench.get(), canvas, samples.get()) 641 : 642 #endif 643 cpu_bench( overhead, bench.get(), canvas, samples.get()); 644 645 if (canvas && !FLAGS_writePath.isEmpty() && FLAGS_writePath[0]) { 646 SkString pngFilename = SkOSPath::Join(FLAGS_writePath[0], config); 647 pngFilename = SkOSPath::Join(pngFilename.c_str(), bench->getUniqueName()); 648 pngFilename.append(".png"); 649 write_canvas_png(canvas, pngFilename); 650 } 651 652 if (kFailedLoops == loops) { 653 // Can't be timed. A warning note has already been printed. 654 continue; 655 } 656 657 Stats stats(samples.get(), FLAGS_samples); 658 log->config(config); 659 log->configOption("name", bench->getName()); 660 benchStream.fillCurrentOptions(log.get()); 661 #if SK_SUPPORT_GPU 662 if (Benchmark::kGPU_Backend == targets[j]->config.backend) { 663 fill_gpu_options(log.get(), targets[j]->gl); 664 } 665 #endif 666 log->timer("min_ms", stats.min); 667 log->timer("median_ms", stats.median); 668 log->timer("mean_ms", stats.mean); 669 log->timer("max_ms", stats.max); 670 log->timer("stddev_ms", sqrt(stats.var)); 671 672 if (kAutoTuneLoops != FLAGS_loops) { 673 if (targets.count() == 1) { 674 config = ""; // Only print the config if we run the same bench on more than one. 675 } 676 SkDebugf("%4dM\t%s\t%s\n" 677 , sk_tools::getMaxResidentSetSizeMB() 678 , bench->getUniqueName() 679 , config); 680 } else if (FLAGS_verbose) { 681 for (int i = 0; i < FLAGS_samples; i++) { 682 SkDebugf("%s ", HUMANIZE(samples[i])); 683 } 684 SkDebugf("%s\n", bench->getUniqueName()); 685 } else if (FLAGS_quiet) { 686 if (targets.count() == 1) { 687 config = ""; // Only print the config if we run the same bench on more than one. 688 } 689 SkDebugf("%s\t%s\t%s\n", HUMANIZE(stats.median), bench->getUniqueName(), config); 690 } else { 691 const double stddev_percent = 100 * sqrt(stats.var) / stats.mean; 692 SkDebugf("%4dM\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n" 693 , sk_tools::getMaxResidentSetSizeMB() 694 , loops 695 , HUMANIZE(stats.min) 696 , HUMANIZE(stats.median) 697 , HUMANIZE(stats.mean) 698 , HUMANIZE(stats.max) 699 , stddev_percent 700 , stats.plot.c_str() 701 , config 702 , bench->getUniqueName() 703 ); 704 } 705 } 706 targets.deleteAll(); 707 708 #if SK_SUPPORT_GPU 709 if (FLAGS_abandonGpuContext) { 710 gGrFactory->abandonContexts(); 711 } 712 if (FLAGS_resetGpuContext || FLAGS_abandonGpuContext) { 713 gGrFactory->destroyContexts(); 714 } 715 #endif 716 } 717 718 return 0; 719 } 720 721 #if !defined SK_BUILD_FOR_IOS 722 int main(int argc, char** argv) { 723 SkCommandLineFlags::Parse(argc, argv); 724 return nanobench_main(); 725 } 726 #endif 727