1 /*****************************************************************************
2 * opengl.c: VLC GL API
3 *****************************************************************************
4 * Copyright (C) 2011 Rémi Denis-Courmont
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include <assert.h>
26 #include <stdlib.h>
27
28 #include <vlc_common.h>
29 #include <vlc_atomic.h>
30 #include <vlc_opengl.h>
31 #include "libvlc.h"
32 #include <vlc_modules.h>
33
34 struct vlc_gl_priv_t
35 {
36 vlc_gl_t gl;
37 atomic_uint ref_count;
38 };
39 #undef vlc_gl_Create
40 /**
41 * Creates an OpenGL context (and its underlying surface).
42 *
43 * @note In most cases, you should vlc_gl_MakeCurrent() afterward.
44 *
45 * @param wnd window to use as OpenGL surface
46 * @param flags OpenGL context type
47 * @param name module name (or NULL for auto)
48 * @return a new context, or NULL on failure
49 */
vlc_gl_Create(struct vout_window_t * wnd,unsigned flags,const char * name)50 vlc_gl_t *vlc_gl_Create(struct vout_window_t *wnd, unsigned flags,
51 const char *name)
52 {
53 vlc_object_t *parent = (vlc_object_t *)wnd;
54 struct vlc_gl_priv_t *glpriv;
55 const char *type;
56
57 switch (flags /*& VLC_OPENGL_API_MASK*/)
58 {
59 case VLC_OPENGL:
60 type = "opengl";
61 break;
62 case VLC_OPENGL_ES2:
63 type = "opengl es2";
64 break;
65 default:
66 return NULL;
67 }
68
69 glpriv = vlc_custom_create(parent, sizeof (*glpriv), "gl");
70 if (unlikely(glpriv == NULL))
71 return NULL;
72
73 glpriv->gl.surface = wnd;
74 glpriv->gl.module = module_need(&glpriv->gl, type, name, true);
75 if (glpriv->gl.module == NULL)
76 {
77 vlc_object_release(&glpriv->gl);
78 return NULL;
79 }
80 atomic_init(&glpriv->ref_count, 1);
81
82 return &glpriv->gl;
83 }
84
vlc_gl_Hold(vlc_gl_t * gl)85 void vlc_gl_Hold(vlc_gl_t *gl)
86 {
87 struct vlc_gl_priv_t *glpriv = (struct vlc_gl_priv_t *)gl;
88 atomic_fetch_add(&glpriv->ref_count, 1);
89 }
90
vlc_gl_Release(vlc_gl_t * gl)91 void vlc_gl_Release(vlc_gl_t *gl)
92 {
93 struct vlc_gl_priv_t *glpriv = (struct vlc_gl_priv_t *)gl;
94 if (atomic_fetch_sub(&glpriv->ref_count, 1) != 1)
95 return;
96 module_unneed(gl, gl->module);
97 vlc_object_release(gl);
98 }
99
100 #include <vlc_vout_window.h>
101
102 typedef struct vlc_gl_surface
103 {
104 int width;
105 int height;
106 vlc_mutex_t lock;
107 } vlc_gl_surface_t;
108
vlc_gl_surface_ResizeNotify(vout_window_t * surface,unsigned width,unsigned height)109 static void vlc_gl_surface_ResizeNotify(vout_window_t *surface,
110 unsigned width, unsigned height)
111 {
112 vlc_gl_surface_t *sys = surface->owner.sys;
113
114 msg_Dbg(surface, "resized to %ux%u", width, height);
115
116 vlc_mutex_lock(&sys->lock);
117 sys->width = width;
118 sys->height = height;
119 vlc_mutex_unlock(&sys->lock);
120 }
121
vlc_gl_surface_Create(vlc_object_t * obj,const vout_window_cfg_t * cfg,struct vout_window_t ** restrict wp)122 vlc_gl_t *vlc_gl_surface_Create(vlc_object_t *obj,
123 const vout_window_cfg_t *cfg,
124 struct vout_window_t **restrict wp)
125 {
126 vlc_gl_surface_t *sys = malloc(sizeof (*sys));
127 if (unlikely(sys == NULL))
128 return NULL;
129
130 sys->width = cfg->width;
131 sys->height = cfg->height;
132 vlc_mutex_init(&sys->lock);
133
134 vout_window_owner_t owner = {
135 .sys = sys,
136 .resized = vlc_gl_surface_ResizeNotify,
137 };
138
139 vout_window_t *surface = vout_window_New(obj, "$window", cfg, &owner);
140 if (surface == NULL)
141 goto error;
142 if (wp != NULL)
143 *wp = surface;
144
145 /* TODO: support ES? */
146 vlc_gl_t *gl = vlc_gl_Create(surface, VLC_OPENGL, NULL);
147 if (gl == NULL) {
148 vout_window_Delete(surface);
149 return NULL;
150 }
151
152 vlc_gl_Resize(gl, cfg->width, cfg->height);
153 return gl;
154
155 error:
156 vlc_mutex_destroy(&sys->lock);
157 free(sys);
158 return NULL;
159 }
160
161 /**
162 * Checks if the dimensions of the surface used by the OpenGL context have
163 * changed (since the previous call), and the OpenGL viewport should be
164 * updated.
165 * \return true if at least one dimension has changed, false otherwise
166 * \warning This function is intrinsically race-prone.
167 * The dimensions can change asynchronously.
168 */
vlc_gl_surface_CheckSize(vlc_gl_t * gl,unsigned * restrict width,unsigned * restrict height)169 bool vlc_gl_surface_CheckSize(vlc_gl_t *gl, unsigned *restrict width,
170 unsigned *restrict height)
171 {
172 vout_window_t *surface = gl->surface;
173 vlc_gl_surface_t *sys = surface->owner.sys;
174 bool ret = false;
175
176 vlc_mutex_lock(&sys->lock);
177 if (sys->width >= 0 && sys->height >= 0)
178 {
179 *width = sys->width;
180 *height = sys->height;
181 sys->width = -1;
182 sys->height = -1;
183
184 vlc_gl_Resize(gl, *width, *height);
185 ret = true;
186 }
187 vlc_mutex_unlock(&sys->lock);
188 return ret;
189 }
190
vlc_gl_surface_Destroy(vlc_gl_t * gl)191 void vlc_gl_surface_Destroy(vlc_gl_t *gl)
192 {
193 vout_window_t *surface = gl->surface;
194 vlc_gl_surface_t *sys = surface->owner.sys;
195
196 vlc_gl_Release(gl);
197 vout_window_Delete(surface);
198 vlc_mutex_destroy(&sys->lock);
199 free(sys);
200 }
201