]> git.sesse.net Git - vlc/blob - modules/video_output/egl.c
EGL: convert to OpenGL provider
[vlc] / modules / video_output / egl.c
1 /**
2  * @file egl.c
3  * @brief EGL OpenGL extension module
4  */
5 /*****************************************************************************
6  * Copyright © 2010-2011 Rémi Denis-Courmont
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1
11  * of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  ****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <assert.h>
29 #include <EGL/egl.h>
30
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_opengl.h>
34 #include <vlc_vout_window.h>
35 #ifdef __unix__
36 # include <vlc_xlib.h>
37 # include <dlfcn.h>
38 #endif
39
40 /* Plugin callbacks */
41 static int OpenGLES2 (vlc_object_t *);
42 static int OpenGLES (vlc_object_t *);
43 static int OpenGL (vlc_object_t *);
44 static void Close (vlc_object_t *);
45
46 vlc_module_begin ()
47     set_shortname (N_("EGL"))
48     set_description (N_("EGL extension for OpenGL"))
49     set_category (CAT_VIDEO)
50     set_subcategory (SUBCAT_VIDEO_VOUT)
51     set_capability ("opengl", 50)
52     set_callbacks (OpenGL, Close)
53
54     add_submodule ()
55     set_capability ("opengl es2", 50)
56     set_callbacks (OpenGLES2, Close)
57
58     add_submodule ()
59     set_capability ("opengl es", 50)
60     set_callbacks (OpenGLES, Close)
61
62 vlc_module_end ()
63
64 typedef struct vlc_gl_sys_t
65 {
66     EGLDisplay display;
67     EGLSurface surface;
68     EGLContext context;
69 } vlc_gl_sys_t;
70
71 /* OpenGL callbacks */
72 static int MakeCurrent (vlc_gl_t *);
73 static void SwapBuffers (vlc_gl_t *);
74
75 static bool CheckAPI (EGLDisplay dpy, const char *api)
76 {
77     const char *apis = eglQueryString (dpy, EGL_CLIENT_APIS);
78     size_t apilen = strlen (api);
79
80     /* Cannot use strtok_r() on constant string... */
81     do
82     {
83         if (!strncmp (apis, api, apilen)
84           && (memchr (" ", apis[apilen], 2) != NULL))
85             return true;
86
87         apis = strchr (apis, ' ');
88     }
89     while (apis != NULL);
90
91     return false;
92 }
93
94 struct gl_api
95 {
96    const char name[10];
97    EGLenum    api;
98    EGLint     min_minor;
99    EGLint     render_bit;
100    EGLint     attr[3];
101 };
102
103 /**
104  * Probe EGL display availability
105  */
106 static int Open (vlc_object_t *obj, const struct gl_api *api)
107 {
108     vlc_gl_t *gl = (vlc_gl_t *)obj;
109
110 #ifdef __unix__
111     /* EGL can only use the default X11 display */
112     if (gl->surface->display.x11 != NULL)
113         return VLC_EGENERIC;
114     if (!vlc_xlib_init (obj))
115         return VLC_EGENERIC;
116 #endif
117
118     /* Initialize EGL display */
119     /* TODO: support various display types */
120     EGLDisplay dpy = eglGetDisplay (EGL_DEFAULT_DISPLAY);
121     if (dpy == EGL_NO_DISPLAY)
122         return VLC_EGENERIC;
123
124     vlc_gl_sys_t *sys = malloc (sizeof (*sys));
125     if (unlikely(sys == NULL))
126         return VLC_ENOMEM;
127     gl->sys = sys;
128     sys->display = dpy;
129
130 #ifdef __unix__
131     /* XXX Explicit hack!
132      * Mesa EGL plugins (as of version 7.8.2) are not properly linked to
133      * libEGL.so even though they import some of its symbols. This is
134      * typically not a problem. Unfortunately, LibVLC loads plugins as
135      * RTLD_LOCAL so that they do not pollute the namespace. Then the
136      * libEGL symbols are not visible to EGL plugins, and the run-time
137      * linker exits the whole process. */
138     if (dlopen ("libEGL.so", RTLD_GLOBAL|RTLD_NOW) == NULL)
139         msg_Warn (gl, "libEGL cannot be loaded. Process might crash.");
140     if (dlopen ("libGL.so", RTLD_GLOBAL|RTLD_NOW) == NULL)
141         msg_Warn (gl, "libGL cannot be loaded. Process might crash.");
142 #endif
143
144     EGLint major, minor;
145     if (eglInitialize (dpy, &major, &minor) != EGL_TRUE)
146     {
147         /* No need to call eglTerminate() in this case */
148         free (sys);
149         return VLC_EGENERIC;
150     }
151
152     if (major != 1 || minor < api->min_minor || !CheckAPI (dpy, api->name))
153         goto error;
154
155     msg_Dbg (obj, "EGL version %s by %s", eglQueryString (dpy, EGL_VERSION),
156              eglQueryString (dpy, EGL_VENDOR));
157     {
158         const char *ext = eglQueryString (dpy, EGL_EXTENSIONS);
159         if (*ext)
160             msg_Dbg (obj, " extensions: %s", ext);
161     }
162
163     const EGLint conf_attr[] = {
164         EGL_RED_SIZE, 5,
165         EGL_GREEN_SIZE, 5,
166         EGL_BLUE_SIZE, 5,
167         EGL_RENDERABLE_TYPE, api->render_bit,
168         EGL_NONE
169     };
170     EGLConfig cfgv[1];
171     EGLint cfgc;
172
173     if (eglChooseConfig (dpy, conf_attr, cfgv, 1, &cfgc) != EGL_TRUE
174      || cfgc == 0)
175         goto error;
176
177     /* Create a drawing surface */
178 #if defined (WIN32)
179     EGLNativeWindowType win = gl->surface->handle.hwnd;
180 #elif defined (__unix__)
181     EGLNativeWindowType win = gl->surface->handle.xid;
182 #endif
183
184     EGLSurface surface = eglCreateWindowSurface (dpy, cfgv[0], win, NULL);
185     if (surface == EGL_NO_SURFACE)
186     {
187         msg_Err (obj, "cannot create EGL window surface");
188         goto error;
189     }
190     sys->surface = surface;
191
192     if (eglBindAPI (api->api) != EGL_TRUE)
193     {
194         msg_Err (obj, "cannot bind EGL API");
195         goto error;
196     }
197
198     EGLContext ctx = eglCreateContext (dpy, cfgv[0], EGL_NO_CONTEXT,
199                                        api->attr);
200     if (ctx == EGL_NO_CONTEXT)
201     {
202         msg_Err (obj, "cannot create EGL context");
203         goto error;
204     }
205     sys->context = ctx;
206
207     /* Initialize OpenGL callbacks */
208     gl->sys = sys;
209     gl->makeCurrent = MakeCurrent;
210     gl->swap = SwapBuffers;
211     gl->lock = NULL;
212     gl->unlock = NULL;
213     return VLC_SUCCESS;
214
215 error:
216     Close (obj);
217     return VLC_EGENERIC;
218 }
219
220 static int OpenGLES2 (vlc_object_t *obj)
221 {
222     static const struct gl_api api = {
223         "OpenGL_ES", EGL_OPENGL_ES_API, 3, EGL_OPENGL_ES2_BIT,
224         { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE },
225     };
226     return Open (obj, &api);
227 }
228
229 static int OpenGLES (vlc_object_t *obj)
230 {
231     static const struct gl_api api = {
232         "OpenGL_ES", EGL_OPENGL_ES_API, 0, EGL_OPENGL_ES_BIT,
233         { EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE },
234     };
235     return Open (obj, &api);
236 }
237
238 static int OpenGL (vlc_object_t *obj)
239 {
240     static const struct gl_api api = {
241         "OpenGL", EGL_OPENGL_API, 4, EGL_OPENGL_BIT,
242         { EGL_NONE },
243     };
244     return Open (obj, &api);
245 }
246
247 static void Close (vlc_object_t *obj)
248 {
249     vlc_gl_t *gl = (vlc_gl_t *)obj;
250     vlc_gl_sys_t *sys = gl->sys;
251
252     eglTerminate (sys->display);
253     free (sys);
254 }
255
256 static int MakeCurrent (vlc_gl_t *gl)
257 {
258     vlc_gl_sys_t *sys = gl->sys;
259
260     if (eglMakeCurrent (sys->display, sys->surface, sys->surface,
261                         sys->context) != EGL_TRUE)
262         return VLC_EGENERIC;
263     return VLC_SUCCESS;
264 }
265
266 static void SwapBuffers (vlc_gl_t *gl)
267 {
268     vlc_gl_sys_t *sys = gl->sys;
269
270     eglSwapBuffers (sys->display, sys->surface);
271 }