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