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