[GTK] Add accelerated 2D canvas support using cairo-gl
[WebKit-https.git] / Source / WebCore / platform / graphics / egl / GLContextEGL.cpp
1 /*
2  * Copyright (C) 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 "GLContextEGL.h"
21
22 #if USE(EGL)
23
24 #include "GraphicsContext3D.h"
25 #include <cairo.h>
26 #include <wtf/OwnPtr.h>
27
28 #if USE(OPENGL_ES_2)
29 #include <GLES2/gl2.h>
30 #include <GLES2/gl2ext.h>
31 #else
32 #include "OpenGLShims.h"
33 #endif
34
35 #if ENABLE(ACCELERATED_2D_CANVAS)
36 #include <cairo-gl.h>
37 #endif
38
39 namespace WebCore {
40
41 static EGLDisplay gSharedEGLDisplay = EGL_NO_DISPLAY;
42
43 #if USE(OPENGL_ES_2)
44 static const EGLenum gGLAPI = EGL_OPENGL_ES_API;
45 #else
46 static const EGLenum gGLAPI = EGL_OPENGL_API;
47 #endif
48
49 static EGLDisplay sharedEGLDisplay()
50 {
51     static bool initialized = false;
52     if (!initialized) {
53         initialized = true;
54 #if PLATFORM(X11)
55         gSharedEGLDisplay = eglGetDisplay(GLContext::sharedX11Display());
56 #else
57         gSharedEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
58 #endif
59         if (gSharedEGLDisplay != EGL_NO_DISPLAY && (!eglInitialize(gSharedEGLDisplay, 0, 0) || !eglBindAPI(gGLAPI)))
60             gSharedEGLDisplay = EGL_NO_DISPLAY;
61     }
62     return gSharedEGLDisplay;
63 }
64
65 static const EGLint gContextAttributes[] = {
66 #if USE(OPENGL_ES_2)
67     EGL_CONTEXT_CLIENT_VERSION, 2,
68 #endif
69     EGL_NONE
70 };
71
72 static bool getEGLConfig(EGLConfig* config, GLContextEGL::EGLSurfaceType surfaceType)
73 {
74     EGLint attributeList[] = {
75 #if USE(OPENGL_ES_2)
76         EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
77 #else
78         EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
79 #endif
80         EGL_RED_SIZE, 8,
81         EGL_GREEN_SIZE, 8,
82         EGL_BLUE_SIZE, 8,
83         EGL_STENCIL_SIZE, 8,
84         EGL_ALPHA_SIZE, 8,
85         EGL_SURFACE_TYPE, EGL_NONE,
86         EGL_NONE
87     };
88
89     switch (surfaceType) {
90     case GLContextEGL::PbufferSurface:
91         attributeList[13] = EGL_PBUFFER_BIT;
92         break;
93     case GLContextEGL::PixmapSurface:
94         attributeList[13] = EGL_PIXMAP_BIT;
95         break;
96     case GLContextEGL::WindowSurface:
97         attributeList[13] = EGL_WINDOW_BIT;
98         break;
99     }
100
101     EGLint numberConfigsReturned;
102     return eglChooseConfig(sharedEGLDisplay(), attributeList, config, 1, &numberConfigsReturned) && numberConfigsReturned;
103 }
104
105 PassOwnPtr<GLContextEGL> GLContextEGL::createWindowContext(EGLNativeWindowType window, GLContext* sharingContext)
106 {
107     EGLContext eglSharingContext = sharingContext ? static_cast<GLContextEGL*>(sharingContext)->m_context : 0;
108
109     EGLDisplay display = sharedEGLDisplay();
110     if (display == EGL_NO_DISPLAY)
111         return nullptr;
112
113     EGLConfig config;
114     if (!getEGLConfig(&config, WindowSurface))
115         return nullptr;
116
117     EGLContext context = eglCreateContext(display, config, eglSharingContext, gContextAttributes);
118     if (context == EGL_NO_CONTEXT)
119         return nullptr;
120
121     EGLSurface surface = eglCreateWindowSurface(display, config, window, 0);
122     if (surface == EGL_NO_SURFACE)
123         return nullptr;
124
125     return adoptPtr(new GLContextEGL(context, surface, WindowSurface));
126 }
127
128 PassOwnPtr<GLContextEGL> GLContextEGL::createPbufferContext(EGLContext sharingContext)
129 {
130     EGLDisplay display = sharedEGLDisplay();
131     if (display == EGL_NO_DISPLAY)
132         return nullptr;
133
134     EGLConfig config;
135     if (!getEGLConfig(&config, PbufferSurface))
136         return nullptr;
137
138     EGLContext context = eglCreateContext(display, config, sharingContext, gContextAttributes);
139     if (context == EGL_NO_CONTEXT)
140         return nullptr;
141
142     static const int pbufferAttributes[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
143     EGLSurface surface = eglCreatePbufferSurface(display, config, pbufferAttributes);
144     if (surface == EGL_NO_SURFACE) {
145         eglDestroyContext(display, context);
146         return nullptr;
147     }
148
149     return adoptPtr(new GLContextEGL(context, surface, PbufferSurface));
150 }
151
152 PassOwnPtr<GLContextEGL> GLContextEGL::createPixmapContext(EGLContext sharingContext)
153 {
154 #if PLATFORM(X11)
155     EGLDisplay display = sharedEGLDisplay();
156     if (display == EGL_NO_DISPLAY)
157         return nullptr;
158
159     EGLConfig config;
160     if (!getEGLConfig(&config, PixmapSurface))
161         return nullptr;
162
163     EGLContext context = eglCreateContext(display, config, sharingContext, gContextAttributes);
164     if (context == EGL_NO_CONTEXT)
165         return nullptr;
166
167     EGLint depth;
168     if (!eglGetConfigAttrib(display, config, EGL_DEPTH_SIZE, &depth))
169         return nullptr;
170
171     Pixmap pixmap = XCreatePixmap(sharedX11Display(), DefaultRootWindow(sharedX11Display()), 1, 1, depth);
172     if (!pixmap)
173         return nullptr;
174
175     EGLSurface surface = eglCreatePixmapSurface(display, config, pixmap, 0);
176 #else
177     EGLSurface surface = EGL_NO_SURFACE;
178 #endif
179     if (surface == EGL_NO_SURFACE)
180         return nullptr;
181
182     return adoptPtr(new GLContextEGL(context, surface, PixmapSurface));
183 }
184
185 PassOwnPtr<GLContextEGL> GLContextEGL::createContext(EGLNativeWindowType window, GLContext* sharingContext)
186 {
187     if (!sharedEGLDisplay())
188         return nullptr;
189
190     static bool initialized = false;
191     static bool success = true;
192     if (!initialized) {
193 #if !USE(OPENGL_ES_2)
194         success = initializeOpenGLShims();
195 #endif
196         initialized = true;
197     }
198     if (!success)
199         return nullptr;
200
201     EGLContext eglSharingContext = sharingContext ? static_cast<GLContextEGL*>(sharingContext)->m_context : 0;
202     OwnPtr<GLContextEGL> context = window ? createWindowContext(window, sharingContext) : nullptr;
203     if (!context)
204         context = createPixmapContext(eglSharingContext);
205
206     if (!context)
207         context = createPbufferContext(eglSharingContext);
208     
209     return context.release();
210 }
211
212 GLContextEGL::GLContextEGL(EGLContext context, EGLSurface surface, EGLSurfaceType type)
213     : m_context(context)
214     , m_surface(surface)
215     , m_type(type)
216     , m_cairoDevice(0)
217 {
218 }
219
220 GLContextEGL::~GLContextEGL()
221 {
222     if (m_cairoDevice)
223         cairo_device_destroy(m_cairoDevice);
224
225     EGLDisplay display = sharedEGLDisplay();
226     if (m_context) {
227         glBindFramebuffer(GL_FRAMEBUFFER, 0);
228         eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
229         eglDestroyContext(display, m_context);
230     }
231
232     if (m_surface)
233         eglDestroySurface(display, m_surface);
234 }
235
236 bool GLContextEGL::canRenderToDefaultFramebuffer()
237 {
238     return m_type == WindowSurface;
239 }
240
241 IntSize GLContextEGL::defaultFrameBufferSize()
242 {
243     if (!canRenderToDefaultFramebuffer())
244         return IntSize();
245
246     EGLint width, height;
247     if (!eglQuerySurface(sharedEGLDisplay(), m_surface, EGL_WIDTH, &width)
248         || !eglQuerySurface(sharedEGLDisplay(), m_surface, EGL_HEIGHT, &height))
249         return IntSize();
250
251     return IntSize(width, height);
252 }
253
254 bool GLContextEGL::makeContextCurrent()
255 {
256     ASSERT(m_context && m_surface);
257
258     GLContext::makeContextCurrent();
259     if (eglGetCurrentContext() == m_context)
260         return true;
261
262     return eglMakeCurrent(sharedEGLDisplay(), m_surface, m_surface, m_context);
263 }
264
265 void GLContextEGL::swapBuffers()
266 {
267     ASSERT(m_surface);
268     eglSwapBuffers(sharedEGLDisplay(), m_surface);
269 }
270
271 void GLContextEGL::waitNative()
272 {
273     eglWaitNative(EGL_CORE_NATIVE_ENGINE);
274 }
275
276 cairo_device_t* GLContextEGL::cairoDevice()
277 {
278     if (m_cairoDevice)
279         return m_cairoDevice;
280
281 #if ENABLE(ACCELERATED_2D_CANVAS)
282     m_cairoDevice = cairo_egl_device_create(sharedEGLDisplay(), m_context);
283 #endif
284
285     return m_cairoDevice;
286 }
287
288 #if ENABLE(WEBGL)
289 PlatformGraphicsContext3D GLContextEGL::platformContext()
290 {
291     return m_context;
292 }
293 #endif
294
295 } // namespace WebCore
296
297 #endif // USE(EGL)