Bug 1728955: part 5) Add missing `// static` comment to `nsClipboard::CreateNativeDat...
[gecko.git] / toolkit / xre / glxtest.cpp
blobd5ec70b3fd77e3647e0a0d366936ed9a293b6607
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=8 et :
3 */
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 //////////////////////////////////////////////////////////////////////////////
9 //
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.
22 #include <cstdio>
23 #include <cstdlib>
24 #include <dlfcn.h>
25 #include <fcntl.h>
26 #include <unistd.h>
28 #include "mozilla/Unused.h"
29 #include "nsAppRunner.h" // for IsWaylandEnabled on IsX11EGLEnabled
30 #include "stdint.h"
32 #ifdef __SUNPRO_CC
33 # include <stdio.h>
34 #endif
36 #ifdef MOZ_X11
37 # include "X11/Xlib.h"
38 # include "X11/Xutil.h"
39 #endif
41 #ifdef MOZ_WAYLAND
42 # include "mozilla/widget/mozwayland.h"
43 # include "mozilla/widget/xdg-output-unstable-v1-client-protocol.h"
44 #endif
46 #ifdef MOZ_X11
47 // stuff from glx.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;
57 # define GLX_RGBA 4
58 # define GLX_RED_SIZE 8
59 # define GLX_GREEN_SIZE 9
60 # define GLX_BLUE_SIZE 10
61 # define GLX_DOUBLEBUFFER 5
62 #endif
64 // stuff from gl.h
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
72 // clang-format off
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
85 // clang-format on
87 // stuff from egl.h
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;
94 typedef int EGLint;
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
101 #define EGL_FALSE 0
102 #define EGL_TRUE 1
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 {
118 uint16_t vendor_id;
119 uint16_t device_id;
120 uint16_t subvendor_id;
121 uint16_t subdevice_id;
122 uint8_t revision_id;
123 } drmPciDeviceInfo, *drmPciDeviceInfoPtr;
125 typedef struct _drmDevice {
126 char** nodes;
127 int available_nodes;
128 int bustype;
129 union {
130 void* pci;
131 void* usb;
132 void* platform;
133 void* host1x;
134 } businfo;
135 union {
136 drmPciDeviceInfoPtr pci;
137 void* usb;
138 void* platform;
139 void* host1x;
140 } deviceinfo;
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"
149 #else
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"
154 #endif
156 #define EXIT_FAILURE_BUFFER_TOO_SMALL 2
158 namespace mozilla {
159 namespace widget {
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) {
186 return;
189 // Append the new values to the buffer, not to exceed the remaining space.
190 int remaining = glxtest_bufsize - glxtest_length;
191 va_list args;
192 va_start(args, format);
193 int max_added =
194 vsnprintf(glxtest_buf + glxtest_length, remaining, format, args);
195 va_end(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;
202 } else {
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);
217 #ifdef MOZ_X11
218 static int x_error_handler(Display*, XErrorEvent* ev) {
219 record_value(
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);
223 record_flush();
224 _exit(EXIT_FAILURE);
225 return 0;
227 #endif
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
233 extern "C" {
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
239 // and 2 is stderr.
240 int fd = open("/dev/null", O_WRONLY);
241 for (int i = 1; i < fd; i++) {
242 dup2(fd, i);
244 close(fd);
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));
249 exit(EXIT_FAILURE);
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);
259 if (!libpci) {
260 libpci = dlopen("libpci.so", RTLD_LAZY);
262 if (!libpci) {
263 record_warning("libpci missing");
264 return 0;
267 typedef struct pci_dev {
268 struct pci_dev* next;
269 uint16_t domain_16;
270 uint8_t bus, dev, func;
271 unsigned int known_fields;
272 uint16_t vendor_id, device_id;
273 uint16_t device_class;
274 } pci_dev;
276 typedef struct pci_access {
277 unsigned int method;
278 int writeable;
279 int buscentric;
280 char* id_file_name;
281 int free_id_name;
282 int numeric_ids;
283 unsigned int id_lookup_mode;
284 int debugging;
285 void* error;
286 void* warning;
287 void* debug;
288 pci_dev* devices;
289 } pci_access;
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) {
307 dlclose(libpci);
308 record_warning("libpci missing methods");
309 return 0;
312 pci_access* pacc = pci_alloc();
313 if (!pacc) {
314 dlclose(libpci);
315 record_warning("libpci alloc failed");
316 return 0;
319 pci_init(pacc);
320 pci_scan_bus(pacc);
322 int count = 0;
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 &&
326 dev->device_id) {
327 ++count;
328 record_value("PCI_VENDOR_ID\n0x%04x\nPCI_DEVICE_ID\n0x%04x\n",
329 dev->vendor_id, dev->device_id);
333 pci_cleanup(pacc);
334 dlclose(libpci);
335 return count;
338 #ifdef MOZ_WAYLAND
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))) {
342 continue;
344 if (strcmp(device->nodes[i], name) == 0) {
345 return true;
348 return false;
351 static void get_render_name(const char* name) {
352 void* libdrm = dlopen(LIBDRM_FILENAME, RTLD_LAZY);
353 if (!libdrm) {
354 record_warning("Failed to open libdrm");
355 return;
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) {
367 record_warning(
368 "libdrm missing methods for drmGetDevices2 or drmFreeDevice");
369 dlclose(libdrm);
370 return;
373 uint32_t flags = 0;
374 int devices_len = drmGetDevices2(flags, nullptr, 0);
375 if (devices_len < 0) {
376 record_warning("drmGetDevices2 failed");
377 dlclose(libdrm);
378 return;
380 drmDevice** devices = (drmDevice**)calloc(devices_len, sizeof(drmDevice*));
381 if (!devices) {
382 record_warning("Allocation error");
383 dlclose(libdrm);
384 return;
386 devices_len = drmGetDevices2(flags, devices, devices_len);
387 if (devices_len < 0) {
388 free(devices);
389 record_warning("drmGetDevices2 failed");
390 dlclose(libdrm);
391 return;
394 const drmDevice* match = nullptr;
395 for (int i = 0; i < devices_len; i++) {
396 if (device_has_name(devices[i], name)) {
397 match = devices[i];
398 break;
402 if (!match) {
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");
406 } else {
407 record_value("DRM_RENDERDEVICE\n%s\n", match->nodes[DRM_NODE_RENDER]);
408 record_value(
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]);
417 free(devices);
419 dlclose(libdrm);
421 #endif
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,
447 EGLint name);
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");
460 return false;
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};
470 EGLConfig config;
471 EGLint num_config;
472 if (eglChooseConfig(dpy, config_attrs, &config, 1, &num_config) ==
473 EGL_FALSE) {
474 record_warning("eglChooseConfig returned an error");
475 return false;
478 if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
479 record_warning("eglBindAPI returned an error");
480 return false;
483 EGLint ctx_attrs[] = {EGL_NONE};
484 EGLContext ectx = eglCreateContext(dpy, config, EGL_NO_CONTEXT, ctx_attrs);
485 if (!ectx) {
486 record_warning("eglCreateContext returned an error");
487 return false;
490 if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ectx) == EGL_FALSE) {
491 record_warning("eglMakeCurrent returned an error");
492 return false;
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;
499 if (!glGetString) {
500 libgl = dlopen(LIBGL_FILENAME, RTLD_LAZY);
501 if (!libgl) {
502 libgl = dlopen(LIBGLES_FILENAME, RTLD_LAZY);
503 if (!libgl) {
504 record_warning(LIBGL_FILENAME " and " LIBGLES_FILENAME " missing");
505 return false;
509 glGetString = cast<PFNGLGETSTRING>(dlsym(libgl, "glGetString"));
510 if (!glGetString) {
511 dlclose(libgl);
512 record_warning("libEGL, libGL and libGLESv2 are missing glGetString");
513 return false;
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);
524 } else {
525 record_warning("EGL glGetString returned null");
526 if (libgl) {
527 dlclose(libgl);
529 return false;
532 if (eglQueryDeviceStringEXT) {
533 EGLDeviceEXT device = nullptr;
535 if (eglQueryDisplayAttribEXT(dpy, EGL_DEVICE_EXT, (EGLAttrib*)&device) ==
536 EGL_TRUE) {
537 const char* deviceString =
538 eglQueryDeviceStringEXT(device, EGL_DRM_DEVICE_FILE_EXT);
539 if (deviceString) {
540 record_value("MESA_ACCELERATED\nTRUE\n");
542 #ifdef MOZ_WAYLAND
543 get_render_name(deviceString);
544 #endif
545 } else {
546 record_value("MESA_ACCELERATED\nFALSE\n");
551 if (libgl) {
552 dlclose(libgl);
554 return true;
557 static bool get_egl_status(EGLNativeDisplayType native_dpy, bool gles_test,
558 bool require_driver) {
559 void* libegl = dlopen(LIBEGL_FILENAME, RTLD_LAZY);
560 if (!libegl) {
561 record_warning("libEGL missing");
562 return false;
565 PFNEGLGETPROCADDRESS eglGetProcAddress =
566 cast<PFNEGLGETPROCADDRESS>(dlsym(libegl, "eglGetProcAddress"));
568 if (!eglGetProcAddress) {
569 dlclose(libegl);
570 record_warning("no eglGetProcAddress");
571 return false;
574 typedef EGLDisplay (*PFNEGLGETDISPLAYPROC)(void* native_display);
575 PFNEGLGETDISPLAYPROC eglGetDisplay =
576 cast<PFNEGLGETDISPLAYPROC>(eglGetProcAddress("eglGetDisplay"));
578 typedef EGLBoolean (*PFNEGLINITIALIZEPROC)(EGLDisplay dpy, EGLint * major,
579 EGLint * minor);
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) {
588 dlclose(libegl);
589 record_warning("libEGL missing methods");
590 return false;
593 EGLDisplay dpy = eglGetDisplay(native_dpy);
594 if (!dpy) {
595 dlclose(libegl);
596 record_warning("libEGL no display");
597 return false;
600 EGLint major, minor;
601 if (!eglInitialize(dpy, &major, &minor)) {
602 dlclose(libegl);
603 record_warning("libEGL initialize failed");
604 return false;
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
615 // the device.
616 const char* driDriver = eglGetDisplayDriverName(dpy);
617 if (driDriver) {
618 record_value("DRI_DRIVER\n%s\n", driDriver);
620 } else if (require_driver) {
621 record_warning("libEGL missing eglGetDisplayDriverName");
622 eglTerminate(dpy);
623 dlclose(libegl);
624 return false;
627 if (gles_test && !get_gles_status(dpy, eglGetProcAddress)) {
628 eglTerminate(dpy);
629 dlclose(libegl);
630 return false;
633 eglTerminate(dpy);
634 dlclose(libegl);
635 return true;
638 #ifdef MOZ_X11
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);
658 if (!libgl) {
659 record_error(LIBGL_FILENAME " missing");
660 return;
663 typedef void* (*PFNGLXGETPROCADDRESS)(const char*);
664 PFNGLXGETPROCADDRESS glXGetProcAddress =
665 cast<PFNGLXGETPROCADDRESS>(dlsym(libgl, "glXGetProcAddress"));
667 if (!glXGetProcAddress) {
668 record_error("no glXGetProcAddress");
669 return;
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,
685 Bool);
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 ||
703 !glGetString) {
704 record_error(LIBGL_FILENAME " missing methods");
705 return;
708 ///// Open a connection to the X server /////
709 Display* dpy = XOpenDisplay(nullptr);
710 if (!dpy) {
711 record_error("Unable to open a connection to the X server");
712 return;
715 ///// Check that the GLX extension is present /////
716 if (!glXQueryExtension(dpy, nullptr, nullptr)) {
717 record_error("GLX extension missing");
718 return;
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);
727 if (!vInfo) {
728 int attribs2[] = {GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE,
729 1, GLX_BLUE_SIZE, 1, GLX_DOUBLEBUFFER,
730 None};
731 vInfo = glXChooseVisual(dpy, DefaultScreen(dpy), attribs2);
732 if (!vInfo) {
733 record_error("No visuals found");
734 return;
738 // using a X11 Window instead of a GLXPixmap does not crash
739 // fglrx in indirect rendering. bug 680644
740 Window window;
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");
766 *gotGlxInfo = 1;
767 } else {
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,
781 &vendorId);
782 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_DEVICE_ID_MESA,
783 &deviceId);
784 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_ACCELERATED_MESA,
785 &accelerated);
786 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_VIDEO_MEMORY_MESA,
787 &videoMemoryMB);
789 // Truncate IDs to 4 digits- that's all PCI IDs are.
790 vendorId &= 0xFFFF;
791 deviceId &= 0xFFFF;
793 record_value(
794 "MESA_VENDOR_ID\n0x%04x\n"
795 "MESA_DEVICE_ID\n0x%04x\n"
796 "MESA_ACCELERATED\n%s\n"
797 "MESA_VRAM\n%dMB\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));
807 if (driDriver) {
808 *gotDriDriver = 1;
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
829 XCloseDisplay(dpy);
830 # else
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
834 // 973192
835 XSync(dpy, False);
836 # endif
838 dlclose(libgl);
841 static bool x11_egltest(int pci_count) {
842 Display* dpy = XOpenDisplay(nullptr);
843 if (!dpy) {
844 return false;
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)) {
851 return false;
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");
861 return true;
864 static void glxtest() {
865 int gotGlxInfo = 0;
866 int gotDriDriver = 0;
868 get_glx_status(&gotGlxInfo, &gotDriDriver);
869 if (!gotGlxInfo) {
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");
879 #endif
881 #ifdef MOZ_WAYLAND
882 typedef void (*print_info_t)(void* info);
883 typedef void (*destroy_info_t)(void* info);
885 struct global_info {
886 struct wl_list link;
888 uint32_t id;
889 uint32_t version;
890 char* interface;
892 print_info_t print;
893 destroy_info_t destroy;
896 struct output_info {
897 struct global_info global;
898 struct wl_list global_link;
900 struct wl_output* output;
902 int32_t version;
904 int32_t scale;
907 struct xdg_output_v1_info {
908 struct wl_list link;
910 struct zxdg_output_v1* xdg_output;
911 struct output_info* output;
913 struct {
914 int32_t width, height;
915 } logical;
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;
926 struct weston_info {
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) {
940 global->id = id;
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);
952 free(info);
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));
970 int pos = 0;
971 wl_list_for_each(output, &info->outputs, link) {
972 infos[pos] = *output;
973 pos++;
976 if (screen_count > 1) {
977 qsort(infos, screen_count, sizeof(struct xdg_output_v1_info),
978 cmpOutputIds);
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);
985 record_value("\n");
987 free(infos);
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,
1043 xdg_output);
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,
1083 int32_t refresh) {}
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,
1088 int32_t scale) {
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,
1096 output_handle_mode,
1097 output_handle_done,
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,
1108 uint32_t version) {
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);
1116 output->scale = 1;
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,
1132 uint32_t version) {
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,
1143 uint32_t name) {}
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);
1160 free(data);
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};
1170 info.display = dpy;
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, &registry_listener, &info);
1179 do {
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);
1194 if (!dpy) {
1195 record_error("Could not connect to wayland socket");
1196 return;
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");
1207 #endif
1209 int childgltest() {
1210 enum { bufsize = 2048 };
1211 char buf[bufsize];
1213 // We save it as a global so that the X error handler can flush the buffer
1214 // before early exiting.
1215 glxtest_buf = buf;
1216 glxtest_bufsize = bufsize;
1218 // Get a list of all GPUs from the PCI bus.
1219 int pci_count = get_pci_status();
1221 #ifdef MOZ_WAYLAND
1222 if (IsWaylandEnabled()) {
1223 wayland_egltest();
1224 } else
1225 #endif
1227 #ifdef MOZ_X11
1228 // TODO: --display command line argument is not properly handled
1229 if (!x11_egltest(pci_count)) {
1230 glxtest();
1232 #endif
1235 // Finally write buffered data to the pipe.
1236 record_flush();
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;
1246 } // extern "C"
1248 /** \returns true in the child glxtest process, false in the parent process */
1249 bool fire_glxtest_process() {
1250 int pfd[2];
1251 if (pipe(pfd) == -1) {
1252 perror("pipe");
1253 return false;
1255 pid_t pid = fork();
1256 if (pid < 0) {
1257 perror("fork");
1258 close(pfd[0]);
1259 close(pfd[1]);
1260 return false;
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).
1264 if (pid == 0) {
1265 close(pfd[0]);
1266 write_end_of_the_pipe = pfd[1];
1267 close_logging();
1268 int rv = childgltest();
1269 close(pfd[1]);
1270 _exit(rv);
1273 close(pfd[1]);
1274 mozilla::widget::glxtest_pipe = pfd[0];
1275 mozilla::widget::glxtest_pid = pid;
1276 return false;