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