Add OpenGL support to Linux viewer

Also adds a command line option (--vulkan) to choose between
Vulkan and OpenGL.
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2011473003

Committed: https://skia.googlesource.com/skia/+/10b3815a11c39b154ec7b74a2af43e019b50d48c

Review-Url: https://codereview.chromium.org/2011473003
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index c4b8b26..b450f21 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -35,7 +35,7 @@
     return viewer->onTouch(owner, state, x, y);
 }
 
-DEFINE_bool2(fullscreen, f, true, "Run fullscreen.");
+DEFINE_bool2(fullscreen, f, false, "Run fullscreen.");
 DEFINE_string(key, "", "Space-separated key/value pairs to add to JSON identifying this builder.");
 DEFINE_string2(match, m, nullptr,
                "[~][^]substring[$] [...] of bench name to run.\n"
@@ -47,6 +47,7 @@
                "If a bench does not match any list entry,\n"
                "it is skipped unless some list entry starts with ~");
 DEFINE_string(skps, "skps", "Directory to read skps from.");
+DEFINE_bool(vulkan, true, "Run with Vulkan.");
 
 const char *kBackendTypeStrings[sk_app::Window::kBackendTypeCount] = {
     " [OpenGL]",
@@ -72,6 +73,9 @@
 
     SkCommandLineFlags::Parse(argc, argv);
 
+    fBackendType = FLAGS_vulkan ? sk_app::Window::kVulkan_BackendType
+                                : sk_app::Window::kNativeGL_BackendType;
+
     fWindow = Window::CreateNativeWindow(platformData);
     fWindow->attach(fBackendType, DisplayParams());
 
@@ -117,6 +121,7 @@
         this->changeZoomLevel(-1.f / 32.f);
         fWindow->inval();
     });
+#if 0  // this doesn't seem to work on any platform right now
 #ifndef SK_BUILD_FOR_ANDROID
     fCommands.addCommand('d', "Modes", "Change rendering backend", [this]() {
         fWindow->detach();
@@ -124,15 +129,17 @@
         if (sk_app::Window::kVulkan_BackendType == fBackendType) {
             fBackendType = sk_app::Window::kNativeGL_BackendType;
         } 
-        // TODO: get Vulkan -> OpenGL working without swapchain creation failure
+        // TODO: get Vulkan -> OpenGL working on Windows without swapchain creation failure
         //else if (sk_app::Window::kNativeGL_BackendType == fBackendType) {
         //    fBackendType = sk_app::Window::kVulkan_BackendType;
         //}
 
         fWindow->attach(fBackendType, DisplayParams());
         this->updateTitle();
+        fWindow->inval();
     });
 #endif
+#endif
 
     // set up slides
     this->initSlides();
diff --git a/tools/viewer/sk_app/VulkanWindowContext.cpp b/tools/viewer/sk_app/VulkanWindowContext.cpp
index 2e2b8a9..f192c69 100644
--- a/tools/viewer/sk_app/VulkanWindowContext.cpp
+++ b/tools/viewer/sk_app/VulkanWindowContext.cpp
@@ -388,6 +388,7 @@
         return;
     }
 
+    GR_VK_CALL(fBackendContext->fInterface, QueueWaitIdle(fPresentQueue));
     GR_VK_CALL(fBackendContext->fInterface, DeviceWaitIdle(fBackendContext->fDevice));
 
     this->destroyBuffers();
diff --git a/tools/viewer/sk_app/Window.cpp b/tools/viewer/sk_app/Window.cpp
index b78f39a..ba905e0 100644
--- a/tools/viewer/sk_app/Window.cpp
+++ b/tools/viewer/sk_app/Window.cpp
@@ -75,6 +75,7 @@
 
         fWindowContext->swapBuffers();
     } else {
+        printf("no backbuffer!?\n");
         // try recreating testcontext
     }
 }
diff --git a/tools/viewer/sk_app/unix/VulkanWindowContext_unix.cpp b/tools/viewer/sk_app/unix/VulkanWindowContext_unix.cpp
index ddfc8e3..02bf516 100644
--- a/tools/viewer/sk_app/unix/VulkanWindowContext_unix.cpp
+++ b/tools/viewer/sk_app/unix/VulkanWindowContext_unix.cpp
@@ -39,7 +39,7 @@
     surfaceCreateInfo.pNext = nullptr;
     surfaceCreateInfo.flags = 0;
     surfaceCreateInfo.connection = XGetXCBConnection(unixPlatformData->fDisplay);
-    surfaceCreateInfo.window = unixPlatformData->fHWnd;
+    surfaceCreateInfo.window = unixPlatformData->fWindow;
 
     VkResult res = createXcbSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface);
     if (VK_SUCCESS != res) {
@@ -64,10 +64,11 @@
                                           reinterpret_cast<ContextPlatformData_unix*>(platformData);
 
     Display* display = unixPlatformData->fDisplay;
+    VisualID visualID = unixPlatformData->fVisualInfo->visualid;
     VkBool32 check = getPhysicalDeviceXcbPresentationSupportKHR(physDev, 
                                                                 queueFamilyIndex, 
                                                                 XGetXCBConnection(display),
-                                                                unixPlatformData->fVisualID);
+                                                                visualID);
     return (VK_FALSE != check);
 }
 
