]> git.sesse.net Git - vlc/blob - modules/video_output/egl.c
EGL: add CheckClientExt() helper
[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 USE_PLATFORM_X11
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 #if defined (USE_PLATFORM_X11)
72     Display *x11;
73 #endif
74 } vlc_gl_sys_t;
75
76 /* OpenGL callbacks */
77 static int MakeCurrent (vlc_gl_t *);
78 static void ReleaseCurrent (vlc_gl_t *);
79 static void SwapBuffers (vlc_gl_t *);
80 static void *GetSymbol(vlc_gl_t *, const char *);
81
82 static bool CheckToken(const char *haystack, const char *needle)
83 {
84     size_t len = strlen(needle);
85
86     while (haystack != NULL)
87     {
88         while (*haystack == ' ')
89             haystack++;
90         if (!strncmp(haystack, needle, len)
91          && (memchr(" ", haystack[len], 2) != NULL))
92             return true;
93
94         haystack = strchr(haystack, ' ');
95     }
96     return false;
97 }
98
99 static bool CheckAPI (EGLDisplay dpy, const char *api)
100 {
101     const char *apis = eglQueryString (dpy, EGL_CLIENT_APIS);
102     return CheckToken(apis, api);
103 }
104
105 static bool CheckClientExt(const char *name)
106 {
107     const char *exts = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
108     return CheckToken(exts, name);
109 }
110
111 struct gl_api
112 {
113    const char name[10];
114    EGLenum    api;
115    EGLint     min_minor;
116    EGLint     render_bit;
117    EGLint     attr[3];
118 };
119
120 /* See http://www.khronos.org/registry/egl/api/EGL/eglplatform.h *
121  * for list and order of default EGL platforms. */
122 #if defined (_WIN32) || defined (__VC32__) \
123  && !defined (__CYGWIN__) && !defined (__SCITECH_SNAP__) /* Win32 and WinCE */
124
125 #elif defined (__WINSCW__) || defined (__SYMBIAN32__)  /* Symbian */
126 # error Symbian EGL not supported.
127 #elif defined (__ANDROID__) || defined (ANDROID)
128 # error Android EGL not supported.
129 #elif defined (__unix__) /* X11 (tentative) */
130
131 #else
132 # error EGL platform not recognized.
133 #endif
134
135 /**
136  * Probe EGL display availability
137  */
138 static int Open (vlc_object_t *obj, const struct gl_api *api)
139 {
140     vlc_gl_t *gl = (vlc_gl_t *)obj;
141     vout_window_t *wnd = gl->surface;
142     EGLNativeWindowType window;
143
144     vlc_gl_sys_t *sys = malloc(sizeof (*sys));
145     if (unlikely(sys == NULL))
146         return VLC_ENOMEM;
147
148     gl->sys = sys;
149     sys->display = EGL_NO_DISPLAY;
150
151 #ifdef USE_PLATFORM_X11
152     sys->x11 = NULL;
153
154     if (wnd->type != VOUT_WINDOW_TYPE_XID || !vlc_xlib_init(obj))
155         goto error;
156
157     sys->x11 = XOpenDisplay(wnd->display.x11);
158     if (sys->x11 == NULL)
159         goto error;
160
161     int snum;
162     {
163         XWindowAttributes wa;
164
165         if (!XGetWindowAttributes(sys->x11, wnd->handle.xid, &wa))
166             goto error;
167         snum = XScreenNumberOfScreen(wa.screen);
168     }
169     if (snum == XDefaultScreen(sys->x11))
170     {
171         sys->display = eglGetDisplay(sys->x11);
172         window = wnd->handle.xid;
173     }
174
175 #elif defined (USE_PLATFORM_WIN32)
176     if (wnd->type != VOUT_WINDOW_TYPE_HWND)
177         goto error;
178
179     sys->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
180     window = wnd->handle.hwnd;
181
182 #endif
183
184     if (sys->display == EGL_NO_DISPLAY)
185         goto error;
186
187     /* Initialize EGL display */
188     EGLint major, minor;
189     if (eglInitialize(sys->display, &major, &minor) != EGL_TRUE)
190         goto error;
191     msg_Dbg(obj, "EGL version %s by %s",
192             eglQueryString(sys->display, EGL_VERSION),
193             eglQueryString(sys->display, EGL_VENDOR));
194
195     const char *ext = eglQueryString(sys->display, EGL_EXTENSIONS);
196     if (*ext)
197         msg_Dbg(obj, " extensions: %s", ext);
198
199     if (major != 1 || minor < api->min_minor
200      || !CheckAPI(sys->display, api->name))
201     {
202         msg_Err(obj, "cannot select %s API", api->name);
203         goto error;
204     }
205
206     const EGLint conf_attr[] = {
207         EGL_RED_SIZE, 5,
208         EGL_GREEN_SIZE, 5,
209         EGL_BLUE_SIZE, 5,
210         EGL_RENDERABLE_TYPE, api->render_bit,
211         EGL_NONE
212     };
213     EGLConfig cfgv[1];
214     EGLint cfgc;
215
216     if (eglChooseConfig(sys->display, conf_attr, cfgv, 1, &cfgc) != EGL_TRUE
217      || cfgc == 0)
218     {
219         msg_Err (obj, "cannot choose EGL configuration");
220         goto error;
221     }
222
223     /* Create a drawing surface */
224     sys->surface = eglCreateWindowSurface(sys->display, cfgv[0], window, NULL);
225     if (sys->surface == EGL_NO_SURFACE)
226     {
227         msg_Err (obj, "cannot create EGL window surface");
228         goto error;
229     }
230
231     if (eglBindAPI (api->api) != EGL_TRUE)
232     {
233         msg_Err (obj, "cannot bind EGL API");
234         goto error;
235     }
236
237     EGLContext ctx = eglCreateContext(sys->display, cfgv[0], EGL_NO_CONTEXT,
238                                       api->attr);
239     if (ctx == EGL_NO_CONTEXT)
240     {
241         msg_Err (obj, "cannot create EGL context");
242         goto error;
243     }
244     sys->context = ctx;
245
246     /* Initialize OpenGL callbacks */
247     gl->makeCurrent = MakeCurrent;
248     gl->releaseCurrent = ReleaseCurrent;
249     gl->swap = SwapBuffers;
250     gl->getProcAddress = GetSymbol;
251     gl->lock = NULL;
252     gl->unlock = NULL;
253     return VLC_SUCCESS;
254
255 error:
256     Close (obj);
257     return VLC_EGENERIC;
258 }
259
260 static int OpenGLES2 (vlc_object_t *obj)
261 {
262     static const struct gl_api api = {
263         "OpenGL_ES", EGL_OPENGL_ES_API, 3, EGL_OPENGL_ES2_BIT,
264         { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE },
265     };
266     return Open (obj, &api);
267 }
268
269 static int OpenGLES (vlc_object_t *obj)
270 {
271     static const struct gl_api api = {
272         "OpenGL_ES", EGL_OPENGL_ES_API, 0, EGL_OPENGL_ES_BIT,
273         { EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE },
274     };
275     return Open (obj, &api);
276 }
277
278 static int OpenGL (vlc_object_t *obj)
279 {
280     static const struct gl_api api = {
281         "OpenGL", EGL_OPENGL_API, 4, EGL_OPENGL_BIT,
282         { EGL_NONE },
283     };
284     return Open (obj, &api);
285 }
286
287 static void Close (vlc_object_t *obj)
288 {
289     vlc_gl_t *gl = (vlc_gl_t *)obj;
290     vlc_gl_sys_t *sys = gl->sys;
291
292     if (sys->display != EGL_NO_DISPLAY)
293         eglTerminate(sys->display);
294 #ifdef USE_PLATFORM_X11
295     if (sys->x11 != NULL)
296         XCloseDisplay(sys->x11);
297 #endif
298     free (sys);
299 }
300
301 static int MakeCurrent (vlc_gl_t *gl)
302 {
303     vlc_gl_sys_t *sys = gl->sys;
304
305     if (eglMakeCurrent (sys->display, sys->surface, sys->surface,
306                         sys->context) != EGL_TRUE)
307         return VLC_EGENERIC;
308     return VLC_SUCCESS;
309 }
310
311 static void ReleaseCurrent (vlc_gl_t *gl)
312 {
313     vlc_gl_sys_t *sys = gl->sys;
314
315     eglMakeCurrent (sys->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
316                     EGL_NO_CONTEXT);
317 }
318
319 static void SwapBuffers (vlc_gl_t *gl)
320 {
321     vlc_gl_sys_t *sys = gl->sys;
322
323     eglSwapBuffers (sys->display, sys->surface);
324 }
325
326 static void *GetSymbol(vlc_gl_t *gl, const char *procname)
327 {
328     (void) gl;
329     return (void *)eglGetProcAddress (procname);
330 }