Bug 1769499 [Linux/EGL] Create GBM based surface for headless GL context r=jgilbert
authorstransky <stransky@redhat.com>
Thu, 19 May 2022 15:18:22 +0000
changeset 618250 c1ef18d0a27ec4c2736cbb2c8ae8b061bd728f2e
parent 618249 1b7f9123f9c3385e7b7257db60a72eeeab565989
child 618251 d75eb3fb217f93514703f74785fa7b08281fb279
child 618252 88d31f6c458de91707eceffc89c3d1f59d642e37
push id39720
push usernbeleuzu@mozilla.com
push dateThu, 19 May 2022 18:03:18 +0000
treeherdermozilla-central@c1ef18d0a27e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgilbert
bugs1769499
milestone102.0a1
first release with
nightly linux32
c1ef18d0a27e / 102.0a1 / 20220519180318 / files
nightly linux64
c1ef18d0a27e / 102.0a1 / 20220519180318 / files
nightly mac
c1ef18d0a27e / 102.0a1 / 20220519180318 / files
nightly win32
c1ef18d0a27e / 102.0a1 / 20220519180318 / files
nightly win64
c1ef18d0a27e / 102.0a1 / 20220519180318 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1769499 [Linux/EGL] Create GBM based surface for headless GL context r=jgilbert Differential Revision: https://phabricator.services.mozilla.com/D146555
gfx/gl/GLContextEGL.h
gfx/gl/GLContextProviderEGL.cpp
gfx/gl/GLLibraryEGL.cpp
--- a/gfx/gl/GLContextEGL.h
+++ b/gfx/gl/GLContextEGL.h
@@ -139,18 +139,22 @@ class GLContextEGL final : public GLCont
   bool mOwnsContext = true;
 
   nsIntRegion mDamageRegion;
 
   static EGLSurface CreatePBufferSurfaceTryingPowerOfTwo(
       EglDisplay&, EGLConfig, EGLenum bindToTextureFormat,
       gfx::IntSize& pbsize);
 
+#ifdef MOZ_WAYLAND
   static EGLSurface CreateWaylandBufferSurface(EglDisplay&, EGLConfig,
                                                gfx::IntSize& pbsize);
+  static EGLSurface CreateGBMBufferSurface(EglDisplay&, EGLConfig,
+                                           gfx::IntSize& pbsize);
+#endif
 
  public:
   EGLSurface CreateCompatibleSurface(void* aWindow) const;
 };
 
 bool CreateConfig(EglDisplay&, EGLConfig* aConfig, int32_t aDepth,
                   bool aEnableDepthBuffer, bool aUseGles,
                   bool aAllowFallback = true);
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -71,59 +71,55 @@
 #include "mozilla/widget/CompositorWidget.h"
 #include "nsDebug.h"
 #include "nsIWidget.h"
 #include "nsThreadUtils.h"
 #include "ScopedGLHelpers.h"
 
 #if defined(MOZ_WIDGET_GTK)
 #  include "mozilla/widget/GtkCompositorWidget.h"
-#  include "mozilla/WidgetUtilsGtk.h"
 #  if defined(MOZ_WAYLAND)
 #    include <gdk/gdkwayland.h>
 #    include <wayland-egl.h>