diff --git a/tools/viewer/sk_app/unix/Window_unix.cpp b/tools/viewer/sk_app/unix/Window_unix.cpp
index 953eb36..431f156 100644
--- a/tools/viewer/sk_app/unix/Window_unix.cpp
+++ b/tools/viewer/sk_app/unix/Window_unix.cpp
@@ -9,6 +9,7 @@
 
 #include "SkUtils.h"
 #include "Timer.h"
+#include "../GLWindowContext.h"
 #include "../VulkanWindowContext.h"
 #include "Window_unix.h"
 
@@ -26,7 +27,7 @@
     Display* display = (Display*)platformData;
 
     Window_unix* window = new Window_unix();
-    if (!window->init(display)) {
+    if (!window->initWindow(display, nullptr)) {
         delete window;
         return nullptr;
     }
@@ -34,27 +35,87 @@
     return window;
 }
 
-bool Window_unix::init(Display* display) {
+const long kEventMask = ExposureMask | StructureNotifyMask | 
+                        KeyPressMask | KeyReleaseMask | 
+                        PointerMotionMask | ButtonPressMask | ButtonReleaseMask;
+
+bool Window_unix::initWindow(Display* display, const DisplayParams* params) {
+    if (params && params->fMSAASampleCount != fMSAASampleCount) {
+        this->closeWindow();
+    }
+    // we already have a window
+    if (fDisplay) {
+        return true;
+    } 
     fDisplay = display;
 
     fWidth = 1280;
     fHeight = 960;
-    fHWnd = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, fWidth, fHeight, 
-                                0, BlackPixel(display, DefaultScreen(display)), 
-                                BlackPixel(display, DefaultScreen(display)));
 
-    if (!fHWnd) {
+    // Attempt to create a window that supports GL
+    GLint att[] = {
+        GLX_RGBA,
+        GLX_DEPTH_SIZE, 24,
+        GLX_DOUBLEBUFFER,
+        GLX_STENCIL_SIZE, 8,
+        None
+    };
+    SkASSERT(nullptr == fVisualInfo);
+    if (params && params->fMSAASampleCount > 0) {
+        static const GLint kAttCount = SK_ARRAY_COUNT(att);
+        GLint msaaAtt[kAttCount + 4];
+        memcpy(msaaAtt, att, sizeof(att));
+        SkASSERT(None == msaaAtt[kAttCount - 1]);
+        msaaAtt[kAttCount - 1] = GLX_SAMPLE_BUFFERS_ARB;
+        msaaAtt[kAttCount + 0] = 1;
+        msaaAtt[kAttCount + 1] = GLX_SAMPLES_ARB;
+        msaaAtt[kAttCount + 2] = params->fMSAASampleCount;
+        msaaAtt[kAttCount + 3] = None;
+        fVisualInfo = glXChooseVisual(display, DefaultScreen(display), msaaAtt);
+        fMSAASampleCount = params->fMSAASampleCount;
+    }
+    if (nullptr == fVisualInfo) {
+        fVisualInfo = glXChooseVisual(display, DefaultScreen(display), att);
+        fMSAASampleCount = 0;
+    }
+
+    if (fVisualInfo) {
+        Colormap colorMap = XCreateColormap(display,
+                                            RootWindow(display, fVisualInfo->screen),
+                                            fVisualInfo->visual,
+                                            AllocNone);
+        XSetWindowAttributes swa;
+        swa.colormap = colorMap;
+        swa.event_mask = kEventMask;
+        fWindow = XCreateWindow(display,
+                                RootWindow(display, fVisualInfo->screen),
+                                0, 0, // x, y
+                                fWidth, fHeight,
+                                0, // border width
+                                fVisualInfo->depth,
+                                InputOutput,
+                                fVisualInfo->visual,
+                                CWEventMask | CWColormap,
+                                &swa);
+    } else {
+        // Create a simple window instead.  We will not be able to show GL
+        fWindow = XCreateSimpleWindow(display,
+                                      DefaultRootWindow(display),
+                                      0, 0,  // x, y
+                                      fWidth, fHeight,
+                                      0,     // border width
+                                      0,     // border value
+                                      0);    // background value
+        XSelectInput(display, fWindow, kEventMask);
+    }
+
+    if (!fWindow) {
         return false;
     }
 
-    // choose the events we care about
-    XSelectInput(display, fHWnd, 
-                 ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask | 
-                 PointerMotionMask | ButtonPressMask | ButtonReleaseMask);
-
     // set up to catch window delete message
     fWmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False);
-    XSetWMProtocols(display, fHWnd, &fWmDeleteMessage, 1);
+    XSetWMProtocols(display, fWindow, &fWmDeleteMessage, 1);
 
     // add to hashtable of windows
     gWindowMap.add(this);
@@ -66,6 +127,21 @@
     return true;
 }
 
