Add config options to run different GPU APIs to dm and nanobench

Add extended config specification form that can be used to run different
gpu backend with different APIs.

The configs can be specified with the form:
gpu(api=string,dit=bool,nvpr=bool,samples=int)

This replaces and removes the --gpuAPI flag.

All existing configs should still work.

Adds following documentation:

out/Debug/dm --help config

Flags:
    --config:	type: string	default: 565 8888 gpu nonrendering
        Options: 565 8888 debug gpu gpudebug gpudft gpunull msaa16 msaa4
        nonrendering null nullgpu nvprmsaa16 nvprmsaa4 pdf pdf_poppler skp svg
        xps or use extended form 'backend(option=value,...)'.

        Extended form: 'backend(option=value,...)'

        Possible backends and options:

        gpu(api=string,dit=bool,nvpr=bool,samples=int)	GPU backend
        	api	type: string	default: native.
        	    Select graphics API to use with gpu backend.
        	    Options:
        		native			Use platform default OpenGL or OpenGL ES backend.
        		gl    			Use OpenGL.
        		gles  			Use OpenGL ES.
        		debug 			Use debug OpenGL.
        		null  			Use null OpenGL.
        	dit	type: bool	default: false.
        	    Use device independent text.
        	nvpr	type: bool	default: false.
        	    Use NV_path_rendering OpenGL and OpenGL ES extension.
        	samples	type: int	default: 0.
        	    Use multisampling with N samples.

        Predefined configs:

        	gpu      	= gpu()
        	msaa4    	= gpu(samples=4)
        	msaa16   	= gpu(samples=16)
        	nvprmsaa4	= gpu(nvpr=true,samples=4)
        	nvprmsaa16	= gpu(nvpr=true,samples=16)
        	gpudft    	= gpu(dit=true)
        	gpudebug  	= gpu(api=debug)
        	gpunull   	= gpu(api=null)
        	debug     	= gpu(api=debug)
        	nullgpu   	= gpu(api=null)

BUG=skia:2992

Committed: https://skia.googlesource.com/skia/+/e13ca329fca4c28cf4e078561f591ab27b743d23
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1490113005

Committed: https://skia.googlesource.com/skia/+/c8b4336444e7b90382e04e33665fb3b8490b825b

Review URL: https://codereview.chromium.org/1490113005
diff --git a/dm/DM.cpp b/dm/DM.cpp
index c7e0934..ac37aeb 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -15,6 +15,7 @@
 #include "SkChecksum.h"
 #include "SkCodec.h"
 #include "SkCommonFlags.h"
+#include "SkCommonFlagsConfig.h"
 #include "SkFontMgr.h"
 #include "SkForceLinking.h"
 #include "SkGraphics.h"
@@ -199,7 +200,7 @@
 };
 
 struct TaggedSink : public SkAutoTDelete<Sink> {
-    const char* tag;
+    SkString tag;
 };
 
 static const bool kMemcpyOK = true;
@@ -541,19 +542,9 @@
     }
 }
 
