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