+void Window_unix::closeWindow() {
+    if (fDisplay) {
+        this->detach();
+        SkASSERT(fGC);
+        XFreeGC(fDisplay, fGC);
+        fGC = nullptr;
+        gWindowMap.remove(fWindow);
+        XDestroyWindow(fDisplay, fWindow);
+        fWindow = 0;
+        fVisualInfo = nullptr;
+        fDisplay = nullptr;
+        fMSAASampleCount = 0;
+    }
+}
+
 static Window::Key get_key(KeySym keysym) {
     static const struct {
         KeySym      fXK;
@@ -108,6 +184,12 @@
 
 bool Window_unix::handleEvent(const XEvent& event) {
     switch (event.type) {
+        case MapNotify:
+            if (!fGC) {
+                fGC = XCreateGC(fDisplay, fWindow, 0, nullptr);
+            }
+            break;
+
         case ClientMessage:
             if ((Atom)event.xclient.data.l[0] == fWmDeleteMessage &&
                 gWindowMap.count() == 1) {
@@ -179,25 +261,29 @@
 void Window_unix::setTitle(const char* title) {
     XTextProperty textproperty;
     XStringListToTextProperty(const_cast<char**>(&title), 1, &textproperty);
-    XSetWMName(fDisplay, fHWnd, &textproperty);    
+    XSetWMName(fDisplay, fWindow, &textproperty);    
 }
 
 void Window_unix::show() {
-    XMapWindow(fDisplay, fHWnd);
+    XMapWindow(fDisplay, fWindow);
 }
 
 bool Window_unix::attach(BackendType attachType, const DisplayParams& params) {
+    this->initWindow(fDisplay, &params);
+
     ContextPlatformData_unix platformData;
     platformData.fDisplay = fDisplay;
-    platformData.fHWnd = fHWnd;
-    XWindowAttributes attribs;
-    XGetWindowAttributes(fDisplay, fHWnd, &attribs);
-    platformData.fVisualID = XVisualIDFromVisual(attribs.visual);
+    platformData.fWindow = fWindow;
+    platformData.fVisualInfo = fVisualInfo;
     switch (attachType) {
         case kVulkan_BackendType:
-        default:
             fWindowContext = VulkanWindowContext::Create((void*)&platformData, params);
             break;
+
+        case kNativeGL_BackendType:
+        default:
+            fWindowContext = GLWindowContext::Create((void*)&platformData, params);
+            break;
     }
 
     return (SkToBool(fWindowContext));
@@ -208,14 +294,14 @@
     event.type = Expose;
     event.xexpose.send_event = True;
     event.xexpose.display = fDisplay;
-    event.xexpose.window = fHWnd;
+    event.xexpose.window = fWindow;
     event.xexpose.x = 0;
     event.xexpose.y = 0;
     event.xexpose.width = fWidth;
     event.xexpose.height = fHeight;
     event.xexpose.count = 0;
     
-    XSendEvent(fDisplay, fHWnd, False, 0, &event);
+    XSendEvent(fDisplay, fWindow, False, 0, &event);
 }
 
 }   // namespace sk_app
diff --git a/tools/viewer/sk_app/unix/Window_unix.h b/tools/viewer/sk_app/unix/Window_unix.h
index c2156fc..fb6b22d 100644
--- a/tools/viewer/sk_app/unix/Window_unix.h
+++ b/tools/viewer/sk_app/unix/Window_unix.h
@@ -9,6 +9,7 @@
 #define Window_unix_DEFINED
 
 #include <X11/Xlib.h>
+#include <GL/glx.h>
 #include "../Window.h"
 #include "SkChecksum.h"
 #include "SkTDynamicHash.h"
@@ -18,17 +19,22 @@
 namespace sk_app {
 
 struct ContextPlatformData_unix {
-    Display* fDisplay;
-    XWindow  fHWnd;
-    VisualID fVisualID;
+    Display*     fDisplay;
+    XWindow      fWindow;
+    XVisualInfo* fVisualInfo;
 };
 
 class Window_unix : public Window {
 public:
-    Window_unix() : Window() {}
-    ~Window_unix() override {}
+    Window_unix() : Window()
+                  , fDisplay(nullptr)
+                  , fWindow(0)
+                  , fGC(nullptr)
+                  , fVisualInfo(nullptr)
+                  , fMSAASampleCount(0) {}
+    ~Window_unix() override { this->closeWindow(); }
 
-    bool init(Display* display);
+    bool initWindow(Display* display, const DisplayParams* params);
 
     void setTitle(const char*) override;
     void show() override;
@@ -40,7 +46,7 @@
     bool handleEvent(const XEvent& event);
 
     static const XWindow& GetKey(const Window_unix& w) {
-        return w.fHWnd;
+        return w.fWindow;
     }
 
     static uint32_t Hash(const XWindow& w) {
@@ -70,8 +76,13 @@
     }
 
 private:
-    Display* fDisplay;
-    XWindow  fHWnd;
+    void closeWindow();
+
+    Display*     fDisplay;
+    XWindow      fWindow;
+    GC           fGC;
+    XVisualInfo* fVisualInfo;
+    int          fMSAASampleCount;
 
     Atom     fWmDeleteMessage;