viewer: Add a "Path renderer" dropdown menu

BUG=skia:

Change-Id: Ia3ed812d24f0f83631ab238bc418a3c95d49b9dc
Reviewed-on: https://skia-review.googlesource.com/9000
Reviewed-by: Yuqian Li <liyuqian@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index 9f274e3..8789c99 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -13,6 +13,7 @@
 #include "SampleSlide.h"
 #include "SKPSlide.h"
 
+#include "GrContext.h"
 #include "SkATrace.h"
 #include "SkCanvas.h"
 #include "SkColorSpace_Base.h"
@@ -22,6 +23,7 @@
 #include "SkGraphics.h"
 #include "SkImagePriv.h"
 #include "SkMetaData.h"
+#include "SkOnce.h"
 #include "SkOSFile.h"
 #include "SkOSPath.h"
 #include "SkRandom.h"
@@ -34,9 +36,13 @@
 #include "imgui.h"
 
 #include <stdlib.h>
+#include <map>
 
 using namespace sk_app;
 
+using GpuPathRenderers = GrContextOptions::GpuPathRenderers;
+static std::map<GpuPathRenderers, std::string> gPathRendererNames;
+
 Application* Application::Create(int argc, char** argv, void* platformData) {
     return new Viewer(argc, argv, platformData);
 }
@@ -215,6 +221,7 @@
 const char* kSlideStateName = "Slide";
 const char* kBackendStateName = "Backend";
 const char* kMSAAStateName = "MSAA";
+const char* kPathRendererStateName = "Path renderer";
 const char* kSoftkeyStateName = "Softkey";
 const char* kSoftkeyHint = "Please select a softkey";
 const char* kFpsStateName = "FPS";
@@ -241,6 +248,19 @@
 {
     static SkTaskGroup::Enabler kTaskGroupEnabler;
     SkGraphics::Init();
+
+    static SkOnce initPathRendererNames;
+    initPathRendererNames([]() {
+        gPathRendererNames[GpuPathRenderers::kAll] = "Default Ganesh Behavior (best path renderer)";
+        gPathRendererNames[GpuPathRenderers::kStencilAndCover] = "NV_path_rendering";
+        gPathRendererNames[GpuPathRenderers::kMSAA] = "Sample shading";
+        gPathRendererNames[GpuPathRenderers::kPLS] = "Pixel local storage";
+        gPathRendererNames[GpuPathRenderers::kDistanceField] = "Distance field (small paths only)";
+        gPathRendererNames[GpuPathRenderers::kTesselating] = "Tessellating";
+        gPathRendererNames[GpuPathRenderers::kDefault] = "Original Ganesh path renderer";
+        gPathRendererNames[GpuPathRenderers::kNone] = "Software masks";
+    });
+
     memset(fPaintTimes, 0, sizeof(fPaintTimes));
     memset(fFlushTimes, 0, sizeof(fFlushTimes));
     memset(fAnimateTimes, 0, sizeof(fAnimateTimes));
@@ -265,6 +285,7 @@
 
     DisplayParams displayParams;
     displayParams.fMSAASampleCount = FLAGS_msaa;
+    displayParams.fGrContextOptions.fGpuPathRenderers = CollectGpuPathRenderersFromFlags();
     fWindow->setRequestedDisplayParams(displayParams);
 
     // register callbacks
@@ -551,6 +572,12 @@
         title.appendf(" MSAA: %i", msaa);
     }
     title.append("]");
+
+    GpuPathRenderers pr = fWindow->getRequestedDisplayParams().fGrContextOptions.fGpuPathRenderers;
+    if (GpuPathRenderers::kAll != pr) {
+        title.appendf(" [Path renderer: %s]", gPathRendererNames[pr].c_str());
+    }
+
     fWindow->setTitle(title.c_str());
 }
 
@@ -1120,6 +1147,36 @@
         }
     }
 
+    // Path renderer state
+    GpuPathRenderers pr = fWindow->getRequestedDisplayParams().fGrContextOptions.fGpuPathRenderers;
+    Json::Value prState(Json::objectValue);
+    prState[kName] = kPathRendererStateName;
+    prState[kValue] = gPathRendererNames[pr];
+    prState[kOptions] = Json::Value(Json::arrayValue);
+    const GrContext* ctx = fWindow->getGrContext();
+    if (!ctx) {
+        prState[kOptions].append("Software");
+    } else if (fWindow->sampleCount()) {
+        prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kAll]);
+        if (ctx->caps()->shaderCaps()->pathRenderingSupport()) {
+            prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kStencilAndCover]);
+        }
+        if (ctx->caps()->sampleShadingSupport()) {
+            prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kMSAA]);
+        }
+        prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kTesselating]);
+        prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kDefault]);
+        prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kNone]);
+    } else {
+        prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kAll]);
+        if (ctx->caps()->shaderCaps()->plsPathRenderingSupport()) {
+            prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kPLS]);
+        }
+        prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kDistanceField]);
+        prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kTesselating]);
+        prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kNone]);
+    }
+
     // Softkey state
     Json::Value softkeyState(Json::objectValue);
     softkeyState[kName] = kSoftkeyStateName;
@@ -1145,6 +1202,7 @@
     state.append(slideState);
     state.append(backendState);
     state.append(msaaState);
+    state.append(prState);
     state.append(softkeyState);
     state.append(fpsState);
 
