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