]> git.sesse.net Git - vlc/blob - modules/video_output/opengl.h
opengl: Fix a typo.
[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 // Define USE_OPENGL_ES to the GL ES Version you want to select
34
35 #if USE_OPENGL_ES == 1
36 # include <OpenGLES/ES1/gl.h>
37 # include <OpenGLES/ES1/glext.h>
38 #elif USE_OPENGL_ES == 2
39 # include <OpenGLES/ES2/gl.h>
40 # include <OpenGLES/ES2/glext.h>
41 #elif defined(__APPLE__)
42 # define MACOS_OPENGL
43 # include <OpenGL/gl.h>
44 # include <OpenGL/glext.h>
45 #else
46 # include <GL/gl.h>
47 #endif
48
49 #ifndef YCBCR_MESA
50 # define YCBCR_MESA 0x8757
51 #endif
52 #ifndef UNSIGNED_SHORT_8_8_MESA
53 # define UNSIGNED_SHORT_8_8_MESA 0x85BA
54 #endif
55 /* RV16 */
56 #ifndef GL_UNSIGNED_SHORT_5_6_5
57 # define GL_UNSIGNED_SHORT_5_6_5 0x8363
58 #endif
59 #ifndef GL_CLAMP_TO_EDGE
60 # define GL_CLAMP_TO_EDGE 0x812F
61 #endif
62
63
64 #if USE_OPENGL_ES
65 # define VLCGL_TARGET GL_TEXTURE_2D
66
67 # define VLCGL_RGB_FORMAT GL_RGB
68 # define VLCGL_RGB_TYPE   GL_UNSIGNED_SHORT_5_6_5
69
70 // Use RGB with OpenGLES
71 # define VLCGL_FORMAT VLCGL_RGB_FORMAT
72 # define VLCGL_TYPE   VLCGL_RGB_TYPE
73
74 # define VLCGL_TEXTURE_COUNT (1)
75
76 #elif defined(MACOS_OPENGL)
77
78 /* On OS X, use GL_TEXTURE_RECTANGLE_EXT instead of GL_TEXTURE_2D.
79    This allows sizes which are not powers of 2 */
80 # define VLCGL_TARGET GL_TEXTURE_RECTANGLE_EXT
81
82 /* OS X OpenGL supports YUV. Hehe. */
83 # define VLCGL_FORMAT GL_YCBCR_422_APPLE
84 # define VLCGL_TYPE   GL_UNSIGNED_SHORT_8_8_APPLE
85
86 # define VLCGL_TEXTURE_COUNT (2)
87 #else
88
89 # define VLCGL_TARGET GL_TEXTURE_2D
90
91 /* RV32 */
92 # define VLCGL_RGB_FORMAT GL_RGBA
93 # define VLCGL_RGB_TYPE GL_UNSIGNED_BYTE
94
95 /* YUY2 */
96 # define VLCGL_YUV_FORMAT YCBCR_MESA
97 # define VLCGL_YUV_TYPE UNSIGNED_SHORT_8_8_MESA
98
99 /* Use RGB on Win32/GLX */
100 # define VLCGL_FORMAT VLCGL_RGB_FORMAT
101 # define VLCGL_TYPE   VLCGL_RGB_TYPE
102
103 # define VLCGL_TEXTURE_COUNT (1)
104 #endif
105
106 static inline int GetAlignedSize(int i_size)
107 {
108     /* Return the nearest power of 2 */
109     int i_result = 1;
110     while(i_result < i_size)
111         i_result *= 2;
112
113     return i_result;
114 }
115
116 typedef struct {
117     vout_opengl_t  *gl;
118
119     video_format_t fmt;
120
121     int        tex_pixel_size;
122     int        tex_width;
123     int        tex_height;
124
125     GLuint     texture[VLCGL_TEXTURE_COUNT];
126     uint8_t    *buffer[VLCGL_TEXTURE_COUNT];
127
128     picture_pool_t *pool;
129 } vout_display_opengl_t;
130
131 static int vout_display_opengl_Init(vout_display_opengl_t *vgl,
132                                     video_format_t *fmt,
133                                     vout_opengl_t *gl)
134 {
135     vgl->gl = gl;
136
137     /* Find the chroma we will use and update fmt */
138     /* TODO: We use YCbCr on Mac which is Y422, but on OSX it seems to == YUY2. Verify */
139 #if (defined(WORDS_BIGENDIAN) && VLCGL_FORMAT == GL_YCBCR_422_APPLE) || (VLCGL_FORMAT == YCBCR_MESA)
140     fmt->i_chroma = VLC_CODEC_YUYV;
141     vgl->tex_pixel_size = 2;
142 #elif defined(GL_YCBCR_422_APPLE) && (VLCGL_FORMAT == GL_YCBCR_422_APPLE)
143     fmt->i_chroma = VLC_CODEC_UYVY;
144     vgl->tex_pixel_size = 2;
145 #elif VLCGL_FORMAT == GL_RGB
146 #   if VLCGL_TYPE == GL_UNSIGNED_BYTE
147     fmt->i_chroma = VLC_CODEC_RGB24;
148 #       if defined(WORDS_BIGENDIAN)
149     fmt->i_rmask = 0x00ff0000;
150     fmt->i_gmask = 0x0000ff00;
151     fmt->i_bmask = 0x000000ff;
152 #       else
153     fmt->i_rmask = 0x000000ff;
154     fmt->i_gmask = 0x0000ff00;
155     fmt->i_bmask = 0x00ff0000;
156 #       endif
157     vgl->tex_pixel_size = 3;
158 #   else
159     fmt->i_chroma = VLC_CODEC_RGB16;
160 #       if defined(WORDS_BIGENDIAN)
161     fmt->i_rmask = 0x001f;
162     fmt->i_gmask = 0x07e0;
163     fmt->i_bmask = 0xf800;
164 #       else
165     fmt->i_rmask = 0xf800;
166     fmt->i_gmask = 0x07e0;
167     fmt->i_bmask = 0x001f;
168 #       endif
169     vgl->tex_pixel_size = 2;
170 #   endif
171 #else
172     fmt->i_chroma = VLC_CODEC_RGB32;
173 #       if defined(WORDS_BIGENDIAN)
174     fmt->i_rmask = 0xff000000;
175     fmt->i_gmask = 0x00ff0000;
176     fmt->i_bmask = 0x0000ff00;
177 #       else
178     fmt->i_rmask = 0x000000ff;
179     fmt->i_gmask = 0x0000ff00;
180     fmt->i_bmask = 0x00ff0000;
181 #       endif
182     vgl->tex_pixel_size = 4;
183 #endif
184
185     vgl->fmt = *fmt;
186
187     /* */
188     for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
189         vgl->texture[i] = 0;
190         vgl->buffer[i]  = NULL;
191     }
192     vgl->pool = NULL;
193
194     bool supports_npot = false;
195 #if USE_OPENGL_ES == 2
196     supports_npot = true;
197 #elif defined(MACOS_OPENGL)
198     supports_npot = true;
199 #endif
200
201 #if defined(__APPLE__) && USE_OPENGL_ES == 1
202     if (!vout_opengl_Lock(vgl->gl)) {
203         const char* extensions = (char*) glGetString(GL_EXTENSIONS);
204         if (extensions) {
205             bool npot = strstr(extensions, "GL_APPLE_texture_2D_limited_npot") != 0;
206             if (npot)
207                 supports_npot = true;
208         }
209         vout_opengl_Unlock(vgl->gl);
210     }
211 #endif
212
213     /* Texture size */
214     if (supports_npot) {
215         vgl->tex_width  = fmt->i_width;
216         vgl->tex_height = fmt->i_height;
217     }
218     else {
219         /* A texture must have a size aligned on a power of 2 */
220         vgl->tex_width  = GetAlignedSize(fmt->i_width);
221         vgl->tex_height = GetAlignedSize(fmt->i_height);
222     }
223
224
225     /* */
226     if (!vout_opengl_Lock(vgl->gl)) {
227
228         glDisable(GL_BLEND);
229         glDisable(GL_DEPTH_TEST);
230         glDepthMask(GL_FALSE);
231         glDisable(GL_CULL_FACE);
232         glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
233         glClear(GL_COLOR_BUFFER_BIT);
234
235         vout_opengl_Unlock(vgl->gl);
236     }
237     return VLC_SUCCESS;
238 }
239
240 static void vout_display_opengl_Clean(vout_display_opengl_t *vgl)
241 {
242     /* */
243     if (!vout_opengl_Lock(vgl->gl)) {
244
245         glFinish();
246         glFlush();
247         glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
248
249         vout_opengl_Unlock(vgl->gl);
250     }
251     if (vgl->pool) {
252         picture_pool_Delete(vgl->pool);
253         for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++)
254             free(vgl->buffer[i]);
255     }
256 }
257
258 static int vout_display_opengl_ResetTextures(vout_display_opengl_t *vgl)
259 {
260     if (vout_opengl_Lock(vgl->gl))
261         return VLC_EGENERIC;
262
263     glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
264
265     glGenTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
266     for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
267         glBindTexture(VLCGL_TARGET, vgl->texture[i]);
268
269 #if !USE_OPENGL_ES
270         /* Set the texture parameters */
271         glTexParameterf(VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0);
272         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
273 #endif
274
275         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
276         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
277         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
278         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
279
280 #ifdef MACOS_OPENGL
281         /* Tell the driver not to make a copy of the texture but to use
282            our buffer */
283         glEnable(GL_UNPACK_CLIENT_STORAGE_APPLE);
284         glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
285
286 #if 0
287         /* Use VRAM texturing */
288         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
289                          GL_STORAGE_CACHED_APPLE);
290 #else
291         /* Use AGP texturing */
292         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
293                          GL_STORAGE_SHARED_APPLE);
294 #endif
295 #endif
296
297         /* Call glTexImage2D only once, and use glTexSubImage2D later */
298         if (vgl->buffer[i]) {
299             glTexImage2D(VLCGL_TARGET, 0, VLCGL_FORMAT, vgl->tex_width,
300                          vgl->tex_height, 0, VLCGL_FORMAT, VLCGL_TYPE,
301                          vgl->buffer[i]);
302         }
303     }
304
305     vout_opengl_Unlock(vgl->gl);
306     return VLC_SUCCESS;
307 }
308
309 #ifdef MACOS_OPENGL
310 /* XXX See comment vout_display_opengl_Prepare */
311 struct picture_sys_t {
312     vout_display_opengl_t *vgl;
313     GLuint *texture;
314 };
315
316 /* Small helper */
317 static inline GLuint get_texture(picture_t *picture)
318 {
319     return *picture->p_sys->texture;
320 }
321
322 static int PictureLock(picture_t *picture)
323 {
324     if (!picture->p_sys)
325         return VLC_SUCCESS;
326
327     vout_display_opengl_t *vgl = picture->p_sys->vgl;
328     if (!vout_opengl_Lock(vgl->gl)) {
329         glBindTexture(VLCGL_TARGET, get_texture(picture));
330         glTexSubImage2D(VLCGL_TARGET, 0, 0, 0,
331                         vgl->fmt.i_width, vgl->fmt.i_height,
332                         VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
333
334         vout_opengl_Unlock(vgl->gl);
335     }
336     return VLC_SUCCESS;
337 }
338 static void PictureUnlock(picture_t *picture)
339 {
340     VLC_UNUSED(picture);
341 }
342 #endif
343
344 static picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl)
345 {
346     picture_t *picture[VLCGL_TEXTURE_COUNT];
347
348     int i;
349     for (i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
350
351         /* TODO memalign would be way better */
352         vgl->buffer[i] = malloc(vgl->tex_width * vgl->tex_height * vgl->tex_pixel_size);
353         if (!vgl->buffer[i])
354             break;
355
356         picture_resource_t rsc;
357         memset(&rsc, 0, sizeof(rsc));
358 #ifdef MACOS_OPENGL
359         rsc.p_sys = malloc(sizeof(*rsc.p_sys));
360         if (rsc.p_sys)
361         {
362             rsc.p_sys->vgl = vgl;
363             rsc.p_sys->texture = &vgl->texture[i];
364         }
365 #endif
366         rsc.p[0].p_pixels = vgl->buffer[i];
367         rsc.p[0].i_pitch  = vgl->fmt.i_width * vgl->tex_pixel_size;
368         rsc.p[0].i_lines  = vgl->fmt.i_height;
369
370         picture[i] = picture_NewFromResource(&vgl->fmt, &rsc);
371         if (!picture[i]) {
372             free(vgl->buffer[i]);
373             vgl->buffer[i] = NULL;
374             break;
375         }
376     }
377     if (i < VLCGL_TEXTURE_COUNT)
378         goto error;
379
380     /* */
381     picture_pool_configuration_t cfg;
382     memset(&cfg, 0, sizeof(cfg));
383     cfg.picture_count = i;
384     cfg.picture = picture;
385 #ifdef MACOS_OPENGL
386     cfg.lock = PictureLock;
387     cfg.unlock = PictureUnlock;
388 #endif
389     vgl->pool = picture_pool_NewExtended(&cfg);
390     if (!vgl->pool)
391         goto error;
392
393     vout_display_opengl_ResetTextures(vgl);
394
395     return vgl->pool;
396
397 error:
398     for (int j = 0; j < i; j++) {
399         picture_Delete(picture[j]);
400         vgl->buffer[j] = NULL;
401     }
402     return NULL;
403 }
404
405 static int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
406                                        picture_t *picture)
407 {
408     /* On Win32/GLX, we do this the usual way:
409        + Fill the buffer with new content,
410        + Reload the texture,
411        + Use the texture.
412
413        On OS X with VRAM or AGP texturing, the order has to be:
414        + Reload the texture,
415        + Fill the buffer with new content,
416        + Use the texture.
417
418        (Thanks to gcc from the Arstechnica forums for the tip)
419
420        Therefore on OSX, we have to use two buffers and textures and use a
421        lock(/unlock) managed picture pool.
422      */
423
424     if (vout_opengl_Lock(vgl->gl))
425         return VLC_EGENERIC;
426
427 #ifdef MACOS_OPENGL
428     /* Bind to the texture for drawing */
429     glBindTexture(VLCGL_TARGET, get_texture(picture));
430 #else
431     /* Update the texture */
432     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
433                     vgl->fmt.i_width, vgl->fmt.i_height,
434                     VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
435 #endif
436
437     vout_opengl_Unlock(vgl->gl);
438     return VLC_SUCCESS;
439 }
440
441 static int vout_display_opengl_Display(vout_display_opengl_t *vgl,
442                                        const video_format_t *source)
443 {
444     if (vout_opengl_Lock(vgl->gl))
445         return VLC_EGENERIC;
446
447     /* glTexCoord works differently with GL_TEXTURE_2D and
448        GL_TEXTURE_RECTANGLE_EXT */
449 #if VLCGL_TARGET == GL_TEXTURE_2D
450     const float f_normw = vgl->tex_width;
451     const float f_normh = vgl->tex_height;
452 #else
453     assert(VLCGL_TARGET == GL_TEXTURE_RECTANGLE_EXT);
454     const float f_normw = 1.0;
455     const float f_normh = 1.0;
456 #endif
457
458     float f_x      = (source->i_x_offset +                       0 ) / f_normw;
459     float f_y      = (source->i_y_offset +                       0 ) / f_normh;
460     float f_width  = (source->i_x_offset + source->i_visible_width ) / f_normw;
461     float f_height = (source->i_y_offset + source->i_visible_height) / f_normh;
462
463     /* Why drawing here and not in Render()? Because this way, the
464        OpenGL providers can call vout_display_opengl_Display to force redraw.i
465        Currently, the OS X provider uses it to get a smooth window resizing */
466
467     glClear(GL_COLOR_BUFFER_BIT);
468
469     glEnable(VLCGL_TARGET);
470
471 #if USE_OPENGL_ES
472     static const GLfloat vertexCoord[] = {
473         -1.0f, -1.0f,
474          1.0f, -1.0f,
475         -1.0f,  1.0f,
476          1.0f,  1.0f,
477     };
478
479     const GLfloat textureCoord[8] = {
480         f_x,     f_height,
481         f_width, f_height,
482         f_x,     f_y,
483         f_width, f_y
484     };
485
486     glEnableClientState(GL_VERTEX_ARRAY);
487     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
488     glVertexPointer(2, GL_FLOAT, 0, vertexCoord);
489     glTexCoordPointer(2, GL_FLOAT, 0, textureCoord);
490
491     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
492 #else
493     glBegin(GL_POLYGON);
494     glTexCoord2f(f_x,      f_y);      glVertex2f(-1.0,  1.0);
495     glTexCoord2f(f_width,  f_y);      glVertex2f( 1.0,  1.0);
496     glTexCoord2f(f_width,  f_height); glVertex2f( 1.0, -1.0);
497     glTexCoord2f(f_x,      f_height); glVertex2f(-1.0, -1.0);
498     glEnd();
499 #endif
500
501     glDisable(VLCGL_TARGET);
502
503     vout_opengl_Swap(vgl->gl);
504
505     vout_opengl_Unlock(vgl->gl);
506     return VLC_SUCCESS;
507 }
508