1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 //////////////////////////////////////////////////////////////////////////////
10 // Explanation: See bug 639842. Safely getting GL driver info on X11 is hard,
11 // because the only way to do that is to create a GL context and call
12 // glGetString(), but with bad drivers, just creating a GL context may crash.
14 // This file implements the idea to do that in a separate process.
16 // The only non-static function here is fire_glxtest_process(). It creates a
17 // pipe, publishes its 'read' end as the mozilla::widget::glxtest_pipe global
18 // variable, forks, and runs that GLX probe in the child process, which runs the
19 // childgltest() static function. This creates a X connection, a GLX context,
20 // calls glGetString, and writes that to the 'write' end of the pipe.
28 #include "mozilla/Unused.h"
29 #include "nsAppRunner.h" // for IsWaylandEnabled on IsX11EGLEnabled
37 # include "X11/Xlib.h"
38 # include "X11/Xutil.h"
39 # include <X11/extensions/Xrandr.h>
43 # include "mozilla/widget/mozwayland.h"
44 # include "mozilla/widget/xdg-output-unstable-v1-client-protocol.h"
49 typedef struct __GLXcontextRec
* GLXContext
;
50 typedef XID GLXPixmap
;
51 typedef XID GLXDrawable
;
52 /* GLX 1.3 and later */
53 typedef struct __GLXFBConfigRec
* GLXFBConfig
;
54 typedef XID GLXFBConfigID
;
55 typedef XID GLXContextID
;
56 typedef XID GLXWindow
;
57 typedef XID GLXPbuffer
;
59 # define GLX_RED_SIZE 8
60 # define GLX_GREEN_SIZE 9
61 # define GLX_BLUE_SIZE 10
62 # define GLX_DOUBLEBUFFER 5
66 typedef uint8_t GLubyte
;
67 typedef uint32_t GLenum
;
68 #define GL_VENDOR 0x1F00
69 #define GL_RENDERER 0x1F01
70 #define GL_VERSION 0x1F02
72 // GLX_MESA_query_renderer
74 #define GLX_RENDERER_VENDOR_ID_MESA 0x8183
75 #define GLX_RENDERER_DEVICE_ID_MESA 0x8184
76 #define GLX_RENDERER_VERSION_MESA 0x8185
77 #define GLX_RENDERER_ACCELERATED_MESA 0x8186
78 #define GLX_RENDERER_VIDEO_MEMORY_MESA 0x8187
79 #define GLX_RENDERER_UNIFIED_MEMORY_ARCHITECTURE_MESA 0x8188
80 #define GLX_RENDERER_PREFERRED_PROFILE_MESA 0x8189
81 #define GLX_RENDERER_OPENGL_CORE_PROFILE_VERSION_MESA 0x818A
82 #define GLX_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_MESA 0x818B
83 #define GLX_RENDERER_OPENGL_ES_PROFILE_VERSION_MESA 0x818C
84 #define GLX_RENDERER_OPENGL_ES2_PROFILE_VERSION_MESA 0x818D
85 #define GLX_RENDERER_ID_MESA 0x818E
89 typedef intptr_t EGLAttrib
;
90 typedef int EGLBoolean
;
91 typedef void* EGLConfig
;
92 typedef void* EGLContext
;
93 typedef void* EGLDeviceEXT
;
94 typedef void* EGLDisplay
;
96 typedef void* EGLNativeDisplayType
;
97 typedef void* EGLSurface
;
98 typedef void* (*PFNEGLGETPROCADDRESS
)(const char*);
100 #define EGL_NO_CONTEXT nullptr
101 #define EGL_NO_SURFACE nullptr
104 #define EGL_BLUE_SIZE 0x3022
105 #define EGL_GREEN_SIZE 0x3023
106 #define EGL_RED_SIZE 0x3024
107 #define EGL_NONE 0x3038
108 #define EGL_VENDOR 0x3053
109 #define EGL_EXTENSIONS 0x3055
110 #define EGL_CONTEXT_CLIENT_VERSION 0x3098
111 #define EGL_OPENGL_API 0x30A2
112 #define EGL_DEVICE_EXT 0x322C
113 #define EGL_DRM_DEVICE_FILE_EXT 0x3233
114 #define EGL_DRM_RENDER_NODE_FILE_EXT 0x3377
116 // stuff from xf86drm.h
117 #define DRM_NODE_RENDER 2
118 #define DRM_NODE_MAX 3
120 typedef struct _drmPciDeviceInfo
{
123 uint16_t subvendor_id
;
124 uint16_t subdevice_id
;
126 } drmPciDeviceInfo
, *drmPciDeviceInfoPtr
;
128 typedef struct _drmDevice
{
139 drmPciDeviceInfoPtr pci
;
144 } drmDevice
, *drmDevicePtr
;
146 // Open libGL and load needed symbols
147 #if defined(__OpenBSD__) || defined(__NetBSD__)
148 # define LIBGL_FILENAME "libGL.so"
149 # define LIBGLES_FILENAME "libGLESv2.so"
150 # define LIBEGL_FILENAME "libEGL.so"
151 # define LIBDRM_FILENAME "libdrm.so"
153 # define LIBGL_FILENAME "libGL.so.1"
154 # define LIBGLES_FILENAME "libGLESv2.so.2"
155 # define LIBEGL_FILENAME "libEGL.so.1"
156 # define LIBDRM_FILENAME "libdrm.so.2"
159 #define EXIT_FAILURE_BUFFER_TOO_SMALL 2
163 // the read end of the pipe, which will be used by GfxInfo
164 extern int glxtest_pipe
;
165 // the PID of the glxtest process, to pass to waitpid()
166 extern pid_t glxtest_pid
;
167 } // namespace widget
168 } // namespace mozilla
170 // the write end of the pipe, which we're going to write to
171 static int write_end_of_the_pipe
= -1;
173 // our buffer, size and used length
174 static char* glxtest_buf
= nullptr;
175 static int glxtest_bufsize
= 0;
176 static int glxtest_length
= 0;
178 // C++ standard collides with C standard in that it doesn't allow casting void*
179 // to function pointer types. So the work-around is to convert first to size_t.
180 // http://www.trilithium.com/johan/2004/12/problem-with-dlsym/
181 template <typename func_ptr_type
>
182 static func_ptr_type
cast(void* ptr
) {
183 return reinterpret_cast<func_ptr_type
>(reinterpret_cast<size_t>(ptr
));
186 static void record_value(const char* format
, ...) {
187 // Don't add more if the buffer is full.
188 if (glxtest_bufsize
<= glxtest_length
) {
192 // Append the new values to the buffer, not to exceed the remaining space.
193 int remaining
= glxtest_bufsize
- glxtest_length
;
195 va_start(args
, format
);
197 vsnprintf(glxtest_buf
+ glxtest_length
, remaining
, format
, args
);
200 // snprintf returns how many char it could have added, not how many it added.
201 // It is important to get this right since it will control how many chars we
202 // will attempt to write to the pipe fd.
203 if (max_added
> remaining
) {
204 glxtest_length
+= remaining
;
206 glxtest_length
+= max_added
;
210 static void record_error(const char* str
) { record_value("ERROR\n%s\n", str
); }
212 static void record_warning(const char* str
) {
213 record_value("WARNING\n%s\n", str
);
216 static void record_flush() {
217 mozilla::Unused
<< write(write_end_of_the_pipe
, glxtest_buf
, glxtest_length
);
221 static int x_error_handler(Display
*, XErrorEvent
* ev
) {
223 "ERROR\nX error, error_code=%d, "
224 "request_code=%d, minor_code=%d\n",
225 ev
->error_code
, ev
->request_code
, ev
->minor_code
);
232 // childgltest is declared inside extern "C" so that the name is not mangled.
233 // The name is used in build/valgrind/x86_64-pc-linux-gnu.sup to suppress
234 // memory leak errors because we run it inside a short lived fork and we don't
235 // care about leaking memory
238 static void close_logging() {
239 // we want to redirect to /dev/null stdout, stderr, and while we're at it,
240 // any PR logging file descriptors. To that effect, we redirect all positive
241 // file descriptors up to what open() returns here. In particular, 1 is stdout
243 int fd
= open("/dev/null", O_WRONLY
);
244 for (int i
= 1; i
< fd
; i
++) {
249 if (getenv("MOZ_AVOID_OPENGL_ALTOGETHER")) {
250 const char* msg
= "ERROR\nMOZ_AVOID_OPENGL_ALTOGETHER envvar set";
251 mozilla::Unused
<< write(write_end_of_the_pipe
, msg
, strlen(msg
));
256 #define PCI_FILL_IDENT 0x0001
257 #define PCI_FILL_CLASS 0x0020
258 #define PCI_BASE_CLASS_DISPLAY 0x03
260 static int get_pci_status() {
261 void* libpci
= dlopen("libpci.so.3", RTLD_LAZY
);
263 libpci
= dlopen("libpci.so", RTLD_LAZY
);
266 record_warning("libpci missing");
270 typedef struct pci_dev
{
271 struct pci_dev
* next
;
273 uint8_t bus
, dev
, func
;
274 unsigned int known_fields
;
275 uint16_t vendor_id
, device_id
;
276 uint16_t device_class
;
279 typedef struct pci_access
{
286 unsigned int id_lookup_mode
;
294 typedef pci_access
* (*PCIALLOC
)(void);
295 PCIALLOC pci_alloc
= cast
<PCIALLOC
>(dlsym(libpci
, "pci_alloc"));
297 typedef void (*PCIINIT
)(pci_access
*);
298 PCIINIT pci_init
= cast
<PCIINIT
>(dlsym(libpci
, "pci_init"));
300 typedef void (*PCICLEANUP
)(pci_access
*);
301 PCICLEANUP pci_cleanup
= cast
<PCICLEANUP
>(dlsym(libpci
, "pci_cleanup"));
303 typedef void (*PCISCANBUS
)(pci_access
*);
304 PCISCANBUS pci_scan_bus
= cast
<PCISCANBUS
>(dlsym(libpci
, "pci_scan_bus"));
306 typedef void (*PCIFILLINFO
)(pci_dev
*, int);
307 PCIFILLINFO pci_fill_info
= cast
<PCIFILLINFO
>(dlsym(libpci
, "pci_fill_info"));
309 if (!pci_alloc
|| !pci_cleanup
|| !pci_scan_bus
|| !pci_fill_info
) {
311 record_warning("libpci missing methods");
315 pci_access
* pacc
= pci_alloc();
318 record_warning("libpci alloc failed");
326 for (pci_dev
* dev
= pacc
->devices
; dev
; dev
= dev
->next
) {
327 pci_fill_info(dev
, PCI_FILL_IDENT
| PCI_FILL_CLASS
);
328 if (dev
->device_class
>> 8 == PCI_BASE_CLASS_DISPLAY
&& dev
->vendor_id
&&
331 record_value("PCI_VENDOR_ID\n0x%04x\nPCI_DEVICE_ID\n0x%04x\n",
332 dev
->vendor_id
, dev
->device_id
);
342 static bool device_has_name(const drmDevice
* device
, const char* name
) {
343 for (size_t i
= 0; i
< DRM_NODE_MAX
; i
++) {
344 if (!(device
->available_nodes
& (1 << i
))) {
347 if (strcmp(device
->nodes
[i
], name
) == 0) {
354 static bool get_render_name(const char* name
) {
355 void* libdrm
= dlopen(LIBDRM_FILENAME
, RTLD_LAZY
);
357 record_warning("Failed to open libdrm");
361 typedef int (*DRMGETDEVICES2
)(uint32_t, drmDevicePtr
*, int);
362 DRMGETDEVICES2 drmGetDevices2
=
363 cast
<DRMGETDEVICES2
>(dlsym(libdrm
, "drmGetDevices2"));
365 typedef void (*DRMFREEDEVICE
)(drmDevicePtr
*);
366 DRMFREEDEVICE drmFreeDevice
=
367 cast
<DRMFREEDEVICE
>(dlsym(libdrm
, "drmFreeDevice"));
369 if (!drmGetDevices2
|| !drmFreeDevice
) {
371 "libdrm missing methods for drmGetDevices2 or drmFreeDevice");
377 int devices_len
= drmGetDevices2(flags
, nullptr, 0);
378 if (devices_len
< 0) {
379 record_warning("drmGetDevices2 failed");
383 drmDevice
** devices
= (drmDevice
**)calloc(devices_len
, sizeof(drmDevice
*));
385 record_warning("Allocation error");
389 devices_len
= drmGetDevices2(flags
, devices
, devices_len
);
390 if (devices_len
< 0) {
392 record_warning("drmGetDevices2 failed");
397 const drmDevice
* match
= nullptr;
398 for (int i
= 0; i
< devices_len
; i
++) {
399 if (device_has_name(devices
[i
], name
)) {
407 record_warning("Cannot find DRM device");
408 } else if (!(match
->available_nodes
& (1 << DRM_NODE_RENDER
))) {
409 record_warning("DRM device has no render node");
411 record_value("DRM_RENDERDEVICE\n%s\n", match
->nodes
[DRM_NODE_RENDER
]);
413 "MESA_VENDOR_ID\n0x%04x\n"
414 "MESA_DEVICE_ID\n0x%04x\n",
415 match
->deviceinfo
.pci
->vendor_id
, match
->deviceinfo
.pci
->device_id
);
419 for (int i
= 0; i
< devices_len
; i
++) {
420 drmFreeDevice(&devices
[i
]);
429 static bool get_gles_status(EGLDisplay dpy
,
430 PFNEGLGETPROCADDRESS eglGetProcAddress
) {
431 typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC
)(
432 EGLDisplay dpy
, EGLint
const* attrib_list
, EGLConfig
* configs
,
433 EGLint config_size
, EGLint
* num_config
);
434 PFNEGLCHOOSECONFIGPROC eglChooseConfig
=
435 cast
<PFNEGLCHOOSECONFIGPROC
>(eglGetProcAddress("eglChooseConfig"));
437 typedef EGLBoolean (*PFNEGLBINDAPIPROC
)(EGLint api
);
438 PFNEGLBINDAPIPROC eglBindAPI
=
439 cast
<PFNEGLBINDAPIPROC
>(eglGetProcAddress("eglBindAPI"));
441 typedef EGLContext (*PFNEGLCREATECONTEXTPROC
)(
442 EGLDisplay dpy
, EGLConfig config
, EGLContext share_context
,
443 EGLint
const* attrib_list
);
444 PFNEGLCREATECONTEXTPROC eglCreateContext
=
445 cast
<PFNEGLCREATECONTEXTPROC
>(eglGetProcAddress("eglCreateContext"));
447 typedef EGLBoolean (*PFNEGLMAKECURRENTPROC
)(
448 EGLDisplay dpy
, EGLSurface draw
, EGLSurface read
, EGLContext context
);
449 PFNEGLMAKECURRENTPROC eglMakeCurrent
=
450 cast
<PFNEGLMAKECURRENTPROC
>(eglGetProcAddress("eglMakeCurrent"));
452 typedef const char* (*PFNEGLQUERYDEVICESTRINGEXTPROC
)(EGLDeviceEXT device
,
454 PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT
=
455 cast
<PFNEGLQUERYDEVICESTRINGEXTPROC
>(
456 eglGetProcAddress("eglQueryDeviceStringEXT"));
458 typedef EGLBoolean (*PFNEGLQUERYDISPLAYATTRIBEXTPROC
)(
459 EGLDisplay dpy
, EGLint name
, EGLAttrib
* value
);
460 PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT
=
461 cast
<PFNEGLQUERYDISPLAYATTRIBEXTPROC
>(
462 eglGetProcAddress("eglQueryDisplayAttribEXT"));
464 if (!eglChooseConfig
|| !eglCreateContext
|| !eglMakeCurrent
||
465 !eglQueryDeviceStringEXT
) {
466 record_warning("libEGL missing methods for GL test");
470 typedef GLubyte
* (*PFNGLGETSTRING
)(GLenum
);
471 PFNGLGETSTRING glGetString
=
472 cast
<PFNGLGETSTRING
>(eglGetProcAddress("glGetString"));
474 EGLint config_attrs
[] = {EGL_RED_SIZE
, 8, EGL_GREEN_SIZE
, 8,
475 EGL_BLUE_SIZE
, 8, EGL_NONE
};
479 if (eglChooseConfig(dpy
, config_attrs
, &config
, 1, &num_config
) ==
481 record_warning("eglChooseConfig returned an error");
485 if (eglBindAPI(EGL_OPENGL_API
) == EGL_FALSE
) {
486 record_warning("eglBindAPI returned an error");
490 EGLint ctx_attrs
[] = {EGL_NONE
};
491 EGLContext ectx
= eglCreateContext(dpy
, config
, EGL_NO_CONTEXT
, ctx_attrs
);
493 record_warning("eglCreateContext returned an error");
497 if (eglMakeCurrent(dpy
, EGL_NO_SURFACE
, EGL_NO_SURFACE
, ectx
) == EGL_FALSE
) {
498 record_warning("eglMakeCurrent returned an error");
502 // Implementations disagree about whether eglGetProcAddress or dlsym
503 // should be used for getting functions from the actual API, see
504 // https://github.com/anholt/libepoxy/commit/14f24485e33816139398d1bd170d617703473738
505 void* libgl
= nullptr;
507 libgl
= dlopen(LIBGL_FILENAME
, RTLD_LAZY
);
509 libgl
= dlopen(LIBGLES_FILENAME
, RTLD_LAZY
);
511 record_warning(LIBGL_FILENAME
" and " LIBGLES_FILENAME
" missing");
516 glGetString
= cast
<PFNGLGETSTRING
>(dlsym(libgl
, "glGetString"));
519 record_warning("libEGL, libGL and libGLESv2 are missing glGetString");
524 const GLubyte
* versionString
= glGetString(GL_VERSION
);
525 const GLubyte
* vendorString
= glGetString(GL_VENDOR
);
526 const GLubyte
* rendererString
= glGetString(GL_RENDERER
);
528 if (versionString
&& vendorString
&& rendererString
) {
529 record_value("VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\nTRUE\n",
530 vendorString
, rendererString
, versionString
);
532 record_warning("EGL glGetString returned null");
540 if (eglQueryDisplayAttribEXT(dpy
, EGL_DEVICE_EXT
, (EGLAttrib
*)&device
) ==
542 const char* deviceExtensions
=
543 eglQueryDeviceStringEXT(device
, EGL_EXTENSIONS
);
544 if (deviceExtensions
&&
545 strstr(deviceExtensions
, "EGL_MESA_device_software")) {
546 record_value("MESA_ACCELERATED\nFALSE\n");
549 const char* deviceString
=
550 eglQueryDeviceStringEXT(device
, EGL_DRM_DEVICE_FILE_EXT
);
551 if (!deviceString
|| !get_render_name(deviceString
)) {
552 const char* renderNodeString
=
553 eglQueryDeviceStringEXT(device
, EGL_DRM_RENDER_NODE_FILE_EXT
);
554 if (renderNodeString
) {
555 record_value("DRM_RENDERDEVICE\n%s\n", renderNodeString
);
568 static bool get_egl_status(EGLNativeDisplayType native_dpy
, bool gles_test
,
569 bool require_driver
) {
570 void* libegl
= dlopen(LIBEGL_FILENAME
, RTLD_LAZY
);
572 record_warning("libEGL missing");
576 PFNEGLGETPROCADDRESS eglGetProcAddress
=
577 cast
<PFNEGLGETPROCADDRESS
>(dlsym(libegl
, "eglGetProcAddress"));
579 if (!eglGetProcAddress
) {
581 record_warning("no eglGetProcAddress");
585 typedef EGLDisplay (*PFNEGLGETDISPLAYPROC
)(void* native_display
);
586 PFNEGLGETDISPLAYPROC eglGetDisplay
=
587 cast
<PFNEGLGETDISPLAYPROC
>(eglGetProcAddress("eglGetDisplay"));
589 typedef EGLBoolean (*PFNEGLINITIALIZEPROC
)(EGLDisplay dpy
, EGLint
* major
,
591 PFNEGLINITIALIZEPROC eglInitialize
=
592 cast
<PFNEGLINITIALIZEPROC
>(eglGetProcAddress("eglInitialize"));
594 typedef EGLBoolean (*PFNEGLTERMINATEPROC
)(EGLDisplay dpy
);
595 PFNEGLTERMINATEPROC eglTerminate
=
596 cast
<PFNEGLTERMINATEPROC
>(eglGetProcAddress("eglTerminate"));
598 if (!eglGetDisplay
|| !eglInitialize
|| !eglTerminate
) {
600 record_warning("libEGL missing methods");
604 EGLDisplay dpy
= eglGetDisplay(native_dpy
);
607 record_warning("libEGL no display");
612 if (!eglInitialize(dpy
, &major
, &minor
)) {
614 record_warning("libEGL initialize failed");
618 typedef const char* (*PFNEGLGETDISPLAYDRIVERNAMEPROC
)(EGLDisplay dpy
);
619 PFNEGLGETDISPLAYDRIVERNAMEPROC eglGetDisplayDriverName
=
620 cast
<PFNEGLGETDISPLAYDRIVERNAMEPROC
>(
621 eglGetProcAddress("eglGetDisplayDriverName"));
622 if (eglGetDisplayDriverName
) {
623 // TODO(aosmond): If the driver name is empty, we probably aren't using Mesa
624 // and instead a proprietary GL, most likely NVIDIA's. The PCI device list
625 // in combination with the vendor name is very likely sufficient to identify
627 const char* driDriver
= eglGetDisplayDriverName(dpy
);
629 record_value("DRI_DRIVER\n%s\n", driDriver
);
631 } else if (require_driver
) {
632 record_warning("libEGL missing eglGetDisplayDriverName");
638 if (gles_test
&& !get_gles_status(dpy
, eglGetProcAddress
)) {
650 static void get_xrandr_info(Display
* dpy
) {
651 // When running on remote X11 the xrandr version may be stuck on an ancient
652 // version. There are still setups using remote X11 out there, so make sure we
654 int eventBase
, errorBase
, major
, minor
;
655 if (!XRRQueryExtension(dpy
, &eventBase
, &errorBase
) ||
656 !XRRQueryVersion(dpy
, &major
, &minor
) ||
657 !(major
> 1 || (major
== 1 && minor
>= 4))) {
661 Window root
= RootWindow(dpy
, DefaultScreen(dpy
));
662 XRRProviderResources
* pr
= XRRGetProviderResources(dpy
, root
);
663 XRRScreenResources
* res
= XRRGetScreenResourcesCurrent(dpy
, root
);
664 RROutput primary
= XRRGetOutputPrimary(dpy
, root
);
666 if (res
->noutput
!= 0) {
667 bool foundCRTC
= false;
669 for (int i
= 0; i
< res
->noutput
; i
++) {
670 XRROutputInfo
* outputInfo
= XRRGetOutputInfo(dpy
, res
, res
->outputs
[i
]);
671 if (!outputInfo
->crtc
) {
676 record_value("SCREEN_INFO\n");
680 XRRCrtcInfo
* crtcInfo
= XRRGetCrtcInfo(dpy
, res
, outputInfo
->crtc
);
681 record_value("%dx%d:%d;", crtcInfo
->width
, crtcInfo
->height
,
682 res
->outputs
[i
] == primary
? 1 : 0);
690 if (pr
->nproviders
!= 0) {
691 record_value("DDX_DRIVER\n");
692 for (int i
= 0; i
< pr
->nproviders
; i
++) {
693 XRRProviderInfo
* info
= XRRGetProviderInfo(dpy
, res
, pr
->providers
[i
]);
694 record_value("%s%s", info
->name
, i
== pr
->nproviders
- 1 ? ";\n" : ";");
699 static void get_glx_status(int* gotGlxInfo
, int* gotDriDriver
) {
700 void* libgl
= dlopen(LIBGL_FILENAME
, RTLD_LAZY
);
702 record_error(LIBGL_FILENAME
" missing");
706 typedef void* (*PFNGLXGETPROCADDRESS
)(const char*);
707 PFNGLXGETPROCADDRESS glXGetProcAddress
=
708 cast
<PFNGLXGETPROCADDRESS
>(dlsym(libgl
, "glXGetProcAddress"));
710 if (!glXGetProcAddress
) {
711 record_error("no glXGetProcAddress");
715 typedef GLXFBConfig
* (*PFNGLXQUERYEXTENSION
)(Display
*, int*, int*);
716 PFNGLXQUERYEXTENSION glXQueryExtension
=
717 cast
<PFNGLXQUERYEXTENSION
>(glXGetProcAddress("glXQueryExtension"));
719 typedef GLXFBConfig
* (*PFNGLXQUERYVERSION
)(Display
*, int*, int*);
720 PFNGLXQUERYVERSION glXQueryVersion
=
721 cast
<PFNGLXQUERYVERSION
>(dlsym(libgl
, "glXQueryVersion"));
723 typedef XVisualInfo
* (*PFNGLXCHOOSEVISUAL
)(Display
*, int, int*);
724 PFNGLXCHOOSEVISUAL glXChooseVisual
=
725 cast
<PFNGLXCHOOSEVISUAL
>(glXGetProcAddress("glXChooseVisual"));
727 typedef GLXContext (*PFNGLXCREATECONTEXT
)(Display
*, XVisualInfo
*, GLXContext
,
729 PFNGLXCREATECONTEXT glXCreateContext
=
730 cast
<PFNGLXCREATECONTEXT
>(glXGetProcAddress("glXCreateContext"));
732 typedef Bool (*PFNGLXMAKECURRENT
)(Display
*, GLXDrawable
, GLXContext
);
733 PFNGLXMAKECURRENT glXMakeCurrent
=
734 cast
<PFNGLXMAKECURRENT
>(glXGetProcAddress("glXMakeCurrent"));
736 typedef void (*PFNGLXDESTROYCONTEXT
)(Display
*, GLXContext
);
737 PFNGLXDESTROYCONTEXT glXDestroyContext
=
738 cast
<PFNGLXDESTROYCONTEXT
>(glXGetProcAddress("glXDestroyContext"));
740 typedef GLubyte
* (*PFNGLGETSTRING
)(GLenum
);
741 PFNGLGETSTRING glGetString
=
742 cast
<PFNGLGETSTRING
>(glXGetProcAddress("glGetString"));
744 if (!glXQueryExtension
|| !glXQueryVersion
|| !glXChooseVisual
||
745 !glXCreateContext
|| !glXMakeCurrent
|| !glXDestroyContext
||
747 record_error(LIBGL_FILENAME
" missing methods");
751 ///// Open a connection to the X server /////
752 Display
* dpy
= XOpenDisplay(nullptr);
754 record_error("Unable to open a connection to the X server");
758 ///// Check that the GLX extension is present /////
759 if (!glXQueryExtension(dpy
, nullptr, nullptr)) {
760 record_error("GLX extension missing");
764 XSetErrorHandler(x_error_handler
);
766 ///// Get a visual /////
767 int attribs
[] = {GLX_RGBA
, GLX_RED_SIZE
, 1, GLX_GREEN_SIZE
,
768 1, GLX_BLUE_SIZE
, 1, None
};
769 XVisualInfo
* vInfo
= glXChooseVisual(dpy
, DefaultScreen(dpy
), attribs
);
771 int attribs2
[] = {GLX_RGBA
, GLX_RED_SIZE
, 1, GLX_GREEN_SIZE
,
772 1, GLX_BLUE_SIZE
, 1, GLX_DOUBLEBUFFER
,
774 vInfo
= glXChooseVisual(dpy
, DefaultScreen(dpy
), attribs2
);
776 record_error("No visuals found");
781 // using a X11 Window instead of a GLXPixmap does not crash
782 // fglrx in indirect rendering. bug 680644
784 XSetWindowAttributes swa
;
785 swa
.colormap
= XCreateColormap(dpy
, RootWindow(dpy
, vInfo
->screen
),
786 vInfo
->visual
, AllocNone
);
788 swa
.border_pixel
= 0;
789 window
= XCreateWindow(dpy
, RootWindow(dpy
, vInfo
->screen
), 0, 0, 16, 16, 0,
790 vInfo
->depth
, InputOutput
, vInfo
->visual
,
791 CWBorderPixel
| CWColormap
, &swa
);
793 ///// Get a GL context and make it current //////
794 GLXContext context
= glXCreateContext(dpy
, vInfo
, nullptr, True
);
795 glXMakeCurrent(dpy
, window
, context
);
797 ///// Look for this symbol to determine texture_from_pixmap support /////
798 void* glXBindTexImageEXT
= glXGetProcAddress("glXBindTexImageEXT");
800 ///// Get GL vendor/renderer/versions strings /////
801 const GLubyte
* versionString
= glGetString(GL_VERSION
);
802 const GLubyte
* vendorString
= glGetString(GL_VENDOR
);
803 const GLubyte
* rendererString
= glGetString(GL_RENDERER
);
805 if (versionString
&& vendorString
&& rendererString
) {
806 record_value("VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\n%s\n",
807 vendorString
, rendererString
, versionString
,
808 glXBindTexImageEXT
? "TRUE" : "FALSE");
811 record_error("glGetString returned null");
814 // If GLX_MESA_query_renderer is available, populate additional data.
815 typedef Bool (*PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC
)(
816 int attribute
, unsigned int* value
);
817 PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC
818 glXQueryCurrentRendererIntegerMESAProc
=
819 cast
<PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC
>(
820 glXGetProcAddress("glXQueryCurrentRendererIntegerMESA"));
821 if (glXQueryCurrentRendererIntegerMESAProc
) {
822 unsigned int vendorId
, deviceId
, accelerated
, videoMemoryMB
;
823 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_VENDOR_ID_MESA
,
825 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_DEVICE_ID_MESA
,
827 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_ACCELERATED_MESA
,
829 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_VIDEO_MEMORY_MESA
,
832 // Truncate IDs to 4 digits- that's all PCI IDs are.
837 "MESA_VENDOR_ID\n0x%04x\n"
838 "MESA_DEVICE_ID\n0x%04x\n"
839 "MESA_ACCELERATED\n%s\n"
841 vendorId
, deviceId
, accelerated
? "TRUE" : "FALSE", videoMemoryMB
);
844 // From Mesa's GL/internal/dri_interface.h, to be used by DRI clients.
845 typedef const char* (*PFNGLXGETSCREENDRIVERPROC
)(Display
* dpy
, int scrNum
);
846 PFNGLXGETSCREENDRIVERPROC glXGetScreenDriverProc
=
847 cast
<PFNGLXGETSCREENDRIVERPROC
>(glXGetProcAddress("glXGetScreenDriver"));
848 if (glXGetScreenDriverProc
) {
849 const char* driDriver
= glXGetScreenDriverProc(dpy
, DefaultScreen(dpy
));
852 record_value("DRI_DRIVER\n%s\n", driDriver
);
856 // Get monitor and DDX driver information
857 get_xrandr_info(dpy
);
859 ///// Clean up. Indeed, the parent process might fail to kill us (e.g. if it
860 ///// doesn't need to check GL info) so we might be staying alive for longer
861 ///// than expected, so it's important to consume as little memory as
862 ///// possible. Also we want to check that we're able to do that too without
863 ///// generating X errors.
864 glXMakeCurrent(dpy
, None
,
865 nullptr); // must release the GL context before destroying it
866 glXDestroyContext(dpy
, context
);
867 XDestroyWindow(dpy
, window
);
868 XFreeColormap(dpy
, swa
.colormap
);
870 # ifdef NS_FREE_PERMANENT_DATA // conditionally defined in nscore.h, don't
871 // forget to #include it above
874 // This XSync call wanted to be instead:
875 // XCloseDisplay(dpy);
876 // but this can cause 1-minute stalls on certain setups using Nouveau, see bug
884 static bool x11_egltest(int pci_count
) {
885 Display
* dpy
= XOpenDisplay(nullptr);
889 XSetErrorHandler(x_error_handler
);
891 // Bug 1667621: 30bit "Deep Color" is broken on EGL on Mesa (as of 2021/10).
892 // Disable all non-standard depths for the initial EGL roleout.
893 int screenCount
= ScreenCount(dpy
);
894 for (int idx
= 0; idx
< screenCount
; idx
++) {
895 if (DefaultDepth(dpy
, idx
) != 24) {
900 // On at least amdgpu open source driver, eglInitialize fails unless
901 // a valid XDisplay pointer is passed as the native display.
902 if (!get_egl_status(dpy
, true, pci_count
!= 1)) {
906 // Get monitor and DDX driver information
907 get_xrandr_info(dpy
);
909 // Bug 1715245: Closing the display connection here crashes on NV prop.
910 // drivers. Just leave it open, the process will exit shortly after anyway.
911 // XCloseDisplay(dpy);
913 record_value("TEST_TYPE\nEGL\n");
917 static void glxtest() {
919 int gotDriDriver
= 0;
921 get_glx_status(&gotGlxInfo
, &gotDriDriver
);
923 get_egl_status(nullptr, true, false);
924 } else if (!gotDriDriver
) {
925 // If we failed to get the driver name from X, try via
926 // EGL_MESA_query_driver. We are probably using Wayland.
927 get_egl_status(nullptr, false, true);
930 record_value("TEST_TYPE\nGLX\n");
935 typedef void (*print_info_t
)(void* info
);
936 typedef void (*destroy_info_t
)(void* info
);
946 destroy_info_t destroy
;
950 struct global_info global
;
951 struct wl_list global_link
;
953 struct wl_output
* output
;
960 struct xdg_output_v1_info
{
963 struct zxdg_output_v1
* xdg_output
;
964 struct output_info
* output
;
967 int32_t width
, height
;
971 struct xdg_output_manager_v1_info
{
972 struct global_info global
;
973 struct zxdg_output_manager_v1
* manager
;
974 struct weston_info
* info
;
976 struct wl_list outputs
;
980 struct wl_display
* display
;
981 struct wl_registry
* registry
;
983 struct wl_list infos
;
984 bool roundtrip_needed
;
986 struct wl_list outputs
;
987 struct xdg_output_manager_v1_info
* xdg_output_manager_v1_info
;
990 static void init_global_info(struct weston_info
* info
,
991 struct global_info
* global
, uint32_t id
,
992 const char* interface
, uint32_t version
) {
994 global
->version
= version
;
995 global
->interface
= strdup(interface
);
997 wl_list_insert(info
->infos
.prev
, &global
->link
);
1000 static void print_output_info(void* data
) {}
1002 static void destroy_xdg_output_v1_info(struct xdg_output_v1_info
* info
) {
1003 wl_list_remove(&info
->link
);
1004 zxdg_output_v1_destroy(info
->xdg_output
);
1008 static int cmpOutputIds(const void* a
, const void* b
) {
1009 return (((struct xdg_output_v1_info
*)a
)->output
->global
.id
-
1010 ((struct xdg_output_v1_info
*)b
)->output
->global
.id
);
1013 static void print_xdg_output_manager_v1_info(void* data
) {
1014 struct xdg_output_manager_v1_info
* info
=
1015 (struct xdg_output_manager_v1_info
*)data
;
1016 struct xdg_output_v1_info
* output
;
1018 int screen_count
= wl_list_length(&info
->outputs
);
1019 if (screen_count
> 0) {
1020 struct xdg_output_v1_info
* infos
= (struct xdg_output_v1_info
*)calloc(
1021 1, screen_count
* sizeof(xdg_output_v1_info
));
1024 wl_list_for_each(output
, &info
->outputs
, link
) {
1025 infos
[pos
] = *output
;
1029 if (screen_count
> 1) {
1030 qsort(infos
, screen_count
, sizeof(struct xdg_output_v1_info
),
1034 record_value("SCREEN_INFO\n");
1035 for (int i
= 0; i
< screen_count
; i
++) {
1036 record_value("%dx%d:0;", infos
[i
].logical
.width
, infos
[i
].logical
.height
);
1044 static void destroy_xdg_output_manager_v1_info(void* data
) {
1045 struct xdg_output_manager_v1_info
* info
=
1046 (struct xdg_output_manager_v1_info
*)data
;
1047 struct xdg_output_v1_info
*output
, *tmp
;
1049 zxdg_output_manager_v1_destroy(info
->manager
);
1051 wl_list_for_each_safe(output
, tmp
, &info
->outputs
, link
) {
1052 destroy_xdg_output_v1_info(output
);
1056 static void handle_xdg_output_v1_logical_position(void* data
,
1057 struct zxdg_output_v1
* output
,
1058 int32_t x
, int32_t y
) {}
1060 static void handle_xdg_output_v1_logical_size(void* data
,
1061 struct zxdg_output_v1
* output
,
1062 int32_t width
, int32_t height
) {
1063 struct xdg_output_v1_info
* xdg_output
= (struct xdg_output_v1_info
*)data
;
1064 xdg_output
->logical
.width
= width
;
1065 xdg_output
->logical
.height
= height
;
1068 static void handle_xdg_output_v1_done(void* data
,
1069 struct zxdg_output_v1
* output
) {}
1071 static void handle_xdg_output_v1_name(void* data
, struct zxdg_output_v1
* output
,
1072 const char* name
) {}
1074 static void handle_xdg_output_v1_description(void* data
,
1075 struct zxdg_output_v1
* output
,
1076 const char* description
) {}
1078 static const struct zxdg_output_v1_listener xdg_output_v1_listener
= {
1079 .logical_position
= handle_xdg_output_v1_logical_position
,
1080 .logical_size
= handle_xdg_output_v1_logical_size
,
1081 .done
= handle_xdg_output_v1_done
,
1082 .name
= handle_xdg_output_v1_name
,
1083 .description
= handle_xdg_output_v1_description
,
1086 static void add_xdg_output_v1_info(
1087 struct xdg_output_manager_v1_info
* manager_info
,
1088 struct output_info
* output
) {
1089 struct xdg_output_v1_info
* xdg_output
=
1090 (struct xdg_output_v1_info
*)calloc(1, sizeof *xdg_output
);
1092 wl_list_insert(&manager_info
->outputs
, &xdg_output
->link
);
1093 xdg_output
->xdg_output
= zxdg_output_manager_v1_get_xdg_output(
1094 manager_info
->manager
, output
->output
);
1095 zxdg_output_v1_add_listener(xdg_output
->xdg_output
, &xdg_output_v1_listener
,
1098 xdg_output
->output
= output
;
1100 manager_info
->info
->roundtrip_needed
= true;
1103 static void add_xdg_output_manager_v1_info(struct weston_info
* info
,
1104 uint32_t id
, uint32_t version
) {
1105 struct output_info
* output
;
1106 struct xdg_output_manager_v1_info
* manager
=
1107 (struct xdg_output_manager_v1_info
*)calloc(1, sizeof *manager
);
1109 wl_list_init(&manager
->outputs
);
1110 manager
->info
= info
;
1112 init_global_info(info
, &manager
->global
, id
,
1113 zxdg_output_manager_v1_interface
.name
, version
);
1114 manager
->global
.print
= print_xdg_output_manager_v1_info
;
1115 manager
->global
.destroy
= destroy_xdg_output_manager_v1_info
;
1117 manager
->manager
= (struct zxdg_output_manager_v1
*)wl_registry_bind(
1118 info
->registry
, id
, &zxdg_output_manager_v1_interface
,
1119 version
> 2 ? 2 : version
);
1121 wl_list_for_each(output
, &info
->outputs
, global_link
) {
1122 add_xdg_output_v1_info(manager
, output
);
1125 info
->xdg_output_manager_v1_info
= manager
;
1128 static void output_handle_geometry(void* data
, struct wl_output
* wl_output
,
1129 int32_t x
, int32_t y
, int32_t physical_width
,
1130 int32_t physical_height
, int32_t subpixel
,
1131 const char* make
, const char* model
,
1132 int32_t output_transform
) {}
1134 static void output_handle_mode(void* data
, struct wl_output
* wl_output
,
1135 uint32_t flags
, int32_t width
, int32_t height
,
1138 static void output_handle_done(void* data
, struct wl_output
* wl_output
) {}
1140 static void output_handle_scale(void* data
, struct wl_output
* wl_output
,
1142 struct output_info
* output
= (struct output_info
*)data
;
1144 output
->scale
= scale
;
1147 static const struct wl_output_listener output_listener
= {
1148 output_handle_geometry
,
1151 output_handle_scale
,
1154 static void destroy_output_info(void* data
) {
1155 struct output_info
* output
= (struct output_info
*)data
;
1157 wl_output_destroy(output
->output
);
1160 static void add_output_info(struct weston_info
* info
, uint32_t id
,
1162 struct output_info
* output
= (struct output_info
*)calloc(1, sizeof *output
);
1164 init_global_info(info
, &output
->global
, id
, "wl_output", version
);
1165 output
->global
.print
= print_output_info
;
1166 output
->global
.destroy
= destroy_output_info
;
1168 output
->version
= MIN(version
, 2);
1171 output
->output
= (struct wl_output
*)wl_registry_bind(
1172 info
->registry
, id
, &wl_output_interface
, output
->version
);
1173 wl_output_add_listener(output
->output
, &output_listener
, output
);
1175 info
->roundtrip_needed
= true;
1176 wl_list_insert(&info
->outputs
, &output
->global_link
);
1178 if (info
->xdg_output_manager_v1_info
) {
1179 add_xdg_output_v1_info(info
->xdg_output_manager_v1_info
, output
);
1183 static void global_handler(void* data
, struct wl_registry
* registry
,
1184 uint32_t id
, const char* interface
,
1186 struct weston_info
* info
= (struct weston_info
*)data
;
1188 if (!strcmp(interface
, "wl_output")) {
1189 add_output_info(info
, id
, version
);
1190 } else if (!strcmp(interface
, zxdg_output_manager_v1_interface
.name
)) {
1191 add_xdg_output_manager_v1_info(info
, id
, version
);
1195 static void global_remove_handler(void* data
, struct wl_registry
* registry
,
1198 static const struct wl_registry_listener registry_listener
= {
1199 global_handler
, global_remove_handler
};
1201 static void print_infos(struct wl_list
* infos
) {
1202 struct global_info
* info
;
1204 wl_list_for_each(info
, infos
, link
) { info
->print(info
); }
1207 static void destroy_info(void* data
) {
1208 struct global_info
* global
= (struct global_info
*)data
;
1210 global
->destroy(data
);
1211 wl_list_remove(&global
->link
);
1212 free(global
->interface
);
1216 static void destroy_infos(struct wl_list
* infos
) {
1217 struct global_info
*info
, *tmp
;
1218 wl_list_for_each_safe(info
, tmp
, infos
, link
) { destroy_info(info
); }
1221 static void get_wayland_screen_info(struct wl_display
* dpy
) {
1222 struct weston_info info
= {0};
1225 info
.xdg_output_manager_v1_info
= NULL
;
1226 wl_list_init(&info
.infos
);
1227 wl_list_init(&info
.outputs
);
1229 info
.registry
= wl_display_get_registry(info
.display
);
1230 wl_registry_add_listener(info
.registry
, ®istry_listener
, &info
);
1233 info
.roundtrip_needed
= false;
1234 wl_display_roundtrip(info
.display
);
1235 } while (info
.roundtrip_needed
);
1237 print_infos(&info
.infos
);
1238 destroy_infos(&info
.infos
);
1240 wl_registry_destroy(info
.registry
);
1243 static void wayland_egltest() {
1244 // NOTE: returns false to fall back to X11 when the Wayland socket doesn't
1245 // exist but fails with record_error if something actually went wrong
1246 struct wl_display
* dpy
= wl_display_connect(nullptr);
1248 record_error("Could not connect to wayland socket");
1252 if (!get_egl_status((EGLNativeDisplayType
)dpy
, true, false)) {
1253 record_error("EGL test failed");
1255 get_wayland_screen_info(dpy
);
1257 wl_display_disconnect(dpy
);
1258 record_value("TEST_TYPE\nEGL\n");
1263 enum { bufsize
= 2048 };
1266 // We save it as a global so that the X error handler can flush the buffer
1267 // before early exiting.
1269 glxtest_bufsize
= bufsize
;
1271 // Get a list of all GPUs from the PCI bus.
1272 int pci_count
= get_pci_status();
1275 if (IsWaylandEnabled()) {
1281 // TODO: --display command line argument is not properly handled
1282 if (!x11_egltest(pci_count
)) {
1288 // Finally write buffered data to the pipe.
1291 // If we completely filled the buffer, we need to tell the parent.
1292 if (glxtest_length
>= glxtest_bufsize
) {
1293 return EXIT_FAILURE_BUFFER_TOO_SMALL
;
1296 return EXIT_SUCCESS
;
1301 /** \returns true in the child glxtest process, false in the parent process */
1302 bool fire_glxtest_process() {
1304 if (pipe(pfd
) == -1) {
1315 // The child exits early to avoid running the full shutdown sequence and avoid
1316 // conflicting with threads we have already spawned (like the profiler).
1319 write_end_of_the_pipe
= pfd
[1];
1321 int rv
= childgltest();
1327 mozilla::widget::glxtest_pipe
= pfd
[0];
1328 mozilla::widget::glxtest_pid
= pid
;