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