blob: 80aed1c280c12e661d5777c2535142fc0d17fb55 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2011 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 */
bsalomon@google.com971d0c82011-08-19 17:22:05 +00007
mtklein@google.comc2897432013-09-10 19:23:38 +00008#include "BenchTimer.h"
commit-bot@chromium.orge3bb3bc2013-12-03 18:16:48 +00009#include "ResultsWriter.h"
scroggo@google.com9a412522012-09-07 15:21:18 +000010#include "SkBenchLogger.h"
bsalomon@google.com971d0c82011-08-19 17:22:05 +000011#include "SkBenchmark.h"
robertphillips@google.com73672252013-08-29 12:40:26 +000012#include "SkBitmapDevice.h"
reed@android.combd700c32009-01-05 03:34:50 +000013#include "SkCanvas.h"
mtklein@google.com78d03792013-09-10 19:42:07 +000014#include "SkColorPriv.h"
sglez@google.com586db932013-07-24 17:24:23 +000015#include "SkCommandLineFlags.h"
bsalomon@google.com82a7bfc2012-04-16 19:11:17 +000016#include "SkDeferredCanvas.h"
commit-bot@chromium.org6adce672014-02-03 14:48:17 +000017#include "SkGMBench.h"
reed@android.com3a859a02009-01-28 00:56:29 +000018#include "SkGraphics.h"
reed@android.comb398fe82009-01-07 11:47:57 +000019#include "SkImageEncoder.h"
mtklein@google.comc2897432013-09-10 19:23:38 +000020#include "SkOSFile.h"
reed@android.com6c924ad2009-03-31 03:48:49 +000021#include "SkPicture.h"
reed@android.combd700c32009-01-05 03:34:50 +000022#include "SkString.h"
reed@android.com29348cb2009-08-04 18:17:15 +000023
djsollen@google.comdb490e92013-11-20 13:15:40 +000024#if SK_SUPPORT_GPU
25#include "GrContext.h"
26#include "GrContextFactory.h"
27#include "GrRenderTarget.h"
28#include "SkGpuDevice.h"
29#include "gl/GrGLDefines.h"
30#else
31class GrContext;
32#endif // SK_SUPPORT_GPU
33
mtklein@google.com9ef1d212013-09-13 20:39:50 +000034#include <limits>
35
commit-bot@chromium.org6adce672014-02-03 14:48:17 +000036// Note that ~SkTDArray is not virtual. This inherits privately to bar using this as a SkTDArray*.
37class RefCntArray : private SkTDArray<SkRefCnt*> {
38public:
39 SkRefCnt** append() { return this->INHERITED::append(); }
40 ~RefCntArray() { this->unrefAll(); }
41private:
42 typedef SkTDArray<SkRefCnt*> INHERITED;
43};
44
45class GMBenchFactory : public SkBenchmarkFactory {
46public:
47 GMBenchFactory(const skiagm::GMRegistry* gmreg)
48 : fGMFactory(gmreg->factory()) {
49 fSelfRegistry = SkNEW_ARGS(BenchRegistry, (this));
50 }
51
52 virtual ~GMBenchFactory() { SkDELETE(fSelfRegistry); }
53
54 virtual SkBenchmark* operator()() const SK_OVERRIDE {
55 skiagm::GM* gm = fGMFactory(NULL);
56 return SkNEW_ARGS(SkGMBench, (gm));
57 }
58
59private:
60 skiagm::GMRegistry::Factory fGMFactory;
61 BenchRegistry* fSelfRegistry;
62};
63
64static void register_gm_benches() {
65 static bool gOnce;
66 static RefCntArray gGMBenchFactories;
67
68 if (!gOnce) {
69 const skiagm::GMRegistry* gmreg = skiagm::GMRegistry::Head();
70 while (gmreg) {
71 skiagm::GM* gm = gmreg->factory()(NULL);
72 if (NULL != gm && skiagm::GM::kAsBench_Flag & gm->getFlags()) {
73 *gGMBenchFactories.append() = SkNEW_ARGS(GMBenchFactory, (gmreg));
74 }
75 SkDELETE(gm);
76 gmreg = gmreg->next();
77 }
78 gOnce = true;
79 }
80}
81
mtklein@google.comc2897432013-09-10 19:23:38 +000082enum BenchMode {
83 kNormal_BenchMode,
84 kDeferred_BenchMode,
85 kDeferredSilent_BenchMode,
86 kRecord_BenchMode,
87 kPictureRecord_BenchMode
keyar@chromium.orgfdd909c2012-07-20 23:03:42 +000088};
mtklein@google.comdbd41c82013-09-13 20:11:09 +000089const char* BenchMode_Name[] = {
90 "normal", "deferred", "deferredSilent", "record", "picturerecord"
91};
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +000092
borenet@google.com4e061d32013-09-16 19:12:19 +000093static const char kDefaultsConfigStr[] = "defaults";
94
keyar@chromium.orgfdd909c2012-07-20 23:03:42 +000095///////////////////////////////////////////////////////////////////////////////
96
reed@android.com6c924ad2009-03-31 03:48:49 +000097static void erase(SkBitmap& bm) {
98 if (bm.config() == SkBitmap::kA8_Config) {
junov@google.comdbfac8a2012-12-06 21:47:40 +000099 bm.eraseColor(SK_ColorTRANSPARENT);
reed@android.com6c924ad2009-03-31 03:48:49 +0000100 } else {
101 bm.eraseColor(SK_ColorWHITE);
102 }
103}
104
reed@android.combd700c32009-01-05 03:34:50 +0000105class Iter {
106public:
mtklein@google.comc2897432013-09-10 19:23:38 +0000107 Iter() : fBench(BenchRegistry::Head()) {}
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000108
reed@android.combd700c32009-01-05 03:34:50 +0000109 SkBenchmark* next() {
110 if (fBench) {
111 BenchRegistry::Factory f = fBench->factory();
112 fBench = fBench->next();
commit-bot@chromium.org6adce672014-02-03 14:48:17 +0000113 return (*f)();
reed@android.combd700c32009-01-05 03:34:50 +0000114 }
115 return NULL;
116 }
bungeman@google.comd1a416a2011-05-18 18:37:07 +0000117
reed@android.combd700c32009-01-05 03:34:50 +0000118private:
119 const BenchRegistry* fBench;
120};
121
bsalomon@google.com30e6d2c2012-08-13 14:03:31 +0000122class AutoPrePostDraw {
123public:
124 AutoPrePostDraw(SkBenchmark* bench) : fBench(bench) {
125 fBench->preDraw();
126 }
127 ~AutoPrePostDraw() {
128 fBench->postDraw();
129 }
130private:
131 SkBenchmark* fBench;
132};
133
reed@android.combd700c32009-01-05 03:34:50 +0000134static void make_filename(const char name[], SkString* path) {
135 path->set(name);
136 for (int i = 0; name[i]; i++) {
137 switch (name[i]) {
138 case '/':
139 case '\\':
140 case ' ':
141 case ':':
142 path->writable_str()[i] = '-';
143 break;
144 default:
145 break;
146 }
147 }
148}
149
reed@android.com4c7d3d62009-01-21 03:15:13 +0000150static void saveFile(const char name[], const char config[], const char dir[],
151 const SkBitmap& bm) {
reed@android.com4c7d3d62009-01-21 03:15:13 +0000152 SkBitmap copy;
153 if (!bm.copyTo(&copy, SkBitmap::kARGB_8888_Config)) {
154 return;
155 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000156
reed@android.comf523e252009-01-26 23:15:37 +0000157 if (bm.config() == SkBitmap::kA8_Config) {
158 // turn alpha into gray-scale
159 size_t size = copy.getSize() >> 2;
160 SkPMColor* p = copy.getAddr32(0, 0);
161 for (size_t i = 0; i < size; i++) {
162 int c = (*p >> SK_A32_SHIFT) & 0xFF;
163 c = 255 - c;
164 c |= (c << 24) | (c << 16) | (c << 8);
165 *p++ = c | (SK_A32_MASK << SK_A32_SHIFT);
166 }
167 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000168
mtklein@google.comc2897432013-09-10 19:23:38 +0000169 SkString filename;
170 make_filename(name, &filename);
171 filename.appendf("_%s.png", config);
172 SkString path = SkOSPath::SkPathJoin(dir, filename.c_str());
173 ::remove(path.c_str());
174 SkImageEncoder::EncodeFile(path.c_str(), copy, SkImageEncoder::kPNG_Type, 100);
reed@android.com4c7d3d62009-01-21 03:15:13 +0000175}
176
177static void performClip(SkCanvas* canvas, int w, int h) {
178 SkRect r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000179
reed@android.com4c7d3d62009-01-21 03:15:13 +0000180 r.set(SkIntToScalar(10), SkIntToScalar(10),
181 SkIntToScalar(w*2/3), SkIntToScalar(h*2/3));
182 canvas->clipRect(r, SkRegion::kIntersect_Op);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000183
reed@android.com4c7d3d62009-01-21 03:15:13 +0000184 r.set(SkIntToScalar(w/3), SkIntToScalar(h/3),
185 SkIntToScalar(w-10), SkIntToScalar(h-10));
186 canvas->clipRect(r, SkRegion::kXOR_Op);
187}
188
189static void performRotate(SkCanvas* canvas, int w, int h) {
190 const SkScalar x = SkIntToScalar(w) / 2;
191 const SkScalar y = SkIntToScalar(h) / 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000192
reed@android.com4c7d3d62009-01-21 03:15:13 +0000193 canvas->translate(x, y);
194 canvas->rotate(SkIntToScalar(35));
195 canvas->translate(-x, -y);
196}
197
reed@android.com387359e2009-08-04 01:51:09 +0000198static void performScale(SkCanvas* canvas, int w, int h) {
199 const SkScalar x = SkIntToScalar(w) / 2;
200 const SkScalar y = SkIntToScalar(h) / 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000201
reed@android.com387359e2009-08-04 01:51:09 +0000202 canvas->translate(x, y);
203 // just enough so we can't take the sprite case
204 canvas->scale(SK_Scalar1 * 99/100, SK_Scalar1 * 99/100);
205 canvas->translate(-x, -y);
206}
207
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000208static SkBaseDevice* make_device(SkBitmap::Config config, const SkIPoint& size,
commit-bot@chromium.org644629c2013-11-21 06:21:58 +0000209 SkBenchmark::Backend backend, int sampleCount, GrContext* context) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000210 SkBaseDevice* device = NULL;
mike@reedtribe.orga9015f82011-05-17 02:25:05 +0000211 SkBitmap bitmap;
212 bitmap.setConfig(config, size.fX, size.fY);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000213
mike@reedtribe.orga9015f82011-05-17 02:25:05 +0000214 switch (backend) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +0000215 case SkBenchmark::kRaster_Backend:
mike@reedtribe.orga9015f82011-05-17 02:25:05 +0000216 bitmap.allocPixels();
217 erase(bitmap);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000218 device = SkNEW_ARGS(SkBitmapDevice, (bitmap));
mike@reedtribe.orga9015f82011-05-17 02:25:05 +0000219 break;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000220#if SK_SUPPORT_GPU
commit-bot@chromium.org644629c2013-11-21 06:21:58 +0000221 case SkBenchmark::kGPU_Backend: {
bsalomon@google.comcb265352013-02-22 16:13:16 +0000222 GrTextureDesc desc;
223 desc.fConfig = kSkia8888_GrPixelConfig;
224 desc.fFlags = kRenderTarget_GrTextureFlagBit;
225 desc.fWidth = size.fX;
226 desc.fHeight = size.fY;
bsalomon@google.com8a70eef2013-03-19 13:58:55 +0000227 desc.fSampleCnt = sampleCount;
bsalomon@google.comcb265352013-02-22 16:13:16 +0000228 SkAutoTUnref<GrTexture> texture(context->createUncachedTexture(desc, NULL, 0));
229 if (!texture) {
230 return NULL;
231 }
232 device = SkNEW_ARGS(SkGpuDevice, (context, texture.get()));
mike@reedtribe.orga9015f82011-05-17 02:25:05 +0000233 break;
bsalomon@google.comcb265352013-02-22 16:13:16 +0000234 }
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000235#endif
commit-bot@chromium.org644629c2013-11-21 06:21:58 +0000236 case SkBenchmark::kPDF_Backend:
mike@reedtribe.orga9015f82011-05-17 02:25:05 +0000237 default:
mtklein@google.com330313a2013-08-22 15:37:26 +0000238 SkDEBUGFAIL("unsupported");
mike@reedtribe.orga9015f82011-05-17 02:25:05 +0000239 }
240 return device;
241}
242
bsalomon@google.comcb265352013-02-22 16:13:16 +0000243#if SK_SUPPORT_GPU
244GrContextFactory gContextFactory;
245typedef GrContextFactory::GLContextType GLContextType;
mtklein@google.comc2897432013-09-10 19:23:38 +0000246static const GLContextType kNative = GrContextFactory::kNative_GLContextType;
commit-bot@chromium.orge8989572014-01-26 20:51:00 +0000247static const GLContextType kNVPR = GrContextFactory::kNVPR_GLContextType;
mtklein@google.comc2897432013-09-10 19:23:38 +0000248#if SK_ANGLE
249static const GLContextType kANGLE = GrContextFactory::kANGLE_GLContextType;
mtklein@google.comc2897432013-09-10 19:23:38 +0000250#endif
251static const GLContextType kDebug = GrContextFactory::kDebug_GLContextType;
252static const GLContextType kNull = GrContextFactory::kNull_GLContextType;
bsalomon@google.comcb265352013-02-22 16:13:16 +0000253#else
254typedef int GLContextType;
mtklein@google.comc2897432013-09-10 19:23:38 +0000255static const GLContextType kNative = 0, kANGLE = 0, kDebug = 0, kNull = 0;
bsalomon@google.comcb265352013-02-22 16:13:16 +0000256#endif
257
mtklein@google.comc2897432013-09-10 19:23:38 +0000258#ifdef SK_DEBUG
259static const bool kIsDebug = true;
260#else
261static const bool kIsDebug = false;
262#endif
263
264static const struct Config {
265 SkBitmap::Config config;
266 const char* name;
267 int sampleCount;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +0000268 SkBenchmark::Backend backend;
mtklein@google.comc2897432013-09-10 19:23:38 +0000269 GLContextType contextType;
270 bool runByDefault;
reed@android.com4bc19832009-01-19 20:08:35 +0000271} gConfigs[] = {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +0000272 { SkBitmap::kNo_Config, "NONRENDERING", 0, SkBenchmark::kNonRendering_Backend, kNative, true},
273 { SkBitmap::kARGB_8888_Config, "8888", 0, SkBenchmark::kRaster_Backend, kNative, true},
274 { SkBitmap::kRGB_565_Config, "565", 0, SkBenchmark::kRaster_Backend, kNative, true},
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000275#if SK_SUPPORT_GPU
commit-bot@chromium.org644629c2013-11-21 06:21:58 +0000276 { SkBitmap::kARGB_8888_Config, "GPU", 0, SkBenchmark::kGPU_Backend, kNative, true},
277 { SkBitmap::kARGB_8888_Config, "MSAA4", 4, SkBenchmark::kGPU_Backend, kNative, false},
278 { SkBitmap::kARGB_8888_Config, "MSAA16", 16, SkBenchmark::kGPU_Backend, kNative, false},
commit-bot@chromium.orge8989572014-01-26 20:51:00 +0000279 { SkBitmap::kARGB_8888_Config, "NVPRMSAA4", 4, SkBenchmark::kGPU_Backend, kNVPR, true},
280 { SkBitmap::kARGB_8888_Config, "NVPRMSAA16", 16, SkBenchmark::kGPU_Backend, kNVPR, false},
robertphillips@google.comd3b9fbb2012-03-28 16:19:11 +0000281#if SK_ANGLE
commit-bot@chromium.org644629c2013-11-21 06:21:58 +0000282 { SkBitmap::kARGB_8888_Config, "ANGLE", 0, SkBenchmark::kGPU_Backend, kANGLE, true},
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000283#endif // SK_ANGLE
commit-bot@chromium.org644629c2013-11-21 06:21:58 +0000284 { SkBitmap::kARGB_8888_Config, "Debug", 0, SkBenchmark::kGPU_Backend, kDebug, kIsDebug},
285 { SkBitmap::kARGB_8888_Config, "NULLGPU", 0, SkBenchmark::kGPU_Backend, kNull, true},
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000286#endif // SK_SUPPORT_GPU
reed@android.com4bc19832009-01-19 20:08:35 +0000287};
288
mtklein@google.comc2897432013-09-10 19:23:38 +0000289DEFINE_string(outDir, "", "If given, image of each bench will be put in outDir.");
290DEFINE_string(timers, "cg", "Timers to display. "
291 "Options: w(all) W(all, truncated) c(pu) C(pu, truncated) g(pu)");
reed@android.com4c7d3d62009-01-21 03:15:13 +0000292
mtklein@google.comc2897432013-09-10 19:23:38 +0000293DEFINE_bool(rotate, false, "Rotate canvas before bench run?");
294DEFINE_bool(scale, false, "Scale canvas before bench run?");
295DEFINE_bool(clip, false, "Clip canvas before bench run?");
bsalomon@google.com8a70eef2013-03-19 13:58:55 +0000296
mtklein@google.comc2897432013-09-10 19:23:38 +0000297DEFINE_bool(forceAA, true, "Force anti-aliasing?");
298DEFINE_bool(forceFilter, false, "Force bitmap filtering?");
299DEFINE_string(forceDither, "default", "Force dithering: true, false, or default?");
300DEFINE_bool(forceBlend, false, "Force alpha blending?");
301
302DEFINE_int32(gpuCacheBytes, -1, "GPU cache size limit in bytes. 0 to disable cache.");
303DEFINE_int32(gpuCacheCount, -1, "GPU cache size limit in object count. 0 to disable cache.");
304
commit-bot@chromium.org6dda8272014-01-23 17:21:19 +0000305DEFINE_bool2(leaks, l, false, "show leaked ref cnt'd objects.");
mtklein@google.comc2897432013-09-10 19:23:38 +0000306DEFINE_string(match, "", "[~][^]substring[$] [...] of test name to run.\n"
307 "Multiple matches may be separated by spaces.\n"
308 "~ causes a matching test to always be skipped\n"
309 "^ requires the start of the test to match\n"
310 "$ requires the end of the test to match\n"
311 "^ and $ requires an exact match\n"
312 "If a test does not match any list entry,\n"
313 "it is skipped unless some list entry starts with ~\n");
314DEFINE_string(mode, "normal",
315 "normal: draw to a normal canvas;\n"
316 "deferred: draw to a deferred canvas;\n"
317 "deferredSilent: deferred with silent playback;\n"
318 "record: draw to an SkPicture;\n"
319 "picturerecord: draw from an SkPicture to an SkPicture.\n");
borenet@google.com4e061d32013-09-16 19:12:19 +0000320DEFINE_string(config, kDefaultsConfigStr,
321 "Run configs given. By default, runs the configs marked \"runByDefault\" in gConfigs.");
mtklein@google.comc2897432013-09-10 19:23:38 +0000322DEFINE_string(logFile, "", "Also write stdout here.");
mtklein@google.comdbd41c82013-09-13 20:11:09 +0000323DEFINE_int32(minMs, 20, "Shortest time we'll allow a benchmark to run.");
324DEFINE_int32(maxMs, 4000, "Longest time we'll allow a benchmark to run.");
325DEFINE_double(error, 0.01,
326 "Ratio of subsequent bench measurements must drop within 1±error to converge.");
mtklein@google.comc2897432013-09-10 19:23:38 +0000327DEFINE_string(timeFormat, "%9.2f", "Format to print results, in milliseconds per 1000 loops.");
mtklein@google.comdbd41c82013-09-13 20:11:09 +0000328DEFINE_bool2(verbose, v, false, "Print more.");
tfarina@chromium.org725a64c2013-12-31 14:29:52 +0000329DEFINE_string2(resourcePath, i, "resources", "directory for test resources.");
commit-bot@chromium.orge3bb3bc2013-12-03 18:16:48 +0000330DEFINE_string(outResultsFile, "", "If given, the results will be written to the file in JSON format.");
mtklein@google.comdbd41c82013-09-13 20:11:09 +0000331
332// Has this bench converged? First arguments are milliseconds / loop iteration,
333// last is overall runtime in milliseconds.
334static bool HasConverged(double prevPerLoop, double currPerLoop, double currRaw) {
335 if (currRaw < FLAGS_minMs) {
336 return false;
337 }
338 const double low = 1 - FLAGS_error, high = 1 + FLAGS_error;
339 const double ratio = currPerLoop / prevPerLoop;
340 return low < ratio && ratio < high;
341}
tomhudson@google.com86bb9b72012-04-03 13:28:57 +0000342
caryclark@google.com5987f582012-10-02 18:33:14 +0000343int tool_main(int argc, char** argv);
344int tool_main(int argc, char** argv) {
commit-bot@chromium.org6adce672014-02-03 14:48:17 +0000345 register_gm_benches();
commit-bot@chromium.org6dda8272014-01-23 17:21:19 +0000346 SkCommandLineFlags::Parse(argc, argv);
bsalomon@google.com4e230682013-01-15 20:37:04 +0000347#if SK_ENABLE_INST_COUNT
commit-bot@chromium.org6dda8272014-01-23 17:21:19 +0000348 if (FLAGS_leaks) {
349 gPrintInstCount = true;
350 }
bsalomon@google.com65a87cc2012-08-14 13:15:44 +0000351#endif
reed@android.com3a859a02009-01-28 00:56:29 +0000352 SkAutoGraphics ag;
bsalomon@google.com65a87cc2012-08-14 13:15:44 +0000353
mtklein@google.comc2897432013-09-10 19:23:38 +0000354 // First, parse some flags.
scroggo@google.com9a412522012-09-07 15:21:18 +0000355 SkBenchLogger logger;
mtklein@google.comc2897432013-09-10 19:23:38 +0000356 if (FLAGS_logFile.count()) {
357 logger.SetLogFile(FLAGS_logFile[0]);
358 }
scroggo@google.com9a412522012-09-07 15:21:18 +0000359
commit-bot@chromium.orge3bb3bc2013-12-03 18:16:48 +0000360 LoggerResultsWriter logWriter(logger, FLAGS_timeFormat[0]);
361 MultiResultsWriter writer;
362 writer.add(&logWriter);
363 SkAutoTDelete<JSONResultsWriter> jsonWriter;
364 if (FLAGS_outResultsFile.count()) {
365 jsonWriter.reset(SkNEW(JSONResultsWriter(FLAGS_outResultsFile[0])));
366 writer.add(jsonWriter.get());
367 }
368 // Instantiate after all the writers have been added to writer so that we
369 // call close() before their destructors are called on the way out.
370 CallEnd<MultiResultsWriter> ender(writer);
371
mtklein@google.comc2897432013-09-10 19:23:38 +0000372 const uint8_t alpha = FLAGS_forceBlend ? 0x80 : 0xFF;
373 SkTriState::State dither = SkTriState::kDefault;
374 for (size_t i = 0; i < 3; i++) {
375 if (strcmp(SkTriState::Name[i], FLAGS_forceDither[0]) == 0) {
376 dither = static_cast<SkTriState::State>(i);
reed@android.comb398fe82009-01-07 11:47:57 +0000377 }
378 }
mtklein@google.comc2897432013-09-10 19:23:38 +0000379
380 BenchMode benchMode = kNormal_BenchMode;
381 for (size_t i = 0; i < SK_ARRAY_COUNT(BenchMode_Name); i++) {
382 if (strcmp(FLAGS_mode[0], BenchMode_Name[i]) == 0) {
383 benchMode = static_cast<BenchMode>(i);
384 }
keyar@chromium.orgfdd909c2012-07-20 23:03:42 +0000385 }
mtklein@google.comc2897432013-09-10 19:23:38 +0000386
387 SkTDArray<int> configs;
borenet@google.com4e061d32013-09-16 19:12:19 +0000388 bool runDefaultConfigs = false;
mtklein@google.comc2897432013-09-10 19:23:38 +0000389 // Try user-given configs first.
390 for (int i = 0; i < FLAGS_config.count(); i++) {
robertphillips@google.come9cd27d2013-10-16 17:48:11 +0000391 for (int j = 0; j < static_cast<int>(SK_ARRAY_COUNT(gConfigs)); ++j) {
mtklein@google.comc2897432013-09-10 19:23:38 +0000392 if (0 == strcmp(FLAGS_config[i], gConfigs[j].name)) {
393 *configs.append() = j;
borenet@google.com4e061d32013-09-16 19:12:19 +0000394 } else if (0 == strcmp(FLAGS_config[i], kDefaultsConfigStr)) {
395 runDefaultConfigs = true;
mtklein@google.comc2897432013-09-10 19:23:38 +0000396 }
397 }
keyar@chromium.orgfdd909c2012-07-20 23:03:42 +0000398 }
mtklein@google.comc2897432013-09-10 19:23:38 +0000399 // If there weren't any, fill in with defaults.
borenet@google.com4e061d32013-09-16 19:12:19 +0000400 if (runDefaultConfigs) {
robertphillips@google.come9cd27d2013-10-16 17:48:11 +0000401 for (int i = 0; i < static_cast<int>(SK_ARRAY_COUNT(gConfigs)); ++i) {
mtklein@google.comc2897432013-09-10 19:23:38 +0000402 if (gConfigs[i].runByDefault) {
bsalomon@google.com8a70eef2013-03-19 13:58:55 +0000403 *configs.append() = i;
404 }
tomhudson@google.com13eaaaa2012-04-16 18:00:40 +0000405 }
406 }
mtklein@google.comc2897432013-09-10 19:23:38 +0000407 // Filter out things we can't run.
408 if (kNormal_BenchMode != benchMode) {
bsalomon@google.com604a56a2013-03-15 15:42:15 +0000409 // Non-rendering configs only run in normal mode
410 for (int i = 0; i < configs.count(); ++i) {
mtklein@google.comc2897432013-09-10 19:23:38 +0000411 const Config& config = gConfigs[configs[i]];
commit-bot@chromium.org644629c2013-11-21 06:21:58 +0000412 if (SkBenchmark::kNonRendering_Backend == config.backend) {
bsalomon@google.com604a56a2013-03-15 15:42:15 +0000413 configs.remove(i, 1);
414 --i;
415 }
416 }
417 }
scroggo@google.com111fd112013-09-25 21:42:12 +0000418 // Set the resource path.
419 if (!FLAGS_resourcePath.isEmpty()) {
420 SkBenchmark::SetResourcePath(FLAGS_resourcePath[0]);
421 }
422
bsalomon@google.com8a70eef2013-03-19 13:58:55 +0000423#if SK_SUPPORT_GPU
424 for (int i = 0; i < configs.count(); ++i) {
mtklein@google.comc2897432013-09-10 19:23:38 +0000425 const Config& config = gConfigs[configs[i]];
bsalomon@google.com8a70eef2013-03-19 13:58:55 +0000426
commit-bot@chromium.org644629c2013-11-21 06:21:58 +0000427 if (SkBenchmark::kGPU_Backend == config.backend) {
mtklein@google.comc2897432013-09-10 19:23:38 +0000428 GrContext* context = gContextFactory.get(config.contextType);
bsalomon@google.com8a70eef2013-03-19 13:58:55 +0000429 if (NULL == context) {
mtklein@google.comdbd41c82013-09-13 20:11:09 +0000430 logger.logError(SkStringPrintf(
431 "Error creating GrContext for config %s. Config will be skipped.\n",
432 config.name));
bsalomon@google.com8a70eef2013-03-19 13:58:55 +0000433 configs.remove(i);
434 --i;
435 continue;
436 }
mtklein@google.comc2897432013-09-10 19:23:38 +0000437 if (config.sampleCount > context->getMaxSampleCount()){
mtklein@google.comdbd41c82013-09-13 20:11:09 +0000438 logger.logError(SkStringPrintf(
439 "Sample count (%d) for config %s is unsupported. Config will be skipped.\n",
440 config.sampleCount, config.name));
bsalomon@google.com8a70eef2013-03-19 13:58:55 +0000441 configs.remove(i);
442 --i;
443 continue;
444 }
445 }
446 }
447#endif
448
mtklein@google.comc2897432013-09-10 19:23:38 +0000449 // All flags should be parsed now. Report our settings.
450 if (kIsDebug) {
451 logger.logError("bench was built in Debug mode, so we're going to hide the times."
452 " It's for your own good!\n");
453 }
commit-bot@chromium.orge3bb3bc2013-12-03 18:16:48 +0000454 writer.option("mode", FLAGS_mode[0]);
455 writer.option("alpha", SkStringPrintf("0x%02X", alpha).c_str());
456 writer.option("antialias", SkStringPrintf("%d", FLAGS_forceAA).c_str());
457 writer.option("filter", SkStringPrintf("%d", FLAGS_forceFilter).c_str());
458 writer.option("dither", SkTriState::Name[dither]);
459
460 writer.option("rotate", SkStringPrintf("%d", FLAGS_rotate).c_str());
461 writer.option("scale", SkStringPrintf("%d", FLAGS_scale).c_str());
462 writer.option("clip", SkStringPrintf("%d", FLAGS_clip).c_str());
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000463
bungeman@google.coma5d48412011-06-15 17:25:46 +0000464#if defined(SK_BUILD_FOR_WIN32)
commit-bot@chromium.orge3bb3bc2013-12-03 18:16:48 +0000465 writer.option("system", "WIN32");
bungeman@google.coma5d48412011-06-15 17:25:46 +0000466#elif defined(SK_BUILD_FOR_MAC)
commit-bot@chromium.orge3bb3bc2013-12-03 18:16:48 +0000467 writer.option("system", "MAC");
bungeman@google.coma5d48412011-06-15 17:25:46 +0000468#elif defined(SK_BUILD_FOR_ANDROID)
commit-bot@chromium.orge3bb3bc2013-12-03 18:16:48 +0000469 writer.option("system", "ANDROID");
bungeman@google.coma5d48412011-06-15 17:25:46 +0000470#elif defined(SK_BUILD_FOR_UNIX)
commit-bot@chromium.orge3bb3bc2013-12-03 18:16:48 +0000471 writer.option("system", "UNIX");
bungeman@google.coma5d48412011-06-15 17:25:46 +0000472#else
commit-bot@chromium.orge3bb3bc2013-12-03 18:16:48 +0000473 writer.option("system", "other");
bungeman@google.coma5d48412011-06-15 17:25:46 +0000474#endif
475
476#if defined(SK_DEBUG)
commit-bot@chromium.orge3bb3bc2013-12-03 18:16:48 +0000477 writer.option("build", "DEBUG");
478#else
479 writer.option("build", "RELEASE");
bungeman@google.coma5d48412011-06-15 17:25:46 +0000480#endif
mtklein@google.comc2897432013-09-10 19:23:38 +0000481
482 // Set texture cache limits if non-default.
bsalomon@google.com5c90e292013-02-22 19:17:13 +0000483 for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); ++i) {
bsalomon@google.comcb265352013-02-22 16:13:16 +0000484#if SK_SUPPORT_GPU
mtklein@google.comc2897432013-09-10 19:23:38 +0000485 const Config& config = gConfigs[i];
commit-bot@chromium.org644629c2013-11-21 06:21:58 +0000486 if (SkBenchmark::kGPU_Backend != config.backend) {
mtklein@google.comc2897432013-09-10 19:23:38 +0000487 continue;
bsalomon@google.comcb265352013-02-22 16:13:16 +0000488 }
mtklein@google.comc2897432013-09-10 19:23:38 +0000489 GrContext* context = gContextFactory.get(config.contextType);
490 if (NULL == context) {
491 continue;
492 }
493
494 size_t bytes;
495 int count;
496 context->getTextureCacheLimits(&count, &bytes);
497 if (-1 != FLAGS_gpuCacheBytes) {
498 bytes = static_cast<size_t>(FLAGS_gpuCacheBytes);
499 }
500 if (-1 != FLAGS_gpuCacheCount) {
501 count = FLAGS_gpuCacheCount;
502 }
503 context->setTextureCacheLimits(count, bytes);
bsalomon@google.comcb265352013-02-22 16:13:16 +0000504#endif
505 }
bsalomon@google.com74913722011-10-27 20:44:19 +0000506
mtklein@google.comc2897432013-09-10 19:23:38 +0000507 // Run each bench in each configuration it supports and we asked for.
508 Iter iter;
commit-bot@chromium.orge3bb3bc2013-12-03 18:16:48 +0000509 SkBenchmark* bench;
reed@android.combd700c32009-01-05 03:34:50 +0000510 while ((bench = iter.next()) != NULL) {
bsalomon@google.com7fbc6042012-08-13 22:10:05 +0000511 SkAutoTUnref<SkBenchmark> benchUnref(bench);
mtklein@google.comc2897432013-09-10 19:23:38 +0000512 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getName())) {
reed@android.comb398fe82009-01-07 11:47:57 +0000513 continue;
514 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000515
mtklein@google.comc2897432013-09-10 19:23:38 +0000516 bench->setForceAlpha(alpha);
517 bench->setForceAA(FLAGS_forceAA);
518 bench->setForceFilter(FLAGS_forceFilter);
519 bench->setDither(dither);
bsalomon@google.com30e6d2c2012-08-13 14:03:31 +0000520 AutoPrePostDraw appd(bench);
521
mtklein@google.comc2897432013-09-10 19:23:38 +0000522 bool loggedBenchName = false;
523 for (int i = 0; i < configs.count(); ++i) {
524 const int configIndex = configs[i];
525 const Config& config = gConfigs[configIndex];
tomhudson@google.com13eaaaa2012-04-16 18:00:40 +0000526
commit-bot@chromium.org644629c2013-11-21 06:21:58 +0000527 if (!bench->isSuitableFor(config.backend)) {
mtklein@google.comc2897432013-09-10 19:23:38 +0000528 continue;
bsalomon@google.com604a56a2013-03-15 15:42:15 +0000529 }
530
bsalomon@google.comcb265352013-02-22 16:13:16 +0000531 GrContext* context = NULL;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000532#if SK_SUPPORT_GPU
sugoi@google.com9c55f802013-03-07 20:52:59 +0000533 SkGLContextHelper* glContext = NULL;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +0000534 if (SkBenchmark::kGPU_Backend == config.backend) {
mtklein@google.comc2897432013-09-10 19:23:38 +0000535 context = gContextFactory.get(config.contextType);
bsalomon@google.comcb265352013-02-22 16:13:16 +0000536 if (NULL == context) {
537 continue;
538 }
mtklein@google.comc2897432013-09-10 19:23:38 +0000539 glContext = gContextFactory.getGLContext(config.contextType);
mike@reedtribe.orga9015f82011-05-17 02:25:05 +0000540 }
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000541#endif
mtklein@google.comc2897432013-09-10 19:23:38 +0000542 SkAutoTUnref<SkBaseDevice> device;
543 SkAutoTUnref<SkCanvas> canvas;
544 SkPicture recordFrom, recordTo;
545 const SkIPoint dim = bench->getSize();
bsalomon@google.com604a56a2013-03-15 15:42:15 +0000546
mtklein@google.comc2897432013-09-10 19:23:38 +0000547 const SkPicture::RecordingFlags kRecordFlags =
548 SkPicture::kUsePathBoundsForClip_RecordingFlag;
549
commit-bot@chromium.org644629c2013-11-21 06:21:58 +0000550 if (SkBenchmark::kNonRendering_Backend != config.backend) {
mtklein@google.comc2897432013-09-10 19:23:38 +0000551 device.reset(make_device(config.config,
552 dim,
553 config.backend,
554 config.sampleCount,
555 context));
556 if (!device.get()) {
mtklein@google.comdbd41c82013-09-13 20:11:09 +0000557 logger.logError(SkStringPrintf(
558 "Device creation failure for config %s. Will skip.\n", config.name));
mtklein@google.comc2897432013-09-10 19:23:38 +0000559 continue;
keyar@chromium.orgfdd909c2012-07-20 23:03:42 +0000560 }
junov@chromium.orgfb103892012-09-20 19:35:43 +0000561
mtklein@google.comc2897432013-09-10 19:23:38 +0000562 switch(benchMode) {
563 case kDeferredSilent_BenchMode:
564 case kDeferred_BenchMode:
565 canvas.reset(SkDeferredCanvas::Create(device.get()));
566 break;
567 case kRecord_BenchMode:
568 canvas.reset(SkRef(recordTo.beginRecording(dim.fX, dim.fY, kRecordFlags)));
569 break;
570 case kPictureRecord_BenchMode:
commit-bot@chromium.org33614712013-12-03 18:17:16 +0000571 bench->draw(1, recordFrom.beginRecording(dim.fX, dim.fY, kRecordFlags));
mtklein@google.comc2897432013-09-10 19:23:38 +0000572 recordFrom.endRecording();
573 canvas.reset(SkRef(recordTo.beginRecording(dim.fX, dim.fY, kRecordFlags)));
574 break;
575 case kNormal_BenchMode:
576 canvas.reset(new SkCanvas(device.get()));
577 break;
578 default:
579 SkASSERT(false);
580 }
581 }
582
583 if (NULL != canvas) {
584 canvas->clear(SK_ColorWHITE);
585 if (FLAGS_clip) { performClip(canvas, dim.fX, dim.fY); }
586 if (FLAGS_scale) { performScale(canvas, dim.fX, dim.fY); }
587 if (FLAGS_rotate) { performRotate(canvas, dim.fX, dim.fY); }
588 }
589
590 if (!loggedBenchName) {
591 loggedBenchName = true;
commit-bot@chromium.orge3bb3bc2013-12-03 18:16:48 +0000592 writer.bench(bench->getName(), dim.fX, dim.fY);
mtklein@google.comc2897432013-09-10 19:23:38 +0000593 }
594
595#if SK_SUPPORT_GPU
596 SkGLContextHelper* contextHelper = NULL;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +0000597 if (SkBenchmark::kGPU_Backend == config.backend) {
mtklein@google.comc2897432013-09-10 19:23:38 +0000598 contextHelper = gContextFactory.getGLContext(config.contextType);
599 }
600 BenchTimer timer(contextHelper);
601#else
602 BenchTimer timer;
603#endif
604
mtklein@google.com9ef1d212013-09-13 20:39:50 +0000605 double previous = std::numeric_limits<double>::infinity();
mtklein@google.comdbd41c82013-09-13 20:11:09 +0000606 bool converged = false;
djsollen@google.com70de4da2013-10-10 17:33:39 +0000607
608 // variables used to compute loopsPerFrame
609 double frameIntervalTime = 0.0f;
610 int frameIntervalTotalLoops = 0;
611
612 bool frameIntervalComputed = false;
613 int loopsPerFrame = 0;
614 int loopsPerIter = 0;
mtklein@google.comdbd41c82013-09-13 20:11:09 +0000615 if (FLAGS_verbose) { SkDebugf("%s %s: ", bench->getName(), config.name); }
mtklein@google.comc2897432013-09-10 19:23:38 +0000616 do {
djsollen@google.com70de4da2013-10-10 17:33:39 +0000617 // Ramp up 1 -> 2 -> 4 -> 8 -> 16 -> ... -> ~1 billion.
618 loopsPerIter = (loopsPerIter == 0) ? 1 : loopsPerIter * 2;
619 if (loopsPerIter >= (1<<30) || timer.fWall > FLAGS_maxMs) {
mtklein@google.comc2897432013-09-10 19:23:38 +0000620 // If you find it takes more than a billion loops to get up to 20ms of runtime,
621 // you've got a computer clocked at several THz or have a broken benchmark. ;)
622 // "1B ought to be enough for anybody."
mtklein@google.comdbd41c82013-09-13 20:11:09 +0000623 logger.logError(SkStringPrintf(
djsollen@google.com70de4da2013-10-10 17:33:39 +0000624 "\nCan't get %s %s to converge in %dms (%d loops)",
625 bench->getName(), config.name, FLAGS_maxMs, loopsPerIter));
mtklein@google.comc2897432013-09-10 19:23:38 +0000626 break;
627 }
mtklein@google.comc2897432013-09-10 19:23:38 +0000628
629 if ((benchMode == kRecord_BenchMode || benchMode == kPictureRecord_BenchMode)) {
630 // Clear the recorded commands so that they do not accumulate.
631 canvas.reset(recordTo.beginRecording(dim.fX, dim.fY, kRecordFlags));
junov@chromium.orgfb103892012-09-20 19:35:43 +0000632 }
robertphillips@google.com91ee3a12012-08-28 12:18:40 +0000633
mtklein@google.comc2897432013-09-10 19:23:38 +0000634 timer.start();
djsollen@google.com70de4da2013-10-10 17:33:39 +0000635 // Inner loop that allows us to break the run into smaller
636 // chunks (e.g. frames). This is especially useful for the GPU
637 // as we can flush and/or swap buffers to keep the GPU from
638 // queuing up too much work.
639 for (int loopCount = loopsPerIter; loopCount > 0; ) {
commit-bot@chromium.org28871192013-10-14 15:28:01 +0000640 // Save and restore around each call to draw() to guarantee a pristine canvas.
641 SkAutoCanvasRestore saveRestore(canvas, true/*also save*/);
642
commit-bot@chromium.org33614712013-12-03 18:17:16 +0000643 int loops;
djsollen@google.com70de4da2013-10-10 17:33:39 +0000644 if (frameIntervalComputed && loopCount > loopsPerFrame) {
commit-bot@chromium.org33614712013-12-03 18:17:16 +0000645 loops = loopsPerFrame;
djsollen@google.com70de4da2013-10-10 17:33:39 +0000646 loopCount -= loopsPerFrame;
647 } else {
commit-bot@chromium.org33614712013-12-03 18:17:16 +0000648 loops = loopCount;
djsollen@google.com70de4da2013-10-10 17:33:39 +0000649 loopCount = 0;
650 }
651
652 if (benchMode == kPictureRecord_BenchMode) {
653 recordFrom.draw(canvas);
654 } else {
commit-bot@chromium.org33614712013-12-03 18:17:16 +0000655 bench->draw(loops, canvas);
djsollen@google.com70de4da2013-10-10 17:33:39 +0000656 }
657
658 if (kDeferredSilent_BenchMode == benchMode) {
659 static_cast<SkDeferredCanvas*>(canvas.get())->silentFlush();
660 } else if (NULL != canvas) {
661 canvas->flush();
662 }
663
664#if SK_SUPPORT_GPU
665 // swap drawing buffers on each frame to prevent the GPU
666 // from queuing up too much work
667 if (NULL != glContext) {
668 glContext->swapBuffers();
669 }
670#endif
mtklein@google.comc2897432013-09-10 19:23:38 +0000671 }
672
mtklein@google.comc2897432013-09-10 19:23:38 +0000673
674
675 // Stop truncated timers before GL calls complete, and stop the full timers after.
676 timer.truncatedEnd();
677#if SK_SUPPORT_GPU
678 if (NULL != glContext) {
679 context->flush();
680 SK_GL(*glContext, Finish());
681 }
682#endif
683 timer.end();
djsollen@google.com70de4da2013-10-10 17:33:39 +0000684
djsollen@google.comdcfed6c2013-10-10 18:48:27 +0000685 // setup the frame interval for subsequent iterations
686 if (!frameIntervalComputed) {
djsollen@google.com70de4da2013-10-10 17:33:39 +0000687 frameIntervalTime += timer.fWall;
688 frameIntervalTotalLoops += loopsPerIter;
689 if (frameIntervalTime >= FLAGS_minMs) {
690 frameIntervalComputed = true;
691 loopsPerFrame =
djsollen@google.com4e1d4b32013-10-10 17:50:52 +0000692 (int)(((double)frameIntervalTotalLoops / frameIntervalTime) * FLAGS_minMs);
djsollen@google.com70de4da2013-10-10 17:33:39 +0000693 if (loopsPerFrame < 1) {
694 loopsPerFrame = 1;
695 }
696// SkDebugf(" %s has %d loops in %f ms (normalized to %d)\n",
697// bench->getName(), frameIntervalTotalLoops,
698// timer.fWall, loopsPerFrame);
699 }
700 }
djsollen@google.comdcfed6c2013-10-10 18:48:27 +0000701
djsollen@google.com70de4da2013-10-10 17:33:39 +0000702 const double current = timer.fWall / loopsPerIter;
mtklein@google.comdbd41c82013-09-13 20:11:09 +0000703 if (FLAGS_verbose && current > previous) { SkDebugf("↑"); }
704 if (FLAGS_verbose) { SkDebugf("%.3g ", current); }
705 converged = HasConverged(previous, current, timer.fWall);
706 previous = current;
707 } while (!kIsDebug && !converged);
708 if (FLAGS_verbose) { SkDebugf("\n"); }
mtklein@google.comc2897432013-09-10 19:23:38 +0000709
commit-bot@chromium.org644629c2013-11-21 06:21:58 +0000710 if (FLAGS_outDir.count() && SkBenchmark::kNonRendering_Backend != config.backend) {
mtklein@google.comc2897432013-09-10 19:23:38 +0000711 saveFile(bench->getName(),
712 config.name,
713 FLAGS_outDir[0],
714 device->accessBitmap(false));
715 }
716
717 if (kIsDebug) {
718 // Let's not mislead ourselves by looking at Debug build bench times!
719 continue;
720 }
721
722 // Normalize to ms per 1000 iterations.
djsollen@google.com70de4da2013-10-10 17:33:39 +0000723 const double normalize = 1000.0 / loopsPerIter;
mtklein@google.comc2897432013-09-10 19:23:38 +0000724 const struct { char shortName; const char* longName; double ms; } times[] = {
725 {'w', "msecs", normalize * timer.fWall},
726 {'W', "Wmsecs", normalize * timer.fTruncatedWall},
727 {'c', "cmsecs", normalize * timer.fCpu},
728 {'C', "Cmsecs", normalize * timer.fTruncatedCpu},
729 {'g', "gmsecs", normalize * timer.fGpu},
730 };
731
commit-bot@chromium.orge3bb3bc2013-12-03 18:16:48 +0000732 writer.config(config.name);
mtklein@google.comc2897432013-09-10 19:23:38 +0000733 for (size_t i = 0; i < SK_ARRAY_COUNT(times); i++) {
734 if (strchr(FLAGS_timers[0], times[i].shortName) && times[i].ms > 0) {
commit-bot@chromium.orge3bb3bc2013-12-03 18:16:48 +0000735 writer.timer(times[i].longName, times[i].ms);
commit-bot@chromium.org7495f592013-06-03 19:31:07 +0000736 }
reed@google.com25df8882011-07-14 19:03:58 +0000737 }
bsalomon@google.com604a56a2013-03-15 15:42:15 +0000738 }
reed@android.combd700c32009-01-05 03:34:50 +0000739 }
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000740#if SK_SUPPORT_GPU
bsalomon@google.comcb265352013-02-22 16:13:16 +0000741 gContextFactory.destroyContexts();
robertphillips@google.com9d594202012-09-13 14:05:00 +0000742#endif
reed@android.combd700c32009-01-05 03:34:50 +0000743 return 0;
744}
caryclark@google.com5987f582012-10-02 18:33:14 +0000745
borenet@google.com7158e6a2012-11-01 17:43:44 +0000746#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
caryclark@google.com5987f582012-10-02 18:33:14 +0000747int main(int argc, char * const argv[]) {
748 return tool_main(argc, (char**) argv);
749}
750#endif