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