nanobench.cpp revision 60869a42a133942f852dd0f1696444c2a5c9ad83
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 "Benchmark.h" 13#include "CodecBench.h" 14#include "CrashHandler.h" 15#include "DecodingBench.h" 16#include "DecodingSubsetBench.h" 17#include "GMBench.h" 18#include "ProcStats.h" 19#include "ResultsWriter.h" 20#include "RecordingBench.h" 21#include "SKPBench.h" 22#include "Stats.h" 23#include "Timer.h" 24 25#include "SkBBoxHierarchy.h" 26#include "SkCanvas.h" 27#include "SkCodec.h" 28#include "SkCommonFlags.h" 29#include "SkData.h" 30#include "SkForceLinking.h" 31#include "SkGraphics.h" 32#include "SkOSFile.h" 33#include "SkPictureRecorder.h" 34#include "SkPictureUtils.h" 35#include "SkString.h" 36#include "SkSurface.h" 37#include "SkTaskGroup.h" 38 39#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 40 #include "nanobenchAndroid.h" 41#endif 42 43#if SK_SUPPORT_GPU 44 #include "gl/GrGLDefines.h" 45 #include "GrContextFactory.h" 46 SkAutoTDelete<GrContextFactory> gGrFactory; 47#endif 48 49__SK_FORCE_IMAGE_DECODER_LINKING; 50 51static const int kAutoTuneLoops = 0; 52 53static const int kDefaultLoops = 54#ifdef SK_DEBUG 55 1; 56#else 57 kAutoTuneLoops; 58#endif 59 60static SkString loops_help_txt() { 61 SkString help; 62 help.printf("Number of times to run each bench. Set this to %d to auto-" 63 "tune for each bench. Timings are only reported when auto-tuning.", 64 kAutoTuneLoops); 65 return help; 66} 67 68DEFINE_int32(loops, kDefaultLoops, loops_help_txt().c_str()); 69 70DEFINE_int32(samples, 10, "Number of samples to measure for each bench."); 71DEFINE_int32(overheadLoops, 100000, "Loops to estimate timer overhead."); 72DEFINE_double(overheadGoal, 0.0001, 73 "Loop until timer overhead is at most this fraction of our measurments."); 74DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU."); 75DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allows to lag."); 76DEFINE_bool(gpuCompressAlphaMasks, false, "Compress masks generated from falling back to " 77 "software path rendering."); 78 79DEFINE_string(outResultsFile, "", "If given, write results here as JSON."); 80DEFINE_int32(maxCalibrationAttempts, 3, 81 "Try up to this many times to guess loops for a bench, or skip the bench."); 82DEFINE_int32(maxLoops, 1000000, "Never run a bench more times than this."); 83DEFINE_string(clip, "0,0,1000,1000", "Clip for SKPs."); 84DEFINE_string(scales, "1.0", "Space-separated scales for SKPs."); 85DEFINE_bool(bbh, true, "Build a BBH for SKPs?"); 86DEFINE_bool(mpd, true, "Use MultiPictureDraw for the SKPs?"); 87DEFINE_int32(flushEvery, 10, "Flush --outResultsFile every Nth run."); 88DEFINE_bool(resetGpuContext, true, "Reset the GrContext before running each test."); 89DEFINE_bool(gpuStats, false, "Print GPU stats after each gpu benchmark?"); 90 91static SkString humanize(double ms) { 92 if (FLAGS_verbose) return SkStringPrintf("%llu", (uint64_t)(ms*1e6)); 93 return HumanizeMs(ms); 94} 95#define HUMANIZE(ms) humanize(ms).c_str() 96 97bool Target::init(SkImageInfo info, Benchmark* bench) { 98 if (Benchmark::kRaster_Backend == config.backend) { 99 this->surface.reset(SkSurface::NewRaster(info)); 100 if (!this->surface.get()) { 101 return false; 102 } 103 } 104 return true; 105} 106bool Target::capturePixels(SkBitmap* bmp) { 107 SkCanvas* canvas = this->getCanvas(); 108 if (!canvas) { 109 return false; 110 } 111 bmp->setInfo(canvas->imageInfo()); 112 if (!canvas->readPixels(bmp, 0, 0)) { 113 SkDebugf("Can't read canvas pixels.\n"); 114 return false; 115 } 116 return true; 117} 118 119#if SK_SUPPORT_GPU 120struct GPUTarget : public Target { 121 explicit GPUTarget(const Config& c) : Target(c), gl(NULL) { } 122 SkGLContext* gl; 123 124 void setup() override { 125 this->gl->makeCurrent(); 126 // Make sure we're done with whatever came before. 127 SK_GL(*this->gl, Finish()); 128 } 129 void endTiming() override { 130 if (this->gl) { 131 SK_GL(*this->gl, Flush()); 132 this->gl->swapBuffers(); 133 } 134 } 135 void fence() override { 136 SK_GL(*this->gl, Finish()); 137 } 138 139 bool needsFrameTiming() const override { return true; } 140 bool init(SkImageInfo info, Benchmark* bench) override { 141 uint32_t flags = this->config.useDFText ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0; 142 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); 143 this->surface.reset(SkSurface::NewRenderTarget(gGrFactory->get(this->config.ctxType), 144 SkSurface::kNo_Budgeted, info, 145 this->config.samples, &props)); 146 this->gl = gGrFactory->getGLContext(this->config.ctxType); 147 if (!this->surface.get()) { 148 return false; 149 } 150 return true; 151 } 152 void fillOptions(ResultsWriter* log) override { 153 const GrGLubyte* version; 154 SK_GL_RET(*this->gl, version, GetString(GR_GL_VERSION)); 155 log->configOption("GL_VERSION", (const char*)(version)); 156 157 SK_GL_RET(*this->gl, version, GetString(GR_GL_RENDERER)); 158 log->configOption("GL_RENDERER", (const char*) version); 159 160 SK_GL_RET(*this->gl, version, GetString(GR_GL_VENDOR)); 161 log->configOption("GL_VENDOR", (const char*) version); 162 163 SK_GL_RET(*this->gl, version, GetString(GR_GL_SHADING_LANGUAGE_VERSION)); 164 log->configOption("GL_SHADING_LANGUAGE_VERSION", (const char*) version); 165 } 166}; 167 168#endif 169 170static double time(int loops, Benchmark* bench, Target* target) { 171 SkCanvas* canvas = target->getCanvas(); 172 if (canvas) { 173 canvas->clear(SK_ColorWHITE); 174 } 175 WallTimer timer; 176 timer.start(); 177 canvas = target->beginTiming(canvas); 178 bench->draw(loops, canvas); 179 if (canvas) { 180 canvas->flush(); 181 } 182 target->endTiming(); 183 timer.end(); 184 return timer.fWall; 185} 186 187static double estimate_timer_overhead() { 188 double overhead = 0; 189 for (int i = 0; i < FLAGS_overheadLoops; i++) { 190 WallTimer timer; 191 timer.start(); 192 timer.end(); 193 overhead += timer.fWall; 194 } 195 return overhead / FLAGS_overheadLoops; 196} 197 198static int detect_forever_loops(int loops) { 199 // look for a magic run-forever value 200 if (loops < 0) { 201 loops = SK_MaxS32; 202 } 203 return loops; 204} 205 206static int clamp_loops(int loops) { 207 if (loops < 1) { 208 SkDebugf("ERROR: clamping loops from %d to 1. " 209 "There's probably something wrong with the bench.\n", loops); 210 return 1; 211 } 212 if (loops > FLAGS_maxLoops) { 213 SkDebugf("WARNING: clamping loops from %d to FLAGS_maxLoops, %d.\n", loops, FLAGS_maxLoops); 214 return FLAGS_maxLoops; 215 } 216 return loops; 217} 218 219static bool write_canvas_png(Target* target, const SkString& filename) { 220 221 if (filename.isEmpty()) { 222 return false; 223 } 224 if (target->getCanvas() && 225 kUnknown_SkColorType == target->getCanvas()->imageInfo().colorType()) { 226 return false; 227 } 228 229 SkBitmap bmp; 230 231 if (!target->capturePixels(&bmp)) { 232 return false; 233 } 234 235 SkString dir = SkOSPath::Dirname(filename.c_str()); 236 if (!sk_mkdir(dir.c_str())) { 237 SkDebugf("Can't make dir %s.\n", dir.c_str()); 238 return false; 239 } 240 SkFILEWStream stream(filename.c_str()); 241 if (!stream.isValid()) { 242 SkDebugf("Can't write %s.\n", filename.c_str()); 243 return false; 244 } 245 if (!SkImageEncoder::EncodeStream(&stream, bmp, SkImageEncoder::kPNG_Type, 100)) { 246 SkDebugf("Can't encode a PNG.\n"); 247 return false; 248 } 249 return true; 250} 251 252static int kFailedLoops = -2; 253static int cpu_bench(const double overhead, Target* target, Benchmark* bench, double* samples) { 254 // First figure out approximately how many loops of bench it takes to make overhead negligible. 255 double bench_plus_overhead = 0.0; 256 int round = 0; 257 if (kAutoTuneLoops == FLAGS_loops) { 258 while (bench_plus_overhead < overhead) { 259 if (round++ == FLAGS_maxCalibrationAttempts) { 260 SkDebugf("WARNING: Can't estimate loops for %s (%s vs. %s); skipping.\n", 261 bench->getUniqueName(), HUMANIZE(bench_plus_overhead), HUMANIZE(overhead)); 262 return kFailedLoops; 263 } 264 bench_plus_overhead = time(1, bench, target); 265 } 266 } 267 268 // Later we'll just start and stop the timer once but loop N times. 269 // We'll pick N to make timer overhead negligible: 270 // 271 // overhead 272 // ------------------------- < FLAGS_overheadGoal 273 // overhead + N * Bench Time 274 // 275 // where bench_plus_overhead ≈ overhead + Bench Time. 276 // 277 // Doing some math, we get: 278 // 279 // (overhead / FLAGS_overheadGoal) - overhead 280 // ------------------------------------------ < N 281 // bench_plus_overhead - overhead) 282 // 283 // Luckily, this also works well in practice. :) 284 int loops = FLAGS_loops; 285 if (kAutoTuneLoops == loops) { 286 const double numer = overhead / FLAGS_overheadGoal - overhead; 287 const double denom = bench_plus_overhead - overhead; 288 loops = (int)ceil(numer / denom); 289 loops = clamp_loops(loops); 290 } else { 291 loops = detect_forever_loops(loops); 292 } 293 294 for (int i = 0; i < FLAGS_samples; i++) { 295 samples[i] = time(loops, bench, target) / loops; 296 } 297 return loops; 298} 299 300static int gpu_bench(Target* target, 301 Benchmark* bench, 302 double* samples) { 303 // First, figure out how many loops it'll take to get a frame up to FLAGS_gpuMs. 304 int loops = FLAGS_loops; 305 if (kAutoTuneLoops == loops) { 306 loops = 1; 307 double elapsed = 0; 308 do { 309 if (1<<30 == loops) { 310 // We're about to wrap. Something's wrong with the bench. 311 loops = 0; 312 break; 313 } 314 loops *= 2; 315 // If the GPU lets frames lag at all, we need to make sure we're timing 316 // _this_ round, not still timing last round. We force this by looping 317 // more times than any reasonable GPU will allow frames to lag. 318 for (int i = 0; i < FLAGS_gpuFrameLag; i++) { 319 elapsed = time(loops, bench, target); 320 } 321 } while (elapsed < FLAGS_gpuMs); 322 323 // We've overshot at least a little. Scale back linearly. 324 loops = (int)ceil(loops * FLAGS_gpuMs / elapsed); 325 loops = clamp_loops(loops); 326 327 // Make sure we're not still timing our calibration. 328 target->fence(); 329 } else { 330 loops = detect_forever_loops(loops); 331 } 332 333 // Pretty much the same deal as the calibration: do some warmup to make 334 // sure we're timing steady-state pipelined frames. 335 for (int i = 0; i < FLAGS_gpuFrameLag; i++) { 336 time(loops, bench, target); 337 } 338 339 // Now, actually do the timing! 340 for (int i = 0; i < FLAGS_samples; i++) { 341 samples[i] = time(loops, bench, target) / loops; 342 } 343 344 return loops; 345} 346 347static SkString to_lower(const char* str) { 348 SkString lower(str); 349 for (size_t i = 0; i < lower.size(); i++) { 350 lower[i] = tolower(lower[i]); 351 } 352 return lower; 353} 354 355static bool is_cpu_config_allowed(const char* name) { 356 for (int i = 0; i < FLAGS_config.count(); i++) { 357 if (to_lower(FLAGS_config[i]).equals(name)) { 358 return true; 359 } 360 } 361 return false; 362} 363 364#if SK_SUPPORT_GPU 365static bool is_gpu_config_allowed(const char* name, GrContextFactory::GLContextType ctxType, 366 int sampleCnt) { 367 if (!is_cpu_config_allowed(name)) { 368 return false; 369 } 370 if (const GrContext* ctx = gGrFactory->get(ctxType)) { 371 return sampleCnt <= ctx->getMaxSampleCount(); 372 } 373 return false; 374} 375#endif 376 377#if SK_SUPPORT_GPU 378#define kBogusGLContextType GrContextFactory::kNative_GLContextType 379#else 380#define kBogusGLContextType 0 381#endif 382 383// Append all configs that are enabled and supported. 384static void create_configs(SkTDArray<Config>* configs) { 385 #define CPU_CONFIG(name, backend, color, alpha) \ 386 if (is_cpu_config_allowed(#name)) { \ 387 Config config = { #name, Benchmark::backend, color, alpha, 0, \ 388 kBogusGLContextType, false }; \ 389 configs->push(config); \ 390 } 391 392 if (FLAGS_cpu) { 393 CPU_CONFIG(nonrendering, kNonRendering_Backend, kUnknown_SkColorType, kUnpremul_SkAlphaType) 394 CPU_CONFIG(8888, kRaster_Backend, kN32_SkColorType, kPremul_SkAlphaType) 395 CPU_CONFIG(565, kRaster_Backend, kRGB_565_SkColorType, kOpaque_SkAlphaType) 396 } 397 398#if SK_SUPPORT_GPU 399 #define GPU_CONFIG(name, ctxType, samples, useDFText) \ 400 if (is_gpu_config_allowed(#name, GrContextFactory::ctxType, samples)) { \ 401 Config config = { \ 402 #name, \ 403 Benchmark::kGPU_Backend, \ 404 kN32_SkColorType, \ 405 kPremul_SkAlphaType, \ 406 samples, \ 407 GrContextFactory::ctxType, \ 408 useDFText }; \ 409 configs->push(config); \ 410 } 411 412 if (FLAGS_gpu) { 413 GPU_CONFIG(gpu, kNative_GLContextType, 0, false) 414 GPU_CONFIG(msaa4, kNative_GLContextType, 4, false) 415 GPU_CONFIG(msaa16, kNative_GLContextType, 16, false) 416 GPU_CONFIG(nvprmsaa4, kNVPR_GLContextType, 4, false) 417 GPU_CONFIG(nvprmsaa16, kNVPR_GLContextType, 16, false) 418 GPU_CONFIG(gpudft, kNative_GLContextType, 0, true) 419 GPU_CONFIG(debug, kDebug_GLContextType, 0, false) 420 GPU_CONFIG(nullgpu, kNull_GLContextType, 0, false) 421#ifdef SK_ANGLE 422 GPU_CONFIG(angle, kANGLE_GLContextType, 0, false) 423#endif 424 } 425#endif 426 427#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 428 if (is_cpu_config_allowed("hwui")) { 429 Config config = { "hwui", Benchmark::kHWUI_Backend, kRGBA_8888_SkColorType, 430 kPremul_SkAlphaType, 0, kBogusGLContextType, false }; 431 configs->push(config); 432 } 433#endif 434} 435 436// If bench is enabled for config, returns a Target* for it, otherwise NULL. 437static Target* is_enabled(Benchmark* bench, const Config& config) { 438 if (!bench->isSuitableFor(config.backend)) { 439 return NULL; 440 } 441 442 SkImageInfo info = SkImageInfo::Make(bench->getSize().fX, bench->getSize().fY, 443 config.color, config.alpha); 444 445 Target* target = NULL; 446 447 switch (config.backend) { 448#if SK_SUPPORT_GPU 449 case Benchmark::kGPU_Backend: 450 target = new GPUTarget(config); 451 break; 452#endif 453#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 454 case Benchmark::kHWUI_Backend: 455 target = new HWUITarget(config, bench); 456 break; 457#endif 458 default: 459 target = new Target(config); 460 break; 461 } 462 463 if (!target->init(info, bench)) { 464 delete target; 465 return NULL; 466 } 467 return target; 468} 469 470// Creates targets for a benchmark and a set of configs. 471static void create_targets(SkTDArray<Target*>* targets, Benchmark* b, 472 const SkTDArray<Config>& configs) { 473 for (int i = 0; i < configs.count(); ++i) { 474 if (Target* t = is_enabled(b, configs[i])) { 475 targets->push(t); 476 } 477 478 } 479} 480 481 482class BenchmarkStream { 483public: 484 BenchmarkStream() : fBenches(BenchRegistry::Head()) 485 , fGMs(skiagm::GMRegistry::Head()) 486 , fCurrentRecording(0) 487 , fCurrentScale(0) 488 , fCurrentSKP(0) 489 , fCurrentUseMPD(0) 490 , fCurrentCodec(0) 491 , fCurrentImage(0) 492 , fCurrentSubsetImage(0) 493 , fCurrentColorType(0) 494 , fDivisor(2) { 495 for (int i = 0; i < FLAGS_skps.count(); i++) { 496 if (SkStrEndsWith(FLAGS_skps[i], ".skp")) { 497 fSKPs.push_back() = FLAGS_skps[i]; 498 } else { 499 SkOSFile::Iter it(FLAGS_skps[i], ".skp"); 500 SkString path; 501 while (it.next(&path)) { 502 fSKPs.push_back() = SkOSPath::Join(FLAGS_skps[0], path.c_str()); 503 } 504 } 505 } 506 507 if (4 != sscanf(FLAGS_clip[0], "%d,%d,%d,%d", 508 &fClip.fLeft, &fClip.fTop, &fClip.fRight, &fClip.fBottom)) { 509 SkDebugf("Can't parse %s from --clip as an SkIRect.\n", FLAGS_clip[0]); 510 exit(1); 511 } 512 513 for (int i = 0; i < FLAGS_scales.count(); i++) { 514 if (1 != sscanf(FLAGS_scales[i], "%f", &fScales.push_back())) { 515 SkDebugf("Can't parse %s from --scales as an SkScalar.\n", FLAGS_scales[i]); 516 exit(1); 517 } 518 } 519 520 fUseMPDs.push_back() = false; 521 if (FLAGS_mpd) { 522 fUseMPDs.push_back() = true; 523 } 524 525 // Prepare the images for decoding 526 for (int i = 0; i < FLAGS_images.count(); i++) { 527 const char* flag = FLAGS_images[i]; 528 if (sk_isdir(flag)) { 529 // If the value passed in is a directory, add all the images 530 SkOSFile::Iter it(flag); 531 SkString file; 532 while (it.next(&file)) { 533 fImages.push_back() = SkOSPath::Join(flag, file.c_str()); 534 } 535 } else if (sk_exists(flag)) { 536 // Also add the value if it is a single image 537 fImages.push_back() = flag; 538 } 539 } 540 541 // Choose the candidate color types for image decoding 542 const SkColorType colorTypes[] = 543 { kN32_SkColorType, kRGB_565_SkColorType, kAlpha_8_SkColorType }; 544 fColorTypes.push_back_n(SK_ARRAY_COUNT(colorTypes), colorTypes); 545 } 546 547 static bool ReadPicture(const char* path, SkAutoTUnref<SkPicture>* pic) { 548 // Not strictly necessary, as it will be checked again later, 549 // but helps to avoid a lot of pointless work if we're going to skip it. 550 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path)) { 551 return false; 552 } 553 554 SkAutoTDelete<SkStream> stream(SkStream::NewFromFile(path)); 555 if (stream.get() == NULL) { 556 SkDebugf("Could not read %s.\n", path); 557 return false; 558 } 559 560 pic->reset(SkPicture::CreateFromStream(stream.get())); 561 if (pic->get() == NULL) { 562 SkDebugf("Could not read %s as an SkPicture.\n", path); 563 return false; 564 } 565 return true; 566 } 567 568 Benchmark* next() { 569 if (fBenches) { 570 Benchmark* bench = fBenches->factory()(NULL); 571 fBenches = fBenches->next(); 572 fSourceType = "bench"; 573 fBenchType = "micro"; 574 return bench; 575 } 576 577 while (fGMs) { 578 SkAutoTDelete<skiagm::GM> gm(fGMs->factory()(NULL)); 579 fGMs = fGMs->next(); 580 if (gm->runAsBench()) { 581 fSourceType = "gm"; 582 fBenchType = "micro"; 583 return SkNEW_ARGS(GMBench, (gm.detach())); 584 } 585 } 586 587 // First add all .skps as RecordingBenches. 588 while (fCurrentRecording < fSKPs.count()) { 589 const SkString& path = fSKPs[fCurrentRecording++]; 590 SkAutoTUnref<SkPicture> pic; 591 if (!ReadPicture(path.c_str(), &pic)) { 592 continue; 593 } 594 SkString name = SkOSPath::Basename(path.c_str()); 595 fSourceType = "skp"; 596 fBenchType = "recording"; 597 fSKPBytes = static_cast<double>(SkPictureUtils::ApproximateBytesUsed(pic)); 598 fSKPOps = pic->approximateOpCount(); 599 return SkNEW_ARGS(RecordingBench, (name.c_str(), pic.get(), FLAGS_bbh)); 600 } 601 602 // Then once each for each scale as SKPBenches (playback). 603 while (fCurrentScale < fScales.count()) { 604 while (fCurrentSKP < fSKPs.count()) { 605 const SkString& path = fSKPs[fCurrentSKP]; 606 SkAutoTUnref<SkPicture> pic; 607 if (!ReadPicture(path.c_str(), &pic)) { 608 fCurrentSKP++; 609 continue; 610 } 611 612 while (fCurrentUseMPD < fUseMPDs.count()) { 613 if (FLAGS_bbh) { 614 // The SKP we read off disk doesn't have a BBH. Re-record so it grows one. 615 SkRTreeFactory factory; 616 SkPictureRecorder recorder; 617 static const int kFlags = SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag; 618 pic->playback(recorder.beginRecording(pic->cullRect().width(), 619 pic->cullRect().height(), 620 &factory, 621 fUseMPDs[fCurrentUseMPD] ? kFlags : 0)); 622 pic.reset(recorder.endRecording()); 623 } 624 SkString name = SkOSPath::Basename(path.c_str()); 625 fSourceType = "skp"; 626 fBenchType = "playback"; 627 return SkNEW_ARGS(SKPBench, 628 (name.c_str(), pic.get(), fClip, 629 fScales[fCurrentScale], fUseMPDs[fCurrentUseMPD++])); 630 } 631 fCurrentUseMPD = 0; 632 fCurrentSKP++; 633 } 634 fCurrentSKP = 0; 635 fCurrentScale++; 636 } 637 638 for (; fCurrentCodec < fImages.count(); fCurrentCodec++) { 639 const SkString& path = fImages[fCurrentCodec]; 640 SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str())); 641 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded)); 642 SkASSERT(codec); 643 if (!codec) { 644 // Nothing to time. 645 continue; 646 } 647 while (fCurrentColorType < fColorTypes.count()) { 648 SkColorType colorType = fColorTypes[fCurrentColorType]; 649 fCurrentColorType++; 650 // Make sure we can decode to this color type. 651 SkBitmap bitmap; 652 SkImageInfo info = codec->getInfo().makeColorType(colorType); 653 bitmap.allocPixels(info); 654 const SkImageGenerator::Result result = codec->getPixels( 655 bitmap.info(), bitmap.getPixels(), bitmap.rowBytes()); 656 switch (result) { 657 case SkImageGenerator::kSuccess: 658 case SkImageGenerator::kIncompleteInput: 659 return new CodecBench(SkOSPath::Basename(path.c_str()), 660 encoded, colorType); 661 case SkImageGenerator::kInvalidConversion: 662 // This is okay. Not all conversions are valid. 663 break; 664 case SkImageGenerator::kCouldNotRewind: 665 // FIXME: This is due to a bug in some implementations 666 // of SkCodec. All should support rewinding. 667 break; 668 default: 669 // This represents some sort of failure. 670 SkASSERT(false); 671 break; 672 } 673 } 674 fCurrentColorType = 0; 675 } 676 677 // Run the DecodingBenches 678 while (fCurrentImage < fImages.count()) { 679 while (fCurrentColorType < fColorTypes.count()) { 680 const SkString& path = fImages[fCurrentImage]; 681 SkColorType colorType = fColorTypes[fCurrentColorType]; 682 fCurrentColorType++; 683 // Check if the image decodes to the right color type 684 // before creating the benchmark 685 SkBitmap bitmap; 686 if (SkImageDecoder::DecodeFile(path.c_str(), &bitmap, 687 colorType, SkImageDecoder::kDecodePixels_Mode) 688 && bitmap.colorType() == colorType) { 689 return new DecodingBench(path, colorType); 690 } 691 } 692 fCurrentColorType = 0; 693 fCurrentImage++; 694 } 695 696 // Run the DecodingSubsetBenches 697 while (fCurrentSubsetImage < fImages.count()) { 698 while (fCurrentColorType < fColorTypes.count()) { 699 const SkString& path = fImages[fCurrentSubsetImage]; 700 SkColorType colorType = fColorTypes[fCurrentColorType]; 701 fCurrentColorType++; 702 // Check if the image decodes before creating the benchmark 703 SkAutoTUnref<SkData> encoded( 704 SkData::NewFromFileName(path.c_str())); 705 SkAutoTDelete<SkMemoryStream> stream( 706 new SkMemoryStream(encoded)); 707 SkAutoTDelete<SkImageDecoder> 708 decoder(SkImageDecoder::Factory(stream.get())); 709 if (!decoder) { 710 SkDebugf("Cannot find decoder for %s\n", path.c_str()); 711 } else { 712 stream->rewind(); 713 int w, h; 714 bool success; 715 if (!decoder->buildTileIndex(stream.detach(), &w, &h) 716 || w*h == 1) { 717 // This is not an error, but in this case we still 718 // do not want to run the benchmark. 719 success = false; 720 } else if (fDivisor > w || fDivisor > h) { 721 SkDebugf("Divisor %d is too big for %s %dx%d\n", 722 fDivisor, path.c_str(), w, h); 723 success = false; 724 } else { 725 const int sW = w / fDivisor; 726 const int sH = h / fDivisor; 727 SkBitmap bitmap; 728 success = true; 729 for (int y = 0; y < h; y += sH) { 730 for (int x = 0; x < w; x += sW) { 731 SkIRect rect = SkIRect::MakeXYWH(x, y, sW, sH); 732 success &= decoder->decodeSubset(&bitmap, rect, 733 colorType); 734 } 735 } 736 } 737 // Create the benchmark if successful 738 if (success) { 739 return new DecodingSubsetBench(path, colorType, 740 fDivisor); 741 } 742 } 743 } 744 fCurrentColorType = 0; 745 fCurrentSubsetImage++; 746 } 747 748 return NULL; 749 } 750 751 void fillCurrentOptions(ResultsWriter* log) const { 752 log->configOption("source_type", fSourceType); 753 log->configOption("bench_type", fBenchType); 754 if (0 == strcmp(fSourceType, "skp")) { 755 log->configOption("clip", 756 SkStringPrintf("%d %d %d %d", fClip.fLeft, fClip.fTop, 757 fClip.fRight, fClip.fBottom).c_str()); 758 log->configOption("scale", SkStringPrintf("%.2g", fScales[fCurrentScale]).c_str()); 759 if (fCurrentUseMPD > 0) { 760 SkASSERT(1 == fCurrentUseMPD || 2 == fCurrentUseMPD); 761 log->configOption("multi_picture_draw", fUseMPDs[fCurrentUseMPD-1] ? "true" : "false"); 762 } 763 } 764 if (0 == strcmp(fBenchType, "recording")) { 765 log->metric("bytes", fSKPBytes); 766 log->metric("ops", fSKPOps); 767 } 768 } 769 770private: 771 const BenchRegistry* fBenches; 772 const skiagm::GMRegistry* fGMs; 773 SkIRect fClip; 774 SkTArray<SkScalar> fScales; 775 SkTArray<SkString> fSKPs; 776 SkTArray<bool> fUseMPDs; 777 SkTArray<SkString> fImages; 778 SkTArray<SkColorType> fColorTypes; 779 780 double fSKPBytes, fSKPOps; 781 782 const char* fSourceType; // What we're benching: bench, GM, SKP, ... 783 const char* fBenchType; // How we bench it: micro, recording, playback, ... 784 int fCurrentRecording; 785 int fCurrentScale; 786 int fCurrentSKP; 787 int fCurrentUseMPD; 788 int fCurrentCodec; 789 int fCurrentImage; 790 int fCurrentSubsetImage; 791 int fCurrentColorType; 792 const int fDivisor; 793}; 794 795int nanobench_main(); 796int nanobench_main() { 797 SetupCrashHandler(); 798 SkAutoGraphics ag; 799 SkTaskGroup::Enabler enabled; 800 801#if SK_SUPPORT_GPU 802 GrContext::Options grContextOpts; 803 grContextOpts.fDrawPathToCompressedTexture = FLAGS_gpuCompressAlphaMasks; 804 gGrFactory.reset(SkNEW_ARGS(GrContextFactory, (grContextOpts))); 805#endif 806 807 if (FLAGS_veryVerbose) { 808 FLAGS_verbose = true; 809 } 810 811 if (kAutoTuneLoops != FLAGS_loops) { 812 FLAGS_samples = 1; 813 FLAGS_gpuFrameLag = 0; 814 } 815 816 if (!FLAGS_writePath.isEmpty()) { 817 SkDebugf("Writing files to %s.\n", FLAGS_writePath[0]); 818 if (!sk_mkdir(FLAGS_writePath[0])) { 819 SkDebugf("Could not create %s. Files won't be written.\n", FLAGS_writePath[0]); 820 FLAGS_writePath.set(0, NULL); 821 } 822 } 823 824 SkAutoTDelete<ResultsWriter> log(SkNEW(ResultsWriter)); 825 if (!FLAGS_outResultsFile.isEmpty()) { 826 log.reset(SkNEW(NanoJSONResultsWriter(FLAGS_outResultsFile[0]))); 827 } 828 829 if (1 == FLAGS_properties.count() % 2) { 830 SkDebugf("ERROR: --properties must be passed with an even number of arguments.\n"); 831 return 1; 832 } 833 for (int i = 1; i < FLAGS_properties.count(); i += 2) { 834 log->property(FLAGS_properties[i-1], FLAGS_properties[i]); 835 } 836 837 if (1 == FLAGS_key.count() % 2) { 838 SkDebugf("ERROR: --key must be passed with an even number of arguments.\n"); 839 return 1; 840 } 841 for (int i = 1; i < FLAGS_key.count(); i += 2) { 842 log->key(FLAGS_key[i-1], FLAGS_key[i]); 843 } 844 845 const double overhead = estimate_timer_overhead(); 846 SkDebugf("Timer overhead: %s\n", HUMANIZE(overhead)); 847 848 SkAutoTMalloc<double> samples(FLAGS_samples); 849 850 if (kAutoTuneLoops != FLAGS_loops) { 851 SkDebugf("Fixed number of loops; times would only be misleading so we won't print them.\n"); 852 } else if (FLAGS_verbose) { 853 // No header. 854 } else if (FLAGS_quiet) { 855 SkDebugf("median\tbench\tconfig\n"); 856 } else { 857 SkDebugf("maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tconfig\tbench\n", 858 FLAGS_samples, "samples"); 859 } 860 861 SkTDArray<Config> configs; 862 create_configs(&configs); 863 864 int runs = 0; 865 BenchmarkStream benchStream; 866 while (Benchmark* b = benchStream.next()) { 867 SkAutoTDelete<Benchmark> bench(b); 868 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getUniqueName())) { 869 continue; 870 } 871 872 SkTDArray<Target*> targets; 873 create_targets(&targets, bench.get(), configs); 874 875 if (!targets.isEmpty()) { 876 log->bench(bench->getUniqueName(), bench->getSize().fX, bench->getSize().fY); 877 bench->preDraw(); 878 } 879 for (int j = 0; j < targets.count(); j++) { 880 // During HWUI output this canvas may be NULL. 881 SkCanvas* canvas = targets[j]->getCanvas(); 882 const char* config = targets[j]->config.name; 883 884 targets[j]->setup(); 885 bench->perCanvasPreDraw(canvas); 886 887 const int loops = 888 targets[j]->needsFrameTiming() 889 ? gpu_bench(targets[j], bench.get(), samples.get()) 890 : cpu_bench(overhead, targets[j], bench.get(), samples.get()); 891 892 bench->perCanvasPostDraw(canvas); 893 894 if (Benchmark::kNonRendering_Backend != targets[j]->config.backend && 895 !FLAGS_writePath.isEmpty() && FLAGS_writePath[0]) { 896 SkString pngFilename = SkOSPath::Join(FLAGS_writePath[0], config); 897 pngFilename = SkOSPath::Join(pngFilename.c_str(), bench->getUniqueName()); 898 pngFilename.append(".png"); 899 write_canvas_png(targets[j], pngFilename); 900 } 901 902 if (kFailedLoops == loops) { 903 // Can't be timed. A warning note has already been printed. 904 continue; 905 } 906 907 Stats stats(samples.get(), FLAGS_samples); 908 log->config(config); 909 log->configOption("name", bench->getName()); 910 benchStream.fillCurrentOptions(log.get()); 911 targets[j]->fillOptions(log.get()); 912 log->metric("min_ms", stats.min); 913 if (runs++ % FLAGS_flushEvery == 0) { 914 log->flush(); 915 } 916 917 if (kAutoTuneLoops != FLAGS_loops) { 918 if (targets.count() == 1) { 919 config = ""; // Only print the config if we run the same bench on more than one. 920 } 921 SkDebugf("%4dM\t%s\t%s\n" 922 , sk_tools::getBestResidentSetSizeMB() 923 , bench->getUniqueName() 924 , config); 925 } else if (FLAGS_verbose) { 926 for (int i = 0; i < FLAGS_samples; i++) { 927 SkDebugf("%s ", HUMANIZE(samples[i])); 928 } 929 SkDebugf("%s\n", bench->getUniqueName()); 930 } else if (FLAGS_quiet) { 931 if (targets.count() == 1) { 932 config = ""; // Only print the config if we run the same bench on more than one. 933 } 934 SkDebugf("%s\t%s\t%s\n", HUMANIZE(stats.median), bench->getUniqueName(), config); 935 } else { 936 const double stddev_percent = 100 * sqrt(stats.var) / stats.mean; 937 SkDebugf("%4dM\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n" 938 , sk_tools::getBestResidentSetSizeMB() 939 , loops 940 , HUMANIZE(stats.min) 941 , HUMANIZE(stats.median) 942 , HUMANIZE(stats.mean) 943 , HUMANIZE(stats.max) 944 , stddev_percent 945 , stats.plot.c_str() 946 , config 947 , bench->getUniqueName() 948 ); 949 } 950#if SK_SUPPORT_GPU 951 if (FLAGS_gpuStats && 952 Benchmark::kGPU_Backend == targets[j]->config.backend) { 953 gGrFactory->get(targets[j]->config.ctxType)->printCacheStats(); 954 gGrFactory->get(targets[j]->config.ctxType)->printGpuStats(); 955 } 956#endif 957 } 958 targets.deleteAll(); 959 960#if SK_SUPPORT_GPU 961 if (FLAGS_abandonGpuContext) { 962 gGrFactory->abandonContexts(); 963 } 964 if (FLAGS_resetGpuContext || FLAGS_abandonGpuContext) { 965 gGrFactory->destroyContexts(); 966 } 967#endif 968 } 969 970 log->bench("memory_usage", 0,0); 971 log->config("meta"); 972 log->metric("max_rss_mb", sk_tools::getMaxResidentSetSizeMB()); 973 974#if SK_SUPPORT_GPU 975 // Make sure we clean up the global GrContextFactory here, otherwise we might race with the 976 // SkEventTracer destructor 977 gGrFactory.reset(NULL); 978#endif 979 980 return 0; 981} 982 983#if !defined SK_BUILD_FOR_IOS 984int main(int argc, char** argv) { 985 SkCommandLineFlags::Parse(argc, argv); 986 return nanobench_main(); 987} 988#endif 989