Bug 1462642 - [Wayland/EGL] Use dummy wl_egl_window instead of PBuffer, r=lsalzman
authorMartin Stransky <stransky@redhat.com>
Tue, 22 May 2018 10:34:32 +0200
changeset 419663 9250f12dd57cadb1015a9fcb23eb0ead78c2916a
parent 419662 1c6cfeb419c3157fc3dd6ae564358f7af8ca9cd1
child 419664 ac922efad9fd198f2401a40e0b921f22aa31098d
push id34042
push usernerli@mozilla.com
push dateThu, 24 May 2018 15:40:41 +0000
treeherdermozilla-central@cd3e33ad7970 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslsalzman
bugs1462642
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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,