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