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 = ctx->caps()->getSampleCount(sampleCount, grPixConfig); 432 if (sampleCount != supportedSampleCount) { 433 SkDebugf("Configuration '%s' sample count %d is not a supported sample count.\n", 434 config->getTag().c_str(), sampleCount); 435 return; 436 } 437 } else { 438 SkDebugf("No context was available matching config '%s'.\n", 439 config->getTag().c_str()); 440 return; 441 } 442 443 Config target = { 444 gpuConfig->getTag(), 445 Benchmark::kGPU_Backend, 446 colorType, 447 kPremul_SkAlphaType, 448 sk_ref_sp(colorSpace), 449 sampleCount, 450 ctxType, 451 ctxOverrides, 452 gpuConfig->getUseDIText() 453 }; 454 455 configs->push_back(target); 456 return; 457 } 458 #endif 459 460 #define CPU_CONFIG(name, backend, color, alpha, colorSpace) \ 461 if (config->getTag().equals(#name)) { \ 462 if (!FLAGS_cpu) { \ 463 SkDebugf("Skipping config '%s' as requested.\n", \ 464 config->getTag().c_str()); \ 465 return; \ 466 } \ 467 Config config = { \ 468 SkString(#name), Benchmark::backend, color, alpha, colorSpace, \ 469 0, kBogusContextType, kBogusContextOverrides, false \ 470 }; \ 471 configs->push_back(config); \ 472 return; \ 473 } 474 475 CPU_CONFIG(nonrendering, kNonRendering_Backend, 476 kUnknown_SkColorType, kUnpremul_SkAlphaType, nullptr) 477 478 CPU_CONFIG(a8, kRaster_Backend, 479 kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr) 480 CPU_CONFIG(8888, kRaster_Backend, 481 kN32_SkColorType, kPremul_SkAlphaType, nullptr) 482 CPU_CONFIG(565, kRaster_Backend, 483 kRGB_565_SkColorType, kOpaque_SkAlphaType, nullptr) 484 auto srgbColorSpace = SkColorSpace::MakeSRGB(); 485 CPU_CONFIG(srgb, kRaster_Backend, 486 kN32_SkColorType, kPremul_SkAlphaType, srgbColorSpace) 487 auto srgbLinearColorSpace = SkColorSpace::MakeSRGBLinear(); 488 CPU_CONFIG(f16, kRaster_Backend, 489 kRGBA_F16_SkColorType, kPremul_SkAlphaType, srgbLinearColorSpace) 490 491 #undef CPU_CONFIG 492 493 SkDebugf("Unknown config '%s'.\n", config->getTag().c_str()); 494 } 495 496 // Append all configs that are enabled and supported. 497 void create_configs(SkTArray<Config>* configs) { 498 SkCommandLineConfigArray array; 499 ParseConfigs(FLAGS_config, &array); 500 for (int i = 0; i < array.count(); ++i) { 501 create_config(array[i].get(), configs); 502 } 503 504 // If no just default configs were requested, then we're okay. 505 if (array.count() == 0 || FLAGS_config.count() == 0 || 506 // If we've been told to ignore undefined flags, we're okay. 507 FLAGS_undefok || 508 // Otherwise, make sure that all specified configs have been created. 509 array.count() == configs->count()) { 510 return; 511 } 512 SkDebugf("Invalid --config. Use --undefok to bypass this warning.\n"); 513 exit(1); 514 } 515 516 // disable warning : switch statement contains default but no 'case' labels 517 #if defined _WIN32 518 #pragma warning ( push ) 519 #pragma warning ( disable : 4065 ) 520 #endif 521 522 // If bench is enabled for config, returns a Target* for it, otherwise nullptr. 523 static Target* is_enabled(Benchmark* bench, const Config& config) { 524 if (!bench->isSuitableFor(config.backend)) { 525 return nullptr; 526 } 527 528 SkImageInfo info = SkImageInfo::Make(bench->getSize().fX, bench->getSize().fY, 529 config.color, config.alpha, config.colorSpace); 530 531 Target* target = nullptr; 532 533 switch (config.backend) { 534 #if SK_SUPPORT_GPU 535 case Benchmark::kGPU_Backend: 536 target = new GPUTarget(config); 537 break; 538 #endif 539 default: 540 target = new Target(config); 541 break; 542 } 543 544 if (!target->init(info, bench)) { 545 delete target; 546 return nullptr; 547 } 548 return target; 549 } 550 551 #if defined _WIN32 552 #pragma warning ( pop ) 553 #endif 554 555 static bool valid_brd_bench(sk_sp<SkData> encoded, SkColorType colorType, uint32_t sampleSize, 556 uint32_t minOutputSize, int* width, int* height) { 557 std::unique_ptr<SkBitmapRegionDecoder> brd( 558 SkBitmapRegionDecoder::Create(encoded, SkBitmapRegionDecoder::kAndroidCodec_Strategy)); 559 if (nullptr == brd.get()) { 560 // This is indicates that subset decoding is not supported for a particular image format. 561 return false; 562 } 563 564 if (sampleSize * minOutputSize > (uint32_t) brd->width() || sampleSize * minOutputSize > 565 (uint32_t) brd->height()) { 566 // This indicates that the image is not large enough to decode a 567 // minOutputSize x minOutputSize subset at the given sampleSize. 568 return false; 569 } 570 571 // Set the image width and height. The calling code will use this to choose subsets to decode. 572 *width = brd->width(); 573 *height = brd->height(); 574 return true; 575 } 576 577 static void cleanup_run(Target* target) { 578 delete target; 579 } 580 581 static void collect_files(const SkCommandLineFlags::StringArray& paths, const char* ext, 582 SkTArray<SkString>* list) { 583 for (int i = 0; i < paths.count(); ++i) { 584 if (SkStrEndsWith(paths[i], ext)) { 585 list->push_back(SkString(paths[i])); 586 } else { 587 SkOSFile::Iter it(paths[i], ext); 588 SkString path; 589 while (it.next(&path)) { 590 list->push_back(SkOSPath::Join(paths[i], path.c_str())); 591 } 592 } 593 } 594 } 595 596 class BenchmarkStream { 597 public: 598 BenchmarkStream() : fBenches(BenchRegistry::Head()) 599 , fGMs(skiagm::GMRegistry::Head()) 600 , fCurrentRecording(0) 601 , fCurrentPiping(0) 602 , fCurrentDeserialPicture(0) 603 , fCurrentScale(0) 604 , fCurrentSKP(0) 605 , fCurrentSVG(0) 606 , fCurrentUseMPD(0) 607 , fCurrentCodec(0) 608 , fCurrentAndroidCodec(0) 609 , fCurrentBRDImage(0) 610 , fCurrentColorImage(0) 611 , fCurrentColorType(0) 612 , fCurrentAlphaType(0) 613 , fCurrentSubsetType(0) 614 , fCurrentSampleSize(0) 615 , fCurrentAnimSKP(0) { 616 collect_files(FLAGS_skps, ".skp", &fSKPs); 617 collect_files(FLAGS_svgs, ".svg", &fSVGs); 618 619 if (4 != sscanf(FLAGS_clip[0], "%d,%d,%d,%d", 620 &fClip.fLeft, &fClip.fTop, &fClip.fRight, &fClip.fBottom)) { 621 SkDebugf("Can't parse %s from --clip as an SkIRect.\n", FLAGS_clip[0]); 622 exit(1); 623 } 624 625 for (int i = 0; i < FLAGS_scales.count(); i++) { 626 if (1 != sscanf(FLAGS_scales[i], "%f", &fScales.push_back())) { 627 SkDebugf("Can't parse %s from --scales as an SkScalar.\n", FLAGS_scales[i]); 628 exit(1); 629 } 630 } 631 632 if (2 != sscanf(FLAGS_zoom[0], "%f,%lf", &fZoomMax, &fZoomPeriodMs)) { 633 SkDebugf("Can't parse %s from --zoom as a zoomMax,zoomPeriodMs.\n", FLAGS_zoom[0]); 634 exit(1); 635 } 636 637 if (FLAGS_mpd) { 638 fUseMPDs.push_back() = true; 639 } 640 fUseMPDs.push_back() = false; 641 642 // Prepare the images for decoding 643 if (!CollectImages(FLAGS_images, &fImages)) { 644 exit(1); 645 } 646 if (!CollectImages(FLAGS_colorImages, &fColorImages)) { 647 exit(1); 648 } 649 650 // Choose the candidate color types for image decoding 651 fColorTypes.push_back(kN32_SkColorType); 652 if (!FLAGS_simpleCodec) { 653 fColorTypes.push_back(kRGB_565_SkColorType); 654 fColorTypes.push_back(kAlpha_8_SkColorType); 655 fColorTypes.push_back(kGray_8_SkColorType); 656 } 657 } 658 659 static sk_sp<SkPicture> ReadPicture(const char* path) { 660 // Not strictly necessary, as it will be checked again later, 661 // but helps to avoid a lot of pointless work if we're going to skip it. 662 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, SkOSPath::Basename(path).c_str())) { 663 return nullptr; 664 } 665 666 std::unique_ptr<SkStream> stream = SkStream::MakeFromFile(path); 667 if (!stream) { 668 SkDebugf("Could not read %s.\n", path); 669 return nullptr; 670 } 671 672 return SkPicture::MakeFromStream(stream.get()); 673 } 674 675 static sk_sp<SkPicture> ReadSVGPicture(const char* path) { 676 SkFILEStream stream(path); 677 if (!stream.isValid()) { 678 SkDebugf("Could not read %s.\n", path); 679 return nullptr; 680 } 681 682 sk_sp<SkSVGDOM> svgDom = SkSVGDOM::MakeFromStream(stream); 683 if (!svgDom) { 684 SkDebugf("Could not parse %s.\n", path); 685 return nullptr; 686 } 687 688 // Use the intrinsic SVG size if available, otherwise fall back to a default value. 689 static const SkSize kDefaultContainerSize = SkSize::Make(128, 128); 690 if (svgDom->containerSize().isEmpty()) { 691 svgDom->setContainerSize(kDefaultContainerSize); 692 } 693 694 SkPictureRecorder recorder; 695 svgDom->render(recorder.beginRecording(svgDom->containerSize().width(), 696 svgDom->containerSize().height())); 697 return recorder.finishRecordingAsPicture(); 698 } 699 700 Benchmark* next() { 701 std::unique_ptr<Benchmark> bench; 702 do { 703 bench.reset(this->rawNext()); 704 if (!bench) { 705 return nullptr; 706 } 707 } while(SkCommandLineFlags::ShouldSkip(FLAGS_sourceType, fSourceType) || 708 SkCommandLineFlags::ShouldSkip(FLAGS_benchType, fBenchType)); 709 return bench.release(); 710 } 711 712 Benchmark* rawNext() { 713 if (fBenches) { 714 Benchmark* bench = fBenches->factory()(nullptr); 715 fBenches = fBenches->next(); 716 fSourceType = "bench"; 717 fBenchType = "micro"; 718 return bench; 719 } 720 721 while (fGMs) { 722 std::unique_ptr<skiagm::GM> gm(fGMs->factory()(nullptr)); 723 fGMs = fGMs->next(); 724 if (gm->runAsBench()) { 725 fSourceType = "gm"; 726 fBenchType = "micro"; 727 return new GMBench(gm.release()); 728 } 729 } 730 731 // First add all .skps as RecordingBenches. 732 while (fCurrentRecording < fSKPs.count()) { 733 const SkString& path = fSKPs[fCurrentRecording++]; 734 sk_sp<SkPicture> pic = ReadPicture(path.c_str()); 735 if (!pic) { 736 continue; 737 } 738 SkString name = SkOSPath::Basename(path.c_str()); 739 fSourceType = "skp"; 740 fBenchType = "recording"; 741 fSKPBytes = static_cast<double>(pic->approximateBytesUsed()); 742 fSKPOps = pic->approximateOpCount(); 743 return new RecordingBench(name.c_str(), pic.get(), FLAGS_bbh, FLAGS_lite); 744 } 745 746 // Add all .skps as PipeBenches. 747 while (fCurrentPiping < fSKPs.count()) { 748 const SkString& path = fSKPs[fCurrentPiping++]; 749 sk_sp<SkPicture> pic = ReadPicture(path.c_str()); 750 if (!pic) { 751 continue; 752 } 753 SkString name = SkOSPath::Basename(path.c_str()); 754 fSourceType = "skp"; 755 fBenchType = "piping"; 756 fSKPBytes = static_cast<double>(pic->approximateBytesUsed()); 757 fSKPOps = pic->approximateOpCount(); 758 return new PipingBench(name.c_str(), pic.get()); 759 } 760 761 // Add all .skps as DeserializePictureBenchs. 762 while (fCurrentDeserialPicture < fSKPs.count()) { 763 const SkString& path = fSKPs[fCurrentDeserialPicture++]; 764 sk_sp<SkData> data = SkData::MakeFromFileName(path.c_str()); 765 if (!data) { 766 continue; 767 } 768 SkString name = SkOSPath::Basename(path.c_str()); 769 fSourceType = "skp"; 770 fBenchType = "deserial"; 771 fSKPBytes = static_cast<double>(data->size()); 772 fSKPOps = 0; 773 return new DeserializePictureBench(name.c_str(), std::move(data)); 774 } 775 776 // Then once each for each scale as SKPBenches (playback). 777 while (fCurrentScale < fScales.count()) { 778 while (fCurrentSKP < fSKPs.count()) { 779 const SkString& path = fSKPs[fCurrentSKP]; 780 sk_sp<SkPicture> pic = ReadPicture(path.c_str()); 781 if (!pic) { 782 fCurrentSKP++; 783 continue; 784 } 785 786 while (fCurrentUseMPD < fUseMPDs.count()) { 787 if (FLAGS_bbh) { 788 // The SKP we read off disk doesn't have a BBH. Re-record so it grows one. 789 SkRTreeFactory factory; 790 SkPictureRecorder recorder; 791 pic->playback(recorder.beginRecording(pic->cullRect().width(), 792 pic->cullRect().height(), 793 &factory, 794 0)); 795 pic = recorder.finishRecordingAsPicture(); 796 } 797 SkString name = SkOSPath::Basename(path.c_str()); 798 fSourceType = "skp"; 799 fBenchType = "playback"; 800 return new SKPBench(name.c_str(), pic.get(), fClip, fScales[fCurrentScale], 801 fUseMPDs[fCurrentUseMPD++], FLAGS_loopSKP); 802 } 803 fCurrentUseMPD = 0; 804 fCurrentSKP++; 805 } 806 807 while (fCurrentSVG++ < fSVGs.count()) { 808 const char* path = fSVGs[fCurrentSVG - 1].c_str(); 809 if (sk_sp<SkPicture> pic = ReadSVGPicture(path)) { 810 fSourceType = "svg"; 811 fBenchType = "playback"; 812 return new SKPBench(SkOSPath::Basename(path).c_str(), pic.get(), fClip, 813 fScales[fCurrentScale], false, FLAGS_loopSKP); 814 } 815 } 816 817 fCurrentSKP = 0; 818 fCurrentSVG = 0; 819 fCurrentScale++; 820 } 821 822 // Now loop over each skp again if we have an animation 823 if (fZoomMax != 1.0f && fZoomPeriodMs > 0) { 824 while (fCurrentAnimSKP < fSKPs.count()) { 825 const SkString& path = fSKPs[fCurrentAnimSKP]; 826 sk_sp<SkPicture> pic = ReadPicture(path.c_str()); 827 if (!pic) { 828 fCurrentAnimSKP++; 829 continue; 830 } 831 832 fCurrentAnimSKP++; 833 SkString name = SkOSPath::Basename(path.c_str()); 834 sk_sp<SKPAnimationBench::Animation> animation( 835 SKPAnimationBench::CreateZoomAnimation(fZoomMax, fZoomPeriodMs)); 836 return new SKPAnimationBench(name.c_str(), pic.get(), fClip, animation.get(), 837 FLAGS_loopSKP); 838 } 839 } 840 841 for (; fCurrentCodec < fImages.count(); fCurrentCodec++) { 842 fSourceType = "image"; 843 fBenchType = "skcodec"; 844 const SkString& path = fImages[fCurrentCodec]; 845 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path.c_str())) { 846 continue; 847 } 848 sk_sp<SkData> encoded(SkData::MakeFromFileName(path.c_str())); 849 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded)); 850 if (!codec) { 851 // Nothing to time. 852 SkDebugf("Cannot find codec for %s\n", path.c_str()); 853 continue; 854 } 855 856 while (fCurrentColorType < fColorTypes.count()) { 857 const SkColorType colorType = fColorTypes[fCurrentColorType]; 858 859 SkAlphaType alphaType = codec->getInfo().alphaType(); 860 if (FLAGS_simpleCodec) { 861 if (kUnpremul_SkAlphaType == alphaType) { 862 alphaType = kPremul_SkAlphaType; 863 } 864 865 fCurrentColorType++; 866 } else { 867 switch (alphaType) { 868 case kOpaque_SkAlphaType: 869 // We only need to test one alpha type (opaque). 870 fCurrentColorType++; 871 break; 872 case kUnpremul_SkAlphaType: 873 case kPremul_SkAlphaType: 874 if (0 == fCurrentAlphaType) { 875 // Test unpremul first. 876 alphaType = kUnpremul_SkAlphaType; 877 fCurrentAlphaType++; 878 } else { 879 // Test premul. 880 alphaType = kPremul_SkAlphaType; 881 fCurrentAlphaType = 0; 882 fCurrentColorType++; 883 } 884 break; 885 default: 886 SkASSERT(false); 887 fCurrentColorType++; 888 break; 889 } 890 } 891 892 // Make sure we can decode to this color type and alpha type. 893 SkImageInfo info = 894 codec->getInfo().makeColorType(colorType).makeAlphaType(alphaType); 895 const size_t rowBytes = info.minRowBytes(); 896 SkAutoMalloc storage(info.computeByteSize(rowBytes)); 897 898 const SkCodec::Result result = codec->getPixels( 899 info, storage.get(), rowBytes); 900 switch (result) { 901 case SkCodec::kSuccess: 902 case SkCodec::kIncompleteInput: 903 return new CodecBench(SkOSPath::Basename(path.c_str()), 904 encoded.get(), colorType, alphaType); 905 case SkCodec::kInvalidConversion: 906 // This is okay. Not all conversions are valid. 907 break; 908 default: 909 // This represents some sort of failure. 910 SkASSERT(false); 911 break; 912 } 913 } 914 fCurrentColorType = 0; 915 } 916 917 // Run AndroidCodecBenches 918 const int sampleSizes[] = { 2, 4, 8 }; 919 for (; fCurrentAndroidCodec < fImages.count(); fCurrentAndroidCodec++) { 920 fSourceType = "image"; 921 fBenchType = "skandroidcodec"; 922 923 const SkString& path = fImages[fCurrentAndroidCodec]; 924 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path.c_str())) { 925 continue; 926 } 927 sk_sp<SkData> encoded(SkData::MakeFromFileName(path.c_str())); 928 std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromData(encoded)); 929 if (!codec) { 930 // Nothing to time. 931 SkDebugf("Cannot find codec for %s\n", path.c_str()); 932 continue; 933 } 934 935 while (fCurrentSampleSize < (int) SK_ARRAY_COUNT(sampleSizes)) { 936 int sampleSize = sampleSizes[fCurrentSampleSize]; 937 fCurrentSampleSize++; 938 if (10 * sampleSize > SkTMin(codec->getInfo().width(), codec->getInfo().height())) { 939 // Avoid benchmarking scaled decodes of already small images. 940 break; 941 } 942 943 return new AndroidCodecBench(SkOSPath::Basename(path.c_str()), 944 encoded.get(), sampleSize); 945 } 946 fCurrentSampleSize = 0; 947 } 948 949 // Run the BRDBenches 950 // We intend to create benchmarks that model the use cases in 951 // android/libraries/social/tiledimage. In this library, an image is decoded in 512x512 952 // tiles. The image can be translated freely, so the location of a tile may be anywhere in 953 // the image. For that reason, we will benchmark decodes in five representative locations 954 // in the image. Additionally, this use case utilizes power of two scaling, so we will 955 // test on power of two sample sizes. The output tile is always 512x512, so, when a 956 // sampleSize is used, the size of the subset that is decoded is always 957 // (sampleSize*512)x(sampleSize*512). 958 // There are a few good reasons to only test on power of two sample sizes at this time: 959 // All use cases we are aware of only scale by powers of two. 960 // PNG decodes use the indicated sampling strategy regardless of the sample size, so 961 // these tests are sufficient to provide good coverage of our scaling options. 962 const uint32_t brdSampleSizes[] = { 1, 2, 4, 8, 16 }; 963 const uint32_t minOutputSize = 512; 964 for (; fCurrentBRDImage < fImages.count(); fCurrentBRDImage++) { 965 fSourceType = "image"; 966 fBenchType = "BRD"; 967 968 const SkString& path = fImages[fCurrentBRDImage]; 969 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path.c_str())) { 970 continue; 971 } 972 973 while (fCurrentColorType < fColorTypes.count()) { 974 while (fCurrentSampleSize < (int) SK_ARRAY_COUNT(brdSampleSizes)) { 975 while (fCurrentSubsetType <= kLastSingle_SubsetType) { 976 977 sk_sp<SkData> encoded(SkData::MakeFromFileName(path.c_str())); 978 const SkColorType colorType = fColorTypes[fCurrentColorType]; 979 uint32_t sampleSize = brdSampleSizes[fCurrentSampleSize]; 980 int currentSubsetType = fCurrentSubsetType++; 981 982 int width = 0; 983 int height = 0; 984 if (!valid_brd_bench(encoded, colorType, sampleSize, minOutputSize, 985 &width, &height)) { 986 break; 987 } 988 989 SkString basename = SkOSPath::Basename(path.c_str()); 990 SkIRect subset; 991 const uint32_t subsetSize = sampleSize * minOutputSize; 992 switch (currentSubsetType) { 993 case kTopLeft_SubsetType: 994 basename.append("_TopLeft"); 995 subset = SkIRect::MakeXYWH(0, 0, subsetSize, subsetSize); 996 break; 997 case kTopRight_SubsetType: 998 basename.append("_TopRight"); 999 subset = SkIRect::MakeXYWH(width - subsetSize, 0, subsetSize, 1000 subsetSize); 1001 break; 1002 case kMiddle_SubsetType: 1003 basename.append("_Middle"); 1004 subset = SkIRect::MakeXYWH((width - subsetSize) / 2, 1005 (height - subsetSize) / 2, subsetSize, subsetSize); 1006 break; 1007 case kBottomLeft_SubsetType: 1008 basename.append("_BottomLeft"); 1009 subset = SkIRect::MakeXYWH(0, height - subsetSize, subsetSize, 1010 subsetSize); 1011 break; 1012 case kBottomRight_SubsetType: 1013 basename.append("_BottomRight"); 1014 subset = SkIRect::MakeXYWH(width - subsetSize, 1015 height - subsetSize, subsetSize, subsetSize); 1016 break; 1017 default: 1018 SkASSERT(false); 1019 } 1020 1021 return new BitmapRegionDecoderBench(basename.c_str(), encoded.get(), 1022 colorType, sampleSize, subset); 1023 } 1024 fCurrentSubsetType = 0; 1025 fCurrentSampleSize++; 1026 } 1027 fCurrentSampleSize = 0; 1028 fCurrentColorType++; 1029 } 1030 fCurrentColorType = 0; 1031 } 1032 1033 while (fCurrentColorImage < fColorImages.count()) { 1034 fSourceType = "colorimage"; 1035 fBenchType = "skcolorcodec"; 1036 const SkString& path = fColorImages[fCurrentColorImage]; 1037 fCurrentColorImage++; 1038 sk_sp<SkData> encoded = SkData::MakeFromFileName(path.c_str()); 1039 if (encoded) { 1040 return new ColorCodecBench(SkOSPath::Basename(path.c_str()).c_str(), 1041 std::move(encoded)); 1042 } else { 1043 SkDebugf("Could not read file %s.\n", path.c_str()); 1044 } 1045 } 1046 1047 return nullptr; 1048 } 1049 1050 void fillCurrentOptions(ResultsWriter* log) const { 1051 log->configOption("source_type", fSourceType); 1052 log->configOption("bench_type", fBenchType); 1053 if (0 == strcmp(fSourceType, "skp")) { 1054 log->configOption("clip", 1055 SkStringPrintf("%d %d %d %d", fClip.fLeft, fClip.fTop, 1056 fClip.fRight, fClip.fBottom).c_str()); 1057 SkASSERT_RELEASE(fCurrentScale < fScales.count()); // debugging paranoia 1058 log->configOption("scale", SkStringPrintf("%.2g", fScales[fCurrentScale]).c_str()); 1059 if (fCurrentUseMPD > 0) { 1060 SkASSERT(1 == fCurrentUseMPD || 2 == fCurrentUseMPD); 1061 log->configOption("multi_picture_draw", fUseMPDs[fCurrentUseMPD-1] ? "true" : "false"); 1062 } 1063 } 1064 if (0 == strcmp(fBenchType, "recording")) { 1065 log->metric("bytes", fSKPBytes); 1066 log->metric("ops", fSKPOps); 1067 } 1068 } 1069 1070 private: 1071 enum SubsetType { 1072 kTopLeft_SubsetType = 0, 1073 kTopRight_SubsetType = 1, 1074 kMiddle_SubsetType = 2, 1075 kBottomLeft_SubsetType = 3, 1076 kBottomRight_SubsetType = 4, 1077 kTranslate_SubsetType = 5, 1078 kZoom_SubsetType = 6, 1079 kLast_SubsetType = kZoom_SubsetType, 1080 kLastSingle_SubsetType = kBottomRight_SubsetType, 1081 }; 1082 1083 const BenchRegistry* fBenches; 1084 const skiagm::GMRegistry* fGMs; 1085 SkIRect fClip; 1086 SkTArray<SkScalar> fScales; 1087 SkTArray<SkString> fSKPs; 1088 SkTArray<SkString> fSVGs; 1089 SkTArray<bool> fUseMPDs; 1090 SkTArray<SkString> fImages; 1091 SkTArray<SkString> fColorImages; 1092 SkTArray<SkColorType, true> fColorTypes; 1093 SkScalar fZoomMax; 1094 double fZoomPeriodMs; 1095 1096 double fSKPBytes, fSKPOps; 1097 1098 const char* fSourceType; // What we're benching: bench, GM, SKP, ... 1099 const char* fBenchType; // How we bench it: micro, recording, playback, ... 1100 int fCurrentRecording; 1101 int fCurrentPiping; 1102 int fCurrentDeserialPicture; 1103 int fCurrentScale; 1104 int fCurrentSKP; 1105 int fCurrentSVG; 1106 int fCurrentUseMPD; 1107 int fCurrentCodec; 1108 int fCurrentAndroidCodec; 1109 int fCurrentBRDImage; 1110 int fCurrentColorImage; 1111 int fCurrentColorType; 1112 int fCurrentAlphaType; 1113 int fCurrentSubsetType; 1114 int fCurrentSampleSize; 1115 int fCurrentAnimSKP; 1116 }; 1117 1118 // Some runs (mostly, Valgrind) are so slow that the bot framework thinks we've hung. 1119 // This prints something every once in a while so that it knows we're still working. 1120 static void start_keepalive() { 1121 static std::thread* intentionallyLeaked = new std::thread([]{ 1122 for (;;) { 1123 static const int kSec = 1200; 1124 #if defined(SK_BUILD_FOR_WIN) 1125 Sleep(kSec * 1000); 1126 #else 1127 sleep(kSec); 1128 #endif 1129 SkDebugf("\nBenchmarks still running...\n"); 1130 } 1131 }); 1132 (void)intentionallyLeaked; 1133 } 1134 1135 int main(int argc, char** argv) { 1136 SkCommandLineFlags::Parse(argc, argv); 1137 1138 initializeEventTracingForTools(); 1139 1140 #if defined(SK_BUILD_FOR_IOS) 1141 cd_Documents(); 1142 #endif 1143 SetupCrashHandler(); 1144 SkAutoGraphics ag; 1145 SkTaskGroup::Enabler enabled(FLAGS_threads); 1146 1147 #if SK_SUPPORT_GPU 1148 SetCtxOptionsFromCommonFlags(&grContextOpts); 1149 #endif 1150 1151 if (FLAGS_veryVerbose) { 1152 FLAGS_verbose = true; 1153 } 1154 1155 if (kAutoTuneLoops != FLAGS_loops) { 1156 FLAGS_samples = 1; 1157 FLAGS_gpuFrameLag = 0; 1158 } 1159 1160 if (!FLAGS_writePath.isEmpty()) { 1161 SkDebugf("Writing files to %s.\n", FLAGS_writePath[0]); 1162 if (!sk_mkdir(FLAGS_writePath[0])) { 1163 SkDebugf("Could not create %s. Files won't be written.\n", FLAGS_writePath[0]); 1164 FLAGS_writePath.set(0, nullptr); 1165 } 1166 } 1167 1168 std::unique_ptr<ResultsWriter> log(new ResultsWriter); 1169 if (!FLAGS_outResultsFile.isEmpty()) { 1170 #if defined(SK_RELEASE) 1171 log.reset(new NanoJSONResultsWriter(FLAGS_outResultsFile[0])); 1172 #else 1173 SkDebugf("I'm ignoring --outResultsFile because this is a Debug build."); 1174 return 1; 1175 #endif 1176 } 1177 1178 if (1 == FLAGS_properties.count() % 2) { 1179 SkDebugf("ERROR: --properties must be passed with an even number of arguments.\n"); 1180 return 1; 1181 } 1182 for (int i = 1; i < FLAGS_properties.count(); i += 2) { 1183 log->property(FLAGS_properties[i-1], FLAGS_properties[i]); 1184 } 1185 1186 if (1 == FLAGS_key.count() % 2) { 1187 SkDebugf("ERROR: --key must be passed with an even number of arguments.\n"); 1188 return 1; 1189 } 1190 for (int i = 1; i < FLAGS_key.count(); i += 2) { 1191 log->key(FLAGS_key[i-1], FLAGS_key[i]); 1192 } 1193 1194 const double overhead = estimate_timer_overhead(); 1195 SkDebugf("Timer overhead: %s\n", HUMANIZE(overhead)); 1196 1197 SkTArray<double> samples; 1198 1199 if (kAutoTuneLoops != FLAGS_loops) { 1200 SkDebugf("Fixed number of loops; times would only be misleading so we won't print them.\n"); 1201 } else if (FLAGS_quiet) { 1202 SkDebugf("! -> high variance, ? -> moderate variance\n"); 1203 SkDebugf(" micros \tbench\n"); 1204 } else if (FLAGS_ms) { 1205 SkDebugf("curr/maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\tsamples\tconfig\tbench\n"); 1206 } else { 1207 SkDebugf("curr/maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tconfig\tbench\n", 1208 FLAGS_samples, "samples"); 1209 } 1210 1211 SkTArray<Config> configs; 1212 create_configs(&configs); 1213 1214 if (FLAGS_keepAlive) { 1215 start_keepalive(); 1216 } 1217 1218 gSkUseAnalyticAA = FLAGS_analyticAA; 1219 gSkUseDeltaAA = FLAGS_deltaAA; 1220 1221 if (FLAGS_forceDeltaAA) { 1222 gSkForceDeltaAA = true; 1223 } 1224 if (FLAGS_forceAnalyticAA) { 1225 gSkForceAnalyticAA = true; 1226 } 1227 if (FLAGS_forceRasterPipeline) { 1228 gSkForceRasterPipelineBlitter = true; 1229 } 1230 1231 int runs = 0; 1232 BenchmarkStream benchStream; 1233 while (Benchmark* b = benchStream.next()) { 1234 std::unique_ptr<Benchmark> bench(b); 1235 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getUniqueName())) { 1236 continue; 1237 } 1238 1239 if (!configs.empty()) { 1240 log->bench(bench->getUniqueName(), bench->getSize().fX, bench->getSize().fY); 1241 bench->delayedSetup(); 1242 } 1243 for (int i = 0; i < configs.count(); ++i) { 1244 Target* target = is_enabled(b, configs[i]); 1245 if (!target) { 1246 continue; 1247 } 1248 1249 // During HWUI output this canvas may be nullptr. 1250 SkCanvas* canvas = target->getCanvas(); 1251 const char* config = target->config.name.c_str(); 1252 1253 if (FLAGS_pre_log || FLAGS_dryRun) { 1254 SkDebugf("Running %s\t%s\n" 1255 , bench->getUniqueName() 1256 , config); 1257 if (FLAGS_dryRun) { 1258 continue; 1259 } 1260 } 1261 1262 TRACE_EVENT2("skia", "Benchmark", "name", TRACE_STR_COPY(bench->getUniqueName()), 1263 "config", TRACE_STR_COPY(config)); 1264 1265 target->setup(); 1266 bench->perCanvasPreDraw(canvas); 1267 1268 int maxFrameLag; 1269 int loops = target->needsFrameTiming(&maxFrameLag) 1270 ? setup_gpu_bench(target, bench.get(), maxFrameLag) 1271 : setup_cpu_bench(overhead, target, bench.get()); 1272 1273 if (kFailedLoops == loops) { 1274 // Can't be timed. A warning note has already been printed. 1275 cleanup_run(target); 1276 continue; 1277 } 1278 1279 if (runs == 0 && FLAGS_ms < 1000) { 1280 // Run the first bench for 1000ms to warm up the nanobench if FLAGS_ms < 1000. 1281 // Otherwise, the first few benches' measurements will be inaccurate. 1282 auto stop = now_ms() + 1000; 1283 do { 1284 time(loops, bench.get(), target); 1285 } while (now_ms() < stop); 1286 } 1287 1288 if (FLAGS_ms) { 1289 samples.reset(); 1290 auto stop = now_ms() + FLAGS_ms; 1291 do { 1292 samples.push_back(time(loops, bench.get(), target) / loops); 1293 } while (now_ms() < stop); 1294 } else { 1295 samples.reset(FLAGS_samples); 1296 for (int s = 0; s < FLAGS_samples; s++) { 1297 samples[s] = time(loops, bench.get(), target) / loops; 1298 } 1299 } 1300 1301 #if SK_SUPPORT_GPU 1302 SkTArray<SkString> keys; 1303 SkTArray<double> values; 1304 bool gpuStatsDump = FLAGS_gpuStatsDump && Benchmark::kGPU_Backend == configs[i].backend; 1305 if (gpuStatsDump) { 1306 // TODO cache stats 1307 bench->getGpuStats(canvas, &keys, &values); 1308 } 1309 #endif 1310 1311 bench->perCanvasPostDraw(canvas); 1312 1313 if (Benchmark::kNonRendering_Backend != target->config.backend && 1314 !FLAGS_writePath.isEmpty() && FLAGS_writePath[0]) { 1315 SkString pngFilename = SkOSPath::Join(FLAGS_writePath[0], config); 1316 pngFilename = SkOSPath::Join(pngFilename.c_str(), bench->getUniqueName()); 1317 pngFilename.append(".png"); 1318 write_canvas_png(target, pngFilename); 1319 } 1320 1321 Stats stats(samples); 1322 log->config(config); 1323 log->configOption("name", bench->getName()); 1324 benchStream.fillCurrentOptions(log.get()); 1325 target->fillOptions(log.get()); 1326 log->metric("min_ms", stats.min); 1327 log->metrics("samples", samples); 1328 #if SK_SUPPORT_GPU 1329 if (gpuStatsDump) { 1330 // dump to json, only SKPBench currently returns valid keys / values 1331 SkASSERT(keys.count() == values.count()); 1332 for (int i = 0; i < keys.count(); i++) { 1333 log->metric(keys[i].c_str(), values[i]); 1334 } 1335 } 1336 #endif 1337 1338 if (runs++ % FLAGS_flushEvery == 0) { 1339 log->flush(); 1340 } 1341 1342 if (kAutoTuneLoops != FLAGS_loops) { 1343 if (configs.count() == 1) { 1344 config = ""; // Only print the config if we run the same bench on more than one. 1345 } 1346 SkDebugf("%4d/%-4dMB\t%s\t%s\n" 1347 , sk_tools::getCurrResidentSetSizeMB() 1348 , sk_tools::getMaxResidentSetSizeMB() 1349 , bench->getUniqueName() 1350 , config); 1351 } else if (FLAGS_quiet) { 1352 const char* mark = " "; 1353 const double stddev_percent = 100 * sqrt(stats.var) / stats.mean; 1354 if (stddev_percent > 5) mark = "?"; 1355 if (stddev_percent > 10) mark = "!"; 1356 1357 SkDebugf("%10.2f %s\t%s\t%s\n", 1358 stats.median*1e3, mark, bench->getUniqueName(), config); 1359 } else if (FLAGS_csv) { 1360 const double stddev_percent = 100 * sqrt(stats.var) / stats.mean; 1361 SkDebugf("%g,%g,%g,%g,%g,%s,%s\n" 1362 , stats.min 1363 , stats.median 1364 , stats.mean 1365 , stats.max 1366 , stddev_percent 1367 , config 1368 , bench->getUniqueName() 1369 ); 1370 } else { 1371 const char* format = "%4d/%-4dMB\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n"; 1372 const double stddev_percent = 100 * sqrt(stats.var) / stats.mean; 1373 SkDebugf(format 1374 , sk_tools::getCurrResidentSetSizeMB() 1375 , sk_tools::getMaxResidentSetSizeMB() 1376 , loops 1377 , HUMANIZE(stats.min) 1378 , HUMANIZE(stats.median) 1379 , HUMANIZE(stats.mean) 1380 , HUMANIZE(stats.max) 1381 , stddev_percent 1382 , FLAGS_ms ? to_string(samples.count()).c_str() : stats.plot.c_str() 1383 , config 1384 , bench->getUniqueName() 1385 ); 1386 } 1387 1388 #if SK_SUPPORT_GPU 1389 if (FLAGS_gpuStats && Benchmark::kGPU_Backend == configs[i].backend) { 1390 target->dumpStats(); 1391 } 1392 #endif 1393 1394 if (FLAGS_verbose) { 1395 SkDebugf("Samples: "); 1396 for (int i = 0; i < samples.count(); i++) { 1397 SkDebugf("%s ", HUMANIZE(samples[i])); 1398 } 1399 SkDebugf("%s\n", bench->getUniqueName()); 1400 } 1401 cleanup_run(target); 1402 } 1403 } 1404 1405 SkGraphics::PurgeAllCaches(); 1406 1407 log->bench("memory_usage", 0,0); 1408 log->config("meta"); 1409 log->metric("max_rss_mb", sk_tools::getMaxResidentSetSizeMB()); 1410 1411 return 0; 1412 } 1413