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