Bug 1650583 [Linux/EGL] Implement GLContextEGL::FindVisual(), r=jgilbert
authorstransky <stransky@redhat.com>
Thu, 03 Sep 2020 09:02:44 +0000
changeset 547651 915c19da6fceed6c61933b0458f216391e3ebe68
parent 547650 dd2d6a667e9c3d7e8caf82840af3b622244413d6
child 547652 e9d534d12e77469312104876f513cb749c25cce5
push id125672
push usercbrindusan@mozilla.com
push dateThu, 03 Sep 2020 09:12:10 +0000
treeherderautoland@e9d534d12e77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgilbert
bugs1650583, 1478454
milestone82.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 1650583 [Linux/EGL] Implement GLContextEGL::FindVisual(), r=jgilbert Implement GLContextEGL::FindVisual() as a EGL counterpart of GLContextGLX::FindVisual() used by GLX. We need to make sure that GdkWindow uses the same visual as GL framebuffer we use for it. That was already implemented for GLX backend (Bug 1478454). The visual match is implemented by visual parameter at CreateConfig()/CreateConfigScreen() routines and when it's non-zero, try to find exact match based on visual ID. Differential Revision: https://phabricator.services.mozilla.com/D87636
gfx/gl/GLContextEGL.h
gfx/gl/GLContextProviderEGL.cpp
--- a/gfx/gl/GLContextEGL.h
+++ b/gfx/gl/GLContextEGL.h
@@ -112,16 +112,21 @@ class GLContextEGL final : public GLCont
       const gfx::IntSize& size, nsACString* const out_FailureId);
   static RefPtr<GLContextEGL> CreateEGLPBufferOffscreenContextImpl(
       std::shared_ptr<EglDisplay>, const GLContextCreateDesc&,
       const gfx::IntSize& size, bool aUseGles, nsACString* const out_FailureId);
 
   static EGLSurface CreateEGLSurfaceForCompositorWidget(
       widget::CompositorWidget* aCompositorWidget, const EGLConfig aConfig);
 
+#ifdef MOZ_X11
+  static bool FindVisual(bool aUseWebRender, bool useAlpha,
+                         int* const out_visualId);
+#endif
+
  protected:
   friend class GLContextProviderEGL;
   friend class GLContextEGLFactory;
 
   virtual void OnMarkDestroyed() override;
 
  public:
   const std::shared_ptr<EglDisplay> mEgl;
@@ -151,16 +156,17 @@ class GLContextEGL final : public GLCont
   static EGLSurface CreateWaylandBufferSurface(EglDisplay&, EGLConfig,
                                                gfx::IntSize& pbsize);
 
  public:
   EGLSurface CreateCompatibleSurface(void* aWindow) const;
 };
 
 // -
-
+// aVisual is used in Linux only to exactly match window and framebuffer
+// visuals on NVIDIA drivers (Bug 1478454).
 bool CreateConfig(EglDisplay&, EGLConfig* aConfig, int32_t depth,
-                  bool aEnableDepthBuffer, bool aUseGles);
+                  bool aEnableDepthBuffer, bool aUseGles, int aVisual = 0);
 
 }  // namespace gl
 }  // namespace mozilla
 
 #endif  // GLCONTEXTEGL_H_
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -87,16 +87,25 @@ inline bool IsWaylandDisplay() {
 #ifdef MOZ_GTK_WAYLAND
   return gdk_display_get_default() &&
          GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default());
 #else
   return false;
 #endif
 }
 
