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.
20 DEFINE_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
29 static const char kGpuAPINameGL[] = "gl";
30 static const char kGpuAPINameGLES[] = "gles";
31 #define GPU_CONFIG_STRING "|gpu|msaa4|msaa16|nvprmsaa4|nvprmsaa16|gpudft"
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.
48 DEFINE_string(config, "8888", "["
49               "8888" GPU_CONFIG_STRING ANGLE_CONFIG_STRING MESA_CONFIG_STRING
50               "]: Use the corresponding config.");
51 
52 DEFINE_bool(deferImageDecoding, false, "Defer decoding until drawing images. "
53             "Has no effect if the provided skp does not have its images encoded.");
54 DEFINE_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");
71 DEFINE_bool(pipe, false, "Use SkGPipe rendering. Currently incompatible with \"mode\".");
72 DEFINE_string2(readPath, r, "", "skp files or directories of skp files to process.");
73 DEFINE_double(scale, 1, "Set the scale factor.");
74 DEFINE_string(tiles, "", "Used with --mode copyTile to specify number of tiles per larger tile "
75               "in the x and y directions.");
76 DEFINE_string(viewport, "", "width height: Set the viewport.");
77 #if SK_SUPPORT_GPU
78 DEFINE_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.");
82 DEFINE_bool(gpuCompressAlphaMasks, false, "Compress masks generated from falling back to "
83                                           "software path rendering.");
84 #endif
85 
parseRenderer(SkString & error,PictureTool tool)86 sk_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 
96 #if SK_SUPPORT_GPU
97     GrContext::Options grContextOpts;
98     grContextOpts.fDrawPathToCompressedTexture = FLAGS_gpuCompressAlphaMasks;
99   #define RENDERER_ARGS (grContextOpts)
100 #else
101   #define RENDERER_ARGS ()
102 #endif
103 
104     SkAutoTUnref<sk_tools::PictureRenderer> renderer;
105     if (FLAGS_mode.count() >= 1) {
106         mode = FLAGS_mode[0];
107         if (0 == strcmp(mode, "record")) {
108             renderer.reset(SkNEW_ARGS(sk_tools::RecordPictureRenderer, RENDERER_ARGS));
109         } else if (0 == strcmp(mode, "tile") || 0 == strcmp(mode, "pow2tile")
110                    || 0 == strcmp(mode, "copyTile")) {
111             useTiles = true;
112 
113             if (0 == strcmp(mode, "pow2tile")) {
114                 isPowerOf2Mode = true;
115             } else if (0 == strcmp(mode, "copyTile")) {
116                 isCopyMode = true;
117             }
118 
119             if (FLAGS_mode.count() < 2) {
120                 error.printf("Missing width for --mode %s\n", mode);
121                 return NULL;
122             }
123 
124             widthString = FLAGS_mode[1];
125             if (FLAGS_mode.count() < 3) {
126                 error.printf("Missing height for --mode %s\n", mode);
127                 return NULL;
128             }
129 
130             heightString = FLAGS_mode[2];
131         } else if (0 == strcmp(mode, "playbackCreation") && kBench_PictureTool == tool) {
132             renderer.reset(SkNEW_ARGS(sk_tools::PlaybackCreationRenderer, RENDERER_ARGS));
133         // undocumented
134         } else if (0 == strcmp(mode, "rerecord") && kRender_PictureTool == tool) {
135             renderer.reset(SkNEW_ARGS(sk_tools::RecordPictureRenderer, RENDERER_ARGS));
136         } else if (0 == strcmp(mode, "simple")) {
137             // Allow 'mode' to be set to 'simple', but do not create a renderer, so we can
138             // ensure that pipe does not override a mode besides simple. The renderer will
139             // be created below.
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 #if SK_SUPPORT_GPU
173             tiledRenderer.reset(SkNEW_ARGS(sk_tools::CopyTilesRenderer, (grContextOpts, x, y)));
174 #else
175             tiledRenderer.reset(SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y)));
176 #endif
177         } else {
178             tiledRenderer.reset(SkNEW_ARGS(sk_tools::TiledPictureRenderer, RENDERER_ARGS));
179         }
180 
181         if (isPowerOf2Mode) {
182             int minWidth = atoi(widthString);
183             if (!SkIsPow2(minWidth) || minWidth < 0) {
184                 SkString err;
185                 error.printf("-mode %s must be given a width"
186                              " value that is a power of two\n", mode);
187                 return NULL;
188             }
189             tiledRenderer->setTileMinPowerOf2Width(minWidth);
190         } else if (sk_tools::is_percentage(widthString)) {
191             if (isCopyMode) {
192                 error.printf("--mode %s does not support percentages.\n", mode);
193                 return NULL;
194             }
195             tiledRenderer->setTileWidthPercentage(atof(widthString));
196             if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
197                 error.printf("--mode %s must be given a width percentage > 0\n", mode);
198                 return NULL;
199             }
200         } else {
201             tiledRenderer->setTileWidth(atoi(widthString));
202             if (!(tiledRenderer->getTileWidth() > 0)) {
203                 error.printf("--mode %s must be given a width > 0\n", mode);
204                 return NULL;
205             }
206         }
207 
208         if (sk_tools::is_percentage(heightString)) {
209             if (isCopyMode) {
210                 error.printf("--mode %s does not support percentages.\n", mode);
211                 return NULL;
212             }
213             tiledRenderer->setTileHeightPercentage(atof(heightString));
214             if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
215                 error.printf("--mode %s must be given a height percentage > 0\n", mode);
216                 return NULL;
217             }
218         } else {
219             tiledRenderer->setTileHeight(atoi(heightString));
220             if (!(tiledRenderer->getTileHeight() > 0)) {
221                 SkString err;
222                 error.printf("--mode %s must be given a height > 0\n", mode);
223                 return NULL;
224             }
225         }
226 
227         renderer.reset(tiledRenderer.detach());
228         if (FLAGS_pipe) {
229             error.printf("Pipe rendering is currently not compatible with tiling.\n"
230                          "Turning off pipe.\n");
231         }
232 
233     } else { // useTiles
234         if (FLAGS_pipe) {
235             if (renderer != NULL) {
236                 error.printf("Pipe is incompatible with other modes.\n");
237                 return NULL;
238             }
239             renderer.reset(SkNEW_ARGS(sk_tools::PipePictureRenderer, RENDERER_ARGS));
240         }
241     }
242 
243     if (NULL == renderer) {
244         renderer.reset(SkNEW_ARGS(sk_tools::SimplePictureRenderer, RENDERER_ARGS));
245     }
246 
247     if (FLAGS_viewport.count() > 0) {
248         if (FLAGS_viewport.count() != 2) {
249             error.printf("--viewport requires a width and a height.\n");
250             return NULL;
251         }
252         SkISize viewport;
253         viewport.fWidth = atoi(FLAGS_viewport[0]);
254         viewport.fHeight = atoi(FLAGS_viewport[1]);
255         renderer->setViewport(viewport);
256     }
257 
258     sk_tools::PictureRenderer::SkDeviceTypes deviceType =
259         sk_tools::PictureRenderer::kBitmap_DeviceType;
260 #if SK_SUPPORT_GPU
261     GrGLStandard gpuAPI = kNone_GrGLStandard;
262     if (1 == FLAGS_gpuAPI.count()) {
263         if (FLAGS_gpuAPI.contains(kGpuAPINameGL)) {
264             gpuAPI = kGL_GrGLStandard;
265         } else if (FLAGS_gpuAPI.contains(kGpuAPINameGLES)) {
266             gpuAPI = kGLES_GrGLStandard;
267         } else {
268             error.printf("--gpuAPI invalid api value.\n");
269             return NULL;
270         }
271     } else if (FLAGS_gpuAPI.count() > 1) {
272         error.printf("--gpuAPI invalid api value.\n");
273         return NULL;
274     }
275 
276     int sampleCount = 0;
277     bool useDFText = false;
278 #endif
279     if (FLAGS_config.count() > 0) {
280         if (0 == strcmp(FLAGS_config[0], "8888")) {
281             deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType;
282         }
283 #if SK_SUPPORT_GPU
284         else if (0 == strcmp(FLAGS_config[0], "gpu")) {
285             deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
286         }
287         else if (0 == strcmp(FLAGS_config[0], "msaa4")) {
288             deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
289             sampleCount = 4;
290         }
291         else if (0 == strcmp(FLAGS_config[0], "msaa16")) {
292             deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
293             sampleCount = 16;
294         }
295         else if (0 == strcmp(FLAGS_config[0], "nvprmsaa4")) {
296             deviceType = sk_tools::PictureRenderer::kNVPR_DeviceType;
297             sampleCount = 4;
298         }
299         else if (0 == strcmp(FLAGS_config[0], "nvprmsaa16")) {
300             deviceType = sk_tools::PictureRenderer::kNVPR_DeviceType;
301             sampleCount = 16;
302         }
303         else if (0 == strcmp(FLAGS_config[0], "gpudft")) {
304             deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
305             useDFText = true;
306         }
307 #if SK_ANGLE
308         else if (0 == strcmp(FLAGS_config[0], "angle")) {
309             deviceType = sk_tools::PictureRenderer::kAngle_DeviceType;
310         }
311 #endif
312 #if SK_MESA
313         else if (0 == strcmp(FLAGS_config[0], "mesa")) {
314             deviceType = sk_tools::PictureRenderer::kMesa_DeviceType;
315         }
316 #endif
317 #endif
318         else {
319             error.printf("%s is not a valid mode for --config\n", FLAGS_config[0]);
320             return NULL;
321         }
322 #if SK_SUPPORT_GPU
323         if (!renderer->setDeviceType(deviceType, gpuAPI)) {
324 #else
325         if (!renderer->setDeviceType(deviceType)) {
326 #endif
327             error.printf("Could not create backend for --config %s\n", FLAGS_config[0]);
328             return NULL;
329         }
330 #if SK_SUPPORT_GPU
331         renderer->setSampleCount(sampleCount);
332         renderer->setUseDFText(useDFText);
333 #endif
334     }
335 
336 
337     sk_tools::PictureRenderer::BBoxHierarchyType bbhType
338             = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
339     if (FLAGS_bbh.count() > 0) {
340         const char* type = FLAGS_bbh[0];
341         if (0 == strcmp(type, "none")) {
342             bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
343         } else if (0 == strcmp(type, "rtree")) {
344             bbhType = sk_tools::PictureRenderer::kRTree_BBoxHierarchyType;
345         } else {
346             error.printf("%s is not a valid value for --bbhType\n", type);
347             return NULL;
348         }
349         if (FLAGS_pipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) {
350             error.printf("--pipe and --bbh cannot be used together\n");
351             return NULL;
352         }
353     }
354     renderer->setBBoxHierarchyType(bbhType);
355     renderer->setScaleFactor(SkDoubleToScalar(FLAGS_scale));
356 
357     return renderer.detach();
358 }
359