[GTK] Add accelerated 2D canvas support using cairo-gl
[WebKit-https.git] / Source / WebCore / platform / graphics / glx / GLContextGLX.cpp
1 /*
2  * Copyright (C) 2011, 2012 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 Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #include "config.h"
20 #include "GLContextGLX.h"
21
22 #if USE(GLX)
23 #include "GraphicsContext3D.h"
24 #include "OpenGLShims.h"
25 #include <GL/glx.h>
26 #include <cairo.h>
27 #include <wtf/OwnPtr.h>
28
29 #if ENABLE(ACCELERATED_2D_CANVAS)
30 #include <cairo-gl.h>
31 #endif
32
33 namespace WebCore {
34
35 PassOwnPtr<GLContextGLX> GLContextGLX::createWindowContext(XID window, GLContext* sharingContext)
36 {
37     Display* display = sharedX11Display();
38     XWindowAttributes attributes;
39     if (!XGetWindowAttributes(display, window, &attributes))
40         return nullptr;
41
42     XVisualInfo visualInfo;
43     visualInfo.visualid = XVisualIDFromVisual(attributes.visual);
44
45     int numReturned = 0;
46     XVisualInfo* visualInfoList = XGetVisualInfo(display, VisualIDMask, &visualInfo, &numReturned);
47
48     GLXContext glxSharingContext = sharingContext ? static_cast<GLContextGLX*>(sharingContext)->m_context : 0;
49     GLXContext context = glXCreateContext(display, visualInfoList, glxSharingContext, True);
50     XFree(visualInfoList);
51
52     if (!context)
53         return nullptr;
54
55     // GLXPbuffer and XID are both the same types underneath, so we have to share
56     // a constructor here with the window path.
57     GLContextGLX* contextWrapper = new GLContextGLX(context);
58     contextWrapper->m_window = window;
59     return adoptPtr(contextWrapper);
60 }
61
62 PassOwnPtr<GLContextGLX> GLContextGLX::createPbufferContext(GLXContext sharingContext)
63 {
64     int fbConfigAttributes[] = {
65         GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
66         GLX_RENDER_TYPE, GLX_RGBA_BIT,
67         GLX_RED_SIZE, 1,
68         GLX_GREEN_SIZE, 1,
69         GLX_BLUE_SIZE, 1,
70         GLX_ALPHA_SIZE, 1,
71         GLX_DOUBLEBUFFER, GL_FALSE,
72         0
73     };
74
75     int returnedElements;
76     Display* display = sharedX11Display();
77     GLXFBConfig* configs = glXChooseFBConfig(display, 0, fbConfigAttributes, &returnedElements);
78     if (!returnedElements) {
79         XFree(configs);
80         return nullptr;
81     }
82
83     // We will be rendering to a texture, so our pbuffer does not need to be large.
84     static const int pbufferAttributes[] = { GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, 0 };
85     GLXPbuffer pbuffer = glXCreatePbuffer(display, configs[0], pbufferAttributes);
86     if (!pbuffer) {
87         XFree(configs);
88         return nullptr;
89     }
90
91     GLXContext context = glXCreateNewContext(display, configs[0], GLX_RGBA_TYPE, sharingContext, GL_TRUE);
92     XFree(configs);
93     if (!context) {
94         glXDestroyPbuffer(display, pbuffer);
95         return nullptr;
96     }
97
98     // GLXPbuffer and XID are both the same types underneath, so we have to share
99     // a constructor here with the window path.
100     GLContextGLX* contextWrapper = new GLContextGLX(context);
101     contextWrapper->m_pbuffer = pbuffer;
102     return adoptPtr(contextWrapper);
103 }
104
105 PassOwnPtr<GLContextGLX> GLContextGLX::createPixmapContext(GLXContext sharingContext)
106 {
107     static int visualAttributes[] = {
108         GLX_RGBA,
109         GLX_RED_SIZE, 1,
110         GLX_GREEN_SIZE, 1,
111         GLX_BLUE_SIZE, 1,
112         GLX_ALPHA_SIZE, 1,
113         0
114     };
115
116     Display* display = sharedX11Display();
117     XVisualInfo* visualInfo = glXChooseVisual(display, DefaultScreen(display), visualAttributes);
118     if (!visualInfo)
119         return nullptr;
120
121     GLXContext context = glXCreateContext(display, visualInfo, sharingContext, GL_TRUE);
122     if (!context) {
123         XFree(visualInfo);
124         return nullptr;
125     }
126
127     Pixmap pixmap = XCreatePixmap(display, DefaultRootWindow(display), 1, 1, visualInfo->depth);
128     if (!pixmap) {
129         XFree(visualInfo);
130         return nullptr;
131     }
132
133     GLXPixmap glxPixmap = glXCreateGLXPixmap(display, visualInfo, pixmap);
134     if (!glxPixmap) {
135         XFreePixmap(display, pixmap);
136         XFree(visualInfo);
137         return nullptr;
138     }
139
140     XFree(visualInfo);
141     return adoptPtr(new GLContextGLX(context, pixmap, glxPixmap));
142 }
143
144 PassOwnPtr<GLContextGLX> GLContextGLX::createContext(XID window, GLContext* sharingContext)
145 {
146     if (!sharedX11Display())
147         return nullptr;
148
149     static bool initialized = false;
150     static bool success = true;
151     if (!initialized) {
152         success = initializeOpenGLShims();
153         initialized = true;
154     }
155     if (!success)
156         return nullptr;
157
158     GLXContext glxSharingContext = sharingContext ? static_cast<GLContextGLX*>(sharingContext)->m_context : 0;
159     OwnPtr<GLContextGLX> context = window ? createWindowContext(window, sharingContext) : nullptr;
160     if (!context)
161         context = createPbufferContext(glxSharingContext);
162     if (!context)
163         context = createPixmapContext(glxSharingContext);
164     if (!context)
165         return nullptr;
166
167     return context.release();
168 }
169
170 GLContextGLX::GLContextGLX(GLXContext context)
171     : m_context(context)
172     , m_window(0)
173     , m_pbuffer(0)
174     , m_pixmap(0)
175     , m_glxPixmap(0)
176     , m_cairoDevice(0)
177 {
178 }
179
180 GLContextGLX::GLContextGLX(GLXContext context, Pixmap pixmap, GLXPixmap glxPixmap)
181     : m_context(context)
182     , m_window(0)
183     , m_pbuffer(0)
184     , m_pixmap(pixmap)
185     , m_glxPixmap(glxPixmap)
186     , m_cairoDevice(0)
187 {
188 }
189
190 GLContextGLX::~GLContextGLX()
191 {
192     if (m_cairoDevice)
193         cairo_device_destroy(m_cairoDevice);
194
195     if (m_context) {
196         // This may be necessary to prevent crashes with NVidia's closed source drivers. Originally
197         // from Mozilla's 3D canvas implementation at: http://bitbucket.org/ilmari/canvas3d/
198         glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
199         glXMakeCurrent(sharedX11Display(), None, None);
200         glXDestroyContext(sharedX11Display(), m_context);
201     }
202
203     if (m_pbuffer) {
204         glXDestroyPbuffer(sharedX11Display(), m_pbuffer);
205         m_pbuffer = 0;
206     }
207     if (m_glxPixmap) {
208         glXDestroyGLXPixmap(sharedX11Display(), m_glxPixmap);
209         m_glxPixmap = 0;
210     }
211     if (m_pixmap) {
212         XFreePixmap(sharedX11Display(), m_pixmap);
213         m_pixmap = 0;
214     }
215 }
216
217 bool GLContextGLX::canRenderToDefaultFramebuffer()
218 {
219     return m_window;
220 }
221
222 IntSize GLContextGLX::defaultFrameBufferSize()
223 {
224     if (!canRenderToDefaultFramebuffer() || !m_window)
225         return IntSize();
226
227     int x, y;
228     Window rootWindow;
229     unsigned int width, height, borderWidth, depth;
230     if (!XGetGeometry(sharedX11Display(), m_window, &rootWindow, &x, &y, &width, &height, &borderWidth, &depth))
231         return IntSize();
232
233     return IntSize(width, height);
234 }
235
236 bool GLContextGLX::makeContextCurrent()
237 {
238     ASSERT(m_context && (m_window || m_pbuffer || m_glxPixmap));
239
240     GLContext::makeContextCurrent();
241     if (glXGetCurrentContext() == m_context)
242         return true;
243
244     if (m_window)
245         return glXMakeCurrent(sharedX11Display(), m_window, m_context);
246
247     if (m_pbuffer)
248         return glXMakeCurrent(sharedX11Display(), m_pbuffer, m_context);
249
250     return ::glXMakeCurrent(sharedX11Display(), m_glxPixmap, m_context);
251 }
252
253 void GLContextGLX::swapBuffers()
254 {
255     if (m_window)
256         glXSwapBuffers(sharedX11Display(), m_window);
257 }
258
259 void GLContextGLX::waitNative()
260 {
261     glXWaitX();
262 }
263
264 cairo_device_t* GLContextGLX::cairoDevice()
265 {
266     if (m_cairoDevice)
267         return m_cairoDevice;
268
269 #if ENABLE(ACCELERATED_2D_CANVAS)
270     m_cairoDevice = cairo_glx_device_create(sharedX11Display(), m_context);
271 #endif
272
273     return m_cairoDevice;
274 }
275
276 #if USE(3D_GRAPHICS)
277 PlatformGraphicsContext3D GLContextGLX::platformContext()
278 {
279     return m_context;
280 }
281 #endif
282
283 } // namespace WebCore
284
285 #endif // USE(GLX)