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