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