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