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"
42 # include "mozilla/widget/mozwayland.h"
43 # include "mozilla/widget/xdg-output-unstable-v1-client-protocol.h"
48 typedef struct __GLXcontextRec
* GLXContext
;
49 typedef XID GLXPixmap
;
50 typedef XID GLXDrawable
;
51 /* GLX 1.3 and later */
52 typedef struct __GLXFBConfigRec
* GLXFBConfig
;
53 typedef XID GLXFBConfigID
;
54 typedef XID GLXContextID
;
55 typedef XID GLXWindow
;
56 typedef XID GLXPbuffer
;
58 # define GLX_RED_SIZE 8
59 # define GLX_GREEN_SIZE 9
60 # define GLX_BLUE_SIZE 10
61 # define GLX_DOUBLEBUFFER 5
65 typedef uint8_t GLubyte
;
66 typedef uint32_t GLenum
;
67 #define GL_VENDOR 0x1F00
68 #define GL_RENDERER 0x1F01
69 #define GL_VERSION 0x1F02
71 // GLX_MESA_query_renderer
73 #define GLX_RENDERER_VENDOR_ID_MESA 0x8183
74 #define GLX_RENDERER_DEVICE_ID_MESA 0x8184
75 #define GLX_RENDERER_VERSION_MESA 0x8185
76 #define GLX_RENDERER_ACCELERATED_MESA 0x8186
77 #define GLX_RENDERER_VIDEO_MEMORY_MESA 0x8187
78 #define GLX_RENDERER_UNIFIED_MEMORY_ARCHITECTURE_MESA 0x8188
79 #define GLX_RENDERER_PREFERRED_PROFILE_MESA 0x8189
80 #define GLX_RENDERER_OPENGL_CORE_PROFILE_VERSION_MESA 0x818A
81 #define GLX_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_MESA 0x818B
82 #define GLX_RENDERER_OPENGL_ES_PROFILE_VERSION_MESA 0x818C
83 #define GLX_RENDERER_OPENGL_ES2_PROFILE_VERSION_MESA 0x818D
84 #define GLX_RENDERER_ID_MESA 0x818E
88 typedef intptr_t EGLAttrib
;
89 typedef int EGLBoolean
;
90 typedef void* EGLConfig
;
91 typedef void* EGLContext
;
92 typedef void* EGLDeviceEXT
;
93 typedef void* EGLDisplay
;
95 typedef void* EGLNativeDisplayType
;
96 typedef void* EGLSurface
;
97 typedef void* (*PFNEGLGETPROCADDRESS
)(const char*);
99 #define EGL_NO_CONTEXT nullptr
100 #define EGL_NO_SURFACE nullptr
103 #define EGL_BLUE_SIZE 0x3022
104 #define EGL_GREEN_SIZE 0x3023
105 #define EGL_RED_SIZE 0x3024
106 #define EGL_NONE 0x3038
107 #define EGL_VENDOR 0x3053
108 #define EGL_CONTEXT_CLIENT_VERSION 0x3098
109 #define EGL_OPENGL_API 0x30A2
110 #define EGL_DEVICE_EXT 0x322C
111 #define EGL_DRM_DEVICE_FILE_EXT 0x3233
113 // stuff from xf86drm.h
114 #define DRM_NODE_RENDER 2
115 #define DRM_NODE_MAX 3
117 typedef struct _drmPciDeviceInfo
{
120 uint16_t subvendor_id
;
121 uint16_t subdevice_id
;
123 } drmPciDeviceInfo
, *drmPciDeviceInfoPtr
;
125 typedef struct _drmDevice
{
136 drmPciDeviceInfoPtr pci
;
141 } drmDevice
, *drmDevicePtr
;
143 // Open libGL and load needed symbols
144 #if defined(__OpenBSD__) || defined(__NetBSD__)
145 # define LIBGL_FILENAME "libGL.so"
146 # define LIBGLES_FILENAME "libGLESv2.so"
147 # define LIBEGL_FILENAME "libEGL.so"
148 # define LIBDRM_FILENAME "libdrm.so"
150 # define LIBGL_FILENAME "libGL.so.1"
151 # define LIBGLES_FILENAME "libGLESv2.so.2"
152 # define LIBEGL_FILENAME "libEGL.so.1"
153 # define LIBDRM_FILENAME "libdrm.so.2"
156 #define EXIT_FAILURE_BUFFER_TOO_SMALL 2
160 // the read end of the pipe, which will be used by GfxInfo
161 extern int glxtest_pipe
;
162 // the PID of the glxtest process, to pass to waitpid()
163 extern pid_t glxtest_pid
;
164 } // namespace widget
165 } // namespace mozilla
167 // the write end of the pipe, which we're going to write to
168 static int write_end_of_the_pipe
= -1;
170 // our buffer, size and used length
171 static char* glxtest_buf
= nullptr;
172 static int glxtest_bufsize
= 0;
173 static int glxtest_length
= 0;
175 // C++ standard collides with C standard in that it doesn't allow casting void*
176 // to function pointer types. So the work-around is to convert first to size_t.
177 // http://www.trilithium.com/johan/2004/12/problem-with-dlsym/
178 template <typename func_ptr_type
>
179 static func_ptr_type
cast(void* ptr
) {
180 return reinterpret_cast<func_ptr_type
>(reinterpret_cast<size_t>(ptr
));
183 static void record_value(const char* format
, ...) {
184 // Don't add more if the buffer is full.
185 if (glxtest_bufsize
<= glxtest_length
) {
189 // Append the new values to the buffer, not to exceed the remaining space.
190 int remaining
= glxtest_bufsize
- glxtest_length
;
192 va_start(args
, format
);
194 vsnprintf(glxtest_buf
+ glxtest_length
, remaining
, format
, args
);
197 // snprintf returns how many char it could have added, not how many it added.
198 // It is important to get this right since it will control how many chars we
199 // will attempt to write to the pipe fd.
200 if (max_added
> remaining
) {
201 glxtest_length
+= remaining
;
203 glxtest_length
+= max_added
;
207 static void record_error(const char* str
) { record_value("ERROR\n%s\n", str
); }
209 static void record_warning(const char* str
) {
210 record_value("WARNING\n%s\n", str
);
213 static void record_flush() {
214 mozilla::Unused
<< write(write_end_of_the_pipe
, glxtest_buf
, glxtest_length
);
218 static int x_error_handler(Display
*, XErrorEvent
* ev
) {
220 "ERROR\nX error, error_code=%d, "
221 "request_code=%d, minor_code=%d\n",
222 ev
->error_code
, ev
->request_code
, ev
->minor_code
);
229 // childgltest is declared inside extern "C" so that the name is not mangled.
230 // The name is used in build/valgrind/x86_64-pc-linux-gnu.sup to suppress
231 // memory leak errors because we run it inside a short lived fork and we don't
232 // care about leaking memory
235 static void close_logging() {
236 // we want to redirect to /dev/null stdout, stderr, and while we're at it,
237 // any PR logging file descriptors. To that effect, we redirect all positive
238 // file descriptors up to what open() returns here. In particular, 1 is stdout
240 int fd
= open("/dev/null", O_WRONLY
);
241 for (int i
= 1; i
< fd
; i
++) {
246 if (getenv("MOZ_AVOID_OPENGL_ALTOGETHER")) {
247 const char* msg
= "ERROR\nMOZ_AVOID_OPENGL_ALTOGETHER envvar set";
248 mozilla::Unused
<< write(write_end_of_the_pipe
, msg
, strlen(msg
));
253 #define PCI_FILL_IDENT 0x0001
254 #define PCI_FILL_CLASS 0x0020
255 #define PCI_BASE_CLASS_DISPLAY 0x03
257 static int get_pci_status() {
258 void* libpci
= dlopen("libpci.so.3", RTLD_LAZY
);
260 libpci
= dlopen("libpci.so", RTLD_LAZY
);
263 record_warning("libpci missing");
267 typedef struct pci_dev
{
268 struct pci_dev
* next
;
270 uint8_t bus
, dev
, func
;
271 unsigned int known_fields
;
272 uint16_t vendor_id
, device_id
;
273 uint16_t device_class
;
276 typedef struct pci_access
{
283 unsigned int id_lookup_mode
;
291 typedef pci_access
* (*PCIALLOC
)(void);
292 PCIALLOC pci_alloc
= cast
<PCIALLOC
>(dlsym(libpci
, "pci_alloc"));
294 typedef void (*PCIINIT
)(pci_access
*);
295 PCIINIT pci_init
= cast
<PCIINIT
>(dlsym(libpci
, "pci_init"));
297 typedef void (*PCICLEANUP
)(pci_access
*);
298 PCICLEANUP pci_cleanup
= cast
<PCICLEANUP
>(dlsym(libpci
, "pci_cleanup"));
300 typedef void (*PCISCANBUS
)(pci_access
*);
301 PCISCANBUS pci_scan_bus
= cast
<PCISCANBUS
>(dlsym(libpci
, "pci_scan_bus"));
303 typedef void (*PCIFILLINFO
)(pci_dev
*, int);
304 PCIFILLINFO pci_fill_info
= cast
<PCIFILLINFO
>(dlsym(libpci
, "pci_fill_info"));
306 if (!pci_alloc
|| !pci_cleanup
|| !pci_scan_bus
|| !pci_fill_info
) {
308 record_warning("libpci missing methods");
312 pci_access
* pacc
= pci_alloc();
315 record_warning("libpci alloc failed");
323 for (pci_dev
* dev
= pacc
->devices
; dev
; dev
= dev
->next
) {
324 pci_fill_info(dev
, PCI_FILL_IDENT
| PCI_FILL_CLASS
);
325 if (dev
->device_class
>> 8 == PCI_BASE_CLASS_DISPLAY
&& dev
->vendor_id
&&
328 record_value("PCI_VENDOR_ID\n0x%04x\nPCI_DEVICE_ID\n0x%04x\n",
329 dev
->vendor_id
, dev
->device_id
);
339 static bool device_has_name(const drmDevice
* device
, const char* name
) {
340 for (size_t i
= 0; i
< DRM_NODE_MAX
; i
++) {
341 if (!(device
->available_nodes
& (1 << i
))) {
344 if (strcmp(device
->nodes
[i
], name
) == 0) {
351 static void get_render_name(const char* name
) {
352 void* libdrm
= dlopen(LIBDRM_FILENAME
, RTLD_LAZY
);
354 record_warning("Failed to open libdrm");
358 typedef int (*DRMGETDEVICES2
)(uint32_t, drmDevicePtr
*, int);
359 DRMGETDEVICES2 drmGetDevices2
=
360 cast
<DRMGETDEVICES2
>(dlsym(libdrm
, "drmGetDevices2"));
362 typedef void (*DRMFREEDEVICE
)(drmDevicePtr
*);
363 DRMFREEDEVICE drmFreeDevice
=
364 cast
<DRMFREEDEVICE
>(dlsym(libdrm
, "drmFreeDevice"));
366 if (!drmGetDevices2
|| !drmFreeDevice
) {
368 "libdrm missing methods for drmGetDevices2 or drmFreeDevice");
374 int devices_len
= drmGetDevices2(flags
, nullptr, 0);
375 if (devices_len
< 0) {
376 record_warning("drmGetDevices2 failed");
380 drmDevice
** devices
= (drmDevice
**)calloc(devices_len
, sizeof(drmDevice
*));
382 record_warning("Allocation error");
386 devices_len
= drmGetDevices2(flags
, devices
, devices_len
);
387 if (devices_len
< 0) {
389 record_warning("drmGetDevices2 failed");
394 const drmDevice
* match
= nullptr;
395 for (int i
= 0; i
< devices_len
; i
++) {
396 if (device_has_name(devices
[i
], name
)) {
403 record_warning("Cannot find DRM device");
404 } else if (!(match
->available_nodes
& (1 << DRM_NODE_RENDER
))) {
405 record_warning("DRM device has no render node");
407 record_value("DRM_RENDERDEVICE\n%s\n", match
->nodes
[DRM_NODE_RENDER
]);
409 "MESA_VENDOR_ID\n0x%04x\n"
410 "MESA_DEVICE_ID\n0x%04x\n",
411 match
->deviceinfo
.pci
->vendor_id
, match
->deviceinfo
.pci
->device_id
);
414 for (int i
= 0; i
< devices_len
; i
++) {
415 drmFreeDevice(&devices
[i
]);
423 static bool get_gles_status(EGLDisplay dpy
,
424 PFNEGLGETPROCADDRESS eglGetProcAddress
) {
425 typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC
)(
426 EGLDisplay dpy
, EGLint
const* attrib_list
, EGLConfig
* configs
,
427 EGLint config_size
, EGLint
* num_config
);
428 PFNEGLCHOOSECONFIGPROC eglChooseConfig
=
429 cast
<PFNEGLCHOOSECONFIGPROC
>(eglGetProcAddress("eglChooseConfig"));
431 typedef EGLBoolean (*PFNEGLBINDAPIPROC
)(EGLint api
);
432 PFNEGLBINDAPIPROC eglBindAPI
=
433 cast
<PFNEGLBINDAPIPROC
>(eglGetProcAddress("eglBindAPI"));
435 typedef EGLContext (*PFNEGLCREATECONTEXTPROC
)(
436 EGLDisplay dpy
, EGLConfig config
, EGLContext share_context
,
437 EGLint
const* attrib_list
);
438 PFNEGLCREATECONTEXTPROC eglCreateContext
=
439 cast
<PFNEGLCREATECONTEXTPROC
>(eglGetProcAddress("eglCreateContext"));
441 typedef EGLBoolean (*PFNEGLMAKECURRENTPROC
)(
442 EGLDisplay dpy
, EGLSurface draw
, EGLSurface read
, EGLContext context
);
443 PFNEGLMAKECURRENTPROC eglMakeCurrent
=
444 cast
<PFNEGLMAKECURRENTPROC
>(eglGetProcAddress("eglMakeCurrent"));
446 typedef const char* (*PFNEGLQUERYDEVICESTRINGEXTPROC
)(EGLDeviceEXT device
,
448 PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT
=
449 cast
<PFNEGLQUERYDEVICESTRINGEXTPROC
>(
450 eglGetProcAddress("eglQueryDeviceStringEXT"));
452 typedef EGLBoolean (*PFNEGLQUERYDISPLAYATTRIBEXTPROC
)(
453 EGLDisplay dpy
, EGLint name
, EGLAttrib
* value
);
454 PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT
=
455 cast
<PFNEGLQUERYDISPLAYATTRIBEXTPROC
>(
456 eglGetProcAddress("eglQueryDisplayAttribEXT"));
458 if (!eglChooseConfig
|| !eglCreateContext
|| !eglMakeCurrent
) {
459 record_warning("libEGL missing methods for GL test");
463 typedef GLubyte
* (*PFNGLGETSTRING
)(GLenum
);
464 PFNGLGETSTRING glGetString
=
465 cast
<PFNGLGETSTRING
>(eglGetProcAddress("glGetString"));
467 EGLint config_attrs
[] = {EGL_RED_SIZE
, 8, EGL_GREEN_SIZE
, 8,
468 EGL_BLUE_SIZE
, 8, EGL_NONE
};
472 if (eglChooseConfig(dpy
, config_attrs
, &config
, 1, &num_config
) ==
474 record_warning("eglChooseConfig returned an error");
478 if (eglBindAPI(EGL_OPENGL_API
) == EGL_FALSE
) {
479 record_warning("eglBindAPI returned an error");
483 EGLint ctx_attrs
[] = {EGL_NONE
};
484 EGLContext ectx
= eglCreateContext(dpy
, config
, EGL_NO_CONTEXT
, ctx_attrs
);
486 record_warning("eglCreateContext returned an error");
490 if (eglMakeCurrent(dpy
, EGL_NO_SURFACE
, EGL_NO_SURFACE
, ectx
) == EGL_FALSE
) {
491 record_warning("eglMakeCurrent returned an error");
495 // Implementations disagree about whether eglGetProcAddress or dlsym
496 // should be used for getting functions from the actual API, see
497 // https://github.com/anholt/libepoxy/commit/14f24485e33816139398d1bd170d617703473738
498 void* libgl
= nullptr;
500 libgl
= dlopen(LIBGL_FILENAME
, RTLD_LAZY
);
502 libgl
= dlopen(LIBGLES_FILENAME
, RTLD_LAZY
);
504 record_warning(LIBGL_FILENAME
" and " LIBGLES_FILENAME
" missing");
509 glGetString
= cast
<PFNGLGETSTRING
>(dlsym(libgl
, "glGetString"));
512 record_warning("libEGL, libGL and libGLESv2 are missing glGetString");
517 const GLubyte
* versionString
= glGetString(GL_VERSION
);
518 const GLubyte
* vendorString
= glGetString(GL_VENDOR
);
519 const GLubyte
* rendererString
= glGetString(GL_RENDERER
);
521 if (versionString
&& vendorString
&& rendererString
) {
522 record_value("VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\nTRUE\n",
523 vendorString
, rendererString
, versionString
);
525 record_warning("EGL glGetString returned null");
532 if (eglQueryDeviceStringEXT
) {
533 EGLDeviceEXT device
= nullptr;
535 if (eglQueryDisplayAttribEXT(dpy
, EGL_DEVICE_EXT
, (EGLAttrib
*)&device
) ==
537 const char* deviceString
=
538 eglQueryDeviceStringEXT(device
, EGL_DRM_DEVICE_FILE_EXT
);
540 record_value("MESA_ACCELERATED\nTRUE\n");
543 get_render_name(deviceString
);
546 record_value("MESA_ACCELERATED\nFALSE\n");
557 static bool get_egl_status(EGLNativeDisplayType native_dpy
, bool gles_test
,
558 bool require_driver
) {
559 void* libegl
= dlopen(LIBEGL_FILENAME
, RTLD_LAZY
);
561 record_warning("libEGL missing");
565 PFNEGLGETPROCADDRESS eglGetProcAddress
=
566 cast
<PFNEGLGETPROCADDRESS
>(dlsym(libegl
, "eglGetProcAddress"));
568 if (!eglGetProcAddress
) {
570 record_warning("no eglGetProcAddress");
574 typedef EGLDisplay (*PFNEGLGETDISPLAYPROC
)(void* native_display
);
575 PFNEGLGETDISPLAYPROC eglGetDisplay
=
576 cast
<PFNEGLGETDISPLAYPROC
>(eglGetProcAddress("eglGetDisplay"));
578 typedef EGLBoolean (*PFNEGLINITIALIZEPROC
)(EGLDisplay dpy
, EGLint
* major
,
580 PFNEGLINITIALIZEPROC eglInitialize
=
581 cast
<PFNEGLINITIALIZEPROC
>(eglGetProcAddress("eglInitialize"));
583 typedef EGLBoolean (*PFNEGLTERMINATEPROC
)(EGLDisplay dpy
);
584 PFNEGLTERMINATEPROC eglTerminate
=
585 cast
<PFNEGLTERMINATEPROC
>(eglGetProcAddress("eglTerminate"));
587 if (!eglGetDisplay
|| !eglInitialize
|| !eglTerminate
) {
589 record_warning("libEGL missing methods");
593 EGLDisplay dpy
= eglGetDisplay(native_dpy
);
596 record_warning("libEGL no display");
601 if (!eglInitialize(dpy
, &major
, &minor
)) {
603 record_warning("libEGL initialize failed");
607 typedef const char* (*PFNEGLGETDISPLAYDRIVERNAMEPROC
)(EGLDisplay dpy
);
608 PFNEGLGETDISPLAYDRIVERNAMEPROC eglGetDisplayDriverName
=
609 cast
<PFNEGLGETDISPLAYDRIVERNAMEPROC
>(
610 eglGetProcAddress("eglGetDisplayDriverName"));
611 if (eglGetDisplayDriverName
) {
612 // TODO(aosmond): If the driver name is empty, we probably aren't using Mesa
613 // and instead a proprietary GL, most likely NVIDIA's. The PCI device list
614 // in combination with the vendor name is very likely sufficient to identify
616 const char* driDriver
= eglGetDisplayDriverName(dpy
);
618 record_value("DRI_DRIVER\n%s\n", driDriver
);
620 } else if (require_driver
) {
621 record_warning("libEGL missing eglGetDisplayDriverName");
627 if (gles_test
&& !get_gles_status(dpy
, eglGetProcAddress
)) {
639 static void get_x11_screen_info(Display
* dpy
) {
640 int screenCount
= ScreenCount(dpy
);
641 int defaultScreen
= DefaultScreen(dpy
);
642 if (screenCount
!= 0) {
643 record_value("SCREEN_INFO\n");
644 for (int idx
= 0; idx
< screenCount
; idx
++) {
645 Screen
* scrn
= ScreenOfDisplay(dpy
, idx
);
646 int current_height
= scrn
->height
;
647 int current_width
= scrn
->width
;
649 record_value("%dx%d:%d%s", current_width
, current_height
,
650 idx
== defaultScreen
? 1 : 0,
651 idx
== screenCount
- 1 ? ";\n" : ";");
656 static void get_glx_status(int* gotGlxInfo
, int* gotDriDriver
) {
657 void* libgl
= dlopen(LIBGL_FILENAME
, RTLD_LAZY
);
659 record_error(LIBGL_FILENAME
" missing");
663 typedef void* (*PFNGLXGETPROCADDRESS
)(const char*);
664 PFNGLXGETPROCADDRESS glXGetProcAddress
=
665 cast
<PFNGLXGETPROCADDRESS
>(dlsym(libgl
, "glXGetProcAddress"));
667 if (!glXGetProcAddress
) {
668 record_error("no glXGetProcAddress");
672 typedef GLXFBConfig
* (*PFNGLXQUERYEXTENSION
)(Display
*, int*, int*);
673 PFNGLXQUERYEXTENSION glXQueryExtension
=
674 cast
<PFNGLXQUERYEXTENSION
>(glXGetProcAddress("glXQueryExtension"));
676 typedef GLXFBConfig
* (*PFNGLXQUERYVERSION
)(Display
*, int*, int*);
677 PFNGLXQUERYVERSION glXQueryVersion
=
678 cast
<PFNGLXQUERYVERSION
>(dlsym(libgl
, "glXQueryVersion"));
680 typedef XVisualInfo
* (*PFNGLXCHOOSEVISUAL
)(Display
*, int, int*);
681 PFNGLXCHOOSEVISUAL glXChooseVisual
=
682 cast
<PFNGLXCHOOSEVISUAL
>(glXGetProcAddress("glXChooseVisual"));
684 typedef GLXContext (*PFNGLXCREATECONTEXT
)(Display
*, XVisualInfo
*, GLXContext
,
686 PFNGLXCREATECONTEXT glXCreateContext
=
687 cast
<PFNGLXCREATECONTEXT
>(glXGetProcAddress("glXCreateContext"));
689 typedef Bool (*PFNGLXMAKECURRENT
)(Display
*, GLXDrawable
, GLXContext
);
690 PFNGLXMAKECURRENT glXMakeCurrent
=
691 cast
<PFNGLXMAKECURRENT
>(glXGetProcAddress("glXMakeCurrent"));
693 typedef void (*PFNGLXDESTROYCONTEXT
)(Display
*, GLXContext
);
694 PFNGLXDESTROYCONTEXT glXDestroyContext
=
695 cast
<PFNGLXDESTROYCONTEXT
>(glXGetProcAddress("glXDestroyContext"));
697 typedef GLubyte
* (*PFNGLGETSTRING
)(GLenum
);
698 PFNGLGETSTRING glGetString
=
699 cast
<PFNGLGETSTRING
>(glXGetProcAddress("glGetString"));
701 if (!glXQueryExtension
|| !glXQueryVersion
|| !glXChooseVisual
||
702 !glXCreateContext
|| !glXMakeCurrent
|| !glXDestroyContext
||
704 record_error(LIBGL_FILENAME
" missing methods");
708 ///// Open a connection to the X server /////
709 Display
* dpy
= XOpenDisplay(nullptr);
711 record_error("Unable to open a connection to the X server");
715 ///// Check that the GLX extension is present /////
716 if (!glXQueryExtension(dpy
, nullptr, nullptr)) {
717 record_error("GLX extension missing");
721 XSetErrorHandler(x_error_handler
);
723 ///// Get a visual /////
724 int attribs
[] = {GLX_RGBA
, GLX_RED_SIZE
, 1, GLX_GREEN_SIZE
,
725 1, GLX_BLUE_SIZE
, 1, None
};
726 XVisualInfo
* vInfo
= glXChooseVisual(dpy
, DefaultScreen(dpy
), attribs
);
728 int attribs2
[] = {GLX_RGBA
, GLX_RED_SIZE
, 1, GLX_GREEN_SIZE
,
729 1, GLX_BLUE_SIZE
, 1, GLX_DOUBLEBUFFER
,
731 vInfo
= glXChooseVisual(dpy
, DefaultScreen(dpy
), attribs2
);
733 record_error("No visuals found");
738 // using a X11 Window instead of a GLXPixmap does not crash
739 // fglrx in indirect rendering. bug 680644
741 XSetWindowAttributes swa
;
742 swa
.colormap
= XCreateColormap(dpy
, RootWindow(dpy
, vInfo
->screen
),
743 vInfo
->visual
, AllocNone
);
745 swa
.border_pixel
= 0;
746 window
= XCreateWindow(dpy
, RootWindow(dpy
, vInfo
->screen
), 0, 0, 16, 16, 0,
747 vInfo
->depth
, InputOutput
, vInfo
->visual
,
748 CWBorderPixel
| CWColormap
, &swa
);
750 ///// Get a GL context and make it current //////
751 GLXContext context
= glXCreateContext(dpy
, vInfo
, nullptr, True
);
752 glXMakeCurrent(dpy
, window
, context
);
754 ///// Look for this symbol to determine texture_from_pixmap support /////
755 void* glXBindTexImageEXT
= glXGetProcAddress("glXBindTexImageEXT");
757 ///// Get GL vendor/renderer/versions strings /////
758 const GLubyte
* versionString
= glGetString(GL_VERSION
);
759 const GLubyte
* vendorString
= glGetString(GL_VENDOR
);
760 const GLubyte
* rendererString
= glGetString(GL_RENDERER
);
762 if (versionString
&& vendorString
&& rendererString
) {
763 record_value("VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\n%s\n",
764 vendorString
, rendererString
, versionString
,
765 glXBindTexImageEXT
? "TRUE" : "FALSE");
768 record_error("glGetString returned null");
771 // If GLX_MESA_query_renderer is available, populate additional data.
772 typedef Bool (*PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC
)(
773 int attribute
, unsigned int* value
);
774 PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC
775 glXQueryCurrentRendererIntegerMESAProc
=
776 cast
<PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC
>(
777 glXGetProcAddress("glXQueryCurrentRendererIntegerMESA"));
778 if (glXQueryCurrentRendererIntegerMESAProc
) {
779 unsigned int vendorId
, deviceId
, accelerated
, videoMemoryMB
;
780 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_VENDOR_ID_MESA
,
782 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_DEVICE_ID_MESA
,
784 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_ACCELERATED_MESA
,
786 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_VIDEO_MEMORY_MESA
,
789 // Truncate IDs to 4 digits- that's all PCI IDs are.
794 "MESA_VENDOR_ID\n0x%04x\n"
795 "MESA_DEVICE_ID\n0x%04x\n"
796 "MESA_ACCELERATED\n%s\n"
798 vendorId
, deviceId
, accelerated
? "TRUE" : "FALSE", videoMemoryMB
);
801 // From Mesa's GL/internal/dri_interface.h, to be used by DRI clients.
802 typedef const char* (*PFNGLXGETSCREENDRIVERPROC
)(Display
* dpy
, int scrNum
);
803 PFNGLXGETSCREENDRIVERPROC glXGetScreenDriverProc
=
804 cast
<PFNGLXGETSCREENDRIVERPROC
>(glXGetProcAddress("glXGetScreenDriver"));
805 if (glXGetScreenDriverProc
) {
806 const char* driDriver
= glXGetScreenDriverProc(dpy
, DefaultScreen(dpy
));
809 record_value("DRI_DRIVER\n%s\n", driDriver
);
813 // Get monitor information
814 get_x11_screen_info(dpy
);
816 ///// Clean up. Indeed, the parent process might fail to kill us (e.g. if it
817 ///// doesn't need to check GL info) so we might be staying alive for longer
818 ///// than expected, so it's important to consume as little memory as
819 ///// possible. Also we want to check that we're able to do that too without
820 ///// generating X errors.
821 glXMakeCurrent(dpy
, None
,
822 nullptr); // must release the GL context before destroying it
823 glXDestroyContext(dpy
, context
);
824 XDestroyWindow(dpy
, window
);
825 XFreeColormap(dpy
, swa
.colormap
);
827 # ifdef NS_FREE_PERMANENT_DATA // conditionally defined in nscore.h, don't
828 // forget to #include it above
831 // This XSync call wanted to be instead:
832 // XCloseDisplay(dpy);
833 // but this can cause 1-minute stalls on certain setups using Nouveau, see bug
841 static bool x11_egltest(int pci_count
) {
842 Display
* dpy
= XOpenDisplay(nullptr);
846 XSetErrorHandler(x_error_handler
);
848 // On at least amdgpu open source driver, eglInitialize fails unless
849 // a valid XDisplay pointer is passed as the native display.
850 if (!get_egl_status(dpy
, true, pci_count
!= 1)) {
854 get_x11_screen_info(dpy
);
856 // Bug 1715245: Closing the display connection here crashes on NV prop.
857 // drivers. Just leave it open, the process will exit shortly after anyway.
858 // XCloseDisplay(dpy);
860 record_value("TEST_TYPE\nEGL\n");
864 static void glxtest() {
866 int gotDriDriver
= 0;
868 get_glx_status(&gotGlxInfo
, &gotDriDriver
);
870 get_egl_status(nullptr, true, false);
871 } else if (!gotDriDriver
) {
872 // If we failed to get the driver name from X, try via
873 // EGL_MESA_query_driver. We are probably using Wayland.
874 get_egl_status(nullptr, false, true);
877 record_value("TEST_TYPE\nGLX\n");
882 typedef void (*print_info_t
)(void* info
);
883 typedef void (*destroy_info_t
)(void* info
);
893 destroy_info_t destroy
;
897 struct global_info global
;
898 struct wl_list global_link
;
900 struct wl_output
* output
;
907 struct xdg_output_v1_info
{
910 struct zxdg_output_v1
* xdg_output
;
911 struct output_info
* output
;
914 int32_t width
, height
;
918 struct xdg_output_manager_v1_info
{
919 struct global_info global
;
920 struct zxdg_output_manager_v1
* manager
;
921 struct weston_info
* info
;
923 struct wl_list outputs
;
927 struct wl_display
* display
;
928 struct wl_registry
* registry
;
930 struct wl_list infos
;
931 bool roundtrip_needed
;
933 struct wl_list outputs
;
934 struct xdg_output_manager_v1_info
* xdg_output_manager_v1_info
;
937 static void init_global_info(struct weston_info
* info
,
938 struct global_info
* global
, uint32_t id
,
939 const char* interface
, uint32_t version
) {
941 global
->version
= version
;
942 global
->interface
= strdup(interface
);
944 wl_list_insert(info
->infos
.prev
, &global
->link
);
947 static void print_output_info(void* data
) {}
949 static void destroy_xdg_output_v1_info(struct xdg_output_v1_info
* info
) {
950 wl_list_remove(&info
->link
);
951 zxdg_output_v1_destroy(info
->xdg_output
);
955 static int cmpOutputIds(const void* a
, const void* b
) {
956 return (((struct xdg_output_v1_info
*)a
)->output
->global
.id
-
957 ((struct xdg_output_v1_info
*)b
)->output
->global
.id
);
960 static void print_xdg_output_manager_v1_info(void* data
) {
961 struct xdg_output_manager_v1_info
* info
=
962 (struct xdg_output_manager_v1_info
*)data
;
963 struct xdg_output_v1_info
* output
;
965 int screen_count
= wl_list_length(&info
->outputs
);
966 if (screen_count
> 0) {
967 struct xdg_output_v1_info
* infos
= (struct xdg_output_v1_info
*)calloc(
968 1, screen_count
* sizeof(xdg_output_v1_info
));
971 wl_list_for_each(output
, &info
->outputs
, link
) {
972 infos
[pos
] = *output
;
976 if (screen_count
> 1) {
977 qsort(infos
, screen_count
, sizeof(struct xdg_output_v1_info
),
981 record_value("SCREEN_INFO\n");
982 for (int i
= 0; i
< screen_count
; i
++) {
983 record_value("%dx%d:0;", infos
[i
].logical
.width
, infos
[i
].logical
.height
);
991 static void destroy_xdg_output_manager_v1_info(void* data
) {
992 struct xdg_output_manager_v1_info
* info
=
993 (struct xdg_output_manager_v1_info
*)data
;
994 struct xdg_output_v1_info
*output
, *tmp
;
996 zxdg_output_manager_v1_destroy(info
->manager
);
998 wl_list_for_each_safe(output
, tmp
, &info
->outputs
, link
) {
999 destroy_xdg_output_v1_info(output
);
1003 static void handle_xdg_output_v1_logical_position(void* data
,
1004 struct zxdg_output_v1
* output
,
1005 int32_t x
, int32_t y
) {}
1007 static void handle_xdg_output_v1_logical_size(void* data
,
1008 struct zxdg_output_v1
* output
,
1009 int32_t width
, int32_t height
) {
1010 struct xdg_output_v1_info
* xdg_output
= (struct xdg_output_v1_info
*)data
;
1011 xdg_output
->logical
.width
= width
;
1012 xdg_output
->logical
.height
= height
;
1015 static void handle_xdg_output_v1_done(void* data
,
1016 struct zxdg_output_v1
* output
) {}
1018 static void handle_xdg_output_v1_name(void* data
, struct zxdg_output_v1
* output
,
1019 const char* name
) {}
1021 static void handle_xdg_output_v1_description(void* data
,
1022 struct zxdg_output_v1
* output
,
1023 const char* description
) {}
1025 static const struct zxdg_output_v1_listener xdg_output_v1_listener
= {
1026 .logical_position
= handle_xdg_output_v1_logical_position
,
1027 .logical_size
= handle_xdg_output_v1_logical_size
,
1028 .done
= handle_xdg_output_v1_done
,
1029 .name
= handle_xdg_output_v1_name
,
1030 .description
= handle_xdg_output_v1_description
,
1033 static void add_xdg_output_v1_info(
1034 struct xdg_output_manager_v1_info
* manager_info
,
1035 struct output_info
* output
) {
1036 struct xdg_output_v1_info
* xdg_output
=
1037 (struct xdg_output_v1_info
*)calloc(1, sizeof *xdg_output
);
1039 wl_list_insert(&manager_info
->outputs
, &xdg_output
->link
);
1040 xdg_output
->xdg_output
= zxdg_output_manager_v1_get_xdg_output(
1041 manager_info
->manager
, output
->output
);
1042 zxdg_output_v1_add_listener(xdg_output
->xdg_output
, &xdg_output_v1_listener
,
1045 xdg_output
->output
= output
;
1047 manager_info
->info
->roundtrip_needed
= true;
1050 static void add_xdg_output_manager_v1_info(struct weston_info
* info
,
1051 uint32_t id
, uint32_t version
) {
1052 struct output_info
* output
;
1053 struct xdg_output_manager_v1_info
* manager
=
1054 (struct xdg_output_manager_v1_info
*)calloc(1, sizeof *manager
);
1056 wl_list_init(&manager
->outputs
);
1057 manager
->info
= info
;
1059 init_global_info(info
, &manager
->global
, id
,
1060 zxdg_output_manager_v1_interface
.name
, version
);
1061 manager
->global
.print
= print_xdg_output_manager_v1_info
;
1062 manager
->global
.destroy
= destroy_xdg_output_manager_v1_info
;
1064 manager
->manager
= (struct zxdg_output_manager_v1
*)wl_registry_bind(
1065 info
->registry
, id
, &zxdg_output_manager_v1_interface
,
1066 version
> 2 ? 2 : version
);
1068 wl_list_for_each(output
, &info
->outputs
, global_link
) {
1069 add_xdg_output_v1_info(manager
, output
);
1072 info
->xdg_output_manager_v1_info
= manager
;
1075 static void output_handle_geometry(void* data
, struct wl_output
* wl_output
,
1076 int32_t x
, int32_t y
, int32_t physical_width
,
1077 int32_t physical_height
, int32_t subpixel
,
1078 const char* make
, const char* model
,
1079 int32_t output_transform
) {}
1081 static void output_handle_mode(void* data
, struct wl_output
* wl_output
,
1082 uint32_t flags
, int32_t width
, int32_t height
,
1085 static void output_handle_done(void* data
, struct wl_output
* wl_output
) {}
1087 static void output_handle_scale(void* data
, struct wl_output
* wl_output
,
1089 struct output_info
* output
= (struct output_info
*)data
;
1091 output
->scale
= scale
;
1094 static const struct wl_output_listener output_listener
= {
1095 output_handle_geometry
,
1098 output_handle_scale
,
1101 static void destroy_output_info(void* data
) {
1102 struct output_info
* output
= (struct output_info
*)data
;
1104 wl_output_destroy(output
->output
);
1107 static void add_output_info(struct weston_info
* info
, uint32_t id
,
1109 struct output_info
* output
= (struct output_info
*)calloc(1, sizeof *output
);
1111 init_global_info(info
, &output
->global
, id
, "wl_output", version
);
1112 output
->global
.print
= print_output_info
;
1113 output
->global
.destroy
= destroy_output_info
;
1115 output
->version
= MIN(version
, 2);
1118 output
->output
= (struct wl_output
*)wl_registry_bind(
1119 info
->registry
, id
, &wl_output_interface
, output
->version
);
1120 wl_output_add_listener(output
->output
, &output_listener
, output
);
1122 info
->roundtrip_needed
= true;
1123 wl_list_insert(&info
->outputs
, &output
->global_link
);
1125 if (info
->xdg_output_manager_v1_info
) {
1126 add_xdg_output_v1_info(info
->xdg_output_manager_v1_info
, output
);
1130 static void global_handler(void* data
, struct wl_registry
* registry
,
1131 uint32_t id
, const char* interface
,
1133 struct weston_info
* info
= (struct weston_info
*)data
;
1135 if (!strcmp(interface
, "wl_output")) {
1136 add_output_info(info
, id
, version
);
1137 } else if (!strcmp(interface
, zxdg_output_manager_v1_interface
.name
)) {
1138 add_xdg_output_manager_v1_info(info
, id
, version
);
1142 static void global_remove_handler(void* data
, struct wl_registry
* registry
,
1145 static const struct wl_registry_listener registry_listener
= {
1146 global_handler
, global_remove_handler
};
1148 static void print_infos(struct wl_list
* infos
) {
1149 struct global_info
* info
;
1151 wl_list_for_each(info
, infos
, link
) { info
->print(info
); }
1154 static void destroy_info(void* data
) {
1155 struct global_info
* global
= (struct global_info
*)data
;
1157 global
->destroy(data
);
1158 wl_list_remove(&global
->link
);
1159 free(global
->interface
);
1163 static void destroy_infos(struct wl_list
* infos
) {
1164 struct global_info
*info
, *tmp
;
1165 wl_list_for_each_safe(info
, tmp
, infos
, link
) { destroy_info(info
); }
1168 static void get_wayland_screen_info(struct wl_display
* dpy
) {
1169 struct weston_info info
= {0};
1172 info
.xdg_output_manager_v1_info
= NULL
;
1173 wl_list_init(&info
.infos
);
1174 wl_list_init(&info
.outputs
);
1176 info
.registry
= wl_display_get_registry(info
.display
);
1177 wl_registry_add_listener(info
.registry
, ®istry_listener
, &info
);
1180 info
.roundtrip_needed
= false;
1181 wl_display_roundtrip(info
.display
);
1182 } while (info
.roundtrip_needed
);
1184 print_infos(&info
.infos
);
1185 destroy_infos(&info
.infos
);
1187 wl_registry_destroy(info
.registry
);
1190 static void wayland_egltest() {
1191 // NOTE: returns false to fall back to X11 when the Wayland socket doesn't
1192 // exist but fails with record_error if something actually went wrong
1193 struct wl_display
* dpy
= wl_display_connect(nullptr);
1195 record_error("Could not connect to wayland socket");
1199 if (!get_egl_status((EGLNativeDisplayType
)dpy
, true, false)) {
1200 record_error("EGL test failed");
1202 get_wayland_screen_info(dpy
);
1204 wl_display_disconnect(dpy
);
1205 record_value("TEST_TYPE\nEGL\n");
1210 enum { bufsize
= 2048 };
1213 // We save it as a global so that the X error handler can flush the buffer
1214 // before early exiting.
1216 glxtest_bufsize
= bufsize
;
1218 // Get a list of all GPUs from the PCI bus.
1219 int pci_count
= get_pci_status();
1222 if (IsWaylandEnabled()) {
1228 // TODO: --display command line argument is not properly handled
1229 if (!x11_egltest(pci_count
)) {
1235 // Finally write buffered data to the pipe.
1238 // If we completely filled the buffer, we need to tell the parent.
1239 if (glxtest_length
>= glxtest_bufsize
) {
1240 return EXIT_FAILURE_BUFFER_TOO_SMALL
;
1243 return EXIT_SUCCESS
;
1248 /** \returns true in the child glxtest process, false in the parent process */
1249 bool fire_glxtest_process() {
1251 if (pipe(pfd
) == -1) {
1262 // The child exits early to avoid running the full shutdown sequence and avoid
1263 // conflicting with threads we have already spawned (like the profiler).
1266 write_end_of_the_pipe
= pfd
[1];
1268 int rv
= childgltest();
1274 mozilla::widget::glxtest_pipe
= pfd
[0];
1275 mozilla::widget::glxtest_pid
= pid
;