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.
34 #if defined(MOZ_ASAN) || defined(FUZZING)
43 # include "X11/Xlib.h"
44 # include "X11/Xutil.h"
45 # include <X11/extensions/Xrandr.h>
50 #include "mozilla/ScopeExit.h"
51 #include "mozilla/Types.h"
53 #include "mozilla/GfxInfoUtils.h"
57 typedef struct __GLXcontextRec
* GLXContext
;
58 typedef XID GLXPixmap
;
59 typedef XID GLXDrawable
;
60 /* GLX 1.3 and later */
61 typedef struct __GLXFBConfigRec
* GLXFBConfig
;
62 typedef XID GLXFBConfigID
;
63 typedef XID GLXContextID
;
64 typedef XID GLXWindow
;
65 typedef XID GLXPbuffer
;
67 # define GLX_RED_SIZE 8
68 # define GLX_GREEN_SIZE 9
69 # define GLX_BLUE_SIZE 10
70 # define GLX_DOUBLEBUFFER 5
74 typedef uint8_t GLubyte
;
75 typedef uint32_t GLenum
;
76 #define GL_VENDOR 0x1F00
77 #define GL_RENDERER 0x1F01
78 #define GL_VERSION 0x1F02
80 // GLX_MESA_query_renderer
82 #define GLX_RENDERER_VENDOR_ID_MESA 0x8183
83 #define GLX_RENDERER_DEVICE_ID_MESA 0x8184
84 #define GLX_RENDERER_VERSION_MESA 0x8185
85 #define GLX_RENDERER_ACCELERATED_MESA 0x8186
86 #define GLX_RENDERER_VIDEO_MEMORY_MESA 0x8187
87 #define GLX_RENDERER_UNIFIED_MEMORY_ARCHITECTURE_MESA 0x8188
88 #define GLX_RENDERER_PREFERRED_PROFILE_MESA 0x8189
89 #define GLX_RENDERER_OPENGL_CORE_PROFILE_VERSION_MESA 0x818A
90 #define GLX_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_MESA 0x818B
91 #define GLX_RENDERER_OPENGL_ES_PROFILE_VERSION_MESA 0x818C
92 #define GLX_RENDERER_OPENGL_ES2_PROFILE_VERSION_MESA 0x818D
93 #define GLX_RENDERER_ID_MESA 0x818E
97 typedef intptr_t EGLAttrib
;
98 typedef int EGLBoolean
;
99 typedef void* EGLConfig
;
100 typedef void* EGLContext
;
101 typedef void* EGLDeviceEXT
;
102 typedef void* EGLDisplay
;
103 typedef unsigned int EGLenum
;
105 typedef void* EGLNativeDisplayType
;
106 typedef void* EGLSurface
;
107 typedef void* (*PFNEGLGETPROCADDRESS
)(const char*);
109 #define EGL_NO_CONTEXT nullptr
110 #define EGL_NO_SURFACE nullptr
113 #define EGL_OPENGL_ES2_BIT 0x0004
114 #define EGL_BLUE_SIZE 0x3022
115 #define EGL_GREEN_SIZE 0x3023
116 #define EGL_RED_SIZE 0x3024
117 #define EGL_NONE 0x3038
118 #define EGL_RENDERABLE_TYPE 0x3040
119 #define EGL_VENDOR 0x3053
120 #define EGL_EXTENSIONS 0x3055
121 #define EGL_CONTEXT_MAJOR_VERSION 0x3098
122 #define EGL_OPENGL_ES_API 0x30A0
123 #define EGL_OPENGL_API 0x30A2
124 #define EGL_DEVICE_EXT 0x322C
125 #define EGL_DRM_DEVICE_FILE_EXT 0x3233
126 #define EGL_DRM_RENDER_NODE_FILE_EXT 0x3377
128 // stuff from xf86drm.h
129 #define DRM_NODE_RENDER 2
130 #define DRM_NODE_MAX 3
132 typedef struct _drmPciDeviceInfo
{
135 uint16_t subvendor_id
;
136 uint16_t subdevice_id
;
138 } drmPciDeviceInfo
, *drmPciDeviceInfoPtr
;
140 typedef struct _drmDevice
{
151 drmPciDeviceInfoPtr pci
;
156 } drmDevice
, *drmDevicePtr
;
158 // Open libGL and load needed symbols
159 #if defined(__OpenBSD__) || defined(__NetBSD__)
160 # define LIBGL_FILENAME "libGL.so"
161 # define LIBGLES_FILENAME "libGLESv2.so"
162 # define LIBEGL_FILENAME "libEGL.so"
163 # define LIBDRM_FILENAME "libdrm.so"
165 # define LIBGL_FILENAME "libGL.so.1"
166 # define LIBGLES_FILENAME "libGLESv2.so.2"
167 # define LIBEGL_FILENAME "libEGL.so.1"
168 # define LIBDRM_FILENAME "libdrm.so.2"
172 static int x_error_handler(Display
*, XErrorEvent
* ev
) {
174 "X error, error_code=%d, "
175 "request_code=%d, minor_code=%d",
176 ev
->error_code
, ev
->request_code
, ev
->minor_code
);
182 // childgltest is declared inside extern "C" so that the name is not mangled.
183 // The name is used in build/valgrind/x86_64-pc-linux-gnu.sup to suppress
184 // memory leak errors because we run it inside a short lived fork and we don't
185 // care about leaking memory
188 #define PCI_FILL_IDENT 0x0001
189 #define PCI_FILL_CLASS 0x0020
190 #define PCI_BASE_CLASS_DISPLAY 0x03
192 static void get_pci_status() {
193 log("GLX_TEST: get_pci_status start\n");
195 #if !defined(XP_FREEBSD) && !defined(XP_NETBSD) && !defined(XP_OPENBSD) && \
197 if (access("/sys/bus/pci/", F_OK
) != 0 &&
198 access("/sys/bus/pci_express/", F_OK
) != 0) {
199 log("GLX_TEST: get_pci_status failed: cannot access /sys/bus/pci\n");
203 void* libpci
= dlopen("libpci.so.3", RTLD_LAZY
);
205 libpci
= dlopen("libpci.so", RTLD_LAZY
);
208 record_warning("libpci missing");
211 auto release
= mozilla::MakeScopeExit([&] { dlclose(libpci
); });
213 typedef struct pci_dev
{
214 struct pci_dev
* next
;
216 uint8_t bus
, dev
, func
;
217 unsigned int known_fields
;
218 uint16_t vendor_id
, device_id
;
219 uint16_t device_class
;
222 typedef struct pci_access
{
229 unsigned int id_lookup_mode
;
237 typedef pci_access
* (*PCIALLOC
)(void);
238 PCIALLOC pci_alloc
= cast
<PCIALLOC
>(dlsym(libpci
, "pci_alloc"));
240 typedef void (*PCIINIT
)(pci_access
*);
241 PCIINIT pci_init
= cast
<PCIINIT
>(dlsym(libpci
, "pci_init"));
243 typedef void (*PCICLEANUP
)(pci_access
*);
244 PCICLEANUP pci_cleanup
= cast
<PCICLEANUP
>(dlsym(libpci
, "pci_cleanup"));
246 typedef void (*PCISCANBUS
)(pci_access
*);
247 PCISCANBUS pci_scan_bus
= cast
<PCISCANBUS
>(dlsym(libpci
, "pci_scan_bus"));
249 typedef void (*PCIFILLINFO
)(pci_dev
*, int);
250 PCIFILLINFO pci_fill_info
= cast
<PCIFILLINFO
>(dlsym(libpci
, "pci_fill_info"));
252 if (!pci_alloc
|| !pci_cleanup
|| !pci_scan_bus
|| !pci_fill_info
) {
254 record_warning("libpci missing methods");
258 pci_access
* pacc
= pci_alloc();
260 record_warning("libpci alloc failed");
267 for (pci_dev
* dev
= pacc
->devices
; dev
; dev
= dev
->next
) {
268 pci_fill_info(dev
, PCI_FILL_IDENT
| PCI_FILL_CLASS
);
269 if (dev
->device_class
>> 8 == PCI_BASE_CLASS_DISPLAY
&& dev
->vendor_id
&&
271 record_value("PCI_VENDOR_ID\n0x%04x\nPCI_DEVICE_ID\n0x%04x\n",
272 dev
->vendor_id
, dev
->device_id
);
279 log("GLX_TEST: get_pci_status finished\n");
282 static void set_render_device_path(const char* render_device_path
) {
283 record_value("DRM_RENDERDEVICE\n%s\n", render_device_path
);
286 static bool device_has_name(const drmDevice
* device
, const char* name
) {
287 for (size_t i
= 0; i
< DRM_NODE_MAX
; i
++) {
288 if (!(device
->available_nodes
& (1 << i
))) {
291 if (strcmp(device
->nodes
[i
], name
) == 0) {
298 static bool get_render_name(const char* name
) {
299 void* libdrm
= dlopen(LIBDRM_FILENAME
, RTLD_LAZY
);
301 record_warning("Failed to open libdrm");
304 auto release
= mozilla::MakeScopeExit([&] { dlclose(libdrm
); });
306 typedef int (*DRMGETDEVICES2
)(uint32_t, drmDevicePtr
*, int);
307 DRMGETDEVICES2 drmGetDevices2
=
308 cast
<DRMGETDEVICES2
>(dlsym(libdrm
, "drmGetDevices2"));
310 typedef void (*DRMFREEDEVICE
)(drmDevicePtr
*);
311 DRMFREEDEVICE drmFreeDevice
=
312 cast
<DRMFREEDEVICE
>(dlsym(libdrm
, "drmFreeDevice"));
314 if (!drmGetDevices2
|| !drmFreeDevice
) {
316 "libdrm missing methods for drmGetDevices2 or drmFreeDevice");
321 int devices_len
= drmGetDevices2(flags
, nullptr, 0);
322 if (devices_len
< 0) {
323 record_warning("drmGetDevices2 failed");
326 drmDevice
** devices
= (drmDevice
**)calloc(devices_len
, sizeof(drmDevice
*));
328 record_warning("Allocation error");
331 devices_len
= drmGetDevices2(flags
, devices
, devices_len
);
332 if (devices_len
< 0) {
334 record_warning("drmGetDevices2 failed");
338 const drmDevice
* match
= nullptr;
339 for (int i
= 0; i
< devices_len
; i
++) {
340 if (device_has_name(devices
[i
], name
)) {
346 // Fallback path for split kms/render devices - if only one drm render node
347 // exists it's most likely the one we're looking for.
348 if (match
&& !(match
->available_nodes
& (1 << DRM_NODE_RENDER
))) {
350 for (int i
= 0; i
< devices_len
; i
++) {
351 if (devices
[i
]->available_nodes
& (1 << DRM_NODE_RENDER
)) {
355 // more than one candidate found, stop trying.
363 "DRM render node not clearly detectable. Falling back to using the "
364 "only one that was found.");
366 record_warning("DRM device has no render node");
372 record_warning("Cannot find DRM device");
374 set_render_device_path(match
->nodes
[DRM_NODE_RENDER
]);
376 "MESA_VENDOR_ID\n0x%04x\n"
377 "MESA_DEVICE_ID\n0x%04x\n",
378 match
->deviceinfo
.pci
->vendor_id
, match
->deviceinfo
.pci
->device_id
);
382 for (int i
= 0; i
< devices_len
; i
++) {
383 drmFreeDevice(&devices
[i
]);
389 static bool get_egl_gl_status(EGLDisplay dpy
,
390 PFNEGLGETPROCADDRESS eglGetProcAddress
) {
391 typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC
)(
392 EGLDisplay dpy
, EGLint
const* attrib_list
, EGLConfig
* configs
,
393 EGLint config_size
, EGLint
* num_config
);
394 PFNEGLCHOOSECONFIGPROC eglChooseConfig
=
395 cast
<PFNEGLCHOOSECONFIGPROC
>(eglGetProcAddress("eglChooseConfig"));
397 typedef EGLBoolean (*PFNEGLBINDAPIPROC
)(EGLint api
);
398 PFNEGLBINDAPIPROC eglBindAPI
=
399 cast
<PFNEGLBINDAPIPROC
>(eglGetProcAddress("eglBindAPI"));
401 typedef EGLContext (*PFNEGLCREATECONTEXTPROC
)(
402 EGLDisplay dpy
, EGLConfig config
, EGLContext share_context
,
403 EGLint
const* attrib_list
);
404 PFNEGLCREATECONTEXTPROC eglCreateContext
=
405 cast
<PFNEGLCREATECONTEXTPROC
>(eglGetProcAddress("eglCreateContext"));
407 typedef EGLBoolean (*PFNEGLDESTROYCONTEXTPROC
)(EGLDisplay dpy
,
409 PFNEGLDESTROYCONTEXTPROC eglDestroyContext
=
410 cast
<PFNEGLDESTROYCONTEXTPROC
>(eglGetProcAddress("eglDestroyContext"));
412 typedef EGLBoolean (*PFNEGLMAKECURRENTPROC
)(
413 EGLDisplay dpy
, EGLSurface draw
, EGLSurface read
, EGLContext context
);
414 PFNEGLMAKECURRENTPROC eglMakeCurrent
=
415 cast
<PFNEGLMAKECURRENTPROC
>(eglGetProcAddress("eglMakeCurrent"));
417 typedef const char* (*PFNEGLQUERYDEVICESTRINGEXTPROC
)(EGLDeviceEXT device
,
419 PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT
=
420 cast
<PFNEGLQUERYDEVICESTRINGEXTPROC
>(
421 eglGetProcAddress("eglQueryDeviceStringEXT"));
423 typedef EGLBoolean (*PFNEGLQUERYDISPLAYATTRIBEXTPROC
)(
424 EGLDisplay dpy
, EGLint name
, EGLAttrib
* value
);
425 PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT
=
426 cast
<PFNEGLQUERYDISPLAYATTRIBEXTPROC
>(
427 eglGetProcAddress("eglQueryDisplayAttribEXT"));
429 log("GLX_TEST: get_egl_gl_status start\n");
431 if (!eglChooseConfig
|| !eglCreateContext
|| !eglDestroyContext
||
432 !eglMakeCurrent
|| !eglQueryDeviceStringEXT
) {
433 record_warning("libEGL missing methods for GL test");
437 typedef GLubyte
* (*PFNGLGETSTRING
)(GLenum
);
438 PFNGLGETSTRING glGetString
=
439 cast
<PFNGLGETSTRING
>(eglGetProcAddress("glGetString"));
441 #if defined(__arm__) || defined(__aarch64__)
444 bool useGles
= false;
447 std::vector
<EGLint
> attribs
;
448 attribs
.push_back(EGL_RED_SIZE
);
449 attribs
.push_back(8);
450 attribs
.push_back(EGL_GREEN_SIZE
);
451 attribs
.push_back(8);
452 attribs
.push_back(EGL_BLUE_SIZE
);
453 attribs
.push_back(8);
455 attribs
.push_back(EGL_RENDERABLE_TYPE
);
456 attribs
.push_back(EGL_OPENGL_ES2_BIT
);
458 attribs
.push_back(EGL_NONE
);
462 if (eglChooseConfig(dpy
, attribs
.data(), &config
, 1, &num_config
) ==
464 record_warning("eglChooseConfig returned an error");
468 EGLenum api
= useGles
? EGL_OPENGL_ES_API
: EGL_OPENGL_API
;
469 if (eglBindAPI(api
) == EGL_FALSE
) {
470 record_warning("eglBindAPI returned an error");
474 EGLint ctx_attrs
[] = {EGL_CONTEXT_MAJOR_VERSION
, 3, EGL_NONE
};
475 EGLContext ectx
= eglCreateContext(dpy
, config
, EGL_NO_CONTEXT
, ctx_attrs
);
477 EGLint ctx_attrs_fallback
[] = {EGL_CONTEXT_MAJOR_VERSION
, 2, EGL_NONE
};
478 ectx
= eglCreateContext(dpy
, config
, EGL_NO_CONTEXT
, ctx_attrs_fallback
);
480 record_warning("eglCreateContext returned an error");
485 if (eglMakeCurrent(dpy
, EGL_NO_SURFACE
, EGL_NO_SURFACE
, ectx
) == EGL_FALSE
) {
486 eglDestroyContext(dpy
, ectx
);
487 record_warning("eglMakeCurrent returned an error");
490 eglDestroyContext(dpy
, ectx
);
492 // Implementations disagree about whether eglGetProcAddress or dlsym
493 // should be used for getting functions from the actual API, see
494 // https://github.com/anholt/libepoxy/commit/14f24485e33816139398d1bd170d617703473738
495 void* libgl
= nullptr;
497 libgl
= dlopen(LIBGL_FILENAME
, RTLD_LAZY
);
499 libgl
= dlopen(LIBGLES_FILENAME
, RTLD_LAZY
);
501 record_warning(LIBGL_FILENAME
" and " LIBGLES_FILENAME
" missing");
506 glGetString
= cast
<PFNGLGETSTRING
>(dlsym(libgl
, "glGetString"));
509 record_warning("libEGL, libGL and libGLESv2 are missing glGetString");
513 auto release
= mozilla::MakeScopeExit([&] {
519 const GLubyte
* versionString
= glGetString(GL_VERSION
);
520 const GLubyte
* vendorString
= glGetString(GL_VENDOR
);
521 const GLubyte
* rendererString
= glGetString(GL_RENDERER
);
523 if (versionString
&& vendorString
&& rendererString
) {
524 record_value("VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\nTRUE\n",
525 vendorString
, rendererString
, versionString
);
527 record_warning("EGL glGetString returned null");
532 if (eglQueryDisplayAttribEXT(dpy
, EGL_DEVICE_EXT
, (EGLAttrib
*)&device
) ==
534 const char* deviceExtensions
=
535 eglQueryDeviceStringEXT(device
, EGL_EXTENSIONS
);
536 if (deviceExtensions
&&
537 strstr(deviceExtensions
, "EGL_MESA_device_software")) {
538 record_value("MESA_ACCELERATED\nFALSE\n");
540 const char* deviceString
=
541 eglQueryDeviceStringEXT(device
, EGL_DRM_DEVICE_FILE_EXT
);
542 if (!deviceString
|| !get_render_name(deviceString
)) {
543 const char* renderNodeString
=
544 eglQueryDeviceStringEXT(device
, EGL_DRM_RENDER_NODE_FILE_EXT
);
545 if (renderNodeString
) {
546 set_render_device_path(renderNodeString
);
552 log("GLX_TEST: get_egl_gl_status finished\n");
556 static bool get_egl_status(EGLNativeDisplayType native_dpy
) {
557 log("GLX_TEST: get_egl_status start\n");
559 EGLDisplay dpy
= nullptr;
561 typedef EGLBoolean (*PFNEGLTERMINATEPROC
)(EGLDisplay dpy
);
562 PFNEGLTERMINATEPROC eglTerminate
= nullptr;
564 void* libegl
= dlopen(LIBEGL_FILENAME
, RTLD_LAZY
);
566 record_warning("libEGL missing");
569 auto release
= mozilla::MakeScopeExit([&] {
573 // Unload libegl here causes ASAN/MemLeaks failures on Linux
574 // as libegl may not be meant to be unloaded runtime.
575 // See 1304156 for reference.
579 PFNEGLGETPROCADDRESS eglGetProcAddress
=
580 cast
<PFNEGLGETPROCADDRESS
>(dlsym(libegl
, "eglGetProcAddress"));
582 if (!eglGetProcAddress
) {
583 record_warning("no eglGetProcAddress");
587 typedef EGLDisplay (*PFNEGLGETDISPLAYPROC
)(void* native_display
);
588 PFNEGLGETDISPLAYPROC eglGetDisplay
=
589 cast
<PFNEGLGETDISPLAYPROC
>(eglGetProcAddress("eglGetDisplay"));
591 typedef EGLBoolean (*PFNEGLINITIALIZEPROC
)(EGLDisplay dpy
, EGLint
* major
,
593 PFNEGLINITIALIZEPROC eglInitialize
=
594 cast
<PFNEGLINITIALIZEPROC
>(eglGetProcAddress("eglInitialize"));
595 eglTerminate
= cast
<PFNEGLTERMINATEPROC
>(eglGetProcAddress("eglTerminate"));
597 if (!eglGetDisplay
|| !eglInitialize
|| !eglTerminate
) {
598 record_warning("libEGL missing methods");
602 dpy
= eglGetDisplay(native_dpy
);
604 record_warning("libEGL no display");
609 if (!eglInitialize(dpy
, &major
, &minor
)) {
610 record_warning("libEGL initialize failed");
614 typedef const char* (*PFNEGLGETDISPLAYDRIVERNAMEPROC
)(EGLDisplay dpy
);
615 PFNEGLGETDISPLAYDRIVERNAMEPROC eglGetDisplayDriverName
=
616 cast
<PFNEGLGETDISPLAYDRIVERNAMEPROC
>(
617 eglGetProcAddress("eglGetDisplayDriverName"));
618 if (eglGetDisplayDriverName
) {
619 const char* driDriver
= eglGetDisplayDriverName(dpy
);
621 record_value("DRI_DRIVER\n%s\n", driDriver
);
625 bool ret
= get_egl_gl_status(dpy
, eglGetProcAddress
);
626 log("GLX_TEST: get_egl_status finished with return: %d\n", ret
);
632 static void get_xrandr_info(Display
* dpy
) {
633 log("GLX_TEST: get_xrandr_info start\n");
635 // When running on remote X11 the xrandr version may be stuck on an ancient
636 // version. There are still setups using remote X11 out there, so make sure we
638 int eventBase
, errorBase
, major
, minor
;
639 if (!XRRQueryExtension(dpy
, &eventBase
, &errorBase
) ||
640 !XRRQueryVersion(dpy
, &major
, &minor
) ||
641 !(major
> 1 || (major
== 1 && minor
>= 4))) {
642 log("GLX_TEST: get_xrandr_info failed, old version.\n");
646 Window root
= RootWindow(dpy
, DefaultScreen(dpy
));
647 XRRProviderResources
* pr
= XRRGetProviderResources(dpy
, root
);
649 log("GLX_TEST: XRRGetProviderResources failed.\n");
652 XRRScreenResources
* res
= XRRGetScreenResourcesCurrent(dpy
, root
);
654 XRRFreeProviderResources(pr
);
655 log("GLX_TEST: XRRGetScreenResourcesCurrent failed.\n");
658 if (pr
->nproviders
!= 0) {
659 record_value("DDX_DRIVER\n");
660 for (int i
= 0; i
< pr
->nproviders
; i
++) {
661 XRRProviderInfo
* info
= XRRGetProviderInfo(dpy
, res
, pr
->providers
[i
]);
663 record_value("%s%s", info
->name
, i
== pr
->nproviders
- 1 ? ";\n" : ";");
664 XRRFreeProviderInfo(info
);
668 XRRFreeScreenResources(res
);
669 XRRFreeProviderResources(pr
);
671 log("GLX_TEST: get_xrandr_info finished\n");
675 log("GLX_TEST: glxtest start\n");
677 Display
* dpy
= nullptr;
678 void* libgl
= dlopen(LIBGL_FILENAME
, RTLD_LAZY
);
680 record_error(LIBGL_FILENAME
" missing");
683 auto release
= mozilla::MakeScopeExit([&] {
688 // This XSync call wanted to be instead:
689 // XCloseDisplay(dpy);
690 // but this can cause 1-minute stalls on certain setups using Nouveau, see
698 typedef void* (*PFNGLXGETPROCADDRESS
)(const char*);
699 PFNGLXGETPROCADDRESS glXGetProcAddress
=
700 cast
<PFNGLXGETPROCADDRESS
>(dlsym(libgl
, "glXGetProcAddress"));
702 if (!glXGetProcAddress
) {
703 record_error("no glXGetProcAddress");
707 typedef GLXFBConfig
* (*PFNGLXQUERYEXTENSION
)(Display
*, int*, int*);
708 PFNGLXQUERYEXTENSION glXQueryExtension
=
709 cast
<PFNGLXQUERYEXTENSION
>(glXGetProcAddress("glXQueryExtension"));
711 typedef GLXFBConfig
* (*PFNGLXQUERYVERSION
)(Display
*, int*, int*);
712 PFNGLXQUERYVERSION glXQueryVersion
=
713 cast
<PFNGLXQUERYVERSION
>(dlsym(libgl
, "glXQueryVersion"));
715 typedef XVisualInfo
* (*PFNGLXCHOOSEVISUAL
)(Display
*, int, int*);
716 PFNGLXCHOOSEVISUAL glXChooseVisual
=
717 cast
<PFNGLXCHOOSEVISUAL
>(glXGetProcAddress("glXChooseVisual"));
719 typedef GLXContext (*PFNGLXCREATECONTEXT
)(Display
*, XVisualInfo
*, GLXContext
,
721 PFNGLXCREATECONTEXT glXCreateContext
=
722 cast
<PFNGLXCREATECONTEXT
>(glXGetProcAddress("glXCreateContext"));
724 typedef Bool (*PFNGLXMAKECURRENT
)(Display
*, GLXDrawable
, GLXContext
);
725 PFNGLXMAKECURRENT glXMakeCurrent
=
726 cast
<PFNGLXMAKECURRENT
>(glXGetProcAddress("glXMakeCurrent"));
728 typedef void (*PFNGLXDESTROYCONTEXT
)(Display
*, GLXContext
);
729 PFNGLXDESTROYCONTEXT glXDestroyContext
=
730 cast
<PFNGLXDESTROYCONTEXT
>(glXGetProcAddress("glXDestroyContext"));
732 typedef GLubyte
* (*PFNGLGETSTRING
)(GLenum
);
733 PFNGLGETSTRING glGetString
=
734 cast
<PFNGLGETSTRING
>(glXGetProcAddress("glGetString"));
736 if (!glXQueryExtension
|| !glXQueryVersion
|| !glXChooseVisual
||
737 !glXCreateContext
|| !glXMakeCurrent
|| !glXDestroyContext
||
739 record_error(LIBGL_FILENAME
" missing methods");
743 ///// Open a connection to the X server /////
744 dpy
= XOpenDisplay(nullptr);
746 record_error("Unable to open a connection to the X server");
750 ///// Check that the GLX extension is present /////
751 if (!glXQueryExtension(dpy
, nullptr, nullptr)) {
752 record_error("GLX extension missing");
756 XSetErrorHandler(x_error_handler
);
758 ///// Get a visual /////
759 int attribs
[] = {GLX_RGBA
, GLX_RED_SIZE
, 1, GLX_GREEN_SIZE
,
760 1, GLX_BLUE_SIZE
, 1, None
};
761 XVisualInfo
* vInfo
= glXChooseVisual(dpy
, DefaultScreen(dpy
), attribs
);
763 int attribs2
[] = {GLX_RGBA
, GLX_RED_SIZE
, 1, GLX_GREEN_SIZE
,
764 1, GLX_BLUE_SIZE
, 1, GLX_DOUBLEBUFFER
,
766 vInfo
= glXChooseVisual(dpy
, DefaultScreen(dpy
), attribs2
);
768 record_error("No visuals found");
773 // using a X11 Window instead of a GLXPixmap does not crash
774 // fglrx in indirect rendering. bug 680644
776 XSetWindowAttributes swa
;
777 swa
.colormap
= XCreateColormap(dpy
, RootWindow(dpy
, vInfo
->screen
),
778 vInfo
->visual
, AllocNone
);
780 swa
.border_pixel
= 0;
781 window
= XCreateWindow(dpy
, RootWindow(dpy
, vInfo
->screen
), 0, 0, 16, 16, 0,
782 vInfo
->depth
, InputOutput
, vInfo
->visual
,
783 CWBorderPixel
| CWColormap
, &swa
);
785 ///// Get a GL context and make it current //////
786 GLXContext context
= glXCreateContext(dpy
, vInfo
, nullptr, True
);
787 glXMakeCurrent(dpy
, window
, context
);
789 ///// Look for this symbol to determine texture_from_pixmap support /////
790 void* glXBindTexImageEXT
= glXGetProcAddress("glXBindTexImageEXT");
792 ///// Get GL vendor/renderer/versions strings /////
793 const GLubyte
* versionString
= glGetString(GL_VERSION
);
794 const GLubyte
* vendorString
= glGetString(GL_VENDOR
);
795 const GLubyte
* rendererString
= glGetString(GL_RENDERER
);
797 if (versionString
&& vendorString
&& rendererString
) {
798 record_value("VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\n%s\n",
799 vendorString
, rendererString
, versionString
,
800 glXBindTexImageEXT
? "TRUE" : "FALSE");
802 record_error("glGetString returned null");
805 // If GLX_MESA_query_renderer is available, populate additional data.
806 typedef Bool (*PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC
)(
807 int attribute
, unsigned int* value
);
808 PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC
809 glXQueryCurrentRendererIntegerMESAProc
=
810 cast
<PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC
>(
811 glXGetProcAddress("glXQueryCurrentRendererIntegerMESA"));
812 if (glXQueryCurrentRendererIntegerMESAProc
) {
813 unsigned int vendorId
, deviceId
, accelerated
, videoMemoryMB
;
814 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_VENDOR_ID_MESA
,
816 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_DEVICE_ID_MESA
,
818 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_ACCELERATED_MESA
,
820 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_VIDEO_MEMORY_MESA
,
823 // Truncate IDs to 4 digits- that's all PCI IDs are.
828 "MESA_VENDOR_ID\n0x%04x\n"
829 "MESA_DEVICE_ID\n0x%04x\n"
830 "MESA_ACCELERATED\n%s\n"
832 vendorId
, deviceId
, accelerated
? "TRUE" : "FALSE", videoMemoryMB
);
835 // From Mesa's GL/internal/dri_interface.h, to be used by DRI clients.
836 typedef const char* (*PFNGLXGETSCREENDRIVERPROC
)(Display
* dpy
, int scrNum
);
837 PFNGLXGETSCREENDRIVERPROC glXGetScreenDriverProc
=
838 cast
<PFNGLXGETSCREENDRIVERPROC
>(glXGetProcAddress("glXGetScreenDriver"));
839 if (glXGetScreenDriverProc
) {
840 const char* driDriver
= glXGetScreenDriverProc(dpy
, DefaultScreen(dpy
));
842 record_value("DRI_DRIVER\n%s\n", driDriver
);
846 // Get monitor and DDX driver information
847 get_xrandr_info(dpy
);
849 ///// Clean up. Indeed, the parent process might fail to kill us (e.g. if it
850 ///// doesn't need to check GL info) so we might be staying alive for longer
851 ///// than expected, so it's important to consume as little memory as
852 ///// possible. Also we want to check that we're able to do that too without
853 ///// generating X errors.
854 glXMakeCurrent(dpy
, None
,
855 nullptr); // must release the GL context before destroying it
856 glXDestroyContext(dpy
, context
);
857 XDestroyWindow(dpy
, window
);
858 XFreeColormap(dpy
, swa
.colormap
);
861 record_value("TEST_TYPE\nGLX\n");
862 log("GLX_TEST: glxtest finished\n");
866 log("GLX_TEST: x11_egltest start\n");
868 Display
* dpy
= XOpenDisplay(nullptr);
870 log("GLX_TEST: XOpenDisplay failed.\n");
874 auto release
= mozilla::MakeScopeExit([&] {
875 // Bug 1715245: Closing the display connection here crashes on NV prop.
876 // drivers. Just leave it open, the process will exit shortly after anyway.
881 XSetErrorHandler(x_error_handler
);
883 if (!get_egl_status(dpy
)) {
887 // Bug 1667621: 30bit "Deep Color" is broken on EGL on Mesa (as of 2021/10).
888 // Disable all non-standard depths for the initial EGL roleout.
889 int screenCount
= ScreenCount(dpy
);
890 for (int idx
= 0; idx
< screenCount
; idx
++) {
891 int depth
= DefaultDepth(dpy
, idx
);
893 log("GLX_TEST: DefaultDepth() is %d, expected to be 24. See Bug "
900 // Get monitor and DDX driver information
901 get_xrandr_info(dpy
);
903 record_value("TEST_TYPE\nEGL\n");
905 log("GLX_TEST: x11_egltest finished\n");
911 void wayland_egltest() {
912 log("GLX_TEST: wayland_egltest start\n");
914 static auto sWlDisplayConnect
= (struct wl_display
* (*)(const char*))
915 dlsym(RTLD_DEFAULT
, "wl_display_connect");
916 static auto sWlDisplayRoundtrip
=
917 (int (*)(struct wl_display
*))dlsym(RTLD_DEFAULT
, "wl_display_roundtrip");
918 static auto sWlDisplayDisconnect
= (void (*)(struct wl_display
*))dlsym(
919 RTLD_DEFAULT
, "wl_display_disconnect");
921 if (!sWlDisplayConnect
|| !sWlDisplayRoundtrip
|| !sWlDisplayDisconnect
) {
922 record_error("Missing Wayland libraries");
926 // NOTE: returns false to fall back to X11 when the Wayland socket doesn't
927 // exist but fails with record_error if something actually went wrong
928 struct wl_display
* dpy
= sWlDisplayConnect(nullptr);
930 record_error("Could not connect to wayland display, WAYLAND_DISPLAY=%s",
931 getenv("WAYLAND_DISPLAY"));
935 if (!get_egl_status((EGLNativeDisplayType
)dpy
)) {
936 record_error("EGL test failed");
939 // This is enough to crash some broken NVIDIA prime + Wayland setups, see
940 // https://github.com/NVIDIA/egl-wayland/issues/41 and bug 1768260.
941 sWlDisplayRoundtrip(dpy
);
943 sWlDisplayDisconnect(dpy
);
944 record_value("TEST_TYPE\nEGL\n");
945 log("GLX_TEST: wayland_egltest finished\n");
949 int childgltest(bool aWayland
) {
950 log("GLX_TEST: childgltest start\n");
952 // Get a list of all GPUs from the PCI bus.
962 // TODO: --display command line argument is not properly handled
963 if (!x11_egltest()) {
968 // Finally write buffered data to the pipe.
971 log("GLX_TEST: childgltest finished\n");
977 static void PrintUsage() {
979 "Firefox OpenGL probe utility\n"
981 "usage: glxtest [options]\n"
985 " -h --help show this message\n"
986 " -f --fd num where to print output, default it stdout\n"
987 " -w --wayland probe OpenGL/EGL on Wayland (default is "
992 int main(int argc
, char** argv
) {
993 struct option longOptions
[] = {{"help", no_argument
, nullptr, 'h'},
994 {"fd", required_argument
, nullptr, 'f'},
995 {"wayland", no_argument
, nullptr, 'w'},
996 {nullptr, 0, nullptr, 0}};
997 const char* shortOptions
= "hf:w";
999 bool wayland
= false;
1000 while ((c
= getopt_long(argc
, argv
, shortOptions
, longOptions
, nullptr)) !=
1007 output_pipe
= atoi(optarg
);
1011 // Dummy call to mozgtk to prevent the linker from removing
1012 // the dependency with --as-needed.
1013 // see toolkit/library/moz.build for details.
1014 gdk_display_get_default();
1022 if (getenv("MOZ_AVOID_OPENGL_ALTOGETHER")) {
1023 const char* msg
= "ERROR\nMOZ_AVOID_OPENGL_ALTOGETHER envvar set";
1024 MOZ_UNUSED(write(output_pipe
, msg
, strlen(msg
)));
1027 const char* env
= getenv("MOZ_GFX_DEBUG");
1028 enable_logging
= env
&& *env
== '1';
1029 if (!enable_logging
) {
1032 #if defined(MOZ_ASAN) || defined(FUZZING)
1033 // If handle_segv=1 (default), then glxtest crash will print a sanitizer
1034 // report which can confuse the harness in fuzzing automation.
1035 signal(SIGSEGV
, SIG_DFL
);
1037 return childgltest(wayland
);