]> git.sesse.net Git - vlc/blob - modules/video_output/egl.c
decoder: fix data race in input_DecoderFrameNext()
[vlc] / modules / video_output / egl.c
1 /**
2  * @file egl.c
3  * @brief EGL OpenGL extension module
4  */
5 /*****************************************************************************
6  * Copyright © 2010-2014 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 #ifdef USE_PLATFORM_WAYLAND
40 # include <wayland-egl.h>
41 #endif
42
43 typedef struct vlc_gl_sys_t
44 {
45     EGLDisplay display;
46     EGLSurface surface;
47     EGLContext context;
48 #if defined (USE_PLATFORM_X11)
49     Display *x11;
50 #endif
51 #if defined (USE_PLATFORM_WAYLAND)
52     struct wl_egl_window *window;
53     unsigned width, height;
54 #endif
55 } vlc_gl_sys_t;
56
57 static int MakeCurrent (vlc_gl_t *gl)
58 {
59     vlc_gl_sys_t *sys = gl->sys;
60
61     if (eglMakeCurrent (sys->display, sys->surface, sys->surface,
62                         sys->context) != EGL_TRUE)
63         return VLC_EGENERIC;
64     return VLC_SUCCESS;
65 }
66
67 static void ReleaseCurrent (vlc_gl_t *gl)
68 {
69     vlc_gl_sys_t *sys = gl->sys;
70
71     eglMakeCurrent (sys->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
72                     EGL_NO_CONTEXT);
73 }
74
75 #ifdef USE_PLATFORM_WAYLAND
76 static void Resize (vlc_gl_t *gl, unsigned width, unsigned height)
77 {
78     vlc_gl_sys_t *sys = gl->sys;
79
80     wl_egl_window_resize(sys->window, width, height,
81                          (sys->width - width) / 2,
82                          (sys->height - height) / 2);
83     sys->width = width;
84     sys->height = height;
85 }
86 #else
87 # define Resize (NULL)
88 #endif
89
90 static void SwapBuffers (vlc_gl_t *gl)
91 {
92     vlc_gl_sys_t *sys = gl->sys;
93
94     eglSwapBuffers (sys->display, sys->surface);
95 }
96
97 static void *GetSymbol(vlc_gl_t *gl, const char *procname)
98 {
99     (void) gl;
100     return (void *)eglGetProcAddress (procname);
101 }
102
103 static bool CheckToken(const char *haystack, const char *needle)
104 {
105     size_t len = strlen(needle);
106
107     while (haystack != NULL)
108     {
109         while (*haystack == ' ')
110             haystack++;
111         if (!strncmp(haystack, needle, len)
112          && (memchr(" ", haystack[len], 2) != NULL))
113             return true;
114
115         haystack = strchr(haystack, ' ');
116     }
117     return false;
118 }
119
120 static bool CheckAPI (EGLDisplay dpy, const char *api)
121 {
122     const char *apis = eglQueryString (dpy, EGL_CLIENT_APIS);
123     return CheckToken(apis, api);
124 }
125
126 static bool CheckClientExt(const char *name)
127 {
128     const char *exts = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
129     return CheckToken(exts, name);
130 }
131
132 struct gl_api
133 {
134    const char name[10];
135    EGLenum    api;
136    EGLint     min_minor;
137    EGLint     render_bit;
138    EGLint     attr[3];
139 };
140
141 #ifdef EGL_EXT_platform_base
142 static EGLDisplay GetDisplayEXT(EGLenum plat, void *dpy, const EGLint *attrs)
143 {
144     PFNEGLGETPLATFORMDISPLAYEXTPROC getDisplay =
145         (PFNEGLGETPLATFORMDISPLAYEXTPROC)
146         eglGetProcAddress("eglGetPlatformDisplayEXT");
147
148     assert(getDisplay != NULL);
149     return getDisplay(plat, dpy, attrs);
150 }
151
152 static EGLSurface CreateWindowSurfaceEXT(EGLDisplay dpy, EGLConfig config,
153                                          void *window, const EGLint *attrs)
154 {
155     PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC createSurface =
156         (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)
157         eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
158
159     assert(createSurface != NULL);
160     return createSurface(dpy, config, window, attrs);
161 }
162 #endif
163
164 static EGLSurface CreateWindowSurface(EGLDisplay dpy, EGLConfig config,
165                                       void *window, const EGLint *attrs)
166 {
167     EGLNativeWindowType *native = window;
168
169     return eglCreateWindowSurface(dpy, config, *native, attrs);
170 }
171
172 static void Close (vlc_object_t *obj)
173 {
174     vlc_gl_t *gl = (vlc_gl_t *)obj;
175     vlc_gl_sys_t *sys = gl->sys;
176
177     if (sys->display != EGL_NO_DISPLAY)
178     {
179         if (sys->surface != EGL_NO_SURFACE)
180             eglDestroySurface(sys->display, sys->surface);
181         eglTerminate(sys->display);
182     }
183 #ifdef USE_PLATFORM_X11
184     if (sys->x11 != NULL)
185         XCloseDisplay(sys->x11);
186 #endif
187 #ifdef USE_PLATFORM_WAYLAND
188     if (sys->window != NULL)
189         wl_egl_window_destroy(sys->window);
190 #endif
191     free (sys);
192 }
193
194 /**
195  * Probe EGL display availability
196  */
197 static int Open (vlc_object_t *obj, const struct gl_api *api)
198 {
199     vlc_gl_t *gl = (vlc_gl_t *)obj;
200     vlc_gl_sys_t *sys = malloc(sizeof (*sys));
201     if (unlikely(sys == NULL))
202         return VLC_ENOMEM;
203
204     gl->sys = sys;
205     sys->display = EGL_NO_DISPLAY;
206     sys->surface = EGL_NO_SURFACE;
207
208     vout_window_t *wnd = gl->surface;
209     EGLSurface (*createSurface)(EGLDisplay, EGLConfig, void *, const EGLint *)
210         = CreateWindowSurface;
211     void *window;
212
213 #ifdef USE_PLATFORM_X11
214     sys->x11 = NULL;
215
216     if (wnd->type != VOUT_WINDOW_TYPE_XID || !vlc_xlib_init(obj))
217         goto error;
218
219     window = &wnd->handle.xid;
220     sys->x11 = XOpenDisplay(wnd->display.x11);
221     if (sys->x11 == NULL)
222         goto error;
223
224     int snum;
225     {
226         XWindowAttributes wa;
227
228         if (!XGetWindowAttributes(sys->x11, wnd->handle.xid, &wa))
229             goto error;
230         snum = XScreenNumberOfScreen(wa.screen);
231     }
232 # ifdef EGL_EXT_platform_x11
233     if (CheckClientExt("EGL_EXT_platform_x11"))
234     {
235         const EGLint attrs[] = {
236             EGL_PLATFORM_X11_SCREEN_EXT, snum,
237             EGL_NONE
238         };
239         sys->display = GetDisplayEXT(EGL_PLATFORM_X11_EXT, sys->x11, attrs);
240         createSurface = CreateWindowSurfaceEXT;
241     }
242     else
243 # endif
244     {
245 # ifdef __unix__
246         if (snum == XDefaultScreen(sys->x11))
247             sys->display = eglGetDisplay(sys->x11);
248     }
249 # endif
250
251 #elif defined (USE_PLATFORM_WAYLAND)
252     sys->window = NULL;
253
254     if (wnd->type != VOUT_WINDOW_TYPE_WAYLAND)
255         goto error;
256
257 # ifdef EGL_EXT_platform_wayland
258     if (!CheckClientExt("EGL_EXT_platform_wayland"))
259         goto error;
260
261     /* Resize() should be called with the proper size before Swap() */
262     window = wl_egl_window_create(wnd->handle.wl, 1, 1);
263     if (window == NULL)
264         goto error;
265     sys->window = window;
266
267     sys->display = GetDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, wnd->display.wl,
268                                  NULL);
269     createSurface = CreateWindowSurfaceEXT;
270
271 # endif
272
273 #elif defined (USE_PLATFORM_WIN32)
274     if (wnd->type != VOUT_WINDOW_TYPE_HWND)
275         goto error;
276
277     window = &wnd->handle.hwnd;
278 # if defined (_WIN32) || defined (__VC32__) \
279   && !defined (__CYGWIN__) && !defined (__SCITECH_SNAP__)
280     sys->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
281 # endif
282
283 #elif defined (USE_PLATFORM_ANDROID)
284     if (wnd->type != VOUT_WINDOW_TYPE_ANDROID_NATIVE)
285         goto error;
286
287     window = &wnd->handle.anativewindow;
288 # if defined (__ANDROID__) || defined (ANDROID)
289     sys->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
290 # endif
291
292 #endif
293
294     if (sys->display == EGL_NO_DISPLAY)
295         goto error;
296
297     /* Initialize EGL display */
298     EGLint major, minor;
299     if (eglInitialize(sys->display, &major, &minor) != EGL_TRUE)
300         goto error;
301     msg_Dbg(obj, "EGL version %s by %s",
302             eglQueryString(sys->display, EGL_VERSION),
303             eglQueryString(sys->display, EGL_VENDOR));
304
305     const char *ext = eglQueryString(sys->display, EGL_EXTENSIONS);
306     if (*ext)
307         msg_Dbg(obj, " extensions: %s", ext);
308
309     if (major != 1 || minor < api->min_minor
310      || !CheckAPI(sys->display, api->name))
311     {
312         msg_Err(obj, "cannot select %s API", api->name);
313         goto error;
314     }
315
316     const EGLint conf_attr[] = {
317         EGL_RED_SIZE, 5,
318         EGL_GREEN_SIZE, 5,
319         EGL_BLUE_SIZE, 5,
320         EGL_RENDERABLE_TYPE, api->render_bit,
321         EGL_NONE
322     };
323     EGLConfig cfgv[1];
324     EGLint cfgc;
325
326     if (eglChooseConfig(sys->display, conf_attr, cfgv, 1, &cfgc) != EGL_TRUE
327      || cfgc == 0)
328     {
329         msg_Err (obj, "cannot choose EGL configuration");
330         goto error;
331     }
332
333     /* Create a drawing surface */
334     sys->surface = createSurface(sys->display, cfgv[0], window, NULL);
335     if (sys->surface == EGL_NO_SURFACE)
336     {
337         msg_Err (obj, "cannot create EGL window surface");
338         goto error;
339     }
340
341     if (eglBindAPI (api->api) != EGL_TRUE)
342     {
343         msg_Err (obj, "cannot bind EGL API");
344         goto error;
345     }
346
347     EGLContext ctx = eglCreateContext(sys->display, cfgv[0], EGL_NO_CONTEXT,
348                                       api->attr);
349     if (ctx == EGL_NO_CONTEXT)
350     {
351         msg_Err (obj, "cannot create EGL context");
352         goto error;
353     }
354     sys->context = ctx;
355
356     /* Initialize OpenGL callbacks */
357     gl->makeCurrent = MakeCurrent;
358     gl->releaseCurrent = ReleaseCurrent;
359     gl->resize = Resize;
360     gl->swap = SwapBuffers;
361     gl->getProcAddress = GetSymbol;
362     return VLC_SUCCESS;
363
364 error:
365     Close (obj);
366     return VLC_EGENERIC;
367 }
368
369 static int OpenGLES2 (vlc_object_t *obj)
370 {
371     static const struct gl_api api = {
372         "OpenGL_ES", EGL_OPENGL_ES_API, 3, EGL_OPENGL_ES2_BIT,
373         { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE },
374     };
375     return Open (obj, &api);
376 }
377
378 static int OpenGLES (vlc_object_t *obj)
379 {
380     static const struct gl_api api = {
381         "OpenGL_ES", EGL_OPENGL_ES_API, 0, EGL_OPENGL_ES_BIT,
382         { EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE },
383     };
384     return Open (obj, &api);
385 }
386
387 static int OpenGL (vlc_object_t *obj)
388 {
389     static const struct gl_api api = {
390         "OpenGL", EGL_OPENGL_API, 4, EGL_OPENGL_BIT,
391         { EGL_NONE },
392     };
393     return Open (obj, &api);
394 }
395
396 vlc_module_begin ()
397     set_shortname (N_("EGL"))
398     set_description (N_("EGL extension for OpenGL"))
399     set_category (CAT_VIDEO)
400     set_subcategory (SUBCAT_VIDEO_VOUT)
401     set_capability ("opengl", 50)
402     set_callbacks (OpenGL, Close)
403     add_shortcut ("egl")
404
405     add_submodule ()
406     set_capability ("opengl es2", 50)
407     set_callbacks (OpenGLES2, Close)
408     add_shortcut ("egl")
409
410     add_submodule ()
411     set_capability ("opengl es", 50)
412     set_callbacks (OpenGLES, Close)
413     add_shortcut ("egl")
414
415 vlc_module_end ()