]> git.sesse.net Git - vlc/blob - modules/video_output/opengl.h
Use var_InheritString for --decklink-video-connection.
[vlc] / modules / video_output / opengl.h
1 /*****************************************************************************
2  * opengl.h: OpenGL vout_display helpers
3  *****************************************************************************
4  * Copyright (C) 2004 the VideoLAN team
5  * Copyright (C) 2009 Laurent Aimar
6  * $Id$
7  *
8  * Authors: Cyril Deguet <asmax@videolan.org>
9  *          Gildas Bazin <gbazin@videolan.org>
10  *          Eric Petit <titer@m0k.org>
11  *          Cedric Cocquebert <cedric.cocquebert@supelec.fr>
12  *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27  *****************************************************************************/
28
29 #include <vlc_common.h>
30 #include <vlc_picture_pool.h>
31 #include <vlc_vout_opengl.h>
32
33 #ifdef __APPLE__
34 # include <OpenGL/gl.h>
35 # include <OpenGL/glext.h>
36 #else
37 # include <GL/gl.h>
38 #endif
39
40 #ifndef YCBCR_MESA
41 # define YCBCR_MESA 0x8757
42 #endif
43 #ifndef UNSIGNED_SHORT_8_8_MESA
44 # define UNSIGNED_SHORT_8_8_MESA 0x85BA
45 #endif
46 /* RV16 */
47 #ifndef GL_UNSIGNED_SHORT_5_6_5
48 # define GL_UNSIGNED_SHORT_5_6_5 0x8363
49 #endif
50 #ifndef GL_CLAMP_TO_EDGE
51 # define GL_CLAMP_TO_EDGE 0x812F
52 #endif
53
54 #ifdef __APPLE__
55 /* On OS X, use GL_TEXTURE_RECTANGLE_EXT instead of GL_TEXTURE_2D.
56    This allows sizes which are not powers of 2 */
57 # define VLCGL_TARGET GL_TEXTURE_RECTANGLE_EXT
58
59 /* OS X OpenGL supports YUV. Hehe. */
60 # define VLCGL_FORMAT GL_YCBCR_422_APPLE
61 # define VLCGL_TYPE   GL_UNSIGNED_SHORT_8_8_APPLE
62
63 # define VLCGL_TEXTURE_COUNT (2)
64 #else
65
66 # define VLCGL_TARGET GL_TEXTURE_2D
67
68 /* RV32 */
69 # define VLCGL_RGB_FORMAT GL_RGBA
70 # define VLCGL_RGB_TYPE GL_UNSIGNED_BYTE
71
72 /* YUY2 */
73 # define VLCGL_YUV_FORMAT YCBCR_MESA
74 # define VLCGL_YUV_TYPE UNSIGNED_SHORT_8_8_MESA
75
76 /* Use RGB on Win32/GLX */
77 # define VLCGL_FORMAT VLCGL_RGB_FORMAT
78 # define VLCGL_TYPE   VLCGL_RGB_TYPE
79
80 # define VLCGL_TEXTURE_COUNT (1)
81 #endif
82
83 static inline int GetAlignedSize(int i_size)
84 {
85     /* Return the nearest power of 2 */
86     int i_result = 1;
87     while(i_result < i_size)
88         i_result *= 2;
89
90     return i_result;
91 }
92
93 typedef struct {
94         vout_opengl_t  *gl;
95
96     video_format_t fmt;
97
98     int        tex_pixel_size;
99     int        tex_width;
100     int        tex_height;
101
102     GLuint     texture[VLCGL_TEXTURE_COUNT];
103     uint8_t    *buffer[VLCGL_TEXTURE_COUNT];
104
105     picture_pool_t *pool;
106 } vout_display_opengl_t;
107
108 static int vout_display_opengl_Init(vout_display_opengl_t *vgl,
109                                     video_format_t *fmt,
110                                     vout_opengl_t *gl)
111 {
112         vgl->gl = gl;
113
114     /* Find the chroma we will use and update fmt */
115     /* TODO: We use YCbCr on Mac which is Y422, but on OSX it seems to == YUY2. Verify */
116 #if (defined(WORDS_BIGENDIAN) && VLCGL_FORMAT == GL_YCBCR_422_APPLE) || (VLCGL_FORMAT == YCBCR_MESA)
117     fmt->i_chroma = VLC_CODEC_YUYV;
118     vgl->tex_pixel_size = 2;
119 #elif defined(GL_YCBCR_422_APPLE) && (VLCGL_FORMAT == GL_YCBCR_422_APPLE)
120     fmt->i_chroma = VLC_CODEC_UYVY;
121     vgl->tex_pixel_size = 2;
122 #elif VLCGL_FORMAT == GL_RGB
123 #   if VLCGL_TYPE == GL_UNSIGNED_BYTE
124     fmt->i_chroma = VLC_CODEC_RGB24;
125 #       if defined(WORDS_BIGENDIAN)
126     fmt->i_rmask = 0x00ff0000;
127     fmt->i_gmask = 0x0000ff00;
128     fmt->i_bmask = 0x000000ff;
129 #       else
130     fmt->i_rmask = 0x000000ff;
131     fmt->i_gmask = 0x0000ff00;
132     fmt->i_bmask = 0x00ff0000;
133 #       endif
134     vgl->tex_pixel_size = 3;
135 #   else
136     fmt->i_chroma = VLC_CODEC_RGB16;
137 #       if defined(WORDS_BIGENDIAN)
138     fmt->i_rmask = 0x001f;
139     fmt->i_gmask = 0x07e0;
140     fmt->i_bmask = 0xf800;
141 #       else
142     fmt->i_rmask = 0xf800;
143     fmt->i_gmask = 0x07e0;
144     fmt->i_bmask = 0x001f;
145 #       endif
146     vgl->tex_pixel_size = 2;
147 #   endif
148 #else
149     fmt->i_chroma = VLC_CODEC_RGB32;
150 #       if defined(WORDS_BIGENDIAN)
151     fmt->i_rmask = 0xff000000;
152     fmt->i_gmask = 0x00ff0000;
153     fmt->i_bmask = 0x0000ff00;
154 #       else
155     fmt->i_rmask = 0x000000ff;
156     fmt->i_gmask = 0x0000ff00;
157     fmt->i_bmask = 0x00ff0000;
158 #       endif
159     vgl->tex_pixel_size = 4;
160 #endif
161
162     vgl->fmt = *fmt;
163
164     /* Texture size */
165 #ifdef __APPLE__
166     vgl->tex_width  = fmt->i_width;
167     vgl->tex_height = fmt->i_height;
168 #else
169     /* A texture must have a size aligned on a power of 2 */
170     vgl->tex_width  = GetAlignedSize(fmt->i_width);
171     vgl->tex_height = GetAlignedSize(fmt->i_height);
172 #endif
173
174     /* */
175         for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
176                 vgl->texture[i] = 0;
177                 vgl->buffer[i]  = NULL;
178         }
179     vgl->pool = NULL;
180
181     /* */
182     if (!vout_opengl_Lock(vgl->gl)) {
183
184         glDisable(GL_BLEND);
185         glDisable(GL_DEPTH_TEST);
186         glDepthMask(GL_FALSE);
187         glDisable(GL_CULL_FACE);
188         glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
189         glClear(GL_COLOR_BUFFER_BIT);
190
191         vout_opengl_Unlock(vgl->gl);
192     }
193         return VLC_SUCCESS;
194 }
195 static void vout_display_opengl_Clean(vout_display_opengl_t *vgl)
196 {
197     /* */
198     if (!vout_opengl_Lock(vgl->gl)) {
199
200         glFinish();
201         glFlush();
202         glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
203
204         vout_opengl_Unlock(vgl->gl);
205     }
206     if (vgl->pool) {
207         picture_pool_Delete(vgl->pool);
208         for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++)
209             free(vgl->buffer[i]);
210     }
211 }
212
213 static int vout_display_opengl_ResetTextures(vout_display_opengl_t *vgl)
214 {
215     if (vout_opengl_Lock(vgl->gl))
216         return VLC_EGENERIC;
217
218     glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
219
220     glGenTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
221     for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
222         glBindTexture(VLCGL_TARGET, vgl->texture[i]);
223
224         /* Set the texture parameters */
225         glTexParameterf(VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0);
226
227         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
228         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
229
230         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
231         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
232
233         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
234
235 #ifdef __APPLE__
236         /* Tell the driver not to make a copy of the texture but to use
237            our buffer */
238         glEnable(GL_UNPACK_CLIENT_STORAGE_APPLE);
239         glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
240
241 #if 0
242         /* Use VRAM texturing */
243         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
244                          GL_STORAGE_CACHED_APPLE);
245 #else
246         /* Use AGP texturing */
247         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
248                          GL_STORAGE_SHARED_APPLE);
249 #endif
250 #endif
251
252         /* Call glTexImage2D only once, and use glTexSubImage2D later */
253         if (vgl->buffer[i])
254             glTexImage2D(VLCGL_TARGET, 0, 3, vgl->tex_width, vgl->tex_height,
255                          0, VLCGL_FORMAT, VLCGL_TYPE, vgl->buffer[i]);
256     }
257
258     vout_opengl_Unlock(vgl->gl);
259     return VLC_SUCCESS;
260 }
261
262 #ifdef __APPLE__
263 /* XXX See comment vout_display_opengl_Prepare */
264 struct picture_sys_t {
265     vout_display_opengl_t *vgl;
266     GLuint *texture;
267 };
268
269 /* Small helper */
270 static inline GLuint get_texture(picture_t *picture)
271 {
272     return *picture->p_sys->texture;
273 }
274
275 static int PictureLock(picture_t *picture)
276 {
277     if (!picture->p_sys)
278         return VLC_SUCCESS;
279
280     vout_display_opengl_t *vgl = picture->p_sys->vgl;
281     if (!vout_opengl_Lock(vgl->gl)) {
282
283         glBindTexture(VLCGL_TARGET, get_texture(picture));
284         glTexSubImage2D(VLCGL_TARGET, 0, 0, 0,
285                         vgl->fmt.i_width, vgl->fmt.i_height,
286                         VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
287
288         vout_opengl_Unlock(vgl->gl);
289     }
290     return VLC_SUCCESS;
291 }
292 static void PictureUnlock(picture_t *picture)
293 {
294     VLC_UNUSED(picture);
295 }
296 #endif
297
298 static picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl)
299 {
300     picture_t *picture[VLCGL_TEXTURE_COUNT];
301
302     int i;
303     for (i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
304
305         /* TODO memalign would be way better */
306         vgl->buffer[i] = malloc(vgl->tex_width * vgl->tex_height * vgl->tex_pixel_size);
307         if (!vgl->buffer[i])
308             break;
309
310         picture_resource_t rsc;
311         memset(&rsc, 0, sizeof(rsc));
312 #ifdef __APPLE__
313         rsc.p_sys = malloc(sizeof(*rsc.p_sys));
314         if (rsc.p_sys)
315         {
316             rsc.p_sys->vgl = vgl;
317             rsc.p_sys->texture = &vgl->texture[i];
318         }
319 #endif
320         rsc.p[0].p_pixels = vgl->buffer[i];
321         rsc.p[0].i_pitch  = vgl->fmt.i_width * vgl->tex_pixel_size;
322         rsc.p[0].i_lines  = vgl->fmt.i_height;
323
324         picture[i] = picture_NewFromResource(&vgl->fmt, &rsc);
325         if (!picture[i]) {
326             free(vgl->buffer[i]);
327             vgl->buffer[i] = NULL;
328             break;
329         }
330     }
331     if (i < VLCGL_TEXTURE_COUNT)
332         goto error;
333
334     /* */
335     picture_pool_configuration_t cfg;
336     memset(&cfg, 0, sizeof(cfg));
337     cfg.picture_count = i;
338     cfg.picture = picture;
339 #ifdef __APPLE__
340     cfg.lock = PictureLock;
341     cfg.unlock = PictureUnlock;
342 #endif
343     vgl->pool = picture_pool_NewExtended(&cfg);
344     if (!vgl->pool)
345         goto error;
346
347     vout_display_opengl_ResetTextures(vgl);
348
349     return vgl->pool;
350
351 error:
352     for (int j = 0; j < i; j++) {
353         picture_Delete(picture[j]);
354         vgl->buffer[j] = NULL;
355     }
356     return NULL;
357 }
358
359 static int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
360                                        picture_t *picture)
361 {
362     /* On Win32/GLX, we do this the usual way:
363        + Fill the buffer with new content,
364        + Reload the texture,
365        + Use the texture.
366
367        On OS X with VRAM or AGP texturing, the order has to be:
368        + Reload the texture,
369        + Fill the buffer with new content,
370        + Use the texture.
371
372        (Thanks to gcc from the Arstechnica forums for the tip)
373
374        Therefore on OSX, we have to use two buffers and textures and use a
375        lock(/unlock) managed picture pool.
376      */
377
378     if (vout_opengl_Lock(vgl->gl))
379         return VLC_EGENERIC;
380
381 #ifdef __APPLE__
382     /* Bind to the texture for drawing */
383     glBindTexture(VLCGL_TARGET, get_texture(picture));
384 #else
385     /* Update the texture */
386     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
387                     vgl->fmt.i_width, vgl->fmt.i_height,
388                     VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
389 #endif
390
391     vout_opengl_Unlock(vgl->gl);
392     return VLC_SUCCESS;
393 }
394
395 static int vout_display_opengl_Display(vout_display_opengl_t *vgl,
396                                        const video_format_t *source)
397 {
398     if (vout_opengl_Lock(vgl->gl))
399         return VLC_EGENERIC;
400
401     /* glTexCoord works differently with GL_TEXTURE_2D and
402        GL_TEXTURE_RECTANGLE_EXT */
403 #if VLCGL_TARGET == GL_TEXTURE_2D
404     const float f_normw = vgl->tex_width;
405     const float f_normh = vgl->tex_height;
406 #else
407     assert(VLCGL_TARGET == GL_TEXTURE_RECTANGLE_EXT);
408     const float f_normw = 1.0;
409     const float f_normh = 1.0;
410 #endif
411
412     float f_x      = (source->i_x_offset +                       0 ) / f_normw;
413     float f_y      = (source->i_y_offset +                       0 ) / f_normh;
414     float f_width  = (source->i_x_offset + source->i_visible_width ) / f_normw;
415     float f_height = (source->i_y_offset + source->i_visible_height) / f_normh;
416
417     /* Why drawing here and not in Render()? Because this way, the
418        OpenGL providers can call vout_display_opengl_Display to force redraw.i
419        Currently, the OS X provider uses it to get a smooth window resizing */
420
421     glClear(GL_COLOR_BUFFER_BIT);
422
423     glEnable(VLCGL_TARGET);
424
425     glBegin(GL_POLYGON);
426     glTexCoord2f(f_x,      f_y);      glVertex2f(-1.0,  1.0);
427     glTexCoord2f(f_width,  f_y);      glVertex2f( 1.0,  1.0);
428     glTexCoord2f(f_width,  f_height); glVertex2f( 1.0, -1.0);
429     glTexCoord2f(f_x,      f_height); glVertex2f(-1.0, -1.0);
430     glEnd();
431
432     glDisable(VLCGL_TARGET);
433
434     vout_opengl_Swap(vgl->gl);
435
436     vout_opengl_Unlock(vgl->gl);
437     return VLC_SUCCESS;
438 }
439