]> git.sesse.net Git - vlc/blob - modules/video_output/opengl.h
opengl: Fix a typo that lead to a crash.
[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 static int PictureLock(picture_t *picture)
269 {
270     if (!picture->p_sys)
271         return VLC_SUCCESS;
272
273     vout_display_opengl_t *vgl = picture->p_sys->vgl;
274     if (!vout_opengl_Lock(vgl->gl)) {
275
276         glBindTexture(VLCGL_TARGET, picture->p_sys->texture);
277         glTexSubImage2D(VLCGL_TARGET, 0, 0, 0,
278                         vgl->fmt.i_width, vgl->fmt.i_height,
279                         VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
280
281         vout_opengl_Unlock(vgl->gl);
282     }
283     return VLC_SUCCESS;
284 }
285 static void PictureUnlock(picture_t *picture)
286 {
287     VLC_UNUSED(picture);
288 }
289 #endif
290
291 static picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl)
292 {
293     picture_t *picture[VLCGL_TEXTURE_COUNT];
294
295     int i;
296     for (i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
297
298         /* TODO memalign would be way better */
299         vgl->buffer[i] = malloc(vgl->tex_width * vgl->tex_height * vgl->tex_pixel_size);
300         if (!vgl->buffer[i])
301             break;
302
303         picture_resource_t rsc;
304         memset(&rsc, 0, sizeof(rsc));
305 #ifdef __APPLE__
306         rsc.p_sys = malloc(sizeof(*rsc.p_sys));
307         if (rsc.p_sys)
308             rsc.p_sys->vgl = vgl;
309 #endif
310         rsc.p[0].p_pixels = vgl->buffer[i];
311         rsc.p[0].i_pitch  = vgl->fmt.i_width * vgl->tex_pixel_size;
312         rsc.p[0].i_lines  = vgl->fmt.i_height;
313
314         picture[i] = picture_NewFromResource(&vgl->fmt, &rsc);
315         if (!picture[i]) {
316             free(vgl->buffer[i]);
317             vgl->buffer[i] = NULL;
318             break;
319         }
320     }
321     if (i < VLCGL_TEXTURE_COUNT)
322         goto error;
323
324     /* */
325     picture_pool_configuration_t cfg;
326     memset(&cfg, 0, sizeof(cfg));
327     cfg.picture_count = i;
328     cfg.picture = picture;
329 #ifdef __APPLE__
330     cfg.lock = PictureLock;
331     cfg.unlock = PictureUnlock;
332 #endif
333     vgl->pool = picture_pool_NewExtended(&cfg);
334     if (!vgl->pool)
335         goto error;
336
337     vout_display_opengl_ResetTextures(vgl);
338
339     return vgl->pool;
340
341 error:
342     for (int j = 0; j < i; j++) {
343         picture_Delete(picture[j]);
344         vgl->buffer[j] = NULL;
345     }
346     return NULL;
347 }
348
349 static int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
350                                        picture_t *picture)
351 {
352     /* On Win32/GLX, we do this the usual way:
353        + Fill the buffer with new content,
354        + Reload the texture,
355        + Use the texture.
356
357        On OS X with VRAM or AGP texturing, the order has to be:
358        + Reload the texture,
359        + Fill the buffer with new content,
360        + Use the texture.
361
362        (Thanks to gcc from the Arstechnica forums for the tip)
363
364        Therefore on OSX, we have to use two buffers and textures and use a
365        lock(/unlock) managed picture pool.
366      */
367
368     if (vout_opengl_Lock(vgl->gl))
369         return VLC_EGENERIC;
370
371 #ifdef __APPLE__
372     /* Bind to the texture for drawing */
373     glBindTexture(VLCGL_TARGET, picture->p_sys->texture);
374 #else
375     /* Update the texture */
376     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
377                     vgl->fmt.i_width, vgl->fmt.i_height,
378                     VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
379 #endif
380
381     vout_opengl_Unlock(vgl->gl);
382     return VLC_SUCCESS;
383 }
384
385 static int vout_display_opengl_Display(vout_display_opengl_t *vgl,
386                                        const video_format_t *source)
387 {
388     if (vout_opengl_Lock(vgl->gl))
389         return VLC_EGENERIC;
390
391     /* glTexCoord works differently with GL_TEXTURE_2D and
392        GL_TEXTURE_RECTANGLE_EXT */
393 #if VLCGL_TARGET == GL_TEXTURE_2D
394     const float f_normw = vgl->tex_width;
395     const float f_normh = vgl->tex_height;
396 #else
397     assert(VLCGL_TARGET == GL_TEXTURE_RECTANGLE_EXT);
398     const float f_normw = 1.0;
399     const float f_normh = 1.0;
400 #endif
401
402     float f_x      = (source->i_x_offset +                       0 ) / f_normw;
403     float f_y      = (source->i_y_offset +                       0 ) / f_normh;
404     float f_width  = (source->i_x_offset + source->i_visible_width ) / f_normw;
405     float f_height = (source->i_y_offset + source->i_visible_height) / f_normh;
406
407     /* Why drawing here and not in Render()? Because this way, the
408        OpenGL providers can call vout_display_opengl_Display to force redraw.i
409        Currently, the OS X provider uses it to get a smooth window resizing */
410
411     glClear(GL_COLOR_BUFFER_BIT);
412
413     glEnable(VLCGL_TARGET);
414
415     glBegin(GL_POLYGON);
416     glTexCoord2f(f_x,      f_y);      glVertex2f(-1.0,  1.0);
417     glTexCoord2f(f_width,  f_y);      glVertex2f( 1.0,  1.0);
418     glTexCoord2f(f_width,  f_height); glVertex2f( 1.0, -1.0);
419     glTexCoord2f(f_x,      f_height); glVertex2f(-1.0, -1.0);
420     glEnd();
421
422     glDisable(VLCGL_TARGET);
423
424     vout_opengl_Swap(vgl->gl);
425
426     vout_opengl_Unlock(vgl->gl);
427     return VLC_SUCCESS;
428 }
429