Bumping manifests a=b2g-bump
[gecko.git] / toolkit / xre / glxtest.cpp
blobb8681ad9ad9d2d22d1571eb7cabd1d83170f0a70
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/. */
9 //////////////////////////////////////////////////////////////////////////////
11 // Explanation: See bug 639842. Safely getting GL driver info on X11 is hard, because the only way to do
12 // that is to create a GL context and call glGetString(), but with bad drivers,
13 // just creating a GL context may crash.
15 // This file implements the idea to do that in a separate process.
17 // The only non-static function here is fire_glxtest_process(). It creates a pipe, publishes its 'read' end as the
18 // mozilla::widget::glxtest_pipe global variable, forks, and runs that GLX probe in the child process,
19 // which runs the glxtest() static function. This creates a X connection, a GLX context, calls glGetString, and writes that
20 // to the 'write' end of the pipe.
22 #include <cstdio>
23 #include <cstdlib>
24 #include <unistd.h>
25 #include <dlfcn.h>
26 #include "nscore.h"
27 #include <fcntl.h>
28 #include "stdint.h"
30 #ifdef __SUNPRO_CC
31 #include <stdio.h>
32 #endif
34 #include "X11/Xlib.h"
35 #include "X11/Xutil.h"
37 // stuff from glx.h
38 typedef struct __GLXcontextRec *GLXContext;
39 typedef XID GLXPixmap;
40 typedef XID GLXDrawable;
41 /* GLX 1.3 and later */
42 typedef struct __GLXFBConfigRec *GLXFBConfig;
43 typedef XID GLXFBConfigID;
44 typedef XID GLXContextID;
45 typedef XID GLXWindow;
46 typedef XID GLXPbuffer;
47 #define GLX_RGBA 4
48 #define GLX_RED_SIZE 8
49 #define GLX_GREEN_SIZE 9
50 #define GLX_BLUE_SIZE 10
52 // stuff from gl.h
53 typedef uint8_t GLubyte;
54 typedef uint32_t GLenum;
55 #define GL_VENDOR 0x1F00
56 #define GL_RENDERER 0x1F01
57 #define GL_VERSION 0x1F02
59 namespace mozilla {
60 namespace widget {
61 // the read end of the pipe, which will be used by GfxInfo
62 extern int glxtest_pipe;
63 // the PID of the glxtest process, to pass to waitpid()
64 extern pid_t glxtest_pid;
68 // the write end of the pipe, which we're going to write to
69 static int write_end_of_the_pipe = -1;
71 // C++ standard collides with C standard in that it doesn't allow casting void* to function pointer types.
72 // So the work-around is to convert first to size_t.
73 // http://www.trilithium.com/johan/2004/12/problem-with-dlsym/
74 template<typename func_ptr_type>
75 static func_ptr_type cast(void *ptr)
77 return reinterpret_cast<func_ptr_type>(
78 reinterpret_cast<size_t>(ptr)
82 static void fatal_error(const char *str)
84 write(write_end_of_the_pipe, str, strlen(str));
85 write(write_end_of_the_pipe, "\n", 1);
86 _exit(EXIT_FAILURE);
89 static int
90 x_error_handler(Display *, XErrorEvent *ev)
92 enum { bufsize = 1024 };
93 char buf[bufsize];
94 int length = snprintf(buf, bufsize,
95 "X error occurred in GLX probe, error_code=%d, request_code=%d, minor_code=%d\n",
96 ev->error_code,
97 ev->request_code,
98 ev->minor_code);
99 write(write_end_of_the_pipe, buf, length);
100 _exit(EXIT_FAILURE);
101 return 0;
105 // glxtest is declared inside extern "C" so that the name is not mangled.
106 // The name is used in build/valgrind/x86_64-redhat-linux-gnu.sup to suppress
107 // memory leak errors because we run it inside a short lived fork and we don't
108 // care about leaking memory
109 extern "C" {
111 void glxtest()
113 // we want to redirect to /dev/null stdout, stderr, and while we're at it,
114 // any PR logging file descriptors. To that effect, we redirect all positive
115 // file descriptors up to what open() returns here. In particular, 1 is stdout and 2 is stderr.
116 int fd = open("/dev/null", O_WRONLY);
117 for (int i = 1; i < fd; i++)
118 dup2(fd, i);
119 close(fd);
121 if (getenv("MOZ_AVOID_OPENGL_ALTOGETHER"))
122 fatal_error("The MOZ_AVOID_OPENGL_ALTOGETHER environment variable is defined");
124 ///// Open libGL and load needed symbols /////
125 #ifdef __OpenBSD__
126 #define LIBGL_FILENAME "libGL.so"
127 #else
128 #define LIBGL_FILENAME "libGL.so.1"
129 #endif
130 void *libgl = dlopen(LIBGL_FILENAME, RTLD_LAZY);
131 if (!libgl)
132 fatal_error("Unable to load " LIBGL_FILENAME);
134 typedef void* (* PFNGLXGETPROCADDRESS) (const char *);
135 PFNGLXGETPROCADDRESS glXGetProcAddress = cast<PFNGLXGETPROCADDRESS>(dlsym(libgl, "glXGetProcAddress"));
137 if (!glXGetProcAddress)
138 fatal_error("Unable to find glXGetProcAddress in " LIBGL_FILENAME);
140 typedef GLXFBConfig* (* PFNGLXQUERYEXTENSION) (Display *, int *, int *);
141 PFNGLXQUERYEXTENSION glXQueryExtension = cast<PFNGLXQUERYEXTENSION>(glXGetProcAddress("glXQueryExtension"));
143 typedef GLXFBConfig* (* PFNGLXQUERYVERSION) (Display *, int *, int *);
144 PFNGLXQUERYVERSION glXQueryVersion = cast<PFNGLXQUERYVERSION>(dlsym(libgl, "glXQueryVersion"));
146 typedef XVisualInfo* (* PFNGLXCHOOSEVISUAL) (Display *, int, int *);
147 PFNGLXCHOOSEVISUAL glXChooseVisual = cast<PFNGLXCHOOSEVISUAL>(glXGetProcAddress("glXChooseVisual"));
149 typedef GLXContext (* PFNGLXCREATECONTEXT) (Display *, XVisualInfo *, GLXContext, Bool);
150 PFNGLXCREATECONTEXT glXCreateContext = cast<PFNGLXCREATECONTEXT>(glXGetProcAddress("glXCreateContext"));
152 typedef Bool (* PFNGLXMAKECURRENT) (Display*, GLXDrawable, GLXContext);
153 PFNGLXMAKECURRENT glXMakeCurrent = cast<PFNGLXMAKECURRENT>(glXGetProcAddress("glXMakeCurrent"));
155 typedef void (* PFNGLXDESTROYCONTEXT) (Display*, GLXContext);
156 PFNGLXDESTROYCONTEXT glXDestroyContext = cast<PFNGLXDESTROYCONTEXT>(glXGetProcAddress("glXDestroyContext"));
158 typedef GLubyte* (* PFNGLGETSTRING) (GLenum);
159 PFNGLGETSTRING glGetString = cast<PFNGLGETSTRING>(glXGetProcAddress("glGetString"));
161 if (!glXQueryExtension ||
162 !glXQueryVersion ||
163 !glXChooseVisual ||
164 !glXCreateContext ||
165 !glXMakeCurrent ||
166 !glXDestroyContext ||
167 !glGetString)
169 fatal_error("glXGetProcAddress couldn't find required functions");
171 ///// Open a connection to the X server /////
172 Display *dpy = XOpenDisplay(nullptr);
173 if (!dpy)
174 fatal_error("Unable to open a connection to the X server");
176 ///// Check that the GLX extension is present /////
177 if (!glXQueryExtension(dpy, nullptr, nullptr))
178 fatal_error("GLX extension missing");
180 XSetErrorHandler(x_error_handler);
182 ///// Get a visual /////
183 int attribs[] = {
184 GLX_RGBA,
185 GLX_RED_SIZE, 1,
186 GLX_GREEN_SIZE, 1,
187 GLX_BLUE_SIZE, 1,
188 None };
189 XVisualInfo *vInfo = glXChooseVisual(dpy, DefaultScreen(dpy), attribs);
190 if (!vInfo)
191 fatal_error("No visuals found");
193 // using a X11 Window instead of a GLXPixmap does not crash
194 // fglrx in indirect rendering. bug 680644
195 Window window;
196 XSetWindowAttributes swa;
197 swa.colormap = XCreateColormap(dpy, RootWindow(dpy, vInfo->screen),
198 vInfo->visual, AllocNone);
200 swa.border_pixel = 0;
201 window = XCreateWindow(dpy, RootWindow(dpy, vInfo->screen),
202 0, 0, 16, 16,
203 0, vInfo->depth, InputOutput, vInfo->visual,
204 CWBorderPixel | CWColormap, &swa);
206 ///// Get a GL context and make it current //////
207 GLXContext context = glXCreateContext(dpy, vInfo, nullptr, True);
208 glXMakeCurrent(dpy, window, context);
210 ///// Look for this symbol to determine texture_from_pixmap support /////
211 void* glXBindTexImageEXT = glXGetProcAddress("glXBindTexImageEXT");
213 ///// Get GL vendor/renderer/versions strings /////
214 enum { bufsize = 1024 };
215 char buf[bufsize];
216 const GLubyte *vendorString = glGetString(GL_VENDOR);
217 const GLubyte *rendererString = glGetString(GL_RENDERER);
218 const GLubyte *versionString = glGetString(GL_VERSION);
220 if (!vendorString || !rendererString || !versionString)
221 fatal_error("glGetString returned null");
223 int length = snprintf(buf, bufsize,
224 "VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\n%s\n",
225 vendorString,
226 rendererString,
227 versionString,
228 glXBindTexImageEXT ? "TRUE" : "FALSE");
229 if (length >= bufsize)
230 fatal_error("GL strings length too large for buffer size");
232 ///// Clean up. Indeed, the parent process might fail to kill us (e.g. if it doesn't need to check GL info)
233 ///// so we might be staying alive for longer than expected, so it's important to consume as little memory as
234 ///// possible. Also we want to check that we're able to do that too without generating X errors.
235 glXMakeCurrent(dpy, None, nullptr); // must release the GL context before destroying it
236 glXDestroyContext(dpy, context);
237 XDestroyWindow(dpy, window);
238 XFreeColormap(dpy, swa.colormap);
240 #ifdef NS_FREE_PERMANENT_DATA // conditionally defined in nscore.h, don't forget to #include it above
241 XCloseDisplay(dpy);
242 #else
243 // This XSync call wanted to be instead:
244 // XCloseDisplay(dpy);
245 // but this can cause 1-minute stalls on certain setups using Nouveau, see bug 973192
246 XSync(dpy, False);
247 #endif
249 dlclose(libgl);
251 ///// Finally write data to the pipe
252 write(write_end_of_the_pipe, buf, length);
257 /** \returns true in the child glxtest process, false in the parent process */
258 bool fire_glxtest_process()
260 int pfd[2];
261 if (pipe(pfd) == -1) {
262 perror("pipe");
263 return false;
265 pid_t pid = fork();
266 if (pid < 0) {
267 perror("fork");
268 close(pfd[0]);
269 close(pfd[1]);
270 return false;
272 // The child exits early to avoid running the full shutdown sequence and avoid conflicting with threads
273 // we have already spawned (like the profiler).
274 if (pid == 0) {
275 close(pfd[0]);
276 write_end_of_the_pipe = pfd[1];
277 glxtest();
278 close(pfd[1]);
279 _exit(0);
282 close(pfd[1]);
283 mozilla::widget::glxtest_pipe = pfd[0];
284 mozilla::widget::glxtest_pid = pid;
285 return false;