+inline bool IsX11Display() {
+#ifdef MOZ_WIDGET_GTK
+  return gdk_display_get_default() &&
+         GDK_IS_X11_DISPLAY(gdk_display_get_default());
+#else
+  return false;
+#endif
+}
+
 struct wl_egl_window;
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace gl {
 
 using namespace mozilla::widget;
@@ -125,17 +134,17 @@ void DeleteWaylandGLSurface(EGLSurface s
       entry.Remove();
     }
   }
 }
 #endif
 
 static bool CreateConfigScreen(EglDisplay&, EGLConfig* const aConfig,
                                const bool aEnableDepthBuffer,
-                               const bool aUseGles);
+                               const bool aUseGles, int aVisual = 0);
 
 // append three zeros at the end of attribs list to work around
 // EGL implementation bugs that iterate until they find 0, instead of
 // EGL_NONE. See bug 948406.
 #define EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS \
   LOCAL_EGL_NONE, 0, 0, 0
 
 static EGLint kTerminationAttribs[] = {
@@ -251,47 +260,68 @@ already_AddRefed<GLContext> GLContextEGL
   }
   const auto egl = lib->CreateDisplay(true, &failureId);
   if (!egl) {
     gfxCriticalNote << "Failed[3] to create EGL library  display: "
                     << failureId.get();
     return nullptr;
   }
 
