]> git.sesse.net Git - vlc/blob - modules/video_output/opengl.c
Define HAVE_V4L2 conditional
[vlc] / modules / video_output / opengl.c
1 /*****************************************************************************
2  * opengl.c: OpenGL and OpenGL ES output common code
3  *****************************************************************************
4  * Copyright (C) 2004 the VideoLAN team
5  * Copyright (C) 2009 Laurent Aimar
6  *
7  * Authors: Cyril Deguet <asmax@videolan.org>
8  *          Gildas Bazin <gbazin@videolan.org>
9  *          Eric Petit <titer@m0k.org>
10  *          Cedric Cocquebert <cedric.cocquebert@supelec.fr>
11  *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc_common.h>
32 #include <vlc_picture_pool.h>
33 #include <vlc_subpicture.h>
34 #include <vlc_opengl.h>
35
36 #include "opengl.h"
37 // Define USE_OPENGL_ES to the GL ES Version you want to select
38
39 #if !defined (__APPLE__)
40 # if USE_OPENGL_ES == 2
41 #  include <GLES2/gl2ext.h>
42 # elif USE_OPENGL_ES == 1
43 #  include <GLES/glext.h>
44 # else
45 #   include <GL/glext.h>
46 # endif
47 #else
48 # if USE_OPENGL_ES == 2
49 #  include <OpenGLES/ES2/gl.h>
50 # elif USE_OPENGL_ES == 1
51 #  include <OpenGLES/ES1/gl.h>
52 # else
53 #  define MACOS_OPENGL
54 #  include <OpenGL/glext.h>
55 # endif
56 # define PFNGLGENPROGRAMSARBPROC              typeof(glGenProgramsARB)*
57 # define PFNGLBINDPROGRAMARBPROC              typeof(glBindProgramARB)*
58 # define PFNGLPROGRAMSTRINGARBPROC            typeof(glProgramStringARB)*
59 # define PFNGLDELETEPROGRAMSARBPROC           typeof(glDeleteProgramsARB)*
60 # define PFNGLPROGRAMLOCALPARAMETER4FVARBPROC typeof(glProgramLocalParameter4fvARB)*
61 # define PFNGLACTIVETEXTUREARBPROC            typeof(glActiveTextureARB)*
62 # define PFNGLMULTITEXCOORD2FARBPROC          typeof(glMultiTexCoord2fARB)*
63 #endif
64
65 /* RV16 */
66 #ifndef GL_UNSIGNED_SHORT_5_6_5
67 # define GL_UNSIGNED_SHORT_5_6_5 0x8363
68 #endif
69 #ifndef GL_CLAMP_TO_EDGE
70 # define GL_CLAMP_TO_EDGE 0x812F
71 #endif
72
73 #if USE_OPENGL_ES
74 #   define VLCGL_TEXTURE_COUNT 1
75 #   define VLCGL_PICTURE_MAX 1
76 #elif defined(MACOS_OPENGL)
77 #   define VLCGL_TEXTURE_COUNT 2
78 #   define VLCGL_PICTURE_MAX 2
79 #else
80 #   define VLCGL_TEXTURE_COUNT 1
81 #   define VLCGL_PICTURE_MAX 128
82 #endif
83
84 static const vlc_fourcc_t gl_subpicture_chromas[] = {
85     VLC_CODEC_RGBA,
86     0
87 };
88
89 typedef struct {
90     GLuint   texture;
91     unsigned format;
92     unsigned type;
93     unsigned width;
94     unsigned height;
95
96     float    alpha;
97
98     float    top;
99     float    left;
100     float    bottom;
101     float    right;
102 } gl_region_t;
103
104 struct vout_display_opengl_t {
105     vlc_gl_t   *gl;
106
107     video_format_t fmt;
108     const vlc_chroma_description_t *chroma;
109
110     int        tex_target;
111     int        tex_format;
112     int        tex_internal;
113     int        tex_type;
114
115     int        tex_width[PICTURE_PLANE_MAX];
116     int        tex_height[PICTURE_PLANE_MAX];
117
118     GLuint     texture[VLCGL_TEXTURE_COUNT][PICTURE_PLANE_MAX];
119
120     int         region_count;
121     gl_region_t *region;
122
123
124     picture_pool_t *pool;
125
126     GLuint     program;
127     int        local_count;
128     GLfloat    local_value[16][4];
129
130     /* fragment_program */
131     PFNGLGENPROGRAMSARBPROC              GenProgramsARB;
132     PFNGLBINDPROGRAMARBPROC              BindProgramARB;
133     PFNGLPROGRAMSTRINGARBPROC            ProgramStringARB;
134     PFNGLDELETEPROGRAMSARBPROC           DeleteProgramsARB;
135     PFNGLPROGRAMLOCALPARAMETER4FVARBPROC ProgramLocalParameter4fvARB;
136
137     /* multitexture */
138     bool use_multitexture;
139     PFNGLACTIVETEXTUREARBPROC   ActiveTextureARB;
140     PFNGLMULTITEXCOORD2FARBPROC MultiTexCoord2fARB;
141 };
142
143 static inline int GetAlignedSize(unsigned size)
144 {
145     /* Return the smallest larger or equal power of 2 */
146     unsigned align = 1 << (8 * sizeof (unsigned) - clz(size));
147     return ((align >> 1) == size) ? size : align;
148 }
149
150 static bool IsLuminance16Supported(int target)
151 {
152     GLuint texture;
153
154     glGenTextures(1, &texture);
155     glBindTexture(target, texture);
156     glTexImage2D(target, 0, GL_LUMINANCE16,
157                  64, 64, 0, GL_LUMINANCE, GL_UNSIGNED_SHORT, NULL);
158     GLint size = 0;
159     glGetTexLevelParameteriv(target, 0, GL_TEXTURE_LUMINANCE_SIZE, &size);
160
161     glDeleteTextures(1, &texture);
162
163     return size == 16;
164 }
165
166 vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
167                                                const vlc_fourcc_t **subpicture_chromas,
168                                                vlc_gl_t *gl)
169 {
170     vout_display_opengl_t *vgl = calloc(1, sizeof(*vgl));
171     if (!vgl)
172         return NULL;
173
174     vgl->gl = gl;
175     if (vlc_gl_Lock(vgl->gl)) {
176         free(vgl);
177         return NULL;
178     }
179
180     const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
181
182     /* Load extensions */
183     bool supports_fp = false;
184     if (HasExtension(extensions, "GL_ARB_fragment_program")) {
185         vgl->GenProgramsARB    = (PFNGLGENPROGRAMSARBPROC)vlc_gl_GetProcAddress(vgl->gl, "glGenProgramsARB");
186         vgl->BindProgramARB    = (PFNGLBINDPROGRAMARBPROC)vlc_gl_GetProcAddress(vgl->gl, "glBindProgramARB");
187         vgl->ProgramStringARB  = (PFNGLPROGRAMSTRINGARBPROC)vlc_gl_GetProcAddress(vgl->gl, "glProgramStringARB");
188         vgl->DeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC)vlc_gl_GetProcAddress(vgl->gl, "glDeleteProgramsARB");
189         vgl->ProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC)vlc_gl_GetProcAddress(vgl->gl, "glProgramLocalParameter4fvARB");
190
191         supports_fp = vgl->GenProgramsARB &&
192                       vgl->BindProgramARB &&
193                       vgl->ProgramStringARB &&
194                       vgl->DeleteProgramsARB &&
195                       vgl->ProgramLocalParameter4fvARB;
196     }
197
198     bool supports_multitexture = false;
199     GLint max_texture_units = 0;
200     if (HasExtension(extensions, "GL_ARB_multitexture")) {
201         vgl->ActiveTextureARB   = (PFNGLACTIVETEXTUREARBPROC)vlc_gl_GetProcAddress(vgl->gl, "glActiveTextureARB");
202         vgl->MultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC)vlc_gl_GetProcAddress(vgl->gl, "glMultiTexCoord2fARB");
203
204         supports_multitexture = vgl->ActiveTextureARB &&
205                                 vgl->MultiTexCoord2fARB;
206         if (supports_multitexture)
207             glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &max_texture_units);
208     }
209
210     /* Initialize with default chroma */
211     vgl->fmt = *fmt;
212 #if USE_OPENGL_ES
213     vgl->fmt.i_chroma = VLC_CODEC_RGB16;
214 #   if defined(WORDS_BIGENDIAN)
215     vgl->fmt.i_rmask  = 0x001f;
216     vgl->fmt.i_gmask  = 0x07e0;
217     vgl->fmt.i_bmask  = 0xf800;
218 #   else
219     vgl->fmt.i_rmask  = 0xf800;
220     vgl->fmt.i_gmask  = 0x07e0;
221     vgl->fmt.i_bmask  = 0x001f;
222 #   endif
223     vgl->tex_target   = GL_TEXTURE_2D;
224     vgl->tex_format   = GL_RGB;
225     vgl->tex_internal = GL_RGB;
226     vgl->tex_type     = GL_UNSIGNED_SHORT_5_6_5;
227 #elif defined(MACOS_OPENGL)
228 #   if defined(WORDS_BIGENDIAN)
229     vgl->fmt.i_chroma = VLC_CODEC_YUYV;
230 #   else
231     vgl->fmt.i_chroma = VLC_CODEC_UYVY;
232 #   endif
233     vgl->tex_target   = GL_TEXTURE_RECTANGLE_EXT;
234     vgl->tex_format   = GL_YCBCR_422_APPLE;
235     vgl->tex_internal = GL_YCBCR_422_APPLE;
236     vgl->tex_type     = GL_UNSIGNED_SHORT_8_8_APPLE;
237 #else
238     vgl->fmt.i_chroma = VLC_CODEC_RGB32;
239 #   if defined(WORDS_BIGENDIAN)
240     vgl->fmt.i_rmask  = 0xff000000;
241     vgl->fmt.i_gmask  = 0x00ff0000;
242     vgl->fmt.i_bmask  = 0x0000ff00;
243 #   else
244     vgl->fmt.i_rmask  = 0x000000ff;
245     vgl->fmt.i_gmask  = 0x0000ff00;
246     vgl->fmt.i_bmask  = 0x00ff0000;
247 #   endif
248     vgl->tex_target   = GL_TEXTURE_2D;
249     vgl->tex_format   = GL_RGBA;
250     vgl->tex_internal = GL_RGBA;
251     vgl->tex_type     = GL_UNSIGNED_BYTE;
252 #endif
253     /* Use YUV if possible and needed */
254     bool need_fs_yuv = false;
255     float yuv_range_correction = 1.0;
256     if (supports_fp && supports_multitexture && max_texture_units >= 3 &&
257         vlc_fourcc_IsYUV(fmt->i_chroma) && !vlc_fourcc_IsYUV(vgl->fmt.i_chroma)) {
258         const vlc_fourcc_t *list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
259         while (*list) {
260             const vlc_chroma_description_t *dsc = vlc_fourcc_GetChromaDescription(*list);
261             if (dsc && dsc->plane_count == 3 && dsc->pixel_size == 1) {
262                 need_fs_yuv       = true;
263                 vgl->fmt          = *fmt;
264                 vgl->fmt.i_chroma = *list;
265                 vgl->tex_format   = GL_LUMINANCE;
266                 vgl->tex_internal = GL_LUMINANCE;
267                 vgl->tex_type     = GL_UNSIGNED_BYTE;
268                 yuv_range_correction = 1.0;
269                 break;
270             } else if (dsc && dsc->plane_count == 3 && dsc->pixel_size == 2 &&
271                        IsLuminance16Supported(vgl->tex_target)) {
272                 need_fs_yuv       = true;
273                 vgl->fmt          = *fmt;
274                 vgl->fmt.i_chroma = *list;
275                 vgl->tex_format   = GL_LUMINANCE;
276                 vgl->tex_internal = GL_LUMINANCE16;
277                 vgl->tex_type     = GL_UNSIGNED_SHORT;
278                 yuv_range_correction = (float)((1 << 16) - 1) / ((1 << dsc->pixel_bits) - 1);
279                 break;
280             }
281             list++;
282         }
283     }
284
285     vgl->chroma = vlc_fourcc_GetChromaDescription(vgl->fmt.i_chroma);
286     vgl->use_multitexture = vgl->chroma->plane_count > 1;
287
288     bool supports_npot = false;
289 #if USE_OPENGL_ES == 2
290     supports_npot = true;
291 #elif defined(MACOS_OPENGL)
292     supports_npot = true;
293 #else
294     supports_npot |= HasExtension(extensions, "GL_APPLE_texture_2D_limited_npot") ||
295                      HasExtension(extensions, "GL_ARB_texture_non_power_of_two");
296 #endif
297
298     /* Texture size */
299     for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
300         int w = vgl->fmt.i_width  * vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den;
301         int h = vgl->fmt.i_height * vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den;
302         if (supports_npot) {
303             vgl->tex_width[j]  = w;
304             vgl->tex_height[j] = h;
305         }
306         else {
307             /* A texture must have a size aligned on a power of 2 */
308             vgl->tex_width[j]  = GetAlignedSize(w);
309             vgl->tex_height[j] = GetAlignedSize(h);
310         }
311     }
312
313     /* Build fragment program if needed */
314     vgl->program = 0;
315     vgl->local_count = 0;
316     if (supports_fp) {
317         char *code = NULL;
318
319         if (need_fs_yuv) {
320             /* [R/G/B][Y U V O] from TV range to full range
321              * XXX we could also do hue/brightness/constrast/gamma
322              * by simply changing the coefficients
323              */
324             const float matrix_bt601_tv2full[3][4] = {
325                 { 1.1640,  0.0000,  1.4030, -0.7773 },
326                 { 1.1640, -0.3440, -0.7140,  0.4580 },
327                 { 1.1640,  1.7730,  0.0000, -0.9630 },
328             };
329             const float matrix_bt709_tv2full[3][4] = {
330                 { 1.1640,  0.0000,  1.5701, -0.8612 },
331                 { 1.1640, -0.1870, -0.4664,  0.2549 },
332                 { 1.1640,  1.8556,  0.0000, -1.0045 },
333             };
334             const float (*matrix)[4] = fmt->i_height > 576 ? matrix_bt709_tv2full
335                                                            : matrix_bt601_tv2full;
336
337             /* Basic linear YUV -> RGB conversion using bilinear interpolation */
338             const char *template_yuv =
339                 "!!ARBfp1.0"
340                 "OPTION ARB_precision_hint_fastest;"
341
342                 "TEMP src;"
343                 "TEX src.x,  fragment.texcoord[0], texture[0], 2D;"
344                 "TEX src.%c, fragment.texcoord[1], texture[1], 2D;"
345                 "TEX src.%c, fragment.texcoord[2], texture[2], 2D;"
346
347                 "PARAM coefficient[4] = { program.local[0..3] };"
348
349                 "TEMP tmp;"
350                 "MAD  tmp.rgb,          src.xxxx, coefficient[0], coefficient[3];"
351                 "MAD  tmp.rgb,          src.yyyy, coefficient[1], tmp;"
352                 "MAD  result.color.rgb, src.zzzz, coefficient[2], tmp;"
353                 "END";
354             bool swap_uv = vgl->fmt.i_chroma == VLC_CODEC_YV12 ||
355                            vgl->fmt.i_chroma == VLC_CODEC_YV9;
356             if (asprintf(&code, template_yuv,
357                          swap_uv ? 'z' : 'y',
358                          swap_uv ? 'y' : 'z') < 0)
359                 code = NULL;
360
361             for (int i = 0; i < 4; i++) {
362                 float correction = i < 3 ? yuv_range_correction : 1.0;
363                 for (int j = 0; j < 4; j++) {
364                     vgl->local_value[vgl->local_count + i][j] = j < 3 ? correction * matrix[j][i] : 0.0;
365                 }
366             }
367             vgl->local_count += 4;
368         }
369         if (code) {
370             vgl->GenProgramsARB(1, &vgl->program);
371             vgl->BindProgramARB(GL_FRAGMENT_PROGRAM_ARB, vgl->program);
372             vgl->ProgramStringARB(GL_FRAGMENT_PROGRAM_ARB,
373                                   GL_PROGRAM_FORMAT_ASCII_ARB,
374                                   strlen(code), (const GLbyte*)code);
375             if (glGetError() == GL_INVALID_OPERATION) {
376                 /* FIXME if the program was needed for YUV, the video will be broken */
377 #if 0
378                 GLint position;
379                 glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &position);
380
381                 const char *msg = (const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB);
382                 fprintf(stderr, "GL_INVALID_OPERATION: error at %d: %s\n", position, msg);
383 #endif
384                 vgl->DeleteProgramsARB(1, &vgl->program);
385                 vgl->program = 0;
386             }
387             free(code);
388         }
389     }
390
391     /* */
392     glDisable(GL_BLEND);
393     glDisable(GL_DEPTH_TEST);
394     glDepthMask(GL_FALSE);
395     glDisable(GL_CULL_FACE);
396     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
397     glClear(GL_COLOR_BUFFER_BIT);
398
399     vlc_gl_Unlock(vgl->gl);
400
401     /* */
402     for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
403         for (int j = 0; j < PICTURE_PLANE_MAX; j++)
404             vgl->texture[i][j] = 0;
405     }
406     vgl->region_count = 0;
407     vgl->region = NULL;
408     vgl->pool = NULL;
409
410     *fmt = vgl->fmt;
411     if (subpicture_chromas) {
412         *subpicture_chromas = NULL;
413 #if !defined(MACOS_OPENGL) && !USE_OPENGL_ES
414         if (supports_npot)
415             *subpicture_chromas = gl_subpicture_chromas;
416 #endif
417     }
418     return vgl;
419 }
420
421 void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
422 {
423     /* */
424     if (!vlc_gl_Lock(vgl->gl)) {
425
426         glFinish();
427         glFlush();
428         for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++)
429             glDeleteTextures(vgl->chroma->plane_count, vgl->texture[i]);
430         for (int i = 0; i < vgl->region_count; i++) {
431             if (vgl->region[i].texture)
432                 glDeleteTextures(1, &vgl->region[i].texture);
433         }
434         free(vgl->region);
435
436         if (vgl->program)
437             vgl->DeleteProgramsARB(1, &vgl->program);
438
439         vlc_gl_Unlock(vgl->gl);
440     }
441     if (vgl->pool)
442         picture_pool_Delete(vgl->pool);
443     free(vgl);
444 }
445
446 #ifdef MACOS_OPENGL
447 struct picture_sys_t {
448     vout_display_opengl_t *vgl;
449     GLuint *texture;
450 };
451
452 /* Small helper */
453 static inline GLuint PictureGetTexture(picture_t *picture)
454 {
455     return *picture->p_sys->texture;
456 }
457
458 static int PictureLock(picture_t *picture)
459 {
460     if (!picture->p_sys)
461         return VLC_SUCCESS;
462
463     vout_display_opengl_t *vgl = picture->p_sys->vgl;
464     if (!vlc_gl_Lock(vgl->gl)) {
465         glBindTexture(vgl->tex_target, PictureGetTexture(picture));
466         glTexSubImage2D(vgl->tex_target, 0,
467                         0, 0, vgl->fmt.i_width, vgl->fmt.i_height,
468                         vgl->tex_format, vgl->tex_type, picture->p[0].p_pixels);
469
470         vlc_gl_Unlock(vgl->gl);
471     }
472     return VLC_SUCCESS;
473 }
474
475 static void PictureUnlock(picture_t *picture)
476 {
477     VLC_UNUSED(picture);
478 }
479 #endif
480
481 picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl, unsigned requested_count)
482 {
483     if (vgl->pool)
484         return vgl->pool;
485
486     /* Allocate our pictures */
487     picture_t *picture[VLCGL_PICTURE_MAX] = {NULL, };
488     unsigned count = 0;
489
490     for (count = 0; count < __MIN(VLCGL_PICTURE_MAX, requested_count); count++) {
491         picture[count] = picture_NewFromFormat(&vgl->fmt);
492         if (!picture[count])
493             break;
494
495 #ifdef MACOS_OPENGL
496         picture_sys_t *sys = picture[count]->p_sys = malloc(sizeof(*sys));
497         if (sys) {
498             sys->vgl = vgl;
499             sys->texture = vgl->texture[count];
500         }
501 #endif
502     }
503     if (count <= 0)
504         return NULL;
505
506     /* Wrap the pictures into a pool */
507     picture_pool_configuration_t cfg;
508     memset(&cfg, 0, sizeof(cfg));
509     cfg.picture_count = count;
510     cfg.picture       = picture;
511 #ifdef MACOS_OPENGL
512     cfg.lock          = PictureLock;
513     cfg.unlock        = PictureUnlock;
514 #endif
515     vgl->pool = picture_pool_NewExtended(&cfg);
516     if (!vgl->pool)
517         goto error;
518
519     /* Allocates our textures */
520     if (vlc_gl_Lock(vgl->gl))
521         return vgl->pool;
522
523     for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
524         glGenTextures(vgl->chroma->plane_count, vgl->texture[i]);
525         for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
526             if (vgl->use_multitexture)
527                 vgl->ActiveTextureARB(GL_TEXTURE0_ARB + j);
528             glBindTexture(vgl->tex_target, vgl->texture[i][j]);
529
530 #if !USE_OPENGL_ES
531             /* Set the texture parameters */
532             glTexParameterf(vgl->tex_target, GL_TEXTURE_PRIORITY, 1.0);
533             glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
534 #endif
535
536             glTexParameteri(vgl->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
537             glTexParameteri(vgl->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
538             glTexParameteri(vgl->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
539             glTexParameteri(vgl->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
540
541 #ifdef MACOS_OPENGL
542             /* Tell the driver not to make a copy of the texture but to use
543                our buffer */
544             glEnable(GL_UNPACK_CLIENT_STORAGE_APPLE);
545             glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
546
547             /* Use AGP texturing */
548             glTexParameteri(vgl->tex_target, GL_TEXTURE_STORAGE_HINT_APPLE,
549                              GL_STORAGE_SHARED_APPLE);
550 #endif
551
552             /* Call glTexImage2D only once, and use glTexSubImage2D later */
553             glTexImage2D(vgl->tex_target, 0,
554                          vgl->tex_internal, vgl->tex_width[j], vgl->tex_height[j],
555                          0, vgl->tex_format, vgl->tex_type, NULL);
556         }
557     }
558
559     vlc_gl_Unlock(vgl->gl);
560
561     return vgl->pool;
562
563 error:
564     for (unsigned i = 0; i < count; i++)
565         picture_Delete(picture[i]);
566     return NULL;
567 }
568
569 int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
570                                 picture_t *picture, subpicture_t *subpicture)
571 {
572     /* On Win32/GLX, we do this the usual way:
573        + Fill the buffer with new content,
574        + Reload the texture,
575        + Use the texture.
576
577        On OS X with VRAM or AGP texturing, the order has to be:
578        + Reload the texture,
579        + Fill the buffer with new content,
580        + Use the texture.
581
582        (Thanks to gcc from the Arstechnica forums for the tip)
583
584        Therefore on OSX, we have to use two buffers and textures and use a
585        lock(/unlock) managed picture pool.
586      */
587
588     if (vlc_gl_Lock(vgl->gl))
589         return VLC_EGENERIC;
590
591 #ifdef MACOS_OPENGL
592     /* Bind to the texture for drawing */
593     glBindTexture(vgl->tex_target, PictureGetTexture(picture));
594 #else
595     /* Update the texture */
596     for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
597         if (vgl->use_multitexture)
598             vgl->ActiveTextureARB(GL_TEXTURE0_ARB + j);
599         glBindTexture(vgl->tex_target, vgl->texture[0][j]);
600         glPixelStorei(GL_UNPACK_ROW_LENGTH, picture->p[j].i_pitch / picture->p[j].i_pixel_pitch);
601         glTexSubImage2D(vgl->tex_target, 0,
602                         0, 0,
603                         vgl->fmt.i_width  * vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den,
604                         vgl->fmt.i_height * vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den,
605                         vgl->tex_format, vgl->tex_type, picture->p[j].p_pixels);
606     }
607 #endif
608
609     int         last_count = vgl->region_count;
610     gl_region_t *last = vgl->region;
611
612     vgl->region_count = 0;
613     vgl->region       = NULL;
614
615     if (subpicture) {
616
617         int count = 0;
618         for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next)
619             count++;
620
621         vgl->region_count = count;
622         vgl->region       = calloc(count, sizeof(*vgl->region));
623
624         if (vgl->use_multitexture)
625             vgl->ActiveTextureARB(GL_TEXTURE0_ARB + 0);
626         int i = 0;
627         for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next, i++) {
628             gl_region_t *glr = &vgl->region[i];
629
630             glr->format = GL_RGBA;
631             glr->type   = GL_UNSIGNED_BYTE;
632             glr->width  = r->fmt.i_visible_width;
633             glr->height = r->fmt.i_visible_height;
634             glr->alpha  = (float)subpicture->i_alpha * r->i_alpha / 255 / 255;
635             glr->left   =  2.0 * (r->i_x                          ) / subpicture->i_original_picture_width  - 1.0;
636             glr->top    = -2.0 * (r->i_y                          ) / subpicture->i_original_picture_height + 1.0;
637             glr->right  =  2.0 * (r->i_x + r->fmt.i_visible_width ) / subpicture->i_original_picture_width  - 1.0;
638             glr->bottom = -2.0 * (r->i_y + r->fmt.i_visible_height) / subpicture->i_original_picture_height + 1.0;
639
640             glr->texture = 0;
641             for (int j = 0; j < last_count; j++) {
642                 if (last[i].texture &&
643                     last[i].width  == glr->width &&
644                     last[i].height == glr->height &&
645                     last[i].format == glr->format &&
646                     last[i].type   == glr->type) {
647                     glr->texture = last[i].texture;
648                     memset(&last[i], 0, sizeof(last[i]));
649                     break;
650                 }
651             }
652
653             if (glr->texture) {
654                 glBindTexture(GL_TEXTURE_2D, glr->texture);
655                 /* TODO set GL_UNPACK_ALIGNMENT */
656                 glPixelStorei(GL_UNPACK_ROW_LENGTH, r->p_picture->p->i_pitch / r->p_picture->p->i_pixel_pitch);
657                 glTexSubImage2D(GL_TEXTURE_2D, 0,
658                                 0, 0, glr->width, glr->height,
659                                 glr->format, glr->type, r->p_picture->p->p_pixels);
660             } else {
661                 glGenTextures(1, &glr->texture);
662                 glBindTexture(GL_TEXTURE_2D, glr->texture);
663                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_PRIORITY, 1.0);
664                 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
665                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
666                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
667                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
668                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
669                 /* TODO set GL_UNPACK_ALIGNMENT */
670                 glPixelStorei(GL_UNPACK_ROW_LENGTH, r->p_picture->p->i_pitch / r->p_picture->p->i_pixel_pitch);
671                 glTexImage2D(GL_TEXTURE_2D, 0, glr->format,
672                              glr->width, glr->height, 0, glr->format, glr->type,
673                              r->p_picture->p->p_pixels);
674             }
675         }
676     }
677     for (int i = 0; i < last_count; i++) {
678         if (last[i].texture)
679             glDeleteTextures(1, &last[i].texture);
680     }
681     free(last);
682
683     vlc_gl_Unlock(vgl->gl);
684     VLC_UNUSED(subpicture);
685     return VLC_SUCCESS;
686 }
687
688 int vout_display_opengl_Display(vout_display_opengl_t *vgl,
689                                 const video_format_t *source)
690 {
691     if (vlc_gl_Lock(vgl->gl))
692         return VLC_EGENERIC;
693
694     /* glTexCoord works differently with GL_TEXTURE_2D and
695        GL_TEXTURE_RECTANGLE_EXT */
696     float left[PICTURE_PLANE_MAX];
697     float top[PICTURE_PLANE_MAX];
698     float right[PICTURE_PLANE_MAX];
699     float bottom[PICTURE_PLANE_MAX];
700     for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
701         float scale_w, scale_h;
702         if (vgl->tex_target == GL_TEXTURE_2D) {
703             scale_w = (float)vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den / vgl->tex_width[j];
704             scale_h = (float)vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den / vgl->tex_height[j];
705
706         } else {
707             scale_w = 1.0;
708             scale_h = 1.0;
709         }
710         left[j]   = (source->i_x_offset +                       0 ) * scale_w;
711         top[j]    = (source->i_y_offset +                       0 ) * scale_h;
712         right[j]  = (source->i_x_offset + source->i_visible_width ) * scale_w;
713         bottom[j] = (source->i_y_offset + source->i_visible_height) * scale_h;
714     }
715
716
717     /* Why drawing here and not in Render()? Because this way, the
718        OpenGL providers can call vout_display_opengl_Display to force redraw.i
719        Currently, the OS X provider uses it to get a smooth window resizing */
720
721     glClear(GL_COLOR_BUFFER_BIT);
722
723     if (vgl->program) {
724         glEnable(GL_FRAGMENT_PROGRAM_ARB);
725         for (int i = 0; i < vgl->local_count; i++)
726             vgl->ProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, i, vgl->local_value[i]);
727     } else {
728         glEnable(vgl->tex_target);
729     }
730
731 #if USE_OPENGL_ES
732     static const GLfloat vertexCoord[] = {
733         -1.0f, -1.0f,
734          1.0f, -1.0f,
735         -1.0f,  1.0f,
736          1.0f,  1.0f,
737     };
738
739     const GLfloat textureCoord[8] = {
740         left[0],  bottom[0],
741         right[0], bottom[0],
742         left[0],  top[0],
743         right[0], top[0]
744     };
745
746     glEnableClientState(GL_VERTEX_ARRAY);
747     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
748     glVertexPointer(2, GL_FLOAT, 0, vertexCoord);
749     glTexCoordPointer(2, GL_FLOAT, 0, textureCoord);
750
751     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
752 #else
753 #if !defined(MACOS_OPENGL)
754     for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
755         if (vgl->use_multitexture)
756             vgl->ActiveTextureARB(GL_TEXTURE0_ARB + j);
757         glBindTexture(vgl->tex_target, vgl->texture[0][j]);
758     }
759 #endif
760     glBegin(GL_POLYGON);
761
762     glTexCoord2f(left[0],  top[0]);
763     for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
764         vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, left[j], top[j]);
765     glVertex2f(-1.0,  1.0);
766
767     glTexCoord2f(right[0], top[0]);
768     for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
769         vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, right[j], top[j]);
770     glVertex2f( 1.0,  1.0);
771
772     glTexCoord2f(right[0], bottom[0]);
773     for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
774         vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, right[j], bottom[j]);
775     glVertex2f( 1.0, -1.0);
776
777     glTexCoord2f(left[0],  bottom[0]);
778     for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
779         vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, left[j], bottom[j]);
780     glVertex2f(-1.0, -1.0);
781
782     glEnd();
783 #endif
784
785     if (vgl->program)
786         glDisable(GL_FRAGMENT_PROGRAM_ARB);
787     else
788         glDisable(vgl->tex_target);
789
790 #if !USE_OPENGL_ES
791     if (vgl->use_multitexture)
792         vgl->ActiveTextureARB(GL_TEXTURE0_ARB + 0);
793     glEnable(GL_TEXTURE_2D);
794     glEnable(GL_BLEND);
795     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
796     for (int i = 0; i < vgl->region_count; i++) {
797         gl_region_t *glr = &vgl->region[i];
798
799         glBindTexture(GL_TEXTURE_2D, glr->texture);
800
801         glBegin(GL_POLYGON);
802
803         glColor4f(1.0, 1.0, 1.0, glr->alpha);
804
805         glTexCoord2f(0.0, 0.0);
806         glVertex2f(glr->left, glr->top);
807
808         glTexCoord2f(1.0, 0.0);
809         glVertex2f(glr->right, glr->top);
810
811         glTexCoord2f(1.0, 1.0);
812         glVertex2f(glr->right, glr->bottom);
813
814         glTexCoord2f(0.0, 1.0);
815         glVertex2f(glr->left, glr->bottom);
816
817         glEnd();
818     }
819     glDisable(GL_BLEND);
820     glDisable(GL_TEXTURE_2D);
821 #endif
822
823     vlc_gl_Swap(vgl->gl);
824
825     vlc_gl_Unlock(vgl->gl);
826     return VLC_SUCCESS;
827 }
828