Bug 1556301 - Wayland-EGL/GLES support in glxtest r=stransky,aosmond
authorGreg V <greg@unrelenting.technology>
Fri, 03 Jan 2020 01:02:44 +0000
changeset 508655 29c31254a273ded97e30c61aa745e6c77a0bd731
parent 508654 aa760b4790051e4adafc612c543fedc749fec158
child 508656 2fc8232e5d3a5550bed2c993a01c8f512c3869f1
push id36975
push userdvarga@mozilla.com
push dateFri, 03 Jan 2020 09:47:38 +0000
treeherdermozilla-central@c0a6eb95b65c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersstransky, aosmond
bugs1556301, 1578598
milestone73.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 1556301 - Wayland-EGL/GLES support in glxtest r=stransky,aosmond This allows GfxInfoX11 to get GLES3 and Mesa version from Wayland-EGL, which allows automatic enablement of acceleration to work without Xwayland. This also fixes bug 1578598 - WebRender did not work correctly without this GL version information. Differential Revision: https://phabricator.services.mozilla.com/D57474
toolkit/xre/glxtest.cpp
widget/gtk/mozwayland/mozwayland.h
--- a/toolkit/xre/glxtest.cpp
+++ b/toolkit/xre/glxtest.cpp
@@ -31,16 +31,21 @@
 #  include <stdio.h>
 #endif
 
 #include "X11/Xlib.h"
 #include "X11/Xutil.h"
 
 #include "mozilla/Unused.h"
 
+#ifdef MOZ_WAYLAND
+#include "nsAppRunner.h"  // for IsWaylandDisabled
+#include "mozilla/widget/mozwayland.h"
+#endif
+
 // stuff from glx.h
 typedef struct __GLXcontextRec* GLXContext;
 typedef XID GLXPixmap;
 typedef XID GLXDrawable;
 /* GLX 1.3 and later */
 typedef struct __GLXFBConfigRec* GLXFBConfig;
 typedef XID GLXFBConfigID;
 typedef XID GLXContextID;
@@ -69,16 +74,25 @@ typedef uint32_t GLenum;
 #define GLX_RENDERER_PREFERRED_PROFILE_MESA                    0x8189
 #define GLX_RENDERER_OPENGL_CORE_PROFILE_VERSION_MESA          0x818A
 #define GLX_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_MESA 0x818B
 #define GLX_RENDERER_OPENGL_ES_PROFILE_VERSION_MESA            0x818C
 #define GLX_RENDERER_OPENGL_ES2_PROFILE_VERSION_MESA           0x818D
 #define GLX_RENDERER_ID_MESA                                   0x818E
 // clang-format on
 
+// stuff from egl.h
+#define EGL_BLUE_SIZE 0x3022
+#define EGL_GREEN_SIZE 0x3023
+#define EGL_RED_SIZE 0x3024
+#define EGL_NONE 0x3038
+#define EGL_VENDOR 0x3053
+#define EGL_CONTEXT_CLIENT_VERSION 0x3098
+#define EGL_NO_CONTEXT nullptr
+
 namespace mozilla {
 namespace widget {
 // the read end of the pipe, which will be used by GfxInfo
 extern int glxtest_pipe;
 // the PID of the glxtest process, to pass to waitpid()
 extern pid_t glxtest_pid;
 }  // namespace widget
 }  // namespace mozilla
