Bug 1462642 - [Wayland/EGL] Use dummy wl_egl_window instead of PBuffer, r?lsalzman draft
authorMartin Stransky <stransky@redhat.com>
Tue, 22 May 2018 10:34:32 +0200
changeset 798702 b9cb9ac76aea9e8955950aaa07b83c4b3c7c7e5e
parent 797566 000309d44abb81084276c9fa977c57aee7126053
push id110831
push userstransky@redhat.com
push dateWed, 23 May 2018 12:05:15 +0000
reviewerslsalzman
bugs1462642
milestone62.0a1
Bug 1462642 - [Wayland/EGL] Use dummy wl_egl_window instead of PBuffer, r?lsalzman Wayland on desktop does not support/implement PBuffer. As a workaround we create a dummy wl_egl_window instead and render to it. As a side effect we need to store and release this wl_egl_window along the associated EGLSurface on Wayland. MozReview-Commit-ID: 1NlzZsOzXz9
gfx/gl/GLContextEGL.h
gfx/gl/GLContextProviderEGL.cpp
--- a/gfx/gl/GLContextEGL.h
+++ b/gfx/gl/GLContextEGL.h
@@ -129,16 +129,20 @@ protected:
     bool mIsDoubleBuffered;
     bool mCanBindToTexture;
     bool mShareWithEGLImage;
     bool mOwnsContext;
 
     static EGLSurface CreatePBufferSurfaceTryingPowerOfTwo(EGLConfig config,
                                                            EGLenum bindToTextureFormat,
                                                            gfx::IntSize& pbsize);
+#if defined(MOZ_WAYLAND)
+    static EGLSurface CreateWaylandBufferSurface(EGLConfig config,
+                                                 gfx::IntSize& pbsize);
+#endif
 #if defined(MOZ_WIDGET_ANDROID)
 public:
     EGLSurface CreateCompatibleSurface(void* aWindow);
 #endif // defined(MOZ_WIDGET_ANDROID)
 };
 
 bool CreateConfig(EGLConfig* config, int32_t depth, bool enableDepthBuffer);
 
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -58,23 +58,63 @@
 #include "mozilla/layers/CompositorOptions.h"
 #include "mozilla/widget/CompositorWidget.h"
 #include "nsDebug.h"
 #include "nsIWidget.h"
 #include "nsThreadUtils.h"
 #include "ScopedGLHelpers.h"
 #include "TextureImageEGL.h"
 
+#if defined(MOZ_WAYLAND)
+#include "nsAutoPtr.h"
+#include "nsDataHashtable.h"
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdkwayland.h>
+#include <wayland-egl.h>
+#include <dlfcn.h>
+#endif
+
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace gl {
 
 using namespace mozilla::widget;
 
+#if defined(MOZ_WAYLAND)
+class WaylandGLSurface {
+public:
+    WaylandGLSurface(struct wl_surface *aWaylandSurface,
+                         struct wl_egl_window *aEGLWindow);
+    ~WaylandGLSurface();
+private:
+    struct wl_surface     *mWaylandSurface;
+    struct wl_egl_window  *mEGLWindow;
+};
+
+static nsDataHashtable<nsPtrHashKey<void>, WaylandGLSurface*>
+        sWaylandGLSurface;
+
+void
+DeleteWaylandGLSurface(EGLSurface surface)
+{
+    // We're running on Wayland which means our EGLSurface may
+    // have attached Wayland backend data which must be released.
+    if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
+        auto entry = sWaylandGLSurface.Lookup(surface);
+        if (entry) {
+            delete entry.Data();
+            entry.Remove();
+        }
+    }
+}
+#endif
+
 #define ADD_ATTR_2(_array, _k, _v) do {         \
     (_array).AppendElement(_k);                 \
     (_array).AppendElement(_v);                 \
 } while (0)
 
 #define ADD_ATTR_1(_array, _k) do {             \
     (_array).AppendElement(_k);                 \
 } while (0)
@@ -120,16 +160,19 @@ is_power_of_two(int v)
 static void
 DestroySurface(EGLSurface oldSurface) {
     if (oldSurface != EGL_NO_SURFACE) {
         // TODO: This breaks TLS MakeCurrent caching.
         sEGLLibrary.fMakeCurrent(EGL_DISPLAY(),
                                  EGL_NO_SURFACE, EGL_NO_SURFACE,
                                  EGL_NO_CONTEXT);
         sEGLLibrary.fDestroySurface(EGL_DISPLAY(), oldSurface);
+#if defined(MOZ_WAYLAND)
+        DeleteWaylandGLSurface(oldSurface);
+#endif
     }
 }
 
 static EGLSurface
 CreateFallbackSurface(const EGLConfig& config)
 {
     if (sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_surfaceless_context)) {
         // We don't need a PBuffer surface in this case
@@ -617,16 +660,62 @@ TRY_AGAIN_POWER_OF_TWO:
 
         NS_WARNING("Failed to create pbuffer surface");
         return nullptr;
     }
 
     return surface;
 }
 
