]> git.sesse.net Git - vlc/blob - modules/video_output/egl.c
270df0903b2de0b5aedd91847297e029a1c931ab
[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 program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program 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 Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, 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 #endif
38
39 /* Plugin callbacks */
40 static int OpenGLES2 (vlc_object_t *);
41 static int OpenGLES (vlc_object_t *);
42 static int OpenGL (vlc_object_t *);
43 static void Close (vlc_object_t *);
44
45 vlc_module_begin ()
46     set_shortname (N_("EGL"))
47     set_description (N_("EGL extension for OpenGL"))
48     set_category (CAT_VIDEO)
49     set_subcategory (SUBCAT_VIDEO_VOUT)
50     set_capability ("opengl", 50)
51     set_callbacks (OpenGL, Close)
52     add_shortcut ("egl")
53
54     add_submodule ()
55     set_capability ("opengl es2", 50)
56     set_callbacks (OpenGLES2, Close)
57     add_shortcut ("egl")
58
59     add_submodule ()
60     set_capability ("opengl es", 50)
61     set_callbacks (OpenGLES, Close)
62     add_shortcut ("egl")
63
64 vlc_module_end ()
65
66 typedef struct vlc_gl_sys_t
67 {
68     EGLDisplay display;
69     EGLSurface surface;
70     EGLContext context;
71 } vlc_gl_sys_t;
72
73 /* OpenGL callbacks */
74 static int MakeCurrent (vlc_gl_t *);
75 static void ReleaseCurrent (vlc_gl_t *);
76 static void SwapBuffers (vlc_gl_t *);
77 static void *GetSymbol(vlc_gl_t *, const char *);
78
79 static bool CheckAPI (EGLDisplay dpy, const char *api)
80 {
81     const char *apis = eglQueryString (dpy, EGL_CLIENT_APIS);
82     size_t apilen = strlen (api);
83
84     /* Cannot use strtok_r() on constant string... */
85     do
86     {
87         while (*apis == ' ')
88             apis++;
89         if (!strncmp (apis, api, apilen)
90           && (memchr (" ", apis[apilen], 2) != NULL))
91             return true;
92
93         apis = strchr (apis, ' ');
94     }
95     while (apis != NULL);
96
97     return false;
98 }
99
100 struct gl_api
101 {
102    const char name[10];
103    EGLenum    api;
104    EGLint     min_minor;
105    EGLint     render_bit;
106    EGLint     attr[3];
107 };
108
109 /**
110  * Probe EGL display availability
111  */
112 static int Open (vlc_object_t *obj, const struct gl_api *api)
113 {
114     vlc_gl_t *gl = (vlc_gl_t *)obj;
115
116     /* http://www.khronos.org/registry/egl/api/EGL/eglplatform.h defines the
117      * list and order of EGL platforms. */
118 #if defined(_WIN32) || defined(__VC32__) \
119  && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
120 # define vlc_eglGetWindow(w) ((w)->handle.hwnd)
121
122 #elif defined(__WINSCW__) || defined(__SYMBIAN32__)  /* Symbian */
123 # error Symbian EGL not supported.
124
125 #elif defined(__ANDROID__) || defined(ANDROID)
126 # error Android EGL not supported
127
128 #elif defined(__unix__) /* X11 (tentative) */
129 # define vlc_eglGetWindow(w) ((w)->handle.xid)
130     /* EGL can only use the default X11 display */
131     if (gl->surface->display.x11 != NULL)
132         return VLC_EGENERIC;
133     if (!vlc_xlib_init (obj))
134         return VLC_EGENERIC;
135
136 #else
137 # error EGL platform not recognized.
138 #endif
139
140     /* Initialize EGL display */
141     /* TODO: support various display types */
142     EGLDisplay dpy = eglGetDisplay (EGL_DEFAULT_DISPLAY);
143     if (dpy == EGL_NO_DISPLAY)
144         return VLC_EGENERIC;
145
146     vlc_gl_sys_t *sys = malloc (sizeof (*sys));
147     if (unlikely(sys == NULL))
148         return VLC_ENOMEM;
149     gl->sys = sys;
150     sys->display = dpy;
151
152     EGLint major, minor;
153     if (eglInitialize (dpy, &major, &minor) != EGL_TRUE)
154     {
155         /* No need to call eglTerminate() in this case */
156         free (sys);
157         return VLC_EGENERIC;
158     }
159
160     if (major != 1 || minor < api->min_minor || !CheckAPI (dpy, api->name))
161         goto error;
162
163     msg_Dbg (obj, "EGL version %s by %s", eglQueryString (dpy, EGL_VERSION),
164              eglQueryString (dpy, EGL_VENDOR));
165     {
166         const char *ext = eglQueryString (dpy, EGL_EXTENSIONS);
167         if (*ext)
168             msg_Dbg (obj, " extensions: %s", ext);
169     }
170
171     const EGLint conf_attr[] = {
172         EGL_RED_SIZE, 5,
173         EGL_GREEN_SIZE, 5,
174         EGL_BLUE_SIZE, 5,
175         EGL_RENDERABLE_TYPE, api->render_bit,
176         EGL_NONE
177     };
178     EGLConfig cfgv[1];
179     EGLint cfgc;
180
181     if (eglChooseConfig (dpy, conf_attr, cfgv, 1, &cfgc) != EGL_TRUE
182      || cfgc == 0)
183     {
184         msg_Err (obj, "cannot choose EGL configuration");
185         goto error;
186     }
187
188     /* Create a drawing surface */
189     EGLNativeWindowType win = vlc_eglGetWindow(gl->surface);
190     EGLSurface surface = eglCreateWindowSurface (dpy, cfgv[0], win, NULL);
191     if (surface == EGL_NO_SURFACE)
192     {
193         msg_Err (obj, "cannot create EGL window surface");
194         goto error;
195     }
196     sys->surface = surface;
197
198     if (eglBindAPI (api->api) != EGL_TRUE)
199     {
200         msg_Err (obj, "cannot bind EGL API");
201         goto error;
202     }
203
204     EGLContext ctx = eglCreateContext (dpy, cfgv[0], EGL_NO_CONTEXT,
205                                        api->attr);
206     if (ctx == EGL_NO_CONTEXT)
207     {
208         msg_Err (obj, "cannot create EGL context");
209         goto error;
210     }
211     sys->context = ctx;
212
213     /* Initialize OpenGL callbacks */
214     gl->sys = sys;
215     gl->makeCurrent = MakeCurrent;
216     gl->releaseCurrent = ReleaseCurrent;
217     gl->swap = SwapBuffers;
218     gl->getProcAddress = GetSymbol;
219     gl->lock = NULL;
220     gl->unlock = NULL;
221     return VLC_SUCCESS;
222
223 error:
224     Close (obj);
225     return VLC_EGENERIC;
226 }
227
228 static int OpenGLES2 (vlc_object_t *obj)
229 {
230     static const struct gl_api api = {
231         "OpenGL_ES", EGL_OPENGL_ES_API, 3, EGL_OPENGL_ES2_BIT,
232         { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE },
233     };
234     return Open (obj, &api);
235 }
236
237 static int OpenGLES (vlc_object_t *obj)
238 {
239     static const struct gl_api api = {
240         "OpenGL_ES", EGL_OPENGL_ES_API, 0, EGL_OPENGL_ES_BIT,
241         { EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE },
242     };
243     return Open (obj, &api);
244 }
245
246 static int OpenGL (vlc_object_t *obj)
247 {
248     static const struct gl_api api = {
249         "OpenGL", EGL_OPENGL_API, 4, EGL_OPENGL_BIT,
250         { EGL_NONE },
251     };
252     return Open (obj, &api);
253 }
254
255 static void Close (vlc_object_t *obj)
256 {
257     vlc_gl_t *gl = (vlc_gl_t *)obj;
258     vlc_gl_sys_t *sys = gl->sys;
259
260     eglTerminate (sys->display);
261     free (sys);
262 }
263
264 static int MakeCurrent (vlc_gl_t *gl)
265 {
266     vlc_gl_sys_t *sys = gl->sys;
267
268     if (eglMakeCurrent (sys->display, sys->surface, sys->surface,
269                         sys->context) != EGL_TRUE)
270         return VLC_EGENERIC;
271     return VLC_SUCCESS;
272 }
273
274 static void ReleaseCurrent (vlc_gl_t *gl)
275 {
276     vlc_gl_sys_t *sys = gl->sys;
277
278     eglMakeCurrent (sys->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
279                     EGL_NO_CONTEXT);
280 }
281
282 static void SwapBuffers (vlc_gl_t *gl)
283 {
284     vlc_gl_sys_t *sys = gl->sys;
285
286     eglSwapBuffers (sys->display, sys->surface);
287 }
288
289 static void *GetSymbol(vlc_gl_t *gl, const char *procname)
290 {
291     (void) gl;
292     return (void *)eglGetProcAddress (procname);
293 }