@@ -113,17 +127,20 @@ static int x_error_handler(Display*, XEr
 }
 
 // glxtest is declared inside extern "C" so that the name is not mangled.
 // The name is used in build/valgrind/x86_64-pc-linux-gnu.sup to suppress
 // memory leak errors because we run it inside a short lived fork and we don't
 // care about leaking memory
 extern "C" {
 
-static int get_egl_status(char* buf, int bufsize) {
+typedef void* EGLNativeDisplayType;
+
+static int get_egl_status(char* buf, int bufsize,
+                          EGLNativeDisplayType native_dpy, bool gles_test) {
   void* libegl = dlopen("libEGL.so.1", RTLD_LAZY);
   if (!libegl) {
     libegl = dlopen("libEGL.so", RTLD_LAZY);
   }
   if (!libegl) {
     return 0;
   }
 
@@ -159,53 +176,155 @@ static int get_egl_status(char* buf, int
           eglGetProcAddress("eglGetDisplayDriverName"));
 
   if (!eglGetDisplay || !eglInitialize || !eglTerminate ||
       !eglGetDisplayDriverName) {
     dlclose(libegl);
     return 0;
   }
 
-  EGLDisplay dpy = eglGetDisplay(nullptr);
+  EGLDisplay dpy = eglGetDisplay(native_dpy);
   if (!dpy) {
     dlclose(libegl);
     return 0;
   }
 
   EGLint major, minor;
   if (!eglInitialize(dpy, &major, &minor)) {
     dlclose(libegl);
     return 0;
   }
 
   int length = 0;
+
+  if (gles_test) {
+    typedef void* EGLConfig;
+    typedef void* EGLContext;
+    typedef void* EGLSurface;
+
+    typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC)(
+        EGLDisplay dpy, EGLint const* attrib_list, EGLConfig* configs,
+        EGLint config_size, EGLint* num_config);
+    PFNEGLCHOOSECONFIGPROC eglChooseConfig =
+        cast<PFNEGLCHOOSECONFIGPROC>(eglGetProcAddress("eglChooseConfig"));
+
+    typedef EGLContext (*PFNEGLCREATECONTEXTPROC)(
+        EGLDisplay dpy, EGLConfig config, EGLContext share_context,
+        EGLint const* attrib_list);
+    PFNEGLCREATECONTEXTPROC eglCreateContext =
+        cast<PFNEGLCREATECONTEXTPROC>(eglGetProcAddress("eglCreateContext"));
+
+    typedef EGLSurface (*PFNEGLCREATEPBUFFERSURFACEPROC)(
+        EGLDisplay dpy, EGLConfig config, EGLint const* attrib_list);
+    PFNEGLCREATEPBUFFERSURFACEPROC eglCreatePbufferSurface =
+        cast<PFNEGLCREATEPBUFFERSURFACEPROC>(
+            eglGetProcAddress("eglCreatePbufferSurface"));
+
+    typedef EGLBoolean (*PFNEGLMAKECURRENTPROC)(
+        EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext context);
+    PFNEGLMAKECURRENTPROC eglMakeCurrent =
+        cast<PFNEGLMAKECURRENTPROC>(eglGetProcAddress("eglMakeCurrent"));
+
+    void* libgles = dlopen("libGLESv2.so.2", RTLD_LAZY);
+    if (!libgles) {
+      libgles = dlopen("libGLESv2.so", RTLD_LAZY);
+    }
+    if (!libgles) {
+      fatal_error("Unable to load libGLESv2");
+    }
+
+    typedef GLubyte* (*PFNGLGETSTRING)(GLenum);
+    PFNGLGETSTRING glGetString =
+        cast<PFNGLGETSTRING>(eglGetProcAddress("glGetString"));
+
+    if (!glGetString) {
+      // Implementations disagree about whether eglGetProcAddress or dlsym
+      // should be used for getting functions from the actual API, see
+      // https://github.com/anholt/libepoxy/commit/14f24485e33816139398d1bd170d617703473738
+      glGetString = cast<PFNGLGETSTRING>(dlsym(libgles, "glGetString"));
+    }
+
+    if (!glGetString) {
+      fatal_error("GLESv2 glGetString not found");
+    }
+
+    EGLint config_attrs[] = {EGL_RED_SIZE,  8, EGL_GREEN_SIZE, 8,
+                             EGL_BLUE_SIZE, 8, EGL_NONE};
+    EGLConfig config;
+    EGLint num_config;
+    eglChooseConfig(dpy, config_attrs, &config, 1, &num_config);
+    EGLint ctx_attrs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
+    EGLContext ectx = eglCreateContext(dpy, config, EGL_NO_CONTEXT, ctx_attrs);
+    EGLSurface pbuf = eglCreatePbufferSurface(dpy, config, nullptr);
+    eglMakeCurrent(dpy, pbuf, pbuf, ectx);
+
+    const GLubyte* versionString = glGetString(GL_VERSION);
+    const GLubyte* vendorString = glGetString(GL_VENDOR);
+    const GLubyte* rendererString = glGetString(GL_RENDERER);
+
+    if (!versionString || !vendorString || !rendererString)
+      fatal_error("glGetString returned null");
+
+    length = snprintf(buf, bufsize,
+                      "VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\nTRUE\n",
+                      vendorString, rendererString, versionString);
+    if (length >= bufsize) {
+      fatal_error("GL strings length too large for buffer size");
+    }
+  }
+
   const char* driDriver = eglGetDisplayDriverName(dpy);
   if (driDriver) {
-    length = snprintf(buf, bufsize, "DRI_DRIVER\n%s\n", driDriver);
+    length +=
+        snprintf(buf + length, bufsize - length, "DRI_DRIVER\n%s\n", driDriver);
   }
 
   eglTerminate(dpy);
   dlclose(libegl);
   return length;
 }
 
-void glxtest() {
+static void close_logging() {
   // we want to redirect to /dev/null stdout, stderr, and while we're at it,
   // any PR logging file descriptors. To that effect, we redirect all positive
   // file descriptors up to what open() returns here. In particular, 1 is stdout
   // and 2 is stderr.
   int fd = open("/dev/null", O_WRONLY);
   for (int i = 1; i < fd; i++) dup2(fd, i);
   close(fd);
 
   if (getenv("MOZ_AVOID_OPENGL_ALTOGETHER"))
     fatal_error(
         "The MOZ_AVOID_OPENGL_ALTOGETHER environment variable is defined");
+}
 
-    ///// Open libGL and load needed symbols /////
+#ifdef MOZ_WAYLAND
+bool wayland_egltest() {
+  // NOTE: returns false to fall back to X11 when the Wayland socket doesn't
+  // exist but fails with fatal_error if something actually went wrong
+  struct wl_display* dpy = wl_display_connect(nullptr);
+  if (!dpy) return false;
+
+  enum { bufsize = 2048 };
+  char buf[bufsize];
+
+  int length = get_egl_status(buf, bufsize, (EGLNativeDisplayType)dpy, true);
+  if (length >= bufsize) {
+    fatal_error("GL strings length too large for buffer size");
+  }
+
+  ///// Finally write data to the pipe
+  mozilla::Unused << write(write_end_of_the_pipe, buf, length);
+
+  return true;
+}
+#endif
+
+void glxtest() {
+  ///// Open libGL and load needed symbols /////
 #if defined(__OpenBSD__) || defined(__NetBSD__)
 #  define LIBGL_FILENAME "libGL.so"
 #else
 #  define LIBGL_FILENAME "libGL.so.1"
 #endif
   void* libgl = dlopen(LIBGL_FILENAME, RTLD_LAZY);
   if (!libgl) fatal_error("Unable to load " LIBGL_FILENAME);
 
@@ -396,17 +515,17 @@ void glxtest() {
   XSync(dpy, False);
 #endif
 
   dlclose(libgl);
 
   // If we failed to get the driver name from X, try via EGL_MESA_query_driver.
   // We are probably using Wayland.
   if (!gotDriDriver) {
-    length += get_egl_status(buf + length, bufsize - length);
+    length += get_egl_status(buf + length, bufsize - length, nullptr, false);
     if (length >= bufsize) {
       fatal_error("GL strings length too large for buffer size");
     }
   }
 
   ///// Finally write data to the pipe
   mozilla::Unused << write(write_end_of_the_pipe, buf, length);
 }
@@ -426,17 +545,22 @@ bool fire_glxtest_process() {
     close(pfd[1]);
     return false;
   }
   // The child exits early to avoid running the full shutdown sequence and avoid
   // conflicting with threads we have already spawned (like the profiler).
   if (pid == 0) {
     close(pfd[0]);
     write_end_of_the_pipe = pfd[1];
-    glxtest();
+    close_logging();
+    // TODO: --display command line argument is not properly handled
+#ifdef MOZ_WAYLAND
+    if (IsWaylandDisabled() || !wayland_egltest())
+#endif
+      glxtest();
     close(pfd[1]);
     _exit(0);
   }
 
   close(pfd[1]);
   mozilla::widget::glxtest_pipe = pfd[0];
   mozilla::widget::glxtest_pid = pid;
   return false;
--- a/widget/gtk/mozwayland/mozwayland.h
+++ b/widget/gtk/mozwayland/mozwayland.h
@@ -15,16 +15,17 @@
 #include "mozilla/Types.h"
 #include <gtk/gtk.h>
 #include <gdk/gdkwayland.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+MOZ_EXPORT struct wl_display* wl_display_connect(const char* name);
 MOZ_EXPORT int wl_display_roundtrip_queue(struct wl_display* display,
                                           struct wl_event_queue* queue);
 MOZ_EXPORT uint32_t wl_proxy_get_version(struct wl_proxy* proxy);
 MOZ_EXPORT struct wl_proxy* wl_proxy_marshal_constructor(
     struct wl_proxy* proxy, uint32_t opcode,
     const struct wl_interface* interface, ...);
 
 MOZ_EXPORT void* wl_proxy_create_wrapper(void* proxy);