+  int visualID = 0;
+  if (IsX11Display()) {
+#ifdef MOZ_X11
+    GdkDisplay* gdkDisplay = gdk_display_get_default();
+    auto display = gdkDisplay ? GDK_DISPLAY_XDISPLAY(gdkDisplay) : nullptr;
+    if (display) {
+      XWindowAttributes windowAttrs;
+      if (!XGetWindowAttributes(display, (Window)aWindow, &windowAttrs)) {
+        NS_WARNING("[EGL] XGetWindowAttributes() failed");
+        return nullptr;
+      }
+      visualID = XVisualIDFromVisual(windowAttrs.visual);
+    }
+#endif
+  }
+
   bool doubleBuffered = true;
 
   EGLConfig config;
   if (aWebRender && egl->mLib->IsANGLE()) {
     // Force enable alpha channel to make sure ANGLE use correct framebuffer
     // formart
     const int bpp = 32;
     const bool withDepth = true;
     if (!CreateConfig(*egl, &config, bpp, withDepth, aUseGles)) {
       gfxCriticalNote << "Failed to create EGLConfig for WebRender ANGLE!";
       return nullptr;
     }
   } else {
     if (aDepth) {
-      if (!CreateConfig(*egl, &config, aDepth, aWebRender, aUseGles)) {
+      if (!CreateConfig(*egl, &config, aDepth, aWebRender, aUseGles,
+                        visualID)) {
         gfxCriticalNote
             << "Failed to create EGLConfig for WebRender with depth!";
         return nullptr;
       }
     } else {
       if (!CreateConfigScreen(*egl, &config,
-                              /* aEnableDepthBuffer */ aWebRender, aUseGles)) {
+                              /* aEnableDepthBuffer */ aWebRender, aUseGles,
+                              visualID)) {
         gfxCriticalNote << "Failed to create EGLConfig!";
         return nullptr;
       }
     }
   }
 
   EGLSurface surface = EGL_NO_SURFACE;
   if (aWindow) {
     surface = mozilla::gl::CreateSurfaceFromNativeWindow(*egl, aWindow, config);
+    if (!surface) {
+      return nullptr;
+    }
   }
 
   CreateContextFlags flags = CreateContextFlags::NONE;
   if (aWebRender && aUseGles) {
     flags |= CreateContextFlags::PREFER_ES3;
   }
   if (!aWebRender) {
     flags |= CreateContextFlags::REQUIRE_COMPAT_PROFILE;
@@ -830,17 +860,17 @@ static const EGLint kEGLConfigAttribsRGB
 static const EGLint kEGLConfigAttribsRGBA32[] = {
     LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT,
     LOCAL_EGL_RED_SIZE,     8,
     LOCAL_EGL_GREEN_SIZE,   8,
     LOCAL_EGL_BLUE_SIZE,    8,
     LOCAL_EGL_ALPHA_SIZE,   8};
 
 bool CreateConfig(EglDisplay& egl, EGLConfig* aConfig, int32_t depth,
-                  bool aEnableDepthBuffer, bool aUseGles) {
+                  bool aEnableDepthBuffer, bool aUseGles, int aVisual) {
   EGLConfig configs[64];
   std::vector<EGLint> attribs;
   EGLint ncfg = ArrayLength(configs);
 
   switch (depth) {
     case 16:
       for (const auto& cur : kEGLConfigAttribsRGB16) {
         attribs.push_back(cur);
@@ -885,33 +915,44 @@ bool CreateConfig(EglDisplay& egl, EGLCo
          (depth == 32 && r == 8 && g == 8 && b == 8 && a == 8))) {
       EGLint z;
       if (aEnableDepthBuffer) {
         if (!egl.fGetConfigAttrib(config, LOCAL_EGL_DEPTH_SIZE, &z) ||
             z != 24) {
           continue;
         }
       }
+#if defined(MOZ_X11)
+      if (aVisual) {
+        int vis;
+        if (!egl.fGetConfigAttrib(config, LOCAL_EGL_NATIVE_VISUAL_ID, &vis) ||
+            aVisual != vis) {
+          continue;
+        }
+      }
+#endif
       *aConfig = config;
       return true;
     }
   }
   return false;
 }
 
 // Return true if a suitable EGLConfig was found and pass it out
 // through aConfig.  Return false otherwise.
 //
 // NB: It's entirely legal for the returned EGLConfig to be valid yet
 // have the value null.
+// aVisual is used in Linux only.
 static bool CreateConfigScreen(EglDisplay& egl, EGLConfig* const aConfig,
                                const bool aEnableDepthBuffer,
-                               const bool aUseGles) {
+                               const bool aUseGles, int aVisual) {
   int32_t depth = gfxVars::ScreenDepth();
-  if (CreateConfig(egl, aConfig, depth, aEnableDepthBuffer, aUseGles)) {
+  if (CreateConfig(egl, aConfig, depth, aEnableDepthBuffer, aUseGles,
+                   aVisual)) {
     return true;
   }
 #ifdef MOZ_WIDGET_ANDROID
   // Bug 736005
   // Android doesn't always support 16 bit so also try 24 bit
   if (depth == 16) {
     return CreateConfig(egl, aConfig, 24, aEnableDepthBuffer, aUseGles);
   }
@@ -1040,16 +1081,42 @@ static EGLConfig ChooseConfig(EglDisplay
       foundConfigs == 0) {
     return EGL_NO_CONFIG;
   }
 
   EGLConfig config = configs[0];
   return config;
 }
 
+#ifdef MOZ_X11
+/* static */
+bool GLContextEGL::FindVisual(bool aUseWebRender, bool useAlpha,
+                              int* const out_visualId) {
+  nsCString discardFailureId;
+  const auto egl = DefaultEglDisplay(&discardFailureId);
+  if (!egl) {
+    gfxCriticalNote
+        << "GLContextEGL::FindVisual(): Failed to load EGL library!";
+    return false;
+  }
+
+  EGLConfig config;
+  const int bpp = useAlpha ? 32 : 24;
+  if (!CreateConfig(*egl, &config, bpp, aUseWebRender, /* aUseGles */ false)) {
+    gfxCriticalNote
+        << "GLContextEGL::FindVisual(): Failed to create EGLConfig!";
+    return false;
+  }
+  if (egl->fGetConfigAttrib(config, LOCAL_EGL_NATIVE_VISUAL_ID, out_visualId)) {
+    return true;
+  }
+  return false;
+}
+#endif
+
 /*static*/
 RefPtr<GLContextEGL> GLContextEGL::CreateEGLPBufferOffscreenContextImpl(
     const std::shared_ptr<EglDisplay> egl, const GLContextCreateDesc& desc,
     const mozilla::gfx::IntSize& size, const bool useGles,
     nsACString* const out_failureId) {
   const EGLConfig config = ChooseConfig(*egl, desc, useGles);
   if (config == EGL_NO_CONFIG) {
     *out_failureId = "FEATURE_FAILURE_EGL_NO_CONFIG"_ns;