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