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/. */
7
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.
13 //
14 // This file implements the idea to do that in a separate process.
15 //
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.
21
22 #include <cstdio>
23 #include <cstdlib>
24 #include <dlfcn.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27
28 #include "mozilla/Unused.h"
29 #include "nsAppRunner.h" // for IsWaylandEnabled on IsX11EGLEnabled
30 #include "stdint.h"
31
32 #ifdef __SUNPRO_CC
33 # include <stdio.h>
34 #endif
35
36 #ifdef MOZ_X11
37 # include "X11/Xlib.h"
38 # include "X11/Xutil.h"
39 #endif
40
41 #ifdef MOZ_WAYLAND
42 # include "mozilla/widget/mozwayland.h"
43 # include "mozilla/widget/xdg-output-unstable-v1-client-protocol.h"
44 #endif
45
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
63 // xrandr.h
64 typedef XID RRMode;
65 typedef XID RRProvider;
66 typedef unsigned long XRRModeFlags;
67 typedef struct _provider provider_t;
68
69 typedef struct _XRRModeInfo {
70 RRMode id;
71 unsigned int width;
72 unsigned int height;
73 unsigned long dotClock;
74 unsigned int hSyncStart;
75 unsigned int hSyncEnd;
76 unsigned int hTotal;
77 unsigned int hSkew;
78 unsigned int vSyncStart;
79 unsigned int vSyncEnd;
80 unsigned int vTotal;
81 char* name;
82 unsigned int nameLength;
83 XRRModeFlags modeFlags;
84 } XRRModeInfo;
85
86 typedef struct _XRRScreenResources {
87 Time timestamp;
88 Time configTimestamp;
89 int ncrtc;
90 void* crtcs;
91 int noutput;
92 void* outputs;
93 int nmode;
94 XRRModeInfo* modes;
95 } XRRScreenResources;
96
97 typedef struct _XRRProviderInfo {
98 unsigned int capabilities;
99 int ncrtcs;
100 void* crtcs;
101 int noutputs;
102 void* outputs;
103 char* name;
104 int nassociatedproviders;
105 RRProvider* associated_providers;
106 unsigned int* associated_capability;
107 int nameLen;
108 } XRRProviderInfo;
109
110 typedef struct {
111 unsigned int kind;
112 char* string;
113 XID xid;
114 int index;
115 } name_t;
116
117 struct _provider {
118 name_t provider;
119 XRRProviderInfo* info;
120 };
121
122 typedef struct _XRRProviderResources {
123 Time timestamp;
124 int nproviders;
125 RRProvider* providers;
126 } XRRProviderResources;
127 #endif
128
129 // stuff from gl.h
130 typedef uint8_t GLubyte;
131 typedef uint32_t GLenum;
132 #define GL_VENDOR 0x1F00
133 #define GL_RENDERER 0x1F01
134 #define GL_VERSION 0x1F02
135
136 // GLX_MESA_query_renderer
137 // clang-format off
138 #define GLX_RENDERER_VENDOR_ID_MESA 0x8183
139 #define GLX_RENDERER_DEVICE_ID_MESA 0x8184
140 #define GLX_RENDERER_VERSION_MESA 0x8185
141 #define GLX_RENDERER_ACCELERATED_MESA 0x8186
142 #define GLX_RENDERER_VIDEO_MEMORY_MESA 0x8187
143 #define GLX_RENDERER_UNIFIED_MEMORY_ARCHITECTURE_MESA 0x8188
144 #define GLX_RENDERER_PREFERRED_PROFILE_MESA 0x8189
145 #define GLX_RENDERER_OPENGL_CORE_PROFILE_VERSION_MESA 0x818A
146 #define GLX_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_MESA 0x818B
147 #define GLX_RENDERER_OPENGL_ES_PROFILE_VERSION_MESA 0x818C
148 #define GLX_RENDERER_OPENGL_ES2_PROFILE_VERSION_MESA 0x818D
149 #define GLX_RENDERER_ID_MESA 0x818E
150 // clang-format on
151
152 // stuff from egl.h
153 typedef intptr_t EGLAttrib;
154 typedef int EGLBoolean;
155 typedef void* EGLConfig;
156 typedef void* EGLContext;
157 typedef void* EGLDeviceEXT;
158 typedef void* EGLDisplay;
159 typedef int EGLint;
160 typedef void* EGLNativeDisplayType;
161 typedef void* EGLSurface;
162 typedef void* (*PFNEGLGETPROCADDRESS)(const char*);
163
164 #define EGL_NO_CONTEXT nullptr
165 #define EGL_NO_SURFACE nullptr
166 #define EGL_FALSE 0
167 #define EGL_TRUE 1
168 #define EGL_BLUE_SIZE 0x3022
169 #define EGL_GREEN_SIZE 0x3023
170 #define EGL_RED_SIZE 0x3024
171 #define EGL_NONE 0x3038
172 #define EGL_VENDOR 0x3053
173 #define EGL_CONTEXT_CLIENT_VERSION 0x3098
174 #define EGL_OPENGL_API 0x30A2
175 #define EGL_DEVICE_EXT 0x322C
176 #define EGL_DRM_DEVICE_FILE_EXT 0x3233
177
178 // stuff from xf86drm.h
179 #define DRM_NODE_RENDER 2
180 #define DRM_NODE_MAX 3
181
182 typedef struct _drmPciDeviceInfo {
183 uint16_t vendor_id;
184 uint16_t device_id;
185 uint16_t subvendor_id;
186 uint16_t subdevice_id;
187 uint8_t revision_id;
188 } drmPciDeviceInfo, *drmPciDeviceInfoPtr;
189
190 typedef struct _drmDevice {
191 char** nodes;
192 int available_nodes;
193 int bustype;
194 union {
195 void* pci;
196 void* usb;
197 void* platform;
198 void* host1x;
199 } businfo;
200 union {
201 drmPciDeviceInfoPtr pci;
202 void* usb;
203 void* platform;
204 void* host1x;
205 } deviceinfo;
206 } drmDevice, *drmDevicePtr;
207
208 // Open libGL and load needed symbols
209 #if defined(__OpenBSD__) || defined(__NetBSD__)
210 # define LIBGL_FILENAME "libGL.so"
211 # define LIBGLES_FILENAME "libGLESv2.so"
212 # define LIBEGL_FILENAME "libEGL.so"
213 # define LIBDRM_FILENAME "libdrm.so"
214 # define LIBXRANDR_FILENAME "libXrandr.so"
215 #else
216 # define LIBGL_FILENAME "libGL.so.1"
217 # define LIBGLES_FILENAME "libGLESv2.so.2"
218 # define LIBEGL_FILENAME "libEGL.so.1"
219 # define LIBDRM_FILENAME "libdrm.so.2"
220 # define LIBXRANDR_FILENAME "libXrandr.so.2"
221 #endif
222
223 #define EXIT_FAILURE_BUFFER_TOO_SMALL 2
224
225 namespace mozilla {
226 namespace widget {
227 // the read end of the pipe, which will be used by GfxInfo
228 extern int glxtest_pipe;
229 // the PID of the glxtest process, to pass to waitpid()
230 extern pid_t glxtest_pid;
231 } // namespace widget
232 } // namespace mozilla
233
234 // the write end of the pipe, which we're going to write to
235 static int write_end_of_the_pipe = -1;
236
237 // our buffer, size and used length
238 static char* glxtest_buf = nullptr;
239 static int glxtest_bufsize = 0;
240 static int glxtest_length = 0;
241
242 // C++ standard collides with C standard in that it doesn't allow casting void*
243 // to function pointer types. So the work-around is to convert first to size_t.
244 // http://www.trilithium.com/johan/2004/12/problem-with-dlsym/
245 template <typename func_ptr_type>
cast(void * ptr)246 static func_ptr_type cast(void* ptr) {
247 return reinterpret_cast<func_ptr_type>(reinterpret_cast<size_t>(ptr));
248 }
249
record_value(const char * format,...)250 static void record_value(const char* format, ...) {
251 // Don't add more if the buffer is full.
252 if (glxtest_bufsize <= glxtest_length) {
253 return;
254 }
255
256 // Append the new values to the buffer, not to exceed the remaining space.
257 int remaining = glxtest_bufsize - glxtest_length;
258 va_list args;
259 va_start(args, format);
260 int max_added =
261 vsnprintf(glxtest_buf + glxtest_length, remaining, format, args);
262 va_end(args);
263
264 // snprintf returns how many char it could have added, not how many it added.
265 // It is important to get this right since it will control how many chars we
266 // will attempt to write to the pipe fd.
267 if (max_added > remaining) {
268 glxtest_length += remaining;
269 } else {
270 glxtest_length += max_added;
271 }
272 }
273
record_error(const char * str)274 static void record_error(const char* str) { record_value("ERROR\n%s\n", str); }
275
record_warning(const char * str)276 static void record_warning(const char* str) {
277 record_value("WARNING\n%s\n", str);
278 }
279
record_flush()280 static void record_flush() {
281 mozilla::Unused << write(write_end_of_the_pipe, glxtest_buf, glxtest_length);
282 }
283
284 #ifdef MOZ_X11
x_error_handler(Display *,XErrorEvent * ev)285 static int x_error_handler(Display*, XErrorEvent* ev) {
286 record_value(
287 "ERROR\nX error, error_code=%d, "
288 "request_code=%d, minor_code=%d\n",
289 ev->error_code, ev->request_code, ev->minor_code);
290 record_flush();
291 _exit(EXIT_FAILURE);
292 return 0;
293 }
294 #endif
295
296 // childgltest is declared inside extern "C" so that the name is not mangled.
297 // The name is used in build/valgrind/x86_64-pc-linux-gnu.sup to suppress
298 // memory leak errors because we run it inside a short lived fork and we don't
299 // care about leaking memory
300 extern "C" {
301
close_logging()302 static void close_logging() {
303 // we want to redirect to /dev/null stdout, stderr, and while we're at it,
304 // any PR logging file descriptors. To that effect, we redirect all positive
305 // file descriptors up to what open() returns here. In particular, 1 is stdout
306 // and 2 is stderr.
307 int fd = open("/dev/null", O_WRONLY);
308 for (int i = 1; i < fd; i++) {
309 dup2(fd, i);
310 }
311 close(fd);
312
313 if (getenv("MOZ_AVOID_OPENGL_ALTOGETHER")) {
314 const char* msg = "ERROR\nMOZ_AVOID_OPENGL_ALTOGETHER envvar set";
315 mozilla::Unused << write(write_end_of_the_pipe, msg, strlen(msg));
316 exit(EXIT_FAILURE);
317 }
318 }
319
320 #define PCI_FILL_IDENT 0x0001
321 #define PCI_FILL_CLASS 0x0020
322 #define PCI_BASE_CLASS_DISPLAY 0x03
323
get_pci_status()324 static int get_pci_status() {
325 void* libpci = dlopen("libpci.so.3", RTLD_LAZY);
326 if (!libpci) {
327 libpci = dlopen("libpci.so", RTLD_LAZY);
328 }
329 if (!libpci) {
330 record_warning("libpci missing");
331 return 0;
332 }
333
334 typedef struct pci_dev {
335 struct pci_dev* next;
336 uint16_t domain_16;
337 uint8_t bus, dev, func;
338 unsigned int known_fields;
339 uint16_t vendor_id, device_id;
340 uint16_t device_class;
341 } pci_dev;
342
343 typedef struct pci_access {
344 unsigned int method;
345 int writeable;
346 int buscentric;
347 char* id_file_name;
348 int free_id_name;
349 int numeric_ids;
350 unsigned int id_lookup_mode;
351 int debugging;
352 void* error;
353 void* warning;
354 void* debug;
355 pci_dev* devices;
356 } pci_access;
357
358 typedef pci_access* (*PCIALLOC)(void);
359 PCIALLOC pci_alloc = cast<PCIALLOC>(dlsym(libpci, "pci_alloc"));
360
361 typedef void (*PCIINIT)(pci_access*);
362 PCIINIT pci_init = cast<PCIINIT>(dlsym(libpci, "pci_init"));
363
364 typedef void (*PCICLEANUP)(pci_access*);
365 PCICLEANUP pci_cleanup = cast<PCICLEANUP>(dlsym(libpci, "pci_cleanup"));
366
367 typedef void (*PCISCANBUS)(pci_access*);
368 PCISCANBUS pci_scan_bus = cast<PCISCANBUS>(dlsym(libpci, "pci_scan_bus"));
369
370 typedef void (*PCIFILLINFO)(pci_dev*, int);
371 PCIFILLINFO pci_fill_info = cast<PCIFILLINFO>(dlsym(libpci, "pci_fill_info"));
372
373 if (!pci_alloc || !pci_cleanup || !pci_scan_bus || !pci_fill_info) {
374 dlclose(libpci);
375 record_warning("libpci missing methods");
376 return 0;
377 }
378
379 pci_access* pacc = pci_alloc();
380 if (!pacc) {
381 dlclose(libpci);
382 record_warning("libpci alloc failed");
383 return 0;
384 }
385
386 pci_init(pacc);
387 pci_scan_bus(pacc);
388
389 int count = 0;
390 for (pci_dev* dev = pacc->devices; dev; dev = dev->next) {
391 pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_CLASS);
392 if (dev->device_class >> 8 == PCI_BASE_CLASS_DISPLAY && dev->vendor_id &&
393 dev->device_id) {
394 ++count;
395 record_value("PCI_VENDOR_ID\n0x%04x\nPCI_DEVICE_ID\n0x%04x\n",
396 dev->vendor_id, dev->device_id);
397 }
398 }
399
400 pci_cleanup(pacc);
401 dlclose(libpci);
402 return count;
403 }
404
405 #ifdef MOZ_WAYLAND
device_has_name(const drmDevice * device,const char * name)406 static bool device_has_name(const drmDevice* device, const char* name) {
407 for (size_t i = 0; i < DRM_NODE_MAX; i++) {
408 if (!(device->available_nodes & (1 << i))) {
409 continue;
410 }
411 if (strcmp(device->nodes[i], name) == 0) {
412 return true;
413 }
414 }
415 return false;
416 }
417
get_render_name(const char * name)418 static void get_render_name(const char* name) {
419 void* libdrm = dlopen(LIBDRM_FILENAME, RTLD_LAZY);
420 if (!libdrm) {
421 record_warning("Failed to open libdrm");
422 return;
423 }
424
425 typedef int (*DRMGETDEVICES2)(uint32_t, drmDevicePtr*, int);
426 DRMGETDEVICES2 drmGetDevices2 =
427 cast<DRMGETDEVICES2>(dlsym(libdrm, "drmGetDevices2"));
428
429 typedef void (*DRMFREEDEVICE)(drmDevicePtr*);
430 DRMFREEDEVICE drmFreeDevice =
431 cast<DRMFREEDEVICE>(dlsym(libdrm, "drmFreeDevice"));
432
433 if (!drmGetDevices2 || !drmFreeDevice) {
434 record_warning(
435 "libdrm missing methods for drmGetDevices2 or drmFreeDevice");
436 dlclose(libdrm);
437 return;
438 }
439
440 uint32_t flags = 0;
441 int devices_len = drmGetDevices2(flags, nullptr, 0);
442 if (devices_len < 0) {
443 record_warning("drmGetDevices2 failed");
444 dlclose(libdrm);
445 return;
446 }
447 drmDevice** devices = (drmDevice**)calloc(devices_len, sizeof(drmDevice*));
448 if (!devices) {
449 record_warning("Allocation error");
450 dlclose(libdrm);
451 return;
452 }
453 devices_len = drmGetDevices2(flags, devices, devices_len);
454 if (devices_len < 0) {
455 free(devices);
456 record_warning("drmGetDevices2 failed");
457 dlclose(libdrm);
458 return;
459 }
460
461 const drmDevice* match = nullptr;
462 for (int i = 0; i < devices_len; i++) {
463 if (device_has_name(devices[i], name)) {
464 match = devices[i];
465 break;
466 }
467 }
468
469 if (!match) {
470 record_warning("Cannot find DRM device");
471 } else if (!(match->available_nodes & (1 << DRM_NODE_RENDER))) {
472 record_warning("DRM device has no render node");
473 } else {
474 record_value("DRM_RENDERDEVICE\n%s\n", match->nodes[DRM_NODE_RENDER]);
475 record_value(
476 "MESA_VENDOR_ID\n0x%04x\n"
477 "MESA_DEVICE_ID\n0x%04x\n",
478 match->deviceinfo.pci->vendor_id, match->deviceinfo.pci->device_id);
479 }
480
481 for (int i = 0; i < devices_len; i++) {
482 drmFreeDevice(&devices[i]);
483 }
484 free(devices);
485
486 dlclose(libdrm);
487 }
488 #endif
489
get_gles_status(EGLDisplay dpy,PFNEGLGETPROCADDRESS eglGetProcAddress)490 static bool get_gles_status(EGLDisplay dpy,
491 PFNEGLGETPROCADDRESS eglGetProcAddress) {
492 typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC)(
493 EGLDisplay dpy, EGLint const* attrib_list, EGLConfig* configs,
494 EGLint config_size, EGLint* num_config);
495 PFNEGLCHOOSECONFIGPROC eglChooseConfig =
496 cast<PFNEGLCHOOSECONFIGPROC>(eglGetProcAddress("eglChooseConfig"));
497
498 typedef EGLBoolean (*PFNEGLBINDAPIPROC)(EGLint api);
499 PFNEGLBINDAPIPROC eglBindAPI =
500 cast<PFNEGLBINDAPIPROC>(eglGetProcAddress("eglBindAPI"));
501
502 typedef EGLContext (*PFNEGLCREATECONTEXTPROC)(
503 EGLDisplay dpy, EGLConfig config, EGLContext share_context,
504 EGLint const* attrib_list);
505 PFNEGLCREATECONTEXTPROC eglCreateContext =
506 cast<PFNEGLCREATECONTEXTPROC>(eglGetProcAddress("eglCreateContext"));
507
508 typedef EGLBoolean (*PFNEGLMAKECURRENTPROC)(
509 EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext context);
510 PFNEGLMAKECURRENTPROC eglMakeCurrent =
511 cast<PFNEGLMAKECURRENTPROC>(eglGetProcAddress("eglMakeCurrent"));
512
513 typedef const char* (*PFNEGLQUERYDEVICESTRINGEXTPROC)(EGLDeviceEXT device,
514 EGLint name);
515 PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT =
516 cast<PFNEGLQUERYDEVICESTRINGEXTPROC>(
517 eglGetProcAddress("eglQueryDeviceStringEXT"));
518
519 typedef EGLBoolean (*PFNEGLQUERYDISPLAYATTRIBEXTPROC)(
520 EGLDisplay dpy, EGLint name, EGLAttrib * value);
521 PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT =
522 cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>(
523 eglGetProcAddress("eglQueryDisplayAttribEXT"));
524
525 if (!eglChooseConfig || !eglCreateContext || !eglMakeCurrent) {
526 record_warning("libEGL missing methods for GL test");
527 return false;
528 }
529
530 typedef GLubyte* (*PFNGLGETSTRING)(GLenum);
531 PFNGLGETSTRING glGetString =
532 cast<PFNGLGETSTRING>(eglGetProcAddress("glGetString"));
533
534 EGLint config_attrs[] = {EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8,
535 EGL_BLUE_SIZE, 8, EGL_NONE};
536
537 EGLConfig config;
538 EGLint num_config;
539 if (eglChooseConfig(dpy, config_attrs, &config, 1, &num_config) ==
540 EGL_FALSE) {
541 record_warning("eglChooseConfig returned an error");
542 return false;
543 }
544
545 if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
546 record_warning("eglBindAPI returned an error");
547 return false;
548 }
549
550 EGLint ctx_attrs[] = {EGL_NONE};
551 EGLContext ectx = eglCreateContext(dpy, config, EGL_NO_CONTEXT, ctx_attrs);
552 if (!ectx) {
553 record_warning("eglCreateContext returned an error");
554 return false;
555 }
556
557 if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ectx) == EGL_FALSE) {
558 record_warning("eglMakeCurrent returned an error");
559 return false;
560 }
561
562 // Implementations disagree about whether eglGetProcAddress or dlsym
563 // should be used for getting functions from the actual API, see
564 // https://github.com/anholt/libepoxy/commit/14f24485e33816139398d1bd170d617703473738
565 void* libgl = nullptr;
566 if (!glGetString) {
567 libgl = dlopen(LIBGL_FILENAME, RTLD_LAZY);
568 if (!libgl) {
569 libgl = dlopen(LIBGLES_FILENAME, RTLD_LAZY);
570 if (!libgl) {
571 record_warning(LIBGL_FILENAME " and " LIBGLES_FILENAME " missing");
572 return false;
573 }
574 }
575
576 glGetString = cast<PFNGLGETSTRING>(dlsym(libgl, "glGetString"));
577 if (!glGetString) {
578 dlclose(libgl);
579 record_warning("libEGL, libGL and libGLESv2 are missing glGetString");
580 return false;
581 }
582 }
583
584 const GLubyte* versionString = glGetString(GL_VERSION);
585 const GLubyte* vendorString = glGetString(GL_VENDOR);
586 const GLubyte* rendererString = glGetString(GL_RENDERER);
587
588 if (versionString && vendorString && rendererString) {
589 record_value("VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\nTRUE\n",
590 vendorString, rendererString, versionString);
591 } else {
592 record_warning("EGL glGetString returned null");
593 if (libgl) {
594 dlclose(libgl);
595 }
596 return false;
597 }
598
599 if (eglQueryDeviceStringEXT) {
600 EGLDeviceEXT device = nullptr;
601
602 if (eglQueryDisplayAttribEXT(dpy, EGL_DEVICE_EXT, (EGLAttrib*)&device) ==
603 EGL_TRUE) {
604 const char* deviceString =
605 eglQueryDeviceStringEXT(device, EGL_DRM_DEVICE_FILE_EXT);
606 if (deviceString) {
607 record_value("MESA_ACCELERATED\nTRUE\n");
608
609 #ifdef MOZ_WAYLAND
610 get_render_name(deviceString);
611 #endif
612 } else {
613 record_value("MESA_ACCELERATED\nFALSE\n");
614 }
615 }
616 }
617
618 if (libgl) {
619 dlclose(libgl);
620 }
621 return true;
622 }
623
get_egl_status(EGLNativeDisplayType native_dpy,bool gles_test,bool require_driver)624 static bool get_egl_status(EGLNativeDisplayType native_dpy, bool gles_test,
625 bool require_driver) {
626 void* libegl = dlopen(LIBEGL_FILENAME, RTLD_LAZY);
627 if (!libegl) {
628 record_warning("libEGL missing");
629 return false;
630 }
631
632 PFNEGLGETPROCADDRESS eglGetProcAddress =
633 cast<PFNEGLGETPROCADDRESS>(dlsym(libegl, "eglGetProcAddress"));
634
635 if (!eglGetProcAddress) {
636 dlclose(libegl);
637 record_warning("no eglGetProcAddress");
638 return false;
639 }
640
641 typedef EGLDisplay (*PFNEGLGETDISPLAYPROC)(void* native_display);
642 PFNEGLGETDISPLAYPROC eglGetDisplay =
643 cast<PFNEGLGETDISPLAYPROC>(eglGetProcAddress("eglGetDisplay"));
644
645 typedef EGLBoolean (*PFNEGLINITIALIZEPROC)(EGLDisplay dpy, EGLint * major,
646 EGLint * minor);
647 PFNEGLINITIALIZEPROC eglInitialize =
648 cast<PFNEGLINITIALIZEPROC>(eglGetProcAddress("eglInitialize"));
649
650 typedef EGLBoolean (*PFNEGLTERMINATEPROC)(EGLDisplay dpy);
651 PFNEGLTERMINATEPROC eglTerminate =
652 cast<PFNEGLTERMINATEPROC>(eglGetProcAddress("eglTerminate"));
653
654 if (!eglGetDisplay || !eglInitialize || !eglTerminate) {
655 dlclose(libegl);
656 record_warning("libEGL missing methods");
657 return false;
658 }
659
660 EGLDisplay dpy = eglGetDisplay(native_dpy);
661 if (!dpy) {
662 dlclose(libegl);
663 record_warning("libEGL no display");
664 return false;
665 }
666
667 EGLint major, minor;
668 if (!eglInitialize(dpy, &major, &minor)) {
669 dlclose(libegl);
670 record_warning("libEGL initialize failed");
671 return false;
672 }
673
674 typedef const char* (*PFNEGLGETDISPLAYDRIVERNAMEPROC)(EGLDisplay dpy);
675 PFNEGLGETDISPLAYDRIVERNAMEPROC eglGetDisplayDriverName =
676 cast<PFNEGLGETDISPLAYDRIVERNAMEPROC>(
677 eglGetProcAddress("eglGetDisplayDriverName"));
678 if (eglGetDisplayDriverName) {
679 // TODO(aosmond): If the driver name is empty, we probably aren't using Mesa
680 // and instead a proprietary GL, most likely NVIDIA's. The PCI device list
681 // in combination with the vendor name is very likely sufficient to identify
682 // the device.
683 const char* driDriver = eglGetDisplayDriverName(dpy);
684 if (driDriver) {
685 record_value("DRI_DRIVER\n%s\n", driDriver);
686 }
687 } else if (require_driver) {
688 record_warning("libEGL missing eglGetDisplayDriverName");
689 eglTerminate(dpy);
690 dlclose(libegl);
691 return false;
692 }
693
694 if (gles_test && !get_gles_status(dpy, eglGetProcAddress)) {
695 eglTerminate(dpy);
696 dlclose(libegl);
697 return false;
698 }
699
700 eglTerminate(dpy);
701 dlclose(libegl);
702 return true;
703 }
704
705 #ifdef MOZ_X11
get_x11_screen_info(Display * dpy)706 static void get_x11_screen_info(Display* dpy) {
707 int screenCount = ScreenCount(dpy);
708 int defaultScreen = DefaultScreen(dpy);
709 if (screenCount != 0) {
710 record_value("SCREEN_INFO\n");
711 for (int idx = 0; idx < screenCount; idx++) {
712 Screen* scrn = ScreenOfDisplay(dpy, idx);
713 int current_height = scrn->height;
714 int current_width = scrn->width;
715
716 record_value("%dx%d:%d%s", current_width, current_height,
717 idx == defaultScreen ? 1 : 0,
718 idx == screenCount - 1 ? ";\n" : ";");
719 }
720 }
721 }
722
get_x11_ddx_info(Display * dpy)723 static void get_x11_ddx_info(Display* dpy) {
724 void* libXrandr = dlopen(LIBXRANDR_FILENAME, RTLD_LAZY);
725 if (!libXrandr) {
726 return;
727 }
728
729 typedef XRRProviderResources* (*XRRGETPROVIDERRESOURCES)(Display*, Window);
730 XRRGETPROVIDERRESOURCES XRRGetProviderResources =
731 cast<XRRGETPROVIDERRESOURCES>(
732 dlsym(libXrandr, "XRRGetProviderResources"));
733
734 typedef XRRScreenResources* (*XRRGETSCREENRESOURCESCURRENT)(Display*, Window);
735 XRRGETSCREENRESOURCESCURRENT XRRGetScreenResourcesCurrent =
736 cast<XRRGETSCREENRESOURCESCURRENT>(
737 dlsym(libXrandr, "XRRGetScreenResourcesCurrent"));
738
739 typedef XRRProviderInfo* (*XRRGETPROVIDERINFO)(Display*, XRRScreenResources*,
740 RRProvider);
741 XRRGETPROVIDERINFO XRRGetProviderInfo =
742 cast<XRRGETPROVIDERINFO>(dlsym(libXrandr, "XRRGetProviderInfo"));
743
744 typedef Bool (*XRRQUERYEXTENSION)(Display*, int*, int*);
745 XRRQUERYEXTENSION XRRQueryExtension =
746 cast<XRRQUERYEXTENSION>(dlsym(libXrandr, "XRRQueryExtension"));
747
748 typedef int (*XRRQUERYVERSION)(Display*, int*, int*);
749 XRRQUERYVERSION XRRQueryVersion =
750 cast<XRRQUERYVERSION>(dlsym(libXrandr, "XRRQueryVersion"));
751
752 if (!XRRGetProviderResources || !XRRGetScreenResourcesCurrent ||
753 !XRRGetProviderInfo || !XRRQueryExtension || !XRRQueryVersion) {
754 dlclose(libXrandr);
755 return;
756 }
757
758 // When running on remote X11 the xrandr version may be stuck on an ancient
759 // version. There are still setups using remote X11 out there, so make sure we
760 // don't crash.
761 int eventBase, errorBase, major, minor;
762 if (!XRRQueryExtension(dpy, &eventBase, &errorBase) ||
763 !XRRQueryVersion(dpy, &major, &minor) ||
764 !(major > 1 || (major == 1 && minor >= 4))) {
765 return;
766 }
767
768 Window root = RootWindow(dpy, DefaultScreen(dpy));
769 XRRProviderResources* pr = XRRGetProviderResources(dpy, root);
770 XRRScreenResources* res = XRRGetScreenResourcesCurrent(dpy, root);
771 int nProviders = pr->nproviders;
772
773 if (nProviders != 0) {
774 record_value("DDX_DRIVER\n");
775 for (int i = 0; i < nProviders; i++) {
776 XRRProviderInfo* info = XRRGetProviderInfo(dpy, res, pr->providers[i]);
777 record_value("%s%s", info->name, i == nProviders - 1 ? ";\n" : ";");
778 }
779 }
780
781 dlclose(libXrandr);
782 }
783
get_glx_status(int * gotGlxInfo,int * gotDriDriver)784 static void get_glx_status(int* gotGlxInfo, int* gotDriDriver) {
785 void* libgl = dlopen(LIBGL_FILENAME, RTLD_LAZY);
786 if (!libgl) {
787 record_error(LIBGL_FILENAME " missing");
788 return;
789 }
790
791 typedef void* (*PFNGLXGETPROCADDRESS)(const char*);
792 PFNGLXGETPROCADDRESS glXGetProcAddress =
793 cast<PFNGLXGETPROCADDRESS>(dlsym(libgl, "glXGetProcAddress"));
794
795 if (!glXGetProcAddress) {
796 record_error("no glXGetProcAddress");
797 return;
798 }
799
800 typedef GLXFBConfig* (*PFNGLXQUERYEXTENSION)(Display*, int*, int*);
801 PFNGLXQUERYEXTENSION glXQueryExtension =
802 cast<PFNGLXQUERYEXTENSION>(glXGetProcAddress("glXQueryExtension"));
803
804 typedef GLXFBConfig* (*PFNGLXQUERYVERSION)(Display*, int*, int*);
805 PFNGLXQUERYVERSION glXQueryVersion =
806 cast<PFNGLXQUERYVERSION>(dlsym(libgl, "glXQueryVersion"));
807
808 typedef XVisualInfo* (*PFNGLXCHOOSEVISUAL)(Display*, int, int*);
809 PFNGLXCHOOSEVISUAL glXChooseVisual =
810 cast<PFNGLXCHOOSEVISUAL>(glXGetProcAddress("glXChooseVisual"));
811
812 typedef GLXContext (*PFNGLXCREATECONTEXT)(Display*, XVisualInfo*, GLXContext,
813 Bool);
814 PFNGLXCREATECONTEXT glXCreateContext =
815 cast<PFNGLXCREATECONTEXT>(glXGetProcAddress("glXCreateContext"));
816
817 typedef Bool (*PFNGLXMAKECURRENT)(Display*, GLXDrawable, GLXContext);
818 PFNGLXMAKECURRENT glXMakeCurrent =
819 cast<PFNGLXMAKECURRENT>(glXGetProcAddress("glXMakeCurrent"));
820
821 typedef void (*PFNGLXDESTROYCONTEXT)(Display*, GLXContext);
822 PFNGLXDESTROYCONTEXT glXDestroyContext =
823 cast<PFNGLXDESTROYCONTEXT>(glXGetProcAddress("glXDestroyContext"));
824
825 typedef GLubyte* (*PFNGLGETSTRING)(GLenum);
826 PFNGLGETSTRING glGetString =
827 cast<PFNGLGETSTRING>(glXGetProcAddress("glGetString"));
828
829 if (!glXQueryExtension || !glXQueryVersion || !glXChooseVisual ||
830 !glXCreateContext || !glXMakeCurrent || !glXDestroyContext ||
831 !glGetString) {
832 record_error(LIBGL_FILENAME " missing methods");
833 return;
834 }
835
836 ///// Open a connection to the X server /////
837 Display* dpy = XOpenDisplay(nullptr);
838 if (!dpy) {
839 record_error("Unable to open a connection to the X server");
840 return;
841 }
842
843 ///// Check that the GLX extension is present /////
844 if (!glXQueryExtension(dpy, nullptr, nullptr)) {
845 record_error("GLX extension missing");
846 return;
847 }
848
849 XSetErrorHandler(x_error_handler);
850
851 ///// Get a visual /////
852 int attribs[] = {GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE,
853 1, GLX_BLUE_SIZE, 1, None};
854 XVisualInfo* vInfo = glXChooseVisual(dpy, DefaultScreen(dpy), attribs);
855 if (!vInfo) {
856 int attribs2[] = {GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE,
857 1, GLX_BLUE_SIZE, 1, GLX_DOUBLEBUFFER,
858 None};
859 vInfo = glXChooseVisual(dpy, DefaultScreen(dpy), attribs2);
860 if (!vInfo) {
861 record_error("No visuals found");
862 return;
863 }
864 }
865
866 // using a X11 Window instead of a GLXPixmap does not crash
867 // fglrx in indirect rendering. bug 680644
868 Window window;
869 XSetWindowAttributes swa;
870 swa.colormap = XCreateColormap(dpy, RootWindow(dpy, vInfo->screen),
871 vInfo->visual, AllocNone);
872
873 swa.border_pixel = 0;
874 window = XCreateWindow(dpy, RootWindow(dpy, vInfo->screen), 0, 0, 16, 16, 0,
875 vInfo->depth, InputOutput, vInfo->visual,
876 CWBorderPixel | CWColormap, &swa);
877
878 ///// Get a GL context and make it current //////
879 GLXContext context = glXCreateContext(dpy, vInfo, nullptr, True);
880 glXMakeCurrent(dpy, window, context);
881
882 ///// Look for this symbol to determine texture_from_pixmap support /////
883 void* glXBindTexImageEXT = glXGetProcAddress("glXBindTexImageEXT");
884
885 ///// Get GL vendor/renderer/versions strings /////
886 const GLubyte* versionString = glGetString(GL_VERSION);
887 const GLubyte* vendorString = glGetString(GL_VENDOR);
888 const GLubyte* rendererString = glGetString(GL_RENDERER);
889
890 if (versionString && vendorString && rendererString) {
891 record_value("VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\n%s\n",
892 vendorString, rendererString, versionString,
893 glXBindTexImageEXT ? "TRUE" : "FALSE");
894 *gotGlxInfo = 1;
895 } else {
896 record_error("glGetString returned null");
897 }
898
899 // If GLX_MESA_query_renderer is available, populate additional data.
900 typedef Bool (*PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC)(
901 int attribute, unsigned int* value);
902 PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC
903 glXQueryCurrentRendererIntegerMESAProc =
904 cast<PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC>(
905 glXGetProcAddress("glXQueryCurrentRendererIntegerMESA"));
906 if (glXQueryCurrentRendererIntegerMESAProc) {
907 unsigned int vendorId, deviceId, accelerated, videoMemoryMB;
908 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_VENDOR_ID_MESA,
909 &vendorId);
910 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_DEVICE_ID_MESA,
911 &deviceId);
912 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_ACCELERATED_MESA,
913 &accelerated);
914 glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_VIDEO_MEMORY_MESA,
915 &videoMemoryMB);
916
917 // Truncate IDs to 4 digits- that's all PCI IDs are.
918 vendorId &= 0xFFFF;
919 deviceId &= 0xFFFF;
920
921 record_value(
922 "MESA_VENDOR_ID\n0x%04x\n"
923 "MESA_DEVICE_ID\n0x%04x\n"
924 "MESA_ACCELERATED\n%s\n"
925 "MESA_VRAM\n%dMB\n",
926 vendorId, deviceId, accelerated ? "TRUE" : "FALSE", videoMemoryMB);
927 }
928
929 // From Mesa's GL/internal/dri_interface.h, to be used by DRI clients.
930 typedef const char* (*PFNGLXGETSCREENDRIVERPROC)(Display * dpy, int scrNum);
931 PFNGLXGETSCREENDRIVERPROC glXGetScreenDriverProc =
932 cast<PFNGLXGETSCREENDRIVERPROC>(glXGetProcAddress("glXGetScreenDriver"));
933 if (glXGetScreenDriverProc) {
934 const char* driDriver = glXGetScreenDriverProc(dpy, DefaultScreen(dpy));
935 if (driDriver) {
936 *gotDriDriver = 1;
937 record_value("DRI_DRIVER\n%s\n", driDriver);
938 }
939 }
940
941 // Get monitor information
942 get_x11_screen_info(dpy);
943
944 // Get DDX driver information
945 get_x11_ddx_info(dpy);
946
947 ///// Clean up. Indeed, the parent process might fail to kill us (e.g. if it
948 ///// doesn't need to check GL info) so we might be staying alive for longer
949 ///// than expected, so it's important to consume as little memory as
950 ///// possible. Also we want to check that we're able to do that too without
951 ///// generating X errors.
952 glXMakeCurrent(dpy, None,
953 nullptr); // must release the GL context before destroying it
954 glXDestroyContext(dpy, context);
955 XDestroyWindow(dpy, window);
956 XFreeColormap(dpy, swa.colormap);
957
958 # ifdef NS_FREE_PERMANENT_DATA // conditionally defined in nscore.h, don't
959 // forget to #include it above
960 XCloseDisplay(dpy);
961 # else
962 // This XSync call wanted to be instead:
963 // XCloseDisplay(dpy);
964 // but this can cause 1-minute stalls on certain setups using Nouveau, see bug
965 // 973192
966 XSync(dpy, False);
967 # endif
968
969 dlclose(libgl);
970 }
971
x11_egltest(int pci_count)972 static bool x11_egltest(int pci_count) {
973 Display* dpy = XOpenDisplay(nullptr);
974 if (!dpy) {
975 return false;
976 }
977 XSetErrorHandler(x_error_handler);
978
979 // On at least amdgpu open source driver, eglInitialize fails unless
980 // a valid XDisplay pointer is passed as the native display.
981 if (!get_egl_status(dpy, true, pci_count != 1)) {
982 return false;
983 }
984
985 // Get monitor information
986 get_x11_screen_info(dpy);
987
988 // Get DDX driver information
989 get_x11_ddx_info(dpy);
990
991 // Bug 1715245: Closing the display connection here crashes on NV prop.
992 // drivers. Just leave it open, the process will exit shortly after anyway.
993 // XCloseDisplay(dpy);
994
995 record_value("TEST_TYPE\nEGL\n");
996 return true;
997 }
998
glxtest()999 static void glxtest() {
1000 int gotGlxInfo = 0;
1001 int gotDriDriver = 0;
1002
1003 get_glx_status(&gotGlxInfo, &gotDriDriver);
1004 if (!gotGlxInfo) {
1005 get_egl_status(nullptr, true, false);
1006 } else if (!gotDriDriver) {
1007 // If we failed to get the driver name from X, try via
1008 // EGL_MESA_query_driver. We are probably using Wayland.
1009 get_egl_status(nullptr, false, true);
1010 }
1011
1012 record_value("TEST_TYPE\nGLX\n");
1013 }
1014 #endif
1015
1016 #ifdef MOZ_WAYLAND
1017 typedef void (*print_info_t)(void* info);
1018 typedef void (*destroy_info_t)(void* info);
1019
1020 struct global_info {
1021 struct wl_list link;
1022
1023 uint32_t id;
1024 uint32_t version;
1025 char* interface;
1026
1027 print_info_t print;
1028 destroy_info_t destroy;
1029 };
1030
1031 struct output_info {
1032 struct global_info global;
1033 struct wl_list global_link;
1034
1035 struct wl_output* output;
1036
1037 int32_t version;
1038
1039 int32_t scale;
1040 };
1041
1042 struct xdg_output_v1_info {
1043 struct wl_list link;
1044
1045 struct zxdg_output_v1* xdg_output;
1046 struct output_info* output;
1047
1048 struct {
1049 int32_t width, height;
1050 } logical;
1051 };
1052
1053 struct xdg_output_manager_v1_info {
1054 struct global_info global;
1055 struct zxdg_output_manager_v1* manager;
1056 struct weston_info* info;
1057
1058 struct wl_list outputs;
1059 };
1060
1061 struct weston_info {
1062 struct wl_display* display;
1063 struct wl_registry* registry;
1064
1065 struct wl_list infos;
1066 bool roundtrip_needed;
1067
1068 struct wl_list outputs;
1069 struct xdg_output_manager_v1_info* xdg_output_manager_v1_info;
1070 };
1071
init_global_info(struct weston_info * info,struct global_info * global,uint32_t id,const char * interface,uint32_t version)1072 static void init_global_info(struct weston_info* info,
1073 struct global_info* global, uint32_t id,
1074 const char* interface, uint32_t version) {
1075 global->id = id;
1076 global->version = version;
1077 global->interface = strdup(interface);
1078
1079 wl_list_insert(info->infos.prev, &global->link);
1080 }
1081
print_output_info(void * data)1082 static void print_output_info(void* data) {}
1083
destroy_xdg_output_v1_info(struct xdg_output_v1_info * info)1084 static void destroy_xdg_output_v1_info(struct xdg_output_v1_info* info) {
1085 wl_list_remove(&info->link);
1086 zxdg_output_v1_destroy(info->xdg_output);
1087 free(info);
1088 }
1089
cmpOutputIds(const void * a,const void * b)1090 static int cmpOutputIds(const void* a, const void* b) {
1091 return (((struct xdg_output_v1_info*)a)->output->global.id -
1092 ((struct xdg_output_v1_info*)b)->output->global.id);
1093 }
1094
print_xdg_output_manager_v1_info(void * data)1095 static void print_xdg_output_manager_v1_info(void* data) {
1096 struct xdg_output_manager_v1_info* info =
1097 (struct xdg_output_manager_v1_info*)data;
1098 struct xdg_output_v1_info* output;
1099
1100 int screen_count = wl_list_length(&info->outputs);
1101 if (screen_count > 0) {
1102 struct xdg_output_v1_info* infos = (struct xdg_output_v1_info*)calloc(
1103 1, screen_count * sizeof(xdg_output_v1_info));
1104
1105 int pos = 0;
1106 wl_list_for_each(output, &info->outputs, link) {
1107 infos[pos] = *output;
1108 pos++;
1109 }
1110
1111 if (screen_count > 1) {
1112 qsort(infos, screen_count, sizeof(struct xdg_output_v1_info),
1113 cmpOutputIds);
1114 }
1115
1116 record_value("SCREEN_INFO\n");
1117 for (int i = 0; i < screen_count; i++) {
1118 record_value("%dx%d:0;", infos[i].logical.width, infos[i].logical.height);
1119 }
1120 record_value("\n");
1121
1122 free(infos);
1123 }
1124 }
1125
destroy_xdg_output_manager_v1_info(void * data)1126 static void destroy_xdg_output_manager_v1_info(void* data) {
1127 struct xdg_output_manager_v1_info* info =
1128 (struct xdg_output_manager_v1_info*)data;
1129 struct xdg_output_v1_info *output, *tmp;
1130
1131 zxdg_output_manager_v1_destroy(info->manager);
1132
1133 wl_list_for_each_safe(output, tmp, &info->outputs, link) {
1134 destroy_xdg_output_v1_info(output);
1135 }
1136 }
1137
handle_xdg_output_v1_logical_position(void * data,struct zxdg_output_v1 * output,int32_t x,int32_t y)1138 static void handle_xdg_output_v1_logical_position(void* data,
1139 struct zxdg_output_v1* output,
1140 int32_t x, int32_t y) {}
1141
handle_xdg_output_v1_logical_size(void * data,struct zxdg_output_v1 * output,int32_t width,int32_t height)1142 static void handle_xdg_output_v1_logical_size(void* data,
1143 struct zxdg_output_v1* output,
1144 int32_t width, int32_t height) {
1145 struct xdg_output_v1_info* xdg_output = (struct xdg_output_v1_info*)data;
1146 xdg_output->logical.width = width;
1147 xdg_output->logical.height = height;
1148 }
1149
handle_xdg_output_v1_done(void * data,struct zxdg_output_v1 * output)1150 static void handle_xdg_output_v1_done(void* data,
1151 struct zxdg_output_v1* output) {}
1152
handle_xdg_output_v1_name(void * data,struct zxdg_output_v1 * output,const char * name)1153 static void handle_xdg_output_v1_name(void* data, struct zxdg_output_v1* output,
1154 const char* name) {}
1155
handle_xdg_output_v1_description(void * data,struct zxdg_output_v1 * output,const char * description)1156 static void handle_xdg_output_v1_description(void* data,
1157 struct zxdg_output_v1* output,
1158 const char* description) {}
1159
1160 static const struct zxdg_output_v1_listener xdg_output_v1_listener = {
1161 .logical_position = handle_xdg_output_v1_logical_position,
1162 .logical_size = handle_xdg_output_v1_logical_size,
1163 .done = handle_xdg_output_v1_done,
1164 .name = handle_xdg_output_v1_name,
1165 .description = handle_xdg_output_v1_description,
1166 };
1167
add_xdg_output_v1_info(struct xdg_output_manager_v1_info * manager_info,struct output_info * output)1168 static void add_xdg_output_v1_info(
1169 struct xdg_output_manager_v1_info* manager_info,
1170 struct output_info* output) {
1171 struct xdg_output_v1_info* xdg_output =
1172 (struct xdg_output_v1_info*)calloc(1, sizeof *xdg_output);
1173
1174 wl_list_insert(&manager_info->outputs, &xdg_output->link);
1175 xdg_output->xdg_output = zxdg_output_manager_v1_get_xdg_output(
1176 manager_info->manager, output->output);
1177 zxdg_output_v1_add_listener(xdg_output->xdg_output, &xdg_output_v1_listener,
1178 xdg_output);
1179
1180 xdg_output->output = output;
1181
1182 manager_info->info->roundtrip_needed = true;
1183 }
1184
add_xdg_output_manager_v1_info(struct weston_info * info,uint32_t id,uint32_t version)1185 static void add_xdg_output_manager_v1_info(struct weston_info* info,
1186 uint32_t id, uint32_t version) {
1187 struct output_info* output;
1188 struct xdg_output_manager_v1_info* manager =
1189 (struct xdg_output_manager_v1_info*)calloc(1, sizeof *manager);
1190
1191 wl_list_init(&manager->outputs);
1192 manager->info = info;
1193
1194 init_global_info(info, &manager->global, id,
1195 zxdg_output_manager_v1_interface.name, version);
1196 manager->global.print = print_xdg_output_manager_v1_info;
1197 manager->global.destroy = destroy_xdg_output_manager_v1_info;
1198
1199 manager->manager = (struct zxdg_output_manager_v1*)wl_registry_bind(
1200 info->registry, id, &zxdg_output_manager_v1_interface,
1201 version > 2 ? 2 : version);
1202
1203 wl_list_for_each(output, &info->outputs, global_link) {
1204 add_xdg_output_v1_info(manager, output);
1205 }
1206
1207 info->xdg_output_manager_v1_info = manager;
1208 }
1209
output_handle_geometry(void * data,struct wl_output * wl_output,int32_t x,int32_t y,int32_t physical_width,int32_t physical_height,int32_t subpixel,const char * make,const char * model,int32_t output_transform)1210 static void output_handle_geometry(void* data, struct wl_output* wl_output,
1211 int32_t x, int32_t y, int32_t physical_width,
1212 int32_t physical_height, int32_t subpixel,
1213 const char* make, const char* model,
1214 int32_t output_transform) {}
1215
output_handle_mode(void * data,struct wl_output * wl_output,uint32_t flags,int32_t width,int32_t height,int32_t refresh)1216 static void output_handle_mode(void* data, struct wl_output* wl_output,
1217 uint32_t flags, int32_t width, int32_t height,
1218 int32_t refresh) {}
1219
output_handle_done(void * data,struct wl_output * wl_output)1220 static void output_handle_done(void* data, struct wl_output* wl_output) {}
1221
output_handle_scale(void * data,struct wl_output * wl_output,int32_t scale)1222 static void output_handle_scale(void* data, struct wl_output* wl_output,
1223 int32_t scale) {
1224 struct output_info* output = (struct output_info*)data;
1225
1226 output->scale = scale;
1227 }
1228
1229 static const struct wl_output_listener output_listener = {
1230 output_handle_geometry,
1231 output_handle_mode,
1232 output_handle_done,
1233 output_handle_scale,
1234 };
1235
destroy_output_info(void * data)1236 static void destroy_output_info(void* data) {
1237 struct output_info* output = (struct output_info*)data;
1238
1239 wl_output_destroy(output->output);
1240 }
1241
add_output_info(struct weston_info * info,uint32_t id,uint32_t version)1242 static void add_output_info(struct weston_info* info, uint32_t id,
1243 uint32_t version) {
1244 struct output_info* output = (struct output_info*)calloc(1, sizeof *output);
1245
1246 init_global_info(info, &output->global, id, "wl_output", version);
1247 output->global.print = print_output_info;
1248 output->global.destroy = destroy_output_info;
1249
1250 output->version = MIN(version, 2);
1251 output->scale = 1;
1252
1253 output->output = (struct wl_output*)wl_registry_bind(
1254 info->registry, id, &wl_output_interface, output->version);
1255 wl_output_add_listener(output->output, &output_listener, output);
1256
1257 info->roundtrip_needed = true;
1258 wl_list_insert(&info->outputs, &output->global_link);
1259
1260 if (info->xdg_output_manager_v1_info) {
1261 add_xdg_output_v1_info(info->xdg_output_manager_v1_info, output);
1262 }
1263 }
1264
global_handler(void * data,struct wl_registry * registry,uint32_t id,const char * interface,uint32_t version)1265 static void global_handler(void* data, struct wl_registry* registry,
1266 uint32_t id, const char* interface,
1267 uint32_t version) {
1268 struct weston_info* info = (struct weston_info*)data;
1269
1270 if (!strcmp(interface, "wl_output")) {
1271 add_output_info(info, id, version);
1272 } else if (!strcmp(interface, zxdg_output_manager_v1_interface.name)) {
1273 add_xdg_output_manager_v1_info(info, id, version);
1274 }
1275 }
1276
global_remove_handler(void * data,struct wl_registry * registry,uint32_t name)1277 static void global_remove_handler(void* data, struct wl_registry* registry,
1278 uint32_t name) {}
1279
1280 static const struct wl_registry_listener registry_listener = {
1281 global_handler, global_remove_handler};
1282
print_infos(struct wl_list * infos)1283 static void print_infos(struct wl_list* infos) {
1284 struct global_info* info;
1285
1286 wl_list_for_each(info, infos, link) { info->print(info); }
1287 }
1288
destroy_info(void * data)1289 static void destroy_info(void* data) {
1290 struct global_info* global = (struct global_info*)data;
1291
1292 global->destroy(data);
1293 wl_list_remove(&global->link);
1294 free(global->interface);
1295 free(data);
1296 }
1297
destroy_infos(struct wl_list * infos)1298 static void destroy_infos(struct wl_list* infos) {
1299 struct global_info *info, *tmp;
1300 wl_list_for_each_safe(info, tmp, infos, link) { destroy_info(info); }
1301 }
1302
get_wayland_screen_info(struct wl_display * dpy)1303 static void get_wayland_screen_info(struct wl_display* dpy) {
1304 struct weston_info info = {0};
1305 info.display = dpy;
1306
1307 info.xdg_output_manager_v1_info = NULL;
1308 wl_list_init(&info.infos);
1309 wl_list_init(&info.outputs);
1310
1311 info.registry = wl_display_get_registry(info.display);
1312 wl_registry_add_listener(info.registry, ®istry_listener, &info);
1313
1314 do {
1315 info.roundtrip_needed = false;
1316 wl_display_roundtrip(info.display);
1317 } while (info.roundtrip_needed);
1318
1319 print_infos(&info.infos);
1320 destroy_infos(&info.infos);
1321
1322 wl_registry_destroy(info.registry);
1323 }
1324
wayland_egltest()1325 static void wayland_egltest() {
1326 // NOTE: returns false to fall back to X11 when the Wayland socket doesn't
1327 // exist but fails with record_error if something actually went wrong
1328 struct wl_display* dpy = wl_display_connect(nullptr);
1329 if (!dpy) {
1330 record_error("Could not connect to wayland socket");
1331 return;
1332 }
1333
1334 if (!get_egl_status((EGLNativeDisplayType)dpy, true, false)) {
1335 record_error("EGL test failed");
1336 }
1337 get_wayland_screen_info(dpy);
1338
1339 wl_display_disconnect(dpy);
1340 record_value("TEST_TYPE\nEGL\n");
1341 }
1342 #endif
1343
childgltest()1344 int childgltest() {
1345 enum { bufsize = 2048 };
1346 char buf[bufsize];
1347
1348 // We save it as a global so that the X error handler can flush the buffer
1349 // before early exiting.
1350 glxtest_buf = buf;
1351 glxtest_bufsize = bufsize;
1352
1353 // Get a list of all GPUs from the PCI bus.
1354 int pci_count = get_pci_status();
1355
1356 #ifdef MOZ_WAYLAND
1357 if (IsWaylandEnabled()) {
1358 wayland_egltest();
1359 } else
1360 #endif
1361 {
1362 #ifdef MOZ_X11
1363 // TODO: --display command line argument is not properly handled
1364 if (!x11_egltest(pci_count)) {
1365 glxtest();
1366 }
1367 #endif
1368 }
1369
1370 // Finally write buffered data to the pipe.
1371 record_flush();
1372
1373 // If we completely filled the buffer, we need to tell the parent.
1374 if (glxtest_length >= glxtest_bufsize) {
1375 return EXIT_FAILURE_BUFFER_TOO_SMALL;
1376 }
1377
1378 return EXIT_SUCCESS;
1379 }
1380
1381 } // extern "C"
1382
1383 /** \returns true in the child glxtest process, false in the parent process */
fire_glxtest_process()1384 bool fire_glxtest_process() {
1385 int pfd[2];
1386 if (pipe(pfd) == -1) {
1387 perror("pipe");
1388 return false;
1389 }
1390 pid_t pid = fork();
1391 if (pid < 0) {
1392 perror("fork");
1393 close(pfd[0]);
1394 close(pfd[1]);
1395 return false;
1396 }
1397 // The child exits early to avoid running the full shutdown sequence and avoid
1398 // conflicting with threads we have already spawned (like the profiler).
1399 if (pid == 0) {
1400 close(pfd[0]);
1401 write_end_of_the_pipe = pfd[1];
1402 close_logging();
1403 int rv = childgltest();
1404 close(pfd[1]);
1405 _exit(rv);
1406 }
1407
1408 close(pfd[1]);
1409 mozilla::widget::glxtest_pipe = pfd[0];
1410 mozilla::widget::glxtest_pid = pid;
1411 return false;
1412 }
1413