blob: 5cd3e2fbf40f1aaacb0abde41d9035275693b37a [file] [log] [blame]
mtkleinf3723212014-06-25 14:08:00 -07001/*
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
mtkleinbb6a0282014-07-01 08:43:42 -07008#include <ctype.h>
9
mtkleinf3723212014-06-25 14:08:00 -070010#include "Benchmark.h"
11#include "CrashHandler.h"
mtklein60317d0f2014-07-14 11:30:37 -070012#include "ResultsWriter.h"
mtkleinf3723212014-06-25 14:08:00 -070013#include "Stats.h"
14#include "Timer.h"
15
16#include "SkCanvas.h"
17#include "SkCommandLineFlags.h"
18#include "SkForceLinking.h"
19#include "SkGraphics.h"
20#include "SkString.h"
21#include "SkSurface.h"
22
mtkleinbb6a0282014-07-01 08:43:42 -070023#if SK_SUPPORT_GPU
jcgregoriobf5e5232014-07-17 13:14:16 -070024 #include "gl/GrGLDefines.h"
mtkleinbb6a0282014-07-01 08:43:42 -070025 #include "GrContextFactory.h"
26 GrContextFactory gGrFactory;
27#endif
28
mtkleinf3723212014-06-25 14:08:00 -070029__SK_FORCE_IMAGE_DECODER_LINKING;
30
mtkleina189ccd2014-07-14 12:28:47 -070031#if SK_DEBUG
32 DEFINE_bool(runOnce, true, "Run each benchmark just once?");
33#else
34 DEFINE_bool(runOnce, false, "Run each benchmark just once?");
35#endif
36
mtkleinf3723212014-06-25 14:08:00 -070037DEFINE_int32(samples, 10, "Number of samples to measure for each bench.");
38DEFINE_int32(overheadLoops, 100000, "Loops to estimate timer overhead.");
39DEFINE_double(overheadGoal, 0.0001,
40 "Loop until timer overhead is at most this fraction of our measurments.");
41DEFINE_string(match, "", "The usual filters on file names of benchmarks to measure.");
42DEFINE_bool2(quiet, q, false, "Print only bench name and minimum sample.");
43DEFINE_bool2(verbose, v, false, "Print all samples.");
mtkleinbb6a0282014-07-01 08:43:42 -070044DEFINE_string(config, "nonrendering 8888 gpu", "Configs to measure. Options: "
45 "565 8888 gpu nonrendering debug nullgpu msaa4 msaa16 nvprmsaa4 nvprmsaa16 angle");
46DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU.");
47DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allows to lag.");
mtkleinf3723212014-06-25 14:08:00 -070048
mtklein40b32be2014-07-09 08:46:49 -070049DEFINE_bool(cpu, true, "Master switch for CPU-bound work.");
50DEFINE_bool(gpu, true, "Master switch for GPU-bound work.");
51
mtklein60317d0f2014-07-14 11:30:37 -070052DEFINE_string(outResultsFile, "", "If given, write results here as JSON.");
mtklein1e319f72014-07-15 08:27:06 -070053DEFINE_bool(resetGpuContext, true, "Reset the GrContext before running each bench.");
mtklein55b0ffc2014-07-17 08:38:23 -070054DEFINE_int32(maxCalibrationAttempts, 3,
55 "Try up to this many times to guess loops for a bench, or skip the bench.");
56DEFINE_int32(maxLoops, 1000000, "Never run a bench more times than this.");
jcgregoriobf5e5232014-07-17 13:14:16 -070057DEFINE_string(key, "", "Space-separated key/value pairs to add to JSON.");
58DEFINE_string(gitHash, "", "Git hash to add to JSON.");
mtklein60317d0f2014-07-14 11:30:37 -070059
mtkleinf3723212014-06-25 14:08:00 -070060
61static SkString humanize(double ms) {
62 if (ms > 1e+3) return SkStringPrintf("%.3gs", ms/1e3);
63 if (ms < 1e-3) return SkStringPrintf("%.3gns", ms*1e6);
mtklein62386882014-07-15 10:30:31 -070064#ifdef SK_BUILD_FOR_WIN
65 if (ms < 1) return SkStringPrintf("%.3gus", ms*1e3);
66#else
mtkleinf3723212014-06-25 14:08:00 -070067 if (ms < 1) return SkStringPrintf("%.3gµs", ms*1e3);
mtklein62386882014-07-15 10:30:31 -070068#endif
mtkleinf3723212014-06-25 14:08:00 -070069 return SkStringPrintf("%.3gms", ms);
70}
mtklein55b0ffc2014-07-17 08:38:23 -070071#define HUMANIZE(ms) humanize(ms).c_str()
mtkleinf3723212014-06-25 14:08:00 -070072
mtkleinbb6a0282014-07-01 08:43:42 -070073static double time(int loops, Benchmark* bench, SkCanvas* canvas, SkGLContextHelper* gl) {
74 WallTimer timer;
75 timer.start();
76 if (bench) {
77 bench->draw(loops, canvas);
78 }
79 if (canvas) {
80 canvas->flush();
81 }
82#if SK_SUPPORT_GPU
83 if (gl) {
84 SK_GL(*gl, Flush());
85 gl->swapBuffers();
86 }
87#endif
88 timer.end();
89 return timer.fWall;
90}
91
mtkleinf3723212014-06-25 14:08:00 -070092static double estimate_timer_overhead() {
93 double overhead = 0;
mtkleinf3723212014-06-25 14:08:00 -070094 for (int i = 0; i < FLAGS_overheadLoops; i++) {
mtkleinbb6a0282014-07-01 08:43:42 -070095 overhead += time(1, NULL, NULL, NULL);
mtkleinf3723212014-06-25 14:08:00 -070096 }
97 return overhead / FLAGS_overheadLoops;
98}
99
mtklein55b0ffc2014-07-17 08:38:23 -0700100static int clamp_loops(int loops) {
101 if (loops < 1) {
102 SkDebugf("ERROR: clamping loops from %d to 1.\n", loops);
103 return 1;
104 }
105 if (loops > FLAGS_maxLoops) {
106 SkDebugf("WARNING: clamping loops from %d to FLAGS_maxLoops, %d.\n", loops, FLAGS_maxLoops);
107 return FLAGS_maxLoops;
108 }
109 return loops;
110}
111
mtkleinbb6a0282014-07-01 08:43:42 -0700112static int cpu_bench(const double overhead, Benchmark* bench, SkCanvas* canvas, double* samples) {
113 // First figure out approximately how many loops of bench it takes to make overhead negligible.
114 double bench_plus_overhead;
mtklein55b0ffc2014-07-17 08:38:23 -0700115 int round = 0;
mtkleinf3723212014-06-25 14:08:00 -0700116 do {
mtkleinbb6a0282014-07-01 08:43:42 -0700117 bench_plus_overhead = time(1, bench, canvas, NULL);
mtklein55b0ffc2014-07-17 08:38:23 -0700118 if (++round == FLAGS_maxCalibrationAttempts) {
119 SkDebugf("WARNING: Can't estimate loops for %s (%s vs. %s); skipping.\n",
120 bench->getName(), HUMANIZE(bench_plus_overhead), HUMANIZE(overhead));
121 return 0;
122 }
Mike Kleine3631362014-07-15 17:56:37 -0400123 } while (bench_plus_overhead < overhead);
mtkleinf3723212014-06-25 14:08:00 -0700124
mtkleinbb6a0282014-07-01 08:43:42 -0700125 // Later we'll just start and stop the timer once but loop N times.
mtkleinf3723212014-06-25 14:08:00 -0700126 // We'll pick N to make timer overhead negligible:
127 //
mtkleinbb6a0282014-07-01 08:43:42 -0700128 // overhead
129 // ------------------------- < FLAGS_overheadGoal
130 // overhead + N * Bench Time
mtkleinf3723212014-06-25 14:08:00 -0700131 //
mtkleinbb6a0282014-07-01 08:43:42 -0700132 // where bench_plus_overhead ≈ overhead + Bench Time.
mtkleinf3723212014-06-25 14:08:00 -0700133 //
134 // Doing some math, we get:
135 //
mtkleinbb6a0282014-07-01 08:43:42 -0700136 // (overhead / FLAGS_overheadGoal) - overhead
137 // ------------------------------------------ < N
138 // bench_plus_overhead - overhead)
mtkleinf3723212014-06-25 14:08:00 -0700139 //
140 // Luckily, this also works well in practice. :)
141 const double numer = overhead / FLAGS_overheadGoal - overhead;
mtkleinbb6a0282014-07-01 08:43:42 -0700142 const double denom = bench_plus_overhead - overhead;
mtklein55b0ffc2014-07-17 08:38:23 -0700143 const int loops = clamp_loops(FLAGS_runOnce ? 1 : (int)ceil(numer / denom));
mtkleinbb6a0282014-07-01 08:43:42 -0700144
145 for (int i = 0; i < FLAGS_samples; i++) {
146 samples[i] = time(loops, bench, canvas, NULL) / loops;
147 }
148 return loops;
mtkleinf3723212014-06-25 14:08:00 -0700149}
150
mtkleinbb6a0282014-07-01 08:43:42 -0700151#if SK_SUPPORT_GPU
152static int gpu_bench(SkGLContextHelper* gl,
153 Benchmark* bench,
154 SkCanvas* canvas,
155 double* samples) {
156 // Make sure we're done with whatever came before.
mtklein9bc86ed2014-07-01 10:02:42 -0700157 SK_GL(*gl, Finish());
mtkleinbb6a0282014-07-01 08:43:42 -0700158
159 // First, figure out how many loops it'll take to get a frame up to FLAGS_gpuMs.
160 int loops = 1;
mtkleina189ccd2014-07-14 12:28:47 -0700161 if (!FLAGS_runOnce) {
162 double elapsed = 0;
163 do {
164 loops *= 2;
165 // If the GPU lets frames lag at all, we need to make sure we're timing
166 // _this_ round, not still timing last round. We force this by looping
167 // more times than any reasonable GPU will allow frames to lag.
168 for (int i = 0; i < FLAGS_gpuFrameLag; i++) {
169 elapsed = time(loops, bench, canvas, gl);
170 }
171 } while (elapsed < FLAGS_gpuMs);
mtkleinbb6a0282014-07-01 08:43:42 -0700172
mtkleina189ccd2014-07-14 12:28:47 -0700173 // We've overshot at least a little. Scale back linearly.
174 loops = (int)ceil(loops * FLAGS_gpuMs / elapsed);
mtkleinbb6a0282014-07-01 08:43:42 -0700175
mtkleina189ccd2014-07-14 12:28:47 -0700176 // Might as well make sure we're not still timing our calibration.
177 SK_GL(*gl, Finish());
178 }
mtklein55b0ffc2014-07-17 08:38:23 -0700179 loops = clamp_loops(loops);
mtkleinbb6a0282014-07-01 08:43:42 -0700180
181 // Pretty much the same deal as the calibration: do some warmup to make
182 // sure we're timing steady-state pipelined frames.
183 for (int i = 0; i < FLAGS_gpuFrameLag; i++) {
184 time(loops, bench, canvas, gl);
mtkleinf3723212014-06-25 14:08:00 -0700185 }
mtkleinbb6a0282014-07-01 08:43:42 -0700186
187 // Now, actually do the timing!
188 for (int i = 0; i < FLAGS_samples; i++) {
189 samples[i] = time(loops, bench, canvas, gl) / loops;
190 }
191 return loops;
192}
193#endif
194
195static SkString to_lower(const char* str) {
196 SkString lower(str);
197 for (size_t i = 0; i < lower.size(); i++) {
198 lower[i] = tolower(lower[i]);
199 }
200 return lower;
mtkleinf3723212014-06-25 14:08:00 -0700201}
202
mtkleinbb6a0282014-07-01 08:43:42 -0700203struct Target {
204 const char* config;
205 Benchmark::Backend backend;
206 SkAutoTDelete<SkSurface> surface;
207#if SK_SUPPORT_GPU
208 SkGLContextHelper* gl;
209#endif
210};
mtkleinf3723212014-06-25 14:08:00 -0700211
mtkleinbb6a0282014-07-01 08:43:42 -0700212// If bench is enabled for backend/config, returns a Target* for them, otherwise NULL.
213static Target* is_enabled(Benchmark* bench, Benchmark::Backend backend, const char* config) {
214 if (!bench->isSuitableFor(backend)) {
215 return NULL;
mtkleinf3723212014-06-25 14:08:00 -0700216 }
217
mtkleinbb6a0282014-07-01 08:43:42 -0700218 for (int i = 0; i < FLAGS_config.count(); i++) {
219 if (to_lower(FLAGS_config[i]).equals(config)) {
220 Target* target = new Target;
221 target->config = config;
222 target->backend = backend;
223 return target;
mtkleinf3723212014-06-25 14:08:00 -0700224 }
225 }
mtkleinbb6a0282014-07-01 08:43:42 -0700226 return NULL;
227}
228
229// Append all targets that are suitable for bench.
230static void create_targets(Benchmark* bench, SkTDArray<Target*>* targets) {
231 const int w = bench->getSize().fX,
232 h = bench->getSize().fY;
233 const SkImageInfo _8888 = { w, h, kN32_SkColorType, kPremul_SkAlphaType },
234 _565 = { w, h, kRGB_565_SkColorType, kOpaque_SkAlphaType };
235
236 #define CPU_TARGET(config, backend, code) \
237 if (Target* t = is_enabled(bench, Benchmark::backend, #config)) { \
238 t->surface.reset(code); \
239 targets->push(t); \
240 }
mtklein40b32be2014-07-09 08:46:49 -0700241 if (FLAGS_cpu) {
242 CPU_TARGET(nonrendering, kNonRendering_Backend, NULL)
243 CPU_TARGET(8888, kRaster_Backend, SkSurface::NewRaster(_8888))
244 CPU_TARGET(565, kRaster_Backend, SkSurface::NewRaster(_565))
245 }
mtkleinbb6a0282014-07-01 08:43:42 -0700246
247#if SK_SUPPORT_GPU
mtklein1e319f72014-07-15 08:27:06 -0700248
mtkleinbb6a0282014-07-01 08:43:42 -0700249 #define GPU_TARGET(config, ctxType, info, samples) \
250 if (Target* t = is_enabled(bench, Benchmark::kGPU_Backend, #config)) { \
251 t->surface.reset(SkSurface::NewRenderTarget(gGrFactory.get(ctxType), info, samples)); \
252 t->gl = gGrFactory.getGLContext(ctxType); \
253 targets->push(t); \
254 }
mtklein40b32be2014-07-09 08:46:49 -0700255 if (FLAGS_gpu) {
256 GPU_TARGET(gpu, GrContextFactory::kNative_GLContextType, _8888, 0)
257 GPU_TARGET(msaa4, GrContextFactory::kNative_GLContextType, _8888, 4)
258 GPU_TARGET(msaa16, GrContextFactory::kNative_GLContextType, _8888, 16)
259 GPU_TARGET(nvprmsaa4, GrContextFactory::kNVPR_GLContextType, _8888, 4)
260 GPU_TARGET(nvprmsaa16, GrContextFactory::kNVPR_GLContextType, _8888, 16)
261 GPU_TARGET(debug, GrContextFactory::kDebug_GLContextType, _8888, 0)
262 GPU_TARGET(nullgpu, GrContextFactory::kNull_GLContextType, _8888, 0)
263 #if SK_ANGLE
264 GPU_TARGET(angle, GrContextFactory::kANGLE_GLContextType, _8888, 0)
265 #endif
266 }
mtkleinbb6a0282014-07-01 08:43:42 -0700267#endif
mtkleinf3723212014-06-25 14:08:00 -0700268}
269
mtklein60317d0f2014-07-14 11:30:37 -0700270static void fill_static_options(ResultsWriter* log) {
271#if defined(SK_BUILD_FOR_WIN32)
272 log->option("system", "WIN32");
273#elif defined(SK_BUILD_FOR_MAC)
274 log->option("system", "MAC");
275#elif defined(SK_BUILD_FOR_ANDROID)
276 log->option("system", "ANDROID");
277#elif defined(SK_BUILD_FOR_UNIX)
278 log->option("system", "UNIX");
279#else
280 log->option("system", "other");
281#endif
mtklein60317d0f2014-07-14 11:30:37 -0700282}
283
jcgregoriobf5e5232014-07-17 13:14:16 -0700284#if SK_SUPPORT_GPU
285static void fill_gpu_options(ResultsWriter* log, SkGLContextHelper* ctx) {
286 const GLubyte* version;
287 SK_GL_RET(*ctx, version, GetString(GR_GL_VERSION));
288 log->configOption("GL_VERSION", (const char*)(version));
289
290 SK_GL_RET(*ctx, version, GetString(GR_GL_RENDERER));
291 log->configOption("GL_RENDERER", (const char*) version);
292
293 SK_GL_RET(*ctx, version, GetString(GR_GL_VENDOR));
294 log->configOption("GL_VENDOR", (const char*) version);
295
296 SK_GL_RET(*ctx, version, GetString(GR_GL_SHADING_LANGUAGE_VERSION));
297 log->configOption("GL_SHADING_LANGUAGE_VERSION", (const char*) version);
298}
299#endif
300
mtkleinf3723212014-06-25 14:08:00 -0700301int tool_main(int argc, char** argv);
302int tool_main(int argc, char** argv) {
303 SetupCrashHandler();
304 SkAutoGraphics ag;
305 SkCommandLineFlags::Parse(argc, argv);
306
mtkleina189ccd2014-07-14 12:28:47 -0700307 if (FLAGS_runOnce) {
308 FLAGS_samples = 1;
309 FLAGS_gpuFrameLag = 0;
310 }
311
mtklein60317d0f2014-07-14 11:30:37 -0700312 MultiResultsWriter log;
jcgregoriobf5e5232014-07-17 13:14:16 -0700313 SkAutoTDelete<NanoJSONResultsWriter> json;
mtklein60317d0f2014-07-14 11:30:37 -0700314 if (!FLAGS_outResultsFile.isEmpty()) {
jcgregoriobf5e5232014-07-17 13:14:16 -0700315 const char* gitHash = FLAGS_gitHash.isEmpty() ? "unknown-revision" : FLAGS_gitHash[0];
316 json.reset(SkNEW(NanoJSONResultsWriter(FLAGS_outResultsFile[0], gitHash)));
mtklein60317d0f2014-07-14 11:30:37 -0700317 log.add(json.get());
318 }
319 CallEnd<MultiResultsWriter> ender(log);
jcgregoriobf5e5232014-07-17 13:14:16 -0700320
321 if (1 == FLAGS_key.count() % 2) {
322 SkDebugf("ERROR: --key must be passed with an even number of arguments.\n");
323 return 1;
324 }
325 for (int i = 1; i < FLAGS_key.count(); i += 2) {
326 log.key(FLAGS_key[i-1], FLAGS_key[i]);
327 }
mtklein60317d0f2014-07-14 11:30:37 -0700328 fill_static_options(&log);
329
mtkleinf3723212014-06-25 14:08:00 -0700330 const double overhead = estimate_timer_overhead();
mtklein55b0ffc2014-07-17 08:38:23 -0700331 SkDebugf("Timer overhead: %s\n", HUMANIZE(overhead));
Mike Klein91294772014-07-16 19:59:32 -0400332
mtkleinbb6a0282014-07-01 08:43:42 -0700333 SkAutoTMalloc<double> samples(FLAGS_samples);
334
mtkleina189ccd2014-07-14 12:28:47 -0700335 if (FLAGS_runOnce) {
336 SkDebugf("--runOnce is true; times would only be misleading so we won't print them.\n");
337 } else if (FLAGS_verbose) {
mtkleinf3723212014-06-25 14:08:00 -0700338 // No header.
339 } else if (FLAGS_quiet) {
mtklein40b32be2014-07-09 08:46:49 -0700340 SkDebugf("median\tbench\tconfig\n");
mtkleinf3723212014-06-25 14:08:00 -0700341 } else {
mtklein5d9d10e2014-07-11 11:57:07 -0700342 SkDebugf("loops\tmin\tmedian\tmean\tmax\tstddev\tsamples\tconfig\tbench\n");
mtkleinf3723212014-06-25 14:08:00 -0700343 }
344
345 for (const BenchRegistry* r = BenchRegistry::Head(); r != NULL; r = r->next()) {
346 SkAutoTDelete<Benchmark> bench(r->factory()(NULL));
347 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getName())) {
348 continue;
349 }
350
mtkleinbb6a0282014-07-01 08:43:42 -0700351 SkTDArray<Target*> targets;
352 create_targets(bench.get(), &targets);
mtkleinf3723212014-06-25 14:08:00 -0700353
jcgregoriobf5e5232014-07-17 13:14:16 -0700354 if (!targets.isEmpty()) {
355 log.bench(bench->getName(), bench->getSize().fX, bench->getSize().fY);
356 bench->preDraw();
357 }
mtkleinbb6a0282014-07-01 08:43:42 -0700358 for (int j = 0; j < targets.count(); j++) {
359 SkCanvas* canvas = targets[j]->surface.get() ? targets[j]->surface->getCanvas() : NULL;
Mike Kleine3631362014-07-15 17:56:37 -0400360 const char* config = targets[j]->config;
mtkleinf3723212014-06-25 14:08:00 -0700361
mtkleinbb6a0282014-07-01 08:43:42 -0700362 const int loops =
363#if SK_SUPPORT_GPU
364 Benchmark::kGPU_Backend == targets[j]->backend
365 ? gpu_bench(targets[j]->gl, bench.get(), canvas, samples.get())
366 :
367#endif
368 cpu_bench( overhead, bench.get(), canvas, samples.get());
mtkleinf3723212014-06-25 14:08:00 -0700369
Mike Kleine3631362014-07-15 17:56:37 -0400370 if (loops == 0) {
mtklein04d53a52014-07-17 11:23:07 -0700371 SkDebugf("Unable to time %s\t%s (overhead %s)\n",
372 bench->getName(), config, HUMANIZE(overhead));
Mike Kleine3631362014-07-15 17:56:37 -0400373 continue;
374 }
375
mtkleinf3723212014-06-25 14:08:00 -0700376 Stats stats(samples.get(), FLAGS_samples);
mtklein60317d0f2014-07-14 11:30:37 -0700377 log.config(config);
jcgregoriobf5e5232014-07-17 13:14:16 -0700378#if SK_SUPPORT_GPU
379 if (Benchmark::kGPU_Backend == targets[j]->backend) {
380 fill_gpu_options(&log, targets[j]->gl);
381 }
382#endif
mtklein60317d0f2014-07-14 11:30:37 -0700383 log.timer("min_ms", stats.min);
384 log.timer("median_ms", stats.median);
385 log.timer("mean_ms", stats.mean);
386 log.timer("max_ms", stats.max);
387 log.timer("stddev_ms", sqrt(stats.var));
388
mtkleina189ccd2014-07-14 12:28:47 -0700389 if (FLAGS_runOnce) {
390 if (targets.count() == 1) {
391 config = ""; // Only print the config if we run the same bench on more than one.
392 }
393 SkDebugf("%s\t%s\n", bench->getName(), config);
394 } else if (FLAGS_verbose) {
mtkleinf3723212014-06-25 14:08:00 -0700395 for (int i = 0; i < FLAGS_samples; i++) {
mtklein55b0ffc2014-07-17 08:38:23 -0700396 SkDebugf("%s ", HUMANIZE(samples[i]));
mtkleinf3723212014-06-25 14:08:00 -0700397 }
398 SkDebugf("%s\n", bench->getName());
399 } else if (FLAGS_quiet) {
mtkleinbb6a0282014-07-01 08:43:42 -0700400 if (targets.count() == 1) {
mtkleinf3723212014-06-25 14:08:00 -0700401 config = ""; // Only print the config if we run the same bench on more than one.
402 }
mtklein55b0ffc2014-07-17 08:38:23 -0700403 SkDebugf("%s\t%s\t%s\n", HUMANIZE(stats.median), bench->getName(), config);
mtkleinf3723212014-06-25 14:08:00 -0700404 } else {
405 const double stddev_percent = 100 * sqrt(stats.var) / stats.mean;
mtklein5d9d10e2014-07-11 11:57:07 -0700406 SkDebugf("%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n"
mtkleinf3723212014-06-25 14:08:00 -0700407 , loops
mtklein55b0ffc2014-07-17 08:38:23 -0700408 , HUMANIZE(stats.min)
409 , HUMANIZE(stats.median)
410 , HUMANIZE(stats.mean)
411 , HUMANIZE(stats.max)
mtkleinf3723212014-06-25 14:08:00 -0700412 , stddev_percent
mtklein5d9d10e2014-07-11 11:57:07 -0700413 , stats.plot.c_str()
mtkleinf3723212014-06-25 14:08:00 -0700414 , config
mtkleinbb6a0282014-07-01 08:43:42 -0700415 , bench->getName()
mtkleinf3723212014-06-25 14:08:00 -0700416 );
417 }
418 }
mtkleinbb6a0282014-07-01 08:43:42 -0700419 targets.deleteAll();
Mike Klein3944a1d2014-07-15 13:40:19 -0400420
421 #if SK_SUPPORT_GPU
422 if (FLAGS_resetGpuContext) {
423 gGrFactory.destroyContexts();
424 }
425 #endif
mtkleinf3723212014-06-25 14:08:00 -0700426 }
427
428 return 0;
429}
430
431#if !defined SK_BUILD_FOR_IOS
432int main(int argc, char * const argv[]) {
433 return tool_main(argc, (char**) argv);
434}
435#endif