1 /*
2 * Copyright (C) 2011 Igalia S.L.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301 USA
18 */
19
20 #include "config.h"
21 #include "GraphicsContext3DInternal.h"
22
23 #if ENABLE(WEBGL)
24
25 #include "GraphicsContext3D.h"
26 #include "OpenGLShims.h"
27 #include <GL/glx.h>
28 #include <dlfcn.h>
29
30 // We do not want to call glXMakeContextCurrent using different Display pointers,
31 // because it might lead to crashes in some drivers (fglrx). We use a shared display
32 // pointer here.
33 static Display* gSharedDisplay = 0;
sharedDisplay()34 static Display* sharedDisplay()
35 {
36 if (!gSharedDisplay)
37 gSharedDisplay = XOpenDisplay(0);
38 return gSharedDisplay;
39 }
40
41 namespace WebCore {
42
43 // Because of driver bugs, exiting the program when there are active pbuffers
44 // can crash the X server (this has been observed with the official Nvidia drivers).
45 // We need to ensure that we clean everything up on exit. There are several reasons
46 // that GraphicsContext3Ds will still be alive at exit, including user error (memory
47 // leaks) and the page cache. In any case, we don't want the X server to crash.
48 static bool cleaningUpAtExit = false;
activeGraphicsContexts()49 static Vector<GraphicsContext3D*>& activeGraphicsContexts()
50 {
51 DEFINE_STATIC_LOCAL(Vector<GraphicsContext3D*>, contexts, ());
52 return contexts;
53 }
54
addActiveGraphicsContext(GraphicsContext3D * context)55 void GraphicsContext3DInternal::addActiveGraphicsContext(GraphicsContext3D* context)
56 {
57 static bool addedAtExitHandler = false;
58 if (!addedAtExitHandler) {
59 atexit(&GraphicsContext3DInternal::cleanupActiveContextsAtExit);
60 addedAtExitHandler = true;
61 }
62 activeGraphicsContexts().append(context);
63 }
64
removeActiveGraphicsContext(GraphicsContext3D * context)65 void GraphicsContext3DInternal::removeActiveGraphicsContext(GraphicsContext3D* context)
66 {
67 if (cleaningUpAtExit)
68 return;
69
70 Vector<GraphicsContext3D*>& contexts = activeGraphicsContexts();
71 size_t location = contexts.find(context);
72 if (location != WTF::notFound)
73 contexts.remove(location);
74 }
75
cleanupActiveContextsAtExit()76 void GraphicsContext3DInternal::cleanupActiveContextsAtExit()
77 {
78 cleaningUpAtExit = true;
79
80 Vector<GraphicsContext3D*>& contexts = activeGraphicsContexts();
81 for (size_t i = 0; i < contexts.size(); i++)
82 contexts[i]->~GraphicsContext3D();
83
84 if (!gSharedDisplay)
85 return;
86 XCloseDisplay(gSharedDisplay);
87 gSharedDisplay = 0;
88 }
89
create()90 GraphicsContext3DInternal* GraphicsContext3DInternal::create()
91 {
92 if (!sharedDisplay())
93 return 0;
94
95 static bool initialized = false;
96 static bool success = true;
97 if (!initialized) {
98 success = initializeOpenGLShims();
99 initialized = true;
100 }
101 if (!success)
102 return 0;
103
104 GraphicsContext3DInternal* internal = createPbufferContext();
105 if (!internal)
106 internal = createPixmapContext();
107 if (!internal)
108 return 0;
109
110 // The GraphicsContext3D constructor requires that this context is the current OpenGL context.
111 internal->makeContextCurrent();
112 return internal;
113 }
114
createPbufferContext()115 GraphicsContext3DInternal* GraphicsContext3DInternal::createPbufferContext()
116 {
117 int fbConfigAttributes[] = {
118 GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
119 GLX_RENDER_TYPE, GLX_RGBA_BIT,
120 GLX_RED_SIZE, 1,
121 GLX_GREEN_SIZE, 1,
122 GLX_BLUE_SIZE, 1,
123 GLX_ALPHA_SIZE, 1,
124 GLX_DEPTH_SIZE, 1,
125 GLX_STENCIL_SIZE, 1,
126 GLX_SAMPLE_BUFFERS, 1,
127 GLX_DOUBLEBUFFER, GL_FALSE,
128 GLX_SAMPLES, 4,
129 0
130 };
131 int returnedElements;
132 GLXFBConfig* configs = glXChooseFBConfig(sharedDisplay(), 0, fbConfigAttributes, &returnedElements);
133 if (!configs) {
134 fbConfigAttributes[20] = 0; // Attempt without anti-aliasing.
135 configs = glXChooseFBConfig(sharedDisplay(), 0, fbConfigAttributes, &returnedElements);
136 }
137 if (!returnedElements) {
138 XFree(configs);
139 return 0;
140 }
141
142 // We will be rendering to a texture, so our pbuffer does not need to be large.
143 static const int pbufferAttributes[] = { GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, 0 };
144 GLXPbuffer pbuffer = glXCreatePbuffer(sharedDisplay(), configs[0], pbufferAttributes);
145 if (!pbuffer) {
146 XFree(configs);
147 return 0;
148 }
149
150 GLXContext context = glXCreateNewContext(sharedDisplay(), configs[0], GLX_RGBA_TYPE, 0, GL_TRUE);
151 XFree(configs);
152 if (!context)
153 return 0;
154 return new GraphicsContext3DInternal(context, pbuffer);
155 }
156
createPixmapContext()157 GraphicsContext3DInternal* GraphicsContext3DInternal::createPixmapContext()
158 {
159 static int visualAttributes[] = {
160 GLX_RGBA,
161 GLX_RED_SIZE, 1,
162 GLX_GREEN_SIZE, 1,
163 GLX_BLUE_SIZE, 1,
164 GLX_ALPHA_SIZE, 1,
165 GLX_DOUBLEBUFFER,
166 0
167 };
168
169 XVisualInfo* visualInfo = glXChooseVisual(sharedDisplay(), DefaultScreen(sharedDisplay()), visualAttributes);
170 if (!visualInfo)
171 return 0;
172
173 GLXContext context = glXCreateContext(sharedDisplay(), visualInfo, 0, GL_TRUE);
174 if (!context) {
175 XFree(visualInfo);
176 return 0;
177 }
178
179 Pixmap pixmap = XCreatePixmap(sharedDisplay(), DefaultRootWindow(sharedDisplay()), 1, 1, visualInfo->depth);
180 if (!pixmap) {
181 XFree(visualInfo);
182 return 0;
183 }
184
185 GLXPixmap glxPixmap = glXCreateGLXPixmap(sharedDisplay(), visualInfo, pixmap);
186 if (!glxPixmap) {
187 XFreePixmap(sharedDisplay(), pixmap);
188 XFree(visualInfo);
189 return 0;
190 }
191
192 return new GraphicsContext3DInternal(context, pixmap, glxPixmap);
193 }
194
GraphicsContext3DInternal(GLXContext context,GLXPbuffer pbuffer)195 GraphicsContext3DInternal::GraphicsContext3DInternal(GLXContext context, GLXPbuffer pbuffer)
196 : m_context(context)
197 , m_pbuffer(pbuffer)
198 , m_pixmap(0)
199 , m_glxPixmap(0)
200 {
201 }
202
GraphicsContext3DInternal(GLXContext context,Pixmap pixmap,GLXPixmap glxPixmap)203 GraphicsContext3DInternal::GraphicsContext3DInternal(GLXContext context, Pixmap pixmap, GLXPixmap glxPixmap)
204 : m_context(context)
205 , m_pbuffer(0)
206 , m_pixmap(pixmap)
207 , m_glxPixmap(glxPixmap)
208 {
209 }
210
~GraphicsContext3DInternal()211 GraphicsContext3DInternal::~GraphicsContext3DInternal()
212 {
213 if (m_context) {
214 // This may be necessary to prevent crashes with NVidia's closed source drivers. Originally
215 // from Mozilla's 3D canvas implementation at: http://bitbucket.org/ilmari/canvas3d/
216 ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
217
218 ::glXMakeContextCurrent(sharedDisplay(), 0, 0, 0);
219 ::glXDestroyContext(sharedDisplay(), m_context);
220 m_context = 0;
221 }
222
223 if (m_pbuffer) {
224 ::glXDestroyPbuffer(sharedDisplay(), m_pbuffer);
225 m_pbuffer = 0;
226 }
227 if (m_glxPixmap) {
228 glXDestroyGLXPixmap(sharedDisplay(), m_glxPixmap);
229 m_glxPixmap = 0;
230 }
231 if (m_pixmap) {
232 XFreePixmap(sharedDisplay(), m_pixmap);
233 m_pixmap = 0;
234 }
235 }
236
makeContextCurrent()237 void GraphicsContext3DInternal::makeContextCurrent()
238 {
239 if (::glXGetCurrentContext() == m_context)
240 return;
241 if (!m_context)
242 return;
243 if (m_pbuffer) {
244 ::glXMakeCurrent(sharedDisplay(), m_pbuffer, m_context);
245 return;
246 }
247
248 ASSERT(m_glxPixmap);
249 ::glXMakeCurrent(sharedDisplay(), m_glxPixmap, m_context);
250 }
251
252 } // namespace WebCore
253
254 #endif // ENABLE_WEBGL
255