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 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