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