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_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 tiles, 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 --deferImageDecoding and "
     54             "SK_LAZY_CACHE_STATS set to true. Report percentage of cache hits when 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(preprocess, false, "If true, perform device specific preprocessing before timing.");
     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(gFilterTypes[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(gFilterFlags[0]);
    108 
    109 static SkString filtersName(sk_tools::PictureRenderer::DrawFilterFlags* drawFilters) {
    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, proc));
    197 
    198     if (NULL == 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     if (FLAGS_preprocess) {
    206         // Because the GPU preprocessing step relies on the in-memory picture
    207         // statistics we need to rerecord the picture here
    208         SkPictureRecorder recorder;
    209         picture->playback(recorder.beginRecording(picture->cullRect().width(),
    210                                                   picture->cullRect().height(),
    211                                                   NULL, 0));
    212         picture.reset(recorder.endRecording());
    213     }
    214 
    215     SkString filename = SkOSPath::Basename(inputPath.c_str());
    216 
    217     gWriter.bench(filename.c_str(),
    218                   SkScalarCeilToInt(picture->cullRect().width()),
    219                   SkScalarCeilToInt(picture->cullRect().height()));
    220 
    221     benchmark.run(picture);
    222 
    223 #if SK_LAZY_CACHE_STATS
    224     if (FLAGS_trackDeferredCaching) {
    225         int cacheHits = pool->getCacheHits();
    226         int cacheMisses = pool->getCacheMisses();
    227         pool->resetCacheHitsAndMisses();
    228         SkString hitString;
    229         hitString.printf("Cache hit rate: %f\n", (double) cacheHits / (cacheHits + cacheMisses));
    230         gLogger.logProgress(hitString);
    231         gTotalCacheHits += cacheHits;
    232         gTotalCacheMisses += cacheMisses;
    233     }
    234 #endif
    235     if (FLAGS_countRAM) {
    236         SkString ramCount("RAM used for bitmaps: ");
    237         size_t bytes = pool->getRAMUsed();
    238         if (bytes > 1024) {
    239             size_t kb = bytes / 1024;
    240             if (kb > 1024) {
    241                 size_t mb = kb / 1024;
    242                 ramCount.appendf("%zi MB\n", mb);
    243             } else {
    244                 ramCount.appendf("%zi KB\n", kb);
    245             }
    246         } else {
    247             ramCount.appendf("%zi bytes\n", bytes);
    248         }
    249         gLogger.logProgress(ramCount);
    250     }
    251 
    252     return true;
    253 }
    254 
    255 static void setup_benchmark(sk_tools::PictureBenchmark* benchmark) {
    256     sk_tools::PictureRenderer::DrawFilterFlags drawFilters[SkDrawFilter::kTypeCount];
    257     sk_bzero(drawFilters, sizeof(drawFilters));
    258 
    259     if (FLAGS_filter.count() > 0) {
    260         const char* filters = FLAGS_filter[0];
    261         const char* colon = strchr(filters, ':');
    262         if (colon) {
    263             int32_t type = -1;
    264             size_t typeLen = colon - filters;
    265             for (size_t tIndex = 0; tIndex < kFilterTypesCount; ++tIndex) {
    266                 if (typeLen == strlen(gFilterTypes[tIndex])
    267                         && !strncmp(filters, gFilterTypes[tIndex], typeLen)) {
    268                     type = SkToS32(tIndex);
    269                     break;
    270                 }
    271             }
    272             if (type < 0) {
    273                 SkString err;
    274                 err.printf("Unknown type for --filter %s\n", filters);
    275                 gLogger.logError(err);
    276                 exit(-1);
    277             }
    278             int flag = -1;
    279             size_t flagLen = strlen(filters) - typeLen - 1;
    280             for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) {
    281                 if (flagLen == strlen(gFilterFlags[fIndex])
    282                         && !strncmp(colon + 1, gFilterFlags[fIndex], flagLen)) {
    283                     flag = 1 << fIndex;
    284                     break;
    285                 }
    286             }
    287             if (flag < 0) {
    288                 SkString err;
    289                 err.printf("Unknown flag for --filter %s\n", filters);
    290                 gLogger.logError(err);
    291                 exit(-1);
    292             }
    293             for (int index = 0; index < SkDrawFilter::kTypeCount; ++index) {
    294                 if (type != SkDrawFilter::kTypeCount && index != type) {
    295                     continue;
    296                 }
    297                 drawFilters[index] = (sk_tools::PictureRenderer::DrawFilterFlags)
    298                         (drawFilters[index] | flag);
    299             }
    300         } else {
    301             SkString err;
    302             err.printf("Unknown arg for --filter %s : missing colon\n", filters);
    303             gLogger.logError(err);
    304             exit(-1);
    305         }
    306     }
    307 
    308     if (FLAGS_timers.count() > 0) {
    309         size_t index = 0;
    310         bool timerWall = false;
    311         bool truncatedTimerWall = false;
    312         bool timerCpu = false;
    313         bool truncatedTimerCpu = false;
    314         bool timerGpu = false;
    315         while (index < strlen(FLAGS_timers[0])) {
    316             switch (FLAGS_timers[0][index]) {
    317                 case 'w':
    318                     timerWall = true;
    319                     break;
    320                 case 'c':
    321                     timerCpu = true;
    322                     break;
    323                 case 'W':
    324                     truncatedTimerWall = true;
    325                     break;
    326                 case 'C':
    327                     truncatedTimerCpu = true;
    328                     break;
    329                 case 'g':
    330                     timerGpu = true;
    331                     break;
    332                 default:
    333                     SkDebugf("mystery character\n");
    334                     break;
    335             }
    336             index++;
    337         }
    338         benchmark->setTimersToShow(timerWall, truncatedTimerWall, timerCpu, truncatedTimerCpu,
    339                                   timerGpu);
    340     }
    341 
    342     SkString errorString;
    343     SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString,
    344                                                                    kBench_PictureTool));
    345 
    346     if (errorString.size() > 0) {
    347         gLogger.logError(errorString);
    348     }
    349 
    350     if (NULL == renderer.get()) {
    351         exit(-1);
    352     }
    353 
    354     if (FLAGS_timeIndividualTiles) {
    355         sk_tools::TiledPictureRenderer* tiledRenderer = renderer->getTiledRenderer();
    356         if (NULL == tiledRenderer) {
    357             gLogger.logError("--timeIndividualTiles requires tiled rendering.\n");
    358             exit(-1);
    359         }
    360         if (!tiledRenderer->supportsTimingIndividualTiles()) {
    361             gLogger.logError("This renderer does not support --timeIndividualTiles.\n");
    362             exit(-1);
    363         }
    364         benchmark->setTimeIndividualTiles(true);
    365     }
    366 
    367     benchmark->setPurgeDecodedTex(FLAGS_purgeDecodedTex);
    368     benchmark->setPreprocess(FLAGS_preprocess);
    369 
    370     if (FLAGS_readPath.count() < 1) {
    371         gLogger.logError(".skp files or directories are required.\n");
    372         exit(-1);
    373     }
    374 
    375     renderer->setDrawFilters(drawFilters, filtersName(drawFilters));
    376     if (FLAGS_logPerIter) {
    377         benchmark->setTimerResultType(TimerData::kPerIter_Result);
    378     } else if (FLAGS_min) {
    379         benchmark->setTimerResultType(TimerData::kMin_Result);
    380     } else {
    381         benchmark->setTimerResultType(TimerData::kAvg_Result);
    382     }
    383     benchmark->setRenderer(renderer);
    384     benchmark->setRepeats(FLAGS_repeat);
    385     benchmark->setWriter(&gWriter);
    386 }
    387 
    388 static int process_input(const char* input,
    389                          sk_tools::PictureBenchmark& benchmark) {
    390     SkString inputAsSkString(input);
    391     SkOSFile::Iter iter(input, "skp");
    392     SkString inputFilename;
    393     int failures = 0;
    394     if (iter.next(&inputFilename)) {
    395         do {
    396             SkString inputPath = SkOSPath::Join(input, inputFilename.c_str());
    397             if (!run_single_benchmark(inputPath, benchmark)) {
    398                 ++failures;
    399             }
    400         } while(iter.next(&inputFilename));
    401     } else if (SkStrEndsWith(input, ".skp")) {
    402         if (!run_single_benchmark(inputAsSkString, benchmark)) {
    403             ++failures;
    404         }
    405     } else {
    406         SkString warning;
    407         warning.printf("Warning: skipping %s\n", input);
    408         gLogger.logError(warning);
    409     }
    410     return failures;
    411 }
    412 
    413 int tool_main(int argc, char** argv);
    414 int tool_main(int argc, char** argv) {
    415     SetupCrashHandler();
    416     SkString usage;
    417     usage.printf("Time drawing .skp files.\n"
    418                  "\tPossible arguments for --filter: [%s]\n\t\t[%s]",
    419                  filterTypesUsage().c_str(), filterFlagsUsage().c_str());
    420     SkCommandLineFlags::SetUsage(usage.c_str());
    421     SkCommandLineFlags::Parse(argc, argv);
    422 
    423     if (FLAGS_repeat < 1) {
    424         SkString error;
    425         error.printf("--repeats must be >= 1. Was %i\n", FLAGS_repeat);
    426         gLogger.logError(error);
    427         exit(-1);
    428     }
    429 
    430     if (FLAGS_logFile.count() == 1) {
    431         if (!gLogger.SetLogFile(FLAGS_logFile[0])) {
    432             SkString str;
    433             str.printf("Could not open %s for writing.\n", FLAGS_logFile[0]);
    434             gLogger.logError(str);
    435             // TODO(borenet): We're disabling this for now, due to
    436             // write-protected Android devices.  The very short-term
    437             // solution is to ignore the fact that we have no log file.
    438             //exit(-1);
    439         }
    440     }
    441 
    442     SkAutoTDelete<PictureJSONResultsWriter> jsonWriter;
    443     if (FLAGS_jsonLog.count() == 1) {
    444         SkASSERT(FLAGS_builderName.count() == 1 && FLAGS_gitHash.count() == 1);
    445         jsonWriter.reset(SkNEW(PictureJSONResultsWriter(
    446                         FLAGS_jsonLog[0],
    447                         FLAGS_builderName[0],
    448                         FLAGS_buildNumber,
    449                         FLAGS_timestamp,
    450                         FLAGS_gitHash[0],
    451                         FLAGS_gitNumber)));
    452         gWriter.add(jsonWriter.get());
    453     }
    454 
    455     gWriter.add(&gLogWriter);
    456 
    457 
    458 #if SK_ENABLE_INST_COUNT
    459     gPrintInstCount = true;
    460 #endif
    461     SkAutoGraphics ag;
    462 
    463     sk_tools::PictureBenchmark benchmark;
    464 
    465     setup_benchmark(&benchmark);
    466 
    467     int failures = 0;
    468     for (int i = 0; i < FLAGS_readPath.count(); ++i) {
    469         failures += process_input(FLAGS_readPath[i], benchmark);
    470     }
    471 
    472     if (failures != 0) {
    473         SkString err;
    474         err.printf("Failed to run %i benchmarks.\n", failures);
    475         gLogger.logError(err);
    476         return 1;
    477     }
    478 #if SK_LAZY_CACHE_STATS
    479     if (FLAGS_trackDeferredCaching) {
    480         SkDebugf("Total cache hit rate: %f\n",
    481                  (double) gTotalCacheHits / (gTotalCacheHits + gTotalCacheMisses));
    482     }
    483 #endif
    484 
    485 #if GR_GPU_STATS
    486     if (FLAGS_gpuStats && benchmark.renderer()->isUsingGpuDevice()) {
    487         GrContext* ctx = benchmark.renderer()->getGrContext();
    488         SkDebugf("RenderTarget Binds: %d\n", ctx->gpuStats()->renderTargetBinds());
    489         SkDebugf("Shader Compilations: %d\n", ctx->gpuStats()->shaderCompilations());
    490     }
    491 #endif
    492 
    493     gWriter.end();
    494     return 0;
    495 }
    496 
    497 #if !defined SK_BUILD_FOR_IOS
    498 int main(int argc, char * const argv[]) {
    499     return tool_main(argc, (char**) argv);
    500 }
    501 #endif
    502