Adding SkTileGrid: a new subclass of BBoxHierarchy, optimized for tiled playback.
[platform/upstream/libSkiaSharp.git] / tools / bench_pictures_main.cpp
1 /*
2  * Copyright 2012 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 "BenchTimer.h"
9 #include "PictureBenchmark.h"
10 #include "SkBenchLogger.h"
11 #include "SkCanvas.h"
12 #include "SkGraphics.h"
13 #include "SkImageDecoder.h"
14 #include "SkMath.h"
15 #include "SkOSFile.h"
16 #include "SkPicture.h"
17 #include "SkStream.h"
18 #include "SkTArray.h"
19 #include "picture_utils.h"
20
21 const int DEFAULT_REPEATS = 1;
22
23 static void usage(const char* argv0) {
24     SkDebugf("SkPicture benchmarking tool\n");
25     SkDebugf("\n"
26 "Usage: \n"
27 "     %s <inputDir>...\n"
28 "     [--logFile filename][--timers [wcgWC]*][--logPerIter 1|0][--min]\n"
29 "     [--repeat] \n"
30 "     [--mode pow2tile minWidth height[] | record | simple\n"
31 "             | tile width[] height[] | playbackCreation]\n"
32 "     [--pipe]\n"
33 "     [--bbh bbhType]\n"
34 "     [--multi numThreads]\n"
35 "     [--device bitmap"
36 #if SK_SUPPORT_GPU
37 " | gpu"
38 #endif
39 "]"
40 , argv0);
41     SkDebugf("\n\n");
42     SkDebugf(
43 "     inputDir:  A list of directories and files to use as input. Files are\n"
44 "                expected to have the .skp extension.\n\n"
45 "     --logFile filename : destination for writing log output, in addition to stdout.\n");
46     SkDebugf("     --logPerIter 1|0 : "
47              "Log each repeat timer instead of mean, default is disabled.\n");
48     SkDebugf("     --min : Print the minimum times (instead of average).\n");
49     SkDebugf("     --timers [wcgWC]* : "
50              "Display wall, cpu, gpu, truncated wall or truncated cpu time for each picture.\n");
51     SkDebugf(
52 "     --mode pow2tile minWidht height[] | record | simple\n"
53 "            | tile width[] height[] | playbackCreation:\n"
54 "            Run in the corresponding mode.\n"
55 "            Default is simple.\n");
56     SkDebugf(
57 "                     pow2tile minWidth height[], Creates tiles with widths\n"
58 "                                                 that are all a power of two\n"
59 "                                                 such that they minimize the\n"
60 "                                                 amount of wasted tile space.\n"
61 "                                                 minWidth is the minimum width\n"
62 "                                                 of these tiles and must be a\n"
63 "                                                 power of two. Simple\n"
64 "                                                 rendering using these tiles\n"
65 "                                                 is benchmarked.\n");
66     SkDebugf(
67 "                     record, Benchmark picture to picture recording.\n");
68     SkDebugf(
69 "                     simple, Benchmark a simple rendering.\n");
70     SkDebugf(
71 "                     tile width[] height[], Benchmark simple rendering using\n"
72 "                                            tiles with the given dimensions.\n");
73     SkDebugf(
74 "                     playbackCreation, Benchmark creation of the SkPicturePlayback.\n");
75     SkDebugf("\n");
76     SkDebugf(
77 "     --multi numThreads : Set the number of threads for multi threaded drawing. Must be greater\n"
78 "                          than 1. Only works with tiled rendering.\n"
79 "     --pipe: Benchmark SkGPipe rendering. Currently incompatible with \"mode\".\n");
80     SkDebugf(
81 "     --bbh bbhType [width height]: Set the bounding box hierarchy type to\n"
82 "                     be used. Accepted values are: none, rtree, grid. Default\n"
83 "                     value is none. Not compatible with --pipe. With value\n"
84 "                     'grid', width and height must be specified. 'grid' can\n"
85 "                     only be used with modes tile, record, and\n"
86 "                     playbackCreation.");
87     SkDebugf(
88 "     --device bitmap"
89 #if SK_SUPPORT_GPU
90 " | gpu"
91 #endif
92 ": Use the corresponding device. Default is bitmap.\n");
93     SkDebugf(
94 "                     bitmap, Render to a bitmap.\n");
95 #if SK_SUPPORT_GPU
96     SkDebugf(
97 "                     gpu, Render to the GPU.\n");
98 #endif
99     SkDebugf("\n");
100     SkDebugf(
101 "     --repeat:  "
102 "Set the number of times to repeat each test."
103 " Default is %i.\n", DEFAULT_REPEATS);
104 }
105
106 SkBenchLogger gLogger;
107
108 static bool run_single_benchmark(const SkString& inputPath,
109                                  sk_tools::PictureBenchmark& benchmark) {
110     SkFILEStream inputStream;
111
112     inputStream.setPath(inputPath.c_str());
113     if (!inputStream.isValid()) {
114         SkString err;
115         err.printf("Could not open file %s\n", inputPath.c_str());
116         gLogger.logError(err);
117         return false;
118     }
119
120     bool success = false;
121     SkPicture picture(&inputStream, &success, &SkImageDecoder::DecodeStream);
122     if (!success) {
123         SkString err;
124         err.printf("Could not read an SkPicture from %s\n", inputPath.c_str());
125         gLogger.logError(err);
126         return false;
127     }
128
129     SkString filename;
130     sk_tools::get_basename(&filename, inputPath);
131
132     SkString result;
133     result.printf("running bench [%i %i] %s ", picture.width(),
134                   picture.height(), filename.c_str());
135     gLogger.logProgress(result);
136
137     benchmark.run(&picture);
138     return true;
139 }
140
141 #define PRINT_USAGE_AND_EXIT \
142     do {                     \
143         usage(argv0);        \
144         exit(-1);            \
145     } while (0)
146
147 static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs,
148                               sk_tools::PictureBenchmark* benchmark) {
149     const char* argv0 = argv[0];
150     char* const* stop = argv + argc;
151
152     int repeats = DEFAULT_REPEATS;
153     sk_tools::PictureRenderer::SkDeviceTypes deviceType =
154         sk_tools::PictureRenderer::kBitmap_DeviceType;
155
156     SkAutoTUnref<sk_tools::PictureRenderer> renderer(NULL);
157
158     // Create a string to show our current settings.
159     // TODO: Make it prettier. Currently it just repeats the command line.
160     SkString commandLine("bench_pictures:");
161     for (int i = 1; i < argc; i++) {
162         commandLine.appendf(" %s", *(argv+i));
163     }
164     commandLine.append("\n");
165
166     bool usePipe = false;
167     int numThreads = 1;
168     bool useTiles = false;
169     const char* widthString = NULL;
170     const char* heightString = NULL;
171     int gridWidth = 0;
172     int gridHeight = 0;
173     bool isPowerOf2Mode = false;
174     const char* mode = NULL;
175     bool gridSupported = false;
176     sk_tools::PictureRenderer::BBoxHierarchyType bbhType =
177         sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
178     for (++argv; argv < stop; ++argv) {
179         if (0 == strcmp(*argv, "--repeat")) {
180             ++argv;
181             if (argv < stop) {
182                 repeats = atoi(*argv);
183                 if (repeats < 1) {
184                     gLogger.logError("--repeat must be given a value > 0\n");
185                     PRINT_USAGE_AND_EXIT;
186                 }
187             } else {
188                 gLogger.logError("Missing arg for --repeat\n");
189                 PRINT_USAGE_AND_EXIT;
190             }
191         } else if (0 == strcmp(*argv, "--pipe")) {
192             usePipe = true;
193         } else if (0 == strcmp(*argv, "--logFile")) {
194             argv++;
195             if (argv < stop) {
196                 if (!gLogger.SetLogFile(*argv)) {
197                     SkString str;
198                     str.printf("Could not open %s for writing.", *argv);
199                     gLogger.logError(str);
200                     usage(argv0);
201                     // TODO(borenet): We're disabling this for now, due to
202                     // write-protected Android devices.  The very short-term
203                     // solution is to ignore the fact that we have no log file.
204                     //exit(-1);
205                 }
206             } else {
207                 gLogger.logError("Missing arg for --logFile\n");
208                 PRINT_USAGE_AND_EXIT;
209             }
210         } else if (0 == strcmp(*argv, "--multi")) {
211             ++argv;
212             if (argv >= stop) {
213                 gLogger.logError("Missing arg for --multi\n");
214                 PRINT_USAGE_AND_EXIT;
215             }
216             numThreads = atoi(*argv);
217             if (numThreads < 2) {
218                 gLogger.logError("Number of threads must be at least 2.\n");
219                 PRINT_USAGE_AND_EXIT;
220             }
221         } else if (0 == strcmp(*argv, "--bbh")) {
222             ++argv;
223             if (argv >= stop) {
224                 gLogger.logError("Missing value for --bbh\n");
225                 PRINT_USAGE_AND_EXIT;
226             }
227             if (0 == strcmp(*argv, "none")) {
228                 bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
229             } else if (0 == strcmp(*argv, "rtree")) {
230                 bbhType = sk_tools::PictureRenderer::kRTree_BBoxHierarchyType;
231             } else if (0 == strcmp(*argv, "grid")) {
232                 bbhType = sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType;
233                 ++argv;
234                 if (argv >= stop) {
235                     gLogger.logError("Missing width for --bbh grid\n");
236                     PRINT_USAGE_AND_EXIT;
237                 }
238                 gridWidth = atoi(*argv);
239                 ++argv;
240                 if (argv >= stop) {
241                     gLogger.logError("Missing height for --bbh grid\n");
242                     PRINT_USAGE_AND_EXIT;
243                 }
244                 gridHeight = atoi(*argv);
245             } else {
246                 SkString err;
247                 err.printf("%s is not a valid value for --bbhType\n", *argv);
248                 gLogger.logError(err);
249                 PRINT_USAGE_AND_EXIT;
250             }
251
252         } else if (0 == strcmp(*argv, "--mode")) {
253             if (renderer.get() != NULL) {
254                 SkDebugf("Cannot combine modes.\n");
255                 PRINT_USAGE_AND_EXIT;
256             }
257
258             ++argv;
259             if (argv >= stop) {
260                 gLogger.logError("Missing mode for --mode\n");
261                 PRINT_USAGE_AND_EXIT;
262             }
263
264             if (0 == strcmp(*argv, "record")) {
265                 renderer.reset(SkNEW(sk_tools::RecordPictureRenderer));
266                 gridSupported = true;
267             } else if (0 == strcmp(*argv, "simple")) {
268                 renderer.reset(SkNEW(sk_tools::SimplePictureRenderer));
269             } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))) {
270                 useTiles = true;
271                 mode = *argv;
272
273                 if (0 == strcmp(*argv, "pow2tile")) {
274                     isPowerOf2Mode = true;
275                 } else {
276                     gridSupported = true;
277                 }
278
279                 ++argv;
280                 if (argv >= stop) {
281                     SkString err;
282                     err.printf("Missing width for --mode %s\n", mode);
283                     gLogger.logError(err);
284                     PRINT_USAGE_AND_EXIT;
285                 }
286
287                 widthString = *argv;
288                 ++argv;
289                 if (argv >= stop) {
290                     gLogger.logError("Missing height for --mode tile\n");
291                     PRINT_USAGE_AND_EXIT;
292                 }
293                 heightString = *argv;
294             } else if (0 == strcmp(*argv, "playbackCreation")) {
295                 renderer.reset(SkNEW(sk_tools::PlaybackCreationRenderer));
296                 gridSupported = true;
297             } else {
298                 SkString err;
299                 err.printf("%s is not a valid mode for --mode\n", *argv);
300                 gLogger.logError(err);
301                 PRINT_USAGE_AND_EXIT;
302             }
303         }  else if (0 == strcmp(*argv, "--device")) {
304             ++argv;
305             if (argv >= stop) {
306                 gLogger.logError("Missing mode for --device\n");
307                 PRINT_USAGE_AND_EXIT;
308             }
309
310             if (0 == strcmp(*argv, "bitmap")) {
311                 deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType;
312             }
313 #if SK_SUPPORT_GPU
314             else if (0 == strcmp(*argv, "gpu")) {
315                 deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
316             }
317 #endif
318             else {
319                 SkString err;
320                 err.printf("%s is not a valid mode for --device\n", *argv);
321                 gLogger.logError(err);
322                 PRINT_USAGE_AND_EXIT;
323             }
324         } else if (0 == strcmp(*argv, "--timers")) {
325             ++argv;
326             if (argv < stop) {
327                 bool timerWall = false;
328                 bool truncatedTimerWall = false;
329                 bool timerCpu = false;
330                 bool truncatedTimerCpu = false;
331                 bool timerGpu = false;
332                 for (char* t = *argv; *t; ++t) {
333                     switch (*t) {
334                         case 'w':
335                             timerWall = true;
336                             break;
337                         case 'c':
338                             timerCpu = true;
339                             break;
340                         case 'W':
341                             truncatedTimerWall = true;
342                             break;
343                         case 'C':
344                             truncatedTimerCpu = true;
345                             break;
346                         case 'g':
347                             timerGpu = true;
348                             break;
349                         default: {
350                             break;
351                         }
352                     }
353                 }
354                 benchmark->setTimersToShow(timerWall, truncatedTimerWall, timerCpu,
355                                            truncatedTimerCpu, timerGpu);
356             } else {
357                 gLogger.logError("Missing arg for --timers\n");
358                 PRINT_USAGE_AND_EXIT;
359             }
360         } else if (0 == strcmp(*argv, "--min")) {
361             benchmark->setPrintMin(true);
362         } else if (0 == strcmp(*argv, "--logPerIter")) {
363             ++argv;
364             if (argv < stop) {
365                 bool log = atoi(*argv) != 0;
366                 benchmark->setLogPerIter(log);
367             } else {
368                 gLogger.logError("Missing arg for --logPerIter\n");
369                 PRINT_USAGE_AND_EXIT;
370             }
371         } else if (0 == strcmp(*argv, "--help") || 0 == strcmp(*argv, "-h")) {
372             PRINT_USAGE_AND_EXIT;
373         } else {
374             inputs->push_back(SkString(*argv));
375         }
376     }
377
378     if (numThreads > 1 && !useTiles) {
379         gLogger.logError("Multithreaded drawing requires tiled rendering.\n");
380         PRINT_USAGE_AND_EXIT;
381     }
382
383     if (usePipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) {
384         gLogger.logError("--pipe and --bbh cannot be used together\n");
385         PRINT_USAGE_AND_EXIT;
386     }
387
388     if (sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType == bbhType &&
389         !gridSupported) {
390         gLogger.logError("'--bbh grid' is not compatible with specified --mode.\n");
391         PRINT_USAGE_AND_EXIT;
392     }
393
394     if (useTiles) {
395         SkASSERT(NULL == renderer);
396         sk_tools::TiledPictureRenderer* tiledRenderer;
397         if (numThreads > 1) {
398             tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads));
399         } else {
400             tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer);
401         }
402         if (isPowerOf2Mode) {
403             int minWidth = atoi(widthString);
404             if (!SkIsPow2(minWidth) || minWidth < 0) {
405                 tiledRenderer->unref();
406                 SkString err;
407                 err.printf("-mode %s must be given a width"
408                          " value that is a power of two\n", mode);
409                 gLogger.logError(err);
410                 PRINT_USAGE_AND_EXIT;
411             }
412             tiledRenderer->setTileMinPowerOf2Width(minWidth);
413         } else if (sk_tools::is_percentage(widthString)) {
414             tiledRenderer->setTileWidthPercentage(atof(widthString));
415             if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
416                 tiledRenderer->unref();
417                 gLogger.logError("--mode tile must be given a width percentage > 0\n");
418                 PRINT_USAGE_AND_EXIT;
419             }
420         } else {
421             tiledRenderer->setTileWidth(atoi(widthString));
422             if (!(tiledRenderer->getTileWidth() > 0)) {
423                 tiledRenderer->unref();
424                 gLogger.logError("--mode tile must be given a width > 0\n");
425                 PRINT_USAGE_AND_EXIT;
426             }
427         }
428
429         if (sk_tools::is_percentage(heightString)) {
430             tiledRenderer->setTileHeightPercentage(atof(heightString));
431             if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
432                 tiledRenderer->unref();
433                 gLogger.logError("--mode tile must be given a height percentage > 0\n");
434                 PRINT_USAGE_AND_EXIT;
435             }
436         } else {
437             tiledRenderer->setTileHeight(atoi(heightString));
438             if (!(tiledRenderer->getTileHeight() > 0)) {
439                 tiledRenderer->unref();
440                 gLogger.logError("--mode tile must be given a height > 0\n");
441                 PRINT_USAGE_AND_EXIT;
442             }
443         }
444         if (numThreads > 1) {
445 #if SK_SUPPORT_GPU
446             if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) {
447                 tiledRenderer->unref();
448                 gLogger.logError("GPU not compatible with multithreaded tiling.\n");
449                 PRINT_USAGE_AND_EXIT;
450             }
451 #endif
452         }
453         renderer.reset(tiledRenderer);
454         if (usePipe) {
455             SkDebugf("Pipe rendering is currently not compatible with tiling.\n"
456                      "Turning off pipe.\n");
457         }
458     } else if (usePipe) {
459         if (renderer.get() != NULL) {
460             SkDebugf("Pipe is incompatible with other modes.\n");
461             PRINT_USAGE_AND_EXIT;
462         }
463         renderer.reset(SkNEW(sk_tools::PipePictureRenderer));
464     }
465     if (inputs->count() < 1) {
466         PRINT_USAGE_AND_EXIT;
467     }
468
469     if (NULL == renderer) {
470         renderer.reset(SkNEW(sk_tools::SimplePictureRenderer));
471     }
472
473     renderer->setBBoxHierarchyType(bbhType);
474     renderer->setGridSize(gridWidth, gridHeight);
475     benchmark->setRenderer(renderer);
476     benchmark->setRepeats(repeats);
477     benchmark->setDeviceType(deviceType);
478     benchmark->setLogger(&gLogger);
479     // Report current settings:
480     gLogger.logProgress(commandLine);
481 }
482
483 static int process_input(const SkString& input,
484                          sk_tools::PictureBenchmark& benchmark) {
485     SkOSFile::Iter iter(input.c_str(), "skp");
486     SkString inputFilename;
487     int failures = 0;
488     if (iter.next(&inputFilename)) {
489         do {
490             SkString inputPath;
491             sk_tools::make_filepath(&inputPath, input, inputFilename);
492             if (!run_single_benchmark(inputPath, benchmark)) {
493                 ++failures;
494             }
495         } while(iter.next(&inputFilename));
496     } else if (SkStrEndsWith(input.c_str(), ".skp")) {
497         if (!run_single_benchmark(input, benchmark)) {
498             ++failures;
499         }
500     } else {
501         SkString warning;
502         warning.printf("Warning: skipping %s\n", input.c_str());
503         gLogger.logError(warning);
504     }
505     return failures;
506 }
507
508 int tool_main(int argc, char** argv);
509 int tool_main(int argc, char** argv) {
510 #ifdef SK_ENABLE_INST_COUNT
511     gPrintInstCount = true;
512 #endif
513     SkAutoGraphics ag;
514
515     SkTArray<SkString> inputs;
516     sk_tools::PictureBenchmark benchmark;
517
518     parse_commandline(argc, argv, &inputs, &benchmark);
519
520     int failures = 0;
521     for (int i = 0; i < inputs.count(); ++i) {
522         failures += process_input(inputs[i], benchmark);
523     }
524
525     if (failures != 0) {
526         SkString err;
527         err.printf("Failed to run %i benchmarks.\n", failures);
528         gLogger.logError(err);
529         return 1;
530     }
531     return 0;
532 }
533
534 #if !defined SK_BUILD_FOR_IOS
535 int main(int argc, char * const argv[]) {
536     return tool_main(argc, (char**) argv);
537 }
538 #endif