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