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 "nanobench.h" 11 12 #include "AndroidCodecBench.h" 13 #include "Benchmark.h" 14 #include "BitmapRegionDecoderBench.h" 15 #include "CodecBench.h" 16 #include "CodecBenchPriv.h" 17 #include "CrashHandler.h" 18 #include "GMBench.h" 19 #include "ProcStats.h" 20 #include "ResultsWriter.h" 21 #include "RecordingBench.h" 22 #include "SKPAnimationBench.h" 23 #include "SKPBench.h" 24 #include "Stats.h" 25 26 #include "SkAndroidCodec.h" 27 #include "SkBitmapRegionDecoder.h" 28 #include "SkBBoxHierarchy.h" 29 #include "SkCanvas.h" 30 #include "SkCodec.h" 31 #include "SkCommonFlags.h" 32 #include "SkCommonFlagsConfig.h" 33 #include "SkData.h" 34 #include "SkForceLinking.h" 35 #include "SkGraphics.h" 36 #include "SkOSFile.h" 37 #include "SkPictureRecorder.h" 38 #include "SkPictureUtils.h" 39 #include "SkString.h" 40 #include "SkSurface.h" 41 #include "SkTaskGroup.h" 42 #include "SkThreadUtils.h" 43 #include "ThermalManager.h" 44 45 #include <stdlib.h> 46 47 #ifndef SK_BUILD_FOR_WIN32 48 #include <unistd.h> 49 #endif 50 51 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 52 #include "nanobenchAndroid.h" 53 #endif 54 55 #if SK_SUPPORT_GPU 56 #include "gl/GrGLDefines.h" 57 #include "GrCaps.h" 58 #include "GrContextFactory.h" 59 SkAutoTDelete<GrContextFactory> gGrFactory; 60 #endif 61 62 struct GrContextOptions; 63 64 __SK_FORCE_IMAGE_DECODER_LINKING; 65 66 static const int kAutoTuneLoops = 0; 67 68 static const int kDefaultLoops = 69 #ifdef SK_DEBUG 70 1; 71 #else 72 kAutoTuneLoops; 73 #endif 74 75 static SkString loops_help_txt() { 76 SkString help; 77 help.printf("Number of times to run each bench. Set this to %d to auto-" 78 "tune for each bench. Timings are only reported when auto-tuning.", 79 kAutoTuneLoops); 80 return help; 81 } 82 83 static SkString to_string(int n) { 84 SkString str; 85 str.appendS32(n); 86 return str; 87 } 88 89 DEFINE_int32(loops, kDefaultLoops, loops_help_txt().c_str()); 90 91 DEFINE_int32(samples, 10, "Number of samples to measure for each bench."); 92 DEFINE_int32(ms, 0, "If >0, run each bench for this many ms instead of obeying --samples."); 93 DEFINE_int32(overheadLoops, 100000, "Loops to estimate timer overhead."); 94 DEFINE_double(overheadGoal, 0.0001, 95 "Loop until timer overhead is at most this fraction of our measurments."); 96 DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU."); 97 DEFINE_int32(gpuFrameLag, 5, "If unknown, estimated maximum number of frames GPU allows to lag."); 98 DEFINE_bool(gpuCompressAlphaMasks, false, "Compress masks generated from falling back to " 99 "software path rendering."); 100 101 DEFINE_string(outResultsFile, "", "If given, write results here as JSON."); 102 DEFINE_int32(maxCalibrationAttempts, 3, 103 "Try up to this many times to guess loops for a bench, or skip the bench."); 104 DEFINE_int32(maxLoops, 1000000, "Never run a bench more times than this."); 105 DEFINE_string(clip, "0,0,1000,1000", "Clip for SKPs."); 106 DEFINE_string(scales, "1.0", "Space-separated scales for SKPs."); 107 DEFINE_string(zoom, "1.0,0", "Comma-separated zoomMax,zoomPeriodMs factors for a periodic SKP zoom " 108 "function that ping-pongs between 1.0 and zoomMax."); 109 DEFINE_bool(bbh, true, "Build a BBH for SKPs?"); 110 DEFINE_bool(mpd, true, "Use MultiPictureDraw for the SKPs?"); 111 DEFINE_bool(loopSKP, true, "Loop SKPs like we do for micro benches?"); 112 DEFINE_int32(flushEvery, 10, "Flush --outResultsFile every Nth run."); 113 DEFINE_bool(resetGpuContext, true, "Reset the GrContext before running each test."); 114 DEFINE_bool(gpuStats, false, "Print GPU stats after each gpu benchmark?"); 115 DEFINE_bool(gpuStatsDump, false, "Dump GPU states after each benchmark to json"); 116 DEFINE_bool(keepAlive, false, "Print a message every so often so that we don't time out"); 117 DEFINE_string(useThermalManager, "0,1,10,1000", "enabled,threshold,sleepTimeMs,TimeoutMs for " 118 "thermalManager\n"); 119 120 DEFINE_string(sourceType, "", 121 "Apply usual --match rules to source type: bench, gm, skp, image, etc."); 122 DEFINE_string(benchType, "", 123 "Apply usual --match rules to bench type: micro, recording, playback, skcodec, etc."); 124 125 static double now_ms() { return SkTime::GetNSecs() * 1e-6; } 126 127 static SkString humanize(double ms) { 128 if (FLAGS_verbose) return SkStringPrintf("%llu", (uint64_t)(ms*1e6)); 129 return HumanizeMs(ms); 130 } 131 #define HUMANIZE(ms) humanize(ms).c_str() 132 133 bool Target::init(SkImageInfo info, Benchmark* bench) { 134 if (Benchmark::kRaster_Backend == config.backend) { 135 this->surface.reset(SkSurface::NewRaster(info)); 136 if (!this->surface.get()) { 137 return false; 138 } 139 } 140 return true; 141 } 142 bool Target::capturePixels(SkBitmap* bmp) { 143 SkCanvas* canvas = this->getCanvas(); 144 if (!canvas) { 145 return false; 146 } 147 bmp->setInfo(canvas->imageInfo()); 148 if (!canvas->readPixels(bmp, 0, 0)) { 149 SkDebugf("Can't read canvas pixels.\n"); 150 return false; 151 } 152 return true; 153 } 154 155 #if SK_SUPPORT_GPU 156 struct GPUTarget : public Target { 157 explicit GPUTarget(const Config& c) : Target(c), gl(nullptr) { } 158 SkGLContext* gl; 159 160 void setup() override { 161 this->gl->makeCurrent(); 162 // Make sure we're done with whatever came before. 163 SK_GL(*this->gl, Finish()); 164 } 165 void endTiming() override { 166 if (this->gl) { 167 SK_GL(*this->gl, Flush()); 168 this->gl->waitOnSyncOrSwap(); 169 } 170 } 171 void fence() override { 172 SK_GL(*this->gl, Finish()); 173 } 174 175 bool needsFrameTiming(int* maxFrameLag) const override { 176 if (!this->gl->getMaxGpuFrameLag(maxFrameLag)) { 177 // Frame lag is unknown. 178 *maxFrameLag = FLAGS_gpuFrameLag; 179 } 180 return true; 181 } 182 bool init(SkImageInfo info, Benchmark* bench) override { 183 uint32_t flags = this->config.useDFText ? SkSurfaceProps::kUseDeviceIndependentFonts_Flag : 184 0; 185 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); 186 this->surface.reset(SkSurface::NewRenderTarget(gGrFactory->get(this->config.ctxType, 187 this->config.ctxOptions), 188 SkBudgeted::kNo, info, 189 this->config.samples, &props)); 190 this->gl = gGrFactory->getContextInfo(this->config.ctxType, 191 this->config.ctxOptions).fGLContext; 192 if (!this->surface.get()) { 193 return false; 194 } 195 if (!this->gl->fenceSyncSupport()) { 196 SkDebugf("WARNING: GL context for config \"%s\" does not support fence sync. " 197 "Timings might not be accurate.\n", this->config.name.c_str()); 198 } 199 return true; 200 } 201 void fillOptions(ResultsWriter* log) override { 202 const GrGLubyte* version; 203 SK_GL_RET(*this->gl, version, GetString(GR_GL_VERSION)); 204 log->configOption("GL_VERSION", (const char*)(version)); 205 206 SK_GL_RET(*this->gl, version, GetString(GR_GL_RENDERER)); 207 log->configOption("GL_RENDERER", (const char*) version); 208 209 SK_GL_RET(*this->gl, version, GetString(GR_GL_VENDOR)); 210 log->configOption("GL_VENDOR", (const char*) version); 211 212 SK_GL_RET(*this->gl, version, GetString(GR_GL_SHADING_LANGUAGE_VERSION)); 213 log->configOption("GL_SHADING_LANGUAGE_VERSION", (const char*) version); 214 } 215 }; 216 217 #endif 218 219 static double time(int loops, Benchmark* bench, Target* target) { 220 SkCanvas* canvas = target->getCanvas(); 221 if (canvas) { 222 canvas->clear(SK_ColorWHITE); 223 } 224 bench->preDraw(canvas); 225 double start = now_ms(); 226 canvas = target->beginTiming(canvas); 227 bench->draw(loops, canvas); 228 if (canvas) { 229 canvas->flush(); 230 } 231 target->endTiming(); 232 double elapsed = now_ms() - start; 233 bench->postDraw(canvas); 234 return elapsed; 235 } 236 237 static double estimate_timer_overhead() { 238 double overhead = 0; 239 for (int i = 0; i < FLAGS_overheadLoops; i++) { 240 double start = now_ms(); 241 overhead += now_ms() - start; 242 } 243 return overhead / FLAGS_overheadLoops; 244 } 245 246 static int detect_forever_loops(int loops) { 247 // look for a magic run-forever value 248 if (loops < 0) { 249 loops = SK_MaxS32; 250 } 251 return loops; 252 } 253 254 static int clamp_loops(int loops) { 255 if (loops < 1) { 256 SkDebugf("ERROR: clamping loops from %d to 1. " 257 "There's probably something wrong with the bench.\n", loops); 258 return 1; 259 } 260 if (loops > FLAGS_maxLoops) { 261 SkDebugf("WARNING: clamping loops from %d to FLAGS_maxLoops, %d.\n", loops, FLAGS_maxLoops); 262 return FLAGS_maxLoops; 263 } 264 return loops; 265 } 266 267 static bool write_canvas_png(Target* target, const SkString& filename) { 268 269 if (filename.isEmpty()) { 270 return false; 271 } 272 if (target->getCanvas() && 273 kUnknown_SkColorType == target->getCanvas()->imageInfo().colorType()) { 274 return false; 275 } 276 277 SkBitmap bmp; 278 279 if (!target->capturePixels(&bmp)) { 280 return false; 281 } 282 283 SkString dir = SkOSPath::Dirname(filename.c_str()); 284 if (!sk_mkdir(dir.c_str())) { 285 SkDebugf("Can't make dir %s.\n", dir.c_str()); 286 return false; 287 } 288 SkFILEWStream stream(filename.c_str()); 289 if (!stream.isValid()) { 290 SkDebugf("Can't write %s.\n", filename.c_str()); 291 return false; 292 } 293 if (!SkImageEncoder::EncodeStream(&stream, bmp, SkImageEncoder::kPNG_Type, 100)) { 294 SkDebugf("Can't encode a PNG.\n"); 295 return false; 296 } 297 return true; 298 } 299 300 static int kFailedLoops = -2; 301 static int setup_cpu_bench(const double overhead, Target* target, Benchmark* bench) { 302 // First figure out approximately how many loops of bench it takes to make overhead negligible. 303 double bench_plus_overhead = 0.0; 304 int round = 0; 305 int loops = bench->calculateLoops(FLAGS_loops); 306 if (kAutoTuneLoops == loops) { 307 while (bench_plus_overhead < overhead) { 308 if (round++ == FLAGS_maxCalibrationAttempts) { 309 SkDebugf("WARNING: Can't estimate loops for %s (%s vs. %s); skipping.\n", 310 bench->getUniqueName(), HUMANIZE(bench_plus_overhead), HUMANIZE(overhead)); 311 return kFailedLoops; 312 } 313 bench_plus_overhead = time(1, bench, target); 314 } 315 } 316 317 // Later we'll just start and stop the timer once but loop N times. 318 // We'll pick N to make timer overhead negligible: 319 // 320 // overhead 321 // ------------------------- < FLAGS_overheadGoal 322 // overhead + N * Bench Time 323 // 324 // where bench_plus_overhead overhead + Bench Time. 325 // 326 // Doing some math, we get: 327 // 328 // (overhead / FLAGS_overheadGoal) - overhead 329 // ------------------------------------------ < N 330 // bench_plus_overhead - overhead) 331 // 332 // Luckily, this also works well in practice. :) 333 if (kAutoTuneLoops == loops) { 334 const double numer = overhead / FLAGS_overheadGoal - overhead; 335 const double denom = bench_plus_overhead - overhead; 336 loops = (int)ceil(numer / denom); 337 loops = clamp_loops(loops); 338 } else { 339 loops = detect_forever_loops(loops); 340 } 341 342 return loops; 343 } 344 345 static int setup_gpu_bench(Target* target, Benchmark* bench, int maxGpuFrameLag) { 346 // First, figure out how many loops it'll take to get a frame up to FLAGS_gpuMs. 347 int loops = bench->calculateLoops(FLAGS_loops); 348 if (kAutoTuneLoops == loops) { 349 loops = 1; 350 double elapsed = 0; 351 do { 352 if (1<<30 == loops) { 353 // We're about to wrap. Something's wrong with the bench. 354 loops = 0; 355 break; 356 } 357 loops *= 2; 358 // If the GPU lets frames lag at all, we need to make sure we're timing 359 // _this_ round, not still timing last round. 360 for (int i = 0; i < maxGpuFrameLag; i++) { 361 elapsed = time(loops, bench, target); 362 } 363 } while (elapsed < FLAGS_gpuMs); 364 365 // We've overshot at least a little. Scale back linearly. 366 loops = (int)ceil(loops * FLAGS_gpuMs / elapsed); 367 loops = clamp_loops(loops); 368 369 // Make sure we're not still timing our calibration. 370 target->fence(); 371 } else { 372 loops = detect_forever_loops(loops); 373 } 374 375 // Pretty much the same deal as the calibration: do some warmup to make 376 // sure we're timing steady-state pipelined frames. 377 for (int i = 0; i < maxGpuFrameLag - 1; i++) { 378 time(loops, bench, target); 379 } 380 381 return loops; 382 } 383 384 #if SK_SUPPORT_GPU 385 #define kBogusGLContextType GrContextFactory::kNative_GLContextType 386 #define kBogusGLContextOptions GrContextFactory::kNone_GLContextOptions 387 #else 388 #define kBogusGLContextType 0 389 #define kBogusGLContextOptions 0 390 #endif 391 392 static void create_config(const SkCommandLineConfig* config, SkTArray<Config>* configs) { 393 394 #if SK_SUPPORT_GPU 395 if (const auto* gpuConfig = config->asConfigGpu()) { 396 if (!FLAGS_gpu) 397 return; 398 399 const auto ctxOptions = gpuConfig->getUseNVPR() ? GrContextFactory::kEnableNVPR_GLContextOptions 400 : GrContextFactory::kNone_GLContextOptions; 401 const auto ctxType = gpuConfig->getContextType(); 402 const auto sampleCount = gpuConfig->getSamples(); 403 404 if (const GrContext* ctx = gGrFactory->get(ctxType, ctxOptions)) { 405 const auto maxSampleCount = ctx->caps()->maxSampleCount(); 406 if (sampleCount > ctx->caps()->maxSampleCount()) { 407 SkDebugf("Configuration sample count %d exceeds maximum %d.\n", 408 sampleCount, maxSampleCount); 409 return; 410 } 411 } else { 412 SkDebugf("No context was available matching config type and options.\n"); 413 return; 414 } 415 416 Config target = { 417 config->getTag(), 418 Benchmark::kGPU_Backend, 419 kN32_SkColorType, 420 kPremul_SkAlphaType, 421 sampleCount, 422 ctxType, 423 ctxOptions, 424 false }; 425 426 configs->push_back(target); 427 return; 428 } 429 #endif 430 431 #define CPU_CONFIG(name, backend, color, alpha) \ 432 if (config->getTag().equals(#name)) { \ 433 Config config = { SkString(#name), Benchmark::backend, color, alpha, 0, \ 434 kBogusGLContextType, kBogusGLContextOptions, \ 435 false }; \ 436 configs->push_back(config); \ 437 return; \ 438 } 439 440 if (FLAGS_cpu) { 441 CPU_CONFIG(nonrendering, kNonRendering_Backend, kUnknown_SkColorType, kUnpremul_SkAlphaType) 442 CPU_CONFIG(8888, kRaster_Backend, kN32_SkColorType, kPremul_SkAlphaType) 443 CPU_CONFIG(565, kRaster_Backend, kRGB_565_SkColorType, kOpaque_SkAlphaType) 444 } 445 446 #undef CPU_CONFIG 447 448 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 449 if (config->getTag().equals("hwui")) { 450 Config config = { SkString("hwui"), Benchmark::kHWUI_Backend, kRGBA_8888_SkColorType, 451 kPremul_SkAlphaType, 0, kBogusGLContextType, kBogusGLContextOptions, 452 false }; 453 configs->push_back(config); 454 } 455 #endif 456 } 457 458 // Append all configs that are enabled and supported. 459 void create_configs(SkTArray<Config>* configs) { 460 SkCommandLineConfigArray array; 461 ParseConfigs(FLAGS_config, &array); 462 for (int i = 0; i < array.count(); ++i) { 463 create_config(array[i], configs); 464 } 465 } 466 467 // If bench is enabled for config, returns a Target* for it, otherwise nullptr. 468 static Target* is_enabled(Benchmark* bench, const Config& config) { 469 if (!bench->isSuitableFor(config.backend)) { 470 return nullptr; 471 } 472 473 SkImageInfo info = SkImageInfo::Make(bench->getSize().fX, bench->getSize().fY, 474 config.color, config.alpha); 475 476 Target* target = nullptr; 477 478 switch (config.backend) { 479 #if SK_SUPPORT_GPU 480 case Benchmark::kGPU_Backend: 481 target = new GPUTarget(config); 482 break; 483 #endif 484 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 485 case Benchmark::kHWUI_Backend: 486 target = new HWUITarget(config, bench); 487 break; 488 #endif 489 default: 490 target = new Target(config); 491 break; 492 } 493 494 if (!target->init(info, bench)) { 495 delete target; 496 return nullptr; 497 } 498 return target; 499 } 500 501 static bool valid_brd_bench(SkData* encoded, SkBitmapRegionDecoder::Strategy strategy, 502 SkColorType colorType, uint32_t sampleSize, uint32_t minOutputSize, int* width, 503 int* height) { 504 SkAutoTDelete<SkBitmapRegionDecoder> brd( 505 SkBitmapRegionDecoder::Create(encoded, strategy)); 506 if (nullptr == brd.get()) { 507 // This is indicates that subset decoding is not supported for a particular image format. 508 return false; 509 } 510 511 SkBitmap bitmap; 512 if (!brd->decodeRegion(&bitmap, nullptr, SkIRect::MakeXYWH(0, 0, brd->width(), brd->height()), 513 1, colorType, false)) { 514 return false; 515 } 516 517 if (sampleSize * minOutputSize > (uint32_t) brd->width() || sampleSize * minOutputSize > 518 (uint32_t) brd->height()) { 519 // This indicates that the image is not large enough to decode a 520 // minOutputSize x minOutputSize subset at the given sampleSize. 521 return false; 522 } 523 524 // Set the image width and height. The calling code will use this to choose subsets to decode. 525 *width = brd->width(); 526 *height = brd->height(); 527 return true; 528 } 529 530 static void cleanup_run(Target* target) { 531 delete target; 532 #if SK_SUPPORT_GPU 533 if (FLAGS_abandonGpuContext) { 534 gGrFactory->abandonContexts(); 535 } 536 if (FLAGS_resetGpuContext || FLAGS_abandonGpuContext) { 537 gGrFactory->destroyContexts(); 538 } 539 #endif 540 } 541 542 class BenchmarkStream { 543 public: 544 BenchmarkStream() : fBenches(BenchRegistry::Head()) 545 , fGMs(skiagm::GMRegistry::Head()) 546 , fCurrentRecording(0) 547 , fCurrentScale(0) 548 , fCurrentSKP(0) 549 , fCurrentUseMPD(0) 550 , fCurrentCodec(0) 551 , fCurrentAndroidCodec(0) 552 , fCurrentBRDImage(0) 553 , fCurrentColorType(0) 554 , fCurrentAlphaType(0) 555 , fCurrentSubsetType(0) 556 , fCurrentBRDStrategy(0) 557 , fCurrentSampleSize(0) 558 , fCurrentAnimSKP(0) { 559 for (int i = 0; i < FLAGS_skps.count(); i++) { 560 if (SkStrEndsWith(FLAGS_skps[i], ".skp")) { 561 fSKPs.push_back() = FLAGS_skps[i]; 562 } else { 563 SkOSFile::Iter it(FLAGS_skps[i], ".skp"); 564 SkString path; 565 while (it.next(&path)) { 566 fSKPs.push_back() = SkOSPath::Join(FLAGS_skps[0], path.c_str()); 567 } 568 } 569 } 570 571 if (4 != sscanf(FLAGS_clip[0], "%d,%d,%d,%d", 572 &fClip.fLeft, &fClip.fTop, &fClip.fRight, &fClip.fBottom)) { 573 SkDebugf("Can't parse %s from --clip as an SkIRect.\n", FLAGS_clip[0]); 574 exit(1); 575 } 576 577 for (int i = 0; i < FLAGS_scales.count(); i++) { 578 if (1 != sscanf(FLAGS_scales[i], "%f", &fScales.push_back())) { 579 SkDebugf("Can't parse %s from --scales as an SkScalar.\n", FLAGS_scales[i]); 580 exit(1); 581 } 582 } 583 584 if (2 != sscanf(FLAGS_zoom[0], "%f,%lf", &fZoomMax, &fZoomPeriodMs)) { 585 SkDebugf("Can't parse %s from --zoom as a zoomMax,zoomPeriodMs.\n", FLAGS_zoom[0]); 586 exit(1); 587 } 588 589 if (FLAGS_mpd) { 590 fUseMPDs.push_back() = true; 591 } 592 fUseMPDs.push_back() = false; 593 594 // Prepare the images for decoding 595 if (!CollectImages(&fImages)) { 596 exit(1); 597 } 598 599 // Choose the candidate color types for image decoding 600 const SkColorType colorTypes[] = 601 { kN32_SkColorType, 602 kRGB_565_SkColorType, 603 kAlpha_8_SkColorType, 604 kIndex_8_SkColorType, 605 kGray_8_SkColorType }; 606 fColorTypes.reset(colorTypes, SK_ARRAY_COUNT(colorTypes)); 607 } 608 609 static bool ReadPicture(const char* path, SkAutoTUnref<SkPicture>* pic) { 610 // Not strictly necessary, as it will be checked again later, 611 // but helps to avoid a lot of pointless work if we're going to skip it. 612 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, SkOSPath::Basename(path).c_str())) { 613 return false; 614 } 615 616 SkAutoTDelete<SkStream> stream(SkStream::NewFromFile(path)); 617 if (stream.get() == nullptr) { 618 SkDebugf("Could not read %s.\n", path); 619 return false; 620 } 621 622 pic->reset(SkPicture::CreateFromStream(stream.get())); 623 if (pic->get() == nullptr) { 624 SkDebugf("Could not read %s as an SkPicture.\n", path); 625 return false; 626 } 627 return true; 628 } 629 630 Benchmark* next() { 631 SkAutoTDelete<Benchmark> bench; 632 do { 633 bench.reset(this->rawNext()); 634 if (!bench) { 635 return nullptr; 636 } 637 } while(SkCommandLineFlags::ShouldSkip(FLAGS_sourceType, fSourceType) || 638 SkCommandLineFlags::ShouldSkip(FLAGS_benchType, fBenchType)); 639 return bench.detach(); 640 } 641 642 Benchmark* rawNext() { 643 if (fBenches) { 644 Benchmark* bench = fBenches->factory()(nullptr); 645 fBenches = fBenches->next(); 646 fSourceType = "bench"; 647 fBenchType = "micro"; 648 return bench; 649 } 650 651 while (fGMs) { 652 SkAutoTDelete<skiagm::GM> gm(fGMs->factory()(nullptr)); 653 fGMs = fGMs->next(); 654 if (gm->runAsBench()) { 655 fSourceType = "gm"; 656 fBenchType = "micro"; 657 return new GMBench(gm.detach()); 658 } 659 } 660 661 // First add all .skps as RecordingBenches. 662 while (fCurrentRecording < fSKPs.count()) { 663 const SkString& path = fSKPs[fCurrentRecording++]; 664 SkAutoTUnref<SkPicture> pic; 665 if (!ReadPicture(path.c_str(), &pic)) { 666 continue; 667 } 668 SkString name = SkOSPath::Basename(path.c_str()); 669 fSourceType = "skp"; 670 fBenchType = "recording"; 671 fSKPBytes = static_cast<double>(SkPictureUtils::ApproximateBytesUsed(pic)); 672 fSKPOps = pic->approximateOpCount(); 673 return new RecordingBench(name.c_str(), pic.get(), FLAGS_bbh); 674 } 675 676 // Then once each for each scale as SKPBenches (playback). 677 while (fCurrentScale < fScales.count()) { 678 while (fCurrentSKP < fSKPs.count()) { 679 const SkString& path = fSKPs[fCurrentSKP]; 680 SkAutoTUnref<SkPicture> pic; 681 if (!ReadPicture(path.c_str(), &pic)) { 682 fCurrentSKP++; 683 continue; 684 } 685 686 while (fCurrentUseMPD < fUseMPDs.count()) { 687 if (FLAGS_bbh) { 688 // The SKP we read off disk doesn't have a BBH. Re-record so it grows one. 689 SkRTreeFactory factory; 690 SkPictureRecorder recorder; 691 static const int kFlags = SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag; 692 pic->playback(recorder.beginRecording(pic->cullRect().width(), 693 pic->cullRect().height(), 694 &factory, 695 fUseMPDs[fCurrentUseMPD] ? kFlags : 0)); 696 pic.reset(recorder.endRecording()); 697 } 698 SkString name = SkOSPath::Basename(path.c_str()); 699 fSourceType = "skp"; 700 fBenchType = "playback"; 701 return new SKPBench(name.c_str(), pic.get(), fClip, fScales[fCurrentScale], 702 fUseMPDs[fCurrentUseMPD++], FLAGS_loopSKP); 703 } 704 fCurrentUseMPD = 0; 705 fCurrentSKP++; 706 } 707 fCurrentSKP = 0; 708 fCurrentScale++; 709 } 710 711 // Now loop over each skp again if we have an animation 712 if (fZoomMax != 1.0f && fZoomPeriodMs > 0) { 713 while (fCurrentAnimSKP < fSKPs.count()) { 714 const SkString& path = fSKPs[fCurrentAnimSKP]; 715 SkAutoTUnref<SkPicture> pic; 716 if (!ReadPicture(path.c_str(), &pic)) { 717 fCurrentAnimSKP++; 718 continue; 719 } 720 721 fCurrentAnimSKP++; 722 SkString name = SkOSPath::Basename(path.c_str()); 723 SkAutoTUnref<SKPAnimationBench::Animation> animation( 724 SKPAnimationBench::CreateZoomAnimation(fZoomMax, fZoomPeriodMs)); 725 return new SKPAnimationBench(name.c_str(), pic.get(), fClip, animation, 726 FLAGS_loopSKP); 727 } 728 } 729 730 for (; fCurrentCodec < fImages.count(); fCurrentCodec++) { 731 fSourceType = "image"; 732 fBenchType = "skcodec"; 733 const SkString& path = fImages[fCurrentCodec]; 734 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path.c_str())) { 735 continue; 736 } 737 SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str())); 738 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded)); 739 if (!codec) { 740 // Nothing to time. 741 SkDebugf("Cannot find codec for %s\n", path.c_str()); 742 continue; 743 } 744 745 while (fCurrentColorType < fColorTypes.count()) { 746 const SkColorType colorType = fColorTypes[fCurrentColorType]; 747 748 SkAlphaType alphaType = codec->getInfo().alphaType(); 749 switch (alphaType) { 750 case kOpaque_SkAlphaType: 751 // We only need to test one alpha type (opaque). 752 fCurrentColorType++; 753 break; 754 case kUnpremul_SkAlphaType: 755 case kPremul_SkAlphaType: 756 if (0 == fCurrentAlphaType) { 757 // Test unpremul first. 758 alphaType = kUnpremul_SkAlphaType; 759 fCurrentAlphaType++; 760 } else { 761 // Test premul. 762 alphaType = kPremul_SkAlphaType; 763 fCurrentAlphaType = 0; 764 fCurrentColorType++; 765 } 766 break; 767 default: 768 SkASSERT(false); 769 fCurrentColorType++; 770 break; 771 } 772 773 // Make sure we can decode to this color type and alpha type. 774 SkImageInfo info = 775 codec->getInfo().makeColorType(colorType).makeAlphaType(alphaType); 776 const size_t rowBytes = info.minRowBytes(); 777 SkAutoMalloc storage(info.getSafeSize(rowBytes)); 778 779 // Used if fCurrentColorType is kIndex_8_SkColorType 780 int colorCount = 256; 781 SkPMColor colors[256]; 782 783 const SkCodec::Result result = codec->getPixels( 784 info, storage.get(), rowBytes, nullptr, colors, 785 &colorCount); 786 switch (result) { 787 case SkCodec::kSuccess: 788 case SkCodec::kIncompleteInput: 789 return new CodecBench(SkOSPath::Basename(path.c_str()), 790 encoded, colorType, alphaType); 791 case SkCodec::kInvalidConversion: 792 // This is okay. Not all conversions are valid. 793 break; 794 default: 795 // This represents some sort of failure. 796 SkASSERT(false); 797 break; 798 } 799 } 800 fCurrentColorType = 0; 801 } 802 803 // Run AndroidCodecBenches 804 const int sampleSizes[] = { 2, 4, 8 }; 805 for (; fCurrentAndroidCodec < fImages.count(); fCurrentAndroidCodec++) { 806 fSourceType = "image"; 807 fBenchType = "skandroidcodec"; 808 809 const SkString& path = fImages[fCurrentAndroidCodec]; 810 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path.c_str())) { 811 continue; 812 } 813 SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str())); 814 SkAutoTDelete<SkAndroidCodec> codec(SkAndroidCodec::NewFromData(encoded)); 815 if (!codec) { 816 // Nothing to time. 817 SkDebugf("Cannot find codec for %s\n", path.c_str()); 818 continue; 819 } 820 821 while (fCurrentSampleSize < (int) SK_ARRAY_COUNT(sampleSizes)) { 822 int sampleSize = sampleSizes[fCurrentSampleSize]; 823 fCurrentSampleSize++; 824 if (10 * sampleSize > SkTMin(codec->getInfo().width(), codec->getInfo().height())) { 825 // Avoid benchmarking scaled decodes of already small images. 826 break; 827 } 828 829 return new AndroidCodecBench(SkOSPath::Basename(path.c_str()), encoded, sampleSize); 830 } 831 fCurrentSampleSize = 0; 832 } 833 834 // Run the BRDBenches 835 // We will benchmark multiple BRD strategies. 836 static const struct { 837 SkBitmapRegionDecoder::Strategy fStrategy; 838 const char* fName; 839 } strategies[] = { 840 { SkBitmapRegionDecoder::kCanvas_Strategy, "BRD_canvas" }, 841 { SkBitmapRegionDecoder::kAndroidCodec_Strategy, "BRD_android_codec" }, 842 }; 843 844 // We intend to create benchmarks that model the use cases in 845 // android/libraries/social/tiledimage. In this library, an image is decoded in 512x512 846 // tiles. The image can be translated freely, so the location of a tile may be anywhere in 847 // the image. For that reason, we will benchmark decodes in five representative locations 848 // in the image. Additionally, this use case utilizes power of two scaling, so we will 849 // test on power of two sample sizes. The output tile is always 512x512, so, when a 850 // sampleSize is used, the size of the subset that is decoded is always 851 // (sampleSize*512)x(sampleSize*512). 852 // There are a few good reasons to only test on power of two sample sizes at this time: 853 // All use cases we are aware of only scale by powers of two. 854 // PNG decodes use the indicated sampling strategy regardless of the sample size, so 855 // these tests are sufficient to provide good coverage of our scaling options. 856 const uint32_t brdSampleSizes[] = { 1, 2, 4, 8, 16 }; 857 const uint32_t minOutputSize = 512; 858 for (; fCurrentBRDImage < fImages.count(); fCurrentBRDImage++) { 859 const SkString& path = fImages[fCurrentBRDImage]; 860 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path.c_str())) { 861 continue; 862 } 863 while (fCurrentBRDStrategy < (int) SK_ARRAY_COUNT(strategies)) { 864 fSourceType = "image"; 865 fBenchType = strategies[fCurrentBRDStrategy].fName; 866 867 const SkBitmapRegionDecoder::Strategy strategy = 868 strategies[fCurrentBRDStrategy].fStrategy; 869 870 while (fCurrentColorType < fColorTypes.count()) { 871 while (fCurrentSampleSize < (int) SK_ARRAY_COUNT(brdSampleSizes)) { 872 while (fCurrentSubsetType <= kLastSingle_SubsetType) { 873 874 SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str())); 875 const SkColorType colorType = fColorTypes[fCurrentColorType]; 876 uint32_t sampleSize = brdSampleSizes[fCurrentSampleSize]; 877 int currentSubsetType = fCurrentSubsetType++; 878 879 int width = 0; 880 int height = 0; 881 if (!valid_brd_bench(encoded.get(), strategy, colorType, sampleSize, 882 minOutputSize, &width, &height)) { 883 break; 884 } 885 886 SkString basename = SkOSPath::Basename(path.c_str()); 887 SkIRect subset; 888 const uint32_t subsetSize = sampleSize * minOutputSize; 889 switch (currentSubsetType) { 890 case kTopLeft_SubsetType: 891 basename.append("_TopLeft"); 892 subset = SkIRect::MakeXYWH(0, 0, subsetSize, subsetSize); 893 break; 894 case kTopRight_SubsetType: 895 basename.append("_TopRight"); 896 subset = SkIRect::MakeXYWH(width - subsetSize, 0, subsetSize, 897 subsetSize); 898 break; 899 case kMiddle_SubsetType: 900 basename.append("_Middle"); 901 subset = SkIRect::MakeXYWH((width - subsetSize) / 2, 902 (height - subsetSize) / 2, subsetSize, subsetSize); 903 break; 904 case kBottomLeft_SubsetType: 905 basename.append("_BottomLeft"); 906 subset = SkIRect::MakeXYWH(0, height - subsetSize, subsetSize, 907 subsetSize); 908 break; 909 case kBottomRight_SubsetType: 910 basename.append("_BottomRight"); 911 subset = SkIRect::MakeXYWH(width - subsetSize, 912 height - subsetSize, subsetSize, subsetSize); 913 break; 914 default: 915 SkASSERT(false); 916 } 917 918 return new BitmapRegionDecoderBench(basename.c_str(), encoded.get(), 919 strategy, colorType, sampleSize, subset); 920 } 921 fCurrentSubsetType = 0; 922 fCurrentSampleSize++; 923 } 924 fCurrentSampleSize = 0; 925 fCurrentColorType++; 926 } 927 fCurrentColorType = 0; 928 fCurrentBRDStrategy++; 929 } 930 fCurrentBRDStrategy = 0; 931 } 932 933 return nullptr; 934 } 935 936 void fillCurrentOptions(ResultsWriter* log) const { 937 log->configOption("source_type", fSourceType); 938 log->configOption("bench_type", fBenchType); 939 if (0 == strcmp(fSourceType, "skp")) { 940 log->configOption("clip", 941 SkStringPrintf("%d %d %d %d", fClip.fLeft, fClip.fTop, 942 fClip.fRight, fClip.fBottom).c_str()); 943 SkASSERT_RELEASE(fCurrentScale < fScales.count()); // debugging paranoia 944 log->configOption("scale", SkStringPrintf("%.2g", fScales[fCurrentScale]).c_str()); 945 if (fCurrentUseMPD > 0) { 946 SkASSERT(1 == fCurrentUseMPD || 2 == fCurrentUseMPD); 947 log->configOption("multi_picture_draw", fUseMPDs[fCurrentUseMPD-1] ? "true" : "false"); 948 } 949 } 950 if (0 == strcmp(fBenchType, "recording")) { 951 log->metric("bytes", fSKPBytes); 952 log->metric("ops", fSKPOps); 953 } 954 } 955 956 private: 957 enum SubsetType { 958 kTopLeft_SubsetType = 0, 959 kTopRight_SubsetType = 1, 960 kMiddle_SubsetType = 2, 961 kBottomLeft_SubsetType = 3, 962 kBottomRight_SubsetType = 4, 963 kTranslate_SubsetType = 5, 964 kZoom_SubsetType = 6, 965 kLast_SubsetType = kZoom_SubsetType, 966 kLastSingle_SubsetType = kBottomRight_SubsetType, 967 }; 968 969 const BenchRegistry* fBenches; 970 const skiagm::GMRegistry* fGMs; 971 SkIRect fClip; 972 SkTArray<SkScalar> fScales; 973 SkTArray<SkString> fSKPs; 974 SkTArray<bool> fUseMPDs; 975 SkTArray<SkString> fImages; 976 SkTArray<SkColorType, true> fColorTypes; 977 SkScalar fZoomMax; 978 double fZoomPeriodMs; 979 980 double fSKPBytes, fSKPOps; 981 982 const char* fSourceType; // What we're benching: bench, GM, SKP, ... 983 const char* fBenchType; // How we bench it: micro, recording, playback, ... 984 int fCurrentRecording; 985 int fCurrentScale; 986 int fCurrentSKP; 987 int fCurrentUseMPD; 988 int fCurrentCodec; 989 int fCurrentAndroidCodec; 990 int fCurrentBRDImage; 991 int fCurrentColorType; 992 int fCurrentAlphaType; 993 int fCurrentSubsetType; 994 int fCurrentBRDStrategy; 995 int fCurrentSampleSize; 996 int fCurrentAnimSKP; 997 }; 998 999 // Some runs (mostly, Valgrind) are so slow that the bot framework thinks we've hung. 1000 // This prints something every once in a while so that it knows we're still working. 1001 static void start_keepalive() { 1002 struct Loop { 1003 static void forever(void*) { 1004 for (;;) { 1005 static const int kSec = 1200; 1006 #if defined(SK_BUILD_FOR_WIN) 1007 Sleep(kSec * 1000); 1008 #else 1009 sleep(kSec); 1010 #endif 1011 SkDebugf("\nBenchmarks still running...\n"); 1012 } 1013 } 1014 }; 1015 static SkThread* intentionallyLeaked = new SkThread(Loop::forever); 1016 intentionallyLeaked->start(); 1017 } 1018 1019 int nanobench_main(); 1020 int nanobench_main() { 1021 SetupCrashHandler(); 1022 SkAutoGraphics ag; 1023 SkTaskGroup::Enabler enabled(FLAGS_threads); 1024 1025 #if SK_SUPPORT_GPU 1026 GrContextOptions grContextOpts; 1027 grContextOpts.fDrawPathToCompressedTexture = FLAGS_gpuCompressAlphaMasks; 1028 gGrFactory.reset(new GrContextFactory(grContextOpts)); 1029 #endif 1030 1031 if (FLAGS_veryVerbose) { 1032 FLAGS_verbose = true; 1033 } 1034 1035 if (kAutoTuneLoops != FLAGS_loops) { 1036 FLAGS_samples = 1; 1037 FLAGS_gpuFrameLag = 0; 1038 } 1039 1040 if (!FLAGS_writePath.isEmpty()) { 1041 SkDebugf("Writing files to %s.\n", FLAGS_writePath[0]); 1042 if (!sk_mkdir(FLAGS_writePath[0])) { 1043 SkDebugf("Could not create %s. Files won't be written.\n", FLAGS_writePath[0]); 1044 FLAGS_writePath.set(0, nullptr); 1045 } 1046 } 1047 1048 SkAutoTDelete<ResultsWriter> log(new ResultsWriter); 1049 if (!FLAGS_outResultsFile.isEmpty()) { 1050 #if defined(SK_RELEASE) 1051 log.reset(new NanoJSONResultsWriter(FLAGS_outResultsFile[0])); 1052 #else 1053 SkDebugf("I'm ignoring --outResultsFile because this is a Debug build."); 1054 return 1; 1055 #endif 1056 } 1057 1058 if (1 == FLAGS_properties.count() % 2) { 1059 SkDebugf("ERROR: --properties must be passed with an even number of arguments.\n"); 1060 return 1; 1061 } 1062 for (int i = 1; i < FLAGS_properties.count(); i += 2) { 1063 log->property(FLAGS_properties[i-1], FLAGS_properties[i]); 1064 } 1065 1066 if (1 == FLAGS_key.count() % 2) { 1067 SkDebugf("ERROR: --key must be passed with an even number of arguments.\n"); 1068 return 1; 1069 } 1070 for (int i = 1; i < FLAGS_key.count(); i += 2) { 1071 log->key(FLAGS_key[i-1], FLAGS_key[i]); 1072 } 1073 1074 const double overhead = estimate_timer_overhead(); 1075 SkDebugf("Timer overhead: %s\n", HUMANIZE(overhead)); 1076 1077 SkTArray<double> samples; 1078 1079 if (kAutoTuneLoops != FLAGS_loops) { 1080 SkDebugf("Fixed number of loops; times would only be misleading so we won't print them.\n"); 1081 } else if (FLAGS_quiet) { 1082 SkDebugf("! -> high variance, ? -> moderate variance\n"); 1083 SkDebugf(" micros \tbench\n"); 1084 } else if (FLAGS_ms) { 1085 SkDebugf("curr/maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\tsamples\tconfig\tbench\n"); 1086 } else { 1087 SkDebugf("curr/maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tconfig\tbench\n", 1088 FLAGS_samples, "samples"); 1089 } 1090 1091 SkTArray<Config> configs; 1092 create_configs(&configs); 1093 1094 #ifdef THERMAL_MANAGER_SUPPORTED 1095 int tmEnabled, tmThreshold, tmSleepTimeMs, tmTimeoutMs; 1096 if (4 != sscanf(FLAGS_useThermalManager[0], "%d,%d,%d,%d", 1097 &tmEnabled, &tmThreshold, &tmSleepTimeMs, &tmTimeoutMs)) { 1098 SkDebugf("Can't parse %s from --useThermalManager.\n", FLAGS_useThermalManager[0]); 1099 exit(1); 1100 } 1101 ThermalManager tm(tmThreshold, tmSleepTimeMs, tmTimeoutMs); 1102 #endif 1103 1104 if (FLAGS_keepAlive) { 1105 start_keepalive(); 1106 } 1107 1108 int runs = 0; 1109 BenchmarkStream benchStream; 1110 while (Benchmark* b = benchStream.next()) { 1111 SkAutoTDelete<Benchmark> bench(b); 1112 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getUniqueName())) { 1113 continue; 1114 } 1115 1116 if (!configs.empty()) { 1117 log->bench(bench->getUniqueName(), bench->getSize().fX, bench->getSize().fY); 1118 bench->delayedSetup(); 1119 } 1120 for (int i = 0; i < configs.count(); ++i) { 1121 #ifdef THERMAL_MANAGER_SUPPORTED 1122 if (tmEnabled && !tm.coolOffIfNecessary()) { 1123 SkDebugf("Could not cool off, timings will be throttled\n"); 1124 } 1125 #endif 1126 Target* target = is_enabled(b, configs[i]); 1127 if (!target) { 1128 continue; 1129 } 1130 1131 // During HWUI output this canvas may be nullptr. 1132 SkCanvas* canvas = target->getCanvas(); 1133 const char* config = target->config.name.c_str(); 1134 1135 if (FLAGS_pre_log || FLAGS_dryRun) { 1136 SkDebugf("Running %s\t%s\n" 1137 , bench->getUniqueName() 1138 , config); 1139 if (FLAGS_dryRun) { 1140 continue; 1141 } 1142 } 1143 1144 target->setup(); 1145 bench->perCanvasPreDraw(canvas); 1146 1147 int maxFrameLag; 1148 int loops = target->needsFrameTiming(&maxFrameLag) 1149 ? setup_gpu_bench(target, bench.get(), maxFrameLag) 1150 : setup_cpu_bench(overhead, target, bench.get()); 1151 1152 if (FLAGS_ms) { 1153 samples.reset(); 1154 auto stop = now_ms() + FLAGS_ms; 1155 do { 1156 samples.push_back(time(loops, bench, target) / loops); 1157 } while (now_ms() < stop); 1158 } else { 1159 samples.reset(FLAGS_samples); 1160 for (int s = 0; s < FLAGS_samples; s++) { 1161 samples[s] = time(loops, bench, target) / loops; 1162 } 1163 } 1164 1165 #if SK_SUPPORT_GPU 1166 SkTArray<SkString> keys; 1167 SkTArray<double> values; 1168 bool gpuStatsDump = FLAGS_gpuStatsDump && Benchmark::kGPU_Backend == configs[i].backend; 1169 if (gpuStatsDump) { 1170 // TODO cache stats 1171 bench->getGpuStats(canvas, &keys, &values); 1172 } 1173 #endif 1174 1175 bench->perCanvasPostDraw(canvas); 1176 1177 if (Benchmark::kNonRendering_Backend != target->config.backend && 1178 !FLAGS_writePath.isEmpty() && FLAGS_writePath[0]) { 1179 SkString pngFilename = SkOSPath::Join(FLAGS_writePath[0], config); 1180 pngFilename = SkOSPath::Join(pngFilename.c_str(), bench->getUniqueName()); 1181 pngFilename.append(".png"); 1182 write_canvas_png(target, pngFilename); 1183 } 1184 1185 if (kFailedLoops == loops) { 1186 // Can't be timed. A warning note has already been printed. 1187 cleanup_run(target); 1188 continue; 1189 } 1190 1191 Stats stats(samples); 1192 log->config(config); 1193 log->configOption("name", bench->getName()); 1194 benchStream.fillCurrentOptions(log.get()); 1195 target->fillOptions(log.get()); 1196 log->metric("min_ms", stats.min); 1197 log->metric("median_ms", stats.median); 1198 #if SK_SUPPORT_GPU 1199 if (gpuStatsDump) { 1200 // dump to json, only SKPBench currently returns valid keys / values 1201 SkASSERT(keys.count() == values.count()); 1202 for (int i = 0; i < keys.count(); i++) { 1203 log->metric(keys[i].c_str(), values[i]); 1204 } 1205 } 1206 #endif 1207 1208 if (runs++ % FLAGS_flushEvery == 0) { 1209 log->flush(); 1210 } 1211 1212 if (kAutoTuneLoops != FLAGS_loops) { 1213 if (configs.count() == 1) { 1214 config = ""; // Only print the config if we run the same bench on more than one. 1215 } 1216 SkDebugf("%4d/%-4dMB\t%s\t%s\n" 1217 , sk_tools::getCurrResidentSetSizeMB() 1218 , sk_tools::getMaxResidentSetSizeMB() 1219 , bench->getUniqueName() 1220 , config); 1221 } else if (FLAGS_quiet) { 1222 const char* mark = " "; 1223 const double stddev_percent = 100 * sqrt(stats.var) / stats.mean; 1224 if (stddev_percent > 5) mark = "?"; 1225 if (stddev_percent > 10) mark = "!"; 1226 1227 SkDebugf("%10.2f %s\t%s\t%s\n", 1228 stats.median*1e3, mark, bench->getUniqueName(), config); 1229 } else { 1230 const double stddev_percent = 100 * sqrt(stats.var) / stats.mean; 1231 SkDebugf("%4d/%-4dMB\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n" 1232 , sk_tools::getCurrResidentSetSizeMB() 1233 , sk_tools::getMaxResidentSetSizeMB() 1234 , loops 1235 , HUMANIZE(stats.min) 1236 , HUMANIZE(stats.median) 1237 , HUMANIZE(stats.mean) 1238 , HUMANIZE(stats.max) 1239 , stddev_percent 1240 , FLAGS_ms ? to_string(samples.count()).c_str() : stats.plot.c_str() 1241 , config 1242 , bench->getUniqueName() 1243 ); 1244 } 1245 1246 #if SK_SUPPORT_GPU 1247 if (FLAGS_gpuStats && Benchmark::kGPU_Backend == configs[i].backend) { 1248 GrContext* context = gGrFactory->get(configs[i].ctxType, 1249 configs[i].ctxOptions); 1250 context->printCacheStats(); 1251 context->printGpuStats(); 1252 } 1253 #endif 1254 1255 if (FLAGS_verbose) { 1256 SkDebugf("Samples: "); 1257 for (int i = 0; i < samples.count(); i++) { 1258 SkDebugf("%s ", HUMANIZE(samples[i])); 1259 } 1260 SkDebugf("%s\n", bench->getUniqueName()); 1261 } 1262 cleanup_run(target); 1263 } 1264 } 1265 1266 log->bench("memory_usage", 0,0); 1267 log->config("meta"); 1268 log->metric("max_rss_mb", sk_tools::getMaxResidentSetSizeMB()); 1269 1270 #if SK_SUPPORT_GPU 1271 // Make sure we clean up the global GrContextFactory here, otherwise we might race with the 1272 // SkEventTracer destructor 1273 gGrFactory.reset(nullptr); 1274 #endif 1275 1276 return 0; 1277 } 1278 1279 #if !defined SK_BUILD_FOR_IOS 1280 int main(int argc, char** argv) { 1281 SkCommandLineFlags::Parse(argc, argv); 1282 return nanobench_main(); 1283 } 1284 #endif 1285