]> git.sesse.net Git - vlc/blob - modules/video_output/glx.c
GLX: add OpenGL provider plugin
[vlc] / modules / video_output / glx.c
1 /**
2  * @file glx.c
3  * @brief GLX OpenGL extension module
4  */
5 /*****************************************************************************
6  * Copyright © 2010-2012 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 <GL/glx.h>
30 #include <GL/glxext.h>
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_opengl.h>
35 #include <vlc_vout_window.h>
36 #include <vlc_xlib.h>
37
38 static int Open (vlc_object_t *);
39 static void Close (vlc_object_t *);
40
41 vlc_module_begin ()
42     set_shortname (N_("GLX"))
43     set_description (N_("GLX extension for OpenGL"))
44     set_category (CAT_VIDEO)
45     set_subcategory (SUBCAT_VIDEO_VOUT)
46     set_capability ("opengl", 20)
47     set_callbacks (Open, Close)
48 vlc_module_end ()
49
50 typedef struct vlc_gl_sys_t
51 {
52     Display *display;
53     GLXWindow win;
54     GLXContext ctx;
55 } vlc_gl_sys_t;
56
57 static int MakeCurrent (vlc_gl_t *);
58 static void SwapBuffers (vlc_gl_t *);
59 static void *GetSymbol(vlc_gl_t *, const char *);
60
61 static bool CheckGLX (vlc_object_t *vd, Display *dpy)
62 {
63     int major, minor;
64     bool ok = false;
65
66     if (!glXQueryVersion (dpy, &major, &minor))
67         msg_Dbg (vd, "GLX extension not available");
68     else
69     if (major != 1)
70         msg_Dbg (vd, "GLX extension version %d.%d unknown", major, minor);
71     else
72     if (minor < 3)
73         msg_Dbg (vd, "GLX extension version %d.%d too old", major, minor);
74     else
75     {
76         msg_Dbg (vd, "using GLX extension version %d.%d", major, minor);
77         ok = true;
78     }
79     return ok;
80 }
81
82 static bool CheckGLXext (Display *dpy, unsigned snum, const char *ext)
83 {
84     const char *exts = glXQueryExtensionsString (dpy, snum);
85     const size_t extlen = strlen (ext);
86
87     while (*exts)
88     {
89         exts += strspn (exts, " ");
90
91         size_t len = strcspn (exts, " ");
92         if (len == extlen && !memcmp (exts, ext, extlen))
93             return true;
94         exts += len;
95     }
96     return false;
97 }
98
99 static int Open (vlc_object_t *obj)
100 {
101     vlc_gl_t *gl = (vlc_gl_t *)obj;
102
103     if (!vlc_xlib_init (obj))
104         return VLC_EGENERIC;
105
106     /* Initialize GLX display */
107     Display *dpy = XOpenDisplay (gl->surface->display.x11);
108     if (dpy == NULL)
109         return VLC_EGENERIC;
110
111     vlc_gl_sys_t *sys = malloc (sizeof (*sys));
112     if (unlikely(sys == NULL))
113     {
114         XCloseDisplay (dpy);
115         return VLC_ENOMEM;
116     }
117     gl->sys = sys;
118     sys->display = dpy;
119
120     if (!CheckGLX (obj, dpy))
121         goto error;
122
123     /* Determine our pixel format */
124     XWindowAttributes wa;
125     if (!XGetWindowAttributes (dpy, gl->surface->handle.xid, &wa))
126         goto error;
127
128     const int snum = XScreenNumberOfScreen (wa.screen);
129     const VisualID visual = XVisualIDFromVisual (wa.visual);
130     static const int attr[] = {
131         GLX_RED_SIZE, 5,
132         GLX_GREEN_SIZE, 5,
133         GLX_BLUE_SIZE, 5,
134         GLX_DOUBLEBUFFER, True,
135         GLX_X_RENDERABLE, True,
136         GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
137         None
138     };
139
140     int nelem;
141     GLXFBConfig *confs = glXChooseFBConfig (dpy, snum, attr, &nelem);
142     if (confs == NULL)
143     {
144         msg_Err (obj, "cannot choose GLX frame buffer configurations");
145         goto error;
146     }
147
148     GLXFBConfig conf;
149     bool found = false;
150     for (int i = 0; i < nelem && !found; i++)
151     {
152         conf = confs[i];
153
154         XVisualInfo *vi = glXGetVisualFromFBConfig (dpy, conf);
155         if (vi == NULL)
156             continue;
157
158         if (vi->visualid == visual)
159             found = true;
160         XFree (vi);
161     }
162     XFree (confs);
163
164     if (!found)
165     {
166         msg_Err (obj, "cannot match GLX frame buffer configuration");
167         goto error;
168     }
169
170     /* Create a drawing surface */
171     sys->win = glXCreateWindow (dpy, conf, gl->surface->handle.xid, NULL);
172     if (sys->win == None)
173     {
174         msg_Err (obj, "cannot create GLX window");
175         goto error;
176     }
177
178     /* Create an OpenGL context */
179     sys->ctx = glXCreateNewContext (dpy, conf, GLX_RGBA_TYPE, NULL, True);
180     if (sys->ctx == NULL)
181     {
182         glXDestroyWindow (dpy, sys->win);
183         msg_Err (obj, "cannot create GLX context");
184         goto error;
185     }
186
187 #ifdef GLX_ARB_get_proc_address
188     bool is_swap_interval_set = false;
189 # ifdef GLX_SGI_swap_control
190     if (!is_swap_interval_set
191      && CheckGLXext (dpy, snum, "GLX_SGI_swap_control"))
192     {
193         PFNGLXSWAPINTERVALSGIPROC SwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)
194             glXGetProcAddressARB ((const GLubyte *)"glXSwapIntervalSGI");
195         assert (SwapIntervalSGI != NULL);
196         is_swap_interval_set = !SwapIntervalSGI (1);
197     }
198 # endif
199 # ifdef GLX_EXT_swap_control
200     if (!is_swap_interval_set
201      && CheckGLXext (dpy, snum, "GLX_EXT_swap_control"))
202     {
203         PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)
204             glXGetProcAddress ((const GLubyte *)"glXSwapIntervalEXT");
205         assert (SwapIntervalEXT != NULL);
206         SwapIntervalEXT (dpy, sys->win, 1);
207         is_swap_interval_set = true;
208     }
209 # endif
210 #endif
211
212     /* Initialize OpenGL callbacks */
213     gl->sys = sys;
214     gl->makeCurrent = MakeCurrent;
215     gl->swap = SwapBuffers;
216     gl->getProcAddress = GetSymbol;
217     gl->lock = NULL;
218     gl->unlock = NULL;
219     return VLC_SUCCESS;
220
221 error:
222     XCloseDisplay (dpy);
223     free (sys);
224     return VLC_EGENERIC;
225 }
226
227 static void Close (vlc_object_t *obj)
228 {
229     vlc_gl_t *gl = (vlc_gl_t *)obj;
230     vlc_gl_sys_t *sys = gl->sys;
231     Display *dpy = sys->display;
232
233     glXDestroyContext (dpy, sys->ctx);
234     glXDestroyWindow (dpy, sys->win);
235     XCloseDisplay (dpy);
236     free (sys);
237 }
238
239 static int MakeCurrent (vlc_gl_t *gl)
240 {
241     vlc_gl_sys_t *sys = gl->sys;
242
243     if (!glXMakeContextCurrent (sys->display, sys->win, sys->win, sys->ctx))
244         return VLC_EGENERIC;
245     return VLC_SUCCESS;
246 }
247
248 static void SwapBuffers (vlc_gl_t *gl)
249 {
250     vlc_gl_sys_t *sys = gl->sys;
251
252     glXSwapBuffers (sys->display, sys->win);
253 }
254
255 static void *GetSymbol(vlc_gl_t *gl, const char *procname)
256 {
257     (void) gl;
258 #ifdef GLX_ARB_get_proc_address
259     return glXGetProcAddressARB ((const GLubyte *)procname);
260 #else
261     return NULL;
262 #endif
263 }