• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "Benchmark.h"
11 #include "CrashHandler.h"
12 #include "GMBench.h"
13 #include "ProcStats.h"
14 #include "ResultsWriter.h"
15 #include "RecordingBench.h"
16 #include "SKPBench.h"
17 #include "Stats.h"
18 #include "Timer.h"
19 
20 #include "SkBBHFactory.h"
21 #include "SkCanvas.h"
22 #include "SkCommonFlags.h"
23 #include "SkForceLinking.h"
24 #include "SkGraphics.h"
25 #include "SkOSFile.h"
26 #include "SkPictureRecorder.h"
27 #include "SkString.h"
28 #include "SkSurface.h"
29 
30 #if SK_SUPPORT_GPU
31     #include "gl/GrGLDefines.h"
32     #include "GrContextFactory.h"
33     SkAutoTDelete<GrContextFactory> gGrFactory;
34 #endif
35 
36 __SK_FORCE_IMAGE_DECODER_LINKING;
37 
38 static const int kAutoTuneLoops = -1;
39 
40 static const int kDefaultLoops =
41 #ifdef SK_DEBUG
42     1;
43 #else
44     kAutoTuneLoops;
45 #endif
46 
loops_help_txt()47 static SkString loops_help_txt() {
48     SkString help;
49     help.printf("Number of times to run each bench. Set this to %d to auto-"
50                 "tune for each bench. Timings are only reported when auto-tuning.",
51                 kAutoTuneLoops);
52     return help;
53 }
54 
55 DEFINE_int32(loops, kDefaultLoops, loops_help_txt().c_str());
56 
57 DEFINE_int32(samples, 10, "Number of samples to measure for each bench.");
58 DEFINE_int32(overheadLoops, 100000, "Loops to estimate timer overhead.");
59 DEFINE_double(overheadGoal, 0.0001,
60               "Loop until timer overhead is at most this fraction of our measurments.");
61 DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU.");
62 DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allows to lag.");
63 DEFINE_bool(gpuCompressAlphaMasks, false, "Compress masks generated from falling back to "
64                                           "software path rendering.");
65 
66 DEFINE_string(outResultsFile, "", "If given, write results here as JSON.");
67 DEFINE_int32(maxCalibrationAttempts, 3,
68              "Try up to this many times to guess loops for a bench, or skip the bench.");
69 DEFINE_int32(maxLoops, 1000000, "Never run a bench more times than this.");
70 DEFINE_string(clip, "0,0,1000,1000", "Clip for SKPs.");
71 DEFINE_string(scales, "1.0", "Space-separated scales for SKPs.");
72 DEFINE_bool(bbh, true, "Build a BBH for SKPs?");
73 
humanize(double ms)74 static SkString humanize(double ms) {
75     if (FLAGS_verbose) return SkStringPrintf("%llu", (uint64_t)(ms*1e6));
76     if (ms > 1e+3)     return SkStringPrintf("%.3gs",  ms/1e3);
77     if (ms < 1e-3)     return SkStringPrintf("%.3gns", ms*1e6);
78 #ifdef SK_BUILD_FOR_WIN
79     if (ms < 1)        return SkStringPrintf("%.3gus", ms*1e3);
80 #else
81     if (ms < 1)        return SkStringPrintf("%.3gµs", ms*1e3);
82 #endif
83     return SkStringPrintf("%.3gms", ms);
84 }
85 #define HUMANIZE(ms) humanize(ms).c_str()
86 
time(int loops,Benchmark * bench,SkCanvas * canvas,SkGLContextHelper * gl)87 static double time(int loops, Benchmark* bench, SkCanvas* canvas, SkGLContextHelper* gl) {
88     if (canvas) {
89         canvas->clear(SK_ColorWHITE);
90     }
91     WallTimer timer;
92     timer.start();
93     if (bench) {
94         bench->draw(loops, canvas);
95     }
96     if (canvas) {
97         canvas->flush();
98     }
99 #if SK_SUPPORT_GPU
100     if (gl) {
101         SK_GL(*gl, Flush());
102         gl->swapBuffers();
103     }
104 #endif
105     timer.end();
106     return timer.fWall;
107 }
108 
estimate_timer_overhead()109 static double estimate_timer_overhead() {
110     double overhead = 0;
111     for (int i = 0; i < FLAGS_overheadLoops; i++) {
112         overhead += time(1, NULL, NULL, NULL);
113     }
114     return overhead / FLAGS_overheadLoops;
115 }
116 
clamp_loops(int loops)117 static int clamp_loops(int loops) {
118     if (loops < 1) {
119         SkDebugf("ERROR: clamping loops from %d to 1.\n", loops);
120         return 1;
121     }
122     if (loops > FLAGS_maxLoops) {
123         SkDebugf("WARNING: clamping loops from %d to FLAGS_maxLoops, %d.\n", loops, FLAGS_maxLoops);
124         return FLAGS_maxLoops;
125     }
126     return loops;
127 }
128 
write_canvas_png(SkCanvas * canvas,const SkString & filename)129 static bool write_canvas_png(SkCanvas* canvas, const SkString& filename) {
130     if (filename.isEmpty()) {
131         return false;
132     }
133     if (kUnknown_SkColorType == canvas->imageInfo().colorType()) {
134         return false;
135     }
136     SkBitmap bmp;
137     bmp.setInfo(canvas->imageInfo());
138     if (!canvas->readPixels(&bmp, 0, 0)) {
139         SkDebugf("Can't read canvas pixels.\n");
140         return false;
141     }
142     SkString dir = SkOSPath::Dirname(filename.c_str());
143     if (!sk_mkdir(dir.c_str())) {
144         SkDebugf("Can't make dir %s.\n", dir.c_str());
145         return false;
146     }
147     SkFILEWStream stream(filename.c_str());
148     if (!stream.isValid()) {
149         SkDebugf("Can't write %s.\n", filename.c_str());
150         return false;
151     }
152     if (!SkImageEncoder::EncodeStream(&stream, bmp, SkImageEncoder::kPNG_Type, 100)) {
153         SkDebugf("Can't encode a PNG.\n");
154         return false;
155     }
156     return true;
157 }
158 
159 static int kFailedLoops = -2;
cpu_bench(const double overhead,Benchmark * bench,SkCanvas * canvas,double * samples)160 static int cpu_bench(const double overhead, Benchmark* bench, SkCanvas* canvas, double* samples) {
161     // First figure out approximately how many loops of bench it takes to make overhead negligible.
162     double bench_plus_overhead = 0.0;
163     int round = 0;
164     if (kAutoTuneLoops == FLAGS_loops) {
165         while (bench_plus_overhead < overhead) {
166             if (round++ == FLAGS_maxCalibrationAttempts) {
167                 SkDebugf("WARNING: Can't estimate loops for %s (%s vs. %s); skipping.\n",
168                          bench->getUniqueName(), HUMANIZE(bench_plus_overhead), HUMANIZE(overhead));
169                 return kFailedLoops;
170             }
171             bench_plus_overhead = time(1, bench, canvas, NULL);
172         }
173     }
174 
175     // Later we'll just start and stop the timer once but loop N times.
176     // We'll pick N to make timer overhead negligible:
177     //
178     //          overhead
179     //  -------------------------  < FLAGS_overheadGoal
180     //  overhead + N * Bench Time
181     //
182     // where bench_plus_overhead ≈ overhead + Bench Time.
183     //
184     // Doing some math, we get:
185     //
186     //  (overhead / FLAGS_overheadGoal) - overhead
187     //  ------------------------------------------  < N
188     //       bench_plus_overhead - overhead)
189     //
190     // Luckily, this also works well in practice. :)
191     int loops = FLAGS_loops;
192     if (kAutoTuneLoops == loops) {
193         const double numer = overhead / FLAGS_overheadGoal - overhead;
194         const double denom = bench_plus_overhead - overhead;
195         loops = (int)ceil(numer / denom);
196     }
197     loops = clamp_loops(loops);
198 
199     for (int i = 0; i < FLAGS_samples; i++) {
200         samples[i] = time(loops, bench, canvas, NULL) / loops;
201     }
202     return loops;
203 }
204 
205 #if SK_SUPPORT_GPU
gpu_bench(SkGLContextHelper * gl,Benchmark * bench,SkCanvas * canvas,double * samples)206 static int gpu_bench(SkGLContextHelper* gl,
207                      Benchmark* bench,
208                      SkCanvas* canvas,
209                      double* samples) {
210     gl->makeCurrent();
211     // Make sure we're done with whatever came before.
212     SK_GL(*gl, Finish());
213 
214     // First, figure out how many loops it'll take to get a frame up to FLAGS_gpuMs.
215     int loops = FLAGS_loops;
216     if (kAutoTuneLoops == loops) {
217         loops = 1;
218         double elapsed = 0;
219         do {
220             loops *= 2;
221             // If the GPU lets frames lag at all, we need to make sure we're timing
222             // _this_ round, not still timing last round.  We force this by looping
223             // more times than any reasonable GPU will allow frames to lag.
224             for (int i = 0; i < FLAGS_gpuFrameLag; i++) {
225                 elapsed = time(loops, bench, canvas, gl);
226             }
227         } while (elapsed < FLAGS_gpuMs);
228 
229         // We've overshot at least a little.  Scale back linearly.
230         loops = (int)ceil(loops * FLAGS_gpuMs / elapsed);
231 
232         // Might as well make sure we're not still timing our calibration.
233         SK_GL(*gl, Finish());
234     }
235     loops = clamp_loops(loops);
236 
237     // Pretty much the same deal as the calibration: do some warmup to make
238     // sure we're timing steady-state pipelined frames.
239     for (int i = 0; i < FLAGS_gpuFrameLag; i++) {
240         time(loops, bench, canvas, gl);
241     }
242 
243     // Now, actually do the timing!
244     for (int i = 0; i < FLAGS_samples; i++) {
245         samples[i] = time(loops, bench, canvas, gl) / loops;
246     }
247     return loops;
248 }
249 #endif
250 
to_lower(const char * str)251 static SkString to_lower(const char* str) {
252     SkString lower(str);
253     for (size_t i = 0; i < lower.size(); i++) {
254         lower[i] = tolower(lower[i]);
255     }
256     return lower;
257 }
258 
259 struct Config {
260     const char* name;
261     Benchmark::Backend backend;
262     SkColorType color;
263     SkAlphaType alpha;
264     int samples;
265 #if SK_SUPPORT_GPU
266     GrContextFactory::GLContextType ctxType;
267 #else
268     int bogusInt;
269 #endif
270 };
271 
272 struct Target {
TargetTarget273     explicit Target(const Config& c) : config(c) {}
274     const Config config;
275     SkAutoTDelete<SkSurface> surface;
276 #if SK_SUPPORT_GPU
277     SkGLContextHelper* gl;
278 #endif
279 };
280 
is_cpu_config_allowed(const char * name)281 static bool is_cpu_config_allowed(const char* name) {
282     for (int i = 0; i < FLAGS_config.count(); i++) {
283         if (to_lower(FLAGS_config[i]).equals(name)) {
284             return true;
285         }
286     }
287     return false;
288 }
289 
290 #if SK_SUPPORT_GPU
is_gpu_config_allowed(const char * name,GrContextFactory::GLContextType ctxType,int sampleCnt)291 static bool is_gpu_config_allowed(const char* name, GrContextFactory::GLContextType ctxType,
292                                   int sampleCnt) {
293     if (!is_cpu_config_allowed(name)) {
294         return false;
295     }
296     if (const GrContext* ctx = gGrFactory->get(ctxType)) {
297         return sampleCnt <= ctx->getMaxSampleCount();
298     }
299     return false;
300 }
301 #endif
302 
303 #if SK_SUPPORT_GPU
304 #define kBogusGLContextType GrContextFactory::kNative_GLContextType
305 #else
306 #define kBogusGLContextType 0
307 #endif
308 
309 // Append all configs that are enabled and supported.
create_configs(SkTDArray<Config> * configs)310 static void create_configs(SkTDArray<Config>* configs) {
311     #define CPU_CONFIG(name, backend, color, alpha)                                               \
312         if (is_cpu_config_allowed(#name)) {                                                       \
313             Config config = { #name, Benchmark::backend, color, alpha, 0, kBogusGLContextType };  \
314             configs->push(config);                                                                \
315         }
316 
317     if (FLAGS_cpu) {
318         CPU_CONFIG(nonrendering, kNonRendering_Backend, kUnknown_SkColorType, kUnpremul_SkAlphaType)
319         CPU_CONFIG(8888, kRaster_Backend, kN32_SkColorType, kPremul_SkAlphaType)
320         CPU_CONFIG(565, kRaster_Backend, kRGB_565_SkColorType, kOpaque_SkAlphaType)
321     }
322 
323 #if SK_SUPPORT_GPU
324     #define GPU_CONFIG(name, ctxType, samples)                                   \
325         if (is_gpu_config_allowed(#name, GrContextFactory::ctxType, samples)) {  \
326             Config config = {                                                    \
327                 #name,                                                           \
328                 Benchmark::kGPU_Backend,                                         \
329                 kN32_SkColorType,                                                \
330                 kPremul_SkAlphaType,                                             \
331                 samples,                                                         \
332                 GrContextFactory::ctxType };                                     \
333             configs->push(config);                                               \
334         }
335 
336     if (FLAGS_gpu) {
337         GPU_CONFIG(gpu, kNative_GLContextType, 0)
338         GPU_CONFIG(msaa4, kNative_GLContextType, 4)
339         GPU_CONFIG(msaa16, kNative_GLContextType, 16)
340         GPU_CONFIG(nvprmsaa4, kNVPR_GLContextType, 4)
341         GPU_CONFIG(nvprmsaa16, kNVPR_GLContextType, 16)
342         GPU_CONFIG(debug, kDebug_GLContextType, 0)
343         GPU_CONFIG(nullgpu, kNull_GLContextType, 0)
344 #ifdef SK_ANGLE
345         GPU_CONFIG(angle, kANGLE_GLContextType, 0)
346 #endif
347     }
348 #endif
349 }
350 
351 // If bench is enabled for config, returns a Target* for it, otherwise NULL.
is_enabled(Benchmark * bench,const Config & config)352 static Target* is_enabled(Benchmark* bench, const Config& config) {
353     if (!bench->isSuitableFor(config.backend)) {
354         return NULL;
355     }
356 
357     SkImageInfo info = SkImageInfo::Make(bench->getSize().fX, bench->getSize().fY,
358                                          config.color, config.alpha);
359 
360     Target* target = new Target(config);
361 
362     if (Benchmark::kRaster_Backend == config.backend) {
363         target->surface.reset(SkSurface::NewRaster(info));
364     }
365 #if SK_SUPPORT_GPU
366     else if (Benchmark::kGPU_Backend == config.backend) {
367         target->surface.reset(SkSurface::NewRenderTarget(gGrFactory->get(config.ctxType), info,
368                                                          config.samples));
369         target->gl = gGrFactory->getGLContext(config.ctxType);
370     }
371 #endif
372 
373     if (Benchmark::kNonRendering_Backend != config.backend && !target->surface.get()) {
374         delete target;
375         return NULL;
376     }
377     return target;
378 }
379 
380 // Creates targets for a benchmark and a set of configs.
create_targets(SkTDArray<Target * > * targets,Benchmark * b,const SkTDArray<Config> & configs)381 static void create_targets(SkTDArray<Target*>* targets, Benchmark* b,
382                            const SkTDArray<Config>& configs) {
383     for (int i = 0; i < configs.count(); ++i) {
384         if (Target* t = is_enabled(b, configs[i])) {
385             targets->push(t);
386         }
387 
388     }
389 }
390 
391 #if SK_SUPPORT_GPU
fill_gpu_options(ResultsWriter * log,SkGLContextHelper * ctx)392 static void fill_gpu_options(ResultsWriter* log, SkGLContextHelper* ctx) {
393     const GrGLubyte* version;
394     SK_GL_RET(*ctx, version, GetString(GR_GL_VERSION));
395     log->configOption("GL_VERSION", (const char*)(version));
396 
397     SK_GL_RET(*ctx, version, GetString(GR_GL_RENDERER));
398     log->configOption("GL_RENDERER", (const char*) version);
399 
400     SK_GL_RET(*ctx, version, GetString(GR_GL_VENDOR));
401     log->configOption("GL_VENDOR", (const char*) version);
402 
403     SK_GL_RET(*ctx, version, GetString(GR_GL_SHADING_LANGUAGE_VERSION));
404     log->configOption("GL_SHADING_LANGUAGE_VERSION", (const char*) version);
405 }
406 #endif
407 
408 class BenchmarkStream {
409 public:
BenchmarkStream()410     BenchmarkStream() : fBenches(BenchRegistry::Head())
411                       , fGMs(skiagm::GMRegistry::Head())
412                       , fCurrentRecording(0)
413                       , fCurrentScale(0)
414                       , fCurrentSKP(0) {
415         for (int i = 0; i < FLAGS_skps.count(); i++) {
416             if (SkStrEndsWith(FLAGS_skps[i], ".skp")) {
417                 fSKPs.push_back() = FLAGS_skps[i];
418             } else {
419                 SkOSFile::Iter it(FLAGS_skps[i], ".skp");
420                 SkString path;
421                 while (it.next(&path)) {
422                     fSKPs.push_back() = SkOSPath::Join(FLAGS_skps[0], path.c_str());
423                 }
424             }
425         }
426 
427         if (4 != sscanf(FLAGS_clip[0], "%d,%d,%d,%d",
428                         &fClip.fLeft, &fClip.fTop, &fClip.fRight, &fClip.fBottom)) {
429             SkDebugf("Can't parse %s from --clip as an SkIRect.\n", FLAGS_clip[0]);
430             exit(1);
431         }
432 
433         for (int i = 0; i < FLAGS_scales.count(); i++) {
434             if (1 != sscanf(FLAGS_scales[i], "%f", &fScales.push_back())) {
435                 SkDebugf("Can't parse %s from --scales as an SkScalar.\n", FLAGS_scales[i]);
436                 exit(1);
437             }
438         }
439     }
440 
ReadPicture(const char * path,SkAutoTUnref<SkPicture> * pic)441     static bool ReadPicture(const char* path, SkAutoTUnref<SkPicture>* pic) {
442         // Not strictly necessary, as it will be checked again later,
443         // but helps to avoid a lot of pointless work if we're going to skip it.
444         if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path)) {
445             return false;
446         }
447 
448         SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path));
449         if (stream.get() == NULL) {
450             SkDebugf("Could not read %s.\n", path);
451             return false;
452         }
453 
454         pic->reset(SkPicture::CreateFromStream(stream.get()));
455         if (pic->get() == NULL) {
456             SkDebugf("Could not read %s as an SkPicture.\n", path);
457             return false;
458         }
459         return true;
460     }
461 
next()462     Benchmark* next() {
463         if (fBenches) {
464             Benchmark* bench = fBenches->factory()(NULL);
465             fBenches = fBenches->next();
466             fSourceType = "bench";
467             fBenchType  = "micro";
468             return bench;
469         }
470 
471         while (fGMs) {
472             SkAutoTDelete<skiagm::GM> gm(fGMs->factory()(NULL));
473             fGMs = fGMs->next();
474             if (gm->getFlags() & skiagm::GM::kAsBench_Flag) {
475                 fSourceType = "gm";
476                 fBenchType  = "micro";
477                 return SkNEW_ARGS(GMBench, (gm.detach()));
478             }
479         }
480 
481         // First add all .skps as RecordingBenches.
482         while (fCurrentRecording < fSKPs.count()) {
483             const SkString& path = fSKPs[fCurrentRecording++];
484             SkAutoTUnref<SkPicture> pic;
485             if (!ReadPicture(path.c_str(), &pic)) {
486                 continue;
487             }
488             SkString name = SkOSPath::Basename(path.c_str());
489             fSourceType = "skp";
490             fBenchType  = "recording";
491             return SkNEW_ARGS(RecordingBench, (name.c_str(), pic.get(), FLAGS_bbh));
492         }
493 
494         // Then once each for each scale as SKPBenches (playback).
495         while (fCurrentScale < fScales.count()) {
496             while (fCurrentSKP < fSKPs.count()) {
497                 const SkString& path = fSKPs[fCurrentSKP++];
498                 SkAutoTUnref<SkPicture> pic;
499                 if (!ReadPicture(path.c_str(), &pic)) {
500                     continue;
501                 }
502                 if (FLAGS_bbh) {
503                     // The SKP we read off disk doesn't have a BBH.  Re-record so it grows one.
504                     // Here we use an SkTileGrid with parameters optimized for FLAGS_clip.
505                     const SkTileGridFactory::TileGridInfo info = {
506                         SkISize::Make(fClip.width(), fClip.height()),  // tile interval
507                         SkISize::Make(0,0),                            // margin
508                         SkIPoint::Make(fClip.left(), fClip.top()),     // offset
509                     };
510                     SkTileGridFactory factory(info);
511                     SkPictureRecorder recorder;
512                     pic->playback(recorder.beginRecording(pic->cullRect().width(),
513                                                           pic->cullRect().height(),
514                                                           &factory));
515                     pic.reset(recorder.endRecording());
516                 }
517                 SkString name = SkOSPath::Basename(path.c_str());
518                 fSourceType = "skp";
519                 fBenchType  = "playback";
520                 return SkNEW_ARGS(SKPBench,
521                         (name.c_str(), pic.get(), fClip, fScales[fCurrentScale]));
522             }
523             fCurrentSKP = 0;
524             fCurrentScale++;
525         }
526 
527         return NULL;
528     }
529 
fillCurrentOptions(ResultsWriter * log) const530     void fillCurrentOptions(ResultsWriter* log) const {
531         log->configOption("source_type", fSourceType);
532         log->configOption("bench_type",  fBenchType);
533         if (0 == strcmp(fSourceType, "skp")) {
534             log->configOption("clip",
535                     SkStringPrintf("%d %d %d %d", fClip.fLeft, fClip.fTop,
536                                                   fClip.fRight, fClip.fBottom).c_str());
537             log->configOption("scale", SkStringPrintf("%.2g", fScales[fCurrentScale]).c_str());
538         }
539     }
540 
541 private:
542     const BenchRegistry* fBenches;
543     const skiagm::GMRegistry* fGMs;
544     SkIRect            fClip;
545     SkTArray<SkScalar> fScales;
546     SkTArray<SkString> fSKPs;
547 
548     const char* fSourceType;  // What we're benching: bench, GM, SKP, ...
549     const char* fBenchType;   // How we bench it: micro, recording, playback, ...
550     int fCurrentRecording;
551     int fCurrentScale;
552     int fCurrentSKP;
553 };
554 
555 int nanobench_main();
nanobench_main()556 int nanobench_main() {
557     SetupCrashHandler();
558     SkAutoGraphics ag;
559 
560 #if SK_SUPPORT_GPU
561     GrContext::Options grContextOpts;
562     grContextOpts.fDrawPathToCompressedTexture = FLAGS_gpuCompressAlphaMasks;
563     gGrFactory.reset(SkNEW_ARGS(GrContextFactory, (grContextOpts)));
564 #endif
565 
566     if (kAutoTuneLoops != FLAGS_loops) {
567         FLAGS_samples     = 1;
568         FLAGS_gpuFrameLag = 0;
569     }
570 
571     if (!FLAGS_writePath.isEmpty()) {
572         SkDebugf("Writing files to %s.\n", FLAGS_writePath[0]);
573         if (!sk_mkdir(FLAGS_writePath[0])) {
574             SkDebugf("Could not create %s. Files won't be written.\n", FLAGS_writePath[0]);
575             FLAGS_writePath.set(0, NULL);
576         }
577     }
578 
579     SkAutoTDelete<ResultsWriter> log(SkNEW(ResultsWriter));
580     if (!FLAGS_outResultsFile.isEmpty()) {
581         log.reset(SkNEW(NanoJSONResultsWriter(FLAGS_outResultsFile[0])));
582     }
583 
584     if (1 == FLAGS_properties.count() % 2) {
585         SkDebugf("ERROR: --properties must be passed with an even number of arguments.\n");
586         return 1;
587     }
588     for (int i = 1; i < FLAGS_properties.count(); i += 2) {
589         log->property(FLAGS_properties[i-1], FLAGS_properties[i]);
590     }
591 
592     if (1 == FLAGS_key.count() % 2) {
593         SkDebugf("ERROR: --key must be passed with an even number of arguments.\n");
594         return 1;
595     }
596     for (int i = 1; i < FLAGS_key.count(); i += 2) {
597         log->key(FLAGS_key[i-1], FLAGS_key[i]);
598     }
599 
600     const double overhead = estimate_timer_overhead();
601     SkDebugf("Timer overhead: %s\n", HUMANIZE(overhead));
602 
603     SkAutoTMalloc<double> samples(FLAGS_samples);
604 
605     if (kAutoTuneLoops != FLAGS_loops) {
606         SkDebugf("Fixed number of loops; times would only be misleading so we won't print them.\n");
607     } else if (FLAGS_verbose) {
608         // No header.
609     } else if (FLAGS_quiet) {
610         SkDebugf("median\tbench\tconfig\n");
611     } else {
612         SkDebugf("maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tconfig\tbench\n",
613                  FLAGS_samples, "samples");
614     }
615 
616     SkTDArray<Config> configs;
617     create_configs(&configs);
618 
619     BenchmarkStream benchStream;
620     while (Benchmark* b = benchStream.next()) {
621         SkAutoTDelete<Benchmark> bench(b);
622         if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getUniqueName())) {
623             continue;
624         }
625 
626         SkTDArray<Target*> targets;
627         create_targets(&targets, bench.get(), configs);
628 
629         if (!targets.isEmpty()) {
630             log->bench(bench->getUniqueName(), bench->getSize().fX, bench->getSize().fY);
631             bench->preDraw();
632         }
633         for (int j = 0; j < targets.count(); j++) {
634             SkCanvas* canvas = targets[j]->surface.get() ? targets[j]->surface->getCanvas() : NULL;
635             const char* config = targets[j]->config.name;
636 
637             const int loops =
638 #if SK_SUPPORT_GPU
639                 Benchmark::kGPU_Backend == targets[j]->config.backend
640                 ? gpu_bench(targets[j]->gl, bench.get(), canvas, samples.get())
641                 :
642 #endif
643                  cpu_bench(       overhead, bench.get(), canvas, samples.get());
644 
645             if (canvas && !FLAGS_writePath.isEmpty() && FLAGS_writePath[0]) {
646                 SkString pngFilename = SkOSPath::Join(FLAGS_writePath[0], config);
647                 pngFilename = SkOSPath::Join(pngFilename.c_str(), bench->getUniqueName());
648                 pngFilename.append(".png");
649                 write_canvas_png(canvas, pngFilename);
650             }
651 
652             if (kFailedLoops == loops) {
653                 // Can't be timed.  A warning note has already been printed.
654                 continue;
655             }
656 
657             Stats stats(samples.get(), FLAGS_samples);
658             log->config(config);
659             log->configOption("name", bench->getName());
660             benchStream.fillCurrentOptions(log.get());
661 #if SK_SUPPORT_GPU
662             if (Benchmark::kGPU_Backend == targets[j]->config.backend) {
663                 fill_gpu_options(log.get(), targets[j]->gl);
664             }
665 #endif
666             log->timer("min_ms",    stats.min);
667             log->timer("median_ms", stats.median);
668             log->timer("mean_ms",   stats.mean);
669             log->timer("max_ms",    stats.max);
670             log->timer("stddev_ms", sqrt(stats.var));
671 
672             if (kAutoTuneLoops != FLAGS_loops) {
673                 if (targets.count() == 1) {
674                     config = ""; // Only print the config if we run the same bench on more than one.
675                 }
676                 SkDebugf("%4dM\t%s\t%s\n"
677                          , sk_tools::getMaxResidentSetSizeMB()
678                          , bench->getUniqueName()
679                          , config);
680             } else if (FLAGS_verbose) {
681                 for (int i = 0; i < FLAGS_samples; i++) {
682                     SkDebugf("%s  ", HUMANIZE(samples[i]));
683                 }
684                 SkDebugf("%s\n", bench->getUniqueName());
685             } else if (FLAGS_quiet) {
686                 if (targets.count() == 1) {
687                     config = ""; // Only print the config if we run the same bench on more than one.
688                 }
689                 SkDebugf("%s\t%s\t%s\n", HUMANIZE(stats.median), bench->getUniqueName(), config);
690             } else {
691                 const double stddev_percent = 100 * sqrt(stats.var) / stats.mean;
692                 SkDebugf("%4dM\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n"
693                         , sk_tools::getMaxResidentSetSizeMB()
694                         , loops
695                         , HUMANIZE(stats.min)
696                         , HUMANIZE(stats.median)
697                         , HUMANIZE(stats.mean)
698                         , HUMANIZE(stats.max)
699                         , stddev_percent
700                         , stats.plot.c_str()
701                         , config
702                         , bench->getUniqueName()
703                         );
704             }
705         }
706         targets.deleteAll();
707 
708     #if SK_SUPPORT_GPU
709         if (FLAGS_abandonGpuContext) {
710             gGrFactory->abandonContexts();
711         }
712         if (FLAGS_resetGpuContext || FLAGS_abandonGpuContext) {
713             gGrFactory->destroyContexts();
714         }
715     #endif
716     }
717 
718     return 0;
719 }
720 
721 #if !defined SK_BUILD_FOR_IOS
main(int argc,char ** argv)722 int main(int argc, char** argv) {
723     SkCommandLineFlags::Parse(argc, argv);
724     return nanobench_main();
725 }
726 #endif
727