-#    define MOZ_GTK_WAYLAND 1
+#    include "mozilla/widget/nsWaylandDisplay.h"
+#    include "mozilla/widget/DMABufLibWrapper.h"
 #  endif
 #endif
 
 struct wl_egl_window;
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace gl {
 
 using namespace mozilla::widget;
 
 #if defined(MOZ_WAYLAND)
-class WaylandGLSurface {
+class SavedGLSurface {
  public:
-  WaylandGLSurface(struct wl_surface* aWaylandSurface,
-                   struct wl_egl_window* aEGLWindow);
-  ~WaylandGLSurface();
+  SavedGLSurface(struct wl_surface* aWaylandSurface,
+                 struct wl_egl_window* aEGLWindow);
+  explicit SavedGLSurface(struct gbm_surface* aGbmSurface);
+  ~SavedGLSurface();
 
  private:
-  struct wl_surface* mWaylandSurface;
-  struct wl_egl_window* mEGLWindow;
+  struct wl_surface* mWaylandSurface = nullptr;
+  struct gbm_surface* mGbmSurface = nullptr;
+  struct wl_egl_window* mEGLWindow = nullptr;
 };
 
-static nsTHashMap<nsPtrHashKey<void>, WaylandGLSurface*> sWaylandGLSurface;
+static nsTHashMap<nsPtrHashKey<void>, SavedGLSurface*> sSavedGLSurfaces;
 
-void DeleteWaylandGLSurface(EGLSurface surface) {
-#  ifdef MOZ_GTK_WAYLAND
-  // We're running on Wayland which means our EGLSurface may
-  // have attached Wayland backend data which must be released.
-  if (GdkIsWaylandDisplay()) {
-    auto entry = sWaylandGLSurface.Lookup(surface);
-    if (entry) {
-      delete entry.Data();
-      entry.Remove();
-    }
+void DeleteSavedGLSurface(EGLSurface surface) {
+  auto entry = sSavedGLSurfaces.Lookup(surface);
+  if (entry) {
+    delete entry.Data();
+    entry.Remove();
   }
-#  endif
 }
 #endif
 
 static bool CreateConfigScreen(EglDisplay&, EGLConfig* const aConfig,
                                const bool aEnableDepthBuffer,
                                const bool aUseGles);
 
 // append three zeros at the end of attribs list to work around
@@ -156,17 +152,17 @@ static bool is_power_of_two(int v) {
 }
 
 static void DestroySurface(EglDisplay& egl, const EGLSurface oldSurface) {
   if (oldSurface != EGL_NO_SURFACE) {
     // TODO: This breaks TLS MakeCurrent caching.
     egl.fMakeCurrent(EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
     egl.fDestroySurface(oldSurface);
 #if defined(MOZ_WAYLAND)
-    DeleteWaylandGLSurface(oldSurface);
+    DeleteSavedGLSurface(oldSurface);
 #endif
   }
 }
 
 static EGLSurface CreateFallbackSurface(EglDisplay& egl,
                                         const EGLConfig& config) {
   if (egl.IsExtensionSupported(EGLExtension::KHR_surfaceless_context)) {
     // We don't need a PBuffer surface in this case
@@ -803,52 +799,75 @@ 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)
+SavedGLSurface::SavedGLSurface(struct wl_surface* aWaylandSurface,
+                               struct wl_egl_window* aEGLWindow)
     : mWaylandSurface(aWaylandSurface), mEGLWindow(aEGLWindow) {}
 
-WaylandGLSurface::~WaylandGLSurface() {
-  wl_egl_window_destroy(mEGLWindow);
-  wl_surface_destroy(mWaylandSurface);
+SavedGLSurface::SavedGLSurface(struct gbm_surface* aGbmSurface)
+    : mGbmSurface(aGbmSurface) {}
+
+SavedGLSurface::~SavedGLSurface() {
+  if (mEGLWindow) {
+    wl_egl_window_destroy(mEGLWindow);
+  }
+  if (mWaylandSurface) {
+    wl_surface_destroy(mWaylandSurface);
+  }
+  if (mGbmSurface) {
+    nsGbmLib::DestroySurface(mGbmSurface);
+  }
 }
-#endif
 
 // static
 EGLSurface GLContextEGL::CreateWaylandBufferSurface(
     EglDisplay& egl, EGLConfig config, mozilla::gfx::IntSize& pbsize) {
   wl_egl_window* eglwindow = nullptr;
 
-#ifdef MOZ_GTK_WAYLAND
   struct wl_compositor* compositor =
       gdk_wayland_display_get_wl_compositor(gdk_display_get_default());
   struct wl_surface* wlsurface = wl_compositor_create_surface(compositor);
   eglwindow = wl_egl_window_create(wlsurface, pbsize.width, pbsize.height);
-#endif
   if (!eglwindow) return nullptr;
 
   const auto surface = egl.fCreateWindowSurface(
       config, reinterpret_cast<EGLNativeWindowType>(eglwindow), 0);
   if (surface) {
-#ifdef MOZ_GTK_WAYLAND
-    MOZ_ASSERT(!sWaylandGLSurface.Contains(surface));
-    sWaylandGLSurface.LookupOrInsert(
-        surface, new WaylandGLSurface(wlsurface, eglwindow));
-#endif
+    MOZ_ASSERT(!sSavedGLSurfaces.Contains(surface));
+    sSavedGLSurfaces.LookupOrInsert(surface,
+                                    new SavedGLSurface(wlsurface, eglwindow));
   }
-
   return surface;
 }
 
+EGLSurface GLContextEGL::CreateGBMBufferSurface(EglDisplay& egl,
+                                                EGLConfig config,
+                                                mozilla::gfx::IntSize& pbsize) {
+  struct gbm_surface* gbmSurface = nsGbmLib::CreateSurface(
+      GetDMABufDevice()->GetGbmDevice(), pbsize.width, pbsize.height,
+      GBM_FORMAT_ARGB8888, GBM_BO_USE_RENDERING);
+  if (!gbmSurface) {
+    return nullptr;
+  }
+  const auto surface = egl.fCreateWindowSurface(
+      config, reinterpret_cast<EGLNativeWindowType>(gbmSurface), 0);
+  if (surface) {
+    MOZ_ASSERT(!sSavedGLSurfaces.Contains(surface));
+    sSavedGLSurfaces.LookupOrInsert(surface, new SavedGLSurface(gbmSurface));
+  }
+  return surface;
+}
+#endif
+
 static const EGLint kEGLConfigAttribsRGB16[] = {
     LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT,
     LOCAL_EGL_RED_SIZE,     5,
     LOCAL_EGL_GREEN_SIZE,   6,
     LOCAL_EGL_BLUE_SIZE,    5,
     LOCAL_EGL_ALPHA_SIZE,   0};
 
 static const EGLint kEGLConfigAttribsRGB24[] = {
@@ -1008,20 +1027,21 @@ EGLSurface GLContextEGL::CreateCompatibl
     gfxCriticalError() << "CreateCompatibleSurface failed: "
                        << hexa(GetError());
   }
   return surface;
 }
 
 static void FillContextAttribs(bool es3, bool useGles, nsTArray<EGLint>* out) {
   out->AppendElement(LOCAL_EGL_SURFACE_TYPE);
-#ifdef MOZ_GTK_WAYLAND
-  if (GdkIsWaylandDisplay()) {
+#ifdef MOZ_WAYLAND
+  if (GdkIsWaylandDisplay() || !gdk_display_get_default()) {
     // Wayland on desktop does not support PBuffer or FBO.
     // We create a dummy wl_egl_window instead.
+    // LOCAL_EGL_WINDOW_BIT is also needed for GBM backend.
     out->AppendElement(LOCAL_EGL_WINDOW_BIT);
   } else
 #endif
   {
     out->AppendElement(LOCAL_EGL_PBUFFER_BIT);
   }
 
   if (useGles) {
@@ -1131,18 +1151,20 @@ RefPtr<GLContextEGL> GLContextEGL::Creat
   }
 
   if (GLContext::ShouldSpew()) {
     egl->DumpEGLConfig(config);
   }
 
   mozilla::gfx::IntSize pbSize(size);
   EGLSurface surface = nullptr;
-#ifdef MOZ_GTK_WAYLAND
-  if (GdkIsWaylandDisplay()) {
+#ifdef MOZ_WAYLAND
+  if (!gdk_display_get_default()) {
+    surface = GLContextEGL::CreateGBMBufferSurface(*egl, config, pbSize);
+  } else if (GdkIsWaylandDisplay()) {
     surface = GLContextEGL::CreateWaylandBufferSurface(*egl, config, pbSize);
   } else
 #endif
   {
     surface = GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(
         *egl, config, LOCAL_EGL_NONE, pbSize);
   }
   if (!surface) {
@@ -1154,17 +1176,17 @@ RefPtr<GLContextEGL> GLContextEGL::Creat
   auto fullDesc = GLContextDesc{desc};
   fullDesc.isOffscreen = true;
   RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(
       egl, fullDesc, config, surface, useGles, out_failureId);
   if (!gl) {
     NS_WARNING("Failed to create GLContext from PBuffer");
     egl->fDestroySurface(surface);
 #if defined(MOZ_WAYLAND)
-    DeleteWaylandGLSurface(surface);
+    DeleteSavedGLSurface(surface);
 #endif
     return nullptr;
   }
 
   return gl;
 }
 
 /*static*/
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -34,16 +34,17 @@
 #include "GLContextProvider.h"
 #include "GLLibraryLoader.h"
 #include "GLReadTexImageHelper.h"
 #include "ScopedGLHelpers.h"
 #ifdef MOZ_WIDGET_GTK
 #  include "mozilla/WidgetUtilsGtk.h"
 #  ifdef MOZ_WAYLAND
 #    include "mozilla/widget/nsWaylandDisplay.h"
+#    include "mozilla/widget/DMABufLibWrapper.h"
 #  endif  // MOZ_WIDGET_GTK
 #  include <gdk/gdk.h>
 #endif  // MOZ_WAYLAND
 
 #include <mutex>  // for call_once
 
 namespace mozilla {
 namespace gl {
@@ -807,16 +808,21 @@ std::shared_ptr<EglDisplay> GLLibraryEGL
     // Some drivers doesn't support EGL_DEFAULT_DISPLAY
     GdkDisplay* gdkDisplay = gdk_display_get_default();
     if (widget::GdkIsWaylandDisplay(gdkDisplay)) {
       nativeDisplay = widget::WaylandDisplayGetWLDisplay(gdkDisplay);
       if (!nativeDisplay) {
         NS_WARNING("Failed to get wl_display.");
         return nullptr;
       }
+    } else if (!gdkDisplay) {
+      // We're running without display connection.
+      // That can happens on RDD process which is not connected to compositor.
+      // In such case use GBM based display.
+      nativeDisplay = widget::GetDMABufDevice()->GetGbmDevice();
     }
 #endif
     ret = GetAndInitDisplay(*this, nativeDisplay, aProofOfLock);
   }
 
   if (!ret) {
     if (out_failureId->IsEmpty()) {
       *out_failureId = "FEATURE_FAILURE_NO_DISPLAY"_ns;