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