static bool run_single_benchmark(const SkString& inputPath, sk_tools::PictureBenchmark& benchmark) { SkFILEStream inputStream; inputStream.setPath(inputPath.c_str()); if (!inputStream.isValid()) { SkString err; err.printf("Could not open file %s\n", inputPath.c_str()); gLogger.logError(err); return false; } bool success = false; SkPicture picture(&inputStream, &success, &SkImageDecoder::DecodeStream); if (!success) { SkString err; err.printf("Could not read an SkPicture from %s\n", inputPath.c_str()); gLogger.logError(err); return false; } SkString filename; sk_tools::get_basename(&filename, inputPath); SkString result; result.printf("running bench [%i %i] %s ", picture.width(), picture.height(), filename.c_str()); gLogger.logProgress(result); benchmark.run(&picture); return true; }
int tool_main(int argc, char** argv) { SkString usage; usage.printf("Time drawing .skp files.\n" "\tPossible arguments for --filter: [%s]\n\t\t[%s]", filterTypesUsage().c_str(), filterFlagsUsage().c_str()); SkCommandLineFlags::SetUsage(usage.c_str()); SkCommandLineFlags::Parse(argc, argv); if (FLAGS_repeat < 1) { SkString error; error.printf("--repeats must be >= 1. Was %i\n", FLAGS_repeat); gLogger.logError(error); exit(-1); } if (FLAGS_logFile.count() == 1) { if (!gLogger.SetLogFile(FLAGS_logFile[0])) { SkString str; str.printf("Could not open %s for writing.\n", FLAGS_logFile[0]); gLogger.logError(str); // TODO(borenet): We're disabling this for now, due to // write-protected Android devices. The very short-term // solution is to ignore the fact that we have no log file. //exit(-1); } } #if SK_ENABLE_INST_COUNT gPrintInstCount = true; #endif SkAutoGraphics ag; sk_tools::PictureBenchmark benchmark; setup_benchmark(&benchmark); int failures = 0; for (int i = 0; i < FLAGS_readPath.count(); ++i) { failures += process_input(FLAGS_readPath[i], benchmark); } if (failures != 0) { SkString err; err.printf("Failed to run %i benchmarks.\n", failures); gLogger.logError(err); return 1; } #if LAZY_CACHE_STATS if (FLAGS_trackDeferredCaching) { SkDebugf("Total cache hit rate: %f\n", (double) gTotalCacheHits / (gTotalCacheHits + gTotalCacheMisses)); } #endif return 0; }
static int process_input(const char* input, sk_tools::PictureBenchmark& benchmark) { SkString inputAsSkString(input); SkOSFile::Iter iter(input, "skp"); SkString inputFilename; int failures = 0; if (iter.next(&inputFilename)) { do { SkString inputPath; sk_tools::make_filepath(&inputPath, inputAsSkString, inputFilename); if (!run_single_benchmark(inputPath, benchmark)) { ++failures; } } while(iter.next(&inputFilename)); } else if (SkStrEndsWith(input, ".skp")) { if (!run_single_benchmark(inputAsSkString, benchmark)) { ++failures; } } else { SkString warning; warning.printf("Warning: skipping %s\n", input); gLogger.logError(warning); } return failures; }
static void run_single_benchmark(const SkString& inputPath, sk_tools::PictureBenchmark& benchmark) { SkFILEStream inputStream; inputStream.setPath(inputPath.c_str()); if (!inputStream.isValid()) { SkString err; err.printf("Could not open file %s\n", inputPath.c_str()); gLogger.logError(err); return; } SkPicture picture(&inputStream); SkString filename; sk_tools::get_basename(&filename, inputPath); SkString result; result.printf("running bench [%i %i] %s ", picture.width(), picture.height(), filename.c_str()); gLogger.logProgress(result); benchmark.run(&picture); }
int tool_main(int argc, char** argv) { #ifdef SK_ENABLE_INST_COUNT gPrintInstCount = true; #endif SkAutoGraphics ag; SkTArray<SkString> inputs; sk_tools::PictureBenchmark benchmark; parse_commandline(argc, argv, &inputs, &benchmark); int failures = 0; for (int i = 0; i < inputs.count(); ++i) { failures += process_input(inputs[i], benchmark); } if (failures != 0) { SkString err; err.printf("Failed to run %i benchmarks.\n", failures); gLogger.logError(err); return 1; } return 0; }
static void setup_benchmark(sk_tools::PictureBenchmark* benchmark) { sk_tools::PictureRenderer::DrawFilterFlags drawFilters[SkDrawFilter::kTypeCount]; sk_bzero(drawFilters, sizeof(drawFilters)); if (FLAGS_filter.count() > 0) { const char* filters = FLAGS_filter[0]; const char* colon = strchr(filters, ':'); if (colon) { int32_t type = -1; size_t typeLen = colon - filters; for (size_t tIndex = 0; tIndex < kFilterTypesCount; ++tIndex) { if (typeLen == strlen(gFilterTypes[tIndex]) && !strncmp(filters, gFilterTypes[tIndex], typeLen)) { type = SkToS32(tIndex); break; } } if (type < 0) { SkString err; err.printf("Unknown type for --filter %s\n", filters); gLogger.logError(err); exit(-1); } int flag = -1; size_t flagLen = strlen(filters) - typeLen - 1; for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) { if (flagLen == strlen(gFilterFlags[fIndex]) && !strncmp(colon + 1, gFilterFlags[fIndex], flagLen)) { flag = 1 << fIndex; break; } } if (flag < 0) { SkString err; err.printf("Unknown flag for --filter %s\n", filters); gLogger.logError(err); exit(-1); } for (int index = 0; index < SkDrawFilter::kTypeCount; ++index) { if (type != SkDrawFilter::kTypeCount && index != type) { continue; } drawFilters[index] = (sk_tools::PictureRenderer::DrawFilterFlags) (drawFilters[index] | flag); } } else { SkString err; err.printf("Unknown arg for --filter %s : missing colon\n", filters); gLogger.logError(err); exit(-1); } } if (FLAGS_timers.count() > 0) { size_t index = 0; bool timerWall = false; bool truncatedTimerWall = false; bool timerCpu = false; bool truncatedTimerCpu = false; bool timerGpu = false; while (index < strlen(FLAGS_timers[0])) { switch (FLAGS_timers[0][index]) { case 'w': timerWall = true; break; case 'c': timerCpu = true; break; case 'W': truncatedTimerWall = true; break; case 'C': truncatedTimerCpu = true; break; case 'g': timerGpu = true; break; default: SkDebugf("mystery character\n"); break; } index++; } benchmark->setTimersToShow(timerWall, truncatedTimerWall, timerCpu, truncatedTimerCpu, timerGpu); } SkString errorString; SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString, kBench_PictureTool)); if (errorString.size() > 0) { gLogger.logError(errorString); } if (NULL == renderer.get()) { exit(-1); } if (FLAGS_timeIndividualTiles) { if (FLAGS_multi > 1) { gLogger.logError("Cannot time individual tiles with more than one thread.\n"); exit(-1); } sk_tools::TiledPictureRenderer* tiledRenderer = renderer->getTiledRenderer(); if (NULL == tiledRenderer) { gLogger.logError("--timeIndividualTiles requires tiled rendering.\n"); exit(-1); } if (!tiledRenderer->supportsTimingIndividualTiles()) { gLogger.logError("This renderer does not support --timeIndividualTiles.\n"); exit(-1); } benchmark->setTimeIndividualTiles(true); } if (FLAGS_readPath.count() < 1) { gLogger.logError(".skp files or directories are required.\n"); exit(-1); } renderer->setDrawFilters(drawFilters, filtersName(drawFilters)); benchmark->setPrintMin(FLAGS_min); benchmark->setLogPerIter(FLAGS_logPerIter); benchmark->setRenderer(renderer); benchmark->setRepeats(FLAGS_repeat); benchmark->setLogger(&gLogger); }
static bool run_single_benchmark(const SkString& inputPath, sk_tools::PictureBenchmark& benchmark) { SkFILEStream inputStream; inputStream.setPath(inputPath.c_str()); if (!inputStream.isValid()) { SkString err; err.printf("Could not open file %s\n", inputPath.c_str()); gLogger.logError(err); return false; } // Since the old picture has been deleted, all pixels should be cleared. SkASSERT(gLruImageCache.getImageCacheUsed() == 0); if (FLAGS_countRAM) { // Set the limit to zero, so all pixels will be kept gLruImageCache.setImageCacheLimit(0); } bool success = false; SkPicture* picture; if (FLAGS_deferImageDecoding) { picture = SkNEW_ARGS(SkPicture, (&inputStream, &success, &lazy_decode_bitmap)); } else { picture = SkNEW_ARGS(SkPicture, (&inputStream, &success, &SkImageDecoder::DecodeMemory)); } SkAutoTDelete<SkPicture> ad(picture); if (!success) { SkString err; err.printf("Could not read an SkPicture from %s\n", inputPath.c_str()); gLogger.logError(err); return false; } SkString filename; sk_tools::get_basename(&filename, inputPath); SkString result; result.printf("running bench [%i %i] %s ", picture->width(), picture->height(), filename.c_str()); gLogger.logProgress(result); benchmark.run(picture); #if LAZY_CACHE_STATS if (FLAGS_trackDeferredCaching) { int32_t cacheHits = SkLazyPixelRef::GetCacheHits(); int32_t cacheMisses = SkLazyPixelRef::GetCacheMisses(); SkLazyPixelRef::ResetCacheStats(); SkString hitString; hitString.printf("Cache hit rate: %f\n", (double) cacheHits / (cacheHits + cacheMisses)); gLogger.logProgress(hitString); gTotalCacheHits += cacheHits; gTotalCacheMisses += cacheMisses; } #endif if (FLAGS_countRAM) { SkString ramCount("RAM used for bitmaps: "); size_t bytes = gLruImageCache.getImageCacheUsed(); if (bytes > 1024) { size_t kb = bytes / 1024; if (kb > 1024) { size_t mb = kb / 1024; ramCount.appendf("%zi MB\n", mb); } else { ramCount.appendf("%zi KB\n", kb); } } else { ramCount.appendf("%zi bytes\n", bytes); } gLogger.logProgress(ramCount); } return true; }
int tool_main(int argc, char** argv) { SkCommandLineFlags::Parse(argc, argv); #if SK_ENABLE_INST_COUNT if (FLAGS_leaks) { gPrintInstCount = true; } #endif SkAutoGraphics ag; // First, parse some flags. SkBenchLogger logger; if (FLAGS_logFile.count()) { logger.SetLogFile(FLAGS_logFile[0]); } LoggerResultsWriter logWriter(logger, FLAGS_timeFormat[0]); MultiResultsWriter writer; writer.add(&logWriter); #ifdef SK_BUILD_JSON_WRITER SkAutoTDelete<JSONResultsWriter> jsonWriter; if (FLAGS_outResultsFile.count()) { jsonWriter.reset(SkNEW(JSONResultsWriter(FLAGS_outResultsFile[0]))); writer.add(jsonWriter.get()); } #endif // Instantiate after all the writers have been added to writer so that we // call close() before their destructors are called on the way out. CallEnd<MultiResultsWriter> ender(writer); const uint8_t alpha = FLAGS_forceBlend ? 0x80 : 0xFF; SkTriState::State dither = SkTriState::kDefault; for (size_t i = 0; i < 3; i++) { if (strcmp(SkTriState::Name[i], FLAGS_forceDither[0]) == 0) { dither = static_cast<SkTriState::State>(i); } } BenchMode benchMode = kNormal_BenchMode; for (size_t i = 0; i < SK_ARRAY_COUNT(BenchMode_Name); i++) { if (strcmp(FLAGS_mode[0], BenchMode_Name[i]) == 0) { benchMode = static_cast<BenchMode>(i); } } SkTDArray<int> configs; bool runDefaultConfigs = false; // Try user-given configs first. for (int i = 0; i < FLAGS_config.count(); i++) { for (int j = 0; j < static_cast<int>(SK_ARRAY_COUNT(gConfigs)); ++j) { if (0 == strcmp(FLAGS_config[i], gConfigs[j].name)) { *configs.append() = j; } else if (0 == strcmp(FLAGS_config[i], kDefaultsConfigStr)) { runDefaultConfigs = true; } } } // If there weren't any, fill in with defaults. if (runDefaultConfigs) { for (int i = 0; i < static_cast<int>(SK_ARRAY_COUNT(gConfigs)); ++i) { if (gConfigs[i].runByDefault) { *configs.append() = i; } } } // Filter out things we can't run. if (kNormal_BenchMode != benchMode) { // Non-rendering configs only run in normal mode for (int i = 0; i < configs.count(); ++i) { const Config& config = gConfigs[configs[i]]; if (SkBenchmark::kNonRendering_Backend == config.backend) { configs.remove(i, 1); --i; } } } // Set the resource path. if (!FLAGS_resourcePath.isEmpty()) { SkBenchmark::SetResourcePath(FLAGS_resourcePath[0]); } #if SK_SUPPORT_GPU for (int i = 0; i < configs.count(); ++i) { const Config& config = gConfigs[configs[i]]; if (SkBenchmark::kGPU_Backend == config.backend) { GrContext* context = gContextFactory.get(config.contextType); if (NULL == context) { SkDebugf("GrContext could not be created for config %s. Config will be skipped.\n", config.name); configs.remove(i); --i; continue; } if (config.sampleCount > context->getMaxSampleCount()){ SkDebugf( "Sample count (%d) for config %s is not supported. Config will be skipped.\n", config.sampleCount, config.name); configs.remove(i); --i; continue; } } } #endif // All flags should be parsed now. Report our settings. if (FLAGS_runOnce) { logger.logError("bench was run with --runOnce, so we're going to hide the times." " It's for your own good!\n"); } writer.option("mode", FLAGS_mode[0]); writer.option("alpha", SkStringPrintf("0x%02X", alpha).c_str()); writer.option("antialias", SkStringPrintf("%d", FLAGS_forceAA).c_str()); writer.option("filter", SkStringPrintf("%d", FLAGS_forceFilter).c_str()); writer.option("dither", SkTriState::Name[dither]); writer.option("rotate", SkStringPrintf("%d", FLAGS_rotate).c_str()); writer.option("scale", SkStringPrintf("%d", FLAGS_scale).c_str()); writer.option("clip", SkStringPrintf("%d", FLAGS_clip).c_str()); #if defined(SK_BUILD_FOR_WIN32) writer.option("system", "WIN32"); #elif defined(SK_BUILD_FOR_MAC) writer.option("system", "MAC"); #elif defined(SK_BUILD_FOR_ANDROID) writer.option("system", "ANDROID"); #elif defined(SK_BUILD_FOR_UNIX) writer.option("system", "UNIX"); #else writer.option("system", "other"); #endif #if defined(SK_DEBUG) writer.option("build", "DEBUG"); #else writer.option("build", "RELEASE"); #endif // Set texture cache limits if non-default. for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); ++i) { #if SK_SUPPORT_GPU const Config& config = gConfigs[i]; if (SkBenchmark::kGPU_Backend != config.backend) { continue; } GrContext* context = gContextFactory.get(config.contextType); if (NULL == context) { continue; } size_t bytes; int count; context->getResourceCacheLimits(&count, &bytes); if (-1 != FLAGS_gpuCacheBytes) { bytes = static_cast<size_t>(FLAGS_gpuCacheBytes); } if (-1 != FLAGS_gpuCacheCount) { count = FLAGS_gpuCacheCount; } context->setResourceCacheLimits(count, bytes); #endif } // Run each bench in each configuration it supports and we asked for. Iter iter; SkBenchmark* bench; while ((bench = iter.next()) != NULL) { SkAutoTUnref<SkBenchmark> benchUnref(bench); if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getName())) { continue; } bench->setForceAlpha(alpha); bench->setForceAA(FLAGS_forceAA); bench->setForceFilter(FLAGS_forceFilter); bench->setDither(dither); AutoPrePostDraw appd(bench); bool loggedBenchName = false; for (int i = 0; i < configs.count(); ++i) { const int configIndex = configs[i]; const Config& config = gConfigs[configIndex]; if (!bench->isSuitableFor(config.backend)) { continue; } GrContext* context = NULL; #if SK_SUPPORT_GPU SkGLContextHelper* glContext = NULL; if (SkBenchmark::kGPU_Backend == config.backend) { context = gContextFactory.get(config.contextType); if (NULL == context) { continue; } glContext = gContextFactory.getGLContext(config.contextType); } #endif SkAutoTUnref<SkCanvas> canvas; SkAutoTUnref<SkPicture> recordFrom; SkPictureRecorder recorderTo; const SkIPoint dim = bench->getSize(); SkAutoTUnref<SkSurface> surface; if (SkBenchmark::kNonRendering_Backend != config.backend) { surface.reset(make_surface(config.fColorType, dim, config.backend, config.sampleCount, context)); if (!surface.get()) { logger.logError(SkStringPrintf( "Device creation failure for config %s. Will skip.\n", config.name)); continue; } switch(benchMode) { case kDeferredSilent_BenchMode: case kDeferred_BenchMode: canvas.reset(SkDeferredCanvas::Create(surface.get())); break; case kRecord_BenchMode: canvas.reset(SkRef(recorderTo.beginRecording(dim.fX, dim.fY))); break; case kPictureRecord_BenchMode: { SkPictureRecorder recorderFrom; bench->draw(1, recorderFrom.beginRecording(dim.fX, dim.fY)); recordFrom.reset(recorderFrom.endRecording()); canvas.reset(SkRef(recorderTo.beginRecording(dim.fX, dim.fY))); break; } case kNormal_BenchMode: canvas.reset(SkRef(surface->getCanvas())); break; default: SkASSERT(false); } } if (NULL != canvas) { canvas->clear(SK_ColorWHITE); if (FLAGS_clip) { perform_clip(canvas, dim.fX, dim.fY); } if (FLAGS_scale) { perform_scale(canvas, dim.fX, dim.fY); } if (FLAGS_rotate) { perform_rotate(canvas, dim.fX, dim.fY); } } if (!loggedBenchName) { loggedBenchName = true; writer.bench(bench->getName(), dim.fX, dim.fY); } #if SK_SUPPORT_GPU SkGLContextHelper* contextHelper = NULL; if (SkBenchmark::kGPU_Backend == config.backend) { contextHelper = gContextFactory.getGLContext(config.contextType); } BenchTimer timer(contextHelper); #else BenchTimer timer; #endif double previous = std::numeric_limits<double>::infinity(); bool converged = false; // variables used to compute loopsPerFrame double frameIntervalTime = 0.0f; int frameIntervalTotalLoops = 0; bool frameIntervalComputed = false; int loopsPerFrame = 0; int loopsPerIter = 0; if (FLAGS_verbose) { SkDebugf("%s %s: ", bench->getName(), config.name); } if (!FLAGS_dryRun) { do { // Ramp up 1 -> 2 -> 4 -> 8 -> 16 -> ... -> ~1 billion. loopsPerIter = (loopsPerIter == 0) ? 1 : loopsPerIter * 2; if (loopsPerIter >= (1<<30) || timer.fWall > FLAGS_maxMs) { // If you find it takes more than a billion loops to get up to 20ms of runtime, // you've got a computer clocked at several THz or have a broken benchmark. ;) // "1B ought to be enough for anybody." logger.logError(SkStringPrintf( "\nCan't get %s %s to converge in %dms (%d loops)", bench->getName(), config.name, FLAGS_maxMs, loopsPerIter)); break; } if ((benchMode == kRecord_BenchMode || benchMode == kPictureRecord_BenchMode)) { // Clear the recorded commands so that they do not accumulate. canvas.reset(SkRef(recorderTo.beginRecording(dim.fX, dim.fY))); } timer.start(); // Inner loop that allows us to break the run into smaller // chunks (e.g. frames). This is especially useful for the GPU // as we can flush and/or swap buffers to keep the GPU from // queuing up too much work. for (int loopCount = loopsPerIter; loopCount > 0; ) { // Save and restore around each call to draw() to guarantee a pristine canvas. SkAutoCanvasRestore saveRestore(canvas, true/*also save*/); int loops; if (frameIntervalComputed && loopCount > loopsPerFrame) { loops = loopsPerFrame; loopCount -= loopsPerFrame; } else { loops = loopCount; loopCount = 0; } if (benchMode == kPictureRecord_BenchMode) { recordFrom->draw(canvas); } else { bench->draw(loops, canvas); } if (kDeferredSilent_BenchMode == benchMode) { static_cast<SkDeferredCanvas*>(canvas.get())->silentFlush(); } else if (NULL != canvas) { canvas->flush(); } #if SK_SUPPORT_GPU // swap drawing buffers on each frame to prevent the GPU // from queuing up too much work if (NULL != glContext) { glContext->swapBuffers(); } #endif } // Stop truncated timers before GL calls complete, and stop the full timers after. timer.truncatedEnd(); #if SK_SUPPORT_GPU if (NULL != glContext) { context->flush(); SK_GL(*glContext, Finish()); } #endif timer.end(); // setup the frame interval for subsequent iterations if (!frameIntervalComputed) { frameIntervalTime += timer.fWall; frameIntervalTotalLoops += loopsPerIter; if (frameIntervalTime >= FLAGS_minMs) { frameIntervalComputed = true; loopsPerFrame = (int)(((double)frameIntervalTotalLoops / frameIntervalTime) * FLAGS_minMs); if (loopsPerFrame < 1) { loopsPerFrame = 1; } // SkDebugf(" %s has %d loops in %f ms (normalized to %d)\n", // bench->getName(), frameIntervalTotalLoops, // timer.fWall, loopsPerFrame); } } const double current = timer.fWall / loopsPerIter; if (FLAGS_verbose && current > previous) { SkDebugf("↑"); } if (FLAGS_verbose) { SkDebugf("%.3g ", current); } converged = HasConverged(previous, current, timer.fWall); previous = current; } while (!FLAGS_runOnce && !converged); } if (FLAGS_verbose) { SkDebugf("\n"); } if (!FLAGS_dryRun && FLAGS_outDir.count() && SkBenchmark::kNonRendering_Backend != config.backend) { SkAutoTUnref<SkImage> image(surface->newImageSnapshot()); if (image.get()) { saveFile(bench->getName(), config.name, FLAGS_outDir[0], image); } } if (FLAGS_runOnce) { // Let's not mislead ourselves by looking at Debug build or single iteration bench times! continue; } // Normalize to ms per 1000 iterations. const double normalize = 1000.0 / loopsPerIter; const struct { char shortName; const char* longName; double ms; } times[] = { {'w', "msecs", normalize * timer.fWall}, {'W', "Wmsecs", normalize * timer.fTruncatedWall}, {'c', "cmsecs", normalize * timer.fCpu}, {'C', "Cmsecs", normalize * timer.fTruncatedCpu}, {'g', "gmsecs", normalize * timer.fGpu}, }; writer.config(config.name); for (size_t i = 0; i < SK_ARRAY_COUNT(times); i++) { if (strchr(FLAGS_timers[0], times[i].shortName) && times[i].ms > 0) { writer.timer(times[i].longName, times[i].ms); } } } } #if SK_SUPPORT_GPU gContextFactory.destroyContexts(); #endif return 0; }
static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs, sk_tools::PictureBenchmark*& benchmark) { const char* argv0 = argv[0]; char* const* stop = argv + argc; int repeats = DEFAULT_REPEATS; sk_tools::PictureRenderer::SkDeviceTypes deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; // Create a string to show our current settings. // TODO: Make it prettier. Currently it just repeats the command line. SkString commandLine("bench_pictures:"); for (int i = 1; i < argc; i++) { commandLine.appendf(" %s", *(argv+i)); } commandLine.append("\n"); bool usePipe = false; bool multiThreaded = false; bool useTiles = false; const char* widthString = NULL; const char* heightString = NULL; bool isPowerOf2Mode = false; const char* mode = NULL; for (++argv; argv < stop; ++argv) { if (0 == strcmp(*argv, "--repeat")) { ++argv; if (argv < stop) { repeats = atoi(*argv); if (repeats < 1) { SkDELETE(benchmark); gLogger.logError("--repeat must be given a value > 0\n"); exit(-1); } } else { SkDELETE(benchmark); gLogger.logError("Missing arg for --repeat\n"); usage(argv0); exit(-1); } } else if (0 == strcmp(*argv, "--pipe")) { usePipe = true; } else if (0 == strcmp(*argv, "--logFile")) { argv++; if (argv < stop) { if (!gLogger.SetLogFile(*argv)) { SkString str; str.printf("Could not open %s for writing.", *argv); gLogger.logError(str); usage(argv0); exit(-1); } } else { gLogger.logError("Missing arg for --logFile\n"); usage(argv0); exit(-1); } } else if (0 == strcmp(*argv, "--mode")) { SkDELETE(benchmark); ++argv; if (argv >= stop) { gLogger.logError("Missing mode for --mode\n"); usage(argv0); exit(-1); } if (0 == strcmp(*argv, "record")) { benchmark = SkNEW(sk_tools::RecordPictureBenchmark); } else if (0 == strcmp(*argv, "simple")) { benchmark = SkNEW(sk_tools::SimplePictureBenchmark); } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))) { useTiles = true; mode = *argv; if (0 == strcmp(*argv, "pow2tile")) { isPowerOf2Mode = true; } ++argv; if (argv >= stop) { SkString err; err.printf("Missing width for --mode %s\n", mode); gLogger.logError(err); usage(argv0); exit(-1); } widthString = *argv; ++argv; if (argv >= stop) { gLogger.logError("Missing height for --mode tile\n"); usage(argv0); exit(-1); } heightString = *argv; ++argv; if (argv < stop && 0 == strcmp(*argv, "multi")) { multiThreaded = true; } else { --argv; } } else if (0 == strcmp(*argv, "playbackCreation")) { benchmark = SkNEW(sk_tools::PlaybackCreationBenchmark); } else { SkString err; err.printf("%s is not a valid mode for --mode\n", *argv); gLogger.logError(err); usage(argv0); exit(-1); } } else if (0 == strcmp(*argv, "--device")) { ++argv; if (argv >= stop) { gLogger.logError("Missing mode for --deivce\n"); usage(argv0); exit(-1); } if (0 == strcmp(*argv, "bitmap")) { deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; } #if SK_SUPPORT_GPU else if (0 == strcmp(*argv, "gpu")) { deviceType = sk_tools::PictureRenderer::kGPU_DeviceType; } #endif else { SkString err; err.printf("%s is not a valid mode for --device\n", *argv); gLogger.logError(err); usage(argv0); exit(-1); } } else if (0 == strcmp(*argv, "--help") || 0 == strcmp(*argv, "-h")) { SkDELETE(benchmark); usage(argv0); exit(0); } else { inputs->push_back(SkString(*argv)); } } if (useTiles) { sk_tools::TiledPictureBenchmark* tileBenchmark = SkNEW(sk_tools::TiledPictureBenchmark); if (isPowerOf2Mode) { int minWidth = atoi(widthString); if (!SkIsPow2(minWidth) || minWidth < 0) { SkDELETE(tileBenchmark); SkString err; err.printf("--mode %s must be given a width" " value that is a power of two\n", mode); gLogger.logError(err); exit(-1); } tileBenchmark->setTileMinPowerOf2Width(minWidth); } else if (sk_tools::is_percentage(widthString)) { tileBenchmark->setTileWidthPercentage(atof(widthString)); if (!(tileBenchmark->getTileWidthPercentage() > 0)) { SkDELETE(tileBenchmark); gLogger.logError("--mode tile must be given a width percentage > 0\n"); exit(-1); } } else { tileBenchmark->setTileWidth(atoi(widthString)); if (!(tileBenchmark->getTileWidth() > 0)) { SkDELETE(tileBenchmark); gLogger.logError("--mode tile must be given a width > 0\n"); exit(-1); } } if (sk_tools::is_percentage(heightString)) { tileBenchmark->setTileHeightPercentage(atof(heightString)); if (!(tileBenchmark->getTileHeightPercentage() > 0)) { SkDELETE(tileBenchmark); gLogger.logError("--mode tile must be given a height percentage > 0\n"); exit(-1); } } else { tileBenchmark->setTileHeight(atoi(heightString)); if (!(tileBenchmark->getTileHeight() > 0)) { SkDELETE(tileBenchmark); gLogger.logError("--mode tile must be given a height > 0\n"); exit(-1); } } tileBenchmark->setThreading(multiThreaded); tileBenchmark->setUsePipe(usePipe); benchmark = tileBenchmark; } else if (usePipe) { SkDELETE(benchmark); benchmark = SkNEW(sk_tools::PipePictureBenchmark); } if (inputs->count() < 1) { SkDELETE(benchmark); usage(argv0); exit(-1); } if (NULL == benchmark) { benchmark = SkNEW(sk_tools::SimplePictureBenchmark); } benchmark->setRepeats(repeats); benchmark->setDeviceType(deviceType); benchmark->setLogger(&gLogger); // Report current settings: gLogger.logProgress(commandLine); }
static bool run_single_benchmark(const SkString& inputPath, sk_tools::PictureBenchmark& benchmark) { SkFILEStream inputStream; inputStream.setPath(inputPath.c_str()); if (!inputStream.isValid()) { SkString err; err.printf("Could not open file %s\n", inputPath.c_str()); gLogger.logError(err); return false; } SkDiscardableMemoryPool* pool = SkGetGlobalDiscardableMemoryPool(); // Since the old picture has been deleted, all pixels should be cleared. SkASSERT(pool->getRAMUsed() == 0); if (FLAGS_countRAM) { pool->setRAMBudget(SK_MaxU32); // Set the limit to max, so all pixels will be kept } SkPicture::InstallPixelRefProc proc; if (FLAGS_deferImageDecoding) { proc = &sk_tools::LazyDecodeBitmap; } else { proc = &SkImageDecoder::DecodeMemory; } SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromStream(&inputStream, proc)); if (NULL == picture.get()) { SkString err; err.printf("Could not read an SkPicture from %s\n", inputPath.c_str()); gLogger.logError(err); return false; } SkString filename; sk_tools::get_basename(&filename, inputPath); SkString result; result.printf("running bench [%i %i] %s ", picture->width(), picture->height(), filename.c_str()); gLogger.logProgress(result); benchmark.run(picture); #if LAZY_CACHE_STATS if (FLAGS_trackDeferredCaching) { int32_t cacheHits = pool->fCacheHits; int32_t cacheMisses = pool->fCacheMisses; pool->fCacheHits = pool->fCacheMisses = 0; SkString hitString; hitString.printf("Cache hit rate: %f\n", (double) cacheHits / (cacheHits + cacheMisses)); gLogger.logProgress(hitString); gTotalCacheHits += cacheHits; gTotalCacheMisses += cacheMisses; } #endif if (FLAGS_countRAM) { SkString ramCount("RAM used for bitmaps: "); size_t bytes = pool->getRAMUsed(); if (bytes > 1024) { size_t kb = bytes / 1024; if (kb > 1024) { size_t mb = kb / 1024; ramCount.appendf("%zi MB\n", mb); } else { ramCount.appendf("%zi KB\n", kb); } } else { ramCount.appendf("%zi bytes\n", bytes); } gLogger.logProgress(ramCount); } return true; }
static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs, sk_tools::PictureBenchmark* benchmark) { const char* argv0 = argv[0]; char* const* stop = argv + argc; int repeats = DEFAULT_REPEATS; sk_tools::PictureRenderer::SkDeviceTypes deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; SkAutoTUnref<sk_tools::PictureRenderer> renderer(NULL); // Create a string to show our current settings. // TODO: Make it prettier. Currently it just repeats the command line. SkString commandLine("bench_pictures:"); for (int i = 1; i < argc; i++) { commandLine.appendf(" %s", *(argv+i)); } commandLine.append("\n"); bool usePipe = false; int numThreads = 1; bool useTiles = false; const char* widthString = NULL; const char* heightString = NULL; int gridWidth = 0; int gridHeight = 0; bool isPowerOf2Mode = false; bool isCopyMode = false; const char* xTilesString = NULL; const char* yTilesString = NULL; const char* mode = NULL; bool gridSupported = false; sk_tools::PictureRenderer::BBoxHierarchyType bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType; sk_tools::PictureRenderer::DrawFilterFlags drawFilters[SkDrawFilter::kTypeCount]; sk_bzero(drawFilters, sizeof(drawFilters)); SkISize viewport; viewport.setEmpty(); for (++argv; argv < stop; ++argv) { if (0 == strcmp(*argv, "--repeat")) { ++argv; if (argv < stop) { repeats = atoi(*argv); if (repeats < 1) { gLogger.logError("--repeat must be given a value > 0\n"); PRINT_USAGE_AND_EXIT; } } else { gLogger.logError("Missing arg for --repeat\n"); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--pipe")) { usePipe = true; } else if (0 == strcmp(*argv, "--logFile")) { argv++; if (argv < stop) { if (!gLogger.SetLogFile(*argv)) { SkString str; str.printf("Could not open %s for writing.", *argv); gLogger.logError(str); usage(argv0); // TODO(borenet): We're disabling this for now, due to // write-protected Android devices. The very short-term // solution is to ignore the fact that we have no log file. //exit(-1); } } else { gLogger.logError("Missing arg for --logFile\n"); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--multi")) { ++argv; if (argv >= stop) { gLogger.logError("Missing arg for --multi\n"); PRINT_USAGE_AND_EXIT; } numThreads = atoi(*argv); if (numThreads < 2) { gLogger.logError("Number of threads must be at least 2.\n"); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--bbh")) { ++argv; if (argv >= stop) { gLogger.logError("Missing value for --bbh\n"); PRINT_USAGE_AND_EXIT; } if (0 == strcmp(*argv, "none")) { bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType; } else if (0 == strcmp(*argv, "rtree")) { bbhType = sk_tools::PictureRenderer::kRTree_BBoxHierarchyType; } else if (0 == strcmp(*argv, "grid")) { bbhType = sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType; ++argv; if (argv >= stop) { gLogger.logError("Missing width for --bbh grid\n"); PRINT_USAGE_AND_EXIT; } gridWidth = atoi(*argv); ++argv; if (argv >= stop) { gLogger.logError("Missing height for --bbh grid\n"); PRINT_USAGE_AND_EXIT; } gridHeight = atoi(*argv); } else { SkString err; err.printf("%s is not a valid value for --bbhType\n", *argv); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--mode")) { if (renderer.get() != NULL) { SkDebugf("Cannot combine modes.\n"); PRINT_USAGE_AND_EXIT; } ++argv; if (argv >= stop) { gLogger.logError("Missing mode for --mode\n"); PRINT_USAGE_AND_EXIT; } if (0 == strcmp(*argv, "record")) { renderer.reset(SkNEW(sk_tools::RecordPictureRenderer)); gridSupported = true; } else if (0 == strcmp(*argv, "clone")) { renderer.reset(sk_tools::CreatePictureCloneRenderer()); } else if (0 == strcmp(*argv, "simple")) { renderer.reset(SkNEW(sk_tools::SimplePictureRenderer)); } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile")) || 0 == strcmp(*argv, "copyTile")) { useTiles = true; mode = *argv; if (0 == strcmp(*argv, "pow2tile")) { isPowerOf2Mode = true; } else if (0 == strcmp(*argv, "copyTile")) { isCopyMode = true; } else { gridSupported = true; } ++argv; if (argv >= stop) { SkString err; err.printf("Missing width for --mode %s\n", mode); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } widthString = *argv; ++argv; if (argv >= stop) { SkString err; err.appendf("Missing height for --mode %s\n", mode); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } heightString = *argv; } else if (0 == strcmp(*argv, "playbackCreation")) { renderer.reset(SkNEW(sk_tools::PlaybackCreationRenderer)); gridSupported = true; } else if (0 == strcmp(*argv, "gatherPixelRefs")) { renderer.reset(sk_tools::CreateGatherPixelRefsRenderer()); } else { SkString err; err.printf("%s is not a valid mode for --mode\n", *argv); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--viewport")) { ++argv; if (argv >= stop) { gLogger.logError("Missing width for --viewport\n"); PRINT_USAGE_AND_EXIT; } viewport.fWidth = atoi(*argv); ++argv; if (argv >= stop) { gLogger.logError("Missing height for --viewport\n"); PRINT_USAGE_AND_EXIT; } viewport.fHeight = atoi(*argv); } else if (0 == strcmp(*argv, "--tiles")) { ++argv; if (argv >= stop) { gLogger.logError("Missing x for --tiles\n"); PRINT_USAGE_AND_EXIT; } xTilesString = *argv; ++argv; if (argv >= stop) { gLogger.logError("Missing y for --tiles\n"); PRINT_USAGE_AND_EXIT; } yTilesString = *argv; } else if (0 == strcmp(*argv, "--device")) { ++argv; if (argv >= stop) { gLogger.logError("Missing mode for --device\n"); PRINT_USAGE_AND_EXIT; } if (0 == strcmp(*argv, "bitmap")) { deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; } #if SK_SUPPORT_GPU else if (0 == strcmp(*argv, "gpu")) { deviceType = sk_tools::PictureRenderer::kGPU_DeviceType; } #endif else { SkString err; err.printf("%s is not a valid mode for --device\n", *argv); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--timers")) { ++argv; if (argv < stop) { bool timerWall = false; bool truncatedTimerWall = false; bool timerCpu = false; bool truncatedTimerCpu = false; bool timerGpu = false; for (char* t = *argv; *t; ++t) { switch (*t) { case 'w': timerWall = true; break; case 'c': timerCpu = true; break; case 'W': truncatedTimerWall = true; break; case 'C': truncatedTimerCpu = true; break; case 'g': timerGpu = true; break; default: { break; } } } benchmark->setTimersToShow(timerWall, truncatedTimerWall, timerCpu, truncatedTimerCpu, timerGpu); } else { gLogger.logError("Missing arg for --timers\n"); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--timeIndividualTiles")) { benchmark->setTimeIndividualTiles(true); } else if (0 == strcmp(*argv, "--min")) { benchmark->setPrintMin(true); } else if (0 == strcmp(*argv, "--logPerIter")) { ++argv; if (argv < stop) { bool log = atoi(*argv) != 0; benchmark->setLogPerIter(log); } else { gLogger.logError("Missing arg for --logPerIter\n"); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--filter")) { ++argv; if (argv < stop) { const char* colon = strchr(*argv, ':'); if (colon) { int type = -1; size_t typeLen = colon - *argv; for (size_t tIndex = 0; tIndex < kFilterTypesCount; ++tIndex) { if (typeLen == strlen(gFilterTypes[tIndex]) && !strncmp(*argv, gFilterTypes[tIndex], typeLen)) { type = tIndex; break; } } if (type < 0) { SkString err; err.printf("Unknown type for --filter %s\n", *argv); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } int flag = -1; size_t flagLen = strlen(*argv) - typeLen - 1; for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) { if (flagLen == strlen(gFilterFlags[fIndex]) && !strncmp(colon + 1, gFilterFlags[fIndex], flagLen)) { flag = 1 << fIndex; break; } } if (flag < 0) { SkString err; err.printf("Unknown flag for --filter %s\n", *argv); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } for (int index = 0; index < SkDrawFilter::kTypeCount; ++index) { if (type != SkDrawFilter::kTypeCount && index != type) { continue; } drawFilters[index] = (sk_tools::PictureRenderer::DrawFilterFlags) (drawFilters[index] | flag); } } else { SkString err; err.printf("Unknown arg for --filter %s : missing colon\n", *argv); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } } else { gLogger.logError("Missing arg for --filter\n"); PRINT_USAGE_AND_EXIT; } } else if (0 == strcmp(*argv, "--help") || 0 == strcmp(*argv, "-h")) { PRINT_USAGE_AND_EXIT; } else { inputs->push_back(SkString(*argv)); } } if (numThreads > 1 && !useTiles) { gLogger.logError("Multithreaded drawing requires tiled rendering.\n"); PRINT_USAGE_AND_EXIT; } if (usePipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) { gLogger.logError("--pipe and --bbh cannot be used together\n"); PRINT_USAGE_AND_EXIT; } if (sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType == bbhType && !gridSupported) { gLogger.logError("'--bbh grid' is not compatible with specified --mode.\n"); PRINT_USAGE_AND_EXIT; } if (useTiles) { SkASSERT(NULL == renderer); sk_tools::TiledPictureRenderer* tiledRenderer; if (isCopyMode) { int x, y; if (xTilesString != NULL) { SkASSERT(yTilesString != NULL); x = atoi(xTilesString); y = atoi(yTilesString); if (x <= 0 || y <= 0) { gLogger.logError("--tiles must be given values > 0\n"); PRINT_USAGE_AND_EXIT; } } else { x = y = 4; } tiledRenderer = SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y)); if (benchmark->timeIndividualTiles()) { gLogger.logError("timeIndividualTiles is not compatible with copyTile\n"); PRINT_USAGE_AND_EXIT; } } else if (numThreads > 1) { tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads)); } else { tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer); } if (isPowerOf2Mode) { int minWidth = atoi(widthString); if (!SkIsPow2(minWidth) || minWidth < 0) { tiledRenderer->unref(); SkString err; err.printf("-mode %s must be given a width" " value that is a power of two\n", mode); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } tiledRenderer->setTileMinPowerOf2Width(minWidth); } else if (sk_tools::is_percentage(widthString)) { if (isCopyMode) { tiledRenderer->unref(); SkString err; err.printf("--mode %s does not support percentages.\n", mode); gLogger.logError(err.c_str()); PRINT_USAGE_AND_EXIT; } tiledRenderer->setTileWidthPercentage(atof(widthString)); if (!(tiledRenderer->getTileWidthPercentage() > 0)) { tiledRenderer->unref(); SkString err; err.appendf("--mode %s must be given a width percentage > 0\n", mode); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } } else { tiledRenderer->setTileWidth(atoi(widthString)); if (!(tiledRenderer->getTileWidth() > 0)) { tiledRenderer->unref(); SkString err; err.appendf("--mode %s must be given a width > 0\n", mode); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } } if (sk_tools::is_percentage(heightString)) { if (isCopyMode) { tiledRenderer->unref(); SkString err; err.printf("--mode %s does not support percentages.\n", mode); gLogger.logError(err.c_str()); PRINT_USAGE_AND_EXIT; } tiledRenderer->setTileHeightPercentage(atof(heightString)); if (!(tiledRenderer->getTileHeightPercentage() > 0)) { tiledRenderer->unref(); SkString err; err.appendf("--mode %s must be given a height percentage > 0\n", mode); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } } else { tiledRenderer->setTileHeight(atoi(heightString)); if (!(tiledRenderer->getTileHeight() > 0)) { tiledRenderer->unref(); SkString err; err.appendf("--mode %s must be given a height > 0\n", mode); gLogger.logError(err); PRINT_USAGE_AND_EXIT; } } if (numThreads > 1) { #if SK_SUPPORT_GPU if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) { tiledRenderer->unref(); gLogger.logError("GPU not compatible with multithreaded tiling.\n"); PRINT_USAGE_AND_EXIT; } #endif } renderer.reset(tiledRenderer); if (usePipe) { gLogger.logError("Pipe rendering is currently not compatible with tiling.\n" "Turning off pipe.\n"); } } else { if (benchmark->timeIndividualTiles()) { gLogger.logError("timeIndividualTiles requires tiled rendering.\n"); PRINT_USAGE_AND_EXIT; } if (usePipe) { if (renderer.get() != NULL) { gLogger.logError("Pipe is incompatible with other modes.\n"); PRINT_USAGE_AND_EXIT; } renderer.reset(SkNEW(sk_tools::PipePictureRenderer)); } } if (inputs->count() < 1) { PRINT_USAGE_AND_EXIT; } if (NULL == renderer) { renderer.reset(SkNEW(sk_tools::SimplePictureRenderer)); } renderer->setBBoxHierarchyType(bbhType); renderer->setDrawFilters(drawFilters, filtersName(drawFilters)); renderer->setGridSize(gridWidth, gridHeight); renderer->setViewport(viewport); benchmark->setRenderer(renderer); benchmark->setRepeats(repeats); benchmark->setDeviceType(deviceType); benchmark->setLogger(&gLogger); // Report current settings: gLogger.logProgress(commandLine); }