Cleanup: Delete sk_tools::get_basename() in favor of SkOSPath::SkBasename().
[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 "CopyTilesRenderer.h"
10 #include "LazyDecodeBitmap.h"
11 #include "PictureBenchmark.h"
12 #include "PictureRenderingFlags.h"
13 #include "SkBenchLogger.h"
14 #include "SkCommandLineFlags.h"
15 #include "SkData.h"
16 #include "SkDiscardableMemoryPool.h"
17 #include "SkGraphics.h"
18 #include "SkImageDecoder.h"
19 #include "SkMath.h"
20 #include "SkOSFile.h"
21 #include "SkPicture.h"
22 #include "SkStream.h"
23 #include "picture_utils.h"
24 #include "PictureResultsWriter.h"
25
26 SkBenchLogger gLogger;
27 PictureResultsLoggerWriter gLogWriter(&gLogger);
28 PictureResultsMultiWriter gWriter;
29
30 // Flags used by this file, in alphabetical order.
31 DEFINE_bool(countRAM, false, "Count the RAM used for bitmap pixels in each skp file");
32 DECLARE_bool(deferImageDecoding);
33 DEFINE_string(filter, "",
34         "type:flag : Enable canvas filtering to disable a paint flag, "
35         "use no blur or low quality blur, or use no hinting or "
36         "slight hinting. For all flags except AAClip, specify the "
37         "type of primitive to effect, or choose all. for AAClip "
38         "alone, the filter affects all clips independent of type. "
39         "Specific flags are listed above.");
40 DEFINE_string(logFile, "", "Destination for writing log output, in addition to stdout.");
41 DEFINE_bool(logPerIter, false, "Log each repeat timer instead of mean.");
42 #ifdef SK_BUILD_JSON_WRITER
43 DEFINE_string(jsonLog, "", "Destination for writing JSON data.");
44 #endif
45 DEFINE_bool(min, false, "Print the minimum times (instead of average).");
46 DECLARE_int32(multi);
47 DECLARE_string(readPath);
48 DEFINE_int32(repeat, 1, "Set the number of times to repeat each test.");
49 DEFINE_bool(timeIndividualTiles, false, "Report times for drawing individual tiles, rather than "
50             "times for drawing the whole page. Requires tiled rendering.");
51 DEFINE_bool(purgeDecodedTex, false, "Purge decoded and GPU-uploaded textures "
52             "after each iteration.");
53 DEFINE_string(timers, "c", "[wcgWC]*: Display wall, cpu, gpu, truncated wall or truncated cpu time"
54               " for each picture.");
55 DEFINE_bool(trackDeferredCaching, false, "Only meaningful with --deferImageDecoding and "
56             "SK_LAZY_CACHE_STATS set to true. Report percentage of cache hits when using "
57             "deferred image decoding.");
58
59 DEFINE_bool(preprocess, false, "If true, perform device specific preprocessing before timing.");
60
61 static char const * const gFilterTypes[] = {
62     "paint",
63     "point",
64     "line",
65     "bitmap",
66     "rect",
67     "oval",
68     "path",
69     "text",
70     "all",
71 };
72
73 static const size_t kFilterTypesCount = sizeof(gFilterTypes) / sizeof(gFilterTypes[0]);
74
75 static char const * const gFilterFlags[] = {
76     "antiAlias",
77     "filterBitmap",
78     "dither",
79     "underlineText",
80     "strikeThruText",
81     "fakeBoldText",
82     "linearText",
83     "subpixelText",
84     "devKernText",
85     "LCDRenderText",
86     "embeddedBitmapText",
87     "autoHinting",
88     "verticalText",
89     "genA8FromLCD",
90     "blur",
91     "hinting",
92     "slightHinting",
93     "AAClip",
94 };
95
96 static const size_t kFilterFlagsCount = sizeof(gFilterFlags) / sizeof(gFilterFlags[0]);
97
98 static SkString filtersName(sk_tools::PictureRenderer::DrawFilterFlags* drawFilters) {
99     int all = drawFilters[0];
100     size_t tIndex;
101     for (tIndex = 1; tIndex < SkDrawFilter::kTypeCount; ++tIndex) {
102         all &= drawFilters[tIndex];
103     }
104     SkString result;
105     for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) {
106         SkString types;
107         if (all & (1 << fIndex)) {
108             types = gFilterTypes[SkDrawFilter::kTypeCount];
109         } else {
110             for (tIndex = 0; tIndex < SkDrawFilter::kTypeCount; ++tIndex) {
111                 if (drawFilters[tIndex] & (1 << fIndex)) {
112                     types += gFilterTypes[tIndex];
113                 }
114             }
115         }
116         if (!types.size()) {
117             continue;
118         }
119         result += "_";
120         result += types;
121         result += ".";
122         result += gFilterFlags[fIndex];
123     }
124     return result;
125 }
126
127 static SkString filterTypesUsage() {
128     SkString result;
129     for (size_t index = 0; index < kFilterTypesCount; ++index) {
130         result += gFilterTypes[index];
131         if (index < kFilterTypesCount - 1) {
132             result += " | ";
133         }
134     }
135     return result;
136 }
137
138 static SkString filterFlagsUsage() {
139     SkString result;
140     size_t len = 0;
141     for (size_t index = 0; index < kFilterFlagsCount; ++index) {
142         result += gFilterFlags[index];
143         if (result.size() - len >= 72) {
144             result += "\n\t\t";
145             len = result.size();
146         }
147         if (index < kFilterFlagsCount - 1) {
148             result += " | ";
149         }
150     }
151     return result;
152 }
153
154 #if SK_LAZY_CACHE_STATS
155 static int32_t gTotalCacheHits;
156 static int32_t gTotalCacheMisses;
157 #endif
158
159 static bool run_single_benchmark(const SkString& inputPath,
160                                  sk_tools::PictureBenchmark& benchmark) {
161     SkFILEStream inputStream;
162
163     inputStream.setPath(inputPath.c_str());
164     if (!inputStream.isValid()) {
165         SkString err;
166         err.printf("Could not open file %s\n", inputPath.c_str());
167         gLogger.logError(err);
168         return false;
169     }
170
171     SkDiscardableMemoryPool* pool = SkGetGlobalDiscardableMemoryPool();
172     // Since the old picture has been deleted, all pixels should be cleared.
173     SkASSERT(pool->getRAMUsed() == 0);
174     if (FLAGS_countRAM) {
175         pool->setRAMBudget(SK_MaxU32);
176         // Set the limit to max, so all pixels will be kept
177     }
178
179     SkPicture::InstallPixelRefProc proc;
180     if (FLAGS_deferImageDecoding) {
181         proc = &sk_tools::LazyDecodeBitmap;
182     } else {
183         proc = &SkImageDecoder::DecodeMemory;
184     }
185     SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromStream(&inputStream, proc));
186
187     if (NULL == picture.get()) {
188         SkString err;
189         err.printf("Could not read an SkPicture from %s\n", inputPath.c_str());
190         gLogger.logError(err);
191         return false;
192     }
193
194     SkString filename = SkOSPath::SkBasename(inputPath.c_str());
195
196     gWriter.bench(filename.c_str(), picture->width(), picture->height());
197
198     benchmark.run(picture);
199
200 #if SK_LAZY_CACHE_STATS
201     if (FLAGS_trackDeferredCaching) {
202         int cacheHits = pool->getCacheHits();
203         int cacheMisses = pool->getCacheMisses();
204         pool->resetCacheHitsAndMisses();
205         SkString hitString;
206         hitString.printf("Cache hit rate: %f\n", (double) cacheHits / (cacheHits + cacheMisses));
207         gLogger.logProgress(hitString);
208         gTotalCacheHits += cacheHits;
209         gTotalCacheMisses += cacheMisses;
210     }
211 #endif
212     if (FLAGS_countRAM) {
213         SkString ramCount("RAM used for bitmaps: ");
214         size_t bytes = pool->getRAMUsed();
215         if (bytes > 1024) {
216             size_t kb = bytes / 1024;
217             if (kb > 1024) {
218                 size_t mb = kb / 1024;
219                 ramCount.appendf("%zi MB\n", mb);
220             } else {
221                 ramCount.appendf("%zi KB\n", kb);
222             }
223         } else {
224             ramCount.appendf("%zi bytes\n", bytes);
225         }
226         gLogger.logProgress(ramCount);
227     }
228
229     return true;
230 }
231
232 static void setup_benchmark(sk_tools::PictureBenchmark* benchmark) {
233     sk_tools::PictureRenderer::DrawFilterFlags drawFilters[SkDrawFilter::kTypeCount];
234     sk_bzero(drawFilters, sizeof(drawFilters));
235
236     if (FLAGS_filter.count() > 0) {
237         const char* filters = FLAGS_filter[0];
238         const char* colon = strchr(filters, ':');
239         if (colon) {
240             int32_t type = -1;
241             size_t typeLen = colon - filters;
242             for (size_t tIndex = 0; tIndex < kFilterTypesCount; ++tIndex) {
243                 if (typeLen == strlen(gFilterTypes[tIndex])
244                         && !strncmp(filters, gFilterTypes[tIndex], typeLen)) {
245                     type = SkToS32(tIndex);
246                     break;
247                 }
248             }
249             if (type < 0) {
250                 SkString err;
251                 err.printf("Unknown type for --filter %s\n", filters);
252                 gLogger.logError(err);
253                 exit(-1);
254             }
255             int flag = -1;
256             size_t flagLen = strlen(filters) - typeLen - 1;
257             for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) {
258                 if (flagLen == strlen(gFilterFlags[fIndex])
259                         && !strncmp(colon + 1, gFilterFlags[fIndex], flagLen)) {
260                     flag = 1 << fIndex;
261                     break;
262                 }
263             }
264             if (flag < 0) {
265                 SkString err;
266                 err.printf("Unknown flag for --filter %s\n", filters);
267                 gLogger.logError(err);
268                 exit(-1);
269             }
270             for (int index = 0; index < SkDrawFilter::kTypeCount; ++index) {
271                 if (type != SkDrawFilter::kTypeCount && index != type) {
272                     continue;
273                 }
274                 drawFilters[index] = (sk_tools::PictureRenderer::DrawFilterFlags)
275                         (drawFilters[index] | flag);
276             }
277         } else {
278             SkString err;
279             err.printf("Unknown arg for --filter %s : missing colon\n", filters);
280             gLogger.logError(err);
281             exit(-1);
282         }
283     }
284
285     if (FLAGS_timers.count() > 0) {
286         size_t index = 0;
287         bool timerWall = false;
288         bool truncatedTimerWall = false;
289         bool timerCpu = false;
290         bool truncatedTimerCpu = false;
291         bool timerGpu = false;
292         while (index < strlen(FLAGS_timers[0])) {
293             switch (FLAGS_timers[0][index]) {
294                 case 'w':
295                     timerWall = true;
296                     break;
297                 case 'c':
298                     timerCpu = true;
299                     break;
300                 case 'W':
301                     truncatedTimerWall = true;
302                     break;
303                 case 'C':
304                     truncatedTimerCpu = true;
305                     break;
306                 case 'g':
307                     timerGpu = true;
308                     break;
309                 default:
310                     SkDebugf("mystery character\n");
311                     break;
312             }
313             index++;
314         }
315         benchmark->setTimersToShow(timerWall, truncatedTimerWall, timerCpu, truncatedTimerCpu,
316                                   timerGpu);
317     }
318
319     SkString errorString;
320     SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString,
321                                                                    kBench_PictureTool));
322
323     if (errorString.size() > 0) {
324         gLogger.logError(errorString);
325     }
326
327     if (NULL == renderer.get()) {
328         exit(-1);
329     }
330
331     if (FLAGS_timeIndividualTiles) {
332         if (FLAGS_multi > 1) {
333             gLogger.logError("Cannot time individual tiles with more than one thread.\n");
334             exit(-1);
335         }
336         sk_tools::TiledPictureRenderer* tiledRenderer = renderer->getTiledRenderer();
337         if (NULL == tiledRenderer) {
338             gLogger.logError("--timeIndividualTiles requires tiled rendering.\n");
339             exit(-1);
340         }
341         if (!tiledRenderer->supportsTimingIndividualTiles()) {
342             gLogger.logError("This renderer does not support --timeIndividualTiles.\n");
343             exit(-1);
344         }
345         benchmark->setTimeIndividualTiles(true);
346     }
347
348     benchmark->setPurgeDecodedTex(FLAGS_purgeDecodedTex);
349     benchmark->setPreprocess(FLAGS_preprocess);
350
351     if (FLAGS_readPath.count() < 1) {
352         gLogger.logError(".skp files or directories are required.\n");
353         exit(-1);
354     }
355
356     renderer->setDrawFilters(drawFilters, filtersName(drawFilters));
357     if (FLAGS_logPerIter) {
358         benchmark->setTimerResultType(TimerData::kPerIter_Result);
359     } else if (FLAGS_min) {
360         benchmark->setTimerResultType(TimerData::kMin_Result);
361     } else {
362         benchmark->setTimerResultType(TimerData::kAvg_Result);
363     }
364     benchmark->setRenderer(renderer);
365     benchmark->setRepeats(FLAGS_repeat);
366     benchmark->setWriter(&gWriter);
367 }
368
369 static int process_input(const char* input,
370                          sk_tools::PictureBenchmark& benchmark) {
371     SkString inputAsSkString(input);
372     SkOSFile::Iter iter(input, "skp");
373     SkString inputFilename;
374     int failures = 0;
375     if (iter.next(&inputFilename)) {
376         do {
377             SkString inputPath;
378             sk_tools::make_filepath(&inputPath, inputAsSkString, inputFilename);
379             if (!run_single_benchmark(inputPath, benchmark)) {
380                 ++failures;
381             }
382         } while(iter.next(&inputFilename));
383     } else if (SkStrEndsWith(input, ".skp")) {
384         if (!run_single_benchmark(inputAsSkString, benchmark)) {
385             ++failures;
386         }
387     } else {
388         SkString warning;
389         warning.printf("Warning: skipping %s\n", input);
390         gLogger.logError(warning);
391     }
392     return failures;
393 }
394
395 int tool_main(int argc, char** argv);
396 int tool_main(int argc, char** argv) {
397     SkString usage;
398     usage.printf("Time drawing .skp files.\n"
399                  "\tPossible arguments for --filter: [%s]\n\t\t[%s]",
400                  filterTypesUsage().c_str(), filterFlagsUsage().c_str());
401     SkCommandLineFlags::SetUsage(usage.c_str());
402     SkCommandLineFlags::Parse(argc, argv);
403
404     if (FLAGS_repeat < 1) {
405         SkString error;
406         error.printf("--repeats must be >= 1. Was %i\n", FLAGS_repeat);
407         gLogger.logError(error);
408         exit(-1);
409     }
410
411     if (FLAGS_logFile.count() == 1) {
412         if (!gLogger.SetLogFile(FLAGS_logFile[0])) {
413             SkString str;
414             str.printf("Could not open %s for writing.\n", FLAGS_logFile[0]);
415             gLogger.logError(str);
416             // TODO(borenet): We're disabling this for now, due to
417             // write-protected Android devices.  The very short-term
418             // solution is to ignore the fact that we have no log file.
419             //exit(-1);
420         }
421     }
422
423 #ifdef SK_BUILD_JSON_WRITER
424     SkAutoTDelete<PictureJSONResultsWriter> jsonWriter;
425     if (FLAGS_jsonLog.count() == 1) {
426         jsonWriter.reset(SkNEW(PictureJSONResultsWriter(FLAGS_jsonLog[0])));
427         gWriter.add(jsonWriter.get());
428     }
429
430 #endif
431     gWriter.add(&gLogWriter);
432
433
434 #if SK_ENABLE_INST_COUNT
435     gPrintInstCount = true;
436 #endif
437     SkAutoGraphics ag;
438
439     sk_tools::PictureBenchmark benchmark;
440
441     setup_benchmark(&benchmark);
442
443     int failures = 0;
444     for (int i = 0; i < FLAGS_readPath.count(); ++i) {
445         failures += process_input(FLAGS_readPath[i], benchmark);
446     }
447
448     if (failures != 0) {
449         SkString err;
450         err.printf("Failed to run %i benchmarks.\n", failures);
451         gLogger.logError(err);
452         return 1;
453     }
454 #if SK_LAZY_CACHE_STATS
455     if (FLAGS_trackDeferredCaching) {
456         SkDebugf("Total cache hit rate: %f\n",
457                  (double) gTotalCacheHits / (gTotalCacheHits + gTotalCacheMisses));
458     }
459 #endif
460     gWriter.end();
461     return 0;
462 }
463
464 #if !defined SK_BUILD_FOR_IOS
465 int main(int argc, char * const argv[]) {
466     return tool_main(argc, (char**) argv);
467 }
468 #endif