| 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 |