blob: 4255773146e7f682e47b9889e97d2d9b018420a6 [file] [log] [blame]
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "PictureRenderingFlags.h"
#include "CopyTilesRenderer.h"
#include "PictureRenderer.h"
#include "picture_utils.h"
#include "SkBitmapFactory.h"
#include "SkCommandLineFlags.h"
#include "SkData.h"
#include "SkImage.h"
#include "SkImageDecoder.h"
#include "SkLruImageCache.h"
#include "SkPurgeableImageCache.h"
#include "SkString.h"
// Alphabetized list of flags used by this file or bench_ and render_pictures.
DEFINE_string(bbh, "none", "bbhType [width height]: Set the bounding box hierarchy type to "
"be used. Accepted values are: none, rtree, grid. "
"Not compatible with --pipe. With value "
"'grid', width and height must be specified. 'grid' can "
"only be used with modes tile, record, and "
"playbackCreation.");
// Although this config does not support all the same options as gm, the names should be kept
// consistent.
#if SK_ANGLE
// ANGLE assumes GPU
DEFINE_string(config, "8888", "[8888|gpu|msaa4|msaa16|angle]: Use the corresponding config.");
#elif SK_SUPPORT_GPU
DEFINE_string(config, "8888", "[8888|gpu|msaa4|msaa16]: Use the corresponding config.");
#else
DEFINE_string(config, "8888", "[8888]: Use the corresponding config.");
#endif
DEFINE_bool(deferImageDecoding, false, "Defer decoding until drawing images. "
"Has no effect if the provided skp does not have its images encoded.");
DEFINE_string(mode, "simple", "Run in the corresponding mode:\n"
"simple: Simple rendering.\n"
"tile width height: Use tiles with the given dimensions or percentages.\n"
"pow2tile minWidth height: Use tiles with widths that are all a power\n"
"\tof two such that they minimize the amount of wasted tile space.\n"
"\tminWidth must be a power of two.\n"
"copyTile width height: Draw the picture, then copy into tiles. If the\n"
"\tpicture is large enough, it is broken into larger tiles to avoid\n"
"\tcreating a large canvas.\n"
// TODO: If bench_pictures and render_pictures were two separate targets, we could use build flags
// to determine which modes to display.
"record: (Only in bench_pictures) Time recording from a picture to a new\n"
"\tpicture.\n"
"playbackCreation: (Only in bench_pictures) Time creation of the \n"
"\tSkPicturePlayback.\n"
"rerecord: (Only in render_pictures) Record the picture as a new skp,\n"
"\twith the bitmaps PNG encoded.\n");
DEFINE_int32(multi, 1, "Set the number of threads for multi threaded drawing. "
"If > 1, requires tiled rendering.");
DEFINE_bool(pipe, false, "Use SkGPipe rendering. Currently incompatible with \"mode\".");
DEFINE_string2(readPath, r, "", "skp files or directories of skp files to process.");
DEFINE_double(scale, 1, "Set the scale factor.");
DEFINE_string(tiles, "", "Used with --mode copyTile to specify number of tiles per larger tile "
"in the x and y directions.");
DEFINE_bool(useVolatileCache, false, "Use a volatile cache for deferred image decoding pixels. "
"Only meaningful if --deferImageDecoding is set to true and the platform has an "
"implementation.");
DEFINE_string(viewport, "", "width height: Set the viewport.");
sk_tools::PictureRenderer* parseRenderer(SkString& error, PictureTool tool) {
error.reset();
if (FLAGS_multi <= 0) {
error.printf("--multi must be > 0, was %i", FLAGS_multi);
return NULL;
}
bool useTiles = false;
const char* widthString = NULL;
const char* heightString = NULL;
bool isPowerOf2Mode = false;
bool isCopyMode = false;
const char* mode = NULL;
bool gridSupported = false;
SkAutoTUnref<sk_tools::PictureRenderer> renderer;
if (FLAGS_mode.count() >= 1) {
mode = FLAGS_mode[0];
if (0 == strcmp(mode, "record")) {
renderer.reset(SkNEW(sk_tools::RecordPictureRenderer));
gridSupported = true;
// undocumented
} else if (0 == strcmp(mode, "clone")) {
renderer.reset(sk_tools::CreatePictureCloneRenderer());
} else if (0 == strcmp(mode, "tile") || 0 == strcmp(mode, "pow2tile")
|| 0 == strcmp(mode, "copyTile")) {
useTiles = true;
if (0 == strcmp(mode, "pow2tile")) {
isPowerOf2Mode = true;
} else if (0 == strcmp(mode, "copyTile")) {
isCopyMode = true;
} else {
gridSupported = true;
}
if (FLAGS_mode.count() < 2) {
error.printf("Missing width for --mode %s\n", mode);
return NULL;
}
widthString = FLAGS_mode[1];
if (FLAGS_mode.count() < 3) {
error.printf("Missing height for --mode %s\n", mode);
return NULL;
}
heightString = FLAGS_mode[2];
} else if (0 == strcmp(mode, "playbackCreation") && kBench_PictureTool == tool) {
renderer.reset(SkNEW(sk_tools::PlaybackCreationRenderer));
gridSupported = true;
// undocumented
} else if (0 == strcmp(mode, "gatherPixelRefs") && kBench_PictureTool == tool) {
renderer.reset(sk_tools::CreateGatherPixelRefsRenderer());
} else if (0 == strcmp(mode, "rerecord") && kRender_PictureTool == tool) {
renderer.reset(SkNEW(sk_tools::RecordPictureRenderer));
// Allow 'mode' to be set to 'simple', but do not create a renderer, so we can
// ensure that pipe does not override a mode besides simple. The renderer will
// be created below.
} else if (0 != strcmp(mode, "simple")) {
error.printf("%s is not a valid mode for --mode\n", mode);
return NULL;
}
}
if (useTiles) {
SkASSERT(NULL == renderer);
SkAutoTUnref<sk_tools::TiledPictureRenderer> tiledRenderer;
if (isCopyMode) {
int xTiles = -1;
int yTiles = -1;
if (FLAGS_tiles.count() > 0) {
if (FLAGS_tiles.count() != 2) {
error.printf("--tiles requires an x value and a y value.\n");
return NULL;
}
xTiles = atoi(FLAGS_tiles[0]);
yTiles = atoi(FLAGS_tiles[1]);
}
int x, y;
if (xTiles != -1 && yTiles != -1) {
x = xTiles;
y = yTiles;
if (x <= 0 || y <= 0) {
error.printf("--tiles must be given values > 0\n");
return NULL;
}
} else {
x = y = 4;
}
tiledRenderer.reset(SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y)));
} else if (FLAGS_multi > 1) {
tiledRenderer.reset(SkNEW_ARGS(sk_tools::MultiCorePictureRenderer,
(FLAGS_multi)));
} else {
tiledRenderer.reset(SkNEW(sk_tools::TiledPictureRenderer));
}
if (isPowerOf2Mode) {
int minWidth = atoi(widthString);
if (!SkIsPow2(minWidth) || minWidth < 0) {
SkString err;
error.printf("-mode %s must be given a width"
" value that is a power of two\n", mode);
return NULL;
}
tiledRenderer->setTileMinPowerOf2Width(minWidth);
} else if (sk_tools::is_percentage(widthString)) {
if (isCopyMode) {
error.printf("--mode %s does not support percentages.\n", mode);
return NULL;
}
tiledRenderer->setTileWidthPercentage(atof(widthString));
if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
error.printf("--mode %s must be given a width percentage > 0\n", mode);
return NULL;
}
} else {
tiledRenderer->setTileWidth(atoi(widthString));
if (!(tiledRenderer->getTileWidth() > 0)) {
error.printf("--mode %s must be given a width > 0\n", mode);
return NULL;
}
}
if (sk_tools::is_percentage(heightString)) {
if (isCopyMode) {
error.printf("--mode %s does not support percentages.\n", mode);
return NULL;
}
tiledRenderer->setTileHeightPercentage(atof(heightString));
if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
error.printf("--mode %s must be given a height percentage > 0\n", mode);
return NULL;
}
} else {
tiledRenderer->setTileHeight(atoi(heightString));
if (!(tiledRenderer->getTileHeight() > 0)) {
SkString err;
error.printf("--mode %s must be given a height > 0\n", mode);
return NULL;
}
}
renderer.reset(tiledRenderer.detach());
if (FLAGS_pipe) {
error.printf("Pipe rendering is currently not compatible with tiling.\n"
"Turning off pipe.\n");
}
} else { // useTiles
if (FLAGS_multi > 1) {
error.printf("Multithreaded drawing requires tiled rendering.\n");
return NULL;
}
if (FLAGS_pipe) {
if (renderer != NULL) {
error.printf("Pipe is incompatible with other modes.\n");
return NULL;
}
renderer.reset(SkNEW(sk_tools::PipePictureRenderer));
}
}
if (NULL == renderer) {
renderer.reset(SkNEW(sk_tools::SimplePictureRenderer));
}
if (FLAGS_viewport.count() > 0) {
if (FLAGS_viewport.count() != 2) {
error.printf("--viewport requires a width and a height.\n");
return NULL;
}
SkISize viewport;
viewport.fWidth = atoi(FLAGS_viewport[0]);
viewport.fHeight = atoi(FLAGS_viewport[1]);
renderer->setViewport(viewport);
}
sk_tools::PictureRenderer::SkDeviceTypes deviceType =
sk_tools::PictureRenderer::kBitmap_DeviceType;
#if SK_SUPPORT_GPU
int sampleCount = 0;
#endif
if (FLAGS_config.count() > 0) {
if (0 == strcmp(FLAGS_config[0], "8888")) {
deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType;
}
#if SK_SUPPORT_GPU
else if (0 == strcmp(FLAGS_config[0], "gpu")) {
deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
if (FLAGS_multi > 1) {
error.printf("GPU not compatible with multithreaded tiling.\n");
return NULL;
}
}
else if (0 == strcmp(FLAGS_config[0], "msaa4")) {
deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
if (FLAGS_multi > 1) {
error.printf("GPU not compatible with multithreaded tiling.\n");
return NULL;
}
sampleCount = 4;
}
else if (0 == strcmp(FLAGS_config[0], "msaa16")) {
deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
if (FLAGS_multi > 1) {
error.printf("GPU not compatible with multithreaded tiling.\n");
return NULL;
}
sampleCount = 16;
}
#if SK_ANGLE
else if (0 == strcmp(FLAGS_config[0], "angle")) {
deviceType = sk_tools::PictureRenderer::kAngle_DeviceType;
if (FLAGS_multi > 1) {
error.printf("Angle not compatible with multithreaded tiling.\n");
return NULL;
}
}
#endif
#endif
else {
error.printf("%s is not a valid mode for --config\n", FLAGS_config[0]);
return NULL;
}
renderer->setDeviceType(deviceType);
#if SK_SUPPORT_GPU
renderer->setSampleCount(sampleCount);
#endif
}
sk_tools::PictureRenderer::BBoxHierarchyType bbhType
= sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
if (FLAGS_bbh.count() > 0) {
const char* type = FLAGS_bbh[0];
if (0 == strcmp(type, "none")) {
bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
} else if (0 == strcmp(type, "rtree")) {
bbhType = sk_tools::PictureRenderer::kRTree_BBoxHierarchyType;
} else if (0 == strcmp(type, "grid")) {
if (!gridSupported) {
error.printf("'--bbh grid' is not compatible with --mode=%s.\n", mode);
return NULL;
}
bbhType = sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType;
if (FLAGS_bbh.count() != 3) {
error.printf("--bbh grid requires a width and a height.\n");
return NULL;
}
int gridWidth = atoi(FLAGS_bbh[1]);
int gridHeight = atoi(FLAGS_bbh[2]);
renderer->setGridSize(gridWidth, gridHeight);
} else {
error.printf("%s is not a valid value for --bbhType\n", type);
return NULL;
}
if (FLAGS_pipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) {
error.printf("--pipe and --bbh cannot be used together\n");
return NULL;
}
}
renderer->setBBoxHierarchyType(bbhType);
renderer->setScaleFactor(SkDoubleToScalar(FLAGS_scale));
return renderer.detach();
}
SkLruImageCache gLruImageCache(1024*1024);
// Simple cache selector to choose between a purgeable cache for large images and the standard one
// for smaller images.
class MyCacheSelector : public SkBitmapFactory::CacheSelector {
public:
MyCacheSelector() {
fPurgeableImageCache = SkPurgeableImageCache::Create();
}
~MyCacheSelector() {
SkSafeUnref(fPurgeableImageCache);
}
virtual SkImageCache* selectCache(const SkImage::Info& info) SK_OVERRIDE {
if (info.fWidth * info.fHeight > 32 * 1024 && fPurgeableImageCache != NULL) {
return fPurgeableImageCache;
}
return &gLruImageCache;
}
private:
SkImageCache* fPurgeableImageCache;
};
static MyCacheSelector gCacheSelector;
static SkBitmapFactory gFactory(&SkImageDecoder::DecodeMemoryToTarget);
bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap);
bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap) {
void* copiedBuffer = sk_malloc_throw(size);
memcpy(copiedBuffer, buffer, size);
SkAutoDataUnref data(SkData::NewFromMalloc(copiedBuffer, size));
static bool gOnce;
if (!gOnce) {
// Only use the cache selector if there is a purgeable image cache to use for large
// images.
if (FLAGS_useVolatileCache && SkAutoTUnref<SkImageCache>(
SkPurgeableImageCache::Create()).get() != NULL) {
gFactory.setCacheSelector(&gCacheSelector);
} else {
gFactory.setImageCache(&gLruImageCache);
}
gOnce = true;
}
return gFactory.installPixelRef(data, bitmap);
}