1/* 2 * Copyright 2015 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 "SkColorSpace_Base.h" 9#include "SkCommonFlagsConfig.h" 10#include "SkImageInfo.h" 11 12#include <stdlib.h> 13 14#if SK_SUPPORT_GPU 15using sk_gpu_test::GrContextFactory; 16#endif 17 18#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_IOS) 19# define DEFAULT_GPU_CONFIG "gles" 20#else 21# define DEFAULT_GPU_CONFIG "gl" 22#endif 23 24static const char defaultConfigs[] = 25 "8888 " DEFAULT_GPU_CONFIG " nonrendering " 26#if defined(SK_BUILD_FOR_WIN) 27 " angle_d3d11_es2" 28#endif 29 ; 30 31#undef DEFAULT_GPU_CONFIG 32 33static const struct { 34 const char* predefinedConfig; 35 const char* backend; 36 const char* options; 37} gPredefinedConfigs[] ={ 38#if SK_SUPPORT_GPU 39 { "gl", "gpu", "api=gl" }, 40 { "gles", "gpu", "api=gles" }, 41 { "glmsaa4", "gpu", "api=gl,samples=4" }, 42 { "glmsaa8" , "gpu", "api=gl,samples=8" }, 43 { "glesmsaa4", "gpu", "api=gles,samples=4" }, 44 { "glnvpr4", "gpu", "api=gl,nvpr=true,samples=4" }, 45 { "glnvpr8" , "gpu", "api=gl,nvpr=true,samples=8" }, 46 { "glnvprdit4", "gpu", "api=gl,nvpr=true,samples=4,dit=true" }, 47 { "glnvprdit8" , "gpu", "api=gl,nvpr=true,samples=8,dit=true" }, 48 { "glesnvpr4", "gpu", "api=gles,nvpr=true,samples=4" }, 49 { "glesnvprdit4", "gpu", "api=gles,nvpr=true,samples=4,dit=true" }, 50 { "glinst", "gpu", "api=gl,inst=true" }, 51 { "glinst4", "gpu", "api=gl,inst=true,samples=4" }, 52 { "glinstdit4", "gpu", "api=gl,inst=true,samples=4,dit=true" }, 53 { "glinst8" , "gpu", "api=gl,inst=true,samples=8" }, 54 { "glinstdit8" , "gpu", "api=gl,inst=true,samples=8,dit=true" }, 55 { "glesinst", "gpu", "api=gles,inst=true" }, 56 { "glesinst4", "gpu", "api=gles,inst=true,samples=4" }, 57 { "glesinstdit4", "gpu", "api=gles,inst=true,samples=4,dit=true" }, 58 { "gl4444", "gpu", "api=gl,color=4444" }, 59 { "gl565", "gpu", "api=gl,color=565" }, 60 { "glf16", "gpu", "api=gl,color=f16" }, 61 { "glsrgb", "gpu", "api=gl,color=srgb" }, 62 { "glsrgbnl", "gpu", "api=gl,color=srgbnl" }, 63 { "glesf16", "gpu", "api=gles,color=f16" }, 64 { "glessrgb", "gpu", "api=gles,color=srgb" }, 65 { "glessrgbnl", "gpu", "api=gles,color=srgbnl" }, 66 { "glsrgb", "gpu", "api=gl,color=srgb" }, 67 { "glwide", "gpu", "api=gl,color=f16_wide" }, 68 { "glnarrow", "gpu", "api=gl,color=f16_narrow" }, 69 { "glnostencils", "gpu", "api=gl,stencils=false" }, 70 { "glessrgb", "gpu", "api=gles,color=srgb" }, 71 { "gleswide", "gpu", "api=gles,color=f16_wide" }, 72 { "glesnarrow", "gpu", "api=gles,color=f16_narrow" }, 73 { "gldft", "gpu", "api=gl,dit=true" }, 74 { "glesdft", "gpu", "api=gles,dit=true" }, 75 { "debuggl", "gpu", "api=debuggl" }, 76 { "nullgl", "gpu", "api=nullgl" }, 77 { "angle_d3d11_es2", "gpu", "api=angle_d3d11_es2" }, 78 { "angle_d3d11_es3", "gpu", "api=angle_d3d11_es3" }, 79 { "angle_d3d9_es2", "gpu", "api=angle_d3d9_es2" }, 80 { "angle_d3d11_es2_msaa4", "gpu", "api=angle_d3d11_es2,samples=4" }, 81 { "angle_d3d11_es2_msaa8", "gpu", "api=angle_d3d11_es2,samples=8" }, 82 { "angle_d3d11_es3_msaa4", "gpu", "api=angle_d3d11_es3,samples=4" }, 83 { "angle_d3d11_es3_msaa8", "gpu", "api=angle_d3d11_es3,samples=8" }, 84 { "angle_gl_es2", "gpu", "api=angle_gl_es2" }, 85 { "angle_gl_es3", "gpu", "api=angle_gl_es3" }, 86 { "commandbuffer", "gpu", "api=commandbuffer" }, 87 { "mock", "gpu", "api=mock" } 88#if SK_MESA 89 ,{ "mesa", "gpu", "api=mesa" } 90#endif 91#ifdef SK_VULKAN 92 ,{ "vk", "gpu", "api=vulkan" } 93 ,{ "vksrgb", "gpu", "api=vulkan,color=srgb" } 94 ,{ "vkwide", "gpu", "api=vulkan,color=f16_wide" } 95 ,{ "vkmsaa4", "gpu", "api=vulkan,samples=4" } 96 ,{ "vkmsaa8", "gpu", "api=vulkan,samples=8" } 97#endif 98#ifdef SK_METAL 99 ,{ "mtl", "gpu", "api=metal" } 100 ,{ "mtlsrgb", "gpu", "api=metal,color=srgb" } 101 ,{ "mtlwide", "gpu", "api=metal,color=f16_wide" } 102 ,{ "mtlmsaa4", "gpu", "api=metal,samples=4" } 103 ,{ "mtlmsaa8", "gpu", "api=metal,samples=8" } 104#endif 105#else 106 { "", "", "" } 107#endif 108}; 109 110static const char configHelp[] = 111 "Options: 565 8888 srgb f16 nonrendering null pdf pdfa skp pipe svg xps"; 112 113static const char* config_help_fn() { 114 static SkString helpString; 115 helpString.set(configHelp); 116 for (const auto& config : gPredefinedConfigs) { 117 helpString.appendf(" %s", config.predefinedConfig); 118 } 119 helpString.append(" or use extended form 'backend[option=value,...]'.\n"); 120 return helpString.c_str(); 121} 122 123static const char configExtendedHelp[] = 124 "Extended form: 'backend(option=value,...)'\n\n" 125 "Possible backends and options:\n" 126#if SK_SUPPORT_GPU 127 "\n" 128 "gpu[api=string,color=string,dit=bool,nvpr=bool,inst=bool,samples=int]\n" 129 "\tapi\ttype: string\trequired\n" 130 "\t Select graphics API to use with gpu backend.\n" 131 "\t Options:\n" 132 "\t\tgl \t\t\tUse OpenGL.\n" 133 "\t\tgles \t\t\tUse OpenGL ES.\n" 134 "\t\tdebuggl \t\t\tUse debug OpenGL.\n" 135 "\t\tnullgl \t\t\tUse null OpenGL.\n" 136 "\t\tangle_d3d9_es2\t\t\tUse OpenGL ES2 on the ANGLE Direct3D9 backend.\n" 137 "\t\tangle_d3d11_es2\t\t\tUse OpenGL ES2 on the ANGLE Direct3D11 backend.\n" 138 "\t\tangle_d3d11_es3\t\t\tUse OpenGL ES3 on the ANGLE Direct3D11 backend.\n" 139 "\t\tangle_gl_es2\t\t\tUse OpenGL ES2 on the ANGLE OpenGL backend.\n" 140 "\t\tangle_gl_es3\t\t\tUse OpenGL ES3 on the ANGLE OpenGL backend.\n" 141 "\t\tcommandbuffer\t\tUse command buffer.\n" 142 "\t\tmock\t\tUse mock context.\n" 143#if SK_MESA 144 "\t\tmesa\t\t\tUse MESA.\n" 145#endif 146#ifdef SK_VULKAN 147 "\t\tvulkan\t\t\tUse Vulkan.\n" 148#endif 149#ifdef SK_METAL 150 "\t\tmetal\t\t\tUse Metal.\n" 151#endif 152 "\tcolor\ttype: string\tdefault: 8888.\n" 153 "\t Select framebuffer color format.\n" 154 "\t Options:\n" 155 "\t\t8888\t\t\tLinear 8888.\n" 156 "\t\t4444\t\t\tLinear 4444.\n" 157 "\t\t565\t\t\tLinear 565.\n" 158 "\t\tf16{_gamut}\t\tLinear 16-bit floating point.\n" 159 "\t\tsrgb{_gamut}\t\tsRGB 8888.\n" 160 "\t gamut\ttype: string\tdefault: srgb.\n" 161 "\t Select color gamut for f16 or sRGB format buffers.\n" 162 "\t Options:\n" 163 "\t\tsrgb\t\t\tsRGB gamut.\n" 164 "\t\twide\t\t\tWide Gamut RGB.\n" 165 "\tdit\ttype: bool\tdefault: false.\n" 166 "\t Use device independent text.\n" 167 "\tnvpr\ttype: bool\tdefault: false.\n" 168 "\t Use NV_path_rendering OpenGL and OpenGL ES extension.\n" 169 "\tsamples\ttype: int\tdefault: 0.\n" 170 "\t Use multisampling with N samples.\n" 171 "\tstencils\ttype: bool\tdefault: true.\n" 172 "\t Allow the use of stencil buffers.\n" 173 "\n" 174 "Predefined configs:\n\n" 175 // Help text for pre-defined configs is auto-generated from gPredefinedConfigs 176#endif 177 ; 178 179static const char* config_extended_help_fn() { 180 static SkString helpString; 181 helpString.set(configExtendedHelp); 182 for (const auto& config : gPredefinedConfigs) { 183 helpString.appendf("\t%-10s\t= gpu(%s)\n", config.predefinedConfig, config.options); 184 } 185 return helpString.c_str(); 186} 187 188DEFINE_extended_string(config, defaultConfigs, config_help_fn(), config_extended_help_fn()); 189 190SkCommandLineConfig::SkCommandLineConfig(const SkString& tag, const SkString& backend, 191 const SkTArray<SkString>& viaParts) 192 : fTag(tag) 193 , fBackend(backend) 194 , fViaParts(viaParts) { 195} 196SkCommandLineConfig::~SkCommandLineConfig() { 197} 198 199#if SK_SUPPORT_GPU 200SkCommandLineConfigGpu::SkCommandLineConfigGpu( 201 const SkString& tag, const SkTArray<SkString>& viaParts, ContextType contextType, bool useNVPR, 202 bool useInstanced, bool useDIText, int samples, SkColorType colorType, SkAlphaType alphaType, 203 sk_sp<SkColorSpace> colorSpace, bool useStencilBuffers) 204 : SkCommandLineConfig(tag, SkString("gpu"), viaParts) 205 , fContextType(contextType) 206 , fContextOverrides(ContextOverrides::kNone) 207 , fUseDIText(useDIText) 208 , fSamples(samples) 209 , fColorType(colorType) 210 , fAlphaType(alphaType) 211 , fColorSpace(std::move(colorSpace)) { 212 if (useNVPR) { 213 fContextOverrides |= ContextOverrides::kRequireNVPRSupport; 214 } else if (!useInstanced) { 215 // We don't disable NVPR for instanced configs. Otherwise the caps wouldn't use mixed 216 // samples and we couldn't test the mixed samples backend for simple shapes. 217 fContextOverrides |= ContextOverrides::kDisableNVPR; 218 } 219 if (useInstanced) { 220 fContextOverrides |= ContextOverrides::kUseInstanced; 221 } 222 // Subtle logic: If the config has a color space attached, we're going to be rendering to sRGB, 223 // so we need that capability. In addition, to get the widest test coverage, we DO NOT require 224 // that we can disable sRGB decode. (That's for rendering sRGB sources to legacy surfaces). 225 // 226 // If the config doesn't have a color space attached, we're going to be rendering in legacy 227 // mode. In that case, we don't require sRGB capability and we defer to the client to decide on 228 // sRGB decode control. 229 if (fColorSpace) { 230 fContextOverrides |= ContextOverrides::kRequireSRGBSupport; 231 fContextOverrides |= ContextOverrides::kAllowSRGBWithoutDecodeControl; 232 } 233 if (!useStencilBuffers) { 234 fContextOverrides |= ContextOverrides::kAvoidStencilBuffers; 235 } 236} 237static bool parse_option_int(const SkString& value, int* outInt) { 238 if (value.isEmpty()) { 239 return false; 240 } 241 char* endptr = nullptr; 242 long intValue = strtol(value.c_str(), &endptr, 10); 243 if (*endptr != '\0') { 244 return false; 245 } 246 *outInt = static_cast<int>(intValue); 247 return true; 248} 249static bool parse_option_bool(const SkString& value, bool* outBool) { 250 if (value.equals("true")) { 251 *outBool = true; 252 return true; 253 } 254 if (value.equals("false")) { 255 *outBool = false; 256 return true; 257 } 258 return false; 259} 260static bool parse_option_gpu_api(const SkString& value, 261 SkCommandLineConfigGpu::ContextType* outContextType) { 262 if (value.equals("gl")) { 263 *outContextType = GrContextFactory::kGL_ContextType; 264 return true; 265 } 266 if (value.equals("gles")) { 267 *outContextType = GrContextFactory::kGLES_ContextType; 268 return true; 269 } 270 if (value.equals("debuggl")) { 271 *outContextType = GrContextFactory::kDebugGL_ContextType; 272 return true; 273 } 274 if (value.equals("nullgl")) { 275 *outContextType = GrContextFactory::kNullGL_ContextType; 276 return true; 277 } 278 if (value.equals("angle_d3d9_es2")) { 279 *outContextType = GrContextFactory::kANGLE_D3D9_ES2_ContextType; 280 return true; 281 } 282 if (value.equals("angle_d3d11_es2")) { 283 *outContextType = GrContextFactory::kANGLE_D3D11_ES2_ContextType; 284 return true; 285 } 286 if (value.equals("angle_d3d11_es3")) { 287 *outContextType = GrContextFactory::kANGLE_D3D11_ES3_ContextType; 288 return true; 289 } 290 if (value.equals("angle_gl_es2")) { 291 *outContextType = GrContextFactory::kANGLE_GL_ES2_ContextType; 292 return true; 293 } 294 if (value.equals("angle_gl_es3")) { 295 *outContextType = GrContextFactory::kANGLE_GL_ES3_ContextType; 296 return true; 297 } 298 if (value.equals("commandbuffer")) { 299 *outContextType = GrContextFactory::kCommandBuffer_ContextType; 300 return true; 301 } 302 if (value.equals("mock")) { 303 *outContextType = GrContextFactory::kMock_ContextType; 304 return true; 305 } 306#if SK_MESA 307 if (value.equals("mesa")) { 308 *outContextType = GrContextFactory::kMESA_ContextType; 309 return true; 310 } 311#endif 312#ifdef SK_VULKAN 313 if (value.equals("vulkan")) { 314 *outContextType = GrContextFactory::kVulkan_ContextType; 315 return true; 316 } 317#endif 318#ifdef SK_METAL 319 if (value.equals("metal")) { 320 *outContextType = GrContextFactory::kMetal_ContextType; 321 return true; 322 } 323#endif 324 return false; 325} 326static bool parse_option_gpu_color(const SkString& value, 327 SkColorType* outColorType, 328 SkAlphaType* alphaType, 329 sk_sp<SkColorSpace>* outColorSpace) { 330 // We always use premul unless the color type is 565. 331 *alphaType = kPremul_SkAlphaType; 332 333 if (value.equals("8888")) { 334 *outColorType = kRGBA_8888_SkColorType; 335 *outColorSpace = nullptr; 336 return true; 337 } else if (value.equals("4444")) { 338 *outColorType = kARGB_4444_SkColorType; 339 *outColorSpace = nullptr; 340 return true; 341 } else if (value.equals("565")) { 342 *outColorType = kRGB_565_SkColorType; 343 *alphaType = kOpaque_SkAlphaType; 344 *outColorSpace = nullptr; 345 return true; 346 } 347 348 SkTArray<SkString> commands; 349 SkStrSplit(value.c_str(), "_", &commands); 350 if (commands.count() < 1 || commands.count() > 2) { 351 return false; 352 } 353 354 const bool linearGamma = commands[0].equals("f16"); 355 SkColorSpace::Gamut gamut = SkColorSpace::kSRGB_Gamut; 356 SkColorSpace::RenderTargetGamma gamma = linearGamma ? SkColorSpace::kLinear_RenderTargetGamma 357 : SkColorSpace::kSRGB_RenderTargetGamma; 358 *outColorSpace = SkColorSpace::MakeRGB(gamma, gamut); 359 360 if (commands.count() == 2) { 361 if (commands[1].equals("srgb")) { 362 // sRGB gamut (which is our default) 363 } else if (commands[1].equals("wide")) { 364 // WideGamut RGB 365 const float gWideGamutRGB_toXYZD50[]{ 366 0.7161046f, 0.1009296f, 0.1471858f, // -> X 367 0.2581874f, 0.7249378f, 0.0168748f, // -> Y 368 0.0000000f, 0.0517813f, 0.7734287f, // -> Z 369 }; 370 SkMatrix44 wideGamutRGBMatrix(SkMatrix44::kUninitialized_Constructor); 371 wideGamutRGBMatrix.set3x3RowMajorf(gWideGamutRGB_toXYZD50); 372 *outColorSpace = SkColorSpace::MakeRGB(gamma, wideGamutRGBMatrix); 373 } else if (commands[1].equals("narrow")) { 374 // NarrowGamut RGB (an artifically smaller than sRGB gamut) 375 SkColorSpacePrimaries primaries ={ 376 0.54f, 0.33f, // Rx, Ry 377 0.33f, 0.50f, // Gx, Gy 378 0.25f, 0.20f, // Bx, By 379 0.3127f, 0.3290f, // Wx, Wy 380 }; 381 SkMatrix44 narrowGamutRGBMatrix(SkMatrix44::kUninitialized_Constructor); 382 primaries.toXYZD50(&narrowGamutRGBMatrix); 383 *outColorSpace = SkColorSpace::MakeRGB(gamma, narrowGamutRGBMatrix); 384 } else { 385 // Unknown color gamut 386 return false; 387 } 388 } 389 390 // Now pick a color type 391 if (commands[0].equals("f16")) { 392 *outColorType = kRGBA_F16_SkColorType; 393 return true; 394 } 395 if (commands[0].equals("srgb") || commands[0].equals("srgbnl")) { 396 *outColorType = kRGBA_8888_SkColorType; 397 return true; 398 } 399 return false; 400} 401 402SkCommandLineConfigGpu* parse_command_line_config_gpu(const SkString& tag, 403 const SkTArray<SkString>& vias, 404 const SkString& options) { 405 // Defaults for GPU backend. 406 bool seenAPI = false; 407 SkCommandLineConfigGpu::ContextType contextType = GrContextFactory::kGL_ContextType; 408 bool seenUseNVPR = false; 409 bool useNVPR = false; 410 bool seenUseInstanced = false; 411 bool useInstanced = false; 412 bool seenUseDIText =false; 413 bool useDIText = false; 414 bool seenSamples = false; 415 int samples = 0; 416 bool seenColor = false; 417 SkColorType colorType = kRGBA_8888_SkColorType; 418 SkAlphaType alphaType = kPremul_SkAlphaType; 419 sk_sp<SkColorSpace> colorSpace = nullptr; 420 bool seenUseStencils = false; 421 bool useStencils = true; 422 423 SkTArray<SkString> optionParts; 424 SkStrSplit(options.c_str(), ",", kStrict_SkStrSplitMode, &optionParts); 425 for (int i = 0; i < optionParts.count(); ++i) { 426 SkTArray<SkString> keyValueParts; 427 SkStrSplit(optionParts[i].c_str(), "=", kStrict_SkStrSplitMode, &keyValueParts); 428 if (keyValueParts.count() != 2) { 429 return nullptr; 430 } 431 const SkString& key = keyValueParts[0]; 432 const SkString& value = keyValueParts[1]; 433 bool valueOk = false; 434 if (key.equals("api") && !seenAPI) { 435 valueOk = parse_option_gpu_api(value, &contextType); 436 seenAPI = true; 437 } else if (key.equals("nvpr") && !seenUseNVPR) { 438 valueOk = parse_option_bool(value, &useNVPR); 439 seenUseNVPR = true; 440 } else if (key.equals("inst") && !seenUseInstanced) { 441 valueOk = parse_option_bool(value, &useInstanced); 442 seenUseInstanced = true; 443 } else if (key.equals("dit") && !seenUseDIText) { 444 valueOk = parse_option_bool(value, &useDIText); 445 seenUseDIText = true; 446 } else if (key.equals("samples") && !seenSamples) { 447 valueOk = parse_option_int(value, &samples); 448 seenSamples = true; 449 } else if (key.equals("color") && !seenColor) { 450 valueOk = parse_option_gpu_color(value, &colorType, &alphaType, &colorSpace); 451 seenColor = true; 452 } else if (key.equals("stencils") && !seenUseStencils) { 453 valueOk = parse_option_bool(value, &useStencils); 454 seenUseStencils = true; 455 } 456 if (!valueOk) { 457 return nullptr; 458 } 459 } 460 if (!seenAPI) { 461 return nullptr; 462 } 463 return new SkCommandLineConfigGpu(tag, vias, contextType, useNVPR, useInstanced, useDIText, 464 samples, colorType, alphaType, colorSpace, useStencils); 465} 466#endif 467 468void ParseConfigs(const SkCommandLineFlags::StringArray& configs, 469 SkCommandLineConfigArray* outResult) { 470 outResult->reset(); 471 for (int i = 0; i < configs.count(); ++i) { 472 SkString extendedBackend; 473 SkString extendedOptions; 474 SkString simpleBackend; 475 SkTArray<SkString> vias; 476 477 SkString tag(configs[i]); 478 SkTArray<SkString> parts; 479 SkStrSplit(tag.c_str(), "[", kStrict_SkStrSplitMode, &parts); 480 if (parts.count() == 2) { 481 SkTArray<SkString> parts2; 482 SkStrSplit(parts[1].c_str(), "]", kStrict_SkStrSplitMode, &parts2); 483 if (parts2.count() == 2 && parts2[1].isEmpty()) { 484 SkStrSplit(parts[0].c_str(), "-", kStrict_SkStrSplitMode, &vias); 485 if (vias.count()) { 486 extendedBackend = vias[vias.count() - 1]; 487 vias.pop_back(); 488 } else { 489 extendedBackend = parts[0]; 490 } 491 extendedOptions = parts2[0]; 492 simpleBackend.printf("%s[%s]", extendedBackend.c_str(), extendedOptions.c_str()); 493 } 494 } 495 496 if (extendedBackend.isEmpty()) { 497 simpleBackend = tag; 498 SkStrSplit(tag.c_str(), "-", kStrict_SkStrSplitMode, &vias); 499 if (vias.count()) { 500 simpleBackend = vias[vias.count() - 1]; 501 vias.pop_back(); 502 } 503 for (auto& predefinedConfig : gPredefinedConfigs) { 504 if (simpleBackend.equals(predefinedConfig.predefinedConfig)) { 505 extendedBackend = predefinedConfig.backend; 506 extendedOptions = predefinedConfig.options; 507 break; 508 } 509 } 510 } 511 SkCommandLineConfig* parsedConfig = nullptr; 512#if SK_SUPPORT_GPU 513 if (extendedBackend.equals("gpu")) { 514 parsedConfig = parse_command_line_config_gpu(tag, vias, extendedOptions); 515 } 516#endif 517 if (!parsedConfig) { 518 parsedConfig = new SkCommandLineConfig(tag, simpleBackend, vias); 519 } 520 outResult->emplace_back(parsedConfig); 521 } 522} 523