blob: 29046aec7150c3fb5383772310cc1c38734c904f [file] [log] [blame]
scroggo@google.com161e1ba2013-03-04 16:41:06 +00001/*
2 * Copyright 2013 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 "PictureRenderingFlags.h"
9
10#include "CopyTilesRenderer.h"
11#include "PictureRenderer.h"
12#include "picture_utils.h"
13
scroggo@google.combb281f72013-03-18 21:37:39 +000014#include "SkBitmapFactory.h"
15#include "SkData.h"
scroggo@google.com161e1ba2013-03-04 16:41:06 +000016#include "SkFlags.h"
scroggo@google.combb281f72013-03-18 21:37:39 +000017#include "SkImage.h"
18#include "SkImageDecoder.h"
19#include "SkLruImageCache.h"
20#include "SkPurgeableImageCache.h"
21#include "SkString.h"
scroggo@google.com161e1ba2013-03-04 16:41:06 +000022
23// Alphabetized list of flags used by this file or bench_ and render_pictures.
24DEFINE_string(bbh, "none", "bbhType [width height]: Set the bounding box hierarchy type to "
25 "be used. Accepted values are: none, rtree, grid. "
26 "Not compatible with --pipe. With value "
27 "'grid', width and height must be specified. 'grid' can "
28 "only be used with modes tile, record, and "
29 "playbackCreation.");
30// Although this config does not support all the same options as gm, the names should be kept
31// consistent.
32#if SK_ANGLE
33// ANGLE assumes GPU
34DEFINE_string(config, "8888", "[8888|gpu|angle]: Use the corresponding config.");
35#elif SK_SUPPORT_GPU
36DEFINE_string(config, "8888", "[8888|gpu]: Use the corresponding config.");
37#else
38DEFINE_string(config, "8888", "[8888]: Use the corresponding config.");
39#endif
40
41DEFINE_bool(deferImageDecoding, false, "Defer decoding until drawing images. "
42 "Has no effect if the provided skp does not have its images encoded.");
43DEFINE_string(mode, "simple", "Run in the corresponding mode:\n"
44 "simple: Simple rendering.\n"
45 "tile width height: Use tiles with the given dimensions or percentages.\n"
46 "pow2tile minWidth height: Use tiles with widths that are all a power\n"
47 "\tof two such that they minimize the amount of wasted tile space.\n"
48 "\tminWidth must be a power of two.\n"
49 "copyTile width height: Draw the picture, then copy into tiles. If the\n"
50 "\tpicture is large enough, it is broken into larger tiles to avoid\n"
51 "\tcreating a large canvas.\n"
52// TODO: If bench_pictures and render_pictures were two separate targets, we could use build flags
53// to determine which modes to display.
54 "record: (Only in bench_pictures) Time recording from a picture to a new\n"
55 "\tpicture.\n"
56 "playbackCreation: (Only in bench_pictures) Time creation of the \n"
57 "\tSkPicturePlayback.\n"
58 "rerecord: (Only in render_pictures) Record the picture as a new skp,\n"
59 "\twith the bitmaps PNG encoded.\n");
60DEFINE_int32(multi, 1, "Set the number of threads for multi threaded drawing. "
61 "If > 1, requires tiled rendering.");
62DEFINE_bool(pipe, false, "Use SkGPipe rendering. Currently incompatible with \"mode\".");
63DEFINE_string(r, "", "skp files or directories of skp files to process.");
64DEFINE_double(scale, 1, "Set the scale factor.");
65DEFINE_string(tiles, "", "Used with --mode copyTile to specify number of tiles per larger tile "
66 "in the x and y directions.");
scroggo@google.combb281f72013-03-18 21:37:39 +000067DEFINE_bool(useVolatileCache, false, "Use a volatile cache for deferred image decoding pixels. "
68 "Only meaningful if --deferImageDecoding is set to true and the platform has an "
69 "implementation.");
scroggo@google.com161e1ba2013-03-04 16:41:06 +000070DEFINE_string(viewport, "", "width height: Set the viewport.");
71
72sk_tools::PictureRenderer* parseRenderer(SkString& error, PictureTool tool) {
73 error.reset();
74
75 if (FLAGS_multi <= 0) {
76 error.printf("--multi must be > 0, was %i", FLAGS_multi);
77 return NULL;
78 }
79
80 bool useTiles = false;
81 const char* widthString = NULL;
82 const char* heightString = NULL;
83 bool isPowerOf2Mode = false;
84 bool isCopyMode = false;
85 const char* mode = NULL;
86 bool gridSupported = false;
87
88 SkAutoTUnref<sk_tools::PictureRenderer> renderer;
89 if (FLAGS_mode.count() >= 1) {
90 mode = FLAGS_mode[0];
91 if (0 == strcmp(mode, "record")) {
92 renderer.reset(SkNEW(sk_tools::RecordPictureRenderer));
93 gridSupported = true;
94 // undocumented
95 } else if (0 == strcmp(mode, "clone")) {
96 renderer.reset(sk_tools::CreatePictureCloneRenderer());
97 } else if (0 == strcmp(mode, "tile") || 0 == strcmp(mode, "pow2tile")
98 || 0 == strcmp(mode, "copyTile")) {
99 useTiles = true;
100
101 if (0 == strcmp(mode, "pow2tile")) {
102 isPowerOf2Mode = true;
103 } else if (0 == strcmp(mode, "copyTile")) {
104 isCopyMode = true;
105 } else {
106 gridSupported = true;
107 }
108
109 if (FLAGS_mode.count() < 2) {
110 error.printf("Missing width for --mode %s\n", mode);
111 return NULL;
112 }
113
114 widthString = FLAGS_mode[1];
115 if (FLAGS_mode.count() < 3) {
116 error.printf("Missing height for --mode %s\n", mode);
117 return NULL;
118 }
119
120 heightString = FLAGS_mode[2];
121 } else if (0 == strcmp(mode, "playbackCreation") && kBench_PictureTool == tool) {
122 renderer.reset(SkNEW(sk_tools::PlaybackCreationRenderer));
123 gridSupported = true;
124 // undocumented
125 } else if (0 == strcmp(mode, "gatherPixelRefs") && kBench_PictureTool == tool) {
126 renderer.reset(sk_tools::CreateGatherPixelRefsRenderer());
127 } else if (0 == strcmp(mode, "rerecord") && kRender_PictureTool == tool) {
128 renderer.reset(SkNEW(sk_tools::RecordPictureRenderer));
129 // Allow 'mode' to be set to 'simple', but do not create a renderer, so we can
130 // ensure that pipe does not override a mode besides simple. The renderer will
131 // be created below.
132 } else if (0 != strcmp(mode, "simple")) {
133 error.printf("%s is not a valid mode for --mode\n", mode);
134 return NULL;
135 }
136 }
137
138 if (useTiles) {
139 SkASSERT(NULL == renderer);
140 SkAutoTUnref<sk_tools::TiledPictureRenderer> tiledRenderer;
141 if (isCopyMode) {
142 int xTiles = -1;
143 int yTiles = -1;
144 if (FLAGS_tiles.count() > 0) {
145 if (FLAGS_tiles.count() != 2) {
146 error.printf("--tiles requires an x value and a y value.\n");
147 return NULL;
148 }
149 xTiles = atoi(FLAGS_tiles[0]);
150 yTiles = atoi(FLAGS_tiles[1]);
151 }
152
153 int x, y;
154 if (xTiles != -1 && yTiles != -1) {
155 x = xTiles;
156 y = yTiles;
157 if (x <= 0 || y <= 0) {
158 error.printf("--tiles must be given values > 0\n");
159 return NULL;
160 }
161 } else {
162 x = y = 4;
163 }
164 tiledRenderer.reset(SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y)));
165 } else if (FLAGS_multi > 1) {
166 tiledRenderer.reset(SkNEW_ARGS(sk_tools::MultiCorePictureRenderer,
167 (FLAGS_multi)));
168 } else {
169 tiledRenderer.reset(SkNEW(sk_tools::TiledPictureRenderer));
170 }
171
172 if (isPowerOf2Mode) {
173 int minWidth = atoi(widthString);
174 if (!SkIsPow2(minWidth) || minWidth < 0) {
175 SkString err;
176 error.printf("-mode %s must be given a width"
177 " value that is a power of two\n", mode);
178 return NULL;
179 }
180 tiledRenderer->setTileMinPowerOf2Width(minWidth);
181 } else if (sk_tools::is_percentage(widthString)) {
182 if (isCopyMode) {
183 error.printf("--mode %s does not support percentages.\n", mode);
184 return NULL;
185 }
186 tiledRenderer->setTileWidthPercentage(atof(widthString));
187 if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
188 error.printf("--mode %s must be given a width percentage > 0\n", mode);
189 return NULL;
190 }
191 } else {
192 tiledRenderer->setTileWidth(atoi(widthString));
193 if (!(tiledRenderer->getTileWidth() > 0)) {
194 error.printf("--mode %s must be given a width > 0\n", mode);
195 return NULL;
196 }
197 }
198
199 if (sk_tools::is_percentage(heightString)) {
200 if (isCopyMode) {
201 error.printf("--mode %s does not support percentages.\n", mode);
202 return NULL;
203 }
204 tiledRenderer->setTileHeightPercentage(atof(heightString));
205 if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
206 error.printf("--mode %s must be given a height percentage > 0\n", mode);
207 return NULL;
208 }
209 } else {
210 tiledRenderer->setTileHeight(atoi(heightString));
211 if (!(tiledRenderer->getTileHeight() > 0)) {
212 SkString err;
213 error.printf("--mode %s must be given a height > 0\n", mode);
214 return NULL;
215 }
216 }
217
218 renderer.reset(tiledRenderer.detach());
219 if (FLAGS_pipe) {
220 error.printf("Pipe rendering is currently not compatible with tiling.\n"
221 "Turning off pipe.\n");
222 }
223
224 } else { // useTiles
225 if (FLAGS_multi > 1) {
226 error.printf("Multithreaded drawing requires tiled rendering.\n");
227 return NULL;
228 }
229 if (FLAGS_pipe) {
230 if (renderer != NULL) {
231 error.printf("Pipe is incompatible with other modes.\n");
232 return NULL;
233 }
234 renderer.reset(SkNEW(sk_tools::PipePictureRenderer));
235 }
236 }
237
238 if (NULL == renderer) {
239 renderer.reset(SkNEW(sk_tools::SimplePictureRenderer));
240 }
241
242 if (FLAGS_viewport.count() > 0) {
243 if (FLAGS_viewport.count() != 2) {
244 error.printf("--viewport requires a width and a height.\n");
245 return NULL;
246 }
247 SkISize viewport;
248 viewport.fWidth = atoi(FLAGS_viewport[0]);
249 viewport.fHeight = atoi(FLAGS_viewport[1]);
250 renderer->setViewport(viewport);
251 }
252
253 sk_tools::PictureRenderer::SkDeviceTypes deviceType =
254 sk_tools::PictureRenderer::kBitmap_DeviceType;
255 if (FLAGS_config.count() > 0) {
256 if (0 == strcmp(FLAGS_config[0], "8888")) {
257 deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType;
258 }
259#if SK_SUPPORT_GPU
260 else if (0 == strcmp(FLAGS_config[0], "gpu")) {
261 deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
262 if (FLAGS_multi > 1) {
263 error.printf("GPU not compatible with multithreaded tiling.\n");
264 return NULL;
265 }
266 }
267#if SK_ANGLE
268 else if (0 == strcmp(FLAGS_config[0], "angle")) {
269 deviceType = sk_tools::PictureRenderer::kAngle_DeviceType;
270 if (FLAGS_multi > 1) {
271 error.printf("Angle not compatible with multithreaded tiling.\n");
272 return NULL;
273 }
274 }
275#endif
276#endif
277 else {
278 error.printf("%s is not a valid mode for --config\n", FLAGS_config[0]);
279 return NULL;
280 }
281 renderer->setDeviceType(deviceType);
282 }
283
284
285 sk_tools::PictureRenderer::BBoxHierarchyType bbhType
286 = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
287 if (FLAGS_bbh.count() > 0) {
288 const char* type = FLAGS_bbh[0];
289 if (0 == strcmp(type, "none")) {
290 bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
291 } else if (0 == strcmp(type, "rtree")) {
292 bbhType = sk_tools::PictureRenderer::kRTree_BBoxHierarchyType;
293 } else if (0 == strcmp(type, "grid")) {
294 if (!gridSupported) {
295 error.printf("'--bbh grid' is not compatible with --mode=%s.\n", mode);
296 return NULL;
297 }
298 bbhType = sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType;
299 if (FLAGS_bbh.count() != 3) {
300 error.printf("--bbh grid requires a width and a height.\n");
301 return NULL;
302 }
303 int gridWidth = atoi(FLAGS_bbh[1]);
304 int gridHeight = atoi(FLAGS_bbh[2]);
305 renderer->setGridSize(gridWidth, gridHeight);
306
307 } else {
308 error.printf("%s is not a valid value for --bbhType\n", type);
309 return NULL;
310 }
311 if (FLAGS_pipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) {
312 error.printf("--pipe and --bbh cannot be used together\n");
313 return NULL;
314 }
315 }
316 renderer->setBBoxHierarchyType(bbhType);
317 renderer->setScaleFactor(SkDoubleToScalar(FLAGS_scale));
318
319 return renderer.detach();
320}
scroggo@google.combb281f72013-03-18 21:37:39 +0000321
322SkLruImageCache gLruImageCache(1024*1024);
323
324// Simple cache selector to choose between a purgeable cache for large images and the standard one
325// for smaller images.
326class MyCacheSelector : public SkBitmapFactory::CacheSelector {
327
328public:
329 MyCacheSelector() {
330 fPurgeableImageCache = SkPurgeableImageCache::Create();
331 }
332
333 ~MyCacheSelector() {
334 SkSafeUnref(fPurgeableImageCache);
335 }
336
337 virtual SkImageCache* selectCache(const SkImage::Info& info) SK_OVERRIDE {
338 if (info.fWidth * info.fHeight > 32 * 1024 && fPurgeableImageCache != NULL) {
339 return fPurgeableImageCache;
340 }
341 return &gLruImageCache;
342 }
343private:
344 SkImageCache* fPurgeableImageCache;
345};
346
347static MyCacheSelector gCacheSelector;
348static SkBitmapFactory gFactory(&SkImageDecoder::DecodeMemoryToTarget);
349
350bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap);
351bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap) {
352 void* copiedBuffer = sk_malloc_throw(size);
353 memcpy(copiedBuffer, buffer, size);
354 SkAutoDataUnref data(SkData::NewFromMalloc(copiedBuffer, size));
355
356 static bool gOnce;
357 if (!gOnce) {
358 // Only use the cache selector if there is a purgeable image cache to use for large
359 // images.
360 if (FLAGS_useVolatileCache && SkAutoTUnref<SkImageCache>(
361 SkPurgeableImageCache::Create()).get() != NULL) {
362 gFactory.setCacheSelector(&gCacheSelector);
363 } else {
364 gFactory.setImageCache(&gLruImageCache);
365 }
366 gOnce = true;
367 }
368 return gFactory.installPixelRef(data, bitmap);
369}