]> git.sesse.net Git - vlc/blob - modules/video_output/egl.c
xcb/glx: reuse the GLX provider code and eliminate a lot of code
[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 #elif defined (USE_PLATFORM_ANDROID)
213     if (wnd->type != VOUT_WINDOW_TYPE_ANDROID_NATIVE)
214         goto error;
215
216 # if USE_DEFAULT_PLATFORM
217     sys->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
218     window.native = wnd->handle.anativewindow;
219 # endif
220
221 #endif
222
223     if (sys->display == EGL_NO_DISPLAY)
224         goto error;
225
226     /* Initialize EGL display */
227     EGLint major, minor;
228     if (eglInitialize(sys->display, &major, &minor) != EGL_TRUE)
229         goto error;
230     msg_Dbg(obj, "EGL version %s by %s",
231             eglQueryString(sys->display, EGL_VERSION),
232             eglQueryString(sys->display, EGL_VENDOR));
233
234     const char *ext = eglQueryString(sys->display, EGL_EXTENSIONS);
235     if (*ext)
236         msg_Dbg(obj, " extensions: %s", ext);
237
238     if (major != 1 || minor < api->min_minor
239      || !CheckAPI(sys->display, api->name))
240     {
241         msg_Err(obj, "cannot select %s API", api->name);
242         goto error;
243     }
244
245     const EGLint conf_attr[] = {
246         EGL_RED_SIZE, 5,
247         EGL_GREEN_SIZE, 5,
248         EGL_BLUE_SIZE, 5,
249         EGL_RENDERABLE_TYPE, api->render_bit,
250         EGL_NONE
251     };
252     EGLConfig cfgv[1];
253     EGLint cfgc;
254
255     if (eglChooseConfig(sys->display, conf_attr, cfgv, 1, &cfgc) != EGL_TRUE
256      || cfgc == 0)
257     {
258         msg_Err (obj, "cannot choose EGL configuration");
259         goto error;
260     }
261
262     /* Create a drawing surface */
263 #ifdef EGL_EXT_platform_base
264     if (createSurface != NULL)
265         sys->surface = createSurface(sys->display, cfgv[0],
266                                      window.ext_platform, NULL);
267     else
268 #endif
269         sys->surface = eglCreateWindowSurface(sys->display, cfgv[0],
270                                               window.native, NULL);
271
272     if (sys->surface == EGL_NO_SURFACE)
273     {
274         msg_Err (obj, "cannot create EGL window surface");
275         goto error;
276     }
277
278     if (eglBindAPI (api->api) != EGL_TRUE)
279     {
280         msg_Err (obj, "cannot bind EGL API");
281         goto error;
282     }
283
284     EGLContext ctx = eglCreateContext(sys->display, cfgv[0], EGL_NO_CONTEXT,
285                                       api->attr);
286     if (ctx == EGL_NO_CONTEXT)
287     {
288         msg_Err (obj, "cannot create EGL context");
289         goto error;
290     }
291     sys->context = ctx;
292
293     /* Initialize OpenGL callbacks */
294     gl->makeCurrent = MakeCurrent;
295     gl->releaseCurrent = ReleaseCurrent;
296     gl->swap = SwapBuffers;
297     gl->getProcAddress = GetSymbol;
298     gl->lock = NULL;
299     gl->unlock = NULL;
300     return VLC_SUCCESS;
301
302 error:
303     Close (obj);
304     return VLC_EGENERIC;
305 }
306
307 static int OpenGLES2 (vlc_object_t *obj)
308 {
309     static const struct gl_api api = {
310         "OpenGL_ES", EGL_OPENGL_ES_API, 3, EGL_OPENGL_ES2_BIT,
311         { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE },
312     };
313     return Open (obj, &api);
314 }
315
316 static int OpenGLES (vlc_object_t *obj)
317 {
318     static const struct gl_api api = {
319         "OpenGL_ES", EGL_OPENGL_ES_API, 0, EGL_OPENGL_ES_BIT,
320         { EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE },
321     };
322     return Open (obj, &api);
323 }
324
325 static int OpenGL (vlc_object_t *obj)
326 {
327     static const struct gl_api api = {
328         "OpenGL", EGL_OPENGL_API, 4, EGL_OPENGL_BIT,
329         { EGL_NONE },
330     };
331     return Open (obj, &api);
332 }
333
334 static void Close (vlc_object_t *obj)
335 {
336     vlc_gl_t *gl = (vlc_gl_t *)obj;
337     vlc_gl_sys_t *sys = gl->sys;
338
339     if (sys->display != EGL_NO_DISPLAY)
340     {
341         if (sys->surface != EGL_NO_SURFACE)
342             eglDestroySurface(sys->display, sys->surface);
343         eglTerminate(sys->display);
344     }
345 #ifdef USE_PLATFORM_X11
346     if (sys->x11 != NULL)
347         XCloseDisplay(sys->x11);
348 #endif
349     free (sys);
350 }
351
352 static int MakeCurrent (vlc_gl_t *gl)
353 {
354     vlc_gl_sys_t *sys = gl->sys;
355
356     if (eglMakeCurrent (sys->display, sys->surface, sys->surface,
357                         sys->context) != EGL_TRUE)
358         return VLC_EGENERIC;
359     return VLC_SUCCESS;
360 }
361
362 static void ReleaseCurrent (vlc_gl_t *gl)
363 {
364     vlc_gl_sys_t *sys = gl->sys;
365
366     eglMakeCurrent (sys->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
367                     EGL_NO_CONTEXT);
368 }
369
370 static void SwapBuffers (vlc_gl_t *gl)
371 {
372     vlc_gl_sys_t *sys = gl->sys;
373
374     eglSwapBuffers (sys->display, sys->surface);
375 }
376
377 static void *GetSymbol(vlc_gl_t *gl, const char *procname)
378 {
379     (void) gl;
380     return (void *)eglGetProcAddress (procname);
381 }