+#if defined(MOZ_WAYLAND)
+WaylandGLSurface::WaylandGLSurface(struct wl_surface *aWaylandSurface,
+                                           struct wl_egl_window *aEGLWindow)
+    : mWaylandSurface(aWaylandSurface)
+    , mEGLWindow(aEGLWindow)
+{
+}
+
+WaylandGLSurface::~WaylandGLSurface()
+{
+    wl_egl_window_destroy(mEGLWindow);
+    wl_surface_destroy(mWaylandSurface);
+}
+
+EGLSurface
+GLContextEGL::CreateWaylandBufferSurface(EGLConfig config,
+                                         mozilla::gfx::IntSize& pbsize)
+{
+    // Available as of GTK 3.8+
+    static auto sGdkWaylandDisplayGetWlCompositor =
+        (wl_compositor *(*)(GdkDisplay *))
+        dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_compositor");
+
+    if (!sGdkWaylandDisplayGetWlCompositor)
+        return nullptr;
+
+    struct wl_compositor *compositor =
+        sGdkWaylandDisplayGetWlCompositor(gdk_display_get_default());
+    struct wl_surface *wlsurface = wl_compositor_create_surface(compositor);
+    struct wl_egl_window *eglwindow =
+        wl_egl_window_create(wlsurface, pbsize.width, pbsize.height);
+
+    EGLSurface surface =
+        sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config, eglwindow, 0);
+
+    if (surface) {
+        WaylandGLSurface* waylandData =
+            new WaylandGLSurface(wlsurface, eglwindow);
+        auto entry = sWaylandGLSurface.LookupForAdd(surface);
+        entry.OrInsert([&waylandData](){ return waylandData; });
+    }
+
+    return surface;
+}
+#endif
+
 static const EGLint kEGLConfigAttribsOffscreenPBuffer[] = {
     LOCAL_EGL_SURFACE_TYPE,    LOCAL_EGL_PBUFFER_BIT,
     LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT,
     // Old versions of llvmpipe seem to need this to properly create the pbuffer (bug 981856)
     LOCAL_EGL_RED_SIZE,        8,
     LOCAL_EGL_GREEN_SIZE,      8,
     LOCAL_EGL_BLUE_SIZE,       8,
     LOCAL_EGL_ALPHA_SIZE,      0,
@@ -836,17 +925,27 @@ GLContextProviderEGL::DestroyEGLSurface(
 }
 #endif // defined(ANDROID)
 
 static void
 FillContextAttribs(bool alpha, bool depth, bool stencil, bool bpp16,
                    bool es3, nsTArray<EGLint>* out)
 {
     out->AppendElement(LOCAL_EGL_SURFACE_TYPE);
+#if defined(MOZ_WAYLAND)
+    if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
+        // Wayland on desktop does not support PBuffer or FBO.
+        // We create a dummy wl_egl_window instead.
+        out->AppendElement(LOCAL_EGL_WINDOW_BIT);
+    } else {
+        out->AppendElement(LOCAL_EGL_PBUFFER_BIT);
+    }
+#else
     out->AppendElement(LOCAL_EGL_PBUFFER_BIT);
+#endif
 
     out->AppendElement(LOCAL_EGL_RENDERABLE_TYPE);
     if (es3) {
         out->AppendElement(LOCAL_EGL_OPENGL_ES3_BIT_KHR);
     } else {
         out->AppendElement(LOCAL_EGL_OPENGL_ES2_BIT);
     }
 
@@ -955,31 +1054,42 @@ GLContextEGL::CreateEGLPBufferOffscreenC
         return nullptr;
     }
 
     if (GLContext::ShouldSpew()) {
         sEGLLibrary.DumpEGLConfig(config);
     }
 
     mozilla::gfx::IntSize pbSize(size);
-    EGLSurface surface = GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(config,
-                                                                            LOCAL_EGL_NONE,
-                                                                            pbSize);
+    EGLSurface surface = nullptr;
+#if defined(MOZ_WAYLAND)
+    if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
+        surface = GLContextEGL::CreateWaylandBufferSurface(config, pbSize);
+    } else
+#endif
+    {
+        surface = GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(config,
+                                                                     LOCAL_EGL_NONE,
+                                                                     pbSize);
+    }
     if (!surface) {
         *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_POT");
         NS_WARNING("Failed to create PBuffer for context!");
         return nullptr;
     }
 
     RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(flags, configCaps, true,
                                                             config, surface,
                                                             out_failureId);
     if (!gl) {
         NS_WARNING("Failed to create GLContext from PBuffer");
         sEGLLibrary.fDestroySurface(sEGLLibrary.Display(), surface);
+#if defined(MOZ_WAYLAND)
+        DeleteWaylandGLSurface(surface);
+#endif
         return nullptr;
     }
 
     return gl.forget();
 }
 
 /*static*/ already_AddRefed<GLContext>
 GLContextProviderEGL::CreateHeadless(CreateContextFlags flags,