@@ -1189,6 +1247,21 @@
             fWindow->setRequestedDisplayParams(params);
             fWindow->inval();
             updateTitle();
+            updateUIState();
+        }
+    } else if (stateName.equals(kPathRendererStateName)) {
+        DisplayParams params = fWindow->getRequestedDisplayParams();
+        for (const auto& pair : gPathRendererNames) {
+            if (pair.second == stateValue.c_str()) {
+                if (params.fGrContextOptions.fGpuPathRenderers != pair.first) {
+                    params.fGrContextOptions.fGpuPathRenderers = pair.first;
+                    fWindow->setRequestedDisplayParams(params);
+                    fWindow->inval();
+                    updateTitle();
+                    updateUIState();
+                }
+                break;
+            }
         }
     } else if (stateName.equals(kSoftkeyStateName)) {
         if (!stateValue.equals(kSoftkeyHint)) {
diff --git a/tools/viewer/sk_app/DisplayParams.h b/tools/viewer/sk_app/DisplayParams.h
index 0c649c0..959735e 100644
--- a/tools/viewer/sk_app/DisplayParams.h
+++ b/tools/viewer/sk_app/DisplayParams.h
@@ -7,6 +7,7 @@
 #ifndef DisplayParams_DEFINED
 #define DisplayParams_DEFINED
 
+#include "GrContextOptions.h"
 #include "SkImageInfo.h"
 
 namespace sk_app {
@@ -20,6 +21,7 @@
     SkColorType         fColorType;
     sk_sp<SkColorSpace> fColorSpace;
     int                 fMSAASampleCount;
+    GrContextOptions    fGrContextOptions;
 };
 
 }   // namespace sk_app
diff --git a/tools/viewer/sk_app/GLWindowContext.cpp b/tools/viewer/sk_app/GLWindowContext.cpp
index 89cd858..ff56ce8 100644
--- a/tools/viewer/sk_app/GLWindowContext.cpp
+++ b/tools/viewer/sk_app/GLWindowContext.cpp
@@ -7,7 +7,6 @@
  */
 
 #include "GrContext.h"
-#include "SkCommonFlagsPathRenderer.h"
 #include "SkSurface.h"
 #include "GLWindowContext.h"
 
@@ -37,11 +36,9 @@
     this->onInitializeContext();
     SkASSERT(nullptr == fContext);
 
-    GrContextOptions ctxOptions;
-    ctxOptions.fGpuPathRenderers = CollectGpuPathRenderersFromFlags();
     fBackendContext.reset(GrGLCreateNativeInterface());
     fContext = GrContext::Create(kOpenGL_GrBackend, (GrBackendContext)fBackendContext.get(),
-                                 ctxOptions);
+                                 fDisplayParams.fGrContextOptions);
     if (!fContext && fDisplayParams.fMSAASampleCount) {
         fDisplayParams.fMSAASampleCount /= 2;
         this->initializeContext();
diff --git a/tools/viewer/sk_app/VulkanWindowContext.cpp b/tools/viewer/sk_app/VulkanWindowContext.cpp
index b8071d3..8e8c059 100644
--- a/tools/viewer/sk_app/VulkanWindowContext.cpp
+++ b/tools/viewer/sk_app/VulkanWindowContext.cpp
@@ -8,7 +8,6 @@
 
 #include "GrContext.h"
 #include "GrRenderTarget.h"
-#include "SkCommonFlagsPathRenderer.h"
 #include "SkAutoMalloc.h"
 #include "SkSurface.h"
 #include "VulkanWindowContext.h"
@@ -62,10 +61,8 @@
     GET_DEV_PROC(AcquireNextImageKHR);
     GET_DEV_PROC(QueuePresentKHR);
 
-    GrContextOptions ctxOptions;
-    ctxOptions.fGpuPathRenderers = CollectGpuPathRenderersFromFlags();
     fContext = GrContext::Create(kVulkan_GrBackend, (GrBackendContext) fBackendContext.get(),
-                                 ctxOptions);
+                                 params.fGrContextOptions);
 
     fSurface = createVkSurface(instance);
     if (VK_NULL_HANDLE == fSurface) {
diff --git a/tools/viewer/sk_app/Window.cpp b/tools/viewer/sk_app/Window.cpp
index d57daf9..a63e1e3 100644
--- a/tools/viewer/sk_app/Window.cpp
+++ b/tools/viewer/sk_app/Window.cpp
@@ -146,6 +146,13 @@
     return fWindowContext->stencilBits();
 }
 
+const GrContext* Window::getGrContext() const {
+    if (!fWindowContext) {
+        return nullptr;
+    }
+    return fWindowContext->getGrContext();
+}
+
 void Window::inval() {
     if (!fWindowContext) {
         return;
diff --git a/tools/viewer/sk_app/Window.h b/tools/viewer/sk_app/Window.h
index 4ae23b1..85cf3ac 100644
--- a/tools/viewer/sk_app/Window.h
+++ b/tools/viewer/sk_app/Window.h
@@ -14,6 +14,7 @@
 #include "SkTypes.h"
 #include "SkJSONCPP.h"
 
+class GrContext;
 class SkCanvas;
 class SkSurface;
 
@@ -198,6 +199,9 @@
     int sampleCount() const;
     int stencilBits() const;
 
+    // Returns null if there is not a GPU backend or if the backend is not yet created.
+    const GrContext* getGrContext() const;
+
 protected:
     Window();