-#if SK_SUPPORT_GPU
-static GrGLStandard get_gpu_api() {
-    if (FLAGS_gpuAPI.contains("gl"))   { return kGL_GrGLStandard; }
-    if (FLAGS_gpuAPI.contains("gles")) { return kGLES_GrGLStandard; }
-    return kNone_GrGLStandard;
-}
-#endif
-
-static void push_sink(const char* tag, Sink* s) {
+static void push_sink(const SkCommandLineConfig& config, Sink* s) {
     SkAutoTDelete<Sink> sink(s);
-    if (!FLAGS_config.contains(tag)) {
-        return;
-    }
+
     // Try a simple Src as a canary.  If it fails, skip this sink.
     struct : public Src {
         Error draw(SkCanvas* c) const override {
@@ -569,13 +560,13 @@
     SkString log;
     Error err = sink->draw(justOneRect, &bitmap, &stream, &log);
     if (err.isFatal()) {
-        SkDebugf("Could not run %s: %s\n", tag, err.c_str());
+        SkDebugf("Could not run %s: %s\n", config.getTag().c_str(), err.c_str());
         exit(1);
     }
 
     TaggedSink& ts = gSinks.push_back();
     ts.reset(sink.detach());
-    ts.tag = tag;
+    ts.tag = config.getTag();
 }
 
 static bool gpu_supported() {
@@ -585,45 +576,32 @@
     return false;
 #endif
 }
-static Sink* create_gpu_sink(const char* tag, GrContextFactory::GLContextType contextType,
-                             GrContextFactory::GLContextOptions contextOptions, int samples,
-                             bool diText, bool threaded) {
-#if SK_SUPPORT_GPU
-    GrContextFactory testFactory;
-    const GrGLStandard api = get_gpu_api();
-    if (testFactory.get(contextType, api, contextOptions)) {
-        return new GPUSink(contextType, contextOptions, api, samples, diText, threaded);
-    }
-    SkDebugf("WARNING: can not create GPU context for config '%s'. GM tests will be skipped.\n", tag);
-#endif
-    return nullptr;
-}
-static Sink* create_sink(const char* tag) {
-#define GPU_SINK(t, ...) if (0 == strcmp(t, tag)) { return create_gpu_sink(tag, __VA_ARGS__); }
-    if (gpu_supported()) {
-        typedef GrContextFactory Gr;
-        GPU_SINK("gpunull",       Gr::kNull_GLContextType,          Gr::kNone_GLContextOptions,        0, false, FLAGS_gpu_threading);
-        GPU_SINK("gpudebug",      Gr::kDebug_GLContextType,         Gr::kNone_GLContextOptions,        0, false, FLAGS_gpu_threading);
-        GPU_SINK("gpu",           Gr::kNative_GLContextType,        Gr::kNone_GLContextOptions,        0, false, FLAGS_gpu_threading);
-        GPU_SINK("gpudft",        Gr::kNative_GLContextType,        Gr::kNone_GLContextOptions,        0,  true, FLAGS_gpu_threading);
-        GPU_SINK("msaa4",         Gr::kNative_GLContextType,        Gr::kNone_GLContextOptions,        4, false, FLAGS_gpu_threading);
-        GPU_SINK("msaa16",        Gr::kNative_GLContextType,        Gr::kNone_GLContextOptions,       16, false, FLAGS_gpu_threading);
-        GPU_SINK("nvprmsaa4",     Gr::kNative_GLContextType,        Gr::kEnableNVPR_GLContextOptions,  4,  true, FLAGS_gpu_threading);
-        GPU_SINK("nvprmsaa16",    Gr::kNative_GLContextType,        Gr::kEnableNVPR_GLContextOptions, 16,  true, FLAGS_gpu_threading);
-#if SK_ANGLE
-        GPU_SINK("angle",         Gr::kANGLE_GLContextType,         Gr::kNone_GLContextOptions,        0, false, FLAGS_gpu_threading);
-        GPU_SINK("angle-gl",      Gr::kANGLE_GL_GLContextType,      Gr::kNone_GLContextOptions,        0, false, FLAGS_gpu_threading);
-#endif
-#if SK_COMMAND_BUFFER
-        GPU_SINK("commandbuffer", Gr::kCommandBuffer_GLContextType, Gr::kNone_GLContextOptions,        0, false, FLAGS_gpu_threading);
-#endif
-#if SK_MESA
-        GPU_SINK("mesa",          Gr::kMESA_GLContextType,          Gr::kNone_GLContextOptions,        0, false, FLAGS_gpu_threading);
-#endif
-    }
-#undef GPU_SINK
 
-#define SINK(t, sink, ...) if (0 == strcmp(t, tag)) { return new sink(__VA_ARGS__); }
+static Sink* create_sink(const SkCommandLineConfig* config) {
+#if SK_SUPPORT_GPU
+    if (gpu_supported()) {
+        if (const SkCommandLineConfigGpu* gpuConfig = config->asConfigGpu()) {
+            GrContextFactory::GLContextType contextType = gpuConfig->getContextType();
+            GrContextFactory::GLContextOptions contextOptions =
+                    GrContextFactory::kNone_GLContextOptions;
+            if (gpuConfig->getUseNVPR()) {
+                contextOptions = static_cast<GrContextFactory::GLContextOptions>(
+                    contextOptions | GrContextFactory::kEnableNVPR_GLContextOptions);
+            }
+            GrContextFactory testFactory;
+            if (!testFactory.get(contextType, contextOptions)) {
+                SkDebugf("WARNING: can not create GPU context for config '%s'. "
+                         "GM tests will be skipped.\n", gpuConfig->getTag().c_str());
+                return nullptr;
+            }
+            return new GPUSink(contextType, contextOptions, gpuConfig->getSamples(),
+                               gpuConfig->getUseDIText(), FLAGS_gpu_threading);
+        }
+    }
+#endif
+
+#define SINK(t, sink, ...) if (config->getBackend().equals(t)) { return new sink(__VA_ARGS__); }
+
 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
     SINK("hwui",           HWUISink);
 #endif
@@ -642,8 +620,8 @@
     return nullptr;
 }
 
-static Sink* create_via(const char* tag, Sink* wrapped) {
-#define VIA(t, via, ...) if (0 == strcmp(t, tag)) { return new via(__VA_ARGS__); }
+static Sink* create_via(const SkString& tag, Sink* wrapped) {
+#define VIA(t, via, ...) if (tag.equals(t)) { return new via(__VA_ARGS__); }
     VIA("twice",     ViaTwice,             wrapped);
     VIA("pipe",      ViaPipe,              wrapped);
     VIA("serialize", ViaSerialization,     wrapped);
@@ -674,17 +652,24 @@
 }
 
 static void gather_sinks() {
-    for (int i = 0; i < FLAGS_config.count(); i++) {
-        const char* config = FLAGS_config[i];
-        SkTArray<SkString> parts;
-        SkStrSplit(config, "-", &parts);
+    SkCommandLineConfigArray configs;
+    ParseConfigs(FLAGS_config, &configs);
+    for (int i = 0; i < configs.count(); i++) {
+        const SkCommandLineConfig& config = *configs[i];
+        Sink* sink = create_sink(&config);
+        if (sink == nullptr) {
+            SkDebugf("Skipping config %s: Don't understand '%s'.\n", config.getTag().c_str(),
+                     config.getTag().c_str());
+            continue;
+        }
 
-        Sink* sink = nullptr;
-        for (int i = parts.count(); i-- > 0;) {
-            const char* part = parts[i].c_str();
-            Sink* next = (sink == nullptr) ? create_sink(part) : create_via(part, sink);
+        const SkTArray<SkString>& parts = config.getViaParts();
+        for (int j = parts.count(); j-- > 0;) {
+            const SkString& part = parts[j];
+            Sink* next = create_via(part, sink);
             if (next == nullptr) {
-                SkDebugf("Skipping %s: Don't understand '%s'.\n", config, part);
+                SkDebugf("Skipping config %s: Don't understand '%s'.\n", config.getTag().c_str(),
+                         part.c_str());
                 delete sink;
                 sink = nullptr;
                 break;
@@ -806,7 +791,7 @@
         //   - this Src / Sink combination is on the blacklist;
         //   - it's a dry run.
         SkString note(task->src->veto(task->sink->flags()) ? " (veto)" : "");
-        SkString whyBlacklisted = is_blacklisted(task->sink.tag, task->src.tag.c_str(),
+        SkString whyBlacklisted = is_blacklisted(task->sink.tag.c_str(), task->src.tag.c_str(),
                                                  task->src.options.c_str(), name.c_str());
         if (!whyBlacklisted.isEmpty()) {
             note.appendf(" (--blacklist %s)", whyBlacklisted.c_str());
@@ -818,15 +803,15 @@
             SkBitmap bitmap;
             SkDynamicMemoryWStream stream;
             if (FLAGS_pre_log) {
-                SkDebugf("\nRunning %s->%s", name.c_str(), task->sink.tag);
+                SkDebugf("\nRunning %s->%s", name.c_str(), task->sink.tag.c_str());
             }
-            start(task->sink.tag, task->src.tag, task->src.options, name.c_str());
+            start(task->sink.tag.c_str(), task->src.tag, task->src.options, name.c_str());
             Error err = task->sink->draw(*task->src, &bitmap, &stream, &log);
             if (!err.isEmpty()) {
                 auto elapsed = now_ms() - timerStart;
                 if (err.isFatal()) {
                     fail(SkStringPrintf("%s %s %s %s: %s",
-                                        task->sink.tag,
+                                        task->sink.tag.c_str(),
                                         task->src.tag.c_str(),
                                         task->src.options.c_str(),
                                         name.c_str(),
@@ -834,7 +819,7 @@
                 } else {
                     note.appendf(" (skipped: %s)", err.c_str());
                 }
-                done(elapsed, task->sink.tag, task->src.tag, task->src.options,
+                done(elapsed, task->sink.tag.c_str(), task->src.tag, task->src.options,
                      name, note, log);
                 return;
             }
@@ -867,11 +852,11 @@
             }
 
             if (!FLAGS_readPath.isEmpty() &&
-                !gGold.contains(Gold(task->sink.tag, task->src.tag.c_str(),
+                !gGold.contains(Gold(task->sink.tag.c_str(), task->src.tag.c_str(),
                                      task->src.options.c_str(), name, md5))) {
                 fail(SkStringPrintf("%s not found for %s %s %s %s in %s",
                                     md5.c_str(),
-                                    task->sink.tag,
+                                    task->sink.tag.c_str(),
                                     task->src.tag.c_str(),
                                     task->src.options.c_str(),
                                     name.c_str(),
@@ -888,7 +873,7 @@
                 }
             }
         }
-        done(now_ms()-timerStart, task->sink.tag, task->src.tag.c_str(), task->src.options.c_str(),
+        done(now_ms()-timerStart, task->sink.tag.c_str(), task->src.tag.c_str(), task->src.options.c_str(),
              name, note, log);
     }
 
@@ -899,7 +884,7 @@
                             const SkBitmap* bitmap) {
         JsonWriter::BitmapResult result;
         result.name          = task.src->name();
-        result.config        = task.sink.tag;
+        result.config        = task.sink.tag.c_str();
         result.sourceType    = task.src.tag;
         result.sourceOptions = task.src.options;
         result.ext           = ext;
@@ -927,7 +912,7 @@
                 return;  // Content-addressed.  If it exists already, we're done.
             }
         } else {
-            path = SkOSPath::Join(dir, task.sink.tag);
+            path = SkOSPath::Join(dir, task.sink.tag.c_str());
             sk_mkdir(path.c_str());
             path = SkOSPath::Join(path.c_str(), task.src.tag.c_str());
             sk_mkdir(path.c_str());
@@ -1179,28 +1164,48 @@
 void RunWithGPUTestContexts(T test, GPUTestContexts testContexts, Reporter* reporter,
                             GrContextFactory* factory) {
 #if SK_SUPPORT_GPU
-    const GrGLStandard api = get_gpu_api();
-    for (int i = 0; i < GrContextFactory::kGLContextTypeCnt; ++i) {
-        GrContextFactory::GLContextType glCtxType = (GrContextFactory::GLContextType) i;
+    // Iterate over context types, except use "native" instead of explicitly trying OpenGL and
+    // OpenGL ES. Do not use GLES on desktop, since tests do not account for not fixing
+    // http://skbug.com/2809
+    GrContextFactory::GLContextType contextTypes[] = {
+        GrContextFactory::kNative_GLContextType,
+#if SK_ANGLE
+#ifdef SK_BUILD_FOR_WIN
+        GrContextFactory::kANGLE_GLContextType,
+#endif
+        GrContextFactory::kANGLE_GL_GLContextType,
+#endif
+#if SK_COMMAND_BUFFER
+        GrContextFactory::kCommandBuffer_GLContextType,
+#endif
+#if SK_MESA
+        GrContextFactory::kMESA_GLContextType,
+#endif
+        GrContextFactory::kNull_GLContextType,
+        GrContextFactory::kDebug_GLContextType,
+    };
+    static_assert(SK_ARRAY_COUNT(contextTypes) == GrContextFactory::kGLContextTypeCnt - 2,
+                  "Skipping unexpected GLContextType for GPU tests");
+
+    for (auto& contextType : contextTypes) {
         int contextSelector = kNone_GPUTestContexts;
-        if (GrContextFactory::IsRenderingGLContext(glCtxType)) {
+        if (GrContextFactory::IsRenderingGLContext(contextType)) {
             contextSelector |= kAllRendering_GPUTestContexts;
-        } else if (glCtxType == GrContextFactory::kNative_GLContextType) {
+        } else if (contextType == GrContextFactory::kNative_GLContextType) {
             contextSelector |= kNative_GPUTestContexts;
-        } else if (glCtxType == GrContextFactory::kNull_GLContextType) {
+        } else if (contextType == GrContextFactory::kNull_GLContextType) {
             contextSelector |= kNull_GPUTestContexts;
-        } else if (glCtxType == GrContextFactory::kDebug_GLContextType) {
+        } else if (contextType == GrContextFactory::kDebug_GLContextType) {
             contextSelector |= kDebug_GPUTestContexts;
         }
         if ((testContexts & contextSelector) == 0) {
             continue;
         }
-        if (GrContextFactory::ContextInfo* context = factory->getContextInfo(glCtxType, api)) {
+        if (GrContextFactory::ContextInfo* context = factory->getContextInfo(contextType)) {
             call_test(test, reporter, context);
         }
         if (GrContextFactory::ContextInfo* context =
-            factory->getContextInfo(glCtxType, api,
-                                    GrContextFactory::kEnableNVPR_GLContextOptions)) {
+            factory->getContextInfo(contextType, GrContextFactory::kEnableNVPR_GLContextOptions)) {
             call_test(test, reporter, context);
         }
     }