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