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