• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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